feat(comms): delegation ledger — task ids, busy retries, durable receipts, read-back tools - #566
Merged
Merged
Conversation
…ceipts, and read-back tools Bots could hand work to a peer but never learn what happened: delegate_bot was fire-and-forget by contract, a busy target CANCELED the handoff outright, and ask_bot's synchronous wait was the only way to get an answer back. Modeled on fx's durable subagent message ledger, adapted to the existing queue in delegations.ts: - queueDelegation returns a task id; the delegating bot receives it in the tool reply as its claim ticket. - A busy target no longer cancels the handoff. The item stays queued with a bounded retry count (3); any of the target's turns settling re-drains the waiting source threads. Fixed en route: a drain request arriving while a drain was already running was silently dropped by the drainingThreads lock — precisely where the waiting-on retry lands — so requests are now remembered and honored when the running drain finishes. - Every terminal outcome writes a durable receipt (done/failed/denied/ busy_gave_up/dropped/error + the peer's bounded reply), persisted to delegation-receipts.json, pruned by count (100) and age (48h), loaded at boot, and written before any mirror short-circuits. - New MCP tools: check_delegation (status now) and wait_delegation (bounded long-poll up to 240s — ONE parked HTTP request instead of a model inference per status peek), over GET /api/internal/delegations/:id with sender/thread ownership enforced. Flat schemas, guiding errors. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reachedNext included review available in 10 seconds. View limit detailsLimit details: You’ve used all 4 included reviews currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This was referenced Aug 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
The async handoff path (
delegate_bot) grows the missing half: the delegating bot can now learn what happened.queueDelegationreturns an id; the tool reply hands it to the bot as a claim ticket.drainDelegationscall landing while a drain was already running was silently dropped by thedrainingThreadslock — exactly where the waiting-on retry lands — now remembered viaqueuedRedrainsand honored when the running drain finishes.done/failed/denied/busy_gave_up/dropped/error+ the peer's bounded reply, 4k chars) persists todelegation-receipts.json, pruned by count (100) and age (48h), loaded at boot, written before any mirror short-circuits so a receipt exists even when the channel/target is gone.check_delegation(status now) andwait_delegation(bounded long-poll, ≤240s — one parked HTTP request instead of a model inference per peek) overGET /api/internal/delegations/:id, with sender+thread ownership enforced (403 on cross-thread reads). Flat schemas and guiding errors per CONTRIBUTING's MCP rules.Why
The third borrow from the vercel-labs/fx architecture review (after #564 and #563): fx's subagent ledger lets a parent delegate, keep working, and pull bounded results later. Our
delegate_botwas fire-and-forget by contract — the requester never learned the outcome — andask_bot's 4-minute synchronous wait was the only alternative. Per the maintainer's direction this replaces the parallel efforts in #516/#423 with one implementation built on main's existingdelegations.tsqueue (whose design — durable pending file, drain-on-settle, approval-at-drain — is kept intact).How it was verified
server/delegations.test.ts— 27 pass, run 3× for determinism. New: busy→waiting-chip→retention→retry-drain dispatch with the task id riding to the target; bounded give-up with abusy_gave_upreceipt; receipts surviving_loadPending()reload and pruning at 100;droppedreceipts on discard. The pre-existing busy test updated from "canceled" to the new waiting semantics.server/drivers/agents-proxy.test.ts+server/comms.test.ts— 42 pass. New: task-id suffix in the delegate reply; flat-schema guard for both new tools; guided error without a wire call;wait_ms/ownership params on the read-back wire.tsc -p tsconfig.server.jsonclean. Lint:server/index.tsexact parity (107→107);delegations.ts+7 and proxy +2 findings, allno-runtime-typeof-family hits in the same style as the surrounding loaders/wire-parsers (lint is not a CI gate; flagged for transparency — the new receipts loader narrows every field into locals with a single SAFETY-commented view).Checklist
pnpm typecheckand the touched test files pass locallydist-server/editsshell: true🤖 Generated with Claude Code