@lesliechan721/aw 0.1.0-beta.7 → 0.1.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authoring/scripts/add-skill.mjs +4 -4
- package/dist/authoring/scripts/add-skill.mjs.map +1 -1
- package/dist/authoring/scripts/init-plugin.mjs +8 -37
- package/dist/authoring/scripts/init-plugin.mjs.map +1 -1
- package/dist/authoring/scripts/lib/plugin-version-check.mjs +1 -1
- package/dist/authoring/scripts/lib/skill-scaffold.d.mts +77 -90
- package/dist/authoring/scripts/lib/skill-scaffold.mjs +99 -120
- package/dist/authoring/scripts/lib/skill-scaffold.mjs.map +1 -1
- package/dist/authoring/scripts/plugin-status.mjs +8 -11
- package/dist/authoring/scripts/plugin-status.mjs.map +1 -1
- package/dist/authoring/scripts/remove-plugin.mjs +2 -1
- package/dist/authoring/scripts/remove-plugin.mjs.map +1 -1
- package/dist/authoring/scripts/validate-plugin.mjs +64 -758
- package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
- package/dist/scripts/aw-skill-eval-materializer.d.ts +24 -0
- package/dist/scripts/{plugin-eval-materializer.js → aw-skill-eval-materializer.js} +34 -145
- package/dist/scripts/aw-skill-eval-materializer.js.map +1 -0
- package/dist/scripts/bundle-plugins.d.ts +1 -0
- package/dist/scripts/bundle-plugins.js +2 -1
- package/dist/scripts/bundle-plugins.js.map +1 -1
- package/dist/scripts/plugin-staging.d.ts +6 -8
- package/dist/scripts/plugin-staging.js +15 -28
- package/dist/scripts/plugin-staging.js.map +1 -1
- package/dist/scripts/test-plugin-scripts.js +2 -33
- package/dist/scripts/test-plugin-scripts.js.map +1 -1
- package/dist/src/adapters/helpers.js +2 -2
- package/dist/src/adapters/helpers.js.map +1 -1
- package/dist/src/authoring/workspace-commands.d.ts +17 -1
- package/dist/src/authoring/workspace-commands.js +21 -4
- package/dist/src/authoring/workspace-commands.js.map +1 -1
- package/dist/src/commands/aw-capabilities.d.ts +78 -0
- package/dist/src/commands/aw-capabilities.js +254 -0
- package/dist/src/commands/aw-capabilities.js.map +1 -0
- package/dist/src/commands/catalog-dev-source.d.ts +118 -0
- package/dist/src/commands/catalog-dev-source.js +417 -0
- package/dist/src/commands/catalog-dev-source.js.map +1 -0
- package/dist/src/commands/catalog-generation.d.ts +13 -0
- package/dist/src/commands/catalog-generation.js +13 -0
- package/dist/src/commands/catalog-generation.js.map +1 -0
- package/dist/src/commands/catalog-publication-inspection.d.ts +29 -28
- package/dist/src/commands/catalog-publication-inspection.js +138 -50
- package/dist/src/commands/catalog-publication-inspection.js.map +1 -1
- package/dist/src/commands/catalog-publication-journal.d.ts +49 -0
- package/dist/src/commands/catalog-publication-journal.js +147 -0
- package/dist/src/commands/catalog-publication-journal.js.map +1 -0
- package/dist/src/commands/catalog-publication-target.d.ts +18 -0
- package/dist/src/commands/catalog-publication-target.js +95 -0
- package/dist/src/commands/catalog-publication-target.js.map +1 -0
- package/dist/src/commands/catalog-publish.d.ts +58 -15
- package/dist/src/commands/catalog-publish.js +590 -210
- package/dist/src/commands/catalog-publish.js.map +1 -1
- package/dist/src/commands/catalog-source.d.ts +12 -15
- package/dist/src/commands/catalog-source.js +61 -86
- package/dist/src/commands/catalog-source.js.map +1 -1
- package/dist/src/commands/catalog-status.d.ts +18 -22
- package/dist/src/commands/catalog-status.js +81 -115
- package/dist/src/commands/catalog-status.js.map +1 -1
- package/dist/src/commands/catalog-workspace-resolution.d.ts +13 -0
- package/dist/src/commands/catalog-workspace-resolution.js +42 -0
- package/dist/src/commands/catalog-workspace-resolution.js.map +1 -0
- package/dist/src/commands/dependency-operation-plan.d.ts +88 -9
- package/dist/src/commands/dependency-operation-plan.js +331 -6
- package/dist/src/commands/dependency-operation-plan.js.map +1 -1
- package/dist/src/commands/init.d.ts +5 -11
- package/dist/src/commands/init.js +30 -51
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/offline-cleanup-evidence.d.ts +43 -0
- package/dist/src/commands/offline-cleanup-evidence.js +116 -0
- package/dist/src/commands/offline-cleanup-evidence.js.map +1 -0
- package/dist/src/commands/options.d.ts +7 -1
- package/dist/src/commands/options.js +65 -6
- package/dist/src/commands/options.js.map +1 -1
- package/dist/src/commands/plugin-capabilities.js +5 -1
- package/dist/src/commands/plugin-capabilities.js.map +1 -1
- package/dist/src/commands/rebuild-command.d.ts +27 -13
- package/dist/src/commands/rebuild-command.js +1472 -471
- package/dist/src/commands/rebuild-command.js.map +1 -1
- package/dist/src/commands/rebuild-integration.d.ts +63 -8
- package/dist/src/commands/rebuild-integration.js +761 -439
- package/dist/src/commands/rebuild-integration.js.map +1 -1
- package/dist/src/commands/rebuild-operations.d.ts +2 -8
- package/dist/src/commands/rebuild-operations.js +48 -132
- package/dist/src/commands/rebuild-operations.js.map +1 -1
- package/dist/src/commands/runtime.js +21 -34
- package/dist/src/commands/runtime.js.map +1 -1
- package/dist/src/commands/skill-eval.d.ts +33 -0
- package/dist/src/commands/skill-eval.js +363 -0
- package/dist/src/commands/skill-eval.js.map +1 -0
- package/dist/src/commands/status-v6.d.ts +202 -0
- package/dist/src/commands/status-v6.js +1938 -0
- package/dist/src/commands/status-v6.js.map +1 -0
- package/dist/src/commands/sync-command.d.ts +21 -0
- package/dist/src/commands/sync-command.js +206 -0
- package/dist/src/commands/sync-command.js.map +1 -0
- package/dist/src/commands/wizard.d.ts +2 -2
- package/dist/src/commands/wizard.js +2 -2
- package/dist/src/commands/wizard.js.map +1 -1
- package/dist/src/commands/write-commands.js +33 -19
- package/dist/src/commands/write-commands.js.map +1 -1
- package/dist/src/constants.d.ts +7 -1
- package/dist/src/constants.js +7 -1
- package/dist/src/constants.js.map +1 -1
- package/dist/src/eval/manifest-v1.d.ts +66 -0
- package/dist/src/eval/manifest-v1.js +326 -0
- package/dist/src/eval/manifest-v1.js.map +1 -0
- package/dist/src/generators/local-plugin.d.ts +3 -0
- package/dist/src/generators/local-plugin.js +167 -45
- package/dist/src/generators/local-plugin.js.map +1 -1
- package/dist/src/index.d.ts +6 -1
- package/dist/src/index.js +863 -126
- package/dist/src/index.js.map +1 -1
- package/dist/src/output/command-receipt.d.ts +58 -8
- package/dist/src/output/command-receipt.js +141 -11
- package/dist/src/output/command-receipt.js.map +1 -1
- package/dist/src/output/model.d.ts +3 -44
- package/dist/src/output/model.js +1 -144
- package/dist/src/output/model.js.map +1 -1
- package/dist/src/output/rebuild-progress.js +0 -3
- package/dist/src/output/rebuild-progress.js.map +1 -1
- package/dist/src/portable/commands.d.ts +6 -2
- package/dist/src/portable/commands.js +57 -6
- package/dist/src/portable/commands.js.map +1 -1
- package/dist/src/portable/materializer.d.ts +4 -4
- package/dist/src/portable/materializer.js +61 -12
- package/dist/src/portable/materializer.js.map +1 -1
- package/dist/src/tui/catalog-generation.d.ts +35 -0
- package/dist/src/tui/catalog-generation.js +85 -0
- package/dist/src/tui/catalog-generation.js.map +1 -0
- package/dist/src/tui/components.js +1 -2
- package/dist/src/tui/components.js.map +1 -1
- package/dist/src/tui/console-app-v4.d.ts +1 -1
- package/dist/src/tui/console-app-v4.js +647 -196
- package/dist/src/tui/console-app-v4.js.map +1 -1
- package/dist/src/tui/controller-runtime.d.ts +6 -2
- package/dist/src/tui/controller-runtime.js +646 -137
- package/dist/src/tui/controller-runtime.js.map +1 -1
- package/dist/src/tui/controller.d.ts +77 -15
- package/dist/src/tui/controller.js +647 -306
- package/dist/src/tui/controller.js.map +1 -1
- package/dist/src/tui/diagnostics.d.ts +1 -2
- package/dist/src/tui/diagnostics.js.map +1 -1
- package/dist/src/tui/index.d.ts +1 -0
- package/dist/src/tui/index.js +1 -0
- package/dist/src/tui/index.js.map +1 -1
- package/dist/src/tui/input-display.d.ts +8 -0
- package/dist/src/tui/input-display.js +36 -0
- package/dist/src/tui/input-display.js.map +1 -0
- package/dist/src/tui/model.d.ts +102 -14
- package/dist/src/tui/model.js +630 -139
- package/dist/src/tui/model.js.map +1 -1
- package/dist/src/tui/router.d.ts +35 -0
- package/dist/src/tui/router.js.map +1 -1
- package/dist/src/tui/source-ref-presets.d.ts +3 -0
- package/dist/src/tui/source-ref-presets.js +11 -0
- package/dist/src/tui/source-ref-presets.js.map +1 -0
- package/dist/src/tui/views.js +31 -3
- package/dist/src/tui/views.js.map +1 -1
- package/dist/src/types.d.ts +104 -13
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils/agent-version.d.ts +7 -1
- package/dist/src/utils/agent-version.js +41 -8
- package/dist/src/utils/agent-version.js.map +1 -1
- package/dist/src/utils/aw-home-mutation-lock.d.ts +1 -0
- package/dist/src/utils/aw-home-mutation-lock.js +32 -4
- package/dist/src/utils/aw-home-mutation-lock.js.map +1 -1
- package/dist/src/utils/bundle-integrity.d.ts +0 -1
- package/dist/src/utils/bundle-integrity.js +0 -25
- package/dist/src/utils/bundle-integrity.js.map +1 -1
- package/dist/src/utils/bundle-layout.js +8 -6
- package/dist/src/utils/bundle-layout.js.map +1 -1
- package/dist/src/utils/catalog-digests.d.ts +7 -0
- package/dist/src/utils/catalog-digests.js +40 -2
- package/dist/src/utils/catalog-digests.js.map +1 -1
- package/dist/src/utils/catalog-generation-control-snapshot.d.ts +11 -0
- package/dist/src/utils/catalog-generation-control-snapshot.js +45 -0
- package/dist/src/utils/catalog-generation-control-snapshot.js.map +1 -0
- package/dist/src/utils/catalog-generation-error.d.ts +13 -0
- package/dist/src/utils/catalog-generation-error.js +28 -0
- package/dist/src/utils/catalog-generation-error.js.map +1 -0
- package/dist/src/utils/catalog-generation-resolver.d.ts +20 -0
- package/dist/src/utils/catalog-generation-resolver.js +143 -0
- package/dist/src/utils/catalog-generation-resolver.js.map +1 -0
- package/dist/src/utils/catalog-generation-service.d.ts +43 -0
- package/dist/src/utils/catalog-generation-service.js +306 -0
- package/dist/src/utils/catalog-generation-service.js.map +1 -0
- package/dist/src/utils/catalog-generation-state.d.ts +17 -0
- package/dist/src/utils/catalog-generation-state.js +168 -0
- package/dist/src/utils/catalog-generation-state.js.map +1 -0
- package/dist/src/utils/catalog-generation-store.d.ts +34 -0
- package/dist/src/utils/catalog-generation-store.js +251 -0
- package/dist/src/utils/catalog-generation-store.js.map +1 -0
- package/dist/src/utils/catalog-manifest-v6.d.ts +5 -0
- package/dist/src/utils/{catalog-manifest-v4.js → catalog-manifest-v6.js} +35 -43
- package/dist/src/utils/catalog-manifest-v6.js.map +1 -0
- package/dist/src/utils/catalog-operation-context.d.ts +20 -0
- package/dist/src/utils/catalog-operation-context.js +141 -0
- package/dist/src/utils/catalog-operation-context.js.map +1 -0
- package/dist/src/utils/catalog-public-error.d.ts +7 -3
- package/dist/src/utils/catalog-public-error.js +21 -8
- package/dist/src/utils/catalog-public-error.js.map +1 -1
- package/dist/src/utils/catalog-runtime-projection.d.ts +3 -0
- package/dist/src/utils/catalog-runtime-projection.js +5 -40
- package/dist/src/utils/catalog-runtime-projection.js.map +1 -1
- package/dist/src/utils/catalog-snapshot.d.ts +5 -3
- package/dist/src/utils/catalog-snapshot.js +11 -3
- package/dist/src/utils/catalog-snapshot.js.map +1 -1
- package/dist/src/utils/catalog-source-identity.d.ts +1 -0
- package/dist/src/utils/catalog-source-identity.js +21 -2
- package/dist/src/utils/catalog-source-identity.js.map +1 -1
- package/dist/src/utils/catalog-source-references.d.ts +7 -0
- package/dist/src/utils/catalog-source-references.js +44 -55
- package/dist/src/utils/catalog-source-references.js.map +1 -1
- package/dist/src/utils/catalog-sources.d.ts +1 -0
- package/dist/src/utils/catalog-sources.js +55 -20
- package/dist/src/utils/catalog-sources.js.map +1 -1
- package/dist/src/utils/codex-config.d.ts +2 -0
- package/dist/src/utils/codex-config.js +26 -5
- package/dist/src/utils/codex-config.js.map +1 -1
- package/dist/src/utils/codex-manager.d.ts +81 -0
- package/dist/src/utils/codex-manager.js +810 -0
- package/dist/src/utils/codex-manager.js.map +1 -0
- package/dist/src/utils/codex-reconciler.d.ts +131 -0
- package/dist/src/utils/codex-reconciler.js +327 -0
- package/dist/src/utils/codex-reconciler.js.map +1 -0
- package/dist/src/utils/codex-shared-native.d.ts +56 -0
- package/dist/src/utils/codex-shared-native.js +87 -0
- package/dist/src/utils/codex-shared-native.js.map +1 -0
- package/dist/src/utils/codex-skill-staging.d.ts +2 -0
- package/dist/src/utils/codex-skill-staging.js.map +1 -1
- package/dist/src/utils/codex-surface-analysis.d.ts +44 -0
- package/dist/src/utils/codex-surface-analysis.js +247 -0
- package/dist/src/utils/codex-surface-analysis.js.map +1 -0
- package/dist/src/utils/dynamic-skill-tree.d.ts +4 -0
- package/dist/src/utils/dynamic-skill-tree.js +35 -5
- package/dist/src/utils/dynamic-skill-tree.js.map +1 -1
- package/dist/src/utils/git-catalog-provider.js +66 -140
- package/dist/src/utils/git-catalog-provider.js.map +1 -1
- package/dist/src/utils/lifecycle-active-store.d.ts +0 -28
- package/dist/src/utils/lifecycle-active-store.js +35 -247
- package/dist/src/utils/lifecycle-active-store.js.map +1 -1
- package/dist/src/utils/manifest.d.ts +1 -3
- package/dist/src/utils/manifest.js +1 -3
- package/dist/src/utils/manifest.js.map +1 -1
- package/dist/src/utils/multi-source-catalog.d.ts +3 -9
- package/dist/src/utils/multi-source-catalog.js +53 -110
- package/dist/src/utils/multi-source-catalog.js.map +1 -1
- package/dist/src/utils/native-cli.d.ts +49 -6
- package/dist/src/utils/native-cli.js +116 -33
- package/dist/src/utils/native-cli.js.map +1 -1
- package/dist/src/utils/orphan-marketplace-cleanup.js +1 -3
- package/dist/src/utils/orphan-marketplace-cleanup.js.map +1 -1
- package/dist/src/utils/ownership.d.ts +10 -11
- package/dist/src/utils/ownership.js +55 -112
- package/dist/src/utils/ownership.js.map +1 -1
- package/dist/src/utils/path-catalog-provider.d.ts +1 -1
- package/dist/src/utils/path-catalog-provider.js +52 -65
- package/dist/src/utils/path-catalog-provider.js.map +1 -1
- package/dist/src/utils/paths.d.ts +0 -1
- package/dist/src/utils/paths.js +0 -1
- package/dist/src/utils/paths.js.map +1 -1
- package/dist/src/utils/plugin-config-write.js +18 -40
- package/dist/src/utils/plugin-config-write.js.map +1 -1
- package/dist/src/utils/plugin-config.d.ts +1 -12
- package/dist/src/utils/plugin-config.js +107 -226
- package/dist/src/utils/plugin-config.js.map +1 -1
- package/dist/src/utils/plugin-intent-state.d.ts +39 -0
- package/dist/src/utils/plugin-intent-state.js +325 -0
- package/dist/src/utils/plugin-intent-state.js.map +1 -0
- package/dist/src/utils/plugin-scripts.d.ts +1 -1
- package/dist/src/utils/plugin-scripts.js +1 -1
- package/dist/src/utils/plugin-scripts.js.map +1 -1
- package/dist/src/utils/state-paths.js +7 -2
- package/dist/src/utils/state-paths.js.map +1 -1
- package/dist/src/utils/target-eligibility.d.ts +8 -2
- package/dist/src/utils/target-eligibility.js +9 -5
- package/dist/src/utils/target-eligibility.js.map +1 -1
- package/package.json +6 -4
- package/dist/authoring/scripts/check-plugin-versions.d.mts +0 -2
- package/dist/authoring/scripts/check-plugin-versions.mjs +0 -5
- package/dist/authoring/scripts/check-plugin-versions.mjs.map +0 -1
- package/dist/scripts/aw-skill-eval-engine.d.ts +0 -13
- package/dist/scripts/aw-skill-eval-engine.js +0 -31
- package/dist/scripts/aw-skill-eval-engine.js.map +0 -1
- package/dist/scripts/codex-eval-app-server.d.ts +0 -89
- package/dist/scripts/codex-eval-app-server.js +0 -1228
- package/dist/scripts/codex-eval-app-server.js.map +0 -1
- package/dist/scripts/codex-eval-isolation.d.ts +0 -61
- package/dist/scripts/codex-eval-isolation.js +0 -318
- package/dist/scripts/codex-eval-isolation.js.map +0 -1
- package/dist/scripts/codex-eval-process-management.d.ts +0 -8
- package/dist/scripts/codex-eval-process-management.js +0 -69
- package/dist/scripts/codex-eval-process-management.js.map +0 -1
- package/dist/scripts/codex-eval-process-tree.d.ts +0 -38
- package/dist/scripts/codex-eval-process-tree.js +0 -803
- package/dist/scripts/codex-eval-process-tree.js.map +0 -1
- package/dist/scripts/codex-eval-runtime-config.d.ts +0 -62
- package/dist/scripts/codex-eval-runtime-config.js +0 -843
- package/dist/scripts/codex-eval-runtime-config.js.map +0 -1
- package/dist/scripts/codex-trigger-eval.d.ts +0 -84
- package/dist/scripts/codex-trigger-eval.js +0 -968
- package/dist/scripts/codex-trigger-eval.js.map +0 -1
- package/dist/scripts/eval-plugin-skills-audited.d.ts +0 -6
- package/dist/scripts/eval-plugin-skills-audited.js +0 -261
- package/dist/scripts/eval-plugin-skills-audited.js.map +0 -1
- package/dist/scripts/eval-plugin-skills.d.ts +0 -45
- package/dist/scripts/eval-plugin-skills.js +0 -1586
- package/dist/scripts/eval-plugin-skills.js.map +0 -1
- package/dist/scripts/eval-run-layout.d.ts +0 -17
- package/dist/scripts/eval-run-layout.js +0 -26
- package/dist/scripts/eval-run-layout.js.map +0 -1
- package/dist/scripts/plugin-eval-contract.d.ts +0 -185
- package/dist/scripts/plugin-eval-contract.js +0 -695
- package/dist/scripts/plugin-eval-contract.js.map +0 -1
- package/dist/scripts/plugin-eval-materializer.d.ts +0 -13
- package/dist/scripts/plugin-eval-materializer.js.map +0 -1
- package/dist/scripts/skill-reporting.d.ts +0 -212
- package/dist/scripts/skill-reporting.js +0 -922
- package/dist/scripts/skill-reporting.js.map +0 -1
- package/dist/src/commands/status-v4.d.ts +0 -123
- package/dist/src/commands/status-v4.js +0 -905
- package/dist/src/commands/status-v4.js.map +0 -1
- package/dist/src/commands/status.d.ts +0 -93
- package/dist/src/commands/status.js +0 -442
- package/dist/src/commands/status.js.map +0 -1
- package/dist/src/utils/active-marketplace-runtime-lock.d.ts +0 -12
- package/dist/src/utils/active-marketplace-runtime-lock.js +0 -133
- package/dist/src/utils/active-marketplace-runtime-lock.js.map +0 -1
- package/dist/src/utils/catalog-manifest-v3.d.ts +0 -2
- package/dist/src/utils/catalog-manifest-v3.js +0 -3
- package/dist/src/utils/catalog-manifest-v3.js.map +0 -1
- package/dist/src/utils/catalog-manifest-v4.d.ts +0 -5
- package/dist/src/utils/catalog-manifest-v4.js.map +0 -1
- package/dist/src/utils/catalog-source-cache.d.ts +0 -62
- package/dist/src/utils/catalog-source-cache.js +0 -296
- package/dist/src/utils/catalog-source-cache.js.map +0 -1
- package/dist/src/utils/codex-project-surface.d.ts +0 -3
- package/dist/src/utils/codex-project-surface.js +0 -49
- package/dist/src/utils/codex-project-surface.js.map +0 -1
- package/dist/src/utils/plugin-release.d.ts +0 -2
- package/dist/src/utils/plugin-release.js +0 -2
- package/dist/src/utils/plugin-release.js.map +0 -1
|
@@ -1,922 +0,0 @@
|
|
|
1
|
-
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
-
import { hostname } from 'node:os';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import fs from 'fs-extra';
|
|
5
|
-
import { acquireProcessLock } from '../src/utils/process-lock.js';
|
|
6
|
-
const BATCH_SCHEMA_VERSION = 1;
|
|
7
|
-
const BATCH_ID_PATTERN = /^\d{8}T\d{6}Z-[a-z0-9](?:[a-z0-9-]{0,47})-[0-9a-f]{8}$/u;
|
|
8
|
-
const RUN_ID_PATTERN = /^\d{8}T\d{6}Z-[0-9a-f]{8}$/u;
|
|
9
|
-
function timestampId(date = new Date()) {
|
|
10
|
-
return date.toISOString().replace(/[-:]/gu, '').replace(/\.\d{3}Z$/u, 'Z');
|
|
11
|
-
}
|
|
12
|
-
function assertSegment(value, label) {
|
|
13
|
-
if (!/^[a-z0-9][a-z0-9-]*$/u.test(value)) {
|
|
14
|
-
throw new Error(`${label} must use lowercase kebab-case.`);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
export function createSkillReportBatchId(name, date = new Date()) {
|
|
18
|
-
assertSegment(name, 'Batch name');
|
|
19
|
-
if (name.length > 48)
|
|
20
|
-
throw new Error('Batch name must contain at most 48 characters.');
|
|
21
|
-
return `${timestampId(date)}-${name}-${randomUUID().slice(0, 8)}`;
|
|
22
|
-
}
|
|
23
|
-
function validateBatchId(value) {
|
|
24
|
-
if (!BATCH_ID_PATTERN.test(value)) {
|
|
25
|
-
throw new Error('--batch-id must use YYYYMMDDTHHMMSSZ-<kebab-name>-xxxxxxxx.');
|
|
26
|
-
}
|
|
27
|
-
return value;
|
|
28
|
-
}
|
|
29
|
-
function createRunId(date = new Date()) {
|
|
30
|
-
return `${timestampId(date)}-${randomUUID().slice(0, 8)}`;
|
|
31
|
-
}
|
|
32
|
-
function validateRunId(value, label = '--run-id') {
|
|
33
|
-
if (!RUN_ID_PATTERN.test(value)) {
|
|
34
|
-
throw new Error(`${label} must use YYYYMMDDTHHMMSSZ-xxxxxxxx.`);
|
|
35
|
-
}
|
|
36
|
-
return value;
|
|
37
|
-
}
|
|
38
|
-
export function resolveSkillEvalRoot(repoRoot, owner) {
|
|
39
|
-
return owner.kind === 'plugin-skill'
|
|
40
|
-
? path.join(repoRoot, '.tmp', 'agent-workflows', 'evals', 'plugin', owner.plugin, owner.skill)
|
|
41
|
-
: path.join(repoRoot, '.tmp', 'agent-workflows', 'evals', 'static', owner.skill);
|
|
42
|
-
}
|
|
43
|
-
async function assertSafeSkillReportPath(repoRoot, candidatePath) {
|
|
44
|
-
const root = path.resolve(repoRoot);
|
|
45
|
-
const candidate = path.resolve(candidatePath);
|
|
46
|
-
const relative = path.relative(root, candidate);
|
|
47
|
-
if (relative === '' ||
|
|
48
|
-
relative === '..' ||
|
|
49
|
-
relative.startsWith(`..${path.sep}`) ||
|
|
50
|
-
path.isAbsolute(relative)) {
|
|
51
|
-
throw new Error(`Skill report path must stay inside the repository: ${candidate}.`);
|
|
52
|
-
}
|
|
53
|
-
let current = root;
|
|
54
|
-
for (const segment of relative.split(path.sep)) {
|
|
55
|
-
current = path.join(current, segment);
|
|
56
|
-
const stats = await fs.lstat(current).catch((error) => {
|
|
57
|
-
if (error.code === 'ENOENT')
|
|
58
|
-
return null;
|
|
59
|
-
throw error;
|
|
60
|
-
});
|
|
61
|
-
if (stats?.isSymbolicLink()) {
|
|
62
|
-
throw new Error(`Skill report path cannot contain a symbolic link: ${current}.`);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
function ownerKey(owner) {
|
|
67
|
-
return owner.kind === 'plugin-skill'
|
|
68
|
-
? `plugin:${owner.plugin}:${owner.skill}`
|
|
69
|
-
: `static:${owner.skill}`;
|
|
70
|
-
}
|
|
71
|
-
function sameOwner(left, right) {
|
|
72
|
-
return ownerKey(left) === ownerKey(right);
|
|
73
|
-
}
|
|
74
|
-
async function writeJsonAtomic(filePath, value) {
|
|
75
|
-
await fs.ensureDir(path.dirname(filePath));
|
|
76
|
-
const temporary = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
77
|
-
await fs.writeJson(temporary, value, { spaces: 2 });
|
|
78
|
-
await fs.rename(temporary, filePath);
|
|
79
|
-
}
|
|
80
|
-
async function writeTextAtomic(filePath, value) {
|
|
81
|
-
await fs.ensureDir(path.dirname(filePath));
|
|
82
|
-
const temporary = `${filePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
83
|
-
await fs.writeFile(temporary, value, 'utf8');
|
|
84
|
-
await fs.rename(temporary, filePath);
|
|
85
|
-
}
|
|
86
|
-
async function acquireBatchLock(batchRoot) {
|
|
87
|
-
const lease = await acquireProcessLock({
|
|
88
|
-
lockPath: path.join(batchRoot, '.batch.lock'),
|
|
89
|
-
timeoutMs: 30_000,
|
|
90
|
-
pollMs: 50,
|
|
91
|
-
});
|
|
92
|
-
return () => lease.release();
|
|
93
|
-
}
|
|
94
|
-
async function withBatchLock(batchRoot, action) {
|
|
95
|
-
const release = await acquireBatchLock(batchRoot);
|
|
96
|
-
try {
|
|
97
|
-
return await action();
|
|
98
|
-
}
|
|
99
|
-
finally {
|
|
100
|
-
await release();
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
async function readBatch(batchRoot) {
|
|
104
|
-
const batch = await fs.readJson(path.join(batchRoot, 'batch.json'));
|
|
105
|
-
if (batch.schemaVersion !== BATCH_SCHEMA_VERSION) {
|
|
106
|
-
throw new Error(`Unsupported report batch schema: ${batch.schemaVersion}.`);
|
|
107
|
-
}
|
|
108
|
-
return batch;
|
|
109
|
-
}
|
|
110
|
-
function batchNameFromId(batchId) {
|
|
111
|
-
return batchId.replace(/^\d{8}T\d{6}Z-/u, '').replace(/-[0-9a-f]{8}$/u, '');
|
|
112
|
-
}
|
|
113
|
-
export async function createSkillEvalRunLayout(options) {
|
|
114
|
-
if ((options.batchName ? 1 : 0) + (options.batchId ? 1 : 0) !== 1) {
|
|
115
|
-
throw new Error('Provide exactly one of --batch-name or --batch-id.');
|
|
116
|
-
}
|
|
117
|
-
if (options.owner.kind === 'plugin-skill')
|
|
118
|
-
assertSegment(options.owner.plugin, 'Plugin name');
|
|
119
|
-
assertSegment(options.owner.skill, 'Skill name');
|
|
120
|
-
const requiredTargets = [...new Set(options.requiredTargets)].sort();
|
|
121
|
-
const targetCompositions = normalizeTargetCompositions(options.targetCompositions, requiredTargets);
|
|
122
|
-
const batchId = options.batchId
|
|
123
|
-
? validateBatchId(options.batchId)
|
|
124
|
-
: createSkillReportBatchId(options.batchName);
|
|
125
|
-
const runId = validateRunId(options.runId ?? createRunId());
|
|
126
|
-
const reportRootBase = resolveSkillEvalRoot(options.repoRoot, options.owner);
|
|
127
|
-
const batchRoot = path.join(reportRootBase, batchId);
|
|
128
|
-
await assertSafeSkillReportPath(options.repoRoot, batchRoot);
|
|
129
|
-
const now = new Date().toISOString();
|
|
130
|
-
if (options.batchName) {
|
|
131
|
-
await fs.ensureDir(reportRootBase);
|
|
132
|
-
await assertSafeSkillReportPath(options.repoRoot, reportRootBase);
|
|
133
|
-
await fs.mkdir(batchRoot);
|
|
134
|
-
await assertSafeSkillReportPath(options.repoRoot, batchRoot);
|
|
135
|
-
const batch = {
|
|
136
|
-
schemaVersion: BATCH_SCHEMA_VERSION,
|
|
137
|
-
batchId,
|
|
138
|
-
name: options.batchName,
|
|
139
|
-
owner: options.owner,
|
|
140
|
-
createdAt: now,
|
|
141
|
-
updatedAt: now,
|
|
142
|
-
source: options.source,
|
|
143
|
-
environmentDigest: options.environmentDigest,
|
|
144
|
-
modelEnvironmentDigest: options.modelEnvironmentDigest ?? null,
|
|
145
|
-
harnessDigest: options.harnessDigest,
|
|
146
|
-
requiredTargets,
|
|
147
|
-
targetCompositions,
|
|
148
|
-
};
|
|
149
|
-
await writeJsonAtomic(path.join(batchRoot, 'batch.json'), batch);
|
|
150
|
-
}
|
|
151
|
-
else if (!(await fs.pathExists(batchRoot))) {
|
|
152
|
-
throw new Error(`Report batch does not exist: ${batchId}.`);
|
|
153
|
-
}
|
|
154
|
-
const reportRoot = path.join(batchRoot, 'evals', 'runs', runId);
|
|
155
|
-
const workRoot = path.join(batchRoot, 'work', runId);
|
|
156
|
-
const startedAt = new Date().toISOString();
|
|
157
|
-
const host = hostname();
|
|
158
|
-
await withBatchLock(batchRoot, async () => {
|
|
159
|
-
const batch = await readBatch(batchRoot);
|
|
160
|
-
await recoverInterruptedRunsUnlocked(batchRoot);
|
|
161
|
-
if (!sameOwner(batch.owner, options.owner)) {
|
|
162
|
-
throw new Error(`Report batch ${batchId} belongs to another Skill.`);
|
|
163
|
-
}
|
|
164
|
-
if (batch.source.digest !== options.source.digest) {
|
|
165
|
-
throw new Error(`Report batch ${batchId} uses a different source digest.`);
|
|
166
|
-
}
|
|
167
|
-
if (batch.environmentDigest !== options.environmentDigest) {
|
|
168
|
-
throw new Error(`Report batch ${batchId} uses a different materialization environment.`);
|
|
169
|
-
}
|
|
170
|
-
if (!batch.harnessDigest || batch.harnessDigest !== options.harnessDigest) {
|
|
171
|
-
throw new Error(`Report batch ${batchId} uses a different eval harness.`);
|
|
172
|
-
}
|
|
173
|
-
const modelEnvironmentDigest = options.modelEnvironmentDigest ?? null;
|
|
174
|
-
if (modelEnvironmentDigest &&
|
|
175
|
-
batch.modelEnvironmentDigest &&
|
|
176
|
-
batch.modelEnvironmentDigest !== modelEnvironmentDigest) {
|
|
177
|
-
throw new Error(`Report batch ${batchId} uses a different model environment.`);
|
|
178
|
-
}
|
|
179
|
-
if (JSON.stringify(batch.requiredTargets) !== JSON.stringify(requiredTargets)) {
|
|
180
|
-
throw new Error(`Report batch ${batchId} uses a different release target contract.`);
|
|
181
|
-
}
|
|
182
|
-
const batchTargetCompositions = normalizeTargetCompositions(batch.targetCompositions, batch.requiredTargets);
|
|
183
|
-
if (JSON.stringify(batchTargetCompositions) !== JSON.stringify(targetCompositions)) {
|
|
184
|
-
throw new Error(`Report batch ${batchId} uses a different release composition contract.`);
|
|
185
|
-
}
|
|
186
|
-
await fs.ensureDir(path.dirname(reportRoot));
|
|
187
|
-
await fs.mkdir(reportRoot, { recursive: false });
|
|
188
|
-
try {
|
|
189
|
-
await fs.mkdir(path.join(reportRoot, 'raw'));
|
|
190
|
-
await fs.ensureDir(workRoot);
|
|
191
|
-
await writeJsonAtomic(path.join(reportRoot, 'run.json'), {
|
|
192
|
-
schemaVersion: 1,
|
|
193
|
-
runId,
|
|
194
|
-
status: 'running',
|
|
195
|
-
startedAt,
|
|
196
|
-
pid: process.pid,
|
|
197
|
-
host,
|
|
198
|
-
modelEnvironmentDigest,
|
|
199
|
-
});
|
|
200
|
-
if (modelEnvironmentDigest && !batch.modelEnvironmentDigest) {
|
|
201
|
-
batch.modelEnvironmentDigest = modelEnvironmentDigest;
|
|
202
|
-
batch.updatedAt = startedAt;
|
|
203
|
-
await writeJsonAtomic(path.join(batchRoot, 'batch.json'), batch);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
catch (error) {
|
|
207
|
-
await Promise.all([fs.remove(reportRoot), fs.remove(workRoot)]);
|
|
208
|
-
throw error;
|
|
209
|
-
}
|
|
210
|
-
});
|
|
211
|
-
return {
|
|
212
|
-
batchId,
|
|
213
|
-
batchRoot,
|
|
214
|
-
reportBoundaryRoot: options.repoRoot,
|
|
215
|
-
runId,
|
|
216
|
-
workRoot,
|
|
217
|
-
reportRoot,
|
|
218
|
-
rawRoot: path.join(reportRoot, 'raw'),
|
|
219
|
-
startedAt,
|
|
220
|
-
pid: process.pid,
|
|
221
|
-
host,
|
|
222
|
-
cleanupWork: () => fs.remove(workRoot),
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
async function listSourceFiles(root) {
|
|
226
|
-
const files = [];
|
|
227
|
-
async function visit(current, prefix = '') {
|
|
228
|
-
const entries = await fs.readdir(current, { withFileTypes: true });
|
|
229
|
-
entries.sort((left, right) => left.name.localeCompare(right.name));
|
|
230
|
-
const isSkillRoot = entries.some((entry) => entry.isFile() && entry.name === 'SKILL.md');
|
|
231
|
-
for (const entry of entries) {
|
|
232
|
-
if (entry.name === '.DS_Store')
|
|
233
|
-
continue;
|
|
234
|
-
const relative = prefix ? path.posix.join(prefix, entry.name) : entry.name;
|
|
235
|
-
if ((prefix === '' && entry.name === 'reports') ||
|
|
236
|
-
(isSkillRoot && entry.isDirectory() && ['evals', 'reports'].includes(entry.name)))
|
|
237
|
-
continue;
|
|
238
|
-
const absolute = path.join(current, entry.name);
|
|
239
|
-
if (entry.isSymbolicLink()) {
|
|
240
|
-
throw new Error(`Author source cannot contain a symlink: ${absolute}`);
|
|
241
|
-
}
|
|
242
|
-
if (entry.isDirectory())
|
|
243
|
-
await visit(absolute, relative);
|
|
244
|
-
else if (entry.isFile())
|
|
245
|
-
files.push(relative);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
await visit(root);
|
|
249
|
-
return files;
|
|
250
|
-
}
|
|
251
|
-
export async function computeAuthorSourceDigest(roots) {
|
|
252
|
-
const records = [];
|
|
253
|
-
for (const entry of roots) {
|
|
254
|
-
for (const relative of await listSourceFiles(entry.root)) {
|
|
255
|
-
const sourcePath = path.join(entry.root, relative);
|
|
256
|
-
const [contents, sourceStat] = await Promise.all([fs.readFile(sourcePath), fs.stat(sourcePath)]);
|
|
257
|
-
records.push({
|
|
258
|
-
path: `${entry.name}/${relative}`,
|
|
259
|
-
mode: process.platform !== 'win32' && (sourceStat.mode & 0o111) !== 0
|
|
260
|
-
? '100755'
|
|
261
|
-
: '100644',
|
|
262
|
-
contents,
|
|
263
|
-
});
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
const hash = createHash('sha256');
|
|
267
|
-
function updateLengthPrefixed(value) {
|
|
268
|
-
const bytes = typeof value === 'string' ? Buffer.from(value, 'utf8') : value;
|
|
269
|
-
const length = Buffer.allocUnsafe(8);
|
|
270
|
-
length.writeBigUInt64BE(BigInt(bytes.length));
|
|
271
|
-
hash.update(length);
|
|
272
|
-
hash.update(bytes);
|
|
273
|
-
}
|
|
274
|
-
updateLengthPrefixed('aw-production-records-v1');
|
|
275
|
-
for (const record of records.sort((left, right) => left.path < right.path ? -1 : left.path > right.path ? 1 : 0)) {
|
|
276
|
-
updateLengthPrefixed(record.path);
|
|
277
|
-
updateLengthPrefixed(record.mode);
|
|
278
|
-
updateLengthPrefixed(createHash('sha256').update(record.contents).digest());
|
|
279
|
-
}
|
|
280
|
-
return `sha256:${hash.digest('hex')}`;
|
|
281
|
-
}
|
|
282
|
-
export function createEnvironmentDigest(value) {
|
|
283
|
-
return `sha256:${createHash('sha256').update(JSON.stringify(value)).digest('hex')}`;
|
|
284
|
-
}
|
|
285
|
-
export function pluginReleaseTargets(manifest) {
|
|
286
|
-
const targets = manifest.profiles
|
|
287
|
-
.filter((profile) => profile.requiredForRelease)
|
|
288
|
-
.map((profile) => `materialization/${profile.id}`);
|
|
289
|
-
for (const suite of manifest.suites.filter((entry) => entry.requiredForRelease)) {
|
|
290
|
-
for (const profile of suite.profiles) {
|
|
291
|
-
for (const split of suite.releaseSplits.length > 0 ? suite.releaseSplits : ['all']) {
|
|
292
|
-
targets.push(`${suite.kind}/${suite.id}/${profile}/${split}/all`);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
return [...new Set(targets)].sort();
|
|
297
|
-
}
|
|
298
|
-
export function staticReleaseTargets(manifest) {
|
|
299
|
-
const targets = ['materialization/project-default'];
|
|
300
|
-
for (const suite of manifest.suites.filter((entry) => entry.requiredForRelease)) {
|
|
301
|
-
for (const split of suite.releaseSplits.length > 0 ? suite.releaseSplits : ['all']) {
|
|
302
|
-
targets.push(`${suite.kind}/${suite.id}/project-default/${split}/all`);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
return targets.sort();
|
|
306
|
-
}
|
|
307
|
-
export function validateSkillReportTargetSegment(value, label) {
|
|
308
|
-
if (value.length === 0 ||
|
|
309
|
-
value === '.' ||
|
|
310
|
-
value === '..' ||
|
|
311
|
-
value.includes('/') ||
|
|
312
|
-
value.includes('\\') ||
|
|
313
|
-
value.includes('\0') ||
|
|
314
|
-
path.isAbsolute(value) ||
|
|
315
|
-
path.win32.isAbsolute(value)) {
|
|
316
|
-
throw new Error(`${label} must be one safe report path segment.`);
|
|
317
|
-
}
|
|
318
|
-
return value;
|
|
319
|
-
}
|
|
320
|
-
function validateTarget(value, label) {
|
|
321
|
-
const parts = value.split('/');
|
|
322
|
-
if (parts.length === 0)
|
|
323
|
-
throw new Error(`${label} must be a report target.`);
|
|
324
|
-
return parts.map((part, index) => validateSkillReportTargetSegment(part, `${label} segment ${index + 1}`)).join('/');
|
|
325
|
-
}
|
|
326
|
-
function normalizeTargetCompositions(value, requiredTargets) {
|
|
327
|
-
const required = new Set(requiredTargets);
|
|
328
|
-
const normalized = {};
|
|
329
|
-
for (const target of Object.keys(value ?? {}).sort()) {
|
|
330
|
-
const normalizedTarget = validateTarget(target, 'Composition target');
|
|
331
|
-
if (!required.has(normalizedTarget)) {
|
|
332
|
-
throw new Error(`Composition target is not required for release: ${normalizedTarget}.`);
|
|
333
|
-
}
|
|
334
|
-
const components = value?.[target];
|
|
335
|
-
if (!Array.isArray(components) || components.length === 0) {
|
|
336
|
-
throw new Error(`Composition target ${normalizedTarget} must have components.`);
|
|
337
|
-
}
|
|
338
|
-
const normalizedComponents = [...new Set(components.map((component, index) => validateTarget(component, `Composition target ${normalizedTarget} component ${index + 1}`)))].sort();
|
|
339
|
-
if (normalizedComponents.includes(normalizedTarget)) {
|
|
340
|
-
throw new Error(`Composition target cannot include itself: ${normalizedTarget}.`);
|
|
341
|
-
}
|
|
342
|
-
normalized[normalizedTarget] = normalizedComponents;
|
|
343
|
-
}
|
|
344
|
-
return normalized;
|
|
345
|
-
}
|
|
346
|
-
export function createSkillEvalTargetKey(parts) {
|
|
347
|
-
return parts.map((part, index) => validateSkillReportTargetSegment(part || 'all', `Eval target segment ${index + 1}`)).join('/');
|
|
348
|
-
}
|
|
349
|
-
const targetKey = createSkillEvalTargetKey;
|
|
350
|
-
async function writeTargetReport(options) {
|
|
351
|
-
const evalRoot = path.resolve(options.batchRoot, 'evals');
|
|
352
|
-
const parent = path.resolve(evalRoot, options.summary.target);
|
|
353
|
-
const relativeParent = path.relative(evalRoot, parent);
|
|
354
|
-
if (relativeParent === '..' ||
|
|
355
|
-
relativeParent.startsWith(`..${path.sep}`) ||
|
|
356
|
-
path.isAbsolute(relativeParent)) {
|
|
357
|
-
throw new Error(`Eval target escapes the report batch: ${options.summary.target}.`);
|
|
358
|
-
}
|
|
359
|
-
if (await fs.pathExists(parent)) {
|
|
360
|
-
const existing = await fs.readdir(parent);
|
|
361
|
-
if (!options.allowExistingMaterialization && !options.allowDuplicate && existing.length > 0) {
|
|
362
|
-
throw new Error(`Eval target already exists in this batch: ${options.summary.target}.`);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
const root = path.join(parent, options.summary.runId);
|
|
366
|
-
if (await fs.pathExists(root)) {
|
|
367
|
-
if (options.allowExistingMaterialization) {
|
|
368
|
-
const current = await fs.readJson(path.join(root, 'summary.json'));
|
|
369
|
-
if (current.kind === 'materialization' &&
|
|
370
|
-
options.summary.kind === 'materialization' &&
|
|
371
|
-
current.target === options.summary.target &&
|
|
372
|
-
current.profile === options.summary.profile &&
|
|
373
|
-
stableJson(current.summary) === stableJson(options.summary.summary))
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
throw new Error(`Eval target attempt already exists for run ${options.summary.runId}: ` +
|
|
377
|
-
options.summary.target);
|
|
378
|
-
}
|
|
379
|
-
const markdown = [
|
|
380
|
-
'# Eval Target Report',
|
|
381
|
-
'',
|
|
382
|
-
`- Target: \`${options.summary.target}\``,
|
|
383
|
-
`- Run: \`${options.summary.runId}\``,
|
|
384
|
-
`- Outcome: \`${options.summary.outcome}\``,
|
|
385
|
-
`- Complete: \`${options.summary.complete}\``,
|
|
386
|
-
`- Mechanical passed: \`${options.summary.mechanicalPassed}\``,
|
|
387
|
-
`- Required for release: \`${options.summary.requiredForRelease}\``,
|
|
388
|
-
'',
|
|
389
|
-
options.summary.reportPath
|
|
390
|
-
? `原始目标报告位于运行目录的 \`${options.summary.reportPath}\`。`
|
|
391
|
-
: '该目标没有独立原始报告。',
|
|
392
|
-
'',
|
|
393
|
-
].join('\n');
|
|
394
|
-
await fs.ensureDir(parent);
|
|
395
|
-
const staging = `${root}.${process.pid}.${randomUUID()}.tmp`;
|
|
396
|
-
await fs.mkdir(staging);
|
|
397
|
-
try {
|
|
398
|
-
await writeJsonAtomic(path.join(staging, 'summary.json'), options.summary);
|
|
399
|
-
await writeTextAtomic(path.join(staging, 'report.md'), markdown);
|
|
400
|
-
await fs.rename(staging, root);
|
|
401
|
-
}
|
|
402
|
-
catch (error) {
|
|
403
|
-
await fs.remove(staging);
|
|
404
|
-
throw error;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
async function assertSuiteTargetsAvailable(options) {
|
|
408
|
-
const targets = options.evaluation.suites.flatMap((suite) => {
|
|
409
|
-
const base = [
|
|
410
|
-
suite.kind,
|
|
411
|
-
suite.id,
|
|
412
|
-
suite.profile ?? 'project-default',
|
|
413
|
-
suite.split ?? 'all',
|
|
414
|
-
];
|
|
415
|
-
return [
|
|
416
|
-
targetKey([...base, options.caseId ?? 'all']),
|
|
417
|
-
...(options.caseId
|
|
418
|
-
? []
|
|
419
|
-
: (suite.caseResults ?? []).map((caseResult) => targetKey([...base, caseResult.id]))),
|
|
420
|
-
];
|
|
421
|
-
});
|
|
422
|
-
if (new Set(targets).size !== targets.length) {
|
|
423
|
-
throw new Error('An eval run cannot publish the same suite target more than once.');
|
|
424
|
-
}
|
|
425
|
-
if (options.allowDuplicateTargets)
|
|
426
|
-
return new Set(targets);
|
|
427
|
-
const attempts = await collectTargetSummaries(options.batchRoot);
|
|
428
|
-
const latestByTarget = latestTargetAttempts(attempts);
|
|
429
|
-
const retryable = new Set();
|
|
430
|
-
for (const target of targets) {
|
|
431
|
-
const existing = latestByTarget.get(target);
|
|
432
|
-
if (!existing)
|
|
433
|
-
continue;
|
|
434
|
-
if (!existing.complete || existing.outcome === 'incomplete') {
|
|
435
|
-
retryable.add(target);
|
|
436
|
-
continue;
|
|
437
|
-
}
|
|
438
|
-
throw new Error(`Eval target already has terminal evidence in this batch: ${target}.`);
|
|
439
|
-
}
|
|
440
|
-
return retryable;
|
|
441
|
-
}
|
|
442
|
-
async function collectTargetSummaries(batchRoot) {
|
|
443
|
-
const evalRoot = path.join(batchRoot, 'evals');
|
|
444
|
-
if (!(await fs.pathExists(evalRoot)))
|
|
445
|
-
return [];
|
|
446
|
-
const summaries = [];
|
|
447
|
-
async function visit(current, relative = '') {
|
|
448
|
-
const entries = await fs.readdir(current, { withFileTypes: true });
|
|
449
|
-
for (const entry of entries) {
|
|
450
|
-
const nextRelative = relative ? path.posix.join(relative, entry.name) : entry.name;
|
|
451
|
-
if (nextRelative === 'runs' || nextRelative.startsWith('runs/'))
|
|
452
|
-
continue;
|
|
453
|
-
const absolute = path.join(current, entry.name);
|
|
454
|
-
if (entry.isDirectory() && entry.name.endsWith('.tmp'))
|
|
455
|
-
continue;
|
|
456
|
-
if (entry.isDirectory())
|
|
457
|
-
await visit(absolute, nextRelative);
|
|
458
|
-
else if (entry.isFile() && entry.name === 'summary.json') {
|
|
459
|
-
summaries.push(await fs.readJson(absolute));
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
await visit(evalRoot);
|
|
464
|
-
return summaries.sort((left, right) => left.target.localeCompare(right.target));
|
|
465
|
-
}
|
|
466
|
-
function compareTargetAttempts(left, right) {
|
|
467
|
-
return left.generatedAt.localeCompare(right.generatedAt) || left.runId.localeCompare(right.runId);
|
|
468
|
-
}
|
|
469
|
-
function latestTargetAttempts(attempts) {
|
|
470
|
-
const latest = new Map();
|
|
471
|
-
for (const attempt of attempts) {
|
|
472
|
-
const current = latest.get(attempt.target);
|
|
473
|
-
if (!current || compareTargetAttempts(current, attempt) < 0) {
|
|
474
|
-
latest.set(attempt.target, attempt);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
return latest;
|
|
478
|
-
}
|
|
479
|
-
function composedTargetSummary(target, components) {
|
|
480
|
-
const latest = [...components].sort(compareTargetAttempts).at(-1);
|
|
481
|
-
const outcomes = components.map((component) => component.outcome);
|
|
482
|
-
const qualitativeReview = components.some((component) => component.qualitativeReview === 'required')
|
|
483
|
-
? 'required'
|
|
484
|
-
: 'not_required';
|
|
485
|
-
return {
|
|
486
|
-
schemaVersion: 1,
|
|
487
|
-
target,
|
|
488
|
-
kind: latest.kind,
|
|
489
|
-
runId: latest.runId,
|
|
490
|
-
generatedAt: latest.generatedAt,
|
|
491
|
-
profile: latest.profile,
|
|
492
|
-
suite: latest.suite,
|
|
493
|
-
split: latest.split,
|
|
494
|
-
case: 'all',
|
|
495
|
-
outcome: combineOutcomes(outcomes),
|
|
496
|
-
complete: components.every((component) => component.complete),
|
|
497
|
-
mechanicalPassed: components.every((component) => component.mechanicalPassed),
|
|
498
|
-
qualitativeReview,
|
|
499
|
-
requiredForRelease: true,
|
|
500
|
-
reportPath: null,
|
|
501
|
-
summary: {
|
|
502
|
-
composition: components.map((component) => ({
|
|
503
|
-
target: component.target,
|
|
504
|
-
runId: component.runId,
|
|
505
|
-
generatedAt: component.generatedAt,
|
|
506
|
-
})),
|
|
507
|
-
},
|
|
508
|
-
diagnostics: components.flatMap((component) => component.diagnostics),
|
|
509
|
-
};
|
|
510
|
-
}
|
|
511
|
-
function effectiveTargetSummaries(attempts, compositions) {
|
|
512
|
-
const effective = latestTargetAttempts(attempts);
|
|
513
|
-
for (const [target, componentTargets] of Object.entries(compositions)) {
|
|
514
|
-
const direct = effective.get(target);
|
|
515
|
-
if (direct?.complete && direct.outcome !== 'incomplete')
|
|
516
|
-
continue;
|
|
517
|
-
const components = componentTargets.map((component) => effective.get(component));
|
|
518
|
-
if (components.some((component) => !component))
|
|
519
|
-
continue;
|
|
520
|
-
effective.set(target, composedTargetSummary(target, components));
|
|
521
|
-
}
|
|
522
|
-
return [...effective.values()].sort((left, right) => left.target.localeCompare(right.target));
|
|
523
|
-
}
|
|
524
|
-
function materializationSummary(subject) {
|
|
525
|
-
return {
|
|
526
|
-
scope: subject.scope,
|
|
527
|
-
...(subject.configDigest ? { configDigest: subject.configDigest } : {}),
|
|
528
|
-
...(subject.bundleDigest ? { bundleDigest: subject.bundleDigest } : {}),
|
|
529
|
-
...(subject.contentDigest ? { contentDigest: subject.contentDigest } : {}),
|
|
530
|
-
artifacts: [...(subject.artifacts ?? [])].sort((left, right) => `${left.kind}:${left.key ?? ''}:${left.contentDigest}`.localeCompare(`${right.kind}:${right.key ?? ''}:${right.contentDigest}`)),
|
|
531
|
-
};
|
|
532
|
-
}
|
|
533
|
-
function stableJson(value) {
|
|
534
|
-
if (Array.isArray(value))
|
|
535
|
-
return `[${value.map(stableJson).join(',')}]`;
|
|
536
|
-
if (value && typeof value === 'object') {
|
|
537
|
-
const row = value;
|
|
538
|
-
return `{${Object.keys(row).sort().map((key) => `${JSON.stringify(key)}:${stableJson(row[key])}`).join(',')}}`;
|
|
539
|
-
}
|
|
540
|
-
return JSON.stringify(value) ?? 'undefined';
|
|
541
|
-
}
|
|
542
|
-
async function assertMaterializedSubjectsCompatible(batchRoot, subjects) {
|
|
543
|
-
const existing = latestTargetAttempts(await collectTargetSummaries(batchRoot));
|
|
544
|
-
for (const subject of subjects) {
|
|
545
|
-
const target = targetKey(['materialization', subject.profile]);
|
|
546
|
-
const previous = existing.get(target);
|
|
547
|
-
if (!previous)
|
|
548
|
-
continue;
|
|
549
|
-
const currentIdentity = materializationSummary(subject);
|
|
550
|
-
if (stableJson(previous.summary) !== stableJson(currentIdentity)) {
|
|
551
|
-
throw new Error(`Materialized subject differs from earlier batch evidence: ${target}.`);
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
function combineOutcomes(values) {
|
|
556
|
-
if (values.length === 0)
|
|
557
|
-
return 'not_run';
|
|
558
|
-
if (values.includes('failed'))
|
|
559
|
-
return 'failed';
|
|
560
|
-
if (values.includes('incomplete'))
|
|
561
|
-
return 'incomplete';
|
|
562
|
-
if (values.includes('needs_review'))
|
|
563
|
-
return 'needs_review';
|
|
564
|
-
return 'passed';
|
|
565
|
-
}
|
|
566
|
-
function isProcessAlive(pid) {
|
|
567
|
-
try {
|
|
568
|
-
process.kill(pid, 0);
|
|
569
|
-
return true;
|
|
570
|
-
}
|
|
571
|
-
catch (error) {
|
|
572
|
-
return error.code === 'EPERM';
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
async function recoverInterruptedRunsUnlocked(batchRoot) {
|
|
576
|
-
const runsRoot = path.join(batchRoot, 'evals', 'runs');
|
|
577
|
-
if (!(await fs.pathExists(runsRoot)))
|
|
578
|
-
return;
|
|
579
|
-
const currentHost = hostname();
|
|
580
|
-
for (const runId of await fs.readdir(runsRoot)) {
|
|
581
|
-
const runPath = path.join(runsRoot, runId, 'run.json');
|
|
582
|
-
if (!(await fs.pathExists(runPath)))
|
|
583
|
-
continue;
|
|
584
|
-
const run = await fs.readJson(runPath);
|
|
585
|
-
if (run.status !== 'running' ||
|
|
586
|
-
run.host !== currentHost ||
|
|
587
|
-
!Number.isInteger(run.pid) ||
|
|
588
|
-
isProcessAlive(run.pid))
|
|
589
|
-
continue;
|
|
590
|
-
await writeJsonAtomic(runPath, {
|
|
591
|
-
...run,
|
|
592
|
-
status: 'failed',
|
|
593
|
-
completedAt: new Date().toISOString(),
|
|
594
|
-
outcome: 'failed',
|
|
595
|
-
failureReason: 'The eval process ended before it published a terminal result.',
|
|
596
|
-
});
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
async function readRunSummaries(batchRoot) {
|
|
600
|
-
const runsRoot = path.join(batchRoot, 'evals', 'runs');
|
|
601
|
-
if (!(await fs.pathExists(runsRoot)))
|
|
602
|
-
return [];
|
|
603
|
-
const values = [];
|
|
604
|
-
for (const runId of await fs.readdir(runsRoot)) {
|
|
605
|
-
const runPath = path.join(runsRoot, runId, 'run.json');
|
|
606
|
-
if (!(await fs.pathExists(runPath)))
|
|
607
|
-
continue;
|
|
608
|
-
const run = await fs.readJson(runPath);
|
|
609
|
-
const summaryPath = path.join(runsRoot, runId, 'summary.json');
|
|
610
|
-
const summary = await fs.pathExists(summaryPath)
|
|
611
|
-
? await fs.readJson(summaryPath)
|
|
612
|
-
: null;
|
|
613
|
-
const status = run.status ?? (run.completedAt ? 'completed' : 'running');
|
|
614
|
-
values.push({
|
|
615
|
-
runId,
|
|
616
|
-
status,
|
|
617
|
-
completedAt: run.completedAt ?? null,
|
|
618
|
-
outcome: status === 'failed' ? 'failed' : summary?.outcome ?? run.outcome ?? null,
|
|
619
|
-
complete: status === 'completed' && summary?.complete === true,
|
|
620
|
-
mechanicalPassed: status === 'completed' && summary?.mechanicalPassed === true,
|
|
621
|
-
reportPath: `evals/runs/${runId}/report.md`,
|
|
622
|
-
});
|
|
623
|
-
}
|
|
624
|
-
return values.sort((left, right) => left.runId.localeCompare(right.runId));
|
|
625
|
-
}
|
|
626
|
-
async function refreshBatchSummary(batchRoot) {
|
|
627
|
-
const batch = await readBatch(batchRoot);
|
|
628
|
-
const targetAttempts = await collectTargetSummaries(batchRoot);
|
|
629
|
-
const targetCompositions = normalizeTargetCompositions(batch.targetCompositions, batch.requiredTargets);
|
|
630
|
-
const targets = effectiveTargetSummaries(targetAttempts, targetCompositions);
|
|
631
|
-
const required = new Set(batch.requiredTargets);
|
|
632
|
-
const covered = new Set(targets.filter((target) => required.has(target.target)).map((target) => target.target));
|
|
633
|
-
const missingTargets = batch.requiredTargets.filter((target) => !covered.has(target));
|
|
634
|
-
const requiredTargets = targets.filter((target) => required.has(target.target));
|
|
635
|
-
const runs = await readRunSummaries(batchRoot);
|
|
636
|
-
const completedRuns = runs.filter((run) => run.completedAt !== null);
|
|
637
|
-
const completionTimes = completedRuns.map((run) => run.completedAt);
|
|
638
|
-
const failedRuns = completedRuns.filter((run) => run.status === 'failed');
|
|
639
|
-
const latestCompletedAt = completionTimes
|
|
640
|
-
.sort((left, right) => right.localeCompare(left))[0] ?? null;
|
|
641
|
-
const summary = {
|
|
642
|
-
schemaVersion: 1,
|
|
643
|
-
batchId: batch.batchId,
|
|
644
|
-
name: batch.name,
|
|
645
|
-
owner: batch.owner,
|
|
646
|
-
createdAt: batch.createdAt,
|
|
647
|
-
updatedAt: new Date().toISOString(),
|
|
648
|
-
latestCompletedAt,
|
|
649
|
-
source: batch.source,
|
|
650
|
-
eval: {
|
|
651
|
-
runCount: completedRuns.length,
|
|
652
|
-
runs,
|
|
653
|
-
targetCount: targets.length,
|
|
654
|
-
attemptCount: targetAttempts.length,
|
|
655
|
-
outcome: targets.length > 0
|
|
656
|
-
? combineOutcomes(targets.map((target) => target.outcome))
|
|
657
|
-
: failedRuns.length > 0
|
|
658
|
-
? 'failed'
|
|
659
|
-
: 'not_run',
|
|
660
|
-
complete: targets.length > 0 && targets.every((target) => target.complete),
|
|
661
|
-
mechanicalPassed: targets.length > 0 &&
|
|
662
|
-
targets.every((target) => target.mechanicalPassed),
|
|
663
|
-
releaseRequired: required.size,
|
|
664
|
-
releaseCovered: covered.size,
|
|
665
|
-
releaseOutcome: missingTargets.length > 0
|
|
666
|
-
? 'incomplete'
|
|
667
|
-
: combineOutcomes(requiredTargets.map((target) => target.outcome)),
|
|
668
|
-
missingTargets,
|
|
669
|
-
targets,
|
|
670
|
-
targetAttempts,
|
|
671
|
-
},
|
|
672
|
-
diagnostics: failedRuns.map((run) => `Eval run ${run.runId} failed before publication.`),
|
|
673
|
-
};
|
|
674
|
-
await writeJsonAtomic(path.join(batchRoot, 'summary.json'), summary);
|
|
675
|
-
await writeJsonAtomic(path.join(batchRoot, 'evals', 'evaluation.json'), {
|
|
676
|
-
schemaVersion: 1,
|
|
677
|
-
generatedAt: summary.updatedAt,
|
|
678
|
-
batchId: summary.batchId,
|
|
679
|
-
owner: summary.owner,
|
|
680
|
-
source: summary.source,
|
|
681
|
-
outcome: summary.eval.releaseOutcome,
|
|
682
|
-
complete: summary.eval.runCount > 0 &&
|
|
683
|
-
summary.eval.releaseOutcome === 'passed' &&
|
|
684
|
-
summary.eval.releaseCovered === summary.eval.releaseRequired &&
|
|
685
|
-
summary.eval.targets.filter((target) => target.requiredForRelease).every((target) => target.complete),
|
|
686
|
-
mechanicalPassed: summary.eval.releaseRequired > 0 &&
|
|
687
|
-
summary.eval.releaseCovered === summary.eval.releaseRequired &&
|
|
688
|
-
summary.eval.targets
|
|
689
|
-
.filter((target) => target.requiredForRelease)
|
|
690
|
-
.every((target) => target.mechanicalPassed),
|
|
691
|
-
targets: summary.eval.targets,
|
|
692
|
-
targetAttempts: summary.eval.targetAttempts,
|
|
693
|
-
diagnostics: summary.diagnostics,
|
|
694
|
-
});
|
|
695
|
-
const runRows = runs.map((run) => `| ${run.runId} | ${run.status} | ${run.outcome ?? '-'} | ${run.complete} |`);
|
|
696
|
-
const targetRows = targets.map((target) => `| ${target.target} | ${target.runId} | ${target.outcome} | ${target.complete} |`);
|
|
697
|
-
await writeTextAtomic(path.join(batchRoot, 'report.md'), [
|
|
698
|
-
'# Skill Verification Batch Report',
|
|
699
|
-
'',
|
|
700
|
-
`- Batch: \`${summary.batchId}\``,
|
|
701
|
-
`- Name: \`${summary.name}\``,
|
|
702
|
-
`- Owner: \`${ownerKey(summary.owner)}\``,
|
|
703
|
-
`- Eval outcome: \`${summary.eval.outcome}\``,
|
|
704
|
-
`- Release outcome: \`${summary.eval.releaseOutcome}\``,
|
|
705
|
-
`- Release coverage: \`${summary.eval.releaseCovered}/${summary.eval.releaseRequired}\``,
|
|
706
|
-
`- Target attempts: \`${summary.eval.attemptCount}\``,
|
|
707
|
-
`- Source: \`${summary.source.digest}\``,
|
|
708
|
-
'',
|
|
709
|
-
'| Run | Status | Outcome | Complete |',
|
|
710
|
-
'| --- | --- | --- | --- |',
|
|
711
|
-
...runRows,
|
|
712
|
-
'',
|
|
713
|
-
'| Target | Run | Outcome | Complete |',
|
|
714
|
-
'| --- | --- | --- | --- |',
|
|
715
|
-
...targetRows,
|
|
716
|
-
'',
|
|
717
|
-
].join('\n'));
|
|
718
|
-
batch.updatedAt = summary.updatedAt;
|
|
719
|
-
await writeJsonAtomic(path.join(batchRoot, 'batch.json'), batch);
|
|
720
|
-
return summary;
|
|
721
|
-
}
|
|
722
|
-
async function readRunningEvalRun(layout) {
|
|
723
|
-
const runPath = path.join(layout.reportRoot, 'run.json');
|
|
724
|
-
const run = await fs.readJson(runPath);
|
|
725
|
-
if (run.status !== 'running') {
|
|
726
|
-
throw new Error(`Eval run ${layout.runId} is not running.`);
|
|
727
|
-
}
|
|
728
|
-
return { runPath, run };
|
|
729
|
-
}
|
|
730
|
-
async function publishEvaluationTargetsUnlocked(options) {
|
|
731
|
-
await assertMaterializedSubjectsCompatible(options.layout.batchRoot, options.evaluation.subjects);
|
|
732
|
-
const retryableTargets = await assertSuiteTargetsAvailable({
|
|
733
|
-
batchRoot: options.layout.batchRoot,
|
|
734
|
-
evaluation: options.evaluation,
|
|
735
|
-
caseId: options.caseId,
|
|
736
|
-
allowDuplicateTargets: options.allowDuplicateTargets,
|
|
737
|
-
});
|
|
738
|
-
const required = new Set(options.batch.requiredTargets);
|
|
739
|
-
for (const subject of options.evaluation.subjects) {
|
|
740
|
-
const target = targetKey(['materialization', subject.profile]);
|
|
741
|
-
await writeTargetReport({
|
|
742
|
-
batchRoot: options.layout.batchRoot,
|
|
743
|
-
allowExistingMaterialization: true,
|
|
744
|
-
summary: {
|
|
745
|
-
schemaVersion: 1,
|
|
746
|
-
target,
|
|
747
|
-
kind: 'materialization',
|
|
748
|
-
runId: options.layout.runId,
|
|
749
|
-
generatedAt: options.evaluation.generatedAt,
|
|
750
|
-
profile: subject.profile,
|
|
751
|
-
suite: null,
|
|
752
|
-
split: 'all',
|
|
753
|
-
case: 'all',
|
|
754
|
-
outcome: 'passed',
|
|
755
|
-
complete: true,
|
|
756
|
-
mechanicalPassed: true,
|
|
757
|
-
qualitativeReview: 'not_required',
|
|
758
|
-
requiredForRelease: required.has(target),
|
|
759
|
-
reportPath: null,
|
|
760
|
-
summary: materializationSummary(subject),
|
|
761
|
-
diagnostics: [],
|
|
762
|
-
},
|
|
763
|
-
});
|
|
764
|
-
}
|
|
765
|
-
for (const suite of options.evaluation.suites) {
|
|
766
|
-
const target = targetKey([
|
|
767
|
-
suite.kind,
|
|
768
|
-
suite.id,
|
|
769
|
-
suite.profile ?? 'project-default',
|
|
770
|
-
suite.split ?? 'all',
|
|
771
|
-
options.caseId ?? 'all',
|
|
772
|
-
]);
|
|
773
|
-
await writeTargetReport({
|
|
774
|
-
batchRoot: options.layout.batchRoot,
|
|
775
|
-
allowDuplicate: options.allowDuplicateTargets || retryableTargets.has(target),
|
|
776
|
-
summary: {
|
|
777
|
-
schemaVersion: 1,
|
|
778
|
-
target,
|
|
779
|
-
kind: suite.kind,
|
|
780
|
-
runId: options.layout.runId,
|
|
781
|
-
generatedAt: options.evaluation.generatedAt,
|
|
782
|
-
profile: suite.profile ?? 'project-default',
|
|
783
|
-
suite: suite.id,
|
|
784
|
-
split: suite.split ?? 'all',
|
|
785
|
-
case: options.caseId ?? 'all',
|
|
786
|
-
outcome: suite.outcome,
|
|
787
|
-
complete: suite.complete,
|
|
788
|
-
mechanicalPassed: suite.mechanicalPassed,
|
|
789
|
-
qualitativeReview: suite.qualitativeReview ?? 'not_required',
|
|
790
|
-
requiredForRelease: required.has(target),
|
|
791
|
-
reportPath: suite.reportPath ?? null,
|
|
792
|
-
summary: suite.summary ?? {},
|
|
793
|
-
diagnostics: suite.diagnostics ?? [],
|
|
794
|
-
},
|
|
795
|
-
});
|
|
796
|
-
if (!options.caseId) {
|
|
797
|
-
for (const caseResult of suite.caseResults ?? []) {
|
|
798
|
-
const caseTarget = targetKey([
|
|
799
|
-
suite.kind,
|
|
800
|
-
suite.id,
|
|
801
|
-
suite.profile ?? 'project-default',
|
|
802
|
-
suite.split ?? 'all',
|
|
803
|
-
caseResult.id,
|
|
804
|
-
]);
|
|
805
|
-
await writeTargetReport({
|
|
806
|
-
batchRoot: options.layout.batchRoot,
|
|
807
|
-
allowDuplicate: options.allowDuplicateTargets || retryableTargets.has(caseTarget),
|
|
808
|
-
summary: {
|
|
809
|
-
schemaVersion: 1,
|
|
810
|
-
target: caseTarget,
|
|
811
|
-
kind: suite.kind,
|
|
812
|
-
runId: options.layout.runId,
|
|
813
|
-
generatedAt: options.evaluation.generatedAt,
|
|
814
|
-
profile: suite.profile ?? 'project-default',
|
|
815
|
-
suite: suite.id,
|
|
816
|
-
split: suite.split ?? 'all',
|
|
817
|
-
case: caseResult.id,
|
|
818
|
-
outcome: caseResult.outcome,
|
|
819
|
-
complete: caseResult.complete,
|
|
820
|
-
mechanicalPassed: caseResult.mechanicalPassed,
|
|
821
|
-
qualitativeReview: caseResult.qualitativeReview ?? suite.qualitativeReview ?? 'not_required',
|
|
822
|
-
requiredForRelease: required.has(caseTarget),
|
|
823
|
-
reportPath: caseResult.reportPath ?? suite.reportPath ?? null,
|
|
824
|
-
summary: caseResult.summary ?? {},
|
|
825
|
-
diagnostics: caseResult.diagnostics ?? [],
|
|
826
|
-
},
|
|
827
|
-
});
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
export async function resolveSkillEvalTargetPlan(options) {
|
|
833
|
-
await assertSafeSkillReportPath(options.layout.reportBoundaryRoot, options.layout.batchRoot);
|
|
834
|
-
return withBatchLock(options.layout.batchRoot, async () => {
|
|
835
|
-
const batch = await readBatch(options.layout.batchRoot);
|
|
836
|
-
await readRunningEvalRun(options.layout);
|
|
837
|
-
const requested = [...new Set(options.targets.map((target, index) => validateTarget(target, `Requested eval target ${index + 1}`)))];
|
|
838
|
-
const effective = new Map(effectiveTargetSummaries(await collectTargetSummaries(options.layout.batchRoot), normalizeTargetCompositions(batch.targetCompositions, batch.requiredTargets)).map((target) => [target.target, target]));
|
|
839
|
-
const pending = [];
|
|
840
|
-
const reusable = [];
|
|
841
|
-
for (const target of requested) {
|
|
842
|
-
const existing = effective.get(target);
|
|
843
|
-
if (existing?.complete && existing.outcome !== 'incomplete') {
|
|
844
|
-
reusable.push(existing);
|
|
845
|
-
}
|
|
846
|
-
else {
|
|
847
|
-
pending.push(target);
|
|
848
|
-
}
|
|
849
|
-
}
|
|
850
|
-
return { pending, reusable };
|
|
851
|
-
});
|
|
852
|
-
}
|
|
853
|
-
export async function publishEvalRunCheckpoint(options) {
|
|
854
|
-
await assertSafeSkillReportPath(options.layout.reportBoundaryRoot, options.layout.batchRoot);
|
|
855
|
-
return withBatchLock(options.layout.batchRoot, async () => {
|
|
856
|
-
const batch = await readBatch(options.layout.batchRoot);
|
|
857
|
-
await readRunningEvalRun(options.layout);
|
|
858
|
-
await publishEvaluationTargetsUnlocked({ ...options, batch });
|
|
859
|
-
return refreshBatchSummary(options.layout.batchRoot);
|
|
860
|
-
});
|
|
861
|
-
}
|
|
862
|
-
export async function completeSkillEvalRun(options) {
|
|
863
|
-
await assertSafeSkillReportPath(options.layout.reportBoundaryRoot, options.layout.batchRoot);
|
|
864
|
-
return withBatchLock(options.layout.batchRoot, async () => {
|
|
865
|
-
await readBatch(options.layout.batchRoot);
|
|
866
|
-
const { runPath, run } = await readRunningEvalRun(options.layout);
|
|
867
|
-
await writeJsonAtomic(runPath, {
|
|
868
|
-
...run,
|
|
869
|
-
status: 'completed',
|
|
870
|
-
completedAt: new Date().toISOString(),
|
|
871
|
-
outcome: options.evaluation.outcome,
|
|
872
|
-
});
|
|
873
|
-
return refreshBatchSummary(options.layout.batchRoot);
|
|
874
|
-
});
|
|
875
|
-
}
|
|
876
|
-
export async function publishEvalRunToBatch(options) {
|
|
877
|
-
await assertSafeSkillReportPath(options.layout.reportBoundaryRoot, options.layout.batchRoot);
|
|
878
|
-
return withBatchLock(options.layout.batchRoot, async () => {
|
|
879
|
-
const batch = await readBatch(options.layout.batchRoot);
|
|
880
|
-
const { runPath, run } = await readRunningEvalRun(options.layout);
|
|
881
|
-
await publishEvaluationTargetsUnlocked({ ...options, batch });
|
|
882
|
-
await writeJsonAtomic(runPath, {
|
|
883
|
-
...run,
|
|
884
|
-
status: 'completed',
|
|
885
|
-
completedAt: new Date().toISOString(),
|
|
886
|
-
outcome: options.evaluation.outcome,
|
|
887
|
-
});
|
|
888
|
-
return refreshBatchSummary(options.layout.batchRoot);
|
|
889
|
-
});
|
|
890
|
-
}
|
|
891
|
-
export async function failSkillEvalRun(options) {
|
|
892
|
-
await assertSafeSkillReportPath(options.layout.reportBoundaryRoot, options.layout.batchRoot);
|
|
893
|
-
return withBatchLock(options.layout.batchRoot, async () => {
|
|
894
|
-
await readBatch(options.layout.batchRoot);
|
|
895
|
-
const runPath = path.join(options.layout.reportRoot, 'run.json');
|
|
896
|
-
const run = await fs.readJson(runPath);
|
|
897
|
-
await writeJsonAtomic(runPath, {
|
|
898
|
-
...run,
|
|
899
|
-
status: 'failed',
|
|
900
|
-
completedAt: new Date().toISOString(),
|
|
901
|
-
outcome: 'failed',
|
|
902
|
-
...(options.message ? { failureReason: options.message } : {}),
|
|
903
|
-
});
|
|
904
|
-
return refreshBatchSummary(options.layout.batchRoot);
|
|
905
|
-
});
|
|
906
|
-
}
|
|
907
|
-
export async function refreshSkillReportBatch(options) {
|
|
908
|
-
const batchRoot = path.join(resolveSkillEvalRoot(options.repoRoot, options.owner), validateBatchId(options.batchId));
|
|
909
|
-
await assertSafeSkillReportPath(options.repoRoot, batchRoot);
|
|
910
|
-
const summary = await withBatchLock(batchRoot, async () => {
|
|
911
|
-
const batch = await readBatch(batchRoot);
|
|
912
|
-
if (!sameOwner(batch.owner, options.owner))
|
|
913
|
-
throw new Error('Report batch owner mismatch.');
|
|
914
|
-
await recoverInterruptedRunsUnlocked(batchRoot);
|
|
915
|
-
return refreshBatchSummary(batchRoot);
|
|
916
|
-
});
|
|
917
|
-
return summary;
|
|
918
|
-
}
|
|
919
|
-
export function reportBatchName(batchId) {
|
|
920
|
-
return batchNameFromId(validateBatchId(batchId));
|
|
921
|
-
}
|
|
922
|
-
//# sourceMappingURL=skill-reporting.js.map
|