@aiwg/cli 2026.8.16 → 2026.8.18

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 (55) hide show
  1. package/bin/aiwg.mjs +24 -2
  2. package/dist/src/a2a/agent-card.js +4 -1
  3. package/dist/src/a2a/client.js +148 -68
  4. package/dist/src/a2a/codecs.js +480 -0
  5. package/dist/src/a2a/events.js +226 -0
  6. package/dist/src/a2a/hitl-driver.js +8 -6
  7. package/dist/src/a2a/hitl.js +2 -1
  8. package/dist/src/a2a/http.js +85 -5
  9. package/dist/src/a2a/protocol.js +136 -0
  10. package/dist/src/a2a/types.js +4 -14
  11. package/dist/src/a2a/webhook.js +101 -4
  12. package/dist/src/artifacts/browser-export.js +1 -0
  13. package/dist/src/artifacts/cli.js +1 -1
  14. package/dist/src/artifacts/fortemi-core-query-adapter.js +6 -0
  15. package/dist/src/artifacts/types.js +73 -1
  16. package/dist/src/audit/operator-decision.js +15 -1
  17. package/dist/src/channel/manager.mjs +89 -17
  18. package/dist/src/cli/agent-spawn.js +4 -2
  19. package/dist/src/cli/handlers/cockpit.js +41 -0
  20. package/dist/src/cli/handlers/help.js +1 -1
  21. package/dist/src/cli/handlers/index.js +3 -1
  22. package/dist/src/cli/handlers/installation.js +79 -0
  23. package/dist/src/cli/handlers/ralph.js +2 -1
  24. package/dist/src/cli/handlers/refresh.js +4 -2
  25. package/dist/src/cli/handlers/runtime-info.js +9 -1
  26. package/dist/src/cli/handlers/sdlc-accelerate.js +2 -1
  27. package/dist/src/cli/handlers/serve.js +107 -4
  28. package/dist/src/cli/handlers/session.js +12 -26
  29. package/dist/src/cli/handlers/use.js +117 -12
  30. package/dist/src/cli/handlers/utilities.js +4 -0
  31. package/dist/src/cli/handlers/version.js +4 -0
  32. package/dist/src/cli/handlers/workspace.js +24 -2
  33. package/dist/src/cli/services/deployment-verification.js +9 -2
  34. package/dist/src/cockpit/doctor.js +257 -0
  35. package/dist/src/config/aiwg-config.js +36 -3
  36. package/dist/src/config/user-config-dir.mjs +29 -0
  37. package/dist/src/config/user-config.js +4 -22
  38. package/dist/src/extensions/commands/definitions.js +20 -1
  39. package/dist/src/features/catalog.js +2 -1
  40. package/dist/src/flow/graph-metadata.js +56 -0
  41. package/dist/src/installation/manager.mjs +243 -0
  42. package/dist/src/providers/transformation-receipt-integration.js +130 -3
  43. package/dist/src/security/artifact-verifier.js +7 -1
  44. package/dist/src/serve/a2a-terminal-observer.js +28 -5
  45. package/dist/src/serve/dispatch-router.js +32 -4
  46. package/dist/src/serve/executor-registry.js +29 -0
  47. package/dist/src/serve/mission-conductor.js +15 -1
  48. package/dist/src/serve/pty-bridge.js +6 -11
  49. package/dist/src/serve/stack-adapters.js +2 -1
  50. package/dist/src/serve/telemetry.js +5 -1
  51. package/dist/src/skills/run.js +15 -6
  52. package/dist/src/update/checker.mjs +16 -15
  53. package/dist/src/update/notifier.mjs +8 -3
  54. package/dist/src/update/service.mjs +49 -5
  55. package/package.json +2 -1
@@ -91,7 +91,7 @@ function displayHelp() {
91
91
  ]);
92
92
  helpGroup('FEATURES', [
93
93
  ['features', 'Show optional feature install status'],
94
- ['cockpit [--status]', 'Launch the opt-in AIWG Cockpit control plane'],
94
+ ['cockpit [--status|doctor]', 'Launch Cockpit or diagnose its executor topology'],
95
95
  ]);
96
96
  helpGroup('VALIDATION', [
97
97
  ['validate-metadata [path]', 'Validate AIWG component metadata (defaults to agentic/code)'],
@@ -63,12 +63,13 @@ import { costReportHandler } from './cost-report.js';
63
63
  import { evidenceHandler } from './evidence.js';
64
64
  import { artifactVerifyHandler } from './artifact-verify.js';
65
65
  import { outputModeHandler } from './output-mode.js';
66
+ import { installationHandler } from './installation.js';
66
67
  // Re-export individual handlers
67
68
  export {
68
69
  // Maintenance
69
70
  helpHandler, versionHandler, authHandler, doctorHandler, contextFirewallHandler, updateHandler, refreshHandler, regenerateHandler, workspaceContextHandler,
70
71
  // Framework management
71
- useHandler, listHandler, removeHandler, promoteHandler, installHandler, packagesHandler, marketplaceHandler, initHandler, setupHandler, setupGenerateHandler, setupRunHandler, setupValidateHandler, issueHandler, issueAuditHandler, runHandler, jobHandler, costReportHandler, evidenceHandler, artifactVerifyHandler, outputModeHandler,
72
+ useHandler, listHandler, removeHandler, promoteHandler, installHandler, packagesHandler, marketplaceHandler, initHandler, setupHandler, setupGenerateHandler, setupRunHandler, setupValidateHandler, issueHandler, issueAuditHandler, runHandler, jobHandler, costReportHandler, evidenceHandler, artifactVerifyHandler, outputModeHandler, installationHandler,
72
73
  // Project
73
74
  newBundleHandler, quickrefHandler, newProjectHandler, sessionHandler, sessionsHandler,
74
75
  // Workspace
@@ -125,6 +126,7 @@ export const allHandlers = [
125
126
  contextFirewallHandler,
126
127
  updateHandler,
127
128
  refreshHandler,
129
+ installationHandler,
128
130
  regenerateHandler,
129
131
  workspaceContextHandler,
130
132
  // Framework management
@@ -0,0 +1,79 @@
1
+ import { getPackageRoot } from '../../channel/manager.mjs';
2
+ import { adoptInstallation, inspectInstallation, loadInstallationIdentity, switchInstallation, } from '../../installation/manager.mjs';
3
+ function valueAfter(args, flag) {
4
+ const index = args.indexOf(flag);
5
+ return index >= 0 ? args[index + 1] : undefined;
6
+ }
7
+ function display(status, json) {
8
+ if (json) {
9
+ console.log(JSON.stringify(status, null, 2));
10
+ return;
11
+ }
12
+ console.log('\nCanonical AIWG Installation');
13
+ console.log('===========================');
14
+ console.log(`State: ${status.state}`);
15
+ console.log(`Canonical method: ${status.identity?.method ?? '(unrecorded)'}`);
16
+ console.log(`Canonical root: ${status.identity?.root ?? '(unrecorded)'}`);
17
+ console.log(`Manager: ${status.identity?.managerExecutable ?? '(internal)'}`);
18
+ console.log(`Update strategy: ${status.identity?.updateStrategy ?? '(unrecorded)'}`);
19
+ console.log(`Run mode: ${status.identity?.runMode ?? '(unrecorded)'}`);
20
+ console.log(`Release channel: ${status.identity?.channel ?? '(unrecorded)'}`);
21
+ console.log(`Actual method: ${status.actualMethod}`);
22
+ console.log(`Actual root: ${status.actualRoot}`);
23
+ if (status.drift.length > 0) {
24
+ console.log('Drift:');
25
+ for (const item of status.drift)
26
+ console.log(` - ${item}`);
27
+ }
28
+ console.log('');
29
+ }
30
+ export const installationHandler = {
31
+ id: 'installation',
32
+ name: 'Installation',
33
+ description: 'Inspect, adopt, or deliberately switch the canonical global installation',
34
+ category: 'maintenance',
35
+ aliases: [],
36
+ async execute(ctx) {
37
+ const [action = 'show'] = ctx.args;
38
+ const json = ctx.args.includes('--json');
39
+ const actualRoot = getPackageRoot();
40
+ const common = {
41
+ actualRoot,
42
+ configDir: valueAfter(ctx.args, '--config-dir'),
43
+ managerExecutable: valueAfter(ctx.args, '--manager'),
44
+ channel: valueAfter(ctx.args, '--channel'),
45
+ };
46
+ if (action === 'show') {
47
+ const identity = loadInstallationIdentity({ ...common, createIfMissing: true });
48
+ display(inspectInstallation({ ...common, identity }), json);
49
+ return { exitCode: 0 };
50
+ }
51
+ if (action === 'adopt') {
52
+ const method = valueAfter(ctx.args, '--method');
53
+ const status = adoptInstallation({
54
+ ...common,
55
+ method,
56
+ runMode: valueAfter(ctx.args, '--run-mode'),
57
+ });
58
+ display(status, json);
59
+ return { exitCode: 0 };
60
+ }
61
+ if (action === 'switch') {
62
+ const root = valueAfter(ctx.args, '--root');
63
+ const method = valueAfter(ctx.args, '--method');
64
+ if (!root || !method) {
65
+ return { exitCode: 2, message: 'Usage: aiwg installation switch --root <path> --method <npm|web|source> [--manager <absolute-path>]' };
66
+ }
67
+ const status = switchInstallation({
68
+ ...common,
69
+ root,
70
+ method,
71
+ runMode: valueAfter(ctx.args, '--run-mode'),
72
+ });
73
+ display(status, json);
74
+ return { exitCode: 0 };
75
+ }
76
+ return { exitCode: 2, message: 'Usage: aiwg installation <show|adopt|switch> [options]' };
77
+ },
78
+ };
79
+ //# sourceMappingURL=installation.js.map
@@ -278,7 +278,8 @@ LFD LOOP CONTROLS (hard cumulative ceilings; loop stops with a best-output repor
278
278
  Spawnable: claude, opencode, codex, hermes
279
279
  --dangerous Enable unrestricted mode for the selected provider.
280
280
  Passes the provider's native flag (e.g. --dangerously-skip-permissions
281
- for claude/opencode, --full-auto for codex). No effect if the
281
+ for claude/opencode,
282
+ --dangerously-bypass-approvals-and-sandbox for codex). No effect if the
282
283
  provider doesn't have a dangerous mode flag.
283
284
  --params "<args>" Pass arbitrary args verbatim to the agent binary.
284
285
  Appended after all other flags. Quoted segments preserved.
@@ -242,8 +242,10 @@ export const refreshHandler = {
242
242
  ui.success('Package up to date');
243
243
  }
244
244
  else {
245
- if (!quiet)
246
- ui.warn('Update check returned non-zero (may already be current)');
245
+ return {
246
+ exitCode: updateResult.exitCode,
247
+ message: 'Installation update failed; refresh stopped before re-deployment. Run `aiwg installation show` for canonical-install diagnostics.',
248
+ };
247
249
  }
248
250
  }
249
251
  }
@@ -10,6 +10,8 @@
10
10
  */
11
11
  import path from 'path';
12
12
  import { AiwgError, EXIT_CODES, handlerResultFromError } from '../errors.js';
13
+ import { getPackageRoot } from '../../channel/manager.mjs';
14
+ import { inspectInstallation } from '../../installation/manager.mjs';
13
15
  function isMissingRuntimeCatalogError(error) {
14
16
  return error instanceof Error && error.message.includes('No catalog found');
15
17
  }
@@ -237,8 +239,9 @@ async function handleRuntimeInfo(args, cwd = process.cwd()) {
237
239
  await discovery.discover();
238
240
  summary = await discovery.getSummary();
239
241
  }
242
+ const installation = inspectInstallation({ actualRoot: getPackageRoot() });
240
243
  if (hasJson) {
241
- console.log(JSON.stringify(summary, null, 2));
244
+ console.log(JSON.stringify({ ...summary, installation }, null, 2));
242
245
  }
243
246
  else {
244
247
  console.log(`\nRuntime Environment Summary`);
@@ -256,6 +259,11 @@ async function handleRuntimeInfo(args, cwd = process.cwd()) {
256
259
  console.log(`\nTotal: ${summary.totalTools} verified tools`);
257
260
  console.log(`\nLast Discovery: ${summary.lastDiscovery}`);
258
261
  console.log(`Catalog: ${summary.catalogPath}`);
262
+ console.log(`\nAIWG Installation:`);
263
+ console.log(` Canonical: ${installation.identity?.method ?? 'unrecorded'} at ${installation.identity?.root ?? '(unrecorded)'}`);
264
+ console.log(` Actual: ${installation.actualMethod} at ${installation.actualRoot}`);
265
+ console.log(` Run mode: ${installation.identity?.runMode ?? '(unrecorded)'}`);
266
+ console.log(` State: ${installation.state}`);
259
267
  // Scheduler backend detection
260
268
  const { execSync } = await import('child_process');
261
269
  let schedulerBackend = 'external trigger required (system cron/systemd/CI)';
@@ -123,7 +123,8 @@ AGENT OPTIONS:
123
123
  IDE-integrated (guidance only): copilot, cursor, factory, warp, windsurf
124
124
  --dangerous Enable unrestricted mode (skips permission prompts).
125
125
  Maps to the provider's native flag — e.g. claude gets
126
- --dangerously-skip-permissions, codex gets --full-auto.
126
+ --dangerously-skip-permissions, codex gets
127
+ --dangerously-bypass-approvals-and-sandbox.
127
128
  Has no effect on providers that don't support it.
128
129
  --params "<args>" Pass arbitrary args directly to the agent binary.
129
130
  Appended verbatim after all other flags. You are
@@ -27,8 +27,14 @@ const pushSecretRegistry = new PushSecretRegistry();
27
27
  const webhookIdempotency = new IdempotencyCache();
28
28
  const DEFAULT_PORT = 7337;
29
29
  const DEFAULT_HOST = '127.0.0.1';
30
+ function readA2AProtocolPolicy(value) {
31
+ const policy = value ?? '0.3';
32
+ if (policy === '0.3' || policy === '1.0' || policy === 'auto')
33
+ return policy;
34
+ throw new Error(`AIWG_A2A_PROTOCOL_POLICY must be 0.3, 1.0, or auto (received '${policy}')`);
35
+ }
30
36
  /**
31
- * Parse --port, --bind, --no-open, --read-only flags from args
37
+ * Parse dashboard and A2A negotiation flags from args.
32
38
  */
33
39
  function parseServeArgs(args) {
34
40
  let port = DEFAULT_PORT;
@@ -36,6 +42,9 @@ function parseServeArgs(args) {
36
42
  let open = true;
37
43
  let readOnly = false;
38
44
  let sandbox = null;
45
+ let a2aProtocolPolicy;
46
+ let allowA2AProtocolFallback;
47
+ let allowLegacyExecutorFallback;
39
48
  for (let i = 0; i < args.length; i++) {
40
49
  const arg = args[i];
41
50
  if (arg === '--port' && args[i + 1]) {
@@ -58,8 +67,33 @@ function parseServeArgs(args) {
58
67
  else if (arg === '--read-only') {
59
68
  readOnly = true;
60
69
  }
70
+ else if (arg === '--a2a-protocol' && args[i + 1]) {
71
+ a2aProtocolPolicy = readA2AProtocolPolicy(args[i + 1]);
72
+ i++;
73
+ }
74
+ else if (arg === '--a2a-protocol-fallback') {
75
+ allowA2AProtocolFallback = true;
76
+ }
77
+ else if (arg === '--no-a2a-protocol-fallback') {
78
+ allowA2AProtocolFallback = false;
79
+ }
80
+ else if (arg === '--a2a-legacy-executor-fallback') {
81
+ allowLegacyExecutorFallback = true;
82
+ }
83
+ else if (arg === '--no-a2a-legacy-executor-fallback') {
84
+ allowLegacyExecutorFallback = false;
85
+ }
61
86
  }
62
- return { port, host, open, readOnly, sandbox };
87
+ return {
88
+ port,
89
+ host,
90
+ open,
91
+ readOnly,
92
+ sandbox,
93
+ ...(a2aProtocolPolicy ? { a2aProtocolPolicy } : {}),
94
+ ...(allowA2AProtocolFallback !== undefined ? { allowA2AProtocolFallback } : {}),
95
+ ...(allowLegacyExecutorFallback !== undefined ? { allowLegacyExecutorFallback } : {}),
96
+ };
63
97
  }
64
98
  // ============================================================
65
99
  // WebSocket routing (#851)
@@ -499,6 +533,13 @@ async function setupWebSockets(httpServer, readOnly) {
499
533
  * @see #1277 — moves the integration suite off spawn-based testing
500
534
  */
501
535
  export async function startServer(opts) {
536
+ const configuredA2AProtocolPolicy = opts.a2aProtocolPolicy
537
+ ?? readA2AProtocolPolicy(process.env['AIWG_A2A_PROTOCOL_POLICY']);
538
+ const configuredA2AProtocolFallback = opts.allowA2AProtocolFallback
539
+ ?? process.env['AIWG_A2A_PROTOCOL_FALLBACK'] === 'true';
540
+ const configuredLegacyExecutorFallback = configuredA2AProtocolPolicy !== '1.0'
541
+ && (opts.allowLegacyExecutorFallback
542
+ ?? process.env['AIWG_A2A_LEGACY_EXECUTOR_FALLBACK'] !== 'false');
502
543
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
503
544
  let honoMod;
504
545
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -760,8 +801,32 @@ export async function startServer(opts) {
760
801
  let a2aInstanceId;
761
802
  let dispatchPath = 'v2';
762
803
  let a2aTask = undefined;
804
+ let a2aProtocolVersion;
805
+ let a2aInterface;
806
+ let a2aFallbackReason;
807
+ const a2aProtocolPolicy = configuredA2AProtocolPolicy;
763
808
  try {
764
809
  const result = await routeDispatch(executor, payload, {
810
+ a2aProtocolPolicy,
811
+ allowA2AProtocolFallback: configuredA2AProtocolFallback,
812
+ allowLegacyExecutorFallback: configuredLegacyExecutorFallback,
813
+ onA2AProtocolSelection: (info) => {
814
+ telemetryStore.ingest(createEvent('a2a.protocol.selected', sessionId, {
815
+ selected_version: info.selected,
816
+ policy: info.policy,
817
+ ...(info.interface ? {
818
+ protocol_binding: info.interface.protocolBinding,
819
+ interface_url: info.interface.url,
820
+ } : {}),
821
+ }, missionId));
822
+ },
823
+ onA2AProtocolFallback: (info) => {
824
+ telemetryStore.ingest(createEvent('a2a.protocol.fallback', sessionId, {
825
+ from_version: info.from,
826
+ to_version: info.to,
827
+ reason: info.reason,
828
+ }, missionId));
829
+ },
765
830
  onV1Fallback: (info) => {
766
831
  logServeWarn('dispatch', `v1 fallback for executor ${info.executorId}: ${info.reason}`);
767
832
  telemetryStore.ingest(createEvent('v1.dispatch.fallback', sessionId, info, missionId));
@@ -775,6 +840,9 @@ export async function startServer(opts) {
775
840
  dispatchPath = result.dispatchPath;
776
841
  a2aInstanceId = result.a2aInstanceId;
777
842
  a2aTask = result.task;
843
+ a2aProtocolVersion = result.a2aProtocolVersion;
844
+ a2aInterface = result.a2aInterface;
845
+ a2aFallbackReason = result.a2aFallbackReason;
778
846
  if (result.estimatedStart)
779
847
  estimatedStart = result.estimatedStart;
780
848
  }
@@ -796,12 +864,22 @@ export async function startServer(opts) {
796
864
  return c.json({ error: errorTag, detail: msg }, status);
797
865
  }
798
866
  // 4. Record the mission and emit telemetry
867
+ if (a2aProtocolVersion && a2aInterface) {
868
+ executorRegistry.recordA2AProtocolSelection(executor.executorId, {
869
+ policy: a2aProtocolPolicy,
870
+ selectedVersion: a2aProtocolVersion,
871
+ interface: a2aInterface,
872
+ ...(a2aFallbackReason ? { fallbackReason: a2aFallbackReason } : {}),
873
+ });
874
+ }
799
875
  executorRegistry.assignMission(missionId, executor.executorId);
800
876
  if (dispatchPath === 'v2' && a2aTask && a2aInstanceId) {
801
877
  void observeA2ATerminalState(executorRegistry, executor, missionId, a2aInstanceId, a2aTask, {
802
878
  onError: (err) => {
803
879
  logServeWarn('dispatch', `A2A terminal observer failed for mission ${missionId}: ${err.message ?? String(err)}`);
804
880
  },
881
+ ...(a2aProtocolVersion ? { protocolVersion: a2aProtocolVersion } : {}),
882
+ ...(a2aInterface ? { selectedInterface: a2aInterface } : {}),
805
883
  });
806
884
  }
807
885
  telemetryStore.ingest(createEvent('mission.dispatch', sessionId, {
@@ -809,6 +887,8 @@ export async function startServer(opts) {
809
887
  executorId: executor.executorId,
810
888
  objective: payload.objective,
811
889
  completion: payload.completion,
890
+ ...(a2aProtocolVersion ? { a2a_protocol_version: a2aProtocolVersion } : {}),
891
+ ...(a2aFallbackReason ? { a2a_fallback_reason: a2aFallbackReason } : {}),
812
892
  }, missionId));
813
893
  // 5. Return 202 Accepted
814
894
  const dispatchResp = {
@@ -819,6 +899,14 @@ export async function startServer(opts) {
819
899
  };
820
900
  if (a2aInstanceId)
821
901
  dispatchResp.a2a_instance_id = a2aInstanceId;
902
+ if (a2aProtocolVersion)
903
+ dispatchResp.a2a_protocol_version = a2aProtocolVersion;
904
+ if (a2aInterface) {
905
+ dispatchResp.a2a_protocol_binding = a2aInterface.protocolBinding;
906
+ dispatchResp.a2a_interface_url = a2aInterface.url;
907
+ }
908
+ if (a2aFallbackReason)
909
+ dispatchResp.a2a_fallback_reason = a2aFallbackReason;
822
910
  if (estimatedStart)
823
911
  dispatchResp.estimated_start = estimatedStart;
824
912
  return c.json(dispatchResp, 202);
@@ -947,6 +1035,7 @@ export async function startServer(opts) {
947
1035
  const result = await handleWebhook(configId, bodyBuf, signature, eventId, {
948
1036
  registry: pushSecretRegistry,
949
1037
  idempotency: webhookIdempotency,
1038
+ contentType: c.req.header('content-type') ?? undefined,
950
1039
  route: async (entry, event) => {
951
1040
  // Append to mission recentEvents via a synthesized envelope.
952
1041
  // The 'mission.webhook' event type falls through the registry's
@@ -990,11 +1079,17 @@ export async function startServer(opts) {
990
1079
  if (!p.secret || typeof p.secret !== 'string' || p.secret.length < 16) {
991
1080
  return c.json({ error: 'secret is required and must be ≥16 chars' }, 400);
992
1081
  }
1082
+ if (p.protocolVersion === '1.0' && !p.taskId) {
1083
+ return c.json({ error: 'taskId is required for A2A 1.0 push ownership scope' }, 400);
1084
+ }
993
1085
  pushSecretRegistry.register({
994
1086
  configId: p.configId,
995
1087
  secret: p.secret,
996
1088
  ...(p.missionId ? { missionId: p.missionId } : {}),
997
1089
  ...(p.taskId ? { taskId: p.taskId } : {}),
1090
+ ...(p.contextId ? { contextId: p.contextId } : {}),
1091
+ ...(p.protocolVersion ? { protocolVersion: p.protocolVersion } : {}),
1092
+ ...(p.taskOwner ? { taskOwner: p.taskOwner } : {}),
998
1093
  ...(p.metadata ? { metadata: p.metadata } : {}),
999
1094
  });
1000
1095
  return c.json({ ok: true, configId: p.configId }, 201);
@@ -1749,10 +1844,18 @@ export const serveHandler = {
1749
1844
  category: 'project',
1750
1845
  aliases: [],
1751
1846
  async execute(ctx) {
1752
- const { port, host, open, readOnly } = parseServeArgs(ctx.args);
1847
+ const { port, host, open, readOnly, a2aProtocolPolicy, allowA2AProtocolFallback, allowLegacyExecutorFallback, } = parseServeArgs(ctx.args);
1753
1848
  let server;
1754
1849
  try {
1755
- server = await startServer({ port, host, readOnly, frameworkRoot: ctx.frameworkRoot });
1850
+ server = await startServer({
1851
+ port,
1852
+ host,
1853
+ readOnly,
1854
+ frameworkRoot: ctx.frameworkRoot,
1855
+ ...(a2aProtocolPolicy ? { a2aProtocolPolicy } : {}),
1856
+ ...(allowA2AProtocolFallback !== undefined ? { allowA2AProtocolFallback } : {}),
1857
+ ...(allowLegacyExecutorFallback !== undefined ? { allowLegacyExecutorFallback } : {}),
1858
+ });
1756
1859
  }
1757
1860
  catch (error) {
1758
1861
  const { handlerResultFromError } = await import('../errors.js');
@@ -21,6 +21,7 @@ import { spawnSync } from 'child_process';
21
21
  import { ensureRuntimeHome, writeProfileConfig, launchWithProfile } from '../../mcp/adapters/codex-runtime.js';
22
22
  import { getFrameworkRoot } from '../../channel/manager.mjs';
23
23
  import { forceUpdateCheck } from '../../update/checker.mjs';
24
+ import { updateInstallation } from '../../update/service.mjs';
24
25
  import { readAiwgConfig, getDeploymentSummary, VALID_PROVIDERS, } from '../../config/aiwg-config.js';
25
26
  import { getProviderConfig, isSpawnableProvider, PROVIDER_CONFIGS, } from '../agent-spawn.js';
26
27
  import { useHandler as useFrameworkHandler } from './use.js';
@@ -84,8 +85,11 @@ async function checkAndUpdateVersion(noRepair) {
84
85
  debug('cli:session:update', 'forceUpdateCheck failed', err);
85
86
  if (!noRepair) {
86
87
  console.log(' Version check failed — attempting sync...');
87
- const r = spawnSync('npm', ['install', '-g', 'aiwg@latest'], { stdio: 'inherit' });
88
- if (r.status !== 0) {
88
+ try {
89
+ await updateInstallation();
90
+ }
91
+ catch (updateError) {
92
+ debug('cli:session:update', 'canonical installation update failed', updateError);
89
93
  console.warn(' WARN Could not update aiwg — continuing with current version.');
90
94
  return false;
91
95
  }
@@ -103,10 +107,11 @@ function runDoctor(_frameworkRoot, cwd) {
103
107
  }
104
108
  /**
105
109
  * Attempt to repair a failed doctor result.
106
- * Strategy: `aiwg sync` first; if that fails, offer full reinstall.
110
+ * Strategy: `aiwg sync`; package repair remains bound to the canonical update
111
+ * strategy and never falls through to an arbitrary npm on PATH.
107
112
  * Returns true if repair succeeded (doctor now passes).
108
113
  */
109
- function repairInstallation(frameworkRoot, cwd, provider, installedFrameworks) {
114
+ function repairInstallation(frameworkRoot, cwd, provider) {
110
115
  // Strategy 1: sync (update + redeploy)
111
116
  console.log('\n Attempting auto-repair via `aiwg sync`...');
112
117
  const syncResult = spawnSync(process.execPath, [process.argv[1], 'sync'], { stdio: 'inherit', cwd });
@@ -118,29 +123,13 @@ function repairInstallation(frameworkRoot, cwd, provider, installedFrameworks) {
118
123
  return true;
119
124
  }
120
125
  }
121
- // Strategy 2: full reinstall
122
- console.log('\n Sync did not fully resolve the issue. Attempting full reinstall...');
123
- const reinstallResult = spawnSync('npm', ['install', '-g', 'aiwg@latest'], { stdio: 'inherit' });
124
- if (reinstallResult.status === 0 && installedFrameworks.length > 0) {
125
- // Redeploy all installed frameworks for this provider
126
- console.log(`\n Redeploying frameworks to ${provider}...`);
127
- for (const fw of installedFrameworks) {
128
- spawnSync(process.execPath, [process.argv[1], 'use', fw, '--provider', provider], { stdio: 'inherit', cwd });
129
- }
130
- // Final doctor check
131
- const finalOk = runDoctor(frameworkRoot, cwd);
132
- if (finalOk) {
133
- console.log(' OK Full reinstall + redeploy succeeded.');
134
- return true;
135
- }
136
- }
137
126
  // Could not auto-repair
138
127
  console.log(`
139
128
  ✗ Auto-repair could not resolve all issues.
140
129
 
141
130
  Manual options:
142
- aiwg sync sync and redeploy
143
- npm install -g aiwg@latest — reinstall package
131
+ aiwg installation show inspect canonical install drift
132
+ aiwg refresh — update and redeploy canonically
144
133
  aiwg use all --provider ${provider.padEnd(10)} — redeploy all frameworks
145
134
 
146
135
  Report this issue:
@@ -291,10 +280,7 @@ export const sessionHandler = {
291
280
  console.log('\n Running health checks...');
292
281
  const doctorOk = runDoctor(frameworkRoot, cwd);
293
282
  if (!doctorOk && !noRepair) {
294
- // Read installed frameworks before attempting repair (needed for redeploy)
295
- const config = await readAiwgConfig(cwd);
296
- const installedFrameworks = Object.keys(config?.installed ?? {});
297
- const repaired = repairInstallation(frameworkRoot, cwd, provider, installedFrameworks);
283
+ const repaired = repairInstallation(frameworkRoot, cwd, provider);
298
284
  if (!repaired) {
299
285
  // Repair failed — still continue (user was already informed)
300
286
  }
@@ -52,7 +52,7 @@ import { generate as generateContextFiles, discoverDeployedArtifacts, } from '..
52
52
  import { verifyModelWrapperDeployment } from '../../models/wrapper-deployment.js';
53
53
  import { loadGraphIndexFile } from '../../artifacts/index-reader.js';
54
54
  import { aggregateUseDeploymentResult, buildDryRunUseResult, renderUseDeploymentResult, verifyProviderDeployment, } from '../services/deployment-verification.js';
55
- import { finalizeProviderTransformationReceipt, sourceVerificationsFromSignedWebRelease, } from '../../providers/transformation-receipt-integration.js';
55
+ import { finalizeProviderTransformationReceipt, providerReceiptHasLocalSources, sourceVerificationsFromSignedWebRelease, } from '../../providers/transformation-receipt-integration.js';
56
56
  import { loadResourceTrustRootFile, resolveWebRelease, } from '../../resources/web-release.js';
57
57
  import { createResourceCredentialProvider } from '../../auth/resource-credentials.js';
58
58
  /**
@@ -75,10 +75,16 @@ function providerReceiptWebReleaseOptions() {
75
75
  : {}),
76
76
  };
77
77
  }
78
- async function signedProviderSourceVerifications(options) {
78
+ function releaseResourceUnavailable(error) {
79
+ const message = error instanceof Error ? error.message : String(error);
80
+ return /fetch failed|request timed out|no fetch implementation/i.test(message);
81
+ }
82
+ export async function resolveProviderReceiptSource(options) {
83
+ if (await providerReceiptHasLocalSources(options))
84
+ return { sourceDisposition: 'local-source' };
79
85
  const versionInfo = await getVersionInfo();
80
86
  if (versionInfo.devMode)
81
- return undefined;
87
+ return { sourceDisposition: 'local-source' };
82
88
  const releaseOptions = providerReceiptWebReleaseOptions();
83
89
  let release;
84
90
  try {
@@ -90,15 +96,24 @@ async function signedProviderSourceVerifications(options) {
90
96
  // Protected production resources require the authenticated release
91
97
  // credential. A configured alternate endpoint may intentionally be public.
92
98
  if (!token && releaseOptions.baseUrl === undefined)
93
- return undefined;
94
- release = await resolveWebRelease({
95
- ...releaseOptions,
96
- selector: versionInfo.version,
97
- credentialProvider: async () => token,
98
- });
99
+ return { sourceDisposition: 'source-unavailable' };
100
+ try {
101
+ release = await resolveWebRelease({
102
+ ...releaseOptions,
103
+ selector: versionInfo.version,
104
+ credentialProvider: async () => token,
105
+ });
106
+ }
107
+ catch (error) {
108
+ if (releaseResourceUnavailable(error))
109
+ return { sourceDisposition: 'source-unavailable' };
110
+ throw error;
111
+ }
99
112
  }
100
113
  const verifications = await sourceVerificationsFromSignedWebRelease(options, release);
101
- return Object.keys(verifications).length > 0 ? verifications : undefined;
114
+ return Object.keys(verifications).length > 0
115
+ ? { sourceVerifications: verifications }
116
+ : { sourceDisposition: 'verification-failed' };
102
117
  }
103
118
  /**
104
119
  * Framework name to deploy mode mapping.
@@ -1005,6 +1020,79 @@ async function countBundleDeployedArtifacts(bundlePath, target, provider) {
1005
1020
  rules: await countDeployedBundleFiles(bundlePath, 'rules', target, paths.rules, ['.md', '.mdc']),
1006
1021
  };
1007
1022
  }
1023
+ const SKILL_SUPPORT_REFERENCE = /(?:^|[\s`('"\[])((?:templates|references|scripts|assets)\/[A-Za-z0-9._@/+\-]+)(?=$|[\s`)'"\],:;])/gm;
1024
+ /**
1025
+ * Project skill-relative support files may live beside the skill or at the
1026
+ * bundle root (plugin payloads commonly share report templates). Materialize
1027
+ * only paths explicitly named by SKILL.md, and fail closed on missing or
1028
+ * unsafe sources so a deployed instruction can never point at absent assets.
1029
+ */
1030
+ async function reconcileProjectLocalSkillAssets(bundlePath, target, provider) {
1031
+ const skillsRoot = path.join(bundlePath, 'skills');
1032
+ let skillDirs;
1033
+ try {
1034
+ skillDirs = (await fs.readdir(skillsRoot, { withFileTypes: true }))
1035
+ .filter(entry => entry.isDirectory())
1036
+ .map(entry => entry.name);
1037
+ }
1038
+ catch {
1039
+ return;
1040
+ }
1041
+ const paths = getProviderPaths(provider);
1042
+ const kernelSkillsPath = getProviderKernelSkillsPath(provider);
1043
+ const deployRoots = [...new Set([
1044
+ paths.skills,
1045
+ kernelSkillsPath,
1046
+ ].filter((value) => Boolean(value)).map(value => resolveDeployPath(target, value)))];
1047
+ for (const skillName of skillDirs) {
1048
+ const sourceSkillDir = path.join(skillsRoot, skillName);
1049
+ const sourceSkillMd = path.join(sourceSkillDir, 'SKILL.md');
1050
+ let content;
1051
+ try {
1052
+ content = await fs.readFile(sourceSkillMd, 'utf8');
1053
+ }
1054
+ catch {
1055
+ continue;
1056
+ }
1057
+ const references = [...new Set([...content.matchAll(SKILL_SUPPORT_REFERENCE)].map(match => match[1]))];
1058
+ for (const relative of references) {
1059
+ const normalized = path.posix.normalize(relative);
1060
+ if (normalized !== relative || normalized.startsWith('../') || path.isAbsolute(normalized)) {
1061
+ throw new Error(`unsafe skill support reference '${relative}' in ${sourceSkillMd}`);
1062
+ }
1063
+ const candidates = [path.join(sourceSkillDir, normalized), path.join(bundlePath, normalized)];
1064
+ let source;
1065
+ for (const candidate of candidates) {
1066
+ try {
1067
+ const stat = await fs.lstat(candidate);
1068
+ if (stat.isFile() && !stat.isSymbolicLink()) {
1069
+ source = candidate;
1070
+ break;
1071
+ }
1072
+ }
1073
+ catch { /* try bundle-root fallback */ }
1074
+ }
1075
+ if (!source)
1076
+ throw new Error(`missing skill support asset '${relative}' referenced by ${sourceSkillMd}`);
1077
+ let deployedSkillRoot;
1078
+ for (const root of deployRoots) {
1079
+ // The deployer may select the bulk or kernel tier; use the tier that
1080
+ // actually contains this skill's transformed SKILL.md.
1081
+ if (await fileExists(path.join(root, skillName, 'SKILL.md'))) {
1082
+ deployedSkillRoot = root;
1083
+ break;
1084
+ }
1085
+ }
1086
+ if (!deployedSkillRoot)
1087
+ throw new Error(`deployed skill '${skillName}' not found while reconciling support assets`);
1088
+ const destination = path.join(deployedSkillRoot, skillName, ...normalized.split('/'));
1089
+ await fs.mkdir(path.dirname(destination), { recursive: true });
1090
+ await fs.copyFile(source, destination);
1091
+ const mode = (await fs.stat(source)).mode & 0o777;
1092
+ await fs.chmod(destination, mode);
1093
+ }
1094
+ }
1095
+ }
1008
1096
  /**
1009
1097
  * Deploy a single project-local bundle to one provider via deploy-agents.mjs.
1010
1098
  * Runs the same script and flags used for upstream addons, with the bundle
@@ -1072,6 +1160,15 @@ async function deployOneProjectLocalBundle(opts) {
1072
1160
  env: { AIWG_ROOT: frameworkRoot },
1073
1161
  });
1074
1162
  exitCode = result.exitCode;
1163
+ if (exitCode === 0 && !dryRun) {
1164
+ try {
1165
+ await reconcileProjectLocalSkillAssets(bundle.artifactPath, target, provider);
1166
+ }
1167
+ catch (error) {
1168
+ ui.warn(`Project-local skill asset deployment failed for '${bundle.id}': ${error.message}`);
1169
+ exitCode = 1;
1170
+ }
1171
+ }
1075
1172
  }
1076
1173
  if (exitCode === 0 && cliCommandCount > 0) {
1077
1174
  try {
@@ -2017,10 +2114,18 @@ export class UseHandler {
2017
2114
  scope: effectiveScope,
2018
2115
  requestedBundles: [requestedBundle],
2019
2116
  };
2020
- const sourceVerifications = await signedProviderSourceVerifications(receiptOptions);
2021
- await finalizeProviderTransformationReceipt({ ...receiptOptions, sourceVerifications });
2117
+ const sourceResolution = await resolveProviderReceiptSource(receiptOptions);
2118
+ await finalizeProviderTransformationReceipt({ ...receiptOptions, ...sourceResolution });
2022
2119
  }
2023
2120
  catch (error) {
2121
+ await finalizeProviderTransformationReceipt({
2122
+ projectRoot: projectDir,
2123
+ frameworkRoot,
2124
+ provider,
2125
+ scope: effectiveScope,
2126
+ requestedBundles: [requestedBundle],
2127
+ sourceDisposition: 'verification-failed',
2128
+ }).catch(() => undefined);
2024
2129
  originalConsole.warn(`Provider receipt finalization failed for ${provider}: ${error instanceof Error ? error.message : String(error)}`);
2025
2130
  }
2026
2131
  }
@@ -777,6 +777,10 @@ export const updateHandler = {
777
777
  console.log(`${update.message}\n`);
778
778
  }
779
779
  catch (error) {
780
+ if (error.code === 'AIWG_INSTALLATION_DRIFT'
781
+ || error.code === 'AIWG_INSTALLATION_INVALID') {
782
+ return { exitCode: 78, message: error instanceof Error ? error.message : String(error) };
783
+ }
780
784
  console.error(`Warning: Update check failed: ${error instanceof Error ? error.message : String(error)}`);
781
785
  console.log('Continuing with re-deployment...\n');
782
786
  }