@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.
- package/agentic/code/providers/capability-matrix.yaml +2 -1
- package/dist/src/artifacts/discovery-shadow-evaluator.js +177 -0
- package/dist/src/artifacts/discovery-shadow.js +195 -0
- package/dist/src/artifacts/index-builder.js +47 -9
- package/dist/src/artifacts/query-engine.js +94 -1
- package/dist/src/artifacts/types.js +1 -0
- package/dist/src/cli/handlers/decision.js +177 -0
- package/dist/src/cli/handlers/gates.js +154 -0
- package/dist/src/cli/handlers/index.js +5 -1
- package/dist/src/cli/handlers/sessions.js +10 -7
- package/dist/src/cli/handlers/utilities.js +20 -0
- package/dist/src/config/aiwg-config.js +15 -2
- package/dist/src/decision/acceptance.js +25 -0
- package/dist/src/decision/adapters/jev.js +29 -3
- package/dist/src/decision/ask.js +352 -0
- package/dist/src/decision/calibration/registry.js +9 -0
- package/dist/src/decision/compose.js +74 -15
- package/dist/src/decision/context-live-promotion.js +344 -0
- package/dist/src/decision/context-live-qualification.js +591 -0
- package/dist/src/decision/context-pruning.js +847 -0
- package/dist/src/decision/driver.js +432 -0
- package/dist/src/decision/egress-live-qualification.js +874 -0
- package/dist/src/decision/ensemble/contract.js +64 -1
- package/dist/src/decision/ensemble/index.js +1 -0
- package/dist/src/decision/ensemble/report.js +8 -42
- package/dist/src/decision/ensemble/runtime.js +571 -0
- package/dist/src/decision/ensemble-study/artifacts.js +85 -0
- package/dist/src/decision/ensemble-study/calibration.js +443 -0
- package/dist/src/decision/ensemble-study/corpus.js +229 -0
- package/dist/src/decision/ensemble-study/multifact-artifacts.js +23 -0
- package/dist/src/decision/ensemble-study/multifact-study.js +596 -0
- package/dist/src/decision/ensemble-study/multifact.js +241 -0
- package/dist/src/decision/ensemble-study/protocol.js +77 -0
- package/dist/src/decision/ensemble-study/score.js +462 -0
- package/dist/src/decision/ensemble-study/staged.js +87 -0
- package/dist/src/decision/ensemble-study/statistics.js +84 -0
- package/dist/src/decision/evaluate.js +227 -14
- package/dist/src/decision/export.js +4 -0
- package/dist/src/decision/feature-export.js +1074 -0
- package/dist/src/decision/graph-index.js +16 -0
- package/dist/src/decision/graph-live-qualification.js +1041 -0
- package/dist/src/decision/graph-live-workload.js +245 -0
- package/dist/src/decision/graph-skill-bridge.js +21 -3
- package/dist/src/decision/heldout/calibration.js +140 -0
- package/dist/src/decision/heldout/collector.js +430 -0
- package/dist/src/decision/heldout/contract.js +309 -0
- package/dist/src/decision/heldout/d29-generator-ids.js +12 -0
- package/dist/src/decision/heldout/d29-passage-baseline-v2.js +93 -0
- package/dist/src/decision/heldout/d29-passage-baseline-v3.js +100 -0
- package/dist/src/decision/heldout/d29-passage-baseline.js +66 -0
- package/dist/src/decision/heldout/d29-pools-v8.js +575 -0
- package/dist/src/decision/heldout/d29-pools.js +288 -0
- package/dist/src/decision/heldout/d29-v6.js +169 -0
- package/dist/src/decision/heldout/d29-v7.js +202 -0
- package/dist/src/decision/heldout/d29-v8.js +586 -0
- package/dist/src/decision/heldout/generator-registry.js +67 -0
- package/dist/src/decision/heldout/generators.js +696 -0
- package/dist/src/decision/heldout/index.js +8 -0
- package/dist/src/decision/heldout/journal.js +346 -0
- package/dist/src/decision/heldout/study-generators.js +64 -0
- package/dist/src/decision/heldout/types.js +2 -0
- package/dist/src/decision/host-policy.js +61 -0
- package/dist/src/decision/index.js +13 -0
- package/dist/src/decision/interop-profile.v1.json +112 -0
- package/dist/src/decision/interop.js +653 -0
- package/dist/src/decision/issue-triage/index.js +3 -0
- package/dist/src/decision/issue-triage/runtime.js +1069 -0
- package/dist/src/decision/issue-triage/types.js +2 -0
- package/dist/src/decision/jev-credentials.js +121 -0
- package/dist/src/decision/patterns/catalog.js +28 -8
- package/dist/src/decision/patterns/playground.js +28 -11
- package/dist/src/decision/patterns/specs.js +19 -10
- package/dist/src/decision/preprocessed-evidence.js +724 -0
- package/dist/src/decision/qualification/capture.js +8 -2
- package/dist/src/decision/qualification/gate-evidence.js +19 -4
- package/dist/src/decision/qualification/quality.js +57 -49
- package/dist/src/decision/qualification/release.js +76 -3
- package/dist/src/decision/qualification/runner.js +5 -2
- package/dist/src/decision/routing/contract.js +175 -0
- package/dist/src/decision/routing/index.js +5 -0
- package/dist/src/decision/routing/report.js +216 -0
- package/dist/src/decision/routing/runtime.js +685 -0
- package/dist/src/decision/routing/types.js +2 -0
- package/dist/src/decision/sdlc-screening.js +1157 -0
- package/dist/src/decision/sensitivity/comparative.js +154 -0
- package/dist/src/decision/sensitivity/contract.js +218 -0
- package/dist/src/decision/sensitivity/index.js +5 -0
- package/dist/src/decision/sensitivity/runtime.js +912 -0
- package/dist/src/decision/sensitivity/types.js +2 -0
- package/dist/src/decision/validate.js +26 -0
- package/dist/src/extensions/commands/definitions.js +38 -0
- package/dist/src/extensions/manifest.js +44 -0
- package/dist/src/gates/discovery.js +315 -0
- package/dist/src/gates/driver.js +388 -0
- package/dist/src/gates/evaluate.js +719 -0
- package/dist/src/gates/floors.js +255 -0
- package/dist/src/gates/index.js +17 -0
- package/dist/src/gates/providers/evidence.js +38 -0
- package/dist/src/gates/providers/index.js +21 -0
- package/dist/src/gates/providers/isolated-child-source.js +446 -0
- package/dist/src/gates/providers/loader.js +1069 -0
- package/dist/src/gates/providers/paired.js +52 -0
- package/dist/src/gates/providers/proportion.js +60 -0
- package/dist/src/gates/providers/registry.js +40 -0
- package/dist/src/gates/providers/scalar.js +41 -0
- package/dist/src/gates/providers/screening.js +53 -0
- package/dist/src/gates/providers/types.js +25 -0
- package/dist/src/gates/registry.js +1003 -0
- package/dist/src/gates/report.js +102 -0
- package/dist/src/gates/schema.js +56 -0
- package/dist/src/gates/stats/binomial.js +26 -0
- package/dist/src/gates/stats/bootstrap.js +62 -0
- package/dist/src/gates/stats/clopper-pearson.js +95 -0
- package/dist/src/gates/stats/digest.js +26 -0
- package/dist/src/gates/stats/error.js +5 -0
- package/dist/src/gates/stats/index.js +9 -0
- package/dist/src/gates/stats/normal.js +35 -0
- package/dist/src/gates/stats/paired.js +120 -0
- package/dist/src/gates/stats/splits.js +30 -0
- package/dist/src/gates/types.js +8 -0
- package/dist/src/governance/redaction.js +14 -4
- package/dist/src/mcp/muse-native-config.mjs +123 -0
- package/dist/src/mcp/tools/decision.mjs +228 -0
- package/dist/src/mcp/tools/subsystems.mjs +2 -0
- package/dist/src/providers/capability-matrix.yaml +2 -1
- package/dist/src/providers/provider-definitions.js +2 -1
- package/dist/src/sessions/adapters/muse.js +336 -39
- package/dist/src/sessions/batch-import.js +7 -0
- package/dist/src/sessions/workspace-discovery.js +13 -2
- package/package.json +1 -1
- package/schemas/decision/ConformalFrozenOpenData.v2.schema.json +371 -0
- package/schemas/decision/ConformalOpenDataReport.v2.schema.json +421 -0
- package/schemas/decision/ConformalPreregistration.v2.schema.json +529 -0
- package/schemas/decision/ContextPruningCandidate.v1.schema.json +47 -0
- package/schemas/decision/ContextPruningEvaluationReport.v1.schema.json +758 -0
- package/schemas/decision/ContextPruningPreregistration.v1.schema.json +175 -0
- package/schemas/decision/ContextPruningReceipt.v1.schema.json +72 -0
- package/schemas/decision/D17Multifact.v1.schema.json +1851 -0
- package/schemas/decision/D17StudyAnalysis.v1.schema.json +42 -0
- package/schemas/decision/D17StudyAnalysis.v2.schema.json +51 -0
- package/schemas/decision/D17StudyCalibration.v1.schema.json +819 -0
- package/schemas/decision/D17StudyDryRun.v1.schema.json +169 -0
- package/schemas/decision/D17StudyDryRun.v2.schema.json +239 -0
- package/schemas/decision/D17StudyGold.v1.schema.json +108 -0
- package/schemas/decision/D17StudyGuide.v1.schema.json +63 -0
- package/schemas/decision/D17StudyNativeTemplates.v1.schema.json +619 -0
- package/schemas/decision/D17StudyProtocol.v1.schema.json +241 -0
- package/schemas/decision/D17StudyReport.v1.schema.json +902 -0
- package/schemas/decision/D17StudyReport.v2.schema.json +1319 -0
- package/schemas/decision/D17StudyReview.v1.schema.json +175 -0
- package/schemas/decision/D17StudySplits.v1.schema.json +217 -0
- package/schemas/decision/D29Study.v1.schema.json +1903 -0
- package/schemas/decision/D29Study.v2.schema.json +1344 -0
- package/schemas/decision/D29Study.v3.schema.json +609 -0
- package/schemas/decision/D29Study.v4.schema.json +1133 -0
- package/schemas/decision/D29Study.v6.schema.json +1205 -0
- package/schemas/decision/D29Study.v7.schema.json +1471 -0
- package/schemas/decision/D29Study.v8.schema.json +671 -0
- package/schemas/decision/D29Study.v9.schema.json +778 -0
- package/schemas/decision/DecisionComparativeReplayAuditSample.v1.schema.json +137 -0
- package/schemas/decision/DecisionComparativeReplayCorpus.v1.schema.json +258 -0
- package/schemas/decision/DecisionComparativeReplayFreeze.v1.schema.json +74 -0
- package/schemas/decision/DecisionComparativeReplayGold.v1.schema.json +50 -0
- package/schemas/decision/DecisionComparativeReplayGoldAsset.v1.schema.json +37 -0
- package/schemas/decision/DecisionComparativeReplayIntegrity.v1.schema.json +81 -0
- package/schemas/decision/DecisionComparativeReplayMembers.v1.schema.json +52 -0
- package/schemas/decision/DecisionComparativeReplayPreregistration.v1.schema.json +113 -0
- package/schemas/decision/DecisionComparativeReplayReport.v1.schema.json +407 -0
- package/schemas/decision/DecisionComparativeReplayReviewPackets.v1.schema.json +260 -0
- package/schemas/decision/DecisionComparativeReplaySplit.v1.schema.json +84 -0
- package/schemas/decision/DecisionFeatureObservation.v1.schema.json +564 -0
- package/schemas/decision/DecisionFeatureSet.v1.schema.json +155 -0
- package/schemas/decision/DecisionInteropMapping.v1.schema.json +85 -0
- package/schemas/decision/DecisionIssueTriageEvaluationManifest.v1.schema.json +174 -0
- package/schemas/decision/DecisionIssueTriageEvaluationReport.v1.schema.json +280 -0
- package/schemas/decision/DecisionIssueTriagePilotPack.v1.schema.json +114 -0
- package/schemas/decision/DecisionRouteReceipt.v1.schema.json +155 -0
- package/schemas/decision/DecisionRoutingPolicy.v1.schema.json +132 -0
- package/schemas/decision/DecisionRoutingShadowReport.v1.schema.json +170 -0
- package/schemas/decision/DecisionRoutingTask.v1.schema.json +49 -0
- package/schemas/decision/DecisionRuleset.schema.json +8 -2
- package/schemas/decision/DecisionRuleset.v1alpha2.schema.json +8 -2
- package/schemas/decision/DecisionSensitivityPlan.v1.schema.json +191 -0
- package/schemas/decision/DecisionSensitivityReport.v1.schema.json +163 -0
- package/schemas/decision/HeldoutApproval.v1.schema.json +312 -0
- package/schemas/decision/HeldoutAttempt.v1.schema.json +211 -0
- package/schemas/decision/HeldoutBaseline.v1.schema.json +90 -0
- package/schemas/decision/HeldoutCalibrationPhase.v1.schema.json +87 -0
- package/schemas/decision/HeldoutCorpus.v1.schema.json +167 -0
- package/schemas/decision/HeldoutEvent.v1.schema.json +41 -0
- package/schemas/decision/HeldoutFrozen.v1.schema.json +142 -0
- package/schemas/decision/HeldoutPreregistration.v1.schema.json +222 -0
- package/schemas/decision/HeldoutSpendEvent.v1.schema.json +122 -0
- package/schemas/decision/HeldoutSpendHead.v1.schema.json +25 -0
- package/schemas/decision/HeldoutSpendHead.v2.schema.json +46 -0
- package/schemas/decision/HeldoutSummary.v1.schema.json +101 -0
- package/schemas/decision/OpaDecisionLogExport.v1.schema.json +65 -0
- package/schemas/decision/PreprocessedEvidence.v1.schema.json +306 -0
- package/schemas/decision/RulesetResult.v1alpha2.schema.json +175 -0
- package/schemas/decision/SdlcEvidenceScreening.v1.schema.json +499 -0
- package/schemas/decision/SdlcScreeningPreregistration.v1.schema.json +132 -0
- package/schemas/decision/SdlcScreeningPreregistration.v2.schema.json +138 -0
- package/schemas/decision/SdlcScreeningRelease.v1.schema.json +654 -0
- package/schemas/decision/SdlcScreeningRelease.v2.schema.json +654 -0
- package/tools/agents/deploy-agents.mjs +1 -1
- package/tools/agents/providers/base.mjs +40 -18
- package/tools/agents/providers/factory.mjs +10 -2
- package/tools/agents/providers/muse-hooks.mjs +1 -29
- 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: '
|
|
1443
|
-
supportedOperations: ['inspect', 'stream'],
|
|
1444
|
-
acquisitionModes: ['manual-export'],
|
|
1445
|
-
reasonCode:
|
|
1446
|
-
remediation: '
|
|
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-
|
|
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:
|
|
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
|
|
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
|
|
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
|
-
|
|
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'));
|