@lesliechan721/aw 0.1.0-beta.6 → 0.1.0-beta.60
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/assets/aw-config.mjs +100 -501
- package/dist/authoring/assets/aw-config.mjs.map +1 -1
- package/dist/authoring/assets/aw-runtime-config.d.mts +13 -0
- package/dist/authoring/assets/aw-runtime-config.mjs +199 -0
- package/dist/authoring/assets/aw-runtime-config.mjs.map +1 -0
- 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 +67 -97
- package/dist/authoring/scripts/init-plugin.mjs.map +1 -1
- package/dist/authoring/scripts/lib/plugin-root.mjs +4 -4
- package/dist/authoring/scripts/lib/plugin-root.mjs.map +1 -1
- package/dist/authoring/scripts/lib/plugin-version-check.d.mts +1 -0
- package/dist/authoring/scripts/lib/plugin-version-check.mjs +229 -8
- package/dist/authoring/scripts/lib/plugin-version-check.mjs.map +1 -1
- package/dist/authoring/scripts/lib/skill-scaffold.d.mts +77 -92
- package/dist/authoring/scripts/lib/skill-scaffold.mjs +99 -124
- 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 +3 -2
- package/dist/authoring/scripts/remove-plugin.mjs.map +1 -1
- package/dist/authoring/scripts/validate-plugin.mjs +552 -1142
- package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
- package/dist/native-runtime/reader-runtime.mjs +34 -0
- package/dist/native-runtime/reader.d.mts +6 -0
- package/dist/native-runtime/reader.mjs +11 -0
- package/dist/scripts/aw-skill-eval-materializer.d.ts +26 -0
- package/dist/scripts/aw-skill-eval-materializer.js +306 -0
- 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 +19 -8
- package/dist/scripts/bundle-plugins.js.map +1 -1
- package/dist/scripts/plugin-staging.d.ts +7 -9
- package/dist/scripts/plugin-staging.js +18 -29
- package/dist/scripts/plugin-staging.js.map +1 -1
- package/dist/scripts/plugin-test-reporter.d.mts +2 -0
- package/dist/scripts/plugin-test-reporter.mjs +21 -0
- package/dist/scripts/plugin-test-reporter.mjs.map +1 -0
- package/dist/scripts/test-plugin-scripts.d.ts +5 -0
- package/dist/scripts/test-plugin-scripts.js +208 -89
- package/dist/scripts/test-plugin-scripts.js.map +1 -1
- package/dist/scripts/test-workers.d.mts +24 -0
- package/dist/scripts/test-workers.mjs +59 -0
- package/dist/scripts/test-workers.mjs.map +1 -0
- package/dist/src/adapters/helpers.d.ts +7 -0
- 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 +19 -1
- package/dist/src/authoring/workspace-commands.js +24 -5
- 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 +266 -0
- package/dist/src/commands/aw-capabilities.js.map +1 -0
- package/dist/src/commands/catalog-dev-source.d.ts +154 -0
- package/dist/src/commands/catalog-dev-source.js +655 -0
- package/dist/src/commands/catalog-dev-source.js.map +1 -0
- package/dist/src/commands/catalog-generation.d.ts +21 -0
- package/dist/src/commands/catalog-generation.js +9 -0
- package/dist/src/commands/catalog-generation.js.map +1 -0
- package/dist/src/commands/catalog-publication-inspection.d.ts +71 -30
- package/dist/src/commands/catalog-publication-inspection.js +475 -63
- package/dist/src/commands/catalog-publication-inspection.js.map +1 -1
- package/dist/src/commands/catalog-publication-journal.d.ts +68 -0
- package/dist/src/commands/catalog-publication-journal.js +292 -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 +73 -15
- package/dist/src/commands/catalog-publish.js +701 -212
- package/dist/src/commands/catalog-publish.js.map +1 -1
- package/dist/src/commands/catalog-source.d.ts +60 -22
- package/dist/src/commands/catalog-source.js +311 -91
- 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-store.d.ts +104 -0
- package/dist/src/commands/catalog-store.js +685 -0
- package/dist/src/commands/catalog-store.js.map +1 -0
- 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/console.js +28 -17
- package/dist/src/commands/console.js.map +1 -1
- package/dist/src/commands/dependency-operation-plan.d.ts +52 -15
- package/dist/src/commands/dependency-operation-plan.js +289 -7
- 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/managed-commands.d.ts +28 -0
- package/dist/src/commands/managed-commands.js +315 -0
- package/dist/src/commands/managed-commands.js.map +1 -0
- package/dist/src/commands/offline-cleanup-evidence.d.ts +67 -0
- package/dist/src/commands/offline-cleanup-evidence.js +181 -0
- package/dist/src/commands/offline-cleanup-evidence.js.map +1 -0
- package/dist/src/commands/operation-plan.d.ts +182 -0
- package/dist/src/commands/operation-plan.js +71 -0
- package/dist/src/commands/operation-plan.js.map +1 -0
- package/dist/src/commands/operation-planner.d.ts +47 -0
- package/dist/src/commands/operation-planner.js +142 -0
- package/dist/src/commands/operation-planner.js.map +1 -0
- package/dist/src/commands/options.d.ts +9 -7
- package/dist/src/commands/options.js +83 -21
- package/dist/src/commands/options.js.map +1 -1
- package/dist/src/commands/plugin-capabilities.js +65 -4
- package/dist/src/commands/plugin-capabilities.js.map +1 -1
- package/dist/src/commands/rebuild-command.d.ts +78 -31
- package/dist/src/commands/rebuild-command.js +5229 -942
- package/dist/src/commands/rebuild-command.js.map +1 -1
- package/dist/src/commands/rebuild-integration.d.ts +143 -21
- package/dist/src/commands/rebuild-integration.js +3384 -1342
- package/dist/src/commands/rebuild-integration.js.map +1 -1
- package/dist/src/commands/rebuild-operations.d.ts +60 -30
- package/dist/src/commands/rebuild-operations.js +267 -320
- package/dist/src/commands/rebuild-operations.js.map +1 -1
- package/dist/src/commands/reconcile-operation-plan.d.ts +28 -0
- package/dist/src/commands/reconcile-operation-plan.js +47 -0
- package/dist/src/commands/reconcile-operation-plan.js.map +1 -0
- package/dist/src/commands/review-inspection.d.ts +60 -0
- package/dist/src/commands/review-inspection.js +422 -0
- package/dist/src/commands/review-inspection.js.map +1 -0
- package/dist/src/commands/review-preparation.d.ts +49 -0
- package/dist/src/commands/review-preparation.js +427 -0
- package/dist/src/commands/review-preparation.js.map +1 -0
- package/dist/src/commands/runtime.d.ts +1 -5
- package/dist/src/commands/runtime.js +27 -53
- package/dist/src/commands/runtime.js.map +1 -1
- package/dist/src/commands/skill-eval.d.ts +39 -0
- package/dist/src/commands/skill-eval.js +400 -0
- package/dist/src/commands/skill-eval.js.map +1 -0
- package/dist/src/commands/status-v6.d.ts +279 -0
- package/dist/src/commands/status-v6.js +2785 -0
- package/dist/src/commands/status-v6.js.map +1 -0
- package/dist/src/commands/sync-command.d.ts +40 -0
- package/dist/src/commands/sync-command.js +254 -0
- package/dist/src/commands/sync-command.js.map +1 -0
- package/dist/src/commands/wizard.d.ts +8 -5
- package/dist/src/commands/wizard.js +6 -4
- package/dist/src/commands/wizard.js.map +1 -1
- package/dist/src/commands/write-commands.d.ts +9 -3
- package/dist/src/commands/write-commands.js +136 -31
- package/dist/src/commands/write-commands.js.map +1 -1
- package/dist/src/constants.d.ts +7 -2
- package/dist/src/constants.js +7 -2
- package/dist/src/constants.js.map +1 -1
- package/dist/src/eval/manifest-v1.d.ts +68 -0
- package/dist/src/eval/manifest-v1.js +353 -0
- package/dist/src/eval/manifest-v1.js.map +1 -0
- package/dist/src/generators/local-plugin.d.ts +5 -1
- package/dist/src/generators/local-plugin.js +261 -109
- package/dist/src/generators/local-plugin.js.map +1 -1
- package/dist/src/generators/permissions.d.ts +5 -2
- package/dist/src/generators/permissions.js +28 -15
- package/dist/src/generators/permissions.js.map +1 -1
- package/dist/src/index.d.ts +10 -4
- package/dist/src/index.js +1774 -609
- package/dist/src/index.js.map +1 -1
- package/dist/src/output/command-receipt.d.ts +47 -46
- package/dist/src/output/command-receipt.js +277 -79
- 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 +15 -13
- package/dist/src/output/rebuild-progress.js.map +1 -1
- package/dist/src/portable/commands.d.ts +46 -7
- package/dist/src/portable/commands.js +349 -37
- package/dist/src/portable/commands.js.map +1 -1
- package/dist/src/portable/materializer.d.ts +17 -4
- package/dist/src/portable/materializer.js +186 -41
- package/dist/src/portable/materializer.js.map +1 -1
- package/dist/src/portable/skills-cli.d.ts +15 -7
- package/dist/src/portable/skills-cli.js +74 -20
- package/dist/src/portable/skills-cli.js.map +1 -1
- package/dist/src/review/eval-source-input.d.ts +43 -0
- package/dist/src/review/eval-source-input.js +207 -0
- package/dist/src/review/eval-source-input.js.map +1 -0
- package/dist/src/review/review-profile.d.ts +17 -0
- package/dist/src/review/review-profile.js +234 -0
- package/dist/src/review/review-profile.js.map +1 -0
- package/dist/src/review/review-selection.d.ts +30 -0
- package/dist/src/review/review-selection.js +527 -0
- package/dist/src/review/review-selection.js.map +1 -0
- package/dist/src/review/review-types.d.ts +68 -0
- package/dist/src/review/review-types.js +16 -0
- package/dist/src/review/review-types.js.map +1 -0
- package/dist/src/review/skill-materialization.d.ts +26 -0
- package/dist/src/review/skill-materialization.js +217 -0
- package/dist/src/review/skill-materialization.js.map +1 -0
- package/dist/src/review/source-binding.d.ts +183 -0
- package/dist/src/review/source-binding.js +708 -0
- package/dist/src/review/source-binding.js.map +1 -0
- package/dist/src/tui/catalog-freshness.d.ts +1 -0
- package/dist/src/tui/catalog-freshness.js +2 -0
- package/dist/src/tui/catalog-freshness.js.map +1 -0
- package/dist/src/tui/catalog-generation.d.ts +21 -0
- package/dist/src/tui/catalog-generation.js +156 -0
- package/dist/src/tui/catalog-generation.js.map +1 -0
- package/dist/src/tui/catalog-sources/projection.d.ts +4 -0
- package/dist/src/tui/catalog-sources/projection.js +17 -0
- package/dist/src/tui/catalog-sources/projection.js.map +1 -0
- package/dist/src/tui/catalog-sources/review.d.ts +3 -0
- package/dist/src/tui/catalog-sources/review.js +13 -0
- package/dist/src/tui/catalog-sources/review.js.map +1 -0
- package/dist/src/tui/catalog-sources/screens.d.ts +12 -0
- package/dist/src/tui/catalog-sources/screens.js +40 -0
- package/dist/src/tui/catalog-sources/screens.js.map +1 -0
- package/dist/src/tui/catalog-sources/session.d.ts +32 -0
- package/dist/src/tui/catalog-sources/session.js +300 -0
- package/dist/src/tui/catalog-sources/session.js.map +1 -0
- package/dist/src/tui/catalog-sources/types.d.ts +54 -0
- package/dist/src/tui/catalog-sources/types.js +2 -0
- package/dist/src/tui/catalog-sources/types.js.map +1 -0
- package/dist/src/tui/catalog-store-task.d.ts +16 -0
- package/dist/src/tui/catalog-store-task.js +19 -0
- package/dist/src/tui/catalog-store-task.js.map +1 -0
- package/dist/src/tui/clipboard.d.ts +13 -0
- package/dist/src/tui/clipboard.js +95 -0
- package/dist/src/tui/clipboard.js.map +1 -0
- package/dist/src/tui/components.d.ts +110 -3
- package/dist/src/tui/components.js +183 -36
- package/dist/src/tui/components.js.map +1 -1
- package/dist/src/tui/config-array-draft.d.ts +29 -0
- package/dist/src/tui/config-array-draft.js +48 -0
- package/dist/src/tui/config-array-draft.js.map +1 -0
- package/dist/src/tui/config-leaf-editor.d.ts +19 -0
- package/dist/src/tui/config-leaf-editor.js +212 -0
- package/dist/src/tui/config-leaf-editor.js.map +1 -0
- package/dist/src/tui/config-references.d.ts +34 -0
- package/dist/src/tui/config-references.js +70 -0
- package/dist/src/tui/config-references.js.map +1 -0
- package/dist/src/tui/config-value-presentation.d.ts +20 -0
- package/dist/src/tui/config-value-presentation.js +104 -0
- package/dist/src/tui/config-value-presentation.js.map +1 -0
- package/dist/src/tui/console-app-v4.d.ts +14 -4
- package/dist/src/tui/console-app-v4.js +9205 -1951
- package/dist/src/tui/console-app-v4.js.map +1 -1
- package/dist/src/tui/content-item-card.d.ts +13 -0
- package/dist/src/tui/content-item-card.js +51 -0
- package/dist/src/tui/content-item-card.js.map +1 -0
- package/dist/src/tui/controller-runtime.d.ts +10 -2
- package/dist/src/tui/controller-runtime.js +3289 -388
- package/dist/src/tui/controller-runtime.js.map +1 -1
- package/dist/src/tui/controller.d.ts +622 -45
- package/dist/src/tui/controller.js +3589 -768
- package/dist/src/tui/controller.js.map +1 -1
- package/dist/src/tui/dependency-graph-view.d.ts +39 -0
- package/dist/src/tui/dependency-graph-view.js +121 -0
- package/dist/src/tui/dependency-graph-view.js.map +1 -0
- package/dist/src/tui/dependency-graph.d.ts +81 -0
- package/dist/src/tui/dependency-graph.js +452 -0
- package/dist/src/tui/dependency-graph.js.map +1 -0
- package/dist/src/tui/diagnostic-projection.d.ts +23 -0
- package/dist/src/tui/diagnostic-projection.js +595 -0
- package/dist/src/tui/diagnostic-projection.js.map +1 -0
- package/dist/src/tui/diagnostics.d.ts +3 -3
- package/dist/src/tui/diagnostics.js +14 -2
- package/dist/src/tui/diagnostics.js.map +1 -1
- package/dist/src/tui/entity-cards.d.ts +55 -0
- package/dist/src/tui/entity-cards.js +220 -0
- package/dist/src/tui/entity-cards.js.map +1 -0
- package/dist/src/tui/glossary.d.ts +23 -0
- package/dist/src/tui/glossary.js +42 -0
- package/dist/src/tui/glossary.js.map +1 -0
- package/dist/src/tui/index.d.ts +6 -1
- package/dist/src/tui/index.js +5 -1
- package/dist/src/tui/index.js.map +1 -1
- package/dist/src/tui/input-display.d.ts +9 -0
- package/dist/src/tui/input-display.js +58 -0
- package/dist/src/tui/input-display.js.map +1 -0
- package/dist/src/tui/keys.d.ts +44 -0
- package/dist/src/tui/keys.js +43 -15
- package/dist/src/tui/keys.js.map +1 -1
- package/dist/src/tui/lifecycle-action-view.d.ts +6 -0
- package/dist/src/tui/lifecycle-action-view.js +36 -0
- package/dist/src/tui/lifecycle-action-view.js.map +1 -0
- package/dist/src/tui/model.d.ts +224 -31
- package/dist/src/tui/model.js +1510 -397
- package/dist/src/tui/model.js.map +1 -1
- package/dist/src/tui/operation-diagnostic-screens.d.ts +8 -0
- package/dist/src/tui/operation-diagnostic-screens.js +71 -0
- package/dist/src/tui/operation-diagnostic-screens.js.map +1 -0
- package/dist/src/tui/operation-history.d.ts +81 -0
- package/dist/src/tui/operation-history.js +136 -0
- package/dist/src/tui/operation-history.js.map +1 -0
- package/dist/src/tui/operation-review-planner.d.ts +21 -0
- package/dist/src/tui/operation-review-planner.js +1392 -0
- package/dist/src/tui/operation-review-planner.js.map +1 -0
- package/dist/src/tui/plugin-content.d.ts +31 -0
- package/dist/src/tui/plugin-content.js +680 -0
- package/dist/src/tui/plugin-content.js.map +1 -0
- package/dist/src/tui/plugin-detail.d.ts +52 -0
- package/dist/src/tui/plugin-detail.js +165 -0
- package/dist/src/tui/plugin-detail.js.map +1 -0
- package/dist/src/tui/plugin-write-task.d.ts +69 -0
- package/dist/src/tui/plugin-write-task.js +286 -0
- package/dist/src/tui/plugin-write-task.js.map +1 -0
- package/dist/src/tui/project-inventory.d.ts +68 -0
- package/dist/src/tui/project-inventory.js +203 -0
- package/dist/src/tui/project-inventory.js.map +1 -0
- package/dist/src/tui/prompt.js +4 -15
- package/dist/src/tui/prompt.js.map +1 -1
- package/dist/src/tui/query-header.d.ts +30 -0
- package/dist/src/tui/query-header.js +40 -0
- package/dist/src/tui/query-header.js.map +1 -0
- package/dist/src/tui/reader.d.ts +37 -0
- package/dist/src/tui/reader.js +47 -0
- package/dist/src/tui/reader.js.map +1 -0
- package/dist/src/tui/router.d.ts +197 -10
- package/dist/src/tui/router.js +338 -50
- package/dist/src/tui/router.js.map +1 -1
- package/dist/src/tui/screen-registry.d.ts +701 -0
- package/dist/src/tui/screen-registry.js +230 -0
- package/dist/src/tui/screen-registry.js.map +1 -0
- package/dist/src/tui/session-console-app.d.ts +6 -0
- package/dist/src/tui/session-console-app.js +13 -0
- package/dist/src/tui/session-console-app.js.map +1 -0
- package/dist/src/tui/session-host.d.ts +45 -0
- package/dist/src/tui/session-host.js +209 -0
- package/dist/src/tui/session-host.js.map +1 -0
- package/dist/src/tui/source-detail-card.d.ts +29 -0
- package/dist/src/tui/source-detail-card.js +45 -0
- package/dist/src/tui/source-detail-card.js.map +1 -0
- 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/theme.d.ts +12 -0
- package/dist/src/tui/theme.js +12 -0
- package/dist/src/tui/theme.js.map +1 -1
- package/dist/src/tui/viewport.d.ts +1 -0
- package/dist/src/tui/viewport.js +23 -0
- package/dist/src/tui/viewport.js.map +1 -1
- package/dist/src/tui/views.js +31 -3
- package/dist/src/tui/views.js.map +1 -1
- package/dist/src/tui/wizard.js +37 -13
- package/dist/src/tui/wizard.js.map +1 -1
- package/dist/src/types.d.ts +264 -21
- package/dist/src/types.js +1 -0
- 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 +36 -7
- package/dist/src/utils/agent-version.js.map +1 -1
- package/dist/src/utils/artifact-plan.d.ts +8 -6
- package/dist/src/utils/artifact-plan.js +67 -13
- package/dist/src/utils/artifact-plan.js.map +1 -1
- package/dist/src/utils/artifact-renderer.d.ts +24 -10
- package/dist/src/utils/artifact-renderer.js +230 -220
- package/dist/src/utils/artifact-renderer.js.map +1 -1
- package/dist/src/utils/artifact-request.d.ts +32 -0
- package/dist/src/utils/artifact-request.js +90 -0
- package/dist/src/utils/artifact-request.js.map +1 -0
- package/dist/src/utils/artifact-sources.d.ts +4 -0
- package/dist/src/utils/artifact-sources.js +17 -0
- package/dist/src/utils/artifact-sources.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 +29 -29
- package/dist/src/utils/bundle-layout.js.map +1 -1
- package/dist/src/utils/catalog-capability-projection.d.ts +8 -0
- package/dist/src/utils/catalog-capability-projection.js +14 -0
- package/dist/src/utils/catalog-capability-projection.js.map +1 -0
- package/dist/src/utils/catalog-digests.d.ts +10 -0
- package/dist/src/utils/catalog-digests.js +57 -20
- 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 +16 -0
- package/dist/src/utils/catalog-generation-error.js +30 -0
- package/dist/src/utils/catalog-generation-error.js.map +1 -0
- package/dist/src/utils/catalog-generation-resolver.d.ts +44 -0
- package/dist/src/utils/catalog-generation-resolver.js +273 -0
- package/dist/src/utils/catalog-generation-resolver.js.map +1 -0
- package/dist/src/utils/catalog-generation-service.d.ts +172 -0
- package/dist/src/utils/catalog-generation-service.js +1271 -0
- package/dist/src/utils/catalog-generation-service.js.map +1 -0
- package/dist/src/utils/catalog-generation-state.d.ts +22 -0
- package/dist/src/utils/catalog-generation-state.js +192 -0
- package/dist/src/utils/catalog-generation-state.js.map +1 -0
- package/dist/src/utils/catalog-generation-store.d.ts +58 -0
- package/dist/src/utils/catalog-generation-store.js +452 -0
- package/dist/src/utils/catalog-generation-store.js.map +1 -0
- package/dist/src/utils/catalog-manifest-v7.d.ts +5 -0
- package/dist/src/utils/{catalog-manifest-v4.js → catalog-manifest-v7.js} +35 -43
- package/dist/src/utils/catalog-manifest-v7.js.map +1 -0
- package/dist/src/utils/catalog-operation-context.d.ts +20 -0
- package/dist/src/utils/catalog-operation-context.js +142 -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 +28 -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-impact.d.ts +21 -0
- package/dist/src/utils/catalog-source-impact.js +344 -0
- package/dist/src/utils/catalog-source-impact.js.map +1 -0
- package/dist/src/utils/catalog-source-references.d.ts +3 -2
- package/dist/src/utils/catalog-source-references.js +195 -347
- package/dist/src/utils/catalog-source-references.js.map +1 -1
- package/dist/src/utils/catalog-sources.d.ts +15 -1
- package/dist/src/utils/catalog-sources.js +206 -27
- package/dist/src/utils/catalog-sources.js.map +1 -1
- package/dist/src/utils/claude-skills-root.d.ts +8 -0
- package/dist/src/utils/claude-skills-root.js +76 -0
- package/dist/src/utils/claude-skills-root.js.map +1 -0
- 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 +79 -0
- package/dist/src/utils/codex-manager.js +735 -0
- package/dist/src/utils/codex-manager.js.map +1 -0
- package/dist/src/utils/codex-marketplace-path.d.ts +5 -0
- package/dist/src/utils/codex-marketplace-path.js +45 -0
- package/dist/src/utils/codex-marketplace-path.js.map +1 -0
- package/dist/src/utils/codex-native-observation.d.ts +49 -0
- package/dist/src/utils/codex-native-observation.js +154 -0
- package/dist/src/utils/codex-native-observation.js.map +1 -0
- package/dist/src/utils/codex-native-render-receipt.d.ts +40 -0
- package/dist/src/utils/codex-native-render-receipt.js +205 -0
- package/dist/src/utils/codex-native-render-receipt.js.map +1 -0
- package/dist/src/utils/codex-reconciler.d.ts +126 -0
- package/dist/src/utils/codex-reconciler.js +298 -0
- package/dist/src/utils/codex-reconciler.js.map +1 -0
- package/dist/src/utils/codex-shared-native.d.ts +43 -0
- package/dist/src/utils/codex-shared-native.js +81 -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-skills-root.d.ts +4 -0
- package/dist/src/utils/codex-skills-root.js +25 -0
- package/dist/src/utils/codex-skills-root.js.map +1 -0
- package/dist/src/utils/codex-surface-analysis.d.ts +46 -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 +5 -0
- package/dist/src/utils/dynamic-skill-tree.js +56 -7
- package/dist/src/utils/dynamic-skill-tree.js.map +1 -1
- package/dist/src/utils/git-catalog-provider.d.ts +16 -0
- package/dist/src/utils/git-catalog-provider.js +177 -145
- package/dist/src/utils/git-catalog-provider.js.map +1 -1
- package/dist/src/utils/local-marketplace.d.ts +27 -1
- package/dist/src/utils/local-marketplace.js +434 -84
- package/dist/src/utils/local-marketplace.js.map +1 -1
- package/dist/src/utils/managed-command-closure.d.ts +3 -0
- package/dist/src/utils/managed-command-closure.js +35 -0
- package/dist/src/utils/managed-command-closure.js.map +1 -0
- package/dist/src/utils/managed-command-evidence.d.ts +7 -0
- package/dist/src/utils/managed-command-evidence.js +86 -0
- package/dist/src/utils/managed-command-evidence.js.map +1 -0
- package/dist/src/utils/managed-command-materialization.d.ts +115 -0
- package/dist/src/utils/managed-command-materialization.js +359 -0
- package/dist/src/utils/managed-command-materialization.js.map +1 -0
- package/dist/src/utils/managed-command-runtime.d.ts +33 -0
- package/dist/src/utils/managed-command-runtime.js +121 -0
- package/dist/src/utils/managed-command-runtime.js.map +1 -0
- package/dist/src/utils/managed-command-store.d.ts +128 -0
- package/dist/src/utils/managed-command-store.js +589 -0
- package/dist/src/utils/managed-command-store.js.map +1 -0
- 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 +54 -110
- package/dist/src/utils/multi-source-catalog.js.map +1 -1
- package/dist/src/utils/native-cli.d.ts +61 -7
- package/dist/src/utils/native-cli.js +426 -58
- package/dist/src/utils/native-cli.js.map +1 -1
- package/dist/src/utils/native-tool-context.d.ts +14 -0
- package/dist/src/utils/native-tool-context.js +38 -0
- package/dist/src/utils/native-tool-context.js.map +1 -0
- package/dist/src/utils/native-tool-review.d.ts +21 -0
- package/dist/src/utils/native-tool-review.js +102 -0
- package/dist/src/utils/native-tool-review.js.map +1 -0
- package/dist/src/utils/orphan-marketplace-cleanup.js +0 -4
- package/dist/src/utils/orphan-marketplace-cleanup.js.map +1 -1
- package/dist/src/utils/ownership.d.ts +88 -20
- package/dist/src/utils/ownership.js +185 -223
- 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 +54 -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-impact.d.ts +40 -0
- package/dist/src/utils/plugin-config-impact.js +373 -0
- package/dist/src/utils/plugin-config-impact.js.map +1 -0
- package/dist/src/utils/plugin-config-write.d.ts +52 -27
- package/dist/src/utils/plugin-config-write.js +1112 -298
- package/dist/src/utils/plugin-config-write.js.map +1 -1
- package/dist/src/utils/plugin-config.d.ts +153 -93
- package/dist/src/utils/plugin-config.js +1116 -968
- package/dist/src/utils/plugin-config.js.map +1 -1
- package/dist/src/utils/plugin-intent-state.d.ts +49 -0
- package/dist/src/utils/plugin-intent-state.js +320 -0
- package/dist/src/utils/plugin-intent-state.js.map +1 -0
- package/dist/src/utils/plugin-scripts.d.ts +66 -125
- package/dist/src/utils/plugin-scripts.js +518 -526
- package/dist/src/utils/plugin-scripts.js.map +1 -1
- package/dist/src/utils/process-lock.js +14 -5
- package/dist/src/utils/process-lock.js.map +1 -1
- package/dist/src/utils/process-tree.d.ts +2 -2
- package/dist/src/utils/process-tree.js +1 -1
- package/dist/src/utils/process-tree.js.map +1 -1
- package/dist/src/utils/project-boundary.d.ts +11 -13
- package/dist/src/utils/project-boundary.js +28 -34
- package/dist/src/utils/project-boundary.js.map +1 -1
- package/dist/src/utils/rendered-contract.js +18 -1
- package/dist/src/utils/rendered-contract.js.map +1 -1
- package/dist/src/utils/runtime-config.d.ts +20 -0
- package/dist/src/utils/runtime-config.js +75 -0
- package/dist/src/utils/runtime-config.js.map +1 -0
- package/dist/src/utils/runtime-skill-closure.js +47 -6
- package/dist/src/utils/runtime-skill-closure.js.map +1 -1
- package/dist/src/utils/safe-fs.d.ts +21 -0
- package/dist/src/utils/safe-fs.js +111 -0
- package/dist/src/utils/safe-fs.js.map +1 -0
- package/dist/src/utils/safe-operation-diagnostic.d.ts +15 -0
- package/dist/src/utils/safe-operation-diagnostic.js +33 -0
- package/dist/src/utils/safe-operation-diagnostic.js.map +1 -0
- package/dist/src/utils/sensitive-content.d.ts +4 -0
- package/dist/src/utils/sensitive-content.js +36 -0
- package/dist/src/utils/sensitive-content.js.map +1 -0
- 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 +17 -8
- package/dist/src/utils/target-eligibility.js.map +1 -1
- package/dist/src/utils/windows-claude-shim.d.ts +7 -0
- package/dist/src/utils/windows-claude-shim.js +10 -0
- package/dist/src/utils/windows-claude-shim.js.map +1 -0
- package/dist/src/utils/windows-npm-shim.d.ts +14 -0
- package/dist/src/utils/windows-npm-shim.js +140 -0
- package/dist/src/utils/windows-npm-shim.js.map +1 -0
- package/dist/vitest.config.js +100 -9
- package/dist/vitest.config.js.map +1 -1
- package/package.json +31 -21
- 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 +0 -421
- 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/review-publication.d.ts +0 -99
- package/dist/src/commands/review-publication.js +0 -904
- package/dist/src/commands/review-publication.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/lifecycle-active-store.d.ts +0 -175
- package/dist/src/utils/lifecycle-active-store.js +0 -1276
- package/dist/src/utils/lifecycle-active-store.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
|
@@ -6,7 +6,7 @@ import { createRequire } from 'node:module';
|
|
|
6
6
|
import * as os from 'node:os';
|
|
7
7
|
import * as path from 'node:path';
|
|
8
8
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
9
|
-
import { getPluginAuthoringAcceptedValues as getCorePluginAuthoringAcceptedValues, getSkillFrontmatterCapabilityRegistry, } from '@lesliechan721/aw-plugin-core/manifest';
|
|
9
|
+
import { AW_RUNTIME_CONFIG_READER_PATH, createNativeToolReaderSource, createRuntimeConfigSnapshot, getPluginAuthoringAcceptedValues as getCorePluginAuthoringAcceptedValues, getSkillFrontmatterCapabilityRegistry, getManagedCommandArtifactTargets, normalizePluginConfigDefinition, parseManagedCommandPlaceholders, replaceManagedCommandPlaceholders, validateCommandEntryFiles, } from '@lesliechan721/aw-plugin-core/manifest';
|
|
10
10
|
import { checkPluginVersions } from './lib/plugin-version-check.mjs';
|
|
11
11
|
import { scanJavaScriptImportSpecifiers } from './lib/javascript-import-scanner.mjs';
|
|
12
12
|
import { assertAuthoringPluginPathFence, assertCanonicalAuthoringPluginRoot, } from './lib/plugin-root.mjs';
|
|
@@ -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 = 18;
|
|
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;
|
|
@@ -124,13 +125,8 @@ const TEMPLATE_TEXT_FILE_EXTENSIONS = /\.(md|markdown|txt|json|yaml|yml|toml|rul
|
|
|
124
125
|
const SLASH_COMMAND_NAME_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
125
126
|
const DEFAULT_SLASH_COMMANDS_DIR = 'slash-commands';
|
|
126
127
|
const SCRIPT_NODE_EXTENSIONS = new Set(['.js', '.mjs', '.cjs']);
|
|
127
|
-
const SCRIPT_AUTO_EXTENSIONS = new Set(['.js', '.mjs', '.cjs', '.sh', '.ps1', '.cmd', '.bat']);
|
|
128
|
-
const SCRIPT_RUNNERS = new Set(['node', 'sh', 'bash', 'pwsh', 'powershell', 'cmd', 'auto']);
|
|
129
|
-
const SCRIPT_OS_SELECTORS = new Set(['default', 'unix', 'windows', 'linux', 'darwin', 'win32']);
|
|
130
|
-
const SCRIPT_LIFECYCLE_EVENTS = new Set(['install', 'upgrade', 'uninstall']);
|
|
131
128
|
const SCRIPT_TIMEOUT_MIN_MS = 1000;
|
|
132
|
-
const SCRIPT_TIMEOUT_MAX_MS =
|
|
133
|
-
const CONFIG_FIELD_TYPES = new Set(getPluginAuthoringAcceptedValues('config', 'config.schema.<key>.type'));
|
|
129
|
+
const SCRIPT_TIMEOUT_MAX_MS = 1800000;
|
|
134
130
|
const ARTIFACT_TARGETS = new Set(getPluginAuthoringAcceptedValues('artifacts', 'artifacts.targets[].target'));
|
|
135
131
|
const ARTIFACT_STATES = new Set(getPluginAuthoringAcceptedValues('artifacts', 'artifacts.targets[].states'));
|
|
136
132
|
const ARTIFACT_SCOPES = new Set(['user', 'project']);
|
|
@@ -149,22 +145,9 @@ const ARTIFACT_PROJECT_MANAGED_KIND_BY_TARGET = {
|
|
|
149
145
|
'codex.skill': 'skill',
|
|
150
146
|
};
|
|
151
147
|
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
148
|
let yamlParser = null;
|
|
167
149
|
let pluginVersionCheckResult = null;
|
|
150
|
+
let skillEvalManifestTools = null;
|
|
168
151
|
function error(msg) {
|
|
169
152
|
errors.push(msg);
|
|
170
153
|
}
|
|
@@ -331,15 +314,26 @@ function loadSkillFrontmatterCapabilityRegistry() {
|
|
|
331
314
|
async function tryLoadCliManifestTools() {
|
|
332
315
|
const manifestModulePath = path.join(CLI_PACKAGE_ROOT, 'dist', 'src', 'utils', 'manifest.js');
|
|
333
316
|
const runtimeProjectionModulePath = path.join(CLI_PACKAGE_ROOT, 'dist', 'src', 'utils', 'catalog-runtime-projection.js');
|
|
334
|
-
|
|
317
|
+
const capabilityModulePath = path.join(CLI_PACKAGE_ROOT, 'dist', 'src', 'commands', 'aw-capabilities.js');
|
|
318
|
+
const artifactPlanPath = path.join(CLI_PACKAGE_ROOT, 'dist', 'src', 'utils', 'artifact-plan.js');
|
|
319
|
+
const artifactRequestPath = path.join(CLI_PACKAGE_ROOT, 'dist', 'src', 'utils', 'artifact-request.js');
|
|
320
|
+
const commandMaterializationPath = path.join(CLI_PACKAGE_ROOT, 'dist', 'src', 'utils', 'managed-command-materialization.js');
|
|
321
|
+
if (!fs.existsSync(manifestModulePath)
|
|
322
|
+
|| !fs.existsSync(runtimeProjectionModulePath)
|
|
323
|
+
|| !fs.existsSync(capabilityModulePath))
|
|
335
324
|
return null;
|
|
336
325
|
try {
|
|
337
|
-
const [manifestModule, runtimeProjectionModule] = await Promise.all([
|
|
326
|
+
const [manifestModule, runtimeProjectionModule, capabilityModule, artifactPlanModule, artifactRequestModule, commandModule] = await Promise.all([
|
|
338
327
|
import(pathToFileURL(manifestModulePath).href),
|
|
339
328
|
import(pathToFileURL(runtimeProjectionModulePath).href),
|
|
329
|
+
import(pathToFileURL(capabilityModulePath).href),
|
|
330
|
+
import(pathToFileURL(artifactPlanPath).href),
|
|
331
|
+
import(pathToFileURL(artifactRequestPath).href),
|
|
332
|
+
import(pathToFileURL(commandMaterializationPath).href),
|
|
340
333
|
]);
|
|
341
334
|
if (manifestModule.SOURCE_PLUGIN_MANIFEST_CONTRACT_VERSION
|
|
342
|
-
!== SOURCE_PLUGIN_MANIFEST_CONTRACT_VERSION
|
|
335
|
+
!== SOURCE_PLUGIN_MANIFEST_CONTRACT_VERSION
|
|
336
|
+
|| typeof artifactRequestModule.requiresNonSensitiveArtifactInput !== 'function') {
|
|
343
337
|
return null;
|
|
344
338
|
}
|
|
345
339
|
return {
|
|
@@ -347,7 +341,14 @@ async function tryLoadCliManifestTools() {
|
|
|
347
341
|
validateSourcePluginManifest: manifestModule.validateSourcePluginManifest ?? null,
|
|
348
342
|
readPluginManifest: manifestModule.readPluginManifest ?? null,
|
|
349
343
|
discoverPlugins: manifestModule.discoverPlugins ?? null,
|
|
344
|
+
resolvePluginRequirements: manifestModule.resolvePluginRequirements ?? null,
|
|
345
|
+
implementedCapabilities: capabilityModule.IMPLEMENTED_AW_CAPABILITIES ?? null,
|
|
350
346
|
createRuntimeResolver: runtimeProjectionModule.createCatalogRuntimeImportResolver ?? null,
|
|
347
|
+
validateArtifactPlan: artifactPlanModule.validateArtifactPlan,
|
|
348
|
+
requiresNonSensitiveArtifactInput: artifactRequestModule.requiresNonSensitiveArtifactInput,
|
|
349
|
+
prepareManagedCommandPackageFiles: commandModule.prepareManagedCommandPackageFiles,
|
|
350
|
+
managedCommandProducerIdentity: commandModule.managedCommandProducerIdentity,
|
|
351
|
+
createManagedCommandReferenceResolver: commandModule.createManagedCommandReferenceResolver,
|
|
351
352
|
};
|
|
352
353
|
}
|
|
353
354
|
catch {
|
|
@@ -498,11 +499,12 @@ function validateManifestLightweight(manifest) {
|
|
|
498
499
|
error('top-level "commands" has been removed; use "slashCommands" instead');
|
|
499
500
|
}
|
|
500
501
|
validateAllowedKeys(input, 'plugin manifest', [
|
|
502
|
+
'schemaVersion',
|
|
501
503
|
'name',
|
|
502
504
|
'version',
|
|
503
505
|
'description',
|
|
504
506
|
'dependencies',
|
|
505
|
-
'
|
|
507
|
+
'requires',
|
|
506
508
|
'skills',
|
|
507
509
|
'slashCommands',
|
|
508
510
|
'mcpServers',
|
|
@@ -515,6 +517,7 @@ function validateManifestLightweight(manifest) {
|
|
|
515
517
|
'scripts',
|
|
516
518
|
'config',
|
|
517
519
|
'artifacts',
|
|
520
|
+
'managedCommands',
|
|
518
521
|
]);
|
|
519
522
|
if (manifest.dependencies !== undefined) {
|
|
520
523
|
if (!Array.isArray(manifest.dependencies)) {
|
|
@@ -613,7 +616,7 @@ function validatePortableNativeArtifactCapabilityLightweight(manifest) {
|
|
|
613
616
|
&& Array.isArray(target.states)
|
|
614
617
|
&& target.states.includes('present'));
|
|
615
618
|
if (!hasOtherNativeDeclaration && !hasPresentNativeArtifact) {
|
|
616
|
-
const message = 'Invalid plugin manifest: portable-only plugins are not supported
|
|
619
|
+
const message = 'Invalid plugin manifest: portable-only plugins are not supported. Declare at least one non-empty native capability.';
|
|
617
620
|
if (!errors.includes(message))
|
|
618
621
|
error(message);
|
|
619
622
|
}
|
|
@@ -950,703 +953,19 @@ function validateNonEmptyString(value, fieldName, maxLength) {
|
|
|
950
953
|
}
|
|
951
954
|
return trimmed;
|
|
952
955
|
}
|
|
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;
|
|
956
|
+
async function loadSkillEvalManifestTools() {
|
|
957
|
+
if (skillEvalManifestTools)
|
|
958
|
+
return skillEvalManifestTools;
|
|
959
|
+
const modulePath = path.join(CLI_PACKAGE_ROOT, 'dist', 'src', 'eval', 'manifest-v1.js');
|
|
1304
960
|
try {
|
|
1305
|
-
|
|
961
|
+
skillEvalManifestTools = await import(pathToFileURL(modulePath).href);
|
|
962
|
+
return skillEvalManifestTools;
|
|
1306
963
|
}
|
|
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}"`);
|
|
964
|
+
catch {
|
|
1313
965
|
return null;
|
|
1314
966
|
}
|
|
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
967
|
}
|
|
1649
|
-
function validatePluginEvalManifest(pluginDir, pluginManifest) {
|
|
968
|
+
async function validatePluginEvalManifest(pluginDir, pluginManifest) {
|
|
1650
969
|
const legacyRoot = path.join(pluginDir, 'evals');
|
|
1651
970
|
if (fs.existsSync(legacyRoot)) {
|
|
1652
971
|
error('Plugin-root evals/ is legacy; put evals/ inside its owning static or dynamic Skill directory.');
|
|
@@ -1655,17 +974,33 @@ function validatePluginEvalManifest(pluginDir, pluginManifest) {
|
|
|
1655
974
|
.sort()
|
|
1656
975
|
.map((skill) => ({ skill, root: path.join(pluginDir, 'skills', skill) }));
|
|
1657
976
|
const seenSkills = new Set();
|
|
977
|
+
let manifestTools = null;
|
|
1658
978
|
for (const owner of skillRoots) {
|
|
1659
979
|
const evalDir = path.join(owner.root, 'evals');
|
|
1660
980
|
const manifestPath = path.join(evalDir, 'manifest.json');
|
|
1661
981
|
if (!fs.existsSync(manifestPath))
|
|
1662
982
|
continue;
|
|
983
|
+
const manifestField = path.relative(pluginDir, manifestPath).split(path.sep).join('/');
|
|
1663
984
|
if (seenSkills.has(owner.skill)) {
|
|
1664
985
|
error(`Skill ${JSON.stringify(owner.skill)} has more than one evals/manifest.json owner`);
|
|
1665
986
|
continue;
|
|
1666
987
|
}
|
|
1667
988
|
seenSkills.add(owner.skill);
|
|
1668
|
-
|
|
989
|
+
manifestTools ??= await loadSkillEvalManifestTools();
|
|
990
|
+
if (typeof manifestTools?.validateAwSkillEvalManifest !== 'function') {
|
|
991
|
+
error('Canonical AwSkillEvalManifest v1 validator is unavailable; run the current built aw CLI.');
|
|
992
|
+
continue;
|
|
993
|
+
}
|
|
994
|
+
const validation = await manifestTools.validateAwSkillEvalManifest({
|
|
995
|
+
plugin: {
|
|
996
|
+
rootDir: pluginDir,
|
|
997
|
+
manifestPath: path.join(pluginDir, '.agents-plugin', 'plugin.json'),
|
|
998
|
+
manifest: pluginManifest,
|
|
999
|
+
},
|
|
1000
|
+
skill: owner.skill,
|
|
1001
|
+
});
|
|
1002
|
+
for (const message of validation.errors)
|
|
1003
|
+
error(`${manifestField}: ${message}`);
|
|
1669
1004
|
}
|
|
1670
1005
|
}
|
|
1671
1006
|
function countCharacters(value) {
|
|
@@ -2058,33 +1393,6 @@ function validateDeclaredNativeAssets(pluginDir, manifest) {
|
|
|
2058
1393
|
}
|
|
2059
1394
|
}
|
|
2060
1395
|
}
|
|
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
1396
|
function collectDeclaredSkillNames(pluginDir, manifest) {
|
|
2089
1397
|
const skillsRoot = typeof manifest.skills === 'string'
|
|
2090
1398
|
? path.join(pluginDir, manifest.skills)
|
|
@@ -2129,9 +1437,6 @@ function collectDeclaredSkillNames(pluginDir, manifest) {
|
|
|
2129
1437
|
function isNodeRunnablePath(relativePath) {
|
|
2130
1438
|
return SCRIPT_NODE_EXTENSIONS.has(path.posix.extname(normalizeRelativePath(relativePath)));
|
|
2131
1439
|
}
|
|
2132
|
-
function isAutoRunnablePath(relativePath) {
|
|
2133
|
-
return SCRIPT_AUTO_EXTENSIONS.has(path.posix.extname(normalizeRelativePath(relativePath)).toLowerCase());
|
|
2134
|
-
}
|
|
2135
1440
|
function normalizePluginScriptPath(pluginDir, value, fieldName) {
|
|
2136
1441
|
if (typeof value !== 'string' || value.trim() === '') {
|
|
2137
1442
|
error(`${fieldName} must be a non-empty string`);
|
|
@@ -2180,17 +1485,11 @@ function validateScriptEntry(pluginDir, value, fieldName, options = {}) {
|
|
|
2180
1485
|
}
|
|
2181
1486
|
validateAllowedKeys(entry, fieldName, ['runner', 'path', 'args']);
|
|
2182
1487
|
const runner = validateManifestString(entry.runner, `${fieldName}.runner`);
|
|
2183
|
-
if (runner
|
|
2184
|
-
error(`${fieldName}.runner has unsupported runner "${runner}"`);
|
|
2185
|
-
}
|
|
2186
|
-
if (options.requireNodeRunner && runner !== 'node') {
|
|
1488
|
+
if (runner !== 'node') {
|
|
2187
1489
|
error(`${fieldName}.runner must be "node"`);
|
|
2188
1490
|
}
|
|
2189
1491
|
const relativePath = normalizePluginScriptPath(pluginDir, entry.path, `${fieldName}.path`);
|
|
2190
1492
|
validateDeclaredScriptFile(pluginDir, relativePath, `${fieldName}.path`);
|
|
2191
|
-
if (runner === 'auto' && relativePath && !isAutoRunnablePath(relativePath)) {
|
|
2192
|
-
error(`${fieldName}.path uses runner "auto" but has no supported extension (${[...SCRIPT_AUTO_EXTENSIONS].join(', ')})`);
|
|
2193
|
-
}
|
|
2194
1493
|
if (options.requireNodePath && relativePath && !isNodeRunnablePath(relativePath)) {
|
|
2195
1494
|
error(`${fieldName}.path must use a Node runnable extension (.js, .mjs, or .cjs)`);
|
|
2196
1495
|
}
|
|
@@ -2208,51 +1507,6 @@ function validateScriptEntry(pluginDir, value, fieldName, options = {}) {
|
|
|
2208
1507
|
}
|
|
2209
1508
|
return runner && relativePath ? { runner, path: relativePath } : null;
|
|
2210
1509
|
}
|
|
2211
|
-
function validateLifecycleDefinition(pluginDir, value, fieldName, runtimeEntries) {
|
|
2212
|
-
if (typeof value === 'string') {
|
|
2213
|
-
const relativePath = normalizePluginScriptPath(pluginDir, value, fieldName);
|
|
2214
|
-
validateDeclaredScriptFile(pluginDir, relativePath, fieldName);
|
|
2215
|
-
if (relativePath && !isAutoRunnablePath(relativePath)) {
|
|
2216
|
-
error(`${fieldName} uses runner "auto" but has no supported extension (${[...SCRIPT_AUTO_EXTENSIONS].join(', ')})`);
|
|
2217
|
-
}
|
|
2218
|
-
if (relativePath && isNodeRunnablePath(relativePath)) {
|
|
2219
|
-
runtimeEntries.push({ path: relativePath, fieldName });
|
|
2220
|
-
}
|
|
2221
|
-
return { default: relativePath ? { runner: 'auto', path: relativePath } : null };
|
|
2222
|
-
}
|
|
2223
|
-
const selector = expectRecord(value, fieldName);
|
|
2224
|
-
if (!selector)
|
|
2225
|
-
return {};
|
|
2226
|
-
if ('runner' in selector || 'path' in selector || 'command' in selector) {
|
|
2227
|
-
error(`${fieldName} must declare lifecycle scripts under OS selectors`);
|
|
2228
|
-
}
|
|
2229
|
-
for (const selectorName of Object.keys(selector)) {
|
|
2230
|
-
if (!SCRIPT_OS_SELECTORS.has(selectorName)) {
|
|
2231
|
-
error(`${fieldName} has unknown OS selector "${selectorName}"`);
|
|
2232
|
-
}
|
|
2233
|
-
}
|
|
2234
|
-
const normalized = {};
|
|
2235
|
-
for (const [selectorName, entryValue] of Object.entries(selector)) {
|
|
2236
|
-
const entry = validateScriptEntry(pluginDir, entryValue, `${fieldName}.${selectorName}`);
|
|
2237
|
-
if (entry) {
|
|
2238
|
-
normalized[selectorName] = entry;
|
|
2239
|
-
if (entry.runner === 'node' || (entry.runner === 'auto' && isNodeRunnablePath(entry.path))) {
|
|
2240
|
-
runtimeEntries.push({ path: entry.path, fieldName: `${fieldName}.${selectorName}.path` });
|
|
2241
|
-
}
|
|
2242
|
-
}
|
|
2243
|
-
}
|
|
2244
|
-
const orders = {
|
|
2245
|
-
linux: ['linux', 'unix', 'default'],
|
|
2246
|
-
darwin: ['darwin', 'unix', 'default'],
|
|
2247
|
-
win32: ['win32', 'windows', 'default'],
|
|
2248
|
-
};
|
|
2249
|
-
for (const [osName, order] of Object.entries(orders)) {
|
|
2250
|
-
if (!order.some((selectorName) => normalized[selectorName])) {
|
|
2251
|
-
error(`${fieldName} must include an entry that resolves on ${osName}`);
|
|
2252
|
-
}
|
|
2253
|
-
}
|
|
2254
|
-
return normalized;
|
|
2255
|
-
}
|
|
2256
1510
|
function isNonRegistryDependencySpecifier(specifier) {
|
|
2257
1511
|
const trimmed = String(specifier).trim();
|
|
2258
1512
|
return (/^(?:workspace|file|link|portal):/i.test(trimmed) ||
|
|
@@ -2390,6 +1644,12 @@ function scanNodeImportContract(pluginDir, entryPaths, options) {
|
|
|
2390
1644
|
continue;
|
|
2391
1645
|
}
|
|
2392
1646
|
if (!resolved.path) {
|
|
1647
|
+
const requested = path.posix.normalize(path.posix.join(path.posix.dirname(relativePath), specifier));
|
|
1648
|
+
if (options.nativeToolReader && requested === options.nativeToolReader)
|
|
1649
|
+
continue;
|
|
1650
|
+
if ((options.runtimeConfigSkillRoots ?? []).some((root) => (relativePath.startsWith(`${root}/`)
|
|
1651
|
+
&& requested === path.posix.join(root, AW_RUNTIME_CONFIG_READER_PATH))))
|
|
1652
|
+
continue;
|
|
2393
1653
|
error(`${options.label}: unable to resolve relative import "${specifier}" from "${relativePath}"`);
|
|
2394
1654
|
continue;
|
|
2395
1655
|
}
|
|
@@ -2439,271 +1699,61 @@ function listFilesRecursive(rootDir) {
|
|
|
2439
1699
|
walk(rootDir);
|
|
2440
1700
|
return output;
|
|
2441
1701
|
}
|
|
2442
|
-
function validateConfigValueLightweight(field, value, fieldName) {
|
|
2443
|
-
if (!isRecord(field) || !CONFIG_FIELD_TYPES.has(field.type))
|
|
2444
|
-
return;
|
|
2445
|
-
if (field.type === 'string' || field.type === 'file' || field.type === 'directory') {
|
|
2446
|
-
validateManifestString(value, fieldName);
|
|
2447
|
-
return;
|
|
2448
|
-
}
|
|
2449
|
-
if (field.type === 'boolean') {
|
|
2450
|
-
if (typeof value !== 'boolean') {
|
|
2451
|
-
error(`${fieldName} must be a boolean`);
|
|
2452
|
-
}
|
|
2453
|
-
return;
|
|
2454
|
-
}
|
|
2455
|
-
if (field.type === 'number') {
|
|
2456
|
-
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
2457
|
-
error(`${fieldName} must be a finite number`);
|
|
2458
|
-
return;
|
|
2459
|
-
}
|
|
2460
|
-
if (typeof field.min === 'number' && value < field.min) {
|
|
2461
|
-
error(`${fieldName} must be greater than or equal to ${field.min}`);
|
|
2462
|
-
}
|
|
2463
|
-
if (typeof field.max === 'number' && value > field.max) {
|
|
2464
|
-
error(`${fieldName} must be less than or equal to ${field.max}`);
|
|
2465
|
-
}
|
|
2466
|
-
return;
|
|
2467
|
-
}
|
|
2468
|
-
if (field.type === 'enum') {
|
|
2469
|
-
if (typeof value !== 'string' || !Array.isArray(field.values) || !field.values.includes(value)) {
|
|
2470
|
-
error(`${fieldName} must be one of config schema values`);
|
|
2471
|
-
}
|
|
2472
|
-
}
|
|
2473
|
-
}
|
|
2474
|
-
function validateConfigPresentationLightweight(presentation, schema) {
|
|
2475
|
-
if (presentation === undefined)
|
|
2476
|
-
return;
|
|
2477
|
-
const root = expectRecord(presentation, 'config.presentation');
|
|
2478
|
-
if (!root)
|
|
2479
|
-
return;
|
|
2480
|
-
validateAllowedKeys(root, 'config.presentation', ['groups', 'fields', 'presets']);
|
|
2481
|
-
const schemaKeys = new Set(Object.keys(schema));
|
|
2482
|
-
if (root.groups !== undefined) {
|
|
2483
|
-
if (!Array.isArray(root.groups)) {
|
|
2484
|
-
error('config.presentation.groups must be an array');
|
|
2485
|
-
}
|
|
2486
|
-
else {
|
|
2487
|
-
const groupIds = new Set();
|
|
2488
|
-
const groupedFields = new Set();
|
|
2489
|
-
root.groups.forEach((rawGroup, index) => {
|
|
2490
|
-
const fieldName = `config.presentation.groups[${index}]`;
|
|
2491
|
-
const group = expectRecord(rawGroup, fieldName);
|
|
2492
|
-
if (!group)
|
|
2493
|
-
return;
|
|
2494
|
-
validateAllowedKeys(group, fieldName, ['id', 'title', 'description', 'fields', 'advanced']);
|
|
2495
|
-
const validId = validateManifestString(group.id, `${fieldName}.id`);
|
|
2496
|
-
const id = validId ? group.id : null;
|
|
2497
|
-
if (id && groupIds.has(id)) {
|
|
2498
|
-
error(`${fieldName}.id has duplicate group id "${id}"`);
|
|
2499
|
-
}
|
|
2500
|
-
else if (id) {
|
|
2501
|
-
groupIds.add(id);
|
|
2502
|
-
}
|
|
2503
|
-
validateManifestString(group.title, `${fieldName}.title`);
|
|
2504
|
-
if (group.description !== undefined) {
|
|
2505
|
-
validateManifestString(group.description, `${fieldName}.description`);
|
|
2506
|
-
}
|
|
2507
|
-
if (group.advanced !== undefined && typeof group.advanced !== 'boolean') {
|
|
2508
|
-
error(`${fieldName}.advanced must be a boolean`);
|
|
2509
|
-
}
|
|
2510
|
-
if (!Array.isArray(group.fields)) {
|
|
2511
|
-
error(`${fieldName}.fields must be an array`);
|
|
2512
|
-
return;
|
|
2513
|
-
}
|
|
2514
|
-
group.fields.forEach((key, fieldIndex) => {
|
|
2515
|
-
const keyFieldName = `${fieldName}.fields[${fieldIndex}]`;
|
|
2516
|
-
const validKey = validateManifestString(key, keyFieldName);
|
|
2517
|
-
if (!validKey)
|
|
2518
|
-
return;
|
|
2519
|
-
if (!schemaKeys.has(key)) {
|
|
2520
|
-
error(`${keyFieldName} references unknown config key "${key}"`);
|
|
2521
|
-
}
|
|
2522
|
-
if (groupedFields.has(key)) {
|
|
2523
|
-
error(`${keyFieldName} assigns config key "${key}" to more than one group`);
|
|
2524
|
-
}
|
|
2525
|
-
else {
|
|
2526
|
-
groupedFields.add(key);
|
|
2527
|
-
}
|
|
2528
|
-
});
|
|
2529
|
-
});
|
|
2530
|
-
}
|
|
2531
|
-
}
|
|
2532
|
-
if (root.fields !== undefined) {
|
|
2533
|
-
const fields = expectRecord(root.fields, 'config.presentation.fields');
|
|
2534
|
-
if (fields) {
|
|
2535
|
-
for (const [key, rawField] of Object.entries(fields)) {
|
|
2536
|
-
const fieldName = `config.presentation.fields.${key}`;
|
|
2537
|
-
const schemaField = schema[key];
|
|
2538
|
-
if (!schemaKeys.has(key)) {
|
|
2539
|
-
error(`config.presentation.fields references unknown config key "${key}"`);
|
|
2540
|
-
}
|
|
2541
|
-
const field = expectRecord(rawField, fieldName);
|
|
2542
|
-
if (!field)
|
|
2543
|
-
continue;
|
|
2544
|
-
validateAllowedKeys(field, fieldName, [
|
|
2545
|
-
'valueLabels',
|
|
2546
|
-
'recommendedValue',
|
|
2547
|
-
'placeholder',
|
|
2548
|
-
'unit',
|
|
2549
|
-
'advanced',
|
|
2550
|
-
]);
|
|
2551
|
-
if (field.placeholder !== undefined) {
|
|
2552
|
-
validateManifestString(field.placeholder, `${fieldName}.placeholder`);
|
|
2553
|
-
}
|
|
2554
|
-
if (field.unit !== undefined) {
|
|
2555
|
-
validateManifestString(field.unit, `${fieldName}.unit`);
|
|
2556
|
-
}
|
|
2557
|
-
if (field.advanced !== undefined && typeof field.advanced !== 'boolean') {
|
|
2558
|
-
error(`${fieldName}.advanced must be a boolean`);
|
|
2559
|
-
}
|
|
2560
|
-
if (field.valueLabels !== undefined) {
|
|
2561
|
-
const labels = expectRecord(field.valueLabels, `${fieldName}.valueLabels`);
|
|
2562
|
-
if (isRecord(schemaField) && schemaField.type !== 'enum') {
|
|
2563
|
-
error(`${fieldName}.valueLabels is only supported for enum config`);
|
|
2564
|
-
}
|
|
2565
|
-
if (labels) {
|
|
2566
|
-
for (const [token, label] of Object.entries(labels)) {
|
|
2567
|
-
if (!isRecord(schemaField) || !Array.isArray(schemaField.values) || !schemaField.values.includes(token)) {
|
|
2568
|
-
error(`${fieldName}.valueLabels references unknown enum token "${token}"`);
|
|
2569
|
-
}
|
|
2570
|
-
validateManifestString(label, `${fieldName}.valueLabels.${token}`);
|
|
2571
|
-
}
|
|
2572
|
-
}
|
|
2573
|
-
}
|
|
2574
|
-
if (field.recommendedValue !== undefined) {
|
|
2575
|
-
validateConfigValueLightweight(schemaField, field.recommendedValue, `${fieldName}.recommendedValue`);
|
|
2576
|
-
}
|
|
2577
|
-
}
|
|
2578
|
-
}
|
|
2579
|
-
}
|
|
2580
|
-
if (root.presets !== undefined) {
|
|
2581
|
-
if (!Array.isArray(root.presets)) {
|
|
2582
|
-
error('config.presentation.presets must be an array');
|
|
2583
|
-
}
|
|
2584
|
-
else {
|
|
2585
|
-
const presetIds = new Set();
|
|
2586
|
-
root.presets.forEach((rawPreset, index) => {
|
|
2587
|
-
const fieldName = `config.presentation.presets[${index}]`;
|
|
2588
|
-
const preset = expectRecord(rawPreset, fieldName);
|
|
2589
|
-
if (!preset)
|
|
2590
|
-
return;
|
|
2591
|
-
validateAllowedKeys(preset, fieldName, ['id', 'title', 'description', 'values']);
|
|
2592
|
-
const validId = validateManifestString(preset.id, `${fieldName}.id`);
|
|
2593
|
-
const id = validId ? preset.id : null;
|
|
2594
|
-
if (id && presetIds.has(id)) {
|
|
2595
|
-
error(`${fieldName}.id has duplicate preset id "${id}"`);
|
|
2596
|
-
}
|
|
2597
|
-
else if (id) {
|
|
2598
|
-
presetIds.add(id);
|
|
2599
|
-
}
|
|
2600
|
-
validateManifestString(preset.title, `${fieldName}.title`);
|
|
2601
|
-
if (preset.description !== undefined) {
|
|
2602
|
-
validateManifestString(preset.description, `${fieldName}.description`);
|
|
2603
|
-
}
|
|
2604
|
-
const values = expectRecord(preset.values, `${fieldName}.values`);
|
|
2605
|
-
if (!values)
|
|
2606
|
-
return;
|
|
2607
|
-
for (const [key, value] of Object.entries(values)) {
|
|
2608
|
-
if (!schemaKeys.has(key)) {
|
|
2609
|
-
error(`${fieldName}.values references unknown config key "${key}"`);
|
|
2610
|
-
continue;
|
|
2611
|
-
}
|
|
2612
|
-
const schemaField = schema[key];
|
|
2613
|
-
if (isRecord(schemaField)
|
|
2614
|
-
&& (schemaField.sensitive === true
|
|
2615
|
-
|| schemaField.type === 'file'
|
|
2616
|
-
|| schemaField.type === 'directory')) {
|
|
2617
|
-
error(`${fieldName}.values.${key} cannot contain sensitive, file, or directory config values`);
|
|
2618
|
-
continue;
|
|
2619
|
-
}
|
|
2620
|
-
validateConfigValueLightweight(schemaField, value, `${fieldName}.values.${key}`);
|
|
2621
|
-
}
|
|
2622
|
-
});
|
|
2623
|
-
}
|
|
2624
|
-
}
|
|
2625
|
-
}
|
|
2626
1702
|
function validateConfigSchemaLightweight(config) {
|
|
2627
1703
|
if (config === undefined)
|
|
2628
1704
|
return;
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
if (!schema)
|
|
2635
|
-
return;
|
|
2636
|
-
for (const [key, rawField] of Object.entries(schema)) {
|
|
2637
|
-
if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(key)) {
|
|
2638
|
-
error(`config.schema has invalid key "${key}"`);
|
|
2639
|
-
}
|
|
2640
|
-
const fieldName = `config.schema.${key}`;
|
|
2641
|
-
const field = expectRecord(rawField, fieldName);
|
|
2642
|
-
if (!field)
|
|
2643
|
-
continue;
|
|
2644
|
-
validateAllowedKeys(field, fieldName, [
|
|
2645
|
-
'type',
|
|
2646
|
-
'title',
|
|
2647
|
-
'description',
|
|
2648
|
-
'default',
|
|
2649
|
-
'required',
|
|
2650
|
-
'sensitive',
|
|
2651
|
-
'values',
|
|
2652
|
-
'min',
|
|
2653
|
-
'max',
|
|
2654
|
-
]);
|
|
2655
|
-
if (typeof field.type !== 'string' || !CONFIG_FIELD_TYPES.has(field.type)) {
|
|
2656
|
-
error(`${fieldName}.type must be string, number, boolean, enum, file, or directory`);
|
|
2657
|
-
}
|
|
2658
|
-
if (field.required !== undefined && typeof field.required !== 'boolean') {
|
|
2659
|
-
error(`${fieldName}.required must be a boolean`);
|
|
2660
|
-
}
|
|
2661
|
-
if (field.sensitive !== undefined && typeof field.sensitive !== 'boolean') {
|
|
2662
|
-
error(`${fieldName}.sensitive must be a boolean`);
|
|
2663
|
-
}
|
|
2664
|
-
if (field.values !== undefined) {
|
|
2665
|
-
if (field.type !== 'enum' || !Array.isArray(field.values) || field.values.length === 0) {
|
|
2666
|
-
error(`${fieldName}.values is only supported for enum config and must be non-empty`);
|
|
2667
|
-
}
|
|
2668
|
-
}
|
|
2669
|
-
else if (field.type === 'enum') {
|
|
2670
|
-
error(`${fieldName}.values is required for enum config`);
|
|
2671
|
-
}
|
|
2672
|
-
if (field.type !== 'number' && (field.min !== undefined || field.max !== undefined)) {
|
|
2673
|
-
error(`${fieldName}.min/max are only supported for number config`);
|
|
2674
|
-
}
|
|
2675
|
-
if (field.min !== undefined && typeof field.min !== 'number') {
|
|
2676
|
-
error(`${fieldName}.min must be a number`);
|
|
2677
|
-
}
|
|
2678
|
-
if (field.max !== undefined && typeof field.max !== 'number') {
|
|
2679
|
-
error(`${fieldName}.max must be a number`);
|
|
2680
|
-
}
|
|
2681
|
-
if (typeof field.min === 'number' && typeof field.max === 'number' && field.min > field.max) {
|
|
2682
|
-
error(`${fieldName}.min must be less than or equal to max`);
|
|
2683
|
-
}
|
|
1705
|
+
try {
|
|
1706
|
+
normalizePluginConfigDefinition(config);
|
|
1707
|
+
}
|
|
1708
|
+
catch (validationError) {
|
|
1709
|
+
error(validationError instanceof Error ? validationError.message : String(validationError));
|
|
2684
1710
|
}
|
|
2685
|
-
validateConfigPresentationLightweight(configRoot.presentation, schema);
|
|
2686
1711
|
}
|
|
2687
1712
|
function configSchemaKeys(manifest) {
|
|
2688
|
-
const schema = manifest.config?.
|
|
1713
|
+
const schema = manifest.config?.fields;
|
|
2689
1714
|
return isRecord(schema) ? new Set(Object.keys(schema)) : new Set();
|
|
2690
1715
|
}
|
|
2691
|
-
function configSchemaField(manifest, key) {
|
|
2692
|
-
const field = manifest.config?.schema?.[key];
|
|
2693
|
-
return isRecord(field) ? field : null;
|
|
2694
|
-
}
|
|
2695
1716
|
function defaultConfigValues(manifest) {
|
|
2696
|
-
const schema = manifest.config?.
|
|
1717
|
+
const schema = manifest.config?.fields;
|
|
2697
1718
|
const values = {};
|
|
2698
1719
|
if (!isRecord(schema))
|
|
2699
1720
|
return values;
|
|
2700
1721
|
for (const [key, field] of Object.entries(schema)) {
|
|
2701
|
-
if (isRecord(field) &&
|
|
1722
|
+
if (isRecord(field) && field.default !== undefined) {
|
|
2702
1723
|
values[key] = field.default;
|
|
2703
1724
|
}
|
|
2704
1725
|
}
|
|
2705
1726
|
return values;
|
|
2706
1727
|
}
|
|
1728
|
+
function configFieldContainsSensitiveValue(field) {
|
|
1729
|
+
if (!isRecord(field))
|
|
1730
|
+
return false;
|
|
1731
|
+
if (field.sensitive === true)
|
|
1732
|
+
return true;
|
|
1733
|
+
if (field.type !== 'object')
|
|
1734
|
+
return false;
|
|
1735
|
+
return ((isRecord(field.properties)
|
|
1736
|
+
&& Object.values(field.properties).some((child) => isRecord(child) && child.sensitive === true))
|
|
1737
|
+
|| (isRecord(field.additionalProperties)
|
|
1738
|
+
&& field.additionalProperties.sensitive === true));
|
|
1739
|
+
}
|
|
1740
|
+
function codexStaticNativeComponentKeys(manifest) {
|
|
1741
|
+
if (!isCodexTargeted(manifest))
|
|
1742
|
+
return [];
|
|
1743
|
+
const keys = [];
|
|
1744
|
+
if (manifest.skills)
|
|
1745
|
+
keys.push('skills');
|
|
1746
|
+
if (manifest.mcpServers)
|
|
1747
|
+
keys.push('mcpServers');
|
|
1748
|
+
if ((Array.isArray(manifest.extensions?.hooks)
|
|
1749
|
+
&& manifest.extensions.hooks.some((hook) => hook?.agentOverrides?.codex !== null))
|
|
1750
|
+
|| (Array.isArray(manifest.extensions?.nativeHooks?.codex)
|
|
1751
|
+
&& manifest.extensions.nativeHooks.codex.length > 0))
|
|
1752
|
+
keys.push('hooks');
|
|
1753
|
+
if (manifest.apps)
|
|
1754
|
+
keys.push('apps');
|
|
1755
|
+
return keys;
|
|
1756
|
+
}
|
|
2707
1757
|
function validateArtifactId(value, fieldName) {
|
|
2708
1758
|
const id = validateManifestString(value, fieldName);
|
|
2709
1759
|
if (!id)
|
|
@@ -2764,7 +1814,7 @@ function validateStringArray(value, fieldName, options = {}) {
|
|
|
2764
1814
|
}
|
|
2765
1815
|
return output;
|
|
2766
1816
|
}
|
|
2767
|
-
function validateArtifactTargetEntry(entry, index, manifest,
|
|
1817
|
+
function validateArtifactTargetEntry(entry, index, manifest, pluginDir) {
|
|
2768
1818
|
const fieldName = `artifacts.targets[${index}]`;
|
|
2769
1819
|
const target = expectRecord(entry, fieldName);
|
|
2770
1820
|
if (!target)
|
|
@@ -2779,11 +1829,13 @@ function validateArtifactTargetEntry(entry, index, manifest, seen, pluginDir) {
|
|
|
2779
1829
|
'projectManagedKind',
|
|
2780
1830
|
'states',
|
|
2781
1831
|
'refreshKeys',
|
|
1832
|
+
'delivery',
|
|
2782
1833
|
'requiresCodexProjectConfig',
|
|
2783
1834
|
'roleKey',
|
|
2784
1835
|
'skillKey',
|
|
2785
1836
|
'sourceDirs',
|
|
2786
1837
|
'runtimeImports',
|
|
1838
|
+
'runtimeConfig',
|
|
2787
1839
|
]);
|
|
2788
1840
|
const id = validateArtifactId(target.id, `${fieldName}.id`);
|
|
2789
1841
|
const artifactTarget = validateManifestString(target.target, `${fieldName}.target`);
|
|
@@ -2791,6 +1843,30 @@ function validateArtifactTargetEntry(entry, index, manifest, seen, pluginDir) {
|
|
|
2791
1843
|
error(`${fieldName}.target: unsupported artifact target "${artifactTarget}"`);
|
|
2792
1844
|
}
|
|
2793
1845
|
const portable = artifactTarget === 'agent-skills.skill';
|
|
1846
|
+
let delivery;
|
|
1847
|
+
if (artifactTarget === 'codex.skill') {
|
|
1848
|
+
delivery = expectRecord(target.delivery, `${fieldName}.delivery`);
|
|
1849
|
+
if (delivery) {
|
|
1850
|
+
if (delivery.mode === 'aw-extension') {
|
|
1851
|
+
validateAllowedKeys(delivery, `${fieldName}.delivery`, ['mode']);
|
|
1852
|
+
}
|
|
1853
|
+
else if (delivery.mode === 'codex-native') {
|
|
1854
|
+
validateAllowedKeys(delivery, `${fieldName}.delivery`, ['mode', 'identity', 'renderProfile']);
|
|
1855
|
+
if (delivery.identity !== 'shared') {
|
|
1856
|
+
error(`${fieldName}.delivery.identity: expected "shared"`);
|
|
1857
|
+
}
|
|
1858
|
+
if (delivery.renderProfile !== 'user') {
|
|
1859
|
+
error(`${fieldName}.delivery.renderProfile: expected "user"`);
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
else {
|
|
1863
|
+
error(`${fieldName}.delivery.mode: expected "codex-native" or "aw-extension"`);
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
else if (target.delivery !== undefined) {
|
|
1868
|
+
error(`${fieldName}.delivery: delivery is only supported for codex.skill targets`);
|
|
1869
|
+
}
|
|
2794
1870
|
const platform = portable || target.platform === undefined
|
|
2795
1871
|
? undefined
|
|
2796
1872
|
: validateManifestString(target.platform, `${fieldName}.platform`);
|
|
@@ -2814,13 +1890,6 @@ function validateArtifactTargetEntry(entry, index, manifest, seen, pluginDir) {
|
|
|
2814
1890
|
error(`${fieldName}.distribution: platform targets do not support distribution`);
|
|
2815
1891
|
}
|
|
2816
1892
|
}
|
|
2817
|
-
const key = id && artifactTarget ? `${id}:${artifactTarget}` : null;
|
|
2818
|
-
if (key) {
|
|
2819
|
-
if (seen.has(key)) {
|
|
2820
|
-
error(`${fieldName}: duplicate artifact id + target "${key}"`);
|
|
2821
|
-
}
|
|
2822
|
-
seen.add(key);
|
|
2823
|
-
}
|
|
2824
1893
|
const expectedKind = ARTIFACT_KIND_BY_TARGET[artifactTarget];
|
|
2825
1894
|
const kind = target.kind === undefined
|
|
2826
1895
|
? expectedKind
|
|
@@ -2863,10 +1932,15 @@ function validateArtifactTargetEntry(entry, index, manifest, seen, pluginDir) {
|
|
|
2863
1932
|
error(`${fieldName}.refreshKeys[${refreshIndex}]: unknown config key "${refreshKey}"`);
|
|
2864
1933
|
continue;
|
|
2865
1934
|
}
|
|
2866
|
-
const
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
1935
|
+
const sensitiveRefreshKey = configFieldContainsSensitiveValue(manifest.config?.fields?.[refreshKey]);
|
|
1936
|
+
const skillTarget = artifactTarget === 'codex.skill'
|
|
1937
|
+
|| artifactTarget === 'claude.skill'
|
|
1938
|
+
|| artifactTarget === 'agent-skills.skill';
|
|
1939
|
+
if (skillTarget && sensitiveRefreshKey) {
|
|
1940
|
+
const native = delivery?.mode === 'codex-native';
|
|
1941
|
+
error(`${native ? 'codexNativeRenderSensitiveRefreshKey' : 'skillRenderSensitiveRefreshKey'}: ` +
|
|
1942
|
+
`${fieldName}.refreshKeys[${refreshIndex}]: ` +
|
|
1943
|
+
`${native ? 'codex-native renderers' : 'Skill renderers'} cannot read sensitive config field "${refreshKey}"`);
|
|
2870
1944
|
}
|
|
2871
1945
|
}
|
|
2872
1946
|
if (target.requiresCodexProjectConfig !== undefined &&
|
|
@@ -2955,9 +2029,11 @@ function validateArtifactTargetEntry(entry, index, manifest, seen, pluginDir) {
|
|
|
2955
2029
|
projectManagedKind,
|
|
2956
2030
|
states,
|
|
2957
2031
|
refreshKeys,
|
|
2032
|
+
delivery,
|
|
2958
2033
|
roleKey,
|
|
2959
2034
|
skillKey,
|
|
2960
2035
|
sourceDirs,
|
|
2036
|
+
runtimeConfig: target.runtimeConfig,
|
|
2961
2037
|
runtimeImports: Array.isArray(target.runtimeImports) ? target.runtimeImports : [],
|
|
2962
2038
|
};
|
|
2963
2039
|
}
|
|
@@ -2974,10 +2050,69 @@ function validateArtifactsConfigLightweight(pluginDir, manifest) {
|
|
|
2974
2050
|
error('artifacts.targets must be an array');
|
|
2975
2051
|
return [];
|
|
2976
2052
|
}
|
|
2977
|
-
const seen = new Set();
|
|
2978
2053
|
const declarations = artifacts.targets
|
|
2979
|
-
.map((entry, index) => validateArtifactTargetEntry(entry, index, manifest,
|
|
2054
|
+
.map((entry, index) => validateArtifactTargetEntry(entry, index, manifest, pluginDir))
|
|
2980
2055
|
.filter(Boolean);
|
|
2056
|
+
const idsBySurfaceScope = new Map();
|
|
2057
|
+
const nativeIds = new Map();
|
|
2058
|
+
const nativeSkillKeys = new Map();
|
|
2059
|
+
for (const [declarationIndex, declaration] of declarations.entries()) {
|
|
2060
|
+
for (const scope of declaration.scopes) {
|
|
2061
|
+
const surface = declaration.platform ?? declaration.distribution;
|
|
2062
|
+
const key = `${surface}:${scope}:${declaration.id}`;
|
|
2063
|
+
const existing = idsBySurfaceScope.get(key);
|
|
2064
|
+
if (existing !== undefined) {
|
|
2065
|
+
error(`artifacts.targets has duplicate artifact id "${declaration.id}" for surface/scope "${surface}:${scope}" in targets[${existing}] and targets[${declarationIndex}]`);
|
|
2066
|
+
}
|
|
2067
|
+
else {
|
|
2068
|
+
idsBySurfaceScope.set(key, declarationIndex);
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
if (declaration.delivery?.mode === 'codex-native') {
|
|
2072
|
+
const existingId = nativeIds.get(declaration.id);
|
|
2073
|
+
if (existingId !== undefined) {
|
|
2074
|
+
error(`artifacts.targets has duplicate codex-native artifact id "${declaration.id}" in the shared projection at targets[${existingId}] and targets[${declarationIndex}]`);
|
|
2075
|
+
}
|
|
2076
|
+
else {
|
|
2077
|
+
nativeIds.set(declaration.id, declarationIndex);
|
|
2078
|
+
}
|
|
2079
|
+
const existingSkillKey = nativeSkillKeys.get(declaration.skillKey);
|
|
2080
|
+
if (existingSkillKey !== undefined) {
|
|
2081
|
+
error(`artifacts.targets has duplicate codex-native skillKey "${declaration.skillKey}" in the shared projection at targets[${existingSkillKey}] and targets[${declarationIndex}]`);
|
|
2082
|
+
}
|
|
2083
|
+
else {
|
|
2084
|
+
nativeSkillKeys.set(declaration.skillKey, declarationIndex);
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
const nativeComponents = codexStaticNativeComponentKeys(manifest).map((key) => ({
|
|
2089
|
+
key,
|
|
2090
|
+
scopes: ['project', 'user'],
|
|
2091
|
+
}));
|
|
2092
|
+
declarations.forEach((declaration, index) => {
|
|
2093
|
+
if (declaration.delivery?.mode === 'codex-native') {
|
|
2094
|
+
nativeComponents.push({ key: `artifacts.targets[${index}]`, scopes: declaration.scopes });
|
|
2095
|
+
}
|
|
2096
|
+
});
|
|
2097
|
+
const expectedNativeComponent = nativeComponents[0];
|
|
2098
|
+
if (expectedNativeComponent) {
|
|
2099
|
+
const canonicalScopes = (component) => [...component.scopes].sort().join(',');
|
|
2100
|
+
const expectedScopes = canonicalScopes(expectedNativeComponent);
|
|
2101
|
+
const mismatch = nativeComponents.find((component) => canonicalScopes(component) !== expectedScopes);
|
|
2102
|
+
if (mismatch) {
|
|
2103
|
+
error(`codexSharedNativeScopeMismatch: Codex native component scope sets must match exactly; ` +
|
|
2104
|
+
`${expectedNativeComponent.key} has {${expectedScopes}} but ${mismatch.key} has ` +
|
|
2105
|
+
`{${canonicalScopes(mismatch)}}`);
|
|
2106
|
+
}
|
|
2107
|
+
}
|
|
2108
|
+
const staticSkillNames = new Set(collectDeclaredSkillNames(pluginDir, manifest).static);
|
|
2109
|
+
for (const [index, declaration] of declarations.entries()) {
|
|
2110
|
+
if (declaration.target === 'codex.skill'
|
|
2111
|
+
&& declaration.delivery?.mode === 'codex-native'
|
|
2112
|
+
&& staticSkillNames.has(declaration.skillKey)) {
|
|
2113
|
+
error(`artifacts.targets[${index}].skillKey: native dynamic Skill "${declaration.skillKey}" conflicts with a static Skill of the same name`);
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2981
2116
|
const owners = new Map();
|
|
2982
2117
|
for (const declaration of declarations) {
|
|
2983
2118
|
if ((declaration.target !== 'codex.skill' && declaration.target !== 'claude.skill' && declaration.target !== 'agent-skills.skill') ||
|
|
@@ -3244,19 +2379,28 @@ function validateRenderedSkillTree(options) {
|
|
|
3244
2379
|
if (!readme || readme.toString('utf8').trim() === '') {
|
|
3245
2380
|
error(`${fieldName} final file tree must contain a non-empty README.md`);
|
|
3246
2381
|
}
|
|
3247
|
-
const sensitiveValues = Object.entries(manifest.config?.schema ?? {})
|
|
3248
|
-
.filter(([, field]) => isRecord(field) && field.sensitive === true)
|
|
3249
|
-
.map(([key, field]) => Object.prototype.hasOwnProperty.call(field, 'default')
|
|
3250
|
-
? String(field.default)
|
|
3251
|
-
: String(defaultConfigValues(manifest)[key] ?? ''))
|
|
3252
|
-
.filter(Boolean);
|
|
3253
2382
|
for (const [relativePath, content] of files) {
|
|
3254
2383
|
const text = content.toString('utf8');
|
|
3255
|
-
|
|
3256
|
-
|
|
2384
|
+
let remaining = text;
|
|
2385
|
+
if (relativePath.endsWith('.md')) {
|
|
2386
|
+
try {
|
|
2387
|
+
const placeholders = parseManagedCommandPlaceholders(text);
|
|
2388
|
+
if (placeholders.length > 0 && !(manifest.requires?.aw?.capabilities ?? []).includes('aw.managed-commands.v1')) {
|
|
2389
|
+
error(`${fieldName} managed command references require aw.managed-commands.v1`);
|
|
2390
|
+
}
|
|
2391
|
+
if (declaration.target === 'agent-skills.skill' && placeholders.length > 0) {
|
|
2392
|
+
error(`${fieldName} managedCommandPortableUnsupported: Portable Skills cannot contain managed command references`);
|
|
2393
|
+
}
|
|
2394
|
+
else if (options.managedTrees) {
|
|
2395
|
+
remaining = replaceManagedCommandPlaceholders(text, () => `cmdp_${'0'.repeat(16)}`);
|
|
2396
|
+
}
|
|
2397
|
+
}
|
|
2398
|
+
catch (cause) {
|
|
2399
|
+
error(`${fieldName} ${cause.message}`);
|
|
2400
|
+
}
|
|
3257
2401
|
}
|
|
3258
|
-
if (
|
|
3259
|
-
error(`${fieldName} final file tree
|
|
2402
|
+
if (/\{\{[^{}]+\}\}/u.test(remaining)) {
|
|
2403
|
+
error(`${fieldName} final file tree contains an unresolved template in "${relativePath}"`);
|
|
3260
2404
|
}
|
|
3261
2405
|
}
|
|
3262
2406
|
if (declaration.target === 'codex.skill') {
|
|
@@ -3274,6 +2418,7 @@ function validateRenderedSkillTree(options) {
|
|
|
3274
2418
|
if (declaration.target === 'codex.skill' && files.has('agents/openai.yaml')) {
|
|
3275
2419
|
validateOpenAiYaml(path.join(materializedRoot, 'agents', 'openai.yaml'), declaration.skillKey, true, manifest, materializedRoot, skillValidationTools);
|
|
3276
2420
|
}
|
|
2421
|
+
options.managedTrees?.push({ declaration, files, scope: options.managedScope });
|
|
3277
2422
|
}
|
|
3278
2423
|
function validateStaticPortableFinalTrees(pluginDir, manifest, runtimeImports, skillValidationTools) {
|
|
3279
2424
|
const portableRoot = manifest.distributions?.agentSkills?.skills;
|
|
@@ -3333,6 +2478,12 @@ function validateRenderedArtifactPlan(plan, declarations, scopeLabel, options) {
|
|
|
3333
2478
|
const entry = expectRecord(artifact, fieldName);
|
|
3334
2479
|
if (!entry)
|
|
3335
2480
|
return;
|
|
2481
|
+
if (entry.target === 'aw.command-set' || entry.target === 'aw.command-package') {
|
|
2482
|
+
const declaration = byKey.get(`${entry.id}:${entry.target}`);
|
|
2483
|
+
if (declaration)
|
|
2484
|
+
returned.add(`${entry.id}:${entry.target}`);
|
|
2485
|
+
return; // Shared ArtifactPlan validation owns command response fields.
|
|
2486
|
+
}
|
|
3336
2487
|
validateAllowedKeys(entry, fieldName, [
|
|
3337
2488
|
'id',
|
|
3338
2489
|
'platform',
|
|
@@ -3439,6 +2590,8 @@ function validateRenderedArtifactPlan(plan, declarations, scopeLabel, options) {
|
|
|
3439
2590
|
runtimeImports: options.runtimeImports,
|
|
3440
2591
|
materializedRoot: path.join(options.tempRoot, `${declaration.platform ?? declaration.distribution}-${scopeLabel.split('/')[1]}-${index}`),
|
|
3441
2592
|
skillValidationTools: options.skillValidationTools,
|
|
2593
|
+
managedTrees: options.managedTrees,
|
|
2594
|
+
managedScope: scopeLabel.split('/')[1],
|
|
3442
2595
|
});
|
|
3443
2596
|
}
|
|
3444
2597
|
}
|
|
@@ -3453,26 +2606,81 @@ function validateRenderedArtifactPlan(plan, declarations, scopeLabel, options) {
|
|
|
3453
2606
|
}
|
|
3454
2607
|
}
|
|
3455
2608
|
}
|
|
3456
|
-
function
|
|
2609
|
+
function resolveArtifactRendererFixtureRequests(declarations, manifest, requiresNonSensitiveArtifactInput) {
|
|
2610
|
+
const awOwned = new Map();
|
|
2611
|
+
const codexNative = new Map();
|
|
2612
|
+
for (const declaration of declarations) {
|
|
2613
|
+
if (declaration.delivery?.mode === 'codex-native') {
|
|
2614
|
+
const scope = declaration.scopes.includes('user') ? 'user' : 'project';
|
|
2615
|
+
const request = codexNative.get(scope) ?? {
|
|
2616
|
+
kind: 'codex-native',
|
|
2617
|
+
surface: 'codex',
|
|
2618
|
+
scope,
|
|
2619
|
+
configScope: 'user',
|
|
2620
|
+
declarations: [],
|
|
2621
|
+
};
|
|
2622
|
+
request.declarations.push(declaration);
|
|
2623
|
+
codexNative.set(scope, request);
|
|
2624
|
+
continue;
|
|
2625
|
+
}
|
|
2626
|
+
const surface = declaration.surface?.kind === 'aw'
|
|
2627
|
+
? declaration.surface.name
|
|
2628
|
+
: declaration.platform ?? declaration.distribution;
|
|
2629
|
+
if (!surface)
|
|
2630
|
+
continue;
|
|
2631
|
+
for (const scope of declaration.scopes) {
|
|
2632
|
+
const key = `${surface}:${scope}`;
|
|
2633
|
+
const request = awOwned.get(key) ?? {
|
|
2634
|
+
kind: 'aw-owned',
|
|
2635
|
+
surface,
|
|
2636
|
+
scope,
|
|
2637
|
+
configScope: scope,
|
|
2638
|
+
declarations: [],
|
|
2639
|
+
};
|
|
2640
|
+
request.declarations.push(declaration);
|
|
2641
|
+
awOwned.set(key, request);
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
const partitionedAwOwned = [...awOwned.values()].flatMap((request) => {
|
|
2645
|
+
const hasNonSensitiveTarget = request.declarations.some(requiresNonSensitiveArtifactInput);
|
|
2646
|
+
const hasSensitiveOtherTarget = request.declarations.some((declaration) => !requiresNonSensitiveArtifactInput(declaration)
|
|
2647
|
+
&& declaration.refreshKeys.some((key) => configFieldContainsSensitiveValue(manifest.config?.fields?.[key])));
|
|
2648
|
+
if (!hasNonSensitiveTarget || !hasSensitiveOtherTarget)
|
|
2649
|
+
return [request];
|
|
2650
|
+
const nonSensitiveDeclarations = request.declarations.filter(requiresNonSensitiveArtifactInput);
|
|
2651
|
+
const otherDeclarations = request.declarations.filter((declaration) => !requiresNonSensitiveArtifactInput(declaration));
|
|
2652
|
+
return [
|
|
2653
|
+
{ ...request, declarations: nonSensitiveDeclarations },
|
|
2654
|
+
{ ...request, declarations: otherDeclarations },
|
|
2655
|
+
];
|
|
2656
|
+
});
|
|
2657
|
+
return [...partitionedAwOwned, ...codexNative.values()];
|
|
2658
|
+
}
|
|
2659
|
+
async function runArtifactRendererFixture(pluginDir, manifest, renderEntry, declarations, resolvedRuntimeImports, skillValidationTools, managedContext, requiresNonSensitiveArtifactInput) {
|
|
3457
2660
|
if (!renderEntry || declarations.length === 0)
|
|
3458
|
-
return;
|
|
3459
|
-
const
|
|
2661
|
+
return [];
|
|
2662
|
+
const requests = resolveArtifactRendererFixtureRequests(declarations, manifest, requiresNonSensitiveArtifactInput);
|
|
3460
2663
|
const values = defaultConfigValues(manifest);
|
|
3461
2664
|
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'aw-artifact-render-'));
|
|
2665
|
+
const returnedArtifacts = [];
|
|
3462
2666
|
try {
|
|
3463
|
-
for (const
|
|
3464
|
-
const
|
|
2667
|
+
for (const [requestIndex, request] of requests.entries()) {
|
|
2668
|
+
const { kind, surface, scope, configScope, declarations: scopedDeclarations, } = request;
|
|
3465
2669
|
const distribution = surface === 'agent-skills' ? surface : undefined;
|
|
3466
|
-
const
|
|
2670
|
+
const awSurface = surface === 'managed-commands';
|
|
2671
|
+
const platform = distribution || awSurface ? undefined : surface;
|
|
3467
2672
|
const event = distribution ? 'materialize' : 'install';
|
|
3468
|
-
const
|
|
3469
|
-
|
|
3470
|
-
const
|
|
3471
|
-
const
|
|
2673
|
+
const requestRoot = path.join(tempRoot, `${requestIndex}-${kind}-${surface}-${scope}`);
|
|
2674
|
+
const rendererAwHome = path.join(requestRoot, 'aw-home');
|
|
2675
|
+
const rendererCodexHome = path.join(requestRoot, 'codex-home');
|
|
2676
|
+
const rendererClaudeHome = path.join(requestRoot, 'claude-home');
|
|
2677
|
+
fs.mkdirSync(requestRoot, { recursive: true });
|
|
2678
|
+
const contextPath = path.join(requestRoot, 'context.json');
|
|
2679
|
+
const runtimeImports = scopedDeclarations.flatMap((target) => (target.runtimeImports ?? []).map((runtimeImport, importIndex) => {
|
|
3472
2680
|
const resolved = resolvedRuntimeImports?.artifactImports.get(authorRuntimeImportKey(target.id, importIndex));
|
|
3473
2681
|
if (!resolved)
|
|
3474
2682
|
return null;
|
|
3475
|
-
const importPath = path.join(
|
|
2683
|
+
const importPath = path.join(requestRoot, `runtime-${target.id}-${importIndex}`);
|
|
3476
2684
|
fs.writeFileSync(importPath, resolved.bytes);
|
|
3477
2685
|
return {
|
|
3478
2686
|
artifactId: target.id,
|
|
@@ -3483,9 +2691,40 @@ function runArtifactRendererFixture(pluginDir, manifest, renderEntry, declaratio
|
|
|
3483
2691
|
digest: resolved.digest,
|
|
3484
2692
|
};
|
|
3485
2693
|
}).filter(Boolean));
|
|
2694
|
+
const refreshKeys = [...new Set(scopedDeclarations.flatMap((target) => target.refreshKeys))];
|
|
2695
|
+
const scopedSchema = Object.fromEntries(refreshKeys.flatMap((key) => manifest.config?.fields?.[key] === undefined
|
|
2696
|
+
? []
|
|
2697
|
+
: [[key, manifest.config.fields[key]]]));
|
|
2698
|
+
const scopedValues = Object.fromEntries(refreshKeys.flatMap((key) => Object.prototype.hasOwnProperty.call(values, key) ? [[key, values[key]]] : []));
|
|
2699
|
+
try {
|
|
2700
|
+
for (const target of scopedDeclarations.filter((declaration) => declaration.runtimeConfig)) {
|
|
2701
|
+
const definition = normalizePluginConfigDefinition(manifest.config);
|
|
2702
|
+
createRuntimeConfigSnapshot({
|
|
2703
|
+
installedIdentity: {
|
|
2704
|
+
sourceId: 'authoring',
|
|
2705
|
+
sourceIdentity: 'authoring',
|
|
2706
|
+
pluginId: `authoring/${manifest.name}`,
|
|
2707
|
+
pluginName: manifest.name,
|
|
2708
|
+
pluginVersion: manifest.version,
|
|
2709
|
+
platform: target.platform,
|
|
2710
|
+
scope: target.delivery?.mode === 'codex-native' ? 'user' : scope,
|
|
2711
|
+
configScope,
|
|
2712
|
+
artifactId: target.id,
|
|
2713
|
+
skillKey: target.skillKey,
|
|
2714
|
+
},
|
|
2715
|
+
declaration: target.runtimeConfig,
|
|
2716
|
+
definition,
|
|
2717
|
+
values,
|
|
2718
|
+
});
|
|
2719
|
+
}
|
|
2720
|
+
}
|
|
2721
|
+
catch (runtimeConfigError) {
|
|
2722
|
+
error(`scripts.artifacts.render fixture (${surface}/${scope}) runtime config failed: ${runtimeConfigError.message}`);
|
|
2723
|
+
continue;
|
|
2724
|
+
}
|
|
3486
2725
|
fs.writeFileSync(contextPath, JSON.stringify({
|
|
3487
2726
|
event,
|
|
3488
|
-
surface: distribution
|
|
2727
|
+
surface: awSurface ? { kind: 'aw', name: 'managed-commands' } : distribution
|
|
3489
2728
|
? { kind: 'distribution', name: distribution }
|
|
3490
2729
|
: { kind: 'platform', name: platform },
|
|
3491
2730
|
...(platform ? { platform } : {}),
|
|
@@ -3498,29 +2737,31 @@ function runArtifactRendererFixture(pluginDir, manifest, renderEntry, declaratio
|
|
|
3498
2737
|
},
|
|
3499
2738
|
config: {
|
|
3500
2739
|
mode: 'artifact-render',
|
|
3501
|
-
targetScope:
|
|
3502
|
-
|
|
3503
|
-
values,
|
|
3504
|
-
sources: Object.fromEntries(Object.keys(
|
|
2740
|
+
targetScope: configScope,
|
|
2741
|
+
...(manifest.config?.fields === undefined ? {} : { schema: scopedSchema }),
|
|
2742
|
+
values: scopedValues,
|
|
2743
|
+
sources: Object.fromEntries(Object.keys(scopedValues).map((key) => [key, 'default'])),
|
|
3505
2744
|
issues: [],
|
|
3506
2745
|
},
|
|
3507
2746
|
changedConfigKeys: [],
|
|
2747
|
+
requestedArtifactIds: scopedDeclarations.map((target) => target.id),
|
|
3508
2748
|
previousArtifacts: [],
|
|
3509
|
-
artifactSources:
|
|
2749
|
+
artifactSources: declarations.filter((target) => (target.target === 'codex.skill' || target.target === 'claude.skill' || target.target === 'agent-skills.skill')
|
|
2750
|
+
&& (distribution ? target.distribution === distribution : target.platform === platform)
|
|
2751
|
+
&& target.scopes.includes(scope)).map((target) => ({
|
|
3510
2752
|
id: target.id,
|
|
3511
2753
|
target: target.target,
|
|
3512
2754
|
sourceDirs: target.sourceDirs.map((sourceDir) => path.join(pluginDir, normalizeRelativePath(sourceDir))),
|
|
3513
2755
|
})),
|
|
2756
|
+
commandSources: (managedContext?.manifest.managedCommands?.packages ?? [])
|
|
2757
|
+
.filter((entry) => scopedDeclarations.some((target) => target.packageKey === entry.key))
|
|
2758
|
+
.map((entry) => ({ packageKey: entry.key, sourceDir: path.join(pluginDir, normalizeRelativePath(entry.sourceDir)) })),
|
|
3514
2759
|
runtimeImports,
|
|
3515
2760
|
paths: {
|
|
3516
|
-
awHome:
|
|
3517
|
-
codexHome:
|
|
3518
|
-
claudeHome:
|
|
2761
|
+
awHome: rendererAwHome,
|
|
2762
|
+
codexHome: rendererCodexHome,
|
|
2763
|
+
claudeHome: rendererClaudeHome,
|
|
3519
2764
|
manifestPath: path.join(pluginDir, '.agents-plugin', 'plugin.json'),
|
|
3520
|
-
configStores: {
|
|
3521
|
-
user: path.join(tempRoot, 'config-user.json'),
|
|
3522
|
-
project: path.join(tempRoot, 'config-project.json'),
|
|
3523
|
-
},
|
|
3524
2765
|
},
|
|
3525
2766
|
renderer: {
|
|
3526
2767
|
runner: renderEntry.runner,
|
|
@@ -3530,14 +2771,17 @@ function runArtifactRendererFixture(pluginDir, manifest, renderEntry, declaratio
|
|
|
3530
2771
|
rendererDigest: 'author-validator-fixture',
|
|
3531
2772
|
},
|
|
3532
2773
|
}), 'utf8');
|
|
3533
|
-
const rendererHome = path.join(
|
|
3534
|
-
const rendererTemp = path.join(
|
|
2774
|
+
const rendererHome = path.join(requestRoot, 'home');
|
|
2775
|
+
const rendererTemp = path.join(requestRoot, 'tmp');
|
|
2776
|
+
for (const rendererStateHome of [rendererAwHome, rendererCodexHome, rendererClaudeHome]) {
|
|
2777
|
+
fs.mkdirSync(rendererStateHome, { recursive: true });
|
|
2778
|
+
}
|
|
3535
2779
|
fs.mkdirSync(rendererHome, { recursive: true });
|
|
3536
2780
|
fs.mkdirSync(rendererTemp, { recursive: true });
|
|
3537
2781
|
const rendererEnv = {
|
|
3538
|
-
AW_HOME:
|
|
3539
|
-
AW_CODEX_HOME:
|
|
3540
|
-
AW_CLAUDE_HOME:
|
|
2782
|
+
AW_HOME: rendererAwHome,
|
|
2783
|
+
AW_CODEX_HOME: rendererCodexHome,
|
|
2784
|
+
AW_CLAUDE_HOME: rendererClaudeHome,
|
|
3541
2785
|
PATH: process.env.PATH ?? process.env.Path ?? '',
|
|
3542
2786
|
AW_PLUGIN_CONTEXT: contextPath,
|
|
3543
2787
|
AW_PLUGIN_ROOT: pluginDir,
|
|
@@ -3546,6 +2790,7 @@ function runArtifactRendererFixture(pluginDir, manifest, renderEntry, declaratio
|
|
|
3546
2790
|
AW_PLUGIN_EVENT: event,
|
|
3547
2791
|
...(platform ? { AW_PLUGIN_PLATFORM: platform } : {}),
|
|
3548
2792
|
...(distribution ? { AW_PLUGIN_DISTRIBUTION: distribution } : {}),
|
|
2793
|
+
...(awSurface ? { AW_PLUGIN_SURFACE: 'managed-commands' } : {}),
|
|
3549
2794
|
AW_PLUGIN_SCOPE: scope,
|
|
3550
2795
|
};
|
|
3551
2796
|
if (process.platform === 'win32') {
|
|
@@ -3592,24 +2837,165 @@ function runArtifactRendererFixture(pluginDir, manifest, renderEntry, declaratio
|
|
|
3592
2837
|
runtimeImports: resolvedRuntimeImports,
|
|
3593
2838
|
tempRoot,
|
|
3594
2839
|
skillValidationTools,
|
|
2840
|
+
managedTrees: managedContext?.trees,
|
|
3595
2841
|
});
|
|
2842
|
+
if (managedContext) {
|
|
2843
|
+
try {
|
|
2844
|
+
const validated = managedContext.tools.validateArtifactPlan({
|
|
2845
|
+
manifest: managedContext.manifest,
|
|
2846
|
+
plan,
|
|
2847
|
+
platform: surface,
|
|
2848
|
+
scope,
|
|
2849
|
+
requestedArtifactIds: scopedDeclarations.map((target) => target.id),
|
|
2850
|
+
renderConfig: { values: scopedValues, sources: Object.fromEntries(Object.keys(scopedValues).map((key) => [key, 'default'])), issues: [] },
|
|
2851
|
+
});
|
|
2852
|
+
returnedArtifacts.push(...validated.artifacts.map((artifact) => ({ artifact, scope, surface })));
|
|
2853
|
+
}
|
|
2854
|
+
catch (cause) {
|
|
2855
|
+
error(`${fieldName}: ${cause.message}`);
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
3596
2858
|
}
|
|
3597
2859
|
}
|
|
3598
2860
|
finally {
|
|
3599
2861
|
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
3600
2862
|
}
|
|
2863
|
+
return returnedArtifacts;
|
|
3601
2864
|
}
|
|
3602
|
-
function validateDynamicArtifactsLightweight(pluginDir, manifest, renderEntry, runtimeImports, skillValidationTools) {
|
|
2865
|
+
async function validateDynamicArtifactsLightweight(pluginDir, manifest, renderEntry, runtimeImports, skillValidationTools, managedContext, requiresNonSensitiveArtifactInput) {
|
|
3603
2866
|
const declarations = validateArtifactsConfigLightweight(pluginDir, manifest);
|
|
2867
|
+
if (managedContext)
|
|
2868
|
+
declarations.push(...getManagedCommandArtifactTargets(managedContext.manifest));
|
|
3604
2869
|
const hasTargets = declarations.length > 0;
|
|
3605
2870
|
const hasRenderer = Boolean(manifest.scripts?.artifacts?.render);
|
|
3606
2871
|
if (hasTargets && !hasRenderer) {
|
|
3607
2872
|
error('artifacts.targets requires scripts.artifacts.render');
|
|
3608
2873
|
}
|
|
3609
2874
|
if (hasRenderer && !hasTargets) {
|
|
3610
|
-
error('scripts.artifacts.render requires artifacts.targets');
|
|
2875
|
+
error('scripts.artifacts.render requires artifacts.targets or dynamic managedCommands targets');
|
|
2876
|
+
}
|
|
2877
|
+
return runArtifactRendererFixture(pluginDir, manifest, renderEntry, declarations, runtimeImports, skillValidationTools, managedContext, requiresNonSensitiveArtifactInput);
|
|
2878
|
+
}
|
|
2879
|
+
async function validateAuthorManagedCommands(pluginDir, manifest, rendered, context) {
|
|
2880
|
+
const { tools, trees } = context;
|
|
2881
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'aw-author-commands-'));
|
|
2882
|
+
const env = { AW_HOME: path.join(tempRoot, 'aw-home'), AW_CODEX_HOME: path.join(tempRoot, 'codex-home'), AW_CLAUDE_HOME: path.join(tempRoot, 'claude-home') };
|
|
2883
|
+
const plugin = { manifest, rootDir: pluginDir, sourceId: 'authoring', sourceIdentity: `sha256:${'0'.repeat(64)}` };
|
|
2884
|
+
const candidates = [];
|
|
2885
|
+
const cleanups = [];
|
|
2886
|
+
try {
|
|
2887
|
+
const root = manifest.skills && path.join(pluginDir, normalizeRelativePath(manifest.skills));
|
|
2888
|
+
if (root && fs.existsSync(root)) {
|
|
2889
|
+
for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
|
|
2890
|
+
if (!entry.isDirectory())
|
|
2891
|
+
continue;
|
|
2892
|
+
for (const platform of targetedPlatforms(manifest)) {
|
|
2893
|
+
if ((manifest.artifacts?.targets ?? []).some((target) => target.platform === platform && target.kind === 'skill' && target.skillKey === entry.name))
|
|
2894
|
+
continue;
|
|
2895
|
+
const files = new Map();
|
|
2896
|
+
collectArtifactSourceFiles(path.join(root, entry.name), `static Skill ${entry.name}`, files);
|
|
2897
|
+
for (const [relative, bytes] of files) {
|
|
2898
|
+
if (isTemplateTextFile(relative))
|
|
2899
|
+
files.set(relative, Buffer.from(renderTemplateForPlatform(manifest, `${normalizeRelativePath(manifest.skills)}/${entry.name}/${relative}`, bytes.toString('utf8'), platform)));
|
|
2900
|
+
}
|
|
2901
|
+
const producer = await tools.managedCommandProducerIdentity({
|
|
2902
|
+
plugin,
|
|
2903
|
+
producer: { kind: 'static-skill', platform, skillKey: entry.name },
|
|
2904
|
+
scope: 'user',
|
|
2905
|
+
cwd: pluginDir,
|
|
2906
|
+
});
|
|
2907
|
+
trees.push({ declaration: { target: `${platform}.skill`, platform, skillKey: entry.name, staticSkill: true }, files, scope: producer.scope });
|
|
2908
|
+
}
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
const setOutputs = (set, platform, scope) => {
|
|
2912
|
+
if (set.mode === 'static')
|
|
2913
|
+
return { set: set.set, state: 'present', entries: set.entries };
|
|
2914
|
+
const output = rendered.find((entry) => entry.artifact.id === `command-set.${set.set}` && entry.scope === scope && entry.surface === platform)?.artifact;
|
|
2915
|
+
if (!output)
|
|
2916
|
+
throw new Error(`commandReferenceInvalid: dynamic command set ${set.set} was not returned`);
|
|
2917
|
+
return { set: set.set, state: output.state, entries: output.entries };
|
|
2918
|
+
};
|
|
2919
|
+
for (const commandPackage of manifest.managedCommands?.packages ?? []) {
|
|
2920
|
+
const sets = (manifest.managedCommands?.sets ?? [])
|
|
2921
|
+
.filter((set) => set.producer.kind === 'package' && set.producer.packageKey === commandPackage.key)
|
|
2922
|
+
.map((set) => setOutputs(set, 'managed-commands', commandPackage.scope));
|
|
2923
|
+
const sourceRoot = path.join(pluginDir, normalizeRelativePath(commandPackage.sourceDir));
|
|
2924
|
+
const files = { ...(rendered.find((entry) => entry.artifact.id === `command-package.${commandPackage.key}`)?.artifact.files ?? {}) };
|
|
2925
|
+
if (manifest.nativeTools) {
|
|
2926
|
+
const readerPath = path.join(pluginDir, normalizeRelativePath(manifest.nativeTools.reader));
|
|
2927
|
+
const relativeReader = path.relative(sourceRoot, readerPath);
|
|
2928
|
+
if (relativeReader && relativeReader !== '..' && !relativeReader.startsWith(`..${path.sep}`) && !path.isAbsolute(relativeReader) && !fs.existsSync(readerPath)) {
|
|
2929
|
+
const readerFile = relativeReader.split(path.sep).join('/');
|
|
2930
|
+
const readerSource = createNativeToolReaderSource();
|
|
2931
|
+
if (Object.hasOwn(files, readerFile) && files[readerFile] !== readerSource)
|
|
2932
|
+
throw new Error('nativeToolReaderMismatch');
|
|
2933
|
+
files[readerFile] = readerSource;
|
|
2934
|
+
}
|
|
2935
|
+
}
|
|
2936
|
+
const prepared = await tools.prepareManagedCommandPackageFiles({ sourceRoot, files, entries: sets.flatMap((set) => set.entries) });
|
|
2937
|
+
cleanups.push(prepared.cleanup);
|
|
2938
|
+
candidates.push({ producer: await tools.managedCommandProducerIdentity({ plugin, producer: { kind: 'package', packageKey: commandPackage.key }, scope: commandPackage.scope, cwd: pluginDir }), sets });
|
|
2939
|
+
}
|
|
2940
|
+
for (const [index, tree] of trees.entries()) {
|
|
2941
|
+
const { declaration } = tree;
|
|
2942
|
+
const scope = declaration.delivery?.mode === 'codex-native' ? 'user' : tree.scope;
|
|
2943
|
+
if (!declaration.platform)
|
|
2944
|
+
continue;
|
|
2945
|
+
const producer = declaration.staticSkill
|
|
2946
|
+
? { kind: 'static-skill', platform: declaration.platform, skillKey: declaration.skillKey }
|
|
2947
|
+
: { kind: 'artifact', artifactId: declaration.id };
|
|
2948
|
+
const sets = (manifest.managedCommands?.sets ?? []).filter((set) => JSON.stringify(set.producer) === JSON.stringify(producer))
|
|
2949
|
+
.map((set) => setOutputs(set, declaration.platform, scope));
|
|
2950
|
+
const resourceRoot = path.join(tempRoot, `skill-${index}`);
|
|
2951
|
+
for (const [relative, bytes] of tree.files) {
|
|
2952
|
+
const target = path.join(resourceRoot, ...relative.split('/'));
|
|
2953
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
2954
|
+
fs.writeFileSync(target, bytes);
|
|
2955
|
+
}
|
|
2956
|
+
await validateCommandEntryFiles(resourceRoot, sets.flatMap((set) => set.entries));
|
|
2957
|
+
candidates.push({ producer: await tools.managedCommandProducerIdentity({ plugin, producer, scope, cwd: pluginDir }), sets });
|
|
2958
|
+
}
|
|
2959
|
+
for (const tree of trees) {
|
|
2960
|
+
if (!tree.declaration.platform)
|
|
2961
|
+
continue;
|
|
2962
|
+
const scope = tree.declaration.delivery?.mode === 'codex-native' ? 'user' : tree.scope;
|
|
2963
|
+
const resolve = await tools.createManagedCommandReferenceResolver({ plugin, platform: tree.declaration.platform, scope, cwd: pluginDir, env, candidates });
|
|
2964
|
+
for (const [relative, bytes] of tree.files) {
|
|
2965
|
+
if (!relative.endsWith('.md'))
|
|
2966
|
+
continue;
|
|
2967
|
+
const text = bytes.toString('utf8');
|
|
2968
|
+
if (parseManagedCommandPlaceholders(text).length > 0 && !(manifest.requires?.aw?.capabilities ?? []).includes('aw.managed-commands.v1'))
|
|
2969
|
+
throw new Error('managed command references require aw.managed-commands.v1');
|
|
2970
|
+
replaceManagedCommandPlaceholders(text, resolve);
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
for (const permission of manifest.extensions?.permissions ?? []) {
|
|
2974
|
+
for (const platform of targetedPlatforms(manifest)) {
|
|
2975
|
+
for (const scope of platform === 'codex' ? ['user'] : ['user', 'project']) {
|
|
2976
|
+
const resolve = await tools.createManagedCommandReferenceResolver({ plugin, platform, scope, cwd: pluginDir, env, candidates });
|
|
2977
|
+
const override = permission.agentOverrides?.[platform];
|
|
2978
|
+
if (override === null)
|
|
2979
|
+
continue;
|
|
2980
|
+
for (const decision of ['allow', 'ask', 'deny'])
|
|
2981
|
+
for (const rule of override?.[decision] ?? permission[decision] ?? [])
|
|
2982
|
+
if (typeof rule !== 'string')
|
|
2983
|
+
resolve(rule.command);
|
|
2984
|
+
if (platform === 'claude')
|
|
2985
|
+
for (const rule of override?.sandbox?.excludedCommands ?? [])
|
|
2986
|
+
resolve(rule.command);
|
|
2987
|
+
}
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
catch (cause) {
|
|
2992
|
+
error(`managed command authoring candidate: ${cause.message}`);
|
|
2993
|
+
}
|
|
2994
|
+
finally {
|
|
2995
|
+
for (const cleanup of cleanups)
|
|
2996
|
+
await cleanup();
|
|
2997
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
3611
2998
|
}
|
|
3612
|
-
runArtifactRendererFixture(pluginDir, manifest, renderEntry, declarations, runtimeImports, skillValidationTools);
|
|
3613
2999
|
}
|
|
3614
3000
|
function validateScriptsLightweight(pluginDir, manifest) {
|
|
3615
3001
|
validateConfigSchemaLightweight(manifest.config);
|
|
@@ -3618,16 +3004,15 @@ function validateScriptsLightweight(pluginDir, manifest) {
|
|
|
3618
3004
|
const scripts = expectRecord(manifest.scripts, 'scripts');
|
|
3619
3005
|
if (!scripts)
|
|
3620
3006
|
return null;
|
|
3621
|
-
validateAllowedKeys(scripts, 'scripts', ['
|
|
3622
|
-
if (scripts.timeoutMs !== undefined &&
|
|
3623
|
-
(!Number.isInteger(scripts.timeoutMs) ||
|
|
3624
|
-
scripts.timeoutMs < SCRIPT_TIMEOUT_MIN_MS ||
|
|
3625
|
-
scripts.timeoutMs > SCRIPT_TIMEOUT_MAX_MS)) {
|
|
3626
|
-
error(`scripts.timeoutMs must be an integer between ${SCRIPT_TIMEOUT_MIN_MS} and ${SCRIPT_TIMEOUT_MAX_MS}`);
|
|
3627
|
-
}
|
|
3007
|
+
validateAllowedKeys(scripts, 'scripts', ['preApply', 'helpers', 'node', 'artifacts', 'tests']);
|
|
3628
3008
|
const runtimeNodeEntries = [];
|
|
3629
3009
|
let artifactRenderEntry = null;
|
|
3630
3010
|
let packageMeta = null;
|
|
3011
|
+
const nativeToolReader = manifest.nativeTools ? normalizeRelativePath(manifest.nativeTools.reader ?? './.aw/native-tool-reader.mjs') : undefined;
|
|
3012
|
+
const runtimeConfigSkillRoots = (manifest.artifacts?.targets ?? [])
|
|
3013
|
+
.filter((target) => target.runtimeConfig !== undefined)
|
|
3014
|
+
.flatMap((target) => target.sourceDirs ?? [])
|
|
3015
|
+
.map(normalizeRelativePath);
|
|
3631
3016
|
if (scripts.helpers !== undefined) {
|
|
3632
3017
|
const helpers = expectRecord(scripts.helpers, 'scripts.helpers');
|
|
3633
3018
|
if (helpers) {
|
|
@@ -3641,17 +3026,21 @@ function validateScriptsLightweight(pluginDir, manifest) {
|
|
|
3641
3026
|
runtimeNodeEntries.push({ path: helperPath, fieldName: 'scripts.helpers.config' });
|
|
3642
3027
|
}
|
|
3643
3028
|
}
|
|
3644
|
-
if (scripts.
|
|
3645
|
-
const
|
|
3646
|
-
if (
|
|
3647
|
-
validateAllowedKeys(
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3029
|
+
if (scripts.preApply !== undefined) {
|
|
3030
|
+
const preApply = expectRecord(scripts.preApply, 'scripts.preApply');
|
|
3031
|
+
if (preApply) {
|
|
3032
|
+
validateAllowedKeys(preApply, 'scripts.preApply', ['path', 'timeoutMs']);
|
|
3033
|
+
const preApplyPath = normalizePluginScriptPath(pluginDir, preApply.path, 'scripts.preApply.path');
|
|
3034
|
+
validateDeclaredScriptFile(pluginDir, preApplyPath, 'scripts.preApply.path');
|
|
3035
|
+
if (preApplyPath && !isNodeRunnablePath(preApplyPath)) {
|
|
3036
|
+
error('scripts.preApply.path must use a Node runnable extension (.js, .mjs, or .cjs)');
|
|
3037
|
+
}
|
|
3038
|
+
if (preApply.timeoutMs !== undefined && (!Number.isInteger(preApply.timeoutMs)
|
|
3039
|
+
|| preApply.timeoutMs < SCRIPT_TIMEOUT_MIN_MS || preApply.timeoutMs > SCRIPT_TIMEOUT_MAX_MS)) {
|
|
3040
|
+
error(`scripts.preApply.timeoutMs must be an integer between ${SCRIPT_TIMEOUT_MIN_MS} and ${SCRIPT_TIMEOUT_MAX_MS}`);
|
|
3654
3041
|
}
|
|
3042
|
+
if (preApplyPath)
|
|
3043
|
+
runtimeNodeEntries.push({ path: preApplyPath, fieldName: 'scripts.preApply.path' });
|
|
3655
3044
|
}
|
|
3656
3045
|
}
|
|
3657
3046
|
if (scripts.node !== undefined) {
|
|
@@ -3671,6 +3060,8 @@ function validateScriptsLightweight(pluginDir, manifest) {
|
|
|
3671
3060
|
label: 'scripts.node.build import contract',
|
|
3672
3061
|
packageMeta,
|
|
3673
3062
|
allowDevDependencies: true,
|
|
3063
|
+
runtimeConfigSkillRoots,
|
|
3064
|
+
nativeToolReader,
|
|
3674
3065
|
});
|
|
3675
3066
|
}
|
|
3676
3067
|
}
|
|
@@ -3720,6 +3111,8 @@ function validateScriptsLightweight(pluginDir, manifest) {
|
|
|
3720
3111
|
label: 'scripts.tests.node import contract',
|
|
3721
3112
|
packageMeta,
|
|
3722
3113
|
allowDevDependencies: true,
|
|
3114
|
+
runtimeConfigSkillRoots,
|
|
3115
|
+
nativeToolReader,
|
|
3723
3116
|
});
|
|
3724
3117
|
}
|
|
3725
3118
|
}
|
|
@@ -3729,6 +3122,8 @@ function validateScriptsLightweight(pluginDir, manifest) {
|
|
|
3729
3122
|
label: 'runtime Node import contract',
|
|
3730
3123
|
packageMeta,
|
|
3731
3124
|
allowDevDependencies: false,
|
|
3125
|
+
runtimeConfigSkillRoots,
|
|
3126
|
+
nativeToolReader,
|
|
3732
3127
|
});
|
|
3733
3128
|
return artifactRenderEntry;
|
|
3734
3129
|
}
|
|
@@ -4130,8 +3525,6 @@ function printUsage() {
|
|
|
4130
3525
|
aw plugin validate <plugin-directory> [options]
|
|
4131
3526
|
|
|
4132
3527
|
Options:
|
|
4133
|
-
--catalog-min-version <semver>
|
|
4134
|
-
Validate compatibility.aw.minVersion against a target catalog.
|
|
4135
3528
|
--strict-agent-skills Allow only Agent Skills portable core frontmatter.
|
|
4136
3529
|
--claude-skill-profile <minimum|current>
|
|
4137
3530
|
Validate Claude fields at AW minimum 2.1.88 (default) or current docs.
|
|
@@ -4200,8 +3593,32 @@ async function validatePluginDir(pluginDir, options) {
|
|
|
4200
3593
|
}
|
|
4201
3594
|
}
|
|
4202
3595
|
const skillValidationTools = await tryLoadCliSkillValidationTools();
|
|
3596
|
+
let normalizedManifest;
|
|
4203
3597
|
try {
|
|
4204
|
-
await cliManifestTools.validateSourcePluginManifest(pluginDir, manifest);
|
|
3598
|
+
normalizedManifest = await cliManifestTools.validateSourcePluginManifest(pluginDir, manifest);
|
|
3599
|
+
// Renderer fixtures consume the same compiled config as installed runtime consumers.
|
|
3600
|
+
if (normalizedManifest.config)
|
|
3601
|
+
manifest.config = normalizedManifest.config;
|
|
3602
|
+
const requirements = await cliManifestTools.resolvePluginRequirements({
|
|
3603
|
+
pluginRoot: pluginDir,
|
|
3604
|
+
manifest: normalizedManifest,
|
|
3605
|
+
implementedCapabilities: cliManifestTools.implementedCapabilities,
|
|
3606
|
+
});
|
|
3607
|
+
if (requirements.unsupportedByCurrentAw.length > 0) {
|
|
3608
|
+
capabilityDiagnostic = {
|
|
3609
|
+
errorCode: 'awCapabilityUnsupported',
|
|
3610
|
+
missingCapabilities: requirements.unsupportedByCurrentAw,
|
|
3611
|
+
availableCapabilities: [...cliManifestTools.implementedCapabilities].sort(),
|
|
3612
|
+
};
|
|
3613
|
+
error(`[awCapabilityUnsupported] missingCapabilities=${JSON.stringify(requirements.unsupportedByCurrentAw)}`);
|
|
3614
|
+
}
|
|
3615
|
+
const satisfiedCapabilities = new Set([...requirements.declared, ...requirements.derived]);
|
|
3616
|
+
const advisoryObservations = requirements.observations.filter((observation) => (observation.code !== 'aw-command-detected'
|
|
3617
|
+
|| observation.capability === undefined
|
|
3618
|
+
|| !satisfiedCapabilities.has(observation.capability)));
|
|
3619
|
+
for (const observation of advisoryObservations) {
|
|
3620
|
+
warn(`[awCapabilityObservation] ${observation.path}: ${observation.message}`);
|
|
3621
|
+
}
|
|
4205
3622
|
}
|
|
4206
3623
|
catch (e) {
|
|
4207
3624
|
error(e.message);
|
|
@@ -4212,8 +3629,7 @@ async function validatePluginDir(pluginDir, options) {
|
|
|
4212
3629
|
validateNoBuildOutputDirs(pluginDir);
|
|
4213
3630
|
validateDeclaredNativeAssets(pluginDir, manifest);
|
|
4214
3631
|
validateCanonicalSkillLayout(pluginDir, manifest);
|
|
4215
|
-
|
|
4216
|
-
validatePluginEvalManifest(pluginDir, manifest);
|
|
3632
|
+
await validatePluginEvalManifest(pluginDir, manifest);
|
|
4217
3633
|
const artifactRenderEntry = validateScriptsLightweight(pluginDir, manifest);
|
|
4218
3634
|
const runtimeImports = await resolveAuthorRuntimeImports({
|
|
4219
3635
|
pluginDir,
|
|
@@ -4222,7 +3638,12 @@ async function validatePluginDir(pluginDir, options) {
|
|
|
4222
3638
|
cliManifestTools,
|
|
4223
3639
|
});
|
|
4224
3640
|
validateStaticPortableFinalTrees(pluginDir, manifest, runtimeImports, skillValidationTools);
|
|
4225
|
-
|
|
3641
|
+
const managedContext = normalizedManifest
|
|
3642
|
+
? { manifest: normalizedManifest, tools: cliManifestTools, trees: [] }
|
|
3643
|
+
: undefined;
|
|
3644
|
+
const rendered = await validateDynamicArtifactsLightweight(pluginDir, manifest, artifactRenderEntry, runtimeImports, skillValidationTools, managedContext, cliManifestTools.requiresNonSensitiveArtifactInput);
|
|
3645
|
+
if (managedContext)
|
|
3646
|
+
await validateAuthorManagedCommands(pluginDir, normalizedManifest, rendered ?? [], managedContext);
|
|
4226
3647
|
validateMcpContent(pluginDir, manifest);
|
|
4227
3648
|
validateAppsContent(pluginDir, manifest);
|
|
4228
3649
|
warnCodexProjectScopeLimitations(manifest);
|
|
@@ -4263,7 +3684,6 @@ function parseArgs(argv) {
|
|
|
4263
3684
|
const args = argv.slice(2);
|
|
4264
3685
|
const options = {
|
|
4265
3686
|
pluginDir: null,
|
|
4266
|
-
catalogMinVersion: null,
|
|
4267
3687
|
strictAgentSkills: false,
|
|
4268
3688
|
claudeSkillProfile: 'minimum',
|
|
4269
3689
|
versionBase: 'HEAD',
|
|
@@ -4320,18 +3740,6 @@ function parseArgs(argv) {
|
|
|
4320
3740
|
}
|
|
4321
3741
|
exitCliError(`Unknown argument: ${arg}`, options.json);
|
|
4322
3742
|
}
|
|
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
3743
|
exitCliError(`Unknown option: ${arg}`, options.json);
|
|
4336
3744
|
}
|
|
4337
3745
|
return options;
|
|
@@ -4363,7 +3771,6 @@ async function main() {
|
|
|
4363
3771
|
const repoRoot = requestedWorkspaceRoot ?? discoverCanonicalRepoRoot(repoRootCandidate);
|
|
4364
3772
|
const gitRoot = discoverCanonicalRepoRoot(findRepoRootCandidate(resolved));
|
|
4365
3773
|
await validatePluginDir(resolved, {
|
|
4366
|
-
catalogMinVersion: args.catalogMinVersion,
|
|
4367
3774
|
strictAgentSkills: args.strictAgentSkills,
|
|
4368
3775
|
claudeSkillProfile: args.claudeSkillProfile,
|
|
4369
3776
|
versionBase: args.versionBase,
|
|
@@ -4377,9 +3784,12 @@ async function main() {
|
|
|
4377
3784
|
profile: args.strictAgentSkills ? 'agent-skills-portable-core' : 'aw-source',
|
|
4378
3785
|
claudeSkillProfile: args.claudeSkillProfile,
|
|
4379
3786
|
pluginDirectory: resolved,
|
|
3787
|
+
workspaceRoot: process.env.AW_AUTHORING_WORKSPACE_ROOT ?? repoRoot,
|
|
3788
|
+
workspaceRootResolvedFrom: process.env.AW_AUTHORING_WORKSPACE_ROOT_RESOLVED_FROM ?? 'explicit',
|
|
4380
3789
|
versionCheck: pluginVersionCheckResult,
|
|
4381
3790
|
warnings,
|
|
4382
3791
|
errors,
|
|
3792
|
+
...(capabilityDiagnostic ?? {}),
|
|
4383
3793
|
}, null, 2));
|
|
4384
3794
|
if (errors.length > 0) {
|
|
4385
3795
|
process.exit(1);
|