niceeval 0.9.1 → 0.10.2
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 +31 -33
- package/dist/agents/types.d.ts +20 -17
- package/dist/context/types.d.ts +21 -21
- package/dist/i18n/en.d.ts +10 -2
- package/dist/i18n/zh-CN.d.ts +14 -6
- package/dist/o11y/execution-tree.d.ts +103 -0
- package/dist/o11y/otlp/select.d.ts +22 -0
- package/dist/report/{react → assets}/colors.d.ts +8 -0
- package/dist/report/{react → assets}/colors.js +23 -0
- package/dist/report/built-in/index.d.ts +2 -2
- package/dist/report/built-in/index.js +2 -2
- package/dist/report/built-in/standard.d.ts +8 -1
- package/dist/report/built-in/standard.js +17 -7
- package/dist/report/components/attempt-detail/AttemptAssertions.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptAssertions.js +17 -0
- package/dist/report/components/attempt-detail/AttemptConversation.d.ts +10 -0
- package/dist/report/components/attempt-detail/AttemptConversation.js +74 -0
- package/dist/report/components/attempt-detail/AttemptDiagnostics.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptDiagnostics.js +7 -0
- package/dist/report/components/attempt-detail/AttemptDiff.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptDiff.js +11 -0
- package/dist/report/components/attempt-detail/AttemptError.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptError.js +16 -0
- package/dist/report/components/attempt-detail/AttemptFixPrompt.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptFixPrompt.js +7 -0
- package/dist/report/components/attempt-detail/AttemptSource.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptSource.js +97 -0
- package/dist/report/components/attempt-detail/AttemptSummary.d.ts +8 -0
- package/dist/report/components/attempt-detail/AttemptSummary.js +20 -0
- package/dist/report/components/attempt-detail/AttemptTimeline.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptTimeline.js +28 -0
- package/dist/report/components/attempt-detail/AttemptTrace.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptTrace.js +29 -0
- package/dist/report/components/attempt-detail/AttemptUsage.d.ts +6 -0
- package/dist/report/components/attempt-detail/AttemptUsage.js +20 -0
- package/dist/report/components/attempt-detail/compute.d.ts +24 -0
- package/dist/report/components/attempt-detail/compute.js +297 -0
- package/dist/report/components/attempt-detail/faces.d.ts +14 -0
- package/dist/report/components/attempt-detail/faces.js +239 -0
- package/dist/report/components/attempt-detail/index.d.ts +38 -0
- package/dist/report/components/attempt-detail/index.js +547 -0
- package/dist/report/{react → components}/cell.d.ts +5 -3
- package/dist/report/{react → components}/cell.js +3 -3
- package/dist/report/{react → components/entity-lists}/AttemptList.d.ts +8 -4
- package/dist/report/{react → components/entity-lists}/AttemptList.js +16 -10
- package/dist/report/{react → components/entity-lists}/EvalList.d.ts +3 -3
- package/dist/report/{react → components/entity-lists}/EvalList.js +0 -0
- package/dist/report/{react → components/entity-lists}/ExperimentList.d.ts +4 -5
- package/dist/report/{react → components/entity-lists}/ExperimentList.js +12 -11
- package/dist/report/components/entity-lists/compute.d.ts +23 -0
- package/dist/report/components/entity-lists/compute.js +171 -0
- package/dist/report/components/entity-lists/faces.d.ts +5 -0
- package/dist/report/components/entity-lists/faces.js +175 -0
- package/dist/report/components/entity-lists/index.d.ts +49 -0
- package/dist/report/components/entity-lists/index.js +169 -0
- package/dist/report/{react → components}/fixtures.d.ts +2 -2
- package/dist/report/{react → components}/fixtures.js +1 -1
- package/dist/report/{react → components/metric-views}/DeltaTable.d.ts +2 -2
- package/dist/report/{react → components/metric-views}/DeltaTable.js +4 -4
- package/dist/report/{react → components/metric-views}/MetricBars.d.ts +3 -3
- package/dist/report/{react → components/metric-views}/MetricBars.js +3 -3
- package/dist/report/{react → components/metric-views}/MetricLine.d.ts +2 -2
- package/dist/report/{react → components/metric-views}/MetricLine.js +8 -5
- package/dist/report/{react → components/metric-views}/MetricMatrix.d.ts +3 -3
- package/dist/report/{react → components/metric-views}/MetricMatrix.js +4 -4
- package/dist/report/components/metric-views/MetricScatter.d.ts +11 -0
- package/dist/report/{react → components/metric-views}/MetricScatter.js +27 -60
- package/dist/report/{react → components/metric-views}/MetricTable.d.ts +3 -3
- package/dist/report/{react → components/metric-views}/MetricTable.js +4 -4
- package/dist/report/{react → components/metric-views}/Scoreboard.d.ts +2 -2
- package/dist/report/{react → components/metric-views}/Scoreboard.js +3 -3
- package/dist/report/components/metric-views/compute.d.ts +89 -0
- package/dist/report/{compute.js → components/metric-views/compute.js} +32 -415
- package/dist/report/components/metric-views/faces.d.ts +13 -0
- package/dist/report/components/metric-views/faces.js +381 -0
- package/dist/report/components/metric-views/index.d.ts +50 -0
- package/dist/report/components/metric-views/index.js +273 -0
- package/dist/report/{text → components/metric-views}/plot.js +1 -1
- package/dist/report/components/shared-compute.d.ts +22 -0
- package/dist/report/components/shared-compute.js +47 -0
- package/dist/report/components/shared-faces.d.ts +9 -0
- package/dist/report/components/shared-faces.js +26 -0
- package/dist/report/components/shared.d.ts +68 -0
- package/dist/report/components/shared.js +125 -0
- package/dist/report/{react → components/site-components}/CopyFixPrompt.d.ts +2 -2
- package/dist/report/{react → components/site-components}/CopyFixPrompt.js +2 -2
- package/dist/report/{react → components/site-components}/HeroCard.d.ts +2 -2
- package/dist/report/{react → components/site-components}/HeroCard.js +2 -2
- package/dist/report/{react → components/site-components}/ScopeWarnings.d.ts +2 -2
- package/dist/report/{react → components/site-components}/ScopeWarnings.js +3 -3
- package/dist/report/{react → components/site-components}/TraceWaterfall.d.ts +3 -3
- package/dist/report/{react → components/site-components}/TraceWaterfall.js +5 -5
- package/dist/report/components/site-components/compute.d.ts +28 -0
- package/dist/report/components/site-components/compute.js +132 -0
- package/dist/report/components/site-components/faces.d.ts +21 -0
- package/dist/report/components/site-components/faces.js +75 -0
- package/dist/report/components/site-components/index.d.ts +74 -0
- package/dist/report/components/site-components/index.js +220 -0
- package/dist/report/{scope-warnings.d.ts → components/site-components/scope-warnings.d.ts} +2 -2
- package/dist/report/{scope-warnings.js → components/site-components/scope-warnings.js} +2 -2
- package/dist/report/{react → components/summaries}/ScopeSummary.d.ts +2 -2
- package/dist/report/{react → components/summaries}/ScopeSummary.js +17 -8
- package/dist/report/components/summaries/compute.d.ts +7 -0
- package/dist/report/components/summaries/compute.js +51 -0
- package/dist/report/components/summaries/faces.d.ts +7 -0
- package/dist/report/components/summaries/faces.js +38 -0
- package/dist/report/components/summaries/index.d.ts +27 -0
- package/dist/report/components/summaries/index.js +78 -0
- package/dist/report/definition/grid-layout.d.ts +50 -0
- package/dist/report/definition/grid-layout.js +89 -0
- package/dist/report/{primitives.d.ts → definition/primitives.d.ts} +34 -4
- package/dist/report/{primitives.js → definition/primitives.js} +101 -10
- package/dist/report/{report.d.ts → definition/report.d.ts} +36 -65
- package/dist/report/{report.js → definition/report.js} +58 -90
- package/dist/report/{text/table.d.ts → definition/table-text.d.ts} +2 -2
- package/dist/report/{text/table.js → definition/table-text.js} +2 -2
- package/dist/report/{tree.d.ts → definition/tree.d.ts} +43 -11
- package/dist/report/{tree.js → definition/tree.js} +9 -11
- package/dist/report/index.d.ts +33 -18
- package/dist/report/index.js +23 -14
- package/dist/report/{aggregate.d.ts → model/aggregate.d.ts} +12 -4
- package/dist/report/{aggregate.js → model/aggregate.js} +28 -8
- package/dist/report/{flag.d.ts → model/flag.d.ts} +14 -1
- package/dist/report/{flag.js → model/flag.js} +35 -0
- package/dist/report/{format.d.ts → model/format.d.ts} +13 -7
- package/dist/report/{format.js → model/format.js} +79 -9
- package/dist/report/{locale.d.ts → model/locale.d.ts} +17 -17
- package/dist/report/{locale.js → model/locale.js} +31 -33
- package/dist/report/{metrics.d.ts → model/metrics.d.ts} +2 -2
- package/dist/report/{metrics.js → model/metrics.js} +2 -2
- package/dist/report/{types.d.ts → model/types.d.ts} +154 -20
- package/dist/report/react/index.d.ts +32 -21
- package/dist/report/react/index.js +33 -21
- package/dist/report/{load.d.ts → runtime/load.d.ts} +1 -1
- package/dist/report/{load.js → runtime/load.js} +1 -1
- package/dist/report/runtime/text.d.ts +67 -0
- package/dist/report/runtime/text.js +114 -0
- package/dist/report/runtime/web.d.ts +35 -0
- package/dist/report/runtime/web.js +70 -0
- package/dist/results/annotated-source.d.ts +87 -0
- package/dist/results/attempt-evidence.d.ts +74 -0
- package/dist/results/attempt-source.d.ts +15 -0
- package/dist/results/select.d.ts +11 -1
- package/dist/results/select.js +27 -5
- package/dist/runner/eval-selection.d.ts +26 -0
- package/dist/runner/feedback/sink.d.ts +26 -1
- package/dist/runner/types.d.ts +151 -24
- package/dist/sandbox/docker.d.ts +6 -0
- package/dist/sandbox/e2b.d.ts +0 -1
- package/dist/sandbox/types.d.ts +23 -21
- package/dist/scoring/display.d.ts +7 -2
- package/dist/scoring/display.js +42 -4
- package/dist/scoring/types.d.ts +3 -3
- package/dist/shared/aggregate.d.ts +14 -0
- package/dist/shared/aggregate.js +31 -0
- package/dist/shared/types.d.ts +6 -1
- package/docs-site/images/logo-dark.svg +7 -0
- package/docs-site/images/logo.svg +6 -5
- package/docs-site/zh/README.md +8 -7
- package/docs-site/zh/examples/ai-agent-application.mdx +5 -5
- package/docs-site/zh/examples/coding-agent-extensions.mdx +4 -4
- package/docs-site/zh/examples/index.mdx +3 -3
- package/docs-site/zh/examples/integrations/ai-sdk-v7.mdx +6 -6
- package/docs-site/zh/examples/integrations/claude-sdk.mdx +3 -3
- package/docs-site/zh/examples/integrations/codex-sdk.mdx +3 -3
- package/docs-site/zh/examples/integrations/langgraph.mdx +3 -3
- package/docs-site/zh/examples/integrations/pi-sdk.mdx +2 -2
- package/docs-site/zh/explanation/adapter.mdx +19 -19
- package/docs-site/zh/explanation/assert.mdx +9 -9
- package/docs-site/zh/explanation/drive.mdx +4 -4
- package/docs-site/zh/explanation/evals.mdx +8 -8
- package/docs-site/zh/explanation/experiment.mdx +8 -8
- package/docs-site/zh/explanation/hitl.mdx +13 -13
- package/docs-site/zh/explanation/judge.mdx +3 -3
- package/docs-site/zh/explanation/overview.mdx +7 -7
- package/docs-site/zh/explanation/runner.mdx +17 -13
- package/docs-site/zh/explanation/tier.mdx +6 -6
- package/docs-site/zh/index.mdx +14 -14
- package/docs-site/zh/introduction.mdx +13 -16
- package/docs-site/zh/reference/builtin-agents.mdx +61 -28
- package/docs-site/zh/reference/capabilities.mdx +7 -27
- package/docs-site/zh/reference/cli.mdx +36 -33
- package/docs-site/zh/reference/define-agent.mdx +32 -31
- package/docs-site/zh/reference/define-config.mdx +6 -6
- package/docs-site/zh/reference/define-eval.mdx +33 -20
- package/docs-site/zh/reference/events.mdx +4 -4
- package/docs-site/zh/reference/expect.mdx +3 -3
- package/docs-site/zh/reference/official-adapters.mdx +17 -17
- package/docs-site/zh/reference/report-components.mdx +195 -121
- package/docs-site/zh/reference/results-data.mdx +13 -13
- package/docs-site/zh/troubleshooting/debug-sandbox.mdx +11 -11
- package/docs-site/zh/troubleshooting/debugging.mdx +40 -16
- package/docs-site/zh/{how-to → tutorials}/agent-feedback-loop.mdx +38 -38
- package/docs-site/zh/tutorials/agent-onboarding.mdx +100 -0
- package/docs-site/zh/tutorials/authoring.mdx +211 -0
- package/docs-site/zh/{how-to → tutorials}/ci-integration.mdx +8 -8
- package/docs-site/zh/{how-to → tutorials}/connect-otel.mdx +22 -22
- package/docs-site/zh/{how-to → tutorials}/connect-your-agent.mdx +58 -63
- package/docs-site/zh/tutorials/custom-reports.mdx +453 -0
- package/docs-site/zh/{how-to → tutorials}/dataset-fanout.mdx +4 -4
- package/docs-site/zh/tutorials/experiments.mdx +102 -0
- package/docs-site/zh/{how-to → tutorials}/fixtures.mdx +9 -9
- package/docs-site/zh/{how-to → tutorials}/publish-report.mdx +6 -6
- package/docs-site/zh/tutorials/quickstart.mdx +18 -18
- package/docs-site/zh/{how-to → tutorials}/reporters.mdx +6 -6
- package/docs-site/zh/{how-to → tutorials}/sandbox-agent.mdx +6 -6
- package/docs-site/zh/{how-to → tutorials}/sandbox-providers.mdx +41 -19
- package/docs-site/zh/{how-to → tutorials}/scoring-guide.mdx +4 -4
- package/docs-site/zh/{how-to → tutorials}/viewing-results.mdx +60 -57
- package/docs-site/zh/tutorials/write-experiment.mdx +353 -0
- package/docs-site/zh/{how-to → tutorials}/write-send.mdx +40 -40
- package/package.json +10 -7
- package/src/agents/bub.ts +21 -8
- package/src/agents/claude-code.ts +23 -10
- package/src/agents/codex.test.ts +22 -7
- package/src/agents/codex.ts +22 -9
- package/src/agents/index.ts +2 -2
- package/src/agents/openai-compat.ts +1 -1
- package/src/agents/post-setup.ts +45 -22
- package/src/agents/streaming.ts +1 -1
- package/src/agents/types.ts +20 -17
- package/src/agents/ui-message-stream.test.ts +10 -0
- package/src/agents/ui-message-stream.ts +12 -1
- package/src/cli.ts +75 -59
- package/src/context/types.ts +21 -21
- package/src/define.ts +13 -0
- package/src/i18n/en.ts +27 -12
- package/src/i18n/zh-CN.ts +27 -12
- package/src/index.ts +2 -0
- package/src/o11y/prices.json +176 -99
- package/src/report/{react → assets}/colors.ts +22 -0
- package/src/report/{react → assets}/enhance.js +1 -33
- package/src/report/assets/styles.css +1785 -0
- package/src/report/built-in/index.tsx +2 -2
- package/src/report/built-in/standard.tsx +18 -6
- package/src/report/components/attempt-detail/AttemptAssertions.tsx +67 -0
- package/src/report/components/attempt-detail/AttemptConversation.tsx +158 -0
- package/src/report/components/attempt-detail/AttemptDiagnostics.tsx +38 -0
- package/src/report/components/attempt-detail/AttemptDiff.tsx +35 -0
- package/src/report/components/attempt-detail/AttemptError.tsx +31 -0
- package/src/report/components/attempt-detail/AttemptFixPrompt.tsx +26 -0
- package/src/report/components/attempt-detail/AttemptSource.tsx +224 -0
- package/src/report/components/attempt-detail/AttemptSummary.tsx +74 -0
- package/src/report/components/attempt-detail/AttemptTimeline.tsx +108 -0
- package/src/report/components/attempt-detail/AttemptTrace.tsx +63 -0
- package/src/report/components/attempt-detail/AttemptUsage.tsx +29 -0
- package/src/report/components/attempt-detail/attempt-components.test.tsx +758 -0
- package/src/report/components/attempt-detail/compute.ts +336 -0
- package/src/report/components/attempt-detail/faces.ts +260 -0
- package/src/report/components/attempt-detail/index.tsx +600 -0
- package/src/report/components/attempt-detail/validate.test.ts +252 -0
- package/src/report/{react → components}/cell.tsx +6 -3
- package/src/report/{report.test.ts → components/compute.test.ts} +316 -46
- package/src/report/{react → components/entity-lists}/AttemptList.tsx +24 -16
- package/src/report/{react → components/entity-lists}/EvalList.tsx +0 -0
- package/src/report/{react → components/entity-lists}/ExperimentList.tsx +33 -27
- package/src/report/components/entity-lists/compute.ts +196 -0
- package/src/report/components/entity-lists/faces.ts +208 -0
- package/src/report/components/entity-lists/index.tsx +238 -0
- package/src/report/components/entity-lists/validate.test.ts +114 -0
- package/src/report/{react → components}/fixtures.ts +2 -2
- package/src/report/{react → components/metric-views}/DeltaTable.tsx +5 -5
- package/src/report/{react → components/metric-views}/MetricBars.tsx +5 -5
- package/src/report/{react → components/metric-views}/MetricLine.tsx +9 -6
- package/src/report/{react → components/metric-views}/MetricMatrix.tsx +6 -6
- package/src/report/{react → components/metric-views}/MetricScatter.tsx +42 -70
- package/src/report/{react → components/metric-views}/MetricTable.tsx +6 -6
- package/src/report/{react → components/metric-views}/Scoreboard.tsx +4 -4
- package/src/report/{compute.ts → components/metric-views/compute.ts} +51 -477
- package/src/report/components/metric-views/faces.ts +423 -0
- package/src/report/components/metric-views/index.tsx +352 -0
- package/src/report/{text → components/metric-views}/plot.ts +1 -1
- package/src/report/components/metric-views/validate.test.ts +211 -0
- package/src/report/{react → components}/render.test.tsx +66 -26
- package/src/report/components/shared-compute.ts +59 -0
- package/src/report/components/shared-faces.ts +30 -0
- package/src/report/components/shared.ts +192 -0
- package/src/report/{react → components/site-components}/CopyFixPrompt.tsx +3 -3
- package/src/report/{react → components/site-components}/HeroCard.tsx +3 -3
- package/src/report/{react → components/site-components}/ScopeWarnings.tsx +4 -4
- package/src/report/{react → components/site-components}/TraceWaterfall.tsx +13 -10
- package/src/report/components/site-components/compute.ts +144 -0
- package/src/report/components/site-components/faces.ts +80 -0
- package/src/report/components/site-components/index.tsx +265 -0
- package/src/report/{scope-warnings.ts → components/site-components/scope-warnings.ts} +3 -3
- package/src/report/{site-components.test.tsx → components/site-components/site-components.test.tsx} +23 -21
- package/src/report/components/site-components/validate.test.ts +89 -0
- package/src/report/{react → components/summaries}/ScopeSummary.tsx +26 -11
- package/src/report/components/summaries/compute.ts +56 -0
- package/src/report/components/summaries/faces.ts +48 -0
- package/src/report/components/summaries/index.tsx +127 -0
- package/src/report/components/summaries/validate.test.ts +49 -0
- package/src/report/definition/grid-layout.test.ts +124 -0
- package/src/report/definition/grid-layout.ts +146 -0
- package/src/report/{primitives.tsx → definition/primitives.tsx} +168 -18
- package/src/report/{report.ts → definition/report.ts} +98 -165
- package/src/report/{shell-head.test.ts → definition/shell-head.test.ts} +2 -2
- package/src/report/{text/table.ts → definition/table-text.ts} +4 -4
- package/src/report/{tree.ts → definition/tree.ts} +56 -20
- package/src/report/index.ts +128 -66
- package/src/report/{aggregate.ts → model/aggregate.ts} +31 -9
- package/src/report/{flag.ts → model/flag.ts} +39 -1
- package/src/report/{format.ts → model/format.ts} +89 -10
- package/src/report/{locale.ts → model/locale.ts} +34 -36
- package/src/report/{metrics.ts → model/metrics.ts} +3 -3
- package/src/report/{types.ts → model/types.ts} +159 -22
- package/src/report/react/index.tsx +54 -26
- package/src/report/{dual-render.test.tsx → runtime/dual-render.test.tsx} +601 -53
- package/src/report/runtime/host.test.ts +44 -0
- package/src/report/runtime/host.ts +138 -0
- package/src/report/{load.ts → runtime/load.ts} +1 -1
- package/src/report/runtime/text.ts +192 -0
- package/src/report/runtime/web.ts +106 -0
- package/src/results/attempt-evidence.ts +5 -1
- package/src/results/host-equivalence.test.ts +53 -2
- package/src/results/select.ts +29 -5
- package/src/runner/attempt.test.ts +48 -1
- package/src/runner/attempt.ts +62 -42
- package/src/runner/cleanup-timeout.test.ts +16 -0
- package/src/runner/cleanup-timeout.ts +23 -0
- package/src/runner/discover.ts +1 -2
- package/src/runner/eval-selection.test.ts +187 -0
- package/src/runner/eval-selection.ts +109 -0
- package/src/runner/experiment-cleanup-registry.test.ts +89 -0
- package/src/runner/experiment-cleanup-registry.ts +39 -0
- package/src/runner/experiment-labels.test.ts +71 -0
- package/src/runner/feedback/agent.test.ts +33 -1
- package/src/runner/feedback/agent.ts +12 -16
- package/src/runner/feedback/ci.test.ts +35 -1
- package/src/runner/feedback/ci.ts +16 -18
- package/src/runner/feedback/coordinator.ts +28 -0
- package/src/runner/feedback/human.test.ts +108 -1
- package/src/runner/feedback/human.ts +71 -21
- package/src/runner/feedback/reducer.test.ts +64 -0
- package/src/runner/feedback/reducer.ts +26 -0
- package/src/runner/feedback/sink.ts +40 -1
- package/src/runner/fingerprint.ts +2 -1
- package/src/runner/report.test.ts +14 -0
- package/src/runner/report.ts +3 -1
- package/src/runner/run.test.ts +390 -12
- package/src/runner/run.ts +259 -26
- package/src/runner/sandbox-selection.test.ts +13 -3
- package/src/runner/sandbox-selection.ts +4 -2
- package/src/runner/types.ts +160 -24
- package/src/sandbox/docker.ts +7 -0
- package/src/sandbox/e2b-reconcile.test.ts +125 -0
- package/src/sandbox/e2b.ts +47 -12
- package/src/sandbox/types.ts +23 -21
- package/src/scoring/display.test.ts +28 -2
- package/src/scoring/display.ts +38 -5
- package/src/scoring/types.ts +3 -3
- package/src/shared/aggregate.test.ts +52 -0
- package/src/shared/aggregate.ts +29 -0
- package/src/shared/types.ts +6 -1
- package/src/show/command.test.ts +34 -0
- package/src/show/command.ts +16 -0
- package/src/show/compose.ts +1 -1
- package/src/show/index.ts +56 -20
- package/src/show/render.ts +15 -239
- package/src/show/show.test.ts +127 -34
- package/src/view/app/App.test.tsx +1 -1
- package/src/view/app/App.tsx +119 -47
- package/src/view/app/i18n.ts +7 -147
- package/src/view/app/lib/attempt-dialog.test.ts +65 -0
- package/src/view/app/lib/attempt-dialog.ts +69 -0
- package/src/view/app/main.tsx +0 -1
- package/src/view/app/types.ts +3 -86
- package/src/view/artifact-serving.test.ts +22 -38
- package/src/view/client-dist/app.css +1 -1
- package/src/view/client-dist/app.js +15 -22
- package/src/view/data.test.ts +48 -36
- package/src/view/data.ts +105 -83
- package/src/view/index.ts +1 -1
- package/src/view/server.ts +32 -2
- package/src/view/shared/types.ts +6 -31
- package/src/view/site-parity.test.ts +24 -1
- package/src/view/site.ts +144 -17
- package/src/view/styles.css +0 -805
- package/src/view/view-report.test.ts +143 -22
- package/dist/report/components.d.ts +0 -179
- package/dist/report/components.js +0 -544
- package/dist/report/compute.d.ts +0 -139
- package/dist/report/react/ExperimentComparison.d.ts +0 -10
- package/dist/report/react/ExperimentComparison.js +0 -12
- package/dist/report/react/MetricScatter.d.ts +0 -9
- package/dist/report/react/format.d.ts +0 -3
- package/dist/report/react/format.js +0 -7
- package/dist/report/text/faces.d.ts +0 -45
- package/dist/report/text/faces.js +0 -671
- package/dist/report/web.d.ts +0 -32
- package/dist/report/web.js +0 -48
- package/docs-site/zh/how-to/authoring.mdx +0 -162
- package/docs-site/zh/how-to/custom-reports.mdx +0 -414
- package/docs-site/zh/how-to/experiments.mdx +0 -86
- package/docs-site/zh/how-to/write-experiment.mdx +0 -164
- package/src/report/components.tsx +0 -925
- package/src/report/react/ExperimentComparison.tsx +0 -73
- package/src/report/react/format.ts +0 -9
- package/src/report/react/styles.css +0 -945
- package/src/report/text/faces.ts +0 -767
- package/src/report/web.ts +0 -77
- package/src/show/report-host.test.ts +0 -205
- package/src/show/report-host.ts +0 -389
- package/src/view/app/components/AttemptModal.tsx +0 -496
- package/src/view/app/components/CodeView.test.tsx +0 -142
- package/src/view/app/components/CodeView.tsx +0 -310
- package/src/view/app/components/CopyControls.tsx +0 -106
- package/src/view/app/components/Trace.tsx +0 -100
- package/src/view/app/components/Transcript.tsx +0 -157
- package/src/view/app/components/ui/badge.tsx +0 -21
- package/src/view/app/lib/artifact-url.ts +0 -17
- package/src/view/app/lib/attempt-route.test.ts +0 -80
- package/src/view/app/lib/attempt-route.ts +0 -52
- package/src/view/app/lib/format.ts +0 -70
- package/src/view/app/lib/guards.test.ts +0 -108
- package/src/view/app/lib/guards.ts +0 -71
- package/src/view/app/lib/rows.ts +0 -22
- package/src/view/app/lib/transcript-data.tsx +0 -151
- package/src/view/app/lib/verdict.ts +0 -23
- package/src/view/app/shared.ts +0 -8
- /package/dist/report/{react → components/metric-views}/chart-math.d.ts +0 -0
- /package/dist/report/{react → components/metric-views}/chart-math.js +0 -0
- /package/dist/report/{text → components/metric-views}/plot.d.ts +0 -0
- /package/dist/report/{react → components/site-components}/PoweredBy.d.ts +0 -0
- /package/dist/report/{react → components/site-components}/PoweredBy.js +0 -0
- /package/dist/report/{text/layout.d.ts → model/text-layout.d.ts} +0 -0
- /package/dist/report/{text/layout.js → model/text-layout.js} +0 -0
- /package/dist/report/{types.js → model/types.js} +0 -0
- /package/src/report/{react → components/metric-views}/chart-math.test.ts +0 -0
- /package/src/report/{react → components/metric-views}/chart-math.ts +0 -0
- /package/src/report/{react → components/site-components}/PoweredBy.tsx +0 -0
- /package/src/report/{text/layout.ts → model/text-layout.ts} +0 -0
package/src/runner/types.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// runner 域类型:结果 / 汇总 / reporter 契约,eval / experiment / config 定义,
|
|
2
2
|
// 以及调度器的编排类型(AgentRun / RunOptions / Attempt)。
|
|
3
3
|
|
|
4
|
-
import type {
|
|
4
|
+
import type { JsonValue, LocalizedText, ScopedFeedback, SourceArtifact } from "../shared/types.ts";
|
|
5
5
|
import type { O11ySummary, StreamEvent, TraceSpan, Usage } from "../o11y/types.ts";
|
|
6
6
|
import type { Agent, AgentSetupManifest } from "../agents/types.ts";
|
|
7
7
|
import type { Sandbox, SandboxHookContext, SandboxOption } from "../sandbox/types.ts";
|
|
@@ -21,6 +21,8 @@ export interface ExperimentRunInfo {
|
|
|
21
21
|
description?: string;
|
|
22
22
|
reasoningEffort?: string;
|
|
23
23
|
flags?: Record<string, JsonValue>;
|
|
24
|
+
/** 报告归类标注(ExperimentDef.labels 原样投影);不透传运行时,不参与可比性配置。 */
|
|
25
|
+
labels?: Record<string, string | number>;
|
|
24
26
|
runs: number;
|
|
25
27
|
earlyExit: boolean;
|
|
26
28
|
timeoutMs?: number;
|
|
@@ -50,6 +52,10 @@ export interface SandboxRunInfo {
|
|
|
50
52
|
* provider / 用户 hook 能直接设置的公共字段。
|
|
51
53
|
*/
|
|
52
54
|
export type LifecyclePhase =
|
|
55
|
+
// 实验级(整场一次,宿主机侧):只用于错误/诊断归因,不属于任何单个 attempt,
|
|
56
|
+
// 永不出现在 phases[] 计时里(见 docs/feature/experiments/architecture.md「实验级生命周期」)
|
|
57
|
+
| "experiment.setup" // ExperimentDef.setup;setup 抛错时本实验所有 attempt 的 error.phase
|
|
58
|
+
| "experiment.teardown" // ExperimentDef.teardown;失败只产生运行级 diagnostic
|
|
53
59
|
// 主链:从排队到 trace collect,覆盖到判定与主证据收集完成,按执行序
|
|
54
60
|
| "sandbox.queue" // 等待并发信号量(调度等待,唯一不属于某个 owner 的成员)
|
|
55
61
|
| "sandbox.create" // provider 起沙箱
|
|
@@ -64,7 +70,7 @@ export type LifecyclePhase =
|
|
|
64
70
|
| "scoring.evaluate" // 断言 finalize + 判定,含 judge 调用
|
|
65
71
|
| "telemetry.collect" // OTLP receiver settle / collect
|
|
66
72
|
// 收尾段:无论主链成败都执行,不计入 durationMs 口径,按执行序
|
|
67
|
-
| "eval.teardown" // EvalDef.
|
|
73
|
+
| "eval.teardown" // EvalDef.teardown
|
|
68
74
|
| "agent.teardown"
|
|
69
75
|
| "sandbox.teardown" // SandboxSpec.teardown() 钩子链
|
|
70
76
|
| "sandbox.suspend" // 留存提交后 provider 把现场转入休眠(docker stop / e2b pause)
|
|
@@ -320,32 +326,41 @@ export interface EvalDef {
|
|
|
320
326
|
description?: string;
|
|
321
327
|
/** 标签,供 CLI `--tag` 过滤和 view 分类;与 id 前缀过滤是两套独立的筛选维度。 */
|
|
322
328
|
tags?: string[];
|
|
323
|
-
/**
|
|
329
|
+
/** 这条评估用例需要的环境 profile id(provider-neutral,如 `"python-3.9-astropy-4.2"`);由 sandbox spec 的 `environments` 表翻译成该 provider 的预制产物。 */
|
|
324
330
|
environment?: string;
|
|
325
|
-
/** 覆盖项目级 Config.judge
|
|
331
|
+
/** 覆盖项目级 Config.judge,只对这一条评估用例生效(如换个更贵的评审模型)。 */
|
|
326
332
|
judge?: JudgeConfig;
|
|
327
|
-
/** 覆盖 / 追加项目级 Config.reporters
|
|
333
|
+
/** 覆盖 / 追加项目级 Config.reporters,只对这一条评估用例生效。 */
|
|
328
334
|
reporters?: Reporter[];
|
|
329
|
-
/** 覆盖项目级 / CLI 的单次 attempt 超时(毫秒)
|
|
335
|
+
/** 覆盖项目级 / CLI 的单次 attempt 超时(毫秒),只对这一条评估用例生效。 */
|
|
330
336
|
timeoutMs?: number;
|
|
331
337
|
/** 任意附加元数据,原样透传进 EvalResult,不参与调度或打分;供自定义 reporter 消费。 */
|
|
332
338
|
metadata?: Record<string, unknown>;
|
|
333
339
|
/**
|
|
334
|
-
* 调整 agent diff 的归因排除清单(
|
|
340
|
+
* 调整 agent diff 的归因排除清单(仅 Sandbox 型;见 docs/feature/eval/README.md):两个数组都是
|
|
335
341
|
* gitignore 风格 glob(workdir 相对)。默认排除 .git/node_modules/构建产物/包管理器缓存;
|
|
336
342
|
* `ignore` 在默认清单上追加排除;`include` 优先级最高,把匹配路径显式加回。
|
|
337
343
|
* 合成规则固定为「默认 ∪ ignore,再被 include 打洞」,清单在分类账锚点时冻结。
|
|
338
344
|
*/
|
|
339
345
|
diff?: { include?: string[]; ignore?: string[] };
|
|
340
346
|
/**
|
|
341
|
-
*
|
|
347
|
+
* 评估用例级预置:拿到 Sandbox(已上传 workspace + git 基线 + 装好依赖前)。
|
|
342
348
|
* 默认命令以非 root 跑(agent 的自然环境);装系统依赖时给 `runCommand` 传 `{ root: true }`
|
|
343
349
|
* (如 `runCommand("apt-get", ["install", …], { root: true })`),跨 provider 语义一致。
|
|
344
350
|
* 第二个参数是绑定到 `eval.setup` 的窄上下文(`ctx.progress` / `ctx.diagnostic`,
|
|
345
|
-
* 见 docs/feature/eval/README.md)
|
|
351
|
+
* 见 docs/feature/eval/README.md)。setup 不返回值;要把产物传给 teardown,
|
|
352
|
+
* 以 `sandbox` 实例作键存取(并发 attempt 共享同一模块,普通模块变量会互相覆写)。
|
|
346
353
|
*/
|
|
347
|
-
setup?: (sandbox: Sandbox, ctx: SandboxHookContext) => Promise<void
|
|
348
|
-
/**
|
|
354
|
+
setup?: (sandbox: Sandbox, ctx: SandboxHookContext) => Promise<void> | void;
|
|
355
|
+
/**
|
|
356
|
+
* 评估用例级收尾:attempt 收尾链的第一段(`eval.teardown` → `agent.teardown` →
|
|
357
|
+
* `sandbox.teardown`),Sandbox 此刻还活着。当且仅当 `eval.setup` 时点走到过才执行——
|
|
358
|
+
* `setup` / `test` 抛错都不豁免,未声明 `setup` 不影响触发;抛错或超 30s 清理上限
|
|
359
|
+
* 只记 `teardown-failed` 诊断,不改判定。管 Sandbox 外的临时 Fixture(临时 repo / bucket),
|
|
360
|
+
* Sandbox 内的东西随销毁自动回收、不需要它。
|
|
361
|
+
*/
|
|
362
|
+
teardown?: (sandbox: Sandbox, ctx: SandboxHookContext) => Promise<void> | void;
|
|
363
|
+
/** 评估用例主体:拿到 TestContext,驱动对话 / Sandbox 操作并就地断言。 */
|
|
349
364
|
test(t: TestContext): Promise<void> | void;
|
|
350
365
|
}
|
|
351
366
|
|
|
@@ -363,6 +378,21 @@ export interface DiscoveredEval extends EvalDef {
|
|
|
363
378
|
source: CapturedEvalSource;
|
|
364
379
|
}
|
|
365
380
|
|
|
381
|
+
/**
|
|
382
|
+
* `ExperimentDef.setup` / `teardown` 拿到的窄上下文。`progress` 更新本实验运行级
|
|
383
|
+
* active 行的次要文本(短命状态,agent/ci profile 不逐条输出),`diagnostic` 进运行级永久
|
|
384
|
+
* 事件流(实验级钩子不属于任何单个 attempt,诊断不落 attempt 的 `result.json`;setup 抛错
|
|
385
|
+
* 以每条 attempt 的结构化 `error` 落盘,失败仍可回顾)。钩子的起止本身由 runner 直接发布为
|
|
386
|
+
* 运行级反馈,不依赖这里的 `progress`(见 docs/feature/experiments/cli.md「实验级钩子的显示」)。
|
|
387
|
+
*/
|
|
388
|
+
export interface ExperimentHookContext extends ScopedFeedback {
|
|
389
|
+
readonly experimentId: string;
|
|
390
|
+
/** 本实验解析后实际选中的 eval id 全集(evals 过滤器的求值结果)。 */
|
|
391
|
+
readonly selectedEvalIds: readonly string[];
|
|
392
|
+
/** 用户中断(Ctrl+C / kill)时 abort;长启动的 setup 应观察它提前退出。 */
|
|
393
|
+
readonly signal?: AbortSignal;
|
|
394
|
+
}
|
|
395
|
+
|
|
366
396
|
export interface ExperimentDef {
|
|
367
397
|
/** 路径推导,定义里禁止手写(defineExperiment 会拒绝显式传入)。 */
|
|
368
398
|
id?: string;
|
|
@@ -381,12 +411,28 @@ export interface ExperimentDef {
|
|
|
381
411
|
* (defineExperiment 解析时校验,非 JSON 直接报错),经 ctx.flags 透传给 adapter、
|
|
382
412
|
* t.flags 暴露给 eval,并原样进入结果快照的 ExperimentRunInfo.flags。 */
|
|
383
413
|
flags?: Record<string, JsonValue>;
|
|
414
|
+
/**
|
|
415
|
+
* 报告归类标注:实验在各对比轴上的坐标(如 `{ line: "codex", memory: "mempal" }`)。
|
|
416
|
+
* 值域 string | number(解析时校验)。与 `flags` 的分界是「会不会改变 attempt 里发生的事」:
|
|
417
|
+
* labels 不透传 ctx / t(agent 和 eval 看不见)、不参与可比性配置(改它不作废已有结果),
|
|
418
|
+
* 只原样投影进快照的 `ExperimentRunInfo.labels` 供报告维度(`label()` / `numericLabel()`)
|
|
419
|
+
* 分组。`line` 键被默认报告识别:组内任一实验声明了它,散点按线归类并连线。
|
|
420
|
+
* 见 docs/feature/experiments/library.md「labels」。
|
|
421
|
+
*/
|
|
422
|
+
labels?: Record<string, string | number>;
|
|
384
423
|
/** 同一 eval 重复跑几次(结果各计一条 attempt);省略/CLI `--runs` 覆盖时默认 1。 */
|
|
385
424
|
runs?: number;
|
|
386
|
-
/** 一次重复(runs > 1)里某次 attempt
|
|
425
|
+
/** 一次重复(runs > 1)里某次 attempt 通过后是否跳过剩余重复;省略默认 false(`runs` 跑满、测完整通过率),
|
|
426
|
+
* 显式打开用于「只想知道能不能过」的省钱场景。 */
|
|
387
427
|
earlyExit?: boolean;
|
|
388
|
-
/**
|
|
389
|
-
|
|
428
|
+
/**
|
|
429
|
+
* 这个实验覆盖哪些 eval:`"*"` 全部、字符串数组按 id 前缀、或自定义谓词(逐条收到发现并扇出后的
|
|
430
|
+
* 只读 `EvalDescriptor`,不暴露路径 / 执行字段);省略等价于 `"*"`。谓词对本次 invocation 的
|
|
431
|
+
* 候选 eval 各求值一次,解析结果作为 `selectedEvalIds` 落进快照——不是运行时反复调用的过滤器
|
|
432
|
+
* (见 docs/feature/eval/library.md「EvalDescriptor」、docs/feature/experiments/library.md
|
|
433
|
+
* 「evals:遍历发现结果,自定义选择」)。
|
|
434
|
+
*/
|
|
435
|
+
evals?: "*" | readonly string[] | ((e: EvalDescriptor) => boolean);
|
|
390
436
|
/** 覆盖项目级 / CLI 的单次 attempt 超时(毫秒),只对这个实验生效。 */
|
|
391
437
|
timeoutMs?: number;
|
|
392
438
|
/**
|
|
@@ -406,11 +452,42 @@ export interface ExperimentDef {
|
|
|
406
452
|
* 或给撞 provider 限额的实验单独降速。
|
|
407
453
|
*/
|
|
408
454
|
maxConcurrency?: number;
|
|
455
|
+
/**
|
|
456
|
+
* 实验级生命周期钩子对的 setup 侧:整场至多一次、宿主机侧,管「每实验一份、所有 attempt
|
|
457
|
+
* 共享」的宿主机资源(隧道、mock server、license 租约)。本实验第一个通过派发许可的
|
|
458
|
+
* attempt 触发(memoized,并发 attempt 等同一个结果;全部结果被 carry 携入时不执行)。
|
|
459
|
+
* setup 不返回值;产物写模块级变量,`teardown` 与同文件 agent / sandbox 钩子从闭包读,
|
|
460
|
+
* runner 不做值的中介。setup 抛错 → 本实验所有 attempt 记 `errored`
|
|
461
|
+
* (code `"experiment-setup-failed"`、phase `"experiment.setup"`),同批其它实验不受影响。
|
|
462
|
+
* 函数体不进 fingerprint,改了钩子逻辑用 `--force` 强制重跑。
|
|
463
|
+
* 见 docs/feature/experiments/architecture.md「实验级生命周期」。
|
|
464
|
+
*/
|
|
465
|
+
setup?: (ctx: ExperimentHookContext) => void | Promise<void>;
|
|
466
|
+
/**
|
|
467
|
+
* 实验级生命周期钩子对的 teardown 侧:本实验全部 attempt 收尾后执行(运行被中断也执行),
|
|
468
|
+
* 当且仅当 setup 时点走到过——setup 抛错不豁免(半初始化现场同样要扫尾,teardown 对可能
|
|
469
|
+
* 未赋值的闭包变量做防御),未声明 setup 不影响触发;一个 attempt 都不派发则跳过。
|
|
470
|
+
* 抛错或超 30s 清理上限只记运行级 diagnostic(`experiment-teardown-failed`),不改判定。
|
|
471
|
+
*/
|
|
472
|
+
teardown?: (ctx: ExperimentHookContext) => void | Promise<void>;
|
|
409
473
|
}
|
|
410
474
|
|
|
411
475
|
export interface DiscoveredExperiment extends ExperimentDef {
|
|
412
476
|
id: string;
|
|
413
|
-
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* 用户谓词(`ExperimentDef.evals`)能看到的唯一形状——发现并扇出后的显式白名单投影,不透传
|
|
481
|
+
* `DiscoveredEval` 原对象(不暴露 `sourcePath` / `baseDir` / `test` / hooks 等内部路径与执行字段)。
|
|
482
|
+
* `tags` 缺省为冻结空数组;`metadata` 原样引用作者声明的对象(至少浅冻结),供 `tags.includes(...)` /
|
|
483
|
+
* `environment` / `metadata.<key>` 判断(见 docs/feature/eval/library.md「EvalDescriptor」)。
|
|
484
|
+
*/
|
|
485
|
+
export interface EvalDescriptor {
|
|
486
|
+
readonly id: string;
|
|
487
|
+
readonly description?: string;
|
|
488
|
+
readonly tags: readonly string[];
|
|
489
|
+
readonly environment?: string;
|
|
490
|
+
readonly metadata?: Readonly<Record<string, unknown>>;
|
|
414
491
|
}
|
|
415
492
|
|
|
416
493
|
export interface Config {
|
|
@@ -419,11 +496,11 @@ export interface Config {
|
|
|
419
496
|
* 可传字符串,或按 locale 提供多语言(如 `{ en: "...", "zh-CN": "..." }`),随 view 语言切换。
|
|
420
497
|
*/
|
|
421
498
|
name?: LocalizedText;
|
|
422
|
-
/**
|
|
499
|
+
/** 项目级默认 Sandbox provider(docker / vercel / e2b / custom);experiment 可覆盖。 */
|
|
423
500
|
sandbox?: SandboxOption;
|
|
424
|
-
/**
|
|
501
|
+
/** 上传进 Sandbox 的工作区根目录,省略则用项目根;评估用例的 sandbox 视图从这里起步。 */
|
|
425
502
|
workspace?: string;
|
|
426
|
-
/** 项目级默认 judge 配置(model / baseUrl / apiKeyEnv);EvalDef.judge
|
|
503
|
+
/** 项目级默认 judge 配置(model / baseUrl / apiKeyEnv);EvalDef.judge 可按评估用例覆盖。 */
|
|
427
504
|
judge?: JudgeConfig;
|
|
428
505
|
/** 项目级默认 reporter 列表(如落盘 / 上传结果);EvalDef.reporters 会与它合并。 */
|
|
429
506
|
reporters?: Reporter[];
|
|
@@ -434,11 +511,11 @@ export interface Config {
|
|
|
434
511
|
/**
|
|
435
512
|
* OTLP 接收配置,niceeval 项目内唯一入口(不读 NICEEVAL_OTLP_* 环境变量)。
|
|
436
513
|
* `port` 钉住接收端口(固定端口模式:长驻服务把 OTEL_EXPORTER_OTLP_ENDPOINT 一次性指到
|
|
437
|
-
* http://localhost:<port>/v1/traces
|
|
514
|
+
* http://localhost:<port>/v1/traces,跑多少次评估用例都不用改)。省略 = 每次运行动态分配
|
|
438
515
|
* 临时端口(经 ctx.telemetry 交给 adapter)。代价:固定端口下同机同时只能跑一个 niceeval 进程,
|
|
439
516
|
* 且该端口被别的进程占用时会报错——换一个空闲端口写回这里即可。
|
|
440
517
|
* `host` 是报给 adapter 的接收端 hostname(而非监听地址,监听地址恒为 0.0.0.0):默认
|
|
441
|
-
* "127.0.0.1";docker
|
|
518
|
+
* "127.0.0.1";docker Sandbox 型 tracing 需要 "host.docker.internal" 之类的场景,或配了隧道
|
|
442
519
|
* 的远程接入,在这里覆盖。
|
|
443
520
|
*/
|
|
444
521
|
telemetry?: { host?: string; port?: number };
|
|
@@ -496,18 +573,29 @@ export interface AgentRun {
|
|
|
496
573
|
resolvedSandboxes?: Map<string, SandboxOption>;
|
|
497
574
|
timeoutMs?: number;
|
|
498
575
|
budget?: number;
|
|
499
|
-
evalFilter: (id: string) => boolean;
|
|
500
576
|
experimentId?: string;
|
|
501
577
|
/** 实验的一句话描述(ExperimentDef.description),进结果快照的 ExperimentRunInfo。 */
|
|
502
578
|
description?: string;
|
|
579
|
+
/** 报告归类标注(ExperimentDef.labels),原样进 ExperimentRunInfo.labels;不透传 ctx / t。 */
|
|
580
|
+
labels?: Record<string, string | number>;
|
|
503
581
|
/** evals 过滤器的指纹(数组内容 / 函数体哈希),进 ExperimentRunInfo.evalFilterFingerprint。 */
|
|
504
582
|
evalFilterFingerprint?: string;
|
|
505
|
-
/**
|
|
506
|
-
|
|
583
|
+
/**
|
|
584
|
+
* 本次 invocation 解析后实际选中的 eval id 全集——CLI 在构造 AgentRun 时对候选 eval 各求值
|
|
585
|
+
* 一次算好(见 `eval-selection.ts` 的 `resolveExperimentEvals()`),下游(dry-run、sandbox 查表、
|
|
586
|
+
* fingerprint/carry、attempt 展开、hook ctx、落盘)只消费这份已解析结果,不重新调用用户谓词。
|
|
587
|
+
* 保持顺序 = discovery 稳定顺序,去重。
|
|
588
|
+
*/
|
|
589
|
+
selectedEvalIds: readonly string[];
|
|
507
590
|
strict?: boolean;
|
|
508
591
|
/** 本配置自己的并发上限(来自 ExperimentDef.maxConcurrency):调度器为它单建信号量,
|
|
509
592
|
* attempt 先过这道闸再占全局并发位;省略则只受全局并发约束。 */
|
|
510
593
|
maxConcurrency?: number;
|
|
594
|
+
/** 实验级生命周期钩子对(来自 ExperimentDef.setup / .teardown):setup 整场至多一次,
|
|
595
|
+
* 调度器 memoize 执行;teardown 在全部 attempt 收尾后执行,当且仅当 setup 时点走到过
|
|
596
|
+
* (语义见 ExperimentDef 对应字段)。 */
|
|
597
|
+
setup?: (ctx: ExperimentHookContext) => void | Promise<void>;
|
|
598
|
+
teardown?: (ctx: ExperimentHookContext) => void | Promise<void>;
|
|
511
599
|
}
|
|
512
600
|
|
|
513
601
|
export interface RunOptions {
|
|
@@ -621,6 +709,25 @@ export interface ActiveAttempt {
|
|
|
621
709
|
detail?: string;
|
|
622
710
|
}
|
|
623
711
|
|
|
712
|
+
/** 实验级钩子只有 setup 与它返回的 teardown 两员,同一实验内两者永不并发
|
|
713
|
+
* (teardown 在全部 attempt 收尾后才触发),所以运行级行按 experimentId 建 key 就够。 */
|
|
714
|
+
export type ExperimentHookName = "setup" | "teardown";
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* dashboard 当前可见的一个实验级钩子运行级行(见 docs/feature/experiments/cli.md
|
|
718
|
+
* 「实验级钩子的显示」)。与 `ActiveAttempt` 分开建模:钩子不属于任何单个 attempt、不占并发位,
|
|
719
|
+
* 也不参与 `total = reused + running + queued + completed` 的计数不变量——等待 setup 的
|
|
720
|
+
* attempt 保持 `queued`,这行就是「为什么它们还在排队」的解释。`detail` 来自实验级
|
|
721
|
+
* `ctx.progress`,后一条覆盖前一条。
|
|
722
|
+
*/
|
|
723
|
+
export interface ActiveExperimentHook {
|
|
724
|
+
experimentId: string;
|
|
725
|
+
hook: ExperimentHookName;
|
|
726
|
+
/** 钩子开始的墙钟时间(epoch ms),用于渲染运行级行的耗时。 */
|
|
727
|
+
startedAt: number;
|
|
728
|
+
detail?: string;
|
|
729
|
+
}
|
|
730
|
+
|
|
624
731
|
/**
|
|
625
732
|
* 一次失败/错误的永久通知:human 撤下 dashboard 后追加一行、agent/ci 立即追加一行,都读它。
|
|
626
733
|
* 字段全部结构化(locator / identity / verdict / phase 都是具名字段),profile renderer 不需要
|
|
@@ -703,6 +810,9 @@ export interface RunFeedbackState {
|
|
|
703
810
|
newTokenCount?: number;
|
|
704
811
|
estimatedCostUSD?: number;
|
|
705
812
|
active: ReadonlyMap<AttemptKey, ActiveAttempt>;
|
|
813
|
+
/** 在飞的实验级钩子(experimentId → 运行级行状态),由 "experiment-hook" 事件增删、
|
|
814
|
+
* "experiment:progress" 更新 detail(见 docs/feature/experiments/cli.md「实验级钩子的显示」)。 */
|
|
815
|
+
experimentHooks: ReadonlyMap<string, ActiveExperimentHook>;
|
|
706
816
|
failures: readonly FailureNotice[];
|
|
707
817
|
/** 本次实际派发后产生的去重失败数;复用失败不消耗 profile 的流式输出上限。 */
|
|
708
818
|
freshFailureCount: number;
|
|
@@ -755,6 +865,17 @@ export type AttemptLifecycleEvent =
|
|
|
755
865
|
}
|
|
756
866
|
| { type: "attempt:early-exit"; at: number; identity: AttemptRef; who: string };
|
|
757
867
|
|
|
868
|
+
/**
|
|
869
|
+
* 实验级 `ctx.progress` 的短命投影:只覆盖对应运行级行的 `detail`,不追加永久行——与
|
|
870
|
+
* `attempt:progress` 同一判断标准(新值使旧值失去意义)。对应的运行级行不存在时静默忽略。
|
|
871
|
+
*/
|
|
872
|
+
export interface ExperimentProgressEvent {
|
|
873
|
+
type: "experiment:progress";
|
|
874
|
+
at: number;
|
|
875
|
+
experimentId: string;
|
|
876
|
+
detail: string;
|
|
877
|
+
}
|
|
878
|
+
|
|
758
879
|
/**
|
|
759
880
|
* 运行级时钟 tick:唯一允许更新 `RunFeedbackState.elapsedMs` 的事件,由 coordinator 的定时器产出
|
|
760
881
|
*(见 plan 的可注入 `FeedbackIO` clock)。reducer 保持纯函数,不自己读 `Date.now()`,elapsedMs
|
|
@@ -812,6 +933,21 @@ export type DurableFeedbackEvent =
|
|
|
812
933
|
sandboxId: string;
|
|
813
934
|
enter?: string;
|
|
814
935
|
}
|
|
936
|
+
/**
|
|
937
|
+
* 实验级钩子(`ExperimentDef.setup` / 它返回的 teardown)的起止,由 runner 在钩子真正
|
|
938
|
+
* 开始/结束时各发一次(见 docs/feature/experiments/cli.md「实验级钩子的显示」)。`failed`
|
|
939
|
+
* 只标记钩子自身的结局——setup 失败的每条 attempt 仍以 "failure" 事件逐条给出。human TTY
|
|
940
|
+
* 用它维护运行级 active 行(不写 scrollback),append-only profile 起止各追加一行。
|
|
941
|
+
*/
|
|
942
|
+
| {
|
|
943
|
+
type: "experiment-hook";
|
|
944
|
+
at: number;
|
|
945
|
+
experimentId: string;
|
|
946
|
+
hook: ExperimentHookName;
|
|
947
|
+
status: "started" | "done" | "failed";
|
|
948
|
+
/** 只在 done / failed 上出现:钩子从开始到结束的耗时。 */
|
|
949
|
+
durationMs?: number;
|
|
950
|
+
}
|
|
815
951
|
| { type: "interrupted"; at: number }
|
|
816
952
|
| { type: "reporter-error"; at: number; reporter: string; required: boolean; message: string }
|
|
817
953
|
| { type: "summary"; at: number; summary: RunSummary; completion: RunCompletion }
|
|
@@ -836,4 +972,4 @@ export type DurableFeedbackEvent =
|
|
|
836
972
|
* profile renderer 只消费这里的具名字段,不解析 `ReporterEvent` 里的 i18n 文案或表格列宽
|
|
837
973
|
*(见 docs/feature/experiments/cli.md「输出流和落盘节奏」)。
|
|
838
974
|
*/
|
|
839
|
-
export type RunFeedbackEvent = AttemptLifecycleEvent | FeedbackTickEvent | DurableFeedbackEvent;
|
|
975
|
+
export type RunFeedbackEvent = AttemptLifecycleEvent | ExperimentProgressEvent | FeedbackTickEvent | DurableFeedbackEvent;
|
package/src/sandbox/docker.ts
CHANGED
|
@@ -535,12 +535,19 @@ export class DockerSandbox implements Sandbox {
|
|
|
535
535
|
/**
|
|
536
536
|
* 向容器任意路径写文件(二进制)。
|
|
537
537
|
* 打成单文件 tar → putArchive 到目标目录,与 uploadFiles 同一机制但目标路径自由。
|
|
538
|
+
*
|
|
539
|
+
* 修正属主:putArchive 以 root 解包,不 chown 的话文件在容器里保持 root 属主——非 root
|
|
540
|
+
* 沙箱用户不仅不能编辑它,后续对它做 `mv`/`rm` 这类改动它所在目录项的操作,只要目标目录带
|
|
541
|
+
* sticky bit(如 `/tmp`),也会因为「非属主不能改别人的目录项」被内核拒成
|
|
542
|
+
* `Operation not permitted`(与 uploadFiles() 对整个目标目录 chown 是同一个属主问题,
|
|
543
|
+
* 这里只需精确 chown 这一个文件;真机复现见 memory/docker-uploadfile-tmp-mv-eperm.md)。
|
|
538
544
|
*/
|
|
539
545
|
async uploadFile(destPath: string, content: Buffer): Promise<void> {
|
|
540
546
|
if (!this.container) throw new Error(t("docker.containerNotInitialized"));
|
|
541
547
|
const absPath = resolveSandboxPath(this.workdir, destPath);
|
|
542
548
|
const pack = packFilesToTar([{ name: basename(absPath), content }]);
|
|
543
549
|
await (this.container as Docker.Container).putArchive(pack, { path: dirname(absPath) });
|
|
550
|
+
await this.chownToSandboxUser(absPath);
|
|
544
551
|
}
|
|
545
552
|
|
|
546
553
|
/** 销毁容器:显式 stop + remove(创建时不带 AutoRemove,见 createContainer 的注释)。 */
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// 覆盖 reconcileProvision 对账逻辑,不打真实 e2b API——mock "e2b" 整个模块。
|
|
2
|
+
// 背景:线上跑分撞见过 nextItems() 之后 `for...of` 抛 "X is not iterable" 的一次,
|
|
3
|
+
// 没能稳定复现出确切成因;这里补的是"对账这条路径本身撞上瞬时错误该怎么办"这层契约,
|
|
4
|
+
// 不是去复现那次具体的 SDK 行为。
|
|
5
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
|
+
|
|
7
|
+
class FakeNotFoundError extends Error {}
|
|
8
|
+
class FakeRateLimitError extends Error {}
|
|
9
|
+
class FakeCommandExitError extends Error {}
|
|
10
|
+
|
|
11
|
+
const listMock = vi.fn();
|
|
12
|
+
const killMock = vi.fn();
|
|
13
|
+
|
|
14
|
+
vi.mock("e2b", () => ({
|
|
15
|
+
Sandbox: { list: (...args: unknown[]) => listMock(...args), kill: (...args: unknown[]) => killMock(...args) },
|
|
16
|
+
NotFoundError: FakeNotFoundError,
|
|
17
|
+
RateLimitError: FakeRateLimitError,
|
|
18
|
+
CommandExitError: FakeCommandExitError,
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
function fakePaginator(pages: Array<() => Promise<unknown[]>>) {
|
|
22
|
+
let i = 0;
|
|
23
|
+
return {
|
|
24
|
+
get hasNext() {
|
|
25
|
+
return i < pages.length;
|
|
26
|
+
},
|
|
27
|
+
nextItems: async () => {
|
|
28
|
+
const page = pages[i];
|
|
29
|
+
i += 1;
|
|
30
|
+
return page();
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
describe("reconcileProvision", () => {
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
listMock.mockReset();
|
|
38
|
+
killMock.mockReset();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("kills every sandbox found under the provision token, across pages", async () => {
|
|
42
|
+
const { reconcileProvision } = await import("./e2b.ts");
|
|
43
|
+
listMock.mockReturnValue(
|
|
44
|
+
fakePaginator([
|
|
45
|
+
async () => [{ sandboxId: "a" }],
|
|
46
|
+
async () => [{ sandboxId: "b" }],
|
|
47
|
+
]),
|
|
48
|
+
);
|
|
49
|
+
killMock.mockResolvedValue(undefined);
|
|
50
|
+
|
|
51
|
+
await reconcileProvision("tok");
|
|
52
|
+
|
|
53
|
+
expect(killMock).toHaveBeenCalledTimes(2);
|
|
54
|
+
expect(killMock.mock.calls.map((c) => c[0])).toEqual(["a", "b"]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("treats a NotFound on kill as already-reconciled, not an error", async () => {
|
|
58
|
+
const { reconcileProvision } = await import("./e2b.ts");
|
|
59
|
+
listMock.mockReturnValue(fakePaginator([async () => [{ sandboxId: "gone" }]]));
|
|
60
|
+
killMock.mockRejectedValue(new FakeNotFoundError("gone"));
|
|
61
|
+
|
|
62
|
+
await expect(reconcileProvision("tok")).resolves.toBeUndefined();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("retries nextItems() on a transient (ambiguous) error and still succeeds", async () => {
|
|
66
|
+
vi.useFakeTimers();
|
|
67
|
+
try {
|
|
68
|
+
const { reconcileProvision } = await import("./e2b.ts");
|
|
69
|
+
let calls = 0;
|
|
70
|
+
let done = false;
|
|
71
|
+
listMock.mockReturnValue({
|
|
72
|
+
get hasNext() {
|
|
73
|
+
return !done;
|
|
74
|
+
},
|
|
75
|
+
nextItems: vi.fn(async () => {
|
|
76
|
+
calls += 1;
|
|
77
|
+
if (calls === 1) throw new Error("fetch failed");
|
|
78
|
+
done = true;
|
|
79
|
+
return [];
|
|
80
|
+
}),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const promise = reconcileProvision("tok");
|
|
84
|
+
await vi.runAllTimersAsync();
|
|
85
|
+
await promise;
|
|
86
|
+
expect(calls).toBe(2);
|
|
87
|
+
} finally {
|
|
88
|
+
vi.useRealTimers();
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("does not swallow a nextItems() result that isn't an array", async () => {
|
|
93
|
+
const { reconcileProvision } = await import("./e2b.ts");
|
|
94
|
+
listMock.mockReturnValue({ hasNext: true, nextItems: vi.fn().mockResolvedValue(undefined) });
|
|
95
|
+
|
|
96
|
+
await expect(reconcileProvision("tok")).rejects.toThrow(/not an array|SandboxInfo/);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("gives up after repeated transient nextItems() failures instead of retrying forever", async () => {
|
|
100
|
+
vi.useFakeTimers();
|
|
101
|
+
try {
|
|
102
|
+
const { reconcileProvision } = await import("./e2b.ts");
|
|
103
|
+
const err = new Error("fetch failed");
|
|
104
|
+
listMock.mockReturnValue({
|
|
105
|
+
hasNext: true,
|
|
106
|
+
nextItems: vi.fn().mockRejectedValue(err),
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
const assertion = expect(reconcileProvision("tok")).rejects.toBe(err);
|
|
110
|
+
await vi.runAllTimersAsync();
|
|
111
|
+
await assertion;
|
|
112
|
+
} finally {
|
|
113
|
+
vi.useRealTimers();
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("propagates a non-retryable kill error immediately", async () => {
|
|
118
|
+
const { reconcileProvision } = await import("./e2b.ts");
|
|
119
|
+
listMock.mockReturnValue(fakePaginator([async () => [{ sandboxId: "a" }]]));
|
|
120
|
+
const err = new Error("permission denied");
|
|
121
|
+
killMock.mockRejectedValue(err);
|
|
122
|
+
|
|
123
|
+
await expect(reconcileProvision("tok")).rejects.toBe(err);
|
|
124
|
+
});
|
|
125
|
+
});
|
package/src/sandbox/e2b.ts
CHANGED
|
@@ -14,7 +14,11 @@ import type {
|
|
|
14
14
|
SourceFiles,
|
|
15
15
|
ReadSourceFilesOptions,
|
|
16
16
|
} from "../types.ts";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
classifyProvisionErrorFallback,
|
|
19
|
+
isRetryableProvisionError,
|
|
20
|
+
type SandboxProvisionErrorKind,
|
|
21
|
+
} from "./errors.ts";
|
|
18
22
|
import { classifySandboxIoError } from "./errors.ts";
|
|
19
23
|
import { readSourceFilesByList } from "./source-files.ts";
|
|
20
24
|
import { collectLocalFiles } from "./local-files.ts";
|
|
@@ -30,6 +34,13 @@ const DEFAULT_COMMAND_TIMEOUT_MS = 600_000;
|
|
|
30
34
|
const SESSION_TIMEOUT_MS = 1_800_000;
|
|
31
35
|
|
|
32
36
|
/** e2b 的限流错误是 SDK 原生的 RateLimitError(HTTP 429 映射而来);见 resolve.ts 的 withProvisionRetry。 */
|
|
37
|
+
// 对账本身只有一次机会:retry.ts 的 withProvisionRetry 对账失败就直接放弃重试、抛回原始
|
|
38
|
+
// create() 错误(见那边的注释)。对账走的这次 list 请求跟刚失败的 create() 往往挨得很近,
|
|
39
|
+
// 大概率处在同一段网络抖动里——不给它自己的重试,一次瞬时失败就会把本可能自愈的 attempt
|
|
40
|
+
// 判死。这里给 nextItems() 单独包一层短重试,只吃与 create() 侧同一套分类下的瞬时错误。
|
|
41
|
+
const RECONCILE_LIST_MAX_ATTEMPTS = 3;
|
|
42
|
+
const RECONCILE_LIST_RETRY_DELAY_MS = 500;
|
|
43
|
+
|
|
33
44
|
/**
|
|
34
45
|
* Provisioning 重试前的对账:按 metadata 里的 provision token 检索远端实例,查到即 kill。
|
|
35
46
|
* 检索或销毁失败必须抛出——对账是重试的硬前置,静默放行等于盲重试,会复制计费实例
|
|
@@ -38,19 +49,43 @@ const SESSION_TIMEOUT_MS = 1_800_000;
|
|
|
38
49
|
*/
|
|
39
50
|
export async function reconcileProvision(token: string): Promise<void> {
|
|
40
51
|
const apiKey = process.env.E2B_API_KEY;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
// Sandbox.list() 是同步方法,返回分页器(SandboxPaginator),不是 Promise<数组>——用
|
|
53
|
+
// hasNext/nextItems() 翻页,不能直接 for...of。metadata 过滤走服务端 query,一次 token
|
|
54
|
+
// 命中的实例数极少,通常一页打完。
|
|
55
|
+
const paginator = E2BSdkSandbox.list({ apiKey, query: { metadata: { "niceeval-provision-token": token } } });
|
|
56
|
+
while (paginator.hasNext) {
|
|
57
|
+
const sandboxes = await fetchNextItemsWithRetry(paginator);
|
|
58
|
+
for (const info of sandboxes) {
|
|
59
|
+
try {
|
|
60
|
+
await E2BSdkSandbox.kill(info.sandboxId, { apiKey });
|
|
61
|
+
} catch (e) {
|
|
62
|
+
if (!(e instanceof NotFoundError)) throw e;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* `nextItems()` 按类型契约总是 resolve 成数组(SDK 内部对空响应也兜了 `?? []`),但对账这条
|
|
70
|
+
* 路径线上真实撞见过它 resolve 成非数组的一次——没能复现出确切成因,不排它,原样让下面的
|
|
71
|
+
* `for...of` 抛出,但换一句能定位的诊断,而不是留一条裸的 "X is not iterable"。
|
|
72
|
+
*/
|
|
73
|
+
async function fetchNextItemsWithRetry(
|
|
74
|
+
paginator: ReturnType<typeof E2BSdkSandbox.list>,
|
|
75
|
+
): Promise<Awaited<ReturnType<typeof paginator.nextItems>>> {
|
|
76
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
50
77
|
try {
|
|
51
|
-
|
|
78
|
+
const items = await paginator.nextItems();
|
|
79
|
+
if (!Array.isArray(items)) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
`e2b Sandbox.list() 分页器 nextItems() 返回了非数组(${typeof items}),不是 SDK 类型契约里的 SandboxInfo[]`,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
return items;
|
|
52
85
|
} catch (e) {
|
|
53
|
-
|
|
86
|
+
const kind = classifyProvisionErrorFallback(e);
|
|
87
|
+
if (attempt >= RECONCILE_LIST_MAX_ATTEMPTS - 1 || !isRetryableProvisionError(kind)) throw e;
|
|
88
|
+
await new Promise((resolve) => setTimeout(resolve, RECONCILE_LIST_RETRY_DELAY_MS * 2 ** attempt));
|
|
54
89
|
}
|
|
55
90
|
}
|
|
56
91
|
}
|