@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.
- package/README.md +78 -9
- package/dist/factory/infrastructure/agentFactory.d.ts +15 -46
- package/dist/factory/infrastructure/agentFactory.js +46 -77
- package/dist/factory/infrastructure/agentFactory.js.map +1 -1
- package/dist/factory/infrastructure/agentLLMFactory.d.ts +46 -0
- package/dist/factory/infrastructure/agentLLMFactory.js +100 -0
- package/dist/factory/infrastructure/agentLLMFactory.js.map +1 -0
- package/dist/factory/infrastructure/types.d.ts +33 -0
- package/dist/factory/infrastructure/types.js +3 -0
- package/dist/factory/infrastructure/types.js.map +1 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/llm/infrastructure/claudeLLMClient.d.ts +6 -2
- package/dist/llm/infrastructure/claudeLLMClient.js +26 -5
- package/dist/llm/infrastructure/claudeLLMClient.js.map +1 -1
- package/dist/llm/infrastructure/openAILLMClient.d.ts +5 -1
- package/dist/llm/infrastructure/openAILLMClient.js +17 -3
- package/dist/llm/infrastructure/openAILLMClient.js.map +1 -1
- package/dist/orchestration/domain/model/instructedAgent.d.ts +15 -0
- package/dist/orchestration/domain/model/instructedAgent.js +17 -0
- package/dist/orchestration/domain/model/instructedAgent.js.map +1 -0
- package/dist/orchestration/domain/model/orchestratorAgent.d.ts +3 -0
- package/dist/orchestration/domain/model/orchestratorAgent.js +9 -9
- package/dist/orchestration/domain/model/orchestratorAgent.js.map +1 -1
- package/dist/tools/infrastructure/createWorkspace.d.ts +2 -1
- package/dist/tools/infrastructure/createWorkspace.js +2 -1
- package/dist/tools/infrastructure/createWorkspace.js.map +1 -1
- 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
|
|
36
|
-
|
|
37
|
-
- **
|
|
38
|
-
|
|
39
|
-
|
|
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('
|
|
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('
|
|
144
|
+
`createAgent('openai')`, or for `createOrchestrator()` and its planner → executor →
|
|
137
145
|
reviewer workflow, changes nothing else in the snippet above.
|
|
138
|
-
`createOrchestrator({ provider: '
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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
|
|
69
|
-
*
|
|
70
|
-
*
|
|
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
|
|
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
|
|
3
|
-
import { CodexAgent
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
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
|
-
|
|
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
|
-
|
|
18
|
-
|
|
15
|
+
anthropic: 'opus',
|
|
16
|
+
openai: 'gpt-5.6-sol',
|
|
19
17
|
};
|
|
20
18
|
/**
|
|
21
|
-
* Builds the agent for a provider
|
|
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 '
|
|
30
|
+
case 'anthropic':
|
|
32
31
|
engine = new ClaudeAgent({
|
|
33
|
-
model: model ?? defaultModels.
|
|
32
|
+
model: model ?? defaultModels.anthropic,
|
|
34
33
|
reasoningEffort,
|
|
35
34
|
autoApprove,
|
|
36
35
|
logger,
|
|
37
36
|
});
|
|
38
37
|
break;
|
|
39
|
-
case '
|
|
38
|
+
case 'openai':
|
|
40
39
|
engine = new CodexAgent({
|
|
41
|
-
model: model ?? defaultModels.
|
|
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: '
|
|
59
|
-
executor: { provider: '
|
|
60
|
-
reviewer: { provider: '
|
|
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
|
-
|
|
63
|
-
planner: { provider: '
|
|
64
|
-
executor: { provider: '
|
|
65
|
-
reviewer: { provider: '
|
|
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
|
-
|
|
68
|
-
planner: { provider: '
|
|
69
|
-
executor: { provider: '
|
|
70
|
-
reviewer: { provider: '
|
|
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
|
-
*
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
105
|
-
*
|
|
106
|
-
*
|
|
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
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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"]}
|