@kontextmind/kxm 0.7.64 → 0.7.66
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 +42 -1
- package/plugins/kxm/dist/mcp-server.js +1 -1
- package/plugins/kxm/dist/runtime-supervisor.js +56 -1
- package/plugins/kxm/dist/runtime.js +57 -1
- package/plugins/kxm/dist/server.js +42 -1
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/src/harness.ts +67 -1
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/oneshot-producer.ts +24 -0
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "kxm",
|
|
4
4
|
"displayName": "KXM",
|
|
5
|
-
"version": "0.7.
|
|
5
|
+
"version": "0.7.66",
|
|
6
6
|
"description": "Headless multi-agent orchestration, durable workflows, and a live operator dashboard for Pi and Claude Code",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "KontextMind",
|
package/plugins/kxm/dist/cli.js
CHANGED
|
@@ -21233,6 +21233,42 @@ function parseCodexOneShotUsage(stdout, stderr) {
|
|
|
21233
21233
|
}
|
|
21234
21234
|
};
|
|
21235
21235
|
}
|
|
21236
|
+
function parsePiOneShotUsage(stdout, _stderr) {
|
|
21237
|
+
let finalMessage;
|
|
21238
|
+
for (const line of stdout.split("\n")) {
|
|
21239
|
+
const trimmed = line.trim();
|
|
21240
|
+
if (!trimmed.startsWith("{")) continue;
|
|
21241
|
+
try {
|
|
21242
|
+
const event = JSON.parse(trimmed);
|
|
21243
|
+
if (event.type !== "message_end" && event.type !== "turn_end") continue;
|
|
21244
|
+
const message = event.message;
|
|
21245
|
+
if (!message || message.role !== "assistant") continue;
|
|
21246
|
+
finalMessage = message;
|
|
21247
|
+
} catch {
|
|
21248
|
+
}
|
|
21249
|
+
}
|
|
21250
|
+
if (!finalMessage) {
|
|
21251
|
+
return { text: "", isError: true, usage: {} };
|
|
21252
|
+
}
|
|
21253
|
+
const content = Array.isArray(finalMessage.content) ? finalMessage.content : [];
|
|
21254
|
+
const text = content.map((part) => part && typeof part === "object" && part.type === "text" ? String(part.text ?? "") : "").filter((part) => part.length > 0).join("\n");
|
|
21255
|
+
const effectiveModel = reportedModelId(finalMessage.model);
|
|
21256
|
+
const rawUsage = finalMessage.usage;
|
|
21257
|
+
const usage = rawUsage && typeof rawUsage === "object" ? {
|
|
21258
|
+
tokensIn: typeof rawUsage.input === "number" ? rawUsage.input : null,
|
|
21259
|
+
tokensOut: typeof rawUsage.output === "number" ? rawUsage.output : null,
|
|
21260
|
+
cacheReadTokens: typeof rawUsage.cacheRead === "number" ? rawUsage.cacheRead : null,
|
|
21261
|
+
cacheWriteTokens: typeof rawUsage.cacheWrite === "number" ? rawUsage.cacheWrite : null,
|
|
21262
|
+
contextTokens: null,
|
|
21263
|
+
costUsd: null
|
|
21264
|
+
} : {};
|
|
21265
|
+
const stopReason = typeof finalMessage.stopReason === "string" ? finalMessage.stopReason : void 0;
|
|
21266
|
+
const terminalError = stopReason === "error" || stopReason === "aborted";
|
|
21267
|
+
if (terminalError || text.length === 0) {
|
|
21268
|
+
return { text, isError: true, ...effectiveModel !== void 0 ? { effectiveModel } : {}, usage };
|
|
21269
|
+
}
|
|
21270
|
+
return { text, ...effectiveModel !== void 0 ? { effectiveModel } : {}, usage };
|
|
21271
|
+
}
|
|
21236
21272
|
function parseGenericOneShotUsage(stdout, _stderr) {
|
|
21237
21273
|
const trimmed = stdout.trim();
|
|
21238
21274
|
try {
|
|
@@ -21334,6 +21370,11 @@ function parseAgyOneShotUsage(stdout, _stderr) {
|
|
|
21334
21370
|
};
|
|
21335
21371
|
}
|
|
21336
21372
|
var READ_ONLY_ONESHOT_ARGS = Object.freeze({
|
|
21373
|
+
// pi: total containment for a one-shot witness — no tools (built-in, extension and
|
|
21374
|
+
// custom), no ambient extension/hook discovery, no skills, no prompt templates, no
|
|
21375
|
+
// AGENTS.md/CLAUDE.md context discovery, and an ephemeral session. `--no-tools` alone
|
|
21376
|
+
// is not enough: extensions and hooks can still run with their own permissions.
|
|
21377
|
+
pi: Object.freeze(["--no-tools", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-context-files", "--no-session"]),
|
|
21337
21378
|
claude: Object.freeze(["--tools", "Read,Glob,Grep", "--restricted", "--safe-mode", "--permission-mode", "plan", "--permission-prompts", "none", "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}', "--disable-slash-commands", "--no-session-persistence"]),
|
|
21338
21379
|
codex: Object.freeze(["--sandbox", "read-only", "--ignore-user-config", "-c", 'approval_policy="never"']),
|
|
21339
21380
|
grok: Object.freeze(["--sandbox", "read-only", "--permission-mode", "plan", "--tools", "Read,Glob,Grep", "--no-subagents", "--disable-web-search"]),
|
|
@@ -21360,7 +21401,7 @@ var BUILTIN_HARNESSES = Object.freeze([
|
|
|
21360
21401
|
argv: ["-p", "--mode", "json"],
|
|
21361
21402
|
promptVia: "arg",
|
|
21362
21403
|
outputFormat: "json",
|
|
21363
|
-
usageParser:
|
|
21404
|
+
usageParser: parsePiOneShotUsage
|
|
21364
21405
|
}
|
|
21365
21406
|
},
|
|
21366
21407
|
{
|
|
@@ -17121,7 +17121,7 @@ async function deliverInboxNotification(messageId, delivered, notify) {
|
|
|
17121
17121
|
}
|
|
17122
17122
|
|
|
17123
17123
|
// plugins/kxm/src/mcp-server.ts
|
|
17124
|
-
var VERSION = "0.7.
|
|
17124
|
+
var VERSION = "0.7.66";
|
|
17125
17125
|
var inbox = /* @__PURE__ */ new Map();
|
|
17126
17126
|
var notifiedInbox = /* @__PURE__ */ new Set();
|
|
17127
17127
|
var meshClient;
|
|
@@ -15431,6 +15431,42 @@ function parseCodexOneShotUsage(stdout, stderr) {
|
|
|
15431
15431
|
}
|
|
15432
15432
|
};
|
|
15433
15433
|
}
|
|
15434
|
+
function parsePiOneShotUsage(stdout, _stderr) {
|
|
15435
|
+
let finalMessage;
|
|
15436
|
+
for (const line of stdout.split("\n")) {
|
|
15437
|
+
const trimmed = line.trim();
|
|
15438
|
+
if (!trimmed.startsWith("{")) continue;
|
|
15439
|
+
try {
|
|
15440
|
+
const event = JSON.parse(trimmed);
|
|
15441
|
+
if (event.type !== "message_end" && event.type !== "turn_end") continue;
|
|
15442
|
+
const message = event.message;
|
|
15443
|
+
if (!message || message.role !== "assistant") continue;
|
|
15444
|
+
finalMessage = message;
|
|
15445
|
+
} catch {
|
|
15446
|
+
}
|
|
15447
|
+
}
|
|
15448
|
+
if (!finalMessage) {
|
|
15449
|
+
return { text: "", isError: true, usage: {} };
|
|
15450
|
+
}
|
|
15451
|
+
const content = Array.isArray(finalMessage.content) ? finalMessage.content : [];
|
|
15452
|
+
const text = content.map((part) => part && typeof part === "object" && part.type === "text" ? String(part.text ?? "") : "").filter((part) => part.length > 0).join("\n");
|
|
15453
|
+
const effectiveModel = reportedModelId(finalMessage.model);
|
|
15454
|
+
const rawUsage = finalMessage.usage;
|
|
15455
|
+
const usage = rawUsage && typeof rawUsage === "object" ? {
|
|
15456
|
+
tokensIn: typeof rawUsage.input === "number" ? rawUsage.input : null,
|
|
15457
|
+
tokensOut: typeof rawUsage.output === "number" ? rawUsage.output : null,
|
|
15458
|
+
cacheReadTokens: typeof rawUsage.cacheRead === "number" ? rawUsage.cacheRead : null,
|
|
15459
|
+
cacheWriteTokens: typeof rawUsage.cacheWrite === "number" ? rawUsage.cacheWrite : null,
|
|
15460
|
+
contextTokens: null,
|
|
15461
|
+
costUsd: null
|
|
15462
|
+
} : {};
|
|
15463
|
+
const stopReason = typeof finalMessage.stopReason === "string" ? finalMessage.stopReason : void 0;
|
|
15464
|
+
const terminalError = stopReason === "error" || stopReason === "aborted";
|
|
15465
|
+
if (terminalError || text.length === 0) {
|
|
15466
|
+
return { text, isError: true, ...effectiveModel !== void 0 ? { effectiveModel } : {}, usage };
|
|
15467
|
+
}
|
|
15468
|
+
return { text, ...effectiveModel !== void 0 ? { effectiveModel } : {}, usage };
|
|
15469
|
+
}
|
|
15434
15470
|
function parseGenericOneShotUsage(stdout, _stderr) {
|
|
15435
15471
|
const trimmed = stdout.trim();
|
|
15436
15472
|
try {
|
|
@@ -15532,6 +15568,11 @@ function parseAgyOneShotUsage(stdout, _stderr) {
|
|
|
15532
15568
|
};
|
|
15533
15569
|
}
|
|
15534
15570
|
var READ_ONLY_ONESHOT_ARGS = Object.freeze({
|
|
15571
|
+
// pi: total containment for a one-shot witness — no tools (built-in, extension and
|
|
15572
|
+
// custom), no ambient extension/hook discovery, no skills, no prompt templates, no
|
|
15573
|
+
// AGENTS.md/CLAUDE.md context discovery, and an ephemeral session. `--no-tools` alone
|
|
15574
|
+
// is not enough: extensions and hooks can still run with their own permissions.
|
|
15575
|
+
pi: Object.freeze(["--no-tools", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-context-files", "--no-session"]),
|
|
15535
15576
|
claude: Object.freeze(["--tools", "Read,Glob,Grep", "--restricted", "--safe-mode", "--permission-mode", "plan", "--permission-prompts", "none", "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}', "--disable-slash-commands", "--no-session-persistence"]),
|
|
15536
15577
|
codex: Object.freeze(["--sandbox", "read-only", "--ignore-user-config", "-c", 'approval_policy="never"']),
|
|
15537
15578
|
grok: Object.freeze(["--sandbox", "read-only", "--permission-mode", "plan", "--tools", "Read,Glob,Grep", "--no-subagents", "--disable-web-search"]),
|
|
@@ -15558,7 +15599,7 @@ var BUILTIN_HARNESSES = Object.freeze([
|
|
|
15558
15599
|
argv: ["-p", "--mode", "json"],
|
|
15559
15600
|
promptVia: "arg",
|
|
15560
15601
|
outputFormat: "json",
|
|
15561
|
-
usageParser:
|
|
15602
|
+
usageParser: parsePiOneShotUsage
|
|
15562
15603
|
}
|
|
15563
15604
|
},
|
|
15564
15605
|
{
|
|
@@ -26475,6 +26516,9 @@ function createKxmOneShotProducer(options = {}) {
|
|
|
26475
26516
|
}
|
|
26476
26517
|
function resolveModelForRequest(request, harness) {
|
|
26477
26518
|
if (request.model) {
|
|
26519
|
+
if (request.provider) {
|
|
26520
|
+
return { provider: request.provider.toLowerCase(), model: request.model, thinking: request.thinking };
|
|
26521
|
+
}
|
|
26478
26522
|
const parsed2 = parseModelString(request.model, harness);
|
|
26479
26523
|
return {
|
|
26480
26524
|
provider: request.provider?.toLowerCase() ?? parsed2.provider,
|
|
@@ -26485,6 +26529,9 @@ function createKxmOneShotProducer(options = {}) {
|
|
|
26485
26529
|
if (options.resolveModel) {
|
|
26486
26530
|
const resolved = options.resolveModel(request.agentId, request.runId);
|
|
26487
26531
|
if (resolved && resolved.model) {
|
|
26532
|
+
if (resolved.provider) {
|
|
26533
|
+
return { provider: resolved.provider.toLowerCase(), model: resolved.model, thinking: resolved.thinking };
|
|
26534
|
+
}
|
|
26488
26535
|
const parsed2 = parseModelString(resolved.model, harness);
|
|
26489
26536
|
return {
|
|
26490
26537
|
provider: resolved.provider?.toLowerCase() ?? parsed2.provider,
|
|
@@ -26594,6 +26641,14 @@ function createKxmOneShotProducer(options = {}) {
|
|
|
26594
26641
|
"stream-json",
|
|
26595
26642
|
"-p"
|
|
26596
26643
|
];
|
|
26644
|
+
} else if (harness === "pi") {
|
|
26645
|
+
const qualified = resolved.provider && resolved.model.startsWith(`${resolved.provider}/`) ? resolved.model : resolved.provider ? `${resolved.provider}/${resolved.model}` : resolved.model;
|
|
26646
|
+
args = [
|
|
26647
|
+
"--model",
|
|
26648
|
+
qualified,
|
|
26649
|
+
...permissionArgs,
|
|
26650
|
+
...oneShot.argv
|
|
26651
|
+
];
|
|
26597
26652
|
} else {
|
|
26598
26653
|
throw new Error(`oneshot_harness_unsupported: ${harness} permission_profile_unaudited`);
|
|
26599
26654
|
}
|
|
@@ -15428,6 +15428,42 @@ function parseCodexOneShotUsage(stdout, stderr) {
|
|
|
15428
15428
|
}
|
|
15429
15429
|
};
|
|
15430
15430
|
}
|
|
15431
|
+
function parsePiOneShotUsage(stdout, _stderr) {
|
|
15432
|
+
let finalMessage;
|
|
15433
|
+
for (const line of stdout.split("\n")) {
|
|
15434
|
+
const trimmed = line.trim();
|
|
15435
|
+
if (!trimmed.startsWith("{")) continue;
|
|
15436
|
+
try {
|
|
15437
|
+
const event = JSON.parse(trimmed);
|
|
15438
|
+
if (event.type !== "message_end" && event.type !== "turn_end") continue;
|
|
15439
|
+
const message = event.message;
|
|
15440
|
+
if (!message || message.role !== "assistant") continue;
|
|
15441
|
+
finalMessage = message;
|
|
15442
|
+
} catch {
|
|
15443
|
+
}
|
|
15444
|
+
}
|
|
15445
|
+
if (!finalMessage) {
|
|
15446
|
+
return { text: "", isError: true, usage: {} };
|
|
15447
|
+
}
|
|
15448
|
+
const content = Array.isArray(finalMessage.content) ? finalMessage.content : [];
|
|
15449
|
+
const text = content.map((part) => part && typeof part === "object" && part.type === "text" ? String(part.text ?? "") : "").filter((part) => part.length > 0).join("\n");
|
|
15450
|
+
const effectiveModel = reportedModelId(finalMessage.model);
|
|
15451
|
+
const rawUsage = finalMessage.usage;
|
|
15452
|
+
const usage = rawUsage && typeof rawUsage === "object" ? {
|
|
15453
|
+
tokensIn: typeof rawUsage.input === "number" ? rawUsage.input : null,
|
|
15454
|
+
tokensOut: typeof rawUsage.output === "number" ? rawUsage.output : null,
|
|
15455
|
+
cacheReadTokens: typeof rawUsage.cacheRead === "number" ? rawUsage.cacheRead : null,
|
|
15456
|
+
cacheWriteTokens: typeof rawUsage.cacheWrite === "number" ? rawUsage.cacheWrite : null,
|
|
15457
|
+
contextTokens: null,
|
|
15458
|
+
costUsd: null
|
|
15459
|
+
} : {};
|
|
15460
|
+
const stopReason = typeof finalMessage.stopReason === "string" ? finalMessage.stopReason : void 0;
|
|
15461
|
+
const terminalError = stopReason === "error" || stopReason === "aborted";
|
|
15462
|
+
if (terminalError || text.length === 0) {
|
|
15463
|
+
return { text, isError: true, ...effectiveModel !== void 0 ? { effectiveModel } : {}, usage };
|
|
15464
|
+
}
|
|
15465
|
+
return { text, ...effectiveModel !== void 0 ? { effectiveModel } : {}, usage };
|
|
15466
|
+
}
|
|
15431
15467
|
function parseGenericOneShotUsage(stdout, _stderr) {
|
|
15432
15468
|
const trimmed = stdout.trim();
|
|
15433
15469
|
try {
|
|
@@ -15529,6 +15565,11 @@ function parseAgyOneShotUsage(stdout, _stderr) {
|
|
|
15529
15565
|
};
|
|
15530
15566
|
}
|
|
15531
15567
|
var READ_ONLY_ONESHOT_ARGS = Object.freeze({
|
|
15568
|
+
// pi: total containment for a one-shot witness — no tools (built-in, extension and
|
|
15569
|
+
// custom), no ambient extension/hook discovery, no skills, no prompt templates, no
|
|
15570
|
+
// AGENTS.md/CLAUDE.md context discovery, and an ephemeral session. `--no-tools` alone
|
|
15571
|
+
// is not enough: extensions and hooks can still run with their own permissions.
|
|
15572
|
+
pi: Object.freeze(["--no-tools", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-context-files", "--no-session"]),
|
|
15532
15573
|
claude: Object.freeze(["--tools", "Read,Glob,Grep", "--restricted", "--safe-mode", "--permission-mode", "plan", "--permission-prompts", "none", "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}', "--disable-slash-commands", "--no-session-persistence"]),
|
|
15533
15574
|
codex: Object.freeze(["--sandbox", "read-only", "--ignore-user-config", "-c", 'approval_policy="never"']),
|
|
15534
15575
|
grok: Object.freeze(["--sandbox", "read-only", "--permission-mode", "plan", "--tools", "Read,Glob,Grep", "--no-subagents", "--disable-web-search"]),
|
|
@@ -15555,7 +15596,7 @@ var BUILTIN_HARNESSES = Object.freeze([
|
|
|
15555
15596
|
argv: ["-p", "--mode", "json"],
|
|
15556
15597
|
promptVia: "arg",
|
|
15557
15598
|
outputFormat: "json",
|
|
15558
|
-
usageParser:
|
|
15599
|
+
usageParser: parsePiOneShotUsage
|
|
15559
15600
|
}
|
|
15560
15601
|
},
|
|
15561
15602
|
{
|
|
@@ -26987,6 +27028,9 @@ function createKxmOneShotProducer(options = {}) {
|
|
|
26987
27028
|
}
|
|
26988
27029
|
function resolveModelForRequest(request, harness) {
|
|
26989
27030
|
if (request.model) {
|
|
27031
|
+
if (request.provider) {
|
|
27032
|
+
return { provider: request.provider.toLowerCase(), model: request.model, thinking: request.thinking };
|
|
27033
|
+
}
|
|
26990
27034
|
const parsed2 = parseModelString(request.model, harness);
|
|
26991
27035
|
return {
|
|
26992
27036
|
provider: request.provider?.toLowerCase() ?? parsed2.provider,
|
|
@@ -26997,6 +27041,9 @@ function createKxmOneShotProducer(options = {}) {
|
|
|
26997
27041
|
if (options.resolveModel) {
|
|
26998
27042
|
const resolved = options.resolveModel(request.agentId, request.runId);
|
|
26999
27043
|
if (resolved && resolved.model) {
|
|
27044
|
+
if (resolved.provider) {
|
|
27045
|
+
return { provider: resolved.provider.toLowerCase(), model: resolved.model, thinking: resolved.thinking };
|
|
27046
|
+
}
|
|
27000
27047
|
const parsed2 = parseModelString(resolved.model, harness);
|
|
27001
27048
|
return {
|
|
27002
27049
|
provider: resolved.provider?.toLowerCase() ?? parsed2.provider,
|
|
@@ -27106,6 +27153,14 @@ function createKxmOneShotProducer(options = {}) {
|
|
|
27106
27153
|
"stream-json",
|
|
27107
27154
|
"-p"
|
|
27108
27155
|
];
|
|
27156
|
+
} else if (harness === "pi") {
|
|
27157
|
+
const qualified = resolved.provider && resolved.model.startsWith(`${resolved.provider}/`) ? resolved.model : resolved.provider ? `${resolved.provider}/${resolved.model}` : resolved.model;
|
|
27158
|
+
args = [
|
|
27159
|
+
"--model",
|
|
27160
|
+
qualified,
|
|
27161
|
+
...permissionArgs,
|
|
27162
|
+
...oneShot.argv
|
|
27163
|
+
];
|
|
27109
27164
|
} else {
|
|
27110
27165
|
throw new Error(`oneshot_harness_unsupported: ${harness} permission_profile_unaudited`);
|
|
27111
27166
|
}
|
|
@@ -30371,6 +30426,7 @@ export {
|
|
|
30371
30426
|
parseGenericOneShotUsage,
|
|
30372
30427
|
parseGrokOneShotUsage,
|
|
30373
30428
|
parseKimiOneShotUsage,
|
|
30429
|
+
parsePiOneShotUsage,
|
|
30374
30430
|
parseSshConfig,
|
|
30375
30431
|
persistKxmRunState,
|
|
30376
30432
|
planHarnessUpdate,
|
|
@@ -11136,6 +11136,42 @@ function parseCodexOneShotUsage(stdout, stderr) {
|
|
|
11136
11136
|
}
|
|
11137
11137
|
};
|
|
11138
11138
|
}
|
|
11139
|
+
function parsePiOneShotUsage(stdout, _stderr) {
|
|
11140
|
+
let finalMessage;
|
|
11141
|
+
for (const line of stdout.split("\n")) {
|
|
11142
|
+
const trimmed = line.trim();
|
|
11143
|
+
if (!trimmed.startsWith("{")) continue;
|
|
11144
|
+
try {
|
|
11145
|
+
const event = JSON.parse(trimmed);
|
|
11146
|
+
if (event.type !== "message_end" && event.type !== "turn_end") continue;
|
|
11147
|
+
const message = event.message;
|
|
11148
|
+
if (!message || message.role !== "assistant") continue;
|
|
11149
|
+
finalMessage = message;
|
|
11150
|
+
} catch {
|
|
11151
|
+
}
|
|
11152
|
+
}
|
|
11153
|
+
if (!finalMessage) {
|
|
11154
|
+
return { text: "", isError: true, usage: {} };
|
|
11155
|
+
}
|
|
11156
|
+
const content = Array.isArray(finalMessage.content) ? finalMessage.content : [];
|
|
11157
|
+
const text2 = content.map((part) => part && typeof part === "object" && part.type === "text" ? String(part.text ?? "") : "").filter((part) => part.length > 0).join("\n");
|
|
11158
|
+
const effectiveModel = reportedModelId(finalMessage.model);
|
|
11159
|
+
const rawUsage = finalMessage.usage;
|
|
11160
|
+
const usage = rawUsage && typeof rawUsage === "object" ? {
|
|
11161
|
+
tokensIn: typeof rawUsage.input === "number" ? rawUsage.input : null,
|
|
11162
|
+
tokensOut: typeof rawUsage.output === "number" ? rawUsage.output : null,
|
|
11163
|
+
cacheReadTokens: typeof rawUsage.cacheRead === "number" ? rawUsage.cacheRead : null,
|
|
11164
|
+
cacheWriteTokens: typeof rawUsage.cacheWrite === "number" ? rawUsage.cacheWrite : null,
|
|
11165
|
+
contextTokens: null,
|
|
11166
|
+
costUsd: null
|
|
11167
|
+
} : {};
|
|
11168
|
+
const stopReason = typeof finalMessage.stopReason === "string" ? finalMessage.stopReason : void 0;
|
|
11169
|
+
const terminalError = stopReason === "error" || stopReason === "aborted";
|
|
11170
|
+
if (terminalError || text2.length === 0) {
|
|
11171
|
+
return { text: text2, isError: true, ...effectiveModel !== void 0 ? { effectiveModel } : {}, usage };
|
|
11172
|
+
}
|
|
11173
|
+
return { text: text2, ...effectiveModel !== void 0 ? { effectiveModel } : {}, usage };
|
|
11174
|
+
}
|
|
11139
11175
|
function parseGenericOneShotUsage(stdout, _stderr) {
|
|
11140
11176
|
const trimmed = stdout.trim();
|
|
11141
11177
|
try {
|
|
@@ -11237,6 +11273,11 @@ function parseAgyOneShotUsage(stdout, _stderr) {
|
|
|
11237
11273
|
};
|
|
11238
11274
|
}
|
|
11239
11275
|
var READ_ONLY_ONESHOT_ARGS = Object.freeze({
|
|
11276
|
+
// pi: total containment for a one-shot witness — no tools (built-in, extension and
|
|
11277
|
+
// custom), no ambient extension/hook discovery, no skills, no prompt templates, no
|
|
11278
|
+
// AGENTS.md/CLAUDE.md context discovery, and an ephemeral session. `--no-tools` alone
|
|
11279
|
+
// is not enough: extensions and hooks can still run with their own permissions.
|
|
11280
|
+
pi: Object.freeze(["--no-tools", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-context-files", "--no-session"]),
|
|
11240
11281
|
claude: Object.freeze(["--tools", "Read,Glob,Grep", "--restricted", "--safe-mode", "--permission-mode", "plan", "--permission-prompts", "none", "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}', "--disable-slash-commands", "--no-session-persistence"]),
|
|
11241
11282
|
codex: Object.freeze(["--sandbox", "read-only", "--ignore-user-config", "-c", 'approval_policy="never"']),
|
|
11242
11283
|
grok: Object.freeze(["--sandbox", "read-only", "--permission-mode", "plan", "--tools", "Read,Glob,Grep", "--no-subagents", "--disable-web-search"]),
|
|
@@ -11263,7 +11304,7 @@ var BUILTIN_HARNESSES = Object.freeze([
|
|
|
11263
11304
|
argv: ["-p", "--mode", "json"],
|
|
11264
11305
|
promptVia: "arg",
|
|
11265
11306
|
outputFormat: "json",
|
|
11266
|
-
usageParser:
|
|
11307
|
+
usageParser: parsePiOneShotUsage
|
|
11267
11308
|
}
|
|
11268
11309
|
},
|
|
11269
11310
|
{
|
package/plugins/kxm/package.json
CHANGED
|
@@ -297,6 +297,67 @@ export function parseCodexOneShotUsage(stdout: string, stderr: string): OneShotP
|
|
|
297
297
|
};
|
|
298
298
|
}
|
|
299
299
|
|
|
300
|
+
/**
|
|
301
|
+
* Parse pi's `--mode json` output: one NDJSON event object per line. The reply text is
|
|
302
|
+
* the text parts of the final assistant message (`message_end`/`turn_end` carry it
|
|
303
|
+
* complete, after the deltas); usage and the effective model ride the same message.
|
|
304
|
+
* Anything unparseable falls back to the raw stream so a shape change is visible in the
|
|
305
|
+
* recorded text rather than silently empty.
|
|
306
|
+
*/
|
|
307
|
+
export function parsePiOneShotUsage(stdout: string, _stderr: string): OneShotParsedOutput {
|
|
308
|
+
// Select the FINAL assistant message first, then extract every field from that one
|
|
309
|
+
// message atomically. Carrying text, usage or model forward from an earlier message
|
|
310
|
+
// let a truncated or empty final turn inherit a previous turn's PASS text and usage.
|
|
311
|
+
let finalMessage: Record<string, unknown> | undefined;
|
|
312
|
+
for (const line of stdout.split("\n")) {
|
|
313
|
+
const trimmed = line.trim();
|
|
314
|
+
if (!trimmed.startsWith("{")) continue;
|
|
315
|
+
try {
|
|
316
|
+
const event = JSON.parse(trimmed) as Record<string, unknown>;
|
|
317
|
+
if (event.type !== "message_end" && event.type !== "turn_end") continue;
|
|
318
|
+
const message = event.message as Record<string, unknown> | undefined;
|
|
319
|
+
if (!message || message.role !== "assistant") continue;
|
|
320
|
+
finalMessage = message;
|
|
321
|
+
} catch {
|
|
322
|
+
// skip malformed lines; the stream is append-only and a partial line means truncation
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (!finalMessage) {
|
|
326
|
+
// No assistant message at all: fail closed with empty text. Returning the raw stream
|
|
327
|
+
// would hand back thinking deltas or diagnostics as if they were the answer.
|
|
328
|
+
return { text: "", isError: true, usage: {} };
|
|
329
|
+
}
|
|
330
|
+
const content = Array.isArray(finalMessage.content) ? finalMessage.content : [];
|
|
331
|
+
const text = content
|
|
332
|
+
.map((part) => (part && typeof part === "object" && (part as Record<string, unknown>).type === "text"
|
|
333
|
+
? String((part as Record<string, unknown>).text ?? "")
|
|
334
|
+
: ""))
|
|
335
|
+
.filter((part) => part.length > 0)
|
|
336
|
+
.join("\n");
|
|
337
|
+
// pi can exit zero on a failed turn; the stop reason is the fact that matters.
|
|
338
|
+
// Metadata comes from the selected final message even on a failed turn: usage and
|
|
339
|
+
// model are facts about what ran, not claims about the outcome, and dropping them
|
|
340
|
+
// turned a real 7/3-token failure into null/null with model "unknown".
|
|
341
|
+
const effectiveModel = reportedModelId(finalMessage.model);
|
|
342
|
+
const rawUsage = finalMessage.usage as Record<string, unknown> | undefined;
|
|
343
|
+
const usage: OneShotUsage = rawUsage && typeof rawUsage === "object"
|
|
344
|
+
? {
|
|
345
|
+
tokensIn: typeof rawUsage.input === "number" ? rawUsage.input : null,
|
|
346
|
+
tokensOut: typeof rawUsage.output === "number" ? rawUsage.output : null,
|
|
347
|
+
cacheReadTokens: typeof rawUsage.cacheRead === "number" ? rawUsage.cacheRead : null,
|
|
348
|
+
cacheWriteTokens: typeof rawUsage.cacheWrite === "number" ? rawUsage.cacheWrite : null,
|
|
349
|
+
contextTokens: null,
|
|
350
|
+
costUsd: null,
|
|
351
|
+
}
|
|
352
|
+
: {};
|
|
353
|
+
const stopReason = typeof finalMessage.stopReason === "string" ? finalMessage.stopReason : undefined;
|
|
354
|
+
const terminalError = stopReason === "error" || stopReason === "aborted";
|
|
355
|
+
if (terminalError || text.length === 0) {
|
|
356
|
+
return { text, isError: true, ...(effectiveModel !== undefined ? { effectiveModel } : {}), usage };
|
|
357
|
+
}
|
|
358
|
+
return { text, ...(effectiveModel !== undefined ? { effectiveModel } : {}), usage };
|
|
359
|
+
}
|
|
360
|
+
|
|
300
361
|
export function parseGenericOneShotUsage(stdout: string, _stderr: string): OneShotParsedOutput {
|
|
301
362
|
const trimmed = stdout.trim();
|
|
302
363
|
try {
|
|
@@ -419,6 +480,11 @@ export function parseAgyOneShotUsage(stdout: string, _stderr: string): OneShotPa
|
|
|
419
480
|
|
|
420
481
|
/** Built-in harnesses. Unknown ids fail closed. Model lists live in `.kxm/models/*.yaml`, not here. */
|
|
421
482
|
const READ_ONLY_ONESHOT_ARGS = Object.freeze({
|
|
483
|
+
// pi: total containment for a one-shot witness — no tools (built-in, extension and
|
|
484
|
+
// custom), no ambient extension/hook discovery, no skills, no prompt templates, no
|
|
485
|
+
// AGENTS.md/CLAUDE.md context discovery, and an ephemeral session. `--no-tools` alone
|
|
486
|
+
// is not enough: extensions and hooks can still run with their own permissions.
|
|
487
|
+
pi: Object.freeze(["--no-tools", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-context-files", "--no-session"]),
|
|
422
488
|
claude: Object.freeze(["--tools", "Read,Glob,Grep", "--restricted", "--safe-mode", "--permission-mode", "plan", "--permission-prompts", "none", "--strict-mcp-config", "--mcp-config", '{"mcpServers":{}}', "--disable-slash-commands", "--no-session-persistence"]),
|
|
423
489
|
codex: Object.freeze(["--sandbox", "read-only", "--ignore-user-config", "-c", 'approval_policy="never"']),
|
|
424
490
|
grok: Object.freeze(["--sandbox", "read-only", "--permission-mode", "plan", "--tools", "Read,Glob,Grep", "--no-subagents", "--disable-web-search"]),
|
|
@@ -447,7 +513,7 @@ export const BUILTIN_HARNESSES: readonly HarnessCatalogEntry[] = Object.freeze([
|
|
|
447
513
|
argv: ["-p", "--mode", "json"],
|
|
448
514
|
promptVia: "arg",
|
|
449
515
|
outputFormat: "json",
|
|
450
|
-
usageParser:
|
|
516
|
+
usageParser: parsePiOneShotUsage,
|
|
451
517
|
},
|
|
452
518
|
},
|
|
453
519
|
{
|
|
@@ -8,7 +8,7 @@ import { AGENT_COMMANDS_MAP, enforceToolPolicy, getMcpTools, reconcileInbox } fr
|
|
|
8
8
|
import { deliverInboxNotification } from "./inbox.ts";
|
|
9
9
|
import type { HubEvent, MessageRecord } from "./protocol.ts";
|
|
10
10
|
|
|
11
|
-
const VERSION = "0.7.
|
|
11
|
+
const VERSION = "0.7.66";
|
|
12
12
|
const inbox = new Map<string, MessageRecord>();
|
|
13
13
|
const notifiedInbox = new Set<string>();
|
|
14
14
|
let meshClient: HubClient | undefined;
|
|
@@ -100,6 +100,13 @@ export function createKxmOneShotProducer(options: KxmOneShotProducerOptions = {}
|
|
|
100
100
|
harness: string,
|
|
101
101
|
): { provider: string; model: string; thinking?: string | undefined } {
|
|
102
102
|
if (request.model) {
|
|
103
|
+
// A separate provider field means the model id is bare and may itself be
|
|
104
|
+
// namespaced (openrouter + qwen/qwen3-coder-plus); reparsing it as a
|
|
105
|
+
// provider/model selector strips the namespace — the same defect #262 fixed
|
|
106
|
+
// in the pi producer. Only parse when the provider must come from the string.
|
|
107
|
+
if (request.provider) {
|
|
108
|
+
return { provider: request.provider.toLowerCase(), model: request.model, thinking: request.thinking };
|
|
109
|
+
}
|
|
103
110
|
const parsed = parseModelString(request.model, harness);
|
|
104
111
|
return {
|
|
105
112
|
provider: request.provider?.toLowerCase() ?? parsed.provider,
|
|
@@ -110,6 +117,9 @@ export function createKxmOneShotProducer(options: KxmOneShotProducerOptions = {}
|
|
|
110
117
|
if (options.resolveModel) {
|
|
111
118
|
const resolved = options.resolveModel(request.agentId, request.runId);
|
|
112
119
|
if (resolved && resolved.model) {
|
|
120
|
+
if (resolved.provider) {
|
|
121
|
+
return { provider: resolved.provider.toLowerCase(), model: resolved.model, thinking: resolved.thinking };
|
|
122
|
+
}
|
|
113
123
|
const parsed = parseModelString(resolved.model, harness);
|
|
114
124
|
return {
|
|
115
125
|
provider: resolved.provider?.toLowerCase() ?? parsed.provider,
|
|
@@ -213,6 +223,20 @@ export function createKxmOneShotProducer(options: KxmOneShotProducerOptions = {}
|
|
|
213
223
|
"stream-json",
|
|
214
224
|
"-p",
|
|
215
225
|
];
|
|
226
|
+
} else if (harness === "pi") {
|
|
227
|
+
// pi's model flag takes the provider-qualified id — exactly the auth probe's
|
|
228
|
+
// rule (#265): prefix with the provider unless the model already starts with it.
|
|
229
|
+
// A bare "contains a slash" test is wrong for provider-local namespaces
|
|
230
|
+
// (openrouter + qwen/qwen3-coder-plus must spawn as the full three-part id).
|
|
231
|
+
const qualified = resolved.provider && resolved.model.startsWith(`${resolved.provider}/`)
|
|
232
|
+
? resolved.model
|
|
233
|
+
: resolved.provider ? `${resolved.provider}/${resolved.model}` : resolved.model;
|
|
234
|
+
args = [
|
|
235
|
+
"--model",
|
|
236
|
+
qualified,
|
|
237
|
+
...permissionArgs,
|
|
238
|
+
...oneShot.argv,
|
|
239
|
+
];
|
|
216
240
|
} else {
|
|
217
241
|
throw new Error(`oneshot_harness_unsupported: ${harness} permission_profile_unaudited`);
|
|
218
242
|
}
|