agent-console-server 3.3.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.
@@ -0,0 +1,1377 @@
1
+ import { M as todoStatusSchema, P as permissionModeSchema, i as commandSchema, t as EphemeralAgentEventTypes } from "./EphemeralAgentEventTypes-Cm5oNQXu.js";
2
+ import { z } from "zod";
3
+ import { ID_SEPARATOR, InvalidOperationError, Operation, exhaustiveGuard, getOrCreate, getResult, getResultAsync, noop } from "@esposter/shared";
4
+ import { getSessionInfo, getSessionMessages, listSessions, query } from "@anthropic-ai/claude-agent-sdk";
5
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
6
+ import { readFile, readdir } from "node:fs/promises";
7
+ import { homedir } from "node:os";
8
+ import { join } from "node:path";
9
+ import { randomBytes, timingSafeEqual } from "node:crypto";
10
+ import { WebSocket, WebSocketServer } from "ws";
11
+ import { once } from "node:events";
12
+ import { createServer } from "node:http";
13
+ //#region src/services/drivers/claudeAgentSdk/getEventId.ts
14
+ const getEventId = (sourceId, suffix) => `${sourceId}${ID_SEPARATOR}${suffix}`;
15
+ //#endregion
16
+ //#region src/services/drivers/claudeAgentSdk/closeOpenSession.ts
17
+ const closeOpenSession = (sessionId, openSession, message, { emit, onSessionsChange, openSessionMap }) => {
18
+ if (openSessionMap.get(sessionId) !== openSession) return;
19
+ for (const pendingPermission of openSession.pendingPermissionMap.values()) pendingPermission.settle("Deny", "");
20
+ const createdAt = /* @__PURE__ */ new Date();
21
+ const closedId = getEventId(crypto.randomUUID(), "SessionState");
22
+ emit(sessionId, [...message ? [{
23
+ createdAt,
24
+ id: getEventId(closedId, "HostError"),
25
+ message,
26
+ type: "HostError"
27
+ }] : [], {
28
+ createdAt,
29
+ id: closedId,
30
+ state: "Closed",
31
+ type: "SessionState"
32
+ }]);
33
+ openSessionMap.delete(sessionId);
34
+ onSessionsChange();
35
+ };
36
+ //#endregion
37
+ //#region src/services/drivers/claudeAgentSdk/constants.ts
38
+ const IGNORED_SDK_MESSAGE_TYPES = ["command_lifecycle"];
39
+ const DEFAULT_DENY_MESSAGE = "The user denied this tool call.";
40
+ const SETTING_SOURCES = [
41
+ "user",
42
+ "project",
43
+ "local"
44
+ ];
45
+ const SESSION_LIST_LIMIT = 50;
46
+ //#endregion
47
+ //#region src/services/drivers/claudeAgentSdk/createInputQueue.ts
48
+ const createInputQueue = () => {
49
+ let items = [];
50
+ let pendingReads = [];
51
+ let isClosed = false;
52
+ const next = () => {
53
+ const [item, ...restItems] = items;
54
+ if (item !== void 0) {
55
+ items = restItems;
56
+ return Promise.resolve({
57
+ done: false,
58
+ value: item
59
+ });
60
+ } else if (isClosed) return Promise.resolve({
61
+ done: true,
62
+ value: void 0
63
+ });
64
+ else return new Promise((resolve) => {
65
+ pendingReads = [...pendingReads, resolve];
66
+ });
67
+ };
68
+ return {
69
+ close: () => {
70
+ isClosed = true;
71
+ const closedReads = pendingReads;
72
+ pendingReads = [];
73
+ for (const closedRead of closedReads) closedRead({
74
+ done: true,
75
+ value: void 0
76
+ });
77
+ },
78
+ iterable: { [Symbol.asyncIterator]: () => ({ next }) },
79
+ push: (item) => {
80
+ const [pendingRead, ...restPendingReads] = pendingReads;
81
+ if (pendingRead) {
82
+ pendingReads = restPendingReads;
83
+ pendingRead({
84
+ done: false,
85
+ value: item
86
+ });
87
+ } else items = [...items, item];
88
+ }
89
+ };
90
+ };
91
+ //#endregion
92
+ //#region src/services/drivers/claudeAgentSdk/toPermissionRequestEvent.ts
93
+ const toPermissionRequestEvent = (toolName, input, options, createdAt) => {
94
+ const requestId = options.requestId ?? crypto.randomUUID();
95
+ return {
96
+ blockedPath: options.blockedPath ?? "",
97
+ createdAt,
98
+ decisionReason: options.decisionReason ?? "",
99
+ hasSuggestions: Boolean(options.suggestions?.length),
100
+ id: requestId,
101
+ input,
102
+ requestId,
103
+ title: options.title ?? options.description ?? "",
104
+ toolName,
105
+ toolUseId: options.toolUseID ?? "",
106
+ type: "PermissionRequest"
107
+ };
108
+ };
109
+ //#endregion
110
+ //#region src/services/drivers/claudeAgentSdk/toPermissionResult.ts
111
+ const toPermissionResult = (input, suggestions, behavior, message) => {
112
+ switch (behavior) {
113
+ case "Allow": return {
114
+ behavior: "allow",
115
+ updatedInput: input
116
+ };
117
+ case "AllowAlways": return {
118
+ behavior: "allow",
119
+ updatedInput: input,
120
+ updatedPermissions: suggestions
121
+ };
122
+ case "Deny": return {
123
+ behavior: "deny",
124
+ message: message || "The user denied this tool call."
125
+ };
126
+ default: return exhaustiveGuard(behavior);
127
+ }
128
+ };
129
+ //#endregion
130
+ //#region src/services/drivers/claudeAgentSdk/createPermissionBridge.ts
131
+ const createPermissionBridge = (sessionId, pendingPermissionMap, emit) => (toolName, input, options) => new Promise((resolve) => {
132
+ const requestEvent = toPermissionRequestEvent(toolName, input, options, /* @__PURE__ */ new Date());
133
+ const { requestId } = requestEvent;
134
+ const resolutionId = getEventId(requestId, "PermissionResolution");
135
+ pendingPermissionMap.set(requestId, { settle: (behavior, message) => {
136
+ if (!pendingPermissionMap.delete(requestId)) return;
137
+ resolve(toPermissionResult(input, options.suggestions ?? [], behavior, message));
138
+ const createdAt = /* @__PURE__ */ new Date();
139
+ emit(sessionId, [{
140
+ behavior,
141
+ createdAt,
142
+ id: resolutionId,
143
+ requestId,
144
+ type: "PermissionResolution"
145
+ }, {
146
+ createdAt,
147
+ id: getEventId(resolutionId, "SessionState"),
148
+ state: "Running",
149
+ type: "SessionState"
150
+ }]);
151
+ } });
152
+ options.signal.addEventListener("abort", () => {
153
+ pendingPermissionMap.get(requestId)?.settle("Deny", "");
154
+ }, { once: true });
155
+ emit(sessionId, [requestEvent, {
156
+ createdAt: requestEvent.createdAt,
157
+ id: getEventId(requestId, "SessionState"),
158
+ state: "RequiresAction",
159
+ type: "SessionState"
160
+ }]);
161
+ });
162
+ //#endregion
163
+ //#region src/services/drivers/claudeAgentSdk/createFileOriginTracker.ts
164
+ const fileEditResultSchema = z.object({
165
+ filePath: z.string().min(1),
166
+ originalFile: z.string().nullable(),
167
+ type: z.enum(["create", "update"]).optional()
168
+ });
169
+ const createFileOriginTracker = () => {
170
+ const changedFilePaths = /* @__PURE__ */ new Set();
171
+ return { readToolResult: (event, toolUseResult) => {
172
+ const fileEditResult = fileEditResultSchema.safeParse(toolUseResult);
173
+ if (event.isError || !fileEditResult.success) return event;
174
+ const { filePath, originalFile, type } = fileEditResult.data;
175
+ if (changedFilePaths.has(filePath)) return event;
176
+ changedFilePaths.add(filePath);
177
+ const originalText = originalFile ?? (type === "create" ? "" : void 0);
178
+ return originalText === void 0 ? event : {
179
+ ...event,
180
+ originalText
181
+ };
182
+ } };
183
+ };
184
+ //#endregion
185
+ //#region src/services/drivers/claudeAgentSdk/createStreamTracker.ts
186
+ const toTurnUsageEvent = (id, createdAt, outputTokens) => ({
187
+ createdAt,
188
+ id,
189
+ outputTokens,
190
+ type: "TurnUsage"
191
+ });
192
+ const createStreamTracker = () => {
193
+ let messageId = "";
194
+ let finishedOutputTokens = 0;
195
+ return {
196
+ readStreamEvent: ({ event, parent_tool_use_id, uuid }, createdAt) => {
197
+ if (parent_tool_use_id) return [];
198
+ switch (event.type) {
199
+ case "content_block_delta":
200
+ if (event.delta.type !== "text_delta" && event.delta.type !== "thinking_delta") return [];
201
+ return [{
202
+ blockId: getEventId(messageId, event.index),
203
+ createdAt,
204
+ id: uuid,
205
+ isThinking: event.delta.type === "thinking_delta",
206
+ text: event.delta.type === "text_delta" ? event.delta.text : event.delta.thinking,
207
+ type: "StreamDelta"
208
+ }];
209
+ case "message_delta":
210
+ finishedOutputTokens += event.usage.output_tokens;
211
+ return [toTurnUsageEvent(uuid, createdAt, finishedOutputTokens)];
212
+ case "message_start":
213
+ ({id: messageId} = event.message);
214
+ return [];
215
+ default: return [];
216
+ }
217
+ },
218
+ readThinkingTokens: ({ estimated_tokens, uuid }, createdAt) => [toTurnUsageEvent(uuid, createdAt, finishedOutputTokens + estimated_tokens)],
219
+ reset: () => {
220
+ finishedOutputTokens = 0;
221
+ }
222
+ };
223
+ };
224
+ //#endregion
225
+ //#region src/models/claudeAgentSdk/TodoToolName.ts
226
+ let TodoToolName = /* @__PURE__ */ function(TodoToolName) {
227
+ TodoToolName["TaskCreate"] = "TaskCreate";
228
+ TodoToolName["TaskUpdate"] = "TaskUpdate";
229
+ TodoToolName["TodoWrite"] = "TodoWrite";
230
+ return TodoToolName;
231
+ }({});
232
+ //#endregion
233
+ //#region src/services/drivers/claudeAgentSdk/createTodoTracker.ts
234
+ const todoWriteInputSchema = z.object({ todos: z.object({
235
+ activeForm: z.string(),
236
+ content: z.string(),
237
+ status: todoStatusSchema
238
+ }).array() });
239
+ const taskCreateInputSchema = z.object({
240
+ activeForm: z.string().optional(),
241
+ subject: z.string()
242
+ });
243
+ const taskCreateResultSchema = z.object({ task: z.object({ id: z.string().min(1) }) });
244
+ const taskUpdateInputSchema = z.object({
245
+ activeForm: z.string().optional(),
246
+ status: z.union([todoStatusSchema, z.literal("deleted")]).optional(),
247
+ subject: z.string().optional(),
248
+ taskId: z.string().min(1)
249
+ });
250
+ const createTodoTracker = () => {
251
+ let todos = [];
252
+ const pendingTodoMap = /* @__PURE__ */ new Map();
253
+ const toTodoUpdateEvent = (sourceId, createdAt) => ({
254
+ createdAt,
255
+ id: getEventId(sourceId, "TodoUpdate"),
256
+ todos,
257
+ type: "TodoUpdate"
258
+ });
259
+ return {
260
+ readToolResult: (event, toolUseResult) => {
261
+ const pendingTodo = pendingTodoMap.get(event.toolUseId);
262
+ if (!pendingTodo) return void 0;
263
+ pendingTodoMap.delete(event.toolUseId);
264
+ const taskCreateResult = taskCreateResultSchema.safeParse(toolUseResult);
265
+ if (!taskCreateResult.success || event.isError) return void 0;
266
+ todos = [...todos, {
267
+ ...pendingTodo,
268
+ id: taskCreateResult.data.task.id
269
+ }];
270
+ return toTodoUpdateEvent(event.id, event.createdAt);
271
+ },
272
+ readToolUse: (event) => {
273
+ if (event.parentToolUseId) return void 0;
274
+ switch (event.name) {
275
+ case "TaskCreate": {
276
+ const input = taskCreateInputSchema.safeParse(event.input);
277
+ if (!input.success) return void 0;
278
+ const { activeForm, subject } = input.data;
279
+ pendingTodoMap.set(event.toolUseId, {
280
+ activeForm: activeForm ?? subject,
281
+ content: subject,
282
+ status: "pending"
283
+ });
284
+ return;
285
+ }
286
+ case "TaskUpdate": {
287
+ const input = taskUpdateInputSchema.safeParse(event.input);
288
+ if (!input.success) return void 0;
289
+ const { activeForm, status, subject, taskId } = input.data;
290
+ todos = status === "deleted" ? todos.filter(({ id }) => id !== taskId) : todos.map((todo) => todo.id === taskId ? {
291
+ activeForm: activeForm ?? todo.activeForm,
292
+ content: subject ?? todo.content,
293
+ id: todo.id,
294
+ status: status ?? todo.status
295
+ } : todo);
296
+ return toTodoUpdateEvent(event.id, event.createdAt);
297
+ }
298
+ case "TodoWrite": {
299
+ const input = todoWriteInputSchema.safeParse(event.input);
300
+ if (!input.success) return void 0;
301
+ todos = input.data.todos.map(({ activeForm, content, status }, index) => ({
302
+ activeForm,
303
+ content,
304
+ id: String(index),
305
+ status
306
+ }));
307
+ return toTodoUpdateEvent(event.id, event.createdAt);
308
+ }
309
+ default: return;
310
+ }
311
+ }
312
+ };
313
+ };
314
+ //#endregion
315
+ //#region src/services/drivers/claudeAgentSdk/toUnknownEvent.ts
316
+ const toUnknownEvent = (id, sdkType, raw, createdAt) => ({
317
+ createdAt,
318
+ id,
319
+ raw,
320
+ sdkType,
321
+ type: "Unknown"
322
+ });
323
+ //#endregion
324
+ //#region src/services/drivers/claudeAgentSdk/mapAssistantContent.ts
325
+ const mapAssistantContent = (content, context) => content.map((block, index) => {
326
+ const id = getEventId(context.messageUuid, index);
327
+ switch (block.type) {
328
+ case "document":
329
+ case "image":
330
+ case "tool_result": return toUnknownEvent(id, block.type, JSON.stringify(block), context.createdAt);
331
+ case "other": return toUnknownEvent(id, block.blockType, block.raw, context.createdAt);
332
+ case "text": return {
333
+ ...context,
334
+ id,
335
+ text: block.text,
336
+ type: "AssistantMessage"
337
+ };
338
+ case "thinking": return {
339
+ ...context,
340
+ id,
341
+ thinking: block.thinking,
342
+ type: "Thinking"
343
+ };
344
+ case "tool_use": return {
345
+ ...context,
346
+ id,
347
+ input: block.input,
348
+ name: block.name,
349
+ toolUseId: block.id,
350
+ type: "ToolUse"
351
+ };
352
+ default: return exhaustiveGuard(block);
353
+ }
354
+ });
355
+ //#endregion
356
+ //#region src/services/drivers/claudeAgentSdk/mapHookMessage.ts
357
+ const HookPhaseMap = {
358
+ hook_progress: "Progress",
359
+ hook_response: "Response",
360
+ hook_started: "Started"
361
+ };
362
+ const mapHookMessage = (message, createdAt) => ({
363
+ createdAt,
364
+ exitCode: "exit_code" in message ? message.exit_code ?? 0 : 0,
365
+ hookEvent: message.hook_event,
366
+ hookId: message.hook_id,
367
+ hookName: message.hook_name,
368
+ id: message.uuid,
369
+ outcome: "outcome" in message ? message.outcome : "",
370
+ output: "output" in message ? message.output : "",
371
+ phase: HookPhaseMap[message.subtype],
372
+ stderr: "stderr" in message ? message.stderr : "",
373
+ stdout: "stdout" in message ? message.stdout : "",
374
+ type: "Hook"
375
+ });
376
+ //#endregion
377
+ //#region src/services/drivers/claudeAgentSdk/mapTaskMessage.ts
378
+ const SubagentStatusMap = {
379
+ completed: "Completed",
380
+ failed: "Failed",
381
+ killed: "Stopped",
382
+ paused: "Running",
383
+ pending: "Started",
384
+ running: "Running",
385
+ stopped: "Stopped"
386
+ };
387
+ const mapTaskMessage = (message, createdAt) => {
388
+ const base = {
389
+ createdAt,
390
+ description: "",
391
+ durationMs: 0,
392
+ id: message.uuid,
393
+ lastToolName: "",
394
+ subagentType: "",
395
+ summary: "",
396
+ taskId: message.task_id,
397
+ toolUseId: "tool_use_id" in message ? message.tool_use_id ?? "" : "",
398
+ toolUses: 0,
399
+ totalTokens: 0,
400
+ type: "Subagent"
401
+ };
402
+ switch (message.subtype) {
403
+ case "task_notification": return {
404
+ ...base,
405
+ durationMs: message.usage?.duration_ms ?? 0,
406
+ status: SubagentStatusMap[message.status],
407
+ summary: message.summary,
408
+ toolUses: message.usage?.tool_uses ?? 0,
409
+ totalTokens: message.usage?.total_tokens ?? 0
410
+ };
411
+ case "task_progress": return {
412
+ ...base,
413
+ description: message.description,
414
+ durationMs: message.usage.duration_ms,
415
+ lastToolName: message.last_tool_name ?? "",
416
+ status: "Running",
417
+ subagentType: message.subagent_type ?? "",
418
+ summary: message.summary ?? "",
419
+ toolUses: message.usage.tool_uses,
420
+ totalTokens: message.usage.total_tokens
421
+ };
422
+ case "task_started": return {
423
+ ...base,
424
+ description: message.description,
425
+ status: "Started",
426
+ subagentType: message.subagent_type ?? ""
427
+ };
428
+ case "task_updated": return {
429
+ ...base,
430
+ description: message.patch.description ?? "",
431
+ status: message.patch.status ? SubagentStatusMap[message.patch.status] : "Running",
432
+ summary: message.patch.error ?? ""
433
+ };
434
+ default: return exhaustiveGuard(message);
435
+ }
436
+ };
437
+ //#endregion
438
+ //#region src/services/drivers/claudeAgentSdk/mapSystemMessage.ts
439
+ const SessionStateMap = {
440
+ idle: "Idle",
441
+ requires_action: "RequiresAction",
442
+ running: "Running"
443
+ };
444
+ const mapSystemMessage = (message, createdAt) => {
445
+ switch (message.subtype) {
446
+ case "compact_boundary": return [{
447
+ createdAt,
448
+ id: message.uuid,
449
+ postTokens: message.compact_metadata.post_tokens ?? 0,
450
+ preTokens: message.compact_metadata.pre_tokens,
451
+ trigger: message.compact_metadata.trigger === "auto" ? "auto" : "manual",
452
+ type: "Compaction"
453
+ }];
454
+ case "hook_progress":
455
+ case "hook_response":
456
+ case "hook_started": return [mapHookMessage(message, createdAt)];
457
+ case "local_command_output": return [{
458
+ content: message.content,
459
+ createdAt,
460
+ id: message.uuid,
461
+ type: "CommandOutput"
462
+ }];
463
+ case "session_state_changed": return [{
464
+ createdAt,
465
+ id: message.uuid,
466
+ state: SessionStateMap[message.state],
467
+ type: "SessionState"
468
+ }];
469
+ case "task_notification":
470
+ case "task_progress":
471
+ case "task_started":
472
+ case "task_updated": return [mapTaskMessage(message, createdAt)];
473
+ default: return [toUnknownEvent(message.uuid, `system:${message.subtype}`, JSON.stringify(message), createdAt)];
474
+ }
475
+ };
476
+ //#endregion
477
+ //#region src/services/drivers/claudeAgentSdk/toToolResultText.ts
478
+ const toToolResultText = (content) => typeof content === "string" ? content : content.map((part) => part.text === void 0 ? `[${part.type}]` : part.text).join("\n");
479
+ //#endregion
480
+ //#region src/services/drivers/claudeAgentSdk/mapUserContent.ts
481
+ const mapUserContent = (content, context) => {
482
+ if (typeof content === "string") return [{
483
+ ...context,
484
+ attachmentCount: 0,
485
+ id: context.messageUuid,
486
+ text: content,
487
+ type: "UserMessage"
488
+ }];
489
+ const events = [];
490
+ const texts = [];
491
+ let attachmentCount = 0;
492
+ for (const [index, block] of content.entries()) {
493
+ const id = getEventId(context.messageUuid, index);
494
+ switch (block.type) {
495
+ case "document":
496
+ case "image":
497
+ attachmentCount++;
498
+ break;
499
+ case "other":
500
+ events.push(toUnknownEvent(id, block.blockType, block.raw, context.createdAt));
501
+ break;
502
+ case "text":
503
+ texts.push(block.text);
504
+ break;
505
+ case "thinking":
506
+ case "tool_use":
507
+ events.push(toUnknownEvent(id, block.type, JSON.stringify(block), context.createdAt));
508
+ break;
509
+ case "tool_result":
510
+ events.push({
511
+ content: toToolResultText(block.content),
512
+ createdAt: context.createdAt,
513
+ id,
514
+ isError: Boolean(block.is_error),
515
+ parentToolUseId: context.parentToolUseId,
516
+ toolUseId: block.tool_use_id,
517
+ type: "ToolResult"
518
+ });
519
+ break;
520
+ default: exhaustiveGuard(block);
521
+ }
522
+ }
523
+ return texts.length > 0 || attachmentCount > 0 ? [{
524
+ ...context,
525
+ attachmentCount,
526
+ id: context.messageUuid,
527
+ text: texts.join("\n\n"),
528
+ type: "UserMessage"
529
+ }, ...events] : events;
530
+ };
531
+ //#endregion
532
+ //#region src/services/drivers/claudeAgentSdk/parseMessageBody.ts
533
+ const documentBlockSchema = z.object({ type: z.literal("document") });
534
+ const imageBlockSchema = z.object({ type: z.literal("image") });
535
+ const textBlockSchema = z.object({
536
+ text: z.string(),
537
+ type: z.literal("text")
538
+ });
539
+ const thinkingBlockSchema = z.object({
540
+ thinking: z.string(),
541
+ type: z.literal("thinking")
542
+ });
543
+ const toolResultBlockSchema = z.object({
544
+ content: z.union([z.string(), z.looseObject({
545
+ text: z.string().optional(),
546
+ type: z.string()
547
+ }).array()]),
548
+ is_error: z.boolean().optional(),
549
+ tool_use_id: z.string().min(1),
550
+ type: z.literal("tool_result")
551
+ });
552
+ const toolUseBlockSchema = z.object({
553
+ id: z.string().min(1),
554
+ input: z.record(z.string(), z.unknown()),
555
+ name: z.string().min(1),
556
+ type: z.literal("tool_use")
557
+ });
558
+ const otherBlockSchema = z.looseObject({ type: z.string() }).transform((block) => ({
559
+ blockType: block.type,
560
+ raw: JSON.stringify(block),
561
+ type: "other"
562
+ }));
563
+ const contentBlockSchema = z.union([
564
+ documentBlockSchema,
565
+ imageBlockSchema,
566
+ textBlockSchema,
567
+ thinkingBlockSchema,
568
+ toolResultBlockSchema,
569
+ toolUseBlockSchema,
570
+ otherBlockSchema
571
+ ]);
572
+ const messageBodySchema = z.looseObject({ content: z.union([contentBlockSchema.array(), z.string()]) });
573
+ const parseMessageBody = (value) => {
574
+ const result = messageBodySchema.safeParse(value);
575
+ return result.success ? result.data : void 0;
576
+ };
577
+ //#endregion
578
+ //#region src/services/drivers/claudeAgentSdk/toSessionInitEvent.ts
579
+ const toSessionInitEvent = (message, permissionMode, createdAt) => ({
580
+ agents: [...new Set(message.agents)],
581
+ claudeCodeVersion: message.claude_code_version,
582
+ createdAt,
583
+ cwd: message.cwd,
584
+ id: message.uuid,
585
+ mcpServers: [...new Map(message.mcp_servers.map(({ name, status }) => [name, {
586
+ name,
587
+ status
588
+ }])).values()],
589
+ model: message.model,
590
+ outputStyle: message.output_style,
591
+ permissionMode,
592
+ plugins: [...new Set(message.plugins.map(({ name }) => name))],
593
+ skills: [...new Set(message.skills)],
594
+ slashCommands: [...new Set(message.slash_commands)],
595
+ tools: [...new Set(message.tools)],
596
+ type: "SessionInit"
597
+ });
598
+ //#endregion
599
+ //#region src/services/drivers/claudeAgentSdk/toTurnResultEvents.ts
600
+ const toTurnResultEvents = (message, createdAt) => [{
601
+ createdAt,
602
+ durationApiMs: message.duration_api_ms,
603
+ durationMs: message.duration_ms,
604
+ errors: message.subtype === "success" ? [] : message.errors,
605
+ id: message.uuid,
606
+ isError: message.is_error,
607
+ numTurns: message.num_turns,
608
+ result: message.subtype === "success" ? message.result : "",
609
+ subtype: message.subtype,
610
+ totalCostUsd: message.total_cost_usd,
611
+ type: "TurnResult",
612
+ usage: {
613
+ cacheCreationInputTokens: message.usage.cache_creation_input_tokens,
614
+ cacheReadInputTokens: message.usage.cache_read_input_tokens,
615
+ inputTokens: message.usage.input_tokens,
616
+ outputTokens: message.usage.output_tokens
617
+ }
618
+ }, {
619
+ createdAt,
620
+ id: getEventId(message.uuid, "SessionState"),
621
+ state: "Idle",
622
+ type: "SessionState"
623
+ }];
624
+ //#endregion
625
+ //#region src/services/drivers/claudeAgentSdk/createSdkMessageMapper.ts
626
+ const createSdkMessageMapper = () => {
627
+ const fileOriginTracker = createFileOriginTracker();
628
+ const streamTracker = createStreamTracker();
629
+ const todoTracker = createTodoTracker();
630
+ const settings = {
631
+ model: "",
632
+ permissionMode: "default"
633
+ };
634
+ const updateSettings = (id, createdAt, { model, permissionMode }) => {
635
+ if (model) settings.model = model;
636
+ const parsedPermissionMode = permissionModeSchema.safeParse(permissionMode);
637
+ if (parsedPermissionMode.success) settings.permissionMode = parsedPermissionMode.data;
638
+ return {
639
+ createdAt,
640
+ id,
641
+ ...settings,
642
+ type: "SessionSettings"
643
+ };
644
+ };
645
+ const mapContent = ({ body, error, messageType, messageUuid, parentToolUseId, toolUseResult }, createdAt) => {
646
+ const messageBody = parseMessageBody(body);
647
+ if (!messageBody) return [toUnknownEvent(messageUuid, messageType, JSON.stringify(body), createdAt)];
648
+ const context = {
649
+ createdAt,
650
+ messageUuid,
651
+ parentToolUseId
652
+ };
653
+ const { content } = messageBody;
654
+ const contentEvents = messageType === "assistant" ? mapAssistantContent(typeof content === "string" ? [{
655
+ text: content,
656
+ type: "text"
657
+ }] : content, context) : mapUserContent(content, context);
658
+ const events = [];
659
+ for (const event of contentEvents) if (event.type === "ToolUse") {
660
+ events.push(event);
661
+ const todoUpdateEvent = todoTracker.readToolUse(event);
662
+ if (todoUpdateEvent) events.push(todoUpdateEvent);
663
+ } else if (event.type === "ToolResult") {
664
+ events.push(fileOriginTracker.readToolResult(event, toolUseResult));
665
+ const todoUpdateEvent = todoTracker.readToolResult(event, toolUseResult);
666
+ if (todoUpdateEvent) events.push(todoUpdateEvent);
667
+ } else events.push(event);
668
+ if (error) events.push({
669
+ createdAt,
670
+ id: getEventId(messageUuid, "HostError"),
671
+ message: error,
672
+ type: "HostError"
673
+ });
674
+ return events;
675
+ };
676
+ const mapMessage = (message, createdAt) => {
677
+ const sdkType = message.type;
678
+ if (IGNORED_SDK_MESSAGE_TYPES.includes(sdkType)) return [];
679
+ switch (message.type) {
680
+ case "assistant": return mapContent({
681
+ body: message.message,
682
+ error: message.error ?? "",
683
+ messageType: message.type,
684
+ messageUuid: message.uuid,
685
+ parentToolUseId: message.parent_tool_use_id ?? "",
686
+ toolUseResult: void 0
687
+ }, createdAt);
688
+ case "rate_limit_event": {
689
+ const { rateLimitType, resetsAt, status, utilization } = message.rate_limit_info;
690
+ return [{
691
+ createdAt,
692
+ id: message.uuid,
693
+ rateLimitType: rateLimitType ?? "",
694
+ resetsAt: resetsAt === void 0 ? void 0 : new Date(Temporal.Duration.from({ seconds: resetsAt }).total("milliseconds")),
695
+ status,
696
+ type: "RateLimit",
697
+ utilization
698
+ }];
699
+ }
700
+ case "result":
701
+ streamTracker.reset();
702
+ return toTurnResultEvents(message, createdAt);
703
+ case "stream_event": return streamTracker.readStreamEvent(message, createdAt);
704
+ case "tool_progress": return [{
705
+ createdAt,
706
+ elapsedSeconds: message.elapsed_time_seconds,
707
+ id: message.uuid,
708
+ parentToolUseId: message.parent_tool_use_id ?? "",
709
+ toolUseId: message.tool_use_id,
710
+ type: "ToolProgress"
711
+ }];
712
+ case "user": return mapContent({
713
+ body: message.message,
714
+ error: "",
715
+ messageType: message.type,
716
+ messageUuid: message.uuid ?? crypto.randomUUID(),
717
+ parentToolUseId: message.parent_tool_use_id ?? "",
718
+ toolUseResult: message.tool_use_result
719
+ }, createdAt);
720
+ case "system": if (message.subtype === "init") {
721
+ const settingsEvent = updateSettings(getEventId(message.uuid, "SessionSettings"), createdAt, {
722
+ model: message.model,
723
+ permissionMode: message.permissionMode
724
+ });
725
+ return [toSessionInitEvent(message, settingsEvent.permissionMode, createdAt), settingsEvent];
726
+ } else if (message.subtype === "status") {
727
+ const stateEvent = {
728
+ createdAt,
729
+ id: message.uuid,
730
+ state: message.status === "compacting" ? "Compacting" : "Running",
731
+ type: "SessionState"
732
+ };
733
+ return message.permissionMode ? [stateEvent, updateSettings(getEventId(message.uuid, "SessionSettings"), createdAt, { permissionMode: message.permissionMode })] : [stateEvent];
734
+ } else if (message.subtype === "thinking_tokens") return streamTracker.readThinkingTokens(message, createdAt);
735
+ else return mapSystemMessage(message, createdAt);
736
+ default: return [toUnknownEvent(message.uuid, message.type, JSON.stringify(message), createdAt)];
737
+ }
738
+ };
739
+ return {
740
+ mapHistory: ({ message, parent_tool_use_id, type, uuid }, createdAt, toolUseResult) => type === "system" ? [toUnknownEvent(uuid, type, JSON.stringify(message), createdAt)] : mapContent({
741
+ body: message,
742
+ error: "",
743
+ messageType: type,
744
+ messageUuid: uuid,
745
+ parentToolUseId: parent_tool_use_id ?? "",
746
+ toolUseResult
747
+ }, createdAt),
748
+ mapMessage,
749
+ updateSettings
750
+ };
751
+ };
752
+ //#endregion
753
+ //#region src/services/drivers/claudeAgentSdk/readSessionHistory.ts
754
+ const readSessionHistory = async (sessionId, cwd, resumeAt) => {
755
+ const sessionMessages = await getSessionMessages(sessionId, { dir: cwd });
756
+ if (!resumeAt) return sessionMessages;
757
+ const resumeAtIndex = sessionMessages.findIndex(({ uuid }) => uuid === resumeAt);
758
+ if (resumeAtIndex === -1) throw new InvalidOperationError(Operation.Read, sessionId, `the transcript holds no message ${resumeAt}`);
759
+ return sessionMessages.slice(0, resumeAtIndex + 1);
760
+ };
761
+ //#endregion
762
+ //#region src/services/drivers/claudeAgentSdk/readToolUseResultMap.ts
763
+ const transcriptEntrySchema = z.object({
764
+ toolUseResult: z.unknown(),
765
+ uuid: z.string()
766
+ });
767
+ const readToolUseResultMap = async (sessionId) => {
768
+ const toolUseResultMap = /* @__PURE__ */ new Map();
769
+ const projectsDirectory = join(process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), ".claude"), "projects");
770
+ if (!existsSync(projectsDirectory)) return toolUseResultMap;
771
+ const transcriptPath = (await readdir(projectsDirectory)).map((projectDirectory) => join(projectsDirectory, projectDirectory, `${sessionId}.jsonl`)).find((path) => existsSync(path));
772
+ if (!transcriptPath) return toolUseResultMap;
773
+ for (const line of (await readFile(transcriptPath, "utf8")).split("\n")) getResult(() => transcriptEntrySchema.parse(JSON.parse(line))).match(({ toolUseResult, uuid }) => {
774
+ if (toolUseResult !== void 0) toolUseResultMap.set(uuid, toolUseResult);
775
+ }, noop);
776
+ return toolUseResultMap;
777
+ };
778
+ //#endregion
779
+ //#region src/services/drivers/claudeAgentSdk/getSessionTitle.ts
780
+ const getSessionTitle = ({ customTitle, firstPrompt, summary }) => customTitle || summary || firstPrompt || "";
781
+ //#endregion
782
+ //#region src/services/drivers/claudeAgentSdk/toCapabilitiesEvent.ts
783
+ const toCapabilitiesEvent = (id, slashCommands, models, createdAt) => ({
784
+ commands: [...new Map(slashCommands.map(({ argumentHint, description, name }) => [name, {
785
+ argumentHint,
786
+ description,
787
+ name
788
+ }])).values()],
789
+ createdAt,
790
+ id,
791
+ models: [...new Map(models.map(({ description, displayName, value }) => [value, {
792
+ description,
793
+ displayName,
794
+ value
795
+ }])).values()],
796
+ type: "Capabilities"
797
+ });
798
+ //#endregion
799
+ //#region src/services/drivers/claudeAgentSdk/toContextUsageEvent.ts
800
+ const toContextUsageEvent = (id, { autoCompactThreshold, isAutoCompactEnabled, maxTokens, percentage, totalTokens }, createdAt) => ({
801
+ autoCompactThreshold: autoCompactThreshold ?? 0,
802
+ createdAt,
803
+ id,
804
+ isAutoCompactEnabled,
805
+ maxTokens,
806
+ percentage,
807
+ totalTokens,
808
+ type: "ContextUsage"
809
+ });
810
+ //#endregion
811
+ //#region src/services/drivers/claudeAgentSdk/watchSession.ts
812
+ const watchSession = async (sessionId, openSession, { emit, onSessionsChange, openSessionMap }) => {
813
+ const { cwd, mapper, query } = openSession;
814
+ const readContextUsage = () => getResultAsync(() => query.getContextUsage()).match((contextUsage) => {
815
+ emit(sessionId, [toContextUsageEvent(crypto.randomUUID(), contextUsage, /* @__PURE__ */ new Date())]);
816
+ }, console.error);
817
+ const readTitle = () => getResultAsync(() => getSessionInfo(sessionId, { dir: cwd })).match((sessionInfo) => {
818
+ if (!sessionInfo) return;
819
+ openSession.title = getSessionTitle(sessionInfo) || openSession.title;
820
+ onSessionsChange();
821
+ }, console.error);
822
+ await Promise.all([
823
+ getResultAsync(() => Promise.all([query.supportedCommands(), query.supportedModels()])).match(([slashCommands, models]) => {
824
+ emit(sessionId, [toCapabilitiesEvent(crypto.randomUUID(), slashCommands, models, /* @__PURE__ */ new Date())]);
825
+ }, console.error),
826
+ readContextUsage(),
827
+ getResultAsync(async () => {
828
+ for await (const message of query) {
829
+ emit(sessionId, mapper.mapMessage(message, /* @__PURE__ */ new Date()));
830
+ if (message.type === "result") await Promise.all([readContextUsage(), readTitle()]);
831
+ }
832
+ }).match(() => {
833
+ closeOpenSession(sessionId, openSession, "", {
834
+ emit,
835
+ onSessionsChange,
836
+ openSessionMap
837
+ });
838
+ }, (error) => {
839
+ closeOpenSession(sessionId, openSession, error.message, {
840
+ emit,
841
+ onSessionsChange,
842
+ openSessionMap
843
+ });
844
+ })
845
+ ]);
846
+ };
847
+ //#endregion
848
+ //#region src/services/drivers/claudeAgentSdk/createSessionOpener.ts
849
+ const createSessionOpener = ({ emit, onSessionOpen, onSessionsChange, openSessionMap, taskRegistry }) => async ({ cwd, isFork, resumeAt, resumeFrom, sessionId }) => {
850
+ const mapper = createSdkMessageMapper();
851
+ const history = resumeFrom ? await readSessionHistory(resumeFrom, cwd, resumeAt) : [];
852
+ const toolUseResultMap = resumeFrom ? await readToolUseResultMap(resumeFrom) : /* @__PURE__ */ new Map();
853
+ const input = createInputQueue();
854
+ const pendingPermissionMap = /* @__PURE__ */ new Map();
855
+ const sessionQuery = query({
856
+ options: {
857
+ canUseTool: createPermissionBridge(sessionId, pendingPermissionMap, emit),
858
+ cwd,
859
+ enableFileCheckpointing: true,
860
+ env: {
861
+ ...process.env,
862
+ CLAUDE_CODE_ENABLE_TODO_TOOLS: "true"
863
+ },
864
+ includeHookEvents: true,
865
+ includePartialMessages: true,
866
+ settingSources: SETTING_SOURCES,
867
+ systemPrompt: {
868
+ preset: "claude_code",
869
+ type: "preset"
870
+ },
871
+ thinking: {
872
+ display: "summarized",
873
+ type: "adaptive"
874
+ },
875
+ ...resumeFrom ? { resume: resumeFrom } : {},
876
+ ...resumeAt ? { resumeSessionAt: resumeAt } : {},
877
+ ...!resumeFrom || isFork ? {
878
+ forkSession: isFork,
879
+ sessionId
880
+ } : {}
881
+ },
882
+ prompt: input.iterable
883
+ });
884
+ const openSession = {
885
+ cwd,
886
+ input,
887
+ lastActivityAt: /* @__PURE__ */ new Date(),
888
+ mapper,
889
+ pendingPermissionMap,
890
+ query: sessionQuery,
891
+ state: "Idle",
892
+ title: ""
893
+ };
894
+ onSessionOpen(sessionId);
895
+ openSessionMap.set(sessionId, openSession);
896
+ const createdAt = /* @__PURE__ */ new Date();
897
+ emit(sessionId, [...history.flatMap((sessionMessage) => mapper.mapHistory(sessionMessage, createdAt, toolUseResultMap.get(sessionMessage.uuid))), {
898
+ createdAt,
899
+ id: getEventId(crypto.randomUUID(), "SessionState"),
900
+ state: "Idle",
901
+ type: "SessionState"
902
+ }]);
903
+ onSessionsChange();
904
+ taskRegistry.run(() => watchSession(sessionId, openSession, {
905
+ emit,
906
+ onSessionsChange,
907
+ openSessionMap
908
+ }));
909
+ return sessionId;
910
+ };
911
+ //#endregion
912
+ //#region src/services/drivers/claudeAgentSdk/readSessionCwd.ts
913
+ const readSessionCwd = async (sessionId) => {
914
+ const sessionInfo = await getSessionInfo(sessionId);
915
+ if (!sessionInfo?.cwd) throw new InvalidOperationError(Operation.Read, sessionId, "no session with this id on disk");
916
+ return sessionInfo.cwd;
917
+ };
918
+ //#endregion
919
+ //#region src/services/drivers/claudeAgentSdk/toContentBlockParam.ts
920
+ const toContentBlockParam = ({ data, mediaType, name }) => {
921
+ switch (mediaType) {
922
+ case "image/gif":
923
+ case "image/jpeg":
924
+ case "image/png":
925
+ case "image/webp": return {
926
+ source: {
927
+ data,
928
+ media_type: mediaType,
929
+ type: "base64"
930
+ },
931
+ type: "image"
932
+ };
933
+ case "application/pdf": return {
934
+ source: {
935
+ data,
936
+ media_type: mediaType,
937
+ type: "base64"
938
+ },
939
+ title: name,
940
+ type: "document"
941
+ };
942
+ case "text/plain": return {
943
+ source: {
944
+ data,
945
+ media_type: mediaType,
946
+ type: "text"
947
+ },
948
+ title: name,
949
+ type: "document"
950
+ };
951
+ default: return exhaustiveGuard(mediaType);
952
+ }
953
+ };
954
+ //#endregion
955
+ //#region src/services/shared/createTaskRegistry.ts
956
+ const createTaskRegistry = () => {
957
+ const pendingSet = /* @__PURE__ */ new Set();
958
+ return {
959
+ drain: async () => {
960
+ await Promise.all(pendingSet);
961
+ },
962
+ run: (task) => {
963
+ const pending = task().finally(() => {
964
+ pendingSet.delete(pending);
965
+ });
966
+ pendingSet.add(pending);
967
+ }
968
+ };
969
+ };
970
+ //#endregion
971
+ //#region src/services/drivers/claudeAgentSdk/createClaudeAgentSdkDriver.ts
972
+ const createClaudeAgentSdkDriver = ({ onEvents, onSessionOpen, onSessionsChange }) => {
973
+ const openSessionMap = /* @__PURE__ */ new Map();
974
+ const taskRegistry = createTaskRegistry();
975
+ const emit = (sessionId, events) => {
976
+ const openSession = openSessionMap.get(sessionId);
977
+ let isStateChanged = false;
978
+ if (openSession) {
979
+ openSession.lastActivityAt = /* @__PURE__ */ new Date();
980
+ for (const event of events) if (event.type === "SessionState" && event.state !== openSession.state) {
981
+ openSession.state = event.state;
982
+ isStateChanged = true;
983
+ }
984
+ }
985
+ onEvents(sessionId, events);
986
+ if (isStateChanged) onSessionsChange();
987
+ };
988
+ const getOpenSession = (sessionId) => {
989
+ const openSession = openSessionMap.get(sessionId);
990
+ if (!openSession) throw new InvalidOperationError(Operation.Read, sessionId, "the session is not open on this host");
991
+ return openSession;
992
+ };
993
+ const openSessionQuery = createSessionOpener({
994
+ emit,
995
+ onSessionOpen,
996
+ onSessionsChange,
997
+ openSessionMap,
998
+ taskRegistry
999
+ });
1000
+ const closeSession = (sessionId) => {
1001
+ const closingSession = getOpenSession(sessionId);
1002
+ closeOpenSession(sessionId, closingSession, "", {
1003
+ emit,
1004
+ onSessionsChange,
1005
+ openSessionMap
1006
+ });
1007
+ closingSession.input.close();
1008
+ closingSession.query.close();
1009
+ };
1010
+ const prompt = (sessionId, text, attachments) => {
1011
+ const openSession = getOpenSession(sessionId);
1012
+ const uuid = crypto.randomUUID();
1013
+ const createdAt = /* @__PURE__ */ new Date();
1014
+ openSession.input.push({
1015
+ message: {
1016
+ content: [...attachments.map((attachment) => toContentBlockParam(attachment)), {
1017
+ text,
1018
+ type: "text"
1019
+ }],
1020
+ role: "user"
1021
+ },
1022
+ parent_tool_use_id: null,
1023
+ type: "user",
1024
+ uuid
1025
+ });
1026
+ openSession.title ||= text;
1027
+ emit(sessionId, [{
1028
+ attachmentCount: attachments.length,
1029
+ createdAt,
1030
+ id: uuid,
1031
+ messageUuid: uuid,
1032
+ parentToolUseId: "",
1033
+ text,
1034
+ type: "UserMessage"
1035
+ }, {
1036
+ createdAt,
1037
+ id: getEventId(uuid, "SessionState"),
1038
+ state: "Running",
1039
+ type: "SessionState"
1040
+ }]);
1041
+ };
1042
+ const resumeSession = async (sessionId) => {
1043
+ if (openSessionMap.has(sessionId)) return sessionId;
1044
+ const cwd = await readSessionCwd(sessionId);
1045
+ return openSessionQuery({
1046
+ cwd,
1047
+ isFork: false,
1048
+ resumeAt: "",
1049
+ resumeFrom: sessionId,
1050
+ sessionId
1051
+ });
1052
+ };
1053
+ return {
1054
+ close: async () => {
1055
+ for (const sessionId of openSessionMap.keys()) closeSession(sessionId);
1056
+ await taskRegistry.drain();
1057
+ },
1058
+ closeSession,
1059
+ createSession: (cwd) => openSessionQuery({
1060
+ cwd,
1061
+ isFork: false,
1062
+ resumeAt: "",
1063
+ resumeFrom: "",
1064
+ sessionId: crypto.randomUUID()
1065
+ }),
1066
+ forkSession: async (sessionId, messageUuid) => {
1067
+ const cwd = await readSessionCwd(sessionId);
1068
+ return openSessionQuery({
1069
+ cwd,
1070
+ isFork: true,
1071
+ resumeAt: messageUuid,
1072
+ resumeFrom: sessionId,
1073
+ sessionId: crypto.randomUUID()
1074
+ });
1075
+ },
1076
+ interrupt: async (sessionId) => {
1077
+ await getOpenSession(sessionId).query.interrupt();
1078
+ },
1079
+ listSessions: async () => {
1080
+ const sessionInfos = await listSessions({ limit: 50 });
1081
+ const savedSessionIds = new Set(sessionInfos.map(({ sessionId }) => sessionId));
1082
+ const unsavedSessions = [...openSessionMap].filter(([id]) => !savedSessionIds.has(id)).map(([id, { cwd, lastActivityAt, state, title }]) => ({
1083
+ cwd,
1084
+ id,
1085
+ lastActivityAt,
1086
+ state,
1087
+ title
1088
+ }));
1089
+ const savedSessions = sessionInfos.map((sessionInfo) => {
1090
+ const openSession = openSessionMap.get(sessionInfo.sessionId);
1091
+ return {
1092
+ cwd: sessionInfo.cwd ?? "",
1093
+ id: sessionInfo.sessionId,
1094
+ lastActivityAt: openSession?.lastActivityAt ?? new Date(sessionInfo.lastModified),
1095
+ state: openSession?.state ?? "Closed",
1096
+ title: openSession?.title || getSessionTitle(sessionInfo)
1097
+ };
1098
+ });
1099
+ return [...unsavedSessions, ...savedSessions];
1100
+ },
1101
+ prompt,
1102
+ resolvePermission: (sessionId, requestId, behavior, message) => {
1103
+ getOpenSession(sessionId).pendingPermissionMap.get(requestId)?.settle(behavior, message);
1104
+ },
1105
+ resumeAt: async (sessionId, messageUuid) => {
1106
+ const cwd = await readSessionCwd(sessionId);
1107
+ if (openSessionMap.has(sessionId)) closeSession(sessionId);
1108
+ return openSessionQuery({
1109
+ cwd,
1110
+ isFork: false,
1111
+ resumeAt: messageUuid,
1112
+ resumeFrom: sessionId,
1113
+ sessionId
1114
+ });
1115
+ },
1116
+ resumeSession,
1117
+ rewindFiles: async (sessionId, messageUuid) => {
1118
+ await resumeSession(sessionId);
1119
+ const { query } = getOpenSession(sessionId);
1120
+ const { canRewind, deletions, error, filesChanged, insertions } = await query.rewindFiles(messageUuid, { dryRun: true });
1121
+ if (!canRewind) throw new InvalidOperationError(Operation.Update, sessionId, error ?? "nothing to rewind");
1122
+ const rewindResult = await query.rewindFiles(messageUuid);
1123
+ if (rewindResult.error) throw new InvalidOperationError(Operation.Update, sessionId, rewindResult.error);
1124
+ emit(sessionId, [{
1125
+ createdAt: /* @__PURE__ */ new Date(),
1126
+ deletions: deletions ?? 0,
1127
+ filePaths: filesChanged ?? [],
1128
+ id: crypto.randomUUID(),
1129
+ insertions: insertions ?? 0,
1130
+ messageUuid,
1131
+ type: "FileRewind"
1132
+ }]);
1133
+ },
1134
+ runSlashCommand: (sessionId, name, commandArguments) => {
1135
+ prompt(sessionId, commandArguments ? `/${name} ${commandArguments}` : `/${name}`, []);
1136
+ },
1137
+ setModel: async (sessionId, model) => {
1138
+ const { mapper, query } = getOpenSession(sessionId);
1139
+ await query.setModel(model);
1140
+ emit(sessionId, [mapper.updateSettings(crypto.randomUUID(), /* @__PURE__ */ new Date(), { model })]);
1141
+ },
1142
+ setPermissionMode: async (sessionId, permissionMode) => {
1143
+ const { mapper, query } = getOpenSession(sessionId);
1144
+ await query.setPermissionMode(permissionMode);
1145
+ emit(sessionId, [mapper.updateSettings(crypto.randomUUID(), /* @__PURE__ */ new Date(), { permissionMode })]);
1146
+ }
1147
+ };
1148
+ };
1149
+ //#endregion
1150
+ //#region src/services/server/constants.ts
1151
+ const HOST_NAME = "agent-console-server";
1152
+ const TOKEN_DIRECTORY_NAME = ".agent-console-server";
1153
+ const TOKEN_FILENAME = "token";
1154
+ const TOKEN_BYTE_LENGTH = 32;
1155
+ //#endregion
1156
+ //#region src/services/server/answerHttpRequest.ts
1157
+ const answerHttpRequest = ({ headers, method }, response) => {
1158
+ response.setHeader("Access-Control-Allow-Origin", headers.origin ?? "*");
1159
+ response.setHeader("Access-Control-Allow-Private-Network", "true");
1160
+ response.setHeader("Vary", "Origin");
1161
+ if (method === "OPTIONS") {
1162
+ response.setHeader("Access-Control-Allow-Methods", "GET");
1163
+ response.setHeader("Access-Control-Allow-Headers", headers["access-control-request-headers"] ?? "*");
1164
+ response.writeHead(204).end();
1165
+ } else if (method === "GET") response.writeHead(200, { "Content-Type": "application/json" }).end(JSON.stringify({ name: HOST_NAME }));
1166
+ else response.writeHead(405).end();
1167
+ };
1168
+ //#endregion
1169
+ //#region src/services/server/checkIsTokenValid.ts
1170
+ const checkIsTokenValid = (candidate, token) => {
1171
+ if (!token) return false;
1172
+ const candidateBuffer = Buffer.from(candidate);
1173
+ const tokenBuffer = Buffer.from(token);
1174
+ return candidateBuffer.length === tokenBuffer.length && timingSafeEqual(candidateBuffer, tokenBuffer);
1175
+ };
1176
+ //#endregion
1177
+ //#region src/services/server/createEventLog.ts
1178
+ const createEventLog = () => {
1179
+ const sessionEventMap = /* @__PURE__ */ new Map();
1180
+ return {
1181
+ append: (sessionId, events) => {
1182
+ const sessionEvents = getOrCreate(sessionEventMap, sessionId, () => ({
1183
+ eventIds: /* @__PURE__ */ new Set(),
1184
+ events: []
1185
+ }));
1186
+ const newEvents = [];
1187
+ for (const event of events) {
1188
+ if (EphemeralAgentEventTypes.includes(event.type)) {
1189
+ newEvents.push(event);
1190
+ continue;
1191
+ }
1192
+ if (sessionEvents.eventIds.has(event.id)) continue;
1193
+ sessionEvents.eventIds.add(event.id);
1194
+ sessionEvents.events.push(event);
1195
+ newEvents.push(event);
1196
+ }
1197
+ return newEvents;
1198
+ },
1199
+ entries: () => Array.from(sessionEventMap, ([sessionId, { events }]) => [sessionId, events]),
1200
+ reset: (sessionId) => {
1201
+ sessionEventMap.delete(sessionId);
1202
+ }
1203
+ };
1204
+ };
1205
+ //#endregion
1206
+ //#region src/services/server/handleCommand.ts
1207
+ const handleCommand = async (driver, command) => {
1208
+ switch (command.type) {
1209
+ case "CloseSession":
1210
+ driver.closeSession(command.sessionId);
1211
+ return "";
1212
+ case "CreateSession": return driver.createSession(command.cwd);
1213
+ case "Fork": return driver.forkSession(command.sessionId, command.messageUuid);
1214
+ case "Interrupt":
1215
+ await driver.interrupt(command.sessionId);
1216
+ return "";
1217
+ case "ListSessions": return "";
1218
+ case "PermissionVerdict":
1219
+ driver.resolvePermission(command.sessionId, command.requestId, command.behavior, command.message);
1220
+ return "";
1221
+ case "Prompt":
1222
+ driver.prompt(command.sessionId, command.text, command.attachments);
1223
+ return "";
1224
+ case "Resume": return driver.resumeSession(command.sessionId);
1225
+ case "ResumeAt": return driver.resumeAt(command.sessionId, command.messageUuid);
1226
+ case "RewindFiles":
1227
+ await driver.rewindFiles(command.sessionId, command.messageUuid);
1228
+ return "";
1229
+ case "SetModel":
1230
+ await driver.setModel(command.sessionId, command.model);
1231
+ return "";
1232
+ case "SetPermissionMode":
1233
+ await driver.setPermissionMode(command.sessionId, command.permissionMode);
1234
+ return "";
1235
+ case "SlashCommand":
1236
+ driver.runSlashCommand(command.sessionId, command.name, command.arguments);
1237
+ return "";
1238
+ default: return exhaustiveGuard(command);
1239
+ }
1240
+ };
1241
+ //#endregion
1242
+ //#region src/services/server/sendServerMessage.ts
1243
+ const sendServerMessage = (webSocket, message) => {
1244
+ if (webSocket.readyState === WebSocket.OPEN) webSocket.send(JSON.stringify(message));
1245
+ };
1246
+ //#endregion
1247
+ //#region src/services/server/createAgentConsoleServer.ts
1248
+ const createAgentConsoleServer = async ({ createDriver, hostname, port, token }) => {
1249
+ const eventLog = createEventLog();
1250
+ const taskRegistry = createTaskRegistry();
1251
+ const webSocketServer = new WebSocketServer({ noServer: true });
1252
+ const broadcast = (message) => {
1253
+ for (const webSocket of webSocketServer.clients) sendServerMessage(webSocket, message);
1254
+ };
1255
+ let isRefreshing = false;
1256
+ let isRefreshStale = false;
1257
+ const refreshSessions = () => {
1258
+ if (isRefreshing) {
1259
+ isRefreshStale = true;
1260
+ return;
1261
+ }
1262
+ isRefreshing = true;
1263
+ taskRegistry.run(async () => {
1264
+ do {
1265
+ isRefreshStale = false;
1266
+ await getResultAsync(() => driver.listSessions()).match((sessions) => {
1267
+ broadcast({
1268
+ sessions,
1269
+ type: "Sessions"
1270
+ });
1271
+ }, console.error);
1272
+ } while (isRefreshStale);
1273
+ isRefreshing = false;
1274
+ });
1275
+ };
1276
+ const driver = createDriver({
1277
+ onEvents: (sessionId, events) => {
1278
+ const newEvents = eventLog.append(sessionId, events);
1279
+ if (newEvents.length > 0) broadcast({
1280
+ events: newEvents,
1281
+ sessionId,
1282
+ type: "Events"
1283
+ });
1284
+ },
1285
+ onSessionOpen: (sessionId) => {
1286
+ eventLog.reset(sessionId);
1287
+ broadcast({
1288
+ sessionId,
1289
+ type: "SessionReset"
1290
+ });
1291
+ },
1292
+ onSessionsChange: () => {
1293
+ refreshSessions();
1294
+ }
1295
+ });
1296
+ const receive = async (webSocket, data) => {
1297
+ const text = Buffer.isBuffer(data) ? data.toString() : Array.isArray(data) ? Buffer.concat(data).toString() : Buffer.from(data).toString();
1298
+ const command = getResult(() => commandSchema.parse(JSON.parse(text))).orTee((error) => {
1299
+ sendServerMessage(webSocket, {
1300
+ commandId: "",
1301
+ message: error.message,
1302
+ type: "CommandError"
1303
+ });
1304
+ }).unwrapOr(void 0);
1305
+ if (!command) return;
1306
+ await getResultAsync(() => handleCommand(driver, command)).match((sessionId) => {
1307
+ if (sessionId) sendServerMessage(webSocket, {
1308
+ commandId: command.id,
1309
+ sessionId,
1310
+ type: "SessionOpened"
1311
+ });
1312
+ else if (command.type === "ListSessions") refreshSessions();
1313
+ }, (error) => {
1314
+ sendServerMessage(webSocket, {
1315
+ commandId: command.id,
1316
+ message: error.message,
1317
+ type: "CommandError"
1318
+ });
1319
+ });
1320
+ };
1321
+ webSocketServer.on("connection", (webSocket) => {
1322
+ for (const [sessionId, events] of eventLog.entries()) sendServerMessage(webSocket, {
1323
+ events,
1324
+ sessionId,
1325
+ type: "Events"
1326
+ });
1327
+ refreshSessions();
1328
+ webSocket.on("message", (data) => {
1329
+ taskRegistry.run(() => receive(webSocket, data));
1330
+ });
1331
+ });
1332
+ const httpServer = createServer((request, response) => {
1333
+ answerHttpRequest(request, response);
1334
+ });
1335
+ httpServer.on("upgrade", (request, socket, head) => {
1336
+ const url = new URL(request.url ?? "/", "http://localhost");
1337
+ if (!checkIsTokenValid(url.searchParams.get("token") ?? "", token)) {
1338
+ socket.end("HTTP/1.1 401 Unauthorized\r\n\r\n");
1339
+ return;
1340
+ }
1341
+ webSocketServer.handleUpgrade(request, socket, head, (webSocket) => {
1342
+ webSocketServer.emit("connection", webSocket, request);
1343
+ });
1344
+ });
1345
+ httpServer.listen(port, hostname);
1346
+ await once(httpServer, "listening");
1347
+ const address = httpServer.address();
1348
+ return {
1349
+ close: async () => {
1350
+ await driver.close();
1351
+ for (const webSocket of webSocketServer.clients) webSocket.terminate();
1352
+ webSocketServer.close();
1353
+ httpServer.close();
1354
+ await Promise.all([
1355
+ taskRegistry.drain(),
1356
+ once(webSocketServer, "close"),
1357
+ once(httpServer, "close")
1358
+ ]);
1359
+ },
1360
+ port: typeof address === "object" && address ? address.port : port
1361
+ };
1362
+ };
1363
+ //#endregion
1364
+ //#region src/services/server/readToken.ts
1365
+ const readToken = () => {
1366
+ const tokenDirectory = join(homedir(), TOKEN_DIRECTORY_NAME);
1367
+ const tokenPath = join(tokenDirectory, TOKEN_FILENAME);
1368
+ const storedToken = existsSync(tokenPath) ? readFileSync(tokenPath, "utf8").trim() : "";
1369
+ if (storedToken) return storedToken;
1370
+ const token = randomBytes(32).toString("base64url");
1371
+ mkdirSync(tokenDirectory, { recursive: true });
1372
+ rmSync(tokenPath, { force: true });
1373
+ writeFileSync(tokenPath, token, { mode: 384 });
1374
+ return token;
1375
+ };
1376
+ //#endregion
1377
+ export { mapTaskMessage as A, toPermissionRequestEvent as B, createSdkMessageMapper as C, mapUserContent as D, parseMessageBody as E, TodoToolName as F, SETTING_SOURCES as G, DEFAULT_DENY_MESSAGE as H, createStreamTracker as I, closeOpenSession as K, createFileOriginTracker as L, mapAssistantContent as M, toUnknownEvent as N, toToolResultText as O, createTodoTracker as P, createPermissionBridge as R, readSessionHistory as S, toSessionInitEvent as T, IGNORED_SDK_MESSAGE_TYPES as U, createInputQueue as V, SESSION_LIST_LIMIT as W, watchSession as _, createEventLog as a, getSessionTitle as b, HOST_NAME as c, TOKEN_FILENAME as d, createClaudeAgentSdkDriver as f, createSessionOpener as g, readSessionCwd as h, handleCommand as i, mapHookMessage as j, mapSystemMessage as k, TOKEN_BYTE_LENGTH as l, toContentBlockParam as m, createAgentConsoleServer as n, checkIsTokenValid as o, createTaskRegistry as p, getEventId as q, sendServerMessage as r, answerHttpRequest as s, readToken as t, TOKEN_DIRECTORY_NAME as u, toContextUsageEvent as v, toTurnResultEvents as w, readToolUseResultMap as x, toCapabilitiesEvent as y, toPermissionResult as z };