@mikode13/harness 3.1.0 → 4.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 +42 -5
- package/dist/agent/domain/agent.d.ts +23 -3
- package/dist/agent/domain/agent.js +2 -1
- package/dist/agent/domain/agent.js.map +1 -1
- package/dist/agent/domain/approval.d.ts +49 -0
- package/dist/agent/domain/approval.js +48 -0
- package/dist/agent/domain/approval.js.map +1 -0
- package/dist/engines/claude/infrastructure/model/claudeAgent.d.ts +2 -2
- package/dist/engines/claude/infrastructure/model/claudeAgent.js +4 -3
- package/dist/engines/claude/infrastructure/model/claudeAgent.js.map +1 -1
- package/dist/engines/codex/infrastructure/model/codexAgent.d.ts +2 -2
- package/dist/engines/codex/infrastructure/model/codexAgent.js +4 -4
- package/dist/engines/codex/infrastructure/model/codexAgent.js.map +1 -1
- package/dist/engines/domain/model/llmAgent.d.ts +15 -5
- package/dist/engines/domain/model/llmAgent.js +97 -26
- package/dist/engines/domain/model/llmAgent.js.map +1 -1
- package/dist/factory/infrastructure/agentFactory.js +1 -1
- package/dist/factory/infrastructure/agentFactory.js.map +1 -1
- package/dist/factory/infrastructure/agentLLMFactory.d.ts +8 -3
- package/dist/factory/infrastructure/agentLLMFactory.js +5 -5
- package/dist/factory/infrastructure/agentLLMFactory.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/llm/domain/errors.d.ts +1 -1
- package/dist/llm/domain/errors.js +1 -1
- package/dist/llm/domain/errors.js.map +1 -1
- package/dist/llm/infrastructure/claudeLLMClient.js +2 -2
- package/dist/llm/infrastructure/claudeLLMClient.js.map +1 -1
- package/dist/llm/infrastructure/openAILLMClient.js +2 -2
- package/dist/llm/infrastructure/openAILLMClient.js.map +1 -1
- package/dist/orchestration/domain/model/instructedAgent.d.ts +2 -2
- package/dist/orchestration/domain/model/instructedAgent.js +2 -2
- package/dist/orchestration/domain/model/instructedAgent.js.map +1 -1
- package/dist/orchestration/domain/model/orchestratorAgent.d.ts +2 -2
- package/dist/orchestration/domain/model/orchestratorAgent.js +10 -10
- package/dist/orchestration/domain/model/orchestratorAgent.js.map +1 -1
- package/dist/retry/domain/model/retryingAgent.d.ts +2 -2
- package/dist/retry/domain/model/retryingAgent.js +4 -4
- package/dist/retry/domain/model/retryingAgent.js.map +1 -1
- package/dist/shared/domain/errors.js.map +1 -0
- package/dist/shared/domain/providerFailure.js.map +1 -0
- package/dist/tools/domain/tool.d.ts +6 -0
- package/dist/tools/domain/tool.js.map +1 -1
- package/dist/tools/infrastructure/createWorkspace.js +2 -2
- package/dist/tools/infrastructure/createWorkspace.js.map +1 -1
- package/dist/tools/infrastructure/defineTool.d.ts +7 -1
- package/dist/tools/infrastructure/defineTool.js +12 -2
- package/dist/tools/infrastructure/defineTool.js.map +1 -1
- package/dist/tools/infrastructure/workspaceTools.js +3 -0
- package/dist/tools/infrastructure/workspaceTools.js.map +1 -1
- package/package.json +1 -1
- package/dist/agent/domain/errors.js.map +0 -1
- package/dist/agent/domain/providerFailure.js.map +0 -1
- /package/dist/{agent → shared}/domain/errors.d.ts +0 -0
- /package/dist/{agent → shared}/domain/errors.js +0 -0
- /package/dist/{agent → shared}/domain/providerFailure.d.ts +0 -0
- /package/dist/{agent → shared}/domain/providerFailure.js +0 -0
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ 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,
|
|
34
|
+
- **A provider-agnostic `Agent` contract** (`run(prompt, { signal, onProgress })`) with
|
|
35
35
|
two paths to each provider — swapping one agent for another, anywhere in the
|
|
36
36
|
composition, changes nothing else.
|
|
37
37
|
- **The Agent SDK path, the default**: `CodexAgent` and `ClaudeAgent` drive Codex
|
|
@@ -84,7 +84,7 @@ file-based agent registries — each waits for a real need.
|
|
|
84
84
|
|
|
85
85
|
A new provider only needs one thing to compose safely into everything above:
|
|
86
86
|
**it must only ever reject with `RecoverableError` or `UnrecoverableError`**
|
|
87
|
-
(`src/
|
|
87
|
+
(`src/shared/domain/errors.ts`), never a raw SDK error. `RetryingAgent` and
|
|
88
88
|
`OrchestratorAgent` both decide what to do next by `instanceof`-checking
|
|
89
89
|
against those two types; anything else leaking through is treated as
|
|
90
90
|
unrecoverable and ends the run, because nothing above the adapter can tell
|
|
@@ -127,7 +127,10 @@ const render = (event: ProgressEvent) => {
|
|
|
127
127
|
};
|
|
128
128
|
|
|
129
129
|
try {
|
|
130
|
-
const result = await agent.run('Summarize this repository.',
|
|
130
|
+
const result = await agent.run('Summarize this repository.', {
|
|
131
|
+
signal: controller.signal,
|
|
132
|
+
onProgress: render,
|
|
133
|
+
});
|
|
131
134
|
// `response` is empty when the run produced no text; `tokens` is missing when the provider
|
|
132
135
|
// reported no usage.
|
|
133
136
|
console.log(result.duration, result.tokens);
|
|
@@ -193,6 +196,7 @@ const now = defineTool({
|
|
|
193
196
|
name: 'now',
|
|
194
197
|
description: 'The current time, in ISO 8601.',
|
|
195
198
|
input: z.object({}),
|
|
199
|
+
risk: 'safe',
|
|
196
200
|
execute: async () => new Date().toISOString(),
|
|
197
201
|
});
|
|
198
202
|
|
|
@@ -203,8 +207,36 @@ const agent = createLLMAgent('openai', {
|
|
|
203
207
|
});
|
|
204
208
|
```
|
|
205
209
|
|
|
206
|
-
|
|
207
|
-
|
|
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'`. An approver that throws, or answers
|
|
228
|
+
anything but a boolean `approved`, ends the run with an `UnrecoverableError`, unless
|
|
229
|
+
the run was cancelled, and the call never runs.
|
|
230
|
+
|
|
231
|
+
The harness keeps no state between runs, so "don't ask again" belongs to your
|
|
232
|
+
approver. `rememberApprovals(ask)` builds one: when `ask` answers
|
|
233
|
+
`{ approved: true, remember: true }`, later calls to that tool are allowed without
|
|
234
|
+
asking. An answer whose `approved` or `remember` is not a boolean throws, so it is
|
|
235
|
+
neither run nor remembered. Its `key` option decides what counts as the same call,
|
|
236
|
+
such as the tool and its path. The memory lives as long as the approver you created,
|
|
237
|
+
so create one per session, per user or per run.
|
|
238
|
+
|
|
239
|
+
`createLLMOrchestrator()`
|
|
208
240
|
takes the same options as `createOrchestrator()` and returns a promise; its executor
|
|
209
241
|
still runs on an Agent SDK, because the harness's own tools cannot change files.
|
|
210
242
|
|
|
@@ -233,6 +265,11 @@ The agents work on the repository root, which is the directory the script runs i
|
|
|
233
265
|
Type your prompt at `>`. Press Ctrl+C while idle at the prompt to exit; pressing
|
|
234
266
|
it while an agent is running cancels only that turn and returns to the prompt.
|
|
235
267
|
|
|
268
|
+
The CLI asks in the terminal before a destructive tool call runs: yes, always for
|
|
269
|
+
that tool until the CLI exits, or no with an optional reason for the model. No
|
|
270
|
+
agent it builds asks yet, because the harness's own tools only read and
|
|
271
|
+
`autoApprove` is on.
|
|
272
|
+
|
|
236
273
|
`cli/cli.ts` currently enables `autoApprove` for its trusted backend agents.
|
|
237
274
|
This maps to each provider's permission-bypass mode and grants those processes
|
|
238
275
|
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
|
-
|
|
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
|
|
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,
|
|
103
|
+
run(prompt: string, options: RunOptions): Promise<AgentResponse>;
|
|
84
104
|
}
|
|
@@ -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| {
|
|
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,49 @@
|
|
|
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;
|
|
45
|
+
/**
|
|
46
|
+
* Reads an approver's answer without trusting its type, since a JavaScript consumer can return
|
|
47
|
+
* anything: only an `approved` that is a boolean decides, and anything else throws.
|
|
48
|
+
*/
|
|
49
|
+
export declare function readDecision(answer: unknown): ApprovalDecision;
|
|
@@ -0,0 +1,48 @@
|
|
|
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 answer = 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
|
+
// Read before keeping anything: a truthy `'false'` would otherwise be allowed and remembered.
|
|
17
|
+
const decision = readDecision(answer);
|
|
18
|
+
if (!decision.approved)
|
|
19
|
+
return decision;
|
|
20
|
+
if (readRemember(answer))
|
|
21
|
+
allowed.add(id);
|
|
22
|
+
return decision;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Reads an approver's answer without trusting its type, since a JavaScript consumer can return
|
|
27
|
+
* anything: only an `approved` that is a boolean decides, and anything else throws.
|
|
28
|
+
*/
|
|
29
|
+
export function readDecision(answer) {
|
|
30
|
+
if (typeof answer !== 'object' || answer === null) {
|
|
31
|
+
throw new TypeError('The approver answered without a decision');
|
|
32
|
+
}
|
|
33
|
+
const { approved, reason } = answer;
|
|
34
|
+
if (typeof approved !== 'boolean') {
|
|
35
|
+
throw new TypeError('The approver answered without a boolean `approved`');
|
|
36
|
+
}
|
|
37
|
+
return approved
|
|
38
|
+
? { approved }
|
|
39
|
+
: { approved, reason: typeof reason === 'string' ? reason : undefined };
|
|
40
|
+
}
|
|
41
|
+
function readRemember(answer) {
|
|
42
|
+
const { remember } = answer;
|
|
43
|
+
if (remember !== undefined && typeof remember !== 'boolean') {
|
|
44
|
+
throw new TypeError('The approver answered without a boolean `remember`');
|
|
45
|
+
}
|
|
46
|
+
return remember === true;
|
|
47
|
+
}
|
|
48
|
+
//# 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,MAAM,GAAG,MAAM,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC1C,0FAA0F;QAC1F,MAAM,CAAC,cAAc,EAAE,CAAC;QACxB,8FAA8F;QAC9F,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;QACtC,IAAI,CAAC,QAAQ,CAAC,QAAQ;YAAE,OAAO,QAAQ,CAAC;QACxC,IAAI,YAAY,CAAC,MAAM,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC1C,OAAO,QAAQ,CAAC;IACjB,CAAC,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,MAAe;IAC3C,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACnD,MAAM,IAAI,SAAS,CAAC,0CAA0C,CAAC,CAAC;IACjE,CAAC;IACD,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAkD,CAAC;IAChF,IAAI,OAAO,QAAQ,KAAK,SAAS,EAAE,CAAC;QACnC,MAAM,IAAI,SAAS,CAAC,oDAAoD,CAAC,CAAC;IAC3E,CAAC;IAED,OAAO,QAAQ;QACd,CAAC,CAAC,EAAE,QAAQ,EAAE;QACd,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;AAC1E,CAAC;AAED,SAAS,YAAY,CAAC,MAAe;IACpC,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAgC,CAAC;IACtD,IAAI,QAAQ,KAAK,SAAS,IAAI,OAAO,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC7D,MAAM,IAAI,SAAS,CAAC,oDAAoD,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,QAAQ,KAAK,IAAI,CAAC;AAC1B,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 answer = 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\t// Read before keeping anything: a truthy `'false'` would otherwise be allowed and remembered.\n\t\tconst decision = readDecision(answer);\n\t\tif (!decision.approved) return decision;\n\t\tif (readRemember(answer)) allowed.add(id);\n\t\treturn decision;\n\t};\n}\n\n/**\n * Reads an approver's answer without trusting its type, since a JavaScript consumer can return\n * anything: only an `approved` that is a boolean decides, and anything else throws.\n */\nexport function readDecision(answer: unknown): ApprovalDecision {\n\tif (typeof answer !== 'object' || answer === null) {\n\t\tthrow new TypeError('The approver answered without a decision');\n\t}\n\tconst { approved, reason } = answer as { approved?: unknown; reason?: unknown };\n\tif (typeof approved !== 'boolean') {\n\t\tthrow new TypeError('The approver answered without a boolean `approved`');\n\t}\n\n\treturn approved\n\t\t? { approved }\n\t\t: { approved, reason: typeof reason === 'string' ? reason : undefined };\n}\n\nfunction readRemember(answer: unknown): boolean {\n\tconst { remember } = answer as { remember?: unknown };\n\tif (remember !== undefined && typeof remember !== 'boolean') {\n\t\tthrow new TypeError('The approver answered without a boolean `remember`');\n\t}\n\treturn remember === true;\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
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:
|
|
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 {
|
|
3
|
-
import {
|
|
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
|
|
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:
|
|
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/
|
|
4
|
-
import { classifiedProviderStream, classifyHostFailure, classifyLocalFailure, classifyProviderFailure, describeFailure, treatErrors, } from '#src/
|
|
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"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Agent, type AgentResponse, type RunOptions } from '#src/agent/domain/agent';
|
|
2
2
|
import type { LLMClient } from '#src/llm/domain/llm';
|
|
3
3
|
import type { Message } from '#src/llm/domain/message';
|
|
4
4
|
import type { Tool } from '#src/tools/domain/tool';
|
|
@@ -15,20 +15,30 @@ export declare class LLMAgent implements Agent {
|
|
|
15
15
|
private readonly tools;
|
|
16
16
|
private readonly toolDefinitions;
|
|
17
17
|
private readonly maxSteps;
|
|
18
|
-
|
|
18
|
+
private readonly autoApprove;
|
|
19
|
+
constructor({ llmClient, messages, tools, maxSteps, autoApprove, }: {
|
|
19
20
|
llmClient: LLMClient;
|
|
20
21
|
messages?: Message[];
|
|
21
22
|
tools?: Tool[];
|
|
22
23
|
/** How many calls to the model one run may make before it fails. */
|
|
23
24
|
maxSteps?: number;
|
|
25
|
+
/** Runs every tool call without asking, `destructive` ones included. Meant for CI. */
|
|
26
|
+
autoApprove?: boolean;
|
|
24
27
|
});
|
|
25
28
|
private llmCall;
|
|
26
29
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
30
|
+
* Undefined when the call may run; otherwise why it may not, which the model receives in its
|
|
31
|
+
* place. Only a `destructive` call is asked about: git can undo a `mutating` one. A throwing
|
|
32
|
+
* approver ends the run, as any host callback does.
|
|
33
|
+
*/
|
|
34
|
+
private denial;
|
|
35
|
+
/**
|
|
36
|
+
* Always answers the call: a missing tool, a denied call or a failing one becomes an error
|
|
37
|
+
* result, so the model can correct itself. Only a cancellation, or a failing approver,
|
|
38
|
+
* escapes, whatever error the tool turned it into.
|
|
29
39
|
*/
|
|
30
40
|
private runTool;
|
|
31
41
|
/** One at a time and in order, so their effects and their narration never interleave. */
|
|
32
42
|
private runTools;
|
|
33
|
-
run(prompt: string,
|
|
43
|
+
run(prompt: string, options: RunOptions): Promise<AgentResponse>;
|
|
34
44
|
}
|