@kontextmind/kxm 0.6.0 → 0.7.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/.claude-plugin/marketplace.json +1 -1
- package/.kxm/agents/coordinator.yaml +9 -0
- package/.kxm/agents/critic-arch.yaml +13 -0
- package/.kxm/agents/critic-cli.yaml +13 -0
- package/.kxm/agents/implementer.yaml +13 -0
- package/.kxm/gates.yaml +8 -0
- package/.kxm/producers.yaml +22 -0
- package/.kxm/project.yaml +15 -0
- package/.kxm/roles/writer.yaml +5 -0
- package/.kxm/workflows/default.yaml +47 -0
- package/CHANGELOG.md +39 -7
- package/README.md +1 -0
- package/docs/README.md +2 -0
- package/docs/agent-skills.md +118 -0
- package/docs/architecture.md +1 -1
- package/docs/assignment-runner.md +21 -8
- package/docs/configuration.md +2 -2
- package/docs/kxm-handbook.md +3 -3
- package/docs/operator-pi-packages.md +63 -0
- package/docs/skills/repo-work-delivery.md +102 -0
- package/docs/test-matrix.md +4 -3
- package/docs/troubleshooting.md +19 -0
- package/docs/vnext/validation.md +9 -0
- package/docs/webhook-workflows.md +2 -2
- package/examples/README.md +1 -1
- package/package.json +16 -17
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/README.md +1 -1
- package/plugins/kxm/dist/cli.js +6875 -3672
- package/plugins/kxm/dist/core.js +214 -34
- package/plugins/kxm/dist/extension.js +7721 -85
- package/plugins/kxm/dist/mcp-server.js +75 -21
- package/plugins/kxm/dist/runtime.js +5403 -1014
- package/plugins/kxm/dist/server.js +3008 -2268
- package/plugins/kxm/dist/vnext-runtime-supervisor.js +5751 -631
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/SUITE.md +5 -0
- package/plugins/kxm/skills/hints.json +73 -0
- package/plugins/kxm/skills/kxm/SKILL.md +30 -83
- package/plugins/kxm/skills/kxm-context-memory/SKILL.md +69 -0
- package/plugins/kxm/skills/kxm-definitions/SKILL.md +65 -0
- package/plugins/kxm/skills/kxm-harness-auth/SKILL.md +34 -0
- package/plugins/kxm/skills/kxm-harvest/SKILL.md +48 -0
- package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +34 -0
- package/plugins/kxm/skills/kxm-insights/SKILL.md +48 -0
- package/plugins/kxm/skills/kxm-mind/SKILL.md +59 -0
- package/plugins/kxm/skills/kxm-peer/SKILL.md +110 -0
- package/plugins/kxm/skills/kxm-project-setup/SKILL.md +35 -0
- package/plugins/kxm/skills/kxm-projects/SKILL.md +43 -0
- package/plugins/kxm/skills/kxm-protocol/SKILL.md +66 -0
- package/plugins/kxm/skills/kxm-query/SKILL.md +45 -0
- package/plugins/kxm/skills/kxm-routing-improve/SKILL.md +30 -0
- package/plugins/kxm/skills/kxm-runs/SKILL.md +29 -0
- package/plugins/kxm/skills/kxm-setup/SKILL.md +55 -0
- package/plugins/kxm/skills/kxm-skill-lifecycle/SKILL.md +31 -0
- package/plugins/kxm/skills/kxm-tasks/SKILL.md +33 -0
- package/plugins/kxm/skills/kxm-triage/SKILL.md +47 -0
- package/plugins/kxm/skills/kxm-work/SKILL.md +44 -0
- package/plugins/kxm/skills/kxm-workflow/SKILL.md +45 -0
- package/plugins/kxm/src/autocomplete.ts +9 -3
- package/plugins/kxm/src/cli.ts +1077 -11
- package/plugins/kxm/src/commands.ts +150 -8
- package/plugins/kxm/src/config.ts +7 -4
- package/plugins/kxm/src/context-packet.ts +172 -0
- package/plugins/kxm/src/extension.ts +36 -1
- package/plugins/kxm/src/external-effects.ts +356 -7
- package/plugins/kxm/src/hub.ts +2 -4
- package/plugins/kxm/src/improve.ts +72 -0
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/model-inventory.ts +127 -0
- package/plugins/kxm/src/policy-draft.d.mts +55 -0
- package/plugins/kxm/src/policy-draft.mjs +565 -0
- package/plugins/kxm/src/price-calc.ts +17 -18
- package/plugins/kxm/src/prices.ts +32 -16
- package/plugins/kxm/src/producers.ts +71 -0
- package/plugins/kxm/src/restricted-yaml.d.mts +31 -0
- package/plugins/kxm/src/restricted-yaml.mjs +145 -0
- package/plugins/kxm/src/role.ts +375 -0
- package/plugins/kxm/src/routing.ts +99 -1
- package/plugins/kxm/src/session-work.ts +9 -2
- package/plugins/kxm/src/studio-layout.ts +660 -17
- package/plugins/kxm/src/suggest.ts +7 -13
- package/plugins/kxm/src/telemetry.ts +82 -0
- package/plugins/kxm/src/tui.ts +140 -0
- package/plugins/kxm/src/vnext-config.ts +15 -110
- package/plugins/kxm/src/vnext-engine.ts +198 -62
- package/plugins/kxm/src/vnext-harness.ts +263 -81
- package/plugins/kxm/src/vnext-oneshot-evidence.ts +85 -0
- package/plugins/kxm/src/vnext-oneshot-process.ts +149 -0
- package/plugins/kxm/src/vnext-oneshot-producer.ts +170 -233
- package/plugins/kxm/src/vnext-runtime-store.ts +35 -1
- package/plugins/kxm/src/vnext-runtime-supervisor.ts +120 -5
- package/plugins/kxm/src/vnext-runtime.ts +14 -0
- package/plugins/kxm/src/workflow-manager.ts +392 -0
- package/plugins/kxm/src/workflow-tui.ts +255 -0
- package/schemas/policy-draft/README.md +17 -0
- package/schemas/policy-draft/model.v2.schema.json +140 -0
- package/schemas/policy-draft/role.v2.schema.json +91 -0
- package/schemas/vnext/role.schema.json +76 -0
- package/schemas/vnext/run-event.schema.json +1 -0
- package/scripts/assignment-run.d.mts +1 -1
- package/scripts/assignment-run.mjs +44 -35
- package/scripts/check-generated.mjs +33 -9
- package/scripts/emit-codex-artifacts.mjs +255 -11
- package/scripts/harness-run.d.mts +12 -4
- package/scripts/harness-run.mjs +65 -17
- package/scripts/kxm-hub.mjs +6 -0
- package/scripts/native-critic.d.mts +5 -0
- package/scripts/native-critic.mjs +60 -0
- package/.kxm/config/README.md +0 -5
- package/.kxm/config/agents.json +0 -43
- package/.kxm/config/env.example +0 -56
- package/.kxm/config/update.example.yaml +0 -9
- package/.kxm/config/workflows/fix.json +0 -160
- package/.kxm/config/workflows/jira-development.json +0 -116
- package/.kxm/config/workflows/provenance-quorum.json +0 -150
- package/.kxm/config/workflows/v04-dogfood.json +0 -72
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { parse } from "yaml";
|
|
5
|
+
import { isProducerAdmitted, listRoleBindings } from "./producers.ts";
|
|
2
6
|
import {
|
|
3
7
|
buildFormalContextPacket,
|
|
8
|
+
buildHandoffManifest,
|
|
4
9
|
formatContextPacketForPrompt,
|
|
10
|
+
pruneContextPacket,
|
|
5
11
|
type FormalContextPacketV2,
|
|
6
12
|
type HandoffManifestV1,
|
|
7
13
|
} from "./context-packet.ts";
|
|
@@ -116,6 +122,8 @@ export interface VnextProducerRequest {
|
|
|
116
122
|
|
|
117
123
|
export interface VnextProducerResult {
|
|
118
124
|
readonly outcome: string;
|
|
125
|
+
/** Host cannot attest termination; retain the issued attempt, never replay or settle it. */
|
|
126
|
+
readonly effectUncertain?: true;
|
|
119
127
|
readonly costBasis?: "metered" | "unmetered" | "unknown";
|
|
120
128
|
readonly costUsd?: number | null;
|
|
121
129
|
readonly tokensIn?: number | null;
|
|
@@ -136,7 +144,7 @@ export interface VnextProducerResult {
|
|
|
136
144
|
}
|
|
137
145
|
|
|
138
146
|
export interface VnextProducer {
|
|
139
|
-
readonly id: "driver-simulated" | "pi" | "oneshot"
|
|
147
|
+
readonly id: "driver-simulated" | "pi" | "oneshot";
|
|
140
148
|
produce(request: VnextProducerRequest): Promise<VnextProducerResult>;
|
|
141
149
|
}
|
|
142
150
|
|
|
@@ -332,7 +340,7 @@ export async function driveVnextRun(
|
|
|
332
340
|
context: VnextRuntimeContext,
|
|
333
341
|
runId: string,
|
|
334
342
|
producer: VnextProducer,
|
|
335
|
-
options: { allowLimits?: boolean } = {},
|
|
343
|
+
options: { allowLimits?: boolean; liveMode?: boolean } = {},
|
|
336
344
|
): Promise<VnextRunDriveResult> {
|
|
337
345
|
requireTrustedProducer(producer);
|
|
338
346
|
const run = requireRun(context, runId);
|
|
@@ -347,7 +355,7 @@ async function driveAdmitted(
|
|
|
347
355
|
runId: string,
|
|
348
356
|
producer: VnextProducer,
|
|
349
357
|
token: string,
|
|
350
|
-
options: { allowLimits?: boolean } = {},
|
|
358
|
+
options: { allowLimits?: boolean; liveMode?: boolean } = {},
|
|
351
359
|
): Promise<VnextRunDriveResult> {
|
|
352
360
|
const run = requireRun(context, runId);
|
|
353
361
|
if (run.status === "preparing") {
|
|
@@ -407,7 +415,11 @@ export class VnextRunScheduler {
|
|
|
407
415
|
return new VnextRunScheduler(context, bundle.configRevision);
|
|
408
416
|
}
|
|
409
417
|
|
|
410
|
-
enqueue(
|
|
418
|
+
enqueue(
|
|
419
|
+
runId: string,
|
|
420
|
+
producer: VnextProducer,
|
|
421
|
+
options: { allowLimits?: boolean; liveMode?: boolean } = {},
|
|
422
|
+
): Promise<VnextRunDriveResult> {
|
|
411
423
|
requireTrustedProducer(producer);
|
|
412
424
|
const policy = vnextSchedulerPolicy(this.context.eventStore.path);
|
|
413
425
|
if (!policy || policy.configRevision !== this.configRevision) {
|
|
@@ -421,7 +433,7 @@ export class VnextRunScheduler {
|
|
|
421
433
|
runId,
|
|
422
434
|
envelope.revisions.config,
|
|
423
435
|
envelope.projectLimits.maxConcurrentRuns,
|
|
424
|
-
(token) => driveAdmitted(this.context, runId, producer, token),
|
|
436
|
+
(token) => driveAdmitted(this.context, runId, producer, token, options),
|
|
425
437
|
) as Promise<VnextRunDriveResult>;
|
|
426
438
|
} catch (error) {
|
|
427
439
|
return Promise.reject(error);
|
|
@@ -471,7 +483,7 @@ export function verifyVnextAttemptCapability(
|
|
|
471
483
|
function invokeProducer(
|
|
472
484
|
producer: VnextProducer,
|
|
473
485
|
request: VnextProducerRequest,
|
|
474
|
-
): Promise<{ result?: VnextProducerResult; error: boolean }> {
|
|
486
|
+
): Promise<{ result?: VnextProducerResult; error: boolean; errorCode?: string }> {
|
|
475
487
|
let pending: Promise<VnextProducerResult>;
|
|
476
488
|
try {
|
|
477
489
|
pending = Promise.resolve(producer.produce(request));
|
|
@@ -884,6 +896,79 @@ function unreconciledPanelAttemptId(state: VnextRunState): string | undefined {
|
|
|
884
896
|
return undefined;
|
|
885
897
|
}
|
|
886
898
|
|
|
899
|
+
function resolveProducerRoute(
|
|
900
|
+
projectRoot: string,
|
|
901
|
+
step: VnextCompiledStep,
|
|
902
|
+
agentId: string,
|
|
903
|
+
): { provider: string; model: string; selector: string } | { error: Omit<VnextRunHandoff, "stepId"> } {
|
|
904
|
+
let agentModel: string | undefined;
|
|
905
|
+
const agentFile = join(projectRoot, ".kxm", "agents", `${agentId}.yaml`);
|
|
906
|
+
if (existsSync(agentFile)) {
|
|
907
|
+
try {
|
|
908
|
+
const parsed = parse(readFileSync(agentFile, "utf8")) as Record<string, unknown>;
|
|
909
|
+
if (typeof parsed?.model === "string") {
|
|
910
|
+
agentModel = parsed.model;
|
|
911
|
+
}
|
|
912
|
+
} catch {
|
|
913
|
+
// ignore
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
let selector = typeof step.model === "string" ? step.model : agentModel;
|
|
918
|
+
if (!selector) {
|
|
919
|
+
if (agentId === "implementer") {
|
|
920
|
+
selector = "xai/grok-4.6";
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
if (!selector) {
|
|
924
|
+
return {
|
|
925
|
+
error: {
|
|
926
|
+
reason: "step_unsupported",
|
|
927
|
+
field: "model",
|
|
928
|
+
detail: "producer_route_unsupported: agent or step has no model declared",
|
|
929
|
+
},
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
const slash = selector.indexOf("/");
|
|
934
|
+
if (slash <= 0 || slash === selector.length - 1) {
|
|
935
|
+
return {
|
|
936
|
+
error: {
|
|
937
|
+
reason: "step_unsupported",
|
|
938
|
+
field: "model",
|
|
939
|
+
detail: `producer_route_unsupported: invalid selector '${selector}'`,
|
|
940
|
+
},
|
|
941
|
+
};
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
if (!isProducerAdmitted(projectRoot, selector)) {
|
|
945
|
+
return {
|
|
946
|
+
error: {
|
|
947
|
+
reason: "step_unsupported",
|
|
948
|
+
field: "model",
|
|
949
|
+
detail: `producer_route_unsupported: model '${selector}' is not admitted`,
|
|
950
|
+
},
|
|
951
|
+
};
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
const role = agentId === "implementer" ? "writer" : agentId;
|
|
955
|
+
const roleBindings = listRoleBindings(projectRoot);
|
|
956
|
+
const roster = roleBindings[role];
|
|
957
|
+
if (roster && !roster.includes(selector)) {
|
|
958
|
+
return {
|
|
959
|
+
error: {
|
|
960
|
+
reason: "step_unsupported",
|
|
961
|
+
field: "model",
|
|
962
|
+
detail: `producer_route_unsupported: model '${selector}' not in role '${role}' roster`,
|
|
963
|
+
},
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
const provider = selector.slice(0, slash);
|
|
968
|
+
const model = selector.slice(slash + 1);
|
|
969
|
+
return { provider, model, selector };
|
|
970
|
+
}
|
|
971
|
+
|
|
887
972
|
function prepareDispatch(
|
|
888
973
|
context: VnextRuntimeContext,
|
|
889
974
|
runId: string,
|
|
@@ -1022,8 +1107,22 @@ function prepareDispatch(
|
|
|
1022
1107
|
};
|
|
1023
1108
|
}
|
|
1024
1109
|
|
|
1025
|
-
const unsupported = unsupportedStep(plan, step);
|
|
1110
|
+
const unsupported = unsupportedStep(plan, step, producerId);
|
|
1026
1111
|
if (unsupported) return { kind: "return", state, handoff: { ...unsupported, stepId } };
|
|
1112
|
+
|
|
1113
|
+
let resolvedRoute: { provider: string; model: string; selector: string } | undefined;
|
|
1114
|
+
if (producerId !== "driver-simulated") {
|
|
1115
|
+
const allowed = step.assignments.allowedAgents;
|
|
1116
|
+
const agentId = (allowed && allowed.length > 0 && allowed[0])
|
|
1117
|
+
? allowed[0]!
|
|
1118
|
+
: (step.kind === "agent" || step.kind === "moa" ? step.agent : "coordinator");
|
|
1119
|
+
const routeResult = resolveProducerRoute(context.projectRoot, step, agentId);
|
|
1120
|
+
if ("error" in routeResult) {
|
|
1121
|
+
return { kind: "return", state, handoff: { ...routeResult.error, stepId } };
|
|
1122
|
+
}
|
|
1123
|
+
resolvedRoute = routeResult;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1027
1126
|
const used = state.stepAttempts[stepId] ?? 0;
|
|
1028
1127
|
if (used >= step.maxAttempts) {
|
|
1029
1128
|
return { kind: "return", ...failBudget(context, run, plan, state, "budget_step_attempts", stepId) };
|
|
@@ -1056,6 +1155,7 @@ function prepareDispatch(
|
|
|
1056
1155
|
stepAttempt,
|
|
1057
1156
|
enterRunning: true,
|
|
1058
1157
|
producerId,
|
|
1158
|
+
resolvedRoute,
|
|
1059
1159
|
});
|
|
1060
1160
|
return {
|
|
1061
1161
|
kind: "panel",
|
|
@@ -1114,6 +1214,7 @@ function birthMember(
|
|
|
1114
1214
|
stepAttempt: number;
|
|
1115
1215
|
enterRunning?: boolean | undefined;
|
|
1116
1216
|
producerId?: ("driver-simulated" | "pi" | string) | undefined;
|
|
1217
|
+
resolvedRoute?: { provider: string; model: string; selector: string } | undefined;
|
|
1117
1218
|
},
|
|
1118
1219
|
): PreparedDispatch {
|
|
1119
1220
|
const run = requireRun(context, input.run.runId);
|
|
@@ -1121,11 +1222,27 @@ function birthMember(
|
|
|
1121
1222
|
if (!birthAllowed(folded, input.step)) {
|
|
1122
1223
|
throw runtimeError("run_events_illegal", run.runId, "member birth is not legal");
|
|
1123
1224
|
}
|
|
1225
|
+
const promptText = context.eventStore.getRunPrompt(run.runId);
|
|
1226
|
+
if (promptText === undefined) {
|
|
1227
|
+
throw runtimeError("run_prompt_mismatch", run.runId, "prompt text missing from accepted run prompt store");
|
|
1228
|
+
}
|
|
1229
|
+
const promptHash = createHash("sha256").update(promptText, "utf8").digest("hex");
|
|
1230
|
+
const expectedHash = run.promptSha256.replace(/^sha256:/, "");
|
|
1231
|
+
if (promptHash !== expectedHash) {
|
|
1232
|
+
throw runtimeError("run_prompt_mismatch", run.runId, "prompt text does not match accepted promptSha256");
|
|
1233
|
+
}
|
|
1124
1234
|
const born = folded.currentStep?.panel.order.length ?? 0;
|
|
1125
1235
|
const allowed = input.step.assignments.allowedAgents;
|
|
1126
1236
|
const agentId = (allowed && allowed.length > born && allowed[born])
|
|
1127
1237
|
? allowed[born]!
|
|
1128
1238
|
: (input.step.kind === "agent" || input.step.kind === "moa" ? input.step.agent : "coordinator");
|
|
1239
|
+
let resolvedRoute = input.resolvedRoute;
|
|
1240
|
+
if (!resolvedRoute && input.producerId && input.producerId !== "driver-simulated") {
|
|
1241
|
+
const routeResult = resolveProducerRoute(context.projectRoot, input.step, agentId);
|
|
1242
|
+
if (!("error" in routeResult)) {
|
|
1243
|
+
resolvedRoute = routeResult;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1129
1246
|
const assignmentId = newVnextAssignmentId();
|
|
1130
1247
|
const attemptId = newVnextAttemptId();
|
|
1131
1248
|
const minted = mintCapabilitySecret();
|
|
@@ -1147,7 +1264,12 @@ function birthMember(
|
|
|
1147
1264
|
push("assignment.created", { assignmentId, stepId: input.stepId, stepAttempt: input.stepAttempt, agentId, status: "created" });
|
|
1148
1265
|
push("assignment.accepted", { assignmentId, status: "accepted" });
|
|
1149
1266
|
push("attempt.created", { attemptId, assignmentId, status: "created" });
|
|
1150
|
-
push("assignment.dispatched", {
|
|
1267
|
+
push("assignment.dispatched", {
|
|
1268
|
+
assignmentId,
|
|
1269
|
+
capabilityHash: minted.hash,
|
|
1270
|
+
status: "dispatched",
|
|
1271
|
+
...(resolvedRoute ? { model: { provider: resolvedRoute.provider, model: resolvedRoute.model } } : {}),
|
|
1272
|
+
});
|
|
1151
1273
|
push("attempt.status_changed", { attemptId, status: "starting" });
|
|
1152
1274
|
if (input.enterRunning) {
|
|
1153
1275
|
push("step.status_changed", { stepId: input.stepId, status: "running", previousStatus: "preparing" });
|
|
@@ -1165,16 +1287,16 @@ function birthMember(
|
|
|
1165
1287
|
});
|
|
1166
1288
|
const next = foldStoredVnextRun(context, run);
|
|
1167
1289
|
persistVnextRunState(context, run.runId, next, events[events.length - 1]!.sequence);
|
|
1168
|
-
const
|
|
1290
|
+
const rawContextPacket = buildFormalContextPacket({
|
|
1169
1291
|
project: run.projectId,
|
|
1170
1292
|
targetRole: agentId,
|
|
1171
1293
|
task: {
|
|
1172
1294
|
taskId: run.runId,
|
|
1173
1295
|
stepId: input.stepId,
|
|
1174
1296
|
stepAttempt: input.stepAttempt,
|
|
1175
|
-
objective:
|
|
1297
|
+
objective: promptText,
|
|
1176
1298
|
allowedOutcomes: [...input.step.outcomes],
|
|
1177
|
-
permissionCeiling: "edit",
|
|
1299
|
+
permissionCeiling: Object.values(input.step.repositories).some((access) => access === "write") ? "edit" : "read-only",
|
|
1178
1300
|
},
|
|
1179
1301
|
acceptanceCriteria: input.step.requiredEvidence.map((ev) => ({
|
|
1180
1302
|
id: ev.key,
|
|
@@ -1189,6 +1311,7 @@ function birthMember(
|
|
|
1189
1311
|
settledDecisions: [],
|
|
1190
1312
|
},
|
|
1191
1313
|
});
|
|
1314
|
+
const { packet: contextPacket } = pruneContextPacket(rawContextPacket);
|
|
1192
1315
|
const generatedPrompt = formatContextPacketForPrompt(contextPacket);
|
|
1193
1316
|
|
|
1194
1317
|
const member: PreparedDispatch = {
|
|
@@ -1215,7 +1338,9 @@ function birthMember(
|
|
|
1215
1338
|
allowedOutcomes: input.step.outcomes,
|
|
1216
1339
|
signal: controller.signal,
|
|
1217
1340
|
prompt: input.step.instructions ? `${input.step.instructions}\n\n${generatedPrompt}` : generatedPrompt,
|
|
1341
|
+
thinking: input.stepAttempt <= 1 ? "low" : "medium",
|
|
1218
1342
|
contextPacket,
|
|
1343
|
+
...(resolvedRoute ? { provider: resolvedRoute.provider, model: resolvedRoute.model } : {}),
|
|
1219
1344
|
},
|
|
1220
1345
|
controller,
|
|
1221
1346
|
state: next,
|
|
@@ -1391,6 +1516,11 @@ async function drivePanel(
|
|
|
1391
1516
|
};
|
|
1392
1517
|
|
|
1393
1518
|
const settleInvoked = (member: PreparedDispatch, produced: { result?: VnextProducerResult; error: boolean }): boolean => {
|
|
1519
|
+
if (produced.result?.effectUncertain) {
|
|
1520
|
+
settlementFailed = true;
|
|
1521
|
+
stopBirths = true;
|
|
1522
|
+
return false;
|
|
1523
|
+
}
|
|
1394
1524
|
try {
|
|
1395
1525
|
context.eventStore.transaction(() => {
|
|
1396
1526
|
if (vnextPanelDispatchSeams.failSettleMember?.(member)) {
|
|
@@ -1515,6 +1645,43 @@ async function drivePanel(
|
|
|
1515
1645
|
}
|
|
1516
1646
|
}
|
|
1517
1647
|
|
|
1648
|
+
function producerRoutingRecord(context: VnextRuntimeContext, dispatch: PreparedDispatch, result: VnextProducerResult, now: string): RoutingRecordV2 {
|
|
1649
|
+
const run = requireRun(context, dispatch.run.runId);
|
|
1650
|
+
if (result.costBasis === undefined || result.costBasis === null) {
|
|
1651
|
+
throw runtimeError("settle_missing_cost_basis", run.runId, `attempt settlement rejected: missing required costBasis for attempt ${dispatch.attemptId}`);
|
|
1652
|
+
}
|
|
1653
|
+
if (result.costBasis !== "metered" && result.costBasis !== "unmetered" && result.costBasis !== "unknown") {
|
|
1654
|
+
throw runtimeError("settle_invalid_cost_basis", run.runId, `attempt settlement rejected: invalid costBasis ${String(result.costBasis)}`);
|
|
1655
|
+
}
|
|
1656
|
+
if (result.costBasis === "metered" && (typeof result.costUsd !== "number" || !Number.isFinite(result.costUsd) || result.costUsd < 0)) {
|
|
1657
|
+
throw runtimeError("settle_invalid_cost", run.runId, "attempt settlement rejected: metered costBasis requires non-negative finite costUsd");
|
|
1658
|
+
}
|
|
1659
|
+
const envelope = loadVnextRunPlanEnvelope(context.eventStore, run);
|
|
1660
|
+
const rawHash = result.behavioralSha256 ?? behavioralConfigHash({
|
|
1661
|
+
requestedModel: result.requestedModel ?? "simulated",
|
|
1662
|
+
effectiveModel: result.effectiveModel ?? result.requestedModel ?? "simulated",
|
|
1663
|
+
agentRole: dispatch.step.kind === "agent" || dispatch.step.kind === "moa" ? dispatch.step.agent : undefined,
|
|
1664
|
+
toolPolicyVersion: envelope.revisions.toolPolicy,
|
|
1665
|
+
});
|
|
1666
|
+
const record: Record<string, unknown> = {
|
|
1667
|
+
schema: ROUTING_RECORD_V2_SCHEMA, recordedAt: now,
|
|
1668
|
+
project: run.projectId, runId: run.runId, stepId: dispatch.stepId,
|
|
1669
|
+
assignmentId: dispatch.assignmentId, attemptId: dispatch.attemptId,
|
|
1670
|
+
harness: result.harness ?? (dispatch.producerId === "pi" ? "pi" : "driver-simulated"),
|
|
1671
|
+
provider: result.provider ?? "simulated", requestedModel: result.requestedModel ?? "simulated",
|
|
1672
|
+
effectiveModel: result.effectiveModel ?? result.requestedModel ?? "simulated",
|
|
1673
|
+
behavioralSha256: rawHash.startsWith("sha256:") ? rawHash : `sha256:${rawHash}`,
|
|
1674
|
+
latencyMs: typeof result.latencyMs === "number" ? result.latencyMs : 0,
|
|
1675
|
+
costBasis: result.costBasis, costUsd: result.costUsd ?? null,
|
|
1676
|
+
retries: Math.max(0, dispatch.stepAttempt - 1),
|
|
1677
|
+
thinking: result.thinking ?? dispatch.request.thinking,
|
|
1678
|
+
};
|
|
1679
|
+
for (const field of ["agentRole", "contextTokens", "tokensIn", "tokensOut", "cacheReadTokens", "cacheWriteTokens", "priceRef", "providerMetadata"] as const) {
|
|
1680
|
+
if (result[field] !== undefined) record[field] = result[field];
|
|
1681
|
+
}
|
|
1682
|
+
return parseRoutingRecordV2(record);
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1518
1685
|
function settleMember(
|
|
1519
1686
|
context: VnextRuntimeContext,
|
|
1520
1687
|
dispatch: PreparedDispatch,
|
|
@@ -1551,6 +1718,8 @@ function settleMember(
|
|
|
1551
1718
|
};
|
|
1552
1719
|
push("attempt.status_changed", { attemptId: dispatch.attemptId, status: "settling" });
|
|
1553
1720
|
if (cancelling) {
|
|
1721
|
+
push("routing.attempt.recorded", { routing: producerRoutingRecord(context, dispatch,
|
|
1722
|
+
result ?? { outcome: "cancelled", costBasis: "unknown", costUsd: null }, now) });
|
|
1554
1723
|
push("assignment.result_recorded", { assignmentId: dispatch.assignmentId, resultClass: "cancelled", status: "result_recorded" });
|
|
1555
1724
|
push("attempt.status_changed", { attemptId: dispatch.attemptId, status: "terminal" });
|
|
1556
1725
|
push("assignment.terminal", { assignmentId: dispatch.assignmentId, outcome: "cancelled", status: "terminal" });
|
|
@@ -1592,57 +1761,10 @@ function settleMember(
|
|
|
1592
1761
|
push("attempt.status_changed", { attemptId: dispatch.attemptId, status: "terminal" });
|
|
1593
1762
|
push("assignment.terminal", { assignmentId: dispatch.assignmentId, outcome: "failed", status: "terminal" });
|
|
1594
1763
|
} else {
|
|
1595
|
-
if (!result
|
|
1764
|
+
if (!result) {
|
|
1596
1765
|
throw runtimeError("settle_missing_cost_basis", run.runId, `attempt settlement rejected: missing required costBasis for attempt ${dispatch.attemptId}`);
|
|
1597
1766
|
}
|
|
1598
|
-
|
|
1599
|
-
throw runtimeError("settle_invalid_cost_basis", run.runId, `attempt settlement rejected: invalid costBasis ${String(result.costBasis)}`);
|
|
1600
|
-
}
|
|
1601
|
-
if (result.costBasis === "metered") {
|
|
1602
|
-
if (typeof result.costUsd !== "number" || !Number.isFinite(result.costUsd) || result.costUsd < 0) {
|
|
1603
|
-
throw runtimeError("settle_invalid_cost", run.runId, "attempt settlement rejected: metered costBasis requires non-negative finite costUsd");
|
|
1604
|
-
}
|
|
1605
|
-
}
|
|
1606
|
-
|
|
1607
|
-
const envelope = loadVnextRunPlanEnvelope(context.eventStore, run);
|
|
1608
|
-
const rawHash = result.behavioralSha256 ?? behavioralConfigHash({
|
|
1609
|
-
requestedModel: result.requestedModel ?? "simulated",
|
|
1610
|
-
effectiveModel: result.effectiveModel ?? result.requestedModel ?? "simulated",
|
|
1611
|
-
agentRole: dispatch.step.kind === "agent" || dispatch.step.kind === "moa" ? dispatch.step.agent : undefined,
|
|
1612
|
-
toolPolicyVersion: envelope.revisions.toolPolicy,
|
|
1613
|
-
});
|
|
1614
|
-
const behavioralSha256 = rawHash.startsWith("sha256:") ? rawHash : `sha256:${rawHash}`;
|
|
1615
|
-
|
|
1616
|
-
const routingRecord: RoutingRecordV2 = {
|
|
1617
|
-
schema: ROUTING_RECORD_V2_SCHEMA,
|
|
1618
|
-
recordedAt: now,
|
|
1619
|
-
project: run.projectId,
|
|
1620
|
-
runId: run.runId,
|
|
1621
|
-
stepId: dispatch.stepId,
|
|
1622
|
-
assignmentId: dispatch.assignmentId,
|
|
1623
|
-
attemptId: dispatch.attemptId,
|
|
1624
|
-
harness: result.harness ?? (dispatch.producerId === "pi" ? "pi" : "driver-simulated"),
|
|
1625
|
-
provider: result.provider ?? "simulated",
|
|
1626
|
-
requestedModel: result.requestedModel ?? "simulated",
|
|
1627
|
-
effectiveModel: result.effectiveModel ?? result.requestedModel ?? "simulated",
|
|
1628
|
-
behavioralSha256,
|
|
1629
|
-
latencyMs: typeof result.latencyMs === "number" ? result.latencyMs : 0,
|
|
1630
|
-
costBasis: result.costBasis,
|
|
1631
|
-
costUsd: result.costBasis === "metered" ? (result.costUsd ?? 0) : (result.costUsd ?? null),
|
|
1632
|
-
retries: Math.max(0, dispatch.stepAttempt - 1),
|
|
1633
|
-
};
|
|
1634
|
-
|
|
1635
|
-
if (result.thinking !== undefined) routingRecord.thinking = result.thinking;
|
|
1636
|
-
if (result.agentRole !== undefined) routingRecord.agentRole = result.agentRole;
|
|
1637
|
-
if (result.contextTokens !== undefined) routingRecord.contextTokens = result.contextTokens;
|
|
1638
|
-
if (result.tokensIn !== undefined) routingRecord.tokensIn = result.tokensIn;
|
|
1639
|
-
if (result.tokensOut !== undefined) routingRecord.tokensOut = result.tokensOut;
|
|
1640
|
-
if (result.cacheReadTokens !== undefined) routingRecord.cacheReadTokens = result.cacheReadTokens;
|
|
1641
|
-
if (result.cacheWriteTokens !== undefined) routingRecord.cacheWriteTokens = result.cacheWriteTokens;
|
|
1642
|
-
if (result.priceRef !== undefined) routingRecord.priceRef = result.priceRef;
|
|
1643
|
-
if (result.providerMetadata !== undefined) routingRecord.providerMetadata = result.providerMetadata;
|
|
1644
|
-
|
|
1645
|
-
push("routing.attempt.recorded", { routing: parseRoutingRecordV2(routingRecord) });
|
|
1767
|
+
push("routing.attempt.recorded", { routing: producerRoutingRecord(context, dispatch, result, now) });
|
|
1646
1768
|
|
|
1647
1769
|
const outcome = typeof result.outcome === "string" ? result.outcome : undefined;
|
|
1648
1770
|
const known = outcome !== undefined && dispatch.step.outcomes.includes(outcome);
|
|
@@ -1844,7 +1966,11 @@ function unsupportedLimit(envelope: VnextRunPlanEnvelope): VnextRunHandoff | und
|
|
|
1844
1966
|
return undefined;
|
|
1845
1967
|
}
|
|
1846
1968
|
|
|
1847
|
-
function unsupportedStep(
|
|
1969
|
+
function unsupportedStep(
|
|
1970
|
+
plan: VnextCompiledPlan,
|
|
1971
|
+
step: VnextCompiledStep,
|
|
1972
|
+
producerId?: "driver-simulated" | "pi" | string,
|
|
1973
|
+
): Omit<VnextRunHandoff, "stepId"> | undefined {
|
|
1848
1974
|
if (step.kind === "gate") throw runtimeError("run_plan_corrupt", plan.workflowId, `unsupportedStep called on gate without envelope; use unsupportedGateStep instead`);
|
|
1849
1975
|
if (step.kind !== "agent" && step.kind !== "moa" && step.kind !== "approval" && step.kind !== "wait") {
|
|
1850
1976
|
return { reason: "step_unsupported", field: "kind", detail: `step kind ${step.kind} is not executed in this slice` };
|
|
@@ -1883,6 +2009,13 @@ function unsupportedStep(plan: VnextCompiledPlan, step: VnextCompiledStep): Omit
|
|
|
1883
2009
|
return { reason: "step_unsupported", field: "repositories", detail: `invalid repository access '${access}' on ${repoId}` };
|
|
1884
2010
|
}
|
|
1885
2011
|
}
|
|
2012
|
+
if (producerId !== "driver-simulated" && Object.values(step.repositories).some((access) => access === "write")) {
|
|
2013
|
+
return {
|
|
2014
|
+
reason: "step_unsupported",
|
|
2015
|
+
field: "repositories",
|
|
2016
|
+
detail: "live write steps are unsupported until writer sandboxing witness passes",
|
|
2017
|
+
};
|
|
2018
|
+
}
|
|
1886
2019
|
return undefined;
|
|
1887
2020
|
}
|
|
1888
2021
|
|
|
@@ -2364,9 +2497,12 @@ export function recoverVnextRun(
|
|
|
2364
2497
|
const run = requireRun(context, runId);
|
|
2365
2498
|
let state = foldStoredVnextRun(context, run);
|
|
2366
2499
|
|
|
2367
|
-
if (state.status !== "blocked_uncertain" && state.currentStep?.effectState !== "blocked_uncertain") {
|
|
2500
|
+
if (state.status !== "blocked_uncertain" && state.status !== "cancelling" && state.currentStep?.effectState !== "blocked_uncertain") {
|
|
2368
2501
|
throw runtimeError("run_events_illegal", runId, `run ${runId} is not in blocked_uncertain status`);
|
|
2369
2502
|
}
|
|
2503
|
+
if (state.status === "cancelling" && state.currentStep?.effectState === "blocked_uncertain") {
|
|
2504
|
+
state = { ...state, status: "blocked_uncertain" };
|
|
2505
|
+
}
|
|
2370
2506
|
|
|
2371
2507
|
const now = new Date().toISOString();
|
|
2372
2508
|
let sequence = context.eventStore.nextSequence(runId);
|