feat(agents): editable session titles with txid-aware sync#4546
feat(agents): editable session titles with txid-aware sync#4546KyleAMathews wants to merge 11 commits into
Conversation
- Fix readTxidResponse to accept numeric txids from Postgres (was silently
discarding them, making tag awaitTxId dead code)
- Fix request body field mismatch: { title } → { value: title }
- Fix double-fire of saveEdit on Enter key (delegate to onBlur instead)
- Add error toasts for title edit and spawn failures
- Extract readRequiredTxid, withOptionalTxid, tagResponseBody helpers
- Add tests for deleteTag return values, numeric txid coercion, set_title
error path, and readTxidResponse non-JSON fallback
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #4546 +/- ##
==========================================
- Coverage 54.80% 54.74% -0.06%
==========================================
Files 317 318 +1
Lines 36681 36810 +129
Branches 10466 10497 +31
==========================================
+ Hits 20104 20153 +49
- Misses 16544 16624 +80
Partials 33 33
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 |
The tag endpoints already require withEntityPermission('write') middleware.
The write token check was a redundant second layer that prevented UI callers
(who authenticate via principal) from setting tags. Make it optional so both
auth paths work.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
✅ Deploy Preview for electric-next ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
✅ Deploy Preview for electric-next ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Tag mutations go directly to Postgres via the entity-registry, not through the entity's durable stream. The stream DB never sees the Postgres xact id, so awaitTxId always times out. The UI-side awaitTxId works because it uses an Electric-synced collection. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
Add editable session titles — users can click a session title in the UI to rename it inline, and Horton gets a
set_titletool to programmatically set titles. Both paths use a new txid-propagation pattern so tag mutations await sync consistency before resolving.Approach
Three layers of change:
1.
set_titletool (packages/agents/src/tools/set-title.ts)New Horton tool that calls
ctx.setTag('title', trimmedTitle). Validates non-empty input, returns structured error responses (doesn't throw) so the LLM can react to failures.2. Click-to-edit UI (
packages/agents-server-ui/src/components/EntityHeader.tsx)Title text becomes a
<button>that switches to an<input>on click. Enter delegates toonBlur(single save path to prevent double-fire). Escape cancels. Optimistic update viacreateSetEntityTitleActionwith rollback on persistence failure. Error toast on failure.3. txid propagation for tag/send/inbox mutations
The core change that makes this work reliably. Tag mutations (
setTag/deleteTag) now return the Postgres transaction ID (pg_current_xact_id()) so clients can callawaitTxId()to wait for the write to round-trip through Electric sync before proceeding. The same pattern was added tosend,updateInboxMessage, anddeleteInboxMessage(usingcrypto.randomUUID()as stream-event txids). The UI-side optimistic actions (spawn,kill,signal,setEntityTitle) all await the txid before resolving.Key Invariants
readTxidResponseaccepts both string and numeric txids (Postgres returns numbers per Electric convention; inbox UUIDs are strings)awaitTxIdonly fires when a write actually occurred (result.txidis present); no-op tag writes skip itonBluris the single save path for title edits —onKeyDown(Enter)blurs the input rather than calling save directlyNon-goals
awaitTxIdcall sites will reject on timeout, causing optimistic rollback even though the server succeeded. This pre-dates this PR and warrants its own design decision.uploadMessageAttachmentsreturns{ ids, txids }— could beArray<{ id, txid }>but that's a separate cleanup.Verification
Files Changed
New:
packages/agents/src/tools/set-title.ts—set_titletool implementationFeature:
packages/agents/src/agents/horton.ts— Register tool, add to system promptpackages/agents-server-ui/src/components/EntityHeader.tsx— Click-to-edit inline titlepackages/agents-server-ui/src/components/EntityHeader.module.css— Styles for title input and buttonpackages/agents-server-ui/src/lib/ElectricAgentsProvider.tsx—createSetEntityTitleAction, spawn/title error toasts,awaitTxIdcallstxid propagation:
packages/agents-server/src/entity-registry.ts— Returnpg_current_xact_id()from tag mutationspackages/agents-server/src/entity-manager.ts— Propagate txid through send/inbox/tag operationspackages/agents-server/src/routing/entities-router.ts— Return txid in tag/send/inbox responses (204→200+JSON)packages/agents-runtime/src/runtime-server-client.ts—readTxidResponseaccepts string+number txidspackages/agents-runtime/src/process-wake.ts—awaitTxIdafter tag operationspackages/agents-server-ui/src/lib/sendMessage.ts—readRequiredTxidhelper, txid propagation in send/upload/inboxTests:
packages/agents-runtime/test/runtime-server-client-update-metadata.test.ts— deleteTag coverage, numeric txid coercion, non-JSON fallbackpackages/agents-server/test/electric-agents-routes.test.ts— Tag endpoint tests with txidpackages/agents-server/test/dispatch-policy-routing.test.ts— Updated mocks for new return typespackages/agents/test/horton-tool-composition.test.ts— set_title happy/empty/error paths🤖 Generated with Claude Code