ak-claude 0.0.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.
package/README.md ADDED
@@ -0,0 +1,530 @@
1
+ # ak-claude
2
+
3
+ **Modular, type-safe wrapper for Anthropic's Claude AI.** Eight class exports for different interaction patterns — JSON transformation, chat, stateless messages, tool-using agents, code-writing agents, document Q&A, and autonomous agent queries — all sharing a common base.
4
+
5
+ ```sh
6
+ npm install ak-claude
7
+ ```
8
+
9
+ Requires Node.js 18+ and [@anthropic-ai/sdk](https://www.npmjs.com/package/@anthropic-ai/sdk).
10
+
11
+ ---
12
+
13
+ ## Quick Start
14
+
15
+ ```javascript
16
+ import { Transformer, Chat, Message, ToolAgent, CodeAgent, RagAgent, AgentQuery } from 'ak-claude';
17
+
18
+ // Vertex AI auth (recommended for GCP deployments — uses Application Default Credentials)
19
+ new Chat({ vertexai: true });
20
+
21
+ // Or direct API key auth
22
+ // export ANTHROPIC_API_KEY=your-key
23
+ new Chat({ apiKey: 'your-key' });
24
+ ```
25
+
26
+ ---
27
+
28
+ ## Classes
29
+
30
+ ### Transformer — JSON Transformation
31
+
32
+ Transform structured data using few-shot examples with validation and retry.
33
+
34
+ ```javascript
35
+ const transformer = new Transformer({
36
+ modelName: 'claude-sonnet-4-6',
37
+ sourceKey: 'INPUT',
38
+ targetKey: 'OUTPUT'
39
+ });
40
+
41
+ await transformer.init();
42
+ await transformer.seed([
43
+ {
44
+ INPUT: { name: 'Alice' },
45
+ OUTPUT: { name: 'Alice', role: 'engineer', emoji: '👩‍💻' }
46
+ }
47
+ ]);
48
+
49
+ const result = await transformer.send({ name: 'Bob' });
50
+ // → { name: 'Bob', role: '...', emoji: '...' }
51
+ ```
52
+
53
+ **Validation & self-healing:**
54
+
55
+ ```javascript
56
+ const result = await transformer.send({ name: 'Bob' }, {}, async (output) => {
57
+ if (!output.role) throw new Error('Missing role field');
58
+ return output;
59
+ });
60
+ ```
61
+
62
+ ### Chat — Multi-Turn Conversation
63
+
64
+ ```javascript
65
+ const chat = new Chat({
66
+ systemPrompt: 'You are a helpful assistant.'
67
+ });
68
+
69
+ const r1 = await chat.send('My name is Alice.');
70
+ const r2 = await chat.send('What is my name?');
71
+ // r2.text → "Alice"
72
+ ```
73
+
74
+ ### Message — Stateless One-Off
75
+
76
+ Each call is independent — no history maintained.
77
+
78
+ ```javascript
79
+ const msg = new Message({
80
+ systemPrompt: 'Extract entities as JSON.',
81
+ responseSchema: {
82
+ type: 'object',
83
+ properties: {
84
+ entities: {
85
+ type: 'array',
86
+ items: {
87
+ type: 'object',
88
+ properties: {
89
+ name: { type: 'string' },
90
+ type: { type: 'string' }
91
+ },
92
+ required: ['name', 'type']
93
+ }
94
+ }
95
+ },
96
+ required: ['entities']
97
+ }
98
+ });
99
+
100
+ const result = await msg.send('Alice works at Acme Corp in New York.');
101
+ // result.data → { entities: [{ name: 'Alice', type: 'person' }, ...] }
102
+ ```
103
+
104
+ When `responseSchema` is provided, the API guarantees valid JSON matching the schema via native structured output (`output_config`). For a lighter alternative without schema guarantees, use `responseFormat: 'json'` instead.
105
+
106
+ ### ToolAgent — Agent with User-Provided Tools
107
+
108
+ Provide tool declarations and an executor function. The agent manages the tool-use loop automatically.
109
+
110
+ ```javascript
111
+ const agent = new ToolAgent({
112
+ systemPrompt: 'You are a research assistant.',
113
+ tools: [
114
+ {
115
+ name: 'http_get',
116
+ description: 'Fetch a URL',
117
+ input_schema: {
118
+ type: 'object',
119
+ properties: { url: { type: 'string' } },
120
+ required: ['url']
121
+ }
122
+ }
123
+ ],
124
+ toolExecutor: async (toolName, args) => {
125
+ if (toolName === 'http_get') {
126
+ const res = await fetch(args.url);
127
+ return { status: res.status, body: await res.text() };
128
+ }
129
+ },
130
+ onBeforeExecution: async (toolName, args) => {
131
+ console.log(`About to call ${toolName}`);
132
+ return true; // return false to deny
133
+ }
134
+ });
135
+
136
+ const result = await agent.chat('Fetch https://api.example.com/data');
137
+ console.log(result.text); // Agent's summary
138
+ console.log(result.toolCalls); // [{ name, args, result }]
139
+ ```
140
+
141
+ **Streaming:**
142
+
143
+ ```javascript
144
+ for await (const event of agent.stream('Fetch the data')) {
145
+ if (event.type === 'text') process.stdout.write(event.text);
146
+ if (event.type === 'tool_call') console.log(`Calling ${event.toolName}...`);
147
+ if (event.type === 'tool_result') console.log(`Result:`, event.result);
148
+ if (event.type === 'done') console.log('Done!');
149
+ }
150
+ ```
151
+
152
+ ### CodeAgent — Agent That Writes and Executes Code
153
+
154
+ Instead of calling tools one by one, the model writes JavaScript that can do everything — read files, write files, run commands — in a single script.
155
+
156
+ ```javascript
157
+ const agent = new CodeAgent({
158
+ workingDirectory: '/path/to/my/project',
159
+ onCodeExecution: (code, output) => {
160
+ console.log('Ran:', code.slice(0, 100));
161
+ console.log('Output:', output.stdout);
162
+ },
163
+ onBeforeExecution: async (code) => {
164
+ // Review code before execution
165
+ console.log('About to run:', code);
166
+ return true; // return false to deny
167
+ }
168
+ });
169
+
170
+ const result = await agent.chat('Find all TODO comments in the codebase');
171
+ console.log(result.text); // Agent's summary
172
+ console.log(result.codeExecutions); // [{ code, output, stderr, exitCode }]
173
+ ```
174
+
175
+ **How it works:**
176
+ 1. On `init()`, gathers codebase context (file tree + key files like package.json)
177
+ 2. Injects context into the system prompt so the model understands the project
178
+ 3. Model writes JavaScript using the `execute_code` tool
179
+ 4. Code runs in a Node.js child process that inherits `process.env`
180
+ 5. Output (stdout/stderr) feeds back to the model
181
+ 6. Model decides if more work is needed
182
+
183
+ **Streaming:**
184
+
185
+ ```javascript
186
+ for await (const event of agent.stream('Refactor the auth module')) {
187
+ if (event.type === 'text') process.stdout.write(event.text);
188
+ if (event.type === 'code') console.log('\n[Running code...]');
189
+ if (event.type === 'output') console.log('[Output]:', event.stdout);
190
+ if (event.type === 'done') console.log('\nDone!');
191
+ }
192
+ ```
193
+
194
+ ### RagAgent — Document & Data Q&A
195
+
196
+ Ground responses in user-provided documents and data. Supports text files, in-memory data, and media files (images, PDFs) via base64 encoding. Optionally enable Claude's built-in citations feature for source attribution.
197
+
198
+ ```javascript
199
+ const rag = new RagAgent({
200
+ localFiles: ['./docs/api.md', './config.yaml'],
201
+ localData: [
202
+ { name: 'users', data: [{ id: 1, name: 'Alice' }] }
203
+ ],
204
+ mediaFiles: ['./diagram.png', './report.pdf'],
205
+ enableCitations: true
206
+ });
207
+
208
+ const result = await rag.chat('What does the API doc say about auth?');
209
+ console.log(result.text);
210
+ console.log(result.citations); // [{ type, cited_text, document_title, ... }]
211
+ ```
212
+
213
+ **Context input types:**
214
+ - **`localFiles`** — read from disk as UTF-8 text (md, json, csv, yaml, txt, js, py, etc.)
215
+ - **`localData`** — in-memory objects serialized as JSON: `{ name: string, data: any }[]`
216
+ - **`mediaFiles`** — images (png, jpg, gif, webp) and PDFs encoded as base64 content blocks
217
+
218
+ **Dynamic context:**
219
+
220
+ ```javascript
221
+ await rag.addLocalFiles(['./new-doc.md']);
222
+ await rag.addMediaFiles(['./chart.png']);
223
+ await rag.addLocalData([{ name: 'metrics', data: { dau: 50000 } }]);
224
+ ```
225
+
226
+ ### AgentQuery — Autonomous Agent via Claude Agent SDK
227
+
228
+ Wraps `@anthropic-ai/claude-agent-sdk` to launch a full autonomous Claude agent with built-in tools (Read, Write, Edit, Bash, Glob, Grep, etc.). Unlike the other classes which use the Messages API directly, AgentQuery launches a separate agent process.
229
+
230
+ ```javascript
231
+ import { AgentQuery } from 'ak-claude';
232
+
233
+ const agent = new AgentQuery({
234
+ cwd: '/path/to/project',
235
+ allowedTools: ['Read', 'Glob', 'Grep'],
236
+ maxTurns: 20,
237
+ maxBudgetUsd: 1.00
238
+ });
239
+
240
+ for await (const msg of agent.run('Find all TODO comments in the codebase')) {
241
+ if (msg.type === 'assistant') {
242
+ console.log(msg.message.content);
243
+ }
244
+ if (msg.type === 'result') {
245
+ console.log('Done:', msg.result);
246
+ console.log('Cost:', msg.total_cost_usd);
247
+ }
248
+ }
249
+ ```
250
+
251
+ **Resume a previous session:**
252
+
253
+ ```javascript
254
+ const sessionId = agent.lastSessionId;
255
+
256
+ for await (const msg of agent.resume(sessionId, 'Now fix those TODOs')) {
257
+ // ...
258
+ }
259
+ ```
260
+
261
+ Requires separate installation: `npm install @anthropic-ai/claude-agent-sdk`
262
+
263
+ ---
264
+
265
+ ## Stopping Agents
266
+
267
+ Both `ToolAgent` and `CodeAgent` support a `stop()` method to cancel execution mid-loop. This is useful for implementing user-facing cancel buttons or safety limits.
268
+
269
+ ```javascript
270
+ const agent = new CodeAgent({ workingDirectory: '.' });
271
+
272
+ // Stop from a callback
273
+ const agent = new ToolAgent({
274
+ tools: [...],
275
+ toolExecutor: myExecutor,
276
+ onBeforeExecution: async (toolName, args) => {
277
+ if (toolName === 'dangerous_tool') {
278
+ agent.stop(); // Stop the agent entirely
279
+ return false; // Deny this specific execution
280
+ }
281
+ return true;
282
+ }
283
+ });
284
+
285
+ // Stop externally (e.g., from a timeout or user action)
286
+ setTimeout(() => agent.stop(), 60_000);
287
+ const result = await agent.chat('Do some work');
288
+ ```
289
+
290
+ For `CodeAgent`, `stop()` also kills any currently running child process via SIGTERM.
291
+
292
+ ---
293
+
294
+ ## Shared Features
295
+
296
+ All classes extend `BaseClaude` and share these features (except `AgentQuery`, which wraps the Claude Agent SDK separately).
297
+
298
+ ### Authentication
299
+
300
+ ```javascript
301
+ // Vertex AI (GCP — uses Application Default Credentials, no API key needed)
302
+ new Chat({ vertexai: true });
303
+
304
+ // Vertex AI with explicit project/region
305
+ new Chat({ vertexai: true, vertexProjectId: 'my-project', vertexRegion: 'us-central1' });
306
+
307
+ // Direct API key
308
+ new Chat({ apiKey: 'your-key' }); // or ANTHROPIC_API_KEY / CLAUDE_API_KEY env var
309
+ ```
310
+
311
+ ### Token Estimation
312
+
313
+ Uses Claude's `countTokens` API for exact input token counts before sending.
314
+
315
+ ```javascript
316
+ const { inputTokens } = await instance.estimate({ some: 'payload' });
317
+ const cost = await instance.estimateCost({ some: 'payload' });
318
+ // → { inputTokens, model, pricing, estimatedInputCost, note }
319
+ ```
320
+
321
+ ### Usage Tracking
322
+
323
+ ```javascript
324
+ const usage = instance.getLastUsage();
325
+ // { promptTokens, responseTokens, totalTokens, cacheCreationTokens, cacheReadTokens,
326
+ // attempts, modelVersion, requestedModel, stopReason, timestamp }
327
+ ```
328
+
329
+ ### Few-Shot Seeding
330
+
331
+ ```javascript
332
+ await instance.seed([
333
+ { PROMPT: { x: 1 }, ANSWER: { y: 2 } }
334
+ ]);
335
+ ```
336
+
337
+ ### Extended Thinking
338
+
339
+ ```javascript
340
+ new Chat({
341
+ modelName: 'claude-sonnet-4-6',
342
+ thinking: { type: 'enabled', budget_tokens: 1024 }
343
+ });
344
+ ```
345
+
346
+ When thinking is enabled, `temperature` is forced to 1 and `top_p`/`top_k` are not sent (Anthropic API requirement).
347
+
348
+ ### Web Search
349
+
350
+ Ground responses in real-time web search results. Uses Claude's built-in server-managed web search tool.
351
+
352
+ ```javascript
353
+ const chat = new Chat({
354
+ enableWebSearch: true,
355
+ webSearchConfig: {
356
+ max_uses: 5,
357
+ allowed_domains: ['docs.anthropic.com'],
358
+ blocked_domains: ['example.com']
359
+ }
360
+ });
361
+
362
+ const result = await chat.send('What are the latest Claude model features?');
363
+ ```
364
+
365
+ The web search tool is automatically prepended to any existing tools (ToolAgent/CodeAgent tool declarations coexist).
366
+
367
+ ### Prompt Caching
368
+
369
+ Reduce costs by caching the system prompt. When enabled, the system prompt is sent with `cache_control: { type: 'ephemeral' }`, allowing Anthropic to cache it across requests.
370
+
371
+ ```javascript
372
+ const chat = new Chat({
373
+ systemPrompt: longSystemPrompt,
374
+ cacheSystemPrompt: true
375
+ });
376
+
377
+ const result = await chat.send('Hello!');
378
+ const usage = chat.getLastUsage();
379
+ console.log(usage.cacheCreationTokens); // Tokens used to create cache
380
+ console.log(usage.cacheReadTokens); // Tokens read from cache (cheaper)
381
+ ```
382
+
383
+ ### Rate Limit Handling (429)
384
+
385
+ The Anthropic SDK handles 429 retries natively via its built-in retry mechanism. Configure the max retry count at construction:
386
+
387
+ ```javascript
388
+ // Defaults: 5 retries with SDK-managed exponential backoff
389
+ const chat = new Chat({ systemPrompt: 'Hello' });
390
+
391
+ // Customize
392
+ const transformer = new Transformer({
393
+ maxRetries: 10 // more retries for high-throughput pipelines
394
+ });
395
+
396
+ // Disable entirely
397
+ const msg = new Message({ maxRetries: 0 });
398
+ ```
399
+
400
+ ---
401
+
402
+ ## Constructor Options
403
+
404
+ All classes (except AgentQuery) accept `BaseClaudeOptions`:
405
+
406
+ | Option | Type | Default | Description |
407
+ |--------|------|---------|-------------|
408
+ | `modelName` | string | `'claude-sonnet-4-6'` | Claude model to use |
409
+ | `systemPrompt` | string | varies by class | System prompt |
410
+ | `apiKey` | string | env var | Anthropic API key (not needed with `vertexai`) |
411
+ | `vertexai` | boolean | `false` | Use Vertex AI auth (Application Default Credentials) |
412
+ | `vertexProjectId` | string | `GOOGLE_CLOUD_PROJECT` | GCP project ID (Vertex AI only) |
413
+ | `vertexRegion` | string | `'us-east5'` | GCP region (Vertex AI only) |
414
+ | `maxTokens` | number | `8192` | Max tokens in response |
415
+ | `temperature` | number | `0.7` | Temperature (not used with thinking) |
416
+ | `topP` | number | `0.95` | Top-P (not used with thinking) |
417
+ | `topK` | number | — | Top-K (optional) |
418
+ | `thinking` | object | — | Extended thinking: `{ type: 'enabled', budget_tokens: N }` |
419
+ | `cacheSystemPrompt` | boolean | `false` | Enable prompt caching on system prompt |
420
+ | `enableWebSearch` | boolean | `false` | Enable Claude's web search tool |
421
+ | `webSearchConfig` | object | — | Web search config (`max_uses`, `allowed_domains`, `blocked_domains`) |
422
+ | `maxRetries` | number | `5` | Max SDK-level retry attempts for 429 errors |
423
+ | `healthCheck` | boolean | `false` | Run API connectivity check during `init()` |
424
+ | `logLevel` | string | based on NODE_ENV | `'trace'`\|`'debug'`\|`'info'`\|`'warn'`\|`'error'`\|`'none'` |
425
+
426
+ ### Transformer-Specific
427
+
428
+ | Option | Type | Default | Description |
429
+ |--------|------|---------|-------------|
430
+ | `sourceKey`/`promptKey` | string | `'PROMPT'` | Key for input in examples |
431
+ | `targetKey`/`answerKey` | string | `'ANSWER'` | Key for output in examples |
432
+ | `contextKey` | string | `'CONTEXT'` | Key for context in examples |
433
+ | `maxRetries` | number | `3` | Retry attempts for validation |
434
+ | `retryDelay` | number | `1000` | Initial retry delay (ms) |
435
+ | `onlyJSON` | boolean | `true` | Enforce JSON-only responses |
436
+ | `asyncValidator` | function | — | Global async validator |
437
+ | `examplesFile` | string | — | Path to JSON file with examples |
438
+ | `exampleData` | array | — | Inline example data |
439
+
440
+ ### Message-Specific
441
+
442
+ | Option | Type | Default | Description |
443
+ |--------|------|---------|-------------|
444
+ | `responseSchema` | object | — | JSON Schema for native structured output |
445
+ | `responseFormat` | string | — | `'json'` for system-prompt-based JSON mode |
446
+
447
+ ### ToolAgent-Specific
448
+
449
+ | Option | Type | Default | Description |
450
+ |--------|------|---------|-------------|
451
+ | `tools` | array | — | Tool declarations (`{ name, description, input_schema }`) |
452
+ | `toolExecutor` | function | — | `async (toolName, args) => result` |
453
+ | `maxToolRounds` | number | `10` | Max tool-use loop iterations |
454
+ | `onToolCall` | function | — | Notification callback when tool is called |
455
+ | `onBeforeExecution` | function | — | `async (toolName, args) => boolean` — gate execution |
456
+ | `toolChoice` | object | — | Tool choice config (`auto`, `any`, `tool`, `none`) |
457
+ | `disableParallelToolUse` | boolean | `false` | Force sequential tool calls |
458
+
459
+ ### CodeAgent-Specific
460
+
461
+ | Option | Type | Default | Description |
462
+ |--------|------|---------|-------------|
463
+ | `workingDirectory` | string | `process.cwd()` | Directory for code execution |
464
+ | `maxRounds` | number | `10` | Max code execution loop iterations |
465
+ | `timeout` | number | `30000` | Per-execution timeout (ms) |
466
+ | `onBeforeExecution` | function | — | `async (code) => boolean` — gate execution |
467
+ | `onCodeExecution` | function | — | Notification after execution |
468
+ | `importantFiles` | array | — | File paths to include in system prompt context |
469
+ | `writeDir` | string | `'{cwd}/tmp'` | Directory for writing script files |
470
+ | `keepArtifacts` | boolean | `false` | Keep script files on disk after execution |
471
+ | `comments` | boolean | `false` | Instruct model to write JSDoc comments |
472
+ | `maxRetries` | number | `3` | Max consecutive failures before stopping |
473
+
474
+ ### RagAgent-Specific
475
+
476
+ | Option | Type | Default | Description |
477
+ |--------|------|---------|-------------|
478
+ | `localFiles` | array | — | Paths to text files read from disk |
479
+ | `localData` | array | — | In-memory data: `{ name, data }[]` |
480
+ | `mediaFiles` | array | — | Paths to images/PDFs (base64 encoded) |
481
+ | `enableCitations` | boolean | `false` | Enable Claude's built-in citations |
482
+
483
+ ### AgentQuery-Specific
484
+
485
+ | Option | Type | Default | Description |
486
+ |--------|------|---------|-------------|
487
+ | `model` | string | `'claude-sonnet-4-6'` | Model to use |
488
+ | `allowedTools` | array | — | Allowed tools (e.g., `['Read', 'Glob', 'Grep']`) |
489
+ | `disallowedTools` | array | — | Disallowed tools |
490
+ | `cwd` | string | `process.cwd()` | Working directory |
491
+ | `maxTurns` | number | — | Max agentic turns |
492
+ | `maxBudgetUsd` | number | — | Maximum budget in USD |
493
+ | `systemPrompt` | string | — | System prompt |
494
+ | `permissionMode` | string | — | Permission mode |
495
+ | `mcpServers` | object | — | MCP server configuration |
496
+ | `hooks` | object | — | Lifecycle hooks |
497
+
498
+ ---
499
+
500
+ ## Exports
501
+
502
+ ```javascript
503
+ // Named exports
504
+ import { Transformer, Chat, Message, ToolAgent, CodeAgent, RagAgent, AgentQuery, BaseClaude, log } from 'ak-claude';
505
+ import { extractJSON, attemptJSONRecovery } from 'ak-claude';
506
+
507
+ // Default export (namespace)
508
+ import AI from 'ak-claude';
509
+ new AI.Transformer({ ... });
510
+ new AI.AgentQuery({ ... });
511
+
512
+ // CommonJS
513
+ const { Transformer, Chat, AgentQuery } = require('ak-claude');
514
+ ```
515
+
516
+ ---
517
+
518
+ ## Testing
519
+
520
+ ```sh
521
+ npm test
522
+ ```
523
+
524
+ All tests use real Anthropic API calls (no mocks). Rate limiting (429 errors) can cause intermittent failures.
525
+
526
+ ---
527
+
528
+ ## Migration from ak-gemini
529
+
530
+ See [MIGRATION.md](./MIGRATION.md) for a detailed guide on migrating from ak-gemini to ak-claude.
package/agent-query.js ADDED
@@ -0,0 +1,139 @@
1
+ /**
2
+ * @fileoverview AgentQuery class — wraps @anthropic-ai/claude-agent-sdk's query().
3
+ *
4
+ * Provides a simplified, declarative interface over the Claude Agent SDK.
5
+ * This class does NOT extend BaseClaude — it wraps a completely different SDK
6
+ * with its own auth, session, and execution model.
7
+ *
8
+ * The Agent SDK launches an autonomous Claude agent with built-in tools
9
+ * (Read, Write, Edit, Bash, Glob, Grep, etc.) that can operate on files
10
+ * and codebases directly.
11
+ */
12
+
13
+ import log from './logger.js';
14
+
15
+ /**
16
+ * @typedef {import('./types').AgentQueryOptions} AgentQueryOptions
17
+ * @typedef {import('./types').AgentQueryRunOptions} AgentQueryRunOptions
18
+ */
19
+
20
+ /**
21
+ * Wraps the Claude Agent SDK's query() function for autonomous agent tasks.
22
+ *
23
+ * Unlike the other classes which use the Messages API directly, AgentQuery
24
+ * launches a full Claude Code agent process with built-in tools for
25
+ * file operations, shell commands, and code search.
26
+ *
27
+ * @example
28
+ * ```javascript
29
+ * import { AgentQuery } from 'ak-claude';
30
+ *
31
+ * const agent = new AgentQuery({
32
+ * cwd: '/path/to/project',
33
+ * allowedTools: ['Read', 'Glob', 'Grep'],
34
+ * });
35
+ *
36
+ * for await (const msg of agent.run('Find all TODO comments in the codebase')) {
37
+ * if (msg.type === 'assistant') {
38
+ * console.log(msg.message.content);
39
+ * }
40
+ * if (msg.type === 'result') {
41
+ * console.log('Done:', msg.result);
42
+ * console.log('Cost:', msg.total_cost_usd);
43
+ * }
44
+ * }
45
+ * ```
46
+ */
47
+ class AgentQuery {
48
+ /**
49
+ * @param {AgentQueryOptions} [options={}]
50
+ */
51
+ constructor(options = {}) {
52
+ this.model = options.model || 'claude-sonnet-4-6';
53
+ this.allowedTools = options.allowedTools || undefined;
54
+ this.disallowedTools = options.disallowedTools || undefined;
55
+ this.cwd = options.cwd || process.cwd();
56
+ this.maxTurns = options.maxTurns || undefined;
57
+ this.maxBudgetUsd = options.maxBudgetUsd || undefined;
58
+ this.systemPrompt = options.systemPrompt || undefined;
59
+ this.permissionMode = options.permissionMode || undefined;
60
+ this.mcpServers = options.mcpServers || undefined;
61
+ this.hooks = options.hooks || undefined;
62
+
63
+ this._lastSessionId = null;
64
+ this._queryFn = null;
65
+
66
+ log.debug(`AgentQuery created with model: ${this.model}`);
67
+ }
68
+
69
+ /**
70
+ * Lazily imports the query function from claude-agent-sdk.
71
+ * @private
72
+ */
73
+ async _getQueryFn() {
74
+ if (this._queryFn) return this._queryFn;
75
+ try {
76
+ const sdk = await import(/** @type {any} */ ('@anthropic-ai/claude-agent-sdk'));
77
+ this._queryFn = sdk.query;
78
+ return this._queryFn;
79
+ } catch (e) {
80
+ throw new Error(
81
+ `Failed to import @anthropic-ai/claude-agent-sdk. ` +
82
+ `Install it with: npm install @anthropic-ai/claude-agent-sdk\n` +
83
+ `Error: ${e.message}`
84
+ );
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Run an autonomous agent query. Yields messages as they arrive.
90
+ *
91
+ * @param {string} prompt - The task for the agent
92
+ * @param {AgentQueryRunOptions} [opts={}] - Per-run overrides
93
+ * @yields {Object} Messages from the agent (system, assistant, tool_progress, result)
94
+ */
95
+ async *run(prompt, opts = {}) {
96
+ const queryFn = await this._getQueryFn();
97
+
98
+ const options = {
99
+ model: opts.model || this.model,
100
+ cwd: opts.cwd || this.cwd,
101
+ ...(opts.allowedTools || this.allowedTools ? { allowedTools: opts.allowedTools || this.allowedTools } : {}),
102
+ ...(opts.disallowedTools || this.disallowedTools ? { disallowedTools: opts.disallowedTools || this.disallowedTools } : {}),
103
+ ...(opts.maxTurns || this.maxTurns ? { maxTurns: opts.maxTurns || this.maxTurns } : {}),
104
+ ...(opts.maxBudgetUsd || this.maxBudgetUsd ? { maxBudgetUsd: opts.maxBudgetUsd || this.maxBudgetUsd } : {}),
105
+ ...(opts.systemPrompt || this.systemPrompt ? { systemPrompt: opts.systemPrompt || this.systemPrompt } : {}),
106
+ ...(opts.permissionMode || this.permissionMode ? { permissionMode: opts.permissionMode || this.permissionMode } : {}),
107
+ ...(opts.mcpServers || this.mcpServers ? { mcpServers: opts.mcpServers || this.mcpServers } : {}),
108
+ ...(opts.hooks || this.hooks ? { hooks: opts.hooks || this.hooks } : {}),
109
+ ...(opts.sessionId ? { resume: opts.sessionId } : {}),
110
+ };
111
+
112
+ for await (const message of queryFn({ prompt, options })) {
113
+ this._lastSessionId = message.session_id || message.sessionId || this._lastSessionId;
114
+ yield message;
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Resume a previous agent session with a new prompt.
120
+ *
121
+ * @param {string} sessionId - The session ID to resume
122
+ * @param {string} prompt - The follow-up prompt
123
+ * @param {AgentQueryRunOptions} [opts={}]
124
+ * @yields {Object} Messages from the agent
125
+ */
126
+ async *resume(sessionId, prompt, opts = {}) {
127
+ yield* this.run(prompt, { ...opts, sessionId });
128
+ }
129
+
130
+ /**
131
+ * The session ID from the last run.
132
+ * @returns {string|null}
133
+ */
134
+ get lastSessionId() {
135
+ return this._lastSessionId;
136
+ }
137
+ }
138
+
139
+ export default AgentQuery;