@mikode13/harness 2.3.0 → 3.1.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 (29) hide show
  1. package/README.md +78 -9
  2. package/dist/factory/infrastructure/agentFactory.d.ts +15 -46
  3. package/dist/factory/infrastructure/agentFactory.js +46 -77
  4. package/dist/factory/infrastructure/agentFactory.js.map +1 -1
  5. package/dist/factory/infrastructure/agentLLMFactory.d.ts +46 -0
  6. package/dist/factory/infrastructure/agentLLMFactory.js +100 -0
  7. package/dist/factory/infrastructure/agentLLMFactory.js.map +1 -0
  8. package/dist/factory/infrastructure/types.d.ts +33 -0
  9. package/dist/factory/infrastructure/types.js +3 -0
  10. package/dist/factory/infrastructure/types.js.map +1 -0
  11. package/dist/index.d.ts +9 -1
  12. package/dist/index.js +6 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/llm/infrastructure/claudeLLMClient.d.ts +6 -2
  15. package/dist/llm/infrastructure/claudeLLMClient.js +26 -5
  16. package/dist/llm/infrastructure/claudeLLMClient.js.map +1 -1
  17. package/dist/llm/infrastructure/openAILLMClient.d.ts +5 -1
  18. package/dist/llm/infrastructure/openAILLMClient.js +17 -3
  19. package/dist/llm/infrastructure/openAILLMClient.js.map +1 -1
  20. package/dist/orchestration/domain/model/instructedAgent.d.ts +15 -0
  21. package/dist/orchestration/domain/model/instructedAgent.js +17 -0
  22. package/dist/orchestration/domain/model/instructedAgent.js.map +1 -0
  23. package/dist/orchestration/domain/model/orchestratorAgent.d.ts +3 -0
  24. package/dist/orchestration/domain/model/orchestratorAgent.js +9 -9
  25. package/dist/orchestration/domain/model/orchestratorAgent.js.map +1 -1
  26. package/dist/tools/infrastructure/createWorkspace.d.ts +2 -1
  27. package/dist/tools/infrastructure/createWorkspace.js +2 -1
  28. package/dist/tools/infrastructure/createWorkspace.js.map +1 -1
  29. package/package.json +7 -4
package/README.md CHANGED
@@ -32,11 +32,16 @@ multi-agent workflow, chosen entirely by what gets wired up in `cli/cli.ts`; the
32
32
  chat loop itself never knows the difference.
33
33
 
34
34
  - **A provider-agnostic `Agent` contract** (`run(prompt, signal, callback)`) with
35
- two real implementations, `CodexAgent` and `ClaudeAgent` — swapping one for the
36
- other, anywhere in the composition, changes nothing else.
37
- - **Conversation continuity for free** in both engines: Codex reuses one `Thread`
38
- across turns; Claude resumes via a captured `session_id` — the engine keeps
39
- context server-side either way.
35
+ two paths to each provider — swapping one agent for another, anywhere in the
36
+ composition, changes nothing else.
37
+ - **The Agent SDK path, the default**: `CodexAgent` and `ClaudeAgent` drive Codex
38
+ and Claude Code. Codex reuses one `Thread` across turns and Claude resumes via a
39
+ captured `session_id`, so the provider keeps the context.
40
+ - **The model API path**: `LLMAgent` calls the OpenAI Responses API or the
41
+ Anthropic Messages API and keeps the conversation itself, in a form that can move
42
+ between providers. It runs its own tool loop over the tools it is given; the
43
+ harness ships three that only read the repository (list, search, read), bounded
44
+ by `.gitignore`.
40
45
  - **`OrchestratorAgent`**: coordinates a planner, an executor, and a reviewer
41
46
  (each an injected `Agent`) in a plan → execute → review loop. The reviewer's
42
47
  decision is a Zod-validated structured `{decision, feedback}`, not free text —
@@ -96,9 +101,12 @@ comment on `Agent` in `src/agent/domain/agent.ts`.
96
101
  ## Install
97
102
 
98
103
  ```sh
99
- pnpm add @mikode13/harness
104
+ pnpm add @mikode13/harness zod
100
105
  ```
101
106
 
107
+ `zod` 4 is a peer dependency: `defineTool` takes your Zod schemas, so the harness uses
108
+ your copy rather than bundling its own.
109
+
102
110
  The package is ESM only and runs on Node.js 22 (22.13 or later) or 24. Its type declarations resolve
103
111
  internal modules through the `imports` field of its `package.json`, so a TypeScript consumer
104
112
  needs `moduleResolution` set to `node16`, `nodenext` or `bundler`; the legacy `node10`
@@ -111,7 +119,7 @@ factories let you replace through their `logger` option:
111
119
  ```ts
112
120
  import { createAgent, UnrecoverableError, type ProgressEvent } from '@mikode13/harness';
113
121
 
114
- const agent = createAgent('claude', { model: 'sonnet' });
122
+ const agent = createAgent('anthropic', { model: 'sonnet' });
115
123
  const controller = new AbortController();
116
124
 
117
125
  const render = (event: ProgressEvent) => {
@@ -133,10 +141,13 @@ try {
133
141
  consumer's decision, not the harness's. `cli/progressEventFormatter.ts` is one
134
142
  terminal-shaped implementation to copy from. New event types can arrive in minor
135
143
  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 →
144
+ `createAgent('openai')`, or for `createOrchestrator()` and its planner → executor →
137
145
  reviewer workflow, changes nothing else in the snippet above.
138
- `createOrchestrator({ provider: 'claude' })` runs every role on one provider, for
146
+ `createOrchestrator({ provider: 'anthropic' })` runs every role on one provider, for
139
147
  example when the other one is out of quota.
148
+ `systemPrompts: { planner, executor, reviewer }` replaces a role's instructions;
149
+ a role left out keeps the harness's own, and the reviewer must still answer with
150
+ the JSON decision described on the option.
140
151
 
141
152
  Every agent a factory returns already retries recoverable failures. A model or
142
153
  reasoning effort the chosen provider does not support throws
@@ -144,6 +155,59 @@ reasoning effort the chosen provider does not support throws
144
155
  `UnrecoverableError` if the Codex CLI binary cannot be found, which happens when
145
156
  optional dependencies were skipped at install time.
146
157
 
158
+ ### Authentication
159
+
160
+ The two paths authenticate differently:
161
+
162
+ - `createAgent` and `createOrchestrator` use the Agent SDKs, which use the login of
163
+ the Codex CLI and of Claude Code, so a run counts against that subscription.
164
+ - `createLLMAgent` and the planner and reviewer of `createLLMOrchestrator` call the
165
+ model APIs, which bill per token and need the key of each provider they use in
166
+ the environment: `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, or both for the
167
+ orchestrator's default roles. A missing OpenAI key fails when the agent is built; a missing
168
+ Anthropic key fails its first run.
169
+
170
+ The Claude Agent SDK
171
+ [authenticates with `ANTHROPIC_API_KEY`](https://code.claude.com/docs/en/agent-sdk) whenever
172
+ it is set, so with that key in the environment a Claude agent from `createAgent`, or the executor of
173
+ `createLLMOrchestrator({ provider: 'anthropic' })`, bills the key instead of the
174
+ subscription.
175
+
176
+ ### Agents that own their conversation
177
+
178
+ `createLLMAgent` takes a system prompt and the tools the model may call, and
179
+ returns the same `Agent`. `defineTool` builds a tool from a Zod 4 schema; a field the
180
+ model may leave out is `.nullable()`, not optional, because both APIs call tools in
181
+ strict mode:
182
+
183
+ ```ts
184
+ import {
185
+ createLLMAgent,
186
+ createWorkspace,
187
+ createWorkspaceTools,
188
+ defineTool,
189
+ } from '@mikode13/harness';
190
+ import { z } from 'zod';
191
+
192
+ const now = defineTool({
193
+ name: 'now',
194
+ description: 'The current time, in ISO 8601.',
195
+ input: z.object({}),
196
+ execute: async () => new Date().toISOString(),
197
+ });
198
+
199
+ const workspace = await createWorkspace({ root: process.cwd() });
200
+ const agent = createLLMAgent('openai', {
201
+ systemPrompt: 'You answer questions about this repository.',
202
+ tools: [...createWorkspaceTools(workspace), now],
203
+ });
204
+ ```
205
+
206
+ The agent runs every tool it is given, without asking: there is no approval step
207
+ yet, so only give it tools you would let run unattended. `createLLMOrchestrator()`
208
+ takes the same options as `createOrchestrator()` and returns a promise; its executor
209
+ still runs on an Agent SDK, because the harness's own tools cannot change files.
210
+
147
211
  ## Tests
148
212
 
149
213
  `pnpm test` runs the unit suite against deterministic fakes; it never contacts a
@@ -161,6 +225,11 @@ exists solely to exercise the library manually while working in this repository:
161
225
  pnpm run dev
162
226
  ```
163
227
 
228
+ The agents work on the repository root, which is the directory the script runs in.
229
+ `pnpm run dev --llm` plans and reviews on the model APIs instead, through
230
+ `createLLMOrchestrator`, and needs the API keys in the environment (see
231
+ [Authentication](#authentication)).
232
+
164
233
  Type your prompt at `>`. Press Ctrl+C while idle at the prompt to exit; pressing
165
234
  it while an agent is running cancels only that turn and returns to the prompt.
166
235
 
@@ -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,46 @@
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. It needs the provider's API key and bills per token, where
23
+ * `createAgent` uses the Agent SDK's login. It has no `autoApprove`: it runs whatever tools it
24
+ * is given, so a caller who gives it tools that change things owns that decision.
25
+ *
26
+ * @throws {InvalidAgentConfigError} for an unknown provider, a model or reasoning effort its
27
+ * client does not support, or two tools that share a name.
28
+ * @throws {UnrecoverableError} when the client cannot be set up, for example because
29
+ * `OPENAI_API_KEY` is missing. The Anthropic SDK resolves credentials lazily, so a missing
30
+ * `ANTHROPIC_API_KEY` fails the first run instead, with an `UnrecoverableError`.
31
+ */
32
+ export declare function createLLMAgent(provider: AgentProvider, { model, reasoningEffort, systemPrompt, tools, logger }: CreateLLMAgentOptions): Agent;
33
+ /**
34
+ * Builds the planner → executor → reviewer workflow with the planner and reviewer on the model
35
+ * APIs, from `createLLMAgent`, each holding its role's instructions, or `systemPrompts`, as its
36
+ * system prompt, with the read-only repository tools for the current working directory. The
37
+ * executor stays on its Agent SDK, from `createAgent`, until an agent of ours can change files;
38
+ * `autoApprove` reaches only it. Asynchronous because finding the program that reads the
39
+ * repository is.
40
+ *
41
+ * @throws {InvalidAgentConfigError} for an unknown provider.
42
+ * @throws {UnrecoverableError} when a role cannot be set up, for example because
43
+ * `OPENAI_API_KEY` or the Codex CLI binary is missing, or when no program can read the
44
+ * repository.
45
+ */
46
+ export declare function createLLMOrchestrator({ provider, autoApprove, systemPrompts, logger, }?: CreateOrchestratorOptions): Promise<Agent>;
@@ -0,0 +1,100 @@
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. It needs the provider's API key and bills per token, where
21
+ * `createAgent` uses the Agent SDK's login. It has no `autoApprove`: it runs whatever tools it
22
+ * is given, so a caller who gives it tools that change things owns that decision.
23
+ *
24
+ * @throws {InvalidAgentConfigError} for an unknown provider, a model or reasoning effort its
25
+ * client does not support, or two tools that share a name.
26
+ * @throws {UnrecoverableError} when the client cannot be set up, for example because
27
+ * `OPENAI_API_KEY` is missing. The Anthropic SDK resolves credentials lazily, so a missing
28
+ * `ANTHROPIC_API_KEY` fails the first run instead, with an `UnrecoverableError`.
29
+ */
30
+ export function createLLMAgent(provider, { model, reasoningEffort, systemPrompt, tools, logger = new Logger() }) {
31
+ let llmClient;
32
+ switch (provider) {
33
+ case 'anthropic':
34
+ llmClient = new ClaudeLLMClient({
35
+ model: model ?? defaultLLMAgentModels.anthropic,
36
+ reasoningEffort,
37
+ systemPrompt,
38
+ logger,
39
+ });
40
+ break;
41
+ case 'openai':
42
+ llmClient = new OpenAILLMClient({
43
+ model: model ?? defaultLLMAgentModels.openai,
44
+ reasoningEffort,
45
+ systemPrompt,
46
+ logger,
47
+ });
48
+ break;
49
+ default: {
50
+ // Reachable from untyped input.
51
+ const unknownProvider = provider;
52
+ throw new InvalidAgentConfigError(`"${String(unknownProvider)}" is not an agent provider; expected one of: ${agentProviders.join(', ')}`);
53
+ }
54
+ }
55
+ // LLMAgent records nothing from a failed call, so the original prompt is the whole story.
56
+ return new RetryingAgent({
57
+ inner: new LLMAgent({ llmClient, tools }),
58
+ logger,
59
+ noteFailures: false,
60
+ });
61
+ }
62
+ // Sent on every call after the role's instructions, so it stays short: it only says how to find
63
+ // one's way around the repository without reading all of it.
64
+ 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.';
65
+ /**
66
+ * Builds the planner → executor → reviewer workflow with the planner and reviewer on the model
67
+ * APIs, from `createLLMAgent`, each holding its role's instructions, or `systemPrompts`, as its
68
+ * system prompt, with the read-only repository tools for the current working directory. The
69
+ * executor stays on its Agent SDK, from `createAgent`, until an agent of ours can change files;
70
+ * `autoApprove` reaches only it. Asynchronous because finding the program that reads the
71
+ * repository is.
72
+ *
73
+ * @throws {InvalidAgentConfigError} for an unknown provider.
74
+ * @throws {UnrecoverableError} when a role cannot be set up, for example because
75
+ * `OPENAI_API_KEY` or the Codex CLI binary is missing, or when no program can read the
76
+ * repository.
77
+ */
78
+ export async function createLLMOrchestrator({ provider, autoApprove, systemPrompts = {}, logger = new Logger(), } = {}) {
79
+ const roles = orchestratorRolesFor(provider);
80
+ const tools = createWorkspaceTools(await createWorkspace({ root: process.cwd(), logger }));
81
+ const llmAgentFor = ({ provider, model, reasoningEffort }, systemPrompt) => createLLMAgent(provider, { model, reasoningEffort, systemPrompt, tools, logger });
82
+ // A caller's own prompt replaces the whole of ours, guidance included.
83
+ const withGuidance = (instructions) => `${instructions}\n\n${repositoryGuidance}`;
84
+ return new OrchestratorAgent({
85
+ plannerAgent: llmAgentFor(roles.planner, systemPrompts.planner ?? withGuidance(plannerInstructions)),
86
+ executorAgent: new InstructedAgent({
87
+ inner: createAgent(roles.executor.provider, {
88
+ model: roles.executor.model,
89
+ reasoningEffort: roles.executor.reasoningEffort,
90
+ autoApprove,
91
+ logger,
92
+ }),
93
+ instructions: systemPrompts.executor ?? executorInstructions,
94
+ }),
95
+ reviewerAgent: llmAgentFor(roles.reviewer, systemPrompts.reviewer ?? withGuidance(reviewerInstructions)),
96
+ reviewerDecisionValidator: new ReviewerDecisionValidator(),
97
+ logger,
98
+ });
99
+ }
100
+ //# 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. It needs the provider's API key and bills per token, where\n * `createAgent` uses the Agent SDK's login. It has no `autoApprove`: it runs whatever tools it\n * is given, so a caller who gives it tools that change things owns that decision.\n *\n * @throws {InvalidAgentConfigError} for an unknown provider, a model or reasoning effort its\n * client does not support, or two tools that share a name.\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;\n * `autoApprove` reaches only it. Asynchronous because finding the program that reads the\n * 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"]}