niceeval 0.15.1-canary.76 → 0.16.1
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/INDEX.md +45 -40
- package/dist/adapter-execution-trace.cjs.map +1 -1
- package/dist/adapter-execution-trace.d.cts +26 -0
- package/dist/adapter-execution-trace.d.mts +26 -0
- package/dist/adapter-execution-trace.d.ts +26 -0
- package/dist/adapter-usage.cjs.map +1 -1
- package/dist/adapter-usage.d.cts +9 -0
- package/dist/adapter-usage.d.mts +9 -0
- package/dist/adapter-usage.d.ts +9 -0
- package/dist/adapter.cjs +19 -4
- package/dist/adapter.cjs.map +1 -1
- package/dist/adapter.d.cts +37 -12
- package/dist/adapter.d.mts +37 -12
- package/dist/adapter.d.ts +37 -12
- package/dist/agents/ai-sdk.cjs +3 -0
- package/dist/agents/ai-sdk.cjs.map +1 -1
- package/dist/agents/bub.cjs +3 -0
- package/dist/agents/bub.cjs.map +1 -1
- package/dist/agents/claude-code-native.cjs +28 -2
- package/dist/agents/claude-code-native.cjs.map +1 -1
- package/dist/agents/claude-code.cjs +3 -0
- package/dist/agents/claude-code.cjs.map +1 -1
- package/dist/agents/codex.cjs +3 -0
- package/dist/agents/codex.cjs.map +1 -1
- package/dist/agents/coding-cli-versions.cjs +9 -10
- package/dist/agents/coding-cli-versions.cjs.map +1 -1
- package/dist/agents/coding-cli-versions.d.cts +1 -1
- package/dist/agents/coding-cli-versions.d.mts +1 -1
- package/dist/agents/coding-cli-versions.d.ts +1 -1
- package/dist/agents/deepseek-harness.cjs +3 -0
- package/dist/agents/deepseek-harness.cjs.map +1 -1
- package/dist/agents/hermes.cjs +3 -0
- package/dist/agents/hermes.cjs.map +1 -1
- package/dist/agents/index.cjs.map +1 -1
- package/dist/agents/index.d.cts +3 -2
- package/dist/agents/index.d.mts +3 -2
- package/dist/agents/index.d.ts +3 -2
- package/dist/agents/omp.cjs +3 -0
- package/dist/agents/omp.cjs.map +1 -1
- package/dist/agents/openai-compat.cjs +6 -0
- package/dist/agents/openai-compat.cjs.map +1 -1
- package/dist/agents/openclaw.cjs +3 -0
- package/dist/agents/openclaw.cjs.map +1 -1
- package/dist/agents/opencode.cjs +3 -0
- package/dist/agents/opencode.cjs.map +1 -1
- package/dist/agents/sdk-streams.cjs +9 -0
- package/dist/agents/sdk-streams.cjs.map +1 -1
- package/dist/agents/ui-message-stream.cjs +3 -0
- package/dist/agents/ui-message-stream.cjs.map +1 -1
- package/dist/assertions/api.cjs.map +1 -1
- package/dist/assertions/api.d.cts +27 -5
- package/dist/assertions/api.d.mts +27 -5
- package/dist/assertions/api.d.ts +27 -5
- package/dist/assertions/collection.cjs +6 -0
- package/dist/assertions/collection.cjs.map +1 -1
- package/dist/assertions/collection.d.cts +4 -2
- package/dist/assertions/collection.d.mts +4 -2
- package/dist/assertions/collection.d.ts +4 -2
- package/dist/assertions/collection.js +12 -8
- package/dist/assertions/context-match.cjs +70 -0
- package/dist/assertions/context-match.cjs.map +1 -0
- package/dist/assertions/context-match.d.cts +107 -0
- package/dist/assertions/context-match.d.mts +107 -0
- package/dist/assertions/context-match.d.ts +107 -0
- package/dist/assertions/context-match.js +12 -0
- package/dist/assertions/context-match.js.map +1 -0
- package/dist/assertions/full-content.cjs +109 -0
- package/dist/assertions/full-content.cjs.map +1 -0
- package/dist/assertions/full-content.d.cts +6 -0
- package/dist/assertions/full-content.d.mts +6 -0
- package/dist/assertions/full-content.d.ts +6 -0
- package/dist/assertions/full-content.js +12 -0
- package/dist/assertions/full-content.js.map +1 -0
- package/dist/assertions/judge-presets.cjs +2 -34
- package/dist/assertions/judge-presets.cjs.map +1 -1
- package/dist/assertions/judge-presets.d.cts +6 -9
- package/dist/assertions/judge-presets.d.mts +6 -9
- package/dist/assertions/judge-presets.d.ts +6 -9
- package/dist/assertions/judge-presets.js +8 -8
- package/dist/assertions/judge.cjs +107 -29
- package/dist/assertions/judge.cjs.map +1 -1
- package/dist/assertions/judge.d.cts +2 -0
- package/dist/assertions/judge.d.mts +2 -0
- package/dist/assertions/judge.d.ts +2 -0
- package/dist/assertions/match.cjs +244 -5
- package/dist/assertions/match.cjs.map +1 -1
- package/dist/assertions/match.d.cts +43 -3
- package/dist/assertions/match.d.mts +43 -3
- package/dist/assertions/match.d.ts +43 -3
- package/dist/assertions/match.js +88 -78
- package/dist/assertions/material.cjs +520 -0
- package/dist/assertions/material.cjs.map +1 -0
- package/dist/assertions/material.d.cts +56 -0
- package/dist/assertions/material.d.mts +56 -0
- package/dist/assertions/material.d.ts +56 -0
- package/dist/assertions/material.js +20 -0
- package/dist/assertions/material.js.map +1 -0
- package/dist/assertions/record/attachment.cjs +5 -2
- package/dist/assertions/record/attachment.cjs.map +1 -1
- package/dist/assertions/record/codec.cjs +2 -1
- package/dist/assertions/record/codec.cjs.map +1 -1
- package/dist/assertions/record/model.cjs.map +1 -1
- package/dist/assertions/record/model.d.cts +6 -1
- package/dist/assertions/record/model.d.mts +6 -1
- package/dist/assertions/record/model.d.ts +6 -1
- package/dist/assertions/runtime.cjs +207 -14
- package/dist/assertions/runtime.cjs.map +1 -1
- package/dist/assertions/runtime.d.cts +9 -0
- package/dist/assertions/runtime.d.mts +9 -0
- package/dist/assertions/runtime.d.ts +9 -0
- package/dist/assertions/runtime.js +14 -8
- package/dist/assertions/score-match-audit.cjs +18 -8
- package/dist/assertions/score-match-audit.cjs.map +1 -1
- package/dist/assertions/score-match-gateway.cjs +83 -25
- package/dist/assertions/score-match-gateway.cjs.map +1 -1
- package/dist/assertions/score-match-gateway.d.cts +2 -0
- package/dist/assertions/score-match-gateway.d.mts +2 -0
- package/dist/assertions/score-match-gateway.d.ts +2 -0
- package/dist/assertions/value-composition.test-d.cjs +77 -0
- package/dist/assertions/value-composition.test-d.cjs.map +1 -0
- package/dist/assertions/value-composition.test-d.d.cts +1 -0
- package/dist/assertions/value-composition.test-d.d.mts +1 -0
- package/dist/assertions/value-composition.test-d.d.ts +1 -0
- package/dist/assertions/value-composition.test-d.js +4 -0
- package/dist/assertions/value-composition.test-d.js.map +1 -0
- package/dist/context/assert-first.cjs +207 -213
- package/dist/context/assert-first.cjs.map +1 -1
- package/dist/context/assert-first.d.cts +49 -23
- package/dist/context/assert-first.d.mts +49 -23
- package/dist/context/assert-first.d.ts +49 -23
- package/dist/context/session.cjs +111 -9
- package/dist/context/session.cjs.map +1 -1
- package/dist/context/session.d.cts +18 -0
- package/dist/context/session.d.mts +18 -0
- package/dist/context/session.d.ts +18 -0
- package/dist/define.cjs +17 -1
- package/dist/define.cjs.map +1 -1
- package/dist/eval/cli/contribution.cjs +2 -0
- package/dist/eval/cli/contribution.cjs.map +1 -1
- package/dist/eval/host/index.cjs +2 -0
- package/dist/eval/host/index.cjs.map +1 -1
- package/dist/eval/record/evaluation.cjs +3 -0
- package/dist/eval/record/evaluation.cjs.map +1 -1
- package/dist/eval/record/membership-provenance.d.cts +15 -15
- package/dist/eval/record/membership-provenance.d.mts +15 -15
- package/dist/eval/record/membership-provenance.d.ts +15 -15
- package/dist/eval/record/score.cjs +7 -0
- package/dist/eval/record/score.cjs.map +1 -1
- package/dist/eval/record/sealed-assertion.cjs +3 -0
- package/dist/eval/record/sealed-assertion.cjs.map +1 -1
- package/dist/eval/record/verdict.cjs +7 -0
- package/dist/eval/record/verdict.cjs.map +1 -1
- package/dist/expect/index.cjs +8 -2
- package/dist/expect/index.cjs.map +1 -1
- package/dist/expect/index.d.cts +9 -4
- package/dist/expect/index.d.mts +9 -4
- package/dist/expect/index.d.ts +9 -4
- package/dist/expect/index.js +72 -62
- package/dist/expect/index.mjs +72 -62
- package/dist/experiment/host/cli/contribution.cjs +37 -23
- package/dist/experiment/host/cli/contribution.cjs.map +1 -1
- package/dist/experiment/host/cli/contribution.d.cts +9 -1
- package/dist/experiment/host/cli/contribution.d.mts +9 -1
- package/dist/experiment/host/cli/contribution.d.ts +9 -1
- package/dist/experiment/host/current.cjs +74 -0
- package/dist/experiment/host/current.cjs.map +1 -0
- package/dist/experiment/host/current.d.cts +33 -0
- package/dist/experiment/host/current.d.mts +33 -0
- package/dist/experiment/host/current.d.ts +33 -0
- package/dist/experiment/host/current.js +10 -0
- package/dist/experiment/host/current.js.map +1 -0
- package/dist/experiment/host/index.cjs +17 -174
- package/dist/experiment/host/index.cjs.map +1 -1
- package/dist/experiment/host/index.d.cts +8 -54
- package/dist/experiment/host/index.d.mts +8 -54
- package/dist/experiment/host/index.d.ts +8 -54
- package/dist/experiment/host/operations.cjs +33 -10
- package/dist/experiment/host/operations.cjs.map +1 -1
- package/dist/experiment/host/operations.d.cts +29 -1
- package/dist/experiment/host/operations.d.mts +29 -1
- package/dist/experiment/host/operations.d.ts +29 -1
- package/dist/experiment/host/operations.js +6 -4
- package/dist/experiment/host/requirements.cjs +3 -0
- package/dist/experiment/host/requirements.cjs.map +1 -0
- package/dist/experiment/host/requirements.d.cts +3 -0
- package/dist/experiment/host/requirements.d.mts +3 -0
- package/dist/experiment/host/requirements.d.ts +3 -0
- package/dist/experiment/host/requirements.js +4 -0
- package/dist/experiment/host/requirements.js.map +1 -0
- package/dist/experiment/host/runtime.cjs +179 -0
- package/dist/experiment/host/runtime.cjs.map +1 -0
- package/dist/experiment/host/runtime.d.cts +79 -0
- package/dist/experiment/host/runtime.d.mts +79 -0
- package/dist/experiment/host/runtime.d.ts +79 -0
- package/dist/experiment/host/runtime.js +6 -0
- package/dist/experiment/host/runtime.js.map +1 -0
- package/dist/experiment/host/types.cjs.map +1 -1
- package/dist/experiment/host/types.d.cts +17 -16
- package/dist/experiment/host/types.d.mts +17 -16
- package/dist/experiment/host/types.d.ts +17 -16
- package/dist/fresh-import.cjs +2 -1
- package/dist/fresh-import.cjs.map +1 -1
- package/dist/index.cjs +4 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -6
- package/dist/index.d.mts +12 -6
- package/dist/index.d.ts +12 -6
- package/dist/index.js +32 -30
- package/dist/index.mjs +32 -30
- package/dist/inspection/artifacts.cjs +83 -54
- package/dist/inspection/artifacts.cjs.map +1 -1
- package/dist/inspection/artifacts.d.cts +8 -0
- package/dist/inspection/artifacts.d.mts +8 -0
- package/dist/inspection/artifacts.d.ts +8 -0
- package/dist/inspection/artifacts.js +2 -0
- package/dist/inspection/assertions.cjs +4 -1
- package/dist/inspection/assertions.cjs.map +1 -1
- package/dist/inspection/cli/contribution.cjs +27 -4
- package/dist/inspection/cli/contribution.cjs.map +1 -1
- package/dist/inspection/cli/contribution.d.cts +2 -1
- package/dist/inspection/cli/contribution.d.mts +2 -1
- package/dist/inspection/cli/contribution.d.ts +2 -1
- package/dist/inspection/execution-traces.cjs +114 -8
- package/dist/inspection/execution-traces.cjs.map +1 -1
- package/dist/inspection/execution-traces.d.cts +38 -0
- package/dist/inspection/execution-traces.d.mts +38 -0
- package/dist/inspection/execution-traces.d.ts +38 -0
- package/dist/inspection/facts.cjs +12 -0
- package/dist/inspection/facts.cjs.map +1 -1
- package/dist/inspection/limits.cjs +5 -1
- package/dist/inspection/limits.cjs.map +1 -1
- package/dist/inspection/limits.d.cts +4 -0
- package/dist/inspection/limits.d.mts +4 -0
- package/dist/inspection/limits.d.ts +4 -0
- package/dist/inspection/limits.js +6 -2
- package/dist/inspection/overview.cjs +74 -33
- package/dist/inspection/overview.cjs.map +1 -1
- package/dist/inspection/overview.d.cts +19 -1
- package/dist/inspection/overview.d.mts +19 -1
- package/dist/inspection/overview.d.ts +19 -1
- package/dist/inspection/overview.js +4 -2
- package/dist/inspection/project-input.cjs +9 -0
- package/dist/inspection/project-input.cjs.map +1 -0
- package/dist/inspection/project-input.d.cts +20 -0
- package/dist/inspection/project-input.d.mts +20 -0
- package/dist/inspection/project-input.d.ts +20 -0
- package/dist/inspection/project-input.js +8 -0
- package/dist/inspection/project-input.js.map +1 -0
- package/dist/inspection/project-result.cjs +60 -0
- package/dist/inspection/project-result.cjs.map +1 -0
- package/dist/inspection/project-result.d.cts +1264 -0
- package/dist/inspection/project-result.d.mts +1264 -0
- package/dist/inspection/project-result.d.ts +1264 -0
- package/dist/inspection/project-result.js +10 -0
- package/dist/inspection/project-result.js.map +1 -0
- package/dist/inspection/project-source.cjs +109 -0
- package/dist/inspection/project-source.cjs.map +1 -0
- package/dist/inspection/project-source.d.cts +10 -0
- package/dist/inspection/project-source.d.mts +10 -0
- package/dist/inspection/project-source.d.ts +10 -0
- package/dist/inspection/project-source.js +6 -0
- package/dist/inspection/project-source.js.map +1 -0
- package/dist/inspection/project.cjs +100 -0
- package/dist/inspection/project.cjs.map +1 -0
- package/dist/inspection/project.d.cts +3 -0
- package/dist/inspection/project.d.mts +3 -0
- package/dist/inspection/project.d.ts +3 -0
- package/dist/inspection/project.js +6 -0
- package/dist/inspection/project.js.map +1 -0
- package/dist/inspection/protocol.cjs +19 -9
- package/dist/inspection/protocol.cjs.map +1 -1
- package/dist/inspection/protocol.d.cts +5506 -38189
- package/dist/inspection/protocol.d.mts +5506 -38189
- package/dist/inspection/protocol.d.ts +5506 -38189
- package/dist/inspection/public.mjs +896 -84
- package/dist/inspection/query.cjs +2 -0
- package/dist/inspection/query.cjs.map +1 -1
- package/dist/inspection/results.cjs +234 -29
- package/dist/inspection/results.cjs.map +1 -1
- package/dist/inspection/results.d.cts +870 -13
- package/dist/inspection/results.d.mts +870 -13
- package/dist/inspection/results.d.ts +870 -13
- package/dist/inspection/results.js +36 -34
- package/dist/inspection/select.cjs +126 -14
- package/dist/inspection/select.cjs.map +1 -1
- package/dist/inspection/select.d.cts +1 -1
- package/dist/inspection/select.d.mts +1 -1
- package/dist/inspection/select.d.ts +1 -1
- package/dist/inspection/source.cjs +4 -0
- package/dist/inspection/source.cjs.map +1 -1
- package/dist/inspection/sources.cjs +5 -0
- package/dist/inspection/sources.cjs.map +1 -1
- package/dist/inspection/trace.cjs +82 -84
- package/dist/inspection/trace.cjs.map +1 -1
- package/dist/inspection/trace.d.cts +7 -2
- package/dist/inspection/trace.d.mts +7 -2
- package/dist/inspection/trace.d.ts +7 -2
- package/dist/model-slots.cjs +35 -0
- package/dist/model-slots.cjs.map +1 -0
- package/dist/model-slots.d.cts +19 -0
- package/dist/model-slots.d.mts +19 -0
- package/dist/model-slots.d.ts +19 -0
- package/dist/model-slots.js +8 -0
- package/dist/model-slots.js.map +1 -0
- package/dist/o11y/adapter-call-price.cjs +4 -0
- package/dist/o11y/adapter-call-price.cjs.map +1 -1
- package/dist/o11y/adapter-call-price.d.cts +24 -0
- package/dist/o11y/adapter-call-price.d.mts +24 -0
- package/dist/o11y/adapter-call-price.d.ts +24 -0
- package/dist/o11y/adapter-call-price.js +6 -2
- package/dist/o11y/adapter-usage-projection.cjs +158 -0
- package/dist/o11y/adapter-usage-projection.cjs.map +1 -0
- package/dist/o11y/adapter-usage-projection.d.cts +141 -0
- package/dist/o11y/adapter-usage-projection.d.mts +141 -0
- package/dist/o11y/adapter-usage-projection.d.ts +141 -0
- package/dist/o11y/adapter-usage-projection.js +20 -0
- package/dist/o11y/adapter-usage-projection.js.map +1 -0
- package/dist/o11y/eval-usage.cjs +149 -0
- package/dist/o11y/eval-usage.cjs.map +1 -0
- package/dist/o11y/eval-usage.d.cts +46 -0
- package/dist/o11y/eval-usage.d.mts +46 -0
- package/dist/o11y/eval-usage.d.ts +46 -0
- package/dist/o11y/eval-usage.js +8 -0
- package/dist/o11y/eval-usage.js.map +1 -0
- package/dist/o11y/judge-call-price.cjs +62 -0
- package/dist/o11y/judge-call-price.cjs.map +1 -0
- package/dist/o11y/judge-call-price.d.cts +3 -0
- package/dist/o11y/judge-call-price.d.mts +3 -0
- package/dist/o11y/judge-call-price.d.ts +3 -0
- package/dist/o11y/judge-call-price.js +6 -0
- package/dist/o11y/judge-call-price.js.map +1 -0
- package/dist/o11y/judge-usage-projection.cjs +134 -0
- package/dist/o11y/judge-usage-projection.cjs.map +1 -0
- package/dist/o11y/judge-usage-projection.d.cts +18 -0
- package/dist/o11y/judge-usage-projection.d.mts +18 -0
- package/dist/o11y/judge-usage-projection.d.ts +18 -0
- package/dist/o11y/judge-usage-projection.js +20 -0
- package/dist/o11y/judge-usage-projection.js.map +1 -0
- package/dist/o11y/judge-usage-receipt.cjs +135 -0
- package/dist/o11y/judge-usage-receipt.cjs.map +1 -0
- package/dist/o11y/judge-usage-receipt.d.cts +5 -0
- package/dist/o11y/judge-usage-receipt.d.mts +5 -0
- package/dist/o11y/judge-usage-receipt.d.ts +5 -0
- package/dist/o11y/judge-usage-receipt.js +6 -0
- package/dist/o11y/judge-usage-receipt.js.map +1 -0
- package/dist/o11y/judge-usage.cjs +171 -0
- package/dist/o11y/judge-usage.cjs.map +1 -0
- package/dist/o11y/judge-usage.d.cts +35 -0
- package/dist/o11y/judge-usage.d.mts +35 -0
- package/dist/o11y/judge-usage.d.ts +35 -0
- package/dist/o11y/judge-usage.js +6 -0
- package/dist/o11y/judge-usage.js.map +1 -0
- package/dist/plugin/contracts.cjs +2 -0
- package/dist/plugin/contracts.cjs.map +1 -1
- package/dist/plugin/link.cjs +7 -0
- package/dist/plugin/link.cjs.map +1 -1
- package/dist/record/attachment/protocol.cjs +8 -6
- package/dist/record/attachment/protocol.cjs.map +1 -1
- package/dist/record/attachment/protocol.d.cts +10 -0
- package/dist/record/attachment/protocol.d.mts +10 -0
- package/dist/record/attachment/protocol.d.ts +10 -0
- package/dist/record/attachment/protocol.js +38 -36
- package/dist/record/codec/seal-manifest.d.cts +3 -3
- package/dist/record/codec/seal-manifest.d.mts +3 -3
- package/dist/record/codec/seal-manifest.d.ts +3 -3
- package/dist/record/family/adapter-usage/definition.d.cts +1 -0
- package/dist/record/family/adapter-usage/definition.d.mts +1 -0
- package/dist/record/family/adapter-usage/definition.d.ts +1 -0
- package/dist/record/family/adapter-usage/persistence.cjs +1 -1
- package/dist/record/family/adapter-usage/persistence.cjs.map +1 -1
- package/dist/record/family/adapter-usage/persistence.d.cts +2 -1
- package/dist/record/family/adapter-usage/persistence.d.mts +2 -1
- package/dist/record/family/adapter-usage/persistence.d.ts +2 -1
- package/dist/record/family/adapter-usage/schema.cjs +21 -2
- package/dist/record/family/adapter-usage/schema.cjs.map +1 -1
- package/dist/record/family/adapter-usage/schema.d.cts +85 -0
- package/dist/record/family/adapter-usage/schema.d.mts +85 -0
- package/dist/record/family/adapter-usage/schema.d.ts +85 -0
- package/dist/record/family/adapter-usage/schema.js +18 -12
- package/dist/record/family/catalog.cjs +31 -27
- package/dist/record/family/catalog.cjs.map +1 -1
- package/dist/record/family/catalog.d.cts +94 -3
- package/dist/record/family/catalog.d.mts +94 -3
- package/dist/record/family/catalog.d.ts +94 -3
- package/dist/record/family/current.cjs +45 -42
- package/dist/record/family/current.cjs.map +1 -1
- package/dist/record/family/current.d.cts +240 -1
- package/dist/record/family/current.d.mts +240 -1
- package/dist/record/family/current.d.ts +240 -1
- package/dist/record/family/execution-traces/definition.d.cts +26 -0
- package/dist/record/family/execution-traces/definition.d.mts +26 -0
- package/dist/record/family/execution-traces/definition.d.ts +26 -0
- package/dist/record/family/execution-traces/definition.js +20 -14
- package/dist/record/family/execution-traces/schema.cjs +57 -1
- package/dist/record/family/execution-traces/schema.cjs.map +1 -1
- package/dist/record/family/execution-traces/schema.d.cts +114 -0
- package/dist/record/family/execution-traces/schema.d.mts +114 -0
- package/dist/record/family/execution-traces/schema.d.ts +114 -0
- package/dist/record/family/execution-traces/schema.js +18 -12
- package/dist/record/family/judge-usage/definition.cjs +12 -0
- package/dist/record/family/judge-usage/definition.cjs.map +1 -0
- package/dist/record/family/judge-usage/definition.d.cts +91 -0
- package/dist/record/family/judge-usage/definition.d.mts +91 -0
- package/dist/record/family/judge-usage/definition.d.ts +91 -0
- package/dist/record/family/judge-usage/definition.js +6 -0
- package/dist/record/family/judge-usage/definition.js.map +1 -0
- package/dist/record/family/judge-usage/persistence.cjs +10 -0
- package/dist/record/family/judge-usage/persistence.cjs.map +1 -0
- package/dist/record/family/judge-usage/persistence.d.cts +91 -0
- package/dist/record/family/judge-usage/persistence.d.mts +91 -0
- package/dist/record/family/judge-usage/persistence.d.ts +91 -0
- package/dist/record/family/judge-usage/persistence.js +6 -0
- package/dist/record/family/judge-usage/persistence.js.map +1 -0
- package/dist/record/family/judge-usage/schema.cjs +307 -0
- package/dist/record/family/judge-usage/schema.cjs.map +1 -0
- package/dist/record/family/judge-usage/schema.d.cts +351 -0
- package/dist/record/family/judge-usage/schema.d.mts +351 -0
- package/dist/record/family/judge-usage/schema.d.ts +351 -0
- package/dist/record/family/judge-usage/schema.js +18 -0
- package/dist/record/family/judge-usage/schema.js.map +1 -0
- package/dist/record/host/sqlite-host.cjs +55 -9
- package/dist/record/host/sqlite-host.cjs.map +1 -1
- package/dist/record/host/types.cjs.map +1 -1
- package/dist/record/host/types.d.cts +7 -0
- package/dist/record/host/types.d.mts +7 -0
- package/dist/record/host/types.d.ts +7 -0
- package/dist/record/model/run-context.cjs +7 -1
- package/dist/record/model/run-context.cjs.map +1 -1
- package/dist/record/model/run-context.d.cts +4 -0
- package/dist/record/model/run-context.d.mts +4 -0
- package/dist/record/model/run-context.d.ts +4 -0
- package/dist/record/model/seal-manifest.d.cts +1 -1
- package/dist/record/model/seal-manifest.d.mts +1 -1
- package/dist/record/model/seal-manifest.d.ts +1 -1
- package/dist/record/sqlite/client.cjs +18 -5
- package/dist/record/sqlite/client.cjs.map +1 -1
- package/dist/record/sqlite/client.d.cts +1 -1
- package/dist/record/sqlite/client.d.mts +1 -1
- package/dist/record/sqlite/client.d.ts +1 -1
- package/dist/record/sqlite/project-state-database.cjs +29 -8
- package/dist/record/sqlite/project-state-database.cjs.map +1 -1
- package/dist/run/host/runtime.cjs +4 -0
- package/dist/run/host/runtime.cjs.map +1 -1
- package/dist/run/storage/sqlite.cjs +20 -0
- package/dist/run/storage/sqlite.cjs.map +1 -1
- package/dist/runner/adapter-execution-trace.cjs +125 -2
- package/dist/runner/adapter-execution-trace.cjs.map +1 -1
- package/dist/runner/adapter-execution-trace.d.cts +1 -1
- package/dist/runner/adapter-execution-trace.d.mts +1 -1
- package/dist/runner/adapter-execution-trace.d.ts +1 -1
- package/dist/runner/adapter-usage.cjs +55 -7
- package/dist/runner/adapter-usage.cjs.map +1 -1
- package/dist/runner/adapter-usage.d.cts +9 -2
- package/dist/runner/adapter-usage.d.mts +9 -2
- package/dist/runner/adapter-usage.d.ts +9 -2
- package/dist/runner/adoption.cjs +9 -0
- package/dist/runner/adoption.cjs.map +1 -1
- package/dist/runner/attempt-cancellation.cjs +12 -0
- package/dist/runner/attempt-cancellation.cjs.map +1 -0
- package/dist/runner/attempt-cancellation.d.cts +6 -0
- package/dist/runner/attempt-cancellation.d.mts +6 -0
- package/dist/runner/attempt-cancellation.d.ts +6 -0
- package/dist/runner/attempt-cancellation.js +6 -0
- package/dist/runner/attempt-cancellation.js.map +1 -0
- package/dist/runner/attempt-resources.cjs +10 -4
- package/dist/runner/attempt-resources.cjs.map +1 -1
- package/dist/runner/attempt-resources.d.cts +2 -1
- package/dist/runner/attempt-resources.d.mts +2 -1
- package/dist/runner/attempt-resources.d.ts +2 -1
- package/dist/runner/attempt.cjs +1177 -1139
- package/dist/runner/attempt.cjs.map +1 -1
- package/dist/runner/config-identity.cjs +20 -1
- package/dist/runner/config-identity.cjs.map +1 -1
- package/dist/runner/config-identity.d.cts +5 -0
- package/dist/runner/config-identity.d.mts +5 -0
- package/dist/runner/config-identity.d.ts +5 -0
- package/dist/runner/discover.cjs +14 -0
- package/dist/runner/discover.cjs.map +1 -1
- package/dist/runner/discover.d.cts +3 -0
- package/dist/runner/discover.d.mts +3 -0
- package/dist/runner/discover.d.ts +3 -0
- package/dist/runner/feedback/human.cjs +9 -4
- package/dist/runner/feedback/human.cjs.map +1 -1
- package/dist/runner/fingerprint.cjs +1 -0
- package/dist/runner/fingerprint.cjs.map +1 -1
- package/dist/runner/judge-usage.cjs +18 -0
- package/dist/runner/judge-usage.cjs.map +1 -0
- package/dist/runner/judge-usage.d.cts +5 -0
- package/dist/runner/judge-usage.d.mts +5 -0
- package/dist/runner/judge-usage.d.ts +5 -0
- package/dist/runner/judge-usage.js +8 -0
- package/dist/runner/judge-usage.js.map +1 -0
- package/dist/runner/project-target.cjs.map +1 -1
- package/dist/runner/project-target.d.cts +2 -0
- package/dist/runner/project-target.d.mts +2 -0
- package/dist/runner/project-target.d.ts +2 -0
- package/dist/runner/record/planning.cjs +1 -0
- package/dist/runner/record/planning.cjs.map +1 -1
- package/dist/runner/record.cjs +4 -0
- package/dist/runner/record.cjs.map +1 -1
- package/dist/runner/reporters/braintrust.cjs +2 -0
- package/dist/runner/reporters/braintrust.cjs.map +1 -1
- package/dist/runner/reuse-diagnostics.cjs +9 -10
- package/dist/runner/reuse-diagnostics.cjs.map +1 -1
- package/dist/runner/reuse-diagnostics.d.cts +5 -6
- package/dist/runner/reuse-diagnostics.d.mts +5 -6
- package/dist/runner/reuse-diagnostics.d.ts +5 -6
- package/dist/runner/reuse-diagnostics.js +4 -4
- package/dist/runner/reuse-plan.cjs +100 -48
- package/dist/runner/reuse-plan.cjs.map +1 -1
- package/dist/runner/reuse-plan.d.cts +60 -3
- package/dist/runner/reuse-plan.d.mts +60 -3
- package/dist/runner/reuse-plan.d.ts +60 -3
- package/dist/runner/reuse-plan.js +14 -10
- package/dist/runner/run.cjs +8 -6
- package/dist/runner/run.cjs.map +1 -1
- package/dist/runner/session.cjs +1 -1
- package/dist/runner/session.cjs.map +1 -1
- package/dist/runner/session.d.cts +1 -1
- package/dist/runner/session.d.mts +1 -1
- package/dist/runner/session.d.ts +1 -1
- package/dist/runner/source-producer.cjs +3 -1
- package/dist/runner/source-producer.cjs.map +1 -1
- package/dist/runner/types.cjs.map +1 -1
- package/dist/runner/types.d.cts +26 -5
- package/dist/runner/types.d.mts +26 -5
- package/dist/runner/types.d.ts +26 -5
- package/dist/sandbox/action.cjs +3 -0
- package/dist/sandbox/action.cjs.map +1 -1
- package/dist/sandbox/commands.cjs +3 -0
- package/dist/sandbox/commands.cjs.map +1 -1
- package/dist/sandbox/compose.cjs +6 -0
- package/dist/sandbox/compose.cjs.map +1 -1
- package/dist/sandbox/docker-profile/registry.cjs +7 -0
- package/dist/sandbox/docker-profile/registry.cjs.map +1 -1
- package/dist/sandbox/docker-profile/runtime.cjs +3 -0
- package/dist/sandbox/docker-profile/runtime.cjs.map +1 -1
- package/dist/sandbox/docker-profile/schema.cjs +7 -0
- package/dist/sandbox/docker-profile/schema.cjs.map +1 -1
- package/dist/sandbox/incus.cjs +7 -0
- package/dist/sandbox/incus.cjs.map +1 -1
- package/dist/sandbox/layer.cjs +12 -0
- package/dist/sandbox/layer.cjs.map +1 -1
- package/dist/shared/adapter-cleanup.cjs +18 -0
- package/dist/shared/adapter-cleanup.cjs.map +1 -0
- package/dist/shared/adapter-cleanup.d.cts +4 -0
- package/dist/shared/adapter-cleanup.d.mts +4 -0
- package/dist/shared/adapter-cleanup.d.ts +4 -0
- package/dist/shared/adapter-cleanup.js +10 -0
- package/dist/shared/adapter-cleanup.js.map +1 -0
- package/dist/shared/attempt-lifecycle.cjs +3 -0
- package/dist/shared/attempt-lifecycle.cjs.map +1 -0
- package/dist/shared/attempt-lifecycle.d.cts +13 -0
- package/dist/shared/attempt-lifecycle.d.mts +13 -0
- package/dist/shared/attempt-lifecycle.d.ts +13 -0
- package/dist/shared/attempt-lifecycle.js +4 -0
- package/dist/shared/attempt-lifecycle.js.map +1 -0
- package/dist/show/contribution.cjs +52 -14
- package/dist/show/contribution.cjs.map +1 -1
- package/dist/show/contribution.d.cts +23 -1
- package/dist/show/contribution.d.mts +23 -1
- package/dist/show/contribution.d.ts +23 -1
- package/dist/show/model.cjs +8 -1
- package/dist/show/model.cjs.map +1 -1
- package/dist/show/model.d.cts +10 -1
- package/dist/show/model.d.mts +10 -1
- package/dist/show/model.d.ts +10 -1
- package/dist/show/render.cjs +287 -49
- package/dist/show/render.cjs.map +1 -1
- package/dist/show/render.d.cts +3 -0
- package/dist/show/render.d.mts +3 -0
- package/dist/show/render.d.ts +3 -0
- package/dist/sources/projector.cjs +3 -0
- package/dist/sources/projector.cjs.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +1 -1
- package/dist/types.d.mts +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/view/app-dist/.vite/manifest.json +2 -2
- package/dist/view/app-dist/assets/index-CmVoXbwG.js +63 -0
- package/dist/view/app-dist/assets/{quality-cost-scatter-CFB3wnkN.js → quality-cost-scatter-BuLISaH4.js} +1 -1
- package/dist/view/app-dist/index.html +1 -1
- package/dist/view/cli/contribution.cjs +54 -21
- package/dist/view/cli/contribution.cjs.map +1 -1
- package/dist/view/cli/contribution.d.cts +2 -1
- package/dist/view/cli/contribution.d.mts +2 -1
- package/dist/view/cli/contribution.d.ts +2 -1
- package/dist/view/function-runtime.mjs +13231 -11647
- package/dist/view/http-protocol.cjs +3 -0
- package/dist/view/http-protocol.cjs.map +1 -1
- package/dist/view/http-protocol.d.cts +8 -0
- package/dist/view/http-protocol.d.mts +8 -0
- package/dist/view/http-protocol.d.ts +8 -0
- package/dist/view/inspection-host.cjs +17 -1
- package/dist/view/inspection-host.cjs.map +1 -1
- package/dist/view/render.cjs +1 -0
- package/dist/view/render.cjs.map +1 -1
- package/dist/view/render.d.cts +2 -1
- package/dist/view/render.d.mts +2 -1
- package/dist/view/render.d.ts +2 -1
- package/dist/view/revision.cjs.map +1 -1
- package/dist/view/revision.d.cts +12 -0
- package/dist/view/revision.d.mts +12 -0
- package/dist/view/revision.d.ts +12 -0
- package/dist/view/server.cjs +10 -9
- package/dist/view/server.cjs.map +1 -1
- package/docs-site/zh/examples/ai-agent-application.mdx +1 -1
- package/docs-site/zh/explanation/adapter.mdx +2 -2
- package/docs-site/zh/explanation/assert.mdx +60 -24
- package/docs-site/zh/explanation/drive.mdx +1 -1
- package/docs-site/zh/explanation/evals.mdx +31 -29
- package/docs-site/zh/explanation/experiment.mdx +15 -15
- package/docs-site/zh/explanation/judge.mdx +23 -15
- package/docs-site/zh/explanation/overview.mdx +44 -21
- package/docs-site/zh/explanation/record.mdx +28 -13
- package/docs-site/zh/explanation/results-flow.mdx +37 -0
- package/docs-site/zh/explanation/runner.mdx +27 -6
- package/docs-site/zh/index.mdx +22 -115
- package/docs-site/zh/introduction.mdx +89 -61
- package/docs-site/zh/reference/cli.mdx +50 -26
- package/docs-site/zh/reference/define-agent.mdx +37 -5
- package/docs-site/zh/reference/define-eval.mdx +130 -29
- package/docs-site/zh/reference/define-experiment.mdx +251 -0
- package/docs-site/zh/reference/events.mdx +34 -30
- package/docs-site/zh/reference/expect.mdx +77 -2
- package/docs-site/zh/reference/official-adapters.mdx +2 -2
- package/docs-site/zh/troubleshooting/debug-sandbox.mdx +43 -24
- package/docs-site/zh/troubleshooting/debugging.mdx +65 -42
- package/docs-site/zh/troubleshooting/recover-after-kill.mdx +35 -28
- package/docs-site/zh/tutorials/accept-results.mdx +34 -24
- package/docs-site/zh/tutorials/agent-feedback-loop.mdx +74 -43
- package/docs-site/zh/tutorials/agent-onboarding.mdx +27 -20
- package/docs-site/zh/tutorials/authoring.mdx +138 -121
- package/docs-site/zh/tutorials/ci-integration.mdx +46 -22
- package/docs-site/zh/tutorials/compare-runs.mdx +40 -13
- package/docs-site/zh/tutorials/concurrency.mdx +66 -62
- package/docs-site/zh/tutorials/configuration.mdx +17 -17
- package/docs-site/zh/tutorials/connect-otel.mdx +48 -37
- package/docs-site/zh/tutorials/connect-your-agent.mdx +80 -173
- package/docs-site/zh/tutorials/control-run-cost.mdx +63 -12
- package/docs-site/zh/tutorials/criteria-files.mdx +28 -21
- package/docs-site/zh/tutorials/custom-application.mdx +75 -55
- package/docs-site/zh/tutorials/dataset-fanout.mdx +32 -22
- package/docs-site/zh/tutorials/debug-lifecycle-plan.mdx +38 -31
- package/docs-site/zh/tutorials/docker-in-docker.mdx +83 -89
- package/docs-site/zh/tutorials/eval-groups.mdx +70 -89
- package/docs-site/zh/tutorials/evaluation-kinds.mdx +12 -5
- package/docs-site/zh/tutorials/experiment-services.mdx +243 -0
- package/docs-site/zh/tutorials/fixtures.mdx +42 -26
- package/docs-site/zh/tutorials/handle-execution-failures.mdx +41 -19
- package/docs-site/zh/tutorials/install-custom-sandbox-agent.mdx +21 -16
- package/docs-site/zh/tutorials/niceeval-taste-application.mdx +394 -0
- package/docs-site/zh/tutorials/niceeval-taste.mdx +190 -0
- package/docs-site/zh/tutorials/nixos-managed-dind.mdx +51 -39
- package/docs-site/zh/tutorials/plugins.mdx +55 -31
- package/docs-site/zh/tutorials/prebuilt-sandbox.mdx +204 -0
- package/docs-site/zh/tutorials/quickstart.mdx +91 -47
- package/docs-site/zh/tutorials/reporters.mdx +45 -30
- package/docs-site/zh/tutorials/rerun-and-cache.mdx +39 -40
- package/docs-site/zh/tutorials/sandbox-agent.mdx +65 -40
- package/docs-site/zh/tutorials/sandbox-providers.mdx +104 -282
- package/docs-site/zh/tutorials/sandbox-reuse.mdx +65 -97
- package/docs-site/zh/tutorials/select-and-preview.mdx +25 -20
- package/docs-site/zh/tutorials/verify-judge.mdx +49 -26
- package/docs-site/zh/tutorials/viewing-results.mdx +130 -80
- package/docs-site/zh/tutorials/write-experiment.mdx +116 -298
- package/docs-site/zh/tutorials/write-send.mdx +53 -52
- package/node_modules/undici/docs/docs/api/Agent.md +8 -8
- package/node_modules/undici/docs/docs/api/DiagnosticsChannel.md +9 -6
- package/node_modules/undici/docs/docs/api/Errors.md +25 -0
- package/node_modules/undici/docs/docs/api/Interceptors.md +4 -3
- package/node_modules/undici/docs/docs/api/Pool.md +5 -6
- package/node_modules/undici/lib/api/readable.js +1 -2
- package/node_modules/undici/lib/core/request.js +23 -2
- package/node_modules/undici/lib/core/util.js +0 -39
- package/node_modules/undici/lib/dispatcher/client-h1.js +11 -4
- package/node_modules/undici/lib/dispatcher/client-h2.js +52 -8
- package/node_modules/undici/lib/dispatcher/client.js +1 -1
- package/node_modules/undici/lib/dispatcher/dispatcher1-wrapper.js +3 -2
- package/node_modules/undici/lib/dispatcher/env-http-proxy-agent.js +21 -6
- package/node_modules/undici/lib/dispatcher/pool-base.js +63 -2
- package/node_modules/undici/lib/dispatcher/pool.js +90 -7
- package/node_modules/undici/lib/dispatcher/proxy-agent.js +16 -3
- package/node_modules/undici/lib/handler/deduplication-handler.js +24 -11
- package/node_modules/undici/lib/handler/redirect-handler.js +5 -0
- package/node_modules/undici/lib/handler/retry-handler.js +72 -13
- package/node_modules/undici/lib/interceptor/cache.js +4 -1
- package/node_modules/undici/lib/interceptor/decompress.js +236 -69
- package/node_modules/undici/lib/web/cache/cache.js +17 -8
- package/node_modules/undici/lib/web/cache/cachestorage.js +11 -5
- package/node_modules/undici/lib/web/cookies/index.js +19 -7
- package/node_modules/undici/lib/web/cookies/util.js +3 -1
- package/node_modules/undici/lib/web/eventsource/eventsource.js +25 -1
- package/node_modules/undici/lib/web/fetch/body.js +22 -15
- package/node_modules/undici/lib/web/fetch/data-url.js +6 -6
- package/node_modules/undici/lib/web/fetch/formdata.js +30 -36
- package/node_modules/undici/lib/web/fetch/headers.js +34 -31
- package/node_modules/undici/lib/web/fetch/index.js +38 -14
- package/node_modules/undici/lib/web/fetch/request.js +74 -79
- package/node_modules/undici/lib/web/fetch/response.js +43 -44
- package/node_modules/undici/lib/web/fetch/util.js +7 -7
- package/node_modules/undici/lib/web/infra/index.js +16 -1
- package/node_modules/undici/lib/web/webidl/index.js +2 -14
- package/node_modules/undici/lib/web/websocket/events.js +43 -26
- package/node_modules/undici/lib/web/websocket/receiver.js +5 -0
- package/node_modules/undici/lib/web/websocket/stream/websocketerror.js +13 -10
- package/node_modules/undici/lib/web/websocket/websocket.js +44 -41
- package/node_modules/undici/package.json +1 -1
- package/node_modules/undici/types/diagnostics-channel.d.ts +2 -1
- package/node_modules/undici/types/interceptors.d.ts +1 -1
- package/node_modules/undici/types/webidl.d.ts +13 -7
- package/package.json +4 -4
- package/dist/view/app-dist/assets/index-DFzw1L97.js +0 -63
- package/docs-site/zh/tutorials/experiments.mdx +0 -101
package/dist/runner/attempt.cjs
CHANGED
|
@@ -6,6 +6,7 @@ exports.scoreFactOutcomeForAttemptError = scoreFactOutcomeForAttemptError;
|
|
|
6
6
|
exports.errorFromThrown = errorFromThrown;
|
|
7
7
|
exports.executeSandboxAction = executeSandboxAction;
|
|
8
8
|
exports.experimentRunInfo = experimentRunInfo;
|
|
9
|
+
const attempt_cancellation_ts_1 = require("./attempt-cancellation.cjs");
|
|
9
10
|
// 单个 attempt 的完整生命周期:资源(沙箱 / OTLP 接收器)经 Effect.Sample 的
|
|
10
11
|
// acquireRelease 接管,无论 body 成功 / 抛错 / 被中断,stop() / close() 都保证执行。
|
|
11
12
|
// 沙箱编排的固定段在 runAttemptBody(基线→setup→驱动 test→采 diff→评分→判定→收 trace),
|
|
@@ -19,6 +20,8 @@ const paths_ts_1 = require("../sandbox/paths.cjs");
|
|
|
19
20
|
const request_executor_ts_1 = require("../sandbox/request-executor.cjs");
|
|
20
21
|
const cleanup_timeout_ts_1 = require("./cleanup-timeout.cjs");
|
|
21
22
|
const adapter_usage_ts_1 = require("./adapter-usage.cjs");
|
|
23
|
+
const judge_usage_ts_1 = require("../o11y/judge-usage.cjs");
|
|
24
|
+
const judge_usage_ts_2 = require("./judge-usage.cjs");
|
|
22
25
|
const adapter_attachments_ts_1 = require("./adapter-attachments.cjs");
|
|
23
26
|
const adapter_execution_trace_ts_1 = require("./adapter-execution-trace.cjs");
|
|
24
27
|
const score_ts_1 = require("../eval/record/score.cjs");
|
|
@@ -140,765 +143,791 @@ function attemptFailureDeclaration(classify, phase, e) {
|
|
|
140
143
|
return { class: cls, phase, text: info.text };
|
|
141
144
|
}
|
|
142
145
|
function runAttemptEffect(a, opts, sandboxSem, { attachmentScope, buildLocators, preparedSetupPrefix, runTiming, parentSignal, invocationSignal, onPhase, concurrencySlot, providerAdmission, onFailureClass, onEnvironmentIncomplete, onSandboxCleanupFailure, onSealedEvaluation: publishSealedEvaluation, onInterruptedResult, reusedSandbox, }) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
assistanceReported
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const base = {
|
|
171
|
-
id: evalDef.id,
|
|
172
|
-
description: evalDef.description,
|
|
173
|
-
experimentId: run.experimentId,
|
|
174
|
-
experiment: experimentRunInfo(run, a.plan, a.sandboxPlansByEval, config, a.judge),
|
|
175
|
-
adapter: (0, adapter_ts_1.adapterIdentity)(adapter),
|
|
176
|
-
model: run.model,
|
|
177
|
-
verdict: "errored",
|
|
178
|
-
fingerprint: a.fingerprint,
|
|
179
|
-
evaluationAlgorithm: types_ts_1.EVALUATION_ALGORITHM,
|
|
180
|
-
configHash: a.configHash,
|
|
181
|
-
attempt,
|
|
182
|
-
startedAt: new Date(t0).toISOString(),
|
|
183
|
-
durationMs: 0,
|
|
184
|
-
factResults: [],
|
|
185
|
-
factUses: [],
|
|
186
|
-
evidenceCoverage: agent?.evidenceCoverage ?? attempt_resources_ts_1.adapterEvidenceUnavailable,
|
|
187
|
-
evaluationKind: evalDef.evaluationKind ?? "pass",
|
|
188
|
-
};
|
|
189
|
-
/**
|
|
190
|
-
* 调度事实:Sandbox 租借给这条 Attempt 的那一刻定死(复用池借出的实例在进入本函数时就已经
|
|
191
|
-
* 借定,一次性沙箱在 create 返回那一刻借定)。Attempt 在任何阶段终结——Eval `setup` 失败、
|
|
192
|
-
* 超时、资源获取失败——记录里都要带全 provider / sandboxId / reused / 编号与承接序号,
|
|
193
|
-
* 只有 `kept` 在收尾时点决定(见 docs/feature/record/architecture.md 的 `sandbox` 字段)。
|
|
194
|
-
* 所以它挂在唯一出口(下方 Effect.map)上,而不是分散在各条返回路径里。
|
|
195
|
-
*/
|
|
196
|
-
let sandboxFacts;
|
|
197
|
-
let currentSandbox;
|
|
198
|
-
/** 留存提交成功(`--keep-sandbox`)——唯一一个在收尾时点才知道的 sandbox 记录键。 */
|
|
199
|
-
let kept = false;
|
|
200
|
-
if (reusedSandbox && agent?.kind === "sandbox") {
|
|
201
|
-
if (a.plan._tag !== "Sandbox") {
|
|
202
|
-
throw new Error(`sandbox agent ${JSON.stringify(agent.name)} received a Direct plan`);
|
|
203
|
-
}
|
|
204
|
-
sandboxFacts = {
|
|
205
|
-
provider: a.plan.providerPlan.provider,
|
|
206
|
-
sandboxId: reusedSandbox.sandbox.sandboxId,
|
|
207
|
-
reused: true,
|
|
208
|
-
reuseSandbox: reusedSandbox.reuseSandbox,
|
|
209
|
-
reuseOrdinal: reusedSandbox.reuseOrdinal,
|
|
146
|
+
return effect_1.Effect.clockWith((clock) => effect_1.Effect.suspend(() => {
|
|
147
|
+
let assistanceReported = false;
|
|
148
|
+
const onSealedEvaluation = (sealed) => effect_1.Effect.suspend(() => {
|
|
149
|
+
if (!assistanceReported) {
|
|
150
|
+
assistanceReported = true;
|
|
151
|
+
reportJudgeErrorAssistance(a, sealed);
|
|
152
|
+
}
|
|
153
|
+
return publishSealedEvaluation?.(sealed) ?? effect_1.Effect.void;
|
|
154
|
+
});
|
|
155
|
+
const config = opts.config;
|
|
156
|
+
const { evalDef, run, attempt } = a;
|
|
157
|
+
const adapter = run.adapter;
|
|
158
|
+
const agent = run.agent;
|
|
159
|
+
const coordinationRoot = opts.coordinationRoot ?? `${process.cwd()}/.niceeval`;
|
|
160
|
+
const t0 = Date.now();
|
|
161
|
+
// Source bytes are snapshotted at the author action, then held only through
|
|
162
|
+
// an internal capability until the Record producer seals its own Attachments.
|
|
163
|
+
const sourceRegistry = (0, source_loc_ts_1.createSourceRegistry)(process.cwd());
|
|
164
|
+
const sourceCapture = (0, source_producer_ts_1.createRunnerAttemptSourceCapture)(sourceRegistry);
|
|
165
|
+
const attemptResources = new attempt_resources_ts_1.ManagedAttemptResources();
|
|
166
|
+
let attemptResourceReleaseFailureReported = false;
|
|
167
|
+
const reportAttemptResourceReleaseFailure = (error) => {
|
|
168
|
+
if (attemptResourceReleaseFailureReported)
|
|
169
|
+
return;
|
|
170
|
+
attemptResourceReleaseFailureReported = true;
|
|
171
|
+
declareFailure("agent.teardown", error);
|
|
172
|
+
diagnostics.push(teardownDiagnostic("agent.teardown", error));
|
|
210
173
|
};
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
let liveAssertions;
|
|
253
|
-
let liveAssertionState;
|
|
254
|
-
let adapterResources;
|
|
255
|
-
let adapterUsage;
|
|
256
|
-
let adapterUsageSnapshot;
|
|
257
|
-
let adapterAttachments;
|
|
258
|
-
let adapterExecutionTraces;
|
|
259
|
-
let adapterSealedAssertions;
|
|
260
|
-
let adapterCleanupError;
|
|
261
|
-
let invocationInterrupted = false;
|
|
262
|
-
let assertionsSealed = false;
|
|
263
|
-
let resolveExecutionTerminal;
|
|
264
|
-
const executionTerminal = new Promise((resolve) => {
|
|
265
|
-
resolveExecutionTerminal = resolve;
|
|
266
|
-
});
|
|
267
|
-
const markExecutionTerminal = () => {
|
|
268
|
-
if (assertionsSealed)
|
|
269
|
-
return;
|
|
270
|
-
assertionsSealed = true;
|
|
271
|
-
resolveExecutionTerminal();
|
|
272
|
-
};
|
|
273
|
-
let timeoutSealedAssertions;
|
|
274
|
-
// Effect.timeoutTo 的 onTimeout 是同步回调,在中断真正下发给 body fiber(从而触发下面的
|
|
275
|
-
// finalizer 链)之前就已经跑完并同步置位这个标记——下面新增的 finalizer 靠它判断本次 Sample
|
|
276
|
-
// release 是不是超时触发的,只在超时路径补折叠证据,正常收尾路径不重复做(见文件顶部
|
|
277
|
-
// Effect.timeoutTo 调用点的注释)。
|
|
278
|
-
let timedOut = false;
|
|
279
|
-
let deadlineError;
|
|
280
|
-
let timeoutDiff;
|
|
281
|
-
let fileChangesCapture;
|
|
282
|
-
let timeoutFileChangesCapture;
|
|
283
|
-
let timeoutSources;
|
|
284
|
-
const closeAuthoring = (reason) => {
|
|
285
|
-
adapterResources?.closeAuthoring();
|
|
286
|
-
liveAssertions?.closeAuthoring(reason);
|
|
287
|
-
};
|
|
288
|
-
const forwardParentAbort = () => {
|
|
289
|
-
closeAuthoring("attempt-interrupted");
|
|
290
|
-
parentAbort.abort(parentSignal?.reason);
|
|
291
|
-
};
|
|
292
|
-
if (parentSignal?.aborted === true)
|
|
293
|
-
forwardParentAbort();
|
|
294
|
-
else
|
|
295
|
-
parentSignal?.addEventListener("abort", forwardParentAbort, { once: true });
|
|
296
|
-
const enterPhase = (phase) => {
|
|
297
|
-
lastPhase = phase;
|
|
298
|
-
recorder.enter(phase);
|
|
299
|
-
onPhase?.(phase);
|
|
300
|
-
(0, sink_ts_1.reportAttemptLifecycle)({ type: "attempt:phase", at: Date.now(), identity, phase });
|
|
301
|
-
};
|
|
302
|
-
/**
|
|
303
|
-
* 终局失败的分类与回执 —— **必须在把错误折成纯数据 `AttemptError` 之前调**:折完之后原错误
|
|
304
|
-
* 对象(连同它携带的 `_tag`/`class` 与 cause 链)就不再是任何人的引用,空间轴声明再也读不出来。
|
|
305
|
-
* 走生命周期链的三道决议(抛出点声明 → 实验分类器 → 缺省不可重试,见
|
|
306
|
-
* src/shared/failure-class.ts 的 `resolveAttemptFailureClass`);send 失败的时间轴已由 send 重试
|
|
307
|
-
* 执行体在 context 层消费完,浮出到这里的一定是终局失败,这里只读空间轴。
|
|
308
|
-
* 缺省档(`scope` 省略或 `"attempt"`)不回执:那是「死因只属于本次执行」,没有闸可落。
|
|
309
|
-
*/
|
|
310
|
-
const declareFailure = (phase, e) => {
|
|
311
|
-
if (!onFailureClass)
|
|
312
|
-
return;
|
|
313
|
-
const declaration = attemptFailureDeclaration(run.classifyFailure, phase, e);
|
|
314
|
-
if (declaration) {
|
|
315
|
-
// 空间轴回执会先于 Attempt 结果封口进入 run.ts,并立即生成运行期反馈与 run.json 的
|
|
316
|
-
// dispatch-halted 诊断。它不能依赖下面的 EvalResult 最终扫描补救,否则同一条错误在
|
|
317
|
-
// result.json 已脱敏、Run 级诊断却仍保留原值。分类决策只消费原始错误;对外回执单独
|
|
318
|
-
// 复制并脱敏正文,scope / phase 等路由事实保持原样。
|
|
319
|
-
onFailureClass({
|
|
320
|
-
...declaration,
|
|
321
|
-
text: (0, redaction_ts_1.redactSensitiveText)(declaration.text, sensitiveValues),
|
|
322
|
-
});
|
|
174
|
+
const base = {
|
|
175
|
+
id: evalDef.id,
|
|
176
|
+
description: evalDef.description,
|
|
177
|
+
experimentId: run.experimentId,
|
|
178
|
+
experiment: experimentRunInfo(run, a.plan, a.sandboxPlansByEval, config, a.judge),
|
|
179
|
+
adapter: (0, adapter_ts_1.adapterIdentity)(adapter),
|
|
180
|
+
model: run.model,
|
|
181
|
+
verdict: "errored",
|
|
182
|
+
fingerprint: a.fingerprint,
|
|
183
|
+
evaluationAlgorithm: types_ts_1.EVALUATION_ALGORITHM,
|
|
184
|
+
configHash: a.configHash,
|
|
185
|
+
attempt,
|
|
186
|
+
startedAt: new Date(t0).toISOString(),
|
|
187
|
+
durationMs: 0,
|
|
188
|
+
factResults: [],
|
|
189
|
+
factUses: [],
|
|
190
|
+
evidenceCoverage: agent?.evidenceCoverage ?? attempt_resources_ts_1.adapterEvidenceUnavailable,
|
|
191
|
+
evaluationKind: evalDef.evaluationKind ?? "pass",
|
|
192
|
+
};
|
|
193
|
+
/**
|
|
194
|
+
* 调度事实:Sandbox 租借给这条 Attempt 的那一刻定死(复用池借出的实例在进入本函数时就已经
|
|
195
|
+
* 借定,一次性沙箱在 create 返回那一刻借定)。Attempt 在任何阶段终结——Eval `setup` 失败、
|
|
196
|
+
* 超时、资源获取失败——记录里都要带全 provider / sandboxId / reused / 编号与承接序号,
|
|
197
|
+
* 只有 `kept` 在收尾时点决定(见 docs/feature/record/architecture.md 的 `sandbox` 字段)。
|
|
198
|
+
* 所以它挂在唯一出口(下方 Effect.map)上,而不是分散在各条返回路径里。
|
|
199
|
+
*/
|
|
200
|
+
let sandboxFacts;
|
|
201
|
+
let currentSandbox;
|
|
202
|
+
/** 留存提交成功(`--keep-sandbox`)——唯一一个在收尾时点才知道的 sandbox 记录键。 */
|
|
203
|
+
let kept = false;
|
|
204
|
+
if (reusedSandbox && agent?.kind === "sandbox") {
|
|
205
|
+
if (a.plan._tag !== "Sandbox") {
|
|
206
|
+
throw new Error(`sandbox agent ${JSON.stringify(agent.name)} received a Direct plan`);
|
|
207
|
+
}
|
|
208
|
+
sandboxFacts = {
|
|
209
|
+
provider: a.plan.providerPlan.provider,
|
|
210
|
+
sandboxId: reusedSandbox.sandbox.sandboxId,
|
|
211
|
+
reused: true,
|
|
212
|
+
reuseSandbox: reusedSandbox.reuseSandbox,
|
|
213
|
+
reuseOrdinal: reusedSandbox.reuseOrdinal,
|
|
214
|
+
};
|
|
323
215
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
216
|
+
// 四层解析链的最后两层(eval → config)在这里接上,并把赢家那一层带出来:超时消息要说得出
|
|
217
|
+
// 「这个上限是哪一层给的」,否则撞线时得回头逐层对照四个声明点(契约见
|
|
218
|
+
// docs/feature/experiments/cli.md「timeout、budget 与基础设施错误」)。
|
|
219
|
+
// 四层都没声明 = 无上限:不挂 deadline、不发软截止信号,也不给 provider 递命令超时,
|
|
220
|
+
// 链末端不发明一条隐藏的线。
|
|
221
|
+
const attemptTimeout = (0, timeout_ts_1.resolveAttemptTimeout)(run, evalDef, config);
|
|
222
|
+
// deadline 的截止**时刻**:沙箱内一切时限从它派生(单条命令未显式传 timeout 时上限 =
|
|
223
|
+
// 剩余量,见 sandbox/deadline.ts)。与 Effect.timeoutTo 同一个锚点,不各取各的 now()。
|
|
224
|
+
const deadlineAt = attemptTimeout ? clock.currentTimeMillisUnsafe() + attemptTimeout.timeoutMs : undefined;
|
|
225
|
+
const executionStartedAt = clock.monotonicTimeNanosUnsafe();
|
|
226
|
+
// Adapter 的协作式 deadline 由专用 controller 承载。它绝不能自己设 timer:Effect.timeoutTo
|
|
227
|
+
// 是唯一的 deadline owner,并会在 onTimeout 同步 abort 这个 controller;否则独立 timer
|
|
228
|
+
// 可能让 adapter 先 reject,把 deadline 错分成外部 interrupt。
|
|
229
|
+
const cancellation = new attempt_cancellation_ts_1.AttemptCancellationController();
|
|
230
|
+
const signal = cancellation.signal;
|
|
231
|
+
// Attempt 阶段的正式生命周期投影(见 docs/feature/experiments/cli.md「Attempt 阶段」)。
|
|
232
|
+
// Direct / reused Attempt 由 run.ts 在进入本函数前发 start;fresh Sandbox 则由下面的 provider
|
|
233
|
+
// admission 在 granted 后发 start,再于同一边界进入 sandbox.create。没有对应 hook/配置的步骤
|
|
234
|
+
// 直接不调用,不产生空阶段。没有活跃 feedback coordinator 时 lifecycle 投影静默 no-op。
|
|
235
|
+
const identity = { experimentId: run.experimentId, evalId: evalDef.id, attempt };
|
|
236
|
+
// 最近跨入的正式 phase:errored 结果的 `error.phase` 从它取(见下方 timeout / scope
|
|
237
|
+
// 兜底与 runAttemptBody 的 body catch)。body 与本函数共用同一个 enterPhase 闭包(经 res 传下去),
|
|
238
|
+
// 所以 body 内部的阶段推进也会更新它,不需要 body 再单独维护一份。
|
|
239
|
+
// 阶段计时:live 展示、error.phase、落盘 phases[].name 用同一套 LifecyclePhase 闭集,
|
|
240
|
+
// 一次 enterPhase 同时推进三者(词表全仓只有一套,见 runner/types.ts 的 LifecyclePhase)。
|
|
241
|
+
let lastPhase;
|
|
242
|
+
const recorder = (0, timing_ts_1.createTimingRecorder)();
|
|
243
|
+
// adapter send 在飞时,错误/诊断归因到嵌套的 `agent.run`(eval.run 内打开,不单列计时条目)。
|
|
244
|
+
let sendActive = false;
|
|
245
|
+
// 超时证据保全的外层句柄:与 recorder 同一模式,runAttemptBody 内部一建好 SessionManager /
|
|
246
|
+
// ChangeLedger 就经 AttemptResources.registerEvidence/registerLedger 登记回这里——中断后由
|
|
247
|
+
// Sample 外层直接读它们组装结果,不随被 Effect 中断放弃的 body fiber 一起消失(见
|
|
248
|
+
// docs/runner.md「超时:双层保护」超时不丢证据)。
|
|
249
|
+
let liveEvents;
|
|
250
|
+
let liveUsage;
|
|
251
|
+
let liveRetryAttempts;
|
|
252
|
+
let liveLedger;
|
|
253
|
+
// Assert-first state becomes visible only after the real TestContext has
|
|
254
|
+
// been constructed. Scope finalization owns the interruption seal so an
|
|
255
|
+
// aborted Attempt cannot leave declared entries unsealed.
|
|
256
|
+
let liveAssertions;
|
|
257
|
+
let liveAssertionState;
|
|
258
|
+
let adapterResources;
|
|
259
|
+
let adapterUsage;
|
|
260
|
+
let adapterUsageSnapshot;
|
|
261
|
+
let adapterAttachments;
|
|
262
|
+
let adapterExecutionTraces;
|
|
263
|
+
let adapterSealedAssertions;
|
|
264
|
+
let adapterCleanupError;
|
|
265
|
+
let invocationInterrupted = false;
|
|
266
|
+
let assertionsSealed = false;
|
|
267
|
+
let resolveExecutionTerminal;
|
|
268
|
+
const executionTerminal = new Promise((resolve) => {
|
|
269
|
+
resolveExecutionTerminal = resolve;
|
|
270
|
+
});
|
|
271
|
+
const markExecutionTerminal = () => {
|
|
272
|
+
if (assertionsSealed)
|
|
273
|
+
return;
|
|
274
|
+
assertionsSealed = true;
|
|
275
|
+
resolveExecutionTerminal();
|
|
276
|
+
};
|
|
277
|
+
let timeoutSealedAssertions;
|
|
278
|
+
// Effect.timeoutTo 的 onTimeout 是同步回调,在中断真正下发给 body fiber(从而触发下面的
|
|
279
|
+
// finalizer 链)之前就已经跑完并同步置位这个标记——下面新增的 finalizer 靠它判断本次 Sample
|
|
280
|
+
// release 是不是超时触发的,只在超时路径补折叠证据,正常收尾路径不重复做(见文件顶部
|
|
281
|
+
// Effect.timeoutTo 调用点的注释)。
|
|
282
|
+
let timedOut = false;
|
|
283
|
+
let deadlineError;
|
|
284
|
+
let timeoutDiff;
|
|
285
|
+
let fileChangesCapture;
|
|
286
|
+
let timeoutFileChangesCapture;
|
|
287
|
+
let timeoutSources;
|
|
288
|
+
const closeAuthoring = (reason) => {
|
|
289
|
+
adapterResources?.closeAuthoring();
|
|
290
|
+
liveAssertions?.closeAuthoring(reason);
|
|
291
|
+
};
|
|
292
|
+
const forwardParentAbort = () => {
|
|
293
|
+
closeAuthoring("attempt-interrupted");
|
|
294
|
+
cancellation.abort({ kind: "cancelled" });
|
|
295
|
+
};
|
|
296
|
+
if (parentSignal?.aborted === true)
|
|
297
|
+
forwardParentAbort();
|
|
298
|
+
else
|
|
299
|
+
parentSignal?.addEventListener("abort", forwardParentAbort, { once: true });
|
|
300
|
+
const enterPhase = (phase) => {
|
|
301
|
+
lastPhase = phase;
|
|
302
|
+
recorder.enter(phase);
|
|
303
|
+
onPhase?.(phase);
|
|
304
|
+
(0, sink_ts_1.reportAttemptLifecycle)({ type: "attempt:phase", at: Date.now(), identity, phase });
|
|
305
|
+
};
|
|
306
|
+
/**
|
|
307
|
+
* 终局失败的分类与回执 —— **必须在把错误折成纯数据 `AttemptError` 之前调**:折完之后原错误
|
|
308
|
+
* 对象(连同它携带的 `_tag`/`class` 与 cause 链)就不再是任何人的引用,空间轴声明再也读不出来。
|
|
309
|
+
* 走生命周期链的三道决议(抛出点声明 → 实验分类器 → 缺省不可重试,见
|
|
310
|
+
* src/shared/failure-class.ts 的 `resolveAttemptFailureClass`);send 失败的时间轴已由 send 重试
|
|
311
|
+
* 执行体在 context 层消费完,浮出到这里的一定是终局失败,这里只读空间轴。
|
|
312
|
+
* 缺省档(`scope` 省略或 `"attempt"`)不回执:那是「死因只属于本次执行」,没有闸可落。
|
|
313
|
+
*/
|
|
314
|
+
const declareFailure = (phase, e) => {
|
|
315
|
+
if (!onFailureClass)
|
|
351
316
|
return;
|
|
317
|
+
const declaration = attemptFailureDeclaration(run.classifyFailure, phase, e);
|
|
318
|
+
if (declaration) {
|
|
319
|
+
// 空间轴回执会先于 Attempt 结果封口进入 run.ts,并立即生成运行期反馈与 run.json 的
|
|
320
|
+
// dispatch-halted 诊断。它不能依赖下面的 EvalResult 最终扫描补救,否则同一条错误在
|
|
321
|
+
// result.json 已脱敏、Run 级诊断却仍保留原值。分类决策只消费原始错误;对外回执单独
|
|
322
|
+
// 复制并脱敏正文,scope / phase 等路由事实保持原样。
|
|
323
|
+
onFailureClass({
|
|
324
|
+
...declaration,
|
|
325
|
+
text: (0, redaction_ts_1.redactSensitiveText)(declaration.text, sensitiveValues),
|
|
326
|
+
});
|
|
352
327
|
}
|
|
353
|
-
}
|
|
354
|
-
const message = (0, redaction_ts_1.redactSensitiveText)(input.message, sensitiveValues);
|
|
355
|
-
const context = input.data === undefined
|
|
356
|
-
? undefined
|
|
357
|
-
: (0, redaction_ts_1.redactSensitiveEvidence)(input.data, sensitiveValues);
|
|
358
|
-
const record = {
|
|
359
|
-
code: input.code,
|
|
360
|
-
level: input.level,
|
|
361
|
-
detail: message,
|
|
362
|
-
origin: (0, timing_ts_1.attemptOrigin)(phase),
|
|
363
|
-
...(context !== undefined ? { context } : {}),
|
|
364
328
|
};
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
diagnostics
|
|
368
|
-
|
|
369
|
-
//
|
|
370
|
-
//
|
|
371
|
-
//
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
data: { ...diagnosticData, phase, origin: (0, timing_ts_1.attemptOrigin)(phase) },
|
|
329
|
+
// 本 attempt 累计的诊断(与 verdict 独立):ScopedFeedback.diagnostic 与 teardown 失败都落这里,
|
|
330
|
+
// 收尾时并入结果;dedupeKey 相同的并发诊断折叠成一条并累计 count。
|
|
331
|
+
const diagnostics = [];
|
|
332
|
+
const dedupeIndex = new Map();
|
|
333
|
+
// Sandbox 命令证据的累加器(与 diagnostics 同一种「共享容器」模式):
|
|
334
|
+
// withCommandTiming 在 CommandResult 交还调用方之前把每条命令(成功与非零退出都算)push 进来,
|
|
335
|
+
// runAttemptBody 的 finally 与本函数的超时/中断兜底分支都读同一个数组引用
|
|
336
|
+
// (见 docs/feature/record/architecture.md「commandsjson」「证据在 CommandResult 返回
|
|
337
|
+
// 调用方之前写入内存」)。
|
|
338
|
+
const commands = [];
|
|
339
|
+
// CommandOptions.sensitiveValues 的 Attempt 级内存集合。值只供记录边界精确替换;最终
|
|
340
|
+
// result 封口后集合随本次调用释放,不进入任何 artifact、指纹或 provider identity。
|
|
341
|
+
const sensitiveValues = new Set();
|
|
342
|
+
// Package-internal only: the final EvalResult is weakly associated with this
|
|
343
|
+
// capture after all Runner evidence is sealed. No public result field or
|
|
344
|
+
// legacy artifact is used as a transport.
|
|
345
|
+
const observabilityRuntime = (0, runtime_ts_3.createRunnerAttemptObservabilityRuntime)({
|
|
346
|
+
providerName: adapter.name,
|
|
347
|
+
sensitiveValues,
|
|
385
348
|
});
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
349
|
+
const recordDiagnostic = (input) => {
|
|
350
|
+
const phase = (sendActive ? "agent.run" : lastPhase) ?? "eval.run";
|
|
351
|
+
if (input.dedupeKey !== undefined) {
|
|
352
|
+
const existing = dedupeIndex.get(input.dedupeKey);
|
|
353
|
+
if (existing) {
|
|
354
|
+
existing.count = (existing.count ?? 1) + 1;
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
const message = (0, redaction_ts_1.redactSensitiveText)(input.message, sensitiveValues);
|
|
359
|
+
const context = input.data === undefined
|
|
360
|
+
? undefined
|
|
361
|
+
: (0, redaction_ts_1.redactSensitiveEvidence)(input.data, sensitiveValues);
|
|
362
|
+
const record = {
|
|
363
|
+
code: input.code,
|
|
364
|
+
level: input.level,
|
|
365
|
+
detail: message,
|
|
366
|
+
origin: (0, timing_ts_1.attemptOrigin)(phase),
|
|
367
|
+
...(context !== undefined ? { context } : {}),
|
|
368
|
+
};
|
|
369
|
+
if (input.dedupeKey !== undefined)
|
|
370
|
+
dedupeIndex.set(input.dedupeKey, record);
|
|
371
|
+
diagnostics.push(record);
|
|
372
|
+
// 同时进运行级永久事件流(human 撤下 dashboard 后追加、JSON stdout 追加 warning,去重按 key)。
|
|
373
|
+
// `key` 只管折叠到多细(作者没给 dedupeKey 时折到「这一条 attempt 的这种诊断」,身份因此
|
|
374
|
+
// 编进去);对外稳定词法始终单独给 `code`,不让消费方从 key 反推(见 sink.ts 的
|
|
375
|
+
// DiagnosticInput.code、docs/feature/experiments/cli.md 的 WarningEvent)。
|
|
376
|
+
const { origin: _authorOrigin, phase: _authorPhase, ...diagnosticData } = context ?? {};
|
|
377
|
+
(0, sink_ts_1.reportDiagnostic)({
|
|
378
|
+
key: input.dedupeKey ?? `${input.code}:${(0, types_ts_1.encodeAttemptKey)(identity)}`,
|
|
379
|
+
code: input.code,
|
|
380
|
+
severity: input.level,
|
|
381
|
+
message,
|
|
382
|
+
identity,
|
|
383
|
+
// phase 由运行器给,且**压过**作者 `data` 里的同名字段:`WarningEvent.phase` 是
|
|
384
|
+
// `LifecyclePhase` 闭集,取值只能由「这条诊断报上来时运行器正处在哪一步」决定。
|
|
385
|
+
// 作者的 `data` 是开放词表,让它盖住这个字段等于允许从 eval / adapter 代码里冒充一个
|
|
386
|
+
// 别的(甚至不在闭集里的)阶段,消费方按 phase 分支就此失效——与 ScopedFeedback
|
|
387
|
+
// 「两个方法都不接受 phase」是同一条纪律(见 src/shared/types.ts 的接口注释)。
|
|
388
|
+
data: { ...diagnosticData, phase, origin: (0, timing_ts_1.attemptOrigin)(phase) },
|
|
389
|
+
});
|
|
390
|
+
};
|
|
391
|
+
// 作用域反馈:progress 只走 attempt:progress(短命状态,归因由 runner 的当前阶段决定),
|
|
392
|
+
// 不进入 recentLogs / timeout stack;diagnostic 落 attempt diagnostics + 运行级永久事件。
|
|
393
|
+
// 绑定见 docs/feature/experiments/library.md。
|
|
394
|
+
const scopedFeedback = {
|
|
395
|
+
progress: (u) => {
|
|
396
|
+
const suffix = u.current !== undefined && u.total !== undefined ? ` (${u.current}/${u.total})` : "";
|
|
397
|
+
(0, sink_ts_1.reportAttemptLifecycle)({
|
|
398
|
+
type: "attempt:progress",
|
|
399
|
+
at: Date.now(),
|
|
400
|
+
identity,
|
|
401
|
+
detail: activeDetailText(`${u.message}${suffix}`, sensitiveValues),
|
|
402
|
+
});
|
|
403
|
+
},
|
|
404
|
+
diagnostic: recordDiagnostic,
|
|
405
|
+
};
|
|
406
|
+
const recordSandboxCleanupFailure = (stage, cause) => {
|
|
407
|
+
if (onSandboxCleanupFailure === undefined)
|
|
408
|
+
return;
|
|
409
|
+
const rawError = cause instanceof Error ? cause : new Error(String(cause));
|
|
410
|
+
// The ledger is later surfaced as an Experiment diagnostic, outside this
|
|
411
|
+
// Attempt's normal diagnostic sink. Preserve the real cleanup outcome, but
|
|
412
|
+
// cross that boundary only after applying this Attempt's evidence redaction.
|
|
413
|
+
const error = new Error((0, redaction_ts_1.redactSensitiveText)(rawError.message, sensitiveValues));
|
|
414
|
+
// This receiver runs inside the Scope finalizer path. It must not let a
|
|
415
|
+
// bookkeeping failure replace the provider or lifecycle cleanup outcome.
|
|
416
|
+
try {
|
|
417
|
+
onSandboxCleanupFailure({ stage, error });
|
|
418
|
+
}
|
|
419
|
+
catch {
|
|
420
|
+
// The owning Experiment will still receive the original cleanup result.
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
/**
|
|
424
|
+
* Sandbox runtime deliberately keeps lifecycle cleanup diagnostic-only so it
|
|
425
|
+
* can continue other finalizers. Only this fresh Sandbox wrapper
|
|
426
|
+
* observes those runtime-owned diagnostics; Eval/Agent diagnostics with the
|
|
427
|
+
* same public sink never enter the physical cleanup ledger.
|
|
428
|
+
*/
|
|
429
|
+
const freshSandboxLifecycleFeedback = {
|
|
430
|
+
progress: scopedFeedback.progress,
|
|
431
|
+
diagnostic: (input) => {
|
|
432
|
+
scopedFeedback.diagnostic(input);
|
|
433
|
+
if (input.code === "sandbox-teardown-failed" ||
|
|
434
|
+
input.code === "sandbox-stop-failed" ||
|
|
435
|
+
input.code === "plugin-lifecycle-teardown-failed") {
|
|
436
|
+
recordSandboxCleanupFailure("sandbox.lifecycle", new Error((0, redaction_ts_1.redactSensitiveText)(input.message, sensitiveValues)));
|
|
437
|
+
}
|
|
438
|
+
},
|
|
439
|
+
};
|
|
440
|
+
const judgeUsage = new judge_usage_ts_1.JudgeUsageCollector(config.pricing, signal, () => recordDiagnostic({
|
|
441
|
+
code: "judge-transport-cleanup-incomplete", level: "warning",
|
|
442
|
+
message: "Judge transport cleanup did not settle before its local deadline; remote completion is unknown.",
|
|
443
|
+
}));
|
|
444
|
+
const sealExecutionError = () => {
|
|
445
|
+
const runtime = liveAssertions ?? (evalDef.evaluationKind === "score"
|
|
446
|
+
? (0, runtime_ts_2.createAssertionsRuntime)({ evaluationKind: "score", judgeUsage })
|
|
447
|
+
: (0, runtime_ts_2.createAssertionsRuntime)({ evaluationKind: "pass", judgeUsage }));
|
|
448
|
+
if (liveAssertions === undefined) {
|
|
449
|
+
// Sandbox acquire/prepare may fail before TestContext exists. It is still
|
|
450
|
+
// an executed Attempt, so seal its empty assertion set with the same
|
|
451
|
+
// execution-error terminal used by failures after Context construction.
|
|
452
|
+
liveAssertions = runtime;
|
|
453
|
+
sourceCapture.attachAssertions(runtime);
|
|
454
|
+
}
|
|
455
|
+
return (0, assertions_ts_1.sealAttemptAssertions)(runtime, { execution: "errored" }).pipe(effect_1.Effect.tap(() => effect_1.Effect.sync(() => {
|
|
456
|
+
markExecutionTerminal();
|
|
457
|
+
})), effect_1.Effect.tap((sealed) => adapter.kind === "custom"
|
|
458
|
+
? effect_1.Effect.sync(() => { adapterSealedAssertions = sealed; })
|
|
459
|
+
: onSealedEvaluation?.(sealed) ?? effect_1.Effect.void));
|
|
460
|
+
};
|
|
461
|
+
// `log` 是显式 timeout breadcrumb;与短命 progress 分开,避免 user/tool ACTIVE 文本落盘。
|
|
462
|
+
const recentLogs = [];
|
|
463
|
+
const log = (m) => {
|
|
464
|
+
const detail = (0, redaction_ts_1.redactSensitiveText)(m, sensitiveValues);
|
|
465
|
+
recentLogs.push(detail);
|
|
466
|
+
if (recentLogs.length > 20)
|
|
467
|
+
recentLogs.shift();
|
|
468
|
+
// 附着在「当前阶段」上的次要文本(见 ActiveAttempt.detail)。所有调用点都晚于 Direct/reuse
|
|
469
|
+
// start,或 fresh provider 的 granted callback,active map 因而已经有这个 identity。这是唯一出口 ——
|
|
470
|
+
// 没有裸写 stderr 的兜底分支(那是给已删除的 Live reporter 用的旧接线,见
|
|
471
|
+
// docs/feature/experiments/cli.md「一个 run 内只有一个终端协调者」);由当前 renderer 决定
|
|
472
|
+
// 是否消费这条 detail(human 展示,JSON 不消费 lifecycle detail)。
|
|
393
473
|
(0, sink_ts_1.reportAttemptLifecycle)({
|
|
394
474
|
type: "attempt:progress",
|
|
395
475
|
at: Date.now(),
|
|
396
476
|
identity,
|
|
397
|
-
detail: activeDetailText(
|
|
477
|
+
detail: activeDetailText(m, sensitiveValues),
|
|
398
478
|
});
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
const
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
const freshSandboxLifecycleFeedback = {
|
|
426
|
-
progress: scopedFeedback.progress,
|
|
427
|
-
diagnostic: (input) => {
|
|
428
|
-
scopedFeedback.diagnostic(input);
|
|
429
|
-
if (input.code === "sandbox-teardown-failed" ||
|
|
430
|
-
input.code === "sandbox-stop-failed" ||
|
|
431
|
-
input.code === "plugin-lifecycle-teardown-failed") {
|
|
432
|
-
recordSandboxCleanupFailure("sandbox.lifecycle", new Error((0, redaction_ts_1.redactSensitiveText)(input.message, sensitiveValues)));
|
|
433
|
-
}
|
|
434
|
-
},
|
|
435
|
-
};
|
|
436
|
-
const sealExecutionError = () => {
|
|
437
|
-
const runtime = liveAssertions ?? (evalDef.evaluationKind === "score"
|
|
438
|
-
? (0, runtime_ts_2.createAssertionsRuntime)({ evaluationKind: "score" })
|
|
439
|
-
: (0, runtime_ts_2.createAssertionsRuntime)({ evaluationKind: "pass" }));
|
|
440
|
-
if (liveAssertions === undefined) {
|
|
441
|
-
// Sandbox acquire/prepare may fail before TestContext exists. It is still
|
|
442
|
-
// an executed Attempt, so seal its empty assertion set with the same
|
|
443
|
-
// execution-error terminal used by failures after Context construction.
|
|
444
|
-
liveAssertions = runtime;
|
|
445
|
-
sourceCapture.attachAssertions(runtime);
|
|
446
|
-
}
|
|
447
|
-
return (0, assertions_ts_1.sealAttemptAssertions)(runtime, { execution: "errored" }).pipe(effect_1.Effect.tap(() => effect_1.Effect.sync(() => {
|
|
448
|
-
markExecutionTerminal();
|
|
449
|
-
})), effect_1.Effect.tap((sealed) => adapter.kind === "custom"
|
|
450
|
-
? effect_1.Effect.sync(() => { adapterSealedAssertions = sealed; })
|
|
451
|
-
: onSealedEvaluation?.(sealed) ?? effect_1.Effect.void));
|
|
452
|
-
};
|
|
453
|
-
// `log` 是显式 timeout breadcrumb;与短命 progress 分开,避免 user/tool ACTIVE 文本落盘。
|
|
454
|
-
const recentLogs = [];
|
|
455
|
-
const log = (m) => {
|
|
456
|
-
const detail = (0, redaction_ts_1.redactSensitiveText)(m, sensitiveValues);
|
|
457
|
-
recentLogs.push(detail);
|
|
458
|
-
if (recentLogs.length > 20)
|
|
459
|
-
recentLogs.shift();
|
|
460
|
-
// 附着在「当前阶段」上的次要文本(见 ActiveAttempt.detail)。所有调用点都晚于 Direct/reuse
|
|
461
|
-
// start,或 fresh provider 的 granted callback,active map 因而已经有这个 identity。这是唯一出口 ——
|
|
462
|
-
// 没有裸写 stderr 的兜底分支(那是给已删除的 Live reporter 用的旧接线,见
|
|
463
|
-
// docs/feature/experiments/cli.md「一个 run 内只有一个终端协调者」);由当前 renderer 决定
|
|
464
|
-
// 是否消费这条 detail(human 展示,JSON 不消费 lifecycle detail)。
|
|
465
|
-
(0, sink_ts_1.reportAttemptLifecycle)({
|
|
466
|
-
type: "attempt:progress",
|
|
467
|
-
at: Date.now(),
|
|
468
|
-
identity,
|
|
469
|
-
detail: activeDetailText(m, sensitiveValues),
|
|
470
|
-
});
|
|
471
|
-
};
|
|
472
|
-
/**
|
|
473
|
-
* ── attempt 总超时的硬边界(P1)──
|
|
474
|
-
* 上限是「整个 attempt(setup+agent+脚本+评分)」的,不是 docker 单条命令的。
|
|
475
|
-
* 到点 → 中断整段 body → Sample 跑 release(停容器、关接收器)→ 产出一条 errored 结果。
|
|
476
|
-
* 即便 adapter / test 完全无视 signal 挂死,这一层也能把它停下来并回收资源。
|
|
477
|
-
*
|
|
478
|
-
* 四层解析链都没声明上限时**这个算子整个不挂**——不是挂一条无穷大的 timer:没有线就没有
|
|
479
|
-
* deadline,attempt 跑到自己结束为止(与携带判据的 `Infinity` 同一个语义)。
|
|
480
|
-
*/
|
|
481
|
-
const applyAttemptDeadline = (self) => {
|
|
482
|
-
if (attemptTimeout === undefined)
|
|
483
|
-
return self;
|
|
484
|
-
const { timeoutMs, source: timeoutSource } = attemptTimeout;
|
|
485
|
-
const deadline = effect_1.Effect.raceFirst(effect_1.Effect.sleep(effect_1.Duration.millis(timeoutMs)).pipe(effect_1.Effect.as("deadline")), effect_1.Effect.promise(() => executionTerminal).pipe(effect_1.Effect.as("terminal"))).pipe(effect_1.Effect.flatMap((winner) => {
|
|
486
|
-
// Assertion seal is the Attempt's terminal freeze. Once it wins, the
|
|
487
|
-
// deadline competitor is permanently retired while publication,
|
|
488
|
-
// source capture, and cleanup finish under their own ownership.
|
|
489
|
-
if (winner === "terminal")
|
|
490
|
-
return effect_1.Effect.never;
|
|
491
|
-
return effect_1.Effect.suspend(() => {
|
|
492
|
-
// Recheck in the same synchronous section that closes admission and
|
|
493
|
-
// flips `timedOut`; seal may have won after the race selected its
|
|
494
|
-
// timer but before this continuation was scheduled.
|
|
495
|
-
if (assertionsSealed)
|
|
479
|
+
};
|
|
480
|
+
/**
|
|
481
|
+
* ── attempt 总超时的硬边界(P1)──
|
|
482
|
+
* 上限是「整个 attempt(setup+agent+脚本+评分)」的,不是 docker 单条命令的。
|
|
483
|
+
* 到点 → 中断整段 body → Sample 跑 release(停容器、关接收器)→ 产出一条 errored 结果。
|
|
484
|
+
* 即便 adapter / test 完全无视 signal 挂死,这一层也能把它停下来并回收资源。
|
|
485
|
+
*
|
|
486
|
+
* 四层解析链都没声明上限时**这个算子整个不挂**——不是挂一条无穷大的 timer:没有线就没有
|
|
487
|
+
* deadline,attempt 跑到自己结束为止(与携带判据的 `Infinity` 同一个语义)。
|
|
488
|
+
*/
|
|
489
|
+
const applyAttemptDeadline = (self) => {
|
|
490
|
+
if (attemptTimeout === undefined)
|
|
491
|
+
return self;
|
|
492
|
+
const { timeoutMs, source: timeoutSource } = attemptTimeout;
|
|
493
|
+
const waitForDeadline = effect_1.Effect.suspend(() => {
|
|
494
|
+
const remaining = timeoutMs - Number(clock.monotonicTimeNanosUnsafe() - executionStartedAt) / 1000000;
|
|
495
|
+
// Native timers truncate fractional milliseconds. Recheck the same
|
|
496
|
+
// monotonic deadline after waking rather than cancelling early.
|
|
497
|
+
return remaining <= 0 ? effect_1.Effect.void
|
|
498
|
+
: effect_1.Effect.sleep(effect_1.Duration.millis(Math.ceil(remaining))).pipe(effect_1.Effect.andThen(waitForDeadline));
|
|
499
|
+
});
|
|
500
|
+
const deadline = effect_1.Effect.raceFirst(waitForDeadline.pipe(effect_1.Effect.as("deadline")), effect_1.Effect.promise(() => executionTerminal).pipe(effect_1.Effect.as("terminal"))).pipe(effect_1.Effect.flatMap((winner) => {
|
|
501
|
+
// Assertion seal is the Attempt's terminal freeze. Once it wins, the
|
|
502
|
+
// deadline competitor is permanently retired while publication,
|
|
503
|
+
// source capture, and cleanup finish under their own ownership.
|
|
504
|
+
if (winner === "terminal")
|
|
496
505
|
return effect_1.Effect.never;
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
506
|
+
return effect_1.Effect.suspend(() => {
|
|
507
|
+
// Recheck in the same synchronous section that closes admission and
|
|
508
|
+
// flips `timedOut`; seal may have won after the race selected its
|
|
509
|
+
// timer but before this continuation was scheduled.
|
|
510
|
+
if (assertionsSealed || signal.aborted)
|
|
511
|
+
return effect_1.Effect.never;
|
|
512
|
+
// 超时:message 是一层原因(首行),recentLogs 明细放进 stack 供 show 展开「卡在哪一步」;
|
|
513
|
+
// operation 取超时那一刻打开的 lifecycle operation。code 稳定为 "timeout"。
|
|
514
|
+
const text = `attempt timed out (${timeoutMs}ms, from ${timeoutSource})
|
|
500
515
|
Recent progress:
|
|
501
516
|
${recentLogs.map((l) => ` · ${l}`).join("\n")}`;
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
517
|
+
const message = (0, util_ts_1.firstLine)(text);
|
|
518
|
+
const rest = text.length > message.length ? text.slice(message.length + 1).replace(/\n+$/, "") : "";
|
|
519
|
+
const error = {
|
|
520
|
+
code: "timeout",
|
|
521
|
+
message,
|
|
522
|
+
origin: (0, timing_ts_1.attemptOrigin)((sendActive ? "agent.run" : lastPhase) ?? "eval.run"),
|
|
523
|
+
// 归属三样一起落盘:撞的是哪层时限、值多少、值从四层解析链的哪层来。
|
|
524
|
+
// 不打一个没有归属说明的 ✗(见 docs/feature/sandbox/architecture.md「时限归属」)。
|
|
525
|
+
timeout: { trigger: "attempt-deadline", limitMs: timeoutMs, source: timeoutSource },
|
|
526
|
+
...(rest.trim() !== "" ? { stack: rest } : {}),
|
|
527
|
+
};
|
|
528
|
+
deadlineError = error;
|
|
529
|
+
recorder.failCurrent();
|
|
530
|
+
// `timeoutTo` 已经赢得 deadline:先同步关闭所有作者/Core 修改入口,
|
|
531
|
+
// 再标记并 abort 协作式 adapter。同步 abort listener 因而也不能追加断言。
|
|
532
|
+
// Scope release below seals the Attempt-local Assertions runtime as
|
|
533
|
+
// producer-interrupted before resources are released.
|
|
534
|
+
// 这个 controller 没有独立 timer;它只把已经裁定的 timeout 传给观察 signal 的
|
|
535
|
+
// adapter。标记必须先于 abort,才能把本 fiber 的 timeout 中断与真正外层取消区分。
|
|
536
|
+
closeAuthoring("attempt-interrupted");
|
|
537
|
+
timedOut = true;
|
|
538
|
+
cancellation.abort({ kind: "timeout", timeoutMs, source: timeoutSource, deadlineAt: deadlineAt });
|
|
539
|
+
// 置位给下面的 finalizer 用(它在 Sample release 里跑,LIFO 早于 sandbox stop,
|
|
540
|
+
// 补折叠 workspace.diff / sources——见该 finalizer 的注释)。events/usage 不必等它:
|
|
541
|
+
// SessionManager 是外层已经登记过的活引用(见 liveEvents/liveUsage),截至这一刻已经
|
|
542
|
+
// 归一化的事件与已累计的用量此刻就能直接读出,不随放弃的 body fiber 一起消失。
|
|
543
|
+
const events = liveEvents?.();
|
|
544
|
+
const usage = liveUsage?.();
|
|
545
|
+
const retryAttempts = liveRetryAttempts?.();
|
|
546
|
+
return effect_1.Effect.succeed({
|
|
547
|
+
...base,
|
|
548
|
+
durationMs: recorder.offsetNow(),
|
|
549
|
+
error,
|
|
550
|
+
...(evalDef.evaluationKind === "score" ? { scoreResult: scoreFactOutcomeForAttemptError(error) } : {}),
|
|
551
|
+
...(events !== undefined ? { events: [...events], o11y: (0, derive_ts_1.buildO11ySummary)(events) } : {}),
|
|
552
|
+
...(usage !== undefined ? { usage: { ...usage } } : {}),
|
|
553
|
+
...(retryAttempts !== undefined && retryAttempts.length > 0
|
|
554
|
+
? { retryAttempts: retryAttempts.map((attempt) => ({ ...attempt })) }
|
|
555
|
+
: {}),
|
|
556
|
+
// 超时前已经登记的命令证据(见 withCommandTiming)不因中断而丢失——它们在
|
|
557
|
+
// CommandResult 返回调用方那一刻就已经写进了这个共享数组。
|
|
558
|
+
...(commands.length > 0 ? { commands: [...commands] } : {}),
|
|
559
|
+
});
|
|
544
560
|
});
|
|
545
|
-
});
|
|
546
|
-
}));
|
|
547
|
-
return effect_1.Effect.raceFirst(self, deadline);
|
|
548
|
-
};
|
|
549
|
-
const layerCleanups = [];
|
|
550
|
-
return effect_1.Effect.uninterruptibleMask((restore) => restore(effect_1.Effect.scoped(effect_1.Effect.gen(function* () {
|
|
551
|
-
// Every Adapter kind enters through the same Attempt-owned bridge,
|
|
552
|
-
// execution deadline, Assertion seal, feedback sink, and publication
|
|
553
|
-
// callback. The driver below contributes only its own preparation,
|
|
554
|
-
// author context, and observations.
|
|
555
|
-
const assertFirst = yield* (0, assert_first_bridge_ts_1.makeAssertFirstAttemptBridge)();
|
|
556
|
-
if (adapter.kind === "custom") {
|
|
557
|
-
const resources = new attempt_resources_ts_1.AdapterAttemptResources(() => {
|
|
558
|
-
adapterUsageSnapshot = adapterUsage.close();
|
|
559
|
-
adapterAttachments.close();
|
|
560
|
-
adapterExecutionTraces.close();
|
|
561
|
-
});
|
|
562
|
-
adapterResources = resources;
|
|
563
|
-
adapterUsage = new adapter_usage_ts_1.AdapterUsageCollector(() => resources.assertCaptureOpen(), config.pricing);
|
|
564
|
-
adapterAttachments = (0, adapter_attachments_ts_1.createAdapterAttachmentCollector)(`${(0, root_ts_1.recordRootPaths)(opts.recordRoot).portableRoot}/attachment-staging`);
|
|
565
|
-
const ownedAttachments = adapterAttachments;
|
|
566
|
-
yield* effect_1.Scope.addFinalizer(attachmentScope, effect_1.Effect.promise(() => ownedAttachments.dispose()));
|
|
567
|
-
adapterExecutionTraces = (0, adapter_execution_trace_ts_1.createAdapterExecutionTraceCollector)(adapterAttachments.artifacts);
|
|
568
|
-
// Scope LIFO freezes Assertions before releasing author resources.
|
|
569
|
-
// The final execution outcome is handed off only after capture drains.
|
|
570
|
-
yield* effect_1.Effect.addFinalizer(() => cleanupAdapterResources(resources, {
|
|
571
|
-
enterPhase,
|
|
572
|
-
recorder,
|
|
573
|
-
feedback: scopedFeedback,
|
|
574
|
-
onTimeout: (error) => { adapterCleanupError = error; },
|
|
575
|
-
}));
|
|
576
|
-
yield* effect_1.Effect.addFinalizer((exit) => effect_1.Effect.suspend(() => {
|
|
577
|
-
const interrupted = timedOut || (effect_1.Exit.isFailure(exit) && effect_1.Cause.hasInterruptsOnly(exit.cause));
|
|
578
|
-
if (!interrupted || liveAssertions === undefined || assertionsSealed) {
|
|
579
|
-
return effect_1.Effect.void;
|
|
580
|
-
}
|
|
581
|
-
closeAuthoring("attempt-interrupted");
|
|
582
|
-
sourceCapture.markInterrupted();
|
|
583
|
-
return (0, assertions_ts_1.sealAttemptAssertions)(liveAssertions, {
|
|
584
|
-
execution: "errored",
|
|
585
|
-
interrupted: true,
|
|
586
|
-
}).pipe(effect_1.Effect.tap((sealed) => effect_1.Effect.sync(() => {
|
|
587
|
-
markExecutionTerminal();
|
|
588
|
-
timeoutSealedAssertions = sealed;
|
|
589
|
-
adapterSealedAssertions = sealed;
|
|
590
|
-
})), effect_1.Effect.catchCause(() => effect_1.Effect.void));
|
|
591
561
|
}));
|
|
592
|
-
return
|
|
593
|
-
a,
|
|
594
|
-
base,
|
|
595
|
-
adapter,
|
|
596
|
-
resources,
|
|
597
|
-
usage: adapterUsage,
|
|
598
|
-
attachments: adapterAttachments,
|
|
599
|
-
executionTraces: adapterExecutionTraces,
|
|
600
|
-
signal,
|
|
601
|
-
sourceCapture,
|
|
602
|
-
sourceRegistry,
|
|
603
|
-
assertFirst,
|
|
604
|
-
enterPhase,
|
|
605
|
-
recorder,
|
|
606
|
-
feedback: scopedFeedback,
|
|
607
|
-
log,
|
|
608
|
-
declareFailure,
|
|
609
|
-
registerAssertions: (runtime) => {
|
|
610
|
-
if (liveAssertions !== undefined && liveAssertions !== runtime) {
|
|
611
|
-
throw new Error("Attempt tried to register a second Assertions runtime");
|
|
612
|
-
}
|
|
613
|
-
liveAssertions = runtime;
|
|
614
|
-
},
|
|
615
|
-
closeAuthoring,
|
|
616
|
-
markAssertionsSealed: (sealed) => {
|
|
617
|
-
markExecutionTerminal();
|
|
618
|
-
adapterSealedAssertions = sealed;
|
|
619
|
-
timeoutSealedAssertions = timedOut ? sealed : timeoutSealedAssertions;
|
|
620
|
-
},
|
|
621
|
-
});
|
|
622
|
-
}
|
|
623
|
-
if (agent === undefined) {
|
|
624
|
-
throw new Error(`Agent Adapter ${JSON.stringify(adapter.name)} is missing its Agent runtime`);
|
|
625
|
-
}
|
|
626
|
-
const projectStateDatabase = yield* project_state_database_ts_1.ProjectStateDatabase;
|
|
627
|
-
const sandboxPlan = a.plan._tag === "Sandbox" ? a.plan : undefined;
|
|
628
|
-
if (agent.kind === "sandbox" && sandboxPlan === undefined) {
|
|
629
|
-
throw new Error(`sandbox agent ${JSON.stringify(agent.name)} received a Direct plan`);
|
|
630
|
-
}
|
|
631
|
-
const runtimeCapabilities = sandboxPlan === undefined ? undefined : (0, runtime_ts_1.sandboxRuntimeCapabilities)(sandboxPlan);
|
|
632
|
-
// 留存 disposition:只在本 attempt 内可变,初始 stop;只有留存提交成功才改成 keep
|
|
633
|
-
// (Ctrl+C 中断外层 Sample 时仍是 stop,照常清理)。是否可留存只读 physical plan
|
|
634
|
-
// 的中性 retention 能力;不在 runner 里按 provider 名或旧声明结构分支。
|
|
635
|
-
let disposition = "stop";
|
|
636
|
-
// The bridge owns its Queue worker inside this Attempt Scope. Promise
|
|
637
|
-
// code only offers work; no internal module starts a second runtime.
|
|
638
|
-
// 退避重试(runtime.ts → retry.ts)期间临时归还这个名额:被限流的 provider 只是在
|
|
639
|
-
// setTimeout 里睡觉,不该攥着 sandboxSem 的槽位陪跑,不然一批 429 能把整体并发拖成个位数。
|
|
640
|
-
const provisioningPermit = (0, provisioning_permit_ts_1.makeProvisioningPermitOwner)(sandboxSem);
|
|
641
|
-
const provisionSlot = provisioningPermit;
|
|
642
|
-
const admissionSlot = {
|
|
643
|
-
// The ordinary provisioning permit is innermost and leaves first. The
|
|
644
|
-
// scheduler permits then leave through their stateful owner handle.
|
|
645
|
-
release: provisioningPermit.release.pipe(effect_1.Effect.andThen(concurrencySlot?.release ?? effect_1.Effect.void)),
|
|
646
|
-
// Re-enter the scheduler in its original order before taking the
|
|
647
|
-
// ordinary provisioning permit again. Each scheduler owner and the
|
|
648
|
-
// provisioning owner is stateful; cancellation cleans only permits
|
|
649
|
-
// actually reacquired and never issues a blind release.
|
|
650
|
-
reacquire: (concurrencySlot?.reacquire ?? effect_1.Effect.void).pipe(effect_1.Effect.andThen(provisioningPermit.reacquire)),
|
|
651
|
-
};
|
|
652
|
-
const runtimeAdmission = {
|
|
653
|
-
queued: (reason) => (providerAdmission?.queued(reason) ?? effect_1.Effect.void).pipe(effect_1.Effect.andThen(effect_1.Effect.sync(() => enterPhase("sandbox.queue")))),
|
|
654
|
-
granted: (providerAdmission?.granted ?? effect_1.Effect.void).pipe(effect_1.Effect.andThen(effect_1.Effect.sync(() => {
|
|
655
|
-
enterPhase("sandbox.create");
|
|
656
|
-
log(`starting sandbox...`);
|
|
657
|
-
}))),
|
|
658
|
-
slot: admissionSlot,
|
|
562
|
+
return effect_1.Effect.raceFirst(self, deadline);
|
|
659
563
|
};
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
const
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
deadline: runtimeDeadline,
|
|
694
|
-
feedback: freshSandboxLifecycleFeedback,
|
|
695
|
-
signal,
|
|
696
|
-
hookContext: {
|
|
697
|
-
experimentId: sandboxPlan.pair.experimentId,
|
|
698
|
-
signal,
|
|
699
|
-
progress: freshSandboxLifecycleFeedback.progress,
|
|
700
|
-
diagnostic: freshSandboxLifecycleFeedback.diagnostic,
|
|
701
|
-
},
|
|
702
|
-
buildLocators,
|
|
703
|
-
...(preparedSetupPrefix === undefined
|
|
704
|
-
? {}
|
|
705
|
-
: { setupPrefixArtifact: preparedSetupPrefix.artifact.locator }),
|
|
706
|
-
agent: agent.kind === "sandbox" ? agent : undefined,
|
|
707
|
-
...(runTiming !== undefined ? { runTiming } : {}),
|
|
708
|
-
provisionSlot: { _tag: "Bound", value: provisionSlot },
|
|
709
|
-
admission: { _tag: "Bound", value: runtimeAdmission },
|
|
710
|
-
services: runtime_ts_1.liveSandboxRuntimeServices,
|
|
711
|
-
// Provider runtime 自己用 acquireRelease 持有 Case;Attempt 只提交显式
|
|
712
|
-
// Managed disposition,避免外层再包一层 acquireRelease 造成 double-stop。
|
|
713
|
-
release: {
|
|
714
|
-
_tag: "Managed",
|
|
715
|
-
run: (owned) => effect_1.Effect.gen(function* () {
|
|
716
|
-
const sb = owned.sandbox;
|
|
717
|
-
if (disposition !== "keep") {
|
|
718
|
-
return yield* tryPromiseAsDefect(() => owned.group.stop()).pipe(effect_1.Effect.onExit((exit) => effect_1.Exit.isFailure(exit)
|
|
719
|
-
? effect_1.Effect.sync(() => {
|
|
720
|
-
recordSandboxCleanupFailure("sandbox.stop", effect_1.Cause.squash(exit.cause));
|
|
721
|
-
})
|
|
722
|
-
: effect_1.Effect.void));
|
|
723
|
-
}
|
|
724
|
-
yield* effect_1.Effect.sync(() => (0, registry_ts_1.unregisterSandbox)(sb));
|
|
725
|
-
const providerName = runtimeCapabilities.provider;
|
|
726
|
-
const suspendStart = recorder.offsetNow();
|
|
727
|
-
return yield* tryPromiseAsDefect(() => (0, keep_ts_1.suspendSandbox)(sb)).pipe(effect_1.Effect.tap(() => effect_1.Effect.sync(() => {
|
|
728
|
-
recorder.record("sandbox.suspend", recorder.offsetNow() - suspendStart);
|
|
729
|
-
})), effect_1.Effect.andThen((0, keep_registry_ts_1.updateKeptEntryEffect)(coordinationRoot, (0, keep_registry_ts_1.keptEntryId)(providerName, sb.sandboxId), {
|
|
730
|
-
state: "dormant",
|
|
731
|
-
}).pipe(effect_1.Effect.provideService(project_state_database_ts_1.ProjectStateDatabase, projectStateDatabase), effect_1.Effect.ignore)), effect_1.Effect.catchCause((cause) => effect_1.Cause.hasInterruptsOnly(cause)
|
|
732
|
-
? effect_1.Effect.failCause(cause)
|
|
733
|
-
: effect_1.Effect.sync(() => {
|
|
734
|
-
const error = effect_1.Cause.squash(cause);
|
|
735
|
-
recorder.record("sandbox.suspend", recorder.offsetNow() - suspendStart, true);
|
|
736
|
-
recordDiagnostic({
|
|
737
|
-
code: "sandbox-suspend-failed",
|
|
738
|
-
level: "warning",
|
|
739
|
-
message: `sandbox ${sb.sandboxId} kept but suspend failed; the instance is still running: ${error instanceof Error ? error.message : String(error)}`,
|
|
740
|
-
dedupeKey: `sandbox-suspend-failed:${sb.sandboxId}`,
|
|
741
|
-
});
|
|
742
|
-
})));
|
|
743
|
-
}),
|
|
744
|
-
},
|
|
745
|
-
};
|
|
746
|
-
const materialized = yield* effect_1.Effect.acquireUseRelease(provisioningPermit.acquire, () => (0, runtime_ts_1.acquireSandboxRunPlan)(runtimeInput), () => provisioningPermit.release);
|
|
747
|
-
// Legacy layer/plugin setup is the earliest opaque barrier and runs after
|
|
748
|
-
// provider admission has been returned, while the same Attempt Scope still owns release.
|
|
749
|
-
if (sandboxPlan.pair.setupHooks.length > 0 || sandboxPlan.pair.pluginLifecycles.length > 0) {
|
|
750
|
-
enterPhase("sandbox.prepare");
|
|
564
|
+
const layerCleanups = [];
|
|
565
|
+
return effect_1.Effect.uninterruptibleMask((restore) => restore(effect_1.Effect.scoped(effect_1.Effect.gen(function* () {
|
|
566
|
+
// Every Adapter kind enters through the same Attempt-owned bridge,
|
|
567
|
+
// execution deadline, Assertion seal, feedback sink, and publication
|
|
568
|
+
// callback. The driver below contributes only its own preparation,
|
|
569
|
+
// author context, and observations.
|
|
570
|
+
yield* effect_1.Effect.addFinalizer(() => effect_1.Effect.promise(() => judgeUsage.closeAndDrain()));
|
|
571
|
+
const assertFirst = yield* (0, assert_first_bridge_ts_1.makeAssertFirstAttemptBridge)();
|
|
572
|
+
if (adapter.kind === "custom") {
|
|
573
|
+
const resources = new attempt_resources_ts_1.AdapterAttemptResources(() => {
|
|
574
|
+
adapterUsageSnapshot = adapterUsage.close();
|
|
575
|
+
adapterExecutionTraces.close();
|
|
576
|
+
adapterAttachments.close();
|
|
577
|
+
});
|
|
578
|
+
adapterResources = resources;
|
|
579
|
+
adapterUsage = new adapter_usage_ts_1.AdapterUsageCollector(() => resources.assertCaptureOpen(), config.pricing, run.models ?? Object.freeze({}));
|
|
580
|
+
adapterAttachments = (0, adapter_attachments_ts_1.createAdapterAttachmentCollector)(`${(0, root_ts_1.recordRootPaths)(opts.recordRoot).portableRoot}/attachment-staging`);
|
|
581
|
+
const ownedAttachments = adapterAttachments;
|
|
582
|
+
yield* effect_1.Scope.addFinalizer(attachmentScope, effect_1.Effect.promise(() => ownedAttachments.dispose()));
|
|
583
|
+
adapterExecutionTraces = (0, adapter_execution_trace_ts_1.createAdapterExecutionTraceCollector)(adapterAttachments.artifacts);
|
|
584
|
+
// Scope LIFO freezes Assertions before releasing author resources.
|
|
585
|
+
// The final execution outcome is handed off only after capture drains.
|
|
586
|
+
yield* effect_1.Effect.addFinalizer(() => cleanupAdapterResources(resources, {
|
|
587
|
+
timeoutMs: adapter.cleanupTimeoutMs,
|
|
588
|
+
enterPhase,
|
|
589
|
+
recorder,
|
|
590
|
+
feedback: scopedFeedback,
|
|
591
|
+
onTimeout: (error) => { adapterCleanupError = error; },
|
|
592
|
+
}));
|
|
593
|
+
yield* effect_1.Effect.addFinalizer((exit) => effect_1.Effect.suspend(() => {
|
|
594
|
+
const interrupted = timedOut || (effect_1.Exit.isFailure(exit) && effect_1.Cause.hasInterruptsOnly(exit.cause));
|
|
595
|
+
if (!interrupted || liveAssertions === undefined || assertionsSealed) {
|
|
596
|
+
return effect_1.Effect.void;
|
|
751
597
|
}
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
const endpoint = otelChannel.receiver.endpoint(config.telemetry?.host ?? "127.0.0.1");
|
|
794
|
-
const env = agent.tracing?.env?.(endpoint);
|
|
795
|
-
telemetry = env ? { endpoint, env } : { endpoint };
|
|
796
|
-
log(`OTLP shared receiver (run-scoped) -> ${endpoint}`);
|
|
797
|
-
}
|
|
798
|
-
catch (e) {
|
|
799
|
-
if (isAttemptAborted(parentSignal))
|
|
800
|
-
throw e;
|
|
801
|
-
recordDiagnostic({
|
|
802
|
-
code: "telemetry-configure-failed",
|
|
803
|
-
level: "warning",
|
|
804
|
-
message: `telemetry configure failed; continuing without OTLP trace: ${(0, util_ts_1.firstLine)((0, util_ts_1.formatThrown)(e))}`,
|
|
805
|
-
data: evidenceDiagnosticData("configure", e),
|
|
598
|
+
closeAuthoring("attempt-interrupted");
|
|
599
|
+
sourceCapture.markInterrupted();
|
|
600
|
+
return (0, assertions_ts_1.sealAttemptAssertions)(liveAssertions, {
|
|
601
|
+
execution: "errored",
|
|
602
|
+
interrupted: true,
|
|
603
|
+
}).pipe(effect_1.Effect.tap((sealed) => effect_1.Effect.sync(() => {
|
|
604
|
+
markExecutionTerminal();
|
|
605
|
+
timeoutSealedAssertions = sealed;
|
|
606
|
+
adapterSealedAssertions = sealed;
|
|
607
|
+
})), effect_1.Effect.catchCause(() => effect_1.Effect.void));
|
|
608
|
+
}));
|
|
609
|
+
return yield* runAdapterAttemptBody({
|
|
610
|
+
a,
|
|
611
|
+
base,
|
|
612
|
+
adapter,
|
|
613
|
+
resources,
|
|
614
|
+
usage: adapterUsage,
|
|
615
|
+
judgeUsage,
|
|
616
|
+
attachments: adapterAttachments,
|
|
617
|
+
executionTraces: adapterExecutionTraces,
|
|
618
|
+
signal,
|
|
619
|
+
sourceCapture,
|
|
620
|
+
sourceRegistry,
|
|
621
|
+
assertFirst,
|
|
622
|
+
enterPhase,
|
|
623
|
+
recorder,
|
|
624
|
+
feedback: scopedFeedback,
|
|
625
|
+
log,
|
|
626
|
+
declareFailure,
|
|
627
|
+
registerAssertions: (runtime) => {
|
|
628
|
+
if (liveAssertions !== undefined && liveAssertions !== runtime) {
|
|
629
|
+
throw new Error("Attempt tried to register a second Assertions runtime");
|
|
630
|
+
}
|
|
631
|
+
liveAssertions = runtime;
|
|
632
|
+
},
|
|
633
|
+
closeAuthoring,
|
|
634
|
+
markAssertionsSealed: (sealed) => {
|
|
635
|
+
markExecutionTerminal();
|
|
636
|
+
adapterSealedAssertions = sealed;
|
|
637
|
+
timeoutSealedAssertions = timedOut ? sealed : timeoutSealedAssertions;
|
|
638
|
+
},
|
|
806
639
|
});
|
|
807
|
-
otelChannel = undefined;
|
|
808
|
-
telemetry = undefined;
|
|
809
640
|
}
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
641
|
+
if (agent === undefined) {
|
|
642
|
+
throw new Error(`Agent Adapter ${JSON.stringify(adapter.name)} is missing its Agent runtime`);
|
|
643
|
+
}
|
|
644
|
+
const projectStateDatabase = yield* project_state_database_ts_1.ProjectStateDatabase;
|
|
645
|
+
const sandboxPlan = a.plan._tag === "Sandbox" ? a.plan : undefined;
|
|
646
|
+
if (agent.kind === "sandbox" && sandboxPlan === undefined) {
|
|
647
|
+
throw new Error(`sandbox agent ${JSON.stringify(agent.name)} received a Direct plan`);
|
|
648
|
+
}
|
|
649
|
+
const runtimeCapabilities = sandboxPlan === undefined ? undefined : (0, runtime_ts_1.sandboxRuntimeCapabilities)(sandboxPlan);
|
|
650
|
+
// 留存 disposition:只在本 attempt 内可变,初始 stop;只有留存提交成功才改成 keep
|
|
651
|
+
// (Ctrl+C 中断外层 Sample 时仍是 stop,照常清理)。是否可留存只读 physical plan
|
|
652
|
+
// 的中性 retention 能力;不在 runner 里按 provider 名或旧声明结构分支。
|
|
653
|
+
let disposition = "stop";
|
|
654
|
+
// The bridge owns its Queue worker inside this Attempt Scope. Promise
|
|
655
|
+
// code only offers work; no internal module starts a second runtime.
|
|
656
|
+
// 退避重试(runtime.ts → retry.ts)期间临时归还这个名额:被限流的 provider 只是在
|
|
657
|
+
// setTimeout 里睡觉,不该攥着 sandboxSem 的槽位陪跑,不然一批 429 能把整体并发拖成个位数。
|
|
658
|
+
const provisioningPermit = (0, provisioning_permit_ts_1.makeProvisioningPermitOwner)(sandboxSem);
|
|
659
|
+
const provisionSlot = provisioningPermit;
|
|
660
|
+
const admissionSlot = {
|
|
661
|
+
// The ordinary provisioning permit is innermost and leaves first. The
|
|
662
|
+
// scheduler permits then leave through their stateful owner handle.
|
|
663
|
+
release: provisioningPermit.release.pipe(effect_1.Effect.andThen(concurrencySlot?.release ?? effect_1.Effect.void)),
|
|
664
|
+
// Re-enter the scheduler in its original order before taking the
|
|
665
|
+
// ordinary provisioning permit again. Each scheduler owner and the
|
|
666
|
+
// provisioning owner is stateful; cancellation cleans only permits
|
|
667
|
+
// actually reacquired and never issues a blind release.
|
|
668
|
+
reacquire: (concurrencySlot?.reacquire ?? effect_1.Effect.void).pipe(effect_1.Effect.andThen(provisioningPermit.reacquire)),
|
|
669
|
+
};
|
|
670
|
+
const runtimeAdmission = {
|
|
671
|
+
queued: (reason) => (providerAdmission?.queued(reason) ?? effect_1.Effect.void).pipe(effect_1.Effect.andThen(effect_1.Effect.sync(() => enterPhase("sandbox.queue")))),
|
|
672
|
+
granted: (providerAdmission?.granted ?? effect_1.Effect.void).pipe(effect_1.Effect.andThen(effect_1.Effect.sync(() => {
|
|
673
|
+
enterPhase("sandbox.create");
|
|
674
|
+
log(`starting sandbox...`);
|
|
675
|
+
}))),
|
|
676
|
+
slot: admissionSlot,
|
|
677
|
+
};
|
|
678
|
+
// Sample release(receiver close + provider stop)整段计成 sandbox.stop:先加的 finalizer
|
|
679
|
+
// 后跑(LIFO),所以「先加的」在 release 链末尾打终点戳、「后加的」在 release 开始前打起点戳;
|
|
680
|
+
// 结果封口(附 phases)发生在 Sample release 完成之后(见下方 Effect.map)。
|
|
681
|
+
let releaseStartedAt;
|
|
682
|
+
if (agent.kind === "sandbox" && !reusedSandbox) {
|
|
683
|
+
yield* effect_1.Effect.addFinalizer(() => effect_1.Effect.sync(() => {
|
|
684
|
+
// 留存路径的 phases 以 sandbox.suspend 结尾,没有 sandbox.stop 条目(见 release)。
|
|
685
|
+
if (releaseStartedAt !== undefined && disposition !== "keep") {
|
|
686
|
+
recorder.record("sandbox.stop", recorder.offsetNow() - releaseStartedAt);
|
|
687
|
+
}
|
|
688
|
+
}));
|
|
689
|
+
}
|
|
690
|
+
let materializedCase;
|
|
691
|
+
const sandbox = reusedSandbox?.sandbox ??
|
|
692
|
+
(agent.kind === "sandbox"
|
|
693
|
+
? yield* effect_1.Effect.gen(function* () {
|
|
694
|
+
// ── 沙箱:acquire=起,release=整组 stop(成功 / 失败 / 中断都跑)──
|
|
695
|
+
// sandboxSem 只覆盖「容器创建」阶段;容器起好后立即释放,后续 npm install / agent 不占位。
|
|
696
|
+
// provider 固有的会话上限不能静默充当默认值:deadline 超出它时,attempt
|
|
697
|
+
// 会跑到一半被平台截断。在派发前就报环境约束,点名 provider 与上限值。
|
|
698
|
+
if (sandboxPlan === undefined || runtimeCapabilities === undefined) {
|
|
699
|
+
throw new Error("sandbox plan invariant violated before materialization");
|
|
700
|
+
}
|
|
701
|
+
assertDeadlineFitsPlan(runtimeCapabilities, attemptTimeout?.timeoutMs);
|
|
702
|
+
if (opts.keepSandbox !== undefined && runtimeCapabilities.retention._tag !== "Suspendable") {
|
|
703
|
+
throw new Error(`--keep-sandbox is unsupported by provider ${JSON.stringify(runtimeCapabilities.provider)}`);
|
|
704
|
+
}
|
|
705
|
+
const runtimeDeadline = attemptTimeout === undefined || deadlineAt === undefined
|
|
706
|
+
? { _tag: "Unlimited" }
|
|
707
|
+
: { _tag: "Bounded", timeoutMs: attemptTimeout.timeoutMs, deadlineAt };
|
|
708
|
+
const runtimeInput = {
|
|
709
|
+
plan: sandboxPlan,
|
|
710
|
+
evalId: evalDef.id,
|
|
711
|
+
deadline: runtimeDeadline,
|
|
712
|
+
feedback: freshSandboxLifecycleFeedback,
|
|
713
|
+
signal,
|
|
714
|
+
hookContext: {
|
|
715
|
+
experimentId: sandboxPlan.pair.experimentId,
|
|
716
|
+
signal,
|
|
717
|
+
progress: freshSandboxLifecycleFeedback.progress,
|
|
718
|
+
diagnostic: freshSandboxLifecycleFeedback.diagnostic,
|
|
719
|
+
},
|
|
720
|
+
buildLocators,
|
|
721
|
+
...(preparedSetupPrefix === undefined
|
|
722
|
+
? {}
|
|
723
|
+
: { setupPrefixArtifact: preparedSetupPrefix.artifact.locator }),
|
|
724
|
+
agent: agent.kind === "sandbox" ? agent : undefined,
|
|
725
|
+
...(runTiming !== undefined ? { runTiming } : {}),
|
|
726
|
+
provisionSlot: { _tag: "Bound", value: provisionSlot },
|
|
727
|
+
admission: { _tag: "Bound", value: runtimeAdmission },
|
|
728
|
+
services: runtime_ts_1.liveSandboxRuntimeServices,
|
|
729
|
+
// Provider runtime 自己用 acquireRelease 持有 Case;Attempt 只提交显式
|
|
730
|
+
// Managed disposition,避免外层再包一层 acquireRelease 造成 double-stop。
|
|
731
|
+
release: {
|
|
732
|
+
_tag: "Managed",
|
|
733
|
+
run: (owned) => effect_1.Effect.gen(function* () {
|
|
734
|
+
const sb = owned.sandbox;
|
|
735
|
+
if (disposition !== "keep") {
|
|
736
|
+
return yield* tryPromiseAsDefect(() => owned.group.stop()).pipe(effect_1.Effect.onExit((exit) => effect_1.Exit.isFailure(exit)
|
|
737
|
+
? effect_1.Effect.sync(() => {
|
|
738
|
+
recordSandboxCleanupFailure("sandbox.stop", effect_1.Cause.squash(exit.cause));
|
|
739
|
+
})
|
|
740
|
+
: effect_1.Effect.void));
|
|
741
|
+
}
|
|
742
|
+
yield* effect_1.Effect.sync(() => (0, registry_ts_1.unregisterSandbox)(sb));
|
|
743
|
+
const providerName = runtimeCapabilities.provider;
|
|
744
|
+
const suspendStart = recorder.offsetNow();
|
|
745
|
+
return yield* tryPromiseAsDefect(() => (0, keep_ts_1.suspendSandbox)(sb)).pipe(effect_1.Effect.tap(() => effect_1.Effect.sync(() => {
|
|
746
|
+
recorder.record("sandbox.suspend", recorder.offsetNow() - suspendStart);
|
|
747
|
+
})), effect_1.Effect.andThen((0, keep_registry_ts_1.updateKeptEntryEffect)(coordinationRoot, (0, keep_registry_ts_1.keptEntryId)(providerName, sb.sandboxId), {
|
|
748
|
+
state: "dormant",
|
|
749
|
+
}).pipe(effect_1.Effect.provideService(project_state_database_ts_1.ProjectStateDatabase, projectStateDatabase), effect_1.Effect.ignore)), effect_1.Effect.catchCause((cause) => effect_1.Cause.hasInterruptsOnly(cause)
|
|
750
|
+
? effect_1.Effect.failCause(cause)
|
|
751
|
+
: effect_1.Effect.sync(() => {
|
|
752
|
+
const error = effect_1.Cause.squash(cause);
|
|
753
|
+
recorder.record("sandbox.suspend", recorder.offsetNow() - suspendStart, true);
|
|
754
|
+
recordDiagnostic({
|
|
755
|
+
code: "sandbox-suspend-failed",
|
|
756
|
+
level: "warning",
|
|
757
|
+
message: `sandbox ${sb.sandboxId} kept but suspend failed; the instance is still running: ${error instanceof Error ? error.message : String(error)}`,
|
|
758
|
+
dedupeKey: `sandbox-suspend-failed:${sb.sandboxId}`,
|
|
759
|
+
});
|
|
760
|
+
})));
|
|
761
|
+
}),
|
|
762
|
+
},
|
|
763
|
+
};
|
|
764
|
+
const materialized = yield* effect_1.Effect.acquireUseRelease(provisioningPermit.acquire, () => (0, runtime_ts_1.acquireSandboxRunPlan)(runtimeInput), () => provisioningPermit.release);
|
|
765
|
+
// Legacy layer/plugin setup is the earliest opaque barrier and runs after
|
|
766
|
+
// provider admission has been returned, while the same Attempt Scope still owns release.
|
|
767
|
+
if (sandboxPlan.pair.setupHooks.length > 0 || sandboxPlan.pair.pluginLifecycles.length > 0) {
|
|
768
|
+
enterPhase("sandbox.prepare");
|
|
769
|
+
}
|
|
770
|
+
yield* (0, runtime_ts_1.prepareMaterializedSandboxRunPlan)(runtimeInput, materialized);
|
|
771
|
+
materializedCase = materialized;
|
|
772
|
+
// fresh Attempt:author facade 在当前 Attempt Scope 内构建。executor 捕获本
|
|
773
|
+
// Scope 的 Runtime,Scope 关闭时统一中断在飞请求并拒绝新调用;release closure
|
|
774
|
+
// 继续用 materialized.sandbox(资源面 facade),两者生命周期互不借用。
|
|
775
|
+
const executor = yield* (0, request_executor_ts_1.makeSandboxRequestExecutor)();
|
|
776
|
+
return (0, paths_ts_1.makeSandboxAuthorFacade)(materialized.authorBackend, executor, runtimeCapabilities.provider);
|
|
777
|
+
})
|
|
778
|
+
: (0, direct_agent_sandbox_ts_1.createDirectAgentSandbox)());
|
|
779
|
+
currentSandbox = sandbox;
|
|
780
|
+
// 一次性沙箱的租借时刻:实例到手就定归属,后面无论走到哪一步终结都带着它。
|
|
781
|
+
if (agent.kind === "sandbox" && !sandboxFacts) {
|
|
782
|
+
sandboxFacts = { provider: runtimeCapabilities.provider, sandboxId: sandbox.sandboxId };
|
|
783
|
+
}
|
|
784
|
+
if (agent.kind !== "sandbox")
|
|
785
|
+
log(`using Direct Agent (no Sandbox created)...`);
|
|
786
|
+
const commandTarget = (0, operations_ts_1.createSandboxCommandTarget)(sandbox);
|
|
787
|
+
// A fresh author facade forks requests into this Attempt Scope. Once Scope.close starts,
|
|
788
|
+
// forkIn() interrupts new children immediately even before the executor's own finalizer flips
|
|
789
|
+
// its closed flag. Cleanup therefore uses the provider-owned resource facade; before/Agent/test
|
|
790
|
+
// continue to use the interruptible author facade above.
|
|
791
|
+
const cleanupCommandTarget = (0, operations_ts_1.createSandboxCommandTarget)(reusedSandbox?.resourceSandbox ?? materializedCase?.sandbox ?? sandbox);
|
|
792
|
+
// ── tracing ──────────────────────────────────────────────────────────────────
|
|
793
|
+
// sandbox.otlpHost:
|
|
794
|
+
// string → provider 承诺该 hostname 可访问宿主 receiver
|
|
795
|
+
// null → provider 不承诺宿主回连,改在沙箱内起 attempt-scope collector
|
|
796
|
+
// defineConfig({ telemetry: { host } }) 可强制覆盖(如配好 tunnel 时)。
|
|
797
|
+
//
|
|
798
|
+
// 非沙箱 agent(远程 / 进程内)不走 per-attempt receiver:被测应用是长驻进程,只有一条
|
|
799
|
+
// 全局 OTel 管线(OTEL_* env 进程启动时读一次)—— per-attempt 端口会在第一个 attempt
|
|
800
|
+
// 结束时关掉,后续 span 全丢。改走 run 级共享池,span 逐轮归属(traceparent / 窗口)。
|
|
801
|
+
let receiver;
|
|
802
|
+
let telemetry;
|
|
803
|
+
let otelChannel;
|
|
804
|
+
// 共享池仅限:config 配了 telemetry(固定端口,无侵入接入的长驻服务)或显式 tracing.scope === "run"。
|
|
805
|
+
// 只声明 tracing 的进程内 adapter(如 aiSdkAgent)保持 per-attempt receiver,attempt 全并发。
|
|
806
|
+
const wantsSharedOtel = config.telemetry !== undefined || agent.tracing?.scope === "run";
|
|
807
|
+
if (agent.kind !== "sandbox" && wantsSharedOtel && opts.otelPool) {
|
|
808
|
+
enterPhase("telemetry.configure");
|
|
809
|
+
try {
|
|
810
|
+
otelChannel = yield* opts.otelPool.channel(agent.name);
|
|
811
|
+
const endpoint = otelChannel.receiver.endpoint(config.telemetry?.host ?? "127.0.0.1");
|
|
819
812
|
const env = agent.tracing?.env?.(endpoint);
|
|
820
813
|
telemetry = env ? { endpoint, env } : { endpoint };
|
|
821
|
-
log(`OTLP receiver (
|
|
814
|
+
log(`OTLP shared receiver (run-scoped) -> ${endpoint}`);
|
|
822
815
|
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
816
|
+
catch (e) {
|
|
817
|
+
if (isAttemptAborted(parentSignal))
|
|
818
|
+
throw e;
|
|
819
|
+
recordDiagnostic({
|
|
820
|
+
code: "telemetry-configure-failed",
|
|
821
|
+
level: "warning",
|
|
822
|
+
message: `telemetry configure failed; continuing without OTLP trace: ${(0, util_ts_1.firstLine)((0, util_ts_1.formatThrown)(e))}`,
|
|
823
|
+
data: evidenceDiagnosticData("configure", e),
|
|
824
|
+
});
|
|
825
|
+
otelChannel = undefined;
|
|
826
|
+
telemetry = undefined;
|
|
831
827
|
}
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
const
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
828
|
+
}
|
|
829
|
+
else if (agent.tracing !== undefined) {
|
|
830
|
+
enterPhase("telemetry.configure");
|
|
831
|
+
try {
|
|
832
|
+
const forcedHost = config.telemetry?.host;
|
|
833
|
+
if (forcedHost) {
|
|
834
|
+
// 显式覆盖:走本地接收器,把指定 host 交给 agent
|
|
835
|
+
receiver = yield* (0, receiver_ts_1.createTraceReceiver)();
|
|
836
|
+
const endpoint = receiver.endpoint(forcedHost);
|
|
837
|
+
const env = agent.tracing?.env?.(endpoint);
|
|
838
|
+
telemetry = env ? { endpoint, env } : { endpoint };
|
|
839
|
+
log(`OTLP receiver (host override) -> ${endpoint}`);
|
|
840
|
+
}
|
|
841
|
+
else if (sandbox.otlpHost !== null) {
|
|
842
|
+
// provider 明确承诺宿主回连:宿主开接收器
|
|
843
|
+
receiver = yield* (0, receiver_ts_1.createTraceReceiver)();
|
|
844
|
+
const endpoint = receiver.endpoint(sandbox.otlpHost);
|
|
845
|
+
const env = agent.tracing?.env?.(endpoint);
|
|
846
|
+
telemetry = env ? { endpoint, env } : { endpoint };
|
|
847
|
+
const proto = agent.tracing?.protocol;
|
|
848
|
+
log(`OTLP receiver -> ${endpoint}${proto ? ` (${proto})` : ""}`);
|
|
849
|
+
}
|
|
850
|
+
else {
|
|
851
|
+
// provider 不承诺宿主回连:在沙箱内起 collector,agent 往 localhost 端口发。
|
|
852
|
+
receiver = yield* (0, sandbox_receiver_ts_1.createInSandboxTraceReceiver)(sandbox);
|
|
853
|
+
const endpoint = receiver.endpoint("");
|
|
854
|
+
const env = agent.tracing?.env?.(endpoint);
|
|
855
|
+
telemetry = env ? { endpoint, env } : { endpoint };
|
|
856
|
+
const proto = agent.tracing?.protocol;
|
|
857
|
+
log(`OTLP in-sandbox collector -> ${endpoint}${proto ? ` (${proto})` : ""}`);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
catch (e) {
|
|
861
|
+
if (isAttemptAborted(parentSignal))
|
|
862
|
+
throw e;
|
|
863
|
+
recordDiagnostic({
|
|
864
|
+
code: "telemetry-configure-failed",
|
|
865
|
+
level: "warning",
|
|
866
|
+
message: `telemetry configure failed; continuing without OTLP trace: ${(0, util_ts_1.firstLine)((0, util_ts_1.formatThrown)(e))}`,
|
|
867
|
+
data: evidenceDiagnosticData("configure", e, runtimeCapabilities?.provider),
|
|
868
|
+
});
|
|
869
|
+
receiver = undefined;
|
|
870
|
+
telemetry = undefined;
|
|
840
871
|
}
|
|
841
872
|
}
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
level: "warning",
|
|
848
|
-
message: `telemetry configure failed; continuing without OTLP trace: ${(0, util_ts_1.firstLine)((0, util_ts_1.formatThrown)(e))}`,
|
|
849
|
-
data: evidenceDiagnosticData("configure", e, runtimeCapabilities?.provider),
|
|
850
|
-
});
|
|
851
|
-
receiver = undefined;
|
|
852
|
-
telemetry = undefined;
|
|
873
|
+
// attempt-scope 的进程内 adapter(如 aiSdkAgent + aiSdkOtel)虽然拥有独立 receiver,
|
|
874
|
+
// 仍需要逐轮窗口归属来给 TimingActivity 写入真实 traceId。独立 receiver 不会与其它
|
|
875
|
+
// attempt 混流,因此直接复用同一个 AgentOtelChannel 算法,不必升级成 run 级共享池。
|
|
876
|
+
if (agent.kind !== "sandbox" && receiver !== undefined && otelChannel === undefined) {
|
|
877
|
+
otelChannel = new turn_otel_ts_1.AgentOtelChannel(receiver);
|
|
853
878
|
}
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
//
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
879
|
+
if (agent.kind === "sandbox" && !reusedSandbox) {
|
|
880
|
+
// 后加先跑:release 链开始时打起点戳(与上面的终点戳配对,测出整段 sandbox.stop)。
|
|
881
|
+
yield* effect_1.Effect.addFinalizer(() => effect_1.Effect.sync(() => {
|
|
882
|
+
releaseStartedAt = recorder.offsetNow();
|
|
883
|
+
}));
|
|
884
|
+
}
|
|
885
|
+
// 超时收尾段的证据保全(docs/runner.md「超时:双层保护」超时不丢证据)。注册在这里,
|
|
886
|
+
// LIFO 意味着 release 时它先于上面已注册的 sandbox stop/suspend finalizer 跑——此刻沙箱
|
|
887
|
+
// (若有)仍然存活,赶在停之前抢一次 workspace.diff 折叠;`timedOut` 由 Effect.timeoutTo
|
|
888
|
+
// 的 onTimeout 同步置位,正常收尾(body 走完)时这里直接跳过,不重复采一次已经采过的证据。
|
|
889
|
+
// 沙箱此刻可能已经被 agent 搞挂——withCleanupTimeout 有界执行,导出挂起也不能拖住收尾,
|
|
890
|
+
// 到点如实缺失。计时单独记一条 phases 条目,不入 durationMs(durationMs 已在 onTimeout
|
|
891
|
+
// 按中断时刻定格)。sources 折叠是本地文件读取,与沙箱是否存活无关,一并在这里补。
|
|
892
|
+
yield* effect_1.Effect.addFinalizer(() => effect_1.Effect.gen(function* () {
|
|
893
|
+
if (!timedOut)
|
|
894
|
+
return;
|
|
895
|
+
if (agent.kind === "sandbox" && liveLedger) {
|
|
896
|
+
const startedAt = recorder.offsetNow();
|
|
897
|
+
const diff = yield* effect_1.Effect.result((0, cleanup_timeout_ts_1.cleanupCallback)(() => liveLedger.exportWindows()));
|
|
898
|
+
if (effect_1.Result.isSuccess(diff)) {
|
|
899
|
+
timeoutDiff = diff.success;
|
|
900
|
+
let document;
|
|
901
|
+
try {
|
|
902
|
+
document = (0, workspace_diff_ts_1.createAgentWorkspaceDiff)({
|
|
903
|
+
windows: diff.success,
|
|
904
|
+
policy: liveLedger.attribution,
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
catch {
|
|
908
|
+
timeoutFileChangesCapture = (0, diff_ts_2.createEmptyFileChangesCapture)({
|
|
909
|
+
policy: liveLedger.attribution,
|
|
910
|
+
limitations: [Object.freeze({
|
|
911
|
+
code: "capture-failed",
|
|
912
|
+
stage: "normalize",
|
|
913
|
+
atWindowId: null,
|
|
914
|
+
})],
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
if (document !== undefined) {
|
|
918
|
+
timeoutFileChangesCapture = (0, diff_ts_2.createFileChangesCapture)({
|
|
919
|
+
document,
|
|
920
|
+
limitations: [Object.freeze({
|
|
921
|
+
code: "capture-interrupted",
|
|
922
|
+
stage: "finalizer-export",
|
|
923
|
+
atWindowId: null,
|
|
924
|
+
})],
|
|
925
|
+
});
|
|
926
|
+
}
|
|
888
927
|
}
|
|
889
|
-
|
|
928
|
+
else {
|
|
890
929
|
timeoutFileChangesCapture = (0, diff_ts_2.createEmptyFileChangesCapture)({
|
|
891
930
|
policy: liveLedger.attribution,
|
|
892
|
-
limitations: [Object.freeze({
|
|
893
|
-
code: "capture-failed",
|
|
894
|
-
stage: "normalize",
|
|
895
|
-
atWindowId: null,
|
|
896
|
-
})],
|
|
897
|
-
});
|
|
898
|
-
}
|
|
899
|
-
if (document !== undefined) {
|
|
900
|
-
timeoutFileChangesCapture = (0, diff_ts_2.createFileChangesCapture)({
|
|
901
|
-
document,
|
|
902
931
|
limitations: [Object.freeze({
|
|
903
932
|
code: "capture-interrupted",
|
|
904
933
|
stage: "finalizer-export",
|
|
@@ -906,427 +935,425 @@ ${recentLogs.map((l) => ` · ${l}`).join("\n")}`;
|
|
|
906
935
|
})],
|
|
907
936
|
});
|
|
908
937
|
}
|
|
938
|
+
if (timeoutFileChangesCapture !== undefined) {
|
|
939
|
+
(0, diff_ts_2.assertFileChangesCaptureRecordable)(timeoutFileChangesCapture);
|
|
940
|
+
}
|
|
941
|
+
// 沙箱不可用 / 导出挂起到点:如实缺失,不阻塞收尾。
|
|
942
|
+
recorder.record("workspace.diff", recorder.offsetNow() - startedAt);
|
|
909
943
|
}
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
944
|
+
const sources = yield* effect_1.Effect.result((0, cleanup_timeout_ts_1.cleanupCallback)(() => collectSources(evalDef.source, sourceRegistry)));
|
|
945
|
+
if (effect_1.Result.isSuccess(sources))
|
|
946
|
+
timeoutSources = sources.success;
|
|
947
|
+
// 源码读不到:如实缺失,不阻塞收尾。
|
|
948
|
+
}));
|
|
949
|
+
// Matcher-only source material must not outlive the Attempt. Register
|
|
950
|
+
// this before the interruption seal so LIFO runs that seal first, then
|
|
951
|
+
// clears the sidecar immediately afterwards.
|
|
952
|
+
yield* effect_1.Effect.addFinalizer(() => effect_1.Effect.sync(() => liveAssertionState?.manager.releaseObservedEvaluationSegments()));
|
|
953
|
+
// Scope release owns the interruption seal. It runs before resource
|
|
954
|
+
// release, so entries already declared by the real Context survive a
|
|
955
|
+
// timeout or external cancellation in declaration order.
|
|
956
|
+
yield* effect_1.Effect.addFinalizer((exit) => effect_1.Effect.suspend(() => {
|
|
957
|
+
const interrupted = timedOut || (effect_1.Exit.isFailure(exit) && effect_1.Cause.hasInterruptsOnly(exit.cause));
|
|
958
|
+
if (interrupted)
|
|
959
|
+
sourceCapture.markInterrupted();
|
|
960
|
+
if (!interrupted || liveAssertions === undefined || assertionsSealed) {
|
|
961
|
+
return effect_1.Effect.void;
|
|
919
962
|
}
|
|
920
|
-
if (
|
|
921
|
-
|
|
963
|
+
if (liveAssertionState?.late.diff.state === "pending") {
|
|
964
|
+
liveAssertionState.late.diff = Object.freeze({
|
|
965
|
+
state: "unavailable",
|
|
966
|
+
reason: "producer-interrupted",
|
|
967
|
+
});
|
|
922
968
|
}
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
return (0, assertions_ts_1.sealAttemptAssertions)(liveAssertions, {
|
|
952
|
-
execution: "errored",
|
|
953
|
-
interrupted: true,
|
|
954
|
-
}).pipe(effect_1.Effect.tap((sealed) => effect_1.Effect.sync(() => {
|
|
955
|
-
markExecutionTerminal();
|
|
956
|
-
timeoutSealedAssertions = sealed;
|
|
957
|
-
})), effect_1.Effect.tap((sealed) => onSealedEvaluation?.(sealed) ?? effect_1.Effect.void),
|
|
958
|
-
// Release must preserve the original interruption/failure. A
|
|
959
|
-
// malformed attachment is still reflected by the sealed runtime,
|
|
960
|
-
// but cannot mask the owning Attempt's terminal cause.
|
|
961
|
-
effect_1.Effect.catchCause(() => effect_1.Effect.void));
|
|
962
|
-
}));
|
|
963
|
-
// Scope 的 LIFO 让作者 cleanup 在 agent teardown 后、Provider Case finalizer 前执行。
|
|
964
|
-
// Cleanup must not fork back into this already-closing Attempt Scope, so it uses the resource
|
|
965
|
-
// facade captured above while retaining the same physical Sandbox and provider release owner.
|
|
966
|
-
if (agent.kind === "sandbox") {
|
|
967
|
-
yield* effect_1.Effect.addFinalizer(() => effect_1.Effect.suspend(() => {
|
|
968
|
-
if (layerCleanups.length === 0)
|
|
969
|
-
return effect_1.Effect.void;
|
|
970
|
-
enterPhase("sandbox.cleanup");
|
|
971
|
-
const phaseStartedAt = recorder.offsetNow();
|
|
972
|
-
const before = diagnostics.length;
|
|
973
|
-
return effect_1.Effect.forEach([...layerCleanups].reverse(), (cleanup) => {
|
|
974
|
-
const startedAt = recorder.offsetNow();
|
|
975
|
-
const node = recorder.child((0, timing_ts_1.sandboxPrepareActivity)({
|
|
976
|
-
label: `${cleanup.label} cleanup`,
|
|
977
|
-
startOffsetMs: startedAt,
|
|
978
|
-
}));
|
|
979
|
-
if (node)
|
|
980
|
-
recorder.pushParent(node);
|
|
981
|
-
return (0, cleanup_timeout_ts_1.cleanupCallback)((cleanupSignal) => cleanup.command(cleanupCommandTarget, {
|
|
982
|
-
...cleanup.context,
|
|
983
|
-
signal: cleanupSignal,
|
|
984
|
-
})).pipe(effect_1.Effect.catch((error) => effect_1.Effect.sync(() => {
|
|
969
|
+
return (0, assertions_ts_1.sealAttemptAssertions)(liveAssertions, {
|
|
970
|
+
execution: "errored",
|
|
971
|
+
interrupted: true,
|
|
972
|
+
}).pipe(effect_1.Effect.tap((sealed) => effect_1.Effect.sync(() => {
|
|
973
|
+
markExecutionTerminal();
|
|
974
|
+
timeoutSealedAssertions = sealed;
|
|
975
|
+
})), effect_1.Effect.tap((sealed) => onSealedEvaluation?.(sealed) ?? effect_1.Effect.void),
|
|
976
|
+
// Release must preserve the original interruption/failure. A
|
|
977
|
+
// malformed attachment is still reflected by the sealed runtime,
|
|
978
|
+
// but cannot mask the owning Attempt's terminal cause.
|
|
979
|
+
effect_1.Effect.catchCause(() => effect_1.Effect.void));
|
|
980
|
+
}));
|
|
981
|
+
// Scope 的 LIFO 让作者 cleanup 在 agent teardown 后、Provider Case finalizer 前执行。
|
|
982
|
+
// Cleanup must not fork back into this already-closing Attempt Scope, so it uses the resource
|
|
983
|
+
// facade captured above while retaining the same physical Sandbox and provider release owner.
|
|
984
|
+
if (agent.kind === "sandbox") {
|
|
985
|
+
yield* effect_1.Effect.addFinalizer(() => effect_1.Effect.suspend(() => {
|
|
986
|
+
if (layerCleanups.length === 0)
|
|
987
|
+
return effect_1.Effect.void;
|
|
988
|
+
enterPhase("sandbox.cleanup");
|
|
989
|
+
const phaseStartedAt = recorder.offsetNow();
|
|
990
|
+
const before = diagnostics.length;
|
|
991
|
+
return effect_1.Effect.forEach([...layerCleanups].reverse(), (cleanup) => {
|
|
992
|
+
const startedAt = recorder.offsetNow();
|
|
993
|
+
const node = recorder.child((0, timing_ts_1.sandboxPrepareActivity)({
|
|
994
|
+
label: `${cleanup.label} cleanup`,
|
|
995
|
+
startOffsetMs: startedAt,
|
|
996
|
+
}));
|
|
985
997
|
if (node)
|
|
986
|
-
node
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
998
|
+
recorder.pushParent(node);
|
|
999
|
+
return (0, cleanup_timeout_ts_1.cleanupCallback)((cleanupSignal) => cleanup.command(cleanupCommandTarget, {
|
|
1000
|
+
...cleanup.context,
|
|
1001
|
+
signal: cleanupSignal,
|
|
1002
|
+
})).pipe(effect_1.Effect.catch((error) => effect_1.Effect.sync(() => {
|
|
1003
|
+
if (node)
|
|
1004
|
+
node.failed = true;
|
|
1005
|
+
declareFailure("sandbox.cleanup", error);
|
|
1006
|
+
diagnostics.push(teardownDiagnostic("sandbox.cleanup", error));
|
|
1007
|
+
recordSandboxCleanupFailure("sandbox.cleanup", error);
|
|
1008
|
+
})), effect_1.Effect.ensuring(effect_1.Effect.sync(() => {
|
|
1009
|
+
if (node) {
|
|
1010
|
+
node.durationMs = Math.max(0, recorder.offsetNow() - startedAt);
|
|
1011
|
+
recorder.popParent();
|
|
1012
|
+
}
|
|
1013
|
+
})));
|
|
1014
|
+
}, { discard: true }).pipe(effect_1.Effect.ensuring(effect_1.Effect.sync(() => {
|
|
1015
|
+
recorder.record("sandbox.cleanup", Math.max(0, recorder.offsetNow() - phaseStartedAt), diagnostics.length > before);
|
|
995
1016
|
})));
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
|
|
1017
|
+
}));
|
|
1018
|
+
}
|
|
1019
|
+
// Cross-send adapter processes belong to the Attempt Scope, not to the
|
|
1020
|
+
// Promise-shaped body. On timeout the bridge is intentionally sealed
|
|
1021
|
+
// before that Promise's finally continuation can submit more work, so a
|
|
1022
|
+
// scope finalizer must remain the unconditional release owner.
|
|
1023
|
+
yield* effect_1.Effect.addFinalizer(() => (0, cleanup_timeout_ts_1.cleanupCallback)((cleanupSignal) => attemptResources.releaseAll(cleanupSignal)).pipe(effect_1.Effect.catch((error) => effect_1.Effect.sync(() => {
|
|
1024
|
+
reportAttemptResourceReleaseFailure(error);
|
|
1025
|
+
}))));
|
|
1026
|
+
// Register the cleanup join after every outer resource finalizer, then
|
|
1027
|
+
// register the forward close last. Scope LIFO therefore first rejects
|
|
1028
|
+
// outstanding Promise work, then lets the body finish Agent teardown →
|
|
1029
|
+
// managed Attempt release → Eval Plugin teardown, and only afterwards
|
|
1030
|
+
// releases the enclosing Sandbox / Provider owners. The join abandons
|
|
1031
|
+
// only a body that never enters cleanup; each cleanup callback owns its
|
|
1032
|
+
// deadline without sharing an aggregate deadline with later callbacks.
|
|
1033
|
+
yield* effect_1.Effect.addFinalizer(() => assertFirst.awaitCleanup());
|
|
1034
|
+
yield* effect_1.Effect.addFinalizer((exit) => effect_1.Effect.suspend(() => {
|
|
1035
|
+
const interrupted = timedOut || (effect_1.Exit.isFailure(exit) && effect_1.Cause.hasInterruptsOnly(exit.cause));
|
|
1036
|
+
return assertFirst.closeEffectRequests(new api_ts_1.AssertionAuthoringClosedError(interrupted ? "attempt-interrupted" : "attempt-sealed"));
|
|
999
1037
|
}));
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
},
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1038
|
+
// The legacy setup/diff body remains Promise-shaped, while OTLP waits,
|
|
1039
|
+
// author execution, and sealing run in this Attempt's Effect scope. No
|
|
1040
|
+
// nested runtime is introduced.
|
|
1041
|
+
const bodyFiber = yield* effect_1.Effect.forkChild(tryPromiseAsDefect((interruptSignal) => runAttemptBody(a, config, t0, base, {
|
|
1042
|
+
judgeUsage,
|
|
1043
|
+
sandbox,
|
|
1044
|
+
sourceRegistry,
|
|
1045
|
+
sourceCapture,
|
|
1046
|
+
receiver,
|
|
1047
|
+
telemetry,
|
|
1048
|
+
otel: otelChannel,
|
|
1049
|
+
signal: AbortSignal.any([signal, interruptSignal]),
|
|
1050
|
+
log,
|
|
1051
|
+
enterPhase,
|
|
1052
|
+
// send 在飞时归因到嵌套的 agent.run(不切换顶层阶段,见 LifecyclePhase 注释)。
|
|
1053
|
+
getPhase: () => (sendActive ? "agent.run" : lastPhase),
|
|
1054
|
+
setSendActive: (active) => {
|
|
1055
|
+
sendActive = active;
|
|
1056
|
+
},
|
|
1057
|
+
recorder,
|
|
1058
|
+
...(attemptTimeout ? { attemptTimeout } : {}),
|
|
1059
|
+
...(deadlineAt !== undefined ? { deadlineAt } : {}),
|
|
1060
|
+
feedback: scopedFeedback,
|
|
1061
|
+
diagnostics,
|
|
1062
|
+
commands,
|
|
1063
|
+
sensitiveValues,
|
|
1064
|
+
observabilityRuntime,
|
|
1065
|
+
...(materializedCase === undefined ? {} : { materializedCase }),
|
|
1066
|
+
...(preparedSetupPrefix === undefined ? {} : { preparedSetupPrefix }),
|
|
1067
|
+
concurrencySlot,
|
|
1068
|
+
declareFailure,
|
|
1069
|
+
onEnvironmentIncomplete,
|
|
1070
|
+
isDeadlineTimedOut: () => timedOut,
|
|
1071
|
+
layerCleanups,
|
|
1072
|
+
attemptResources,
|
|
1073
|
+
reportAttemptResourceReleaseFailure,
|
|
1074
|
+
registerEvidence: (getEvents, getUsage, getRetryAttempts) => {
|
|
1075
|
+
liveEvents = getEvents;
|
|
1076
|
+
liveUsage = getUsage;
|
|
1077
|
+
liveRetryAttempts = getRetryAttempts;
|
|
1078
|
+
},
|
|
1079
|
+
registerLedger: (ledger) => {
|
|
1080
|
+
liveLedger = ledger;
|
|
1081
|
+
},
|
|
1082
|
+
retainFileChangesCapture: (capture) => {
|
|
1083
|
+
fileChangesCapture = capture;
|
|
1084
|
+
},
|
|
1085
|
+
assertFirst,
|
|
1086
|
+
registerAssertions: (runtime, state) => {
|
|
1087
|
+
if (liveAssertions !== undefined && liveAssertions !== runtime) {
|
|
1088
|
+
throw new Error("Attempt tried to register a second Assertions runtime");
|
|
1089
|
+
}
|
|
1090
|
+
if (liveAssertionState !== undefined && liveAssertionState !== state) {
|
|
1091
|
+
throw new Error("Attempt tried to register a second Assertions state");
|
|
1092
|
+
}
|
|
1093
|
+
liveAssertions = runtime;
|
|
1094
|
+
liveAssertionState = state;
|
|
1095
|
+
},
|
|
1096
|
+
...(opts.artifactPrepare !== undefined ? { prepareCoordinator: opts.artifactPrepare } : {}),
|
|
1097
|
+
}, parentSignal)));
|
|
1098
|
+
const contextExit = yield* effect_1.Effect.exit(assertFirst.awaitContext());
|
|
1099
|
+
if (effect_1.Exit.isFailure(contextExit)) {
|
|
1100
|
+
if (effect_1.Cause.hasInterruptsOnly(contextExit.cause))
|
|
1101
|
+
return yield* effect_1.Effect.interrupt;
|
|
1102
|
+
// Context construction failed before the Agent body could register its
|
|
1103
|
+
// runtime. Seal the single empty execution-error result now so the
|
|
1104
|
+
// execution deadline retires before the Promise body's existing
|
|
1105
|
+
// Agent/Plugin cleanup chain is joined.
|
|
1106
|
+
const sealed = yield* sealExecutionError();
|
|
1107
|
+
const bodyResult = yield* effect_1.Fiber.join(bodyFiber);
|
|
1108
|
+
return withSealedAssertions(bodyResult, sealed);
|
|
1109
|
+
}
|
|
1110
|
+
const author = effect_1.Effect.suspend(() => {
|
|
1111
|
+
// This is the original frozen author Context; the assertion only
|
|
1112
|
+
// resolves Eval-kind overloads and never substitutes a wrapper.
|
|
1113
|
+
// Both Promise and deferred Effect calls capture through explicit
|
|
1114
|
+
// Attempt-owned dependencies, never fiber-local ALS.
|
|
1115
|
+
const returned = evalDef.test(contextExit.value);
|
|
1116
|
+
if (effect_1.Effect.isEffect(returned)) {
|
|
1117
|
+
return returned;
|
|
1073
1118
|
}
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
}, parentSignal)));
|
|
1079
|
-
const contextExit = yield* effect_1.Effect.exit(assertFirst.awaitContext());
|
|
1080
|
-
if (effect_1.Exit.isFailure(contextExit)) {
|
|
1081
|
-
if (effect_1.Cause.hasInterruptsOnly(contextExit.cause))
|
|
1082
|
-
return yield* effect_1.Effect.interrupt;
|
|
1083
|
-
// Context construction failed before the Agent body could register its
|
|
1084
|
-
// runtime. Seal the single empty execution-error result now so the
|
|
1085
|
-
// execution deadline retires before the Promise body's existing
|
|
1086
|
-
// Agent/Plugin cleanup chain is joined.
|
|
1087
|
-
const sealed = yield* sealExecutionError();
|
|
1088
|
-
const bodyResult = yield* effect_1.Fiber.join(bodyFiber);
|
|
1089
|
-
return withSealedAssertions(bodyResult, sealed);
|
|
1090
|
-
}
|
|
1091
|
-
const author = effect_1.Effect.suspend(() => {
|
|
1092
|
-
// This is the original frozen author Context; the assertion only
|
|
1093
|
-
// resolves Eval-kind overloads and never substitutes a wrapper.
|
|
1094
|
-
// Both Promise and deferred Effect calls capture through explicit
|
|
1095
|
-
// Attempt-owned dependencies, never fiber-local ALS.
|
|
1096
|
-
const returned = evalDef.test(contextExit.value);
|
|
1097
|
-
if (effect_1.Effect.isEffect(returned)) {
|
|
1098
|
-
return returned;
|
|
1099
|
-
}
|
|
1100
|
-
return effect_1.Effect.tryPromise({
|
|
1101
|
-
try: () => Promise.resolve(returned),
|
|
1102
|
-
catch: (error) => error,
|
|
1119
|
+
return effect_1.Effect.tryPromise({
|
|
1120
|
+
try: () => Promise.resolve(returned),
|
|
1121
|
+
catch: (error) => error,
|
|
1122
|
+
});
|
|
1103
1123
|
});
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
: { _tag: "
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
return yield* effect_1.
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
const registered = yield* effect_1.Effect.result((0, keep_registry_ts_1.writeKeptEntryEffect)(coordinationRoot, {
|
|
1164
|
-
sandboxId: sandbox.sandboxId,
|
|
1165
|
-
provider: providerName,
|
|
1166
|
-
evalId: evalDef.id,
|
|
1167
|
-
attempt,
|
|
1168
|
-
...(run.experimentId !== undefined ? { experimentId: run.experimentId } : {}),
|
|
1169
|
-
locator: String(a.locator),
|
|
1170
|
-
verdict: bodyResult.verdict,
|
|
1171
|
-
keptAt,
|
|
1172
|
-
workdir: sandbox.workdir,
|
|
1173
|
-
...(enter !== undefined ? { enter } : {}),
|
|
1174
|
-
...(expiresAt !== undefined ? { expiresAt } : {}),
|
|
1175
|
-
state: "alive",
|
|
1176
|
-
}));
|
|
1177
|
-
if (effect_1.Result.isSuccess(registered)) {
|
|
1178
|
-
disposition = "keep";
|
|
1179
|
-
(0, sink_ts_1.reportKept)({
|
|
1180
|
-
locator: a.locator,
|
|
1181
|
-
identity,
|
|
1182
|
-
who: (0, types_ts_1.runWho)({ agentName: agent.name, model: run.model, experimentId: run.experimentId }),
|
|
1183
|
-
verdict: bodyResult.verdict,
|
|
1184
|
-
provider: providerName,
|
|
1124
|
+
const authorExit = yield* effect_1.Effect.exit(author);
|
|
1125
|
+
if (effect_1.Exit.isSuccess(authorExit)) {
|
|
1126
|
+
yield* assertFirst.completeAuthor({ _tag: "succeeded" });
|
|
1127
|
+
}
|
|
1128
|
+
else {
|
|
1129
|
+
if (effect_1.Cause.hasInterruptsOnly(authorExit.cause))
|
|
1130
|
+
return yield* effect_1.Effect.interrupt;
|
|
1131
|
+
const failure = effect_1.Cause.findErrorOption(authorExit.cause);
|
|
1132
|
+
const defect = effect_1.Cause.findDefect(authorExit.cause);
|
|
1133
|
+
const completion = effect_1.Option.isSome(failure)
|
|
1134
|
+
? isAssertionStopError(failure.value)
|
|
1135
|
+
? { _tag: "stopped" }
|
|
1136
|
+
: { _tag: "failed", error: failure.value }
|
|
1137
|
+
: { _tag: "defect", cause: effect_1.Result.isSuccess(defect) ? defect.success : authorExit.cause };
|
|
1138
|
+
yield* assertFirst.completeAuthor(completion);
|
|
1139
|
+
}
|
|
1140
|
+
// The declared author callback has returned. A detached continuation may
|
|
1141
|
+
// still hold a handle, but it cannot enqueue a new control barrier while
|
|
1142
|
+
// this Attempt moves to its single ordered seal.
|
|
1143
|
+
yield* assertFirst.closeAssertionRequests(new api_ts_1.AssertionAuthoringClosedError("attempt-sealing"));
|
|
1144
|
+
const sealRequestExit = yield* effect_1.Effect.exit(assertFirst.awaitSealRequest());
|
|
1145
|
+
if (effect_1.Exit.isFailure(sealRequestExit)) {
|
|
1146
|
+
if (effect_1.Cause.hasInterruptsOnly(sealRequestExit.cause))
|
|
1147
|
+
return yield* effect_1.Effect.interrupt;
|
|
1148
|
+
return yield* effect_1.Fiber.join(bodyFiber);
|
|
1149
|
+
}
|
|
1150
|
+
const sealExit = yield* effect_1.Effect.exit((0, assertions_ts_1.sealAttemptAssertions)(sealRequestExit.value.runtime, sealRequestExit.value.options, sealRequestExit.value.workspaceDiff).pipe(effect_1.Effect.tap(() => effect_1.Effect.sync(() => {
|
|
1151
|
+
markExecutionTerminal();
|
|
1152
|
+
})), effect_1.Effect.tap((sealed) => onSealedEvaluation?.(sealed) ?? effect_1.Effect.void)));
|
|
1153
|
+
liveAssertionState?.manager.releaseObservedEvaluationSegments();
|
|
1154
|
+
if (effect_1.Exit.isSuccess(sealExit)) {
|
|
1155
|
+
yield* assertFirst.completeSeal(sealExit.value);
|
|
1156
|
+
}
|
|
1157
|
+
else {
|
|
1158
|
+
if (effect_1.Cause.hasInterruptsOnly(sealExit.cause))
|
|
1159
|
+
return yield* effect_1.Effect.interrupt;
|
|
1160
|
+
const failure = effect_1.Cause.findErrorOption(sealExit.cause);
|
|
1161
|
+
const defect = effect_1.Cause.findDefect(sealExit.cause);
|
|
1162
|
+
yield* assertFirst.failSeal(effect_1.Option.isSome(failure)
|
|
1163
|
+
? failure.value
|
|
1164
|
+
: effect_1.Result.isSuccess(defect)
|
|
1165
|
+
? defect.success
|
|
1166
|
+
: effect_1.Cause.squash(sealExit.cause));
|
|
1167
|
+
}
|
|
1168
|
+
const bodyResult = yield* effect_1.Fiber.join(bodyFiber);
|
|
1169
|
+
// 留存提交:verdict 定稿、其余收尾(teardown 链、diff 采集)已在 body 内完成后,按档位
|
|
1170
|
+
// 提交——failed 档留 failed/errored,all 档全部;顺序不可调换:先原子写登记项,写入成功
|
|
1171
|
+
// 才把 disposition 改成 keep;写入失败保持 stop、记 diagnostic,`sandbox.kept` 不得为 true。
|
|
1172
|
+
const keepMode = opts.keepSandbox;
|
|
1173
|
+
if (agent.kind === "sandbox" &&
|
|
1174
|
+
keepMode !== undefined &&
|
|
1175
|
+
a.locator !== undefined &&
|
|
1176
|
+
(keepMode === "all" || bodyResult.verdict === "failed" || bodyResult.verdict === "errored")) {
|
|
1177
|
+
const providerName = runtimeCapabilities.provider;
|
|
1178
|
+
if (runtimeCapabilities.retention._tag === "Suspendable") {
|
|
1179
|
+
const enter = (0, keep_ts_1.nativeEnterCommand)(providerName, sandbox.sandboxId);
|
|
1180
|
+
const keptAt = new Date().toISOString();
|
|
1181
|
+
const expiresAt = (0, keep_ts_1.computeExpiresAt)(providerName, keptAt);
|
|
1182
|
+
const registered = yield* effect_1.Effect.result((0, keep_registry_ts_1.writeKeptEntryEffect)(coordinationRoot, {
|
|
1185
1183
|
sandboxId: sandbox.sandboxId,
|
|
1184
|
+
provider: providerName,
|
|
1185
|
+
evalId: evalDef.id,
|
|
1186
|
+
attempt,
|
|
1187
|
+
...(run.experimentId !== undefined ? { experimentId: run.experimentId } : {}),
|
|
1188
|
+
locator: String(a.locator),
|
|
1189
|
+
verdict: bodyResult.verdict,
|
|
1190
|
+
keptAt,
|
|
1191
|
+
workdir: sandbox.workdir,
|
|
1186
1192
|
...(enter !== undefined ? { enter } : {}),
|
|
1193
|
+
...(expiresAt !== undefined ? { expiresAt } : {}),
|
|
1194
|
+
state: "alive",
|
|
1195
|
+
}));
|
|
1196
|
+
if (effect_1.Result.isSuccess(registered)) {
|
|
1197
|
+
disposition = "keep";
|
|
1198
|
+
(0, sink_ts_1.reportKept)({
|
|
1199
|
+
locator: a.locator,
|
|
1200
|
+
identity,
|
|
1201
|
+
who: (0, types_ts_1.runWho)({ agentName: agent.name, model: run.model, experimentId: run.experimentId }),
|
|
1202
|
+
verdict: bodyResult.verdict,
|
|
1203
|
+
provider: providerName,
|
|
1204
|
+
sandboxId: sandbox.sandboxId,
|
|
1205
|
+
...(enter !== undefined ? { enter } : {}),
|
|
1206
|
+
});
|
|
1207
|
+
// 只有 `kept` 在收尾时点决定;provider / sandboxId 等归属键仍由租借时刻的
|
|
1208
|
+
// sandboxFacts 统一挂上(见下方 Effect.map)。
|
|
1209
|
+
kept = true;
|
|
1210
|
+
return bodyResult;
|
|
1211
|
+
}
|
|
1212
|
+
const error = registered.failure;
|
|
1213
|
+
recordDiagnostic({
|
|
1214
|
+
code: "sandbox-keep-failed",
|
|
1215
|
+
level: "warning",
|
|
1216
|
+
message: `failed to register kept sandbox ${sandbox.sandboxId}; it will be destroyed normally: ${error instanceof Error ? error.message : String(error)}`,
|
|
1217
|
+
dedupeKey: `sandbox-keep-failed:${sandbox.sandboxId}`,
|
|
1187
1218
|
});
|
|
1188
|
-
// 只有 `kept` 在收尾时点决定;provider / sandboxId 等归属键仍由租借时刻的
|
|
1189
|
-
// sandboxFacts 统一挂上(见下方 Effect.map)。
|
|
1190
|
-
kept = true;
|
|
1191
|
-
return bodyResult;
|
|
1192
1219
|
}
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1220
|
+
}
|
|
1221
|
+
return bodyResult;
|
|
1222
|
+
}).pipe(applyAttemptDeadline,
|
|
1223
|
+
// Observe only interruption of the complete Attempt execution, never a
|
|
1224
|
+
// losing internal race fiber. The original Cause continues unchanged.
|
|
1225
|
+
effect_1.Effect.onInterrupt(() => effect_1.Effect.sync(() => {
|
|
1226
|
+
if (!assertionsSealed)
|
|
1227
|
+
forwardParentAbort();
|
|
1228
|
+
}))))).pipe(
|
|
1229
|
+
// The execution race ends before Scope finalizers start. Cleanup retains
|
|
1230
|
+
// its own budgets; custom capture failure participates in the final fold.
|
|
1231
|
+
// body 自己已兜了 agent 执行错;这里兜的是资源获取 / Sample 层的意外(起沙箱失败等)。
|
|
1232
|
+
// 中断【不】吞:此时 Sample 已跑完 release(容器已停),把中断继续上抛,让 forEach 整体停掉,
|
|
1233
|
+
// 否则会把中断「恢复」成一条 errored 结果、并让后续 attempt 继续起 —— 那就停不下来了。
|
|
1234
|
+
// `timeoutTo` 已在内层把自己的中断消费成 onTimeout 的成功值。这里不能仅凭 Cause 中含
|
|
1235
|
+
// interrupt 就升级成整批取消:earlyExit 也会中断 losing attempt 的 fiber。整个 Invocation
|
|
1236
|
+
// 是否取消只由 invocationSignal 裁决;其它中断在本 attempt 内封成 errored,随后由调度器
|
|
1237
|
+
// 的 earlyExit 去重分支丢弃。兄弟 fiber 的真实 defect 仍由它自己的 Cause 向外传播。
|
|
1238
|
+
effect_1.Effect.catchCause((cause) => isAttemptAborted(invocationSignal) && adapter.kind !== "custom"
|
|
1239
|
+
? effect_1.Effect.interrupt
|
|
1240
|
+
: effect_1.Effect.suspend(() => {
|
|
1241
|
+
invocationInterrupted = isAttemptAborted(invocationSignal) && effect_1.Cause.hasInterruptsOnly(cause);
|
|
1242
|
+
// 资源获取 / Sample 层的意外(起沙箱失败、provisioning 的确定性配置死因)同样是终局
|
|
1243
|
+
// 失败:先读空间轴回执,再折成纯数据 AttemptError(顺序不可换,见 declareFailure)。
|
|
1244
|
+
const raw = effect_1.Cause.squash(cause);
|
|
1245
|
+
const phase = (sendActive ? "agent.run" : lastPhase) ?? "eval.run";
|
|
1246
|
+
declareFailure(phase, raw);
|
|
1247
|
+
const error = timedOut && deadlineError !== undefined
|
|
1248
|
+
? deadlineError
|
|
1249
|
+
: errorFromThrown(raw, sendActive ? "agent.run" : lastPhase, attemptTimeout);
|
|
1250
|
+
const fallback = {
|
|
1251
|
+
...base,
|
|
1252
|
+
durationMs: recorder.offsetNow(),
|
|
1253
|
+
error,
|
|
1254
|
+
...(evalDef.evaluationKind === "score" ? { scoreResult: scoreFactOutcomeForAttemptError(error) } : {}),
|
|
1255
|
+
...(commands.length > 0 ? { commands: [...commands] } : {}),
|
|
1256
|
+
};
|
|
1257
|
+
return effect_1.Effect.succeed(fallback);
|
|
1258
|
+
})),
|
|
1259
|
+
// A prepare/setup failure can be caught by runAttemptBody before a real
|
|
1260
|
+
// TestContext exists. In that case the scoped body returns an errored
|
|
1261
|
+
// value successfully, so catchAllCause above never runs. Enforce the
|
|
1262
|
+
// Record boundary as an output postcondition: every executed errored
|
|
1263
|
+
// Attempt owns one sealed (possibly empty) assertion result.
|
|
1264
|
+
effect_1.Effect.flatMap((result) => assertionsSealed || result.verdict !== "errored"
|
|
1265
|
+
? effect_1.Effect.succeed(result)
|
|
1266
|
+
: sealExecutionError().pipe(effect_1.Effect.map((sealed) => withSealedAssertions(result, sealed)), effect_1.Effect.catch(() => effect_1.Effect.succeed(result)))), effect_1.Effect.map((result) => !timedOut || timeoutSealedAssertions === undefined
|
|
1267
|
+
? result
|
|
1268
|
+
: withSealedAssertions(result, timeoutSealedAssertions)), effect_1.Effect.flatMap((result) => effect_1.Effect.gen(function* () {
|
|
1269
|
+
if (adapter.kind !== "custom")
|
|
1270
|
+
return result;
|
|
1271
|
+
const captureFailure = adapterUsage?.failure ?? adapterAttachments?.failure() ?? adapterExecutionTraces?.failure();
|
|
1272
|
+
// Cleanup must not erase the original execution failure or timeout attribution.
|
|
1273
|
+
// Its own timeout remains independently recorded as a teardown diagnostic.
|
|
1274
|
+
const error = result.error ?? adapterCleanupError ?? (captureFailure === undefined
|
|
1275
|
+
? undefined
|
|
1276
|
+
: errorFromThrown(captureFailure, "attempt.teardown"));
|
|
1277
|
+
let sealed = adapterSealedAssertions;
|
|
1278
|
+
if (sealed === undefined)
|
|
1279
|
+
return result;
|
|
1280
|
+
if (error !== undefined || invocationInterrupted) {
|
|
1281
|
+
// Assertions were evaluated once before cleanup. Only the execution
|
|
1282
|
+
// outcome changes; the existing domain folds retain every earned point.
|
|
1283
|
+
const evaluation = Object.freeze({ ...sealed.evaluation, execution: "errored" });
|
|
1284
|
+
const score = evalDef.evaluationKind === "score" ? (0, score_ts_1.buildScorePayload)(evaluation) : undefined;
|
|
1285
|
+
if (score !== undefined && effect_1.Result.isFailure(score))
|
|
1286
|
+
return yield* effect_1.Effect.fail(score.failure);
|
|
1287
|
+
sealed = Object.freeze({
|
|
1288
|
+
...sealed,
|
|
1289
|
+
evaluation,
|
|
1290
|
+
verdict: Object.freeze({ state: (0, verdict_ts_1.foldVerdict)(evaluation) }),
|
|
1291
|
+
...(score === undefined ? {} : { score: score.success }),
|
|
1199
1292
|
});
|
|
1200
1293
|
}
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
effect_1.Effect.catchCause((cause) => isAttemptAborted(invocationSignal) && adapter.kind !== "custom"
|
|
1214
|
-
? effect_1.Effect.interrupt
|
|
1215
|
-
: effect_1.Effect.suspend(() => {
|
|
1216
|
-
invocationInterrupted = isAttemptAborted(invocationSignal) && effect_1.Cause.hasInterruptsOnly(cause);
|
|
1217
|
-
// 资源获取 / Sample 层的意外(起沙箱失败、provisioning 的确定性配置死因)同样是终局
|
|
1218
|
-
// 失败:先读空间轴回执,再折成纯数据 AttemptError(顺序不可换,见 declareFailure)。
|
|
1219
|
-
const raw = effect_1.Cause.squash(cause);
|
|
1220
|
-
const phase = (sendActive ? "agent.run" : lastPhase) ?? "eval.run";
|
|
1221
|
-
declareFailure(phase, raw);
|
|
1222
|
-
const error = timedOut && deadlineError !== undefined
|
|
1223
|
-
? deadlineError
|
|
1224
|
-
: errorFromThrown(raw, sendActive ? "agent.run" : lastPhase, attemptTimeout);
|
|
1225
|
-
const fallback = {
|
|
1226
|
-
...base,
|
|
1227
|
-
durationMs: recorder.offsetNow(),
|
|
1228
|
-
error,
|
|
1229
|
-
...(evalDef.evaluationKind === "score" ? { scoreResult: scoreFactOutcomeForAttemptError(error) } : {}),
|
|
1294
|
+
yield* onSealedEvaluation?.(sealed) ?? effect_1.Effect.void;
|
|
1295
|
+
return withSealedAssertions({ ...result, ...(error === undefined ? {} : { error }) }, sealed);
|
|
1296
|
+
})),
|
|
1297
|
+
// 结果封口在 Sample release 完成之后:sandbox.stop 已由 finalizer 写进 recorder,
|
|
1298
|
+
// 这里把完整的阶段计时挂到即将交还的结果上(timeout / scope 兜底分支同样带上)。超时路径
|
|
1299
|
+
// 额外把上面那个 finalizer 折叠出的 workspace.diff / sources 并进来——它俩是异步产出,
|
|
1300
|
+
// 必须等 Sample release(本 map 之前的所有 finalizer)跑完才有值,不能在 onTimeout 的同步
|
|
1301
|
+
// 回调里就地给,原理与 phases 完全一致(见该 finalizer 与 onTimeout 的注释)。
|
|
1302
|
+
effect_1.Effect.map((r) => {
|
|
1303
|
+
const withEvidence = {
|
|
1304
|
+
...r,
|
|
1305
|
+
...(diagnostics.length > 0 ? { diagnostics: [...diagnostics] } : {}),
|
|
1230
1306
|
...(commands.length > 0 ? { commands: [...commands] } : {}),
|
|
1231
1307
|
};
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
...(commands.length > 0 ? { commands: [...commands] } : {}),
|
|
1282
|
-
};
|
|
1283
|
-
const phases = recorder.finalize();
|
|
1284
|
-
// 现有阶段计时尚未把 sandbox.create 起点单独持久化;在它可用前落同口径的保守值,
|
|
1285
|
-
// 读取旧记录也按 durationMs 回退,绝不因缺值错误携带。
|
|
1286
|
-
const withPhases = {
|
|
1287
|
-
...(phases ? { ...withEvidence, phases } : withEvidence),
|
|
1288
|
-
executionMs: withEvidence.executionMs ?? withEvidence.durationMs,
|
|
1289
|
-
};
|
|
1290
|
-
// 调度事实统一在这里挂:每条出口(正常、body 抛错、超时、Sample 层失败)都经过本 map,
|
|
1291
|
-
// 归属因此不随「走到了哪一步」丢失(见 sandboxFacts 的声明)。
|
|
1292
|
-
const withSandbox = sandboxFacts
|
|
1293
|
-
? {
|
|
1294
|
-
...withPhases,
|
|
1295
|
-
sandbox: {
|
|
1296
|
-
...sandboxFacts,
|
|
1297
|
-
// SetupPrefix restore/capture rebases the stable backend onto a new private clone.
|
|
1298
|
-
// Read the final dynamic facade here instead of retaining the acquisition id.
|
|
1299
|
-
sandboxId: currentSandbox?.sandboxId ?? sandboxFacts.sandboxId,
|
|
1300
|
-
...(kept ? { kept: true } : {}),
|
|
1301
|
-
},
|
|
1302
|
-
}
|
|
1303
|
-
: withPhases;
|
|
1304
|
-
const completed = timedOut
|
|
1305
|
-
? {
|
|
1306
|
-
...withSandbox,
|
|
1307
|
-
...(timeoutDiff !== undefined ? { diff: timeoutDiff } : {}),
|
|
1308
|
-
sources: timeoutSources ?? [],
|
|
1309
|
-
}
|
|
1310
|
-
: withSandbox;
|
|
1311
|
-
const finalResult = (0, source_producer_ts_1.retainRunnerAttemptSourceCapture)(redactEvalResultEvidence(completed, sensitiveValues), sourceCapture.snapshot());
|
|
1312
|
-
const capture = timedOut
|
|
1313
|
-
? timeoutFileChangesCapture ?? fileChangesCapture
|
|
1314
|
-
: fileChangesCapture;
|
|
1315
|
-
if (capture !== undefined) {
|
|
1316
|
-
(0, sandbox_record_producer_ts_1.retainRunnerAttemptFileChangesCapture)(finalResult, capture);
|
|
1317
|
-
}
|
|
1318
|
-
if (adapterUsageSnapshot !== undefined)
|
|
1319
|
-
(0, adapter_usage_ts_1.retainAdapterUsage)(finalResult, adapterUsageSnapshot);
|
|
1320
|
-
if (adapterAttachments !== undefined)
|
|
1321
|
-
(0, adapter_attachments_ts_1.retainAdapterAttachments)(finalResult, adapterAttachments.snapshot());
|
|
1322
|
-
if (adapterExecutionTraces !== undefined)
|
|
1323
|
-
(0, adapter_execution_trace_ts_1.retainAdapterExecutionTraces)(finalResult, adapterExecutionTraces.snapshot());
|
|
1324
|
-
return finalResult;
|
|
1325
|
-
}), effect_1.Effect.tap((finalResult) => (0, runtime_ts_3.bindRunnerAttemptObservabilityCapture)(finalResult, observabilityRuntime)), effect_1.Effect.flatMap((finalResult) => invocationInterrupted
|
|
1326
|
-
? (onInterruptedResult?.(finalResult) ?? effect_1.Effect.void).pipe(effect_1.Effect.andThen(effect_1.Effect.interrupt))
|
|
1327
|
-
: effect_1.Effect.succeed(finalResult)), effect_1.Effect.ensuring(effect_1.Effect.sync(() => {
|
|
1328
|
-
parentSignal?.removeEventListener("abort", forwardParentAbort);
|
|
1329
|
-
}))));
|
|
1308
|
+
const phases = recorder.finalize();
|
|
1309
|
+
// 现有阶段计时尚未把 sandbox.create 起点单独持久化;在它可用前落同口径的保守值,
|
|
1310
|
+
// 读取旧记录也按 durationMs 回退,绝不因缺值错误携带。
|
|
1311
|
+
const withPhases = {
|
|
1312
|
+
...(phases ? { ...withEvidence, phases } : withEvidence),
|
|
1313
|
+
executionMs: withEvidence.executionMs ?? withEvidence.durationMs,
|
|
1314
|
+
};
|
|
1315
|
+
// 调度事实统一在这里挂:每条出口(正常、body 抛错、超时、Sample 层失败)都经过本 map,
|
|
1316
|
+
// 归属因此不随「走到了哪一步」丢失(见 sandboxFacts 的声明)。
|
|
1317
|
+
const withSandbox = sandboxFacts
|
|
1318
|
+
? {
|
|
1319
|
+
...withPhases,
|
|
1320
|
+
sandbox: {
|
|
1321
|
+
...sandboxFacts,
|
|
1322
|
+
// SetupPrefix restore/capture rebases the stable backend onto a new private clone.
|
|
1323
|
+
// Read the final dynamic facade here instead of retaining the acquisition id.
|
|
1324
|
+
sandboxId: currentSandbox?.sandboxId ?? sandboxFacts.sandboxId,
|
|
1325
|
+
...(kept ? { kept: true } : {}),
|
|
1326
|
+
},
|
|
1327
|
+
}
|
|
1328
|
+
: withPhases;
|
|
1329
|
+
const completed = timedOut
|
|
1330
|
+
? {
|
|
1331
|
+
...withSandbox,
|
|
1332
|
+
...(timeoutDiff !== undefined ? { diff: timeoutDiff } : {}),
|
|
1333
|
+
sources: timeoutSources ?? [],
|
|
1334
|
+
}
|
|
1335
|
+
: withSandbox;
|
|
1336
|
+
const finalResult = (0, source_producer_ts_1.retainRunnerAttemptSourceCapture)(redactEvalResultEvidence(completed, sensitiveValues), sourceCapture.snapshot());
|
|
1337
|
+
const capture = timedOut
|
|
1338
|
+
? timeoutFileChangesCapture ?? fileChangesCapture
|
|
1339
|
+
: fileChangesCapture;
|
|
1340
|
+
if (capture !== undefined) {
|
|
1341
|
+
(0, sandbox_record_producer_ts_1.retainRunnerAttemptFileChangesCapture)(finalResult, capture);
|
|
1342
|
+
}
|
|
1343
|
+
(0, judge_usage_ts_2.retainJudgeUsage)(finalResult, judgeUsage.snapshot());
|
|
1344
|
+
if (adapterUsageSnapshot !== undefined)
|
|
1345
|
+
(0, adapter_usage_ts_1.retainAdapterUsage)(finalResult, adapterUsageSnapshot);
|
|
1346
|
+
if (adapterAttachments !== undefined)
|
|
1347
|
+
(0, adapter_attachments_ts_1.retainAdapterAttachments)(finalResult, adapterAttachments.snapshot());
|
|
1348
|
+
if (adapterExecutionTraces !== undefined)
|
|
1349
|
+
(0, adapter_execution_trace_ts_1.retainAdapterExecutionTraces)(finalResult, adapterExecutionTraces.snapshot());
|
|
1350
|
+
return finalResult;
|
|
1351
|
+
}), effect_1.Effect.tap((finalResult) => (0, runtime_ts_3.bindRunnerAttemptObservabilityCapture)(finalResult, observabilityRuntime)), effect_1.Effect.flatMap((finalResult) => invocationInterrupted
|
|
1352
|
+
? (onInterruptedResult?.(finalResult) ?? effect_1.Effect.void).pipe(effect_1.Effect.andThen(effect_1.Effect.interrupt))
|
|
1353
|
+
: effect_1.Effect.succeed(finalResult)), effect_1.Effect.ensuring(effect_1.Effect.sync(() => {
|
|
1354
|
+
parentSignal?.removeEventListener("abort", forwardParentAbort);
|
|
1355
|
+
}))));
|
|
1356
|
+
}));
|
|
1330
1357
|
}
|
|
1331
1358
|
function adapterAuthorCallbackEffect(resources, signal, callback) {
|
|
1332
1359
|
const author = effect_1.Effect.tryPromise({
|
|
@@ -1349,6 +1376,9 @@ function runAdapterAttemptBody(input) {
|
|
|
1349
1376
|
const { a, base, adapter, resources, usage, attachments, executionTraces, signal, sourceCapture, sourceRegistry, assertFirst, enterPhase, recorder, feedback, log, declareFailure, registerAssertions, closeAuthoring, markAssertionsSealed, } = input;
|
|
1350
1377
|
return effect_1.Effect.gen(function* () {
|
|
1351
1378
|
const { context: core, state } = (0, assert_first_ts_1.createAssertFirstCoreContext)({
|
|
1379
|
+
readUsage: () => usage.snapshot(),
|
|
1380
|
+
judgeUsage: input.judgeUsage,
|
|
1381
|
+
elapsedMs: recorder.offsetNow,
|
|
1352
1382
|
evaluationKind: a.evalDef.evaluationKind ?? "pass",
|
|
1353
1383
|
model: a.run.model,
|
|
1354
1384
|
reasoningEffort: a.run.reasoningEffort,
|
|
@@ -1372,6 +1402,7 @@ function runAdapterAttemptBody(input) {
|
|
|
1372
1402
|
experimentId: a.run.experimentId,
|
|
1373
1403
|
attempt: a.attempt,
|
|
1374
1404
|
signal,
|
|
1405
|
+
models: a.run.models ?? Object.freeze({}),
|
|
1375
1406
|
model: a.run.model,
|
|
1376
1407
|
reasoningEffort: a.run.reasoningEffort,
|
|
1377
1408
|
flags: a.run.flags,
|
|
@@ -1380,6 +1411,7 @@ function runAdapterAttemptBody(input) {
|
|
|
1380
1411
|
log,
|
|
1381
1412
|
onCleanup: (cleanup) => resources.onCleanup(cleanup),
|
|
1382
1413
|
recordUsage: usage.record,
|
|
1414
|
+
sealUsage: usage.seal,
|
|
1383
1415
|
attach: (input) => resources.trackHandoff(attachments.attach(input)),
|
|
1384
1416
|
recordTrace: executionTraces.recordTrace,
|
|
1385
1417
|
});
|
|
@@ -1457,12 +1489,15 @@ function runAdapterAttemptBody(input) {
|
|
|
1457
1489
|
});
|
|
1458
1490
|
}
|
|
1459
1491
|
function cleanupAdapterResources(resources, input) {
|
|
1460
|
-
return effect_1.Effect.suspend(() => {
|
|
1492
|
+
return effect_1.Effect.clockWith((clock) => effect_1.Effect.suspend(() => {
|
|
1493
|
+
const timeoutMs = input.timeoutMs;
|
|
1494
|
+
const deadlineAt = clock.currentTimeMillisUnsafe() + timeoutMs;
|
|
1495
|
+
const cleanupStartedAt = clock.monotonicTimeNanosUnsafe();
|
|
1461
1496
|
resources.beginCleanup();
|
|
1462
1497
|
input.enterPhase("attempt.teardown");
|
|
1463
1498
|
const startedAt = input.recorder.offsetNow();
|
|
1464
1499
|
let failed = false;
|
|
1465
|
-
return (0, cleanup_timeout_ts_1.cleanupCallback)((signal) => resources.cleanup(signal),
|
|
1500
|
+
return (0, cleanup_timeout_ts_1.cleanupCallback)((signal) => resources.cleanup(Object.freeze({ signal, timeoutMs, deadlineAt })), Math.max(0, Math.ceil(timeoutMs - Number(clock.monotonicTimeNanosUnsafe() - cleanupStartedAt) / 1000000))).pipe(effect_1.Effect.tap((result) => effect_1.Effect.sync(() => {
|
|
1466
1501
|
for (const failure of result.failures) {
|
|
1467
1502
|
failed = true;
|
|
1468
1503
|
input.feedback.diagnostic({
|
|
@@ -1475,13 +1510,13 @@ function cleanupAdapterResources(resources, input) {
|
|
|
1475
1510
|
failed = true;
|
|
1476
1511
|
input.onTimeout({
|
|
1477
1512
|
code: "adapter-cleanup-timeout",
|
|
1478
|
-
message: `Adapter cleanup timed out after ${
|
|
1513
|
+
message: `Adapter cleanup timed out after ${timeoutMs}ms`,
|
|
1479
1514
|
origin: (0, timing_ts_1.attemptOrigin)("attempt.teardown"),
|
|
1480
1515
|
});
|
|
1481
1516
|
input.feedback.diagnostic({
|
|
1482
1517
|
code: "adapter-cleanup-timeout",
|
|
1483
1518
|
level: "warning",
|
|
1484
|
-
message: `Adapter cleanup timed out after ${
|
|
1519
|
+
message: `Adapter cleanup timed out after ${timeoutMs}ms`,
|
|
1485
1520
|
});
|
|
1486
1521
|
}
|
|
1487
1522
|
})), effect_1.Effect.catch((failure) => effect_1.Effect.sync(() => {
|
|
@@ -1496,7 +1531,7 @@ function cleanupAdapterResources(resources, input) {
|
|
|
1496
1531
|
resources.close();
|
|
1497
1532
|
input.recorder.record("attempt.teardown", input.recorder.offsetNow() - startedAt, failed || undefined);
|
|
1498
1533
|
})));
|
|
1499
|
-
});
|
|
1534
|
+
}));
|
|
1500
1535
|
}
|
|
1501
1536
|
/**
|
|
1502
1537
|
* `EvalResult` still has historical renderer fields while its replacement
|
|
@@ -2470,6 +2505,7 @@ async function runAttemptBody(a, config, t0, base, res, parentSignal) {
|
|
|
2470
2505
|
} }),
|
|
2471
2506
|
model: run.model,
|
|
2472
2507
|
reasoningEffort: run.reasoningEffort,
|
|
2508
|
+
judgeUsage: res.judgeUsage,
|
|
2473
2509
|
// maxCost 断言的估算价目表;与 observed usage.costUSD 互不兜底。
|
|
2474
2510
|
pricing: config.pricing,
|
|
2475
2511
|
flags: run.flags,
|
|
@@ -2503,6 +2539,7 @@ async function runAttemptBody(a, config, t0, base, res, parentSignal) {
|
|
|
2503
2539
|
}
|
|
2504
2540
|
: undefined,
|
|
2505
2541
|
onSendActive: setSendActive,
|
|
2542
|
+
elapsedMs: recorder.offsetNow,
|
|
2506
2543
|
timingNow: recorder.offsetNow,
|
|
2507
2544
|
// 每次 send 一个 turn 节点:本地单调时钟测得的端到端包络 + 与 diff/source 共用的
|
|
2508
2545
|
// session/turn 身份 token;
|
|
@@ -3263,6 +3300,7 @@ async function collectSources(evalSource, registry) {
|
|
|
3263
3300
|
function experimentRunInfo(run, plan, sandboxPlansByEval, config, judge) {
|
|
3264
3301
|
const runLevelTimeoutMs = run.timeoutMs ?? config?.timeoutMs;
|
|
3265
3302
|
return {
|
|
3303
|
+
...(run.models === undefined ? {} : { models: run.models }),
|
|
3266
3304
|
...(run.description !== undefined ? { description: run.description } : {}),
|
|
3267
3305
|
...(run.reasoningEffort !== undefined ? { reasoningEffort: run.reasoningEffort } : {}),
|
|
3268
3306
|
...(Object.keys(run.flags).length > 0 ? { flags: run.flags } : {}),
|