@aiwg/cli 2026.9.24 → 2026.10.1

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 (209) hide show
  1. package/agentic/code/providers/capability-matrix.yaml +2 -1
  2. package/dist/src/artifacts/discovery-shadow-evaluator.js +177 -0
  3. package/dist/src/artifacts/discovery-shadow.js +195 -0
  4. package/dist/src/artifacts/index-builder.js +47 -9
  5. package/dist/src/artifacts/query-engine.js +94 -1
  6. package/dist/src/artifacts/types.js +1 -0
  7. package/dist/src/cli/handlers/decision.js +177 -0
  8. package/dist/src/cli/handlers/gates.js +154 -0
  9. package/dist/src/cli/handlers/index.js +5 -1
  10. package/dist/src/cli/handlers/sessions.js +10 -7
  11. package/dist/src/cli/handlers/utilities.js +20 -0
  12. package/dist/src/config/aiwg-config.js +15 -2
  13. package/dist/src/decision/acceptance.js +25 -0
  14. package/dist/src/decision/adapters/jev.js +29 -3
  15. package/dist/src/decision/ask.js +352 -0
  16. package/dist/src/decision/calibration/registry.js +9 -0
  17. package/dist/src/decision/compose.js +74 -15
  18. package/dist/src/decision/context-live-promotion.js +344 -0
  19. package/dist/src/decision/context-live-qualification.js +591 -0
  20. package/dist/src/decision/context-pruning.js +847 -0
  21. package/dist/src/decision/driver.js +432 -0
  22. package/dist/src/decision/egress-live-qualification.js +874 -0
  23. package/dist/src/decision/ensemble/contract.js +64 -1
  24. package/dist/src/decision/ensemble/index.js +1 -0
  25. package/dist/src/decision/ensemble/report.js +8 -42
  26. package/dist/src/decision/ensemble/runtime.js +571 -0
  27. package/dist/src/decision/ensemble-study/artifacts.js +85 -0
  28. package/dist/src/decision/ensemble-study/calibration.js +443 -0
  29. package/dist/src/decision/ensemble-study/corpus.js +229 -0
  30. package/dist/src/decision/ensemble-study/multifact-artifacts.js +23 -0
  31. package/dist/src/decision/ensemble-study/multifact-study.js +596 -0
  32. package/dist/src/decision/ensemble-study/multifact.js +241 -0
  33. package/dist/src/decision/ensemble-study/protocol.js +77 -0
  34. package/dist/src/decision/ensemble-study/score.js +462 -0
  35. package/dist/src/decision/ensemble-study/staged.js +87 -0
  36. package/dist/src/decision/ensemble-study/statistics.js +84 -0
  37. package/dist/src/decision/evaluate.js +227 -14
  38. package/dist/src/decision/export.js +4 -0
  39. package/dist/src/decision/feature-export.js +1074 -0
  40. package/dist/src/decision/graph-index.js +16 -0
  41. package/dist/src/decision/graph-live-qualification.js +1041 -0
  42. package/dist/src/decision/graph-live-workload.js +245 -0
  43. package/dist/src/decision/graph-skill-bridge.js +21 -3
  44. package/dist/src/decision/heldout/calibration.js +140 -0
  45. package/dist/src/decision/heldout/collector.js +430 -0
  46. package/dist/src/decision/heldout/contract.js +309 -0
  47. package/dist/src/decision/heldout/d29-generator-ids.js +12 -0
  48. package/dist/src/decision/heldout/d29-passage-baseline-v2.js +93 -0
  49. package/dist/src/decision/heldout/d29-passage-baseline-v3.js +100 -0
  50. package/dist/src/decision/heldout/d29-passage-baseline.js +66 -0
  51. package/dist/src/decision/heldout/d29-pools-v8.js +575 -0
  52. package/dist/src/decision/heldout/d29-pools.js +288 -0
  53. package/dist/src/decision/heldout/d29-v6.js +169 -0
  54. package/dist/src/decision/heldout/d29-v7.js +202 -0
  55. package/dist/src/decision/heldout/d29-v8.js +586 -0
  56. package/dist/src/decision/heldout/generator-registry.js +67 -0
  57. package/dist/src/decision/heldout/generators.js +696 -0
  58. package/dist/src/decision/heldout/index.js +8 -0
  59. package/dist/src/decision/heldout/journal.js +346 -0
  60. package/dist/src/decision/heldout/study-generators.js +64 -0
  61. package/dist/src/decision/heldout/types.js +2 -0
  62. package/dist/src/decision/host-policy.js +61 -0
  63. package/dist/src/decision/index.js +13 -0
  64. package/dist/src/decision/interop-profile.v1.json +112 -0
  65. package/dist/src/decision/interop.js +653 -0
  66. package/dist/src/decision/issue-triage/index.js +3 -0
  67. package/dist/src/decision/issue-triage/runtime.js +1069 -0
  68. package/dist/src/decision/issue-triage/types.js +2 -0
  69. package/dist/src/decision/jev-credentials.js +121 -0
  70. package/dist/src/decision/patterns/catalog.js +28 -8
  71. package/dist/src/decision/patterns/playground.js +28 -11
  72. package/dist/src/decision/patterns/specs.js +19 -10
  73. package/dist/src/decision/preprocessed-evidence.js +724 -0
  74. package/dist/src/decision/qualification/capture.js +8 -2
  75. package/dist/src/decision/qualification/gate-evidence.js +19 -4
  76. package/dist/src/decision/qualification/quality.js +57 -49
  77. package/dist/src/decision/qualification/release.js +76 -3
  78. package/dist/src/decision/qualification/runner.js +5 -2
  79. package/dist/src/decision/routing/contract.js +175 -0
  80. package/dist/src/decision/routing/index.js +5 -0
  81. package/dist/src/decision/routing/report.js +216 -0
  82. package/dist/src/decision/routing/runtime.js +685 -0
  83. package/dist/src/decision/routing/types.js +2 -0
  84. package/dist/src/decision/sdlc-screening.js +1157 -0
  85. package/dist/src/decision/sensitivity/comparative.js +154 -0
  86. package/dist/src/decision/sensitivity/contract.js +218 -0
  87. package/dist/src/decision/sensitivity/index.js +5 -0
  88. package/dist/src/decision/sensitivity/runtime.js +912 -0
  89. package/dist/src/decision/sensitivity/types.js +2 -0
  90. package/dist/src/decision/validate.js +26 -0
  91. package/dist/src/extensions/commands/definitions.js +38 -0
  92. package/dist/src/extensions/manifest.js +44 -0
  93. package/dist/src/gates/discovery.js +315 -0
  94. package/dist/src/gates/driver.js +388 -0
  95. package/dist/src/gates/evaluate.js +719 -0
  96. package/dist/src/gates/floors.js +255 -0
  97. package/dist/src/gates/index.js +17 -0
  98. package/dist/src/gates/providers/evidence.js +38 -0
  99. package/dist/src/gates/providers/index.js +21 -0
  100. package/dist/src/gates/providers/isolated-child-source.js +446 -0
  101. package/dist/src/gates/providers/loader.js +1069 -0
  102. package/dist/src/gates/providers/paired.js +52 -0
  103. package/dist/src/gates/providers/proportion.js +60 -0
  104. package/dist/src/gates/providers/registry.js +40 -0
  105. package/dist/src/gates/providers/scalar.js +41 -0
  106. package/dist/src/gates/providers/screening.js +53 -0
  107. package/dist/src/gates/providers/types.js +25 -0
  108. package/dist/src/gates/registry.js +1003 -0
  109. package/dist/src/gates/report.js +102 -0
  110. package/dist/src/gates/schema.js +56 -0
  111. package/dist/src/gates/stats/binomial.js +26 -0
  112. package/dist/src/gates/stats/bootstrap.js +62 -0
  113. package/dist/src/gates/stats/clopper-pearson.js +95 -0
  114. package/dist/src/gates/stats/digest.js +26 -0
  115. package/dist/src/gates/stats/error.js +5 -0
  116. package/dist/src/gates/stats/index.js +9 -0
  117. package/dist/src/gates/stats/normal.js +35 -0
  118. package/dist/src/gates/stats/paired.js +120 -0
  119. package/dist/src/gates/stats/splits.js +30 -0
  120. package/dist/src/gates/types.js +8 -0
  121. package/dist/src/governance/redaction.js +14 -4
  122. package/dist/src/mcp/muse-native-config.mjs +123 -0
  123. package/dist/src/mcp/tools/decision.mjs +228 -0
  124. package/dist/src/mcp/tools/subsystems.mjs +2 -0
  125. package/dist/src/providers/capability-matrix.yaml +2 -1
  126. package/dist/src/providers/provider-definitions.js +2 -1
  127. package/dist/src/sessions/adapters/muse.js +336 -39
  128. package/dist/src/sessions/batch-import.js +7 -0
  129. package/dist/src/sessions/workspace-discovery.js +13 -2
  130. package/package.json +1 -1
  131. package/schemas/decision/ConformalFrozenOpenData.v2.schema.json +371 -0
  132. package/schemas/decision/ConformalOpenDataReport.v2.schema.json +421 -0
  133. package/schemas/decision/ConformalPreregistration.v2.schema.json +529 -0
  134. package/schemas/decision/ContextPruningCandidate.v1.schema.json +47 -0
  135. package/schemas/decision/ContextPruningEvaluationReport.v1.schema.json +758 -0
  136. package/schemas/decision/ContextPruningPreregistration.v1.schema.json +175 -0
  137. package/schemas/decision/ContextPruningReceipt.v1.schema.json +72 -0
  138. package/schemas/decision/D17Multifact.v1.schema.json +1851 -0
  139. package/schemas/decision/D17StudyAnalysis.v1.schema.json +42 -0
  140. package/schemas/decision/D17StudyAnalysis.v2.schema.json +51 -0
  141. package/schemas/decision/D17StudyCalibration.v1.schema.json +819 -0
  142. package/schemas/decision/D17StudyDryRun.v1.schema.json +169 -0
  143. package/schemas/decision/D17StudyDryRun.v2.schema.json +239 -0
  144. package/schemas/decision/D17StudyGold.v1.schema.json +108 -0
  145. package/schemas/decision/D17StudyGuide.v1.schema.json +63 -0
  146. package/schemas/decision/D17StudyNativeTemplates.v1.schema.json +619 -0
  147. package/schemas/decision/D17StudyProtocol.v1.schema.json +241 -0
  148. package/schemas/decision/D17StudyReport.v1.schema.json +902 -0
  149. package/schemas/decision/D17StudyReport.v2.schema.json +1319 -0
  150. package/schemas/decision/D17StudyReview.v1.schema.json +175 -0
  151. package/schemas/decision/D17StudySplits.v1.schema.json +217 -0
  152. package/schemas/decision/D29Study.v1.schema.json +1903 -0
  153. package/schemas/decision/D29Study.v2.schema.json +1344 -0
  154. package/schemas/decision/D29Study.v3.schema.json +609 -0
  155. package/schemas/decision/D29Study.v4.schema.json +1133 -0
  156. package/schemas/decision/D29Study.v6.schema.json +1205 -0
  157. package/schemas/decision/D29Study.v7.schema.json +1471 -0
  158. package/schemas/decision/D29Study.v8.schema.json +671 -0
  159. package/schemas/decision/D29Study.v9.schema.json +778 -0
  160. package/schemas/decision/DecisionComparativeReplayAuditSample.v1.schema.json +137 -0
  161. package/schemas/decision/DecisionComparativeReplayCorpus.v1.schema.json +258 -0
  162. package/schemas/decision/DecisionComparativeReplayFreeze.v1.schema.json +74 -0
  163. package/schemas/decision/DecisionComparativeReplayGold.v1.schema.json +50 -0
  164. package/schemas/decision/DecisionComparativeReplayGoldAsset.v1.schema.json +37 -0
  165. package/schemas/decision/DecisionComparativeReplayIntegrity.v1.schema.json +81 -0
  166. package/schemas/decision/DecisionComparativeReplayMembers.v1.schema.json +52 -0
  167. package/schemas/decision/DecisionComparativeReplayPreregistration.v1.schema.json +113 -0
  168. package/schemas/decision/DecisionComparativeReplayReport.v1.schema.json +407 -0
  169. package/schemas/decision/DecisionComparativeReplayReviewPackets.v1.schema.json +260 -0
  170. package/schemas/decision/DecisionComparativeReplaySplit.v1.schema.json +84 -0
  171. package/schemas/decision/DecisionFeatureObservation.v1.schema.json +564 -0
  172. package/schemas/decision/DecisionFeatureSet.v1.schema.json +155 -0
  173. package/schemas/decision/DecisionInteropMapping.v1.schema.json +85 -0
  174. package/schemas/decision/DecisionIssueTriageEvaluationManifest.v1.schema.json +174 -0
  175. package/schemas/decision/DecisionIssueTriageEvaluationReport.v1.schema.json +280 -0
  176. package/schemas/decision/DecisionIssueTriagePilotPack.v1.schema.json +114 -0
  177. package/schemas/decision/DecisionRouteReceipt.v1.schema.json +155 -0
  178. package/schemas/decision/DecisionRoutingPolicy.v1.schema.json +132 -0
  179. package/schemas/decision/DecisionRoutingShadowReport.v1.schema.json +170 -0
  180. package/schemas/decision/DecisionRoutingTask.v1.schema.json +49 -0
  181. package/schemas/decision/DecisionRuleset.schema.json +8 -2
  182. package/schemas/decision/DecisionRuleset.v1alpha2.schema.json +8 -2
  183. package/schemas/decision/DecisionSensitivityPlan.v1.schema.json +191 -0
  184. package/schemas/decision/DecisionSensitivityReport.v1.schema.json +163 -0
  185. package/schemas/decision/HeldoutApproval.v1.schema.json +312 -0
  186. package/schemas/decision/HeldoutAttempt.v1.schema.json +211 -0
  187. package/schemas/decision/HeldoutBaseline.v1.schema.json +90 -0
  188. package/schemas/decision/HeldoutCalibrationPhase.v1.schema.json +87 -0
  189. package/schemas/decision/HeldoutCorpus.v1.schema.json +167 -0
  190. package/schemas/decision/HeldoutEvent.v1.schema.json +41 -0
  191. package/schemas/decision/HeldoutFrozen.v1.schema.json +142 -0
  192. package/schemas/decision/HeldoutPreregistration.v1.schema.json +222 -0
  193. package/schemas/decision/HeldoutSpendEvent.v1.schema.json +122 -0
  194. package/schemas/decision/HeldoutSpendHead.v1.schema.json +25 -0
  195. package/schemas/decision/HeldoutSpendHead.v2.schema.json +46 -0
  196. package/schemas/decision/HeldoutSummary.v1.schema.json +101 -0
  197. package/schemas/decision/OpaDecisionLogExport.v1.schema.json +65 -0
  198. package/schemas/decision/PreprocessedEvidence.v1.schema.json +306 -0
  199. package/schemas/decision/RulesetResult.v1alpha2.schema.json +175 -0
  200. package/schemas/decision/SdlcEvidenceScreening.v1.schema.json +499 -0
  201. package/schemas/decision/SdlcScreeningPreregistration.v1.schema.json +132 -0
  202. package/schemas/decision/SdlcScreeningPreregistration.v2.schema.json +138 -0
  203. package/schemas/decision/SdlcScreeningRelease.v1.schema.json +654 -0
  204. package/schemas/decision/SdlcScreeningRelease.v2.schema.json +654 -0
  205. package/tools/agents/deploy-agents.mjs +1 -1
  206. package/tools/agents/providers/base.mjs +40 -18
  207. package/tools/agents/providers/factory.mjs +10 -2
  208. package/tools/agents/providers/muse-hooks.mjs +1 -29
  209. package/tools/agents/providers/muse.mjs +6 -3
@@ -0,0 +1,177 @@
1
+ import { askDecision, DecisionAskUsageError, decisionCapabilities, evaluateRequestPath, listPatterns, livePlan, materializeSyntheticClassificationSetup, readDecisionContextFile, runOfflinePattern, setupJev, showPattern, syntheticClassificationSetup, validateDecisionInput, } from '../../decision/driver.js';
2
+ function jsonResult(value, exitCode = 0) {
3
+ return { exitCode, message: JSON.stringify(value, null, 2), rawOutput: true };
4
+ }
5
+ function usage() {
6
+ return [
7
+ 'Usage: aiwg decision <capabilities|status|patterns|validate|evaluate|setup> [options]',
8
+ ' aiwg decision ask --question "<q>" (--yes-no | --choices a,b,c | --scale 1-5) [--context <text> | --context-file <path> | --context-stdin] [--threshold 0.8] [--timeout-ms 15000] [--json]',
9
+ '',
10
+ 'Commands:',
11
+ ' capabilities|status Show offline readiness, primitives, config and feature availability',
12
+ ' patterns list List governed decision pattern packs',
13
+ ' patterns show <id> Show one pattern pack and schemas',
14
+ ' patterns offline-run <id> [fixture] Run a recorded offline fixture through the decision runtime',
15
+ ' patterns live-plan <id> [--opt-in] [--credential-resolved] [--egress-approved]',
16
+ ' validate <request|definition|ruleset|binding> <path>',
17
+ ' evaluate --request <path> [--host-policy-module <path>]',
18
+ ' setup synthetic-classification [--output-dir <dir>]',
19
+ ' setup jev [--token-stdin] [--region <r>] [--endpoint <url>] [--verify] [--remove]',
20
+ ' ask --question "<q>" (--yes-no | --choices a,b,c | --scale 1-5)',
21
+ ].join('\n');
22
+ }
23
+ function takeOption(args, name) {
24
+ const index = args.indexOf(name);
25
+ if (index < 0)
26
+ return undefined;
27
+ return args[index + 1];
28
+ }
29
+ function takeRequiredOption(args, name) {
30
+ const value = takeOption(args, name);
31
+ if (!value || value.startsWith('--'))
32
+ throw new DecisionAskUsageError(`missing-${name.slice(2)}`, `${name} requires a value`);
33
+ return value;
34
+ }
35
+ function patternId(value) {
36
+ if (!value)
37
+ throw new Error('Pattern id is required');
38
+ return value;
39
+ }
40
+ function flagCount(args, names) {
41
+ return args.filter(arg => names.includes(arg)).length;
42
+ }
43
+ async function readStdin() {
44
+ const chunks = [];
45
+ for await (const chunk of process.stdin)
46
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
47
+ return Buffer.concat(chunks).toString('utf8');
48
+ }
49
+ function parseNumber(value, name) {
50
+ if (value === undefined)
51
+ throw new DecisionAskUsageError(`missing-${name.slice(2)}`, `${name} requires a value`);
52
+ const parsed = Number(value);
53
+ if (!Number.isFinite(parsed))
54
+ throw new DecisionAskUsageError(`invalid-${name.slice(2)}`, `${name} must be numeric`);
55
+ return parsed;
56
+ }
57
+ function parseScale(value) {
58
+ if (!value)
59
+ throw new DecisionAskUsageError('invalid-scale', '--scale requires a range like 1-5');
60
+ const match = /^(-?\d+)-(-?\d+)$/.exec(value);
61
+ if (!match)
62
+ throw new DecisionAskUsageError('invalid-scale', '--scale requires a range like 1-5');
63
+ return { low: Number(match[1]), high: Number(match[2]) };
64
+ }
65
+ async function decisionSetupJev(rest) {
66
+ const token = rest.includes('--token-stdin') ? (await readStdin()).trim() : undefined;
67
+ const result = await setupJev({
68
+ token,
69
+ region: takeOption(rest, '--region'),
70
+ endpoint: takeOption(rest, '--endpoint'),
71
+ verify: rest.includes('--verify'),
72
+ remove: rest.includes('--remove'),
73
+ }, { env: process.env });
74
+ return jsonResult(result);
75
+ }
76
+ async function decisionAsk(ctx) {
77
+ const args = ctx.args.slice(1);
78
+ const modeCount = flagCount(args, ['--yes-no']) + (args.includes('--choices') ? 1 : 0) + (args.includes('--scale') ? 1 : 0);
79
+ if (modeCount !== 1)
80
+ throw new DecisionAskUsageError('invalid-mode', 'Select exactly one of --yes-no, --choices, or --scale');
81
+ const contextModes = flagCount(args, ['--context', '--context-file', '--context-stdin']);
82
+ if (contextModes > 1)
83
+ throw new DecisionAskUsageError('invalid-context', 'Select at most one context source');
84
+ const question = args.includes('--question') ? takeRequiredOption(args, '--question') : '';
85
+ let context = takeOption(args, '--context');
86
+ if (args.includes('--context') && (context === undefined || context.startsWith('--'))) {
87
+ throw new DecisionAskUsageError('missing-context', '--context requires text');
88
+ }
89
+ if (args.includes('--context-file')) {
90
+ const filePath = takeRequiredOption(args, '--context-file');
91
+ context = await readDecisionContextFile(filePath);
92
+ }
93
+ if (args.includes('--context-stdin'))
94
+ context = await readStdin();
95
+ const choices = args.includes('--choices') ? takeRequiredOption(args, '--choices') : undefined;
96
+ const scale = args.includes('--scale') ? parseScale(takeOption(args, '--scale')) : null;
97
+ const input = {
98
+ question,
99
+ mode: args.includes('--yes-no') ? { kind: 'yes-no' }
100
+ : choices !== undefined ? { kind: 'choices', choices: choices.split(',').map(value => value.trim()).filter(Boolean) }
101
+ : { kind: 'scale', ...scale },
102
+ ...(context === undefined ? {} : { context }),
103
+ ...(args.includes('--threshold') ? { threshold: parseNumber(takeOption(args, '--threshold'), '--threshold') } : {}),
104
+ ...(args.includes('--timeout-ms') ? { timeoutMs: parseNumber(takeOption(args, '--timeout-ms'), '--timeout-ms') } : {}),
105
+ };
106
+ const result = await askDecision(input, { env: process.env });
107
+ if (args.includes('--json'))
108
+ return jsonResult(result);
109
+ if (result.status === 'fallback')
110
+ return { exitCode: 0, message: `FALLBACK to LLM: ${result.reason}`, rawOutput: true };
111
+ return { exitCode: 0, message: `${String(result.answer)} (confidence ${result.confidence?.toFixed(2) ?? 'unknown'}, ${result.model ?? 'unknown-model'})`, rawOutput: true };
112
+ }
113
+ async function executeDecision(ctx) {
114
+ const [command, subcommand, ...rest] = ctx.args;
115
+ const options = { cwd: ctx.cwd, frameworkRoot: ctx.frameworkRoot, env: process.env };
116
+ try {
117
+ if (!command || command === '--help' || command === '-h')
118
+ return { exitCode: 0, message: usage(), rawOutput: true };
119
+ if (command === 'capabilities' || command === 'status')
120
+ return jsonResult(decisionCapabilities(options));
121
+ if (command === 'ask')
122
+ return await decisionAsk(ctx);
123
+ if (command === 'patterns') {
124
+ if (subcommand === 'list')
125
+ return jsonResult(listPatterns());
126
+ if (subcommand === 'show')
127
+ return jsonResult(showPattern(patternId(rest[0])));
128
+ if (subcommand === 'offline-run')
129
+ return jsonResult(await runOfflinePattern(patternId(rest[0]), rest[1]));
130
+ if (subcommand === 'live-plan')
131
+ return jsonResult(livePlan(patternId(rest[0]), {
132
+ explicitOptIn: rest.includes('--opt-in'),
133
+ credentialResolved: rest.includes('--credential-resolved'),
134
+ egressApproved: rest.includes('--egress-approved'),
135
+ }));
136
+ }
137
+ if (command === 'validate') {
138
+ const target = subcommand;
139
+ const result = await validateDecisionInput(target, rest[0] ?? '', options);
140
+ return jsonResult(result, result.valid ? 0 : 2);
141
+ }
142
+ if (command === 'evaluate') {
143
+ const request = takeOption(ctx.args, '--request');
144
+ if (!request)
145
+ return { exitCode: 2, message: 'decision evaluate requires --request <path>' };
146
+ const result = await evaluateRequestPath(request, options, {
147
+ hostPolicyModulePath: takeOption(ctx.args, '--host-policy-module'),
148
+ });
149
+ return jsonResult(result, result.exitCode);
150
+ }
151
+ if (command === 'setup' && subcommand === 'synthetic-classification') {
152
+ const outputDir = takeOption(ctx.args, '--output-dir');
153
+ if (outputDir)
154
+ return jsonResult(await materializeSyntheticClassificationSetup(outputDir, {}, options));
155
+ return jsonResult(syntheticClassificationSetup({}, options));
156
+ }
157
+ if (command === 'setup' && subcommand === 'jev')
158
+ return await decisionSetupJev(rest);
159
+ return { exitCode: 2, message: usage(), rawOutput: true };
160
+ }
161
+ catch (error) {
162
+ if (error instanceof DecisionAskUsageError) {
163
+ return { exitCode: 2, message: JSON.stringify({ schema: 'aiwg-decision-error/v1', reason: error.reason, message: error.message }, null, 2), rawOutput: true };
164
+ }
165
+ return { exitCode: 1, message: error instanceof Error ? error.message : String(error) };
166
+ }
167
+ }
168
+ export const decisionHandler = {
169
+ id: 'decision',
170
+ name: 'Decision Driver',
171
+ description: 'Inspect, validate and run governed decision classification workflows',
172
+ category: 'utility',
173
+ aliases: [],
174
+ help: async () => ({ exitCode: 0, message: usage(), rawOutput: true }),
175
+ execute: executeDecision,
176
+ };
177
+ //# sourceMappingURL=decision.js.map
@@ -0,0 +1,154 @@
1
+ function jsonResult(value, exitCode = 0) {
2
+ return { exitCode, message: JSON.stringify(value, null, 2), rawOutput: true };
3
+ }
4
+ function usage() {
5
+ return [
6
+ 'Usage: aiwg gates <validate|evaluate|show|list> [options]',
7
+ '',
8
+ 'Commands:',
9
+ ' validate <pack|binding|report> <path> [--pack-dir <dir>] [--aiwg-root <dir>]',
10
+ ' evaluate --binding <file> --metrics <file> --holdout <file> [--upstream <file>] --now <iso> --trusted-binding-digest <sha256> [--pack-dir <dir>] [--aiwg-root <dir>]',
11
+ ' show <id> [--pack-dir <dir>] [--aiwg-root <dir>]',
12
+ ' list [--namespace <ns>] [--rule [<rule-id>]] [--pack-dir <dir>] [--aiwg-root <dir>]',
13
+ '',
14
+ 'Packs live in <bundle>/gate-packs/*.gatepack.yaml|json and register as',
15
+ 'aiwg:<bundle>/<name> (shipped).',
16
+ 'Single files and bundle directories passed via --pack-dir are always',
17
+ 'project: packs; aiwg: is reserved for the installed tree, and a --pack-dir',
18
+ 'manifest claiming a shipped or installed bundle id refuses. An --aiwg-root',
19
+ 'override is untrusted:',
20
+ 'list/show/validate still inspect it, but evaluate refuses it.',
21
+ 'Evaluation is pure and offline: pass --now <iso> for the fake clock.',
22
+ 'The holdout and upstream files must already carry their sealed digests,',
23
+ 'and --upstream is required whenever the binding observes an',
24
+ 'upstream-ceiling gate. Project floors load from .aiwg/aiwg.config in the',
25
+ 'cwd (unreadable or invalid config refuses evaluation). Reports printed',
26
+ "here carry attestation: 'offline-cli'.",
27
+ ].join('\n');
28
+ }
29
+ function takeOption(args, name) {
30
+ const index = args.indexOf(name);
31
+ if (index < 0)
32
+ return undefined;
33
+ return args[index + 1];
34
+ }
35
+ function takeAllOptions(args, name) {
36
+ const values = [];
37
+ for (let index = 0; index < args.length; index += 1) {
38
+ if (args[index] === name && index + 1 < args.length)
39
+ values.push(args[index + 1]);
40
+ }
41
+ return values;
42
+ }
43
+ function driverOptions(ctx) {
44
+ // The shipped root resolves from the installed framework location, never
45
+ // from the cwd: any checkout could otherwise impersonate the aiwg:
46
+ // namespace. An explicit --aiwg-root is kept for inspection only; packs
47
+ // loaded from it are marked untrusted and refused by evaluate.
48
+ const aiwgRoot = takeOption(ctx.args, '--aiwg-root') ?? ctx.frameworkRoot;
49
+ const packDirs = takeAllOptions(ctx.args, '--pack-dir');
50
+ return { cwd: ctx.cwd, aiwgRoot, ...(packDirs.length ? { packDirs } : {}) };
51
+ }
52
+ /** Positional args with each known value-flag and its value skipped in order. */
53
+ function positionals(args, skipCommand) {
54
+ const valueFlags = new Set(['--pack-dir', '--aiwg-root', '--binding', '--metrics', '--holdout', '--upstream', '--now', '--namespace', '--rule', '--trusted-binding-digest']);
55
+ const found = [];
56
+ let skipNext = false;
57
+ for (let index = 0; index < args.length; index += 1) {
58
+ const arg = args[index];
59
+ if (skipNext) {
60
+ skipNext = false;
61
+ continue;
62
+ }
63
+ if (valueFlags.has(arg)) {
64
+ skipNext = true;
65
+ continue;
66
+ }
67
+ if (arg.startsWith('-'))
68
+ continue;
69
+ if (arg === skipCommand && index === 0)
70
+ continue;
71
+ found.push(arg);
72
+ }
73
+ return found;
74
+ }
75
+ async function executeGates(ctx) {
76
+ const [command] = ctx.args;
77
+ try {
78
+ if (!command || command === '--help' || command === '-h')
79
+ return { exitCode: 0, message: usage(), rawOutput: true };
80
+ const { validateGateFile, evaluateGatesFromFiles, showGatePack, listGatePacks, packsReferencedByRule, } = await import('../../gates/driver.js');
81
+ const options = driverOptions(ctx);
82
+ if (command === 'validate') {
83
+ const args = positionals(ctx.args, 'validate');
84
+ const target = (args[0] ?? '');
85
+ const targetPath = args[1] ?? '';
86
+ if (!['pack', 'binding', 'report', 'auto'].includes(target) || !targetPath) {
87
+ return { exitCode: 2, message: `gates validate requires <pack|binding|report|auto> <path>\n\n${usage()}`, rawOutput: true };
88
+ }
89
+ const result = await validateGateFile(target === 'auto' ? 'auto' : target, targetPath, options);
90
+ return jsonResult(result, result.valid ? 0 : 2);
91
+ }
92
+ if (command === 'evaluate') {
93
+ const binding = takeOption(ctx.args, '--binding');
94
+ const metrics = takeOption(ctx.args, '--metrics');
95
+ const holdout = takeOption(ctx.args, '--holdout');
96
+ const upstream = takeOption(ctx.args, '--upstream');
97
+ const now = takeOption(ctx.args, '--now');
98
+ const trustedBindingDigest = takeOption(ctx.args, '--trusted-binding-digest');
99
+ if (!binding || !metrics || !holdout || !now || !trustedBindingDigest) {
100
+ return { exitCode: 2, message: `gates evaluate requires --binding <file> --metrics <file> --holdout <file> --now <iso> --trusted-binding-digest <sha256:..>\n\n${usage()}`, rawOutput: true };
101
+ }
102
+ if (!/^sha256:[0-9a-f]{64}$/.test(trustedBindingDigest)) {
103
+ return { exitCode: 2, message: `gates evaluate requires --trusted-binding-digest <sha256:..> (64 hex chars)\n\n${usage()}`, rawOutput: true };
104
+ }
105
+ const report = await evaluateGatesFromFiles({
106
+ bindingPath: binding, metricsPath: metrics, holdoutPath: holdout,
107
+ ...(upstream ? { upstreamPath: upstream } : {}), now,
108
+ trustedBindingDigest: trustedBindingDigest,
109
+ }, options);
110
+ return jsonResult(report, 0);
111
+ }
112
+ if (command === 'show') {
113
+ const id = positionals(ctx.args, 'show')[0];
114
+ if (!id)
115
+ return { exitCode: 2, message: `gates show requires <id>\n\n${usage()}`, rawOutput: true };
116
+ return jsonResult(showGatePack(id, options), 0);
117
+ }
118
+ if (command === 'list') {
119
+ const namespace = takeOption(ctx.args, '--namespace');
120
+ const ruleIndex = ctx.args.indexOf('--rule');
121
+ const ruleValue = ruleIndex >= 0 ? ctx.args[ruleIndex + 1] : undefined;
122
+ const rule = ruleIndex >= 0 ? (ruleValue && !ruleValue.startsWith('-') ? ruleValue : null) : undefined;
123
+ const packs = listGatePacks({ ...options, ...(namespace ? { namespace } : {}) });
124
+ if (rule === undefined)
125
+ return jsonResult({ schema: 'aiwg-gates-list/v1', packs }, 0);
126
+ if (rule === null) {
127
+ return jsonResult({
128
+ schema: 'aiwg-gates-list/v1', packs,
129
+ enforcedBy: { status: 'stub', note: 'Rule enforcedBy coverage is tracked in #2839; rules without enforcedBy remain guidance.' },
130
+ }, 0);
131
+ }
132
+ const referenced = new Set(await packsReferencedByRule(rule, options));
133
+ return jsonResult({
134
+ schema: 'aiwg-gates-list/v1', rule,
135
+ packs: packs.filter(pack => referenced.has(pack.id)),
136
+ enforcedBy: { status: 'stub', note: 'Rule enforcedBy coverage is tracked in #2839.' },
137
+ }, 0);
138
+ }
139
+ return { exitCode: 2, message: usage(), rawOutput: true };
140
+ }
141
+ catch (error) {
142
+ return { exitCode: 1, message: error instanceof Error ? error.message : String(error) };
143
+ }
144
+ }
145
+ export const gatesHandler = {
146
+ id: 'gates',
147
+ name: 'Gates',
148
+ description: 'Validate, evaluate and inspect declarative gate packs offline',
149
+ category: 'utility',
150
+ aliases: [],
151
+ help: async () => ({ exitCode: 0, message: usage(), rawOutput: true }),
152
+ execute: executeGates,
153
+ };
154
+ //# sourceMappingURL=gates.js.map
@@ -73,12 +73,14 @@ import { installationHandler } from './installation.js';
73
73
  import { uhpHandler } from './uhp.js';
74
74
  import { schemaHandler } from './schema.js';
75
75
  import { datasetHandler } from './dataset.js';
76
+ import { decisionHandler } from './decision.js';
77
+ import { gatesHandler } from './gates.js';
76
78
  // Re-export individual handlers
77
79
  export {
78
80
  // Maintenance
79
81
  helpHandler, versionHandler, authHandler, doctorHandler, contextFirewallHandler, updateHandler, refreshHandler, regenerateHandler, workspaceContextHandler,
80
82
  // Framework management
81
- useHandler, listHandler, removeHandler, promoteHandler, installHandler, packagesHandler, marketplaceHandler, initHandler, setupHandler, setupGenerateHandler, setupRunHandler, setupValidateHandler, issueHandler, issueAuditHandler, runHandler, jobHandler, costReportHandler, evidenceHandler, artifactVerifyHandler, outputModeHandler, writerProfileHandler, botHandoffHandler, buildVerifyHandler, writingHandler, schemaHandler, datasetHandler, installationHandler,
83
+ useHandler, listHandler, removeHandler, promoteHandler, installHandler, packagesHandler, marketplaceHandler, initHandler, setupHandler, setupGenerateHandler, setupRunHandler, setupValidateHandler, issueHandler, issueAuditHandler, runHandler, jobHandler, costReportHandler, evidenceHandler, artifactVerifyHandler, outputModeHandler, writerProfileHandler, botHandoffHandler, buildVerifyHandler, writingHandler, schemaHandler, datasetHandler, decisionHandler, gatesHandler, installationHandler,
82
84
  // Project
83
85
  newBundleHandler, quickrefHandler, newProjectHandler, sessionHandler, sessionsHandler,
84
86
  // Workspace
@@ -169,6 +171,8 @@ export const allHandlers = [
169
171
  writingHandler,
170
172
  schemaHandler,
171
173
  datasetHandler,
174
+ decisionHandler,
175
+ gatesHandler,
172
176
  // Workspace management
173
177
  ...workspaceHandlers,
174
178
  // Subcommand handlers (MCP, catalog, index, skills)
@@ -64,6 +64,7 @@ Options:
64
64
  --provider-home <path> Override the provider home root (testing/portable homes)
65
65
  --codex-root <path> Explicitly authorize a shared Codex sessions/export root
66
66
  --omp-root <path> Explicitly authorize an OMP profile sessions root
67
+ --muse-root <path> Explicitly authorize a Muse Code sessions root
67
68
  --dsh-root <path> Explicitly authorize a DeepSeek Harness sessions root
68
69
  --confirm, --yes Confirm a persistent discovered batch import
69
70
  --lock-wait-ms <n> Maximum import-lease wait (default 5000)
@@ -1172,6 +1173,8 @@ async function discoverWorkspace(ctx, args) {
1172
1173
  : undefined,
1173
1174
  ompRoot: args.values.has('--omp-root')
1174
1175
  ? resolve(ctx.cwd, args.values.get('--omp-root')) : undefined,
1176
+ museRoot: args.values.has('--muse-root')
1177
+ ? resolve(ctx.cwd, args.values.get('--muse-root')) : undefined,
1175
1178
  dshRoot: args.values.has('--dsh-root')
1176
1179
  ? resolve(ctx.cwd, args.values.get('--dsh-root')) : undefined,
1177
1180
  codexRoot: args.values.has('--codex-root')
@@ -1439,14 +1442,14 @@ function providerDisposition(provider) {
1439
1442
  }
1440
1443
  if (provider === 'muse') {
1441
1444
  return {
1442
- provider, disposition: 'manual-only', operationalState: 'available',
1443
- supportedOperations: ['inspect', 'stream'],
1444
- acquisitionModes: ['manual-export'],
1445
- reasonCode: 'MANUAL_SOURCE_SELECTION_REQUIRED',
1446
- remediation: 'Run `muse export --session <id-or-session.jsonl>` and explicitly select the trajectory JSON; sessions live under $XDG_DATA_HOME/muse/sessions, but that native log format is internal, so auto-discover is unsupported.',
1445
+ provider, disposition: 'implemented', operationalState: 'available',
1446
+ supportedOperations: ['discover', 'inspect', 'stream'],
1447
+ acquisitionModes: ['manual-export', 'jsonl'],
1448
+ reasonCode: null,
1449
+ remediation: 'Import an explicit `muse export` trajectory JSON, or pass `--muse-root` with an explicitly authorized $XDG_DATA_HOME/muse/sessions root for evidence-gated native discovery.',
1447
1450
  evidence: {
1448
1451
  adapterVersion: MUSE_ADAPTER_VERSION,
1449
- verifiedAt: '2026-09-24',
1452
+ verifiedAt: '2026-10-02',
1450
1453
  documentation: 'docs/providers/muse-sessions.md',
1451
1454
  },
1452
1455
  };
@@ -1559,7 +1562,7 @@ function parseArgs(argv) {
1559
1562
  '--entity', '--sensitivity', '--extraction-state', '--page-size', '--max-documents',
1560
1563
  '--state', '--reviewer', '--reason', '--policy-version', '--min-confidence',
1561
1564
  '--consumer', '--actor-class', '--reason-code', '--dependent-action', '--basis',
1562
- '--manifest', '--provider-home', '--codex-root', '--omp-root', '--dsh-root', '--lock-wait-ms', '--min-coverage', '--gap',
1565
+ '--manifest', '--provider-home', '--codex-root', '--omp-root', '--muse-root', '--dsh-root', '--lock-wait-ms', '--min-coverage', '--gap',
1563
1566
  '--inactivity-threshold',
1564
1567
  '--control-events',
1565
1568
  '--session', '--status', '--actor', '--group-by',
@@ -29,6 +29,13 @@ const REGISTRY_ID_TO_USE_NAME = {
29
29
  'research-complete': 'research',
30
30
  'forensics-complete': 'forensics',
31
31
  };
32
+ function flagValue(args, flag) {
33
+ const index = args.indexOf(flag);
34
+ if (index >= 0)
35
+ return args[index + 1];
36
+ const prefix = `${flag}=`;
37
+ return args.find((arg) => arg.startsWith(prefix))?.slice(prefix.length);
38
+ }
32
39
  /**
33
40
  * Read the installed frameworks from the on-disk registry.
34
41
  */
@@ -519,6 +526,19 @@ export const doctorHandler = {
519
526
  catch (error) {
520
527
  console.log(`\n── Grok Build native extensions ──\n ⚠ unable to audit: ${error instanceof Error ? error.message : String(error)}`);
521
528
  }
529
+ try {
530
+ const projectDir = ctx.cwd || process.cwd();
531
+ const provider = flagValue(ctx.args, '--provider');
532
+ const requested = provider === 'muse' || ctx.args.includes('--all-providers');
533
+ const { inspectMuseNative, isMuseDeployedProject } = await import('../../mcp/muse-native-config.mjs');
534
+ if (requested || await isMuseDeployedProject(projectDir)) {
535
+ const native = await inspectMuseNative({ projectDir });
536
+ console.log(`\n── Muse Code native extensions ──\n CLI: ${native.cli}\n Hooks: ${native.hooks}\n MCP: ${native.mcp}`);
537
+ }
538
+ }
539
+ catch (error) {
540
+ console.log(`\n── Muse Code native extensions ──\n ⚠ unable to audit: ${error instanceof Error ? error.message : String(error)}`);
541
+ }
522
542
  try {
523
543
  const { buildAgentSkillsDoctorSection } = await import('../../skills/doctor.js');
524
544
  const section = buildAgentSkillsDoctorSection(ctx.cwd || process.cwd());
@@ -17,6 +17,7 @@ import { validateAuthorization, } from '../policy/authorization.js';
17
17
  import { projectAiwgPath, projectControlPath, resolveProjectAiwgDir, } from './project-artifacts.js';
18
18
  import { defaultThreatAssessmentConfig, validateThreatAssessmentConfig, } from '../security/threat-assessment-config.js';
19
19
  import { defaultArtifactOutputs, validateArtifactOutputs } from '../artifacts/output-policy.js';
20
+ import { validateGatesConfig } from '../gates/floors.js';
20
21
  import { validateUhpConfig } from '../uhp/config.js';
21
22
  const CONFIG_FILENAME = 'aiwg.config';
22
23
  /**
@@ -174,7 +175,7 @@ export function resolveDelivery(delivery) {
174
175
  * should tune. Conservative 10 default.
175
176
  * - grokbot: desktop multi-agent; conservative 4 until native evidence.
176
177
  * - grok-build: experimental; conservative 4 until Wave 2/3 evidence.
177
- * - muse: experimental; conservative 4 until Wave 2/3 evidence.
178
+ * - muse: stable; conservative 4 until native concurrency evidence.
178
179
  * - unknown: conservative 4 default.
179
180
  */
180
181
  export const PROVIDER_PARALLELISM_DEFAULTS = {
@@ -192,7 +193,7 @@ export const PROVIDER_PARALLELISM_DEFAULTS = {
192
193
  // Desktop multi-agent; conservative until native concurrency evidence exists.
193
194
  grokbot: { max_parallel_subagents: 4, max_parallel_ralph_loops: 2, max_parallel_mc_missions: 4 },
194
195
  'grok-build': { max_parallel_subagents: 4, max_parallel_ralph_loops: 2, max_parallel_mc_missions: 4 },
195
- // Muse Code is experimental; conservative until Wave 2/3 native concurrency evidence (#235).
196
+ // Muse Code is stable; keep conservative defaults until native concurrency evidence (#235).
196
197
  muse: { max_parallel_subagents: 4, max_parallel_ralph_loops: 2, max_parallel_mc_missions: 4 },
197
198
  // Conservative defaults for remaining stable/experimental harnesses (#249).
198
199
  openhuman: { max_parallel_subagents: 4, max_parallel_ralph_loops: 2, max_parallel_mc_missions: 4 },
@@ -882,6 +883,15 @@ export async function readAiwgConfig(projectDir) {
882
883
  const uhpErrors = validateUhpConfig(parsed.uhp);
883
884
  if (uhpErrors.length > 0)
884
885
  throw new Error(`Invalid .aiwg/aiwg.config:\n${uhpErrors.join('\n')}`);
886
+ // Gates errors warn (non-fatal): an invalid `gates` section must not brick
887
+ // every command that reads the config. Gates entry points (`aiwg gates
888
+ // evaluate`, binding resolution with floors) re-validate strictly via
889
+ // `validateGatesConfig` and fail closed there; `writeAiwgConfig` below
890
+ // stays strict so bad policy is never persisted.
891
+ const gatesErrors = validateGatesConfig(parsed.gates);
892
+ if (gatesErrors.length > 0) {
893
+ console.warn(`Invalid gates section in .aiwg/aiwg.config (ignored):\n${gatesErrors.join('\n')}`);
894
+ }
885
895
  return parsed;
886
896
  }
887
897
  /**
@@ -903,6 +913,9 @@ export async function writeAiwgConfig(projectDir, config) {
903
913
  const uhpErrors = validateUhpConfig(config.uhp);
904
914
  if (uhpErrors.length > 0)
905
915
  throw new Error(`Invalid .aiwg/aiwg.config:\n${uhpErrors.join('\n')}`);
916
+ const gatesErrors = validateGatesConfig(config.gates);
917
+ if (gatesErrors.length > 0)
918
+ throw new Error(`Invalid .aiwg/aiwg.config:\n${gatesErrors.join('\n')}`);
906
919
  const localPath = getConfigPath(projectDir);
907
920
  const artifactDir = resolveProjectAiwgDir(projectDir);
908
921
  const artifactPath = join(artifactDir, CONFIG_FILENAME);
@@ -76,6 +76,31 @@ export function applyPrimitiveAcceptance(definition, policy, observation) {
76
76
  }
77
77
  return routed(observation, policy, policy.defaultRoute, 'default', evidence);
78
78
  }
79
+ export function applyTargetAcceptance(definition, target, observation) {
80
+ if (observation.status !== 'success')
81
+ return observation;
82
+ if (target.acceptance.mode === 'typed-value')
83
+ return observation;
84
+ if (target.acceptance.mode === 'primitive-policy')
85
+ return applyPrimitiveAcceptance(definition, target.acceptance, observation);
86
+ if (!observation.uncertainty || observation.uncertainty.confidence === null)
87
+ return abstained('missing-confidence', observation);
88
+ if (observation.uncertainty.profile !== target.acceptance.profile)
89
+ return abstained('confidence-profile-mismatch', observation);
90
+ // Compare on the probability scale: minimumBps / 10_000 is the exact double for the decimal threshold,
91
+ // whereas confidence * 10_000 can round below it (0.7 * 10_000 === 6999.999999999999).
92
+ return observation.uncertainty.confidence < target.acceptance.minimumBps / 10_000
93
+ ? abstained('low-confidence', observation) : observation;
94
+ }
95
+ // Mirrors the evaluator's failure observation: an abstention keeps provenance but drops the value.
96
+ function abstained(reason, observation) {
97
+ return {
98
+ status: 'abstained', reason, uncertainty: observation.uncertainty ?? null,
99
+ actualModel: observation.actualModel ?? null,
100
+ usage: observation.usage ?? { inputTokens: null, outputTokens: null, costUsd: null },
101
+ requestId: observation.requestId ?? null,
102
+ };
103
+ }
79
104
  function deriveEvidence(definition, observation) {
80
105
  const values = {};
81
106
  const add = (metric, value, normalized, provenance, calibrationRef = null) => {
@@ -34,6 +34,7 @@ export class JevDecisionAdapter {
34
34
  pinnedFetch;
35
35
  now;
36
36
  region;
37
+ observeResponseBody;
37
38
  constructor(options = {}) {
38
39
  this.endpoint = options.endpoint ?? JEV_ENDPOINT;
39
40
  this.fetchImpl = options.fetch ?? fetch;
@@ -42,6 +43,23 @@ export class JevDecisionAdapter {
42
43
  this.pinnedFetch = options.pinnedFetch ?? pinnedHttpsFetch;
43
44
  this.now = options.now ?? Date.now;
44
45
  this.region = options.region?.trim() ? options.region : null;
46
+ this.observeResponseBody = options.observeResponseBody;
47
+ }
48
+ observeResponse(body) {
49
+ if (!this.observeResponseBody)
50
+ return;
51
+ let parsed = {};
52
+ try {
53
+ parsed = asRecord(parseBoundedJson(body));
54
+ }
55
+ catch { /* Invalid answers still have bounded body evidence. */ }
56
+ let usage = emptyUsage();
57
+ try {
58
+ usage = normalizeUsage(parsed.usage);
59
+ }
60
+ catch { /* Missing usage is unknown, including on auth errors. */ }
61
+ this.observeResponseBody(body, { actualModel: typeof parsed.model === 'string' && parsed.model ? parsed.model : null,
62
+ usage });
45
63
  }
46
64
  async capabilities() {
47
65
  return {
@@ -167,6 +185,7 @@ export class JevDecisionAdapter {
167
185
  if (!response.ok) {
168
186
  try {
169
187
  const errorBody = await readBoundedBody(response, signal);
188
+ this.observeResponse(errorBody);
170
189
  if (!metadata.requestId && response.headers.get('content-type')?.includes('json')) {
171
190
  let parsedError;
172
191
  try {
@@ -198,6 +217,7 @@ export class JevDecisionAdapter {
198
217
  let parsed;
199
218
  try {
200
219
  const encoded = await readBoundedBody(response, signal);
220
+ this.observeResponse(encoded);
201
221
  parsed = parseBoundedJson(encoded);
202
222
  if (request.signal.aborted)
203
223
  return externalInterruption(request, 'terminal-response', metadata);
@@ -304,7 +324,7 @@ export class JevDecisionAdapter {
304
324
  }
305
325
  if (!response.ok) {
306
326
  try {
307
- await readBoundedBody(response, signal);
327
+ this.observeResponse(await readBoundedBody(response, signal));
308
328
  }
309
329
  catch {
310
330
  return batchFailure(requests, failure('invalid-output', metadata));
@@ -313,7 +333,9 @@ export class JevDecisionAdapter {
313
333
  return batchFailure(requests, failure(mapStatus(response.status), { ...metadata, ...(retryAfterMs === null ? {} : { retryAfterMs }) }));
314
334
  }
315
335
  try {
316
- const parsed = parseBoundedJson(await readBoundedBody(response, signal));
336
+ const encoded = await readBoundedBody(response, signal);
337
+ this.observeResponse(encoded);
338
+ const parsed = parseBoundedJson(encoded);
317
339
  const normalized = normalizeBatchResponse(requests, parsed).map(answer => ({
318
340
  questionId: answer.questionId,
319
341
  observation: { ...answer.observation, ...metadata },
@@ -431,7 +453,11 @@ function normalizeAnswer(request, body, questionId) {
431
453
  throw new DecisionValidationError('Score legend does not match declared levels');
432
454
  });
433
455
  const mean = Object.entries(distribution).reduce((sum, [index, probability]) => sum + Number(index) * probability, 0);
434
- if (Math.abs(mean - answer.score) > 0.02)
456
+ // Jev reports probabilities and the score to two decimals, so each probability carries up to
457
+ // 0.005 of rounding error, weighted by its index, plus 0.005 on the score itself.
458
+ const indexSum = Object.keys(distribution).reduce((sum, index) => sum + Number(index), 0);
459
+ const tolerance = Math.max(0.02, 0.005 * indexSum + 0.005) + 1e-9;
460
+ if (Math.abs(mean - answer.score) > tolerance)
435
461
  throw new DecisionValidationError('Score is not the distribution weighted mean');
436
462
  validateDecisionValue(request.definition, answer.score);
437
463
  return success(answer.score, model, usage, uncertainty(answer.confidence, distribution, 'typesafe-distribution-v1'));