@kontextmind/kxm 0.7.23 → 0.7.25
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/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +59 -5
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime.js +507 -27
- package/plugins/kxm/dist/vnext-runtime-supervisor.js +500 -26
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/src/cli/vnext.ts +65 -3
- package/plugins/kxm/src/cli.ts +1 -1
- package/plugins/kxm/src/database.ts +3 -1
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/vnext-config.ts +31 -0
- package/plugins/kxm/src/vnext-engine-fold.ts +87 -2
- package/plugins/kxm/src/vnext-engine-gate-records.ts +19 -6
- package/plugins/kxm/src/vnext-engine.ts +325 -9
- package/plugins/kxm/src/vnext-runtime-owner.ts +2 -0
- package/plugins/kxm/src/vnext-runtime-store.ts +158 -2
- package/plugins/kxm/src/vnext-runtime-supervisor.ts +24 -3
- package/plugins/kxm/src/vnext-runtime.ts +17 -3
- package/schemas/vnext/common.schema.json +1 -0
- package/schemas/vnext/drive-receipt.schema.json +95 -0
- package/schemas/vnext/run-event.schema.json +39 -0
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
} from "./vnext-runtime.ts";
|
|
24
24
|
import { createVnextOneShotProducer } from "./vnext-oneshot-producer.ts";
|
|
25
25
|
import { isProducerAdmitted } from "./producers.ts";
|
|
26
|
-
import { VnextRunScheduler, createVnextSimulatedProducer, recoverVnextRun } from "./vnext-engine.ts";
|
|
26
|
+
import { VnextRunScheduler, createVnextSimulatedProducer, recordDriveReceipt, recoverVnextRun, vnextDrivePollProjection } from "./vnext-engine.ts";
|
|
27
27
|
import { vnextOpenDriveSessions } from "./vnext-runtime-owner.ts";
|
|
28
28
|
|
|
29
29
|
/* ------------------------------------------------------------------ *
|
|
@@ -318,6 +318,16 @@ async function waitForDriveSessions(settled: Promise<unknown>[], graceMs: number
|
|
|
318
318
|
}
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
+
async function driveSessionStillPending(settled: Promise<unknown>): Promise<boolean> {
|
|
322
|
+
let pending = true;
|
|
323
|
+
void settled.then(
|
|
324
|
+
() => { pending = false; },
|
|
325
|
+
() => { pending = false; },
|
|
326
|
+
);
|
|
327
|
+
await Promise.resolve();
|
|
328
|
+
return pending;
|
|
329
|
+
}
|
|
330
|
+
|
|
321
331
|
export interface VnextRuntimeSupervisor {
|
|
322
332
|
server: Server;
|
|
323
333
|
port: number;
|
|
@@ -446,7 +456,9 @@ async function startVnextRuntimeSupervisorInner(
|
|
|
446
456
|
const bundle = loadVnextProject(projectRoot, {});
|
|
447
457
|
if (request.method === "GET" && !sub) {
|
|
448
458
|
const projected = rebuildVnextRunProjection(context, runId);
|
|
449
|
-
|
|
459
|
+
const folded = foldStoredVnextRun(context, projected);
|
|
460
|
+
const drive = vnextDrivePollProjection(context, runId, folded);
|
|
461
|
+
sendJson(response, 200, { ok: true, run: projected, ...(drive !== undefined ? { drive } : {}) });
|
|
450
462
|
return;
|
|
451
463
|
}
|
|
452
464
|
if (request.method === "POST" && sub === "drive") {
|
|
@@ -493,7 +505,7 @@ async function startVnextRuntimeSupervisorInner(
|
|
|
493
505
|
const scheduler = VnextRunScheduler.for(context, bundle);
|
|
494
506
|
const opening = scheduler.openDriveSession(runId, {
|
|
495
507
|
mode: body.mode,
|
|
496
|
-
allowLimits:
|
|
508
|
+
allowLimits: false,
|
|
497
509
|
liveMode: body.mode === "live",
|
|
498
510
|
createProducer,
|
|
499
511
|
});
|
|
@@ -684,6 +696,15 @@ async function startVnextRuntimeSupervisorInner(
|
|
|
684
696
|
}
|
|
685
697
|
}
|
|
686
698
|
await waitForDriveSessions(openSessions.map(({ session }) => session.settled), runtimeStopGraceMs());
|
|
699
|
+
for (const { context, session } of openSessions) {
|
|
700
|
+
if (!(await driveSessionStillPending(session.settled))) continue;
|
|
701
|
+
recordDriveReceipt(context, session, {
|
|
702
|
+
kind: "unsettled",
|
|
703
|
+
reason: "runtime_shutdown_grace_expired",
|
|
704
|
+
producerId: session.producerId,
|
|
705
|
+
producerClosed: false,
|
|
706
|
+
});
|
|
707
|
+
}
|
|
687
708
|
for (const context of contexts.values()) closeVnextRuntimeContext(context);
|
|
688
709
|
contexts.clear();
|
|
689
710
|
const closed = new Promise<void>((resolveStop) => server.close(() => resolveStop()));
|
|
@@ -464,6 +464,7 @@ export function foldStoredVnextRun(context: VnextRuntimeContext, run: VnextRunRe
|
|
|
464
464
|
if (!step || step.kind !== "gate") return undefined;
|
|
465
465
|
return envelope.gates.definitions[step.gate]?.kind;
|
|
466
466
|
},
|
|
467
|
+
projectLimits: envelope.projectLimits,
|
|
467
468
|
}
|
|
468
469
|
: {});
|
|
469
470
|
if (envelope) verifyVnextGateEvidence(context.eventStore, run, envelope, events, state);
|
|
@@ -551,7 +552,13 @@ export interface VnextRunCancelResult {
|
|
|
551
552
|
export function cancelVnextRun(
|
|
552
553
|
context: VnextRuntimeContext,
|
|
553
554
|
runId: string,
|
|
554
|
-
options: {
|
|
555
|
+
options: {
|
|
556
|
+
commandId?: string;
|
|
557
|
+
now?: string;
|
|
558
|
+
monotonicNs?: string;
|
|
559
|
+
reason?: string;
|
|
560
|
+
budget?: { budgetMs: number; elapsedMs: number; source: "workflow" | "project" | "both" };
|
|
561
|
+
} = {},
|
|
555
562
|
): VnextRunCancelResult {
|
|
556
563
|
const commandId = options.commandId ?? newVnextCommandId();
|
|
557
564
|
const now = options.now ?? new Date().toISOString();
|
|
@@ -600,10 +607,17 @@ export function cancelVnextRun(
|
|
|
600
607
|
|
|
601
608
|
const cancelReason = options.reason ?? "operator_cancel";
|
|
602
609
|
const activeAttempt = Boolean(folded.currentStep?.attemptId) || vnextFoldPanelAttemptIds(folded.currentStep).length > 0;
|
|
603
|
-
|
|
610
|
+
const cancelPayload: Record<string, unknown> = {
|
|
604
611
|
actor: { kind: "runtime", id: context.homeRuntimeId },
|
|
605
612
|
reason: cancelReason,
|
|
606
|
-
}
|
|
613
|
+
};
|
|
614
|
+
if (cancelReason === "budget_run_duration") {
|
|
615
|
+
if (!options.budget) {
|
|
616
|
+
throw runtimeError("run_events_illegal", runId, "budget_run_duration requires a budget payload");
|
|
617
|
+
}
|
|
618
|
+
cancelPayload.budget = options.budget;
|
|
619
|
+
}
|
|
620
|
+
push("run.cancel_requested", cancelPayload);
|
|
607
621
|
|
|
608
622
|
let status: VnextRunStatus = "cancelled";
|
|
609
623
|
if (folded.status === "running" && activeAttempt) {
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://schemas.kxm.dev/vnext/drive-receipt.schema.json",
|
|
4
|
+
"title": "KXM vNext drive session receipt",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": [
|
|
7
|
+
"schema",
|
|
8
|
+
"driveId",
|
|
9
|
+
"runId",
|
|
10
|
+
"projectId",
|
|
11
|
+
"homeRuntimeId",
|
|
12
|
+
"mode",
|
|
13
|
+
"openedAt",
|
|
14
|
+
"openedSequence",
|
|
15
|
+
"closedAt",
|
|
16
|
+
"lastSequence",
|
|
17
|
+
"logHash",
|
|
18
|
+
"settlement",
|
|
19
|
+
"budget",
|
|
20
|
+
"producer"
|
|
21
|
+
],
|
|
22
|
+
"properties": {
|
|
23
|
+
"schema": { "const": "kxm.drive-receipt.v1" },
|
|
24
|
+
"driveId": { "$ref": "common.schema.json#/$defs/opaqueId" },
|
|
25
|
+
"runId": { "$ref": "common.schema.json#/$defs/opaqueId" },
|
|
26
|
+
"projectId": { "$ref": "common.schema.json#/$defs/opaqueId" },
|
|
27
|
+
"homeRuntimeId": { "$ref": "common.schema.json#/$defs/opaqueId" },
|
|
28
|
+
"mode": { "enum": ["simulated", "live"] },
|
|
29
|
+
"openedAt": { "$ref": "common.schema.json#/$defs/timestamp" },
|
|
30
|
+
"openedSequence": { "type": "integer", "minimum": 1 },
|
|
31
|
+
"closedAt": { "$ref": "common.schema.json#/$defs/timestamp" },
|
|
32
|
+
"lastSequence": { "type": "integer", "minimum": 1 },
|
|
33
|
+
"logHash": { "$ref": "common.schema.json#/$defs/sha256" },
|
|
34
|
+
"settlement": { "$ref": "#/$defs/settlement" },
|
|
35
|
+
"budget": { "$ref": "#/$defs/budget" },
|
|
36
|
+
"producer": {
|
|
37
|
+
"type": "object",
|
|
38
|
+
"required": ["id", "closed"],
|
|
39
|
+
"properties": {
|
|
40
|
+
"id": { "$ref": "common.schema.json#/$defs/identifier" },
|
|
41
|
+
"closed": { "type": "boolean" }
|
|
42
|
+
},
|
|
43
|
+
"additionalProperties": false
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"additionalProperties": false,
|
|
47
|
+
"$defs": {
|
|
48
|
+
"settlement": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"required": ["kind", "status", "reason"],
|
|
51
|
+
"properties": {
|
|
52
|
+
"kind": { "enum": ["terminal", "handoff", "unsettled"] },
|
|
53
|
+
"status": { "$ref": "common.schema.json#/$defs/lifecycleStatus" },
|
|
54
|
+
"reason": { "type": "string", "maxLength": 4000 },
|
|
55
|
+
"handoff": { "$ref": "#/$defs/handoff" },
|
|
56
|
+
"error": { "$ref": "common.schema.json#/$defs/errorSummary" }
|
|
57
|
+
},
|
|
58
|
+
"additionalProperties": false,
|
|
59
|
+
"allOf": [
|
|
60
|
+
{
|
|
61
|
+
"if": { "properties": { "kind": { "const": "handoff" } }, "required": ["kind"] },
|
|
62
|
+
"then": { "required": ["handoff"] },
|
|
63
|
+
"else": { "not": { "required": ["handoff"] } }
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
},
|
|
67
|
+
"budget": {
|
|
68
|
+
"oneOf": [
|
|
69
|
+
{ "type": "null" },
|
|
70
|
+
{
|
|
71
|
+
"type": "object",
|
|
72
|
+
"required": ["budgetMs", "source", "elapsedMs", "overrun"],
|
|
73
|
+
"properties": {
|
|
74
|
+
"budgetMs": { "$ref": "common.schema.json#/$defs/durationMs" },
|
|
75
|
+
"source": { "enum": ["workflow", "project", "both"] },
|
|
76
|
+
"elapsedMs": { "type": "integer", "minimum": 0 },
|
|
77
|
+
"overrun": { "type": "boolean" }
|
|
78
|
+
},
|
|
79
|
+
"additionalProperties": false
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
"handoff": {
|
|
84
|
+
"type": "object",
|
|
85
|
+
"required": ["reason", "detail"],
|
|
86
|
+
"properties": {
|
|
87
|
+
"reason": { "type": "string", "minLength": 1, "maxLength": 80 },
|
|
88
|
+
"field": { "type": "string", "minLength": 1, "maxLength": 128 },
|
|
89
|
+
"stepId": { "$ref": "common.schema.json#/$defs/identifier" },
|
|
90
|
+
"detail": { "type": "string", "minLength": 1, "maxLength": 4000 }
|
|
91
|
+
},
|
|
92
|
+
"additionalProperties": false
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -90,6 +90,24 @@
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
},
|
|
93
|
+
{
|
|
94
|
+
"if": {
|
|
95
|
+
"properties": {
|
|
96
|
+
"eventType": { "const": "run.cancel_requested" },
|
|
97
|
+
"payload": {
|
|
98
|
+
"type": "object",
|
|
99
|
+
"properties": { "reason": { "const": "budget_run_duration" } },
|
|
100
|
+
"required": ["reason"]
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
"required": ["eventType", "payload"]
|
|
104
|
+
},
|
|
105
|
+
"then": {
|
|
106
|
+
"properties": {
|
|
107
|
+
"payload": { "type": "object", "required": ["actor", "reason", "budget"] }
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
},
|
|
93
111
|
{
|
|
94
112
|
"if": { "properties": { "eventType": { "const": "step.entered" } }, "required": ["eventType"] },
|
|
95
113
|
"then": {
|
|
@@ -278,10 +296,28 @@
|
|
|
278
296
|
"payload": { "type": "object", "required": ["routing"] }
|
|
279
297
|
}
|
|
280
298
|
}
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
"if": { "properties": { "eventType": { "const": "run.drive_opened" } }, "required": ["eventType"] },
|
|
302
|
+
"then": {
|
|
303
|
+
"properties": {
|
|
304
|
+
"payload": { "type": "object", "required": ["driveId", "mode"] }
|
|
305
|
+
}
|
|
306
|
+
}
|
|
281
307
|
}
|
|
282
308
|
],
|
|
283
309
|
"additionalProperties": false,
|
|
284
310
|
"$defs": {
|
|
311
|
+
"runDurationBudget": {
|
|
312
|
+
"type": "object",
|
|
313
|
+
"required": ["budgetMs", "elapsedMs", "source"],
|
|
314
|
+
"properties": {
|
|
315
|
+
"budgetMs": { "$ref": "common.schema.json#/$defs/durationMs" },
|
|
316
|
+
"elapsedMs": { "type": "integer", "minimum": 0 },
|
|
317
|
+
"source": { "enum": ["workflow", "project", "both"] }
|
|
318
|
+
},
|
|
319
|
+
"additionalProperties": false
|
|
320
|
+
},
|
|
285
321
|
"repositorySnapshot": {
|
|
286
322
|
"type": "object",
|
|
287
323
|
"required": ["repositoryId", "baseCommit", "contentSnapshotHash"],
|
|
@@ -383,6 +419,7 @@
|
|
|
383
419
|
"timing": { "$ref": "common.schema.json#/$defs/timing" },
|
|
384
420
|
"usage": { "$ref": "common.schema.json#/$defs/usage" },
|
|
385
421
|
"error": { "$ref": "common.schema.json#/$defs/errorSummary" },
|
|
422
|
+
"budget": { "$ref": "#/$defs/runDurationBudget" },
|
|
386
423
|
"actor": { "$ref": "common.schema.json#/$defs/actor" },
|
|
387
424
|
"resolvedBy": { "$ref": "common.schema.json#/$defs/actor" },
|
|
388
425
|
"resolutionAction": {
|
|
@@ -394,6 +431,8 @@
|
|
|
394
431
|
"maxItems": 64
|
|
395
432
|
},
|
|
396
433
|
"lease": { "$ref": "common.schema.json#/$defs/lease" },
|
|
434
|
+
"driveId": { "$ref": "common.schema.json#/$defs/opaqueId" },
|
|
435
|
+
"mode": { "enum": ["simulated", "live"] },
|
|
397
436
|
"candidateId": { "$ref": "common.schema.json#/$defs/opaqueId" },
|
|
398
437
|
"counts": {
|
|
399
438
|
"type": "object",
|