Add collaborative markdown document tools#4530
Conversation
✅ Deploy Preview for electric-next ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
dbd12ec to
828ffa0
Compare
828ffa0 to
319eba2
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## samwillis/agents-streaming-tool-calls #4530 +/- ##
=========================================================================
+ Coverage 55.05% 56.60% +1.55%
=========================================================================
Files 318 360 +42
Lines 37016 40834 +3818
Branches 10547 11241 +694
=========================================================================
+ Hits 20379 23116 +2737
- Misses 16604 17661 +1057
- Partials 33 57 +24
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Electric Agents Mobile BuildLocal mobile checks ran for commit The EAS Android preview build was skipped because the |
kevin-dp
left a comment
There was a problem hiding this comment.
I checked out this branch to give it a spin. I asked Horton to create a markdown document which it did. Then i opened it in the pane on the right and i added some text to the document. Then i asked Horton to tell me what i added to the document. However, it told me that the document is unchanged. And if i open the document again the changes are indeed missing. So it seems that my changes aren't automatically saving.
I asked Claude about it and it told me this is a bug:
Found it — this is a real bug in the PR, not a missing save step. Your edits are being silently rejected by the server. Here's the full chain, verified empirically against your running stack:
What's happening
- There is no save button by design — the editor streams every keystroke to a server-side "updates stream" for the doc.
- But when Horton's turn (wake) that created the document finished, the agent runtime closed its document session.
YjsProvider.disconnect()ends by callingproducer.close(), which sends a final append with astream-closed: trueheader — and that permanently closes the document's update stream on the server. - From that moment, every write from anyone — your editor, future agent edits — gets HTTP 409 "Stream is closed". The client library swallows the error (its
flush()even resolves successfully), so the UI keeps showing "Connected" and your text stays in local memory only. - When Horton re-read the doc, it correctly saw only the original server-side content — your sentence never made it there.
How I verified it
- Fetched your doc's raw stream from the server: it contains Horton's original markdown but no "SYNCINGG" — your edit never landed.
- Replayed the editor's exact write path with a script: appends to your doc return
409 Stream is closed. - Controlled experiment on a fresh doc: session A writes fine (200), disconnects (final append carries
stream-closed: true, server answers 204), then session B's writes all fail with 409. Reproduces every time.
Where the fix belongs
The closing call is in @durable-streams/y-durable-streams (v0.2.7, latest): disconnect() → closeUpdatesProducer() → producer.close(), and in @durable-streams/client a producer's close() means "close the stream", not "leave the session". For a multi-writer collaborative doc, no single participant's disconnect should close the shared stream. Either the provider needs a flush-without-close disconnect, the PR's wake-scoped session close() (packages/agents-runtime/src/markdown-document-session.ts:100) shouldn't disconnect this way, or the agents-server needs to implement the stream-rollover the protocol seems to expect. A second, smaller issue: the UI shows "Connected" while writes 409 — sync failures should surface in that status indicator.
There's no workaround for testing user→agent edits right now — every doc's stream gets closed as soon as the creating wake ends.
Summary
Stacked on #4555. Adds collaborative markdown documents for Electric Agents. This PR consumes the streaming tool-call hook from #4555, but keeps the markdown document implementation in a separate layer.
What this adds:
documentmanifest entry for markdown docs with:text/markdownapplication/vnd.electric-agents.markdown-yjsy-durable-streamsmarkdownY.Doc, keep it synced through@durable-streams/y-durable-streams, and append binary Yjs updates to durable streams.Architecture notes:
Y.Doc; the Yjs provider writes updates to the durable stream.onArgsDeltahook.Validation
pnpm --filter @electric-ax/agents-runtime typecheckpnpm --filter @electric-ax/agents-server-ui typecheckpnpm --filter @electric-ax/agents typecheckpnpm --filter @electric-ax/agents-server typecheckpnpm --filter @electric-ax/agents-runtime exec vitest run test/markdown-docs-tools.test.ts test/outbound-bridge.test.ts test/pi-adapter.test.tspnpm --filter @electric-ax/agents-runtime exec vitest run test/runtime-dsl.test.tspnpm --filter @electric-ax/agents exec vitest run test/spawn-worker-tool.test.ts test/horton-tool-composition.test.ts test/horton-system-prompt.test.tspnpm --filter @electric-ax/agents-server exec vitest run test/electric-agents-routes.test.ts test/electric-agents-status.test.ts test/electric-agents-manager-write-validation.test.tspnpm --filter @electric-ax/agents-server-ui exec vitest run src/components/views/MarkdownDocumentView.test.ts src/lib/workspace/workspaceReducer.test.tspnpm --filter @electric-examples/yjs run typecheckpnpm --filter @electric-ax/agents-mobile run ci:checkgit diff --check