@stigmer/runner 3.0.9-dev.20260615153829 → 3.1.0
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/dist/.build-fingerprint +1 -1
- package/dist/__test-utils__/approval-contract/types.d.ts +174 -0
- package/dist/__test-utils__/approval-contract/types.js +24 -0
- package/dist/__test-utils__/approval-contract/types.js.map +1 -0
- package/dist/activities/call-agent-status.d.ts +19 -1
- package/dist/activities/call-agent-status.js +64 -5
- package/dist/activities/call-agent-status.js.map +1 -1
- package/dist/activities/call-llm.js +19 -53
- package/dist/activities/call-llm.js.map +1 -1
- package/dist/activities/classify-tool-approvals.d.ts +24 -0
- package/dist/activities/classify-tool-approvals.js +69 -17
- package/dist/activities/classify-tool-approvals.js.map +1 -1
- package/dist/activities/discover-mcp-server.d.ts +7 -0
- package/dist/activities/discover-mcp-server.js +11 -1
- package/dist/activities/discover-mcp-server.js.map +1 -1
- package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.d.ts +140 -0
- package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.js +130 -0
- package/dist/activities/execute-cursor/__test-utils__/cursor-hook-harness.js.map +1 -0
- package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.d.ts +18 -0
- package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js +123 -0
- package/dist/activities/execute-cursor/__test-utils__/gateway-substrate.js.map +1 -0
- package/dist/activities/execute-cursor/approval-policy.d.ts +17 -61
- package/dist/activities/execute-cursor/approval-policy.js +8 -120
- package/dist/activities/execute-cursor/approval-policy.js.map +1 -1
- package/dist/activities/execute-cursor/approval-state.d.ts +219 -9
- package/dist/activities/execute-cursor/approval-state.js +233 -17
- package/dist/activities/execute-cursor/approval-state.js.map +1 -1
- package/dist/activities/execute-cursor/capture-flow.d.ts +127 -0
- package/dist/activities/execute-cursor/capture-flow.js +234 -0
- package/dist/activities/execute-cursor/capture-flow.js.map +1 -0
- package/dist/activities/execute-cursor/cas-observations.d.ts +96 -0
- package/dist/activities/execute-cursor/cas-observations.js +184 -0
- package/dist/activities/execute-cursor/cas-observations.js.map +1 -0
- package/dist/activities/execute-cursor/command-provenance.d.ts +62 -0
- package/dist/activities/execute-cursor/command-provenance.js +128 -0
- package/dist/activities/execute-cursor/command-provenance.js.map +1 -0
- package/dist/activities/execute-cursor/exact-apply.d.ts +110 -0
- package/dist/activities/execute-cursor/exact-apply.js +204 -0
- package/dist/activities/execute-cursor/exact-apply.js.map +1 -0
- package/dist/activities/execute-cursor/hook-script.d.ts +53 -24
- package/dist/activities/execute-cursor/hook-script.js +310 -47
- package/dist/activities/execute-cursor/hook-script.js.map +1 -1
- package/dist/activities/execute-cursor/index.d.ts +8 -1
- package/dist/activities/execute-cursor/index.js +500 -64
- package/dist/activities/execute-cursor/index.js.map +1 -1
- package/dist/activities/execute-cursor/message-translator.d.ts +258 -11
- package/dist/activities/execute-cursor/message-translator.js +836 -90
- package/dist/activities/execute-cursor/message-translator.js.map +1 -1
- package/dist/activities/execute-cursor/prompt-builder.d.ts +20 -23
- package/dist/activities/execute-cursor/prompt-builder.js +74 -15
- package/dist/activities/execute-cursor/prompt-builder.js.map +1 -1
- package/dist/activities/execute-cursor/workspace-setup.d.ts +17 -0
- package/dist/activities/execute-cursor/workspace-setup.js +212 -33
- package/dist/activities/execute-cursor/workspace-setup.js.map +1 -1
- package/dist/activities/execute-deep-agent/__test-utils__/gateway-substrate.d.ts +19 -0
- package/dist/activities/execute-deep-agent/__test-utils__/gateway-substrate.js +143 -0
- package/dist/activities/execute-deep-agent/__test-utils__/gateway-substrate.js.map +1 -0
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.d.ts +88 -0
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js +81 -0
- package/dist/activities/execute-deep-agent/__test-utils__/scripted-model.js.map +1 -0
- package/dist/activities/execute-deep-agent/approval-file-change.d.ts +47 -0
- package/dist/activities/execute-deep-agent/approval-file-change.js +68 -0
- package/dist/activities/execute-deep-agent/approval-file-change.js.map +1 -0
- package/dist/activities/execute-deep-agent/attachment-injector.d.ts +8 -1
- package/dist/activities/execute-deep-agent/attachment-injector.js +7 -7
- package/dist/activities/execute-deep-agent/attachment-injector.js.map +1 -1
- package/dist/activities/execute-deep-agent/cas-capture-backend.d.ts +42 -0
- package/dist/activities/execute-deep-agent/cas-capture-backend.js +47 -0
- package/dist/activities/execute-deep-agent/cas-capture-backend.js.map +1 -0
- package/dist/activities/execute-deep-agent/cas-capture-observer.d.ts +79 -0
- package/dist/activities/execute-deep-agent/cas-capture-observer.js +112 -0
- package/dist/activities/execute-deep-agent/cas-capture-observer.js.map +1 -0
- package/dist/activities/execute-deep-agent/hitl.d.ts +16 -3
- package/dist/activities/execute-deep-agent/hitl.js +11 -3
- package/dist/activities/execute-deep-agent/hitl.js.map +1 -1
- package/dist/activities/execute-deep-agent/index.d.ts +2 -1
- package/dist/activities/execute-deep-agent/index.js +393 -33
- package/dist/activities/execute-deep-agent/index.js.map +1 -1
- package/dist/activities/execute-deep-agent/inline-publisher.d.ts +7 -1
- package/dist/activities/execute-deep-agent/inline-publisher.js +23 -2
- package/dist/activities/execute-deep-agent/inline-publisher.js.map +1 -1
- package/dist/activities/execute-deep-agent/setup.d.ts +53 -2
- package/dist/activities/execute-deep-agent/setup.js +149 -92
- package/dist/activities/execute-deep-agent/setup.js.map +1 -1
- package/dist/activities/execute-deep-agent/stamp-flowed-rows.d.ts +36 -0
- package/dist/activities/execute-deep-agent/stamp-flowed-rows.js +56 -0
- package/dist/activities/execute-deep-agent/stamp-flowed-rows.js.map +1 -0
- package/dist/activities/execute-deep-agent/status-builder-shared.d.ts +34 -1
- package/dist/activities/execute-deep-agent/status-builder-shared.js +26 -25
- package/dist/activities/execute-deep-agent/status-builder-shared.js.map +1 -1
- package/dist/activities/execute-deep-agent/status-builder.d.ts +17 -5
- package/dist/activities/execute-deep-agent/status-builder.js +44 -3
- package/dist/activities/execute-deep-agent/status-builder.js.map +1 -1
- package/dist/activities/execute-deep-agent/streaming-side-effects.js +2 -19
- package/dist/activities/execute-deep-agent/streaming-side-effects.js.map +1 -1
- package/dist/activities/execute-deep-agent/streaming.js +3 -15
- package/dist/activities/execute-deep-agent/streaming.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-transformer.d.ts +25 -7
- package/dist/activities/execute-deep-agent/subagent-transformer.js +23 -7
- package/dist/activities/execute-deep-agent/subagent-transformer.js.map +1 -1
- package/dist/activities/execute-deep-agent/subagent-wiring.d.ts +30 -3
- package/dist/activities/execute-deep-agent/subagent-wiring.js +29 -3
- package/dist/activities/execute-deep-agent/subagent-wiring.js.map +1 -1
- package/dist/activities/execute-deep-agent/v3-status-builder.js +32 -2
- package/dist/activities/execute-deep-agent/v3-status-builder.js.map +1 -1
- package/dist/claimcheck/payload-codec.js +9 -5
- package/dist/claimcheck/payload-codec.js.map +1 -1
- package/dist/client/stigmer-client.d.ts +2 -0
- package/dist/client/stigmer-client.js +2 -0
- package/dist/client/stigmer-client.js.map +1 -1
- package/dist/middleware/approval-gate.d.ts +85 -4
- package/dist/middleware/approval-gate.js +165 -38
- package/dist/middleware/approval-gate.js.map +1 -1
- package/dist/middleware/types.d.ts +2 -5
- package/dist/shared/activity-input.d.ts +43 -0
- package/dist/shared/activity-input.js +17 -0
- package/dist/shared/activity-input.js.map +1 -0
- package/dist/shared/approval-canonicalize.d.ts +19 -0
- package/dist/shared/approval-canonicalize.js +119 -0
- package/dist/shared/approval-canonicalize.js.map +1 -0
- package/dist/shared/approval-fingerprint.d.ts +106 -0
- package/dist/shared/approval-fingerprint.js +113 -0
- package/dist/shared/approval-fingerprint.js.map +1 -0
- package/dist/shared/approval-policy.d.ts +182 -12
- package/dist/shared/approval-policy.js +213 -27
- package/dist/shared/approval-policy.js.map +1 -1
- package/dist/shared/args-preview.d.ts +52 -0
- package/dist/shared/args-preview.js +93 -0
- package/dist/shared/args-preview.js.map +1 -0
- package/dist/shared/artifact-storage.d.ts +19 -1
- package/dist/shared/artifact-storage.js +48 -11
- package/dist/shared/artifact-storage.js.map +1 -1
- package/dist/shared/file-change.d.ts +44 -0
- package/dist/shared/file-change.js +57 -0
- package/dist/shared/file-change.js.map +1 -0
- package/dist/shared/file-tools.d.ts +107 -0
- package/dist/shared/file-tools.js +168 -0
- package/dist/shared/file-tools.js.map +1 -0
- package/dist/shared/filereview/capture.d.ts +202 -0
- package/dist/shared/filereview/capture.js +498 -0
- package/dist/shared/filereview/capture.js.map +1 -0
- package/dist/shared/filereview/cas-substrate.d.ts +190 -0
- package/dist/shared/filereview/cas-substrate.js +284 -0
- package/dist/shared/filereview/cas-substrate.js.map +1 -0
- package/dist/shared/filereview/digest.d.ts +40 -0
- package/dist/shared/filereview/digest.js +66 -0
- package/dist/shared/filereview/digest.js.map +1 -0
- package/dist/shared/filereview/events.d.ts +170 -0
- package/dist/shared/filereview/events.js +298 -0
- package/dist/shared/filereview/events.js.map +1 -0
- package/dist/shared/filereview/git-substrate.d.ts +175 -0
- package/dist/shared/filereview/git-substrate.js +439 -0
- package/dist/shared/filereview/git-substrate.js.map +1 -0
- package/dist/shared/filereview/index.d.ts +11 -0
- package/dist/shared/filereview/index.js +12 -0
- package/dist/shared/filereview/index.js.map +1 -0
- package/dist/shared/filereview/secret-paths.d.ts +63 -0
- package/dist/shared/filereview/secret-paths.js +105 -0
- package/dist/shared/filereview/secret-paths.js.map +1 -0
- package/dist/shared/fingerprint-secret.d.ts +26 -0
- package/dist/shared/fingerprint-secret.js +47 -0
- package/dist/shared/fingerprint-secret.js.map +1 -0
- package/dist/shared/model-client.d.ts +51 -0
- package/dist/shared/model-client.js +77 -0
- package/dist/shared/model-client.js.map +1 -0
- package/dist/shared/plan-artifact.js +0 -2
- package/dist/shared/plan-artifact.js.map +1 -1
- package/dist/shared/status-offload.d.ts +83 -9
- package/dist/shared/status-offload.js +399 -79
- package/dist/shared/status-offload.js.map +1 -1
- package/dist/shared/status.js +14 -1
- package/dist/shared/status.js.map +1 -1
- package/dist/shared/tool-kind.d.ts +19 -0
- package/dist/shared/tool-kind.js +13 -0
- package/dist/shared/tool-kind.js.map +1 -1
- package/dist/shared/tool-row.d.ts +88 -0
- package/dist/shared/tool-row.js +127 -0
- package/dist/shared/tool-row.js.map +1 -0
- package/dist/shared/workspace/platform-dir.d.ts +25 -0
- package/dist/shared/workspace/platform-dir.js +38 -2
- package/dist/shared/workspace/platform-dir.js.map +1 -1
- package/dist/workflows/call-agent-orchestrator.js +56 -7
- package/dist/workflows/call-agent-orchestrator.js.map +1 -1
- package/dist/workflows/connect-mcp-server.d.ts +50 -0
- package/dist/workflows/connect-mcp-server.js +136 -15
- package/dist/workflows/connect-mcp-server.js.map +1 -1
- package/dist/workflows/types.d.ts +8 -0
- package/package.json +2 -2
- package/src/__test-utils__/approval-contract/contract.ts +224 -0
- package/src/__test-utils__/approval-contract/types.ts +179 -0
- package/src/__test-utils__/fake-artifact-storage.ts +72 -0
- package/src/__tests__/approval-gateway-contract.test.ts +29 -0
- package/src/__tests__/claimcheck-codec.test.ts +16 -53
- package/src/__tests__/golden-e2e.test.ts +2 -0
- package/src/__tests__/runner-token-coordinator.test.ts +3 -3
- package/src/activities/__tests__/call-agent-status.test.ts +135 -0
- package/src/activities/__tests__/call-llm.test.ts +1 -1
- package/src/activities/__tests__/classify-tool-approvals.test.ts +208 -1
- package/src/activities/__tests__/discover-mcp-server.test.ts +30 -0
- package/src/activities/__tests__/workflow-event-activities.test.ts +2 -1
- package/src/activities/call-agent-status.ts +74 -4
- package/src/activities/call-llm.ts +18 -63
- package/src/activities/classify-tool-approvals.ts +101 -19
- package/src/activities/discover-mcp-server.ts +29 -1
- package/src/activities/execute-cursor/__test-utils__/cursor-hook-harness.ts +216 -0
- package/src/activities/execute-cursor/__test-utils__/gateway-substrate.ts +148 -0
- package/src/activities/execute-cursor/__tests__/approval-gate.test.ts +41 -9
- package/src/activities/execute-cursor/__tests__/approval-state.test.ts +292 -0
- package/src/activities/execute-cursor/__tests__/build-prompt.test.ts +68 -1
- package/src/activities/execute-cursor/__tests__/capture-flow.test.ts +1005 -0
- package/src/activities/execute-cursor/__tests__/cas-observations.test.ts +187 -0
- package/src/activities/execute-cursor/__tests__/coarse-fingerprint.test.ts +97 -0
- package/src/activities/execute-cursor/__tests__/command-provenance.test.ts +240 -0
- package/src/activities/execute-cursor/__tests__/deny-gate-exact-apply.test.ts +203 -0
- package/src/activities/execute-cursor/__tests__/exact-apply.test.ts +375 -0
- package/src/activities/execute-cursor/__tests__/hitl-ledger.test.ts +1294 -24
- package/src/activities/execute-cursor/__tests__/hitl-resume-history.test.ts +446 -0
- package/src/activities/execute-cursor/__tests__/hook-script.test.ts +384 -110
- package/src/activities/execute-cursor/__tests__/message-translator.test.ts +171 -25
- package/src/activities/execute-cursor/__tests__/sequential-gate-resume.test.ts +189 -0
- package/src/activities/execute-cursor/__tests__/tool-result-image.test.ts +44 -23
- package/src/activities/execute-cursor/__tests__/workspace-setup.test.ts +190 -10
- package/src/activities/execute-cursor/approval-policy.ts +28 -159
- package/src/activities/execute-cursor/approval-state.ts +366 -18
- package/src/activities/execute-cursor/capture-flow.ts +323 -0
- package/src/activities/execute-cursor/cas-observations.ts +204 -0
- package/src/activities/execute-cursor/command-provenance.ts +168 -0
- package/src/activities/execute-cursor/exact-apply.ts +253 -0
- package/src/activities/execute-cursor/hook-script.ts +317 -51
- package/src/activities/execute-cursor/index.ts +575 -67
- package/src/activities/execute-cursor/message-translator.ts +963 -89
- package/src/activities/execute-cursor/prompt-builder.ts +80 -14
- package/src/activities/execute-cursor/workspace-setup.ts +257 -42
- package/src/activities/execute-deep-agent/__test-utils__/gateway-substrate.ts +180 -0
- package/src/activities/execute-deep-agent/__test-utils__/scripted-model.ts +134 -0
- package/src/activities/execute-deep-agent/__tests__/approval-file-change.test.ts +84 -0
- package/src/activities/execute-deep-agent/__tests__/attachment-injector.test.ts +11 -24
- package/src/activities/execute-deep-agent/__tests__/cas-capture-backend.test.ts +64 -0
- package/src/activities/execute-deep-agent/__tests__/cas-capture-observer.test.ts +163 -0
- package/src/activities/execute-deep-agent/__tests__/hitl-integration.test.ts +8 -5
- package/src/activities/execute-deep-agent/__tests__/hitl-resume-approve-all.test.ts +342 -0
- package/src/activities/execute-deep-agent/__tests__/hitl-resume-history.test.ts +284 -0
- package/src/activities/execute-deep-agent/__tests__/hitl.test.ts +14 -36
- package/src/activities/execute-deep-agent/__tests__/inline-publisher.test.ts +31 -13
- package/src/activities/execute-deep-agent/__tests__/sequential-gate-resume.test.ts +349 -0
- package/src/activities/execute-deep-agent/__tests__/stamp-flowed-rows.test.ts +119 -0
- package/src/activities/execute-deep-agent/__tests__/status-builder.test.ts +91 -10
- package/src/activities/execute-deep-agent/__tests__/streaming-v3.test.ts +9 -9
- package/src/activities/execute-deep-agent/__tests__/subagent-approval-propagation.test.ts +160 -0
- package/src/activities/execute-deep-agent/__tests__/subagent-gitignored-capture.test.ts +213 -0
- package/src/activities/execute-deep-agent/__tests__/subagent-transformer.test.ts +3 -6
- package/src/activities/execute-deep-agent/__tests__/subagent-wiring.test.ts +84 -1
- package/src/activities/execute-deep-agent/__tests__/v3-status-builder.test.ts +4 -1
- package/src/activities/execute-deep-agent/approval-file-change.ts +80 -0
- package/src/activities/execute-deep-agent/attachment-injector.ts +20 -11
- package/src/activities/execute-deep-agent/cas-capture-backend.ts +66 -0
- package/src/activities/execute-deep-agent/cas-capture-observer.ts +125 -0
- package/src/activities/execute-deep-agent/hitl.ts +23 -7
- package/src/activities/execute-deep-agent/index.ts +463 -40
- package/src/activities/execute-deep-agent/inline-publisher.ts +27 -4
- package/src/activities/execute-deep-agent/setup.ts +223 -125
- package/src/activities/execute-deep-agent/stamp-flowed-rows.ts +64 -0
- package/src/activities/execute-deep-agent/status-builder-shared.ts +62 -23
- package/src/activities/execute-deep-agent/status-builder.ts +63 -8
- package/src/activities/execute-deep-agent/streaming-side-effects.ts +2 -16
- package/src/activities/execute-deep-agent/streaming.ts +3 -13
- package/src/activities/execute-deep-agent/subagent-transformer.ts +53 -13
- package/src/activities/execute-deep-agent/subagent-wiring.ts +50 -3
- package/src/activities/execute-deep-agent/v3-status-builder.ts +36 -2
- package/src/claimcheck/payload-codec.ts +8 -8
- package/src/client/stigmer-client.ts +9 -1
- package/src/middleware/__tests__/approval-gate.test.ts +488 -4
- package/src/middleware/approval-gate.ts +247 -38
- package/src/middleware/types.ts +5 -5
- package/src/shared/__tests__/activity-input.test.ts +78 -0
- package/src/shared/__tests__/approval-canonicalize.test.ts +106 -0
- package/src/shared/__tests__/approval-fingerprint.test.ts +115 -0
- package/src/shared/__tests__/approval-policy.test.ts +274 -40
- package/src/shared/__tests__/args-preview.test.ts +78 -0
- package/src/shared/__tests__/artifact-storage-extended.test.ts +62 -10
- package/src/shared/__tests__/artifact-storage.test.ts +123 -11
- package/src/shared/__tests__/file-change.test.ts +85 -0
- package/src/shared/__tests__/file-tools.test.ts +90 -0
- package/src/shared/__tests__/fingerprint-secret.test.ts +51 -0
- package/src/shared/__tests__/lease-scope-corpus.test.ts +56 -0
- package/src/shared/__tests__/model-client.test.ts +162 -0
- package/src/shared/__tests__/plan-artifact.test.ts +11 -26
- package/src/shared/__tests__/policy-source-corpus.test.ts +58 -0
- package/src/shared/__tests__/status-offload.test.ts +573 -16
- package/src/shared/__tests__/status.test.ts +4 -5
- package/src/shared/__tests__/tool-kind.test.ts +24 -1
- package/src/shared/__tests__/tool-row.test.ts +221 -0
- package/src/shared/activity-input.ts +57 -0
- package/src/shared/approval-canonicalize.ts +159 -0
- package/src/shared/approval-fingerprint.ts +148 -0
- package/src/shared/approval-policy.ts +303 -27
- package/src/shared/args-preview.ts +98 -0
- package/src/shared/artifact-storage.ts +62 -11
- package/src/shared/checkpointer/__tests__/http-saver.test.ts +1 -2
- package/src/shared/file-change.ts +64 -0
- package/src/shared/file-tools.ts +169 -0
- package/src/shared/filereview/__tests__/capture.test.ts +856 -0
- package/src/shared/filereview/__tests__/cas-substrate.test.ts +404 -0
- package/src/shared/filereview/__tests__/digest.test.ts +100 -0
- package/src/shared/filereview/__tests__/events.test.ts +245 -0
- package/src/shared/filereview/__tests__/git-substrate.test.ts +362 -0
- package/src/shared/filereview/__tests__/proxy-reconcile.test.ts +286 -0
- package/src/shared/filereview/__tests__/secret-paths.test.ts +121 -0
- package/src/shared/filereview/capture.ts +727 -0
- package/src/shared/filereview/cas-substrate.ts +401 -0
- package/src/shared/filereview/digest.ts +83 -0
- package/src/shared/filereview/events.ts +449 -0
- package/src/shared/filereview/git-substrate.ts +555 -0
- package/src/shared/filereview/index.ts +60 -0
- package/src/shared/filereview/secret-paths.ts +121 -0
- package/src/shared/fingerprint-secret.ts +53 -0
- package/src/shared/model-client.ts +122 -0
- package/src/shared/plan-artifact.ts +0 -2
- package/src/shared/status-offload.ts +433 -77
- package/src/shared/status.ts +13 -0
- package/src/shared/tool-kind.ts +33 -0
- package/src/shared/tool-row.ts +135 -0
- package/src/shared/workspace/platform-dir.ts +41 -2
- package/src/workflow-engine/__tests__/golden-execution.test.ts +35 -18
- package/src/workflow-engine/__tests__/tasks/try.test.ts +1 -1
- package/src/workflows/__tests__/connect-mcp-server.test.ts +304 -29
- package/src/workflows/call-agent-orchestrator.ts +53 -6
- package/src/workflows/connect-mcp-server.ts +179 -24
- package/src/workflows/types.ts +8 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @regression file-hitl-proxy-reconcile — the file-review reconcile crash in
|
|
3
|
+
* cloud/desktop (proxy) artifact mode (see _cursor/error.md: an ExecuteCursor
|
|
4
|
+
* turn died on `Artifact download failed (HTTP 404) ... NoSuchKey` for a CAS
|
|
5
|
+
* manifest a git-only turn never wrote).
|
|
6
|
+
*
|
|
7
|
+
* This exercises the REAL {@link ProxyArtifactStorage} end to end against a mock
|
|
8
|
+
* proxy + object store, so the whole stack — presign, the ranged-GET existence
|
|
9
|
+
* probe, blob upload/download, and the manifest reconcile — is proven over HTTP.
|
|
10
|
+
* That is precisely the coverage that was missing when the bug shipped: every
|
|
11
|
+
* prior file-review proof used LocalArtifactStorage / in-memory fakes, whose
|
|
12
|
+
* `exists()` is honest, so none of them could reproduce the proxy behavior where
|
|
13
|
+
* a presigned URL is minted for a key that does not exist.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { execFile } from "node:child_process";
|
|
17
|
+
import { createServer, type IncomingMessage, type Server } from "node:http";
|
|
18
|
+
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
19
|
+
import { type AddressInfo } from "node:net";
|
|
20
|
+
import { tmpdir } from "node:os";
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
import { promisify } from "node:util";
|
|
23
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
24
|
+
import { clone, create } from "@bufbuild/protobuf";
|
|
25
|
+
import { AgentExecutionStatusSchema } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
26
|
+
import type { AgentExecutionStatus } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/api_pb";
|
|
27
|
+
import {
|
|
28
|
+
CapturedFileChangeSchema,
|
|
29
|
+
FileChangeSetSchema,
|
|
30
|
+
FileDecisionSchema,
|
|
31
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/filereview_pb";
|
|
32
|
+
import type { CapturedFileChange } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/filereview_pb";
|
|
33
|
+
import {
|
|
34
|
+
FileCaptureClass,
|
|
35
|
+
FileDecisionAction,
|
|
36
|
+
FileDecisionScope,
|
|
37
|
+
FileChangeSetStatus,
|
|
38
|
+
FileReviewEventType,
|
|
39
|
+
} from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v1/enum_pb";
|
|
40
|
+
import { ProxyArtifactStorage } from "../../artifact-storage.js";
|
|
41
|
+
import { casBlobReader, casManifestKey } from "../cas-substrate.js";
|
|
42
|
+
import {
|
|
43
|
+
applyCaptureDecisions,
|
|
44
|
+
captureBaselineToLedger,
|
|
45
|
+
captureCandidateToLedger,
|
|
46
|
+
} from "../capture.js";
|
|
47
|
+
|
|
48
|
+
const execFileAsync = promisify(execFile);
|
|
49
|
+
const EXEC_ID = "exec-proxy-1";
|
|
50
|
+
const CHANGE_SET_ID = `${EXEC_ID}:0`;
|
|
51
|
+
const HARNESS = "cursor";
|
|
52
|
+
|
|
53
|
+
// ─── Mock proxy + object store (faithful to ProxyArtifactStorage's contract) ──
|
|
54
|
+
|
|
55
|
+
interface MockProxy {
|
|
56
|
+
readonly endpoint: string;
|
|
57
|
+
/** The object store (R2 stand-in), keyed by artifact key. */
|
|
58
|
+
readonly objects: Map<string, Buffer>;
|
|
59
|
+
/** Every object key that was GET (download or existence-probe). */
|
|
60
|
+
readonly objectGets: string[];
|
|
61
|
+
close(): Promise<void>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function readBody(req: IncomingMessage): Promise<Buffer> {
|
|
65
|
+
const chunks: Buffer[] = [];
|
|
66
|
+
for await (const c of req) chunks.push(c as Buffer);
|
|
67
|
+
return Buffer.concat(chunks);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function startMockProxy(): Promise<MockProxy> {
|
|
71
|
+
const objects = new Map<string, Buffer>();
|
|
72
|
+
const objectGets: string[] = [];
|
|
73
|
+
|
|
74
|
+
const server: Server = createServer(async (req, res) => {
|
|
75
|
+
const url = new URL(req.url ?? "/", "http://127.0.0.1");
|
|
76
|
+
const base = `http://127.0.0.1:${(server.address() as AddressInfo).port}`;
|
|
77
|
+
|
|
78
|
+
// Presign endpoints mint a URL for ANY key — the real proxy does NOT verify
|
|
79
|
+
// the object exists. This is exactly the property exists() must not trust.
|
|
80
|
+
if (req.method === "POST" && url.pathname.endsWith("/presigned-upload-url")) {
|
|
81
|
+
const { key } = JSON.parse((await readBody(req)).toString()) as { key: string };
|
|
82
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
83
|
+
res.end(JSON.stringify({ url: `${base}/obj/${encodeURIComponent(key)}`, headers: {} }));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (req.method === "POST" && url.pathname.endsWith("/presigned-download-url")) {
|
|
87
|
+
const { key } = JSON.parse((await readBody(req)).toString()) as { key: string };
|
|
88
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
89
|
+
res.end(JSON.stringify({ url: `${base}/obj/${encodeURIComponent(key)}` }));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (url.pathname.startsWith("/obj/")) {
|
|
94
|
+
const key = decodeURIComponent(url.pathname.slice("/obj/".length));
|
|
95
|
+
if (req.method === "PUT") {
|
|
96
|
+
objects.set(key, await readBody(req));
|
|
97
|
+
res.writeHead(200);
|
|
98
|
+
res.end();
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (req.method === "GET") {
|
|
102
|
+
objectGets.push(key);
|
|
103
|
+
const buf = objects.get(key);
|
|
104
|
+
if (!buf) {
|
|
105
|
+
res.writeHead(404);
|
|
106
|
+
res.end("<?xml version=\"1.0\"?><Error><Code>NoSuchKey</Code></Error>");
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const range = req.headers["range"];
|
|
110
|
+
if (typeof range === "string" && range.startsWith("bytes=")) {
|
|
111
|
+
if (buf.length === 0) {
|
|
112
|
+
res.writeHead(416); // empty object: range unsatisfiable, yet it exists
|
|
113
|
+
res.end();
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
res.writeHead(206, { "content-range": `bytes 0-0/${buf.length}` });
|
|
117
|
+
res.end(buf.subarray(0, 1));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
res.writeHead(200);
|
|
121
|
+
res.end(buf);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
res.writeHead(404);
|
|
126
|
+
res.end();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", () => resolve()));
|
|
130
|
+
const endpoint = `http://127.0.0.1:${(server.address() as AddressInfo).port}`;
|
|
131
|
+
return {
|
|
132
|
+
endpoint,
|
|
133
|
+
objects,
|
|
134
|
+
objectGets,
|
|
135
|
+
close: () =>
|
|
136
|
+
new Promise<void>((resolve) => {
|
|
137
|
+
server.closeAllConnections?.();
|
|
138
|
+
server.close(() => resolve());
|
|
139
|
+
}),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ─── Change-set helpers (mirror the server projection: DECIDED set + snapshot) ─
|
|
144
|
+
|
|
145
|
+
function eventsOfType(status: AgentExecutionStatus, type: FileReviewEventType) {
|
|
146
|
+
return (status.fileReviewEventStream?.events ?? []).filter((e) => e.eventType === type);
|
|
147
|
+
}
|
|
148
|
+
function candidateChanges(status: AgentExecutionStatus): CapturedFileChange[] {
|
|
149
|
+
const ev = eventsOfType(status, FileReviewEventType.CANDIDATE_CAPTURED)[0];
|
|
150
|
+
return ev?.payload.case === "candidateCaptured" ? ev.payload.value.changes : [];
|
|
151
|
+
}
|
|
152
|
+
function decidedChangeSet(
|
|
153
|
+
status: AgentExecutionStatus,
|
|
154
|
+
decisionByPath: Record<string, FileDecisionAction>,
|
|
155
|
+
) {
|
|
156
|
+
const changes = candidateChanges(status).map((c) => clone(CapturedFileChangeSchema, c));
|
|
157
|
+
const decisions = changes
|
|
158
|
+
.filter((c) => decisionByPath[c.pathAfter || c.pathBefore] !== undefined)
|
|
159
|
+
.map((c) =>
|
|
160
|
+
create(FileDecisionSchema, {
|
|
161
|
+
id: `${c.id}:d`,
|
|
162
|
+
changeSetId: CHANGE_SET_ID,
|
|
163
|
+
scope: FileDecisionScope.FILE,
|
|
164
|
+
fileChangeId: c.id,
|
|
165
|
+
action: decisionByPath[c.pathAfter || c.pathBefore],
|
|
166
|
+
expectedDigest: c.fileDigest,
|
|
167
|
+
}),
|
|
168
|
+
);
|
|
169
|
+
const candEv = eventsOfType(status, FileReviewEventType.CANDIDATE_CAPTURED)[0];
|
|
170
|
+
const candidateSnapshot =
|
|
171
|
+
candEv?.payload.case === "candidateCaptured" ? candEv.payload.value.candidateSnapshot : undefined;
|
|
172
|
+
return create(FileChangeSetSchema, {
|
|
173
|
+
id: CHANGE_SET_ID,
|
|
174
|
+
changes,
|
|
175
|
+
decisions,
|
|
176
|
+
candidateSnapshot,
|
|
177
|
+
status: FileChangeSetStatus.DECIDED,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ─── Tests ────────────────────────────────────────────────────────────────
|
|
182
|
+
|
|
183
|
+
describe("proxy-mode file-review reconcile (real ProxyArtifactStorage)", () => {
|
|
184
|
+
let proxy: MockProxy;
|
|
185
|
+
let storage: ProxyArtifactStorage;
|
|
186
|
+
|
|
187
|
+
beforeEach(async () => {
|
|
188
|
+
proxy = await startMockProxy();
|
|
189
|
+
storage = new ProxyArtifactStorage(proxy.endpoint, "tok");
|
|
190
|
+
});
|
|
191
|
+
afterEach(async () => {
|
|
192
|
+
await proxy.close();
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("git-only turn reconciles without probing/downloading a nonexistent CAS manifest (error.md regression)", async () => {
|
|
196
|
+
const repo = await mkdtemp(join(tmpdir(), "stigmer-proxy-git-"));
|
|
197
|
+
try {
|
|
198
|
+
const git = (args: string[]) => execFileAsync("git", args, { cwd: repo });
|
|
199
|
+
await git(["init", "-q"]);
|
|
200
|
+
await git(["config", "user.email", "t@t.local"]);
|
|
201
|
+
await git(["config", "user.name", "t"]);
|
|
202
|
+
await writeFile(join(repo, "notes.md"), "planton notes\n");
|
|
203
|
+
await git(["add", "-A"]);
|
|
204
|
+
await git(["commit", "-q", "-m", "initial"]);
|
|
205
|
+
|
|
206
|
+
const status = create(AgentExecutionStatusSchema, {});
|
|
207
|
+
const baseline = await captureBaselineToLedger({
|
|
208
|
+
status, gitRoot: repo, executionId: EXEC_ID, changeSetId: CHANGE_SET_ID, harnessId: HARNESS,
|
|
209
|
+
});
|
|
210
|
+
await writeFile(join(repo, "notes.md"), "PLANTON notes edited\n");
|
|
211
|
+
await captureCandidateToLedger({
|
|
212
|
+
status, gitRoot: repo, executionId: EXEC_ID, changeSetId: CHANGE_SET_ID,
|
|
213
|
+
baselineTree: baseline, harnessId: HARNESS,
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
const changeSet = decidedChangeSet(status, { "notes.md": FileDecisionAction.APPROVE });
|
|
217
|
+
|
|
218
|
+
// The exact production call shape: a real ProxyArtifactStorage is threaded.
|
|
219
|
+
const result = await applyCaptureDecisions({
|
|
220
|
+
status, gitRoot: repo, executionId: EXEC_ID, changeSet, harnessId: HARNESS,
|
|
221
|
+
storage, readBlob: casBlobReader(storage),
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
expect(result.failed).toBe(false);
|
|
225
|
+
expect(result.approvedPaths).toEqual(["notes.md"]);
|
|
226
|
+
expect(await readFile(join(repo, "notes.md"), "utf8")).toBe("PLANTON notes edited\n");
|
|
227
|
+
// The regression proof: the reconcile never fetched the (nonexistent) CAS
|
|
228
|
+
// manifest from the object store. On the old code, exists() trusted the
|
|
229
|
+
// presign, this GET returned 404, and the execution crashed.
|
|
230
|
+
expect(proxy.objectGets).not.toContain(casManifestKey(EXEC_ID, CHANGE_SET_ID));
|
|
231
|
+
} finally {
|
|
232
|
+
await rm(repo, { recursive: true, force: true });
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it("non-git CAS turn round-trips blobs + manifest through the real proxy (approve byte-exact, reject reverted)", async () => {
|
|
237
|
+
const ws = await mkdtemp(join(tmpdir(), "stigmer-proxy-cas-"));
|
|
238
|
+
try {
|
|
239
|
+
const enc = (s: string) => new TextEncoder().encode(s);
|
|
240
|
+
const status = create(AgentExecutionStatusSchema, {});
|
|
241
|
+
const baseline = await captureBaselineToLedger({
|
|
242
|
+
status, gitRoot: ws, executionId: EXEC_ID, changeSetId: CHANGE_SET_ID,
|
|
243
|
+
harnessId: HARNESS, gitWorkspace: false,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// The turn's edits are already on disk (the "after" state under review).
|
|
247
|
+
await writeFile(join(ws, "created.txt"), "X");
|
|
248
|
+
await writeFile(join(ws, "edited.txt"), "NEW");
|
|
249
|
+
await captureCandidateToLedger({
|
|
250
|
+
status, gitRoot: ws, executionId: EXEC_ID, changeSetId: CHANGE_SET_ID,
|
|
251
|
+
baselineTree: baseline, harnessId: HARNESS, gitWorkspace: false, storage,
|
|
252
|
+
casCaptures: [
|
|
253
|
+
{ path: "created.txt", before: null, after: enc("X"), captureClass: FileCaptureClass.NON_GIT_CAS },
|
|
254
|
+
{ path: "edited.txt", before: enc("OLD"), after: enc("NEW"), captureClass: FileCaptureClass.NON_GIT_CAS },
|
|
255
|
+
],
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Blobs + manifest were uploaded THROUGH THE REAL PROXY. The dedup check
|
|
259
|
+
// (exists() -> object GET 404 -> PUT) is what the broken exists() defeated:
|
|
260
|
+
// it reported "already present" and skipped every upload.
|
|
261
|
+
expect(proxy.objects.has(casManifestKey(EXEC_ID, CHANGE_SET_ID))).toBe(true);
|
|
262
|
+
// 2 after-blobs ("X", "NEW") + 1 before-blob ("OLD") + 1 manifest.
|
|
263
|
+
expect(proxy.objects.size).toBe(4);
|
|
264
|
+
|
|
265
|
+
const changeSet = decidedChangeSet(status, {
|
|
266
|
+
"created.txt": FileDecisionAction.APPROVE,
|
|
267
|
+
"edited.txt": FileDecisionAction.REJECT,
|
|
268
|
+
});
|
|
269
|
+
const result = await applyCaptureDecisions({
|
|
270
|
+
status, gitRoot: ws, executionId: EXEC_ID, changeSet, harnessId: HARNESS,
|
|
271
|
+
storage, readBlob: casBlobReader(storage), gitWorkspace: false,
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
expect(result.failed).toBe(false);
|
|
275
|
+
// Approved create kept byte-exact from the downloaded after-blob.
|
|
276
|
+
expect(await readFile(join(ws, "created.txt"), "utf8")).toBe("X");
|
|
277
|
+
// Rejected modify snapped back to its downloaded before-blob.
|
|
278
|
+
expect(await readFile(join(ws, "edited.txt"), "utf8")).toBe("OLD");
|
|
279
|
+
// The manifest WAS downloaded this time (a genuine CAS turn), unlike the
|
|
280
|
+
// git-only case above.
|
|
281
|
+
expect(proxy.objectGets).toContain(casManifestKey(EXEC_ID, CHANGE_SET_ID));
|
|
282
|
+
} finally {
|
|
283
|
+
await rm(ws, { recursive: true, force: true });
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
});
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the DD-E secret-safety gate. These pin the block-vs-capture
|
|
3
|
+
* decision that keeps secret bytes out of durable CAS storage; the same corpus
|
|
4
|
+
* intent will be mirrored cross-edition.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, expect, it } from "vitest";
|
|
8
|
+
import { isSecretLikePath, partitionIgnoredPathsBySecret } from "../secret-paths.js";
|
|
9
|
+
|
|
10
|
+
describe("isSecretLikePath — blocks secret-like paths (fail-closed)", () => {
|
|
11
|
+
it.each([
|
|
12
|
+
".env",
|
|
13
|
+
".env.local",
|
|
14
|
+
".env.production",
|
|
15
|
+
"config/.env.test",
|
|
16
|
+
"app.pem",
|
|
17
|
+
"server.key",
|
|
18
|
+
"cert.pfx",
|
|
19
|
+
"keystore.p12",
|
|
20
|
+
"id_rsa",
|
|
21
|
+
"nested/dir/id_ed25519",
|
|
22
|
+
"credentials",
|
|
23
|
+
"credentials.json",
|
|
24
|
+
"secrets.yaml",
|
|
25
|
+
"terraform.tfstate",
|
|
26
|
+
"terraform.tfstate.backup",
|
|
27
|
+
"prod.tfvars",
|
|
28
|
+
".npmrc",
|
|
29
|
+
".netrc",
|
|
30
|
+
"vault.kdbx",
|
|
31
|
+
".ssh/known_hosts",
|
|
32
|
+
"home/.aws/credentials",
|
|
33
|
+
".kube/config",
|
|
34
|
+
".docker/config.json",
|
|
35
|
+
])("blocks %s", (path) => {
|
|
36
|
+
expect(isSecretLikePath(path)).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("blocks an empty path (fail-closed)", () => {
|
|
40
|
+
expect(isSecretLikePath("")).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("matches basenames case-insensitively", () => {
|
|
44
|
+
expect(isSecretLikePath("Server.KEY")).toBe(true);
|
|
45
|
+
expect(isSecretLikePath(".ENV")).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("handles Windows-style separators", () => {
|
|
49
|
+
expect(isSecretLikePath("config\\.env.local")).toBe(true);
|
|
50
|
+
expect(isSecretLikePath("home\\.ssh\\id_rsa")).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe("isSecretLikePath — captures ordinary ignored files", () => {
|
|
55
|
+
it.each([
|
|
56
|
+
"dist/bundle.js",
|
|
57
|
+
"build/output.txt",
|
|
58
|
+
"node_modules/left-pad/index.js",
|
|
59
|
+
"coverage/report.html",
|
|
60
|
+
"notes.md",
|
|
61
|
+
"src/main.ts",
|
|
62
|
+
"id_rsa.pub", // a PUBLIC key is not a secret
|
|
63
|
+
"environment.ts", // not a .env file
|
|
64
|
+
"keyboard.tsx", // not a *.key file
|
|
65
|
+
".envrc.example",
|
|
66
|
+
])("captures %s", (path) => {
|
|
67
|
+
expect(isSecretLikePath(path)).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe("partitionIgnoredPathsBySecret", () => {
|
|
72
|
+
it("captures ordinary ignored paths and withholds secret-like ones", () => {
|
|
73
|
+
const { capturablePaths, unreviewablePaths } = partitionIgnoredPathsBySecret(
|
|
74
|
+
["cache/data.json", ".env", "dist/app.js", "server.key"],
|
|
75
|
+
new Set(),
|
|
76
|
+
);
|
|
77
|
+
expect(capturablePaths).toEqual(["cache/data.json", "dist/app.js"]);
|
|
78
|
+
expect(unreviewablePaths).toEqual([".env", "server.key"]);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("withholds a secret observed with an EMPTY gate set (global-bypass backstop)", () => {
|
|
82
|
+
// Under spec.auto_approve_all the approval gate is never installed, so the
|
|
83
|
+
// blocked set is empty and this re-check is the ONLY thing keeping a secret's
|
|
84
|
+
// bytes out of durable CAS storage. This is the load-bearing case.
|
|
85
|
+
const { capturablePaths, unreviewablePaths } = partitionIgnoredPathsBySecret(
|
|
86
|
+
[".env"],
|
|
87
|
+
new Set(),
|
|
88
|
+
);
|
|
89
|
+
expect(capturablePaths).toEqual([]);
|
|
90
|
+
expect(unreviewablePaths).toEqual([".env"]);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("unions the gate's blocks with observed secrets, gate blocks first, deduped", () => {
|
|
94
|
+
// The gate blocked `.aws/credentials` up front (it never reached the
|
|
95
|
+
// observer); `.env` was observed and re-caught here; `id_rsa` is both.
|
|
96
|
+
const { capturablePaths, unreviewablePaths } = partitionIgnoredPathsBySecret(
|
|
97
|
+
[".env", "cache/x", "id_rsa"],
|
|
98
|
+
new Set([".aws/credentials", "id_rsa"]),
|
|
99
|
+
);
|
|
100
|
+
expect(capturablePaths).toEqual(["cache/x"]);
|
|
101
|
+
// Gate blocks first (set insertion order), then observed secrets not already present.
|
|
102
|
+
expect(unreviewablePaths).toEqual([".aws/credentials", "id_rsa", ".env"]);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("returns empty partitions for an empty turn", () => {
|
|
106
|
+
const { capturablePaths, unreviewablePaths } = partitionIgnoredPathsBySecret(
|
|
107
|
+
[],
|
|
108
|
+
new Set(),
|
|
109
|
+
);
|
|
110
|
+
expect(capturablePaths).toEqual([]);
|
|
111
|
+
expect(unreviewablePaths).toEqual([]);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("preserves observed order for capturable paths", () => {
|
|
115
|
+
const { capturablePaths } = partitionIgnoredPathsBySecret(
|
|
116
|
+
["cache/b.txt", "cache/a.txt", "logs/c.log"],
|
|
117
|
+
new Set(),
|
|
118
|
+
);
|
|
119
|
+
expect(capturablePaths).toEqual(["cache/b.txt", "cache/a.txt", "logs/c.log"]);
|
|
120
|
+
});
|
|
121
|
+
});
|