@kontextmind/kxm 0.7.7 → 0.7.9
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 +250 -38
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime.js +318 -33
- package/plugins/kxm/dist/vnext-runtime-supervisor.js +85 -28
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/src/cli/system.ts +3 -3
- package/plugins/kxm/src/cli/tasks.ts +2 -2
- package/plugins/kxm/src/cli/vnext.ts +2 -2
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/model-inventory.ts +8 -8
- package/plugins/kxm/src/runtime.ts +1 -0
- package/plugins/kxm/src/subagent-control.ts +312 -0
- package/plugins/kxm/src/vnext-harness.ts +88 -20
- package/plugins/kxm/src/vnext-oneshot-evidence.ts +39 -7
- package/plugins/kxm/src/vnext-oneshot-process.ts +25 -4
- package/plugins/kxm/src/vnext-oneshot-producer.ts +4 -1
- package/plugins/kxm/src/vnext-pi-producer.ts +11 -7
- package/plugins/kxm/src/vnext-runtime-supervisor.ts +5 -3
|
@@ -15159,6 +15159,7 @@ function defaultSpawn(command, args, options) {
|
|
|
15159
15159
|
let child;
|
|
15160
15160
|
const finish = () => {
|
|
15161
15161
|
if (finished) return;
|
|
15162
|
+
if (stopping && !killSent) return;
|
|
15162
15163
|
finished = true;
|
|
15163
15164
|
for (const timer of [wallTimer, killTimer, drainTimer, reapTimer]) clearTimeout(timer);
|
|
15164
15165
|
options.signal?.removeEventListener("abort", onAbort);
|
|
@@ -15168,7 +15169,18 @@ function defaultSpawn(command, args, options) {
|
|
|
15168
15169
|
child?.unref();
|
|
15169
15170
|
const started = Boolean(child?.pid);
|
|
15170
15171
|
if (started && !observedChildExit) error ??= new Error("process_exit_unobserved");
|
|
15171
|
-
|
|
15172
|
+
const unverifiedDescendants = stopping || started && !observedChildExit;
|
|
15173
|
+
resolve9({
|
|
15174
|
+
stdout: Buffer.concat(stdout).toString("utf8"),
|
|
15175
|
+
stderr: Buffer.concat(stderr).toString("utf8"),
|
|
15176
|
+
code,
|
|
15177
|
+
signal,
|
|
15178
|
+
started,
|
|
15179
|
+
observedChildExit,
|
|
15180
|
+
terminationRequested: stopping,
|
|
15181
|
+
...unverifiedDescendants ? { unverifiedDescendants: true } : {},
|
|
15182
|
+
...error ? { error } : {}
|
|
15183
|
+
});
|
|
15172
15184
|
};
|
|
15173
15185
|
const kill = (requested) => {
|
|
15174
15186
|
killProcessTree(child, requested);
|
|
@@ -15199,7 +15211,7 @@ function defaultSpawn(command, args, options) {
|
|
|
15199
15211
|
cwd: options.cwd,
|
|
15200
15212
|
env: options.env ?? process.env,
|
|
15201
15213
|
stdio: ["pipe", "pipe", "pipe"],
|
|
15202
|
-
shell:
|
|
15214
|
+
shell: options.shell === true,
|
|
15203
15215
|
windowsHide: true,
|
|
15204
15216
|
detached: process.platform !== "win32"
|
|
15205
15217
|
});
|
|
@@ -15243,7 +15255,7 @@ function defaultSpawn(command, args, options) {
|
|
|
15243
15255
|
closed = true;
|
|
15244
15256
|
code = exitCode;
|
|
15245
15257
|
signal = exitSignal;
|
|
15246
|
-
|
|
15258
|
+
finish();
|
|
15247
15259
|
});
|
|
15248
15260
|
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
15249
15261
|
if (options.signal?.aborted) onAbort();
|
|
@@ -15684,6 +15696,31 @@ function defaultRunner(env) {
|
|
|
15684
15696
|
}
|
|
15685
15697
|
};
|
|
15686
15698
|
}
|
|
15699
|
+
function commandResultFromSpawn(result) {
|
|
15700
|
+
const errno = result.error && "code" in result.error ? result.error.code : void 0;
|
|
15701
|
+
const error = (typeof errno === "string" && errno ? errno : void 0) ?? result.error?.message ?? (result.observedChildExit !== true ? "auth_probe_exit_unobserved" : void 0);
|
|
15702
|
+
return {
|
|
15703
|
+
ok: result.code === 0 && !error,
|
|
15704
|
+
code: result.code,
|
|
15705
|
+
stdout: result.stdout,
|
|
15706
|
+
stderr: result.stderr,
|
|
15707
|
+
...error ? { error } : {}
|
|
15708
|
+
};
|
|
15709
|
+
}
|
|
15710
|
+
function defaultAsyncRunner(env, signal) {
|
|
15711
|
+
return async (command, args, timeoutMs) => {
|
|
15712
|
+
try {
|
|
15713
|
+
return commandResultFromSpawn(await defaultSpawn(command, args, {
|
|
15714
|
+
env,
|
|
15715
|
+
timeoutMs,
|
|
15716
|
+
signal,
|
|
15717
|
+
shell: harnessSpawnUsesShell(command)
|
|
15718
|
+
}));
|
|
15719
|
+
} catch (error) {
|
|
15720
|
+
return { ok: false, code: null, stdout: "", stderr: "", error: error instanceof Error ? error.message : "spawn_failed" };
|
|
15721
|
+
}
|
|
15722
|
+
};
|
|
15723
|
+
}
|
|
15687
15724
|
function firstLine(text) {
|
|
15688
15725
|
const line = text.split(/\r?\n/).map((candidate) => candidate.trim()).find(Boolean);
|
|
15689
15726
|
return line && line.length <= 200 ? line : void 0;
|
|
@@ -15882,6 +15919,13 @@ function probeHarnesses(options = {}) {
|
|
|
15882
15919
|
harnesses: BUILTIN_HARNESSES.map((entry) => probeEntry(entry, runCommand, timeoutMs, platform, options))
|
|
15883
15920
|
};
|
|
15884
15921
|
}
|
|
15922
|
+
async function probeHarnessesAsync(options = {}) {
|
|
15923
|
+
return replayHarnessProbe(
|
|
15924
|
+
options.runCommand ?? defaultAsyncRunner(options.env ?? process.env, options.signal),
|
|
15925
|
+
(runCommand) => probeHarnesses({ ...options, runCommand }),
|
|
15926
|
+
256
|
|
15927
|
+
);
|
|
15928
|
+
}
|
|
15885
15929
|
function eligibleHarnesses(inventory) {
|
|
15886
15930
|
const eligible = inventory.harnesses.filter((entry) => entry !== void 0 && entry.detected && entry.authenticated === true).map((entry) => entry.id);
|
|
15887
15931
|
if (eligible.length === 0) throw new Error("no_authenticated_harness");
|
|
@@ -16118,35 +16162,31 @@ var PendingHarnessCommand = class {
|
|
|
16118
16162
|
this.key = key;
|
|
16119
16163
|
}
|
|
16120
16164
|
};
|
|
16121
|
-
async function
|
|
16165
|
+
async function replayHarnessProbe(run, execute, limit) {
|
|
16122
16166
|
const observed = /* @__PURE__ */ new Map();
|
|
16123
|
-
|
|
16124
|
-
const result = await defaultSpawn(command, args, { env: options.env, timeoutMs, signal: options.signal });
|
|
16125
|
-
const error = result.error?.message ?? (result.observedChildExit !== true ? "auth_probe_exit_unobserved" : void 0);
|
|
16126
|
-
return {
|
|
16127
|
-
ok: result.code === 0 && !error,
|
|
16128
|
-
code: result.code,
|
|
16129
|
-
stdout: result.stdout,
|
|
16130
|
-
stderr: result.stderr,
|
|
16131
|
-
...error ? { error } : {}
|
|
16132
|
-
};
|
|
16133
|
-
});
|
|
16134
|
-
for (let commands = 0; commands <= 32; commands++) {
|
|
16167
|
+
for (let commands = 0; commands <= limit; commands++) {
|
|
16135
16168
|
try {
|
|
16136
|
-
return
|
|
16169
|
+
return execute((command, args, timeoutMs) => {
|
|
16137
16170
|
const key = JSON.stringify([command, args, timeoutMs]);
|
|
16138
16171
|
const result = observed.get(key);
|
|
16139
16172
|
if (result) return result;
|
|
16140
16173
|
throw new PendingHarnessCommand(command, args, timeoutMs, key);
|
|
16141
|
-
}
|
|
16174
|
+
});
|
|
16142
16175
|
} catch (pending) {
|
|
16143
16176
|
if (!(pending instanceof PendingHarnessCommand)) throw pending;
|
|
16144
|
-
if (commands ===
|
|
16177
|
+
if (commands === limit) throw new Error("auth_probe_command_limit");
|
|
16145
16178
|
observed.set(pending.key, await run(pending.command, pending.args, pending.timeoutMs));
|
|
16146
16179
|
}
|
|
16147
16180
|
}
|
|
16148
16181
|
throw new Error("auth_probe_command_limit");
|
|
16149
16182
|
}
|
|
16183
|
+
async function probeHarnessAssignmentAsync(options) {
|
|
16184
|
+
return replayHarnessProbe(
|
|
16185
|
+
options.runCommand ?? defaultAsyncRunner(options.env ?? process.env, options.signal),
|
|
16186
|
+
(runCommand) => probeHarnessAssignment({ ...options, runCommand }),
|
|
16187
|
+
32
|
|
16188
|
+
);
|
|
16189
|
+
}
|
|
16150
16190
|
function probeHarnessesForModel(modelSpec, options = {}) {
|
|
16151
16191
|
const timeoutMs = options.timeoutMs ?? 3e3;
|
|
16152
16192
|
const runCommand = options.runCommand ?? defaultRunner(options.env ?? process.env);
|
|
@@ -16167,6 +16207,13 @@ function probeHarnessesForModel(modelSpec, options = {}) {
|
|
|
16167
16207
|
)
|
|
16168
16208
|
};
|
|
16169
16209
|
}
|
|
16210
|
+
async function probeHarnessesForModelAsync(modelSpec, options = {}) {
|
|
16211
|
+
return replayHarnessProbe(
|
|
16212
|
+
options.runCommand ?? defaultAsyncRunner(options.env ?? process.env, options.signal),
|
|
16213
|
+
(runCommand) => probeHarnessesForModel(modelSpec, { ...options, runCommand }),
|
|
16214
|
+
256
|
|
16215
|
+
);
|
|
16216
|
+
}
|
|
16170
16217
|
function scopesFor(scope, entry) {
|
|
16171
16218
|
if (scope === "self") return ["self"];
|
|
16172
16219
|
if (scope === "extensions") return entry.update.extensions ? ["extensions"] : [];
|
|
@@ -22351,9 +22398,28 @@ import { join as join11 } from "node:path";
|
|
|
22351
22398
|
import { createHash as createHash7, randomUUID as randomUUID3 } from "node:crypto";
|
|
22352
22399
|
import { lstat, mkdir, open, rename } from "node:fs/promises";
|
|
22353
22400
|
import { join as join6 } from "node:path";
|
|
22401
|
+
var ONESHOT_EVIDENCE_SCHEMA = "kxm.oneshot-evidence.v2";
|
|
22402
|
+
var RETIRED_ONESHOT_EVIDENCE_SCHEMAS = /* @__PURE__ */ new Set(["kxm.oneshot-evidence.v1"]);
|
|
22354
22403
|
var TEXT_LIMIT = 4 * 1024 * 1024;
|
|
22404
|
+
var ARGV_LIMIT = 64 * 1024;
|
|
22355
22405
|
var RECORD_LIMIT = 16 * 1024 * 1024;
|
|
22356
22406
|
var digest = (value) => `sha256:${createHash7("sha256").update(value).digest("hex")}`;
|
|
22407
|
+
function parseOneShotEvidenceRecord(bytes) {
|
|
22408
|
+
let parsed;
|
|
22409
|
+
try {
|
|
22410
|
+
parsed = JSON.parse(bytes);
|
|
22411
|
+
} catch {
|
|
22412
|
+
throw new Error("oneshot_evidence_record_invalid");
|
|
22413
|
+
}
|
|
22414
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
22415
|
+
throw new Error("oneshot_evidence_record_invalid");
|
|
22416
|
+
}
|
|
22417
|
+
const schema = parsed.schema;
|
|
22418
|
+
if (typeof schema !== "string") throw new Error("oneshot_evidence_record_invalid");
|
|
22419
|
+
if (RETIRED_ONESHOT_EVIDENCE_SCHEMAS.has(schema)) throw new Error("oneshot_evidence_schema_retired");
|
|
22420
|
+
if (schema !== ONESHOT_EVIDENCE_SCHEMA) throw new Error("oneshot_evidence_schema_unknown");
|
|
22421
|
+
return parsed;
|
|
22422
|
+
}
|
|
22357
22423
|
async function beginOneShotEvidence(root, intent, sensitive) {
|
|
22358
22424
|
await mkdir(root, { recursive: true, mode: 448 });
|
|
22359
22425
|
const stat = await lstat(root);
|
|
@@ -22367,11 +22433,11 @@ async function beginOneShotEvidence(root, intent, sensitive) {
|
|
|
22367
22433
|
const redact = (value) => {
|
|
22368
22434
|
let text = value;
|
|
22369
22435
|
for (const secret of secrets) text = text.replaceAll(secret, "[REDACTED]");
|
|
22370
|
-
return text.replace(/(\b(?:authorization|api[_-]?key|access[_-]?token|refresh[_-]?token|password|secret)\b["']?\s*[:=]\s*["']?)(?:Bearer\s+)?[^\s"',;}]+/gi, "$1[REDACTED]");
|
|
22436
|
+
return text.replace(/(\b(?:authorization|api[_-]?key|access[_-]?token|refresh[_-]?token|password|secret)\b["']?\s*[:=]\s*["']?)(?:Bearer\s+)?[^\s"',;}]+/gi, "$1[REDACTED]").replace(/\bBearer\s+[A-Za-z0-9._~+/=-]+/gi, "Bearer [REDACTED]");
|
|
22371
22437
|
};
|
|
22372
|
-
const bounded = (value) => {
|
|
22438
|
+
const bounded = (value, max = TEXT_LIMIT) => {
|
|
22373
22439
|
const bytes = Buffer.from(redact(value));
|
|
22374
|
-
return { text: bytes.subarray(0,
|
|
22440
|
+
return { text: bytes.subarray(0, max).toString("utf8"), truncated: bytes.length > max, sha256: digest(redact(value)) };
|
|
22375
22441
|
};
|
|
22376
22442
|
const write = async (name, value) => {
|
|
22377
22443
|
const current = await lstat(dir);
|
|
@@ -22380,6 +22446,7 @@ async function beginOneShotEvidence(root, intent, sensitive) {
|
|
|
22380
22446
|
}
|
|
22381
22447
|
const bytes = JSON.stringify(value);
|
|
22382
22448
|
if (Buffer.byteLength(bytes) > RECORD_LIMIT) throw new Error("oneshot_evidence_record_limit");
|
|
22449
|
+
parseOneShotEvidenceRecord(bytes);
|
|
22383
22450
|
const target = join6(dir, name);
|
|
22384
22451
|
const partial = `${target}.partial`;
|
|
22385
22452
|
const file = await open(partial, "wx", 384);
|
|
@@ -22401,7 +22468,7 @@ async function beginOneShotEvidence(root, intent, sensitive) {
|
|
|
22401
22468
|
return digest(bytes);
|
|
22402
22469
|
};
|
|
22403
22470
|
await write("intent.json", {
|
|
22404
|
-
schema:
|
|
22471
|
+
schema: ONESHOT_EVIDENCE_SCHEMA,
|
|
22405
22472
|
id,
|
|
22406
22473
|
recordedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22407
22474
|
runId: intent.runId,
|
|
@@ -22412,9 +22479,10 @@ async function beginOneShotEvidence(root, intent, sensitive) {
|
|
|
22412
22479
|
provider: intent.provider,
|
|
22413
22480
|
requestedModel: intent.model,
|
|
22414
22481
|
cwd: intent.cwd,
|
|
22415
|
-
command: intent.command,
|
|
22416
|
-
argv: bounded(JSON.stringify(intent.args)),
|
|
22482
|
+
command: redact(intent.command),
|
|
22483
|
+
argv: bounded(JSON.stringify(intent.args), ARGV_LIMIT),
|
|
22417
22484
|
stdin: intent.input === void 0 ? null : bounded(intent.input),
|
|
22485
|
+
env: null,
|
|
22418
22486
|
acceptance: false
|
|
22419
22487
|
});
|
|
22420
22488
|
let finished = false;
|
|
@@ -22424,7 +22492,7 @@ async function beginOneShotEvidence(root, intent, sensitive) {
|
|
|
22424
22492
|
if (finished) throw new Error("oneshot_evidence_already_finished");
|
|
22425
22493
|
finished = true;
|
|
22426
22494
|
return write("result.json", {
|
|
22427
|
-
schema:
|
|
22495
|
+
schema: ONESHOT_EVIDENCE_SCHEMA,
|
|
22428
22496
|
id,
|
|
22429
22497
|
recordedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
22430
22498
|
stdout: bounded(result.stdout),
|
|
@@ -22434,6 +22502,7 @@ async function beginOneShotEvidence(root, intent, sensitive) {
|
|
|
22434
22502
|
started: result.started ?? null,
|
|
22435
22503
|
observedChildExit: result.observedChildExit ?? null,
|
|
22436
22504
|
terminationRequested: result.terminationRequested ?? null,
|
|
22505
|
+
unverifiedDescendants: result.unverifiedDescendants ?? null,
|
|
22437
22506
|
error: result.error ? bounded(result.error.message) : null,
|
|
22438
22507
|
observation: bounded(JSON.stringify(observation)),
|
|
22439
22508
|
acceptance: false
|
|
@@ -26106,7 +26175,8 @@ Return a final JSON object with an "outcome" field chosen from ${JSON.stringify(
|
|
|
26106
26175
|
parsed = { text: "", isError: true };
|
|
26107
26176
|
}
|
|
26108
26177
|
const aborted = request.signal.aborted || procResult.error?.message === "process_aborted";
|
|
26109
|
-
const
|
|
26178
|
+
const unverifiedDescendants = procResult.unverifiedDescendants === true || procResult.terminationRequested === true || procResult.error?.message === "process_exit_unobserved" || procResult.observedChildExit === false && procResult.started !== false;
|
|
26179
|
+
const effectUncertain = unverifiedDescendants || Boolean(procResult.signal);
|
|
26110
26180
|
const transportFailed = procResult.code !== 0 || Boolean(procResult.error) || effectUncertain;
|
|
26111
26181
|
const outcome = aborted ? "cancelled" : transportFailed || parsed.isError ? "failed" : determineOutcome(parsed.text, request.allowedOutcomes);
|
|
26112
26182
|
const providerMetadata = {
|
|
@@ -26118,6 +26188,7 @@ Return a final JSON object with an "outcome" field chosen from ${JSON.stringify(
|
|
|
26118
26188
|
if (procResult.observedChildExit !== void 0) providerMetadata.observedChildExit = procResult.observedChildExit;
|
|
26119
26189
|
if (procResult.started !== void 0) providerMetadata.processStarted = procResult.started;
|
|
26120
26190
|
if (procResult.terminationRequested !== void 0) providerMetadata.terminationRequested = procResult.terminationRequested;
|
|
26191
|
+
if (unverifiedDescendants) providerMetadata.descendantEffects = "unverified";
|
|
26121
26192
|
if (procResult.error) {
|
|
26122
26193
|
const reason = procResult.error.message;
|
|
26123
26194
|
providerMetadata.processError = /^process_(aborted|timeout|output_limit|stdin_error|stdio_error|stdio_unclosed|exit_unobserved)$/.test(reason) ? reason : "process_error";
|
|
@@ -26598,7 +26669,6 @@ async function startVnextRuntimeSupervisorInner(paths, requestedPortOption, now)
|
|
|
26598
26669
|
});
|
|
26599
26670
|
} catch (err) {
|
|
26600
26671
|
earlyError = err;
|
|
26601
|
-
drivePromise = Promise.reject(err);
|
|
26602
26672
|
}
|
|
26603
26673
|
await Promise.resolve();
|
|
26604
26674
|
if (earlyError) {
|
|
@@ -26624,6 +26694,7 @@ async function startVnextRuntimeSupervisorInner(paths, requestedPortOption, now)
|
|
|
26624
26694
|
} catch {
|
|
26625
26695
|
}
|
|
26626
26696
|
}
|
|
26697
|
+
}).catch(() => {
|
|
26627
26698
|
});
|
|
26628
26699
|
sendJson(response, 202, {
|
|
26629
26700
|
ok: true,
|
|
@@ -27109,19 +27180,21 @@ function createVnextPiProducer(options = {}) {
|
|
|
27109
27180
|
const parsed = parseModelString(defaultModel);
|
|
27110
27181
|
return { provider: parsed.provider, model: parsed.model };
|
|
27111
27182
|
}
|
|
27112
|
-
function checkPiAuth(provider, model) {
|
|
27183
|
+
async function checkPiAuth(provider, model, signal) {
|
|
27113
27184
|
if (options.inventory) {
|
|
27114
27185
|
const piEntry = options.inventory.harnesses.find((h) => h.id === "pi");
|
|
27115
27186
|
if (!piEntry || !piEntry.detected || piEntry.authenticated === false) {
|
|
27116
27187
|
throw new Error("pi_not_authenticated: pi harness not authenticated in inventory");
|
|
27117
27188
|
}
|
|
27118
27189
|
}
|
|
27119
|
-
const probeFn = options.probeHarness ??
|
|
27120
|
-
const probe = probeFn({
|
|
27190
|
+
const probeFn = options.probeHarness ?? probeHarnessAssignmentAsync;
|
|
27191
|
+
const probe = await probeFn({
|
|
27121
27192
|
harness: "pi",
|
|
27122
27193
|
provider,
|
|
27123
27194
|
model,
|
|
27124
|
-
env: options.env
|
|
27195
|
+
env: options.env,
|
|
27196
|
+
signal,
|
|
27197
|
+
timeoutMs: 1e4
|
|
27125
27198
|
});
|
|
27126
27199
|
if (!probe.detected) {
|
|
27127
27200
|
throw new Error(`pi_not_authenticated: pi harness not detected (${probe.issues.join(", ")})`);
|
|
@@ -27197,7 +27270,7 @@ function createVnextPiProducer(options = {}) {
|
|
|
27197
27270
|
async produce(request) {
|
|
27198
27271
|
const startTime = Date.now();
|
|
27199
27272
|
const resolved = resolveModelForRequest(request);
|
|
27200
|
-
checkPiAuth(resolved.provider, resolved.model);
|
|
27273
|
+
await checkPiAuth(resolved.provider, resolved.model, request.signal);
|
|
27201
27274
|
const session = await getOrCreateSession(request, resolved.model);
|
|
27202
27275
|
const promptMessage = request.prompt ?? `Execute step ${request.stepId} (attempt ${request.stepAttempt}) for agent ${request.agentId}. Allowed outcomes: ${request.allowedOutcomes.join(", ")}.`;
|
|
27203
27276
|
let promptResult;
|
|
@@ -28815,6 +28888,212 @@ function executeSshRun(params) {
|
|
|
28815
28888
|
durationMs: Date.now() - startTime
|
|
28816
28889
|
};
|
|
28817
28890
|
}
|
|
28891
|
+
|
|
28892
|
+
// plugins/kxm/src/subagent-control.ts
|
|
28893
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
28894
|
+
var SUBAGENT_TYPES = Object.freeze([
|
|
28895
|
+
"general",
|
|
28896
|
+
"Explore",
|
|
28897
|
+
"Plan",
|
|
28898
|
+
"Reviewer",
|
|
28899
|
+
"Auditor"
|
|
28900
|
+
]);
|
|
28901
|
+
var DEFAULT_SUBAGENT_MODELS = Object.freeze({
|
|
28902
|
+
general: "grok/grok-4.6",
|
|
28903
|
+
Explore: "openrouter/qwen/qwen3-coder-plus",
|
|
28904
|
+
Plan: "claude/fable",
|
|
28905
|
+
Reviewer: "openai/gpt-5.6-sol",
|
|
28906
|
+
Auditor: "claude/fable"
|
|
28907
|
+
});
|
|
28908
|
+
function narrowSubagentTools(requestedTools, parentTools) {
|
|
28909
|
+
if (!requestedTools || requestedTools.length === 0) {
|
|
28910
|
+
return parentTools ? [...parentTools] : ["read", "grep", "find"];
|
|
28911
|
+
}
|
|
28912
|
+
if (!parentTools || parentTools.length === 0) {
|
|
28913
|
+
return [...requestedTools];
|
|
28914
|
+
}
|
|
28915
|
+
const parentSet = new Set(parentTools);
|
|
28916
|
+
const narrowed = [];
|
|
28917
|
+
for (const tool of requestedTools) {
|
|
28918
|
+
if (parentSet.has(tool)) {
|
|
28919
|
+
narrowed.push(tool);
|
|
28920
|
+
}
|
|
28921
|
+
}
|
|
28922
|
+
if (narrowed.length === 0 && requestedTools.length > 0) {
|
|
28923
|
+
throw new Error(
|
|
28924
|
+
`Cannot spawn subagent: requested tools [${requestedTools.join(", ")}] expand beyond parent capabilities.`
|
|
28925
|
+
);
|
|
28926
|
+
}
|
|
28927
|
+
return narrowed;
|
|
28928
|
+
}
|
|
28929
|
+
var SubagentManager = class {
|
|
28930
|
+
subagents = /* @__PURE__ */ new Map();
|
|
28931
|
+
/**
|
|
28932
|
+
* Spawns a new managed subagent with strict capability boundaries.
|
|
28933
|
+
*/
|
|
28934
|
+
spawn(params) {
|
|
28935
|
+
if (!params.prompt || !params.prompt.trim()) {
|
|
28936
|
+
throw new Error('Subagent "prompt" is required.');
|
|
28937
|
+
}
|
|
28938
|
+
if (!params.description || !params.description.trim()) {
|
|
28939
|
+
throw new Error('Subagent "description" is required.');
|
|
28940
|
+
}
|
|
28941
|
+
const type = params.type ?? "general";
|
|
28942
|
+
const model = params.model ?? DEFAULT_SUBAGENT_MODELS[type] ?? "grok/grok-4.6";
|
|
28943
|
+
const thinking = params.thinking ?? "low";
|
|
28944
|
+
const allowedTools = narrowSubagentTools(params.allowed_tools, params.parent_tools);
|
|
28945
|
+
const id = `ag_${randomUUID5().replaceAll("-", "").slice(0, 10)}`;
|
|
28946
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
28947
|
+
const record2 = {
|
|
28948
|
+
id,
|
|
28949
|
+
description: params.description.trim(),
|
|
28950
|
+
type,
|
|
28951
|
+
model,
|
|
28952
|
+
thinking,
|
|
28953
|
+
allowedTools,
|
|
28954
|
+
status: params.background === false ? "completed" : "running",
|
|
28955
|
+
turnsMax: params.turns ?? 10,
|
|
28956
|
+
turnsCompleted: 0,
|
|
28957
|
+
prompt: params.prompt.trim(),
|
|
28958
|
+
createdAt: now,
|
|
28959
|
+
updatedAt: now,
|
|
28960
|
+
steeringMessages: []
|
|
28961
|
+
};
|
|
28962
|
+
this.subagents.set(id, record2);
|
|
28963
|
+
return record2;
|
|
28964
|
+
}
|
|
28965
|
+
/**
|
|
28966
|
+
* Dispatches an action from the `agent_control` tool.
|
|
28967
|
+
*/
|
|
28968
|
+
control(params) {
|
|
28969
|
+
const action = params.action;
|
|
28970
|
+
if (action === "info") {
|
|
28971
|
+
const kind = params.kind ?? "active";
|
|
28972
|
+
if (kind === "types") {
|
|
28973
|
+
return {
|
|
28974
|
+
ok: true,
|
|
28975
|
+
action: "info",
|
|
28976
|
+
types: SUBAGENT_TYPES
|
|
28977
|
+
};
|
|
28978
|
+
}
|
|
28979
|
+
if (kind === "models") {
|
|
28980
|
+
return {
|
|
28981
|
+
ok: true,
|
|
28982
|
+
action: "info",
|
|
28983
|
+
result: JSON.stringify(DEFAULT_SUBAGENT_MODELS)
|
|
28984
|
+
};
|
|
28985
|
+
}
|
|
28986
|
+
const active = Array.from(this.subagents.values()).map((ag) => ({
|
|
28987
|
+
id: ag.id,
|
|
28988
|
+
description: ag.description,
|
|
28989
|
+
type: ag.type,
|
|
28990
|
+
status: ag.status
|
|
28991
|
+
}));
|
|
28992
|
+
return {
|
|
28993
|
+
ok: true,
|
|
28994
|
+
action: "info",
|
|
28995
|
+
activeAgents: active
|
|
28996
|
+
};
|
|
28997
|
+
}
|
|
28998
|
+
if (!params.agent_id) {
|
|
28999
|
+
return {
|
|
29000
|
+
ok: false,
|
|
29001
|
+
action,
|
|
29002
|
+
error: 'Parameter "agent_id" is required for result, steer, and stop actions.'
|
|
29003
|
+
};
|
|
29004
|
+
}
|
|
29005
|
+
const agent = this.subagents.get(params.agent_id);
|
|
29006
|
+
if (!agent) {
|
|
29007
|
+
return {
|
|
29008
|
+
ok: false,
|
|
29009
|
+
action,
|
|
29010
|
+
agent_id: params.agent_id,
|
|
29011
|
+
error: `Subagent "${params.agent_id}" not found.`
|
|
29012
|
+
};
|
|
29013
|
+
}
|
|
29014
|
+
if (action === "result") {
|
|
29015
|
+
return {
|
|
29016
|
+
ok: true,
|
|
29017
|
+
action: "result",
|
|
29018
|
+
agent_id: agent.id,
|
|
29019
|
+
status: agent.status,
|
|
29020
|
+
result: agent.output ?? `[Subagent is currently ${agent.status}]`
|
|
29021
|
+
};
|
|
29022
|
+
}
|
|
29023
|
+
if (action === "steer") {
|
|
29024
|
+
if (!params.message || !params.message.trim()) {
|
|
29025
|
+
return {
|
|
29026
|
+
ok: false,
|
|
29027
|
+
action: "steer",
|
|
29028
|
+
agent_id: agent.id,
|
|
29029
|
+
error: 'Parameter "message" is required for steer action.'
|
|
29030
|
+
};
|
|
29031
|
+
}
|
|
29032
|
+
if (agent.status === "completed" || agent.status === "stopped" || agent.status === "failed") {
|
|
29033
|
+
return {
|
|
29034
|
+
ok: false,
|
|
29035
|
+
action: "steer",
|
|
29036
|
+
agent_id: agent.id,
|
|
29037
|
+
error: `Cannot steer subagent "${agent.id}" because it is already ${agent.status}.`
|
|
29038
|
+
};
|
|
29039
|
+
}
|
|
29040
|
+
agent.steeringMessages.push({
|
|
29041
|
+
text: params.message.trim(),
|
|
29042
|
+
injectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
29043
|
+
processed: false
|
|
29044
|
+
});
|
|
29045
|
+
agent.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
29046
|
+
return {
|
|
29047
|
+
ok: true,
|
|
29048
|
+
action: "steer",
|
|
29049
|
+
agent_id: agent.id,
|
|
29050
|
+
status: agent.status,
|
|
29051
|
+
steered: true
|
|
29052
|
+
};
|
|
29053
|
+
}
|
|
29054
|
+
if (action === "stop") {
|
|
29055
|
+
agent.status = "stopped";
|
|
29056
|
+
agent.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
29057
|
+
return {
|
|
29058
|
+
ok: true,
|
|
29059
|
+
action: "stop",
|
|
29060
|
+
agent_id: agent.id,
|
|
29061
|
+
status: "stopped",
|
|
29062
|
+
stopped: true
|
|
29063
|
+
};
|
|
29064
|
+
}
|
|
29065
|
+
return {
|
|
29066
|
+
ok: false,
|
|
29067
|
+
action,
|
|
29068
|
+
error: `Unknown control action "${action}".`
|
|
29069
|
+
};
|
|
29070
|
+
}
|
|
29071
|
+
/**
|
|
29072
|
+
* Retrieves an agent record by ID.
|
|
29073
|
+
*/
|
|
29074
|
+
get(agentId) {
|
|
29075
|
+
return this.subagents.get(agentId);
|
|
29076
|
+
}
|
|
29077
|
+
/**
|
|
29078
|
+
* Marks a subagent as completed with output.
|
|
29079
|
+
*/
|
|
29080
|
+
complete(agentId, output) {
|
|
29081
|
+
const agent = this.subagents.get(agentId);
|
|
29082
|
+
if (agent) {
|
|
29083
|
+
agent.status = "completed";
|
|
29084
|
+
agent.output = output;
|
|
29085
|
+
agent.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
29086
|
+
}
|
|
29087
|
+
}
|
|
29088
|
+
/**
|
|
29089
|
+
* Returns all active running subagents.
|
|
29090
|
+
*/
|
|
29091
|
+
listActive() {
|
|
29092
|
+
return Array.from(this.subagents.values()).filter(
|
|
29093
|
+
(ag) => ag.status === "running" || ag.status === "pending" || ag.status === "blocked"
|
|
29094
|
+
);
|
|
29095
|
+
}
|
|
29096
|
+
};
|
|
28818
29097
|
export {
|
|
28819
29098
|
BUILTIN_HARNESSES,
|
|
28820
29099
|
BUILTIN_HARNESS_IDS,
|
|
@@ -28825,6 +29104,7 @@ export {
|
|
|
28825
29104
|
DEFAULT_LOG_MAX_FILES,
|
|
28826
29105
|
DEFAULT_MODES_CONFIG,
|
|
28827
29106
|
DEFAULT_SOCKET_DIR,
|
|
29107
|
+
DEFAULT_SUBAGENT_MODELS,
|
|
28828
29108
|
IMPROVEMENT_REPORT_SCHEMA,
|
|
28829
29109
|
IMPROVEMENT_REPORT_V1_SCHEMA,
|
|
28830
29110
|
LOG_LEVEL_PRIORITY,
|
|
@@ -28835,7 +29115,9 @@ export {
|
|
|
28835
29115
|
PI_NATIVE_BRAKE_PROVIDERS,
|
|
28836
29116
|
PiSession,
|
|
28837
29117
|
SAFE_HARNESS_COMMAND_ID,
|
|
29118
|
+
SUBAGENT_TYPES,
|
|
28838
29119
|
SteelClient,
|
|
29120
|
+
SubagentManager,
|
|
28839
29121
|
VIEWPORT_PRESETS,
|
|
28840
29122
|
VNEXT_ABSENT_MEMORY_REVISION,
|
|
28841
29123
|
VNEXT_EVENT_STORE_SCHEMA_VERSION,
|
|
@@ -28898,6 +29180,7 @@ export {
|
|
|
28898
29180
|
isVnextRuntimeContextClosed,
|
|
28899
29181
|
isWindowsHarnessShim,
|
|
28900
29182
|
loadModesConfig,
|
|
29183
|
+
narrowSubagentTools,
|
|
28901
29184
|
newVnextAssignmentId,
|
|
28902
29185
|
newVnextAttemptId,
|
|
28903
29186
|
newVnextCommandId,
|
|
@@ -28921,7 +29204,9 @@ export {
|
|
|
28921
29204
|
probeHarnessAssignment,
|
|
28922
29205
|
probeHarnessAssignmentAsync,
|
|
28923
29206
|
probeHarnesses,
|
|
29207
|
+
probeHarnessesAsync,
|
|
28924
29208
|
probeHarnessesForModel,
|
|
29209
|
+
probeHarnessesForModelAsync,
|
|
28925
29210
|
projectRuntimeKey,
|
|
28926
29211
|
pruneSocketDir,
|
|
28927
29212
|
readVnextRunStatus,
|