@mikode13/harness 4.0.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +285 -20
- package/dist/agent/domain/agent.d.ts +12 -0
- package/dist/agent/domain/agent.js.map +1 -1
- package/dist/agent/domain/approval.d.ts +14 -0
- package/dist/agent/domain/approval.js +28 -3
- package/dist/agent/domain/approval.js.map +1 -1
- package/dist/agent/domain/runContext.d.ts +31 -0
- package/dist/agent/domain/runContext.js +47 -0
- package/dist/agent/domain/runContext.js.map +1 -0
- package/dist/diff/domain/unifiedDiff.d.ts +19 -0
- package/dist/diff/domain/unifiedDiff.js +166 -0
- package/dist/diff/domain/unifiedDiff.js.map +1 -0
- package/dist/engines/domain/model/historyFollower.d.ts +41 -0
- package/dist/engines/domain/model/historyFollower.js +158 -0
- package/dist/engines/domain/model/historyFollower.js.map +1 -0
- package/dist/engines/domain/model/llmAgent.d.ts +42 -4
- package/dist/engines/domain/model/llmAgent.js +224 -36
- package/dist/engines/domain/model/llmAgent.js.map +1 -1
- package/dist/factory/infrastructure/agentFactory.d.ts +14 -0
- package/dist/factory/infrastructure/agentFactory.js +20 -2
- package/dist/factory/infrastructure/agentFactory.js.map +1 -1
- package/dist/factory/infrastructure/agentLLMFactory.d.ts +119 -14
- package/dist/factory/infrastructure/agentLLMFactory.js +284 -57
- package/dist/factory/infrastructure/agentLLMFactory.js.map +1 -1
- package/dist/index.d.ts +11 -3
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -1
- package/dist/llm/domain/conversation.d.ts +44 -4
- package/dist/llm/domain/conversation.js +56 -7
- package/dist/llm/domain/conversation.js.map +1 -1
- package/dist/llm/domain/llm.d.ts +3 -1
- package/dist/llm/domain/llm.js.map +1 -1
- package/dist/llm/domain/tool.d.ts +13 -0
- package/dist/llm/domain/tool.js +12 -1
- package/dist/llm/domain/tool.js.map +1 -1
- package/dist/llm/infrastructure/claudeLLMClient.d.ts +3 -2
- package/dist/llm/infrastructure/claudeLLMClient.js +38 -10
- package/dist/llm/infrastructure/claudeLLMClient.js.map +1 -1
- package/dist/llm/infrastructure/openAILLMClient.d.ts +2 -1
- package/dist/llm/infrastructure/openAILLMClient.js +66 -16
- package/dist/llm/infrastructure/openAILLMClient.js.map +1 -1
- package/dist/orchestration/domain/model/orchestratorAgent.d.ts +11 -2
- package/dist/orchestration/domain/model/orchestratorAgent.js +43 -4
- package/dist/orchestration/domain/model/orchestratorAgent.js.map +1 -1
- package/dist/recovery/domain/fileChanges.d.ts +31 -0
- package/dist/recovery/domain/fileChanges.js +48 -0
- package/dist/recovery/domain/fileChanges.js.map +1 -0
- package/dist/recovery/domain/history.d.ts +114 -0
- package/dist/recovery/domain/history.js +62 -0
- package/dist/recovery/domain/history.js.map +1 -0
- package/dist/recovery/domain/recoveryStore.d.ts +233 -0
- package/dist/recovery/domain/recoveryStore.js +32 -0
- package/dist/recovery/domain/recoveryStore.js.map +1 -0
- package/dist/recovery/domain/retention.d.ts +33 -0
- package/dist/recovery/domain/retention.js +90 -0
- package/dist/recovery/domain/retention.js.map +1 -0
- package/dist/recovery/domain/runChanges.d.ts +37 -0
- package/dist/recovery/domain/runChanges.js +109 -0
- package/dist/recovery/domain/runChanges.js.map +1 -0
- package/dist/recovery/domain/runTree.d.ts +32 -0
- package/dist/recovery/domain/runTree.js +90 -0
- package/dist/recovery/domain/runTree.js.map +1 -0
- package/dist/recovery/infrastructure/createHistory.d.ts +11 -0
- package/dist/recovery/infrastructure/createHistory.js +17 -0
- package/dist/recovery/infrastructure/createHistory.js.map +1 -0
- package/dist/recovery/infrastructure/fileRecoveryStore.d.ts +133 -0
- package/dist/recovery/infrastructure/fileRecoveryStore.js +1043 -0
- package/dist/recovery/infrastructure/fileRecoveryStore.js.map +1 -0
- package/dist/recovery/infrastructure/workspaceFiles.d.ts +30 -0
- package/dist/recovery/infrastructure/workspaceFiles.js +125 -0
- package/dist/recovery/infrastructure/workspaceFiles.js.map +1 -0
- package/dist/tools/domain/accessPolicy.d.ts +30 -0
- package/dist/tools/domain/accessPolicy.js +11 -0
- package/dist/tools/domain/accessPolicy.js.map +1 -0
- package/dist/tools/domain/commands.d.ts +93 -0
- package/dist/tools/domain/commands.js +191 -0
- package/dist/tools/domain/commands.js.map +1 -0
- package/dist/tools/domain/editResponse.d.ts +14 -0
- package/dist/tools/domain/editResponse.js +79 -0
- package/dist/tools/domain/editResponse.js.map +1 -0
- package/dist/tools/domain/fileEdits.d.ts +64 -0
- package/dist/tools/domain/fileEdits.js +21 -0
- package/dist/tools/domain/fileEdits.js.map +1 -0
- package/dist/tools/domain/lineLength.d.ts +7 -0
- package/dist/tools/domain/lineLength.js +26 -0
- package/dist/tools/domain/lineLength.js.map +1 -0
- package/dist/tools/domain/preparedCall.d.ts +31 -0
- package/dist/tools/domain/preparedCall.js +14 -0
- package/dist/tools/domain/preparedCall.js.map +1 -0
- package/dist/tools/domain/readRegistry.d.ts +46 -0
- package/dist/tools/domain/readRegistry.js +62 -0
- package/dist/tools/domain/readRegistry.js.map +1 -0
- package/dist/tools/domain/textLines.d.ts +26 -0
- package/dist/tools/domain/textLines.js +44 -0
- package/dist/tools/domain/textLines.js.map +1 -0
- package/dist/tools/domain/v4aPatch.d.ts +17 -0
- package/dist/tools/domain/v4aPatch.js +236 -0
- package/dist/tools/domain/v4aPatch.js.map +1 -0
- package/dist/tools/infrastructure/boundedWorkspace.d.ts +11 -2
- package/dist/tools/infrastructure/boundedWorkspace.js +9 -1
- package/dist/tools/infrastructure/boundedWorkspace.js.map +1 -1
- package/dist/tools/infrastructure/commandCheckpoint.d.ts +55 -0
- package/dist/tools/infrastructure/commandCheckpoint.js +178 -0
- package/dist/tools/infrastructure/commandCheckpoint.js.map +1 -0
- package/dist/tools/infrastructure/commandProcess.d.ts +47 -0
- package/dist/tools/infrastructure/commandProcess.js +217 -0
- package/dist/tools/infrastructure/commandProcess.js.map +1 -0
- package/dist/tools/infrastructure/commandTool.d.ts +36 -0
- package/dist/tools/infrastructure/commandTool.js +254 -0
- package/dist/tools/infrastructure/commandTool.js.map +1 -0
- package/dist/tools/infrastructure/createWorkspace.d.ts +10 -0
- package/dist/tools/infrastructure/createWorkspace.js +11 -3
- package/dist/tools/infrastructure/createWorkspace.js.map +1 -1
- package/dist/tools/infrastructure/defineTool.d.ts +14 -0
- package/dist/tools/infrastructure/defineTool.js +28 -5
- package/dist/tools/infrastructure/defineTool.js.map +1 -1
- package/dist/tools/infrastructure/editFormats.d.ts +22 -0
- package/dist/tools/infrastructure/editFormats.js +211 -0
- package/dist/tools/infrastructure/editFormats.js.map +1 -0
- package/dist/tools/infrastructure/fileEditor.d.ts +64 -0
- package/dist/tools/infrastructure/fileEditor.js +306 -0
- package/dist/tools/infrastructure/fileEditor.js.map +1 -0
- package/dist/tools/infrastructure/fileTools.d.ts +81 -0
- package/dist/tools/infrastructure/fileTools.js +152 -0
- package/dist/tools/infrastructure/fileTools.js.map +1 -0
- package/dist/tools/infrastructure/gitIgnoreRules.d.ts +15 -0
- package/dist/tools/infrastructure/gitIgnoreRules.js +74 -0
- package/dist/tools/infrastructure/gitIgnoreRules.js.map +1 -0
- package/dist/tools/infrastructure/gitWorkspace.d.ts +1 -1
- package/dist/tools/infrastructure/gitWorkspace.js +1 -1
- package/dist/tools/infrastructure/gitWorkspace.js.map +1 -1
- package/dist/tools/infrastructure/ignoreFileChanges.d.ts +14 -0
- package/dist/tools/infrastructure/ignoreFileChanges.js +62 -0
- package/dist/tools/infrastructure/ignoreFileChanges.js.map +1 -0
- package/dist/tools/infrastructure/policyWorkspace.d.ts +52 -0
- package/dist/tools/infrastructure/policyWorkspace.js +32 -0
- package/dist/tools/infrastructure/policyWorkspace.js.map +1 -0
- package/dist/tools/infrastructure/ripgrepWorkspace.d.ts +2 -1
- package/dist/tools/infrastructure/ripgrepWorkspace.js +1 -1
- package/dist/tools/infrastructure/ripgrepWorkspace.js.map +1 -1
- package/dist/tools/infrastructure/rootsAccessPolicy.d.ts +100 -0
- package/dist/tools/infrastructure/rootsAccessPolicy.js +301 -0
- package/dist/tools/infrastructure/rootsAccessPolicy.js.map +1 -0
- package/dist/tools/infrastructure/runProcess.d.ts +4 -1
- package/dist/tools/infrastructure/runProcess.js +4 -2
- package/dist/tools/infrastructure/runProcess.js.map +1 -1
- package/dist/tools/infrastructure/secretPaths.d.ts +8 -0
- package/dist/tools/infrastructure/secretPaths.js +76 -0
- package/dist/tools/infrastructure/secretPaths.js.map +1 -0
- package/dist/tools/infrastructure/showChangesTool.d.ts +12 -0
- package/dist/tools/infrastructure/showChangesTool.js +43 -0
- package/dist/tools/infrastructure/showChangesTool.js.map +1 -0
- package/dist/tools/infrastructure/trackedEdits.d.ts +33 -0
- package/dist/tools/infrastructure/trackedEdits.js +70 -0
- package/dist/tools/infrastructure/trackedEdits.js.map +1 -0
- package/dist/tools/infrastructure/trackedReadFile.d.ts +21 -0
- package/dist/tools/infrastructure/trackedReadFile.js +78 -0
- package/dist/tools/infrastructure/trackedReadFile.js.map +1 -0
- package/dist/tools/infrastructure/workspaceTools.d.ts +28 -0
- package/dist/tools/infrastructure/workspaceTools.js +53 -40
- package/dist/tools/infrastructure/workspaceTools.js.map +1 -1
- package/dist/tools/infrastructure/workspaceWrites.d.ts +23 -0
- package/dist/tools/infrastructure/workspaceWrites.js +42 -0
- package/dist/tools/infrastructure/workspaceWrites.js.map +1 -0
- package/dist/tools/infrastructure/writeSession.d.ts +35 -0
- package/dist/tools/infrastructure/writeSession.js +81 -0
- package/dist/tools/infrastructure/writeSession.js.map +1 -0
- package/package.json +1 -1
|
@@ -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/shared/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"]}
|
|
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/shared/domain/errors';\nimport {\n\tClaudeAgent,\n\tclaudeModels,\n\tclaudeReasoningEfforts,\n} from '#src/engines/claude/infrastructure/model/claudeAgent';\nimport {\n\tCodexAgent,\n\tcodexModels,\n\tcodexReasoningEfforts,\n} 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\n/** The models each provider's Agent SDK accepts. */\nexport const agentModels = {\n\tanthropic: claudeModels,\n\topenai: codexModels,\n} as const satisfies Readonly<Record<AgentProvider, readonly AgentModel[]>>;\n\n/** The reasoning efforts each provider's Agent SDK accepts. */\nexport const agentReasoningEfforts = {\n\tanthropic: claudeReasoningEfforts,\n\topenai: codexReasoningEfforts,\n} as const satisfies Readonly<Record<AgentProvider, readonly ReasoningEffort[]>>;\n\n/** Narrows a model to one accepted by the selected provider's Agent SDK. */\nexport function isModelOf<Provider extends AgentProvider>(\n\tprovider: Provider,\n\tvalue: string,\n): value is (typeof agentModels)[Provider][number] {\n\treturn isOneOf(agentModels[provider], value);\n}\n\n/** Narrows a reasoning effort to one accepted by the selected provider's Agent SDK. */\nexport function isReasoningEffortOf<Provider extends AgentProvider>(\n\tprovider: Provider,\n\tvalue: string,\n): value is (typeof agentReasoningEfforts)[Provider][number] {\n\treturn isOneOf(agentReasoningEfforts[provider], 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"]}
|
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
import type { Agent } from '#src/agent/domain/agent';
|
|
2
2
|
import type { ILogger } from '#src/shared/domain/logger';
|
|
3
|
+
import { type CommandRole, type Commands } from '#src/tools/domain/commands';
|
|
3
4
|
import type { Tool } from '#src/tools/domain/tool';
|
|
5
|
+
import { type FileTool, type WorkspaceOptions } from '#src/tools/infrastructure/fileTools';
|
|
4
6
|
import { type AgentModel, type AgentProvider, type CreateOrchestratorOptions, type ReasoningEffort } from './types.ts';
|
|
7
|
+
/** The models each provider's model API accepts. */
|
|
8
|
+
export declare const llmModels: {
|
|
9
|
+
readonly anthropic: readonly ["opus", "fable", "sonnet", "haiku"];
|
|
10
|
+
readonly openai: readonly ["gpt-6-astra", "gpt-5.6-sol", "gpt-5.6-luna", "gpt-5.6-terra"];
|
|
11
|
+
};
|
|
12
|
+
/** The reasoning efforts each provider's model API accepts. */
|
|
13
|
+
export declare const llmReasoningEfforts: {
|
|
14
|
+
readonly anthropic: readonly ["low", "medium", "high", "xhigh", "max"];
|
|
15
|
+
readonly openai: readonly ["low", "medium", "high", "xhigh", "max"];
|
|
16
|
+
};
|
|
17
|
+
/** Narrows a model to one accepted by the selected provider's model API. */
|
|
18
|
+
export declare function isLLMModelOf<Provider extends AgentProvider>(provider: Provider, value: string): value is (typeof llmModels)[Provider][number];
|
|
19
|
+
/** Narrows a reasoning effort to one accepted by the selected provider's model API. */
|
|
20
|
+
export declare function isLLMReasoningEffortOf<Provider extends AgentProvider>(provider: Provider, value: string): value is (typeof llmReasoningEfforts)[Provider][number];
|
|
5
21
|
export interface CreateLLMAgentOptions {
|
|
6
22
|
/**
|
|
7
23
|
* Defaults to `'sonnet'` on Anthropic and `'gpt-5.6-luna'` on OpenAI. The provider's client
|
|
@@ -12,8 +28,26 @@ export interface CreateLLMAgentOptions {
|
|
|
12
28
|
reasoningEffort?: ReasoningEffort;
|
|
13
29
|
/** Sent as the model's instructions on every call. */
|
|
14
30
|
systemPrompt: string;
|
|
15
|
-
/** The tools the model may call
|
|
16
|
-
tools?: Tool[];
|
|
31
|
+
/** The tools the model may call: the consumer's own, and those of `createFileTools`. Defaults to none. */
|
|
32
|
+
tools?: (Tool | FileTool)[];
|
|
33
|
+
/**
|
|
34
|
+
* The folders the agent works on, the same as given to `createFileTools`. With it, the agent
|
|
35
|
+
* follows the workspace's history: after the user undoes runs, its next run starts from the
|
|
36
|
+
* conversation as it was, and is told what was undone. The system prompt is followed by the
|
|
37
|
+
* roots and how to name a file in them.
|
|
38
|
+
*/
|
|
39
|
+
workspace?: WorkspaceOptions;
|
|
40
|
+
/**
|
|
41
|
+
* With `workspace`, a summary of what was undone, from the provider's cheap model, in place
|
|
42
|
+
* of the undone prompts. That is Claude Haiku without thinking, or `gpt-5.6-luna` at high
|
|
43
|
+
* effort, and the call is billed with the run's tokens. Defaults to `true`.
|
|
44
|
+
*/
|
|
45
|
+
summarizeUndone?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* How many calls to the model one run may make before it fails. Defaults to 65, with no upper
|
|
48
|
+
* bound. A run that asks for exactly the same tool calls 5 times in a row fails before that.
|
|
49
|
+
*/
|
|
50
|
+
maxSteps?: number;
|
|
17
51
|
/**
|
|
18
52
|
* Runs every tool call without asking, `destructive` ones included, for CI where no one can
|
|
19
53
|
* answer. Without it, a `destructive` call goes to the run's `approve`, or is denied.
|
|
@@ -22,6 +56,35 @@ export interface CreateLLMAgentOptions {
|
|
|
22
56
|
/** Defaults to warnings on stderr. */
|
|
23
57
|
logger?: ILogger;
|
|
24
58
|
}
|
|
59
|
+
/** A limit per role of `createLLMOrchestrator`; a role left out keeps its default. */
|
|
60
|
+
export type RoleSteps = Partial<Record<OrchestratorRole, number>>;
|
|
61
|
+
type OrchestratorRole = CommandRole;
|
|
62
|
+
export interface CreateLLMOrchestratorOptions extends CreateOrchestratorOptions {
|
|
63
|
+
/**
|
|
64
|
+
* The folders the agents work on. The executor changes files only in a `write` root, every
|
|
65
|
+
* change recorded in the workspace's history. Defaults to the current working directory,
|
|
66
|
+
* read only.
|
|
67
|
+
*/
|
|
68
|
+
workspace?: WorkspaceOptions;
|
|
69
|
+
/**
|
|
70
|
+
* How many calls to the model one run of each role may make: one number for every role, or a
|
|
71
|
+
* number per role. Defaults to 80 for the executor and 65 for the planner and the reviewer,
|
|
72
|
+
* with no upper bound. A run that asks for exactly the same tool calls 5 times in a row fails
|
|
73
|
+
* before its limit.
|
|
74
|
+
*/
|
|
75
|
+
maxSteps?: number | RoleSteps;
|
|
76
|
+
/**
|
|
77
|
+
* Commands the roles may run, such as the project's tests, through `runCommand`. Each goes to
|
|
78
|
+
* the roles its `roles` names, the executor by default. See `createFileTools`. Each needs a
|
|
79
|
+
* `write` root to run in.
|
|
80
|
+
*/
|
|
81
|
+
commands?: Commands;
|
|
82
|
+
/**
|
|
83
|
+
* A summary of what the user undid, from the planner's provider's cheap model, for every
|
|
84
|
+
* role. See `CreateLLMAgentOptions.summarizeUndone`. Defaults to `true`.
|
|
85
|
+
*/
|
|
86
|
+
summarizeUndone?: boolean;
|
|
87
|
+
}
|
|
25
88
|
/**
|
|
26
89
|
* Builds an agent whose conversation MiKode owns, on a provider's model API, already wrapped in
|
|
27
90
|
* the harness's retry policy. It needs the provider's API key and bills per token, where
|
|
@@ -34,18 +97,60 @@ export interface CreateLLMAgentOptions {
|
|
|
34
97
|
* `OPENAI_API_KEY` is missing. The Anthropic SDK resolves credentials lazily, so a missing
|
|
35
98
|
* `ANTHROPIC_API_KEY` fails the first run instead, with an `UnrecoverableError`.
|
|
36
99
|
*/
|
|
37
|
-
export declare function createLLMAgent(provider: AgentProvider, { model, reasoningEffort, systemPrompt, tools, autoApprove, logger, }: CreateLLMAgentOptions): Agent;
|
|
100
|
+
export declare function createLLMAgent(provider: AgentProvider, { model, reasoningEffort, systemPrompt, tools, workspace, summarizeUndone, maxSteps, autoApprove, logger, }: CreateLLMAgentOptions): Agent;
|
|
101
|
+
/**
|
|
102
|
+
* The file tools for an agent from `createLLMAgent` on `provider`, over `workspace`, in the
|
|
103
|
+
* provider's own format:
|
|
104
|
+
*
|
|
105
|
+
* - OpenAI: `listFiles`, `searchText`, `readFile` and its native `apply_patch`;
|
|
106
|
+
* - Anthropic: `listFiles`, `searchText`, its native text editor, whose `view` reads, and
|
|
107
|
+
* `delete_file`.
|
|
108
|
+
*
|
|
109
|
+
* Every one goes through the workspace's access policy: a path outside the roots, a secret,
|
|
110
|
+
* a file `.gitignore` excludes or the harness protects is refused. A change is made only in a
|
|
111
|
+
* `write` root, only from a version the agent read, and is recorded in the workspace's history,
|
|
112
|
+
* so it can be undone. Without a `write` root the tools only read. Give the agent the same
|
|
113
|
+
* `workspace`, so it follows what the history undoes.
|
|
114
|
+
*
|
|
115
|
+
* With `commands`, they include `runCommand`, which runs one of them by name, with only the
|
|
116
|
+
* parameters it declares and never through a shell. Each run asks the approver, showing the
|
|
117
|
+
* program exactly as it will run, and gets a small environment without the provider keys. The
|
|
118
|
+
* files of the `write` roots are captured before it and compared after, so what it changed is
|
|
119
|
+
* in the history too. It is trusted execution, not a sandbox: a command can reach whatever
|
|
120
|
+
* this user can. Not supported on Windows yet.
|
|
121
|
+
*
|
|
122
|
+
* @throws {InvalidAgentConfigError} for an unknown provider, a workspace with no root, or an
|
|
123
|
+
* invalid command, including one whose `cwd` is not in a `write` root, or any on Windows.
|
|
124
|
+
* @throws {UnrecoverableError} when no program can read the first root.
|
|
125
|
+
*/
|
|
126
|
+
export declare function createFileTools(provider: AgentProvider, workspace: WorkspaceOptions, { logger, commands }?: {
|
|
127
|
+
logger?: ILogger;
|
|
128
|
+
commands?: Commands;
|
|
129
|
+
}): Promise<FileTool[]>;
|
|
38
130
|
/**
|
|
39
|
-
* Builds the planner → executor → reviewer workflow with
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
131
|
+
* Builds the planner → executor → reviewer workflow with every role on the model APIs, each
|
|
132
|
+
* holding its role's instructions, or `systemPrompts`, as its system prompt, followed by the
|
|
133
|
+
* workspace's roots.
|
|
134
|
+
*
|
|
135
|
+
* - The planner and the reviewer read: `listFiles`, `searchText`, `readFile`, and
|
|
136
|
+
* `showChanges`, the current task's changes as far as each may read them.
|
|
137
|
+
* - The executor gets its provider's file tools (see `createFileTools`), and changes files only
|
|
138
|
+
* in a `write` root; `autoApprove` reaches only it.
|
|
139
|
+
* - Each role has its own step limit: `maxSteps`, 80 for the executor and 65 for the others by
|
|
140
|
+
* default.
|
|
141
|
+
* - Each role gets `runCommand` with the `commands` whose `roles` name it, the executor by
|
|
142
|
+
* default. A command asks the approver in any role, and its files are captured around it.
|
|
143
|
+
* - The executor is told to finish only once the tests and checks pass, and the reviewer that
|
|
144
|
+
* they passed, when the executor has a command; without one, both are told the tests were
|
|
145
|
+
* not run, and the reviewer judges from the code whether they would pass.
|
|
146
|
+
*
|
|
147
|
+
* One run of the orchestrator is one run of the workspace's history, every round and retry
|
|
148
|
+
* included, and its `AgentResponse` names it. Every role follows what the user undoes.
|
|
149
|
+
* Asynchronous because opening the workspace is.
|
|
150
|
+
*
|
|
151
|
+
* @throws {InvalidAgentConfigError} for an unknown provider, or a workspace with no root.
|
|
47
152
|
* @throws {UnrecoverableError} when a role cannot be set up, for example because
|
|
48
|
-
* `OPENAI_API_KEY`
|
|
49
|
-
* repository.
|
|
153
|
+
* `OPENAI_API_KEY` is missing, or when no program can read the repository.
|
|
50
154
|
*/
|
|
51
|
-
export declare function createLLMOrchestrator({ provider, autoApprove, systemPrompts, logger, }?:
|
|
155
|
+
export declare function createLLMOrchestrator({ provider, autoApprove, systemPrompts, workspace, maxSteps, commands, summarizeUndone, logger, }?: CreateLLMOrchestratorOptions): Promise<Agent>;
|
|
156
|
+
export {};
|
|
@@ -1,98 +1,325 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
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 {
|
|
6
|
-
import { executorInstructions, OrchestratorAgent, plannerInstructions, reviewerInstructions, } from '#src/orchestration/domain/model/orchestratorAgent';
|
|
3
|
+
import { ClaudeLLMClient, claudeLLMModels, claudeLLMReasoningEfforts, } from '#src/llm/infrastructure/claudeLLMClient';
|
|
4
|
+
import { OpenAILLMClient, openAIModels, openAIReasoningEfforts, } from '#src/llm/infrastructure/openAILLMClient';
|
|
5
|
+
import { executorInstructions, executorInstructionsWithoutCommands, OrchestratorAgent, plannerInstructions, reviewerInstructions, reviewerInstructionsWithoutCommands, } from '#src/orchestration/domain/model/orchestratorAgent';
|
|
7
6
|
import { ReviewerDecisionValidator } from '#src/orchestration/infrastructure/model/reviewerDecisionValidator';
|
|
8
7
|
import { RetryingAgent } from '#src/retry/domain/model/retryingAgent';
|
|
8
|
+
import { InvalidAgentConfigError } from '#src/shared/domain/errors';
|
|
9
|
+
import { isOneOf } from '#src/shared/domain/isOneOf';
|
|
9
10
|
import { Logger } from '#src/shared/infrastructure/logger';
|
|
10
|
-
import {
|
|
11
|
+
import { commandsFor, validateCommands, } from '#src/tools/domain/commands';
|
|
12
|
+
import { commandToolFor, fileToolsFor, openStore, openWorkspace, stateDirectoryOf, } from '#src/tools/infrastructure/fileTools';
|
|
13
|
+
import { createShowChangesTool } from '#src/tools/infrastructure/showChangesTool';
|
|
14
|
+
import { createTrackedReadFile } from '#src/tools/infrastructure/trackedReadFile';
|
|
11
15
|
import { createWorkspaceTools } from '#src/tools/infrastructure/workspaceTools';
|
|
12
|
-
import {
|
|
16
|
+
import { isAgentProvider, orchestratorRolesFor } from "./agentFactory.js";
|
|
13
17
|
import { agentProviders, } from "./types.js";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
/** The models each provider's model API accepts. */
|
|
19
|
+
export const llmModels = {
|
|
20
|
+
anthropic: claudeLLMModels,
|
|
21
|
+
openai: openAIModels,
|
|
22
|
+
};
|
|
23
|
+
/** The reasoning efforts each provider's model API accepts. */
|
|
24
|
+
export const llmReasoningEfforts = {
|
|
25
|
+
anthropic: claudeLLMReasoningEfforts,
|
|
26
|
+
openai: openAIReasoningEfforts,
|
|
27
|
+
};
|
|
28
|
+
/** Narrows a model to one accepted by the selected provider's model API. */
|
|
29
|
+
export function isLLMModelOf(provider, value) {
|
|
30
|
+
return isOneOf(llmModels[provider], value);
|
|
31
|
+
}
|
|
32
|
+
/** Narrows a reasoning effort to one accepted by the selected provider's model API. */
|
|
33
|
+
export function isLLMReasoningEffortOf(provider, value) {
|
|
34
|
+
return isOneOf(llmReasoningEfforts[provider], value);
|
|
35
|
+
}
|
|
36
|
+
// The executor reads, edits and checks, so it needs more calls than the roles that only read.
|
|
37
|
+
const defaultRoleSteps = {
|
|
38
|
+
planner: 65,
|
|
39
|
+
executor: 80,
|
|
40
|
+
reviewer: 65,
|
|
17
41
|
};
|
|
18
42
|
/**
|
|
19
|
-
*
|
|
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. Each tool judges the risk of each call; a
|
|
22
|
-
* `destructive` one runs only when the run's `approve` allows it, or with `autoApprove`.
|
|
43
|
+
* Each role's step limit, from one number for all of them or a number per role.
|
|
23
44
|
*
|
|
24
|
-
* @throws {InvalidAgentConfigError} for
|
|
25
|
-
*
|
|
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`.
|
|
45
|
+
* @throws {InvalidAgentConfigError} for a value that is neither, and for a role that does not
|
|
46
|
+
* exist, which is a typo that would otherwise leave that role on its default without a word.
|
|
29
47
|
*/
|
|
30
|
-
|
|
31
|
-
|
|
48
|
+
function stepsPerRole(maxSteps) {
|
|
49
|
+
if (maxSteps === undefined)
|
|
50
|
+
return defaultRoleSteps;
|
|
51
|
+
if (typeof maxSteps === 'number') {
|
|
52
|
+
return { planner: maxSteps, executor: maxSteps, reviewer: maxSteps };
|
|
53
|
+
}
|
|
54
|
+
// Untyped input, such as the CLI's configuration file, can hold `null` or a list here.
|
|
55
|
+
if (typeof maxSteps !== 'object' || maxSteps === null || Array.isArray(maxSteps)) {
|
|
56
|
+
throw new InvalidAgentConfigError('maxSteps must be a number or an object of steps per role, such as { "executor": 120 }');
|
|
57
|
+
}
|
|
58
|
+
const unknown = Object.keys(maxSteps).filter(role => !(role in defaultRoleSteps));
|
|
59
|
+
if (unknown.length > 0) {
|
|
60
|
+
throw new InvalidAgentConfigError(`maxSteps names roles that do not exist: ${unknown.join(', ')}; expected planner, executor or reviewer`);
|
|
61
|
+
}
|
|
62
|
+
// A role given as `undefined`, which untyped input can do, keeps its default.
|
|
63
|
+
const given = Object.entries(maxSteps).filter(([, steps]) => steps !== undefined);
|
|
64
|
+
return { ...defaultRoleSteps, ...Object.fromEntries(given) };
|
|
65
|
+
}
|
|
66
|
+
const defaultLLMAgentModels = {
|
|
67
|
+
anthropic: 'sonnet',
|
|
68
|
+
openai: 'gpt-5.6-luna',
|
|
69
|
+
};
|
|
70
|
+
function unknownProvider(provider) {
|
|
71
|
+
return new InvalidAgentConfigError(`"${String(provider)}" is not an agent provider; expected one of: ${agentProviders.join(', ')}`);
|
|
72
|
+
}
|
|
73
|
+
function llmClientFor(provider, { model, reasoningEffort, systemPrompt, logger, }) {
|
|
32
74
|
switch (provider) {
|
|
33
75
|
case 'anthropic':
|
|
34
|
-
|
|
76
|
+
return new ClaudeLLMClient({
|
|
35
77
|
model: model ?? defaultLLMAgentModels.anthropic,
|
|
36
78
|
reasoningEffort,
|
|
37
79
|
systemPrompt,
|
|
38
80
|
logger,
|
|
39
81
|
});
|
|
40
|
-
break;
|
|
41
82
|
case 'openai':
|
|
42
|
-
|
|
83
|
+
return new OpenAILLMClient({
|
|
43
84
|
model: model ?? defaultLLMAgentModels.openai,
|
|
44
85
|
reasoningEffort,
|
|
45
86
|
systemPrompt,
|
|
46
87
|
logger,
|
|
47
88
|
});
|
|
48
|
-
|
|
49
|
-
default: {
|
|
89
|
+
default:
|
|
50
90
|
// Reachable from untyped input.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
91
|
+
throw unknownProvider(provider);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/** The provider's cheap model, for the summary of what was undone. */
|
|
95
|
+
function summarizerFor(provider, logger) {
|
|
96
|
+
return provider === 'anthropic'
|
|
97
|
+
? new ClaudeLLMClient({ model: 'haiku', systemPrompt: '', logger })
|
|
98
|
+
: new OpenAILLMClient({
|
|
99
|
+
model: 'gpt-5.6-luna',
|
|
100
|
+
reasoningEffort: 'high',
|
|
101
|
+
systemPrompt: '',
|
|
102
|
+
logger,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* The workspace's history, opened on first use, so a factory that builds an agent stays
|
|
107
|
+
* synchronous. Every agent and every file tool of one workspace reads the same files on disk.
|
|
108
|
+
*/
|
|
109
|
+
function historyOf(workspace) {
|
|
110
|
+
// Checked now: an agent with no root to follow is a setup error, not a first-run one.
|
|
111
|
+
if (workspace.roots.length === 0) {
|
|
112
|
+
throw new InvalidAgentConfigError('A workspace needs at least one root');
|
|
54
113
|
}
|
|
114
|
+
let store;
|
|
115
|
+
const open = () => {
|
|
116
|
+
store ??= stateDirectoryOf(workspace)
|
|
117
|
+
.then(directory => openStore(workspace, directory))
|
|
118
|
+
.catch((error) => {
|
|
119
|
+
// Not kept: the next run tries again, as one whose disk was full for a moment.
|
|
120
|
+
store = undefined;
|
|
121
|
+
throw error;
|
|
122
|
+
});
|
|
123
|
+
return store;
|
|
124
|
+
};
|
|
125
|
+
return {
|
|
126
|
+
listRuns: async () => (await open()).listRuns(),
|
|
127
|
+
listRevisions: async () => (await open()).listRevisions(),
|
|
128
|
+
readRun: async (runId) => (await open()).readRun(runId),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* What the model is told about the workspace after its instructions: the roots, which it may
|
|
133
|
+
* change, and how to name a file. A host path in an error sends the model looking for it, so
|
|
134
|
+
* the real root is given here once.
|
|
135
|
+
*/
|
|
136
|
+
function workspaceGuidance(roots) {
|
|
137
|
+
const [first, ...others] = roots.map(root => ({ ...root, path: resolve(root.path) }));
|
|
138
|
+
if (!first)
|
|
139
|
+
return '';
|
|
140
|
+
const described = [first, ...others].map(({ path, access }) => `- ${path}${access === 'write' ? '' : ' (read only)'}`);
|
|
141
|
+
return [
|
|
142
|
+
'The workspace:',
|
|
143
|
+
...described,
|
|
144
|
+
`Name a file by its path relative to ${first.path}, such as src/index.ts, and a file in another folder of the workspace by its full path. Read a file before you change it.`,
|
|
145
|
+
].join('\n');
|
|
146
|
+
}
|
|
147
|
+
function withGuidance(systemPrompt, guidance) {
|
|
148
|
+
return guidance === '' ? systemPrompt : `${systemPrompt}\n\n${guidance}`;
|
|
149
|
+
}
|
|
150
|
+
/** An `LLMAgent` on `provider`, wrapped in the harness's retry policy. */
|
|
151
|
+
function buildLLMAgent(provider, { model, reasoningEffort, systemPrompt, tools, history, summarizer, maxSteps, autoApprove, logger, }) {
|
|
152
|
+
const llmClient = llmClientFor(provider, { model, reasoningEffort, systemPrompt, logger });
|
|
55
153
|
// LLMAgent records nothing from a failed call, so the original prompt is the whole story.
|
|
56
154
|
return new RetryingAgent({
|
|
57
|
-
inner: new LLMAgent({
|
|
155
|
+
inner: new LLMAgent({
|
|
156
|
+
llmClient,
|
|
157
|
+
tools,
|
|
158
|
+
autoApprove,
|
|
159
|
+
logger,
|
|
160
|
+
...(history ? { history } : {}),
|
|
161
|
+
...(summarizer ? { summarizer } : {}),
|
|
162
|
+
...(maxSteps === undefined ? {} : { maxSteps }),
|
|
163
|
+
}),
|
|
58
164
|
logger,
|
|
59
165
|
noteFailures: false,
|
|
60
166
|
});
|
|
61
167
|
}
|
|
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
168
|
/**
|
|
66
|
-
* Builds
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
169
|
+
* Builds an agent whose conversation MiKode owns, on a provider's model API, already wrapped in
|
|
170
|
+
* the harness's retry policy. It needs the provider's API key and bills per token, where
|
|
171
|
+
* `createAgent` uses the Agent SDK's login. Each tool judges the risk of each call; a
|
|
172
|
+
* `destructive` one runs only when the run's `approve` allows it, or with `autoApprove`.
|
|
173
|
+
*
|
|
174
|
+
* @throws {InvalidAgentConfigError} for an unknown provider, a model or reasoning effort its
|
|
175
|
+
* client does not support, or two tools that share a name.
|
|
176
|
+
* @throws {UnrecoverableError} when the client cannot be set up, for example because
|
|
177
|
+
* `OPENAI_API_KEY` is missing. The Anthropic SDK resolves credentials lazily, so a missing
|
|
178
|
+
* `ANTHROPIC_API_KEY` fails the first run instead, with an `UnrecoverableError`.
|
|
179
|
+
*/
|
|
180
|
+
export function createLLMAgent(provider, { model, reasoningEffort, systemPrompt, tools = [], workspace, summarizeUndone = true, maxSteps, autoApprove, logger = new Logger(), }) {
|
|
181
|
+
return buildLLMAgent(provider, {
|
|
182
|
+
model,
|
|
183
|
+
reasoningEffort,
|
|
184
|
+
systemPrompt: workspace
|
|
185
|
+
? withGuidance(systemPrompt, workspaceGuidance(workspace.roots))
|
|
186
|
+
: systemPrompt,
|
|
187
|
+
// A file tool is a harness tool under its public name.
|
|
188
|
+
tools: tools,
|
|
189
|
+
...(workspace ? { history: historyOf(workspace) } : {}),
|
|
190
|
+
...(workspace && summarizeUndone ? { summarizer: summarizerFor(provider, logger) } : {}),
|
|
191
|
+
maxSteps,
|
|
192
|
+
autoApprove,
|
|
193
|
+
logger,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* The file tools for an agent from `createLLMAgent` on `provider`, over `workspace`, in the
|
|
198
|
+
* provider's own format:
|
|
199
|
+
*
|
|
200
|
+
* - OpenAI: `listFiles`, `searchText`, `readFile` and its native `apply_patch`;
|
|
201
|
+
* - Anthropic: `listFiles`, `searchText`, its native text editor, whose `view` reads, and
|
|
202
|
+
* `delete_file`.
|
|
203
|
+
*
|
|
204
|
+
* Every one goes through the workspace's access policy: a path outside the roots, a secret,
|
|
205
|
+
* a file `.gitignore` excludes or the harness protects is refused. A change is made only in a
|
|
206
|
+
* `write` root, only from a version the agent read, and is recorded in the workspace's history,
|
|
207
|
+
* so it can be undone. Without a `write` root the tools only read. Give the agent the same
|
|
208
|
+
* `workspace`, so it follows what the history undoes.
|
|
209
|
+
*
|
|
210
|
+
* With `commands`, they include `runCommand`, which runs one of them by name, with only the
|
|
211
|
+
* parameters it declares and never through a shell. Each run asks the approver, showing the
|
|
212
|
+
* program exactly as it will run, and gets a small environment without the provider keys. The
|
|
213
|
+
* files of the `write` roots are captured before it and compared after, so what it changed is
|
|
214
|
+
* in the history too. It is trusted execution, not a sandbox: a command can reach whatever
|
|
215
|
+
* this user can. Not supported on Windows yet.
|
|
72
216
|
*
|
|
73
|
-
* @throws {InvalidAgentConfigError} for an unknown provider
|
|
217
|
+
* @throws {InvalidAgentConfigError} for an unknown provider, a workspace with no root, or an
|
|
218
|
+
* invalid command, including one whose `cwd` is not in a `write` root, or any on Windows.
|
|
219
|
+
* @throws {UnrecoverableError} when no program can read the first root.
|
|
220
|
+
*/
|
|
221
|
+
export async function createFileTools(provider, workspace, { logger = new Logger(), commands } = {}) {
|
|
222
|
+
// Reachable from untyped input.
|
|
223
|
+
if (!isAgentProvider(provider))
|
|
224
|
+
throw unknownProvider(provider);
|
|
225
|
+
if (commands !== undefined)
|
|
226
|
+
validateCommands(commands);
|
|
227
|
+
const opened = await openWorkspace(workspace, logger);
|
|
228
|
+
return (await executorTools(provider, opened, commands));
|
|
229
|
+
}
|
|
230
|
+
/** The executor's file tools, and `runCommand` when there are commands. */
|
|
231
|
+
async function executorTools(provider, workspace, commands) {
|
|
232
|
+
const tools = fileToolsFor(provider, workspace);
|
|
233
|
+
// Every command may belong to other roles, leaving none for this one.
|
|
234
|
+
if (commands === undefined || Object.keys(commands).length === 0)
|
|
235
|
+
return tools;
|
|
236
|
+
return [...tools, await commandToolFor(workspace, commands)];
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* What the planner and the reviewer read with: no edits, the current task's changes, and the
|
|
240
|
+
* commands given to their role.
|
|
241
|
+
*/
|
|
242
|
+
async function readingTools(workspace, role, commands) {
|
|
243
|
+
const { read, policy, store } = workspace;
|
|
244
|
+
const finding = createWorkspaceTools(read).filter(tool => tool.name !== 'readFile');
|
|
245
|
+
const tools = [
|
|
246
|
+
...finding,
|
|
247
|
+
createTrackedReadFile(policy),
|
|
248
|
+
createShowChangesTool({ store, policy }),
|
|
249
|
+
];
|
|
250
|
+
const own = commands && commandsFor(commands, role);
|
|
251
|
+
if (!own || Object.keys(own).length === 0)
|
|
252
|
+
return tools;
|
|
253
|
+
return [...tools, await commandToolFor(workspace, own)];
|
|
254
|
+
}
|
|
255
|
+
// Sent after the workspace's roots, so it stays short: it only says how to find one's way
|
|
256
|
+
// around the repository without reading all of it.
|
|
257
|
+
const repositoryGuidance = 'Start with AGENTS.md and the architecture document it links to learn where things live, then read only the files your task needs. When you know you need several files, or several searches, ask for all of them in one answer rather than one at a time: each answer is a step, and steps are limited.';
|
|
258
|
+
/**
|
|
259
|
+
* Builds the planner → executor → reviewer workflow with every role on the model APIs, each
|
|
260
|
+
* holding its role's instructions, or `systemPrompts`, as its system prompt, followed by the
|
|
261
|
+
* workspace's roots.
|
|
262
|
+
*
|
|
263
|
+
* - The planner and the reviewer read: `listFiles`, `searchText`, `readFile`, and
|
|
264
|
+
* `showChanges`, the current task's changes as far as each may read them.
|
|
265
|
+
* - The executor gets its provider's file tools (see `createFileTools`), and changes files only
|
|
266
|
+
* in a `write` root; `autoApprove` reaches only it.
|
|
267
|
+
* - Each role has its own step limit: `maxSteps`, 80 for the executor and 65 for the others by
|
|
268
|
+
* default.
|
|
269
|
+
* - Each role gets `runCommand` with the `commands` whose `roles` name it, the executor by
|
|
270
|
+
* default. A command asks the approver in any role, and its files are captured around it.
|
|
271
|
+
* - The executor is told to finish only once the tests and checks pass, and the reviewer that
|
|
272
|
+
* they passed, when the executor has a command; without one, both are told the tests were
|
|
273
|
+
* not run, and the reviewer judges from the code whether they would pass.
|
|
274
|
+
*
|
|
275
|
+
* One run of the orchestrator is one run of the workspace's history, every round and retry
|
|
276
|
+
* included, and its `AgentResponse` names it. Every role follows what the user undoes.
|
|
277
|
+
* Asynchronous because opening the workspace is.
|
|
278
|
+
*
|
|
279
|
+
* @throws {InvalidAgentConfigError} for an unknown provider, or a workspace with no root.
|
|
74
280
|
* @throws {UnrecoverableError} when a role cannot be set up, for example because
|
|
75
|
-
* `OPENAI_API_KEY`
|
|
76
|
-
* repository.
|
|
281
|
+
* `OPENAI_API_KEY` is missing, or when no program can read the repository.
|
|
77
282
|
*/
|
|
78
|
-
export async function createLLMOrchestrator({ provider, autoApprove, systemPrompts = {}, logger = new Logger(), } = {}) {
|
|
283
|
+
export async function createLLMOrchestrator({ provider, autoApprove, systemPrompts = {}, workspace = { roots: [{ path: process.cwd(), access: 'read' }] }, maxSteps, commands, summarizeUndone = true, logger = new Logger(), } = {}) {
|
|
79
284
|
const roles = orchestratorRolesFor(provider);
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
const
|
|
285
|
+
const steps = stepsPerRole(maxSteps);
|
|
286
|
+
const opened = await openWorkspace(workspace, logger);
|
|
287
|
+
const guidance = workspaceGuidance(workspace.roots);
|
|
288
|
+
const summarizer = summarizeUndone ? summarizerFor(roles.planner.provider, logger) : undefined;
|
|
289
|
+
const shared = { history: opened.store, ...(summarizer ? { summarizer } : {}), logger };
|
|
290
|
+
// A caller's own prompt replaces our instructions and guidance, not the workspace: the model
|
|
291
|
+
// needs the roots to name a file, as `createLLMAgent` gives them.
|
|
292
|
+
const prompt = (own, instructions) => own === undefined
|
|
293
|
+
? withGuidance(instructions, `${guidance}\n${repositoryGuidance}`)
|
|
294
|
+
: withGuidance(own, guidance);
|
|
295
|
+
// Every command, checked once, before any role takes its share.
|
|
296
|
+
if (commands !== undefined)
|
|
297
|
+
validateCommands(commands);
|
|
298
|
+
const reader = async ({ provider, model, reasoningEffort }, role, systemPrompt) => buildLLMAgent(provider, {
|
|
299
|
+
model,
|
|
300
|
+
reasoningEffort,
|
|
301
|
+
systemPrompt,
|
|
302
|
+
tools: await readingTools(opened, role, commands),
|
|
303
|
+
maxSteps: steps[role],
|
|
304
|
+
...shared,
|
|
305
|
+
});
|
|
306
|
+
const { executor } = roles;
|
|
307
|
+
const executorToolset = await executorTools(executor.provider, opened, commands && commandsFor(commands, 'executor'));
|
|
308
|
+
// The executor and the reviewer are told whether the tests were run, which only an executor
|
|
309
|
+
// with a command can do.
|
|
310
|
+
const executorRuns = executorToolset.some(tool => tool.name === 'runCommand');
|
|
84
311
|
return new OrchestratorAgent({
|
|
85
|
-
plannerAgent:
|
|
86
|
-
executorAgent:
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
312
|
+
plannerAgent: await reader(roles.planner, 'planner', prompt(systemPrompts.planner, plannerInstructions)),
|
|
313
|
+
executorAgent: buildLLMAgent(executor.provider, {
|
|
314
|
+
model: executor.model,
|
|
315
|
+
reasoningEffort: executor.reasoningEffort,
|
|
316
|
+
systemPrompt: prompt(systemPrompts.executor, executorRuns ? executorInstructions : executorInstructionsWithoutCommands),
|
|
317
|
+
tools: executorToolset,
|
|
318
|
+
maxSteps: steps.executor,
|
|
319
|
+
autoApprove,
|
|
320
|
+
...shared,
|
|
94
321
|
}),
|
|
95
|
-
reviewerAgent:
|
|
322
|
+
reviewerAgent: await reader(roles.reviewer, 'reviewer', prompt(systemPrompts.reviewer, executorRuns ? reviewerInstructions : reviewerInstructionsWithoutCommands)),
|
|
96
323
|
reviewerDecisionValidator: new ReviewerDecisionValidator(),
|
|
97
324
|
logger,
|
|
98
325
|
});
|