@stigmer/runner 3.15.0 → 3.15.2-dev.20260913223433
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 +2 -4
- package/dist/.build-fingerprint +1 -1
- package/dist/__test-utils__/execution-record-fixture.d.ts +14 -0
- package/dist/__test-utils__/execution-record-fixture.js +4 -0
- package/dist/__test-utils__/execution-record-fixture.js.map +1 -1
- package/dist/__test-utils__/file-review-projection.d.ts +40 -0
- package/dist/__test-utils__/file-review-projection.js +136 -0
- package/dist/__test-utils__/file-review-projection.js.map +1 -0
- package/dist/__test-utils__/harness-contract/types.d.ts +23 -1
- package/dist/__test-utils__/harness-contract/types.js +15 -0
- package/dist/__test-utils__/harness-contract/types.js.map +1 -1
- package/dist/__test-utils__/hermetic-activity.d.ts +44 -12
- package/dist/__test-utils__/hermetic-activity.js +52 -14
- package/dist/__test-utils__/hermetic-activity.js.map +1 -1
- package/dist/__test-utils__/model-registry-fixture.d.ts +32 -7
- package/dist/__test-utils__/model-registry-fixture.js +31 -7
- package/dist/__test-utils__/model-registry-fixture.js.map +1 -1
- package/dist/__test-utils__/turn-input-fixture.d.ts +1 -1
- package/dist/__test-utils__/turn-input-fixture.js +1 -1
- package/dist/__test-utils__/turn-input-fixture.js.map +1 -1
- package/dist/activities/call-agent.js +2 -2
- package/dist/activities/call-agent.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/contract-subject.js +45 -3
- package/dist/activities/execute-cursor/__test-utils__/contract-subject.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.d.ts +0 -9
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js +2 -41
- package/dist/activities/execute-cursor/__test-utils__/hermetic-cursor.js.map +1 -1
- package/dist/activities/execute-cursor/adapter.d.ts +7 -5
- package/dist/activities/execute-cursor/adapter.js +7 -5
- package/dist/activities/execute-cursor/adapter.js.map +1 -1
- package/dist/activities/execute-cursor/approval-policy.d.ts +5 -2
- package/dist/activities/execute-cursor/approval-policy.js +5 -2
- package/dist/activities/execute-cursor/approval-policy.js.map +1 -1
- package/dist/activities/execute-cursor/approval-state.d.ts +16 -8
- package/dist/activities/execute-cursor/approval-state.js +6 -3
- package/dist/activities/execute-cursor/approval-state.js.map +1 -1
- package/dist/activities/execute-cursor/cas-observations.d.ts +14 -2
- package/dist/activities/execute-cursor/cas-observations.js +19 -2
- package/dist/activities/execute-cursor/cas-observations.js.map +1 -1
- package/dist/activities/execute-cursor/cursor-capabilities.d.ts +8 -4
- package/dist/activities/execute-cursor/cursor-capabilities.js +21 -4
- package/dist/activities/execute-cursor/cursor-capabilities.js.map +1 -1
- package/dist/activities/execute-cursor/message-translator.d.ts +12 -20
- package/dist/activities/execute-cursor/message-translator.js +19 -32
- package/dist/activities/execute-cursor/message-translator.js.map +1 -1
- package/dist/activities/execute-cursor/prompt-builder.d.ts +48 -58
- package/dist/activities/execute-cursor/prompt-builder.js +76 -95
- package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
- package/dist/activities/execute-cursor/turn-boundary.d.ts +27 -55
- package/dist/activities/execute-cursor/turn-boundary.js +30 -90
- 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 +37 -49
- package/dist/activities/execute-cursor/turn-settle.js.map +1 -1
- package/dist/activities/execute-cursor/turn-setup.d.ts +16 -24
- package/dist/activities/execute-cursor/turn-setup.js +29 -59
- package/dist/activities/execute-cursor/turn-setup.js.map +1 -1
- package/dist/activities/execute-cursor/turn-stream.d.ts +3 -8
- package/dist/activities/execute-cursor/turn-stream.js +9 -22
- package/dist/activities/execute-cursor/turn-stream.js.map +1 -1
- package/dist/activities/execute-cursor/turn.js +5 -1
- package/dist/activities/execute-cursor/turn.js.map +1 -1
- package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.d.ts +93 -0
- package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.js +348 -0
- package/dist/activities/execute-deep-agent/__test-utils__/contract-subject.js.map +1 -0
- package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.d.ts +170 -0
- package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js +225 -0
- package/dist/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.js.map +1 -0
- package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.d.ts +24 -0
- package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.js +41 -0
- package/dist/activities/execute-deep-agent/__test-utils__/hitl-script.js.map +1 -0
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.d.ts +54 -0
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.js +72 -0
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model-module.js.map +1 -0
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.d.ts +187 -23
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js +238 -27
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js.map +1 -1
- package/dist/activities/execute-deep-agent/adapter.d.ts +41 -0
- package/dist/activities/execute-deep-agent/adapter.js +78 -0
- package/dist/activities/execute-deep-agent/adapter.js.map +1 -0
- package/dist/activities/execute-deep-agent/cas-capture-observer.d.ts +8 -0
- package/dist/activities/execute-deep-agent/cas-capture-observer.js +9 -0
- package/dist/activities/execute-deep-agent/cas-capture-observer.js.map +1 -1
- package/dist/activities/execute-deep-agent/deep-agent-capabilities.d.ts +31 -0
- package/dist/activities/execute-deep-agent/deep-agent-capabilities.js +40 -0
- package/dist/activities/execute-deep-agent/deep-agent-capabilities.js.map +1 -0
- package/dist/activities/execute-deep-agent/execution-state.d.ts +4 -11
- package/dist/activities/execute-deep-agent/execution-state.js +4 -12
- package/dist/activities/execute-deep-agent/execution-state.js.map +1 -1
- package/dist/activities/execute-deep-agent/hitl.d.ts +49 -47
- package/dist/activities/execute-deep-agent/hitl.js +77 -103
- package/dist/activities/execute-deep-agent/hitl.js.map +1 -1
- package/dist/activities/execute-deep-agent/prompt-builder.d.ts +51 -23
- package/dist/activities/execute-deep-agent/prompt-builder.js +115 -76
- package/dist/activities/execute-deep-agent/prompt-builder.js.map +1 -1
- package/dist/activities/execute-deep-agent/status-builder-shared.d.ts +18 -32
- package/dist/activities/execute-deep-agent/status-builder-shared.js +10 -65
- package/dist/activities/execute-deep-agent/status-builder-shared.js.map +1 -1
- package/dist/activities/execute-deep-agent/streaming-side-effects.d.ts +10 -8
- package/dist/activities/execute-deep-agent/streaming-side-effects.js +12 -14
- package/dist/activities/execute-deep-agent/streaming-side-effects.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-tracker.d.ts +36 -13
- package/dist/activities/execute-deep-agent/subagent-tracker.js +76 -39
- package/dist/activities/execute-deep-agent/subagent-tracker.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-transformer.d.ts +25 -28
- package/dist/activities/execute-deep-agent/subagent-transformer.js +29 -82
- package/dist/activities/execute-deep-agent/subagent-transformer.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-wiring.d.ts +3 -2
- package/dist/activities/execute-deep-agent/subagent-wiring.js +2 -2
- package/dist/activities/execute-deep-agent/subagent-wiring.js.map +1 -1
- package/dist/activities/execute-deep-agent/turn-settle.d.ts +42 -0
- package/dist/activities/execute-deep-agent/turn-settle.js +146 -0
- package/dist/activities/execute-deep-agent/turn-settle.js.map +1 -0
- package/dist/activities/execute-deep-agent/turn-setup.d.ts +212 -0
- package/dist/activities/execute-deep-agent/turn-setup.js +430 -0
- package/dist/activities/execute-deep-agent/turn-setup.js.map +1 -0
- package/dist/activities/execute-deep-agent/turn-stream.d.ts +97 -0
- package/dist/activities/execute-deep-agent/turn-stream.js +242 -0
- package/dist/activities/execute-deep-agent/turn-stream.js.map +1 -0
- package/dist/activities/execute-deep-agent/turn.d.ts +42 -0
- package/dist/activities/execute-deep-agent/turn.js +116 -0
- package/dist/activities/execute-deep-agent/turn.js.map +1 -0
- package/dist/activities/execute-deep-agent/v3-event-recorder.d.ts +6 -5
- package/dist/activities/execute-deep-agent/v3-event-recorder.js +6 -5
- package/dist/activities/execute-deep-agent/v3-event-recorder.js.map +1 -1
- package/dist/activities/execute-deep-agent/v3-events.d.ts +1 -1
- package/dist/activities/execute-deep-agent/v3-events.js +1 -1
- package/dist/activities/execute-deep-agent/v3-status-builder.d.ts +60 -17
- package/dist/activities/execute-deep-agent/v3-status-builder.js +72 -57
- package/dist/activities/execute-deep-agent/v3-status-builder.js.map +1 -1
- package/dist/harness/approval-decisions.d.ts +61 -0
- package/dist/harness/approval-decisions.js +102 -0
- package/dist/harness/approval-decisions.js.map +1 -0
- package/dist/harness/capabilities.d.ts +16 -5
- package/dist/harness/capabilities.js +9 -0
- package/dist/harness/capabilities.js.map +1 -1
- package/dist/harness/capture.d.ts +144 -0
- package/dist/harness/capture.js +219 -0
- package/dist/harness/capture.js.map +1 -0
- package/dist/harness/persist-chokepoint.d.ts +19 -4
- package/dist/harness/persist-chokepoint.js +17 -4
- package/dist/harness/persist-chokepoint.js.map +1 -1
- package/dist/harness/run-turn.js +115 -29
- package/dist/harness/run-turn.js.map +1 -1
- package/dist/harness/terminal-table.d.ts +40 -11
- package/dist/harness/terminal-table.js +58 -19
- package/dist/harness/terminal-table.js.map +1 -1
- package/dist/harness/turn-context.d.ts +141 -76
- package/dist/harness/turn-context.js +194 -99
- package/dist/harness/turn-context.js.map +1 -1
- package/dist/harness/types.d.ts +65 -12
- package/dist/harness/types.js +8 -2
- package/dist/harness/types.js.map +1 -1
- package/dist/harness-adapters.d.ts +12 -8
- package/dist/harness-adapters.js +17 -9
- package/dist/harness-adapters.js.map +1 -1
- package/dist/middleware/approval-gate.d.ts +1 -1
- package/dist/middleware/approval-gate.js +1 -1
- package/dist/middleware/approval-gate.js.map +1 -1
- package/dist/middleware/cost-advisory.d.ts +39 -0
- package/dist/middleware/{cost-cap.js → cost-advisory.js} +37 -55
- package/dist/middleware/cost-advisory.js.map +1 -0
- package/dist/middleware/index.d.ts +23 -12
- package/dist/middleware/index.js +26 -18
- package/dist/middleware/index.js.map +1 -1
- package/dist/middleware/types.d.ts +3 -2
- package/dist/runner-manager.js +1 -5
- package/dist/runner-manager.js.map +1 -1
- package/dist/runner.js +1 -5
- package/dist/runner.js.map +1 -1
- package/dist/shared/attachment-resolver.d.ts +41 -18
- package/dist/shared/attachment-resolver.js +178 -46
- package/dist/shared/attachment-resolver.js.map +1 -1
- package/dist/shared/attachment-zip.d.ts +85 -0
- package/dist/shared/attachment-zip.js +185 -0
- package/dist/shared/attachment-zip.js.map +1 -0
- package/dist/shared/connect-backfill.js +5 -4
- package/dist/shared/connect-backfill.js.map +1 -1
- package/dist/shared/cost-guard.d.ts +3 -3
- package/dist/shared/cost-guard.js +3 -3
- package/dist/shared/execution-status-writer.d.ts +5 -5
- package/dist/shared/execution-status-writer.js +5 -5
- package/dist/shared/extract-structured-output.d.ts +8 -7
- package/dist/shared/extract-structured-output.js +8 -7
- package/dist/shared/extract-structured-output.js.map +1 -1
- package/dist/shared/filereview/cas-progress.d.ts +1 -18
- package/dist/shared/filereview/cas-progress.js +4 -0
- package/dist/shared/filereview/cas-progress.js.map +1 -1
- package/dist/shared/filereview/cas-touched.d.ts +56 -0
- package/dist/shared/filereview/cas-touched.js +63 -0
- package/dist/shared/filereview/cas-touched.js.map +1 -0
- package/dist/shared/filereview/index.d.ts +2 -1
- package/dist/shared/filereview/index.js +2 -1
- package/dist/shared/filereview/index.js.map +1 -1
- package/dist/shared/mcp-resolver.d.ts +4 -5
- package/dist/shared/mcp-resolver.js +4 -5
- package/dist/shared/mcp-resolver.js.map +1 -1
- package/dist/shared/persist-decision.d.ts +3 -4
- package/dist/shared/persist-decision.js +3 -4
- package/dist/shared/persist-decision.js.map +1 -1
- package/dist/shared/plan-mode-permissions.d.ts +2 -2
- package/dist/shared/plan-mode-permissions.js +2 -2
- package/dist/shared/plan-mode-prompt.d.ts +1 -1
- package/dist/shared/plan-mode-prompt.js +1 -1
- package/dist/shared/prompt-sections.d.ts +163 -0
- package/dist/shared/prompt-sections.js +164 -0
- package/dist/shared/prompt-sections.js.map +1 -0
- package/dist/shared/skill-resolver.d.ts +4 -2
- package/dist/shared/skill-resolver.js +4 -2
- package/dist/shared/skill-resolver.js.map +1 -1
- package/dist/shared/tool-rounds.d.ts +32 -0
- package/dist/shared/tool-rounds.js +43 -0
- package/dist/shared/tool-rounds.js.map +1 -1
- package/dist/shared/tool-row.d.ts +34 -0
- package/dist/shared/tool-row.js +52 -0
- package/dist/shared/tool-row.js.map +1 -1
- package/dist/shared/workspace/session-provision.d.ts +3 -3
- package/dist/shared/workspace/session-provision.js +3 -3
- package/dist/shared/workspace/stigmer-link.d.ts +3 -3
- package/dist/shared/workspace/stigmer-link.js +3 -3
- package/dist/shared/workspace/writeback-coordinator.d.ts +15 -22
- package/dist/shared/workspace/writeback-coordinator.js +16 -53
- package/dist/shared/workspace/writeback-coordinator.js.map +1 -1
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.js +1 -1
- package/package.json +4 -4
- package/src/__test-utils__/__tests__/harness-contract-self-check.test.ts +1 -0
- package/src/__test-utils__/execution-record-fixture.ts +18 -2
- package/src/__test-utils__/file-review-projection.ts +156 -0
- package/src/__test-utils__/git-workspace-fixture.ts +58 -0
- package/src/__test-utils__/harness-boot-order-child.ts +7 -2
- package/src/__test-utils__/harness-contract/contract.ts +33 -20
- package/src/__test-utils__/harness-contract/recording-sink.ts +11 -1
- package/src/__test-utils__/harness-contract/runtime-contract.ts +328 -10
- package/src/__test-utils__/harness-contract/scripted-adapter.ts +38 -6
- package/src/__test-utils__/harness-contract/types.ts +27 -3
- package/src/__test-utils__/hermetic-activity.ts +52 -14
- package/src/__test-utils__/model-registry-fixture.ts +32 -7
- package/src/__test-utils__/module-specifiers.ts +24 -0
- package/src/__test-utils__/turn-input-fixture.ts +2 -2
- package/src/__tests__/harness-boot-order.test.ts +9 -2
- package/src/activities/call-agent.ts +2 -2
- package/src/activities/execute-cursor/__test-utils__/contract-subject.ts +47 -3
- package/src/activities/execute-cursor/__test-utils__/hermetic-cursor.ts +2 -48
- package/src/activities/execute-cursor/__tests__/approval-decisions-agree.test.ts +1 -1
- package/src/activities/execute-cursor/__tests__/approval-gate.test.ts +6 -1
- package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +52 -27
- package/src/activities/execute-cursor/__tests__/cas-observations.test.ts +25 -0
- package/src/activities/execute-cursor/__tests__/goldens/prompt.enhanced.everything.prompt.md +166 -0
- package/src/activities/execute-cursor/__tests__/goldens/prompt.enhanced.plan-mode.prompt.md +186 -0
- package/src/activities/execute-cursor/__tests__/goldens/prompt.hitl.recovery.prompt.md +191 -0
- package/src/activities/execute-cursor/__tests__/goldens/prompt.hitl.reinvocation.prompt.md +13 -0
- package/src/activities/execute-cursor/__tests__/goldens/prompt.resumed.prefixed.prompt.md +32 -0
- package/src/activities/execute-cursor/__tests__/goldens/prompt.resumed.raw.prompt.md +1 -0
- package/src/activities/execute-cursor/__tests__/hermetic/file-review-capture.test.ts +1 -2
- package/src/activities/execute-cursor/__tests__/hermetic/goldens/sdk-error-at-create.status.json +4 -0
- package/src/activities/execute-cursor/__tests__/hermetic/harness-contract.test.ts +24 -4
- package/src/activities/execute-cursor/__tests__/hermetic/stream-self-stop-arms.test.ts +6 -5
- package/src/activities/execute-cursor/__tests__/hermetic/thrown-error-arms.test.ts +9 -0
- package/src/activities/execute-cursor/__tests__/hitl-resume-history.test.ts +11 -8
- package/src/activities/execute-cursor/__tests__/prompt-goldens.test.ts +229 -0
- package/src/activities/execute-cursor/__tests__/same-identity-reproposal.test.ts +3 -3
- package/src/activities/execute-cursor/__tests__/sequential-gate-resume.test.ts +2 -2
- package/src/activities/execute-cursor/__tests__/turn-boundary.test.ts +78 -234
- package/src/activities/execute-cursor/__tests__/turn-stream.test.ts +1 -26
- package/src/activities/execute-cursor/adapter.ts +7 -5
- package/src/activities/execute-cursor/approval-policy.ts +5 -2
- package/src/activities/execute-cursor/approval-state.ts +16 -8
- package/src/activities/execute-cursor/cas-observations.ts +20 -2
- package/src/activities/execute-cursor/cursor-capabilities.ts +24 -5
- package/src/activities/execute-cursor/message-translator.ts +29 -41
- package/src/activities/execute-cursor/prompt-builder.ts +99 -157
- package/src/activities/execute-cursor/turn-boundary.ts +34 -140
- package/src/activities/execute-cursor/turn-settle.ts +37 -51
- package/src/activities/execute-cursor/turn-setup.ts +30 -70
- package/src/activities/execute-cursor/turn-stream.ts +8 -31
- package/src/activities/execute-cursor/turn.ts +5 -1
- package/src/activities/execute-deep-agent/__test-utils__/contract-subject.ts +391 -0
- package/src/activities/execute-deep-agent/__test-utils__/hermetic-deep-agent.ts +313 -0
- package/src/activities/execute-deep-agent/__test-utils__/hitl-script.ts +48 -0
- package/src/activities/execute-deep-agent/__test-utils__/scripted-model-module.ts +85 -0
- package/src/activities/execute-deep-agent/__test-utils__/scripted-model.ts +383 -36
- package/src/activities/execute-deep-agent/__tests__/adapter-graph-is-sdk-free.test.ts +76 -0
- package/src/activities/execute-deep-agent/__tests__/adapter-is-temporal-free.test.ts +47 -0
- package/src/activities/execute-deep-agent/__tests__/adapter.test.ts +80 -0
- package/src/activities/execute-deep-agent/__tests__/cas-capture-backend.test.ts +1 -1
- package/src/activities/execute-deep-agent/__tests__/execution-state-extended.test.ts +0 -2
- package/src/activities/execute-deep-agent/__tests__/execution-state.test.ts +0 -3
- package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.build-from-plan.prompt.md +172 -0
- package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.everything.prompt.md +167 -0
- package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.minimal.prompt.md +36 -0
- package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.plan-mode.prompt.md +178 -0
- package/src/activities/execute-deep-agent/__tests__/goldens/system-prompt.skills-below-threshold.prompt.md +158 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/file-review-capture.test.ts +210 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve-all-lease.turn1.status.json +74 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve-all-lease.turn2.status.json +104 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/approve.turn2.status.json +78 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/file-review-capture.status.json +138 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/gate.turn1.status.json +68 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn1.status.json +154 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/inline-artifact.turn2.status.json +116 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/pause.loop.after-tool.status.json +69 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/pause.loop.mid-tool.status.json +67 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/plain-turn.status.json +47 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/platform-stop.status.json +68 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/recursion-limit.persisted.status.json +280 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/reject.turn2.status.json +77 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/resolution-error.status.json +18 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sequential-gates.turn2.status.json +99 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sequential-gates.turn3.status.json +109 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/skip.turn2.status.json +76 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/structured-output.text-fallback.status.json +51 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/structured-output.tier2.status.json +51 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/structured-output.tool-strategy.status.json +56 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/sub-agent-delegation.status.json +90 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/tool-call.status.json +70 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/goldens/worker-shutdown.status.json +71 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/harness-contract.test.ts +144 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-approve-all-lease.test.ts +184 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-round-trips.test.ts +203 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/hitl-sequential-gates.test.ts +170 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/inline-artifact-across-gate.test.ts +175 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/pause-vs-shutdown.test.ts +206 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/plain-turn.test.ts +142 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/platform-stop.test.ts +126 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/recursion-limit.test.ts +139 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/resolution-error.test.ts +122 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/structured-output.test.ts +236 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/sub-agent-delegation.test.ts +161 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/tool-call.test.ts +157 -0
- package/src/activities/execute-deep-agent/__tests__/hermetic/workspace-lock-timeout.test.ts +152 -0
- package/src/activities/execute-deep-agent/__tests__/hitl.test.ts +50 -122
- package/src/activities/execute-deep-agent/__tests__/inline-publisher.test.ts +8 -6
- package/src/activities/execute-deep-agent/__tests__/plan-mode-path-normalization.test.ts +2 -2
- package/src/activities/execute-deep-agent/__tests__/prompt-builder.test.ts +35 -35
- package/src/activities/execute-deep-agent/__tests__/prompt-goldens.test.ts +247 -0
- package/src/activities/execute-deep-agent/__tests__/scripted-model.test.ts +329 -0
- package/src/activities/execute-deep-agent/__tests__/status-builder-shared.test.ts +1 -20
- package/src/activities/execute-deep-agent/__tests__/streaming-side-effects.test.ts +78 -0
- package/src/activities/execute-deep-agent/__tests__/subagent-gitignored-capture.test.ts +1 -1
- package/src/activities/execute-deep-agent/__tests__/subagent-plan-mode-permissions.test.ts +2 -2
- package/src/activities/execute-deep-agent/__tests__/subagent-tracker.test.ts +57 -31
- package/src/activities/execute-deep-agent/__tests__/subagent-transformer.test.ts +19 -79
- package/src/activities/execute-deep-agent/__tests__/subagent-wiring.test.ts +15 -15
- package/src/activities/execute-deep-agent/__tests__/summarization-verification.test.ts +1 -1
- package/src/activities/execute-deep-agent/__tests__/turn-stream.test.ts +391 -0
- package/src/activities/execute-deep-agent/__tests__/v3-status-builder.test.ts +343 -38
- package/src/activities/execute-deep-agent/__tests__/vision-input.test.ts +5 -5
- package/src/activities/execute-deep-agent/adapter.ts +94 -0
- package/src/activities/execute-deep-agent/cas-capture-observer.ts +11 -0
- package/src/activities/execute-deep-agent/deep-agent-capabilities.ts +43 -0
- package/src/activities/execute-deep-agent/execution-state.ts +4 -13
- package/src/activities/execute-deep-agent/hitl.ts +100 -123
- package/src/activities/execute-deep-agent/prompt-builder.ts +141 -115
- package/src/activities/execute-deep-agent/status-builder-shared.ts +21 -84
- package/src/activities/execute-deep-agent/streaming-side-effects.ts +13 -19
- package/src/activities/execute-deep-agent/subagent-tracker.ts +77 -42
- package/src/activities/execute-deep-agent/subagent-transformer.ts +46 -105
- package/src/activities/execute-deep-agent/subagent-wiring.ts +5 -4
- package/src/activities/execute-deep-agent/turn-settle.ts +164 -0
- package/src/activities/execute-deep-agent/turn-setup.ts +541 -0
- package/src/activities/execute-deep-agent/turn-stream.ts +313 -0
- package/src/activities/execute-deep-agent/turn.ts +131 -0
- package/src/activities/execute-deep-agent/v3-event-recorder.ts +6 -5
- package/src/activities/execute-deep-agent/v3-events.ts +1 -1
- package/src/activities/execute-deep-agent/v3-status-builder.ts +84 -63
- package/src/harness/__tests__/approval-decisions.test.ts +116 -0
- package/src/harness/__tests__/capture.test.ts +268 -0
- package/src/harness/__tests__/mount-skills.test.ts +169 -0
- package/src/harness/__tests__/persist-chokepoint.test.ts +38 -2
- package/src/harness/__tests__/run-turn-deterministic.test.ts +94 -0
- package/src/harness/__tests__/run-turn.test.ts +39 -23
- package/src/harness/__tests__/turn-attachments-vision.test.ts +80 -0
- package/src/harness/__tests__/turn-context.test.ts +73 -26
- package/src/harness/approval-decisions.ts +104 -0
- package/src/harness/capabilities.ts +16 -5
- package/src/harness/capture.ts +301 -0
- package/src/harness/persist-chokepoint.ts +24 -4
- package/src/harness/run-turn.ts +120 -29
- package/src/harness/terminal-table.ts +61 -20
- package/src/harness/turn-context.ts +208 -111
- package/src/harness/types.ts +66 -12
- package/src/harness-adapters.ts +17 -9
- package/src/middleware/__tests__/approval-gate.test.ts +1 -1
- package/src/middleware/__tests__/cost-advisory.test.ts +113 -0
- package/src/middleware/approval-gate.ts +2 -2
- package/src/middleware/{cost-cap.ts → cost-advisory.ts} +40 -70
- package/src/middleware/index.ts +29 -29
- package/src/middleware/types.ts +3 -2
- package/src/runner-manager.ts +0 -8
- package/src/runner.ts +0 -8
- package/src/shared/__tests__/attachment-naming.test.ts +6 -19
- package/src/shared/__tests__/attachment-resolver.test.ts +158 -8
- package/src/shared/__tests__/attachment-zip.test.ts +236 -0
- package/src/shared/__tests__/bedrock-adapter.test.ts +1 -1
- package/src/shared/__tests__/blueprint-resolver.test.ts +43 -0
- package/src/shared/__tests__/cost-guard.test.ts +3 -3
- package/src/shared/__tests__/prompt-sections.test.ts +167 -0
- package/src/shared/__tests__/secret-leak-scan.test.ts +2 -2
- package/src/{activities/execute-deep-agent → shared}/__tests__/stamp-flowed-rows.test.ts +33 -10
- package/src/shared/__tests__/vertex-adapter.test.ts +1 -1
- package/src/shared/attachment-resolver.ts +218 -55
- package/src/shared/attachment-zip.ts +281 -0
- package/src/shared/connect-backfill.ts +9 -11
- package/src/shared/cost-guard.ts +3 -3
- package/src/shared/execution-status-writer.ts +5 -5
- package/src/shared/extract-structured-output.ts +8 -7
- package/src/shared/filereview/__tests__/capture.test.ts +86 -4
- package/src/shared/filereview/__tests__/cas-progress.test.ts +2 -1
- package/src/shared/filereview/cas-progress.ts +5 -19
- package/src/shared/filereview/cas-touched.ts +89 -0
- package/src/shared/filereview/index.ts +4 -2
- package/src/shared/mcp-resolver.ts +4 -5
- package/src/shared/persist-decision.ts +3 -4
- package/src/shared/plan-mode-permissions.ts +2 -2
- package/src/shared/plan-mode-prompt.ts +1 -1
- package/src/shared/prompt-sections.ts +256 -0
- package/src/shared/skill-resolver.ts +4 -2
- package/src/shared/tool-rounds.ts +48 -0
- package/src/shared/tool-row.ts +61 -0
- package/src/shared/workspace/__tests__/stigmer-link.test.ts +7 -22
- package/src/shared/workspace/__tests__/writeback-coordinator.test.ts +20 -61
- package/src/shared/workspace/session-provision.ts +3 -3
- package/src/shared/workspace/stigmer-link.ts +3 -3
- package/src/shared/workspace/writeback-coordinator.ts +16 -64
- package/src/tools/index.ts +1 -1
- package/dist/activities/execute-cursor/capture-flow.d.ts +0 -142
- package/dist/activities/execute-cursor/capture-flow.js +0 -285
- package/dist/activities/execute-cursor/capture-flow.js.map +0 -1
- package/dist/activities/execute-cursor/command-provenance.d.ts +0 -48
- package/dist/activities/execute-cursor/command-provenance.js +0 -38
- package/dist/activities/execute-cursor/command-provenance.js.map +0 -1
- package/dist/activities/execute-deep-agent/attachment-injector.d.ts +0 -134
- package/dist/activities/execute-deep-agent/attachment-injector.js +0 -386
- package/dist/activities/execute-deep-agent/attachment-injector.js.map +0 -1
- package/dist/activities/execute-deep-agent/command-provenance.d.ts +0 -61
- package/dist/activities/execute-deep-agent/command-provenance.js +0 -72
- package/dist/activities/execute-deep-agent/command-provenance.js.map +0 -1
- package/dist/activities/execute-deep-agent/environment.d.ts +0 -24
- package/dist/activities/execute-deep-agent/environment.js +0 -58
- package/dist/activities/execute-deep-agent/environment.js.map +0 -1
- package/dist/activities/execute-deep-agent/event-recorder.d.ts +0 -21
- package/dist/activities/execute-deep-agent/event-recorder.js +0 -67
- package/dist/activities/execute-deep-agent/event-recorder.js.map +0 -1
- package/dist/activities/execute-deep-agent/index.d.ts +0 -15
- package/dist/activities/execute-deep-agent/index.js +0 -918
- package/dist/activities/execute-deep-agent/index.js.map +0 -1
- package/dist/activities/execute-deep-agent/mcp-gate.d.ts +0 -28
- package/dist/activities/execute-deep-agent/mcp-gate.js +0 -22
- package/dist/activities/execute-deep-agent/mcp-gate.js.map +0 -1
- package/dist/activities/execute-deep-agent/post-stream.d.ts +0 -23
- package/dist/activities/execute-deep-agent/post-stream.js +0 -71
- package/dist/activities/execute-deep-agent/post-stream.js.map +0 -1
- package/dist/activities/execute-deep-agent/setup.d.ts +0 -136
- package/dist/activities/execute-deep-agent/setup.js +0 -767
- package/dist/activities/execute-deep-agent/setup.js.map +0 -1
- package/dist/activities/execute-deep-agent/stamp-flowed-rows.d.ts +0 -36
- package/dist/activities/execute-deep-agent/stamp-flowed-rows.js +0 -56
- package/dist/activities/execute-deep-agent/stamp-flowed-rows.js.map +0 -1
- package/dist/activities/execute-deep-agent/status-builder.d.ts +0 -93
- package/dist/activities/execute-deep-agent/status-builder.js +0 -365
- package/dist/activities/execute-deep-agent/status-builder.js.map +0 -1
- package/dist/activities/execute-deep-agent/streaming-terminal.d.ts +0 -21
- package/dist/activities/execute-deep-agent/streaming-terminal.js +0 -80
- package/dist/activities/execute-deep-agent/streaming-terminal.js.map +0 -1
- package/dist/activities/execute-deep-agent/streaming-v3.d.ts +0 -13
- package/dist/activities/execute-deep-agent/streaming-v3.js +0 -174
- package/dist/activities/execute-deep-agent/streaming-v3.js.map +0 -1
- package/dist/activities/execute-deep-agent/streaming.d.ts +0 -81
- package/dist/activities/execute-deep-agent/streaming.js +0 -160
- package/dist/activities/execute-deep-agent/streaming.js.map +0 -1
- package/dist/middleware/cost-cap.d.ts +0 -22
- package/dist/middleware/cost-cap.js.map +0 -1
- package/dist/middleware/graceful-stop.d.ts +0 -17
- package/dist/middleware/graceful-stop.js +0 -63
- package/dist/middleware/graceful-stop.js.map +0 -1
- package/dist/shared/skill-writer.d.ts +0 -75
- package/dist/shared/skill-writer.js +0 -207
- package/dist/shared/skill-writer.js.map +0 -1
- package/src/activities/execute-cursor/__tests__/capture-flow.test.ts +0 -1032
- package/src/activities/execute-cursor/__tests__/command-provenance.test.ts +0 -240
- package/src/activities/execute-cursor/__tests__/progress-substrate.test.ts +0 -169
- package/src/activities/execute-cursor/capture-flow.ts +0 -379
- package/src/activities/execute-cursor/command-provenance.ts +0 -73
- package/src/activities/execute-deep-agent/__tests__/attachment-injector.test.ts +0 -1161
- package/src/activities/execute-deep-agent/__tests__/command-provenance.test.ts +0 -252
- package/src/activities/execute-deep-agent/__tests__/environment.test.ts +0 -108
- package/src/activities/execute-deep-agent/__tests__/event-recorder.test.ts +0 -150
- package/src/activities/execute-deep-agent/__tests__/hitl-integration.test.ts +0 -226
- package/src/activities/execute-deep-agent/__tests__/hitl-reject.test.ts +0 -318
- package/src/activities/execute-deep-agent/__tests__/hitl-resume-approve-all.test.ts +0 -351
- package/src/activities/execute-deep-agent/__tests__/hitl-resume-history.test.ts +0 -293
- package/src/activities/execute-deep-agent/__tests__/index.test.ts +0 -99
- package/src/activities/execute-deep-agent/__tests__/mcp-gate.test.ts +0 -44
- package/src/activities/execute-deep-agent/__tests__/post-stream.test.ts +0 -112
- package/src/activities/execute-deep-agent/__tests__/sequential-gate-resume.test.ts +0 -358
- package/src/activities/execute-deep-agent/__tests__/status-builder.test.ts +0 -1933
- package/src/activities/execute-deep-agent/__tests__/streaming-terminal.test.ts +0 -40
- package/src/activities/execute-deep-agent/__tests__/streaming-v3.test.ts +0 -597
- package/src/activities/execute-deep-agent/__tests__/streaming.test.ts +0 -508
- package/src/activities/execute-deep-agent/attachment-injector.ts +0 -627
- package/src/activities/execute-deep-agent/command-provenance.ts +0 -102
- package/src/activities/execute-deep-agent/environment.ts +0 -76
- package/src/activities/execute-deep-agent/event-recorder.ts +0 -95
- package/src/activities/execute-deep-agent/index.ts +0 -1072
- package/src/activities/execute-deep-agent/mcp-gate.ts +0 -37
- package/src/activities/execute-deep-agent/post-stream.ts +0 -109
- package/src/activities/execute-deep-agent/setup.ts +0 -1081
- package/src/activities/execute-deep-agent/stamp-flowed-rows.ts +0 -64
- package/src/activities/execute-deep-agent/status-builder.ts +0 -481
- package/src/activities/execute-deep-agent/streaming-terminal.ts +0 -106
- package/src/activities/execute-deep-agent/streaming-v3.ts +0 -277
- package/src/activities/execute-deep-agent/streaming.ts +0 -313
- package/src/middleware/__tests__/cost-cap.test.ts +0 -192
- package/src/middleware/__tests__/graceful-stop.test.ts +0 -105
- package/src/middleware/graceful-stop.ts +0 -86
- package/src/shared/__tests__/skill-writer.test.ts +0 -372
- package/src/shared/skill-writer.ts +0 -266
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hermetic goldens: STRUCTURED OUTPUT — an execution whose config carries a
|
|
3
|
+
* JSON schema, through the three paths a native turn has:
|
|
4
|
+
*
|
|
5
|
+
* 1. The engine's `structuredResponse`. langchain 1.4.1 turns a bare schema
|
|
6
|
+
* into `toolStrategy` when the model's profile does not declare native
|
|
7
|
+
* structured output (`agents/responses.js`): a synthetic function tool is
|
|
8
|
+
* bound (named `extract-N` from a per-module counter — deepagents passes
|
|
9
|
+
* no name and the schema's `title` is not consulted, so the script selects
|
|
10
|
+
* it by pattern rather than by name), the model calls it with the answer
|
|
11
|
+
* as arguments, and the graph's `run.output.structuredResponse` holds the
|
|
12
|
+
* parsed object. The adapter folds it onto the status (`turn-settle.ts`).
|
|
13
|
+
* 2. The text fallback (the runtime's tier 1): the model answers in prose
|
|
14
|
+
* with a fenced JSON block and `shared/extract-json.ts` recovers the
|
|
15
|
+
* object from the final text (`run-turn.ts` `extractStructuredOutputFromText`).
|
|
16
|
+
* 3. The LLM fallback (the runtime's tier 2, Q-S3-7): the final text carries
|
|
17
|
+
* no JSON at all, so the runtime asks a second model — the registry's
|
|
18
|
+
* economy tier for the primary's provider — to extract it through
|
|
19
|
+
* `withStructuredOutput` (`shared/extract-structured-output.ts`). Native
|
|
20
|
+
* gained this arm at S3 M2a, when the turn runtime took the epilogue
|
|
21
|
+
* over; it is pinned here since M2b (F-M2b-14), where every other ruled
|
|
22
|
+
* alignment is. Two facts the golden records: the extraction call is a
|
|
23
|
+
* SECOND model build (`maxTokens: 4096`, on the primary — the fixture
|
|
24
|
+
* registry declares no economy row, `getEconomyModel`'s last resort),
|
|
25
|
+
* and its tool call never reaches the transcript (it is not a graph
|
|
26
|
+
* step). The arm runs in the OSS direct-credential posture
|
|
27
|
+
* (`ANTHROPIC_API_KEY` stubbed for the arm only): without a proxy the
|
|
28
|
+
* runtime refuses tier 2 when no credential path exists, and that
|
|
29
|
+
* refusal is a log line, never a lost run.
|
|
30
|
+
*
|
|
31
|
+
* Whatever each path yields is recorded as found: `status.structuredOutput`
|
|
32
|
+
* and the slim's `structured`.
|
|
33
|
+
*
|
|
34
|
+
* The `extract-N` name is deterministic per test FILE (vitest isolates each
|
|
35
|
+
* file's module graph, so the counter starts at 1), and only the tool-strategy
|
|
36
|
+
* arm compiles a graph with a schema tool in this file, so its row — if one
|
|
37
|
+
* is persisted — is `extract-1`. Tier 2's tool is `@langchain/core`'s default
|
|
38
|
+
* `extract` (`withStructuredOutput` with no name), bound on the extraction
|
|
39
|
+
* model alone, which is how the script tells the two calls apart.
|
|
40
|
+
*
|
|
41
|
+
* Since S3 M2a the activity is the turn runtime over the native adapter, and
|
|
42
|
+
* `streamingUsage` carries the runtime accountant's fields — `model`,
|
|
43
|
+
* `estimatedCostUsd` priced at the registry's rates, the requested tier and
|
|
44
|
+
* thinking mode (Q-M2a-2: one writer of the summary, the runtime). Nothing
|
|
45
|
+
* else in this golden moved with the flip.
|
|
46
|
+
*
|
|
47
|
+
* Regenerate ONLY after a deliberate behavior change:
|
|
48
|
+
* npx vitest run src/activities/execute-deep-agent/__tests__/hermetic -u
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
52
|
+
import { toJson } from "@bufbuild/protobuf";
|
|
53
|
+
import { AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
54
|
+
import { ExecutionPhase } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
55
|
+
|
|
56
|
+
vi.mock("../../../../shared/model-client.js", async () =>
|
|
57
|
+
(await import("../../__test-utils__/scripted-model-module.js")).scriptedModelClientModule(),
|
|
58
|
+
);
|
|
59
|
+
vi.mock("../../../../client/stigmer-client.js", async () =>
|
|
60
|
+
(await import("../../../../__test-utils__/hermetic-activity.js")).hermeticStigmerClientModule(),
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
import {
|
|
64
|
+
ScriptedClock,
|
|
65
|
+
createHermeticEnvironment,
|
|
66
|
+
type HermeticEnvironment,
|
|
67
|
+
} from "../../../../__test-utils__/hermetic-activity.js";
|
|
68
|
+
import { stubRegistryFetch } from "../../../../__test-utils__/model-registry-fixture.js";
|
|
69
|
+
import {
|
|
70
|
+
beginDeepAgentScenario,
|
|
71
|
+
deepAgentExecutionRecord,
|
|
72
|
+
runDeepAgentTurn,
|
|
73
|
+
} from "../../__test-utils__/hermetic-deep-agent.js";
|
|
74
|
+
import { recordedModelBuilds } from "../../__test-utils__/scripted-model-module.js";
|
|
75
|
+
import { FIXTURE_NATIVE_MODEL } from "../../../../__test-utils__/model-registry-fixture.js";
|
|
76
|
+
|
|
77
|
+
const SCHEMA = {
|
|
78
|
+
type: "object",
|
|
79
|
+
properties: { answer: { type: "string" }, score: { type: "number" } },
|
|
80
|
+
required: ["answer", "score"],
|
|
81
|
+
};
|
|
82
|
+
const ANSWER = { answer: "forty-two", score: 42 };
|
|
83
|
+
const SCHEMA_TOOL_PATTERN = /^extract-\d+$/;
|
|
84
|
+
|
|
85
|
+
describe("ExecuteDeepAgent hermetic — structured output", () => {
|
|
86
|
+
let env: HermeticEnvironment;
|
|
87
|
+
let registry: ReturnType<typeof stubRegistryFetch>;
|
|
88
|
+
const clock = new ScriptedClock();
|
|
89
|
+
|
|
90
|
+
beforeAll(() => {
|
|
91
|
+
env = createHermeticEnvironment();
|
|
92
|
+
registry = stubRegistryFetch();
|
|
93
|
+
clock.install();
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
afterAll(() => {
|
|
97
|
+
clock.uninstall();
|
|
98
|
+
registry.restore();
|
|
99
|
+
env.dispose();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("tool strategy: the model calls the bound schema tool", async () => {
|
|
103
|
+
// ── Arrange ──────────────────────────────────────────────────────────────
|
|
104
|
+
clock.reset();
|
|
105
|
+
const record = deepAgentExecutionRecord({ message: "Give me the answer.", structuredOutputSchema: SCHEMA });
|
|
106
|
+
const boundSchemaTools: string[] = [];
|
|
107
|
+
const scenario = beginDeepAgentScenario({
|
|
108
|
+
env,
|
|
109
|
+
clock,
|
|
110
|
+
record,
|
|
111
|
+
script: (tools) => {
|
|
112
|
+
const schemaTool = tools.find((t) => SCHEMA_TOOL_PATTERN.test(t));
|
|
113
|
+
if (schemaTool) boundSchemaTools.push(schemaTool);
|
|
114
|
+
return {
|
|
115
|
+
turns: [
|
|
116
|
+
{
|
|
117
|
+
text: "Here is the structured answer.",
|
|
118
|
+
toolCalls: schemaTool ? [{ id: "call-hermetic-extract-0001", name: schemaTool, args: ANSWER }] : [],
|
|
119
|
+
usage: { inputTokens: 1_300, outputTokens: 40 },
|
|
120
|
+
},
|
|
121
|
+
{ text: "Recorded.", usage: { inputTokens: 1_500, outputTokens: 10 } },
|
|
122
|
+
],
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// ── Act ──────────────────────────────────────────────────────────────────
|
|
128
|
+
const invocation = await runDeepAgentTurn(scenario);
|
|
129
|
+
|
|
130
|
+
// ── Assert ───────────────────────────────────────────────────────────────
|
|
131
|
+
expect(invocation.outcome.kind).toBe("returned");
|
|
132
|
+
const slim = (invocation.outcome as { value: Record<string, unknown> }).value;
|
|
133
|
+
expect(slim.phase).toBe("EXECUTION_COMPLETED");
|
|
134
|
+
expect(boundSchemaTools[0], "langchain bound the toolStrategy schema tool").toBe("extract-1");
|
|
135
|
+
expect(record.persistedPhases.at(-1)).toBe(ExecutionPhase.EXECUTION_COMPLETED);
|
|
136
|
+
const final = record.lastFullStatus!;
|
|
137
|
+
expect(final.structuredOutput, "the engine's structuredResponse reaches the status").toEqual(ANSWER);
|
|
138
|
+
expect(slim.structured).toEqual(ANSWER);
|
|
139
|
+
|
|
140
|
+
const json = JSON.stringify(toJson(AgentExecutionStatusSchema, final), null, 2) + "\n";
|
|
141
|
+
await expect(json).toMatchFileSnapshot("./goldens/structured-output.tool-strategy.status.json");
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("text fallback: a fenced JSON block in the final text", async () => {
|
|
145
|
+
// ── Arrange ──────────────────────────────────────────────────────────────
|
|
146
|
+
clock.reset();
|
|
147
|
+
const record = deepAgentExecutionRecord({ message: "Give me the answer.", structuredOutputSchema: SCHEMA });
|
|
148
|
+
const scenario = beginDeepAgentScenario({
|
|
149
|
+
env,
|
|
150
|
+
clock,
|
|
151
|
+
record,
|
|
152
|
+
script: () => ({
|
|
153
|
+
turns: [
|
|
154
|
+
{
|
|
155
|
+
text: "Here you go:\n```json\n" + JSON.stringify(ANSWER) + "\n```",
|
|
156
|
+
usage: { inputTokens: 1_300, outputTokens: 40 },
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
}),
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// ── Act ──────────────────────────────────────────────────────────────────
|
|
163
|
+
const invocation = await runDeepAgentTurn(scenario);
|
|
164
|
+
|
|
165
|
+
// ── Assert ───────────────────────────────────────────────────────────────
|
|
166
|
+
expect(invocation.outcome.kind).toBe("returned");
|
|
167
|
+
const slim = (invocation.outcome as { value: Record<string, unknown> }).value;
|
|
168
|
+
expect(slim.phase).toBe("EXECUTION_COMPLETED");
|
|
169
|
+
const final = record.lastFullStatus!;
|
|
170
|
+
expect(final.structuredOutput, "extracted from the final text").toEqual(ANSWER);
|
|
171
|
+
expect(slim.structured).toEqual(ANSWER);
|
|
172
|
+
expect(record.toolCalls(), "no tool was involved").toHaveLength(0);
|
|
173
|
+
|
|
174
|
+
expect(registry.urls.every((u) => u.includes("/model-registry"))).toBe(true);
|
|
175
|
+
const json = JSON.stringify(toJson(AgentExecutionStatusSchema, final), null, 2) + "\n";
|
|
176
|
+
await expect(json).toMatchFileSnapshot("./goldens/structured-output.text-fallback.status.json");
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("LLM fallback (tier 2): prose with no JSON is extracted by a second model call the transcript never sees (Q-S3-7)", async () => {
|
|
180
|
+
// ── Arrange ──────────────────────────────────────────────────────────────
|
|
181
|
+
clock.reset();
|
|
182
|
+
// The OSS direct-credential posture, for this arm only: with no proxy the
|
|
183
|
+
// runtime refuses tier 2 unless a credential path exists.
|
|
184
|
+
vi.stubEnv("ANTHROPIC_API_KEY", "hermetic-tier2-credential");
|
|
185
|
+
const PROSE = "The answer is forty-two, and I would score it a perfect forty-two out of the same.";
|
|
186
|
+
const record = deepAgentExecutionRecord({ message: "Give me the answer.", structuredOutputSchema: SCHEMA });
|
|
187
|
+
const extractionCalls: string[][] = [];
|
|
188
|
+
const scenario = beginDeepAgentScenario({
|
|
189
|
+
env,
|
|
190
|
+
clock,
|
|
191
|
+
record,
|
|
192
|
+
script: (tools) => {
|
|
193
|
+
// The extraction model binds exactly one tool, core's default `extract`;
|
|
194
|
+
// the agent's graph never binds a tool of that name.
|
|
195
|
+
if (tools.length === 1 && tools[0] === "extract") {
|
|
196
|
+
extractionCalls.push([...tools]);
|
|
197
|
+
return { turns: [{ text: "", toolCalls: [{ id: "call-hermetic-tier2-0001", name: "extract", args: ANSWER }] }] };
|
|
198
|
+
}
|
|
199
|
+
return { turns: [{ text: PROSE, usage: { inputTokens: 1_300, outputTokens: 40 } }] };
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
// ── Act ────────────────────────────────────────────────────────────────
|
|
205
|
+
const invocation = await runDeepAgentTurn(scenario);
|
|
206
|
+
|
|
207
|
+
// ── Assert ─────────────────────────────────────────────────────────────
|
|
208
|
+
expect(invocation.outcome.kind).toBe("returned");
|
|
209
|
+
const slim = (invocation.outcome as { value: Record<string, unknown> }).value;
|
|
210
|
+
expect(slim.phase).toBe("EXECUTION_COMPLETED");
|
|
211
|
+
const final = record.lastFullStatus!;
|
|
212
|
+
expect(final.structuredOutput, "extracted by the second model").toEqual(ANSWER);
|
|
213
|
+
expect(slim.structured).toEqual(ANSWER);
|
|
214
|
+
expect(record.toolCalls(), "the extraction tool call is not a graph step and never reaches the transcript").toHaveLength(0);
|
|
215
|
+
expect(extractionCalls, "the extractor was asked exactly once").toHaveLength(1);
|
|
216
|
+
|
|
217
|
+
// The agent's build, the sub-agent factory's builds (every registered
|
|
218
|
+
// profile sub-agent is compiled, invoked or not), then — last, after the
|
|
219
|
+
// graph has ended — the extractor's: the one build that caps its output
|
|
220
|
+
// (`maxTokens: 4096`) and carries no execution header scope.
|
|
221
|
+
const builds = recordedModelBuilds();
|
|
222
|
+
const extractorBuilds = builds.filter((b) => b.maxTokens === 4096);
|
|
223
|
+
expect(extractorBuilds, "exactly one extraction build").toHaveLength(1);
|
|
224
|
+
expect(builds.at(-1), "the extractor is built after the graph's run, never before").toBe(extractorBuilds[0]);
|
|
225
|
+
expect(extractorBuilds[0]!.headerScope).toBeUndefined();
|
|
226
|
+
// The fixture registry declares no economy row, so `getEconomyModel`
|
|
227
|
+
// falls back to the primary (its last resort).
|
|
228
|
+
expect(extractorBuilds[0]!.modelName).toBe(FIXTURE_NATIVE_MODEL);
|
|
229
|
+
|
|
230
|
+
const json = JSON.stringify(toJson(AgentExecutionStatusSchema, final), null, 2) + "\n";
|
|
231
|
+
await expect(json).toMatchFileSnapshot("./goldens/structured-output.tier2.status.json");
|
|
232
|
+
} finally {
|
|
233
|
+
vi.unstubAllEnvs();
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
});
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hermetic golden: SUB-AGENT DELEGATION — the root agent hands a task to a
|
|
3
|
+
* declared sub-agent through deepagents' `task` tool, and the sub-agent's
|
|
4
|
+
* own turn is tracked as a `SubAgentExecution`.
|
|
5
|
+
*
|
|
6
|
+
* Invariant pinned: `transformAndCompileSubagents` compiles the agent's
|
|
7
|
+
* declared sub-agent (plus deepagents' built-in `general-purpose` and the
|
|
8
|
+
* runner's `shell`) on the SAME model double through `modelFactory`; the root
|
|
9
|
+
* proposes `task({ subagent_type, description })`; the tools node runs the
|
|
10
|
+
* sub-agent graph under a nested namespace (`tools:<uuid>|...`); the
|
|
11
|
+
* `SubAgentTracker` opens a `SubAgentExecution` keyed by the `task` call id
|
|
12
|
+
* (not by the namespace uuid, which never reaches the status), routes the
|
|
13
|
+
* nested model events into its own transcript, and closes it COMPLETED when
|
|
14
|
+
* the `task` tool finishes; the root's transcript carries the `task` row and
|
|
15
|
+
* its closing text. The golden (`goldens/sub-agent-delegation.status.json`)
|
|
16
|
+
* pins the nested transcript shape S4 must reproduce.
|
|
17
|
+
*
|
|
18
|
+
* Role selection: `createDeepAgent` gives the sub-agent the same tool set as
|
|
19
|
+
* its parent (`task` included), so the script tells the roles apart by the
|
|
20
|
+
* system prompt (`ScriptRoleContext.systemPrompt`), which carries the
|
|
21
|
+
* sub-agent's own instructions — the one thing that differs.
|
|
22
|
+
*
|
|
23
|
+
* Since S3 M2a the activity is the turn runtime over the native adapter, and
|
|
24
|
+
* `streamingUsage` carries the runtime accountant's fields — `model`,
|
|
25
|
+
* `estimatedCostUsd` priced at the registry's rates, the requested tier and
|
|
26
|
+
* thinking mode (Q-M2a-2: one writer of the summary, the runtime). Nothing
|
|
27
|
+
* else in this golden moved with the flip.
|
|
28
|
+
*
|
|
29
|
+
* Since S3 M2b (Q-M2b-1, owner ruling 2026-09-13) the sub-agent's spend is
|
|
30
|
+
* the execution's spend: the helper's one turn (600 in, 12 out) is reported
|
|
31
|
+
* through the same hook as the root's, so `streamingUsage` reads 3,900 in /
|
|
32
|
+
* 102 out / 3 turns where it read 3,300 / 90 / 2, and `estimatedCostUsd`
|
|
33
|
+
* 0.00441 where it read 0.00375 — the helper's 660 micro-dollars at the
|
|
34
|
+
* PARENT's rate (per-sub-agent pricing is an S5 item; the in-graph cost cap
|
|
35
|
+
* this replaces priced sub-agents the same way). The five lines of
|
|
36
|
+
* `streamingUsage` are the whole diff; the transcript and the sub-agent row
|
|
37
|
+
* are byte-identical. `max_cost_usd` is now enforced over this total by the
|
|
38
|
+
* runtime, the one enforcement since the cost middleware became advisory
|
|
39
|
+
* (Q-S3-3).
|
|
40
|
+
*
|
|
41
|
+
* Regenerate ONLY after a deliberate behavior change:
|
|
42
|
+
* npx vitest run src/activities/execute-deep-agent/__tests__/hermetic -u
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
46
|
+
import { create, toJson } from "@bufbuild/protobuf";
|
|
47
|
+
import { AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
48
|
+
import {
|
|
49
|
+
ExecutionPhase,
|
|
50
|
+
MessageType,
|
|
51
|
+
SubAgentStatus,
|
|
52
|
+
ToolCallStatus,
|
|
53
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
54
|
+
import { SubAgentSchema } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/spec_pb";
|
|
55
|
+
|
|
56
|
+
vi.mock("../../../../shared/model-client.js", async () =>
|
|
57
|
+
(await import("../../__test-utils__/scripted-model-module.js")).scriptedModelClientModule(),
|
|
58
|
+
);
|
|
59
|
+
vi.mock("../../../../client/stigmer-client.js", async () =>
|
|
60
|
+
(await import("../../../../__test-utils__/hermetic-activity.js")).hermeticStigmerClientModule(),
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
import {
|
|
64
|
+
ScriptedClock,
|
|
65
|
+
createHermeticEnvironment,
|
|
66
|
+
type HermeticEnvironment,
|
|
67
|
+
} from "../../../../__test-utils__/hermetic-activity.js";
|
|
68
|
+
import { stubRegistryFetch } from "../../../../__test-utils__/model-registry-fixture.js";
|
|
69
|
+
import {
|
|
70
|
+
beginDeepAgentScenario,
|
|
71
|
+
deepAgentExecutionRecord,
|
|
72
|
+
runDeepAgentTurn,
|
|
73
|
+
} from "../../__test-utils__/hermetic-deep-agent.js";
|
|
74
|
+
import { recordedModelBuilds } from "../../__test-utils__/scripted-model-module.js";
|
|
75
|
+
|
|
76
|
+
const HELPER_INSTRUCTIONS = "You are the hermetic helper. Answer in one line.";
|
|
77
|
+
const TASK_CALL_ID = "call-hermetic-task-0001";
|
|
78
|
+
const TASK_DESCRIPTION = "Look up the fixture value.";
|
|
79
|
+
const HELPER_ANSWER = "The fixture value is forty-two.";
|
|
80
|
+
const ROOT_CLOSING = "The helper reports the fixture value is forty-two.";
|
|
81
|
+
|
|
82
|
+
describe("ExecuteDeepAgent hermetic — sub-agent delegation", () => {
|
|
83
|
+
let env: HermeticEnvironment;
|
|
84
|
+
let registry: ReturnType<typeof stubRegistryFetch>;
|
|
85
|
+
const clock = new ScriptedClock();
|
|
86
|
+
|
|
87
|
+
beforeAll(() => {
|
|
88
|
+
env = createHermeticEnvironment();
|
|
89
|
+
registry = stubRegistryFetch();
|
|
90
|
+
clock.install();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
afterAll(() => {
|
|
94
|
+
clock.uninstall();
|
|
95
|
+
registry.restore();
|
|
96
|
+
env.dispose();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("tracks the delegated turn as a SubAgentExecution keyed by the task call and completes", async () => {
|
|
100
|
+
// ── Arrange ──────────────────────────────────────────────────────────────
|
|
101
|
+
const helper = create(SubAgentSchema, {
|
|
102
|
+
name: "helper",
|
|
103
|
+
description: "Looks up fixture values.",
|
|
104
|
+
instructions: HELPER_INSTRUCTIONS,
|
|
105
|
+
});
|
|
106
|
+
const record = deepAgentExecutionRecord({ message: "Ask the helper for the fixture value.", subAgents: [helper] });
|
|
107
|
+
const scenario = beginDeepAgentScenario({
|
|
108
|
+
env,
|
|
109
|
+
clock,
|
|
110
|
+
record,
|
|
111
|
+
script: (_tools, context) =>
|
|
112
|
+
context.systemPrompt.includes(HELPER_INSTRUCTIONS)
|
|
113
|
+
? { turns: [{ text: HELPER_ANSWER, usage: { inputTokens: 600, outputTokens: 12 } }] }
|
|
114
|
+
: {
|
|
115
|
+
turns: [
|
|
116
|
+
{
|
|
117
|
+
text: "Delegating to the helper.",
|
|
118
|
+
toolCalls: [{ id: TASK_CALL_ID, name: "task", args: { description: TASK_DESCRIPTION, subagent_type: "helper" } }],
|
|
119
|
+
usage: { inputTokens: 1_500, outputTokens: 60 },
|
|
120
|
+
},
|
|
121
|
+
{ text: ROOT_CLOSING, usage: { inputTokens: 1_800, outputTokens: 30 } },
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// ── Act ──────────────────────────────────────────────────────────────────
|
|
127
|
+
const invocation = await runDeepAgentTurn(scenario);
|
|
128
|
+
|
|
129
|
+
// ── Assert: outcome ──────────────────────────────────────────────────────
|
|
130
|
+
expect(invocation.outcome.kind).toBe("returned");
|
|
131
|
+
expect((invocation.outcome as { value: Record<string, unknown> }).value.phase).toBe("EXECUTION_COMPLETED");
|
|
132
|
+
expect(record.persistedPhases.at(-1)).toBe(ExecutionPhase.EXECUTION_COMPLETED);
|
|
133
|
+
|
|
134
|
+
// ── Assert: the root transcript ──────────────────────────────────────────
|
|
135
|
+
const final = record.lastFullStatus!;
|
|
136
|
+
const rows = record.toolCalls();
|
|
137
|
+
expect(rows.map((tc) => [tc.id, tc.name, tc.status])).toEqual([[TASK_CALL_ID, "task", ToolCallStatus.TOOL_CALL_COMPLETED]]);
|
|
138
|
+
expect(final.messages.filter((m) => m.type === MessageType.MESSAGE_AI).map((m) => m.content)).toEqual([
|
|
139
|
+
"Delegating to the helper.",
|
|
140
|
+
"",
|
|
141
|
+
ROOT_CLOSING,
|
|
142
|
+
]);
|
|
143
|
+
|
|
144
|
+
// ── Assert: the sub-agent execution ──────────────────────────────────────
|
|
145
|
+
expect(final.subAgentExecutions).toHaveLength(1);
|
|
146
|
+
const sub = final.subAgentExecutions[0];
|
|
147
|
+
expect(sub.id, "keyed by the task call id, never by the namespace uuid").toBe(TASK_CALL_ID);
|
|
148
|
+
expect(sub.name).toBe("helper");
|
|
149
|
+
expect(sub.subject).toBe(TASK_DESCRIPTION);
|
|
150
|
+
expect(sub.status).toBe(SubAgentStatus.SUB_AGENT_COMPLETED);
|
|
151
|
+
expect(sub.messages.map((m) => [m.type, m.content])).toEqual([[MessageType.MESSAGE_AI, HELPER_ANSWER]]);
|
|
152
|
+
|
|
153
|
+
// ── Assert: the model double was built for every compiled role ──────────
|
|
154
|
+
expect(recordedModelBuilds().length, "the primary plus one per compiled sub-agent").toBeGreaterThanOrEqual(2);
|
|
155
|
+
expect(registry.urls.every((u) => u.includes("/model-registry"))).toBe(true);
|
|
156
|
+
|
|
157
|
+
// ── Assert: the golden ───────────────────────────────────────────────────
|
|
158
|
+
const json = JSON.stringify(toJson(AgentExecutionStatusSchema, final), null, 2) + "\n";
|
|
159
|
+
await expect(json).toMatchFileSnapshot("./goldens/sub-agent-delegation.status.json");
|
|
160
|
+
});
|
|
161
|
+
});
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hermetic golden: a turn with an UNGATED TOOL CALL through the whole
|
|
3
|
+
* `ExecuteDeepAgent` activity — the deepagents built-in `read_file` on a
|
|
4
|
+
* file seeded in the session workspace.
|
|
5
|
+
*
|
|
6
|
+
* Invariant pinned: the model's turn ("Let me read it." plus one `read_file`
|
|
7
|
+
* proposal), the tools node's `tool-started` / `tool-finished` events (status
|
|
8
|
+
* RUNNING then COMPLETED, `result` from the tool's output, `startedAt` /
|
|
9
|
+
* `completedAt` on the scripted clock), and the follow-up assistant turn fold
|
|
10
|
+
* into ONE tool row, and the turn ends COMPLETED. A read-only built-in is never
|
|
11
|
+
* gated (`shared/tool-kind.ts` FILE_READ), so no approval fields are set. The
|
|
12
|
+
* mid-stream persist the tool-call boundary forces (`persist-decision.ts`
|
|
13
|
+
* `contentDirty`) is what the platform-stop arm relies on.
|
|
14
|
+
*
|
|
15
|
+
* What the golden shows about TODAY's transcript shape, recorded as found
|
|
16
|
+
* (S3 M0 finding F-M0-1; a transcript-shape fact for S4): the row does NOT sit on
|
|
17
|
+
* the AI message that carries the text of the same LLM turn. LangGraph 1.3.2
|
|
18
|
+
* stamps the model's events with namespace `["model_request:<uuid>"]` and the
|
|
19
|
+
* tools node's with `["tools:<uuid>"]`; `V3StatusBuilder.ensureAiMessage`
|
|
20
|
+
* files the text under the raw namespace while `handleToolStarted` looks the
|
|
21
|
+
* parent up under `resolveAgentNamespace(...)`, which strips only `tools:`
|
|
22
|
+
* segments and so resolves to `""` — a miss, and `ensureAiMessageForToolCall`
|
|
23
|
+
* appends an EMPTY AI message for the row. So the transcript is THREE AI
|
|
24
|
+
* messages: the text, an empty one carrying the row, the closing text. The
|
|
25
|
+
* hand-authored fixtures in `v3-event-fixtures.ts` use `namespace: []` and
|
|
26
|
+
* never reached this arm; this golden, on the real graph, does. The golden
|
|
27
|
+
* (`goldens/tool-call.status.json`) is the shape S4's canonical transcript
|
|
28
|
+
* builder is measured against, and the shape any fix must change on purpose.
|
|
29
|
+
*
|
|
30
|
+
* Since S3 M2a the activity is the turn runtime over the native adapter, and
|
|
31
|
+
* `streamingUsage` carries the runtime accountant's fields — `model`,
|
|
32
|
+
* `estimatedCostUsd` priced at the registry's rates, the requested tier and
|
|
33
|
+
* thinking mode (Q-M2a-2: one writer of the summary, the runtime). Nothing
|
|
34
|
+
* else in this golden moved with the flip.
|
|
35
|
+
*
|
|
36
|
+
* Regenerate ONLY after a deliberate behavior change:
|
|
37
|
+
* npx vitest run src/activities/execute-deep-agent/__tests__/hermetic -u
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
41
|
+
import { join } from "node:path";
|
|
42
|
+
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
|
43
|
+
import { toJson } from "@bufbuild/protobuf";
|
|
44
|
+
import { AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
45
|
+
import { ExecutionPhase, MessageType, ToolCallStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
46
|
+
|
|
47
|
+
vi.mock("../../../../shared/model-client.js", async () =>
|
|
48
|
+
(await import("../../__test-utils__/scripted-model-module.js")).scriptedModelClientModule(),
|
|
49
|
+
);
|
|
50
|
+
vi.mock("../../../../client/stigmer-client.js", async () =>
|
|
51
|
+
(await import("../../../../__test-utils__/hermetic-activity.js")).hermeticStigmerClientModule(),
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
import {
|
|
55
|
+
ScriptedClock,
|
|
56
|
+
createHermeticEnvironment,
|
|
57
|
+
type HermeticEnvironment,
|
|
58
|
+
} from "../../../../__test-utils__/hermetic-activity.js";
|
|
59
|
+
import { stubRegistryFetch } from "../../../../__test-utils__/model-registry-fixture.js";
|
|
60
|
+
import {
|
|
61
|
+
beginDeepAgentScenario,
|
|
62
|
+
deepAgentExecutionRecord,
|
|
63
|
+
runDeepAgentTurn,
|
|
64
|
+
sessionWorkspaceDir,
|
|
65
|
+
} from "../../__test-utils__/hermetic-deep-agent.js";
|
|
66
|
+
|
|
67
|
+
const CALL_ID = "call-hermetic-read-0001";
|
|
68
|
+
const USER_MESSAGE = "What is in README.md?";
|
|
69
|
+
const README = "# Hermetic\n\nA fixture readme.\n";
|
|
70
|
+
const ASSISTANT_TEXT = "README.md holds a one-line fixture description.";
|
|
71
|
+
|
|
72
|
+
describe("ExecuteDeepAgent hermetic — ungated tool call", () => {
|
|
73
|
+
let env: HermeticEnvironment;
|
|
74
|
+
let registry: ReturnType<typeof stubRegistryFetch>;
|
|
75
|
+
const clock = new ScriptedClock();
|
|
76
|
+
|
|
77
|
+
beforeAll(() => {
|
|
78
|
+
env = createHermeticEnvironment();
|
|
79
|
+
registry = stubRegistryFetch();
|
|
80
|
+
clock.install();
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
afterAll(() => {
|
|
84
|
+
clock.uninstall();
|
|
85
|
+
registry.restore();
|
|
86
|
+
env.dispose();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("folds the proposal and the tools node's events into one COMPLETED tool row and completes", async () => {
|
|
90
|
+
// ── Arrange ──────────────────────────────────────────────────────────────
|
|
91
|
+
const record = deepAgentExecutionRecord({ message: USER_MESSAGE });
|
|
92
|
+
const scenario = beginDeepAgentScenario({
|
|
93
|
+
env,
|
|
94
|
+
clock,
|
|
95
|
+
record,
|
|
96
|
+
script: () => ({
|
|
97
|
+
turns: [
|
|
98
|
+
{
|
|
99
|
+
text: "Let me read it.",
|
|
100
|
+
toolCalls: [{ id: CALL_ID, name: "read_file", args: { file_path: "/README.md" } }],
|
|
101
|
+
usage: { inputTokens: 1_500, outputTokens: 60 },
|
|
102
|
+
},
|
|
103
|
+
{ text: ASSISTANT_TEXT, usage: { inputTokens: 1_700, outputTokens: 30 } },
|
|
104
|
+
],
|
|
105
|
+
}),
|
|
106
|
+
});
|
|
107
|
+
// Seeded AFTER the scenario begins: beginning a scenario clears the
|
|
108
|
+
// session workspace so every scenario starts where a new session starts.
|
|
109
|
+
const workspace = sessionWorkspaceDir(env);
|
|
110
|
+
mkdirSync(workspace, { recursive: true });
|
|
111
|
+
writeFileSync(join(workspace, "README.md"), README, "utf-8");
|
|
112
|
+
|
|
113
|
+
// ── Act ──────────────────────────────────────────────────────────────────
|
|
114
|
+
const invocation = await runDeepAgentTurn(scenario);
|
|
115
|
+
|
|
116
|
+
// ── Assert: outcome and phases ───────────────────────────────────────────
|
|
117
|
+
expect(invocation.outcome.kind).toBe("returned");
|
|
118
|
+
const slim = (invocation.outcome as { value: Record<string, unknown> }).value;
|
|
119
|
+
expect(slim.phase).toBe("EXECUTION_COMPLETED");
|
|
120
|
+
expect(record.persistedPhases).toEqual([
|
|
121
|
+
ExecutionPhase.EXECUTION_IN_PROGRESS,
|
|
122
|
+
ExecutionPhase.EXECUTION_COMPLETED,
|
|
123
|
+
]);
|
|
124
|
+
expect(record.persisted.length, "the tool-call boundary forces a mid-stream persist").toBeGreaterThan(2);
|
|
125
|
+
|
|
126
|
+
// ── Assert: the one tool-call row ────────────────────────────────────────
|
|
127
|
+
const rows = record.toolCalls();
|
|
128
|
+
expect(rows, "proposal + started + finished fold into ONE row, never two").toHaveLength(1);
|
|
129
|
+
const row = rows[0];
|
|
130
|
+
expect(row.id).toBe(CALL_ID);
|
|
131
|
+
expect(row.name).toBe("read_file");
|
|
132
|
+
expect(row.status).toBe(ToolCallStatus.TOOL_CALL_COMPLETED);
|
|
133
|
+
expect(row.args).toEqual({ file_path: "/README.md" });
|
|
134
|
+
expect(row.result, "the built-in's numbered listing of the seeded file").toContain("A fixture readme.");
|
|
135
|
+
expect(row.requiresApproval, "a read-only built-in is not gated").toBe(false);
|
|
136
|
+
|
|
137
|
+
// ── Assert: the transcript, as it is today (F-M0-1 in the header) ────────
|
|
138
|
+
const final = record.lastFullStatus!;
|
|
139
|
+
const ai = final.messages.filter((m) => m.type === MessageType.MESSAGE_AI);
|
|
140
|
+
expect(ai.map((m) => m.content), "text, an EMPTY message carrying the row, closing text").toEqual([
|
|
141
|
+
"Let me read it.",
|
|
142
|
+
"",
|
|
143
|
+
ASSISTANT_TEXT,
|
|
144
|
+
]);
|
|
145
|
+
expect(ai[1].toolCalls.map((tc) => tc.id), "the row sits on the empty message, not beside its text").toEqual([
|
|
146
|
+
CALL_ID,
|
|
147
|
+
]);
|
|
148
|
+
expect(final.streamingUsage?.turnCount).toBe(2);
|
|
149
|
+
|
|
150
|
+
// ── Assert: hermeticity ──────────────────────────────────────────────────
|
|
151
|
+
expect(registry.urls.every((u) => u.includes("/model-registry"))).toBe(true);
|
|
152
|
+
|
|
153
|
+
// ── Assert: the golden ───────────────────────────────────────────────────
|
|
154
|
+
const json = JSON.stringify(toJson(AgentExecutionStatusSchema, final), null, 2) + "\n";
|
|
155
|
+
await expect(json).toMatchFileSnapshot("./goldens/tool-call.status.json");
|
|
156
|
+
});
|
|
157
|
+
});
|