@leo000001/claude-code-mcp 1.6.0 → 2.0.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.
- package/CHANGELOG.md +27 -3
- package/README.md +216 -130
- package/SECURITY.md +3 -3
- package/dist/index.js +1634 -1041
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -11,16 +11,16 @@ import { z } from "zod";
|
|
|
11
11
|
var DEFAULT_SESSION_TTL_MS = 30 * 60 * 1e3;
|
|
12
12
|
var DEFAULT_RUNNING_SESSION_MAX_MS = 4 * 60 * 60 * 1e3;
|
|
13
13
|
var DEFAULT_CLEANUP_INTERVAL_MS = 6e4;
|
|
14
|
-
var
|
|
14
|
+
var DEFAULT_EVENT_BUFFER_MAX_SIZE = 1e3;
|
|
15
|
+
var DEFAULT_EVENT_BUFFER_HARD_MAX_SIZE = 2e3;
|
|
16
|
+
var SessionManager = class _SessionManager {
|
|
15
17
|
sessions = /* @__PURE__ */ new Map();
|
|
18
|
+
runtime = /* @__PURE__ */ new Map();
|
|
16
19
|
cleanupTimer;
|
|
17
|
-
sessionTtlMs;
|
|
18
|
-
runningSessionMaxMs;
|
|
19
|
-
constructor(
|
|
20
|
-
this.
|
|
21
|
-
this.runningSessionMaxMs = opts?.runningSessionMaxMs !== void 0 ? opts.runningSessionMaxMs : DEFAULT_RUNNING_SESSION_MAX_MS;
|
|
22
|
-
const cleanupIntervalMs = opts?.cleanupIntervalMs !== void 0 ? opts.cleanupIntervalMs : DEFAULT_CLEANUP_INTERVAL_MS;
|
|
23
|
-
this.cleanupTimer = setInterval(() => this.cleanup(), cleanupIntervalMs);
|
|
20
|
+
sessionTtlMs = DEFAULT_SESSION_TTL_MS;
|
|
21
|
+
runningSessionMaxMs = DEFAULT_RUNNING_SESSION_MAX_MS;
|
|
22
|
+
constructor() {
|
|
23
|
+
this.cleanupTimer = setInterval(() => this.cleanup(), DEFAULT_CLEANUP_INTERVAL_MS);
|
|
24
24
|
if (this.cleanupTimer.unref) {
|
|
25
25
|
this.cleanupTimer.unref();
|
|
26
26
|
}
|
|
@@ -40,7 +40,7 @@ var SessionManager = class {
|
|
|
40
40
|
totalCostUsd: 0,
|
|
41
41
|
cwd: params.cwd,
|
|
42
42
|
model: params.model,
|
|
43
|
-
permissionMode: params.permissionMode ?? "
|
|
43
|
+
permissionMode: params.permissionMode ?? "default",
|
|
44
44
|
allowedTools: params.allowedTools,
|
|
45
45
|
disallowedTools: params.disallowedTools,
|
|
46
46
|
tools: params.tools,
|
|
@@ -69,11 +69,23 @@ var SessionManager = class {
|
|
|
69
69
|
abortController: params.abortController
|
|
70
70
|
};
|
|
71
71
|
this.sessions.set(params.sessionId, info);
|
|
72
|
+
this.runtime.set(params.sessionId, {
|
|
73
|
+
buffer: {
|
|
74
|
+
events: [],
|
|
75
|
+
maxSize: DEFAULT_EVENT_BUFFER_MAX_SIZE,
|
|
76
|
+
hardMaxSize: DEFAULT_EVENT_BUFFER_HARD_MAX_SIZE,
|
|
77
|
+
nextId: 0
|
|
78
|
+
},
|
|
79
|
+
pendingPermissions: /* @__PURE__ */ new Map()
|
|
80
|
+
});
|
|
72
81
|
return info;
|
|
73
82
|
}
|
|
74
83
|
get(sessionId) {
|
|
75
84
|
return this.sessions.get(sessionId);
|
|
76
85
|
}
|
|
86
|
+
updateStatus(sessionId, status) {
|
|
87
|
+
return this.update(sessionId, { status });
|
|
88
|
+
}
|
|
77
89
|
list() {
|
|
78
90
|
return Array.from(this.sessions.values());
|
|
79
91
|
}
|
|
@@ -95,35 +107,199 @@ var SessionManager = class {
|
|
|
95
107
|
info.status = "running";
|
|
96
108
|
info.abortController = abortController;
|
|
97
109
|
info.lastActiveAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
110
|
+
this.clearTerminalEvents(sessionId);
|
|
98
111
|
return info;
|
|
99
112
|
}
|
|
100
|
-
cancel(sessionId) {
|
|
113
|
+
cancel(sessionId, opts) {
|
|
101
114
|
const info = this.sessions.get(sessionId);
|
|
102
115
|
if (!info) return false;
|
|
103
|
-
if (info.status !== "running") return false;
|
|
116
|
+
if (info.status !== "running" && info.status !== "waiting_permission") return false;
|
|
117
|
+
if (info.status === "waiting_permission") {
|
|
118
|
+
this.finishAllPending(
|
|
119
|
+
sessionId,
|
|
120
|
+
{ behavior: "deny", message: "Session cancelled", interrupt: true },
|
|
121
|
+
"cancel"
|
|
122
|
+
);
|
|
123
|
+
}
|
|
104
124
|
if (info.abortController) {
|
|
105
125
|
info.abortController.abort();
|
|
106
126
|
}
|
|
107
127
|
info.status = "cancelled";
|
|
128
|
+
info.cancelledAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
129
|
+
info.cancelledReason = opts?.reason ?? "Session cancelled";
|
|
130
|
+
info.cancelledSource = opts?.source ?? "cancel";
|
|
108
131
|
info.lastActiveAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
109
132
|
return true;
|
|
110
133
|
}
|
|
111
134
|
delete(sessionId) {
|
|
135
|
+
this.finishAllPending(
|
|
136
|
+
sessionId,
|
|
137
|
+
{ behavior: "deny", message: "Session deleted", interrupt: true },
|
|
138
|
+
"cleanup"
|
|
139
|
+
);
|
|
140
|
+
this.runtime.delete(sessionId);
|
|
112
141
|
return this.sessions.delete(sessionId);
|
|
113
142
|
}
|
|
143
|
+
setResult(sessionId, result) {
|
|
144
|
+
const state = this.runtime.get(sessionId);
|
|
145
|
+
if (!state) return;
|
|
146
|
+
state.storedResult = result;
|
|
147
|
+
}
|
|
148
|
+
getResult(sessionId) {
|
|
149
|
+
return this.runtime.get(sessionId)?.storedResult;
|
|
150
|
+
}
|
|
151
|
+
setInitTools(sessionId, tools) {
|
|
152
|
+
const state = this.runtime.get(sessionId);
|
|
153
|
+
if (!state) return;
|
|
154
|
+
state.initTools = tools;
|
|
155
|
+
}
|
|
156
|
+
getInitTools(sessionId) {
|
|
157
|
+
return this.runtime.get(sessionId)?.initTools;
|
|
158
|
+
}
|
|
159
|
+
pushEvent(sessionId, event) {
|
|
160
|
+
const state = this.runtime.get(sessionId);
|
|
161
|
+
if (!state) return void 0;
|
|
162
|
+
const full = _SessionManager.pushEvent(
|
|
163
|
+
state.buffer,
|
|
164
|
+
event,
|
|
165
|
+
(requestId) => state.pendingPermissions.has(requestId)
|
|
166
|
+
);
|
|
167
|
+
const info = this.sessions.get(sessionId);
|
|
168
|
+
if (info) {
|
|
169
|
+
info.lastActiveAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
170
|
+
const data = event.data;
|
|
171
|
+
const toolUseId = typeof data?.tool_use_id === "string" && data.tool_use_id || typeof data?.toolUseID === "string" && data.toolUseID || typeof data?.parent_tool_use_id === "string" && data.parent_tool_use_id || void 0;
|
|
172
|
+
if (toolUseId) info.lastToolUseId = toolUseId;
|
|
173
|
+
}
|
|
174
|
+
return full;
|
|
175
|
+
}
|
|
176
|
+
getLastEventId(sessionId) {
|
|
177
|
+
const state = this.runtime.get(sessionId);
|
|
178
|
+
if (!state) return void 0;
|
|
179
|
+
return state.buffer.nextId > 0 ? state.buffer.nextId - 1 : void 0;
|
|
180
|
+
}
|
|
181
|
+
readEvents(sessionId, cursor) {
|
|
182
|
+
const state = this.runtime.get(sessionId);
|
|
183
|
+
if (!state) return { events: [], nextCursor: cursor ?? 0 };
|
|
184
|
+
return _SessionManager.readEvents(state.buffer, cursor);
|
|
185
|
+
}
|
|
186
|
+
clearTerminalEvents(sessionId) {
|
|
187
|
+
const state = this.runtime.get(sessionId);
|
|
188
|
+
if (!state) return;
|
|
189
|
+
_SessionManager.clearTerminalEvents(state.buffer);
|
|
190
|
+
}
|
|
191
|
+
setPendingPermission(sessionId, req, finish, timeoutMs) {
|
|
192
|
+
const state = this.runtime.get(sessionId);
|
|
193
|
+
const info = this.sessions.get(sessionId);
|
|
194
|
+
if (!state || !info) return false;
|
|
195
|
+
if (!state.pendingPermissions.has(req.requestId)) {
|
|
196
|
+
const timeoutId = setTimeout(() => {
|
|
197
|
+
this.finishRequest(
|
|
198
|
+
sessionId,
|
|
199
|
+
req.requestId,
|
|
200
|
+
{
|
|
201
|
+
behavior: "deny",
|
|
202
|
+
message: `Permission request timed out after ${timeoutMs}ms.`,
|
|
203
|
+
interrupt: false
|
|
204
|
+
},
|
|
205
|
+
"timeout"
|
|
206
|
+
);
|
|
207
|
+
}, timeoutMs);
|
|
208
|
+
state.pendingPermissions.set(req.requestId, { record: req, finish, timeoutId });
|
|
209
|
+
info.status = "waiting_permission";
|
|
210
|
+
info.lastActiveAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
211
|
+
this.pushEvent(sessionId, {
|
|
212
|
+
type: "permission_request",
|
|
213
|
+
data: req,
|
|
214
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
215
|
+
});
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
getPendingPermissionCount(sessionId) {
|
|
221
|
+
return this.runtime.get(sessionId)?.pendingPermissions.size ?? 0;
|
|
222
|
+
}
|
|
223
|
+
listPendingPermissions(sessionId) {
|
|
224
|
+
const state = this.runtime.get(sessionId);
|
|
225
|
+
if (!state) return [];
|
|
226
|
+
return Array.from(state.pendingPermissions.values()).map((p) => p.record).sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
227
|
+
}
|
|
228
|
+
finishRequest(sessionId, requestId, result, source) {
|
|
229
|
+
const state = this.runtime.get(sessionId);
|
|
230
|
+
const info = this.sessions.get(sessionId);
|
|
231
|
+
if (!state || !info) return false;
|
|
232
|
+
const pending = state.pendingPermissions.get(requestId);
|
|
233
|
+
if (!pending) return false;
|
|
234
|
+
let finalResult = result;
|
|
235
|
+
if (finalResult.behavior === "allow") {
|
|
236
|
+
const disallowed = info.disallowedTools;
|
|
237
|
+
if (Array.isArray(disallowed) && disallowed.includes(pending.record.toolName) && pending.record.toolName.trim() !== "") {
|
|
238
|
+
finalResult = {
|
|
239
|
+
behavior: "deny",
|
|
240
|
+
message: `Tool '${pending.record.toolName}' is disallowed by session policy.`,
|
|
241
|
+
interrupt: false
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (pending.timeoutId) clearTimeout(pending.timeoutId);
|
|
246
|
+
state.pendingPermissions.delete(requestId);
|
|
247
|
+
this.pushEvent(sessionId, {
|
|
248
|
+
type: "permission_result",
|
|
249
|
+
data: { requestId, behavior: finalResult.behavior, source },
|
|
250
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
251
|
+
});
|
|
252
|
+
try {
|
|
253
|
+
pending.finish(finalResult);
|
|
254
|
+
} catch {
|
|
255
|
+
}
|
|
256
|
+
if (info.status === "waiting_permission" && state.pendingPermissions.size === 0) {
|
|
257
|
+
info.status = "running";
|
|
258
|
+
info.lastActiveAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
259
|
+
}
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
finishAllPending(sessionId, result, source) {
|
|
263
|
+
const state = this.runtime.get(sessionId);
|
|
264
|
+
if (!state) return;
|
|
265
|
+
for (const requestId of Array.from(state.pendingPermissions.keys())) {
|
|
266
|
+
this.finishRequest(sessionId, requestId, result, source);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
114
269
|
/** Remove sessions that have been idle for too long, or stuck running too long */
|
|
115
270
|
cleanup() {
|
|
116
271
|
const now = Date.now();
|
|
117
272
|
for (const [id, info] of this.sessions) {
|
|
118
273
|
const lastActive = new Date(info.lastActiveAt).getTime();
|
|
119
274
|
if (Number.isNaN(lastActive)) {
|
|
275
|
+
this.finishAllPending(
|
|
276
|
+
id,
|
|
277
|
+
{ behavior: "deny", message: "Session expired", interrupt: true },
|
|
278
|
+
"cleanup"
|
|
279
|
+
);
|
|
120
280
|
this.sessions.delete(id);
|
|
281
|
+
this.runtime.delete(id);
|
|
121
282
|
} else if (info.status === "running" && now - lastActive > this.runningSessionMaxMs) {
|
|
122
283
|
if (info.abortController) info.abortController.abort();
|
|
123
284
|
info.status = "error";
|
|
124
285
|
info.lastActiveAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
125
|
-
} else if (info.status
|
|
286
|
+
} else if (info.status === "waiting_permission" && now - lastActive > this.runningSessionMaxMs) {
|
|
287
|
+
this.finishAllPending(
|
|
288
|
+
id,
|
|
289
|
+
{ behavior: "deny", message: "Session timed out", interrupt: true },
|
|
290
|
+
"cleanup"
|
|
291
|
+
);
|
|
292
|
+
if (info.abortController) info.abortController.abort();
|
|
293
|
+
info.status = "error";
|
|
294
|
+
info.lastActiveAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
295
|
+
} else if (info.status !== "running" && info.status !== "waiting_permission" && now - lastActive > this.sessionTtlMs) {
|
|
296
|
+
this.finishAllPending(
|
|
297
|
+
id,
|
|
298
|
+
{ behavior: "deny", message: "Session expired", interrupt: true },
|
|
299
|
+
"cleanup"
|
|
300
|
+
);
|
|
126
301
|
this.sessions.delete(id);
|
|
302
|
+
this.runtime.delete(id);
|
|
127
303
|
}
|
|
128
304
|
}
|
|
129
305
|
}
|
|
@@ -167,36 +343,97 @@ var SessionManager = class {
|
|
|
167
343
|
destroy() {
|
|
168
344
|
clearInterval(this.cleanupTimer);
|
|
169
345
|
for (const info of this.sessions.values()) {
|
|
170
|
-
|
|
346
|
+
this.finishAllPending(
|
|
347
|
+
info.sessionId,
|
|
348
|
+
{ behavior: "deny", message: "Server shutting down", interrupt: true },
|
|
349
|
+
"destroy"
|
|
350
|
+
);
|
|
351
|
+
if ((info.status === "running" || info.status === "waiting_permission") && info.abortController) {
|
|
171
352
|
info.abortController.abort();
|
|
172
353
|
}
|
|
173
354
|
info.status = "cancelled";
|
|
355
|
+
info.cancelledAt = info.cancelledAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
356
|
+
info.cancelledReason = info.cancelledReason ?? "Server shutting down";
|
|
357
|
+
info.cancelledSource = info.cancelledSource ?? "destroy";
|
|
358
|
+
info.lastActiveAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
static pushEvent(buffer, event, isActivePermissionRequest) {
|
|
362
|
+
const pinned = event.pinned ?? (event.type === "permission_request" || event.type === "permission_result" || event.type === "result" || event.type === "error");
|
|
363
|
+
const full = {
|
|
364
|
+
id: buffer.nextId++,
|
|
365
|
+
type: event.type,
|
|
366
|
+
data: event.data,
|
|
367
|
+
timestamp: event.timestamp,
|
|
368
|
+
pinned
|
|
369
|
+
};
|
|
370
|
+
buffer.events.push(full);
|
|
371
|
+
while (buffer.events.length > buffer.maxSize) {
|
|
372
|
+
const idx = buffer.events.findIndex((e) => !e.pinned);
|
|
373
|
+
if (idx !== -1) {
|
|
374
|
+
buffer.events.splice(idx, 1);
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
const pinnedDropIdx = buffer.events.findIndex((e) => {
|
|
378
|
+
if (e.type === "permission_result") return true;
|
|
379
|
+
if (e.type === "permission_request") {
|
|
380
|
+
const requestId = e.data?.requestId;
|
|
381
|
+
if (typeof requestId !== "string") return true;
|
|
382
|
+
return isActivePermissionRequest ? !isActivePermissionRequest(requestId) : true;
|
|
383
|
+
}
|
|
384
|
+
return false;
|
|
385
|
+
});
|
|
386
|
+
if (pinnedDropIdx === -1) break;
|
|
387
|
+
buffer.events.splice(pinnedDropIdx, 1);
|
|
388
|
+
}
|
|
389
|
+
while (buffer.events.length > buffer.hardMaxSize) {
|
|
390
|
+
const idx = buffer.events.findIndex((e) => {
|
|
391
|
+
if (e.type === "permission_request") {
|
|
392
|
+
const requestId = e.data?.requestId;
|
|
393
|
+
if (typeof requestId !== "string") return true;
|
|
394
|
+
return isActivePermissionRequest ? !isActivePermissionRequest(requestId) : true;
|
|
395
|
+
}
|
|
396
|
+
if (e.type === "permission_result") return true;
|
|
397
|
+
return false;
|
|
398
|
+
});
|
|
399
|
+
if (idx === -1) break;
|
|
400
|
+
buffer.events.splice(idx, 1);
|
|
174
401
|
}
|
|
402
|
+
return full;
|
|
403
|
+
}
|
|
404
|
+
static readEvents(buffer, cursor) {
|
|
405
|
+
let cursorResetTo;
|
|
406
|
+
if (cursor != null) {
|
|
407
|
+
const earliest = buffer.events[0]?.id;
|
|
408
|
+
if (earliest != null && earliest > cursor) cursorResetTo = earliest;
|
|
409
|
+
if (earliest == null && buffer.nextId > cursor) cursorResetTo = buffer.nextId;
|
|
410
|
+
}
|
|
411
|
+
const startFrom = cursorResetTo ?? cursor ?? 0;
|
|
412
|
+
const filtered = buffer.events.filter((e) => e.id >= startFrom);
|
|
413
|
+
const nextCursor = filtered.length > 0 ? filtered[filtered.length - 1].id + 1 : startFrom;
|
|
414
|
+
return { events: filtered, nextCursor, cursorResetTo };
|
|
415
|
+
}
|
|
416
|
+
static clearTerminalEvents(buffer) {
|
|
417
|
+
buffer.events = buffer.events.filter((e) => e.type !== "result" && e.type !== "error");
|
|
175
418
|
}
|
|
176
419
|
};
|
|
177
420
|
|
|
178
|
-
// src/tools/claude-code.ts
|
|
179
|
-
import { query, AbortError } from "@anthropic-ai/claude-agent-sdk";
|
|
180
|
-
|
|
181
421
|
// src/types.ts
|
|
182
|
-
var PERMISSION_MODES = [
|
|
183
|
-
"default",
|
|
184
|
-
"acceptEdits",
|
|
185
|
-
"bypassPermissions",
|
|
186
|
-
"plan",
|
|
187
|
-
"delegate",
|
|
188
|
-
"dontAsk"
|
|
189
|
-
];
|
|
190
422
|
var EFFORT_LEVELS = ["low", "medium", "high", "max"];
|
|
191
423
|
var AGENT_MODELS = ["sonnet", "opus", "haiku", "inherit"];
|
|
192
|
-
var CONFIGURE_ACTIONS = ["enable_bypass", "disable_bypass", "get_config"];
|
|
193
424
|
var SESSION_ACTIONS = ["list", "get", "cancel"];
|
|
194
425
|
var DEFAULT_SETTING_SOURCES = ["user", "project", "local"];
|
|
426
|
+
var CHECK_ACTIONS = ["poll", "respond_permission"];
|
|
427
|
+
var CHECK_RESPONSE_MODES = ["minimal", "full"];
|
|
428
|
+
|
|
429
|
+
// src/tools/query-consumer.ts
|
|
430
|
+
import { AbortError, query } from "@anthropic-ai/claude-agent-sdk";
|
|
195
431
|
|
|
196
432
|
// src/utils/windows.ts
|
|
197
433
|
import { existsSync } from "fs";
|
|
198
434
|
import { execSync } from "child_process";
|
|
199
|
-
import
|
|
435
|
+
import path from "path";
|
|
436
|
+
var { join, dirname, normalize } = path.win32;
|
|
200
437
|
function isWindows() {
|
|
201
438
|
return process.platform === "win32";
|
|
202
439
|
}
|
|
@@ -268,847 +505,1227 @@ function enhanceWindowsError(errorMessage) {
|
|
|
268
505
|
return errorMessage;
|
|
269
506
|
}
|
|
270
507
|
|
|
271
|
-
// src/tools/
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
508
|
+
// src/tools/query-consumer.ts
|
|
509
|
+
var MAX_TRANSIENT_RETRIES = 3;
|
|
510
|
+
var INITIAL_RETRY_DELAY_MS = 1e3;
|
|
511
|
+
function classifyError(err, abortSignal) {
|
|
512
|
+
if (abortSignal.aborted) return "abort";
|
|
513
|
+
if (err instanceof AbortError || err instanceof Error && err.name === "AbortError") {
|
|
514
|
+
return "abort";
|
|
515
|
+
}
|
|
516
|
+
if (err instanceof Error && (err.message.includes("ECONNRESET") || err.message.includes("ETIMEDOUT") || err.message.includes("ECONNREFUSED") || err.message.includes("ENOTFOUND") || err.message.includes("EAI_AGAIN") || err.message.includes("EPIPE") || err.message.includes("stream ended unexpectedly") || err.message.includes("socket hang up"))) {
|
|
517
|
+
return "transient";
|
|
518
|
+
}
|
|
519
|
+
return "fatal";
|
|
520
|
+
}
|
|
521
|
+
function isSystemInitMessage(msg) {
|
|
522
|
+
return msg.type === "system" && msg.subtype === "init";
|
|
523
|
+
}
|
|
524
|
+
function summarizePermission(toolName, input) {
|
|
525
|
+
const keys = Object.keys(input ?? {}).slice(0, 5);
|
|
526
|
+
const suffix = keys.length > 0 ? ` (keys: ${keys.join(", ")})` : "";
|
|
527
|
+
return `${toolName} permission request${suffix}`;
|
|
528
|
+
}
|
|
529
|
+
function describeTool(toolName, toolCache) {
|
|
530
|
+
const tools = toolCache?.getTools();
|
|
531
|
+
const found = tools?.find((t) => t.name === toolName);
|
|
532
|
+
return found?.description;
|
|
533
|
+
}
|
|
534
|
+
function sdkResultToAgentResult(result) {
|
|
535
|
+
const base = {
|
|
536
|
+
sessionId: result.session_id,
|
|
537
|
+
durationMs: result.duration_ms,
|
|
538
|
+
durationApiMs: result.duration_api_ms,
|
|
539
|
+
numTurns: result.num_turns,
|
|
540
|
+
totalCostUsd: result.total_cost_usd,
|
|
541
|
+
stopReason: result.stop_reason,
|
|
542
|
+
usage: result.usage,
|
|
543
|
+
modelUsage: result.modelUsage,
|
|
544
|
+
permissionDenials: result.permission_denials
|
|
545
|
+
};
|
|
546
|
+
if (result.subtype === "success") {
|
|
275
547
|
return {
|
|
276
|
-
|
|
277
|
-
result:
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
numTurns: 0,
|
|
281
|
-
totalCostUsd: 0
|
|
548
|
+
...base,
|
|
549
|
+
result: result.result,
|
|
550
|
+
structuredOutput: result.structured_output,
|
|
551
|
+
isError: false
|
|
282
552
|
};
|
|
283
553
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
554
|
+
const errors = Array.isArray(result.errors) && result.errors.length > 0 ? result.errors.map(String).join("\n") : `Error [${result.subtype}]: Unknown error`;
|
|
555
|
+
return {
|
|
556
|
+
...base,
|
|
557
|
+
result: errors,
|
|
558
|
+
isError: true,
|
|
559
|
+
errorSubtype: result.subtype
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
function errorToAgentResult(sessionId, err) {
|
|
563
|
+
const message = err instanceof Error ? enhanceWindowsError(err.message) : enhanceWindowsError(String(err));
|
|
564
|
+
return {
|
|
565
|
+
sessionId,
|
|
566
|
+
result: `Error [${"INTERNAL" /* INTERNAL */}]: ${message}`,
|
|
567
|
+
isError: true,
|
|
568
|
+
durationMs: 0,
|
|
569
|
+
numTurns: 0,
|
|
570
|
+
totalCostUsd: 0
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
function messageToEvent(msg) {
|
|
574
|
+
if (msg.type === "assistant") {
|
|
302
575
|
return {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
576
|
+
type: "output",
|
|
577
|
+
data: {
|
|
578
|
+
type: "assistant",
|
|
579
|
+
message: msg.message,
|
|
580
|
+
parent_tool_use_id: msg.parent_tool_use_id,
|
|
581
|
+
error: msg.error
|
|
582
|
+
}
|
|
309
583
|
};
|
|
310
584
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
585
|
+
if (msg.type === "tool_use_summary") {
|
|
586
|
+
return { type: "progress", data: { type: "tool_use_summary", summary: msg.summary } };
|
|
587
|
+
}
|
|
588
|
+
if (msg.type === "tool_progress") {
|
|
589
|
+
return {
|
|
590
|
+
type: "progress",
|
|
591
|
+
data: {
|
|
592
|
+
type: "tool_progress",
|
|
593
|
+
tool_use_id: msg.tool_use_id,
|
|
594
|
+
tool_name: msg.tool_name,
|
|
595
|
+
elapsed_time_seconds: msg.elapsed_time_seconds
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
if (msg.type === "auth_status") {
|
|
600
|
+
return {
|
|
601
|
+
type: "progress",
|
|
602
|
+
data: {
|
|
603
|
+
type: "auth_status",
|
|
604
|
+
isAuthenticating: msg.isAuthenticating,
|
|
605
|
+
output: msg.output,
|
|
606
|
+
error: msg.error
|
|
607
|
+
}
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
if (msg.type === "system" && msg.subtype === "status") {
|
|
611
|
+
return {
|
|
612
|
+
type: "progress",
|
|
613
|
+
data: { type: "status", status: msg.status, permissionMode: msg.permissionMode }
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
if (msg.type === "system" && msg.subtype === "task_notification") {
|
|
617
|
+
return {
|
|
618
|
+
type: "progress",
|
|
619
|
+
data: {
|
|
620
|
+
type: "task_notification",
|
|
621
|
+
task_id: msg.task_id,
|
|
622
|
+
status: msg.status,
|
|
623
|
+
summary: msg.summary,
|
|
624
|
+
output_file: msg.output_file
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
return null;
|
|
629
|
+
}
|
|
630
|
+
function consumeQuery(params) {
|
|
631
|
+
let resolveSessionId;
|
|
632
|
+
let rejectSessionId;
|
|
633
|
+
const sdkSessionIdPromise = new Promise((resolve, reject) => {
|
|
634
|
+
resolveSessionId = resolve;
|
|
635
|
+
rejectSessionId = reject;
|
|
636
|
+
});
|
|
637
|
+
const waitForInitSessionId = params.mode !== "start" ? params.waitForInitSessionId ?? false : false;
|
|
638
|
+
const shouldWaitForInit = params.mode === "start" || waitForInitSessionId;
|
|
639
|
+
let sessionIdResolved = false;
|
|
640
|
+
let activeSessionId = "";
|
|
641
|
+
if (params.mode !== "start" && !waitForInitSessionId) {
|
|
642
|
+
sessionIdResolved = true;
|
|
643
|
+
activeSessionId = params.sessionId;
|
|
644
|
+
resolveSessionId(activeSessionId);
|
|
645
|
+
}
|
|
646
|
+
const getSessionId = async () => {
|
|
647
|
+
if (activeSessionId) return activeSessionId;
|
|
648
|
+
activeSessionId = await sdkSessionIdPromise;
|
|
649
|
+
return activeSessionId;
|
|
650
|
+
};
|
|
651
|
+
let initTimeoutId;
|
|
652
|
+
const canUseTool = async (toolName, input, options2) => {
|
|
653
|
+
const sessionId = await getSessionId();
|
|
654
|
+
const sessionInfo = params.sessionManager.get(sessionId);
|
|
655
|
+
if (sessionInfo) {
|
|
656
|
+
if (Array.isArray(sessionInfo.disallowedTools) && sessionInfo.disallowedTools.includes(toolName)) {
|
|
657
|
+
return { behavior: "deny", message: `Tool '${toolName}' is disallowed by session policy.` };
|
|
658
|
+
}
|
|
659
|
+
if (!options2.blockedPath && Array.isArray(sessionInfo.allowedTools) && sessionInfo.allowedTools.includes(toolName)) {
|
|
660
|
+
return { behavior: "allow" };
|
|
661
|
+
}
|
|
319
662
|
}
|
|
320
|
-
const
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
663
|
+
const requestId = `${options2.toolUseID}:${toolName}:${Date.now()}:${Math.random().toString(16).slice(2)}`;
|
|
664
|
+
const record = {
|
|
665
|
+
requestId,
|
|
666
|
+
toolName,
|
|
667
|
+
input,
|
|
668
|
+
summary: summarizePermission(toolName, input),
|
|
669
|
+
description: describeTool(toolName, params.toolCache),
|
|
670
|
+
decisionReason: options2.decisionReason,
|
|
671
|
+
blockedPath: options2.blockedPath,
|
|
672
|
+
toolUseID: options2.toolUseID,
|
|
673
|
+
agentID: options2.agentID,
|
|
674
|
+
suggestions: options2.suggestions,
|
|
675
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
325
676
|
};
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
677
|
+
return await new Promise((resolve) => {
|
|
678
|
+
let finished = false;
|
|
679
|
+
const abortListener = () => {
|
|
680
|
+
params.sessionManager.finishRequest(
|
|
681
|
+
sessionId,
|
|
682
|
+
requestId,
|
|
683
|
+
{ behavior: "deny", message: "Session cancelled", interrupt: true },
|
|
684
|
+
"signal"
|
|
685
|
+
);
|
|
686
|
+
};
|
|
687
|
+
const finish = (result) => {
|
|
688
|
+
if (finished) return;
|
|
689
|
+
finished = true;
|
|
690
|
+
options2.signal.removeEventListener("abort", abortListener);
|
|
691
|
+
resolve(result);
|
|
692
|
+
};
|
|
693
|
+
const registered = params.sessionManager.setPendingPermission(
|
|
694
|
+
sessionId,
|
|
695
|
+
record,
|
|
696
|
+
finish,
|
|
697
|
+
params.permissionRequestTimeoutMs
|
|
698
|
+
);
|
|
699
|
+
if (!registered) {
|
|
700
|
+
finish({ behavior: "deny", message: "Session no longer exists.", interrupt: true });
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
options2.signal.addEventListener("abort", abortListener, { once: true });
|
|
704
|
+
if (options2.signal.aborted) {
|
|
705
|
+
abortListener();
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
};
|
|
709
|
+
const options = {
|
|
710
|
+
...params.options,
|
|
711
|
+
abortController: params.abortController,
|
|
712
|
+
permissionMode: "default",
|
|
713
|
+
canUseTool
|
|
714
|
+
};
|
|
715
|
+
const startQuery = (opts) => query({
|
|
716
|
+
prompt: params.prompt,
|
|
717
|
+
options: opts
|
|
718
|
+
});
|
|
719
|
+
if (params.mode === "resume" || params.mode === "disk-resume") {
|
|
720
|
+
options.resume = params.sessionId;
|
|
721
|
+
}
|
|
722
|
+
let activeQuery = startQuery(options);
|
|
723
|
+
const close = () => {
|
|
724
|
+
try {
|
|
725
|
+
activeQuery.close?.();
|
|
726
|
+
} finally {
|
|
727
|
+
params.abortController.abort();
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
const interrupt = () => {
|
|
731
|
+
activeQuery.interrupt?.();
|
|
732
|
+
};
|
|
733
|
+
const done = (async () => {
|
|
734
|
+
const preInit = [];
|
|
735
|
+
if (shouldWaitForInit) {
|
|
736
|
+
initTimeoutId = setTimeout(() => {
|
|
737
|
+
close();
|
|
738
|
+
rejectSessionId(
|
|
739
|
+
new Error(
|
|
740
|
+
`Error [${"TIMEOUT" /* TIMEOUT */}]: session init timed out after ${params.sessionInitTimeoutMs}ms.`
|
|
741
|
+
)
|
|
742
|
+
);
|
|
743
|
+
}, params.sessionInitTimeoutMs);
|
|
744
|
+
}
|
|
745
|
+
let retryCount = 0;
|
|
746
|
+
let currentStream = activeQuery;
|
|
747
|
+
while (true) {
|
|
748
|
+
try {
|
|
749
|
+
for await (const message of currentStream) {
|
|
750
|
+
if (isSystemInitMessage(message)) {
|
|
751
|
+
params.toolCache?.updateFromInit(message.tools);
|
|
752
|
+
params.onInit?.(message);
|
|
753
|
+
params.sessionManager.setInitTools(message.session_id, message.tools);
|
|
754
|
+
activeSessionId = message.session_id;
|
|
755
|
+
if (!sessionIdResolved && shouldWaitForInit) {
|
|
756
|
+
sessionIdResolved = true;
|
|
757
|
+
resolveSessionId(activeSessionId);
|
|
758
|
+
if (initTimeoutId) clearTimeout(initTimeoutId);
|
|
759
|
+
for (const buffered of preInit) {
|
|
760
|
+
const event2 = messageToEvent(buffered);
|
|
761
|
+
if (!event2) continue;
|
|
762
|
+
params.sessionManager.pushEvent(activeSessionId, {
|
|
763
|
+
type: event2.type,
|
|
764
|
+
data: event2.data,
|
|
765
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
preInit.length = 0;
|
|
769
|
+
}
|
|
770
|
+
continue;
|
|
771
|
+
}
|
|
772
|
+
if (shouldWaitForInit && !sessionIdResolved) {
|
|
773
|
+
preInit.push(message);
|
|
774
|
+
continue;
|
|
775
|
+
}
|
|
776
|
+
if (message.type === "result") {
|
|
777
|
+
const sessionId2 = message.session_id ?? await getSessionId();
|
|
778
|
+
const agentResult = sdkResultToAgentResult(message);
|
|
779
|
+
const stored = {
|
|
780
|
+
type: agentResult.isError ? "error" : "result",
|
|
781
|
+
result: agentResult,
|
|
782
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
783
|
+
};
|
|
784
|
+
params.sessionManager.setResult(sessionId2, stored);
|
|
785
|
+
params.sessionManager.clearTerminalEvents(sessionId2);
|
|
786
|
+
params.sessionManager.pushEvent(sessionId2, {
|
|
787
|
+
type: agentResult.isError ? "error" : "result",
|
|
788
|
+
data: agentResult,
|
|
789
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
790
|
+
});
|
|
791
|
+
const current = params.sessionManager.get(sessionId2);
|
|
792
|
+
if (current && current.status !== "cancelled") {
|
|
793
|
+
params.sessionManager.update(sessionId2, {
|
|
794
|
+
status: agentResult.isError ? "error" : "idle",
|
|
795
|
+
totalTurns: agentResult.numTurns,
|
|
796
|
+
totalCostUsd: agentResult.totalCostUsd,
|
|
797
|
+
abortController: void 0
|
|
798
|
+
});
|
|
799
|
+
} else if (current) {
|
|
800
|
+
params.sessionManager.update(sessionId2, {
|
|
801
|
+
totalTurns: agentResult.numTurns,
|
|
802
|
+
totalCostUsd: agentResult.totalCostUsd,
|
|
803
|
+
abortController: void 0
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
const sessionId = message.session_id ?? await getSessionId();
|
|
809
|
+
const event = messageToEvent(message);
|
|
810
|
+
if (event) {
|
|
811
|
+
params.sessionManager.pushEvent(sessionId, {
|
|
812
|
+
type: event.type,
|
|
813
|
+
data: event.data,
|
|
814
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
if (shouldWaitForInit && !sessionIdResolved) {
|
|
819
|
+
rejectSessionId(
|
|
820
|
+
new Error(
|
|
821
|
+
`Error [${"INTERNAL" /* INTERNAL */}]: query stream ended before receiving session init.`
|
|
822
|
+
)
|
|
823
|
+
);
|
|
824
|
+
} else if (activeSessionId) {
|
|
825
|
+
const sessionId = activeSessionId;
|
|
826
|
+
const current = params.sessionManager.get(sessionId);
|
|
827
|
+
if (current && current.status !== "cancelled") {
|
|
828
|
+
params.sessionManager.finishAllPending(
|
|
829
|
+
sessionId,
|
|
830
|
+
{
|
|
831
|
+
behavior: "deny",
|
|
832
|
+
message: "Session ended before permission was resolved.",
|
|
833
|
+
interrupt: true
|
|
834
|
+
},
|
|
835
|
+
"cleanup"
|
|
836
|
+
);
|
|
837
|
+
const agentResult = errorToAgentResult(
|
|
838
|
+
sessionId,
|
|
839
|
+
"No result message received from agent."
|
|
840
|
+
);
|
|
841
|
+
const stored = {
|
|
842
|
+
type: "error",
|
|
843
|
+
result: agentResult,
|
|
844
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
845
|
+
};
|
|
846
|
+
params.sessionManager.setResult(sessionId, stored);
|
|
847
|
+
params.sessionManager.clearTerminalEvents(sessionId);
|
|
848
|
+
params.sessionManager.pushEvent(sessionId, {
|
|
849
|
+
type: "error",
|
|
850
|
+
data: agentResult,
|
|
851
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
852
|
+
});
|
|
853
|
+
params.sessionManager.update(sessionId, {
|
|
854
|
+
status: "error",
|
|
855
|
+
abortController: void 0
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
return;
|
|
860
|
+
} catch (err) {
|
|
861
|
+
const errClass = classifyError(err, params.abortController.signal);
|
|
862
|
+
if (shouldWaitForInit && !sessionIdResolved) {
|
|
863
|
+
rejectSessionId(
|
|
864
|
+
new Error(
|
|
865
|
+
errClass === "abort" ? `Error [${"CANCELLED" /* CANCELLED */}]: session was cancelled before init.` : `Error [${"INTERNAL" /* INTERNAL */}]: ${enhanceWindowsError(err instanceof Error ? err.message : String(err))}`
|
|
866
|
+
)
|
|
867
|
+
);
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
if (!activeSessionId) return;
|
|
871
|
+
const sessionId = activeSessionId;
|
|
872
|
+
if (errClass === "transient" && retryCount < MAX_TRANSIENT_RETRIES) {
|
|
873
|
+
retryCount++;
|
|
874
|
+
const delay = INITIAL_RETRY_DELAY_MS * Math.pow(2, retryCount - 1);
|
|
875
|
+
params.sessionManager.pushEvent(sessionId, {
|
|
876
|
+
type: "progress",
|
|
877
|
+
data: {
|
|
878
|
+
type: "retry",
|
|
879
|
+
attempt: retryCount,
|
|
880
|
+
maxRetries: MAX_TRANSIENT_RETRIES,
|
|
881
|
+
delayMs: delay,
|
|
882
|
+
error: err instanceof Error ? err.message : String(err)
|
|
883
|
+
},
|
|
884
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
885
|
+
});
|
|
886
|
+
await new Promise((r) => {
|
|
887
|
+
const timer = setTimeout(r, delay);
|
|
888
|
+
const onAbort = () => {
|
|
889
|
+
clearTimeout(timer);
|
|
890
|
+
r();
|
|
891
|
+
};
|
|
892
|
+
params.abortController.signal.addEventListener("abort", onAbort, { once: true });
|
|
893
|
+
});
|
|
894
|
+
if (params.abortController.signal.aborted) return;
|
|
895
|
+
params.sessionManager.finishAllPending(
|
|
896
|
+
sessionId,
|
|
897
|
+
{ behavior: "deny", message: "Retrying after transient error.", interrupt: false },
|
|
898
|
+
"cleanup"
|
|
899
|
+
);
|
|
900
|
+
const retryOpts = {
|
|
901
|
+
...options,
|
|
902
|
+
resume: sessionId
|
|
903
|
+
};
|
|
904
|
+
currentStream = startQuery(retryOpts);
|
|
905
|
+
activeQuery = currentStream;
|
|
906
|
+
continue;
|
|
907
|
+
}
|
|
908
|
+
const current = params.sessionManager.get(sessionId);
|
|
909
|
+
if (current && current.status !== "cancelled") {
|
|
910
|
+
params.sessionManager.finishAllPending(
|
|
911
|
+
sessionId,
|
|
912
|
+
{
|
|
913
|
+
behavior: "deny",
|
|
914
|
+
message: "Session failed before permission was resolved.",
|
|
915
|
+
interrupt: true
|
|
916
|
+
},
|
|
917
|
+
"cleanup"
|
|
918
|
+
);
|
|
919
|
+
const agentResult = errClass === "abort" ? {
|
|
920
|
+
sessionId,
|
|
921
|
+
result: `Error [${"CANCELLED" /* CANCELLED */}]: Session was cancelled.`,
|
|
922
|
+
isError: true,
|
|
923
|
+
durationMs: 0,
|
|
924
|
+
numTurns: 0,
|
|
925
|
+
totalCostUsd: 0
|
|
926
|
+
} : errorToAgentResult(sessionId, err);
|
|
927
|
+
params.sessionManager.setResult(sessionId, {
|
|
928
|
+
type: "error",
|
|
929
|
+
result: agentResult,
|
|
930
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
931
|
+
});
|
|
932
|
+
params.sessionManager.clearTerminalEvents(sessionId);
|
|
933
|
+
params.sessionManager.pushEvent(sessionId, {
|
|
934
|
+
type: "error",
|
|
935
|
+
data: agentResult,
|
|
936
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
937
|
+
});
|
|
938
|
+
params.sessionManager.update(sessionId, { status: "error", abortController: void 0 });
|
|
939
|
+
}
|
|
940
|
+
return;
|
|
941
|
+
} finally {
|
|
942
|
+
if (initTimeoutId) clearTimeout(initTimeoutId);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
})();
|
|
946
|
+
return { sdkSessionIdPromise, done, close, interrupt };
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// src/utils/resume-token.ts
|
|
950
|
+
import { createHmac } from "crypto";
|
|
951
|
+
function getResumeSecret() {
|
|
952
|
+
const secret = process.env.CLAUDE_CODE_MCP_RESUME_SECRET;
|
|
953
|
+
if (typeof secret !== "string") return void 0;
|
|
954
|
+
const trimmed = secret.trim();
|
|
955
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
956
|
+
}
|
|
957
|
+
function computeResumeToken(sessionId, secret) {
|
|
958
|
+
return createHmac("sha256", secret).update(sessionId).digest("base64url");
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
// src/utils/race-with-abort.ts
|
|
962
|
+
function raceWithAbort(promise, signal, onAbort) {
|
|
963
|
+
if (!signal) return promise;
|
|
964
|
+
if (signal.aborted) {
|
|
965
|
+
try {
|
|
966
|
+
onAbort();
|
|
967
|
+
} catch {
|
|
359
968
|
}
|
|
360
|
-
|
|
361
|
-
|
|
969
|
+
return Promise.reject(new Error(`Error [${"CANCELLED" /* CANCELLED */}]: request was cancelled.`));
|
|
970
|
+
}
|
|
971
|
+
return new Promise((resolve, reject) => {
|
|
972
|
+
const abortListener = () => {
|
|
973
|
+
try {
|
|
974
|
+
onAbort();
|
|
975
|
+
} catch {
|
|
976
|
+
}
|
|
977
|
+
reject(new Error(`Error [${"CANCELLED" /* CANCELLED */}]: request was cancelled.`));
|
|
978
|
+
};
|
|
979
|
+
signal.addEventListener("abort", abortListener, { once: true });
|
|
980
|
+
promise.then(resolve, reject).finally(() => signal.removeEventListener("abort", abortListener));
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
// src/utils/build-options.ts
|
|
985
|
+
function buildOptions(src) {
|
|
986
|
+
const opts = { cwd: src.cwd };
|
|
987
|
+
if (src.allowedTools !== void 0) opts.allowedTools = src.allowedTools;
|
|
988
|
+
if (src.disallowedTools !== void 0) opts.disallowedTools = src.disallowedTools;
|
|
989
|
+
if (src.tools !== void 0) opts.tools = src.tools;
|
|
990
|
+
if (src.maxTurns !== void 0) opts.maxTurns = src.maxTurns;
|
|
991
|
+
if (src.model !== void 0) opts.model = src.model;
|
|
992
|
+
if (src.systemPrompt !== void 0) opts.systemPrompt = src.systemPrompt;
|
|
993
|
+
if (src.agents !== void 0) opts.agents = src.agents;
|
|
994
|
+
if (src.maxBudgetUsd !== void 0) opts.maxBudgetUsd = src.maxBudgetUsd;
|
|
995
|
+
if (src.effort !== void 0) opts.effort = src.effort;
|
|
996
|
+
if (src.betas !== void 0) opts.betas = src.betas;
|
|
997
|
+
if (src.additionalDirectories !== void 0)
|
|
998
|
+
opts.additionalDirectories = src.additionalDirectories;
|
|
999
|
+
if (src.outputFormat !== void 0) opts.outputFormat = src.outputFormat;
|
|
1000
|
+
if (src.thinking !== void 0) opts.thinking = src.thinking;
|
|
1001
|
+
if (src.persistSession !== void 0) opts.persistSession = src.persistSession;
|
|
1002
|
+
if (src.resumeSessionAt !== void 0) opts.resumeSessionAt = src.resumeSessionAt;
|
|
1003
|
+
if (src.pathToClaudeCodeExecutable !== void 0)
|
|
1004
|
+
opts.pathToClaudeCodeExecutable = src.pathToClaudeCodeExecutable;
|
|
1005
|
+
if (src.agent !== void 0) opts.agent = src.agent;
|
|
1006
|
+
if (src.mcpServers !== void 0) opts.mcpServers = src.mcpServers;
|
|
1007
|
+
if (src.sandbox !== void 0) opts.sandbox = src.sandbox;
|
|
1008
|
+
if (src.fallbackModel !== void 0) opts.fallbackModel = src.fallbackModel;
|
|
1009
|
+
if (src.enableFileCheckpointing !== void 0)
|
|
1010
|
+
opts.enableFileCheckpointing = src.enableFileCheckpointing;
|
|
1011
|
+
if (src.includePartialMessages !== void 0)
|
|
1012
|
+
opts.includePartialMessages = src.includePartialMessages;
|
|
1013
|
+
if (src.strictMcpConfig !== void 0) opts.strictMcpConfig = src.strictMcpConfig;
|
|
1014
|
+
if (src.settingSources !== void 0) opts.settingSources = src.settingSources;
|
|
1015
|
+
else opts.settingSources = DEFAULT_SETTING_SOURCES;
|
|
1016
|
+
if (src.debug !== void 0) opts.debug = src.debug;
|
|
1017
|
+
if (src.debugFile !== void 0) opts.debugFile = src.debugFile;
|
|
1018
|
+
if (src.env !== void 0) opts.env = { ...process.env, ...src.env };
|
|
1019
|
+
return opts;
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
// src/tools/claude-code.ts
|
|
1023
|
+
async function executeClaudeCode(input, sessionManager, serverCwd, toolCache, requestSignal) {
|
|
1024
|
+
const cwd = input.cwd !== void 0 ? input.cwd : serverCwd;
|
|
1025
|
+
if (typeof cwd !== "string" || cwd.trim() === "") {
|
|
1026
|
+
return {
|
|
1027
|
+
sessionId: "",
|
|
1028
|
+
status: "error",
|
|
1029
|
+
error: `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: cwd must be a non-empty string.`
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
const abortController = new AbortController();
|
|
1033
|
+
const adv = input.advanced ?? {};
|
|
1034
|
+
const permissionRequestTimeoutMs = input.permissionRequestTimeoutMs ?? 6e4;
|
|
1035
|
+
const sessionInitTimeoutMs = adv.sessionInitTimeoutMs ?? 1e4;
|
|
1036
|
+
const flat = {
|
|
1037
|
+
cwd,
|
|
1038
|
+
allowedTools: input.allowedTools,
|
|
1039
|
+
disallowedTools: input.disallowedTools,
|
|
1040
|
+
maxTurns: input.maxTurns,
|
|
1041
|
+
model: input.model,
|
|
1042
|
+
systemPrompt: input.systemPrompt,
|
|
1043
|
+
...adv
|
|
1044
|
+
};
|
|
1045
|
+
try {
|
|
1046
|
+
const handle = consumeQuery({
|
|
1047
|
+
mode: "start",
|
|
362
1048
|
prompt: input.prompt,
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
1049
|
+
abortController,
|
|
1050
|
+
options: buildOptions(flat),
|
|
1051
|
+
permissionRequestTimeoutMs,
|
|
1052
|
+
sessionInitTimeoutMs,
|
|
1053
|
+
sessionManager,
|
|
1054
|
+
toolCache,
|
|
1055
|
+
onInit: (init) => {
|
|
1056
|
+
if (sessionManager.get(init.session_id)) return;
|
|
368
1057
|
sessionManager.create({
|
|
369
|
-
sessionId,
|
|
1058
|
+
sessionId: init.session_id,
|
|
370
1059
|
cwd,
|
|
371
1060
|
model: input.model,
|
|
372
|
-
permissionMode:
|
|
1061
|
+
permissionMode: "default",
|
|
373
1062
|
allowedTools: input.allowedTools,
|
|
374
1063
|
disallowedTools: input.disallowedTools,
|
|
375
|
-
tools:
|
|
1064
|
+
tools: adv.tools,
|
|
376
1065
|
maxTurns: input.maxTurns,
|
|
377
1066
|
systemPrompt: input.systemPrompt,
|
|
378
|
-
agents:
|
|
379
|
-
maxBudgetUsd:
|
|
380
|
-
effort:
|
|
381
|
-
betas:
|
|
382
|
-
additionalDirectories:
|
|
383
|
-
outputFormat:
|
|
384
|
-
thinking:
|
|
385
|
-
persistSession:
|
|
386
|
-
pathToClaudeCodeExecutable:
|
|
387
|
-
agent:
|
|
388
|
-
mcpServers:
|
|
389
|
-
sandbox:
|
|
390
|
-
fallbackModel:
|
|
391
|
-
enableFileCheckpointing:
|
|
392
|
-
includePartialMessages:
|
|
393
|
-
strictMcpConfig:
|
|
394
|
-
settingSources:
|
|
395
|
-
debug:
|
|
396
|
-
debugFile:
|
|
397
|
-
env:
|
|
1067
|
+
agents: adv.agents,
|
|
1068
|
+
maxBudgetUsd: adv.maxBudgetUsd,
|
|
1069
|
+
effort: adv.effort,
|
|
1070
|
+
betas: adv.betas,
|
|
1071
|
+
additionalDirectories: adv.additionalDirectories,
|
|
1072
|
+
outputFormat: adv.outputFormat,
|
|
1073
|
+
thinking: adv.thinking,
|
|
1074
|
+
persistSession: adv.persistSession,
|
|
1075
|
+
pathToClaudeCodeExecutable: adv.pathToClaudeCodeExecutable,
|
|
1076
|
+
agent: adv.agent,
|
|
1077
|
+
mcpServers: adv.mcpServers,
|
|
1078
|
+
sandbox: adv.sandbox,
|
|
1079
|
+
fallbackModel: adv.fallbackModel,
|
|
1080
|
+
enableFileCheckpointing: adv.enableFileCheckpointing,
|
|
1081
|
+
includePartialMessages: adv.includePartialMessages,
|
|
1082
|
+
strictMcpConfig: adv.strictMcpConfig,
|
|
1083
|
+
settingSources: adv.settingSources ?? DEFAULT_SETTING_SOURCES,
|
|
1084
|
+
debug: adv.debug,
|
|
1085
|
+
debugFile: adv.debugFile,
|
|
1086
|
+
env: adv.env,
|
|
398
1087
|
abortController
|
|
399
1088
|
});
|
|
400
1089
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
if (result.subtype === "success") {
|
|
415
|
-
resultText = result.result;
|
|
416
|
-
structuredOutput = result.structured_output;
|
|
417
|
-
} else {
|
|
418
|
-
isError = true;
|
|
419
|
-
errorSubtype = result.subtype;
|
|
420
|
-
resultText = result.errors.map(String).join("\n") || `Error [${result.subtype}]: Unknown error`;
|
|
421
|
-
}
|
|
422
|
-
break;
|
|
423
|
-
}
|
|
424
|
-
}
|
|
1090
|
+
});
|
|
1091
|
+
const sessionId = await raceWithAbort(
|
|
1092
|
+
handle.sdkSessionIdPromise,
|
|
1093
|
+
requestSignal,
|
|
1094
|
+
() => abortController.abort()
|
|
1095
|
+
);
|
|
1096
|
+
const resumeSecret = getResumeSecret();
|
|
1097
|
+
return {
|
|
1098
|
+
sessionId,
|
|
1099
|
+
status: "running",
|
|
1100
|
+
pollInterval: 3e3,
|
|
1101
|
+
resumeToken: resumeSecret ? computeResumeToken(sessionId, resumeSecret) : void 0
|
|
1102
|
+
};
|
|
425
1103
|
} catch (err) {
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
}
|
|
433
|
-
if (sessionId) {
|
|
434
|
-
const current = sessionManager.get(sessionId);
|
|
435
|
-
if (current) {
|
|
436
|
-
if (timedOut && current.status !== "cancelled" && current.status !== "error") {
|
|
437
|
-
sessionManager.update(sessionId, { status: "error" });
|
|
438
|
-
} else if (isAborted && current.status === "running") {
|
|
439
|
-
sessionManager.update(sessionId, { status: "cancelled" });
|
|
440
|
-
} else if (!isAborted && current.status !== "cancelled") {
|
|
441
|
-
sessionManager.update(sessionId, { status: "error" });
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
} finally {
|
|
446
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
447
|
-
}
|
|
448
|
-
if (!seenResult && !isError && abortController.signal.aborted) {
|
|
449
|
-
isError = true;
|
|
450
|
-
resultText = timedOut ? `Error [${"TIMEOUT" /* TIMEOUT */}]: Session timed out after ${input.timeout}ms.` : `Error [${"CANCELLED" /* CANCELLED */}]: Session was cancelled.`;
|
|
451
|
-
}
|
|
452
|
-
if (sessionId && !seenResult && !isError) {
|
|
453
|
-
isError = true;
|
|
454
|
-
errorSubtype = errorSubtype ?? "missing_result";
|
|
455
|
-
const noResultMsg = `Error [${"INTERNAL" /* INTERNAL */}]: No result message received from agent.`;
|
|
456
|
-
resultText = resultText ? `${noResultMsg} Original: ${resultText}` : noResultMsg;
|
|
457
|
-
}
|
|
458
|
-
if (sessionId) {
|
|
459
|
-
const current = sessionManager.get(sessionId);
|
|
460
|
-
if (current && current.status !== "cancelled") {
|
|
461
|
-
sessionManager.update(sessionId, {
|
|
462
|
-
status: isError ? "error" : "idle",
|
|
463
|
-
totalTurns: numTurns,
|
|
464
|
-
totalCostUsd,
|
|
465
|
-
abortController: void 0
|
|
466
|
-
});
|
|
467
|
-
const updated = sessionManager.get(sessionId);
|
|
468
|
-
sessionTotalTurns = updated?.totalTurns;
|
|
469
|
-
sessionTotalCostUsd = updated?.totalCostUsd;
|
|
470
|
-
} else if (current) {
|
|
471
|
-
sessionManager.update(sessionId, {
|
|
472
|
-
totalTurns: numTurns,
|
|
473
|
-
totalCostUsd,
|
|
474
|
-
abortController: void 0
|
|
475
|
-
});
|
|
476
|
-
const updated = sessionManager.get(sessionId);
|
|
477
|
-
sessionTotalTurns = updated?.totalTurns;
|
|
478
|
-
sessionTotalCostUsd = updated?.totalCostUsd;
|
|
479
|
-
}
|
|
480
|
-
} else {
|
|
481
|
-
isError = true;
|
|
482
|
-
const noInitMsg = timedOut ? `Error [${"TIMEOUT" /* TIMEOUT */}]: Session timed out after ${input.timeout}ms (no session ID received).` : `Error [${"INTERNAL" /* INTERNAL */}]: No session ID received from agent.`;
|
|
483
|
-
resultText = resultText ? `${noInitMsg} Original: ${resultText}` : noInitMsg;
|
|
1104
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1105
|
+
return {
|
|
1106
|
+
sessionId: "",
|
|
1107
|
+
status: "error",
|
|
1108
|
+
error: message.includes("Error [") ? message : `Error [${"INTERNAL" /* INTERNAL */}]: ${message}`
|
|
1109
|
+
};
|
|
484
1110
|
}
|
|
485
|
-
return {
|
|
486
|
-
sessionId,
|
|
487
|
-
result: resultText,
|
|
488
|
-
isError,
|
|
489
|
-
durationMs,
|
|
490
|
-
durationApiMs,
|
|
491
|
-
numTurns,
|
|
492
|
-
totalCostUsd,
|
|
493
|
-
sessionTotalTurns,
|
|
494
|
-
sessionTotalCostUsd,
|
|
495
|
-
structuredOutput,
|
|
496
|
-
stopReason,
|
|
497
|
-
errorSubtype,
|
|
498
|
-
usage,
|
|
499
|
-
modelUsage,
|
|
500
|
-
permissionDenials
|
|
501
|
-
};
|
|
502
1111
|
}
|
|
503
1112
|
|
|
504
1113
|
// src/tools/claude-code-reply.ts
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
const
|
|
508
|
-
|
|
1114
|
+
function toStartError(sessionId, err) {
|
|
1115
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1116
|
+
const errorText = message.includes("Error [") ? message : `Error [${"INTERNAL" /* INTERNAL */}]: ${message}`;
|
|
1117
|
+
return {
|
|
1118
|
+
agentResult: {
|
|
1119
|
+
sessionId,
|
|
1120
|
+
result: errorText,
|
|
1121
|
+
isError: true,
|
|
1122
|
+
durationMs: 0,
|
|
1123
|
+
numTurns: 0,
|
|
1124
|
+
totalCostUsd: 0
|
|
1125
|
+
},
|
|
1126
|
+
errorText
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
function buildOptionsFromDiskResume(dr) {
|
|
1130
|
+
if (dr.cwd === void 0 || typeof dr.cwd !== "string" || dr.cwd.trim() === "") {
|
|
1131
|
+
throw new Error(`Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: cwd must be provided for disk resume.`);
|
|
1132
|
+
}
|
|
1133
|
+
return buildOptions(dr);
|
|
1134
|
+
}
|
|
1135
|
+
async function executeClaudeCodeReply(input, sessionManager, toolCache, requestSignal) {
|
|
1136
|
+
const permissionRequestTimeoutMs = input.permissionRequestTimeoutMs ?? 6e4;
|
|
1137
|
+
const sessionInitTimeoutMs = input.sessionInitTimeoutMs ?? 1e4;
|
|
1138
|
+
const existing = sessionManager.get(input.sessionId);
|
|
1139
|
+
if (!existing) {
|
|
509
1140
|
const allowDiskResume = process.env.CLAUDE_CODE_MCP_ALLOW_DISK_RESUME === "1";
|
|
510
1141
|
if (!allowDiskResume) {
|
|
511
1142
|
return {
|
|
512
1143
|
sessionId: input.sessionId,
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
1144
|
+
status: "error",
|
|
1145
|
+
error: `Error [${"SESSION_NOT_FOUND" /* SESSION_NOT_FOUND */}]: Session '${input.sessionId}' not found or expired.`
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
const resumeSecret = getResumeSecret();
|
|
1149
|
+
if (!resumeSecret) {
|
|
1150
|
+
return {
|
|
1151
|
+
sessionId: input.sessionId,
|
|
1152
|
+
status: "error",
|
|
1153
|
+
error: `Error [${"PERMISSION_DENIED" /* PERMISSION_DENIED */}]: Disk resume is enabled but CLAUDE_CODE_MCP_RESUME_SECRET is not set.`
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
const dr = input.diskResumeConfig ?? {};
|
|
1157
|
+
if (typeof dr.resumeToken !== "string" || dr.resumeToken.trim() === "") {
|
|
1158
|
+
return {
|
|
1159
|
+
sessionId: input.sessionId,
|
|
1160
|
+
status: "error",
|
|
1161
|
+
error: `Error [${"PERMISSION_DENIED" /* PERMISSION_DENIED */}]: resumeToken is required for disk resume fallback.`
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
const expectedToken = computeResumeToken(input.sessionId, resumeSecret);
|
|
1165
|
+
if (dr.resumeToken !== expectedToken) {
|
|
1166
|
+
return {
|
|
1167
|
+
sessionId: input.sessionId,
|
|
1168
|
+
status: "error",
|
|
1169
|
+
error: `Error [${"PERMISSION_DENIED" /* PERMISSION_DENIED */}]: Invalid resumeToken for session '${input.sessionId}'.`
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
try {
|
|
1173
|
+
const abortController2 = new AbortController();
|
|
1174
|
+
const options2 = buildOptionsFromDiskResume(dr);
|
|
1175
|
+
sessionManager.create({
|
|
1176
|
+
sessionId: input.sessionId,
|
|
1177
|
+
cwd: options2.cwd ?? dr.cwd ?? "",
|
|
1178
|
+
model: dr.model,
|
|
1179
|
+
permissionMode: "default",
|
|
1180
|
+
allowedTools: dr.allowedTools,
|
|
1181
|
+
disallowedTools: dr.disallowedTools,
|
|
1182
|
+
tools: dr.tools,
|
|
1183
|
+
maxTurns: dr.maxTurns,
|
|
1184
|
+
systemPrompt: dr.systemPrompt,
|
|
1185
|
+
agents: dr.agents,
|
|
1186
|
+
maxBudgetUsd: dr.maxBudgetUsd,
|
|
1187
|
+
effort: dr.effort,
|
|
1188
|
+
betas: dr.betas,
|
|
1189
|
+
additionalDirectories: dr.additionalDirectories,
|
|
1190
|
+
outputFormat: dr.outputFormat,
|
|
1191
|
+
thinking: dr.thinking,
|
|
1192
|
+
persistSession: dr.persistSession,
|
|
1193
|
+
pathToClaudeCodeExecutable: dr.pathToClaudeCodeExecutable,
|
|
1194
|
+
agent: dr.agent,
|
|
1195
|
+
mcpServers: dr.mcpServers,
|
|
1196
|
+
sandbox: dr.sandbox,
|
|
1197
|
+
fallbackModel: dr.fallbackModel,
|
|
1198
|
+
enableFileCheckpointing: dr.enableFileCheckpointing,
|
|
1199
|
+
includePartialMessages: dr.includePartialMessages,
|
|
1200
|
+
strictMcpConfig: dr.strictMcpConfig,
|
|
1201
|
+
settingSources: dr.settingSources ?? DEFAULT_SETTING_SOURCES,
|
|
1202
|
+
debug: dr.debug,
|
|
1203
|
+
debugFile: dr.debugFile,
|
|
1204
|
+
env: dr.env,
|
|
1205
|
+
abortController: abortController2
|
|
1206
|
+
});
|
|
1207
|
+
try {
|
|
1208
|
+
consumeQuery({
|
|
1209
|
+
mode: "disk-resume",
|
|
1210
|
+
sessionId: input.sessionId,
|
|
1211
|
+
prompt: input.prompt,
|
|
1212
|
+
abortController: abortController2,
|
|
1213
|
+
options: options2,
|
|
1214
|
+
permissionRequestTimeoutMs,
|
|
1215
|
+
sessionInitTimeoutMs,
|
|
1216
|
+
sessionManager,
|
|
1217
|
+
toolCache
|
|
1218
|
+
});
|
|
1219
|
+
} catch (err) {
|
|
1220
|
+
const { agentResult, errorText } = toStartError(input.sessionId, err);
|
|
1221
|
+
sessionManager.setResult(input.sessionId, {
|
|
1222
|
+
type: "error",
|
|
1223
|
+
result: agentResult,
|
|
1224
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1225
|
+
});
|
|
1226
|
+
sessionManager.pushEvent(input.sessionId, {
|
|
1227
|
+
type: "error",
|
|
1228
|
+
data: agentResult,
|
|
1229
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1230
|
+
});
|
|
1231
|
+
sessionManager.update(input.sessionId, { status: "error", abortController: void 0 });
|
|
1232
|
+
return { sessionId: input.sessionId, status: "error", error: errorText };
|
|
1233
|
+
}
|
|
1234
|
+
return {
|
|
1235
|
+
sessionId: input.sessionId,
|
|
1236
|
+
status: "running",
|
|
1237
|
+
pollInterval: 3e3,
|
|
1238
|
+
resumeToken: computeResumeToken(input.sessionId, resumeSecret)
|
|
1239
|
+
};
|
|
1240
|
+
} catch (err) {
|
|
1241
|
+
const { agentResult, errorText } = toStartError(input.sessionId, err);
|
|
1242
|
+
if (sessionManager.get(input.sessionId)) {
|
|
1243
|
+
sessionManager.setResult(input.sessionId, {
|
|
1244
|
+
type: "error",
|
|
1245
|
+
result: agentResult,
|
|
1246
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1247
|
+
});
|
|
1248
|
+
sessionManager.pushEvent(input.sessionId, {
|
|
1249
|
+
type: "error",
|
|
1250
|
+
data: agentResult,
|
|
1251
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1252
|
+
});
|
|
1253
|
+
sessionManager.update(input.sessionId, { status: "error", abortController: void 0 });
|
|
1254
|
+
}
|
|
1255
|
+
return {
|
|
1256
|
+
sessionId: input.sessionId,
|
|
1257
|
+
status: "error",
|
|
1258
|
+
error: errorText
|
|
518
1259
|
};
|
|
519
1260
|
}
|
|
520
|
-
return executeClaudeCodeReplyDiskResume(input, sessionManager, allowBypass);
|
|
521
|
-
}
|
|
522
|
-
if (session.status === "running") {
|
|
523
|
-
return {
|
|
524
|
-
sessionId: input.sessionId,
|
|
525
|
-
result: `Error [${"SESSION_BUSY" /* SESSION_BUSY */}]: Session is currently running. Wait for it to complete or cancel it.`,
|
|
526
|
-
isError: true,
|
|
527
|
-
durationMs: 0,
|
|
528
|
-
numTurns: 0,
|
|
529
|
-
totalCostUsd: 0
|
|
530
|
-
};
|
|
531
1261
|
}
|
|
532
|
-
if (
|
|
1262
|
+
if (existing.status === "running" || existing.status === "waiting_permission") {
|
|
533
1263
|
return {
|
|
534
1264
|
sessionId: input.sessionId,
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
durationMs: 0,
|
|
538
|
-
numTurns: 0,
|
|
539
|
-
totalCostUsd: 0
|
|
1265
|
+
status: "error",
|
|
1266
|
+
error: `Error [${"SESSION_BUSY" /* SESSION_BUSY */}]: Session is not available (status: ${existing.status}).`
|
|
540
1267
|
};
|
|
541
1268
|
}
|
|
542
|
-
if (
|
|
1269
|
+
if (existing.status === "cancelled") {
|
|
543
1270
|
return {
|
|
544
1271
|
sessionId: input.sessionId,
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
durationMs: 0,
|
|
548
|
-
numTurns: 0,
|
|
549
|
-
totalCostUsd: 0
|
|
1272
|
+
status: "error",
|
|
1273
|
+
error: `Error [${"CANCELLED" /* CANCELLED */}]: Session '${input.sessionId}' has been cancelled and cannot be resumed.`
|
|
550
1274
|
};
|
|
551
1275
|
}
|
|
552
|
-
const originalStatus =
|
|
1276
|
+
const originalStatus = existing.status;
|
|
553
1277
|
const abortController = new AbortController();
|
|
554
|
-
let timedOut = false;
|
|
555
1278
|
const acquired = sessionManager.tryAcquire(input.sessionId, originalStatus, abortController);
|
|
556
1279
|
if (!acquired) {
|
|
557
1280
|
const current = sessionManager.get(input.sessionId);
|
|
558
|
-
if (!current) {
|
|
559
|
-
return {
|
|
560
|
-
sessionId: input.sessionId,
|
|
561
|
-
result: `Error [${"SESSION_NOT_FOUND" /* SESSION_NOT_FOUND */}]: Session '${input.sessionId}' not found or expired.`,
|
|
562
|
-
isError: true,
|
|
563
|
-
durationMs: 0,
|
|
564
|
-
numTurns: 0,
|
|
565
|
-
totalCostUsd: 0
|
|
566
|
-
};
|
|
567
|
-
}
|
|
568
1281
|
return {
|
|
569
1282
|
sessionId: input.sessionId,
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
durationMs: 0,
|
|
573
|
-
numTurns: 0,
|
|
574
|
-
totalCostUsd: 0
|
|
1283
|
+
status: "error",
|
|
1284
|
+
error: current ? `Error [${"SESSION_BUSY" /* SESSION_BUSY */}]: Session is not available (status: ${current.status}).` : `Error [${"SESSION_NOT_FOUND" /* SESSION_NOT_FOUND */}]: Session '${input.sessionId}' not found or expired.`
|
|
575
1285
|
};
|
|
576
1286
|
}
|
|
577
|
-
|
|
578
|
-
if (input.
|
|
579
|
-
timeoutId = setTimeout(() => {
|
|
580
|
-
timedOut = true;
|
|
581
|
-
abortController.abort();
|
|
582
|
-
}, input.timeout);
|
|
583
|
-
}
|
|
584
|
-
let resultText = "";
|
|
585
|
-
let isError = false;
|
|
586
|
-
let durationMs = 0;
|
|
587
|
-
let durationApiMs;
|
|
588
|
-
let numTurns = 0;
|
|
589
|
-
let totalCostUsd = 0;
|
|
590
|
-
let sessionTotalTurns;
|
|
591
|
-
let sessionTotalCostUsd;
|
|
592
|
-
let newSessionId = input.sessionId;
|
|
593
|
-
let structuredOutput;
|
|
594
|
-
let stopReason;
|
|
595
|
-
let errorSubtype;
|
|
596
|
-
let usage;
|
|
597
|
-
let modelUsage;
|
|
598
|
-
let permissionDenials;
|
|
599
|
-
let seenResult = false;
|
|
600
|
-
let forkSessionCreated = false;
|
|
1287
|
+
const options = buildOptions(existing);
|
|
1288
|
+
if (input.forkSession) options.forkSession = true;
|
|
601
1289
|
try {
|
|
602
|
-
const
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
cwd: session.cwd,
|
|
606
|
-
permissionMode: session.permissionMode
|
|
607
|
-
};
|
|
608
|
-
if (session.allowedTools !== void 0) options.allowedTools = session.allowedTools;
|
|
609
|
-
if (session.disallowedTools !== void 0) options.disallowedTools = session.disallowedTools;
|
|
610
|
-
if (session.maxTurns !== void 0) options.maxTurns = session.maxTurns;
|
|
611
|
-
if (session.model !== void 0) options.model = session.model;
|
|
612
|
-
if (session.maxBudgetUsd !== void 0) options.maxBudgetUsd = session.maxBudgetUsd;
|
|
613
|
-
if (session.agents !== void 0) options.agents = session.agents;
|
|
614
|
-
if (session.effort !== void 0) options.effort = session.effort;
|
|
615
|
-
if (session.betas !== void 0) options.betas = session.betas;
|
|
616
|
-
if (session.additionalDirectories !== void 0)
|
|
617
|
-
options.additionalDirectories = session.additionalDirectories;
|
|
618
|
-
if (session.outputFormat !== void 0) options.outputFormat = session.outputFormat;
|
|
619
|
-
if (session.thinking !== void 0) options.thinking = session.thinking;
|
|
620
|
-
if (session.tools !== void 0) options.tools = session.tools;
|
|
621
|
-
if (session.systemPrompt !== void 0) options.systemPrompt = session.systemPrompt;
|
|
622
|
-
if (session.persistSession !== void 0) options.persistSession = session.persistSession;
|
|
623
|
-
if (session.pathToClaudeCodeExecutable !== void 0)
|
|
624
|
-
options.pathToClaudeCodeExecutable = session.pathToClaudeCodeExecutable;
|
|
625
|
-
if (session.agent !== void 0) options.agent = session.agent;
|
|
626
|
-
if (session.mcpServers !== void 0)
|
|
627
|
-
options.mcpServers = session.mcpServers;
|
|
628
|
-
if (session.sandbox !== void 0) options.sandbox = session.sandbox;
|
|
629
|
-
if (session.fallbackModel !== void 0) options.fallbackModel = session.fallbackModel;
|
|
630
|
-
if (session.enableFileCheckpointing !== void 0)
|
|
631
|
-
options.enableFileCheckpointing = session.enableFileCheckpointing;
|
|
632
|
-
if (session.includePartialMessages !== void 0)
|
|
633
|
-
options.includePartialMessages = session.includePartialMessages;
|
|
634
|
-
if (session.strictMcpConfig !== void 0) options.strictMcpConfig = session.strictMcpConfig;
|
|
635
|
-
if (session.settingSources !== void 0) options.settingSources = session.settingSources;
|
|
636
|
-
else options.settingSources = DEFAULT_SETTING_SOURCES;
|
|
637
|
-
if (session.debug !== void 0) options.debug = session.debug;
|
|
638
|
-
if (session.debugFile !== void 0) options.debugFile = session.debugFile;
|
|
639
|
-
if (session.env !== void 0) options.env = { ...process.env, ...session.env };
|
|
640
|
-
if (session.permissionMode === "bypassPermissions") {
|
|
641
|
-
options.allowDangerouslySkipPermissions = true;
|
|
642
|
-
}
|
|
643
|
-
if (input.forkSession) {
|
|
644
|
-
options.forkSession = true;
|
|
645
|
-
}
|
|
646
|
-
for await (const message of query2({
|
|
1290
|
+
const handle = consumeQuery({
|
|
1291
|
+
mode: "resume",
|
|
1292
|
+
sessionId: input.sessionId,
|
|
647
1293
|
prompt: input.prompt,
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
1294
|
+
abortController,
|
|
1295
|
+
options,
|
|
1296
|
+
permissionRequestTimeoutMs,
|
|
1297
|
+
sessionInitTimeoutMs,
|
|
1298
|
+
waitForInitSessionId: !!input.forkSession,
|
|
1299
|
+
sessionManager,
|
|
1300
|
+
toolCache,
|
|
1301
|
+
onInit: (init) => {
|
|
1302
|
+
if (!input.forkSession) return;
|
|
1303
|
+
if (init.session_id === input.sessionId) return;
|
|
1304
|
+
if (!sessionManager.get(init.session_id)) {
|
|
653
1305
|
sessionManager.create({
|
|
654
|
-
sessionId:
|
|
655
|
-
cwd:
|
|
656
|
-
model:
|
|
657
|
-
permissionMode:
|
|
658
|
-
allowedTools:
|
|
659
|
-
disallowedTools:
|
|
660
|
-
tools:
|
|
661
|
-
maxTurns:
|
|
662
|
-
systemPrompt:
|
|
663
|
-
agents:
|
|
664
|
-
maxBudgetUsd:
|
|
665
|
-
effort:
|
|
666
|
-
betas:
|
|
667
|
-
additionalDirectories:
|
|
668
|
-
outputFormat:
|
|
669
|
-
thinking:
|
|
670
|
-
persistSession:
|
|
671
|
-
pathToClaudeCodeExecutable:
|
|
672
|
-
agent:
|
|
673
|
-
mcpServers:
|
|
674
|
-
sandbox:
|
|
675
|
-
fallbackModel:
|
|
676
|
-
enableFileCheckpointing:
|
|
677
|
-
includePartialMessages:
|
|
678
|
-
strictMcpConfig:
|
|
679
|
-
settingSources:
|
|
680
|
-
debug:
|
|
681
|
-
debugFile:
|
|
682
|
-
env:
|
|
1306
|
+
sessionId: init.session_id,
|
|
1307
|
+
cwd: existing.cwd,
|
|
1308
|
+
model: existing.model,
|
|
1309
|
+
permissionMode: "default",
|
|
1310
|
+
allowedTools: existing.allowedTools,
|
|
1311
|
+
disallowedTools: existing.disallowedTools,
|
|
1312
|
+
tools: existing.tools,
|
|
1313
|
+
maxTurns: existing.maxTurns,
|
|
1314
|
+
systemPrompt: existing.systemPrompt,
|
|
1315
|
+
agents: existing.agents,
|
|
1316
|
+
maxBudgetUsd: existing.maxBudgetUsd,
|
|
1317
|
+
effort: existing.effort,
|
|
1318
|
+
betas: existing.betas,
|
|
1319
|
+
additionalDirectories: existing.additionalDirectories,
|
|
1320
|
+
outputFormat: existing.outputFormat,
|
|
1321
|
+
thinking: existing.thinking,
|
|
1322
|
+
persistSession: existing.persistSession,
|
|
1323
|
+
pathToClaudeCodeExecutable: existing.pathToClaudeCodeExecutable,
|
|
1324
|
+
agent: existing.agent,
|
|
1325
|
+
mcpServers: existing.mcpServers,
|
|
1326
|
+
sandbox: existing.sandbox,
|
|
1327
|
+
fallbackModel: existing.fallbackModel,
|
|
1328
|
+
enableFileCheckpointing: existing.enableFileCheckpointing,
|
|
1329
|
+
includePartialMessages: existing.includePartialMessages,
|
|
1330
|
+
strictMcpConfig: existing.strictMcpConfig,
|
|
1331
|
+
settingSources: existing.settingSources ?? DEFAULT_SETTING_SOURCES,
|
|
1332
|
+
debug: existing.debug,
|
|
1333
|
+
debugFile: existing.debugFile,
|
|
1334
|
+
env: existing.env,
|
|
683
1335
|
abortController
|
|
684
1336
|
});
|
|
685
|
-
forkSessionCreated = true;
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
if (message.type === "result") {
|
|
689
|
-
if (seenResult) continue;
|
|
690
|
-
seenResult = true;
|
|
691
|
-
const result = message;
|
|
692
|
-
durationMs = result.duration_ms;
|
|
693
|
-
durationApiMs = result.duration_api_ms;
|
|
694
|
-
numTurns = result.num_turns;
|
|
695
|
-
totalCostUsd = result.total_cost_usd;
|
|
696
|
-
isError = result.is_error;
|
|
697
|
-
stopReason = result.stop_reason;
|
|
698
|
-
usage = result.usage;
|
|
699
|
-
modelUsage = result.modelUsage;
|
|
700
|
-
permissionDenials = result.permission_denials;
|
|
701
|
-
if (result.subtype === "success") {
|
|
702
|
-
resultText = result.result;
|
|
703
|
-
structuredOutput = result.structured_output;
|
|
704
|
-
} else {
|
|
705
|
-
isError = true;
|
|
706
|
-
errorSubtype = result.subtype;
|
|
707
|
-
resultText = result.errors.map(String).join("\n") || `Error [${result.subtype}]: Unknown error`;
|
|
708
1337
|
}
|
|
709
|
-
|
|
1338
|
+
sessionManager.update(input.sessionId, {
|
|
1339
|
+
status: originalStatus,
|
|
1340
|
+
abortController: void 0
|
|
1341
|
+
});
|
|
710
1342
|
}
|
|
1343
|
+
});
|
|
1344
|
+
const sessionId = input.forkSession ? await raceWithAbort(
|
|
1345
|
+
handle.sdkSessionIdPromise,
|
|
1346
|
+
requestSignal,
|
|
1347
|
+
() => abortController.abort()
|
|
1348
|
+
) : input.sessionId;
|
|
1349
|
+
if (input.forkSession && sessionId === input.sessionId) {
|
|
1350
|
+
return {
|
|
1351
|
+
sessionId: input.sessionId,
|
|
1352
|
+
status: "error",
|
|
1353
|
+
error: `Error [${"INTERNAL" /* INTERNAL */}]: Fork requested but no new session ID received from agent.`
|
|
1354
|
+
};
|
|
711
1355
|
}
|
|
1356
|
+
const resumeSecret = getResumeSecret();
|
|
1357
|
+
return {
|
|
1358
|
+
sessionId,
|
|
1359
|
+
status: "running",
|
|
1360
|
+
pollInterval: 3e3,
|
|
1361
|
+
resumeToken: resumeSecret ? computeResumeToken(sessionId, resumeSecret) : void 0
|
|
1362
|
+
};
|
|
712
1363
|
} catch (err) {
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
if (isAborted) {
|
|
716
|
-
resultText = timedOut ? `Error [${"TIMEOUT" /* TIMEOUT */}]: Session timed out after ${input.timeout}ms.` : `Error [${"CANCELLED" /* CANCELLED */}]: Session was cancelled.`;
|
|
717
|
-
} else {
|
|
718
|
-
resultText = enhanceWindowsError(err instanceof Error ? err.message : String(err));
|
|
719
|
-
}
|
|
720
|
-
} finally {
|
|
721
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
722
|
-
}
|
|
723
|
-
if (!seenResult && !isError && abortController.signal.aborted) {
|
|
724
|
-
isError = true;
|
|
725
|
-
resultText = timedOut ? `Error [${"TIMEOUT" /* TIMEOUT */}]: Session timed out after ${input.timeout}ms.` : `Error [${"CANCELLED" /* CANCELLED */}]: Session was cancelled.`;
|
|
726
|
-
}
|
|
727
|
-
if (!seenResult && !isError) {
|
|
728
|
-
isError = true;
|
|
729
|
-
errorSubtype = errorSubtype ?? "missing_result";
|
|
730
|
-
const noResultMsg = `Error [${"INTERNAL" /* INTERNAL */}]: No result message received from agent.`;
|
|
731
|
-
resultText = resultText ? `${noResultMsg} Original: ${resultText}` : noResultMsg;
|
|
732
|
-
}
|
|
733
|
-
if (input.forkSession && newSessionId !== input.sessionId) {
|
|
734
|
-
const forkedCurrent = sessionManager.get(newSessionId);
|
|
735
|
-
if (forkedCurrent && forkedCurrent.status !== "cancelled") {
|
|
736
|
-
sessionManager.update(newSessionId, {
|
|
737
|
-
status: isError ? "error" : "idle",
|
|
738
|
-
totalTurns: numTurns,
|
|
739
|
-
totalCostUsd,
|
|
740
|
-
abortController: void 0
|
|
741
|
-
});
|
|
742
|
-
const updatedFork = sessionManager.get(newSessionId);
|
|
743
|
-
sessionTotalTurns = updatedFork?.totalTurns;
|
|
744
|
-
sessionTotalCostUsd = updatedFork?.totalCostUsd;
|
|
745
|
-
} else if (forkedCurrent) {
|
|
746
|
-
sessionManager.update(newSessionId, {
|
|
747
|
-
totalTurns: numTurns,
|
|
748
|
-
totalCostUsd,
|
|
749
|
-
abortController: void 0
|
|
750
|
-
});
|
|
751
|
-
const updatedFork = sessionManager.get(newSessionId);
|
|
752
|
-
sessionTotalTurns = updatedFork?.totalTurns;
|
|
753
|
-
sessionTotalCostUsd = updatedFork?.totalCostUsd;
|
|
754
|
-
}
|
|
755
|
-
const origCurrent = sessionManager.get(input.sessionId);
|
|
756
|
-
if (origCurrent && origCurrent.status !== "cancelled") {
|
|
1364
|
+
const { agentResult, errorText } = toStartError(input.sessionId, err);
|
|
1365
|
+
if (input.forkSession) {
|
|
757
1366
|
sessionManager.update(input.sessionId, {
|
|
758
1367
|
status: originalStatus,
|
|
759
1368
|
abortController: void 0
|
|
760
1369
|
});
|
|
761
|
-
} else
|
|
762
|
-
sessionManager.
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
} else {
|
|
767
|
-
const current = sessionManager.get(input.sessionId);
|
|
768
|
-
if (current && current.status !== "cancelled") {
|
|
769
|
-
sessionManager.update(input.sessionId, {
|
|
770
|
-
status: isError ? "error" : "idle",
|
|
771
|
-
totalTurns: (session.totalTurns ?? 0) + numTurns,
|
|
772
|
-
totalCostUsd: (session.totalCostUsd ?? 0) + totalCostUsd,
|
|
773
|
-
abortController: void 0
|
|
1370
|
+
} else {
|
|
1371
|
+
sessionManager.setResult(input.sessionId, {
|
|
1372
|
+
type: "error",
|
|
1373
|
+
result: agentResult,
|
|
1374
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
774
1375
|
});
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
sessionManager.update(input.sessionId, {
|
|
780
|
-
totalTurns: (session.totalTurns ?? 0) + numTurns,
|
|
781
|
-
totalCostUsd: (session.totalCostUsd ?? 0) + totalCostUsd,
|
|
782
|
-
abortController: void 0
|
|
1376
|
+
sessionManager.pushEvent(input.sessionId, {
|
|
1377
|
+
type: "error",
|
|
1378
|
+
data: agentResult,
|
|
1379
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
783
1380
|
});
|
|
784
|
-
|
|
785
|
-
sessionTotalTurns = updated?.totalTurns;
|
|
786
|
-
sessionTotalCostUsd = updated?.totalCostUsd;
|
|
1381
|
+
sessionManager.update(input.sessionId, { status: "error", abortController: void 0 });
|
|
787
1382
|
}
|
|
1383
|
+
return {
|
|
1384
|
+
sessionId: input.sessionId,
|
|
1385
|
+
status: "error",
|
|
1386
|
+
error: errorText
|
|
1387
|
+
};
|
|
788
1388
|
}
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
// src/tools/tool-discovery.ts
|
|
1392
|
+
var TOOL_CATALOG = {
|
|
1393
|
+
Bash: {
|
|
1394
|
+
description: "Run shell commands (e.g. npm install, git commit, ls) in the project directory.",
|
|
1395
|
+
category: "execute"
|
|
1396
|
+
},
|
|
1397
|
+
Read: { description: "Read the contents of a file given its path.", category: "file_read" },
|
|
1398
|
+
Write: {
|
|
1399
|
+
description: "Create a new file or completely replace an existing file's contents.",
|
|
1400
|
+
category: "file_write"
|
|
1401
|
+
},
|
|
1402
|
+
Edit: {
|
|
1403
|
+
description: "Make targeted changes to specific parts of an existing file without rewriting the whole file.",
|
|
1404
|
+
category: "file_write"
|
|
1405
|
+
},
|
|
1406
|
+
Glob: {
|
|
1407
|
+
description: "Find files by name pattern (e.g. '**/*.ts' finds all TypeScript files).",
|
|
1408
|
+
category: "file_read"
|
|
1409
|
+
},
|
|
1410
|
+
Grep: {
|
|
1411
|
+
description: "Search inside files for text or regex patterns (like grep/ripgrep).",
|
|
1412
|
+
category: "file_read"
|
|
1413
|
+
},
|
|
1414
|
+
NotebookEdit: {
|
|
1415
|
+
description: "Edit individual cells in Jupyter notebooks (.ipynb files).",
|
|
1416
|
+
category: "file_write"
|
|
1417
|
+
},
|
|
1418
|
+
WebFetch: {
|
|
1419
|
+
description: "Download and read the content of a web page or API endpoint.",
|
|
1420
|
+
category: "network"
|
|
1421
|
+
},
|
|
1422
|
+
WebSearch: { description: "Search the web and return relevant results.", category: "network" },
|
|
1423
|
+
Task: {
|
|
1424
|
+
description: "Spawn a subagent to handle a subtask independently (requires this tool to be in allowedTools).",
|
|
1425
|
+
category: "agent"
|
|
1426
|
+
},
|
|
1427
|
+
TaskOutput: { description: "Get the output from a background subagent task.", category: "agent" },
|
|
1428
|
+
TaskStop: { description: "Cancel a running background subagent task.", category: "agent" },
|
|
1429
|
+
TodoWrite: {
|
|
1430
|
+
description: "Create and update a structured task/todo checklist.",
|
|
1431
|
+
category: "agent"
|
|
1432
|
+
},
|
|
1433
|
+
AskUserQuestion: {
|
|
1434
|
+
description: "Ask the user a question and wait for their answer before continuing.",
|
|
1435
|
+
category: "interaction"
|
|
794
1436
|
}
|
|
1437
|
+
};
|
|
1438
|
+
function uniq(items) {
|
|
1439
|
+
return Array.from(new Set(items));
|
|
1440
|
+
}
|
|
1441
|
+
function discoverToolsFromInit(initTools) {
|
|
1442
|
+
const names = uniq(initTools.filter((t) => typeof t === "string" && t.trim() !== ""));
|
|
1443
|
+
return names.map((name) => ({
|
|
1444
|
+
name,
|
|
1445
|
+
description: TOOL_CATALOG[name]?.description ?? name,
|
|
1446
|
+
category: TOOL_CATALOG[name]?.category
|
|
1447
|
+
}));
|
|
1448
|
+
}
|
|
1449
|
+
function defaultCatalogTools() {
|
|
1450
|
+
return Object.keys(TOOL_CATALOG).sort((a, b) => a.localeCompare(b)).map((name) => ({ name, ...TOOL_CATALOG[name] }));
|
|
1451
|
+
}
|
|
1452
|
+
var ToolDiscoveryCache = class {
|
|
1453
|
+
cached;
|
|
1454
|
+
constructor(initial) {
|
|
1455
|
+
this.cached = initial ?? defaultCatalogTools();
|
|
1456
|
+
}
|
|
1457
|
+
getTools() {
|
|
1458
|
+
return this.cached;
|
|
1459
|
+
}
|
|
1460
|
+
updateFromInit(initTools) {
|
|
1461
|
+
const discovered = discoverToolsFromInit(initTools);
|
|
1462
|
+
const next = mergeToolLists(discovered, defaultCatalogTools());
|
|
1463
|
+
const updated = JSON.stringify(next) !== JSON.stringify(this.cached);
|
|
1464
|
+
if (updated) this.cached = next;
|
|
1465
|
+
return { updated, tools: this.cached };
|
|
1466
|
+
}
|
|
1467
|
+
};
|
|
1468
|
+
function mergeToolLists(primary, fallback) {
|
|
1469
|
+
const byName = /* @__PURE__ */ new Map();
|
|
1470
|
+
for (const t of fallback) byName.set(t.name, t);
|
|
1471
|
+
for (const t of primary) byName.set(t.name, t);
|
|
1472
|
+
return Array.from(byName.values()).sort((a, b) => a.name.localeCompare(b.name));
|
|
1473
|
+
}
|
|
1474
|
+
function groupByCategory(tools) {
|
|
1475
|
+
const grouped = {};
|
|
1476
|
+
for (const tool of tools) {
|
|
1477
|
+
const category = tool.category ?? "other";
|
|
1478
|
+
grouped[category] ??= [];
|
|
1479
|
+
grouped[category].push(tool);
|
|
1480
|
+
}
|
|
1481
|
+
for (const category of Object.keys(grouped)) {
|
|
1482
|
+
grouped[category].sort((a, b) => a.name.localeCompare(b.name));
|
|
1483
|
+
}
|
|
1484
|
+
return grouped;
|
|
1485
|
+
}
|
|
1486
|
+
function buildInternalToolsDescription(tools) {
|
|
1487
|
+
const grouped = groupByCategory(tools);
|
|
1488
|
+
const categories = Object.keys(grouped).sort((a, b) => a.localeCompare(b));
|
|
1489
|
+
let desc = 'Start a new Claude Code agent session.\n\nLaunches an autonomous coding agent that can read/write files, run shell commands, search code, manage git, access the web, and more. Returns immediately with a sessionId \u2014 the agent runs asynchronously in the background.\n\nWorkflow:\n1. Call claude_code with a prompt \u2192 returns { sessionId, status: "running", pollInterval }\n2. Poll with claude_code_check (action="poll") to receive progress events and the final result\n3. If the agent needs permission for a tool call, approve or deny via claude_code_check (action="respond_permission")\n\n';
|
|
1490
|
+
desc += "Defaults:\n- settingSources: ['user', 'project', 'local'] (loads ~/.claude/settings.json, .claude/settings.json, .claude/settings.local.json, and CLAUDE.md)\n- persistSession: true\n- sessionInitTimeoutMs: 10000\n- permissionRequestTimeoutMs: 60000\n- allowedTools/disallowedTools: [] (none)\n- resumeToken: omitted unless CLAUDE_CODE_MCP_RESUME_SECRET is set on the server\n\n";
|
|
1491
|
+
desc += "Internal tools available to the agent (use allowedTools/disallowedTools to control approval policy; authoritative list returned by claude_code_check with includeTools=true):\n";
|
|
1492
|
+
for (const category of categories) {
|
|
1493
|
+
desc += `
|
|
1494
|
+
[${category}]
|
|
1495
|
+
`;
|
|
1496
|
+
for (const tool of grouped[category]) {
|
|
1497
|
+
desc += `- ${tool.name}: ${tool.description}
|
|
1498
|
+
`;
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
desc += '\nUse `allowedTools` to pre-approve tools (no permission prompts). Use `disallowedTools` to permanently block specific tools. Any tool not in either list will pause the session (status: "waiting_permission") until approved or denied via claude_code_check.\n';
|
|
1502
|
+
return desc;
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
// src/tools/claude-code-check.ts
|
|
1506
|
+
function pollIntervalForStatus(status) {
|
|
1507
|
+
if (status === "waiting_permission") return 1e3;
|
|
1508
|
+
if (status === "running") return 3e3;
|
|
1509
|
+
return void 0;
|
|
1510
|
+
}
|
|
1511
|
+
function toPermissionResult(params) {
|
|
1512
|
+
if (params.decision === "allow") {
|
|
1513
|
+
return {
|
|
1514
|
+
behavior: "allow",
|
|
1515
|
+
updatedInput: params.updatedInput,
|
|
1516
|
+
updatedPermissions: params.updatedPermissions
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
return {
|
|
1520
|
+
behavior: "deny",
|
|
1521
|
+
message: params.denyMessage ?? "Permission denied by caller",
|
|
1522
|
+
interrupt: params.interrupt
|
|
1523
|
+
};
|
|
1524
|
+
}
|
|
1525
|
+
function slimAssistantData(data) {
|
|
1526
|
+
if (!data || typeof data !== "object") return data;
|
|
1527
|
+
const d = data;
|
|
1528
|
+
if (d.type !== "assistant") return data;
|
|
1529
|
+
const msg = d.message;
|
|
1530
|
+
if (!msg || typeof msg !== "object") return data;
|
|
1531
|
+
const m = msg;
|
|
1532
|
+
const slimmed = {};
|
|
1533
|
+
if (m.role !== void 0) slimmed.role = m.role;
|
|
1534
|
+
if (m.stop_reason !== void 0) slimmed.stop_reason = m.stop_reason;
|
|
1535
|
+
if (Array.isArray(m.content)) {
|
|
1536
|
+
slimmed.content = m.content.map((block) => {
|
|
1537
|
+
const { cache_control, ...rest } = block;
|
|
1538
|
+
return rest;
|
|
1539
|
+
});
|
|
1540
|
+
}
|
|
1541
|
+
return {
|
|
1542
|
+
type: d.type,
|
|
1543
|
+
message: slimmed,
|
|
1544
|
+
...d.parent_tool_use_id ? { parent_tool_use_id: d.parent_tool_use_id } : {},
|
|
1545
|
+
...d.error ? { error: d.error } : {}
|
|
1546
|
+
};
|
|
1547
|
+
}
|
|
1548
|
+
function toEvents(events, opts) {
|
|
1549
|
+
return events.map((e) => {
|
|
1550
|
+
if ((e.type === "result" || e.type === "error") && isAgentResult(e.data)) {
|
|
1551
|
+
const redacted = redactAgentResult(e.data, opts);
|
|
1552
|
+
return { id: e.id, type: e.type, data: redacted, timestamp: e.timestamp };
|
|
1553
|
+
}
|
|
1554
|
+
if (opts.slim && e.type === "output") {
|
|
1555
|
+
return { id: e.id, type: e.type, data: slimAssistantData(e.data), timestamp: e.timestamp };
|
|
1556
|
+
}
|
|
1557
|
+
return { id: e.id, type: e.type, data: e.data, timestamp: e.timestamp };
|
|
1558
|
+
});
|
|
1559
|
+
}
|
|
1560
|
+
function buildResult(sessionManager, toolCache, input) {
|
|
1561
|
+
const responseMode = input.responseMode ?? "minimal";
|
|
1562
|
+
const po = input.pollOptions ?? {};
|
|
1563
|
+
const includeTools = po.includeTools;
|
|
1564
|
+
const includeEvents = po.includeEvents ?? true;
|
|
1565
|
+
const includeActions = po.includeActions ?? true;
|
|
1566
|
+
const includeResult = po.includeResult ?? true;
|
|
1567
|
+
const includeUsage = po.includeUsage ?? responseMode === "full";
|
|
1568
|
+
const includeModelUsage = po.includeModelUsage ?? responseMode === "full";
|
|
1569
|
+
const includeStructuredOutput = po.includeStructuredOutput ?? responseMode === "full";
|
|
1570
|
+
const includeTerminalEvents = po.includeTerminalEvents ?? responseMode === "full";
|
|
1571
|
+
const includeProgressEvents = po.includeProgressEvents ?? responseMode === "full";
|
|
1572
|
+
const maxEvents = input.maxEvents ?? (responseMode === "minimal" ? 200 : void 0);
|
|
1573
|
+
const sessionId = input.sessionId;
|
|
1574
|
+
const session = sessionManager.get(sessionId);
|
|
1575
|
+
const status = session?.status ?? "error";
|
|
1576
|
+
const {
|
|
1577
|
+
events: rawEvents,
|
|
1578
|
+
nextCursor: rawNextCursor,
|
|
1579
|
+
cursorResetTo
|
|
1580
|
+
} = sessionManager.readEvents(sessionId, input.cursor);
|
|
1581
|
+
let truncated = false;
|
|
1582
|
+
const truncatedFields = [];
|
|
1583
|
+
const windowEvents = maxEvents !== void 0 && rawEvents.length > maxEvents ? rawEvents.slice(0, maxEvents) : rawEvents;
|
|
1584
|
+
const nextCursor = maxEvents !== void 0 && rawEvents.length > maxEvents ? windowEvents.length > 0 ? windowEvents[windowEvents.length - 1].id + 1 : rawNextCursor : rawNextCursor;
|
|
1585
|
+
if (maxEvents !== void 0 && rawEvents.length > maxEvents) {
|
|
1586
|
+
truncated = true;
|
|
1587
|
+
truncatedFields.push("events");
|
|
1588
|
+
}
|
|
1589
|
+
const outputEvents = (() => {
|
|
1590
|
+
if (!includeEvents) return [];
|
|
1591
|
+
let filtered = windowEvents;
|
|
1592
|
+
if (!includeTerminalEvents && includeResult && (status === "idle" || status === "error")) {
|
|
1593
|
+
filtered = filtered.filter((e) => e.type !== "result" && e.type !== "error");
|
|
1594
|
+
}
|
|
1595
|
+
if (!includeProgressEvents) {
|
|
1596
|
+
filtered = filtered.filter((e) => {
|
|
1597
|
+
if (e.type !== "progress") return true;
|
|
1598
|
+
const d = e.data;
|
|
1599
|
+
const progressType = d?.type;
|
|
1600
|
+
return progressType !== "tool_progress" && progressType !== "auth_status";
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1603
|
+
return filtered;
|
|
1604
|
+
})();
|
|
1605
|
+
const pending = status === "waiting_permission" ? sessionManager.listPendingPermissions(sessionId) : [];
|
|
1606
|
+
const stored = status === "idle" || status === "error" ? sessionManager.getResult(sessionId) : void 0;
|
|
1607
|
+
const initTools = includeTools ? sessionManager.getInitTools(sessionId) : void 0;
|
|
1608
|
+
const availableTools = includeTools && initTools ? discoverToolsFromInit(initTools) : void 0;
|
|
795
1609
|
return {
|
|
796
|
-
sessionId
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
1610
|
+
sessionId,
|
|
1611
|
+
status,
|
|
1612
|
+
pollInterval: pollIntervalForStatus(status),
|
|
1613
|
+
cursorResetTo,
|
|
1614
|
+
truncated: truncated ? true : void 0,
|
|
1615
|
+
truncatedFields: truncatedFields.length > 0 ? truncatedFields : void 0,
|
|
1616
|
+
events: toEvents(outputEvents, {
|
|
1617
|
+
includeUsage,
|
|
1618
|
+
includeModelUsage,
|
|
1619
|
+
includeStructuredOutput,
|
|
1620
|
+
slim: responseMode === "minimal"
|
|
1621
|
+
}),
|
|
1622
|
+
nextCursor,
|
|
1623
|
+
availableTools,
|
|
1624
|
+
actions: includeActions && status === "waiting_permission" ? pending.map((req) => ({
|
|
1625
|
+
type: "permission",
|
|
1626
|
+
requestId: req.requestId,
|
|
1627
|
+
toolName: req.toolName,
|
|
1628
|
+
input: req.input,
|
|
1629
|
+
summary: req.summary,
|
|
1630
|
+
decisionReason: req.decisionReason,
|
|
1631
|
+
blockedPath: req.blockedPath,
|
|
1632
|
+
toolUseID: req.toolUseID,
|
|
1633
|
+
agentID: req.agentID,
|
|
1634
|
+
suggestions: req.suggestions,
|
|
1635
|
+
description: req.description,
|
|
1636
|
+
createdAt: req.createdAt
|
|
1637
|
+
})) : void 0,
|
|
1638
|
+
result: includeResult && stored?.result ? redactAgentResult(stored.result, {
|
|
1639
|
+
includeUsage,
|
|
1640
|
+
includeModelUsage,
|
|
1641
|
+
includeStructuredOutput,
|
|
1642
|
+
slim: responseMode === "minimal"
|
|
1643
|
+
}) : void 0,
|
|
1644
|
+
cancelledAt: session?.cancelledAt,
|
|
1645
|
+
cancelledReason: session?.cancelledReason,
|
|
1646
|
+
cancelledSource: session?.cancelledSource,
|
|
1647
|
+
lastEventId: responseMode === "full" ? sessionManager.getLastEventId(sessionId) : void 0,
|
|
1648
|
+
lastToolUseId: responseMode === "full" ? session?.lastToolUseId : void 0
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
function isAgentResult(value) {
|
|
1652
|
+
if (!value || typeof value !== "object") return false;
|
|
1653
|
+
const v = value;
|
|
1654
|
+
return typeof v.sessionId === "string" && typeof v.result === "string" && typeof v.isError === "boolean" && typeof v.durationMs === "number" && typeof v.numTurns === "number" && typeof v.totalCostUsd === "number";
|
|
1655
|
+
}
|
|
1656
|
+
function redactAgentResult(result, opts) {
|
|
1657
|
+
const {
|
|
1658
|
+
usage,
|
|
1659
|
+
modelUsage,
|
|
1660
|
+
structuredOutput,
|
|
800
1661
|
durationApiMs,
|
|
801
|
-
numTurns,
|
|
802
|
-
totalCostUsd,
|
|
803
1662
|
sessionTotalTurns,
|
|
804
1663
|
sessionTotalCostUsd,
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
1664
|
+
...rest
|
|
1665
|
+
} = result;
|
|
1666
|
+
return {
|
|
1667
|
+
...rest,
|
|
1668
|
+
durationApiMs: opts.slim ? void 0 : durationApiMs,
|
|
1669
|
+
sessionTotalTurns: opts.slim ? void 0 : sessionTotalTurns,
|
|
1670
|
+
sessionTotalCostUsd: opts.slim ? void 0 : sessionTotalCostUsd,
|
|
1671
|
+
usage: opts.includeUsage ? usage : void 0,
|
|
1672
|
+
modelUsage: opts.includeModelUsage ? modelUsage : void 0,
|
|
1673
|
+
structuredOutput: opts.includeStructuredOutput ? structuredOutput : void 0
|
|
811
1674
|
};
|
|
812
1675
|
}
|
|
813
|
-
|
|
814
|
-
if (
|
|
1676
|
+
function executeClaudeCodeCheck(input, sessionManager, toolCache) {
|
|
1677
|
+
if (typeof input.sessionId !== "string" || input.sessionId.trim() === "") {
|
|
815
1678
|
return {
|
|
816
|
-
sessionId:
|
|
817
|
-
|
|
818
|
-
isError: true
|
|
819
|
-
durationMs: 0,
|
|
820
|
-
numTurns: 0,
|
|
821
|
-
totalCostUsd: 0
|
|
1679
|
+
sessionId: "",
|
|
1680
|
+
error: `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: sessionId must be a non-empty string.`,
|
|
1681
|
+
isError: true
|
|
822
1682
|
};
|
|
823
1683
|
}
|
|
824
|
-
const
|
|
825
|
-
if (
|
|
1684
|
+
const session = sessionManager.get(input.sessionId);
|
|
1685
|
+
if (!session) {
|
|
826
1686
|
return {
|
|
827
1687
|
sessionId: input.sessionId,
|
|
828
|
-
|
|
829
|
-
isError: true
|
|
830
|
-
durationMs: 0,
|
|
831
|
-
numTurns: 0,
|
|
832
|
-
totalCostUsd: 0
|
|
1688
|
+
error: `Error [${"SESSION_NOT_FOUND" /* SESSION_NOT_FOUND */}]: Session '${input.sessionId}' not found or expired.`,
|
|
1689
|
+
isError: true
|
|
833
1690
|
};
|
|
834
1691
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
let timeoutId;
|
|
838
|
-
if (input.timeout !== void 0) {
|
|
839
|
-
timeoutId = setTimeout(() => {
|
|
840
|
-
timedOut = true;
|
|
841
|
-
abortController.abort();
|
|
842
|
-
}, input.timeout);
|
|
843
|
-
}
|
|
844
|
-
let resultText = "";
|
|
845
|
-
let isError = false;
|
|
846
|
-
let durationMs = 0;
|
|
847
|
-
let durationApiMs;
|
|
848
|
-
let numTurns = 0;
|
|
849
|
-
let totalCostUsd = 0;
|
|
850
|
-
let sessionTotalTurns;
|
|
851
|
-
let sessionTotalCostUsd;
|
|
852
|
-
let newSessionId = input.sessionId;
|
|
853
|
-
let structuredOutput;
|
|
854
|
-
let stopReason;
|
|
855
|
-
let errorSubtype;
|
|
856
|
-
let usage;
|
|
857
|
-
let modelUsage;
|
|
858
|
-
let permissionDenials;
|
|
859
|
-
let seenResult = false;
|
|
860
|
-
if (!input.forkSession) {
|
|
861
|
-
const existing = sessionManager.get(input.sessionId);
|
|
862
|
-
if (!existing) {
|
|
863
|
-
const cwd = input.cwd ?? process.cwd();
|
|
864
|
-
if (typeof cwd !== "string" || cwd.trim() === "") {
|
|
865
|
-
return {
|
|
866
|
-
sessionId: input.sessionId,
|
|
867
|
-
result: `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: cwd must be a non-empty string.`,
|
|
868
|
-
isError: true,
|
|
869
|
-
durationMs: 0,
|
|
870
|
-
numTurns: 0,
|
|
871
|
-
totalCostUsd: 0
|
|
872
|
-
};
|
|
873
|
-
}
|
|
874
|
-
try {
|
|
875
|
-
sessionManager.create({
|
|
876
|
-
sessionId: input.sessionId,
|
|
877
|
-
cwd,
|
|
878
|
-
model: input.model,
|
|
879
|
-
permissionMode: effectivePermissionMode,
|
|
880
|
-
allowedTools: input.allowedTools,
|
|
881
|
-
disallowedTools: input.disallowedTools,
|
|
882
|
-
tools: input.tools,
|
|
883
|
-
maxTurns: input.maxTurns,
|
|
884
|
-
systemPrompt: input.systemPrompt,
|
|
885
|
-
agents: input.agents,
|
|
886
|
-
maxBudgetUsd: input.maxBudgetUsd,
|
|
887
|
-
effort: input.effort,
|
|
888
|
-
betas: input.betas,
|
|
889
|
-
additionalDirectories: input.additionalDirectories,
|
|
890
|
-
outputFormat: input.outputFormat,
|
|
891
|
-
thinking: input.thinking,
|
|
892
|
-
persistSession: input.persistSession,
|
|
893
|
-
pathToClaudeCodeExecutable: input.pathToClaudeCodeExecutable,
|
|
894
|
-
agent: input.agent,
|
|
895
|
-
mcpServers: input.mcpServers,
|
|
896
|
-
sandbox: input.sandbox,
|
|
897
|
-
fallbackModel: input.fallbackModel,
|
|
898
|
-
enableFileCheckpointing: input.enableFileCheckpointing,
|
|
899
|
-
includePartialMessages: input.includePartialMessages,
|
|
900
|
-
strictMcpConfig: input.strictMcpConfig,
|
|
901
|
-
settingSources: input.settingSources ?? DEFAULT_SETTING_SOURCES,
|
|
902
|
-
debug: input.debug,
|
|
903
|
-
debugFile: input.debugFile,
|
|
904
|
-
env: input.env,
|
|
905
|
-
abortController
|
|
906
|
-
});
|
|
907
|
-
} catch {
|
|
908
|
-
return executeClaudeCodeReply(input, sessionManager, allowBypass);
|
|
909
|
-
}
|
|
910
|
-
}
|
|
1692
|
+
if (input.action === "poll") {
|
|
1693
|
+
return buildResult(sessionManager, toolCache, input);
|
|
911
1694
|
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
1695
|
+
if (typeof input.requestId !== "string" || input.requestId.trim() === "") {
|
|
1696
|
+
return {
|
|
1697
|
+
sessionId: input.sessionId,
|
|
1698
|
+
error: `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: requestId is required for respond_permission.`,
|
|
1699
|
+
isError: true
|
|
917
1700
|
};
|
|
918
|
-
if (input.cwd !== void 0) options.cwd = input.cwd;
|
|
919
|
-
if (input.allowedTools !== void 0) options.allowedTools = input.allowedTools;
|
|
920
|
-
if (input.disallowedTools !== void 0) options.disallowedTools = input.disallowedTools;
|
|
921
|
-
if (input.maxTurns !== void 0) options.maxTurns = input.maxTurns;
|
|
922
|
-
if (input.model !== void 0) options.model = input.model;
|
|
923
|
-
if (input.maxBudgetUsd !== void 0) options.maxBudgetUsd = input.maxBudgetUsd;
|
|
924
|
-
if (input.agents !== void 0) options.agents = input.agents;
|
|
925
|
-
if (input.effort !== void 0) options.effort = input.effort;
|
|
926
|
-
if (input.betas !== void 0) options.betas = input.betas;
|
|
927
|
-
if (input.additionalDirectories !== void 0)
|
|
928
|
-
options.additionalDirectories = input.additionalDirectories;
|
|
929
|
-
if (input.outputFormat !== void 0) options.outputFormat = input.outputFormat;
|
|
930
|
-
if (input.thinking !== void 0) options.thinking = input.thinking;
|
|
931
|
-
if (input.tools !== void 0) options.tools = input.tools;
|
|
932
|
-
if (input.systemPrompt !== void 0) options.systemPrompt = input.systemPrompt;
|
|
933
|
-
if (input.persistSession !== void 0) options.persistSession = input.persistSession;
|
|
934
|
-
if (input.resumeSessionAt !== void 0) options.resumeSessionAt = input.resumeSessionAt;
|
|
935
|
-
if (input.pathToClaudeCodeExecutable !== void 0)
|
|
936
|
-
options.pathToClaudeCodeExecutable = input.pathToClaudeCodeExecutable;
|
|
937
|
-
if (input.agent !== void 0) options.agent = input.agent;
|
|
938
|
-
if (input.mcpServers !== void 0)
|
|
939
|
-
options.mcpServers = input.mcpServers;
|
|
940
|
-
if (input.sandbox !== void 0) options.sandbox = input.sandbox;
|
|
941
|
-
if (input.fallbackModel !== void 0) options.fallbackModel = input.fallbackModel;
|
|
942
|
-
if (input.enableFileCheckpointing !== void 0)
|
|
943
|
-
options.enableFileCheckpointing = input.enableFileCheckpointing;
|
|
944
|
-
if (input.includePartialMessages !== void 0)
|
|
945
|
-
options.includePartialMessages = input.includePartialMessages;
|
|
946
|
-
if (input.strictMcpConfig !== void 0) options.strictMcpConfig = input.strictMcpConfig;
|
|
947
|
-
if (input.settingSources !== void 0) options.settingSources = input.settingSources;
|
|
948
|
-
else options.settingSources = DEFAULT_SETTING_SOURCES;
|
|
949
|
-
if (input.debug !== void 0) options.debug = input.debug;
|
|
950
|
-
if (input.debugFile !== void 0) options.debugFile = input.debugFile;
|
|
951
|
-
if (input.env !== void 0) options.env = { ...process.env, ...input.env };
|
|
952
|
-
if (effectivePermissionMode === "bypassPermissions") {
|
|
953
|
-
options.allowDangerouslySkipPermissions = true;
|
|
954
|
-
}
|
|
955
|
-
if (input.forkSession) {
|
|
956
|
-
options.forkSession = true;
|
|
957
|
-
}
|
|
958
|
-
for await (const message of query2({
|
|
959
|
-
prompt: input.prompt,
|
|
960
|
-
options
|
|
961
|
-
})) {
|
|
962
|
-
if (message.type === "system" && message.subtype === "init") {
|
|
963
|
-
if (message.permissionMode === "bypassPermissions" && !allowBypass) {
|
|
964
|
-
isError = true;
|
|
965
|
-
resultText = `Error [${"PERMISSION_DENIED" /* PERMISSION_DENIED */}]: Cannot resume a bypassPermissions session while bypass is disabled. Use the claude_code_configure tool with action 'enable_bypass' first.`;
|
|
966
|
-
abortController.abort();
|
|
967
|
-
break;
|
|
968
|
-
}
|
|
969
|
-
if (input.forkSession) {
|
|
970
|
-
newSessionId = message.session_id;
|
|
971
|
-
if (newSessionId !== input.sessionId && !sessionManager.get(newSessionId)) {
|
|
972
|
-
try {
|
|
973
|
-
sessionManager.create({
|
|
974
|
-
sessionId: newSessionId,
|
|
975
|
-
cwd: message.cwd,
|
|
976
|
-
model: message.model,
|
|
977
|
-
permissionMode: effectivePermissionMode,
|
|
978
|
-
allowedTools: input.allowedTools,
|
|
979
|
-
disallowedTools: input.disallowedTools,
|
|
980
|
-
tools: input.tools ?? message.tools,
|
|
981
|
-
maxTurns: input.maxTurns,
|
|
982
|
-
systemPrompt: input.systemPrompt,
|
|
983
|
-
agents: input.agents,
|
|
984
|
-
maxBudgetUsd: input.maxBudgetUsd,
|
|
985
|
-
effort: input.effort,
|
|
986
|
-
betas: input.betas ?? message.betas,
|
|
987
|
-
additionalDirectories: input.additionalDirectories,
|
|
988
|
-
outputFormat: input.outputFormat,
|
|
989
|
-
thinking: input.thinking,
|
|
990
|
-
persistSession: input.persistSession,
|
|
991
|
-
pathToClaudeCodeExecutable: input.pathToClaudeCodeExecutable,
|
|
992
|
-
agent: input.agent,
|
|
993
|
-
mcpServers: input.mcpServers,
|
|
994
|
-
sandbox: input.sandbox,
|
|
995
|
-
fallbackModel: input.fallbackModel,
|
|
996
|
-
enableFileCheckpointing: input.enableFileCheckpointing,
|
|
997
|
-
includePartialMessages: input.includePartialMessages,
|
|
998
|
-
strictMcpConfig: input.strictMcpConfig,
|
|
999
|
-
settingSources: input.settingSources ?? DEFAULT_SETTING_SOURCES,
|
|
1000
|
-
debug: input.debug,
|
|
1001
|
-
debugFile: input.debugFile,
|
|
1002
|
-
env: input.env,
|
|
1003
|
-
abortController
|
|
1004
|
-
});
|
|
1005
|
-
} catch {
|
|
1006
|
-
isError = true;
|
|
1007
|
-
resultText = `Error [${"SESSION_BUSY" /* SESSION_BUSY */}]: Session is not available (status: running).`;
|
|
1008
|
-
abortController.abort();
|
|
1009
|
-
break;
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
} else {
|
|
1013
|
-
sessionManager.update(input.sessionId, {
|
|
1014
|
-
cwd: message.cwd,
|
|
1015
|
-
model: message.model,
|
|
1016
|
-
permissionMode: effectivePermissionMode,
|
|
1017
|
-
betas: input.betas ?? message.betas,
|
|
1018
|
-
tools: input.tools ?? message.tools
|
|
1019
|
-
});
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
if (message.type === "result") {
|
|
1023
|
-
if (seenResult) continue;
|
|
1024
|
-
seenResult = true;
|
|
1025
|
-
const result = message;
|
|
1026
|
-
durationMs = result.duration_ms;
|
|
1027
|
-
durationApiMs = result.duration_api_ms;
|
|
1028
|
-
numTurns = result.num_turns;
|
|
1029
|
-
totalCostUsd = result.total_cost_usd;
|
|
1030
|
-
isError = result.is_error;
|
|
1031
|
-
stopReason = result.stop_reason;
|
|
1032
|
-
usage = result.usage;
|
|
1033
|
-
modelUsage = result.modelUsage;
|
|
1034
|
-
permissionDenials = result.permission_denials;
|
|
1035
|
-
if (result.subtype === "success") {
|
|
1036
|
-
resultText = result.result;
|
|
1037
|
-
structuredOutput = result.structured_output;
|
|
1038
|
-
} else {
|
|
1039
|
-
isError = true;
|
|
1040
|
-
errorSubtype = result.subtype;
|
|
1041
|
-
resultText = result.errors.map(String).join("\n") || `Error [${result.subtype}]: Unknown error`;
|
|
1042
|
-
}
|
|
1043
|
-
break;
|
|
1044
|
-
}
|
|
1045
|
-
}
|
|
1046
|
-
} catch (err) {
|
|
1047
|
-
isError = true;
|
|
1048
|
-
const isAborted = abortController.signal.aborted || err instanceof AbortError2 || err instanceof Error && err.name === "AbortError";
|
|
1049
|
-
if (isAborted) {
|
|
1050
|
-
resultText = timedOut ? `Error [${"TIMEOUT" /* TIMEOUT */}]: Session timed out after ${input.timeout}ms.` : `Error [${"CANCELLED" /* CANCELLED */}]: Session was cancelled.`;
|
|
1051
|
-
} else {
|
|
1052
|
-
resultText = enhanceWindowsError(err instanceof Error ? err.message : String(err));
|
|
1053
|
-
}
|
|
1054
|
-
} finally {
|
|
1055
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
1056
|
-
}
|
|
1057
|
-
if (!seenResult && !isError && abortController.signal.aborted) {
|
|
1058
|
-
isError = true;
|
|
1059
|
-
resultText = timedOut ? `Error [${"TIMEOUT" /* TIMEOUT */}]: Session timed out after ${input.timeout}ms.` : `Error [${"CANCELLED" /* CANCELLED */}]: Session was cancelled.`;
|
|
1060
|
-
}
|
|
1061
|
-
if (!seenResult && !isError) {
|
|
1062
|
-
isError = true;
|
|
1063
|
-
errorSubtype = errorSubtype ?? "missing_result";
|
|
1064
|
-
const noResultMsg = `Error [${"INTERNAL" /* INTERNAL */}]: No result message received from agent.`;
|
|
1065
|
-
resultText = resultText ? `${noResultMsg} Original: ${resultText}` : noResultMsg;
|
|
1066
|
-
}
|
|
1067
|
-
const targetSessionId = input.forkSession ? newSessionId : input.sessionId;
|
|
1068
|
-
const tracked = sessionManager.get(targetSessionId);
|
|
1069
|
-
if (tracked) {
|
|
1070
|
-
const nextTurns = input.forkSession ? numTurns : (tracked.totalTurns ?? 0) + numTurns;
|
|
1071
|
-
const nextCost = input.forkSession ? totalCostUsd : (tracked.totalCostUsd ?? 0) + totalCostUsd;
|
|
1072
|
-
if (tracked.status !== "cancelled") {
|
|
1073
|
-
sessionManager.update(targetSessionId, {
|
|
1074
|
-
status: isError ? "error" : "idle",
|
|
1075
|
-
totalTurns: nextTurns,
|
|
1076
|
-
totalCostUsd: nextCost,
|
|
1077
|
-
abortController: void 0
|
|
1078
|
-
});
|
|
1079
|
-
} else {
|
|
1080
|
-
sessionManager.update(targetSessionId, {
|
|
1081
|
-
totalTurns: nextTurns,
|
|
1082
|
-
totalCostUsd: nextCost,
|
|
1083
|
-
abortController: void 0
|
|
1084
|
-
});
|
|
1085
|
-
}
|
|
1086
|
-
const updated = sessionManager.get(targetSessionId);
|
|
1087
|
-
sessionTotalTurns = updated?.totalTurns;
|
|
1088
|
-
sessionTotalCostUsd = updated?.totalCostUsd;
|
|
1089
1701
|
}
|
|
1090
|
-
if (input.
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1702
|
+
if (input.decision !== "allow" && input.decision !== "deny") {
|
|
1703
|
+
return {
|
|
1704
|
+
sessionId: input.sessionId,
|
|
1705
|
+
error: `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: decision must be 'allow' or 'deny'.`,
|
|
1706
|
+
isError: true
|
|
1707
|
+
};
|
|
1094
1708
|
}
|
|
1095
|
-
|
|
1096
|
-
sessionId
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1709
|
+
const ok = sessionManager.finishRequest(
|
|
1710
|
+
input.sessionId,
|
|
1711
|
+
input.requestId,
|
|
1712
|
+
toPermissionResult({
|
|
1713
|
+
decision: input.decision,
|
|
1714
|
+
updatedInput: input.permissionOptions?.updatedInput,
|
|
1715
|
+
updatedPermissions: input.permissionOptions?.updatedPermissions,
|
|
1716
|
+
denyMessage: input.denyMessage,
|
|
1717
|
+
interrupt: input.interrupt
|
|
1718
|
+
}),
|
|
1719
|
+
"respond"
|
|
1720
|
+
);
|
|
1721
|
+
if (!ok) {
|
|
1722
|
+
return {
|
|
1723
|
+
sessionId: input.sessionId,
|
|
1724
|
+
error: `Error [${"PERMISSION_REQUEST_NOT_FOUND" /* PERMISSION_REQUEST_NOT_FOUND */}]: requestId '${input.requestId}' not found (already finished or expired).`,
|
|
1725
|
+
isError: true
|
|
1726
|
+
};
|
|
1727
|
+
}
|
|
1728
|
+
return buildResult(sessionManager, toolCache, input);
|
|
1112
1729
|
}
|
|
1113
1730
|
|
|
1114
1731
|
// src/tools/claude-code-session.ts
|
|
@@ -1145,7 +1762,10 @@ function executeClaudeCodeSession(input, sessionManager) {
|
|
|
1145
1762
|
isError: true
|
|
1146
1763
|
};
|
|
1147
1764
|
}
|
|
1148
|
-
const cancelled = sessionManager.cancel(input.sessionId
|
|
1765
|
+
const cancelled = sessionManager.cancel(input.sessionId, {
|
|
1766
|
+
reason: "Cancelled by caller",
|
|
1767
|
+
source: "claude_code_session"
|
|
1768
|
+
});
|
|
1149
1769
|
if (!cancelled) {
|
|
1150
1770
|
const session = sessionManager.get(input.sessionId);
|
|
1151
1771
|
if (!session) {
|
|
@@ -1176,164 +1796,117 @@ function executeClaudeCodeSession(input, sessionManager) {
|
|
|
1176
1796
|
}
|
|
1177
1797
|
}
|
|
1178
1798
|
|
|
1179
|
-
// src/tools/claude-code-configure.ts
|
|
1180
|
-
function executeClaudeCodeConfigure(input, config) {
|
|
1181
|
-
switch (input.action) {
|
|
1182
|
-
case "enable_bypass":
|
|
1183
|
-
config.setAllowBypass(true);
|
|
1184
|
-
return {
|
|
1185
|
-
allowBypass: true,
|
|
1186
|
-
message: "bypassPermissions mode is now enabled for this server session. Use with caution."
|
|
1187
|
-
};
|
|
1188
|
-
case "disable_bypass":
|
|
1189
|
-
config.setAllowBypass(false);
|
|
1190
|
-
return {
|
|
1191
|
-
allowBypass: false,
|
|
1192
|
-
message: "bypassPermissions mode is now disabled."
|
|
1193
|
-
};
|
|
1194
|
-
case "get_config":
|
|
1195
|
-
return {
|
|
1196
|
-
allowBypass: config.getAllowBypass(),
|
|
1197
|
-
message: `Current config: bypassPermissions ${config.getAllowBypass() ? "enabled" : "disabled"}.`
|
|
1198
|
-
};
|
|
1199
|
-
default:
|
|
1200
|
-
return {
|
|
1201
|
-
allowBypass: config.getAllowBypass(),
|
|
1202
|
-
message: `Error [${"INVALID_ARGUMENT" /* INVALID_ARGUMENT */}]: Unknown action '${input.action}'. Use 'enable_bypass', 'disable_bypass', or 'get_config'.`,
|
|
1203
|
-
isError: true
|
|
1204
|
-
};
|
|
1205
|
-
}
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
1799
|
// src/server.ts
|
|
1209
|
-
var SERVER_VERSION = true ? "
|
|
1210
|
-
function createServer(serverCwd
|
|
1211
|
-
const
|
|
1212
|
-
|
|
1213
|
-
const parsed = Number.parseInt(value, 10);
|
|
1214
|
-
if (!Number.isFinite(parsed) || parsed <= 0) return void 0;
|
|
1215
|
-
return parsed;
|
|
1216
|
-
};
|
|
1217
|
-
const sessionManager = new SessionManager({
|
|
1218
|
-
sessionTtlMs: parsePositiveInt(process.env.CLAUDE_CODE_MCP_SESSION_TTL_MS),
|
|
1219
|
-
runningSessionMaxMs: parsePositiveInt(process.env.CLAUDE_CODE_MCP_RUNNING_SESSION_MAX_MS),
|
|
1220
|
-
cleanupIntervalMs: parsePositiveInt(process.env.CLAUDE_CODE_MCP_CLEANUP_INTERVAL_MS)
|
|
1221
|
-
});
|
|
1222
|
-
let allowBypass = opts?.allowBypass ?? false;
|
|
1223
|
-
const config = {
|
|
1224
|
-
getAllowBypass: () => allowBypass,
|
|
1225
|
-
setAllowBypass: (v) => {
|
|
1226
|
-
allowBypass = v;
|
|
1227
|
-
}
|
|
1228
|
-
};
|
|
1800
|
+
var SERVER_VERSION = true ? "2.0.0" : "0.0.0-dev";
|
|
1801
|
+
function createServer(serverCwd) {
|
|
1802
|
+
const sessionManager = new SessionManager();
|
|
1803
|
+
const toolCache = new ToolDiscoveryCache();
|
|
1229
1804
|
const server = new McpServer({
|
|
1230
1805
|
name: "claude-code-mcp",
|
|
1231
1806
|
version: SERVER_VERSION
|
|
1232
1807
|
});
|
|
1808
|
+
const agentDefinitionSchema = z.object({
|
|
1809
|
+
description: z.string(),
|
|
1810
|
+
prompt: z.string(),
|
|
1811
|
+
tools: z.array(z.string()).optional(),
|
|
1812
|
+
disallowedTools: z.array(z.string()).optional(),
|
|
1813
|
+
model: z.enum(AGENT_MODELS).optional(),
|
|
1814
|
+
maxTurns: z.number().int().positive().optional(),
|
|
1815
|
+
mcpServers: z.array(z.union([z.string(), z.record(z.string(), z.unknown())])).optional(),
|
|
1816
|
+
skills: z.array(z.string()).optional(),
|
|
1817
|
+
criticalSystemReminder_EXPERIMENTAL: z.string().optional()
|
|
1818
|
+
});
|
|
1819
|
+
const systemPromptSchema = z.union([
|
|
1820
|
+
z.string(),
|
|
1821
|
+
z.object({
|
|
1822
|
+
type: z.literal("preset"),
|
|
1823
|
+
preset: z.literal("claude_code"),
|
|
1824
|
+
append: z.string().optional().describe("Additional instructions to append to the preset")
|
|
1825
|
+
})
|
|
1826
|
+
]);
|
|
1827
|
+
const toolsConfigSchema = z.union([
|
|
1828
|
+
z.array(z.string()),
|
|
1829
|
+
z.object({
|
|
1830
|
+
type: z.literal("preset"),
|
|
1831
|
+
preset: z.literal("claude_code")
|
|
1832
|
+
})
|
|
1833
|
+
]);
|
|
1834
|
+
const thinkingSchema = z.union([
|
|
1835
|
+
z.object({ type: z.literal("adaptive") }),
|
|
1836
|
+
z.object({
|
|
1837
|
+
type: z.literal("enabled"),
|
|
1838
|
+
budgetTokens: z.number().int().positive().describe("Token budget for thinking")
|
|
1839
|
+
}),
|
|
1840
|
+
z.object({ type: z.literal("disabled") })
|
|
1841
|
+
]);
|
|
1842
|
+
const outputFormatSchema = z.object({
|
|
1843
|
+
type: z.literal("json_schema"),
|
|
1844
|
+
schema: z.record(z.string(), z.unknown()).describe("JSON Schema for structured output")
|
|
1845
|
+
});
|
|
1846
|
+
const advancedOptionsSchema = z.object({
|
|
1847
|
+
tools: toolsConfigSchema.optional().describe(
|
|
1848
|
+
"Define the base tool set visible to the agent. Default: omitted (SDK/Claude Code default). Pass an array of tool names, or {type: 'preset', preset: 'claude_code'} for the default set."
|
|
1849
|
+
),
|
|
1850
|
+
persistSession: z.boolean().optional().describe("Persist session history to disk (~/.claude/projects). Default: true."),
|
|
1851
|
+
sessionInitTimeoutMs: z.number().int().positive().optional().describe("How long to wait (in ms) for the agent process to initialize. Default: 10000."),
|
|
1852
|
+
agents: z.record(z.string(), agentDefinitionSchema).optional().describe(
|
|
1853
|
+
"Define custom sub-agents the main agent can delegate tasks to. Each key is the agent name."
|
|
1854
|
+
),
|
|
1855
|
+
agent: z.string().optional().describe("Name of a custom agent (defined in 'agents') to use as the primary agent."),
|
|
1856
|
+
maxBudgetUsd: z.number().positive().optional().describe("Maximum budget in USD for this session."),
|
|
1857
|
+
effort: z.enum(EFFORT_LEVELS).optional().describe("Effort level: 'low' | 'medium' | 'high' | 'max'."),
|
|
1858
|
+
betas: z.array(z.string()).optional().describe("Beta features to enable."),
|
|
1859
|
+
additionalDirectories: z.array(z.string()).optional().describe("Additional directories the agent can access beyond cwd."),
|
|
1860
|
+
outputFormat: outputFormatSchema.optional().describe("Structured output format with JSON Schema."),
|
|
1861
|
+
thinking: thinkingSchema.optional().describe("Thinking mode: 'adaptive' | 'enabled' (with budget) | 'disabled'."),
|
|
1862
|
+
pathToClaudeCodeExecutable: z.string().optional().describe("Path to the Claude Code executable. Default: SDK-bundled."),
|
|
1863
|
+
mcpServers: z.record(z.string(), z.record(z.string(), z.unknown())).optional().describe("MCP server configurations (key: server name, value: server config)."),
|
|
1864
|
+
sandbox: z.record(z.string(), z.unknown()).optional().describe("Sandbox configuration for isolating shell command execution."),
|
|
1865
|
+
fallbackModel: z.string().optional().describe("Fallback model if the primary model fails or is unavailable."),
|
|
1866
|
+
enableFileCheckpointing: z.boolean().optional().describe("Enable file checkpointing to track file changes. Default: false."),
|
|
1867
|
+
includePartialMessages: z.boolean().optional().describe("Include intermediate messages as events in claude_code_check. Default: false."),
|
|
1868
|
+
strictMcpConfig: z.boolean().optional().describe("Enforce strict validation of MCP server configurations. Default: false."),
|
|
1869
|
+
settingSources: z.array(z.enum(["user", "project", "local"])).optional().describe(
|
|
1870
|
+
"Which local config files to load. Default: ['user', 'project', 'local']. Pass [] to disable all."
|
|
1871
|
+
),
|
|
1872
|
+
debug: z.boolean().optional().describe("Enable debug mode. Default: false."),
|
|
1873
|
+
debugFile: z.string().optional().describe("Write debug logs to a file path (implicitly enables debug mode)."),
|
|
1874
|
+
env: z.record(z.string(), z.string().optional()).optional().describe("Environment variables to merge with process.env.")
|
|
1875
|
+
}).optional().describe(
|
|
1876
|
+
"Low-frequency SDK options. All fields are optional with sensible defaults. Most callers can omit this entirely."
|
|
1877
|
+
);
|
|
1233
1878
|
server.tool(
|
|
1234
1879
|
"claude_code",
|
|
1235
|
-
|
|
1236
|
-
Returns a sessionId that can be passed to claude_code_reply for multi-turn conversations.
|
|
1237
|
-
Defaults: permissionMode="dontAsk" (auto-approves allowed tools without prompting), loads all local Claude settings (user, project, local) including CLAUDE.md.`,
|
|
1880
|
+
buildInternalToolsDescription(toolCache.getTools()),
|
|
1238
1881
|
{
|
|
1239
1882
|
prompt: z.string().describe("The task or question for Claude Code"),
|
|
1240
|
-
cwd: z.string().optional().describe("Working directory
|
|
1883
|
+
cwd: z.string().optional().describe("Working directory. Default: server cwd."),
|
|
1241
1884
|
allowedTools: z.array(z.string()).optional().describe(
|
|
1242
|
-
"
|
|
1885
|
+
"Tools the agent can use without asking for permission. Default: [] (no auto-approvals). Example: ['Bash', 'Read', 'Write', 'Edit']. Tools not listed here or in disallowedTools will trigger a permission request via claude_code_check."
|
|
1243
1886
|
),
|
|
1244
1887
|
disallowedTools: z.array(z.string()).optional().describe(
|
|
1245
|
-
"
|
|
1246
|
-
),
|
|
1247
|
-
tools: z.union([
|
|
1248
|
-
z.array(z.string()),
|
|
1249
|
-
z.object({
|
|
1250
|
-
type: z.literal("preset"),
|
|
1251
|
-
preset: z.literal("claude_code")
|
|
1252
|
-
})
|
|
1253
|
-
]).optional().describe(
|
|
1254
|
-
"Define the base tool set for the session. Pass an array of tool name strings, or use {type: 'preset', preset: 'claude_code'} for the default Claude Code toolset. allowedTools/disallowedTools further filter on top of this base set."
|
|
1255
|
-
),
|
|
1256
|
-
persistSession: z.boolean().optional().describe(
|
|
1257
|
-
"Persist session history to disk (~/.claude/projects). Default: true. Set false to disable persistence."
|
|
1888
|
+
"Tools the agent is forbidden from using. Default: [] (none). Takes priority over allowedTools."
|
|
1258
1889
|
),
|
|
1259
|
-
|
|
1260
|
-
|
|
1890
|
+
maxTurns: z.number().int().positive().optional().describe("Maximum number of reasoning steps the agent can take."),
|
|
1891
|
+
model: z.string().optional().describe("Model to use, e.g. 'claude-sonnet-4-5-20250929'."),
|
|
1892
|
+
systemPrompt: systemPromptSchema.optional().describe(
|
|
1893
|
+
"Override the agent's system prompt. Pass a string for full replacement, or use {type: 'preset', preset: 'claude_code', append: '...'} to extend the default."
|
|
1261
1894
|
),
|
|
1262
|
-
|
|
1263
|
-
"
|
|
1895
|
+
permissionRequestTimeoutMs: z.number().int().positive().optional().describe(
|
|
1896
|
+
"How long to wait (in ms) for a permission decision via claude_code_check before auto-denying. Default: 60000."
|
|
1264
1897
|
),
|
|
1265
|
-
|
|
1266
|
-
systemPrompt: z.union([
|
|
1267
|
-
z.string(),
|
|
1268
|
-
z.object({
|
|
1269
|
-
type: z.literal("preset"),
|
|
1270
|
-
preset: z.literal("claude_code"),
|
|
1271
|
-
append: z.string().optional().describe("Additional instructions to append to the preset")
|
|
1272
|
-
})
|
|
1273
|
-
]).optional().describe(
|
|
1274
|
-
"Override the agent's system prompt. Pass a string for full replacement, or use {type: 'preset', preset: 'claude_code', append: '...'} to extend the default Claude Code prompt with additional instructions."
|
|
1275
|
-
),
|
|
1276
|
-
agents: z.record(
|
|
1277
|
-
z.string(),
|
|
1278
|
-
z.object({
|
|
1279
|
-
description: z.string(),
|
|
1280
|
-
prompt: z.string(),
|
|
1281
|
-
tools: z.array(z.string()).optional(),
|
|
1282
|
-
disallowedTools: z.array(z.string()).optional(),
|
|
1283
|
-
model: z.enum(AGENT_MODELS).optional(),
|
|
1284
|
-
maxTurns: z.number().int().positive().optional(),
|
|
1285
|
-
mcpServers: z.array(z.union([z.string(), z.record(z.string(), z.unknown())])).optional(),
|
|
1286
|
-
skills: z.array(z.string()).optional(),
|
|
1287
|
-
criticalSystemReminder_EXPERIMENTAL: z.string().optional()
|
|
1288
|
-
})
|
|
1289
|
-
).optional().describe(
|
|
1290
|
-
"Define custom sub-agents that the main agent can delegate tasks to. Each key is the agent name; the value specifies its system prompt, available tools, model, and other constraints."
|
|
1291
|
-
),
|
|
1292
|
-
maxBudgetUsd: z.number().positive().optional().describe("Maximum budget in USD for this session"),
|
|
1293
|
-
effort: z.enum(EFFORT_LEVELS).optional().describe(
|
|
1294
|
-
"Effort level: 'low' (fast), 'medium' (balanced), 'high' (thorough), 'max' (maximum)"
|
|
1295
|
-
),
|
|
1296
|
-
betas: z.array(z.string()).optional().describe("Beta features to enable (e.g. ['context-1m-2025-08-07'])"),
|
|
1297
|
-
additionalDirectories: z.array(z.string()).optional().describe("Additional directories the agent can access beyond cwd"),
|
|
1298
|
-
outputFormat: z.object({
|
|
1299
|
-
type: z.literal("json_schema"),
|
|
1300
|
-
schema: z.record(z.string(), z.unknown()).describe("JSON Schema for structured output")
|
|
1301
|
-
}).optional().describe("Structured output format with JSON Schema (omit for plain text output)"),
|
|
1302
|
-
thinking: z.union([
|
|
1303
|
-
z.object({ type: z.literal("adaptive") }),
|
|
1304
|
-
z.object({
|
|
1305
|
-
type: z.literal("enabled"),
|
|
1306
|
-
budgetTokens: z.number().int().positive().describe("Token budget for thinking")
|
|
1307
|
-
}),
|
|
1308
|
-
z.object({ type: z.literal("disabled") })
|
|
1309
|
-
]).optional().describe("Thinking mode: 'adaptive' (auto), 'enabled' (with budget), or 'disabled'"),
|
|
1310
|
-
timeout: z.number().int().positive().optional().describe("Timeout in milliseconds for this session"),
|
|
1311
|
-
pathToClaudeCodeExecutable: z.string().optional().describe("Path to the Claude Code executable"),
|
|
1312
|
-
agent: z.string().optional().describe(
|
|
1313
|
-
"Name of a custom agent (defined in 'agents' parameter) to use as the primary agent for this session, applying its system prompt, tool restrictions, and model override."
|
|
1314
|
-
),
|
|
1315
|
-
mcpServers: z.record(z.string(), z.record(z.string(), z.unknown())).optional().describe("MCP server configurations (key: server name, value: server config)"),
|
|
1316
|
-
sandbox: z.record(z.string(), z.unknown()).optional().describe(
|
|
1317
|
-
"Sandbox configuration for isolating shell command execution (e.g., Docker container settings). Controls the execution environment for Bash tool calls."
|
|
1318
|
-
),
|
|
1319
|
-
fallbackModel: z.string().optional().describe("Fallback model if the primary model fails or is unavailable"),
|
|
1320
|
-
enableFileCheckpointing: z.boolean().optional().describe("Enable file checkpointing to track file changes during the session"),
|
|
1321
|
-
includePartialMessages: z.boolean().optional().describe(
|
|
1322
|
-
"When true, includes intermediate streaming messages in the response (e.g., partial tool outputs as they arrive). Useful for real-time progress monitoring. Default: false."
|
|
1323
|
-
),
|
|
1324
|
-
strictMcpConfig: z.boolean().optional().describe("Enforce strict validation of MCP server configurations"),
|
|
1325
|
-
settingSources: z.array(z.enum(["user", "project", "local"])).optional().describe(
|
|
1326
|
-
'Control which filesystem settings are loaded. Defaults to ["user", "project", "local"] (loads all settings including ~/.claude/settings.json, .claude/settings.json, .claude/settings.local.json, and CLAUDE.md). Pass an empty array [] to disable all settings (SDK isolation mode).'
|
|
1327
|
-
),
|
|
1328
|
-
debug: z.boolean().optional().describe("Enable debug mode for verbose logging"),
|
|
1329
|
-
debugFile: z.string().optional().describe("Write debug logs to a specific file path (implicitly enables debug mode)"),
|
|
1330
|
-
env: z.record(z.string(), z.string().optional()).optional().describe(
|
|
1331
|
-
"Environment variables to merge with process.env and pass to the Claude Code process (user-provided values take precedence)"
|
|
1332
|
-
)
|
|
1898
|
+
advanced: advancedOptionsSchema
|
|
1333
1899
|
},
|
|
1334
|
-
async (args) => {
|
|
1900
|
+
async (args, extra) => {
|
|
1335
1901
|
try {
|
|
1336
|
-
const result = await executeClaudeCode(
|
|
1902
|
+
const result = await executeClaudeCode(
|
|
1903
|
+
args,
|
|
1904
|
+
sessionManager,
|
|
1905
|
+
serverCwd,
|
|
1906
|
+
toolCache,
|
|
1907
|
+
extra.signal
|
|
1908
|
+
);
|
|
1909
|
+
const isError = typeof result.error === "string";
|
|
1337
1910
|
return {
|
|
1338
1911
|
content: [
|
|
1339
1912
|
{
|
|
@@ -1341,7 +1914,7 @@ Defaults: permissionMode="dontAsk" (auto-approves allowed tools without promptin
|
|
|
1341
1914
|
text: JSON.stringify(result, null, 2)
|
|
1342
1915
|
}
|
|
1343
1916
|
],
|
|
1344
|
-
isError
|
|
1917
|
+
isError
|
|
1345
1918
|
};
|
|
1346
1919
|
} catch (err) {
|
|
1347
1920
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -1359,102 +1932,69 @@ Defaults: permissionMode="dontAsk" (auto-approves allowed tools without promptin
|
|
|
1359
1932
|
);
|
|
1360
1933
|
server.tool(
|
|
1361
1934
|
"claude_code_reply",
|
|
1362
|
-
`
|
|
1363
|
-
|
|
1935
|
+
`Send a follow-up message to an existing Claude Code session.
|
|
1936
|
+
|
|
1937
|
+
The agent retains full context from previous turns (files read, code analyzed, conversation history). Returns immediately \u2014 use claude_code_check to poll for the result.
|
|
1938
|
+
|
|
1939
|
+
Supports session forking (forkSession=true) to explore alternative approaches without modifying the original session.
|
|
1940
|
+
|
|
1941
|
+
Defaults:
|
|
1942
|
+
- forkSession: false
|
|
1943
|
+
- sessionInitTimeoutMs: 10000 (only used when forkSession=true)
|
|
1944
|
+
- permissionRequestTimeoutMs: 60000
|
|
1945
|
+
- Disk resume: disabled unless CLAUDE_CODE_MCP_ALLOW_DISK_RESUME=1
|
|
1946
|
+
|
|
1947
|
+
Disk resume: If the server restarted and the session is no longer in memory, set CLAUDE_CODE_MCP_ALLOW_DISK_RESUME=1 to let the agent resume from its on-disk transcript. Pass diskResumeConfig with resumeToken and session parameters.`,
|
|
1364
1948
|
{
|
|
1365
1949
|
sessionId: z.string().describe("The session ID to continue (from a previous claude_code call)"),
|
|
1366
1950
|
prompt: z.string().describe("Follow-up prompt or instruction"),
|
|
1367
1951
|
forkSession: z.boolean().optional().describe(
|
|
1368
|
-
"
|
|
1369
|
-
),
|
|
1370
|
-
timeout: z.number().int().positive().optional().describe("Timeout in milliseconds for this reply"),
|
|
1371
|
-
// Optional disk-resume overrides (only used when CLAUDE_CODE_MCP_ALLOW_DISK_RESUME=1
|
|
1372
|
-
// and the in-memory session metadata is missing)
|
|
1373
|
-
cwd: z.string().optional().describe("Working directory (disk resume fallback)"),
|
|
1374
|
-
allowedTools: z.array(z.string()).optional().describe(
|
|
1375
|
-
"Auto-approved tool names (disk resume fallback). See claude_code tool for details."
|
|
1952
|
+
"Branch this session into a new copy that diverges from the current point. The original session remains unchanged. Default: false."
|
|
1376
1953
|
),
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
z.object({
|
|
1381
|
-
type: z.literal("preset"),
|
|
1382
|
-
preset: z.literal("claude_code")
|
|
1383
|
-
})
|
|
1384
|
-
]).optional().describe("Base tool set (disk resume fallback). See claude_code tool for details."),
|
|
1385
|
-
persistSession: z.boolean().optional().describe("Persist session history to disk (~/.claude/projects). Default: true."),
|
|
1386
|
-
permissionMode: z.enum(PERMISSION_MODES).optional().describe("Permission mode (disk resume fallback). See claude_code tool for details."),
|
|
1387
|
-
maxTurns: z.number().int().positive().optional().describe("Maximum number of agent reasoning steps for this reply."),
|
|
1388
|
-
model: z.string().optional().describe("Model to use, e.g. 'claude-sonnet-4-5-20250929'"),
|
|
1389
|
-
systemPrompt: z.union([
|
|
1390
|
-
z.string(),
|
|
1391
|
-
z.object({
|
|
1392
|
-
type: z.literal("preset"),
|
|
1393
|
-
preset: z.literal("claude_code"),
|
|
1394
|
-
append: z.string().optional().describe("Additional instructions to append to the preset")
|
|
1395
|
-
})
|
|
1396
|
-
]).optional().describe("Override the agent's system prompt. See claude_code tool for details."),
|
|
1397
|
-
agents: z.record(
|
|
1398
|
-
z.string(),
|
|
1399
|
-
z.object({
|
|
1400
|
-
description: z.string(),
|
|
1401
|
-
prompt: z.string(),
|
|
1402
|
-
tools: z.array(z.string()).optional(),
|
|
1403
|
-
disallowedTools: z.array(z.string()).optional(),
|
|
1404
|
-
model: z.enum(AGENT_MODELS).optional(),
|
|
1405
|
-
maxTurns: z.number().int().positive().optional(),
|
|
1406
|
-
mcpServers: z.array(z.union([z.string(), z.record(z.string(), z.unknown())])).optional(),
|
|
1407
|
-
skills: z.array(z.string()).optional(),
|
|
1408
|
-
criticalSystemReminder_EXPERIMENTAL: z.string().optional()
|
|
1409
|
-
})
|
|
1410
|
-
).optional().describe("Define custom sub-agents. See claude_code tool for details."),
|
|
1411
|
-
maxBudgetUsd: z.number().positive().optional().describe("Maximum budget in USD for this reply"),
|
|
1412
|
-
effort: z.enum(EFFORT_LEVELS).optional().describe(
|
|
1413
|
-
"Effort level: 'low' (fast), 'medium' (balanced), 'high' (thorough), 'max' (maximum)"
|
|
1954
|
+
sessionInitTimeoutMs: z.number().int().positive().optional().describe("How long to wait (in ms) for a forked session to initialize. Default: 10000."),
|
|
1955
|
+
permissionRequestTimeoutMs: z.number().int().positive().optional().describe(
|
|
1956
|
+
"How long to wait (in ms) for a permission decision via claude_code_check before auto-denying. Default: 60000."
|
|
1414
1957
|
),
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
z.
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
z.
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
"Sandbox configuration
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
debug: z.boolean().optional().describe("Enable debug mode for verbose logging"),
|
|
1450
|
-
debugFile: z.string().optional().describe("Write debug logs to a specific file path (implicitly enables debug mode)"),
|
|
1451
|
-
env: z.record(z.string(), z.string().optional()).optional().describe(
|
|
1452
|
-
"Environment variables to merge with process.env and pass to the Claude Code process (user-provided values take precedence)"
|
|
1958
|
+
diskResumeConfig: z.object({
|
|
1959
|
+
resumeToken: z.string().optional().describe(
|
|
1960
|
+
"Resume token returned by claude_code / claude_code_reply. Required for disk resume."
|
|
1961
|
+
),
|
|
1962
|
+
cwd: z.string().optional().describe("Working directory. Required for disk resume."),
|
|
1963
|
+
allowedTools: z.array(z.string()).optional().describe("Tools the agent can use without permission."),
|
|
1964
|
+
disallowedTools: z.array(z.string()).optional().describe("Tools the agent is forbidden from using."),
|
|
1965
|
+
tools: toolsConfigSchema.optional().describe("Which tools the agent can see."),
|
|
1966
|
+
persistSession: z.boolean().optional().describe("Persist session history to disk. Default: true."),
|
|
1967
|
+
maxTurns: z.number().int().positive().optional().describe("Maximum reasoning steps."),
|
|
1968
|
+
model: z.string().optional().describe("Model to use."),
|
|
1969
|
+
systemPrompt: systemPromptSchema.optional().describe("Override the agent's system prompt."),
|
|
1970
|
+
agents: z.record(z.string(), agentDefinitionSchema).optional().describe("Define custom sub-agents."),
|
|
1971
|
+
agent: z.string().optional().describe("Name of a custom agent to use as primary."),
|
|
1972
|
+
maxBudgetUsd: z.number().positive().optional().describe("Maximum budget in USD."),
|
|
1973
|
+
effort: z.enum(EFFORT_LEVELS).optional().describe("Effort level."),
|
|
1974
|
+
betas: z.array(z.string()).optional().describe("Beta features to enable."),
|
|
1975
|
+
additionalDirectories: z.array(z.string()).optional().describe("Additional accessible directories."),
|
|
1976
|
+
outputFormat: outputFormatSchema.optional().describe("Structured output format."),
|
|
1977
|
+
thinking: thinkingSchema.optional().describe("Thinking mode configuration."),
|
|
1978
|
+
resumeSessionAt: z.string().optional().describe("Resume only up to a specific message UUID."),
|
|
1979
|
+
pathToClaudeCodeExecutable: z.string().optional().describe("Path to the Claude Code executable."),
|
|
1980
|
+
mcpServers: z.record(z.string(), z.record(z.string(), z.unknown())).optional().describe("MCP server configurations."),
|
|
1981
|
+
sandbox: z.record(z.string(), z.unknown()).optional().describe("Sandbox configuration."),
|
|
1982
|
+
fallbackModel: z.string().optional().describe("Fallback model."),
|
|
1983
|
+
enableFileCheckpointing: z.boolean().optional().describe("Enable file checkpointing. Default: false."),
|
|
1984
|
+
includePartialMessages: z.boolean().optional().describe("Include intermediate messages as events. Default: false."),
|
|
1985
|
+
strictMcpConfig: z.boolean().optional().describe("Enforce strict MCP validation. Default: false."),
|
|
1986
|
+
settingSources: z.array(z.enum(["user", "project", "local"])).optional().describe("Which local config files to load."),
|
|
1987
|
+
debug: z.boolean().optional().describe("Enable debug mode. Default: false."),
|
|
1988
|
+
debugFile: z.string().optional().describe("Write debug logs to a file path."),
|
|
1989
|
+
env: z.record(z.string(), z.string().optional()).optional().describe("Environment variables to merge with process.env.")
|
|
1990
|
+
}).optional().describe(
|
|
1991
|
+
"Disk resume fallback configuration. Only needed when CLAUDE_CODE_MCP_ALLOW_DISK_RESUME=1 and the in-memory session is missing. Contains resumeToken + all session config overrides."
|
|
1453
1992
|
)
|
|
1454
1993
|
},
|
|
1455
|
-
async (args) => {
|
|
1994
|
+
async (args, extra) => {
|
|
1456
1995
|
try {
|
|
1457
|
-
const result = await executeClaudeCodeReply(args, sessionManager,
|
|
1996
|
+
const result = await executeClaudeCodeReply(args, sessionManager, toolCache, extra.signal);
|
|
1997
|
+
const isError = typeof result.error === "string";
|
|
1458
1998
|
return {
|
|
1459
1999
|
content: [
|
|
1460
2000
|
{
|
|
@@ -1462,7 +2002,7 @@ Note: When CLAUDE_CODE_MCP_ALLOW_DISK_RESUME=1 is set and the in-memory session
|
|
|
1462
2002
|
text: JSON.stringify(result, null, 2)
|
|
1463
2003
|
}
|
|
1464
2004
|
],
|
|
1465
|
-
isError
|
|
2005
|
+
isError
|
|
1466
2006
|
};
|
|
1467
2007
|
} catch (err) {
|
|
1468
2008
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -1480,32 +2020,19 @@ Note: When CLAUDE_CODE_MCP_ALLOW_DISK_RESUME=1 is set and the in-memory session
|
|
|
1480
2020
|
);
|
|
1481
2021
|
server.tool(
|
|
1482
2022
|
"claude_code_session",
|
|
1483
|
-
`
|
|
2023
|
+
`List, inspect, or cancel Claude Code sessions.
|
|
2024
|
+
|
|
2025
|
+
- action="list": Get all sessions with their status, cost, turn count, and settings.
|
|
2026
|
+
- action="get": Get full details for one session (pass sessionId). Add includeSensitive=true to also see cwd, systemPrompt, agents, and additionalDirectories.
|
|
2027
|
+
- action="cancel": Stop a running session immediately (pass sessionId).`,
|
|
1484
2028
|
{
|
|
1485
2029
|
action: z.enum(SESSION_ACTIONS).describe("Action to perform: 'list', 'get', or 'cancel'"),
|
|
1486
2030
|
sessionId: z.string().optional().describe("Session ID (required for 'get' and 'cancel')"),
|
|
1487
2031
|
includeSensitive: z.boolean().optional().describe(
|
|
1488
|
-
"When true, includes sensitive fields (cwd, systemPrompt, agents, additionalDirectories) in the response.
|
|
2032
|
+
"When true, includes sensitive fields (cwd, systemPrompt, agents, additionalDirectories) in the response. Default: false."
|
|
1489
2033
|
)
|
|
1490
2034
|
},
|
|
1491
2035
|
async (args) => {
|
|
1492
|
-
const allowSensitive = process.env.CLAUDE_CODE_MCP_ALLOW_SENSITIVE_SESSION_DETAILS === "1";
|
|
1493
|
-
if (args.includeSensitive && !allowSensitive) {
|
|
1494
|
-
const result2 = {
|
|
1495
|
-
sessions: [],
|
|
1496
|
-
message: `Error [${"PERMISSION_DENIED" /* PERMISSION_DENIED */}]: includeSensitive is disabled. Set CLAUDE_CODE_MCP_ALLOW_SENSITIVE_SESSION_DETAILS=1 to enable.`,
|
|
1497
|
-
isError: true
|
|
1498
|
-
};
|
|
1499
|
-
return {
|
|
1500
|
-
content: [
|
|
1501
|
-
{
|
|
1502
|
-
type: "text",
|
|
1503
|
-
text: JSON.stringify(result2, null, 2)
|
|
1504
|
-
}
|
|
1505
|
-
],
|
|
1506
|
-
isError: true
|
|
1507
|
-
};
|
|
1508
|
-
}
|
|
1509
2036
|
const result = executeClaudeCodeSession(args, sessionManager);
|
|
1510
2037
|
return {
|
|
1511
2038
|
content: [
|
|
@@ -1519,16 +2046,82 @@ Note: When CLAUDE_CODE_MCP_ALLOW_DISK_RESUME=1 is set and the in-memory session
|
|
|
1519
2046
|
}
|
|
1520
2047
|
);
|
|
1521
2048
|
server.tool(
|
|
1522
|
-
"
|
|
1523
|
-
`
|
|
1524
|
-
|
|
2049
|
+
"claude_code_check",
|
|
2050
|
+
`Query a running session for new events, retrieve the final result, or respond to permission requests.
|
|
2051
|
+
|
|
2052
|
+
Two actions:
|
|
2053
|
+
|
|
2054
|
+
Defaults (poll):
|
|
2055
|
+
- responseMode: "minimal"
|
|
2056
|
+
- minimal mode strips verbose fields from assistant messages (usage, model, id, cache_control) and filters out noisy progress events (tool_progress, auth_status)
|
|
2057
|
+
- maxEvents: 200 in minimal mode (unlimited in full mode unless maxEvents is set)
|
|
2058
|
+
|
|
2059
|
+
action="poll" \u2014 Retrieve events since the last poll.
|
|
2060
|
+
Returns events (agent output, progress updates, permission requests, errors, final result).
|
|
2061
|
+
Pass the cursor from the previous poll's nextCursor for incremental updates. Omit cursor to get all buffered events.
|
|
2062
|
+
If the agent is waiting for permission, the response includes an "actions" array with pending requests.
|
|
2063
|
+
|
|
2064
|
+
action="respond_permission" \u2014 Approve or deny a pending permission request.
|
|
2065
|
+
Pass the requestId from the actions array, plus decision="allow" or decision="deny".
|
|
2066
|
+
Approving resumes agent execution. Denying (with optional interrupt=true) can halt the entire session.
|
|
2067
|
+
The response also includes the latest poll state (events, status, etc.), so a separate poll call is not needed.`,
|
|
1525
2068
|
{
|
|
1526
|
-
action: z.enum(
|
|
1527
|
-
|
|
2069
|
+
action: z.enum(CHECK_ACTIONS).describe('Action to perform: "poll" or "respond_permission"'),
|
|
2070
|
+
sessionId: z.string().describe("Session ID to check"),
|
|
2071
|
+
cursor: z.number().int().nonnegative().optional().describe(
|
|
2072
|
+
"Event cursor for incremental polling. Pass nextCursor from the previous poll response."
|
|
2073
|
+
),
|
|
2074
|
+
responseMode: z.enum(CHECK_RESPONSE_MODES).optional().describe("Response shaping preset. 'minimal' reduces payload size. Default: 'minimal'."),
|
|
2075
|
+
maxEvents: z.number().int().positive().optional().describe(
|
|
2076
|
+
"Max number of events to return per poll (pagination via nextCursor). Default: 200 in minimal mode."
|
|
2077
|
+
),
|
|
2078
|
+
requestId: z.string().optional().describe(
|
|
2079
|
+
"The permission request ID to respond to (from the actions array). Required for respond_permission."
|
|
2080
|
+
),
|
|
2081
|
+
decision: z.enum(["allow", "deny"]).optional().describe(
|
|
2082
|
+
"Whether to approve or reject the permission request. Required for respond_permission."
|
|
2083
|
+
),
|
|
2084
|
+
denyMessage: z.string().optional().describe(
|
|
2085
|
+
"Reason for denying, shown to the agent. Only used with decision='deny'. Default: 'Permission denied by caller'."
|
|
2086
|
+
),
|
|
2087
|
+
interrupt: z.boolean().optional().describe(
|
|
2088
|
+
"When true with decision='deny', stops the entire agent session. Default: false."
|
|
2089
|
+
),
|
|
2090
|
+
pollOptions: z.object({
|
|
2091
|
+
includeTools: z.boolean().optional().describe("Include availableTools array from session init. Default: false."),
|
|
2092
|
+
includeEvents: z.boolean().optional().describe(
|
|
2093
|
+
"When false, omits the events array (nextCursor still advances). Default: true."
|
|
2094
|
+
),
|
|
2095
|
+
includeActions: z.boolean().optional().describe("When false, omits actions[] even if waiting_permission. Default: true."),
|
|
2096
|
+
includeResult: z.boolean().optional().describe("When false, omits the top-level result when idle/error. Default: true."),
|
|
2097
|
+
includeUsage: z.boolean().optional().describe("Include AgentResult.usage. Default: true in full mode, false in minimal."),
|
|
2098
|
+
includeModelUsage: z.boolean().optional().describe(
|
|
2099
|
+
"Include AgentResult.modelUsage. Default: true in full mode, false in minimal."
|
|
2100
|
+
),
|
|
2101
|
+
includeStructuredOutput: z.boolean().optional().describe(
|
|
2102
|
+
"Include AgentResult.structuredOutput. Default: true in full mode, false in minimal."
|
|
2103
|
+
),
|
|
2104
|
+
includeTerminalEvents: z.boolean().optional().describe(
|
|
2105
|
+
"Include terminal result/error events in events stream. Default: true in full, false in minimal."
|
|
2106
|
+
),
|
|
2107
|
+
includeProgressEvents: z.boolean().optional().describe(
|
|
2108
|
+
"Include progress events (tool_progress, auth_status). Default: true in full, false in minimal."
|
|
2109
|
+
)
|
|
2110
|
+
}).optional().describe(
|
|
2111
|
+
"Fine-grained poll control. Overrides responseMode defaults for individual fields. Most callers can omit this."
|
|
2112
|
+
),
|
|
2113
|
+
permissionOptions: z.object({
|
|
2114
|
+
updatedInput: z.record(z.string(), z.unknown()).optional().describe(
|
|
2115
|
+
"Modified tool input to use instead of the original. Only with decision='allow'."
|
|
2116
|
+
),
|
|
2117
|
+
updatedPermissions: z.array(z.record(z.string(), z.unknown())).optional().describe("Permission rule updates to apply. Only with decision='allow'.")
|
|
2118
|
+
}).optional().describe(
|
|
2119
|
+
"Advanced permission response options. Only used with respond_permission + decision='allow'."
|
|
1528
2120
|
)
|
|
1529
2121
|
},
|
|
1530
2122
|
async (args) => {
|
|
1531
|
-
const result =
|
|
2123
|
+
const result = executeClaudeCodeCheck(args, sessionManager, toolCache);
|
|
2124
|
+
const isError = result.isError === true;
|
|
1532
2125
|
return {
|
|
1533
2126
|
content: [
|
|
1534
2127
|
{
|
|
@@ -1536,7 +2129,7 @@ Actions: 'enable_bypass' allows sessions to use permissionMode='bypassPermission
|
|
|
1536
2129
|
text: JSON.stringify(result, null, 2)
|
|
1537
2130
|
}
|
|
1538
2131
|
],
|
|
1539
|
-
isError
|
|
2132
|
+
isError
|
|
1540
2133
|
};
|
|
1541
2134
|
}
|
|
1542
2135
|
);
|