Collaboration & the Agent-as-CRDT-Peer
This is OpenKnowledge's quietest but most important idea. An agent's edit is not a suggestion in a side-panel — it is a write into the same CRDT document the human is editing live. The agent is just another collaborator on the wire.
1. The editor stack
The WYSIWYG surface (packages/app/src/editor/) is TipTap / ProseMirror with a
patched @handlewithcare/remark-prosemirror bridge so markdown round-trips into
rich text and back without corrupting the source. The extension set is broad —
slash commands (extensions/slash-command.ts), drag handles and block movers
(drag-handle.ts, block-mover.ts), find/replace, heading anchors, footnotes,
table controls with frozen headers, @-mentions (composer-mention/), a Callout
node, and collaborationCursor for presence. The bridge-id-plugin.ts and
source-dirty-observer.ts keep the rich-text view and the markdown source
reconciled.
"Editing markdown feels like Notion" is achieved by never showing the user markdown — they edit the ProseMirror tree; the markdown is the serialization, held in a separate CRDT field.
2. Two CRDT fields per document
Each document is a Yjs Y.Doc carrying two synchronized types
(packages/server/src/external-change.ts:63-64):
Y.Text('source')— the raw markdown text.Y.XmlFragment('default')— the ProseMirror rich-text state, bound via@tiptap/y-tiptap'supdateYFragment(packages/server/src/persistence.ts:25).
Both are Yjs CRDTs, so concurrent edits — from any number of humans and agents —
merge by causal order with no explicit lock or merge step. YAML frontmatter is
stripped before TipTap ingestion so config can live in the same file as editable
content (packages/core/src/markdown/pipeline.ts).
3. The seam: where an agent's edit enters
agent (Claude Code)
│ MCP: write { path, content } / edit { path, find, replace }
▼
ok mcp (stdio) ──► connects to collab server as a Yjs client
│
▼
applies change to Y.Text('source') + Y.XmlFragment('default')
│
▼
Hocuspocus broadcasts the Yjs update packages/server/src/server-factory.ts:513-518
│
├──► human's open editor renders it LIVE, agent avatar in presence bar
└──► persistence commits it to git (07_git_sync_audit.md)
There is no separate "AI changes" code path. The MCP write/edit tools mutate
the CRDT layer; from the editor's perspective the agent is a peer that happens to
have an agent avatar. This is the inverse of the common "diff review" UX where AI
output sits in a staging area — here it lands, and the review happens after, on
git history and the activity panel.
4. Reviewing agent work — the safety rails
Because agent edits land live, OK invests in after-the-fact review (the docs are the authority here):
- Presence: agent and human avatars appear top-right while editing
(
docs/content/features/agent-activity.mdx). - Agent-activity panel: click an agent avatar to see every file it touched this session, grouped by file, with per-edit additions/deletions, timestamp, and diff. While the session is live you can undo the agent's latest edit, or all of its edits in a file — selective rollback of one collaborator's contributions.
- Timeline & recovery: after the session closes, the per-document timeline
shows agent edits, human edits, upstream git sync, and external file-system
changes, each with an inline diff and an append-only restore (restoring
writes a new version, never deletes history) (
docs/content/features/timeline-and-recovery.mdx). - Conflict freeze: a doc with unresolved merge conflicts can't be edited — "the
same applies to agents writing over MCP" (
docs/content/features/github-sync.mdx). Conflicts surface as a tab badge, a pinned Conflicts list, and a unified diff view;conflicts/resolve_conflictMCP tools let an agent participate.
5. The collaboration server
Hocuspocus (server-factory.ts:513-518) runs five orthogonal extensions over the
document stream (:545-699): persistence (flush to git on quiescence),
liveDerivedIndex (maintain backlink/tag indexes as docs change),
principalAuth (validate client token + expected branch), cc1Broadcaster
(push file/backlink/graph/tag changes to clients), and serverObserver (track
who's focused on what). Adding a concern is adding an extension, not threading a
new code path through the sync loop.
What's worth stealing (for Swisscheese)
- Agent-as-peer, not agent-as-stage. Letting agent output land in the live document — with strong after review (activity panel, append-only timeline, selective per-agent rollback) — is a different bet than diff-gating everything up front. For multi-agent review it means N reviewers can annotate the same artifact concurrently and you reconcile by CRDT, not by queue.
- Selective rollback by author. "Undo everything this agent did in this file" is exactly the control you want when one of five parallel agents goes off the rails.
- Append-only restore. Recovery that writes a new version instead of deleting keeps the audit trail intact — a reviewer can always see what was reverted.
- Conflict freeze applies to agents too. A frozen-doc invariant that binds humans and MCP writers prevents an agent from blindly overwriting a contested change.
- Orthogonal extensions over one stream. Auth, persistence, indexing, presence as separate Hocuspocus extensions is a clean template for layering governance onto a shared-state server.