@a5c-ai/agent-mux-adapters 0.3.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/README.md +3 -1
  2. package/dist/agent-mux-remote-adapter.d.ts.map +1 -1
  3. package/dist/agent-mux-remote-adapter.js +4 -0
  4. package/dist/agent-mux-remote-adapter.js.map +1 -1
  5. package/dist/amp-adapter.d.ts +32 -0
  6. package/dist/amp-adapter.d.ts.map +1 -0
  7. package/dist/amp-adapter.js +412 -0
  8. package/dist/amp-adapter.js.map +1 -0
  9. package/dist/babysitter-adapter.d.ts +29 -0
  10. package/dist/babysitter-adapter.d.ts.map +1 -0
  11. package/dist/babysitter-adapter.js +338 -0
  12. package/dist/babysitter-adapter.js.map +1 -0
  13. package/dist/base-adapter-helpers.d.ts +8 -0
  14. package/dist/base-adapter-helpers.d.ts.map +1 -0
  15. package/dist/base-adapter-helpers.js +68 -0
  16. package/dist/base-adapter-helpers.js.map +1 -0
  17. package/dist/base-adapter.d.ts +21 -9
  18. package/dist/base-adapter.d.ts.map +1 -1
  19. package/dist/base-adapter.js +136 -67
  20. package/dist/base-adapter.js.map +1 -1
  21. package/dist/claude-adapter.d.ts +14 -3
  22. package/dist/claude-adapter.d.ts.map +1 -1
  23. package/dist/claude-adapter.js +224 -24
  24. package/dist/claude-adapter.js.map +1 -1
  25. package/dist/claude-agent-sdk-adapter.d.ts +40 -0
  26. package/dist/claude-agent-sdk-adapter.d.ts.map +1 -0
  27. package/dist/claude-agent-sdk-adapter.js +839 -0
  28. package/dist/claude-agent-sdk-adapter.js.map +1 -0
  29. package/dist/claude-code/runtime-hooks/ephemeral-config.d.ts +12 -0
  30. package/dist/claude-code/runtime-hooks/ephemeral-config.d.ts.map +1 -0
  31. package/dist/claude-code/runtime-hooks/ephemeral-config.js +143 -0
  32. package/dist/claude-code/runtime-hooks/ephemeral-config.js.map +1 -0
  33. package/dist/claude-code/runtime-hooks/hook-socket-server.d.ts +10 -0
  34. package/dist/claude-code/runtime-hooks/hook-socket-server.d.ts.map +1 -0
  35. package/dist/claude-code/runtime-hooks/hook-socket-server.js +79 -0
  36. package/dist/claude-code/runtime-hooks/hook-socket-server.js.map +1 -0
  37. package/dist/claude-code/runtime-hooks/lifecycle.d.ts +3 -0
  38. package/dist/claude-code/runtime-hooks/lifecycle.d.ts.map +1 -0
  39. package/dist/claude-code/runtime-hooks/lifecycle.js +24 -0
  40. package/dist/claude-code/runtime-hooks/lifecycle.js.map +1 -0
  41. package/dist/claude-remote-control-adapter.d.ts +43 -0
  42. package/dist/claude-remote-control-adapter.d.ts.map +1 -0
  43. package/dist/claude-remote-control-adapter.js +505 -0
  44. package/dist/claude-remote-control-adapter.js.map +1 -0
  45. package/dist/codex-adapter.d.ts.map +1 -1
  46. package/dist/codex-adapter.js +128 -17
  47. package/dist/codex-adapter.js.map +1 -1
  48. package/dist/codex-sdk-adapter.d.ts +32 -0
  49. package/dist/codex-sdk-adapter.d.ts.map +1 -0
  50. package/dist/codex-sdk-adapter.js +388 -0
  51. package/dist/codex-sdk-adapter.js.map +1 -0
  52. package/dist/codex-sdk-mocks.d.ts +51 -0
  53. package/dist/codex-sdk-mocks.d.ts.map +1 -0
  54. package/dist/codex-sdk-mocks.js +97 -0
  55. package/dist/codex-sdk-mocks.js.map +1 -0
  56. package/dist/codex-websocket-adapter.d.ts +34 -0
  57. package/dist/codex-websocket-adapter.d.ts.map +1 -0
  58. package/dist/codex-websocket-adapter.js +398 -0
  59. package/dist/codex-websocket-adapter.js.map +1 -0
  60. package/dist/codex-websocket-connection.d.ts +78 -0
  61. package/dist/codex-websocket-connection.d.ts.map +1 -0
  62. package/dist/codex-websocket-connection.js +733 -0
  63. package/dist/codex-websocket-connection.js.map +1 -0
  64. package/dist/copilot-adapter.d.ts.map +1 -1
  65. package/dist/copilot-adapter.js +11 -3
  66. package/dist/copilot-adapter.js.map +1 -1
  67. package/dist/cursor-adapter.d.ts +3 -1
  68. package/dist/cursor-adapter.d.ts.map +1 -1
  69. package/dist/cursor-adapter.js +10 -3
  70. package/dist/cursor-adapter.js.map +1 -1
  71. package/dist/droid-adapter.d.ts +32 -0
  72. package/dist/droid-adapter.d.ts.map +1 -0
  73. package/dist/droid-adapter.js +378 -0
  74. package/dist/droid-adapter.js.map +1 -0
  75. package/dist/gemini-adapter.d.ts +3 -1
  76. package/dist/gemini-adapter.d.ts.map +1 -1
  77. package/dist/gemini-adapter.js +10 -3
  78. package/dist/gemini-adapter.js.map +1 -1
  79. package/dist/hermes-adapter.d.ts.map +1 -1
  80. package/dist/hermes-adapter.js +10 -2
  81. package/dist/hermes-adapter.js.map +1 -1
  82. package/dist/index.d.ts +13 -0
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +13 -1
  85. package/dist/index.js.map +1 -1
  86. package/dist/mcp-plugins.d.ts +5 -3
  87. package/dist/mcp-plugins.d.ts.map +1 -1
  88. package/dist/mcp-plugins.js +58 -24
  89. package/dist/mcp-plugins.js.map +1 -1
  90. package/dist/omp-adapter.d.ts.map +1 -1
  91. package/dist/omp-adapter.js +10 -3
  92. package/dist/omp-adapter.js.map +1 -1
  93. package/dist/openclaw-adapter.d.ts +3 -1
  94. package/dist/openclaw-adapter.d.ts.map +1 -1
  95. package/dist/openclaw-adapter.js +11 -4
  96. package/dist/openclaw-adapter.js.map +1 -1
  97. package/dist/opencode-adapter.d.ts +5 -3
  98. package/dist/opencode-adapter.d.ts.map +1 -1
  99. package/dist/opencode-adapter.js +179 -63
  100. package/dist/opencode-adapter.js.map +1 -1
  101. package/dist/opencode-http-adapter.d.ts +43 -0
  102. package/dist/opencode-http-adapter.d.ts.map +1 -0
  103. package/dist/opencode-http-adapter.js +374 -0
  104. package/dist/opencode-http-adapter.js.map +1 -0
  105. package/dist/opencode-http-connection.d.ts +33 -0
  106. package/dist/opencode-http-connection.d.ts.map +1 -0
  107. package/dist/opencode-http-connection.js +198 -0
  108. package/dist/opencode-http-connection.js.map +1 -0
  109. package/dist/pi-adapter.d.ts.map +1 -1
  110. package/dist/pi-adapter.js +9 -2
  111. package/dist/pi-adapter.js.map +1 -1
  112. package/dist/pi-sdk-adapter.d.ts +46 -0
  113. package/dist/pi-sdk-adapter.d.ts.map +1 -0
  114. package/dist/pi-sdk-adapter.js +553 -0
  115. package/dist/pi-sdk-adapter.js.map +1 -0
  116. package/dist/programmatic-adapter-base.d.ts +88 -0
  117. package/dist/programmatic-adapter-base.d.ts.map +1 -0
  118. package/dist/programmatic-adapter-base.js +169 -0
  119. package/dist/programmatic-adapter-base.js.map +1 -0
  120. package/dist/provider-translation.d.ts +10 -0
  121. package/dist/provider-translation.d.ts.map +1 -0
  122. package/dist/provider-translation.js +2 -0
  123. package/dist/provider-translation.js.map +1 -0
  124. package/dist/qwen-adapter.d.ts +3 -1
  125. package/dist/qwen-adapter.d.ts.map +1 -1
  126. package/dist/qwen-adapter.js +10 -3
  127. package/dist/qwen-adapter.js.map +1 -1
  128. package/dist/remote-adapter-base.d.ts +85 -0
  129. package/dist/remote-adapter-base.d.ts.map +1 -0
  130. package/dist/remote-adapter-base.js +102 -0
  131. package/dist/remote-adapter-base.js.map +1 -0
  132. package/dist/session-fs.d.ts +15 -5
  133. package/dist/session-fs.d.ts.map +1 -1
  134. package/dist/session-fs.js +249 -0
  135. package/dist/session-fs.js.map +1 -1
  136. package/dist/shared/runtime-hooks-virtual.d.ts +3 -0
  137. package/dist/shared/runtime-hooks-virtual.d.ts.map +1 -0
  138. package/dist/shared/runtime-hooks-virtual.js +13 -0
  139. package/dist/shared/runtime-hooks-virtual.js.map +1 -0
  140. package/dist/translate-for-harness.d.ts +6 -0
  141. package/dist/translate-for-harness.d.ts.map +1 -0
  142. package/dist/translate-for-harness.js +36 -0
  143. package/dist/translate-for-harness.js.map +1 -0
  144. package/dist/translations/claude-translation.d.ts +4 -0
  145. package/dist/translations/claude-translation.d.ts.map +1 -0
  146. package/dist/translations/claude-translation.js +50 -0
  147. package/dist/translations/claude-translation.js.map +1 -0
  148. package/dist/translations/codex-translation.d.ts +4 -0
  149. package/dist/translations/codex-translation.d.ts.map +1 -0
  150. package/dist/translations/codex-translation.js +32 -0
  151. package/dist/translations/codex-translation.js.map +1 -0
  152. package/dist/translations/gemini-translation.d.ts +4 -0
  153. package/dist/translations/gemini-translation.d.ts.map +1 -0
  154. package/dist/translations/gemini-translation.js +20 -0
  155. package/dist/translations/gemini-translation.js.map +1 -0
  156. package/dist/translations/generic-openai-translation.d.ts +4 -0
  157. package/dist/translations/generic-openai-translation.d.ts.map +1 -0
  158. package/dist/translations/generic-openai-translation.js +19 -0
  159. package/dist/translations/generic-openai-translation.js.map +1 -0
  160. package/dist/translations/opencode-translation.d.ts +4 -0
  161. package/dist/translations/opencode-translation.d.ts.map +1 -0
  162. package/dist/translations/opencode-translation.js +51 -0
  163. package/dist/translations/opencode-translation.js.map +1 -0
  164. package/package.json +4 -2
@@ -0,0 +1,839 @@
1
+ import * as os from 'node:os';
2
+ import * as path from 'node:path';
3
+ import { promises as fs } from 'node:fs';
4
+ import { createRequire } from 'node:module';
5
+ import { BaseProgrammaticAdapter } from './programmatic-adapter-base.js';
6
+ import { createVirtualRuntimeHookCapabilities } from './shared/runtime-hooks-virtual.js';
7
+ import { mcpListPlugins, mcpInstallPlugin, mcpUninstallPlugin } from './mcp-plugins.js';
8
+ import { listJsonlFiles, parseJsonlSessionFile, readJsonFile, writeJsonFileAtomic, } from './session-fs.js';
9
+ const require = createRequire(import.meta.url);
10
+ class AsyncQueue {
11
+ values = [];
12
+ waiters = [];
13
+ closed = false;
14
+ failure = null;
15
+ enqueue(value) {
16
+ if (this.closed) {
17
+ throw new Error('Queue is closed');
18
+ }
19
+ const waiter = this.waiters.shift();
20
+ if (waiter) {
21
+ waiter.resolve({ value, done: false });
22
+ return;
23
+ }
24
+ this.values.push(value);
25
+ }
26
+ fail(error) {
27
+ if (this.closed) {
28
+ return;
29
+ }
30
+ this.closed = true;
31
+ this.failure = error;
32
+ for (const waiter of this.waiters.splice(0)) {
33
+ waiter.reject(error);
34
+ }
35
+ }
36
+ close() {
37
+ if (this.closed) {
38
+ return;
39
+ }
40
+ this.closed = true;
41
+ for (const waiter of this.waiters.splice(0)) {
42
+ waiter.resolve({ value: undefined, done: true });
43
+ }
44
+ }
45
+ async next() {
46
+ if (this.values.length > 0) {
47
+ return { value: this.values.shift(), done: false };
48
+ }
49
+ if (this.failure != null) {
50
+ throw this.failure;
51
+ }
52
+ if (this.closed) {
53
+ return { value: undefined, done: true };
54
+ }
55
+ return new Promise((resolve, reject) => {
56
+ this.waiters.push({ resolve, reject });
57
+ });
58
+ }
59
+ async return() {
60
+ this.close();
61
+ return { value: undefined, done: true };
62
+ }
63
+ [Symbol.asyncIterator]() {
64
+ return this;
65
+ }
66
+ }
67
+ export class ClaudeAgentSdkAdapter extends BaseProgrammaticAdapter {
68
+ agent = 'claude-agent-sdk';
69
+ displayName = 'Claude (Agent SDK)';
70
+ minVersion = '0.2.0';
71
+ hostEnvSignals = ['ANTHROPIC_API_KEY', 'CLAUDE_AGENT_API_KEY', 'CLAUDE_CODE_ENTRYPOINT'];
72
+ capabilities = {
73
+ agent: 'claude-agent-sdk',
74
+ canResume: true,
75
+ canFork: true,
76
+ supportsMultiTurn: true,
77
+ sessionPersistence: 'file',
78
+ supportsTextStreaming: true,
79
+ supportsToolCallStreaming: true,
80
+ supportsThinkingStreaming: true,
81
+ supportsNativeTools: true,
82
+ supportsMCP: true,
83
+ supportsParallelToolCalls: true,
84
+ requiresToolApproval: true,
85
+ approvalModes: ['yolo', 'prompt', 'deny'],
86
+ runtimeHooks: createVirtualRuntimeHookCapabilities(),
87
+ supportsThinking: true,
88
+ thinkingEffortLevels: ['low', 'medium', 'high', 'max'],
89
+ supportsThinkingBudgetTokens: true,
90
+ supportsJsonMode: true,
91
+ supportsStructuredOutput: true,
92
+ structuredSessionTransport: 'persistent',
93
+ sessionControlPlane: 'self-managed',
94
+ supportsSkills: true,
95
+ supportsAgentsMd: true,
96
+ skillsFormat: 'file',
97
+ supportsSubagentDispatch: true,
98
+ supportsParallelExecution: true,
99
+ maxParallelTasks: 10,
100
+ supportsInteractiveMode: true,
101
+ supportsStdinInjection: true,
102
+ supportsImageInput: true,
103
+ supportsImageOutput: false,
104
+ supportsFileAttachments: false,
105
+ supportsPlugins: true,
106
+ pluginFormats: ['mcp-server'],
107
+ pluginRegistries: [{ name: 'mcp', url: 'https://modelcontextprotocol.io', searchable: false }],
108
+ supportedPlatforms: ['darwin', 'linux', 'win32'],
109
+ requiresGitRepo: false,
110
+ requiresPty: false,
111
+ authMethods: [
112
+ { type: 'api_key', name: 'API Key', description: 'ANTHROPIC_API_KEY environment variable' },
113
+ { type: 'oauth', name: 'Claude Login', description: 'Claude Code browser login or stored credentials' },
114
+ ],
115
+ authFiles: ['.claude.json', '.claude/.credentials.json', '.claude/settings.json'],
116
+ installMethods: [
117
+ { platform: 'all', type: 'npm', command: 'npm install -g @anthropic-ai/claude-agent-sdk' },
118
+ ],
119
+ };
120
+ models = [
121
+ {
122
+ agent: 'claude-agent-sdk',
123
+ modelId: 'claude-sonnet-4-20250514',
124
+ modelAlias: 'sonnet',
125
+ displayName: 'Claude Sonnet 4 (SDK)',
126
+ deprecated: false,
127
+ contextWindow: 200000,
128
+ maxOutputTokens: 16384,
129
+ maxThinkingTokens: 128000,
130
+ inputPricePerMillion: 3,
131
+ outputPricePerMillion: 15,
132
+ thinkingPricePerMillion: 3,
133
+ cachedInputPricePerMillion: 0.3,
134
+ supportsThinking: true,
135
+ thinkingEffortLevels: ['low', 'medium', 'high', 'max'],
136
+ supportsToolCalling: true,
137
+ supportsParallelToolCalls: true,
138
+ supportsToolCallStreaming: true,
139
+ supportsJsonMode: true,
140
+ supportsStructuredOutput: true,
141
+ supportsTextStreaming: true,
142
+ supportsThinkingStreaming: true,
143
+ supportsImageInput: true,
144
+ supportsImageOutput: false,
145
+ supportsFileInput: false,
146
+ cliArgKey: 'model',
147
+ cliArgValue: 'claude-sonnet-4-20250514',
148
+ lastUpdated: '2025-05-14',
149
+ source: 'bundled',
150
+ },
151
+ {
152
+ agent: 'claude-agent-sdk',
153
+ modelId: 'claude-opus-4-20250514',
154
+ modelAlias: 'opus',
155
+ displayName: 'Claude Opus 4 (SDK)',
156
+ deprecated: false,
157
+ contextWindow: 200000,
158
+ maxOutputTokens: 16384,
159
+ maxThinkingTokens: 128000,
160
+ inputPricePerMillion: 15,
161
+ outputPricePerMillion: 75,
162
+ thinkingPricePerMillion: 15,
163
+ cachedInputPricePerMillion: 1.5,
164
+ supportsThinking: true,
165
+ thinkingEffortLevels: ['low', 'medium', 'high', 'max'],
166
+ supportsToolCalling: true,
167
+ supportsParallelToolCalls: true,
168
+ supportsToolCallStreaming: true,
169
+ supportsJsonMode: true,
170
+ supportsStructuredOutput: true,
171
+ supportsTextStreaming: true,
172
+ supportsThinkingStreaming: true,
173
+ supportsImageInput: true,
174
+ supportsImageOutput: false,
175
+ supportsFileInput: false,
176
+ cliArgKey: 'model',
177
+ cliArgValue: 'claude-opus-4-20250514',
178
+ lastUpdated: '2025-05-14',
179
+ source: 'bundled',
180
+ },
181
+ ];
182
+ defaultModelId = 'claude-sonnet-4-20250514';
183
+ configSchema = {
184
+ agent: 'claude-agent-sdk',
185
+ version: 1,
186
+ fields: [],
187
+ configFilePaths: [path.join(os.homedir(), '.claude', 'settings.json')],
188
+ projectConfigFilePaths: ['.claude/settings.json'],
189
+ configFormat: 'json',
190
+ supportsProjectConfig: true,
191
+ };
192
+ execute(options) {
193
+ this.validateRunOptions(options);
194
+ const runId = this.generateRunId();
195
+ const modelId = this.resolveModel(options);
196
+ const events = new AsyncQueue();
197
+ const prompts = new AsyncQueue();
198
+ const pendingApprovals = new Map();
199
+ const pendingInputs = new Map();
200
+ const toolsByIndex = new Map();
201
+ const toolsById = new Map();
202
+ let queryHandle = null;
203
+ let closed = false;
204
+ let turnIndex = -1;
205
+ let textAccumulated = '';
206
+ let thinkingAccumulated = '';
207
+ let sessionId;
208
+ const queueError = (code, message) => {
209
+ events.enqueue(this.createErrorEvent(runId, code, message, false));
210
+ };
211
+ const rejectPending = (reason) => {
212
+ for (const pending of pendingApprovals.values()) {
213
+ pending.reject(reason);
214
+ }
215
+ pendingApprovals.clear();
216
+ for (const pending of pendingInputs.values()) {
217
+ pending.reject(reason);
218
+ }
219
+ pendingInputs.clear();
220
+ };
221
+ const closeRun = async () => {
222
+ if (closed) {
223
+ return;
224
+ }
225
+ closed = true;
226
+ prompts.close();
227
+ rejectPending(new Error('Claude Agent SDK session closed'));
228
+ queryHandle?.close();
229
+ };
230
+ const sdkPromise = (async () => {
231
+ try {
232
+ const authState = await this.detectAuth();
233
+ if (authState.status !== 'authenticated') {
234
+ queueError('AUTH_MISSING', 'Anthropic authentication not found');
235
+ return;
236
+ }
237
+ if (options.attachments?.some((attachment) => !this.isImageAttachment(attachment.mimeType, attachment.filePath, attachment.url))) {
238
+ queueError('CAPABILITY_ERROR', 'claude-agent-sdk currently supports only image attachments through agent-mux');
239
+ return;
240
+ }
241
+ const initialMessage = await this.buildUserMessage(this.normalizePrompt(options.prompt), options.attachments);
242
+ prompts.enqueue(initialMessage);
243
+ if (options.nonInteractive) {
244
+ prompts.close();
245
+ }
246
+ const sdk = await this.loadSdkModule();
247
+ queryHandle = sdk.query({
248
+ prompt: prompts,
249
+ options: this.buildSdkOptions(options, modelId, async (toolName, input, ctx) => {
250
+ const interactionId = ctx.toolUseID || `approval-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
251
+ events.enqueue({
252
+ ...this.createBaseEvent('approval_request', runId),
253
+ type: 'approval_request',
254
+ interactionId,
255
+ action: ctx.title || `Allow ${toolName}`,
256
+ detail: ctx.description || JSON.stringify(input),
257
+ toolName,
258
+ riskLevel: this.estimateRiskLevel(toolName),
259
+ });
260
+ return await new Promise((resolve, reject) => {
261
+ pendingApprovals.set(interactionId, { resolve, reject });
262
+ });
263
+ }, async (request) => {
264
+ const interactionId = request.elicitationId || `input-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
265
+ events.enqueue({
266
+ ...this.createBaseEvent('input_required', runId),
267
+ type: 'input_required',
268
+ interactionId,
269
+ question: request.title || request.message,
270
+ context: request.description || request.url,
271
+ source: 'tool',
272
+ });
273
+ return await new Promise((resolve, reject) => {
274
+ pendingInputs.set(interactionId, { resolve, reject });
275
+ });
276
+ }),
277
+ });
278
+ for await (const message of queryHandle) {
279
+ const translated = this.translateSdkMessage({
280
+ runId,
281
+ options,
282
+ message,
283
+ modelId,
284
+ textAccumulated,
285
+ thinkingAccumulated,
286
+ toolsByIndex,
287
+ toolsById,
288
+ turnIndex,
289
+ sessionId,
290
+ });
291
+ textAccumulated = translated.textAccumulated;
292
+ thinkingAccumulated = translated.thinkingAccumulated;
293
+ turnIndex = translated.turnIndex;
294
+ sessionId = translated.sessionId;
295
+ for (const event of translated.events) {
296
+ events.enqueue(event);
297
+ }
298
+ }
299
+ }
300
+ catch (error) {
301
+ const message = error instanceof Error ? error.message : String(error);
302
+ queueError('INTERNAL', `Claude Agent SDK error: ${message}`);
303
+ }
304
+ finally {
305
+ await closeRun();
306
+ events.close();
307
+ }
308
+ })();
309
+ void sdkPromise;
310
+ return Object.assign(events, {
311
+ send: async (text) => {
312
+ if (closed) {
313
+ throw new Error('Claude Agent SDK session is closed');
314
+ }
315
+ prompts.enqueue(await this.buildUserMessage(text, []));
316
+ },
317
+ respond: async (interactionId, response) => {
318
+ const pendingApproval = pendingApprovals.get(interactionId);
319
+ if (pendingApproval) {
320
+ pendingApprovals.delete(interactionId);
321
+ if (response.type === 'approve') {
322
+ events.enqueue({
323
+ ...this.createBaseEvent('approval_granted', runId),
324
+ type: 'approval_granted',
325
+ interactionId,
326
+ });
327
+ pendingApproval.resolve({ behavior: 'allow' });
328
+ return;
329
+ }
330
+ if (response.type === 'deny') {
331
+ events.enqueue({
332
+ ...this.createBaseEvent('approval_denied', runId),
333
+ type: 'approval_denied',
334
+ interactionId,
335
+ reason: response.reason,
336
+ });
337
+ pendingApproval.resolve({ behavior: 'deny', message: response.reason || 'Denied by user' });
338
+ return;
339
+ }
340
+ throw new Error('Approval requests require approve/deny responses');
341
+ }
342
+ const pendingInput = pendingInputs.get(interactionId);
343
+ if (pendingInput) {
344
+ pendingInputs.delete(interactionId);
345
+ if (response.type !== 'text') {
346
+ pendingInput.resolve({ action: 'decline' });
347
+ return;
348
+ }
349
+ pendingInput.resolve({ action: 'accept', content: { response: response.text } });
350
+ return;
351
+ }
352
+ throw new Error(`No pending Claude SDK interaction with id '${interactionId}'`);
353
+ },
354
+ interrupt: async () => {
355
+ await queryHandle?.interrupt();
356
+ },
357
+ close: async () => {
358
+ await closeRun();
359
+ },
360
+ });
361
+ }
362
+ async detectInstallation() {
363
+ try {
364
+ const sdkEntry = require.resolve('@anthropic-ai/claude-agent-sdk');
365
+ const pkgPath = path.join(path.dirname(sdkEntry), 'package.json');
366
+ const pkg = await readJsonFile(pkgPath);
367
+ return {
368
+ installed: true,
369
+ version: pkg?.version ?? undefined,
370
+ path: sdkEntry,
371
+ };
372
+ }
373
+ catch {
374
+ return { installed: false };
375
+ }
376
+ }
377
+ async detectAuth() {
378
+ const apiKey = process.env['ANTHROPIC_API_KEY'] || process.env['CLAUDE_AGENT_API_KEY'];
379
+ if (apiKey) {
380
+ return {
381
+ status: 'authenticated',
382
+ method: 'api_key',
383
+ identity: `anthropic:...${apiKey.slice(-4)}`,
384
+ };
385
+ }
386
+ for (const credentialsPath of [
387
+ path.join(os.homedir(), '.claude', '.credentials.json'),
388
+ path.join(os.homedir(), '.claude.json'),
389
+ path.join(os.homedir(), '.claude', 'settings.json'),
390
+ ]) {
391
+ try {
392
+ const data = await readJsonFile(credentialsPath);
393
+ if (data) {
394
+ const email = typeof data['email'] === 'string' ? data['email'] : undefined;
395
+ const userId = typeof data['userId'] === 'string'
396
+ ? data['userId']
397
+ : typeof data['user']?.['id'] === 'string'
398
+ ? data['user']['id']
399
+ : undefined;
400
+ if (email || userId || Object.keys(data).length > 0) {
401
+ return {
402
+ status: 'authenticated',
403
+ method: 'oauth',
404
+ identity: email ? `claude:${email}` : `claude:${userId ?? 'local'}`,
405
+ };
406
+ }
407
+ }
408
+ }
409
+ catch {
410
+ // Ignore missing or invalid auth files.
411
+ }
412
+ }
413
+ return { status: 'unauthenticated' };
414
+ }
415
+ getAuthGuidance() {
416
+ return {
417
+ agent: 'claude-agent-sdk',
418
+ providerName: 'Anthropic',
419
+ steps: [
420
+ {
421
+ step: 1,
422
+ description: 'Get an API key from https://console.anthropic.com/settings/keys',
423
+ url: 'https://console.anthropic.com/settings/keys',
424
+ },
425
+ {
426
+ step: 2,
427
+ description: 'Set the ANTHROPIC_API_KEY environment variable',
428
+ command: 'export ANTHROPIC_API_KEY=sk-ant-...',
429
+ },
430
+ {
431
+ step: 3,
432
+ description: 'Or sign in through Claude Code and let the SDK reuse the stored credentials',
433
+ command: 'claude',
434
+ },
435
+ ],
436
+ envVars: [
437
+ { name: 'ANTHROPIC_API_KEY', description: 'Anthropic API key', required: false, exampleFormat: 'sk-ant-...' },
438
+ { name: 'CLAUDE_AGENT_API_KEY', description: 'Alternate Anthropic API key env var', required: false, exampleFormat: 'sk-ant-...' },
439
+ ],
440
+ documentationUrls: ['https://platform.claude.com/docs/en/agent-sdk/overview'],
441
+ loginCommand: 'claude',
442
+ verifyCommand: 'node -e "import(\'@anthropic-ai/claude-agent-sdk\').then(() => console.log(\'OK\'))"',
443
+ };
444
+ }
445
+ sessionDir(_cwd) {
446
+ return path.join(os.homedir(), '.claude', 'projects');
447
+ }
448
+ async parseSessionFile(filePath) {
449
+ const parsed = await parseJsonlSessionFile(filePath, 'claude-agent-sdk');
450
+ return { ...parsed, agent: 'claude-agent-sdk' };
451
+ }
452
+ async listSessionFiles(_cwd) {
453
+ return listJsonlFiles(this.sessionDir());
454
+ }
455
+ async readConfig(_cwd) {
456
+ const filePath = this.configSchema.configFilePaths?.[0];
457
+ if (!filePath)
458
+ return { agent: 'claude-agent-sdk', source: 'global' };
459
+ const data = (await readJsonFile(filePath)) ?? {};
460
+ return { agent: 'claude-agent-sdk', source: 'global', filePaths: [filePath], ...data };
461
+ }
462
+ async writeConfig(config, _cwd) {
463
+ const filePath = this.configSchema.configFilePaths?.[0];
464
+ if (!filePath)
465
+ return;
466
+ const existing = (await readJsonFile(filePath)) ?? {};
467
+ const { agent: _agent, source: _source, filePaths: _filePaths, ...rest } = config;
468
+ void _agent;
469
+ void _source;
470
+ void _filePaths;
471
+ await writeJsonFileAtomic(filePath, { ...existing, ...rest });
472
+ }
473
+ async listPlugins() {
474
+ return mcpListPlugins(this.agent);
475
+ }
476
+ async installPlugin(pluginId, options) {
477
+ return mcpInstallPlugin(this.agent, pluginId, options);
478
+ }
479
+ async uninstallPlugin(pluginId, _options) {
480
+ return mcpUninstallPlugin(this.agent, pluginId);
481
+ }
482
+ async loadSdkModule() {
483
+ return await import('@anthropic-ai/claude-agent-sdk');
484
+ }
485
+ buildSdkOptions(options, modelId, canUseTool, onElicitation) {
486
+ const permissionMode = this.mapPermissionMode(options.approvalMode);
487
+ const sdkOptions = {
488
+ model: modelId,
489
+ cwd: options.cwd ?? process.cwd(),
490
+ env: options.env ? { ...process.env, ...options.env } : undefined,
491
+ resume: options.sessionId ?? options.forkSessionId,
492
+ forkSession: options.forkSessionId != null,
493
+ persistSession: options.noSession ? false : true,
494
+ permissionMode,
495
+ allowDangerouslySkipPermissions: permissionMode === 'bypassPermissions',
496
+ canUseTool,
497
+ onElicitation,
498
+ includePartialMessages: true,
499
+ includeHookEvents: true,
500
+ settingSources: ['user', 'project', 'local'],
501
+ mcpServers: this.buildMcpServers(options),
502
+ maxTurns: options.maxTurns,
503
+ systemPrompt: this.buildSystemPrompt(options),
504
+ stderr: (data) => {
505
+ if (data.trim().length > 0) {
506
+ // Surface stderr as a debug event via the regular SDK stream handling.
507
+ }
508
+ },
509
+ };
510
+ if (options.thinkingBudgetTokens != null) {
511
+ sdkOptions.thinking = {
512
+ type: 'enabled',
513
+ budgetTokens: options.thinkingBudgetTokens,
514
+ };
515
+ }
516
+ if (options.thinkingEffort) {
517
+ sdkOptions.effort = options.thinkingEffort === 'max' ? 'max' : options.thinkingEffort;
518
+ }
519
+ return sdkOptions;
520
+ }
521
+ buildMcpServers(options) {
522
+ if (!options.mcpServers || options.mcpServers.length === 0) {
523
+ return undefined;
524
+ }
525
+ const servers = {};
526
+ for (const server of options.mcpServers) {
527
+ if (server.transport === 'stdio') {
528
+ servers[server.name] = {
529
+ type: 'stdio',
530
+ command: server.command,
531
+ args: server.args,
532
+ env: server.env,
533
+ };
534
+ continue;
535
+ }
536
+ servers[server.name] = {
537
+ type: server.transport === 'streamable-http' ? 'http' : 'sse',
538
+ url: server.url,
539
+ headers: server.headers,
540
+ };
541
+ }
542
+ return servers;
543
+ }
544
+ buildSystemPrompt(options) {
545
+ if (!options.systemPrompt) {
546
+ return undefined;
547
+ }
548
+ if (options.systemPromptMode === 'append' || options.systemPromptMode == null) {
549
+ return {
550
+ type: 'preset',
551
+ preset: 'claude_code',
552
+ append: options.systemPrompt,
553
+ };
554
+ }
555
+ if (options.systemPromptMode === 'replace') {
556
+ return options.systemPrompt;
557
+ }
558
+ return {
559
+ type: 'preset',
560
+ preset: 'claude_code',
561
+ append: options.systemPrompt,
562
+ };
563
+ }
564
+ mapPermissionMode(mode) {
565
+ switch (mode) {
566
+ case 'yolo':
567
+ return 'bypassPermissions';
568
+ case 'deny':
569
+ return 'dontAsk';
570
+ default:
571
+ return 'default';
572
+ }
573
+ }
574
+ estimateRiskLevel(toolName) {
575
+ const lowered = toolName.toLowerCase();
576
+ if (lowered.includes('bash') || lowered.includes('delete') || lowered.includes('write') || lowered.includes('edit')) {
577
+ return 'high';
578
+ }
579
+ if (lowered.includes('read') || lowered.includes('grep') || lowered.includes('glob')) {
580
+ return 'low';
581
+ }
582
+ return 'medium';
583
+ }
584
+ async buildUserMessage(prompt, attachments = []) {
585
+ const content = [{ type: 'text', text: prompt }];
586
+ for (const attachment of attachments ?? []) {
587
+ const mimeType = await this.resolveMimeType(attachment.mimeType, attachment.filePath, attachment.url);
588
+ if (!mimeType || !mimeType.startsWith('image/')) {
589
+ throw new Error('Only image attachments are currently supported for claude-agent-sdk');
590
+ }
591
+ const data = await this.readAttachmentAsBase64(attachment);
592
+ content.push({
593
+ type: 'image',
594
+ source: {
595
+ type: 'base64',
596
+ media_type: mimeType,
597
+ data,
598
+ },
599
+ });
600
+ }
601
+ return {
602
+ type: 'user',
603
+ message: {
604
+ role: 'user',
605
+ content: attachments && attachments.length > 0 ? content : prompt,
606
+ },
607
+ parent_tool_use_id: null,
608
+ };
609
+ }
610
+ isImageAttachment(mimeType, filePath, url) {
611
+ if (mimeType?.startsWith('image/')) {
612
+ return true;
613
+ }
614
+ const source = filePath ?? url;
615
+ if (!source) {
616
+ return false;
617
+ }
618
+ return /\.(png|jpe?g|gif|webp|bmp)$/i.test(source);
619
+ }
620
+ async readAttachmentAsBase64(attachment) {
621
+ if (attachment.base64) {
622
+ return attachment.base64;
623
+ }
624
+ if (attachment.filePath) {
625
+ const data = await fs.readFile(attachment.filePath);
626
+ return data.toString('base64');
627
+ }
628
+ if (attachment.url) {
629
+ const response = await fetch(attachment.url);
630
+ if (!response.ok) {
631
+ throw new Error(`Failed to fetch attachment from ${attachment.url}: ${response.status}`);
632
+ }
633
+ const data = Buffer.from(await response.arrayBuffer());
634
+ return data.toString('base64');
635
+ }
636
+ throw new Error('Attachment must provide base64, filePath, or url');
637
+ }
638
+ async resolveMimeType(mimeType, filePath, url) {
639
+ if (mimeType) {
640
+ return mimeType;
641
+ }
642
+ const source = (filePath ?? url ?? '').toLowerCase();
643
+ if (source.endsWith('.png'))
644
+ return 'image/png';
645
+ if (source.endsWith('.jpg') || source.endsWith('.jpeg'))
646
+ return 'image/jpeg';
647
+ if (source.endsWith('.gif'))
648
+ return 'image/gif';
649
+ if (source.endsWith('.webp'))
650
+ return 'image/webp';
651
+ if (source.endsWith('.bmp'))
652
+ return 'image/bmp';
653
+ return undefined;
654
+ }
655
+ translateSdkMessage(args) {
656
+ const { runId, options, message, modelId, toolsByIndex, toolsById, } = args;
657
+ let textAccumulated = args.textAccumulated;
658
+ let thinkingAccumulated = args.thinkingAccumulated;
659
+ let turnIndex = args.turnIndex;
660
+ let sessionId = args.sessionId;
661
+ const events = [];
662
+ if (message.type === 'system' && message.subtype === 'init') {
663
+ sessionId = message.session_id;
664
+ events.push({
665
+ ...this.createBaseEvent('session_start', runId),
666
+ type: 'session_start',
667
+ sessionId,
668
+ resumed: Boolean(options.sessionId),
669
+ forkedFrom: options.forkSessionId,
670
+ });
671
+ return { events, textAccumulated, thinkingAccumulated, turnIndex, sessionId };
672
+ }
673
+ if (message.type === 'system' && message.subtype === 'session_state_changed') {
674
+ if (message.state === 'running') {
675
+ textAccumulated = '';
676
+ thinkingAccumulated = '';
677
+ turnIndex += 1;
678
+ events.push({
679
+ ...this.createBaseEvent('turn_start', runId),
680
+ type: 'turn_start',
681
+ turnIndex,
682
+ });
683
+ }
684
+ else if (message.state === 'idle' && turnIndex >= 0) {
685
+ if (thinkingAccumulated.length > 0) {
686
+ events.push({
687
+ ...this.createBaseEvent('thinking_stop', runId),
688
+ type: 'thinking_stop',
689
+ thinking: thinkingAccumulated,
690
+ });
691
+ thinkingAccumulated = '';
692
+ }
693
+ if (textAccumulated.length > 0) {
694
+ events.push(this.createMessageStopEvent(runId, textAccumulated));
695
+ textAccumulated = '';
696
+ }
697
+ events.push({
698
+ ...this.createBaseEvent('turn_end', runId),
699
+ type: 'turn_end',
700
+ turnIndex,
701
+ });
702
+ }
703
+ return { events, textAccumulated, thinkingAccumulated, turnIndex, sessionId };
704
+ }
705
+ if (message.type === 'stream_event') {
706
+ const event = message.event;
707
+ if (event.type === 'message_start') {
708
+ events.push({
709
+ ...this.createBaseEvent('message_start', runId),
710
+ type: 'message_start',
711
+ });
712
+ }
713
+ if (event.type === 'content_block_start' && event.content_block) {
714
+ const contentBlock = event.content_block;
715
+ if (contentBlock.type === 'tool_use' || contentBlock.type === 'server_tool_use' || contentBlock.type === 'mcp_tool_use') {
716
+ const id = String(contentBlock.id ?? `tool-${event.index}`);
717
+ const state = {
718
+ id,
719
+ name: String(contentBlock.name ?? 'tool'),
720
+ rawInput: contentBlock.input ? JSON.stringify(contentBlock.input) : '',
721
+ };
722
+ toolsByIndex.set(Number(event.index ?? 0), state);
723
+ toolsById.set(id, state);
724
+ events.push(this.createToolCallStartEvent(runId, id, state.name, state.rawInput));
725
+ }
726
+ else if (contentBlock.type === 'thinking') {
727
+ events.push({
728
+ ...this.createBaseEvent('thinking_start', runId),
729
+ type: 'thinking_start',
730
+ });
731
+ }
732
+ }
733
+ if (event.type === 'content_block_delta' && event.delta) {
734
+ const delta = event.delta;
735
+ if (delta.type === 'text_delta' && typeof delta.text === 'string') {
736
+ textAccumulated += delta.text;
737
+ events.push(this.createTextDeltaEvent(runId, delta.text, textAccumulated));
738
+ }
739
+ else if (delta.type === 'thinking_delta' && typeof delta.thinking === 'string') {
740
+ thinkingAccumulated += delta.thinking;
741
+ events.push({
742
+ ...this.createBaseEvent('thinking_delta', runId),
743
+ type: 'thinking_delta',
744
+ delta: delta.thinking,
745
+ accumulated: thinkingAccumulated,
746
+ });
747
+ }
748
+ else if (delta.type === 'input_json_delta' && typeof delta.partial_json === 'string') {
749
+ const state = toolsByIndex.get(Number(event.index ?? 0));
750
+ if (state) {
751
+ state.rawInput += delta.partial_json;
752
+ events.push({
753
+ ...this.createBaseEvent('tool_input_delta', runId),
754
+ type: 'tool_input_delta',
755
+ toolCallId: state.id,
756
+ delta: delta.partial_json,
757
+ inputAccumulated: state.rawInput,
758
+ });
759
+ }
760
+ }
761
+ }
762
+ if (event.type === 'content_block_stop') {
763
+ const state = toolsByIndex.get(Number(event.index ?? 0));
764
+ if (state) {
765
+ events.push({
766
+ ...this.createBaseEvent('tool_call_ready', runId),
767
+ type: 'tool_call_ready',
768
+ toolCallId: state.id,
769
+ toolName: state.name,
770
+ input: this.parseToolInput(state.rawInput),
771
+ });
772
+ }
773
+ }
774
+ return { events, textAccumulated, thinkingAccumulated, turnIndex, sessionId };
775
+ }
776
+ if (message.type === 'user' && message.parent_tool_use_id && message.tool_use_result !== undefined) {
777
+ const toolState = toolsById.get(message.parent_tool_use_id);
778
+ events.push(this.createToolResultEvent(runId, message.parent_tool_use_id, toolState?.name ?? 'tool', message.tool_use_result));
779
+ return { events, textAccumulated, thinkingAccumulated, turnIndex, sessionId };
780
+ }
781
+ if (message.type === 'result') {
782
+ const cost = this.extractCostFromResult(message, modelId);
783
+ if (cost) {
784
+ events.push(this.createCostEvent(runId, cost));
785
+ }
786
+ if (message.is_error) {
787
+ events.push(this.createErrorEvent(runId, 'INTERNAL', message.subtype === 'success' ? 'Claude Agent SDK returned an error result' : message.errors.join('; ') || message.subtype, false));
788
+ }
789
+ events.push(this.createMessageStopEvent(runId, message.subtype === 'success' ? message.result : textAccumulated));
790
+ return { events, textAccumulated, thinkingAccumulated, turnIndex, sessionId };
791
+ }
792
+ if (message.type === 'system' && message.subtype === 'notification') {
793
+ events.push({
794
+ ...this.createBaseEvent('debug', runId),
795
+ type: 'debug',
796
+ level: 'info',
797
+ message: message.text,
798
+ });
799
+ return { events, textAccumulated, thinkingAccumulated, turnIndex, sessionId };
800
+ }
801
+ if (message.type === 'system' && message.subtype === 'local_command_output') {
802
+ events.push(this.createTextDeltaEvent(runId, `${message.content}\n`, textAccumulated + `${message.content}\n`));
803
+ textAccumulated += `${message.content}\n`;
804
+ return { events, textAccumulated, thinkingAccumulated, turnIndex, sessionId };
805
+ }
806
+ return { events, textAccumulated, thinkingAccumulated, turnIndex, sessionId };
807
+ }
808
+ parseToolInput(rawInput) {
809
+ if (!rawInput) {
810
+ return {};
811
+ }
812
+ try {
813
+ return JSON.parse(rawInput);
814
+ }
815
+ catch {
816
+ return rawInput;
817
+ }
818
+ }
819
+ extractCostFromResult(result, modelId) {
820
+ const model = this.models.find((candidate) => candidate.modelId === modelId);
821
+ if (!model) {
822
+ return null;
823
+ }
824
+ const usage = result.usage;
825
+ const inputTokens = typeof usage?.input_tokens === 'number' ? usage.input_tokens : 0;
826
+ const outputTokens = typeof usage?.output_tokens === 'number' ? usage.output_tokens : 0;
827
+ const cachedTokens = typeof usage?.cache_read_input_tokens === 'number' ? usage.cache_read_input_tokens : 0;
828
+ const thinkingTokens = typeof usage?.server_tool_use === 'number' ? usage.server_tool_use : 0;
829
+ return {
830
+ totalUsd: result.total_cost_usd ?? ((inputTokens / 1_000_000) * (model.inputPricePerMillion ?? 0) +
831
+ (outputTokens / 1_000_000) * (model.outputPricePerMillion ?? 0)),
832
+ inputTokens,
833
+ outputTokens,
834
+ cachedTokens,
835
+ thinkingTokens,
836
+ };
837
+ }
838
+ }
839
+ //# sourceMappingURL=claude-agent-sdk-adapter.js.map