@mikode13/harness 5.4.0 → 6.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.
- package/README.md +127 -3
- package/dist/agent/domain/agent.d.ts +30 -3
- package/dist/agent/domain/agent.js.map +1 -1
- package/dist/engines/domain/model/llmAgent.js +9 -8
- package/dist/engines/domain/model/llmAgent.js.map +1 -1
- package/dist/engines/domain/model/stepContext.d.ts +1 -1
- package/dist/engines/domain/model/stepContext.js +8 -1
- package/dist/engines/domain/model/stepContext.js.map +1 -1
- package/dist/factory/infrastructure/agentLLMFactory.d.ts +17 -2
- package/dist/factory/infrastructure/agentLLMFactory.js +57 -27
- package/dist/factory/infrastructure/agentLLMFactory.js.map +1 -1
- package/dist/factory/infrastructure/workflowFactory.d.ts +9 -0
- package/dist/factory/infrastructure/workflowFactory.js +42 -0
- package/dist/factory/infrastructure/workflowFactory.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/llm/domain/llm.d.ts +2 -2
- package/dist/llm/domain/llm.js.map +1 -1
- package/dist/llm/domain/message.d.ts +17 -1
- package/dist/llm/domain/message.js +14 -1
- package/dist/llm/domain/message.js.map +1 -1
- package/dist/llm/infrastructure/claudeLLMClient.d.ts +5 -2
- package/dist/llm/infrastructure/claudeLLMClient.js +61 -5
- package/dist/llm/infrastructure/claudeLLMClient.js.map +1 -1
- package/dist/llm/infrastructure/openAILLMClient.d.ts +5 -2
- package/dist/llm/infrastructure/openAILLMClient.js +25 -2
- package/dist/llm/infrastructure/openAILLMClient.js.map +1 -1
- package/dist/orchestration/domain/model/orchestratorAgent.d.ts +1 -1
- package/dist/orchestration/domain/model/orchestratorAgent.js.map +1 -1
- package/dist/orchestration/infrastructure/model/reviewerDecisionValidator.d.ts +1 -1
- package/dist/orchestration/infrastructure/model/reviewerDecisionValidator.js.map +1 -1
- package/dist/shared/domain/isAbortError.d.ts +1 -0
- package/dist/shared/domain/isAbortError.js +2 -1
- package/dist/shared/domain/isAbortError.js.map +1 -1
- package/dist/shared/domain/validator.d.ts +4 -0
- package/dist/shared/domain/validator.js.map +1 -0
- package/dist/tools/infrastructure/workspaceTools.d.ts +1 -0
- package/dist/tools/infrastructure/workspaceTools.js +32 -10
- package/dist/tools/infrastructure/workspaceTools.js.map +1 -1
- package/dist/workflow/domain/workflow.d.ts +100 -0
- package/dist/workflow/domain/workflow.js +12 -0
- package/dist/workflow/domain/workflow.js.map +1 -0
- package/dist/workflow/domain/workflowRunner.d.ts +54 -0
- package/dist/workflow/domain/workflowRunner.js +271 -0
- package/dist/workflow/domain/workflowRunner.js.map +1 -0
- package/package.json +1 -1
- package/dist/orchestration/domain/interface/validator.d.ts +0 -3
- package/dist/orchestration/domain/interface/validator.js.map +0 -1
- /package/dist/{orchestration/domain/interface → shared/domain}/validator.js +0 -0
|
@@ -1,2 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* What the client labelled `source` sends of `context`: everything from its latest compaction
|
|
3
|
+
* on, that compaction included, or the whole context when it has none.
|
|
4
|
+
*/
|
|
5
|
+
export function sentContext(context, source) {
|
|
6
|
+
for (let index = context.length - 1; index >= 0; index--) {
|
|
7
|
+
const message = context[index];
|
|
8
|
+
const at = message?.content.findLastIndex(part => part.type === 'compaction' && part.source === source) ?? -1;
|
|
9
|
+
if (message && at !== -1) {
|
|
10
|
+
return [{ ...message, content: message.content.slice(at) }, ...context.slice(index + 1)];
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return context;
|
|
14
|
+
}
|
|
2
15
|
//# sourceMappingURL=message.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message.js","sourceRoot":"","sources":["../../../src/llm/domain/message.ts"],"names":[],"mappings":"","sourcesContent":["export type MessagePart =\n\tTextPart | ReasoningPart | ToolCallPart | ToolResultPart | ProviderDataPart;\n\nexport interface TextPart {\n\ttype: 'text';\n\ttext: string;\n}\n\nexport interface ReasoningPart {\n\ttype: 'reasoning';\n\ttext: string;\n}\n\nexport interface ToolCallPart {\n\tid: string;\n\ttype: 'toolCall';\n\tname: string;\n\tinput: unknown;\n}\n\nexport interface ToolResultPart {\n\tcallId: string;\n\ttype: 'toolResult';\n\tname: string;\n\toutput: string;\n\tisError: boolean;\n}\n\n/**\n * A block only the client that produced it can read back, such as signed or encrypted\n * reasoning, kept whole so that client can send it again on the next call. `source` is the\n * client's own label and the domain gives it no meaning: every other client leaves the part out.\n * `data` must stay plain JSON, since the conversation is copied and will be persisted.\n */\nexport interface ProviderDataPart {\n\ttype: 'providerData';\n\tsource: string;\n\tdata: unknown;\n}\n\nexport interface Message {\n\trole: 'user' | 'assistant' | 'tool';\n\tcontent: MessagePart[];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"message.js","sourceRoot":"","sources":["../../../src/llm/domain/message.ts"],"names":[],"mappings":"AAyDA;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,OAAkB,EAAE,MAA0B;IACzE,KAAK,IAAI,KAAK,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QAC1D,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;QAC/B,MAAM,EAAE,GACP,OAAO,EAAE,OAAO,CAAC,aAAa,CAC7B,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,YAAY,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,CAC5D,IAAI,CAAC,CAAC,CAAC;QACT,IAAI,OAAO,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;YAC1B,OAAO,CAAC,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;QAC1F,CAAC;IACF,CAAC;IACD,OAAO,OAAO,CAAC;AAChB,CAAC","sourcesContent":["export type MessagePart =\n\tTextPart | ReasoningPart | ToolCallPart | ToolResultPart | ProviderDataPart | CompactionPart;\n\nexport interface TextPart {\n\ttype: 'text';\n\ttext: string;\n}\n\nexport interface ReasoningPart {\n\ttype: 'reasoning';\n\ttext: string;\n}\n\nexport interface ToolCallPart {\n\tid: string;\n\ttype: 'toolCall';\n\tname: string;\n\tinput: unknown;\n}\n\nexport interface ToolResultPart {\n\tcallId: string;\n\ttype: 'toolResult';\n\tname: string;\n\toutput: string;\n\tisError: boolean;\n}\n\n/**\n * A block only the client that produced it can read back, such as signed or encrypted\n * reasoning, kept whole so that client can send it again on the next call. `source` is the\n * client's own label and the domain gives it no meaning: every other client leaves the part out.\n * `data` must stay plain JSON, since the conversation is copied and will be persisted.\n */\nexport interface ProviderDataPart {\n\ttype: 'providerData';\n\tsource: string;\n\tdata: unknown;\n}\n\n/**\n * A provider's own summary of everything before it, written when its context passed the\n * client's `compactAt`. Like `ProviderDataPart`, only the client labelled `source` reads it\n * back: that client sends the conversation from its latest compaction on, and every other client\n * leaves the part out and sends the conversation whole, which the conversation still holds.\n */\nexport interface CompactionPart {\n\ttype: 'compaction';\n\tsource: string;\n\tdata: unknown;\n}\n\nexport interface Message {\n\trole: 'user' | 'assistant' | 'tool';\n\tcontent: MessagePart[];\n}\n\n/**\n * What the client labelled `source` sends of `context`: everything from its latest compaction\n * on, that compaction included, or the whole context when it has none.\n */\nexport function sentContext(context: Message[], source: string | undefined): Message[] {\n\tfor (let index = context.length - 1; index >= 0; index--) {\n\t\tconst message = context[index];\n\t\tconst at =\n\t\t\tmessage?.content.findLastIndex(\n\t\t\t\tpart => part.type === 'compaction' && part.source === source,\n\t\t\t) ?? -1;\n\t\tif (message && at !== -1) {\n\t\t\treturn [{ ...message, content: message.content.slice(at) }, ...context.slice(index + 1)];\n\t\t}\n\t}\n\treturn context;\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ILogger } from '#src/shared/domain/logger';
|
|
2
2
|
import type { LLMClient, LLMResponse } from '../domain/llm.ts';
|
|
3
|
-
import type
|
|
3
|
+
import { type Message } from '../domain/message.ts';
|
|
4
4
|
import { type NativeTool, type ToolDefinition } from '../domain/tool.ts';
|
|
5
5
|
export declare const claudeLLMModels: readonly ["opus", "fable", "sonnet", "haiku"];
|
|
6
6
|
export type ClaudeLLMModel = (typeof claudeLLMModels)[number];
|
|
@@ -14,12 +14,15 @@ export declare class ClaudeLLMClient implements LLMClient {
|
|
|
14
14
|
private readonly client;
|
|
15
15
|
private readonly model;
|
|
16
16
|
private readonly reasoningEffort;
|
|
17
|
+
private readonly compactAt;
|
|
17
18
|
readonly systemPrompt: string;
|
|
18
19
|
readonly providerDataSource = "claude";
|
|
19
20
|
private readonly logger;
|
|
20
|
-
constructor({ model, reasoningEffort, systemPrompt, logger, }: {
|
|
21
|
+
constructor({ model, reasoningEffort, compactAt, systemPrompt, logger, }: {
|
|
21
22
|
model: string;
|
|
22
23
|
reasoningEffort?: string;
|
|
24
|
+
/** The input tokens at which Anthropic compacts the context on its side. Without it, never. */
|
|
25
|
+
compactAt?: number | undefined;
|
|
23
26
|
systemPrompt: string;
|
|
24
27
|
logger: ILogger;
|
|
25
28
|
});
|
|
@@ -3,6 +3,7 @@ import { InvalidAgentConfigError, UnrecoverableError, withSpentTokens, } from '#
|
|
|
3
3
|
import { classifyHostFailure, classifyLocalFailure, classifyProviderFailure, describeFailure, treatErrors, } from '#src/shared/domain/providerFailure';
|
|
4
4
|
import { isOneOf } from '#src/shared/domain/isOneOf';
|
|
5
5
|
import { MaxContextError } from "../domain/errors.js";
|
|
6
|
+
import { sentContext } from "../domain/message.js";
|
|
6
7
|
import { nativeOf } from "../domain/tool.js";
|
|
7
8
|
// The Messages API takes full model IDs. Callers name a model by the Agent SDK's alias, so both
|
|
8
9
|
// paths share one vocabulary; this table pins the ID each alias means here, and moves by hand
|
|
@@ -32,6 +33,9 @@ const unrecoverableStatuses = [400, 401, 403, 404, 413, 422];
|
|
|
32
33
|
const unrecoverableErrorTypes = ['billing_error', 'authentication_error', 'permission_error'];
|
|
33
34
|
// This client's label on the thinking blocks it keeps for replay; see `ProviderDataPart`.
|
|
34
35
|
const claudeSource = 'claude';
|
|
36
|
+
// Anthropic's compaction at a token threshold, and the least threshold it accepts.
|
|
37
|
+
const compactionBeta = 'compact-2026-01-12';
|
|
38
|
+
const minCompactAt = 50_000;
|
|
35
39
|
/**
|
|
36
40
|
* Anthropic rejects a `tool_use` whose input is not an object, with a 400 on every later call.
|
|
37
41
|
* Another provider's call can hold anything else, such as OpenAI arguments that did not parse;
|
|
@@ -64,6 +68,9 @@ function toClaudeBlocks(part) {
|
|
|
64
68
|
return part.source === claudeSource
|
|
65
69
|
? [part.data]
|
|
66
70
|
: [];
|
|
71
|
+
case 'compaction':
|
|
72
|
+
// Another provider's summary means nothing here; this client sends the turns it replaced.
|
|
73
|
+
return part.source === claudeSource ? [part.data] : [];
|
|
67
74
|
case 'reasoning':
|
|
68
75
|
// Unsigned text cannot be replayed as thinking. The API accepts a turn without it.
|
|
69
76
|
return [];
|
|
@@ -122,6 +129,15 @@ function describeBlock(block, logger) {
|
|
|
122
129
|
}
|
|
123
130
|
case 'tool_use':
|
|
124
131
|
return [{ type: 'toolCall', id: block.id, name: block.name, input: block.input }];
|
|
132
|
+
case 'compaction': {
|
|
133
|
+
// Claude's summary of the context before it, readable, with what it alone reads back.
|
|
134
|
+
const compaction = {
|
|
135
|
+
type: 'compaction',
|
|
136
|
+
content: block.content,
|
|
137
|
+
encrypted_content: block.encrypted_content,
|
|
138
|
+
};
|
|
139
|
+
return [{ type: 'compaction', source: claudeSource, data: compaction }];
|
|
140
|
+
}
|
|
125
141
|
default:
|
|
126
142
|
// Server tools are never offered, so any other block is output this client does not expect.
|
|
127
143
|
treatErrors(() => {
|
|
@@ -152,7 +168,10 @@ function toStopReason(response) {
|
|
|
152
168
|
});
|
|
153
169
|
}
|
|
154
170
|
}
|
|
155
|
-
/**
|
|
171
|
+
/**
|
|
172
|
+
* Unlike OpenAI, Anthropic counts cache reads and writes apart from `input_tokens`. A call that
|
|
173
|
+
* compacted leaves the compaction out of its totals, so with iterations their sum is the bill.
|
|
174
|
+
*/
|
|
156
175
|
function toTokens(usage, logger) {
|
|
157
176
|
if (!usage) {
|
|
158
177
|
// Zeros would report a call that may have been billed as free.
|
|
@@ -161,6 +180,14 @@ function toTokens(usage, logger) {
|
|
|
161
180
|
}, classifyHostFailure, 'Claude client logger failed while reading usage');
|
|
162
181
|
return undefined;
|
|
163
182
|
}
|
|
183
|
+
if (usage.iterations && usage.iterations.length > 0) {
|
|
184
|
+
return usage.iterations.reduce((sum, iteration) => ({
|
|
185
|
+
inputTokens: sum.inputTokens + iteration.input_tokens,
|
|
186
|
+
readCacheTokens: sum.readCacheTokens + iteration.cache_read_input_tokens,
|
|
187
|
+
writtenCacheTokens: sum.writtenCacheTokens + iteration.cache_creation_input_tokens,
|
|
188
|
+
outputTokens: sum.outputTokens + iteration.output_tokens,
|
|
189
|
+
}), { inputTokens: 0, readCacheTokens: 0, writtenCacheTokens: 0, outputTokens: 0 });
|
|
190
|
+
}
|
|
164
191
|
return {
|
|
165
192
|
inputTokens: usage.input_tokens,
|
|
166
193
|
readCacheTokens: usage.cache_read_input_tokens ?? 0,
|
|
@@ -231,12 +258,13 @@ export class ClaudeLLMClient {
|
|
|
231
258
|
model;
|
|
232
259
|
// Missing for Haiku, which runs without thinking.
|
|
233
260
|
reasoningEffort;
|
|
261
|
+
compactAt;
|
|
234
262
|
systemPrompt;
|
|
235
263
|
providerDataSource = claudeSource;
|
|
236
264
|
logger;
|
|
237
265
|
// `model` and `reasoningEffort` are untyped on purpose: this adapter is the one that knows
|
|
238
266
|
// what Claude supports.
|
|
239
|
-
constructor({ model, reasoningEffort, systemPrompt, logger, }) {
|
|
267
|
+
constructor({ model, reasoningEffort, compactAt, systemPrompt, logger, }) {
|
|
240
268
|
if (!isOneOf(claudeLLMModels, model)) {
|
|
241
269
|
throw new InvalidAgentConfigError(`"${model}" is not a Claude API model; expected one of: ${claudeLLMModels.join(', ')}`);
|
|
242
270
|
}
|
|
@@ -247,6 +275,15 @@ export class ClaudeLLMClient {
|
|
|
247
275
|
if (effort !== undefined && !isOneOf(claudeLLMReasoningEfforts, effort)) {
|
|
248
276
|
throw new InvalidAgentConfigError(`"${effort}" is not a Claude API reasoning effort; expected one of: ${claudeLLMReasoningEfforts.join(', ')}`);
|
|
249
277
|
}
|
|
278
|
+
if (compactAt !== undefined) {
|
|
279
|
+
// The alias names Claude Haiku 4.5, which Anthropic does not compact.
|
|
280
|
+
if (model === 'haiku') {
|
|
281
|
+
throw new InvalidAgentConfigError('Claude Haiku cannot compact: leave compactAt out');
|
|
282
|
+
}
|
|
283
|
+
if (!(Number.isInteger(compactAt) && compactAt >= minCompactAt)) {
|
|
284
|
+
throw new InvalidAgentConfigError(`compactAt must be a whole number of tokens of at least ${String(minCompactAt)} on Claude, not ${String(compactAt)}`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
250
287
|
try {
|
|
251
288
|
// The SDK's default retries stay on, as OpenAI's do. See "The provider SDKs keep their
|
|
252
289
|
// transport retries under RetryingAgent" in decisions.md.
|
|
@@ -261,6 +298,7 @@ export class ClaudeLLMClient {
|
|
|
261
298
|
}
|
|
262
299
|
this.model = claudeLLMModelIds[model];
|
|
263
300
|
this.reasoningEffort = effort;
|
|
301
|
+
this.compactAt = compactAt;
|
|
264
302
|
this.systemPrompt = systemPrompt;
|
|
265
303
|
this.logger = logger;
|
|
266
304
|
}
|
|
@@ -270,11 +308,17 @@ export class ClaudeLLMClient {
|
|
|
270
308
|
async send({ context, tools }, signal) {
|
|
271
309
|
let response;
|
|
272
310
|
try {
|
|
273
|
-
response = await this.client.messages.create({
|
|
311
|
+
response = await this.client.beta.messages.create({
|
|
274
312
|
model: this.model,
|
|
275
313
|
max_tokens: maxOutputTokens,
|
|
276
|
-
|
|
277
|
-
|
|
314
|
+
// A compaction changes the conversation's first block, so the cache would otherwise
|
|
315
|
+
// keep nothing: this keeps the system prompt cached across it. The API rejects an
|
|
316
|
+
// empty text block, and an empty prompt has nothing to cache.
|
|
317
|
+
system: this.compactAt === undefined || this.systemPrompt === ''
|
|
318
|
+
? this.systemPrompt
|
|
319
|
+
: [{ type: 'text', text: this.systemPrompt, cache_control: { type: 'ephemeral' } }],
|
|
320
|
+
// The latest compaction carries everything before it, which Anthropic would only ignore.
|
|
321
|
+
messages: sentContext(context, claudeSource).flatMap(toClaudeInput),
|
|
278
322
|
// Left out when empty, so a request without tools stays as it always was.
|
|
279
323
|
...(tools.length > 0 && { tools: tools.map(toClaudeTool) }),
|
|
280
324
|
// Haiku has no adaptive thinking, and an effort is refused with a 400.
|
|
@@ -285,6 +329,18 @@ export class ClaudeLLMClient {
|
|
|
285
329
|
// Anthropic caches only on request. This marks the last block, so the next call
|
|
286
330
|
// reads everything before it from the cache instead of paying for it again.
|
|
287
331
|
cache_control: { type: 'ephemeral' },
|
|
332
|
+
// Anthropic compacts inside the call that passes it, and answers with the compaction first.
|
|
333
|
+
...(this.compactAt !== undefined && {
|
|
334
|
+
betas: [compactionBeta],
|
|
335
|
+
context_management: {
|
|
336
|
+
edits: [
|
|
337
|
+
{
|
|
338
|
+
type: 'compact_20260112',
|
|
339
|
+
trigger: { type: 'input_tokens', value: this.compactAt },
|
|
340
|
+
},
|
|
341
|
+
],
|
|
342
|
+
},
|
|
343
|
+
}),
|
|
288
344
|
}, { signal });
|
|
289
345
|
}
|
|
290
346
|
catch (error) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claudeLLMClient.js","sourceRoot":"","sources":["../../../src/llm/infrastructure/claudeLLMClient.ts"],"names":[],"mappings":"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","sourcesContent":["import Anthropic, { AnthropicError, APIError } from '@anthropic-ai/sdk';\nimport type {\n\tContentBlock,\n\tContentBlockParam,\n\tMessage as AnthropicMessage,\n\tMessageParam,\n\tOutputConfig,\n\tRedactedThinkingBlockParam,\n\tThinkingBlockParam,\n\tTool as ClaudeTool,\n\tToolTextEditor20250728,\n\tUsage,\n} from '@anthropic-ai/sdk/resources/messages';\nimport {\n\tInvalidAgentConfigError,\n\tUnrecoverableError,\n\twithSpentTokens,\n} from '#src/shared/domain/errors';\nimport {\n\tclassifyHostFailure,\n\tclassifyLocalFailure,\n\tclassifyProviderFailure,\n\tdescribeFailure,\n\ttreatErrors,\n} from '#src/shared/domain/providerFailure';\nimport { isOneOf } from '#src/shared/domain/isOneOf';\nimport type { ILogger } from '#src/shared/domain/logger';\nimport type { Tokens } from '#src/shared/domain/tokens';\nimport { MaxContextError } from '../domain/errors.ts';\nimport type { LLMClient, LLMResponse, StopReason } from '../domain/llm.ts';\nimport type { Message, MessagePart } from '../domain/message.ts';\nimport { type NativeTool, nativeOf, type ToolDefinition } from '../domain/tool.ts';\n\n// The Messages API takes full model IDs. Callers name a model by the Agent SDK's alias, so both\n// paths share one vocabulary; this table pins the ID each alias means here, and moves by hand\n// when Anthropic releases a newer model. Haiku runs without thinking: it has no adaptive thinking\n// and takes no reasoning effort, and it is here for short, simple calls such as a summary.\nexport const claudeLLMModels = ['opus', 'fable', 'sonnet', 'haiku'] as const;\nexport type ClaudeLLMModel = (typeof claudeLLMModels)[number];\n\nconst claudeLLMModelIds = {\n\topus: 'claude-opus-5-5',\n\tfable: 'claude-fable-5-1',\n\tsonnet: 'claude-sonnet-5',\n\thaiku: 'claude-haiku-4-5',\n} as const satisfies Record<ClaudeLLMModel, string>;\n\nexport const claudeLLMReasoningEfforts = [\n\t'low',\n\t'medium',\n\t'high',\n\t'xhigh',\n\t'max',\n] as const satisfies readonly NonNullable<OutputConfig['effort']>[];\nexport type ClaudeLLMReasoningEffort = (typeof claudeLLMReasoningEfforts)[number];\n\n// Adaptive thinking spends from this budget too. Above roughly 21,000 the SDK refuses a request\n// that is not streamed, because it could outlast its ten-minute timeout.\nconst maxOutputTokens = 16_000;\n\n// The request itself is wrong or not allowed, so sending it again cannot succeed.\nconst unrecoverableStatuses = [400, 401, 403, 404, 413, 422];\n\n// The account is out of credit, or its key cannot make the request. Resending cannot fix\n// either, whatever status the error arrives with.\nconst unrecoverableErrorTypes = ['billing_error', 'authentication_error', 'permission_error'];\n\n// This client's label on the thinking blocks it keeps for replay; see `ProviderDataPart`.\nconst claudeSource = 'claude';\n\n/**\n * Anthropic rejects a `tool_use` whose input is not an object, with a 400 on every later call.\n * Another provider's call can hold anything else, such as OpenAI arguments that did not parse;\n * its result already told the model the call failed, so sending it as empty loses nothing.\n */\nfunction toClaudeInputObject(input: unknown): unknown {\n\treturn typeof input === 'object' && input !== null && !Array.isArray(input) ? input : {};\n}\n\n/** Thinking crosses back only as the signed block this client kept, never as its text. */\nfunction toClaudeBlocks(part: MessagePart): ContentBlockParam[] {\n\tswitch (part.type) {\n\t\tcase 'text':\n\t\t\t// The API rejects an empty text block.\n\t\t\treturn part.text ? [{ type: 'text', text: part.text }] : [];\n\t\tcase 'toolCall':\n\t\t\treturn [\n\t\t\t\t{ type: 'tool_use', id: part.id, name: part.name, input: toClaudeInputObject(part.input) },\n\t\t\t];\n\t\tcase 'toolResult':\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'tool_result',\n\t\t\t\t\ttool_use_id: part.callId,\n\t\t\t\t\tcontent: part.output,\n\t\t\t\t\tis_error: part.isError,\n\t\t\t\t},\n\t\t\t];\n\t\tcase 'providerData':\n\t\t\t// Another provider's block would be rejected; losing it only costs that provider's reasoning.\n\t\t\treturn part.source === claudeSource\n\t\t\t\t? [part.data as ThinkingBlockParam | RedactedThinkingBlockParam]\n\t\t\t\t: [];\n\t\tcase 'reasoning':\n\t\t\t// Unsigned text cannot be replayed as thinking. The API accepts a turn without it.\n\t\t\treturn [];\n\t}\n}\n\n/**\n * Tool results travel in a user turn, as the API requires. A message left with no blocks is\n * left out whole; the API joins the consecutive user turns that leaves.\n */\nfunction toClaudeInput(message: Message): MessageParam[] {\n\tconst content = message.content.flatMap(toClaudeBlocks);\n\tif (content.length === 0) {\n\t\treturn [];\n\t}\n\n\treturn [{ role: message.role === 'tool' ? 'user' : message.role, content }];\n}\n\n// Anthropic fixes the text editor's name: its calls arrive under it.\nconst textEditorName = 'str_replace_based_edit_tool';\n\n/**\n * Strict: the API constrains the model to the schema, so its input always parses and matches.\n * A schema outside what strict mode supports fails the request with a 400. The native text\n * editor has Anthropic's own schema, and its calls and results are ordinary tool blocks.\n */\nfunction toClaudeTool(definition: ToolDefinition): ClaudeTool | ToolTextEditor20250728 {\n\tconst native = nativeOf(definition);\n\tif (native === 'textEditor') {\n\t\tif (definition.name !== textEditorName) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`Claude's text editor must be named \"${textEditorName}\", not \"${definition.name}\"`,\n\t\t\t);\n\t\t}\n\t\treturn { type: 'text_editor_20250728', name: textEditorName };\n\t}\n\tif (native !== undefined) {\n\t\tthrow new InvalidAgentConfigError(`Claude has no native tool \"${native}\"`);\n\t}\n\tconst { name, description, inputSchema } = definition;\n\treturn { name, description, input_schema: inputSchema, strict: true };\n}\n\nfunction describeBlock(block: ContentBlock, logger: ILogger): MessagePart[] {\n\tswitch (block.type) {\n\t\tcase 'text':\n\t\t\treturn [{ type: 'text', text: block.text }];\n\t\tcase 'thinking': {\n\t\t\t// Kept signed for replay; the summarized text is only for narration.\n\t\t\tconst signed: ThinkingBlockParam = {\n\t\t\t\ttype: 'thinking',\n\t\t\t\tthinking: block.thinking,\n\t\t\t\tsignature: block.signature,\n\t\t\t};\n\t\t\tconst replay: MessagePart = { type: 'providerData', source: claudeSource, data: signed };\n\t\t\treturn block.thinking ? [{ type: 'reasoning', text: block.thinking }, replay] : [replay];\n\t\t}\n\t\tcase 'redacted_thinking': {\n\t\t\t// Encrypted by design: nothing to narrate, but it still goes back.\n\t\t\tconst redacted: RedactedThinkingBlockParam = { type: 'redacted_thinking', data: block.data };\n\t\t\treturn [{ type: 'providerData', source: claudeSource, data: redacted }];\n\t\t}\n\t\tcase 'tool_use':\n\t\t\treturn [{ type: 'toolCall', id: block.id, name: block.name, input: block.input }];\n\t\tdefault:\n\t\t\t// Server tools are never offered, so any other block is output this client does not expect.\n\t\t\ttreatErrors(\n\t\t\t\t() => {\n\t\t\t\t\tlogger.warn(block, 'Claude returned a content block the client does not map');\n\t\t\t\t},\n\t\t\t\tclassifyHostFailure,\n\t\t\t\t'Claude client logger failed while mapping a response',\n\t\t\t);\n\t\t\treturn [];\n\t}\n}\n\nfunction toStopReason(response: AnthropicMessage): StopReason {\n\tswitch (response.stop_reason) {\n\t\t// `tool_use` is a complete answer that asks for tools: running them is the agent's call.\n\t\tcase 'end_turn':\n\t\tcase 'tool_use':\n\t\t\treturn 'completed';\n\t\tcase 'max_tokens':\n\t\t\treturn 'truncated';\n\t\tcase 'refusal':\n\t\t\treturn 'refused';\n\t\tcase 'model_context_window_exceeded':\n\t\t\tthrow new MaxContextError('The context no longer fits the Claude model', {\n\t\t\t\tcause: 'The response stopped at the model context window.',\n\t\t\t});\n\t\tdefault:\n\t\t\t// `pause_turn` and `stop_sequence` need server tools or stop sequences, and this\n\t\t\t// client sends neither.\n\t\t\tthrow new UnrecoverableError('The Claude response stopped unexpectedly', {\n\t\t\t\tcause: `The response stopped with \"${response.stop_reason ?? 'unknown'}\".`,\n\t\t\t});\n\t}\n}\n\n/** Unlike OpenAI, Anthropic counts cache reads and writes apart from `input_tokens`. */\nfunction toTokens(usage: Usage | undefined, logger: ILogger): Tokens | undefined {\n\tif (!usage) {\n\t\t// Zeros would report a call that may have been billed as free.\n\t\ttreatErrors(\n\t\t\t() => {\n\t\t\t\tlogger.warn('Claude returned a response without usage; it cannot be accounted for');\n\t\t\t},\n\t\t\tclassifyHostFailure,\n\t\t\t'Claude client logger failed while reading usage',\n\t\t);\n\t\treturn undefined;\n\t}\n\n\treturn {\n\t\tinputTokens: usage.input_tokens,\n\t\treadCacheTokens: usage.cache_read_input_tokens ?? 0,\n\t\twrittenCacheTokens: usage.cache_creation_input_tokens ?? 0,\n\t\toutputTokens: usage.output_tokens,\n\t};\n}\n\nfunction toLLMResponse(response: AnthropicMessage, logger: ILogger): LLMResponse {\n\tlet usage: Tokens | undefined;\n\n\ttry {\n\t\t// Inside the try: it warns when the usage is missing, and that warning can throw too.\n\t\tusage = toTokens(response.usage, logger);\n\t\treturn {\n\t\t\tmessage: {\n\t\t\t\trole: 'assistant',\n\t\t\t\tcontent: response.content.flatMap(block => describeBlock(block, logger)),\n\t\t\t},\n\t\t\tusage,\n\t\t\tstopReason: toStopReason(response),\n\t\t};\n\t} catch (error) {\n\t\t// The call was billed whether the response failed or a logger did while mapping it, and\n\t\t// an answer without usage leaves the run's count unknown.\n\t\tthrow withSpentTokens(error, usage, !response.usage);\n\t}\n}\n\n/**\n * The SDK resolves credentials lazily, so a missing key surfaces on the first call, as a plain\n * `Error` the SDK gives no class of its own. An `AnthropicError` that is not an `APIError` never\n * carries an answer from the API: on this client's path it is the SDK refusing to make the\n * call, such as an unresolved profile or a request too large to send without streaming.\n */\nfunction isLocalSDKFailure(error: unknown): boolean {\n\treturn (\n\t\t(error instanceof AnthropicError && !(error instanceof APIError)) ||\n\t\t(error instanceof Error && error.message.startsWith('Could not resolve authentication method'))\n\t);\n}\n\nfunction classifyClaudeFailure(error: unknown): Error {\n\tif (isLocalSDKFailure(error)) {\n\t\t// The API never answered, and calling again would fail the same way.\n\t\treturn new UnrecoverableError('The Claude client cannot send the request', {\n\t\t\tcause: describeFailure(error),\n\t\t});\n\t}\n\tif (error instanceof APIError) {\n\t\t// Anthropic gives an overlong prompt no code of its own, only a 400 with this message.\n\t\tif (error.status === 400 && error.message.includes('prompt is too long')) {\n\t\t\treturn new MaxContextError('The context no longer fits the Claude model', {\n\t\t\t\tcause: describeFailure(error),\n\t\t\t});\n\t\t}\n\t\tif (\n\t\t\tunrecoverableErrorTypes.some(type => type === error.type) ||\n\t\t\tunrecoverableStatuses.some(status => status === error.status)\n\t\t) {\n\t\t\treturn new UnrecoverableError('Claude rejected the request', {\n\t\t\t\tcause: describeFailure(error),\n\t\t\t});\n\t\t}\n\t}\n\n\t// A rate limit, an overload (529), a 5xx or a network failure: a later attempt can succeed.\n\treturn classifyProviderFailure(error, 'The Claude request failed');\n}\n\n/**\n * A stateless `LLMClient` on the Anthropic Messages API, which keeps nothing between calls:\n * every call carries the whole context, so the conversation is MiKode's, not the provider's.\n */\nexport class ClaudeLLMClient implements LLMClient {\n\tprivate readonly client: Anthropic;\n\tprivate readonly model: (typeof claudeLLMModelIds)[ClaudeLLMModel];\n\t// Missing for Haiku, which runs without thinking.\n\tprivate readonly reasoningEffort: ClaudeLLMReasoningEffort | undefined;\n\treadonly systemPrompt: string;\n\treadonly providerDataSource = claudeSource;\n\tprivate readonly logger: ILogger;\n\n\t// `model` and `reasoningEffort` are untyped on purpose: this adapter is the one that knows\n\t// what Claude supports.\n\tconstructor({\n\t\tmodel,\n\t\treasoningEffort,\n\t\tsystemPrompt,\n\t\tlogger,\n\t}: {\n\t\tmodel: string;\n\t\treasoningEffort?: string;\n\t\tsystemPrompt: string;\n\t\tlogger: ILogger;\n\t}) {\n\t\tif (!isOneOf(claudeLLMModels, model)) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`\"${model}\" is not a Claude API model; expected one of: ${claudeLLMModels.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\tif (model === 'haiku' && reasoningEffort !== undefined) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t'Claude Haiku takes no reasoning effort: it runs without thinking',\n\t\t\t);\n\t\t}\n\t\tconst effort = model === 'haiku' ? undefined : (reasoningEffort ?? 'high');\n\t\tif (effort !== undefined && !isOneOf(claudeLLMReasoningEfforts, effort)) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`\"${effort}\" is not a Claude API reasoning effort; expected one of: ${claudeLLMReasoningEfforts.join(', ')}`,\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\t// The SDK's default retries stay on, as OpenAI's do. See \"The provider SDKs keep their\n\t\t\t// transport retries under RetryingAgent\" in decisions.md.\n\t\t\tthis.client = new Anthropic();\n\t\t} catch (error) {\n\t\t\t// Conflicting credential options, for example: building it again cannot fix them. A\n\t\t\t// missing key does not fail here but on the first call; see `isLocalSDKFailure`.\n\t\t\tthrow new UnrecoverableError('The Claude client could not be set up', {\n\t\t\t\tcause: describeFailure(error),\n\t\t\t});\n\t\t}\n\n\t\tthis.model = claudeLLMModelIds[model];\n\t\tthis.reasoningEffort = effort;\n\t\tthis.systemPrompt = systemPrompt;\n\t\tthis.logger = logger;\n\t}\n\n\tsupportsNative(tool: NativeTool): boolean {\n\t\treturn tool === 'textEditor';\n\t}\n\n\tasync send(\n\t\t{ context, tools }: { context: Message[]; tools: ToolDefinition[] },\n\t\tsignal: AbortSignal,\n\t): Promise<LLMResponse> {\n\t\tlet response: AnthropicMessage;\n\n\t\ttry {\n\t\t\tresponse = await this.client.messages.create(\n\t\t\t\t{\n\t\t\t\t\tmodel: this.model,\n\t\t\t\t\tmax_tokens: maxOutputTokens,\n\t\t\t\t\tsystem: this.systemPrompt,\n\t\t\t\t\tmessages: context.flatMap(toClaudeInput),\n\t\t\t\t\t// Left out when empty, so a request without tools stays as it always was.\n\t\t\t\t\t...(tools.length > 0 && { tools: tools.map(toClaudeTool) }),\n\t\t\t\t\t// Haiku has no adaptive thinking, and an effort is refused with a 400.\n\t\t\t\t\t...(this.reasoningEffort !== undefined && {\n\t\t\t\t\t\tthinking: { type: 'adaptive', display: 'summarized' },\n\t\t\t\t\t\toutput_config: { effort: this.reasoningEffort },\n\t\t\t\t\t}),\n\t\t\t\t\t// Anthropic caches only on request. This marks the last block, so the next call\n\t\t\t\t\t// reads everything before it from the cache instead of paying for it again.\n\t\t\t\t\tcache_control: { type: 'ephemeral' },\n\t\t\t\t},\n\t\t\t\t{ signal },\n\t\t\t);\n\t\t} catch (error) {\n\t\t\t// The SDK's abort error is named 'Error', so it would pass for a provider failure.\n\t\t\tsignal.throwIfAborted();\n\t\t\tthrow classifyClaudeFailure(error);\n\t\t}\n\n\t\treturn treatErrors(\n\t\t\t() => toLLMResponse(response, this.logger),\n\t\t\tclassifyLocalFailure,\n\t\t\t'Claude response mapping failed',\n\t\t);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"claudeLLMClient.js","sourceRoot":"","sources":["../../../src/llm/infrastructure/claudeLLMClient.ts"],"names":[],"mappings":"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","sourcesContent":["import Anthropic, { AnthropicError, APIError } from '@anthropic-ai/sdk';\n// The beta surface, for compaction: without its beta header, a request is the stable one.\nimport type {\n\tBetaCompactionBlockParam as CompactionBlockParam,\n\tBetaContentBlock as ContentBlock,\n\tBetaContentBlockParam as ContentBlockParam,\n\tBetaMessage as AnthropicMessage,\n\tBetaMessageParam as MessageParam,\n\tBetaOutputConfig as OutputConfig,\n\tBetaRedactedThinkingBlockParam as RedactedThinkingBlockParam,\n\tBetaThinkingBlockParam as ThinkingBlockParam,\n\tBetaTool as ClaudeTool,\n\tBetaToolTextEditor20250728 as ToolTextEditor20250728,\n\tBetaUsage as Usage,\n} from '@anthropic-ai/sdk/resources/beta/messages/messages';\nimport {\n\tInvalidAgentConfigError,\n\tUnrecoverableError,\n\twithSpentTokens,\n} from '#src/shared/domain/errors';\nimport {\n\tclassifyHostFailure,\n\tclassifyLocalFailure,\n\tclassifyProviderFailure,\n\tdescribeFailure,\n\ttreatErrors,\n} from '#src/shared/domain/providerFailure';\nimport { isOneOf } from '#src/shared/domain/isOneOf';\nimport type { ILogger } from '#src/shared/domain/logger';\nimport type { Tokens } from '#src/shared/domain/tokens';\nimport { MaxContextError } from '../domain/errors.ts';\nimport type { LLMClient, LLMResponse, StopReason } from '../domain/llm.ts';\nimport { type Message, type MessagePart, sentContext } from '../domain/message.ts';\nimport { type NativeTool, nativeOf, type ToolDefinition } from '../domain/tool.ts';\n\n// The Messages API takes full model IDs. Callers name a model by the Agent SDK's alias, so both\n// paths share one vocabulary; this table pins the ID each alias means here, and moves by hand\n// when Anthropic releases a newer model. Haiku runs without thinking: it has no adaptive thinking\n// and takes no reasoning effort, and it is here for short, simple calls such as a summary.\nexport const claudeLLMModels = ['opus', 'fable', 'sonnet', 'haiku'] as const;\nexport type ClaudeLLMModel = (typeof claudeLLMModels)[number];\n\nconst claudeLLMModelIds = {\n\topus: 'claude-opus-5-5',\n\tfable: 'claude-fable-5-1',\n\tsonnet: 'claude-sonnet-5',\n\thaiku: 'claude-haiku-4-5',\n} as const satisfies Record<ClaudeLLMModel, string>;\n\nexport const claudeLLMReasoningEfforts = [\n\t'low',\n\t'medium',\n\t'high',\n\t'xhigh',\n\t'max',\n] as const satisfies readonly NonNullable<OutputConfig['effort']>[];\nexport type ClaudeLLMReasoningEffort = (typeof claudeLLMReasoningEfforts)[number];\n\n// Adaptive thinking spends from this budget too. Above roughly 21,000 the SDK refuses a request\n// that is not streamed, because it could outlast its ten-minute timeout.\nconst maxOutputTokens = 16_000;\n\n// The request itself is wrong or not allowed, so sending it again cannot succeed.\nconst unrecoverableStatuses = [400, 401, 403, 404, 413, 422];\n\n// The account is out of credit, or its key cannot make the request. Resending cannot fix\n// either, whatever status the error arrives with.\nconst unrecoverableErrorTypes = ['billing_error', 'authentication_error', 'permission_error'];\n\n// This client's label on the thinking blocks it keeps for replay; see `ProviderDataPart`.\nconst claudeSource = 'claude';\n\n// Anthropic's compaction at a token threshold, and the least threshold it accepts.\nconst compactionBeta = 'compact-2026-01-12';\nconst minCompactAt = 50_000;\n\n/**\n * Anthropic rejects a `tool_use` whose input is not an object, with a 400 on every later call.\n * Another provider's call can hold anything else, such as OpenAI arguments that did not parse;\n * its result already told the model the call failed, so sending it as empty loses nothing.\n */\nfunction toClaudeInputObject(input: unknown): unknown {\n\treturn typeof input === 'object' && input !== null && !Array.isArray(input) ? input : {};\n}\n\n/** Thinking crosses back only as the signed block this client kept, never as its text. */\nfunction toClaudeBlocks(part: MessagePart): ContentBlockParam[] {\n\tswitch (part.type) {\n\t\tcase 'text':\n\t\t\t// The API rejects an empty text block.\n\t\t\treturn part.text ? [{ type: 'text', text: part.text }] : [];\n\t\tcase 'toolCall':\n\t\t\treturn [\n\t\t\t\t{ type: 'tool_use', id: part.id, name: part.name, input: toClaudeInputObject(part.input) },\n\t\t\t];\n\t\tcase 'toolResult':\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'tool_result',\n\t\t\t\t\ttool_use_id: part.callId,\n\t\t\t\t\tcontent: part.output,\n\t\t\t\t\tis_error: part.isError,\n\t\t\t\t},\n\t\t\t];\n\t\tcase 'providerData':\n\t\t\t// Another provider's block would be rejected; losing it only costs that provider's reasoning.\n\t\t\treturn part.source === claudeSource\n\t\t\t\t? [part.data as ThinkingBlockParam | RedactedThinkingBlockParam]\n\t\t\t\t: [];\n\t\tcase 'compaction':\n\t\t\t// Another provider's summary means nothing here; this client sends the turns it replaced.\n\t\t\treturn part.source === claudeSource ? [part.data as CompactionBlockParam] : [];\n\t\tcase 'reasoning':\n\t\t\t// Unsigned text cannot be replayed as thinking. The API accepts a turn without it.\n\t\t\treturn [];\n\t}\n}\n\n/**\n * Tool results travel in a user turn, as the API requires. A message left with no blocks is\n * left out whole; the API joins the consecutive user turns that leaves.\n */\nfunction toClaudeInput(message: Message): MessageParam[] {\n\tconst content = message.content.flatMap(toClaudeBlocks);\n\tif (content.length === 0) {\n\t\treturn [];\n\t}\n\n\treturn [{ role: message.role === 'tool' ? 'user' : message.role, content }];\n}\n\n// Anthropic fixes the text editor's name: its calls arrive under it.\nconst textEditorName = 'str_replace_based_edit_tool';\n\n/**\n * Strict: the API constrains the model to the schema, so its input always parses and matches.\n * A schema outside what strict mode supports fails the request with a 400. The native text\n * editor has Anthropic's own schema, and its calls and results are ordinary tool blocks.\n */\nfunction toClaudeTool(definition: ToolDefinition): ClaudeTool | ToolTextEditor20250728 {\n\tconst native = nativeOf(definition);\n\tif (native === 'textEditor') {\n\t\tif (definition.name !== textEditorName) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`Claude's text editor must be named \"${textEditorName}\", not \"${definition.name}\"`,\n\t\t\t);\n\t\t}\n\t\treturn { type: 'text_editor_20250728', name: textEditorName };\n\t}\n\tif (native !== undefined) {\n\t\tthrow new InvalidAgentConfigError(`Claude has no native tool \"${native}\"`);\n\t}\n\tconst { name, description, inputSchema } = definition;\n\treturn { name, description, input_schema: inputSchema, strict: true };\n}\n\nfunction describeBlock(block: ContentBlock, logger: ILogger): MessagePart[] {\n\tswitch (block.type) {\n\t\tcase 'text':\n\t\t\treturn [{ type: 'text', text: block.text }];\n\t\tcase 'thinking': {\n\t\t\t// Kept signed for replay; the summarized text is only for narration.\n\t\t\tconst signed: ThinkingBlockParam = {\n\t\t\t\ttype: 'thinking',\n\t\t\t\tthinking: block.thinking,\n\t\t\t\tsignature: block.signature,\n\t\t\t};\n\t\t\tconst replay: MessagePart = { type: 'providerData', source: claudeSource, data: signed };\n\t\t\treturn block.thinking ? [{ type: 'reasoning', text: block.thinking }, replay] : [replay];\n\t\t}\n\t\tcase 'redacted_thinking': {\n\t\t\t// Encrypted by design: nothing to narrate, but it still goes back.\n\t\t\tconst redacted: RedactedThinkingBlockParam = { type: 'redacted_thinking', data: block.data };\n\t\t\treturn [{ type: 'providerData', source: claudeSource, data: redacted }];\n\t\t}\n\t\tcase 'tool_use':\n\t\t\treturn [{ type: 'toolCall', id: block.id, name: block.name, input: block.input }];\n\t\tcase 'compaction': {\n\t\t\t// Claude's summary of the context before it, readable, with what it alone reads back.\n\t\t\tconst compaction: CompactionBlockParam = {\n\t\t\t\ttype: 'compaction',\n\t\t\t\tcontent: block.content,\n\t\t\t\tencrypted_content: block.encrypted_content,\n\t\t\t};\n\t\t\treturn [{ type: 'compaction', source: claudeSource, data: compaction }];\n\t\t}\n\t\tdefault:\n\t\t\t// Server tools are never offered, so any other block is output this client does not expect.\n\t\t\ttreatErrors(\n\t\t\t\t() => {\n\t\t\t\t\tlogger.warn(block, 'Claude returned a content block the client does not map');\n\t\t\t\t},\n\t\t\t\tclassifyHostFailure,\n\t\t\t\t'Claude client logger failed while mapping a response',\n\t\t\t);\n\t\t\treturn [];\n\t}\n}\n\nfunction toStopReason(response: AnthropicMessage): StopReason {\n\tswitch (response.stop_reason) {\n\t\t// `tool_use` is a complete answer that asks for tools: running them is the agent's call.\n\t\tcase 'end_turn':\n\t\tcase 'tool_use':\n\t\t\treturn 'completed';\n\t\tcase 'max_tokens':\n\t\t\treturn 'truncated';\n\t\tcase 'refusal':\n\t\t\treturn 'refused';\n\t\tcase 'model_context_window_exceeded':\n\t\t\tthrow new MaxContextError('The context no longer fits the Claude model', {\n\t\t\t\tcause: 'The response stopped at the model context window.',\n\t\t\t});\n\t\tdefault:\n\t\t\t// `pause_turn` and `stop_sequence` need server tools or stop sequences, and this\n\t\t\t// client sends neither.\n\t\t\tthrow new UnrecoverableError('The Claude response stopped unexpectedly', {\n\t\t\t\tcause: `The response stopped with \"${response.stop_reason ?? 'unknown'}\".`,\n\t\t\t});\n\t}\n}\n\n/**\n * Unlike OpenAI, Anthropic counts cache reads and writes apart from `input_tokens`. A call that\n * compacted leaves the compaction out of its totals, so with iterations their sum is the bill.\n */\nfunction toTokens(usage: Usage | undefined, logger: ILogger): Tokens | undefined {\n\tif (!usage) {\n\t\t// Zeros would report a call that may have been billed as free.\n\t\ttreatErrors(\n\t\t\t() => {\n\t\t\t\tlogger.warn('Claude returned a response without usage; it cannot be accounted for');\n\t\t\t},\n\t\t\tclassifyHostFailure,\n\t\t\t'Claude client logger failed while reading usage',\n\t\t);\n\t\treturn undefined;\n\t}\n\n\tif (usage.iterations && usage.iterations.length > 0) {\n\t\treturn usage.iterations.reduce<Tokens>(\n\t\t\t(sum, iteration) => ({\n\t\t\t\tinputTokens: sum.inputTokens + iteration.input_tokens,\n\t\t\t\treadCacheTokens: sum.readCacheTokens + iteration.cache_read_input_tokens,\n\t\t\t\twrittenCacheTokens: sum.writtenCacheTokens + iteration.cache_creation_input_tokens,\n\t\t\t\toutputTokens: sum.outputTokens + iteration.output_tokens,\n\t\t\t}),\n\t\t\t{ inputTokens: 0, readCacheTokens: 0, writtenCacheTokens: 0, outputTokens: 0 },\n\t\t);\n\t}\n\treturn {\n\t\tinputTokens: usage.input_tokens,\n\t\treadCacheTokens: usage.cache_read_input_tokens ?? 0,\n\t\twrittenCacheTokens: usage.cache_creation_input_tokens ?? 0,\n\t\toutputTokens: usage.output_tokens,\n\t};\n}\n\nfunction toLLMResponse(response: AnthropicMessage, logger: ILogger): LLMResponse {\n\tlet usage: Tokens | undefined;\n\n\ttry {\n\t\t// Inside the try: it warns when the usage is missing, and that warning can throw too.\n\t\tusage = toTokens(response.usage, logger);\n\t\treturn {\n\t\t\tmessage: {\n\t\t\t\trole: 'assistant',\n\t\t\t\tcontent: response.content.flatMap(block => describeBlock(block, logger)),\n\t\t\t},\n\t\t\tusage,\n\t\t\tstopReason: toStopReason(response),\n\t\t};\n\t} catch (error) {\n\t\t// The call was billed whether the response failed or a logger did while mapping it, and\n\t\t// an answer without usage leaves the run's count unknown.\n\t\tthrow withSpentTokens(error, usage, !response.usage);\n\t}\n}\n\n/**\n * The SDK resolves credentials lazily, so a missing key surfaces on the first call, as a plain\n * `Error` the SDK gives no class of its own. An `AnthropicError` that is not an `APIError` never\n * carries an answer from the API: on this client's path it is the SDK refusing to make the\n * call, such as an unresolved profile or a request too large to send without streaming.\n */\nfunction isLocalSDKFailure(error: unknown): boolean {\n\treturn (\n\t\t(error instanceof AnthropicError && !(error instanceof APIError)) ||\n\t\t(error instanceof Error && error.message.startsWith('Could not resolve authentication method'))\n\t);\n}\n\nfunction classifyClaudeFailure(error: unknown): Error {\n\tif (isLocalSDKFailure(error)) {\n\t\t// The API never answered, and calling again would fail the same way.\n\t\treturn new UnrecoverableError('The Claude client cannot send the request', {\n\t\t\tcause: describeFailure(error),\n\t\t});\n\t}\n\tif (error instanceof APIError) {\n\t\t// Anthropic gives an overlong prompt no code of its own, only a 400 with this message.\n\t\tif (error.status === 400 && error.message.includes('prompt is too long')) {\n\t\t\treturn new MaxContextError('The context no longer fits the Claude model', {\n\t\t\t\tcause: describeFailure(error),\n\t\t\t});\n\t\t}\n\t\tif (\n\t\t\tunrecoverableErrorTypes.some(type => type === error.type) ||\n\t\t\tunrecoverableStatuses.some(status => status === error.status)\n\t\t) {\n\t\t\treturn new UnrecoverableError('Claude rejected the request', {\n\t\t\t\tcause: describeFailure(error),\n\t\t\t});\n\t\t}\n\t}\n\n\t// A rate limit, an overload (529), a 5xx or a network failure: a later attempt can succeed.\n\treturn classifyProviderFailure(error, 'The Claude request failed');\n}\n\n/**\n * A stateless `LLMClient` on the Anthropic Messages API, which keeps nothing between calls:\n * every call carries the whole context, so the conversation is MiKode's, not the provider's.\n */\nexport class ClaudeLLMClient implements LLMClient {\n\tprivate readonly client: Anthropic;\n\tprivate readonly model: (typeof claudeLLMModelIds)[ClaudeLLMModel];\n\t// Missing for Haiku, which runs without thinking.\n\tprivate readonly reasoningEffort: ClaudeLLMReasoningEffort | undefined;\n\tprivate readonly compactAt: number | undefined;\n\treadonly systemPrompt: string;\n\treadonly providerDataSource = claudeSource;\n\tprivate readonly logger: ILogger;\n\n\t// `model` and `reasoningEffort` are untyped on purpose: this adapter is the one that knows\n\t// what Claude supports.\n\tconstructor({\n\t\tmodel,\n\t\treasoningEffort,\n\t\tcompactAt,\n\t\tsystemPrompt,\n\t\tlogger,\n\t}: {\n\t\tmodel: string;\n\t\treasoningEffort?: string;\n\t\t/** The input tokens at which Anthropic compacts the context on its side. Without it, never. */\n\t\tcompactAt?: number | undefined;\n\t\tsystemPrompt: string;\n\t\tlogger: ILogger;\n\t}) {\n\t\tif (!isOneOf(claudeLLMModels, model)) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`\"${model}\" is not a Claude API model; expected one of: ${claudeLLMModels.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\tif (model === 'haiku' && reasoningEffort !== undefined) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t'Claude Haiku takes no reasoning effort: it runs without thinking',\n\t\t\t);\n\t\t}\n\t\tconst effort = model === 'haiku' ? undefined : (reasoningEffort ?? 'high');\n\t\tif (effort !== undefined && !isOneOf(claudeLLMReasoningEfforts, effort)) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`\"${effort}\" is not a Claude API reasoning effort; expected one of: ${claudeLLMReasoningEfforts.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\tif (compactAt !== undefined) {\n\t\t\t// The alias names Claude Haiku 4.5, which Anthropic does not compact.\n\t\t\tif (model === 'haiku') {\n\t\t\t\tthrow new InvalidAgentConfigError('Claude Haiku cannot compact: leave compactAt out');\n\t\t\t}\n\t\t\tif (!(Number.isInteger(compactAt) && compactAt >= minCompactAt)) {\n\t\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t\t`compactAt must be a whole number of tokens of at least ${String(minCompactAt)} on Claude, not ${String(compactAt)}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\t// The SDK's default retries stay on, as OpenAI's do. See \"The provider SDKs keep their\n\t\t\t// transport retries under RetryingAgent\" in decisions.md.\n\t\t\tthis.client = new Anthropic();\n\t\t} catch (error) {\n\t\t\t// Conflicting credential options, for example: building it again cannot fix them. A\n\t\t\t// missing key does not fail here but on the first call; see `isLocalSDKFailure`.\n\t\t\tthrow new UnrecoverableError('The Claude client could not be set up', {\n\t\t\t\tcause: describeFailure(error),\n\t\t\t});\n\t\t}\n\n\t\tthis.model = claudeLLMModelIds[model];\n\t\tthis.reasoningEffort = effort;\n\t\tthis.compactAt = compactAt;\n\t\tthis.systemPrompt = systemPrompt;\n\t\tthis.logger = logger;\n\t}\n\n\tsupportsNative(tool: NativeTool): boolean {\n\t\treturn tool === 'textEditor';\n\t}\n\n\tasync send(\n\t\t{ context, tools }: { context: Message[]; tools: ToolDefinition[] },\n\t\tsignal: AbortSignal,\n\t): Promise<LLMResponse> {\n\t\tlet response: AnthropicMessage;\n\n\t\ttry {\n\t\t\tresponse = await this.client.beta.messages.create(\n\t\t\t\t{\n\t\t\t\t\tmodel: this.model,\n\t\t\t\t\tmax_tokens: maxOutputTokens,\n\t\t\t\t\t// A compaction changes the conversation's first block, so the cache would otherwise\n\t\t\t\t\t// keep nothing: this keeps the system prompt cached across it. The API rejects an\n\t\t\t\t\t// empty text block, and an empty prompt has nothing to cache.\n\t\t\t\t\tsystem:\n\t\t\t\t\t\tthis.compactAt === undefined || this.systemPrompt === ''\n\t\t\t\t\t\t\t? this.systemPrompt\n\t\t\t\t\t\t\t: [{ type: 'text', text: this.systemPrompt, cache_control: { type: 'ephemeral' } }],\n\t\t\t\t\t// The latest compaction carries everything before it, which Anthropic would only ignore.\n\t\t\t\t\tmessages: sentContext(context, claudeSource).flatMap(toClaudeInput),\n\t\t\t\t\t// Left out when empty, so a request without tools stays as it always was.\n\t\t\t\t\t...(tools.length > 0 && { tools: tools.map(toClaudeTool) }),\n\t\t\t\t\t// Haiku has no adaptive thinking, and an effort is refused with a 400.\n\t\t\t\t\t...(this.reasoningEffort !== undefined && {\n\t\t\t\t\t\tthinking: { type: 'adaptive', display: 'summarized' },\n\t\t\t\t\t\toutput_config: { effort: this.reasoningEffort },\n\t\t\t\t\t}),\n\t\t\t\t\t// Anthropic caches only on request. This marks the last block, so the next call\n\t\t\t\t\t// reads everything before it from the cache instead of paying for it again.\n\t\t\t\t\tcache_control: { type: 'ephemeral' },\n\t\t\t\t\t// Anthropic compacts inside the call that passes it, and answers with the compaction first.\n\t\t\t\t\t...(this.compactAt !== undefined && {\n\t\t\t\t\t\tbetas: [compactionBeta],\n\t\t\t\t\t\tcontext_management: {\n\t\t\t\t\t\t\tedits: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: 'compact_20260112',\n\t\t\t\t\t\t\t\t\ttrigger: { type: 'input_tokens', value: this.compactAt },\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t\t{ signal },\n\t\t\t);\n\t\t} catch (error) {\n\t\t\t// The SDK's abort error is named 'Error', so it would pass for a provider failure.\n\t\t\tsignal.throwIfAborted();\n\t\t\tthrow classifyClaudeFailure(error);\n\t\t}\n\n\t\treturn treatErrors(\n\t\t\t() => toLLMResponse(response, this.logger),\n\t\t\tclassifyLocalFailure,\n\t\t\t'Claude response mapping failed',\n\t\t);\n\t}\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ILogger } from '#src/shared/domain/logger';
|
|
2
2
|
import type { LLMClient, LLMResponse } from '../domain/llm.ts';
|
|
3
|
-
import type
|
|
3
|
+
import { type Message } from '../domain/message.ts';
|
|
4
4
|
import { type NativeTool, type ToolDefinition } from '../domain/tool.ts';
|
|
5
5
|
export declare const openAIModels: readonly ["gpt-6-astra", "gpt-5.6-sol", "gpt-5.6-luna", "gpt-5.6-terra"];
|
|
6
6
|
export type OpenAIModel = (typeof openAIModels)[number];
|
|
@@ -15,12 +15,15 @@ export declare class OpenAILLMClient implements LLMClient {
|
|
|
15
15
|
private readonly client;
|
|
16
16
|
private readonly model;
|
|
17
17
|
private readonly reasoningEffort;
|
|
18
|
+
private readonly compactAt;
|
|
18
19
|
readonly systemPrompt: string;
|
|
19
20
|
readonly providerDataSource = "openai";
|
|
20
21
|
private readonly logger;
|
|
21
|
-
constructor({ model, reasoningEffort, systemPrompt, logger, }: {
|
|
22
|
+
constructor({ model, reasoningEffort, compactAt, systemPrompt, logger, }: {
|
|
22
23
|
model: string;
|
|
23
24
|
reasoningEffort?: string;
|
|
25
|
+
/** The input tokens at which OpenAI compacts the context on its side. Without it, never. */
|
|
26
|
+
compactAt?: number | undefined;
|
|
24
27
|
systemPrompt: string;
|
|
25
28
|
logger: ILogger;
|
|
26
29
|
});
|
|
@@ -3,6 +3,7 @@ import { InvalidAgentConfigError, RecoverableError, UnrecoverableError, withSpen
|
|
|
3
3
|
import { classifyHostFailure, classifyLocalFailure, classifyProviderFailure, describeFailure, treatErrors, } from '#src/shared/domain/providerFailure';
|
|
4
4
|
import { isOneOf } from '#src/shared/domain/isOneOf';
|
|
5
5
|
import { MaxContextError } from "../domain/errors.js";
|
|
6
|
+
import { sentContext } from "../domain/message.js";
|
|
6
7
|
import { nativeOf } from "../domain/tool.js";
|
|
7
8
|
// The SDK types `model` as a plain string, so this list is maintained by hand.
|
|
8
9
|
export const openAIModels = [
|
|
@@ -88,6 +89,9 @@ function toOpenAIItems(part, role, applyPatch) {
|
|
|
88
89
|
case 'providerData':
|
|
89
90
|
// Another provider's item would be rejected; losing it only costs that provider's reasoning.
|
|
90
91
|
return part.source === openAISource ? [part.data] : [];
|
|
92
|
+
case 'compaction':
|
|
93
|
+
// Another provider's summary means nothing here; this client sends the turns it replaced.
|
|
94
|
+
return part.source === openAISource ? [part.data] : [];
|
|
91
95
|
case 'reasoning':
|
|
92
96
|
return [];
|
|
93
97
|
}
|
|
@@ -150,6 +154,15 @@ function describeItem(item, logger, applyPatch) {
|
|
|
150
154
|
}
|
|
151
155
|
return parts;
|
|
152
156
|
}
|
|
157
|
+
case 'compaction': {
|
|
158
|
+
// Encrypted: OpenAI's summary of the context before it, which it alone reads back.
|
|
159
|
+
const compaction = {
|
|
160
|
+
type: 'compaction',
|
|
161
|
+
id: item.id,
|
|
162
|
+
encrypted_content: item.encrypted_content,
|
|
163
|
+
};
|
|
164
|
+
return [{ type: 'compaction', source: openAISource, data: compaction }];
|
|
165
|
+
}
|
|
153
166
|
case 'function_call':
|
|
154
167
|
return [
|
|
155
168
|
{
|
|
@@ -255,18 +268,22 @@ export class OpenAILLMClient {
|
|
|
255
268
|
client;
|
|
256
269
|
model;
|
|
257
270
|
reasoningEffort;
|
|
271
|
+
compactAt;
|
|
258
272
|
systemPrompt;
|
|
259
273
|
providerDataSource = openAISource;
|
|
260
274
|
logger;
|
|
261
275
|
// `model` and `reasoningEffort` are untyped on purpose: this adapter is the one that knows
|
|
262
276
|
// what OpenAI supports.
|
|
263
|
-
constructor({ model, reasoningEffort = 'high', systemPrompt, logger, }) {
|
|
277
|
+
constructor({ model, reasoningEffort = 'high', compactAt, systemPrompt, logger, }) {
|
|
264
278
|
if (!isOneOf(openAIModels, model)) {
|
|
265
279
|
throw new InvalidAgentConfigError(`"${model}" is not an OpenAI model; expected one of: ${openAIModels.join(', ')}`);
|
|
266
280
|
}
|
|
267
281
|
if (!isOneOf(openAIReasoningEfforts, reasoningEffort)) {
|
|
268
282
|
throw new InvalidAgentConfigError(`"${reasoningEffort}" is not an OpenAI reasoning effort; expected one of: ${openAIReasoningEfforts.join(', ')}`);
|
|
269
283
|
}
|
|
284
|
+
if (compactAt !== undefined && !(Number.isInteger(compactAt) && compactAt > 0)) {
|
|
285
|
+
throw new InvalidAgentConfigError(`compactAt must be a positive whole number of tokens, not ${String(compactAt)}`);
|
|
286
|
+
}
|
|
270
287
|
try {
|
|
271
288
|
// The SDK's default retries stay on: they back off and honour `Retry-After`, which
|
|
272
289
|
// `RetryingAgent` does not. See "The provider SDKs keep their transport retries" in decisions.md.
|
|
@@ -280,6 +297,7 @@ export class OpenAILLMClient {
|
|
|
280
297
|
}
|
|
281
298
|
this.model = model;
|
|
282
299
|
this.reasoningEffort = reasoningEffort;
|
|
300
|
+
this.compactAt = compactAt;
|
|
283
301
|
this.systemPrompt = systemPrompt;
|
|
284
302
|
this.logger = logger;
|
|
285
303
|
}
|
|
@@ -293,9 +311,14 @@ export class OpenAILLMClient {
|
|
|
293
311
|
response = await this.client.responses.create({
|
|
294
312
|
model: this.model,
|
|
295
313
|
instructions: this.systemPrompt,
|
|
296
|
-
|
|
314
|
+
// The latest compaction carries everything before it, which OpenAI would only ignore.
|
|
315
|
+
input: sentContext(context, openAISource).flatMap(message => toOpenAIInput(message, applyPatch)),
|
|
297
316
|
// Left out when empty, so a request without tools stays as it always was.
|
|
298
317
|
...(tools.length > 0 && { tools: tools.map(toOpenAITool) }),
|
|
318
|
+
// OpenAI compacts inside the call that passes it, and answers with the compaction first.
|
|
319
|
+
...(this.compactAt !== undefined && {
|
|
320
|
+
context_management: [{ type: 'compaction', compact_threshold: this.compactAt }],
|
|
321
|
+
}),
|
|
299
322
|
store: false,
|
|
300
323
|
reasoning: { effort: this.reasoningEffort, summary: 'concise' },
|
|
301
324
|
// With `store: false` this is the only way to send the reasoning back.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openAILLMClient.js","sourceRoot":"","sources":["../../../src/llm/infrastructure/openAILLMClient.ts"],"names":[],"mappings":"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","sourcesContent":["import OpenAI, { APIError } from 'openai';\nimport type { ReasoningEffort } from 'openai/resources/shared';\nimport type {\n\tApplyPatchTool,\n\tFunctionTool,\n\tResponse,\n\tResponseApplyPatchToolCall,\n\tResponseInputItem,\n\tResponseOutputItem,\n\tResponseReasoningItem,\n\tResponseUsage,\n} from 'openai/resources/responses/responses';\nimport {\n\tInvalidAgentConfigError,\n\tRecoverableError,\n\tUnrecoverableError,\n\twithSpentTokens,\n} from '#src/shared/domain/errors';\nimport {\n\tclassifyHostFailure,\n\tclassifyLocalFailure,\n\tclassifyProviderFailure,\n\tdescribeFailure,\n\ttreatErrors,\n} from '#src/shared/domain/providerFailure';\nimport { isOneOf } from '#src/shared/domain/isOneOf';\nimport type { ILogger } from '#src/shared/domain/logger';\nimport type { Tokens } from '#src/shared/domain/tokens';\nimport { MaxContextError } from '../domain/errors.ts';\nimport type { LLMClient, LLMResponse, StopReason } from '../domain/llm.ts';\nimport type { Message, MessagePart } from '../domain/message.ts';\nimport { type NativeTool, nativeOf, type ToolDefinition } from '../domain/tool.ts';\n\n// The SDK types `model` as a plain string, so this list is maintained by hand.\nexport const openAIModels = [\n\t'gpt-6-astra',\n\t'gpt-5.6-sol',\n\t'gpt-5.6-luna',\n\t'gpt-5.6-terra',\n] as const;\nexport type OpenAIModel = (typeof openAIModels)[number];\n\n// The efforts both the Responses API and Codex's model catalog list for the models above.\nexport const openAIReasoningEfforts = [\n\t'low',\n\t'medium',\n\t'high',\n\t'xhigh',\n\t'max',\n] as const satisfies readonly NonNullable<ReasoningEffort>[];\nexport type OpenAIReasoningEffort = (typeof openAIReasoningEfforts)[number];\n\n// The request itself is wrong or not allowed, so sending it again cannot succeed.\nconst unrecoverableStatuses = [400, 401, 403, 404, 422];\n\n// Out of credit or over a spend limit. These arrive as a 429 like a rate limit, but the account\n// stays blocked however often the request is sent.\nconst exhaustedQuotaCodes = [\n\t'insufficient_quota',\n\t'credit_balance_exhausted',\n\t'project_spend_limit_exceeded',\n];\n\n// The codes OpenAI gives a failed response that a new attempt can succeed past.\nconst transientResponseErrorCodes = ['server_error', 'rate_limit_exceeded'];\n\n// This client's label on the reasoning items it keeps for replay; see `ProviderDataPart`.\nconst openAISource = 'openai';\n\n// Marks a failed call's output, which would otherwise read the same as a successful one.\nconst failedToolPrefix = 'The tool call failed: ';\n\n/**\n * OpenAI takes a flat list of items, so every part becomes its own, in order. Reasoning crosses\n * back only as the encrypted item this client kept, which OpenAI requires beside the tool calls\n * that followed it; a reasoning part is its summary, kept for narration. A call to the tool\n * offered as `apply_patch`, named `applyPatch` here, and its result go back as that tool's own\n * items.\n */\nfunction toOpenAIItems(\n\tpart: MessagePart,\n\trole: 'user' | 'assistant',\n\tapplyPatch: string | undefined,\n): ResponseInputItem[] {\n\tswitch (part.type) {\n\t\tcase 'text':\n\t\t\treturn part.text ? [{ role, content: part.text }] : [];\n\t\tcase 'toolCall':\n\t\t\tif (part.name === applyPatch) {\n\t\t\t\treturn [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'apply_patch_call',\n\t\t\t\t\t\tcall_id: part.id,\n\t\t\t\t\t\t// Kept as OpenAI sent it: the conversation holds the operation unchanged.\n\t\t\t\t\t\toperation: part.input as ResponseApplyPatchToolCall['operation'],\n\t\t\t\t\t\tstatus: 'completed',\n\t\t\t\t\t},\n\t\t\t\t];\n\t\t\t}\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'function_call',\n\t\t\t\t\tcall_id: part.id,\n\t\t\t\t\tname: part.name,\n\t\t\t\t\t// A string is arguments that did not parse, sent back as the model wrote them.\n\t\t\t\t\targuments: typeof part.input === 'string' ? part.input : JSON.stringify(part.input),\n\t\t\t\t},\n\t\t\t];\n\t\tcase 'toolResult':\n\t\t\tif (part.name === applyPatch) {\n\t\t\t\treturn [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'apply_patch_call_output',\n\t\t\t\t\t\tcall_id: part.callId,\n\t\t\t\t\t\tstatus: part.isError ? 'failed' : 'completed',\n\t\t\t\t\t\toutput: part.output,\n\t\t\t\t\t},\n\t\t\t\t];\n\t\t\t}\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'function_call_output',\n\t\t\t\t\tcall_id: part.callId,\n\t\t\t\t\t// OpenAI has no error flag, so the text says it; the conversation keeps the output as it was.\n\t\t\t\t\toutput: part.isError ? `${failedToolPrefix}${part.output}` : part.output,\n\t\t\t\t},\n\t\t\t];\n\t\tcase 'providerData':\n\t\t\t// Another provider's item would be rejected; losing it only costs that provider's reasoning.\n\t\t\treturn part.source === openAISource ? [part.data as ResponseReasoningItem] : [];\n\t\tcase 'reasoning':\n\t\t\treturn [];\n\t}\n}\n\nfunction toOpenAIInput(message: Message, applyPatch: string | undefined): ResponseInputItem[] {\n\t// A tool message holds only results, which carry no role.\n\tconst role = message.role === 'assistant' ? 'assistant' : 'user';\n\treturn message.content.flatMap(part => toOpenAIItems(part, role, applyPatch));\n}\n\n/**\n * Strict: the API constrains the model to the schema, so its arguments always parse and match.\n * A schema outside what strict mode supports fails the request with a 400. The native\n * `apply_patch` has OpenAI's own schema.\n */\nfunction toOpenAITool(definition: ToolDefinition): FunctionTool | ApplyPatchTool {\n\tconst native = nativeOf(definition);\n\tif (native === 'applyPatch') return { type: 'apply_patch' };\n\tif (native !== undefined) {\n\t\tthrow new InvalidAgentConfigError(`OpenAI has no native tool \"${native}\"`);\n\t}\n\tconst { name, description, inputSchema } = definition;\n\treturn { type: 'function', name, description, parameters: inputSchema, strict: true };\n}\n\n/** The name the tool offered as `apply_patch` has in the conversation, if one is offered. */\nfunction applyPatchName(tools: ToolDefinition[]): string | undefined {\n\treturn tools.find(tool => nativeOf(tool) === 'applyPatch')?.name;\n}\n\n/**\n * Strict mode makes arguments that do not parse rare: a response cut short mid-call ends as\n * `truncated` before any tool runs. Should one arrive, the tool gets the raw string and its\n * rejection lets the model correct itself, instead of the run failing.\n */\nfunction parseArguments(serialized: string): unknown {\n\ttry {\n\t\treturn JSON.parse(serialized);\n\t} catch {\n\t\treturn serialized;\n\t}\n}\n\nfunction describeItem(\n\titem: ResponseOutputItem,\n\tlogger: ILogger,\n\tapplyPatch: string | undefined,\n): MessagePart[] {\n\tswitch (item.type) {\n\t\tcase 'message':\n\t\t\t// A refusal is not an answer: it only sets the stop reason.\n\t\t\treturn item.content.flatMap(content =>\n\t\t\t\tcontent.type === 'output_text' ? [{ type: 'text' as const, text: content.text }] : [],\n\t\t\t);\n\t\tcase 'reasoning': {\n\t\t\t// The summary is the readable part; the encrypted item is what OpenAI reads back.\n\t\t\tconst text = item.summary.map(summary => summary.text).join('\\n');\n\t\t\tconst parts: MessagePart[] = text ? [{ type: 'reasoning', text }] : [];\n\t\t\t// Without its encrypted content the item cannot be replayed: OpenAI stores nothing.\n\t\t\tif (item.encrypted_content) {\n\t\t\t\tconst encrypted: ResponseReasoningItem = {\n\t\t\t\t\ttype: 'reasoning',\n\t\t\t\t\tid: item.id,\n\t\t\t\t\tsummary: item.summary,\n\t\t\t\t\tencrypted_content: item.encrypted_content,\n\t\t\t\t};\n\t\t\t\tparts.push({ type: 'providerData', source: openAISource, data: encrypted });\n\t\t\t}\n\t\t\treturn parts;\n\t\t}\n\t\tcase 'function_call':\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'toolCall',\n\t\t\t\t\tid: item.call_id,\n\t\t\t\t\tname: item.name,\n\t\t\t\t\tinput: parseArguments(item.arguments),\n\t\t\t\t},\n\t\t\t];\n\t\tcase 'apply_patch_call':\n\t\t\t// Only when `apply_patch` was offered; otherwise it is unexpected, as below.\n\t\t\tif (applyPatch !== undefined) {\n\t\t\t\treturn [{ type: 'toolCall', id: item.call_id, name: applyPatch, input: item.operation }];\n\t\t\t}\n\t\t\treturn unmapped(item, logger);\n\t\tdefault:\n\t\t\treturn unmapped(item, logger);\n\t}\n}\n\n/** An item this client does not map, which it reports and leaves out. */\nfunction unmapped(item: ResponseOutputItem, logger: ILogger): MessagePart[] {\n\t// No other built-in tool is offered, so any other item is output this client does not expect.\n\ttreatErrors(\n\t\t() => {\n\t\t\tlogger.warn(item, 'OpenAI returned an output item the client does not map');\n\t\t},\n\t\tclassifyHostFailure,\n\t\t'OpenAI client logger failed while mapping a response',\n\t);\n\treturn [];\n}\n\nfunction toStopReason(response: Response): StopReason {\n\tif (response.status === 'incomplete') {\n\t\treturn response.incomplete_details?.reason === 'content_filter' ? 'refused' : 'truncated';\n\t}\n\tif (response.status !== 'completed') {\n\t\tconst cause =\n\t\t\tresponse.error?.message ??\n\t\t\t`The response ended with status \"${response.status ?? 'unknown'}\".`;\n\t\t// The same failure is retried when it arrives as an SDK error, so it must be here too.\n\t\tif (response.error && transientResponseErrorCodes.includes(response.error.code)) {\n\t\t\tthrow new RecoverableError('The OpenAI response failed', { cause });\n\t\t}\n\t\tthrow new UnrecoverableError('The OpenAI response did not complete', { cause });\n\t}\n\n\tconst refused = response.output.some(\n\t\titem => item.type === 'message' && item.content.some(content => content.type === 'refusal'),\n\t);\n\treturn refused ? 'refused' : 'completed';\n}\n\n/** OpenAI counts both cache reads and cache writes inside `input_tokens`. */\nfunction toTokens(usage: ResponseUsage | undefined, logger: ILogger): Tokens | undefined {\n\tif (!usage) {\n\t\t// Zeros would report a call that may have been billed as free.\n\t\ttreatErrors(\n\t\t\t() => {\n\t\t\t\tlogger.warn('OpenAI returned a response without usage; it cannot be accounted for');\n\t\t\t},\n\t\t\tclassifyHostFailure,\n\t\t\t'OpenAI client logger failed while reading usage',\n\t\t);\n\t\treturn undefined;\n\t}\n\n\tconst { cached_tokens, cache_write_tokens } = usage.input_tokens_details;\n\treturn {\n\t\tinputTokens: usage.input_tokens - cached_tokens - cache_write_tokens,\n\t\treadCacheTokens: cached_tokens,\n\t\twrittenCacheTokens: cache_write_tokens,\n\t\toutputTokens: usage.output_tokens,\n\t};\n}\n\nfunction toLLMResponse(\n\tresponse: Response,\n\tlogger: ILogger,\n\tapplyPatch: string | undefined,\n): LLMResponse {\n\tlet usage: Tokens | undefined;\n\n\ttry {\n\t\t// Inside the try: it warns when the usage is missing, and that warning can throw too.\n\t\tusage = toTokens(response.usage, logger);\n\t\treturn {\n\t\t\tmessage: {\n\t\t\t\trole: 'assistant',\n\t\t\t\tcontent: response.output.flatMap(item => describeItem(item, logger, applyPatch)),\n\t\t\t},\n\t\t\tusage,\n\t\t\tstopReason: toStopReason(response),\n\t\t};\n\t} catch (error) {\n\t\t// The call was billed whether the response failed or a logger did while mapping it, and\n\t\t// an answer without usage leaves the run's count unknown.\n\t\tthrow withSpentTokens(error, usage, !response.usage);\n\t}\n}\n\nfunction classifyOpenAIFailure(error: unknown): Error {\n\tif (error instanceof APIError) {\n\t\tif (error.code === 'context_length_exceeded') {\n\t\t\treturn new MaxContextError('The context no longer fits the OpenAI model', {\n\t\t\t\tcause: describeFailure(error),\n\t\t\t});\n\t\t}\n\t\tif (\n\t\t\texhaustedQuotaCodes.some(code => code === error.code) ||\n\t\t\tunrecoverableStatuses.some(status => status === error.status)\n\t\t) {\n\t\t\treturn new UnrecoverableError('OpenAI rejected the request', {\n\t\t\t\tcause: describeFailure(error),\n\t\t\t});\n\t\t}\n\t}\n\n\treturn classifyProviderFailure(error, 'The OpenAI request failed');\n}\n\n/**\n * A stateless `LLMClient` on the OpenAI Responses API. With `store: false` OpenAI keeps\n * nothing between calls, so every call carries the whole context: the conversation is\n * MiKode's, not the provider's.\n */\nexport class OpenAILLMClient implements LLMClient {\n\tprivate readonly client: OpenAI;\n\tprivate readonly model: OpenAIModel;\n\tprivate readonly reasoningEffort: OpenAIReasoningEffort;\n\treadonly systemPrompt: string;\n\treadonly providerDataSource = openAISource;\n\tprivate readonly logger: ILogger;\n\n\t// `model` and `reasoningEffort` are untyped on purpose: this adapter is the one that knows\n\t// what OpenAI supports.\n\tconstructor({\n\t\tmodel,\n\t\treasoningEffort = 'high',\n\t\tsystemPrompt,\n\t\tlogger,\n\t}: {\n\t\tmodel: string;\n\t\treasoningEffort?: string;\n\t\tsystemPrompt: string;\n\t\tlogger: ILogger;\n\t}) {\n\t\tif (!isOneOf(openAIModels, model)) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`\"${model}\" is not an OpenAI model; expected one of: ${openAIModels.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\tif (!isOneOf(openAIReasoningEfforts, reasoningEffort)) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`\"${reasoningEffort}\" is not an OpenAI reasoning effort; expected one of: ${openAIReasoningEfforts.join(', ')}`,\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\t// The SDK's default retries stay on: they back off and honour `Retry-After`, which\n\t\t\t// `RetryingAgent` does not. See \"The provider SDKs keep their transport retries\" in decisions.md.\n\t\t\tthis.client = new OpenAI();\n\t\t} catch (error) {\n\t\t\t// A missing API key, for example: building the client again cannot fix it.\n\t\t\tthrow new UnrecoverableError('The OpenAI client could not be set up', {\n\t\t\t\tcause: describeFailure(error),\n\t\t\t});\n\t\t}\n\n\t\tthis.model = model;\n\t\tthis.reasoningEffort = reasoningEffort;\n\t\tthis.systemPrompt = systemPrompt;\n\t\tthis.logger = logger;\n\t}\n\n\tsupportsNative(tool: NativeTool): boolean {\n\t\treturn tool === 'applyPatch';\n\t}\n\n\tasync send(\n\t\t{ context, tools }: { context: Message[]; tools: ToolDefinition[] },\n\t\tsignal: AbortSignal,\n\t): Promise<LLMResponse> {\n\t\tlet response: Response;\n\t\tconst applyPatch = applyPatchName(tools);\n\n\t\ttry {\n\t\t\tresponse = await this.client.responses.create(\n\t\t\t\t{\n\t\t\t\t\tmodel: this.model,\n\t\t\t\t\tinstructions: this.systemPrompt,\n\t\t\t\t\tinput: context.flatMap(message => toOpenAIInput(message, applyPatch)),\n\t\t\t\t\t// Left out when empty, so a request without tools stays as it always was.\n\t\t\t\t\t...(tools.length > 0 && { tools: tools.map(toOpenAITool) }),\n\t\t\t\t\tstore: false,\n\t\t\t\t\treasoning: { effort: this.reasoningEffort, summary: 'concise' },\n\t\t\t\t\t// With `store: false` this is the only way to send the reasoning back.\n\t\t\t\t\tinclude: ['reasoning.encrypted_content'],\n\t\t\t\t},\n\t\t\t\t{ signal },\n\t\t\t);\n\t\t} catch (error) {\n\t\t\t// The SDK's abort error is named 'Error', so it would pass for a provider failure.\n\t\t\tsignal.throwIfAborted();\n\t\t\tthrow classifyOpenAIFailure(error);\n\t\t}\n\n\t\treturn treatErrors(\n\t\t\t() => toLLMResponse(response, this.logger, applyPatch),\n\t\t\tclassifyLocalFailure,\n\t\t\t'OpenAI response mapping failed',\n\t\t);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"openAILLMClient.js","sourceRoot":"","sources":["../../../src/llm/infrastructure/openAILLMClient.ts"],"names":[],"mappings":"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","sourcesContent":["import OpenAI, { APIError } from 'openai';\nimport type { ReasoningEffort } from 'openai/resources/shared';\nimport type {\n\tApplyPatchTool,\n\tFunctionTool,\n\tResponse,\n\tResponseApplyPatchToolCall,\n\tResponseCompactionItemParam,\n\tResponseInputItem,\n\tResponseOutputItem,\n\tResponseReasoningItem,\n\tResponseUsage,\n} from 'openai/resources/responses/responses';\nimport {\n\tInvalidAgentConfigError,\n\tRecoverableError,\n\tUnrecoverableError,\n\twithSpentTokens,\n} from '#src/shared/domain/errors';\nimport {\n\tclassifyHostFailure,\n\tclassifyLocalFailure,\n\tclassifyProviderFailure,\n\tdescribeFailure,\n\ttreatErrors,\n} from '#src/shared/domain/providerFailure';\nimport { isOneOf } from '#src/shared/domain/isOneOf';\nimport type { ILogger } from '#src/shared/domain/logger';\nimport type { Tokens } from '#src/shared/domain/tokens';\nimport { MaxContextError } from '../domain/errors.ts';\nimport type { LLMClient, LLMResponse, StopReason } from '../domain/llm.ts';\nimport { type Message, type MessagePart, sentContext } from '../domain/message.ts';\nimport { type NativeTool, nativeOf, type ToolDefinition } from '../domain/tool.ts';\n\n// The SDK types `model` as a plain string, so this list is maintained by hand.\nexport const openAIModels = [\n\t'gpt-6-astra',\n\t'gpt-5.6-sol',\n\t'gpt-5.6-luna',\n\t'gpt-5.6-terra',\n] as const;\nexport type OpenAIModel = (typeof openAIModels)[number];\n\n// The efforts both the Responses API and Codex's model catalog list for the models above.\nexport const openAIReasoningEfforts = [\n\t'low',\n\t'medium',\n\t'high',\n\t'xhigh',\n\t'max',\n] as const satisfies readonly NonNullable<ReasoningEffort>[];\nexport type OpenAIReasoningEffort = (typeof openAIReasoningEfforts)[number];\n\n// The request itself is wrong or not allowed, so sending it again cannot succeed.\nconst unrecoverableStatuses = [400, 401, 403, 404, 422];\n\n// Out of credit or over a spend limit. These arrive as a 429 like a rate limit, but the account\n// stays blocked however often the request is sent.\nconst exhaustedQuotaCodes = [\n\t'insufficient_quota',\n\t'credit_balance_exhausted',\n\t'project_spend_limit_exceeded',\n];\n\n// The codes OpenAI gives a failed response that a new attempt can succeed past.\nconst transientResponseErrorCodes = ['server_error', 'rate_limit_exceeded'];\n\n// This client's label on the reasoning items it keeps for replay; see `ProviderDataPart`.\nconst openAISource = 'openai';\n\n// Marks a failed call's output, which would otherwise read the same as a successful one.\nconst failedToolPrefix = 'The tool call failed: ';\n\n/**\n * OpenAI takes a flat list of items, so every part becomes its own, in order. Reasoning crosses\n * back only as the encrypted item this client kept, which OpenAI requires beside the tool calls\n * that followed it; a reasoning part is its summary, kept for narration. A call to the tool\n * offered as `apply_patch`, named `applyPatch` here, and its result go back as that tool's own\n * items.\n */\nfunction toOpenAIItems(\n\tpart: MessagePart,\n\trole: 'user' | 'assistant',\n\tapplyPatch: string | undefined,\n): ResponseInputItem[] {\n\tswitch (part.type) {\n\t\tcase 'text':\n\t\t\treturn part.text ? [{ role, content: part.text }] : [];\n\t\tcase 'toolCall':\n\t\t\tif (part.name === applyPatch) {\n\t\t\t\treturn [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'apply_patch_call',\n\t\t\t\t\t\tcall_id: part.id,\n\t\t\t\t\t\t// Kept as OpenAI sent it: the conversation holds the operation unchanged.\n\t\t\t\t\t\toperation: part.input as ResponseApplyPatchToolCall['operation'],\n\t\t\t\t\t\tstatus: 'completed',\n\t\t\t\t\t},\n\t\t\t\t];\n\t\t\t}\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'function_call',\n\t\t\t\t\tcall_id: part.id,\n\t\t\t\t\tname: part.name,\n\t\t\t\t\t// A string is arguments that did not parse, sent back as the model wrote them.\n\t\t\t\t\targuments: typeof part.input === 'string' ? part.input : JSON.stringify(part.input),\n\t\t\t\t},\n\t\t\t];\n\t\tcase 'toolResult':\n\t\t\tif (part.name === applyPatch) {\n\t\t\t\treturn [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'apply_patch_call_output',\n\t\t\t\t\t\tcall_id: part.callId,\n\t\t\t\t\t\tstatus: part.isError ? 'failed' : 'completed',\n\t\t\t\t\t\toutput: part.output,\n\t\t\t\t\t},\n\t\t\t\t];\n\t\t\t}\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'function_call_output',\n\t\t\t\t\tcall_id: part.callId,\n\t\t\t\t\t// OpenAI has no error flag, so the text says it; the conversation keeps the output as it was.\n\t\t\t\t\toutput: part.isError ? `${failedToolPrefix}${part.output}` : part.output,\n\t\t\t\t},\n\t\t\t];\n\t\tcase 'providerData':\n\t\t\t// Another provider's item would be rejected; losing it only costs that provider's reasoning.\n\t\t\treturn part.source === openAISource ? [part.data as ResponseReasoningItem] : [];\n\t\tcase 'compaction':\n\t\t\t// Another provider's summary means nothing here; this client sends the turns it replaced.\n\t\t\treturn part.source === openAISource ? [part.data as ResponseCompactionItemParam] : [];\n\t\tcase 'reasoning':\n\t\t\treturn [];\n\t}\n}\n\nfunction toOpenAIInput(message: Message, applyPatch: string | undefined): ResponseInputItem[] {\n\t// A tool message holds only results, which carry no role.\n\tconst role = message.role === 'assistant' ? 'assistant' : 'user';\n\treturn message.content.flatMap(part => toOpenAIItems(part, role, applyPatch));\n}\n\n/**\n * Strict: the API constrains the model to the schema, so its arguments always parse and match.\n * A schema outside what strict mode supports fails the request with a 400. The native\n * `apply_patch` has OpenAI's own schema.\n */\nfunction toOpenAITool(definition: ToolDefinition): FunctionTool | ApplyPatchTool {\n\tconst native = nativeOf(definition);\n\tif (native === 'applyPatch') return { type: 'apply_patch' };\n\tif (native !== undefined) {\n\t\tthrow new InvalidAgentConfigError(`OpenAI has no native tool \"${native}\"`);\n\t}\n\tconst { name, description, inputSchema } = definition;\n\treturn { type: 'function', name, description, parameters: inputSchema, strict: true };\n}\n\n/** The name the tool offered as `apply_patch` has in the conversation, if one is offered. */\nfunction applyPatchName(tools: ToolDefinition[]): string | undefined {\n\treturn tools.find(tool => nativeOf(tool) === 'applyPatch')?.name;\n}\n\n/**\n * Strict mode makes arguments that do not parse rare: a response cut short mid-call ends as\n * `truncated` before any tool runs. Should one arrive, the tool gets the raw string and its\n * rejection lets the model correct itself, instead of the run failing.\n */\nfunction parseArguments(serialized: string): unknown {\n\ttry {\n\t\treturn JSON.parse(serialized);\n\t} catch {\n\t\treturn serialized;\n\t}\n}\n\nfunction describeItem(\n\titem: ResponseOutputItem,\n\tlogger: ILogger,\n\tapplyPatch: string | undefined,\n): MessagePart[] {\n\tswitch (item.type) {\n\t\tcase 'message':\n\t\t\t// A refusal is not an answer: it only sets the stop reason.\n\t\t\treturn item.content.flatMap(content =>\n\t\t\t\tcontent.type === 'output_text' ? [{ type: 'text' as const, text: content.text }] : [],\n\t\t\t);\n\t\tcase 'reasoning': {\n\t\t\t// The summary is the readable part; the encrypted item is what OpenAI reads back.\n\t\t\tconst text = item.summary.map(summary => summary.text).join('\\n');\n\t\t\tconst parts: MessagePart[] = text ? [{ type: 'reasoning', text }] : [];\n\t\t\t// Without its encrypted content the item cannot be replayed: OpenAI stores nothing.\n\t\t\tif (item.encrypted_content) {\n\t\t\t\tconst encrypted: ResponseReasoningItem = {\n\t\t\t\t\ttype: 'reasoning',\n\t\t\t\t\tid: item.id,\n\t\t\t\t\tsummary: item.summary,\n\t\t\t\t\tencrypted_content: item.encrypted_content,\n\t\t\t\t};\n\t\t\t\tparts.push({ type: 'providerData', source: openAISource, data: encrypted });\n\t\t\t}\n\t\t\treturn parts;\n\t\t}\n\t\tcase 'compaction': {\n\t\t\t// Encrypted: OpenAI's summary of the context before it, which it alone reads back.\n\t\t\tconst compaction: ResponseCompactionItemParam = {\n\t\t\t\ttype: 'compaction',\n\t\t\t\tid: item.id,\n\t\t\t\tencrypted_content: item.encrypted_content,\n\t\t\t};\n\t\t\treturn [{ type: 'compaction', source: openAISource, data: compaction }];\n\t\t}\n\t\tcase 'function_call':\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: 'toolCall',\n\t\t\t\t\tid: item.call_id,\n\t\t\t\t\tname: item.name,\n\t\t\t\t\tinput: parseArguments(item.arguments),\n\t\t\t\t},\n\t\t\t];\n\t\tcase 'apply_patch_call':\n\t\t\t// Only when `apply_patch` was offered; otherwise it is unexpected, as below.\n\t\t\tif (applyPatch !== undefined) {\n\t\t\t\treturn [{ type: 'toolCall', id: item.call_id, name: applyPatch, input: item.operation }];\n\t\t\t}\n\t\t\treturn unmapped(item, logger);\n\t\tdefault:\n\t\t\treturn unmapped(item, logger);\n\t}\n}\n\n/** An item this client does not map, which it reports and leaves out. */\nfunction unmapped(item: ResponseOutputItem, logger: ILogger): MessagePart[] {\n\t// No other built-in tool is offered, so any other item is output this client does not expect.\n\ttreatErrors(\n\t\t() => {\n\t\t\tlogger.warn(item, 'OpenAI returned an output item the client does not map');\n\t\t},\n\t\tclassifyHostFailure,\n\t\t'OpenAI client logger failed while mapping a response',\n\t);\n\treturn [];\n}\n\nfunction toStopReason(response: Response): StopReason {\n\tif (response.status === 'incomplete') {\n\t\treturn response.incomplete_details?.reason === 'content_filter' ? 'refused' : 'truncated';\n\t}\n\tif (response.status !== 'completed') {\n\t\tconst cause =\n\t\t\tresponse.error?.message ??\n\t\t\t`The response ended with status \"${response.status ?? 'unknown'}\".`;\n\t\t// The same failure is retried when it arrives as an SDK error, so it must be here too.\n\t\tif (response.error && transientResponseErrorCodes.includes(response.error.code)) {\n\t\t\tthrow new RecoverableError('The OpenAI response failed', { cause });\n\t\t}\n\t\tthrow new UnrecoverableError('The OpenAI response did not complete', { cause });\n\t}\n\n\tconst refused = response.output.some(\n\t\titem => item.type === 'message' && item.content.some(content => content.type === 'refusal'),\n\t);\n\treturn refused ? 'refused' : 'completed';\n}\n\n/** OpenAI counts both cache reads and cache writes inside `input_tokens`. */\nfunction toTokens(usage: ResponseUsage | undefined, logger: ILogger): Tokens | undefined {\n\tif (!usage) {\n\t\t// Zeros would report a call that may have been billed as free.\n\t\ttreatErrors(\n\t\t\t() => {\n\t\t\t\tlogger.warn('OpenAI returned a response without usage; it cannot be accounted for');\n\t\t\t},\n\t\t\tclassifyHostFailure,\n\t\t\t'OpenAI client logger failed while reading usage',\n\t\t);\n\t\treturn undefined;\n\t}\n\n\tconst { cached_tokens, cache_write_tokens } = usage.input_tokens_details;\n\treturn {\n\t\tinputTokens: usage.input_tokens - cached_tokens - cache_write_tokens,\n\t\treadCacheTokens: cached_tokens,\n\t\twrittenCacheTokens: cache_write_tokens,\n\t\toutputTokens: usage.output_tokens,\n\t};\n}\n\nfunction toLLMResponse(\n\tresponse: Response,\n\tlogger: ILogger,\n\tapplyPatch: string | undefined,\n): LLMResponse {\n\tlet usage: Tokens | undefined;\n\n\ttry {\n\t\t// Inside the try: it warns when the usage is missing, and that warning can throw too.\n\t\tusage = toTokens(response.usage, logger);\n\t\treturn {\n\t\t\tmessage: {\n\t\t\t\trole: 'assistant',\n\t\t\t\tcontent: response.output.flatMap(item => describeItem(item, logger, applyPatch)),\n\t\t\t},\n\t\t\tusage,\n\t\t\tstopReason: toStopReason(response),\n\t\t};\n\t} catch (error) {\n\t\t// The call was billed whether the response failed or a logger did while mapping it, and\n\t\t// an answer without usage leaves the run's count unknown.\n\t\tthrow withSpentTokens(error, usage, !response.usage);\n\t}\n}\n\nfunction classifyOpenAIFailure(error: unknown): Error {\n\tif (error instanceof APIError) {\n\t\tif (error.code === 'context_length_exceeded') {\n\t\t\treturn new MaxContextError('The context no longer fits the OpenAI model', {\n\t\t\t\tcause: describeFailure(error),\n\t\t\t});\n\t\t}\n\t\tif (\n\t\t\texhaustedQuotaCodes.some(code => code === error.code) ||\n\t\t\tunrecoverableStatuses.some(status => status === error.status)\n\t\t) {\n\t\t\treturn new UnrecoverableError('OpenAI rejected the request', {\n\t\t\t\tcause: describeFailure(error),\n\t\t\t});\n\t\t}\n\t}\n\n\treturn classifyProviderFailure(error, 'The OpenAI request failed');\n}\n\n/**\n * A stateless `LLMClient` on the OpenAI Responses API. With `store: false` OpenAI keeps\n * nothing between calls, so every call carries the whole context: the conversation is\n * MiKode's, not the provider's.\n */\nexport class OpenAILLMClient implements LLMClient {\n\tprivate readonly client: OpenAI;\n\tprivate readonly model: OpenAIModel;\n\tprivate readonly reasoningEffort: OpenAIReasoningEffort;\n\tprivate readonly compactAt: number | undefined;\n\treadonly systemPrompt: string;\n\treadonly providerDataSource = openAISource;\n\tprivate readonly logger: ILogger;\n\n\t// `model` and `reasoningEffort` are untyped on purpose: this adapter is the one that knows\n\t// what OpenAI supports.\n\tconstructor({\n\t\tmodel,\n\t\treasoningEffort = 'high',\n\t\tcompactAt,\n\t\tsystemPrompt,\n\t\tlogger,\n\t}: {\n\t\tmodel: string;\n\t\treasoningEffort?: string;\n\t\t/** The input tokens at which OpenAI compacts the context on its side. Without it, never. */\n\t\tcompactAt?: number | undefined;\n\t\tsystemPrompt: string;\n\t\tlogger: ILogger;\n\t}) {\n\t\tif (!isOneOf(openAIModels, model)) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`\"${model}\" is not an OpenAI model; expected one of: ${openAIModels.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\tif (!isOneOf(openAIReasoningEfforts, reasoningEffort)) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`\"${reasoningEffort}\" is not an OpenAI reasoning effort; expected one of: ${openAIReasoningEfforts.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\tif (compactAt !== undefined && !(Number.isInteger(compactAt) && compactAt > 0)) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`compactAt must be a positive whole number of tokens, not ${String(compactAt)}`,\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\t// The SDK's default retries stay on: they back off and honour `Retry-After`, which\n\t\t\t// `RetryingAgent` does not. See \"The provider SDKs keep their transport retries\" in decisions.md.\n\t\t\tthis.client = new OpenAI();\n\t\t} catch (error) {\n\t\t\t// A missing API key, for example: building the client again cannot fix it.\n\t\t\tthrow new UnrecoverableError('The OpenAI client could not be set up', {\n\t\t\t\tcause: describeFailure(error),\n\t\t\t});\n\t\t}\n\n\t\tthis.model = model;\n\t\tthis.reasoningEffort = reasoningEffort;\n\t\tthis.compactAt = compactAt;\n\t\tthis.systemPrompt = systemPrompt;\n\t\tthis.logger = logger;\n\t}\n\n\tsupportsNative(tool: NativeTool): boolean {\n\t\treturn tool === 'applyPatch';\n\t}\n\n\tasync send(\n\t\t{ context, tools }: { context: Message[]; tools: ToolDefinition[] },\n\t\tsignal: AbortSignal,\n\t): Promise<LLMResponse> {\n\t\tlet response: Response;\n\t\tconst applyPatch = applyPatchName(tools);\n\n\t\ttry {\n\t\t\tresponse = await this.client.responses.create(\n\t\t\t\t{\n\t\t\t\t\tmodel: this.model,\n\t\t\t\t\tinstructions: this.systemPrompt,\n\t\t\t\t\t// The latest compaction carries everything before it, which OpenAI would only ignore.\n\t\t\t\t\tinput: sentContext(context, openAISource).flatMap(message =>\n\t\t\t\t\t\ttoOpenAIInput(message, applyPatch),\n\t\t\t\t\t),\n\t\t\t\t\t// Left out when empty, so a request without tools stays as it always was.\n\t\t\t\t\t...(tools.length > 0 && { tools: tools.map(toOpenAITool) }),\n\t\t\t\t\t// OpenAI compacts inside the call that passes it, and answers with the compaction first.\n\t\t\t\t\t...(this.compactAt !== undefined && {\n\t\t\t\t\t\tcontext_management: [{ type: 'compaction', compact_threshold: this.compactAt }],\n\t\t\t\t\t}),\n\t\t\t\t\tstore: false,\n\t\t\t\t\treasoning: { effort: this.reasoningEffort, summary: 'concise' },\n\t\t\t\t\t// With `store: false` this is the only way to send the reasoning back.\n\t\t\t\t\tinclude: ['reasoning.encrypted_content'],\n\t\t\t\t},\n\t\t\t\t{ signal },\n\t\t\t);\n\t\t} catch (error) {\n\t\t\t// The SDK's abort error is named 'Error', so it would pass for a provider failure.\n\t\t\tsignal.throwIfAborted();\n\t\t\tthrow classifyOpenAIFailure(error);\n\t\t}\n\n\t\treturn treatErrors(\n\t\t\t() => toLLMResponse(response, this.logger, applyPatch),\n\t\t\tclassifyLocalFailure,\n\t\t\t'OpenAI response mapping failed',\n\t\t);\n\t}\n}\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Agent, AgentResponse, RunOptions } from '#src/agent/domain/agent';
|
|
2
2
|
import type { ReviewerDecision } from './reviewerDecision.ts';
|
|
3
|
-
import type { Validator } from '
|
|
3
|
+
import type { Validator } from '#src/shared/domain/validator';
|
|
4
4
|
import type { ILogger } from '#src/shared/domain/logger';
|
|
5
5
|
export declare const plannerInstructions = "You are the planner agent. Read the relevant repository code and create a concise, engineering-grade plan for the executor. Cover the requested behaviour, structural issues, affected files or symbols, API contracts and boundaries, important failure paths, and meaningful tests. Keep the plan focused on the current request and do not require an architecture redesign yet. Communicate tersely and professionally. In reasoning updates, state only the next concrete action or a verified finding, in one short sentence. Do not narrate routine intentions, uncertainty, self-reflection, encouragement, or a desire to be thorough. Do not restate tool results. Once you decide what to do, act immediately. Use only the detail required by your role in the final response.";
|
|
6
6
|
export declare const executorInstructions = "You are the executor agent. Read the relevant repository code and implement the original user request according to the planner's current plan. Preserve contracts and boundaries, handle important failure paths, keep the change focused, and add or update meaningful tests. Inspect and change the code; do not merely describe what should be done. Communicate tersely and professionally. In reasoning updates, state only the next concrete action or a verified finding, in one short sentence. Do not narrate routine intentions, uncertainty, self-reflection, encouragement, or a desire to be thorough. Do not restate tool results. Once you decide what to do, act immediately. Use only the detail required by your role in the final response. Before you finish, run the project's tests and checks, and finish only when they pass. Once they pass and the plan is carried out, finish at once: do not add work beyond the plan, because the reviewer asks for what is missing. If you cannot make them pass, finish by saying which ones fail and why.";
|