@aiwg/cli 2026.10.3 → 2026.10.4
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/README.md +2 -2
- package/agentic/code/providers/model-capabilities.v1.json +96 -4
- package/bin/aiwg.mjs +50 -27
- package/dist/src/agents/grounding/knowledge-base.js +54 -6
- package/dist/src/artifacts/browser-export.js +6 -0
- package/dist/src/artifacts/checksum-manifest.js +1 -0
- package/dist/src/artifacts/citation-parser.js +173 -92
- package/dist/src/artifacts/citation-table-schema.js +59 -0
- package/dist/src/artifacts/cli.js +57 -18
- package/dist/src/artifacts/corpus-tools/snapshot.js +1 -1
- package/dist/src/artifacts/corpus-tools/vision-extract.js +2 -0
- package/dist/src/artifacts/corpus-views/ref-parser.js +3 -1
- package/dist/src/artifacts/corpus-views/renderers.js +3 -3
- package/dist/src/artifacts/discovery-eval.js +6 -4
- package/dist/src/artifacts/fortemi-core-query-adapter.js +36 -3
- package/dist/src/artifacts/fortemi-core-sync.js +166 -40
- package/dist/src/artifacts/framework-capabilities.js +171 -0
- package/dist/src/artifacts/framework-index-paths.js +73 -0
- package/dist/src/artifacts/framework-source-digest.js +75 -0
- package/dist/src/artifacts/index-builder.js +285 -131
- package/dist/src/artifacts/index-files.js +132 -18
- package/dist/src/artifacts/index-preparation.js +65 -0
- package/dist/src/artifacts/index-refresh.js +576 -0
- package/dist/src/artifacts/index-worker.mjs +9 -0
- package/dist/src/artifacts/prebuilt-build-lock.js +47 -4
- package/dist/src/artifacts/prebuilt-deployment.js +46 -0
- package/dist/src/artifacts/prebuilt-relations.js +80 -0
- package/dist/src/artifacts/query-engine.js +410 -88
- package/dist/src/artifacts/relations-builder.js +168 -0
- package/dist/src/artifacts/relations.js +148 -0
- package/dist/src/artifacts/stats.js +5 -2
- package/dist/src/artifacts/types.js +30 -10
- package/dist/src/artifacts/watcher.js +1 -1
- package/dist/src/auth/credential-store.js +2 -1
- package/dist/src/catalog/cli.d.mts +5 -0
- package/dist/src/channel/manager.d.mts +20 -0
- package/dist/src/channel/manager.mjs +19 -2
- package/dist/src/cli/command-log.js +2 -3
- package/dist/src/cli/handlers/artifacts.js +3 -0
- package/dist/src/cli/handlers/auth.js +3 -0
- package/dist/src/cli/handlers/decision.js +19 -12
- package/dist/src/cli/handlers/diagnose.js +4 -3
- package/dist/src/cli/handlers/effect.js +4 -2
- package/dist/src/cli/handlers/evidence.js +3 -0
- package/dist/src/cli/handlers/help.js +1 -0
- package/dist/src/cli/handlers/index.js +3 -1
- package/dist/src/cli/handlers/init.js +4 -1
- package/dist/src/cli/handlers/lint.js +5 -0
- package/dist/src/cli/handlers/models.js +104 -8
- package/dist/src/cli/handlers/ralph-launcher.js +2 -0
- package/dist/src/cli/handlers/refresh.js +12 -0
- package/dist/src/cli/handlers/resource-versions.js +3 -0
- package/dist/src/cli/handlers/rules.js +195 -0
- package/dist/src/cli/handlers/run.js +4 -0
- package/dist/src/cli/handlers/runtime-info.js +1 -2
- package/dist/src/cli/handlers/script-runner.js +3 -2
- package/dist/src/cli/handlers/session.js +19 -9
- package/dist/src/cli/handlers/sessions.js +98 -37
- package/dist/src/cli/handlers/setup-manifest.js +39 -44
- package/dist/src/cli/handlers/setup.js +41 -0
- package/dist/src/cli/handlers/steward.js +11 -0
- package/dist/src/cli/handlers/subcommands.js +57 -2
- package/dist/src/cli/handlers/uhp.js +3 -0
- package/dist/src/cli/handlers/use.js +294 -55
- package/dist/src/cli/handlers/utilities.js +26 -10
- package/dist/src/cli/handlers/version.js +8 -0
- package/dist/src/cli/router.js +15 -1
- package/dist/src/cli/services/deployment-verification.js +19 -9
- package/dist/src/cli/services/provider-prepare-coordinator.js +24 -0
- package/dist/src/cli/skill-usage.js +2 -3
- package/dist/src/config/aiwg-config.js +87 -37
- package/dist/src/config/cli.js +111 -5
- package/dist/src/config/decision-config.js +106 -0
- package/dist/src/config/dispatch-thresholds-runtime.d.mts +8 -0
- package/dist/src/config/gitignore.js +50 -0
- package/dist/src/config/index-graph-overrides.js +36 -0
- package/dist/src/config/project-artifacts-health.d.mts +37 -0
- package/dist/src/config/project-artifacts-runtime.d.mts +16 -0
- package/dist/src/config/user-config-dir.d.mts +9 -0
- package/dist/src/config/user-config.js +2 -1
- package/dist/src/config/user-registry.js +23 -15
- package/dist/src/config/user-state.js +7 -0
- package/dist/src/decision/ask.js +203 -43
- package/dist/src/decision/context-live-qualification.js +2 -2
- package/dist/src/decision/driver.js +13 -8
- package/dist/src/decision/ensemble/aggregate.js +23 -10
- package/dist/src/decision/ensemble-study/multifact-study.js +6 -2
- package/dist/src/decision/entry.js +2 -15
- package/dist/src/decision/graph-live-qualification.js +1 -1
- package/dist/src/decision/graph-run.js +9 -0
- package/dist/src/decision/heldout/collector.js +5 -13
- package/dist/src/decision/heldout/contract.js +30 -7
- package/dist/src/decision/jev-credentials.js +15 -1
- package/dist/src/decision/ledger.js +131 -28
- package/dist/src/decision/offload-activation.js +137 -0
- package/dist/src/decision/offload.js +550 -0
- package/dist/src/decision/qualification/load.js +4 -6
- package/dist/src/decision/savings.js +28 -0
- package/dist/src/effects/types.js +5 -1
- package/dist/src/effects/verifiers/git.js +2 -1
- package/dist/src/effects/verifiers/tracker.js +26 -14
- package/dist/src/extensions/commands/definitions.js +19 -0
- package/dist/src/extensions/project-local-remove.js +36 -1
- package/dist/src/gates/providers/loader.js +61 -17
- package/dist/src/installation/manager-command.d.mts +18 -0
- package/dist/src/installation/manager-command.mjs +14 -2
- package/dist/src/installation/manager.d.mts +49 -0
- package/dist/src/installation/manager.mjs +78 -3
- package/dist/src/issues/cli.js +4 -4
- package/dist/src/issues/local.js +24 -5
- package/dist/src/jobs/runner.js +1 -1
- package/dist/src/lint/cli.js +27 -1
- package/dist/src/lint/loader.js +2 -1
- package/dist/src/lint/reporters.js +25 -1
- package/dist/src/lint/rule-activation.js +43 -0
- package/dist/src/lint/runner.js +137 -62
- package/dist/src/mcp/adapters/codex-runtime.js +7 -1
- package/dist/src/mcp/claude-install.d.mts +4 -0
- package/dist/src/mcp/claude-install.mjs +41 -0
- package/dist/src/mcp/cli.d.mts +1 -0
- package/dist/src/mcp/cli.mjs +36 -11
- package/dist/src/mcp/config-file.d.mts +28 -0
- package/dist/src/mcp/config-layers.d.mts +36 -0
- package/dist/src/mcp/credentials.d.mts +51 -0
- package/dist/src/mcp/grok-build-config.d.mts +6 -0
- package/dist/src/mcp/grok-build-config.mjs +1 -1
- package/dist/src/mcp/helpers.mjs +3 -2
- package/dist/src/mcp/muse-native-config.d.mts +13 -0
- package/dist/src/mcp/muse-native-config.mjs +1 -0
- package/dist/src/mcp/omp-config.d.mts +4 -0
- package/dist/src/mcp/registry.js +3 -93
- package/dist/src/mcp/registry.mjs +3 -87
- package/dist/src/mcp/server-config.d.mts +3 -0
- package/dist/src/mcp/server-config.mjs +89 -0
- package/dist/src/mcp/server.mjs +37 -375
- package/dist/src/mcp/test-simple.mjs +2 -1
- package/dist/src/mcp/toml-editor.d.mts +4 -0
- package/dist/src/mcp/toml-string.d.mts +1 -0
- package/dist/src/mcp/toml-strip-mcp.d.mts +1 -0
- package/dist/src/mcp/tool-filters.d.mts +46 -0
- package/dist/src/mcp/tools/decision.mjs +12 -11
- package/dist/src/mcp/tools/discovery.mjs +62 -55
- package/dist/src/mcp/tools/orchestration.mjs +12 -2
- package/dist/src/mcp/tools/worker-invoke.mjs +1340 -23
- package/dist/src/mcp/tools/worker-shell-control.mjs +52 -21
- package/dist/src/mcp/tools/worker-stdin-runtime.mjs +3 -3
- package/dist/src/mission-protocol/codecs.js +8 -0
- package/dist/src/models/config-loader.js +7 -2
- package/dist/src/models/model-capabilities.v1.json +96 -4
- package/dist/src/models/model-discovery.js +76 -11
- package/dist/src/models/provider-policy.js +101 -7
- package/dist/src/models/qualification-digest.d.mts +2 -0
- package/dist/src/models/resolver.js +12 -10
- package/dist/src/models/worker-direct-readiness.js +65 -44
- package/dist/src/models/worker-dispatch-offer.js +3 -3
- package/dist/src/models/worker-health-readiness.js +16 -3
- package/dist/src/models/worker-installation.d.mts +39 -0
- package/dist/src/models/worker-installation.mjs +135 -28
- package/dist/src/process/bounded-child-process.d.mts +84 -0
- package/dist/src/process/bounded-child-process.mjs +43 -0
- package/dist/src/process/cli-command.d.mts +16 -0
- package/dist/src/process/signal-child.d.mts +6 -0
- package/dist/src/process/signal-child.mjs +7 -0
- package/dist/src/providers/deployed-skill-roots.js +20 -0
- package/dist/src/providers/grok-build-acp.js +3 -2
- package/dist/src/providers/grok-build-headless.js +12 -4
- package/dist/src/providers/grok-build-inspect.js +1 -0
- package/dist/src/providers/omp-agent.d.mts +8 -0
- package/dist/src/providers/omp-diagnostics.d.mts +1 -0
- package/dist/src/providers/omp-paths.d.mts +13 -0
- package/dist/src/providers/skill-ownership.d.mts +9 -0
- package/dist/src/providers/skill-ownership.mjs +88 -0
- package/dist/src/rules/activation.d.mts +21 -0
- package/dist/src/rules/activation.mjs +62 -0
- package/dist/src/rules/inline-budget.d.mts +26 -0
- package/dist/src/rules/inline-budget.mjs +66 -0
- package/dist/src/rules/inventory.js +129 -0
- package/dist/src/rules/pin-state.js +73 -0
- package/dist/src/rules/pins.d.mts +14 -0
- package/dist/src/rules/pins.mjs +112 -0
- package/dist/src/rules/rule-frontmatter.schema.json +197 -0
- package/dist/src/serve/mission-conductor.js +38 -12
- package/dist/src/serve/worker-invocation.js +30 -10
- package/dist/src/serve/worker-stdin-frame.d.mts +10 -0
- package/dist/src/sessions/adapter-registry.js +23 -19
- package/dist/src/sessions/adapters/claude.js +39 -54
- package/dist/src/sessions/adapters/codex.js +82 -67
- package/dist/src/sessions/adapters/copilot.js +4 -4
- package/dist/src/sessions/adapters/cursor.js +40 -57
- package/dist/src/sessions/adapters/deepseek-harness.js +15 -12
- package/dist/src/sessions/adapters/factory.js +31 -27
- package/dist/src/sessions/adapters/generic.js +10 -10
- package/dist/src/sessions/adapters/grok-build.js +11 -11
- package/dist/src/sessions/adapters/grokbot.js +24 -13
- package/dist/src/sessions/adapters/hermes.js +27 -29
- package/dist/src/sessions/adapters/muse-workspace-scan.js +31 -11
- package/dist/src/sessions/adapters/muse.js +150 -93
- package/dist/src/sessions/adapters/omp.js +28 -14
- package/dist/src/sessions/adapters/openclaw.js +89 -79
- package/dist/src/sessions/adapters/opencode.js +27 -24
- package/dist/src/sessions/adapters/openhuman.js +18 -60
- package/dist/src/sessions/adapters/pi.js +29 -14
- package/dist/src/sessions/adapters/warp.js +3 -3
- package/dist/src/sessions/adapters/windsurf.js +19 -48
- package/dist/src/sessions/batch-contracts.js +16 -8
- package/dist/src/sessions/batch-import.js +51 -11
- package/dist/src/sessions/contracts.js +40 -2
- package/dist/src/sessions/coverage-preflight.js +1 -1
- package/dist/src/sessions/export-plan.js +4 -3
- package/dist/src/sessions/import-exclusions.js +72 -0
- package/dist/src/sessions/import-lease.js +34 -18
- package/dist/src/sessions/import-limits.js +37 -0
- package/dist/src/sessions/importer.js +60 -38
- package/dist/src/sessions/lifecycle.js +45 -0
- package/dist/src/sessions/policy.js +133 -25
- package/dist/src/sessions/readers.js +106 -53
- package/dist/src/sessions/refresh.js +106 -0
- package/dist/src/sessions/repository.js +147 -52
- package/dist/src/sessions/workspace-discovery.js +221 -56
- package/dist/src/sessions/workspace-matching.js +107 -0
- package/dist/src/skills/importer.js +1 -0
- package/dist/src/smiths/context-pipeline/aiwg-md.js +11 -0
- package/dist/src/smiths/context-pipeline/claude-hook.js +3 -26
- package/dist/src/smiths/context-pipeline/finalization.js +6 -5
- package/dist/src/smiths/context-pipeline/generator.js +21 -4
- package/dist/src/smiths/context-pipeline/parallelism-section.js +7 -15
- package/dist/src/smiths/mcpsmith/generator.js +1 -1
- package/dist/src/smiths/toolsmith/runtime-discovery.d.mts +10 -0
- package/dist/src/smiths/toolsmith/runtime-discovery.mjs +76 -18
- package/dist/src/storage/backends/fs.js +18 -76
- package/dist/src/storage/backends/obsidian.js +1 -0
- package/dist/src/tracker/forge-http.js +2 -1
- package/dist/src/update/checker.d.mts +2 -0
- package/dist/src/update/service.d.mts +22 -0
- package/dist/src/utils/contained-filesystem.d.mts +37 -0
- package/dist/src/utils/contained-filesystem.mjs +683 -0
- package/dist/src/utils/temporary-directory.js +36 -0
- package/dist/src/writing/example-generator.js +1 -1
- package/package.json +15 -2
- package/schemas/models/model-catalog.v1.schema.json +1 -1
- package/schemas/models/provider-model-capabilities.v1.schema.json +6 -0
- package/tools/_resolve-impl.mjs +13 -15
- package/tools/agents/deploy-agents.mjs +49 -21
- package/tools/agents/providers/base.mjs +104 -134
- package/tools/agents/providers/claude.mjs +8 -24
- package/tools/agents/providers/codex.mjs +18 -11
- package/tools/agents/providers/cursor.mjs +19 -18
- package/tools/agents/providers/grok-build.mjs +3 -1
- package/tools/agents/providers/hermes.mjs +57 -47
- package/tools/agents/providers/openclaw.mjs +9 -4
- package/tools/agents/providers/pi.mjs +5 -0
- package/tools/agents/providers/provider-prepare-channel.mjs +5 -0
- package/tools/agents/providers/provider-prepare-coordinator.mjs +247 -0
- package/tools/agents/providers/provider-prepare-worker.mjs +84 -0
- package/tools/agents/providers/rule-delivery.mjs +1270 -0
- package/tools/agents/providers/warp.mjs +20 -26
- package/tools/cli/decision-offload-report.mjs +40 -0
- package/tools/cli/workspace-status.mjs +862 -0
- package/tools/plugin/package-plugins.mjs +186 -8
- package/tools/plugin/plugin-links.mjs +114 -0
- package/tools/providers/antigravity-transport.mjs +3 -1
- package/tools/providers/deepseek-harness-transport.mjs +6 -4
- package/tools/providers/omp-transport.mjs +4 -2
- package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +1748 -0
- package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +55 -0
- package/tools/ralph-external/lib/omp-adapter.mjs +1 -1
- package/tools/ralph-external/lib/pi-adapter.mjs +0 -4
- package/tools/ralph-external/lib/provider-adapter.mjs +59 -1
- package/tools/ralph-external/lib/recovery-probe.mjs +72 -0
- package/tools/ralph-external/lib/session-environment.mjs +28 -0
- package/tools/ralph-external/lib/systemd-client.mjs +49 -0
- package/tools/ralph-external/lib/worker-health.mjs +1 -1
- package/tools/ralph-external/lib/worker-isolation.mjs +717 -0
- package/tools/ralph-external/lib/worker-overflow.mjs +50 -0
- package/tools/ralph-external/process-monitor.mjs +1017 -0
- package/tools/ralph-external/session-launcher.mjs +5057 -0
- package/tools/scripts/resource-preflight.mjs +541 -0
- package/tools/skills/deploy-skills-codex.mjs +2 -1
- package/vscode-extension/schemas/aiwg.config.v1.json +1194 -231
|
@@ -507,9 +507,14 @@ Options:
|
|
|
507
507
|
--bundle <name> Limit deployment verification to one bundle
|
|
508
508
|
--scope <scope> Verification scope: project or user
|
|
509
509
|
--user Alias for --scope user
|
|
510
|
+
--qualify With --probe-workers, record installation and configured model/role approval
|
|
511
|
+
--yes Confirm worker qualification without a terminal
|
|
512
|
+
--dry-run With --probe-workers, preview qualification without writes
|
|
513
|
+
--cli NAME Limit worker probes/qualification to one configured CLI
|
|
514
|
+
--decision-offload Show offline advisory decision readiness and adoption report
|
|
510
515
|
--probe-workers Run bounded worker CLI version probes (default: config-only, no subprocess)
|
|
511
516
|
--project-local Include project-local artifact diagnostics
|
|
512
|
-
--json Emit machine-readable output (with --probe-workers: one worker-probe record per CLI; with --deployment: deployment verification)
|
|
517
|
+
--json Emit machine-readable output (with --probe-workers: one worker-probe record per CLI; with --deployment: deployment verification; with --decision-offload: offline snapshot)
|
|
513
518
|
--verbose, -v Include verbose diagnostics
|
|
514
519
|
--help, -h Show this help
|
|
515
520
|
`;
|
|
@@ -537,10 +542,10 @@ export const doctorHandler = {
|
|
|
537
542
|
async execute(ctx) {
|
|
538
543
|
// Validate before any early-return mode: deployment and user-scope doctor
|
|
539
544
|
// never invoke doctor.mjs, so they must enforce the same flag contract.
|
|
540
|
-
const valueFlags = ['--provider', '--bundle', '--scope', '--context-baseline', '--context-budget-tokens'];
|
|
545
|
+
const valueFlags = ['--provider', '--bundle', '--scope', '--context-baseline', '--context-budget-tokens', '--cli'];
|
|
541
546
|
const knownFlags = new Set([
|
|
542
|
-
...valueFlags, '--all-providers', '--probe-workers', '--no-budget-check', '--strict-context', '--json',
|
|
543
|
-
'--deployment', '--user', '--verbose', '-v', '--project-local', '--quiet', '--help', '-h',
|
|
547
|
+
...valueFlags, '--all-providers', '--probe-workers', '--qualify', '--yes', '--dry-run', '--no-budget-check', '--strict-context', '--json',
|
|
548
|
+
'--decision-offload', '--deployment', '--user', '--verbose', '-v', '--project-local', '--quiet', '--help', '-h',
|
|
544
549
|
]);
|
|
545
550
|
const hasSwitch = (name) => hasPositionalOrSwitch(ctx.args, name, valueFlags);
|
|
546
551
|
const values = new Map();
|
|
@@ -558,8 +563,14 @@ export const doctorHandler = {
|
|
|
558
563
|
throw new Error(`Unknown flag: ${name}`);
|
|
559
564
|
}
|
|
560
565
|
}
|
|
561
|
-
if (hasSwitch('--
|
|
562
|
-
throw new Error('--
|
|
566
|
+
if (hasSwitch('--decision-offload') && (hasSwitch('--probe-workers') || hasSwitch('--deployment') || hasSwitch('--user') || values.get('--scope') !== undefined)) {
|
|
567
|
+
throw new Error('--decision-offload is a separate offline mode; do not combine it with worker, deployment or user-scope checks');
|
|
568
|
+
}
|
|
569
|
+
if (['--qualify', '--cli', '--yes', '--dry-run'].some(flag => hasSwitch(flag) || values.get(flag)) && !hasSwitch('--probe-workers')) {
|
|
570
|
+
throw new Error('--qualify, --cli, --yes and --dry-run require --probe-workers');
|
|
571
|
+
}
|
|
572
|
+
if (hasSwitch('--json') && !hasSwitch('--probe-workers') && !hasSwitch('--deployment') && !hasSwitch('--decision-offload')) {
|
|
573
|
+
throw new Error('--json requires --probe-workers or --deployment or --decision-offload');
|
|
563
574
|
}
|
|
564
575
|
}
|
|
565
576
|
catch (error) {
|
|
@@ -578,11 +589,13 @@ export const doctorHandler = {
|
|
|
578
589
|
bundle: values.get('--bundle'),
|
|
579
590
|
scope: scopeValue,
|
|
580
591
|
}, ctx.frameworkRoot);
|
|
592
|
+
const { rulePinState } = await import('../../rules/pin-state.js');
|
|
593
|
+
const pins = rulePinState(ctx.cwd, ctx.frameworkRoot);
|
|
581
594
|
return {
|
|
582
595
|
exitCode: result.exitCode,
|
|
583
596
|
message: hasSwitch('--json')
|
|
584
|
-
? JSON.stringify(result, null, 2)
|
|
585
|
-
: renderUseDeploymentResult(result),
|
|
597
|
+
? JSON.stringify({ ...result, rule_pins: { rules: pins.rules, warnings: pins.warnings } }, null, 2)
|
|
598
|
+
: renderUseDeploymentResult(result) + '\n' + pins.rules.map(rule => `${rule.name}: pinned kernel (canonical ${rule.canonical_activation})`).concat(pins.warnings).join('\n'),
|
|
586
599
|
rawOutput: hasSwitch('--json'),
|
|
587
600
|
};
|
|
588
601
|
}
|
|
@@ -595,7 +608,7 @@ export const doctorHandler = {
|
|
|
595
608
|
if (userScopeRequested && !hasSwitch('--probe-workers')) {
|
|
596
609
|
return await runUserScopeDoctor(hasSwitch('--verbose') || hasSwitch('-v'));
|
|
597
610
|
}
|
|
598
|
-
const frameworkRoot = await getFrameworkRoot();
|
|
611
|
+
const frameworkRoot = hasSwitch('--decision-offload') ? ctx.frameworkRoot : await getFrameworkRoot();
|
|
599
612
|
const runner = createScriptRunner(frameworkRoot);
|
|
600
613
|
// Run core doctor diagnostics
|
|
601
614
|
const result = await runner.run('tools/cli/doctor.mjs', ctx.args, { cwd: ctx.cwd });
|
|
@@ -603,7 +616,7 @@ export const doctorHandler = {
|
|
|
603
616
|
// printed exactly one JSON document (or a usage error) on its own
|
|
604
617
|
// stdio — every section below writes human-readable text, so returning
|
|
605
618
|
// here keeps stdout to the JSON document alone.
|
|
606
|
-
if (hasSwitch('--json') || result.exitCode === 2)
|
|
619
|
+
if (hasSwitch('--decision-offload') || hasSwitch('--json') || hasSwitch('--qualify') || hasSwitch('--cli') || hasSwitch('--dry-run') || result.exitCode === 2)
|
|
607
620
|
return result;
|
|
608
621
|
let agentSkillsFailure = false;
|
|
609
622
|
try {
|
|
@@ -801,6 +814,9 @@ export const contextFirewallHandler = {
|
|
|
801
814
|
description: 'Audit provider context and manage its reviewed baseline',
|
|
802
815
|
category: 'maintenance',
|
|
803
816
|
aliases: ['-context-firewall', '--context-firewall'],
|
|
817
|
+
async help() {
|
|
818
|
+
return { exitCode: 0, message: CONTEXT_FIREWALL_HELP, rawOutput: true };
|
|
819
|
+
},
|
|
804
820
|
async execute(ctx) {
|
|
805
821
|
if (ctx.args.includes('--help') || ctx.args.includes('-h')) {
|
|
806
822
|
return { exitCode: 0, message: CONTEXT_FIREWALL_HELP };
|
|
@@ -55,6 +55,9 @@ function collectFingerprint(versionInfo) {
|
|
|
55
55
|
const loggerInfo = getLoggerInfo();
|
|
56
56
|
const fp = {
|
|
57
57
|
version: versionInfo.version,
|
|
58
|
+
launcherVersion: versionInfo.launcherVersion ?? versionInfo.version,
|
|
59
|
+
runtimeVersion: versionInfo.runtimeVersion ?? versionInfo.version,
|
|
60
|
+
runtimePath: versionInfo.runtimePath ?? versionInfo.packageRoot,
|
|
58
61
|
channel: versionInfo.devMode ? 'dev' : versionInfo.channel,
|
|
59
62
|
packageRoot: versionInfo.packageRoot,
|
|
60
63
|
node: process.version,
|
|
@@ -132,6 +135,11 @@ async function displayVersion(opts) {
|
|
|
132
135
|
}
|
|
133
136
|
ui.blank();
|
|
134
137
|
console.log(` ${ui.brandMark()} ${ui.bold('aiwg')} ${ui.bold(fp.version)} ${ui.channelLabel(fp.channel)}`);
|
|
138
|
+
if (info.devMode || fp.launcherVersion !== fp.runtimeVersion) {
|
|
139
|
+
ui.dim(` launcher: ${fp.launcherVersion}`);
|
|
140
|
+
ui.dim(` runtime: ${fp.runtimeVersion}`);
|
|
141
|
+
ui.dim(` runtime path: ${fp.runtimePath}`);
|
|
142
|
+
}
|
|
135
143
|
if (!opts.verbose) {
|
|
136
144
|
if (fp.git)
|
|
137
145
|
ui.dim(` git: ${fp.git.sha} (${fp.git.branch})`);
|
package/dist/src/cli/router.js
CHANGED
|
@@ -75,6 +75,15 @@ export function getHookRegistry() {
|
|
|
75
75
|
* ```
|
|
76
76
|
*/
|
|
77
77
|
export async function run(args, options = {}) {
|
|
78
|
+
// Scheduled index maintenance has no hooks, telemetry, or deployment side effects.
|
|
79
|
+
if (args[0] === 'index' && args[1] === 'refresh'
|
|
80
|
+
&& !args.includes('--help') && !args.includes('-h')) {
|
|
81
|
+
const { runIndexRefresh } = await import('../artifacts/index-refresh.js');
|
|
82
|
+
const report = await runIndexRefresh(args.slice(2), options.cwd ?? process.cwd(), options.signal);
|
|
83
|
+
if (report.totals.errors)
|
|
84
|
+
process.exit(1);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
78
87
|
const started = process.hrtime.bigint();
|
|
79
88
|
const registry = await initRouter();
|
|
80
89
|
// Normalize `help <command>` into the same non-executing help path.
|
|
@@ -320,7 +329,12 @@ async function logCommandInvocation(input) {
|
|
|
320
329
|
* @returns Handler context
|
|
321
330
|
*/
|
|
322
331
|
async function buildContext(args, rawArgs, options) {
|
|
323
|
-
const
|
|
332
|
+
const readOnlyRuleInventory = rawArgs[0] === 'rules' && ['inventory', 'list'].includes(args[0]);
|
|
333
|
+
const readOnlyOffloadReport = (['doctor', '-doctor', '--doctor'].includes(rawArgs[0] ?? '') && args.includes('--decision-offload'))
|
|
334
|
+
|| (rawArgs[0] === 'steward' && args[0] === 'decision-offload');
|
|
335
|
+
const frameworkRoot = await getFrameworkRoot({
|
|
336
|
+
createIfMissing: !args.includes('--dry-run') && !readOnlyRuleInventory && !readOnlyOffloadReport,
|
|
337
|
+
});
|
|
324
338
|
const ctx = {
|
|
325
339
|
args,
|
|
326
340
|
rawArgs,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { access, readFile, readdir, stat } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { loadPrebuiltDeploymentIndex } from '../../artifacts/prebuilt-deployment.js';
|
|
3
4
|
import { loadGraphIndexFile } from '../../artifacts/index-reader.js';
|
|
4
5
|
import { readAiwgConfig } from '../../config/aiwg-config.js';
|
|
5
6
|
import { readUserRegistry } from '../../config/user-registry.js';
|
|
@@ -102,7 +103,7 @@ export function normalizeFrameworkDiscoveryInventory(stats) {
|
|
|
102
103
|
};
|
|
103
104
|
}
|
|
104
105
|
function frameworkDiscoveryInventory(frameworkRoot) {
|
|
105
|
-
const stats = loadGraphIndexFile(frameworkRoot, 'stats.json', 'framework');
|
|
106
|
+
const stats = loadPrebuiltDeploymentIndex(frameworkRoot, { strictRoot: true })?.stats ?? loadGraphIndexFile(frameworkRoot, 'stats.json', 'framework');
|
|
106
107
|
return stats ? normalizeFrameworkDiscoveryInventory(stats) : null;
|
|
107
108
|
}
|
|
108
109
|
const BUNDLE_INDEX_TOKENS = {
|
|
@@ -510,9 +511,11 @@ export async function verifyProviderDeployment(options) {
|
|
|
510
511
|
const frameworkBundles = options.requestedBundles.filter((bundle) => !projectLocalBundles.includes(bundle));
|
|
511
512
|
const indexesToVerify = [];
|
|
512
513
|
if (frameworkBundles.length > 0) {
|
|
514
|
+
const prebuilt = loadPrebuiltDeploymentIndex(options.frameworkRoot, { strictRoot: true });
|
|
513
515
|
indexesToVerify.push({
|
|
514
|
-
index: loadGraphIndexFile(options.frameworkRoot, 'metadata.json', 'framework'),
|
|
515
|
-
stats: loadGraphIndexFile(options.frameworkRoot, 'stats.json', 'framework'),
|
|
516
|
+
index: prebuilt?.index ?? loadGraphIndexFile(options.frameworkRoot, 'metadata.json', 'framework'),
|
|
517
|
+
stats: prebuilt?.stats ?? loadGraphIndexFile(options.frameworkRoot, 'stats.json', 'framework'),
|
|
518
|
+
prebuiltValidated: Boolean(prebuilt),
|
|
516
519
|
graph: 'framework',
|
|
517
520
|
bundles: frameworkBundles,
|
|
518
521
|
});
|
|
@@ -521,11 +524,12 @@ export async function verifyProviderDeployment(options) {
|
|
|
521
524
|
const graph = options.scope === 'user' ? 'user' : 'project';
|
|
522
525
|
indexesToVerify.push({
|
|
523
526
|
index: loadGraphIndexFile(options.projectRoot, 'metadata.json', graph),
|
|
527
|
+
stats: loadGraphIndexFile(options.projectRoot, 'stats.json', graph),
|
|
524
528
|
graph,
|
|
525
529
|
bundles: projectLocalBundles,
|
|
526
530
|
});
|
|
527
531
|
}
|
|
528
|
-
for (const { index, stats, graph, bundles } of indexesToVerify) {
|
|
532
|
+
for (const { index, stats, graph, bundles, prebuiltValidated } of indexesToVerify) {
|
|
529
533
|
if (!index || !index.entries || Object.keys(index.entries).length === 0) {
|
|
530
534
|
findings.push(finding(normalized, `index-unreadable:${graph}`, 'blocking', `The ${graph} capability index is missing, unreadable, or empty.`, `Run aiwg index build --graph ${graph}, then re-run the same aiwg use command.`));
|
|
531
535
|
continue;
|
|
@@ -550,11 +554,14 @@ export async function verifyProviderDeployment(options) {
|
|
|
550
554
|
if (!surfacePresent) {
|
|
551
555
|
findings.push(finding(normalized, `index-surface-missing:${graph}`, 'blocking', `The ${graph} capability index does not contain the requested bundle surface.`, `Rebuild the ${graph} index from the selected AIWG source and re-run aiwg use.`, { requestedBundles: bundles, entryCount: entryPaths.length }));
|
|
552
556
|
}
|
|
553
|
-
if (options.invocationStartedAt) {
|
|
554
|
-
|
|
557
|
+
if (options.invocationStartedAt && !prebuiltValidated) {
|
|
558
|
+
// No-op builds preserve metadata bytes and record the refresh in stats.
|
|
559
|
+
const refreshedAt = Math.max(...[index.builtAt, stats?.builtAt]
|
|
560
|
+
.map((timestamp) => Date.parse(timestamp ?? ''))
|
|
561
|
+
.filter(Number.isFinite));
|
|
555
562
|
const startedAt = Date.parse(options.invocationStartedAt);
|
|
556
|
-
if (!Number.isFinite(
|
|
557
|
-
findings.push(finding(normalized, `index-stale:${graph}`, 'blocking', `The ${graph} capability index was not refreshed during this deployment.`, `Re-run aiwg use after correcting the ${graph} index build failure.`, { graph, builtAt: index.builtAt, invocationStartedAt: options.invocationStartedAt }));
|
|
563
|
+
if (!Number.isFinite(refreshedAt) || refreshedAt + 2_000 < startedAt) {
|
|
564
|
+
findings.push(finding(normalized, `index-stale:${graph}`, 'blocking', `The ${graph} capability index was not refreshed during this deployment.`, `Re-run aiwg use after correcting the ${graph} index build failure.`, { graph, builtAt: index.builtAt, statsBuiltAt: stats?.builtAt, invocationStartedAt: options.invocationStartedAt }));
|
|
558
565
|
}
|
|
559
566
|
}
|
|
560
567
|
}
|
|
@@ -569,7 +576,7 @@ export async function verifyProviderDeployment(options) {
|
|
|
569
576
|
}
|
|
570
577
|
const requiredContext = [
|
|
571
578
|
path.join(options.projectRoot, 'WORKSPACE.md'),
|
|
572
|
-
path.join(options.projectRoot, 'AIWG.md'),
|
|
579
|
+
...(definition?.paths.contextFiles.aiwgMd === false ? [] : [path.join(options.projectRoot, 'AIWG.md')]),
|
|
573
580
|
path.join(options.projectRoot, '.aiwg', 'AIWG.md'),
|
|
574
581
|
];
|
|
575
582
|
for (const contextPath of requiredContext) {
|
|
@@ -773,9 +780,12 @@ export async function buildDeploymentStatusProbe(projectRoot, frameworkRoot = pr
|
|
|
773
780
|
const notConfigured = result.requestedBundles.length === 0
|
|
774
781
|
&& result.findings.length > 0
|
|
775
782
|
&& result.findings.every((item) => item.id === 'deployment-not-configured');
|
|
783
|
+
const { rulePinState } = await import('../../rules/pin-state.js');
|
|
784
|
+
const rulePins = rulePinState(projectRoot, frameworkRoot);
|
|
776
785
|
const engaged = result.outcome === 'ready' || result.outcome === 'ready-restart-required' || result.outcome === 'degraded';
|
|
777
786
|
return {
|
|
778
787
|
schema: 'aiwg.status.probe.v1',
|
|
788
|
+
rule_pins: { rules: rulePins.rules, warnings: rulePins.warnings },
|
|
779
789
|
generated_at: result.generatedAt,
|
|
780
790
|
project_root: result.projectRoot,
|
|
781
791
|
engaged,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { pathToFileURL } from 'node:url';
|
|
3
|
+
export async function prepareProviderDeployments(frameworkRoot, jobs) {
|
|
4
|
+
const deployerUrl = pathToFileURL(path.join(frameworkRoot, 'tools', 'agents', 'deploy-agents.mjs')).href;
|
|
5
|
+
const deployer = await import(deployerUrl);
|
|
6
|
+
// Commands-to-skills migration is interactive and changes provider inputs.
|
|
7
|
+
// Run it serially in the coordinator before the shared snapshot is created;
|
|
8
|
+
// finalization then only applies post-deploy mirrors/pruning/config writes.
|
|
9
|
+
for (const job of jobs)
|
|
10
|
+
await deployer.main(job.args, { preflightOnly: true });
|
|
11
|
+
const url = pathToFileURL(path.join(frameworkRoot, 'tools', 'agents', 'providers', 'provider-prepare-coordinator.mjs')).href;
|
|
12
|
+
const coordinator = await import(url);
|
|
13
|
+
const sourceIndex = jobs[0]?.args.findIndex(arg => arg === '--source') ?? -1;
|
|
14
|
+
const srcRoot = sourceIndex >= 0 && jobs[0]?.args[sourceIndex + 1]
|
|
15
|
+
? path.resolve(jobs[0].args[sourceIndex + 1])
|
|
16
|
+
: frameworkRoot;
|
|
17
|
+
return coordinator.prepareProviderDeployments({ srcRoot, jobs, maxConcurrency: 2 });
|
|
18
|
+
}
|
|
19
|
+
export async function finalizePreparedProvider(frameworkRoot, args) {
|
|
20
|
+
const url = pathToFileURL(path.join(frameworkRoot, 'tools', 'agents', 'deploy-agents.mjs')).href;
|
|
21
|
+
const deployer = await import(url);
|
|
22
|
+
await deployer.main(args, { finalizeOnly: true });
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=provider-prepare-coordinator.js.map
|
|
@@ -3,7 +3,7 @@ import { createReadStream, existsSync } from 'fs';
|
|
|
3
3
|
import { createHash } from 'crypto';
|
|
4
4
|
import { createInterface } from 'readline';
|
|
5
5
|
import path from 'path';
|
|
6
|
-
import
|
|
6
|
+
import { getUserStateDir } from '../config/user-state.js';
|
|
7
7
|
import { readAiwgConfig } from '../config/aiwg-config.js';
|
|
8
8
|
import { PROJECT_AIWG_LOCATION_FILE, projectAiwgPath, projectControlPath, } from '../config/project-artifacts.js';
|
|
9
9
|
const DEFAULT_MAX_BYTES = 1_048_576;
|
|
@@ -487,8 +487,7 @@ function resolveUsagePath(scope, projectRoot, env) {
|
|
|
487
487
|
return null;
|
|
488
488
|
return projectAiwgPath(projectRoot, 'telemetry', 'skill-usage.jsonl');
|
|
489
489
|
}
|
|
490
|
-
|
|
491
|
-
return path.join(stateHome, 'aiwg', 'skill-usage.jsonl');
|
|
490
|
+
return path.join(getUserStateDir(env), 'skill-usage.jsonl');
|
|
492
491
|
}
|
|
493
492
|
async function appendBoundedJsonl(filePath, event, maxBytes) {
|
|
494
493
|
await mkdir(path.dirname(filePath), { recursive: true });
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { validateDecisionConfig } from './decision-config.js';
|
|
1
2
|
/**
|
|
2
3
|
* Project-Level AIWG Config
|
|
3
4
|
*
|
|
@@ -8,8 +9,13 @@
|
|
|
8
9
|
*
|
|
9
10
|
* @implements #621
|
|
10
11
|
*/
|
|
12
|
+
import { validateImportExclusions } from '../sessions/import-exclusions.js';
|
|
11
13
|
import { resolveDispatchHostThresholds } from './dispatch-thresholds-runtime.mjs';
|
|
12
14
|
export { DEFAULT_DISPATCH_HOST_THRESHOLDS, resolveDispatchHostThresholds } from './dispatch-thresholds-runtime.mjs';
|
|
15
|
+
import { validateIndexGraphOverrides } from './index-graph-overrides.js';
|
|
16
|
+
import { DEFAULT_RULES_INLINE_BUDGET_TOKENS } from '../rules/inline-budget.mjs';
|
|
17
|
+
export { validateRulesConfig, resolveRulesInlineBudget } from '../rules/inline-budget.mjs';
|
|
18
|
+
import { loadRulePinConfigs, installedRuleCapabilities, validateRulePinCapabilities } from '../rules/pins.mjs';
|
|
13
19
|
import Ajv from 'ajv';
|
|
14
20
|
import addFormats from 'ajv-formats';
|
|
15
21
|
import { readFileSync, existsSync } from 'node:fs';
|
|
@@ -297,7 +303,7 @@ export function resolveWorkerGroupInstallation(parallelism, installationId, regi
|
|
|
297
303
|
* `options.aiwgHome`, when provided, is passed through to `ConfigLoader`
|
|
298
304
|
* unchanged — it never reads or writes `process.env.AIWG_HOME`. Omitting it
|
|
299
305
|
* keeps `ConfigLoader`'s own default precedence (`AIWG_HOME` env, else the
|
|
300
|
-
*
|
|
306
|
+
* running package root).
|
|
301
307
|
*/
|
|
302
308
|
export async function validateWorkerConfigCandidate(projectDir, config, options) {
|
|
303
309
|
const errors = validateParallelismConfig(config.parallelism);
|
|
@@ -456,37 +462,7 @@ export function validateIndexConfig(index) {
|
|
|
456
462
|
errors.push(`index.graphBackend: must be one of ${GRAPH_BACKENDS.join(' | ')}`);
|
|
457
463
|
}
|
|
458
464
|
const isStringArray = (v) => Array.isArray(v) && v.every((x) => typeof x === 'string');
|
|
459
|
-
|
|
460
|
-
if (graphOverrides !== undefined) {
|
|
461
|
-
if (typeof graphOverrides !== 'object' || graphOverrides === null || Array.isArray(graphOverrides)) {
|
|
462
|
-
errors.push('index.graphOverrides: must be an object mapping supported built-in graph names to overrides');
|
|
463
|
-
}
|
|
464
|
-
else {
|
|
465
|
-
for (const [name, rawOverride] of Object.entries(graphOverrides)) {
|
|
466
|
-
const where = `index.graphOverrides.${name}`;
|
|
467
|
-
if (name !== 'codebase') {
|
|
468
|
-
errors.push(`${where}: unsupported built-in graph override (supported: codebase)`);
|
|
469
|
-
continue;
|
|
470
|
-
}
|
|
471
|
-
if (typeof rawOverride !== 'object' || rawOverride === null || Array.isArray(rawOverride)) {
|
|
472
|
-
errors.push(`${where}: must be an object`);
|
|
473
|
-
continue;
|
|
474
|
-
}
|
|
475
|
-
const override = rawOverride;
|
|
476
|
-
for (const field of Object.keys(override)) {
|
|
477
|
-
if (field !== 'scanDirs' && field !== 'extensions') {
|
|
478
|
-
errors.push(`${where}.${field}: unknown field (supported: scanDirs, extensions)`);
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
if (override.scanDirs !== undefined && (!isStringArray(override.scanDirs) || override.scanDirs.length === 0)) {
|
|
482
|
-
errors.push(`${where}.scanDirs: must be a non-empty array of strings`);
|
|
483
|
-
}
|
|
484
|
-
if (override.extensions !== undefined && (!isStringArray(override.extensions) || override.extensions.length === 0)) {
|
|
485
|
-
errors.push(`${where}.extensions: must be a non-empty array of strings`);
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
}
|
|
465
|
+
errors.push(...validateIndexGraphOverrides(indexObject.graphOverrides));
|
|
490
466
|
const graphs = indexObject.graphs;
|
|
491
467
|
if (graphs === undefined)
|
|
492
468
|
return errors; // index with no graphs is permissible
|
|
@@ -973,6 +949,7 @@ export function emptyConfig(providers = ['claude']) {
|
|
|
973
949
|
threatAssessment: defaultThreatAssessmentConfig(),
|
|
974
950
|
},
|
|
975
951
|
artifact_outputs: defaultArtifactOutputs(),
|
|
952
|
+
rules: { inline_budget_tokens: DEFAULT_RULES_INLINE_BUDGET_TOKENS },
|
|
976
953
|
delivery: {
|
|
977
954
|
mode: 'pr-required',
|
|
978
955
|
default_branch: 'main',
|
|
@@ -1058,6 +1035,30 @@ export async function readAiwgConfigCandidate(projectDir) {
|
|
|
1058
1035
|
parsed.installed = {};
|
|
1059
1036
|
if (!parsed.scripts)
|
|
1060
1037
|
parsed.scripts = {};
|
|
1038
|
+
if (parsed.sessions !== undefined && (parsed.sessions === null || typeof parsed.sessions !== 'object' || Array.isArray(parsed.sessions)
|
|
1039
|
+
|| (parsed.sessions.codex_roots !== undefined && (!Array.isArray(parsed.sessions.codex_roots)
|
|
1040
|
+
|| !parsed.sessions.codex_roots.every((root) => typeof root === 'string' && root.trim().length > 0))))) {
|
|
1041
|
+
throw new Error('sessions.codex_roots must be an array of non-empty paths');
|
|
1042
|
+
}
|
|
1043
|
+
if (parsed.sessions?.discovery_manifests !== undefined && (!Array.isArray(parsed.sessions.discovery_manifests)
|
|
1044
|
+
|| !parsed.sessions.discovery_manifests.every((file) => typeof file === 'string' && file.trim().length > 0))) {
|
|
1045
|
+
throw new Error('sessions.discovery_manifests must be an array of non-empty paths');
|
|
1046
|
+
}
|
|
1047
|
+
if (parsed.sessions?.import !== undefined) {
|
|
1048
|
+
const limits = parsed.sessions.import;
|
|
1049
|
+
if (limits === null || typeof limits !== 'object' || Array.isArray(limits)) {
|
|
1050
|
+
throw new Error('sessions.import must be an object');
|
|
1051
|
+
}
|
|
1052
|
+
if (limits.exclude !== undefined)
|
|
1053
|
+
validateImportExclusions(limits.exclude);
|
|
1054
|
+
for (const key of ['max_record_bytes', 'max_source_bytes']) {
|
|
1055
|
+
const value = limits[key];
|
|
1056
|
+
const minimum = key === 'max_source_bytes' ? 0 : 1;
|
|
1057
|
+
if (value !== undefined && (typeof value !== 'number' || !Number.isSafeInteger(value) || value < minimum)) {
|
|
1058
|
+
throw new Error(`sessions.import.${key} must be a ${minimum ? 'positive' : 'non-negative'} integer`);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1061
1062
|
const externalLinkErrors = validateExternalLinks(parsed.externalLinks);
|
|
1062
1063
|
if (externalLinkErrors.length > 0) {
|
|
1063
1064
|
throw new Error(`Invalid .aiwg/aiwg.config:\n${externalLinkErrors.join('\n')}`);
|
|
@@ -1076,6 +1077,8 @@ export async function readAiwgConfigCandidate(projectDir) {
|
|
|
1076
1077
|
...validateThreatAssessmentConfig(parsed.security?.threatAssessment),
|
|
1077
1078
|
...validateProjectConfig(parsed.project),
|
|
1078
1079
|
...validateParallelismConfig(parsed.parallelism),
|
|
1080
|
+
...validateDecisionConfig(parsed.decision),
|
|
1081
|
+
...validateRulePinCapabilities(parsed.rules, installedRuleCapabilities(loadRulePinConfigs(projectDir, parsed)), loadRulePinConfigs(projectDir, parsed).user.rules?.pin_groups),
|
|
1079
1082
|
];
|
|
1080
1083
|
if (threatAssessmentErrors.length > 0) {
|
|
1081
1084
|
throw new Error(`Invalid .aiwg/aiwg.config:\n${threatAssessmentErrors.join('\n')}`);
|
|
@@ -1119,10 +1122,46 @@ export async function readAiwgConfig(projectDir, options) {
|
|
|
1119
1122
|
* sync so split-root health remains deterministic.
|
|
1120
1123
|
*/
|
|
1121
1124
|
export async function writeAiwgConfig(projectDir, config) {
|
|
1125
|
+
// Uninstall is an explicit capability change. Retire project selectors for
|
|
1126
|
+
// removed capabilities before strict validation, and tell the operator.
|
|
1127
|
+
const previousFile = getConfigPath(projectDir);
|
|
1128
|
+
let previous = null;
|
|
1129
|
+
if (existsSync(previousFile)) {
|
|
1130
|
+
try {
|
|
1131
|
+
previous = JSON.parse(readFileSync(previousFile, 'utf8'));
|
|
1132
|
+
}
|
|
1133
|
+
catch (error) {
|
|
1134
|
+
if (!(error instanceof SyntaxError))
|
|
1135
|
+
throw error;
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
const pinConfigs = loadRulePinConfigs(projectDir, config);
|
|
1139
|
+
const currentCapabilities = new Set(installedRuleCapabilities(pinConfigs));
|
|
1140
|
+
const removed = previous ? installedRuleCapabilities({ ...pinConfigs, project: previous }).filter(id => !currentCapabilities.has(id)) : [];
|
|
1141
|
+
if (removed.length && (config.rules?.always_load?.length || Object.keys(config.rules?.pin_groups ?? {}).length)) {
|
|
1142
|
+
const removedSet = new Set(removed);
|
|
1143
|
+
const rules = config.rules;
|
|
1144
|
+
const userGroups = loadRulePinConfigs(projectDir, config).user.rules?.pin_groups ?? {};
|
|
1145
|
+
const pinGroups = Object.fromEntries(Object.entries(rules.pin_groups ?? {})
|
|
1146
|
+
.map(([name, ids]) => [name, ids.filter(id => !removedSet.has(id))]).filter(([, ids]) => ids.length));
|
|
1147
|
+
const selectors = (rules.always_load ?? []).flatMap(selector => {
|
|
1148
|
+
if (selector.group && ((!Object.hasOwn(pinGroups, selector.group) && Object.hasOwn(rules.pin_groups ?? {}, selector.group))
|
|
1149
|
+
|| (!Object.hasOwn(pinGroups, selector.group) && userGroups[selector.group]?.some(id => removedSet.has(id)))))
|
|
1150
|
+
return [];
|
|
1151
|
+
if (!selector.capabilities)
|
|
1152
|
+
return [selector];
|
|
1153
|
+
const capabilities = selector.capabilities.filter(id => !removedSet.has(id));
|
|
1154
|
+
return capabilities.length ? [{ ...selector, capabilities }] : [];
|
|
1155
|
+
});
|
|
1156
|
+
config = { ...config, rules: { ...config.rules, pin_groups: pinGroups, always_load: selectors } };
|
|
1157
|
+
console.warn(`Uninstalled ${removed.join(', ')}: their rules leave the effective pinned set; retired project capability selectors/groups. User pins remain visible as inactive warnings.`);
|
|
1158
|
+
}
|
|
1122
1159
|
const threatAssessmentErrors = [
|
|
1123
1160
|
...validateThreatAssessmentConfig(config.security?.threatAssessment),
|
|
1124
1161
|
...validateProjectConfig(config.project),
|
|
1125
1162
|
...validateParallelismConfig(config.parallelism),
|
|
1163
|
+
...validateDecisionConfig(config.decision),
|
|
1164
|
+
...validateRulePinCapabilities(config.rules, installedRuleCapabilities(loadRulePinConfigs(projectDir, config)), loadRulePinConfigs(projectDir, config).user.rules?.pin_groups),
|
|
1126
1165
|
];
|
|
1127
1166
|
if (threatAssessmentErrors.length > 0) {
|
|
1128
1167
|
throw new Error(`Invalid .aiwg/aiwg.config:\n${threatAssessmentErrors.join('\n')}`);
|
|
@@ -1379,13 +1418,24 @@ export function validateWorkerGroupReferences(parallelism, selection, qualifiedR
|
|
|
1379
1418
|
errors.push(`OG_CONFIG_REFERENCE /defaults/workerSelection/roleGuidance/${role}/groups: Unknown group ${group}`);
|
|
1380
1419
|
}
|
|
1381
1420
|
if (qualifiedRoles)
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
if (!group.members.some(m => m.enabled !== false && parallelism.clis?.[m.cli]?.enabled !== false && m.roles.includes(role) && qualifiedRoles[`${id}/${m.id}`]?.includes(role)))
|
|
1385
|
-
errors.push(`OG_ADAPTER_UNQUALIFIED /parallelism/groups/${id}/requiredRoles: No qualified route covers ${role}`);
|
|
1386
|
-
}
|
|
1421
|
+
errors.push(...validateWorkerGroupQualifications(parallelism, qualifiedRoles)
|
|
1422
|
+
.map(diagnostic => `${diagnostic.code} ${diagnostic.pointer}: ${diagnostic.message}`));
|
|
1387
1423
|
return errors;
|
|
1388
1424
|
}
|
|
1425
|
+
/** Structured qualification gaps retain role scope for readiness projections. */
|
|
1426
|
+
export function validateWorkerGroupQualifications(parallelism, qualifiedRoles) {
|
|
1427
|
+
const diagnostics = [];
|
|
1428
|
+
for (const [id, group] of Object.entries(parallelism.groups ?? {})) {
|
|
1429
|
+
for (const role of group.requiredRoles ?? []) {
|
|
1430
|
+
if (!group.members.some(m => m.enabled !== false && parallelism.clis?.[m.cli]?.enabled !== false
|
|
1431
|
+
&& m.roles.includes(role) && qualifiedRoles[`${id}/${m.id}`]?.includes(role))) {
|
|
1432
|
+
diagnostics.push({ code: 'OG_ADAPTER_UNQUALIFIED', pointer: `/parallelism/groups/${id}/requiredRoles`,
|
|
1433
|
+
message: `No qualified route covers ${role}`, roles: [role] });
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
return diagnostics;
|
|
1438
|
+
}
|
|
1389
1439
|
/** A configured hard ceiling is eligible only where a qualified control enforces it. */
|
|
1390
1440
|
export function validateWorkerBudgetEnforcement(budgets, enforceable) {
|
|
1391
1441
|
if (budgets?.enforcement !== 'hard')
|