@aiwg/cli 2026.8.8 → 2026.8.10

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.
Files changed (68) hide show
  1. package/README.md +23 -8
  2. package/THIRD_PARTY_NOTICES.md +35 -0
  3. package/agentic/code/providers/capability-matrix.yaml +3 -3
  4. package/bin/aiwg.mjs +125 -0
  5. package/dist/src/artifacts/backends/graphology-backend.js +4 -3
  6. package/dist/src/artifacts/backends/sqlite-backend.js +4 -5
  7. package/dist/src/artifacts/cli.js +2 -2
  8. package/dist/src/artifacts/corpus-tools/cli.js +27 -0
  9. package/dist/src/artifacts/corpus-tools/profile-embed.js +3 -2
  10. package/dist/src/artifacts/corpus-tools/retrieval-lab.js +356 -0
  11. package/dist/src/artifacts/discover-facets.js +2 -2
  12. package/dist/src/artifacts/embedding-index.js +9 -8
  13. package/dist/src/artifacts/graph-backend.js +2 -2
  14. package/dist/src/artifacts/query-engine.js +21 -7
  15. package/dist/src/artifacts/repair.js +47 -0
  16. package/dist/src/artifacts/types.js +3 -3
  17. package/dist/src/cli/command-log.js +2 -2
  18. package/dist/src/cli/handlers/artifacts.js +50 -1
  19. package/dist/src/cli/handlers/cost-report.js +71 -0
  20. package/dist/src/cli/handlers/evidence.js +78 -0
  21. package/dist/src/cli/handlers/help.js +9 -0
  22. package/dist/src/cli/handlers/index.js +8 -3
  23. package/dist/src/cli/handlers/local-executor.js +4 -3
  24. package/dist/src/cli/handlers/refresh.js +20 -8
  25. package/dist/src/cli/handlers/regenerate.js +3 -3
  26. package/dist/src/cli/handlers/serve.js +15 -36
  27. package/dist/src/cli/handlers/setup-manifest.js +8 -1
  28. package/dist/src/cli/handlers/use.js +256 -70
  29. package/dist/src/cli/handlers/utilities.js +149 -0
  30. package/dist/src/cli/handlers/workspace.js +10 -0
  31. package/dist/src/cli/help-generator.js +2 -1
  32. package/dist/src/cli/router.js +4 -1
  33. package/dist/src/cli/services/deployment-verification.js +596 -0
  34. package/dist/src/cli/skill-usage.js +2 -2
  35. package/dist/src/cli/workflow-orchestrator.js +1 -1
  36. package/dist/src/cli/workspace-signals.js +2 -2
  37. package/dist/src/config/aiwg-config.js +54 -27
  38. package/dist/src/config/cli.js +3 -3
  39. package/dist/src/config/project-artifacts-health.js +2 -0
  40. package/dist/src/config/project-artifacts-health.mjs +123 -0
  41. package/dist/src/config/project-artifacts-runtime.mjs +16 -0
  42. package/dist/src/config/project-artifacts.js +2 -1
  43. package/dist/src/cost/fleet-report.js +329 -0
  44. package/dist/src/evidence/bundle.js +256 -0
  45. package/dist/src/extensions/commands/definitions.js +77 -25
  46. package/dist/src/extensions/deployment-registration.js +6 -4
  47. package/dist/src/features/catalog.js +26 -0
  48. package/dist/src/features/cli.js +1 -3
  49. package/dist/src/features/runtime.js +17 -1
  50. package/dist/src/issues/cli.js +91 -7
  51. package/dist/src/mcp/server.mjs +1 -1
  52. package/dist/src/ops/registry.js +2 -2
  53. package/dist/src/policy/authorization.js +2 -2
  54. package/dist/src/providers/capability-matrix.yaml +3 -3
  55. package/dist/src/providers/provider-definitions.js +7 -5
  56. package/dist/src/providers/provider-definitions.mjs +1 -1
  57. package/dist/src/serve/pty-bridge.js +2 -8
  58. package/dist/src/serve/screen-reader.js +3 -6
  59. package/dist/src/smiths/context-pipeline/aiwg-md.js +2 -2
  60. package/dist/src/smiths/context-pipeline/finalization.js +18 -5
  61. package/dist/src/smiths/context-pipeline/generator.js +2 -2
  62. package/dist/src/smiths/context-pipeline/workspace-context.js +16 -17
  63. package/package.json +2 -1
  64. package/tools/agents/deploy-agents.mjs +10 -11
  65. package/tools/agents/providers/base.mjs +47 -5
  66. package/tools/agents/providers/openclaw.mjs +5 -2
  67. package/tools/agents/providers/windsurf.mjs +13 -24
  68. package/tools/skills/deploy-skills-codex.mjs +21 -5
@@ -1,12 +1,17 @@
1
1
  import { getProjectDir } from '../../config/aiwg-config.js';
2
2
  import { moveProjectArtifacts } from '../../artifacts/move.js';
3
+ import { repairProjectArtifacts } from '../../artifacts/repair.js';
4
+ import { resolveProjectAiwgDir } from '../../config/project-artifacts.js';
3
5
  function usage() {
4
6
  return [
5
7
  'aiwg artifacts — Manage the project AIWG artifact root',
6
8
  '',
7
9
  'Usage:',
10
+ ' aiwg artifacts path [--json]',
8
11
  ' aiwg artifacts move --to <path> [--from <path>] [--dry-run] [--no-reindex] [--no-sync]',
9
12
  ' aiwg artifacts attach --to <existing-path> [--dry-run] [--no-reindex] [--no-sync]',
13
+ ' aiwg artifacts repair --dry-run',
14
+ ' aiwg artifacts repair --apply',
10
15
  '',
11
16
  'Notes:',
12
17
  ' move relocates a local artifact root; attach adopts an existing populated root.',
@@ -32,9 +37,53 @@ export const artifactsHandler = {
32
37
  if (action === 'help' || ctx.args.includes('--help') || ctx.args.includes('-h')) {
33
38
  return { exitCode: 0, message: usage() };
34
39
  }
35
- if (action !== 'move' && action !== 'attach') {
40
+ if (action !== 'path' && action !== 'move' && action !== 'attach' && action !== 'repair') {
36
41
  return { exitCode: 1, message: `Unknown artifacts action: ${action}\n\n${usage()}` };
37
42
  }
43
+ if (action === 'path') {
44
+ const projectDir = getProjectDir(ctx, ctx.args);
45
+ const artifactRoot = resolveProjectAiwgDir(projectDir);
46
+ if (ctx.args.includes('--json')) {
47
+ return {
48
+ exitCode: 0,
49
+ message: JSON.stringify({
50
+ schema: 'aiwg.artifacts.path.v1',
51
+ project_root: projectDir,
52
+ artifact_root: artifactRoot,
53
+ }, null, 2),
54
+ rawOutput: true,
55
+ };
56
+ }
57
+ return { exitCode: 0, message: artifactRoot, rawOutput: true };
58
+ }
59
+ if (action === 'repair') {
60
+ try {
61
+ const applied = ctx.args.includes('--apply');
62
+ const result = await repairProjectArtifacts({
63
+ projectDir: getProjectDir(ctx, ctx.args),
64
+ apply: applied,
65
+ });
66
+ return {
67
+ exitCode: 0,
68
+ message: [
69
+ `${applied ? 'Repaired' : 'Artifact repair dry run for'} ${result.before.classification}`,
70
+ ` Local control plane: ${result.before.local_control_root}`,
71
+ ` External corpus: ${result.before.artifact_root}`,
72
+ ` Copy locally: ${result.copied.length ? result.copied.join(', ') : 'none'}`,
73
+ ` Remove local identical corpus copies: ${result.removed.length ? result.removed.join(', ') : 'none'}`,
74
+ ` Result: ${result.after.classification}`,
75
+ applied ? '' : 'No files changed. Re-run with --apply after reviewing this plan.',
76
+ ].filter(Boolean).join('\n'),
77
+ };
78
+ }
79
+ catch (error) {
80
+ return {
81
+ exitCode: 1,
82
+ error: error instanceof Error ? error : new Error(String(error)),
83
+ message: `Artifact repair failed: ${error instanceof Error ? error.message : String(error)}`,
84
+ };
85
+ }
86
+ }
38
87
  const to = valueAfter(ctx.args, '--to');
39
88
  if (!to) {
40
89
  return { exitCode: 1, message: `Error: --to <path> is required.\n\n${usage()}` };
@@ -0,0 +1,71 @@
1
+ /** OpenRouter fleet cost-report CLI handler. @issue #1187 */
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { FleetConfigMissingError, formatFleetSpendReport, generateFleetSpendReport, } from '../../cost/fleet-report.js';
5
+ function option(args, name) {
6
+ const index = args.indexOf(name);
7
+ const value = index >= 0 ? args[index + 1] : undefined;
8
+ return value && !value.startsWith('-') ? value : undefined;
9
+ }
10
+ function usage() {
11
+ return [
12
+ 'Usage: aiwg cost-report (--fleet | --key <key_ref>) [--source openrouter] [--config <fleet.yaml>] [--json]',
13
+ '',
14
+ 'Reads bot-to-key references from ~/.config/aiwg/fleet.yaml and credentials from',
15
+ '~/.config/aiwg/keys/<key_ref> or AIWG_OPENROUTER_KEY_<KEY_REF>.',
16
+ '',
17
+ 'AIWG observes and correlates spend; OpenRouter enforces all key limits and caps.',
18
+ ].join('\n');
19
+ }
20
+ export const costReportHandler = {
21
+ id: 'cost-report',
22
+ name: 'Cost Report',
23
+ description: 'Observe OpenRouter fleet spend and correlate it with local AIWG activity',
24
+ category: 'utility',
25
+ aliases: [],
26
+ async execute(ctx) {
27
+ if (ctx.args.includes('--help') || ctx.args.includes('-h'))
28
+ return { exitCode: 0, message: usage() };
29
+ const keyRef = option(ctx.args, '--key');
30
+ if (ctx.args.includes('--fleet') && keyRef)
31
+ return { exitCode: 2, message: 'Choose either --fleet or --key, not both.' };
32
+ if (!ctx.args.includes('--fleet') && !keyRef) {
33
+ return { exitCode: 2, message: `Select --fleet or --key <key_ref>.\n\n${usage()}` };
34
+ }
35
+ const source = option(ctx.args, '--source') ?? 'openrouter';
36
+ if (source !== 'openrouter')
37
+ return { exitCode: 2, message: `Unsupported source '${source}'. Only openrouter is supported.` };
38
+ const configuredPath = option(ctx.args, '--config');
39
+ const configPath = configuredPath
40
+ ? path.resolve(ctx.cwd, configuredPath)
41
+ : path.join(os.homedir(), '.config', 'aiwg', 'fleet.yaml');
42
+ try {
43
+ const capValue = option(ctx.args, '--monthly-cap');
44
+ const cap = capValue === undefined ? 0 : Number(capValue);
45
+ if (!Number.isFinite(cap) || cap < 0)
46
+ return { exitCode: 2, message: '--monthly-cap must be a non-negative number.' };
47
+ const report = await generateFleetSpendReport({
48
+ cwd: ctx.cwd,
49
+ configPath,
50
+ signal: ctx.signal,
51
+ ...(keyRef ? {
52
+ fleet: [{
53
+ bot: option(ctx.args, '--bot') ?? keyRef,
54
+ machine: option(ctx.args, '--machine') ?? os.hostname(),
55
+ key_ref: keyRef,
56
+ monthly_cap: cap,
57
+ }],
58
+ } : {}),
59
+ });
60
+ return {
61
+ exitCode: report.bots.some(bot => bot.error) ? 1 : 0,
62
+ message: ctx.args.includes('--json') ? JSON.stringify(report, null, 2) : formatFleetSpendReport(report),
63
+ };
64
+ }
65
+ catch (error) {
66
+ const message = error instanceof FleetConfigMissingError ? error.message : `Fleet cost report failed: ${error instanceof Error ? error.message : String(error)}`;
67
+ return { exitCode: 1, message };
68
+ }
69
+ },
70
+ };
71
+ //# sourceMappingURL=cost-report.js.map
@@ -0,0 +1,78 @@
1
+ /** Evidence bundle export and verification CLI. @issue #2039 */
2
+ import path from 'node:path';
3
+ import { createEvidenceBundle, verifyEvidenceBundle } from '../../evidence/bundle.js';
4
+ function values(args, flag) {
5
+ const result = [];
6
+ for (let index = 0; index < args.length; index++)
7
+ if (args[index] === flag && args[index + 1])
8
+ result.push(args[++index]);
9
+ return result;
10
+ }
11
+ function value(args, flag) { return values(args, flag)[0]; }
12
+ function versions(items) {
13
+ return Object.fromEntries(items.map(item => {
14
+ const index = item.indexOf('=');
15
+ if (index < 1 || index === item.length - 1)
16
+ throw new Error(`version '${item}' must use name=value`);
17
+ return [item.slice(0, index), item.slice(index + 1)];
18
+ }));
19
+ }
20
+ function usage() {
21
+ return [
22
+ 'Usage:',
23
+ ' aiwg evidence export --output <dir> [--activity-export <json>] [--report <file>] [--source <file>]',
24
+ ' [--eval-config <file>] [--provenance <file>] [--model-version name=value] [--tool-version name=value]',
25
+ ' [--check-only --not-run <reason>] [--json]',
26
+ ' aiwg evidence verify <bundle> [--expected-root <sha256>] [--json]',
27
+ ].join('\n');
28
+ }
29
+ export const evidenceHandler = {
30
+ id: 'evidence', name: 'Evidence', description: 'Export and verify portable evaluation evidence bundles', category: 'utility', aliases: [],
31
+ async execute(ctx) {
32
+ const [subcommand] = ctx.args;
33
+ if (!subcommand || subcommand === '--help' || subcommand === '-h')
34
+ return { exitCode: 0, message: usage() };
35
+ try {
36
+ if (subcommand === 'verify') {
37
+ const bundle = ctx.args[1];
38
+ if (!bundle || bundle.startsWith('-'))
39
+ return { exitCode: 2, message: usage() };
40
+ const expectedRoot = value(ctx.args, '--expected-root');
41
+ if (expectedRoot && !/^[0-9a-f]{64}$/i.test(expectedRoot))
42
+ return { exitCode: 2, message: '--expected-root must be a SHA-256 hex digest' };
43
+ const result = await verifyEvidenceBundle(path.resolve(ctx.cwd, bundle), expectedRoot?.toLowerCase());
44
+ return { exitCode: result.valid ? 0 : 1, message: ctx.args.includes('--json') ? JSON.stringify(result, null, 2) : [
45
+ `Evidence bundle: ${result.valid ? 'VALID' : 'INVALID'} (${result.status})`,
46
+ ...result.errors.map(error => `ERROR: ${error}`), ...result.warnings.map(warning => `WARN: ${warning}`),
47
+ ].join('\n') };
48
+ }
49
+ if (subcommand !== 'export')
50
+ return { exitCode: 2, message: usage() };
51
+ const output = value(ctx.args, '--output');
52
+ if (!output)
53
+ return { exitCode: 2, message: '--output is required.\n\n' + usage() };
54
+ const checkOnly = ctx.args.includes('--check-only');
55
+ const notRunReason = value(ctx.args, '--not-run');
56
+ if (checkOnly !== Boolean(notRunReason))
57
+ return { exitCode: 2, message: '--check-only and --not-run <reason> must be used together' };
58
+ const inputs = [
59
+ ...values(ctx.args, '--activity-export').map(file => ({ file: path.resolve(ctx.cwd, file), role: 'activity-export' })),
60
+ ...values(ctx.args, '--report').map(file => ({ file: path.resolve(ctx.cwd, file), role: 'report' })),
61
+ ...values(ctx.args, '--source').map(file => ({ file: path.resolve(ctx.cwd, file), role: 'source' })),
62
+ ...values(ctx.args, '--eval-config').map(file => ({ file: path.resolve(ctx.cwd, file), role: 'eval-config' })),
63
+ ...values(ctx.args, '--provenance').map(file => ({ file: path.resolve(ctx.cwd, file), role: 'provenance' })),
64
+ ];
65
+ const manifest = await createEvidenceBundle({
66
+ output: path.resolve(ctx.cwd, output), inputs,
67
+ modelVersions: versions(values(ctx.args, '--model-version')),
68
+ toolVersions: { aiwg: process.env.npm_package_version ?? 'unknown', node: process.version, ...versions(values(ctx.args, '--tool-version')) },
69
+ checkOnly, notRunReason,
70
+ });
71
+ return { exitCode: 0, message: ctx.args.includes('--json') ? JSON.stringify(manifest, null, 2) : `Evidence bundle ${manifest.status}: ${path.resolve(ctx.cwd, output)}\nVerifier root: ${manifest.verifier.root}` };
72
+ }
73
+ catch (error) {
74
+ return { exitCode: 1, message: `Evidence command failed: ${error instanceof Error ? error.message : String(error)}` };
75
+ }
76
+ },
77
+ };
78
+ //# sourceMappingURL=evidence.js.map
@@ -95,6 +95,15 @@ function displayHelp() {
95
95
  ]);
96
96
  helpGroup('VALIDATION', [
97
97
  ['validate-metadata [path]', 'Validate AIWG component metadata (defaults to agentic/code)'],
98
+ ['context-firewall [scan]', 'Audit provider context, trust, drift, poisoning signals, and budget'],
99
+ ['context-firewall baseline', 'Plan or explicitly write the reviewed context baseline'],
100
+ ]);
101
+ helpGroup('METRICS', [
102
+ ['cost-report --fleet', 'Observe OpenRouter per-bot MTD spend and correlate local activity'],
103
+ ]);
104
+ helpGroup('EVIDENCE', [
105
+ ['evidence export --output <dir>', 'Package portable activity, report, source, eval, and provenance evidence'],
106
+ ['evidence verify <bundle>', 'Verify every member hash and the bundle checkpoint'],
98
107
  ]);
99
108
  helpGroup('SCAFFOLDING', [
100
109
  ['new-bundle <name>', 'Create project-local bundle (--type extension|addon|framework|plugin|provider, --starter skill|rule|agent|minimal, --dry-run)'],
@@ -16,7 +16,7 @@ import { versionHandler } from './version.js';
16
16
  import { authHandler } from './auth.js';
17
17
  import { useHandler } from './use.js';
18
18
  import { statusHandler, wizardHandler, migrateWorkspaceHandler, rollbackWorkspaceHandler, workspaceHandlers, } from './workspace.js';
19
- import { prefillCardsHandler, contributeStartHandler, validateMetadataHandler, doctorHandler, updateHandler, utilityHandlers, } from './utilities.js';
19
+ import { prefillCardsHandler, contributeStartHandler, validateMetadataHandler, doctorHandler, contextFirewallHandler, updateHandler, utilityHandlers, } from './utilities.js';
20
20
  import { skillLintHandler } from './skill-lint.js';
21
21
  import { addAgentHandler, addCommandHandler, addSkillHandler, addBehaviorHandler, addTemplateHandler, scaffoldAddonHandler, scaffoldExtensionHandler, scaffoldFrameworkHandler, scaffoldingHandlers, } from './scaffolding.js';
22
22
  import { behaviorHandler, daemonInitHandler, daemonHandlers, } from './daemon.js';
@@ -59,12 +59,14 @@ import { skillUsageHandler } from './skill-usage.js';
59
59
  import { modelsHandler } from './models.js';
60
60
  import { versionsHandler } from './resource-versions.js';
61
61
  import { jobHandler } from './job.js';
62
+ import { costReportHandler } from './cost-report.js';
63
+ import { evidenceHandler } from './evidence.js';
62
64
  // Re-export individual handlers
63
65
  export {
64
66
  // Maintenance
65
- helpHandler, versionHandler, authHandler, doctorHandler, updateHandler, refreshHandler, regenerateHandler, workspaceContextHandler,
67
+ helpHandler, versionHandler, authHandler, doctorHandler, contextFirewallHandler, updateHandler, refreshHandler, regenerateHandler, workspaceContextHandler,
66
68
  // Framework management
67
- useHandler, listHandler, removeHandler, promoteHandler, installHandler, packagesHandler, marketplaceHandler, initHandler, setupHandler, setupGenerateHandler, setupRunHandler, setupValidateHandler, issueHandler, issueAuditHandler, runHandler, jobHandler,
69
+ useHandler, listHandler, removeHandler, promoteHandler, installHandler, packagesHandler, marketplaceHandler, initHandler, setupHandler, setupGenerateHandler, setupRunHandler, setupValidateHandler, issueHandler, issueAuditHandler, runHandler, jobHandler, costReportHandler, evidenceHandler,
68
70
  // Project
69
71
  newBundleHandler, quickrefHandler, newProjectHandler, sessionHandler, sessionsHandler,
70
72
  // Workspace
@@ -118,6 +120,7 @@ export const allHandlers = [
118
120
  versionHandler,
119
121
  authHandler,
120
122
  doctorHandler,
123
+ contextFirewallHandler,
121
124
  updateHandler,
122
125
  refreshHandler,
123
126
  regenerateHandler,
@@ -143,6 +146,8 @@ export const allHandlers = [
143
146
  issueAuditHandler,
144
147
  runHandler,
145
148
  jobHandler,
149
+ costReportHandler,
150
+ evidenceHandler,
146
151
  // Workspace management
147
152
  ...workspaceHandlers,
148
153
  // Subcommand handlers (MCP, catalog, index, skills)
@@ -19,6 +19,7 @@
19
19
  import path from 'path';
20
20
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
21
21
  import { randomUUID } from 'crypto';
22
+ import { pathToFileURL } from 'node:url';
22
23
  import { projectAiwgPath } from '../../config/project-artifacts.js';
23
24
  const DEFAULT_PORT = 8200;
24
25
  const DEFAULT_BIND = '127.0.0.1';
@@ -131,7 +132,7 @@ export const localExecutorServeHandler = {
131
132
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
132
133
  let DaemonSupervisor;
133
134
  try {
134
- const shimMod = await (new Function('m', 'return import(m)'))(path.join(ctx.frameworkRoot, 'tools', 'ralph-external', 'executor-shim.mjs'));
135
+ const shimMod = await import(pathToFileURL(path.join(ctx.frameworkRoot, 'tools', 'ralph-external', 'executor-shim.mjs')).href);
135
136
  ExecutorShim = shimMod.ExecutorShim;
136
137
  startExecutorServer = shimMod.startExecutorServer;
137
138
  }
@@ -142,7 +143,7 @@ export const localExecutorServeHandler = {
142
143
  };
143
144
  }
144
145
  try {
145
- const dsMod = await (new Function('m', 'return import(m)'))(path.join(ctx.frameworkRoot, 'tools', 'ralph-external', 'daemon-supervisor.mjs'));
146
+ const dsMod = await import(pathToFileURL(path.join(ctx.frameworkRoot, 'tools', 'ralph-external', 'daemon-supervisor.mjs')).href);
146
147
  DaemonSupervisor = dsMod.DaemonSupervisor;
147
148
  }
148
149
  catch (err) {
@@ -155,7 +156,7 @@ export const localExecutorServeHandler = {
155
156
  // requires it. Try loading from ralph-external/orchestrator.mjs.
156
157
  let agentSupervisorInstance = null;
157
158
  try {
158
- const orchMod = await (new Function('m', 'return import(m)'))(path.join(ctx.frameworkRoot, 'tools', 'ralph-external', 'orchestrator.mjs'));
159
+ const orchMod = await import(pathToFileURL(path.join(ctx.frameworkRoot, 'tools', 'ralph-external', 'orchestrator.mjs')).href);
159
160
  const OrchClass = orchMod.Orchestrator ?? orchMod.default;
160
161
  if (OrchClass) {
161
162
  const orch = new OrchClass({ maxConcurrent: opts.maxConcurrency });
@@ -149,7 +149,7 @@ export function collectModelDeployArgs(args) {
149
149
  export const refreshHandler = {
150
150
  id: 'refresh',
151
151
  name: 'Refresh',
152
- description: 'Refresh AIWG to latest version and re-deploy all frameworks',
152
+ description: 'Refresh AIWG to latest version and re-deploy installed frameworks',
153
153
  category: 'maintenance',
154
154
  aliases: ['--refresh', 'sync', '--sync'],
155
155
  async execute(ctx) {
@@ -241,13 +241,24 @@ export const refreshHandler = {
241
241
  if (!quiet)
242
242
  ui.dim(' Skipping package update (--skip-update)');
243
243
  }
244
- // Step 4: Re-deploy frameworks
245
- const frameworks = frameworksArg ? frameworksArg.split(',') : undefined;
244
+ // Step 4: Re-deploy frameworks. Both the default form and --all mean
245
+ // "all installed", never the `aiwg use all` expansion meta-target. This
246
+ // preserves the operator's selected footprint and removal symmetry.
247
+ const refreshConfig = await readAiwgConfig(ctx.cwd);
248
+ const installedFrameworks = Object.keys(refreshConfig?.installed ?? {});
249
+ const requestedFrameworks = frameworksArg
250
+ ? frameworksArg.split(',').map(item => item.trim()).filter(Boolean)
251
+ : [];
252
+ const frameworks = !frameworksArg || requestedFrameworks.includes('all')
253
+ ? installedFrameworks
254
+ : requestedFrameworks;
246
255
  if (!quiet)
247
256
  ui.info(dryRun ? 'Would re-deploy frameworks...' : 'Re-deploying frameworks...');
248
257
  if (!dryRun) {
249
- const deployTarget = frameworks || ['all'];
250
- for (const fw of deployTarget) {
258
+ if (frameworks.length === 0 && !quiet) {
259
+ ui.dim(' No installed frameworks or addons to re-deploy');
260
+ }
261
+ for (const fw of frameworks) {
251
262
  const providerArgs = ['--provider', detectedProvider, ...modelDeployArgs];
252
263
  const useResult = await runner.run('tools/cli/deploy.mjs', [fw, ...providerArgs], { capture: quiet });
253
264
  if (useResult.exitCode === 0) {
@@ -261,9 +272,10 @@ export const refreshHandler = {
261
272
  }
262
273
  }
263
274
  else {
264
- const targets = frameworks || ['all installed frameworks'];
265
275
  if (!quiet) {
266
- for (const fw of targets) {
276
+ if (frameworks.length === 0)
277
+ ui.dim(' No installed frameworks or addons');
278
+ for (const fw of frameworks) {
267
279
  ui.dim(` Would re-deploy: ${fw}`);
268
280
  }
269
281
  }
@@ -419,7 +431,7 @@ export const refreshHandler = {
419
431
  const output = JSON.stringify({
420
432
  status: dryRun ? 'dry-run' : 'refreshed',
421
433
  provider: detectedProvider,
422
- frameworks: frameworks || ['all'],
434
+ frameworks,
423
435
  skipUpdate,
424
436
  channel: channel || undefined,
425
437
  staleAgentRemovals,
@@ -25,7 +25,7 @@ import * as ui from '../ui.js';
25
25
  import { generate as generateContextFiles, discoverDeployedArtifacts, shouldEmitContextFiles, buildNormalizedAiwgMd, writeNormalizedAiwgMd, injectLegacyContext, migrateWorkspaceContext, extractExistingProjectContext, } from '../../smiths/context-pipeline/index.js';
26
26
  import { resolveActiveProvider } from '../provider-resolution.js';
27
27
  import { getProviderContextDiscoveryPathStrings } from '../../providers/provider-definitions.js';
28
- import { projectAiwgPath } from '../../config/project-artifacts.js';
28
+ import { projectControlPath } from '../../config/project-artifacts.js';
29
29
  import { selectRegenerateBranch } from '../regenerate-selector.js';
30
30
  async function handleRegenerate(args, cwd) {
31
31
  if (args.includes('--help') || args.includes('-h')) {
@@ -198,7 +198,7 @@ async function handleRegenerate(args, cwd) {
198
198
  if (legacy) {
199
199
  if (skipWorkspaceMd)
200
200
  console.log(' Note: --no-workspace-md is implicit in legacy mode.');
201
- const normalizedPath = projectAiwgPath(target, 'AIWG.md');
201
+ const normalizedPath = projectControlPath(target, 'AIWG.md');
202
202
  let existing = '';
203
203
  try {
204
204
  existing = await fs.readFile(normalizedPath, 'utf8');
@@ -246,7 +246,7 @@ async function handleRegenerate(args, cwd) {
246
246
  console.log(` Would regenerate:`);
247
247
  if (!skipWorkspaceMd)
248
248
  console.log(` - ${path.join(target, 'WORKSPACE.md')} (managed graph; operator section preserved)`);
249
- console.log(` - ${projectAiwgPath(target, 'AIWG.md')}`);
249
+ console.log(` - ${projectControlPath(target, 'AIWG.md')}`);
250
250
  if (!skipAiwgMd)
251
251
  console.log(` - ${aiwgMd}`);
252
252
  if (provider === 'claude') {
@@ -10,7 +10,6 @@
10
10
  */
11
11
  import path from 'path';
12
12
  import { existsSync, readFileSync } from 'fs';
13
- import { spawnSync } from 'child_process';
14
13
  import { createPtyWsHandler, registry as ptyRegistry } from '../../serve/pty-bridge.js';
15
14
  import { telemetryStore, createEvent } from '../../serve/telemetry.js';
16
15
  import { sandboxRegistry, normalizeSandboxEvent, } from '../../serve/sandbox-registry.js';
@@ -21,6 +20,7 @@ import { executorRegistry, validateRegisterPayload, validateDispatchPayload, val
21
20
  import { handleWebhook, IdempotencyCache, PushSecretRegistry, } from '../../a2a/webhook.js';
22
21
  import { AiwgError, EXIT_CODES } from '../errors.js';
23
22
  import { projectAiwgPath } from '../../config/project-artifacts.js';
23
+ import { loadFeaturePackage } from '../../features/runtime.js';
24
24
  // A2A push-notification state — module-scoped so the test harness can
25
25
  // monkey-patch them in if needed. One process serves one set of secrets.
26
26
  const pushSecretRegistry = new PushSecretRegistry();
@@ -190,11 +190,10 @@ async function setupWebSockets(httpServer, readOnly) {
190
190
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
191
191
  let wsMod;
192
192
  try {
193
- // @ts-expect-error ws lacks bundled types; we use the runtime constructor only
194
- wsMod = await import('ws');
193
+ wsMod = await loadFeaturePackage('ws');
195
194
  }
196
195
  catch {
197
- console.warn('[serve] ws package not available WebSocket routes disabled. Install with: npm install ws');
196
+ console.warn('[serve] WebSocket routes disabledrun `aiwg features install webserver` to enable them.');
198
197
  return;
199
198
  }
200
199
  // ws ships as CJS; ESM import may wrap in .default
@@ -510,40 +509,19 @@ export async function startServer(opts) {
510
509
  // import path raises ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING (#1277).
511
510
  // hono is an optionalDependency; tsc may not find its types under
512
511
  // `npm ci --omit=optional` (e.g. metadata-validation workflow). The
513
- // try/catch + auto-install fallback below handles that at runtime.
512
+ // try/catch below emits the managed feature-install route at runtime.
514
513
  // @ts-ignore — optional dep; may not be installed at typecheck time
515
- honoMod = await import('hono');
514
+ honoMod = await loadFeaturePackage('hono');
516
515
  // @ts-ignore — optional dep; may not be installed at typecheck time
517
- nodeMod = await import('@hono/node-server');
516
+ nodeMod = await loadFeaturePackage('@hono/node-server');
518
517
  }
519
518
  catch {
520
- // Auto-install optional serve dependencies on first use
521
- console.log('Installing serve dependencies (hono, @hono/node-server, ws)...');
522
- const result = spawnSync('npm', ['install', '--save-optional', 'hono', '@hono/node-server', 'ws'], { stdio: 'inherit' });
523
- if (result.status !== 0) {
524
- throw new AiwgError({
525
- code: 'ERR_SERVE_DEPS_INSTALL_FAILED',
526
- message: 'Failed to install serve dependencies (hono, @hono/node-server, ws)',
527
- hint: 'Install manually: npm install hono @hono/node-server ws',
528
- exitCode: EXIT_CODES.GENERAL,
529
- });
530
- }
531
- // Retry imports after install
532
- try {
533
- // @ts-ignore — optional dep; may not be installed at typecheck time
534
- honoMod = await import('hono');
535
- // @ts-ignore — optional dep; may not be installed at typecheck time
536
- nodeMod = await import('@hono/node-server');
537
- }
538
- catch (err) {
539
- throw new AiwgError({
540
- code: 'ERR_SERVE_DEPS_LOAD_FAILED',
541
- message: 'Serve dependencies installed but could not be loaded',
542
- hint: 'Try: npm install hono @hono/node-server ws',
543
- exitCode: EXIT_CODES.GENERAL,
544
- cause: err,
545
- });
546
- }
519
+ throw new AiwgError({
520
+ code: 'ERR_SERVE_DEPS_MISSING',
521
+ message: 'The optional webserver feature is not available',
522
+ hint: 'Run `aiwg features install webserver`, then retry `aiwg serve`.',
523
+ exitCode: EXIT_CODES.GENERAL,
524
+ });
547
525
  }
548
526
  const { Hono } = honoMod;
549
527
  const { serve } = nodeMod;
@@ -1692,7 +1670,8 @@ export async function startServer(opts) {
1692
1670
  const webDistDir = path.join(opts.frameworkRoot, 'apps', 'web', 'dist');
1693
1671
  if (existsSync(webDistDir)) {
1694
1672
  try {
1695
- const { serveStatic } = await (new Function('m', 'return import(m)'))('@hono/node-server/serve-static');
1673
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1674
+ const { serveStatic } = await loadFeaturePackage('@hono/node-server/serve-static');
1696
1675
  app.use('/*', serveStatic({ root: webDistDir }));
1697
1676
  }
1698
1677
  catch {
@@ -1788,7 +1767,7 @@ export const serveHandler = {
1788
1767
  if (open) {
1789
1768
  try {
1790
1769
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
1791
- const openMod = await (new Function('m', 'return import(m)'))('open');
1770
+ const openMod = await loadFeaturePackage('open');
1792
1771
  const openBrowser = openMod.default ?? openMod;
1793
1772
  await openBrowser(url);
1794
1773
  }
@@ -232,6 +232,7 @@ function installerConsistencyChecks(manifest, manifestDir) {
232
232
  const recoveryIds = new Set((manifest.spec.recovery ?? []).map((recovery) => recovery.id));
233
233
  const osConfigIds = new Set((manifest.spec.os_config ?? []).map((entry) => entry.id));
234
234
  const installType = manifest.metadata.install_type ?? 'user';
235
+ const executionMode = manifest.metadata.execution_mode ?? 'deterministic';
235
236
  for (const [index, step] of manifest.spec.steps.entries()) {
236
237
  if (allStepIds.has(step.id)) {
237
238
  findings.push({ severity: 'error', path: `/spec/steps/${index}/id`, rule: 'uniqueStepId', message: `duplicate step id '${step.id}'` });
@@ -260,7 +261,7 @@ function installerConsistencyChecks(manifest, manifestDir) {
260
261
  if (!step.instruction) {
261
262
  findings.push({ severity: 'error', path: `${pointer}/instruction`, rule: 'agenticInstruction', message: 'agentic step requires instruction' });
262
263
  }
263
- else {
264
+ else if (executionMode !== 'provider-orchestrated') {
264
265
  findings.push({ severity: 'warning', path: pointer, rule: 'agenticStep', message: 'agentic steps are exception handling only and require manual intervention during setup-run' });
265
266
  }
266
267
  }
@@ -687,6 +688,12 @@ export function runSetupManifest(options) {
687
688
  return { exitCode: 1, message: 'setup-run: manifest validation failed before execution' };
688
689
  }
689
690
  const manifest = validation.manifest;
691
+ if (manifest.metadata.execution_mode === 'provider-orchestrated') {
692
+ return {
693
+ exitCode: 2,
694
+ message: 'setup-run: this manifest is provider-orchestrated; give its URL or contents to a supported AI provider instead of executing it as a deterministic CLI manifest',
695
+ };
696
+ }
690
697
  const target = detectPlatform(options);
691
698
  if (!manifest.spec.platforms.some((candidate) => platformMatches(target, candidate))) {
692
699
  return { exitCode: 1, message: `setup-run: platform ${target.os}${target.distro ? `/${target.distro}` : ''}/${target.arch}/${target.shell} is not declared in the manifest` };