@mikode13/harness 2.2.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +6 -3
  2. package/dist/engines/domain/model/llmAgent.d.ts +1 -1
  3. package/dist/engines/domain/model/llmAgent.js.map +1 -1
  4. package/dist/factory/infrastructure/agentFactory.d.ts +15 -46
  5. package/dist/factory/infrastructure/agentFactory.js +46 -77
  6. package/dist/factory/infrastructure/agentFactory.js.map +1 -1
  7. package/dist/factory/infrastructure/agentLLMFactory.d.ts +44 -0
  8. package/dist/factory/infrastructure/agentLLMFactory.js +98 -0
  9. package/dist/factory/infrastructure/agentLLMFactory.js.map +1 -0
  10. package/dist/factory/infrastructure/types.d.ts +33 -0
  11. package/dist/factory/infrastructure/types.js +3 -0
  12. package/dist/factory/infrastructure/types.js.map +1 -0
  13. package/dist/index.d.ts +2 -1
  14. package/dist/index.js +2 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/llm/domain/tool.d.ts +1 -8
  17. package/dist/llm/domain/tool.js.map +1 -1
  18. package/dist/llm/infrastructure/claudeLLMClient.d.ts +6 -2
  19. package/dist/llm/infrastructure/claudeLLMClient.js +26 -5
  20. package/dist/llm/infrastructure/claudeLLMClient.js.map +1 -1
  21. package/dist/llm/infrastructure/openAILLMClient.d.ts +5 -1
  22. package/dist/llm/infrastructure/openAILLMClient.js +17 -3
  23. package/dist/llm/infrastructure/openAILLMClient.js.map +1 -1
  24. package/dist/orchestration/domain/model/instructedAgent.d.ts +15 -0
  25. package/dist/orchestration/domain/model/instructedAgent.js +17 -0
  26. package/dist/orchestration/domain/model/instructedAgent.js.map +1 -0
  27. package/dist/orchestration/domain/model/orchestratorAgent.d.ts +3 -0
  28. package/dist/orchestration/domain/model/orchestratorAgent.js +9 -9
  29. package/dist/orchestration/domain/model/orchestratorAgent.js.map +1 -1
  30. package/dist/tools/domain/tool.d.ts +9 -0
  31. package/dist/tools/domain/tool.js +2 -0
  32. package/dist/tools/domain/tool.js.map +1 -0
  33. package/dist/tools/domain/workspace.d.ts +48 -0
  34. package/dist/tools/domain/workspace.js +2 -0
  35. package/dist/tools/domain/workspace.js.map +1 -0
  36. package/dist/tools/infrastructure/boundedWorkspace.d.ts +66 -0
  37. package/dist/tools/infrastructure/boundedWorkspace.js +160 -0
  38. package/dist/tools/infrastructure/boundedWorkspace.js.map +1 -0
  39. package/dist/tools/infrastructure/createWorkspace.d.ts +14 -0
  40. package/dist/tools/infrastructure/createWorkspace.js +54 -0
  41. package/dist/tools/infrastructure/createWorkspace.js.map +1 -0
  42. package/dist/tools/infrastructure/defineTool.d.ts +15 -0
  43. package/dist/tools/infrastructure/defineTool.js +87 -0
  44. package/dist/tools/infrastructure/defineTool.js.map +1 -0
  45. package/dist/tools/infrastructure/gitWorkspace.d.ts +13 -0
  46. package/dist/tools/infrastructure/gitWorkspace.js +61 -0
  47. package/dist/tools/infrastructure/gitWorkspace.js.map +1 -0
  48. package/dist/tools/infrastructure/ripgrepWorkspace.d.ts +20 -0
  49. package/dist/tools/infrastructure/ripgrepWorkspace.js +85 -0
  50. package/dist/tools/infrastructure/ripgrepWorkspace.js.map +1 -0
  51. package/dist/tools/infrastructure/runProcess.d.ts +23 -0
  52. package/dist/tools/infrastructure/runProcess.js +91 -0
  53. package/dist/tools/infrastructure/runProcess.js.map +1 -0
  54. package/dist/tools/infrastructure/workspaceTools.d.ts +4 -0
  55. package/dist/tools/infrastructure/workspaceTools.js +110 -0
  56. package/dist/tools/infrastructure/workspaceTools.js.map +1 -0
  57. package/package.json +2 -1
package/README.md CHANGED
@@ -111,7 +111,7 @@ factories let you replace through their `logger` option:
111
111
  ```ts
112
112
  import { createAgent, UnrecoverableError, type ProgressEvent } from '@mikode13/harness';
113
113
 
114
- const agent = createAgent('claude', { model: 'sonnet' });
114
+ const agent = createAgent('anthropic', { model: 'sonnet' });
115
115
  const controller = new AbortController();
116
116
 
117
117
  const render = (event: ProgressEvent) => {
@@ -133,10 +133,13 @@ try {
133
133
  consumer's decision, not the harness's. `cli/progressEventFormatter.ts` is one
134
134
  terminal-shaped implementation to copy from. New event types can arrive in minor
135
135
  releases, so render the ones you know and ignore the rest. Swapping the agent for
136
- `createAgent('codex')`, or for `createOrchestrator()` and its planner → executor →
136
+ `createAgent('openai')`, or for `createOrchestrator()` and its planner → executor →
137
137
  reviewer workflow, changes nothing else in the snippet above.
138
- `createOrchestrator({ provider: 'claude' })` runs every role on one provider, for
138
+ `createOrchestrator({ provider: 'anthropic' })` runs every role on one provider, for
139
139
  example when the other one is out of quota.
140
+ `systemPrompts: { planner, executor, reviewer }` replaces a role's instructions;
141
+ a role left out keeps the harness's own, and the reviewer must still answer with
142
+ the JSON decision described on the option.
140
143
 
141
144
  Every agent a factory returns already retries recoverable failures. A model or
142
145
  reasoning effort the chosen provider does not support throws
@@ -1,7 +1,7 @@
1
1
  import type { Agent, AgentResponse, Callback } from '#src/agent/domain/agent';
2
2
  import type { LLMClient } from '#src/llm/domain/llm';
3
3
  import type { Message } from '#src/llm/domain/message';
4
- import type { Tool } from '#src/llm/domain/tool';
4
+ import type { Tool } from '#src/tools/domain/tool';
5
5
  /**
6
6
  * An agent whose conversation is owned by MiKode: every run sends the whole context to a
7
7
  * stateless `LLMClient`. Within a run the model may call the agent's tools; the agent runs them
@@ -1 +1 @@
1
- {"version":3,"file":"llmAgent.js","sourceRoot":"","sources":["../../../../src/engines/domain/model/llmAgent.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Agent, AgentResponse, Callback, ProgressEvent } from '#src/agent/domain/agent';\nimport {\n\tInvalidAgentConfigError,\n\tRecoverableError,\n\tUnrecoverableError,\n\twithSpentTokens,\n} from '#src/agent/domain/errors';\nimport {\n\tclassifyHostFailure,\n\tclassifyProviderFailure,\n\tdescribeFailure,\n\ttreatErrors,\n} from '#src/agent/domain/providerFailure';\nimport { Conversation } from '#src/llm/domain/conversation';\nimport type { LLMClient, LLMResponse } from '#src/llm/domain/llm';\nimport type { Message, MessagePart, ToolCallPart, ToolResultPart } from '#src/llm/domain/message';\nimport type { Tool, ToolDefinition } from '#src/llm/domain/tool';\nimport { addTokens, type Tokens } from '#src/shared/domain/tokens';\n\nfunction describePart(part: MessagePart): ProgressEvent | undefined {\n\tswitch (part.type) {\n\t\tcase 'text':\n\t\t\treturn { type: 'agentMessage', message: part.text };\n\t\tcase 'reasoning':\n\t\t\treturn { type: 'reasoning', message: part.text };\n\t\tcase 'toolCall':\n\t\t\treturn { type: 'tool', id: part.id, name: part.name, status: 'in_progress' };\n\t\tcase 'toolResult':\n\t\t\treturn {\n\t\t\t\ttype: 'tool',\n\t\t\t\tid: part.callId,\n\t\t\t\tname: part.name,\n\t\t\t\tstatus: part.isError ? 'error' : 'completed',\n\t\t\t};\n\t\tcase 'providerData':\n\t\t\t// Opaque by design: its readable side, if any, arrives as a reasoning part.\n\t\t\treturn undefined;\n\t}\n}\n\nfunction narrate(part: MessagePart, callback: Callback): void {\n\tconst event = describePart(part);\n\tif (!event) {\n\t\treturn;\n\t}\n\n\ttreatErrors(\n\t\t() => {\n\t\t\tcallback(event);\n\t\t},\n\t\tclassifyHostFailure,\n\t\t'LLM agent progress callback failed',\n\t);\n}\n\n/**\n * An agent whose conversation is owned by MiKode: every run sends the whole context to a\n * stateless `LLMClient`. Within a run the model may call the agent's tools; the agent runs them\n * and sends their results back until the model answers without calling one. Turn boundaries\n * are the consumer's to emit, as with the other engines; this agent only narrates what the\n * model produced and what its tools did.\n */\nexport class LLMAgent implements Agent {\n\tprivate readonly llmClient: LLMClient;\n\tprivate readonly conversation: Conversation;\n\tprivate readonly tools: Map<string, Tool>;\n\tprivate readonly toolDefinitions: ToolDefinition[];\n\tprivate readonly maxSteps: number;\n\n\tconstructor({\n\t\tllmClient,\n\t\tmessages,\n\t\ttools = [],\n\t\tmaxSteps = 25,\n\t}: {\n\t\tllmClient: LLMClient;\n\t\tmessages?: Message[];\n\t\ttools?: Tool[];\n\t\t/** How many calls to the model one run may make before it fails. */\n\t\tmaxSteps?: number;\n\t}) {\n\t\tif (!Number.isInteger(maxSteps) || maxSteps < 1) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`maxSteps must be a positive integer; got ${String(maxSteps)}`,\n\t\t\t);\n\t\t}\n\n\t\tthis.llmClient = llmClient;\n\t\tthis.conversation = new Conversation(messages);\n\t\tthis.maxSteps = maxSteps;\n\t\tthis.tools = new Map(tools.map(tool => [tool.name, tool]));\n\t\tif (this.tools.size !== tools.length) {\n\t\t\t// The model calls tools by name, so a second one with the same name could never run.\n\t\t\tthrow new InvalidAgentConfigError('Two tools share a name; each name must be unique');\n\t\t}\n\t\t// The client only describes the tools to the model; running them stays with the agent.\n\t\tthis.toolDefinitions = tools.map(({ name, description, inputSchema }) => ({\n\t\t\tname,\n\t\t\tdescription,\n\t\t\tinputSchema,\n\t\t}));\n\t}\n\n\tprivate async llmCall(runMessages: Message[], signal: AbortSignal): Promise<LLMResponse> {\n\t\tlet response: LLMResponse;\n\t\ttry {\n\t\t\t// Sent as a copy, so what the run records is what the agent built, whatever the client does.\n\t\t\tresponse = await this.llmClient.send(\n\t\t\t\t{\n\t\t\t\t\tcontext: [...this.conversation.getContext(), ...structuredClone(runMessages)],\n\t\t\t\t\ttools: this.toolDefinitions,\n\t\t\t\t},\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\treturn response;\n\t}\n\n\t/**\n\t * Always answers the call: a missing tool or a failing one becomes an error result, so the\n\t * model can correct itself. Only a cancellation escapes, whatever error the tool turned it into.\n\t */\n\tprivate async runTool(call: ToolCallPart, signal: AbortSignal): Promise<ToolResultPart> {\n\t\tconst result = { type: 'toolResult' as const, callId: call.id, name: call.name };\n\t\tconst tool = this.tools.get(call.name);\n\t\tif (!tool) {\n\t\t\treturn {\n\t\t\t\t...result,\n\t\t\t\toutput: `There is no tool named \"${call.name}\". Available tools: ${[...this.tools.keys()].join(', ') || 'none'}.`,\n\t\t\t\tisError: true,\n\t\t\t};\n\t\t}\n\n\t\t// Checked again at the last moment: the consumer may cancel on the announcement itself.\n\t\tsignal.throwIfAborted();\n\t\ttry {\n\t\t\treturn { ...result, output: await tool.execute(call.input, signal), isError: false };\n\t\t} catch (error) {\n\t\t\tsignal.throwIfAborted();\n\t\t\treturn { ...result, output: describeFailure(error), isError: true };\n\t\t}\n\t}\n\n\t/** One at a time and in order, so their effects and their narration never interleave. */\n\tprivate async runTools(\n\t\tcalls: ToolCallPart[],\n\t\tsignal: AbortSignal,\n\t\tcallback: Callback,\n\t): Promise<Message> {\n\t\tconst results: MessagePart[] = [];\n\t\tfor (const call of calls) {\n\t\t\t// A call before this one may have ignored the cancellation; this one must not start,\n\t\t\t// nor be announced as running.\n\t\t\tsignal.throwIfAborted();\n\t\t\t// Announced only as its turn comes, so a consumer never shows a call as running early.\n\t\t\tnarrate(call, callback);\n\t\t\tconst result = await this.runTool(call, signal);\n\t\t\tnarrate(result, callback);\n\t\t\tresults.push(result);\n\t\t}\n\n\t\treturn { role: 'tool', content: results };\n\t}\n\n\tasync run(prompt: string, signal: AbortSignal, callback: Callback): Promise<AgentResponse> {\n\t\tconst start = Date.now();\n\t\tconst runMessages: Message[] = [{ role: 'user', content: [{ type: 'text', text: prompt }] }];\n\t\tlet tokens: Tokens | undefined;\n\t\t// Once one call went unreported the run's total is unknown, however many report later.\n\t\tlet unreported = false;\n\t\tlet toolRan = false;\n\n\t\ttry {\n\t\t\tfor (let step = 1; step <= this.maxSteps; step++) {\n\t\t\t\tconst response = await this.llmCall(runMessages, signal);\n\t\t\t\ttokens = addTokens(tokens, response.usage);\n\t\t\t\tunreported ||= !response.usage;\n\t\t\t\trunMessages.push(response.message);\n\n\t\t\t\t// Tool calls are narrated as they start, in `runTools`, and never if they do not run.\n\t\t\t\tfor (const part of response.message.content) {\n\t\t\t\t\tif (part.type !== 'toolCall') narrate(part, callback);\n\t\t\t\t}\n\n\t\t\t\tconst calls = response.message.content.filter(part => part.type === 'toolCall');\n\t\t\t\tif (calls.length === 0) {\n\t\t\t\t\t// Recorded only now, so a failed run leaves no tool call without its result.\n\t\t\t\t\tthis.conversation.addRun(runMessages);\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\t// Only the final answer: text from earlier steps was narrated as it came.\n\t\t\t\t\t\tresponse: response.message.content\n\t\t\t\t\t\t\t.filter(part => part.type === 'text')\n\t\t\t\t\t\t\t.map(part => part.text)\n\t\t\t\t\t\t\t.join('\\n'),\n\t\t\t\t\t\ttokens: unreported ? undefined : tokens,\n\t\t\t\t\t\tduration: (Date.now() - start) / 1000,\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\t// On the last step no call is left to send the results to, so the tools do not run.\n\t\t\t\tif (step < this.maxSteps) {\n\t\t\t\t\t// Only a call to a tool the agent has reaches `execute`; a missing one has no\n\t\t\t\t\t// effect a retry could repeat.\n\t\t\t\t\ttoolRan ||= calls.some(call => this.tools.has(call.name));\n\t\t\t\t\trunMessages.push(await this.runTools(calls, signal, callback));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthrow new UnrecoverableError('The LLM did not finish within its step limit', {\n\t\t\t\tcause: `The model was still calling tools after ${String(this.maxSteps)} steps.`,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tlet failure = error;\n\t\t\tif (toolRan && error instanceof RecoverableError) {\n\t\t\t\t// RetryingAgent would run the whole prompt again, and with it every tool this run\n\t\t\t\t// already executed, so a failure after one ran must not invite a retry.\n\t\t\t\tconst unrecoverable = new UnrecoverableError(error.message, {\n\t\t\t\t\tcause: error.cause,\n\t\t\t\t\ttokens: error.tokens,\n\t\t\t\t\tusageUnreported: error.usageUnreported,\n\t\t\t\t});\n\t\t\t\tunrecoverable.stack = error.stack;\n\t\t\t\tfailure = unrecoverable;\n\t\t\t}\n\n\t\t\t// The run spent the tokens of every step before the one that failed.\n\t\t\tthrow withSpentTokens(failure, tokens, unreported);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"llmAgent.js","sourceRoot":"","sources":["../../../../src/engines/domain/model/llmAgent.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Agent, AgentResponse, Callback, ProgressEvent } from '#src/agent/domain/agent';\nimport {\n\tInvalidAgentConfigError,\n\tRecoverableError,\n\tUnrecoverableError,\n\twithSpentTokens,\n} from '#src/agent/domain/errors';\nimport {\n\tclassifyHostFailure,\n\tclassifyProviderFailure,\n\tdescribeFailure,\n\ttreatErrors,\n} from '#src/agent/domain/providerFailure';\nimport { Conversation } from '#src/llm/domain/conversation';\nimport type { LLMClient, LLMResponse } from '#src/llm/domain/llm';\nimport type { Message, MessagePart, ToolCallPart, ToolResultPart } from '#src/llm/domain/message';\nimport type { ToolDefinition } from '#src/llm/domain/tool';\nimport type { Tool } from '#src/tools/domain/tool';\nimport { addTokens, type Tokens } from '#src/shared/domain/tokens';\n\nfunction describePart(part: MessagePart): ProgressEvent | undefined {\n\tswitch (part.type) {\n\t\tcase 'text':\n\t\t\treturn { type: 'agentMessage', message: part.text };\n\t\tcase 'reasoning':\n\t\t\treturn { type: 'reasoning', message: part.text };\n\t\tcase 'toolCall':\n\t\t\treturn { type: 'tool', id: part.id, name: part.name, status: 'in_progress' };\n\t\tcase 'toolResult':\n\t\t\treturn {\n\t\t\t\ttype: 'tool',\n\t\t\t\tid: part.callId,\n\t\t\t\tname: part.name,\n\t\t\t\tstatus: part.isError ? 'error' : 'completed',\n\t\t\t};\n\t\tcase 'providerData':\n\t\t\t// Opaque by design: its readable side, if any, arrives as a reasoning part.\n\t\t\treturn undefined;\n\t}\n}\n\nfunction narrate(part: MessagePart, callback: Callback): void {\n\tconst event = describePart(part);\n\tif (!event) {\n\t\treturn;\n\t}\n\n\ttreatErrors(\n\t\t() => {\n\t\t\tcallback(event);\n\t\t},\n\t\tclassifyHostFailure,\n\t\t'LLM agent progress callback failed',\n\t);\n}\n\n/**\n * An agent whose conversation is owned by MiKode: every run sends the whole context to a\n * stateless `LLMClient`. Within a run the model may call the agent's tools; the agent runs them\n * and sends their results back until the model answers without calling one. Turn boundaries\n * are the consumer's to emit, as with the other engines; this agent only narrates what the\n * model produced and what its tools did.\n */\nexport class LLMAgent implements Agent {\n\tprivate readonly llmClient: LLMClient;\n\tprivate readonly conversation: Conversation;\n\tprivate readonly tools: Map<string, Tool>;\n\tprivate readonly toolDefinitions: ToolDefinition[];\n\tprivate readonly maxSteps: number;\n\n\tconstructor({\n\t\tllmClient,\n\t\tmessages,\n\t\ttools = [],\n\t\tmaxSteps = 25,\n\t}: {\n\t\tllmClient: LLMClient;\n\t\tmessages?: Message[];\n\t\ttools?: Tool[];\n\t\t/** How many calls to the model one run may make before it fails. */\n\t\tmaxSteps?: number;\n\t}) {\n\t\tif (!Number.isInteger(maxSteps) || maxSteps < 1) {\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`maxSteps must be a positive integer; got ${String(maxSteps)}`,\n\t\t\t);\n\t\t}\n\n\t\tthis.llmClient = llmClient;\n\t\tthis.conversation = new Conversation(messages);\n\t\tthis.maxSteps = maxSteps;\n\t\tthis.tools = new Map(tools.map(tool => [tool.name, tool]));\n\t\tif (this.tools.size !== tools.length) {\n\t\t\t// The model calls tools by name, so a second one with the same name could never run.\n\t\t\tthrow new InvalidAgentConfigError('Two tools share a name; each name must be unique');\n\t\t}\n\t\t// The client only describes the tools to the model; running them stays with the agent.\n\t\tthis.toolDefinitions = tools.map(({ name, description, inputSchema }) => ({\n\t\t\tname,\n\t\t\tdescription,\n\t\t\tinputSchema,\n\t\t}));\n\t}\n\n\tprivate async llmCall(runMessages: Message[], signal: AbortSignal): Promise<LLMResponse> {\n\t\tlet response: LLMResponse;\n\t\ttry {\n\t\t\t// Sent as a copy, so what the run records is what the agent built, whatever the client does.\n\t\t\tresponse = await this.llmClient.send(\n\t\t\t\t{\n\t\t\t\t\tcontext: [...this.conversation.getContext(), ...structuredClone(runMessages)],\n\t\t\t\t\ttools: this.toolDefinitions,\n\t\t\t\t},\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\treturn response;\n\t}\n\n\t/**\n\t * Always answers the call: a missing tool or a failing one becomes an error result, so the\n\t * model can correct itself. Only a cancellation escapes, whatever error the tool turned it into.\n\t */\n\tprivate async runTool(call: ToolCallPart, signal: AbortSignal): Promise<ToolResultPart> {\n\t\tconst result = { type: 'toolResult' as const, callId: call.id, name: call.name };\n\t\tconst tool = this.tools.get(call.name);\n\t\tif (!tool) {\n\t\t\treturn {\n\t\t\t\t...result,\n\t\t\t\toutput: `There is no tool named \"${call.name}\". Available tools: ${[...this.tools.keys()].join(', ') || 'none'}.`,\n\t\t\t\tisError: true,\n\t\t\t};\n\t\t}\n\n\t\t// Checked again at the last moment: the consumer may cancel on the announcement itself.\n\t\tsignal.throwIfAborted();\n\t\ttry {\n\t\t\treturn { ...result, output: await tool.execute(call.input, signal), isError: false };\n\t\t} catch (error) {\n\t\t\tsignal.throwIfAborted();\n\t\t\treturn { ...result, output: describeFailure(error), isError: true };\n\t\t}\n\t}\n\n\t/** One at a time and in order, so their effects and their narration never interleave. */\n\tprivate async runTools(\n\t\tcalls: ToolCallPart[],\n\t\tsignal: AbortSignal,\n\t\tcallback: Callback,\n\t): Promise<Message> {\n\t\tconst results: MessagePart[] = [];\n\t\tfor (const call of calls) {\n\t\t\t// A call before this one may have ignored the cancellation; this one must not start,\n\t\t\t// nor be announced as running.\n\t\t\tsignal.throwIfAborted();\n\t\t\t// Announced only as its turn comes, so a consumer never shows a call as running early.\n\t\t\tnarrate(call, callback);\n\t\t\tconst result = await this.runTool(call, signal);\n\t\t\tnarrate(result, callback);\n\t\t\tresults.push(result);\n\t\t}\n\n\t\treturn { role: 'tool', content: results };\n\t}\n\n\tasync run(prompt: string, signal: AbortSignal, callback: Callback): Promise<AgentResponse> {\n\t\tconst start = Date.now();\n\t\tconst runMessages: Message[] = [{ role: 'user', content: [{ type: 'text', text: prompt }] }];\n\t\tlet tokens: Tokens | undefined;\n\t\t// Once one call went unreported the run's total is unknown, however many report later.\n\t\tlet unreported = false;\n\t\tlet toolRan = false;\n\n\t\ttry {\n\t\t\tfor (let step = 1; step <= this.maxSteps; step++) {\n\t\t\t\tconst response = await this.llmCall(runMessages, signal);\n\t\t\t\ttokens = addTokens(tokens, response.usage);\n\t\t\t\tunreported ||= !response.usage;\n\t\t\t\trunMessages.push(response.message);\n\n\t\t\t\t// Tool calls are narrated as they start, in `runTools`, and never if they do not run.\n\t\t\t\tfor (const part of response.message.content) {\n\t\t\t\t\tif (part.type !== 'toolCall') narrate(part, callback);\n\t\t\t\t}\n\n\t\t\t\tconst calls = response.message.content.filter(part => part.type === 'toolCall');\n\t\t\t\tif (calls.length === 0) {\n\t\t\t\t\t// Recorded only now, so a failed run leaves no tool call without its result.\n\t\t\t\t\tthis.conversation.addRun(runMessages);\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\t// Only the final answer: text from earlier steps was narrated as it came.\n\t\t\t\t\t\tresponse: response.message.content\n\t\t\t\t\t\t\t.filter(part => part.type === 'text')\n\t\t\t\t\t\t\t.map(part => part.text)\n\t\t\t\t\t\t\t.join('\\n'),\n\t\t\t\t\t\ttokens: unreported ? undefined : tokens,\n\t\t\t\t\t\tduration: (Date.now() - start) / 1000,\n\t\t\t\t\t};\n\t\t\t\t}\n\n\t\t\t\t// On the last step no call is left to send the results to, so the tools do not run.\n\t\t\t\tif (step < this.maxSteps) {\n\t\t\t\t\t// Only a call to a tool the agent has reaches `execute`; a missing one has no\n\t\t\t\t\t// effect a retry could repeat.\n\t\t\t\t\ttoolRan ||= calls.some(call => this.tools.has(call.name));\n\t\t\t\t\trunMessages.push(await this.runTools(calls, signal, callback));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthrow new UnrecoverableError('The LLM did not finish within its step limit', {\n\t\t\t\tcause: `The model was still calling tools after ${String(this.maxSteps)} steps.`,\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tlet failure = error;\n\t\t\tif (toolRan && error instanceof RecoverableError) {\n\t\t\t\t// RetryingAgent would run the whole prompt again, and with it every tool this run\n\t\t\t\t// already executed, so a failure after one ran must not invite a retry.\n\t\t\t\tconst unrecoverable = new UnrecoverableError(error.message, {\n\t\t\t\t\tcause: error.cause,\n\t\t\t\t\ttokens: error.tokens,\n\t\t\t\t\tusageUnreported: error.usageUnreported,\n\t\t\t\t});\n\t\t\t\tunrecoverable.stack = error.stack;\n\t\t\t\tfailure = unrecoverable;\n\t\t\t}\n\n\t\t\t// The run spent the tokens of every step before the one that failed.\n\t\t\tthrow withSpentTokens(failure, tokens, unreported);\n\t\t}\n\t}\n}\n"]}
@@ -1,16 +1,9 @@
1
1
  import type { Agent } from '#src/agent/domain/agent';
2
- import { type ClaudeModel, type ClaudeReasoningEffort } from '#src/engines/claude/infrastructure/model/claudeAgent';
3
- import { type CodexModel, type CodexReasoningEffort } from '#src/engines/codex/infrastructure/model/codexAgent';
4
2
  import type { ILogger } from '#src/shared/domain/logger';
5
- export declare const agentProviders: readonly ["claude", "codex"];
6
- export type AgentProvider = (typeof agentProviders)[number];
7
- /** Any provider's model. The engine the provider selects rejects one it does not support. */
8
- export type AgentModel = ClaudeModel | CodexModel;
9
- /** Any provider's reasoning effort. The engine the provider selects rejects one it does not support. */
10
- export type ReasoningEffort = ClaudeReasoningEffort | CodexReasoningEffort;
3
+ import { type AgentModel, type AgentProvider, type CreateOrchestratorOptions, type ReasoningEffort } from './types.ts';
11
4
  export declare function isAgentProvider(value: string): value is AgentProvider;
12
5
  export interface CreateAgentOptions {
13
- /** Defaults to `'opus'` on Claude and `'gpt-5.6-sol'` on Codex. */
6
+ /** Defaults to `'opus'` on Anthropic and `'gpt-5.6-sol'` on OpenAI. */
14
7
  model?: AgentModel;
15
8
  /** Defaults to `'high'`. */
16
9
  reasoningEffort?: ReasoningEffort;
@@ -20,7 +13,8 @@ export interface CreateAgentOptions {
20
13
  logger?: ILogger;
21
14
  }
22
15
  /**
23
- * Builds the agent for a provider, already wrapped in the harness's retry policy.
16
+ * Builds the agent for a provider on its Agent SDK — Claude Code for Anthropic, Codex for
17
+ * OpenAI — already wrapped in the harness's retry policy.
24
18
  *
25
19
  * @throws {InvalidAgentConfigError} for an unknown provider, or a model or reasoning effort
26
20
  * the provider does not support.
@@ -28,47 +22,22 @@ export interface CreateAgentOptions {
28
22
  * is missing.
29
23
  */
30
24
  export declare function createAgent(provider: AgentProvider, { model, reasoningEffort, autoApprove, logger }?: CreateAgentOptions): Agent;
31
- export interface CreateOrchestratorOptions {
32
- /**
33
- * Runs every role on one provider, for example when the other one is out of quota. By
34
- * default Codex plans and executes, and Claude reviews.
35
- */
36
- provider?: AgentProvider;
37
- /** Maps to each provider's permission-bypass mode. Defaults to `false`. */
38
- autoApprove?: boolean;
39
- /** Defaults to warnings on stderr. */
40
- logger?: ILogger;
25
+ export interface Role {
26
+ provider: AgentProvider;
27
+ model: AgentModel;
28
+ reasoningEffort: ReasoningEffort;
41
29
  }
42
30
  /**
43
- * Builds the planner → executor → reviewer workflow, each role an agent from `createAgent`.
31
+ * The roles for `provider`, or the default mix without one.
44
32
  *
45
33
  * @throws {InvalidAgentConfigError} for an unknown provider.
46
- * @throws {UnrecoverableError} when a Codex role cannot be set up, for example because its CLI
47
- * binary is missing.
48
34
  */
49
- export declare function createOrchestrator({ provider, autoApprove, logger, }?: CreateOrchestratorOptions): Agent;
50
- export declare const llmProviders: readonly ["claude", "openai"];
51
- export type LLMProvider = (typeof llmProviders)[number];
52
- export interface CreateLLMAgentOptions {
53
- /**
54
- * Defaults to `'claude-sonnet-5'` on Claude and `'gpt-5.6-luna'` on OpenAI. The provider's
55
- * client rejects a model it does not support.
56
- */
57
- model?: string;
58
- /** Sent as the model's instructions on every call. */
59
- systemPrompt: string;
60
- /** Defaults to warnings on stderr. */
61
- logger?: ILogger;
62
- }
35
+ export declare function orchestratorRolesFor(provider: AgentProvider | undefined): Record<'planner' | 'executor' | 'reviewer', Role>;
63
36
  /**
64
- * Builds an agent whose conversation MiKode owns, on a provider's model API, already wrapped in
65
- * the harness's retry policy. Internal while #23 reaches parity with the Agent SDK engines: it
66
- * is not exported from `src/index.ts`. It runs no tools, so it has no `autoApprove`.
37
+ * Builds the planner → executor → reviewer workflow, each role an agent from `createAgent`.
67
38
  *
68
- * @throws {InvalidAgentConfigError} for an unknown provider, or a model its client does not
69
- * support.
70
- * @throws {UnrecoverableError} when the client cannot be set up, for example because
71
- * `OPENAI_API_KEY` is missing. The Anthropic SDK resolves credentials lazily, so a missing
72
- * `ANTHROPIC_API_KEY` fails the first run instead, with an `UnrecoverableError`.
39
+ * @throws {InvalidAgentConfigError} for an unknown provider.
40
+ * @throws {UnrecoverableError} when an OpenAI role cannot be set up, for example because the
41
+ * Codex CLI binary is missing.
73
42
  */
74
- export declare function createLLMAgent(provider: LLMProvider, { model, systemPrompt, logger }: CreateLLMAgentOptions): Agent;
43
+ export declare function createOrchestrator({ provider, autoApprove, systemPrompts, logger, }?: CreateOrchestratorOptions): Agent;
@@ -1,24 +1,23 @@
1
1
  import { InvalidAgentConfigError } from '#src/agent/domain/errors';
2
- import { ClaudeAgent, } from '#src/engines/claude/infrastructure/model/claudeAgent';
3
- import { CodexAgent, } from '#src/engines/codex/infrastructure/model/codexAgent';
4
- import { LLMAgent } from '#src/engines/domain/model/llmAgent';
5
- import { ClaudeLLMClient } from '#src/llm/infrastructure/claudeLLMClient';
6
- import { OpenAILLMClient } from '#src/llm/infrastructure/openAILLMClient';
7
- import { OrchestratorAgent } from '#src/orchestration/domain/model/orchestratorAgent';
2
+ import { ClaudeAgent } from '#src/engines/claude/infrastructure/model/claudeAgent';
3
+ import { CodexAgent } from '#src/engines/codex/infrastructure/model/codexAgent';
4
+ import { InstructedAgent } from '#src/orchestration/domain/model/instructedAgent';
5
+ import { executorInstructions, OrchestratorAgent, plannerInstructions, reviewerInstructions, } from '#src/orchestration/domain/model/orchestratorAgent';
8
6
  import { ReviewerDecisionValidator } from '#src/orchestration/infrastructure/model/reviewerDecisionValidator';
9
7
  import { RetryingAgent } from '#src/retry/domain/model/retryingAgent';
10
8
  import { isOneOf } from '#src/shared/domain/isOneOf';
11
9
  import { Logger } from '#src/shared/infrastructure/logger';
12
- export const agentProviders = ['claude', 'codex'];
10
+ import { agentProviders, } from "./types.js";
13
11
  export function isAgentProvider(value) {
14
12
  return isOneOf(agentProviders, value);
15
13
  }
16
14
  const defaultModels = {
17
- claude: 'opus',
18
- codex: 'gpt-5.6-sol',
15
+ anthropic: 'opus',
16
+ openai: 'gpt-5.6-sol',
19
17
  };
20
18
  /**
21
- * Builds the agent for a provider, already wrapped in the harness's retry policy.
19
+ * Builds the agent for a provider on its Agent SDK — Claude Code for Anthropic, Codex for
20
+ * OpenAI — already wrapped in the harness's retry policy.
22
21
  *
23
22
  * @throws {InvalidAgentConfigError} for an unknown provider, or a model or reasoning effort
24
23
  * the provider does not support.
@@ -28,17 +27,17 @@ const defaultModels = {
28
27
  export function createAgent(provider, { model, reasoningEffort, autoApprove, logger = new Logger() } = {}) {
29
28
  let engine;
30
29
  switch (provider) {
31
- case 'claude':
30
+ case 'anthropic':
32
31
  engine = new ClaudeAgent({
33
- model: model ?? defaultModels.claude,
32
+ model: model ?? defaultModels.anthropic,
34
33
  reasoningEffort,
35
34
  autoApprove,
36
35
  logger,
37
36
  });
38
37
  break;
39
- case 'codex':
38
+ case 'openai':
40
39
  engine = new CodexAgent({
41
- model: model ?? defaultModels.codex,
40
+ model: model ?? defaultModels.openai,
42
41
  reasoningEffort,
43
42
  autoApprove,
44
43
  logger,
@@ -52,85 +51,55 @@ export function createAgent(provider, { model, reasoningEffort, autoApprove, log
52
51
  }
53
52
  return new RetryingAgent({ inner: engine, logger });
54
53
  }
55
- // A stronger model plans and reviews; a cheaper one executes the plan.
54
+ // A stronger model plans and reviews; a cheaper one executes the plan. Every model and effort
55
+ // here is one both the Agent SDKs and the model APIs accept, so either path can run a role.
56
56
  const orchestratorRoles = {
57
57
  default: {
58
- planner: { provider: 'codex', model: 'gpt-5.6-sol', reasoningEffort: 'high' },
59
- executor: { provider: 'codex', model: 'gpt-5.6-luna', reasoningEffort: 'xhigh' },
60
- reviewer: { provider: 'claude', model: 'opus', reasoningEffort: 'high' },
58
+ planner: { provider: 'openai', model: 'gpt-5.6-sol', reasoningEffort: 'high' },
59
+ executor: { provider: 'openai', model: 'gpt-5.6-luna', reasoningEffort: 'xhigh' },
60
+ reviewer: { provider: 'anthropic', model: 'opus', reasoningEffort: 'high' },
61
61
  },
62
- claude: {
63
- planner: { provider: 'claude', model: 'opus', reasoningEffort: 'high' },
64
- executor: { provider: 'claude', model: 'sonnet', reasoningEffort: 'xhigh' },
65
- reviewer: { provider: 'claude', model: 'opus', reasoningEffort: 'high' },
62
+ anthropic: {
63
+ planner: { provider: 'anthropic', model: 'opus', reasoningEffort: 'high' },
64
+ executor: { provider: 'anthropic', model: 'sonnet', reasoningEffort: 'xhigh' },
65
+ reviewer: { provider: 'anthropic', model: 'opus', reasoningEffort: 'high' },
66
66
  },
67
- codex: {
68
- planner: { provider: 'codex', model: 'gpt-5.6-sol', reasoningEffort: 'high' },
69
- executor: { provider: 'codex', model: 'gpt-5.6-luna', reasoningEffort: 'xhigh' },
70
- reviewer: { provider: 'codex', model: 'gpt-5.6-sol', reasoningEffort: 'high' },
67
+ openai: {
68
+ planner: { provider: 'openai', model: 'gpt-5.6-sol', reasoningEffort: 'high' },
69
+ executor: { provider: 'openai', model: 'gpt-5.6-luna', reasoningEffort: 'xhigh' },
70
+ reviewer: { provider: 'openai', model: 'gpt-5.6-sol', reasoningEffort: 'high' },
71
71
  },
72
72
  };
73
73
  /**
74
- * Builds the planner → executor → reviewer workflow, each role an agent from `createAgent`.
74
+ * The roles for `provider`, or the default mix without one.
75
75
  *
76
76
  * @throws {InvalidAgentConfigError} for an unknown provider.
77
- * @throws {UnrecoverableError} when a Codex role cannot be set up, for example because its CLI
78
- * binary is missing.
79
77
  */
80
- export function createOrchestrator({ provider, autoApprove, logger = new Logger(), } = {}) {
78
+ export function orchestratorRolesFor(provider) {
81
79
  if (provider !== undefined && !isAgentProvider(provider)) {
82
80
  throw new InvalidAgentConfigError(`"${String(provider)}" is not an agent provider; expected one of: ${agentProviders.join(', ')}`);
83
81
  }
84
- const roles = orchestratorRoles[provider ?? 'default'];
85
- const agentFor = ({ provider, model, reasoningEffort }) => createAgent(provider, { model, reasoningEffort, autoApprove, logger });
86
- return new OrchestratorAgent({
87
- plannerAgent: agentFor(roles.planner),
88
- executorAgent: agentFor(roles.executor),
89
- reviewerAgent: agentFor(roles.reviewer),
90
- reviewerDecisionValidator: new ReviewerDecisionValidator(),
91
- logger,
92
- });
82
+ return orchestratorRoles[provider ?? 'default'];
93
83
  }
94
- export const llmProviders = ['claude', 'openai'];
95
- const defaultLLMAgentModels = {
96
- claude: 'claude-sonnet-5',
97
- openai: 'gpt-5.6-luna',
98
- };
99
84
  /**
100
- * Builds an agent whose conversation MiKode owns, on a provider's model API, already wrapped in
101
- * the harness's retry policy. Internal while #23 reaches parity with the Agent SDK engines: it
102
- * is not exported from `src/index.ts`. It runs no tools, so it has no `autoApprove`.
85
+ * Builds the planner → executor → reviewer workflow, each role an agent from `createAgent`.
103
86
  *
104
- * @throws {InvalidAgentConfigError} for an unknown provider, or a model its client does not
105
- * support.
106
- * @throws {UnrecoverableError} when the client cannot be set up, for example because
107
- * `OPENAI_API_KEY` is missing. The Anthropic SDK resolves credentials lazily, so a missing
108
- * `ANTHROPIC_API_KEY` fails the first run instead, with an `UnrecoverableError`.
87
+ * @throws {InvalidAgentConfigError} for an unknown provider.
88
+ * @throws {UnrecoverableError} when an OpenAI role cannot be set up, for example because the
89
+ * Codex CLI binary is missing.
109
90
  */
110
- export function createLLMAgent(provider, { model, systemPrompt, logger = new Logger() }) {
111
- let llmClient;
112
- switch (provider) {
113
- case 'claude':
114
- llmClient = new ClaudeLLMClient({
115
- model: model ?? defaultLLMAgentModels.claude,
116
- systemPrompt,
117
- logger,
118
- });
119
- break;
120
- case 'openai':
121
- llmClient = new OpenAILLMClient({
122
- model: model ?? defaultLLMAgentModels.openai,
123
- systemPrompt,
124
- logger,
125
- });
126
- break;
127
- default: {
128
- // Reachable from untyped input.
129
- const unknownProvider = provider;
130
- throw new InvalidAgentConfigError(`"${String(unknownProvider)}" is not an LLM provider; expected one of: ${llmProviders.join(', ')}`);
131
- }
132
- }
133
- // LLMAgent records nothing from a failed call, so the original prompt is the whole story.
134
- return new RetryingAgent({ inner: new LLMAgent({ llmClient }), logger, noteFailures: false });
91
+ export function createOrchestrator({ provider, autoApprove, systemPrompts = {}, logger = new Logger(), } = {}) {
92
+ const roles = orchestratorRolesFor(provider);
93
+ const agentFor = ({ provider, model, reasoningEffort }, instructions) => new InstructedAgent({
94
+ inner: createAgent(provider, { model, reasoningEffort, autoApprove, logger }),
95
+ instructions,
96
+ });
97
+ return new OrchestratorAgent({
98
+ plannerAgent: agentFor(roles.planner, systemPrompts.planner ?? plannerInstructions),
99
+ executorAgent: agentFor(roles.executor, systemPrompts.executor ?? executorInstructions),
100
+ reviewerAgent: agentFor(roles.reviewer, systemPrompts.reviewer ?? reviewerInstructions),
101
+ reviewerDecisionValidator: new ReviewerDecisionValidator(),
102
+ logger,
103
+ });
135
104
  }
136
105
  //# 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 '#src/agent/domain/agent';\nimport { InvalidAgentConfigError } from '#src/agent/domain/errors';\nimport {\n\tClaudeAgent,\n\ttype ClaudeModel,\n\ttype ClaudeReasoningEffort,\n} from '#src/engines/claude/infrastructure/model/claudeAgent';\nimport {\n\tCodexAgent,\n\ttype CodexModel,\n\ttype CodexReasoningEffort,\n} from '#src/engines/codex/infrastructure/model/codexAgent';\nimport { LLMAgent } from '#src/engines/domain/model/llmAgent';\nimport type { LLMClient } from '#src/llm/domain/llm';\nimport { ClaudeLLMClient, type ClaudeLLMModel } from '#src/llm/infrastructure/claudeLLMClient';\nimport { OpenAILLMClient, type OpenAIModel } from '#src/llm/infrastructure/openAILLMClient';\nimport { OrchestratorAgent } from '#src/orchestration/domain/model/orchestratorAgent';\nimport { ReviewerDecisionValidator } from '#src/orchestration/infrastructure/model/reviewerDecisionValidator';\nimport { RetryingAgent } from '#src/retry/domain/model/retryingAgent';\nimport { isOneOf } from '#src/shared/domain/isOneOf';\nimport type { ILogger } from '#src/shared/domain/logger';\nimport { Logger } from '#src/shared/infrastructure/logger';\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 const llmProviders = ['claude', 'openai'] as const;\nexport type LLMProvider = (typeof llmProviders)[number];\n\nexport interface CreateLLMAgentOptions {\n\t/**\n\t * Defaults to `'claude-sonnet-5'` on Claude and `'gpt-5.6-luna'` on OpenAI. The provider's\n\t * client rejects a model it does not support.\n\t */\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 defaultLLMAgentModels = {\n\tclaude: 'claude-sonnet-5' satisfies ClaudeLLMModel,\n\topenai: 'gpt-5.6-luna' satisfies OpenAIModel,\n} as const satisfies Record<LLMProvider, string>;\n\n/**\n * Builds an agent whose conversation MiKode owns, on a provider's model API, already wrapped in\n * the harness's retry policy. Internal while #23 reaches parity with the Agent SDK engines: it\n * is not exported from `src/index.ts`. It runs no tools, so it has no `autoApprove`.\n *\n * @throws {InvalidAgentConfigError} for an unknown provider, or a model its client does not\n * support.\n * @throws {UnrecoverableError} when the client cannot be set up, for example because\n * `OPENAI_API_KEY` is missing. The Anthropic SDK resolves credentials lazily, so a missing\n * `ANTHROPIC_API_KEY` fails the first run instead, with an `UnrecoverableError`.\n */\nexport function createLLMAgent(\n\tprovider: LLMProvider,\n\t{ model, systemPrompt, logger = new Logger() }: CreateLLMAgentOptions,\n): Agent {\n\tlet llmClient: LLMClient;\n\n\tswitch (provider) {\n\t\tcase 'claude':\n\t\t\tllmClient = new ClaudeLLMClient({\n\t\t\t\tmodel: model ?? defaultLLMAgentModels.claude,\n\t\t\t\tsystemPrompt,\n\t\t\t\tlogger,\n\t\t\t});\n\t\t\tbreak;\n\t\tcase 'openai':\n\t\t\tllmClient = new OpenAILLMClient({\n\t\t\t\tmodel: model ?? defaultLLMAgentModels.openai,\n\t\t\t\tsystemPrompt,\n\t\t\t\tlogger,\n\t\t\t});\n\t\t\tbreak;\n\t\tdefault: {\n\t\t\t// Reachable from untyped input.\n\t\t\tconst unknownProvider: never = provider;\n\t\t\tthrow new InvalidAgentConfigError(\n\t\t\t\t`\"${String(unknownProvider)}\" is not an LLM provider; expected one of: ${llmProviders.join(', ')}`,\n\t\t\t);\n\t\t}\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: new LLMAgent({ llmClient }), logger, noteFailures: false });\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 '#src/agent/domain/agent';\nimport { InvalidAgentConfigError } from '#src/agent/domain/errors';\nimport { ClaudeAgent } from '#src/engines/claude/infrastructure/model/claudeAgent';\nimport { CodexAgent } from '#src/engines/codex/infrastructure/model/codexAgent';\nimport { InstructedAgent } from '#src/orchestration/domain/model/instructedAgent';\nimport {\n\texecutorInstructions,\n\tOrchestratorAgent,\n\tplannerInstructions,\n\treviewerInstructions,\n} from '#src/orchestration/domain/model/orchestratorAgent';\nimport { ReviewerDecisionValidator } from '#src/orchestration/infrastructure/model/reviewerDecisionValidator';\nimport { RetryingAgent } from '#src/retry/domain/model/retryingAgent';\nimport { isOneOf } from '#src/shared/domain/isOneOf';\nimport type { ILogger } from '#src/shared/domain/logger';\nimport { Logger } from '#src/shared/infrastructure/logger';\nimport {\n\tagentProviders,\n\ttype AgentModel,\n\ttype AgentProvider,\n\ttype CreateOrchestratorOptions,\n\ttype ReasoningEffort,\n} from './types.ts';\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 Anthropic and `'gpt-5.6-sol'` on OpenAI. */\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\tanthropic: 'opus',\n\topenai: 'gpt-5.6-sol',\n} as const satisfies Record<AgentProvider, AgentModel>;\n\n/**\n * Builds the agent for a provider on its Agent SDK — Claude Code for Anthropic, Codex for\n * OpenAI — 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 'anthropic':\n\t\t\tengine = new ClaudeAgent({\n\t\t\t\tmodel: model ?? defaultModels.anthropic,\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 'openai':\n\t\t\tengine = new CodexAgent({\n\t\t\t\tmodel: model ?? defaultModels.openai,\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 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. Every model and effort\n// here is one both the Agent SDKs and the model APIs accept, so either path can run a role.\nconst orchestratorRoles = {\n\tdefault: {\n\t\tplanner: { provider: 'openai', model: 'gpt-5.6-sol', reasoningEffort: 'high' },\n\t\texecutor: { provider: 'openai', model: 'gpt-5.6-luna', reasoningEffort: 'xhigh' },\n\t\treviewer: { provider: 'anthropic', model: 'opus', reasoningEffort: 'high' },\n\t},\n\tanthropic: {\n\t\tplanner: { provider: 'anthropic', model: 'opus', reasoningEffort: 'high' },\n\t\texecutor: { provider: 'anthropic', model: 'sonnet', reasoningEffort: 'xhigh' },\n\t\treviewer: { provider: 'anthropic', model: 'opus', reasoningEffort: 'high' },\n\t},\n\topenai: {\n\t\tplanner: { provider: 'openai', model: 'gpt-5.6-sol', reasoningEffort: 'high' },\n\t\texecutor: { provider: 'openai', model: 'gpt-5.6-luna', reasoningEffort: 'xhigh' },\n\t\treviewer: { provider: 'openai', 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 * The roles for `provider`, or the default mix without one.\n *\n * @throws {InvalidAgentConfigError} for an unknown provider.\n */\nexport function orchestratorRolesFor(\n\tprovider: AgentProvider | undefined,\n): Record<'planner' | 'executor' | 'reviewer', Role> {\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\treturn orchestratorRoles[provider ?? 'default'];\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 an OpenAI role cannot be set up, for example because the\n * Codex CLI binary is missing.\n */\nexport function createOrchestrator({\n\tprovider,\n\tautoApprove,\n\tsystemPrompts = {},\n\tlogger = new Logger(),\n}: CreateOrchestratorOptions = {}): Agent {\n\tconst roles = orchestratorRolesFor(provider);\n\tconst agentFor = ({ provider, model, reasoningEffort }: Role, instructions: string) =>\n\t\tnew InstructedAgent({\n\t\t\tinner: createAgent(provider, { model, reasoningEffort, autoApprove, logger }),\n\t\t\tinstructions,\n\t\t});\n\n\treturn new OrchestratorAgent({\n\t\tplannerAgent: agentFor(roles.planner, systemPrompts.planner ?? plannerInstructions),\n\t\texecutorAgent: agentFor(roles.executor, systemPrompts.executor ?? executorInstructions),\n\t\treviewerAgent: agentFor(roles.reviewer, systemPrompts.reviewer ?? reviewerInstructions),\n\t\treviewerDecisionValidator: new ReviewerDecisionValidator(),\n\t\tlogger,\n\t});\n}\n"]}
@@ -0,0 +1,44 @@
1
+ import type { Agent } from '#src/agent/domain/agent';
2
+ import type { ILogger } from '#src/shared/domain/logger';
3
+ import type { Tool } from '#src/tools/domain/tool';
4
+ import { type AgentModel, type AgentProvider, type CreateOrchestratorOptions, type ReasoningEffort } from './types.ts';
5
+ export interface CreateLLMAgentOptions {
6
+ /**
7
+ * Defaults to `'sonnet'` on Anthropic and `'gpt-5.6-luna'` on OpenAI. The provider's client
8
+ * rejects a model it does not support.
9
+ */
10
+ model?: AgentModel;
11
+ /** Defaults to `'high'`. The provider's client rejects an effort it does not support. */
12
+ reasoningEffort?: ReasoningEffort;
13
+ /** Sent as the model's instructions on every call. */
14
+ systemPrompt: string;
15
+ /** The tools the model may call. Defaults to none. */
16
+ tools?: Tool[];
17
+ /** Defaults to warnings on stderr. */
18
+ logger?: ILogger;
19
+ }
20
+ /**
21
+ * Builds an agent whose conversation MiKode owns, on a provider's model API, already wrapped in
22
+ * the harness's retry policy. Internal while #23 reaches parity with the Agent SDK engines: it
23
+ * is not exported from `src/index.ts`. Its tools only read, so it has no `autoApprove`.
24
+ *
25
+ * @throws {InvalidAgentConfigError} for an unknown provider, or a model or reasoning effort its
26
+ * client does not support.
27
+ * @throws {UnrecoverableError} when the client cannot be set up, for example because
28
+ * `OPENAI_API_KEY` is missing. The Anthropic SDK resolves credentials lazily, so a missing
29
+ * `ANTHROPIC_API_KEY` fails the first run instead, with an `UnrecoverableError`.
30
+ */
31
+ export declare function createLLMAgent(provider: AgentProvider, { model, reasoningEffort, systemPrompt, tools, logger }: CreateLLMAgentOptions): Agent;
32
+ /**
33
+ * Builds the planner → executor → reviewer workflow with the planner and reviewer on the model
34
+ * APIs, from `createLLMAgent`, each holding its role's instructions, or `systemPrompts`, as its
35
+ * system prompt, with the read-only repository tools for the current working directory. The
36
+ * executor stays on its Agent SDK, from `createAgent`, until an agent of ours can change files; `autoApprove` reaches
37
+ * only it. Asynchronous because finding the program that reads the repository is.
38
+ *
39
+ * @throws {InvalidAgentConfigError} for an unknown provider.
40
+ * @throws {UnrecoverableError} when a role cannot be set up, for example because
41
+ * `OPENAI_API_KEY` or the Codex CLI binary is missing, or when no program can read the
42
+ * repository.
43
+ */
44
+ export declare function createLLMOrchestrator({ provider, autoApprove, systemPrompts, logger, }?: CreateOrchestratorOptions): Promise<Agent>;
@@ -0,0 +1,98 @@
1
+ import { InvalidAgentConfigError } from '#src/agent/domain/errors';
2
+ import { LLMAgent } from '#src/engines/domain/model/llmAgent';
3
+ import { ClaudeLLMClient } from '#src/llm/infrastructure/claudeLLMClient';
4
+ import { OpenAILLMClient } from '#src/llm/infrastructure/openAILLMClient';
5
+ import { InstructedAgent } from '#src/orchestration/domain/model/instructedAgent';
6
+ import { executorInstructions, OrchestratorAgent, plannerInstructions, reviewerInstructions, } from '#src/orchestration/domain/model/orchestratorAgent';
7
+ import { ReviewerDecisionValidator } from '#src/orchestration/infrastructure/model/reviewerDecisionValidator';
8
+ import { RetryingAgent } from '#src/retry/domain/model/retryingAgent';
9
+ import { Logger } from '#src/shared/infrastructure/logger';
10
+ import { createWorkspace } from '#src/tools/infrastructure/createWorkspace';
11
+ import { createWorkspaceTools } from '#src/tools/infrastructure/workspaceTools';
12
+ import { createAgent, orchestratorRolesFor } from "./agentFactory.js";
13
+ import { agentProviders, } from "./types.js";
14
+ const defaultLLMAgentModels = {
15
+ anthropic: 'sonnet',
16
+ openai: 'gpt-5.6-luna',
17
+ };
18
+ /**
19
+ * Builds an agent whose conversation MiKode owns, on a provider's model API, already wrapped in
20
+ * the harness's retry policy. Internal while #23 reaches parity with the Agent SDK engines: it
21
+ * is not exported from `src/index.ts`. Its tools only read, so it has no `autoApprove`.
22
+ *
23
+ * @throws {InvalidAgentConfigError} for an unknown provider, or a model or reasoning effort its
24
+ * client does not support.
25
+ * @throws {UnrecoverableError} when the client cannot be set up, for example because
26
+ * `OPENAI_API_KEY` is missing. The Anthropic SDK resolves credentials lazily, so a missing
27
+ * `ANTHROPIC_API_KEY` fails the first run instead, with an `UnrecoverableError`.
28
+ */
29
+ export function createLLMAgent(provider, { model, reasoningEffort, systemPrompt, tools, logger = new Logger() }) {
30
+ let llmClient;
31
+ switch (provider) {
32
+ case 'anthropic':
33
+ llmClient = new ClaudeLLMClient({
34
+ model: model ?? defaultLLMAgentModels.anthropic,
35
+ reasoningEffort,
36
+ systemPrompt,
37
+ logger,
38
+ });
39
+ break;
40
+ case 'openai':
41
+ llmClient = new OpenAILLMClient({
42
+ model: model ?? defaultLLMAgentModels.openai,
43
+ reasoningEffort,
44
+ systemPrompt,
45
+ logger,
46
+ });
47
+ break;
48
+ default: {
49
+ // Reachable from untyped input.
50
+ const unknownProvider = provider;
51
+ throw new InvalidAgentConfigError(`"${String(unknownProvider)}" is not an agent provider; expected one of: ${agentProviders.join(', ')}`);
52
+ }
53
+ }
54
+ // LLMAgent records nothing from a failed call, so the original prompt is the whole story.
55
+ return new RetryingAgent({
56
+ inner: new LLMAgent({ llmClient, tools }),
57
+ logger,
58
+ noteFailures: false,
59
+ });
60
+ }
61
+ // Sent on every call after the role's instructions, so it stays short: it only says how to find
62
+ // one's way around the repository without reading all of it.
63
+ const repositoryGuidance = 'Paths are relative to the repository root. Start with AGENTS.md and the architecture document it links to learn where things live, then read only the files your task needs.';
64
+ /**
65
+ * Builds the planner → executor → reviewer workflow with the planner and reviewer on the model
66
+ * APIs, from `createLLMAgent`, each holding its role's instructions, or `systemPrompts`, as its
67
+ * system prompt, with the read-only repository tools for the current working directory. The
68
+ * executor stays on its Agent SDK, from `createAgent`, until an agent of ours can change files; `autoApprove` reaches
69
+ * only it. Asynchronous because finding the program that reads the repository is.
70
+ *
71
+ * @throws {InvalidAgentConfigError} for an unknown provider.
72
+ * @throws {UnrecoverableError} when a role cannot be set up, for example because
73
+ * `OPENAI_API_KEY` or the Codex CLI binary is missing, or when no program can read the
74
+ * repository.
75
+ */
76
+ export async function createLLMOrchestrator({ provider, autoApprove, systemPrompts = {}, logger = new Logger(), } = {}) {
77
+ const roles = orchestratorRolesFor(provider);
78
+ const tools = createWorkspaceTools(await createWorkspace({ root: process.cwd(), logger }));
79
+ const llmAgentFor = ({ provider, model, reasoningEffort }, systemPrompt) => createLLMAgent(provider, { model, reasoningEffort, systemPrompt, tools, logger });
80
+ // A caller's own prompt replaces the whole of ours, guidance included.
81
+ const withGuidance = (instructions) => `${instructions}\n\n${repositoryGuidance}`;
82
+ return new OrchestratorAgent({
83
+ plannerAgent: llmAgentFor(roles.planner, systemPrompts.planner ?? withGuidance(plannerInstructions)),
84
+ executorAgent: new InstructedAgent({
85
+ inner: createAgent(roles.executor.provider, {
86
+ model: roles.executor.model,
87
+ reasoningEffort: roles.executor.reasoningEffort,
88
+ autoApprove,
89
+ logger,
90
+ }),
91
+ instructions: systemPrompts.executor ?? executorInstructions,
92
+ }),
93
+ reviewerAgent: llmAgentFor(roles.reviewer, systemPrompts.reviewer ?? withGuidance(reviewerInstructions)),
94
+ reviewerDecisionValidator: new ReviewerDecisionValidator(),
95
+ logger,
96
+ });
97
+ }
98
+ //# sourceMappingURL=agentLLMFactory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentLLMFactory.js","sourceRoot":"","sources":["../../../src/factory/infrastructure/agentLLMFactory.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Agent } from '#src/agent/domain/agent';\nimport { InvalidAgentConfigError } from '#src/agent/domain/errors';\nimport { LLMAgent } from '#src/engines/domain/model/llmAgent';\nimport type { LLMClient } from '#src/llm/domain/llm';\nimport { ClaudeLLMClient } from '#src/llm/infrastructure/claudeLLMClient';\nimport { OpenAILLMClient } from '#src/llm/infrastructure/openAILLMClient';\nimport { InstructedAgent } from '#src/orchestration/domain/model/instructedAgent';\nimport {\n\texecutorInstructions,\n\tOrchestratorAgent,\n\tplannerInstructions,\n\treviewerInstructions,\n} from '#src/orchestration/domain/model/orchestratorAgent';\nimport { ReviewerDecisionValidator } from '#src/orchestration/infrastructure/model/reviewerDecisionValidator';\nimport { RetryingAgent } from '#src/retry/domain/model/retryingAgent';\nimport type { ILogger } from '#src/shared/domain/logger';\nimport { Logger } from '#src/shared/infrastructure/logger';\nimport type { Tool } from '#src/tools/domain/tool';\nimport { createWorkspace } from '#src/tools/infrastructure/createWorkspace';\nimport { createWorkspaceTools } from '#src/tools/infrastructure/workspaceTools';\nimport { createAgent, orchestratorRolesFor, type Role } from './agentFactory.ts';\nimport {\n\tagentProviders,\n\ttype AgentModel,\n\ttype AgentProvider,\n\ttype CreateOrchestratorOptions,\n\ttype ReasoningEffort,\n} from './types.ts';\n\nexport interface CreateLLMAgentOptions {\n\t/**\n\t * Defaults to `'sonnet'` on Anthropic and `'gpt-5.6-luna'` on OpenAI. The provider's client\n\t * rejects a model it does not support.\n\t */\n\tmodel?: AgentModel;\n\t/** Defaults to `'high'`. The provider's client rejects an effort it does not support. */\n\treasoningEffort?: ReasoningEffort;\n\t/** Sent as the model's instructions on every call. */\n\tsystemPrompt: string;\n\t/** The tools the model may call. Defaults to none. */\n\ttools?: Tool[];\n\t/** Defaults to warnings on stderr. */\n\tlogger?: ILogger;\n}\n\nconst defaultLLMAgentModels = {\n\tanthropic: 'sonnet',\n\topenai: 'gpt-5.6-luna',\n} as const satisfies Record<AgentProvider, AgentModel>;\n\n/**\n * Builds an agent whose conversation MiKode owns, on a provider's model API, already wrapped in\n * the harness's retry policy. Internal while #23 reaches parity with the Agent SDK engines: it\n * is not exported from `src/index.ts`. Its tools only read, so it has no `autoApprove`.\n *\n * @throws {InvalidAgentConfigError} for an unknown provider, or a model or reasoning effort its\n * client does not support.\n * @throws {UnrecoverableError} when the client cannot be set up, for example because\n * `OPENAI_API_KEY` is missing. The Anthropic SDK resolves credentials lazily, so a missing\n * `ANTHROPIC_API_KEY` fails the first run instead, with an `UnrecoverableError`.\n */\nexport function createLLMAgent(\n\tprovider: AgentProvider,\n\t{ model, reasoningEffort, systemPrompt, tools, logger = new Logger() }: CreateLLMAgentOptions,\n): Agent {\n\tlet llmClient: LLMClient;\n\n\tswitch (provider) {\n\t\tcase 'anthropic':\n\t\t\tllmClient = new ClaudeLLMClient({\n\t\t\t\tmodel: model ?? defaultLLMAgentModels.anthropic,\n\t\t\t\treasoningEffort,\n\t\t\t\tsystemPrompt,\n\t\t\t\tlogger,\n\t\t\t});\n\t\t\tbreak;\n\t\tcase 'openai':\n\t\t\tllmClient = new OpenAILLMClient({\n\t\t\t\tmodel: model ?? defaultLLMAgentModels.openai,\n\t\t\t\treasoningEffort,\n\t\t\t\tsystemPrompt,\n\t\t\t\tlogger,\n\t\t\t});\n\t\t\tbreak;\n\t\tdefault: {\n\t\t\t// Reachable from untyped input.\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\t// LLMAgent records nothing from a failed call, so the original prompt is the whole story.\n\treturn new RetryingAgent({\n\t\tinner: new LLMAgent({ llmClient, tools }),\n\t\tlogger,\n\t\tnoteFailures: false,\n\t});\n}\n\n// Sent on every call after the role's instructions, so it stays short: it only says how to find\n// one's way around the repository without reading all of it.\nconst repositoryGuidance =\n\t'Paths are relative to the repository root. Start with AGENTS.md and the architecture document it links to learn where things live, then read only the files your task needs.';\n\n/**\n * Builds the planner → executor → reviewer workflow with the planner and reviewer on the model\n * APIs, from `createLLMAgent`, each holding its role's instructions, or `systemPrompts`, as its\n * system prompt, with the read-only repository tools for the current working directory. The\n * executor stays on its Agent SDK, from `createAgent`, until an agent of ours can change files; `autoApprove` reaches\n * only it. Asynchronous because finding the program that reads the repository is.\n *\n * @throws {InvalidAgentConfigError} for an unknown provider.\n * @throws {UnrecoverableError} when a role cannot be set up, for example because\n * `OPENAI_API_KEY` or the Codex CLI binary is missing, or when no program can read the\n * repository.\n */\nexport async function createLLMOrchestrator({\n\tprovider,\n\tautoApprove,\n\tsystemPrompts = {},\n\tlogger = new Logger(),\n}: CreateOrchestratorOptions = {}): Promise<Agent> {\n\tconst roles = orchestratorRolesFor(provider);\n\tconst tools = createWorkspaceTools(await createWorkspace({ root: process.cwd(), logger }));\n\tconst llmAgentFor = ({ provider, model, reasoningEffort }: Role, systemPrompt: string) =>\n\t\tcreateLLMAgent(provider, { model, reasoningEffort, systemPrompt, tools, logger });\n\t// A caller's own prompt replaces the whole of ours, guidance included.\n\tconst withGuidance = (instructions: string) => `${instructions}\\n\\n${repositoryGuidance}`;\n\n\treturn new OrchestratorAgent({\n\t\tplannerAgent: llmAgentFor(\n\t\t\troles.planner,\n\t\t\tsystemPrompts.planner ?? withGuidance(plannerInstructions),\n\t\t),\n\t\texecutorAgent: new InstructedAgent({\n\t\t\tinner: createAgent(roles.executor.provider, {\n\t\t\t\tmodel: roles.executor.model,\n\t\t\t\treasoningEffort: roles.executor.reasoningEffort,\n\t\t\t\tautoApprove,\n\t\t\t\tlogger,\n\t\t\t}),\n\t\t\tinstructions: systemPrompts.executor ?? executorInstructions,\n\t\t}),\n\t\treviewerAgent: llmAgentFor(\n\t\t\troles.reviewer,\n\t\t\tsystemPrompts.reviewer ?? withGuidance(reviewerInstructions),\n\t\t),\n\t\treviewerDecisionValidator: new ReviewerDecisionValidator(),\n\t\tlogger,\n\t});\n}\n"]}