Files
WeHub Mirror 6bf8bebf51
CI / Test and Build (push) Failing after 1s
CI / Migrate Dev DB (push) Has been skipped
CI / Migrate DB (push) Has been skipped
CodeQL / Analyze actions (push) Has been cancelled
CodeQL / Analyze javascript-typescript (push) Has been cancelled
CI / Detect Version (push) Has been cancelled
CI / Detect Desktop Changes (push) Has been cancelled
CI / Build AMD64 (blacksmith-2vcpu-ubuntu-2404, ./docker/cron.Dockerfile, ubuntu-latest, ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Build AMD64 (blacksmith-2vcpu-ubuntu-2404, ./docker/db.Dockerfile, ECR_MIGRATIONS, ubuntu-latest, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (blacksmith-4vcpu-ubuntu-2404, ./docker/pii.Dockerfile, ECR_PII, ubuntu-latest, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (blacksmith-4vcpu-ubuntu-2404, ./docker/realtime.Dockerfile, ECR_REALTIME, ubuntu-latest, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build AMD64 (blacksmith-8vcpu-ubuntu-2404, ./docker/app.Dockerfile, ECR_APP, linux-x64-8-core, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/cron.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/db.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/pii.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-4vcpu-ubuntu-2404-arm, ./docker/realtime.Dockerfile, ubuntu-24.04-arm, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (blacksmith-8vcpu-ubuntu-2404-arm, ./docker/app.Dockerfile, linux-arm64-8-core, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
Helm Chart / Lint, test, and validate chart (push) Has been cancelled
Helm Chart / Chart version bumped (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Build Dev ECR (blacksmith-8vcpu-ubuntu-2404, ./docker/app.Dockerfile, ECR_APP, linux-x64-8-core) (push) Has been cancelled
CI / Promote Images (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/cron) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-2vcpu-ubuntu-2404, ./docker/db.Dockerfile, ECR_MIGRATIONS, ubuntu-latest) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-4vcpu-ubuntu-2404, ./docker/pii.Dockerfile, ECR_PII, ubuntu-latest) (push) Has been cancelled
CI / Build Dev ECR (blacksmith-4vcpu-ubuntu-2404, ./docker/realtime.Dockerfile, ECR_REALTIME, ubuntu-latest) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Check Desktop Signing Secrets (push) Has been cancelled
CI / Desktop Release (push) Has been cancelled
CI / Create Desktop Prerelease (push) Has been cancelled
CI / Desktop Prerelease Build (push) Has been cancelled
CI / Publish Desktop Prerelease (push) Has been cancelled
CI / Prune Desktop Prereleases (push) Has been cancelled
Helm Chart / Install on kind and run helm test (push) Has been cancelled
WeHub snapshot of cb28d14c6f2c081de7a0d8729a8c816c9adef67a
2026-08-10 11:17:50 +08:00
..

@/lib/collab-doc — server-side collaborative-document conversion

Server-side conversion between a file's markdown (the durable source of truth) and its collaborative Yjs document, so the server can own the doc: seed it, project it back to markdown, and let the agent write into it while a user is typing.

Why this exists

Collaborative file editing had two writers with no shared CRDT: copilot edit_content wrote markdown straight to the file while the user typed into an ephemeral, client-seeded Yjs doc. They couldn't reconcile — the agent's edit didn't stream into the editor, and last-writer clobbered. The fix is a server-authoritative Yjs doc both sides write into, with markdown as a projection.

What Stage A (this module) provides

Function Purpose
markdownToYDoc(md) Cold-start seed: file markdown → a fresh Y.Doc.
yDocToMarkdown(ydoc) Projection: Y.Doc → the file's canonical markdown.
applyMarkdownToYDoc(ydoc, md) Agent write: merge new content into a live Y.Doc as a minimal CRDT diff (no clobber).

Design decisions (why it's not hacky)

  • Parity by construction. The markdown↔ProseMirror step reuses the exact client engine (parseMarkdownToDoc / serializeDocToMarkdown, @tiptap/markdown on the shared extension set) — not a second markdown implementation — so the server can never diverge from what the editor renders. The custom-fidelity constructs (tables, footnotes, raw HTML, sim: mentions) are covered by the same code that covers them in the browser; the round-trip test asserts equivalence.
  • Same Yjs binding as the browser. ProseMirror↔Yjs uses @tiptap/y-tiptap (what TipTap's Collaboration extension uses), pinned to the same version and sharing the same prosemirror-model / yjs instances (peer deps) — so the structure the server produces is byte-compatible with the client, targeting the same 'default' fragment.
  • Merge, not replace. applyMarkdownToYDoc uses updateYFragment (the primitive ySyncPlugin runs on every keystroke) to apply only the diff, so Yjs reconciles the agent's write with in-flight remote edits. The test proves an agent write and a concurrent remote edit both survive.
  • Server-only, DOM via jsdom. The markdown engine builds a (never-mounted) TipTap editor that needs a DOM; on the server it's backed by a single lazily-created jsdom window. Lazy-required so the client bundle never pulls jsdom in.

Server-authoritative seeding (shipped alongside this module)

The realtime relay seeds each room's document from this module over an internal endpoint (buildFileDocSeedPOST /api/internal/file-doc/seedensureServerSeed), which let the entire client-seeder subsystem (election / deadlines / triedSeeders / MAX_SEED_ROUNDS / the SEED_REQUEST handshake) be deleted. The client's connect-deadline offline fallback is deliberately kept — it is unrelated to seeding. No feature flag: the cutover is all-at-once.

Remaining stages (future PRs)

  • Durable persistence. A DB column for the Yjs binary + debounced snapshotting, so a document survives with no collaborators connected instead of being re-seeded from markdown on cold open.
  • Copilot into the doc + projection. edit_content calls applyMarkdownToYDoc when a doc is live; a debounced yDocToMarkdown projection keeps the file's markdown current.