@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
|
@@ -18,7 +18,8 @@ const CLI_PACKAGE_ROOT = path.basename(authoringRuntimeRoot) === 'dist'
|
|
|
18
18
|
: authoringRuntimeRoot;
|
|
19
19
|
const errors = [];
|
|
20
20
|
const warnings = [];
|
|
21
|
-
const SOURCE_PLUGIN_MANIFEST_CONTRACT_VERSION =
|
|
21
|
+
const SOURCE_PLUGIN_MANIFEST_CONTRACT_VERSION = 9;
|
|
22
|
+
let capabilityDiagnostic = null;
|
|
22
23
|
const MAX_SKILL_NAME_LEN = 64;
|
|
23
24
|
const MAX_SKILL_DESCRIPTION_LEN = 1024;
|
|
24
25
|
const MAX_SKILL_COMPATIBILITY_LEN = 500;
|
|
@@ -149,22 +150,9 @@ const ARTIFACT_PROJECT_MANAGED_KIND_BY_TARGET = {
|
|
|
149
150
|
'codex.skill': 'skill',
|
|
150
151
|
};
|
|
151
152
|
const PLUGIN_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
152
|
-
const PLUGIN_EVAL_SCHEMA_VERSION = 2;
|
|
153
|
-
const PLUGIN_EVAL_CASE_SCHEMA_VERSION = 1;
|
|
154
|
-
const PLUGIN_EVAL_SCOPES = new Set(['user', 'project']);
|
|
155
|
-
const PLUGIN_EVAL_SPLITS = new Set(['train', 'validation', 'sanity', 'all']);
|
|
156
|
-
const PLUGIN_EVAL_TRIGGER_CASE_SPLITS = new Set(['train', 'validation', 'sanity']);
|
|
157
|
-
const PLUGIN_EVAL_COMMAND_SUPPORTS = new Set([
|
|
158
|
-
'split',
|
|
159
|
-
'runs',
|
|
160
|
-
'case',
|
|
161
|
-
'model',
|
|
162
|
-
'codexBin',
|
|
163
|
-
'timeout',
|
|
164
|
-
'allowEnvironmentSkips',
|
|
165
|
-
]);
|
|
166
153
|
let yamlParser = null;
|
|
167
154
|
let pluginVersionCheckResult = null;
|
|
155
|
+
let skillEvalManifestTools = null;
|
|
168
156
|
function error(msg) {
|
|
169
157
|
errors.push(msg);
|
|
170
158
|
}
|
|
@@ -331,12 +319,16 @@ function loadSkillFrontmatterCapabilityRegistry() {
|
|
|
331
319
|
async function tryLoadCliManifestTools() {
|
|
332
320
|
const manifestModulePath = path.join(CLI_PACKAGE_ROOT, 'dist', 'src', 'utils', 'manifest.js');
|
|
333
321
|
const runtimeProjectionModulePath = path.join(CLI_PACKAGE_ROOT, 'dist', 'src', 'utils', 'catalog-runtime-projection.js');
|
|
334
|
-
|
|
322
|
+
const capabilityModulePath = path.join(CLI_PACKAGE_ROOT, 'dist', 'src', 'commands', 'aw-capabilities.js');
|
|
323
|
+
if (!fs.existsSync(manifestModulePath)
|
|
324
|
+
|| !fs.existsSync(runtimeProjectionModulePath)
|
|
325
|
+
|| !fs.existsSync(capabilityModulePath))
|
|
335
326
|
return null;
|
|
336
327
|
try {
|
|
337
|
-
const [manifestModule, runtimeProjectionModule] = await Promise.all([
|
|
328
|
+
const [manifestModule, runtimeProjectionModule, capabilityModule] = await Promise.all([
|
|
338
329
|
import(pathToFileURL(manifestModulePath).href),
|
|
339
330
|
import(pathToFileURL(runtimeProjectionModulePath).href),
|
|
331
|
+
import(pathToFileURL(capabilityModulePath).href),
|
|
340
332
|
]);
|
|
341
333
|
if (manifestModule.SOURCE_PLUGIN_MANIFEST_CONTRACT_VERSION
|
|
342
334
|
!== SOURCE_PLUGIN_MANIFEST_CONTRACT_VERSION) {
|
|
@@ -347,6 +339,8 @@ async function tryLoadCliManifestTools() {
|
|
|
347
339
|
validateSourcePluginManifest: manifestModule.validateSourcePluginManifest ?? null,
|
|
348
340
|
readPluginManifest: manifestModule.readPluginManifest ?? null,
|
|
349
341
|
discoverPlugins: manifestModule.discoverPlugins ?? null,
|
|
342
|
+
resolvePluginRequirements: manifestModule.resolvePluginRequirements ?? null,
|
|
343
|
+
implementedCapabilities: capabilityModule.IMPLEMENTED_AW_CAPABILITIES ?? null,
|
|
350
344
|
createRuntimeResolver: runtimeProjectionModule.createCatalogRuntimeImportResolver ?? null,
|
|
351
345
|
};
|
|
352
346
|
}
|
|
@@ -498,11 +492,12 @@ function validateManifestLightweight(manifest) {
|
|
|
498
492
|
error('top-level "commands" has been removed; use "slashCommands" instead');
|
|
499
493
|
}
|
|
500
494
|
validateAllowedKeys(input, 'plugin manifest', [
|
|
495
|
+
'schemaVersion',
|
|
501
496
|
'name',
|
|
502
497
|
'version',
|
|
503
498
|
'description',
|
|
504
499
|
'dependencies',
|
|
505
|
-
'
|
|
500
|
+
'requires',
|
|
506
501
|
'skills',
|
|
507
502
|
'slashCommands',
|
|
508
503
|
'mcpServers',
|
|
@@ -613,7 +608,7 @@ function validatePortableNativeArtifactCapabilityLightweight(manifest) {
|
|
|
613
608
|
&& Array.isArray(target.states)
|
|
614
609
|
&& target.states.includes('present'));
|
|
615
610
|
if (!hasOtherNativeDeclaration && !hasPresentNativeArtifact) {
|
|
616
|
-
const message = 'Invalid plugin manifest: portable-only plugins are not supported
|
|
611
|
+
const message = 'Invalid plugin manifest: portable-only plugins are not supported. Declare at least one non-empty native capability.';
|
|
617
612
|
if (!errors.includes(message))
|
|
618
613
|
error(message);
|
|
619
614
|
}
|
|
@@ -950,703 +945,19 @@ function validateNonEmptyString(value, fieldName, maxLength) {
|
|
|
950
945
|
}
|
|
951
946
|
return trimmed;
|
|
952
947
|
}
|
|
953
|
-
function
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
!path.isAbsolute(relative));
|
|
958
|
-
}
|
|
959
|
-
function validateRealPathBoundary(boundary, candidate, fieldName, boundaryName) {
|
|
960
|
-
const canonicalBoundary = fs.realpathSync(boundary);
|
|
961
|
-
const canonicalCandidate = fs.realpathSync(candidate);
|
|
962
|
-
if (!isPathInsideOrEqual(canonicalBoundary, canonicalCandidate)) {
|
|
963
|
-
error(`${fieldName} must stay inside the ${boundaryName}`);
|
|
964
|
-
return false;
|
|
965
|
-
}
|
|
966
|
-
return true;
|
|
967
|
-
}
|
|
968
|
-
function validateEvalRelativeFile(evalDir, value, fieldName) {
|
|
969
|
-
const relativePath = validateNonEmptyString(value, fieldName);
|
|
970
|
-
if (!relativePath)
|
|
971
|
-
return null;
|
|
972
|
-
const normalized = stripDotSlash(relativePath).split('\\').join('/');
|
|
973
|
-
if (path.isAbsolute(normalized) || normalized.split('/').includes('..')) {
|
|
974
|
-
error(`${fieldName} must stay inside the Skill eval directory`);
|
|
975
|
-
return null;
|
|
976
|
-
}
|
|
977
|
-
const absolutePath = path.resolve(evalDir, normalized);
|
|
978
|
-
const relativeToRoot = path.relative(evalDir, absolutePath);
|
|
979
|
-
if (relativeToRoot.startsWith('..') || path.isAbsolute(relativeToRoot)) {
|
|
980
|
-
error(`${fieldName} must stay inside the Skill eval directory`);
|
|
981
|
-
return null;
|
|
982
|
-
}
|
|
983
|
-
if (!fs.existsSync(absolutePath) || !fs.statSync(absolutePath).isFile()) {
|
|
984
|
-
error(`${fieldName} does not exist: ${normalized}`);
|
|
985
|
-
return null;
|
|
986
|
-
}
|
|
987
|
-
if (!validateRealPathBoundary(evalDir, absolutePath, fieldName, 'Skill eval directory')) {
|
|
988
|
-
return null;
|
|
989
|
-
}
|
|
990
|
-
return absolutePath;
|
|
991
|
-
}
|
|
992
|
-
function validateEvalWorkspacePath(value, fieldName, options = {}) {
|
|
993
|
-
const raw = validateNonEmptyString(value, fieldName);
|
|
994
|
-
if (!raw)
|
|
995
|
-
return null;
|
|
996
|
-
const treePattern = options.allowTreePattern === true && raw.endsWith('/**');
|
|
997
|
-
const candidate = treePattern ? raw.slice(0, -3) : raw;
|
|
998
|
-
if (candidate === '' ||
|
|
999
|
-
candidate.includes('\0') ||
|
|
1000
|
-
candidate.includes('\\') ||
|
|
1001
|
-
candidate.includes('*') ||
|
|
1002
|
-
candidate.includes('?') ||
|
|
1003
|
-
candidate.includes('[') ||
|
|
1004
|
-
candidate.includes(']') ||
|
|
1005
|
-
path.isAbsolute(candidate) ||
|
|
1006
|
-
path.posix.normalize(candidate) !== candidate ||
|
|
1007
|
-
candidate.split('/').includes('..')) {
|
|
1008
|
-
error(`${fieldName} must be a normalized POSIX path inside the eval workspace`);
|
|
1009
|
-
return null;
|
|
1010
|
-
}
|
|
1011
|
-
const firstSegment = candidate.split('/')[0];
|
|
1012
|
-
if (firstSegment === '.git' || firstSegment === '.agents' || firstSegment === '.codex') {
|
|
1013
|
-
error(`${fieldName} must not target an eval control directory`);
|
|
1014
|
-
return null;
|
|
1015
|
-
}
|
|
1016
|
-
return raw;
|
|
1017
|
-
}
|
|
1018
|
-
function evalWorkspacePathMatchesPattern(relativePath, pattern) {
|
|
1019
|
-
if (pattern.endsWith('/**')) {
|
|
1020
|
-
const prefix = pattern.slice(0, -3);
|
|
1021
|
-
return relativePath === prefix || relativePath.startsWith(`${prefix}/`);
|
|
1022
|
-
}
|
|
1023
|
-
return relativePath === pattern;
|
|
1024
|
-
}
|
|
1025
|
-
function validateConversationCaseCorpus(evalDir, suite, fieldName) {
|
|
1026
|
-
const casesPath = validateEvalRelativeFile(evalDir, suite.cases, `${fieldName}.cases`);
|
|
1027
|
-
if (!casesPath)
|
|
1028
|
-
return;
|
|
1029
|
-
let document;
|
|
1030
|
-
try {
|
|
1031
|
-
document = JSON.parse(fs.readFileSync(casesPath, 'utf8'));
|
|
1032
|
-
}
|
|
1033
|
-
catch (e) {
|
|
1034
|
-
error(`${fieldName}.cases is not valid JSON: ${e.message}`);
|
|
1035
|
-
return;
|
|
1036
|
-
}
|
|
1037
|
-
const corpus = expectRecord(document, `${fieldName}.cases document`);
|
|
1038
|
-
if (!corpus)
|
|
1039
|
-
return;
|
|
1040
|
-
const skillName = validateNonEmptyString(corpus.skill_name, `${fieldName}.cases.skill_name`);
|
|
1041
|
-
if (skillName && skillName !== suite.skill) {
|
|
1042
|
-
error(`${fieldName}.cases.skill_name must match ${JSON.stringify(suite.skill)}`);
|
|
1043
|
-
}
|
|
1044
|
-
if (!Array.isArray(corpus.evals) || corpus.evals.length === 0) {
|
|
1045
|
-
error(`${fieldName}.cases.evals must be a non-empty array`);
|
|
1046
|
-
return;
|
|
1047
|
-
}
|
|
1048
|
-
const ids = new Set();
|
|
1049
|
-
corpus.evals.forEach((value, caseIndex) => {
|
|
1050
|
-
const caseField = `${fieldName}.cases.evals[${caseIndex}]`;
|
|
1051
|
-
const entry = expectRecord(value, caseField);
|
|
1052
|
-
if (!entry)
|
|
1053
|
-
return;
|
|
1054
|
-
const id = validateNonEmptyString(entry.id, `${caseField}.id`);
|
|
1055
|
-
if (id && ids.has(id)) {
|
|
1056
|
-
error(`${fieldName}.cases has duplicate case id ${JSON.stringify(id)}`);
|
|
1057
|
-
}
|
|
1058
|
-
else if (id) {
|
|
1059
|
-
ids.add(id);
|
|
1060
|
-
}
|
|
1061
|
-
validateNonEmptyString(entry.prompt, `${caseField}.prompt`);
|
|
1062
|
-
const sandbox = entry.sandbox === undefined
|
|
1063
|
-
? 'read-only'
|
|
1064
|
-
: validateNonEmptyString(entry.sandbox, `${caseField}.sandbox`);
|
|
1065
|
-
if (sandbox && sandbox !== 'read-only' && sandbox !== 'workspace-write') {
|
|
1066
|
-
error(`${caseField}.sandbox must be "read-only" or "workspace-write"`);
|
|
1067
|
-
}
|
|
1068
|
-
if (!Array.isArray(entry.scripted_replies)) {
|
|
1069
|
-
error(`${caseField}.scripted_replies must be an array`);
|
|
1070
|
-
}
|
|
1071
|
-
else {
|
|
1072
|
-
entry.scripted_replies.forEach((reply, replyIndex) => {
|
|
1073
|
-
const replyField = `${caseField}.scripted_replies[${replyIndex}]`;
|
|
1074
|
-
if (typeof reply === 'string')
|
|
1075
|
-
return;
|
|
1076
|
-
const replyEntry = expectRecord(reply, replyField);
|
|
1077
|
-
if (!replyEntry)
|
|
1078
|
-
return;
|
|
1079
|
-
if (!Array.isArray(replyEntry.answers_by_index) || replyEntry.answers_by_index.some((answer) => typeof answer !== 'string')) {
|
|
1080
|
-
error(`${replyField}.answers_by_index must be an array of strings`);
|
|
1081
|
-
}
|
|
1082
|
-
if (replyEntry.expected_question_count !== undefined &&
|
|
1083
|
-
(!Number.isInteger(replyEntry.expected_question_count) ||
|
|
1084
|
-
replyEntry.expected_question_count < 0)) {
|
|
1085
|
-
error(`${replyField}.expected_question_count must be a non-negative integer`);
|
|
1086
|
-
}
|
|
1087
|
-
validateNonEmptyString(replyEntry.unexpected_question_answer, `${replyField}.unexpected_question_answer`);
|
|
1088
|
-
});
|
|
1089
|
-
}
|
|
1090
|
-
validateNonEmptyString(entry.expected_output, `${caseField}.expected_output`);
|
|
1091
|
-
if (entry.workspace_files !== undefined) {
|
|
1092
|
-
const workspaceFiles = expectRecord(entry.workspace_files, `${caseField}.workspace_files`);
|
|
1093
|
-
if (workspaceFiles) {
|
|
1094
|
-
const workspaceFileEntries = Object.entries(workspaceFiles);
|
|
1095
|
-
if (workspaceFileEntries.length === 0) {
|
|
1096
|
-
error(`${caseField}.workspace_files must contain at least one file`);
|
|
1097
|
-
}
|
|
1098
|
-
workspaceFileEntries.forEach(([relativePath, content]) => {
|
|
1099
|
-
validateEvalWorkspacePath(relativePath, `${caseField}.workspace_files.${relativePath}`, { allowTreePattern: false });
|
|
1100
|
-
validateNonEmptyString(content, `${caseField}.workspace_files.${relativePath}`);
|
|
1101
|
-
});
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1104
|
-
const review = expectRecord(entry.review, `${caseField}.review`);
|
|
1105
|
-
if (review) {
|
|
1106
|
-
if (!Array.isArray(review.checks) || review.checks.length === 0 || review.checks.some((check) => typeof check !== 'string' || check.trim() === '')) {
|
|
1107
|
-
error(`${caseField}.review.checks must be a non-empty array of strings`);
|
|
1108
|
-
}
|
|
1109
|
-
if (review.workspace_unchanged !== undefined &&
|
|
1110
|
-
typeof review.workspace_unchanged !== 'boolean') {
|
|
1111
|
-
error(`${caseField}.review.workspace_unchanged must be boolean`);
|
|
1112
|
-
}
|
|
1113
|
-
const allowedWorkspaceChanges = [];
|
|
1114
|
-
if (review.allowed_workspace_changes !== undefined) {
|
|
1115
|
-
if (!Array.isArray(review.allowed_workspace_changes) ||
|
|
1116
|
-
review.allowed_workspace_changes.length === 0) {
|
|
1117
|
-
error(`${caseField}.review.allowed_workspace_changes must be a non-empty array`);
|
|
1118
|
-
}
|
|
1119
|
-
else {
|
|
1120
|
-
review.allowed_workspace_changes.forEach((pathValue, pathIndex) => {
|
|
1121
|
-
const normalized = validateEvalWorkspacePath(pathValue, `${caseField}.review.allowed_workspace_changes[${pathIndex}]`, { allowTreePattern: true });
|
|
1122
|
-
if (normalized)
|
|
1123
|
-
allowedWorkspaceChanges.push(normalized);
|
|
1124
|
-
});
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
const artifactPaths = [];
|
|
1128
|
-
if (review.artifacts !== undefined) {
|
|
1129
|
-
if (!Array.isArray(review.artifacts) || review.artifacts.length === 0) {
|
|
1130
|
-
error(`${caseField}.review.artifacts must be a non-empty array`);
|
|
1131
|
-
}
|
|
1132
|
-
else {
|
|
1133
|
-
review.artifacts.forEach((artifactValue, artifactIndex) => {
|
|
1134
|
-
const artifactField = `${caseField}.review.artifacts[${artifactIndex}]`;
|
|
1135
|
-
const artifact = expectRecord(artifactValue, artifactField);
|
|
1136
|
-
if (!artifact)
|
|
1137
|
-
return;
|
|
1138
|
-
validateAllowedKeys(artifact, artifactField, ['path', 'exists', 'contains', 'not_contains', 'json']);
|
|
1139
|
-
const artifactPath = validateEvalWorkspacePath(artifact.path, `${artifactField}.path`);
|
|
1140
|
-
if (artifactPath)
|
|
1141
|
-
artifactPaths.push(artifactPath);
|
|
1142
|
-
if (artifact.exists !== undefined && typeof artifact.exists !== 'boolean') {
|
|
1143
|
-
error(`${artifactField}.exists must be boolean`);
|
|
1144
|
-
}
|
|
1145
|
-
for (const key of ['contains', 'not_contains']) {
|
|
1146
|
-
if (artifact[key] !== undefined &&
|
|
1147
|
-
(!Array.isArray(artifact[key]) || artifact[key].length === 0 || artifact[key].some((fragment) => typeof fragment !== 'string' || fragment.length === 0))) {
|
|
1148
|
-
error(`${artifactField}.${key} must be a non-empty array of strings`);
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
|
-
if (artifact.json !== undefined && !isRecord(artifact.json)) {
|
|
1152
|
-
error(`${artifactField}.json must be an object`);
|
|
1153
|
-
}
|
|
1154
|
-
});
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
if (new Set(artifactPaths).size !== artifactPaths.length) {
|
|
1158
|
-
error(`${caseField}.review.artifacts contains duplicate paths`);
|
|
1159
|
-
}
|
|
1160
|
-
if (sandbox === 'workspace-write') {
|
|
1161
|
-
if (review.workspace_unchanged !== false) {
|
|
1162
|
-
error(`${caseField} workspace-write requires review.workspace_unchanged=false`);
|
|
1163
|
-
}
|
|
1164
|
-
if (allowedWorkspaceChanges.length === 0 || artifactPaths.length === 0) {
|
|
1165
|
-
error(`${caseField} workspace-write requires allowed changes and artifacts`);
|
|
1166
|
-
}
|
|
1167
|
-
const unmatchedArtifact = artifactPaths.find((artifactPath) => !allowedWorkspaceChanges.some((pattern) => evalWorkspacePathMatchesPattern(artifactPath, pattern)));
|
|
1168
|
-
if (unmatchedArtifact) {
|
|
1169
|
-
error(`${caseField} artifact ${JSON.stringify(unmatchedArtifact)} is outside allowed changes`);
|
|
1170
|
-
}
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
});
|
|
1174
|
-
}
|
|
1175
|
-
function validateTriggerCaseCorpus(evalDir, suite, fieldName) {
|
|
1176
|
-
const casesPath = validateEvalRelativeFile(evalDir, suite.cases, `${fieldName}.cases`);
|
|
1177
|
-
if (!casesPath)
|
|
1178
|
-
return;
|
|
1179
|
-
let document;
|
|
1180
|
-
try {
|
|
1181
|
-
document = JSON.parse(fs.readFileSync(casesPath, 'utf8'));
|
|
1182
|
-
}
|
|
1183
|
-
catch (e) {
|
|
1184
|
-
error(`${fieldName}.cases is not valid JSON: ${e.message}`);
|
|
1185
|
-
return;
|
|
1186
|
-
}
|
|
1187
|
-
const corpus = expectRecord(document, `${fieldName}.cases document`);
|
|
1188
|
-
if (!corpus)
|
|
1189
|
-
return;
|
|
1190
|
-
if (corpus.schemaVersion !== PLUGIN_EVAL_CASE_SCHEMA_VERSION) {
|
|
1191
|
-
error(`${fieldName}.cases.schemaVersion must be ${PLUGIN_EVAL_CASE_SCHEMA_VERSION}`);
|
|
1192
|
-
}
|
|
1193
|
-
if (corpus.skill !== suite.skill) {
|
|
1194
|
-
error(`${fieldName}.cases.skill must match ${JSON.stringify(suite.skill)}`);
|
|
1195
|
-
}
|
|
1196
|
-
if (!Array.isArray(corpus.cases) || corpus.cases.length === 0) {
|
|
1197
|
-
error(`${fieldName}.cases.cases must be a non-empty array`);
|
|
1198
|
-
return;
|
|
1199
|
-
}
|
|
1200
|
-
const ids = new Set();
|
|
1201
|
-
const seenSplits = new Set();
|
|
1202
|
-
let hasWorkspaceFixture = false;
|
|
1203
|
-
corpus.cases.forEach((value, caseIndex) => {
|
|
1204
|
-
const caseField = `${fieldName}.cases.cases[${caseIndex}]`;
|
|
1205
|
-
const entry = expectRecord(value, caseField);
|
|
1206
|
-
if (!entry)
|
|
1207
|
-
return;
|
|
1208
|
-
const id = validateNonEmptyString(entry.id, `${caseField}.id`);
|
|
1209
|
-
if (id && !/^[a-z0-9][a-z0-9_]*$/.test(id)) {
|
|
1210
|
-
error(`${caseField}.id must use lowercase snake_case`);
|
|
1211
|
-
}
|
|
1212
|
-
else if (id && ids.has(id)) {
|
|
1213
|
-
error(`${fieldName}.cases has duplicate case id ${JSON.stringify(id)}`);
|
|
1214
|
-
}
|
|
1215
|
-
else if (id) {
|
|
1216
|
-
ids.add(id);
|
|
1217
|
-
}
|
|
1218
|
-
if (!PLUGIN_EVAL_TRIGGER_CASE_SPLITS.has(entry.split)) {
|
|
1219
|
-
error(`${caseField}.split must be train, validation, or sanity`);
|
|
1220
|
-
}
|
|
1221
|
-
else {
|
|
1222
|
-
seenSplits.add(entry.split);
|
|
1223
|
-
}
|
|
1224
|
-
validateNonEmptyString(entry.query, `${caseField}.query`);
|
|
1225
|
-
const usesLegacyExpectation = entry.should_load_skill !== undefined;
|
|
1226
|
-
const usesRouteExpectation = entry.expected_loaded_skills !== undefined;
|
|
1227
|
-
if (usesLegacyExpectation === usesRouteExpectation) {
|
|
1228
|
-
error(`${caseField} must declare exactly one of should_load_skill or expected_loaded_skills`);
|
|
1229
|
-
}
|
|
1230
|
-
else if (usesLegacyExpectation && typeof entry.should_load_skill !== 'boolean') {
|
|
1231
|
-
error(`${caseField}.should_load_skill must be boolean`);
|
|
1232
|
-
}
|
|
1233
|
-
else if (usesRouteExpectation) {
|
|
1234
|
-
if (!Array.isArray(entry.expected_loaded_skills)) {
|
|
1235
|
-
error(`${caseField}.expected_loaded_skills must be an array`);
|
|
1236
|
-
}
|
|
1237
|
-
else {
|
|
1238
|
-
const expectedSkills = entry.expected_loaded_skills;
|
|
1239
|
-
expectedSkills.forEach((skillName, skillIndex) => {
|
|
1240
|
-
if (typeof skillName !== 'string' || skillName.trim() === '') {
|
|
1241
|
-
error(`${caseField}.expected_loaded_skills[${skillIndex}] must be a non-empty string`);
|
|
1242
|
-
}
|
|
1243
|
-
else if (!suite.skills.includes(skillName)) {
|
|
1244
|
-
error(`${caseField}.expected_loaded_skills[${skillIndex}] must name a participant Skill`);
|
|
1245
|
-
}
|
|
1246
|
-
});
|
|
1247
|
-
if (new Set(expectedSkills).size !== expectedSkills.length) {
|
|
1248
|
-
error(`${caseField}.expected_loaded_skills must not contain duplicates`);
|
|
1249
|
-
}
|
|
1250
|
-
if (suite.forbidMultipleSkillLoads === true && expectedSkills.length > 1) {
|
|
1251
|
-
error(`${caseField} expects multiple Skills but the suite forbids multiple Skill loads`);
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1255
|
-
if (suite.observeSpawnAgent === true) {
|
|
1256
|
-
if (typeof entry.should_spawn_agent !== 'boolean') {
|
|
1257
|
-
error(`${caseField}.should_spawn_agent is required when observeSpawnAgent=true`);
|
|
1258
|
-
}
|
|
1259
|
-
}
|
|
1260
|
-
else if (entry.should_spawn_agent !== undefined) {
|
|
1261
|
-
error(`${caseField}.should_spawn_agent requires observeSpawnAgent=true`);
|
|
1262
|
-
}
|
|
1263
|
-
if (entry.workspace_fixture !== undefined) {
|
|
1264
|
-
hasWorkspaceFixture = true;
|
|
1265
|
-
validateNonEmptyString(entry.workspace_fixture, `${caseField}.workspace_fixture`);
|
|
1266
|
-
}
|
|
1267
|
-
if (entry.use_project_rules !== undefined &&
|
|
1268
|
-
typeof entry.use_project_rules !== 'boolean') {
|
|
1269
|
-
error(`${caseField}.use_project_rules must be boolean`);
|
|
1270
|
-
}
|
|
1271
|
-
if (entry.tags !== undefined) {
|
|
1272
|
-
if (!Array.isArray(entry.tags) || entry.tags.length === 0) {
|
|
1273
|
-
error(`${caseField}.tags must be a non-empty array`);
|
|
1274
|
-
}
|
|
1275
|
-
else {
|
|
1276
|
-
entry.tags.forEach((tag, tagIndex) => {
|
|
1277
|
-
validateNonEmptyString(tag, `${caseField}.tags[${tagIndex}]`);
|
|
1278
|
-
});
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
});
|
|
1282
|
-
if (hasWorkspaceFixture && suite.fixtureModule === undefined) {
|
|
1283
|
-
error(`${fieldName}.fixtureModule is required when cases declare workspace_fixture`);
|
|
1284
|
-
}
|
|
1285
|
-
if (Array.isArray(suite.releaseSplits)) {
|
|
1286
|
-
suite.releaseSplits.forEach((split) => {
|
|
1287
|
-
if (split !== 'all' && !seenSplits.has(split)) {
|
|
1288
|
-
error(`${fieldName}.cases has no cases for release split ${JSON.stringify(split)}`);
|
|
1289
|
-
}
|
|
1290
|
-
});
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
function resolveEvalProfilePlugin(pluginDir, ownerManifest, pluginName, fieldName) {
|
|
1294
|
-
if (pluginName === ownerManifest.name) {
|
|
1295
|
-
return { pluginDir, manifest: ownerManifest };
|
|
1296
|
-
}
|
|
1297
|
-
const companionDir = path.join(path.dirname(pluginDir), pluginName);
|
|
1298
|
-
const manifestPath = path.join(companionDir, '.agents-plugin', 'plugin.json');
|
|
1299
|
-
if (!fs.existsSync(manifestPath)) {
|
|
1300
|
-
error(`${fieldName} must reference repo-local plugin "${pluginName}"`);
|
|
1301
|
-
return null;
|
|
1302
|
-
}
|
|
1303
|
-
let manifest;
|
|
948
|
+
async function loadSkillEvalManifestTools() {
|
|
949
|
+
if (skillEvalManifestTools)
|
|
950
|
+
return skillEvalManifestTools;
|
|
951
|
+
const modulePath = path.join(CLI_PACKAGE_ROOT, 'dist', 'src', 'eval', 'manifest-v1.js');
|
|
1304
952
|
try {
|
|
1305
|
-
|
|
953
|
+
skillEvalManifestTools = await import(pathToFileURL(modulePath).href);
|
|
954
|
+
return skillEvalManifestTools;
|
|
1306
955
|
}
|
|
1307
|
-
catch
|
|
1308
|
-
error(`${fieldName} companion manifest is not valid JSON: ${e.message}`);
|
|
1309
|
-
return null;
|
|
1310
|
-
}
|
|
1311
|
-
if (!isRecord(manifest) || manifest.name !== pluginName) {
|
|
1312
|
-
error(`${fieldName} companion source must declare plugin name "${pluginName}"`);
|
|
956
|
+
catch {
|
|
1313
957
|
return null;
|
|
1314
958
|
}
|
|
1315
|
-
return { pluginDir: companionDir, manifest };
|
|
1316
|
-
}
|
|
1317
|
-
function validateOnePluginEvalManifest(pluginDir, pluginManifest, evalDir, expectedSkill) {
|
|
1318
|
-
const evalManifestPath = path.join(evalDir, 'manifest.json');
|
|
1319
|
-
const manifestField = path.relative(pluginDir, evalManifestPath).split(path.sep).join('/');
|
|
1320
|
-
let document;
|
|
1321
|
-
try {
|
|
1322
|
-
document = JSON.parse(fs.readFileSync(evalManifestPath, 'utf8'));
|
|
1323
|
-
}
|
|
1324
|
-
catch (e) {
|
|
1325
|
-
error(`${manifestField} is not valid JSON: ${e.message}`);
|
|
1326
|
-
return;
|
|
1327
|
-
}
|
|
1328
|
-
const manifest = expectRecord(document, manifestField);
|
|
1329
|
-
if (!manifest)
|
|
1330
|
-
return;
|
|
1331
|
-
validateAllowedKeys(manifest, manifestField, ['schemaVersion', 'plugin', 'skill', 'profiles', 'suites']);
|
|
1332
|
-
if (manifest.schemaVersion !== PLUGIN_EVAL_SCHEMA_VERSION) {
|
|
1333
|
-
error(`${manifestField}.schemaVersion must be ${PLUGIN_EVAL_SCHEMA_VERSION}`);
|
|
1334
|
-
}
|
|
1335
|
-
if (manifest.plugin !== pluginManifest.name) {
|
|
1336
|
-
error(`${manifestField}.plugin must match plugin name ${JSON.stringify(pluginManifest.name)}`);
|
|
1337
|
-
}
|
|
1338
|
-
const skill = validateNonEmptyString(manifest.skill, `${manifestField}.skill`);
|
|
1339
|
-
if (skill && skill !== expectedSkill) {
|
|
1340
|
-
error(`${manifestField}.skill must match owning Skill ${JSON.stringify(expectedSkill)}`);
|
|
1341
|
-
}
|
|
1342
|
-
if (skill && !collectDeclaredSkillNames(pluginDir, pluginManifest).all.has(skill)) {
|
|
1343
|
-
error(`${manifestField}.skill must reference a declared static or dynamic skill`);
|
|
1344
|
-
}
|
|
1345
|
-
if (!Array.isArray(manifest.profiles) || manifest.profiles.length === 0) {
|
|
1346
|
-
error(`${manifestField}.profiles must be a non-empty array`);
|
|
1347
|
-
return;
|
|
1348
|
-
}
|
|
1349
|
-
const profilesById = new Map();
|
|
1350
|
-
manifest.profiles.forEach((value, index) => {
|
|
1351
|
-
const fieldName = `${manifestField}.profiles[${index}]`;
|
|
1352
|
-
const profile = expectRecord(value, fieldName);
|
|
1353
|
-
if (!profile)
|
|
1354
|
-
return;
|
|
1355
|
-
validateAllowedKeys(profile, fieldName, ['id', 'scope', 'plugins', 'runModel', 'requiredForRelease']);
|
|
1356
|
-
const id = validateNonEmptyString(profile.id, `${fieldName}.id`);
|
|
1357
|
-
if (id && !PLUGIN_NAME_PATTERN.test(id)) {
|
|
1358
|
-
error(`${fieldName}.id must use lowercase kebab-case`);
|
|
1359
|
-
}
|
|
1360
|
-
else if (id && profilesById.has(id)) {
|
|
1361
|
-
error(`${manifestField} has duplicate profile id ${JSON.stringify(id)}`);
|
|
1362
|
-
}
|
|
1363
|
-
if (!PLUGIN_EVAL_SCOPES.has(profile.scope)) {
|
|
1364
|
-
error(`${fieldName}.scope must be user or project`);
|
|
1365
|
-
}
|
|
1366
|
-
const skillOwners = new Map();
|
|
1367
|
-
const installedPlugins = new Set();
|
|
1368
|
-
if (!Array.isArray(profile.plugins) || profile.plugins.length === 0) {
|
|
1369
|
-
error(`${fieldName}.plugins must be a non-empty array`);
|
|
1370
|
-
}
|
|
1371
|
-
else {
|
|
1372
|
-
profile.plugins.forEach((value, pluginIndex) => {
|
|
1373
|
-
const pluginField = `${fieldName}.plugins[${pluginIndex}]`;
|
|
1374
|
-
const installation = expectRecord(value, pluginField);
|
|
1375
|
-
if (!installation)
|
|
1376
|
-
return;
|
|
1377
|
-
validateAllowedKeys(installation, pluginField, ['name', 'config']);
|
|
1378
|
-
const pluginName = validateNonEmptyString(installation.name, `${pluginField}.name`);
|
|
1379
|
-
if (!pluginName)
|
|
1380
|
-
return;
|
|
1381
|
-
if (!PLUGIN_NAME_PATTERN.test(pluginName)) {
|
|
1382
|
-
error(`${pluginField}.name must use lowercase kebab-case`);
|
|
1383
|
-
}
|
|
1384
|
-
if (installedPlugins.has(pluginName)) {
|
|
1385
|
-
error(`${fieldName}.plugins has duplicate plugin ${JSON.stringify(pluginName)}`);
|
|
1386
|
-
return;
|
|
1387
|
-
}
|
|
1388
|
-
installedPlugins.add(pluginName);
|
|
1389
|
-
if (installation.config !== undefined && !isRecord(installation.config)) {
|
|
1390
|
-
error(`${pluginField}.config must be an object`);
|
|
1391
|
-
}
|
|
1392
|
-
const source = resolveEvalProfilePlugin(pluginDir, pluginManifest, pluginName, `${pluginField}.name`);
|
|
1393
|
-
if (!source)
|
|
1394
|
-
return;
|
|
1395
|
-
for (const skillName of collectDeclaredSkillNames(source.pluginDir, source.manifest).all) {
|
|
1396
|
-
const existing = skillOwners.get(skillName);
|
|
1397
|
-
if (existing && existing !== pluginName) {
|
|
1398
|
-
error(`${fieldName} installs duplicate Skill key ${JSON.stringify(skillName)} ` +
|
|
1399
|
-
`from plugins ${JSON.stringify(existing)} and ${JSON.stringify(pluginName)}`);
|
|
1400
|
-
}
|
|
1401
|
-
else {
|
|
1402
|
-
skillOwners.set(skillName, pluginName);
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
});
|
|
1406
|
-
}
|
|
1407
|
-
if (!installedPlugins.has(pluginManifest.name)) {
|
|
1408
|
-
error(`${fieldName}.plugins must include owner plugin ${JSON.stringify(pluginManifest.name)}`);
|
|
1409
|
-
}
|
|
1410
|
-
if (typeof profile.runModel !== 'boolean') {
|
|
1411
|
-
error(`${fieldName}.runModel must be boolean`);
|
|
1412
|
-
}
|
|
1413
|
-
if (typeof profile.requiredForRelease !== 'boolean') {
|
|
1414
|
-
error(`${fieldName}.requiredForRelease must be boolean`);
|
|
1415
|
-
}
|
|
1416
|
-
if (profile.scope === 'user' && profile.runModel === true) {
|
|
1417
|
-
error(`${fieldName}: eval schema v2 does not allow real model execution for user scope`);
|
|
1418
|
-
}
|
|
1419
|
-
if (id && PLUGIN_NAME_PATTERN.test(id) && !profilesById.has(id)) {
|
|
1420
|
-
profilesById.set(id, { ...profile, skillOwners });
|
|
1421
|
-
}
|
|
1422
|
-
});
|
|
1423
|
-
if (!manifest.profiles.some((profile) => isRecord(profile) && profile.scope === 'project' && profile.runModel === true)) {
|
|
1424
|
-
error(`${manifestField} must define at least one project profile with runModel=true`);
|
|
1425
|
-
}
|
|
1426
|
-
if (!Array.isArray(manifest.suites) || manifest.suites.length === 0) {
|
|
1427
|
-
error(`${manifestField}.suites must be a non-empty array`);
|
|
1428
|
-
return;
|
|
1429
|
-
}
|
|
1430
|
-
const suiteIds = new Set();
|
|
1431
|
-
manifest.suites.forEach((value, index) => {
|
|
1432
|
-
const fieldName = `${manifestField}.suites[${index}]`;
|
|
1433
|
-
const suite = expectRecord(value, fieldName);
|
|
1434
|
-
if (!suite)
|
|
1435
|
-
return;
|
|
1436
|
-
const id = validateNonEmptyString(suite.id, `${fieldName}.id`);
|
|
1437
|
-
if (id && !PLUGIN_NAME_PATTERN.test(id)) {
|
|
1438
|
-
error(`${fieldName}.id must use lowercase kebab-case`);
|
|
1439
|
-
}
|
|
1440
|
-
else if (id && suiteIds.has(id)) {
|
|
1441
|
-
error(`${manifestField} has duplicate suite id ${JSON.stringify(id)}`);
|
|
1442
|
-
}
|
|
1443
|
-
else if (id) {
|
|
1444
|
-
suiteIds.add(id);
|
|
1445
|
-
}
|
|
1446
|
-
if (suite.kind !== 'command' && suite.kind !== 'conversation' && suite.kind !== 'trigger') {
|
|
1447
|
-
error(`${fieldName}.kind must be command, conversation, or trigger`);
|
|
1448
|
-
}
|
|
1449
|
-
const suiteSkills = new Set();
|
|
1450
|
-
if (!Array.isArray(suite.skills) || suite.skills.length === 0) {
|
|
1451
|
-
error(`${fieldName}.skills must be a non-empty array`);
|
|
1452
|
-
}
|
|
1453
|
-
else {
|
|
1454
|
-
suite.skills.forEach((skillName, skillIndex) => {
|
|
1455
|
-
const skillField = `${fieldName}.skills[${skillIndex}]`;
|
|
1456
|
-
const normalized = validateNonEmptyString(skillName, skillField);
|
|
1457
|
-
if (!normalized)
|
|
1458
|
-
return;
|
|
1459
|
-
if (!PLUGIN_NAME_PATTERN.test(normalized)) {
|
|
1460
|
-
error(`${skillField} must use lowercase kebab-case`);
|
|
1461
|
-
}
|
|
1462
|
-
else if (suiteSkills.has(normalized)) {
|
|
1463
|
-
error(`${fieldName}.skills has duplicate Skill ${JSON.stringify(normalized)}`);
|
|
1464
|
-
}
|
|
1465
|
-
else {
|
|
1466
|
-
suiteSkills.add(normalized);
|
|
1467
|
-
}
|
|
1468
|
-
});
|
|
1469
|
-
}
|
|
1470
|
-
if (skill && !suiteSkills.has(skill)) {
|
|
1471
|
-
error(`${fieldName}.skills must include owner Skill ${JSON.stringify(skill)}`);
|
|
1472
|
-
}
|
|
1473
|
-
if (!Array.isArray(suite.profiles) || suite.profiles.length === 0) {
|
|
1474
|
-
error(`${fieldName}.profiles must be a non-empty array`);
|
|
1475
|
-
}
|
|
1476
|
-
else {
|
|
1477
|
-
suite.profiles.forEach((profileId, profileIndex) => {
|
|
1478
|
-
const profileField = `${fieldName}.profiles[${profileIndex}]`;
|
|
1479
|
-
if (typeof profileId !== 'string' || profileId.trim() === '') {
|
|
1480
|
-
error(`${profileField} must be a non-empty string`);
|
|
1481
|
-
return;
|
|
1482
|
-
}
|
|
1483
|
-
const profile = profilesById.get(profileId);
|
|
1484
|
-
if (!profile) {
|
|
1485
|
-
error(`${fieldName} references unknown profile ${JSON.stringify(profileId)}`);
|
|
1486
|
-
}
|
|
1487
|
-
else if (profile.runModel !== true) {
|
|
1488
|
-
error(`${fieldName} cannot execute materialize-only profile ${JSON.stringify(profileId)}`);
|
|
1489
|
-
}
|
|
1490
|
-
if (profile) {
|
|
1491
|
-
for (const skillName of suiteSkills) {
|
|
1492
|
-
if (!profile.skillOwners.has(skillName)) {
|
|
1493
|
-
error(`${fieldName} requires Skill ${JSON.stringify(skillName)}, but profile ` +
|
|
1494
|
-
`${JSON.stringify(profileId)} does not install a plugin that declares it`);
|
|
1495
|
-
}
|
|
1496
|
-
}
|
|
1497
|
-
}
|
|
1498
|
-
});
|
|
1499
|
-
}
|
|
1500
|
-
if (!Number.isInteger(suite.defaultRuns) || suite.defaultRuns <= 0) {
|
|
1501
|
-
error(`${fieldName}.defaultRuns must be a positive integer`);
|
|
1502
|
-
}
|
|
1503
|
-
if (!Number.isInteger(suite.perRunTimeoutMs) || suite.perRunTimeoutMs <= 0) {
|
|
1504
|
-
error(`${fieldName}.perRunTimeoutMs must be a positive integer`);
|
|
1505
|
-
}
|
|
1506
|
-
if (typeof suite.requiredForRelease !== 'boolean') {
|
|
1507
|
-
error(`${fieldName}.requiredForRelease must be boolean`);
|
|
1508
|
-
}
|
|
1509
|
-
if (!Array.isArray(suite.releaseSplits)) {
|
|
1510
|
-
error(`${fieldName}.releaseSplits must be an array`);
|
|
1511
|
-
}
|
|
1512
|
-
else {
|
|
1513
|
-
suite.releaseSplits.forEach((split, splitIndex) => {
|
|
1514
|
-
if (!PLUGIN_EVAL_SPLITS.has(split)) {
|
|
1515
|
-
error(`${fieldName}.releaseSplits[${splitIndex}] is unsupported`);
|
|
1516
|
-
}
|
|
1517
|
-
});
|
|
1518
|
-
}
|
|
1519
|
-
if (suite.kind === 'command') {
|
|
1520
|
-
validateAllowedKeys(suite, fieldName, [
|
|
1521
|
-
'id', 'kind', 'skills', 'entry', 'cases', 'profiles', 'defaultRuns', 'releaseSplits',
|
|
1522
|
-
'requiredForRelease', 'perRunTimeoutMs', 'suiteTimeoutMs', 'supports',
|
|
1523
|
-
]);
|
|
1524
|
-
validateEvalRelativeFile(evalDir, suite.entry, `${fieldName}.entry`);
|
|
1525
|
-
if (!Number.isInteger(suite.suiteTimeoutMs) || suite.suiteTimeoutMs <= 0) {
|
|
1526
|
-
error(`${fieldName}.suiteTimeoutMs must be a positive integer`);
|
|
1527
|
-
}
|
|
1528
|
-
else if (suite.suiteTimeoutMs < suite.perRunTimeoutMs) {
|
|
1529
|
-
error(`${fieldName}.suiteTimeoutMs must be greater than or equal to perRunTimeoutMs`);
|
|
1530
|
-
}
|
|
1531
|
-
if (!Array.isArray(suite.supports)) {
|
|
1532
|
-
error(`${fieldName}.supports must be an array`);
|
|
1533
|
-
}
|
|
1534
|
-
else {
|
|
1535
|
-
suite.supports.forEach((support, supportIndex) => {
|
|
1536
|
-
if (!PLUGIN_EVAL_COMMAND_SUPPORTS.has(support)) {
|
|
1537
|
-
error(`${fieldName}.supports[${supportIndex}] is unsupported`);
|
|
1538
|
-
}
|
|
1539
|
-
});
|
|
1540
|
-
if (Array.isArray(suite.releaseSplits) &&
|
|
1541
|
-
suite.releaseSplits.length > 0 &&
|
|
1542
|
-
!suite.supports.includes('split')) {
|
|
1543
|
-
error(`${fieldName} declares releaseSplits but does not support split`);
|
|
1544
|
-
}
|
|
1545
|
-
const supportsCase = suite.supports.includes('case');
|
|
1546
|
-
if (supportsCase && suite.cases === undefined) {
|
|
1547
|
-
error(`${fieldName}.cases is required when supports includes case`);
|
|
1548
|
-
}
|
|
1549
|
-
else if (!supportsCase && suite.cases !== undefined) {
|
|
1550
|
-
error(`${fieldName}.cases requires supports to include case`);
|
|
1551
|
-
}
|
|
1552
|
-
if (supportsCase && suite.cases !== undefined) {
|
|
1553
|
-
const casesPath = validateEvalRelativeFile(evalDir, suite.cases, `${fieldName}.cases`);
|
|
1554
|
-
if (casesPath) {
|
|
1555
|
-
try {
|
|
1556
|
-
const casesDocument = JSON.parse(fs.readFileSync(casesPath, 'utf8'));
|
|
1557
|
-
if (!isRecord(casesDocument) || casesDocument.skill_name !== skill) {
|
|
1558
|
-
error(`${fieldName}.cases.skill_name must match ${JSON.stringify(skill)}`);
|
|
1559
|
-
}
|
|
1560
|
-
else if (!Array.isArray(casesDocument.evals) || casesDocument.evals.length === 0) {
|
|
1561
|
-
error(`${fieldName}.cases.evals must be a non-empty array`);
|
|
1562
|
-
}
|
|
1563
|
-
else {
|
|
1564
|
-
const caseIds = new Set();
|
|
1565
|
-
casesDocument.evals.forEach((entry, caseIndex) => {
|
|
1566
|
-
const caseField = `${fieldName}.cases.evals[${caseIndex}]`;
|
|
1567
|
-
if (!isRecord(entry) || typeof entry.id !== 'string' || !/^[a-z0-9_]+$/.test(entry.id)) {
|
|
1568
|
-
error(`${caseField}.id must use lowercase snake_case`);
|
|
1569
|
-
}
|
|
1570
|
-
else if (caseIds.has(entry.id)) {
|
|
1571
|
-
error(`${fieldName}.cases has duplicate case id ${JSON.stringify(entry.id)}`);
|
|
1572
|
-
}
|
|
1573
|
-
else {
|
|
1574
|
-
caseIds.add(entry.id);
|
|
1575
|
-
}
|
|
1576
|
-
});
|
|
1577
|
-
}
|
|
1578
|
-
}
|
|
1579
|
-
catch (cause) {
|
|
1580
|
-
error(`${fieldName}.cases is not valid JSON: ${cause.message}`);
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1583
|
-
}
|
|
1584
|
-
}
|
|
1585
|
-
}
|
|
1586
|
-
else if (suite.kind === 'conversation' || suite.kind === 'trigger') {
|
|
1587
|
-
const normalizedSuite = { ...suite, skill };
|
|
1588
|
-
const isolation = suite.isolation ?? 'standard';
|
|
1589
|
-
if (isolation !== 'standard' && isolation !== 'strict') {
|
|
1590
|
-
error(`${fieldName}.isolation must be standard or strict`);
|
|
1591
|
-
}
|
|
1592
|
-
if (suite.kind === 'conversation') {
|
|
1593
|
-
validateAllowedKeys(suite, fieldName, [
|
|
1594
|
-
'id', 'kind', 'skills', 'cases', 'profiles', 'defaultRuns', 'releaseSplits',
|
|
1595
|
-
'requiredForRelease', 'perRunTimeoutMs', 'isolation',
|
|
1596
|
-
]);
|
|
1597
|
-
if (Array.isArray(suite.releaseSplits) && suite.releaseSplits.length > 0) {
|
|
1598
|
-
error(`${fieldName} cannot declare releaseSplits because conversation cases do not support split filtering`);
|
|
1599
|
-
}
|
|
1600
|
-
validateConversationCaseCorpus(evalDir, normalizedSuite, fieldName);
|
|
1601
|
-
}
|
|
1602
|
-
else {
|
|
1603
|
-
validateAllowedKeys(suite, fieldName, [
|
|
1604
|
-
'id', 'kind', 'skills', 'cases', 'fixtureModule', 'observeSpawnAgent', 'isolation',
|
|
1605
|
-
'strictNegativeSplits', 'minimumPassRate', 'forbidMultipleSkillLoads',
|
|
1606
|
-
'profiles', 'defaultRuns', 'releaseSplits',
|
|
1607
|
-
'requiredForRelease', 'perRunTimeoutMs',
|
|
1608
|
-
]);
|
|
1609
|
-
if (typeof suite.observeSpawnAgent !== 'boolean') {
|
|
1610
|
-
error(`${fieldName}.observeSpawnAgent must be boolean`);
|
|
1611
|
-
}
|
|
1612
|
-
if (!Array.isArray(suite.strictNegativeSplits)) {
|
|
1613
|
-
error(`${fieldName}.strictNegativeSplits must be an array`);
|
|
1614
|
-
}
|
|
1615
|
-
else {
|
|
1616
|
-
suite.strictNegativeSplits.forEach((split, splitIndex) => {
|
|
1617
|
-
if (!PLUGIN_EVAL_TRIGGER_CASE_SPLITS.has(split)) {
|
|
1618
|
-
error(`${fieldName}.strictNegativeSplits[${splitIndex}] is unsupported`);
|
|
1619
|
-
}
|
|
1620
|
-
});
|
|
1621
|
-
}
|
|
1622
|
-
if (suite.minimumPassRate !== undefined &&
|
|
1623
|
-
(typeof suite.minimumPassRate !== 'number' ||
|
|
1624
|
-
!Number.isFinite(suite.minimumPassRate) ||
|
|
1625
|
-
suite.minimumPassRate <= 0 ||
|
|
1626
|
-
suite.minimumPassRate > 1)) {
|
|
1627
|
-
error(`${fieldName}.minimumPassRate must be greater than 0 and at most 1`);
|
|
1628
|
-
}
|
|
1629
|
-
if (suite.forbidMultipleSkillLoads !== undefined &&
|
|
1630
|
-
typeof suite.forbidMultipleSkillLoads !== 'boolean') {
|
|
1631
|
-
error(`${fieldName}.forbidMultipleSkillLoads must be boolean`);
|
|
1632
|
-
}
|
|
1633
|
-
if (suite.fixtureModule !== undefined) {
|
|
1634
|
-
const fixturePath = validateEvalRelativeFile(evalDir, suite.fixtureModule, `${fieldName}.fixtureModule`);
|
|
1635
|
-
if (fixturePath && path.extname(fixturePath) !== '.mjs') {
|
|
1636
|
-
error(`${fieldName}.fixtureModule must reference an .mjs file`);
|
|
1637
|
-
}
|
|
1638
|
-
}
|
|
1639
|
-
validateTriggerCaseCorpus(evalDir, normalizedSuite, fieldName);
|
|
1640
|
-
}
|
|
1641
|
-
}
|
|
1642
|
-
});
|
|
1643
|
-
if (skill
|
|
1644
|
-
&& collectDeclaredSkillNames(pluginDir, pluginManifest).dynamic.has(skill)
|
|
1645
|
-
&& !manifest.suites.some((suite) => isRecord(suite) && suite.requiredForRelease === true)) {
|
|
1646
|
-
error(`${manifestField}.suites must include at least one release-required suite for dynamic Skill ${JSON.stringify(skill)}`);
|
|
1647
|
-
}
|
|
1648
959
|
}
|
|
1649
|
-
function validatePluginEvalManifest(pluginDir, pluginManifest) {
|
|
960
|
+
async function validatePluginEvalManifest(pluginDir, pluginManifest) {
|
|
1650
961
|
const legacyRoot = path.join(pluginDir, 'evals');
|
|
1651
962
|
if (fs.existsSync(legacyRoot)) {
|
|
1652
963
|
error('Plugin-root evals/ is legacy; put evals/ inside its owning static or dynamic Skill directory.');
|
|
@@ -1655,17 +966,33 @@ function validatePluginEvalManifest(pluginDir, pluginManifest) {
|
|
|
1655
966
|
.sort()
|
|
1656
967
|
.map((skill) => ({ skill, root: path.join(pluginDir, 'skills', skill) }));
|
|
1657
968
|
const seenSkills = new Set();
|
|
969
|
+
let manifestTools = null;
|
|
1658
970
|
for (const owner of skillRoots) {
|
|
1659
971
|
const evalDir = path.join(owner.root, 'evals');
|
|
1660
972
|
const manifestPath = path.join(evalDir, 'manifest.json');
|
|
1661
973
|
if (!fs.existsSync(manifestPath))
|
|
1662
974
|
continue;
|
|
975
|
+
const manifestField = path.relative(pluginDir, manifestPath).split(path.sep).join('/');
|
|
1663
976
|
if (seenSkills.has(owner.skill)) {
|
|
1664
977
|
error(`Skill ${JSON.stringify(owner.skill)} has more than one evals/manifest.json owner`);
|
|
1665
978
|
continue;
|
|
1666
979
|
}
|
|
1667
980
|
seenSkills.add(owner.skill);
|
|
1668
|
-
|
|
981
|
+
manifestTools ??= await loadSkillEvalManifestTools();
|
|
982
|
+
if (typeof manifestTools?.validateAwSkillEvalManifest !== 'function') {
|
|
983
|
+
error('Canonical AwSkillEvalManifest v1 validator is unavailable; run the current built aw CLI.');
|
|
984
|
+
continue;
|
|
985
|
+
}
|
|
986
|
+
const validation = await manifestTools.validateAwSkillEvalManifest({
|
|
987
|
+
plugin: {
|
|
988
|
+
rootDir: pluginDir,
|
|
989
|
+
manifestPath: path.join(pluginDir, '.agents-plugin', 'plugin.json'),
|
|
990
|
+
manifest: pluginManifest,
|
|
991
|
+
},
|
|
992
|
+
skill: owner.skill,
|
|
993
|
+
});
|
|
994
|
+
for (const message of validation.errors)
|
|
995
|
+
error(`${manifestField}: ${message}`);
|
|
1669
996
|
}
|
|
1670
997
|
}
|
|
1671
998
|
function countCharacters(value) {
|
|
@@ -2058,33 +1385,6 @@ function validateDeclaredNativeAssets(pluginDir, manifest) {
|
|
|
2058
1385
|
}
|
|
2059
1386
|
}
|
|
2060
1387
|
}
|
|
2061
|
-
function validatePluginVersionContracts(manifest, options) {
|
|
2062
|
-
if (manifest.compatibility === undefined) {
|
|
2063
|
-
error('Invalid compatibility.aw.minVersion: expected a strict SemVer 2.0.0 string. Received undefined.');
|
|
2064
|
-
return;
|
|
2065
|
-
}
|
|
2066
|
-
const compatibility = expectRecord(manifest.compatibility, 'compatibility');
|
|
2067
|
-
if (!compatibility) {
|
|
2068
|
-
return;
|
|
2069
|
-
}
|
|
2070
|
-
validateAllowedKeys(compatibility, 'compatibility', ['aw']);
|
|
2071
|
-
if (compatibility.aw === undefined) {
|
|
2072
|
-
error('Invalid compatibility.aw.minVersion: expected a strict SemVer 2.0.0 string. Received undefined.');
|
|
2073
|
-
return;
|
|
2074
|
-
}
|
|
2075
|
-
const aw = expectRecord(compatibility.aw, 'compatibility.aw');
|
|
2076
|
-
if (!aw) {
|
|
2077
|
-
return;
|
|
2078
|
-
}
|
|
2079
|
-
validateAllowedKeys(aw, 'compatibility.aw', ['minVersion']);
|
|
2080
|
-
const pluginMinVersion = validateStrictSemVerString(aw.minVersion, 'compatibility.aw.minVersion');
|
|
2081
|
-
if (!pluginMinVersion || !options.catalogMinVersion) {
|
|
2082
|
-
return;
|
|
2083
|
-
}
|
|
2084
|
-
if (compareStrictSemVer(pluginMinVersion, options.catalogMinVersion) > 0) {
|
|
2085
|
-
error(`Invalid compatibility.aw.minVersion for plugin "${pluginNameForError(manifest)}": received ${JSON.stringify(pluginMinVersion)}, which is higher than target catalog minimum ${JSON.stringify(options.catalogMinVersion)}.`);
|
|
2086
|
-
}
|
|
2087
|
-
}
|
|
2088
1388
|
function collectDeclaredSkillNames(pluginDir, manifest) {
|
|
2089
1389
|
const skillsRoot = typeof manifest.skills === 'string'
|
|
2090
1390
|
? path.join(pluginDir, manifest.skills)
|
|
@@ -4130,8 +3430,6 @@ function printUsage() {
|
|
|
4130
3430
|
aw plugin validate <plugin-directory> [options]
|
|
4131
3431
|
|
|
4132
3432
|
Options:
|
|
4133
|
-
--catalog-min-version <semver>
|
|
4134
|
-
Validate compatibility.aw.minVersion against a target catalog.
|
|
4135
3433
|
--strict-agent-skills Allow only Agent Skills portable core frontmatter.
|
|
4136
3434
|
--claude-skill-profile <minimum|current>
|
|
4137
3435
|
Validate Claude fields at AW minimum 2.1.88 (default) or current docs.
|
|
@@ -4201,7 +3499,27 @@ async function validatePluginDir(pluginDir, options) {
|
|
|
4201
3499
|
}
|
|
4202
3500
|
const skillValidationTools = await tryLoadCliSkillValidationTools();
|
|
4203
3501
|
try {
|
|
4204
|
-
await cliManifestTools.validateSourcePluginManifest(pluginDir, manifest);
|
|
3502
|
+
const normalizedManifest = await cliManifestTools.validateSourcePluginManifest(pluginDir, manifest);
|
|
3503
|
+
const requirements = await cliManifestTools.resolvePluginRequirements({
|
|
3504
|
+
pluginRoot: pluginDir,
|
|
3505
|
+
manifest: normalizedManifest,
|
|
3506
|
+
implementedCapabilities: cliManifestTools.implementedCapabilities,
|
|
3507
|
+
});
|
|
3508
|
+
if (requirements.unsupportedByCurrentAw.length > 0) {
|
|
3509
|
+
capabilityDiagnostic = {
|
|
3510
|
+
errorCode: 'awCapabilityUnsupported',
|
|
3511
|
+
missingCapabilities: requirements.unsupportedByCurrentAw,
|
|
3512
|
+
availableCapabilities: [...cliManifestTools.implementedCapabilities].sort(),
|
|
3513
|
+
};
|
|
3514
|
+
error(`[awCapabilityUnsupported] missingCapabilities=${JSON.stringify(requirements.unsupportedByCurrentAw)}`);
|
|
3515
|
+
}
|
|
3516
|
+
const satisfiedCapabilities = new Set([...requirements.declared, ...requirements.derived]);
|
|
3517
|
+
const advisoryObservations = requirements.observations.filter((observation) => (observation.code !== 'aw-command-detected'
|
|
3518
|
+
|| observation.capability === undefined
|
|
3519
|
+
|| !satisfiedCapabilities.has(observation.capability)));
|
|
3520
|
+
for (const observation of advisoryObservations) {
|
|
3521
|
+
warn(`[awCapabilityObservation] ${observation.path}: ${observation.message}`);
|
|
3522
|
+
}
|
|
4205
3523
|
}
|
|
4206
3524
|
catch (e) {
|
|
4207
3525
|
error(e.message);
|
|
@@ -4212,8 +3530,7 @@ async function validatePluginDir(pluginDir, options) {
|
|
|
4212
3530
|
validateNoBuildOutputDirs(pluginDir);
|
|
4213
3531
|
validateDeclaredNativeAssets(pluginDir, manifest);
|
|
4214
3532
|
validateCanonicalSkillLayout(pluginDir, manifest);
|
|
4215
|
-
|
|
4216
|
-
validatePluginEvalManifest(pluginDir, manifest);
|
|
3533
|
+
await validatePluginEvalManifest(pluginDir, manifest);
|
|
4217
3534
|
const artifactRenderEntry = validateScriptsLightweight(pluginDir, manifest);
|
|
4218
3535
|
const runtimeImports = await resolveAuthorRuntimeImports({
|
|
4219
3536
|
pluginDir,
|
|
@@ -4263,7 +3580,6 @@ function parseArgs(argv) {
|
|
|
4263
3580
|
const args = argv.slice(2);
|
|
4264
3581
|
const options = {
|
|
4265
3582
|
pluginDir: null,
|
|
4266
|
-
catalogMinVersion: null,
|
|
4267
3583
|
strictAgentSkills: false,
|
|
4268
3584
|
claudeSkillProfile: 'minimum',
|
|
4269
3585
|
versionBase: 'HEAD',
|
|
@@ -4320,18 +3636,6 @@ function parseArgs(argv) {
|
|
|
4320
3636
|
}
|
|
4321
3637
|
exitCliError(`Unknown argument: ${arg}`, options.json);
|
|
4322
3638
|
}
|
|
4323
|
-
if (arg === '--catalog-min-version') {
|
|
4324
|
-
const value = args[index + 1];
|
|
4325
|
-
if (!value || value.startsWith('--')) {
|
|
4326
|
-
exitCliError('--catalog-min-version requires a value.', options.json);
|
|
4327
|
-
}
|
|
4328
|
-
index += 1;
|
|
4329
|
-
if (!STRICT_SEMVER_PATTERN.test(value)) {
|
|
4330
|
-
exitCliError(`Invalid --catalog-min-version: expected a strict SemVer 2.0.0 string. Received ${JSON.stringify(value)}.`, options.json);
|
|
4331
|
-
}
|
|
4332
|
-
options.catalogMinVersion = value;
|
|
4333
|
-
continue;
|
|
4334
|
-
}
|
|
4335
3639
|
exitCliError(`Unknown option: ${arg}`, options.json);
|
|
4336
3640
|
}
|
|
4337
3641
|
return options;
|
|
@@ -4363,7 +3667,6 @@ async function main() {
|
|
|
4363
3667
|
const repoRoot = requestedWorkspaceRoot ?? discoverCanonicalRepoRoot(repoRootCandidate);
|
|
4364
3668
|
const gitRoot = discoverCanonicalRepoRoot(findRepoRootCandidate(resolved));
|
|
4365
3669
|
await validatePluginDir(resolved, {
|
|
4366
|
-
catalogMinVersion: args.catalogMinVersion,
|
|
4367
3670
|
strictAgentSkills: args.strictAgentSkills,
|
|
4368
3671
|
claudeSkillProfile: args.claudeSkillProfile,
|
|
4369
3672
|
versionBase: args.versionBase,
|
|
@@ -4377,9 +3680,12 @@ async function main() {
|
|
|
4377
3680
|
profile: args.strictAgentSkills ? 'agent-skills-portable-core' : 'aw-source',
|
|
4378
3681
|
claudeSkillProfile: args.claudeSkillProfile,
|
|
4379
3682
|
pluginDirectory: resolved,
|
|
3683
|
+
workspaceRoot: process.env.AW_AUTHORING_WORKSPACE_ROOT ?? repoRoot,
|
|
3684
|
+
workspaceRootResolvedFrom: process.env.AW_AUTHORING_WORKSPACE_ROOT_RESOLVED_FROM ?? 'explicit',
|
|
4380
3685
|
versionCheck: pluginVersionCheckResult,
|
|
4381
3686
|
warnings,
|
|
4382
3687
|
errors,
|
|
3688
|
+
...(capabilityDiagnostic ?? {}),
|
|
4383
3689
|
}, null, 2));
|
|
4384
3690
|
if (errors.length > 0) {
|
|
4385
3691
|
process.exit(1);
|