@kontextmind/kxm 0.7.95 → 0.7.97
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/README.md +39 -9
- package/CHANGELOG.md +23 -1
- package/README.md +147 -257
- package/SECURITY.md +21 -12
- package/docs/README.md +133 -54
- package/docs/adr/ADR-0002-browser-automation-steel-doks.md +24 -18
- package/docs/adr/ADR-0003-sqlite-only-store.md +100 -0
- package/docs/adr/ADR-0004-edge-identity-authentik.md +99 -0
- package/docs/adr/README.md +33 -0
- package/docs/concepts/architecture.md +262 -0
- package/docs/concepts/data-and-storage.md +194 -0
- package/docs/concepts/trust-model.md +153 -0
- package/docs/contracts/README.md +22 -14
- package/docs/contracts/effects-and-recovery.md +3 -0
- package/docs/contracts/migration.md +2 -2
- package/docs/contracts/routing.md +6 -5
- package/docs/contributing/assignment-runner.md +388 -0
- package/docs/contributing/ci-and-release.md +231 -0
- package/docs/contributing/development.md +362 -0
- package/docs/contributing/harness-routing-internals.md +192 -0
- package/docs/{packages.md → contributing/packages.md} +13 -15
- package/docs/{skills → contributing}/repo-work-delivery.md +20 -21
- package/docs/contributing/test-matrix.md +208 -0
- package/docs/{tui-components.md → contributing/tui-components.md} +30 -22
- package/docs/contributing/writing-docs.md +340 -0
- package/docs/glossary.md +471 -0
- package/docs/guides/agent-skills.md +137 -0
- package/docs/guides/browser-automation.md +160 -0
- package/docs/guides/context-and-memory.md +352 -0
- package/docs/guides/continuous-improvement.md +228 -0
- package/docs/guides/governed-skills.md +173 -0
- package/docs/guides/nous-providers.md +186 -0
- package/docs/guides/peer-messaging.md +304 -0
- package/docs/guides/pi-workers.md +219 -0
- package/docs/guides/provenance-gates.md +313 -0
- package/docs/guides/webhook-workflows.md +399 -0
- package/docs/kb/how-credentials-retrieved-safely.md +38 -12
- package/docs/kb/how-to-capture-and-annotate-section.md +15 -13
- package/docs/kb/how-to-connect-playwright-to-steel.md +16 -11
- package/docs/kb/how-to-recover-expired-session-or-orphan.md +26 -16
- package/docs/kb/how-to-resume-after-mfa.md +19 -11
- package/docs/kb/how-to-take-over-session.md +17 -13
- package/docs/kb/why-authentication-disappeared.md +22 -14
- package/docs/kb/why-automation-opened-different-browser.md +23 -14
- package/docs/kb/why-session-viewer-cannot-control.md +13 -12
- package/docs/operations/backup-and-restore.md +248 -0
- package/docs/operations/deploy.md +307 -0
- package/docs/operations/monitoring.md +209 -0
- package/docs/operations/runtime-sync.md +192 -0
- package/docs/operations/troubleshooting.md +266 -0
- package/docs/operations/upgrade.md +124 -0
- package/docs/prompts/browser-annotate-feedback.md +7 -7
- package/docs/prompts/browser-diagnose-recover.md +11 -10
- package/docs/prompts/browser-explore.md +7 -7
- package/docs/prompts/browser-repro-fix.md +7 -7
- package/docs/prompts/browser-start.md +12 -11
- package/docs/prompts/browser-takeover.md +8 -8
- package/docs/{cli-reference.md → reference/cli-reference.md} +88 -46
- package/docs/{config-reference.md → reference/config-reference.md} +159 -148
- package/docs/reference/configuration.md +299 -0
- package/docs/reference/harness-routing.md +508 -0
- package/docs/reference/http-api.md +203 -0
- package/docs/reference/tools.md +370 -0
- package/docs/{workflow-guide.md → reference/workflow-catalog.md} +92 -153
- package/docs/reference/workflow-definitions.md +286 -0
- package/docs/start/first-workflow.md +287 -0
- package/docs/start/install.md +146 -0
- package/docs/start/quickstart-claude-code.md +405 -0
- package/docs/start/quickstart-pi.md +213 -0
- package/docs/templates/README.md +78 -73
- package/docs/templates/adr.md +13 -13
- package/docs/templates/architecture.md +55 -71
- package/docs/templates/bug-fix.md +13 -16
- package/docs/templates/feature.md +14 -19
- package/docs/templates/handoff.md +44 -46
- package/docs/templates/postmortem.md +30 -43
- package/docs/templates/research.md +15 -20
- package/docs/templates/review.md +49 -50
- package/docs/templates/runbook.md +38 -30
- package/docs/templates/test-plan.md +16 -23
- package/docs/templates/test-report.md +14 -17
- package/examples/README.md +9 -5
- package/examples/provenance-workflow.json +1 -1
- package/examples/webhook-workflows/jira-development.json +59 -0
- package/examples/webhook-workflows/jira-issue-updated.json +12 -0
- package/examples/workflow-signal.ts +4 -5
- package/package.json +1 -1
- package/packages/core/tui/README.md +1 -1
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/README.md +31 -32
- package/plugins/kxm/dist/claude-hook.js +11 -1
- package/plugins/kxm/dist/cli.js +164 -79
- package/plugins/kxm/dist/client.js +3 -1
- package/plugins/kxm/dist/core.js +11 -1
- package/plugins/kxm/dist/extension.js +45 -13
- package/plugins/kxm/dist/mcp-server.js +20 -4
- package/plugins/kxm/dist/runtime-supervisor.js +1 -3
- package/plugins/kxm/dist/runtime.js +18 -4
- package/plugins/kxm/dist/server.js +115 -20
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/kxm/references/protocol.md +3 -1
- package/plugins/kxm/skills/kxm-browser-auth/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-browser-diagnostics/SKILL.md +5 -5
- package/plugins/kxm/skills/kxm-browser-explore/SKILL.md +2 -2
- package/plugins/kxm/skills/kxm-browser-session/SKILL.md +10 -13
- package/plugins/kxm/skills/kxm-browser-takeover/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-browser-verify/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-context-memory/SKILL.md +13 -4
- package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +3 -1
- package/plugins/kxm/skills/kxm-mind-setup/SKILL.md +2 -1
- package/plugins/kxm/skills/kxm-project-setup/SKILL.md +31 -54
- package/plugins/kxm/skills/kxm-projects/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-protocol/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-routing-improve/SKILL.md +15 -7
- package/plugins/kxm/skills/kxm-runs/SKILL.md +11 -5
- package/plugins/kxm/skills/kxm-session/SKILL.md +1 -1
- package/plugins/kxm/skills/kxm-tasks/SKILL.md +9 -7
- package/plugins/kxm/skills/kxm-workflow/SKILL.md +10 -2
- package/plugins/kxm/src/cli/system.ts +1 -1
- package/plugins/kxm/src/cli/workflows.ts +12 -7
- package/plugins/kxm/src/cli.ts +22 -8
- package/plugins/kxm/src/client.ts +4 -0
- package/plugins/kxm/src/commands.ts +23 -1
- package/plugins/kxm/src/extension.ts +20 -14
- package/plugins/kxm/src/github-watch.ts +8 -5
- 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 +1 -1
- package/plugins/kxm/src/mcp-server.ts +9 -2
- package/plugins/kxm/src/modes.ts +1 -1
- package/plugins/kxm/src/runtime-store.ts +23 -0
- package/plugins/kxm/src/workflow.ts +70 -1
- package/schemas/README.md +1 -1
- package/scripts/smoke-multi-pi.mjs +5 -1
- package/docs/agent-communication-envelopes-and-gates.md +0 -553
- package/docs/agent-skills.md +0 -198
- package/docs/architecture.md +0 -245
- package/docs/assignment-runner.md +0 -264
- package/docs/browser-automation.md +0 -139
- package/docs/configuration.md +0 -437
- package/docs/continuous-improvement.md +0 -226
- package/docs/getting-started.md +0 -277
- package/docs/harness-routing.md +0 -616
- package/docs/kb/qa-authentik-authentication.md +0 -97
- package/docs/kb/qa-extension-install-and-hub-bootstrap.md +0 -85
- package/docs/kb/qa-hub-on-a-public-host.md +0 -48
- package/docs/kb/qa-sqlite-vs-duckdb.md +0 -35
- package/docs/kb/qa-what-the-hub-stores.md +0 -64
- package/docs/kxm-handbook.md +0 -1181
- package/docs/operations.md +0 -510
- package/docs/operator-pi-packages.md +0 -67
- package/docs/provenance-gates.md +0 -295
- package/docs/skills.md +0 -47
- package/docs/test-matrix.md +0 -132
- package/docs/troubleshooting.md +0 -322
- package/docs/webhook-workflows.md +0 -240
package/plugins/kxm/dist/core.js
CHANGED
|
@@ -981,6 +981,13 @@ var HubHttpError = class extends Error {
|
|
|
981
981
|
};
|
|
982
982
|
|
|
983
983
|
// plugins/kxm/src/commands.ts
|
|
984
|
+
function forwardedHops(handling) {
|
|
985
|
+
if (!handling?.length) return void 0;
|
|
986
|
+
return {
|
|
987
|
+
hops: Math.max(...handling.map((message) => message.hops)) + 1,
|
|
988
|
+
maxHops: Math.min(...handling.map((message) => message.maxHops))
|
|
989
|
+
};
|
|
990
|
+
}
|
|
984
991
|
function requiredString(value, name) {
|
|
985
992
|
if (typeof value !== "string" || value.trim() === "") throw new Error(`${name} is required`);
|
|
986
993
|
return value.trim();
|
|
@@ -1110,12 +1117,13 @@ var AGENT_COMMANDS = [
|
|
|
1110
1117
|
required: ["target", "content"],
|
|
1111
1118
|
additionalProperties: false
|
|
1112
1119
|
},
|
|
1113
|
-
async execute(client, args) {
|
|
1120
|
+
async execute(client, args, context) {
|
|
1114
1121
|
const delivery = optionalString2(args.delivery);
|
|
1115
1122
|
const correlationId = optionalString2(args.correlationId);
|
|
1116
1123
|
const idempotencyKey = optionalString2(args.idempotencyKey);
|
|
1117
1124
|
const workflowContext = optionalWorkflowContext(args.workflowContext);
|
|
1118
1125
|
const message = await client.send({
|
|
1126
|
+
...forwardedHops(context?.handling),
|
|
1119
1127
|
target: requiredString(args.target, "target"),
|
|
1120
1128
|
content: requiredString(args.content, "content"),
|
|
1121
1129
|
...delivery ? { delivery } : {},
|
|
@@ -1193,6 +1201,7 @@ var AGENT_COMMANDS = [
|
|
|
1193
1201
|
const targets = Array.isArray(args.targets) ? args.targets.map((t) => requiredString(t, "target")) : [];
|
|
1194
1202
|
return {
|
|
1195
1203
|
responses: await client.fanout({
|
|
1204
|
+
...forwardedHops(context?.handling),
|
|
1196
1205
|
targets,
|
|
1197
1206
|
content: requiredString(args.content, "content"),
|
|
1198
1207
|
...optionalString2(args.correlationId) ? { correlationId: optionalString2(args.correlationId) } : {},
|
|
@@ -2156,6 +2165,7 @@ export {
|
|
|
2156
2165
|
enforceToolPolicy,
|
|
2157
2166
|
evaluateCircuitBreaker,
|
|
2158
2167
|
formatRoutingReport,
|
|
2168
|
+
forwardedHops,
|
|
2159
2169
|
gateWorker,
|
|
2160
2170
|
generateRoutingReport,
|
|
2161
2171
|
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.97";
|
|
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 {
|
|
@@ -17440,9 +17440,6 @@ import { createHash as createHash4, randomUUID } from "node:crypto";
|
|
|
17440
17440
|
import { existsSync as existsSync5, lstatSync as lstatSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, realpathSync as realpathSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
17441
17441
|
import { dirname as dirname5, join as join5, resolve as resolve4 } from "node:path";
|
|
17442
17442
|
|
|
17443
|
-
// plugins/kxm/src/bindings.ts
|
|
17444
|
-
import { homedir } from "node:os";
|
|
17445
|
-
|
|
17446
17443
|
// plugins/kxm/src/sqlite.ts
|
|
17447
17444
|
import { createRequire } from "node:module";
|
|
17448
17445
|
var requireFromHere = createRequire(import.meta.url);
|
|
@@ -17483,6 +17480,7 @@ var DatabaseSync = class {
|
|
|
17483
17480
|
};
|
|
17484
17481
|
|
|
17485
17482
|
// plugins/kxm/src/bindings.ts
|
|
17483
|
+
import { homedir } from "node:os";
|
|
17486
17484
|
import { dirname as dirname3, isAbsolute as isAbsolute2, join as join3, parse, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
|
|
17487
17485
|
var MAX_BINDING_RECORD_BYTES = 256 * 1024;
|
|
17488
17486
|
var BINDING_LABEL = "Runtime-local repository bindings";
|
|
@@ -17610,9 +17610,6 @@ import { createHash as createHash5, randomUUID } from "node:crypto";
|
|
|
17610
17610
|
import { existsSync as existsSync5, lstatSync as lstatSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, realpathSync as realpathSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
17611
17611
|
import { dirname as dirname5, join as join5, resolve as resolve4 } from "node:path";
|
|
17612
17612
|
|
|
17613
|
-
// plugins/kxm/src/bindings.ts
|
|
17614
|
-
import { homedir } from "node:os";
|
|
17615
|
-
|
|
17616
17613
|
// plugins/kxm/src/sqlite.ts
|
|
17617
17614
|
import { createRequire } from "node:module";
|
|
17618
17615
|
var requireFromHere = createRequire(import.meta.url);
|
|
@@ -17653,6 +17650,7 @@ var DatabaseSync = class {
|
|
|
17653
17650
|
};
|
|
17654
17651
|
|
|
17655
17652
|
// plugins/kxm/src/bindings.ts
|
|
17653
|
+
import { homedir } from "node:os";
|
|
17656
17654
|
import { dirname as dirname3, isAbsolute as isAbsolute2, join as join3, parse, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
|
|
17657
17655
|
var MAX_BINDING_RECORD_BYTES = 256 * 1024;
|
|
17658
17656
|
var BINDING_LABEL = "Runtime-local repository bindings";
|
|
@@ -18555,6 +18553,20 @@ function projectRuntimeKey(projectRoot) {
|
|
|
18555
18553
|
const folded = process.platform === "win32" ? canonical.toLocaleLowerCase("en-US") : canonical;
|
|
18556
18554
|
return createHash5("sha256").update(folded, "utf8").digest("hex").slice(0, 24);
|
|
18557
18555
|
}
|
|
18556
|
+
function kxmProjectRunEventsPath(projectRoot, env) {
|
|
18557
|
+
return join5(kxmRuntimePaths({ env }).projectsDir, projectRuntimeKey(projectRoot), "run-events.db");
|
|
18558
|
+
}
|
|
18559
|
+
function projectRuntimeOwnsRun(projectRoot, runId, env) {
|
|
18560
|
+
const path = kxmProjectRunEventsPath(projectRoot, env);
|
|
18561
|
+
if (!existsSync5(path)) return false;
|
|
18562
|
+
const database = new DatabaseSync(path, { readOnly: true });
|
|
18563
|
+
try {
|
|
18564
|
+
database.exec("PRAGMA busy_timeout = 5000");
|
|
18565
|
+
return database.prepare("SELECT 1 FROM runs WHERE run_id = ?").get(runId) !== void 0;
|
|
18566
|
+
} finally {
|
|
18567
|
+
database.close();
|
|
18568
|
+
}
|
|
18569
|
+
}
|
|
18558
18570
|
var KXM_REGISTRY_SCHEMA_VERSION = 1;
|
|
18559
18571
|
var REGISTRY_TABLES = {
|
|
18560
18572
|
supervisor: ["singleton_id", "runtime_id", "pid", "port", "token_hash", "started_at", "heartbeat_at", "state"],
|
|
@@ -32067,7 +32079,7 @@ var DEFAULT_MODES_CONFIG = Object.freeze({
|
|
|
32067
32079
|
browser: {
|
|
32068
32080
|
description: "Web application exploration, screenshotting, and UI testing",
|
|
32069
32081
|
baseTools: ["read", "bash"],
|
|
32070
|
-
contextFiles: ["docs/browser-automation.md"],
|
|
32082
|
+
contextFiles: ["docs/guides/browser-automation.md"],
|
|
32071
32083
|
thinkingLevel: "medium",
|
|
32072
32084
|
model: "grok/grok-4.6"
|
|
32073
32085
|
}
|
|
@@ -33023,6 +33035,7 @@ export {
|
|
|
33023
33035
|
kxmMonotonicNs,
|
|
33024
33036
|
kxmPolicyRevisions,
|
|
33025
33037
|
kxmProjectAdmissionLimits,
|
|
33038
|
+
kxmProjectRunEventsPath,
|
|
33026
33039
|
kxmRunRevisionDrift,
|
|
33027
33040
|
kxmRuntimePaths,
|
|
33028
33041
|
kxmRuntimeRequest,
|
|
@@ -33062,6 +33075,7 @@ export {
|
|
|
33062
33075
|
probeHarnessesForModelAsync,
|
|
33063
33076
|
projectKxmRunReadOnly,
|
|
33064
33077
|
projectRuntimeKey,
|
|
33078
|
+
projectRuntimeOwnsRun,
|
|
33065
33079
|
pruneSocketDir,
|
|
33066
33080
|
readKxmRunStatus,
|
|
33067
33081
|
readKxmSupervisorToken,
|
|
@@ -14779,7 +14779,7 @@ var require_dist = __commonJS({
|
|
|
14779
14779
|
});
|
|
14780
14780
|
|
|
14781
14781
|
// plugins/kxm/src/hub.ts
|
|
14782
|
-
import { createHash as createHash5, createHmac } from "node:crypto";
|
|
14782
|
+
import { createHash as createHash5, createHmac as createHmac2 } from "node:crypto";
|
|
14783
14783
|
import { existsSync as existsSync6 } from "node:fs";
|
|
14784
14784
|
import { createServer } from "node:http";
|
|
14785
14785
|
import { isIP } from "node:net";
|
|
@@ -15650,7 +15650,7 @@ function workflowScopeExtras(operation, assignedCoordinatorName) {
|
|
|
15650
15650
|
import { createHash as createHash3, randomUUID as randomUUID2, timingSafeEqual } from "node:crypto";
|
|
15651
15651
|
|
|
15652
15652
|
// plugins/kxm/src/workflow.ts
|
|
15653
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
15653
|
+
import { createHash as createHash2, createHmac } from "node:crypto";
|
|
15654
15654
|
|
|
15655
15655
|
// plugins/kxm/src/relevance.ts
|
|
15656
15656
|
var RELEVANCE_STOPWORDS = Object.freeze(/* @__PURE__ */ new Set([
|
|
@@ -16572,6 +16572,30 @@ function canonicalWorkflowDefinitionJson(definition) {
|
|
|
16572
16572
|
function workflowDefinitionHash(definition) {
|
|
16573
16573
|
return createHash2("sha256").update(canonicalWorkflowDefinitionJson(definition), "utf8").digest("hex");
|
|
16574
16574
|
}
|
|
16575
|
+
var WORKFLOW_WEBHOOK_SIGNATURE_VERSION = "kxm-webhook-v1";
|
|
16576
|
+
var WORKFLOW_WEBHOOK_MAX_SKEW_SECONDS = 300;
|
|
16577
|
+
function workflowWebhookSignedMaterial(scope, timestamp, deliveryId, body) {
|
|
16578
|
+
const fields = [
|
|
16579
|
+
WORKFLOW_WEBHOOK_SIGNATURE_VERSION,
|
|
16580
|
+
scope.runId === void 0 ? "start" : "signal",
|
|
16581
|
+
timestamp,
|
|
16582
|
+
deliveryId,
|
|
16583
|
+
scope.definitionId,
|
|
16584
|
+
scope.runId ?? "",
|
|
16585
|
+
scope.signalKey ?? ""
|
|
16586
|
+
];
|
|
16587
|
+
if (fields.some((field) => /[\r\n]/.test(field))) {
|
|
16588
|
+
throw new ProtocolError(400, "webhook signature fields must not contain line breaks", "webhook_signature_field_invalid");
|
|
16589
|
+
}
|
|
16590
|
+
return Buffer.concat([
|
|
16591
|
+
Buffer.from(`${fields.join("\n")}
|
|
16592
|
+
`, "utf8"),
|
|
16593
|
+
typeof body === "string" ? Buffer.from(body, "utf8") : Buffer.from(body)
|
|
16594
|
+
]);
|
|
16595
|
+
}
|
|
16596
|
+
function workflowWebhookSignature(secret, scope, timestamp, deliveryId, body) {
|
|
16597
|
+
return `sha256=${createHmac("sha256", secret).update(workflowWebhookSignedMaterial(scope, timestamp, deliveryId, body)).digest("hex")}`;
|
|
16598
|
+
}
|
|
16575
16599
|
function resolveOutcomeRule(stage, outcomeKey) {
|
|
16576
16600
|
const raw = stage.on?.[outcomeKey];
|
|
16577
16601
|
return raw === void 0 ? void 0 : normalizeOutcomeValue(raw, `stage ${stage.id} on.${outcomeKey}`);
|
|
@@ -16895,6 +16919,13 @@ var HubHttpError = class extends Error {
|
|
|
16895
16919
|
};
|
|
16896
16920
|
|
|
16897
16921
|
// plugins/kxm/src/commands.ts
|
|
16922
|
+
function forwardedHops(handling) {
|
|
16923
|
+
if (!handling?.length) return void 0;
|
|
16924
|
+
return {
|
|
16925
|
+
hops: Math.max(...handling.map((message) => message.hops)) + 1,
|
|
16926
|
+
maxHops: Math.min(...handling.map((message) => message.maxHops))
|
|
16927
|
+
};
|
|
16928
|
+
}
|
|
16898
16929
|
function requiredString(value, name) {
|
|
16899
16930
|
if (typeof value !== "string" || value.trim() === "") throw new Error(`${name} is required`);
|
|
16900
16931
|
return value.trim();
|
|
@@ -17024,12 +17055,13 @@ var AGENT_COMMANDS = [
|
|
|
17024
17055
|
required: ["target", "content"],
|
|
17025
17056
|
additionalProperties: false
|
|
17026
17057
|
},
|
|
17027
|
-
async execute(client, args) {
|
|
17058
|
+
async execute(client, args, context) {
|
|
17028
17059
|
const delivery = optionalString2(args.delivery);
|
|
17029
17060
|
const correlationId = optionalString2(args.correlationId);
|
|
17030
17061
|
const idempotencyKey = optionalString2(args.idempotencyKey);
|
|
17031
17062
|
const workflowContext = optionalWorkflowContext(args.workflowContext);
|
|
17032
17063
|
const message = await client.send({
|
|
17064
|
+
...forwardedHops(context?.handling),
|
|
17033
17065
|
target: requiredString(args.target, "target"),
|
|
17034
17066
|
content: requiredString(args.content, "content"),
|
|
17035
17067
|
...delivery ? { delivery } : {},
|
|
@@ -17107,6 +17139,7 @@ var AGENT_COMMANDS = [
|
|
|
17107
17139
|
const targets = Array.isArray(args.targets) ? args.targets.map((t) => requiredString(t, "target")) : [];
|
|
17108
17140
|
return {
|
|
17109
17141
|
responses: await client.fanout({
|
|
17142
|
+
...forwardedHops(context?.handling),
|
|
17110
17143
|
targets,
|
|
17111
17144
|
content: requiredString(args.content, "content"),
|
|
17112
17145
|
...optionalString2(args.correlationId) ? { correlationId: optionalString2(args.correlationId) } : {},
|
|
@@ -21089,20 +21122,59 @@ data: ${JSON.stringify({ type: "ops", project, topic, at: nowIso() })}
|
|
|
21089
21122
|
const candidate = payload.webhookEvent ?? payload.event;
|
|
21090
21123
|
return typeof candidate === "string" && candidate.trim() ? candidate.trim() : void 0;
|
|
21091
21124
|
}
|
|
21092
|
-
function
|
|
21093
|
-
const signature = request.headers["x-hub-signature-256"] ?? request.headers["x-hub-signature"];
|
|
21094
|
-
if (typeof signature !== "string") {
|
|
21095
|
-
throw new ProtocolError(401, "webhook signature is required", "webhook_signature_missing");
|
|
21096
|
-
}
|
|
21125
|
+
function requireSha256Signature(signature) {
|
|
21097
21126
|
const separator = signature.indexOf("=");
|
|
21098
21127
|
const algorithm = separator > 0 ? signature.slice(0, separator).toLowerCase() : "";
|
|
21099
21128
|
if (algorithm !== "sha256") {
|
|
21100
21129
|
throw new ProtocolError(401, "webhook signature must use sha256", "webhook_signature_unsupported");
|
|
21101
21130
|
}
|
|
21102
|
-
|
|
21131
|
+
}
|
|
21132
|
+
function verifyProviderWebhookSignature(request, body, definition) {
|
|
21133
|
+
if (definition.source === "generic") {
|
|
21134
|
+
throw new ProtocolError(
|
|
21135
|
+
401,
|
|
21136
|
+
"generic workflow webhooks require x-kxm-signature over the timestamp, delivery ID, definition, and body",
|
|
21137
|
+
"webhook_signature_missing"
|
|
21138
|
+
);
|
|
21139
|
+
}
|
|
21140
|
+
const signature = request.headers["x-hub-signature-256"] ?? request.headers["x-hub-signature"];
|
|
21141
|
+
if (typeof signature !== "string") {
|
|
21142
|
+
throw new ProtocolError(401, "webhook signature is required", "webhook_signature_missing");
|
|
21143
|
+
}
|
|
21144
|
+
requireSha256Signature(signature);
|
|
21145
|
+
const expected = `sha256=${createHmac2("sha256", definition.secret).update(body).digest("hex")}`;
|
|
21103
21146
|
if (!safeTokenEqual(signature, expected)) {
|
|
21104
21147
|
throw new ProtocolError(401, "webhook signature is invalid", "webhook_signature_invalid");
|
|
21105
21148
|
}
|
|
21149
|
+
const deliveryHeader = definition.source === "jira" ? request.headers["x-atlassian-webhook-identifier"] : request.headers["x-github-delivery"];
|
|
21150
|
+
return requireString(deliveryHeader, "webhook delivery identifier", { max: 128 });
|
|
21151
|
+
}
|
|
21152
|
+
function verifyKxmWebhookSignature(request, body, secret, scope) {
|
|
21153
|
+
const signature = request.headers["x-kxm-signature"];
|
|
21154
|
+
if (typeof signature !== "string") {
|
|
21155
|
+
throw new ProtocolError(
|
|
21156
|
+
401,
|
|
21157
|
+
"KXM webhooks require x-kxm-signature over the timestamp, delivery ID, route, and body",
|
|
21158
|
+
"webhook_signature_missing"
|
|
21159
|
+
);
|
|
21160
|
+
}
|
|
21161
|
+
requireSha256Signature(signature);
|
|
21162
|
+
const timestamp = request.headers["x-kxm-timestamp"];
|
|
21163
|
+
if (typeof timestamp !== "string" || !/^[0-9]{1,12}$/.test(timestamp)) {
|
|
21164
|
+
throw new ProtocolError(401, "x-kxm-timestamp must be Unix seconds", "webhook_timestamp_invalid");
|
|
21165
|
+
}
|
|
21166
|
+
const deliveryId = requireString(request.headers["x-kxm-delivery-id"], "webhook delivery identifier", { max: 128 });
|
|
21167
|
+
if (!safeTokenEqual(signature, workflowWebhookSignature(secret, scope, timestamp, deliveryId, body))) {
|
|
21168
|
+
throw new ProtocolError(401, "webhook signature is invalid", "webhook_signature_invalid");
|
|
21169
|
+
}
|
|
21170
|
+
if (Math.abs(Date.now() / 1e3 - Number(timestamp)) > WORKFLOW_WEBHOOK_MAX_SKEW_SECONDS) {
|
|
21171
|
+
throw new ProtocolError(
|
|
21172
|
+
401,
|
|
21173
|
+
`webhook timestamp is outside the ${WORKFLOW_WEBHOOK_MAX_SKEW_SECONDS}-second window; re-sign at send time`,
|
|
21174
|
+
"webhook_timestamp_expired"
|
|
21175
|
+
);
|
|
21176
|
+
}
|
|
21177
|
+
return deliveryId;
|
|
21106
21178
|
}
|
|
21107
21179
|
function workflowPrompt(definition, runId, payload) {
|
|
21108
21180
|
const rendered = renderWorkflowPrompt(definition.promptTemplate, payload);
|
|
@@ -21125,7 +21197,7 @@ data: ${JSON.stringify({ type: "ops", project, topic, at: nowIso() })}
|
|
|
21125
21197
|
stageList,
|
|
21126
21198
|
"",
|
|
21127
21199
|
`At every stage, record material knowledge with kxm_workflow_record in one of these categories: ${JOURNAL_CATEGORIES.join(", ")}. Pass the stageId the entry belongs to; the hub binds the attempt and, when you omit area, uses the stage's declared area.`,
|
|
21128
|
-
"Keep
|
|
21200
|
+
"Keep reviewed configuration as YAML directly under .kxm/ (such as .kxm/project.yaml and .kxm/workflows/), logs in .kxm/logs, and durable workflow artifacts in .kxm/assets. Never create .kxm/config, which KXM refuses, and never commit runtime logs, .kxm/state, or secrets.",
|
|
21129
21201
|
"Complete each stage with kxm_workflow_checkpoint. Supply evidence as an object whose keys exactly match the stage's required evidence keys. Unrelated keys never satisfy a requirement. A warning or failure must be corrected and checkpointed again until it passes or the attempt limit is reached.",
|
|
21130
21202
|
"For a peer-evidence requirement, send or fan out with workflowContext containing this run ID, the exact stage ID, requirement key, and current 1-based attempt. At checkpoint, cite only the returned message IDs under evidenceRefs; the hub derives producer and reply provenance.",
|
|
21131
21203
|
"When an external system must finish asynchronously, call kxm_workflow_wait with a stable signal key and any already-verified keyed evidence. That evidence is accumulated with the signed callback before the stage can pass; then settle the turn.",
|
|
@@ -21463,14 +21535,17 @@ data: ${JSON.stringify({ type: "ops", project, topic, at: nowIso() })}
|
|
|
21463
21535
|
const definition = webhookWorkflows2.get(definitionId);
|
|
21464
21536
|
if (!definition) throw new ProtocolError(404, "webhook workflow not found", "webhook_not_found");
|
|
21465
21537
|
const rawBody = await readBody(request);
|
|
21466
|
-
|
|
21538
|
+
const deliveryId = verifyKxmWebhookSignature(
|
|
21539
|
+
request,
|
|
21540
|
+
rawBody,
|
|
21541
|
+
definition.signalSecret ?? definition.secret,
|
|
21542
|
+
{ definitionId: definition.id, runId, signalKey }
|
|
21543
|
+
);
|
|
21467
21544
|
const body = parseJsonBody(rawBody);
|
|
21468
21545
|
const run = workflowRuns.get(runId);
|
|
21469
21546
|
if (!run || run.definitionId !== definition.id) {
|
|
21470
21547
|
throw new ProtocolError(404, "workflow run not found", "workflow_not_found");
|
|
21471
21548
|
}
|
|
21472
|
-
const deliveryHeader = request.headers["x-atlassian-webhook-identifier"] ?? request.headers["x-github-delivery"] ?? request.headers["x-kxm-delivery-id"];
|
|
21473
|
-
const deliveryId = requireString(deliveryHeader, "webhook delivery identifier", { max: 128 });
|
|
21474
21549
|
const payloadHash = createHash5("sha256").update(rawBody).digest("hex");
|
|
21475
21550
|
const existingReceipt = run.signalReceipts?.find((receipt2) => receipt2.deliveryId === deliveryId);
|
|
21476
21551
|
if (existingReceipt) {
|
|
@@ -21616,7 +21691,8 @@ data: ${JSON.stringify({ type: "ops", project, topic, at: nowIso() })}
|
|
|
21616
21691
|
const definition = webhookWorkflows2.get(definitionId);
|
|
21617
21692
|
if (!definition) throw new ProtocolError(404, "webhook workflow not found", "webhook_not_found");
|
|
21618
21693
|
const rawBody = await readBody(request);
|
|
21619
|
-
|
|
21694
|
+
const bodyOnlySignature = request.headers["x-kxm-signature"] === void 0;
|
|
21695
|
+
const deliveryId = bodyOnlySignature ? verifyProviderWebhookSignature(request, rawBody, definition) : verifyKxmWebhookSignature(request, rawBody, definition.secret, { definitionId: definition.id });
|
|
21620
21696
|
const payload = parseJsonBody(rawBody);
|
|
21621
21697
|
const event = webhookEvent(request, payload);
|
|
21622
21698
|
if (definition.event && event !== definition.event) {
|
|
@@ -21627,15 +21703,30 @@ data: ${JSON.stringify({ type: "ops", project, topic, at: nowIso() })}
|
|
|
21627
21703
|
response.writeHead(204, { "cache-control": "no-store" }).end();
|
|
21628
21704
|
return;
|
|
21629
21705
|
}
|
|
21630
|
-
const
|
|
21631
|
-
const deliveryId = requireString(deliveryHeader, "webhook delivery identifier", { max: 128 });
|
|
21706
|
+
const payloadHash = createHash5("sha256").update(rawBody).digest("hex");
|
|
21632
21707
|
const existing = [...workflowRuns.values()].find(
|
|
21633
21708
|
(run2) => run2.definitionId === definition.id && run2.deliveryId === deliveryId
|
|
21634
21709
|
);
|
|
21635
21710
|
if (existing) {
|
|
21636
|
-
|
|
21711
|
+
if (existing.payloadHash !== payloadHash) {
|
|
21712
|
+
throw new ProtocolError(
|
|
21713
|
+
409,
|
|
21714
|
+
"webhook delivery identifier was already used for a different body",
|
|
21715
|
+
"webhook_delivery_conflict"
|
|
21716
|
+
);
|
|
21717
|
+
}
|
|
21718
|
+
json(response, 200, { duplicate: true, runId: existing.id, status: existing.status });
|
|
21637
21719
|
return;
|
|
21638
21720
|
}
|
|
21721
|
+
if (bodyOnlySignature && [...workflowRuns.values()].some(
|
|
21722
|
+
(run2) => run2.definitionId === definition.id && run2.payloadHash === payloadHash
|
|
21723
|
+
)) {
|
|
21724
|
+
throw new ProtocolError(
|
|
21725
|
+
409,
|
|
21726
|
+
"this signed body already started a run under another delivery identifier",
|
|
21727
|
+
"webhook_payload_replayed"
|
|
21728
|
+
);
|
|
21729
|
+
}
|
|
21639
21730
|
const target = findKnownTarget(definition.project, definition.target);
|
|
21640
21731
|
const createdAt = nowIso();
|
|
21641
21732
|
const runId = newId("run");
|
|
@@ -21685,7 +21776,7 @@ data: ${JSON.stringify({ type: "ops", project, topic, at: nowIso() })}
|
|
|
21685
21776
|
definitionId: definition.id,
|
|
21686
21777
|
source: definition.source,
|
|
21687
21778
|
deliveryId,
|
|
21688
|
-
payloadHash
|
|
21779
|
+
payloadHash,
|
|
21689
21780
|
definitionHash: workflowDefinitionHash(definition),
|
|
21690
21781
|
...event ? { event } : {},
|
|
21691
21782
|
project: definition.project,
|
|
@@ -21716,7 +21807,7 @@ data: ${JSON.stringify({ type: "ops", project, topic, at: nowIso() })}
|
|
|
21716
21807
|
project: definition.project,
|
|
21717
21808
|
target: target.id
|
|
21718
21809
|
});
|
|
21719
|
-
json(response, 202, { run, duplicate: false });
|
|
21810
|
+
json(response, 202, { run, runId: run.id, duplicate: false });
|
|
21720
21811
|
return;
|
|
21721
21812
|
}
|
|
21722
21813
|
if (method === "GET" && url.pathname === "/metrics") {
|
|
@@ -22656,7 +22747,11 @@ data: ${JSON.stringify({ agent: publicAgent(current, staleAfterMs) })}
|
|
|
22656
22747
|
const hops = parseBoundedInteger(body.hops, "hops", 0, 0, 100);
|
|
22657
22748
|
const maxHops = parseBoundedInteger(body.maxHops, "maxHops", DEFAULT_MAX_HOPS, 1, 20);
|
|
22658
22749
|
if (hops >= maxHops) {
|
|
22659
|
-
throw new ProtocolError(
|
|
22750
|
+
throw new ProtocolError(
|
|
22751
|
+
400,
|
|
22752
|
+
`hop limit reached (${hops}/${maxHops}): this request would extend a chain of forwarded requests past its limit; answer the inbound request directly`,
|
|
22753
|
+
"hop_limit_reached"
|
|
22754
|
+
);
|
|
22660
22755
|
}
|
|
22661
22756
|
const ttlMs = parseBoundedInteger(
|
|
22662
22757
|
body.ttlMs,
|
package/plugins/kxm/package.json
CHANGED
|
@@ -112,7 +112,9 @@ token before committing anything shared — a refusal means stop, not retry.
|
|
|
112
112
|
- `replied`: contains the recipient's final reply.
|
|
113
113
|
- `cancelled`: sender cancelled queued or delivered work.
|
|
114
114
|
- `expired`: the request exceeded its TTL before a reply.
|
|
115
|
-
- `error`:
|
|
115
|
+
- `error`: reserved. The protocol declares it, but the hub never sets it, so
|
|
116
|
+
it is not a failure path to wait for. A `kxm_fanout` entry with
|
|
117
|
+
`status: "error"` reports a local send or wait failure, not this state.
|
|
116
118
|
|
|
117
119
|
Messages use a 24-hour default TTL and terminal records are retained for seven days by default. TTL begins when the message is sent and includes queued time; normally omit it for model work. A fanout's local wait ending does not change message state and returns a recoverable pending handle. Inspect it with `kxm_get` or repeat the exact request using the same correlation and idempotency prefix. Never create a replacement key while the original is pending, and never count a pending peer as workflow evidence. A stable `idempotencyKey` deduplicates an exact retry from the same sender. Durable `kxm_fanout` calls should use the workflow run ID as `correlationId` and a stage-specific `idempotencyKeyPrefix`; the client scopes the resulting key by correlation and normalized target. State is persisted in SQLite by the standard hub executable.
|
|
118
120
|
|
|
@@ -24,7 +24,7 @@ Always retrieve credentials and API keys directly from `pass-cli`:
|
|
|
24
24
|
pass-cli item view --vault-name "<vault>" --item-title "<title>" --field password
|
|
25
25
|
|
|
26
26
|
# Retrieve Steel infrastructure API key
|
|
27
|
-
pass-cli item view --vault-name "
|
|
27
|
+
pass-cli item view --vault-name "<vault>" --item-title "<steel-item>" --field STEEL_API_KEY
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
### 2. Secret Redaction Invariants
|
|
@@ -13,8 +13,8 @@ Use this skill to investigate and resolve connectivity failures, CDP attachment
|
|
|
13
13
|
|
|
14
14
|
- **Symptom**: `Failed to fetch Steel session (401)` or `Connection refused`.
|
|
15
15
|
- **Diagnosis**:
|
|
16
|
-
- Verify Steel API endpoint is reachable: `curl -sI
|
|
17
|
-
- Check `STEEL_API_KEY` in
|
|
16
|
+
- Verify Steel API endpoint is reachable: `curl -sI "$STEEL_API_URL/v1/health"`.
|
|
17
|
+
- Check that `STEEL_API_KEY` is set in the environment (`test -n "$STEEL_API_KEY" && echo set`); never print its value.
|
|
18
18
|
- **Remedy**: Update expired or missing API key in your session environment.
|
|
19
19
|
|
|
20
20
|
### 2. CDP WebSocket Attachment Failure
|
|
@@ -35,14 +35,14 @@ Use this skill to investigate and resolve connectivity failures, CDP attachment
|
|
|
35
35
|
|
|
36
36
|
### 4. Interactive Takeover Viewer Inaccessible
|
|
37
37
|
|
|
38
|
-
- **Symptom**: `
|
|
38
|
+
- **Symptom**: `$STEEL_UI_URL` opens but cannot interact with elements.
|
|
39
39
|
- **Diagnosis**: Self-hosted Steel OSS serves the session screencast and devtools.
|
|
40
|
-
- **Remedy**: Connect directly to the devtools inspector URL:
|
|
40
|
+
- **Remedy**: Connect directly to the devtools inspector URL: `$STEEL_API_URL/v1/devtools/inspector.html` or open the browser devtools panel to perform input actions.
|
|
41
41
|
|
|
42
42
|
### 5. Orphaned Browser Processes & Cleanup
|
|
43
43
|
|
|
44
44
|
- **Symptom**: Node memory pressure or high active session counts.
|
|
45
|
-
- **Diagnosis**: Query active sessions list: `
|
|
45
|
+
- **Diagnosis**: Query active sessions list: `printf 'x-steel-api-key: %s\n' "$STEEL_API_KEY" | curl -sS -H @- "$STEEL_API_URL/v1/sessions"`.
|
|
46
46
|
- **Remedy**:
|
|
47
47
|
- Iterate through inactive sessions and post `/release` for each stale ID.
|
|
48
48
|
- Ensure all automation scripts wrap browser usage in `try...finally` to release sessions reliably.
|