Commit 50e93c2
feat(agents): editable session titles with txid-aware sync (#4546)
## 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
```bash
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.ts` — `set_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.tsx` —
`createSetEntityTitleAction`, 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.ts` —
`readTxidResponse` accepts string+number txids
- `packages/agents-runtime/src/process-wake.ts` — `awaitTxId` after tag
operations
- `packages/agents-server-ui/src/lib/sendMessage.ts` —
`readRequiredTxid` 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](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>1 parent eed9ade commit 50e93c2
20 files changed
Lines changed: 739 additions & 126 deletions
File tree
- .changeset
- packages
- agents-runtime
- src
- test
- agents-server-conformance-tests/src
- agents-server-ui/src
- components
- lib
- agents-server
- src
- routing
- test
- agents
- src
- agents
- tools
- test
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2127 | 2127 | | |
2128 | 2128 | | |
2129 | 2129 | | |
2130 | | - | |
2131 | | - | |
2132 | | - | |
2133 | | - | |
| 2130 | + | |
| 2131 | + | |
| 2132 | + | |
| 2133 | + | |
| 2134 | + | |
| 2135 | + | |
2134 | 2136 | | |
2135 | 2137 | | |
2136 | 2138 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
206 | 206 | | |
207 | 207 | | |
208 | 208 | | |
209 | | - | |
| 209 | + | |
210 | 210 | | |
211 | 211 | | |
212 | 212 | | |
213 | 213 | | |
214 | | - | |
| 214 | + | |
215 | 215 | | |
216 | 216 | | |
217 | 217 | | |
| |||
308 | 308 | | |
309 | 309 | | |
310 | 310 | | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
311 | 324 | | |
312 | 325 | | |
313 | 326 | | |
| |||
837 | 850 | | |
838 | 851 | | |
839 | 852 | | |
840 | | - | |
| 853 | + | |
841 | 854 | | |
842 | 855 | | |
843 | 856 | | |
| |||
852 | 865 | | |
853 | 866 | | |
854 | 867 | | |
| 868 | + | |
855 | 869 | | |
856 | 870 | | |
857 | 871 | | |
858 | 872 | | |
859 | 873 | | |
860 | 874 | | |
861 | | - | |
| 875 | + | |
862 | 876 | | |
863 | 877 | | |
864 | 878 | | |
| |||
871 | 885 | | |
872 | 886 | | |
873 | 887 | | |
| 888 | + | |
874 | 889 | | |
875 | 890 | | |
876 | 891 | | |
| |||
Lines changed: 131 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
79 | 79 | | |
80 | 80 | | |
81 | 81 | | |
82 | | - | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
83 | 88 | | |
| 89 | + | |
84 | 90 | | |
85 | 91 | | |
86 | 92 | | |
| |||
94 | 100 | | |
95 | 101 | | |
96 | 102 | | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
97 | 139 | | |
98 | 140 | | |
99 | 141 | | |
| |||
121 | 163 | | |
122 | 164 | | |
123 | 165 | | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
124 | 180 | | |
125 | 181 | | |
126 | 182 | | |
| |||
136 | 192 | | |
137 | 193 | | |
138 | 194 | | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
139 | 269 | | |
140 | 270 | | |
141 | 271 | | |
| |||
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1126 | 1126 | | |
1127 | 1127 | | |
1128 | 1128 | | |
1129 | | - | |
| 1129 | + | |
1130 | 1130 | | |
1131 | 1131 | | |
1132 | 1132 | | |
| |||
Lines changed: 10 additions & 10 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
293 | 293 | | |
294 | 294 | | |
295 | 295 | | |
296 | | - | |
| 296 | + | |
297 | 297 | | |
298 | 298 | | |
299 | 299 | | |
| |||
1382 | 1382 | | |
1383 | 1383 | | |
1384 | 1384 | | |
1385 | | - | |
| 1385 | + | |
1386 | 1386 | | |
1387 | 1387 | | |
1388 | 1388 | | |
| |||
1552 | 1552 | | |
1553 | 1553 | | |
1554 | 1554 | | |
1555 | | - | |
| 1555 | + | |
1556 | 1556 | | |
1557 | 1557 | | |
1558 | 1558 | | |
| |||
2268 | 2268 | | |
2269 | 2269 | | |
2270 | 2270 | | |
2271 | | - | |
| 2271 | + | |
2272 | 2272 | | |
2273 | 2273 | | |
2274 | 2274 | | |
| |||
2288 | 2288 | | |
2289 | 2289 | | |
2290 | 2290 | | |
2291 | | - | |
| 2291 | + | |
2292 | 2292 | | |
2293 | 2293 | | |
2294 | 2294 | | |
| |||
2365 | 2365 | | |
2366 | 2366 | | |
2367 | 2367 | | |
2368 | | - | |
| 2368 | + | |
2369 | 2369 | | |
2370 | 2370 | | |
2371 | 2371 | | |
| |||
2602 | 2602 | | |
2603 | 2603 | | |
2604 | 2604 | | |
2605 | | - | |
| 2605 | + | |
2606 | 2606 | | |
2607 | 2607 | | |
2608 | 2608 | | |
| |||
2611 | 2611 | | |
2612 | 2612 | | |
2613 | 2613 | | |
2614 | | - | |
| 2614 | + | |
2615 | 2615 | | |
2616 | 2616 | | |
2617 | 2617 | | |
| |||
2627 | 2627 | | |
2628 | 2628 | | |
2629 | 2629 | | |
2630 | | - | |
| 2630 | + | |
2631 | 2631 | | |
2632 | 2632 | | |
2633 | 2633 | | |
| |||
2676 | 2676 | | |
2677 | 2677 | | |
2678 | 2678 | | |
2679 | | - | |
| 2679 | + | |
2680 | 2680 | | |
2681 | 2681 | | |
2682 | 2682 | | |
| |||
Lines changed: 30 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
9 | 21 | | |
10 | 22 | | |
11 | 23 | | |
| |||
14 | 26 | | |
15 | 27 | | |
16 | 28 | | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
17 | 47 | | |
18 | 48 | | |
19 | 49 | | |
| |||
0 commit comments