Skip to content

feat(agents): editable session titles with txid-aware sync#4546

Open
KyleAMathews wants to merge 11 commits into
mainfrom
kyle/editable-session-titles
Open

feat(agents): editable session titles with txid-aware sync#4546
KyleAMathews wants to merge 11 commits into
mainfrom
kyle/editable-session-titles

Conversation

@KyleAMathews

Copy link
Copy Markdown
Contributor

Summary

Add editable session titles — users can click a session title in the UI to rename it inline, and Horton gets a set_title tool 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_title tool (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 to onBlur (single save path to prevent double-fire). Escape cancels. Optimistic update via createSetEntityTitleAction with 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 call awaitTxId() to wait for the write to round-trip through Electric sync before proceeding. The same pattern was added to send, updateInboxMessage, and deleteInboxMessage (using crypto.randomUUID() as stream-event txids). The UI-side optimistic actions (spawn, kill, signal, setEntityTitle) all await the txid before resolving.

Key Invariants

  • readTxidResponse accepts both string and numeric txids (Postgres returns numbers per Electric convention; inbox UUIDs are strings)
  • Tag awaitTxId only fires when a write actually occurred (result.txid is present); no-op tag writes skip it
  • onBlur is the single save path for title edits — onKeyDown(Enter) blurs the input rather than calling save directly
  • All mutation actions show error toasts on failure (spawn was missing one; now consistent)

Non-goals

  • awaitTxId timeout handling: All 12 awaitTxId call sites will reject on timeout, causing optimistic rollback even though the server succeeded. This pre-dates this PR and warrants its own design decision.
  • Parallel arrays refactor: uploadMessageAttachments returns { ids, txids } — could be Array<{ id, txid }> but that's a separate cleanup.

Verification

pnpm vitest run packages/agents-runtime/test/runtime-server-client-update-metadata.test.ts
pnpm vitest run packages/agents/test/horton-tool-composition.test.ts
pnpm vitest run packages/agents-server/test/electric-agents-routes.test.ts
pnpm vitest run packages/agents-server/test/dispatch-policy-routing.test.ts

Files Changed

New:

  • packages/agents/src/tools/set-title.tsset_title tool implementation

Feature:

  • packages/agents/src/agents/horton.ts — Register tool, add to system prompt
  • packages/agents-server-ui/src/components/EntityHeader.tsx — Click-to-edit inline title
  • packages/agents-server-ui/src/components/EntityHeader.module.css — Styles for title input and button
  • packages/agents-server-ui/src/lib/ElectricAgentsProvider.tsxcreateSetEntityTitleAction, spawn/title error toasts, awaitTxId calls

txid propagation:

  • packages/agents-server/src/entity-registry.ts — Return pg_current_xact_id() from tag mutations
  • packages/agents-server/src/entity-manager.ts — Propagate txid through send/inbox/tag operations
  • packages/agents-server/src/routing/entities-router.ts — Return txid in tag/send/inbox responses (204→200+JSON)
  • packages/agents-runtime/src/runtime-server-client.tsreadTxidResponse accepts string+number txids
  • packages/agents-runtime/src/process-wake.tsawaitTxId after tag operations
  • packages/agents-server-ui/src/lib/sendMessage.tsreadRequiredTxid helper, txid propagation in send/upload/inbox

Tests:

  • packages/agents-runtime/test/runtime-server-client-update-metadata.test.ts — deleteTag coverage, numeric txid coercion, non-JSON fallback
  • packages/agents-server/test/electric-agents-routes.test.ts — Tag endpoint tests with txid
  • packages/agents-server/test/dispatch-policy-routing.test.ts — Updated mocks for new return types
  • packages/agents/test/horton-tool-composition.test.ts — set_title happy/empty/error paths

🤖 Generated with Claude Code

KyleAMathews and others added 4 commits June 9, 2026 08:21
- 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>
@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Electric Agents Desktop Builds

Build artifacts for commit 0414f89.

Platform Status Artifact
macOS Apple Silicon Passed DMG
macOS Intel Passed DMG
Windows x64 Passed Installer
Linux x64 Passed AppImage / deb

Workflow run

@codecov

codecov Bot commented Jun 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 31.86813% with 124 lines in your changes missing coverage. Please review.
✅ Project coverage is 54.74%. Comparing base (916f6cd) to head (0414f89).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...gents-server-ui/src/lib/ElectricAgentsProvider.tsx 0.00% 48 Missing ⚠️
...s/agents-server-ui/src/components/EntityHeader.tsx 0.00% 34 Missing ⚠️
packages/agents-server-ui/src/lib/sendMessage.ts 0.00% 27 Missing ⚠️
packages/agents-server/src/entity-manager.ts 65.00% 7 Missing ⚠️
packages/agents-runtime/src/process-wake.ts 33.33% 4 Missing ⚠️
...kages/agents-server/src/routing/entities-router.ts 71.42% 4 Missing ⚠️
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              
Flag Coverage Δ
packages/agents 70.81% <100.00%> (+0.28%) ⬆️
packages/agents-mobile 71.42% <ø> (ø)
packages/agents-runtime 80.30% <81.81%> (+0.05%) ⬆️
packages/agents-server 73.98% <71.05%> (+0.03%) ⬆️
packages/agents-server-ui 5.63% <0.00%> (-0.05%) ⬇️
packages/electric-ax 46.42% <ø> (ø)
typescript 54.74% <31.86%> (-0.06%) ⬇️
unit-tests 54.74% <31.86%> (-0.06%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Electric Agents Mobile Build

Local mobile checks ran for commit 0414f89.

The EAS Android preview build was skipped because the mobile-eas-build label is not present.
Add the mobile-eas-build label to this PR to produce an installable preview build.

Workflow run

KyleAMathews and others added 3 commits June 9, 2026 11:53
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>
@netlify

netlify Bot commented Jun 9, 2026

Copy link
Copy Markdown

Deploy Preview for electric-next ready!

Name Link
🔨 Latest commit bbe4a35
🔍 Latest deploy log https://app.netlify.com/projects/electric-next/deploys/6a285147ac03ba0008d1f91b
😎 Deploy Preview https://deploy-preview-4546--electric-next.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@netlify

netlify Bot commented Jun 9, 2026

Copy link
Copy Markdown

Deploy Preview for electric-next ready!

Name Link
🔨 Latest commit d1afc9a
🔍 Latest deploy log https://app.netlify.com/projects/electric-next/deploys/6a2851cc1f2d23000862f2eb
😎 Deploy Preview https://deploy-preview-4546--electric-next.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

KyleAMathews and others added 4 commits June 9, 2026 12:01
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant