You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[console] Agent-to-agent connections have no UI — the pump is invisible #182
The "pump" (agent-to-agent connections, agent_connections, migration 0056) has no console UI at all. Creating a connection is a raw API call:
POST /v1/instances/:id/connections { eventType, targetInstanceId, action, config }
The delivery outbox added in migration 0058 is API-only too:
GET /v1/instances/:id/connections/deliveries?status=pending|delivered|dead
POST /v1/instances/:id/connections/deliveries/:did/replay
So the reliability work — retry, dead-lettering, replay, routing filters, trace correlation — is invisible to the person who needs it. A chain that stalls is discoverable only by curl.
This is the difference between the chain being a product feature and being an internal capability. The lead chain (Lead Finder -> Website Builder -> Outreach) cannot be assembled by a user today.
Scope
Connections list/create/delete on the instance Settings surface: source event, target instance, action, and the routing filter.
Filter editor reusing the [{field,op,value}] vocabulary (the same predicate language as the filter step) with create-time validation surfaced inline.
Delivery view: pending / delivered / dead with attempt count, next attempt, last error, and a Replay action for dead letters.
Show the correlation trace id and link a delivery to the child run it started.
Acceptance
A user can wire Lead Finder -> Website Builder -> Outreach entirely from the console.
A dead letter is visible with its reason and can be replayed in one click.
An invalid filter is rejected at create time with a readable message.
Owner-scoped throughout.
Related: #19 covers the same gap for triggers; this is the connections half.
Code audit update: this is partially shipped, but still not complete. Current Settings > Teamwork UI now supports listing/creating/deleting connections, a simple routing filter, dead-letter listing, and replay. The remaining gap from this issue is broader delivery observability: pending/delivered/dead views, trace/run links, and a fuller operator UX for debugging message flow. Keeping open.
First, a correction to the ticket. "No console UI at all" was stale — the Teamwork card on the instance Settings surface (store/console/src/tabs/TeamworkSection.tsx, commits 802d764 / f5f521a / dfc5c91) already did connections list/create/delete, a one-clause filter editor, and dead-letter replay. What was actually missing was everything that makes the pump legible, plus one bug that made the headline use case unreachable.
The bug. The filter editor sent every value as a raw string. The server compares gt/gte/lt/lte numerically and rejects a string at create time, and eq is strict (===), so a text-typed "4" never equals a numeric 4 in the payload. "Only Sydney leads rated 4+" — the canonical example the routing filter exists for — was therefore impossible to create from the console, and the near-miss variants created fine and then silently dropped every event. Values are now typed (numbers, true/false/null), with quoting ("2000") as the escape hatch for a postcode that must stay text. The editor also takes several clauses with an all/any toggle; one AND-ed clause could not express the two-clause example either.
What shipped
Connection rows state their own health, derived from the deliveries they produced: N undelivered / N retrying / N delivered. The negative is the point: deliverEvent writes an outbox row only for a payload that passes the filter, so a filtered connection with zero deliveries has never once fired — surfaced as "nothing has matched this filter yet". That is the failure mode where a chain is stopped and every row still reads "enabled".
Delivery log across every status, not just dead. Pending rows say when the next attempt lands and how many are left ("2 attempts failed, retrying in 15m · 3 left"); delivered rows admit when they only succeeded on a retry; an account-wide headline says "N events never arrived" before you expand anything. Replay stays one click on a dead letter.
Trace correlation: a delivery's trace id expands inline into the run it started, reading /v1/instances/:id/trace?trace_id=. The emitting run rides onto the delivery and chain.link is logged under the parent trace, so this is what joins two agents' runs into one story.
API: GET …/connections/deliveries now returns source and sourceInstanceId. Without them an account-wide row from a shared outbox is unreadable.
Tests. The rules that turn a row into a sentence are pure and live in store/console/src/lib/teamwork.ts — 41 unit tests in teamwork.test.ts (filter parsing/summarising, literal typing, status grouping, countdown, connection health). Suite: 205 files / 2903 tests green, pnpm typecheck green, 47/47 console e2e green including the 375px no-overflow guard. One new e2e covers the acceptance path end to end: filter summary on the row, dead letter with its reason, replay in one click.
Deliberately left out
No connection enable/disable toggle. There is no PATCH route; enabled is only ever set to 1 at create. The UI reads and renders the flag ("disabled — it routes nothing") so the column is honest, but wiring a toggle means a new route, which belongs in its own change.
No config.params editor.buildConnectionConfig supports the field and it is unit-tested, but nothing in the UI writes it yet — a static-params form needs a schema to key against, and guessing one would be worse than the API call.
No standalone trace/run viewer. The console has no trace route at all, so a delivery expands its own trace inline rather than deep-linking somewhere that does not exist. A proper trace surface is its own ticket.
The log is capped at the 200 most recent deliveries. "Nothing has matched this filter yet" is therefore a statement about the recent log, not all history — worded accordingly.
Activity
feat(mcp): make composing a multi-agent system self-serve (#182, #183, …
feat(console): Teamwork — wire supervision + connections with a mouse (…
feat(console): autonomous-runs history (#158, #182)
feat: escalation ladder, repo setting attaches a repo, routing-filter…
serge-ivo commentedon Aug 6, 2026
Code audit update: this is partially shipped, but still not complete. Current Settings > Teamwork UI now supports listing/creating/deleting connections, a simple routing filter, dead-letter listing, and replay. The remaining gap from this issue is broader delivery observability: pending/delivered/dead views, trace/run links, and a fuller operator UX for debugging message flow. Keeping open.
feat(console): the pump's failures are the part you need to see (#182)
serge-ivo commentedon Aug 6, 2026
Shipped in e16af12.
First, a correction to the ticket. "No console UI at all" was stale — the Teamwork card on the instance Settings surface (
store/console/src/tabs/TeamworkSection.tsx, commits 802d764 / f5f521a / dfc5c91) already did connections list/create/delete, a one-clause filter editor, and dead-letter replay. What was actually missing was everything that makes the pump legible, plus one bug that made the headline use case unreachable.The bug. The filter editor sent every value as a raw string. The server compares
gt/gte/lt/ltenumerically and rejects a string at create time, andeqis strict (===), so a text-typed"4"never equals a numeric4in the payload. "Only Sydney leads rated 4+" — the canonical example the routing filter exists for — was therefore impossible to create from the console, and the near-miss variants created fine and then silently dropped every event. Values are now typed (numbers,true/false/null), with quoting ("2000") as the escape hatch for a postcode that must stay text. The editor also takes several clauses with an all/any toggle; one AND-ed clause could not express the two-clause example either.What shipped
N undelivered/N retrying/N delivered. The negative is the point:deliverEventwrites an outbox row only for a payload that passes the filter, so a filtered connection with zero deliveries has never once fired — surfaced as "nothing has matched this filter yet". That is the failure mode where a chain is stopped and every row still reads "enabled"./v1/instances/:id/trace?trace_id=. The emitting run rides onto the delivery andchain.linkis logged under the parent trace, so this is what joins two agents' runs into one story.GET …/connections/deliveriesnow returnssourceandsourceInstanceId. Without them an account-wide row from a shared outbox is unreadable.Tests. The rules that turn a row into a sentence are pure and live in
store/console/src/lib/teamwork.ts— 41 unit tests inteamwork.test.ts(filter parsing/summarising, literal typing, status grouping, countdown, connection health). Suite: 205 files / 2903 tests green,pnpm typecheckgreen, 47/47 console e2e green including the 375px no-overflow guard. One new e2e covers the acceptance path end to end: filter summary on the row, dead letter with its reason, replay in one click.Deliberately left out
enabledis only ever set to 1 at create. The UI reads and renders the flag ("disabled — it routes nothing") so the column is honest, but wiring a toggle means a new route, which belongs in its own change.config.paramseditor.buildConnectionConfigsupports the field and it is unit-tested, but nothing in the UI writes it yet — a static-params form needs a schema to key against, and guessing one would be worse than the API call.fix(coding): a repo's address is stored once, and it can be corrected (…