@kontextmind/kxm 0.6.0

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.
Files changed (227) hide show
  1. package/.claude-plugin/marketplace.json +19 -0
  2. package/.kxm/README.md +14 -0
  3. package/.kxm/assets/README.md +5 -0
  4. package/.kxm/assets/retrospectives/README.md +5 -0
  5. package/.kxm/config/README.md +5 -0
  6. package/.kxm/config/agents.json +43 -0
  7. package/.kxm/config/env.example +56 -0
  8. package/.kxm/config/update.example.yaml +9 -0
  9. package/.kxm/config/workflows/fix.json +160 -0
  10. package/.kxm/config/workflows/jira-development.json +116 -0
  11. package/.kxm/config/workflows/provenance-quorum.json +150 -0
  12. package/.kxm/config/workflows/v04-dogfood.json +72 -0
  13. package/CHANGELOG.md +465 -0
  14. package/LICENSE +21 -0
  15. package/README.md +306 -0
  16. package/SECURITY.md +72 -0
  17. package/docs/README.md +48 -0
  18. package/docs/agent-communication-envelopes-and-gates.md +553 -0
  19. package/docs/architecture.md +242 -0
  20. package/docs/assignment-runner.md +241 -0
  21. package/docs/configuration.md +361 -0
  22. package/docs/continuous-improvement.md +114 -0
  23. package/docs/getting-started.md +253 -0
  24. package/docs/kxm-handbook.md +1090 -0
  25. package/docs/operations.md +205 -0
  26. package/docs/provenance-gates.md +291 -0
  27. package/docs/skills.md +45 -0
  28. package/docs/templates/README.md +95 -0
  29. package/docs/templates/adr.md +88 -0
  30. package/docs/templates/architecture.md +120 -0
  31. package/docs/templates/bug-fix.md +109 -0
  32. package/docs/templates/feature.md +108 -0
  33. package/docs/templates/handoff.md +72 -0
  34. package/docs/templates/postmortem.md +77 -0
  35. package/docs/templates/research.md +100 -0
  36. package/docs/templates/review.md +85 -0
  37. package/docs/templates/runbook.md +73 -0
  38. package/docs/templates/test-plan.md +87 -0
  39. package/docs/templates/test-report.md +72 -0
  40. package/docs/test-matrix.md +121 -0
  41. package/docs/troubleshooting.md +249 -0
  42. package/docs/vnext/README.md +62 -0
  43. package/docs/vnext/architecture.md +185 -0
  44. package/docs/vnext/effects-and-recovery.md +172 -0
  45. package/docs/vnext/lifecycles.md +235 -0
  46. package/docs/vnext/migration.md +220 -0
  47. package/docs/vnext/routing.md +184 -0
  48. package/docs/vnext/synchronization.md +172 -0
  49. package/docs/vnext/terminology.md +240 -0
  50. package/docs/vnext/validation.md +335 -0
  51. package/docs/webhook-workflows.md +240 -0
  52. package/docs/workflow-guide.md +1150 -0
  53. package/examples/README.md +102 -0
  54. package/examples/provenance-workflow.json +40 -0
  55. package/examples/requester.ts +30 -0
  56. package/examples/reviewer-agent.ts +29 -0
  57. package/examples/roundtrip.ts +46 -0
  58. package/examples/vnext/.kxm/agents/coordinator.yaml +16 -0
  59. package/examples/vnext/.kxm/agents/critic-1.yaml +16 -0
  60. package/examples/vnext/.kxm/agents/critic-2.yaml +15 -0
  61. package/examples/vnext/.kxm/agents/critic-3.yaml +15 -0
  62. package/examples/vnext/.kxm/agents/implementer.yaml +15 -0
  63. package/examples/vnext/.kxm/agents/planner.yaml +13 -0
  64. package/examples/vnext/.kxm/agents/reproducer.yaml +15 -0
  65. package/examples/vnext/.kxm/agents/reviewer.yaml +15 -0
  66. package/examples/vnext/.kxm/gates.yaml +8 -0
  67. package/examples/vnext/.kxm/models/critic-claude.yaml +11 -0
  68. package/examples/vnext/.kxm/models/critic-gemini.yaml +11 -0
  69. package/examples/vnext/.kxm/models/critic-grok.yaml +12 -0
  70. package/examples/vnext/.kxm/models/implementation.yaml +14 -0
  71. package/examples/vnext/.kxm/models/primary.yaml +17 -0
  72. package/examples/vnext/.kxm/prices.yaml +111 -0
  73. package/examples/vnext/.kxm/project/env.yaml +7 -0
  74. package/examples/vnext/.kxm/project.yaml +32 -0
  75. package/examples/vnext/.kxm/repo/repo.yaml +8 -0
  76. package/examples/vnext/.kxm/workflows/default.yaml +92 -0
  77. package/examples/vnext/.kxm/workflows/fix.yaml +376 -0
  78. package/examples/vnext/.kxm/workflows/improve.yaml +57 -0
  79. package/examples/vnext/README.md +53 -0
  80. package/examples/vnext/records/assignment-result-recorded.json +63 -0
  81. package/examples/vnext/records/assignment-result.json +46 -0
  82. package/examples/vnext/records/context-candidate.json +42 -0
  83. package/examples/vnext/records/delivery-manifest.json +66 -0
  84. package/examples/vnext/records/effect-uncertainty-resolved-sync.json +67 -0
  85. package/examples/vnext/records/effect-uncertainty-resolved.json +62 -0
  86. package/examples/vnext/records/run-created.json +54 -0
  87. package/examples/vnext/records/sync-event.json +65 -0
  88. package/examples/vnext/repositories/api/.kxm/repo/env.yaml +7 -0
  89. package/examples/vnext/repositories/api/.kxm/repo/repo.yaml +8 -0
  90. package/examples/vnext/repositories/web/.kxm/repo/repo.yaml +8 -0
  91. package/examples/workflow-signal.ts +63 -0
  92. package/package.json +129 -0
  93. package/plugins/kxm/.claude-plugin/plugin.json +73 -0
  94. package/plugins/kxm/.mcp.json +19 -0
  95. package/plugins/kxm/README.md +93 -0
  96. package/plugins/kxm/dist/cli.js +42853 -0
  97. package/plugins/kxm/dist/client.js +416 -0
  98. package/plugins/kxm/dist/core.js +1823 -0
  99. package/plugins/kxm/dist/extension.js +3797 -0
  100. package/plugins/kxm/dist/mcp-server.js +17104 -0
  101. package/plugins/kxm/dist/runtime.js +23361 -0
  102. package/plugins/kxm/dist/server.js +13640 -0
  103. package/plugins/kxm/dist/vnext-runtime-supervisor.js +21109 -0
  104. package/plugins/kxm/package.json +12 -0
  105. package/plugins/kxm/skills/kxm/SKILL.md +97 -0
  106. package/plugins/kxm/skills/kxm/references/protocol.md +103 -0
  107. package/plugins/kxm/skills/kxm-session/SKILL.md +53 -0
  108. package/plugins/kxm/src/arbiter.ts +355 -0
  109. package/plugins/kxm/src/artifacts-exist.ts +62 -0
  110. package/plugins/kxm/src/autocomplete.ts +236 -0
  111. package/plugins/kxm/src/cli.ts +3707 -0
  112. package/plugins/kxm/src/client.ts +614 -0
  113. package/plugins/kxm/src/commands.ts +1063 -0
  114. package/plugins/kxm/src/config.ts +290 -0
  115. package/plugins/kxm/src/context/providers.ts +101 -0
  116. package/plugins/kxm/src/context-packet.ts +332 -0
  117. package/plugins/kxm/src/context.ts +499 -0
  118. package/plugins/kxm/src/core.ts +6 -0
  119. package/plugins/kxm/src/database.ts +563 -0
  120. package/plugins/kxm/src/diagnostics.ts +184 -0
  121. package/plugins/kxm/src/envelope.ts +118 -0
  122. package/plugins/kxm/src/extension.ts +895 -0
  123. package/plugins/kxm/src/external-effects.ts +299 -0
  124. package/plugins/kxm/src/github-watch.ts +255 -0
  125. package/plugins/kxm/src/hub-binding.ts +160 -0
  126. package/plugins/kxm/src/hub.ts +2502 -0
  127. package/plugins/kxm/src/improve.ts +383 -0
  128. package/plugins/kxm/src/inbox.ts +10 -0
  129. package/plugins/kxm/src/kxm-install-kind.ts +113 -0
  130. package/plugins/kxm/src/kxm-update-config.ts +39 -0
  131. package/plugins/kxm/src/kxm-update.ts +238 -0
  132. package/plugins/kxm/src/local-snapshot.ts +406 -0
  133. package/plugins/kxm/src/logger.ts +198 -0
  134. package/plugins/kxm/src/mcp-server.ts +143 -0
  135. package/plugins/kxm/src/memory.ts +385 -0
  136. package/plugins/kxm/src/nous-pi.ts +287 -0
  137. package/plugins/kxm/src/nous-provider.ts +729 -0
  138. package/plugins/kxm/src/price-calc.ts +87 -0
  139. package/plugins/kxm/src/prices.ts +121 -0
  140. package/plugins/kxm/src/protocol.ts +172 -0
  141. package/plugins/kxm/src/recovery.ts +211 -0
  142. package/plugins/kxm/src/redact.ts +26 -0
  143. package/plugins/kxm/src/retrospective.ts +400 -0
  144. package/plugins/kxm/src/routing.ts +830 -0
  145. package/plugins/kxm/src/runtime.ts +9 -0
  146. package/plugins/kxm/src/server.ts +117 -0
  147. package/plugins/kxm/src/session-work.ts +571 -0
  148. package/plugins/kxm/src/session.ts +184 -0
  149. package/plugins/kxm/src/skills.ts +535 -0
  150. package/plugins/kxm/src/state.ts +326 -0
  151. package/plugins/kxm/src/store.ts +637 -0
  152. package/plugins/kxm/src/studio-layout.ts +268 -0
  153. package/plugins/kxm/src/suggest.ts +162 -0
  154. package/plugins/kxm/src/task-manager.ts +244 -0
  155. package/plugins/kxm/src/telemetry.ts +116 -0
  156. package/plugins/kxm/src/tui.ts +1046 -0
  157. package/plugins/kxm/src/vnext-bindings.ts +403 -0
  158. package/plugins/kxm/src/vnext-config.ts +1646 -0
  159. package/plugins/kxm/src/vnext-engine-artifacts.ts +86 -0
  160. package/plugins/kxm/src/vnext-engine-command.ts +533 -0
  161. package/plugins/kxm/src/vnext-engine-compile.ts +722 -0
  162. package/plugins/kxm/src/vnext-engine-evidence.ts +273 -0
  163. package/plugins/kxm/src/vnext-engine-fold.ts +1400 -0
  164. package/plugins/kxm/src/vnext-engine-gate-records.ts +583 -0
  165. package/plugins/kxm/src/vnext-engine-plan.ts +717 -0
  166. package/plugins/kxm/src/vnext-engine.ts +2458 -0
  167. package/plugins/kxm/src/vnext-gate-hash.ts +10 -0
  168. package/plugins/kxm/src/vnext-harness.ts +1142 -0
  169. package/plugins/kxm/src/vnext-init.ts +430 -0
  170. package/plugins/kxm/src/vnext-migrate.ts +1848 -0
  171. package/plugins/kxm/src/vnext-oneshot-producer.ts +424 -0
  172. package/plugins/kxm/src/vnext-permission.ts +936 -0
  173. package/plugins/kxm/src/vnext-pi-producer.ts +628 -0
  174. package/plugins/kxm/src/vnext-repair.ts +1094 -0
  175. package/plugins/kxm/src/vnext-runtime-owner.ts +320 -0
  176. package/plugins/kxm/src/vnext-runtime-store.ts +1560 -0
  177. package/plugins/kxm/src/vnext-runtime-supervisor.ts +586 -0
  178. package/plugins/kxm/src/vnext-runtime.ts +663 -0
  179. package/plugins/kxm/src/vnext-template.ts +247 -0
  180. package/plugins/kxm/src/wiki.ts +313 -0
  181. package/plugins/kxm/src/workflow.ts +1548 -0
  182. package/schemas/vnext/README.md +46 -0
  183. package/schemas/vnext/agent.schema.json +40 -0
  184. package/schemas/vnext/assignment-result.schema.json +66 -0
  185. package/schemas/vnext/backup-manifest.schema.json +89 -0
  186. package/schemas/vnext/candidate.schema.json +109 -0
  187. package/schemas/vnext/common.schema.json +422 -0
  188. package/schemas/vnext/context-candidate.schema.json +76 -0
  189. package/schemas/vnext/context-packet.schema.json +192 -0
  190. package/schemas/vnext/delivery-manifest.schema.json +159 -0
  191. package/schemas/vnext/environment.schema.json +66 -0
  192. package/schemas/vnext/gate-registry.schema.json +109 -0
  193. package/schemas/vnext/handoff-manifest.schema.json +146 -0
  194. package/schemas/vnext/init-operation.schema.json +61 -0
  195. package/schemas/vnext/local-repository-bindings.schema.json +30 -0
  196. package/schemas/vnext/memory-record.schema.json +45 -0
  197. package/schemas/vnext/migration-decision.schema.json +26 -0
  198. package/schemas/vnext/migration-plan.schema.json +123 -0
  199. package/schemas/vnext/migration-receipt.schema.json +52 -0
  200. package/schemas/vnext/model.schema.json +42 -0
  201. package/schemas/vnext/permission-diff.schema.json +57 -0
  202. package/schemas/vnext/prices.schema.json +115 -0
  203. package/schemas/vnext/project.schema.json +85 -0
  204. package/schemas/vnext/repository.schema.json +24 -0
  205. package/schemas/vnext/run-event.schema.json +460 -0
  206. package/schemas/vnext/session-brief.schema.json +153 -0
  207. package/schemas/vnext/sync-event.schema.json +234 -0
  208. package/schemas/vnext/template-provenance.schema.json +38 -0
  209. package/schemas/vnext/workflow.schema.json +248 -0
  210. package/scripts/assignment-run.d.mts +354 -0
  211. package/scripts/assignment-run.mjs +4451 -0
  212. package/scripts/build-runtime.mjs +56 -0
  213. package/scripts/check-generated.mjs +77 -0
  214. package/scripts/check-versions.mjs +34 -0
  215. package/scripts/emit-codex-artifacts.d.mts +9 -0
  216. package/scripts/emit-codex-artifacts.mjs +91 -0
  217. package/scripts/harness-run.d.mts +83 -0
  218. package/scripts/harness-run.mjs +2095 -0
  219. package/scripts/kxm-hub.mjs +105 -0
  220. package/scripts/kxm-publish-npm.mjs +327 -0
  221. package/scripts/kxm-release-github.mjs +472 -0
  222. package/scripts/kxm-runtime-supervisor.mjs +7 -0
  223. package/scripts/kxm-worker.mjs +1127 -0
  224. package/scripts/kxm.mjs +27 -0
  225. package/scripts/roster-policy.d.mts +20 -0
  226. package/scripts/roster-policy.mjs +161 -0
  227. package/scripts/smoke-multi-pi.mjs +479 -0
@@ -0,0 +1,1823 @@
1
+ // plugins/kxm/src/protocol.ts
2
+ import { randomUUID } from "node:crypto";
3
+ var DEFAULT_PORT = 7331;
4
+ var DEFAULT_STALE_AFTER_MS = 3e4;
5
+ var DEFAULT_MAX_HOPS = 5;
6
+ var DEFAULT_MESSAGE_TTL_MS = 24 * 60 * 6e4;
7
+ var MIN_MESSAGE_TTL_MS = 1e3;
8
+ var MAX_MESSAGE_TTL_MS = 7 * 24 * 60 * 6e4;
9
+ var DEFAULT_MESSAGE_RETENTION_MS = 7 * 24 * 60 * 6e4;
10
+ var MIN_MESSAGE_RETENTION_MS = 1e3;
11
+ var DEFAULT_RATE_LIMIT_MAX = 600;
12
+ var DEFAULT_RATE_LIMIT_WINDOW_MS = 6e4;
13
+ var MAX_BODY_BYTES = 256 * 1024;
14
+ var MAX_CONTENT_CHARS = 32e3;
15
+ var ProtocolError = class extends Error {
16
+ statusCode;
17
+ code;
18
+ extras;
19
+ constructor(statusCode, message, code = "protocol_error", extras) {
20
+ super(message);
21
+ this.name = "ProtocolError";
22
+ this.statusCode = statusCode;
23
+ this.code = code;
24
+ if (extras) this.extras = extras;
25
+ }
26
+ };
27
+ function nowIso() {
28
+ return (/* @__PURE__ */ new Date()).toISOString();
29
+ }
30
+ function newId(prefix) {
31
+ return `${prefix}_${randomUUID().replaceAll("-", "")}`;
32
+ }
33
+ function requireString(value, field, options = {}) {
34
+ if (typeof value !== "string") {
35
+ throw new ProtocolError(400, `${field} must be a string`);
36
+ }
37
+ const result = value.trim();
38
+ if (!options.allowEmpty && result.length === 0) {
39
+ throw new ProtocolError(400, `${field} cannot be empty`);
40
+ }
41
+ if (options.max !== void 0 && result.length > options.max) {
42
+ throw new ProtocolError(400, `${field} exceeds ${options.max} characters`);
43
+ }
44
+ return result;
45
+ }
46
+ function optionalString(value, field, max) {
47
+ if (value === void 0 || value === null || value === "") return void 0;
48
+ return requireString(value, field, { max });
49
+ }
50
+ function parseDeliveryMode(value) {
51
+ if (value === void 0) return "followUp";
52
+ if (value === "steer" || value === "followUp" || value === "nextTurn") return value;
53
+ throw new ProtocolError(400, "delivery must be steer, followUp, or nextTurn");
54
+ }
55
+ function parseBoundedInteger(value, field, fallback, min, max) {
56
+ if (value === void 0) return fallback;
57
+ if (!Number.isInteger(value) || value < min || value > max) {
58
+ throw new ProtocolError(400, `${field} must be an integer between ${min} and ${max}`);
59
+ }
60
+ return value;
61
+ }
62
+
63
+ // plugins/kxm/src/routing.ts
64
+ import { createHash } from "node:crypto";
65
+
66
+ // plugins/kxm/src/price-calc.ts
67
+ function findModelPrice(catalog, model, provider) {
68
+ const normalizedModel = model.trim().toLowerCase();
69
+ const normalizedProvider = provider?.trim().toLowerCase();
70
+ for (const entry of catalog.models) {
71
+ if (normalizedProvider && entry.provider.toLowerCase() !== normalizedProvider) {
72
+ const matchesAlias = entry.aliases?.some((a) => a.toLowerCase() === normalizedModel);
73
+ if (!matchesAlias) continue;
74
+ }
75
+ if (entry.id.toLowerCase() === normalizedModel || entry.model.toLowerCase() === normalizedModel) {
76
+ return entry;
77
+ }
78
+ if (entry.aliases?.some((a) => a.toLowerCase() === normalizedModel)) {
79
+ return entry;
80
+ }
81
+ }
82
+ return void 0;
83
+ }
84
+ function calculateModelCost(catalog, params) {
85
+ const row = findModelPrice(catalog, params.model, params.provider);
86
+ if (!row || row.tiers.length === 0) return void 0;
87
+ const context = params.contextTokens ?? params.tokensIn ?? 0;
88
+ let selectedTier = row.tiers[0];
89
+ for (const tier of row.tiers) {
90
+ if (tier.upToContextTokens !== void 0 && tier.upToContextTokens !== null && context > tier.upToContextTokens) {
91
+ continue;
92
+ }
93
+ selectedTier = tier;
94
+ break;
95
+ }
96
+ const tokensIn = params.tokensIn ?? 0;
97
+ const tokensOut = params.tokensOut ?? 0;
98
+ const cacheRead = params.cacheReadTokens ?? 0;
99
+ const cacheWrite = params.cacheWriteTokens ?? 0;
100
+ const cost = tokensIn / 1e6 * selectedTier.inputPerMillion + tokensOut / 1e6 * selectedTier.outputPerMillion + cacheRead / 1e6 * (selectedTier.cacheReadPerMillion ?? 0) + cacheWrite / 1e6 * (selectedTier.cacheWritePerMillion ?? 0);
101
+ const priceRef = `${catalog.date}#${row.id}`;
102
+ return {
103
+ costUsd: Math.round(cost * 1e6) / 1e6,
104
+ priceRef
105
+ };
106
+ }
107
+
108
+ // plugins/kxm/src/routing.ts
109
+ var ROUTING_RECORD_SCHEMA = "kxm.routing-record.v1";
110
+ var ROUTING_RECORD_V2_SCHEMA = "kxm.routing-record.v2";
111
+ var BEHAVIORAL_HASH_VERSION = 1;
112
+ var MAX_CONTEXT_ITEM_IDS = 256;
113
+ var MAX_SKILL_REFS = 32;
114
+ var MAX_PROVIDER_METADATA_FIELDS = 32;
115
+ function normalizeModel(value) {
116
+ if (value === void 0) return void 0;
117
+ const normalized = value.trim().toLowerCase().replace(/\s+/g, "-");
118
+ return normalized === "" ? void 0 : normalized;
119
+ }
120
+ function behavioralConfigHash(input) {
121
+ const canonical = {
122
+ version: BEHAVIORAL_HASH_VERSION,
123
+ requestedModel: normalizeModel(input.requestedModel) ?? null,
124
+ effectiveModel: normalizeModel(input.effectiveModel) ?? null,
125
+ reasoningEffort: input.reasoningEffort?.trim() ?? null,
126
+ agentRole: input.agentRole ? input.agentRole.trim().toLowerCase().replace(/\s+/g, "-") : null,
127
+ rolePromptSha256: input.rolePromptSha256?.trim() ?? null,
128
+ skills: (input.skills ?? []).map((skill) => ({ id: skill.id.trim(), sha: skill.contentSha256.trim() })).sort((left, right) => left.id.localeCompare(right.id) || left.sha.localeCompare(right.sha)),
129
+ contextPolicyVersion: input.contextPolicyVersion?.trim() ?? null,
130
+ toolPolicyVersion: input.toolPolicyVersion?.trim() ?? null,
131
+ workflowDefinitionSha256: input.workflowDefinitionSha256?.trim() ?? null,
132
+ verifierConfigSha256: input.verifierConfigSha256?.trim() ?? null
133
+ };
134
+ return createHash("sha256").update(JSON.stringify(canonical), "utf8").digest("hex");
135
+ }
136
+ function boundedInt(value, field) {
137
+ if (value === void 0 || value === null) return void 0;
138
+ if (!Number.isInteger(value) || value < 0 || value > 1e6) {
139
+ throw new Error(`${field} must be an integer between 0 and 1000000`);
140
+ }
141
+ return value;
142
+ }
143
+ function boundedString(value, field, max) {
144
+ if (value === void 0 || value === null) return void 0;
145
+ if (typeof value !== "string" || value.length > max) {
146
+ throw new Error(`${field} must be a string of at most ${max} characters`);
147
+ }
148
+ return value;
149
+ }
150
+ function parseRoutingRecord(value) {
151
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
152
+ throw new Error("routing record must be an object");
153
+ }
154
+ const input = value;
155
+ if (input.schema === ROUTING_RECORD_V2_SCHEMA) {
156
+ return parseRoutingRecordV2(value);
157
+ }
158
+ if (input.schema !== ROUTING_RECORD_SCHEMA) {
159
+ throw new Error(`routing record schema must be ${ROUTING_RECORD_SCHEMA} or ${ROUTING_RECORD_V2_SCHEMA}`);
160
+ }
161
+ const skills = input.skills === void 0 || input.skills === null ? [] : (() => {
162
+ if (!Array.isArray(input.skills) || input.skills.length > MAX_SKILL_REFS) {
163
+ throw new Error(`routing skills must be an array of at most ${MAX_SKILL_REFS} refs`);
164
+ }
165
+ return input.skills.map((skill) => {
166
+ const ref = skill;
167
+ if (typeof ref?.id !== "string" || !ref.id.trim() || ref.id.length > 200 || typeof ref?.contentSha256 !== "string" || !/^[a-f0-9]{64}$/.test(ref.contentSha256)) {
168
+ throw new Error("routing skill refs must carry an id and a sha256 content hash");
169
+ }
170
+ return { id: ref.id.trim(), contentSha256: ref.contentSha256 };
171
+ });
172
+ })();
173
+ const contextItemIds = input.contextItemIds === void 0 || input.contextItemIds === null ? [] : (() => {
174
+ if (!Array.isArray(input.contextItemIds) || input.contextItemIds.length > MAX_CONTEXT_ITEM_IDS) {
175
+ throw new Error(`routing contextItemIds must be an array of at most ${MAX_CONTEXT_ITEM_IDS} ids`);
176
+ }
177
+ return input.contextItemIds.map((id) => {
178
+ if (typeof id !== "string" || !id.trim() || id.length > 200) {
179
+ throw new Error("routing contextItemIds must be bounded non-empty strings");
180
+ }
181
+ return id.trim();
182
+ });
183
+ })();
184
+ const providerMetadata = input.providerMetadata === void 0 || input.providerMetadata === null ? void 0 : (() => {
185
+ if (!input.providerMetadata || typeof input.providerMetadata !== "object" || Array.isArray(input.providerMetadata)) {
186
+ throw new Error("routing providerMetadata must be an object");
187
+ }
188
+ const entries = Object.entries(input.providerMetadata);
189
+ if (entries.length > MAX_PROVIDER_METADATA_FIELDS) {
190
+ throw new Error(`routing providerMetadata may carry at most ${MAX_PROVIDER_METADATA_FIELDS} fields`);
191
+ }
192
+ const normalized = {};
193
+ for (const [key, field] of entries) {
194
+ if (typeof key !== "string" || key.length > 64 || /prompt|body|content|message/i.test(key) || typeof field !== "string" && typeof field !== "number" && typeof field !== "boolean") {
195
+ throw new Error("routing providerMetadata values must be bounded strings, numbers, or booleans; raw bodies are rejected");
196
+ }
197
+ normalized[key] = typeof field === "string" ? field.slice(0, 200) : field;
198
+ }
199
+ return normalized;
200
+ })();
201
+ const verifierOutcome = input.verifierOutcome === void 0 || input.verifierOutcome === null ? void 0 : (() => {
202
+ if (input.verifierOutcome !== "passed" && input.verifierOutcome !== "warning" && input.verifierOutcome !== "failed") {
203
+ throw new Error("routing verifierOutcome must be passed, warning, or failed");
204
+ }
205
+ return input.verifierOutcome;
206
+ })();
207
+ const finalOutcome = input.finalOutcome === void 0 || input.finalOutcome === null ? void 0 : (() => {
208
+ if (input.finalOutcome !== "accepted" && input.finalOutcome !== "blocked" && input.finalOutcome !== "failed" && input.finalOutcome !== "pending") {
209
+ throw new Error("routing finalOutcome must be accepted, blocked, failed, or pending");
210
+ }
211
+ return input.finalOutcome;
212
+ })();
213
+ const costUsd = input.costUsd === void 0 || input.costUsd === null ? void 0 : (() => {
214
+ if (typeof input.costUsd !== "number" || !Number.isFinite(input.costUsd) || input.costUsd < 0 || input.costUsd > 1e6) {
215
+ throw new Error("routing costUsd must be a non-negative finite number");
216
+ }
217
+ return input.costUsd;
218
+ })();
219
+ const behavioralSha256 = boundedString(input.behavioralSha256, "behavioralSha256", 64) ?? "";
220
+ if (!/^[a-f0-9]{64}$/.test(behavioralSha256)) {
221
+ throw new Error("routing behavioralSha256 must be a sha256 hex digest");
222
+ }
223
+ const record = {
224
+ schema: ROUTING_RECORD_SCHEMA,
225
+ behavioralHashVersion: BEHAVIORAL_HASH_VERSION,
226
+ behavioralSha256,
227
+ skills,
228
+ contextItemIds,
229
+ retries: boundedInt(input.retries, "retries") ?? 0,
230
+ transitions: boundedInt(input.transitions, "transitions") ?? 0,
231
+ humanInterventions: boundedInt(input.humanInterventions, "humanInterventions") ?? 0
232
+ };
233
+ const strings = [
234
+ ["workflowRunId", boundedString(input.workflowRunId, "workflowRunId", 128)],
235
+ ["stageId", boundedString(input.stageId, "stageId", 128)],
236
+ ["requestedModel", normalizeModel(boundedString(input.requestedModel, "requestedModel", 200))],
237
+ ["effectiveModel", normalizeModel(boundedString(input.effectiveModel, "effectiveModel", 200))],
238
+ ["reasoningEffort", boundedString(input.reasoningEffort, "reasoningEffort", 64)],
239
+ ["agentRole", boundedString(input.agentRole, "agentRole", 64)],
240
+ ["rolePromptSha256", boundedString(input.rolePromptSha256, "rolePromptSha256", 64)],
241
+ ["contextPolicyVersion", boundedString(input.contextPolicyVersion, "contextPolicyVersion", 64)],
242
+ ["toolPolicyVersion", boundedString(input.toolPolicyVersion, "toolPolicyVersion", 64)],
243
+ ["workflowDefinitionSha256", boundedString(input.workflowDefinitionSha256, "workflowDefinitionSha256", 64)],
244
+ ["verifierConfigSha256", boundedString(input.verifierConfigSha256, "verifierConfigSha256", 64)]
245
+ ];
246
+ for (const [key, value2] of strings) {
247
+ if (value2 !== void 0) record[key] = value2;
248
+ }
249
+ for (const [key, value2] of [["attempt", boundedInt(input.attempt, "attempt")], ["tokensIn", boundedInt(input.tokensIn, "tokensIn")], ["tokensOut", boundedInt(input.tokensOut, "tokensOut")], ["cacheReadTokens", boundedInt(input.cacheReadTokens, "cacheReadTokens")]]) {
250
+ if (value2 !== void 0) record[key] = value2;
251
+ }
252
+ if (costUsd !== void 0) record.costUsd = costUsd;
253
+ if (verifierOutcome !== void 0) record.verifierOutcome = verifierOutcome;
254
+ if (finalOutcome !== void 0) record.finalOutcome = finalOutcome;
255
+ if (providerMetadata !== void 0) record.providerMetadata = providerMetadata;
256
+ return record;
257
+ }
258
+ function parseRoutingRecordV2(value) {
259
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
260
+ throw new Error("routing record v2 must be an object");
261
+ }
262
+ const input = value;
263
+ if (input.schema !== ROUTING_RECORD_V2_SCHEMA) {
264
+ throw new Error(`routing record v2 schema must be ${ROUTING_RECORD_V2_SCHEMA}`);
265
+ }
266
+ const costBasis = input.costBasis;
267
+ if (costBasis !== "metered" && costBasis !== "unmetered" && costBasis !== "unknown") {
268
+ throw new Error("routing record v2 costBasis must be metered, unmetered, or unknown");
269
+ }
270
+ let costUsd = void 0;
271
+ if (costBasis === "metered") {
272
+ if (typeof input.costUsd !== "number" || !Number.isFinite(input.costUsd) || input.costUsd < 0 || input.costUsd > 1e6) {
273
+ throw new Error("routing record v2 costUsd must be a non-negative finite number when costBasis is metered");
274
+ }
275
+ costUsd = input.costUsd;
276
+ } else if (input.costUsd !== void 0 && input.costUsd !== null) {
277
+ if (typeof input.costUsd !== "number" || !Number.isFinite(input.costUsd) || input.costUsd < 0) {
278
+ throw new Error("routing record v2 costUsd must be a non-negative finite number when present");
279
+ }
280
+ costUsd = input.costUsd;
281
+ } else {
282
+ costUsd = null;
283
+ }
284
+ const rawHash = boundedString(input.behavioralSha256, "behavioralSha256", 72) ?? "";
285
+ if (!/^(?:sha256:)?[a-f0-9]{64}$/.test(rawHash)) {
286
+ throw new Error("routing record v2 behavioralSha256 must be a sha256 hex digest");
287
+ }
288
+ const behavioralSha256 = rawHash.startsWith("sha256:") ? rawHash : `sha256:${rawHash}`;
289
+ const latencyMs = input.latencyMs;
290
+ if (typeof latencyMs !== "number" || !Number.isFinite(latencyMs) || latencyMs < 0) {
291
+ throw new Error("routing record v2 latencyMs must be a non-negative number");
292
+ }
293
+ const providerMetadata = input.providerMetadata === void 0 || input.providerMetadata === null ? void 0 : (() => {
294
+ if (!input.providerMetadata || typeof input.providerMetadata !== "object" || Array.isArray(input.providerMetadata)) {
295
+ throw new Error("routing providerMetadata must be an object");
296
+ }
297
+ const entries = Object.entries(input.providerMetadata);
298
+ if (entries.length > MAX_PROVIDER_METADATA_FIELDS) {
299
+ throw new Error(`routing providerMetadata may carry at most ${MAX_PROVIDER_METADATA_FIELDS} fields`);
300
+ }
301
+ const normalized = {};
302
+ for (const [key, field] of entries) {
303
+ if (typeof key !== "string" || key.length > 64 || /prompt|body|content|message/i.test(key) || typeof field !== "string" && typeof field !== "number" && typeof field !== "boolean") {
304
+ throw new Error("routing providerMetadata values must be bounded strings, numbers, or booleans; raw bodies are rejected");
305
+ }
306
+ normalized[key] = typeof field === "string" ? field.slice(0, 200) : field;
307
+ }
308
+ return normalized;
309
+ })();
310
+ const record = {
311
+ schema: ROUTING_RECORD_V2_SCHEMA,
312
+ recordedAt: boundedString(input.recordedAt, "recordedAt", 64) ?? (/* @__PURE__ */ new Date()).toISOString(),
313
+ project: boundedString(input.project, "project", 128) ?? "",
314
+ runId: boundedString(input.runId, "runId", 128) ?? "",
315
+ stepId: boundedString(input.stepId, "stepId", 128) ?? "",
316
+ assignmentId: boundedString(input.assignmentId, "assignmentId", 128) ?? "",
317
+ attemptId: boundedString(input.attemptId, "attemptId", 128) ?? "",
318
+ harness: boundedString(input.harness, "harness", 64) ?? "",
319
+ provider: boundedString(input.provider, "provider", 64) ?? "",
320
+ requestedModel: normalizeModel(boundedString(input.requestedModel, "requestedModel", 200)) ?? "",
321
+ effectiveModel: normalizeModel(boundedString(input.effectiveModel, "effectiveModel", 200)) ?? "",
322
+ behavioralSha256,
323
+ latencyMs,
324
+ costBasis,
325
+ costUsd,
326
+ retries: boundedInt(input.retries, "retries") ?? 0
327
+ };
328
+ if (input.thinking !== void 0 && input.thinking !== null) {
329
+ record.thinking = boundedString(input.thinking, "thinking", 64);
330
+ }
331
+ const rawRole = input.agentRole ?? input.role;
332
+ if (rawRole !== void 0 && rawRole !== null) {
333
+ record.agentRole = boundedString(rawRole, "agentRole", 64);
334
+ }
335
+ if (input.contextTokens !== void 0) {
336
+ record.contextTokens = boundedInt(input.contextTokens, "contextTokens") ?? null;
337
+ }
338
+ if (input.tokensIn !== void 0) {
339
+ record.tokensIn = boundedInt(input.tokensIn, "tokensIn") ?? null;
340
+ }
341
+ if (input.tokensOut !== void 0) {
342
+ record.tokensOut = boundedInt(input.tokensOut, "tokensOut") ?? null;
343
+ }
344
+ if (input.cacheReadTokens !== void 0) {
345
+ record.cacheReadTokens = boundedInt(input.cacheReadTokens, "cacheReadTokens") ?? null;
346
+ }
347
+ if (input.cacheWriteTokens !== void 0) {
348
+ record.cacheWriteTokens = boundedInt(input.cacheWriteTokens, "cacheWriteTokens") ?? null;
349
+ }
350
+ if (input.priceRef !== void 0 && input.priceRef !== null) {
351
+ record.priceRef = boundedString(input.priceRef, "priceRef", 128);
352
+ }
353
+ if (input.verifierOutcome !== void 0 && input.verifierOutcome !== null) {
354
+ if (input.verifierOutcome !== "passed" && input.verifierOutcome !== "warning" && input.verifierOutcome !== "failed") {
355
+ throw new Error("routing verifierOutcome must be passed, warning, or failed");
356
+ }
357
+ record.verifierOutcome = input.verifierOutcome;
358
+ }
359
+ if (input.finalOutcome !== void 0 && input.finalOutcome !== null) {
360
+ record.finalOutcome = typeof input.finalOutcome === "string" ? input.finalOutcome.slice(0, 64) : void 0;
361
+ }
362
+ if (input.transitions !== void 0) {
363
+ record.transitions = boundedInt(input.transitions, "transitions");
364
+ }
365
+ if (input.humanInterventions !== void 0) {
366
+ record.humanInterventions = boundedInt(input.humanInterventions, "humanInterventions");
367
+ }
368
+ if (providerMetadata !== void 0) {
369
+ record.providerMetadata = providerMetadata;
370
+ }
371
+ return record;
372
+ }
373
+ function compareRoutingRecords(records) {
374
+ if (records.length === 0) {
375
+ throw new Error("compareRoutingRecords requires at least one record");
376
+ }
377
+ const behavioralSha256 = records[0].behavioralSha256;
378
+ for (const record of records) {
379
+ if (record.behavioralSha256 !== behavioralSha256) {
380
+ throw new Error("compareRoutingRecords requires records with identical behavioral hashes");
381
+ }
382
+ }
383
+ const settled = records.filter((record) => record.finalOutcome !== void 0 && record.finalOutcome !== "pending");
384
+ const accepted = settled.filter((record) => record.finalOutcome === "accepted").length;
385
+ const blocked = settled.filter((record) => record.finalOutcome === "blocked").length;
386
+ const failed = settled.filter((record) => record.finalOutcome === "failed").length;
387
+ const reworked = records.filter((record) => record.retries > 0 || record.transitions > 0).length;
388
+ return {
389
+ behavioralSha256,
390
+ runs: records.length,
391
+ verifiedCompletions: accepted,
392
+ blocked,
393
+ failed,
394
+ reworkRate: records.length === 0 ? 0 : Math.round(reworked / records.length * 100) / 100,
395
+ totalCostUsd: Math.round(records.reduce((sum, record) => sum + (record.costUsd ?? 0), 0) * 1e4) / 1e4,
396
+ totalTokensIn: records.reduce((sum, record) => sum + (record.tokensIn ?? 0), 0),
397
+ totalTokensOut: records.reduce((sum, record) => sum + (record.tokensOut ?? 0), 0),
398
+ totalHumanInterventions: records.reduce((sum, record) => sum + record.humanInterventions, 0)
399
+ };
400
+ }
401
+ function groupByBehavior(records) {
402
+ const groups = /* @__PURE__ */ new Map();
403
+ for (const record of records) {
404
+ const bucket = groups.get(record.behavioralSha256) ?? [];
405
+ bucket.push(record);
406
+ groups.set(record.behavioralSha256, bucket);
407
+ }
408
+ return groups;
409
+ }
410
+ var ROUTING_REPORT_SCHEMA = "kxm.routing-report.v1";
411
+ function isQuotaExhausted(record) {
412
+ if ("providerMetadata" in record && record.providerMetadata) {
413
+ const meta = record.providerMetadata;
414
+ if (meta.failureClass === "quota" || meta.errorCode === "provider_quota" || meta.stopReason === "quota_exhausted" || meta.quotaExhausted === true || meta.quota_exhausted === true) {
415
+ return true;
416
+ }
417
+ for (const [key, val] of Object.entries(meta)) {
418
+ if (/quota/i.test(key) && val === true) return true;
419
+ if (typeof val === "string" && /\b(quota reached|quota exceeded|rate limit(?:ed)?|too many requests|resource exhausted|http 429|quota_exhausted)\b/i.test(val)) {
420
+ return true;
421
+ }
422
+ }
423
+ }
424
+ if (record.finalOutcome === "quota" || record.finalOutcome === "quota_exhausted") return true;
425
+ return false;
426
+ }
427
+ function computePercentile(sorted, p) {
428
+ if (sorted.length === 0) return 0;
429
+ if (sorted.length === 1) return sorted[0];
430
+ const pos = p * (sorted.length - 1);
431
+ const base = Math.floor(pos);
432
+ const rest = pos - base;
433
+ if (sorted[base + 1] !== void 0) {
434
+ return Math.round(sorted[base] + rest * (sorted[base + 1] - sorted[base]));
435
+ }
436
+ return sorted[base];
437
+ }
438
+ function generateRoutingReport(records, options = {}) {
439
+ const generatedAt = options.now ? options.now() : (/* @__PURE__ */ new Date()).toISOString();
440
+ if (records.length === 0) {
441
+ return {
442
+ schema: ROUTING_REPORT_SCHEMA,
443
+ generatedAt,
444
+ totalAttempts: 0,
445
+ rows: []
446
+ };
447
+ }
448
+ const groups = /* @__PURE__ */ new Map();
449
+ for (const record of records) {
450
+ const isV2 = record.schema === ROUTING_RECORD_V2_SCHEMA;
451
+ const harness = (isV2 ? record.harness : record.providerMetadata?.harness) || "unknown";
452
+ const model = (isV2 ? record.effectiveModel || record.requestedModel : record.effectiveModel || record.requestedModel) || "unknown";
453
+ const thinking = (isV2 ? record.thinking : record.reasoningEffort) || "none";
454
+ const role = record.role || record.agentRole || "unknown";
455
+ const key = `${harness}\0${model}\0${thinking}\0${role}`;
456
+ let bucket = groups.get(key);
457
+ if (!bucket) {
458
+ bucket = [];
459
+ groups.set(key, bucket);
460
+ }
461
+ bucket.push(record);
462
+ }
463
+ const rows = [];
464
+ for (const [key, groupRecords] of groups.entries()) {
465
+ const [harness, model, thinking, role] = key.split("\0");
466
+ const attempts = groupRecords.length;
467
+ let verifyPassedCount = 0;
468
+ let reworkCount = 0;
469
+ let acceptedCount = 0;
470
+ let meteredCostTotal = 0;
471
+ let unmeteredAttempts = 0;
472
+ let unknownCostAttempts = 0;
473
+ let quotaExhaustedAttempts = 0;
474
+ const latencies = [];
475
+ const contextVals = [];
476
+ for (const r of groupRecords) {
477
+ const isV2 = r.schema === ROUTING_RECORD_V2_SCHEMA;
478
+ const v2 = isV2 ? r : void 0;
479
+ if (r.verifierOutcome === "passed" || !r.verifierOutcome && r.finalOutcome === "accepted") {
480
+ verifyPassedCount++;
481
+ }
482
+ if ((r.transitions ?? 0) > 0) {
483
+ reworkCount++;
484
+ }
485
+ if (r.finalOutcome === "accepted" || r.verifierOutcome === "passed") {
486
+ acceptedCount++;
487
+ }
488
+ const costBasis = v2 ? v2.costBasis : typeof r.costUsd === "number" ? "metered" : "unknown";
489
+ if (costBasis === "metered") {
490
+ if (typeof r.costUsd === "number" && Number.isFinite(r.costUsd)) {
491
+ meteredCostTotal += r.costUsd;
492
+ }
493
+ } else if (costBasis === "unmetered") {
494
+ unmeteredAttempts++;
495
+ } else {
496
+ unknownCostAttempts++;
497
+ }
498
+ if (isQuotaExhausted(r)) {
499
+ quotaExhaustedAttempts++;
500
+ }
501
+ const lat = v2 ? v2.latencyMs : typeof r.providerMetadata?.latencyMs === "number" ? r.providerMetadata.latencyMs : void 0;
502
+ if (typeof lat === "number" && Number.isFinite(lat) && lat >= 0) {
503
+ latencies.push(lat);
504
+ }
505
+ const ctx = v2 ? v2.contextTokens ?? v2.tokensIn : r.tokensIn;
506
+ if (typeof ctx === "number" && Number.isFinite(ctx) && ctx >= 0) {
507
+ contextVals.push(ctx);
508
+ }
509
+ }
510
+ const verifyPassRate = attempts > 0 ? Math.round(verifyPassedCount / attempts * 1e3) / 1e3 : 0;
511
+ const reworkRate = attempts > 0 ? Math.round(reworkCount / attempts * 1e3) / 1e3 : 0;
512
+ latencies.sort((a, b) => a - b);
513
+ const latencyP50Ms = computePercentile(latencies, 0.5);
514
+ const latencyP95Ms = computePercentile(latencies, 0.95);
515
+ contextVals.sort((a, b) => a - b);
516
+ let medianContextTokens = null;
517
+ if (contextVals.length > 0) {
518
+ const mid = Math.floor(contextVals.length / 2);
519
+ medianContextTokens = contextVals.length % 2 !== 0 ? contextVals[mid] : Math.round((contextVals[mid - 1] + contextVals[mid]) / 2);
520
+ }
521
+ const meteredCostUsd = Math.round(meteredCostTotal * 1e4) / 1e4;
522
+ const costPerAcceptedUsd = acceptedCount > 0 ? meteredCostUsd > 0 || unmeteredAttempts > 0 ? Math.round(meteredCostUsd / acceptedCount * 1e4) / 1e4 : unknownCostAttempts === attempts ? null : 0 : null;
523
+ const flagged = unknownCostAttempts > 0;
524
+ let equivalentListCostUsd = void 0;
525
+ if (options.includeEquivalentListCost && options.catalog) {
526
+ let equivTotal = 0;
527
+ let calculatedAll = true;
528
+ for (const r of groupRecords) {
529
+ const isV2 = r.schema === ROUTING_RECORD_V2_SCHEMA;
530
+ const v2 = isV2 ? r : void 0;
531
+ const costBasis = v2 ? v2.costBasis : typeof r.costUsd === "number" ? "metered" : "unknown";
532
+ if (costBasis === "metered" && typeof r.costUsd === "number") {
533
+ equivTotal += r.costUsd;
534
+ } else {
535
+ const calc = calculateModelCost(options.catalog, {
536
+ model,
537
+ ...v2?.provider ? { provider: v2.provider } : {},
538
+ tokensIn: r.tokensIn ?? null,
539
+ tokensOut: r.tokensOut ?? null,
540
+ cacheReadTokens: r.cacheReadTokens ?? null,
541
+ contextTokens: v2?.contextTokens ?? r.tokensIn ?? null
542
+ });
543
+ if (calc) {
544
+ equivTotal += calc.costUsd;
545
+ } else {
546
+ calculatedAll = false;
547
+ }
548
+ }
549
+ }
550
+ equivalentListCostUsd = calculatedAll ? Math.round(equivTotal * 1e4) / 1e4 : null;
551
+ }
552
+ rows.push({
553
+ harness,
554
+ model,
555
+ thinking,
556
+ role,
557
+ attempts,
558
+ verifyPassRate,
559
+ reworkRate,
560
+ latencyP50Ms,
561
+ latencyP95Ms,
562
+ medianContextTokens,
563
+ meteredCostUsd,
564
+ costPerAcceptedUsd,
565
+ unmeteredAttempts,
566
+ unknownCostAttempts,
567
+ quotaExhaustedAttempts,
568
+ flagged,
569
+ ...options.includeEquivalentListCost ? { equivalentListCostUsd } : {}
570
+ });
571
+ }
572
+ rows.sort((a, b) => {
573
+ if (a.verifyPassRate !== b.verifyPassRate) {
574
+ return b.verifyPassRate - a.verifyPassRate;
575
+ }
576
+ if (a.reworkRate !== b.reworkRate) {
577
+ return a.reworkRate - b.reworkRate;
578
+ }
579
+ const aCostUnknown = a.costPerAcceptedUsd === null && a.unknownCostAttempts > 0 && a.meteredCostUsd === 0;
580
+ const bCostUnknown = b.costPerAcceptedUsd === null && b.unknownCostAttempts > 0 && b.meteredCostUsd === 0;
581
+ if (aCostUnknown && !bCostUnknown) return 1;
582
+ if (!aCostUnknown && bCostUnknown) return -1;
583
+ if (a.costPerAcceptedUsd !== null && b.costPerAcceptedUsd !== null) {
584
+ if (a.costPerAcceptedUsd !== b.costPerAcceptedUsd) {
585
+ return a.costPerAcceptedUsd - b.costPerAcceptedUsd;
586
+ }
587
+ } else if (a.costPerAcceptedUsd !== null) {
588
+ return -1;
589
+ } else if (b.costPerAcceptedUsd !== null) {
590
+ return 1;
591
+ }
592
+ if (a.meteredCostUsd !== b.meteredCostUsd) {
593
+ return a.meteredCostUsd - b.meteredCostUsd;
594
+ }
595
+ if (a.attempts !== b.attempts) {
596
+ return b.attempts - a.attempts;
597
+ }
598
+ const cmpH = a.harness.localeCompare(b.harness);
599
+ if (cmpH !== 0) return cmpH;
600
+ const cmpM = a.model.localeCompare(b.model);
601
+ if (cmpM !== 0) return cmpM;
602
+ return a.role.localeCompare(b.role);
603
+ });
604
+ return {
605
+ schema: ROUTING_REPORT_SCHEMA,
606
+ generatedAt,
607
+ totalAttempts: records.length,
608
+ rows
609
+ };
610
+ }
611
+ function formatRoutingReport(report, options = {}) {
612
+ if (report.rows.length === 0) {
613
+ return "no routing records to report";
614
+ }
615
+ const showListCost = Boolean(options.equivalentListCost);
616
+ const headers = [
617
+ "Harness".padEnd(10),
618
+ "Model".padEnd(24),
619
+ "Effort".padEnd(8),
620
+ "Role".padEnd(14),
621
+ "Att".padStart(4),
622
+ "Pass%".padStart(7),
623
+ "Rwk%".padStart(6),
624
+ "p50(ms)".padStart(8),
625
+ "p95(ms)".padStart(8),
626
+ "CtxTok".padStart(8),
627
+ "Metered($)".padStart(11),
628
+ "$/Acc".padStart(9),
629
+ "Unm".padStart(4),
630
+ "Unk".padStart(5),
631
+ "Quota".padStart(6),
632
+ ...showListCost ? ["ListEquiv($)".padStart(13)] : []
633
+ ].join(" ");
634
+ const lines = [
635
+ `Routing Telemetry Report (${report.totalAttempts} attempt(s) across ${report.rows.length} route(s), quality-first ranking)`,
636
+ headers
637
+ ];
638
+ for (const row of report.rows) {
639
+ const passPct = `${(row.verifyPassRate * 100).toFixed(1)}%`;
640
+ const rwkPct = `${(row.reworkRate * 100).toFixed(1)}%`;
641
+ const p50 = `${row.latencyP50Ms}`;
642
+ const p95 = `${row.latencyP95Ms}`;
643
+ const ctx = row.medianContextTokens !== null ? `${row.medianContextTokens}` : "-";
644
+ const metered = `$${row.meteredCostUsd.toFixed(4)}`;
645
+ const perAcc = row.costPerAcceptedUsd !== null ? `$${row.costPerAcceptedUsd.toFixed(4)}` : "-";
646
+ const unkText = `${row.unknownCostAttempts}${row.flagged ? "*" : ""}`;
647
+ const cells = [
648
+ row.harness.padEnd(10),
649
+ (row.model.length > 24 ? `${row.model.slice(0, 21)}...` : row.model).padEnd(24),
650
+ row.thinking.padEnd(8),
651
+ (row.role.length > 14 ? `${row.role.slice(0, 11)}...` : row.role).padEnd(14),
652
+ String(row.attempts).padStart(4),
653
+ passPct.padStart(7),
654
+ rwkPct.padStart(6),
655
+ p50.padStart(8),
656
+ p95.padStart(8),
657
+ ctx.padStart(8),
658
+ metered.padStart(11),
659
+ perAcc.padStart(9),
660
+ String(row.unmeteredAttempts).padStart(4),
661
+ unkText.padStart(5),
662
+ String(row.quotaExhaustedAttempts).padStart(6),
663
+ ...showListCost ? [(row.equivalentListCostUsd !== void 0 && row.equivalentListCostUsd !== null ? `$${row.equivalentListCostUsd.toFixed(4)}` : "-").padStart(13)] : []
664
+ ];
665
+ lines.push(cells.join(" "));
666
+ }
667
+ if (report.rows.some((r) => r.flagged)) {
668
+ lines.push("* = unknown-cost attempts present (never ranked cheapest)");
669
+ }
670
+ return lines.join("\n");
671
+ }
672
+
673
+ // plugins/kxm/src/envelope.ts
674
+ var WORKER_SCHEMA = "kxm.worker.v1";
675
+ var WORKER_RESULT_SCHEMA = "kxm.worker-result.v1";
676
+ function agentWorker(input) {
677
+ return {
678
+ schema: WORKER_SCHEMA,
679
+ kind: "agent",
680
+ driver: "ai",
681
+ name: input.name,
682
+ ...input.project ? { project: input.project } : {},
683
+ ...input.purpose ? { purpose: input.purpose } : {},
684
+ ...input.model ? { model: input.model } : {},
685
+ ...input.thinking ? { thinking: input.thinking } : {}
686
+ };
687
+ }
688
+ function gateWorker(input) {
689
+ return {
690
+ schema: WORKER_SCHEMA,
691
+ kind: "gate",
692
+ driver: "code",
693
+ name: input.name,
694
+ ...input.project ? { project: input.project } : {},
695
+ ...input.purpose ? { purpose: input.purpose } : {}
696
+ };
697
+ }
698
+ function workerResult(worker, payload) {
699
+ const { summary, outcome, createdAt, ...rest } = payload;
700
+ if (outcome === "passed" && payload.ok === false) {
701
+ throw new Error('workerResult outcome contradicts ok: "passed" requires ok: true');
702
+ }
703
+ if (outcome === "failed" && payload.ok === true) {
704
+ throw new Error('workerResult outcome contradicts ok: "failed" requires ok: false');
705
+ }
706
+ if (rest.routing !== void 0) {
707
+ parseRoutingRecord(rest.routing);
708
+ }
709
+ return {
710
+ ...rest,
711
+ schema: WORKER_RESULT_SCHEMA,
712
+ worker,
713
+ createdAt: createdAt ?? nowIso(),
714
+ outcome: outcome ?? (payload.ok ? "passed" : "failed"),
715
+ summary
716
+ };
717
+ }
718
+
719
+ // plugins/kxm/src/redact.ts
720
+ var SECRET_PATTERNS = [
721
+ /\bsk-[A-Za-z0-9_-]{8,}\b/g,
722
+ /\bghp_[A-Za-z0-9_]{20,}\b/g,
723
+ /\bgithub_pat_[A-Za-z0-9_]{20,}\b/g,
724
+ /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/gi,
725
+ /\bBearer\s+[A-Za-z0-9._~+/=-]+\b/gi,
726
+ /\bKXM_[A-Z0-9_]*(TOKEN|SECRET|KEY)[A-Z0-9_]*=\S+/gi,
727
+ /\b(GITHUB_TOKEN|GH_TOKEN|KXM_AUTH_TOKEN|KXM_WORKFLOW_SIGNAL_SECRET)=\S+/gi,
728
+ /\b[A-Fa-f0-9]{64}\b/g
729
+ ];
730
+ function redactSecrets(value) {
731
+ let result = value;
732
+ for (const pattern of SECRET_PATTERNS) {
733
+ result = result.replace(pattern, "[redacted]");
734
+ }
735
+ return result;
736
+ }
737
+ function looksLikeSecret(value) {
738
+ return redactSecrets(value) !== value;
739
+ }
740
+ function redactStringList(values, maxItems = 32) {
741
+ return values.slice(0, maxItems).map((value) => redactSecrets(value).slice(0, 500));
742
+ }
743
+
744
+ // plugins/kxm/src/commands.ts
745
+ import { randomUUID as randomUUID2 } from "node:crypto";
746
+
747
+ // plugins/kxm/src/client.ts
748
+ var HubHttpError = class extends Error {
749
+ statusCode;
750
+ code;
751
+ requestId;
752
+ extras;
753
+ constructor(statusCode, message, code, requestId, extras) {
754
+ super(message);
755
+ this.name = "HubHttpError";
756
+ this.statusCode = statusCode;
757
+ if (code) this.code = code;
758
+ if (requestId) this.requestId = requestId;
759
+ if (extras) this.extras = extras;
760
+ }
761
+ };
762
+
763
+ // plugins/kxm/src/commands.ts
764
+ function requiredString(value, name) {
765
+ if (typeof value !== "string" || value.trim() === "") throw new Error(`${name} is required`);
766
+ return value.trim();
767
+ }
768
+ function optionalString2(value) {
769
+ return typeof value === "string" && value.trim() ? value.trim() : void 0;
770
+ }
771
+ function asRecord(value) {
772
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
773
+ }
774
+ function optionalWorkflowContext(value) {
775
+ if (value === void 0) return void 0;
776
+ const context = asRecord(value);
777
+ if (!Number.isInteger(context.attempt) || context.attempt < 1 || context.attempt > 20) {
778
+ throw new Error("workflowContext.attempt must be an integer between 1 and 20");
779
+ }
780
+ return {
781
+ runId: requiredString(context.runId, "workflowContext.runId"),
782
+ stageId: requiredString(context.stageId, "workflowContext.stageId"),
783
+ requirementKey: requiredString(context.requirementKey, "workflowContext.requirementKey"),
784
+ attempt: context.attempt
785
+ };
786
+ }
787
+ function optionalEvidenceRefs(value) {
788
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
789
+ }
790
+ function isTerminalMessageError(error) {
791
+ return error instanceof HubHttpError && (error.statusCode === 409 || error.statusCode === 404 && error.code === "message_not_found");
792
+ }
793
+ function isTerminalMessage(message) {
794
+ return message.status === "replied" || message.status === "cancelled" || message.status === "expired" || message.status === "error";
795
+ }
796
+ async function reconcileInbox(client, inbox, notifiedInbox) {
797
+ await Promise.all(
798
+ [...inbox.keys()].map(async (messageId) => {
799
+ try {
800
+ const current = await client.getMessage(messageId);
801
+ if (isTerminalMessage(current)) {
802
+ inbox.delete(messageId);
803
+ notifiedInbox?.delete(messageId);
804
+ } else {
805
+ inbox.set(messageId, current);
806
+ }
807
+ } catch (error) {
808
+ if (isTerminalMessageError(error)) {
809
+ inbox.delete(messageId);
810
+ notifiedInbox?.delete(messageId);
811
+ return;
812
+ }
813
+ throw error;
814
+ }
815
+ })
816
+ );
817
+ }
818
+ function resolveProject(client, projectArg) {
819
+ const proj = optionalString2(projectArg) ?? client.agent?.project;
820
+ if (!proj) {
821
+ throw new Error('missing required parameter "project"');
822
+ }
823
+ return proj;
824
+ }
825
+ var AGENT_COMMANDS = [
826
+ {
827
+ name: "kxm_list",
828
+ group: "peer",
829
+ verb: "list",
830
+ label: "List hub peers",
831
+ description: "List online peer agents in this project's hub pool, including their names and purposes.",
832
+ parameters: {
833
+ type: "object",
834
+ properties: {},
835
+ additionalProperties: false
836
+ },
837
+ async execute(client) {
838
+ return { agents: await client.listAgents() };
839
+ }
840
+ },
841
+ {
842
+ name: "kxm_send",
843
+ group: "peer",
844
+ verb: "send",
845
+ label: "Send peer request",
846
+ description: "Send a focused request to a peer agent. Returns a message ID for kxm_get or kxm_await. For durable peer evidence, workflowContext is the hub-authorized provenance scope; correlation and idempotency are transport concerns and do not establish evidence provenance.",
847
+ parameters: {
848
+ type: "object",
849
+ properties: {
850
+ target: { type: "string", description: "Peer name or agent ID" },
851
+ content: { type: "string", description: "Focused request with the expected response or artifact" },
852
+ delivery: {
853
+ type: "string",
854
+ enum: ["steer", "followUp", "nextTurn"],
855
+ default: "followUp",
856
+ description: "followUp is the safe default; use steer only for active blockers"
857
+ },
858
+ correlationId: {
859
+ type: "string",
860
+ description: "Optional task grouping; the hub binds it to runId when workflowContext is supplied"
861
+ },
862
+ idempotencyKey: {
863
+ type: "string",
864
+ description: "Retry/deduplication key only; not a workflow security or evidence binding"
865
+ },
866
+ workflowContext: {
867
+ type: "object",
868
+ description: "Requested provenance scope; the hub authorizes and persists the canonical binding",
869
+ properties: {
870
+ runId: { type: "string", description: "Active durable workflow run ID" },
871
+ stageId: { type: "string", description: "Active workflow stage ID" },
872
+ requirementKey: { type: "string", description: "Required evidence identity this peer reply may satisfy" },
873
+ attempt: { type: "integer", minimum: 1, maximum: 20, description: "Current one-based stage attempt" }
874
+ },
875
+ required: ["runId", "stageId", "requirementKey", "attempt"],
876
+ additionalProperties: false
877
+ },
878
+ ttlMs: { type: "number", minimum: 1e3, maximum: 6048e5, description: "Message TTL in milliseconds" }
879
+ },
880
+ required: ["target", "content"],
881
+ additionalProperties: false
882
+ },
883
+ async execute(client, args) {
884
+ const delivery = optionalString2(args.delivery);
885
+ const correlationId = optionalString2(args.correlationId);
886
+ const idempotencyKey = optionalString2(args.idempotencyKey);
887
+ const workflowContext = optionalWorkflowContext(args.workflowContext);
888
+ const message = await client.send({
889
+ target: requiredString(args.target, "target"),
890
+ content: requiredString(args.content, "content"),
891
+ ...delivery ? { delivery } : {},
892
+ ...correlationId ? { correlationId } : {},
893
+ ...idempotencyKey ? { idempotencyKey } : {},
894
+ ...workflowContext ? { workflowContext } : {},
895
+ ...typeof args.ttlMs === "number" ? { ttlMs: args.ttlMs } : {}
896
+ });
897
+ return { messageId: message.id, status: message.status, target: message.toName };
898
+ }
899
+ },
900
+ {
901
+ name: "kxm_get",
902
+ group: "peer",
903
+ verb: "get",
904
+ label: "Get peer request",
905
+ description: "Check the status and optional reply for a previously sent request.",
906
+ parameters: {
907
+ type: "object",
908
+ properties: {
909
+ messageId: { type: "string", description: "Message ID of the sent request" }
910
+ },
911
+ required: ["messageId"],
912
+ additionalProperties: false
913
+ },
914
+ async execute(client, args) {
915
+ return await client.getMessage(requiredString(args.messageId, "messageId"));
916
+ }
917
+ },
918
+ {
919
+ name: "kxm_fanout",
920
+ group: "peer",
921
+ verb: "fanout",
922
+ label: "Fanout peer requests",
923
+ description: "Ask one through three peers independently and return replies for comparison and synthesis. A local timeout or request cancellation returns a pending response with a durable messageId for kxm_get or an exact retry.",
924
+ parameters: {
925
+ type: "object",
926
+ properties: {
927
+ targets: {
928
+ type: "array",
929
+ items: { type: "string" },
930
+ minItems: 1,
931
+ maxItems: 3,
932
+ description: "One through three target peer names or agent IDs"
933
+ },
934
+ content: { type: "string", description: "Task description sent to all targets" },
935
+ correlationId: {
936
+ type: "string",
937
+ description: "Optional task grouping; the hub binds it to runId when workflowContext is supplied"
938
+ },
939
+ idempotencyKeyPrefix: {
940
+ type: "string",
941
+ description: "Stable retry/deduplication prefix only; not a workflow security or evidence binding"
942
+ },
943
+ workflowContext: {
944
+ type: "object",
945
+ description: "Requested provenance scope shared by each request",
946
+ properties: {
947
+ runId: { type: "string", description: "Active durable workflow run ID" },
948
+ stageId: { type: "string", description: "Active workflow stage ID" },
949
+ requirementKey: { type: "string", description: "Required evidence identity these peer replies may satisfy" },
950
+ attempt: { type: "integer", minimum: 1, maximum: 20, description: "Current one-based stage attempt" }
951
+ },
952
+ required: ["runId", "stageId", "requirementKey", "attempt"],
953
+ additionalProperties: false
954
+ },
955
+ ttlMs: { type: "number", minimum: 1e3, maximum: 6048e5, description: "Message TTL in milliseconds" },
956
+ timeoutMs: { type: "number", minimum: 100, maximum: 18e5, description: "Client wait timeout in milliseconds" }
957
+ },
958
+ required: ["targets", "content"],
959
+ additionalProperties: false
960
+ },
961
+ async execute(client, args, context) {
962
+ const targets = Array.isArray(args.targets) ? args.targets.map((t) => requiredString(t, "target")) : [];
963
+ return {
964
+ responses: await client.fanout({
965
+ targets,
966
+ content: requiredString(args.content, "content"),
967
+ ...optionalString2(args.correlationId) ? { correlationId: optionalString2(args.correlationId) } : {},
968
+ ...optionalString2(args.idempotencyKeyPrefix) ? { idempotencyKeyPrefix: optionalString2(args.idempotencyKeyPrefix) } : {},
969
+ ...optionalWorkflowContext(args.workflowContext) ? { workflowContext: optionalWorkflowContext(args.workflowContext) } : {},
970
+ ...typeof args.ttlMs === "number" ? { ttlMs: args.ttlMs } : {},
971
+ ...typeof args.timeoutMs === "number" ? { timeoutMs: args.timeoutMs } : {},
972
+ ...context?.signal ? { signal: context.signal } : {}
973
+ })
974
+ };
975
+ }
976
+ },
977
+ {
978
+ name: "kxm_await",
979
+ group: "peer",
980
+ verb: "await",
981
+ label: "Await peer response",
982
+ description: "Wait until a sent request receives a reply or reaches a terminal error. Capped at 60 seconds (60000ms); longer waits are workflow wait steps.",
983
+ parameters: {
984
+ type: "object",
985
+ properties: {
986
+ messageId: { type: "string", description: "Message ID of the sent request" },
987
+ timeoutMs: {
988
+ type: "number",
989
+ minimum: 100,
990
+ maximum: 6e4,
991
+ default: 6e4,
992
+ description: "Timeout in milliseconds (capped at 60 seconds)"
993
+ }
994
+ },
995
+ required: ["messageId"],
996
+ additionalProperties: false
997
+ },
998
+ async execute(client, args, context) {
999
+ const timeoutMs = Math.min(
1000
+ typeof args.timeoutMs === "number" ? args.timeoutMs : 6e4,
1001
+ 6e4
1002
+ );
1003
+ return await client.awaitResponse(
1004
+ requiredString(args.messageId, "messageId"),
1005
+ timeoutMs,
1006
+ context?.signal
1007
+ );
1008
+ }
1009
+ },
1010
+ {
1011
+ name: "kxm_cancel",
1012
+ group: "peer",
1013
+ verb: "cancel",
1014
+ label: "Cancel peer request",
1015
+ description: "Cancel a queued or delivered request sent by this agent.",
1016
+ parameters: {
1017
+ type: "object",
1018
+ properties: {
1019
+ messageId: { type: "string", description: "Message ID of the request to cancel" }
1020
+ },
1021
+ required: ["messageId"],
1022
+ additionalProperties: false
1023
+ },
1024
+ async execute(client, args) {
1025
+ return await client.cancel(requiredString(args.messageId, "messageId"));
1026
+ }
1027
+ },
1028
+ {
1029
+ name: "kxm_inbox",
1030
+ group: "peer",
1031
+ verb: "inbox",
1032
+ label: "List inbound requests",
1033
+ description: "List inbound peer requests awaiting a reply.",
1034
+ parameters: {
1035
+ type: "object",
1036
+ properties: {},
1037
+ additionalProperties: false
1038
+ },
1039
+ async execute(client, _args, context) {
1040
+ if (context?.inbox) {
1041
+ await reconcileInbox(client, context.inbox, context.notifiedInbox);
1042
+ return { messages: [...context.inbox.values()] };
1043
+ }
1044
+ return { messages: [] };
1045
+ }
1046
+ },
1047
+ {
1048
+ name: "kxm_reply",
1049
+ group: "peer",
1050
+ verb: "reply",
1051
+ label: "Reply to peer request",
1052
+ description: "Reply to an inbound peer request using its message ID.",
1053
+ parameters: {
1054
+ type: "object",
1055
+ properties: {
1056
+ messageId: { type: "string", description: "Message ID of the inbound request" },
1057
+ content: { type: "string", description: "Final response with evidence and remaining risks" }
1058
+ },
1059
+ required: ["messageId", "content"],
1060
+ additionalProperties: false
1061
+ },
1062
+ async execute(client, args, context) {
1063
+ const messageId = requiredString(args.messageId, "messageId");
1064
+ try {
1065
+ const message = await client.reply(messageId, requiredString(args.content, "content"));
1066
+ if (context?.inbox) {
1067
+ context.inbox.delete(messageId);
1068
+ context.notifiedInbox?.delete(messageId);
1069
+ }
1070
+ return { messageId, status: message.status, recipient: message.fromName };
1071
+ } catch (error) {
1072
+ if (context?.inbox && isTerminalMessageError(error)) {
1073
+ context.inbox.delete(messageId);
1074
+ context.notifiedInbox?.delete(messageId);
1075
+ }
1076
+ throw error;
1077
+ }
1078
+ }
1079
+ },
1080
+ {
1081
+ name: "kxm_workflow_list",
1082
+ group: "workflow",
1083
+ verb: "runs",
1084
+ label: "List workflow runs",
1085
+ description: "List durable webhook workflows assigned to this agent.",
1086
+ parameters: {
1087
+ type: "object",
1088
+ properties: {},
1089
+ additionalProperties: false
1090
+ },
1091
+ async execute(client) {
1092
+ return { runs: await client.listWorkflows() };
1093
+ }
1094
+ },
1095
+ {
1096
+ name: "kxm_workflow_get",
1097
+ group: "workflow",
1098
+ verb: "run",
1099
+ label: "Get workflow run",
1100
+ description: "Get a workflow's stages and journal of plans, decisions, contradictions, errors, and lessons.",
1101
+ parameters: {
1102
+ type: "object",
1103
+ properties: {
1104
+ runId: { type: "string", description: "Workflow run ID" }
1105
+ },
1106
+ required: ["runId"],
1107
+ additionalProperties: false
1108
+ },
1109
+ async execute(client, args) {
1110
+ return await client.getWorkflow(requiredString(args.runId, "runId"));
1111
+ }
1112
+ },
1113
+ {
1114
+ name: "kxm_workflow_checkpoint",
1115
+ group: "workflow",
1116
+ verb: "checkpoint",
1117
+ label: "Checkpoint workflow stage",
1118
+ description: "Record a stage result with evidence keyed by the stage's required evidence identities. Cite peer-reply requirements through evidenceRefs so the hub can verify provenance and quorum; caller-authored evidence strings cannot satisfy those policies. Warnings and failures require another attempt until passed or exhausted.",
1119
+ parameters: {
1120
+ type: "object",
1121
+ properties: {
1122
+ runId: { type: "string", description: "Active durable workflow run ID" },
1123
+ stageId: { type: "string", description: "Active stage ID" },
1124
+ status: { type: "string", enum: ["passed", "warning", "failed"], description: "Stage outcome" },
1125
+ summary: { type: "string", description: "Summary of changes, verification, and remaining risks" },
1126
+ evidence: {
1127
+ type: "object",
1128
+ additionalProperties: { type: "string" },
1129
+ maxProperties: 64,
1130
+ description: "Key-value evidence mapping required keys to proof strings"
1131
+ },
1132
+ evidenceRefs: {
1133
+ type: "object",
1134
+ description: "Peer evidence references keyed by required evidence identity",
1135
+ patternProperties: {
1136
+ "^(.*)$": {
1137
+ type: "object",
1138
+ properties: {
1139
+ messageIds: { type: "array", items: { type: "string" }, minItems: 1, maxItems: 16 }
1140
+ },
1141
+ required: ["messageIds"],
1142
+ additionalProperties: false
1143
+ }
1144
+ },
1145
+ additionalProperties: {
1146
+ type: "object",
1147
+ properties: {
1148
+ messageIds: { type: "array", items: { type: "string" }, minItems: 1, maxItems: 16 }
1149
+ },
1150
+ required: ["messageIds"],
1151
+ additionalProperties: false
1152
+ },
1153
+ maxProperties: 32
1154
+ }
1155
+ },
1156
+ required: ["runId", "stageId", "status", "summary"],
1157
+ additionalProperties: false
1158
+ },
1159
+ async execute(client, args) {
1160
+ return await client.checkpointWorkflow(requiredString(args.runId, "runId"), {
1161
+ stageId: requiredString(args.stageId, "stageId"),
1162
+ status: requiredString(args.status, "status"),
1163
+ summary: requiredString(args.summary, "summary"),
1164
+ ...args.evidence && typeof args.evidence === "object" && !Array.isArray(args.evidence) ? { evidence: args.evidence } : {},
1165
+ ...optionalEvidenceRefs(args.evidenceRefs) ? { evidenceRefs: optionalEvidenceRefs(args.evidenceRefs) } : {}
1166
+ });
1167
+ }
1168
+ },
1169
+ {
1170
+ name: "kxm_workflow_record",
1171
+ group: "workflow",
1172
+ verb: "record",
1173
+ label: "Record workflow journal entry",
1174
+ description: "Record a plan, decision, contradiction, error, or lesson for continuous improvement.",
1175
+ parameters: {
1176
+ type: "object",
1177
+ properties: {
1178
+ runId: { type: "string", description: "Active durable workflow run ID" },
1179
+ category: {
1180
+ type: "string",
1181
+ enum: ["plan", "decision", "contradiction", "error", "lesson"],
1182
+ description: "Category of journal entry"
1183
+ },
1184
+ area: {
1185
+ type: "string",
1186
+ enum: ["harness", "gates", "implementation", "workflow", "documentation", "security", "other"],
1187
+ description: "System area"
1188
+ },
1189
+ severity: {
1190
+ type: "string",
1191
+ enum: ["info", "warning", "error"],
1192
+ default: "info",
1193
+ description: "Severity level"
1194
+ },
1195
+ summary: { type: "string", description: "Concise description of the observation or decision" },
1196
+ details: { type: "string", description: "Extended details, context, and reasoning" },
1197
+ evidence: {
1198
+ type: "array",
1199
+ items: { type: "string" },
1200
+ maxItems: 32,
1201
+ description: "Durable evidence strings or URIs"
1202
+ },
1203
+ relatedEntryIds: {
1204
+ type: "array",
1205
+ items: { type: "string" },
1206
+ maxItems: 16,
1207
+ description: "Related previous journal entry IDs"
1208
+ }
1209
+ },
1210
+ required: ["runId", "category", "area", "summary"],
1211
+ additionalProperties: false
1212
+ },
1213
+ async execute(client, args) {
1214
+ return await client.recordWorkflowEntry(requiredString(args.runId, "runId"), {
1215
+ category: requiredString(args.category, "category"),
1216
+ area: requiredString(args.area, "area"),
1217
+ ...optionalString2(args.severity) ? { severity: optionalString2(args.severity) } : {},
1218
+ summary: requiredString(args.summary, "summary"),
1219
+ ...optionalString2(args.details) ? { details: optionalString2(args.details) } : {},
1220
+ ...Array.isArray(args.evidence) ? { evidence: args.evidence } : {},
1221
+ ...Array.isArray(args.relatedEntryIds) ? { relatedEntryIds: args.relatedEntryIds } : {}
1222
+ });
1223
+ }
1224
+ },
1225
+ {
1226
+ name: "kxm_workflow_wait",
1227
+ group: "workflow",
1228
+ verb: "wait",
1229
+ label: "Wait for workflow signal",
1230
+ description: "Pause the active stage until a signed external callback checkpoints it and resumes the coordinator. Cite peer-reply requirements through evidenceRefs so the hub can verify provenance and quorum; verified evidence is accumulated with callback evidence.",
1231
+ parameters: {
1232
+ type: "object",
1233
+ properties: {
1234
+ runId: { type: "string", description: "Active durable workflow run ID" },
1235
+ stageId: { type: "string", description: "Active stage ID" },
1236
+ signalKey: { type: "string", description: "Stable callback key, such as github-pr-42-checks" },
1237
+ summary: { type: "string", description: "What is running externally and what result is expected" },
1238
+ evidence: {
1239
+ type: "object",
1240
+ additionalProperties: { type: "string" },
1241
+ maxProperties: 64,
1242
+ description: "Evidence gathered before the wait"
1243
+ },
1244
+ evidenceRefs: {
1245
+ type: "object",
1246
+ description: "Peer evidence references verified before waiting",
1247
+ patternProperties: {
1248
+ "^(.*)$": {
1249
+ type: "object",
1250
+ properties: {
1251
+ messageIds: { type: "array", items: { type: "string" }, minItems: 1, maxItems: 16 }
1252
+ },
1253
+ required: ["messageIds"],
1254
+ additionalProperties: false
1255
+ }
1256
+ },
1257
+ additionalProperties: {
1258
+ type: "object",
1259
+ properties: {
1260
+ messageIds: { type: "array", items: { type: "string" }, minItems: 1, maxItems: 16 }
1261
+ },
1262
+ required: ["messageIds"],
1263
+ additionalProperties: false
1264
+ },
1265
+ maxProperties: 32
1266
+ },
1267
+ timeoutMs: {
1268
+ type: "number",
1269
+ minimum: 1e3,
1270
+ maximum: 2592e6,
1271
+ description: "Maximum wait duration in milliseconds"
1272
+ }
1273
+ },
1274
+ required: ["runId", "stageId", "signalKey", "summary"],
1275
+ additionalProperties: false
1276
+ },
1277
+ async execute(client, args) {
1278
+ return await client.waitForWorkflowSignal(requiredString(args.runId, "runId"), {
1279
+ stageId: requiredString(args.stageId, "stageId"),
1280
+ signalKey: requiredString(args.signalKey, "signalKey"),
1281
+ summary: requiredString(args.summary, "summary"),
1282
+ ...args.evidence && typeof args.evidence === "object" && !Array.isArray(args.evidence) ? { evidence: args.evidence } : {},
1283
+ ...optionalEvidenceRefs(args.evidenceRefs) ? { evidenceRefs: optionalEvidenceRefs(args.evidenceRefs) } : {},
1284
+ ...typeof args.timeoutMs === "number" ? { timeoutMs: args.timeoutMs } : {}
1285
+ });
1286
+ }
1287
+ },
1288
+ {
1289
+ name: "kxm_improvement_report",
1290
+ group: "workflow",
1291
+ verb: "improve-report",
1292
+ label: "Summarize improvement report",
1293
+ description: "Summarize workflow errors, contradictions, and lessons by improvement area.",
1294
+ parameters: {
1295
+ type: "object",
1296
+ properties: {},
1297
+ additionalProperties: false
1298
+ },
1299
+ async execute(client) {
1300
+ return await client.improvementReport();
1301
+ }
1302
+ },
1303
+ {
1304
+ name: "kxm_context",
1305
+ group: "context",
1306
+ verb: "get",
1307
+ label: "Get KXM context packet",
1308
+ description: "Normal entry point for KXM context. Assembles a token-budgeted role-aware context packet from durable journal evidence, temporal state, knowledge, episodes, and skills. Superseded and rejected records are excluded. Use KXM context tools instead of provider-specific memory APIs.",
1309
+ parameters: {
1310
+ type: "object",
1311
+ properties: {
1312
+ project: { type: "string", description: "Project scope (must be the client's project)" },
1313
+ role: {
1314
+ type: "string",
1315
+ description: "Requesting role: repro, planner, critic, implementer, verifier, or custom"
1316
+ },
1317
+ task: { type: "string", description: "What the role is trying to accomplish" },
1318
+ workflowRunId: { type: "string", description: "Workflow run scope" },
1319
+ stageId: { type: "string", description: "Workflow stage scope" },
1320
+ budgetTokens: { type: "integer", description: "Token budget; defaults to the role policy" },
1321
+ includeKinds: {
1322
+ type: "array",
1323
+ items: { type: "string", enum: ["evidence", "state", "episode", "knowledge", "skill"] },
1324
+ description: "Restrict packet to these item kinds"
1325
+ }
1326
+ },
1327
+ required: ["role", "task"],
1328
+ additionalProperties: false
1329
+ },
1330
+ async execute(client, args) {
1331
+ return await client.contextGet({
1332
+ project: resolveProject(client, args.project),
1333
+ role: requiredString(args.role, "role"),
1334
+ task: requiredString(args.task, "task"),
1335
+ ...optionalString2(args.workflowRunId) ? { workflowRunId: optionalString2(args.workflowRunId) } : {},
1336
+ ...optionalString2(args.stageId) ? { stageId: optionalString2(args.stageId) } : {},
1337
+ ...typeof args.budgetTokens === "number" ? { budgetTokens: args.budgetTokens } : {},
1338
+ ...Array.isArray(args.includeKinds) ? { includeKinds: args.includeKinds } : {}
1339
+ });
1340
+ }
1341
+ },
1342
+ {
1343
+ name: "kxm_recall",
1344
+ group: "context",
1345
+ verb: "recall",
1346
+ label: "Recall context metadata",
1347
+ description: "Search durable context records for a project by query; returns bounded metadata only.",
1348
+ parameters: {
1349
+ type: "object",
1350
+ properties: {
1351
+ project: { type: "string", description: "Project identifier" },
1352
+ query: { type: "string", description: "Query string" },
1353
+ kinds: { type: "array", items: { type: "string" }, description: "Kinds filter" },
1354
+ limit: { type: "integer", minimum: 1, maximum: 100, description: "Maximum results" }
1355
+ },
1356
+ additionalProperties: false
1357
+ },
1358
+ async execute(client, args) {
1359
+ return await client.contextRecall({
1360
+ project: resolveProject(client, args.project),
1361
+ ...optionalString2(args.query) ? { query: optionalString2(args.query) } : {},
1362
+ ...Array.isArray(args.kinds) ? { kinds: args.kinds } : {},
1363
+ ...typeof args.limit === "number" ? { limit: args.limit } : {}
1364
+ });
1365
+ }
1366
+ },
1367
+ {
1368
+ name: "kxm_state",
1369
+ group: "context",
1370
+ verb: "state",
1371
+ label: "Get temporal state",
1372
+ description: "Current value for one temporal state key, optionally as of a historical timestamp.",
1373
+ parameters: {
1374
+ type: "object",
1375
+ properties: {
1376
+ project: { type: "string", description: "Project identifier" },
1377
+ key: { type: "string", description: "State key" },
1378
+ asOf: { type: "string", description: "ISO-8601 timestamp for historical queries" }
1379
+ },
1380
+ required: ["key"],
1381
+ additionalProperties: false
1382
+ },
1383
+ async execute(client, args) {
1384
+ return await client.contextState({
1385
+ project: resolveProject(client, args.project),
1386
+ key: requiredString(args.key, "key"),
1387
+ ...optionalString2(args.asOf) ? { asOf: optionalString2(args.asOf) } : {}
1388
+ });
1389
+ }
1390
+ },
1391
+ {
1392
+ name: "kxm_episode",
1393
+ group: "context",
1394
+ verb: "episode",
1395
+ label: "Get workflow episodes",
1396
+ description: "Episodic learning from workflow journals: errors, lessons, observations, experiments for a project.",
1397
+ parameters: {
1398
+ type: "object",
1399
+ properties: {
1400
+ project: { type: "string", description: "Project identifier" },
1401
+ workflowRunId: { type: "string", description: "Optional workflow run scope" }
1402
+ },
1403
+ additionalProperties: false
1404
+ },
1405
+ async execute(client, args) {
1406
+ return await client.contextEpisode({
1407
+ project: resolveProject(client, args.project),
1408
+ ...optionalString2(args.workflowRunId) ? { workflowRunId: optionalString2(args.workflowRunId) } : {}
1409
+ });
1410
+ }
1411
+ },
1412
+ {
1413
+ name: "kxm_promote",
1414
+ group: "context",
1415
+ verb: "promote",
1416
+ label: "Propose state promotion",
1417
+ description: "Propose a change to one authoritative state key. Promotion requires durable evidence.",
1418
+ parameters: {
1419
+ type: "object",
1420
+ properties: {
1421
+ project: { type: "string", description: "Project identifier" },
1422
+ key: { type: "string", description: "State key to promote" },
1423
+ summary: { type: "string", description: "Promotion summary" },
1424
+ authority: {
1425
+ type: "string",
1426
+ enum: ["policy", "instruction", "evidence", "hypothesis"],
1427
+ description: "Authority class"
1428
+ },
1429
+ confidence: {
1430
+ type: "string",
1431
+ enum: ["verified", "probable", "uncertain"],
1432
+ description: "Confidence level"
1433
+ },
1434
+ evidenceRefs: {
1435
+ type: "array",
1436
+ items: { type: "string" },
1437
+ minItems: 1,
1438
+ maxItems: 32,
1439
+ description: "Evidence item references backing the promotion"
1440
+ }
1441
+ },
1442
+ required: ["key", "summary", "authority", "confidence", "evidenceRefs"],
1443
+ additionalProperties: false
1444
+ },
1445
+ async execute(client, args) {
1446
+ return await client.contextStatePropose({
1447
+ project: resolveProject(client, args.project),
1448
+ key: requiredString(args.key, "key"),
1449
+ summary: requiredString(args.summary, "summary"),
1450
+ authority: requiredString(args.authority, "authority"),
1451
+ confidence: requiredString(args.confidence, "confidence"),
1452
+ evidenceRefs: Array.isArray(args.evidenceRefs) ? args.evidenceRefs : []
1453
+ });
1454
+ }
1455
+ }
1456
+ ];
1457
+ var AGENT_COMMANDS_MAP = new Map(
1458
+ AGENT_COMMANDS.map((cmd) => [cmd.name, cmd])
1459
+ );
1460
+ function getMcpTools() {
1461
+ return AGENT_COMMANDS.map((cmd) => ({
1462
+ name: cmd.name,
1463
+ description: cmd.description,
1464
+ inputSchema: cmd.parameters
1465
+ }));
1466
+ }
1467
+ function getPiToolDefinitions() {
1468
+ return AGENT_COMMANDS.map((cmd) => ({
1469
+ name: cmd.name,
1470
+ label: cmd.label,
1471
+ description: cmd.description,
1472
+ parameters: cmd.parameters
1473
+ }));
1474
+ }
1475
+ function getCliAgentCommands() {
1476
+ return AGENT_COMMANDS;
1477
+ }
1478
+ function mintAttemptToken(input) {
1479
+ const toolPolicy = input.toolPolicy ?? (input.allowedTools || input.deniedTools || input.preset ? {
1480
+ ...input.preset ? { preset: input.preset } : {},
1481
+ ...input.allowedTools ? { allow: input.allowedTools, allowedTools: input.allowedTools } : {},
1482
+ ...input.deniedTools ? { deny: input.deniedTools, deniedTools: input.deniedTools } : {}
1483
+ } : void 0);
1484
+ const payload = {
1485
+ schema: "kxm.attempt-token.v1",
1486
+ runId: input.runId,
1487
+ stepId: input.stepId ?? input.stageId ?? "step-1",
1488
+ stepAttempt: input.stepAttempt ?? input.attempt ?? 1,
1489
+ stageId: input.stageId ?? input.stepId ?? "step-1",
1490
+ attempt: input.attempt ?? input.stepAttempt ?? 1,
1491
+ assignmentId: input.assignmentId ?? `assign-${randomUUID2()}`,
1492
+ attemptId: input.attemptId ?? `attempt-${randomUUID2()}`,
1493
+ agentId: input.agentId ?? "agent-worker",
1494
+ issuedAt: (/* @__PURE__ */ new Date()).toISOString(),
1495
+ ...toolPolicy ? { toolPolicy } : {}
1496
+ };
1497
+ return Buffer.from(JSON.stringify(payload)).toString("base64url");
1498
+ }
1499
+ function parseAttemptToken(token) {
1500
+ try {
1501
+ const raw = Buffer.from(token.trim(), "base64url").toString("utf8");
1502
+ const parsed = JSON.parse(raw);
1503
+ if (parsed && typeof parsed === "object" && parsed.schema === "kxm.attempt-token.v1" && typeof parsed.runId === "string") {
1504
+ return parsed;
1505
+ }
1506
+ } catch {
1507
+ return void 0;
1508
+ }
1509
+ return void 0;
1510
+ }
1511
+ function mintSessionToken(input) {
1512
+ const toolPolicy = input?.toolPolicy ?? (input?.allowedTools || input?.deniedTools || input?.preset ? {
1513
+ ...input?.preset ? { preset: input.preset } : {},
1514
+ ...input?.allowedTools ? { allow: input.allowedTools } : {},
1515
+ ...input?.deniedTools ? { deny: input.deniedTools } : {}
1516
+ } : void 0);
1517
+ const payload = {
1518
+ schema: "kxm.session-token.v1",
1519
+ sessionId: input?.sessionId ?? `session-${randomUUID2()}`,
1520
+ issuedAt: (/* @__PURE__ */ new Date()).toISOString(),
1521
+ ...input?.agentName ? { agentName: input.agentName } : {},
1522
+ ...toolPolicy ? { toolPolicy } : {}
1523
+ };
1524
+ return Buffer.from(JSON.stringify(payload)).toString("base64url");
1525
+ }
1526
+ function parseSessionToken(token) {
1527
+ try {
1528
+ const raw = Buffer.from(token.trim(), "base64url").toString("utf8");
1529
+ const parsed = JSON.parse(raw);
1530
+ if (parsed && typeof parsed === "object" && parsed.schema === "kxm.session-token.v1" && typeof parsed.sessionId === "string") {
1531
+ return parsed;
1532
+ }
1533
+ } catch {
1534
+ return void 0;
1535
+ }
1536
+ return void 0;
1537
+ }
1538
+ function matchToolPattern(pattern, toolName) {
1539
+ if (pattern === "*" || pattern === toolName) return true;
1540
+ if (pattern.endsWith("*")) {
1541
+ const prefix = pattern.slice(0, -1);
1542
+ return toolName.startsWith(prefix);
1543
+ }
1544
+ return false;
1545
+ }
1546
+ function isToolAllowed(commandName, policy) {
1547
+ if (!policy) return true;
1548
+ const canonical = commandName.startsWith("kxm_") ? commandName : `kxm_${commandName}`;
1549
+ const bare = commandName.replace(/^kxm_/, "");
1550
+ const denyList = policy.deny ?? policy.deniedTools;
1551
+ if (Array.isArray(denyList)) {
1552
+ for (const d of denyList) {
1553
+ if (d === canonical || d === commandName || d === bare || matchToolPattern(d, canonical)) {
1554
+ return false;
1555
+ }
1556
+ }
1557
+ }
1558
+ const allowList = policy.allow ?? policy.allowedTools;
1559
+ if (Array.isArray(allowList) && allowList.length > 0) {
1560
+ const matched = allowList.some(
1561
+ (a) => a === canonical || a === commandName || a === bare || a === "*" || matchToolPattern(a, canonical)
1562
+ );
1563
+ if (!matched) return false;
1564
+ }
1565
+ if (policy.preset === "read-only") {
1566
+ const mutating = [
1567
+ "kxm_send",
1568
+ "kxm_reply",
1569
+ "kxm_cancel",
1570
+ "kxm_fanout",
1571
+ "kxm_workflow_checkpoint",
1572
+ "kxm_workflow_record",
1573
+ "kxm_workflow_wait",
1574
+ "kxm_promote"
1575
+ ];
1576
+ if (mutating.includes(canonical)) return false;
1577
+ }
1578
+ return true;
1579
+ }
1580
+ function enforceToolPolicy(commandName, env = process.env) {
1581
+ const attemptTokenRaw = env.KXM_ATTEMPT_TOKEN?.trim();
1582
+ if (attemptTokenRaw) {
1583
+ const attempt = parseAttemptToken(attemptTokenRaw);
1584
+ if (!attempt) {
1585
+ return { allowed: false, error: "attempt_token_invalid", detail: "KXM_ATTEMPT_TOKEN is malformed" };
1586
+ }
1587
+ if (!isToolAllowed(commandName, attempt.toolPolicy)) {
1588
+ return {
1589
+ allowed: false,
1590
+ error: "tool_policy_denied",
1591
+ detail: `command ${commandName} is denied by attempt tool policy`
1592
+ };
1593
+ }
1594
+ return { allowed: true };
1595
+ }
1596
+ const sessionTokenRaw = env.KXM_SESSION_TOKEN?.trim();
1597
+ if (sessionTokenRaw) {
1598
+ const session = parseSessionToken(sessionTokenRaw);
1599
+ if (!session) {
1600
+ return { allowed: false, error: "session_token_invalid", detail: "KXM_SESSION_TOKEN is malformed" };
1601
+ }
1602
+ if (!isToolAllowed(commandName, session.toolPolicy)) {
1603
+ return {
1604
+ allowed: false,
1605
+ error: "tool_policy_denied",
1606
+ detail: `command ${commandName} is denied by session tool policy`
1607
+ };
1608
+ }
1609
+ return { allowed: true };
1610
+ }
1611
+ return { allowed: true };
1612
+ }
1613
+
1614
+ // plugins/kxm/src/logger.ts
1615
+ import { appendFileSync, existsSync, mkdirSync, renameSync, statSync, unlinkSync } from "node:fs";
1616
+ import { dirname } from "node:path";
1617
+ var LOG_LEVEL_PRIORITY = {
1618
+ debug: 10,
1619
+ info: 20,
1620
+ warn: 30,
1621
+ error: 40
1622
+ };
1623
+ var DEFAULT_LOG_MAX_BYTES = 10 * 1024 * 1024;
1624
+ var DEFAULT_LOG_MAX_FILES = 3;
1625
+ var SENSITIVE_KEY_PATTERN = /(?:^|_)(?:token|secret|password|apiKey|api_key|authorization|bearer)(?:$|_)/i;
1626
+ var ALLOWED_EXACT_KEYS = /* @__PURE__ */ new Set(["auth", "authType", "authMethod", "authArgs", "canUpdate", "status"]);
1627
+ function redactLogValue(val, key) {
1628
+ if (val === null || val === void 0) return val;
1629
+ if (typeof val === "string") {
1630
+ if (key && SENSITIVE_KEY_PATTERN.test(key) && !ALLOWED_EXACT_KEYS.has(key)) {
1631
+ return "[redacted]";
1632
+ }
1633
+ return redactSecrets(val);
1634
+ }
1635
+ if (typeof val === "number" || typeof val === "boolean") {
1636
+ return val;
1637
+ }
1638
+ if (Array.isArray(val)) {
1639
+ return val.map((item) => redactLogValue(item, key));
1640
+ }
1641
+ if (typeof val === "object") {
1642
+ const out = {};
1643
+ for (const [k, v] of Object.entries(val)) {
1644
+ out[k] = redactLogValue(v, k);
1645
+ }
1646
+ return out;
1647
+ }
1648
+ return String(val);
1649
+ }
1650
+ function rotateLogFiles(filePath, maxFiles) {
1651
+ for (let i = maxFiles; i >= 1; i--) {
1652
+ const current = `${filePath}.${i}`;
1653
+ if (existsSync(current)) {
1654
+ if (i >= maxFiles) {
1655
+ try {
1656
+ unlinkSync(current);
1657
+ } catch {
1658
+ }
1659
+ } else {
1660
+ try {
1661
+ renameSync(current, `${filePath}.${i + 1}`);
1662
+ } catch {
1663
+ }
1664
+ }
1665
+ }
1666
+ }
1667
+ if (existsSync(filePath)) {
1668
+ try {
1669
+ renameSync(filePath, `${filePath}.1`);
1670
+ } catch {
1671
+ }
1672
+ }
1673
+ }
1674
+ function createLogger(options) {
1675
+ const component = options.component;
1676
+ const filePath = options.path;
1677
+ const maxBytes = Math.max(100, options.maxBytes ?? DEFAULT_LOG_MAX_BYTES);
1678
+ const maxFiles = Math.max(1, options.maxFiles ?? DEFAULT_LOG_MAX_FILES);
1679
+ const configuredLevel = options.level ?? "info";
1680
+ const isDaemon = Boolean(options.daemon ?? (process.env.KXM_DAEMON === "1" || process.env.KXM_DAEMON === "true"));
1681
+ const shouldStdout = options.stdout ?? !isDaemon;
1682
+ const correlationDefaults = options.correlation ?? {};
1683
+ let currentSize = 0;
1684
+ if (filePath && existsSync(filePath)) {
1685
+ try {
1686
+ currentSize = statSync(filePath).size;
1687
+ } catch {
1688
+ currentSize = 0;
1689
+ }
1690
+ }
1691
+ function emit(level, entryOrEvent, extra) {
1692
+ const minPriority = LOG_LEVEL_PRIORITY[configuredLevel] ?? LOG_LEVEL_PRIORITY.info;
1693
+ const currentPriority = LOG_LEVEL_PRIORITY[level] ?? LOG_LEVEL_PRIORITY.info;
1694
+ if (currentPriority < minPriority) return;
1695
+ let base;
1696
+ if (typeof entryOrEvent === "string") {
1697
+ base = { event: entryOrEvent, ...extra };
1698
+ } else {
1699
+ base = { ...entryOrEvent, ...extra };
1700
+ }
1701
+ const timestamp = typeof base.timestamp === "string" ? base.timestamp : (/* @__PURE__ */ new Date()).toISOString();
1702
+ delete base.timestamp;
1703
+ delete base.level;
1704
+ delete base.component;
1705
+ const payload = {
1706
+ timestamp,
1707
+ level,
1708
+ component,
1709
+ ...correlationDefaults,
1710
+ ...base
1711
+ };
1712
+ const sanitized = redactLogValue(payload);
1713
+ const line = `${JSON.stringify(sanitized)}
1714
+ `;
1715
+ if (filePath) {
1716
+ const lineBytes = Buffer.byteLength(line, "utf8");
1717
+ if (currentSize + lineBytes > maxBytes) {
1718
+ rotateLogFiles(filePath, maxFiles);
1719
+ currentSize = 0;
1720
+ }
1721
+ try {
1722
+ mkdirSync(dirname(filePath), { recursive: true });
1723
+ appendFileSync(filePath, line, { encoding: "utf8", mode: 384 });
1724
+ currentSize += lineBytes;
1725
+ } catch {
1726
+ }
1727
+ }
1728
+ if (shouldStdout) {
1729
+ process.stdout.write(line);
1730
+ }
1731
+ }
1732
+ const logFn = ((entryOrEvent, extra) => {
1733
+ let lvl = "info";
1734
+ if (typeof entryOrEvent === "object" && entryOrEvent !== null && typeof entryOrEvent.level === "string") {
1735
+ const candidate = entryOrEvent.level.toLowerCase();
1736
+ if (candidate === "debug" || candidate === "info" || candidate === "warn" || candidate === "error") {
1737
+ lvl = candidate;
1738
+ }
1739
+ }
1740
+ emit(lvl, entryOrEvent, extra);
1741
+ });
1742
+ logFn.info = (entryOrEvent, extra) => emit("info", entryOrEvent, extra);
1743
+ logFn.warn = (entryOrEvent, extra) => emit("warn", entryOrEvent, extra);
1744
+ logFn.error = (entryOrEvent, extra) => emit("error", entryOrEvent, extra);
1745
+ logFn.debug = (entryOrEvent, extra) => emit("debug", entryOrEvent, extra);
1746
+ logFn.child = (sub) => {
1747
+ return createLogger({
1748
+ ...options,
1749
+ component: sub.component ? `${component}.${sub.component}` : component,
1750
+ correlation: { ...correlationDefaults, ...sub.correlation }
1751
+ });
1752
+ };
1753
+ logFn.close = () => {
1754
+ };
1755
+ Object.defineProperty(logFn, "options", {
1756
+ value: Object.freeze({ ...options }),
1757
+ writable: false,
1758
+ enumerable: true
1759
+ });
1760
+ return logFn;
1761
+ }
1762
+ export {
1763
+ AGENT_COMMANDS,
1764
+ AGENT_COMMANDS_MAP,
1765
+ BEHAVIORAL_HASH_VERSION,
1766
+ DEFAULT_LOG_MAX_BYTES,
1767
+ DEFAULT_LOG_MAX_FILES,
1768
+ DEFAULT_MAX_HOPS,
1769
+ DEFAULT_MESSAGE_RETENTION_MS,
1770
+ DEFAULT_MESSAGE_TTL_MS,
1771
+ DEFAULT_PORT,
1772
+ DEFAULT_RATE_LIMIT_MAX,
1773
+ DEFAULT_RATE_LIMIT_WINDOW_MS,
1774
+ DEFAULT_STALE_AFTER_MS,
1775
+ LOG_LEVEL_PRIORITY,
1776
+ MAX_BODY_BYTES,
1777
+ MAX_CONTENT_CHARS,
1778
+ MAX_CONTEXT_ITEM_IDS,
1779
+ MAX_MESSAGE_TTL_MS,
1780
+ MAX_PROVIDER_METADATA_FIELDS,
1781
+ MAX_SKILL_REFS,
1782
+ MIN_MESSAGE_RETENTION_MS,
1783
+ MIN_MESSAGE_TTL_MS,
1784
+ ProtocolError,
1785
+ ROUTING_RECORD_SCHEMA,
1786
+ ROUTING_RECORD_V2_SCHEMA,
1787
+ ROUTING_REPORT_SCHEMA,
1788
+ WORKER_RESULT_SCHEMA,
1789
+ WORKER_SCHEMA,
1790
+ agentWorker,
1791
+ behavioralConfigHash,
1792
+ compareRoutingRecords,
1793
+ createLogger,
1794
+ enforceToolPolicy,
1795
+ formatRoutingReport,
1796
+ gateWorker,
1797
+ generateRoutingReport,
1798
+ getCliAgentCommands,
1799
+ getMcpTools,
1800
+ getPiToolDefinitions,
1801
+ groupByBehavior,
1802
+ isQuotaExhausted,
1803
+ isToolAllowed,
1804
+ looksLikeSecret,
1805
+ mintAttemptToken,
1806
+ mintSessionToken,
1807
+ newId,
1808
+ nowIso,
1809
+ optionalString,
1810
+ parseAttemptToken,
1811
+ parseBoundedInteger,
1812
+ parseDeliveryMode,
1813
+ parseRoutingRecord,
1814
+ parseRoutingRecordV2,
1815
+ parseSessionToken,
1816
+ reconcileInbox,
1817
+ redactLogValue,
1818
+ redactSecrets,
1819
+ redactStringList,
1820
+ requireString,
1821
+ rotateLogFiles,
1822
+ workerResult
1823
+ };