@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
|
@@ -1,13 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The native translator's integration arms: raw LangGraph v3 protocol events
|
|
3
|
+
* through `DeepAgentTranslator` and then `TranscriptBuilder`, end to end —
|
|
4
|
+
* the exact path `turn-stream.ts`'s loop takes — asserting the transcript
|
|
5
|
+
* that comes out. Every sequence here is the wire's real shape (the fixtures
|
|
6
|
+
* default to LangGraph's namespaces), so these arms prove that the
|
|
7
|
+
* translator's reading of the engine and the builder's rules compose into
|
|
8
|
+
* the goldens' shapes.
|
|
9
|
+
*
|
|
10
|
+
* What is NOT here: the builder's own rules, driven with
|
|
11
|
+
* `TranscriptEvent`s directly, live in
|
|
12
|
+
* `harness/transcript/__tests__/builder.test.ts`; the translator's own
|
|
13
|
+
* reading of the wire in `translator.test.ts`. This file began as the
|
|
14
|
+
* builder's unit suite when the builder was the native adapter's
|
|
15
|
+
* (`v3-status-builder.test.ts`, #1096) and became what it always was in
|
|
16
|
+
* truth in #1097.
|
|
17
|
+
*
|
|
18
|
+
* In the adapter's folder because the fixtures and the translator are
|
|
19
|
+
* `activities/` modules the direction fence keeps out of `harness/`.
|
|
20
|
+
*/
|
|
21
|
+
|
|
1
22
|
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
23
|
import { create } from "@bufbuild/protobuf";
|
|
3
|
-
import { AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
4
|
-
import { ApprovalAction, ExecutionPhase, MessageType, ToolCallStatus, ToolKind, TodoStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
24
|
+
import { type AgentExecutionStatus, AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
25
|
+
import { ApprovalAction, ApprovalPolicySource, ExecutionPhase, MessageType, ToolCallStatus, ToolKind, TodoStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
5
26
|
import { AgentMessageSchema, ToolCallSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/message_pb";
|
|
6
27
|
import { ExecutionArtifactSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/artifact_pb";
|
|
7
28
|
import { WorkspaceWriteBackSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/writeback_pb";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import type {
|
|
29
|
+
import { TranscriptBuilder } from "../../../harness/transcript/builder.js";
|
|
30
|
+
import { DeepAgentTranslator } from "../translator.js";
|
|
31
|
+
import type { DeepAgentGateState } from "../turn-setup.js";
|
|
11
32
|
import type { MergedToolPolicy } from "../../../shared/approval-policy.js";
|
|
12
33
|
import {
|
|
13
34
|
resetSeq,
|
|
@@ -20,69 +41,52 @@ import {
|
|
|
20
41
|
makeToolError,
|
|
21
42
|
makeToolCallArgDelta,
|
|
22
43
|
makeToolOutputDelta,
|
|
23
|
-
makeUsageEvent,
|
|
24
44
|
} from "../__test-utils__/v3-event-fixtures.js";
|
|
25
45
|
import type { V3ProtocolEvent } from "../v3-event-recorder.js";
|
|
26
46
|
|
|
27
47
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
48
|
+
* A builder over an empty status. Usage never reaches the builder:
|
|
49
|
+
* the loop reads it off the wire through `usageOf` and prices it into the
|
|
50
|
+
* sink — `turn-stream.test.ts` pins that; `translator.test.ts` pins what
|
|
51
|
+
* `usageOf` reads. The `usage:` fields the sequences below still
|
|
52
|
+
* carry are the wire's real shape and are ignored by everything here.
|
|
31
53
|
*/
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
cacheWriteTokens: number;
|
|
37
|
-
turnCount: number;
|
|
54
|
+
/** A builder and the status it builds into: the test writes through `sb` and reads `status`, as production reads `TurnSink.status`. */
|
|
55
|
+
function makeBuilder(): { sb: TranscriptBuilder; status: AgentExecutionStatus } {
|
|
56
|
+
const status = create(AgentExecutionStatusSchema, {});
|
|
57
|
+
return { sb: new TranscriptBuilder("exec-test", status), status };
|
|
38
58
|
}
|
|
39
59
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
reported.inputTokens += usage.input_tokens ?? 0;
|
|
47
|
-
reported.outputTokens += usage.output_tokens ?? 0;
|
|
48
|
-
reported.cacheReadTokens += usage.input_token_details?.cache_read ?? 0;
|
|
49
|
-
reported.cacheWriteTokens += usage.input_token_details?.cache_creation ?? 0;
|
|
50
|
-
reported.turnCount += 1;
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
reportedByBuilder.set(sb, reported);
|
|
54
|
-
return sb;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function reportedUsage(sb: V3StatusBuilder): ReportedUsage {
|
|
58
|
-
return reportedByBuilder.get(sb)!;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function feedAll(sb: V3StatusBuilder, events: V3ProtocolEvent[]): void {
|
|
60
|
+
/**
|
|
61
|
+
* Feed raw v3 events through the translator (over the given gate posture, or
|
|
62
|
+
* none) into the builder — the production path, `turn-stream.ts`'s loop.
|
|
63
|
+
*/
|
|
64
|
+
function feedAll(sb: TranscriptBuilder, events: V3ProtocolEvent[], gate: DeepAgentGateState | null = null): void {
|
|
65
|
+
const translator = new DeepAgentTranslator(gate);
|
|
62
66
|
for (const raw of events) {
|
|
63
|
-
for (const e of
|
|
64
|
-
sb.
|
|
67
|
+
for (const e of translator.translate(raw)) {
|
|
68
|
+
sb.apply(e);
|
|
65
69
|
}
|
|
66
70
|
}
|
|
67
71
|
}
|
|
68
72
|
|
|
69
73
|
beforeEach(() => resetSeq());
|
|
70
74
|
|
|
71
|
-
describe("
|
|
75
|
+
describe("the native translator through the builder", () => {
|
|
72
76
|
|
|
73
77
|
// ── Initialization ───────────────────────────────────────────────
|
|
74
78
|
|
|
75
79
|
describe("initialization", () => {
|
|
76
|
-
it("writes neither phase nor startedAt (the turn runtime owns both
|
|
77
|
-
const sb = makeBuilder();
|
|
78
|
-
expect(
|
|
79
|
-
expect(
|
|
80
|
+
it("writes neither phase nor startedAt (the turn runtime owns both)", () => {
|
|
81
|
+
const { sb, status } = makeBuilder();
|
|
82
|
+
expect(status.phase).toBe(ExecutionPhase.EXECUTION_PHASE_UNSPECIFIED);
|
|
83
|
+
expect(status.startedAt).toBe("");
|
|
80
84
|
expect(sb.awaitingApproval).toBe(false);
|
|
81
85
|
});
|
|
82
86
|
|
|
83
|
-
it("starts with
|
|
84
|
-
const sb = makeBuilder();
|
|
85
|
-
expect(sb.
|
|
87
|
+
it("starts with dirty = false", () => {
|
|
88
|
+
const { sb } = makeBuilder();
|
|
89
|
+
expect(sb.dirty).toBe(false);
|
|
86
90
|
});
|
|
87
91
|
});
|
|
88
92
|
|
|
@@ -90,14 +94,14 @@ describe("V3StatusBuilder", () => {
|
|
|
90
94
|
// GOLDEN SEQUENCES
|
|
91
95
|
//
|
|
92
96
|
// Same proto assertions as v2 StatusBuilder golden tests, but input
|
|
93
|
-
// is v3 ProtocolEvent sequences fed through the
|
|
97
|
+
// is v3 ProtocolEvent sequences fed through the translator.
|
|
94
98
|
// ═══════════════════════════════════════════════════════════════════
|
|
95
99
|
|
|
96
100
|
describe("golden sequences", () => {
|
|
97
101
|
|
|
98
102
|
describe("plain chat — 2-turn text-only conversation", () => {
|
|
99
|
-
it("produces 2 AI messages
|
|
100
|
-
const sb = makeBuilder();
|
|
103
|
+
it("produces 2 AI messages, one per run, each closed by its finish", () => {
|
|
104
|
+
const { sb, status } = makeBuilder();
|
|
101
105
|
feedAll(sb, [
|
|
102
106
|
makeMessageStart("run-1"),
|
|
103
107
|
makeTextDelta("run-1", "Hello, "),
|
|
@@ -114,7 +118,6 @@ describe("V3StatusBuilder", () => {
|
|
|
114
118
|
}),
|
|
115
119
|
]);
|
|
116
120
|
|
|
117
|
-
const status = sb.currentStatus;
|
|
118
121
|
|
|
119
122
|
expect(status.messages).toHaveLength(2);
|
|
120
123
|
expect(status.messages[0].type).toBe(MessageType.MESSAGE_AI);
|
|
@@ -126,17 +129,13 @@ describe("V3StatusBuilder", () => {
|
|
|
126
129
|
expect(status.messages[1].content).toBe("Here is more detail.");
|
|
127
130
|
expect(status.messages[1].isStreaming).toBe(false);
|
|
128
131
|
|
|
129
|
-
|
|
130
|
-
expect(usage.inputTokens).toBe(130);
|
|
131
|
-
expect(usage.outputTokens).toBe(20);
|
|
132
|
-
expect(usage.turnCount).toBe(2);
|
|
133
|
-
expect(status.streamingUsage, "the runtime owns streamingUsage; the builder reports").toBeUndefined();
|
|
132
|
+
expect(status.streamingUsage, "the runtime owns streamingUsage; the builder never touches it").toBeUndefined();
|
|
134
133
|
});
|
|
135
134
|
});
|
|
136
135
|
|
|
137
136
|
describe("Anthropic thinking + text", () => {
|
|
138
137
|
it("produces THINKING then AI messages with correct turn boundaries", () => {
|
|
139
|
-
const sb = makeBuilder();
|
|
138
|
+
const { sb, status } = makeBuilder();
|
|
140
139
|
feedAll(sb, [
|
|
141
140
|
makeMessageStart("run-1"),
|
|
142
141
|
makeReasoningDelta("run-1", "Let me analyze "),
|
|
@@ -157,13 +156,14 @@ describe("V3StatusBuilder", () => {
|
|
|
157
156
|
}),
|
|
158
157
|
]);
|
|
159
158
|
|
|
160
|
-
const status = sb.currentStatus;
|
|
161
159
|
|
|
162
160
|
expect(status.messages).toHaveLength(3);
|
|
163
161
|
|
|
164
162
|
expect(status.messages[0].type).toBe(MessageType.MESSAGE_THINKING);
|
|
165
163
|
expect(status.messages[0].content).toBe("Let me analyze this problem carefully.");
|
|
166
|
-
|
|
164
|
+
// The run's finish closes its thinking with its text (since #1097);
|
|
165
|
+
// until then the THINKING row spun until finalize.
|
|
166
|
+
expect(status.messages[0].isStreaming).toBe(false);
|
|
167
167
|
|
|
168
168
|
expect(status.messages[1].type).toBe(MessageType.MESSAGE_AI);
|
|
169
169
|
expect(status.messages[1].content).toBe("Based on my analysis, here is the answer.");
|
|
@@ -172,18 +172,12 @@ describe("V3StatusBuilder", () => {
|
|
|
172
172
|
expect(status.messages[2].type).toBe(MessageType.MESSAGE_AI);
|
|
173
173
|
expect(status.messages[2].content).toBe("Let me elaborate.");
|
|
174
174
|
expect(status.messages[2].isStreaming).toBe(false);
|
|
175
|
-
|
|
176
|
-
const usage = reportedUsage(sb);
|
|
177
|
-
expect(usage.inputTokens).toBe(500);
|
|
178
|
-
expect(usage.outputTokens).toBe(120);
|
|
179
|
-
expect(usage.cacheReadTokens).toBe(50);
|
|
180
|
-
expect(usage.turnCount).toBe(2);
|
|
181
175
|
});
|
|
182
176
|
});
|
|
183
177
|
|
|
184
178
|
describe("single tool call — ReAct pattern", () => {
|
|
185
179
|
it("produces AI message with tool call followed by response", () => {
|
|
186
|
-
const sb = makeBuilder();
|
|
180
|
+
const { sb, status } = makeBuilder();
|
|
187
181
|
feedAll(sb, [
|
|
188
182
|
makeMessageStart("run-1"),
|
|
189
183
|
makeTextDelta("run-1", "I'll read the file for you."),
|
|
@@ -199,7 +193,6 @@ describe("V3StatusBuilder", () => {
|
|
|
199
193
|
}),
|
|
200
194
|
]);
|
|
201
195
|
|
|
202
|
-
const status = sb.currentStatus;
|
|
203
196
|
|
|
204
197
|
expect(status.messages).toHaveLength(2);
|
|
205
198
|
|
|
@@ -224,17 +217,12 @@ describe("V3StatusBuilder", () => {
|
|
|
224
217
|
expect(secondMsg.type).toBe(MessageType.MESSAGE_AI);
|
|
225
218
|
expect(secondMsg.content).toBe("The file contains a main function that logs 'hello'.");
|
|
226
219
|
expect(secondMsg.toolCalls).toHaveLength(0);
|
|
227
|
-
|
|
228
|
-
const usage = reportedUsage(sb);
|
|
229
|
-
expect(usage.inputTokens).toBe(300);
|
|
230
|
-
expect(usage.outputTokens).toBe(35);
|
|
231
|
-
expect(usage.turnCount).toBe(2);
|
|
232
220
|
});
|
|
233
221
|
});
|
|
234
222
|
|
|
235
223
|
describe("tool error — failed tool call", () => {
|
|
236
224
|
it("marks tool as FAILED with error string", () => {
|
|
237
|
-
const sb = makeBuilder();
|
|
225
|
+
const { sb, status } = makeBuilder();
|
|
238
226
|
feedAll(sb, [
|
|
239
227
|
makeMessageStart("run-1"),
|
|
240
228
|
makeTextDelta("run-1", "I'll write to the file."),
|
|
@@ -250,7 +238,6 @@ describe("V3StatusBuilder", () => {
|
|
|
250
238
|
}),
|
|
251
239
|
]);
|
|
252
240
|
|
|
253
|
-
const status = sb.currentStatus;
|
|
254
241
|
|
|
255
242
|
expect(status.messages).toHaveLength(2);
|
|
256
243
|
|
|
@@ -265,7 +252,7 @@ describe("V3StatusBuilder", () => {
|
|
|
265
252
|
|
|
266
253
|
describe("multi-tool concurrent", () => {
|
|
267
254
|
it("handles two parallel tools on one message with out-of-order completion", () => {
|
|
268
|
-
const sb = makeBuilder();
|
|
255
|
+
const { sb, status } = makeBuilder();
|
|
269
256
|
feedAll(sb, [
|
|
270
257
|
makeMessageStart("run-1"),
|
|
271
258
|
makeTextDelta("run-1", "I'll look up both files."),
|
|
@@ -279,7 +266,6 @@ describe("V3StatusBuilder", () => {
|
|
|
279
266
|
makeToolFinished("toolu_1", "content of a"),
|
|
280
267
|
]);
|
|
281
268
|
|
|
282
|
-
const status = sb.currentStatus;
|
|
283
269
|
|
|
284
270
|
const msg = status.messages[0];
|
|
285
271
|
expect(msg.toolCalls).toHaveLength(2);
|
|
@@ -293,10 +279,16 @@ describe("V3StatusBuilder", () => {
|
|
|
293
279
|
});
|
|
294
280
|
});
|
|
295
281
|
|
|
296
|
-
describe("
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
282
|
+
describe("a gated MCP tool that STARTED", () => {
|
|
283
|
+
// On native a held call never starts: LangGraph's interrupt() runs
|
|
284
|
+
// before the tool handler, so no tool_started arrives and the gate's
|
|
285
|
+
// hold reaches the transcript as approval_proposed from the post-stream
|
|
286
|
+
// seed. A tool_started that does arrive is the engine's word that
|
|
287
|
+
// the call was authorized, so the row is RUNNING and attributed — never
|
|
288
|
+
// WAITING at creation, whatever the policy says.
|
|
289
|
+
it("is RUNNING and attributed to its server; nothing waits", () => {
|
|
290
|
+
const { sb, status } = makeBuilder();
|
|
291
|
+
const gate = gateWith({
|
|
300
292
|
policies: new Map([
|
|
301
293
|
["my-server/dangerous_tool", {
|
|
302
294
|
toolName: "dangerous_tool",
|
|
@@ -307,8 +299,7 @@ describe("V3StatusBuilder", () => {
|
|
|
307
299
|
}],
|
|
308
300
|
]),
|
|
309
301
|
toolServerMap: new Map([["dangerous_tool", "my-server"]]),
|
|
310
|
-
|
|
311
|
-
} as ApprovalPolicyProvider);
|
|
302
|
+
});
|
|
312
303
|
|
|
313
304
|
feedAll(sb, [
|
|
314
305
|
makeMessageStart("run-1"),
|
|
@@ -318,67 +309,23 @@ describe("V3StatusBuilder", () => {
|
|
|
318
309
|
usage: { input_tokens: 100, output_tokens: 10 },
|
|
319
310
|
}),
|
|
320
311
|
makeToolStarted("toolu_1", "dangerous_tool", { path: "/etc/shadow" }),
|
|
321
|
-
]);
|
|
312
|
+
], gate);
|
|
322
313
|
|
|
323
|
-
|
|
324
|
-
expect(sb.awaitingApproval, "a fact for the caller, never a phase write").toBe(true);
|
|
314
|
+
expect(sb.awaitingApproval, "no tool_started parks a row").toBe(false);
|
|
325
315
|
expect(status.phase).toBe(ExecutionPhase.EXECUTION_PHASE_UNSPECIFIED);
|
|
326
316
|
|
|
327
317
|
const tc = status.messages[0].toolCalls[0];
|
|
328
|
-
expect(tc.status).toBe(ToolCallStatus.
|
|
329
|
-
expect(tc.requiresApproval).toBe(
|
|
330
|
-
expect(tc.approvalMessage).toBe("This tool will modify /etc/shadow");
|
|
318
|
+
expect(tc.status).toBe(ToolCallStatus.TOOL_CALL_RUNNING);
|
|
319
|
+
expect(tc.requiresApproval).toBe(false);
|
|
331
320
|
expect(tc.mcpServerSlug).toBe("my-server");
|
|
332
|
-
expect(tc.
|
|
333
|
-
expect(tc.
|
|
334
|
-
});
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
describe("usage accumulation — 3-turn with cache", () => {
|
|
338
|
-
it("accumulates input/output/cache tokens across turns", () => {
|
|
339
|
-
const sb = makeBuilder();
|
|
340
|
-
feedAll(sb, [
|
|
341
|
-
makeMessageStart("run-1"),
|
|
342
|
-
makeTextDelta("run-1", "Turn 1"),
|
|
343
|
-
makeMessageFinish("run-1", {
|
|
344
|
-
usage: {
|
|
345
|
-
input_tokens: 100,
|
|
346
|
-
output_tokens: 20,
|
|
347
|
-
input_token_details: { cache_creation: 80 },
|
|
348
|
-
},
|
|
349
|
-
}),
|
|
350
|
-
makeMessageStart("run-2"),
|
|
351
|
-
makeTextDelta("run-2", "Turn 2"),
|
|
352
|
-
makeMessageFinish("run-2", {
|
|
353
|
-
usage: {
|
|
354
|
-
input_tokens: 200,
|
|
355
|
-
output_tokens: 30,
|
|
356
|
-
input_token_details: { cache_read: 50 },
|
|
357
|
-
},
|
|
358
|
-
}),
|
|
359
|
-
makeMessageStart("run-3"),
|
|
360
|
-
makeTextDelta("run-3", "Turn 3"),
|
|
361
|
-
makeMessageFinish("run-3", {
|
|
362
|
-
usage: {
|
|
363
|
-
input_tokens: 300,
|
|
364
|
-
output_tokens: 10,
|
|
365
|
-
input_token_details: { cache_read: 100 },
|
|
366
|
-
},
|
|
367
|
-
}),
|
|
368
|
-
]);
|
|
369
|
-
|
|
370
|
-
const usage = reportedUsage(sb);
|
|
371
|
-
expect(usage.inputTokens).toBe(600);
|
|
372
|
-
expect(usage.outputTokens).toBe(60);
|
|
373
|
-
expect(usage.cacheWriteTokens).toBe(80);
|
|
374
|
-
expect(usage.cacheReadTokens).toBe(150);
|
|
375
|
-
expect(usage.turnCount).toBe(3);
|
|
321
|
+
expect(tc.approvalPolicySource, "the policy's layer still attributes the row").toBe(ApprovalPolicySource.CLASSIFIER_DEFAULT);
|
|
322
|
+
expect(tc.approvalRequestedAt).toBe("");
|
|
376
323
|
});
|
|
377
324
|
});
|
|
378
325
|
|
|
379
326
|
describe("namespace isolation — parent + subagent", () => {
|
|
380
327
|
it("creates separate message trees per namespace", () => {
|
|
381
|
-
const sb = makeBuilder();
|
|
328
|
+
const { sb, status } = makeBuilder();
|
|
382
329
|
const subNs = ["subagent:worker-1"];
|
|
383
330
|
|
|
384
331
|
feedAll(sb, [
|
|
@@ -412,7 +359,6 @@ describe("V3StatusBuilder", () => {
|
|
|
412
359
|
}),
|
|
413
360
|
]);
|
|
414
361
|
|
|
415
|
-
const status = sb.currentStatus;
|
|
416
362
|
|
|
417
363
|
expect(status.messages).toHaveLength(4);
|
|
418
364
|
|
|
@@ -432,11 +378,6 @@ describe("V3StatusBuilder", () => {
|
|
|
432
378
|
|
|
433
379
|
expect(status.messages[3].type).toBe(MessageType.MESSAGE_AI);
|
|
434
380
|
expect(status.messages[3].content).toBe("The research is complete.");
|
|
435
|
-
|
|
436
|
-
const usage = reportedUsage(sb);
|
|
437
|
-
expect(usage.inputTokens).toBe(650);
|
|
438
|
-
expect(usage.outputTokens).toBe(50);
|
|
439
|
-
expect(usage.turnCount).toBe(3);
|
|
440
381
|
});
|
|
441
382
|
});
|
|
442
383
|
});
|
|
@@ -445,7 +386,7 @@ describe("V3StatusBuilder", () => {
|
|
|
445
386
|
|
|
446
387
|
describe("v3-specific", () => {
|
|
447
388
|
it("keys tool calls by callId, not runId", () => {
|
|
448
|
-
const sb = makeBuilder();
|
|
389
|
+
const { sb, status } = makeBuilder();
|
|
449
390
|
feedAll(sb, [
|
|
450
391
|
makeMessageStart("run-1"),
|
|
451
392
|
makeTextDelta("run-1", "Reading file."),
|
|
@@ -454,29 +395,12 @@ describe("V3StatusBuilder", () => {
|
|
|
454
395
|
makeToolFinished("toolu_abc123", "contents"),
|
|
455
396
|
]);
|
|
456
397
|
|
|
457
|
-
const tc =
|
|
398
|
+
const tc = status.messages[0].toolCalls[0];
|
|
458
399
|
expect(tc.id).toBe("toolu_abc123");
|
|
459
400
|
});
|
|
460
401
|
|
|
461
|
-
it("
|
|
462
|
-
const sb = makeBuilder();
|
|
463
|
-
feedAll(sb, [
|
|
464
|
-
makeMessageStart("run-1"),
|
|
465
|
-
makeTextDelta("run-1", "hi"),
|
|
466
|
-
makeUsageEvent("run-1", { input_tokens: 50, output_tokens: 10 }),
|
|
467
|
-
makeMessageFinish("run-1", {
|
|
468
|
-
usage: { input_tokens: 50, output_tokens: 10 },
|
|
469
|
-
}),
|
|
470
|
-
]);
|
|
471
|
-
|
|
472
|
-
const usage = reportedUsage(sb);
|
|
473
|
-
expect(usage.inputTokens).toBe(50);
|
|
474
|
-
expect(usage.outputTokens).toBe(10);
|
|
475
|
-
expect(usage.turnCount).toBe(1);
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
it("accumulates tool_call_arg_delta into ToolCall.args", () => {
|
|
479
|
-
const sb = makeBuilder();
|
|
402
|
+
it("accumulates tool_arg_delta into ToolCall.args", () => {
|
|
403
|
+
const { sb, status } = makeBuilder();
|
|
480
404
|
feedAll(sb, [
|
|
481
405
|
makeMessageStart("run-1"),
|
|
482
406
|
makeTextDelta("run-1", "Calling tool."),
|
|
@@ -487,12 +411,12 @@ describe("V3StatusBuilder", () => {
|
|
|
487
411
|
makeToolFinished("toolu_1", "ok"),
|
|
488
412
|
]);
|
|
489
413
|
|
|
490
|
-
const tc =
|
|
414
|
+
const tc = status.messages[0].toolCalls[0];
|
|
491
415
|
expect(tc.args).toEqual({ thought: "test" });
|
|
492
416
|
});
|
|
493
417
|
|
|
494
418
|
it("handles tool_output_delta by accumulating into result", () => {
|
|
495
|
-
const sb = makeBuilder();
|
|
419
|
+
const { sb, status } = makeBuilder();
|
|
496
420
|
feedAll(sb, [
|
|
497
421
|
makeMessageStart("run-1"),
|
|
498
422
|
makeTextDelta("run-1", "Running."),
|
|
@@ -503,32 +427,32 @@ describe("V3StatusBuilder", () => {
|
|
|
503
427
|
makeToolFinished("toolu_1", " final"),
|
|
504
428
|
]);
|
|
505
429
|
|
|
506
|
-
const tc =
|
|
430
|
+
const tc = status.messages[0].toolCalls[0];
|
|
507
431
|
expect(tc.result).toBe(" final");
|
|
508
432
|
});
|
|
509
433
|
|
|
510
|
-
it("sets
|
|
511
|
-
const sb = makeBuilder();
|
|
434
|
+
it("sets dirty on tool start and tool finish", () => {
|
|
435
|
+
const { sb } = makeBuilder();
|
|
512
436
|
|
|
513
437
|
feedAll(sb, [
|
|
514
438
|
makeMessageStart("run-1"),
|
|
515
439
|
makeTextDelta("run-1", "x"),
|
|
516
440
|
makeMessageFinish("run-1", { usage: { input_tokens: 1, output_tokens: 1 } }),
|
|
517
441
|
]);
|
|
518
|
-
expect(sb.
|
|
442
|
+
expect(sb.dirty).toBe(false);
|
|
519
443
|
|
|
520
444
|
feedAll(sb, [makeToolStarted("toolu_1", "read", {})]);
|
|
521
|
-
expect(sb.
|
|
522
|
-
sb.
|
|
445
|
+
expect(sb.dirty).toBe(true);
|
|
446
|
+
sb.markPersisted();
|
|
523
447
|
|
|
524
448
|
feedAll(sb, [makeToolFinished("toolu_1", "done")]);
|
|
525
|
-
expect(sb.
|
|
449
|
+
expect(sb.dirty).toBe(true);
|
|
526
450
|
});
|
|
527
451
|
|
|
528
452
|
it("ignores tool-finished for unknown callId", () => {
|
|
529
|
-
const sb = makeBuilder();
|
|
453
|
+
const { sb, status } = makeBuilder();
|
|
530
454
|
feedAll(sb, [makeToolFinished("unknown_toolu", "some result")]);
|
|
531
|
-
expect(
|
|
455
|
+
expect(status.messages).toHaveLength(0);
|
|
532
456
|
});
|
|
533
457
|
});
|
|
534
458
|
|
|
@@ -539,7 +463,7 @@ describe("V3StatusBuilder", () => {
|
|
|
539
463
|
|
|
540
464
|
describe("todo extraction", () => {
|
|
541
465
|
it("projects a completed write_todos into status.todos", () => {
|
|
542
|
-
const sb = makeBuilder();
|
|
466
|
+
const { sb, status } = makeBuilder();
|
|
543
467
|
feedAll(sb, [
|
|
544
468
|
makeMessageStart("run-1"),
|
|
545
469
|
makeTextDelta("run-1", "Planning the build."),
|
|
@@ -553,17 +477,17 @@ describe("V3StatusBuilder", () => {
|
|
|
553
477
|
makeToolFinished("todo-1", "Todos updated"),
|
|
554
478
|
]);
|
|
555
479
|
|
|
556
|
-
const todos =
|
|
480
|
+
const todos = status.todos;
|
|
557
481
|
expect(Object.keys(todos)).toEqual(["todo-0", "todo-1"]);
|
|
558
482
|
expect(todos["todo-0"].content).toBe("Step one");
|
|
559
483
|
expect(todos["todo-0"].status).toBe(TodoStatus.TODO_IN_PROGRESS);
|
|
560
484
|
expect(todos["todo-1"].content).toBe("Step two");
|
|
561
485
|
expect(todos["todo-1"].status).toBe(TodoStatus.TODO_PENDING);
|
|
562
|
-
expect(sb.
|
|
486
|
+
expect(sb.dirty).toBe(true);
|
|
563
487
|
});
|
|
564
488
|
|
|
565
489
|
it("keeps the write_todos ToolCall in messages stamped ToolKind.TODO", () => {
|
|
566
|
-
const sb = makeBuilder();
|
|
490
|
+
const { sb, status } = makeBuilder();
|
|
567
491
|
feedAll(sb, [
|
|
568
492
|
makeToolStarted("todo-1", "write_todos", {
|
|
569
493
|
todos: [{ content: "Step one", status: "pending" }],
|
|
@@ -571,28 +495,28 @@ describe("V3StatusBuilder", () => {
|
|
|
571
495
|
makeToolFinished("todo-1", "Todos updated"),
|
|
572
496
|
]);
|
|
573
497
|
|
|
574
|
-
const toolCalls =
|
|
498
|
+
const toolCalls = status.messages.flatMap((m) => m.toolCalls);
|
|
575
499
|
const tc = toolCalls.find((t) => t.name === "write_todos");
|
|
576
500
|
expect(tc).toBeDefined();
|
|
577
501
|
expect(tc!.toolKind).toBe(ToolKind.TODO);
|
|
578
502
|
});
|
|
579
503
|
|
|
580
504
|
it("does not project until the call completes", () => {
|
|
581
|
-
const sb = makeBuilder();
|
|
505
|
+
const { sb, status } = makeBuilder();
|
|
582
506
|
feedAll(sb, [
|
|
583
507
|
makeToolStarted("todo-1", "write_todos", {
|
|
584
508
|
todos: [{ content: "Step one", status: "pending" }],
|
|
585
509
|
}),
|
|
586
510
|
]);
|
|
587
511
|
// Tool started but not finished — the state Command has not run yet.
|
|
588
|
-
expect(Object.keys(
|
|
512
|
+
expect(Object.keys(status.todos)).toHaveLength(0);
|
|
589
513
|
|
|
590
514
|
feedAll(sb, [makeToolFinished("todo-1", "Todos updated")]);
|
|
591
|
-
expect(Object.keys(
|
|
515
|
+
expect(Object.keys(status.todos)).toHaveLength(1);
|
|
592
516
|
});
|
|
593
517
|
|
|
594
518
|
it("full-replaces the map on a subsequent write_todos", () => {
|
|
595
|
-
const sb = makeBuilder();
|
|
519
|
+
const { sb, status } = makeBuilder();
|
|
596
520
|
feedAll(sb, [
|
|
597
521
|
makeToolStarted("todo-1", "write_todos", {
|
|
598
522
|
todos: [
|
|
@@ -607,37 +531,33 @@ describe("V3StatusBuilder", () => {
|
|
|
607
531
|
makeToolFinished("todo-2", "ok"),
|
|
608
532
|
]);
|
|
609
533
|
|
|
610
|
-
const todos =
|
|
534
|
+
const todos = status.todos;
|
|
611
535
|
expect(Object.keys(todos)).toEqual(["todo-0"]);
|
|
612
536
|
expect(todos["todo-0"].content).toBe("Step two");
|
|
613
537
|
expect(todos["todo-0"].status).toBe(TodoStatus.TODO_COMPLETED);
|
|
614
538
|
});
|
|
615
539
|
|
|
616
|
-
it("
|
|
617
|
-
const sb = makeBuilder();
|
|
540
|
+
it("routes a sub-agent's message to its own row, never the parent's", () => {
|
|
541
|
+
const { sb, status } = makeBuilder();
|
|
618
542
|
const subNs = ["tools:task-1", "model_request:sub-run"];
|
|
619
543
|
feedAll(sb, [
|
|
620
|
-
...[makeMessageStart("parent-1"), makeTextDelta("parent-1", "Delegating."), makeMessageFinish("parent-1"
|
|
544
|
+
...[makeMessageStart("parent-1"), makeTextDelta("parent-1", "Delegating."), makeMessageFinish("parent-1")],
|
|
621
545
|
makeToolStarted("task-1", "task", { subagent_type: "worker", description: "delegate" }),
|
|
622
546
|
makeMessageStart("sub-run", { namespace: subNs }),
|
|
623
547
|
makeTextDelta("sub-run", "Working on it.", { namespace: subNs }),
|
|
624
|
-
makeMessageFinish("sub-run", { namespace: subNs
|
|
548
|
+
makeMessageFinish("sub-run", { namespace: subNs }),
|
|
625
549
|
]);
|
|
626
550
|
|
|
627
|
-
|
|
628
|
-
expect(
|
|
629
|
-
expect(
|
|
630
|
-
expect(usage.turnCount).toBe(2);
|
|
631
|
-
// The transcript still routes the sub-agent's message to its own row, never the parent's.
|
|
632
|
-
expect(sb.currentStatus.messages.map((m) => m.content)).toEqual(["Delegating."]);
|
|
633
|
-
expect(sb.currentStatus.subAgentExecutions).toHaveLength(1);
|
|
551
|
+
expect(status.messages.map((m) => m.content)).toEqual(["Delegating."]);
|
|
552
|
+
expect(status.subAgentExecutions).toHaveLength(1);
|
|
553
|
+
expect(status.subAgentExecutions[0].messages.map((m) => m.content)).toEqual(["Working on it."]);
|
|
634
554
|
});
|
|
635
555
|
|
|
636
556
|
it("does not project a sub-agent's write_todos into parent status.todos", () => {
|
|
637
|
-
const sb = makeBuilder();
|
|
557
|
+
const { sb, status } = makeBuilder();
|
|
638
558
|
const subNs = ["tools:task-1", "tools:sub-todo"];
|
|
639
559
|
feedAll(sb, [
|
|
640
|
-
//
|
|
560
|
+
// Open a sub-agent so its namespace routes to the sub-agent's own transcript scope.
|
|
641
561
|
makeToolStarted("task-1", "task", {
|
|
642
562
|
subagent_type: "worker",
|
|
643
563
|
description: "delegate",
|
|
@@ -652,20 +572,29 @@ describe("V3StatusBuilder", () => {
|
|
|
652
572
|
makeToolFinished("sub-todo", "ok", { namespace: subNs }),
|
|
653
573
|
]);
|
|
654
574
|
|
|
655
|
-
expect(Object.keys(
|
|
575
|
+
expect(Object.keys(status.todos)).toHaveLength(0);
|
|
656
576
|
});
|
|
657
577
|
});
|
|
658
578
|
// ═══════════════════════════════════════════════════════════════════
|
|
659
579
|
// Carried from the v2 StatusBuilder's suite when that builder retired
|
|
660
|
-
// with the v2 stream (
|
|
580
|
+
// with the v2 stream (#1096): every rule below is the
|
|
661
581
|
// builder's own — how a policy decides a row's status, what the row
|
|
662
582
|
// carries, how a seeded row is resolved, how artifacts and write-backs
|
|
663
583
|
// are upserted — and had no v3 twin. The sanitizer's own rules
|
|
664
584
|
// (redaction, truncation) are `shared/__tests__/args-preview.test.ts`'s.
|
|
665
585
|
// ═══════════════════════════════════════════════════════════════════
|
|
666
586
|
|
|
667
|
-
|
|
668
|
-
|
|
587
|
+
/** A gate posture for the translator: no policies, no leases, attended, not bypassed — unless overridden. */
|
|
588
|
+
function gateWith(overrides: Partial<DeepAgentGateState> = {}): DeepAgentGateState {
|
|
589
|
+
return {
|
|
590
|
+
policies: new Map(),
|
|
591
|
+
toolServerMap: new Map(),
|
|
592
|
+
leasedCategories: new Set(),
|
|
593
|
+
globalBypass: false,
|
|
594
|
+
unattended: false,
|
|
595
|
+
unattendedSkips: new Set(),
|
|
596
|
+
...overrides,
|
|
597
|
+
};
|
|
669
598
|
}
|
|
670
599
|
|
|
671
600
|
function policyFor(serverSlug: string, toolName: string, message = "Approve?"): [string, MergedToolPolicy] {
|
|
@@ -675,119 +604,104 @@ describe("V3StatusBuilder", () => {
|
|
|
675
604
|
];
|
|
676
605
|
}
|
|
677
606
|
|
|
678
|
-
/** A text turn, then one tool start,
|
|
679
|
-
function firstToolCallUnder(
|
|
680
|
-
const sb = makeBuilder();
|
|
681
|
-
sb.setApprovalProvider(provider);
|
|
607
|
+
/** A text turn, then one tool start, through a translator over the given gate posture. */
|
|
608
|
+
function firstToolCallUnder(gate: DeepAgentGateState, toolName: string, input: Record<string, unknown> = {}) {
|
|
609
|
+
const { sb, status } = makeBuilder();
|
|
682
610
|
feedAll(sb, [
|
|
683
611
|
makeMessageStart("run-1"),
|
|
684
612
|
makeTextDelta("run-1", "Working."),
|
|
685
613
|
makeMessageFinish("run-1", { usage: { input_tokens: 10, output_tokens: 5 } }),
|
|
686
614
|
makeToolStarted("toolu_1", toolName, input),
|
|
687
|
-
]);
|
|
688
|
-
return { sb, tc:
|
|
615
|
+
], gate);
|
|
616
|
+
return { sb, tc: status.messages[0].toolCalls[0] };
|
|
689
617
|
}
|
|
690
618
|
|
|
691
|
-
describe("
|
|
692
|
-
|
|
619
|
+
describe("attribution and provenance on tool_started", () => {
|
|
620
|
+
// The translator answers WHICH server a tool belongs to and WHICH policy
|
|
621
|
+
// layer governs it; it never answers whether the gate holds the call —
|
|
622
|
+
// on native a held call never starts (the interrupt precedes the
|
|
623
|
+
// handler), so every started call is RUNNING and nothing here waits.
|
|
624
|
+
// The decision itself, `resolveToolApproval`, has its own arms in
|
|
625
|
+
// `shared/__tests__/approval-policy.test.ts`; the gate's use of it in
|
|
626
|
+
// `middleware/__tests__/approval-gate.test.ts`.
|
|
627
|
+
|
|
628
|
+
it("an MCP tool with a gating policy STILL runs when it started, attributed to its server and the policy's layer", () => {
|
|
693
629
|
const [key, policy] = policyFor("github", "create_issue");
|
|
694
630
|
const { sb, tc } = firstToolCallUnder(
|
|
695
|
-
|
|
631
|
+
gateWith({ policies: new Map([[key, policy]]), toolServerMap: new Map([["create_issue", "github"]]) }),
|
|
696
632
|
"create_issue",
|
|
697
633
|
{ title: "Bug fix" },
|
|
698
634
|
);
|
|
699
|
-
expect(tc.status).toBe(ToolCallStatus.TOOL_CALL_WAITING_APPROVAL);
|
|
700
|
-
expect(tc.requiresApproval).toBe(true);
|
|
701
|
-
expect(tc.approvalMessage).toBe("Approve?");
|
|
702
|
-
expect(tc.mcpServerSlug).toBe("github");
|
|
703
|
-
expect(tc.approvalRequestedAt, "stamped when approval is required").toContain("T");
|
|
704
|
-
expect(sb.awaitingApproval).toBe(true);
|
|
705
|
-
});
|
|
706
|
-
|
|
707
|
-
it("the global bypass (spec.auto_approve_all) runs a gated tool without approval", () => {
|
|
708
|
-
const [key, policy] = policyFor("github", "delete_repo");
|
|
709
|
-
const { sb, tc } = firstToolCallUnder(
|
|
710
|
-
providerWith({ policies: new Map([[key, policy]]), toolServerMap: new Map([["delete_repo", "github"]]), globalBypass: true }),
|
|
711
|
-
"delete_repo",
|
|
712
|
-
);
|
|
713
635
|
expect(tc.status).toBe(ToolCallStatus.TOOL_CALL_RUNNING);
|
|
714
636
|
expect(tc.requiresApproval).toBe(false);
|
|
637
|
+
expect(tc.mcpServerSlug).toBe("github");
|
|
638
|
+
expect(tc.approvalPolicySource).toBe(ApprovalPolicySource.CLASSIFIER_DEFAULT);
|
|
639
|
+
expect(tc.policyEngineVersion).toBe("phase-7");
|
|
715
640
|
expect(sb.awaitingApproval).toBe(false);
|
|
716
641
|
});
|
|
717
642
|
|
|
718
|
-
it("the global bypass
|
|
643
|
+
it("the global bypass (spec.auto_approve_all) attributes every row to it", () => {
|
|
644
|
+
const [key, policy] = policyFor("github", "delete_repo");
|
|
719
645
|
const { tc } = firstToolCallUnder(
|
|
720
|
-
|
|
721
|
-
"
|
|
722
|
-
{ input: "hello" },
|
|
646
|
+
gateWith({ policies: new Map([[key, policy]]), toolServerMap: new Map([["delete_repo", "github"]]), globalBypass: true }),
|
|
647
|
+
"delete_repo",
|
|
723
648
|
);
|
|
724
649
|
expect(tc.status).toBe(ToolCallStatus.TOOL_CALL_RUNNING);
|
|
725
|
-
expect(tc.mcpServerSlug).toBe("
|
|
650
|
+
expect(tc.mcpServerSlug, "the bypass still attributes the row to its MCP server").toBe("github");
|
|
651
|
+
expect(tc.approvalPolicySource).toBe(ApprovalPolicySource.AUTO_APPROVE_ALL);
|
|
726
652
|
});
|
|
727
653
|
|
|
728
|
-
it("
|
|
729
|
-
const { tc } = firstToolCallUnder(
|
|
730
|
-
expect(tc.status).toBe(ToolCallStatus.TOOL_CALL_RUNNING);
|
|
654
|
+
it("an MCP tool with no policy is attributed to its server, classifier_default", () => {
|
|
655
|
+
const { tc } = firstToolCallUnder(gateWith({ toolServerMap: new Map([["read", "filesystem"]]) }), "read");
|
|
731
656
|
expect(tc.mcpServerSlug).toBe("filesystem");
|
|
657
|
+
expect(tc.approvalPolicySource).toBe(ApprovalPolicySource.CLASSIFIER_DEFAULT);
|
|
732
658
|
});
|
|
733
659
|
|
|
734
|
-
it("a
|
|
660
|
+
it("a tool the server map does not know carries no server (the policy key needs the server)", () => {
|
|
735
661
|
const [key, policy] = policyFor("github", "create_pr");
|
|
736
|
-
const { tc } = firstToolCallUnder(
|
|
737
|
-
expect(tc.
|
|
662
|
+
const { tc } = firstToolCallUnder(gateWith({ policies: new Map([[key, policy]]), toolServerMap: new Map() }), "create_pr");
|
|
663
|
+
expect(tc.mcpServerSlug).toBe("");
|
|
664
|
+
expect(tc.approvalPolicySource, "an unknown built-in is governed by no layer").toBe(ApprovalPolicySource.UNSPECIFIED);
|
|
665
|
+
expect(tc.policyEngineVersion).toBe("");
|
|
738
666
|
});
|
|
739
667
|
|
|
740
|
-
it("
|
|
741
|
-
const
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
{ title: "Fix crash", repo: "stigmer/stigmer" },
|
|
746
|
-
);
|
|
747
|
-
expect(tc.approvalMessage).toBe("Create issue 'Fix crash' in stigmer/stigmer?");
|
|
668
|
+
it("a mutating built-in that started is attributed builtin_category — the gate let it flow (capture mode, or a resume)", () => {
|
|
669
|
+
const { sb, tc } = firstToolCallUnder(gateWith(), "execute", { command: "rm -rf build" });
|
|
670
|
+
expect(tc.status).toBe(ToolCallStatus.TOOL_CALL_RUNNING);
|
|
671
|
+
expect(tc.approvalPolicySource).toBe(ApprovalPolicySource.BUILTIN_CATEGORY);
|
|
672
|
+
expect(sb.awaitingApproval).toBe(false);
|
|
748
673
|
});
|
|
749
|
-
});
|
|
750
674
|
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
providerWith({ policies: new Map([[key, policy]]), toolServerMap: new Map([["connect", "db"]]) }),
|
|
756
|
-
"connect",
|
|
757
|
-
{ host: "localhost", password: "super-secret" },
|
|
758
|
-
);
|
|
759
|
-
const preview = JSON.parse(gated.tc.argsPreview) as Record<string, string>;
|
|
760
|
-
expect(preview.host).toBe("localhost");
|
|
761
|
-
expect(preview.password, "the shared sanitizer redacts secret keys").toBe("[REDACTED]");
|
|
675
|
+
it("a leased built-in category is attributed to its lease", () => {
|
|
676
|
+
const { tc } = firstToolCallUnder(gateWith({ leasedCategories: new Set(["shell"]) }), "execute", { command: "ls" });
|
|
677
|
+
expect(tc.approvalPolicySource).toBe(ApprovalPolicySource.APPROVAL_LEASE);
|
|
678
|
+
});
|
|
762
679
|
|
|
763
|
-
|
|
764
|
-
|
|
680
|
+
it("a read-only built-in is governed by no layer: UNSPECIFIED, no engine version", () => {
|
|
681
|
+
const { tc } = firstToolCallUnder(gateWith(), "read_file", { path: "/x" });
|
|
682
|
+
expect(tc.approvalPolicySource).toBe(ApprovalPolicySource.UNSPECIFIED);
|
|
683
|
+
expect(tc.policyEngineVersion).toBe("");
|
|
765
684
|
});
|
|
766
685
|
|
|
767
|
-
it("
|
|
768
|
-
const
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
"call",
|
|
774
|
-
circular,
|
|
775
|
-
);
|
|
776
|
-
expect(tc.status).toBe(ToolCallStatus.TOOL_CALL_WAITING_APPROVAL);
|
|
777
|
-
expect(tc.argsPreview).toBe("");
|
|
686
|
+
it("with no gate posture at all (a turn the unit arms drive bare) nothing is attributed", () => {
|
|
687
|
+
const { sb, status } = makeBuilder();
|
|
688
|
+
feedAll(sb, [makeToolStarted("toolu_1", "execute", { command: "ls" })]);
|
|
689
|
+
const tc = status.messages[0].toolCalls[0];
|
|
690
|
+
expect(tc.mcpServerSlug).toBe("");
|
|
691
|
+
expect(tc.approvalPolicySource).toBe(ApprovalPolicySource.UNSPECIFIED);
|
|
778
692
|
});
|
|
779
693
|
});
|
|
780
694
|
|
|
781
695
|
describe("tool rows without a preceding text turn, and interleaving", () => {
|
|
782
696
|
it("a tool start with no prior text creates the AI message that carries it", () => {
|
|
783
|
-
const sb = makeBuilder();
|
|
697
|
+
const { sb, status } = makeBuilder();
|
|
784
698
|
feedAll(sb, [makeToolStarted("orphan", "read", {})]);
|
|
785
|
-
expect(
|
|
786
|
-
expect(
|
|
699
|
+
expect(status.messages).toHaveLength(1);
|
|
700
|
+
expect(status.messages[0].toolCalls[0].name).toBe("read");
|
|
787
701
|
});
|
|
788
702
|
|
|
789
703
|
it("interleaved start/finish pairs settle each row independently", () => {
|
|
790
|
-
const sb = makeBuilder();
|
|
704
|
+
const { sb, status } = makeBuilder();
|
|
791
705
|
feedAll(sb, [
|
|
792
706
|
makeMessageStart("run-1"),
|
|
793
707
|
makeTextDelta("run-1", "working"),
|
|
@@ -797,29 +711,29 @@ describe("V3StatusBuilder", () => {
|
|
|
797
711
|
makeToolStarted("toolu_b", "write", {}),
|
|
798
712
|
makeToolFinished("toolu_b", "done"),
|
|
799
713
|
]);
|
|
800
|
-
const rows =
|
|
714
|
+
const rows = status.messages.flatMap((m) => m.toolCalls);
|
|
801
715
|
expect(rows.map((r) => r.status)).toEqual([ToolCallStatus.TOOL_CALL_COMPLETED, ToolCallStatus.TOOL_CALL_COMPLETED]);
|
|
802
716
|
});
|
|
803
717
|
});
|
|
804
718
|
|
|
805
719
|
describe("a handler that throws", () => {
|
|
806
|
-
it("is logged with the execution
|
|
807
|
-
const sb = makeBuilder();
|
|
720
|
+
it("is logged with the execution and the event kind, and never escapes apply", () => {
|
|
721
|
+
const { sb } = makeBuilder();
|
|
808
722
|
const errors: string[] = [];
|
|
809
723
|
const original = console.error;
|
|
810
724
|
console.error = (msg: unknown) => { errors.push(String(msg)); };
|
|
811
725
|
try {
|
|
812
|
-
const poisoned =
|
|
813
|
-
// A getter that throws where the handler reads the
|
|
814
|
-
const event = { ...poisoned, get
|
|
815
|
-
expect(() => sb.
|
|
726
|
+
const poisoned = new DeepAgentTranslator(null).translate(makeToolFinished("toolu_x", "ok"))[0]!;
|
|
727
|
+
// A getter that throws where the handler reads the result.
|
|
728
|
+
const event = { ...poisoned, get result(): string { throw new Error("property access failed"); } };
|
|
729
|
+
expect(() => sb.apply(event as typeof poisoned)).not.toThrow();
|
|
816
730
|
} finally {
|
|
817
731
|
console.error = original;
|
|
818
732
|
}
|
|
819
733
|
expect(errors).toHaveLength(1);
|
|
820
734
|
expect(errors[0]).toContain("exec-test");
|
|
821
735
|
expect(errors[0]).toContain("kind=tool_finished");
|
|
822
|
-
expect(errors[0]).
|
|
736
|
+
expect(errors[0]).toContain("property access failed");
|
|
823
737
|
});
|
|
824
738
|
});
|
|
825
739
|
|
|
@@ -846,9 +760,9 @@ describe("V3StatusBuilder", () => {
|
|
|
846
760
|
}),
|
|
847
761
|
],
|
|
848
762
|
});
|
|
849
|
-
const sb = new
|
|
763
|
+
const sb = new TranscriptBuilder("exec-resume", status);
|
|
850
764
|
const [key, policy] = policyFor("my-server", "dangerous_tool", "Execute dangerous_tool");
|
|
851
|
-
|
|
765
|
+
const gate = gateWith({ policies: new Map([[key, policy]]), toolServerMap: new Map([["dangerous_tool", "my-server"]]) });
|
|
852
766
|
|
|
853
767
|
// The durable checkpoint re-emits the SAME tool_call_id now that approval is granted.
|
|
854
768
|
feedAll(sb, [
|
|
@@ -857,60 +771,60 @@ describe("V3StatusBuilder", () => {
|
|
|
857
771
|
makeMessageStart("llm-after"),
|
|
858
772
|
makeTextDelta("llm-after", "All done."),
|
|
859
773
|
makeMessageFinish("llm-after", { usage: { input_tokens: 1, output_tokens: 1 } }),
|
|
860
|
-
]);
|
|
774
|
+
], gate);
|
|
861
775
|
|
|
862
|
-
const gated =
|
|
776
|
+
const gated = status.messages.flatMap((m) => m.toolCalls).filter((tc) => tc.id === GATED_ID);
|
|
863
777
|
expect(gated).toHaveLength(1);
|
|
864
778
|
expect(gated[0].status).toBe(ToolCallStatus.TOOL_CALL_COMPLETED);
|
|
865
779
|
expect(gated[0].result).toContain("tool executed ok");
|
|
866
780
|
expect(gated[0].args, "the resumed start fills the args the seed lacked").toEqual({ target: "prod" });
|
|
867
|
-
const allText =
|
|
781
|
+
const allText = status.messages.map((m) => m.content).join("\n");
|
|
868
782
|
expect(allText).toContain("I'll call the gated tool.");
|
|
869
783
|
expect(allText).toContain("All done.");
|
|
870
784
|
expect(sb.awaitingApproval, "a resumed tool never re-gates").toBe(false);
|
|
871
785
|
});
|
|
872
786
|
});
|
|
873
787
|
|
|
874
|
-
describe("artifacts and write-backs
|
|
788
|
+
describe("artifacts and write-backs", () => {
|
|
875
789
|
const artifact = (sandboxPath: string, contentHash: string) =>
|
|
876
790
|
create(ExecutionArtifactSchema, { sandboxPath, contentHash, storageKey: `k/${contentHash}` });
|
|
877
791
|
|
|
878
792
|
it("appends a new artifact and forces the next persist", () => {
|
|
879
|
-
const sb = makeBuilder();
|
|
793
|
+
const { sb, status } = makeBuilder();
|
|
880
794
|
sb.addArtifact(artifact("out/a.txt", "h1"));
|
|
881
|
-
expect(
|
|
882
|
-
expect(sb.
|
|
795
|
+
expect(status.artifacts).toHaveLength(1);
|
|
796
|
+
expect(sb.dirty).toBe(true);
|
|
883
797
|
});
|
|
884
798
|
|
|
885
799
|
it("deduplicates by sandboxPath: the same contentHash is a no-op that does not force a persist", () => {
|
|
886
|
-
const sb = makeBuilder();
|
|
800
|
+
const { sb, status } = makeBuilder();
|
|
887
801
|
sb.addArtifact(artifact("out/a.txt", "h1"));
|
|
888
|
-
sb.
|
|
802
|
+
sb.markPersisted();
|
|
889
803
|
sb.addArtifact(artifact("out/a.txt", "h1"));
|
|
890
|
-
expect(
|
|
891
|
-
expect(sb.
|
|
804
|
+
expect(status.artifacts).toHaveLength(1);
|
|
805
|
+
expect(sb.dirty).toBe(false);
|
|
892
806
|
});
|
|
893
807
|
|
|
894
808
|
it("replaces the artifact in place when its contentHash changes", () => {
|
|
895
|
-
const sb = makeBuilder();
|
|
809
|
+
const { sb, status } = makeBuilder();
|
|
896
810
|
sb.addArtifact(artifact("out/a.txt", "h1"));
|
|
897
|
-
sb.
|
|
811
|
+
sb.markPersisted();
|
|
898
812
|
sb.addArtifact(artifact("out/a.txt", "h2"));
|
|
899
|
-
expect(
|
|
900
|
-
expect(
|
|
901
|
-
expect(sb.
|
|
813
|
+
expect(status.artifacts).toHaveLength(1);
|
|
814
|
+
expect(status.artifacts[0].contentHash).toBe("h2");
|
|
815
|
+
expect(sb.dirty).toBe(true);
|
|
902
816
|
});
|
|
903
817
|
|
|
904
818
|
it("upserts write-backs by workspaceEntryName, tracking entries independently", () => {
|
|
905
|
-
const sb = makeBuilder();
|
|
819
|
+
const { sb, status } = makeBuilder();
|
|
906
820
|
sb.addWriteBack(create(WorkspaceWriteBackSchema, { workspaceEntryName: "repo-a", branchName: "stigmer/s1" }));
|
|
907
821
|
sb.addWriteBack(create(WorkspaceWriteBackSchema, { workspaceEntryName: "repo-b", branchName: "stigmer/s1" }));
|
|
908
|
-
sb.
|
|
822
|
+
sb.markPersisted();
|
|
909
823
|
sb.addWriteBack(create(WorkspaceWriteBackSchema, { workspaceEntryName: "repo-a", branchName: "stigmer/s1", pullRequestUrl: "https://example/pr/1" }));
|
|
910
|
-
const backs =
|
|
824
|
+
const backs = status.workspaceWriteBacks;
|
|
911
825
|
expect(backs.map((b) => b.workspaceEntryName)).toEqual(["repo-a", "repo-b"]);
|
|
912
826
|
expect(backs[0].pullRequestUrl).toBe("https://example/pr/1");
|
|
913
|
-
expect(sb.
|
|
827
|
+
expect(sb.dirty).toBe(true);
|
|
914
828
|
});
|
|
915
829
|
});
|
|
916
830
|
});
|