@mikode13/harness 11.1.0 → 11.2.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 +48 -10
- package/dist/agent/domain/agent.d.ts +17 -0
- package/dist/agent/domain/agent.js.map +1 -1
- package/dist/agent/domain/runContext.d.ts +5 -0
- package/dist/agent/domain/runContext.js.map +1 -1
- package/dist/engines/domain/model/compactor.d.ts +40 -0
- package/dist/engines/domain/model/compactor.js +5 -0
- package/dist/engines/domain/model/compactor.js.map +1 -0
- package/dist/engines/domain/model/llmAgent.d.ts +48 -2
- package/dist/engines/domain/model/llmAgent.js +351 -14
- package/dist/engines/domain/model/llmAgent.js.map +1 -1
- package/dist/engines/domain/model/recentCompaction.d.ts +39 -0
- package/dist/engines/domain/model/recentCompaction.js +119 -0
- package/dist/engines/domain/model/recentCompaction.js.map +1 -0
- package/dist/engines/domain/model/stepContext.js +3 -0
- package/dist/engines/domain/model/stepContext.js.map +1 -1
- package/dist/engines/domain/model/triagedCompaction.d.ts +27 -0
- package/dist/engines/domain/model/triagedCompaction.js +60 -0
- package/dist/engines/domain/model/triagedCompaction.js.map +1 -0
- package/dist/factory/infrastructure/agentLLMFactory.d.ts +40 -12
- package/dist/factory/infrastructure/agentLLMFactory.js +114 -18
- package/dist/factory/infrastructure/agentLLMFactory.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/llm/domain/contextTriage.d.ts +26 -0
- package/dist/llm/domain/contextTriage.js +3 -0
- package/dist/llm/domain/contextTriage.js.map +1 -0
- package/dist/llm/domain/llm.d.ts +18 -1
- package/dist/llm/domain/llm.js.map +1 -1
- package/dist/llm/domain/message.d.ts +18 -1
- package/dist/llm/domain/message.js +20 -0
- package/dist/llm/domain/message.js.map +1 -1
- package/dist/llm/infrastructure/claudeLLMClient.d.ts +4 -5
- package/dist/llm/infrastructure/claudeLLMClient.js +39 -13
- package/dist/llm/infrastructure/claudeLLMClient.js.map +1 -1
- package/dist/llm/infrastructure/jevContextTriage.d.ts +18 -0
- package/dist/llm/infrastructure/jevContextTriage.js +92 -0
- package/dist/llm/infrastructure/jevContextTriage.js.map +1 -0
- package/dist/llm/infrastructure/openAILLMClient.d.ts +4 -5
- package/dist/llm/infrastructure/openAILLMClient.js +20 -9
- package/dist/llm/infrastructure/openAILLMClient.js.map +1 -1
- package/dist/tools/domain/preparedCall.d.ts +2 -1
- package/dist/tools/domain/preparedCall.js +7 -1
- package/dist/tools/domain/preparedCall.js.map +1 -1
- package/dist/tools/domain/readRegistry.d.ts +11 -0
- package/dist/tools/domain/readRegistry.js.map +1 -1
- package/dist/tools/infrastructure/agentAsTool.d.ts +6 -0
- package/dist/tools/infrastructure/agentAsTool.js +9 -0
- package/dist/tools/infrastructure/agentAsTool.js.map +1 -1
- package/dist/tools/infrastructure/commandTool.js +4 -1
- package/dist/tools/infrastructure/commandTool.js.map +1 -1
- package/dist/tools/infrastructure/exploreTool.js +2 -0
- package/dist/tools/infrastructure/exploreTool.js.map +1 -1
- package/dist/tools/infrastructure/fileTools.js +1 -0
- package/dist/tools/infrastructure/fileTools.js.map +1 -1
- package/dist/tools/infrastructure/removeWorktreeTool.js +7 -1
- package/dist/tools/infrastructure/removeWorktreeTool.js.map +1 -1
- package/dist/tools/infrastructure/subagents.d.ts +3 -1
- package/dist/tools/infrastructure/subagents.js +2 -1
- package/dist/tools/infrastructure/subagents.js.map +1 -1
- package/dist/tools/infrastructure/trackedEdits.js +1 -0
- package/dist/tools/infrastructure/trackedEdits.js.map +1 -1
- package/dist/tools/infrastructure/workspaceWrites.d.ts +13 -1
- package/dist/tools/infrastructure/workspaceWrites.js +18 -1
- package/dist/tools/infrastructure/workspaceWrites.js.map +1 -1
- package/dist/tools/infrastructure/writeCeiling.d.ts +16 -0
- package/dist/tools/infrastructure/writeCeiling.js +49 -0
- package/dist/tools/infrastructure/writeCeiling.js.map +1 -0
- package/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preparedCall.js","sourceRoot":"","sources":["../../../src/tools/domain/preparedCall.ts"],"names":[],"mappings":"AAiDA;;;;GAIG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IACnC,QAAQ,CAAqB;IAEtC,YAAY,OAAe,EAAE,EAAE,QAAQ,KAA4B,EAAE;QACpE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC1B,CAAC;CACD;AAED;;;;GAIG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IACnC,OAAO,CAAU;IAE1B,YAAY,OAAgB;QAC3B,KAAK,CAAC,2CAA2C,CAAC,CAAC;QACnD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACxB,CAAC;CACD;AA4DD,MAAM,UAAU,YAAY,CAAC,IAAe;IAC3C,OAAO,UAAU,IAAI,IAAI,CAAC;AAC3B,CAAC;AAED,SAAS,WAAW,CAAC,IAAe;IACnC,OAAO,SAAS,IAAI,IAAI,CAAC;AAC1B,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"preparedCall.js","sourceRoot":"","sources":["../../../src/tools/domain/preparedCall.ts"],"names":[],"mappings":"AAiDA;;;;GAIG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IACnC,QAAQ,CAAqB;IAEtC,YAAY,OAAe,EAAE,EAAE,QAAQ,KAA4B,EAAE;QACpE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC1B,CAAC;CACD;AAED;;;;GAIG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IACnC,OAAO,CAAU;IAE1B,YAAY,OAAgB;QAC3B,KAAK,CAAC,2CAA2C,CAAC,CAAC;QACnD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACxB,CAAC;CACD;AA4DD,MAAM,UAAU,YAAY,CAAC,IAAe;IAC3C,OAAO,UAAU,IAAI,IAAI,CAAC;AAC3B,CAAC;AAED,SAAS,WAAW,CAAC,IAAe;IACnC,OAAO,SAAS,IAAI,IAAI,CAAC;AAC1B,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAChC,IAA0B,EAC1B,KAAc,EACd,MAAmB,EACnB,IAAqB;IAErB,IAAI,WAAW,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IAEhE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9B,6FAA6F;IAC7F,sFAAsF;IACtF,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,CAAC,YAAY,EAAE,MAAM,KAAK,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CACd,4EAA4E,IAAI,CAAC,IAAI,4JAA4J,CACjP,CAAC;IACH,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;AACrF,CAAC","sourcesContent":["import type { CommandLine, ToolRisk, WorktreeRemoval } from '#src/agent/domain/approval';\nimport type { ToolDefinition } from '#src/llm/domain/tool';\nimport type { ToolCallContext } from './readRegistry.ts';\nimport type { Tool, ToolSummarizer } from './tool.ts';\n\n/** How a configured command ended, for telling a command run again for nothing. */\nexport interface CommandEnding {\n\t/** It exited with code 0. */\n\tpassed: boolean;\n\t/** How it ended, such as \"exited with code 1\" or \"ran past its time limit\". */\n\thow: string;\n\t/** What it printed, or the end of it, without colors, as the user is shown it. */\n\toutput: string;\n\t/**\n\t * A hash of everything it printed, line by line, without colors or the times a known test\n\t * runner adds: what tells two endings apart.\n\t */\n\tfingerprint: string;\n\t/**\n\t * What the files the run changed held when it ended, as `RunJournal.contentState` gives it.\n\t * Missing when it changed something the history could not record.\n\t */\n\tstate?: string;\n\t/** The file of every change the run had applied by then, in order, as the model names it. */\n\tchanged: readonly string[];\n}\n\n/**\n * A tool call checked and fixed before anyone approves it: its risk is judged on what will\n * really happen, and `run` does exactly that, nothing worked out again.\n */\nexport interface PreparedCall {\n\treadonly risk: ToolRisk;\n\t/** For a change to a file, what it will change as a unified diff, fixed when prepared. */\n\treadonly diff?: string;\n\t/** For a call that runs a configured command, what it will run, fixed when prepared. */\n\treadonly command?: CommandLine;\n\t/** For a call that removes a worktree, which one and what is lost with it, when prepared. */\n\treadonly worktree?: WorktreeRemoval;\n\t/**\n\t * For a call that runs a configured command, how it ended once it has. `undefined` before it\n\t * runs, and when it never did.\n\t */\n\treadonly ending?: () => CommandEnding | undefined;\n\t/** A consumer's call: whatever it changes, the history does not record. */\n\treadonly unrecorded?: true;\n\trun(signal: AbortSignal): Promise<string>;\n}\n\n/**\n * A tool ran and produced a result the model should see as an error. Internal tools use this\n * instead of a plain error when the result also carries narrative metadata, such as a command's\n * non-zero exit code.\n */\nexport class ToolExecutionError extends Error {\n\treadonly exitCode: number | undefined;\n\n\tconstructor(message: string, { exitCode }: { exitCode?: number } = {}) {\n\t\tsuper(message);\n\t\tthis.exitCode = exitCode;\n\t}\n}\n\n/**\n * A failure inside a call that ends the run instead of the call, such as an approver that failed\n * while an agent the call started was asking it: host code failed, and the model must not carry\n * on as if a tool had. Internal.\n */\nexport class RunEndingToolError extends Error {\n\treadonly failure: unknown;\n\n\tconstructor(failure: unknown) {\n\t\tsuper('A failure inside a tool call ends the run');\n\t\tthis.failure = failure;\n\t}\n}\n\n/**\n * A tool built by the harness, such as a write tool, which prepares each call against the\n * state of the run before it is approved. Internal: consumers build a `Tool`.\n */\nexport interface PreparingTool extends ToolDefinition {\n\t/** Describes the model's input without exposing preparation details to the provider. */\n\tsummarize?: ToolSummarizer;\n\t/** Rejects with a message for the model when the call cannot run. */\n\tprepare(input: unknown, signal: AbortSignal, call: ToolCallContext): Promise<PreparedCall>;\n}\n\n/** What an agent a call handed a task to answered, for the model that sent it. */\nexport interface DelegatedAnswer {\n\treadonly text: string;\n\t/** It failed, and `text` says why. */\n\treadonly failed?: true;\n}\n\n/** An agent a call handed a task to, working in the background until it answers. */\nexport interface Delegated {\n\t/** Which agent it is, as its `subagent` events name it: `explorer`, or the tool's name. */\n\treadonly name: string;\n\t/** Its number among the agents of its call, from 1, as its `subagent` events carry it. */\n\treadonly subagent: number;\n\t/** The task it was given, as the model wrote it. */\n\treadonly task: string;\n\t/**\n\t * Its answer for the model that sent it, or why it failed. It rejects only when the run was\n\t * cancelled, or with a `RunEndingToolError`.\n\t */\n\treadonly answer: Promise<DelegatedAnswer>;\n}\n\n/**\n * A call that hands tasks to agents, checked before anyone approves it. Once approved, `start`\n * sends them and the call answers at once: they work in the background, and each one's answer\n * reaches the model when it finishes.\n */\nexport interface PreparedDelegation {\n\treadonly risk: ToolRisk;\n\t/** The agents may run a consumer's tools, whose changes the history does not record. */\n\treadonly unrecorded?: true;\n\tstart(signal: AbortSignal): readonly Delegated[];\n}\n\n/**\n * A tool built by the harness whose calls hand tasks to agents working in the background, such\n * as `explore`. Internal.\n */\nexport interface DelegatingTool extends ToolDefinition {\n\tsummarize?: ToolSummarizer;\n\t/** Rejects with a message for the model when the tasks cannot be handed over. */\n\tdelegate(input: unknown, signal: AbortSignal, call: ToolCallContext): Promise<PreparedDelegation>;\n}\n\n/** What an agent can run: a consumer's `Tool`, or one the harness built. */\nexport type AgentTool = Tool | PreparingTool | DelegatingTool;\n\nexport function isDelegating(tool: AgentTool): tool is DelegatingTool {\n\treturn 'delegate' in tool;\n}\n\nfunction isPreparing(tool: AgentTool): tool is PreparingTool {\n\treturn 'prepare' in tool;\n}\n\n/**\n * Brings any tool into the one lifecycle of prepare, approve, run. A consumer's `Tool` has its\n * risk judged now, and later runs on the input as the model sent it; one that is not `safe` is\n * refused for an agent started by an agent that only reads.\n */\nexport async function prepareCall(\n\ttool: Tool | PreparingTool,\n\tinput: unknown,\n\tsignal: AbortSignal,\n\tcall: ToolCallContext,\n): Promise<PreparedCall> {\n\tif (isPreparing(tool)) return tool.prepare(input, signal, call);\n\n\tconst risk = tool.risk(input);\n\t// Where a consumer's tool writes is unknown, so under an agent that only reads, one that may\n\t// write is refused whole. Under one that writes, it writes where the consumer let it.\n\tif (risk !== 'safe' && call.writeCeiling?.length === 0) {\n\t\tthrow new Error(\n\t\t\t`Nothing was changed: the agent that handed you this task only reads, and ${tool.name} may change things. An agent another agent started never writes more than that agent may. Answer with what you found, and say what you would have changed.`,\n\t\t);\n\t}\n\treturn { risk, unrecorded: true, run: runSignal => tool.execute(input, runSignal) };\n}\n"]}
|
|
@@ -65,4 +65,15 @@ export interface ToolCallContext {
|
|
|
65
65
|
readonly ask?: Asker;
|
|
66
66
|
/** How many delegations deep the agent that made the call runs: 0 for the top-level one. */
|
|
67
67
|
readonly depth: number;
|
|
68
|
+
/**
|
|
69
|
+
* The folders the call may write in, as real paths, set by the agents that delegated to the
|
|
70
|
+
* one that made it: empty when it may write nowhere, absent when none set a limit.
|
|
71
|
+
*/
|
|
72
|
+
readonly writeCeiling?: readonly string[];
|
|
73
|
+
/**
|
|
74
|
+
* The `write` roots of the workspace of the agent that made the call, as it was given them:
|
|
75
|
+
* an agent the call starts writes only within them. Empty when it only reads, absent when it
|
|
76
|
+
* has no workspace.
|
|
77
|
+
*/
|
|
78
|
+
readonly agentWriteRoots?: readonly string[];
|
|
68
79
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readRegistry.js","sourceRoot":"","sources":["../../../src/tools/domain/readRegistry.ts"],"names":[],"mappings":"AAMA;;;;;;;GAOG;AACH,MAAM,OAAO,YAAY;IAChB,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEzC,0FAA0F;IAC1F,KAAK;QACJ,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE;YACrC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QAClB,CAAC,CAAC,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,KAAK;QACJ,IAAI,CAAC,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC;IACvB,CAAC;CACD;AAED;;;;GAIG;AACH,MAAM,OAAO,QAAQ;IACH,IAAI,CAA8B;IAClC,GAAG,GAAG,IAAI,GAAG,EAA8B,CAAC;IAC5C,IAAI,CAAsC;IAE3D,YAAY,IAAiC,EAAE,IAAyC;QACvF,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,CAAC;IAED,mEAAmE;IACnE,SAAS,CAAC,QAAgB;QACzB,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAClF,CAAC;IAED,iFAAiF;IACjF,MAAM,CAAC,QAAgB,EAAE,IAAY;QACpC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED,sDAAsD;IACtD,MAAM,CAAC,QAAgB;QACtB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IACnC,CAAC;IAED,+DAA+D;IAC/D,MAAM;QACL,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChC,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;YACrC,IAAI,IAAI,KAAK,SAAS;gBAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;;gBACrC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC3B,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC;CACD","sourcesContent":["import type { ProgressEvent } from '#src/agent/domain/agent';\nimport type { Approver } from '#src/agent/domain/approval';\nimport type { Asker } from '#src/agent/domain/question';\nimport type { RunContext } from '#src/agent/domain/runContext';\nimport type { Tokens } from '#src/shared/domain/tokens';\n\n/**\n * What one conversation has seen of each file: the hash of the content a read showed it, or an\n * edit of its own left. An edit is made only from that version, so the model never changes a\n * file it has not read, or one that changed since.\n *\n * Keyed by the file's real path, so two spellings of one file are one entry. It belongs to the\n * conversation, not to the run: each role, with its own conversation, has its own.\n */\nexport class ReadRegistry {\n\tprivate seen = new Map<string, string>();\n\n\t/** The reads of one run, which count for the conversation only once the run succeeded. */\n\tbegin(): RunReads {\n\t\treturn new RunReads(this.seen, seen => {\n\t\t\tthis.seen = seen;\n\t\t});\n\t}\n\n\t/**\n\t * Forgets every file: the workspace moved through its history, so what the conversation saw\n\t * may no longer be what the files hold.\n\t */\n\tclear(): void {\n\t\tthis.seen = new Map();\n\t}\n}\n\n/**\n * The versions one run has seen, on top of what its conversation saw before it. A failed run\n * records nothing in its conversation, so the model does not remember what it read there: its\n * reads are kept only by `commit`.\n */\nexport class RunReads {\n\tprivate readonly base: ReadonlyMap<string, string>;\n\tprivate readonly own = new Map<string, string | undefined>();\n\tprivate readonly save: (seen: Map<string, string>) => void;\n\n\tconstructor(base: ReadonlyMap<string, string>, save: (seen: Map<string, string>) => void) {\n\t\tthis.base = base;\n\t\tthis.save = save;\n\t}\n\n\t/** The version of `absolute` the conversation has seen, if any. */\n\tversionOf(absolute: string): string | undefined {\n\t\treturn this.own.has(absolute) ? this.own.get(absolute) : this.base.get(absolute);\n\t}\n\n\t/** The conversation now knows `absolute` as holding the content `hash` names. */\n\trecord(absolute: string, hash: string): void {\n\t\tthis.own.set(absolute, hash);\n\t}\n\n\t/** The file is gone, as the conversation knows it. */\n\tforget(absolute: string): void {\n\t\tthis.own.set(absolute, undefined);\n\t}\n\n\t/** Keeps this run's reads for the conversation's next runs. */\n\tcommit(): void {\n\t\tconst seen = new Map(this.base);\n\t\tfor (const [path, hash] of this.own) {\n\t\t\tif (hash === undefined) seen.delete(path);\n\t\t\telse seen.set(path, hash);\n\t\t}\n\t\tthis.save(seen);\n\t}\n}\n\n/**\n * What a harness-built tool gets for one call: the run it belongs to, shared by every role, the\n * reads of the conversation that made the call, and the run's narration and usage, for a call\n * that does its own work on the model, such as running subagents.\n */\nexport interface ToolCallContext {\n\treadonly run: RunContext;\n\treadonly reads: RunReads;\n\t/** The id of the call, which its `tool` events carry. */\n\treadonly callId: string;\n\t/**\n\t * Narrates an event in the run's progress. It never throws: a failing callback ends the run\n\t * once the call returns.\n\t */\n\treadonly onProgress: (event: ProgressEvent) => void;\n\t/**\n\t * Adds what the call spent on the model to the run's usage. `unreported` when a call it made\n\t * came back without usage, which leaves the run's total unknown.\n\t */\n\treadonly spend: (tokens: Tokens | undefined, unreported: boolean) => void;\n\t/** The run's approver and asker, for an agent the call starts. */\n\treadonly approve?: Approver;\n\treadonly ask?: Asker;\n\t/** How many delegations deep the agent that made the call runs: 0 for the top-level one. */\n\treadonly depth: number;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"readRegistry.js","sourceRoot":"","sources":["../../../src/tools/domain/readRegistry.ts"],"names":[],"mappings":"AAMA;;;;;;;GAOG;AACH,MAAM,OAAO,YAAY;IAChB,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEzC,0FAA0F;IAC1F,KAAK;QACJ,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE;YACrC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QAClB,CAAC,CAAC,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,KAAK;QACJ,IAAI,CAAC,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC;IACvB,CAAC;CACD;AAED;;;;GAIG;AACH,MAAM,OAAO,QAAQ;IACH,IAAI,CAA8B;IAClC,GAAG,GAAG,IAAI,GAAG,EAA8B,CAAC;IAC5C,IAAI,CAAsC;IAE3D,YAAY,IAAiC,EAAE,IAAyC;QACvF,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,CAAC;IAED,mEAAmE;IACnE,SAAS,CAAC,QAAgB;QACzB,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAClF,CAAC;IAED,iFAAiF;IACjF,MAAM,CAAC,QAAgB,EAAE,IAAY;QACpC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED,sDAAsD;IACtD,MAAM,CAAC,QAAgB;QACtB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IACnC,CAAC;IAED,+DAA+D;IAC/D,MAAM;QACL,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChC,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;YACrC,IAAI,IAAI,KAAK,SAAS;gBAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;;gBACrC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC3B,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjB,CAAC;CACD","sourcesContent":["import type { ProgressEvent } from '#src/agent/domain/agent';\nimport type { Approver } from '#src/agent/domain/approval';\nimport type { Asker } from '#src/agent/domain/question';\nimport type { RunContext } from '#src/agent/domain/runContext';\nimport type { Tokens } from '#src/shared/domain/tokens';\n\n/**\n * What one conversation has seen of each file: the hash of the content a read showed it, or an\n * edit of its own left. An edit is made only from that version, so the model never changes a\n * file it has not read, or one that changed since.\n *\n * Keyed by the file's real path, so two spellings of one file are one entry. It belongs to the\n * conversation, not to the run: each role, with its own conversation, has its own.\n */\nexport class ReadRegistry {\n\tprivate seen = new Map<string, string>();\n\n\t/** The reads of one run, which count for the conversation only once the run succeeded. */\n\tbegin(): RunReads {\n\t\treturn new RunReads(this.seen, seen => {\n\t\t\tthis.seen = seen;\n\t\t});\n\t}\n\n\t/**\n\t * Forgets every file: the workspace moved through its history, so what the conversation saw\n\t * may no longer be what the files hold.\n\t */\n\tclear(): void {\n\t\tthis.seen = new Map();\n\t}\n}\n\n/**\n * The versions one run has seen, on top of what its conversation saw before it. A failed run\n * records nothing in its conversation, so the model does not remember what it read there: its\n * reads are kept only by `commit`.\n */\nexport class RunReads {\n\tprivate readonly base: ReadonlyMap<string, string>;\n\tprivate readonly own = new Map<string, string | undefined>();\n\tprivate readonly save: (seen: Map<string, string>) => void;\n\n\tconstructor(base: ReadonlyMap<string, string>, save: (seen: Map<string, string>) => void) {\n\t\tthis.base = base;\n\t\tthis.save = save;\n\t}\n\n\t/** The version of `absolute` the conversation has seen, if any. */\n\tversionOf(absolute: string): string | undefined {\n\t\treturn this.own.has(absolute) ? this.own.get(absolute) : this.base.get(absolute);\n\t}\n\n\t/** The conversation now knows `absolute` as holding the content `hash` names. */\n\trecord(absolute: string, hash: string): void {\n\t\tthis.own.set(absolute, hash);\n\t}\n\n\t/** The file is gone, as the conversation knows it. */\n\tforget(absolute: string): void {\n\t\tthis.own.set(absolute, undefined);\n\t}\n\n\t/** Keeps this run's reads for the conversation's next runs. */\n\tcommit(): void {\n\t\tconst seen = new Map(this.base);\n\t\tfor (const [path, hash] of this.own) {\n\t\t\tif (hash === undefined) seen.delete(path);\n\t\t\telse seen.set(path, hash);\n\t\t}\n\t\tthis.save(seen);\n\t}\n}\n\n/**\n * What a harness-built tool gets for one call: the run it belongs to, shared by every role, the\n * reads of the conversation that made the call, and the run's narration and usage, for a call\n * that does its own work on the model, such as running subagents.\n */\nexport interface ToolCallContext {\n\treadonly run: RunContext;\n\treadonly reads: RunReads;\n\t/** The id of the call, which its `tool` events carry. */\n\treadonly callId: string;\n\t/**\n\t * Narrates an event in the run's progress. It never throws: a failing callback ends the run\n\t * once the call returns.\n\t */\n\treadonly onProgress: (event: ProgressEvent) => void;\n\t/**\n\t * Adds what the call spent on the model to the run's usage. `unreported` when a call it made\n\t * came back without usage, which leaves the run's total unknown.\n\t */\n\treadonly spend: (tokens: Tokens | undefined, unreported: boolean) => void;\n\t/** The run's approver and asker, for an agent the call starts. */\n\treadonly approve?: Approver;\n\treadonly ask?: Asker;\n\t/** How many delegations deep the agent that made the call runs: 0 for the top-level one. */\n\treadonly depth: number;\n\t/**\n\t * The folders the call may write in, as real paths, set by the agents that delegated to the\n\t * one that made it: empty when it may write nowhere, absent when none set a limit.\n\t */\n\treadonly writeCeiling?: readonly string[];\n\t/**\n\t * The `write` roots of the workspace of the agent that made the call, as it was given them:\n\t * an agent the call starts writes only within them. Empty when it only reads, absent when it\n\t * has no workspace.\n\t */\n\treadonly agentWriteRoots?: readonly string[];\n}\n"]}
|
|
@@ -42,6 +42,12 @@ export interface AgentAsToolOptions {
|
|
|
42
42
|
*
|
|
43
43
|
* - its changes go into the same run of the workspace's history, so one undo reverts both, and
|
|
44
44
|
* each agent keeps its own tools' access policy and its own reads;
|
|
45
|
+
* - it never writes more than the calling agent may: only within the `write` roots of that
|
|
46
|
+
* agent's `workspace`, and nowhere when they are none, as for the planner and the reviewer.
|
|
47
|
+
* A calling agent with no workspace sets no limit of its own. Its tools that may write elsewhere are
|
|
48
|
+
* refused, a command included, and the model is told why. A consumer's `Tool` that is not
|
|
49
|
+
* `safe` is refused under a calling agent that only reads, since where it writes is unknown;
|
|
50
|
+
* under one that writes, it writes wherever it does;
|
|
45
51
|
* - what it spends counts in the caller's `tokens`, a failed attempt included;
|
|
46
52
|
* - its progress reaches the consumer as `subagent` events of the call, and a transcript it gives
|
|
47
53
|
* names the call in `delegation`;
|
|
@@ -7,6 +7,7 @@ import { classifyHostFailure } from '#src/shared/domain/providerFailure';
|
|
|
7
7
|
import { RunEndingToolError } from "../domain/preparedCall.js";
|
|
8
8
|
import { defineDelegatingTool } from "./defineTool.js";
|
|
9
9
|
import { cutAnswer, failureOf, subagentOptions } from "./subagents.js";
|
|
10
|
+
import { delegateCeiling } from "./writeCeiling.js";
|
|
10
11
|
/** How deep agents may delegate unless a tool says otherwise: planner → coder → committer. */
|
|
11
12
|
export const defaultMaxDepth = 2;
|
|
12
13
|
/**
|
|
@@ -17,6 +18,12 @@ export const defaultMaxDepth = 2;
|
|
|
17
18
|
*
|
|
18
19
|
* - its changes go into the same run of the workspace's history, so one undo reverts both, and
|
|
19
20
|
* each agent keeps its own tools' access policy and its own reads;
|
|
21
|
+
* - it never writes more than the calling agent may: only within the `write` roots of that
|
|
22
|
+
* agent's `workspace`, and nowhere when they are none, as for the planner and the reviewer.
|
|
23
|
+
* A calling agent with no workspace sets no limit of its own. Its tools that may write elsewhere are
|
|
24
|
+
* refused, a command included, and the model is told why. A consumer's `Tool` that is not
|
|
25
|
+
* `safe` is refused under a calling agent that only reads, since where it writes is unknown;
|
|
26
|
+
* under one that writes, it writes wherever it does;
|
|
20
27
|
* - what it spends counts in the caller's `tokens`, a failed attempt included;
|
|
21
28
|
* - its progress reaches the consumer as `subagent` events of the call, and a transcript it gives
|
|
22
29
|
* names the call in `delegation`;
|
|
@@ -67,6 +74,7 @@ export function agentAsTool({ name, description, createAgent, risk = 'mutating',
|
|
|
67
74
|
// The run's approver and asker are host code: one that fails inside the agent ends the
|
|
68
75
|
// run, not just the agent's task.
|
|
69
76
|
const failed = { host: false };
|
|
77
|
+
const writeCeiling = await delegateCeiling(call);
|
|
70
78
|
const options = subagentOptions(call, {
|
|
71
79
|
tool: name,
|
|
72
80
|
name,
|
|
@@ -77,6 +85,7 @@ export function agentAsTool({ name, description, createAgent, risk = 'mutating',
|
|
|
77
85
|
...(call.approve ? { approve: watchedApprover(call.approve, failed) } : {}),
|
|
78
86
|
...(call.ask ? { ask: watchedAsker(call.ask, failed) } : {}),
|
|
79
87
|
},
|
|
88
|
+
writeCeiling,
|
|
80
89
|
});
|
|
81
90
|
let agent;
|
|
82
91
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agentAsTool.js","sourceRoot":"","sources":["../../../src/tools/infrastructure/agentAsTool.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport type { Agent, RunOptions } from '#src/agent/domain/agent';\nimport { type Approver, readDecision, type ToolRisk } from '#src/agent/domain/approval';\nimport { type Asker, readAnswers } from '#src/agent/domain/question';\nimport {\n\tInvalidAgentConfigError,\n\tRecoverableError,\n\tUnrecoverableError,\n\twithSpentTokens,\n} from '#src/shared/domain/errors';\nimport { isAbortError } from '#src/shared/domain/isAbortError';\nimport { classifyHostFailure } from '#src/shared/domain/providerFailure';\nimport type { ToolDefinition } from '#src/llm/domain/tool';\nimport { type DelegatedAnswer, RunEndingToolError } from '../domain/preparedCall.ts';\nimport type { ToolCallContext } from '../domain/readRegistry.ts';\nimport { defineDelegatingTool } from './defineTool.ts';\nimport { cutAnswer, failureOf, subagentOptions } from './subagents.ts';\n\ndeclare const delegationToolBrand: unique symbol;\n\n/**\n * A tool from `agentAsTool`, which hands a task to another agent. It goes to an agent from\n * `createLLMAgent` beside its other tools; what it does inside is the harness's.\n */\nexport type DelegationTool = ToolDefinition & { readonly [delegationToolBrand]: true };\n\n/** How deep agents may delegate unless a tool says otherwise: planner → coder → committer. */\nexport const defaultMaxDepth = 2;\n\nexport interface AgentAsToolOptions {\n\t/** The name the model calls it by, such as `commit`. */\n\tname: string;\n\t/** What the agent does and when to hand it a task, for the model that may call it. */\n\tdescription: string;\n\t/**\n\t * A new agent for each call, such as one from `createLLMAgent`: it starts from the task alone\n\t * and never shares a conversation with another call.\n\t */\n\tcreateAgent: () => Agent;\n\t/**\n\t * How the call itself is judged before it runs. `mutating` unless given: handing over a task\n\t * asks no one, and the agent's own `destructive` calls are approved through the run's\n\t * `approve`, as the calling agent's would be. `destructive` asks before every task is handed\n\t * over.\n\t */\n\trisk?: ToolRisk;\n\t/**\n\t * How many delegations deep the agent may run, 1 for one the top-level agent calls. A call\n\t * that would go deeper is refused, and the model is told why. Defaults to 2.\n\t */\n\tmaxDepth?: number;\n}\n\n/**\n * Offers an agent to another agent as a tool. The calling model writes the task, and the call\n * answers at once: the agent works on it in the background, as part of the caller's run, while\n * the caller keeps working. Its answer reaches the caller's model in a message of its own once it\n * finishes, and nothing else of its work does:\n *\n * - its changes go into the same run of the workspace's history, so one undo reverts both, and\n * each agent keeps its own tools' access policy and its own reads;\n * - what it spends counts in the caller's `tokens`, a failed attempt included;\n * - its progress reaches the consumer as `subagent` events of the call, and a transcript it gives\n * names the call in `delegation`;\n * - it is asked and approved through the run's `ask` and `approve`. Delegating never grants\n * `autoApprove`: the agent has it only if the `createAgent` option gave it.\n *\n * The caller's run ends only once every agent it sent has finished. Every change and command of\n * the run is made one at a time, so the history stays exact, but agents working at once can see\n * each other's unfinished work. An agent that fails answers with why, for the model to act on; a\n * cancellation, or a run's approver or asker that failed inside it, ends the run and stops every\n * agent still working.\n *\n * Only for agents on the model APIs, from `createLLMAgent`: an Agent SDK agent from the\n * `createAgent` factory runs as its own run, outside these guarantees.\n *\n * @throws {InvalidAgentConfigError} when `maxDepth` is not a positive integer.\n */\nexport function agentAsTool({\n\tname,\n\tdescription,\n\tcreateAgent,\n\trisk = 'mutating',\n\tmaxDepth = defaultMaxDepth,\n}: AgentAsToolOptions): DelegationTool {\n\tif (!Number.isInteger(maxDepth) || maxDepth < 1) {\n\t\tthrow new InvalidAgentConfigError(\n\t\t\t`maxDepth of \"${name}\" must be a positive integer; got ${String(maxDepth)}`,\n\t\t);\n\t}\n\tconst tool = defineDelegatingTool({\n\t\tname,\n\t\tdescription,\n\t\tinput: z.object({\n\t\t\ttask: z\n\t\t\t\t.string()\n\t\t\t\t.describe(\n\t\t\t\t\t'The whole task. The agent starts from it alone, so say what to do and what to report back.',\n\t\t\t\t),\n\t\t}),\n\t\tsummarize: ({ task }) => task,\n\t\tdelegate: ({ task }, _, call) => {\n\t\t\tconst asked = task.trim();\n\t\t\tif (asked === '') return Promise.reject(new Error('The task needs some text.'));\n\t\t\tif (call.depth >= maxDepth) {\n\t\t\t\treturn Promise.reject(\n\t\t\t\t\tnew Error(\n\t\t\t\t\t\t`\"${name}\" may run at most ${String(maxDepth)} delegations deep, and this agent already runs ${String(call.depth)} deep. Do the work yourself, or answer with what you have.`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn Promise.resolve({\n\t\t\t\trisk,\n\t\t\t\t// What the agent changes is in the history, but it may run tools of the consumer's\n\t\t\t\t// that change what the history never sees.\n\t\t\t\tunrecorded: true,\n\t\t\t\tstart: signal => [{ name, subagent: 1, task: asked, answer: hand(asked, signal, call) }],\n\t\t\t});\n\t\t},\n\t});\n\n\tasync function hand(\n\t\ttask: string,\n\t\tsignal: AbortSignal,\n\t\tcall: ToolCallContext,\n\t): Promise<DelegatedAnswer> {\n\t\t// The run's approver and asker are host code: one that fails inside the agent ends the\n\t\t// run, not just the agent's task.\n\t\tconst failed = { host: false };\n\t\tconst options = subagentOptions(call, {\n\t\t\ttool: name,\n\t\t\tname,\n\t\t\tsubagent: 1,\n\t\t\ttask,\n\t\t\toptions: {\n\t\t\t\tsignal,\n\t\t\t\t...(call.approve ? { approve: watchedApprover(call.approve, failed) } : {}),\n\t\t\t\t...(call.ask ? { ask: watchedAsker(call.ask, failed) } : {}),\n\t\t\t} satisfies RunOptions,\n\t\t});\n\n\t\tlet agent: Agent;\n\t\ttry {\n\t\t\tagent = createAgent();\n\t\t} catch (error) {\n\t\t\tthrow new RunEndingToolError(\n\t\t\t\tclassifyHostFailure(error, `The agent for \"${name}\" could not be created`),\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\tconst { response, tokens } = await agent.run(task, options);\n\t\t\tcall.spend(tokens, tokens === undefined);\n\t\t\treturn { text: cutAnswer(response || 'It finished without an answer.') };\n\t\t} catch (error) {\n\t\t\tif (isAbortError(error)) throw error;\n\t\t\tconst classified = error instanceof RecoverableError || error instanceof UnrecoverableError;\n\t\t\t// Its failure carries what it spent, which the run's failure keeps.\n\t\t\tif (classified && failed.host) throw new RunEndingToolError(error);\n\t\t\tif (!classified) {\n\t\t\t\t// It broke the contract of `Agent`, so what it spent is unknown, not nothing.\n\t\t\t\tthrow new RunEndingToolError(\n\t\t\t\t\twithSpentTokens(\n\t\t\t\t\t\tclassifyHostFailure(error, `The agent for \"${name}\" failed`),\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\tcall.spend(error.tokens, error.usageUnreported);\n\t\t\treturn { text: `The agent failed: ${failureOf(error)}`, failed: true };\n\t\t}\n\t}\n\n\t// A file tool's kind of brand: the harness built it, and only `createLLMAgent` takes it.\n\treturn tool as unknown as DelegationTool;\n}\n\n/** `approve`, noting in `failed` when it throws or answers with no decision. */\nfunction watchedApprover(approve: Approver, failed: { host: boolean }): Approver {\n\treturn async (request, signal) => {\n\t\ttry {\n\t\t\tconst answer = await approve(request, signal);\n\t\t\treadDecision(answer);\n\t\t\treturn answer;\n\t\t} catch (error) {\n\t\t\t// One that gives up on a cancellation did not fail.\n\t\t\tif (!signal.aborted) failed.host = true;\n\t\t\tthrow error;\n\t\t}\n\t};\n}\n\n/** `ask`, noting in `failed` when it throws or answers in a shape no agent accepts. */\nfunction watchedAsker(ask: Asker, failed: { host: boolean }): Asker {\n\treturn async (questions, signal) => {\n\t\ttry {\n\t\t\tconst answers = await ask(questions, signal);\n\t\t\tif (!readAnswers(answers, questions)) failed.host = true;\n\t\t\treturn answers;\n\t\t} catch (error) {\n\t\t\tif (!signal.aborted) failed.host = true;\n\t\t\tthrow error;\n\t\t}\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"agentAsTool.js","sourceRoot":"","sources":["../../../src/tools/infrastructure/agentAsTool.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport type { Agent, RunOptions } from '#src/agent/domain/agent';\nimport { type Approver, readDecision, type ToolRisk } from '#src/agent/domain/approval';\nimport { type Asker, readAnswers } from '#src/agent/domain/question';\nimport {\n\tInvalidAgentConfigError,\n\tRecoverableError,\n\tUnrecoverableError,\n\twithSpentTokens,\n} from '#src/shared/domain/errors';\nimport { isAbortError } from '#src/shared/domain/isAbortError';\nimport { classifyHostFailure } from '#src/shared/domain/providerFailure';\nimport type { ToolDefinition } from '#src/llm/domain/tool';\nimport { type DelegatedAnswer, RunEndingToolError } from '../domain/preparedCall.ts';\nimport type { ToolCallContext } from '../domain/readRegistry.ts';\nimport { defineDelegatingTool } from './defineTool.ts';\nimport { cutAnswer, failureOf, subagentOptions } from './subagents.ts';\nimport { delegateCeiling } from './writeCeiling.ts';\n\ndeclare const delegationToolBrand: unique symbol;\n\n/**\n * A tool from `agentAsTool`, which hands a task to another agent. It goes to an agent from\n * `createLLMAgent` beside its other tools; what it does inside is the harness's.\n */\nexport type DelegationTool = ToolDefinition & { readonly [delegationToolBrand]: true };\n\n/** How deep agents may delegate unless a tool says otherwise: planner → coder → committer. */\nexport const defaultMaxDepth = 2;\n\nexport interface AgentAsToolOptions {\n\t/** The name the model calls it by, such as `commit`. */\n\tname: string;\n\t/** What the agent does and when to hand it a task, for the model that may call it. */\n\tdescription: string;\n\t/**\n\t * A new agent for each call, such as one from `createLLMAgent`: it starts from the task alone\n\t * and never shares a conversation with another call.\n\t */\n\tcreateAgent: () => Agent;\n\t/**\n\t * How the call itself is judged before it runs. `mutating` unless given: handing over a task\n\t * asks no one, and the agent's own `destructive` calls are approved through the run's\n\t * `approve`, as the calling agent's would be. `destructive` asks before every task is handed\n\t * over.\n\t */\n\trisk?: ToolRisk;\n\t/**\n\t * How many delegations deep the agent may run, 1 for one the top-level agent calls. A call\n\t * that would go deeper is refused, and the model is told why. Defaults to 2.\n\t */\n\tmaxDepth?: number;\n}\n\n/**\n * Offers an agent to another agent as a tool. The calling model writes the task, and the call\n * answers at once: the agent works on it in the background, as part of the caller's run, while\n * the caller keeps working. Its answer reaches the caller's model in a message of its own once it\n * finishes, and nothing else of its work does:\n *\n * - its changes go into the same run of the workspace's history, so one undo reverts both, and\n * each agent keeps its own tools' access policy and its own reads;\n * - it never writes more than the calling agent may: only within the `write` roots of that\n * agent's `workspace`, and nowhere when they are none, as for the planner and the reviewer.\n * A calling agent with no workspace sets no limit of its own. Its tools that may write elsewhere are\n * refused, a command included, and the model is told why. A consumer's `Tool` that is not\n * `safe` is refused under a calling agent that only reads, since where it writes is unknown;\n * under one that writes, it writes wherever it does;\n * - what it spends counts in the caller's `tokens`, a failed attempt included;\n * - its progress reaches the consumer as `subagent` events of the call, and a transcript it gives\n * names the call in `delegation`;\n * - it is asked and approved through the run's `ask` and `approve`. Delegating never grants\n * `autoApprove`: the agent has it only if the `createAgent` option gave it.\n *\n * The caller's run ends only once every agent it sent has finished. Every change and command of\n * the run is made one at a time, so the history stays exact, but agents working at once can see\n * each other's unfinished work. An agent that fails answers with why, for the model to act on; a\n * cancellation, or a run's approver or asker that failed inside it, ends the run and stops every\n * agent still working.\n *\n * Only for agents on the model APIs, from `createLLMAgent`: an Agent SDK agent from the\n * `createAgent` factory runs as its own run, outside these guarantees.\n *\n * @throws {InvalidAgentConfigError} when `maxDepth` is not a positive integer.\n */\nexport function agentAsTool({\n\tname,\n\tdescription,\n\tcreateAgent,\n\trisk = 'mutating',\n\tmaxDepth = defaultMaxDepth,\n}: AgentAsToolOptions): DelegationTool {\n\tif (!Number.isInteger(maxDepth) || maxDepth < 1) {\n\t\tthrow new InvalidAgentConfigError(\n\t\t\t`maxDepth of \"${name}\" must be a positive integer; got ${String(maxDepth)}`,\n\t\t);\n\t}\n\tconst tool = defineDelegatingTool({\n\t\tname,\n\t\tdescription,\n\t\tinput: z.object({\n\t\t\ttask: z\n\t\t\t\t.string()\n\t\t\t\t.describe(\n\t\t\t\t\t'The whole task. The agent starts from it alone, so say what to do and what to report back.',\n\t\t\t\t),\n\t\t}),\n\t\tsummarize: ({ task }) => task,\n\t\tdelegate: ({ task }, _, call) => {\n\t\t\tconst asked = task.trim();\n\t\t\tif (asked === '') return Promise.reject(new Error('The task needs some text.'));\n\t\t\tif (call.depth >= maxDepth) {\n\t\t\t\treturn Promise.reject(\n\t\t\t\t\tnew Error(\n\t\t\t\t\t\t`\"${name}\" may run at most ${String(maxDepth)} delegations deep, and this agent already runs ${String(call.depth)} deep. Do the work yourself, or answer with what you have.`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn Promise.resolve({\n\t\t\t\trisk,\n\t\t\t\t// What the agent changes is in the history, but it may run tools of the consumer's\n\t\t\t\t// that change what the history never sees.\n\t\t\t\tunrecorded: true,\n\t\t\t\tstart: signal => [{ name, subagent: 1, task: asked, answer: hand(asked, signal, call) }],\n\t\t\t});\n\t\t},\n\t});\n\n\tasync function hand(\n\t\ttask: string,\n\t\tsignal: AbortSignal,\n\t\tcall: ToolCallContext,\n\t): Promise<DelegatedAnswer> {\n\t\t// The run's approver and asker are host code: one that fails inside the agent ends the\n\t\t// run, not just the agent's task.\n\t\tconst failed = { host: false };\n\t\tconst writeCeiling = await delegateCeiling(call);\n\t\tconst options = subagentOptions(call, {\n\t\t\ttool: name,\n\t\t\tname,\n\t\t\tsubagent: 1,\n\t\t\ttask,\n\t\t\toptions: {\n\t\t\t\tsignal,\n\t\t\t\t...(call.approve ? { approve: watchedApprover(call.approve, failed) } : {}),\n\t\t\t\t...(call.ask ? { ask: watchedAsker(call.ask, failed) } : {}),\n\t\t\t} satisfies RunOptions,\n\t\t\twriteCeiling,\n\t\t});\n\n\t\tlet agent: Agent;\n\t\ttry {\n\t\t\tagent = createAgent();\n\t\t} catch (error) {\n\t\t\tthrow new RunEndingToolError(\n\t\t\t\tclassifyHostFailure(error, `The agent for \"${name}\" could not be created`),\n\t\t\t);\n\t\t}\n\n\t\ttry {\n\t\t\tconst { response, tokens } = await agent.run(task, options);\n\t\t\tcall.spend(tokens, tokens === undefined);\n\t\t\treturn { text: cutAnswer(response || 'It finished without an answer.') };\n\t\t} catch (error) {\n\t\t\tif (isAbortError(error)) throw error;\n\t\t\tconst classified = error instanceof RecoverableError || error instanceof UnrecoverableError;\n\t\t\t// Its failure carries what it spent, which the run's failure keeps.\n\t\t\tif (classified && failed.host) throw new RunEndingToolError(error);\n\t\t\tif (!classified) {\n\t\t\t\t// It broke the contract of `Agent`, so what it spent is unknown, not nothing.\n\t\t\t\tthrow new RunEndingToolError(\n\t\t\t\t\twithSpentTokens(\n\t\t\t\t\t\tclassifyHostFailure(error, `The agent for \"${name}\" failed`),\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t\ttrue,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\tcall.spend(error.tokens, error.usageUnreported);\n\t\t\treturn { text: `The agent failed: ${failureOf(error)}`, failed: true };\n\t\t}\n\t}\n\n\t// A file tool's kind of brand: the harness built it, and only `createLLMAgent` takes it.\n\treturn tool as unknown as DelegationTool;\n}\n\n/** `approve`, noting in `failed` when it throws or answers with no decision. */\nfunction watchedApprover(approve: Approver, failed: { host: boolean }): Approver {\n\treturn async (request, signal) => {\n\t\ttry {\n\t\t\tconst answer = await approve(request, signal);\n\t\t\treadDecision(answer);\n\t\t\treturn answer;\n\t\t} catch (error) {\n\t\t\t// One that gives up on a cancellation did not fail.\n\t\t\tif (!signal.aborted) failed.host = true;\n\t\t\tthrow error;\n\t\t}\n\t};\n}\n\n/** `ask`, noting in `failed` when it throws or answers in a shape no agent accepts. */\nfunction watchedAsker(ask: Asker, failed: { host: boolean }): Asker {\n\treturn async (questions, signal) => {\n\t\ttry {\n\t\t\tconst answers = await ask(questions, signal);\n\t\t\tif (!readAnswers(answers, questions)) failed.host = true;\n\t\t\treturn answers;\n\t\t} catch (error) {\n\t\t\tif (!signal.aborted) failed.host = true;\n\t\t\tthrow error;\n\t\t}\n\t};\n}\n"]}
|
|
@@ -54,11 +54,14 @@ export async function createCommandTool({ commands, policy, writes, covered, bas
|
|
|
54
54
|
// use a tool outside LLMAgent. This description never uses the resolved cwd.
|
|
55
55
|
return definition ? visibleCommand(definition, values) : name;
|
|
56
56
|
},
|
|
57
|
-
prepare: async ({ command: { name, ...values } }, signal,
|
|
57
|
+
prepare: async ({ command: { name, ...values } }, signal, call) => {
|
|
58
|
+
const { run } = call;
|
|
58
59
|
const command = resolved.get(name);
|
|
59
60
|
// The schema allows only the configured names.
|
|
60
61
|
if (!command)
|
|
61
62
|
throw new Error(`There is no command named "${name}"`);
|
|
63
|
+
// A command can change anything in the write roots.
|
|
64
|
+
await writes.checkCeiling(call);
|
|
62
65
|
const paramValues = {};
|
|
63
66
|
for (const parameter of parametersOf(command.definition)) {
|
|
64
67
|
const value = values[parameter.name] ?? null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commandTool.js","sourceRoot":"","sources":["../../../src/tools/infrastructure/commandTool.ts"],"names":[],"mappings":"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","sourcesContent":["import { realpath } from 'node:fs/promises';\nimport { isAbsolute, relative, resolve } from 'node:path';\nimport { z } from 'zod';\nimport type { CommandLine } from '#src/agent/domain/approval';\nimport type { RunContext } from '#src/agent/domain/runContext';\nimport { displayPath } from '#src/recovery/domain/runChanges';\nimport { InvalidAgentConfigError } from '#src/shared/domain/errors';\nimport type { AccessPolicy } from '../domain/accessPolicy.ts';\nimport {\n\tbuildArguments,\n\ttype CommandDefinition,\n\ttype CommandParameter,\n\ttype Commands,\n\tdefaultCommandOutputBytes,\n\tdefaultCommandTimeoutMs,\n\ttype ParameterValue,\n\tparametersOf,\n\tvalidateCommands,\n} from '../domain/commands.ts';\nimport { EditRefusedError, EditUnconfirmedError } from '../domain/fileEdits.ts';\nimport {\n\ttype CommandEnding,\n\tToolExecutionError,\n\ttype PreparingTool,\n} from '../domain/preparedCall.ts';\nimport { CommandCheckpoint, type CoveredFiles } from './commandCheckpoint.ts';\nimport { OutputFingerprint, withoutColors } from './commandOutput.ts';\nimport { type CommandProcessResult, runCommandProcess } from './commandProcess.ts';\nimport { definePreparingTool } from './defineTool.ts';\nimport type { WorkspaceWrites } from './workspaceWrites.ts';\nimport type { WriteSession } from './writeSession.ts';\n\n// Only what a toolchain needs to find itself and its locale. Everything else, the provider\n// keys included, stays in this process.\nconst inheritedVariables = ['PATH', 'HOME', 'TMPDIR', 'USER', 'SHELL', 'LANG'];\n// Long enough for a slow disk, short enough not to hold up building an agent.\nconst cwdCheckMs = 10_000;\n\n/** A command ready to run: its definition, and where it runs as a real path. */\ninterface ResolvedCommand {\n\tdefinition: CommandDefinition;\n\tcwd: string;\n}\n\n/**\n * `runCommand`, which runs one of the commands the consumer configured, by name, with the\n * parameters each one declares. Every call asks the approver, showing the program exactly as\n * it will run; `autoApprove` skips that.\n *\n * Around each command, the covered files are captured before and compared after, so what it\n * changed is in the run's history and can be undone: see `CommandCheckpoint`. If the capture\n * fails, the command does not run. A timeout or a cancellation stops its whole process group,\n * and is never reported as a completed command. If the group cannot be confirmed stopped, what\n * changed so far is recorded, the observation stays open, and writing stops for the run.\n *\n * This is not a sandbox: a command runs the repository's code, with everything this user can\n * reach. See \"Configured commands are trusted, not sandboxed\" in decisions.md.\n *\n * @throws {InvalidAgentConfigError} for an invalid configuration, a working directory outside\n * every `write` root, or on Windows, where a process group cannot be stopped.\n */\nexport async function createCommandTool({\n\tcommands,\n\tpolicy,\n\twrites,\n\tcovered,\n\tbase,\n\tplatform = process.platform,\n\trunProcess = runCommandProcess,\n}: {\n\tcommands: Commands;\n\tpolicy: AccessPolicy;\n\twrites: WorkspaceWrites;\n\tcovered: CoveredFiles;\n\t/** The real path of the first root, where a relative `cwd` starts. */\n\tbase: string;\n\tplatform?: NodeJS.Platform;\n\t/** Runs the program. Replaced in tests, to reach what a real process rarely does. */\n\trunProcess?: typeof runCommandProcess;\n}): Promise<PreparingTool> {\n\tif (platform === 'win32') {\n\t\tthrow new InvalidAgentConfigError(\n\t\t\t'Configured commands are not supported on Windows yet: the harness cannot stop a command and everything it started there',\n\t\t);\n\t}\n\tvalidateCommands(commands);\n\tconst resolved = new Map<string, ResolvedCommand>();\n\tfor (const [name, definition] of Object.entries(commands)) {\n\t\tresolved.set(name, {\n\t\t\tdefinition,\n\t\t\tcwd: await resolveCwd(`Command \"${name}\"`, definition.cwd, policy, base),\n\t\t});\n\t}\n\n\treturn definePreparingTool({\n\t\tname: 'runCommand',\n\t\tdescription: describeTool(commands),\n\t\tinput: inputSchema(commands),\n\t\tsummarize: ({ command: { name, ...values } }) => {\n\t\t\tconst definition = commands[name];\n\t\t\t// The schema only permits configured names, but keep this defensive for callers that\n\t\t\t// use a tool outside LLMAgent. This description never uses the resolved cwd.\n\t\t\treturn definition ? visibleCommand(definition, values) : name;\n\t\t},\n\t\tprepare: async ({ command: { name, ...values } }, signal, { run }) => {\n\t\t\tconst command = resolved.get(name);\n\t\t\t// The schema allows only the configured names.\n\t\t\tif (!command) throw new Error(`There is no command named \"${name}\"`);\n\t\t\tconst paramValues: Record<string, ParameterValue> = {};\n\t\t\tfor (const parameter of parametersOf(command.definition)) {\n\t\t\t\tconst value =\n\t\t\t\t\t(values as Record<string, ParameterValue | undefined>)[parameter.name] ?? null;\n\t\t\t\tparamValues[parameter.name] =\n\t\t\t\t\tparameter.type === 'paths' && Array.isArray(value)\n\t\t\t\t\t\t? await resolvePaths(value, command.cwd, policy, signal)\n\t\t\t\t\t\t: value;\n\t\t\t}\n\t\t\tconst line: CommandLine = Object.freeze({\n\t\t\t\texecutable: command.definition.executable,\n\t\t\t\targs: Object.freeze(buildArguments(command.definition, paramValues)) as string[],\n\t\t\t\tcwd: command.cwd,\n\t\t\t});\n\n\t\t\t// Now, so a set of tools that cannot join the run's writes is refused before the user is asked.\n\t\t\twrites.sessionFor(run);\n\t\t\tlet ending: CommandEnding | undefined;\n\t\t\treturn {\n\t\t\t\t// A command can change anything its user can: it is always asked about.\n\t\t\t\trisk: 'destructive',\n\t\t\t\tcommand: line,\n\t\t\t\tending: () => ending,\n\t\t\t\trun: runSignal =>\n\t\t\t\t\trunCommand({\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tline,\n\t\t\t\t\t\tdefinition: command.definition,\n\t\t\t\t\t\tcontext: run,\n\t\t\t\t\t\tsignal: runSignal,\n\t\t\t\t\t\tended: end => {\n\t\t\t\t\t\t\tending = end;\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t};\n\t\t},\n\t});\n\n\tinterface CommandRun {\n\t\tname: string;\n\t\tline: CommandLine;\n\t\tdefinition: CommandDefinition;\n\t\tcontext: RunContext;\n\t\tsignal: AbortSignal;\n\t\t/** Told how the command ended, once it ran to an end and everything it changed is recorded. */\n\t\tended: (ending: CommandEnding) => void;\n\t}\n\n\t// Alone: a change another agent of the run made while it ran would be taken for its own.\n\tfunction runCommand(run: CommandRun): Promise<string> {\n\t\tconst session = writes.sessionFor(run.context);\n\t\treturn session.exclusive(() => runCommandAlone(run, session), run.signal);\n\t}\n\n\tasync function runCommandAlone(\n\t\t{ name, line, definition, signal, ended }: CommandRun,\n\t\tsession: WriteSession,\n\t): Promise<string> {\n\t\tsignal.throwIfAborted();\n\t\tconst journal = await session.journalForChange();\n\t\tlet checkpoint: CommandCheckpoint;\n\t\ttry {\n\t\t\tcheckpoint = await CommandCheckpoint.begin({ journal, covered, by: name, signal });\n\t\t} catch (error) {\n\t\t\tsignal.throwIfAborted();\n\t\t\tthrow new EditRefusedError(\n\t\t\t\t`The harness could not capture the files before \"${name}\", so it did not run`,\n\t\t\t\t{ cause: error },\n\t\t\t);\n\t\t}\n\n\t\tlet result: CommandProcessResult | undefined;\n\t\tlet startFailure: unknown;\n\t\tconst fingerprint = new OutputFingerprint();\n\t\ttry {\n\t\t\tresult = await runProcess(line.executable, line.args, {\n\t\t\t\tcwd: line.cwd,\n\t\t\t\tenv: environmentFor(definition),\n\t\t\t\ttimeoutMs: definition.timeoutMs ?? defaultCommandTimeoutMs,\n\t\t\t\toutputBytes: definition.outputBytes ?? defaultCommandOutputBytes,\n\t\t\t\tsignal,\n\t\t\t\tonOutput: (stream, chunk) => {\n\t\t\t\t\tfingerprint.push(stream, chunk);\n\t\t\t\t},\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tstartFailure = error;\n\t\t}\n\n\t\tconst complete = result?.stopped ?? true;\n\t\tlet observed: Awaited<ReturnType<CommandCheckpoint['end']>>;\n\t\ttry {\n\t\t\tobserved = await checkpoint.end({ complete });\n\t\t} catch (error) {\n\t\t\tsession.stop('Writing stopped: the harness could not record what a command changed');\n\t\t\tthrow new EditUnconfirmedError(\n\t\t\t\t`\"${name}\" ran, but the harness could not record what it changed; writing has stopped for this run`,\n\t\t\t\t{ cause: error },\n\t\t\t);\n\t\t}\n\n\t\tif (!result) {\n\t\t\tsignal.throwIfAborted();\n\t\t\tthrow new EditRefusedError(`\"${name}\" could not be started`, { cause: startFailure });\n\t\t}\n\t\tconst report = describeRun(name, definition, result, observed);\n\t\tif (!complete) {\n\t\t\tsession.stop('Writing stopped: a command could not be stopped');\n\t\t\tthrow new EditUnconfirmedError(\n\t\t\t\t`${report}\\n\"${name}\" could not be stopped: something it started may still be changing files, and the history may not hold all of it. Writing has stopped for this run.`,\n\t\t\t);\n\t\t}\n\t\t// After its changes are recorded: a cancelled command is never reported as finished.\n\t\tif (result.outcome === 'cancelled') signal.throwIfAborted();\n\t\tended({\n\t\t\tpassed: result.outcome === 'exited' && result.exitCode === 0,\n\t\t\thow: howItEnded(result),\n\t\t\toutput: withoutColors(result.output),\n\t\t\tfingerprint: fingerprint.digest(),\n\t\t\t...(observed.unrecorded.length === 0 ? { state: journal.contentState() } : {}),\n\t\t\tchanged: journal.appliedPaths.map(path => displayPath(base, path)),\n\t\t});\n\t\tif (result.outcome !== 'exited') throw new ToolExecutionError(report);\n\t\tif (result.exitCode !== 0) {\n\t\t\tthrow new ToolExecutionError(report, {\n\t\t\t\t...(result.exitCode === null ? {} : { exitCode: result.exitCode }),\n\t\t\t});\n\t\t}\n\t\treturn report;\n\t}\n\n\tfunction describeRun(\n\t\tname: string,\n\t\tdefinition: CommandDefinition,\n\t\tresult: CommandProcessResult,\n\t\t{ changes, unrecorded }: Awaited<ReturnType<CommandCheckpoint['end']>>,\n\t): string {\n\t\tconst seconds = (result.durationMs / 1000).toFixed(1);\n\t\tconst lines: string[] = [];\n\t\tif (result.outcome === 'timedOut') {\n\t\t\tconst limit = (definition.timeoutMs ?? defaultCommandTimeoutMs) / 1000;\n\t\t\tlines.push(`\"${name}\" ran past its limit of ${String(limit)} s and was stopped.`);\n\t\t} else if (result.outcome === 'cancelled') {\n\t\t\tlines.push(`\"${name}\" was stopped: the run was cancelled.`);\n\t\t} else if (result.exitCode !== null) {\n\t\t\tlines.push(`\"${name}\" exited with code ${String(result.exitCode)} after ${seconds} s.`);\n\t\t} else {\n\t\t\tlines.push(`\"${name}\" was killed by ${result.signal ?? 'a signal'} after ${seconds} s.`);\n\t\t}\n\n\t\tconst shown = (path: string) => displayPath(base, path);\n\t\tlines.push(\n\t\t\tchanges.length === 0\n\t\t\t\t? 'It changed no file the history covers.'\n\t\t\t\t: `Files it changed, recorded in the history: ${changes.map(change => shown(change.path)).join(', ')}.`,\n\t\t);\n\t\tif (unrecorded.length > 0) {\n\t\t\tlines.push(\n\t\t\t\t`Not recorded, because the history cannot copy them (a folder, a link, or a file over 64 MB) before or after: ${unrecorded.map(shown).join(', ')}. Undo leaves them as they are.`,\n\t\t\t);\n\t\t}\n\n\t\tconst limit = definition.outputBytes ?? defaultCommandOutputBytes;\n\t\tif (result.printedBytes === 0) {\n\t\t\tlines.push('It printed nothing.');\n\t\t} else {\n\t\t\tlines.push(\n\t\t\t\tresult.printedBytes > limit\n\t\t\t\t\t? `Output, the last ${kilobytes(limit)} of ${kilobytes(result.printedBytes)}:`\n\t\t\t\t\t: 'Output:',\n\t\t\t\tresult.output,\n\t\t\t);\n\t\t}\n\t\treturn lines.join('\\n');\n\t}\n}\n\nfunction howItEnded(result: CommandProcessResult): string {\n\tif (result.outcome === 'timedOut') return 'ran past its time limit';\n\tif (result.outcome === 'cancelled') return 'was cancelled';\n\tif (result.exitCode !== null) return `exited with code ${String(result.exitCode)}`;\n\treturn `was killed by ${result.signal ?? 'a signal'}`;\n}\n\nfunction kilobytes(bytes: number): string {\n\treturn `${String(Math.ceil(bytes / 1024))} KB`;\n}\n\n/** The command line the model can see, before paths are resolved against the host workspace. */\nfunction visibleCommand(\n\tdefinition: CommandDefinition,\n\tvalues: Record<string, ParameterValue | undefined>,\n): string {\n\tconst args: string[] = [];\n\tfor (const argument of definition.args ?? []) {\n\t\tif (typeof argument === 'string') {\n\t\t\targs.push(argument);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst value = values[argument.name] ?? null;\n\t\tif (argument.type === 'paths') {\n\t\t\tif (Array.isArray(value)) args.push(...value);\n\t\t} else if (value !== null && typeof value === 'string') {\n\t\t\targs.push(value);\n\t\t}\n\t}\n\n\treturn [definition.executable, ...args].join(' ');\n}\n\n/**\n * Where a configured program runs, as a real path inside a `write` root. `program` names it in\n * the error, as `Command \"test\"`.\n */\nexport async function resolveCwd(\n\tprogram: string,\n\tcwd: string | undefined,\n\tpolicy: AccessPolicy,\n\tbase: string,\n): Promise<string> {\n\tconst named = resolve(base, cwd ?? '.');\n\tlet real: string;\n\ttry {\n\t\treal = await realpath(named);\n\t\tawait policy.check(real, 'write', AbortSignal.timeout(cwdCheckMs));\n\t} catch (error) {\n\t\tthrow new InvalidAgentConfigError(\n\t\t\t`${program} needs a cwd inside a write root of the workspace`,\n\t\t\t{ cause: error },\n\t\t);\n\t}\n\treturn real;\n}\n\n/**\n * Each path, once the policy lets the agent read it, relative to where the command runs. One\n * that would read as a flag is given as `./` and its name.\n */\nasync function resolvePaths(\n\tpaths: string[],\n\tcwd: string,\n\tpolicy: AccessPolicy,\n\tsignal: AbortSignal,\n): Promise<string[]> {\n\tconst resolved: string[] = [];\n\tfor (const path of paths) {\n\t\tconst { absolute } = await policy.check(path, 'read', signal);\n\t\tconst fromCwd = relative(cwd, absolute) || '.';\n\t\tresolved.push(fromCwd.startsWith('-') && !isAbsolute(fromCwd) ? `./${fromCwd}` : fromCwd);\n\t}\n\treturn resolved;\n}\n\n/** A small environment of its own: the inherited few, then what the consumer set. */\nexport function environmentFor(\n\tdefinition: Pick<CommandDefinition, 'env'>,\n\tenv: NodeJS.ProcessEnv = process.env,\n): NodeJS.ProcessEnv {\n\tconst inherited: NodeJS.ProcessEnv = {};\n\tfor (const [variable, value] of Object.entries(env)) {\n\t\tif (value === undefined) continue;\n\t\tif (inheritedVariables.includes(variable) || variable.startsWith('LC_')) {\n\t\t\tinherited[variable] = value;\n\t\t}\n\t}\n\treturn { ...inherited, ...definition.env };\n}\n\nfunction describeTool(commands: Commands): string {\n\tconst list = Object.entries(commands)\n\t\t.map(([name, { description }]) => `- ${name}: ${description}`)\n\t\t.join('\\n');\n\treturn [\n\t\t\"Runs one of the project's configured commands, by name, such as its tests or its formatter.\",\n\t\t'Files it changes are recorded in the history, so they can be undone. Its output is cut to its end.',\n\t\t'Commands:',\n\t\tlist,\n\t].join('\\n');\n}\n\n/**\n * One object per command, told apart by `name`, each with the parameters that command takes.\n * Strict mode needs every key present, so a parameter that may be left out takes `null`.\n */\nfunction inputSchema(commands: Commands) {\n\tconst variants = Object.entries(commands).map(([name, definition]) =>\n\t\tz.object({\n\t\t\tname: z.enum([name]),\n\t\t\t...Object.fromEntries(\n\t\t\t\tparametersOf(definition).map(parameter => [parameter.name, parameterSchema(parameter)]),\n\t\t\t),\n\t\t}),\n\t);\n\tconst [first, ...rest] = variants;\n\t// `validateCommands` refuses an empty configuration.\n\tif (!first) throw new InvalidAgentConfigError('No command is configured');\n\treturn z.object({\n\t\tcommand: (rest.length === 0 ? first : z.union([first, ...rest])).describe(\n\t\t\t'The command to run, and its parameters.',\n\t\t),\n\t});\n}\n\nfunction parameterSchema(parameter: CommandParameter): z.ZodType {\n\tswitch (parameter.type) {\n\t\tcase 'paths':\n\t\t\treturn z\n\t\t\t\t.array(z.string())\n\t\t\t\t.describe(`${parameter.description} Paths inside the workspace; [] for none.`);\n\t\tcase 'choice': {\n\t\t\tconst values = z.enum(parameter.values as [string, ...string[]]);\n\t\t\treturn parameter.required === true\n\t\t\t\t? values.describe(parameter.description)\n\t\t\t\t: values.nullable().describe(`${parameter.description} null to leave it out.`);\n\t\t}\n\t\tcase 'text': {\n\t\t\tconst text = z.string();\n\t\t\tconst description = `${parameter.description} Must match the regular expression ${parameter.pattern}, and not start with \"-\".`;\n\t\t\treturn parameter.required === true\n\t\t\t\t? text.describe(description)\n\t\t\t\t: text.nullable().describe(`${description} null to leave it out.`);\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"commandTool.js","sourceRoot":"","sources":["../../../src/tools/infrastructure/commandTool.ts"],"names":[],"mappings":"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","sourcesContent":["import { realpath } from 'node:fs/promises';\nimport { isAbsolute, relative, resolve } from 'node:path';\nimport { z } from 'zod';\nimport type { CommandLine } from '#src/agent/domain/approval';\nimport type { RunContext } from '#src/agent/domain/runContext';\nimport { displayPath } from '#src/recovery/domain/runChanges';\nimport { InvalidAgentConfigError } from '#src/shared/domain/errors';\nimport type { AccessPolicy } from '../domain/accessPolicy.ts';\nimport {\n\tbuildArguments,\n\ttype CommandDefinition,\n\ttype CommandParameter,\n\ttype Commands,\n\tdefaultCommandOutputBytes,\n\tdefaultCommandTimeoutMs,\n\ttype ParameterValue,\n\tparametersOf,\n\tvalidateCommands,\n} from '../domain/commands.ts';\nimport { EditRefusedError, EditUnconfirmedError } from '../domain/fileEdits.ts';\nimport {\n\ttype CommandEnding,\n\tToolExecutionError,\n\ttype PreparingTool,\n} from '../domain/preparedCall.ts';\nimport { CommandCheckpoint, type CoveredFiles } from './commandCheckpoint.ts';\nimport { OutputFingerprint, withoutColors } from './commandOutput.ts';\nimport { type CommandProcessResult, runCommandProcess } from './commandProcess.ts';\nimport { definePreparingTool } from './defineTool.ts';\nimport type { WorkspaceWrites } from './workspaceWrites.ts';\nimport type { WriteSession } from './writeSession.ts';\n\n// Only what a toolchain needs to find itself and its locale. Everything else, the provider\n// keys included, stays in this process.\nconst inheritedVariables = ['PATH', 'HOME', 'TMPDIR', 'USER', 'SHELL', 'LANG'];\n// Long enough for a slow disk, short enough not to hold up building an agent.\nconst cwdCheckMs = 10_000;\n\n/** A command ready to run: its definition, and where it runs as a real path. */\ninterface ResolvedCommand {\n\tdefinition: CommandDefinition;\n\tcwd: string;\n}\n\n/**\n * `runCommand`, which runs one of the commands the consumer configured, by name, with the\n * parameters each one declares. Every call asks the approver, showing the program exactly as\n * it will run; `autoApprove` skips that.\n *\n * Around each command, the covered files are captured before and compared after, so what it\n * changed is in the run's history and can be undone: see `CommandCheckpoint`. If the capture\n * fails, the command does not run. A timeout or a cancellation stops its whole process group,\n * and is never reported as a completed command. If the group cannot be confirmed stopped, what\n * changed so far is recorded, the observation stays open, and writing stops for the run.\n *\n * This is not a sandbox: a command runs the repository's code, with everything this user can\n * reach. See \"Configured commands are trusted, not sandboxed\" in decisions.md.\n *\n * @throws {InvalidAgentConfigError} for an invalid configuration, a working directory outside\n * every `write` root, or on Windows, where a process group cannot be stopped.\n */\nexport async function createCommandTool({\n\tcommands,\n\tpolicy,\n\twrites,\n\tcovered,\n\tbase,\n\tplatform = process.platform,\n\trunProcess = runCommandProcess,\n}: {\n\tcommands: Commands;\n\tpolicy: AccessPolicy;\n\twrites: WorkspaceWrites;\n\tcovered: CoveredFiles;\n\t/** The real path of the first root, where a relative `cwd` starts. */\n\tbase: string;\n\tplatform?: NodeJS.Platform;\n\t/** Runs the program. Replaced in tests, to reach what a real process rarely does. */\n\trunProcess?: typeof runCommandProcess;\n}): Promise<PreparingTool> {\n\tif (platform === 'win32') {\n\t\tthrow new InvalidAgentConfigError(\n\t\t\t'Configured commands are not supported on Windows yet: the harness cannot stop a command and everything it started there',\n\t\t);\n\t}\n\tvalidateCommands(commands);\n\tconst resolved = new Map<string, ResolvedCommand>();\n\tfor (const [name, definition] of Object.entries(commands)) {\n\t\tresolved.set(name, {\n\t\t\tdefinition,\n\t\t\tcwd: await resolveCwd(`Command \"${name}\"`, definition.cwd, policy, base),\n\t\t});\n\t}\n\n\treturn definePreparingTool({\n\t\tname: 'runCommand',\n\t\tdescription: describeTool(commands),\n\t\tinput: inputSchema(commands),\n\t\tsummarize: ({ command: { name, ...values } }) => {\n\t\t\tconst definition = commands[name];\n\t\t\t// The schema only permits configured names, but keep this defensive for callers that\n\t\t\t// use a tool outside LLMAgent. This description never uses the resolved cwd.\n\t\t\treturn definition ? visibleCommand(definition, values) : name;\n\t\t},\n\t\tprepare: async ({ command: { name, ...values } }, signal, call) => {\n\t\t\tconst { run } = call;\n\t\t\tconst command = resolved.get(name);\n\t\t\t// The schema allows only the configured names.\n\t\t\tif (!command) throw new Error(`There is no command named \"${name}\"`);\n\t\t\t// A command can change anything in the write roots.\n\t\t\tawait writes.checkCeiling(call);\n\t\t\tconst paramValues: Record<string, ParameterValue> = {};\n\t\t\tfor (const parameter of parametersOf(command.definition)) {\n\t\t\t\tconst value =\n\t\t\t\t\t(values as Record<string, ParameterValue | undefined>)[parameter.name] ?? null;\n\t\t\t\tparamValues[parameter.name] =\n\t\t\t\t\tparameter.type === 'paths' && Array.isArray(value)\n\t\t\t\t\t\t? await resolvePaths(value, command.cwd, policy, signal)\n\t\t\t\t\t\t: value;\n\t\t\t}\n\t\t\tconst line: CommandLine = Object.freeze({\n\t\t\t\texecutable: command.definition.executable,\n\t\t\t\targs: Object.freeze(buildArguments(command.definition, paramValues)) as string[],\n\t\t\t\tcwd: command.cwd,\n\t\t\t});\n\n\t\t\t// Now, so a set of tools that cannot join the run's writes is refused before the user is asked.\n\t\t\twrites.sessionFor(run);\n\t\t\tlet ending: CommandEnding | undefined;\n\t\t\treturn {\n\t\t\t\t// A command can change anything its user can: it is always asked about.\n\t\t\t\trisk: 'destructive',\n\t\t\t\tcommand: line,\n\t\t\t\tending: () => ending,\n\t\t\t\trun: runSignal =>\n\t\t\t\t\trunCommand({\n\t\t\t\t\t\tname,\n\t\t\t\t\t\tline,\n\t\t\t\t\t\tdefinition: command.definition,\n\t\t\t\t\t\tcontext: run,\n\t\t\t\t\t\tsignal: runSignal,\n\t\t\t\t\t\tended: end => {\n\t\t\t\t\t\t\tending = end;\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t};\n\t\t},\n\t});\n\n\tinterface CommandRun {\n\t\tname: string;\n\t\tline: CommandLine;\n\t\tdefinition: CommandDefinition;\n\t\tcontext: RunContext;\n\t\tsignal: AbortSignal;\n\t\t/** Told how the command ended, once it ran to an end and everything it changed is recorded. */\n\t\tended: (ending: CommandEnding) => void;\n\t}\n\n\t// Alone: a change another agent of the run made while it ran would be taken for its own.\n\tfunction runCommand(run: CommandRun): Promise<string> {\n\t\tconst session = writes.sessionFor(run.context);\n\t\treturn session.exclusive(() => runCommandAlone(run, session), run.signal);\n\t}\n\n\tasync function runCommandAlone(\n\t\t{ name, line, definition, signal, ended }: CommandRun,\n\t\tsession: WriteSession,\n\t): Promise<string> {\n\t\tsignal.throwIfAborted();\n\t\tconst journal = await session.journalForChange();\n\t\tlet checkpoint: CommandCheckpoint;\n\t\ttry {\n\t\t\tcheckpoint = await CommandCheckpoint.begin({ journal, covered, by: name, signal });\n\t\t} catch (error) {\n\t\t\tsignal.throwIfAborted();\n\t\t\tthrow new EditRefusedError(\n\t\t\t\t`The harness could not capture the files before \"${name}\", so it did not run`,\n\t\t\t\t{ cause: error },\n\t\t\t);\n\t\t}\n\n\t\tlet result: CommandProcessResult | undefined;\n\t\tlet startFailure: unknown;\n\t\tconst fingerprint = new OutputFingerprint();\n\t\ttry {\n\t\t\tresult = await runProcess(line.executable, line.args, {\n\t\t\t\tcwd: line.cwd,\n\t\t\t\tenv: environmentFor(definition),\n\t\t\t\ttimeoutMs: definition.timeoutMs ?? defaultCommandTimeoutMs,\n\t\t\t\toutputBytes: definition.outputBytes ?? defaultCommandOutputBytes,\n\t\t\t\tsignal,\n\t\t\t\tonOutput: (stream, chunk) => {\n\t\t\t\t\tfingerprint.push(stream, chunk);\n\t\t\t\t},\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tstartFailure = error;\n\t\t}\n\n\t\tconst complete = result?.stopped ?? true;\n\t\tlet observed: Awaited<ReturnType<CommandCheckpoint['end']>>;\n\t\ttry {\n\t\t\tobserved = await checkpoint.end({ complete });\n\t\t} catch (error) {\n\t\t\tsession.stop('Writing stopped: the harness could not record what a command changed');\n\t\t\tthrow new EditUnconfirmedError(\n\t\t\t\t`\"${name}\" ran, but the harness could not record what it changed; writing has stopped for this run`,\n\t\t\t\t{ cause: error },\n\t\t\t);\n\t\t}\n\n\t\tif (!result) {\n\t\t\tsignal.throwIfAborted();\n\t\t\tthrow new EditRefusedError(`\"${name}\" could not be started`, { cause: startFailure });\n\t\t}\n\t\tconst report = describeRun(name, definition, result, observed);\n\t\tif (!complete) {\n\t\t\tsession.stop('Writing stopped: a command could not be stopped');\n\t\t\tthrow new EditUnconfirmedError(\n\t\t\t\t`${report}\\n\"${name}\" could not be stopped: something it started may still be changing files, and the history may not hold all of it. Writing has stopped for this run.`,\n\t\t\t);\n\t\t}\n\t\t// After its changes are recorded: a cancelled command is never reported as finished.\n\t\tif (result.outcome === 'cancelled') signal.throwIfAborted();\n\t\tended({\n\t\t\tpassed: result.outcome === 'exited' && result.exitCode === 0,\n\t\t\thow: howItEnded(result),\n\t\t\toutput: withoutColors(result.output),\n\t\t\tfingerprint: fingerprint.digest(),\n\t\t\t...(observed.unrecorded.length === 0 ? { state: journal.contentState() } : {}),\n\t\t\tchanged: journal.appliedPaths.map(path => displayPath(base, path)),\n\t\t});\n\t\tif (result.outcome !== 'exited') throw new ToolExecutionError(report);\n\t\tif (result.exitCode !== 0) {\n\t\t\tthrow new ToolExecutionError(report, {\n\t\t\t\t...(result.exitCode === null ? {} : { exitCode: result.exitCode }),\n\t\t\t});\n\t\t}\n\t\treturn report;\n\t}\n\n\tfunction describeRun(\n\t\tname: string,\n\t\tdefinition: CommandDefinition,\n\t\tresult: CommandProcessResult,\n\t\t{ changes, unrecorded }: Awaited<ReturnType<CommandCheckpoint['end']>>,\n\t): string {\n\t\tconst seconds = (result.durationMs / 1000).toFixed(1);\n\t\tconst lines: string[] = [];\n\t\tif (result.outcome === 'timedOut') {\n\t\t\tconst limit = (definition.timeoutMs ?? defaultCommandTimeoutMs) / 1000;\n\t\t\tlines.push(`\"${name}\" ran past its limit of ${String(limit)} s and was stopped.`);\n\t\t} else if (result.outcome === 'cancelled') {\n\t\t\tlines.push(`\"${name}\" was stopped: the run was cancelled.`);\n\t\t} else if (result.exitCode !== null) {\n\t\t\tlines.push(`\"${name}\" exited with code ${String(result.exitCode)} after ${seconds} s.`);\n\t\t} else {\n\t\t\tlines.push(`\"${name}\" was killed by ${result.signal ?? 'a signal'} after ${seconds} s.`);\n\t\t}\n\n\t\tconst shown = (path: string) => displayPath(base, path);\n\t\tlines.push(\n\t\t\tchanges.length === 0\n\t\t\t\t? 'It changed no file the history covers.'\n\t\t\t\t: `Files it changed, recorded in the history: ${changes.map(change => shown(change.path)).join(', ')}.`,\n\t\t);\n\t\tif (unrecorded.length > 0) {\n\t\t\tlines.push(\n\t\t\t\t`Not recorded, because the history cannot copy them (a folder, a link, or a file over 64 MB) before or after: ${unrecorded.map(shown).join(', ')}. Undo leaves them as they are.`,\n\t\t\t);\n\t\t}\n\n\t\tconst limit = definition.outputBytes ?? defaultCommandOutputBytes;\n\t\tif (result.printedBytes === 0) {\n\t\t\tlines.push('It printed nothing.');\n\t\t} else {\n\t\t\tlines.push(\n\t\t\t\tresult.printedBytes > limit\n\t\t\t\t\t? `Output, the last ${kilobytes(limit)} of ${kilobytes(result.printedBytes)}:`\n\t\t\t\t\t: 'Output:',\n\t\t\t\tresult.output,\n\t\t\t);\n\t\t}\n\t\treturn lines.join('\\n');\n\t}\n}\n\nfunction howItEnded(result: CommandProcessResult): string {\n\tif (result.outcome === 'timedOut') return 'ran past its time limit';\n\tif (result.outcome === 'cancelled') return 'was cancelled';\n\tif (result.exitCode !== null) return `exited with code ${String(result.exitCode)}`;\n\treturn `was killed by ${result.signal ?? 'a signal'}`;\n}\n\nfunction kilobytes(bytes: number): string {\n\treturn `${String(Math.ceil(bytes / 1024))} KB`;\n}\n\n/** The command line the model can see, before paths are resolved against the host workspace. */\nfunction visibleCommand(\n\tdefinition: CommandDefinition,\n\tvalues: Record<string, ParameterValue | undefined>,\n): string {\n\tconst args: string[] = [];\n\tfor (const argument of definition.args ?? []) {\n\t\tif (typeof argument === 'string') {\n\t\t\targs.push(argument);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst value = values[argument.name] ?? null;\n\t\tif (argument.type === 'paths') {\n\t\t\tif (Array.isArray(value)) args.push(...value);\n\t\t} else if (value !== null && typeof value === 'string') {\n\t\t\targs.push(value);\n\t\t}\n\t}\n\n\treturn [definition.executable, ...args].join(' ');\n}\n\n/**\n * Where a configured program runs, as a real path inside a `write` root. `program` names it in\n * the error, as `Command \"test\"`.\n */\nexport async function resolveCwd(\n\tprogram: string,\n\tcwd: string | undefined,\n\tpolicy: AccessPolicy,\n\tbase: string,\n): Promise<string> {\n\tconst named = resolve(base, cwd ?? '.');\n\tlet real: string;\n\ttry {\n\t\treal = await realpath(named);\n\t\tawait policy.check(real, 'write', AbortSignal.timeout(cwdCheckMs));\n\t} catch (error) {\n\t\tthrow new InvalidAgentConfigError(\n\t\t\t`${program} needs a cwd inside a write root of the workspace`,\n\t\t\t{ cause: error },\n\t\t);\n\t}\n\treturn real;\n}\n\n/**\n * Each path, once the policy lets the agent read it, relative to where the command runs. One\n * that would read as a flag is given as `./` and its name.\n */\nasync function resolvePaths(\n\tpaths: string[],\n\tcwd: string,\n\tpolicy: AccessPolicy,\n\tsignal: AbortSignal,\n): Promise<string[]> {\n\tconst resolved: string[] = [];\n\tfor (const path of paths) {\n\t\tconst { absolute } = await policy.check(path, 'read', signal);\n\t\tconst fromCwd = relative(cwd, absolute) || '.';\n\t\tresolved.push(fromCwd.startsWith('-') && !isAbsolute(fromCwd) ? `./${fromCwd}` : fromCwd);\n\t}\n\treturn resolved;\n}\n\n/** A small environment of its own: the inherited few, then what the consumer set. */\nexport function environmentFor(\n\tdefinition: Pick<CommandDefinition, 'env'>,\n\tenv: NodeJS.ProcessEnv = process.env,\n): NodeJS.ProcessEnv {\n\tconst inherited: NodeJS.ProcessEnv = {};\n\tfor (const [variable, value] of Object.entries(env)) {\n\t\tif (value === undefined) continue;\n\t\tif (inheritedVariables.includes(variable) || variable.startsWith('LC_')) {\n\t\t\tinherited[variable] = value;\n\t\t}\n\t}\n\treturn { ...inherited, ...definition.env };\n}\n\nfunction describeTool(commands: Commands): string {\n\tconst list = Object.entries(commands)\n\t\t.map(([name, { description }]) => `- ${name}: ${description}`)\n\t\t.join('\\n');\n\treturn [\n\t\t\"Runs one of the project's configured commands, by name, such as its tests or its formatter.\",\n\t\t'Files it changes are recorded in the history, so they can be undone. Its output is cut to its end.',\n\t\t'Commands:',\n\t\tlist,\n\t].join('\\n');\n}\n\n/**\n * One object per command, told apart by `name`, each with the parameters that command takes.\n * Strict mode needs every key present, so a parameter that may be left out takes `null`.\n */\nfunction inputSchema(commands: Commands) {\n\tconst variants = Object.entries(commands).map(([name, definition]) =>\n\t\tz.object({\n\t\t\tname: z.enum([name]),\n\t\t\t...Object.fromEntries(\n\t\t\t\tparametersOf(definition).map(parameter => [parameter.name, parameterSchema(parameter)]),\n\t\t\t),\n\t\t}),\n\t);\n\tconst [first, ...rest] = variants;\n\t// `validateCommands` refuses an empty configuration.\n\tif (!first) throw new InvalidAgentConfigError('No command is configured');\n\treturn z.object({\n\t\tcommand: (rest.length === 0 ? first : z.union([first, ...rest])).describe(\n\t\t\t'The command to run, and its parameters.',\n\t\t),\n\t});\n}\n\nfunction parameterSchema(parameter: CommandParameter): z.ZodType {\n\tswitch (parameter.type) {\n\t\tcase 'paths':\n\t\t\treturn z\n\t\t\t\t.array(z.string())\n\t\t\t\t.describe(`${parameter.description} Paths inside the workspace; [] for none.`);\n\t\tcase 'choice': {\n\t\t\tconst values = z.enum(parameter.values as [string, ...string[]]);\n\t\t\treturn parameter.required === true\n\t\t\t\t? values.describe(parameter.description)\n\t\t\t\t: values.nullable().describe(`${parameter.description} null to leave it out.`);\n\t\t}\n\t\tcase 'text': {\n\t\t\tconst text = z.string();\n\t\t\tconst description = `${parameter.description} Must match the regular expression ${parameter.pattern}, and not start with \"-\".`;\n\t\t\treturn parameter.required === true\n\t\t\t\t? text.describe(description)\n\t\t\t\t: text.nullable().describe(`${description} null to leave it out.`);\n\t\t}\n\t}\n}\n"]}
|
|
@@ -17,6 +17,8 @@ async function explore(question, subagent, explorer, signal, call) {
|
|
|
17
17
|
subagent,
|
|
18
18
|
task: question,
|
|
19
19
|
options: { signal },
|
|
20
|
+
// It only finds and reads.
|
|
21
|
+
writeCeiling: [],
|
|
20
22
|
});
|
|
21
23
|
try {
|
|
22
24
|
const { response, tokens } = await explorer().run(question, options);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exploreTool.js","sourceRoot":"","sources":["../../../src/tools/infrastructure/exploreTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAGjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEvE,wFAAwF;AACxF,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC;AAE9B;;;;GAIG;AACH,KAAK,UAAU,OAAO,CACrB,QAAgB,EAChB,QAAgB,EAChB,QAAqB,EACrB,MAAmB,EACnB,IAAqB;IAErB,2EAA2E;IAC3E,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,EAAE;QACrC,IAAI,EAAE,SAAS;QACf,IAAI,EAAE,UAAU;QAChB,QAAQ;QACR,IAAI,EAAE,QAAQ;QACd,OAAO,EAAE,EAAE,MAAM,EAAE;
|
|
1
|
+
{"version":3,"file":"exploreTool.js","sourceRoot":"","sources":["../../../src/tools/infrastructure/exploreTool.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod';\nimport type { Agent } from '#src/agent/domain/agent';\nimport { RecoverableError, UnrecoverableError } from '#src/shared/domain/errors';\nimport type { DelegatedAnswer, DelegatingTool } from '../domain/preparedCall.ts';\nimport type { ToolCallContext } from '../domain/readRegistry.ts';\nimport { defineDelegatingTool } from './defineTool.ts';\nimport { cutAnswer, failureOf, subagentOptions } from './subagents.ts';\n\n/** The most explorers one call sends, so a single call cannot fan out without bound. */\nexport const maxExplorers = 4;\n\n/**\n * Sends one explorer, a new agent that starts from its question alone. What it spends counts in\n * the run's usage, whether it answered or failed; a failure becomes its answer. A cancellation\n * escapes.\n */\nasync function explore(\n\tquestion: string,\n\tsubagent: number,\n\texplorer: () => Agent,\n\tsignal: AbortSignal,\n\tcall: ToolCallContext,\n): Promise<DelegatedAnswer> {\n\t// Within the run that made the call, so the explorer's reads belong to it.\n\tconst options = subagentOptions(call, {\n\t\ttool: 'explore',\n\t\tname: 'explorer',\n\t\tsubagent,\n\t\ttask: question,\n\t\toptions: { signal },\n\t\t// It only finds and reads.\n\t\twriteCeiling: [],\n\t});\n\ttry {\n\t\tconst { response, tokens } = await explorer().run(question, options);\n\t\tcall.spend(tokens, tokens === undefined);\n\t\treturn { text: cutAnswer(response || 'It found nothing to report.') };\n\t} catch (error) {\n\t\t// Whatever it threw once the run was cancelled, it ends as cancelled.\n\t\tsignal.throwIfAborted();\n\t\tif (!(error instanceof RecoverableError || error instanceof UnrecoverableError)) throw error;\n\t\tcall.spend(error.tokens, error.usageUnreported);\n\t\treturn { text: `The explorer failed: ${failureOf(error)}`, failed: true };\n\t}\n}\n\n/**\n * `explore` for the planner and the reviewer: it sends cheaper, read-only explorers, created by\n * `explorer`, to find what an agent needs in the repository, so the agent that called it reads\n * their reports instead of filling its own context with the files.\n */\nexport function createExploreTool(explorer: () => Agent): DelegatingTool {\n\treturn defineDelegatingTool({\n\t\tname: 'explore',\n\t\tdescription: `Sends read-only explorers to find things in the repository for you, one for each question, all at once. They work in the background: keep working meanwhile, and each explorer's report reaches you in a message of its own once it finishes. Give your final answer only once every explorer you sent has reported: an answer while they work is taken as waiting for them, and only the answer you give after the last report is kept. Each explorer starts from nothing but its question, so make each one complete: what to find, and what to report about it, such as which files and lines implement something and how. Ask up to ${String(maxExplorers)} independent questions in one call. Each answer is an explorer's report, not the files: read a file yourself when you need its exact text.`,\n\t\tinput: z.object({\n\t\t\tquestions: z.array(z.string()).describe('One complete, independent question per explorer.'),\n\t\t}),\n\t\tsummarize: ({ questions }) =>\n\t\t\tquestions.length === 1 ? (questions[0] ?? '') : `${String(questions.length)} questions`,\n\t\tdelegate: ({ questions }, _, call) => {\n\t\t\tconst asked = questions.map(question => question.trim());\n\t\t\tif (asked.length === 0 || asked.length > maxExplorers) {\n\t\t\t\treturn Promise.reject(\n\t\t\t\t\tnew Error(`Ask between 1 and ${String(maxExplorers)} questions in one call.`),\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (asked.some(question => question === '')) {\n\t\t\t\treturn Promise.reject(new Error('Every question needs some text.'));\n\t\t\t}\n\t\t\treturn Promise.resolve({\n\t\t\t\trisk: 'safe',\n\t\t\t\tstart: signal =>\n\t\t\t\t\tasked.map((question, index) => ({\n\t\t\t\t\t\tname: 'explorer',\n\t\t\t\t\t\tsubagent: index + 1,\n\t\t\t\t\t\ttask: question,\n\t\t\t\t\t\tanswer: explore(question, index + 1, explorer, signal, call),\n\t\t\t\t\t})),\n\t\t\t});\n\t\t},\n\t});\n}\n"]}
|
|
@@ -95,6 +95,7 @@ export async function openWorkspace(workspace, logger, { formatter } = {}) {
|
|
|
95
95
|
policy,
|
|
96
96
|
store,
|
|
97
97
|
...(fileFormatter ? { formatter: fileFormatter } : {}),
|
|
98
|
+
writeRoots: roots.filter(({ access }) => access === 'write').map(({ path }) => resolve(path)),
|
|
98
99
|
}),
|
|
99
100
|
writable: roots.some(({ access }) => access === 'write'),
|
|
100
101
|
base: root,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fileTools.js","sourceRoot":"","sources":["../../../src/tools/infrastructure/fileTools.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdir, realpath } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\nimport type { ToolDefinition } from '#src/llm/domain/tool';\nimport type { RecoveryStore } from '#src/recovery/domain/recoveryStore';\nimport {\n\tdefaultStateDirectory,\n\tFileRecoveryStore,\n} from '#src/recovery/infrastructure/fileRecoveryStore';\nimport { InvalidAgentConfigError, UnrecoverableError } from '#src/shared/domain/errors';\nimport type { ILogger } from '#src/shared/domain/logger';\nimport { describeFailure } from '#src/shared/domain/providerFailure';\nimport type { AccessPolicy, WorkspaceRoot } from '../domain/accessPolicy.ts';\nimport type { Commands, Formatter } from '../domain/commands.ts';\nimport type { AgentTool } from '../domain/preparedCall.ts';\nimport type { Workspace } from '../domain/workspace.ts';\nimport type { CoveredFiles } from './commandCheckpoint.ts';\nimport { createCommandTool } from './commandTool.ts';\nimport { chooseWorkspace } from './createWorkspace.ts';\nimport { createApplyPatchTool, createDeleteFileTool, createTextEditorTool } from './editFormats.ts';\nimport { FileFormatter } from './fileFormatter.ts';\nimport { GitIgnoreRules } from './gitIgnoreRules.ts';\nimport { PolicyWorkspace } from './policyWorkspace.ts';\nimport { RootsAccessPolicy, type SecretRules } from './rootsAccessPolicy.ts';\nimport { createTrackedReadFile, createTrackedReadFiles } from './trackedReadFile.ts';\nimport { createWorkspaceTools } from './workspaceTools.ts';\nimport { WorkspaceWrites } from './workspaceWrites.ts';\n\n/** The folders an agent works on, and what it may do in each. */\nexport interface WorkspaceOptions {\n\t/**\n\t * Each folder `read` or `write`. The first is where a relative path starts, and the one whose\n\t * history of runs is kept; a write to another root is recorded in it too.\n\t */\n\troots: WorkspaceRoot[];\n\t/** More files to keep closed as secrets, and exact files to open. */\n\tsecrets?: SecretRules;\n\t/**\n\t * Where the history of runs is kept, relative to the working directory if not absolute.\n\t * Defaults to the platform's state directory. No tool may reach it, even inside a root.\n\t */\n\tstateDirectory?: string;\n}\n\ndeclare const fileToolBrand: unique symbol;\n\n/**\n * A tool the harness builds for files, from `createFileTools`. It goes to an agent from\n * `createLLMAgent` beside the consumer's own tools; what it does inside is the harness's.\n */\nexport type FileTool = ToolDefinition & { readonly [fileToolBrand]: true };\n\n/** What the file tools of one workspace share: one policy, one history, one record of writes. */\nexport interface OpenedWorkspace {\n\treadonly roots: WorkspaceRoot[];\n\treadonly policy: AccessPolicy;\n\treadonly store: RecoveryStore;\n\t/** The read side, with the policy's word on every path. */\n\treadonly read: Workspace;\n\treadonly writes: WorkspaceWrites;\n\treadonly writable: boolean;\n\t/** The real path of the first root. */\n\treadonly base: string;\n\t/** Where the history is kept, as a real path. */\n\treadonly stateDirectory: string;\n\t/** What a command's checkpoint captures and compares: see `CoveredFiles`. */\n\treadonly covered: CoveredFiles;\n}\n\n/**\n * Where a workspace's history is kept, as one real path: made if missing, then resolved, links\n * included. The store and the access policy must name the same folder, or a tool could reach the\n * history the policy means to protect. A relative path is from the working directory.\n *\n * @throws {UnrecoverableError} when the folder cannot be made or reached.\n */\nexport async function stateDirectoryOf({ stateDirectory }: WorkspaceOptions): Promise<string> {\n\tconst directory = resolve(stateDirectory ?? defaultStateDirectory());\n\ttry {\n\t\tawait mkdir(directory, { recursive: true, mode: 0o700 });\n\t\treturn await realpath(directory);\n\t} catch (error) {\n\t\tthrow new UnrecoverableError('The folder for the workspace history could not be made', {\n\t\t\tcause: describeFailure(error),\n\t\t});\n\t}\n}\n\n/**\n * The recovery store of a workspace, at its first root.\n *\n * @throws {InvalidAgentConfigError} when the workspace has no root.\n * @throws {UnrecoverableError} when the store cannot be opened.\n */\nexport async function openStore(\n\tworkspace: WorkspaceOptions,\n\tdirectory: string,\n\tlogger: ILogger,\n): Promise<RecoveryStore> {\n\tconst [first] = workspace.roots;\n\tif (!first) throw new InvalidAgentConfigError('A workspace needs at least one root');\n\ttry {\n\t\treturn await FileRecoveryStore.open({\n\t\t\troot: resolve(first.path),\n\t\t\tdirectory,\n\t\t\tlogger,\n\t\t\twriteRoots: workspace.roots\n\t\t\t\t.filter(({ access }) => access === 'write')\n\t\t\t\t.map(({ path }) => resolve(path)),\n\t\t});\n\t} catch (error) {\n\t\tthrow new UnrecoverableError('The workspace history could not be opened', {\n\t\t\tcause: describeFailure(error),\n\t\t});\n\t}\n}\n\n/**\n * Opens what the file tools of one workspace share. With `formatter`, its edits format each file\n * they write.\n *\n * @throws {InvalidAgentConfigError} for a workspace with no root, or an invalid formatter.\n */\nexport async function openWorkspace(\n\tworkspace: WorkspaceOptions,\n\tlogger: ILogger,\n\t{ formatter }: { formatter?: Formatter } = {},\n): Promise<OpenedWorkspace> {\n\tconst { roots, secrets } = workspace;\n\tconst stateDirectory = await stateDirectoryOf(workspace);\n\tconst policy = await RootsAccessPolicy.create({\n\t\troots,\n\t\t...(secrets ? { secrets } : {}),\n\t\tignoreRules: new GitIgnoreRules(),\n\t\t// The history holds the source a run replaced: no tool may read or write it.\n\t\tprotectedPaths: [stateDirectory],\n\t});\n\tconst store = await openStore(workspace, stateDirectory, logger);\n\t// Real, as the policy's paths are, so a file's path can be judged by name.\n\tconst root = await realpath(resolve(roots[0]?.path ?? '.'));\n\tconst inner = await chooseWorkspace({\n\t\troot,\n\t\tlogger,\n\t\t// Left out before a search counts or stores a match, so no total, limit or error can tell\n\t\t// that a secret matched. The policy still decides each path after it.\n\t\thidden: path => policy.hidesFromReading(join(root, path)),\n\t});\n\tconst read = new PolicyWorkspace({ inner, policy });\n\tconst fileFormatter =\n\t\tformatter && (await FileFormatter.create({ formatter, policy, base: root }));\n\treturn {\n\t\troots,\n\t\tpolicy,\n\t\tstore,\n\t\tread,\n\t\twrites: new WorkspaceWrites({\n\t\t\tpolicy,\n\t\t\tstore,\n\t\t\t...(fileFormatter ? { formatter: fileFormatter } : {}),\n\t\t}),\n\t\twritable: roots.some(({ access }) => access === 'write'),\n\t\tbase: root,\n\t\tstateDirectory,\n\t\tcovered: coveredFiles(roots, policy, logger),\n\t};\n}\n\n/**\n * Lists every file of the `write` roots that `.gitignore` does not exclude, less what the policy\n * closes to writing by name, before anything is read. Each root is listed by its own program,\n * opened on the first listing.\n */\nfunction coveredFiles(\n\troots: WorkspaceRoot[],\n\tpolicy: RootsAccessPolicy,\n\tlogger: ILogger,\n): CoveredFiles {\n\tlet listers: Promise<{ root: string; workspace: Workspace }[]> | undefined;\n\tconst open = () =>\n\t\tPromise.all(\n\t\t\troots\n\t\t\t\t.filter(({ access }) => access === 'write')\n\t\t\t\t.map(async ({ path }) => {\n\t\t\t\t\tconst root = await realpath(resolve(path));\n\t\t\t\t\tconst workspace = await chooseWorkspace({\n\t\t\t\t\t\troot,\n\t\t\t\t\t\tlogger,\n\t\t\t\t\t\thidden: relativePath => policy.closedToWriting(join(root, relativePath)),\n\t\t\t\t\t});\n\t\t\t\t\treturn { root, workspace };\n\t\t\t\t}),\n\t\t);\n\n\treturn async signal => {\n\t\tlisters ??= open().catch((error: unknown) => {\n\t\t\t// Not kept: the next command tries again.\n\t\t\tlisters = undefined;\n\t\t\tthrow error;\n\t\t});\n\t\tconst files = new Set<string>();\n\t\tfor (const { root, workspace } of await listers) {\n\t\t\tconst listed = await workspace.listFiles({ limit: Number.MAX_SAFE_INTEGER }, signal);\n\t\t\tfor (const path of listed.files) files.add(join(root, path));\n\t\t}\n\t\treturn [...files];\n\t};\n}\n\n/**\n * `runCommand` over an opened workspace: see `createCommandTool`.\n *\n * @throws {InvalidAgentConfigError} for an invalid configuration, or on Windows.\n */\nexport function commandToolFor(workspace: OpenedWorkspace, commands: Commands): Promise<AgentTool> {\n\tconst { policy, writes, covered, base } = workspace;\n\treturn createCommandTool({ commands, policy, writes, covered, base });\n}\n\n/**\n * The file tools of a provider's executor, over one workspace. Every one reads through the access\n * policy, and an edit starts only from a version the conversation read.\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 * Without a `write` root they only read: `readFile` and `readFiles` in place of the editor and\n * the patch.\n */\nexport function fileToolsFor(\n\tprovider: 'anthropic' | 'openai',\n\tworkspace: OpenedWorkspace,\n): AgentTool[] {\n\tconst { policy, read, writes, writable } = workspace;\n\tconst [listFiles, searchText] = createWorkspaceTools(read).filter(\n\t\ttool => tool.name === 'listFiles' || tool.name === 'searchText',\n\t);\n\tconst finding: AgentTool[] = [listFiles, searchText].filter(tool => tool !== undefined);\n\tconst reading = [createTrackedReadFile(policy), createTrackedReadFiles(policy)];\n\tif (!writable) return [...finding, ...reading];\n\tif (provider === 'openai') return [...finding, ...reading, createApplyPatchTool(writes)];\n\treturn [\n\t\t...finding,\n\t\tcreateTextEditorTool({ writes, policy, workspace: read }),\n\t\tcreateDeleteFileTool(writes),\n\t];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"fileTools.js","sourceRoot":"","sources":["../../../src/tools/infrastructure/fileTools.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdir, realpath } from 'node:fs/promises';\nimport { join, resolve } from 'node:path';\nimport type { ToolDefinition } from '#src/llm/domain/tool';\nimport type { RecoveryStore } from '#src/recovery/domain/recoveryStore';\nimport {\n\tdefaultStateDirectory,\n\tFileRecoveryStore,\n} from '#src/recovery/infrastructure/fileRecoveryStore';\nimport { InvalidAgentConfigError, UnrecoverableError } from '#src/shared/domain/errors';\nimport type { ILogger } from '#src/shared/domain/logger';\nimport { describeFailure } from '#src/shared/domain/providerFailure';\nimport type { AccessPolicy, WorkspaceRoot } from '../domain/accessPolicy.ts';\nimport type { Commands, Formatter } from '../domain/commands.ts';\nimport type { AgentTool } from '../domain/preparedCall.ts';\nimport type { Workspace } from '../domain/workspace.ts';\nimport type { CoveredFiles } from './commandCheckpoint.ts';\nimport { createCommandTool } from './commandTool.ts';\nimport { chooseWorkspace } from './createWorkspace.ts';\nimport { createApplyPatchTool, createDeleteFileTool, createTextEditorTool } from './editFormats.ts';\nimport { FileFormatter } from './fileFormatter.ts';\nimport { GitIgnoreRules } from './gitIgnoreRules.ts';\nimport { PolicyWorkspace } from './policyWorkspace.ts';\nimport { RootsAccessPolicy, type SecretRules } from './rootsAccessPolicy.ts';\nimport { createTrackedReadFile, createTrackedReadFiles } from './trackedReadFile.ts';\nimport { createWorkspaceTools } from './workspaceTools.ts';\nimport { WorkspaceWrites } from './workspaceWrites.ts';\n\n/** The folders an agent works on, and what it may do in each. */\nexport interface WorkspaceOptions {\n\t/**\n\t * Each folder `read` or `write`. The first is where a relative path starts, and the one whose\n\t * history of runs is kept; a write to another root is recorded in it too.\n\t */\n\troots: WorkspaceRoot[];\n\t/** More files to keep closed as secrets, and exact files to open. */\n\tsecrets?: SecretRules;\n\t/**\n\t * Where the history of runs is kept, relative to the working directory if not absolute.\n\t * Defaults to the platform's state directory. No tool may reach it, even inside a root.\n\t */\n\tstateDirectory?: string;\n}\n\ndeclare const fileToolBrand: unique symbol;\n\n/**\n * A tool the harness builds for files, from `createFileTools`. It goes to an agent from\n * `createLLMAgent` beside the consumer's own tools; what it does inside is the harness's.\n */\nexport type FileTool = ToolDefinition & { readonly [fileToolBrand]: true };\n\n/** What the file tools of one workspace share: one policy, one history, one record of writes. */\nexport interface OpenedWorkspace {\n\treadonly roots: WorkspaceRoot[];\n\treadonly policy: AccessPolicy;\n\treadonly store: RecoveryStore;\n\t/** The read side, with the policy's word on every path. */\n\treadonly read: Workspace;\n\treadonly writes: WorkspaceWrites;\n\treadonly writable: boolean;\n\t/** The real path of the first root. */\n\treadonly base: string;\n\t/** Where the history is kept, as a real path. */\n\treadonly stateDirectory: string;\n\t/** What a command's checkpoint captures and compares: see `CoveredFiles`. */\n\treadonly covered: CoveredFiles;\n}\n\n/**\n * Where a workspace's history is kept, as one real path: made if missing, then resolved, links\n * included. The store and the access policy must name the same folder, or a tool could reach the\n * history the policy means to protect. A relative path is from the working directory.\n *\n * @throws {UnrecoverableError} when the folder cannot be made or reached.\n */\nexport async function stateDirectoryOf({ stateDirectory }: WorkspaceOptions): Promise<string> {\n\tconst directory = resolve(stateDirectory ?? defaultStateDirectory());\n\ttry {\n\t\tawait mkdir(directory, { recursive: true, mode: 0o700 });\n\t\treturn await realpath(directory);\n\t} catch (error) {\n\t\tthrow new UnrecoverableError('The folder for the workspace history could not be made', {\n\t\t\tcause: describeFailure(error),\n\t\t});\n\t}\n}\n\n/**\n * The recovery store of a workspace, at its first root.\n *\n * @throws {InvalidAgentConfigError} when the workspace has no root.\n * @throws {UnrecoverableError} when the store cannot be opened.\n */\nexport async function openStore(\n\tworkspace: WorkspaceOptions,\n\tdirectory: string,\n\tlogger: ILogger,\n): Promise<RecoveryStore> {\n\tconst [first] = workspace.roots;\n\tif (!first) throw new InvalidAgentConfigError('A workspace needs at least one root');\n\ttry {\n\t\treturn await FileRecoveryStore.open({\n\t\t\troot: resolve(first.path),\n\t\t\tdirectory,\n\t\t\tlogger,\n\t\t\twriteRoots: workspace.roots\n\t\t\t\t.filter(({ access }) => access === 'write')\n\t\t\t\t.map(({ path }) => resolve(path)),\n\t\t});\n\t} catch (error) {\n\t\tthrow new UnrecoverableError('The workspace history could not be opened', {\n\t\t\tcause: describeFailure(error),\n\t\t});\n\t}\n}\n\n/**\n * Opens what the file tools of one workspace share. With `formatter`, its edits format each file\n * they write.\n *\n * @throws {InvalidAgentConfigError} for a workspace with no root, or an invalid formatter.\n */\nexport async function openWorkspace(\n\tworkspace: WorkspaceOptions,\n\tlogger: ILogger,\n\t{ formatter }: { formatter?: Formatter } = {},\n): Promise<OpenedWorkspace> {\n\tconst { roots, secrets } = workspace;\n\tconst stateDirectory = await stateDirectoryOf(workspace);\n\tconst policy = await RootsAccessPolicy.create({\n\t\troots,\n\t\t...(secrets ? { secrets } : {}),\n\t\tignoreRules: new GitIgnoreRules(),\n\t\t// The history holds the source a run replaced: no tool may read or write it.\n\t\tprotectedPaths: [stateDirectory],\n\t});\n\tconst store = await openStore(workspace, stateDirectory, logger);\n\t// Real, as the policy's paths are, so a file's path can be judged by name.\n\tconst root = await realpath(resolve(roots[0]?.path ?? '.'));\n\tconst inner = await chooseWorkspace({\n\t\troot,\n\t\tlogger,\n\t\t// Left out before a search counts or stores a match, so no total, limit or error can tell\n\t\t// that a secret matched. The policy still decides each path after it.\n\t\thidden: path => policy.hidesFromReading(join(root, path)),\n\t});\n\tconst read = new PolicyWorkspace({ inner, policy });\n\tconst fileFormatter =\n\t\tformatter && (await FileFormatter.create({ formatter, policy, base: root }));\n\treturn {\n\t\troots,\n\t\tpolicy,\n\t\tstore,\n\t\tread,\n\t\twrites: new WorkspaceWrites({\n\t\t\tpolicy,\n\t\t\tstore,\n\t\t\t...(fileFormatter ? { formatter: fileFormatter } : {}),\n\t\t\twriteRoots: roots.filter(({ access }) => access === 'write').map(({ path }) => resolve(path)),\n\t\t}),\n\t\twritable: roots.some(({ access }) => access === 'write'),\n\t\tbase: root,\n\t\tstateDirectory,\n\t\tcovered: coveredFiles(roots, policy, logger),\n\t};\n}\n\n/**\n * Lists every file of the `write` roots that `.gitignore` does not exclude, less what the policy\n * closes to writing by name, before anything is read. Each root is listed by its own program,\n * opened on the first listing.\n */\nfunction coveredFiles(\n\troots: WorkspaceRoot[],\n\tpolicy: RootsAccessPolicy,\n\tlogger: ILogger,\n): CoveredFiles {\n\tlet listers: Promise<{ root: string; workspace: Workspace }[]> | undefined;\n\tconst open = () =>\n\t\tPromise.all(\n\t\t\troots\n\t\t\t\t.filter(({ access }) => access === 'write')\n\t\t\t\t.map(async ({ path }) => {\n\t\t\t\t\tconst root = await realpath(resolve(path));\n\t\t\t\t\tconst workspace = await chooseWorkspace({\n\t\t\t\t\t\troot,\n\t\t\t\t\t\tlogger,\n\t\t\t\t\t\thidden: relativePath => policy.closedToWriting(join(root, relativePath)),\n\t\t\t\t\t});\n\t\t\t\t\treturn { root, workspace };\n\t\t\t\t}),\n\t\t);\n\n\treturn async signal => {\n\t\tlisters ??= open().catch((error: unknown) => {\n\t\t\t// Not kept: the next command tries again.\n\t\t\tlisters = undefined;\n\t\t\tthrow error;\n\t\t});\n\t\tconst files = new Set<string>();\n\t\tfor (const { root, workspace } of await listers) {\n\t\t\tconst listed = await workspace.listFiles({ limit: Number.MAX_SAFE_INTEGER }, signal);\n\t\t\tfor (const path of listed.files) files.add(join(root, path));\n\t\t}\n\t\treturn [...files];\n\t};\n}\n\n/**\n * `runCommand` over an opened workspace: see `createCommandTool`.\n *\n * @throws {InvalidAgentConfigError} for an invalid configuration, or on Windows.\n */\nexport function commandToolFor(workspace: OpenedWorkspace, commands: Commands): Promise<AgentTool> {\n\tconst { policy, writes, covered, base } = workspace;\n\treturn createCommandTool({ commands, policy, writes, covered, base });\n}\n\n/**\n * The file tools of a provider's executor, over one workspace. Every one reads through the access\n * policy, and an edit starts only from a version the conversation read.\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 * Without a `write` root they only read: `readFile` and `readFiles` in place of the editor and\n * the patch.\n */\nexport function fileToolsFor(\n\tprovider: 'anthropic' | 'openai',\n\tworkspace: OpenedWorkspace,\n): AgentTool[] {\n\tconst { policy, read, writes, writable } = workspace;\n\tconst [listFiles, searchText] = createWorkspaceTools(read).filter(\n\t\ttool => tool.name === 'listFiles' || tool.name === 'searchText',\n\t);\n\tconst finding: AgentTool[] = [listFiles, searchText].filter(tool => tool !== undefined);\n\tconst reading = [createTrackedReadFile(policy), createTrackedReadFiles(policy)];\n\tif (!writable) return [...finding, ...reading];\n\tif (provider === 'openai') return [...finding, ...reading, createApplyPatchTool(writes)];\n\treturn [\n\t\t...finding,\n\t\tcreateTextEditorTool({ writes, policy, workspace: read }),\n\t\tcreateDeleteFileTool(writes),\n\t];\n}\n"]}
|
|
@@ -4,6 +4,7 @@ import { z } from 'zod';
|
|
|
4
4
|
import { WorkspaceBusyError } from '#src/recovery/domain/recoveryStore';
|
|
5
5
|
import { FolderClaims } from '#src/recovery/infrastructure/folderClaims';
|
|
6
6
|
import { definePreparingTool } from "./defineTool.js";
|
|
7
|
+
import { ceilingRefusal } from "./writeCeiling.js";
|
|
7
8
|
import { git, listWorktrees } from "./worktrees.js";
|
|
8
9
|
function within(path, folder) {
|
|
9
10
|
return path === folder || path.startsWith(`${folder}${sep}`);
|
|
@@ -120,11 +121,16 @@ export function createRemoveWorktreeTool({ base, roots, stateDirectory, }) {
|
|
|
120
121
|
.describe('The folder of the worktree, absolute or relative to the first root.'),
|
|
121
122
|
}),
|
|
122
123
|
summarize: ({ path }) => path,
|
|
123
|
-
prepare: async ({ path }, signal) => {
|
|
124
|
+
prepare: async ({ path }, signal, { writeCeiling }) => {
|
|
124
125
|
const asked = resolve(base, path);
|
|
125
126
|
const target = await realpath(asked).catch(() => {
|
|
126
127
|
throw new Error(`There is no folder at ${asked}.`);
|
|
127
128
|
});
|
|
129
|
+
// Removing it writes there, which an agent another agent started may do only where that
|
|
130
|
+
// agent may.
|
|
131
|
+
const refusal = ceilingRefusal([target], writeCeiling);
|
|
132
|
+
if (refusal !== undefined)
|
|
133
|
+
throw new Error(refusal);
|
|
128
134
|
const worktree = await removableWorktree(target, { base, roots }, signal);
|
|
129
135
|
const { removal, fingerprint } = await inspect(target, worktree.branch, signal);
|
|
130
136
|
return {
|