@kontextmind/kxm 0.7.17 → 0.7.18

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 (30) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/package.json +8 -6
  3. package/plugins/kxm/.claude-plugin/plugin.json +1 -1
  4. package/plugins/kxm/dist/cli.js +43 -39
  5. package/plugins/kxm/dist/core.js +3 -2
  6. package/plugins/kxm/dist/extension.js +34472 -11328
  7. package/plugins/kxm/dist/mcp-server.js +1 -1
  8. package/plugins/kxm/dist/runtime.js +43 -39
  9. package/plugins/kxm/dist/server.js +43 -39
  10. package/plugins/kxm/dist/vnext-runtime-supervisor.js +40 -37
  11. package/plugins/kxm/package.json +4 -1
  12. package/plugins/kxm/src/extension.ts +9 -0
  13. package/plugins/kxm/src/mcp-server.ts +1 -1
  14. package/plugins/kxm/src/providers/claude-bridge/LICENSE +21 -0
  15. package/plugins/kxm/src/providers/claude-bridge/README.md +18 -0
  16. package/plugins/kxm/src/providers/claude-bridge/config.ts +53 -0
  17. package/plugins/kxm/src/providers/claude-bridge/convert.ts +180 -0
  18. package/plugins/kxm/src/providers/claude-bridge/index.ts +28 -0
  19. package/plugins/kxm/src/providers/claude-bridge/mcp-server.ts +59 -0
  20. package/plugins/kxm/src/providers/claude-bridge/models.ts +166 -0
  21. package/plugins/kxm/src/providers/claude-bridge/pi-compat.ts +215 -0
  22. package/plugins/kxm/src/providers/claude-bridge/prompt-stream.ts +89 -0
  23. package/plugins/kxm/src/providers/claude-bridge/provider.ts +286 -0
  24. package/plugins/kxm/src/providers/claude-bridge/query-state.ts +72 -0
  25. package/plugins/kxm/src/providers/claude-bridge/redact.ts +26 -0
  26. package/plugins/kxm/src/providers/claude-bridge/register.ts +124 -0
  27. package/plugins/kxm/src/providers/claude-bridge/stream.ts +390 -0
  28. package/plugins/kxm/src/providers/claude-bridge/tools.ts +140 -0
  29. package/plugins/kxm/src/redact.ts +3 -2
  30. package/scripts/build-runtime.mjs +5 -1
@@ -0,0 +1,390 @@
1
+ // @ts-nocheck — vendored snapshot; host tsconfig is stricter than upstream.
2
+ import type { AssistantMessage, AssistantMessageEventStream, Model } from "./pi-compat.ts";
3
+ import { QueryContext } from "./query-state.ts";
4
+ import { mapToolArgs, piToolNameFor } from "./tools.ts";
5
+
6
+ export type SdkMessage = {
7
+ type: string;
8
+ subtype?: string;
9
+ is_error?: boolean;
10
+ result?: string;
11
+ errors?: unknown;
12
+ error?: unknown;
13
+ event?: Record<string, any>;
14
+ message?: { content?: Array<Record<string, any>>; usage?: Record<string, number | undefined> };
15
+ session_id?: string;
16
+ rate_limit_info?: {
17
+ status?: string;
18
+ rateLimitType?: string;
19
+ resetsAt?: number;
20
+ utilization?: number;
21
+ surpassedThreshold?: number;
22
+ };
23
+ modelUsage?: Record<string, { contextWindow?: number; maxOutputTokens?: number }>;
24
+ };
25
+
26
+ const completedStreams = new WeakSet<object>();
27
+
28
+ export function markStreamComplete(stream: AssistantMessageEventStream | null): void {
29
+ if (stream) completedStreams.add(stream as object);
30
+ }
31
+
32
+ export function claimCurrentPiStream(
33
+ stream: AssistantMessageEventStream,
34
+ _label: string,
35
+ c: QueryContext,
36
+ ): void {
37
+ c.currentPiStream = stream;
38
+ }
39
+
40
+ export function ensureTurnStarted(c: QueryContext): void {
41
+ if (!c.turnStarted && c.currentPiStream && c.turnOutput) {
42
+ c.currentPiStream.push({ type: "start", partial: c.turnOutput });
43
+ c.turnStarted = true;
44
+ }
45
+ }
46
+
47
+ export function mapStopReason(reason: string | undefined): "stop" | "length" | "toolUse" {
48
+ switch (reason) {
49
+ case "tool_use":
50
+ return "toolUse";
51
+ case "max_tokens":
52
+ return "length";
53
+ default:
54
+ return "stop";
55
+ }
56
+ }
57
+
58
+ export function parsePartialJson(input: string, fallback: Record<string, unknown>): Record<string, unknown> {
59
+ if (!input) return fallback;
60
+ try {
61
+ return JSON.parse(input);
62
+ } catch {
63
+ return fallback;
64
+ }
65
+ }
66
+
67
+ export function updateUsage(
68
+ output: AssistantMessage,
69
+ usage: Record<string, number | undefined>,
70
+ _model?: Model,
71
+ ): void {
72
+ if (usage.input_tokens != null) output.usage.input = usage.input_tokens;
73
+ if (usage.output_tokens != null) output.usage.output = usage.output_tokens;
74
+ if (usage.cache_read_input_tokens != null) output.usage.cacheRead = usage.cache_read_input_tokens;
75
+ if (usage.cache_creation_input_tokens != null) output.usage.cacheWrite = usage.cache_creation_input_tokens;
76
+ const reasoning = usage.reasoning_tokens ?? usage.thinking_tokens;
77
+ if (reasoning != null) output.usage.reasoning = reasoning;
78
+ output.usage.totalTokens =
79
+ output.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite;
80
+ }
81
+
82
+ export function resultErrorText(message: SdkMessage): string | undefined {
83
+ if (message.subtype === "success") {
84
+ return message.is_error ? message.result || "Claude Code reported an error" : undefined;
85
+ }
86
+ if (Array.isArray(message.errors) && message.errors.length) return message.errors.map(String).join("\n");
87
+ if (typeof message.error === "string") return message.error;
88
+ if (message.type === "result") return `Claude Code failed: ${message.subtype ?? "unknown result"}`;
89
+ return undefined;
90
+ }
91
+
92
+ export function describeRateLimitFailure(
93
+ rejection: { rateLimitType?: string; resetsAt?: number },
94
+ failure: string,
95
+ ): string {
96
+ const kind = rejection.rateLimitType ? ` (${rejection.rateLimitType})` : "";
97
+ const resets = rejection.resetsAt
98
+ ? ` — resets ${new Date(rejection.resetsAt * 1000).toLocaleTimeString()}`
99
+ : "";
100
+ return `Claude rate limit${kind}${resets}: ${failure}`;
101
+ }
102
+
103
+ export function finalizeCurrentStream(c: QueryContext, stopReason?: string): void {
104
+ if (!c.currentPiStream || !c.turnOutput) return;
105
+ if (!c.turnStarted) ensureTurnStarted(c);
106
+ const stream = c.currentPiStream;
107
+ if (c.turnOutput.stopReason === "error") {
108
+ stream.push({ type: "error", reason: "error", error: c.turnOutput });
109
+ } else {
110
+ const reason = stopReason === "length" ? "length" : "stop";
111
+ stream.push({ type: "done", reason, message: c.turnOutput });
112
+ }
113
+ markStreamComplete(stream);
114
+ stream.end();
115
+ c.currentPiStream = null;
116
+ }
117
+
118
+ export function processStreamEvent(
119
+ message: SdkMessage,
120
+ customToolNameToPi: Map<string, string>,
121
+ model: Model,
122
+ c: QueryContext,
123
+ ): void {
124
+ if (!c.currentPiStream || !c.turnOutput) return;
125
+ c.turnSawStreamEvent = true;
126
+ const event = message.event;
127
+ if (!event) return;
128
+
129
+ if (event.type === "message_start") {
130
+ c.turnToolCallIds = [];
131
+ if (event.message?.usage) updateUsage(c.turnOutput, event.message.usage, model);
132
+ return;
133
+ }
134
+
135
+ if (event.type === "content_block_start") {
136
+ ensureTurnStarted(c);
137
+ if (event.content_block?.type === "text") {
138
+ c.turnBlocks.push({ type: "text", text: "", index: event.index });
139
+ c.currentPiStream.push({ type: "text_start", contentIndex: c.turnBlocks.length - 1, partial: c.turnOutput });
140
+ } else if (event.content_block?.type === "thinking") {
141
+ c.turnBlocks.push({ type: "thinking", thinking: "", thinkingSignature: "", index: event.index });
142
+ c.currentPiStream.push({
143
+ type: "thinking_start",
144
+ contentIndex: c.turnBlocks.length - 1,
145
+ partial: c.turnOutput,
146
+ });
147
+ } else if (event.content_block?.type === "tool_use") {
148
+ const piName = piToolNameFor(event.content_block.name, customToolNameToPi);
149
+ if (!piName) return;
150
+ c.turnSawToolCall = true;
151
+ c.turnToolCallIds.push(event.content_block.id);
152
+ c.turnBlocks.push({
153
+ type: "toolCall",
154
+ id: event.content_block.id,
155
+ name: piName,
156
+ arguments: event.content_block.input ?? {},
157
+ partialJson: "",
158
+ index: event.index,
159
+ });
160
+ c.currentPiStream.push({
161
+ type: "toolcall_start",
162
+ contentIndex: c.turnBlocks.length - 1,
163
+ partial: c.turnOutput,
164
+ });
165
+ }
166
+ return;
167
+ }
168
+
169
+ if (event.type === "content_block_delta") {
170
+ const index = c.turnBlocks.findIndex((b) => b.index === event.index);
171
+ const block = c.turnBlocks[index];
172
+ if (!block) return;
173
+ if (event.delta?.type === "text_delta" && block.type === "text") {
174
+ block.text += event.delta.text;
175
+ c.currentPiStream.push({
176
+ type: "text_delta",
177
+ contentIndex: index,
178
+ delta: event.delta.text,
179
+ partial: c.turnOutput,
180
+ });
181
+ } else if (event.delta?.type === "thinking_delta" && block.type === "thinking") {
182
+ block.thinking += event.delta.thinking;
183
+ c.currentPiStream.push({
184
+ type: "thinking_delta",
185
+ contentIndex: index,
186
+ delta: event.delta.thinking,
187
+ partial: c.turnOutput,
188
+ });
189
+ } else if (event.delta?.type === "input_json_delta" && block.type === "toolCall") {
190
+ block.partialJson += event.delta.partial_json;
191
+ block.arguments = parsePartialJson(block.partialJson, block.arguments);
192
+ c.currentPiStream.push({
193
+ type: "toolcall_delta",
194
+ contentIndex: index,
195
+ delta: event.delta.partial_json,
196
+ partial: c.turnOutput,
197
+ });
198
+ } else if (event.delta?.type === "signature_delta" && block.type === "thinking") {
199
+ block.thinkingSignature = (block.thinkingSignature ?? "") + event.delta.signature;
200
+ }
201
+ return;
202
+ }
203
+
204
+ if (event.type === "content_block_stop") {
205
+ const index = c.turnBlocks.findIndex((b) => b.index === event.index);
206
+ const block = c.turnBlocks[index];
207
+ if (!block) return;
208
+ delete block.index;
209
+ if (block.type === "text") {
210
+ c.currentPiStream.push({ type: "text_end", contentIndex: index, content: block.text, partial: c.turnOutput });
211
+ } else if (block.type === "thinking") {
212
+ c.currentPiStream.push({
213
+ type: "thinking_end",
214
+ contentIndex: index,
215
+ content: block.thinking,
216
+ partial: c.turnOutput,
217
+ });
218
+ } else if (block.type === "toolCall") {
219
+ c.turnSawToolCall = true;
220
+ block.arguments = mapToolArgs(block.name, parsePartialJson(block.partialJson, block.arguments));
221
+ delete block.partialJson;
222
+ c.currentPiStream.push({ type: "toolcall_end", contentIndex: index, toolCall: block, partial: c.turnOutput });
223
+ }
224
+ return;
225
+ }
226
+
227
+ if (event.type === "message_delta") {
228
+ c.turnOutput.stopReason = mapStopReason(event.delta?.stop_reason);
229
+ if (event.usage) updateUsage(c.turnOutput, event.usage, model);
230
+ return;
231
+ }
232
+
233
+ if (event.type === "message_stop" && c.turnSawToolCall) {
234
+ c.turnOutput.stopReason = "toolUse";
235
+ const stream = c.currentPiStream;
236
+ stream.push({ type: "done", reason: "toolUse", message: c.turnOutput });
237
+ markStreamComplete(stream);
238
+ stream.end();
239
+ c.currentPiStream = null;
240
+ }
241
+ }
242
+
243
+ export function processAssistantMessage(
244
+ message: SdkMessage,
245
+ model: Model,
246
+ customToolNameToPi: Map<string, string>,
247
+ c: QueryContext,
248
+ ): void {
249
+ if (c.turnSawStreamEvent) return;
250
+ const assistantMsg = message.message;
251
+ if (!assistantMsg?.content) return;
252
+ c.turnToolCallIds = [];
253
+ for (const block of assistantMsg.content) {
254
+ if (block.type === "text" && block.text) {
255
+ ensureTurnStarted(c);
256
+ c.turnBlocks.push({ type: "text", text: block.text });
257
+ const idx = c.turnBlocks.length - 1;
258
+ c.currentPiStream?.push({ type: "text_start", contentIndex: idx, partial: c.turnOutput });
259
+ c.currentPiStream?.push({ type: "text_delta", contentIndex: idx, delta: block.text, partial: c.turnOutput });
260
+ c.currentPiStream?.push({ type: "text_end", contentIndex: idx, content: block.text, partial: c.turnOutput });
261
+ } else if (block.type === "thinking") {
262
+ ensureTurnStarted(c);
263
+ c.turnBlocks.push({
264
+ type: "thinking",
265
+ thinking: block.thinking ?? "",
266
+ thinkingSignature: block.signature ?? "",
267
+ });
268
+ const idx = c.turnBlocks.length - 1;
269
+ c.currentPiStream?.push({ type: "thinking_start", contentIndex: idx, partial: c.turnOutput });
270
+ if (block.thinking) {
271
+ c.currentPiStream?.push({
272
+ type: "thinking_delta",
273
+ contentIndex: idx,
274
+ delta: block.thinking,
275
+ partial: c.turnOutput,
276
+ });
277
+ }
278
+ c.currentPiStream?.push({
279
+ type: "thinking_end",
280
+ contentIndex: idx,
281
+ content: block.thinking ?? "",
282
+ partial: c.turnOutput,
283
+ });
284
+ } else if (block.type === "tool_use") {
285
+ const piName = piToolNameFor(block.name, customToolNameToPi);
286
+ if (!piName) continue;
287
+ ensureTurnStarted(c);
288
+ c.turnSawToolCall = true;
289
+ c.turnToolCallIds.push(block.id);
290
+ c.turnBlocks.push({
291
+ type: "toolCall",
292
+ id: block.id,
293
+ name: piName,
294
+ arguments: mapToolArgs(piName, block.input),
295
+ });
296
+ const idx = c.turnBlocks.length - 1;
297
+ c.currentPiStream?.push({ type: "toolcall_start", contentIndex: idx, partial: c.turnOutput });
298
+ c.currentPiStream?.push({ type: "toolcall_end", contentIndex: idx, toolCall: c.turnBlocks[idx], partial: c.turnOutput });
299
+ }
300
+ }
301
+ if (assistantMsg.usage && c.turnOutput) updateUsage(c.turnOutput, assistantMsg.usage, model);
302
+ if (c.turnSawToolCall && c.currentPiStream && c.turnOutput) {
303
+ c.turnOutput.stopReason = "toolUse";
304
+ const stream = c.currentPiStream;
305
+ stream.push({ type: "done", reason: "toolUse", message: c.turnOutput });
306
+ markStreamComplete(stream);
307
+ stream.end();
308
+ c.currentPiStream = null;
309
+ }
310
+ }
311
+
312
+ export async function consumeQuery(
313
+ sdkQuery: AsyncIterable<SdkMessage>,
314
+ customToolNameToPi: Map<string, string>,
315
+ model: Model,
316
+ wasAborted: () => boolean,
317
+ queryCtx: QueryContext,
318
+ ): Promise<{ capturedSessionId?: string }> {
319
+ let capturedSessionId: string | undefined;
320
+
321
+ for await (const message of sdkQuery) {
322
+ if (wasAborted()) break;
323
+ let resultError: string | undefined;
324
+ if (message.type === "result") {
325
+ queryCtx.promptStream?.end();
326
+ resultError = resultErrorText(message);
327
+ if (resultError !== undefined) {
328
+ if (queryCtx.rateLimitRejection) {
329
+ resultError = describeRateLimitFailure(queryCtx.rateLimitRejection, resultError);
330
+ queryCtx.rateLimitRejection = null;
331
+ }
332
+ if (queryCtx.turnOutput) {
333
+ queryCtx.turnOutput.stopReason = "error";
334
+ queryCtx.turnOutput.errorMessage = resultError;
335
+ }
336
+ }
337
+ }
338
+ if (message.type === "rate_limit_event") {
339
+ const info = message.rate_limit_info;
340
+ if (info?.status === "rejected") {
341
+ queryCtx.rateLimitRejection = info;
342
+ queryCtx.lastRateLimitWarnStep = null;
343
+ queryCtx.lastRateLimitWarnThreshold = undefined;
344
+ } else if (info?.status === "allowed") {
345
+ queryCtx.lastRateLimitWarnStep = null;
346
+ queryCtx.lastRateLimitWarnThreshold = undefined;
347
+ } else if (info?.status === "allowed_warning") {
348
+ const percent = Math.round((info.utilization ?? 0) * 100);
349
+ const step = Math.floor(percent / 5);
350
+ const rose = queryCtx.lastRateLimitWarnStep === null || step > queryCtx.lastRateLimitWarnStep;
351
+ if (rose || info.surpassedThreshold !== queryCtx.lastRateLimitWarnThreshold) {
352
+ queryCtx.lastRateLimitWarnStep = step;
353
+ queryCtx.lastRateLimitWarnThreshold = info.surpassedThreshold;
354
+ }
355
+ }
356
+ continue;
357
+ }
358
+ if (!queryCtx.currentPiStream || !queryCtx.turnOutput) continue;
359
+
360
+ switch (message.type) {
361
+ case "stream_event":
362
+ processStreamEvent(message, customToolNameToPi, model, queryCtx);
363
+ break;
364
+ case "assistant":
365
+ processAssistantMessage(message, model, customToolNameToPi, queryCtx);
366
+ break;
367
+ case "result": {
368
+ if (resultError === undefined && !queryCtx.turnSawStreamEvent && message.subtype === "success") {
369
+ ensureTurnStarted(queryCtx);
370
+ const text = message.result || "";
371
+ queryCtx.turnBlocks.push({ type: "text", text });
372
+ const idx = queryCtx.turnBlocks.length - 1;
373
+ queryCtx.currentPiStream?.push({ type: "text_start", contentIndex: idx, partial: queryCtx.turnOutput });
374
+ queryCtx.currentPiStream?.push({ type: "text_delta", contentIndex: idx, delta: text, partial: queryCtx.turnOutput });
375
+ queryCtx.currentPiStream?.push({ type: "text_end", contentIndex: idx, content: text, partial: queryCtx.turnOutput });
376
+ }
377
+ break;
378
+ }
379
+ case "system":
380
+ if (message.subtype === "init" && (message as { session_id?: string }).session_id) {
381
+ capturedSessionId = (message as { session_id?: string }).session_id;
382
+ }
383
+ break;
384
+ default:
385
+ break;
386
+ }
387
+ }
388
+
389
+ return { capturedSessionId };
390
+ }
@@ -0,0 +1,140 @@
1
+ // @ts-nocheck — vendored snapshot; host tsconfig is stricter than upstream.
2
+ import type { Context, Tool } from "./pi-compat.ts";
3
+
4
+ export const MCP_SERVER_NAME = "custom-tools";
5
+ export const MCP_TOOL_PREFIX = `mcp__${MCP_SERVER_NAME}__`;
6
+
7
+ export type McpContent = Array<
8
+ | { type: "text"; text: string }
9
+ | { type: "image"; data: string; mimeType: string }
10
+ >;
11
+
12
+ export interface McpResult {
13
+ content: McpContent;
14
+ isError?: boolean;
15
+ toolCallId?: string;
16
+ [key: string]: unknown;
17
+ }
18
+
19
+ export function pascalCase(name: string): string {
20
+ return name
21
+ .replace(/[-_\s]+(.)?/g, (_, c: string | undefined) => (c ? c.toUpperCase() : ""))
22
+ .replace(/^(.)/, (c) => c.toUpperCase());
23
+ }
24
+
25
+ export const PI_TO_SDK_TOOL_NAME: Record<string, string> = {
26
+ read: "Read",
27
+ write: "Write",
28
+ edit: "Edit",
29
+ bash: "Bash",
30
+ };
31
+
32
+ export function sanitizeToolId(id: string, cache: Map<string, string>): string {
33
+ const existing = cache.get(id);
34
+ if (existing) return existing;
35
+ const clean = id.replace(/[^a-zA-Z0-9_-]/g, "_");
36
+ cache.set(id, clean);
37
+ return clean;
38
+ }
39
+
40
+ export function mapPiToolNameToSdk(name: string, customToolNameToSdk?: Map<string, string>): string {
41
+ if (!name) return "";
42
+ const normalized = name.toLowerCase();
43
+ if (normalized.startsWith(MCP_TOOL_PREFIX)) {
44
+ throw new Error(`mapPiToolNameToSdk: "${name}" is already an SDK tool name — pi history holds pi tool names`);
45
+ }
46
+ if (!customToolNameToSdk) return PI_TO_SDK_TOOL_NAME[normalized] ?? pascalCase(name);
47
+ return customToolNameToSdk.get(name) ?? customToolNameToSdk.get(normalized) ?? `${MCP_TOOL_PREFIX}${name}`;
48
+ }
49
+
50
+ export function piToolNameFor(name: string, customToolNameToPi: Map<string, string>): string | undefined {
51
+ return customToolNameToPi.get(name) ?? customToolNameToPi.get(name.toLowerCase());
52
+ }
53
+
54
+ const SDK_KEY_RENAMES: Record<string, Record<string, string>> = {
55
+ read: { file_path: "path" },
56
+ write: { file_path: "path" },
57
+ edit: {
58
+ file_path: "path",
59
+ old_string: "oldText",
60
+ new_string: "newText",
61
+ old_text: "oldText",
62
+ new_text: "newText",
63
+ },
64
+ };
65
+
66
+ export function mapToolArgs(toolName: string, args: Record<string, unknown> | undefined): Record<string, unknown> {
67
+ const input = args ?? {};
68
+ const renames = SDK_KEY_RENAMES[toolName.toLowerCase()];
69
+ const result: Record<string, unknown> = {};
70
+ for (const [key, value] of Object.entries(input)) {
71
+ const piKey = renames?.[key] ?? key;
72
+ if (!(piKey in result)) result[piKey] = value;
73
+ }
74
+ if (toolName.toLowerCase() === "bash" && result.timeout == null) {
75
+ result.timeout = 120;
76
+ }
77
+ return result;
78
+ }
79
+
80
+ export function toolResultToMcpContent(
81
+ content: string | Array<{ type: string; text?: string; data?: string; mimeType?: string }>,
82
+ ): McpContent {
83
+ if (typeof content === "string") return [{ type: "text", text: content || "" }];
84
+ if (!Array.isArray(content)) return [{ type: "text", text: "" }];
85
+ const blocks: McpContent = [];
86
+ for (const block of content) {
87
+ if (block.type === "text" && block.text) blocks.push({ type: "text", text: block.text });
88
+ else if (block.type === "image" && block.data && block.mimeType) {
89
+ blocks.push({ type: "image", data: block.data, mimeType: block.mimeType });
90
+ }
91
+ }
92
+ return blocks.length ? blocks : [{ type: "text", text: "" }];
93
+ }
94
+
95
+ export function extractAllToolResults(
96
+ messages: Array<{ role: string; content?: unknown; toolCallId?: string; isError?: boolean; [key: string]: unknown }>,
97
+ ): { results: McpResult[]; stopIdx: number } {
98
+ const results: McpResult[] = [];
99
+ let stopIdx = -1;
100
+ for (let i = messages.length - 1; i >= 0; i--) {
101
+ const msg = messages[i];
102
+ if (msg.role === "toolResult") {
103
+ results.unshift({
104
+ content: toolResultToMcpContent(
105
+ msg.content as string | Array<{ type: string; text?: string; data?: string; mimeType?: string }>,
106
+ ),
107
+ isError: msg.isError,
108
+ toolCallId: msg.toolCallId,
109
+ });
110
+ } else if (msg.role === "assistant") {
111
+ stopIdx = i;
112
+ break;
113
+ }
114
+ }
115
+ return { results, stopIdx };
116
+ }
117
+
118
+ export function resolveMcpTools(
119
+ context: Context,
120
+ excludeToolName?: string,
121
+ ): {
122
+ mcpTools: Tool[];
123
+ customToolNameToSdk: Map<string, string>;
124
+ customToolNameToPi: Map<string, string>;
125
+ } {
126
+ const mcpTools: Tool[] = [];
127
+ const customToolNameToSdk = new Map<string, string>();
128
+ const customToolNameToPi = new Map<string, string>();
129
+ if (!context.tools) return { mcpTools, customToolNameToSdk, customToolNameToPi };
130
+ for (const tool of context.tools) {
131
+ if (tool.name === excludeToolName) continue;
132
+ const sdkName = `${MCP_TOOL_PREFIX}${tool.name}`;
133
+ mcpTools.push(tool);
134
+ customToolNameToSdk.set(tool.name, sdkName);
135
+ customToolNameToSdk.set(tool.name.toLowerCase(), sdkName);
136
+ customToolNameToPi.set(sdkName, tool.name);
137
+ customToolNameToPi.set(sdkName.toLowerCase(), tool.name);
138
+ }
139
+ return { mcpTools, customToolNameToSdk, customToolNameToPi };
140
+ }
@@ -1,5 +1,6 @@
1
1
  const SECRET_PATTERNS: RegExp[] = [
2
2
  /\bsk-[A-Za-z0-9_-]{8,}\b/g,
3
+ /\bsk-ant-[A-Za-z0-9_-]{8,}\b/g,
3
4
  /\bghp_[A-Za-z0-9_]{20,}\b/g,
4
5
  /\bgithub_pat_[A-Za-z0-9_]{20,}\b/g,
5
6
  /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/gi,
@@ -7,9 +8,9 @@ const SECRET_PATTERNS: RegExp[] = [
7
8
  /\bya29\.[A-Za-z0-9._~+/-]+=*/g,
8
9
  /\b1\/\/[A-Za-z0-9_-]+/g,
9
10
  /\b1\/[A-Za-z0-9_-]{20,}/g,
10
- /("?(?:access_token|refresh_token|id_token)"?\s*[:=]\s*")[^"]*(")/gi,
11
+ /("?(?:access_token|refresh_token|id_token|sessionKey|session_key|claude_oauth_token|anthropicApiKey)"?\s*[:=]\s*")[^"]*(")/gi,
11
12
  /\bKXM_[A-Z0-9_]*(TOKEN|SECRET|KEY)[A-Z0-9_]*=\S+/gi,
12
- /\b(GITHUB_TOKEN|GH_TOKEN|KXM_AUTH_TOKEN|KXM_WORKFLOW_SIGNAL_SECRET)=\S+/gi,
13
+ /\b(GITHUB_TOKEN|GH_TOKEN|KXM_AUTH_TOKEN|KXM_WORKFLOW_SIGNAL_SECRET|ANTHROPIC_API_KEY|ANTHROPIC_AUTH_TOKEN|CLAUDE_API_KEY)=\S+/gi,
13
14
  /\b[A-Fa-f0-9]{64}\b/g,
14
15
  ];
15
16
 
@@ -25,7 +25,11 @@ await build({
25
25
  target: "node22",
26
26
  outdir: "plugins/kxm/dist",
27
27
  entryNames: "[name]",
28
- external: ["@earendil-works/pi-coding-agent", "typebox"],
28
+ external: [
29
+ "@earendil-works/pi-coding-agent",
30
+ "typebox",
31
+ "@anthropic-ai/claude-agent-sdk",
32
+ ],
29
33
  });
30
34
 
31
35
  const shebang = "#!/usr/bin/env node\n";