@kontextmind/kxm 0.7.96 → 0.7.98
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/workflows/default.yaml +2 -0
- package/CHANGELOG.md +61 -2
- package/docs/concepts/architecture.md +1 -1
- package/docs/concepts/data-and-storage.md +1 -1
- package/docs/concepts/trust-model.md +3 -2
- package/docs/contracts/routing.md +1 -1
- package/docs/contributing/test-matrix.md +6 -5
- package/docs/glossary.md +1 -1
- package/docs/guides/peer-messaging.md +2 -2
- package/docs/guides/pi-workers.md +1 -1
- package/docs/guides/webhook-workflows.md +53 -18
- package/docs/operations/backup-and-restore.md +43 -24
- package/docs/operations/deploy.md +2 -2
- package/docs/operations/troubleshooting.md +3 -2
- package/docs/reference/cli-reference.md +65 -30
- package/docs/reference/config-reference.md +24 -13
- package/docs/reference/configuration.md +2 -2
- package/docs/reference/harness-routing.md +3 -3
- package/docs/reference/http-api.md +7 -7
- package/docs/reference/tools.md +1 -1
- package/docs/reference/workflow-definitions.md +2 -2
- package/docs/start/first-workflow.md +4 -4
- package/docs/start/quickstart-claude-code.md +2 -2
- package/docs/start/quickstart-pi.md +1 -1
- package/examples/workflow-signal.ts +4 -5
- package/package.json +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/dist/claude-hook.js +11 -1
- package/plugins/kxm/dist/cli.js +552 -228
- package/plugins/kxm/dist/client.js +3 -1
- package/plugins/kxm/dist/core.js +16 -3
- package/plugins/kxm/dist/extension.js +45 -13
- package/plugins/kxm/dist/mcp-server.js +20 -4
- package/plugins/kxm/dist/runtime-supervisor.js +232 -51
- package/plugins/kxm/dist/runtime.js +432 -95
- package/plugins/kxm/dist/server.js +122 -20
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +4 -3
- package/plugins/kxm/skills/kxm-project-setup/SKILL.md +3 -2
- package/plugins/kxm/skills/kxm-routing-improve/SKILL.md +3 -0
- package/plugins/kxm/skills/kxm-runs/SKILL.md +8 -7
- package/plugins/kxm/src/cli/project.ts +22 -13
- package/plugins/kxm/src/cli/system.ts +22 -1
- package/plugins/kxm/src/cli/workflows.ts +12 -7
- package/plugins/kxm/src/cli.ts +30 -8
- package/plugins/kxm/src/client.ts +4 -0
- package/plugins/kxm/src/commands.ts +23 -1
- package/plugins/kxm/src/database.ts +210 -36
- package/plugins/kxm/src/engine.ts +117 -2
- package/plugins/kxm/src/extension.ts +20 -14
- package/plugins/kxm/src/github-watch.ts +8 -5
- package/plugins/kxm/src/harness.ts +29 -0
- package/plugins/kxm/src/hub-env.ts +19 -1
- package/plugins/kxm/src/hub.ts +105 -21
- package/plugins/kxm/src/improve-sources.ts +2 -7
- package/plugins/kxm/src/init-guide-setup.ts +43 -28
- package/plugins/kxm/src/mcp-server.ts +9 -2
- package/plugins/kxm/src/oneshot-producer.ts +16 -8
- package/plugins/kxm/src/prices.ts +33 -2
- package/plugins/kxm/src/routing.ts +13 -7
- package/plugins/kxm/src/runtime-store.ts +23 -0
- package/plugins/kxm/src/studio-layout.ts +5 -4
- package/plugins/kxm/src/template.ts +31 -0
- package/plugins/kxm/src/workflow.ts +70 -1
- package/plugins/kxm/src/worktree-witness.ts +71 -0
- package/schemas/backup-manifest.schema.json +33 -0
- package/scripts/smoke-multi-pi.mjs +5 -1
|
@@ -219,7 +219,9 @@ var HubClient = class {
|
|
|
219
219
|
options.workflowContext
|
|
220
220
|
)
|
|
221
221
|
} : {},
|
|
222
|
-
...options.ttlMs ? { ttlMs: options.ttlMs } : {}
|
|
222
|
+
...options.ttlMs ? { ttlMs: options.ttlMs } : {},
|
|
223
|
+
...options.hops !== void 0 ? { hops: options.hops } : {},
|
|
224
|
+
...options.maxHops !== void 0 ? { maxHops: options.maxHops } : {}
|
|
223
225
|
});
|
|
224
226
|
const completed = await this.awaitResponse(
|
|
225
227
|
message.id,
|
package/plugins/kxm/dist/core.js
CHANGED
|
@@ -461,6 +461,8 @@ function compareRoutingRecords(records) {
|
|
|
461
461
|
const blocked = settled.filter((record) => record.finalOutcome === "blocked").length;
|
|
462
462
|
const failed = settled.filter((record) => record.finalOutcome === "failed").length;
|
|
463
463
|
const reworked = records.filter((record) => record.retries > 0 || record.transitions > 0).length;
|
|
464
|
+
const missingCostRuns = records.filter((record) => typeof record.costUsd !== "number" || !Number.isFinite(record.costUsd)).length;
|
|
465
|
+
const summedCost = records.reduce((sum, record) => sum + (typeof record.costUsd === "number" && Number.isFinite(record.costUsd) ? record.costUsd : 0), 0);
|
|
464
466
|
return {
|
|
465
467
|
behavioralSha256,
|
|
466
468
|
runs: records.length,
|
|
@@ -468,7 +470,8 @@ function compareRoutingRecords(records) {
|
|
|
468
470
|
blocked,
|
|
469
471
|
failed,
|
|
470
472
|
reworkRate: records.length === 0 ? 0 : Math.round(reworked / records.length * 100) / 100,
|
|
471
|
-
totalCostUsd:
|
|
473
|
+
totalCostUsd: missingCostRuns > 0 ? null : Math.round(summedCost * 1e4) / 1e4,
|
|
474
|
+
missingCostRuns,
|
|
472
475
|
totalTokensIn: records.reduce((sum, record) => sum + (record.tokensIn ?? 0), 0),
|
|
473
476
|
totalTokensOut: records.reduce((sum, record) => sum + (record.tokensOut ?? 0), 0),
|
|
474
477
|
totalHumanInterventions: records.reduce((sum, record) => sum + record.humanInterventions, 0)
|
|
@@ -652,7 +655,7 @@ function generateRoutingReport(records, options = {}) {
|
|
|
652
655
|
medianContextTokens = contextVals.length % 2 !== 0 ? contextVals[mid] : Math.round((contextVals[mid - 1] + contextVals[mid]) / 2);
|
|
653
656
|
}
|
|
654
657
|
const meteredCostUsd = Math.round(meteredCostTotal * 1e4) / 1e4;
|
|
655
|
-
const costPerAcceptedUsd = acceptedCount > 0 ? meteredCostUsd > 0 || unmeteredAttempts > 0 ? Math.round(meteredCostUsd / acceptedCount * 1e4) / 1e4 :
|
|
658
|
+
const costPerAcceptedUsd = acceptedCount > 0 ? unknownCostAttempts > 0 ? null : meteredCostUsd > 0 || unmeteredAttempts > 0 ? Math.round(meteredCostUsd / acceptedCount * 1e4) / 1e4 : 0 : null;
|
|
656
659
|
const flagged = unknownCostAttempts > 0;
|
|
657
660
|
let equivalentListCostUsd = void 0;
|
|
658
661
|
if (options.includeEquivalentListCost && options.catalog) {
|
|
@@ -981,6 +984,13 @@ var HubHttpError = class extends Error {
|
|
|
981
984
|
};
|
|
982
985
|
|
|
983
986
|
// plugins/kxm/src/commands.ts
|
|
987
|
+
function forwardedHops(handling) {
|
|
988
|
+
if (!handling?.length) return void 0;
|
|
989
|
+
return {
|
|
990
|
+
hops: Math.max(...handling.map((message) => message.hops)) + 1,
|
|
991
|
+
maxHops: Math.min(...handling.map((message) => message.maxHops))
|
|
992
|
+
};
|
|
993
|
+
}
|
|
984
994
|
function requiredString(value, name) {
|
|
985
995
|
if (typeof value !== "string" || value.trim() === "") throw new Error(`${name} is required`);
|
|
986
996
|
return value.trim();
|
|
@@ -1110,12 +1120,13 @@ var AGENT_COMMANDS = [
|
|
|
1110
1120
|
required: ["target", "content"],
|
|
1111
1121
|
additionalProperties: false
|
|
1112
1122
|
},
|
|
1113
|
-
async execute(client, args) {
|
|
1123
|
+
async execute(client, args, context) {
|
|
1114
1124
|
const delivery = optionalString2(args.delivery);
|
|
1115
1125
|
const correlationId = optionalString2(args.correlationId);
|
|
1116
1126
|
const idempotencyKey = optionalString2(args.idempotencyKey);
|
|
1117
1127
|
const workflowContext = optionalWorkflowContext(args.workflowContext);
|
|
1118
1128
|
const message = await client.send({
|
|
1129
|
+
...forwardedHops(context?.handling),
|
|
1119
1130
|
target: requiredString(args.target, "target"),
|
|
1120
1131
|
content: requiredString(args.content, "content"),
|
|
1121
1132
|
...delivery ? { delivery } : {},
|
|
@@ -1193,6 +1204,7 @@ var AGENT_COMMANDS = [
|
|
|
1193
1204
|
const targets = Array.isArray(args.targets) ? args.targets.map((t) => requiredString(t, "target")) : [];
|
|
1194
1205
|
return {
|
|
1195
1206
|
responses: await client.fanout({
|
|
1207
|
+
...forwardedHops(context?.handling),
|
|
1196
1208
|
targets,
|
|
1197
1209
|
content: requiredString(args.content, "content"),
|
|
1198
1210
|
...optionalString2(args.correlationId) ? { correlationId: optionalString2(args.correlationId) } : {},
|
|
@@ -2156,6 +2168,7 @@ export {
|
|
|
2156
2168
|
enforceToolPolicy,
|
|
2157
2169
|
evaluateCircuitBreaker,
|
|
2158
2170
|
formatRoutingReport,
|
|
2171
|
+
forwardedHops,
|
|
2159
2172
|
gateWorker,
|
|
2160
2173
|
generateRoutingReport,
|
|
2161
2174
|
getCliAgentCommands,
|
|
@@ -14799,7 +14799,9 @@ var HubClient = class {
|
|
|
14799
14799
|
options.workflowContext
|
|
14800
14800
|
)
|
|
14801
14801
|
} : {},
|
|
14802
|
-
...options.ttlMs ? { ttlMs: options.ttlMs } : {}
|
|
14802
|
+
...options.ttlMs ? { ttlMs: options.ttlMs } : {},
|
|
14803
|
+
...options.hops !== void 0 ? { hops: options.hops } : {},
|
|
14804
|
+
...options.maxHops !== void 0 ? { maxHops: options.maxHops } : {}
|
|
14803
14805
|
});
|
|
14804
14806
|
const completed = await this.awaitResponse(
|
|
14805
14807
|
message.id,
|
|
@@ -15106,6 +15108,13 @@ async function hubJsonRequest(options, path, init, headers) {
|
|
|
15106
15108
|
}
|
|
15107
15109
|
|
|
15108
15110
|
// plugins/kxm/src/commands.ts
|
|
15111
|
+
function forwardedHops(handling) {
|
|
15112
|
+
if (!handling?.length) return void 0;
|
|
15113
|
+
return {
|
|
15114
|
+
hops: Math.max(...handling.map((message) => message.hops)) + 1,
|
|
15115
|
+
maxHops: Math.min(...handling.map((message) => message.maxHops))
|
|
15116
|
+
};
|
|
15117
|
+
}
|
|
15109
15118
|
function requiredString(value, name) {
|
|
15110
15119
|
if (typeof value !== "string" || value.trim() === "") throw new Error(`${name} is required`);
|
|
15111
15120
|
return value.trim();
|
|
@@ -15235,12 +15244,13 @@ var AGENT_COMMANDS = [
|
|
|
15235
15244
|
required: ["target", "content"],
|
|
15236
15245
|
additionalProperties: false
|
|
15237
15246
|
},
|
|
15238
|
-
async execute(client, args) {
|
|
15247
|
+
async execute(client, args, context) {
|
|
15239
15248
|
const delivery = optionalString(args.delivery);
|
|
15240
15249
|
const correlationId = optionalString(args.correlationId);
|
|
15241
15250
|
const idempotencyKey = optionalString(args.idempotencyKey);
|
|
15242
15251
|
const workflowContext = optionalWorkflowContext(args.workflowContext);
|
|
15243
15252
|
const message = await client.send({
|
|
15253
|
+
...forwardedHops(context?.handling),
|
|
15244
15254
|
target: requiredString(args.target, "target"),
|
|
15245
15255
|
content: requiredString(args.content, "content"),
|
|
15246
15256
|
...delivery ? { delivery } : {},
|
|
@@ -15318,6 +15328,7 @@ var AGENT_COMMANDS = [
|
|
|
15318
15328
|
const targets = Array.isArray(args.targets) ? args.targets.map((t) => requiredString(t, "target")) : [];
|
|
15319
15329
|
return {
|
|
15320
15330
|
responses: await client.fanout({
|
|
15331
|
+
...forwardedHops(context?.handling),
|
|
15321
15332
|
targets,
|
|
15322
15333
|
content: requiredString(args.content, "content"),
|
|
15323
15334
|
...optionalString(args.correlationId) ? { correlationId: optionalString(args.correlationId) } : {},
|
|
@@ -16370,6 +16381,24 @@ function resolveHubCredentials(options = {}) {
|
|
|
16370
16381
|
written: needsPersist === true
|
|
16371
16382
|
};
|
|
16372
16383
|
}
|
|
16384
|
+
function resolveAgentHubAuthToken(env, project) {
|
|
16385
|
+
const envToken = env.KXM_AUTH_TOKEN?.trim();
|
|
16386
|
+
if (envToken) return envToken;
|
|
16387
|
+
const tokens = readHubEnvRecord(env)?.projectTokens;
|
|
16388
|
+
if (!tokens || !Object.hasOwn(tokens, project)) return void 0;
|
|
16389
|
+
return tokens[project]?.trim() || void 0;
|
|
16390
|
+
}
|
|
16391
|
+
var AgentProjectTokenMissingError = class extends Error {
|
|
16392
|
+
code = "project_token_missing";
|
|
16393
|
+
project;
|
|
16394
|
+
constructor(project) {
|
|
16395
|
+
super(
|
|
16396
|
+
`kxm has no project token for project ${project} on this machine. Set KXM_AUTH_TOKEN to that project's token, or add ${project} to the hub KXM_PROJECT_TOKENS (list every existing project too, because that variable replaces the saved map). An agent never uses the hub admin token.`
|
|
16397
|
+
);
|
|
16398
|
+
this.name = "AgentProjectTokenMissingError";
|
|
16399
|
+
this.project = project;
|
|
16400
|
+
}
|
|
16401
|
+
};
|
|
16373
16402
|
|
|
16374
16403
|
// plugins/kxm/src/repo-root.ts
|
|
16375
16404
|
import { existsSync as existsSync5 } from "node:fs";
|
|
@@ -38060,13 +38089,11 @@ function piMeshExtension(pi) {
|
|
|
38060
38089
|
removeMatchingLegacyRecoveryContext();
|
|
38061
38090
|
const purpose = process.env.KXM_AGENT_PURPOSE ?? "General-purpose coding agent";
|
|
38062
38091
|
const model = ctx2.model ? `${ctx2.model.provider}/${ctx2.model.id}` : void 0;
|
|
38063
|
-
let autoStartToken;
|
|
38064
38092
|
try {
|
|
38065
38093
|
const config2 = loadKxmConfig(ctx2.cwd);
|
|
38066
38094
|
if (hubAutoStartMode(config2) === "background") {
|
|
38067
38095
|
const ensured = await ensureHubRunning({ config: config2, cwd: ctx2.cwd });
|
|
38068
38096
|
if (ensured.status === "started") {
|
|
38069
|
-
autoStartToken = ensured.authToken;
|
|
38070
38097
|
ctx2.ui.notify(
|
|
38071
38098
|
`kxm hub started in the background (pid ${ensured.pid}); logs: ${ensured.logPath}` + (ensured.authTokenSource === "generated" ? "; new admin token generated and persisted to user state" : ""),
|
|
38072
38099
|
"info"
|
|
@@ -38078,21 +38105,25 @@ function piMeshExtension(pi) {
|
|
|
38078
38105
|
} catch (error2) {
|
|
38079
38106
|
ctx2.ui.notify(`kxm hub auto-start failed: ${error2 instanceof Error ? error2.message : String(error2)}`, "warning");
|
|
38080
38107
|
}
|
|
38081
|
-
|
|
38082
|
-
|
|
38108
|
+
let hubAuthToken;
|
|
38109
|
+
try {
|
|
38110
|
+
hubAuthToken = resolveAgentHubAuthToken(process.env, project);
|
|
38111
|
+
} catch (error2) {
|
|
38112
|
+
ctx2.ui.notify(`kxm could not read persisted hub credentials: ${error2 instanceof Error ? error2.message : String(error2)}`, "error");
|
|
38113
|
+
await applySessionChrome(ctx2, event, true);
|
|
38114
|
+
return;
|
|
38115
|
+
}
|
|
38083
38116
|
if (!hubAuthToken) {
|
|
38084
|
-
|
|
38085
|
-
|
|
38086
|
-
|
|
38087
|
-
ctx2.ui.notify(`kxm could not read persisted hub credentials: ${error2 instanceof Error ? error2.message : String(error2)}`, "warning");
|
|
38088
|
-
}
|
|
38117
|
+
ctx2.ui.notify(new AgentProjectTokenMissingError(project).message, "error");
|
|
38118
|
+
await applySessionChrome(ctx2, event, true);
|
|
38119
|
+
return;
|
|
38089
38120
|
}
|
|
38090
38121
|
client = new HubClient({
|
|
38091
38122
|
serverUrl,
|
|
38092
38123
|
name,
|
|
38093
38124
|
purpose,
|
|
38094
38125
|
project,
|
|
38095
|
-
|
|
38126
|
+
authToken: hubAuthToken,
|
|
38096
38127
|
...model ? { model } : {}
|
|
38097
38128
|
});
|
|
38098
38129
|
notify = (message, type) => ctx2.ui.notify(message, type);
|
|
@@ -38248,9 +38279,10 @@ function piMeshExtension(pi) {
|
|
|
38248
38279
|
if (!policy.allowed) {
|
|
38249
38280
|
throw new Error(`tool_policy_denied: ${policy.detail ?? policy.error}`);
|
|
38250
38281
|
}
|
|
38282
|
+
const handling = activeInbound ? [activeInbound] : [];
|
|
38251
38283
|
return result(
|
|
38252
38284
|
await workflowCall(
|
|
38253
|
-
() => cmd.execute(requireClient(), params ?? {}, { signal })
|
|
38285
|
+
() => cmd.execute(requireClient(), params ?? {}, { signal, handling })
|
|
38254
38286
|
)
|
|
38255
38287
|
);
|
|
38256
38288
|
}
|
|
@@ -15999,7 +15999,9 @@ var HubClient = class {
|
|
|
15999
15999
|
options.workflowContext
|
|
16000
16000
|
)
|
|
16001
16001
|
} : {},
|
|
16002
|
-
...options.ttlMs ? { ttlMs: options.ttlMs } : {}
|
|
16002
|
+
...options.ttlMs ? { ttlMs: options.ttlMs } : {},
|
|
16003
|
+
...options.hops !== void 0 ? { hops: options.hops } : {},
|
|
16004
|
+
...options.maxHops !== void 0 ? { maxHops: options.maxHops } : {}
|
|
16003
16005
|
});
|
|
16004
16006
|
const completed = await this.awaitResponse(
|
|
16005
16007
|
message.id,
|
|
@@ -16398,6 +16400,13 @@ function defaultProjectName(cwd, env = process.env) {
|
|
|
16398
16400
|
import { chmodSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
16399
16401
|
import { homedir as homedir2 } from "node:os";
|
|
16400
16402
|
import { dirname as dirname2, join as join3, resolve as resolve2 } from "node:path";
|
|
16403
|
+
function forwardedHops(handling) {
|
|
16404
|
+
if (!handling?.length) return void 0;
|
|
16405
|
+
return {
|
|
16406
|
+
hops: Math.max(...handling.map((message) => message.hops)) + 1,
|
|
16407
|
+
maxHops: Math.min(...handling.map((message) => message.maxHops))
|
|
16408
|
+
};
|
|
16409
|
+
}
|
|
16401
16410
|
function requiredString(value, name) {
|
|
16402
16411
|
if (typeof value !== "string" || value.trim() === "") throw new Error(`${name} is required`);
|
|
16403
16412
|
return value.trim();
|
|
@@ -16527,12 +16536,13 @@ var AGENT_COMMANDS = [
|
|
|
16527
16536
|
required: ["target", "content"],
|
|
16528
16537
|
additionalProperties: false
|
|
16529
16538
|
},
|
|
16530
|
-
async execute(client, args) {
|
|
16539
|
+
async execute(client, args, context) {
|
|
16531
16540
|
const delivery = optionalString(args.delivery);
|
|
16532
16541
|
const correlationId = optionalString(args.correlationId);
|
|
16533
16542
|
const idempotencyKey = optionalString(args.idempotencyKey);
|
|
16534
16543
|
const workflowContext = optionalWorkflowContext(args.workflowContext);
|
|
16535
16544
|
const message = await client.send({
|
|
16545
|
+
...forwardedHops(context?.handling),
|
|
16536
16546
|
target: requiredString(args.target, "target"),
|
|
16537
16547
|
content: requiredString(args.content, "content"),
|
|
16538
16548
|
...delivery ? { delivery } : {},
|
|
@@ -16610,6 +16620,7 @@ var AGENT_COMMANDS = [
|
|
|
16610
16620
|
const targets = Array.isArray(args.targets) ? args.targets.map((t) => requiredString(t, "target")) : [];
|
|
16611
16621
|
return {
|
|
16612
16622
|
responses: await client.fanout({
|
|
16623
|
+
...forwardedHops(context?.handling),
|
|
16613
16624
|
targets,
|
|
16614
16625
|
content: requiredString(args.content, "content"),
|
|
16615
16626
|
...optionalString(args.correlationId) ? { correlationId: optionalString(args.correlationId) } : {},
|
|
@@ -17285,7 +17296,7 @@ function sessionTokenFixHint(policy) {
|
|
|
17285
17296
|
}
|
|
17286
17297
|
|
|
17287
17298
|
// plugins/kxm/src/mcp-server.ts
|
|
17288
|
-
var VERSION = "0.7.
|
|
17299
|
+
var VERSION = "0.7.98";
|
|
17289
17300
|
var CONFIGURE_PLUGIN = "/plugin configure kxm@kxm";
|
|
17290
17301
|
var inbox = /* @__PURE__ */ new Map();
|
|
17291
17302
|
var notifiedInbox = /* @__PURE__ */ new Set();
|
|
@@ -17442,7 +17453,12 @@ mcp.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
|
17442
17453
|
const cmd = AGENT_COMMANDS_MAP.get(request.params.name);
|
|
17443
17454
|
if (!cmd) throw new Error(`unknown tool: ${request.params.name}`);
|
|
17444
17455
|
const args = asRecord2(request.params.arguments);
|
|
17445
|
-
const result = await cmd.execute(client, args, {
|
|
17456
|
+
const result = await cmd.execute(client, args, {
|
|
17457
|
+
signal: extra.signal,
|
|
17458
|
+
inbox,
|
|
17459
|
+
notifiedInbox,
|
|
17460
|
+
handling: [...inbox.values()]
|
|
17461
|
+
});
|
|
17446
17462
|
return textResult(result);
|
|
17447
17463
|
} catch (error2) {
|
|
17448
17464
|
return {
|