@aiwg/cli 2026.10.10 → 2026.10.12

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 (156) hide show
  1. package/agentic/code/providers/model-capabilities.v1.json +78 -14
  2. package/agentic/code/providers/model-catalog.v1.json +62 -12
  3. package/dist/src/artifacts/binding-target-identity.js +51 -0
  4. package/dist/src/artifacts/checksum-manifest.js +12 -0
  5. package/dist/src/artifacts/index-builder.js +11 -13
  6. package/dist/src/artifacts/query-engine.js +5 -11
  7. package/dist/src/cli/handlers/doctor-help.js +32 -0
  8. package/dist/src/cli/handlers/gates.js +19 -18
  9. package/dist/src/cli/handlers/models-help.js +7 -1
  10. package/dist/src/cli/handlers/models.js +3 -3
  11. package/dist/src/cli/handlers/script-runner.js +8 -5
  12. package/dist/src/cli/handlers/sessions-intake.js +104 -0
  13. package/dist/src/cli/handlers/sessions-tail.js +172 -0
  14. package/dist/src/cli/handlers/sessions.js +287 -31
  15. package/dist/src/cli/handlers/skill-lint.js +10 -3
  16. package/dist/src/cli/handlers/steward-format.js +100 -0
  17. package/dist/src/cli/handlers/steward-models.js +167 -0
  18. package/dist/src/cli/handlers/steward.js +14 -248
  19. package/dist/src/cli/handlers/use-bundle-counts.js +61 -0
  20. package/dist/src/cli/handlers/use.js +18 -28
  21. package/dist/src/cli/handlers/utilities.js +1 -24
  22. package/dist/src/config/aiwg-config.js +7 -0
  23. package/dist/src/config/authorization-cli.js +89 -0
  24. package/dist/src/config/cli-help.js +1 -0
  25. package/dist/src/config/cli.js +10 -1
  26. package/dist/src/config/worker-context.js +337 -0
  27. package/dist/src/decision/context-pruning.js +3 -2
  28. package/dist/src/decision/ensemble/contract.js +15 -4
  29. package/dist/src/decision/ensemble-study/artifacts.js +5 -0
  30. package/dist/src/decision/ensemble-study/score.js +4 -3
  31. package/dist/src/decision/feature-export.js +3 -9
  32. package/dist/src/decision/heldout/collector.js +2 -2
  33. package/dist/src/decision/issue-triage/runtime.js +3 -9
  34. package/dist/src/decision/qualification/gates.js +2 -1
  35. package/dist/src/decision/qualification/release.js +2 -3
  36. package/dist/src/decision/routing/report.js +2 -0
  37. package/dist/src/decision/sdlc-screening.js +3 -5
  38. package/dist/src/decision/sensitivity/comparative.js +5 -3
  39. package/dist/src/gates/driver.js +6 -77
  40. package/dist/src/gates/evaluate.js +4 -11
  41. package/dist/src/gates/floors.js +4 -4
  42. package/dist/src/gates/index.js +3 -3
  43. package/dist/src/gates/lattice.js +26 -0
  44. package/dist/src/gates/registry.js +2 -1
  45. package/dist/src/gates/report.js +19 -7
  46. package/dist/src/issues/audit-cli.js +120 -0
  47. package/dist/src/issues/audit-contract.js +64 -0
  48. package/dist/src/issues/audit-local.js +82 -0
  49. package/dist/src/issues/audit.js +220 -0
  50. package/dist/src/lint/rule-activation.js +15 -1
  51. package/dist/src/mcp/tools/worker-invoke.mjs +108 -10
  52. package/dist/src/mission-protocol/canonical-codecs.js +270 -0
  53. package/dist/src/mission-protocol/codecs.js +22 -550
  54. package/dist/src/mission-protocol/health-trace-codecs.js +281 -0
  55. package/dist/src/mission-protocol/health-trace-types.js +43 -0
  56. package/dist/src/mission-protocol/types.js +2 -42
  57. package/dist/src/mission-protocol/worker-contract-json.js +10 -0
  58. package/dist/src/mission-protocol/worker-review-gate.js +100 -0
  59. package/dist/src/mission-protocol/worker-review-types.js +2 -0
  60. package/dist/src/models/catalog-policy.js +280 -0
  61. package/dist/src/models/index.js +1 -1
  62. package/dist/src/models/model-capabilities.v1.json +78 -14
  63. package/dist/src/models/model-catalog.v1.json +62 -12
  64. package/dist/src/models/policy-resources.js +28 -0
  65. package/dist/src/models/provider-policy.js +10 -539
  66. package/dist/src/models/resolver.js +26 -8
  67. package/dist/src/models/router.js +15 -2
  68. package/dist/src/models/tiers.js +13 -0
  69. package/dist/src/models/types.js +0 -6
  70. package/dist/src/models/worker-ceilings.js +59 -0
  71. package/dist/src/models/worker-invocation-policy.js +243 -0
  72. package/dist/src/models/worker-plan.js +16 -20
  73. package/dist/src/models/worker-review-lineage.js +55 -0
  74. package/dist/src/models/worker-selection-policy.js +1 -1
  75. package/dist/src/models/wrapper-route.js +6 -3
  76. package/dist/src/policy/authorization.js +30 -1
  77. package/dist/src/policy/hitl-config.js +63 -0
  78. package/dist/src/policy/hitl-receipt.js +57 -0
  79. package/dist/src/policy/hitl-types.js +4 -0
  80. package/dist/src/policy/hitl-validation.js +39 -0
  81. package/dist/src/policy/hitl.js +117 -0
  82. package/dist/src/process/aiwg-probe-command.d.mts +15 -0
  83. package/dist/src/process/aiwg-probe-command.mjs +37 -0
  84. package/dist/src/rules/activation.d.mts +1 -1
  85. package/dist/src/rules/activation.mjs +0 -2
  86. package/dist/src/rules/gate-coverage.js +51 -0
  87. package/dist/src/rules/gate-references.js +16 -0
  88. package/dist/src/rules/rule-frontmatter.schema.json +7 -1
  89. package/dist/src/serve/worker-command-request.js +21 -4
  90. package/dist/src/serve/worker-invocation-preparation.js +3 -1
  91. package/dist/src/serve/worker-invocation.js +21 -10
  92. package/dist/src/sessions/adapters/deepseek-harness.js +1 -0
  93. package/dist/src/sessions/adapters/grok-build-native.js +269 -0
  94. package/dist/src/sessions/adapters/grok-build.js +57 -15
  95. package/dist/src/sessions/adapters/grokbot.js +7 -0
  96. package/dist/src/sessions/adapters/hermes-state-db.js +252 -0
  97. package/dist/src/sessions/adapters/hermes.js +42 -3
  98. package/dist/src/sessions/adapters/warp.js +1 -1
  99. package/dist/src/sessions/batch-import.js +44 -6
  100. package/dist/src/sessions/contracts.js +20 -1
  101. package/dist/src/sessions/coverage-preflight.js +7 -1
  102. package/dist/src/sessions/discovery-capability.js +38 -0
  103. package/dist/src/sessions/fortemi-export-mapping.js +33 -3
  104. package/dist/src/sessions/host-catalog.js +157 -0
  105. package/dist/src/sessions/image-blobs.js +141 -0
  106. package/dist/src/sessions/importer.js +9 -232
  107. package/dist/src/sessions/index.js +6 -0
  108. package/dist/src/sessions/intake-dir.js +71 -0
  109. package/dist/src/sessions/normalization.js +251 -0
  110. package/dist/src/sessions/refresh.js +25 -2
  111. package/dist/src/sessions/repository.js +164 -22
  112. package/dist/src/sessions/tail.js +91 -0
  113. package/dist/src/sessions/workspace-discovery.js +88 -9
  114. package/dist/src/skills/agent-skill-sidecar.js +28 -0
  115. package/dist/src/skills/agent-skills.js +4 -23
  116. package/dist/src/skills/claude-surface-projection.js +25 -0
  117. package/dist/src/skills/deployer.js +8 -288
  118. package/dist/src/skills/deployment-policy.js +114 -0
  119. package/dist/src/skills/deployment-projection.js +152 -0
  120. package/dist/src/skills/deployment-types.js +20 -0
  121. package/dist/src/skills/exporter.js +8 -31
  122. package/dist/src/skills/provider-extensions.js +41 -0
  123. package/dist/src/skills/run.js +6 -0
  124. package/dist/src/skills/types.js +0 -8
  125. package/dist/src/skills/validator-resources.js +68 -0
  126. package/dist/src/skills/validator.js +6 -55
  127. package/dist/src/storage/cli-migrate-options.js +125 -0
  128. package/dist/src/storage/cli-migrate.js +111 -0
  129. package/dist/src/storage/cli.js +4 -279
  130. package/dist/src/storage/migration-adapter.js +79 -0
  131. package/dist/src/storage/migration-copy.js +108 -0
  132. package/dist/src/storage/migration-protocol.js +14 -197
  133. package/dist/src/storage/migration-records.js +186 -0
  134. package/dist/src/storage/migration-types.js +12 -0
  135. package/dist/src/tracker/forge-http.js +2 -1
  136. package/dist/src/tracker/issue-pages.js +110 -0
  137. package/package.json +7 -1
  138. package/schemas/mission-v1.schema.json +335 -0
  139. package/schemas/models/model-catalog.v1.schema.json +19 -1
  140. package/schemas/models/model-config-compatibility.v1.schema.json +3 -2
  141. package/schemas/models/model-policy.v1.schema.json +1 -1
  142. package/tools/agents/providers/provider-paths-staging.mjs +35 -0
  143. package/tools/agents/providers/rule-binding-targets.mjs +146 -0
  144. package/tools/agents/providers/rule-delivery.mjs +7 -126
  145. package/tools/ralph-external/lib/muse-adapter.mjs +10 -0
  146. package/tools/ralph-external/lib/worker-context.mjs +237 -0
  147. package/tools/ralph-external/lib/worker-invocation-ancestry.mjs +132 -0
  148. package/tools/ralph-external/lib/worker-invocation-processes.mjs +123 -0
  149. package/tools/ralph-external/lib/worker-isolation-status.mjs +48 -0
  150. package/tools/ralph-external/lib/worker-isolation.mjs +17 -29
  151. package/tools/ralph-external/lib/worker-launch-cli.mjs +3 -1
  152. package/tools/ralph-external/lib/worker-overflow.mjs +48 -13
  153. package/tools/ralph-external/lib/worker-retry-evidence.mjs +103 -0
  154. package/tools/ralph-external/lib/worker-termination.mjs +82 -0
  155. package/tools/ralph-external/session-launcher.mjs +51 -96
  156. package/vscode-extension/schemas/aiwg.config.v1.json +177 -0
@@ -178,6 +178,13 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
178
178
  import { mcpError, mcpJson } from '../helpers.mjs';
179
179
  import { buildShellStopControl, waitForUnitTermination } from './worker-shell-control.mjs';
180
180
  import { launchPreparedShellWorker, sweepWorkerStdin } from './worker-stdin-runtime.mjs';
181
+ // NOTE: no static import of ../../config/worker-context.js here. That
182
+ // module is TypeScript: a literal relative import resolves under vitest
183
+ // but throws ERR_MODULE_NOT_FOUND under raw node (e.g. `node
184
+ // src/mcp/cli.mjs`, which test/unit/mcp exercises directly). The
185
+ // request-time loader below resolves it through importImpl (dist when
186
+ // built, source otherwise), so merely loading this bridge never touches
187
+ // it. See tools/_resolve-impl.mjs (#3062).
181
188
 
182
189
  const execFileAsync = promisify(execFile);
183
190
 
@@ -642,6 +649,64 @@ function describeGateFailures(failures) {
642
649
  return `worker dispatch gate blocked: ${failures.map(f => `${f.signal} (${f.reason})`).join('; ')}`;
643
650
  }
644
651
 
652
+ /** Request-time loader for the TypeScript worker-context module (see the
653
+ * import note above): resolved through importImpl so production uses the
654
+ * compiled `dist` tree while a checkout falls back to source. Cached for
655
+ * the process lifetime; only request paths that actually ensure worker
656
+ * context ever trigger the load. */
657
+ let cachedWorkerContextModule = null;
658
+ async function loadWorkerContextModule() {
659
+ if (!cachedWorkerContextModule) cachedWorkerContextModule = await importImpl('config/worker-context.js');
660
+ return cachedWorkerContextModule;
661
+ }
662
+
663
+ /** Map the worker CLI to its provider and ensure its bootstrap is present
664
+ * in the worker workdir (#3685 round 2). Deploys the resolved union set
665
+ * (config providers plus orchestration CLIs) in one pass when the worker
666
+ * provider's bootstrap is missing; an unmappable CLI refuses with the
667
+ * typed diagnostic, since no deploy could target it. */
668
+ async function ensureWorkerContextForInvocation({ invocation, config }) {
669
+ const { ensureWorkerContextBootstrap, mapCliToProviderId, resolveWorkerContextProvidersFromConfig, WorkerContextBootstrapError } =
670
+ await loadWorkerContextModule();
671
+ const cli = invocation?.selected?.cli;
672
+ const mapped = mapCliToProviderId(cli, config?.parallelism);
673
+ if (!('provider' in mapped) || !mapped.provider) {
674
+ throw new WorkerContextBootstrapError(
675
+ `cannot ensure worker context: CLI '${cli}' maps to no AIWG provider (${mapped.skipped ?? 'unmapped'})`,
676
+ `configure parallelism.clis.${cli} with a providers:<id> adapter, or dispatch a CLI whose adapter maps to a known provider`,
677
+ { workdir: invocation?.cwd },
678
+ );
679
+ }
680
+ let providers = [mapped.provider];
681
+ try {
682
+ providers = [...new Set([...resolveWorkerContextProvidersFromConfig(config).providers, mapped.provider])];
683
+ } catch {
684
+ providers = [mapped.provider];
685
+ }
686
+ return ensureWorkerContextBootstrap({ workdir: invocation?.cwd, provider: mapped.provider, providers });
687
+ }
688
+
689
+ /** Refuse a failed bootstrap ensure as a typed blocked diagnostic that
690
+ * names the remedy — never a bare string the operator cannot act on.
691
+ * `code`/`remedy`/`provider`/`workdir` survive on any error object that
692
+ * carries them (the tools-side ensure throws a duck-typed refusal, not
693
+ * necessarily this module's class instance); only genuinely bare failures
694
+ * fall back to the generic code and remedy. */
695
+ function toWorkerContextDiagnostic(error) {
696
+ const fields = (error !== null && typeof error === 'object') ? error : {};
697
+ // Duck-typed on purpose: the worker-context module loads lazily (see
698
+ // above), so no class identity is available at module scope. Every
699
+ // refusal path sets `code`/`remedy` on the thrown object instead.
700
+ const code = (typeof fields.code === 'string' && fields.code ? fields.code : 'OG_WORKER_CONTEXT_UNAVAILABLE');
701
+ const remedy = (typeof fields.remedy === 'string' && fields.remedy ? fields.remedy
702
+ : 'ensure the worker CLI maps to a known AIWG provider and the aiwg CLI is installed (AIWG_BIN), then retry the run');
703
+ return { code,
704
+ message: error instanceof Error ? error.message : String(error?.message ?? error),
705
+ remedy,
706
+ ...(fields.provider !== undefined ? { provider: fields.provider } : {}),
707
+ ...(fields.workdir !== undefined ? { workdir: fields.workdir } : {}) };
708
+ }
709
+
645
710
  // Audited observer; refresh the historical baseline graph after rebase.
646
711
  const waitForUnitTerminationBaseline = waitForUnitTermination;
647
712
  export { waitForUnitTerminationBaseline as __testOnlyWaitForUnitTerminationBaseline };
@@ -758,6 +823,7 @@ export class ChildProcessTransport {
758
823
  // cap since a long-lived connection could otherwise grow it unbounded.
759
824
  this._rawResponses = new Map();
760
825
  this._buffer = undefined;
826
+ this._bufferBytes = 0;
761
827
  }
762
828
 
763
829
  async start() {
@@ -769,7 +835,7 @@ export class ChildProcessTransport {
769
835
  try {
770
836
  // Check each frame BEFORE concatenation or UTF-8 decoding.
771
837
  let offset = 0;
772
- let frameBytes = this._buffer?.length ?? 0;
838
+ let frameBytes = this._bufferBytes;
773
839
  while (offset < chunk.length) {
774
840
  const newline = chunk.indexOf(10, offset);
775
841
  const end = newline === -1 ? chunk.length : newline;
@@ -782,8 +848,7 @@ export class ChildProcessTransport {
782
848
  frameBytes = 0;
783
849
  offset = newline + 1;
784
850
  }
785
- this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
786
- this._drain();
851
+ this._drain(chunk);
787
852
  } catch (error) { this.onerror?.(error); }
788
853
  });
789
854
  child.stdout?.on('error', error => this.onerror?.(error));
@@ -799,6 +864,7 @@ export class ChildProcessTransport {
799
864
  if (this._closed) return;
800
865
  this._closed = true;
801
866
  this._buffer = undefined;
867
+ this._bufferBytes = 0;
802
868
  this._rawResponses.clear();
803
869
  this.onclose?.();
804
870
  }
@@ -807,6 +873,7 @@ export class ChildProcessTransport {
807
873
  if (this._failure) return;
808
874
  this._failure = Object.assign(new Error(code), { code });
809
875
  this._buffer = undefined;
876
+ this._bufferBytes = 0;
810
877
  this._rawResponses.clear();
811
878
  // Start stopping before pipe closure can exit the leader; onclose rejects
812
879
  // pending SDK requests immediately while termination proof is awaited.
@@ -825,14 +892,26 @@ export class ChildProcessTransport {
825
892
  // (own-property-preserving, retained in `_rawResponses` — see
826
893
  // constructor doc) and once through the SDK's own `JSONRPCMessageSchema`
827
894
  // (routed to `onmessage`, unchanged behavior for the Client/Protocol).
828
- _drain() {
829
- while (this._buffer) {
830
- const index = this._buffer.indexOf('\n');
831
- if (index === -1) return;
832
- const line = this._buffer.toString('utf8', 0, index).replace(/\r$/, '');
833
- this._buffer = this._buffer.subarray(index + 1);
895
+ _drain(chunk) {
896
+ // Retain fragments without copying or rescanning the growing partial
897
+ // frame on every pipe read. Join once, only when a newline arrives.
898
+ let offset = 0;
899
+ while (offset < chunk.length) {
900
+ const newline = chunk.indexOf(10, offset);
901
+ const end = newline === -1 ? chunk.length : newline;
902
+ if (end > offset) {
903
+ (this._buffer ??= []).push(chunk.subarray(offset, end));
904
+ this._bufferBytes += end - offset;
905
+ }
906
+ if (newline === -1) return;
907
+ const frame = this._buffer?.length === 1
908
+ ? this._buffer[0] : Buffer.concat(this._buffer ?? [], this._bufferBytes);
909
+ const line = frame.toString('utf8').replace(/\r$/, '');
910
+ this._buffer = undefined;
911
+ this._bufferBytes = 0;
834
912
  this._handleLine(line);
835
- if (this._buffer?.length === 0) this._buffer = undefined;
913
+ if (this._closed || this._failure) return;
914
+ offset = newline + 1;
836
915
  }
837
916
  }
838
917
 
@@ -905,6 +984,7 @@ export class ChildProcessTransport {
905
984
  try { signalChild(child, 'SIGKILL'); } catch { /* already gone */ }
906
985
  }
907
986
  this._buffer = undefined;
987
+ this._bufferBytes = 0;
908
988
  }
909
989
  }
910
990
 
@@ -1745,6 +1825,24 @@ export async function registerWorkerInvokeToolset(server, options = {}) {
1745
1825
  }
1746
1826
  const { root, snapshot, drivers, invocation } = prefix;
1747
1827
 
1828
+ // #3685 round 2: the worker CLI's AIWG bootstrap must be present in
1829
+ // the worker workdir before dispatch — a context-free worker would run
1830
+ // with no project rules/skills/hooks. Present: no-op. Missing: exactly
1831
+ // one `aiwg use all` pass into the workdir (never elsewhere). A failed
1832
+ // ensure refuses as `blocked` with the typed diagnostic (never a
1833
+ // launch). Default on; `ensureWorkerContext: false` skips (synthetic
1834
+ // tests), a function value replaces the ensure.
1835
+ if (options.ensureWorkerContext !== false) {
1836
+ try {
1837
+ const ensure = typeof options.ensureWorkerContext === 'function'
1838
+ ? options.ensureWorkerContext
1839
+ : ensureWorkerContextForInvocation;
1840
+ await ensure({ invocation, config: snapshot.config });
1841
+ } catch (error) {
1842
+ return markTerminal({ status: 'blocked', termination: 'confirmed', diagnostics: [toWorkerContextDiagnostic(error)] });
1843
+ }
1844
+ }
1845
+
1748
1846
  // =========================================================================
1749
1847
  // #2915 round 8 (D6): ONE decision, immediately after the prefix, from
1750
1848
  // the launcher's own pure eligibility predicate,
@@ -0,0 +1,270 @@
1
+ // Canonical Mission projections and legacy transport codecs.
2
+ import { assertMissionWorkerHandoff } from './codecs.js';
3
+ import { MISSION_API_VERSION } from './types.js';
4
+ const TERMINAL = new Set(['completed', 'failed', 'incomplete', 'cancelled']);
5
+ function object(value, label = 'Mission input') {
6
+ if (!value || typeof value !== 'object' || Array.isArray(value))
7
+ throw new Error(`${label} must be an object.`);
8
+ return value;
9
+ }
10
+ function string(value) {
11
+ return typeof value === 'string' && value.length ? value : undefined;
12
+ }
13
+ function requiredId(record) {
14
+ const metadata = record.metadata && typeof record.metadata === 'object' ? record.metadata : {};
15
+ const id = string(record.missionId) ?? string(record.mission_id) ?? string(record.id)
16
+ ?? string(record.taskId) ?? string(record.task_id) ?? string(record.responseId) ?? string(metadata.id);
17
+ if (!id)
18
+ throw new Error('Mission source is missing a stable identifier.');
19
+ return id;
20
+ }
21
+ export function normalizeMissionState(nativeState) {
22
+ const state = String(nativeState ?? 'unknown').toLowerCase().replace(/_/g, '-');
23
+ if (['done', 'completed', 'succeeded', 'success'].includes(state))
24
+ return 'completed';
25
+ if (['aborted', 'cancelled', 'canceled'].includes(state))
26
+ return 'cancelled';
27
+ if (['failed', 'error', 'rejected', 'timed-out'].includes(state))
28
+ return 'failed';
29
+ if (['incomplete', 'budget-exhausted', 'max-iterations', 'max-tokens'].includes(state))
30
+ return 'incomplete';
31
+ if (['running', 'in-progress', 'working', 'started', 'assigned', 'admitted', 'starting'].includes(state))
32
+ return 'running';
33
+ if (['pending', 'submitted', 'queued', 'runnable', 'scheduled'].includes(state))
34
+ return 'pending';
35
+ if (['blocked', 'blocked-hitl', 'input-required', 'auth-required', 'paused', 'suspended'].includes(state))
36
+ return 'blocked';
37
+ if (['operator-review', 'operator-review-required', 'manual-review'].includes(state))
38
+ return 'operator-review';
39
+ if (['unknown', 'disconnected', 'detached', 'unreachable'].includes(state))
40
+ return 'unknown';
41
+ return 'unknown';
42
+ }
43
+ function knownKeysFor(source) {
44
+ const common = ['id', 'missionId', 'mission_id', 'taskId', 'task_id', 'responseId', 'title', 'goal', 'objective', 'prompt', 'completion', 'completionCriterion', 'completion_criterion', 'status', 'state', 'nativeState', 'createdAt', 'created_at', 'updatedAt', 'updated_at', 'metadata', 'spec', 'provenance', 'extensions', 'artifacts', 'output', 'partialOutput', 'error', 'checkpoint', 'cycles', 'activityLog', 'totalCost', 'runtimesUsed', 'apiVersion', 'api_version', 'schemaVersion', 'schema_version', 'kind', 'session_id', 'previous_response_id'];
45
+ if (source === 'uhp-2026-08-11')
46
+ common.push('object', 'model', 'incomplete_details', 'store', 'usage');
47
+ return new Set(common);
48
+ }
49
+ function extensions(record, source) {
50
+ const explicit = record.extensions && typeof record.extensions === 'object' ? structuredClone(record.extensions) : {};
51
+ if (record.metadata && typeof record.metadata === 'object')
52
+ explicit.metadata = structuredClone(record.metadata);
53
+ if (record.status && typeof record.status === 'object')
54
+ explicit.nativeStatus = structuredClone(record.status);
55
+ for (const [key, value] of Object.entries(record))
56
+ if (!knownKeysFor(source).has(key))
57
+ explicit[key] = structuredClone(value);
58
+ return explicit;
59
+ }
60
+ function artifacts(record) {
61
+ const values = Array.isArray(record.artifacts) ? record.artifacts : [];
62
+ return values.flatMap((item, index) => {
63
+ if (!item || typeof item !== 'object')
64
+ return [];
65
+ const artifact = item;
66
+ const id = string(artifact.id) ?? string(artifact.fileId) ?? string(artifact.file_id) ?? string(artifact.uri) ?? `artifact-${index}`;
67
+ return [{
68
+ id,
69
+ kind: string(artifact.kind) ?? 'other',
70
+ ...(string(artifact.uri) ? { uri: string(artifact.uri) } : {}),
71
+ ...(string(artifact.sha256) ? { sha256: string(artifact.sha256) } : {}),
72
+ ...(string(artifact.mediaType) ?? string(artifact.media_type) ? { mediaType: string(artifact.mediaType) ?? string(artifact.media_type) } : {}),
73
+ extensions: structuredClone(artifact),
74
+ }];
75
+ });
76
+ }
77
+ function uhpOutputArtifacts(record) {
78
+ const found = new Map();
79
+ function visit(value) {
80
+ if (Array.isArray(value)) {
81
+ value.forEach(visit);
82
+ return;
83
+ }
84
+ if (!value || typeof value !== 'object')
85
+ return;
86
+ const item = value;
87
+ const id = string(item.file_id);
88
+ if (id)
89
+ found.set(id, { id, kind: 'uhp-file', ...(string(item.media_type) ? { mediaType: string(item.media_type) } : {}), extensions: { 'uhp.file': structuredClone(item) } });
90
+ Object.values(item).forEach(visit);
91
+ }
92
+ visit(record.output);
93
+ return [...found.values()];
94
+ }
95
+ function nativeStateFor(source, record, statusRecord) {
96
+ const explicit = string(statusRecord.state) ?? string(record.nativeState) ?? string(record.status)
97
+ ?? string(record.state) ?? string(record.observed_state) ?? string(record.observedState) ?? string(record.nodeState);
98
+ if (explicit)
99
+ return explicit;
100
+ if (source === 'mission-ledger' && record.checkpoint && typeof record.checkpoint === 'object') {
101
+ const checkpoint = record.checkpoint;
102
+ if (Array.isArray(checkpoint.pending) && checkpoint.pending.length)
103
+ return 'running';
104
+ if (Array.isArray(checkpoint.failed) && checkpoint.failed.length)
105
+ return 'failed';
106
+ if (Array.isArray(checkpoint.completed) && checkpoint.completed.length)
107
+ return 'completed';
108
+ }
109
+ return 'unknown';
110
+ }
111
+ function sourceVersion(source, record) {
112
+ if (source === 'canonical')
113
+ return string(record.apiVersion) ?? 'unknown';
114
+ if (source === 'uhp-2026-08-11')
115
+ return '2026-08-11';
116
+ if (source === 'executor-v1')
117
+ return 'executor.aiwg.io/v1';
118
+ if (source === 'fleet-workload-v1')
119
+ return 'fleet-workload/v1';
120
+ if (source === 'graph-flow-v1')
121
+ return 'graph.flow.aiwg.io/v1';
122
+ if (source === 'activity-v1')
123
+ return 'activity.aiwg.io/v1';
124
+ return string(record.apiVersion) ?? string(record.api_version) ?? string(record.schemaVersion) ?? string(record.schema_version) ?? 'unversioned';
125
+ }
126
+ function assertSupported(version) {
127
+ const supported = new Set(['unversioned', MISSION_API_VERSION, '2026-08-11', 'executor.aiwg.io/v1', 'fleet-workload/v1', 'graph.flow.aiwg.io/v1', 'activity.aiwg.io/v1']);
128
+ if (supported.has(version))
129
+ return;
130
+ const major = version.match(/(?:^|\/)v(\d+)(?:$|[.-])/)?.[1];
131
+ if (major && major !== '1')
132
+ throw new Error(`Unsupported Mission source major version '${version}'; supported major is v1.`);
133
+ throw new Error(`Unsupported Mission source version '${version}'.`);
134
+ }
135
+ export function validateCanonicalMission(value) {
136
+ const record = object(value, 'Canonical Mission');
137
+ assertMissionWorkerHandoff(record, 'canonical');
138
+ if (record.apiVersion !== MISSION_API_VERSION)
139
+ throw new Error(`Unsupported canonical Mission version '${String(record.apiVersion)}'.`);
140
+ if (record.kind !== 'Mission')
141
+ throw new Error("Canonical Mission kind must be 'Mission'.");
142
+ const metadata = object(record.metadata, 'Mission metadata');
143
+ const spec = object(record.spec, 'Mission spec');
144
+ const status = object(record.status, 'Mission status');
145
+ const provenance = object(record.provenance, 'Mission provenance');
146
+ if (!string(metadata.id) || !string(spec.objective) || !string(provenance.sourceContract) || !string(provenance.sourceVersion))
147
+ throw new Error('Canonical Mission is missing required identity, objective, or provenance.');
148
+ const state = normalizeMissionState(status.state);
149
+ if (status.state !== state)
150
+ throw new Error(`Canonical Mission state '${String(status.state)}' is not normalized.`);
151
+ if (status.terminal !== TERMINAL.has(state))
152
+ throw new Error(`Canonical Mission terminal flag contradicts state '${state}'.`);
153
+ if (!Array.isArray(status.artifacts))
154
+ throw new Error('Canonical Mission artifacts must be an array.');
155
+ return structuredClone(value);
156
+ }
157
+ export function decodeMission(input, source, workerConsumer) {
158
+ const record = object(input);
159
+ assertMissionWorkerHandoff(record, source, workerConsumer);
160
+ if (source === 'canonical') {
161
+ const value = validateCanonicalMission(record);
162
+ return { value, sourceVersion: MISSION_API_VERSION, warnings: [], preservedExtensions: structuredClone(value.extensions ?? {}), lossReport: [] };
163
+ }
164
+ const version = sourceVersion(source, record);
165
+ assertSupported(version);
166
+ const statusRecord = record.status && typeof record.status === 'object' ? record.status : {};
167
+ const nativeState = nativeStateFor(source, record, statusRecord);
168
+ const state = normalizeMissionState(nativeState);
169
+ const objective = string(record.goal) ?? string(record.objective) ?? string(record.title) ?? string(record.prompt) ?? '(legacy mission objective unavailable)';
170
+ const completionCriterion = string(record.completionCriterion) ?? string(record.completion_criterion) ?? string(record.completion);
171
+ const preserved = extensions(record, source);
172
+ const warnings = [];
173
+ const lossReport = [];
174
+ if (objective.startsWith('(legacy'))
175
+ warnings.push('Source did not contain a mission objective; placeholder retained.');
176
+ if (state === 'unknown' && nativeState !== 'unknown')
177
+ warnings.push(`Unknown native state '${nativeState}' preserved beside normalized unknown state.`);
178
+ const sourceArtifacts = artifacts(record);
179
+ if (source === 'uhp-2026-08-11') {
180
+ const ids = new Set(sourceArtifacts.map(artifact => artifact.id));
181
+ for (const artifact of uhpOutputArtifacts(record))
182
+ if (!ids.has(artifact.id))
183
+ sourceArtifacts.push(artifact);
184
+ }
185
+ const budgets = record.budgets && typeof record.budgets === 'object' ? record.budgets : undefined;
186
+ const lineage = Array.isArray(record.lineage) ? record.lineage.flatMap(item => {
187
+ if (!item || typeof item !== 'object')
188
+ return [];
189
+ const entry = item;
190
+ return string(entry.relation) && string(entry.id) ? [{ relation: string(entry.relation), id: string(entry.id) }] : [];
191
+ }) : undefined;
192
+ const value = {
193
+ apiVersion: MISSION_API_VERSION,
194
+ kind: 'Mission',
195
+ metadata: {
196
+ id: requiredId(record),
197
+ ...(string(record.createdAt) ?? string(record.created_at) ? { createdAt: string(record.createdAt) ?? string(record.created_at) } : {}),
198
+ ...(string(record.updatedAt) ?? string(record.updated_at) ? { updatedAt: string(record.updatedAt) ?? string(record.updated_at) } : {}),
199
+ ...(string(record.previous_response_id) ? { previousId: string(record.previous_response_id) } : {}),
200
+ ...(lineage?.length ? { lineage } : {}),
201
+ },
202
+ spec: { objective, ...(completionCriterion ? { completionCriterion } : {}), ...(budgets ? { budgets: structuredClone(budgets) } : {}) },
203
+ status: {
204
+ state,
205
+ terminal: TERMINAL.has(state),
206
+ nativeState,
207
+ artifacts: sourceArtifacts,
208
+ ...(record.output !== undefined || record.partialOutput !== undefined || record.partial_output !== undefined ? { partialOutput: structuredClone(record.partialOutput ?? record.partial_output ?? record.output) } : {}),
209
+ },
210
+ provenance: { sourceContract: source, sourceVersion: version, ...(source === 'a2a' || source.startsWith('uhp') ? { transport: source.split('-')[0] } : {}), sourceId: requiredId(record) },
211
+ ...(Object.keys(preserved).length ? { extensions: { [`aiwg.source.${source}`]: preserved } } : {}),
212
+ };
213
+ return { value, sourceVersion: version, warnings, preservedExtensions: preserved, lossReport };
214
+ }
215
+ function projectionLosses(value, target) {
216
+ const losses = [];
217
+ if (value.status.verification?.length)
218
+ losses.push({ path: '/status/verification', reason: `${target} has no native verification ledger`, severity: 'warning' });
219
+ if (value.metadata.lineage?.length && !['mission-ledger', 'fleet-workload-v1', 'canonical'].includes(target))
220
+ losses.push({ path: '/metadata/lineage', reason: `${target} cannot represent full lineage`, severity: 'required' });
221
+ if (value.spec.budgets && !['fleet-workload-v1', 'canonical'].includes(target))
222
+ losses.push({ path: '/spec/budgets', reason: `${target} cannot represent every canonical budget`, severity: 'warning' });
223
+ return losses;
224
+ }
225
+ export function encodeMission(value, target, options = {}) {
226
+ assertMissionWorkerHandoff(value, target, options.workerConsumer);
227
+ const mission = validateCanonicalMission(value);
228
+ if (target === 'canonical')
229
+ return { value: mission, targetVersion: MISSION_API_VERSION, warnings: [], lossReport: [] };
230
+ const lossReport = projectionLosses(mission, target);
231
+ const required = lossReport.filter(loss => loss.severity === 'required');
232
+ if (required.length && !options.allowLoss)
233
+ throw new Error(`Projection to ${target} would silently lose required semantics: ${required.map(loss => loss.path).join(', ')}.`);
234
+ const base = {
235
+ missionId: mission.metadata.id,
236
+ goal: mission.spec.objective,
237
+ completionCriterion: mission.spec.completionCriterion,
238
+ status: mission.status.nativeState ?? mission.status.state,
239
+ artifacts: structuredClone(mission.status.artifacts),
240
+ partialOutput: structuredClone(mission.status.partialOutput),
241
+ lineage: structuredClone(mission.metadata.lineage ?? []),
242
+ extensions: {
243
+ 'aiwg.mission.canonical': { apiVersion: mission.apiVersion, state: mission.status.state, lossReport },
244
+ ...(mission.extensions ?? {}),
245
+ },
246
+ };
247
+ let projected = base;
248
+ if (target === 'mission-plan')
249
+ projected = { missionId: base.missionId, goal: base.goal, completionCriterion: base.completionCriterion ?? '', cycles: [], extensions: base.extensions };
250
+ else if (target === 'mission-ledger')
251
+ projected = { ...base, activityLog: [], cycles: [], totalCost: 0, checkpoint: { completed: [], pending: [], failed: [] }, runtimesUsed: [] };
252
+ else if (target === 'uhp-2026-08-11')
253
+ projected = { id: mission.provenance.sourceId ?? mission.metadata.id, object: 'response', created_at: 0, status: base.status, model: '', output: mission.status.partialOutput ?? [], metadata: base.extensions };
254
+ else if (target === 'a2a')
255
+ projected = { id: mission.provenance.sourceId ?? mission.metadata.id, status: { state: base.status }, artifacts: base.artifacts, partialOutput: base.partialOutput, metadata: base.extensions };
256
+ else if (target === 'graph-flow-v1')
257
+ projected = { schemaVersion: 'graph.flow.aiwg.io/v1', runId: mission.metadata.id, nodeState: mission.status.state, metadata: base.extensions };
258
+ else if (target === 'cockpit')
259
+ projected = { id: mission.metadata.id, session_id: mission.metadata.id, source: mission.provenance.transport ?? mission.provenance.sourceContract, title: mission.spec.objective, completion: mission.spec.completionCriterion, status: mission.status.state, artifacts: base.artifacts, partialOutput: base.partialOutput, extensions: base.extensions };
260
+ else if (target === 'activity-v1')
261
+ projected = { event: 'mission.projected', missionId: mission.metadata.id, objective: mission.spec.objective, state: mission.status.state, metadata: base.extensions };
262
+ else if (target === 'executor-v1')
263
+ projected = { mission_id: mission.metadata.id, objective: mission.spec.objective, completion_criterion: mission.spec.completionCriterion, state: base.status, artifacts: base.artifacts, partial_output: base.partialOutput, metadata: base.extensions };
264
+ else if (target === 'fleet-workload-v1')
265
+ projected = { mission_id: mission.metadata.id, objective: mission.spec.objective, completion_criterion: mission.spec.completionCriterion, observed_state: base.status, budgets: mission.spec.budgets, artifacts: base.artifacts, partial_output: base.partialOutput, lineage: base.lineage, metadata: base.extensions };
266
+ else if (target === 'mission-control-session')
267
+ projected = { id: mission.metadata.id, objective: mission.spec.objective, status: mission.status.state, completion: mission.spec.completionCriterion, artifacts: base.artifacts, partialOutput: base.partialOutput, extensions: base.extensions };
268
+ return { value: projected, targetVersion: target, warnings: lossReport.map(loss => `${loss.path}: ${loss.reason}`), lossReport };
269
+ }
270
+ //# sourceMappingURL=canonical-codecs.js.map