@mikode13/harness 3.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +109 -9
  2. package/dist/agent/domain/agent.d.ts +23 -3
  3. package/dist/agent/domain/agent.js +2 -1
  4. package/dist/agent/domain/agent.js.map +1 -1
  5. package/dist/agent/domain/approval.d.ts +44 -0
  6. package/dist/agent/domain/approval.js +23 -0
  7. package/dist/agent/domain/approval.js.map +1 -0
  8. package/dist/engines/claude/infrastructure/model/claudeAgent.d.ts +2 -2
  9. package/dist/engines/claude/infrastructure/model/claudeAgent.js +4 -3
  10. package/dist/engines/claude/infrastructure/model/claudeAgent.js.map +1 -1
  11. package/dist/engines/codex/infrastructure/model/codexAgent.d.ts +2 -2
  12. package/dist/engines/codex/infrastructure/model/codexAgent.js +4 -4
  13. package/dist/engines/codex/infrastructure/model/codexAgent.js.map +1 -1
  14. package/dist/engines/domain/model/llmAgent.d.ts +15 -5
  15. package/dist/engines/domain/model/llmAgent.js +109 -26
  16. package/dist/engines/domain/model/llmAgent.js.map +1 -1
  17. package/dist/factory/infrastructure/agentFactory.js +1 -1
  18. package/dist/factory/infrastructure/agentFactory.js.map +1 -1
  19. package/dist/factory/infrastructure/agentLLMFactory.d.ts +14 -7
  20. package/dist/factory/infrastructure/agentLLMFactory.js +11 -9
  21. package/dist/factory/infrastructure/agentLLMFactory.js.map +1 -1
  22. package/dist/index.d.ts +10 -2
  23. package/dist/index.js +6 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/llm/domain/errors.d.ts +1 -1
  26. package/dist/llm/domain/errors.js +1 -1
  27. package/dist/llm/domain/errors.js.map +1 -1
  28. package/dist/llm/infrastructure/claudeLLMClient.js +2 -2
  29. package/dist/llm/infrastructure/claudeLLMClient.js.map +1 -1
  30. package/dist/llm/infrastructure/openAILLMClient.js +2 -2
  31. package/dist/llm/infrastructure/openAILLMClient.js.map +1 -1
  32. package/dist/orchestration/domain/model/instructedAgent.d.ts +2 -2
  33. package/dist/orchestration/domain/model/instructedAgent.js +2 -2
  34. package/dist/orchestration/domain/model/instructedAgent.js.map +1 -1
  35. package/dist/orchestration/domain/model/orchestratorAgent.d.ts +2 -2
  36. package/dist/orchestration/domain/model/orchestratorAgent.js +10 -10
  37. package/dist/orchestration/domain/model/orchestratorAgent.js.map +1 -1
  38. package/dist/retry/domain/model/retryingAgent.d.ts +2 -2
  39. package/dist/retry/domain/model/retryingAgent.js +4 -4
  40. package/dist/retry/domain/model/retryingAgent.js.map +1 -1
  41. package/dist/shared/domain/errors.js.map +1 -0
  42. package/dist/shared/domain/providerFailure.js.map +1 -0
  43. package/dist/tools/domain/tool.d.ts +6 -0
  44. package/dist/tools/domain/tool.js.map +1 -1
  45. package/dist/tools/infrastructure/createWorkspace.d.ts +2 -1
  46. package/dist/tools/infrastructure/createWorkspace.js +4 -3
  47. package/dist/tools/infrastructure/createWorkspace.js.map +1 -1
  48. package/dist/tools/infrastructure/defineTool.d.ts +7 -1
  49. package/dist/tools/infrastructure/defineTool.js +12 -2
  50. package/dist/tools/infrastructure/defineTool.js.map +1 -1
  51. package/dist/tools/infrastructure/workspaceTools.js +3 -0
  52. package/dist/tools/infrastructure/workspaceTools.js.map +1 -1
  53. package/package.json +7 -4
  54. package/dist/agent/domain/errors.js.map +0 -1
  55. package/dist/agent/domain/providerFailure.js.map +0 -1
  56. /package/dist/{agent → shared}/domain/errors.d.ts +0 -0
  57. /package/dist/{agent → shared}/domain/errors.js +0 -0
  58. /package/dist/{agent → shared}/domain/providerFailure.d.ts +0 -0
  59. /package/dist/{agent → shared}/domain/providerFailure.js +0 -0
package/README.md CHANGED
@@ -31,12 +31,17 @@ An interactive terminal chat, backed by one `Agent` — a single engine, or a fu
31
31
  multi-agent workflow, chosen entirely by what gets wired up in `cli/cli.ts`; the
32
32
  chat loop itself never knows the difference.
33
33
 
34
- - **A provider-agnostic `Agent` contract** (`run(prompt, signal, callback)`) with
35
- two real implementations, `CodexAgent` and `ClaudeAgent` — swapping one for the
36
- other, anywhere in the composition, changes nothing else.
37
- - **Conversation continuity for free** in both engines: Codex reuses one `Thread`
38
- across turns; Claude resumes via a captured `session_id` — the engine keeps
39
- context server-side either way.
34
+ - **A provider-agnostic `Agent` contract** (`run(prompt, { signal, onProgress })`) with
35
+ two paths to each provider — swapping one agent for another, anywhere in the
36
+ composition, changes nothing else.
37
+ - **The Agent SDK path, the default**: `CodexAgent` and `ClaudeAgent` drive Codex
38
+ and Claude Code. Codex reuses one `Thread` across turns and Claude resumes via a
39
+ captured `session_id`, so the provider keeps the context.
40
+ - **The model API path**: `LLMAgent` calls the OpenAI Responses API or the
41
+ Anthropic Messages API and keeps the conversation itself, in a form that can move
42
+ between providers. It runs its own tool loop over the tools it is given; the
43
+ harness ships three that only read the repository (list, search, read), bounded
44
+ by `.gitignore`.
40
45
  - **`OrchestratorAgent`**: coordinates a planner, an executor, and a reviewer
41
46
  (each an injected `Agent`) in a plan → execute → review loop. The reviewer's
42
47
  decision is a Zod-validated structured `{decision, feedback}`, not free text —
@@ -79,7 +84,7 @@ file-based agent registries — each waits for a real need.
79
84
 
80
85
  A new provider only needs one thing to compose safely into everything above:
81
86
  **it must only ever reject with `RecoverableError` or `UnrecoverableError`**
82
- (`src/agent/domain/errors.ts`), never a raw SDK error. `RetryingAgent` and
87
+ (`src/shared/domain/errors.ts`), never a raw SDK error. `RetryingAgent` and
83
88
  `OrchestratorAgent` both decide what to do next by `instanceof`-checking
84
89
  against those two types; anything else leaking through is treated as
85
90
  unrecoverable and ends the run, because nothing above the adapter can tell
@@ -96,9 +101,12 @@ comment on `Agent` in `src/agent/domain/agent.ts`.
96
101
  ## Install
97
102
 
98
103
  ```sh
99
- pnpm add @mikode13/harness
104
+ pnpm add @mikode13/harness zod
100
105
  ```
101
106
 
107
+ `zod` 4 is a peer dependency: `defineTool` takes your Zod schemas, so the harness uses
108
+ your copy rather than bundling its own.
109
+
102
110
  The package is ESM only and runs on Node.js 22 (22.13 or later) or 24. Its type declarations resolve
103
111
  internal modules through the `imports` field of its `package.json`, so a TypeScript consumer
104
112
  needs `moduleResolution` set to `node16`, `nodenext` or `bundler`; the legacy `node10`
@@ -119,7 +127,10 @@ const render = (event: ProgressEvent) => {
119
127
  };
120
128
 
121
129
  try {
122
- const result = await agent.run('Summarize this repository.', controller.signal, render);
130
+ const result = await agent.run('Summarize this repository.', {
131
+ signal: controller.signal,
132
+ onProgress: render,
133
+ });
123
134
  // `response` is empty when the run produced no text; `tokens` is missing when the provider
124
135
  // reported no usage.
125
136
  console.log(result.duration, result.tokens);
@@ -147,6 +158,85 @@ reasoning effort the chosen provider does not support throws
147
158
  `UnrecoverableError` if the Codex CLI binary cannot be found, which happens when
148
159
  optional dependencies were skipped at install time.
149
160
 
161
+ ### Authentication
162
+
163
+ The two paths authenticate differently:
164
+
165
+ - `createAgent` and `createOrchestrator` use the Agent SDKs, which use the login of
166
+ the Codex CLI and of Claude Code, so a run counts against that subscription.
167
+ - `createLLMAgent` and the planner and reviewer of `createLLMOrchestrator` call the
168
+ model APIs, which bill per token and need the key of each provider they use in
169
+ the environment: `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, or both for the
170
+ orchestrator's default roles. A missing OpenAI key fails when the agent is built; a missing
171
+ Anthropic key fails its first run.
172
+
173
+ The Claude Agent SDK
174
+ [authenticates with `ANTHROPIC_API_KEY`](https://code.claude.com/docs/en/agent-sdk) whenever
175
+ it is set, so with that key in the environment a Claude agent from `createAgent`, or the executor of
176
+ `createLLMOrchestrator({ provider: 'anthropic' })`, bills the key instead of the
177
+ subscription.
178
+
179
+ ### Agents that own their conversation
180
+
181
+ `createLLMAgent` takes a system prompt and the tools the model may call, and
182
+ returns the same `Agent`. `defineTool` builds a tool from a Zod 4 schema; a field the
183
+ model may leave out is `.nullable()`, not optional, because both APIs call tools in
184
+ strict mode:
185
+
186
+ ```ts
187
+ import {
188
+ createLLMAgent,
189
+ createWorkspace,
190
+ createWorkspaceTools,
191
+ defineTool,
192
+ } from '@mikode13/harness';
193
+ import { z } from 'zod';
194
+
195
+ const now = defineTool({
196
+ name: 'now',
197
+ description: 'The current time, in ISO 8601.',
198
+ input: z.object({}),
199
+ risk: 'safe',
200
+ execute: async () => new Date().toISOString(),
201
+ });
202
+
203
+ const workspace = await createWorkspace({ root: process.cwd() });
204
+ const agent = createLLMAgent('openai', {
205
+ systemPrompt: 'You answer questions about this repository.',
206
+ tools: [...createWorkspaceTools(workspace), now],
207
+ });
208
+ ```
209
+
210
+ Each tool judges the `risk` of each call: `safe` (a read), `mutating` (a change git
211
+ can undo) or `destructive` (one that loses something), as a fixed level or as a
212
+ function of the validated input. Only a `destructive` call is held back. It runs
213
+ when the run's `approve` allows it, or always when the agent was built with
214
+ `autoApprove`, which is meant for CI where nobody can answer:
215
+
216
+ ```ts
217
+ // `ask` stands for however your app asks its user.
218
+ await agent.run('Tidy the docs folder.', {
219
+ signal: controller.signal,
220
+ approve: async ({ tool, input }) =>
221
+ (await ask(tool, input)) ? { approved: true } : { approved: false, reason: 'keep it' },
222
+ });
223
+ ```
224
+
225
+ With no `approve`, a `destructive` call is denied. A denial is not a failure: the
226
+ model receives it, with the reason when there is one, and carries on. Progress
227
+ reports the call with `status: 'denied'`.
228
+
229
+ The harness keeps no state between runs, so "don't ask again" belongs to your
230
+ approver. `rememberApprovals(ask)` builds one: when `ask` answers
231
+ `{ approved: true, remember: true }`, later calls to that tool are allowed without
232
+ asking. Its `key` option decides what counts as the same call, such as the tool and
233
+ its path. The memory lives as long as the approver you created, so create one per
234
+ session, per user or per run.
235
+
236
+ `createLLMOrchestrator()`
237
+ takes the same options as `createOrchestrator()` and returns a promise; its executor
238
+ still runs on an Agent SDK, because the harness's own tools cannot change files.
239
+
150
240
  ## Tests
151
241
 
152
242
  `pnpm test` runs the unit suite against deterministic fakes; it never contacts a
@@ -164,9 +254,19 @@ exists solely to exercise the library manually while working in this repository:
164
254
  pnpm run dev
165
255
  ```
166
256
 
257
+ The agents work on the repository root, which is the directory the script runs in.
258
+ `pnpm run dev --llm` plans and reviews on the model APIs instead, through
259
+ `createLLMOrchestrator`, and needs the API keys in the environment (see
260
+ [Authentication](#authentication)).
261
+
167
262
  Type your prompt at `>`. Press Ctrl+C while idle at the prompt to exit; pressing
168
263
  it while an agent is running cancels only that turn and returns to the prompt.
169
264
 
265
+ The CLI asks in the terminal before a destructive tool call runs: yes, always for
266
+ that tool until the CLI exits, or no with an optional reason for the model. No
267
+ agent it builds asks yet, because the harness's own tools only read and
268
+ `autoApprove` is on.
269
+
170
270
  `cli/cli.ts` currently enables `autoApprove` for its trusted backend agents.
171
271
  This maps to each provider's permission-bypass mode and grants those processes
172
272
  unrestricted command access. Keep it disabled when the host may receive untrusted
@@ -1,4 +1,5 @@
1
1
  import type { Tokens } from '#src/shared/domain/tokens';
2
+ import type { Approver } from './approval.ts';
2
3
  /**
3
4
  * How a run ended when it did not fail. Every token a run spends travels with its end: here
4
5
  * when it finishes, in `RecoverableError.tokens` or `UnrecoverableError.tokens` when it fails.
@@ -15,6 +16,21 @@ export interface AgentResponse {
15
16
  duration: number;
16
17
  }
17
18
  export type Callback = (item: ProgressEvent) => void;
19
+ /** What a run needs besides its prompt. Decorators pass it on whole, so a new field reaches every agent. */
20
+ export interface RunOptions {
21
+ /** Cancels the run, which then rejects with the signal's `AbortError`. */
22
+ signal: AbortSignal;
23
+ /** Receives the run's live activity. Without it, the run is silent. */
24
+ onProgress?: Callback | undefined;
25
+ /**
26
+ * Asked before a model-backed agent runs a `destructive` tool call. Without it, such a call
27
+ * is denied. An agent built with `autoApprove` never asks, and the Agent SDK engines ignore
28
+ * it: their own permission systems decide.
29
+ */
30
+ approve?: Approver | undefined;
31
+ }
32
+ /** The `onProgress` of a run that gave none. */
33
+ export declare const ignoreProgress: Callback;
18
34
  /**
19
35
  * Live activity during a run, for narration only: the result travels in `AgentResponse`.
20
36
  * New event types can arrive in a minor release, so render the types you know and ignore
@@ -50,7 +66,11 @@ export type ProgressEvent = {
50
66
  type: 'tool';
51
67
  id: string;
52
68
  name: string;
53
- status: 'in_progress' | 'completed' | 'error';
69
+ /**
70
+ * `in_progress` only once the call is about to run, after any approval. `denied` when
71
+ * it never ran because no one allowed it, with no `in_progress` before it.
72
+ */
73
+ status: 'in_progress' | 'completed' | 'error' | 'denied';
54
74
  } | {
55
75
  type: 'todoList';
56
76
  items: {
@@ -67,7 +87,7 @@ export type ProgressEvent = {
67
87
  * decorator (RetryingAgent, OrchestratorAgent) is built against.
68
88
  *
69
89
  * Implementers MUST only ever reject with `RecoverableError` or `UnrecoverableError`
70
- * (see ./errors.ts) — never a raw SDK error, a plain `Error`, or anything else leaked
90
+ * (see src/shared/domain/errors.ts) — never a raw SDK error, a plain `Error`, or anything else leaked
71
91
  * unclassified. Every consumer of `Agent` (RetryingAgent's retry decision,
72
92
  * OrchestratorAgent's failure handling) `instanceof`-checks against those two types to
73
93
  * decide what to do next; a leaked, unclassified error bypasses that decision
@@ -80,5 +100,5 @@ export type ProgressEvent = {
80
100
  * consumers check for it before either error type.
81
101
  */
82
102
  export interface Agent {
83
- run(prompt: string, signal: AbortSignal, callback: Callback): Promise<AgentResponse>;
103
+ run(prompt: string, options: RunOptions): Promise<AgentResponse>;
84
104
  }
@@ -1,2 +1,3 @@
1
- export {};
1
+ /** The `onProgress` of a run that gave none. */
2
+ export const ignoreProgress = () => undefined;
2
3
  //# sourceMappingURL=agent.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent.js","sourceRoot":"","sources":["../../../src/agent/domain/agent.ts"],"names":[],"mappings":"","sourcesContent":["import type { Tokens } from '#src/shared/domain/tokens';\n\n/**\n * How a run ended when it did not fail. Every token a run spends travels with its end: here\n * when it finishes, in `RecoverableError.tokens` or `UnrecoverableError.tokens` when it fails.\n */\nexport interface AgentResponse {\n\t/** The final text. Empty when the run produced none, for example a turn that only edited files. */\n\tresponse: string;\n\t/**\n\t * Missing when any call in the run completed without reporting usage, which is not the same\n\t * as zero: a partial sum would look complete.\n\t */\n\ttokens?: Tokens;\n\t/** Wall-clock seconds the consumer waited for the run, retries and every role included. */\n\tduration: number;\n}\n\nexport type Callback = (item: ProgressEvent) => void;\n\n/**\n * Live activity during a run, for narration only: the result travels in `AgentResponse`.\n * New event types can arrive in a minor release, so render the types you know and ignore\n * the rest instead of switching over them exhaustively with a `never` check.\n */\nexport type ProgressEvent =\n\t| { type: 'command'; command: string; exitCode?: number }\n\t| { type: 'reasoning'; message: string }\n\t| { type: 'search'; query: string }\n\t| { type: 'fileChange'; changes: { path: string; kind: 'add' | 'update' | 'delete' }[] }\n\t| { type: 'mcpTool'; server: string; tool: string; status: string }\n\t| { type: 'agentMessage'; message: string }\n\t/** `id` pairs a call's end with its start when one step calls the same tool more than once. */\n\t| { type: 'tool'; id: string; name: string; status: 'in_progress' | 'completed' | 'error' }\n\t| { type: 'todoList'; items: { text: string; completed: boolean }[] }\n\t| { type: 'turnStarted' }\n\t| { type: 'turnEnded' };\n\n/**\n * The contract every engine (CodexAgent, ClaudeAgent, future providers) and every\n * decorator (RetryingAgent, OrchestratorAgent) is built against.\n *\n * Implementers MUST only ever reject with `RecoverableError` or `UnrecoverableError`\n * (see ./errors.ts) — never a raw SDK error, a plain `Error`, or anything else leaked\n * unclassified. Every consumer of `Agent` (RetryingAgent's retry decision,\n * OrchestratorAgent's failure handling) `instanceof`-checks against those two types to\n * decide what to do next; a leaked, unclassified error bypasses that decision\n * entirely — it gets retried when it shouldn't be, or crashes a run that a retry\n * would have recovered. Wrap every call into the underlying SDK so nothing escapes\n * unclassified, including failures the SDK itself doesn't model as a domain error\n * (network errors, malformed responses, etc.); `classifyProviderFailure` does this.\n *\n * Cancellation is the one exception: an `AbortError` must propagate unchanged, because\n * consumers check for it before either error type.\n */\nexport interface Agent {\n\trun(prompt: string, signal: AbortSignal, callback: Callback): Promise<AgentResponse>;\n}\n"]}
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../../../src/agent/domain/agent.ts"],"names":[],"mappings":"AAmCA,gDAAgD;AAChD,MAAM,CAAC,MAAM,cAAc,GAAa,GAAG,EAAE,CAAC,SAAS,CAAC","sourcesContent":["import type { Tokens } from '#src/shared/domain/tokens';\nimport type { Approver } from './approval.ts';\n\n/**\n * How a run ended when it did not fail. Every token a run spends travels with its end: here\n * when it finishes, in `RecoverableError.tokens` or `UnrecoverableError.tokens` when it fails.\n */\nexport interface AgentResponse {\n\t/** The final text. Empty when the run produced none, for example a turn that only edited files. */\n\tresponse: string;\n\t/**\n\t * Missing when any call in the run completed without reporting usage, which is not the same\n\t * as zero: a partial sum would look complete.\n\t */\n\ttokens?: Tokens;\n\t/** Wall-clock seconds the consumer waited for the run, retries and every role included. */\n\tduration: number;\n}\n\nexport type Callback = (item: ProgressEvent) => void;\n\n/** What a run needs besides its prompt. Decorators pass it on whole, so a new field reaches every agent. */\nexport interface RunOptions {\n\t/** Cancels the run, which then rejects with the signal's `AbortError`. */\n\tsignal: AbortSignal;\n\t/** Receives the run's live activity. Without it, the run is silent. */\n\tonProgress?: Callback | undefined;\n\t/**\n\t * Asked before a model-backed agent runs a `destructive` tool call. Without it, such a call\n\t * is denied. An agent built with `autoApprove` never asks, and the Agent SDK engines ignore\n\t * it: their own permission systems decide.\n\t */\n\tapprove?: Approver | undefined;\n}\n\n/** The `onProgress` of a run that gave none. */\nexport const ignoreProgress: Callback = () => undefined;\n\n/**\n * Live activity during a run, for narration only: the result travels in `AgentResponse`.\n * New event types can arrive in a minor release, so render the types you know and ignore\n * the rest instead of switching over them exhaustively with a `never` check.\n */\nexport type ProgressEvent =\n\t| { type: 'command'; command: string; exitCode?: number }\n\t| { type: 'reasoning'; message: string }\n\t| { type: 'search'; query: string }\n\t| { type: 'fileChange'; changes: { path: string; kind: 'add' | 'update' | 'delete' }[] }\n\t| { type: 'mcpTool'; server: string; tool: string; status: string }\n\t| { type: 'agentMessage'; message: string }\n\t/** `id` pairs a call's end with its start when one step calls the same tool more than once. */\n\t| {\n\t\t\ttype: 'tool';\n\t\t\tid: string;\n\t\t\tname: string;\n\t\t\t/**\n\t\t\t * `in_progress` only once the call is about to run, after any approval. `denied` when\n\t\t\t * it never ran because no one allowed it, with no `in_progress` before it.\n\t\t\t */\n\t\t\tstatus: 'in_progress' | 'completed' | 'error' | 'denied';\n\t }\n\t| { type: 'todoList'; items: { text: string; completed: boolean }[] }\n\t| { type: 'turnStarted' }\n\t| { type: 'turnEnded' };\n\n/**\n * The contract every engine (CodexAgent, ClaudeAgent, future providers) and every\n * decorator (RetryingAgent, OrchestratorAgent) is built against.\n *\n * Implementers MUST only ever reject with `RecoverableError` or `UnrecoverableError`\n * (see src/shared/domain/errors.ts) — never a raw SDK error, a plain `Error`, or anything else leaked\n * unclassified. Every consumer of `Agent` (RetryingAgent's retry decision,\n * OrchestratorAgent's failure handling) `instanceof`-checks against those two types to\n * decide what to do next; a leaked, unclassified error bypasses that decision\n * entirely — it gets retried when it shouldn't be, or crashes a run that a retry\n * would have recovered. Wrap every call into the underlying SDK so nothing escapes\n * unclassified, including failures the SDK itself doesn't model as a domain error\n * (network errors, malformed responses, etc.); `classifyProviderFailure` does this.\n *\n * Cancellation is the one exception: an `AbortError` must propagate unchanged, because\n * consumers check for it before either error type.\n */\nexport interface Agent {\n\trun(prompt: string, options: RunOptions): Promise<AgentResponse>;\n}\n"]}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * How much one tool call can hurt, judged by the tool from that call's input:
3
+ * - `safe` changes nothing, as with a read.
4
+ * - `mutating` makes a change git can undo, as with creating a file.
5
+ * - `destructive` loses something, as with overwriting or deleting a file.
6
+ */
7
+ export type ToolRisk = 'safe' | 'mutating' | 'destructive';
8
+ /** A tool call waiting for someone to allow it. */
9
+ export interface ApprovalRequest {
10
+ tool: string;
11
+ /** What the model sent, unvalidated: the tool validates it only if the call is allowed. */
12
+ input: unknown;
13
+ risk: ToolRisk;
14
+ }
15
+ /** A denial's `reason` reaches the model, so it can try something else. */
16
+ export type ApprovalDecision = {
17
+ approved: true;
18
+ } | {
19
+ approved: false;
20
+ reason?: string | undefined;
21
+ };
22
+ /**
23
+ * Decides one tool call at a time. A consumer that wants "don't ask again" remembers its own
24
+ * answers, so the harness keeps no session state. A throw ends the run as an
25
+ * `UnrecoverableError`.
26
+ */
27
+ export type Approver = (request: ApprovalRequest, signal: AbortSignal) => ApprovalDecision | Promise<ApprovalDecision>;
28
+ /** What `rememberApprovals` asks for: `remember` allows the same call again without asking. */
29
+ export type RememberableDecision = {
30
+ approved: true;
31
+ remember?: boolean | undefined;
32
+ } | {
33
+ approved: false;
34
+ reason?: string | undefined;
35
+ };
36
+ /**
37
+ * Wraps `ask` so a call the user allowed with `remember` is not asked about again. `key` decides
38
+ * what counts as the same call; it defaults to the tool's name, so "always" allows every
39
+ * destructive call to that tool. The memory lives in the returned approver, so its lifetime is
40
+ * the consumer's choice: one per session, one per user, or one per run.
41
+ */
42
+ export declare function rememberApprovals(ask: (request: ApprovalRequest, signal: AbortSignal) => RememberableDecision | Promise<RememberableDecision>, { key }?: {
43
+ key?: (request: ApprovalRequest) => string;
44
+ }): Approver;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Wraps `ask` so a call the user allowed with `remember` is not asked about again. `key` decides
3
+ * what counts as the same call; it defaults to the tool's name, so "always" allows every
4
+ * destructive call to that tool. The memory lives in the returned approver, so its lifetime is
5
+ * the consumer's choice: one per session, one per user, or one per run.
6
+ */
7
+ export function rememberApprovals(ask, { key = request => request.tool } = {}) {
8
+ const allowed = new Set();
9
+ return async (request, signal) => {
10
+ const id = key(request);
11
+ if (allowed.has(id))
12
+ return { approved: true };
13
+ const decision = await ask(request, signal);
14
+ // The agent discards an answer that arrives after a cancellation, so it must not be kept.
15
+ signal.throwIfAborted();
16
+ if (!decision.approved)
17
+ return decision;
18
+ if (decision.remember)
19
+ allowed.add(id);
20
+ return { approved: true };
21
+ };
22
+ }
23
+ //# sourceMappingURL=approval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval.js","sourceRoot":"","sources":["../../../src/agent/domain/approval.ts"],"names":[],"mappings":"AAmCA;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAChC,GAGyD,EACzD,EAAE,GAAG,GAAG,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,KAAqD,EAAE;IAEtF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAElC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;QAChC,MAAM,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;QACxB,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAE/C,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC5C,0FAA0F;QAC1F,MAAM,CAAC,cAAc,EAAE,CAAC;QACxB,IAAI,CAAC,QAAQ,CAAC,QAAQ;YAAE,OAAO,QAAQ,CAAC;QACxC,IAAI,QAAQ,CAAC,QAAQ;YAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACvC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC,CAAC;AACH,CAAC","sourcesContent":["/**\n * How much one tool call can hurt, judged by the tool from that call's input:\n * - `safe` changes nothing, as with a read.\n * - `mutating` makes a change git can undo, as with creating a file.\n * - `destructive` loses something, as with overwriting or deleting a file.\n */\nexport type ToolRisk = 'safe' | 'mutating' | 'destructive';\n\n/** A tool call waiting for someone to allow it. */\nexport interface ApprovalRequest {\n\ttool: string;\n\t/** What the model sent, unvalidated: the tool validates it only if the call is allowed. */\n\tinput: unknown;\n\trisk: ToolRisk;\n}\n\n/** A denial's `reason` reaches the model, so it can try something else. */\nexport type ApprovalDecision =\n\t{ approved: true } | { approved: false; reason?: string | undefined };\n\n/**\n * Decides one tool call at a time. A consumer that wants \"don't ask again\" remembers its own\n * answers, so the harness keeps no session state. A throw ends the run as an\n * `UnrecoverableError`.\n */\nexport type Approver = (\n\trequest: ApprovalRequest,\n\tsignal: AbortSignal,\n) => ApprovalDecision | Promise<ApprovalDecision>;\n\n/** What `rememberApprovals` asks for: `remember` allows the same call again without asking. */\nexport type RememberableDecision =\n\t| { approved: true; remember?: boolean | undefined }\n\t| { approved: false; reason?: string | undefined };\n\n/**\n * Wraps `ask` so a call the user allowed with `remember` is not asked about again. `key` decides\n * what counts as the same call; it defaults to the tool's name, so \"always\" allows every\n * destructive call to that tool. The memory lives in the returned approver, so its lifetime is\n * the consumer's choice: one per session, one per user, or one per run.\n */\nexport function rememberApprovals(\n\task: (\n\t\trequest: ApprovalRequest,\n\t\tsignal: AbortSignal,\n\t) => RememberableDecision | Promise<RememberableDecision>,\n\t{ key = request => request.tool }: { key?: (request: ApprovalRequest) => string } = {},\n): Approver {\n\tconst allowed = new Set<string>();\n\n\treturn async (request, signal) => {\n\t\tconst id = key(request);\n\t\tif (allowed.has(id)) return { approved: true };\n\n\t\tconst decision = await ask(request, signal);\n\t\t// The agent discards an answer that arrives after a cancellation, so it must not be kept.\n\t\tsignal.throwIfAborted();\n\t\tif (!decision.approved) return decision;\n\t\tif (decision.remember) allowed.add(id);\n\t\treturn { approved: true };\n\t};\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { Agent, AgentResponse, Callback } from '#src/agent/domain/agent';
1
+ import { type Agent, type AgentResponse, type RunOptions } from '#src/agent/domain/agent';
2
2
  import type { ILogger } from '#src/shared/domain/logger';
3
3
  export declare const claudeModels: readonly ["haiku", "sonnet", "opus", "fable"];
4
4
  export type ClaudeModel = (typeof claudeModels)[number];
@@ -16,7 +16,7 @@ export declare class ClaudeAgent implements Agent {
16
16
  reasoningEffort?: string;
17
17
  logger: ILogger;
18
18
  });
19
- run(prompt: string, signal: AbortSignal, callback: Callback): Promise<AgentResponse>;
19
+ run(prompt: string, { signal, onProgress: callback }: RunOptions): Promise<AgentResponse>;
20
20
  private parseResponse;
21
21
  private handleSystemMessage;
22
22
  private handleAssistantMessage;
@@ -1,6 +1,7 @@
1
1
  import { query } from '@anthropic-ai/claude-agent-sdk';
2
- import { InvalidAgentConfigError, RecoverableError, UnrecoverableError, withSpentTokens, } from '#src/agent/domain/errors';
3
- import { classifiedProviderStream, classifyHostFailure, classifyLocalFailure, classifyProviderFailure, treatErrors, } from '#src/agent/domain/providerFailure';
2
+ import { ignoreProgress, } from '#src/agent/domain/agent';
3
+ import { InvalidAgentConfigError, RecoverableError, UnrecoverableError, withSpentTokens, } from '#src/shared/domain/errors';
4
+ import { classifiedProviderStream, classifyHostFailure, classifyLocalFailure, classifyProviderFailure, treatErrors, } from '#src/shared/domain/providerFailure';
4
5
  import { isOneOf } from '#src/shared/domain/isOneOf';
5
6
  // The SDK types `model` as a plain string, so this list is maintained by hand.
6
7
  export const claudeModels = ['haiku', 'sonnet', 'opus', 'fable'];
@@ -126,7 +127,7 @@ export class ClaudeAgent {
126
127
  this.reasoningEffort = reasoningEffort;
127
128
  this.logger = logger;
128
129
  }
129
- async run(prompt, signal, callback) {
130
+ async run(prompt, { signal, onProgress: callback = ignoreProgress }) {
130
131
  signal.throwIfAborted();
131
132
  const start = Date.now();
132
133
  let stream;
@@ -1 +1 @@
1
- {"version":3,"file":"claudeAgent.js","sourceRoot":"","sources":["../../../../../src/engines/claude/infrastructure/model/claudeAgent.ts"],"names":[],"mappings":"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","sourcesContent":["import { query } from '@anthropic-ai/claude-agent-sdk';\nimport type {\n\tNonNullableUsage,\n\tEffortLevel,\n\tQuery,\n\tSDKAssistantMessage,\n\tSDKMessage,\n\tSDKResultSuccess,\n\tSDKUserMessage,\n} from '@anthropic-ai/claude-agent-sdk';\nimport type { Agent, AgentResponse, Callback, ProgressEvent } from '#src/agent/domain/agent';\nimport {\n\tInvalidAgentConfigError,\n\tRecoverableError,\n\tUnrecoverableError,\n\twithSpentTokens,\n} from '#src/agent/domain/errors';\nimport {\n\tclassifiedProviderStream,\n\tclassifyHostFailure,\n\tclassifyLocalFailure,\n\tclassifyProviderFailure,\n\ttreatErrors,\n} from '#src/agent/domain/providerFailure';\nimport type { ILogger } from '#src/shared/domain/logger';\nimport type { Tokens } from '#src/shared/domain/tokens';\nimport { isOneOf } from '#src/shared/domain/isOneOf';\n\n// The SDK types `model` as a plain string, so this list is maintained by hand.\nexport const claudeModels = ['haiku', 'sonnet', 'opus', 'fable'] as const;\nexport type ClaudeModel = (typeof claudeModels)[number];\n\nexport const claudeReasoningEfforts = [\n\t'low',\n\t'medium',\n\t'high',\n\t'xhigh',\n\t'max',\n] as const satisfies readonly EffortLevel[];\nexport type ClaudeReasoningEffort = (typeof claudeReasoningEfforts)[number];\n\ntype SDKSystemMessage = Extract<SDKMessage, { type: 'system' }>;\n\ninterface PendingTool {\n\tname: string;\n\tinput: Record<string, unknown>;\n\tserver?: string;\n}\n\ninterface ToolResultBlock {\n\ttype: string;\n\ttool_use_id: string;\n\tis_error?: boolean;\n\tcontent?: unknown;\n}\n\n// Claude reports its cache counters apart from input_tokens already.\nfunction toTokens(usage: NonNullableUsage): Tokens {\n\treturn {\n\t\tinputTokens: usage.input_tokens,\n\t\treadCacheTokens: usage.cache_read_input_tokens,\n\t\twrittenCacheTokens: usage.cache_creation_input_tokens,\n\t\toutputTokens: usage.output_tokens,\n\t};\n}\n\nfunction emitProgress(callback: Callback, event: ProgressEvent): void {\n\ttry {\n\t\tcallback(event);\n\t} catch (error) {\n\t\tthrow classifyHostFailure(error, 'Claude progress callback failed');\n\t}\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n\treturn typeof value === 'object' && value !== null && !Array.isArray(value)\n\t\t? (value as Record<string, unknown>)\n\t\t: undefined;\n}\n\nfunction stringValue(value: unknown): string | undefined {\n\treturn typeof value === 'string' ? value : undefined;\n}\n\nfunction toolResultBlock(value: unknown): ToolResultBlock | undefined {\n\tconst block = asRecord(value);\n\tif (!block || typeof block.type !== 'string' || typeof block.tool_use_id !== 'string') {\n\t\treturn undefined;\n\t}\n\n\treturn block as unknown as ToolResultBlock;\n}\n\nfunction toolResultId(value: unknown): string | undefined {\n\treturn toolResultBlock(value)?.tool_use_id;\n}\n\nfunction fileChange(\n\tname: 'Edit' | 'Write',\n\ttool: PendingTool,\n\toutput: unknown,\n): ProgressEvent | undefined {\n\tconst inputPath = stringValue(tool.input.file_path);\n\tconst outputPath = stringValue(asRecord(output)?.filePath);\n\tconst path = outputPath ?? inputPath;\n\tif (!path) return undefined;\n\n\tconst kind =\n\t\tname === 'Write' && stringValue(asRecord(output)?.type) === 'create' ? 'add' : 'update';\n\treturn { type: 'fileChange', changes: [{ path, kind }] };\n}\n\nfunction todoList(tool: PendingTool): ProgressEvent | undefined {\n\tif (!Array.isArray(tool.input.todos)) return undefined;\n\n\tconst items = tool.input.todos.flatMap(todo => {\n\t\tconst item = asRecord(todo);\n\t\tconst text = stringValue(item?.content);\n\t\tconst status = stringValue(item?.status);\n\t\treturn text && status ? [{ text, completed: status === 'completed' }] : [];\n\t});\n\n\treturn { type: 'todoList', items };\n}\n\nfunction mcpTool(tool: PendingTool, result: ToolResultBlock): ProgressEvent | undefined {\n\tconst server = tool.server;\n\tconst toolName = tool.name.startsWith('mcp__')\n\t\t? tool.name.slice('mcp__'.length).split('__').slice(1).join('__')\n\t\t: tool.name;\n\tif (!server || !toolName) return undefined;\n\n\treturn {\n\t\ttype: 'mcpTool',\n\t\tserver,\n\t\ttool: toolName,\n\t\tstatus: result.is_error ? 'failed' : 'completed',\n\t};\n}\n\n// Tools without narration (Read, Grep, Task, ...) are a presentation choice, not an unknown:\n// the tool set is open-ended, so warning here would only produce noise.\nfunction describeCompletedTool(\n\ttool: PendingTool,\n\tresult: ToolResultBlock,\n\toutput: unknown,\n): ProgressEvent | undefined {\n\tif (tool.name === 'Bash') {\n\t\tconst exitCode = asRecord(output)?.exitCode ?? asRecord(output)?.exit_code;\n\t\treturn {\n\t\t\ttype: 'command',\n\t\t\tcommand: stringValue(tool.input.command) ?? '',\n\t\t\t...(typeof exitCode === 'number' ? { exitCode } : {}),\n\t\t};\n\t}\n\n\tif (tool.name === 'WebSearch') {\n\t\tconst query = stringValue(tool.input.query);\n\t\treturn query ? { type: 'search', query } : undefined;\n\t}\n\n\tif (tool.name === 'Edit' || tool.name === 'Write') {\n\t\treturn result.is_error ? undefined : fileChange(tool.name, tool, output);\n\t}\n\n\tif (tool.name === 'TodoWrite') {\n\t\treturn result.is_error ? undefined : todoList(tool);\n\t}\n\n\tif (tool.name.startsWith('mcp__') || tool.server) {\n\t\treturn mcpTool(tool, result);\n\t}\n\n\treturn undefined;\n}\n\nexport class ClaudeAgent implements Agent {\n\tprivate model: ClaudeModel;\n\tprivate autoApprove: boolean;\n\tprivate reasoningEffort: ClaudeReasoningEffort;\n\tprivate sessionId?: string;\n\tprivate logger: ILogger;\n\n\t// `model` and `reasoningEffort` are untyped on purpose: this adapter is the one that knows\n\t// what Claude supports, so it validates them instead of trusting the caller.\n\tconstructor({\n\t\tmodel,\n\t\tautoApprove = false,\n\t\treasoningEffort = 'high',\n\t\tlogger,\n\t}: {\n\t\tmodel: string;\n\t\tautoApprove?: boolean;\n\t\treasoningEffort?: string;\n\t\tlogger: ILogger;\n\t}) {\n\t\tif (!isOneOf(claudeModels, model)) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`\"${model}\" is not a Claude model; expected one of: ${claudeModels.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\tif (!isOneOf(claudeReasoningEfforts, reasoningEffort)) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`\"${reasoningEffort}\" is not a Claude reasoning effort; expected one of: ${claudeReasoningEfforts.join(', ')}`,\n\t\t\t);\n\t\t}\n\n\t\tthis.model = model;\n\t\tthis.autoApprove = autoApprove;\n\t\tthis.reasoningEffort = reasoningEffort;\n\t\tthis.logger = logger;\n\t}\n\n\tasync run(prompt: string, signal: AbortSignal, callback: Callback): Promise<AgentResponse> {\n\t\tsignal.throwIfAborted();\n\t\tconst start = Date.now();\n\n\t\tlet stream: Query;\n\n\t\ttry {\n\t\t\tstream = query({\n\t\t\t\tprompt,\n\t\t\t\toptions: {\n\t\t\t\t\teffort: this.reasoningEffort,\n\t\t\t\t\tmodel: this.model,\n\t\t\t\t\tmaxTurns: 3,\n\t\t\t\t\tcwd: process.cwd(),\n\t\t\t\t\tresume: this.sessionId,\n\t\t\t\t\t...(this.autoApprove\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tpermissionMode: 'bypassPermissions' as const,\n\t\t\t\t\t\t\t\tallowDangerouslySkipPermissions: true,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: {}),\n\t\t\t\t},\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthrow classifyProviderFailure(error, 'Claude refused the request');\n\t\t}\n\n\t\tconst closeStream = () => {\n\t\t\tstream.close();\n\t\t};\n\t\tsignal.addEventListener('abort', closeStream, { once: true });\n\n\t\ttry {\n\t\t\treturn await this.parseResponse(stream, signal, callback, start);\n\t\t} finally {\n\t\t\tsignal.removeEventListener('abort', closeStream);\n\t\t}\n\t}\n\n\tprivate async parseResponse(\n\t\tstream: Query,\n\t\tsignal: AbortSignal,\n\t\tcallback: Callback,\n\t\tstart: number,\n\t): Promise<AgentResponse> {\n\t\tconst lines: string[] = [];\n\t\tlet resultMessage: SDKResultSuccess | undefined;\n\t\tconst pendingTools = new Map<string, PendingTool>();\n\n\t\t// Once Claude sent an assistant message, the provider answered: a failure after that\n\t\t// without usage leaves the run's count unknown, unlike one that never got an answer.\n\t\tlet answered = false;\n\n\t\ttry {\n\t\t\tconst messages = classifiedProviderStream(stream, 'Claude stream ended unexpectedly');\n\n\t\t\tfor await (const message of messages) {\n\t\t\t\tthis.sessionId ??= message.session_id;\n\n\t\t\t\ttry {\n\t\t\t\t\tswitch (message.type) {\n\t\t\t\t\t\tcase 'assistant':\n\t\t\t\t\t\t\tanswered = true;\n\t\t\t\t\t\t\tthis.handleAssistantMessage(message, pendingTools, callback);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'user':\n\t\t\t\t\t\t\tthis.handleUserMessage(message, pendingTools, callback);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'result':\n\t\t\t\t\t\t\tif (message.subtype === 'success') {\n\t\t\t\t\t\t\t\tlines.push(message.result);\n\t\t\t\t\t\t\t\tresultMessage = message;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthrow new RecoverableError('Claude sdk error', {\n\t\t\t\t\t\t\t\t\tcause: [message.stop_reason, message.terminal_reason, ...message.errors].join(\n\t\t\t\t\t\t\t\t\t\t',',\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t// A failed result still reports what the turn spent.\n\t\t\t\t\t\t\t\t\ttokens: toTokens(message.usage),\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'system':\n\t\t\t\t\t\t\tthis.handleSystemMessage(message);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t// Known messages with nothing to narrate or report.\n\t\t\t\t\t\tcase 'stream_event':\n\t\t\t\t\t\tcase 'tool_progress':\n\t\t\t\t\t\tcase 'tool_use_summary':\n\t\t\t\t\t\tcase 'auth_status':\n\t\t\t\t\t\tcase 'rate_limit_event':\n\t\t\t\t\t\tcase 'prompt_suggestion':\n\t\t\t\t\t\tcase 'conversation_reset':\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault: {\n\t\t\t\t\t\t\t// `never` breaks the build when an SDK upgrade adds a message type; the\n\t\t\t\t\t\t\t// warning covers a CLI binary that is newer than the types.\n\t\t\t\t\t\t\tconst unknownMessage: never = message;\n\t\t\t\t\t\t\tthis.warn(unknownMessage, 'Unknown Claude message type');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthrow classifyLocalFailure(error, 'Claude failed while mapping progress');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsignal.throwIfAborted();\n\n\t\t\tif (!resultMessage) {\n\t\t\t\t// The turn's work stands; only its accounting is missing.\n\t\t\t\tthis.warn('Claude ended a turn without a result message; its usage is unknown');\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// A failed result reports the turn's usage itself.\n\t\t\tconst reported =\n\t\t\t\t(error instanceof RecoverableError || error instanceof UnrecoverableError) &&\n\t\t\t\terror.tokens !== undefined;\n\t\t\tthrow answered && !reported ? withSpentTokens(error, undefined, true) : error;\n\t\t}\n\n\t\treturn {\n\t\t\tresponse: lines.join('\\n'),\n\t\t\ttokens: resultMessage && toTokens(resultMessage.usage),\n\t\t\t// Measured here, like the other engines, so it exists even without a result message.\n\t\t\tduration: (Date.now() - start) / 1000,\n\t\t};\n\t}\n\n\tprivate handleSystemMessage(message: SDKSystemMessage): void {\n\t\tswitch (message.subtype) {\n\t\t\t// Failures the SDK handled without failing the turn: only a warning makes them visible.\n\t\t\tcase 'api_retry':\n\t\t\tcase 'model_refusal_fallback':\n\t\t\tcase 'model_refusal_no_fallback':\n\t\t\tcase 'permission_denied':\n\t\t\tcase 'mirror_error':\n\t\t\t\tthis.warn(message, 'Claude reported a failure without failing the turn');\n\t\t\t\treturn;\n\t\t\t// Known messages with nothing to narrate or report.\n\t\t\tcase 'init':\n\t\t\tcase 'compact_boundary':\n\t\t\tcase 'status':\n\t\t\tcase 'control_request_progress':\n\t\t\tcase 'local_command_output':\n\t\t\tcase 'hook_started':\n\t\t\tcase 'hook_progress':\n\t\t\tcase 'hook_response':\n\t\t\tcase 'plugin_install':\n\t\t\tcase 'task_notification':\n\t\t\tcase 'task_started':\n\t\t\tcase 'task_updated':\n\t\t\tcase 'task_progress':\n\t\t\tcase 'background_tasks_changed':\n\t\t\tcase 'thinking_tokens':\n\t\t\tcase 'session_state_changed':\n\t\t\tcase 'worker_shutting_down':\n\t\t\tcase 'commands_changed':\n\t\t\tcase 'notification':\n\t\t\tcase 'files_persisted':\n\t\t\tcase 'memory_recall':\n\t\t\tcase 'elicitation_complete':\n\t\t\tcase 'informational':\n\t\t\t\treturn;\n\t\t\tdefault: {\n\t\t\t\tconst unknownMessage: never = message;\n\t\t\t\tthis.warn(unknownMessage, 'Unknown Claude system message');\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate handleAssistantMessage(\n\t\tmessage: SDKAssistantMessage,\n\t\tpendingTools: Map<string, PendingTool>,\n\t\tcallback: Callback,\n\t): void {\n\t\tfor (const block of message.message.content) {\n\t\t\tswitch (block.type) {\n\t\t\t\tcase 'text':\n\t\t\t\t\temitProgress(callback, { type: 'agentMessage', message: block.text });\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'thinking':\n\t\t\t\t\temitProgress(callback, { type: 'reasoning', message: block.thinking });\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'tool_use':\n\t\t\t\tcase 'server_tool_use':\n\t\t\t\tcase 'mcp_tool_use': {\n\t\t\t\t\tconst input = asRecord(block.input);\n\t\t\t\t\tif (input) {\n\t\t\t\t\t\tpendingTools.set(block.id, {\n\t\t\t\t\t\t\tname: block.name === 'web_search' ? 'WebSearch' : block.name,\n\t\t\t\t\t\t\tinput,\n\t\t\t\t\t\t\t...('server_name' in block && typeof block.server_name === 'string'\n\t\t\t\t\t\t\t\t? { server: block.server_name }\n\t\t\t\t\t\t\t\t: parseMcpServer(block.name)),\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'web_search_tool_result':\n\t\t\t\tcase 'web_fetch_tool_result':\n\t\t\t\tcase 'advisor_tool_result':\n\t\t\t\tcase 'code_execution_tool_result':\n\t\t\t\tcase 'bash_code_execution_tool_result':\n\t\t\t\tcase 'text_editor_code_execution_tool_result':\n\t\t\t\tcase 'tool_search_tool_result':\n\t\t\t\tcase 'mcp_tool_result':\n\t\t\t\t\tthis.handleCompletedTool(block, pendingTools, callback);\n\t\t\t\t\tbreak;\n\t\t\t\t// Known blocks with nothing to narrate.\n\t\t\t\tcase 'redacted_thinking':\n\t\t\t\tcase 'container_upload':\n\t\t\t\tcase 'compaction':\n\t\t\t\tcase 'fallback':\n\t\t\t\t\tbreak;\n\t\t\t\tdefault: {\n\t\t\t\t\tconst unknownBlock: never = block;\n\t\t\t\t\tthis.warn(unknownBlock, 'Unknown Claude content block');\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate handleUserMessage(\n\t\tmessage: SDKUserMessage,\n\t\tpendingTools: Map<string, PendingTool>,\n\t\tcallback: Callback,\n\t): void {\n\t\tif (!Array.isArray(message.message.content)) return;\n\n\t\tfor (const block of message.message.content) {\n\t\t\tthis.handleCompletedTool(block, pendingTools, callback, message.tool_use_result);\n\t\t}\n\t}\n\n\tprivate handleCompletedTool(\n\t\tblock: unknown,\n\t\tpendingTools: Map<string, PendingTool>,\n\t\tcallback: Callback,\n\t\toutput?: unknown,\n\t): void {\n\t\tconst id = toolResultId(block);\n\t\tif (!id) return;\n\n\t\tconst tool = pendingTools.get(id);\n\t\tif (!tool) return;\n\n\t\tpendingTools.delete(id);\n\t\tconst result = toolResultBlock(block);\n\t\tif (!result) return;\n\n\t\tconst description = describeCompletedTool(tool, result, output ?? result.content);\n\t\tif (description) emitProgress(callback, description);\n\t}\n\n\tprivate warn(...args: unknown[]): void {\n\t\ttreatErrors(\n\t\t\t() => {\n\t\t\t\tthis.logger.warn(...args);\n\t\t\t},\n\t\t\tclassifyHostFailure,\n\t\t\t'Claude logger failed while reporting progress',\n\t\t);\n\t}\n}\n\nfunction parseMcpServer(name: string): { server: string } | undefined {\n\tif (!name.startsWith('mcp__')) return undefined;\n\n\tconst server = name.slice('mcp__'.length).split('__')[0];\n\treturn server ? { server } : undefined;\n}\n"]}
1
+ {"version":3,"file":"claudeAgent.js","sourceRoot":"","sources":["../../../../../src/engines/claude/infrastructure/model/claudeAgent.ts"],"names":[],"mappings":"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","sourcesContent":["import { query } from '@anthropic-ai/claude-agent-sdk';\nimport type {\n\tNonNullableUsage,\n\tEffortLevel,\n\tQuery,\n\tSDKAssistantMessage,\n\tSDKMessage,\n\tSDKResultSuccess,\n\tSDKUserMessage,\n} from '@anthropic-ai/claude-agent-sdk';\nimport {\n\ttype Agent,\n\ttype AgentResponse,\n\ttype Callback,\n\ttype ProgressEvent,\n\ttype RunOptions,\n\tignoreProgress,\n} from '#src/agent/domain/agent';\nimport {\n\tInvalidAgentConfigError,\n\tRecoverableError,\n\tUnrecoverableError,\n\twithSpentTokens,\n} from '#src/shared/domain/errors';\nimport {\n\tclassifiedProviderStream,\n\tclassifyHostFailure,\n\tclassifyLocalFailure,\n\tclassifyProviderFailure,\n\ttreatErrors,\n} from '#src/shared/domain/providerFailure';\nimport type { ILogger } from '#src/shared/domain/logger';\nimport type { Tokens } from '#src/shared/domain/tokens';\nimport { isOneOf } from '#src/shared/domain/isOneOf';\n\n// The SDK types `model` as a plain string, so this list is maintained by hand.\nexport const claudeModels = ['haiku', 'sonnet', 'opus', 'fable'] as const;\nexport type ClaudeModel = (typeof claudeModels)[number];\n\nexport const claudeReasoningEfforts = [\n\t'low',\n\t'medium',\n\t'high',\n\t'xhigh',\n\t'max',\n] as const satisfies readonly EffortLevel[];\nexport type ClaudeReasoningEffort = (typeof claudeReasoningEfforts)[number];\n\ntype SDKSystemMessage = Extract<SDKMessage, { type: 'system' }>;\n\ninterface PendingTool {\n\tname: string;\n\tinput: Record<string, unknown>;\n\tserver?: string;\n}\n\ninterface ToolResultBlock {\n\ttype: string;\n\ttool_use_id: string;\n\tis_error?: boolean;\n\tcontent?: unknown;\n}\n\n// Claude reports its cache counters apart from input_tokens already.\nfunction toTokens(usage: NonNullableUsage): Tokens {\n\treturn {\n\t\tinputTokens: usage.input_tokens,\n\t\treadCacheTokens: usage.cache_read_input_tokens,\n\t\twrittenCacheTokens: usage.cache_creation_input_tokens,\n\t\toutputTokens: usage.output_tokens,\n\t};\n}\n\nfunction emitProgress(callback: Callback, event: ProgressEvent): void {\n\ttry {\n\t\tcallback(event);\n\t} catch (error) {\n\t\tthrow classifyHostFailure(error, 'Claude progress callback failed');\n\t}\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n\treturn typeof value === 'object' && value !== null && !Array.isArray(value)\n\t\t? (value as Record<string, unknown>)\n\t\t: undefined;\n}\n\nfunction stringValue(value: unknown): string | undefined {\n\treturn typeof value === 'string' ? value : undefined;\n}\n\nfunction toolResultBlock(value: unknown): ToolResultBlock | undefined {\n\tconst block = asRecord(value);\n\tif (!block || typeof block.type !== 'string' || typeof block.tool_use_id !== 'string') {\n\t\treturn undefined;\n\t}\n\n\treturn block as unknown as ToolResultBlock;\n}\n\nfunction toolResultId(value: unknown): string | undefined {\n\treturn toolResultBlock(value)?.tool_use_id;\n}\n\nfunction fileChange(\n\tname: 'Edit' | 'Write',\n\ttool: PendingTool,\n\toutput: unknown,\n): ProgressEvent | undefined {\n\tconst inputPath = stringValue(tool.input.file_path);\n\tconst outputPath = stringValue(asRecord(output)?.filePath);\n\tconst path = outputPath ?? inputPath;\n\tif (!path) return undefined;\n\n\tconst kind =\n\t\tname === 'Write' && stringValue(asRecord(output)?.type) === 'create' ? 'add' : 'update';\n\treturn { type: 'fileChange', changes: [{ path, kind }] };\n}\n\nfunction todoList(tool: PendingTool): ProgressEvent | undefined {\n\tif (!Array.isArray(tool.input.todos)) return undefined;\n\n\tconst items = tool.input.todos.flatMap(todo => {\n\t\tconst item = asRecord(todo);\n\t\tconst text = stringValue(item?.content);\n\t\tconst status = stringValue(item?.status);\n\t\treturn text && status ? [{ text, completed: status === 'completed' }] : [];\n\t});\n\n\treturn { type: 'todoList', items };\n}\n\nfunction mcpTool(tool: PendingTool, result: ToolResultBlock): ProgressEvent | undefined {\n\tconst server = tool.server;\n\tconst toolName = tool.name.startsWith('mcp__')\n\t\t? tool.name.slice('mcp__'.length).split('__').slice(1).join('__')\n\t\t: tool.name;\n\tif (!server || !toolName) return undefined;\n\n\treturn {\n\t\ttype: 'mcpTool',\n\t\tserver,\n\t\ttool: toolName,\n\t\tstatus: result.is_error ? 'failed' : 'completed',\n\t};\n}\n\n// Tools without narration (Read, Grep, Task, ...) are a presentation choice, not an unknown:\n// the tool set is open-ended, so warning here would only produce noise.\nfunction describeCompletedTool(\n\ttool: PendingTool,\n\tresult: ToolResultBlock,\n\toutput: unknown,\n): ProgressEvent | undefined {\n\tif (tool.name === 'Bash') {\n\t\tconst exitCode = asRecord(output)?.exitCode ?? asRecord(output)?.exit_code;\n\t\treturn {\n\t\t\ttype: 'command',\n\t\t\tcommand: stringValue(tool.input.command) ?? '',\n\t\t\t...(typeof exitCode === 'number' ? { exitCode } : {}),\n\t\t};\n\t}\n\n\tif (tool.name === 'WebSearch') {\n\t\tconst query = stringValue(tool.input.query);\n\t\treturn query ? { type: 'search', query } : undefined;\n\t}\n\n\tif (tool.name === 'Edit' || tool.name === 'Write') {\n\t\treturn result.is_error ? undefined : fileChange(tool.name, tool, output);\n\t}\n\n\tif (tool.name === 'TodoWrite') {\n\t\treturn result.is_error ? undefined : todoList(tool);\n\t}\n\n\tif (tool.name.startsWith('mcp__') || tool.server) {\n\t\treturn mcpTool(tool, result);\n\t}\n\n\treturn undefined;\n}\n\nexport class ClaudeAgent implements Agent {\n\tprivate model: ClaudeModel;\n\tprivate autoApprove: boolean;\n\tprivate reasoningEffort: ClaudeReasoningEffort;\n\tprivate sessionId?: string;\n\tprivate logger: ILogger;\n\n\t// `model` and `reasoningEffort` are untyped on purpose: this adapter is the one that knows\n\t// what Claude supports, so it validates them instead of trusting the caller.\n\tconstructor({\n\t\tmodel,\n\t\tautoApprove = false,\n\t\treasoningEffort = 'high',\n\t\tlogger,\n\t}: {\n\t\tmodel: string;\n\t\tautoApprove?: boolean;\n\t\treasoningEffort?: string;\n\t\tlogger: ILogger;\n\t}) {\n\t\tif (!isOneOf(claudeModels, model)) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`\"${model}\" is not a Claude model; expected one of: ${claudeModels.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\tif (!isOneOf(claudeReasoningEfforts, reasoningEffort)) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`\"${reasoningEffort}\" is not a Claude reasoning effort; expected one of: ${claudeReasoningEfforts.join(', ')}`,\n\t\t\t);\n\t\t}\n\n\t\tthis.model = model;\n\t\tthis.autoApprove = autoApprove;\n\t\tthis.reasoningEffort = reasoningEffort;\n\t\tthis.logger = logger;\n\t}\n\n\tasync run(\n\t\tprompt: string,\n\t\t{ signal, onProgress: callback = ignoreProgress }: RunOptions,\n\t): Promise<AgentResponse> {\n\t\tsignal.throwIfAborted();\n\t\tconst start = Date.now();\n\n\t\tlet stream: Query;\n\n\t\ttry {\n\t\t\tstream = query({\n\t\t\t\tprompt,\n\t\t\t\toptions: {\n\t\t\t\t\teffort: this.reasoningEffort,\n\t\t\t\t\tmodel: this.model,\n\t\t\t\t\tmaxTurns: 3,\n\t\t\t\t\tcwd: process.cwd(),\n\t\t\t\t\tresume: this.sessionId,\n\t\t\t\t\t...(this.autoApprove\n\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\tpermissionMode: 'bypassPermissions' as const,\n\t\t\t\t\t\t\t\tallowDangerouslySkipPermissions: true,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: {}),\n\t\t\t\t},\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthrow classifyProviderFailure(error, 'Claude refused the request');\n\t\t}\n\n\t\tconst closeStream = () => {\n\t\t\tstream.close();\n\t\t};\n\t\tsignal.addEventListener('abort', closeStream, { once: true });\n\n\t\ttry {\n\t\t\treturn await this.parseResponse(stream, signal, callback, start);\n\t\t} finally {\n\t\t\tsignal.removeEventListener('abort', closeStream);\n\t\t}\n\t}\n\n\tprivate async parseResponse(\n\t\tstream: Query,\n\t\tsignal: AbortSignal,\n\t\tcallback: Callback,\n\t\tstart: number,\n\t): Promise<AgentResponse> {\n\t\tconst lines: string[] = [];\n\t\tlet resultMessage: SDKResultSuccess | undefined;\n\t\tconst pendingTools = new Map<string, PendingTool>();\n\n\t\t// Once Claude sent an assistant message, the provider answered: a failure after that\n\t\t// without usage leaves the run's count unknown, unlike one that never got an answer.\n\t\tlet answered = false;\n\n\t\ttry {\n\t\t\tconst messages = classifiedProviderStream(stream, 'Claude stream ended unexpectedly');\n\n\t\t\tfor await (const message of messages) {\n\t\t\t\tthis.sessionId ??= message.session_id;\n\n\t\t\t\ttry {\n\t\t\t\t\tswitch (message.type) {\n\t\t\t\t\t\tcase 'assistant':\n\t\t\t\t\t\t\tanswered = true;\n\t\t\t\t\t\t\tthis.handleAssistantMessage(message, pendingTools, callback);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'user':\n\t\t\t\t\t\t\tthis.handleUserMessage(message, pendingTools, callback);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'result':\n\t\t\t\t\t\t\tif (message.subtype === 'success') {\n\t\t\t\t\t\t\t\tlines.push(message.result);\n\t\t\t\t\t\t\t\tresultMessage = message;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tthrow new RecoverableError('Claude sdk error', {\n\t\t\t\t\t\t\t\t\tcause: [message.stop_reason, message.terminal_reason, ...message.errors].join(\n\t\t\t\t\t\t\t\t\t\t',',\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t// A failed result still reports what the turn spent.\n\t\t\t\t\t\t\t\t\ttokens: toTokens(message.usage),\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'system':\n\t\t\t\t\t\t\tthis.handleSystemMessage(message);\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t// Known messages with nothing to narrate or report.\n\t\t\t\t\t\tcase 'stream_event':\n\t\t\t\t\t\tcase 'tool_progress':\n\t\t\t\t\t\tcase 'tool_use_summary':\n\t\t\t\t\t\tcase 'auth_status':\n\t\t\t\t\t\tcase 'rate_limit_event':\n\t\t\t\t\t\tcase 'prompt_suggestion':\n\t\t\t\t\t\tcase 'conversation_reset':\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tdefault: {\n\t\t\t\t\t\t\t// `never` breaks the build when an SDK upgrade adds a message type; the\n\t\t\t\t\t\t\t// warning covers a CLI binary that is newer than the types.\n\t\t\t\t\t\t\tconst unknownMessage: never = message;\n\t\t\t\t\t\t\tthis.warn(unknownMessage, 'Unknown Claude message type');\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthrow classifyLocalFailure(error, 'Claude failed while mapping progress');\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsignal.throwIfAborted();\n\n\t\t\tif (!resultMessage) {\n\t\t\t\t// The turn's work stands; only its accounting is missing.\n\t\t\t\tthis.warn('Claude ended a turn without a result message; its usage is unknown');\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// A failed result reports the turn's usage itself.\n\t\t\tconst reported =\n\t\t\t\t(error instanceof RecoverableError || error instanceof UnrecoverableError) &&\n\t\t\t\terror.tokens !== undefined;\n\t\t\tthrow answered && !reported ? withSpentTokens(error, undefined, true) : error;\n\t\t}\n\n\t\treturn {\n\t\t\tresponse: lines.join('\\n'),\n\t\t\ttokens: resultMessage && toTokens(resultMessage.usage),\n\t\t\t// Measured here, like the other engines, so it exists even without a result message.\n\t\t\tduration: (Date.now() - start) / 1000,\n\t\t};\n\t}\n\n\tprivate handleSystemMessage(message: SDKSystemMessage): void {\n\t\tswitch (message.subtype) {\n\t\t\t// Failures the SDK handled without failing the turn: only a warning makes them visible.\n\t\t\tcase 'api_retry':\n\t\t\tcase 'model_refusal_fallback':\n\t\t\tcase 'model_refusal_no_fallback':\n\t\t\tcase 'permission_denied':\n\t\t\tcase 'mirror_error':\n\t\t\t\tthis.warn(message, 'Claude reported a failure without failing the turn');\n\t\t\t\treturn;\n\t\t\t// Known messages with nothing to narrate or report.\n\t\t\tcase 'init':\n\t\t\tcase 'compact_boundary':\n\t\t\tcase 'status':\n\t\t\tcase 'control_request_progress':\n\t\t\tcase 'local_command_output':\n\t\t\tcase 'hook_started':\n\t\t\tcase 'hook_progress':\n\t\t\tcase 'hook_response':\n\t\t\tcase 'plugin_install':\n\t\t\tcase 'task_notification':\n\t\t\tcase 'task_started':\n\t\t\tcase 'task_updated':\n\t\t\tcase 'task_progress':\n\t\t\tcase 'background_tasks_changed':\n\t\t\tcase 'thinking_tokens':\n\t\t\tcase 'session_state_changed':\n\t\t\tcase 'worker_shutting_down':\n\t\t\tcase 'commands_changed':\n\t\t\tcase 'notification':\n\t\t\tcase 'files_persisted':\n\t\t\tcase 'memory_recall':\n\t\t\tcase 'elicitation_complete':\n\t\t\tcase 'informational':\n\t\t\t\treturn;\n\t\t\tdefault: {\n\t\t\t\tconst unknownMessage: never = message;\n\t\t\t\tthis.warn(unknownMessage, 'Unknown Claude system message');\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate handleAssistantMessage(\n\t\tmessage: SDKAssistantMessage,\n\t\tpendingTools: Map<string, PendingTool>,\n\t\tcallback: Callback,\n\t): void {\n\t\tfor (const block of message.message.content) {\n\t\t\tswitch (block.type) {\n\t\t\t\tcase 'text':\n\t\t\t\t\temitProgress(callback, { type: 'agentMessage', message: block.text });\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'thinking':\n\t\t\t\t\temitProgress(callback, { type: 'reasoning', message: block.thinking });\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'tool_use':\n\t\t\t\tcase 'server_tool_use':\n\t\t\t\tcase 'mcp_tool_use': {\n\t\t\t\t\tconst input = asRecord(block.input);\n\t\t\t\t\tif (input) {\n\t\t\t\t\t\tpendingTools.set(block.id, {\n\t\t\t\t\t\t\tname: block.name === 'web_search' ? 'WebSearch' : block.name,\n\t\t\t\t\t\t\tinput,\n\t\t\t\t\t\t\t...('server_name' in block && typeof block.server_name === 'string'\n\t\t\t\t\t\t\t\t? { server: block.server_name }\n\t\t\t\t\t\t\t\t: parseMcpServer(block.name)),\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'web_search_tool_result':\n\t\t\t\tcase 'web_fetch_tool_result':\n\t\t\t\tcase 'advisor_tool_result':\n\t\t\t\tcase 'code_execution_tool_result':\n\t\t\t\tcase 'bash_code_execution_tool_result':\n\t\t\t\tcase 'text_editor_code_execution_tool_result':\n\t\t\t\tcase 'tool_search_tool_result':\n\t\t\t\tcase 'mcp_tool_result':\n\t\t\t\t\tthis.handleCompletedTool(block, pendingTools, callback);\n\t\t\t\t\tbreak;\n\t\t\t\t// Known blocks with nothing to narrate.\n\t\t\t\tcase 'redacted_thinking':\n\t\t\t\tcase 'container_upload':\n\t\t\t\tcase 'compaction':\n\t\t\t\tcase 'fallback':\n\t\t\t\t\tbreak;\n\t\t\t\tdefault: {\n\t\t\t\t\tconst unknownBlock: never = block;\n\t\t\t\t\tthis.warn(unknownBlock, 'Unknown Claude content block');\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate handleUserMessage(\n\t\tmessage: SDKUserMessage,\n\t\tpendingTools: Map<string, PendingTool>,\n\t\tcallback: Callback,\n\t): void {\n\t\tif (!Array.isArray(message.message.content)) return;\n\n\t\tfor (const block of message.message.content) {\n\t\t\tthis.handleCompletedTool(block, pendingTools, callback, message.tool_use_result);\n\t\t}\n\t}\n\n\tprivate handleCompletedTool(\n\t\tblock: unknown,\n\t\tpendingTools: Map<string, PendingTool>,\n\t\tcallback: Callback,\n\t\toutput?: unknown,\n\t): void {\n\t\tconst id = toolResultId(block);\n\t\tif (!id) return;\n\n\t\tconst tool = pendingTools.get(id);\n\t\tif (!tool) return;\n\n\t\tpendingTools.delete(id);\n\t\tconst result = toolResultBlock(block);\n\t\tif (!result) return;\n\n\t\tconst description = describeCompletedTool(tool, result, output ?? result.content);\n\t\tif (description) emitProgress(callback, description);\n\t}\n\n\tprivate warn(...args: unknown[]): void {\n\t\ttreatErrors(\n\t\t\t() => {\n\t\t\t\tthis.logger.warn(...args);\n\t\t\t},\n\t\t\tclassifyHostFailure,\n\t\t\t'Claude logger failed while reporting progress',\n\t\t);\n\t}\n}\n\nfunction parseMcpServer(name: string): { server: string } | undefined {\n\tif (!name.startsWith('mcp__')) return undefined;\n\n\tconst server = name.slice('mcp__'.length).split('__')[0];\n\treturn server ? { server } : undefined;\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { type Agent, type AgentResponse, type Callback } from '#src/agent/domain/agent';
1
+ import { type Agent, type AgentResponse, type RunOptions } from '#src/agent/domain/agent';
2
2
  import type { ILogger } from '#src/shared/domain/logger';
3
3
  export declare const codexModels: readonly ["gpt-6-astra", "gpt-5.6-sol", "gpt-5.6-luna", "gpt-5.6-terra"];
4
4
  export type CodexModel = (typeof codexModels)[number];
@@ -13,6 +13,6 @@ export declare class CodexAgent implements Agent {
13
13
  reasoningEffort?: string;
14
14
  logger: ILogger;
15
15
  });
16
- run(prompt: string, signal: AbortSignal, callback: Callback): Promise<AgentResponse>;
16
+ run(prompt: string, { signal, onProgress: callback }: RunOptions): Promise<AgentResponse>;
17
17
  private parseResponse;
18
18
  }
@@ -1,7 +1,7 @@
1
1
  import { Codex, } from '@openai/codex-sdk';
2
- import {} from '#src/agent/domain/agent';
3
- import { InvalidAgentConfigError, RecoverableError, UnrecoverableError, withSpentTokens, } from '#src/agent/domain/errors';
4
- import { classifiedProviderStream, classifyHostFailure, classifyLocalFailure, classifyProviderFailure, describeFailure, treatErrors, } from '#src/agent/domain/providerFailure';
2
+ import { ignoreProgress, } from '#src/agent/domain/agent';
3
+ import { InvalidAgentConfigError, RecoverableError, UnrecoverableError, withSpentTokens, } from '#src/shared/domain/errors';
4
+ import { classifiedProviderStream, classifyHostFailure, classifyLocalFailure, classifyProviderFailure, describeFailure, treatErrors, } from '#src/shared/domain/providerFailure';
5
5
  import { isOneOf } from '#src/shared/domain/isOneOf';
6
6
  // The SDK types `model` as a plain string, so this list is maintained by hand.
7
7
  export const codexModels = ['gpt-6-astra', 'gpt-5.6-sol', 'gpt-5.6-luna', 'gpt-5.6-terra'];
@@ -100,7 +100,7 @@ export class CodexAgent {
100
100
  }
101
101
  this.logger = logger;
102
102
  }
103
- async run(prompt, signal, callback) {
103
+ async run(prompt, { signal, onProgress: callback = ignoreProgress }) {
104
104
  const start = Date.now();
105
105
  let turn;
106
106
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"codexAgent.js","sourceRoot":"","sources":["../../../../../src/engines/codex/infrastructure/model/codexAgent.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tCodex,\n\ttype ModelReasoningEffort,\n\ttype Thread,\n\ttype ThreadEvent,\n\ttype ThreadItem,\n\ttype Usage,\n} from '@openai/codex-sdk';\nimport {\n\ttype Agent,\n\ttype AgentResponse,\n\ttype Callback,\n\ttype ProgressEvent,\n} from '#src/agent/domain/agent';\nimport {\n\tInvalidAgentConfigError,\n\tRecoverableError,\n\tUnrecoverableError,\n\twithSpentTokens,\n} from '#src/agent/domain/errors';\nimport {\n\tclassifiedProviderStream,\n\tclassifyHostFailure,\n\tclassifyLocalFailure,\n\tclassifyProviderFailure,\n\tdescribeFailure,\n\ttreatErrors,\n} from '#src/agent/domain/providerFailure';\nimport type { ILogger } from '#src/shared/domain/logger';\nimport type { Tokens } from '#src/shared/domain/tokens';\nimport { isOneOf } from '#src/shared/domain/isOneOf';\n\n// The SDK types `model` as a plain string, so this list is maintained by hand.\nexport const codexModels = ['gpt-6-astra', 'gpt-5.6-sol', 'gpt-5.6-luna', 'gpt-5.6-terra'] as const;\nexport type CodexModel = (typeof codexModels)[number];\n\n// The efforts Codex's model catalog lists; the SDK also types 'minimal' and 'persistent', which\n// none of the models above supports. `satisfies` only proves this is a subset of the SDK's type,\n// so an SDK upgrade that adds an effort has to be reconciled here by hand.\nexport const codexReasoningEfforts = [\n\t'low',\n\t'medium',\n\t'high',\n\t'xhigh',\n\t'max',\n\t'ultra',\n] as const satisfies readonly ModelReasoningEffort[];\nexport type CodexReasoningEffort = (typeof codexReasoningEfforts)[number];\n\ninterface StreamedTurn {\n\tevents: AsyncGenerator<ThreadEvent>;\n}\n\nfunction convertEventToItem(event: ThreadEvent): ThreadItem | undefined {\n\tif (event.type === 'error')\n\t\tthrow new UnrecoverableError('Codex stream error', { cause: event.message });\n\n\tif (event.type === 'turn.failed')\n\t\tthrow new UnrecoverableError('Turn failed from codex sdk', { cause: event.error.message });\n\n\tif (event.type === 'item.completed') return event.item;\n\n\treturn undefined;\n}\n\nfunction describeItem(item: ThreadItem, logger: ILogger): ProgressEvent | undefined {\n\tswitch (item.type) {\n\t\tcase 'agent_message':\n\t\t\treturn { type: 'agentMessage', message: item.text };\n\t\tcase 'reasoning':\n\t\t\treturn { type: 'reasoning', message: item.text };\n\t\tcase 'command_execution':\n\t\t\treturn { type: 'command', command: item.command, exitCode: item.exit_code };\n\t\tcase 'web_search':\n\t\t\treturn { type: 'search', query: item.query };\n\t\tcase 'file_change':\n\t\t\treturn { type: 'fileChange', changes: item.changes };\n\t\tcase 'mcp_tool_call': {\n\t\t\tif (item.error) {\n\t\t\t\tthrow new RecoverableError('skill failed', { cause: item.error.message });\n\t\t\t}\n\t\t\treturn { type: 'mcpTool', server: item.server, tool: item.tool, status: item.status };\n\t\t}\n\t\tcase 'todo_list':\n\t\t\treturn { type: 'todoList', items: item.items };\n\t\tcase 'error':\n\t\t\tthrow new RecoverableError('error while using the codex tools', { cause: item.message });\n\t\tdefault:\n\t\t\ttreatErrors(\n\t\t\t\t() => {\n\t\t\t\t\tlogger.warn(item, 'new type');\n\t\t\t\t},\n\t\t\t\tclassifyHostFailure,\n\t\t\t\t'Codex logger failed while reporting progress',\n\t\t\t);\n\t\t\treturn undefined;\n\t}\n}\n\n// Codex counts cached tokens inside input_tokens; Tokens keeps them apart.\nfunction toTokens(usage: Usage): Tokens {\n\treturn {\n\t\tinputTokens: usage.input_tokens - usage.cached_input_tokens - usage.cache_write_input_tokens,\n\t\treadCacheTokens: usage.cached_input_tokens,\n\t\twrittenCacheTokens: usage.cache_write_input_tokens,\n\t\toutputTokens: usage.output_tokens,\n\t};\n}\n\nexport class CodexAgent implements Agent {\n\tprivate thread: Thread;\n\tprivate logger: ILogger;\n\n\t// `model` and `reasoningEffort` are untyped on purpose: this adapter is the one that knows\n\t// what Codex supports, so it validates them instead of trusting the caller.\n\tconstructor({\n\t\tmodel,\n\t\tautoApprove = false,\n\t\treasoningEffort = 'high',\n\t\tlogger,\n\t}: {\n\t\tmodel: string;\n\t\tautoApprove?: boolean;\n\t\treasoningEffort?: string;\n\t\tlogger: ILogger;\n\t}) {\n\t\t// Checked before the SDK is involved: an unsupported value is the caller's mistake, not\n\t\t// a configuration Codex rejected.\n\t\tif (!isOneOf(codexModels, model)) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`\"${model}\" is not a Codex model; expected one of: ${codexModels.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\tif (!isOneOf(codexReasoningEfforts, reasoningEffort)) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`\"${reasoningEffort}\" is not a Codex reasoning effort; expected one of: ${codexReasoningEfforts.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\t// The one per-model gap in Codex's catalog.\n\t\tif (model === 'gpt-5.6-luna' && reasoningEffort === 'ultra') {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t'\"gpt-5.6-luna\" does not support the \"ultra\" reasoning effort',\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\tconst sdk = new Codex();\n\t\t\tthis.thread = sdk.startThread({\n\t\t\t\tmodel,\n\t\t\t\tmodelReasoningEffort: reasoningEffort,\n\t\t\t\t...(autoApprove\n\t\t\t\t\t? { approvalPolicy: 'never' as const, sandboxMode: 'danger-full-access' as const }\n\t\t\t\t\t: {}),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\t// Not recoverable, unlike a request: a thread the SDK refused to open at all is\n\t\t\t// rejected configuration, and running the same constructor again cannot fix it.\n\t\t\tthrow new UnrecoverableError('Codex rejected the thread configuration', {\n\t\t\t\tcause: describeFailure(error),\n\t\t\t});\n\t\t}\n\n\t\tthis.logger = logger;\n\t}\n\n\tasync run(prompt: string, signal: AbortSignal, callback: Callback): Promise<AgentResponse> {\n\t\tconst start = Date.now();\n\t\tlet turn: StreamedTurn;\n\n\t\ttry {\n\t\t\tturn = await this.thread.runStreamed(prompt, { signal });\n\t\t} catch (error) {\n\t\t\tthrow classifyProviderFailure(error, 'Codex refused the request');\n\t\t}\n\n\t\treturn await this.parseResponse(turn, callback, start);\n\t}\n\n\tprivate async parseResponse(\n\t\tturn: StreamedTurn,\n\t\tcallback: Callback,\n\t\tstart: number,\n\t): Promise<AgentResponse> {\n\t\tconst lines: string[] = [];\n\t\tlet usage: Usage | undefined = undefined;\n\n\t\t// Once Codex produced an item, the provider answered: a failure after that without usage\n\t\t// leaves the run's count unknown, unlike one that never got an answer.\n\t\tlet answered = false;\n\n\t\ttry {\n\t\t\tconst events = classifiedProviderStream(turn.events, 'Codex stream ended unexpectedly');\n\n\t\t\tfor await (const event of events) {\n\t\t\t\t// A `turn.failed` alone may be a quota or authentication failure with no model work.\n\t\t\t\tif (event.type.startsWith('item.')) answered = true;\n\t\t\t\tif (event.type === 'turn.completed') {\n\t\t\t\t\tusage = event.usage;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst item = convertEventToItem(event);\n\n\t\t\t\tif (!item) continue;\n\n\t\t\t\tlet description: ProgressEvent | undefined;\n\t\t\t\ttry {\n\t\t\t\t\tdescription = describeItem(item, this.logger);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthrow classifyLocalFailure(error, 'Codex failed while mapping progress');\n\t\t\t\t}\n\n\t\t\t\tif (description) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcallback(description);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tthrow classifyHostFailure(error, 'Codex progress callback failed');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (description?.type === 'agentMessage') lines.push(description.message);\n\t\t\t}\n\n\t\t\tif (!usage) {\n\t\t\t\t// The turn's work stands; only its accounting is missing.\n\t\t\t\ttreatErrors(\n\t\t\t\t\t() => {\n\t\t\t\t\t\tthis.logger.warn('Codex ended a turn without reporting usage');\n\t\t\t\t\t},\n\t\t\t\t\tclassifyHostFailure,\n\t\t\t\t\t'Codex logger failed while reporting missing usage',\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (usage) throw withSpentTokens(error, toTokens(usage));\n\t\t\tthrow answered ? withSpentTokens(error, undefined, true) : error;\n\t\t}\n\n\t\treturn {\n\t\t\t// Empty when the turn only ran commands or edited files.\n\t\t\tresponse: lines.join('\\n'),\n\t\t\ttokens: usage && toTokens(usage),\n\t\t\tduration: (Date.now() - start) / 1000,\n\t\t};\n\t}\n}\n"]}
1
+ {"version":3,"file":"codexAgent.js","sourceRoot":"","sources":["../../../../../src/engines/codex/infrastructure/model/codexAgent.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tCodex,\n\ttype ModelReasoningEffort,\n\ttype Thread,\n\ttype ThreadEvent,\n\ttype ThreadItem,\n\ttype Usage,\n} from '@openai/codex-sdk';\nimport {\n\ttype Agent,\n\ttype AgentResponse,\n\ttype Callback,\n\tignoreProgress,\n\ttype ProgressEvent,\n\ttype RunOptions,\n} from '#src/agent/domain/agent';\nimport {\n\tInvalidAgentConfigError,\n\tRecoverableError,\n\tUnrecoverableError,\n\twithSpentTokens,\n} from '#src/shared/domain/errors';\nimport {\n\tclassifiedProviderStream,\n\tclassifyHostFailure,\n\tclassifyLocalFailure,\n\tclassifyProviderFailure,\n\tdescribeFailure,\n\ttreatErrors,\n} from '#src/shared/domain/providerFailure';\nimport type { ILogger } from '#src/shared/domain/logger';\nimport type { Tokens } from '#src/shared/domain/tokens';\nimport { isOneOf } from '#src/shared/domain/isOneOf';\n\n// The SDK types `model` as a plain string, so this list is maintained by hand.\nexport const codexModels = ['gpt-6-astra', 'gpt-5.6-sol', 'gpt-5.6-luna', 'gpt-5.6-terra'] as const;\nexport type CodexModel = (typeof codexModels)[number];\n\n// The efforts Codex's model catalog lists; the SDK also types 'minimal' and 'persistent', which\n// none of the models above supports. `satisfies` only proves this is a subset of the SDK's type,\n// so an SDK upgrade that adds an effort has to be reconciled here by hand.\nexport const codexReasoningEfforts = [\n\t'low',\n\t'medium',\n\t'high',\n\t'xhigh',\n\t'max',\n\t'ultra',\n] as const satisfies readonly ModelReasoningEffort[];\nexport type CodexReasoningEffort = (typeof codexReasoningEfforts)[number];\n\ninterface StreamedTurn {\n\tevents: AsyncGenerator<ThreadEvent>;\n}\n\nfunction convertEventToItem(event: ThreadEvent): ThreadItem | undefined {\n\tif (event.type === 'error')\n\t\tthrow new UnrecoverableError('Codex stream error', { cause: event.message });\n\n\tif (event.type === 'turn.failed')\n\t\tthrow new UnrecoverableError('Turn failed from codex sdk', { cause: event.error.message });\n\n\tif (event.type === 'item.completed') return event.item;\n\n\treturn undefined;\n}\n\nfunction describeItem(item: ThreadItem, logger: ILogger): ProgressEvent | undefined {\n\tswitch (item.type) {\n\t\tcase 'agent_message':\n\t\t\treturn { type: 'agentMessage', message: item.text };\n\t\tcase 'reasoning':\n\t\t\treturn { type: 'reasoning', message: item.text };\n\t\tcase 'command_execution':\n\t\t\treturn { type: 'command', command: item.command, exitCode: item.exit_code };\n\t\tcase 'web_search':\n\t\t\treturn { type: 'search', query: item.query };\n\t\tcase 'file_change':\n\t\t\treturn { type: 'fileChange', changes: item.changes };\n\t\tcase 'mcp_tool_call': {\n\t\t\tif (item.error) {\n\t\t\t\tthrow new RecoverableError('skill failed', { cause: item.error.message });\n\t\t\t}\n\t\t\treturn { type: 'mcpTool', server: item.server, tool: item.tool, status: item.status };\n\t\t}\n\t\tcase 'todo_list':\n\t\t\treturn { type: 'todoList', items: item.items };\n\t\tcase 'error':\n\t\t\tthrow new RecoverableError('error while using the codex tools', { cause: item.message });\n\t\tdefault:\n\t\t\ttreatErrors(\n\t\t\t\t() => {\n\t\t\t\t\tlogger.warn(item, 'new type');\n\t\t\t\t},\n\t\t\t\tclassifyHostFailure,\n\t\t\t\t'Codex logger failed while reporting progress',\n\t\t\t);\n\t\t\treturn undefined;\n\t}\n}\n\n// Codex counts cached tokens inside input_tokens; Tokens keeps them apart.\nfunction toTokens(usage: Usage): Tokens {\n\treturn {\n\t\tinputTokens: usage.input_tokens - usage.cached_input_tokens - usage.cache_write_input_tokens,\n\t\treadCacheTokens: usage.cached_input_tokens,\n\t\twrittenCacheTokens: usage.cache_write_input_tokens,\n\t\toutputTokens: usage.output_tokens,\n\t};\n}\n\nexport class CodexAgent implements Agent {\n\tprivate thread: Thread;\n\tprivate logger: ILogger;\n\n\t// `model` and `reasoningEffort` are untyped on purpose: this adapter is the one that knows\n\t// what Codex supports, so it validates them instead of trusting the caller.\n\tconstructor({\n\t\tmodel,\n\t\tautoApprove = false,\n\t\treasoningEffort = 'high',\n\t\tlogger,\n\t}: {\n\t\tmodel: string;\n\t\tautoApprove?: boolean;\n\t\treasoningEffort?: string;\n\t\tlogger: ILogger;\n\t}) {\n\t\t// Checked before the SDK is involved: an unsupported value is the caller's mistake, not\n\t\t// a configuration Codex rejected.\n\t\tif (!isOneOf(codexModels, model)) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`\"${model}\" is not a Codex model; expected one of: ${codexModels.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\tif (!isOneOf(codexReasoningEfforts, reasoningEffort)) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`\"${reasoningEffort}\" is not a Codex reasoning effort; expected one of: ${codexReasoningEfforts.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\t// The one per-model gap in Codex's catalog.\n\t\tif (model === 'gpt-5.6-luna' && reasoningEffort === 'ultra') {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t'\"gpt-5.6-luna\" does not support the \"ultra\" reasoning effort',\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\tconst sdk = new Codex();\n\t\t\tthis.thread = sdk.startThread({\n\t\t\t\tmodel,\n\t\t\t\tmodelReasoningEffort: reasoningEffort,\n\t\t\t\t...(autoApprove\n\t\t\t\t\t? { approvalPolicy: 'never' as const, sandboxMode: 'danger-full-access' as const }\n\t\t\t\t\t: {}),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\t// Not recoverable, unlike a request: a thread the SDK refused to open at all is\n\t\t\t// rejected configuration, and running the same constructor again cannot fix it.\n\t\t\tthrow new UnrecoverableError('Codex rejected the thread configuration', {\n\t\t\t\tcause: describeFailure(error),\n\t\t\t});\n\t\t}\n\n\t\tthis.logger = logger;\n\t}\n\n\tasync run(\n\t\tprompt: string,\n\t\t{ signal, onProgress: callback = ignoreProgress }: RunOptions,\n\t): Promise<AgentResponse> {\n\t\tconst start = Date.now();\n\t\tlet turn: StreamedTurn;\n\n\t\ttry {\n\t\t\tturn = await this.thread.runStreamed(prompt, { signal });\n\t\t} catch (error) {\n\t\t\tthrow classifyProviderFailure(error, 'Codex refused the request');\n\t\t}\n\n\t\treturn await this.parseResponse(turn, callback, start);\n\t}\n\n\tprivate async parseResponse(\n\t\tturn: StreamedTurn,\n\t\tcallback: Callback,\n\t\tstart: number,\n\t): Promise<AgentResponse> {\n\t\tconst lines: string[] = [];\n\t\tlet usage: Usage | undefined = undefined;\n\n\t\t// Once Codex produced an item, the provider answered: a failure after that without usage\n\t\t// leaves the run's count unknown, unlike one that never got an answer.\n\t\tlet answered = false;\n\n\t\ttry {\n\t\t\tconst events = classifiedProviderStream(turn.events, 'Codex stream ended unexpectedly');\n\n\t\t\tfor await (const event of events) {\n\t\t\t\t// A `turn.failed` alone may be a quota or authentication failure with no model work.\n\t\t\t\tif (event.type.startsWith('item.')) answered = true;\n\t\t\t\tif (event.type === 'turn.completed') {\n\t\t\t\t\tusage = event.usage;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst item = convertEventToItem(event);\n\n\t\t\t\tif (!item) continue;\n\n\t\t\t\tlet description: ProgressEvent | undefined;\n\t\t\t\ttry {\n\t\t\t\t\tdescription = describeItem(item, this.logger);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthrow classifyLocalFailure(error, 'Codex failed while mapping progress');\n\t\t\t\t}\n\n\t\t\t\tif (description) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcallback(description);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tthrow classifyHostFailure(error, 'Codex progress callback failed');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (description?.type === 'agentMessage') lines.push(description.message);\n\t\t\t}\n\n\t\t\tif (!usage) {\n\t\t\t\t// The turn's work stands; only its accounting is missing.\n\t\t\t\ttreatErrors(\n\t\t\t\t\t() => {\n\t\t\t\t\t\tthis.logger.warn('Codex ended a turn without reporting usage');\n\t\t\t\t\t},\n\t\t\t\t\tclassifyHostFailure,\n\t\t\t\t\t'Codex logger failed while reporting missing usage',\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (usage) throw withSpentTokens(error, toTokens(usage));\n\t\t\tthrow answered ? withSpentTokens(error, undefined, true) : error;\n\t\t}\n\n\t\treturn {\n\t\t\t// Empty when the turn only ran commands or edited files.\n\t\t\tresponse: lines.join('\\n'),\n\t\t\ttokens: usage && toTokens(usage),\n\t\t\tduration: (Date.now() - start) / 1000,\n\t\t};\n\t}\n}\n"]}