@mikode13/harness 9.2.0 → 9.3.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 CHANGED
@@ -595,8 +595,9 @@ Configure only commands you would run yourself on that repository.
595
595
  ### Formatting what the agent writes
596
596
 
597
597
  With `formatter`, `createFileTools` and `createLLMOrchestrator` run your formatter on every
598
- file an edit creates or changes, right after writing it. The model reads the file as the
599
- formatter left it, so no later edit fails because the formatter moved its lines:
598
+ file an edit creates or changes, right after writing it. The edit's response shows the lines
599
+ the formatter rewrote, as it left them, and the model must read a file the formatter rewrote
600
+ again before it edits it, so it never edits on from the lines it sent:
600
601
 
601
602
  ```ts
602
603
  const tools = await createFileTools('openai', workspace, {
@@ -151,8 +151,8 @@ export declare function createLLMAgent(provider: AgentProvider, { model, reasoni
151
151
  * this user can. Not supported on Windows yet.
152
152
  *
153
153
  * With `formatter`, every file an edit creates or changes is formatted right after, its path
154
- * the formatter's last argument, and the edit returns the file as the formatter left it. Like a
155
- * command it runs without a shell, in a small environment, and what it changes in that file is
154
+ * the formatter's last argument. The edit shows the lines the formatter rewrote, and the model
155
+ * reads a file it rewrote again before editing it. Like a command it runs without a shell, in a small environment, and what it changes in that file is
156
156
  * in the history. When it fails, the file stays as the edit wrote it, and the model is told
157
157
  * why. Not supported on Windows yet.
158
158
  *
@@ -233,8 +233,8 @@ export function createLLMAgent(provider, { model, reasoningEffort, systemPrompt,
233
233
  * this user can. Not supported on Windows yet.
234
234
  *
235
235
  * With `formatter`, every file an edit creates or changes is formatted right after, its path
236
- * the formatter's last argument, and the edit returns the file as the formatter left it. Like a
237
- * command it runs without a shell, in a small environment, and what it changes in that file is
236
+ * the formatter's last argument. The edit shows the lines the formatter rewrote, and the model
237
+ * reads a file it rewrote again before editing it. Like a command it runs without a shell, in a small environment, and what it changes in that file is
238
238
  * in the history. When it fails, the file stays as the edit wrote it, and the model is told
239
239
  * why. Not supported on Windows yet.
240
240
  *
@@ -1 +1 @@
1
- {"version":3,"file":"agentLLMFactory.js","sourceRoot":"","sources":["../../../src/factory/infrastructure/agentLLMFactory.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolve } from 'node:path';\nimport type { Agent } from '#src/agent/domain/agent';\nimport type { Transcript, TranscriptCallback } from '#src/agent/domain/transcript';\nimport type { HistoryReader } from '#src/engines/domain/model/historyFollower';\nimport { LLMAgent } from '#src/engines/domain/model/llmAgent';\nimport type { LLMClient } from '#src/llm/domain/llm';\nimport {\n\tClaudeLLMClient,\n\tclaudeLLMModels,\n\tclaudeLLMReasoningEfforts,\n} from '#src/llm/infrastructure/claudeLLMClient';\nimport {\n\tOpenAILLMClient,\n\topenAIModels,\n\topenAIReasoningEfforts,\n} from '#src/llm/infrastructure/openAILLMClient';\nimport {\n\texecutorInstructions,\n\texecutorInstructionsWithoutCommands,\n\tOrchestratorAgent,\n\tplannerInstructions,\n\treviewerInstructions,\n\treviewerInstructionsWithoutCommands,\n} from '#src/orchestration/domain/model/orchestratorAgent';\nimport { ReviewerDecisionValidator } from '#src/orchestration/infrastructure/model/reviewerDecisionValidator';\nimport type { RecoveryStore } from '#src/recovery/domain/recoveryStore';\nimport { RetryingAgent } from '#src/retry/domain/model/retryingAgent';\nimport { InvalidAgentConfigError } from '#src/shared/domain/errors';\nimport { isOneOf } from '#src/shared/domain/isOneOf';\nimport type { ILogger } from '#src/shared/domain/logger';\nimport { Logger } from '#src/shared/infrastructure/logger';\nimport type { WorkspaceRoot } from '#src/tools/domain/accessPolicy';\nimport {\n\ttype CommandRole,\n\ttype Commands,\n\tcommandsFor,\n\ttype Formatter,\n\tvalidateCommands,\n\tvalidateFormatter,\n} from '#src/tools/domain/commands';\nimport type { AgentTool } from '#src/tools/domain/preparedCall';\nimport type { Tool } from '#src/tools/domain/tool';\nimport {\n\tcommandToolFor,\n\ttype FileTool,\n\tfileToolsFor,\n\ttype OpenedWorkspace,\n\topenStore,\n\topenWorkspace,\n\tstateDirectoryOf,\n\ttype WorkspaceOptions,\n} from '#src/tools/infrastructure/fileTools';\nimport { createExploreTool } from '#src/tools/infrastructure/exploreTool';\nimport { createShowChangesTool } from '#src/tools/infrastructure/showChangesTool';\nimport {\n\tcreateTrackedReadFile,\n\tcreateTrackedReadFiles,\n} from '#src/tools/infrastructure/trackedReadFile';\nimport { createWorkspaceTools } from '#src/tools/infrastructure/workspaceTools';\nimport { isAgentProvider, 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\n/** The models each provider's model API accepts. */\nexport const llmModels = {\n\tanthropic: claudeLLMModels,\n\topenai: openAIModels,\n} as const satisfies Readonly<Record<AgentProvider, readonly AgentModel[]>>;\n\n/** The reasoning efforts each provider's model API accepts. */\nexport const llmReasoningEfforts = {\n\tanthropic: claudeLLMReasoningEfforts,\n\topenai: openAIReasoningEfforts,\n} as const satisfies Readonly<Record<AgentProvider, readonly ReasoningEffort[]>>;\n\n/** Narrows a model to one accepted by the selected provider's model API. */\nexport function isLLMModelOf<Provider extends AgentProvider>(\n\tprovider: Provider,\n\tvalue: string,\n): value is (typeof llmModels)[Provider][number] {\n\treturn isOneOf(llmModels[provider], value);\n}\n\n/** Narrows a reasoning effort to one accepted by the selected provider's model API. */\nexport function isLLMReasoningEffortOf<Provider extends AgentProvider>(\n\tprovider: Provider,\n\tvalue: string,\n): value is (typeof llmReasoningEfforts)[Provider][number] {\n\treturn isOneOf(llmReasoningEfforts[provider], value);\n}\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: the consumer's own, and those of `createFileTools`. Defaults to none. */\n\ttools?: (Tool | FileTool)[];\n\t/**\n\t * The folders the agent works on, the same as given to `createFileTools`. With it, the agent\n\t * follows the workspace's history: after the user undoes runs, its next run starts from the\n\t * conversation as it was, and is told what was undone. The system prompt is followed by the\n\t * roots and how to name a file in them.\n\t */\n\tworkspace?: WorkspaceOptions;\n\t/**\n\t * With `workspace`, a summary of what was undone, from the provider's cheap model, in place\n\t * of the undone prompts. That is Claude Haiku 5.5 or `gpt-5.6-luna`, at high effort, and the\n\t * call is billed with the run's tokens. Defaults to `true`.\n\t */\n\tsummarizeUndone?: boolean;\n\t/**\n\t * How many calls to the model one run may make before it fails. Defaults to 65, with no upper\n\t * bound. A run that asks for exactly the same tool calls 5 times in a row fails before that.\n\t */\n\tmaxSteps?: number;\n\t/**\n\t * The input tokens at which the provider compacts the conversation on its side: it replaces\n\t * every turn before that call with its own summary, which only its model reads back. The\n\t * conversation keeps every original turn. Without it, the conversation is never compacted.\n\t * At least 50,000 on Anthropic.\n\t */\n\tcompactAt?: number;\n\t/**\n\t * Runs every tool call without asking, `destructive` ones included, for CI where no one can\n\t * answer. Without it, a `destructive` call goes to the run's `approve`, or is denied.\n\t */\n\tautoApprove?: boolean;\n\t/** Defaults to warnings on stderr. */\n\tlogger?: ILogger;\n\t/**\n\t * Receives what the model saw in each run once the run ended, completed, failed or\n\t * cancelled, for debugging. See `Transcript`. Without it, nothing is kept.\n\t */\n\tonTranscript?: TranscriptCallback;\n}\n\n/** A limit per role of `createLLMOrchestrator`; a role left out keeps its default. */\nexport type RoleSteps = Partial<Record<OrchestratorRole, number>>;\n\n/** A number of tokens per role of `createLLMOrchestrator`; a role left out keeps its default. */\nexport type RoleTokens = Partial<Record<OrchestratorRole, number>>;\n\n// The same three roles a command can be given to.\ntype OrchestratorRole = CommandRole;\n\n// The executor reads, edits and checks, so it needs more calls than the roles that only read.\nconst defaultRoleSteps: Record<OrchestratorRole, number> = {\n\tplanner: 65,\n\texecutor: 80,\n\treviewer: 65,\n};\n\n// No role compacts unless asked to.\nconst defaultRoleCompaction: Record<OrchestratorRole, number | undefined> = {\n\tplanner: undefined,\n\texecutor: undefined,\n\treviewer: undefined,\n};\n\n/**\n * Each role's value of the option `name`, from one number for all of them or a number per role.\n * Each number is checked by whatever takes it.\n *\n * @throws {InvalidAgentConfigError} for a value that is neither, and for a role that does not\n * exist, which is a typo that would otherwise leave that role on its default without a word.\n */\nfunction perRole<T extends number | undefined>(\n\tname: string,\n\tvalue: unknown,\n\tdefaults: Record<OrchestratorRole, T>,\n\t{ unit, example }: { unit: string; example: string },\n): Record<OrchestratorRole, T | number> {\n\tif (value === undefined) return defaults;\n\tif (typeof value === 'number') return { planner: value, executor: value, reviewer: value };\n\t// Untyped input, such as the CLI's configuration file, can hold `null` or a list here.\n\tif (typeof value !== 'object' || value === null || Array.isArray(value)) {\n\t\tthrow new InvalidAgentConfigError(\n\t\t\t`${name} must be a number or an object of ${unit} per role, such as ${example}`,\n\t\t);\n\t}\n\tconst unknown = Object.keys(value).filter(role => !(role in defaults));\n\tif (unknown.length > 0) {\n\t\tthrow new InvalidAgentConfigError(\n\t\t\t`${name} names roles that do not exist: ${unknown.join(', ')}; expected planner, executor or reviewer`,\n\t\t);\n\t}\n\t// A role given as `undefined`, which untyped input can do, keeps its default.\n\tconst given = Object.entries(value as Record<string, unknown>).filter(\n\t\t([, each]) => each !== undefined,\n\t);\n\treturn { ...defaults, ...Object.fromEntries(given) };\n}\n\nexport interface CreateLLMOrchestratorOptions extends CreateOrchestratorOptions {\n\t/**\n\t * The folders the agents work on. The executor changes files only in a `write` root, every\n\t * change recorded in the workspace's history. Defaults to the current working directory,\n\t * read only.\n\t */\n\tworkspace?: WorkspaceOptions;\n\t/**\n\t * How many calls to the model one run of each role may make: one number for every role, or a\n\t * number per role. Defaults to 80 for the executor and 65 for the planner and the reviewer,\n\t * with no upper bound. A run that asks for exactly the same tool calls 5 times in a row fails\n\t * before its limit.\n\t */\n\tmaxSteps?: number | RoleSteps;\n\t/**\n\t * The input tokens at which each role's provider compacts its conversation, as\n\t * `CreateLLMAgentOptions.compactAt`: one number for every role, or a number per role. Without\n\t * it, no role compacts.\n\t */\n\tcompactAt?: number | RoleTokens;\n\t/**\n\t * Commands the roles may run, such as the project's tests, through `runCommand`. Each goes to\n\t * the roles its `roles` names, the executor by default. See `createFileTools`. Each needs a\n\t * `write` root to run in.\n\t */\n\tcommands?: Commands;\n\t/**\n\t * A program the executor's edits run on each file they create or change, such as the\n\t * project's formatter. See `createFileTools`. It needs a `write` root to run in.\n\t */\n\tformatter?: Formatter;\n\t/**\n\t * A summary of what the user undid, from the planner's provider's cheap model, for every\n\t * role. See `CreateLLMAgentOptions.summarizeUndone`. Defaults to `true`.\n\t */\n\tsummarizeUndone?: boolean;\n\t/**\n\t * Receives what the model saw in each run of each role and each explorer, once the run\n\t * ended, for debugging. Each transcript names its role. See `Transcript`.\n\t */\n\tonTranscript?: TranscriptCallback;\n}\n\nconst defaultLLMAgentModels = {\n\tanthropic: 'sonnet',\n\topenai: 'gpt-5.6-luna',\n} as const satisfies Record<AgentProvider, AgentModel>;\n\nfunction unknownProvider(provider: never): InvalidAgentConfigError {\n\treturn new InvalidAgentConfigError(\n\t\t`\"${String(provider)}\" is not an agent provider; expected one of: ${agentProviders.join(', ')}`,\n\t);\n}\n\nfunction llmClientFor(\n\tprovider: AgentProvider,\n\t{\n\t\tmodel,\n\t\treasoningEffort,\n\t\tcompactAt,\n\t\tsystemPrompt,\n\t\tlogger,\n\t}: {\n\t\tmodel?: AgentModel;\n\t\treasoningEffort?: ReasoningEffort;\n\t\tcompactAt?: number | undefined;\n\t\tsystemPrompt: string;\n\t\tlogger: ILogger;\n\t},\n): LLMClient {\n\tswitch (provider) {\n\t\tcase 'anthropic':\n\t\t\treturn new ClaudeLLMClient({\n\t\t\t\tmodel: model ?? defaultLLMAgentModels.anthropic,\n\t\t\t\treasoningEffort,\n\t\t\t\tcompactAt,\n\t\t\t\tsystemPrompt,\n\t\t\t\tlogger,\n\t\t\t});\n\t\tcase 'openai':\n\t\t\treturn new OpenAILLMClient({\n\t\t\t\tmodel: model ?? defaultLLMAgentModels.openai,\n\t\t\t\treasoningEffort,\n\t\t\t\tcompactAt,\n\t\t\t\tsystemPrompt,\n\t\t\t\tlogger,\n\t\t\t});\n\t\tdefault:\n\t\t\t// Reachable from untyped input.\n\t\t\tthrow unknownProvider(provider);\n\t}\n}\n\n/** The provider's cheap model, for the summary of what was undone. */\nfunction summarizerFor(provider: AgentProvider, logger: ILogger): LLMClient {\n\treturn provider === 'anthropic'\n\t\t? new ClaudeLLMClient({ model: 'haiku', reasoningEffort: 'high', systemPrompt: '', logger })\n\t\t: new OpenAILLMClient({\n\t\t\t\tmodel: 'gpt-5.6-luna',\n\t\t\t\treasoningEffort: 'high',\n\t\t\t\tsystemPrompt: '',\n\t\t\t\tlogger,\n\t\t\t});\n}\n\n/**\n * The workspace's history, opened on first use, so a factory that builds an agent stays\n * synchronous. Every agent and every file tool of one workspace reads the same files on disk.\n */\nfunction historyOf(workspace: WorkspaceOptions): HistoryReader {\n\t// Checked now: an agent with no root to follow is a setup error, not a first-run one.\n\tif (workspace.roots.length === 0) {\n\t\tthrow new InvalidAgentConfigError('A workspace needs at least one root');\n\t}\n\tlet store: Promise<RecoveryStore> | undefined;\n\tconst open = () => {\n\t\tstore ??= stateDirectoryOf(workspace)\n\t\t\t.then(directory => openStore(workspace, directory))\n\t\t\t.catch((error: unknown) => {\n\t\t\t\t// Not kept: the next run tries again, as one whose disk was full for a moment.\n\t\t\t\tstore = undefined;\n\t\t\t\tthrow error;\n\t\t\t});\n\t\treturn store;\n\t};\n\treturn {\n\t\tlistRuns: async () => (await open()).listRuns(),\n\t\tlistRevisions: async () => (await open()).listRevisions(),\n\t\treadRun: async runId => (await open()).readRun(runId),\n\t};\n}\n\n/**\n * What the model is told about the workspace after its instructions: the roots, which it may\n * change, and how to name a file. A host path in an error sends the model looking for it, so\n * the real root is given here once.\n */\nfunction workspaceGuidance(roots: WorkspaceRoot[]): string {\n\tconst [first, ...others] = roots.map(root => ({ ...root, path: resolve(root.path) }));\n\tif (!first) return '';\n\tconst described = [first, ...others].map(\n\t\t({ path, access }) => `- ${path}${access === 'write' ? '' : ' (read only)'}`,\n\t);\n\treturn [\n\t\t'The workspace:',\n\t\t...described,\n\t\t`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.`,\n\t].join('\\n');\n}\n\nfunction withGuidance(systemPrompt: string, guidance: string): string {\n\treturn guidance === '' ? systemPrompt : `${systemPrompt}\\n\\n${guidance}`;\n}\n\n/** An `LLMAgent` on `provider`, wrapped in the harness's retry policy. */\nfunction buildLLMAgent(\n\tprovider: AgentProvider,\n\t{\n\t\tmodel,\n\t\treasoningEffort,\n\t\tsystemPrompt,\n\t\ttools,\n\t\thistory,\n\t\tsummarizer,\n\t\tmaxSteps,\n\t\tcompactAt,\n\t\tautoApprove,\n\t\tlogger,\n\t\tonTranscript,\n\t}: {\n\t\tmodel?: AgentModel;\n\t\treasoningEffort?: ReasoningEffort;\n\t\tsystemPrompt: string;\n\t\ttools: AgentTool[];\n\t\thistory?: HistoryReader;\n\t\tsummarizer?: LLMClient;\n\t\tmaxSteps?: number;\n\t\tcompactAt?: number | undefined;\n\t\tautoApprove?: boolean;\n\t\tlogger: ILogger;\n\t\tonTranscript?: TranscriptCallback | undefined;\n\t},\n): Agent {\n\tconst llmClient = llmClientFor(provider, {\n\t\tmodel,\n\t\treasoningEffort,\n\t\tcompactAt,\n\t\tsystemPrompt,\n\t\tlogger,\n\t});\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({\n\t\t\tllmClient,\n\t\t\ttools,\n\t\t\tautoApprove,\n\t\t\tlogger,\n\t\t\t...(history ? { history } : {}),\n\t\t\t...(summarizer ? { summarizer } : {}),\n\t\t\t...(maxSteps === undefined ? {} : { maxSteps }),\n\t\t\t...(onTranscript ? { onTranscript } : {}),\n\t\t}),\n\t\tlogger,\n\t\tnoteFailures: false,\n\t});\n}\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. Each tool judges the risk of each call; a\n * `destructive` one runs only when the run's `approve` allows it, or with `autoApprove`.\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{\n\t\tmodel,\n\t\treasoningEffort,\n\t\tsystemPrompt,\n\t\ttools = [],\n\t\tworkspace,\n\t\tsummarizeUndone = true,\n\t\tmaxSteps,\n\t\tcompactAt,\n\t\tautoApprove,\n\t\tlogger = new Logger(),\n\t\tonTranscript,\n\t}: CreateLLMAgentOptions,\n): Agent {\n\treturn buildLLMAgent(provider, {\n\t\tmodel,\n\t\treasoningEffort,\n\t\tsystemPrompt: workspace\n\t\t\t? withGuidance(systemPrompt, workspaceGuidance(workspace.roots))\n\t\t\t: systemPrompt,\n\t\t// A file tool is a harness tool under its public name.\n\t\ttools: tools as AgentTool[],\n\t\t...(workspace ? { history: historyOf(workspace) } : {}),\n\t\t...(workspace && summarizeUndone ? { summarizer: summarizerFor(provider, logger) } : {}),\n\t\tmaxSteps,\n\t\tcompactAt,\n\t\tautoApprove,\n\t\tlogger,\n\t\tonTranscript,\n\t});\n}\n\n/**\n * The file tools for an agent from `createLLMAgent` on `provider`, over `workspace`, in the\n * provider's own format:\n *\n * - OpenAI: `listFiles`, `searchText`, `readFile`, `readFiles` and its native `apply_patch`;\n * - Anthropic: `listFiles`, `searchText`, its native text editor, whose `view` reads, and\n * `delete_file`.\n *\n * Every one goes through the workspace's access policy: a path outside the roots, a secret,\n * a file `.gitignore` excludes or the harness protects is refused. A change is made only in a\n * `write` root, only from a version the agent read, and is recorded in the workspace's history,\n * so it can be undone. Without a `write` root the tools only read. Give the agent the same\n * `workspace`, so it follows what the history undoes.\n *\n * With `commands`, they include `runCommand`, which runs one of them by name, with only the\n * parameters it declares and never through a shell. Each run asks the approver, showing the\n * program exactly as it will run, and gets a small environment without the provider keys. The\n * files of the `write` roots are captured before it and compared after, so what it changed is\n * in the history too. It is trusted execution, not a sandbox: a command can reach whatever\n * this user can. Not supported on Windows yet.\n *\n * With `formatter`, every file an edit creates or changes is formatted right after, its path\n * the formatter's last argument, and the edit returns the file as the formatter left it. Like a\n * command it runs without a shell, in a small environment, and what it changes in that file is\n * in the history. When it fails, the file stays as the edit wrote it, and the model is told\n * why. Not supported on Windows yet.\n *\n * @throws {InvalidAgentConfigError} for an unknown provider, a workspace with no root, or an\n * invalid command or formatter, including one whose `cwd` is not in a `write` root, or any on\n * Windows.\n * @throws {UnrecoverableError} when no program can read the first root.\n */\nexport async function createFileTools(\n\tprovider: AgentProvider,\n\tworkspace: WorkspaceOptions,\n\t{\n\t\tlogger = new Logger(),\n\t\tcommands,\n\t\tformatter,\n\t}: { logger?: ILogger; commands?: Commands; formatter?: Formatter } = {},\n): Promise<FileTool[]> {\n\t// Reachable from untyped input.\n\tif (!isAgentProvider(provider)) throw unknownProvider(provider);\n\tif (commands !== undefined) validateCommands(commands);\n\tif (formatter !== undefined) validateFormatter(formatter);\n\tconst opened = await openWorkspace(workspace, logger, formatter ? { formatter } : {});\n\treturn (await executorTools(provider, opened, commands)) as unknown as FileTool[];\n}\n\n/** The executor's file tools, and `runCommand` when there are commands. */\nasync function executorTools(\n\tprovider: AgentProvider,\n\tworkspace: OpenedWorkspace,\n\tcommands: Commands | undefined,\n): Promise<AgentTool[]> {\n\tconst tools = fileToolsFor(provider, workspace);\n\t// Every command may belong to other roles, leaving none for this one.\n\tif (commands === undefined || Object.keys(commands).length === 0) return tools;\n\treturn [...tools, await commandToolFor(workspace, commands)];\n}\n\n/** What an explorer finds and reads with: no edits, no changes, no commands. */\nfunction explorerTools({ read, policy }: OpenedWorkspace): AgentTool[] {\n\tconst finding = createWorkspaceTools(read).filter(\n\t\ttool => tool.name !== 'readFile' && tool.name !== 'readFiles',\n\t);\n\treturn [...finding, createTrackedReadFile(policy), createTrackedReadFiles(policy)];\n}\n\n/**\n * What the planner and the reviewer read with: no edits, the current task's changes, `explore`\n * with `explorer`, and the commands given to their role.\n */\nasync function readingTools(\n\tworkspace: OpenedWorkspace,\n\trole: CommandRole,\n\tcommands: Commands | undefined,\n\texplorer: () => Agent,\n): Promise<AgentTool[]> {\n\tconst { policy, store } = workspace;\n\tconst tools = [\n\t\t...explorerTools(workspace),\n\t\tcreateShowChangesTool({ store, policy }),\n\t\tcreateExploreTool(explorer),\n\t];\n\tconst own = commands && commandsFor(commands, role);\n\tif (!own || Object.keys(own).length === 0) return tools;\n\treturn [...tools, await commandToolFor(workspace, own)];\n}\n\nconst batching =\n\t'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.';\n\n// For the roles that have `readFiles`, which the executor on Claude does not.\nconst batchingReads =\n\t'When you know you need several files, or several ranges, read them in one readFiles call, and ask for several searches in one answer, rather than one at a time: each answer is a step, and steps are limited.';\n\n// The explorers find and the role reads, as a reader asks a librarian where a book is and then\n// reads it: their reports are where to look, not a summary to work from.\nconst exploring =\n\t'To find where something is in the repository, ask explorers instead of searching yourself: send `explore` the independent questions you have in one call, and each answers with the files and line ranges that hold what you asked. Then read those ranges yourself, all of them in one readFiles call.';\n\n// Each provider's cheaper model, which only finds and reads.\nconst explorerRoles = {\n\tanthropic: { model: 'haiku', reasoningEffort: 'medium' },\n\topenai: { model: 'gpt-5.6-luna', reasoningEffort: 'medium' },\n} as const satisfies Record<AgentProvider, { model: AgentModel; reasoningEffort: ReasoningEffort }>;\n\nconst explorerSteps = 30;\n\nconst explorerInstructions = `You find things in a repository for another agent, which is planning or reviewing a change and asked you one question, as a reader asks a librarian where a book is. Find where the answer is with listFiles, searchText, readFile and readFiles, reading only what you need to be sure. Answer with where it is: each file and the line ranges that matter, and in a sentence what each range holds and why it answers the question. The other agent reads those ranges itself, so do not copy code beyond a line that marks a place. Say what you looked for and could not find. Do not propose changes or a plan. ${batchingReads}`;\n\n// How each role finds its way, sent after the workspace's roots, so it stays short. Only the\n// planner learns the repository; the executor and the reviewer work from its plan, so a plan\n// that leaves them guessing is the planner's to fix, not theirs to make up for.\nconst repositoryGuidance: Record<CommandRole, string> = {\n\tplanner: `Start with AGENTS.md, which says where the architecture and the decisions are, and read only the parts your task needs. Name in the plan every file to change and what to change in it, so the executor needs nothing else. ${exploring} ${batchingReads}`,\n\texecutor: `Work from the plan: read the files it names, and only what you need to change them. Do not study the repository's architecture or decisions; the plan already applied them. ${batching}`,\n\treviewer: `Work from the plan and the changes: read what they touch, and only what you need to judge them. ${exploring} ${batchingReads}`,\n};\n\n/**\n * Builds the planner → executor → reviewer workflow with every role on the model APIs, each\n * holding its role's instructions, or `systemPrompts`, as its system prompt, followed by the\n * workspace's roots.\n *\n * - The planner and the reviewer read: `listFiles`, `searchText`, `readFile`, `readFiles`, `showChanges`,\n * the current task's changes as far as each may read them, and `explore`, which sends up to 4\n * read-only explorers at once on the role's provider's cheaper model, each a new agent that\n * starts from its question.\n * - The executor gets its provider's file tools (see `createFileTools`), and changes files only\n * in a `write` root; `autoApprove` reaches only it.\n * - Each role has its own step limit: `maxSteps`, 80 for the executor and 65 for the others by\n * default.\n * - Each role gets `runCommand` with the `commands` whose `roles` name it, the executor by\n * default. A command asks the approver in any role, and its files are captured around it.\n * - When the executor has a command, it is told to finish only once the tests and checks pass,\n * or to say which fail, and the reviewer takes from its response whether they pass. Without\n * one, both are told the tests were not run, and the reviewer judges from the code whether\n * they would pass.\n *\n * One run of the orchestrator is one run of the workspace's history, every round and retry\n * included, and its `AgentResponse` names it. Every role follows what the user undoes.\n * Asynchronous because opening the workspace is.\n *\n * @throws {InvalidAgentConfigError} for an unknown provider, or a workspace with no root.\n * @throws {UnrecoverableError} when a role cannot be set up, for example because\n * `OPENAI_API_KEY` is missing, or when no program can read the repository.\n */\nexport async function createLLMOrchestrator({\n\tprovider,\n\tautoApprove,\n\tsystemPrompts = {},\n\tworkspace = { roots: [{ path: process.cwd(), access: 'read' }] },\n\tmaxSteps,\n\tcompactAt,\n\tcommands,\n\tformatter,\n\tsummarizeUndone = true,\n\tlogger = new Logger(),\n\tonTranscript,\n}: CreateLLMOrchestratorOptions = {}): Promise<Agent> {\n\tconst roles = orchestratorRolesFor(provider);\n\tconst steps = perRole('maxSteps', maxSteps, defaultRoleSteps, {\n\t\tunit: 'steps',\n\t\texample: '{ \"executor\": 120 }',\n\t});\n\tconst compaction = perRole('compactAt', compactAt, defaultRoleCompaction, {\n\t\tunit: 'tokens',\n\t\texample: '{ \"executor\": 200000 }',\n\t});\n\tif (formatter !== undefined) validateFormatter(formatter);\n\tconst opened = await openWorkspace(workspace, logger, formatter ? { formatter } : {});\n\tconst guidance = workspaceGuidance(workspace.roots);\n\tconst summarizer = summarizeUndone ? summarizerFor(roles.planner.provider, logger) : undefined;\n\tconst shared = { history: opened.store, ...(summarizer ? { summarizer } : {}), logger };\n\t// A caller's own prompt replaces our instructions and guidance, not the workspace: the model\n\t// needs the roots to name a file, as `createLLMAgent` gives them.\n\tconst prompt = (role: CommandRole, own: string | undefined, instructions: string) =>\n\t\town === undefined\n\t\t\t? withGuidance(instructions, `${guidance}\\n${repositoryGuidance[role]}`)\n\t\t\t: withGuidance(own, guidance);\n\t// Each transcript names the agent it is of.\n\tconst transcriptsOf = (agent: Transcript['agent']) =>\n\t\tonTranscript && ((transcript: Transcript) => onTranscript({ ...transcript, agent }));\n\t// Every command, checked once, before any role takes its share.\n\tif (commands !== undefined) validateCommands(commands);\n\t// A new explorer for each question, on the role's provider: it starts from the question alone.\n\tconst explorerToolset = explorerTools(opened);\n\tconst explorer = (provider: AgentProvider) => () =>\n\t\tbuildLLMAgent(provider, {\n\t\t\t...explorerRoles[provider],\n\t\t\tsystemPrompt: withGuidance(explorerInstructions, guidance),\n\t\t\ttools: explorerToolset,\n\t\t\tmaxSteps: explorerSteps,\n\t\t\tlogger,\n\t\t\tonTranscript: transcriptsOf('explorer'),\n\t\t});\n\tconst reader = async (\n\t\t{ provider, model, reasoningEffort }: Role,\n\t\trole: CommandRole,\n\t\tsystemPrompt: string,\n\t) =>\n\t\tbuildLLMAgent(provider, {\n\t\t\tmodel,\n\t\t\treasoningEffort,\n\t\t\tsystemPrompt,\n\t\t\ttools: await readingTools(opened, role, commands, explorer(provider)),\n\t\t\tmaxSteps: steps[role],\n\t\t\tcompactAt: compaction[role],\n\t\t\tonTranscript: transcriptsOf(role),\n\t\t\t...shared,\n\t\t});\n\tconst { executor } = roles;\n\tconst executorToolset = await executorTools(\n\t\texecutor.provider,\n\t\topened,\n\t\tcommands && commandsFor(commands, 'executor'),\n\t);\n\t// The executor and the reviewer are told whether the tests were run, which only an executor\n\t// with a command can do.\n\tconst executorRuns = executorToolset.some(tool => tool.name === 'runCommand');\n\n\treturn new OrchestratorAgent({\n\t\tplannerAgent: await reader(\n\t\t\troles.planner,\n\t\t\t'planner',\n\t\t\tprompt('planner', systemPrompts.planner, plannerInstructions),\n\t\t),\n\t\texecutorAgent: buildLLMAgent(executor.provider, {\n\t\t\tmodel: executor.model,\n\t\t\treasoningEffort: executor.reasoningEffort,\n\t\t\tsystemPrompt: prompt(\n\t\t\t\t'executor',\n\t\t\t\tsystemPrompts.executor,\n\t\t\t\texecutorRuns ? executorInstructions : executorInstructionsWithoutCommands,\n\t\t\t),\n\t\t\ttools: executorToolset,\n\t\t\tmaxSteps: steps.executor,\n\t\t\tcompactAt: compaction.executor,\n\t\t\tautoApprove,\n\t\t\tonTranscript: transcriptsOf('executor'),\n\t\t\t...shared,\n\t\t}),\n\t\treviewerAgent: await reader(\n\t\t\troles.reviewer,\n\t\t\t'reviewer',\n\t\t\tprompt(\n\t\t\t\t'reviewer',\n\t\t\t\tsystemPrompts.reviewer,\n\t\t\t\texecutorRuns ? reviewerInstructions : reviewerInstructionsWithoutCommands,\n\t\t\t),\n\t\t),\n\t\treviewerDecisionValidator: new ReviewerDecisionValidator(),\n\t\tlogger,\n\t});\n}\n"]}
1
+ {"version":3,"file":"agentLLMFactory.js","sourceRoot":"","sources":["../../../src/factory/infrastructure/agentLLMFactory.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolve } from 'node:path';\nimport type { Agent } from '#src/agent/domain/agent';\nimport type { Transcript, TranscriptCallback } from '#src/agent/domain/transcript';\nimport type { HistoryReader } from '#src/engines/domain/model/historyFollower';\nimport { LLMAgent } from '#src/engines/domain/model/llmAgent';\nimport type { LLMClient } from '#src/llm/domain/llm';\nimport {\n\tClaudeLLMClient,\n\tclaudeLLMModels,\n\tclaudeLLMReasoningEfforts,\n} from '#src/llm/infrastructure/claudeLLMClient';\nimport {\n\tOpenAILLMClient,\n\topenAIModels,\n\topenAIReasoningEfforts,\n} from '#src/llm/infrastructure/openAILLMClient';\nimport {\n\texecutorInstructions,\n\texecutorInstructionsWithoutCommands,\n\tOrchestratorAgent,\n\tplannerInstructions,\n\treviewerInstructions,\n\treviewerInstructionsWithoutCommands,\n} from '#src/orchestration/domain/model/orchestratorAgent';\nimport { ReviewerDecisionValidator } from '#src/orchestration/infrastructure/model/reviewerDecisionValidator';\nimport type { RecoveryStore } from '#src/recovery/domain/recoveryStore';\nimport { RetryingAgent } from '#src/retry/domain/model/retryingAgent';\nimport { InvalidAgentConfigError } from '#src/shared/domain/errors';\nimport { isOneOf } from '#src/shared/domain/isOneOf';\nimport type { ILogger } from '#src/shared/domain/logger';\nimport { Logger } from '#src/shared/infrastructure/logger';\nimport type { WorkspaceRoot } from '#src/tools/domain/accessPolicy';\nimport {\n\ttype CommandRole,\n\ttype Commands,\n\tcommandsFor,\n\ttype Formatter,\n\tvalidateCommands,\n\tvalidateFormatter,\n} from '#src/tools/domain/commands';\nimport type { AgentTool } from '#src/tools/domain/preparedCall';\nimport type { Tool } from '#src/tools/domain/tool';\nimport {\n\tcommandToolFor,\n\ttype FileTool,\n\tfileToolsFor,\n\ttype OpenedWorkspace,\n\topenStore,\n\topenWorkspace,\n\tstateDirectoryOf,\n\ttype WorkspaceOptions,\n} from '#src/tools/infrastructure/fileTools';\nimport { createExploreTool } from '#src/tools/infrastructure/exploreTool';\nimport { createShowChangesTool } from '#src/tools/infrastructure/showChangesTool';\nimport {\n\tcreateTrackedReadFile,\n\tcreateTrackedReadFiles,\n} from '#src/tools/infrastructure/trackedReadFile';\nimport { createWorkspaceTools } from '#src/tools/infrastructure/workspaceTools';\nimport { isAgentProvider, 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\n/** The models each provider's model API accepts. */\nexport const llmModels = {\n\tanthropic: claudeLLMModels,\n\topenai: openAIModels,\n} as const satisfies Readonly<Record<AgentProvider, readonly AgentModel[]>>;\n\n/** The reasoning efforts each provider's model API accepts. */\nexport const llmReasoningEfforts = {\n\tanthropic: claudeLLMReasoningEfforts,\n\topenai: openAIReasoningEfforts,\n} as const satisfies Readonly<Record<AgentProvider, readonly ReasoningEffort[]>>;\n\n/** Narrows a model to one accepted by the selected provider's model API. */\nexport function isLLMModelOf<Provider extends AgentProvider>(\n\tprovider: Provider,\n\tvalue: string,\n): value is (typeof llmModels)[Provider][number] {\n\treturn isOneOf(llmModels[provider], value);\n}\n\n/** Narrows a reasoning effort to one accepted by the selected provider's model API. */\nexport function isLLMReasoningEffortOf<Provider extends AgentProvider>(\n\tprovider: Provider,\n\tvalue: string,\n): value is (typeof llmReasoningEfforts)[Provider][number] {\n\treturn isOneOf(llmReasoningEfforts[provider], value);\n}\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: the consumer's own, and those of `createFileTools`. Defaults to none. */\n\ttools?: (Tool | FileTool)[];\n\t/**\n\t * The folders the agent works on, the same as given to `createFileTools`. With it, the agent\n\t * follows the workspace's history: after the user undoes runs, its next run starts from the\n\t * conversation as it was, and is told what was undone. The system prompt is followed by the\n\t * roots and how to name a file in them.\n\t */\n\tworkspace?: WorkspaceOptions;\n\t/**\n\t * With `workspace`, a summary of what was undone, from the provider's cheap model, in place\n\t * of the undone prompts. That is Claude Haiku 5.5 or `gpt-5.6-luna`, at high effort, and the\n\t * call is billed with the run's tokens. Defaults to `true`.\n\t */\n\tsummarizeUndone?: boolean;\n\t/**\n\t * How many calls to the model one run may make before it fails. Defaults to 65, with no upper\n\t * bound. A run that asks for exactly the same tool calls 5 times in a row fails before that.\n\t */\n\tmaxSteps?: number;\n\t/**\n\t * The input tokens at which the provider compacts the conversation on its side: it replaces\n\t * every turn before that call with its own summary, which only its model reads back. The\n\t * conversation keeps every original turn. Without it, the conversation is never compacted.\n\t * At least 50,000 on Anthropic.\n\t */\n\tcompactAt?: number;\n\t/**\n\t * Runs every tool call without asking, `destructive` ones included, for CI where no one can\n\t * answer. Without it, a `destructive` call goes to the run's `approve`, or is denied.\n\t */\n\tautoApprove?: boolean;\n\t/** Defaults to warnings on stderr. */\n\tlogger?: ILogger;\n\t/**\n\t * Receives what the model saw in each run once the run ended, completed, failed or\n\t * cancelled, for debugging. See `Transcript`. Without it, nothing is kept.\n\t */\n\tonTranscript?: TranscriptCallback;\n}\n\n/** A limit per role of `createLLMOrchestrator`; a role left out keeps its default. */\nexport type RoleSteps = Partial<Record<OrchestratorRole, number>>;\n\n/** A number of tokens per role of `createLLMOrchestrator`; a role left out keeps its default. */\nexport type RoleTokens = Partial<Record<OrchestratorRole, number>>;\n\n// The same three roles a command can be given to.\ntype OrchestratorRole = CommandRole;\n\n// The executor reads, edits and checks, so it needs more calls than the roles that only read.\nconst defaultRoleSteps: Record<OrchestratorRole, number> = {\n\tplanner: 65,\n\texecutor: 80,\n\treviewer: 65,\n};\n\n// No role compacts unless asked to.\nconst defaultRoleCompaction: Record<OrchestratorRole, number | undefined> = {\n\tplanner: undefined,\n\texecutor: undefined,\n\treviewer: undefined,\n};\n\n/**\n * Each role's value of the option `name`, from one number for all of them or a number per role.\n * Each number is checked by whatever takes it.\n *\n * @throws {InvalidAgentConfigError} for a value that is neither, and for a role that does not\n * exist, which is a typo that would otherwise leave that role on its default without a word.\n */\nfunction perRole<T extends number | undefined>(\n\tname: string,\n\tvalue: unknown,\n\tdefaults: Record<OrchestratorRole, T>,\n\t{ unit, example }: { unit: string; example: string },\n): Record<OrchestratorRole, T | number> {\n\tif (value === undefined) return defaults;\n\tif (typeof value === 'number') return { planner: value, executor: value, reviewer: value };\n\t// Untyped input, such as the CLI's configuration file, can hold `null` or a list here.\n\tif (typeof value !== 'object' || value === null || Array.isArray(value)) {\n\t\tthrow new InvalidAgentConfigError(\n\t\t\t`${name} must be a number or an object of ${unit} per role, such as ${example}`,\n\t\t);\n\t}\n\tconst unknown = Object.keys(value).filter(role => !(role in defaults));\n\tif (unknown.length > 0) {\n\t\tthrow new InvalidAgentConfigError(\n\t\t\t`${name} names roles that do not exist: ${unknown.join(', ')}; expected planner, executor or reviewer`,\n\t\t);\n\t}\n\t// A role given as `undefined`, which untyped input can do, keeps its default.\n\tconst given = Object.entries(value as Record<string, unknown>).filter(\n\t\t([, each]) => each !== undefined,\n\t);\n\treturn { ...defaults, ...Object.fromEntries(given) };\n}\n\nexport interface CreateLLMOrchestratorOptions extends CreateOrchestratorOptions {\n\t/**\n\t * The folders the agents work on. The executor changes files only in a `write` root, every\n\t * change recorded in the workspace's history. Defaults to the current working directory,\n\t * read only.\n\t */\n\tworkspace?: WorkspaceOptions;\n\t/**\n\t * How many calls to the model one run of each role may make: one number for every role, or a\n\t * number per role. Defaults to 80 for the executor and 65 for the planner and the reviewer,\n\t * with no upper bound. A run that asks for exactly the same tool calls 5 times in a row fails\n\t * before its limit.\n\t */\n\tmaxSteps?: number | RoleSteps;\n\t/**\n\t * The input tokens at which each role's provider compacts its conversation, as\n\t * `CreateLLMAgentOptions.compactAt`: one number for every role, or a number per role. Without\n\t * it, no role compacts.\n\t */\n\tcompactAt?: number | RoleTokens;\n\t/**\n\t * Commands the roles may run, such as the project's tests, through `runCommand`. Each goes to\n\t * the roles its `roles` names, the executor by default. See `createFileTools`. Each needs a\n\t * `write` root to run in.\n\t */\n\tcommands?: Commands;\n\t/**\n\t * A program the executor's edits run on each file they create or change, such as the\n\t * project's formatter. See `createFileTools`. It needs a `write` root to run in.\n\t */\n\tformatter?: Formatter;\n\t/**\n\t * A summary of what the user undid, from the planner's provider's cheap model, for every\n\t * role. See `CreateLLMAgentOptions.summarizeUndone`. Defaults to `true`.\n\t */\n\tsummarizeUndone?: boolean;\n\t/**\n\t * Receives what the model saw in each run of each role and each explorer, once the run\n\t * ended, for debugging. Each transcript names its role. See `Transcript`.\n\t */\n\tonTranscript?: TranscriptCallback;\n}\n\nconst defaultLLMAgentModels = {\n\tanthropic: 'sonnet',\n\topenai: 'gpt-5.6-luna',\n} as const satisfies Record<AgentProvider, AgentModel>;\n\nfunction unknownProvider(provider: never): InvalidAgentConfigError {\n\treturn new InvalidAgentConfigError(\n\t\t`\"${String(provider)}\" is not an agent provider; expected one of: ${agentProviders.join(', ')}`,\n\t);\n}\n\nfunction llmClientFor(\n\tprovider: AgentProvider,\n\t{\n\t\tmodel,\n\t\treasoningEffort,\n\t\tcompactAt,\n\t\tsystemPrompt,\n\t\tlogger,\n\t}: {\n\t\tmodel?: AgentModel;\n\t\treasoningEffort?: ReasoningEffort;\n\t\tcompactAt?: number | undefined;\n\t\tsystemPrompt: string;\n\t\tlogger: ILogger;\n\t},\n): LLMClient {\n\tswitch (provider) {\n\t\tcase 'anthropic':\n\t\t\treturn new ClaudeLLMClient({\n\t\t\t\tmodel: model ?? defaultLLMAgentModels.anthropic,\n\t\t\t\treasoningEffort,\n\t\t\t\tcompactAt,\n\t\t\t\tsystemPrompt,\n\t\t\t\tlogger,\n\t\t\t});\n\t\tcase 'openai':\n\t\t\treturn new OpenAILLMClient({\n\t\t\t\tmodel: model ?? defaultLLMAgentModels.openai,\n\t\t\t\treasoningEffort,\n\t\t\t\tcompactAt,\n\t\t\t\tsystemPrompt,\n\t\t\t\tlogger,\n\t\t\t});\n\t\tdefault:\n\t\t\t// Reachable from untyped input.\n\t\t\tthrow unknownProvider(provider);\n\t}\n}\n\n/** The provider's cheap model, for the summary of what was undone. */\nfunction summarizerFor(provider: AgentProvider, logger: ILogger): LLMClient {\n\treturn provider === 'anthropic'\n\t\t? new ClaudeLLMClient({ model: 'haiku', reasoningEffort: 'high', systemPrompt: '', logger })\n\t\t: new OpenAILLMClient({\n\t\t\t\tmodel: 'gpt-5.6-luna',\n\t\t\t\treasoningEffort: 'high',\n\t\t\t\tsystemPrompt: '',\n\t\t\t\tlogger,\n\t\t\t});\n}\n\n/**\n * The workspace's history, opened on first use, so a factory that builds an agent stays\n * synchronous. Every agent and every file tool of one workspace reads the same files on disk.\n */\nfunction historyOf(workspace: WorkspaceOptions): HistoryReader {\n\t// Checked now: an agent with no root to follow is a setup error, not a first-run one.\n\tif (workspace.roots.length === 0) {\n\t\tthrow new InvalidAgentConfigError('A workspace needs at least one root');\n\t}\n\tlet store: Promise<RecoveryStore> | undefined;\n\tconst open = () => {\n\t\tstore ??= stateDirectoryOf(workspace)\n\t\t\t.then(directory => openStore(workspace, directory))\n\t\t\t.catch((error: unknown) => {\n\t\t\t\t// Not kept: the next run tries again, as one whose disk was full for a moment.\n\t\t\t\tstore = undefined;\n\t\t\t\tthrow error;\n\t\t\t});\n\t\treturn store;\n\t};\n\treturn {\n\t\tlistRuns: async () => (await open()).listRuns(),\n\t\tlistRevisions: async () => (await open()).listRevisions(),\n\t\treadRun: async runId => (await open()).readRun(runId),\n\t};\n}\n\n/**\n * What the model is told about the workspace after its instructions: the roots, which it may\n * change, and how to name a file. A host path in an error sends the model looking for it, so\n * the real root is given here once.\n */\nfunction workspaceGuidance(roots: WorkspaceRoot[]): string {\n\tconst [first, ...others] = roots.map(root => ({ ...root, path: resolve(root.path) }));\n\tif (!first) return '';\n\tconst described = [first, ...others].map(\n\t\t({ path, access }) => `- ${path}${access === 'write' ? '' : ' (read only)'}`,\n\t);\n\treturn [\n\t\t'The workspace:',\n\t\t...described,\n\t\t`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.`,\n\t].join('\\n');\n}\n\nfunction withGuidance(systemPrompt: string, guidance: string): string {\n\treturn guidance === '' ? systemPrompt : `${systemPrompt}\\n\\n${guidance}`;\n}\n\n/** An `LLMAgent` on `provider`, wrapped in the harness's retry policy. */\nfunction buildLLMAgent(\n\tprovider: AgentProvider,\n\t{\n\t\tmodel,\n\t\treasoningEffort,\n\t\tsystemPrompt,\n\t\ttools,\n\t\thistory,\n\t\tsummarizer,\n\t\tmaxSteps,\n\t\tcompactAt,\n\t\tautoApprove,\n\t\tlogger,\n\t\tonTranscript,\n\t}: {\n\t\tmodel?: AgentModel;\n\t\treasoningEffort?: ReasoningEffort;\n\t\tsystemPrompt: string;\n\t\ttools: AgentTool[];\n\t\thistory?: HistoryReader;\n\t\tsummarizer?: LLMClient;\n\t\tmaxSteps?: number;\n\t\tcompactAt?: number | undefined;\n\t\tautoApprove?: boolean;\n\t\tlogger: ILogger;\n\t\tonTranscript?: TranscriptCallback | undefined;\n\t},\n): Agent {\n\tconst llmClient = llmClientFor(provider, {\n\t\tmodel,\n\t\treasoningEffort,\n\t\tcompactAt,\n\t\tsystemPrompt,\n\t\tlogger,\n\t});\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({\n\t\t\tllmClient,\n\t\t\ttools,\n\t\t\tautoApprove,\n\t\t\tlogger,\n\t\t\t...(history ? { history } : {}),\n\t\t\t...(summarizer ? { summarizer } : {}),\n\t\t\t...(maxSteps === undefined ? {} : { maxSteps }),\n\t\t\t...(onTranscript ? { onTranscript } : {}),\n\t\t}),\n\t\tlogger,\n\t\tnoteFailures: false,\n\t});\n}\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. Each tool judges the risk of each call; a\n * `destructive` one runs only when the run's `approve` allows it, or with `autoApprove`.\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{\n\t\tmodel,\n\t\treasoningEffort,\n\t\tsystemPrompt,\n\t\ttools = [],\n\t\tworkspace,\n\t\tsummarizeUndone = true,\n\t\tmaxSteps,\n\t\tcompactAt,\n\t\tautoApprove,\n\t\tlogger = new Logger(),\n\t\tonTranscript,\n\t}: CreateLLMAgentOptions,\n): Agent {\n\treturn buildLLMAgent(provider, {\n\t\tmodel,\n\t\treasoningEffort,\n\t\tsystemPrompt: workspace\n\t\t\t? withGuidance(systemPrompt, workspaceGuidance(workspace.roots))\n\t\t\t: systemPrompt,\n\t\t// A file tool is a harness tool under its public name.\n\t\ttools: tools as AgentTool[],\n\t\t...(workspace ? { history: historyOf(workspace) } : {}),\n\t\t...(workspace && summarizeUndone ? { summarizer: summarizerFor(provider, logger) } : {}),\n\t\tmaxSteps,\n\t\tcompactAt,\n\t\tautoApprove,\n\t\tlogger,\n\t\tonTranscript,\n\t});\n}\n\n/**\n * The file tools for an agent from `createLLMAgent` on `provider`, over `workspace`, in the\n * provider's own format:\n *\n * - OpenAI: `listFiles`, `searchText`, `readFile`, `readFiles` and its native `apply_patch`;\n * - Anthropic: `listFiles`, `searchText`, its native text editor, whose `view` reads, and\n * `delete_file`.\n *\n * Every one goes through the workspace's access policy: a path outside the roots, a secret,\n * a file `.gitignore` excludes or the harness protects is refused. A change is made only in a\n * `write` root, only from a version the agent read, and is recorded in the workspace's history,\n * so it can be undone. Without a `write` root the tools only read. Give the agent the same\n * `workspace`, so it follows what the history undoes.\n *\n * With `commands`, they include `runCommand`, which runs one of them by name, with only the\n * parameters it declares and never through a shell. Each run asks the approver, showing the\n * program exactly as it will run, and gets a small environment without the provider keys. The\n * files of the `write` roots are captured before it and compared after, so what it changed is\n * in the history too. It is trusted execution, not a sandbox: a command can reach whatever\n * this user can. Not supported on Windows yet.\n *\n * With `formatter`, every file an edit creates or changes is formatted right after, its path\n * the formatter's last argument. The edit shows the lines the formatter rewrote, and the model\n * reads a file it rewrote again before editing it. Like a command it runs without a shell, in a small environment, and what it changes in that file is\n * in the history. When it fails, the file stays as the edit wrote it, and the model is told\n * why. Not supported on Windows yet.\n *\n * @throws {InvalidAgentConfigError} for an unknown provider, a workspace with no root, or an\n * invalid command or formatter, including one whose `cwd` is not in a `write` root, or any on\n * Windows.\n * @throws {UnrecoverableError} when no program can read the first root.\n */\nexport async function createFileTools(\n\tprovider: AgentProvider,\n\tworkspace: WorkspaceOptions,\n\t{\n\t\tlogger = new Logger(),\n\t\tcommands,\n\t\tformatter,\n\t}: { logger?: ILogger; commands?: Commands; formatter?: Formatter } = {},\n): Promise<FileTool[]> {\n\t// Reachable from untyped input.\n\tif (!isAgentProvider(provider)) throw unknownProvider(provider);\n\tif (commands !== undefined) validateCommands(commands);\n\tif (formatter !== undefined) validateFormatter(formatter);\n\tconst opened = await openWorkspace(workspace, logger, formatter ? { formatter } : {});\n\treturn (await executorTools(provider, opened, commands)) as unknown as FileTool[];\n}\n\n/** The executor's file tools, and `runCommand` when there are commands. */\nasync function executorTools(\n\tprovider: AgentProvider,\n\tworkspace: OpenedWorkspace,\n\tcommands: Commands | undefined,\n): Promise<AgentTool[]> {\n\tconst tools = fileToolsFor(provider, workspace);\n\t// Every command may belong to other roles, leaving none for this one.\n\tif (commands === undefined || Object.keys(commands).length === 0) return tools;\n\treturn [...tools, await commandToolFor(workspace, commands)];\n}\n\n/** What an explorer finds and reads with: no edits, no changes, no commands. */\nfunction explorerTools({ read, policy }: OpenedWorkspace): AgentTool[] {\n\tconst finding = createWorkspaceTools(read).filter(\n\t\ttool => tool.name !== 'readFile' && tool.name !== 'readFiles',\n\t);\n\treturn [...finding, createTrackedReadFile(policy), createTrackedReadFiles(policy)];\n}\n\n/**\n * What the planner and the reviewer read with: no edits, the current task's changes, `explore`\n * with `explorer`, and the commands given to their role.\n */\nasync function readingTools(\n\tworkspace: OpenedWorkspace,\n\trole: CommandRole,\n\tcommands: Commands | undefined,\n\texplorer: () => Agent,\n): Promise<AgentTool[]> {\n\tconst { policy, store } = workspace;\n\tconst tools = [\n\t\t...explorerTools(workspace),\n\t\tcreateShowChangesTool({ store, policy }),\n\t\tcreateExploreTool(explorer),\n\t];\n\tconst own = commands && commandsFor(commands, role);\n\tif (!own || Object.keys(own).length === 0) return tools;\n\treturn [...tools, await commandToolFor(workspace, own)];\n}\n\nconst batching =\n\t'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.';\n\n// For the roles that have `readFiles`, which the executor on Claude does not.\nconst batchingReads =\n\t'When you know you need several files, or several ranges, read them in one readFiles call, and ask for several searches in one answer, rather than one at a time: each answer is a step, and steps are limited.';\n\n// The explorers find and the role reads, as a reader asks a librarian where a book is and then\n// reads it: their reports are where to look, not a summary to work from.\nconst exploring =\n\t'To find where something is in the repository, ask explorers instead of searching yourself: send `explore` the independent questions you have in one call, and each answers with the files and line ranges that hold what you asked. Then read those ranges yourself, all of them in one readFiles call.';\n\n// Each provider's cheaper model, which only finds and reads.\nconst explorerRoles = {\n\tanthropic: { model: 'haiku', reasoningEffort: 'medium' },\n\topenai: { model: 'gpt-5.6-luna', reasoningEffort: 'medium' },\n} as const satisfies Record<AgentProvider, { model: AgentModel; reasoningEffort: ReasoningEffort }>;\n\nconst explorerSteps = 30;\n\nconst explorerInstructions = `You find things in a repository for another agent, which is planning or reviewing a change and asked you one question, as a reader asks a librarian where a book is. Find where the answer is with listFiles, searchText, readFile and readFiles, reading only what you need to be sure. Answer with where it is: each file and the line ranges that matter, and in a sentence what each range holds and why it answers the question. The other agent reads those ranges itself, so do not copy code beyond a line that marks a place. Say what you looked for and could not find. Do not propose changes or a plan. ${batchingReads}`;\n\n// How each role finds its way, sent after the workspace's roots, so it stays short. Only the\n// planner learns the repository; the executor and the reviewer work from its plan, so a plan\n// that leaves them guessing is the planner's to fix, not theirs to make up for.\nconst repositoryGuidance: Record<CommandRole, string> = {\n\tplanner: `Start with AGENTS.md, which says where the architecture and the decisions are, and read only the parts your task needs. Name in the plan every file to change and what to change in it, so the executor needs nothing else. ${exploring} ${batchingReads}`,\n\texecutor: `Work from the plan: read the files it names, and only what you need to change them. Do not study the repository's architecture or decisions; the plan already applied them. ${batching}`,\n\treviewer: `Work from the plan and the changes: read what they touch, and only what you need to judge them. ${exploring} ${batchingReads}`,\n};\n\n/**\n * Builds the planner → executor → reviewer workflow with every role on the model APIs, each\n * holding its role's instructions, or `systemPrompts`, as its system prompt, followed by the\n * workspace's roots.\n *\n * - The planner and the reviewer read: `listFiles`, `searchText`, `readFile`, `readFiles`, `showChanges`,\n * the current task's changes as far as each may read them, and `explore`, which sends up to 4\n * read-only explorers at once on the role's provider's cheaper model, each a new agent that\n * starts from its question.\n * - The executor gets its provider's file tools (see `createFileTools`), and changes files only\n * in a `write` root; `autoApprove` reaches only it.\n * - Each role has its own step limit: `maxSteps`, 80 for the executor and 65 for the others by\n * default.\n * - Each role gets `runCommand` with the `commands` whose `roles` name it, the executor by\n * default. A command asks the approver in any role, and its files are captured around it.\n * - When the executor has a command, it is told to finish only once the tests and checks pass,\n * or to say which fail, and the reviewer takes from its response whether they pass. Without\n * one, both are told the tests were not run, and the reviewer judges from the code whether\n * they would pass.\n *\n * One run of the orchestrator is one run of the workspace's history, every round and retry\n * included, and its `AgentResponse` names it. Every role follows what the user undoes.\n * Asynchronous because opening the workspace is.\n *\n * @throws {InvalidAgentConfigError} for an unknown provider, or a workspace with no root.\n * @throws {UnrecoverableError} when a role cannot be set up, for example because\n * `OPENAI_API_KEY` is missing, or when no program can read the repository.\n */\nexport async function createLLMOrchestrator({\n\tprovider,\n\tautoApprove,\n\tsystemPrompts = {},\n\tworkspace = { roots: [{ path: process.cwd(), access: 'read' }] },\n\tmaxSteps,\n\tcompactAt,\n\tcommands,\n\tformatter,\n\tsummarizeUndone = true,\n\tlogger = new Logger(),\n\tonTranscript,\n}: CreateLLMOrchestratorOptions = {}): Promise<Agent> {\n\tconst roles = orchestratorRolesFor(provider);\n\tconst steps = perRole('maxSteps', maxSteps, defaultRoleSteps, {\n\t\tunit: 'steps',\n\t\texample: '{ \"executor\": 120 }',\n\t});\n\tconst compaction = perRole('compactAt', compactAt, defaultRoleCompaction, {\n\t\tunit: 'tokens',\n\t\texample: '{ \"executor\": 200000 }',\n\t});\n\tif (formatter !== undefined) validateFormatter(formatter);\n\tconst opened = await openWorkspace(workspace, logger, formatter ? { formatter } : {});\n\tconst guidance = workspaceGuidance(workspace.roots);\n\tconst summarizer = summarizeUndone ? summarizerFor(roles.planner.provider, logger) : undefined;\n\tconst shared = { history: opened.store, ...(summarizer ? { summarizer } : {}), logger };\n\t// A caller's own prompt replaces our instructions and guidance, not the workspace: the model\n\t// needs the roots to name a file, as `createLLMAgent` gives them.\n\tconst prompt = (role: CommandRole, own: string | undefined, instructions: string) =>\n\t\town === undefined\n\t\t\t? withGuidance(instructions, `${guidance}\\n${repositoryGuidance[role]}`)\n\t\t\t: withGuidance(own, guidance);\n\t// Each transcript names the agent it is of.\n\tconst transcriptsOf = (agent: Transcript['agent']) =>\n\t\tonTranscript && ((transcript: Transcript) => onTranscript({ ...transcript, agent }));\n\t// Every command, checked once, before any role takes its share.\n\tif (commands !== undefined) validateCommands(commands);\n\t// A new explorer for each question, on the role's provider: it starts from the question alone.\n\tconst explorerToolset = explorerTools(opened);\n\tconst explorer = (provider: AgentProvider) => () =>\n\t\tbuildLLMAgent(provider, {\n\t\t\t...explorerRoles[provider],\n\t\t\tsystemPrompt: withGuidance(explorerInstructions, guidance),\n\t\t\ttools: explorerToolset,\n\t\t\tmaxSteps: explorerSteps,\n\t\t\tlogger,\n\t\t\tonTranscript: transcriptsOf('explorer'),\n\t\t});\n\tconst reader = async (\n\t\t{ provider, model, reasoningEffort }: Role,\n\t\trole: CommandRole,\n\t\tsystemPrompt: string,\n\t) =>\n\t\tbuildLLMAgent(provider, {\n\t\t\tmodel,\n\t\t\treasoningEffort,\n\t\t\tsystemPrompt,\n\t\t\ttools: await readingTools(opened, role, commands, explorer(provider)),\n\t\t\tmaxSteps: steps[role],\n\t\t\tcompactAt: compaction[role],\n\t\t\tonTranscript: transcriptsOf(role),\n\t\t\t...shared,\n\t\t});\n\tconst { executor } = roles;\n\tconst executorToolset = await executorTools(\n\t\texecutor.provider,\n\t\topened,\n\t\tcommands && commandsFor(commands, 'executor'),\n\t);\n\t// The executor and the reviewer are told whether the tests were run, which only an executor\n\t// with a command can do.\n\tconst executorRuns = executorToolset.some(tool => tool.name === 'runCommand');\n\n\treturn new OrchestratorAgent({\n\t\tplannerAgent: await reader(\n\t\t\troles.planner,\n\t\t\t'planner',\n\t\t\tprompt('planner', systemPrompts.planner, plannerInstructions),\n\t\t),\n\t\texecutorAgent: buildLLMAgent(executor.provider, {\n\t\t\tmodel: executor.model,\n\t\t\treasoningEffort: executor.reasoningEffort,\n\t\t\tsystemPrompt: prompt(\n\t\t\t\t'executor',\n\t\t\t\tsystemPrompts.executor,\n\t\t\t\texecutorRuns ? executorInstructions : executorInstructionsWithoutCommands,\n\t\t\t),\n\t\t\ttools: executorToolset,\n\t\t\tmaxSteps: steps.executor,\n\t\t\tcompactAt: compaction.executor,\n\t\t\tautoApprove,\n\t\t\tonTranscript: transcriptsOf('executor'),\n\t\t\t...shared,\n\t\t}),\n\t\treviewerAgent: await reader(\n\t\t\troles.reviewer,\n\t\t\t'reviewer',\n\t\t\tprompt(\n\t\t\t\t'reviewer',\n\t\t\t\tsystemPrompts.reviewer,\n\t\t\t\texecutorRuns ? reviewerInstructions : reviewerInstructionsWithoutCommands,\n\t\t\t),\n\t\t),\n\t\treviewerDecisionValidator: new ReviewerDecisionValidator(),\n\t\tlogger,\n\t});\n}\n"]}
@@ -1,9 +1,14 @@
1
1
  /**
2
2
  * What a modification returns: its path, then the lines around each change as the file now
3
- * reads them, numbered, with two lines of context. The model can check its edit without reading
4
- * the file again. What does not fit is counted, not shown.
3
+ * reads them. The model can check its edit without reading the file again.
5
4
  */
6
5
  export declare function describeModification(path: string, before: string, after: string): string;
6
+ /**
7
+ * What an edit adds to its response when the formatter rewrote what the model sent: that the file
8
+ * must be read again, and those lines as it now reads them. Nothing when the formatter left the
9
+ * file as written.
10
+ */
11
+ export declare function describeFormatting(path: string, written: string, formatted: string): string;
7
12
  /**
8
13
  * An edit as a git-style unified diff, for the person watching it happen. A missing side is a
9
14
  * file that did not exist, or no longer does. Too long a diff is cut, and says so.
@@ -15,13 +15,10 @@ function lineCount(text) {
15
15
  return lines.at(-1) === '' ? lines.length - 1 : lines.length;
16
16
  }
17
17
  /**
18
- * What a modification returns: its path, then the lines around each change as the file now
19
- * reads them, numbered, with two lines of context. The model can check its edit without reading
20
- * the file again. What does not fit is counted, not shown.
18
+ * The lines around each change from `before` to `after`, as `after` reads them, numbered, with
19
+ * two lines of context. What does not fit is counted, not shown.
21
20
  */
22
- export function describeModification(path, before, after) {
23
- if (after === '')
24
- return `Changed ${path}. The file is now empty.`;
21
+ function changedLines(path, before, after) {
25
22
  const shown = [];
26
23
  let hidden = 0;
27
24
  let line = 0;
@@ -43,11 +40,36 @@ export function describeModification(path, before, after) {
43
40
  hidden++;
44
41
  line++;
45
42
  }
46
- const header = `Changed ${path}. It now reads, around the change:`;
47
- const notice = hidden > 0
48
- ? [`[${String(hidden)} more lines of the change are not shown. Read the file to see them.]`]
49
- : [];
50
- return [header, ...shown, ...notice].join('\n');
43
+ if (hidden > 0) {
44
+ shown.push(`[${String(hidden)} more lines of the change are not shown. Read the file to see them.]`);
45
+ }
46
+ return shown;
47
+ }
48
+ /**
49
+ * What a modification returns: its path, then the lines around each change as the file now
50
+ * reads them. The model can check its edit without reading the file again.
51
+ */
52
+ export function describeModification(path, before, after) {
53
+ if (after === '')
54
+ return `Changed ${path}. The file is now empty.`;
55
+ return [
56
+ `Changed ${path}. It now reads, around the change:`,
57
+ ...changedLines(path, before, after),
58
+ ].join('\n');
59
+ }
60
+ /**
61
+ * What an edit adds to its response when the formatter rewrote what the model sent: that the file
62
+ * must be read again, and those lines as it now reads them. Nothing when the formatter left the
63
+ * file as written.
64
+ */
65
+ export function describeFormatting(path, written, formatted) {
66
+ if (written === formatted || formatted === '')
67
+ return '';
68
+ return [
69
+ '',
70
+ 'The formatter rewrote some of what you sent, so read the file again before you edit it. Those lines now read:',
71
+ ...changedLines(path, written, formatted),
72
+ ].join('\n');
51
73
  }
52
74
  // What one edit's diff may hold for the person watching; the history keeps the whole change.
53
75
  const maxDiffLines = 400;
@@ -1 +1 @@
1
- {"version":3,"file":"editResponse.js","sourceRoot":"","sources":["../../../src/tools/domain/editResponse.ts"],"names":[],"mappings":"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","sourcesContent":["import { unifiedDiff } from '#src/diff/domain/unifiedDiff';\nimport { cutLine } from './lineLength.ts';\n\n// Enough to place a change without reading the file again.\nconst contextLines = 2;\n// What one response may show: a large rewrite is named, not repeated.\nconst maxShownLines = 60;\n/** A line of `path` as the model reads it: without the `\\r` of a CRLF ending, and not too long. */\nfunction shownLine(path: string, text: string): string {\n\treturn cutLine(path, text.endsWith('\\r') ? text.slice(0, -1) : text);\n}\n\nfunction lineCount(text: string): number {\n\tif (text === '') return 0;\n\tconst lines = text.split('\\n');\n\treturn lines.at(-1) === '' ? lines.length - 1 : lines.length;\n}\n\n/**\n * What a modification returns: its path, then the lines around each change as the file now\n * reads them, numbered, with two lines of context. The model can check its edit without reading\n * the file again. What does not fit is counted, not shown.\n */\nexport function describeModification(path: string, before: string, after: string): string {\n\tif (after === '') return `Changed ${path}. The file is now empty.`;\n\tconst shown: string[] = [];\n\tlet hidden = 0;\n\tlet line = 0;\n\tfor (const text of unifiedDiff(before, after, { context: contextLines }).split('\\n')) {\n\t\tconst hunk = /^@@ -\\d+(?:,\\d+)? \\+(\\d+)(?:,\\d+)? @@/.exec(text);\n\t\tif (hunk) {\n\t\t\tif (shown.length > 0 && shown.length < maxShownLines) shown.push('…');\n\t\t\tline = Number(hunk[1]);\n\t\t\tcontinue;\n\t\t}\n\t\t// Removed lines are gone from the file, and git's marker is not a line of it.\n\t\tif (!text.startsWith(' ') && !text.startsWith('+')) continue;\n\t\tif (shown.length < maxShownLines) {\n\t\t\tshown.push(`${String(line)}: ${shownLine(path, text.slice(1))}`);\n\t\t} else hidden++;\n\t\tline++;\n\t}\n\n\tconst header = `Changed ${path}. It now reads, around the change:`;\n\tconst notice =\n\t\thidden > 0\n\t\t\t? [`[${String(hidden)} more lines of the change are not shown. Read the file to see them.]`]\n\t\t\t: [];\n\treturn [header, ...shown, ...notice].join('\\n');\n}\n\n// What one edit's diff may hold for the person watching; the history keeps the whole change.\nconst maxDiffLines = 400;\n\n/**\n * An edit as a git-style unified diff, for the person watching it happen. A missing side is a\n * file that did not exist, or no longer does. Too long a diff is cut, and says so.\n */\nexport function editDiff(\n\tpath: string,\n\tbefore: string | undefined,\n\tafter: string | undefined,\n): string {\n\tconst lines = [\n\t\t`--- ${before === undefined ? '/dev/null' : `a/${path}`}`,\n\t\t`+++ ${after === undefined ? '/dev/null' : `b/${path}`}`,\n\t\t...unifiedDiff(before ?? '', after ?? '', { context: contextLines }).split('\\n'),\n\t];\n\tif (lines.length <= maxDiffLines) return lines.join('\\n');\n\treturn [\n\t\t...lines.slice(0, maxDiffLines),\n\t\t`... ${String(lines.length - maxDiffLines)} more lines of the diff not shown`,\n\t].join('\\n');\n}\n\n/** What a creation returns: a confirmation and the size, not the content the model just sent. */\nexport function describeCreation(path: string, content: Buffer): string {\n\tconst lines = lineCount(content.toString('utf8'));\n\treturn `Created ${path} (${String(lines)} ${lines === 1 ? 'line' : 'lines'}, ${String(content.length)} bytes).`;\n}\n\nexport function describeDeletion(path: string): string {\n\treturn `Deleted ${path}.`;\n}\n"]}
1
+ {"version":3,"file":"editResponse.js","sourceRoot":"","sources":["../../../src/tools/domain/editResponse.ts"],"names":[],"mappings":"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","sourcesContent":["import { unifiedDiff } from '#src/diff/domain/unifiedDiff';\nimport { cutLine } from './lineLength.ts';\n\n// Enough to place a change without reading the file again.\nconst contextLines = 2;\n// What one response may show: a large rewrite is named, not repeated.\nconst maxShownLines = 60;\n/** A line of `path` as the model reads it: without the `\\r` of a CRLF ending, and not too long. */\nfunction shownLine(path: string, text: string): string {\n\treturn cutLine(path, text.endsWith('\\r') ? text.slice(0, -1) : text);\n}\n\nfunction lineCount(text: string): number {\n\tif (text === '') return 0;\n\tconst lines = text.split('\\n');\n\treturn lines.at(-1) === '' ? lines.length - 1 : lines.length;\n}\n\n/**\n * The lines around each change from `before` to `after`, as `after` reads them, numbered, with\n * two lines of context. What does not fit is counted, not shown.\n */\nfunction changedLines(path: string, before: string, after: string): string[] {\n\tconst shown: string[] = [];\n\tlet hidden = 0;\n\tlet line = 0;\n\tfor (const text of unifiedDiff(before, after, { context: contextLines }).split('\\n')) {\n\t\tconst hunk = /^@@ -\\d+(?:,\\d+)? \\+(\\d+)(?:,\\d+)? @@/.exec(text);\n\t\tif (hunk) {\n\t\t\tif (shown.length > 0 && shown.length < maxShownLines) shown.push('…');\n\t\t\tline = Number(hunk[1]);\n\t\t\tcontinue;\n\t\t}\n\t\t// Removed lines are gone from the file, and git's marker is not a line of it.\n\t\tif (!text.startsWith(' ') && !text.startsWith('+')) continue;\n\t\tif (shown.length < maxShownLines) {\n\t\t\tshown.push(`${String(line)}: ${shownLine(path, text.slice(1))}`);\n\t\t} else hidden++;\n\t\tline++;\n\t}\n\tif (hidden > 0) {\n\t\tshown.push(\n\t\t\t`[${String(hidden)} more lines of the change are not shown. Read the file to see them.]`,\n\t\t);\n\t}\n\treturn shown;\n}\n\n/**\n * What a modification returns: its path, then the lines around each change as the file now\n * reads them. The model can check its edit without reading the file again.\n */\nexport function describeModification(path: string, before: string, after: string): string {\n\tif (after === '') return `Changed ${path}. The file is now empty.`;\n\treturn [\n\t\t`Changed ${path}. It now reads, around the change:`,\n\t\t...changedLines(path, before, after),\n\t].join('\\n');\n}\n\n/**\n * What an edit adds to its response when the formatter rewrote what the model sent: that the file\n * must be read again, and those lines as it now reads them. Nothing when the formatter left the\n * file as written.\n */\nexport function describeFormatting(path: string, written: string, formatted: string): string {\n\tif (written === formatted || formatted === '') return '';\n\treturn [\n\t\t'',\n\t\t'The formatter rewrote some of what you sent, so read the file again before you edit it. Those lines now read:',\n\t\t...changedLines(path, written, formatted),\n\t].join('\\n');\n}\n\n// What one edit's diff may hold for the person watching; the history keeps the whole change.\nconst maxDiffLines = 400;\n\n/**\n * An edit as a git-style unified diff, for the person watching it happen. A missing side is a\n * file that did not exist, or no longer does. Too long a diff is cut, and says so.\n */\nexport function editDiff(\n\tpath: string,\n\tbefore: string | undefined,\n\tafter: string | undefined,\n): string {\n\tconst lines = [\n\t\t`--- ${before === undefined ? '/dev/null' : `a/${path}`}`,\n\t\t`+++ ${after === undefined ? '/dev/null' : `b/${path}`}`,\n\t\t...unifiedDiff(before ?? '', after ?? '', { context: contextLines }).split('\\n'),\n\t];\n\tif (lines.length <= maxDiffLines) return lines.join('\\n');\n\treturn [\n\t\t...lines.slice(0, maxDiffLines),\n\t\t`... ${String(lines.length - maxDiffLines)} more lines of the diff not shown`,\n\t].join('\\n');\n}\n\n/** What a creation returns: a confirmation and the size, not the content the model just sent. */\nexport function describeCreation(path: string, content: Buffer): string {\n\tconst lines = lineCount(content.toString('utf8'));\n\treturn `Created ${path} (${String(lines)} ${lines === 1 ? 'line' : 'lines'}, ${String(content.length)} bytes).`;\n}\n\nexport function describeDeletion(path: string): string {\n\treturn `Deleted ${path}.`;\n}\n"]}
@@ -1,11 +1,11 @@
1
1
  /**
2
- * Applies the hunks of a V4A `update_file` diff to `content`, in order, each after the one before
3
- * it. A hunk with no anchor must match exactly once in the part of the file it searches, or
4
- * nothing is changed: a hunk is never applied where the model did not mean it. After an anchor,
5
- * it is the first match: the anchor already says where. Each line the hunk does not change is
6
- * written back as it was, ending included, and an added line ends like the line before it.
2
+ * Applies the hunks of a V4A `update_file` diff to `content`, in order, each at its first match
3
+ * after the one before it and after its anchor, as Codex does: the order of the hunks says which
4
+ * of two alike places is meant. Each line the hunk does not change is written back as it was,
5
+ * ending included, and an added line ends like the line before it.
7
6
  *
8
- * @throws {EditRefusedError} naming the hunk, what it expected and what the file has near it.
7
+ * @throws {EditRefusedError} naming the hunk, what it expected and the lines of the file most
8
+ * like it.
9
9
  */
10
10
  export declare function applyUpdate(content: string, diff: string, path: string): string;
11
11
  /**
@@ -5,6 +5,7 @@ const maxDiffBytes = 512 * 1024;
5
5
  const maxHunks = 500;
6
6
  // What an error quotes of a hunk or of the file, to fix the call without flooding the context.
7
7
  const quotedLines = 8;
8
+ const nearLines = 16;
8
9
  /** The lines a hunk expects in the file: the ones it keeps and the ones it removes. */
9
10
  function expected(hunk) {
10
11
  return hunk.steps.filter(step => step.kind !== 'add').map(step => step.text);
@@ -119,19 +120,58 @@ function quote(lines) {
119
120
  shown.push(` … ${String(lines.length - quotedLines)} more`);
120
121
  return shown.join('\n');
121
122
  }
122
- /** The real lines near where a hunk's first line appears, so the model can copy them. */
123
- function near(lines, hunk) {
124
- const first = expected(hunk)
125
- .find(line => line.trim() !== '')
126
- ?.trim();
127
- const at = first === undefined ? -1 : lines.findIndex(line => line.trim() === first);
128
- if (at === -1)
123
+ /**
124
+ * The stretch of the file most like a hunk that did not match, so the model can copy its real
125
+ * lines: as long as the hunk, holding the most of its lines in any order, so a line changed or
126
+ * left out does not hide it. On a tie, the first at or after where the hunk was looked for.
127
+ */
128
+ function near(lines, hunk, from) {
129
+ const old = expected(hunk);
130
+ const wanted = new Set(old.map(line => line.trim()).filter(line => line !== ''));
131
+ // The stretch slides one line at a time, counting the hunk's lines in it as they enter and
132
+ // leave, so a large hunk that failed costs no more than reading the file once.
133
+ const inStretch = new Map();
134
+ let score = 0;
135
+ const slide = (line, by) => {
136
+ const text = line?.trim();
137
+ if (text === undefined || !wanted.has(text))
138
+ return;
139
+ const count = (inStretch.get(text) ?? 0) + by;
140
+ inStretch.set(text, count);
141
+ if (count === (by === 1 ? 1 : 0))
142
+ score += by;
143
+ };
144
+ for (let at = 0; at < old.length; at++)
145
+ slide(lines[at], 1);
146
+ let best = { at: -1, score: 0 };
147
+ for (let at = 0; at < lines.length; at++) {
148
+ if (at > 0) {
149
+ slide(lines[at - 1], -1);
150
+ slide(lines[at + old.length - 1], 1);
151
+ }
152
+ const later = best.at < from && at >= from;
153
+ if (score > best.score || (score > 0 && score === best.score && later))
154
+ best = { at, score };
155
+ }
156
+ if (best.at === -1)
129
157
  return '';
130
- const from = Math.max(0, at - 1);
158
+ // From the first of the hunk's lines in the stretch to the last, with a line around them.
159
+ const held = (line) => line !== undefined && wanted.has(line.trim());
160
+ let first = best.at;
161
+ while (!held(lines[first]))
162
+ first++;
163
+ let last = Math.min(best.at + old.length, lines.length) - 1;
164
+ while (!held(lines[last]))
165
+ last--;
166
+ const start = Math.max(0, first - 1);
167
+ const end = Math.min(lines.length, last + 2, start + nearLines);
131
168
  const shown = lines
132
- .slice(from, from + quotedLines)
133
- .map((line, offset) => ` ${String(from + offset + 1)}: ${line}`);
134
- return `\nThe file has, near line ${String(at + 1)}:\n${shown.join('\n')}`;
169
+ .slice(start, end)
170
+ .map((line, offset) => ` ${String(start + offset + 1)}: ${line}`);
171
+ const order = first < from
172
+ ? '\nThose lines come before where the hunk was looked for: each hunk is found after the one before it, and after its "@@" lines.'
173
+ : '';
174
+ return `\nThe file has, near line ${String(first + 1)}:\n${shown.join('\n')}${order}`;
135
175
  }
136
176
  /**
137
177
  * Where a hunk's anchor lines are, one after another, so its change is found after them. The
@@ -152,13 +192,13 @@ function seekAnchor(lines, hunk, from, path, n) {
152
192
  return cursor;
153
193
  }
154
194
  /**
155
- * Applies the hunks of a V4A `update_file` diff to `content`, in order, each after the one before
156
- * it. A hunk with no anchor must match exactly once in the part of the file it searches, or
157
- * nothing is changed: a hunk is never applied where the model did not mean it. After an anchor,
158
- * it is the first match: the anchor already says where. Each line the hunk does not change is
159
- * written back as it was, ending included, and an added line ends like the line before it.
195
+ * Applies the hunks of a V4A `update_file` diff to `content`, in order, each at its first match
196
+ * after the one before it and after its anchor, as Codex does: the order of the hunks says which
197
+ * of two alike places is meant. Each line the hunk does not change is written back as it was,
198
+ * ending included, and an added line ends like the line before it.
160
199
  *
161
- * @throws {EditRefusedError} naming the hunk, what it expected and what the file has near it.
200
+ * @throws {EditRefusedError} naming the hunk, what it expected and the lines of the file most
201
+ * like it.
162
202
  */
163
203
  export function applyUpdate(content, diff, path) {
164
204
  checkSize(diff, path);
@@ -181,11 +221,9 @@ export function applyUpdate(content, diff, path) {
181
221
  cursor = at + added.length;
182
222
  continue;
183
223
  }
184
- const at = seek(texts, old, { from, atEnd: hunk.atEnd, once: hunk.anchor === undefined });
224
+ const at = seek(texts, old, { from, atEnd: hunk.atEnd, once: false });
185
225
  if (typeof at !== 'number') {
186
- throw new EditRefusedError(at === null
187
- ? `Hunk ${String(n)} of the patch for "${path}" does not match the file. It expected:\n${quote(old)}${near(texts, hunk)}`
188
- : `Hunk ${String(n)} of the patch for "${path}" matches ${String(at.places)} places; add an "@@" line naming the function or more context`);
226
+ throw new EditRefusedError(`Hunk ${String(n)} of the patch for "${path}" does not match the file. It expected:\n${quote(old)}${near(texts, hunk, from)}`);
189
227
  }
190
228
  // A kept line stays as the file has it, even where the patch copied it with a space lost. An
191
229
  // added line that takes a removed one's place takes its ending too.
@@ -1 +1 @@
1
- {"version":3,"file":"v4aPatch.js","sourceRoot":"","sources":["../../../src/tools/domain/v4aPatch.ts"],"names":[],"mappings":"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","sourcesContent":["import { EditRefusedError } from './fileEdits.ts';\nimport { addedLine, joinLines, type Line, splitLines, textsOf } from './textLines.ts';\n\n// A patch is a few changes, not a file: one far larger is more likely a mistake.\nconst maxDiffBytes = 512 * 1024;\nconst maxHunks = 500;\n// What an error quotes of a hunk or of the file, to fix the call without flooding the context.\nconst quotedLines = 8;\n\n/** One line of a hunk: kept as the file has it, removed, or added. */\ninterface Step {\n\tkind: 'keep' | 'remove' | 'add';\n\ttext: string;\n}\n\n/** One change of a V4A diff: the lines it expects, and the lines it puts in their place. */\ninterface Hunk {\n\t/** The text after `@@`, such as a function's first line, found before the change. */\n\tanchor: string | undefined;\n\tsteps: Step[];\n\t/** `*** End of File`: the change is at the end of the file. */\n\tatEnd: boolean;\n}\n\n/** The lines a hunk expects in the file: the ones it keeps and the ones it removes. */\nfunction expected(hunk: Hunk): string[] {\n\treturn hunk.steps.filter(step => step.kind !== 'add').map(step => step.text);\n}\n\nfunction changes(hunk: Hunk): boolean {\n\treturn hunk.steps.some(step => step.kind !== 'keep');\n}\n\nfunction checkSize(diff: string, path: string): void {\n\tif (Buffer.byteLength(diff) > maxDiffBytes) {\n\t\tthrow new EditRefusedError(\n\t\t\t`The patch for \"${path}\" is larger than ${String(maxDiffBytes / 1024)} KB; send smaller patches`,\n\t\t);\n\t}\n}\n\n/** The diff's lines, without the newline that ends the last one. */\nfunction diffLines(diff: string): string[] {\n\tconst lines = diff.split('\\n').map(line => (line.endsWith('\\r') ? line.slice(0, -1) : line));\n\tif (lines.at(-1) === '') lines.pop();\n\treturn lines;\n}\n\nfunction parseHunks(diff: string, path: string): Hunk[] {\n\tconst hunks: Hunk[] = [];\n\tlet current: Hunk | undefined;\n\tfor (const [index, line] of diffLines(diff).entries()) {\n\t\tif (line.startsWith('@@')) {\n\t\t\tconst anchor = line.slice(2).trim();\n\t\t\t// Two `@@` lines in a row name an anchor and then one inside it.\n\t\t\tif (current?.steps.length === 0 && !current.atEnd) {\n\t\t\t\tcurrent.anchor = [current.anchor, anchor].filter(Boolean).join('\\n') || undefined;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tcurrent = { anchor: anchor || undefined, steps: [], atEnd: false };\n\t\t\thunks.push(current);\n\t\t\tcontinue;\n\t\t}\n\t\tif (line === '*** End of File') {\n\t\t\tif (current) current.atEnd = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (current?.atEnd) {\n\t\t\tthrow new EditRefusedError(\n\t\t\t\t`Line ${String(index + 1)} of the patch for \"${path}\" follows \"*** End of File\", which ends its hunk; start another hunk with \"@@\"`,\n\t\t\t);\n\t\t}\n\t\t// A diff may open with its first change, without `@@`.\n\t\tif (!current) {\n\t\t\tcurrent = { anchor: undefined, steps: [], atEnd: false };\n\t\t\thunks.push(current);\n\t\t}\n\t\tconst marker = line[0];\n\t\tconst text = line.slice(1);\n\t\t// An empty line is an empty context line whose space was trimmed.\n\t\tif (line === '' || marker === ' ') {\n\t\t\tcurrent.steps.push({ kind: 'keep', text });\n\t\t} else if (marker === '-') {\n\t\t\tcurrent.steps.push({ kind: 'remove', text });\n\t\t} else if (marker === '+') {\n\t\t\tcurrent.steps.push({ kind: 'add', text });\n\t\t} else {\n\t\t\tthrow new EditRefusedError(\n\t\t\t\t`Line ${String(index + 1)} of the patch for \"${path}\" starts with \"${marker ?? ''}\"; each line of a hunk starts with \" \", \"-\" or \"+\"`,\n\t\t\t);\n\t\t}\n\t}\n\tconst changing = hunks.filter(changes);\n\tif (changing.length === 0) {\n\t\tthrow new EditRefusedError(`The patch for \"${path}\" changes nothing`);\n\t}\n\tif (changing.length > maxHunks) {\n\t\tthrow new EditRefusedError(\n\t\t\t`The patch for \"${path}\" has more than ${String(maxHunks)} hunks; send smaller patches`,\n\t\t);\n\t}\n\treturn changing;\n}\n\n// Each pass forgives a little more: the model may copy a line with its trailing spaces lost.\nconst passes: ((line: string) => string)[] = [\n\tline => line,\n\tline => line.trimEnd(),\n\tline => line.trim(),\n];\n\n/**\n * Where `pattern` starts in `lines`, at or after `from`, by the first pass that finds it. `null`\n * when no pass does. With `once`, the first pass that matches must find one place, or the number\n * of places is returned; without it, the first place is taken.\n */\nfunction seek(\n\tlines: string[],\n\tpattern: string[],\n\t{ from, atEnd, once }: { from: number; atEnd: boolean; once: boolean },\n): number | null | { places: number } {\n\tconst last = lines.length - pattern.length;\n\tfor (const normalize of passes) {\n\t\tconst wanted = pattern.map(normalize);\n\t\tconst matchesAt = (start: number) =>\n\t\t\twanted.every((line, offset) => normalize(lines[start + offset] ?? '') === line);\n\t\tif (atEnd) {\n\t\t\tif (last >= from && matchesAt(last)) return last;\n\t\t\tcontinue;\n\t\t}\n\t\tconst found: number[] = [];\n\t\tfor (let start = from; start <= last; start++) {\n\t\t\tif (!matchesAt(start)) continue;\n\t\t\tif (!once) return start;\n\t\t\tfound.push(start);\n\t\t}\n\t\tif (found.length === 1) return found[0] ?? null;\n\t\tif (found.length > 1) return { places: found.length };\n\t}\n\treturn null;\n}\n\nfunction quote(lines: string[]): string {\n\tconst shown = lines.slice(0, quotedLines).map(line => ` ${line}`);\n\tif (lines.length > quotedLines) shown.push(` … ${String(lines.length - quotedLines)} more`);\n\treturn shown.join('\\n');\n}\n\n/** The real lines near where a hunk's first line appears, so the model can copy them. */\nfunction near(lines: string[], hunk: Hunk): string {\n\tconst first = expected(hunk)\n\t\t.find(line => line.trim() !== '')\n\t\t?.trim();\n\tconst at = first === undefined ? -1 : lines.findIndex(line => line.trim() === first);\n\tif (at === -1) return '';\n\tconst from = Math.max(0, at - 1);\n\tconst shown = lines\n\t\t.slice(from, from + quotedLines)\n\t\t.map((line, offset) => ` ${String(from + offset + 1)}: ${line}`);\n\treturn `\\nThe file has, near line ${String(at + 1)}:\\n${shown.join('\\n')}`;\n}\n\n/**\n * Where a hunk's anchor lines are, one after another, so its change is found after them. The\n * first must appear once; each one after it is the first that follows the one before, as V4A\n * reads `@@ class B:` then `@@ def __init__(self):`.\n */\nfunction seekAnchor(lines: string[], hunk: Hunk, from: number, path: string, n: number): number {\n\tlet cursor = from;\n\tfor (const [index, anchor] of (hunk.anchor?.split('\\n') ?? []).entries()) {\n\t\tconst at = seek(lines, [anchor], { from: cursor, atEnd: false, once: index === 0 });\n\t\tif (typeof at !== 'number') {\n\t\t\tthrow new EditRefusedError(\n\t\t\t\tat === null\n\t\t\t\t\t? `Hunk ${String(n)} of the patch for \"${path}\" names \"@@ ${anchor}\", which is not in the file after the hunks before it`\n\t\t\t\t\t: `Hunk ${String(n)} of the patch for \"${path}\" names \"@@ ${anchor}\", which appears ${String(at.places)} times; name a line that appears once, or put an \"@@\" line before it naming what holds it`,\n\t\t\t);\n\t\t}\n\t\tcursor = at + 1;\n\t}\n\treturn cursor;\n}\n\n/**\n * Applies the hunks of a V4A `update_file` diff to `content`, in order, each after the one before\n * it. A hunk with no anchor must match exactly once in the part of the file it searches, or\n * nothing is changed: a hunk is never applied where the model did not mean it. After an anchor,\n * it is the first match: the anchor already says where. Each line the hunk does not change is\n * written back as it was, ending included, and an added line ends like the line before it.\n *\n * @throws {EditRefusedError} naming the hunk, what it expected and what the file has near it.\n */\nexport function applyUpdate(content: string, diff: string, path: string): string {\n\tcheckSize(diff, path);\n\tconst text = splitLines(content);\n\tconst lines: Line[] = [...text.lines];\n\tlet cursor = 0;\n\tfor (const [index, hunk] of parseHunks(diff, path).entries()) {\n\t\tconst n = index + 1;\n\t\tconst texts = textsOf(lines);\n\t\tconst from = seekAnchor(texts, hunk, cursor, path, n);\n\t\tconst old = expected(hunk);\n\t\tif (old.length === 0) {\n\t\t\t// Only added lines: after the anchor, or at the end.\n\t\t\tconst at = hunk.anchor === undefined || hunk.atEnd ? lines.length : from;\n\t\t\tconst added: Line[] = [];\n\t\t\tfor (const step of hunk.steps) {\n\t\t\t\tadded.push(addedLine(step.text, added.at(-1) ?? lines[at - 1], text.eol));\n\t\t\t}\n\t\t\tlines.splice(at, 0, ...added);\n\t\t\tcursor = at + added.length;\n\t\t\tcontinue;\n\t\t}\n\t\tconst at = seek(texts, old, { from, atEnd: hunk.atEnd, once: hunk.anchor === undefined });\n\t\tif (typeof at !== 'number') {\n\t\t\tthrow new EditRefusedError(\n\t\t\t\tat === null\n\t\t\t\t\t? `Hunk ${String(n)} of the patch for \"${path}\" does not match the file. It expected:\\n${quote(old)}${near(texts, hunk)}`\n\t\t\t\t\t: `Hunk ${String(n)} of the patch for \"${path}\" matches ${String(at.places)} places; add an \"@@\" line naming the function or more context`,\n\t\t\t);\n\t\t}\n\t\t// A kept line stays as the file has it, even where the patch copied it with a space lost. An\n\t\t// added line that takes a removed one's place takes its ending too.\n\t\tconst replacement: Line[] = [];\n\t\tconst replaced: Line[] = [];\n\t\tlet offset = 0;\n\t\tfor (const step of hunk.steps) {\n\t\t\tif (step.kind === 'remove') {\n\t\t\t\tconst removed = lines[at + offset++];\n\t\t\t\tif (removed) replaced.push(removed);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (step.kind === 'keep') {\n\t\t\t\treplaced.length = 0;\n\t\t\t\tconst kept = lines[at + offset++];\n\t\t\t\treplacement.push(kept ?? addedLine(step.text, replacement.at(-1), text.eol));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst before = replaced.shift() ?? replacement.at(-1) ?? lines[at - 1];\n\t\t\treplacement.push(addedLine(step.text, before, text.eol));\n\t\t}\n\t\tlines.splice(at, old.length, ...replacement);\n\t\tcursor = at + replacement.length;\n\t}\n\treturn joinLines({ ...text, lines, finalNewline: text.finalNewline || text.lines.length === 0 });\n}\n\n/**\n * The content of a file a V4A `create_file` diff makes: every line starts with `+`. It ends with\n * a newline, as a text file does.\n *\n * @throws {EditRefusedError} when a line is not an added one.\n */\nexport function createdContent(diff: string, path: string): string {\n\tcheckSize(diff, path);\n\tconst lines = diffLines(diff).filter(line => line !== '*** End of File');\n\tfor (const [index, line] of lines.entries()) {\n\t\tif (!line.startsWith('+')) {\n\t\t\tthrow new EditRefusedError(\n\t\t\t\t`Line ${String(index + 1)} of the patch creating \"${path}\" does not start with \"+\"; every line of a new file does`,\n\t\t\t);\n\t\t}\n\t}\n\treturn joinLines({\n\t\tlines: lines.map(line => ({ text: line.slice(1), end: '\\n' })),\n\t\teol: '\\n',\n\t\tfinalNewline: true,\n\t});\n}\n"]}
1
+ {"version":3,"file":"v4aPatch.js","sourceRoot":"","sources":["../../../src/tools/domain/v4aPatch.ts"],"names":[],"mappings":"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","sourcesContent":["import { EditRefusedError } from './fileEdits.ts';\nimport { addedLine, joinLines, type Line, splitLines, textsOf } from './textLines.ts';\n\n// A patch is a few changes, not a file: one far larger is more likely a mistake.\nconst maxDiffBytes = 512 * 1024;\nconst maxHunks = 500;\n// What an error quotes of a hunk or of the file, to fix the call without flooding the context.\nconst quotedLines = 8;\nconst nearLines = 16;\n\n/** One line of a hunk: kept as the file has it, removed, or added. */\ninterface Step {\n\tkind: 'keep' | 'remove' | 'add';\n\ttext: string;\n}\n\n/** One change of a V4A diff: the lines it expects, and the lines it puts in their place. */\ninterface Hunk {\n\t/** The text after `@@`, such as a function's first line, found before the change. */\n\tanchor: string | undefined;\n\tsteps: Step[];\n\t/** `*** End of File`: the change is at the end of the file. */\n\tatEnd: boolean;\n}\n\n/** The lines a hunk expects in the file: the ones it keeps and the ones it removes. */\nfunction expected(hunk: Hunk): string[] {\n\treturn hunk.steps.filter(step => step.kind !== 'add').map(step => step.text);\n}\n\nfunction changes(hunk: Hunk): boolean {\n\treturn hunk.steps.some(step => step.kind !== 'keep');\n}\n\nfunction checkSize(diff: string, path: string): void {\n\tif (Buffer.byteLength(diff) > maxDiffBytes) {\n\t\tthrow new EditRefusedError(\n\t\t\t`The patch for \"${path}\" is larger than ${String(maxDiffBytes / 1024)} KB; send smaller patches`,\n\t\t);\n\t}\n}\n\n/** The diff's lines, without the newline that ends the last one. */\nfunction diffLines(diff: string): string[] {\n\tconst lines = diff.split('\\n').map(line => (line.endsWith('\\r') ? line.slice(0, -1) : line));\n\tif (lines.at(-1) === '') lines.pop();\n\treturn lines;\n}\n\nfunction parseHunks(diff: string, path: string): Hunk[] {\n\tconst hunks: Hunk[] = [];\n\tlet current: Hunk | undefined;\n\tfor (const [index, line] of diffLines(diff).entries()) {\n\t\tif (line.startsWith('@@')) {\n\t\t\tconst anchor = line.slice(2).trim();\n\t\t\t// Two `@@` lines in a row name an anchor and then one inside it.\n\t\t\tif (current?.steps.length === 0 && !current.atEnd) {\n\t\t\t\tcurrent.anchor = [current.anchor, anchor].filter(Boolean).join('\\n') || undefined;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tcurrent = { anchor: anchor || undefined, steps: [], atEnd: false };\n\t\t\thunks.push(current);\n\t\t\tcontinue;\n\t\t}\n\t\tif (line === '*** End of File') {\n\t\t\tif (current) current.atEnd = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (current?.atEnd) {\n\t\t\tthrow new EditRefusedError(\n\t\t\t\t`Line ${String(index + 1)} of the patch for \"${path}\" follows \"*** End of File\", which ends its hunk; start another hunk with \"@@\"`,\n\t\t\t);\n\t\t}\n\t\t// A diff may open with its first change, without `@@`.\n\t\tif (!current) {\n\t\t\tcurrent = { anchor: undefined, steps: [], atEnd: false };\n\t\t\thunks.push(current);\n\t\t}\n\t\tconst marker = line[0];\n\t\tconst text = line.slice(1);\n\t\t// An empty line is an empty context line whose space was trimmed.\n\t\tif (line === '' || marker === ' ') {\n\t\t\tcurrent.steps.push({ kind: 'keep', text });\n\t\t} else if (marker === '-') {\n\t\t\tcurrent.steps.push({ kind: 'remove', text });\n\t\t} else if (marker === '+') {\n\t\t\tcurrent.steps.push({ kind: 'add', text });\n\t\t} else {\n\t\t\tthrow new EditRefusedError(\n\t\t\t\t`Line ${String(index + 1)} of the patch for \"${path}\" starts with \"${marker ?? ''}\"; each line of a hunk starts with \" \", \"-\" or \"+\"`,\n\t\t\t);\n\t\t}\n\t}\n\tconst changing = hunks.filter(changes);\n\tif (changing.length === 0) {\n\t\tthrow new EditRefusedError(`The patch for \"${path}\" changes nothing`);\n\t}\n\tif (changing.length > maxHunks) {\n\t\tthrow new EditRefusedError(\n\t\t\t`The patch for \"${path}\" has more than ${String(maxHunks)} hunks; send smaller patches`,\n\t\t);\n\t}\n\treturn changing;\n}\n\n// Each pass forgives a little more: the model may copy a line with its trailing spaces lost.\nconst passes: ((line: string) => string)[] = [\n\tline => line,\n\tline => line.trimEnd(),\n\tline => line.trim(),\n];\n\n/**\n * Where `pattern` starts in `lines`, at or after `from`, by the first pass that finds it. `null`\n * when no pass does. With `once`, the first pass that matches must find one place, or the number\n * of places is returned; without it, the first place is taken.\n */\nfunction seek(\n\tlines: string[],\n\tpattern: string[],\n\t{ from, atEnd, once }: { from: number; atEnd: boolean; once: boolean },\n): number | null | { places: number } {\n\tconst last = lines.length - pattern.length;\n\tfor (const normalize of passes) {\n\t\tconst wanted = pattern.map(normalize);\n\t\tconst matchesAt = (start: number) =>\n\t\t\twanted.every((line, offset) => normalize(lines[start + offset] ?? '') === line);\n\t\tif (atEnd) {\n\t\t\tif (last >= from && matchesAt(last)) return last;\n\t\t\tcontinue;\n\t\t}\n\t\tconst found: number[] = [];\n\t\tfor (let start = from; start <= last; start++) {\n\t\t\tif (!matchesAt(start)) continue;\n\t\t\tif (!once) return start;\n\t\t\tfound.push(start);\n\t\t}\n\t\tif (found.length === 1) return found[0] ?? null;\n\t\tif (found.length > 1) return { places: found.length };\n\t}\n\treturn null;\n}\n\nfunction quote(lines: string[]): string {\n\tconst shown = lines.slice(0, quotedLines).map(line => ` ${line}`);\n\tif (lines.length > quotedLines) shown.push(` … ${String(lines.length - quotedLines)} more`);\n\treturn shown.join('\\n');\n}\n\n/**\n * The stretch of the file most like a hunk that did not match, so the model can copy its real\n * lines: as long as the hunk, holding the most of its lines in any order, so a line changed or\n * left out does not hide it. On a tie, the first at or after where the hunk was looked for.\n */\nfunction near(lines: string[], hunk: Hunk, from: number): string {\n\tconst old = expected(hunk);\n\tconst wanted = new Set(old.map(line => line.trim()).filter(line => line !== ''));\n\t// The stretch slides one line at a time, counting the hunk's lines in it as they enter and\n\t// leave, so a large hunk that failed costs no more than reading the file once.\n\tconst inStretch = new Map<string, number>();\n\tlet score = 0;\n\tconst slide = (line: string | undefined, by: 1 | -1) => {\n\t\tconst text = line?.trim();\n\t\tif (text === undefined || !wanted.has(text)) return;\n\t\tconst count = (inStretch.get(text) ?? 0) + by;\n\t\tinStretch.set(text, count);\n\t\tif (count === (by === 1 ? 1 : 0)) score += by;\n\t};\n\tfor (let at = 0; at < old.length; at++) slide(lines[at], 1);\n\tlet best = { at: -1, score: 0 };\n\tfor (let at = 0; at < lines.length; at++) {\n\t\tif (at > 0) {\n\t\t\tslide(lines[at - 1], -1);\n\t\t\tslide(lines[at + old.length - 1], 1);\n\t\t}\n\t\tconst later = best.at < from && at >= from;\n\t\tif (score > best.score || (score > 0 && score === best.score && later)) best = { at, score };\n\t}\n\tif (best.at === -1) return '';\n\t// From the first of the hunk's lines in the stretch to the last, with a line around them.\n\tconst held = (line: string | undefined) => line !== undefined && wanted.has(line.trim());\n\tlet first = best.at;\n\twhile (!held(lines[first])) first++;\n\tlet last = Math.min(best.at + old.length, lines.length) - 1;\n\twhile (!held(lines[last])) last--;\n\tconst start = Math.max(0, first - 1);\n\tconst end = Math.min(lines.length, last + 2, start + nearLines);\n\tconst shown = lines\n\t\t.slice(start, end)\n\t\t.map((line, offset) => ` ${String(start + offset + 1)}: ${line}`);\n\tconst order =\n\t\tfirst < from\n\t\t\t? '\\nThose lines come before where the hunk was looked for: each hunk is found after the one before it, and after its \"@@\" lines.'\n\t\t\t: '';\n\treturn `\\nThe file has, near line ${String(first + 1)}:\\n${shown.join('\\n')}${order}`;\n}\n\n/**\n * Where a hunk's anchor lines are, one after another, so its change is found after them. The\n * first must appear once; each one after it is the first that follows the one before, as V4A\n * reads `@@ class B:` then `@@ def __init__(self):`.\n */\nfunction seekAnchor(lines: string[], hunk: Hunk, from: number, path: string, n: number): number {\n\tlet cursor = from;\n\tfor (const [index, anchor] of (hunk.anchor?.split('\\n') ?? []).entries()) {\n\t\tconst at = seek(lines, [anchor], { from: cursor, atEnd: false, once: index === 0 });\n\t\tif (typeof at !== 'number') {\n\t\t\tthrow new EditRefusedError(\n\t\t\t\tat === null\n\t\t\t\t\t? `Hunk ${String(n)} of the patch for \"${path}\" names \"@@ ${anchor}\", which is not in the file after the hunks before it`\n\t\t\t\t\t: `Hunk ${String(n)} of the patch for \"${path}\" names \"@@ ${anchor}\", which appears ${String(at.places)} times; name a line that appears once, or put an \"@@\" line before it naming what holds it`,\n\t\t\t);\n\t\t}\n\t\tcursor = at + 1;\n\t}\n\treturn cursor;\n}\n\n/**\n * Applies the hunks of a V4A `update_file` diff to `content`, in order, each at its first match\n * after the one before it and after its anchor, as Codex does: the order of the hunks says which\n * of two alike places is meant. Each line the hunk does not change is written back as it was,\n * ending included, and an added line ends like the line before it.\n *\n * @throws {EditRefusedError} naming the hunk, what it expected and the lines of the file most\n * like it.\n */\nexport function applyUpdate(content: string, diff: string, path: string): string {\n\tcheckSize(diff, path);\n\tconst text = splitLines(content);\n\tconst lines: Line[] = [...text.lines];\n\tlet cursor = 0;\n\tfor (const [index, hunk] of parseHunks(diff, path).entries()) {\n\t\tconst n = index + 1;\n\t\tconst texts = textsOf(lines);\n\t\tconst from = seekAnchor(texts, hunk, cursor, path, n);\n\t\tconst old = expected(hunk);\n\t\tif (old.length === 0) {\n\t\t\t// Only added lines: after the anchor, or at the end.\n\t\t\tconst at = hunk.anchor === undefined || hunk.atEnd ? lines.length : from;\n\t\t\tconst added: Line[] = [];\n\t\t\tfor (const step of hunk.steps) {\n\t\t\t\tadded.push(addedLine(step.text, added.at(-1) ?? lines[at - 1], text.eol));\n\t\t\t}\n\t\t\tlines.splice(at, 0, ...added);\n\t\t\tcursor = at + added.length;\n\t\t\tcontinue;\n\t\t}\n\t\tconst at = seek(texts, old, { from, atEnd: hunk.atEnd, once: false });\n\t\tif (typeof at !== 'number') {\n\t\t\tthrow new EditRefusedError(\n\t\t\t\t`Hunk ${String(n)} of the patch for \"${path}\" does not match the file. It expected:\\n${quote(old)}${near(texts, hunk, from)}`,\n\t\t\t);\n\t\t}\n\t\t// A kept line stays as the file has it, even where the patch copied it with a space lost. An\n\t\t// added line that takes a removed one's place takes its ending too.\n\t\tconst replacement: Line[] = [];\n\t\tconst replaced: Line[] = [];\n\t\tlet offset = 0;\n\t\tfor (const step of hunk.steps) {\n\t\t\tif (step.kind === 'remove') {\n\t\t\t\tconst removed = lines[at + offset++];\n\t\t\t\tif (removed) replaced.push(removed);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (step.kind === 'keep') {\n\t\t\t\treplaced.length = 0;\n\t\t\t\tconst kept = lines[at + offset++];\n\t\t\t\treplacement.push(kept ?? addedLine(step.text, replacement.at(-1), text.eol));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst before = replaced.shift() ?? replacement.at(-1) ?? lines[at - 1];\n\t\t\treplacement.push(addedLine(step.text, before, text.eol));\n\t\t}\n\t\tlines.splice(at, old.length, ...replacement);\n\t\tcursor = at + replacement.length;\n\t}\n\treturn joinLines({ ...text, lines, finalNewline: text.finalNewline || text.lines.length === 0 });\n}\n\n/**\n * The content of a file a V4A `create_file` diff makes: every line starts with `+`. It ends with\n * a newline, as a text file does.\n *\n * @throws {EditRefusedError} when a line is not an added one.\n */\nexport function createdContent(diff: string, path: string): string {\n\tcheckSize(diff, path);\n\tconst lines = diffLines(diff).filter(line => line !== '*** End of File');\n\tfor (const [index, line] of lines.entries()) {\n\t\tif (!line.startsWith('+')) {\n\t\t\tthrow new EditRefusedError(\n\t\t\t\t`Line ${String(index + 1)} of the patch creating \"${path}\" does not start with \"+\"; every line of a new file does`,\n\t\t\t);\n\t\t}\n\t}\n\treturn joinLines({\n\t\tlines: lines.map(line => ({ text: line.slice(1), end: '\\n' })),\n\t\teol: '\\n',\n\t\tfinalNewline: true,\n\t});\n}\n"]}
@@ -26,8 +26,8 @@ export type EditRequest = {
26
26
  * - a file changed since it read it is `STALE_FILE`;
27
27
  * - a new file needs a path that does not exist.
28
28
  *
29
- * Once applied, and formatted when the workspace has a formatter, the conversation knows the
30
- * file as it is then, so it can edit it again without reading it. What it returns to the model
31
- * is short: the lines around a change, or a confirmation.
29
+ * Once applied, the conversation knows the file as it wrote it, so it can edit it again without
30
+ * reading it, unless the formatter rewrote it. What it returns to the model is short: the lines
31
+ * around a change, or a confirmation, and the lines the formatter rewrote.
32
32
  */
33
33
  export declare function prepareTrackedEdit(writes: WorkspaceWrites, request: EditRequest, signal: AbortSignal, call: ToolCallContext): Promise<PreparedCall>;
@@ -1,5 +1,5 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { describeCreation, describeDeletion, describeModification, editDiff, } from "../domain/editResponse.js";
2
+ import { describeCreation, describeDeletion, describeFormatting, describeModification, editDiff, } from "../domain/editResponse.js";
3
3
  import { EditRefusedError } from "../domain/fileEdits.js";
4
4
  import { editRisk } from "./fileEditor.js";
5
5
  function hashOf(content) {
@@ -7,8 +7,9 @@ function hashOf(content) {
7
7
  }
8
8
  /**
9
9
  * What the file holds once the formatter, if there is one, ran on what the edit wrote, and what
10
- * the model needs to know when it may not be formatted. The conversation knows the file as it is
11
- * now, so it can edit it again without reading it.
10
+ * the model needs to know when it may not be formatted. A file as the edit wrote it is known to
11
+ * the conversation, so it can edit it again without reading it. One the formatter rewrote is not:
12
+ * the model would edit on from the lines it sent, so it must read the file again.
12
13
  */
13
14
  async function afterFormatting(writes, editor, { path, absolute, written }, { run, reads }, signal) {
14
15
  const { formatter } = writes;
@@ -19,10 +20,10 @@ async function afterFormatting(writes, editor, { path, absolute, written }, { ru
19
20
  }
20
21
  const failure = await formatter.format(absolute, writes.sessionFor(run), signal);
21
22
  const { content } = await editor.current(path, signal);
22
- if (content === undefined)
23
- reads.forget(absolute);
24
- else
23
+ if (content?.equals(written))
25
24
  reads.record(absolute, hashOf(content));
25
+ else
26
+ reads.forget(absolute);
26
27
  return { content, note: failure === undefined ? '' : `\n${failure}` };
27
28
  }
28
29
  /**
@@ -32,9 +33,9 @@ async function afterFormatting(writes, editor, { path, absolute, written }, { ru
32
33
  * - a file changed since it read it is `STALE_FILE`;
33
34
  * - a new file needs a path that does not exist.
34
35
  *
35
- * Once applied, and formatted when the workspace has a formatter, the conversation knows the
36
- * file as it is then, so it can edit it again without reading it. What it returns to the model
37
- * is short: the lines around a change, or a confirmation.
36
+ * Once applied, the conversation knows the file as it wrote it, so it can edit it again without
37
+ * reading it, unless the formatter rewrote it. What it returns to the model is short: the lines
38
+ * around a change, or a confirmation, and the lines the formatter rewrote.
38
39
  */
39
40
  export async function prepareTrackedEdit(writes, request, signal, call) {
40
41
  const { run, reads } = call;
@@ -48,7 +49,9 @@ export async function prepareTrackedEdit(writes, request, signal, call) {
48
49
  run: async (runSignal) => {
49
50
  await editor.apply(prepared, runSignal);
50
51
  const { content, note } = await afterFormatting(writes, editor, { path, absolute: prepared.target.absolute, written: request.content }, call, runSignal);
51
- return `${content === undefined ? goneAfterFormatting(path) : describeCreation(path, content)}${note}`;
52
+ return content === undefined
53
+ ? `${goneAfterFormatting(path)}${note}`
54
+ : `${describeCreation(path, content)}${describeFormatting(path, request.content.toString('utf8'), content.toString('utf8'))}${note}`;
52
55
  },
53
56
  };
54
57
  }
@@ -83,7 +86,10 @@ export async function prepareTrackedEdit(writes, request, signal, call) {
83
86
  run: async (runSignal) => {
84
87
  await editor.apply(prepared, runSignal);
85
88
  const { content: now, note } = await afterFormatting(writes, editor, { path, absolute: target.absolute, written: next }, call, runSignal);
86
- return `${now === undefined ? goneAfterFormatting(path) : describeModification(path, content.toString('utf8'), now.toString('utf8'))}${note}`;
89
+ if (now === undefined)
90
+ return `${goneAfterFormatting(path)}${note}`;
91
+ const formatted = now.toString('utf8');
92
+ return `${describeModification(path, content.toString('utf8'), formatted)}${describeFormatting(path, next.toString('utf8'), formatted)}${note}`;
87
93
  },
88
94
  };
89
95
  }
@@ -1 +1 @@
1
- {"version":3,"file":"trackedEdits.js","sourceRoot":"","sources":["../../../src/tools/infrastructure/trackedEdits.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'node:crypto';\nimport {\n\tdescribeCreation,\n\tdescribeDeletion,\n\tdescribeModification,\n\teditDiff,\n} from '../domain/editResponse.ts';\nimport { EditRefusedError } from '../domain/fileEdits.ts';\nimport type { PreparedCall } from '../domain/preparedCall.ts';\nimport type { ToolCallContext } from '../domain/readRegistry.ts';\nimport { editRisk, type FileEditor } from './fileEditor.ts';\nimport type { WorkspaceWrites } from './workspaceWrites.ts';\n\n/**\n * One edit, as an edit format asks for it, with the path as the model wrote it. A modification\n * works its new content out from what the file holds now, the version the model read; it throws\n * `EditRefusedError` with what the model needs to fix the call, such as a text that matched\n * twice.\n */\nexport type EditRequest =\n\t| { kind: 'create'; path: string; content: Buffer }\n\t| { kind: 'modify'; path: string; change: (current: Buffer) => Buffer }\n\t| { kind: 'delete'; path: string };\n\nfunction hashOf(content: Buffer): string {\n\treturn createHash('sha256').update(content).digest('hex');\n}\n\n/**\n * What the file holds once the formatter, if there is one, ran on what the edit wrote, and what\n * the model needs to know when it may not be formatted. The conversation knows the file as it is\n * now, so it can edit it again without reading it.\n */\nasync function afterFormatting(\n\twrites: WorkspaceWrites,\n\teditor: FileEditor,\n\t{ path, absolute, written }: { path: string; absolute: string; written: Buffer },\n\t{ run, reads }: ToolCallContext,\n\tsignal: AbortSignal,\n): Promise<{ content: Buffer | undefined; note: string }> {\n\tconst { formatter } = writes;\n\tif (!formatter) {\n\t\t// It wrote the content itself, so it has seen it.\n\t\treads.record(absolute, hashOf(written));\n\t\treturn { content: written, note: '' };\n\t}\n\tconst failure = await formatter.format(absolute, writes.sessionFor(run), signal);\n\tconst { content } = await editor.current(path, signal);\n\tif (content === undefined) reads.forget(absolute);\n\telse reads.record(absolute, hashOf(content));\n\treturn { content, note: failure === undefined ? '' : `\\n${failure}` };\n}\n\n/**\n * Prepares an edit from the version of the file this conversation read, and only from it:\n *\n * - a file it never read is `READ_REQUIRED`;\n * - a file changed since it read it is `STALE_FILE`;\n * - a new file needs a path that does not exist.\n *\n * Once applied, and formatted when the workspace has a formatter, the conversation knows the\n * file as it is then, so it can edit it again without reading it. What it returns to the model\n * is short: the lines around a change, or a confirmation.\n */\nexport async function prepareTrackedEdit(\n\twrites: WorkspaceWrites,\n\trequest: EditRequest,\n\tsignal: AbortSignal,\n\tcall: ToolCallContext,\n): Promise<PreparedCall> {\n\tconst { run, reads } = call;\n\tconst editor = writes.editorFor(run);\n\tconst { path } = request;\n\n\tif (request.kind === 'create') {\n\t\tconst prepared = await editor.prepare(\n\t\t\t{ kind: 'create', path, content: request.content },\n\t\t\tsignal,\n\t\t);\n\t\treturn {\n\t\t\trisk: editRisk(prepared),\n\t\t\tdiff: editDiff(path, undefined, request.content.toString('utf8')),\n\t\t\trun: async runSignal => {\n\t\t\t\tawait editor.apply(prepared, runSignal);\n\t\t\t\tconst { content, note } = await afterFormatting(\n\t\t\t\t\twrites,\n\t\t\t\t\teditor,\n\t\t\t\t\t{ path, absolute: prepared.target.absolute, written: request.content },\n\t\t\t\t\tcall,\n\t\t\t\t\trunSignal,\n\t\t\t\t);\n\t\t\t\treturn `${content === undefined ? goneAfterFormatting(path) : describeCreation(path, content)}${note}`;\n\t\t\t},\n\t\t};\n\t}\n\n\tconst { target, state, content } = await editor.current(path, signal);\n\tif (!state.exists || content === undefined) {\n\t\tthrow new EditRefusedError(`\"${path}\" does not exist`);\n\t}\n\tconst seen = reads.versionOf(target.absolute);\n\tif (seen === undefined) {\n\t\tthrow new EditRefusedError(`READ_REQUIRED: read \"${path}\" before changing it`);\n\t}\n\tif (seen !== state.hash) {\n\t\tthrow new EditRefusedError(`STALE_FILE: \"${path}\" changed since you read it; read it again`);\n\t}\n\n\tif (request.kind === 'delete') {\n\t\tconst prepared = await editor.prepare({ kind: 'delete', path, expected: seen }, signal);\n\t\treturn {\n\t\t\trisk: editRisk(prepared),\n\t\t\tdiff: editDiff(path, content.toString('utf8'), undefined),\n\t\t\trun: async runSignal => {\n\t\t\t\tawait editor.apply(prepared, runSignal);\n\t\t\t\treads.forget(target.absolute);\n\t\t\t\treturn describeDeletion(path);\n\t\t\t},\n\t\t};\n\t}\n\n\tconst next = request.change(content);\n\tconst prepared = await editor.prepare(\n\t\t{ kind: 'replace', path, content: next, expected: seen },\n\t\tsignal,\n\t);\n\treturn {\n\t\trisk: editRisk(prepared),\n\t\tdiff: editDiff(path, content.toString('utf8'), next.toString('utf8')),\n\t\trun: async runSignal => {\n\t\t\tawait editor.apply(prepared, runSignal);\n\t\t\tconst { content: now, note } = await afterFormatting(\n\t\t\t\twrites,\n\t\t\t\teditor,\n\t\t\t\t{ path, absolute: target.absolute, written: next },\n\t\t\t\tcall,\n\t\t\t\trunSignal,\n\t\t\t);\n\t\t\treturn `${now === undefined ? goneAfterFormatting(path) : describeModification(path, content.toString('utf8'), now.toString('utf8'))}${note}`;\n\t\t},\n\t};\n}\n\nfunction goneAfterFormatting(path: string): string {\n\treturn `Changed ${path}, but it no longer exists once the formatter ran.`;\n}\n"]}
1
+ {"version":3,"file":"trackedEdits.js","sourceRoot":"","sources":["../../../src/tools/infrastructure/trackedEdits.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from 'node:crypto';\nimport {\n\tdescribeCreation,\n\tdescribeDeletion,\n\tdescribeFormatting,\n\tdescribeModification,\n\teditDiff,\n} from '../domain/editResponse.ts';\nimport { EditRefusedError } from '../domain/fileEdits.ts';\nimport type { PreparedCall } from '../domain/preparedCall.ts';\nimport type { ToolCallContext } from '../domain/readRegistry.ts';\nimport { editRisk, type FileEditor } from './fileEditor.ts';\nimport type { WorkspaceWrites } from './workspaceWrites.ts';\n\n/**\n * One edit, as an edit format asks for it, with the path as the model wrote it. A modification\n * works its new content out from what the file holds now, the version the model read; it throws\n * `EditRefusedError` with what the model needs to fix the call, such as a text that matched\n * twice.\n */\nexport type EditRequest =\n\t| { kind: 'create'; path: string; content: Buffer }\n\t| { kind: 'modify'; path: string; change: (current: Buffer) => Buffer }\n\t| { kind: 'delete'; path: string };\n\nfunction hashOf(content: Buffer): string {\n\treturn createHash('sha256').update(content).digest('hex');\n}\n\n/**\n * What the file holds once the formatter, if there is one, ran on what the edit wrote, and what\n * the model needs to know when it may not be formatted. A file as the edit wrote it is known to\n * the conversation, so it can edit it again without reading it. One the formatter rewrote is not:\n * the model would edit on from the lines it sent, so it must read the file again.\n */\nasync function afterFormatting(\n\twrites: WorkspaceWrites,\n\teditor: FileEditor,\n\t{ path, absolute, written }: { path: string; absolute: string; written: Buffer },\n\t{ run, reads }: ToolCallContext,\n\tsignal: AbortSignal,\n): Promise<{ content: Buffer | undefined; note: string }> {\n\tconst { formatter } = writes;\n\tif (!formatter) {\n\t\t// It wrote the content itself, so it has seen it.\n\t\treads.record(absolute, hashOf(written));\n\t\treturn { content: written, note: '' };\n\t}\n\tconst failure = await formatter.format(absolute, writes.sessionFor(run), signal);\n\tconst { content } = await editor.current(path, signal);\n\tif (content?.equals(written)) reads.record(absolute, hashOf(content));\n\telse reads.forget(absolute);\n\treturn { content, note: failure === undefined ? '' : `\\n${failure}` };\n}\n\n/**\n * Prepares an edit from the version of the file this conversation read, and only from it:\n *\n * - a file it never read is `READ_REQUIRED`;\n * - a file changed since it read it is `STALE_FILE`;\n * - a new file needs a path that does not exist.\n *\n * Once applied, the conversation knows the file as it wrote it, so it can edit it again without\n * reading it, unless the formatter rewrote it. What it returns to the model is short: the lines\n * around a change, or a confirmation, and the lines the formatter rewrote.\n */\nexport async function prepareTrackedEdit(\n\twrites: WorkspaceWrites,\n\trequest: EditRequest,\n\tsignal: AbortSignal,\n\tcall: ToolCallContext,\n): Promise<PreparedCall> {\n\tconst { run, reads } = call;\n\tconst editor = writes.editorFor(run);\n\tconst { path } = request;\n\n\tif (request.kind === 'create') {\n\t\tconst prepared = await editor.prepare(\n\t\t\t{ kind: 'create', path, content: request.content },\n\t\t\tsignal,\n\t\t);\n\t\treturn {\n\t\t\trisk: editRisk(prepared),\n\t\t\tdiff: editDiff(path, undefined, request.content.toString('utf8')),\n\t\t\trun: async runSignal => {\n\t\t\t\tawait editor.apply(prepared, runSignal);\n\t\t\t\tconst { content, note } = await afterFormatting(\n\t\t\t\t\twrites,\n\t\t\t\t\teditor,\n\t\t\t\t\t{ path, absolute: prepared.target.absolute, written: request.content },\n\t\t\t\t\tcall,\n\t\t\t\t\trunSignal,\n\t\t\t\t);\n\t\t\t\treturn content === undefined\n\t\t\t\t\t? `${goneAfterFormatting(path)}${note}`\n\t\t\t\t\t: `${describeCreation(path, content)}${describeFormatting(path, request.content.toString('utf8'), content.toString('utf8'))}${note}`;\n\t\t\t},\n\t\t};\n\t}\n\n\tconst { target, state, content } = await editor.current(path, signal);\n\tif (!state.exists || content === undefined) {\n\t\tthrow new EditRefusedError(`\"${path}\" does not exist`);\n\t}\n\tconst seen = reads.versionOf(target.absolute);\n\tif (seen === undefined) {\n\t\tthrow new EditRefusedError(`READ_REQUIRED: read \"${path}\" before changing it`);\n\t}\n\tif (seen !== state.hash) {\n\t\tthrow new EditRefusedError(`STALE_FILE: \"${path}\" changed since you read it; read it again`);\n\t}\n\n\tif (request.kind === 'delete') {\n\t\tconst prepared = await editor.prepare({ kind: 'delete', path, expected: seen }, signal);\n\t\treturn {\n\t\t\trisk: editRisk(prepared),\n\t\t\tdiff: editDiff(path, content.toString('utf8'), undefined),\n\t\t\trun: async runSignal => {\n\t\t\t\tawait editor.apply(prepared, runSignal);\n\t\t\t\treads.forget(target.absolute);\n\t\t\t\treturn describeDeletion(path);\n\t\t\t},\n\t\t};\n\t}\n\n\tconst next = request.change(content);\n\tconst prepared = await editor.prepare(\n\t\t{ kind: 'replace', path, content: next, expected: seen },\n\t\tsignal,\n\t);\n\treturn {\n\t\trisk: editRisk(prepared),\n\t\tdiff: editDiff(path, content.toString('utf8'), next.toString('utf8')),\n\t\trun: async runSignal => {\n\t\t\tawait editor.apply(prepared, runSignal);\n\t\t\tconst { content: now, note } = await afterFormatting(\n\t\t\t\twrites,\n\t\t\t\teditor,\n\t\t\t\t{ path, absolute: target.absolute, written: next },\n\t\t\t\tcall,\n\t\t\t\trunSignal,\n\t\t\t);\n\t\t\tif (now === undefined) return `${goneAfterFormatting(path)}${note}`;\n\t\t\tconst formatted = now.toString('utf8');\n\t\t\treturn `${describeModification(path, content.toString('utf8'), formatted)}${describeFormatting(path, next.toString('utf8'), formatted)}${note}`;\n\t\t},\n\t};\n}\n\nfunction goneAfterFormatting(path: string): string {\n\treturn `Changed ${path}, but it no longer exists once the formatter ran.`;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikode13/harness",
3
- "version": "9.2.0",
3
+ "version": "9.3.0",
4
4
  "description": "Test/agent harness utility",
5
5
  "type": "module",
6
6
  "license": "SEE LICENSE IN LICENSE",