@mikode13/harness 1.0.1 → 2.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 +5 -2
- package/dist/agent/domain/agent.d.ts +13 -3
- package/dist/agent/domain/agent.js.map +1 -1
- package/dist/agent/domain/errors.d.ts +29 -6
- package/dist/agent/domain/errors.js +35 -0
- package/dist/agent/domain/errors.js.map +1 -1
- package/dist/engines/claude/infrastructure/model/claudeAgent.d.ts +1 -1
- package/dist/engines/claude/infrastructure/model/claudeAgent.js +74 -49
- package/dist/engines/claude/infrastructure/model/claudeAgent.js.map +1 -1
- package/dist/engines/codex/infrastructure/model/codexAgent.d.ts +1 -1
- package/dist/engines/codex/infrastructure/model/codexAgent.js +54 -29
- package/dist/engines/codex/infrastructure/model/codexAgent.js.map +1 -1
- package/dist/engines/domain/model/llmAgent.d.ts +17 -0
- package/dist/engines/domain/model/llmAgent.js +63 -0
- package/dist/engines/domain/model/llmAgent.js.map +1 -0
- package/dist/factory/infrastructure/agentFactory.d.ts +19 -0
- package/dist/factory/infrastructure/agentFactory.js +20 -0
- package/dist/factory/infrastructure/agentFactory.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js.map +1 -1
- package/dist/llm/domain/conversation.d.ts +20 -0
- package/dist/llm/domain/conversation.js +26 -0
- package/dist/llm/domain/conversation.js.map +1 -0
- package/dist/llm/domain/errors.d.ts +4 -0
- package/dist/llm/domain/errors.js +5 -0
- package/dist/llm/domain/errors.js.map +1 -0
- package/dist/llm/domain/llm.d.ts +18 -0
- package/dist/llm/domain/llm.js +2 -0
- package/dist/llm/domain/llm.js.map +1 -0
- package/dist/llm/domain/message.d.ts +11 -0
- package/dist/llm/domain/message.js +2 -0
- package/dist/llm/domain/message.js.map +1 -0
- package/dist/llm/infrastructure/openAILLMClient.d.ts +22 -0
- package/dist/llm/infrastructure/openAILLMClient.js +172 -0
- package/dist/llm/infrastructure/openAILLMClient.js.map +1 -0
- package/dist/orchestration/domain/model/orchestratorAgent.d.ts +2 -1
- package/dist/orchestration/domain/model/orchestratorAgent.js +35 -16
- package/dist/orchestration/domain/model/orchestratorAgent.js.map +1 -1
- package/dist/retry/domain/model/retryingAgent.d.ts +10 -2
- package/dist/retry/domain/model/retryingAgent.js +40 -10
- package/dist/retry/domain/model/retryingAgent.js.map +1 -1
- package/dist/shared/domain/tokens.d.ts +26 -0
- package/dist/shared/domain/tokens.js +19 -0
- package/dist/shared/domain/tokens.js.map +1 -0
- package/package.json +3 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codexAgent.js","sourceRoot":"","sources":["../../../../../src/engines/codex/infrastructure/model/codexAgent.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tCodex,\n\ttype ModelReasoningEffort,\n\ttype Thread,\n\ttype ThreadEvent,\n\ttype ThreadItem,\n\ttype Usage,\n} from '@openai/codex-sdk';\nimport {\n\ttype Agent,\n\ttype AgentResponse,\n\ttype Callback,\n\ttype ProgressEvent,\n} from '../../../../agent/domain/agent.ts';\nimport {\n\tInvalidAgentConfigError,\n\tRecoverableError,\n\tUnrecoverableError,\n} from '../../../../agent/domain/errors.ts';\nimport {\n\tclassifiedProviderStream,\n\tclassifyHostFailure,\n\tclassifyLocalFailure,\n\tclassifyProviderFailure,\n\tdescribeFailure,\n\ttreatErrors,\n} from '../../../../agent/domain/providerFailure.ts';\nimport type { ILogger } from '../../../../shared/domain/logger.ts';\nimport { isOneOf } from '../../../../shared/domain/isOneOf.ts';\n\n// The SDK types `model` as a plain string, so this list is maintained by hand.\nexport const codexModels = ['gpt-6-astra', 'gpt-5.6-sol', 'gpt-5.6-luna', 'gpt-5.6-terra'] as const;\nexport type CodexModel = (typeof codexModels)[number];\n\n// The efforts Codex's model catalog lists; the SDK also types 'minimal' and 'persistent', which\n// none of the models above supports. `satisfies` only proves this is a subset of the SDK's type,\n// so an SDK upgrade that adds an effort has to be reconciled here by hand.\nexport const codexReasoningEfforts = [\n\t'low',\n\t'medium',\n\t'high',\n\t'xhigh',\n\t'max',\n\t'ultra',\n] as const satisfies readonly ModelReasoningEffort[];\nexport type CodexReasoningEffort = (typeof codexReasoningEfforts)[number];\n\ninterface StreamedTurn {\n\tevents: AsyncGenerator<ThreadEvent>;\n}\n\nfunction convertEventToItem(event: ThreadEvent): ThreadItem | undefined {\n\tif (event.type === 'error')\n\t\tthrow new UnrecoverableError('Codex stream error', { cause: event.message });\n\n\tif (event.type === 'turn.failed')\n\t\tthrow new UnrecoverableError('Turn failed from codex sdk', { cause: event.error.message });\n\n\tif (event.type === 'item.completed') return event.item;\n\n\treturn undefined;\n}\n\nfunction describeItem(item: ThreadItem, logger: ILogger): ProgressEvent | undefined {\n\tswitch (item.type) {\n\t\tcase 'agent_message':\n\t\t\treturn { type: 'agentMessage', message: item.text };\n\t\tcase 'reasoning':\n\t\t\treturn { type: 'reasoning', message: item.text };\n\t\tcase 'command_execution':\n\t\t\treturn { type: 'command', command: item.command, exitCode: item.exit_code };\n\t\tcase 'web_search':\n\t\t\treturn { type: 'search', query: item.query };\n\t\tcase 'file_change':\n\t\t\treturn { type: 'fileChange', changes: item.changes };\n\t\tcase 'mcp_tool_call': {\n\t\t\tif (item.error) {\n\t\t\t\tthrow new RecoverableError('skill failed', { cause: item.error.message });\n\t\t\t}\n\t\t\treturn { type: 'mcpTool', server: item.server, tool: item.tool, status: item.status };\n\t\t}\n\t\tcase 'todo_list':\n\t\t\treturn { type: 'todoList', items: item.items };\n\t\tcase 'error':\n\t\t\tthrow new RecoverableError('error while using the codex tools', { cause: item.message });\n\t\tdefault:\n\t\t\ttreatErrors(\n\t\t\t\t() => {\n\t\t\t\t\tlogger.warn(item, 'new type');\n\t\t\t\t},\n\t\t\t\tclassifyHostFailure,\n\t\t\t\t'Codex logger failed while reporting progress',\n\t\t\t);\n\t\t\treturn undefined;\n\t}\n}\n\nexport class CodexAgent implements Agent {\n\tprivate thread: Thread;\n\tprivate logger: ILogger;\n\n\t// `model` and `reasoningEffort` are untyped on purpose: this adapter is the one that knows\n\t// what Codex supports, so it validates them instead of trusting the caller.\n\tconstructor({\n\t\tmodel,\n\t\tautoApprove = false,\n\t\treasoningEffort = 'high',\n\t\tlogger,\n\t}: {\n\t\tmodel: string;\n\t\tautoApprove?: boolean;\n\t\treasoningEffort?: string;\n\t\tlogger: ILogger;\n\t}) {\n\t\t// Checked before the SDK is involved: an unsupported value is the caller's mistake, not\n\t\t// a configuration Codex rejected.\n\t\tif (!isOneOf(codexModels, model)) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`\"${model}\" is not a Codex model; expected one of: ${codexModels.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\tif (!isOneOf(codexReasoningEfforts, reasoningEffort)) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`\"${reasoningEffort}\" is not a Codex reasoning effort; expected one of: ${codexReasoningEfforts.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\t// The one per-model gap in Codex's catalog.\n\t\tif (model === 'gpt-5.6-luna' && reasoningEffort === 'ultra') {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t'\"gpt-5.6-luna\" does not support the \"ultra\" reasoning effort',\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\tconst sdk = new Codex();\n\t\t\tthis.thread = sdk.startThread({\n\t\t\t\tmodel,\n\t\t\t\tmodelReasoningEffort: reasoningEffort,\n\t\t\t\t...(autoApprove\n\t\t\t\t\t? { approvalPolicy: 'never' as const, sandboxMode: 'danger-full-access' as const }\n\t\t\t\t\t: {}),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\t// Not recoverable, unlike a request: a thread the SDK refused to open at all is\n\t\t\t// rejected configuration, and running the same constructor again cannot fix it.\n\t\t\tthrow new UnrecoverableError('Codex rejected the thread configuration', {\n\t\t\t\tcause: describeFailure(error),\n\t\t\t});\n\t\t}\n\n\t\tthis.logger = logger;\n\t}\n\n\tasync run(\n\t\tprompt: string,\n\t\tsignal: AbortSignal,\n\t\tcallback: Callback,\n\t): Promise<AgentResponse | undefined> {\n\t\tlet turn: StreamedTurn;\n\n\t\ttry {\n\t\t\tturn = await this.thread.runStreamed(prompt, { signal });\n\t\t} catch (error) {\n\t\t\tthrow classifyProviderFailure(error, 'Codex refused the request');\n\t\t}\n\n\t\treturn await this.parseResponse(turn, callback);\n\t}\n\n\tprivate async parseResponse(\n\t\tturn: StreamedTurn,\n\t\tcallback: Callback,\n\t): Promise<AgentResponse | undefined> {\n\t\tconst lines: string[] = [];\n\t\tconst start = Date.now();\n\t\tlet usage: Usage | undefined = undefined;\n\n\t\tconst events = classifiedProviderStream(turn.events, 'Codex stream ended unexpectedly');\n\n\t\tfor await (const event of events) {\n\t\t\tif (event.type === 'turn.completed') {\n\t\t\t\tusage = event.usage;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst item = convertEventToItem(event);\n\n\t\t\tif (!item) continue;\n\n\t\t\tlet description: ProgressEvent | undefined;\n\t\t\ttry {\n\t\t\t\tdescription = describeItem(item, this.logger);\n\t\t\t} catch (error) {\n\t\t\t\tthrow classifyLocalFailure(error, 'Codex failed while mapping progress');\n\t\t\t}\n\n\t\t\tif (description) {\n\t\t\t\ttry {\n\t\t\t\t\tcallback(description);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthrow classifyHostFailure(error, 'Codex progress callback failed');\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (description?.type === 'agentMessage') lines.push(description.message);\n\t\t}\n\n\t\tif (!lines.length || !usage) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn {\n\t\t\tresponse: lines.join('\\n'),\n\t\t\tinputTokens: usage.input_tokens,\n\t\t\toutputTokens: usage.output_tokens,\n\t\t\tduration: (Date.now() - start) / 1000,\n\t\t};\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"codexAgent.js","sourceRoot":"","sources":["../../../../../src/engines/codex/infrastructure/model/codexAgent.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tCodex,\n\ttype ModelReasoningEffort,\n\ttype Thread,\n\ttype ThreadEvent,\n\ttype ThreadItem,\n\ttype Usage,\n} from '@openai/codex-sdk';\nimport {\n\ttype Agent,\n\ttype AgentResponse,\n\ttype Callback,\n\ttype ProgressEvent,\n} from '../../../../agent/domain/agent.ts';\nimport {\n\tInvalidAgentConfigError,\n\tRecoverableError,\n\tUnrecoverableError,\n\twithSpentTokens,\n} from '../../../../agent/domain/errors.ts';\nimport {\n\tclassifiedProviderStream,\n\tclassifyHostFailure,\n\tclassifyLocalFailure,\n\tclassifyProviderFailure,\n\tdescribeFailure,\n\ttreatErrors,\n} from '../../../../agent/domain/providerFailure.ts';\nimport type { ILogger } from '../../../../shared/domain/logger.ts';\nimport type { Tokens } from '../../../../shared/domain/tokens.ts';\nimport { isOneOf } from '../../../../shared/domain/isOneOf.ts';\n\n// The SDK types `model` as a plain string, so this list is maintained by hand.\nexport const codexModels = ['gpt-6-astra', 'gpt-5.6-sol', 'gpt-5.6-luna', 'gpt-5.6-terra'] as const;\nexport type CodexModel = (typeof codexModels)[number];\n\n// The efforts Codex's model catalog lists; the SDK also types 'minimal' and 'persistent', which\n// none of the models above supports. `satisfies` only proves this is a subset of the SDK's type,\n// so an SDK upgrade that adds an effort has to be reconciled here by hand.\nexport const codexReasoningEfforts = [\n\t'low',\n\t'medium',\n\t'high',\n\t'xhigh',\n\t'max',\n\t'ultra',\n] as const satisfies readonly ModelReasoningEffort[];\nexport type CodexReasoningEffort = (typeof codexReasoningEfforts)[number];\n\ninterface StreamedTurn {\n\tevents: AsyncGenerator<ThreadEvent>;\n}\n\nfunction convertEventToItem(event: ThreadEvent): ThreadItem | undefined {\n\tif (event.type === 'error')\n\t\tthrow new UnrecoverableError('Codex stream error', { cause: event.message });\n\n\tif (event.type === 'turn.failed')\n\t\tthrow new UnrecoverableError('Turn failed from codex sdk', { cause: event.error.message });\n\n\tif (event.type === 'item.completed') return event.item;\n\n\treturn undefined;\n}\n\nfunction describeItem(item: ThreadItem, logger: ILogger): ProgressEvent | undefined {\n\tswitch (item.type) {\n\t\tcase 'agent_message':\n\t\t\treturn { type: 'agentMessage', message: item.text };\n\t\tcase 'reasoning':\n\t\t\treturn { type: 'reasoning', message: item.text };\n\t\tcase 'command_execution':\n\t\t\treturn { type: 'command', command: item.command, exitCode: item.exit_code };\n\t\tcase 'web_search':\n\t\t\treturn { type: 'search', query: item.query };\n\t\tcase 'file_change':\n\t\t\treturn { type: 'fileChange', changes: item.changes };\n\t\tcase 'mcp_tool_call': {\n\t\t\tif (item.error) {\n\t\t\t\tthrow new RecoverableError('skill failed', { cause: item.error.message });\n\t\t\t}\n\t\t\treturn { type: 'mcpTool', server: item.server, tool: item.tool, status: item.status };\n\t\t}\n\t\tcase 'todo_list':\n\t\t\treturn { type: 'todoList', items: item.items };\n\t\tcase 'error':\n\t\t\tthrow new RecoverableError('error while using the codex tools', { cause: item.message });\n\t\tdefault:\n\t\t\ttreatErrors(\n\t\t\t\t() => {\n\t\t\t\t\tlogger.warn(item, 'new type');\n\t\t\t\t},\n\t\t\t\tclassifyHostFailure,\n\t\t\t\t'Codex logger failed while reporting progress',\n\t\t\t);\n\t\t\treturn undefined;\n\t}\n}\n\n// Codex counts cached tokens inside input_tokens; Tokens keeps them apart.\nfunction toTokens(usage: Usage): Tokens {\n\treturn {\n\t\tinputTokens: usage.input_tokens - usage.cached_input_tokens - usage.cache_write_input_tokens,\n\t\treadCacheTokens: usage.cached_input_tokens,\n\t\twrittenCacheTokens: usage.cache_write_input_tokens,\n\t\toutputTokens: usage.output_tokens,\n\t};\n}\n\nexport class CodexAgent implements Agent {\n\tprivate thread: Thread;\n\tprivate logger: ILogger;\n\n\t// `model` and `reasoningEffort` are untyped on purpose: this adapter is the one that knows\n\t// what Codex supports, so it validates them instead of trusting the caller.\n\tconstructor({\n\t\tmodel,\n\t\tautoApprove = false,\n\t\treasoningEffort = 'high',\n\t\tlogger,\n\t}: {\n\t\tmodel: string;\n\t\tautoApprove?: boolean;\n\t\treasoningEffort?: string;\n\t\tlogger: ILogger;\n\t}) {\n\t\t// Checked before the SDK is involved: an unsupported value is the caller's mistake, not\n\t\t// a configuration Codex rejected.\n\t\tif (!isOneOf(codexModels, model)) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`\"${model}\" is not a Codex model; expected one of: ${codexModels.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\tif (!isOneOf(codexReasoningEfforts, reasoningEffort)) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`\"${reasoningEffort}\" is not a Codex reasoning effort; expected one of: ${codexReasoningEfforts.join(', ')}`,\n\t\t\t);\n\t\t}\n\t\t// The one per-model gap in Codex's catalog.\n\t\tif (model === 'gpt-5.6-luna' && reasoningEffort === 'ultra') {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t'\"gpt-5.6-luna\" does not support the \"ultra\" reasoning effort',\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\tconst sdk = new Codex();\n\t\t\tthis.thread = sdk.startThread({\n\t\t\t\tmodel,\n\t\t\t\tmodelReasoningEffort: reasoningEffort,\n\t\t\t\t...(autoApprove\n\t\t\t\t\t? { approvalPolicy: 'never' as const, sandboxMode: 'danger-full-access' as const }\n\t\t\t\t\t: {}),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\t// Not recoverable, unlike a request: a thread the SDK refused to open at all is\n\t\t\t// rejected configuration, and running the same constructor again cannot fix it.\n\t\t\tthrow new UnrecoverableError('Codex rejected the thread configuration', {\n\t\t\t\tcause: describeFailure(error),\n\t\t\t});\n\t\t}\n\n\t\tthis.logger = logger;\n\t}\n\n\tasync run(prompt: string, signal: AbortSignal, callback: Callback): Promise<AgentResponse> {\n\t\tconst start = Date.now();\n\t\tlet turn: StreamedTurn;\n\n\t\ttry {\n\t\t\tturn = await this.thread.runStreamed(prompt, { signal });\n\t\t} catch (error) {\n\t\t\tthrow classifyProviderFailure(error, 'Codex refused the request');\n\t\t}\n\n\t\treturn await this.parseResponse(turn, callback, start);\n\t}\n\n\tprivate async parseResponse(\n\t\tturn: StreamedTurn,\n\t\tcallback: Callback,\n\t\tstart: number,\n\t): Promise<AgentResponse> {\n\t\tconst lines: string[] = [];\n\t\tlet usage: Usage | undefined = undefined;\n\n\t\t// Once Codex produced an item, the provider answered: a failure after that without usage\n\t\t// leaves the run's count unknown, unlike one that never got an answer.\n\t\tlet answered = false;\n\n\t\ttry {\n\t\t\tconst events = classifiedProviderStream(turn.events, 'Codex stream ended unexpectedly');\n\n\t\t\tfor await (const event of events) {\n\t\t\t\t// A `turn.failed` alone may be a quota or authentication failure with no model work.\n\t\t\t\tif (event.type.startsWith('item.')) answered = true;\n\t\t\t\tif (event.type === 'turn.completed') {\n\t\t\t\t\tusage = event.usage;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst item = convertEventToItem(event);\n\n\t\t\t\tif (!item) continue;\n\n\t\t\t\tlet description: ProgressEvent | undefined;\n\t\t\t\ttry {\n\t\t\t\t\tdescription = describeItem(item, this.logger);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthrow classifyLocalFailure(error, 'Codex failed while mapping progress');\n\t\t\t\t}\n\n\t\t\t\tif (description) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcallback(description);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tthrow classifyHostFailure(error, 'Codex progress callback failed');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (description?.type === 'agentMessage') lines.push(description.message);\n\t\t\t}\n\n\t\t\tif (!usage) {\n\t\t\t\t// The turn's work stands; only its accounting is missing.\n\t\t\t\ttreatErrors(\n\t\t\t\t\t() => {\n\t\t\t\t\t\tthis.logger.warn('Codex ended a turn without reporting usage');\n\t\t\t\t\t},\n\t\t\t\t\tclassifyHostFailure,\n\t\t\t\t\t'Codex logger failed while reporting missing usage',\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tif (usage) throw withSpentTokens(error, toTokens(usage));\n\t\t\tthrow answered ? withSpentTokens(error, undefined, true) : error;\n\t\t}\n\n\t\treturn {\n\t\t\t// Empty when the turn only ran commands or edited files.\n\t\t\tresponse: lines.join('\\n'),\n\t\t\ttokens: usage && toTokens(usage),\n\t\t\tduration: (Date.now() - start) / 1000,\n\t\t};\n\t}\n}\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Agent, AgentResponse, Callback } from '../../../agent/domain/agent.ts';
|
|
2
|
+
import type { LLMClient } from '../../../llm/domain/llm.ts';
|
|
3
|
+
import type { Message } from '../../../llm/domain/message.ts';
|
|
4
|
+
/**
|
|
5
|
+
* An agent whose conversation is owned by MiKode: every run sends the whole context to a
|
|
6
|
+
* stateless `LLMClient`. Turn boundaries are the consumer's to emit, as with the other
|
|
7
|
+
* engines; this agent only narrates what the model produced.
|
|
8
|
+
*/
|
|
9
|
+
export declare class LLMAgent implements Agent {
|
|
10
|
+
private readonly llmClient;
|
|
11
|
+
private readonly conversation;
|
|
12
|
+
constructor({ llmClient, messages }: {
|
|
13
|
+
llmClient: LLMClient;
|
|
14
|
+
messages?: Message[];
|
|
15
|
+
});
|
|
16
|
+
run(prompt: string, signal: AbortSignal, callback: Callback): Promise<AgentResponse>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { UnrecoverableError, withSpentTokens } from "../../../agent/domain/errors.js";
|
|
2
|
+
import { classifyHostFailure, classifyProviderFailure, treatErrors, } from "../../../agent/domain/providerFailure.js";
|
|
3
|
+
import { Conversation } from "../../../llm/domain/conversation.js";
|
|
4
|
+
function describePart(part) {
|
|
5
|
+
return part.type === 'text'
|
|
6
|
+
? { type: 'agentMessage', message: part.text }
|
|
7
|
+
: { type: 'reasoning', message: part.text };
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* An agent whose conversation is owned by MiKode: every run sends the whole context to a
|
|
11
|
+
* stateless `LLMClient`. Turn boundaries are the consumer's to emit, as with the other
|
|
12
|
+
* engines; this agent only narrates what the model produced.
|
|
13
|
+
*/
|
|
14
|
+
export class LLMAgent {
|
|
15
|
+
llmClient;
|
|
16
|
+
conversation;
|
|
17
|
+
constructor({ llmClient, messages }) {
|
|
18
|
+
this.llmClient = llmClient;
|
|
19
|
+
this.conversation = new Conversation(messages);
|
|
20
|
+
}
|
|
21
|
+
async run(prompt, signal, callback) {
|
|
22
|
+
const start = Date.now();
|
|
23
|
+
const userMessage = { role: 'user', content: [{ type: 'text', text: prompt }] };
|
|
24
|
+
let response;
|
|
25
|
+
try {
|
|
26
|
+
// The prompt is sent as a copy so the one recorded below is the one the agent built.
|
|
27
|
+
response = await this.llmClient.send([...this.conversation.getContext(), structuredClone(userMessage)], signal);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
throw classifyProviderFailure(error, 'The LLM call failed');
|
|
31
|
+
}
|
|
32
|
+
if (response.stopReason !== 'completed') {
|
|
33
|
+
throw new UnrecoverableError('The LLM stopped before completing its answer', {
|
|
34
|
+
cause: `The model stopped with "${response.stopReason}".`,
|
|
35
|
+
// The call was billed even though its answer is unusable.
|
|
36
|
+
tokens: response.usage,
|
|
37
|
+
usageUnreported: !response.usage,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
this.conversation.addExchange(userMessage, response.message);
|
|
41
|
+
try {
|
|
42
|
+
for (const part of response.message.content) {
|
|
43
|
+
treatErrors(() => {
|
|
44
|
+
callback(describePart(part));
|
|
45
|
+
}, classifyHostFailure, 'LLM agent progress callback failed');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
// The answer already arrived, so the run spent its tokens whatever the consumer did.
|
|
50
|
+
throw withSpentTokens(error, response.usage, !response.usage);
|
|
51
|
+
}
|
|
52
|
+
const text = response.message.content
|
|
53
|
+
.filter(part => part.type === 'text')
|
|
54
|
+
.map(part => part.text)
|
|
55
|
+
.join('\n');
|
|
56
|
+
return {
|
|
57
|
+
response: text,
|
|
58
|
+
tokens: response.usage,
|
|
59
|
+
duration: (Date.now() - start) / 1000,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=llmAgent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llmAgent.js","sourceRoot":"","sources":["../../../../src/engines/domain/model/llmAgent.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACtF,OAAO,EACN,mBAAmB,EACnB,uBAAuB,EACvB,WAAW,GACX,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AAInE,SAAS,YAAY,CAAC,IAAiB;IACtC,OAAO,IAAI,CAAC,IAAI,KAAK,MAAM;QAC1B,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE;QAC9C,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;AAC9C,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,QAAQ;IACH,SAAS,CAAY;IACrB,YAAY,CAAe;IAE5C,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAkD;QAClF,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,YAAY,GAAG,IAAI,YAAY,CAAC,QAAQ,CAAC,CAAC;IAChD,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,MAAc,EAAE,MAAmB,EAAE,QAAkB;QAChE,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACzB,MAAM,WAAW,GAAY,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QAEzF,IAAI,QAAqB,CAAC;QAC1B,IAAI,CAAC;YACJ,qFAAqF;YACrF,QAAQ,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CACnC,CAAC,GAAG,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,EAAE,eAAe,CAAC,WAAW,CAAC,CAAC,EACjE,MAAM,CACN,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,uBAAuB,CAAC,KAAK,EAAE,qBAAqB,CAAC,CAAC;QAC7D,CAAC;QAED,IAAI,QAAQ,CAAC,UAAU,KAAK,WAAW,EAAE,CAAC;YACzC,MAAM,IAAI,kBAAkB,CAAC,8CAA8C,EAAE;gBAC5E,KAAK,EAAE,2BAA2B,QAAQ,CAAC,UAAU,IAAI;gBACzD,0DAA0D;gBAC1D,MAAM,EAAE,QAAQ,CAAC,KAAK;gBACtB,eAAe,EAAE,CAAC,QAAQ,CAAC,KAAK;aAChC,CAAC,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,WAAW,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;QAE7D,IAAI,CAAC;YACJ,KAAK,MAAM,IAAI,IAAI,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;gBAC7C,WAAW,CACV,GAAG,EAAE;oBACJ,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC9B,CAAC,EACD,mBAAmB,EACnB,oCAAoC,CACpC,CAAC;YACH,CAAC;QACF,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,qFAAqF;YACrF,MAAM,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC/D,CAAC;QAED,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO;aACnC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC;aACpC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;aACtB,IAAI,CAAC,IAAI,CAAC,CAAC;QAEb,OAAO;YACN,QAAQ,EAAE,IAAI;YACd,MAAM,EAAE,QAAQ,CAAC,KAAK;YACtB,QAAQ,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,GAAG,IAAI;SACrC,CAAC;IACH,CAAC;CACD","sourcesContent":["import type { Agent, AgentResponse, Callback, ProgressEvent } from '../../../agent/domain/agent.ts';\nimport { UnrecoverableError, withSpentTokens } from '../../../agent/domain/errors.ts';\nimport {\n\tclassifyHostFailure,\n\tclassifyProviderFailure,\n\ttreatErrors,\n} from '../../../agent/domain/providerFailure.ts';\nimport { Conversation } from '../../../llm/domain/conversation.ts';\nimport type { LLMClient, LLMResponse } from '../../../llm/domain/llm.ts';\nimport type { Message, MessagePart } from '../../../llm/domain/message.ts';\n\nfunction describePart(part: MessagePart): ProgressEvent {\n\treturn part.type === 'text'\n\t\t? { type: 'agentMessage', message: part.text }\n\t\t: { type: 'reasoning', message: part.text };\n}\n\n/**\n * An agent whose conversation is owned by MiKode: every run sends the whole context to a\n * stateless `LLMClient`. Turn boundaries are the consumer's to emit, as with the other\n * engines; this agent only narrates what the model produced.\n */\nexport class LLMAgent implements Agent {\n\tprivate readonly llmClient: LLMClient;\n\tprivate readonly conversation: Conversation;\n\n\tconstructor({ llmClient, messages }: { llmClient: LLMClient; messages?: Message[] }) {\n\t\tthis.llmClient = llmClient;\n\t\tthis.conversation = new Conversation(messages);\n\t}\n\n\tasync run(prompt: string, signal: AbortSignal, callback: Callback): Promise<AgentResponse> {\n\t\tconst start = Date.now();\n\t\tconst userMessage: Message = { role: 'user', content: [{ type: 'text', text: prompt }] };\n\n\t\tlet response: LLMResponse;\n\t\ttry {\n\t\t\t// The prompt is sent as a copy so the one recorded below is the one the agent built.\n\t\t\tresponse = await this.llmClient.send(\n\t\t\t\t[...this.conversation.getContext(), structuredClone(userMessage)],\n\t\t\t\tsignal,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tthrow classifyProviderFailure(error, 'The LLM call failed');\n\t\t}\n\n\t\tif (response.stopReason !== 'completed') {\n\t\t\tthrow new UnrecoverableError('The LLM stopped before completing its answer', {\n\t\t\t\tcause: `The model stopped with \"${response.stopReason}\".`,\n\t\t\t\t// The call was billed even though its answer is unusable.\n\t\t\t\ttokens: response.usage,\n\t\t\t\tusageUnreported: !response.usage,\n\t\t\t});\n\t\t}\n\n\t\tthis.conversation.addExchange(userMessage, response.message);\n\n\t\ttry {\n\t\t\tfor (const part of response.message.content) {\n\t\t\t\ttreatErrors(\n\t\t\t\t\t() => {\n\t\t\t\t\t\tcallback(describePart(part));\n\t\t\t\t\t},\n\t\t\t\t\tclassifyHostFailure,\n\t\t\t\t\t'LLM agent progress callback failed',\n\t\t\t\t);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// The answer already arrived, so the run spent its tokens whatever the consumer did.\n\t\t\tthrow withSpentTokens(error, response.usage, !response.usage);\n\t\t}\n\n\t\tconst text = response.message.content\n\t\t\t.filter(part => part.type === 'text')\n\t\t\t.map(part => part.text)\n\t\t\t.join('\\n');\n\n\t\treturn {\n\t\t\tresponse: text,\n\t\t\ttokens: response.usage,\n\t\t\tduration: (Date.now() - start) / 1000,\n\t\t};\n\t}\n}\n"]}
|
|
@@ -47,3 +47,22 @@ export interface CreateOrchestratorOptions {
|
|
|
47
47
|
* binary is missing.
|
|
48
48
|
*/
|
|
49
49
|
export declare function createOrchestrator({ provider, autoApprove, logger, }?: CreateOrchestratorOptions): Agent;
|
|
50
|
+
export interface CreateLLMAgentOptions {
|
|
51
|
+
/** Defaults to `'gpt-5.6-luna'`. The OpenAI client rejects a model it does not support. */
|
|
52
|
+
model?: string;
|
|
53
|
+
/** Sent as the model's instructions on every call. */
|
|
54
|
+
systemPrompt: string;
|
|
55
|
+
/** Defaults to warnings on stderr. */
|
|
56
|
+
logger?: ILogger;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Builds an agent whose conversation MiKode owns, on the OpenAI Responses API, already wrapped
|
|
60
|
+
* in the harness's retry policy. Internal while #23 reaches parity with the Agent SDK engines:
|
|
61
|
+
* it is not exported from `src/index.ts`, and it takes no provider because OpenAI is the only
|
|
62
|
+
* `LLMClient` so far. It runs no tools, so it has no `autoApprove`.
|
|
63
|
+
*
|
|
64
|
+
* @throws {InvalidAgentConfigError} for a model the OpenAI client does not support.
|
|
65
|
+
* @throws {UnrecoverableError} when the OpenAI client cannot be set up, for example because
|
|
66
|
+
* `OPENAI_API_KEY` is missing.
|
|
67
|
+
*/
|
|
68
|
+
export declare function createLLMAgent({ model, systemPrompt, logger, }: CreateLLMAgentOptions): Agent;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { InvalidAgentConfigError } from "../../agent/domain/errors.js";
|
|
2
2
|
import { ClaudeAgent, } from "../../engines/claude/infrastructure/model/claudeAgent.js";
|
|
3
3
|
import { CodexAgent, } from "../../engines/codex/infrastructure/model/codexAgent.js";
|
|
4
|
+
import { LLMAgent } from "../../engines/domain/model/llmAgent.js";
|
|
5
|
+
import { OpenAILLMClient } from "../../llm/infrastructure/openAILLMClient.js";
|
|
4
6
|
import { OrchestratorAgent } from "../../orchestration/domain/model/orchestratorAgent.js";
|
|
5
7
|
import { ReviewerDecisionValidator } from "../../orchestration/infrastructure/model/reviewerDecisionValidator.js";
|
|
6
8
|
import { RetryingAgent } from "../../retry/domain/model/retryingAgent.js";
|
|
@@ -88,4 +90,22 @@ export function createOrchestrator({ provider, autoApprove, logger = new Logger(
|
|
|
88
90
|
logger,
|
|
89
91
|
});
|
|
90
92
|
}
|
|
93
|
+
const defaultLLMAgentModel = 'gpt-5.6-luna';
|
|
94
|
+
/**
|
|
95
|
+
* Builds an agent whose conversation MiKode owns, on the OpenAI Responses API, already wrapped
|
|
96
|
+
* in the harness's retry policy. Internal while #23 reaches parity with the Agent SDK engines:
|
|
97
|
+
* it is not exported from `src/index.ts`, and it takes no provider because OpenAI is the only
|
|
98
|
+
* `LLMClient` so far. It runs no tools, so it has no `autoApprove`.
|
|
99
|
+
*
|
|
100
|
+
* @throws {InvalidAgentConfigError} for a model the OpenAI client does not support.
|
|
101
|
+
* @throws {UnrecoverableError} when the OpenAI client cannot be set up, for example because
|
|
102
|
+
* `OPENAI_API_KEY` is missing.
|
|
103
|
+
*/
|
|
104
|
+
export function createLLMAgent({ model = defaultLLMAgentModel, systemPrompt, logger = new Logger(), }) {
|
|
105
|
+
const engine = new LLMAgent({
|
|
106
|
+
llmClient: new OpenAILLMClient({ model, systemPrompt, logger }),
|
|
107
|
+
});
|
|
108
|
+
// LLMAgent records nothing from a failed call, so the original prompt is the whole story.
|
|
109
|
+
return new RetryingAgent({ inner: engine, logger, noteFailures: false });
|
|
110
|
+
}
|
|
91
111
|
//# sourceMappingURL=agentFactory.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agentFactory.js","sourceRoot":"","sources":["../../../src/factory/infrastructure/agentFactory.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Agent } from '../../agent/domain/agent.ts';\nimport { InvalidAgentConfigError } from '../../agent/domain/errors.ts';\nimport {\n\tClaudeAgent,\n\ttype ClaudeModel,\n\ttype ClaudeReasoningEffort,\n} from '../../engines/claude/infrastructure/model/claudeAgent.ts';\nimport {\n\tCodexAgent,\n\ttype CodexModel,\n\ttype CodexReasoningEffort,\n} from '../../engines/codex/infrastructure/model/codexAgent.ts';\nimport { OrchestratorAgent } from '../../orchestration/domain/model/orchestratorAgent.ts';\nimport { ReviewerDecisionValidator } from '../../orchestration/infrastructure/model/reviewerDecisionValidator.ts';\nimport { RetryingAgent } from '../../retry/domain/model/retryingAgent.ts';\nimport { isOneOf } from '../../shared/domain/isOneOf.ts';\nimport type { ILogger } from '../../shared/domain/logger.ts';\nimport { Logger } from '../../shared/infrastructure/logger.ts';\n\nexport const agentProviders = ['claude', 'codex'] as const;\nexport type AgentProvider = (typeof agentProviders)[number];\n\n/** Any provider's model. The engine the provider selects rejects one it does not support. */\nexport type AgentModel = ClaudeModel | CodexModel;\n/** Any provider's reasoning effort. The engine the provider selects rejects one it does not support. */\nexport type ReasoningEffort = ClaudeReasoningEffort | CodexReasoningEffort;\n\nexport function isAgentProvider(value: string): value is AgentProvider {\n\treturn isOneOf(agentProviders, value);\n}\n\nexport interface CreateAgentOptions {\n\t/** Defaults to `'opus'` on Claude and `'gpt-5.6-sol'` on Codex. */\n\tmodel?: AgentModel;\n\t/** Defaults to `'high'`. */\n\treasoningEffort?: ReasoningEffort;\n\t/** Maps to the provider's permission-bypass mode. Defaults to `false`. */\n\tautoApprove?: boolean;\n\t/** Defaults to warnings on stderr. */\n\tlogger?: ILogger;\n}\n\nconst defaultModels = {\n\tclaude: 'opus',\n\tcodex: 'gpt-5.6-sol',\n} as const satisfies Record<AgentProvider, AgentModel>;\n\n/**\n * Builds the agent for a provider, already wrapped in the harness's retry policy.\n *\n * @throws {InvalidAgentConfigError} for an unknown provider, or a model or reasoning effort\n * the provider does not support.\n * @throws {UnrecoverableError} when Codex cannot be set up, for example because its CLI binary\n * is missing.\n */\nexport function createAgent(\n\tprovider: AgentProvider,\n\t{ model, reasoningEffort, autoApprove, logger = new Logger() }: CreateAgentOptions = {},\n): Agent {\n\tlet engine: Agent;\n\n\tswitch (provider) {\n\t\tcase 'claude':\n\t\t\tengine = new ClaudeAgent({\n\t\t\t\tmodel: model ?? defaultModels.claude,\n\t\t\t\treasoningEffort,\n\t\t\t\tautoApprove,\n\t\t\t\tlogger,\n\t\t\t});\n\t\t\tbreak;\n\t\tcase 'codex':\n\t\t\tengine = new CodexAgent({\n\t\t\t\tmodel: model ?? defaultModels.codex,\n\t\t\t\treasoningEffort,\n\t\t\t\tautoApprove,\n\t\t\t\tlogger,\n\t\t\t});\n\t\t\tbreak;\n\t\tdefault: {\n\t\t\t// Reachable from untyped input that skipped `isAgentProvider`.\n\t\t\tconst unknownProvider: never = provider;\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`\"${String(unknownProvider)}\" is not an agent provider; expected one of: ${agentProviders.join(', ')}`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn new RetryingAgent({ inner: engine, logger });\n}\n\nexport interface CreateOrchestratorOptions {\n\t/**\n\t * Runs every role on one provider, for example when the other one is out of quota. By\n\t * default Codex plans and executes, and Claude reviews.\n\t */\n\tprovider?: AgentProvider;\n\t/** Maps to each provider's permission-bypass mode. Defaults to `false`. */\n\tautoApprove?: boolean;\n\t/** Defaults to warnings on stderr. */\n\tlogger?: ILogger;\n}\n\ninterface Role {\n\tprovider: AgentProvider;\n\tmodel: AgentModel;\n\treasoningEffort: ReasoningEffort;\n}\n\n// A stronger model plans and reviews; a cheaper one executes the plan.\nconst orchestratorRoles = {\n\tdefault: {\n\t\tplanner: { provider: 'codex', model: 'gpt-5.6-sol', reasoningEffort: 'high' },\n\t\texecutor: { provider: 'codex', model: 'gpt-5.6-luna', reasoningEffort: 'xhigh' },\n\t\treviewer: { provider: 'claude', model: 'opus', reasoningEffort: 'high' },\n\t},\n\tclaude: {\n\t\tplanner: { provider: 'claude', model: 'opus', reasoningEffort: 'high' },\n\t\texecutor: { provider: 'claude', model: 'sonnet', reasoningEffort: 'xhigh' },\n\t\treviewer: { provider: 'claude', model: 'opus', reasoningEffort: 'high' },\n\t},\n\tcodex: {\n\t\tplanner: { provider: 'codex', model: 'gpt-5.6-sol', reasoningEffort: 'high' },\n\t\texecutor: { provider: 'codex', model: 'gpt-5.6-luna', reasoningEffort: 'xhigh' },\n\t\treviewer: { provider: 'codex', model: 'gpt-5.6-sol', reasoningEffort: 'high' },\n\t},\n} as const satisfies Record<\n\tAgentProvider | 'default',\n\tRecord<'planner' | 'executor' | 'reviewer', Role>\n>;\n\n/**\n * Builds the planner → executor → reviewer workflow, each role an agent from `createAgent`.\n *\n * @throws {InvalidAgentConfigError} for an unknown provider.\n * @throws {UnrecoverableError} when a Codex role cannot be set up, for example because its CLI\n * binary is missing.\n */\nexport function createOrchestrator({\n\tprovider,\n\tautoApprove,\n\tlogger = new Logger(),\n}: CreateOrchestratorOptions = {}): Agent {\n\tif (provider !== undefined && !isAgentProvider(provider)) {\n\t\tthrow new InvalidAgentConfigError(\n\t\t\t`\"${String(provider)}\" is not an agent provider; expected one of: ${agentProviders.join(', ')}`,\n\t\t);\n\t}\n\n\tconst roles = orchestratorRoles[provider ?? 'default'];\n\tconst agentFor = ({ provider, model, reasoningEffort }: Role) =>\n\t\tcreateAgent(provider, { model, reasoningEffort, autoApprove, logger });\n\n\treturn new OrchestratorAgent({\n\t\tplannerAgent: agentFor(roles.planner),\n\t\texecutorAgent: agentFor(roles.executor),\n\t\treviewerAgent: agentFor(roles.reviewer),\n\t\treviewerDecisionValidator: new ReviewerDecisionValidator(),\n\t\tlogger,\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"agentFactory.js","sourceRoot":"","sources":["../../../src/factory/infrastructure/agentFactory.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Agent } from '../../agent/domain/agent.ts';\nimport { InvalidAgentConfigError } from '../../agent/domain/errors.ts';\nimport {\n\tClaudeAgent,\n\ttype ClaudeModel,\n\ttype ClaudeReasoningEffort,\n} from '../../engines/claude/infrastructure/model/claudeAgent.ts';\nimport {\n\tCodexAgent,\n\ttype CodexModel,\n\ttype CodexReasoningEffort,\n} from '../../engines/codex/infrastructure/model/codexAgent.ts';\nimport { LLMAgent } from '../../engines/domain/model/llmAgent.ts';\nimport { OpenAILLMClient, type OpenAIModel } from '../../llm/infrastructure/openAILLMClient.ts';\nimport { OrchestratorAgent } from '../../orchestration/domain/model/orchestratorAgent.ts';\nimport { ReviewerDecisionValidator } from '../../orchestration/infrastructure/model/reviewerDecisionValidator.ts';\nimport { RetryingAgent } from '../../retry/domain/model/retryingAgent.ts';\nimport { isOneOf } from '../../shared/domain/isOneOf.ts';\nimport type { ILogger } from '../../shared/domain/logger.ts';\nimport { Logger } from '../../shared/infrastructure/logger.ts';\n\nexport const agentProviders = ['claude', 'codex'] as const;\nexport type AgentProvider = (typeof agentProviders)[number];\n\n/** Any provider's model. The engine the provider selects rejects one it does not support. */\nexport type AgentModel = ClaudeModel | CodexModel;\n/** Any provider's reasoning effort. The engine the provider selects rejects one it does not support. */\nexport type ReasoningEffort = ClaudeReasoningEffort | CodexReasoningEffort;\n\nexport function isAgentProvider(value: string): value is AgentProvider {\n\treturn isOneOf(agentProviders, value);\n}\n\nexport interface CreateAgentOptions {\n\t/** Defaults to `'opus'` on Claude and `'gpt-5.6-sol'` on Codex. */\n\tmodel?: AgentModel;\n\t/** Defaults to `'high'`. */\n\treasoningEffort?: ReasoningEffort;\n\t/** Maps to the provider's permission-bypass mode. Defaults to `false`. */\n\tautoApprove?: boolean;\n\t/** Defaults to warnings on stderr. */\n\tlogger?: ILogger;\n}\n\nconst defaultModels = {\n\tclaude: 'opus',\n\tcodex: 'gpt-5.6-sol',\n} as const satisfies Record<AgentProvider, AgentModel>;\n\n/**\n * Builds the agent for a provider, already wrapped in the harness's retry policy.\n *\n * @throws {InvalidAgentConfigError} for an unknown provider, or a model or reasoning effort\n * the provider does not support.\n * @throws {UnrecoverableError} when Codex cannot be set up, for example because its CLI binary\n * is missing.\n */\nexport function createAgent(\n\tprovider: AgentProvider,\n\t{ model, reasoningEffort, autoApprove, logger = new Logger() }: CreateAgentOptions = {},\n): Agent {\n\tlet engine: Agent;\n\n\tswitch (provider) {\n\t\tcase 'claude':\n\t\t\tengine = new ClaudeAgent({\n\t\t\t\tmodel: model ?? defaultModels.claude,\n\t\t\t\treasoningEffort,\n\t\t\t\tautoApprove,\n\t\t\t\tlogger,\n\t\t\t});\n\t\t\tbreak;\n\t\tcase 'codex':\n\t\t\tengine = new CodexAgent({\n\t\t\t\tmodel: model ?? defaultModels.codex,\n\t\t\t\treasoningEffort,\n\t\t\t\tautoApprove,\n\t\t\t\tlogger,\n\t\t\t});\n\t\t\tbreak;\n\t\tdefault: {\n\t\t\t// Reachable from untyped input that skipped `isAgentProvider`.\n\t\t\tconst unknownProvider: never = provider;\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`\"${String(unknownProvider)}\" is not an agent provider; expected one of: ${agentProviders.join(', ')}`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn new RetryingAgent({ inner: engine, logger });\n}\n\nexport interface CreateOrchestratorOptions {\n\t/**\n\t * Runs every role on one provider, for example when the other one is out of quota. By\n\t * default Codex plans and executes, and Claude reviews.\n\t */\n\tprovider?: AgentProvider;\n\t/** Maps to each provider's permission-bypass mode. Defaults to `false`. */\n\tautoApprove?: boolean;\n\t/** Defaults to warnings on stderr. */\n\tlogger?: ILogger;\n}\n\ninterface Role {\n\tprovider: AgentProvider;\n\tmodel: AgentModel;\n\treasoningEffort: ReasoningEffort;\n}\n\n// A stronger model plans and reviews; a cheaper one executes the plan.\nconst orchestratorRoles = {\n\tdefault: {\n\t\tplanner: { provider: 'codex', model: 'gpt-5.6-sol', reasoningEffort: 'high' },\n\t\texecutor: { provider: 'codex', model: 'gpt-5.6-luna', reasoningEffort: 'xhigh' },\n\t\treviewer: { provider: 'claude', model: 'opus', reasoningEffort: 'high' },\n\t},\n\tclaude: {\n\t\tplanner: { provider: 'claude', model: 'opus', reasoningEffort: 'high' },\n\t\texecutor: { provider: 'claude', model: 'sonnet', reasoningEffort: 'xhigh' },\n\t\treviewer: { provider: 'claude', model: 'opus', reasoningEffort: 'high' },\n\t},\n\tcodex: {\n\t\tplanner: { provider: 'codex', model: 'gpt-5.6-sol', reasoningEffort: 'high' },\n\t\texecutor: { provider: 'codex', model: 'gpt-5.6-luna', reasoningEffort: 'xhigh' },\n\t\treviewer: { provider: 'codex', model: 'gpt-5.6-sol', reasoningEffort: 'high' },\n\t},\n} as const satisfies Record<\n\tAgentProvider | 'default',\n\tRecord<'planner' | 'executor' | 'reviewer', Role>\n>;\n\n/**\n * Builds the planner → executor → reviewer workflow, each role an agent from `createAgent`.\n *\n * @throws {InvalidAgentConfigError} for an unknown provider.\n * @throws {UnrecoverableError} when a Codex role cannot be set up, for example because its CLI\n * binary is missing.\n */\nexport function createOrchestrator({\n\tprovider,\n\tautoApprove,\n\tlogger = new Logger(),\n}: CreateOrchestratorOptions = {}): Agent {\n\tif (provider !== undefined && !isAgentProvider(provider)) {\n\t\tthrow new InvalidAgentConfigError(\n\t\t\t`\"${String(provider)}\" is not an agent provider; expected one of: ${agentProviders.join(', ')}`,\n\t\t);\n\t}\n\n\tconst roles = orchestratorRoles[provider ?? 'default'];\n\tconst agentFor = ({ provider, model, reasoningEffort }: Role) =>\n\t\tcreateAgent(provider, { model, reasoningEffort, autoApprove, logger });\n\n\treturn new OrchestratorAgent({\n\t\tplannerAgent: agentFor(roles.planner),\n\t\texecutorAgent: agentFor(roles.executor),\n\t\treviewerAgent: agentFor(roles.reviewer),\n\t\treviewerDecisionValidator: new ReviewerDecisionValidator(),\n\t\tlogger,\n\t});\n}\n\nexport interface CreateLLMAgentOptions {\n\t/** Defaults to `'gpt-5.6-luna'`. The OpenAI client rejects a model it does not support. */\n\tmodel?: string;\n\t/** Sent as the model's instructions on every call. */\n\tsystemPrompt: string;\n\t/** Defaults to warnings on stderr. */\n\tlogger?: ILogger;\n}\n\nconst defaultLLMAgentModel = 'gpt-5.6-luna' satisfies OpenAIModel;\n\n/**\n * Builds an agent whose conversation MiKode owns, on the OpenAI Responses API, already wrapped\n * in the harness's retry policy. Internal while #23 reaches parity with the Agent SDK engines:\n * it is not exported from `src/index.ts`, and it takes no provider because OpenAI is the only\n * `LLMClient` so far. It runs no tools, so it has no `autoApprove`.\n *\n * @throws {InvalidAgentConfigError} for a model the OpenAI client does not support.\n * @throws {UnrecoverableError} when the OpenAI client cannot be set up, for example because\n * `OPENAI_API_KEY` is missing.\n */\nexport function createLLMAgent({\n\tmodel = defaultLLMAgentModel,\n\tsystemPrompt,\n\tlogger = new Logger(),\n}: CreateLLMAgentOptions): Agent {\n\tconst engine = new LLMAgent({\n\t\tllmClient: new OpenAILLMClient({ model, systemPrompt, logger }),\n\t});\n\n\t// LLMAgent records nothing from a failed call, so the original prompt is the whole story.\n\treturn new RetryingAgent({ inner: engine, logger, noteFailures: false });\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,3 +3,4 @@ export { agentProviders, createAgent, createOrchestrator, isAgentProvider, type
|
|
|
3
3
|
export { type Agent, type AgentResponse, type Callback, type ProgressEvent, } from './agent/domain/agent.ts';
|
|
4
4
|
export { InvalidAgentConfigError, RecoverableError, UnrecoverableError, } from './agent/domain/errors.ts';
|
|
5
5
|
export { isAbortError } from './shared/domain/isAbortError.ts';
|
|
6
|
+
export type { Tokens } from './shared/domain/tokens.ts';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACN,cAAc,EACd,WAAW,EACX,kBAAkB,EAClB,eAAe,GAMf,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAKN,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACN,uBAAuB,EACvB,gBAAgB,EAChB,kBAAkB,GAClB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC","sourcesContent":["export type { ILogger } from './shared/domain/logger.ts';\nexport {\n\tagentProviders,\n\tcreateAgent,\n\tcreateOrchestrator,\n\tisAgentProvider,\n\ttype AgentModel,\n\ttype AgentProvider,\n\ttype CreateAgentOptions,\n\ttype CreateOrchestratorOptions,\n\ttype ReasoningEffort,\n} from './factory/infrastructure/agentFactory.ts';\nexport {\n\ttype Agent,\n\ttype AgentResponse,\n\ttype Callback,\n\ttype ProgressEvent,\n} from './agent/domain/agent.ts';\nexport {\n\tInvalidAgentConfigError,\n\tRecoverableError,\n\tUnrecoverableError,\n} from './agent/domain/errors.ts';\nexport { isAbortError } from './shared/domain/isAbortError.ts';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACN,cAAc,EACd,WAAW,EACX,kBAAkB,EAClB,eAAe,GAMf,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAKN,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACN,uBAAuB,EACvB,gBAAgB,EAChB,kBAAkB,GAClB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC","sourcesContent":["export type { ILogger } from './shared/domain/logger.ts';\nexport {\n\tagentProviders,\n\tcreateAgent,\n\tcreateOrchestrator,\n\tisAgentProvider,\n\ttype AgentModel,\n\ttype AgentProvider,\n\ttype CreateAgentOptions,\n\ttype CreateOrchestratorOptions,\n\ttype ReasoningEffort,\n} from './factory/infrastructure/agentFactory.ts';\nexport {\n\ttype Agent,\n\ttype AgentResponse,\n\ttype Callback,\n\ttype ProgressEvent,\n} from './agent/domain/agent.ts';\nexport {\n\tInvalidAgentConfigError,\n\tRecoverableError,\n\tUnrecoverableError,\n} from './agent/domain/errors.ts';\nexport { isAbortError } from './shared/domain/isAbortError.ts';\nexport type { Tokens } from './shared/domain/tokens.ts';\n"]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Message } from './message.ts';
|
|
2
|
+
/**
|
|
3
|
+
* What one agent remembers across its runs. The LLM never sees this object, only the
|
|
4
|
+
* context built from it, so how the context is derived (compaction, later) can change
|
|
5
|
+
* without touching either side.
|
|
6
|
+
*/
|
|
7
|
+
export declare class Conversation {
|
|
8
|
+
private readonly messages;
|
|
9
|
+
constructor(messages?: Message[]);
|
|
10
|
+
/**
|
|
11
|
+
* Records a prompt and its answer together, once the call succeeded. A failed call
|
|
12
|
+
* records nothing, so a retry of the same prompt cannot appear twice.
|
|
13
|
+
*/
|
|
14
|
+
addExchange(prompt: Message, answer: Message): void;
|
|
15
|
+
/**
|
|
16
|
+
* A deep copy, like everything the conversation takes in: a client that rewrites the
|
|
17
|
+
* messages it was sent cannot change what the next turn sends.
|
|
18
|
+
*/
|
|
19
|
+
getContext(): Message[];
|
|
20
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What one agent remembers across its runs. The LLM never sees this object, only the
|
|
3
|
+
* context built from it, so how the context is derived (compaction, later) can change
|
|
4
|
+
* without touching either side.
|
|
5
|
+
*/
|
|
6
|
+
export class Conversation {
|
|
7
|
+
messages;
|
|
8
|
+
constructor(messages = []) {
|
|
9
|
+
this.messages = structuredClone(messages);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Records a prompt and its answer together, once the call succeeded. A failed call
|
|
13
|
+
* records nothing, so a retry of the same prompt cannot appear twice.
|
|
14
|
+
*/
|
|
15
|
+
addExchange(prompt, answer) {
|
|
16
|
+
this.messages.push(structuredClone(prompt), structuredClone(answer));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A deep copy, like everything the conversation takes in: a client that rewrites the
|
|
20
|
+
* messages it was sent cannot change what the next turn sends.
|
|
21
|
+
*/
|
|
22
|
+
getContext() {
|
|
23
|
+
return structuredClone(this.messages);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=conversation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation.js","sourceRoot":"","sources":["../../../src/llm/domain/conversation.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,OAAO,YAAY;IACP,QAAQ,CAAY;IAErC,YAAY,WAAsB,EAAE;QACnC,IAAI,CAAC,QAAQ,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC3C,CAAC;IAED;;;OAGG;IACH,WAAW,CAAC,MAAe,EAAE,MAAe;QAC3C,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC;IACtE,CAAC;IAED;;;OAGG;IACH,UAAU;QACT,OAAO,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACvC,CAAC;CACD","sourcesContent":["import type { Message } from './message.ts';\n\n/**\n * What one agent remembers across its runs. The LLM never sees this object, only the\n * context built from it, so how the context is derived (compaction, later) can change\n * without touching either side.\n */\nexport class Conversation {\n\tprivate readonly messages: Message[];\n\n\tconstructor(messages: Message[] = []) {\n\t\tthis.messages = structuredClone(messages);\n\t}\n\n\t/**\n\t * Records a prompt and its answer together, once the call succeeded. A failed call\n\t * records nothing, so a retry of the same prompt cannot appear twice.\n\t */\n\taddExchange(prompt: Message, answer: Message): void {\n\t\tthis.messages.push(structuredClone(prompt), structuredClone(answer));\n\t}\n\n\t/**\n\t * A deep copy, like everything the conversation takes in: a client that rewrites the\n\t * messages it was sent cannot change what the next turn sends.\n\t */\n\tgetContext(): Message[] {\n\t\treturn structuredClone(this.messages);\n\t}\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/llm/domain/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAElE,kGAAkG;AAClG,MAAM,OAAO,eAAgB,SAAQ,kBAAkB;CAAG","sourcesContent":["import { UnrecoverableError } from '../../agent/domain/errors.ts';\n\n/** The context no longer fits the model's window; the agent must compact before sending again. */\nexport class MaxContextError extends UnrecoverableError {}\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Tokens } from '../../shared/domain/tokens.ts';
|
|
2
|
+
import type { Message } from './message.ts';
|
|
3
|
+
export type StopReason = 'completed' | 'truncated' | 'refused';
|
|
4
|
+
export interface LLMResponse {
|
|
5
|
+
message: Message & {
|
|
6
|
+
role: 'assistant';
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Missing when the provider did not report it. The call was made and may have been billed,
|
|
10
|
+
* so the agent keeps the answer and only its tokens become unknown; a failure raised from
|
|
11
|
+
* such a response is marked `usageUnreported`.
|
|
12
|
+
*/
|
|
13
|
+
usage: Tokens | undefined;
|
|
14
|
+
stopReason: StopReason;
|
|
15
|
+
}
|
|
16
|
+
export interface LLMClient {
|
|
17
|
+
send(context: Message[], signal: AbortSignal): Promise<LLMResponse>;
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"llm.js","sourceRoot":"","sources":["../../../src/llm/domain/llm.ts"],"names":[],"mappings":"","sourcesContent":["import type { Tokens } from '../../shared/domain/tokens.ts';\nimport type { Message } from './message.ts';\n\nexport type StopReason = 'completed' | 'truncated' | 'refused';\n\nexport interface LLMResponse {\n\tmessage: Message & { role: 'assistant' };\n\t/**\n\t * Missing when the provider did not report it. The call was made and may have been billed,\n\t * so the agent keeps the answer and only its tokens become unknown; a failure raised from\n\t * such a response is marked `usageUnreported`.\n\t */\n\tusage: Tokens | undefined;\n\tstopReason: StopReason;\n}\n\nexport interface LLMClient {\n\tsend(context: Message[], signal: AbortSignal): Promise<LLMResponse>;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message.js","sourceRoot":"","sources":["../../../src/llm/domain/message.ts"],"names":[],"mappings":"","sourcesContent":["export type MessagePart = { type: 'text'; text: string } | { type: 'reasoning'; text: string };\n\nexport interface Message {\n\trole: 'user' | 'assistant';\n\tcontent: MessagePart[];\n}\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ILogger } from '../../shared/domain/logger.ts';
|
|
2
|
+
import type { LLMClient, LLMResponse } from '../domain/llm.ts';
|
|
3
|
+
import type { Message } from '../domain/message.ts';
|
|
4
|
+
export declare const openAIModels: readonly ["gpt-6-astra", "gpt-5.6-sol", "gpt-5.6-luna", "gpt-5.6-terra"];
|
|
5
|
+
export type OpenAIModel = (typeof openAIModels)[number];
|
|
6
|
+
/**
|
|
7
|
+
* A stateless `LLMClient` on the OpenAI Responses API. With `store: false` OpenAI keeps
|
|
8
|
+
* nothing between calls, so every call carries the whole context: the conversation is
|
|
9
|
+
* MiKode's, not the provider's.
|
|
10
|
+
*/
|
|
11
|
+
export declare class OpenAILLMClient implements LLMClient {
|
|
12
|
+
private readonly client;
|
|
13
|
+
private readonly model;
|
|
14
|
+
private readonly systemPrompt;
|
|
15
|
+
private readonly logger;
|
|
16
|
+
constructor({ model, systemPrompt, logger, }: {
|
|
17
|
+
model: string;
|
|
18
|
+
systemPrompt: string;
|
|
19
|
+
logger: ILogger;
|
|
20
|
+
});
|
|
21
|
+
send(context: Message[], signal: AbortSignal): Promise<LLMResponse>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import OpenAI, { APIError } from 'openai';
|
|
2
|
+
import { InvalidAgentConfigError, RecoverableError, UnrecoverableError, withSpentTokens, } from "../../agent/domain/errors.js";
|
|
3
|
+
import { classifyHostFailure, classifyLocalFailure, classifyProviderFailure, describeFailure, treatErrors, } from "../../agent/domain/providerFailure.js";
|
|
4
|
+
import { isOneOf } from "../../shared/domain/isOneOf.js";
|
|
5
|
+
import { MaxContextError } from "../domain/errors.js";
|
|
6
|
+
// The SDK types `model` as a plain string, so this list is maintained by hand.
|
|
7
|
+
export const openAIModels = [
|
|
8
|
+
'gpt-6-astra',
|
|
9
|
+
'gpt-5.6-sol',
|
|
10
|
+
'gpt-5.6-luna',
|
|
11
|
+
'gpt-5.6-terra',
|
|
12
|
+
];
|
|
13
|
+
// The request itself is wrong or not allowed, so sending it again cannot succeed.
|
|
14
|
+
const unrecoverableStatuses = [400, 401, 403, 404, 422];
|
|
15
|
+
// Out of credit or over a spend limit. These arrive as a 429 like a rate limit, but the account
|
|
16
|
+
// stays blocked however often the request is sent.
|
|
17
|
+
const exhaustedQuotaCodes = [
|
|
18
|
+
'insufficient_quota',
|
|
19
|
+
'credit_balance_exhausted',
|
|
20
|
+
'project_spend_limit_exceeded',
|
|
21
|
+
];
|
|
22
|
+
// The codes OpenAI gives a failed response that a new attempt can succeed past.
|
|
23
|
+
const transientResponseErrorCodes = ['server_error', 'rate_limit_exceeded'];
|
|
24
|
+
/**
|
|
25
|
+
* Only the text crosses back. OpenAI replays reasoning only from the encrypted item it
|
|
26
|
+
* returned, which a `Message` does not keep, so a reasoning part stays in the conversation
|
|
27
|
+
* for narration and is left out of the request. A message with no text is left out whole.
|
|
28
|
+
*/
|
|
29
|
+
function toOpenAIInput(message) {
|
|
30
|
+
const text = message.content
|
|
31
|
+
.filter(part => part.type === 'text')
|
|
32
|
+
.map(part => part.text)
|
|
33
|
+
.join('\n');
|
|
34
|
+
return text ? [{ role: message.role, content: text }] : [];
|
|
35
|
+
}
|
|
36
|
+
function describeItem(item, logger) {
|
|
37
|
+
switch (item.type) {
|
|
38
|
+
case 'message':
|
|
39
|
+
// A refusal is not an answer: it only sets the stop reason.
|
|
40
|
+
return item.content.flatMap(content => content.type === 'output_text' ? [{ type: 'text', text: content.text }] : []);
|
|
41
|
+
case 'reasoning': {
|
|
42
|
+
// The summary is the readable part; the encrypted content is opaque by design.
|
|
43
|
+
const text = item.summary.map(summary => summary.text).join('\n');
|
|
44
|
+
return text ? [{ type: 'reasoning', text }] : [];
|
|
45
|
+
}
|
|
46
|
+
default:
|
|
47
|
+
// No tools are sent yet, so any other item is output this client does not expect.
|
|
48
|
+
treatErrors(() => {
|
|
49
|
+
logger.warn(item, 'OpenAI returned an output item the client does not map');
|
|
50
|
+
}, classifyHostFailure, 'OpenAI client logger failed while mapping a response');
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function toStopReason(response) {
|
|
55
|
+
if (response.status === 'incomplete') {
|
|
56
|
+
return response.incomplete_details?.reason === 'content_filter' ? 'refused' : 'truncated';
|
|
57
|
+
}
|
|
58
|
+
if (response.status !== 'completed') {
|
|
59
|
+
const cause = response.error?.message ??
|
|
60
|
+
`The response ended with status "${response.status ?? 'unknown'}".`;
|
|
61
|
+
// The same failure is retried when it arrives as an SDK error, so it must be here too.
|
|
62
|
+
if (response.error && transientResponseErrorCodes.includes(response.error.code)) {
|
|
63
|
+
throw new RecoverableError('The OpenAI response failed', { cause });
|
|
64
|
+
}
|
|
65
|
+
throw new UnrecoverableError('The OpenAI response did not complete', { cause });
|
|
66
|
+
}
|
|
67
|
+
const refused = response.output.some(item => item.type === 'message' && item.content.some(content => content.type === 'refusal'));
|
|
68
|
+
return refused ? 'refused' : 'completed';
|
|
69
|
+
}
|
|
70
|
+
/** OpenAI counts both cache reads and cache writes inside `input_tokens`. */
|
|
71
|
+
function toTokens(usage, logger) {
|
|
72
|
+
if (!usage) {
|
|
73
|
+
// Zeros would report a call that may have been billed as free.
|
|
74
|
+
treatErrors(() => {
|
|
75
|
+
logger.warn('OpenAI returned a response without usage; it cannot be accounted for');
|
|
76
|
+
}, classifyHostFailure, 'OpenAI client logger failed while reading usage');
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
const { cached_tokens, cache_write_tokens } = usage.input_tokens_details;
|
|
80
|
+
return {
|
|
81
|
+
inputTokens: usage.input_tokens - cached_tokens - cache_write_tokens,
|
|
82
|
+
readCacheTokens: cached_tokens,
|
|
83
|
+
writtenCacheTokens: cache_write_tokens,
|
|
84
|
+
outputTokens: usage.output_tokens,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function toLLMResponse(response, logger) {
|
|
88
|
+
let usage;
|
|
89
|
+
try {
|
|
90
|
+
// Inside the try: it warns when the usage is missing, and that warning can throw too.
|
|
91
|
+
usage = toTokens(response.usage, logger);
|
|
92
|
+
return {
|
|
93
|
+
message: {
|
|
94
|
+
role: 'assistant',
|
|
95
|
+
content: response.output.flatMap(item => describeItem(item, logger)),
|
|
96
|
+
},
|
|
97
|
+
usage,
|
|
98
|
+
stopReason: toStopReason(response),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
// The call was billed whether the response failed or a logger did while mapping it, and
|
|
103
|
+
// an answer without usage leaves the run's count unknown.
|
|
104
|
+
throw withSpentTokens(error, usage, !response.usage);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function classifyOpenAIFailure(error) {
|
|
108
|
+
if (error instanceof APIError) {
|
|
109
|
+
if (error.code === 'context_length_exceeded') {
|
|
110
|
+
return new MaxContextError('The context no longer fits the OpenAI model', {
|
|
111
|
+
cause: describeFailure(error),
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
if (exhaustedQuotaCodes.some(code => code === error.code) ||
|
|
115
|
+
unrecoverableStatuses.some(status => status === error.status)) {
|
|
116
|
+
return new UnrecoverableError('OpenAI rejected the request', {
|
|
117
|
+
cause: describeFailure(error),
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return classifyProviderFailure(error, 'The OpenAI request failed');
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* A stateless `LLMClient` on the OpenAI Responses API. With `store: false` OpenAI keeps
|
|
125
|
+
* nothing between calls, so every call carries the whole context: the conversation is
|
|
126
|
+
* MiKode's, not the provider's.
|
|
127
|
+
*/
|
|
128
|
+
export class OpenAILLMClient {
|
|
129
|
+
client;
|
|
130
|
+
model;
|
|
131
|
+
systemPrompt;
|
|
132
|
+
logger;
|
|
133
|
+
// `model` is untyped on purpose: this adapter is the one that knows what OpenAI supports.
|
|
134
|
+
constructor({ model, systemPrompt, logger, }) {
|
|
135
|
+
if (!isOneOf(openAIModels, model)) {
|
|
136
|
+
throw new InvalidAgentConfigError(`"${model}" is not an OpenAI model; expected one of: ${openAIModels.join(', ')}`);
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
// The SDK's default retries stay on: they back off and honour `Retry-After`, which
|
|
140
|
+
// `RetryingAgent` does not. See "The OpenAI SDK keeps its transport retries" in decisions.md.
|
|
141
|
+
this.client = new OpenAI();
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
// A missing API key, for example: building the client again cannot fix it.
|
|
145
|
+
throw new UnrecoverableError('The OpenAI client could not be set up', {
|
|
146
|
+
cause: describeFailure(error),
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
this.model = model;
|
|
150
|
+
this.systemPrompt = systemPrompt;
|
|
151
|
+
this.logger = logger;
|
|
152
|
+
}
|
|
153
|
+
async send(context, signal) {
|
|
154
|
+
let response;
|
|
155
|
+
try {
|
|
156
|
+
response = await this.client.responses.create({
|
|
157
|
+
model: this.model,
|
|
158
|
+
instructions: this.systemPrompt,
|
|
159
|
+
input: context.flatMap(toOpenAIInput),
|
|
160
|
+
store: false,
|
|
161
|
+
reasoning: { summary: 'auto' },
|
|
162
|
+
}, { signal });
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
// The SDK's abort error is named 'Error', so it would pass for a provider failure.
|
|
166
|
+
signal.throwIfAborted();
|
|
167
|
+
throw classifyOpenAIFailure(error);
|
|
168
|
+
}
|
|
169
|
+
return treatErrors(() => toLLMResponse(response, this.logger), classifyLocalFailure, 'OpenAI response mapping failed');
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=openAILLMClient.js.map
|