agentic-workflow-manager 9.11.11 → 9.12.0
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/dist/src/commands/doctor.js +23 -1
- package/dist/src/commands/hooks/claude.js +61 -8
- package/dist/src/commands/hooks/resync.js +1 -1
- package/dist/src/commands/job/index.js +39 -4
- package/dist/src/commands/model-policy/index.js +253 -0
- package/dist/src/commands/plan/index.js +53 -12
- package/dist/src/commands/preflight/checks.js +18 -0
- package/dist/src/commands/watch/apply.js +71 -5
- package/dist/src/commands/watch/supervisor.js +63 -3
- package/dist/src/core/admission/index.js +20 -6
- package/dist/src/core/journal/types.js +21 -5
- package/dist/src/core/model-policy/capabilities-v2.js +152 -0
- package/dist/src/core/model-policy/capabilities.js +7 -1
- package/dist/src/core/model-policy/claude-hook-events.js +120 -0
- package/dist/src/core/model-policy/event-store.js +221 -0
- package/dist/src/core/model-policy/hook-diagnostics.js +94 -0
- package/dist/src/core/model-policy/journal.js +125 -16
- package/dist/src/core/model-policy/local-claude-scope.js +41 -0
- package/dist/src/core/model-policy/local-codex-scope.js +33 -0
- package/dist/src/core/model-policy/local-event-scope.js +13 -0
- package/dist/src/core/model-policy/machine-key.js +56 -0
- package/dist/src/core/model-policy/native-claude-agent-definition.js +82 -0
- package/dist/src/core/model-policy/native-claude-config.js +107 -0
- package/dist/src/core/model-policy/native-claude-machine.js +58 -0
- package/dist/src/core/model-policy/native-claude-transcript.js +80 -0
- package/dist/src/core/model-policy/native-claude.js +41 -0
- package/dist/src/core/model-policy/native-codex-machine.js +116 -0
- package/dist/src/core/model-policy/native-codex-rollout.js +111 -0
- package/dist/src/core/model-policy/native-codex.js +302 -0
- package/dist/src/core/model-policy/native-routing-observe.js +106 -0
- package/dist/src/core/model-policy/native-routing-proof.js +52 -0
- package/dist/src/core/model-policy/native-runtime.js +86 -0
- package/dist/src/core/model-policy/resolve.js +117 -3
- package/dist/src/core/model-policy/selection-policy-digest.js +22 -0
- package/dist/src/core/model-policy/setup-readiness.js +77 -0
- package/dist/src/providers/index.js +3 -1
- package/dist/tests/commands/doctor-routing.test.js +22 -0
- package/dist/tests/commands/doctor.test.js +5 -0
- package/dist/tests/commands/hooks/install.test.js +6 -0
- package/dist/tests/commands/hooks/resync.test.js +3 -0
- package/dist/tests/commands/hooks/status.test.js +12 -1
- package/dist/tests/commands/job/routing-file.test.js +32 -0
- package/dist/tests/commands/job/routing-report-cli.test.js +12 -5
- package/dist/tests/commands/job/routing.test.js +2 -2
- package/dist/tests/commands/model-policy/claude-hook-flow.test.js +104 -0
- package/dist/tests/commands/model-policy/index.test.js +179 -0
- package/dist/tests/commands/plan/index.test.js +2 -2
- package/dist/tests/commands/preflight/preflight.test.js +65 -0
- package/dist/tests/commands/watch/apply.test.js +116 -10
- package/dist/tests/commands/watch/dispatch-routing-identity.test.js +23 -0
- package/dist/tests/core/admission/index.test.js +21 -0
- package/dist/tests/core/model-policy/capabilities-v2.test.js +42 -0
- package/dist/tests/core/model-policy/claude-hook-events.test.js +40 -0
- package/dist/tests/core/model-policy/event-store-claude.test.js +64 -0
- package/dist/tests/core/model-policy/event-store.test.js +151 -0
- package/dist/tests/core/model-policy/hook-diagnostics.test.js +34 -0
- package/dist/tests/core/model-policy/journal.test.js +165 -12
- package/dist/tests/core/model-policy/local-claude-scope.test.js +31 -0
- package/dist/tests/core/model-policy/machine-key.test.js +38 -0
- package/dist/tests/core/model-policy/native-claude-agent-definition.test.js +31 -0
- package/dist/tests/core/model-policy/native-claude-config.test.js +53 -0
- package/dist/tests/core/model-policy/native-claude-machine.test.js +18 -0
- package/dist/tests/core/model-policy/native-claude-transcript.test.js +45 -0
- package/dist/tests/core/model-policy/native-claude.test.js +21 -0
- package/dist/tests/core/model-policy/native-codex-machine.test.js +34 -0
- package/dist/tests/core/model-policy/native-codex-rollout.test.js +44 -0
- package/dist/tests/core/model-policy/native-codex.test.js +133 -0
- package/dist/tests/core/model-policy/native-routing-observe.test.js +60 -0
- package/dist/tests/core/model-policy/native-routing-proof.test.js +30 -0
- package/dist/tests/core/model-policy/native-runtime.test.js +14 -0
- package/dist/tests/core/model-policy/resolve-event.test.js +65 -0
- package/dist/tests/core/model-policy/resolve.test.js +17 -0
- package/dist/tests/core/model-policy/setup-readiness.test.js +83 -0
- package/dist/tests/helpers/routing-approval.js +14 -0
- package/dist/tests/providers/index.test.js +1 -1
- package/package.json +2 -2
- package/prebuilds/darwin-arm64/secure_fs.node +0 -0
- package/prebuilds/darwin-x64/secure_fs.node +0 -0
- package/prebuilds/win32-arm64/secure_fs.node +0 -0
- package/prebuilds/win32-x64/secure_fs.node +0 -0
|
@@ -23,6 +23,8 @@ const store_2 = require("../../core/model-policy/store");
|
|
|
23
23
|
const capabilities_1 = require("../../core/model-policy/capabilities");
|
|
24
24
|
const resolve_1 = require("../../core/model-policy/resolve");
|
|
25
25
|
const journal_1 = require("../../core/model-policy/journal");
|
|
26
|
+
const event_store_1 = require("../../core/model-policy/event-store");
|
|
27
|
+
const local_event_scope_1 = require("../../core/model-policy/local-event-scope");
|
|
26
28
|
const SUPPORTED_SCHEMA = 'compact-slices/v1, compact-slices/v2';
|
|
27
29
|
const MAX_PATH_LENGTH = 4096;
|
|
28
30
|
const MAX_DIAGNOSTICS = 20;
|
|
@@ -145,7 +147,7 @@ function registerPlanCommand(program, deps = { validatePlanFile: validate_1.vali
|
|
|
145
147
|
throw new Error('program must be a Commander command');
|
|
146
148
|
assertDependencies(deps);
|
|
147
149
|
const plan = program.command('plan').description('inspect plan contracts');
|
|
148
|
-
plan.command('resolve <plan-path>').requiredOption('--provider <target>').requiredOption('--runtime-kind <kind>').requiredOption('--runtime-version <version>').requiredOption('--account-scope-digest <sha>').requiredOption('--role <role>').option('--opt-in-v1').option('--slice <id>').option('--lineage <id>').option('--cwd <path>').option('--json').action((planPath, options) => {
|
|
150
|
+
plan.command('resolve <plan-path>').requiredOption('--provider <target>').requiredOption('--runtime-kind <kind>').requiredOption('--runtime-version <version>').requiredOption('--account-scope-digest <sha>').requiredOption('--role <role>').option('--opt-in-v1').option('--slice <id>').option('--lineage <id>').option('--cwd <path>').option('--json').action(async (planPath, options) => {
|
|
149
151
|
assertText(planPath, 'plan path');
|
|
150
152
|
for (const [value, label] of [[options.provider, '--provider'], [options.runtimeKind, '--runtime-kind'], [options.runtimeVersion, '--runtime-version'], [options.accountScopeDigest, '--account-scope-digest'], [options.role, '--role']])
|
|
151
153
|
assertText(value, label);
|
|
@@ -184,11 +186,31 @@ function registerPlanCommand(program, deps = { validatePlanFile: validate_1.vali
|
|
|
184
186
|
if (!roles.includes(options.role))
|
|
185
187
|
throw new Error('--role is invalid');
|
|
186
188
|
const runtime = (0, capabilities_1.validateRuntimeKey)({ target: options.provider, kind: options.runtimeKind, version: options.runtimeVersion, accountScopeDigest: options.accountScopeDigest });
|
|
189
|
+
let eventFacts;
|
|
190
|
+
if (report.schema === 'compact-slices/v2' && (runtime.target === 'codex' || runtime.target === 'claude-code')) {
|
|
191
|
+
const stored = (deps.readStoredEventReceipt ?? event_store_1.readStoredEventReceipt)(runtime);
|
|
192
|
+
if (stored.state === 'invalid') {
|
|
193
|
+
process.stdout.write(`${JSON.stringify({ state: 'blocked', diagnostics: [{ code: 'ROUTING_CAPABILITY_INVALID', message: 'Native event receipt is invalid.' }] })}\n`);
|
|
194
|
+
process.exitCode = 2;
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (stored.state === 'present') {
|
|
198
|
+
const local = await (runtime.target === 'codex' && deps.queryLocalCodexScope ? deps.queryLocalCodexScope(path_1.default.resolve(cwd), runtime.kind) : (0, local_event_scope_1.queryLocalEventScope)(path_1.default.resolve(cwd), runtime.target, runtime.kind));
|
|
199
|
+
if (local.state !== 'current' || JSON.stringify(local.scope.runtime) !== JSON.stringify(runtime)) {
|
|
200
|
+
const reason = local.state === 'unverified' ? local.reason : 'RUNTIME_IDENTITY_MISMATCH';
|
|
201
|
+
process.stdout.write(`${JSON.stringify({ state: 'blocked', diagnostics: [{ code: `ROUTING_${reason}`, message: `Current local ${runtime.target} scope cannot be verified (${reason}); run awm model-policy setup --provider ${runtime.target} --json.` }] })}\n`);
|
|
202
|
+
process.exitCode = 2;
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
eventFacts = { eventReceipt: stored.receipt, eventScope: local.scope };
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const circuitIncidents = report.schema === 'compact-slices/v2' ? journalObservation(cwd).journalState?.routingIncidents : undefined;
|
|
187
209
|
const result = report.schema === 'compact-slices/v1' && !options.optInV1 ? (0, resolve_1.resolveDispatch)({ plan: report, role: options.role, policy: undefined, capabilities: undefined, runtime, now: new Date(), optInV1: false }) : (() => {
|
|
188
210
|
const policy = (deps.readEffectivePolicy ?? store_2.readEffectivePolicy)(cwd);
|
|
189
|
-
const capabilities = (deps.readCapabilities ?? capabilities_1.readCapabilities)(runtime, new Date());
|
|
211
|
+
const capabilities = eventFacts ? { state: 'absent' } : (deps.readCapabilities ?? capabilities_1.readCapabilities)(runtime, new Date());
|
|
190
212
|
if (!options.lineage) {
|
|
191
|
-
const first = (0, resolve_1.resolveDispatch)({ plan: report, role: options.role, sliceId: options.slice, policy: policy.state === 'approved' ? policy.policy : undefined, capabilities: capabilities.state === 'current' ? capabilities.receipt : undefined, runtime, now: new Date(), optInV1: options.optInV1 === true });
|
|
213
|
+
const first = (0, resolve_1.resolveDispatch)({ plan: report, role: options.role, sliceId: options.slice, policy: policy.state === 'approved' ? policy.policy : undefined, capabilities: capabilities.state === 'current' ? capabilities.receipt : undefined, ...eventFacts, circuitIncidents, runtime, now: new Date(), optInV1: options.optInV1 === true });
|
|
192
214
|
// The consumer forwards THIS envelope to `job routing-reserve`, which
|
|
193
215
|
// is what creates the lineage the --lineage escalation path below
|
|
194
216
|
// then reads. Emitting it only there made the first attempt of any
|
|
@@ -204,18 +226,15 @@ function registerPlanCommand(program, deps = { validatePlanFile: validate_1.vali
|
|
|
204
226
|
const requestedProfile = options.role === 'implementer' ? slice?.implementerProfile : 'full';
|
|
205
227
|
if (requestedProfile === undefined)
|
|
206
228
|
return first;
|
|
207
|
-
return { ...first, envelope: { schema: 'routing-envelope/v1', runtime, role: options.role, planDigest: report.planDigest, executionDigest: report.executionDigest, ...(options.slice === undefined ? {} : { sliceId: options.slice }), requestedProfile, effectiveProfile: first.effectiveProfile, resolved: first.selection, policyDigest: first.policyDigest, capabilityDigest: first.capabilityDigest, outcome: first.outcome, unavailableEvidence: first.unavailableEvidence } };
|
|
229
|
+
return { ...first, envelope: { schema: 'routing-envelope/v1', runtime, role: options.role, planDigest: report.planDigest, executionDigest: report.executionDigest, ...(options.slice === undefined ? {} : { sliceId: options.slice }), requestedProfile, effectiveProfile: first.effectiveProfile, resolved: first.selection, policyDigest: first.policyDigest, capabilityDigest: first.capabilityDigest, outcome: first.outcome, unavailableEvidence: first.unavailableEvidence, ...(first.nativeAgentType ? { nativeAgentType: first.nativeAgentType } : {}) } };
|
|
208
230
|
}
|
|
209
231
|
const state = journalObservation(cwd).journalState;
|
|
210
232
|
const lineage = state.implementationLineages.find(candidate => candidate.id === options.lineage);
|
|
211
233
|
const next = (0, journal_1.resolveLineageEscalation)(state, options.lineage, lineage.initialProfile);
|
|
212
|
-
const resolved = (0, resolve_1.
|
|
234
|
+
const resolved = (0, resolve_1.resolveEscalatedSelection)({ role: 'implementer', requestedProfile: next.profile, expectedEffort: next.effort, policy: policy.state === 'approved' ? policy.policy : undefined, capabilities: capabilities.state === 'current' ? capabilities.receipt : undefined, ...eventFacts, circuitIncidents, runtime, now: new Date() });
|
|
213
235
|
if (resolved.state === 'blocked')
|
|
214
236
|
return resolved;
|
|
215
|
-
|
|
216
|
-
if (resolved.selection.effort.kind !== 'explicit' || resolved.selection.effort.value !== next.effort || !highRequiresFull)
|
|
217
|
-
return { state: 'blocked', diagnostics: [{ code: 'ROUTING_ESCALATION_EFFORT_UNAVAILABLE', message: 'The approved selected effort does not match the next lineage escalation.' }] };
|
|
218
|
-
return { ...resolved, envelope: { schema: 'routing-envelope/v1', runtime, role: 'implementer', planDigest: report.planDigest, executionDigest: report.executionDigest, sliceId: lineage.sliceId, requestedProfile: lineage.initialProfile, effectiveProfile: next.profile, resolved: resolved.selection, policyDigest: resolved.policyDigest, capabilityDigest: resolved.capabilityDigest, outcome: resolved.outcome, unavailableEvidence: resolved.unavailableEvidence }, custodyHandoff: { kind: 'routing-reserve', obligationId: lineage.obligationId, lineageId: lineage.id } };
|
|
237
|
+
return { ...resolved, envelope: { schema: 'routing-envelope/v1', runtime, role: 'implementer', planDigest: report.planDigest, executionDigest: report.executionDigest, sliceId: lineage.sliceId, requestedProfile: lineage.initialProfile, effectiveProfile: resolved.effectiveProfile, resolved: resolved.selection, policyDigest: resolved.policyDigest, capabilityDigest: resolved.capabilityDigest, outcome: resolved.outcome, unavailableEvidence: resolved.unavailableEvidence, ...(resolved.nativeAgentType ? { nativeAgentType: resolved.nativeAgentType } : {}) }, custodyHandoff: { kind: 'routing-reserve', obligationId: lineage.obligationId, lineageId: lineage.id } };
|
|
219
238
|
})();
|
|
220
239
|
process.stdout.write(options.json ? `${JSON.stringify(result)}\n` : `Plan routing: ${result.state}\n`);
|
|
221
240
|
if (result.state === 'blocked')
|
|
@@ -293,9 +312,31 @@ function registerPlanCommand(program, deps = { validatePlanFile: validate_1.vali
|
|
|
293
312
|
process.exitCode = 2;
|
|
294
313
|
return;
|
|
295
314
|
}
|
|
296
|
-
const readRouting = () => {
|
|
297
|
-
|
|
298
|
-
|
|
315
|
+
const readRouting = async () => {
|
|
316
|
+
if (!options.runtimeKind || !options.runtimeVersion || !options.accountScopeDigest)
|
|
317
|
+
return undefined;
|
|
318
|
+
const runtime = (0, capabilities_1.validateRuntimeKey)({ target: options.provider, kind: options.runtimeKind, version: options.runtimeVersion, accountScopeDigest: options.accountScopeDigest });
|
|
319
|
+
const policy = (deps.readEffectivePolicy ?? store_2.readEffectivePolicy)(options.cwd);
|
|
320
|
+
const at = deps.routingNow?.() ?? new Date();
|
|
321
|
+
if (!(at instanceof Date) || !Number.isFinite(at.getTime()))
|
|
322
|
+
throw new Error('routingNow must return a finite Date');
|
|
323
|
+
const base = { runtime, policy: policy.state === 'approved' ? policy.policy : undefined, now: at };
|
|
324
|
+
if (runtime.target === 'codex' || runtime.target === 'claude-code') {
|
|
325
|
+
const stored = (deps.readStoredEventReceipt ?? event_store_1.readStoredEventReceipt)(runtime);
|
|
326
|
+
if (stored.state === 'invalid')
|
|
327
|
+
return { ...base, eventIssue: { code: 'ROUTING_CAPABILITY_INVALID', message: 'Sealed native event receipt is invalid.' } };
|
|
328
|
+
if (stored.state === 'present') {
|
|
329
|
+
const local = await (runtime.target === 'codex' && deps.queryLocalCodexScope ? deps.queryLocalCodexScope(path_1.default.resolve(options.cwd), runtime.kind) : (0, local_event_scope_1.queryLocalEventScope)(path_1.default.resolve(options.cwd), runtime.target, runtime.kind));
|
|
330
|
+
if (local.state !== 'current' || JSON.stringify(local.scope.runtime) !== JSON.stringify(runtime)) {
|
|
331
|
+
const reason = local.state === 'unverified' ? local.reason : 'RUNTIME_IDENTITY_MISMATCH';
|
|
332
|
+
return { ...base, eventIssue: { code: `ROUTING_${reason}`, message: `Local ${runtime.target} scope cannot be verified (${reason}); run awm model-policy setup --provider ${runtime.target} --json.` } };
|
|
333
|
+
}
|
|
334
|
+
return { ...base, eventReceipt: stored.receipt, eventScope: local.scope };
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
const capabilities = (deps.readCapabilities ?? capabilities_1.readCapabilities)(runtime, at);
|
|
338
|
+
return { ...base, capabilities: capabilities.state === 'current' ? capabilities.receipt : undefined };
|
|
339
|
+
};
|
|
299
340
|
if (options.controllerAutonomy !== undefined && !(0, adapter_1.isControllerAutonomy)(options.controllerAutonomy))
|
|
300
341
|
throw new Error(`--controller-autonomy is invalid: ${options.controllerAutonomy} (valid: ${adapter_1.CONTROLLER_AUTONOMIES.join(', ')})`);
|
|
301
342
|
const report = await (0, registry_contracts_1.admitRegistryPlan)({ plan: planReport, provider: options.provider, cwd: options.cwd, enabledAgents, executionMode, planPath: normalizedPlanPath, ...journal, requireCurrent: options.requireCurrent === true, verifySensors: options.verifySensors === true, ...(options.controllerAutonomy === undefined ? {} : { controllerAutonomy: options.controllerAutonomy }) }, { admitPlan: admission, listRegistries: registryInventory, checkCurrentness: currentnessCheck, runSensors: sensorRun, readRouting });
|
|
@@ -17,7 +17,24 @@ const registries_1 = require("../../core/registries");
|
|
|
17
17
|
const inspect_1 = require("../../core/context-kernel/inspect");
|
|
18
18
|
const registries_2 = require("../../core/registries");
|
|
19
19
|
const check_1 = require("../../core/currentness/check");
|
|
20
|
+
const store_1 = require("../../core/model-policy/store");
|
|
21
|
+
const setup_readiness_1 = require("../../core/model-policy/setup-readiness");
|
|
20
22
|
const MANIFEST = path_1.default.join('.awm', 'sensors.json');
|
|
23
|
+
function checkRoutingSetup(cwd) {
|
|
24
|
+
const guidance = (0, setup_readiness_1.routingSetupGuidance)((0, store_1.readEffectivePolicy)(cwd));
|
|
25
|
+
if (guidance.length === 0)
|
|
26
|
+
return [];
|
|
27
|
+
const invalid = guidance.find(item => item.state === 'policy-invalid');
|
|
28
|
+
if (invalid?.state === 'policy-invalid')
|
|
29
|
+
return [{ id: 'routing-setup', ok: false, advisory: true, detail: 'approved routing policy is invalid', remedy: 'repair the model policy before using routed subagents' }];
|
|
30
|
+
const targets = guidance.filter((item) => item.state === 'needs-evidence');
|
|
31
|
+
if (targets.length === 0)
|
|
32
|
+
return [{ id: 'routing-setup', ok: true, advisory: true,
|
|
33
|
+
detail: 'sealed routing selections are covered locally; doctor/preflight do not verify current runtime/account/config scope (checked at routed dispatch)' }];
|
|
34
|
+
return [{ id: 'routing-setup', ok: false, advisory: true,
|
|
35
|
+
detail: `routing machine evidence needs a native check for ${targets.map(item => `${item.target}/${item.runtimeKind}`).join(', ')}`,
|
|
36
|
+
remedy: targets[0].command }];
|
|
37
|
+
}
|
|
21
38
|
/**
|
|
22
39
|
* The agent needs project context delivered every session. A repo with neither file
|
|
23
40
|
* hands every agent — and every teammate's agent — a blank slate.
|
|
@@ -472,6 +489,7 @@ async function preflight(cwd = process.cwd(), opts = {}) {
|
|
|
472
489
|
...(opts.verifySensors === true ? [await checkSensorExecution(cwd)] : []),
|
|
473
490
|
// Strict mode is the only path that contacts authoritative npm/Git sources.
|
|
474
491
|
...(opts.requireCurrent === true ? [checkCompatibility()] : []),
|
|
492
|
+
...checkRoutingSetup(cwd),
|
|
475
493
|
// Runs unconditionally — orthogonal to sensor configuration entirely, this is
|
|
476
494
|
// about PR/MR tooling, not sensors.
|
|
477
495
|
checkHost(cwd),
|
|
@@ -20,7 +20,30 @@ const redact_1 = require("../../core/journal/redact");
|
|
|
20
20
|
const git_1 = require("../../core/tracks/git");
|
|
21
21
|
const types_1 = require("../../core/journal/types");
|
|
22
22
|
const journal_1 = require("../../core/model-policy/journal");
|
|
23
|
+
const store_2 = require("../../core/model-policy/store");
|
|
24
|
+
const capabilities_1 = require("../../core/model-policy/capabilities");
|
|
25
|
+
const event_store_1 = require("../../core/model-policy/event-store");
|
|
26
|
+
const capabilities_v2_1 = require("../../core/model-policy/capabilities-v2");
|
|
27
|
+
const machine_key_1 = require("../../core/model-policy/machine-key");
|
|
28
|
+
const paths_2 = require("../../core/paths");
|
|
29
|
+
const native_routing_proof_1 = require("../../core/model-policy/native-routing-proof");
|
|
30
|
+
const selection_policy_digest_1 = require("../../core/model-policy/selection-policy-digest");
|
|
23
31
|
function now() { return new Date().toISOString(); }
|
|
32
|
+
function currentNativeAttempt(repoRoot, envelope, eventScope, at) {
|
|
33
|
+
if (!eventScope || JSON.stringify(eventScope.runtime) !== JSON.stringify(envelope.runtime))
|
|
34
|
+
return false;
|
|
35
|
+
try {
|
|
36
|
+
const stored = (0, event_store_1.readStoredEventReceipt)((0, capabilities_1.validateRuntimeKey)(envelope.runtime));
|
|
37
|
+
const approved = (0, store_2.readEffectivePolicy)(repoRoot);
|
|
38
|
+
if (stored.state !== 'present' || approved.state !== 'approved' || approved.policy.contentDigest !== envelope.policyDigest)
|
|
39
|
+
return false;
|
|
40
|
+
const mapping = approved.policy.content.mappings.find(row => row.target === envelope.runtime.target && row.runtimeKind === envelope.runtime.kind);
|
|
41
|
+
return !!mapping && (0, capabilities_v2_1.evaluateEventReceipt)(stored.receipt, eventScope, envelope.resolved, (0, selection_policy_digest_1.selectionPolicyDigest)(mapping, envelope.resolved), at).state === 'current';
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
24
47
|
const VERIFICATION_KINDS = ['test', 'lint', 'sensors', 'review', 'qa', 'interlock', 'track-integration'];
|
|
25
48
|
function verificationItems(value, field) {
|
|
26
49
|
if (!Array.isArray(value) || !value.every((item) => typeof item === 'object' && item !== null
|
|
@@ -105,7 +128,7 @@ function registerTrackIntegrationItems(state) {
|
|
|
105
128
|
function trackWorktreePath(repoRoot, trackId) {
|
|
106
129
|
return path_1.default.join(path_1.default.dirname(repoRoot), `${path_1.default.basename(repoRoot)}.track-${trackId}`);
|
|
107
130
|
}
|
|
108
|
-
function applyRequestToState(s, env, digest, repoRoot) {
|
|
131
|
+
function applyRequestToState(s, env, digest, repoRoot, eventScope) {
|
|
109
132
|
const base = { requestId: env.requestId, idempotencyKey: env.idempotencyKey, payloadDigest: digest };
|
|
110
133
|
if (env.kind === 'controller-heartbeat') {
|
|
111
134
|
s.controllerHeartbeatAt = now();
|
|
@@ -116,7 +139,21 @@ function applyRequestToState(s, env, digest, repoRoot) {
|
|
|
116
139
|
const p = env.payload;
|
|
117
140
|
if (typeof p.obligationId !== 'string' || typeof p.lineageId !== 'string' || !(0, types_1.isRoutingEnvelope)(p.envelope) || typeof p.fingerprint !== 'string' || (p.at !== undefined && typeof p.at !== 'string'))
|
|
118
141
|
throw new Error('routing-reserve requiere payload estricto');
|
|
119
|
-
const
|
|
142
|
+
const envelope = p.envelope;
|
|
143
|
+
const reserved = (0, journal_1.reserveRoutingAttempt)(s, { obligationId: p.obligationId, lineageId: p.lineageId, envelope, fingerprint: p.fingerprint, approval: () => {
|
|
144
|
+
const checkedAt = new Date();
|
|
145
|
+
const policy = (0, store_2.readEffectivePolicy)(repoRoot);
|
|
146
|
+
const event = (0, event_store_1.readStoredEventReceipt)((0, capabilities_1.validateRuntimeKey)(envelope.runtime));
|
|
147
|
+
if (event.state === 'invalid')
|
|
148
|
+
throw new Error('routing native event receipt is invalid');
|
|
149
|
+
if (event.state === 'present') {
|
|
150
|
+
if (!eventScope)
|
|
151
|
+
throw new Error('routing native event scope is unverified');
|
|
152
|
+
return { checkedAt, policy: policy.state === 'approved' ? policy.policy : undefined, eventReceipt: event.receipt, eventScope };
|
|
153
|
+
}
|
|
154
|
+
const capabilities = (0, capabilities_1.readCapabilities)((0, capabilities_1.validateRuntimeKey)(envelope.runtime), checkedAt);
|
|
155
|
+
return { checkedAt, policy: policy.state === 'approved' ? policy.policy : undefined, capabilities: capabilities.state === 'current' ? capabilities.receipt : undefined };
|
|
156
|
+
} }, p.at ?? now());
|
|
120
157
|
Object.assign(s, reserved.state);
|
|
121
158
|
(0, requests_1.applyOutcome)(s, { ...base, outcome: 'applied', resultRef: reserved.attemptId });
|
|
122
159
|
return;
|
|
@@ -125,7 +162,36 @@ function applyRequestToState(s, env, digest, repoRoot) {
|
|
|
125
162
|
const p = env.payload;
|
|
126
163
|
if (typeof p.attemptId !== 'string' || typeof p.nativeAgentId !== 'string' || (p.observed !== undefined && !(0, types_1.isRoutingSelection)(p.observed)) || (p.unavailableReason !== undefined && typeof p.unavailableReason !== 'string') || (p.at !== undefined && typeof p.at !== 'string'))
|
|
127
164
|
throw new Error('routing-observe requiere payload estricto');
|
|
128
|
-
const
|
|
165
|
+
const attempt = s.routingAttempts?.find(item => item.id === p.attemptId);
|
|
166
|
+
if (!attempt)
|
|
167
|
+
throw new Error('routing-observe attempt is unknown');
|
|
168
|
+
let key = null;
|
|
169
|
+
if (attempt.nativeEvidenceRequired) {
|
|
170
|
+
try {
|
|
171
|
+
key = (0, machine_key_1.readMachineKey)((0, paths_2.awmHome)());
|
|
172
|
+
}
|
|
173
|
+
catch { /* missing/unsafe machine key is recorded as PROVENANCE_MISSING */ }
|
|
174
|
+
}
|
|
175
|
+
const proof = p.nativeProof;
|
|
176
|
+
const nativeProof = proof;
|
|
177
|
+
const checkedAt = new Date();
|
|
178
|
+
const validProof = attempt.nativeEvidenceRequired && key && attempt.reservedAt
|
|
179
|
+
? (0, native_routing_proof_1.verifyNativeRoutingProof)(proof, { attemptId: p.attemptId, nativeAgentId: p.nativeAgentId,
|
|
180
|
+
reservedAt: attempt.reservedAt, selection: attempt.envelope.resolved }, key, checkedAt)
|
|
181
|
+
&& checkedAt.getTime() - Date.parse(nativeProof.observedAt) <= 15 * 60_000
|
|
182
|
+
&& currentNativeAttempt(repoRoot, attempt.envelope, eventScope, checkedAt)
|
|
183
|
+
&& (attempt.envelope.runtime.target === 'codex' ? nativeProof?.source === 'codex-turn-context' : nativeProof?.source === 'claude-assistant-transcript')
|
|
184
|
+
&& p.observed !== undefined && JSON.stringify(p.observed) === JSON.stringify(nativeProof?.selection)
|
|
185
|
+
&& !s.routingAttempts?.some(item => item.id !== attempt.id && item.nativeEvidenceRequired
|
|
186
|
+
&& (item.nativeAgentId === p.nativeAgentId || item.nativeEventDigest === nativeProof?.eventDigest))
|
|
187
|
+
: false;
|
|
188
|
+
const observed = (0, journal_1.observeRoutingAttempt)(s, { attemptId: p.attemptId, nativeAgentId: p.nativeAgentId,
|
|
189
|
+
...(validProof ? { observed: nativeProof.selection, nativeEventDigest: nativeProof.eventDigest,
|
|
190
|
+
...(nativeProof.actualModel === undefined ? {} : { observedBackendModel: nativeProof.actualModel }) }
|
|
191
|
+
: attempt.nativeEvidenceRequired ? {} : p.observed === undefined ? {} : { observed: p.observed }),
|
|
192
|
+
...(attempt.nativeEvidenceRequired
|
|
193
|
+
? validProof ? {} : { unavailableReason: p.unavailableReason === 'PROVIDER_REJECTED' ? 'PROVIDER_REJECTED' : 'PROVENANCE_MISSING' }
|
|
194
|
+
: p.unavailableReason === undefined ? {} : { unavailableReason: p.unavailableReason }) }, p.at ?? now());
|
|
129
195
|
Object.assign(s, observed);
|
|
130
196
|
(0, requests_1.applyOutcome)(s, { ...base, outcome: 'applied', resultRef: p.attemptId });
|
|
131
197
|
return;
|
|
@@ -533,7 +599,7 @@ function applyRequestToState(s, env, digest, repoRoot) {
|
|
|
533
599
|
/** Consume TODAS las requests pendientes en orden. ORDEN CRITICO (R1.3,
|
|
534
600
|
* bloqueador 4): (1) mutar estado, (2) writeJournal, (3) borrar archivos,
|
|
535
601
|
* (4) fsync del directorio. Solo el supervisor llama esto (single-writer). */
|
|
536
|
-
function consumePendingRequests(repoRoot, branch, activeToken) {
|
|
602
|
+
function consumePendingRequests(repoRoot, branch, activeToken, eventScope) {
|
|
537
603
|
const r = (0, store_1.readJournal)(repoRoot, branch);
|
|
538
604
|
if (r.corrupt || r.state === null)
|
|
539
605
|
throw new Error('journal corrupto: el supervisor no opera sobre corrupcion (R1.6)');
|
|
@@ -591,7 +657,7 @@ function consumePendingRequests(repoRoot, branch, activeToken) {
|
|
|
591
657
|
// nunca deja un estado parcialmente contaminado que luego no se
|
|
592
658
|
// pueda serializar o reintentar.
|
|
593
659
|
const candidate = structuredClone(s);
|
|
594
|
-
applyRequestToState(candidate, env, digest, repoRoot);
|
|
660
|
+
applyRequestToState(candidate, env, digest, repoRoot, eventScope);
|
|
595
661
|
s = candidate;
|
|
596
662
|
applied++;
|
|
597
663
|
stateTouched = true;
|
|
@@ -15,6 +15,9 @@ const registry_contracts_1 = require("../../core/admission/registry-contracts");
|
|
|
15
15
|
const config_1 = require("../../utils/config");
|
|
16
16
|
const store_2 = require("../../core/model-policy/store");
|
|
17
17
|
const capabilities_1 = require("../../core/model-policy/capabilities");
|
|
18
|
+
const event_store_1 = require("../../core/model-policy/event-store");
|
|
19
|
+
const local_event_scope_1 = require("../../core/model-policy/local-event-scope");
|
|
20
|
+
const journal_1 = require("../../core/model-policy/journal");
|
|
18
21
|
const adapter_1 = require("../../core/journal/adapter");
|
|
19
22
|
const process_1 = require("../../core/journal/process");
|
|
20
23
|
const gate_1 = require("../job/gate");
|
|
@@ -56,10 +59,39 @@ function defaultDispatchAdmission(repoRoot, branch, provider, identity, deps = {
|
|
|
56
59
|
}
|
|
57
60
|
const plan = (0, validate_1.validatePlanFile)(binding.path, repoRoot);
|
|
58
61
|
const preferences = (0, config_1.readPreferences)();
|
|
62
|
+
let routing;
|
|
63
|
+
if ((provider === 'codex' || provider === 'claude-code') && identity) {
|
|
64
|
+
const runtime = (0, capabilities_1.validateRuntimeKey)({ target: provider, kind: identity.kind, version: identity.version, accountScopeDigest: identity.accountScopeDigest });
|
|
65
|
+
const stored = (deps.readStoredEventReceipt ?? event_store_1.readStoredEventReceipt)(runtime);
|
|
66
|
+
if (stored.state === 'absent')
|
|
67
|
+
routing = routingFacts(provider, identity, repoRoot, deps);
|
|
68
|
+
else {
|
|
69
|
+
const at = deps.now?.() ?? new Date();
|
|
70
|
+
if (!(at instanceof Date) || !Number.isFinite(at.getTime()))
|
|
71
|
+
throw new Error('routing observation time must be a finite Date');
|
|
72
|
+
const policy = (deps.readEffectivePolicy ?? store_2.readEffectivePolicy)(repoRoot);
|
|
73
|
+
routing = { runtime, policy: policy.state === 'approved' ? policy.policy : undefined, now: at };
|
|
74
|
+
if (stored.state === 'invalid')
|
|
75
|
+
routing.eventIssue = { code: 'ROUTING_CAPABILITY_INVALID', message: `Sealed native event receipt is invalid; run awm model-policy setup --provider ${provider}.` };
|
|
76
|
+
else {
|
|
77
|
+
const local = await (provider === 'codex' && deps.queryLocalCodexScope ? deps.queryLocalCodexScope(repoRoot, identity.kind) : (0, local_event_scope_1.queryLocalEventScope)(repoRoot, provider, identity.kind));
|
|
78
|
+
if (local.state === 'current' && JSON.stringify(local.scope.runtime) === JSON.stringify(runtime)) {
|
|
79
|
+
routing.eventReceipt = stored.receipt;
|
|
80
|
+
routing.eventScope = local.scope;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
const reason = local.state === 'unverified' ? local.reason : 'RUNTIME_IDENTITY_MISMATCH';
|
|
84
|
+
routing.eventIssue = { code: `ROUTING_${reason}`, message: `${provider} runtime/account scope changed or cannot be verified (${reason}); run awm model-policy setup --provider ${provider} --json.` };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
else
|
|
90
|
+
routing = routingFacts(provider, identity, repoRoot, deps);
|
|
59
91
|
return (deps.admit ?? registry_contracts_1.admitRegistryPlan)({ plan, provider, cwd: repoRoot, enabledAgents: preferences.enabledAgents,
|
|
60
92
|
executionMode: 'desatendido', requireCurrent: true, verifySensors: true,
|
|
61
93
|
journalState: observed.state, journalCorrupt: observed.corrupt, planPath: binding.path,
|
|
62
|
-
routing
|
|
94
|
+
routing,
|
|
63
95
|
controllerAutonomy: autonomy });
|
|
64
96
|
};
|
|
65
97
|
}
|
|
@@ -414,7 +446,31 @@ class Supervisor {
|
|
|
414
446
|
if (r0.corrupt || r0.state === null)
|
|
415
447
|
throw new Error('journal corrupto: el supervisor no opera sobre corrupcion (R1.6)');
|
|
416
448
|
const gen = (0, generations_1.activeGeneration)(r0.state);
|
|
417
|
-
|
|
449
|
+
let eventScope;
|
|
450
|
+
if ((this.cfg.provider === 'codex' || this.cfg.provider === 'claude-code') && this.cfg.routingIdentity) {
|
|
451
|
+
const asserted = (0, capabilities_1.validateRuntimeKey)({ target: this.cfg.provider, ...this.cfg.routingIdentity });
|
|
452
|
+
const event = (0, event_store_1.readStoredEventReceipt)(asserted);
|
|
453
|
+
if (event.state === 'present') {
|
|
454
|
+
const local = await (0, local_event_scope_1.queryLocalEventScope)(this.repoRoot, asserted.target, asserted.kind);
|
|
455
|
+
if (local.state === 'current' && JSON.stringify(local.scope.runtime) === JSON.stringify(asserted))
|
|
456
|
+
eventScope = local.scope;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
(0, apply_1.consumePendingRequests)(this.repoRoot, this.branch, gen?.token ?? null, eventScope);
|
|
460
|
+
const alertRead = (0, store_1.readJournal)(this.repoRoot, this.branch);
|
|
461
|
+
if (alertRead.corrupt || !alertRead.state)
|
|
462
|
+
throw new Error('routing alert cannot read durable journal');
|
|
463
|
+
const pendingAlerts = alertRead.state.routingIncidents?.filter(item => item.alertState === 'pending') ?? [];
|
|
464
|
+
if (pendingAlerts.length > 0) {
|
|
465
|
+
for (const incident of pendingAlerts) {
|
|
466
|
+
const alert = { id: incident.id, reasonCode: incident.reasonCode, selection: incident.selection,
|
|
467
|
+
fallbackCount: incident.fallbackCount, remedy: `awm model-policy setup --provider ${incident.target} --json` };
|
|
468
|
+
process.stderr.write(`AWM routing alert ${JSON.stringify(alert)}\n`);
|
|
469
|
+
(0, store_1.appendEvent)(this.repoRoot, this.branch, { kind: 'routing-incident', ...alert });
|
|
470
|
+
incident.alertState = 'reported';
|
|
471
|
+
}
|
|
472
|
+
(0, store_1.writeJournal)(this.repoRoot, this.branch, alertRead.state);
|
|
473
|
+
}
|
|
418
474
|
// P1/P2 (R4.1-R4.10): a lo sumo un side effect de bootstrap de tracks
|
|
419
475
|
// por tick, ANTES de tocar jobs — mientras la cohorte está PREPARING,
|
|
420
476
|
// ningún job de track se despacha (eso lo maneja `runnerTick` con los
|
|
@@ -674,8 +730,12 @@ async function runSupervisorLoop(repoRoot, branch, cfg, spawner = (0, runner_1.d
|
|
|
674
730
|
// 'frozen' (Task 10): mismo camino de salida que 'complete' —
|
|
675
731
|
// drenar ownership y liberar el lock, el track ya cumplió su
|
|
676
732
|
// freeze y no debe seguir despachando ni corriendo su loop.
|
|
677
|
-
if (out === 'complete' || out === 'frozen')
|
|
733
|
+
if (out === 'complete' || out === 'frozen') {
|
|
734
|
+
const finalState = (0, store_1.readJournal)(repoRoot, branch);
|
|
735
|
+
if (!finalState.corrupt && finalState.state?.routingIncidents?.length)
|
|
736
|
+
process.stderr.write(`AWM final routing summary ${JSON.stringify((0, journal_1.routingReport)(finalState.state))}\n`);
|
|
678
737
|
break;
|
|
738
|
+
}
|
|
679
739
|
// 'custody': NO liberar lock, NO salir — seguir auditando (R4.5)
|
|
680
740
|
await new Promise((resolve) => {
|
|
681
741
|
let settled = false;
|
|
@@ -246,20 +246,24 @@ async function admitPlan(input) {
|
|
|
246
246
|
: diagnostic('ADMISSION_CONTROLLER_AUTONOMY_REQUIRED', `Unattended dispatch on ${provider} requires an explicit controller autonomy posture; without it the controller blocks on its first tool call.`);
|
|
247
247
|
return blocked(input, [reason], { planDigest: plan.planDigest, provider, executionMode: mode, journal, currentness: input.requireCurrent ? 'current' : 'not-checked', cliCurrentness, sensors: input.verifySensors ? 'pass' : 'not-required', capabilityResolution: resolution });
|
|
248
248
|
}
|
|
249
|
-
|
|
249
|
+
const routing = plan.schema === 'compact-slices/v2' ? input.routing ?? await input.routingReader?.() : input.routing;
|
|
250
|
+
return completeAdmission({ ...input, routing, routingReader: undefined }, plan, provider, mode, journal, input.requireCurrent ? 'current' : 'not-checked', input.verifySensors ? 'pass' : 'not-required', resolution, cliCurrentness);
|
|
250
251
|
}
|
|
251
252
|
const capabilities = exports.PROVIDER_EXECUTION_CAPABILITIES[provider];
|
|
252
253
|
const resolution = { outcome: capabilities.interactiveExecution === 'supported' ? 'native' : 'blocked', provider, capabilities, evidenceVersion: 'r1-v1', diagnostics: [] };
|
|
253
254
|
if (resolution.outcome === 'blocked')
|
|
254
255
|
return blocked(input, [diagnostic('ADMISSION_CAPABILITY_UNVERIFIED', `Provider ${provider} has no verified interactive execution capability.`)], { planDigest: plan.planDigest, provider, executionMode: mode, capabilityResolution: resolution });
|
|
255
|
-
|
|
256
|
+
const routing = plan.schema === 'compact-slices/v2' ? input.routing ?? await input.routingReader?.() : input.routing;
|
|
257
|
+
return completeAdmission({ ...input, routing, routingReader: undefined }, plan, provider, mode, 'not-required', input.requireCurrent ? 'current' : 'not-checked', input.verifySensors ? 'pass' : 'not-required', resolution, cliCurrentness);
|
|
256
258
|
}
|
|
257
259
|
function completeAdmission(input, plan, provider, executionMode, journal, currentness, sensors, capabilityResolution, cliCurrentness = 'not-checked') {
|
|
258
260
|
if (plan.schema === 'compact-slices/v1')
|
|
259
261
|
return { state: 'admitted', planState: 'valid', planDigest: plan.planDigest, provider, executionMode, journal, currentness, cliCurrentness, sensors, capabilityResolution, forecast: forecast(plan.manifest.slices.length), diagnostics: [] };
|
|
260
|
-
const routing = input.routing
|
|
261
|
-
if (
|
|
262
|
-
return blocked(input, [diagnostic(
|
|
262
|
+
const routing = input.routing;
|
|
263
|
+
if (routing?.eventIssue)
|
|
264
|
+
return blocked(input, [diagnostic(routing.eventIssue.code, routing.eventIssue.message)], { planDigest: plan.planDigest, provider, executionMode, journal, currentness, cliCurrentness, sensors, capabilityResolution });
|
|
265
|
+
if (!routing?.runtime || !routing.policy || (!routing.capabilities && !(routing.eventReceipt && routing.eventScope)))
|
|
266
|
+
return blocked(input, [diagnostic('ADMISSION_ROUTING_FACTS_REQUIRED', 'Compact v2 requires an approved policy, current capability evidence, and runtime identity.')], { planDigest: plan.planDigest, provider, executionMode, journal, currentness, cliCurrentness, sensors, capabilityResolution });
|
|
263
267
|
let runtime;
|
|
264
268
|
try {
|
|
265
269
|
runtime = (0, capabilities_1.validateRuntimeKey)(routing.runtime);
|
|
@@ -269,12 +273,22 @@ function completeAdmission(input, plan, provider, executionMode, journal, curren
|
|
|
269
273
|
}
|
|
270
274
|
if (runtime.target !== provider)
|
|
271
275
|
return blocked(input, [diagnostic('ADMISSION_ROUTING_PROVIDER_MISMATCH', 'Compact v2 routing runtime target must exactly match the admitted provider.')], { planDigest: plan.planDigest, provider, executionMode, journal, currentness, cliCurrentness, sensors, capabilityResolution });
|
|
276
|
+
if (routing.eventReceipt && routing.eventScope && JSON.stringify(runtime) !== JSON.stringify(routing.eventScope.runtime))
|
|
277
|
+
return blocked(input, [diagnostic('ADMISSION_ROUTING_RUNTIME_MISMATCH', 'Event-scoped routing facts do not match the admitted runtime.')], { planDigest: plan.planDigest, provider, executionMode, journal, currentness, cliCurrentness, sensors, capabilityResolution });
|
|
272
278
|
const now = routing.now ?? new Date();
|
|
273
279
|
for (const slice of plan.manifest.slices) {
|
|
274
|
-
const
|
|
280
|
+
const profile = slice.implementerProfile;
|
|
281
|
+
const resolved = routing.eventReceipt && routing.eventScope
|
|
282
|
+
? (0, resolve_1.resolveEventWithFallback)({ role: 'implementer', requestedProfile: profile, policy: routing.policy, receipt: routing.eventReceipt, scope: routing.eventScope, now })
|
|
283
|
+
: (0, resolve_1.resolveSelection)({ role: 'implementer', requestedProfile: profile, policy: routing.policy, capabilities: routing.capabilities, runtime, now });
|
|
275
284
|
if (resolved.state === 'blocked')
|
|
276
285
|
return blocked(input, resolved.diagnostics, { planDigest: plan.planDigest, provider, executionMode, journal, currentness, cliCurrentness, sensors, capabilityResolution });
|
|
277
286
|
}
|
|
287
|
+
if (routing.eventReceipt && routing.eventScope) {
|
|
288
|
+
const full = (0, resolve_1.resolveEventSelection)({ role: 'code-quality-reviewer', requestedProfile: 'full', policy: routing.policy, receipt: routing.eventReceipt, scope: routing.eventScope, now });
|
|
289
|
+
if (full.state === 'blocked')
|
|
290
|
+
return blocked(input, full.diagnostics, { planDigest: plan.planDigest, provider, executionMode, journal, currentness, cliCurrentness, sensors, capabilityResolution });
|
|
291
|
+
}
|
|
278
292
|
return { state: 'admitted', planState: 'valid', planDigest: plan.planDigest, provider, executionMode, journal, currentness, cliCurrentness, sensors, capabilityResolution, forecast: forecast(plan.manifest.slices.length), routingForecast: routingForecast(plan, routing), diagnostics: [] };
|
|
279
293
|
}
|
|
280
294
|
// Structural exhaustiveness guard: adding an AgentTarget requires an explicit entry above.
|
|
@@ -94,6 +94,8 @@ function isWellFormedState(x) {
|
|
|
94
94
|
return false;
|
|
95
95
|
if (x.routingAttempts !== undefined && (!Array.isArray(x.routingAttempts) || x.routingAttempts.length > 4096 || !x.routingAttempts.every(isWellFormedRoutingAttempt)))
|
|
96
96
|
return false;
|
|
97
|
+
if (x.routingIncidents !== undefined && (!Array.isArray(x.routingIncidents) || x.routingIncidents.length > 4096 || !x.routingIncidents.every(isWellFormedRoutingIncident)))
|
|
98
|
+
return false;
|
|
97
99
|
if (x.implementationLineages !== undefined && (!Array.isArray(x.implementationLineages) || x.implementationLineages.length > 4096 || !x.implementationLineages.every(isWellFormedImplementationLineage)))
|
|
98
100
|
return false;
|
|
99
101
|
if (!Array.isArray(x.requestProblems) || !x.requestProblems.every(isWellFormedRequestProblem))
|
|
@@ -211,17 +213,31 @@ function isWellFormedAppliedRequest(x) {
|
|
|
211
213
|
&& (x.resultRef === undefined || typeof x.resultRef === 'string');
|
|
212
214
|
}
|
|
213
215
|
function isWellFormedRoutingAttempt(x) {
|
|
214
|
-
return isObj(x) && Object.keys(x).every(key => ['schema', 'id', 'obligationId', 'lineageId', 'attempt', 'envelope', 'envelopeDigest', 'fingerprint', 'state', 'nativeAgentId', 'observed', 'verdict', 'reasonCode', 'administrativeRepair'].includes(key))
|
|
216
|
+
return isObj(x) && Object.keys(x).every(key => ['schema', 'id', 'obligationId', 'lineageId', 'attempt', 'envelope', 'envelopeDigest', 'fingerprint', 'state', 'reservedAt', 'nativeEvidenceRequired', 'nativeEventDigest', 'nativeAgentId', 'observed', 'observedBackendModel', 'verdict', 'reasonCode', 'administrativeRepair'].includes(key))
|
|
215
217
|
&& x.schema === 'routing-attempt/v1'
|
|
216
218
|
&& typeof x.id === 'string' && typeof x.obligationId === 'string' && typeof x.lineageId === 'string' && Number.isSafeInteger(x.attempt) && x.attempt > 0
|
|
217
219
|
&& typeof x.envelopeDigest === 'string' && /^[a-f0-9]{64}$/.test(x.envelopeDigest) && typeof x.fingerprint === 'string' && /^[a-f0-9]{64}$/.test(x.fingerprint)
|
|
218
|
-
&& ['reserved', 'active', 'complete', 'blocked', 'unknown'].includes(String(x.state)) && (x.nativeAgentId === undefined || typeof x.nativeAgentId === 'string') && (x.observed === undefined || isRoutingSelection(x.observed)) && (x.verdict === undefined || ['pass', 'fail', 'inconclusive'].includes(String(x.verdict))) && (x.reasonCode === undefined || typeof x.reasonCode === 'string') && (x.administrativeRepair === undefined || x.administrativeRepair === true) && isRoutingEnvelope(x.envelope);
|
|
220
|
+
&& ['reserved', 'active', 'complete', 'blocked', 'unknown'].includes(String(x.state)) && (x.reservedAt === undefined || (typeof x.reservedAt === 'string' && Number.isFinite(Date.parse(x.reservedAt)))) && (x.nativeEvidenceRequired === undefined || x.nativeEvidenceRequired === true) && (x.nativeEventDigest === undefined || isDigest(x.nativeEventDigest)) && (x.nativeAgentId === undefined || typeof x.nativeAgentId === 'string') && (x.observed === undefined || isRoutingSelection(x.observed)) && (x.observedBackendModel === undefined || (typeof x.observedBackendModel === 'string' && x.observedBackendModel.length > 0 && x.observedBackendModel.length <= 128)) && (x.verdict === undefined || ['pass', 'fail', 'inconclusive'].includes(String(x.verdict))) && (x.reasonCode === undefined || typeof x.reasonCode === 'string') && (x.administrativeRepair === undefined || x.administrativeRepair === true) && isRoutingEnvelope(x.envelope);
|
|
221
|
+
}
|
|
222
|
+
function isWellFormedRoutingIncident(x) {
|
|
223
|
+
if (!isObj(x) || Object.keys(x).length !== 12 || !Object.keys(x).every(key => ['schema', 'id', 'runStartedAt', 'target', 'selection', 'reasonCode', 'firstAt', 'lastAt', 'affectedObligations', 'fallbackCount', 'blockedCount', 'alertState'].includes(key)))
|
|
224
|
+
return false;
|
|
225
|
+
return x.schema === 'routing-incident/v1' && typeof x.id === 'string' && /^[a-f0-9]{24}$/.test(x.id)
|
|
226
|
+
&& typeof x.runStartedAt === 'string' && Number.isFinite(Date.parse(x.runStartedAt))
|
|
227
|
+
&& typeof x.target === 'string' && x.target.length > 0 && x.target.length <= 128
|
|
228
|
+
&& isRoutingSelection(x.selection) && typeof x.reasonCode === 'string' && /^[A-Z0-9_]{3,64}$/.test(x.reasonCode)
|
|
229
|
+
&& typeof x.firstAt === 'string' && Number.isFinite(Date.parse(x.firstAt))
|
|
230
|
+
&& typeof x.lastAt === 'string' && Number.isFinite(Date.parse(x.lastAt))
|
|
231
|
+
&& Number.isSafeInteger(x.affectedObligations) && x.affectedObligations > 0
|
|
232
|
+
&& Number.isSafeInteger(x.fallbackCount) && x.fallbackCount >= 0
|
|
233
|
+
&& Number.isSafeInteger(x.blockedCount) && x.blockedCount >= 0
|
|
234
|
+
&& ['pending', 'reported'].includes(String(x.alertState));
|
|
219
235
|
}
|
|
220
236
|
function isDigest(value) { return typeof value === 'string' && /^[a-f0-9]{64}$/.test(value); }
|
|
221
237
|
function isRoutingSelection(x) { return isObj(x) && Object.keys(x).every(key => key === 'selector' || key === 'effort') && isObj(x.selector) && Object.keys(x.selector).every(key => key === 'kind' || key === 'id') && ['model', 'tier'].includes(String(x.selector.kind)) && typeof x.selector.id === 'string' && x.selector.id.length > 0 && x.selector.id.length <= 128 && isObj(x.effort) && ((x.effort.kind === 'runtime-default' && Object.keys(x.effort).length === 1) || (x.effort.kind === 'explicit' && Object.keys(x.effort).length === 2 && typeof x.effort.value === 'string' && x.effort.value.length > 0 && x.effort.value.length <= 128)); }
|
|
222
|
-
function isRoutingEnvelope(x) { if (!isObj(x) || !Object.keys(x).every(key => ['schema', 'runtime', 'role', 'planDigest', 'executionDigest', 'sliceId', 'requestedProfile', 'effectiveProfile', 'resolved', 'policyDigest', 'capabilityDigest', 'outcome', 'unavailableEvidence'].includes(key)))
|
|
223
|
-
return false; const runtime = x.runtime; return x.schema === 'routing-envelope/v1' && isObj(runtime) && Object.keys(runtime).every(key => ['target', 'kind', 'version', 'accountScopeDigest'].includes(key)) && ['codex', 'claude-code', 'cursor', 'copilot', 'opencode', 'antigravity'].includes(String(runtime.target)) && typeof runtime.kind === 'string' && runtime.kind.length > 0 && runtime.kind.length <= 128 && typeof runtime.version === 'string' && runtime.version.length > 0 && runtime.version.length <= 128 && isDigest(runtime.accountScopeDigest) && typeof x.role === 'string' && ROUTING_ROLES.includes(x.role) && isDigest(x.planDigest) && isDigest(x.executionDigest) && (x.sliceId === undefined || (typeof x.sliceId === 'string' && x.sliceId.length > 0 && x.sliceId.length <= 128)) && ['mechanical', 'integration', 'judgment', 'full'].includes(String(x.requestedProfile)) && ['mechanical', 'integration', 'judgment', 'full'].includes(String(x.effectiveProfile)) && isRoutingSelection(x.resolved) && isDigest(x.policyDigest) && isDigest(x.capabilityDigest) && ['native', 'degraded'].includes(String(x.outcome)) && Array.isArray(x.unavailableEvidence) && x.unavailableEvidence.length <= UNAVAILABLE_EVIDENCE.length && x.unavailableEvidence.every(value => typeof value === 'string' && UNAVAILABLE_EVIDENCE.includes(value)); }
|
|
224
|
-
function isWellFormedImplementationLineage(x) { return isObj(x) && Object.keys(x).every(key => ['id', 'obligationId', 'sliceId', 'planDigest', 'executionDigest', 'initialProfile', 'initialEffort', 'attempts'].includes(key)) && typeof x.id === 'string' && x.id.length > 0 && typeof x.obligationId === 'string' && x.obligationId.length > 0 && typeof x.sliceId === 'string' && x.sliceId.length > 0 && isDigest(x.planDigest) && isDigest(x.executionDigest) && ['mechanical', 'integration', 'judgment'].includes(String(x.initialProfile)) && ['medium', 'high'].includes(String(x.initialEffort)) && Number.isSafeInteger(x.attempts) && x.attempts >= 0 && x.attempts <= 3; }
|
|
238
|
+
function isRoutingEnvelope(x) { if (!isObj(x) || !Object.keys(x).every(key => ['schema', 'runtime', 'role', 'planDigest', 'executionDigest', 'sliceId', 'requestedProfile', 'effectiveProfile', 'resolved', 'policyDigest', 'capabilityDigest', 'outcome', 'unavailableEvidence', 'nativeAgentType'].includes(key)))
|
|
239
|
+
return false; const runtime = x.runtime; return x.schema === 'routing-envelope/v1' && isObj(runtime) && Object.keys(runtime).every(key => ['target', 'kind', 'version', 'accountScopeDigest'].includes(key)) && ['codex', 'claude-code', 'cursor', 'copilot', 'opencode', 'antigravity'].includes(String(runtime.target)) && typeof runtime.kind === 'string' && runtime.kind.length > 0 && runtime.kind.length <= 128 && typeof runtime.version === 'string' && runtime.version.length > 0 && runtime.version.length <= 128 && isDigest(runtime.accountScopeDigest) && typeof x.role === 'string' && ROUTING_ROLES.includes(x.role) && isDigest(x.planDigest) && isDigest(x.executionDigest) && (x.sliceId === undefined || (typeof x.sliceId === 'string' && x.sliceId.length > 0 && x.sliceId.length <= 128)) && ['mechanical', 'integration', 'judgment', 'full'].includes(String(x.requestedProfile)) && ['mechanical', 'integration', 'judgment', 'full'].includes(String(x.effectiveProfile)) && isRoutingSelection(x.resolved) && isDigest(x.policyDigest) && isDigest(x.capabilityDigest) && ['native', 'degraded'].includes(String(x.outcome)) && Array.isArray(x.unavailableEvidence) && x.unavailableEvidence.length <= UNAVAILABLE_EVIDENCE.length && x.unavailableEvidence.every(value => typeof value === 'string' && UNAVAILABLE_EVIDENCE.includes(value)) && (x.nativeAgentType === undefined || (runtime.target === 'claude-code' && typeof x.nativeAgentType === 'string' && /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/.test(x.nativeAgentType))); }
|
|
240
|
+
function isWellFormedImplementationLineage(x) { return isObj(x) && Object.keys(x).every(key => ['id', 'obligationId', 'sliceId', 'planDigest', 'executionDigest', 'initialProfile', 'initialEffort', 'attempts'].includes(key)) && typeof x.id === 'string' && x.id.length > 0 && typeof x.obligationId === 'string' && x.obligationId.length > 0 && typeof x.sliceId === 'string' && x.sliceId.length > 0 && isDigest(x.planDigest) && isDigest(x.executionDigest) && ['mechanical', 'integration', 'judgment'].includes(String(x.initialProfile)) && ['medium', 'high', 'runtime-default'].includes(String(x.initialEffort)) && Number.isSafeInteger(x.attempts) && x.attempts >= 0 && x.attempts <= 3; }
|
|
225
241
|
function isWellFormedRequestProblem(x) {
|
|
226
242
|
return isObj(x) && typeof x.file === 'string' && (x.kind === 'corrupt' || x.kind === 'rejected')
|
|
227
243
|
&& typeof x.detail === 'string' && typeof x.at === 'string';
|