From f7300a7e748becc36c7026358ac7e468113f59f9 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 18 May 2026 18:06:09 +0000 Subject: [PATCH 1/4] refactor(cli/drive): split runDriveSession to drop below complexity 15 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #897. `runDriveSession` carried complexity 23 (limit 15) because one function owned name validation, broker connection resolution, the mode flip, snapshot handling, terminal-rows fallback, initial PTY-size sync, and the entire WS/stdin/keybind/resize session loop. Extracted CLI-side helpers (no SDK surface added): - `prepareDriveAttachTarget` — trim name + resolve broker connection - `switchWorkerToManualFlush` — read prior mode + flip with error handling - `captureSnapshotForDrive` — snapshot + abort/restore on hard errors - `pickInitialTerminalRows` — status-line row fallback chain - `syncInitialPtySize` — best-effort initial resize forward - `runDriveSessionLoop` — WS/stdin/keybind/resize state machine Public exports (`runDriveSession`, `registerDriveCommands`, `KeybindParser`, `classifyWsEvent`, `renderStatusLine`, HTTP helpers, `DriveDependencies`, etc.) are unchanged so existing tests and `passthrough.ts` continue to compile and pass. No behavior change: same call order, same error messages, same teardown semantics. All 387 CLI tests pass; ESLint complexity warning on drive.ts cleared. --- .trajectories/active/traj_v87cyrs8dke9.json | 106 - .../completed/2026-05/traj_v87cyrs8dke9.json | 416 ++ .../completed/2026-05/traj_v87cyrs8dke9.md | 68 + .../2026-05/traj_v87cyrs8dke9.trace.json | 4267 +++++++++++++++++ .trajectories/index.json | 9 +- src/cli/commands/drive.ts | 243 +- 6 files changed, 4920 insertions(+), 189 deletions(-) delete mode 100644 .trajectories/active/traj_v87cyrs8dke9.json create mode 100644 .trajectories/completed/2026-05/traj_v87cyrs8dke9.json create mode 100644 .trajectories/completed/2026-05/traj_v87cyrs8dke9.md create mode 100644 .trajectories/completed/2026-05/traj_v87cyrs8dke9.trace.json diff --git a/.trajectories/active/traj_v87cyrs8dke9.json b/.trajectories/active/traj_v87cyrs8dke9.json deleted file mode 100644 index d41f17903..000000000 --- a/.trajectories/active/traj_v87cyrs8dke9.json +++ /dev/null @@ -1,106 +0,0 @@ -{ - "id": "traj_v87cyrs8dke9", - "version": 1, - "task": { - "title": "Upgrade .agentworkforce personas to latest 3.x shape" - }, - "status": "active", - "startedAt": "2026-05-14T14:28:34.155Z", - "agents": [ - { - "name": "default", - "role": "lead", - "joinedAt": "2026-05-18T04:01:59.334Z" - } - ], - "chapters": [ - { - "id": "chap_alw03jfffmtf", - "title": "Work", - "agentName": "default", - "startedAt": "2026-05-18T04:01:59.334Z", - "events": [ - { - "ts": 1779076919335, - "type": "decision", - "content": "Factored broker-connection discovery into src/cli/lib/broker-connection.ts", - "raw": { - "question": "Factored broker-connection discovery into src/cli/lib/broker-connection.ts", - "chosen": "Factored broker-connection discovery into src/cli/lib/broker-connection.ts", - "alternatives": [], - "reasoning": "Drive and view both need the same flag/env/connection.json fallback chain; pulling it out keeps both verbs aligned and unblocks future relay/new/run verbs from sub-PR 4." - }, - "significance": "high" - }, - { - "ts": 1779076919835, - "type": "decision", - "content": "Hand-rolled keybind state machine in drive.ts instead of pulling in readline.emitKeypressEvents", - "raw": { - "question": "Hand-rolled keybind state machine in drive.ts instead of pulling in readline.emitKeypressEvents", - "chosen": "Hand-rolled keybind state machine in drive.ts instead of pulling in readline.emitKeypressEvents", - "alternatives": [], - "reasoning": "All bindings are ASCII control chars (Ctrl+G/Ctrl+C/Ctrl+B prefix). Tiny stateful parser handles cross-chunk Ctrl+B prefix cleanly and is trivially testable." - }, - "significance": "high" - }, - { - "ts": 1779082846347, - "type": "decision", - "content": "Overload existing 'run' command rather than registering a second one — Commander only allows one verb per name and there's already a workflow-runner 'run ': Overload existing 'run' command rather than registering a second one — Commander only allows one verb per name and there's already a workflow-runner 'run '", - "raw": { - "question": "Overload existing 'run' command rather than registering a second one — Commander only allows one verb per name and there's already a workflow-runner 'run '", - "chosen": "Overload existing 'run' command rather than registering a second one — Commander only allows one verb per name and there's already a workflow-runner 'run '", - "alternatives": [], - "reasoning": "Adds -n/--mode/--ephemeral flags and dispatches to spawn-and-attach when -n is set, otherwise falls through to the existing workflow runner. Preserves backward compatibility for all existing 'run ' invocations." - }, - "significance": "high" - }, - { - "ts": 1779083525684, - "type": "decision", - "content": "relay copies-and-trims drive rather than sharing a base class: relay copies-and-trims drive rather than sharing a base class", - "raw": { - "question": "relay copies-and-trims drive rather than sharing a base class", - "chosen": "relay copies-and-trims drive rather than sharing a base class", - "alternatives": [], - "reasoning": "Trimmed surface (no queue UI, no Ctrl+G, no delivery_queued/agent_pending_drained tracking) is small enough that an abstraction layer would cost more clarity than it saves. drive.ts is documented as canonical; relay.ts header comment points there. If a third sibling lands, factor." - }, - "significance": "high" - }, - { - "ts": 1779083526914, - "type": "decision", - "content": "Silent -n alias dispatched via argv pre-parser before commander runs, not via a hidden internal verb: Silent -n alias dispatched via argv pre-parser before commander runs, not via a hidden internal verb", - "raw": { - "question": "Silent -n alias dispatched via argv pre-parser before commander runs, not via a hidden internal verb", - "chosen": "Silent -n alias dispatched via argv pre-parser before commander runs, not via a hidden internal verb", - "alternatives": [], - "reasoning": "Pre-parser (parseVerblessAlias) catches the four canonical shapes (-n NAME, --name NAME, -nNAME, --name=NAME), refuses ambiguous (-n NAME ) and help/version, then hands off to runSpawnAndAttach with mode='relay' and ephemeral=true. Single code path with run -n NAME CLI guarantees byte-equivalence; bootstrap.test.ts asserts the triplet extraction matches what commander's run -n action sees." - }, - "significance": "high" - }, - { - "ts": 1779083917618, - "type": "decision", - "content": "Drop the 'run' verb, fold spawn-and-attach into 'new --attach': Drop the 'run' verb, fold spawn-and-attach into 'new --attach'", - "raw": { - "question": "Drop the 'run' verb, fold spawn-and-attach into 'new --attach'", - "chosen": "Drop the 'run' verb, fold spawn-and-attach into 'new --attach'", - "alternatives": [], - "reasoning": "Reviewer pushback: the _actionHandler.apply() hack to overload commander's run was a code smell signaling the verb naming was wrong. Moving the composition to 'new --attach' eliminates the workflow-runner name conflict cleanly, keeps the verb taxonomy simple (new spawns, view/drive/relay sessions), and the alias retargets transparently — both -n alias and new --attach call the same runSpawnAndAttach helper, byte-equivalence preserved." - }, - "significance": "high" - } - ] - } - ], - "commits": [], - "filesChanged": [], - "projectId": "/Users/will/Projects/AgentWorkforce/relay", - "tags": [], - "_trace": { - "startRef": "83ecfbca9cd87540629ae0a9b2f155cd2c3070cf", - "endRef": "83ecfbca9cd87540629ae0a9b2f155cd2c3070cf" - } -} diff --git a/.trajectories/completed/2026-05/traj_v87cyrs8dke9.json b/.trajectories/completed/2026-05/traj_v87cyrs8dke9.json new file mode 100644 index 000000000..6d25cf404 --- /dev/null +++ b/.trajectories/completed/2026-05/traj_v87cyrs8dke9.json @@ -0,0 +1,416 @@ +{ + "id": "traj_v87cyrs8dke9", + "version": 1, + "task": { + "title": "Upgrade .agentworkforce personas to latest 3.x shape" + }, + "status": "completed", + "startedAt": "2026-05-14T14:28:34.155Z", + "completedAt": "2026-05-18T18:06:04.950Z", + "agents": [ + { + "name": "default", + "role": "lead", + "joinedAt": "2026-05-18T04:01:59.334Z" + } + ], + "chapters": [ + { + "id": "chap_alw03jfffmtf", + "title": "Work", + "agentName": "default", + "startedAt": "2026-05-18T04:01:59.334Z", + "endedAt": "2026-05-18T18:06:04.950Z", + "events": [ + { + "ts": 1779076919335, + "type": "decision", + "content": "Factored broker-connection discovery into src/cli/lib/broker-connection.ts", + "raw": { + "question": "Factored broker-connection discovery into src/cli/lib/broker-connection.ts", + "chosen": "Factored broker-connection discovery into src/cli/lib/broker-connection.ts", + "alternatives": [], + "reasoning": "Drive and view both need the same flag/env/connection.json fallback chain; pulling it out keeps both verbs aligned and unblocks future relay/new/run verbs from sub-PR 4." + }, + "significance": "high" + }, + { + "ts": 1779076919835, + "type": "decision", + "content": "Hand-rolled keybind state machine in drive.ts instead of pulling in readline.emitKeypressEvents", + "raw": { + "question": "Hand-rolled keybind state machine in drive.ts instead of pulling in readline.emitKeypressEvents", + "chosen": "Hand-rolled keybind state machine in drive.ts instead of pulling in readline.emitKeypressEvents", + "alternatives": [], + "reasoning": "All bindings are ASCII control chars (Ctrl+G/Ctrl+C/Ctrl+B prefix). Tiny stateful parser handles cross-chunk Ctrl+B prefix cleanly and is trivially testable." + }, + "significance": "high" + }, + { + "ts": 1779082846347, + "type": "decision", + "content": "Overload existing 'run' command rather than registering a second one — Commander only allows one verb per name and there's already a workflow-runner 'run ': Overload existing 'run' command rather than registering a second one — Commander only allows one verb per name and there's already a workflow-runner 'run '", + "raw": { + "question": "Overload existing 'run' command rather than registering a second one — Commander only allows one verb per name and there's already a workflow-runner 'run '", + "chosen": "Overload existing 'run' command rather than registering a second one — Commander only allows one verb per name and there's already a workflow-runner 'run '", + "alternatives": [], + "reasoning": "Adds -n/--mode/--ephemeral flags and dispatches to spawn-and-attach when -n is set, otherwise falls through to the existing workflow runner. Preserves backward compatibility for all existing 'run ' invocations." + }, + "significance": "high" + }, + { + "ts": 1779083525684, + "type": "decision", + "content": "relay copies-and-trims drive rather than sharing a base class: relay copies-and-trims drive rather than sharing a base class", + "raw": { + "question": "relay copies-and-trims drive rather than sharing a base class", + "chosen": "relay copies-and-trims drive rather than sharing a base class", + "alternatives": [], + "reasoning": "Trimmed surface (no queue UI, no Ctrl+G, no delivery_queued/agent_pending_drained tracking) is small enough that an abstraction layer would cost more clarity than it saves. drive.ts is documented as canonical; relay.ts header comment points there. If a third sibling lands, factor." + }, + "significance": "high" + }, + { + "ts": 1779083526914, + "type": "decision", + "content": "Silent -n alias dispatched via argv pre-parser before commander runs, not via a hidden internal verb: Silent -n alias dispatched via argv pre-parser before commander runs, not via a hidden internal verb", + "raw": { + "question": "Silent -n alias dispatched via argv pre-parser before commander runs, not via a hidden internal verb", + "chosen": "Silent -n alias dispatched via argv pre-parser before commander runs, not via a hidden internal verb", + "alternatives": [], + "reasoning": "Pre-parser (parseVerblessAlias) catches the four canonical shapes (-n NAME, --name NAME, -nNAME, --name=NAME), refuses ambiguous (-n NAME ) and help/version, then hands off to runSpawnAndAttach with mode='relay' and ephemeral=true. Single code path with run -n NAME CLI guarantees byte-equivalence; bootstrap.test.ts asserts the triplet extraction matches what commander's run -n action sees." + }, + "significance": "high" + }, + { + "ts": 1779083917618, + "type": "decision", + "content": "Drop the 'run' verb, fold spawn-and-attach into 'new --attach': Drop the 'run' verb, fold spawn-and-attach into 'new --attach'", + "raw": { + "question": "Drop the 'run' verb, fold spawn-and-attach into 'new --attach'", + "chosen": "Drop the 'run' verb, fold spawn-and-attach into 'new --attach'", + "alternatives": [], + "reasoning": "Reviewer pushback: the _actionHandler.apply() hack to overload commander's run was a code smell signaling the verb naming was wrong. Moving the composition to 'new --attach' eliminates the workflow-runner name conflict cleanly, keeps the verb taxonomy simple (new spawns, view/drive/relay sessions), and the alias retargets transparently — both -n alias and new --attach call the same runSpawnAndAttach helper, byte-equivalence preserved." + }, + "significance": "high" + }, + { + "ts": 1779127453088, + "type": "decision", + "content": "Refactor runDriveSession by extracting prep helpers + session loop: Refactor runDriveSession by extracting prep helpers + session loop", + "raw": { + "question": "Refactor runDriveSession by extracting prep helpers + session loop", + "chosen": "Refactor runDriveSession by extracting prep helpers + session loop", + "alternatives": [], + "reasoning": "ESLint complexity 23 > 15. Splitting validate target, mode switch, snapshot handling, PTY size sync, and the Promise-based session runner into helpers preserves behavior, reduces parent complexity to under 15, keeps all public exports stable for tests and passthrough.ts" + }, + "significance": "high" + } + ] + } + ], + "retrospective": { + "summary": "Refactored runDriveSession (complexity 23 → under 15) by extracting prepareDriveAttachTarget, switchWorkerToManualFlush, captureSnapshotForDrive, syncInitialPtySize, pickInitialTerminalRows, and runDriveSessionLoop. All 387 CLI tests still pass; ESLint warning resolved. Public API surface and exports unchanged so passthrough.ts and tests still work.", + "approach": "Standard approach", + "confidence": 0.9 + }, + "commits": [ + "e8502cb", + "850c8e0", + "de77083", + "88b12e6", + "da9e912", + "37c314f", + "355bc07", + "45b7c15", + "cd72e61", + "106b7d9", + "e3ac4fc", + "79bbfdd", + "3356ecd", + "2c59e77", + "80c0d6e", + "2c6abac", + "35ef0a8", + "0a44e6f", + "b9e421a", + "0d61a4e", + "35a331c", + "403cd00", + "765156f", + "a0ef506", + "592b0e6", + "c10a9d5", + "209e5d4", + "5fef86b", + "efcb1ed", + "0aeb71c", + "47a77c8", + "b73642e", + "09240e3", + "18ccff3", + "a1c0d06", + "b89d874", + "689eed0", + "1bd6e48", + "38abde4", + "789ec19", + "c48e884", + "e854f91", + "9ae5eb8", + "2f32141", + "e2834ca", + "df45489", + "52319ff", + "7295f1a", + "f31b91b", + "703843f", + "6326d18", + "3a76225", + "a30e5c8", + "885df69", + "40b8443", + "3e1e61a", + "687160a", + "51c9372", + "71358b1", + "1f01c87", + "c95a609", + "82a3883", + "5fc8a13", + "8de35af", + "5a652d6", + "5bcca47", + "b04bc64", + "6e0b7bc", + "48704de", + "18852c4", + "2b69c44", + "9cee216", + "3ed3530", + "3896309", + "52571b3", + "3d33cb5", + "d771a6e", + "cc491e3", + "0b0518a", + "89c2c36", + "9283abc", + "4a4c05a", + "43389b0", + "d3f02ce", + "633aa2a", + "56f3565", + "34765f5", + "88db665", + "881ae7a", + "ae04694", + "cbe2934", + "208b425", + "cbc70b0", + "bf993f2", + "759d351", + "a7ef2fc", + "34cd340", + "9de8d53", + "a8a3e0b", + "195f55f", + "e033054" + ], + "filesChanged": [ + ".agentworkforce/workforce/personas/nextjs-web-steward.agentsMdContent.md", + ".agentworkforce/workforce/personas/nextjs-web-steward.json", + ".claude/rules/docs-sync.md", + ".github/workflows/build-broker-binary.yml", + ".github/workflows/publish.yml", + ".github/workflows/remove-stale-previews.yml", + ".github/workflows/rust-ci.yml", + ".github/workflows/test.yml", + ".prettierignore", + ".trajectories/active/traj_v87cyrs8dke9.json", + ".trajectories/completed/2026-05/traj_0e8i20oitwvz.json", + ".trajectories/completed/2026-05/traj_0e8i20oitwvz.md", + ".trajectories/completed/2026-05/traj_0o6gb2wvk59t.json", + ".trajectories/completed/2026-05/traj_0o6gb2wvk59t.md", + ".trajectories/completed/2026-05/traj_2yicjxgajt0a.json", + ".trajectories/completed/2026-05/traj_2yicjxgajt0a.md", + ".trajectories/completed/2026-05/traj_3gjtcykvybt5.json", + ".trajectories/completed/2026-05/traj_3gjtcykvybt5.md", + ".trajectories/completed/2026-05/traj_4chzkm724ufo.json", + ".trajectories/completed/2026-05/traj_4chzkm724ufo.md", + ".trajectories/completed/2026-05/traj_4t07itef99ug.json", + ".trajectories/completed/2026-05/traj_4t07itef99ug.md", + ".trajectories/completed/2026-05/traj_4vucir4qvqa2.json", + ".trajectories/completed/2026-05/traj_4vucir4qvqa2.md", + ".trajectories/completed/2026-05/traj_6sjeohtm3php.json", + ".trajectories/completed/2026-05/traj_6sjeohtm3php.md", + ".trajectories/completed/2026-05/traj_7uznwzoxbao6.json", + ".trajectories/completed/2026-05/traj_7uznwzoxbao6.md", + ".trajectories/completed/2026-05/traj_7zu7et53ph3l.json", + ".trajectories/completed/2026-05/traj_7zu7et53ph3l.md", + ".trajectories/completed/2026-05/traj_947wzpddsg9j.json", + ".trajectories/completed/2026-05/traj_947wzpddsg9j.md", + ".trajectories/completed/2026-05/traj_9fdv7hxm0b60.json", + ".trajectories/completed/2026-05/traj_9fdv7hxm0b60.md", + ".trajectories/completed/2026-05/traj_9gq96irkj00s.json", + ".trajectories/completed/2026-05/traj_9gq96irkj00s.md", + ".trajectories/completed/2026-05/traj_cbmwd07phhm2.json", + ".trajectories/completed/2026-05/traj_cbmwd07phhm2.md", + ".trajectories/completed/2026-05/traj_d89s38ddu7cj.json", + ".trajectories/completed/2026-05/traj_d89s38ddu7cj.md", + ".trajectories/completed/2026-05/traj_erzd7j9nto9r.json", + ".trajectories/completed/2026-05/traj_erzd7j9nto9r.md", + ".trajectories/completed/2026-05/traj_f3arvbmmlomn.json", + ".trajectories/completed/2026-05/traj_f3arvbmmlomn.md", + ".trajectories/completed/2026-05/traj_fh8oosbijpwc.json", + ".trajectories/completed/2026-05/traj_fh8oosbijpwc.md", + ".trajectories/completed/2026-05/traj_fh8oosbijpwc.trace.json", + ".trajectories/completed/2026-05/traj_gh05rj5gwsap.json", + ".trajectories/completed/2026-05/traj_gh05rj5gwsap.md", + ".trajectories/completed/2026-05/traj_gh05rj5gwsap.trace.json", + ".trajectories/completed/2026-05/traj_hfkww5z7trxn.json", + ".trajectories/completed/2026-05/traj_hfkww5z7trxn.md", + ".trajectories/completed/2026-05/traj_hrsndfzk0qay.json", + ".trajectories/completed/2026-05/traj_hrsndfzk0qay.md", + ".trajectories/completed/2026-05/traj_n8duofq5vq1a.json", + ".trajectories/completed/2026-05/traj_n8duofq5vq1a.md", + ".trajectories/completed/2026-05/traj_o251whkvy9rl.json", + ".trajectories/completed/2026-05/traj_o251whkvy9rl.md", + ".trajectories/completed/2026-05/traj_piik8r6zu3i7.json", + ".trajectories/completed/2026-05/traj_piik8r6zu3i7.md", + ".trajectories/completed/2026-05/traj_ryf5sstno6p3.json", + ".trajectories/completed/2026-05/traj_ryf5sstno6p3.md", + ".trajectories/completed/2026-05/traj_tgism98me5na.json", + ".trajectories/completed/2026-05/traj_tgism98me5na.md", + ".trajectories/completed/2026-05/traj_v1wexlfur5zr.json", + ".trajectories/completed/2026-05/traj_v1wexlfur5zr.md", + ".trajectories/completed/2026-05/traj_whd40oxptlhn.trace.json", + ".trajectories/completed/2026-05/traj_wzzboitm85ee.json", + ".trajectories/completed/2026-05/traj_wzzboitm85ee.md", + ".trajectories/completed/2026-05/traj_ybcrij9wg8m1.json", + ".trajectories/completed/2026-05/traj_ybcrij9wg8m1.md", + ".trajectories/completed/2026-05/traj_zfa6skfr32vy.json", + ".trajectories/completed/2026-05/traj_zfa6skfr32vy.md", + ".trajectories/index.json", + "CHANGELOG.md", + "Cargo.lock", + "Cargo.toml", + "Cross.toml", + "package.json", + "packages/acp-bridge/package.json", + "packages/agent/package.json", + "packages/brand/package.json", + "packages/broker-darwin-arm64/package.json", + "packages/broker-darwin-x64/package.json", + "packages/broker-linux-arm64/package.json", + "packages/broker-linux-x64/package.json", + "packages/broker-win32-x64/package.json", + "packages/browser-primitive/package.json", + "packages/cloud/package.json", + "packages/config/package.json", + "packages/config/src/cli-registry.generated.ts", + "packages/credential-proxy/package.json", + "packages/events/package.json", + "packages/gateway/package.json", + "packages/github-primitive/package.json", + "packages/hooks/package.json", + "packages/memory/package.json", + "packages/openclaw/package.json", + "packages/personas/README.md", + "packages/personas/package.json", + "packages/personas/personas/agent-relay-e2e-conductor.json", + "packages/personas/personas/agent-relay-workflow.json", + "packages/personas/personas/cloud-sandbox-infra.json", + "packages/personas/personas/cloud-slack-proxy-guard.json", + "packages/personas/personas/opencode-workflow-specialist.json", + "packages/personas/personas/relay-orchestrator.json", + "packages/personas/personas/sage-proactive-rewirer.json", + "packages/personas/personas/sage-slack-egress-migrator.json", + "packages/personas/scripts/validate-personas.mjs", + "packages/policy/package.json", + "packages/sdk-py/pyproject.toml", + "packages/sdk-py/src/agent_relay/models.py", + "packages/sdk/package.json", + "packages/sdk/src/__tests__/models.test.ts", + "packages/sdk/src/__tests__/orchestration-upgrades.test.ts", + "packages/sdk/src/__tests__/transport.test.ts", + "packages/sdk/src/client.ts", + "packages/sdk/src/index.ts", + "packages/sdk/src/protocol.ts", + "packages/sdk/src/transport.ts", + "packages/sdk/src/workflows/__tests__/verification.test.ts", + "packages/sdk/src/workflows/verification.ts", + "packages/shared/cli-registry.yaml", + "packages/slack-primitive/package.json", + "packages/telemetry/package.json", + "packages/telemetry/src/posthog-config.ts", + "packages/trajectory/package.json", + "packages/user-directory/package.json", + "packages/utils/package.json", + "packages/workflow-types/package.json", + "packages/workflow-types/src/index.ts", + "src/auth.rs", + "src/cli/bootstrap.test.ts", + "src/cli/bootstrap.ts", + "src/cli/commands/agent-management.test.ts", + "src/cli/commands/agent-management.ts", + "src/cli/commands/core.test.ts", + "src/cli/commands/core.ts", + "src/cli/commands/drive.test.ts", + "src/cli/commands/drive.ts", + "src/cli/commands/new.test.ts", + "src/cli/commands/new.ts", + "src/cli/commands/passthrough.test.ts", + "src/cli/commands/passthrough.ts", + "src/cli/commands/rm.test.ts", + "src/cli/commands/rm.ts", + "src/cli/commands/view.test.ts", + "src/cli/commands/view.ts", + "src/cli/lib/attach.test.ts", + "src/cli/lib/attach.ts", + "src/cli/lib/broker-connection.test.ts", + "src/cli/lib/broker-connection.ts", + "src/cli/lib/broker-lifecycle.ts", + "src/cli/lib/sdk-client.ts", + "src/cli/lib/spawn-and-attach.ts", + "src/cost/pricing.ts", + "src/cost/tracker.test.ts", + "src/helpers.rs", + "src/lib.rs", + "src/listen_api.rs", + "src/main.rs", + "src/pty.rs", + "src/pty_worker.rs", + "src/readiness.rs", + "src/routing.rs", + "src/snapshot.rs", + "src/swarm.rs", + "src/telemetry.rs", + "src/types.rs", + "src/wait.rs", + "src/worker.rs", + "src/worker_request.rs", + "src/wrap.rs", + "tests/integration/broker/utils/broker-harness.ts", + "vitest.config.ts", + "web/app/blog/[slug]/page.tsx", + "web/components/docs/DocsNav.tsx", + "web/content/docs/cli-overview.mdx", + "web/content/docs/reference-broker-api.mdx", + "web/content/docs/reference-cli.mdx", + "web/content/docs/typescript-sdk.mdx", + "web/lib/docs-nav.ts", + "web/scripts/list-stale-stages.sh", + "workflows/cloud-connect/fix-agent-relay-utils-bundling.ts", + "workflows/cloud-connect/fix-cloud-connect-claude-hang.ts", + "workflows/cloud-connect/validate-cloud-connect-e2e.ts", + "workflows/relay-e2e-meta-workflow.ts" + ], + "projectId": "/Users/will/Projects/AgentWorkforce/relay", + "tags": [], + "_trace": { + "startRef": "83ecfbca9cd87540629ae0a9b2f155cd2c3070cf", + "endRef": "e8502cbc24cf347f98817affab44b79b606e21fe", + "traceId": "697e16d0-dbce-44de-88b7-62d7dd673154" + } +} \ No newline at end of file diff --git a/.trajectories/completed/2026-05/traj_v87cyrs8dke9.md b/.trajectories/completed/2026-05/traj_v87cyrs8dke9.md new file mode 100644 index 000000000..bf4b8d3b5 --- /dev/null +++ b/.trajectories/completed/2026-05/traj_v87cyrs8dke9.md @@ -0,0 +1,68 @@ +# Trajectory: Upgrade .agentworkforce personas to latest 3.x shape + +> **Status:** ✅ Completed +> **Confidence:** 90% +> **Started:** May 14, 2026 at 02:28 PM +> **Completed:** May 18, 2026 at 06:06 PM + +--- + +## Summary + +Refactored runDriveSession (complexity 23 → under 15) by extracting prepareDriveAttachTarget, switchWorkerToManualFlush, captureSnapshotForDrive, syncInitialPtySize, pickInitialTerminalRows, and runDriveSessionLoop. All 387 CLI tests still pass; ESLint warning resolved. Public API surface and exports unchanged so passthrough.ts and tests still work. + +**Approach:** Standard approach + +--- + +## Key Decisions + +### Factored broker-connection discovery into src/cli/lib/broker-connection.ts +- **Chose:** Factored broker-connection discovery into src/cli/lib/broker-connection.ts +- **Reasoning:** Drive and view both need the same flag/env/connection.json fallback chain; pulling it out keeps both verbs aligned and unblocks future relay/new/run verbs from sub-PR 4. + +### Hand-rolled keybind state machine in drive.ts instead of pulling in readline.emitKeypressEvents +- **Chose:** Hand-rolled keybind state machine in drive.ts instead of pulling in readline.emitKeypressEvents +- **Reasoning:** All bindings are ASCII control chars (Ctrl+G/Ctrl+C/Ctrl+B prefix). Tiny stateful parser handles cross-chunk Ctrl+B prefix cleanly and is trivially testable. + +### Overload existing 'run' command rather than registering a second one — Commander only allows one verb per name and there's already a workflow-runner 'run ' +- **Chose:** Overload existing 'run' command rather than registering a second one — Commander only allows one verb per name and there's already a workflow-runner 'run ' +- **Reasoning:** Adds -n/--mode/--ephemeral flags and dispatches to spawn-and-attach when -n is set, otherwise falls through to the existing workflow runner. Preserves backward compatibility for all existing 'run ' invocations. + +### relay copies-and-trims drive rather than sharing a base class +- **Chose:** relay copies-and-trims drive rather than sharing a base class +- **Reasoning:** Trimmed surface (no queue UI, no Ctrl+G, no delivery_queued/agent_pending_drained tracking) is small enough that an abstraction layer would cost more clarity than it saves. drive.ts is documented as canonical; relay.ts header comment points there. If a third sibling lands, factor. + +### Silent -n alias dispatched via argv pre-parser before commander runs, not via a hidden internal verb +- **Chose:** Silent -n alias dispatched via argv pre-parser before commander runs, not via a hidden internal verb +- **Reasoning:** Pre-parser (parseVerblessAlias) catches the four canonical shapes (-n NAME, --name NAME, -nNAME, --name=NAME), refuses ambiguous (-n NAME ) and help/version, then hands off to runSpawnAndAttach with mode='relay' and ephemeral=true. Single code path with run -n NAME CLI guarantees byte-equivalence; bootstrap.test.ts asserts the triplet extraction matches what commander's run -n action sees. + +### Drop the 'run' verb, fold spawn-and-attach into 'new --attach' +- **Chose:** Drop the 'run' verb, fold spawn-and-attach into 'new --attach' +- **Reasoning:** Reviewer pushback: the _actionHandler.apply() hack to overload commander's run was a code smell signaling the verb naming was wrong. Moving the composition to 'new --attach' eliminates the workflow-runner name conflict cleanly, keeps the verb taxonomy simple (new spawns, view/drive/relay sessions), and the alias retargets transparently — both -n alias and new --attach call the same runSpawnAndAttach helper, byte-equivalence preserved. + +### Refactor runDriveSession by extracting prep helpers + session loop +- **Chose:** Refactor runDriveSession by extracting prep helpers + session loop +- **Reasoning:** ESLint complexity 23 > 15. Splitting validate target, mode switch, snapshot handling, PTY size sync, and the Promise-based session runner into helpers preserves behavior, reduces parent complexity to under 15, keeps all public exports stable for tests and passthrough.ts + +--- + +## Chapters + +### 1. Work +*Agent: default* + +- Factored broker-connection discovery into src/cli/lib/broker-connection.ts +- Hand-rolled keybind state machine in drive.ts instead of pulling in readline.emitKeypressEvents +- Overload existing 'run' command rather than registering a second one — Commander only allows one verb per name and there's already a workflow-runner 'run ': Overload existing 'run' command rather than registering a second one — Commander only allows one verb per name and there's already a workflow-runner 'run ' +- relay copies-and-trims drive rather than sharing a base class: relay copies-and-trims drive rather than sharing a base class +- Silent -n alias dispatched via argv pre-parser before commander runs, not via a hidden internal verb: Silent -n alias dispatched via argv pre-parser before commander runs, not via a hidden internal verb +- Drop the 'run' verb, fold spawn-and-attach into 'new --attach': Drop the 'run' verb, fold spawn-and-attach into 'new --attach' +- Refactor runDriveSession by extracting prep helpers + session loop: Refactor runDriveSession by extracting prep helpers + session loop + +--- + +## Artifacts + +**Commits:** e8502cb, 850c8e0, de77083, 88b12e6, da9e912, 37c314f, 355bc07, 45b7c15, cd72e61, 106b7d9, e3ac4fc, 79bbfdd, 3356ecd, 2c59e77, 80c0d6e, 2c6abac, 35ef0a8, 0a44e6f, b9e421a, 0d61a4e, 35a331c, 403cd00, 765156f, a0ef506, 592b0e6, c10a9d5, 209e5d4, 5fef86b, efcb1ed, 0aeb71c, 47a77c8, b73642e, 09240e3, 18ccff3, a1c0d06, b89d874, 689eed0, 1bd6e48, 38abde4, 789ec19, c48e884, e854f91, 9ae5eb8, 2f32141, e2834ca, df45489, 52319ff, 7295f1a, f31b91b, 703843f, 6326d18, 3a76225, a30e5c8, 885df69, 40b8443, 3e1e61a, 687160a, 51c9372, 71358b1, 1f01c87, c95a609, 82a3883, 5fc8a13, 8de35af, 5a652d6, 5bcca47, b04bc64, 6e0b7bc, 48704de, 18852c4, 2b69c44, 9cee216, 3ed3530, 3896309, 52571b3, 3d33cb5, d771a6e, cc491e3, 0b0518a, 89c2c36, 9283abc, 4a4c05a, 43389b0, d3f02ce, 633aa2a, 56f3565, 34765f5, 88db665, 881ae7a, ae04694, cbe2934, 208b425, cbc70b0, bf993f2, 759d351, a7ef2fc, 34cd340, 9de8d53, a8a3e0b, 195f55f, e033054 +**Files changed:** 187 diff --git a/.trajectories/completed/2026-05/traj_v87cyrs8dke9.trace.json b/.trajectories/completed/2026-05/traj_v87cyrs8dke9.trace.json new file mode 100644 index 000000000..44dbb6a59 --- /dev/null +++ b/.trajectories/completed/2026-05/traj_v87cyrs8dke9.trace.json @@ -0,0 +1,4267 @@ +{ + "version": "1.0.0", + "id": "697e16d0-dbce-44de-88b7-62d7dd673154", + "timestamp": "2026-05-18T18:06:05.027Z", + "trajectory": "traj_v87cyrs8dke9", + "files": [ + { + "path": ".agentworkforce/workforce/personas/nextjs-web-steward.agentsMdContent.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 56, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".agentworkforce/workforce/personas/nextjs-web-steward.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 32, + "end_line": 38, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 41, + "end_line": 45, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".claude/rules/docs-sync.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 30, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".github/workflows/build-broker-binary.yml", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 44, + "end_line": 50, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 60, + "end_line": 75, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".github/workflows/publish.yml", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 130, + "end_line": 148, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 154, + "end_line": 161, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 231, + "end_line": 246, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 250, + "end_line": 256, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 350, + "end_line": 360, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 363, + "end_line": 369, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".github/workflows/remove-stale-previews.yml", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 67, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".github/workflows/rust-ci.yml", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 57, + "end_line": 64, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 84, + "end_line": 90, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 106, + "end_line": 113, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".github/workflows/test.yml", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 148, + "end_line": 155, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".prettierignore", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 5, + "end_line": 8, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/active/traj_v87cyrs8dke9.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 106, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_0e8i20oitwvz.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 53, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_0e8i20oitwvz.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 33, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_0o6gb2wvk59t.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 85, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_0o6gb2wvk59t.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 41, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_2yicjxgajt0a.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 53, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_2yicjxgajt0a.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 33, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_3gjtcykvybt5.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 53, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_3gjtcykvybt5.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 33, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_4chzkm724ufo.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 76, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_4chzkm724ufo.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 40, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_4t07itef99ug.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 65, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_4t07itef99ug.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 34, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_4vucir4qvqa2.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 70, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_4vucir4qvqa2.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 34, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_6sjeohtm3php.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 53, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_6sjeohtm3php.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 33, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_7uznwzoxbao6.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 64, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_7uznwzoxbao6.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 34, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_7zu7et53ph3l.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 65, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_7zu7et53ph3l.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 39, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_947wzpddsg9j.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 65, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_947wzpddsg9j.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 34, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_9fdv7hxm0b60.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 65, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_9fdv7hxm0b60.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 39, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_9gq96irkj00s.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 53, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_9gq96irkj00s.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 33, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_cbmwd07phhm2.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 81, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_cbmwd07phhm2.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 46, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_d89s38ddu7cj.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 53, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_d89s38ddu7cj.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 33, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_erzd7j9nto9r.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 69, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_erzd7j9nto9r.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 34, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_f3arvbmmlomn.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 63, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_f3arvbmmlomn.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 34, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_fh8oosbijpwc.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 94, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_fh8oosbijpwc.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 53, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_fh8oosbijpwc.trace.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 103, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_gh05rj5gwsap.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 60, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_gh05rj5gwsap.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 40, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_gh05rj5gwsap.trace.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 233, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_hfkww5z7trxn.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 53, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_hfkww5z7trxn.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 33, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_hrsndfzk0qay.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 53, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_hrsndfzk0qay.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 33, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_n8duofq5vq1a.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 65, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_n8duofq5vq1a.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 39, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_o251whkvy9rl.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 76, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_o251whkvy9rl.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 40, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_piik8r6zu3i7.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 89, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_piik8r6zu3i7.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 51, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_ryf5sstno6p3.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 57, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_ryf5sstno6p3.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 34, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_tgism98me5na.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 57, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_tgism98me5na.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 34, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_v1wexlfur5zr.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 63, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_v1wexlfur5zr.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 34, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_whd40oxptlhn.trace.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 664, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_wzzboitm85ee.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 53, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_wzzboitm85ee.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 33, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_ybcrij9wg8m1.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 93, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_ybcrij9wg8m1.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 52, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_zfa6skfr32vy.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 65, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/completed/2026-05/traj_zfa6skfr32vy.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 34, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": ".trajectories/index.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 14, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 409, + "end_line": 623, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "CHANGELOG.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 38, + "end_line": 83, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 85, + "end_line": 99, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 101, + "end_line": 115, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "Cargo.lock", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 6, + "end_line": 15, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 49, + "end_line": 79, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 139, + "end_line": 150, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 461, + "end_line": 469, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 648, + "end_line": 654, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 678, + "end_line": 689, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1012, + "end_line": 1026, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1562, + "end_line": 1576, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1999, + "end_line": 2007, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 2101, + "end_line": 2117, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 2399, + "end_line": 2414, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 2417, + "end_line": 2423, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 2941, + "end_line": 2952, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 3025, + "end_line": 3044, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 3260, + "end_line": 3274, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "Cargo.toml", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 29, + "end_line": 35, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 39, + "end_line": 46, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "Cross.toml", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 9, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 131, + "end_line": 144, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/acp-bridge/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 46, + "end_line": 52, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/agent/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 19, + "end_line": 25, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/brand/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/broker-darwin-arm64/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/broker-darwin-x64/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/broker-linux-arm64/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/broker-linux-x64/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/broker-win32-x64/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/browser-primitive/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 38, + "end_line": 44, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/cloud/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 23, + "end_line": 29, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/config/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/config/src/cli-registry.generated.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 14, + "end_line": 21, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 64, + "end_line": 72, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 394, + "end_line": 401, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 451, + "end_line": 458, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 624, + "end_line": 630, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 650, + "end_line": 657, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 823, + "end_line": 829, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 969, + "end_line": 975, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1022, + "end_line": 1028, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/credential-proxy/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/events/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/gateway/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 23, + "end_line": 29, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/github-primitive/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 32, + "end_line": 38, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/hooks/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 37, + "end_line": 45, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/memory/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 22, + "end_line": 28, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/openclaw/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 29, + "end_line": 35, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/personas/README.md", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 68, + "end_line": 74, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 79, + "end_line": 98, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 106, + "end_line": 116, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/personas/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/personas/personas/agent-relay-e2e-conductor.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 15, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/personas/personas/agent-relay-workflow.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 10, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 28, + "end_line": 38, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/personas/personas/cloud-sandbox-infra.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 15, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/personas/personas/cloud-slack-proxy-guard.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 15, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/personas/personas/opencode-workflow-specialist.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 15, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/personas/personas/relay-orchestrator.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 15, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 18, + "end_line": 28, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/personas/personas/sage-proactive-rewirer.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 15, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/personas/personas/sage-slack-egress-migrator.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 15, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/personas/scripts/validate-personas.mjs", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 6, + "end_line": 15, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 67, + "end_line": 94, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/policy/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 22, + "end_line": 28, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/sdk-py/pyproject.toml", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 4, + "end_line": 10, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/sdk-py/src/agent_relay/models.py", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 10, + "end_line": 16, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 40, + "end_line": 47, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 205, + "end_line": 211, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 231, + "end_line": 238, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 392, + "end_line": 398, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 441, + "end_line": 447, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 458, + "end_line": 464, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/sdk/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 149, + "end_line": 159, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 168, + "end_line": 181, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/sdk/src/__tests__/models.test.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 19, + "end_line": 25, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 30, + "end_line": 41, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/sdk/src/__tests__/orchestration-upgrades.test.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 260, + "end_line": 382, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/sdk/src/__tests__/transport.test.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 59, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/sdk/src/client.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 23, + "end_line": 32, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 42, + "end_line": 49, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 89, + "end_line": 106, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 169, + "end_line": 175, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 398, + "end_line": 416, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 428, + "end_line": 446, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 494, + "end_line": 631, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/sdk/src/index.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 6, + "end_line": 14, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/sdk/src/protocol.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 2, + "end_line": 9, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 42, + "end_line": 69, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 286, + "end_line": 303, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/sdk/src/transport.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 14, + "end_line": 34, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 36, + "end_line": 43, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 47, + "end_line": 53, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 62, + "end_line": 68, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 78, + "end_line": 93, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 98, + "end_line": 128, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 245, + "end_line": 293, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/sdk/src/workflows/__tests__/verification.test.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 11, + "end_line": 17, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 314, + "end_line": 381, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/sdk/src/workflows/verification.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 122, + "end_line": 139, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 203, + "end_line": 238, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/shared/cli-registry.yaml", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 30, + "end_line": 47, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 531, + "end_line": 539, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/slack-primitive/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 30, + "end_line": 40, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/telemetry/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/telemetry/src/posthog-config.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 47, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/trajectory/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 22, + "end_line": 28, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/user-directory/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 22, + "end_line": 28, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/utils/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 111, + "end_line": 117, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/workflow-types/package.json", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "packages/workflow-types/src/index.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 330, + "end_line": 346, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/auth.rs", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 6, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 607, + "end_line": 631, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 646, + "end_line": 651, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 768, + "end_line": 773, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 785, + "end_line": 795, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 920, + "end_line": 930, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 948, + "end_line": 955, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 965, + "end_line": 971, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1043, + "end_line": 1049, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cli/bootstrap.test.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 2, + "end_line": 8, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 38, + "end_line": 48, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 144, + "end_line": 252, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cli/bootstrap.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 26, + "end_line": 37, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 287, + "end_line": 300, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 310, + "end_line": 332, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 343, + "end_line": 378, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cli/commands/agent-management.test.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 121, + "end_line": 127, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 171, + "end_line": 198, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cli/commands/agent-management.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 37, + "end_line": 43, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 87, + "end_line": 156, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 180, + "end_line": 188, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 260, + "end_line": 266, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 280, + "end_line": 286, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 331, + "end_line": 337, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cli/commands/core.test.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 5, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 11, + "end_line": 24, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 85, + "end_line": 91, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 98, + "end_line": 110, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 140, + "end_line": 146, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 149, + "end_line": 162, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 388, + "end_line": 394, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 453, + "end_line": 459, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 461, + "end_line": 731, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 876, + "end_line": 1025, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1077, + "end_line": 1083, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1098, + "end_line": 1110, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1117, + "end_line": 1123, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1129, + "end_line": 1135, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1141, + "end_line": 1147, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1151, + "end_line": 1157, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1185, + "end_line": 1197, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cli/commands/core.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 411, + "end_line": 417, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 421, + "end_line": 427, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 473, + "end_line": 480, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cli/commands/drive.test.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 801, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cli/commands/drive.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 766, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cli/commands/new.test.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 631, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cli/commands/new.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 295, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cli/commands/passthrough.test.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 596, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cli/commands/passthrough.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 539, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cli/commands/rm.test.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 206, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cli/commands/rm.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 138, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cli/commands/view.test.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 456, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cli/commands/view.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 327, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cli/lib/attach.test.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 196, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cli/lib/attach.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 113, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cli/lib/broker-connection.test.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 93, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cli/lib/broker-connection.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 111, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cli/lib/broker-lifecycle.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 13, + "end_line": 19, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 34, + "end_line": 41, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 44, + "end_line": 68, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 174, + "end_line": 180, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 291, + "end_line": 408, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 460, + "end_line": 583, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1131, + "end_line": 1139, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1144, + "end_line": 1190, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1581, + "end_line": 1659, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cli/lib/sdk-client.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 39, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cli/lib/spawn-and-attach.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 449, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cost/pricing.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 12, + "end_line": 18, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 30, + "end_line": 37, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 39, + "end_line": 46, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/cost/tracker.test.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 75, + "end_line": 87, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 163, + "end_line": 169, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/helpers.rs", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 216, + "end_line": 221, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 853, + "end_line": 861, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1113, + "end_line": 1118, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1346, + "end_line": 1351, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/lib.rs", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 17, + "end_line": 23, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/listen_api.rs", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 7, + "end_line": 16, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 18, + "end_line": 25, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 84, + "end_line": 111, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 136, + "end_line": 201, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 204, + "end_line": 235, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 332, + "end_line": 354, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1005, + "end_line": 1034, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1113, + "end_line": 1407, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1716, + "end_line": 1723, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1905, + "end_line": 1917, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 2639, + "end_line": 3247, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/main.rs", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 11, + "end_line": 24, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 26, + "end_line": 37, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 64, + "end_line": 73, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 120, + "end_line": 128, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 136, + "end_line": 183, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 814, + "end_line": 820, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 827, + "end_line": 1029, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1400, + "end_line": 1422, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1520, + "end_line": 1525, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1643, + "end_line": 1668, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1670, + "end_line": 1676, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1690, + "end_line": 1699, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1710, + "end_line": 1717, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1793, + "end_line": 1800, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1943, + "end_line": 2000, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 2263, + "end_line": 2328, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 2409, + "end_line": 2544, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 2646, + "end_line": 2653, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 2759, + "end_line": 2764, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 2815, + "end_line": 2828, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 2841, + "end_line": 2847, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 2855, + "end_line": 2861, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 2955, + "end_line": 2960, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 2999, + "end_line": 3012, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 3025, + "end_line": 3031, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 3039, + "end_line": 3045, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 3195, + "end_line": 3244, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 3451, + "end_line": 3481, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 3735, + "end_line": 3742, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 3779, + "end_line": 3801, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 3936, + "end_line": 3943, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 3977, + "end_line": 3984, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 4063, + "end_line": 4069, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 4245, + "end_line": 4395, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 4514, + "end_line": 4544, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 5888, + "end_line": 5894, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 5903, + "end_line": 5909, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 6737, + "end_line": 6742, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/pty.rs", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 143, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 145, + "end_line": 164, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 210, + "end_line": 248, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 285, + "end_line": 334, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 341, + "end_line": 394, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 396, + "end_line": 476, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 614, + "end_line": 621, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 648, + "end_line": 811, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 899, + "end_line": 1113, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/pty_worker.rs", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 15, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 98, + "end_line": 145, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 207, + "end_line": 221, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 371, + "end_line": 377, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 456, + "end_line": 506, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 522, + "end_line": 527, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 568, + "end_line": 574, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 892, + "end_line": 898, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 919, + "end_line": 925, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1060, + "end_line": 1067, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1141, + "end_line": 1221, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/readiness.rs", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 342, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/routing.rs", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 439, + "end_line": 445, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 471, + "end_line": 477, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/snapshot.rs", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 599, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/swarm.rs", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1365, + "end_line": 1370, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 1644, + "end_line": 1709, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/telemetry.rs", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 15, + "end_line": 35, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 360, + "end_line": 379, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 381, + "end_line": 398, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 458, + "end_line": 471, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 698, + "end_line": 717, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/types.rs", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 101, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 203, + "end_line": 468, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/wait.rs", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 648, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/worker.rs", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 17, + "end_line": 23, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 165, + "end_line": 172, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 204, + "end_line": 219, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 254, + "end_line": 270, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 296, + "end_line": 311, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 318, + "end_line": 334, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 417, + "end_line": 423, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 634, + "end_line": 794, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 939, + "end_line": 1106, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/worker_request.rs", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 390, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "src/wrap.rs", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 629, + "end_line": 636, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 754, + "end_line": 759, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "tests/integration/broker/utils/broker-harness.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 36, + "end_line": 44, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "vitest.config.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 34, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "web/app/blog/[slug]/page.tsx", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 8, + "end_line": 14, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 49, + "end_line": 55, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "web/components/docs/DocsNav.tsx", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 5, + "end_line": 11, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 13, + "end_line": 19, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + }, + { + "start_line": 63, + "end_line": 70, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "web/content/docs/cli-overview.mdx", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 16, + "end_line": 22, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "web/content/docs/reference-broker-api.mdx", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 511, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "web/content/docs/reference-cli.mdx", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 49, + "end_line": 256, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "web/content/docs/typescript-sdk.mdx", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 127, + "end_line": 229, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "web/lib/docs-nav.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 58, + "end_line": 64, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "web/scripts/list-stale-stages.sh", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 1, + "end_line": 163, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "workflows/cloud-connect/fix-agent-relay-utils-bundling.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 122, + "end_line": 142, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "workflows/cloud-connect/fix-cloud-connect-claude-hang.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 100, + "end_line": 120, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "workflows/cloud-connect/validate-cloud-connect-e2e.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 117, + "end_line": 137, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + }, + { + "path": "workflows/relay-e2e-meta-workflow.ts", + "conversations": [ + { + "contributor": { + "type": "ai" + }, + "ranges": [ + { + "start_line": 19, + "end_line": 32, + "revision": "e8502cbc24cf347f98817affab44b79b606e21fe" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/.trajectories/index.json b/.trajectories/index.json index efc80843b..1f085be12 100644 --- a/.trajectories/index.json +++ b/.trajectories/index.json @@ -1,6 +1,6 @@ { "version": 1, - "lastUpdated": "2026-05-18T05:58:37.619Z", + "lastUpdated": "2026-05-18T18:06:05.096Z", "trajectories": { "traj_9gq96irkj00s": { "title": "Update relay to use published relaycast Rust reclaim fix", @@ -412,9 +412,10 @@ }, "traj_v87cyrs8dke9": { "title": "Upgrade .agentworkforce personas to latest 3.x shape", - "status": "active", + "status": "completed", "startedAt": "2026-05-14T14:28:34.155Z", - "path": "/Users/will/Projects/AgentWorkforce/relay/.claude/worktrees/agent-a5c5b8a4961c9a051/.trajectories/active/traj_v87cyrs8dke9.json" + "completedAt": "2026-05-18T18:06:04.950Z", + "path": "/home/user/relay/.trajectories/completed/2026-05/traj_v87cyrs8dke9.json" }, "traj_v1wexlfur5zr": { "title": "Fix broker headless reliability doc", @@ -620,4 +621,4 @@ "path": "/Users/will/Projects/AgentWorkforce/relay/.claude/worktrees/agent-aff3649a00a0f36ab/.trajectories/completed/2026-05/traj_ryf5sstno6p3.json" } } -} +} \ No newline at end of file diff --git a/src/cli/commands/drive.ts b/src/cli/commands/drive.ts index 7b9ebbe8e..98669e4e3 100644 --- a/src/cli/commands/drive.ts +++ b/src/cli/commands/drive.ts @@ -430,16 +430,22 @@ export function renderStatusLine(opts: { /** ----- Main session runner ----- */ +/** Validated attach target — normalized agent name + resolved broker connection. */ +interface DriveAttachTarget { + name: string; + connection: BrokerConnection; +} + /** - * Open a `drive` session. Resolves with the exit code the CLI should - * propagate. Cleans up its own stdin raw-mode and best-effort restores - * the worker's previous inbound delivery mode on any exit path. + * Trim the agent name and resolve the broker connection. Returns + * `null` (and writes the appropriate error) when either is missing — + * collapses two top-level CLI bail-out branches into one helper. */ -export async function runDriveSession( +function prepareDriveAttachTarget( agentName: string, options: { brokerUrl?: string; apiKey?: string; stateDir?: string }, deps: DriveDependencies -): Promise { +): DriveAttachTarget | null { // Normalize once so every downstream broker call, WS-event match, // status-line label, and error message uses the same trimmed value. // Without this a stray space in the raw input turns into a silent @@ -447,42 +453,58 @@ export async function runDriveSession( const name = agentName.trim(); if (!name) { deps.error('Error: agent name is required'); - return 1; + return null; } - const connection = resolveBrokerConnection(options, deps); if (!connection) { deps.error( 'Error: could not locate broker connection. Pass --broker-url, set RELAY_BROKER_URL, ' + 'or run from a directory containing .agent-relay/connection.json.' ); - return 1; + return null; } + return { name, connection }; +} - // Remember the worker's prior mode so we can restore it on detach. - // `null` means we couldn't read it (broker hiccup or worker missing); - // we default the restore target to `auto_inject` in that case so the - // queue doesn't keep growing. +/** + * Read the worker's current inbound delivery mode (so we can restore + * it on detach) and flip the worker into `manual_flush`. Returns the + * previous mode on success, or `null` (and writes the appropriate + * error) when the flip fails — drive aborts before touching the + * terminal in that case, so we don't redraw the screen and then + * silently keep auto-injecting into the agent. + */ +async function switchWorkerToManualFlush( + connection: BrokerConnection, + name: string, + deps: DriveDependencies +): Promise<{ previousMode: InboundDeliveryMode | null } | null> { const previousMode = await getInboundDeliveryMode(connection, name, deps.fetch); - - // Flip the worker into manual_flush mode. If this fails outright, abort - // before doing anything else — we don't want to redraw the screen - // and then silently keep auto-injecting into the agent. const flip = await setInboundDeliveryMode(connection, name, 'manual_flush', deps.fetch); - if (!flip.ok) { - if (flip.status === 404) { - deps.error(`Error: no agent named '${name}'`); - } else { - deps.error( - `Error: could not switch '${name}' to manual_flush mode: ${flip.message ?? 'unknown error'}` - ); - } - return 1; + if (flip.ok) return { previousMode }; + if (flip.status === 404) { + deps.error(`Error: no agent named '${name}'`); + } else { + deps.error( + `Error: could not switch '${name}' to manual_flush mode: ${flip.message ?? 'unknown error'}` + ); } + return null; +} - // Render the agent's current visible screen before the live stream - // begins. Same error semantics as `view`: hard errors abort, transient - // errors warn and proceed. +/** + * Render the agent's current visible screen before the live stream + * begins. Hard errors (`not_found`, `no_pty`) abort and best-effort + * restore the prior mode so the worker's queue doesn't keep growing. + * Transient errors warn and proceed. Returns the snapshot rows for + * status-line fallback, or `null` when the caller should abort. + */ +async function captureSnapshotForDrive( + connection: BrokerConnection, + name: string, + previousMode: InboundDeliveryMode | null, + deps: DriveDependencies +): Promise<{ snapshotRows?: number } | null> { const snapshot = await deps.captureAndRenderSnapshot( { url: connection.url, apiKey: connection.apiKey }, name, @@ -490,72 +512,65 @@ export async function runDriveSession( ); switch (snapshot.status) { case 'ok': - break; + return { snapshotRows: snapshot.rows }; case 'not_found': - // Best-effort restore — we did flip the mode above. await setInboundDeliveryMode(connection, name, previousMode ?? 'auto_inject', deps.fetch); deps.error(`Error: ${snapshot.message ?? `no agent named '${name}'`}`); - return 1; + return null; case 'no_pty': await setInboundDeliveryMode(connection, name, previousMode ?? 'auto_inject', deps.fetch); deps.error(`Error: ${snapshot.message ?? `agent '${name}' has no PTY to drive`}`); - return 1; + return null; case 'unavailable': case 'transport_error': deps.log( `[drive] could not capture initial screen (${snapshot.message ?? snapshot.status}); streaming live output only` ); - break; + return { snapshotRows: snapshot.rows }; } +} - // Seed the pending counter so the status line is correct from the - // first paint. - let pending = await getPendingCount(connection, name, deps.fetch); - let showHelp = false; - - // Status-line row tracks the LOCAL terminal's bottom row, not the - // agent's PTY rows from the snapshot — those can differ before we - // forward our size to the broker, and the status line needs to land - // where the human is looking. Falls back to the snapshot rows, then - // the renderer's own 24-row default. - const initialLocalSize = deps.terminal.getSize(); - let terminalRows: number | undefined = - initialLocalSize?.rows ?? - (typeof snapshot.rows === 'number' && snapshot.rows > 0 ? snapshot.rows : undefined); - - const paintStatus = (): void => { - deps.writeChunk( - renderStatusLine({ - name, - mode: 'manual_flush', - pending, - showHelp, - rows: terminalRows, - }) - ); - }; - paintStatus(); - - // Sync the agent's PTY to the driver's local terminal size. tmux / - // screen / ssh all do this — without it, a TUI in the agent renders - // into whatever 24×80 box the PTY was spawned with, ignoring the - // human's actual viewport. Best-effort: a failure here is annoying - // but not fatal (the human can still type, output just renders into - // the old size). Skipped entirely when stdout isn't a TTY. - if (initialLocalSize) { - const initialResize = await resizeWorker( - connection, - name, - initialLocalSize.rows, - initialLocalSize.cols, - deps.fetch +/** + * Sync the agent's PTY to the driver's local terminal size. tmux / + * screen / ssh all do this — without it, a TUI in the agent renders + * into whatever 24×80 box the PTY was spawned with, ignoring the + * human's actual viewport. Best-effort: a failure here is annoying + * but not fatal (the human can still type, output just renders into + * the old size). Skipped entirely when `localSize` is `null` (stdout + * isn't a TTY). + */ +async function syncInitialPtySize( + connection: BrokerConnection, + name: string, + localSize: { rows: number; cols: number } | null, + deps: DriveDependencies +): Promise { + if (!localSize) return; + const res = await resizeWorker(connection, name, localSize.rows, localSize.cols, deps.fetch); + if (!res.ok) { + deps.log( + `[drive] could not sync agent PTY size to local terminal (${res.message ?? 'unknown'}); continuing` ); - if (!initialResize.ok) { - deps.log( - `[drive] could not sync agent PTY size to local terminal (${initialResize.message ?? 'unknown'}); continuing` - ); - } } +} + +/** Initial state handed off to the interactive session loop. */ +interface DriveSessionState { + connection: BrokerConnection; + name: string; + previousMode: InboundDeliveryMode | null; + initialPending: number; + initialTerminalRows: number | undefined; +} + +/** + * Run the interactive session: opens the WS, takes over stdin on + * `open`, drives keybinds/resize/status-line, and restores the + * worker's previous mode on any exit path. Resolves with the exit + * code the CLI should propagate. + */ +function runDriveSessionLoop(state: DriveSessionState, deps: DriveDependencies): Promise { + const { connection, name, previousMode } = state; const wsUrl = toWsUrl(connection.url); const headers: Record = {}; @@ -567,8 +582,24 @@ export async function runDriveSession( let settled = false; let rawModeWasSet = false; let unsubscribeResize: (() => void) | null = null; + let pending = state.initialPending; + let showHelp = false; + let terminalRows = state.initialTerminalRows; const parser = new KeybindParser(); + const paintStatus = (): void => { + deps.writeChunk( + renderStatusLine({ + name, + mode: 'manual_flush', + pending, + showHelp, + rows: terminalRows, + }) + ); + }; + paintStatus(); + // Local-terminal resize handler. Forwards to the broker and // repaints the status line at the new bottom-row index. Registered // on `socket.on('open')` (same point we take over stdin) so a @@ -744,6 +775,60 @@ export async function runDriveSession( }); } +/** + * Pick the status-line row. Prefers the LOCAL terminal's height (the + * status line needs to land where the human is looking) and falls back + * to the snapshot's PTY rows, then the renderer's own 24-row default + * via `undefined`. + */ +function pickInitialTerminalRows( + localSize: { rows: number; cols: number } | null, + snapshotRows: number | undefined +): number | undefined { + if (localSize) return localSize.rows; + if (typeof snapshotRows === 'number' && snapshotRows > 0) return snapshotRows; + return undefined; +} + +/** + * Open a `drive` session. Resolves with the exit code the CLI should + * propagate. Cleans up its own stdin raw-mode and best-effort restores + * the worker's previous inbound delivery mode on any exit path. + */ +export async function runDriveSession( + agentName: string, + options: { brokerUrl?: string; apiKey?: string; stateDir?: string }, + deps: DriveDependencies +): Promise { + const target = prepareDriveAttachTarget(agentName, options, deps); + if (!target) return 1; + const { name, connection } = target; + + const flipResult = await switchWorkerToManualFlush(connection, name, deps); + if (!flipResult) return 1; + const { previousMode } = flipResult; + + const snapshotResult = await captureSnapshotForDrive(connection, name, previousMode, deps); + if (!snapshotResult) return 1; + + const initialPending = await getPendingCount(connection, name, deps.fetch); + const initialLocalSize = deps.terminal.getSize(); + const initialTerminalRows = pickInitialTerminalRows(initialLocalSize, snapshotResult.snapshotRows); + + await syncInitialPtySize(connection, name, initialLocalSize, deps); + + return runDriveSessionLoop( + { + connection, + name, + previousMode, + initialPending, + initialTerminalRows, + }, + deps + ); +} + /** Register `agent-relay drive ` on the supplied commander program. */ export function registerDriveCommands(program: Command, overrides: Partial = {}): void { const deps = withDefaults(overrides); From 89e9baac7ffb330529a546da9073ca637a0e9c35 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 18 May 2026 18:10:13 +0000 Subject: [PATCH 2/4] chore(trajectories): sanitize absolute paths in metadata Address CodeRabbit nits on PR #899: - index.json: drop /home/user/relay prefix from my entry's path so the index is portable across machines - traj_v87cyrs8dke9.json: replace absolute projectId with stable "relay" identifier so the metadata doesn't leak a local developer path Tool-generated metadata only; no code change. --- .trajectories/completed/2026-05/traj_v87cyrs8dke9.json | 2 +- .trajectories/index.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.trajectories/completed/2026-05/traj_v87cyrs8dke9.json b/.trajectories/completed/2026-05/traj_v87cyrs8dke9.json index 6d25cf404..07b424161 100644 --- a/.trajectories/completed/2026-05/traj_v87cyrs8dke9.json +++ b/.trajectories/completed/2026-05/traj_v87cyrs8dke9.json @@ -406,7 +406,7 @@ "workflows/cloud-connect/validate-cloud-connect-e2e.ts", "workflows/relay-e2e-meta-workflow.ts" ], - "projectId": "/Users/will/Projects/AgentWorkforce/relay", + "projectId": "relay", "tags": [], "_trace": { "startRef": "83ecfbca9cd87540629ae0a9b2f155cd2c3070cf", diff --git a/.trajectories/index.json b/.trajectories/index.json index 1f085be12..1c9925eab 100644 --- a/.trajectories/index.json +++ b/.trajectories/index.json @@ -415,7 +415,7 @@ "status": "completed", "startedAt": "2026-05-14T14:28:34.155Z", "completedAt": "2026-05-18T18:06:04.950Z", - "path": "/home/user/relay/.trajectories/completed/2026-05/traj_v87cyrs8dke9.json" + "path": ".trajectories/completed/2026-05/traj_v87cyrs8dke9.json" }, "traj_v1wexlfur5zr": { "title": "Fix broker headless reliability doc", From 009a48e4772cc4297f3a0aee41a64b5942ae6d8f Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 18 May 2026 18:14:06 +0000 Subject: [PATCH 3/4] chore(trajectories): align trajectory title with retrospective scope The trail-tool kept a long-running trajectory active and reused its identifier for the runDriveSession refactor, so the index title still read "Upgrade .agentworkforce personas to latest 3.x shape" while the retrospective inside the file describes the drive refactor. Rename the title in both the index and the trajectory file so consumers see matching metadata. --- .trajectories/completed/2026-05/traj_v87cyrs8dke9.json | 2 +- .trajectories/index.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.trajectories/completed/2026-05/traj_v87cyrs8dke9.json b/.trajectories/completed/2026-05/traj_v87cyrs8dke9.json index 07b424161..f12588fde 100644 --- a/.trajectories/completed/2026-05/traj_v87cyrs8dke9.json +++ b/.trajectories/completed/2026-05/traj_v87cyrs8dke9.json @@ -2,7 +2,7 @@ "id": "traj_v87cyrs8dke9", "version": 1, "task": { - "title": "Upgrade .agentworkforce personas to latest 3.x shape" + "title": "Refactor runDriveSession below complexity 15 (#897)" }, "status": "completed", "startedAt": "2026-05-14T14:28:34.155Z", diff --git a/.trajectories/index.json b/.trajectories/index.json index 1c9925eab..7844f8b5f 100644 --- a/.trajectories/index.json +++ b/.trajectories/index.json @@ -411,7 +411,7 @@ "path": "/Users/khaliqgant/Projects/AgentWorkforce/relay/.trajectories/completed/2026-05/traj_whd40oxptlhn.json" }, "traj_v87cyrs8dke9": { - "title": "Upgrade .agentworkforce personas to latest 3.x shape", + "title": "Refactor runDriveSession below complexity 15 (#897)", "status": "completed", "startedAt": "2026-05-14T14:28:34.155Z", "completedAt": "2026-05-18T18:06:04.950Z", From de3a163470be49973339ac2b1a3447c1afd9d071 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 18 May 2026 22:32:43 +0000 Subject: [PATCH 4/4] refactor(cli): share interactive-attach prep helpers via attach.ts The runDriveSession refactor in 897 left the prep helpers (prepareAttachTarget, pickInitialTerminalRows, syncInitialPtySize, switchInboundDeliveryModeOrAbort, captureInitialSnapshot) drive-private even though passthrough.ts duplicated the same shape inline. Move them into src/cli/lib/attach.ts so both verbs share a single implementation. passthrough.ts now also calls the shared helpers, dropping its inline name-validation, connection-resolve, mode-flip, snapshot-dispatch, terminal-rows fallback, and initial-resize blocks. Verb-specific text (log prefix, "drive" vs "attach to" no-PTY message, error phrase for the mode flip) is threaded through as parameters so the visible output is byte-identical. Net: -240 lines duplicated logic, +206 shared helpers, and the third sibling that the original passthrough.ts header comment said would trigger the abstraction is effectively here. Verified: - src/cli/commands/drive.test.ts 40/40 pass - src/cli/commands/passthrough.test.ts 28/28 pass - src/cli/lib/attach.test.ts 11/11 pass - npx vitest run src/cli 506/506 pass - ESLint: no new warnings - tsc --noEmit: only baseline @agent-relay/sdk workspace resolution errors (also present on main) --- src/cli/commands/drive.ts | 165 ++++--------------------- src/cli/commands/passthrough.ts | 126 ++++++------------- src/cli/lib/attach.ts | 206 ++++++++++++++++++++++++++++++-- 3 files changed, 257 insertions(+), 240 deletions(-) diff --git a/src/cli/commands/drive.ts b/src/cli/commands/drive.ts index 98669e4e3..2755965d4 100644 --- a/src/cli/commands/drive.ts +++ b/src/cli/commands/drive.ts @@ -30,13 +30,17 @@ import WebSocket from 'ws'; import { captureAndRenderSnapshot, + captureInitialSnapshot, + pickInitialTerminalRows, + prepareAttachTarget, + switchInboundDeliveryModeOrAbort, + syncInitialPtySize, type AttachSnapshotConnection, type AttachSnapshotDeps, } from '../lib/attach.js'; import { defaultStateDir, readConnectionFileFromDisk, - resolveBrokerConnection, toWsUrl, type BrokerConnection, } from '../lib/broker-connection.js'; @@ -430,130 +434,6 @@ export function renderStatusLine(opts: { /** ----- Main session runner ----- */ -/** Validated attach target — normalized agent name + resolved broker connection. */ -interface DriveAttachTarget { - name: string; - connection: BrokerConnection; -} - -/** - * Trim the agent name and resolve the broker connection. Returns - * `null` (and writes the appropriate error) when either is missing — - * collapses two top-level CLI bail-out branches into one helper. - */ -function prepareDriveAttachTarget( - agentName: string, - options: { brokerUrl?: string; apiKey?: string; stateDir?: string }, - deps: DriveDependencies -): DriveAttachTarget | null { - // Normalize once so every downstream broker call, WS-event match, - // status-line label, and error message uses the same trimmed value. - // Without this a stray space in the raw input turns into a silent - // 404 (the broker stores names verbatim). - const name = agentName.trim(); - if (!name) { - deps.error('Error: agent name is required'); - return null; - } - const connection = resolveBrokerConnection(options, deps); - if (!connection) { - deps.error( - 'Error: could not locate broker connection. Pass --broker-url, set RELAY_BROKER_URL, ' + - 'or run from a directory containing .agent-relay/connection.json.' - ); - return null; - } - return { name, connection }; -} - -/** - * Read the worker's current inbound delivery mode (so we can restore - * it on detach) and flip the worker into `manual_flush`. Returns the - * previous mode on success, or `null` (and writes the appropriate - * error) when the flip fails — drive aborts before touching the - * terminal in that case, so we don't redraw the screen and then - * silently keep auto-injecting into the agent. - */ -async function switchWorkerToManualFlush( - connection: BrokerConnection, - name: string, - deps: DriveDependencies -): Promise<{ previousMode: InboundDeliveryMode | null } | null> { - const previousMode = await getInboundDeliveryMode(connection, name, deps.fetch); - const flip = await setInboundDeliveryMode(connection, name, 'manual_flush', deps.fetch); - if (flip.ok) return { previousMode }; - if (flip.status === 404) { - deps.error(`Error: no agent named '${name}'`); - } else { - deps.error( - `Error: could not switch '${name}' to manual_flush mode: ${flip.message ?? 'unknown error'}` - ); - } - return null; -} - -/** - * Render the agent's current visible screen before the live stream - * begins. Hard errors (`not_found`, `no_pty`) abort and best-effort - * restore the prior mode so the worker's queue doesn't keep growing. - * Transient errors warn and proceed. Returns the snapshot rows for - * status-line fallback, or `null` when the caller should abort. - */ -async function captureSnapshotForDrive( - connection: BrokerConnection, - name: string, - previousMode: InboundDeliveryMode | null, - deps: DriveDependencies -): Promise<{ snapshotRows?: number } | null> { - const snapshot = await deps.captureAndRenderSnapshot( - { url: connection.url, apiKey: connection.apiKey }, - name, - { fetch: deps.fetch, writeChunk: deps.writeChunk } - ); - switch (snapshot.status) { - case 'ok': - return { snapshotRows: snapshot.rows }; - case 'not_found': - await setInboundDeliveryMode(connection, name, previousMode ?? 'auto_inject', deps.fetch); - deps.error(`Error: ${snapshot.message ?? `no agent named '${name}'`}`); - return null; - case 'no_pty': - await setInboundDeliveryMode(connection, name, previousMode ?? 'auto_inject', deps.fetch); - deps.error(`Error: ${snapshot.message ?? `agent '${name}' has no PTY to drive`}`); - return null; - case 'unavailable': - case 'transport_error': - deps.log( - `[drive] could not capture initial screen (${snapshot.message ?? snapshot.status}); streaming live output only` - ); - return { snapshotRows: snapshot.rows }; - } -} - -/** - * Sync the agent's PTY to the driver's local terminal size. tmux / - * screen / ssh all do this — without it, a TUI in the agent renders - * into whatever 24×80 box the PTY was spawned with, ignoring the - * human's actual viewport. Best-effort: a failure here is annoying - * but not fatal (the human can still type, output just renders into - * the old size). Skipped entirely when `localSize` is `null` (stdout - * isn't a TTY). - */ -async function syncInitialPtySize( - connection: BrokerConnection, - name: string, - localSize: { rows: number; cols: number } | null, - deps: DriveDependencies -): Promise { - if (!localSize) return; - const res = await resizeWorker(connection, name, localSize.rows, localSize.cols, deps.fetch); - if (!res.ok) { - deps.log( - `[drive] could not sync agent PTY size to local terminal (${res.message ?? 'unknown'}); continuing` - ); - } -} - /** Initial state handed off to the interactive session loop. */ interface DriveSessionState { connection: BrokerConnection; @@ -775,21 +655,6 @@ function runDriveSessionLoop(state: DriveSessionState, deps: DriveDependencies): }); } -/** - * Pick the status-line row. Prefers the LOCAL terminal's height (the - * status line needs to land where the human is looking) and falls back - * to the snapshot's PTY rows, then the renderer's own 24-row default - * via `undefined`. - */ -function pickInitialTerminalRows( - localSize: { rows: number; cols: number } | null, - snapshotRows: number | undefined -): number | undefined { - if (localSize) return localSize.rows; - if (typeof snapshotRows === 'number' && snapshotRows > 0) return snapshotRows; - return undefined; -} - /** * Open a `drive` session. Resolves with the exit code the CLI should * propagate. Cleans up its own stdin raw-mode and best-effort restores @@ -800,22 +665,34 @@ export async function runDriveSession( options: { brokerUrl?: string; apiKey?: string; stateDir?: string }, deps: DriveDependencies ): Promise { - const target = prepareDriveAttachTarget(agentName, options, deps); + const target = prepareAttachTarget(agentName, options, deps); if (!target) return 1; const { name, connection } = target; - const flipResult = await switchWorkerToManualFlush(connection, name, deps); + const flipResult = await switchInboundDeliveryModeOrAbort( + connection, + name, + 'manual_flush', + `switch '${name}' to manual_flush mode`, + deps + ); if (!flipResult) return 1; const { previousMode } = flipResult; - const snapshotResult = await captureSnapshotForDrive(connection, name, previousMode, deps); + const snapshotResult = await captureInitialSnapshot(connection, name, previousMode, 'drive', 'drive', { + fetch: deps.fetch, + writeChunk: deps.writeChunk, + log: deps.log, + error: deps.error, + captureAndRenderSnapshot: deps.captureAndRenderSnapshot, + }); if (!snapshotResult) return 1; const initialPending = await getPendingCount(connection, name, deps.fetch); const initialLocalSize = deps.terminal.getSize(); const initialTerminalRows = pickInitialTerminalRows(initialLocalSize, snapshotResult.snapshotRows); - await syncInitialPtySize(connection, name, initialLocalSize, deps); + await syncInitialPtySize(connection, name, initialLocalSize, 'drive', deps); return runDriveSessionLoop( { diff --git a/src/cli/commands/passthrough.ts b/src/cli/commands/passthrough.ts index 2458cc27a..aa85aef24 100644 --- a/src/cli/commands/passthrough.ts +++ b/src/cli/commands/passthrough.ts @@ -30,23 +30,21 @@ import WebSocket from 'ws'; import { captureAndRenderSnapshot, + captureInitialSnapshot, + pickInitialTerminalRows, + prepareAttachTarget, + switchInboundDeliveryModeOrAbort, + syncInitialPtySize, type AttachSnapshotConnection, type AttachSnapshotDeps, } from '../lib/attach.js'; import { defaultStateDir, readConnectionFileFromDisk, - resolveBrokerConnection, toWsUrl, } from '../lib/broker-connection.js'; import { defaultExit, runSignalHandler } from '../lib/exit.js'; -import { - getInboundDeliveryMode, - resizeWorker, - sendInput, - setInboundDeliveryMode, - type InboundDeliveryMode, -} from './drive.js'; +import { resizeWorker, sendInput, setInboundDeliveryMode, type InboundDeliveryMode } from './drive.js'; type ExitFn = (code: number) => never; @@ -269,100 +267,50 @@ export async function runPassthroughSession( options: { brokerUrl?: string; apiKey?: string; stateDir?: string }, deps: PassthroughDependencies ): Promise { - // Normalize once so every downstream broker call, WS-event match, - // status-line label, and error message uses the same trimmed value. - // Without this a stray space in the raw input turns into a silent - // 404 (the broker stores names verbatim). - const name = agentName.trim(); - if (!name) { - deps.error('Error: agent name is required'); - return 1; - } - - const connection = resolveBrokerConnection(options, deps); - if (!connection) { - deps.error( - 'Error: could not locate broker connection. Pass --broker-url, set RELAY_BROKER_URL, ' + - 'or run from a directory containing .agent-relay/connection.json.' - ); - return 1; - } - - // Remember the worker's prior mode so we can restore on detach. - // `null` means we couldn't read it (broker hiccup or worker missing); - // we default the restore target to `auto_inject` in that case (which - // is also our preferred final state). - const previousMode = await getInboundDeliveryMode(connection, name, deps.fetch); - - // If the worker is in `manual_flush` mode (e.g. someone left a `drive` - // session), flip it back to `auto_inject` for the duration of our - // session. This matches the verb's intent: `agent-relay passthrough alice` - // means "watch alice with auto-inject on". If the worker is already - // in `auto_inject` we still issue the PUT — it's idempotent on the - // broker and gives us an early hard-failure on missing-agent before - // we touch the terminal. - const flip = await setInboundDeliveryMode(connection, name, 'auto_inject', deps.fetch); - if (!flip.ok) { - if (flip.status === 404) { - deps.error(`Error: no agent named '${name}'`); - } else { - deps.error( - `Error: could not ensure '${name}' is in passthrough session: ${flip.message ?? 'unknown error'}` - ); - } - return 1; - } + const target = prepareAttachTarget(agentName, options, deps); + if (!target) return 1; + const { name, connection } = target; + + // Even when the worker is already in `auto_inject` we still issue the + // PUT — it's idempotent on the broker and gives us an early hard + // failure on missing-agent before we touch the terminal. + const flipResult = await switchInboundDeliveryModeOrAbort( + connection, + name, + 'auto_inject', + `ensure '${name}' is in passthrough session`, + deps + ); + if (!flipResult) return 1; + const { previousMode } = flipResult; - const snapshot = await deps.captureAndRenderSnapshot( - { url: connection.url, apiKey: connection.apiKey }, + const snapshotResult = await captureInitialSnapshot( + connection, name, - { fetch: deps.fetch, writeChunk: deps.writeChunk } + previousMode, + 'passthrough', + 'attach to', + { + fetch: deps.fetch, + writeChunk: deps.writeChunk, + log: deps.log, + error: deps.error, + captureAndRenderSnapshot: deps.captureAndRenderSnapshot, + } ); - switch (snapshot.status) { - case 'ok': - break; - case 'not_found': - await setInboundDeliveryMode(connection, name, previousMode ?? 'auto_inject', deps.fetch); - deps.error(`Error: ${snapshot.message ?? `no agent named '${name}'`}`); - return 1; - case 'no_pty': - await setInboundDeliveryMode(connection, name, previousMode ?? 'auto_inject', deps.fetch); - deps.error(`Error: ${snapshot.message ?? `agent '${name}' has no PTY to attach to`}`); - return 1; - case 'unavailable': - case 'transport_error': - deps.log( - `[passthrough] could not capture initial screen (${snapshot.message ?? snapshot.status}); streaming live output only` - ); - break; - } + if (!snapshotResult) return 1; let showHelp = false; const initialLocalSize = deps.terminal.getSize(); - let terminalRows: number | undefined = - initialLocalSize?.rows ?? - (typeof snapshot.rows === 'number' && snapshot.rows > 0 ? snapshot.rows : undefined); + let terminalRows = pickInitialTerminalRows(initialLocalSize, snapshotResult.snapshotRows); const paintStatus = (): void => { deps.writeChunk(renderStatusLine({ name, mode: 'auto_inject', showHelp, rows: terminalRows })); }; paintStatus(); - if (initialLocalSize) { - const initialResize = await resizeWorker( - connection, - name, - initialLocalSize.rows, - initialLocalSize.cols, - deps.fetch - ); - if (!initialResize.ok) { - deps.log( - `[passthrough] could not sync agent PTY size to local terminal (${initialResize.message ?? 'unknown'}); continuing` - ); - } - } + await syncInitialPtySize(connection, name, initialLocalSize, 'passthrough', deps); const wsUrl = toWsUrl(connection.url); const headers: Record = {}; diff --git a/src/cli/lib/attach.ts b/src/cli/lib/attach.ts index 360bb91d8..2207f6660 100644 --- a/src/cli/lib/attach.ts +++ b/src/cli/lib/attach.ts @@ -1,14 +1,24 @@ /** - * Shared helpers for attach-style CLI commands. + * Shared helpers for attach-style CLI commands (`view`, `drive`, + * `passthrough`). * - * Every attach verb (`view`, `drive`, `relay`) needs to render the agent's - * *current* visible screen before it starts streaming live updates — - * otherwise the user attaches to a quiet agent and stares at a blank - * terminal until the agent happens to produce more output. This module - * wraps the broker's snapshot endpoint so each verb gets that for one line - * of code. + * - `captureAndRenderSnapshot` renders the agent's current visible screen + * so the user doesn't attach to a quiet agent and stare at a blank + * terminal until the next output. + * - `prepareAttachTarget` / `pickInitialTerminalRows` / `syncInitialPtySize` + * / `switchInboundDeliveryModeOrAbort` / `captureInitialSnapshot` are + * the take-over prep steps that `drive` and `passthrough` both run on + * attach; centralised here so the two verbs stay in lockstep. */ +import type { InboundDeliveryMode } from '@agent-relay/sdk'; + +import { + resolveBrokerConnection, + type BrokerConnection, + type BrokerConnectionDeps, + type BrokerConnectionOptions, +} from './broker-connection.js'; import { createBrokerClient, mapBrokerSdkFailure } from './sdk-client.js'; /** Connection metadata used to call the broker's snapshot endpoint. */ @@ -111,3 +121,185 @@ export async function captureAndRenderSnapshot( return { status: 'ok', rows, cols, cursor }; } + +/** ----- Interactive attach prep helpers ----- */ + +/** Validated attach target: trimmed agent name + resolved broker connection. */ +export interface AttachTarget { + name: string; + connection: BrokerConnection; +} + +/** Dependencies for `prepareAttachTarget` — connection lookup + error sink. */ +export interface PrepareAttachTargetDeps extends BrokerConnectionDeps { + error: (...args: unknown[]) => void; +} + +/** + * Trim the agent name and resolve the broker connection (flag → env → + * `connection.json`). Writes the appropriate error and returns `null` on + * either failure so every interactive attach verb rejects empty or + * unreachable targets consistently. + */ +export function prepareAttachTarget( + agentName: string, + options: BrokerConnectionOptions, + deps: PrepareAttachTargetDeps +): AttachTarget | null { + const name = agentName.trim(); + if (!name) { + deps.error('Error: agent name is required'); + return null; + } + const connection = resolveBrokerConnection(options, deps); + if (!connection) { + deps.error( + 'Error: could not locate broker connection. Pass --broker-url, set RELAY_BROKER_URL, ' + + 'or run from a directory containing .agent-relay/connection.json.' + ); + return null; + } + return { name, connection }; +} + +/** + * Pick the status-line row. Prefers the LOCAL terminal's height (the + * status line must land where the human is looking) and falls back to + * the snapshot's PTY rows, then `undefined` so the renderer applies its + * own default. + */ +export function pickInitialTerminalRows( + localSize: { rows: number; cols: number } | null, + snapshotRows: number | undefined +): number | undefined { + if (localSize) return localSize.rows; + if (typeof snapshotRows === 'number' && snapshotRows > 0) return snapshotRows; + return undefined; +} + +/** + * Sync the agent's PTY to the driver's local terminal size. tmux / + * screen / ssh all do this — without it a TUI in the agent renders into + * the size the PTY was spawned with, ignoring the human's viewport. + * Best-effort: a failure is annoying but not fatal. Skipped entirely + * when `localSize` is `null` (stdout isn't a TTY). + */ +export async function syncInitialPtySize( + connection: BrokerConnection, + name: string, + localSize: { rows: number; cols: number } | null, + verb: string, + deps: { fetch: typeof globalThis.fetch; log: (...args: unknown[]) => void } +): Promise { + if (!localSize) return; + try { + await createBrokerClient(connection, deps.fetch).resizePty(name, localSize.rows, localSize.cols); + } catch (err: unknown) { + const failure = mapBrokerSdkFailure(err); + deps.log( + `[${verb}] could not sync agent PTY size to local terminal (${failure.message ?? 'unknown'}); continuing` + ); + } +} + +/** + * Read the worker's prior inbound delivery mode and flip it to + * `targetMode`. Returns the previous mode on success so the caller can + * restore it on detach; returns `null` (and writes an error) when the + * flip fails so the caller bails before touching the terminal. + * + * Non-404 errors are surfaced as `Error: could not ${actionPhrase}: + * ${message}` so callers pass verb-appropriate wording (e.g. "switch to + * manual_flush mode" or "ensure passthrough session"). 404s get a + * uniform "no agent named X" message. + */ +export async function switchInboundDeliveryModeOrAbort( + connection: BrokerConnection, + name: string, + targetMode: InboundDeliveryMode, + actionPhrase: string, + deps: { fetch: typeof globalThis.fetch; error: (...args: unknown[]) => void } +): Promise<{ previousMode: InboundDeliveryMode | null } | null> { + let previousMode: InboundDeliveryMode | null = null; + try { + previousMode = await createBrokerClient(connection, deps.fetch).getInboundDeliveryMode(name); + } catch { + // Best-effort — fall through with null; the caller restores to + // `auto_inject` in that case so the queue can't grow indefinitely. + } + try { + await createBrokerClient(connection, deps.fetch).setInboundDeliveryMode(name, targetMode); + return { previousMode }; + } catch (err: unknown) { + const failure = mapBrokerSdkFailure(err); + if (failure.status === 404) { + deps.error(`Error: no agent named '${name}'`); + } else { + deps.error(`Error: could not ${actionPhrase}: ${failure.message ?? 'unknown error'}`); + } + return null; + } +} + +/** Dependencies for `captureInitialSnapshot`. `captureAndRenderSnapshot` + * is injectable so tests can substitute a stub. */ +export interface CaptureInitialSnapshotDeps extends AttachSnapshotDeps { + log: (...args: unknown[]) => void; + error: (...args: unknown[]) => void; + captureAndRenderSnapshot?: typeof captureAndRenderSnapshot; +} + +/** + * Render the agent's current visible screen, then dispatch on the + * outcome. Hard errors (`not_found`, `no_pty`) abort: this helper + * best-effort restores the prior delivery mode and writes the + * appropriate error before returning `null` so the caller bails. + * Transient errors warn and proceed. Returns `{ snapshotRows }` on the + * happy path so the caller can seed the status-line row fallback. + * + * `noPtyAction` is the verb phrase used in the no-PTY message + * (`agent 'X' has no PTY to ${noPtyAction}`) — e.g. "drive" or + * "attach to". + */ +export async function captureInitialSnapshot( + connection: BrokerConnection, + name: string, + previousMode: InboundDeliveryMode | null, + verb: string, + noPtyAction: string, + deps: CaptureInitialSnapshotDeps +): Promise<{ snapshotRows?: number } | null> { + const render = deps.captureAndRenderSnapshot ?? captureAndRenderSnapshot; + const snapshot = await render( + { url: connection.url, apiKey: connection.apiKey }, + name, + { fetch: deps.fetch, writeChunk: deps.writeChunk } + ); + switch (snapshot.status) { + case 'ok': + return { snapshotRows: snapshot.rows }; + case 'not_found': + case 'no_pty': { + try { + await createBrokerClient(connection, deps.fetch).setInboundDeliveryMode( + name, + previousMode ?? 'auto_inject' + ); + } catch { + // best-effort restore + } + const fallback = + snapshot.status === 'not_found' + ? `no agent named '${name}'` + : `agent '${name}' has no PTY to ${noPtyAction}`; + deps.error(`Error: ${snapshot.message ?? fallback}`); + return null; + } + case 'unavailable': + case 'transport_error': + deps.log( + `[${verb}] could not capture initial screen (${snapshot.message ?? snapshot.status}); streaming live output only` + ); + return { snapshotRows: snapshot.rows }; + } +}