Repository navigation
Commit ee0da19
Fix Electric Agents quickstart runner registration (#4610)
## Executive Summary
Fixes the Electric Agents quickstart path so the printed CLI commands
can spawn, route, send to, and observe Horton entities without extra
principal or dispatch-policy setup. The user-visible result is that
`electric agents quickstart` creates a usable local runner-backed
environment, `electric agents observe` uses the same shared timeline
live query as the other UIs, and interactive terminal sends remain
visible while they reconcile with the entity stream.
## Root Cause
There were several related quickstart gaps:
- The built-in runner registered with one principal while the CLI used
another, so entity type management, runner ownership checks, and default
user permissions did not line up.
- Built-in Horton/Worker type registration intentionally omitted a
default dispatch policy, so `electric agents spawn /horton/onboarding`
could create an entity that no pull-wake runner would claim.
- The CLI-launched built-in runtime installed the optional global Undici
fetch cache; in local quickstart this could surface as opaque fetch
failures such as `invalid onError method` during type registration.
- `electric agents observe` and the shared UI timeline query were
resolving different `@tanstack/db` package instances. TanStack validates
live-query sources with package-local `CollectionImpl` identity, so
valid StreamDB collections were rejected as non-collections.
- The shared live timeline query concatenated streamed text deltas by
row key rather than stream sequence. Once a response reached `msg-0:10`,
lexicographic ordering placed that chunk before `msg-0:2`, which only
surfaced in consumers reading the live query path.
- Live run item ordering mixed text row creation order with tool-call
timeline order. A text row can be created before its first delta
arrives, so text could render before an earlier tool call.
- Interactive `observe` sends posted successfully but did not await the
returned stream `txid` or send a stable client key, so the optimistic
inbox row could disappear before the real stream event reconciled.
- The CLI entity stream helper constructed a bare StreamDB instead of
using the agents-runtime `createEntityStreamDB` wrapper used by the web
UI. That skipped runtime row-offset materialization (`_timeline_order`),
so the shared timeline query fell back to source tie-breakers and
grouped inbox rows ahead of run rows.
- Published CLI builds floated `electricax/agents-server:latest`, so a
released CLI could start a server image with mismatched runtime
contracts.
## Approach
- Make quickstart resolve one runner owner principal and force that same
principal into the built-in runtime's server headers.
- Default the CLI principal to `user:<ELECTRIC_AGENTS_IDENTITY>` so the
no-env quickstart commands match the built-in `principal_kind=user`
grants.
- Register built-in Horton/Worker types with the local pull-wake runner
as their default dispatch target.
- Disable the optional global Durable Streams fetch cache only for the
CLI-managed built-in runtime.
- Align `electric-ax` with the same TypeScript/TanStack DB peer set as
`agents-runtime`, so both packages resolve the same `@tanstack/db`
instance.
- Restore CLI `observe` to `createEntityTimelineQuery(db)` plus
`useLiveQuery`, then adapt the shared query rows into the existing Ink
terminal sections.
- Order text/reasoning deltas by `_seq` in the shared live query,
matching the materialized timeline view.
- Order live run child items in one sequence domain: tool calls by
`_seq`, text items by first text-delta `_seq` with text-row `_seq` as
fallback.
- Keep interactive observe sends optimistic until the returned `txid` is
observed, using a stable client key and pending timeline order so the
optimistic row and synced inbox row reconcile.
- Make the CLI entity stream helper delegate to the agents-runtime
StreamDB helper, matching the web UI stream setup and preserving
`_timeline_order` for shared timeline row ordering.
- Add shared timeline indexes for joined collections so the live query
path is warning-free in both CLI and UI consumers.
- Inject the matching `@electric-ax/agents-server` package version into
the CLI Docker image tag at build time, and make `ci:publish` rebuild
packages after Changesets updates versions.
- Add endpoint/cause context around entity type and runner registration
fetch failures.
- Clarify browser-only credentials settings copy so it points at the
connected runtime, not editable browser-side provider keys.
## Key Invariants
- The runner registration principal, runner owner, runner claim headers,
and CLI default principal must agree for the local quickstart path.
- Built-in agent types must have a runner default dispatch policy when
they are registered by a pull-wake built-in runtime.
- `ELECTRIC_AGENTS_PRINCIPAL` remains the explicit override for
non-default deployments.
- `electric-ax`, `agents-runtime`, and UI consumers must resolve a
single TanStack DB module instance for shared live-query collection
identity checks.
- CLI and desktop/web timeline consumers should share
`createEntityTimelineQuery(db)`; only the rendering adapter should
differ.
- Live streamed deltas must be ordered by stream sequence, not by
human-readable event keys.
- Live run child items must be ordered by model event sequence, not by
when a text container row was created.
- Terminal optimistic sends must use a stable key and await stream
reconciliation before dropping optimistic state.
- CLI and web UI StreamDB setup must share the runtime helper so row
offsets and `_timeline_order` are materialized consistently.
- Released `electric-ax` builds should start an agents-server image
matching the release version unless explicitly overridden.
## Non-goals
- This does not make provider keys editable from the browser UI.
- This does not alter runner ownership authorization rules on the
server; quickstart now supplies matching principals instead.
- This does not mask model provider failures.
- This does not make the terminal observer render every rich
desktop-only timeline row. It maps the shared query output into the
existing terminal sections.
## Trade-offs
The CLI observer now shares the same timeline query as the desktop/web
UIs, which keeps query semantics in one place and prevents the terminal
observer from drifting. The terminal still has a small adapter because
its Ink renderer expects simpler records than the desktop timeline
component.
The timeline query now creates a few `BasicIndex` indexes before
building joins. That is a small setup cost per StreamDB instance, but it
avoids repeated TanStack warnings and keeps the shared query suitable
for CLI and UI use.
The Docker image pin is injected at build time rather than hard-coded
because Changesets owns release version updates. Rebuilding during
`ci:publish` ensures the generated artifacts see the bumped package
versions.
## Verification
```sh
pnpm --filter @electric-ax/agents-runtime build
pnpm --filter electric-ax build
pnpm --filter @electric-ax/agents-runtime exec vitest run test/entity-timeline.test.ts
pnpm --filter electric-ax test -- entity-stream-db.test.ts observe-ui.test.tsx
pnpm --filter electric-ax test -- observe-ui.test.tsx cli.test.ts start.test.ts
pnpm --filter @electric-ax/agents-server-ui typecheck
pnpm --filter @electric-ax/agents exec vitest run test/builtin-pull-wake-registration.test.ts
```
Manual isolated quickstart verification:
```sh
cd /tmp
ELECTRIC_AGENTS_COMPOSE_PROJECT=electric-agents-verify \
ELECTRIC_AGENTS_PORT=4448 \
node /Users/kylemathews/programs/electric/packages/electric-ax/dist/index.js agents quickstart
```
In a second terminal:
```sh
ELECTRIC_AGENTS_URL=http://localhost:4448 \
node /Users/kylemathews/programs/electric/packages/electric-ax/dist/index.js agents types
ELECTRIC_AGENTS_URL=http://localhost:4448 \
node /Users/kylemathews/programs/electric/packages/electric-ax/dist/index.js agents spawn /horton/onboarding
ELECTRIC_AGENTS_URL=http://localhost:4448 \
node /Users/kylemathews/programs/electric/packages/electric-ax/dist/index.js agents inspect /horton/onboarding
ELECTRIC_AGENTS_URL=http://localhost:4448 \
node /Users/kylemathews/programs/electric/packages/electric-ax/dist/index.js agents send /horton/onboarding "Onboard me to Electric Agents"
ELECTRIC_AGENTS_URL=http://localhost:4448 \
node /Users/kylemathews/programs/electric/packages/electric-ax/dist/index.js agents observe /horton/onboarding
```
Observed results:
- Horton and Worker type registration succeeded.
- Pull-wake runner `builtin-agents` started.
- `agents types` listed built-ins without a manual principal override.
- `agents spawn /horton/onboarding` succeeded.
- `agents inspect /horton/onboarding` showed
`dispatch_policy.targets[0].runnerId = builtin-agents`.
- `agents send /horton/onboarding "Onboard me to Electric Agents"` was
picked up by the runner.
- `agents observe /horton/onboarding` rendered the timeline through the
shared live query instead of throwing `Invalid source for live query`,
with no TanStack join-index warning.
- The terminal observer rendered the full streamed Horton response after
`_seq` ordering replaced key ordering for live deltas.
- Tool calls now remain before later text when the text row was created
before its first delta.
## Files Changed
- `.changeset/quickstart-runner-principal.md`: patch release note for
affected packages.
- `package.json`: rebuild packages before publish/tag so injected
version constants are current after Changesets versioning.
- `packages/agents-runtime/src/create-handler.ts`: include registration
endpoint and fetch cause in entity type registration failures.
- `packages/agents-runtime/src/entity-timeline.ts`: create shared
timeline join indexes, keep CLI/UI consumers on the same live query, and
order live deltas by stream sequence.
- `packages/agents-runtime/test/create-handler.test.ts`,
`packages/agents-runtime/test/entity-timeline.test.ts`: regression
coverage for fetch detail, live delta ordering, top-level live timeline
row interleaving, and live run child item ordering.
- `packages/agents/src/server.ts`,
`packages/agents/test/builtin-pull-wake-registration.test.ts`: default
built-in type dispatch to the local runner and cover it.
- `packages/electric-ax/package.json`, `pnpm-lock.yaml`: align
`electric-ax` with the same TanStack DB/TypeScript peer instance as
`agents-runtime`.
- `packages/electric-ax/src/start.ts`,
`packages/electric-ax/src/index.ts`: align quickstart runner
owner/server headers and default CLI principal behavior.
- `packages/electric-ax/src/entity-stream-db.ts`,
`packages/electric-ax/test/entity-stream-db.test.ts`: delegate CLI
stream setup to the agents-runtime helper so timeline ordering matches
the web UI.
- `packages/electric-ax/src/observe-ui.tsx`,
`packages/electric-ax/test/observe-ui.test.tsx`: render observe from the
shared live query, fix terminal wrapping, send interactive messages with
stable optimistic keys, and await send txids.
- `packages/electric-ax/src/version.ts`,
`packages/electric-ax/tsdown*.config.ts`: inject the matching
agents-server Docker image tag into release builds.
- `packages/electric-ax/test/cli.test.ts`,
`packages/electric-ax/test/start.test.ts`: coverage for
principal/default header behavior.
-
`packages/agents-server-ui/src/components/settings/pages/CredentialsPage.tsx`:
clarify browser-only provider credentials copy.
---------
Co-authored-by: Sam Willis <sam.willis@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>1 parent 37b9922 commit ee0da19
22 files changed
Lines changed: 999 additions & 151 deletions
File tree
- .changeset
- packages
- agents-runtime
- src
- test
- agents-server-ui/src/components/settings/pages
- agents
- src
- test
- electric-ax
- src
- test
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | | - | |
| 11 | + | |
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
567 | 567 | | |
568 | 568 | | |
569 | 569 | | |
570 | | - | |
571 | | - | |
572 | | - | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
573 | 577 | | |
574 | 578 | | |
575 | 579 | | |
576 | | - | |
577 | | - | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
578 | 587 | | |
579 | 588 | | |
580 | 589 | | |
| |||
758 | 767 | | |
759 | 768 | | |
760 | 769 | | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
| 777 | + | |
| 778 | + | |
| 779 | + | |
| 780 | + | |
761 | 781 | | |
762 | 782 | | |
763 | 783 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| 13 | + | |
13 | 14 | | |
14 | 15 | | |
15 | 16 | | |
16 | 17 | | |
17 | 18 | | |
18 | | - | |
| 19 | + | |
19 | 20 | | |
20 | 21 | | |
21 | 22 | | |
| |||
1316 | 1317 | | |
1317 | 1318 | | |
1318 | 1319 | | |
| 1320 | + | |
| 1321 | + | |
| 1322 | + | |
| 1323 | + | |
| 1324 | + | |
| 1325 | + | |
| 1326 | + | |
| 1327 | + | |
| 1328 | + | |
| 1329 | + | |
| 1330 | + | |
| 1331 | + | |
| 1332 | + | |
| 1333 | + | |
| 1334 | + | |
| 1335 | + | |
| 1336 | + | |
| 1337 | + | |
| 1338 | + | |
| 1339 | + | |
| 1340 | + | |
| 1341 | + | |
| 1342 | + | |
| 1343 | + | |
| 1344 | + | |
| 1345 | + | |
| 1346 | + | |
| 1347 | + | |
| 1348 | + | |
| 1349 | + | |
| 1350 | + | |
| 1351 | + | |
| 1352 | + | |
| 1353 | + | |
| 1354 | + | |
| 1355 | + | |
| 1356 | + | |
| 1357 | + | |
| 1358 | + | |
| 1359 | + | |
| 1360 | + | |
1319 | 1361 | | |
1320 | 1362 | | |
1321 | 1363 | | |
| |||
1339 | 1381 | | |
1340 | 1382 | | |
1341 | 1383 | | |
| 1384 | + | |
1342 | 1385 | | |
1343 | 1386 | | |
1344 | 1387 | | |
| |||
1423 | 1466 | | |
1424 | 1467 | | |
1425 | 1468 | | |
| 1469 | + | |
| 1470 | + | |
| 1471 | + | |
| 1472 | + | |
| 1473 | + | |
| 1474 | + | |
| 1475 | + | |
| 1476 | + | |
1426 | 1477 | | |
1427 | 1478 | | |
1428 | 1479 | | |
| |||
1435 | 1486 | | |
1436 | 1487 | | |
1437 | 1488 | | |
1438 | | - | |
1439 | | - | |
| 1489 | + | |
| 1490 | + | |
| 1491 | + | |
| 1492 | + | |
| 1493 | + | |
1440 | 1494 | | |
1441 | 1495 | | |
1442 | 1496 | | |
1443 | 1497 | | |
1444 | | - | |
| 1498 | + | |
1445 | 1499 | | |
1446 | 1500 | | |
1447 | 1501 | | |
1448 | 1502 | | |
1449 | 1503 | | |
1450 | 1504 | | |
1451 | 1505 | | |
1452 | | - | |
1453 | | - | |
1454 | | - | |
1455 | | - | |
| 1506 | + | |
1456 | 1507 | | |
1457 | 1508 | | |
1458 | 1509 | | |
1459 | 1510 | | |
1460 | 1511 | | |
1461 | 1512 | | |
1462 | | - | |
| 1513 | + | |
1463 | 1514 | | |
1464 | 1515 | | |
1465 | 1516 | | |
| |||
1486 | 1537 | | |
1487 | 1538 | | |
1488 | 1539 | | |
1489 | | - | |
| 1540 | + | |
1490 | 1541 | | |
1491 | 1542 | | |
1492 | 1543 | | |
| |||
1497 | 1548 | | |
1498 | 1549 | | |
1499 | 1550 | | |
1500 | | - | |
1501 | | - | |
1502 | | - | |
1503 | | - | |
| 1551 | + | |
1504 | 1552 | | |
1505 | 1553 | | |
1506 | 1554 | | |
| |||
1667 | 1715 | | |
1668 | 1716 | | |
1669 | 1717 | | |
| 1718 | + | |
1670 | 1719 | | |
1671 | 1720 | | |
1672 | 1721 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
648 | 648 | | |
649 | 649 | | |
650 | 650 | | |
| 651 | + | |
| 652 | + | |
| 653 | + | |
| 654 | + | |
| 655 | + | |
| 656 | + | |
| 657 | + | |
| 658 | + | |
| 659 | + | |
| 660 | + | |
| 661 | + | |
| 662 | + | |
| 663 | + | |
| 664 | + | |
| 665 | + | |
| 666 | + | |
| 667 | + | |
651 | 668 | | |
652 | 669 | | |
653 | 670 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1835 | 1835 | | |
1836 | 1836 | | |
1837 | 1837 | | |
| 1838 | + | |
| 1839 | + | |
| 1840 | + | |
| 1841 | + | |
| 1842 | + | |
| 1843 | + | |
| 1844 | + | |
| 1845 | + | |
| 1846 | + | |
| 1847 | + | |
| 1848 | + | |
| 1849 | + | |
| 1850 | + | |
| 1851 | + | |
| 1852 | + | |
| 1853 | + | |
| 1854 | + | |
| 1855 | + | |
| 1856 | + | |
| 1857 | + | |
| 1858 | + | |
| 1859 | + | |
| 1860 | + | |
| 1861 | + | |
| 1862 | + | |
| 1863 | + | |
| 1864 | + | |
| 1865 | + | |
| 1866 | + | |
| 1867 | + | |
| 1868 | + | |
| 1869 | + | |
| 1870 | + | |
| 1871 | + | |
| 1872 | + | |
| 1873 | + | |
| 1874 | + | |
| 1875 | + | |
| 1876 | + | |
| 1877 | + | |
| 1878 | + | |
| 1879 | + | |
| 1880 | + | |
| 1881 | + | |
| 1882 | + | |
| 1883 | + | |
| 1884 | + | |
| 1885 | + | |
| 1886 | + | |
| 1887 | + | |
| 1888 | + | |
| 1889 | + | |
| 1890 | + | |
| 1891 | + | |
| 1892 | + | |
1838 | 1893 | | |
1839 | 1894 | | |
1840 | 1895 | | |
| |||
2497 | 2552 | | |
2498 | 2553 | | |
2499 | 2554 | | |
| 2555 | + | |
| 2556 | + | |
| 2557 | + | |
| 2558 | + | |
| 2559 | + | |
| 2560 | + | |
| 2561 | + | |
| 2562 | + | |
| 2563 | + | |
| 2564 | + | |
| 2565 | + | |
| 2566 | + | |
| 2567 | + | |
| 2568 | + | |
| 2569 | + | |
| 2570 | + | |
| 2571 | + | |
| 2572 | + | |
| 2573 | + | |
| 2574 | + | |
| 2575 | + | |
| 2576 | + | |
| 2577 | + | |
| 2578 | + | |
| 2579 | + | |
| 2580 | + | |
| 2581 | + | |
| 2582 | + | |
| 2583 | + | |
| 2584 | + | |
| 2585 | + | |
| 2586 | + | |
| 2587 | + | |
| 2588 | + | |
| 2589 | + | |
| 2590 | + | |
| 2591 | + | |
| 2592 | + | |
| 2593 | + | |
| 2594 | + | |
| 2595 | + | |
| 2596 | + | |
| 2597 | + | |
| 2598 | + | |
| 2599 | + | |
| 2600 | + | |
| 2601 | + | |
| 2602 | + | |
| 2603 | + | |
| 2604 | + | |
| 2605 | + | |
| 2606 | + | |
| 2607 | + | |
| 2608 | + | |
| 2609 | + | |
| 2610 | + | |
| 2611 | + | |
| 2612 | + | |
| 2613 | + | |
| 2614 | + | |
| 2615 | + | |
| 2616 | + | |
| 2617 | + | |
| 2618 | + | |
| 2619 | + | |
| 2620 | + | |
| 2621 | + | |
| 2622 | + | |
| 2623 | + | |
| 2624 | + | |
| 2625 | + | |
| 2626 | + | |
| 2627 | + | |
| 2628 | + | |
| 2629 | + | |
| 2630 | + | |
| 2631 | + | |
| 2632 | + | |
| 2633 | + | |
| 2634 | + | |
| 2635 | + | |
| 2636 | + | |
| 2637 | + | |
| 2638 | + | |
| 2639 | + | |
2500 | 2640 | | |
2501 | 2641 | | |
2502 | 2642 | | |
| |||
0 commit comments