@kontextmind/kxm 0.7.14 → 0.7.16
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/docs/configuration.md +1 -2
- package/docs/operator-pi-packages.md +5 -2
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/cli.js +20 -3
- package/plugins/kxm/dist/core.js +4 -0
- package/plugins/kxm/dist/extension.js +3066 -2
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime.js +263 -70
- package/plugins/kxm/dist/server.js +4 -0
- package/plugins/kxm/dist/vnext-runtime-supervisor.js +259 -70
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/src/cli/vnext.ts +20 -3
- package/plugins/kxm/src/extension.ts +9 -0
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/providers/antigravity/LICENSE +21 -0
- package/plugins/kxm/src/providers/antigravity/README.md +19 -0
- package/plugins/kxm/src/providers/antigravity/auth/index.ts +17 -0
- package/plugins/kxm/src/providers/antigravity/auth/oauth.ts +449 -0
- package/plugins/kxm/src/providers/antigravity/client/client.ts +562 -0
- package/plugins/kxm/src/providers/antigravity/client/index.ts +1 -0
- package/plugins/kxm/src/providers/antigravity/diagnostics/diagnostics.ts +97 -0
- package/plugins/kxm/src/providers/antigravity/diagnostics/index.ts +1 -0
- package/plugins/kxm/src/providers/antigravity/index.ts +24 -0
- package/plugins/kxm/src/providers/antigravity/models/discovery.ts +155 -0
- package/plugins/kxm/src/providers/antigravity/models/grouping.ts +425 -0
- package/plugins/kxm/src/providers/antigravity/models/index.ts +3 -0
- package/plugins/kxm/src/providers/antigravity/models/models.ts +501 -0
- package/plugins/kxm/src/providers/antigravity/pi-compat.ts +279 -0
- package/plugins/kxm/src/providers/antigravity/register.ts +123 -0
- package/plugins/kxm/src/providers/antigravity/stream/index.ts +1 -0
- package/plugins/kxm/src/providers/antigravity/stream/stream.ts +1461 -0
- package/plugins/kxm/src/providers/antigravity/types/enums.ts +50 -0
- package/plugins/kxm/src/providers/antigravity/types/index.ts +2 -0
- package/plugins/kxm/src/providers/antigravity/types/types.ts +293 -0
- package/plugins/kxm/src/providers/antigravity/usage/index.ts +1 -0
- package/plugins/kxm/src/providers/antigravity/usage/usage.ts +361 -0
- package/plugins/kxm/src/providers/antigravity/utils/http.ts +32 -0
- package/plugins/kxm/src/providers/antigravity/utils/index.ts +3 -0
- package/plugins/kxm/src/providers/antigravity/utils/security.ts +75 -0
- package/plugins/kxm/src/providers/antigravity/utils/util.ts +133 -0
- package/plugins/kxm/src/redact.ts +4 -0
- package/plugins/kxm/src/vnext-engine.ts +183 -3
- package/plugins/kxm/src/vnext-runtime-owner.ts +50 -0
- package/plugins/kxm/src/vnext-runtime-supervisor.ts +83 -58
- package/plugins/kxm/src/vnext-runtime.ts +9 -8
|
@@ -24,7 +24,8 @@ import {
|
|
|
24
24
|
} from "./vnext-runtime.ts";
|
|
25
25
|
import { createVnextOneShotProducer } from "./vnext-oneshot-producer.ts";
|
|
26
26
|
import { isProducerAdmitted } from "./producers.ts";
|
|
27
|
-
import { VnextRunScheduler, createVnextSimulatedProducer,
|
|
27
|
+
import { VnextRunScheduler, createVnextSimulatedProducer, recoverVnextRun } from "./vnext-engine.ts";
|
|
28
|
+
import { vnextOpenDriveSessions } from "./vnext-runtime-owner.ts";
|
|
28
29
|
|
|
29
30
|
/* ------------------------------------------------------------------ *
|
|
30
31
|
* Token management
|
|
@@ -280,6 +281,44 @@ function sendJson(response: ServerResponse, status: number, payload: unknown): v
|
|
|
280
281
|
response.end(body);
|
|
281
282
|
}
|
|
282
283
|
|
|
284
|
+
export const DEFAULT_RUNTIME_STOP_GRACE_MS = 30_000;
|
|
285
|
+
/** Documented upper bound: 10 minutes, well under Node's 2^31-1 ms timer range. */
|
|
286
|
+
export const MAX_RUNTIME_STOP_GRACE_MS = 600_000;
|
|
287
|
+
|
|
288
|
+
export function runtimeStopGraceMs(env: NodeJS.ProcessEnv = process.env): number {
|
|
289
|
+
const raw = env.KXM_RUNTIME_STOP_GRACE_MS;
|
|
290
|
+
if (raw === undefined || raw.trim() === "") return DEFAULT_RUNTIME_STOP_GRACE_MS;
|
|
291
|
+
const parsed = Number(raw.trim());
|
|
292
|
+
if (
|
|
293
|
+
!Number.isFinite(parsed)
|
|
294
|
+
|| !Number.isInteger(parsed)
|
|
295
|
+
|| parsed < 0
|
|
296
|
+
|| parsed > MAX_RUNTIME_STOP_GRACE_MS
|
|
297
|
+
) {
|
|
298
|
+
process.stderr.write(
|
|
299
|
+
`KXM_RUNTIME_STOP_GRACE_MS must be an integer between 0 and ${MAX_RUNTIME_STOP_GRACE_MS} ms (10 minutes); using default ${DEFAULT_RUNTIME_STOP_GRACE_MS}\n`,
|
|
300
|
+
);
|
|
301
|
+
return DEFAULT_RUNTIME_STOP_GRACE_MS;
|
|
302
|
+
}
|
|
303
|
+
return parsed;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
async function waitForDriveSessions(settled: Promise<unknown>[], graceMs: number): Promise<void> {
|
|
307
|
+
if (settled.length === 0) return;
|
|
308
|
+
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
309
|
+
try {
|
|
310
|
+
await Promise.race([
|
|
311
|
+
Promise.allSettled(settled),
|
|
312
|
+
new Promise<void>((resolve) => {
|
|
313
|
+
timeout = setTimeout(resolve, graceMs);
|
|
314
|
+
timeout.unref?.();
|
|
315
|
+
}),
|
|
316
|
+
]);
|
|
317
|
+
} finally {
|
|
318
|
+
if (timeout !== undefined) clearTimeout(timeout);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
283
322
|
export interface VnextRuntimeSupervisor {
|
|
284
323
|
server: Server;
|
|
285
324
|
port: number;
|
|
@@ -322,7 +361,6 @@ async function startVnextRuntimeSupervisorInner(
|
|
|
322
361
|
const runtimeId = `rtm_${createHash("sha256").update(`${paths.stateRoot}\0${process.pid}\0${now()}\0${randomBytes(16).toString("hex")}`, "utf8").digest("hex").slice(0, 24)}`;
|
|
323
362
|
const requestedPort = requestedPortOption ?? 0;
|
|
324
363
|
let activeRuntimeId = runtimeId;
|
|
325
|
-
const activeDrives = new Map<string, Promise<unknown>>();
|
|
326
364
|
|
|
327
365
|
const contexts = new Map<string, VnextRuntimeContext>();
|
|
328
366
|
const contextFor = (projectRoot: string): VnextRuntimeContext => {
|
|
@@ -420,32 +458,9 @@ async function startVnextRuntimeSupervisorInner(
|
|
|
420
458
|
}
|
|
421
459
|
const run = context.eventStore.run(runId);
|
|
422
460
|
if (!run) throw runtimeError("run_unknown", runId, "run not found");
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
}
|
|
427
|
-
if (run.status === "completed" || run.status === "failed" || run.status === "cancelled" || run.status === "cancelling") {
|
|
428
|
-
sendJson(response, 409, { ok: false, error: "run_busy", message: `run ${runId} is already ${run.status}` });
|
|
429
|
-
return;
|
|
430
|
-
}
|
|
431
|
-
let releaseActive = () => {};
|
|
432
|
-
const activePromise = new Promise<void>((resolve) => { releaseActive = resolve; });
|
|
433
|
-
activeDrives.set(runId, activePromise);
|
|
434
|
-
const cleanupActive = () => {
|
|
435
|
-
activeDrives.delete(runId);
|
|
436
|
-
releaseActive();
|
|
437
|
-
};
|
|
438
|
-
|
|
439
|
-
if (run.status === "created") {
|
|
440
|
-
pinVnextCompiledPlan(context, bundle, runId);
|
|
441
|
-
const plan = startVnextRun(context, runId, { allowLimits: true });
|
|
442
|
-
if (plan.handoff) {
|
|
443
|
-
cleanupActive();
|
|
444
|
-
sendJson(response, 409, { ok: false, error: "run_handoff_required", handoff: plan.handoff });
|
|
445
|
-
return;
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
const producer = body.mode === "live"
|
|
461
|
+
|
|
462
|
+
const delayMs = typeof body.delayMs === "number" && body.delayMs > 0 ? body.delayMs : 0;
|
|
463
|
+
const createProducer = () => body.mode === "live"
|
|
449
464
|
? createVnextOneShotProducer({
|
|
450
465
|
projectRoot,
|
|
451
466
|
defaultHarness: String(bundle.project.value.defaultHarness ?? "pi"),
|
|
@@ -466,56 +481,53 @@ async function startVnextRuntimeSupervisorInner(
|
|
|
466
481
|
return { provider, model: modelName };
|
|
467
482
|
},
|
|
468
483
|
})
|
|
469
|
-
: (() => {
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
})();
|
|
478
|
-
|
|
479
|
-
let drivePromise: Promise<unknown> | undefined;
|
|
484
|
+
: createVnextSimulatedProducer(async () => {
|
|
485
|
+
if (delayMs > 0) {
|
|
486
|
+
await new Promise((r) => setTimeout(r, delayMs));
|
|
487
|
+
}
|
|
488
|
+
return { outcome: "passed" };
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
let session: Awaited<ReturnType<VnextRunScheduler["openDriveSession"]>> | undefined;
|
|
480
492
|
let earlyError: unknown;
|
|
481
493
|
try {
|
|
482
494
|
const scheduler = VnextRunScheduler.for(context, bundle);
|
|
483
|
-
|
|
484
|
-
|
|
495
|
+
const opening = scheduler.openDriveSession(runId, {
|
|
496
|
+
mode: body.mode,
|
|
497
|
+
allowLimits: true,
|
|
498
|
+
liveMode: body.mode === "live",
|
|
499
|
+
createProducer,
|
|
500
|
+
});
|
|
501
|
+
opening.catch((err) => { earlyError = err; });
|
|
502
|
+
session = await opening;
|
|
485
503
|
} catch (err) {
|
|
486
504
|
earlyError = err;
|
|
487
505
|
}
|
|
488
506
|
|
|
489
|
-
// Yield microtask to catch synchronous duplicate queue check (e.g. run_busy)
|
|
490
|
-
await Promise.resolve();
|
|
491
|
-
|
|
492
507
|
if (earlyError) {
|
|
493
|
-
cleanupActive();
|
|
494
508
|
const errStr = String(earlyError);
|
|
509
|
+
if (errStr.includes("run_handoff_required")) {
|
|
510
|
+
const handoff = (earlyError as { handoff?: unknown }).handoff;
|
|
511
|
+
sendJson(response, 409, { ok: false, error: "run_handoff_required", ...(handoff !== undefined ? { handoff } : {}) });
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
495
514
|
if (errStr.includes("run_busy") || errStr.includes("scheduler_policy_conflict")) {
|
|
496
|
-
if ("close" in producer && typeof producer.close === "function") {
|
|
497
|
-
try { await producer.close(); } catch { /* ignore */ }
|
|
498
|
-
}
|
|
499
515
|
sendJson(response, 409, { ok: false, error: "run_busy", message: `run ${runId} is already admitted or queued` });
|
|
500
516
|
return;
|
|
501
517
|
}
|
|
502
518
|
throw earlyError;
|
|
503
519
|
}
|
|
504
520
|
|
|
505
|
-
void
|
|
506
|
-
|
|
507
|
-
if ("close" in producer && typeof producer.close === "function") {
|
|
508
|
-
try { await producer.close(); } catch { /* ignore */ }
|
|
509
|
-
}
|
|
510
|
-
}).catch(() => {
|
|
511
|
-
// Derived finally() re-rejects when the admitted drive or cleanup
|
|
512
|
-
// throws; void does not consume that. Keep the request 202.
|
|
521
|
+
void session!.settled.catch(() => {
|
|
522
|
+
// Post-202 drive failures stay on the session; keep the request 202.
|
|
513
523
|
});
|
|
514
524
|
|
|
525
|
+
if (response.writableEnded) return;
|
|
515
526
|
sendJson(response, 202, {
|
|
516
527
|
ok: true,
|
|
517
528
|
status: "accepted",
|
|
518
529
|
runId,
|
|
530
|
+
driveId: session!.driveId,
|
|
519
531
|
poll: `/v1/runs/${runId}`,
|
|
520
532
|
mode: body.mode,
|
|
521
533
|
});
|
|
@@ -657,9 +669,22 @@ async function startVnextRuntimeSupervisorInner(
|
|
|
657
669
|
stopping = true;
|
|
658
670
|
clearInterval(heartbeat);
|
|
659
671
|
try { registry.markStopping(process.pid, now()); } catch { /* best effort */ }
|
|
660
|
-
|
|
661
|
-
|
|
672
|
+
const openSessions = [...contexts.values()].flatMap((context) => (
|
|
673
|
+
vnextOpenDriveSessions(context.eventStore.path).map((session) => ({ context, session }))
|
|
674
|
+
));
|
|
675
|
+
for (const { context, session } of openSessions) {
|
|
676
|
+
try {
|
|
677
|
+
cancelVnextRun(context, session.runId, { reason: "runtime_shutdown" });
|
|
678
|
+
} catch {
|
|
679
|
+
// Terminal or already cancelling runs still release on session settle.
|
|
680
|
+
}
|
|
681
|
+
try {
|
|
682
|
+
session.controller.abort();
|
|
683
|
+
} catch {
|
|
684
|
+
// Session abort is best-effort after cancel is recorded.
|
|
685
|
+
}
|
|
662
686
|
}
|
|
687
|
+
await waitForDriveSessions(openSessions.map(({ session }) => session.settled), runtimeStopGraceMs());
|
|
663
688
|
for (const context of contexts.values()) closeVnextRuntimeContext(context);
|
|
664
689
|
contexts.clear();
|
|
665
690
|
const closed = new Promise<void>((resolveStop) => server.close(() => resolveStop()));
|
|
@@ -551,7 +551,7 @@ export interface VnextRunCancelResult {
|
|
|
551
551
|
export function cancelVnextRun(
|
|
552
552
|
context: VnextRuntimeContext,
|
|
553
553
|
runId: string,
|
|
554
|
-
options: { commandId?: string; now?: string; monotonicNs?: string } = {},
|
|
554
|
+
options: { commandId?: string; now?: string; monotonicNs?: string; reason?: string } = {},
|
|
555
555
|
): VnextRunCancelResult {
|
|
556
556
|
const commandId = options.commandId ?? newVnextCommandId();
|
|
557
557
|
const now = options.now ?? new Date().toISOString();
|
|
@@ -598,16 +598,17 @@ export function cancelVnextRun(
|
|
|
598
598
|
return event;
|
|
599
599
|
};
|
|
600
600
|
|
|
601
|
+
const cancelReason = options.reason ?? "operator_cancel";
|
|
601
602
|
const activeAttempt = Boolean(folded.currentStep?.attemptId) || vnextFoldPanelAttemptIds(folded.currentStep).length > 0;
|
|
602
603
|
push("run.cancel_requested", {
|
|
603
604
|
actor: { kind: "runtime", id: context.homeRuntimeId },
|
|
604
|
-
reason:
|
|
605
|
+
reason: cancelReason,
|
|
605
606
|
});
|
|
606
607
|
|
|
607
608
|
let status: VnextRunStatus = "cancelled";
|
|
608
609
|
if (folded.status === "running" && activeAttempt) {
|
|
609
610
|
status = "cancelling";
|
|
610
|
-
push("run.status_changed", { status: "cancelling", reason:
|
|
611
|
+
push("run.status_changed", { status: "cancelling", reason: cancelReason });
|
|
611
612
|
const attemptIds = new Set(vnextFoldPanelAttemptIds(folded.currentStep));
|
|
612
613
|
if (folded.currentStep?.attemptId) attemptIds.add(folded.currentStep.attemptId);
|
|
613
614
|
for (const attemptId of attemptIds) {
|
|
@@ -620,7 +621,7 @@ export function cancelVnextRun(
|
|
|
620
621
|
abortControllers.push(owned.controller);
|
|
621
622
|
}
|
|
622
623
|
} else if (folded.status === "blocked_uncertain") {
|
|
623
|
-
push("run.status_changed", { status: "cancelling", reason:
|
|
624
|
+
push("run.status_changed", { status: "cancelling", reason: cancelReason });
|
|
624
625
|
const attemptId = folded.currentStep?.attemptId;
|
|
625
626
|
if (attemptId) {
|
|
626
627
|
const capability = context.eventStore.capabilityByAttempt(attemptId);
|
|
@@ -632,13 +633,13 @@ export function cancelVnextRun(
|
|
|
632
633
|
for (const owned of vnextAttemptControllers(context.eventStore.path, runId)) {
|
|
633
634
|
abortControllers.push(owned.controller);
|
|
634
635
|
}
|
|
635
|
-
push("run.status_changed", { status: "cancelled", reason:
|
|
636
|
+
push("run.status_changed", { status: "cancelled", reason: cancelReason });
|
|
636
637
|
status = "cancelled";
|
|
637
638
|
} else if (folded.status === "running" && !activeAttempt) {
|
|
638
|
-
push("run.status_changed", { status: "cancelling", reason:
|
|
639
|
-
push("run.status_changed", { status: "cancelled", reason:
|
|
639
|
+
push("run.status_changed", { status: "cancelling", reason: cancelReason });
|
|
640
|
+
push("run.status_changed", { status: "cancelled", reason: cancelReason });
|
|
640
641
|
} else {
|
|
641
|
-
push("run.status_changed", { status: "cancelled", reason:
|
|
642
|
+
push("run.status_changed", { status: "cancelled", reason: cancelReason });
|
|
642
643
|
}
|
|
643
644
|
|
|
644
645
|
for (const event of events) context.eventStore.appendEvent(event);
|