@aiwg/cli 2026.10.5 → 2026.10.7

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 (263) hide show
  1. package/agentic/code/addons/composition-engine/lib/runtime.mjs +1401 -0
  2. package/agentic/code/addons/composition-engine/lib/validator.mjs +513 -0
  3. package/agentic/code/addons/composition-engine/schemas/flow-graph.schema.json +372 -0
  4. package/agentic/code/providers/model-capabilities.v1.json +74 -50
  5. package/bin/aiwg.mjs +7 -3
  6. package/dist/src/a2a/agent-card.js +11 -0
  7. package/dist/src/api/index.d.ts +1 -0
  8. package/dist/src/api/index.js +1 -0
  9. package/dist/src/artifacts/browser-export.js +1 -0
  10. package/dist/src/artifacts/citation-parser.js +8 -1
  11. package/dist/src/artifacts/cli.js +57 -6
  12. package/dist/src/artifacts/fortemi-core-query-adapter.js +2 -0
  13. package/dist/src/artifacts/fortemi-core-sync.js +26 -8
  14. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/contract.json +103 -0
  15. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-embedding-config.schema.json +212 -0
  16. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-embedding-set.schema.json +240 -0
  17. package/dist/src/artifacts/fortemi-shard-contract/2.2.0/full-v1-manifest.schema.json +451 -0
  18. package/dist/src/artifacts/fortemi-shard-export.js +284 -0
  19. package/dist/src/artifacts/graph-query.js +18 -8
  20. package/dist/src/artifacts/index-adapters.js +339 -0
  21. package/dist/src/artifacts/index-builder.js +143 -14
  22. package/dist/src/artifacts/index-files.js +15 -4
  23. package/dist/src/artifacts/index-preparation.js +2 -0
  24. package/dist/src/artifacts/index-reader.js +44 -0
  25. package/dist/src/artifacts/index-refresh.js +114 -55
  26. package/dist/src/artifacts/index-status.js +20 -5
  27. package/dist/src/artifacts/memory-maintenance.js +503 -0
  28. package/dist/src/artifacts/prebuilt-relations.js +3 -1
  29. package/dist/src/artifacts/query-engine.js +263 -108
  30. package/dist/src/artifacts/relations-builder.js +2 -2
  31. package/dist/src/artifacts/source-graph.js +2 -2
  32. package/dist/src/artifacts/types.js +9 -1
  33. package/dist/src/auth/client.js +5 -1
  34. package/dist/src/auth/credential-store.js +5 -1
  35. package/dist/src/auth/sensitive-input.js +151 -0
  36. package/dist/src/channel/manager.mjs +3 -0
  37. package/dist/src/cli/handlers/agentcard.js +47 -25
  38. package/dist/src/cli/handlers/auth.js +22 -2
  39. package/dist/src/cli/handlers/decision.js +22 -3
  40. package/dist/src/cli/handlers/effect.js +4 -84
  41. package/dist/src/cli/handlers/guided-setup.js +470 -0
  42. package/dist/src/cli/handlers/help.js +2 -2
  43. package/dist/src/cli/handlers/housekeeper.js +1110 -0
  44. package/dist/src/cli/handlers/index.js +4 -2
  45. package/dist/src/cli/handlers/init.js +41 -6
  46. package/dist/src/cli/handlers/install.js +6 -0
  47. package/dist/src/cli/handlers/local-executor.js +5 -3
  48. package/dist/src/cli/handlers/marketplace.js +15 -4
  49. package/dist/src/cli/handlers/mc.js +509 -53
  50. package/dist/src/cli/handlers/models.js +32 -2
  51. package/dist/src/cli/handlers/ralph-launcher.js +76 -4
  52. package/dist/src/cli/handlers/rules.js +1 -1
  53. package/dist/src/cli/handlers/script-runner.js +73 -2
  54. package/dist/src/cli/handlers/serve.js +5 -3
  55. package/dist/src/cli/handlers/sessions.js +1 -0
  56. package/dist/src/cli/handlers/setup.js +432 -1
  57. package/dist/src/cli/handlers/skill-lint.js +1 -1
  58. package/dist/src/cli/handlers/subcommands.js +118 -8
  59. package/dist/src/cli/handlers/use.js +335 -107
  60. package/dist/src/cli/handlers/workspace.js +3 -1
  61. package/dist/src/cli/prompt-utils.js +81 -0
  62. package/dist/src/cli/router.js +16 -1
  63. package/dist/src/config/aiwg-config.js +42 -0
  64. package/dist/src/config/cli.js +7 -3
  65. package/dist/src/config/decision-config.js +14 -1
  66. package/dist/src/config/gitignore.js +4 -0
  67. package/dist/src/config/native-sandbox-warnings.js +25 -0
  68. package/dist/src/config/workspace.js +2 -1
  69. package/dist/src/contributors/validation.js +6 -5
  70. package/dist/src/decision/adapters/jev.js +20 -31
  71. package/dist/src/decision/admission.js +7 -3
  72. package/dist/src/decision/ask.js +6 -1
  73. package/dist/src/decision/benchmark.js +482 -0
  74. package/dist/src/decision/ledger.js +22 -3
  75. package/dist/src/decision/pricing.js +147 -0
  76. package/dist/src/decision/qualification/gate-evidence.js +23 -18
  77. package/dist/src/decision/qualification/load-staged.js +576 -0
  78. package/dist/src/decision/qualification/load.js +109 -33
  79. package/dist/src/decision/qualification/runner.js +12 -5
  80. package/dist/src/decision/review/service.js +6 -4
  81. package/dist/src/decision/review/validate.js +23 -2
  82. package/dist/src/decision/savings.js +8 -3
  83. package/dist/src/discovery/index.js +492 -0
  84. package/dist/src/discovery/interview.js +238 -0
  85. package/dist/src/discovery/plan-apply.js +567 -0
  86. package/dist/src/discovery/plan-store.js +171 -0
  87. package/dist/src/discovery/plan-types.js +2 -0
  88. package/dist/src/discovery/plan-validation.js +139 -0
  89. package/dist/src/discovery/types.js +2 -0
  90. package/dist/src/effects/host.js +91 -0
  91. package/dist/src/extensions/commands/definitions.js +49 -9
  92. package/dist/src/extensions/manifest.js +71 -44
  93. package/dist/src/extensions/project-local-discovery.js +1 -1
  94. package/dist/src/extensions/project-local-gitignore.js +2 -0
  95. package/dist/src/extensions/project-quickref.js +18 -17
  96. package/dist/src/extensions/validation.js +32 -29
  97. package/dist/src/housekeeper/daemon-config.js +148 -0
  98. package/dist/src/housekeeper/ipc.js +84 -0
  99. package/dist/src/housekeeper/lifecycle.js +148 -0
  100. package/dist/src/housekeeper/platform.js +35 -0
  101. package/dist/src/housekeeper/status.js +70 -0
  102. package/dist/src/housekeeper/units.js +114 -0
  103. package/dist/src/issues/cli.js +16 -5
  104. package/dist/src/issues/live.js +44 -12
  105. package/dist/src/jobs/flow.js +22 -21
  106. package/dist/src/lint/rule-activation.js +20 -5
  107. package/dist/src/lint/runner.js +1 -1
  108. package/dist/src/marketplace/exchange.js +18 -6
  109. package/dist/src/marketplace/provenance.js +3 -2
  110. package/dist/src/mcp/adapters/codex-runtime.js +46 -76
  111. package/dist/src/mcp/cli.mjs +22 -96
  112. package/dist/src/mcp/config-file.mjs +6 -2
  113. package/dist/src/mcp/config-layers.mjs +23 -7
  114. package/dist/src/mcp/grok-build-config.mjs +2 -1
  115. package/dist/src/mcp/muse-native-config.mjs +2 -1
  116. package/dist/src/mcp/profiles.js +18 -31
  117. package/dist/src/mcp/profiles.mjs +18 -32
  118. package/dist/src/mcp/registry.js +7 -27
  119. package/dist/src/mcp/registry.mjs +7 -28
  120. package/dist/src/mcp/server.mjs +34 -1
  121. package/dist/src/mcp/tools/agentic-sandbox.mjs +8 -7
  122. package/dist/src/mcp/tools/command-run.mjs +3 -2
  123. package/dist/src/mcp/tools/decision.mjs +49 -48
  124. package/dist/src/mcp/tools/discovery.mjs +46 -21
  125. package/dist/src/mcp/tools/orchestration.mjs +22 -22
  126. package/dist/src/mcp/tools/subsystems.mjs +9 -8
  127. package/dist/src/mcp/tools/worker-invoke.mjs +30 -6
  128. package/dist/src/mcp/tools/writer-profiles.mjs +1 -1
  129. package/dist/src/memory/audit.js +719 -0
  130. package/dist/src/memory/canonical-context.js +392 -13
  131. package/dist/src/memory/canonical-maintenance.js +95 -0
  132. package/dist/src/memory/cli-result.js +22 -0
  133. package/dist/src/memory/cli.js +176 -0
  134. package/dist/src/memory/context-pack.js +57 -6
  135. package/dist/src/memory/cost.js +512 -0
  136. package/dist/src/memory/firewall-classification.d.mts +7 -0
  137. package/dist/src/memory/firewall-classification.mjs +32 -0
  138. package/dist/src/memory/index.js +8 -0
  139. package/dist/src/memory/line-maintenance.js +68 -0
  140. package/dist/src/memory/maintenance-cli.js +286 -0
  141. package/dist/src/memory/maintenance-contract.js +136 -0
  142. package/dist/src/memory/maintenance-index.js +18 -0
  143. package/dist/src/memory/maintenance-owner.js +2 -0
  144. package/dist/src/memory/maintenance-record.js +138 -0
  145. package/dist/src/memory/maintenance.js +254 -0
  146. package/dist/src/memory/project-registry.js +2 -1
  147. package/dist/src/memory/provider-memory-adapter.js +154 -0
  148. package/dist/src/memory/recommend-cli.js +83 -0
  149. package/dist/src/memory/recommend.js +269 -0
  150. package/dist/src/memory/scheduled.js +81 -0
  151. package/dist/src/memory/taxonomy.js +44 -0
  152. package/dist/src/mission-protocol/codecs.js +19 -9
  153. package/dist/src/mission-protocol/worker-status.js +91 -1
  154. package/dist/src/models/config-loader.js +11 -1
  155. package/dist/src/models/model-capabilities.v1.json +74 -50
  156. package/dist/src/models/provider-policy.js +125 -62
  157. package/dist/src/models/resolver.js +45 -8
  158. package/dist/src/models/worker-installation.d.mts +8 -1
  159. package/dist/src/models/worker-installation.mjs +231 -38
  160. package/dist/src/models/wrapper-route.js +1 -1
  161. package/dist/src/output-modes/registry.js +8 -7
  162. package/dist/src/packages/adapters/git.js +99 -23
  163. package/dist/src/packages/adapters/gitea.js +8 -27
  164. package/dist/src/packages/adapters/github.js +7 -25
  165. package/dist/src/packages/registry.js +4 -4
  166. package/dist/src/plugin/metadata-validator.js +1 -1
  167. package/dist/src/process/cli-command.d.mts +5 -0
  168. package/dist/src/process/cli-command.mjs +42 -0
  169. package/dist/src/providers/deploy-version.d.mts +1 -0
  170. package/dist/src/providers/deploy-version.mjs +17 -0
  171. package/dist/src/providers/provider-definitions.js +19 -16
  172. package/dist/src/providers/skill-ownership.d.mts +11 -2
  173. package/dist/src/providers/skill-ownership.mjs +35 -17
  174. package/dist/src/rules/activation.d.mts +11 -2
  175. package/dist/src/rules/activation.mjs +82 -6
  176. package/dist/src/rules/inventory.js +1 -1
  177. package/dist/src/rules/rule-frontmatter.schema.json +2 -0
  178. package/dist/src/schema/zod-validation.d.mts +6 -0
  179. package/dist/src/schema/zod-validation.mjs +50 -0
  180. package/dist/src/serve/executor-registry.js +297 -20
  181. package/dist/src/serve/executor-token.d.mts +1 -0
  182. package/dist/src/serve/executor-token.mjs +8 -0
  183. package/dist/src/serve/shared-host-scheduler.js +1102 -31
  184. package/dist/src/serve/worker-invocation.js +124 -15
  185. package/dist/src/sessions/adapters/claude.js +19 -18
  186. package/dist/src/sessions/adapters/codex.js +10 -9
  187. package/dist/src/sessions/adapters/copilot.js +14 -13
  188. package/dist/src/sessions/adapters/cursor.js +19 -18
  189. package/dist/src/sessions/adapters/deepseek-harness.js +9 -8
  190. package/dist/src/sessions/adapters/factory.js +11 -10
  191. package/dist/src/sessions/adapters/generic.js +32 -23
  192. package/dist/src/sessions/adapters/grokbot.js +35 -35
  193. package/dist/src/sessions/adapters/hermes.js +20 -19
  194. package/dist/src/sessions/adapters/muse.js +50 -49
  195. package/dist/src/sessions/adapters/omp.js +5 -4
  196. package/dist/src/sessions/adapters/openclaw.js +30 -29
  197. package/dist/src/sessions/adapters/opencode.js +26 -26
  198. package/dist/src/sessions/adapters/openhuman.js +24 -23
  199. package/dist/src/sessions/adapters/pi.js +5 -4
  200. package/dist/src/sessions/adapters/windsurf.js +8 -8
  201. package/dist/src/sessions/analytics.js +10 -9
  202. package/dist/src/sessions/candidates.js +7 -6
  203. package/dist/src/sessions/contracts.js +63 -53
  204. package/dist/src/sessions/output-registration.js +11 -10
  205. package/dist/src/sessions/workspace-discovery.js +3 -4
  206. package/dist/src/smiths/context-pipeline/aiwg-md.js +18 -1
  207. package/dist/src/smiths/context-pipeline/claude-hook.js +46 -10
  208. package/dist/src/smiths/context-pipeline/context-file-safety.js +330 -9
  209. package/dist/src/smiths/context-pipeline/finalization.js +3 -2
  210. package/dist/src/smiths/context-pipeline/generator.js +7 -19
  211. package/dist/src/smiths/context-pipeline/legacy-inject.js +3 -10
  212. package/dist/src/smiths/context-pipeline/managed-hook.js +5 -5
  213. package/dist/src/smiths/context-pipeline/parallelism-section.js +72 -5
  214. package/dist/src/smiths/context-pipeline/workspace-context.js +110 -22
  215. package/dist/src/tracker/project-inputs.js +2 -0
  216. package/dist/src/update/checker.d.mts +4 -0
  217. package/dist/src/update/checker.mjs +14 -0
  218. package/dist/src/update/notifier.mjs +2 -4
  219. package/dist/src/writing/exemplar-selection.js +6 -6
  220. package/dist/src/writing/voice-evaluation.js +24 -23
  221. package/dist/src/writing/writer-migration.js +14 -13
  222. package/dist/src/writing/writer-profile.js +19 -18
  223. package/dist/src/writing/writing-brief.js +18 -17
  224. package/dist/src/writing/writing-receipt.js +47 -46
  225. package/package.json +5 -2
  226. package/schemas/decision/DecisionReview.schema.json +78 -1
  227. package/schemas/mission-v1.schema.json +3 -0
  228. package/tools/agents/deploy-agents.mjs +67 -39
  229. package/tools/agents/providers/antigravity.mjs +6 -1
  230. package/tools/agents/providers/base.mjs +24 -14
  231. package/tools/agents/providers/claude.mjs +6 -3
  232. package/tools/agents/providers/codex.mjs +54 -57
  233. package/tools/agents/providers/context-write.mjs +28 -0
  234. package/tools/agents/providers/copilot.mjs +13 -4
  235. package/tools/agents/providers/cursor.mjs +1 -1
  236. package/tools/agents/providers/deepseek-harness.mjs +1 -1
  237. package/tools/agents/providers/factory.mjs +7 -1
  238. package/tools/agents/providers/grok-build.mjs +18 -2
  239. package/tools/agents/providers/grokbot.mjs +1 -1
  240. package/tools/agents/providers/hermes.mjs +12 -15
  241. package/tools/agents/providers/muse-hooks.mjs +21 -14
  242. package/tools/agents/providers/muse.mjs +7 -2
  243. package/tools/agents/providers/omp.mjs +31 -5
  244. package/tools/agents/providers/opencode.mjs +14 -5
  245. package/tools/agents/providers/openhuman.mjs +1 -1
  246. package/tools/agents/providers/pi.mjs +1 -1
  247. package/tools/agents/providers/provider-prepare-coordinator.mjs +29 -9
  248. package/tools/agents/providers/rule-delivery.mjs +289 -80
  249. package/tools/agents/providers/warp.mjs +4 -1
  250. package/tools/agents/providers/windsurf.mjs +13 -3
  251. package/tools/cli/workspace-status.mjs +5 -0
  252. package/tools/commands/deploy-prompts-codex.mjs +11 -8
  253. package/tools/ralph-external/lib/codex-adapter.mjs +6 -2
  254. package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +26 -0
  255. package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +7 -1
  256. package/tools/ralph-external/lib/muse-adapter.mjs +12 -2
  257. package/tools/ralph-external/lib/recovery-probe.mjs +14 -4
  258. package/tools/ralph-external/lib/worker-health.mjs +1 -1
  259. package/tools/ralph-external/lib/worker-isolation.mjs +65 -9
  260. package/tools/ralph-external/process-monitor.mjs +49 -8
  261. package/tools/ralph-external/session-launcher.mjs +405 -35
  262. package/tools/skills/deploy-skills-codex.mjs +11 -7
  263. package/vscode-extension/schemas/aiwg.config.v1.json +11 -1
@@ -1,6 +1,8 @@
1
1
  import { createHash } from 'node:crypto';
2
2
  import { canonicalJson } from '../../security/artifact-trust.js';
3
3
  import { AdmissionError, DecisionAdmissionRegistry } from '../admission.js';
4
+ import { verifyDecisionStagedLoadRecord } from './load-staged.js';
5
+ export * from './load-staged.js';
4
6
  export function decisionLoadManifestDigest(bytes) {
5
7
  return `sha256:${createHash('sha256').update(bytes).digest('hex')}`;
6
8
  }
@@ -56,7 +58,7 @@ export function decisionLoadPolicy(manifest, workspaceId, principalId) {
56
58
  };
57
59
  }
58
60
  /** Drives each arrival straight through a registry-owned `DecisionAdmissionController`. */
59
- export function admissionControllerLoadDriver(manifest, clock, registry = new DecisionAdmissionRegistry()) {
61
+ export function admissionControllerLoadDriver(manifest, clock, registry = new DecisionAdmissionRegistry(), hooks = {}) {
60
62
  const workspaceId = 'load-harness';
61
63
  for (const principal of manifest.principals)
62
64
  registry.register(decisionLoadPolicy(manifest, workspaceId, principal.id), clock.now);
@@ -68,32 +70,81 @@ export function admissionControllerLoadDriver(manifest, clock, registry = new De
68
70
  attempts += 1;
69
71
  let lease;
70
72
  try {
71
- lease = await controller.acquire({ budgetId: `load-${arrival.ordinal}`, principalId: arrival.principalId,
73
+ const pendingLease = controller.acquire({ budgetId: `load-${arrival.ordinal}`, principalId: arrival.principalId,
72
74
  workspaceId, providerId: arrival.providerId, signal,
73
- estimate: { tokens: arrival.tokens, attempts: 1, batchSize: 1, items: 1, costUsd: 0, retainedWork: 1 },
75
+ estimate: { tokens: arrival.tokens, attempts: 1, batchSize: 1, items: 1, costUsd: hooks.estimateCost?.(arrival) ?? 0, retainedWork: 1 },
74
76
  deadlineEpochMs: clock.now() + manifest.workspace.maxQueueWaitMs + arrival.serviceMs });
77
+ hooks.onStateChange?.();
78
+ lease = await pendingLease;
75
79
  }
76
80
  catch (error) {
77
- if (!(error instanceof AdmissionError))
81
+ if (!(error instanceof AdmissionError)) {
82
+ hooks.onDriverError?.();
83
+ controller.releaseBudget(`load-${arrival.ordinal}`);
78
84
  throw error;
85
+ }
79
86
  controller.releaseBudget(`load-${arrival.ordinal}`);
80
87
  return { attempts, outcome: error.evidence.reason === 'cancelled' ? 'cancelled' : 'shed',
81
88
  admissionReason: error.evidence.reason, queueDelaysMs, settledAtMs: clock.now() };
82
89
  }
83
- queueDelaysMs.push(lease.evidence.queueDelayMs);
84
- const dispatched = await context.dispatch(arrival.serviceMs, signal);
85
- const failed = arrival.failsFirstAttempt && retry === 0;
86
- lease.release({ success: dispatched === 'completed' && !failed });
87
- if (dispatched === 'aborted') {
88
- controller.releaseBudget(`load-${arrival.ordinal}`);
89
- return { attempts, outcome: 'cancelled', queueDelaysMs, settledAtMs: clock.now() };
90
+ try {
91
+ queueDelaysMs.push(lease.evidence.queueDelayMs);
92
+ if (signal.aborted || hooks.beforeDispatch?.(arrival, retry + 1) === false) {
93
+ lease.release();
94
+ controller.releaseBudget(`load-${arrival.ordinal}`);
95
+ return { attempts: retry, outcome: 'cancelled', queueDelaysMs, settledAtMs: clock.now() };
96
+ }
97
+ hooks.onAdmission?.(arrival, retry + 1, lease.evidence);
98
+ let observation;
99
+ let dispatched;
100
+ try {
101
+ if (hooks.backend) {
102
+ observation = await hooks.backend(arrival, signal, context, retry + 1);
103
+ dispatched = signal.aborted ? 'aborted' : 'completed';
104
+ }
105
+ else
106
+ dispatched = await context.dispatch(arrival.serviceMs, signal);
107
+ }
108
+ catch {
109
+ observation = { status: 'error', reason: 'executor-unavailable', actualModel: null, uncertainty: null,
110
+ requestId: null, usage: { inputTokens: null, outputTokens: null, costUsd: null } };
111
+ dispatched = signal.aborted ? 'aborted' : 'completed';
112
+ }
113
+ const failed = observation ? observation.status !== 'success' : arrival.failsFirstAttempt && retry === 0;
114
+ lease.release({ success: dispatched === 'completed' && !failed, ...(observation?.retryAfterMs === undefined ? {} : { retryAfterMs: observation.retryAfterMs }) });
115
+ if (observation)
116
+ hooks.onObservation?.(arrival, retry + 1, lease.evidence, observation);
117
+ if (dispatched === 'aborted') {
118
+ controller.releaseBudget(`load-${arrival.ordinal}`);
119
+ return { attempts, outcome: 'cancelled', queueDelaysMs, settledAtMs: clock.now() };
120
+ }
121
+ if (!failed) {
122
+ controller.releaseBudget(`load-${arrival.ordinal}`);
123
+ return { attempts, outcome: 'completed', queueDelaysMs, settledAtMs: clock.now() };
124
+ }
125
+ if (observation && !['service-error', 'rate-limited', 'network-transient', 'timeout'].includes(observation.reason))
126
+ break;
127
+ if (signal.aborted)
128
+ break;
129
+ if (retry < manifest.arrivalModel.maxRetries) {
130
+ await new Promise(done => {
131
+ const finish = () => { clearTimeout(timer); signal.removeEventListener('abort', finish); done(); };
132
+ const timer = setTimeout(finish, manifest.arrivalModel.retryBackoffMs);
133
+ if (signal.aborted)
134
+ finish();
135
+ else
136
+ signal.addEventListener('abort', finish, { once: true });
137
+ });
138
+ }
90
139
  }
91
- if (!failed) {
140
+ catch (error) {
141
+ hooks.onDriverError?.();
92
142
  controller.releaseBudget(`load-${arrival.ordinal}`);
93
- return { attempts, outcome: 'completed', queueDelaysMs, settledAtMs: clock.now() };
143
+ throw error;
94
144
  }
95
- if (retry < manifest.arrivalModel.maxRetries) {
96
- await new Promise(done => setTimeout(done, manifest.arrivalModel.retryBackoffMs));
145
+ finally {
146
+ lease.release();
147
+ hooks.onStateChange?.();
97
148
  }
98
149
  }
99
150
  controller.releaseBudget(`load-${arrival.ordinal}`);
@@ -145,28 +196,56 @@ export async function runDecisionLoadHarness(options) {
145
196
  signal.addEventListener('abort', aborted, { once: true });
146
197
  }),
147
198
  };
199
+ const advance = async (ms) => {
200
+ if (!options.signal) {
201
+ await clock.advance(ms);
202
+ return;
203
+ }
204
+ const end = clock.now() + ms;
205
+ while (clock.now() < end && !options.signal.aborted)
206
+ await clock.advance(Math.min(1000, end - clock.now()));
207
+ };
148
208
  const outcomes = [];
149
209
  for (const arrival of arrivals) {
210
+ if (options.signal?.aborted)
211
+ break;
150
212
  const due = start + arrival.atMs - clock.now();
151
213
  if (due > 0)
152
- await clock.advance(due);
214
+ await advance(due);
215
+ if (options.signal?.aborted)
216
+ break;
153
217
  const controller = new AbortController();
154
218
  const cancellation = { abortedAtMs: null };
219
+ let cancellationTimer;
155
220
  if (arrival.cancelAtMs !== null) {
156
- setTimeout(() => { cancellation.abortedAtMs = clock.now(); controller.abort(); }, arrival.cancelAtMs - arrival.atMs);
221
+ cancellationTimer = setTimeout(() => { cancellation.abortedAtMs = clock.now(); controller.abort(); }, arrival.cancelAtMs - arrival.atMs);
157
222
  }
158
- outcomes.push(driver(arrival, controller.signal, context)
159
- .then(result => ({ arrival, result, abortedAtMs: cancellation.abortedAtMs })));
223
+ const combined = options.signal ? AbortSignal.any([controller.signal, options.signal]) : controller.signal;
224
+ const pending = driver(arrival, combined, context)
225
+ .then(result => ({ arrival, result, abortedAtMs: cancellation.abortedAtMs }))
226
+ .finally(() => clearTimeout(cancellationTimer));
227
+ // Observe rejection immediately; allSettled below retains it for the caller.
228
+ void pending.catch(() => { });
229
+ outcomes.push(pending);
160
230
  observe();
161
231
  if (arrival.ordinal % 500 === 0)
162
232
  sampleMemory();
163
233
  }
164
234
  let settled = false;
165
- const all = Promise.all(outcomes).then(values => { settled = true; return values; });
235
+ const all = Promise.allSettled(outcomes).then(values => { settled = true; return values; });
166
236
  const drainStep = manifest.arrivalModel.serviceTimeMs.maximum + manifest.arrivalModel.retryBackoffMs;
167
237
  while (!settled)
168
238
  await clock.advance(drainStep);
169
- const results = await all;
239
+ const settledResults = await all;
240
+ const failure = settledResults.find(result => result.status === 'rejected');
241
+ if (failure?.status === 'rejected')
242
+ throw failure.reason;
243
+ const results = settledResults.map(result => result.value);
244
+ if (options.fullWindow && !options.signal?.aborted) {
245
+ const windowMs = (options.windowSeconds ?? manifest.duration.loadSeconds + manifest.duration.spikeSeconds + manifest.duration.soakSeconds) * 1000;
246
+ if (start + windowMs > clock.now())
247
+ await advance(start + windowMs - clock.now());
248
+ }
170
249
  sampleMemory();
171
250
  const threadUsage = process.threadCpuUsage(cpuStart);
172
251
  const cpuMicros = threadUsage.user + threadUsage.system;
@@ -205,10 +284,10 @@ export async function runDecisionLoadHarness(options) {
205
284
  const ratio = (value, total) => total ? Number((value / total).toFixed(6)) : 1;
206
285
  return {
207
286
  observations: {
208
- arrivals: arrivals.length, attempts, completed, failed, shed, cancelled,
287
+ arrivals: results.length, attempts, completed, failed, shed, cancelled,
209
288
  shedReasons: Object.fromEntries(Object.entries(shedReasons).sort(([left], [right]) => left.localeCompare(right))),
210
289
  maximumActiveCalls: maximumActive, maximumQueuedCalls: maximumQueued, maximumEligibleLaneWaitMs: laneWait,
211
- retryAmplificationRatio: ratio(attempts, arrivals.length), maximumCancellationLatencyMs: cancellationLatency,
290
+ retryAmplificationRatio: ratio(attempts, results.length), maximumCancellationLatencyMs: cancellationLatency,
212
291
  quietPrincipalAdmissionRatio: ratio(admitted.quiet, submitted.quiet),
213
292
  noisyPrincipalAdmissionRatio: ratio(admitted.noisy, submitted.noisy),
214
293
  virtualDurationMs,
@@ -262,19 +341,16 @@ export function verifyDecisionLoadResultRecord(record, manifestDigest) {
262
341
  return record.schema === 'decision-load-result/v1' && record.manifest.digest === manifestDigest
263
342
  && digest === decisionLoadManifestDigest(canonicalJson(body));
264
343
  }
265
- /**
266
- * G5 evidence flags. Offline fake-provider records can prove the harness and
267
- * the admission path, but only a passing staged-provider record bound to its
268
- * manifest may set `load-manifest-qualified`.
269
- */
270
- export function decisionLoadEvidenceFlags(records, manifestDigest) {
271
- const bound = records.filter(record => verifyDecisionLoadResultRecord(record, manifestDigest));
344
+ /** Historical offline evidence stays separate from verified, exact-commit v3 G5 artifacts. */
345
+ export function decisionLoadEvidenceFlags(records, manifestDigest, sourceCommit) {
346
+ const legacy = records.filter((record) => 'schema' in record);
347
+ const bound = legacy.filter(record => verifyDecisionLoadResultRecord(record, manifestDigest));
272
348
  const offline = bound.filter(record => record.mode === 'offline-fake-provider');
273
349
  return {
274
- 'load-manifest-offline-passed': offline.length > 0 && offline.length === records.filter(record => record.mode === 'offline-fake-provider').length
350
+ 'load-manifest-offline-passed': offline.length > 0 && offline.length === legacy.filter(record => record.mode === 'offline-fake-provider').length
275
351
  && offline.every(record => record.passed),
276
- 'load-manifest-qualified': bound.some(record => record.mode === 'staged-provider' && record.passed)
277
- && bound.length === records.length && bound.every(record => record.passed),
352
+ 'load-manifest-qualified': records.length > 0 && records.every(record => 'schemaVersion' in record
353
+ && record.manifestDigest === manifestDigest && verifyDecisionStagedLoadRecord(record, sourceCommit)),
278
354
  };
279
355
  }
280
356
  //# sourceMappingURL=load.js.map
@@ -1,13 +1,15 @@
1
1
  import { createHash } from 'node:crypto';
2
2
  import { lstat, mkdir, readFile, rename, writeFile } from 'node:fs/promises';
3
3
  import { dirname, resolve, sep } from 'node:path';
4
+ import { DECISION_STAGED_LOAD_RESULT_VERSIONS } from './load.js';
4
5
  import { evaluateQualification } from './gates.js';
5
6
  import { deriveGateEvidence, DERIVED_GATE_EVIDENCE, GATE_ARTIFACT_FLAGS, GATE_ARTIFACT_SCHEMAS, IMMUTABLE_SPLIT_VERSIONS, validateGateArtifact, } from './gate-evidence.js';
6
7
  import { scanQualificationPrivacy } from './privacy.js';
7
8
  /** Named master-test-plan suites plus the M01-M11 amendment IDs. */
8
9
  export const QUALIFICATION_EVIDENCE_ID = /^(?:CON|POL|BCH|CTX|CNC|CAN|RTY|REC|SEC|PRV|CAL|DRF|CCP|M(?:0[1-9]|1[01]))-[A-Z0-9][A-Z0-9._-]*$/;
9
10
  const DEFAULT_TIMEOUT_MS = 30_000;
10
- const DEFAULT_MAX_ARTIFACT_BYTES = 256 * 1024;
11
+ // Full staged-load receipt history and fixed-interval samples exceed 256 KiB.
12
+ const DEFAULT_MAX_ARTIFACT_BYTES = 1024 * 1024;
11
13
  const DEFAULT_CONCURRENCY = 4;
12
14
  function boundedInteger(value, fallback, minimum, maximum) {
13
15
  if (value === undefined)
@@ -232,7 +234,7 @@ async function attachGateArtifacts(plan, evidence, maxBytes) {
232
234
  const temporary = `${target}.${process.pid}.${Date.now()}.tmp`;
233
235
  await writeFile(temporary, bytes, { flag: 'wx', mode: 0o600 });
234
236
  await rename(temporary, target);
235
- refs[flag] = { artifact: relative, digest: digest(bytes), schemaVersion: GATE_ARTIFACT_SCHEMAS[flag] };
237
+ refs[flag] = { artifact: relative, digest: digest(bytes), schemaVersion: parsed.schemaVersion };
236
238
  context.accepted[flag] = parsed;
237
239
  }
238
240
  return refs;
@@ -268,8 +270,11 @@ export async function verifyQualificationGateArtifacts(manifest, artifactRoot, m
268
270
  const parsed = parseJson(bytes);
269
271
  const versionOk = flag === 'immutable-splits'
270
272
  ? IMMUTABLE_SPLIT_VERSIONS.includes(ref.schemaVersion)
271
- : ref.schemaVersion === GATE_ARTIFACT_SCHEMAS[flag];
272
- if (!versionOk || !validateGateArtifact(flag, parsed, context)) {
273
+ : flag === 'load-manifest-qualified'
274
+ ? DECISION_STAGED_LOAD_RESULT_VERSIONS.includes(ref.schemaVersion)
275
+ : ref.schemaVersion === GATE_ARTIFACT_SCHEMAS[flag];
276
+ if (!versionOk || (flag === 'load-manifest-qualified' && parsed?.schemaVersion !== ref.schemaVersion)
277
+ || !validateGateArtifact(flag, parsed, context)) {
273
278
  results.push({ flag, verified: false, reason: 'invalid-artifact' });
274
279
  continue;
275
280
  }
@@ -401,7 +406,9 @@ export async function evaluateExecutedQualification(manifest, artifactRoot, maxA
401
406
  && verification.every(item => item.verified && caseIds.has(item.caseId))
402
407
  && gateVerification.every(item => item.verified);
403
408
  return { manifest, verification, gateVerification,
404
- report: evaluateQualification(hardened, undefined, { artifactsVerified }) };
409
+ report: evaluateQualification(hardened, undefined, {
410
+ artifactsVerified, loadArtifactVerified: gateVerified.has('load-manifest-qualified'),
411
+ }) };
405
412
  }
406
413
  /** Executes, persists, verifies, and evaluates in one promotion-capable pipeline. */
407
414
  export async function executeAndEvaluateQualification(plan) {
@@ -1,4 +1,4 @@
1
- import { assertReviewProjection, currentProposal, ReviewAccessError, ReviewConflictError, reviewDigest, reviewOperatorEventId } from './validate.js';
1
+ import { assertReviewProjection, currentProposal, ReviewAccessError, ReviewConflictError, reviewDigest, reviewOperatorEventId, validateReviewEvaluation } from './validate.js';
2
2
  import { validateDecisionLifecyclePolicy } from '../lifecycle.js';
3
3
  /** Only an executor that can attest zero external effect may report definitive failure. */
4
4
  export class ReviewDefinitiveExecutionError extends Error {
@@ -233,7 +233,8 @@ export class DecisionReviewService {
233
233
  return this.append(review, 'claimed', scope.actor, rationale, 'claimed');
234
234
  }, true);
235
235
  }
236
- async decide(scope, id, decision, rationale) {
236
+ async decide(scope, id, decision, rationale, evaluation) {
237
+ const recordedEvaluation = evaluation === undefined ? {} : { evaluation: structuredClone(evaluation) };
237
238
  return this.mutate(scope, id, 'decide', async (review) => {
238
239
  this.requireActive(review);
239
240
  const proposal = currentProposal(review);
@@ -243,10 +244,11 @@ export class DecisionReviewService {
243
244
  throw new ReviewAccessError('Self-approval is denied');
244
245
  if (review.decisions.some(item => item.reviewer.id === scope.actor.id && item.proposalVersion === proposal.version))
245
246
  throw new ReviewConflictError('Reviewer already decided this proposal');
246
- const decisions = [...review.decisions, { reviewer: scope.actor, proposalVersion: proposal.version, decision, rationale, atEpochMs: this.now() }];
247
+ validateReviewEvaluation(recordedEvaluation.evaluation);
248
+ const decisions = [...review.decisions, { reviewer: scope.actor, proposalVersion: proposal.version, decision, rationale, atEpochMs: this.now(), ...recordedEvaluation }];
247
249
  const approvals = decisions.filter(item => item.proposalVersion === proposal.version && item.decision === 'approve').length;
248
250
  const status = decision === 'reject' ? 'rejected' : approvals >= review.quorum ? 'approved' : 'claimed';
249
- return this.append({ ...review, decisions }, decision === 'approve' ? 'approved' : 'rejected', scope.actor, rationale, status, { quorumSatisfied: approvals >= review.quorum });
251
+ return this.append({ ...review, decisions }, decision === 'approve' ? 'approved' : 'rejected', scope.actor, rationale, status, { quorumSatisfied: approvals >= review.quorum, ...recordedEvaluation });
250
252
  });
251
253
  }
252
254
  edit(scope, id, action, rationale, newResumeToken) {
@@ -15,6 +15,22 @@ export function reviewOperatorEventId(reviewId, sequence) {
15
15
  const digestPattern = /^sha256:[a-f0-9]{64}$/;
16
16
  const restrictedKey = /(?:password|credential|api.?key|secret|private.?reasoning|provider.?body|raw.?state|vault.?locator)/i;
17
17
  const restrictedValue = /(?:vault:\/\/|\b(?:sk-(?:test-)?[a-z0-9_-]{12,}|ghp_[a-z0-9]{12,})\b)/i;
18
+ /** Optional review evidence remains strict when present, including imported records. */
19
+ export function validateReviewEvaluation(value) {
20
+ if (value === undefined)
21
+ return;
22
+ const object = (item) => typeof item === 'object' && item !== null && !Array.isArray(item);
23
+ const text = (item) => typeof item === 'string' && item.trim().length > 0 && item.length <= 8192;
24
+ const fields = ['evaluatorModel', 'evaluatorVendor', 'evaluatorFamily', 'acceptanceCriteriaRef', 'reReviewOf'];
25
+ if (!object(value) || Object.keys(value).some(key => key !== 'disagreements' && !fields.includes(key))
26
+ || fields.some(key => value[key] !== undefined && !text(value[key])))
27
+ throw new ReviewIntegrityError('Invalid review evaluation');
28
+ if (value.disagreements !== undefined && (!Array.isArray(value.disagreements) || value.disagreements.some(item => !object(item) || Object.keys(item).some(key => !['claimRef', 'generatorPosition', 'evaluatorPosition', 'resolutionRef'].includes(key))
29
+ || !text(item.claimRef) || !text(item.generatorPosition) || !text(item.evaluatorPosition)
30
+ || (item.resolutionRef !== undefined && !text(item.resolutionRef)))))
31
+ throw new ReviewIntegrityError('Invalid review disagreements');
32
+ assertReviewProjection(value);
33
+ }
18
34
  /** Enforce reference-only default review payloads; do not echo rejected material. */
19
35
  export function assertReviewProjection(value) {
20
36
  if (typeof value === 'string') {
@@ -67,7 +83,11 @@ export function validateReview(review) {
67
83
  if (proposal.version !== index + 1 || proposal.actionDigest !== reviewDigest(proposal.action))
68
84
  throw new ReviewIntegrityError('Invalid proposal lineage');
69
85
  });
70
- review.decisions.forEach(decision => { assertReviewProjection(decision.reviewer); assertReviewProjection(decision.rationale); });
86
+ review.decisions.forEach(decision => {
87
+ assertReviewProjection(decision.reviewer);
88
+ assertReviewProjection(decision.rationale);
89
+ validateReviewEvaluation(decision.evaluation);
90
+ });
71
91
  review.events.forEach(event => { assertReviewProjection(event.actor); assertReviewProjection(event.rationale); });
72
92
  if (review.effectReceipt)
73
93
  assertReviewProjection(review.effectReceipt.result);
@@ -108,7 +128,8 @@ export function validateReview(review) {
108
128
  throw new ReviewIntegrityError('Illegal decision');
109
129
  const decision = review.decisions[decisionIndex++];
110
130
  if (!decision || decision.proposalVersion !== version || decision.decision !== (event.type === 'approved' ? 'approve' : 'reject') ||
111
- canonicalJson(decision.reviewer) !== canonicalJson(event.actor) || decision.rationale !== event.rationale || decision.atEpochMs !== event.atEpochMs) {
131
+ canonicalJson(decision.reviewer) !== canonicalJson(event.actor) || decision.rationale !== event.rationale || decision.atEpochMs !== event.atEpochMs ||
132
+ canonicalJson(decision.evaluation ?? null) !== canonicalJson(event.data?.evaluation ?? null)) {
112
133
  throw new ReviewIntegrityError('Decision event mismatch');
113
134
  }
114
135
  if (event.type === 'rejected')
@@ -16,13 +16,18 @@ export function decisionSavingsWeights(ask) {
16
16
  output_tokens: ask?.savings?.output_tokens ?? 128,
17
17
  };
18
18
  }
19
- export function estimateDecisionSavings(inputTokens, answered, jevTokens, weights) {
19
+ export function estimateDecisionSavings(inputTokens, answered, jevTokens, weights, usage) {
20
20
  const llmTokens = answered ? inputTokens + weights.reasoning_tokens + weights.output_tokens : 0;
21
+ const split = weights.jev_input_per_mtok !== undefined && weights.jev_output_per_mtok !== undefined
22
+ && usage?.inputTokens != null && (usage.outputTokens != null || weights.jev_output_per_mtok === 0);
23
+ const jevCost = split
24
+ ? (usage.inputTokens * weights.jev_input_per_mtok + (usage.outputTokens ?? 0) * weights.jev_output_per_mtok) / 1_000_000
25
+ : jevTokens * weights.jev_per_mtok / 1_000_000;
21
26
  return {
22
27
  llm_tokens_avoided: llmTokens,
23
28
  jev_tokens: jevTokens,
24
- cost_weighted_net_usd: (llmTokens * weights.llm_per_mtok - jevTokens * weights.jev_per_mtok) / 1_000_000,
25
- estimationMethod: { version: 'aiwg-decision-savings/v2', input_tokens: inputTokens, ...weights },
29
+ cost_weighted_net_usd: llmTokens * weights.llm_per_mtok / 1_000_000 - jevCost,
30
+ estimationMethod: { version: 'aiwg-decision-savings/v2', input_tokens: inputTokens, ...weights, source: weights.source ?? 'manual', jev_rate_basis: split ? 'split' : 'blended' },
26
31
  };
27
32
  }
28
33
  //# sourceMappingURL=savings.js.map