@kontextmind/kxm 0.6.0 → 0.7.10
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 +7 -0
- package/.kxm/workflows/default.yaml +47 -0
- package/CHANGELOG.md +39 -7
- package/README.md +1 -0
- package/docs/README.md +5 -0
- package/docs/adr/ADR-0002-browser-automation-steel-doks.md +103 -0
- package/docs/agent-skills.md +135 -0
- package/docs/architecture.md +1 -1
- package/docs/assignment-runner.md +21 -8
- package/docs/browser-automation.md +116 -0
- package/docs/configuration.md +11 -2
- package/docs/getting-started.md +21 -0
- package/docs/kb/how-credentials-retrieved-safely.md +31 -0
- package/docs/kb/how-to-capture-and-annotate-section.md +60 -0
- package/docs/kb/how-to-connect-playwright-to-steel.md +54 -0
- package/docs/kb/how-to-recover-expired-session-or-orphan.md +54 -0
- package/docs/kb/how-to-resume-after-mfa.md +28 -0
- package/docs/kb/how-to-take-over-session.md +32 -0
- package/docs/kb/why-authentication-disappeared.md +32 -0
- package/docs/kb/why-automation-opened-different-browser.md +32 -0
- package/docs/kb/why-session-viewer-cannot-control.md +31 -0
- package/docs/kxm-handbook.md +3 -3
- package/docs/operations.md +24 -0
- package/docs/operator-pi-packages.md +63 -0
- package/docs/prompts/browser-annotate-feedback.md +41 -0
- package/docs/prompts/browser-diagnose-recover.md +38 -0
- package/docs/prompts/browser-explore.md +42 -0
- package/docs/prompts/browser-repro-fix.md +48 -0
- package/docs/prompts/browser-start.md +41 -0
- package/docs/prompts/browser-takeover.md +50 -0
- package/docs/skills/repo-work-delivery.md +107 -0
- package/docs/skills.md +2 -0
- package/docs/test-matrix.md +4 -3
- package/docs/troubleshooting.md +41 -1
- 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 +41620 -35578
- package/plugins/kxm/dist/core.js +271 -34
- package/plugins/kxm/dist/extension.js +7759 -86
- package/plugins/kxm/dist/mcp-server.js +75 -21
- package/plugins/kxm/dist/runtime.js +8218 -2328
- package/plugins/kxm/dist/server.js +3125 -2260
- package/plugins/kxm/dist/vnext-runtime-supervisor.js +5961 -661
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/SUITE.md +5 -0
- package/plugins/kxm/skills/hints.json +103 -0
- package/plugins/kxm/skills/kxm/SKILL.md +30 -83
- package/plugins/kxm/skills/kxm-browser-annotate/SKILL.md +90 -0
- package/plugins/kxm/skills/kxm-browser-auth/SKILL.md +47 -0
- package/plugins/kxm/skills/kxm-browser-diagnostics/SKILL.md +48 -0
- package/plugins/kxm/skills/kxm-browser-explore/SKILL.md +48 -0
- package/plugins/kxm/skills/kxm-browser-session/SKILL.md +94 -0
- package/plugins/kxm/skills/kxm-browser-takeover/SKILL.md +87 -0
- package/plugins/kxm/skills/kxm-browser-verify/SKILL.md +71 -0
- 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 +43 -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 +42 -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/browser.ts +603 -0
- package/plugins/kxm/src/cli/context-skills.ts +373 -0
- package/plugins/kxm/src/cli/hub.ts +614 -0
- package/plugins/kxm/src/cli/roles.ts +615 -0
- package/plugins/kxm/src/cli/system.ts +906 -0
- package/plugins/kxm/src/cli/tasks.ts +364 -0
- package/plugins/kxm/src/cli/types.ts +270 -0
- package/plugins/kxm/src/cli/vnext.ts +698 -0
- package/plugins/kxm/src/cli/workflows.ts +699 -0
- package/plugins/kxm/src/cli.ts +362 -2849
- package/plugins/kxm/src/commands.ts +150 -8
- package/plugins/kxm/src/completion-install.ts +223 -0
- package/plugins/kxm/src/config.ts +7 -4
- package/plugins/kxm/src/context-packet.ts +172 -0
- package/plugins/kxm/src/database.ts +1 -1
- package/plugins/kxm/src/extension.ts +36 -1
- package/plugins/kxm/src/external-effects.ts +357 -8
- package/plugins/kxm/src/hub-env.ts +193 -0
- package/plugins/kxm/src/hub.ts +2 -4
- package/plugins/kxm/src/improve.ts +72 -0
- package/plugins/kxm/src/init-guide-setup.ts +547 -0
- package/plugins/kxm/src/local-snapshot.ts +1 -1
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/model-inventory.ts +127 -0
- package/plugins/kxm/src/modes.ts +348 -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/protocol.ts +111 -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 +710 -0
- package/plugins/kxm/src/routing.ts +99 -1
- package/plugins/kxm/src/runtime.ts +4 -0
- package/plugins/kxm/src/safety-integrity.ts +76 -0
- package/plugins/kxm/src/session-work.ts +9 -2
- package/plugins/kxm/src/sqlite.ts +76 -0
- package/plugins/kxm/src/ssh-remote.ts +560 -0
- package/plugins/kxm/src/store.ts +1 -1
- package/plugins/kxm/src/studio-layout.ts +660 -17
- package/plugins/kxm/src/subagent-control.ts +312 -0
- 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-bindings.ts +1 -1
- package/plugins/kxm/src/vnext-config.ts +53 -111
- package/plugins/kxm/src/vnext-engine-command.ts +2 -0
- package/plugins/kxm/src/vnext-engine.ts +214 -62
- package/plugins/kxm/src/vnext-harness.ts +336 -84
- package/plugins/kxm/src/vnext-oneshot-evidence.ts +117 -0
- package/plugins/kxm/src/vnext-oneshot-process.ts +187 -0
- package/plugins/kxm/src/vnext-oneshot-producer.ts +182 -224
- package/plugins/kxm/src/vnext-pi-producer.ts +11 -7
- package/plugins/kxm/src/vnext-runtime-store.ts +36 -2
- package/plugins/kxm/src/vnext-runtime-supervisor.ts +122 -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/plugins/kxm/src/workflow.ts +144 -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/modes.schema.json +56 -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-bump-version.mjs +146 -0
- package/scripts/kxm-hub.mjs +150 -2
- package/scripts/kxm-publish-npm.mjs +3 -1
- package/scripts/kxm-release-github.mjs +3 -1
- package/scripts/kxm.mjs +0 -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";
|
|
@@ -88,6 +94,21 @@ import {
|
|
|
88
94
|
parseRoutingRecordV2,
|
|
89
95
|
behavioralConfigHash,
|
|
90
96
|
} from "./routing.ts";
|
|
97
|
+
import {
|
|
98
|
+
assertCommandSeatbelt,
|
|
99
|
+
DESTRUCTIVE_COMMAND_PATTERNS,
|
|
100
|
+
isRtkBypassRequired,
|
|
101
|
+
assertPinnedSshHostKeyPolicy,
|
|
102
|
+
type RtkBypassContext,
|
|
103
|
+
} from "./safety-integrity.ts";
|
|
104
|
+
|
|
105
|
+
export {
|
|
106
|
+
assertCommandSeatbelt,
|
|
107
|
+
DESTRUCTIVE_COMMAND_PATTERNS,
|
|
108
|
+
isRtkBypassRequired,
|
|
109
|
+
assertPinnedSshHostKeyPolicy,
|
|
110
|
+
type RtkBypassContext,
|
|
111
|
+
};
|
|
91
112
|
|
|
92
113
|
const trustedProducers = new WeakSet<object>();
|
|
93
114
|
const CAPABILITY_PREFIX = "kxm-attempt-capability\0";
|
|
@@ -116,6 +137,8 @@ export interface VnextProducerRequest {
|
|
|
116
137
|
|
|
117
138
|
export interface VnextProducerResult {
|
|
118
139
|
readonly outcome: string;
|
|
140
|
+
/** Host cannot attest termination; retain the issued attempt, never replay or settle it. */
|
|
141
|
+
readonly effectUncertain?: true;
|
|
119
142
|
readonly costBasis?: "metered" | "unmetered" | "unknown";
|
|
120
143
|
readonly costUsd?: number | null;
|
|
121
144
|
readonly tokensIn?: number | null;
|
|
@@ -136,7 +159,7 @@ export interface VnextProducerResult {
|
|
|
136
159
|
}
|
|
137
160
|
|
|
138
161
|
export interface VnextProducer {
|
|
139
|
-
readonly id: "driver-simulated" | "pi" | "oneshot"
|
|
162
|
+
readonly id: "driver-simulated" | "pi" | "oneshot";
|
|
140
163
|
produce(request: VnextProducerRequest): Promise<VnextProducerResult>;
|
|
141
164
|
}
|
|
142
165
|
|
|
@@ -332,7 +355,7 @@ export async function driveVnextRun(
|
|
|
332
355
|
context: VnextRuntimeContext,
|
|
333
356
|
runId: string,
|
|
334
357
|
producer: VnextProducer,
|
|
335
|
-
options: { allowLimits?: boolean } = {},
|
|
358
|
+
options: { allowLimits?: boolean; liveMode?: boolean } = {},
|
|
336
359
|
): Promise<VnextRunDriveResult> {
|
|
337
360
|
requireTrustedProducer(producer);
|
|
338
361
|
const run = requireRun(context, runId);
|
|
@@ -347,7 +370,7 @@ async function driveAdmitted(
|
|
|
347
370
|
runId: string,
|
|
348
371
|
producer: VnextProducer,
|
|
349
372
|
token: string,
|
|
350
|
-
options: { allowLimits?: boolean } = {},
|
|
373
|
+
options: { allowLimits?: boolean; liveMode?: boolean } = {},
|
|
351
374
|
): Promise<VnextRunDriveResult> {
|
|
352
375
|
const run = requireRun(context, runId);
|
|
353
376
|
if (run.status === "preparing") {
|
|
@@ -407,7 +430,11 @@ export class VnextRunScheduler {
|
|
|
407
430
|
return new VnextRunScheduler(context, bundle.configRevision);
|
|
408
431
|
}
|
|
409
432
|
|
|
410
|
-
enqueue(
|
|
433
|
+
enqueue(
|
|
434
|
+
runId: string,
|
|
435
|
+
producer: VnextProducer,
|
|
436
|
+
options: { allowLimits?: boolean; liveMode?: boolean } = {},
|
|
437
|
+
): Promise<VnextRunDriveResult> {
|
|
411
438
|
requireTrustedProducer(producer);
|
|
412
439
|
const policy = vnextSchedulerPolicy(this.context.eventStore.path);
|
|
413
440
|
if (!policy || policy.configRevision !== this.configRevision) {
|
|
@@ -421,7 +448,7 @@ export class VnextRunScheduler {
|
|
|
421
448
|
runId,
|
|
422
449
|
envelope.revisions.config,
|
|
423
450
|
envelope.projectLimits.maxConcurrentRuns,
|
|
424
|
-
(token) => driveAdmitted(this.context, runId, producer, token),
|
|
451
|
+
(token) => driveAdmitted(this.context, runId, producer, token, options),
|
|
425
452
|
) as Promise<VnextRunDriveResult>;
|
|
426
453
|
} catch (error) {
|
|
427
454
|
return Promise.reject(error);
|
|
@@ -471,7 +498,7 @@ export function verifyVnextAttemptCapability(
|
|
|
471
498
|
function invokeProducer(
|
|
472
499
|
producer: VnextProducer,
|
|
473
500
|
request: VnextProducerRequest,
|
|
474
|
-
): Promise<{ result?: VnextProducerResult; error: boolean }> {
|
|
501
|
+
): Promise<{ result?: VnextProducerResult; error: boolean; errorCode?: string }> {
|
|
475
502
|
let pending: Promise<VnextProducerResult>;
|
|
476
503
|
try {
|
|
477
504
|
pending = Promise.resolve(producer.produce(request));
|
|
@@ -545,6 +572,7 @@ async function runPreparedCommandGate(
|
|
|
545
572
|
definition: { readonly kind: "command"; readonly argv: readonly string[]; readonly timeoutMs: number; readonly cwd?: "control" },
|
|
546
573
|
token: string,
|
|
547
574
|
): Promise<VnextRunDriveResult> {
|
|
575
|
+
assertCommandSeatbelt(definition.argv.join(" "));
|
|
548
576
|
const storePath = context.eventStore.path;
|
|
549
577
|
const observer = createCommandObserver({
|
|
550
578
|
definition,
|
|
@@ -884,6 +912,79 @@ function unreconciledPanelAttemptId(state: VnextRunState): string | undefined {
|
|
|
884
912
|
return undefined;
|
|
885
913
|
}
|
|
886
914
|
|
|
915
|
+
function resolveProducerRoute(
|
|
916
|
+
projectRoot: string,
|
|
917
|
+
step: VnextCompiledStep,
|
|
918
|
+
agentId: string,
|
|
919
|
+
): { provider: string; model: string; selector: string } | { error: Omit<VnextRunHandoff, "stepId"> } {
|
|
920
|
+
let agentModel: string | undefined;
|
|
921
|
+
const agentFile = join(projectRoot, ".kxm", "agents", `${agentId}.yaml`);
|
|
922
|
+
if (existsSync(agentFile)) {
|
|
923
|
+
try {
|
|
924
|
+
const parsed = parse(readFileSync(agentFile, "utf8")) as Record<string, unknown>;
|
|
925
|
+
if (typeof parsed?.model === "string") {
|
|
926
|
+
agentModel = parsed.model;
|
|
927
|
+
}
|
|
928
|
+
} catch {
|
|
929
|
+
// ignore
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
let selector = typeof step.model === "string" ? step.model : agentModel;
|
|
934
|
+
if (!selector) {
|
|
935
|
+
if (agentId === "implementer") {
|
|
936
|
+
selector = "xai/grok-4.6";
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
if (!selector) {
|
|
940
|
+
return {
|
|
941
|
+
error: {
|
|
942
|
+
reason: "step_unsupported",
|
|
943
|
+
field: "model",
|
|
944
|
+
detail: "producer_route_unsupported: agent or step has no model declared",
|
|
945
|
+
},
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
const slash = selector.indexOf("/");
|
|
950
|
+
if (slash <= 0 || slash === selector.length - 1) {
|
|
951
|
+
return {
|
|
952
|
+
error: {
|
|
953
|
+
reason: "step_unsupported",
|
|
954
|
+
field: "model",
|
|
955
|
+
detail: `producer_route_unsupported: invalid selector '${selector}'`,
|
|
956
|
+
},
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
if (!isProducerAdmitted(projectRoot, selector)) {
|
|
961
|
+
return {
|
|
962
|
+
error: {
|
|
963
|
+
reason: "step_unsupported",
|
|
964
|
+
field: "model",
|
|
965
|
+
detail: `producer_route_unsupported: model '${selector}' is not admitted`,
|
|
966
|
+
},
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
const role = agentId === "implementer" ? "writer" : agentId;
|
|
971
|
+
const roleBindings = listRoleBindings(projectRoot);
|
|
972
|
+
const roster = roleBindings[role];
|
|
973
|
+
if (roster && !roster.includes(selector)) {
|
|
974
|
+
return {
|
|
975
|
+
error: {
|
|
976
|
+
reason: "step_unsupported",
|
|
977
|
+
field: "model",
|
|
978
|
+
detail: `producer_route_unsupported: model '${selector}' not in role '${role}' roster`,
|
|
979
|
+
},
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
const provider = selector.slice(0, slash);
|
|
984
|
+
const model = selector.slice(slash + 1);
|
|
985
|
+
return { provider, model, selector };
|
|
986
|
+
}
|
|
987
|
+
|
|
887
988
|
function prepareDispatch(
|
|
888
989
|
context: VnextRuntimeContext,
|
|
889
990
|
runId: string,
|
|
@@ -1022,8 +1123,22 @@ function prepareDispatch(
|
|
|
1022
1123
|
};
|
|
1023
1124
|
}
|
|
1024
1125
|
|
|
1025
|
-
const unsupported = unsupportedStep(plan, step);
|
|
1126
|
+
const unsupported = unsupportedStep(plan, step, producerId);
|
|
1026
1127
|
if (unsupported) return { kind: "return", state, handoff: { ...unsupported, stepId } };
|
|
1128
|
+
|
|
1129
|
+
let resolvedRoute: { provider: string; model: string; selector: string } | undefined;
|
|
1130
|
+
if (producerId !== "driver-simulated") {
|
|
1131
|
+
const allowed = step.assignments.allowedAgents;
|
|
1132
|
+
const agentId = (allowed && allowed.length > 0 && allowed[0])
|
|
1133
|
+
? allowed[0]!
|
|
1134
|
+
: (step.kind === "agent" || step.kind === "moa" ? step.agent : "coordinator");
|
|
1135
|
+
const routeResult = resolveProducerRoute(context.projectRoot, step, agentId);
|
|
1136
|
+
if ("error" in routeResult) {
|
|
1137
|
+
return { kind: "return", state, handoff: { ...routeResult.error, stepId } };
|
|
1138
|
+
}
|
|
1139
|
+
resolvedRoute = routeResult;
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1027
1142
|
const used = state.stepAttempts[stepId] ?? 0;
|
|
1028
1143
|
if (used >= step.maxAttempts) {
|
|
1029
1144
|
return { kind: "return", ...failBudget(context, run, plan, state, "budget_step_attempts", stepId) };
|
|
@@ -1056,6 +1171,7 @@ function prepareDispatch(
|
|
|
1056
1171
|
stepAttempt,
|
|
1057
1172
|
enterRunning: true,
|
|
1058
1173
|
producerId,
|
|
1174
|
+
resolvedRoute,
|
|
1059
1175
|
});
|
|
1060
1176
|
return {
|
|
1061
1177
|
kind: "panel",
|
|
@@ -1114,6 +1230,7 @@ function birthMember(
|
|
|
1114
1230
|
stepAttempt: number;
|
|
1115
1231
|
enterRunning?: boolean | undefined;
|
|
1116
1232
|
producerId?: ("driver-simulated" | "pi" | string) | undefined;
|
|
1233
|
+
resolvedRoute?: { provider: string; model: string; selector: string } | undefined;
|
|
1117
1234
|
},
|
|
1118
1235
|
): PreparedDispatch {
|
|
1119
1236
|
const run = requireRun(context, input.run.runId);
|
|
@@ -1121,11 +1238,27 @@ function birthMember(
|
|
|
1121
1238
|
if (!birthAllowed(folded, input.step)) {
|
|
1122
1239
|
throw runtimeError("run_events_illegal", run.runId, "member birth is not legal");
|
|
1123
1240
|
}
|
|
1241
|
+
const promptText = context.eventStore.getRunPrompt(run.runId);
|
|
1242
|
+
if (promptText === undefined) {
|
|
1243
|
+
throw runtimeError("run_prompt_mismatch", run.runId, "prompt text missing from accepted run prompt store");
|
|
1244
|
+
}
|
|
1245
|
+
const promptHash = createHash("sha256").update(promptText, "utf8").digest("hex");
|
|
1246
|
+
const expectedHash = run.promptSha256.replace(/^sha256:/, "");
|
|
1247
|
+
if (promptHash !== expectedHash) {
|
|
1248
|
+
throw runtimeError("run_prompt_mismatch", run.runId, "prompt text does not match accepted promptSha256");
|
|
1249
|
+
}
|
|
1124
1250
|
const born = folded.currentStep?.panel.order.length ?? 0;
|
|
1125
1251
|
const allowed = input.step.assignments.allowedAgents;
|
|
1126
1252
|
const agentId = (allowed && allowed.length > born && allowed[born])
|
|
1127
1253
|
? allowed[born]!
|
|
1128
1254
|
: (input.step.kind === "agent" || input.step.kind === "moa" ? input.step.agent : "coordinator");
|
|
1255
|
+
let resolvedRoute = input.resolvedRoute;
|
|
1256
|
+
if (!resolvedRoute && input.producerId && input.producerId !== "driver-simulated") {
|
|
1257
|
+
const routeResult = resolveProducerRoute(context.projectRoot, input.step, agentId);
|
|
1258
|
+
if (!("error" in routeResult)) {
|
|
1259
|
+
resolvedRoute = routeResult;
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1129
1262
|
const assignmentId = newVnextAssignmentId();
|
|
1130
1263
|
const attemptId = newVnextAttemptId();
|
|
1131
1264
|
const minted = mintCapabilitySecret();
|
|
@@ -1147,7 +1280,12 @@ function birthMember(
|
|
|
1147
1280
|
push("assignment.created", { assignmentId, stepId: input.stepId, stepAttempt: input.stepAttempt, agentId, status: "created" });
|
|
1148
1281
|
push("assignment.accepted", { assignmentId, status: "accepted" });
|
|
1149
1282
|
push("attempt.created", { attemptId, assignmentId, status: "created" });
|
|
1150
|
-
push("assignment.dispatched", {
|
|
1283
|
+
push("assignment.dispatched", {
|
|
1284
|
+
assignmentId,
|
|
1285
|
+
capabilityHash: minted.hash,
|
|
1286
|
+
status: "dispatched",
|
|
1287
|
+
...(resolvedRoute ? { model: { provider: resolvedRoute.provider, model: resolvedRoute.model } } : {}),
|
|
1288
|
+
});
|
|
1151
1289
|
push("attempt.status_changed", { attemptId, status: "starting" });
|
|
1152
1290
|
if (input.enterRunning) {
|
|
1153
1291
|
push("step.status_changed", { stepId: input.stepId, status: "running", previousStatus: "preparing" });
|
|
@@ -1165,16 +1303,16 @@ function birthMember(
|
|
|
1165
1303
|
});
|
|
1166
1304
|
const next = foldStoredVnextRun(context, run);
|
|
1167
1305
|
persistVnextRunState(context, run.runId, next, events[events.length - 1]!.sequence);
|
|
1168
|
-
const
|
|
1306
|
+
const rawContextPacket = buildFormalContextPacket({
|
|
1169
1307
|
project: run.projectId,
|
|
1170
1308
|
targetRole: agentId,
|
|
1171
1309
|
task: {
|
|
1172
1310
|
taskId: run.runId,
|
|
1173
1311
|
stepId: input.stepId,
|
|
1174
1312
|
stepAttempt: input.stepAttempt,
|
|
1175
|
-
objective:
|
|
1313
|
+
objective: promptText,
|
|
1176
1314
|
allowedOutcomes: [...input.step.outcomes],
|
|
1177
|
-
permissionCeiling: "edit",
|
|
1315
|
+
permissionCeiling: Object.values(input.step.repositories).some((access) => access === "write") ? "edit" : "read-only",
|
|
1178
1316
|
},
|
|
1179
1317
|
acceptanceCriteria: input.step.requiredEvidence.map((ev) => ({
|
|
1180
1318
|
id: ev.key,
|
|
@@ -1189,6 +1327,7 @@ function birthMember(
|
|
|
1189
1327
|
settledDecisions: [],
|
|
1190
1328
|
},
|
|
1191
1329
|
});
|
|
1330
|
+
const { packet: contextPacket } = pruneContextPacket(rawContextPacket);
|
|
1192
1331
|
const generatedPrompt = formatContextPacketForPrompt(contextPacket);
|
|
1193
1332
|
|
|
1194
1333
|
const member: PreparedDispatch = {
|
|
@@ -1215,7 +1354,9 @@ function birthMember(
|
|
|
1215
1354
|
allowedOutcomes: input.step.outcomes,
|
|
1216
1355
|
signal: controller.signal,
|
|
1217
1356
|
prompt: input.step.instructions ? `${input.step.instructions}\n\n${generatedPrompt}` : generatedPrompt,
|
|
1357
|
+
thinking: input.stepAttempt <= 1 ? "low" : "medium",
|
|
1218
1358
|
contextPacket,
|
|
1359
|
+
...(resolvedRoute ? { provider: resolvedRoute.provider, model: resolvedRoute.model } : {}),
|
|
1219
1360
|
},
|
|
1220
1361
|
controller,
|
|
1221
1362
|
state: next,
|
|
@@ -1391,6 +1532,11 @@ async function drivePanel(
|
|
|
1391
1532
|
};
|
|
1392
1533
|
|
|
1393
1534
|
const settleInvoked = (member: PreparedDispatch, produced: { result?: VnextProducerResult; error: boolean }): boolean => {
|
|
1535
|
+
if (produced.result?.effectUncertain) {
|
|
1536
|
+
settlementFailed = true;
|
|
1537
|
+
stopBirths = true;
|
|
1538
|
+
return false;
|
|
1539
|
+
}
|
|
1394
1540
|
try {
|
|
1395
1541
|
context.eventStore.transaction(() => {
|
|
1396
1542
|
if (vnextPanelDispatchSeams.failSettleMember?.(member)) {
|
|
@@ -1515,6 +1661,43 @@ async function drivePanel(
|
|
|
1515
1661
|
}
|
|
1516
1662
|
}
|
|
1517
1663
|
|
|
1664
|
+
function producerRoutingRecord(context: VnextRuntimeContext, dispatch: PreparedDispatch, result: VnextProducerResult, now: string): RoutingRecordV2 {
|
|
1665
|
+
const run = requireRun(context, dispatch.run.runId);
|
|
1666
|
+
if (result.costBasis === undefined || result.costBasis === null) {
|
|
1667
|
+
throw runtimeError("settle_missing_cost_basis", run.runId, `attempt settlement rejected: missing required costBasis for attempt ${dispatch.attemptId}`);
|
|
1668
|
+
}
|
|
1669
|
+
if (result.costBasis !== "metered" && result.costBasis !== "unmetered" && result.costBasis !== "unknown") {
|
|
1670
|
+
throw runtimeError("settle_invalid_cost_basis", run.runId, `attempt settlement rejected: invalid costBasis ${String(result.costBasis)}`);
|
|
1671
|
+
}
|
|
1672
|
+
if (result.costBasis === "metered" && (typeof result.costUsd !== "number" || !Number.isFinite(result.costUsd) || result.costUsd < 0)) {
|
|
1673
|
+
throw runtimeError("settle_invalid_cost", run.runId, "attempt settlement rejected: metered costBasis requires non-negative finite costUsd");
|
|
1674
|
+
}
|
|
1675
|
+
const envelope = loadVnextRunPlanEnvelope(context.eventStore, run);
|
|
1676
|
+
const rawHash = result.behavioralSha256 ?? behavioralConfigHash({
|
|
1677
|
+
requestedModel: result.requestedModel ?? "simulated",
|
|
1678
|
+
effectiveModel: result.effectiveModel ?? result.requestedModel ?? "simulated",
|
|
1679
|
+
agentRole: dispatch.step.kind === "agent" || dispatch.step.kind === "moa" ? dispatch.step.agent : undefined,
|
|
1680
|
+
toolPolicyVersion: envelope.revisions.toolPolicy,
|
|
1681
|
+
});
|
|
1682
|
+
const record: Record<string, unknown> = {
|
|
1683
|
+
schema: ROUTING_RECORD_V2_SCHEMA, recordedAt: now,
|
|
1684
|
+
project: run.projectId, runId: run.runId, stepId: dispatch.stepId,
|
|
1685
|
+
assignmentId: dispatch.assignmentId, attemptId: dispatch.attemptId,
|
|
1686
|
+
harness: result.harness ?? (dispatch.producerId === "pi" ? "pi" : "driver-simulated"),
|
|
1687
|
+
provider: result.provider ?? "simulated", requestedModel: result.requestedModel ?? "simulated",
|
|
1688
|
+
effectiveModel: result.effectiveModel ?? result.requestedModel ?? "simulated",
|
|
1689
|
+
behavioralSha256: rawHash.startsWith("sha256:") ? rawHash : `sha256:${rawHash}`,
|
|
1690
|
+
latencyMs: typeof result.latencyMs === "number" ? result.latencyMs : 0,
|
|
1691
|
+
costBasis: result.costBasis, costUsd: result.costUsd ?? null,
|
|
1692
|
+
retries: Math.max(0, dispatch.stepAttempt - 1),
|
|
1693
|
+
thinking: result.thinking ?? dispatch.request.thinking,
|
|
1694
|
+
};
|
|
1695
|
+
for (const field of ["agentRole", "contextTokens", "tokensIn", "tokensOut", "cacheReadTokens", "cacheWriteTokens", "priceRef", "providerMetadata"] as const) {
|
|
1696
|
+
if (result[field] !== undefined) record[field] = result[field];
|
|
1697
|
+
}
|
|
1698
|
+
return parseRoutingRecordV2(record);
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1518
1701
|
function settleMember(
|
|
1519
1702
|
context: VnextRuntimeContext,
|
|
1520
1703
|
dispatch: PreparedDispatch,
|
|
@@ -1551,6 +1734,8 @@ function settleMember(
|
|
|
1551
1734
|
};
|
|
1552
1735
|
push("attempt.status_changed", { attemptId: dispatch.attemptId, status: "settling" });
|
|
1553
1736
|
if (cancelling) {
|
|
1737
|
+
push("routing.attempt.recorded", { routing: producerRoutingRecord(context, dispatch,
|
|
1738
|
+
result ?? { outcome: "cancelled", costBasis: "unknown", costUsd: null }, now) });
|
|
1554
1739
|
push("assignment.result_recorded", { assignmentId: dispatch.assignmentId, resultClass: "cancelled", status: "result_recorded" });
|
|
1555
1740
|
push("attempt.status_changed", { attemptId: dispatch.attemptId, status: "terminal" });
|
|
1556
1741
|
push("assignment.terminal", { assignmentId: dispatch.assignmentId, outcome: "cancelled", status: "terminal" });
|
|
@@ -1592,57 +1777,10 @@ function settleMember(
|
|
|
1592
1777
|
push("attempt.status_changed", { attemptId: dispatch.attemptId, status: "terminal" });
|
|
1593
1778
|
push("assignment.terminal", { assignmentId: dispatch.assignmentId, outcome: "failed", status: "terminal" });
|
|
1594
1779
|
} else {
|
|
1595
|
-
if (!result
|
|
1780
|
+
if (!result) {
|
|
1596
1781
|
throw runtimeError("settle_missing_cost_basis", run.runId, `attempt settlement rejected: missing required costBasis for attempt ${dispatch.attemptId}`);
|
|
1597
1782
|
}
|
|
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) });
|
|
1783
|
+
push("routing.attempt.recorded", { routing: producerRoutingRecord(context, dispatch, result, now) });
|
|
1646
1784
|
|
|
1647
1785
|
const outcome = typeof result.outcome === "string" ? result.outcome : undefined;
|
|
1648
1786
|
const known = outcome !== undefined && dispatch.step.outcomes.includes(outcome);
|
|
@@ -1844,7 +1982,11 @@ function unsupportedLimit(envelope: VnextRunPlanEnvelope): VnextRunHandoff | und
|
|
|
1844
1982
|
return undefined;
|
|
1845
1983
|
}
|
|
1846
1984
|
|
|
1847
|
-
function unsupportedStep(
|
|
1985
|
+
function unsupportedStep(
|
|
1986
|
+
plan: VnextCompiledPlan,
|
|
1987
|
+
step: VnextCompiledStep,
|
|
1988
|
+
producerId?: "driver-simulated" | "pi" | string,
|
|
1989
|
+
): Omit<VnextRunHandoff, "stepId"> | undefined {
|
|
1848
1990
|
if (step.kind === "gate") throw runtimeError("run_plan_corrupt", plan.workflowId, `unsupportedStep called on gate without envelope; use unsupportedGateStep instead`);
|
|
1849
1991
|
if (step.kind !== "agent" && step.kind !== "moa" && step.kind !== "approval" && step.kind !== "wait") {
|
|
1850
1992
|
return { reason: "step_unsupported", field: "kind", detail: `step kind ${step.kind} is not executed in this slice` };
|
|
@@ -1883,6 +2025,13 @@ function unsupportedStep(plan: VnextCompiledPlan, step: VnextCompiledStep): Omit
|
|
|
1883
2025
|
return { reason: "step_unsupported", field: "repositories", detail: `invalid repository access '${access}' on ${repoId}` };
|
|
1884
2026
|
}
|
|
1885
2027
|
}
|
|
2028
|
+
if (producerId !== "driver-simulated" && Object.values(step.repositories).some((access) => access === "write")) {
|
|
2029
|
+
return {
|
|
2030
|
+
reason: "step_unsupported",
|
|
2031
|
+
field: "repositories",
|
|
2032
|
+
detail: "live write steps are unsupported until writer sandboxing witness passes",
|
|
2033
|
+
};
|
|
2034
|
+
}
|
|
1886
2035
|
return undefined;
|
|
1887
2036
|
}
|
|
1888
2037
|
|
|
@@ -2364,9 +2513,12 @@ export function recoverVnextRun(
|
|
|
2364
2513
|
const run = requireRun(context, runId);
|
|
2365
2514
|
let state = foldStoredVnextRun(context, run);
|
|
2366
2515
|
|
|
2367
|
-
if (state.status !== "blocked_uncertain" && state.currentStep?.effectState !== "blocked_uncertain") {
|
|
2516
|
+
if (state.status !== "blocked_uncertain" && state.status !== "cancelling" && state.currentStep?.effectState !== "blocked_uncertain") {
|
|
2368
2517
|
throw runtimeError("run_events_illegal", runId, `run ${runId} is not in blocked_uncertain status`);
|
|
2369
2518
|
}
|
|
2519
|
+
if (state.status === "cancelling" && state.currentStep?.effectState === "blocked_uncertain") {
|
|
2520
|
+
state = { ...state, status: "blocked_uncertain" };
|
|
2521
|
+
}
|
|
2370
2522
|
|
|
2371
2523
|
const now = new Date().toISOString();
|
|
2372
2524
|
let sequence = context.eventStore.nextSequence(runId);
|