@stigmer/runner 3.15.3-dev.20260914100347 → 3.15.3-dev.20260916211208
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/.build-fingerprint +1 -1
- package/dist/__test-utils__/approval-contract/types.d.ts +1 -1
- package/dist/__test-utils__/hermetic-activity.d.ts +61 -7
- package/dist/__test-utils__/hermetic-activity.js +100 -9
- package/dist/__test-utils__/hermetic-activity.js.map +1 -1
- package/dist/__test-utils__/model-registry-fixture.d.ts +22 -3
- package/dist/__test-utils__/model-registry-fixture.js +22 -7
- package/dist/__test-utils__/model-registry-fixture.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/contract-subject.d.ts +2 -2
- package/dist/activities/execute-cursor/__test-utils__/contract-subject.js +14 -11
- package/dist/activities/execute-cursor/__test-utils__/contract-subject.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/fold.d.ts +61 -0
- package/dist/activities/execute-cursor/__test-utils__/fold.js +94 -0
- package/dist/activities/execute-cursor/__test-utils__/fold.js.map +1 -0
- package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js +1 -1
- package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.d.ts +52 -14
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js +46 -12
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/scripted-agent.d.ts +21 -28
- package/dist/activities/execute-cursor/__test-utils__/scripted-agent.js +27 -11
- package/dist/activities/execute-cursor/__test-utils__/scripted-agent.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.d.ts +70 -26
- package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.js +82 -32
- package/dist/activities/execute-cursor/__test-utils__/scripted-sdk.js.map +1 -1
- package/dist/activities/execute-cursor/adapter.d.ts +8 -7
- package/dist/activities/execute-cursor/adapter.js +13 -9
- package/dist/activities/execute-cursor/adapter.js.map +1 -1
- package/dist/activities/execute-cursor/approval-policy.d.ts +10 -10
- package/dist/activities/execute-cursor/approval-policy.js +12 -23
- package/dist/activities/execute-cursor/approval-policy.js.map +1 -1
- package/dist/activities/execute-cursor/approval-state.d.ts +35 -1
- package/dist/activities/execute-cursor/approval-state.js +65 -0
- package/dist/activities/execute-cursor/approval-state.js.map +1 -1
- package/dist/activities/execute-cursor/{message-translator.d.ts → boundary-rows.d.ts} +45 -341
- package/dist/activities/execute-cursor/boundary-rows.js +1018 -0
- package/dist/activities/execute-cursor/boundary-rows.js.map +1 -0
- package/dist/activities/execute-cursor/cursor-capabilities.d.ts +1 -1
- package/dist/activities/execute-cursor/cursor-capabilities.js +1 -1
- package/dist/activities/execute-cursor/cursor-event-recorder.d.ts +33 -14
- package/dist/activities/execute-cursor/cursor-event-recorder.js +43 -21
- package/dist/activities/execute-cursor/cursor-event-recorder.js.map +1 -1
- package/dist/activities/execute-cursor/prompt-builder.d.ts +3 -3
- package/dist/activities/execute-cursor/prompt-builder.js +4 -4
- package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
- package/dist/activities/execute-cursor/sdk-warmup.d.ts +15 -6
- package/dist/activities/execute-cursor/sdk-warmup.js +15 -6
- package/dist/activities/execute-cursor/sdk-warmup.js.map +1 -1
- package/dist/activities/execute-cursor/service-tier.js +1 -2
- package/dist/activities/execute-cursor/service-tier.js.map +1 -1
- package/dist/activities/execute-cursor/session-lifecycle.d.ts +38 -59
- package/dist/activities/execute-cursor/session-lifecycle.js +59 -96
- package/dist/activities/execute-cursor/session-lifecycle.js.map +1 -1
- package/dist/activities/execute-cursor/session-store.d.ts +100 -0
- package/dist/activities/execute-cursor/session-store.js +157 -0
- package/dist/activities/execute-cursor/session-store.js.map +1 -0
- package/dist/activities/execute-cursor/sub-agent-steps.d.ts +79 -0
- package/dist/activities/execute-cursor/sub-agent-steps.js +180 -0
- package/dist/activities/execute-cursor/sub-agent-steps.js.map +1 -0
- package/dist/activities/execute-cursor/tool-result.d.ts +49 -0
- package/dist/activities/execute-cursor/tool-result.js +130 -0
- package/dist/activities/execute-cursor/tool-result.js.map +1 -0
- package/dist/activities/execute-cursor/translator.d.ts +178 -0
- package/dist/activities/execute-cursor/translator.js +485 -0
- package/dist/activities/execute-cursor/translator.js.map +1 -0
- package/dist/activities/execute-cursor/turn-boundary.d.ts +15 -5
- package/dist/activities/execute-cursor/turn-boundary.js +10 -14
- package/dist/activities/execute-cursor/turn-boundary.js.map +1 -1
- package/dist/activities/execute-cursor/turn-settle.d.ts +11 -7
- package/dist/activities/execute-cursor/turn-settle.js +47 -34
- package/dist/activities/execute-cursor/turn-settle.js.map +1 -1
- package/dist/activities/execute-cursor/turn-setup.d.ts +2 -2
- package/dist/activities/execute-cursor/turn-setup.js +5 -5
- package/dist/activities/execute-cursor/turn-setup.js.map +1 -1
- package/dist/activities/execute-cursor/turn-stream.d.ts +23 -14
- package/dist/activities/execute-cursor/turn-stream.js +41 -28
- package/dist/activities/execute-cursor/turn-stream.js.map +1 -1
- package/dist/activities/execute-cursor/turn.d.ts +4 -4
- package/dist/activities/execute-cursor/turn.js +5 -5
- package/dist/activities/execute-cursor/usage-pricing.d.ts +2 -2
- package/dist/activities/execute-cursor/usage-pricing.js +2 -2
- package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.d.ts +2 -2
- package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.js +2 -2
- package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.d.ts +16 -11
- package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js +22 -10
- package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js.map +1 -1
- package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.js +1 -1
- package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.js.map +1 -1
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.d.ts +2 -3
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.js +2 -3
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.js.map +1 -1
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.d.ts +12 -3
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js +18 -4
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js.map +1 -1
- package/dist/activities/execute-deep-agent/__test-utils__/v3-event-fixtures.d.ts +10 -1
- package/dist/activities/execute-deep-agent/__test-utils__/v3-event-fixtures.js +24 -11
- package/dist/activities/execute-deep-agent/__test-utils__/v3-event-fixtures.js.map +1 -1
- package/dist/activities/execute-deep-agent/adapter.d.ts +1 -1
- package/dist/activities/execute-deep-agent/adapter.js +1 -1
- package/dist/activities/execute-deep-agent/approval-file-change.d.ts +19 -19
- package/dist/activities/execute-deep-agent/approval-file-change.js +16 -15
- package/dist/activities/execute-deep-agent/approval-file-change.js.map +1 -1
- package/dist/activities/execute-deep-agent/deep-agent-capabilities.d.ts +2 -2
- package/dist/activities/execute-deep-agent/deep-agent-capabilities.js +2 -2
- package/dist/activities/execute-deep-agent/hitl.d.ts +2 -2
- package/dist/activities/execute-deep-agent/hitl.js +2 -2
- package/dist/activities/execute-deep-agent/inline-publisher.d.ts +13 -3
- package/dist/activities/execute-deep-agent/inline-publisher.js +3 -3
- package/dist/activities/execute-deep-agent/inline-publisher.js.map +1 -1
- package/dist/activities/execute-deep-agent/prompt-builder.d.ts +3 -3
- package/dist/activities/execute-deep-agent/prompt-builder.js +6 -7
- package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
- package/dist/activities/execute-deep-agent/streaming-side-effects.d.ts +16 -8
- package/dist/activities/execute-deep-agent/streaming-side-effects.js +20 -33
- package/dist/activities/execute-deep-agent/streaming-side-effects.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-transformer.d.ts +3 -3
- package/dist/activities/execute-deep-agent/subagent-transformer.js +1 -1
- package/dist/activities/execute-deep-agent/translator.d.ts +134 -0
- package/dist/activities/execute-deep-agent/translator.js +418 -0
- package/dist/activities/execute-deep-agent/translator.js.map +1 -0
- package/dist/activities/execute-deep-agent/turn-settle.d.ts +13 -7
- package/dist/activities/execute-deep-agent/turn-settle.js +34 -45
- package/dist/activities/execute-deep-agent/turn-settle.js.map +1 -1
- package/dist/activities/execute-deep-agent/turn-setup.d.ts +8 -8
- package/dist/activities/execute-deep-agent/turn-setup.js +7 -7
- package/dist/activities/execute-deep-agent/turn-stream.d.ts +13 -10
- package/dist/activities/execute-deep-agent/turn-stream.js +44 -41
- package/dist/activities/execute-deep-agent/turn-stream.js.map +1 -1
- package/dist/activities/execute-deep-agent/turn.d.ts +3 -3
- package/dist/activities/execute-deep-agent/turn.js +3 -3
- package/dist/config.d.ts +2 -2
- package/dist/harness/approval-decisions.d.ts +1 -1
- package/dist/harness/approval-decisions.js +1 -1
- package/dist/harness/capabilities.d.ts +21 -11
- package/dist/harness/capabilities.js +2 -2
- package/dist/harness/capture.d.ts +4 -4
- package/dist/harness/capture.js +4 -4
- package/dist/harness/persist-chokepoint.d.ts +3 -3
- package/dist/harness/persist-chokepoint.js +3 -3
- package/dist/harness/registry.d.ts +2 -2
- package/dist/harness/registry.js +2 -2
- package/dist/harness/run-turn.d.ts +6 -4
- package/dist/harness/run-turn.js +31 -9
- package/dist/harness/run-turn.js.map +1 -1
- package/dist/harness/stop-controller.d.ts +1 -1
- package/dist/harness/stop-controller.js +1 -1
- package/dist/harness/terminal-table.d.ts +7 -7
- package/dist/harness/terminal-table.js +7 -7
- package/dist/harness/transcript/builder.d.ts +182 -0
- package/dist/harness/transcript/builder.js +693 -0
- package/dist/harness/transcript/builder.js.map +1 -0
- package/dist/harness/transcript/events.d.ts +218 -0
- package/dist/harness/transcript/events.js +50 -0
- package/dist/harness/transcript/events.js.map +1 -0
- package/dist/harness/transcript/state.d.ts +110 -0
- package/dist/harness/transcript/state.js +148 -0
- package/dist/harness/transcript/state.js.map +1 -0
- package/dist/harness/turn-context.d.ts +54 -38
- package/dist/harness/turn-context.js +42 -43
- package/dist/harness/turn-context.js.map +1 -1
- package/dist/harness/types.d.ts +63 -22
- package/dist/harness/types.js +12 -11
- package/dist/harness/types.js.map +1 -1
- package/dist/harness/usage-accumulator.d.ts +2 -2
- package/dist/harness/usage-accumulator.js +2 -2
- package/dist/harness-adapters.d.ts +5 -4
- package/dist/harness-adapters.js +5 -4
- package/dist/harness-adapters.js.map +1 -1
- package/dist/middleware/approval-gate.d.ts +6 -0
- package/dist/middleware/approval-gate.js +7 -57
- package/dist/middleware/approval-gate.js.map +1 -1
- package/dist/middleware/cost-advisory.d.ts +3 -3
- package/dist/middleware/cost-advisory.js +3 -3
- package/dist/middleware/index.d.ts +1 -1
- package/dist/middleware/index.js +1 -1
- package/dist/runner-manager.js +1 -1
- package/dist/runner-manager.js.map +1 -1
- package/dist/shared/approval-policy.d.ts +53 -0
- package/dist/shared/approval-policy.js +76 -0
- package/dist/shared/approval-policy.js.map +1 -1
- package/dist/shared/args-preview.d.ts +45 -22
- package/dist/shared/args-preview.js +51 -41
- package/dist/shared/args-preview.js.map +1 -1
- package/dist/shared/attachment-resolver.d.ts +4 -4
- package/dist/shared/attachment-resolver.js +4 -4
- package/dist/shared/attachment-vision.d.ts +2 -1
- package/dist/shared/attachment-vision.js.map +1 -1
- package/dist/shared/attachment-zip.d.ts +2 -2
- package/dist/shared/attachment-zip.js +2 -2
- package/dist/shared/cost-guard.d.ts +2 -2
- package/dist/shared/cost-guard.js +2 -2
- package/dist/shared/exact-apply.d.ts +1 -1
- package/dist/shared/exact-apply.js +1 -1
- package/dist/shared/execution-context.d.ts +2 -2
- package/dist/shared/execution-context.js +2 -2
- package/dist/shared/extract-structured-output.d.ts +2 -2
- package/dist/shared/extract-structured-output.js +2 -2
- package/dist/shared/filereview/cas-touched.d.ts +2 -2
- package/dist/shared/filereview/cas-touched.js +2 -2
- package/dist/shared/heartbeat.d.ts +1 -1
- package/dist/shared/heartbeat.js +1 -1
- package/dist/shared/llm-backend.d.ts +0 -4
- package/dist/shared/llm-backend.js +0 -4
- package/dist/shared/llm-backend.js.map +1 -1
- package/dist/shared/model-error.d.ts +1 -2
- package/dist/shared/model-error.js +1 -2
- package/dist/shared/model-error.js.map +1 -1
- package/dist/shared/persist-decision.d.ts +18 -24
- package/dist/shared/persist-decision.js +19 -11
- package/dist/shared/persist-decision.js.map +1 -1
- package/dist/shared/prompt-sections.d.ts +4 -4
- package/dist/shared/prompt-sections.js +4 -4
- package/dist/shared/skill-resolver.d.ts +3 -3
- package/dist/shared/skill-resolver.js +3 -3
- package/dist/shared/status-offload.d.ts +1 -1
- package/dist/shared/status-offload.js +2 -2
- package/dist/shared/status-offload.js.map +1 -1
- package/dist/shared/streaming-scheduler.d.ts +2 -7
- package/dist/shared/streaming-scheduler.js +6 -14
- package/dist/shared/streaming-scheduler.js.map +1 -1
- package/dist/shared/subagent-rows.d.ts +2 -2
- package/dist/shared/subagent-rows.js +2 -2
- package/dist/shared/todos.d.ts +3 -3
- package/dist/shared/todos.js +3 -3
- package/dist/shared/tool-row.d.ts +19 -1
- package/dist/shared/tool-row.js +36 -2
- package/dist/shared/tool-row.js.map +1 -1
- package/dist/shared/worker-shutdown.d.ts +1 -1
- package/dist/shared/worker-shutdown.js +1 -1
- package/dist/shared/workspace/session-provision.d.ts +1 -1
- package/dist/shared/workspace/session-provision.js +1 -1
- package/dist/shared/workspace/sources/empty.d.ts +1 -1
- package/dist/shared/workspace/sources/empty.js +1 -2
- package/dist/shared/workspace/sources/empty.js.map +1 -1
- package/dist/shared/workspace/sources/git.js +4 -4
- package/dist/shared/workspace/sources/git.js.map +1 -1
- package/dist/shared/workspace/sources/local-path.js +6 -6
- package/dist/shared/workspace/sources/local-path.js.map +1 -1
- package/dist/shared/workspace/stigmer-link.d.ts +1 -1
- package/dist/shared/workspace/stigmer-link.js +1 -1
- package/dist/shared/workspace/types.d.ts +2 -5
- package/dist/shared/workspace/types.js +0 -6
- package/dist/shared/workspace/types.js.map +1 -1
- package/dist/shared/workspace/writeback-coordinator.d.ts +17 -4
- package/dist/shared/workspace/writeback-coordinator.js +7 -9
- package/dist/shared/workspace/writeback-coordinator.js.map +1 -1
- package/package.json +5 -5
- package/src/__test-utils__/__tests__/execution-record-barrier.test.ts +70 -0
- package/src/__test-utils__/__tests__/harness-contract-self-check.test.ts +35 -1
- package/src/__test-utils__/approval-contract/contract.ts +1 -1
- package/src/__test-utils__/approval-contract/types.ts +1 -1
- package/src/__test-utils__/harness-boot-order-child.ts +1 -1
- package/src/__test-utils__/harness-contract/contract.ts +8 -8
- package/src/__test-utils__/harness-contract/recording-sink.ts +11 -1
- package/src/__test-utils__/harness-contract/runtime-contract.ts +52 -16
- package/src/__test-utils__/harness-contract/scripted-adapter.ts +47 -43
- package/src/__test-utils__/hermetic-activity.ts +113 -9
- package/src/__test-utils__/model-registry-fixture.ts +31 -7
- package/src/__test-utils__/module-specifiers.ts +32 -3
- package/src/__tests__/harness-boot-order.test.ts +1 -1
- package/src/__tests__/harness-contract.test.ts +1 -1
- package/src/__tests__/runner-manager.test.ts +1 -1
- package/src/activities/execute-cursor/__test-utils__/__tests__/scripted-double.test.ts +32 -8
- package/src/activities/execute-cursor/__test-utils__/contract-subject.ts +12 -10
- package/src/activities/execute-cursor/__test-utils__/fold.ts +116 -0
- package/src/activities/execute-cursor/__test-utils__/gateway-substrate.ts +1 -1
- package/src/activities/execute-cursor/__test-utils__/hermetic-cursor.ts +72 -19
- package/src/activities/execute-cursor/__test-utils__/scripted-agent.ts +35 -29
- package/src/activities/execute-cursor/__test-utils__/scripted-sdk.ts +94 -38
- package/src/activities/execute-cursor/__tests__/approval-gate.test.ts +2 -1
- package/src/activities/execute-cursor/__tests__/approval-state.test.ts +14 -21
- package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +3 -3
- package/src/activities/execute-cursor/__tests__/coarse-fingerprint.test.ts +2 -1
- package/src/activities/execute-cursor/__tests__/cursor-baseurl-routing.test.ts +10 -8
- package/src/activities/execute-cursor/__tests__/cursor-event-recorder.test.ts +142 -0
- package/src/activities/execute-cursor/__tests__/cursor-fetch-interceptor-bypass.test.ts +24 -12
- package/src/activities/execute-cursor/__tests__/cursor-generate-image-live.test.ts +7 -2
- package/src/activities/execute-cursor/__tests__/cursor-hook-protocol-live.test.ts +163 -0
- package/src/activities/execute-cursor/__tests__/cursor-sdk-auth-smoke.test.ts +23 -20
- package/src/activities/execute-cursor/__tests__/cursor-transcript-live.test.ts +298 -0
- package/src/activities/execute-cursor/__tests__/deny-gate-exact-apply.test.ts +4 -2
- package/src/activities/execute-cursor/__tests__/fetch-interceptor.test.ts +1 -1
- package/src/activities/execute-cursor/__tests__/hermetic/deny-and-retry.test.ts +5 -2
- package/src/activities/execute-cursor/__tests__/hermetic/file-review-capture.test.ts +5 -2
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/mcp-tool-call.status.json +71 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/sdk-task-note.status.json +73 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.error-delta.status.json +78 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.output-first.status.json +72 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/shell-output-delta.status.json +72 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/sub-agent-delegation.status.json +109 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/thinking-turn.status.json +78 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/todo-write.status.json +119 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/tool-error.status.json +68 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/tool-only-step.status.json +67 -0
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/unattributed-hook-block.status.json +1 -1
- package/src/activities/execute-cursor/__tests__/hermetic/harness-contract.test.ts +10 -7
- package/src/activities/execute-cursor/__tests__/hermetic/mcp-tool-call.test.ts +146 -0
- package/src/activities/execute-cursor/__tests__/hermetic/pause-vs-shutdown.test.ts +8 -5
- package/src/activities/execute-cursor/__tests__/hermetic/plain-turn.test.ts +6 -2
- package/src/activities/execute-cursor/__tests__/hermetic/recovery-fresh-agent.test.ts +4 -1
- package/src/activities/execute-cursor/__tests__/hermetic/run-wait-arms.test.ts +6 -2
- package/src/activities/execute-cursor/__tests__/hermetic/sdk-task-note.test.ts +122 -0
- package/src/activities/execute-cursor/__tests__/hermetic/shell-output-delta.test.ts +276 -0
- package/src/activities/execute-cursor/__tests__/hermetic/stream-self-stop-arms.test.ts +12 -9
- package/src/activities/execute-cursor/__tests__/hermetic/sub-agent-delegation.test.ts +196 -0
- package/src/activities/execute-cursor/__tests__/hermetic/thinking-turn.test.ts +145 -0
- package/src/activities/execute-cursor/__tests__/hermetic/thrown-error-arms.test.ts +4 -1
- package/src/activities/execute-cursor/__tests__/hermetic/todo-write.test.ts +165 -0
- package/src/activities/execute-cursor/__tests__/hermetic/tool-call.test.ts +7 -4
- package/src/activities/execute-cursor/__tests__/hermetic/tool-error.test.ts +138 -0
- package/src/activities/execute-cursor/__tests__/hermetic/tool-only-step.test.ts +130 -0
- package/src/activities/execute-cursor/__tests__/hermetic/unattributed-hook-block.test.ts +17 -2
- package/src/activities/execute-cursor/__tests__/hermetic/workspace-lock-timeout.test.ts +5 -2
- package/src/activities/execute-cursor/__tests__/hitl-ledger.test.ts +43 -60
- package/src/activities/execute-cursor/__tests__/hitl-resume-history.test.ts +23 -28
- package/src/activities/execute-cursor/__tests__/hook-script.test.ts +2 -1
- package/src/activities/execute-cursor/__tests__/http2-interceptor.test.ts +1 -1
- package/src/activities/execute-cursor/__tests__/prompt-goldens.test.ts +5 -5
- package/src/activities/execute-cursor/__tests__/same-identity-reproposal.test.ts +11 -13
- package/src/activities/execute-cursor/__tests__/sequential-gate-resume.test.ts +8 -11
- package/src/activities/execute-cursor/__tests__/session-lifecycle.test.ts +86 -58
- package/src/activities/execute-cursor/__tests__/session-store.test.ts +220 -0
- package/src/activities/execute-cursor/__tests__/tool-result-image.test.ts +8 -22
- package/src/activities/execute-cursor/__tests__/translator.test.ts +594 -0
- package/src/activities/execute-cursor/__tests__/turn-boundary.test.ts +8 -5
- package/src/activities/execute-cursor/__tests__/turn-stream.test.ts +87 -63
- package/src/activities/execute-cursor/adapter.ts +13 -9
- package/src/activities/execute-cursor/approval-policy.ts +12 -24
- package/src/activities/execute-cursor/approval-state.ts +66 -1
- package/src/activities/execute-cursor/boundary-rows.ts +1132 -0
- package/src/activities/execute-cursor/cursor-capabilities.ts +1 -1
- package/src/activities/execute-cursor/cursor-event-recorder.ts +57 -28
- package/src/activities/execute-cursor/fetch-interceptor.ts +1 -1
- package/src/activities/execute-cursor/http2-interceptor.ts +1 -1
- package/src/activities/execute-cursor/prompt-builder.ts +6 -6
- package/src/activities/execute-cursor/sdk-warmup.ts +15 -6
- package/src/activities/execute-cursor/service-tier.ts +1 -2
- package/src/activities/execute-cursor/session-lifecycle.ts +63 -109
- package/src/activities/execute-cursor/session-store.ts +187 -0
- package/src/activities/execute-cursor/sub-agent-steps.ts +188 -0
- package/src/activities/execute-cursor/tool-result.ts +128 -0
- package/src/activities/execute-cursor/translator.ts +541 -0
- package/src/activities/execute-cursor/turn-boundary.ts +21 -14
- package/src/activities/execute-cursor/turn-settle.ts +46 -34
- package/src/activities/execute-cursor/turn-setup.ts +5 -5
- package/src/activities/execute-cursor/turn-stream.ts +49 -46
- package/src/activities/execute-cursor/turn.ts +5 -5
- package/src/activities/execute-cursor/usage-pricing.ts +2 -2
- package/src/activities/execute-deep-agent/__test-utils__/contract-subject.ts +2 -2
- package/src/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.ts +23 -11
- package/src/activities/execute-deep-agent/__test-utils__/hitl-script.ts +1 -1
- package/src/activities/execute-deep-agent/__test-utils__/scripted-model-module.ts +2 -3
- package/src/activities/execute-deep-agent/__test-utils__/scripted-model.ts +25 -4
- package/src/activities/execute-deep-agent/__test-utils__/v3-event-fixtures.ts +25 -11
- package/src/activities/execute-deep-agent/__tests__/adapter-graph-is-sdk-free.test.ts +1 -1
- package/src/activities/execute-deep-agent/__tests__/adapter-is-temporal-free.test.ts +1 -1
- package/src/activities/execute-deep-agent/__tests__/approval-file-change.test.ts +6 -17
- package/src/activities/execute-deep-agent/__tests__/hermetic/file-review-capture.test.ts +25 -8
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve-all-lease.turn1.status.json +2 -7
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve-all-lease.turn2.status.json +4 -14
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve.turn2.status.json +1 -5
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/file-review-capture.status.json +4 -7
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/gate.turn1.status.json +1 -5
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn1.status.json +2 -9
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn2.status.json +2 -9
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/pause.loop.after-tool.status.json +1 -4
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/pause.loop.mid-tool.status.json +1 -4
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/platform-stop.status.json +1 -4
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/recursion-limit.persisted.status.json +109 -76
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/reject.turn2.status.json +1 -5
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sequential-gates.turn2.status.json +2 -10
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sequential-gates.turn3.status.json +2 -10
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/skip.turn2.status.json +1 -5
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sub-agent-delegation.status.json +3 -6
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/tool-call.status.json +1 -4
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/worker-shutdown.status.json +1 -4
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/write-todos.status.json +92 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/harness-contract.test.ts +3 -3
- package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-approve-all-lease.test.ts +9 -5
- package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-round-trips.test.ts +3 -3
- package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-sequential-gates.test.ts +3 -3
- package/src/activities/execute-deep-agent/__tests__/hermetic/inline-artifact-across-gate.test.ts +7 -6
- package/src/activities/execute-deep-agent/__tests__/hermetic/pause-vs-shutdown.test.ts +10 -11
- package/src/activities/execute-deep-agent/__tests__/hermetic/plain-turn.test.ts +4 -4
- package/src/activities/execute-deep-agent/__tests__/hermetic/platform-stop.test.ts +9 -8
- package/src/activities/execute-deep-agent/__tests__/hermetic/recursion-limit.test.ts +21 -13
- package/src/activities/execute-deep-agent/__tests__/hermetic/resolution-error.test.ts +2 -2
- package/src/activities/execute-deep-agent/__tests__/hermetic/structured-output.test.ts +7 -7
- package/src/activities/execute-deep-agent/__tests__/hermetic/sub-agent-delegation.test.ts +28 -12
- package/src/activities/execute-deep-agent/__tests__/hermetic/tool-call.test.ts +21 -21
- package/src/activities/execute-deep-agent/__tests__/hermetic/workspace-lock-timeout.test.ts +5 -5
- package/src/activities/execute-deep-agent/__tests__/hermetic/write-todos.test.ts +160 -0
- package/src/activities/execute-deep-agent/__tests__/inline-publisher.test.ts +35 -30
- package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +3 -4
- package/src/activities/execute-deep-agent/__tests__/prompt-goldens.test.ts +12 -10
- package/src/activities/execute-deep-agent/__tests__/scripted-model.test.ts +13 -3
- package/src/activities/execute-deep-agent/__tests__/streaming-side-effects.test.ts +26 -18
- package/src/activities/execute-deep-agent/__tests__/subagent-wiring.test.ts +1 -1
- package/src/activities/execute-deep-agent/__tests__/{subagent-tracker.test.ts → translator-subagents.test.ts} +76 -57
- package/src/activities/execute-deep-agent/__tests__/{v3-status-builder.test.ts → translator-through-builder.test.ts} +213 -299
- package/src/activities/execute-deep-agent/__tests__/translator.test.ts +505 -0
- package/src/activities/execute-deep-agent/__tests__/turn-stream.test.ts +11 -29
- package/src/activities/execute-deep-agent/adapter.ts +1 -1
- package/src/activities/execute-deep-agent/approval-file-change.ts +20 -22
- package/src/activities/execute-deep-agent/deep-agent-capabilities.ts +2 -2
- package/src/activities/execute-deep-agent/hitl.ts +2 -2
- package/src/activities/execute-deep-agent/inline-publisher.ts +16 -5
- package/src/activities/execute-deep-agent/prompt-builder.ts +6 -7
- package/src/activities/execute-deep-agent/streaming-side-effects.ts +22 -31
- package/src/activities/execute-deep-agent/subagent-transformer.ts +3 -3
- package/src/activities/execute-deep-agent/translator.ts +463 -0
- package/src/activities/execute-deep-agent/turn-settle.ts +35 -43
- package/src/activities/execute-deep-agent/turn-setup.ts +8 -8
- package/src/activities/execute-deep-agent/turn-stream.ts +53 -51
- package/src/activities/execute-deep-agent/turn.ts +3 -3
- package/src/config.ts +2 -2
- package/src/harness/README.md +335 -0
- package/src/harness/__tests__/approval-decisions.test.ts +2 -2
- package/src/harness/__tests__/capture.test.ts +3 -3
- package/src/harness/__tests__/import-direction.test.ts +3 -2
- package/src/harness/__tests__/mount-skills.test.ts +4 -1
- package/src/harness/__tests__/readme-index.test.ts +339 -0
- package/src/harness/__tests__/run-turn.test.ts +131 -6
- package/src/harness/__tests__/transcript-is-engine-free.test.ts +83 -0
- package/src/harness/__tests__/transcript-writers.test.ts +191 -0
- package/src/harness/__tests__/turn-attachments-vision.test.ts +5 -2
- package/src/harness/__tests__/turn-context.test.ts +5 -5
- package/src/harness/approval-decisions.ts +1 -1
- package/src/harness/capabilities.ts +21 -11
- package/src/harness/capture.ts +4 -4
- package/src/harness/persist-chokepoint.ts +3 -3
- package/src/harness/registry.ts +2 -2
- package/src/harness/run-turn.ts +33 -9
- package/src/harness/stop-controller.ts +1 -1
- package/src/harness/terminal-table.ts +7 -7
- package/src/harness/transcript/__tests__/builder.test.ts +975 -0
- package/src/harness/transcript/__tests__/state.test.ts +143 -0
- package/src/harness/transcript/builder.ts +755 -0
- package/src/harness/transcript/events.ts +262 -0
- package/src/harness/transcript/state.ts +167 -0
- package/src/harness/turn-context.ts +61 -46
- package/src/harness/types.ts +64 -22
- package/src/harness/usage-accumulator.ts +2 -2
- package/src/harness-adapters.ts +5 -4
- package/src/middleware/__tests__/cost-advisory.test.ts +2 -2
- package/src/middleware/approval-gate.ts +7 -74
- package/src/middleware/cost-advisory.ts +3 -3
- package/src/middleware/index.ts +1 -1
- package/src/runner-manager.ts +1 -1
- package/src/shared/__tests__/approval-policy.test.ts +45 -0
- package/src/shared/__tests__/args-preview.test.ts +40 -20
- package/src/shared/__tests__/attachment-naming.test.ts +1 -1
- package/src/shared/__tests__/attachment-resolver.test.ts +5 -5
- package/src/shared/__tests__/attachment-zip.test.ts +1 -1
- package/src/shared/__tests__/blueprint-resolver.test.ts +1 -1
- package/src/shared/__tests__/cost-guard.test.ts +1 -1
- package/src/shared/__tests__/exact-apply.test.ts +2 -1
- package/src/shared/__tests__/persist-decision.test.ts +19 -33
- package/src/shared/__tests__/prompt-sections.test.ts +1 -1
- package/src/shared/__tests__/stamp-flowed-rows.test.ts +1 -1
- package/src/shared/__tests__/status-offload.test.ts +1 -1
- package/src/shared/__tests__/streaming-scheduler.test.ts +8 -9
- package/src/shared/__tests__/todos.test.ts +1 -1
- package/src/shared/__tests__/tool-row.test.ts +1 -1
- package/src/shared/approval-policy.ts +95 -0
- package/src/shared/args-preview.ts +51 -42
- package/src/shared/attachment-resolver.ts +4 -4
- package/src/shared/attachment-vision.ts +2 -1
- package/src/shared/attachment-zip.ts +2 -2
- package/src/shared/cost-guard.ts +2 -2
- package/src/shared/exact-apply.ts +1 -1
- package/src/shared/execution-context.ts +2 -2
- package/src/shared/extract-structured-output.ts +2 -2
- package/src/shared/filereview/__tests__/capture.test.ts +9 -5
- package/src/shared/filereview/__tests__/git-substrate.test.ts +4 -1
- package/src/shared/filereview/__tests__/proxy-reconcile.test.ts +3 -3
- package/src/shared/filereview/cas-touched.ts +2 -2
- package/src/shared/heartbeat.ts +1 -1
- package/src/shared/llm-backend.ts +0 -4
- package/src/shared/model-error.ts +1 -2
- package/src/shared/persist-decision.ts +19 -28
- package/src/shared/prompt-sections.ts +4 -4
- package/src/shared/skill-resolver.ts +3 -3
- package/src/shared/status-offload.ts +2 -2
- package/src/shared/streaming-scheduler.ts +8 -13
- package/src/shared/subagent-rows.ts +2 -2
- package/src/shared/todos.ts +3 -3
- package/src/shared/tool-row.ts +38 -2
- package/src/shared/worker-shutdown.ts +1 -1
- package/src/shared/workspace/__tests__/git-source.test.ts +3 -3
- package/src/shared/workspace/__tests__/provisioner.test.ts +5 -5
- package/src/shared/workspace/__tests__/session-provision.test.ts +2 -2
- package/src/shared/workspace/__tests__/writeback-coordinator.integration.test.ts +8 -9
- package/src/shared/workspace/__tests__/writeback-coordinator.test.ts +25 -25
- package/src/shared/workspace/provisioner.ts +1 -1
- package/src/shared/workspace/session-provision.ts +1 -1
- package/src/shared/workspace/sources/empty.ts +2 -2
- package/src/shared/workspace/sources/git.ts +3 -4
- package/src/shared/workspace/sources/local-path.ts +5 -6
- package/src/shared/workspace/stigmer-link.ts +1 -1
- package/src/shared/workspace/types.ts +2 -5
- package/src/shared/workspace/writeback-coordinator.ts +23 -12
- package/dist/activities/execute-cursor/delta-enricher.d.ts +0 -87
- package/dist/activities/execute-cursor/delta-enricher.js +0 -265
- package/dist/activities/execute-cursor/delta-enricher.js.map +0 -1
- package/dist/activities/execute-cursor/message-translator.js +0 -2040
- package/dist/activities/execute-cursor/message-translator.js.map +0 -1
- package/dist/activities/execute-cursor/todo-tracker.d.ts +0 -40
- package/dist/activities/execute-cursor/todo-tracker.js +0 -80
- package/dist/activities/execute-cursor/todo-tracker.js.map +0 -1
- package/dist/activities/execute-deep-agent/execution-state.d.ts +0 -54
- package/dist/activities/execute-deep-agent/execution-state.js +0 -68
- package/dist/activities/execute-deep-agent/execution-state.js.map +0 -1
- package/dist/activities/execute-deep-agent/status-builder-shared.d.ts +0 -65
- package/dist/activities/execute-deep-agent/status-builder-shared.js +0 -90
- package/dist/activities/execute-deep-agent/status-builder-shared.js.map +0 -1
- package/dist/activities/execute-deep-agent/subagent-tracker.d.ts +0 -113
- package/dist/activities/execute-deep-agent/subagent-tracker.js +0 -400
- package/dist/activities/execute-deep-agent/subagent-tracker.js.map +0 -1
- package/dist/activities/execute-deep-agent/v3-events.d.ts +0 -102
- package/dist/activities/execute-deep-agent/v3-events.js +0 -35
- package/dist/activities/execute-deep-agent/v3-events.js.map +0 -1
- package/dist/activities/execute-deep-agent/v3-protocol-normalizer.d.ts +0 -15
- package/dist/activities/execute-deep-agent/v3-protocol-normalizer.js +0 -235
- package/dist/activities/execute-deep-agent/v3-protocol-normalizer.js.map +0 -1
- package/dist/activities/execute-deep-agent/v3-status-builder.d.ts +0 -111
- package/dist/activities/execute-deep-agent/v3-status-builder.js +0 -457
- package/dist/activities/execute-deep-agent/v3-status-builder.js.map +0 -1
- package/dist/shared/execution-status-writer.d.ts +0 -31
- package/dist/shared/execution-status-writer.js +0 -43
- package/dist/shared/execution-status-writer.js.map +0 -1
- package/src/activities/execute-cursor/__tests__/delta-enricher.test.ts +0 -310
- package/src/activities/execute-cursor/__tests__/message-translator.test.ts +0 -1033
- package/src/activities/execute-cursor/__tests__/todo-tracker.test.ts +0 -498
- package/src/activities/execute-cursor/delta-enricher.ts +0 -307
- package/src/activities/execute-cursor/message-translator.ts +0 -2319
- package/src/activities/execute-cursor/todo-tracker.ts +0 -91
- package/src/activities/execute-deep-agent/__tests__/execution-state-extended.test.ts +0 -148
- package/src/activities/execute-deep-agent/__tests__/execution-state.test.ts +0 -154
- package/src/activities/execute-deep-agent/__tests__/status-builder-shared.test.ts +0 -47
- package/src/activities/execute-deep-agent/__tests__/v3-protocol-normalizer.test.ts +0 -324
- package/src/activities/execute-deep-agent/execution-state.ts +0 -78
- package/src/activities/execute-deep-agent/status-builder-shared.ts +0 -142
- package/src/activities/execute-deep-agent/subagent-tracker.ts +0 -479
- package/src/activities/execute-deep-agent/v3-events.ts +0 -153
- package/src/activities/execute-deep-agent/v3-protocol-normalizer.ts +0 -264
- package/src/activities/execute-deep-agent/v3-status-builder.ts +0 -565
- package/src/shared/execution-status-writer.ts +0 -56
|
@@ -0,0 +1,693 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TranscriptBuilder — folds `TranscriptEvent`s into the transcript half of
|
|
3
|
+
* the AgentExecutionStatus proto it is handed: messages, tool-call rows and
|
|
4
|
+
* their approval status, sub-agent rows, todos, artifacts, write-backs.
|
|
5
|
+
*
|
|
6
|
+
* The one transcript builder, for every harness (#1097). It
|
|
7
|
+
* was the native harness's `V3StatusBuilder` (the one builder there since
|
|
8
|
+
* #1096 retired the v2 `StatusBuilder`), promoted here on 2026-09-14
|
|
9
|
+
* unchanged and then cut engine-neutral one rule per commit:
|
|
10
|
+
* it reads no engine envelope, decides nothing about approval,
|
|
11
|
+
* and knows no engine's tool names or namespace grammar — every engine
|
|
12
|
+
* fact reaches it as a translator's output, on one union. The Cursor
|
|
13
|
+
* harness feeds it through a translator; the runtime constructs it once
|
|
14
|
+
* per turn and hands it to every adapter as `TurnSink.transcript`, so
|
|
15
|
+
* a transcript row has exactly one way into the status — a source-walking
|
|
16
|
+
* fence, `harness/__tests__/transcript-writers.test.ts`, refuses a second.
|
|
17
|
+
*
|
|
18
|
+
* ONE set of handlers over ONE scope shape: every handler takes
|
|
19
|
+
* the scope's {@link Transcript} — the root's over `status.messages`, or a
|
|
20
|
+
* sub-agent's over its row's — from `TranscriptState`, so a sub-agent's
|
|
21
|
+
* transcript is folded by exactly the rules the root's is. Until #1097 the
|
|
22
|
+
* sub-agents' half was a second module (`SubAgentTracker`) with its own copy
|
|
23
|
+
* of every handler, differing only in which array it pushed into — the
|
|
24
|
+
* memo's third copy of the folding rule, and the one whose namespace filter
|
|
25
|
+
* differed from the root's by one segment (found in #1096: the root
|
|
26
|
+
* filed text under `model_request:<uuid>` and looked the tool's parent up
|
|
27
|
+
* under `""`, so every native tool row sat on an empty message after its
|
|
28
|
+
* text). With scope a single value per transcript, that class of miss
|
|
29
|
+
* cannot exist.
|
|
30
|
+
*
|
|
31
|
+
* The rules this builder owns, each with an arm in
|
|
32
|
+
* `harness/transcript/__tests__/builder.test.ts` and the harness whose rule
|
|
33
|
+
* it generalises in parentheses:
|
|
34
|
+
*
|
|
35
|
+
* - A row per `callId`, indexed over the status as handed and over what
|
|
36
|
+
* {@link seed} appends (a reinvocation's prior rows, indexed by the same
|
|
37
|
+
* routine, so a builder born before the seed is no different from one
|
|
38
|
+
* born after it), reconciled in place on a re-emitted start — an UNSETTLED row becomes
|
|
39
|
+
* RUNNING (a WAITING row on native's durable-checkpoint resume; an
|
|
40
|
+
* INTERRUPTED row on a recovery replay, the enum's supersede rule) and
|
|
41
|
+
* takes the args and preview it lacked — never duplicated. Every row
|
|
42
|
+
* with args carries `argsPreview`, elided and redacted, salient fields
|
|
43
|
+
* verbatim. A finish or an error is an upsert: status
|
|
44
|
+
* monotonic, `completedAt` once, a non-empty result or message
|
|
45
|
+
* overwrites, an empty one never clears; a re-emit that
|
|
46
|
+
* changes nothing forces no persist; a fact the harness
|
|
47
|
+
* observed earlier and delivers later stamps its own instant
|
|
48
|
+
* (`observedAt`); an output chunk for a settled row is stale
|
|
49
|
+
* and dropped — the completion's result is the whole output.
|
|
50
|
+
* - The AI-message boundary (native's own rule, made correct by
|
|
51
|
+
* scoping): a tool row attaches to the scope's current AI message — the
|
|
52
|
+
* latest with text, the seed's last AI message over a seeded transcript
|
|
53
|
+
* — and to a new empty one only when the scope has none.
|
|
54
|
+
* - A THINKING row per `runId`, a text message per `runId`; a
|
|
55
|
+
* new run closes the previous message's streaming flag, and a run's
|
|
56
|
+
* finish closes both its text and its thinking.
|
|
57
|
+
* - A sub-agent row per `subAgentId`, opened by `sub_agent_started`,
|
|
58
|
+
* closed COMPLETED/FAILED by `sub_agent_finished/failed` with its
|
|
59
|
+
* transcript's streaming flags cleared; a known id is never re-opened.
|
|
60
|
+
* CANCELLED is not this builder's: `shared/subagent-rows.ts`
|
|
61
|
+
* `cancelInProgressSubAgentProtos` is the one home of that transition.
|
|
62
|
+
* - `approval_proposed` is the one place a row is ever WAITING: a known
|
|
63
|
+
* row REOPENS — WAITING, the outcome fields cleared,
|
|
64
|
+
* `approvalRequestedAt` stamped once, the proposal's args and preview
|
|
65
|
+
* taken when carried (Cursor's `markWaitingApproval` + `applyGateInput`);
|
|
66
|
+
* an unknown call gets a WAITING row on the scope's current AI message,
|
|
67
|
+
* the text that proposed it (native's post-stream seed, before #1097 a new
|
|
68
|
+
* empty message of its own). Either way {@link awaitingApproval} is set.
|
|
69
|
+
* - `system_note` is a SYSTEM message in the scope, in the harness's
|
|
70
|
+
* voice; it hosts no rows and moves no boundary.
|
|
71
|
+
* - A completed `ToolKind.TODO` call in the ROOT scope is projected into
|
|
72
|
+
* `status.todos` through the shared `applyTodoUpdate`, a full replace
|
|
73
|
+
* unless the call's args say `merge: true` (the row stays — the
|
|
74
|
+
* clients filter it); a sub-agent's is not.
|
|
75
|
+
* - `finalize()` clears every streaming flag in every scope.
|
|
76
|
+
* - Artifacts upsert by `sandboxPath`/`contentHash`, write-backs by
|
|
77
|
+
* `workspaceEntryName`: the inline publisher and the write-back
|
|
78
|
+
* coordinator register theirs here, each through the one-method view it
|
|
79
|
+
* declares for itself (`ArtifactSink`, `WriteBackSink`), which this class
|
|
80
|
+
* satisfies by shape.
|
|
81
|
+
* - {@link dirty} is set by every discrete change above and by nothing a
|
|
82
|
+
* token delta does; the adapter's loop reads it and clears it with
|
|
83
|
+
* {@link markPersisted} as it requests the persist.
|
|
84
|
+
*
|
|
85
|
+
* What this builder deliberately does NOT write, and who does (the adapter
|
|
86
|
+
* contract's field ownership, `harness/types.ts` `TurnSink`): the phase,
|
|
87
|
+
* `startedAt`, `streamingUsage` and the epilogue's plan artifact are the
|
|
88
|
+
* turn runtime's (the plan artifact supersedes by its own name rule, which
|
|
89
|
+
* is the plan's knowledge, not the transcript's). A row left
|
|
90
|
+
* WAITING_APPROVAL is reported as the {@link awaitingApproval} fact and the
|
|
91
|
+
* caller decides what that means for the turn — a fact only
|
|
92
|
+
* `approval_proposed` produces, because a call that has
|
|
93
|
+
* STARTED is never parked at creation. Usage never passes
|
|
94
|
+
* through here: the native loop reads it off the wire (`usageOf`) and
|
|
95
|
+
* prices it into the sink, the Cursor loop reads its own from the SDK.
|
|
96
|
+
*/
|
|
97
|
+
import { create } from "@bufbuild/protobuf";
|
|
98
|
+
import { AgentMessageSchema, ToolCallSchema, } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
99
|
+
import { SubAgentExecutionSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/subagent_pb";
|
|
100
|
+
import { MessageType, SubAgentStatus, ToolCallStatus, ToolCallStreamingSource, ToolKind, } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
101
|
+
import { POLICY_ENGINE_VERSION, toProtoPolicySource } from "../../shared/approval-policy.js";
|
|
102
|
+
import { SALIENT_ARG_FIELDS, buildElidedArgsPreview } from "../../shared/args-preview.js";
|
|
103
|
+
import { classifyTool } from "../../shared/tool-kind.js";
|
|
104
|
+
import { applyTodoUpdate } from "../../shared/todos.js";
|
|
105
|
+
import { utcTimestamp } from "../../shared/status.js";
|
|
106
|
+
import { TranscriptState } from "./state.js";
|
|
107
|
+
export class TranscriptBuilder {
|
|
108
|
+
executionId;
|
|
109
|
+
state;
|
|
110
|
+
_dirty = false;
|
|
111
|
+
_awaitingApproval = false;
|
|
112
|
+
/**
|
|
113
|
+
* Builds INTO `status`, by reference: its `messages`, `subAgentExecutions`
|
|
114
|
+
* and every sub-agent row's `messages` are the arrays indexed and pushed
|
|
115
|
+
* into, never replaced, so a caller that wraps the same status (the
|
|
116
|
+
* runtime's chokepoint, a settle amending rows by identity) keeps seeing
|
|
117
|
+
* every row. Rows already on the status are indexed here; rows a
|
|
118
|
+
* reinvocation carries over arrive later through {@link seed}, indexed by
|
|
119
|
+
* the same routine, so a re-driven event reconciles onto them either way.
|
|
120
|
+
*
|
|
121
|
+
* The builder hands nothing back. A reader holds the status it built the
|
|
122
|
+
* builder over — `TurnSink.status` in production, the test's own in a
|
|
123
|
+
* test — so the transcript has one read handle and one write handle, and
|
|
124
|
+
* no second spelling of either.
|
|
125
|
+
*/
|
|
126
|
+
constructor(executionId, status) {
|
|
127
|
+
this.executionId = executionId;
|
|
128
|
+
this.state = new TranscriptState(status);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Append a reinvocation's persisted transcript — messages, sub-agent
|
|
132
|
+
* rows, artifacts, write-backs, todos — and index it, so the turn that
|
|
133
|
+
* follows APPENDS onto prior history and a re-issued call reconciles onto
|
|
134
|
+
* its seeded row instead of duplicating it. The runtime calls this once,
|
|
135
|
+
* in its reinvocation phase, before any harness runs; the reasons every
|
|
136
|
+
* collection is seeded are that phase's (`harness/turn-context.ts`
|
|
137
|
+
* `seedFromPersistedStatus`). Seeded rows are last turn's facts: this sets
|
|
138
|
+
* neither {@link dirty} nor {@link awaitingApproval}.
|
|
139
|
+
*/
|
|
140
|
+
seed(persisted) {
|
|
141
|
+
this.state.seed(persisted);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* True once a row this turn was left WAITING_APPROVAL: the engine has
|
|
145
|
+
* interrupted (or the harness's boundary parked the call) and the turn
|
|
146
|
+
* should end awaiting a decision. A fact about the transcript, not a phase
|
|
147
|
+
* — the caller owns the phase. No `tool_started` sets it (a started call
|
|
148
|
+
* is running); `approval_proposed` does.
|
|
149
|
+
*/
|
|
150
|
+
get awaitingApproval() {
|
|
151
|
+
return this._awaitingApproval;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* True once a discrete, user-visible change has landed since the last
|
|
155
|
+
* persist — a row started or settled, a sub-agent opened or closed, an
|
|
156
|
+
* artifact or write-back registered, a note appended; never a token
|
|
157
|
+
* delta, which rides the streaming cadence. The one fact the adapter's
|
|
158
|
+
* persist decision reads (`shared/persist-decision.ts`); the adapter
|
|
159
|
+
* clears it with {@link markPersisted} as it requests the write, so a
|
|
160
|
+
* change that lands after the write started dirties the next one.
|
|
161
|
+
*/
|
|
162
|
+
get dirty() {
|
|
163
|
+
return this._dirty;
|
|
164
|
+
}
|
|
165
|
+
markPersisted() {
|
|
166
|
+
this._dirty = false;
|
|
167
|
+
}
|
|
168
|
+
/** Fold one canonical event into the transcript. Never throws: a handler's error is logged and the stream goes on. */
|
|
169
|
+
apply(event) {
|
|
170
|
+
try {
|
|
171
|
+
switch (event.kind) {
|
|
172
|
+
case "sub_agent_started":
|
|
173
|
+
this.openSubAgent(event);
|
|
174
|
+
return;
|
|
175
|
+
case "sub_agent_finished":
|
|
176
|
+
this.closeSubAgent(event);
|
|
177
|
+
return;
|
|
178
|
+
case "sub_agent_failed":
|
|
179
|
+
this.failSubAgent(event);
|
|
180
|
+
return;
|
|
181
|
+
default:
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
const scope = this.state.scope(event.subAgentId);
|
|
185
|
+
if (!scope) {
|
|
186
|
+
throw new Error(`event for unknown sub-agent ${event.subAgentId}`);
|
|
187
|
+
}
|
|
188
|
+
switch (event.kind) {
|
|
189
|
+
case "message_start":
|
|
190
|
+
this.handleMessageStart(scope, event.runId);
|
|
191
|
+
break;
|
|
192
|
+
case "text_delta":
|
|
193
|
+
this.appendTextContent(scope, event.runId, event.text);
|
|
194
|
+
break;
|
|
195
|
+
case "reasoning_delta":
|
|
196
|
+
this.appendThinkingContent(scope, event.runId, event.text);
|
|
197
|
+
break;
|
|
198
|
+
case "message_finish":
|
|
199
|
+
this.handleMessageFinish(scope, event.runId);
|
|
200
|
+
break;
|
|
201
|
+
case "tool_started":
|
|
202
|
+
this.handleToolStarted(scope, event);
|
|
203
|
+
break;
|
|
204
|
+
case "tool_arg_delta":
|
|
205
|
+
this.handleToolArgDelta(scope, event.callId, event.argsChunk);
|
|
206
|
+
break;
|
|
207
|
+
case "tool_output_delta":
|
|
208
|
+
this.handleToolOutputDelta(scope, event.callId, event.delta);
|
|
209
|
+
break;
|
|
210
|
+
case "tool_finished":
|
|
211
|
+
this.handleToolFinished(scope, event.callId, event.result, event.observedAt);
|
|
212
|
+
break;
|
|
213
|
+
case "tool_error":
|
|
214
|
+
this.handleToolError(scope, event.callId, event.message, event.observedAt);
|
|
215
|
+
break;
|
|
216
|
+
case "approval_proposed":
|
|
217
|
+
this.handleApprovalProposed(scope, event);
|
|
218
|
+
break;
|
|
219
|
+
case "system_note":
|
|
220
|
+
this.appendSystemNote(scope, event.text);
|
|
221
|
+
break;
|
|
222
|
+
default: {
|
|
223
|
+
const exhaustive = event;
|
|
224
|
+
throw new Error(`unknown transcript event ${String(exhaustive.kind)}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
catch (err) {
|
|
229
|
+
console.error(`[TranscriptBuilder] Event handler error: execution=${this.executionId} kind=${event.kind}: ${err}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Every streaming flag off, in every scope — messages and rows. What a
|
|
234
|
+
* turn owes the transcript when its stream ends, however it ended: a
|
|
235
|
+
* stopped turn's in-flight message must not persist as still streaming.
|
|
236
|
+
* The sub-agent rows' own CANCELLED transition is the caller's, through
|
|
237
|
+
* the shared `cancelInProgressSubAgentProtos`.
|
|
238
|
+
*/
|
|
239
|
+
finalize() {
|
|
240
|
+
for (const transcript of this.state.transcripts())
|
|
241
|
+
finalizeStreaming(transcript);
|
|
242
|
+
}
|
|
243
|
+
// ── Artifact & WriteBack ───────────────────────────────────────────
|
|
244
|
+
addArtifact(artifact) {
|
|
245
|
+
const artifacts = this.state.proto.artifacts;
|
|
246
|
+
const idx = artifacts.findIndex(a => a.sandboxPath === artifact.sandboxPath);
|
|
247
|
+
if (idx >= 0) {
|
|
248
|
+
if (artifacts[idx].contentHash !== artifact.contentHash) {
|
|
249
|
+
artifacts[idx] = artifact;
|
|
250
|
+
this._dirty = true;
|
|
251
|
+
}
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
artifacts.push(artifact);
|
|
255
|
+
this._dirty = true;
|
|
256
|
+
}
|
|
257
|
+
addWriteBack(wb) {
|
|
258
|
+
const backs = this.state.proto.workspaceWriteBacks;
|
|
259
|
+
const idx = backs.findIndex(b => b.workspaceEntryName === wb.workspaceEntryName);
|
|
260
|
+
if (idx >= 0) {
|
|
261
|
+
backs[idx] = wb;
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
backs.push(wb);
|
|
265
|
+
}
|
|
266
|
+
this._dirty = true;
|
|
267
|
+
}
|
|
268
|
+
// ── Sub-Agent Rows ─────────────────────────────────────────────────
|
|
269
|
+
openSubAgent(event) {
|
|
270
|
+
// A seeded row re-announced by a replayed engine (the memory-checkpointer
|
|
271
|
+
// re-drives the whole graph): the row and its transcript already exist.
|
|
272
|
+
if (this.state.subAgent(event.subAgentId))
|
|
273
|
+
return;
|
|
274
|
+
const row = create(SubAgentExecutionSchema, {
|
|
275
|
+
id: event.subAgentId,
|
|
276
|
+
name: event.name,
|
|
277
|
+
subject: event.subject,
|
|
278
|
+
input: event.input,
|
|
279
|
+
status: SubAgentStatus.SUB_AGENT_IN_PROGRESS,
|
|
280
|
+
startedAt: utcTimestamp(),
|
|
281
|
+
});
|
|
282
|
+
this.state.openSubAgent(row);
|
|
283
|
+
this._dirty = true;
|
|
284
|
+
}
|
|
285
|
+
closeSubAgent(event) {
|
|
286
|
+
const scope = this.state.subAgent(event.subAgentId);
|
|
287
|
+
if (!scope)
|
|
288
|
+
return;
|
|
289
|
+
scope.row.status = SubAgentStatus.SUB_AGENT_COMPLETED;
|
|
290
|
+
scope.row.completedAt = utcTimestamp();
|
|
291
|
+
if (event.output !== undefined)
|
|
292
|
+
scope.row.output = event.output;
|
|
293
|
+
finalizeStreaming(scope.transcript);
|
|
294
|
+
this._dirty = true;
|
|
295
|
+
}
|
|
296
|
+
failSubAgent(event) {
|
|
297
|
+
const scope = this.state.subAgent(event.subAgentId);
|
|
298
|
+
if (!scope)
|
|
299
|
+
return;
|
|
300
|
+
scope.row.status = SubAgentStatus.SUB_AGENT_FAILED;
|
|
301
|
+
scope.row.completedAt = utcTimestamp();
|
|
302
|
+
scope.row.error = event.error;
|
|
303
|
+
finalizeStreaming(scope.transcript);
|
|
304
|
+
this._dirty = true;
|
|
305
|
+
}
|
|
306
|
+
// ── Message Handlers ───────────────────────────────────────────────
|
|
307
|
+
handleMessageStart(scope, runId) {
|
|
308
|
+
// Record the run for the message boundary but do NOT create the AI
|
|
309
|
+
// message yet: it is created lazily on the first text_delta, so a
|
|
310
|
+
// THINKING row that streams first sits before the text it precedes.
|
|
311
|
+
if (scope.lastRunId && scope.lastRunId !== runId && scope.currentAiMessage) {
|
|
312
|
+
scope.currentAiMessage.isStreaming = false;
|
|
313
|
+
}
|
|
314
|
+
scope.lastRunId = runId;
|
|
315
|
+
}
|
|
316
|
+
handleMessageFinish(scope, runId) {
|
|
317
|
+
// A finished run's text AND its thinking are finished (Cursor closes
|
|
318
|
+
// both when a tool call ends the segment). Before #1097 only the
|
|
319
|
+
// text closed here and the THINKING row spun until `finalize()` — a live
|
|
320
|
+
// spinner on a block the model had left, on both harnesses.
|
|
321
|
+
const msg = scope.messagesByRun.get(runId);
|
|
322
|
+
if (msg) {
|
|
323
|
+
msg.isStreaming = false;
|
|
324
|
+
}
|
|
325
|
+
const thinking = scope.messagesByRun.get(thinkingKeyOf(runId));
|
|
326
|
+
if (thinking) {
|
|
327
|
+
thinking.isStreaming = false;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
appendTextContent(scope, runId, text) {
|
|
331
|
+
const msg = this.ensureAiMessage(scope, runId);
|
|
332
|
+
msg.content += text;
|
|
333
|
+
msg.isStreaming = true;
|
|
334
|
+
}
|
|
335
|
+
appendThinkingContent(scope, runId, text) {
|
|
336
|
+
// One THINKING row per run, keyed apart from the run's text so
|
|
337
|
+
// the two never share a message.
|
|
338
|
+
const thinkingKey = thinkingKeyOf(runId);
|
|
339
|
+
const existingMsg = scope.messagesByRun.get(thinkingKey);
|
|
340
|
+
if (existingMsg && existingMsg.type === MessageType.MESSAGE_THINKING) {
|
|
341
|
+
existingMsg.content += text;
|
|
342
|
+
existingMsg.isStreaming = true;
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
const msg = create(AgentMessageSchema, {
|
|
346
|
+
type: MessageType.MESSAGE_THINKING,
|
|
347
|
+
content: text,
|
|
348
|
+
timestamp: utcTimestamp(),
|
|
349
|
+
isStreaming: true,
|
|
350
|
+
});
|
|
351
|
+
scope.messages.push(msg);
|
|
352
|
+
scope.messagesByRun.set(thinkingKey, msg);
|
|
353
|
+
}
|
|
354
|
+
// ── Tool Handlers ──────────────────────────────────────────────────
|
|
355
|
+
handleToolStarted(scope, event) {
|
|
356
|
+
const { callId, name, input } = event;
|
|
357
|
+
// A re-emitted start reconciles onto the existing row — never a duplicate,
|
|
358
|
+
// never a second gate. Keyed by the provider's tool_call_id, so this is an
|
|
359
|
+
// exact match (no name heuristics). Two shapes reach here: the durable
|
|
360
|
+
// checkpoint re-emitting a seeded WAITING call now that approval is granted
|
|
361
|
+
// (native's resume; the runtime's `seedFromPersistedStatus`), and a
|
|
362
|
+
// recovery replaying a call the server had marked INTERRUPTED when the
|
|
363
|
+
// execution terminalized with it in flight (the enum's supersede rule; the
|
|
364
|
+
// Cursor accumulator's merge). Either way an UNSETTLED row becomes RUNNING
|
|
365
|
+
// (before #1097 only WAITING flipped); a settled one keeps
|
|
366
|
+
// its outcome. Args it lacked are filled and previewed, once.
|
|
367
|
+
const existing = scope.toolCalls.get(callId);
|
|
368
|
+
if (existing) {
|
|
369
|
+
if (!isSettled(existing.status)) {
|
|
370
|
+
existing.status = ToolCallStatus.TOOL_CALL_RUNNING;
|
|
371
|
+
}
|
|
372
|
+
if (Object.keys(input).length > 0) {
|
|
373
|
+
if (!existing.args)
|
|
374
|
+
existing.args = input;
|
|
375
|
+
if (!existing.argsPreview)
|
|
376
|
+
stampArgsPreview(existing, input);
|
|
377
|
+
}
|
|
378
|
+
this._dirty = true;
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
// The message boundary: the row joins the message whose text
|
|
382
|
+
// proposed it — the scope's current AI message — and gets an empty one
|
|
383
|
+
// only when the scope has no message yet.
|
|
384
|
+
const parentMsg = scope.currentAiMessage ?? this.ensureAiMessageForToolCall(scope);
|
|
385
|
+
// A started call is RUNNING, gated or not: a gated call that has started
|
|
386
|
+
// runs until the harness's boundary parks it through `approval_proposed`
|
|
387
|
+
// (Cursor), and on native a held call never starts at all — the engine
|
|
388
|
+
// interrupts before the tool runs and the post-stream seed proposes it.
|
|
389
|
+
// The builder writes the gate's word it is told and decides nothing
|
|
390
|
+
// about approval.
|
|
391
|
+
const tc = create(ToolCallSchema, {
|
|
392
|
+
id: callId,
|
|
393
|
+
name,
|
|
394
|
+
status: ToolCallStatus.TOOL_CALL_RUNNING,
|
|
395
|
+
startedAt: utcTimestamp(),
|
|
396
|
+
});
|
|
397
|
+
if (Object.keys(input).length > 0) {
|
|
398
|
+
tc.args = input;
|
|
399
|
+
stampArgsPreview(tc, input);
|
|
400
|
+
}
|
|
401
|
+
if (event.mcpServerSlug) {
|
|
402
|
+
tc.mcpServerSlug = event.mcpServerSlug;
|
|
403
|
+
}
|
|
404
|
+
// Classify after mcpServerSlug is set so MCP tools resolve correctly.
|
|
405
|
+
tc.toolKind = classifyTool(tc.name, tc.mcpServerSlug);
|
|
406
|
+
// Authorization provenance — which policy layer governs the call — stamped
|
|
407
|
+
// in the same spot as tool_kind, for every observed call the translator
|
|
408
|
+
// could attribute (gated or auto-approved), so the persisted record is
|
|
409
|
+
// auditable and the UI can explain the gate. Absent provenance leaves the
|
|
410
|
+
// field at UNSPECIFIED, like an unclassified tool_kind. A gated call is
|
|
411
|
+
// re-seeded on reinvocation (the runtime's wide seed) with the same source
|
|
412
|
+
// carried through the interrupt.
|
|
413
|
+
if (event.provenance !== undefined) {
|
|
414
|
+
tc.approvalPolicySource = toProtoPolicySource(event.provenance);
|
|
415
|
+
tc.policyEngineVersion = POLICY_ENGINE_VERSION;
|
|
416
|
+
}
|
|
417
|
+
// The gate's word: the row says it is gated and what the card will ask;
|
|
418
|
+
// `approvalRequestedAt` is stamped when the boundary actually parks it
|
|
419
|
+
// (`approval_proposed`, or a gated row's `tool_error`), never here.
|
|
420
|
+
if (event.gate) {
|
|
421
|
+
tc.requiresApproval = true;
|
|
422
|
+
tc.approvalMessage = event.gate.message;
|
|
423
|
+
}
|
|
424
|
+
parentMsg.toolCalls.push(tc);
|
|
425
|
+
scope.toolCalls.set(callId, tc);
|
|
426
|
+
this._dirty = true;
|
|
427
|
+
}
|
|
428
|
+
handleToolFinished(scope, callId, result, observedAt) {
|
|
429
|
+
const tc = scope.toolCalls.get(callId);
|
|
430
|
+
if (!tc)
|
|
431
|
+
return;
|
|
432
|
+
// An UPSERT, not an overwrite (Cursor's `mergeToolCallEvent`):
|
|
433
|
+
// the status advances monotonically — a settled row never regresses;
|
|
434
|
+
// `completedAt` is stamped once; only a non-empty result overwrites, so a
|
|
435
|
+
// completion that carries none never wipes the output an earlier one
|
|
436
|
+
// did. Two completions per call is the Cursor shape after stigmer#1053
|
|
437
|
+
// (the SDK's timing delta precedes the stream's completion); on native
|
|
438
|
+
// there is one, and the rules read as the plain write they replace.
|
|
439
|
+
//
|
|
440
|
+
// Store the faithful result; bounding the gRPC payload is owned solely by
|
|
441
|
+
// the persist chokepoint (offload + enforce in status.ts/status-offload.ts).
|
|
442
|
+
// Truncating here would corrupt binary content (e.g. a screenshot's base64)
|
|
443
|
+
// before offload can lift it into a renderable ToolCallOutputRef.
|
|
444
|
+
//
|
|
445
|
+
// A completion that changes nothing — a settled row re-completed with no
|
|
446
|
+
// new result — forces no persist (Cursor's "a redundant
|
|
447
|
+
// terminal re-emit is noise, not a state change"). On Cursor every call
|
|
448
|
+
// completes twice (the delta, then the stream), and the second carries the
|
|
449
|
+
// result, so the flush the UI needs still happens exactly once.
|
|
450
|
+
const wasSettled = isSettled(tc.status);
|
|
451
|
+
const resultChanged = !!result && result !== tc.result;
|
|
452
|
+
if (!wasSettled)
|
|
453
|
+
tc.status = ToolCallStatus.TOOL_CALL_COMPLETED;
|
|
454
|
+
// The instant the harness OBSERVED the completion when it delivers it
|
|
455
|
+
// later (Cursor's queued delta); otherwise now.
|
|
456
|
+
if (!tc.completedAt)
|
|
457
|
+
tc.completedAt = observedAt ?? utcTimestamp();
|
|
458
|
+
if (resultChanged)
|
|
459
|
+
tc.result = result;
|
|
460
|
+
stopStreaming(tc);
|
|
461
|
+
scope.argBuffers.delete(callId);
|
|
462
|
+
if (wasSettled && !resultChanged)
|
|
463
|
+
return;
|
|
464
|
+
// Project a completed to-do write into status.todos. deepagents' write_todos
|
|
465
|
+
// runs its state-mutating Command when the tool node COMPLETES, so we mirror
|
|
466
|
+
// it here (not at tool-start) — a call cancelled before finishing correctly
|
|
467
|
+
// projects nothing. Keyed on the harness-agnostic ToolKind.TODO (stamped at
|
|
468
|
+
// tool-start) and fed by the same shared mapper every harness uses; the
|
|
469
|
+
// call's own `merge` flag decides replace-or-merge (Cursor's `updateTodos`
|
|
470
|
+
// sets it; deepagents never does, so native stays a full replace);
|
|
471
|
+
// a sub-agent's list is its own, never the execution's. The tool
|
|
472
|
+
// call itself stays in messages (the client filters ToolKind.TODO from the
|
|
473
|
+
// thread).
|
|
474
|
+
if (scope === this.state.root && tc.toolKind === ToolKind.TODO) {
|
|
475
|
+
applyTodoUpdate(this.state.proto.todos, tc.args?.todos, { merge: tc.args?.merge === true });
|
|
476
|
+
}
|
|
477
|
+
this._dirty = true;
|
|
478
|
+
}
|
|
479
|
+
handleToolError(scope, callId, message, observedAt) {
|
|
480
|
+
const tc = scope.toolCalls.get(callId);
|
|
481
|
+
if (!tc)
|
|
482
|
+
return;
|
|
483
|
+
// The same upsert as a finish. A gated row that fails is the
|
|
484
|
+
// Cursor boundary's shape — the hook denied the call and the stream
|
|
485
|
+
// reports `error`; the boundary parks it after the stream — so the
|
|
486
|
+
// moment approval became due is stamped here if nothing stamped it yet.
|
|
487
|
+
const wasSettled = isSettled(tc.status);
|
|
488
|
+
const messageChanged = !!message && message !== tc.error;
|
|
489
|
+
const at = observedAt ?? utcTimestamp();
|
|
490
|
+
if (!wasSettled)
|
|
491
|
+
tc.status = ToolCallStatus.TOOL_CALL_FAILED;
|
|
492
|
+
if (!tc.completedAt)
|
|
493
|
+
tc.completedAt = at;
|
|
494
|
+
if (messageChanged)
|
|
495
|
+
tc.error = message;
|
|
496
|
+
if (tc.requiresApproval && !tc.approvalRequestedAt)
|
|
497
|
+
tc.approvalRequestedAt = at;
|
|
498
|
+
stopStreaming(tc);
|
|
499
|
+
scope.argBuffers.delete(callId);
|
|
500
|
+
// The same no-change rule as a finish.
|
|
501
|
+
if (wasSettled && !messageChanged)
|
|
502
|
+
return;
|
|
503
|
+
this._dirty = true;
|
|
504
|
+
}
|
|
505
|
+
handleToolArgDelta(scope, callId, argsChunk) {
|
|
506
|
+
const tc = scope.toolCalls.get(callId);
|
|
507
|
+
if (!tc)
|
|
508
|
+
return;
|
|
509
|
+
const buffer = (scope.argBuffers.get(callId) ?? "") + argsChunk;
|
|
510
|
+
scope.argBuffers.set(callId, buffer);
|
|
511
|
+
try {
|
|
512
|
+
tc.args = JSON.parse(buffer);
|
|
513
|
+
}
|
|
514
|
+
catch {
|
|
515
|
+
// Partial JSON — will parse on next chunk or tool-finished
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
handleToolOutputDelta(scope, callId, delta) {
|
|
519
|
+
const tc = scope.toolCalls.get(callId);
|
|
520
|
+
if (!tc)
|
|
521
|
+
return;
|
|
522
|
+
// A chunk for a row that has already settled is stale: the completion's
|
|
523
|
+
// result IS the whole output, and appending would double it (Cursor's
|
|
524
|
+
// delta channel and stream can deliver the output's
|
|
525
|
+
// chunks and the completion in one loop window, completion first).
|
|
526
|
+
if (isSettled(tc.status))
|
|
527
|
+
return;
|
|
528
|
+
// Output arriving live: the row streams its OUTPUT (Cursor's
|
|
529
|
+
// enricher), so the client can show the partial result as it grows;
|
|
530
|
+
// the finish, or `finalize()`, closes it.
|
|
531
|
+
tc.result = (tc.result ?? "") + delta;
|
|
532
|
+
tc.isStreaming = true;
|
|
533
|
+
tc.streamingSource = ToolCallStreamingSource.OUTPUT;
|
|
534
|
+
}
|
|
535
|
+
// ── Post-Stream Facts ──────────────────────────────────────────────
|
|
536
|
+
handleApprovalProposed(scope, event) {
|
|
537
|
+
const now = utcTimestamp();
|
|
538
|
+
const existing = scope.toolCalls.get(event.callId);
|
|
539
|
+
if (existing) {
|
|
540
|
+
// Reopen (Cursor's `markWaitingApproval`): the boundary is re-proposing
|
|
541
|
+
// a call the stream already showed — an errored built-in the hook
|
|
542
|
+
// denied — so the outcome the stream wrote is not the outcome.
|
|
543
|
+
existing.status = ToolCallStatus.TOOL_CALL_WAITING_APPROVAL;
|
|
544
|
+
existing.requiresApproval = true;
|
|
545
|
+
existing.approvalMessage = event.message;
|
|
546
|
+
if (!existing.approvalRequestedAt)
|
|
547
|
+
existing.approvalRequestedAt = now;
|
|
548
|
+
existing.completedAt = "";
|
|
549
|
+
existing.error = "";
|
|
550
|
+
existing.result = "";
|
|
551
|
+
// The proposal's args are the authoritative proposed change when the
|
|
552
|
+
// harness carries them (Cursor's `applyGateInput`: the hook's captured
|
|
553
|
+
// input outranks what the stream carried before the first-denial cancel),
|
|
554
|
+
// and they re-derive the preview. Absent, the row
|
|
555
|
+
// keeps what it had.
|
|
556
|
+
if (event.args && Object.keys(event.args).length > 0) {
|
|
557
|
+
existing.args = event.args;
|
|
558
|
+
stampArgsPreview(existing, event.args);
|
|
559
|
+
}
|
|
560
|
+
if (event.contentDigest)
|
|
561
|
+
existing.approvalContentDigest = event.contentDigest;
|
|
562
|
+
stopStreaming(existing);
|
|
563
|
+
this._awaitingApproval = true;
|
|
564
|
+
this._dirty = true;
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
// A call the stream never showed (native: the engine interrupted before
|
|
568
|
+
// the tool started). Its row lands on the text that proposed it, like
|
|
569
|
+
// any other row (before #1097 the seed pushed a new empty message).
|
|
570
|
+
const parentMsg = scope.currentAiMessage ?? this.ensureAiMessageForToolCall(scope);
|
|
571
|
+
const tc = create(ToolCallSchema, {
|
|
572
|
+
id: event.callId,
|
|
573
|
+
name: event.name,
|
|
574
|
+
status: ToolCallStatus.TOOL_CALL_WAITING_APPROVAL,
|
|
575
|
+
requiresApproval: true,
|
|
576
|
+
approvalMessage: event.message,
|
|
577
|
+
approvalRequestedAt: now,
|
|
578
|
+
startedAt: now,
|
|
579
|
+
});
|
|
580
|
+
if (event.mcpServerSlug)
|
|
581
|
+
tc.mcpServerSlug = event.mcpServerSlug;
|
|
582
|
+
tc.toolKind = classifyTool(tc.name, tc.mcpServerSlug);
|
|
583
|
+
if (event.provenance !== undefined) {
|
|
584
|
+
tc.approvalPolicySource = toProtoPolicySource(event.provenance);
|
|
585
|
+
tc.policyEngineVersion = POLICY_ENGINE_VERSION;
|
|
586
|
+
}
|
|
587
|
+
if (event.args && Object.keys(event.args).length > 0) {
|
|
588
|
+
tc.args = event.args;
|
|
589
|
+
stampArgsPreview(tc, event.args);
|
|
590
|
+
}
|
|
591
|
+
if (event.contentDigest)
|
|
592
|
+
tc.approvalContentDigest = event.contentDigest;
|
|
593
|
+
parentMsg.toolCalls.push(tc);
|
|
594
|
+
scope.toolCalls.set(event.callId, tc);
|
|
595
|
+
this._awaitingApproval = true;
|
|
596
|
+
this._dirty = true;
|
|
597
|
+
}
|
|
598
|
+
appendSystemNote(scope, text) {
|
|
599
|
+
scope.messages.push(create(AgentMessageSchema, {
|
|
600
|
+
type: MessageType.MESSAGE_SYSTEM,
|
|
601
|
+
content: text,
|
|
602
|
+
timestamp: utcTimestamp(),
|
|
603
|
+
}));
|
|
604
|
+
this._dirty = true;
|
|
605
|
+
}
|
|
606
|
+
// ── Content Helpers ───────────────────────────────────────────────
|
|
607
|
+
/** The AI message a run's text streams into, created on its first token; a new run closes the previous message's flag. */
|
|
608
|
+
ensureAiMessage(scope, runId) {
|
|
609
|
+
const existingByRun = scope.messagesByRun.get(runId);
|
|
610
|
+
if (existingByRun)
|
|
611
|
+
return existingByRun;
|
|
612
|
+
if (scope.lastRunId && scope.lastRunId !== runId && scope.currentAiMessage) {
|
|
613
|
+
scope.currentAiMessage.isStreaming = false;
|
|
614
|
+
}
|
|
615
|
+
const msg = create(AgentMessageSchema, {
|
|
616
|
+
type: MessageType.MESSAGE_AI,
|
|
617
|
+
content: "",
|
|
618
|
+
timestamp: utcTimestamp(),
|
|
619
|
+
isStreaming: true,
|
|
620
|
+
});
|
|
621
|
+
scope.messages.push(msg);
|
|
622
|
+
scope.messagesByRun.set(runId, msg);
|
|
623
|
+
scope.currentAiMessage = msg;
|
|
624
|
+
scope.lastRunId = runId;
|
|
625
|
+
return msg;
|
|
626
|
+
}
|
|
627
|
+
/** The message a tool row lands on when the scope has no AI message yet: the latest run's, else a new empty one. */
|
|
628
|
+
ensureAiMessageForToolCall(scope) {
|
|
629
|
+
if (scope.lastRunId) {
|
|
630
|
+
const existing = scope.messagesByRun.get(scope.lastRunId);
|
|
631
|
+
if (existing) {
|
|
632
|
+
scope.currentAiMessage = existing;
|
|
633
|
+
return existing;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
const msg = create(AgentMessageSchema, {
|
|
637
|
+
type: MessageType.MESSAGE_AI,
|
|
638
|
+
content: "",
|
|
639
|
+
timestamp: utcTimestamp(),
|
|
640
|
+
isStreaming: false,
|
|
641
|
+
});
|
|
642
|
+
scope.messages.push(msg);
|
|
643
|
+
scope.currentAiMessage = msg;
|
|
644
|
+
return msg;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
/** The `messagesByRun` key of a run's THINKING row — apart from the run's text, so the two never share a message. */
|
|
648
|
+
function thinkingKeyOf(runId) {
|
|
649
|
+
return `thinking:${runId}`;
|
|
650
|
+
}
|
|
651
|
+
/** Every streaming flag off in one transcript — messages and rows. */
|
|
652
|
+
function finalizeStreaming(transcript) {
|
|
653
|
+
for (const message of transcript.messages) {
|
|
654
|
+
message.isStreaming = false;
|
|
655
|
+
for (const tc of message.toolCalls)
|
|
656
|
+
stopStreaming(tc);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* The row's `args_preview`, on EVERY row with args (since #1097):
|
|
661
|
+
* `message.proto` promises it sanitized, redacted, at creation, for inline
|
|
662
|
+
* visibility, and the one builder that keeps it small and always valid JSON
|
|
663
|
+
* is `buildElidedArgsPreview` over the platform's salient fields. Until then
|
|
664
|
+
* native stamped a whole-string-truncating preview on gated rows only and
|
|
665
|
+
* Cursor stamped `JSON.stringify(args)` unredacted. An empty preview (a
|
|
666
|
+
* cycle in the args) leaves the field unset rather than failing the row.
|
|
667
|
+
*/
|
|
668
|
+
function stampArgsPreview(tc, args) {
|
|
669
|
+
const preview = buildElidedArgsPreview(args, SALIENT_ARG_FIELDS);
|
|
670
|
+
if (preview)
|
|
671
|
+
tc.argsPreview = preview;
|
|
672
|
+
}
|
|
673
|
+
/** A row no longer streaming: the flag off and the source cleared, so a stale OUTPUT marker never outlives the stream. */
|
|
674
|
+
function stopStreaming(tc) {
|
|
675
|
+
if (!tc.isStreaming)
|
|
676
|
+
return;
|
|
677
|
+
tc.isStreaming = false;
|
|
678
|
+
tc.streamingSource = ToolCallStreamingSource.UNSPECIFIED;
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* A row that has finished, however it finished — the statuses a later event
|
|
682
|
+
* must not move backwards (Cursor's `isTerminalToolStatus`). INTERRUPTED is
|
|
683
|
+
* deliberately NOT here: it is server-authored when an execution
|
|
684
|
+
* terminalized with the call in flight, and a recovery-replayed event must
|
|
685
|
+
* be able to advance the row to its true outcome (the enum's recovery
|
|
686
|
+
* supersede rule; `shared/tool-row.ts` explains the two sets).
|
|
687
|
+
*/
|
|
688
|
+
function isSettled(status) {
|
|
689
|
+
return (status === ToolCallStatus.TOOL_CALL_COMPLETED ||
|
|
690
|
+
status === ToolCallStatus.TOOL_CALL_FAILED ||
|
|
691
|
+
status === ToolCallStatus.TOOL_CALL_SKIPPED);
|
|
692
|
+
}
|
|
693
|
+
//# sourceMappingURL=builder.js.map
|