@mikode13/harness 9.3.0 → 10.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +66 -8
  2. package/dist/agent/domain/agent.d.ts +11 -5
  3. package/dist/agent/domain/agent.js.map +1 -1
  4. package/dist/agent/domain/approval.d.ts +2 -1
  5. package/dist/agent/domain/approval.js +2 -1
  6. package/dist/agent/domain/approval.js.map +1 -1
  7. package/dist/agent/domain/question.d.ts +5 -0
  8. package/dist/agent/domain/question.js +11 -0
  9. package/dist/agent/domain/question.js.map +1 -1
  10. package/dist/agent/domain/runContext.d.ts +17 -0
  11. package/dist/agent/domain/runContext.js +9 -0
  12. package/dist/agent/domain/runContext.js.map +1 -1
  13. package/dist/agent/domain/transcript.d.ts +10 -0
  14. package/dist/agent/domain/transcript.js.map +1 -1
  15. package/dist/engines/domain/model/askUser.d.ts +0 -5
  16. package/dist/engines/domain/model/askUser.js +0 -9
  17. package/dist/engines/domain/model/askUser.js.map +1 -1
  18. package/dist/engines/domain/model/historyFollower.d.ts +1 -1
  19. package/dist/engines/domain/model/historyFollower.js +14 -3
  20. package/dist/engines/domain/model/historyFollower.js.map +1 -1
  21. package/dist/engines/domain/model/llmAgent.d.ts +19 -3
  22. package/dist/engines/domain/model/llmAgent.js +141 -51
  23. package/dist/engines/domain/model/llmAgent.js.map +1 -1
  24. package/dist/factory/infrastructure/agentLLMFactory.d.ts +6 -2
  25. package/dist/factory/infrastructure/agentLLMFactory.js +4 -4
  26. package/dist/factory/infrastructure/agentLLMFactory.js.map +1 -1
  27. package/dist/index.d.ts +1 -0
  28. package/dist/index.js +1 -0
  29. package/dist/index.js.map +1 -1
  30. package/dist/recovery/domain/history.d.ts +3 -2
  31. package/dist/recovery/domain/history.js +6 -2
  32. package/dist/recovery/domain/history.js.map +1 -1
  33. package/dist/recovery/domain/recoveryStore.d.ts +47 -5
  34. package/dist/recovery/domain/recoveryStore.js.map +1 -1
  35. package/dist/recovery/domain/runChanges.js +11 -2
  36. package/dist/recovery/domain/runChanges.js.map +1 -1
  37. package/dist/recovery/infrastructure/fileRecoveryStore.d.ts +41 -9
  38. package/dist/recovery/infrastructure/fileRecoveryStore.js +217 -65
  39. package/dist/recovery/infrastructure/fileRecoveryStore.js.map +1 -1
  40. package/dist/tools/domain/preparedCall.d.ts +14 -0
  41. package/dist/tools/domain/preparedCall.js +12 -0
  42. package/dist/tools/domain/preparedCall.js.map +1 -1
  43. package/dist/tools/domain/readRegistry.d.ts +7 -0
  44. package/dist/tools/domain/readRegistry.js.map +1 -1
  45. package/dist/tools/infrastructure/agentAsTool.d.ts +61 -0
  46. package/dist/tools/infrastructure/agentAsTool.js +144 -0
  47. package/dist/tools/infrastructure/agentAsTool.js.map +1 -0
  48. package/dist/tools/infrastructure/commandCheckpoint.d.ts +3 -2
  49. package/dist/tools/infrastructure/commandCheckpoint.js +21 -9
  50. package/dist/tools/infrastructure/commandCheckpoint.js.map +1 -1
  51. package/dist/tools/infrastructure/commandTool.js +7 -3
  52. package/dist/tools/infrastructure/commandTool.js.map +1 -1
  53. package/dist/tools/infrastructure/exploreTool.js +9 -22
  54. package/dist/tools/infrastructure/exploreTool.js.map +1 -1
  55. package/dist/tools/infrastructure/fileEditor.d.ts +2 -0
  56. package/dist/tools/infrastructure/fileEditor.js +5 -1
  57. package/dist/tools/infrastructure/fileEditor.js.map +1 -1
  58. package/dist/tools/infrastructure/fileFormatter.d.ts +1 -0
  59. package/dist/tools/infrastructure/fileFormatter.js +5 -1
  60. package/dist/tools/infrastructure/fileFormatter.js.map +1 -1
  61. package/dist/tools/infrastructure/fileTools.d.ts +1 -1
  62. package/dist/tools/infrastructure/fileTools.js +3 -3
  63. package/dist/tools/infrastructure/fileTools.js.map +1 -1
  64. package/dist/tools/infrastructure/subagents.d.ts +19 -0
  65. package/dist/tools/infrastructure/subagents.js +29 -0
  66. package/dist/tools/infrastructure/subagents.js.map +1 -0
  67. package/dist/tools/infrastructure/workspaceWrites.d.ts +8 -5
  68. package/dist/tools/infrastructure/workspaceWrites.js +19 -7
  69. package/dist/tools/infrastructure/workspaceWrites.js.map +1 -1
  70. package/dist/tools/infrastructure/writeSession.d.ts +8 -0
  71. package/dist/tools/infrastructure/writeSession.js +37 -0
  72. package/dist/tools/infrastructure/writeSession.js.map +1 -1
  73. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"agentLLMFactory.js","sourceRoot":"","sources":["../../../src/factory/infrastructure/agentLLMFactory.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolve } from 'node:path';\nimport type { Agent } from '#src/agent/domain/agent';\nimport type { Transcript, TranscriptCallback } from '#src/agent/domain/transcript';\nimport type { HistoryReader } from '#src/engines/domain/model/historyFollower';\nimport { LLMAgent } from '#src/engines/domain/model/llmAgent';\nimport type { LLMClient } from '#src/llm/domain/llm';\nimport {\n\tClaudeLLMClient,\n\tclaudeLLMModels,\n\tclaudeLLMReasoningEfforts,\n} from '#src/llm/infrastructure/claudeLLMClient';\nimport {\n\tOpenAILLMClient,\n\topenAIModels,\n\topenAIReasoningEfforts,\n} from '#src/llm/infrastructure/openAILLMClient';\nimport {\n\texecutorInstructions,\n\texecutorInstructionsWithoutCommands,\n\tOrchestratorAgent,\n\tplannerInstructions,\n\treviewerInstructions,\n\treviewerInstructionsWithoutCommands,\n} from '#src/orchestration/domain/model/orchestratorAgent';\nimport { ReviewerDecisionValidator } from '#src/orchestration/infrastructure/model/reviewerDecisionValidator';\nimport type { RecoveryStore } from '#src/recovery/domain/recoveryStore';\nimport { RetryingAgent } from '#src/retry/domain/model/retryingAgent';\nimport { InvalidAgentConfigError } from '#src/shared/domain/errors';\nimport { isOneOf } from '#src/shared/domain/isOneOf';\nimport type { ILogger } from '#src/shared/domain/logger';\nimport { Logger } from '#src/shared/infrastructure/logger';\nimport type { WorkspaceRoot } from '#src/tools/domain/accessPolicy';\nimport {\n\ttype CommandRole,\n\ttype Commands,\n\tcommandsFor,\n\ttype Formatter,\n\tvalidateCommands,\n\tvalidateFormatter,\n} from '#src/tools/domain/commands';\nimport type { AgentTool } from '#src/tools/domain/preparedCall';\nimport type { Tool } from '#src/tools/domain/tool';\nimport {\n\tcommandToolFor,\n\ttype FileTool,\n\tfileToolsFor,\n\ttype OpenedWorkspace,\n\topenStore,\n\topenWorkspace,\n\tstateDirectoryOf,\n\ttype WorkspaceOptions,\n} from '#src/tools/infrastructure/fileTools';\nimport { createExploreTool } from '#src/tools/infrastructure/exploreTool';\nimport { createShowChangesTool } from '#src/tools/infrastructure/showChangesTool';\nimport {\n\tcreateTrackedReadFile,\n\tcreateTrackedReadFiles,\n} from '#src/tools/infrastructure/trackedReadFile';\nimport { createWorkspaceTools } from '#src/tools/infrastructure/workspaceTools';\nimport { isAgentProvider, orchestratorRolesFor, type Role } from './agentFactory.ts';\nimport {\n\tagentProviders,\n\ttype AgentModel,\n\ttype AgentProvider,\n\ttype CreateOrchestratorOptions,\n\ttype ReasoningEffort,\n} from './types.ts';\n\n/** The models each provider's model API accepts. */\nexport const llmModels = {\n\tanthropic: claudeLLMModels,\n\topenai: openAIModels,\n} as const satisfies Readonly<Record<AgentProvider, readonly AgentModel[]>>;\n\n/** The reasoning efforts each provider's model API accepts. */\nexport const llmReasoningEfforts = {\n\tanthropic: claudeLLMReasoningEfforts,\n\topenai: openAIReasoningEfforts,\n} as const satisfies Readonly<Record<AgentProvider, readonly ReasoningEffort[]>>;\n\n/** Narrows a model to one accepted by the selected provider's model API. */\nexport function isLLMModelOf<Provider extends AgentProvider>(\n\tprovider: Provider,\n\tvalue: string,\n): value is (typeof llmModels)[Provider][number] {\n\treturn isOneOf(llmModels[provider], value);\n}\n\n/** Narrows a reasoning effort to one accepted by the selected provider's model API. */\nexport function isLLMReasoningEffortOf<Provider extends AgentProvider>(\n\tprovider: Provider,\n\tvalue: string,\n): value is (typeof llmReasoningEfforts)[Provider][number] {\n\treturn isOneOf(llmReasoningEfforts[provider], value);\n}\n\nexport interface CreateLLMAgentOptions {\n\t/**\n\t * Defaults to `'sonnet'` on Anthropic and `'gpt-5.6-luna'` on OpenAI. The provider's client\n\t * rejects a model it does not support.\n\t */\n\tmodel?: AgentModel;\n\t/** Defaults to `'high'`. The provider's client rejects an effort it does not support. */\n\treasoningEffort?: ReasoningEffort;\n\t/** Sent as the model's instructions on every call. */\n\tsystemPrompt: string;\n\t/** The tools the model may call: the consumer's own, and those of `createFileTools`. Defaults to none. */\n\ttools?: (Tool | FileTool)[];\n\t/**\n\t * The folders the agent works on, the same as given to `createFileTools`. With it, the agent\n\t * follows the workspace's history: after the user undoes runs, its next run starts from the\n\t * conversation as it was, and is told what was undone. The system prompt is followed by the\n\t * roots and how to name a file in them.\n\t */\n\tworkspace?: WorkspaceOptions;\n\t/**\n\t * With `workspace`, a summary of what was undone, from the provider's cheap model, in place\n\t * of the undone prompts. That is Claude Haiku 5.5 or `gpt-5.6-luna`, at high effort, and the\n\t * call is billed with the run's tokens. Defaults to `true`.\n\t */\n\tsummarizeUndone?: boolean;\n\t/**\n\t * How many calls to the model one run may make before it fails. Defaults to 65, with no upper\n\t * bound. A run that asks for exactly the same tool calls 5 times in a row fails before that.\n\t */\n\tmaxSteps?: number;\n\t/**\n\t * The input tokens at which the provider compacts the conversation on its side: it replaces\n\t * every turn before that call with its own summary, which only its model reads back. The\n\t * conversation keeps every original turn. Without it, the conversation is never compacted.\n\t * At least 50,000 on Anthropic.\n\t */\n\tcompactAt?: number;\n\t/**\n\t * Runs every tool call without asking, `destructive` ones included, for CI where no one can\n\t * answer. Without it, a `destructive` call goes to the run's `approve`, or is denied.\n\t */\n\tautoApprove?: boolean;\n\t/** Defaults to warnings on stderr. */\n\tlogger?: ILogger;\n\t/**\n\t * Receives what the model saw in each run once the run ended, completed, failed or\n\t * cancelled, for debugging. See `Transcript`. Without it, nothing is kept.\n\t */\n\tonTranscript?: TranscriptCallback;\n}\n\n/** A limit per role of `createLLMOrchestrator`; a role left out keeps its default. */\nexport type RoleSteps = Partial<Record<OrchestratorRole, number>>;\n\n/** A number of tokens per role of `createLLMOrchestrator`; a role left out keeps its default. */\nexport type RoleTokens = Partial<Record<OrchestratorRole, number>>;\n\n// The same three roles a command can be given to.\ntype OrchestratorRole = CommandRole;\n\n// The executor reads, edits and checks, so it needs more calls than the roles that only read.\nconst defaultRoleSteps: Record<OrchestratorRole, number> = {\n\tplanner: 65,\n\texecutor: 80,\n\treviewer: 65,\n};\n\n// No role compacts unless asked to.\nconst defaultRoleCompaction: Record<OrchestratorRole, number | undefined> = {\n\tplanner: undefined,\n\texecutor: undefined,\n\treviewer: undefined,\n};\n\n/**\n * Each role's value of the option `name`, from one number for all of them or a number per role.\n * Each number is checked by whatever takes it.\n *\n * @throws {InvalidAgentConfigError} for a value that is neither, and for a role that does not\n * exist, which is a typo that would otherwise leave that role on its default without a word.\n */\nfunction perRole<T extends number | undefined>(\n\tname: string,\n\tvalue: unknown,\n\tdefaults: Record<OrchestratorRole, T>,\n\t{ unit, example }: { unit: string; example: string },\n): Record<OrchestratorRole, T | number> {\n\tif (value === undefined) return defaults;\n\tif (typeof value === 'number') return { planner: value, executor: value, reviewer: value };\n\t// Untyped input, such as the CLI's configuration file, can hold `null` or a list here.\n\tif (typeof value !== 'object' || value === null || Array.isArray(value)) {\n\t\tthrow new InvalidAgentConfigError(\n\t\t\t`${name} must be a number or an object of ${unit} per role, such as ${example}`,\n\t\t);\n\t}\n\tconst unknown = Object.keys(value).filter(role => !(role in defaults));\n\tif (unknown.length > 0) {\n\t\tthrow new InvalidAgentConfigError(\n\t\t\t`${name} names roles that do not exist: ${unknown.join(', ')}; expected planner, executor or reviewer`,\n\t\t);\n\t}\n\t// A role given as `undefined`, which untyped input can do, keeps its default.\n\tconst given = Object.entries(value as Record<string, unknown>).filter(\n\t\t([, each]) => each !== undefined,\n\t);\n\treturn { ...defaults, ...Object.fromEntries(given) };\n}\n\nexport interface CreateLLMOrchestratorOptions extends CreateOrchestratorOptions {\n\t/**\n\t * The folders the agents work on. The executor changes files only in a `write` root, every\n\t * change recorded in the workspace's history. Defaults to the current working directory,\n\t * read only.\n\t */\n\tworkspace?: WorkspaceOptions;\n\t/**\n\t * How many calls to the model one run of each role may make: one number for every role, or a\n\t * number per role. Defaults to 80 for the executor and 65 for the planner and the reviewer,\n\t * with no upper bound. A run that asks for exactly the same tool calls 5 times in a row fails\n\t * before its limit.\n\t */\n\tmaxSteps?: number | RoleSteps;\n\t/**\n\t * The input tokens at which each role's provider compacts its conversation, as\n\t * `CreateLLMAgentOptions.compactAt`: one number for every role, or a number per role. Without\n\t * it, no role compacts.\n\t */\n\tcompactAt?: number | RoleTokens;\n\t/**\n\t * Commands the roles may run, such as the project's tests, through `runCommand`. Each goes to\n\t * the roles its `roles` names, the executor by default. See `createFileTools`. Each needs a\n\t * `write` root to run in.\n\t */\n\tcommands?: Commands;\n\t/**\n\t * A program the executor's edits run on each file they create or change, such as the\n\t * project's formatter. See `createFileTools`. It needs a `write` root to run in.\n\t */\n\tformatter?: Formatter;\n\t/**\n\t * A summary of what the user undid, from the planner's provider's cheap model, for every\n\t * role. See `CreateLLMAgentOptions.summarizeUndone`. Defaults to `true`.\n\t */\n\tsummarizeUndone?: boolean;\n\t/**\n\t * Receives what the model saw in each run of each role and each explorer, once the run\n\t * ended, for debugging. Each transcript names its role. See `Transcript`.\n\t */\n\tonTranscript?: TranscriptCallback;\n}\n\nconst defaultLLMAgentModels = {\n\tanthropic: 'sonnet',\n\topenai: 'gpt-5.6-luna',\n} as const satisfies Record<AgentProvider, AgentModel>;\n\nfunction unknownProvider(provider: never): InvalidAgentConfigError {\n\treturn new InvalidAgentConfigError(\n\t\t`\"${String(provider)}\" is not an agent provider; expected one of: ${agentProviders.join(', ')}`,\n\t);\n}\n\nfunction llmClientFor(\n\tprovider: AgentProvider,\n\t{\n\t\tmodel,\n\t\treasoningEffort,\n\t\tcompactAt,\n\t\tsystemPrompt,\n\t\tlogger,\n\t}: {\n\t\tmodel?: AgentModel;\n\t\treasoningEffort?: ReasoningEffort;\n\t\tcompactAt?: number | undefined;\n\t\tsystemPrompt: string;\n\t\tlogger: ILogger;\n\t},\n): LLMClient {\n\tswitch (provider) {\n\t\tcase 'anthropic':\n\t\t\treturn new ClaudeLLMClient({\n\t\t\t\tmodel: model ?? defaultLLMAgentModels.anthropic,\n\t\t\t\treasoningEffort,\n\t\t\t\tcompactAt,\n\t\t\t\tsystemPrompt,\n\t\t\t\tlogger,\n\t\t\t});\n\t\tcase 'openai':\n\t\t\treturn new OpenAILLMClient({\n\t\t\t\tmodel: model ?? defaultLLMAgentModels.openai,\n\t\t\t\treasoningEffort,\n\t\t\t\tcompactAt,\n\t\t\t\tsystemPrompt,\n\t\t\t\tlogger,\n\t\t\t});\n\t\tdefault:\n\t\t\t// Reachable from untyped input.\n\t\t\tthrow unknownProvider(provider);\n\t}\n}\n\n/** The provider's cheap model, for the summary of what was undone. */\nfunction summarizerFor(provider: AgentProvider, logger: ILogger): LLMClient {\n\treturn provider === 'anthropic'\n\t\t? new ClaudeLLMClient({ model: 'haiku', reasoningEffort: 'high', systemPrompt: '', logger })\n\t\t: new OpenAILLMClient({\n\t\t\t\tmodel: 'gpt-5.6-luna',\n\t\t\t\treasoningEffort: 'high',\n\t\t\t\tsystemPrompt: '',\n\t\t\t\tlogger,\n\t\t\t});\n}\n\n/**\n * The workspace's history, opened on first use, so a factory that builds an agent stays\n * synchronous. Every agent and every file tool of one workspace reads the same files on disk.\n */\nfunction historyOf(workspace: WorkspaceOptions): HistoryReader {\n\t// Checked now: an agent with no root to follow is a setup error, not a first-run one.\n\tif (workspace.roots.length === 0) {\n\t\tthrow new InvalidAgentConfigError('A workspace needs at least one root');\n\t}\n\tlet store: Promise<RecoveryStore> | undefined;\n\tconst open = () => {\n\t\tstore ??= stateDirectoryOf(workspace)\n\t\t\t.then(directory => openStore(workspace, directory))\n\t\t\t.catch((error: unknown) => {\n\t\t\t\t// Not kept: the next run tries again, as one whose disk was full for a moment.\n\t\t\t\tstore = undefined;\n\t\t\t\tthrow error;\n\t\t\t});\n\t\treturn store;\n\t};\n\treturn {\n\t\tlistRuns: async () => (await open()).listRuns(),\n\t\tlistRevisions: async () => (await open()).listRevisions(),\n\t\treadRun: async runId => (await open()).readRun(runId),\n\t};\n}\n\n/**\n * What the model is told about the workspace after its instructions: the roots, which it may\n * change, and how to name a file. A host path in an error sends the model looking for it, so\n * the real root is given here once.\n */\nfunction workspaceGuidance(roots: WorkspaceRoot[]): string {\n\tconst [first, ...others] = roots.map(root => ({ ...root, path: resolve(root.path) }));\n\tif (!first) return '';\n\tconst described = [first, ...others].map(\n\t\t({ path, access }) => `- ${path}${access === 'write' ? '' : ' (read only)'}`,\n\t);\n\treturn [\n\t\t'The workspace:',\n\t\t...described,\n\t\t`Name a file by its path relative to ${first.path}, such as src/index.ts, and a file in another folder of the workspace by its full path. Read a file before you change it.`,\n\t].join('\\n');\n}\n\nfunction withGuidance(systemPrompt: string, guidance: string): string {\n\treturn guidance === '' ? systemPrompt : `${systemPrompt}\\n\\n${guidance}`;\n}\n\n/** An `LLMAgent` on `provider`, wrapped in the harness's retry policy. */\nfunction buildLLMAgent(\n\tprovider: AgentProvider,\n\t{\n\t\tmodel,\n\t\treasoningEffort,\n\t\tsystemPrompt,\n\t\ttools,\n\t\thistory,\n\t\tsummarizer,\n\t\tmaxSteps,\n\t\tcompactAt,\n\t\tautoApprove,\n\t\tlogger,\n\t\tonTranscript,\n\t}: {\n\t\tmodel?: AgentModel;\n\t\treasoningEffort?: ReasoningEffort;\n\t\tsystemPrompt: string;\n\t\ttools: AgentTool[];\n\t\thistory?: HistoryReader;\n\t\tsummarizer?: LLMClient;\n\t\tmaxSteps?: number;\n\t\tcompactAt?: number | undefined;\n\t\tautoApprove?: boolean;\n\t\tlogger: ILogger;\n\t\tonTranscript?: TranscriptCallback | undefined;\n\t},\n): Agent {\n\tconst llmClient = llmClientFor(provider, {\n\t\tmodel,\n\t\treasoningEffort,\n\t\tcompactAt,\n\t\tsystemPrompt,\n\t\tlogger,\n\t});\n\t// LLMAgent records nothing from a failed call, so the original prompt is the whole story.\n\treturn new RetryingAgent({\n\t\tinner: new LLMAgent({\n\t\t\tllmClient,\n\t\t\ttools,\n\t\t\tautoApprove,\n\t\t\tlogger,\n\t\t\t...(history ? { history } : {}),\n\t\t\t...(summarizer ? { summarizer } : {}),\n\t\t\t...(maxSteps === undefined ? {} : { maxSteps }),\n\t\t\t...(onTranscript ? { onTranscript } : {}),\n\t\t}),\n\t\tlogger,\n\t\tnoteFailures: false,\n\t});\n}\n\n/**\n * Builds an agent whose conversation MiKode owns, on a provider's model API, already wrapped in\n * the harness's retry policy. It needs the provider's API key and bills per token, where\n * `createAgent` uses the Agent SDK's login. Each tool judges the risk of each call; a\n * `destructive` one runs only when the run's `approve` allows it, or with `autoApprove`.\n *\n * @throws {InvalidAgentConfigError} for an unknown provider, a model or reasoning effort its\n * client does not support, or two tools that share a name.\n * @throws {UnrecoverableError} when the client cannot be set up, for example because\n * `OPENAI_API_KEY` is missing. The Anthropic SDK resolves credentials lazily, so a missing\n * `ANTHROPIC_API_KEY` fails the first run instead, with an `UnrecoverableError`.\n */\nexport function createLLMAgent(\n\tprovider: AgentProvider,\n\t{\n\t\tmodel,\n\t\treasoningEffort,\n\t\tsystemPrompt,\n\t\ttools = [],\n\t\tworkspace,\n\t\tsummarizeUndone = true,\n\t\tmaxSteps,\n\t\tcompactAt,\n\t\tautoApprove,\n\t\tlogger = new Logger(),\n\t\tonTranscript,\n\t}: CreateLLMAgentOptions,\n): Agent {\n\treturn buildLLMAgent(provider, {\n\t\tmodel,\n\t\treasoningEffort,\n\t\tsystemPrompt: workspace\n\t\t\t? withGuidance(systemPrompt, workspaceGuidance(workspace.roots))\n\t\t\t: systemPrompt,\n\t\t// A file tool is a harness tool under its public name.\n\t\ttools: tools as AgentTool[],\n\t\t...(workspace ? { history: historyOf(workspace) } : {}),\n\t\t...(workspace && summarizeUndone ? { summarizer: summarizerFor(provider, logger) } : {}),\n\t\tmaxSteps,\n\t\tcompactAt,\n\t\tautoApprove,\n\t\tlogger,\n\t\tonTranscript,\n\t});\n}\n\n/**\n * The file tools for an agent from `createLLMAgent` on `provider`, over `workspace`, in the\n * provider's own format:\n *\n * - OpenAI: `listFiles`, `searchText`, `readFile`, `readFiles` and its native `apply_patch`;\n * - Anthropic: `listFiles`, `searchText`, its native text editor, whose `view` reads, and\n * `delete_file`.\n *\n * Every one goes through the workspace's access policy: a path outside the roots, a secret,\n * a file `.gitignore` excludes or the harness protects is refused. A change is made only in a\n * `write` root, only from a version the agent read, and is recorded in the workspace's history,\n * so it can be undone. Without a `write` root the tools only read. Give the agent the same\n * `workspace`, so it follows what the history undoes.\n *\n * With `commands`, they include `runCommand`, which runs one of them by name, with only the\n * parameters it declares and never through a shell. Each run asks the approver, showing the\n * program exactly as it will run, and gets a small environment without the provider keys. The\n * files of the `write` roots are captured before it and compared after, so what it changed is\n * in the history too. It is trusted execution, not a sandbox: a command can reach whatever\n * this user can. Not supported on Windows yet.\n *\n * With `formatter`, every file an edit creates or changes is formatted right after, its path\n * the formatter's last argument. The edit shows the lines the formatter rewrote, and the model\n * reads a file it rewrote again before editing it. Like a command it runs without a shell, in a small environment, and what it changes in that file is\n * in the history. When it fails, the file stays as the edit wrote it, and the model is told\n * why. Not supported on Windows yet.\n *\n * @throws {InvalidAgentConfigError} for an unknown provider, a workspace with no root, or an\n * invalid command or formatter, including one whose `cwd` is not in a `write` root, or any on\n * Windows.\n * @throws {UnrecoverableError} when no program can read the first root.\n */\nexport async function createFileTools(\n\tprovider: AgentProvider,\n\tworkspace: WorkspaceOptions,\n\t{\n\t\tlogger = new Logger(),\n\t\tcommands,\n\t\tformatter,\n\t}: { logger?: ILogger; commands?: Commands; formatter?: Formatter } = {},\n): Promise<FileTool[]> {\n\t// Reachable from untyped input.\n\tif (!isAgentProvider(provider)) throw unknownProvider(provider);\n\tif (commands !== undefined) validateCommands(commands);\n\tif (formatter !== undefined) validateFormatter(formatter);\n\tconst opened = await openWorkspace(workspace, logger, formatter ? { formatter } : {});\n\treturn (await executorTools(provider, opened, commands)) as unknown as FileTool[];\n}\n\n/** The executor's file tools, and `runCommand` when there are commands. */\nasync function executorTools(\n\tprovider: AgentProvider,\n\tworkspace: OpenedWorkspace,\n\tcommands: Commands | undefined,\n): Promise<AgentTool[]> {\n\tconst tools = fileToolsFor(provider, workspace);\n\t// Every command may belong to other roles, leaving none for this one.\n\tif (commands === undefined || Object.keys(commands).length === 0) return tools;\n\treturn [...tools, await commandToolFor(workspace, commands)];\n}\n\n/** What an explorer finds and reads with: no edits, no changes, no commands. */\nfunction explorerTools({ read, policy }: OpenedWorkspace): AgentTool[] {\n\tconst finding = createWorkspaceTools(read).filter(\n\t\ttool => tool.name !== 'readFile' && tool.name !== 'readFiles',\n\t);\n\treturn [...finding, createTrackedReadFile(policy), createTrackedReadFiles(policy)];\n}\n\n/**\n * What the planner and the reviewer read with: no edits, the current task's changes, `explore`\n * with `explorer`, and the commands given to their role.\n */\nasync function readingTools(\n\tworkspace: OpenedWorkspace,\n\trole: CommandRole,\n\tcommands: Commands | undefined,\n\texplorer: () => Agent,\n): Promise<AgentTool[]> {\n\tconst { policy, store } = workspace;\n\tconst tools = [\n\t\t...explorerTools(workspace),\n\t\tcreateShowChangesTool({ store, policy }),\n\t\tcreateExploreTool(explorer),\n\t];\n\tconst own = commands && commandsFor(commands, role);\n\tif (!own || Object.keys(own).length === 0) return tools;\n\treturn [...tools, await commandToolFor(workspace, own)];\n}\n\nconst batching =\n\t'When you know you need several files, or several searches, ask for all of them in one answer rather than one at a time: each answer is a step, and steps are limited.';\n\n// For the roles that have `readFiles`, which the executor on Claude does not.\nconst batchingReads =\n\t'When you know you need several files, or several ranges, read them in one readFiles call, and ask for several searches in one answer, rather than one at a time: each answer is a step, and steps are limited.';\n\n// The explorers find and the role reads, as a reader asks a librarian where a book is and then\n// reads it: their reports are where to look, not a summary to work from.\nconst exploring =\n\t'To find where something is in the repository, ask explorers instead of searching yourself: send `explore` the independent questions you have in one call, and each answers with the files and line ranges that hold what you asked. Then read those ranges yourself, all of them in one readFiles call.';\n\n// Each provider's cheaper model, which only finds and reads.\nconst explorerRoles = {\n\tanthropic: { model: 'haiku', reasoningEffort: 'medium' },\n\topenai: { model: 'gpt-5.6-luna', reasoningEffort: 'medium' },\n} as const satisfies Record<AgentProvider, { model: AgentModel; reasoningEffort: ReasoningEffort }>;\n\nconst explorerSteps = 30;\n\nconst explorerInstructions = `You find things in a repository for another agent, which is planning or reviewing a change and asked you one question, as a reader asks a librarian where a book is. Find where the answer is with listFiles, searchText, readFile and readFiles, reading only what you need to be sure. Answer with where it is: each file and the line ranges that matter, and in a sentence what each range holds and why it answers the question. The other agent reads those ranges itself, so do not copy code beyond a line that marks a place. Say what you looked for and could not find. Do not propose changes or a plan. ${batchingReads}`;\n\n// How each role finds its way, sent after the workspace's roots, so it stays short. Only the\n// planner learns the repository; the executor and the reviewer work from its plan, so a plan\n// that leaves them guessing is the planner's to fix, not theirs to make up for.\nconst repositoryGuidance: Record<CommandRole, string> = {\n\tplanner: `Start with AGENTS.md, which says where the architecture and the decisions are, and read only the parts your task needs. Name in the plan every file to change and what to change in it, so the executor needs nothing else. ${exploring} ${batchingReads}`,\n\texecutor: `Work from the plan: read the files it names, and only what you need to change them. Do not study the repository's architecture or decisions; the plan already applied them. ${batching}`,\n\treviewer: `Work from the plan and the changes: read what they touch, and only what you need to judge them. ${exploring} ${batchingReads}`,\n};\n\n/**\n * Builds the planner → executor → reviewer workflow with every role on the model APIs, each\n * holding its role's instructions, or `systemPrompts`, as its system prompt, followed by the\n * workspace's roots.\n *\n * - The planner and the reviewer read: `listFiles`, `searchText`, `readFile`, `readFiles`, `showChanges`,\n * the current task's changes as far as each may read them, and `explore`, which sends up to 4\n * read-only explorers at once on the role's provider's cheaper model, each a new agent that\n * starts from its question.\n * - The executor gets its provider's file tools (see `createFileTools`), and changes files only\n * in a `write` root; `autoApprove` reaches only it.\n * - Each role has its own step limit: `maxSteps`, 80 for the executor and 65 for the others by\n * default.\n * - Each role gets `runCommand` with the `commands` whose `roles` name it, the executor by\n * default. A command asks the approver in any role, and its files are captured around it.\n * - When the executor has a command, it is told to finish only once the tests and checks pass,\n * or to say which fail, and the reviewer takes from its response whether they pass. Without\n * one, both are told the tests were not run, and the reviewer judges from the code whether\n * they would pass.\n *\n * One run of the orchestrator is one run of the workspace's history, every round and retry\n * included, and its `AgentResponse` names it. Every role follows what the user undoes.\n * Asynchronous because opening the workspace is.\n *\n * @throws {InvalidAgentConfigError} for an unknown provider, or a workspace with no root.\n * @throws {UnrecoverableError} when a role cannot be set up, for example because\n * `OPENAI_API_KEY` is missing, or when no program can read the repository.\n */\nexport async function createLLMOrchestrator({\n\tprovider,\n\tautoApprove,\n\tsystemPrompts = {},\n\tworkspace = { roots: [{ path: process.cwd(), access: 'read' }] },\n\tmaxSteps,\n\tcompactAt,\n\tcommands,\n\tformatter,\n\tsummarizeUndone = true,\n\tlogger = new Logger(),\n\tonTranscript,\n}: CreateLLMOrchestratorOptions = {}): Promise<Agent> {\n\tconst roles = orchestratorRolesFor(provider);\n\tconst steps = perRole('maxSteps', maxSteps, defaultRoleSteps, {\n\t\tunit: 'steps',\n\t\texample: '{ \"executor\": 120 }',\n\t});\n\tconst compaction = perRole('compactAt', compactAt, defaultRoleCompaction, {\n\t\tunit: 'tokens',\n\t\texample: '{ \"executor\": 200000 }',\n\t});\n\tif (formatter !== undefined) validateFormatter(formatter);\n\tconst opened = await openWorkspace(workspace, logger, formatter ? { formatter } : {});\n\tconst guidance = workspaceGuidance(workspace.roots);\n\tconst summarizer = summarizeUndone ? summarizerFor(roles.planner.provider, logger) : undefined;\n\tconst shared = { history: opened.store, ...(summarizer ? { summarizer } : {}), logger };\n\t// A caller's own prompt replaces our instructions and guidance, not the workspace: the model\n\t// needs the roots to name a file, as `createLLMAgent` gives them.\n\tconst prompt = (role: CommandRole, own: string | undefined, instructions: string) =>\n\t\town === undefined\n\t\t\t? withGuidance(instructions, `${guidance}\\n${repositoryGuidance[role]}`)\n\t\t\t: withGuidance(own, guidance);\n\t// Each transcript names the agent it is of.\n\tconst transcriptsOf = (agent: Transcript['agent']) =>\n\t\tonTranscript && ((transcript: Transcript) => onTranscript({ ...transcript, agent }));\n\t// Every command, checked once, before any role takes its share.\n\tif (commands !== undefined) validateCommands(commands);\n\t// A new explorer for each question, on the role's provider: it starts from the question alone.\n\tconst explorerToolset = explorerTools(opened);\n\tconst explorer = (provider: AgentProvider) => () =>\n\t\tbuildLLMAgent(provider, {\n\t\t\t...explorerRoles[provider],\n\t\t\tsystemPrompt: withGuidance(explorerInstructions, guidance),\n\t\t\ttools: explorerToolset,\n\t\t\tmaxSteps: explorerSteps,\n\t\t\tlogger,\n\t\t\tonTranscript: transcriptsOf('explorer'),\n\t\t});\n\tconst reader = async (\n\t\t{ provider, model, reasoningEffort }: Role,\n\t\trole: CommandRole,\n\t\tsystemPrompt: string,\n\t) =>\n\t\tbuildLLMAgent(provider, {\n\t\t\tmodel,\n\t\t\treasoningEffort,\n\t\t\tsystemPrompt,\n\t\t\ttools: await readingTools(opened, role, commands, explorer(provider)),\n\t\t\tmaxSteps: steps[role],\n\t\t\tcompactAt: compaction[role],\n\t\t\tonTranscript: transcriptsOf(role),\n\t\t\t...shared,\n\t\t});\n\tconst { executor } = roles;\n\tconst executorToolset = await executorTools(\n\t\texecutor.provider,\n\t\topened,\n\t\tcommands && commandsFor(commands, 'executor'),\n\t);\n\t// The executor and the reviewer are told whether the tests were run, which only an executor\n\t// with a command can do.\n\tconst executorRuns = executorToolset.some(tool => tool.name === 'runCommand');\n\n\treturn new OrchestratorAgent({\n\t\tplannerAgent: await reader(\n\t\t\troles.planner,\n\t\t\t'planner',\n\t\t\tprompt('planner', systemPrompts.planner, plannerInstructions),\n\t\t),\n\t\texecutorAgent: buildLLMAgent(executor.provider, {\n\t\t\tmodel: executor.model,\n\t\t\treasoningEffort: executor.reasoningEffort,\n\t\t\tsystemPrompt: prompt(\n\t\t\t\t'executor',\n\t\t\t\tsystemPrompts.executor,\n\t\t\t\texecutorRuns ? executorInstructions : executorInstructionsWithoutCommands,\n\t\t\t),\n\t\t\ttools: executorToolset,\n\t\t\tmaxSteps: steps.executor,\n\t\t\tcompactAt: compaction.executor,\n\t\t\tautoApprove,\n\t\t\tonTranscript: transcriptsOf('executor'),\n\t\t\t...shared,\n\t\t}),\n\t\treviewerAgent: await reader(\n\t\t\troles.reviewer,\n\t\t\t'reviewer',\n\t\t\tprompt(\n\t\t\t\t'reviewer',\n\t\t\t\tsystemPrompts.reviewer,\n\t\t\t\texecutorRuns ? reviewerInstructions : reviewerInstructionsWithoutCommands,\n\t\t\t),\n\t\t),\n\t\treviewerDecisionValidator: new ReviewerDecisionValidator(),\n\t\tlogger,\n\t});\n}\n"]}
1
+ {"version":3,"file":"agentLLMFactory.js","sourceRoot":"","sources":["../../../src/factory/infrastructure/agentLLMFactory.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolve } from 'node:path';\nimport type { Agent } from '#src/agent/domain/agent';\nimport type { Transcript, TranscriptCallback } from '#src/agent/domain/transcript';\nimport type { HistoryReader } from '#src/engines/domain/model/historyFollower';\nimport { LLMAgent } from '#src/engines/domain/model/llmAgent';\nimport type { LLMClient } from '#src/llm/domain/llm';\nimport {\n\tClaudeLLMClient,\n\tclaudeLLMModels,\n\tclaudeLLMReasoningEfforts,\n} from '#src/llm/infrastructure/claudeLLMClient';\nimport {\n\tOpenAILLMClient,\n\topenAIModels,\n\topenAIReasoningEfforts,\n} from '#src/llm/infrastructure/openAILLMClient';\nimport {\n\texecutorInstructions,\n\texecutorInstructionsWithoutCommands,\n\tOrchestratorAgent,\n\tplannerInstructions,\n\treviewerInstructions,\n\treviewerInstructionsWithoutCommands,\n} from '#src/orchestration/domain/model/orchestratorAgent';\nimport { ReviewerDecisionValidator } from '#src/orchestration/infrastructure/model/reviewerDecisionValidator';\nimport type { RecoveryStore } from '#src/recovery/domain/recoveryStore';\nimport { RetryingAgent } from '#src/retry/domain/model/retryingAgent';\nimport { InvalidAgentConfigError } from '#src/shared/domain/errors';\nimport { isOneOf } from '#src/shared/domain/isOneOf';\nimport type { ILogger } from '#src/shared/domain/logger';\nimport { Logger } from '#src/shared/infrastructure/logger';\nimport type { WorkspaceRoot } from '#src/tools/domain/accessPolicy';\nimport {\n\ttype CommandRole,\n\ttype Commands,\n\tcommandsFor,\n\ttype Formatter,\n\tvalidateCommands,\n\tvalidateFormatter,\n} from '#src/tools/domain/commands';\nimport type { AgentTool } from '#src/tools/domain/preparedCall';\nimport type { Tool } from '#src/tools/domain/tool';\nimport {\n\tcommandToolFor,\n\ttype FileTool,\n\tfileToolsFor,\n\ttype OpenedWorkspace,\n\topenStore,\n\topenWorkspace,\n\tstateDirectoryOf,\n\ttype WorkspaceOptions,\n} from '#src/tools/infrastructure/fileTools';\nimport type { DelegationTool } from '#src/tools/infrastructure/agentAsTool';\nimport { createExploreTool } from '#src/tools/infrastructure/exploreTool';\nimport { createShowChangesTool } from '#src/tools/infrastructure/showChangesTool';\nimport {\n\tcreateTrackedReadFile,\n\tcreateTrackedReadFiles,\n} from '#src/tools/infrastructure/trackedReadFile';\nimport { createWorkspaceTools } from '#src/tools/infrastructure/workspaceTools';\nimport { isAgentProvider, orchestratorRolesFor, type Role } from './agentFactory.ts';\nimport {\n\tagentProviders,\n\ttype AgentModel,\n\ttype AgentProvider,\n\ttype CreateOrchestratorOptions,\n\ttype ReasoningEffort,\n} from './types.ts';\n\n/** The models each provider's model API accepts. */\nexport const llmModels = {\n\tanthropic: claudeLLMModels,\n\topenai: openAIModels,\n} as const satisfies Readonly<Record<AgentProvider, readonly AgentModel[]>>;\n\n/** The reasoning efforts each provider's model API accepts. */\nexport const llmReasoningEfforts = {\n\tanthropic: claudeLLMReasoningEfforts,\n\topenai: openAIReasoningEfforts,\n} as const satisfies Readonly<Record<AgentProvider, readonly ReasoningEffort[]>>;\n\n/** Narrows a model to one accepted by the selected provider's model API. */\nexport function isLLMModelOf<Provider extends AgentProvider>(\n\tprovider: Provider,\n\tvalue: string,\n): value is (typeof llmModels)[Provider][number] {\n\treturn isOneOf(llmModels[provider], value);\n}\n\n/** Narrows a reasoning effort to one accepted by the selected provider's model API. */\nexport function isLLMReasoningEffortOf<Provider extends AgentProvider>(\n\tprovider: Provider,\n\tvalue: string,\n): value is (typeof llmReasoningEfforts)[Provider][number] {\n\treturn isOneOf(llmReasoningEfforts[provider], value);\n}\n\nexport interface CreateLLMAgentOptions {\n\t/**\n\t * Defaults to `'sonnet'` on Anthropic and `'gpt-5.6-luna'` on OpenAI. The provider's client\n\t * rejects a model it does not support.\n\t */\n\tmodel?: AgentModel;\n\t/** Defaults to `'high'`. The provider's client rejects an effort it does not support. */\n\treasoningEffort?: ReasoningEffort;\n\t/** Sent as the model's instructions on every call. */\n\tsystemPrompt: string;\n\t/**\n\t * The tools the model may call: the consumer's own, those of `createFileTools`, and agents\n\t * offered with `agentAsTool`. Defaults to none.\n\t */\n\ttools?: (Tool | FileTool | DelegationTool)[];\n\t/**\n\t * The folders the agent works on, the same as given to `createFileTools`. With it, the agent\n\t * follows the workspace's history: after the user undoes runs, its next run starts from the\n\t * conversation as it was, and is told what was undone. The system prompt is followed by the\n\t * roots and how to name a file in them.\n\t */\n\tworkspace?: WorkspaceOptions;\n\t/**\n\t * With `workspace`, a summary of what was undone, from the provider's cheap model, in place\n\t * of the undone prompts. That is Claude Haiku 5.5 or `gpt-5.6-luna`, at high effort, and the\n\t * call is billed with the run's tokens. Defaults to `true`.\n\t */\n\tsummarizeUndone?: boolean;\n\t/**\n\t * How many calls to the model one run may make before it fails. Defaults to 65, with no upper\n\t * bound. A run that asks for exactly the same tool calls 5 times in a row fails before that.\n\t */\n\tmaxSteps?: number;\n\t/**\n\t * The input tokens at which the provider compacts the conversation on its side: it replaces\n\t * every turn before that call with its own summary, which only its model reads back. The\n\t * conversation keeps every original turn. Without it, the conversation is never compacted.\n\t * At least 50,000 on Anthropic.\n\t */\n\tcompactAt?: number;\n\t/**\n\t * Runs every tool call without asking, `destructive` ones included, for CI where no one can\n\t * answer. Without it, a `destructive` call goes to the run's `approve`, or is denied.\n\t */\n\tautoApprove?: boolean;\n\t/** Defaults to warnings on stderr. */\n\tlogger?: ILogger;\n\t/**\n\t * Receives what the model saw in each run once the run ended, completed, failed or\n\t * cancelled, for debugging. See `Transcript`. Without it, nothing is kept.\n\t */\n\tonTranscript?: TranscriptCallback;\n}\n\n/** A limit per role of `createLLMOrchestrator`; a role left out keeps its default. */\nexport type RoleSteps = Partial<Record<OrchestratorRole, number>>;\n\n/** A number of tokens per role of `createLLMOrchestrator`; a role left out keeps its default. */\nexport type RoleTokens = Partial<Record<OrchestratorRole, number>>;\n\n// The same three roles a command can be given to.\ntype OrchestratorRole = CommandRole;\n\n// The executor reads, edits and checks, so it needs more calls than the roles that only read.\nconst defaultRoleSteps: Record<OrchestratorRole, number> = {\n\tplanner: 65,\n\texecutor: 80,\n\treviewer: 65,\n};\n\n// No role compacts unless asked to.\nconst defaultRoleCompaction: Record<OrchestratorRole, number | undefined> = {\n\tplanner: undefined,\n\texecutor: undefined,\n\treviewer: undefined,\n};\n\n/**\n * Each role's value of the option `name`, from one number for all of them or a number per role.\n * Each number is checked by whatever takes it.\n *\n * @throws {InvalidAgentConfigError} for a value that is neither, and for a role that does not\n * exist, which is a typo that would otherwise leave that role on its default without a word.\n */\nfunction perRole<T extends number | undefined>(\n\tname: string,\n\tvalue: unknown,\n\tdefaults: Record<OrchestratorRole, T>,\n\t{ unit, example }: { unit: string; example: string },\n): Record<OrchestratorRole, T | number> {\n\tif (value === undefined) return defaults;\n\tif (typeof value === 'number') return { planner: value, executor: value, reviewer: value };\n\t// Untyped input, such as the CLI's configuration file, can hold `null` or a list here.\n\tif (typeof value !== 'object' || value === null || Array.isArray(value)) {\n\t\tthrow new InvalidAgentConfigError(\n\t\t\t`${name} must be a number or an object of ${unit} per role, such as ${example}`,\n\t\t);\n\t}\n\tconst unknown = Object.keys(value).filter(role => !(role in defaults));\n\tif (unknown.length > 0) {\n\t\tthrow new InvalidAgentConfigError(\n\t\t\t`${name} names roles that do not exist: ${unknown.join(', ')}; expected planner, executor or reviewer`,\n\t\t);\n\t}\n\t// A role given as `undefined`, which untyped input can do, keeps its default.\n\tconst given = Object.entries(value as Record<string, unknown>).filter(\n\t\t([, each]) => each !== undefined,\n\t);\n\treturn { ...defaults, ...Object.fromEntries(given) };\n}\n\nexport interface CreateLLMOrchestratorOptions extends CreateOrchestratorOptions {\n\t/**\n\t * The folders the agents work on. The executor changes files only in a `write` root, every\n\t * change recorded in the workspace's history. Defaults to the current working directory,\n\t * read only.\n\t */\n\tworkspace?: WorkspaceOptions;\n\t/**\n\t * How many calls to the model one run of each role may make: one number for every role, or a\n\t * number per role. Defaults to 80 for the executor and 65 for the planner and the reviewer,\n\t * with no upper bound. A run that asks for exactly the same tool calls 5 times in a row fails\n\t * before its limit.\n\t */\n\tmaxSteps?: number | RoleSteps;\n\t/**\n\t * The input tokens at which each role's provider compacts its conversation, as\n\t * `CreateLLMAgentOptions.compactAt`: one number for every role, or a number per role. Without\n\t * it, no role compacts.\n\t */\n\tcompactAt?: number | RoleTokens;\n\t/**\n\t * Commands the roles may run, such as the project's tests, through `runCommand`. Each goes to\n\t * the roles its `roles` names, the executor by default. See `createFileTools`. Each needs a\n\t * `write` root to run in.\n\t */\n\tcommands?: Commands;\n\t/**\n\t * A program the executor's edits run on each file they create or change, such as the\n\t * project's formatter. See `createFileTools`. It needs a `write` root to run in.\n\t */\n\tformatter?: Formatter;\n\t/**\n\t * A summary of what the user undid, from the planner's provider's cheap model, for every\n\t * role. See `CreateLLMAgentOptions.summarizeUndone`. Defaults to `true`.\n\t */\n\tsummarizeUndone?: boolean;\n\t/**\n\t * Receives what the model saw in each run of each role and each explorer, once the run\n\t * ended, for debugging. Each transcript names its role. See `Transcript`.\n\t */\n\tonTranscript?: TranscriptCallback;\n}\n\nconst defaultLLMAgentModels = {\n\tanthropic: 'sonnet',\n\topenai: 'gpt-5.6-luna',\n} as const satisfies Record<AgentProvider, AgentModel>;\n\nfunction unknownProvider(provider: never): InvalidAgentConfigError {\n\treturn new InvalidAgentConfigError(\n\t\t`\"${String(provider)}\" is not an agent provider; expected one of: ${agentProviders.join(', ')}`,\n\t);\n}\n\nfunction llmClientFor(\n\tprovider: AgentProvider,\n\t{\n\t\tmodel,\n\t\treasoningEffort,\n\t\tcompactAt,\n\t\tsystemPrompt,\n\t\tlogger,\n\t}: {\n\t\tmodel?: AgentModel;\n\t\treasoningEffort?: ReasoningEffort;\n\t\tcompactAt?: number | undefined;\n\t\tsystemPrompt: string;\n\t\tlogger: ILogger;\n\t},\n): LLMClient {\n\tswitch (provider) {\n\t\tcase 'anthropic':\n\t\t\treturn new ClaudeLLMClient({\n\t\t\t\tmodel: model ?? defaultLLMAgentModels.anthropic,\n\t\t\t\treasoningEffort,\n\t\t\t\tcompactAt,\n\t\t\t\tsystemPrompt,\n\t\t\t\tlogger,\n\t\t\t});\n\t\tcase 'openai':\n\t\t\treturn new OpenAILLMClient({\n\t\t\t\tmodel: model ?? defaultLLMAgentModels.openai,\n\t\t\t\treasoningEffort,\n\t\t\t\tcompactAt,\n\t\t\t\tsystemPrompt,\n\t\t\t\tlogger,\n\t\t\t});\n\t\tdefault:\n\t\t\t// Reachable from untyped input.\n\t\t\tthrow unknownProvider(provider);\n\t}\n}\n\n/** The provider's cheap model, for the summary of what was undone. */\nfunction summarizerFor(provider: AgentProvider, logger: ILogger): LLMClient {\n\treturn provider === 'anthropic'\n\t\t? new ClaudeLLMClient({ model: 'haiku', reasoningEffort: 'high', systemPrompt: '', logger })\n\t\t: new OpenAILLMClient({\n\t\t\t\tmodel: 'gpt-5.6-luna',\n\t\t\t\treasoningEffort: 'high',\n\t\t\t\tsystemPrompt: '',\n\t\t\t\tlogger,\n\t\t\t});\n}\n\n/**\n * The workspace's history, opened on first use, so a factory that builds an agent stays\n * synchronous. Every agent and every file tool of one workspace reads the same files on disk.\n */\nfunction historyOf(workspace: WorkspaceOptions, logger: ILogger): HistoryReader {\n\t// Checked now: an agent with no root to follow is a setup error, not a first-run one.\n\tif (workspace.roots.length === 0) {\n\t\tthrow new InvalidAgentConfigError('A workspace needs at least one root');\n\t}\n\tlet store: Promise<RecoveryStore> | undefined;\n\tconst open = () => {\n\t\tstore ??= stateDirectoryOf(workspace)\n\t\t\t.then(directory => openStore(workspace, directory, logger))\n\t\t\t.catch((error: unknown) => {\n\t\t\t\t// Not kept: the next run tries again, as one whose disk was full for a moment.\n\t\t\t\tstore = undefined;\n\t\t\t\tthrow error;\n\t\t\t});\n\t\treturn store;\n\t};\n\treturn {\n\t\tlistRuns: async () => (await open()).listRuns(),\n\t\tlistRevisions: async () => (await open()).listRevisions(),\n\t\treadRun: async runId => (await open()).readRun(runId),\n\t};\n}\n\n/**\n * What the model is told about the workspace after its instructions: the roots, which it may\n * change, and how to name a file. A host path in an error sends the model looking for it, so\n * the real root is given here once.\n */\nfunction workspaceGuidance(roots: WorkspaceRoot[]): string {\n\tconst [first, ...others] = roots.map(root => ({ ...root, path: resolve(root.path) }));\n\tif (!first) return '';\n\tconst described = [first, ...others].map(\n\t\t({ path, access }) => `- ${path}${access === 'write' ? '' : ' (read only)'}`,\n\t);\n\treturn [\n\t\t'The workspace:',\n\t\t...described,\n\t\t`Name a file by its path relative to ${first.path}, such as src/index.ts, and a file in another folder of the workspace by its full path. Read a file before you change it.`,\n\t].join('\\n');\n}\n\nfunction withGuidance(systemPrompt: string, guidance: string): string {\n\treturn guidance === '' ? systemPrompt : `${systemPrompt}\\n\\n${guidance}`;\n}\n\n/** An `LLMAgent` on `provider`, wrapped in the harness's retry policy. */\nfunction buildLLMAgent(\n\tprovider: AgentProvider,\n\t{\n\t\tmodel,\n\t\treasoningEffort,\n\t\tsystemPrompt,\n\t\ttools,\n\t\thistory,\n\t\tsummarizer,\n\t\tmaxSteps,\n\t\tcompactAt,\n\t\tautoApprove,\n\t\tlogger,\n\t\tonTranscript,\n\t}: {\n\t\tmodel?: AgentModel;\n\t\treasoningEffort?: ReasoningEffort;\n\t\tsystemPrompt: string;\n\t\ttools: AgentTool[];\n\t\thistory?: HistoryReader;\n\t\tsummarizer?: LLMClient;\n\t\tmaxSteps?: number;\n\t\tcompactAt?: number | undefined;\n\t\tautoApprove?: boolean;\n\t\tlogger: ILogger;\n\t\tonTranscript?: TranscriptCallback | undefined;\n\t},\n): Agent {\n\tconst llmClient = llmClientFor(provider, {\n\t\tmodel,\n\t\treasoningEffort,\n\t\tcompactAt,\n\t\tsystemPrompt,\n\t\tlogger,\n\t});\n\t// LLMAgent records nothing from a failed call, so the original prompt is the whole story.\n\treturn new RetryingAgent({\n\t\tinner: new LLMAgent({\n\t\t\tllmClient,\n\t\t\ttools,\n\t\t\tautoApprove,\n\t\t\tlogger,\n\t\t\t...(history ? { history } : {}),\n\t\t\t...(summarizer ? { summarizer } : {}),\n\t\t\t...(maxSteps === undefined ? {} : { maxSteps }),\n\t\t\t...(onTranscript ? { onTranscript } : {}),\n\t\t}),\n\t\tlogger,\n\t\tnoteFailures: false,\n\t});\n}\n\n/**\n * Builds an agent whose conversation MiKode owns, on a provider's model API, already wrapped in\n * the harness's retry policy. It needs the provider's API key and bills per token, where\n * `createAgent` uses the Agent SDK's login. Each tool judges the risk of each call; a\n * `destructive` one runs only when the run's `approve` allows it, or with `autoApprove`.\n *\n * @throws {InvalidAgentConfigError} for an unknown provider, a model or reasoning effort its\n * client does not support, or two tools that share a name.\n * @throws {UnrecoverableError} when the client cannot be set up, for example because\n * `OPENAI_API_KEY` is missing. The Anthropic SDK resolves credentials lazily, so a missing\n * `ANTHROPIC_API_KEY` fails the first run instead, with an `UnrecoverableError`.\n */\nexport function createLLMAgent(\n\tprovider: AgentProvider,\n\t{\n\t\tmodel,\n\t\treasoningEffort,\n\t\tsystemPrompt,\n\t\ttools = [],\n\t\tworkspace,\n\t\tsummarizeUndone = true,\n\t\tmaxSteps,\n\t\tcompactAt,\n\t\tautoApprove,\n\t\tlogger = new Logger(),\n\t\tonTranscript,\n\t}: CreateLLMAgentOptions,\n): Agent {\n\treturn buildLLMAgent(provider, {\n\t\tmodel,\n\t\treasoningEffort,\n\t\tsystemPrompt: workspace\n\t\t\t? withGuidance(systemPrompt, workspaceGuidance(workspace.roots))\n\t\t\t: systemPrompt,\n\t\t// A file tool or a delegation tool is a harness tool under its public name.\n\t\ttools: tools as AgentTool[],\n\t\t...(workspace ? { history: historyOf(workspace, logger) } : {}),\n\t\t...(workspace && summarizeUndone ? { summarizer: summarizerFor(provider, logger) } : {}),\n\t\tmaxSteps,\n\t\tcompactAt,\n\t\tautoApprove,\n\t\tlogger,\n\t\tonTranscript,\n\t});\n}\n\n/**\n * The file tools for an agent from `createLLMAgent` on `provider`, over `workspace`, in the\n * provider's own format:\n *\n * - OpenAI: `listFiles`, `searchText`, `readFile`, `readFiles` and its native `apply_patch`;\n * - Anthropic: `listFiles`, `searchText`, its native text editor, whose `view` reads, and\n * `delete_file`.\n *\n * Every one goes through the workspace's access policy: a path outside the roots, a secret,\n * a file `.gitignore` excludes or the harness protects is refused. A change is made only in a\n * `write` root, only from a version the agent read, and is recorded in the workspace's history,\n * so it can be undone. Without a `write` root the tools only read. Give the agent the same\n * `workspace`, so it follows what the history undoes.\n *\n * With `commands`, they include `runCommand`, which runs one of them by name, with only the\n * parameters it declares and never through a shell. Each run asks the approver, showing the\n * program exactly as it will run, and gets a small environment without the provider keys. The\n * files of the `write` roots are captured before it and compared after, so what it changed is\n * in the history too. It is trusted execution, not a sandbox: a command can reach whatever\n * this user can. Not supported on Windows yet.\n *\n * With `formatter`, every file an edit creates or changes is formatted right after, its path\n * the formatter's last argument. The edit shows the lines the formatter rewrote, and the model\n * reads a file it rewrote again before editing it. Like a command it runs without a shell, in a small environment, and what it changes in that file is\n * in the history. When it fails, the file stays as the edit wrote it, and the model is told\n * why. Not supported on Windows yet.\n *\n * @throws {InvalidAgentConfigError} for an unknown provider, a workspace with no root, or an\n * invalid command or formatter, including one whose `cwd` is not in a `write` root, or any on\n * Windows.\n * @throws {UnrecoverableError} when no program can read the first root.\n */\nexport async function createFileTools(\n\tprovider: AgentProvider,\n\tworkspace: WorkspaceOptions,\n\t{\n\t\tlogger = new Logger(),\n\t\tcommands,\n\t\tformatter,\n\t}: { logger?: ILogger; commands?: Commands; formatter?: Formatter } = {},\n): Promise<FileTool[]> {\n\t// Reachable from untyped input.\n\tif (!isAgentProvider(provider)) throw unknownProvider(provider);\n\tif (commands !== undefined) validateCommands(commands);\n\tif (formatter !== undefined) validateFormatter(formatter);\n\tconst opened = await openWorkspace(workspace, logger, formatter ? { formatter } : {});\n\treturn (await executorTools(provider, opened, commands)) as unknown as FileTool[];\n}\n\n/** The executor's file tools, and `runCommand` when there are commands. */\nasync function executorTools(\n\tprovider: AgentProvider,\n\tworkspace: OpenedWorkspace,\n\tcommands: Commands | undefined,\n): Promise<AgentTool[]> {\n\tconst tools = fileToolsFor(provider, workspace);\n\t// Every command may belong to other roles, leaving none for this one.\n\tif (commands === undefined || Object.keys(commands).length === 0) return tools;\n\treturn [...tools, await commandToolFor(workspace, commands)];\n}\n\n/** What an explorer finds and reads with: no edits, no changes, no commands. */\nfunction explorerTools({ read, policy }: OpenedWorkspace): AgentTool[] {\n\tconst finding = createWorkspaceTools(read).filter(\n\t\ttool => tool.name !== 'readFile' && tool.name !== 'readFiles',\n\t);\n\treturn [...finding, createTrackedReadFile(policy), createTrackedReadFiles(policy)];\n}\n\n/**\n * What the planner and the reviewer read with: no edits, the current task's changes, `explore`\n * with `explorer`, and the commands given to their role.\n */\nasync function readingTools(\n\tworkspace: OpenedWorkspace,\n\trole: CommandRole,\n\tcommands: Commands | undefined,\n\texplorer: () => Agent,\n): Promise<AgentTool[]> {\n\tconst { policy, store } = workspace;\n\tconst tools = [\n\t\t...explorerTools(workspace),\n\t\tcreateShowChangesTool({ store, policy }),\n\t\tcreateExploreTool(explorer),\n\t];\n\tconst own = commands && commandsFor(commands, role);\n\tif (!own || Object.keys(own).length === 0) return tools;\n\treturn [...tools, await commandToolFor(workspace, own)];\n}\n\nconst batching =\n\t'When you know you need several files, or several searches, ask for all of them in one answer rather than one at a time: each answer is a step, and steps are limited.';\n\n// For the roles that have `readFiles`, which the executor on Claude does not.\nconst batchingReads =\n\t'When you know you need several files, or several ranges, read them in one readFiles call, and ask for several searches in one answer, rather than one at a time: each answer is a step, and steps are limited.';\n\n// The explorers find and the role reads, as a reader asks a librarian where a book is and then\n// reads it: their reports are where to look, not a summary to work from.\nconst exploring =\n\t'To find where something is in the repository, ask explorers instead of searching yourself: send `explore` the independent questions you have in one call, and each answers with the files and line ranges that hold what you asked. Then read those ranges yourself, all of them in one readFiles call.';\n\n// Each provider's cheaper model, which only finds and reads.\nconst explorerRoles = {\n\tanthropic: { model: 'haiku', reasoningEffort: 'medium' },\n\topenai: { model: 'gpt-5.6-luna', reasoningEffort: 'medium' },\n} as const satisfies Record<AgentProvider, { model: AgentModel; reasoningEffort: ReasoningEffort }>;\n\nconst explorerSteps = 30;\n\nconst explorerInstructions = `You find things in a repository for another agent, which is planning or reviewing a change and asked you one question, as a reader asks a librarian where a book is. Find where the answer is with listFiles, searchText, readFile and readFiles, reading only what you need to be sure. Answer with where it is: each file and the line ranges that matter, and in a sentence what each range holds and why it answers the question. The other agent reads those ranges itself, so do not copy code beyond a line that marks a place. Say what you looked for and could not find. Do not propose changes or a plan. ${batchingReads}`;\n\n// How each role finds its way, sent after the workspace's roots, so it stays short. Only the\n// planner learns the repository; the executor and the reviewer work from its plan, so a plan\n// that leaves them guessing is the planner's to fix, not theirs to make up for.\nconst repositoryGuidance: Record<CommandRole, string> = {\n\tplanner: `Start with AGENTS.md, which says where the architecture and the decisions are, and read only the parts your task needs. Name in the plan every file to change and what to change in it, so the executor needs nothing else. ${exploring} ${batchingReads}`,\n\texecutor: `Work from the plan: read the files it names, and only what you need to change them. Do not study the repository's architecture or decisions; the plan already applied them. ${batching}`,\n\treviewer: `Work from the plan and the changes: read what they touch, and only what you need to judge them. ${exploring} ${batchingReads}`,\n};\n\n/**\n * Builds the planner → executor → reviewer workflow with every role on the model APIs, each\n * holding its role's instructions, or `systemPrompts`, as its system prompt, followed by the\n * workspace's roots.\n *\n * - The planner and the reviewer read: `listFiles`, `searchText`, `readFile`, `readFiles`, `showChanges`,\n * the current task's changes as far as each may read them, and `explore`, which sends up to 4\n * read-only explorers at once on the role's provider's cheaper model, each a new agent that\n * starts from its question.\n * - The executor gets its provider's file tools (see `createFileTools`), and changes files only\n * in a `write` root; `autoApprove` reaches only it.\n * - Each role has its own step limit: `maxSteps`, 80 for the executor and 65 for the others by\n * default.\n * - Each role gets `runCommand` with the `commands` whose `roles` name it, the executor by\n * default. A command asks the approver in any role, and its files are captured around it.\n * - When the executor has a command, it is told to finish only once the tests and checks pass,\n * or to say which fail, and the reviewer takes from its response whether they pass. Without\n * one, both are told the tests were not run, and the reviewer judges from the code whether\n * they would pass.\n *\n * One run of the orchestrator is one run of the workspace's history, every round and retry\n * included, and its `AgentResponse` names it. Every role follows what the user undoes.\n * Asynchronous because opening the workspace is.\n *\n * @throws {InvalidAgentConfigError} for an unknown provider, or a workspace with no root.\n * @throws {UnrecoverableError} when a role cannot be set up, for example because\n * `OPENAI_API_KEY` is missing, or when no program can read the repository.\n */\nexport async function createLLMOrchestrator({\n\tprovider,\n\tautoApprove,\n\tsystemPrompts = {},\n\tworkspace = { roots: [{ path: process.cwd(), access: 'read' }] },\n\tmaxSteps,\n\tcompactAt,\n\tcommands,\n\tformatter,\n\tsummarizeUndone = true,\n\tlogger = new Logger(),\n\tonTranscript,\n}: CreateLLMOrchestratorOptions = {}): Promise<Agent> {\n\tconst roles = orchestratorRolesFor(provider);\n\tconst steps = perRole('maxSteps', maxSteps, defaultRoleSteps, {\n\t\tunit: 'steps',\n\t\texample: '{ \"executor\": 120 }',\n\t});\n\tconst compaction = perRole('compactAt', compactAt, defaultRoleCompaction, {\n\t\tunit: 'tokens',\n\t\texample: '{ \"executor\": 200000 }',\n\t});\n\tif (formatter !== undefined) validateFormatter(formatter);\n\tconst opened = await openWorkspace(workspace, logger, formatter ? { formatter } : {});\n\tconst guidance = workspaceGuidance(workspace.roots);\n\tconst summarizer = summarizeUndone ? summarizerFor(roles.planner.provider, logger) : undefined;\n\tconst shared = { history: opened.store, ...(summarizer ? { summarizer } : {}), logger };\n\t// A caller's own prompt replaces our instructions and guidance, not the workspace: the model\n\t// needs the roots to name a file, as `createLLMAgent` gives them.\n\tconst prompt = (role: CommandRole, own: string | undefined, instructions: string) =>\n\t\town === undefined\n\t\t\t? withGuidance(instructions, `${guidance}\\n${repositoryGuidance[role]}`)\n\t\t\t: withGuidance(own, guidance);\n\t// Each transcript names the agent it is of.\n\tconst transcriptsOf = (agent: Transcript['agent']) =>\n\t\tonTranscript && ((transcript: Transcript) => onTranscript({ ...transcript, agent }));\n\t// Every command, checked once, before any role takes its share.\n\tif (commands !== undefined) validateCommands(commands);\n\t// A new explorer for each question, on the role's provider: it starts from the question alone.\n\tconst explorerToolset = explorerTools(opened);\n\tconst explorer = (provider: AgentProvider) => () =>\n\t\tbuildLLMAgent(provider, {\n\t\t\t...explorerRoles[provider],\n\t\t\tsystemPrompt: withGuidance(explorerInstructions, guidance),\n\t\t\ttools: explorerToolset,\n\t\t\tmaxSteps: explorerSteps,\n\t\t\tlogger,\n\t\t\tonTranscript: transcriptsOf('explorer'),\n\t\t});\n\tconst reader = async (\n\t\t{ provider, model, reasoningEffort }: Role,\n\t\trole: CommandRole,\n\t\tsystemPrompt: string,\n\t) =>\n\t\tbuildLLMAgent(provider, {\n\t\t\tmodel,\n\t\t\treasoningEffort,\n\t\t\tsystemPrompt,\n\t\t\ttools: await readingTools(opened, role, commands, explorer(provider)),\n\t\t\tmaxSteps: steps[role],\n\t\t\tcompactAt: compaction[role],\n\t\t\tonTranscript: transcriptsOf(role),\n\t\t\t...shared,\n\t\t});\n\tconst { executor } = roles;\n\tconst executorToolset = await executorTools(\n\t\texecutor.provider,\n\t\topened,\n\t\tcommands && commandsFor(commands, 'executor'),\n\t);\n\t// The executor and the reviewer are told whether the tests were run, which only an executor\n\t// with a command can do.\n\tconst executorRuns = executorToolset.some(tool => tool.name === 'runCommand');\n\n\treturn new OrchestratorAgent({\n\t\tplannerAgent: await reader(\n\t\t\troles.planner,\n\t\t\t'planner',\n\t\t\tprompt('planner', systemPrompts.planner, plannerInstructions),\n\t\t),\n\t\texecutorAgent: buildLLMAgent(executor.provider, {\n\t\t\tmodel: executor.model,\n\t\t\treasoningEffort: executor.reasoningEffort,\n\t\t\tsystemPrompt: prompt(\n\t\t\t\t'executor',\n\t\t\t\tsystemPrompts.executor,\n\t\t\t\texecutorRuns ? executorInstructions : executorInstructionsWithoutCommands,\n\t\t\t),\n\t\t\ttools: executorToolset,\n\t\t\tmaxSteps: steps.executor,\n\t\t\tcompactAt: compaction.executor,\n\t\t\tautoApprove,\n\t\t\tonTranscript: transcriptsOf('executor'),\n\t\t\t...shared,\n\t\t}),\n\t\treviewerAgent: await reader(\n\t\t\troles.reviewer,\n\t\t\t'reviewer',\n\t\t\tprompt(\n\t\t\t\t'reviewer',\n\t\t\t\tsystemPrompts.reviewer,\n\t\t\t\texecutorRuns ? reviewerInstructions : reviewerInstructionsWithoutCommands,\n\t\t\t),\n\t\t),\n\t\treviewerDecisionValidator: new ReviewerDecisionValidator(),\n\t\tlogger,\n\t});\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export type { ILogger } from './shared/domain/logger.ts';
2
2
  export { createAgent, createOrchestrator, agentModels, agentReasoningEfforts, isAgentProvider, isModelOf, isReasoningEffortOf, type CreateAgentOptions, } from './factory/infrastructure/agentFactory.ts';
3
3
  export { createFileTools, createLLMAgent, createLLMOrchestrator, isLLMModelOf, isLLMReasoningEffortOf, llmModels, llmReasoningEfforts, type CreateLLMAgentOptions, type CreateLLMOrchestratorOptions, type RoleSteps, type RoleTokens, } from './factory/infrastructure/agentLLMFactory.ts';
4
4
  export type { FileTool, WorkspaceOptions } from './tools/infrastructure/fileTools.ts';
5
+ export { agentAsTool, type AgentAsToolOptions, type DelegationTool, } from './tools/infrastructure/agentAsTool.ts';
5
6
  export type { WorkspaceRoot } from './tools/domain/accessPolicy.ts';
6
7
  export type { CommandArgument, CommandDefinition, CommandParameter, CommandRole, Commands, Formatter, } from './tools/domain/commands.ts';
7
8
  export type { SecretRules } from './tools/infrastructure/rootsAccessPolicy.ts';
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export { createAgent, createOrchestrator, agentModels, agentReasoningEfforts, isAgentProvider, isModelOf, isReasoningEffortOf, } from "./factory/infrastructure/agentFactory.js";
2
2
  export { createFileTools, createLLMAgent, createLLMOrchestrator, isLLMModelOf, isLLMReasoningEffortOf, llmModels, llmReasoningEfforts, } from "./factory/infrastructure/agentLLMFactory.js";
3
+ export { agentAsTool, } from "./tools/infrastructure/agentAsTool.js";
3
4
  export { agentProviders, } from "./factory/infrastructure/types.js";
4
5
  export {} from "./agent/domain/agent.js";
5
6
  export { InvalidAgentConfigError, RecoverableError, UnrecoverableError, } from "./shared/domain/errors.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACN,WAAW,EACX,kBAAkB,EAClB,WAAW,EACX,qBAAqB,EACrB,eAAe,EACf,SAAS,EACT,mBAAmB,GAEnB,MAAM,0CAA0C,CAAC;AAClD,OAAO,EACN,eAAe,EACf,cAAc,EACd,qBAAqB,EACrB,YAAY,EACZ,sBAAsB,EACtB,SAAS,EACT,mBAAmB,GAKnB,MAAM,6CAA6C,CAAC;AAYrD,OAAO,EACN,cAAc,GAKd,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAQN,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACN,uBAAuB,EACvB,gBAAgB,EAChB,kBAAkB,GAClB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAE/D,OAAO,EACN,iBAAiB,GAOjB,MAAM,4BAA4B,CAAC;AAKpC,OAAO,EAAE,UAAU,EAAE,MAAM,sCAAsC,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,2CAA2C,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,0CAA0C,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,4CAA4C,CAAC;AAE3E,OAAO,EACN,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,GAGnB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACpF,OAAO,EAAE,cAAc,EAAE,MAAM,6CAA6C,CAAC;AAC7E,OAAO,EACN,oBAAoB,GASpB,MAAM,+BAA+B,CAAC","sourcesContent":["export type { ILogger } from './shared/domain/logger.ts';\nexport {\n\tcreateAgent,\n\tcreateOrchestrator,\n\tagentModels,\n\tagentReasoningEfforts,\n\tisAgentProvider,\n\tisModelOf,\n\tisReasoningEffortOf,\n\ttype CreateAgentOptions,\n} from './factory/infrastructure/agentFactory.ts';\nexport {\n\tcreateFileTools,\n\tcreateLLMAgent,\n\tcreateLLMOrchestrator,\n\tisLLMModelOf,\n\tisLLMReasoningEffortOf,\n\tllmModels,\n\tllmReasoningEfforts,\n\ttype CreateLLMAgentOptions,\n\ttype CreateLLMOrchestratorOptions,\n\ttype RoleSteps,\n\ttype RoleTokens,\n} from './factory/infrastructure/agentLLMFactory.ts';\nexport type { FileTool, WorkspaceOptions } from './tools/infrastructure/fileTools.ts';\nexport type { WorkspaceRoot } from './tools/domain/accessPolicy.ts';\nexport type {\n\tCommandArgument,\n\tCommandDefinition,\n\tCommandParameter,\n\tCommandRole,\n\tCommands,\n\tFormatter,\n} from './tools/domain/commands.ts';\nexport type { SecretRules } from './tools/infrastructure/rootsAccessPolicy.ts';\nexport {\n\tagentProviders,\n\ttype AgentModel,\n\ttype AgentProvider,\n\ttype CreateOrchestratorOptions,\n\ttype ReasoningEffort,\n} from './factory/infrastructure/types.ts';\nexport {\n\ttype Agent,\n\ttype AgentResponse,\n\ttype Callback,\n\ttype ProgressAgent,\n\ttype ProgressEvent,\n\ttype StepContext,\n\ttype RunOptions,\n} from './agent/domain/agent.ts';\nexport type { Transcript, TranscriptCallback } from './agent/domain/transcript.ts';\nexport {\n\tInvalidAgentConfigError,\n\tRecoverableError,\n\tUnrecoverableError,\n} from './shared/domain/errors.ts';\nexport { isAbortError } from './shared/domain/isAbortError.ts';\nexport type { Tokens } from './shared/domain/tokens.ts';\nexport {\n\trememberApprovals,\n\ttype ApprovalDecision,\n\ttype ApprovalRequest,\n\ttype Approver,\n\ttype CommandLine,\n\ttype RememberableDecision,\n\ttype ToolRisk,\n} from './agent/domain/approval.ts';\nexport type { Asker, QuestionOption, UserQuestion } from './agent/domain/question.ts';\nexport type { JSONSchema, ToolDefinition } from './llm/domain/tool.ts';\nexport type { Tool, ToolSummarizer } from './tools/domain/tool.ts';\nexport type { TextMatch, Workspace } from './tools/domain/workspace.ts';\nexport { defineTool } from './tools/infrastructure/defineTool.ts';\nexport { createWorkspace } from './tools/infrastructure/createWorkspace.ts';\nexport { createWorkspaceTools } from './tools/infrastructure/workspaceTools.ts';\nexport { createHistory } from './recovery/infrastructure/createHistory.ts';\nexport type { History, HistoryRun, Move } from './recovery/domain/history.ts';\nexport {\n\thistoryStart,\n\tNothingToMoveError,\n\tWorkspaceBusyError,\n\tWorkspaceMovedError,\n\ttype MoveOptions,\n\ttype RunStatus,\n} from './recovery/domain/recoveryStore.ts';\nexport { HistoryExpiredError, UnknownRunError } from './recovery/domain/runTree.ts';\nexport { createWorkflow } from './factory/infrastructure/workflowFactory.ts';\nexport {\n\tdefineWorkflowStages,\n\ttype CreateWorkflowOptions,\n\ttype Workflow,\n\ttype WorkflowResult,\n\ttype WorkflowStage,\n\ttype WorkflowStageDefinition,\n\ttype WorkflowStageName,\n\ttype WorkflowStageReport,\n\ttype WorkflowTransition,\n} from './workflow/domain/workflow.ts';\nexport type { Validator } from './shared/domain/validator.ts';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACN,WAAW,EACX,kBAAkB,EAClB,WAAW,EACX,qBAAqB,EACrB,eAAe,EACf,SAAS,EACT,mBAAmB,GAEnB,MAAM,0CAA0C,CAAC;AAClD,OAAO,EACN,eAAe,EACf,cAAc,EACd,qBAAqB,EACrB,YAAY,EACZ,sBAAsB,EACtB,SAAS,EACT,mBAAmB,GAKnB,MAAM,6CAA6C,CAAC;AAErD,OAAO,EACN,WAAW,GAGX,MAAM,uCAAuC,CAAC;AAW/C,OAAO,EACN,cAAc,GAKd,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAQN,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACN,uBAAuB,EACvB,gBAAgB,EAChB,kBAAkB,GAClB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AAE/D,OAAO,EACN,iBAAiB,GAOjB,MAAM,4BAA4B,CAAC;AAKpC,OAAO,EAAE,UAAU,EAAE,MAAM,sCAAsC,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,2CAA2C,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,0CAA0C,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,4CAA4C,CAAC;AAE3E,OAAO,EACN,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,GAGnB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACpF,OAAO,EAAE,cAAc,EAAE,MAAM,6CAA6C,CAAC;AAC7E,OAAO,EACN,oBAAoB,GASpB,MAAM,+BAA+B,CAAC","sourcesContent":["export type { ILogger } from './shared/domain/logger.ts';\nexport {\n\tcreateAgent,\n\tcreateOrchestrator,\n\tagentModels,\n\tagentReasoningEfforts,\n\tisAgentProvider,\n\tisModelOf,\n\tisReasoningEffortOf,\n\ttype CreateAgentOptions,\n} from './factory/infrastructure/agentFactory.ts';\nexport {\n\tcreateFileTools,\n\tcreateLLMAgent,\n\tcreateLLMOrchestrator,\n\tisLLMModelOf,\n\tisLLMReasoningEffortOf,\n\tllmModels,\n\tllmReasoningEfforts,\n\ttype CreateLLMAgentOptions,\n\ttype CreateLLMOrchestratorOptions,\n\ttype RoleSteps,\n\ttype RoleTokens,\n} from './factory/infrastructure/agentLLMFactory.ts';\nexport type { FileTool, WorkspaceOptions } from './tools/infrastructure/fileTools.ts';\nexport {\n\tagentAsTool,\n\ttype AgentAsToolOptions,\n\ttype DelegationTool,\n} from './tools/infrastructure/agentAsTool.ts';\nexport type { WorkspaceRoot } from './tools/domain/accessPolicy.ts';\nexport type {\n\tCommandArgument,\n\tCommandDefinition,\n\tCommandParameter,\n\tCommandRole,\n\tCommands,\n\tFormatter,\n} from './tools/domain/commands.ts';\nexport type { SecretRules } from './tools/infrastructure/rootsAccessPolicy.ts';\nexport {\n\tagentProviders,\n\ttype AgentModel,\n\ttype AgentProvider,\n\ttype CreateOrchestratorOptions,\n\ttype ReasoningEffort,\n} from './factory/infrastructure/types.ts';\nexport {\n\ttype Agent,\n\ttype AgentResponse,\n\ttype Callback,\n\ttype ProgressAgent,\n\ttype ProgressEvent,\n\ttype StepContext,\n\ttype RunOptions,\n} from './agent/domain/agent.ts';\nexport type { Transcript, TranscriptCallback } from './agent/domain/transcript.ts';\nexport {\n\tInvalidAgentConfigError,\n\tRecoverableError,\n\tUnrecoverableError,\n} from './shared/domain/errors.ts';\nexport { isAbortError } from './shared/domain/isAbortError.ts';\nexport type { Tokens } from './shared/domain/tokens.ts';\nexport {\n\trememberApprovals,\n\ttype ApprovalDecision,\n\ttype ApprovalRequest,\n\ttype Approver,\n\ttype CommandLine,\n\ttype RememberableDecision,\n\ttype ToolRisk,\n} from './agent/domain/approval.ts';\nexport type { Asker, QuestionOption, UserQuestion } from './agent/domain/question.ts';\nexport type { JSONSchema, ToolDefinition } from './llm/domain/tool.ts';\nexport type { Tool, ToolSummarizer } from './tools/domain/tool.ts';\nexport type { TextMatch, Workspace } from './tools/domain/workspace.ts';\nexport { defineTool } from './tools/infrastructure/defineTool.ts';\nexport { createWorkspace } from './tools/infrastructure/createWorkspace.ts';\nexport { createWorkspaceTools } from './tools/infrastructure/workspaceTools.ts';\nexport { createHistory } from './recovery/infrastructure/createHistory.ts';\nexport type { History, HistoryRun, Move } from './recovery/domain/history.ts';\nexport {\n\thistoryStart,\n\tNothingToMoveError,\n\tWorkspaceBusyError,\n\tWorkspaceMovedError,\n\ttype MoveOptions,\n\ttype RunStatus,\n} from './recovery/domain/recoveryStore.ts';\nexport { HistoryExpiredError, UnknownRunError } from './recovery/domain/runTree.ts';\nexport { createWorkflow } from './factory/infrastructure/workflowFactory.ts';\nexport {\n\tdefineWorkflowStages,\n\ttype CreateWorkflowOptions,\n\ttype Workflow,\n\ttype WorkflowResult,\n\ttype WorkflowStage,\n\ttype WorkflowStageDefinition,\n\ttype WorkflowStageName,\n\ttype WorkflowStageReport,\n\ttype WorkflowTransition,\n} from './workflow/domain/workflow.ts';\nexport type { Validator } from './shared/domain/validator.ts';\n"]}
@@ -29,8 +29,9 @@ export interface Move {
29
29
  /** Every file reached the state the history expected. Otherwise `conflicts` names those left. */
30
30
  complete: boolean;
31
31
  /**
32
- * The files left as they were because they did not hold what the history expected, such as
33
- * one the user edited since, and the run whose change could not be undone or redone.
32
+ * The files left as they were, and the run whose change could not be undone or redone: one
33
+ * that did not hold what the history expected, such as one the user edited since, or one a
34
+ * command changed into, or from, something the history cannot copy, such as a file over 64 MB.
34
35
  */
35
36
  conflicts: {
36
37
  path: string;
@@ -20,7 +20,11 @@ export class StoreHistory {
20
20
  });
21
21
  if (!read)
22
22
  continue;
23
- const { record, entries } = read;
23
+ const { record, entries, unrecorded } = read;
24
+ const files = [
25
+ ...changedFiles(record, entries).map(change => change.path),
26
+ ...unrecorded.map(change => change.path),
27
+ ];
24
28
  listed.push({
25
29
  runId: record.runId,
26
30
  ...(record.parentRunId ? { parentRunId: record.parentRunId } : {}),
@@ -28,7 +32,7 @@ export class StoreHistory {
28
32
  status: record.status,
29
33
  startedAt: record.startedAt,
30
34
  ...(record.finishedAt ? { finishedAt: record.finishedAt } : {}),
31
- files: changedFiles(record, entries).map(change => displayPath(record.root, change.path)),
35
+ files: [...new Set(files)].map(path => displayPath(record.root, path)),
32
36
  });
33
37
  }
34
38
  return { ...(head ? { head } : {}), runs: listed };
@@ -1 +1 @@
1
- {"version":3,"file":"history.js","sourceRoot":"","sources":["../../../src/recovery/domain/history.ts"],"names":[],"mappings":"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","sourcesContent":["import { changedFiles, displayPath, showChanges } from './runChanges.ts';\nimport type { MoveOptions, RecoveryStore, Revision, RunStatus } from './recoveryStore.ts';\nimport { HistoryExpiredError } from './runTree.ts';\n\n/** One run that wrote to the workspace. */\nexport interface HistoryRun {\n\trunId: string;\n\t/** The run the workspace was at when this one started; absent when it started at the start. */\n\tparentRunId?: string;\n\t/**\n\t * Runs whose changes retention chained into this one, oldest first: going back past this run\n\t * goes back past them too. An id an earlier `AgentResponse` gave may be found here.\n\t */\n\tabsorbed?: string[];\n\tstatus: RunStatus;\n\tstartedAt: string;\n\tfinishedAt?: string;\n\t/**\n\t * The files it changed, in the order it first touched each one: relative to the root, or the\n\t * real path of a file outside it. A host decides whether to show them.\n\t */\n\tfiles: string[];\n}\n\n/** One move of the workspace through its history. */\nexport interface Move {\n\t/** The run the workspace was at; absent for the start of the history. */\n\tfrom?: string;\n\t/** The run the workspace is at now; absent for the start of the history. */\n\tto?: string;\n\tat: string;\n\treason?: string;\n\t/** Every file reached the state the history expected. Otherwise `conflicts` names those left. */\n\tcomplete: boolean;\n\t/**\n\t * The files left as they were because they did not hold what the history expected, such as\n\t * one the user edited since, and the run whose change could not be undone or redone.\n\t */\n\tconflicts: { path: string; runId: string }[];\n}\n\n/**\n * The history of the runs that wrote to one workspace: what each run changed, and moving the\n * workspace back and forward through them. Runs form a tree: going back and writing again\n * starts a new branch, and the earlier one stays reachable with `goTo`.\n */\nexport interface History {\n\t/**\n\t * Every run kept, oldest first, and the run the workspace is at (`head`), absent at the start\n\t * of the history. A run whose process died while writing is `interrupted`.\n\t */\n\tlist(): Promise<{ head?: string; runs: HistoryRun[] }>;\n\t/**\n\t * What a run changed, as a git-style diff per file. What does not fit in `maxLines`, 1000 by\n\t * default, is named instead.\n\t *\n\t * @throws {UnknownRunError} when `runId` was never a run of this workspace.\n\t * @throws {HistoryExpiredError} when retention removed it or chained it into a later run.\n\t */\n\tchanges(runId: string, options?: { maxLines?: number }): Promise<string>;\n\t/**\n\t * Moves the workspace to the state right after `target`, or to how it was before any run with\n\t * `historyStart`. A file that does not hold what the history expects is left as it is, and the\n\t * move is then not `complete`. With `from`, it moves only if the workspace is still there: a\n\t * host that showed the user what a move will do passes where the workspace was, so a run that\n\t * ended meanwhile cannot make it do something else.\n\t *\n\t * @throws {WorkspaceBusyError} when a run or another move holds the workspace.\n\t * @throws {WorkspaceMovedError} when the workspace is not at `from`.\n\t * @throws {UnknownRunError} when `target` is not in the history.\n\t * @throws {HistoryExpiredError} when retention chained `target` into a later run.\n\t */\n\tgoTo(target: string, options?: MoveOptions): Promise<Move>;\n\t/**\n\t * Moves the workspace to the run before the one it is at.\n\t *\n\t * @throws {NothingToMoveError} at the start of the history.\n\t * @throws {WorkspaceBusyError} when a run or another move holds the workspace.\n\t * @throws {WorkspaceMovedError} when the workspace is not at `from`.\n\t */\n\tundo(options?: MoveOptions): Promise<Move>;\n\t/**\n\t * Moves the workspace to the next run: towards where it was most recently, or else the newest\n\t * branch.\n\t *\n\t * @throws {NothingToMoveError} when no run follows the one it is at.\n\t * @throws {WorkspaceBusyError} when a run or another move holds the workspace.\n\t * @throws {WorkspaceMovedError} when the workspace is not at `from`.\n\t */\n\tredo(options?: MoveOptions): Promise<Move>;\n}\n\n/** `History` over a recovery store, with paths shown relative to the workspace's real root. */\nexport class StoreHistory implements History {\n\tprivate readonly store: RecoveryStore;\n\tprivate readonly root: string;\n\n\tconstructor({ store, root }: { store: RecoveryStore; root: string }) {\n\t\tthis.store = store;\n\t\tthis.root = root;\n\t}\n\n\tasync list(): Promise<{ head?: string; runs: HistoryRun[] }> {\n\t\tconst { head, runs } = await this.store.listRuns();\n\t\tconst listed: HistoryRun[] = [];\n\t\tfor (const run of runs) {\n\t\t\tconst read = await this.store.readRun(run.runId).catch((error: unknown) => {\n\t\t\t\t// Retention took it since it was listed: it is no longer in the history.\n\t\t\t\tif (error instanceof HistoryExpiredError) return undefined;\n\t\t\t\tthrow error;\n\t\t\t});\n\t\t\tif (!read) continue;\n\t\t\tconst { record, entries } = read;\n\t\t\tlisted.push({\n\t\t\t\trunId: record.runId,\n\t\t\t\t...(record.parentRunId ? { parentRunId: record.parentRunId } : {}),\n\t\t\t\t...(record.absorbed ? { absorbed: [...record.absorbed] } : {}),\n\t\t\t\tstatus: record.status,\n\t\t\t\tstartedAt: record.startedAt,\n\t\t\t\t...(record.finishedAt ? { finishedAt: record.finishedAt } : {}),\n\t\t\t\tfiles: changedFiles(record, entries).map(change => displayPath(record.root, change.path)),\n\t\t\t});\n\t\t}\n\t\treturn { ...(head ? { head } : {}), runs: listed };\n\t}\n\n\tchanges(runId: string, options: { maxLines?: number } = {}): Promise<string> {\n\t\treturn showChanges(this.store, runId, options);\n\t}\n\n\tasync goTo(target: string, options: MoveOptions = {}): Promise<Move> {\n\t\treturn this.moveOf(await this.store.goTo(target, options));\n\t}\n\n\tasync undo(options: MoveOptions = {}): Promise<Move> {\n\t\treturn this.moveOf(await this.store.undo(options));\n\t}\n\n\tasync redo(options: MoveOptions = {}): Promise<Move> {\n\t\treturn this.moveOf(await this.store.redo(options));\n\t}\n\n\tprivate moveOf(revision: Revision): Move {\n\t\treturn {\n\t\t\t...(revision.from ? { from: revision.from } : {}),\n\t\t\t...(revision.to ? { to: revision.to } : {}),\n\t\t\tat: revision.at,\n\t\t\t...(revision.reason ? { reason: revision.reason } : {}),\n\t\t\tcomplete: revision.complete,\n\t\t\tconflicts: revision.conflicts.map(({ path, runId }) => ({\n\t\t\t\tpath: displayPath(this.root, path),\n\t\t\t\trunId,\n\t\t\t})),\n\t\t};\n\t}\n}\n"]}
1
+ {"version":3,"file":"history.js","sourceRoot":"","sources":["../../../src/recovery/domain/history.ts"],"names":[],"mappings":"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","sourcesContent":["import { changedFiles, displayPath, showChanges } from './runChanges.ts';\nimport type { MoveOptions, RecoveryStore, Revision, RunStatus } from './recoveryStore.ts';\nimport { HistoryExpiredError } from './runTree.ts';\n\n/** One run that wrote to the workspace. */\nexport interface HistoryRun {\n\trunId: string;\n\t/** The run the workspace was at when this one started; absent when it started at the start. */\n\tparentRunId?: string;\n\t/**\n\t * Runs whose changes retention chained into this one, oldest first: going back past this run\n\t * goes back past them too. An id an earlier `AgentResponse` gave may be found here.\n\t */\n\tabsorbed?: string[];\n\tstatus: RunStatus;\n\tstartedAt: string;\n\tfinishedAt?: string;\n\t/**\n\t * The files it changed, in the order it first touched each one: relative to the root, or the\n\t * real path of a file outside it. A host decides whether to show them.\n\t */\n\tfiles: string[];\n}\n\n/** One move of the workspace through its history. */\nexport interface Move {\n\t/** The run the workspace was at; absent for the start of the history. */\n\tfrom?: string;\n\t/** The run the workspace is at now; absent for the start of the history. */\n\tto?: string;\n\tat: string;\n\treason?: string;\n\t/** Every file reached the state the history expected. Otherwise `conflicts` names those left. */\n\tcomplete: boolean;\n\t/**\n\t * The files left as they were, and the run whose change could not be undone or redone: one\n\t * that did not hold what the history expected, such as one the user edited since, or one a\n\t * command changed into, or from, something the history cannot copy, such as a file over 64 MB.\n\t */\n\tconflicts: { path: string; runId: string }[];\n}\n\n/**\n * The history of the runs that wrote to one workspace: what each run changed, and moving the\n * workspace back and forward through them. Runs form a tree: going back and writing again\n * starts a new branch, and the earlier one stays reachable with `goTo`.\n */\nexport interface History {\n\t/**\n\t * Every run kept, oldest first, and the run the workspace is at (`head`), absent at the start\n\t * of the history. A run whose process died while writing is `interrupted`.\n\t */\n\tlist(): Promise<{ head?: string; runs: HistoryRun[] }>;\n\t/**\n\t * What a run changed, as a git-style diff per file. What does not fit in `maxLines`, 1000 by\n\t * default, is named instead.\n\t *\n\t * @throws {UnknownRunError} when `runId` was never a run of this workspace.\n\t * @throws {HistoryExpiredError} when retention removed it or chained it into a later run.\n\t */\n\tchanges(runId: string, options?: { maxLines?: number }): Promise<string>;\n\t/**\n\t * Moves the workspace to the state right after `target`, or to how it was before any run with\n\t * `historyStart`. A file that does not hold what the history expects is left as it is, and the\n\t * move is then not `complete`. With `from`, it moves only if the workspace is still there: a\n\t * host that showed the user what a move will do passes where the workspace was, so a run that\n\t * ended meanwhile cannot make it do something else.\n\t *\n\t * @throws {WorkspaceBusyError} when a run or another move holds the workspace.\n\t * @throws {WorkspaceMovedError} when the workspace is not at `from`.\n\t * @throws {UnknownRunError} when `target` is not in the history.\n\t * @throws {HistoryExpiredError} when retention chained `target` into a later run.\n\t */\n\tgoTo(target: string, options?: MoveOptions): Promise<Move>;\n\t/**\n\t * Moves the workspace to the run before the one it is at.\n\t *\n\t * @throws {NothingToMoveError} at the start of the history.\n\t * @throws {WorkspaceBusyError} when a run or another move holds the workspace.\n\t * @throws {WorkspaceMovedError} when the workspace is not at `from`.\n\t */\n\tundo(options?: MoveOptions): Promise<Move>;\n\t/**\n\t * Moves the workspace to the next run: towards where it was most recently, or else the newest\n\t * branch.\n\t *\n\t * @throws {NothingToMoveError} when no run follows the one it is at.\n\t * @throws {WorkspaceBusyError} when a run or another move holds the workspace.\n\t * @throws {WorkspaceMovedError} when the workspace is not at `from`.\n\t */\n\tredo(options?: MoveOptions): Promise<Move>;\n}\n\n/** `History` over a recovery store, with paths shown relative to the workspace's real root. */\nexport class StoreHistory implements History {\n\tprivate readonly store: RecoveryStore;\n\tprivate readonly root: string;\n\n\tconstructor({ store, root }: { store: RecoveryStore; root: string }) {\n\t\tthis.store = store;\n\t\tthis.root = root;\n\t}\n\n\tasync list(): Promise<{ head?: string; runs: HistoryRun[] }> {\n\t\tconst { head, runs } = await this.store.listRuns();\n\t\tconst listed: HistoryRun[] = [];\n\t\tfor (const run of runs) {\n\t\t\tconst read = await this.store.readRun(run.runId).catch((error: unknown) => {\n\t\t\t\t// Retention took it since it was listed: it is no longer in the history.\n\t\t\t\tif (error instanceof HistoryExpiredError) return undefined;\n\t\t\t\tthrow error;\n\t\t\t});\n\t\t\tif (!read) continue;\n\t\t\tconst { record, entries, unrecorded } = read;\n\t\t\tconst files = [\n\t\t\t\t...changedFiles(record, entries).map(change => change.path),\n\t\t\t\t...unrecorded.map(change => change.path),\n\t\t\t];\n\t\t\tlisted.push({\n\t\t\t\trunId: record.runId,\n\t\t\t\t...(record.parentRunId ? { parentRunId: record.parentRunId } : {}),\n\t\t\t\t...(record.absorbed ? { absorbed: [...record.absorbed] } : {}),\n\t\t\t\tstatus: record.status,\n\t\t\t\tstartedAt: record.startedAt,\n\t\t\t\t...(record.finishedAt ? { finishedAt: record.finishedAt } : {}),\n\t\t\t\tfiles: [...new Set(files)].map(path => displayPath(record.root, path)),\n\t\t\t});\n\t\t}\n\t\treturn { ...(head ? { head } : {}), runs: listed };\n\t}\n\n\tchanges(runId: string, options: { maxLines?: number } = {}): Promise<string> {\n\t\treturn showChanges(this.store, runId, options);\n\t}\n\n\tasync goTo(target: string, options: MoveOptions = {}): Promise<Move> {\n\t\treturn this.moveOf(await this.store.goTo(target, options));\n\t}\n\n\tasync undo(options: MoveOptions = {}): Promise<Move> {\n\t\treturn this.moveOf(await this.store.undo(options));\n\t}\n\n\tasync redo(options: MoveOptions = {}): Promise<Move> {\n\t\treturn this.moveOf(await this.store.redo(options));\n\t}\n\n\tprivate moveOf(revision: Revision): Move {\n\t\treturn {\n\t\t\t...(revision.from ? { from: revision.from } : {}),\n\t\t\t...(revision.to ? { to: revision.to } : {}),\n\t\t\tat: revision.at,\n\t\t\t...(revision.reason ? { reason: revision.reason } : {}),\n\t\t\tcomplete: revision.complete,\n\t\t\tconflicts: revision.conflicts.map(({ path, runId }) => ({\n\t\t\t\tpath: displayPath(this.root, path),\n\t\t\t\trunId,\n\t\t\t})),\n\t\t};\n\t}\n}\n"]}
@@ -36,6 +36,22 @@ export interface JournalEntry {
36
36
  */
37
37
  export interface Manifest {
38
38
  files: Record<string, FileState>;
39
+ /**
40
+ * Covered paths it could not copy, such as a folder, a link or a file too large: what they
41
+ * held is unknown, so a change to one can be named but not undone.
42
+ */
43
+ uncopyable?: string[];
44
+ }
45
+ /**
46
+ * A path a command changed into, or from, something the history cannot copy, such as a file
47
+ * over the size limit: what it held before or after is unknown. Undo and redo leave it as it is,
48
+ * and report it as a conflict.
49
+ */
50
+ export interface UnrecordedChange {
51
+ /** The real path, absolute. */
52
+ path: string;
53
+ /** The command it was seen around. */
54
+ by: string;
39
55
  }
40
56
  /** What one observation cost: before and after together. */
41
57
  export interface ObservationMeasure {
@@ -71,6 +87,12 @@ export interface RunRecord {
71
87
  startedAt: string;
72
88
  finishedAt?: string;
73
89
  status: RunStatus;
90
+ /**
91
+ * It finished with an observation still open, such as a command whose changes could not be
92
+ * compared after it ran: its journal may not hold all it changed yet. The next run or move
93
+ * settles it, as for an interrupted run, and keeps its status.
94
+ */
95
+ unsettled?: true;
74
96
  /** The process that ran it, so a run whose process died can be told from a live one. */
75
97
  pid: number;
76
98
  }
@@ -103,6 +125,11 @@ export interface RunJournal {
103
125
  * files cost, is kept with it.
104
126
  */
105
127
  observed(sequence: number, measure?: ObservationMeasure): Promise<void>;
128
+ /**
129
+ * Records that `by` changed `path` into, or from, something the history cannot copy. A move
130
+ * over the run leaves it as it is, and is not complete.
131
+ */
132
+ unrecorded(path: string, by: string): Promise<void>;
106
133
  /** How many bytes of content this run stored that the store did not hold yet. */
107
134
  readonly storedBytes: number;
108
135
  /** The path of every change it recorded as applied, in order: each one reached a file. */
@@ -114,20 +141,27 @@ export interface RunJournal {
114
141
  */
115
142
  contentState(): string;
116
143
  /**
117
- * Whether a change it recorded may have reached a file: one prepared and not abandoned. It
118
- * holds whatever happens to the record afterwards.
144
+ * Whether a change it recorded may have reached a file: one prepared and not abandoned, or one
145
+ * it could not copy. It holds whatever happens to the record afterwards, except once `finish`
146
+ * settled an observation the run left open and found that the run changed nothing: the run
147
+ * left the history, so it is `false`.
119
148
  */
120
149
  readonly changed: boolean;
121
150
  /**
122
151
  * Ends the run and releases the workspace for the next writer. A run that `changed` nothing
123
152
  * leaves the history, and the workspace is back at the run before it. If removing it fails,
124
- * the store still treats it as gone, and the next run or move finishes removing it.
153
+ * the store still treats it as gone, and the next run or move finishes removing it. A run that
154
+ * ends with an observation open is settled from its manifest now, or by the next run or move
155
+ * if that fails.
125
156
  */
126
157
  finish(status: Exclude<RunStatus, 'running' | 'interrupted'>): Promise<void>;
127
158
  }
128
159
  /** The point before any run: going there takes the workspace back to how it was. */
129
160
  export declare const historyStart = "start";
130
- /** A file a move left as it was, because it did not hold what the history expected there. */
161
+ /**
162
+ * A file a move left as it was: it did not hold what the history expected there, or a command
163
+ * changed it into, or from, something the history cannot copy.
164
+ */
131
165
  export interface MoveConflict {
132
166
  path: string;
133
167
  /** The run whose change could not be undone or redone. */
@@ -169,6 +203,12 @@ export declare class NothingToMoveError extends Error {
169
203
  * workspace itself. Only one run writes to a workspace at a time.
170
204
  */
171
205
  export interface RecoveryStore {
206
+ /**
207
+ * The history on disk this store keeps, from the real paths of its root and its state
208
+ * directory: two stores opened over one history, under any spelling of those paths, have the
209
+ * same one.
210
+ */
211
+ readonly workspaceId: string;
172
212
  /**
173
213
  * Starts recording a run and holds the workspace for it until `finish`. The new run's parent
174
214
  * is the run the workspace is at, and the workspace is at the new run from then on. Runs a
@@ -215,7 +255,8 @@ export interface RecoveryStore {
215
255
  /** Every move of the workspace, oldest first. */
216
256
  listRevisions(): Promise<Revision[]>;
217
257
  /**
218
- * A run's record and its changes, in the order they were prepared.
258
+ * A run's record, its changes, in the order they were prepared, and the changes it could not
259
+ * copy.
219
260
  *
220
261
  * @throws {UnknownRunError} when `runId` was never a run of this workspace.
221
262
  * @throws {HistoryExpiredError} when retention removed `runId` or chained it into a later run.
@@ -223,6 +264,7 @@ export interface RecoveryStore {
223
264
  readRun(runId: string): Promise<{
224
265
  record: RunRecord;
225
266
  entries: JournalEntry[];
267
+ unrecorded: UnrecordedChange[];
226
268
  }>;
227
269
  /** The bytes a hash names. */
228
270
  readContent(hash: string): Promise<Buffer>;
@@ -1 +1 @@
1
- {"version":3,"file":"recoveryStore.js","sourceRoot":"","sources":["../../../src/recovery/domain/recoveryStore.ts"],"names":[],"mappings":"AAgIA,oFAAoF;AACpF,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC;AAsCpC,2FAA2F;AAC3F,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAC5C,YAAY,SAA0B;QACrC,KAAK,CAAC,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC;QACpF,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IAClC,CAAC;CACD;AA4DD,uFAAuF;AACvF,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC7C,uEAAuE;IAC9D,QAAQ,CAAS;IAC1B,wDAAwD;IAC/C,MAAM,CAAS;IAExB,YAAY,QAAgB,EAAE,MAAc;QAC3C,MAAM,IAAI,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC;QACxF,KAAK,CAAC,iCAAiC,IAAI,CAAC,QAAQ,CAAC,cAAc,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACnF,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;QAClC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,CAAC;CACD;AAED,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IACnC,KAAK,CAAS;IAEvB,YAAY,KAAa;QACxB,KAAK,CAAC,0BAA0B,KAAK,mBAAmB,CAAC,CAAC;QAC1D,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACpB,CAAC;CACD","sourcesContent":["/**\n * What one path held at one moment: nothing, or a regular file with this content and mode.\n * `hash` names the content in the store, so the bytes themselves never travel in a record.\n */\nexport type FileState = { exists: false } | { exists: true; hash: string; mode: number };\n\n/**\n * One change to one file. It is recorded as `prepared` before the file is touched, and only\n * then applied, so a run that stops halfway always leaves a record of what it may have done.\n * `abandoned` means the change was prepared but never happened.\n */\nexport interface JournalEntry {\n\tsequence: number;\n\t/** The file's real path, absolute: a run may write to more than one root of its workspace. */\n\tpath: string;\n\tbefore: FileState;\n\tafter: FileState;\n\t/** Folders the change creates for a new file, outermost first: undo removes them if empty. */\n\tcreatedFolders?: string[];\n\t/**\n\t * The command whose run the change was seen around, for a change no tool of the harness made\n\t * itself. It was found by comparing the file before and after, not journaled before it was made.\n\t */\n\tobservedBy?: string;\n\tstatus: 'prepared' | 'applied' | 'abandoned';\n}\n\n/**\n * What a group of files held right before something outside the journal ran, such as a\n * command: the content of each, by its real path, absolute. It is stored as content, and the\n * journal names its hash before the command starts.\n */\nexport interface Manifest {\n\tfiles: Record<string, FileState>;\n}\n\n/** What one observation cost: before and after together. */\nexport interface ObservationMeasure {\n\t/** Files captured before, and compared after. */\n\tfiles: number;\n\tbytesRead: number;\n\t/** New content stored, which the store did not hold already. */\n\tbytesStored: number;\n\t/** Spent capturing and comparing, not running what was observed. */\n\tdurationMs: number;\n}\n\n/**\n * `interrupted` is a run whose process died while it was writing. The next run to take the\n * workspace settles each change it left `prepared`, from what the file holds now.\n */\nexport type RunStatus = 'running' | 'completed' | 'failed' | 'cancelled' | 'interrupted';\n\nexport interface RunRecord {\n\trunId: string;\n\t/** The real path of the workspace root the run wrote to. */\n\troot: string;\n\t/**\n\t * The run the workspace was at when this one started, so runs form a tree: going back to an\n\t * earlier run and writing again starts a new branch. Absent for the first run.\n\t */\n\tparentRunId?: string;\n\t/**\n\t * Runs whose changes retention chained into this one, oldest first. They are no longer in\n\t * the history: this run now goes back to where the oldest of them started.\n\t */\n\tabsorbed?: string[];\n\t/** The journal file that holds the run's changes. Absent while it is the first one written. */\n\tjournal?: string;\n\tstartedAt: string;\n\tfinishedAt?: string;\n\tstatus: RunStatus;\n\t/** The process that ran it, so a run whose process died can be told from a live one. */\n\tpid: number;\n}\n\n/**\n * The recovery record of one run that writes. Every method returns only once what it records\n * is on disk: a change is never made before the record that would undo it exists. Once a step\n * fails to reach the journal, the journal refuses every later step, because a line written\n * after a broken one could not be read back.\n */\nexport interface RunJournal {\n\treadonly runId: string;\n\t/** Stores `content` and returns the hash that names it. Content stored before is reused. */\n\tsaveContent(content: Buffer): Promise<string>;\n\t/** Records a change about to be made, and returns its sequence number. */\n\tprepare(change: Omit<JournalEntry, 'sequence' | 'status'>): Promise<number>;\n\t/** The change was made. */\n\tapplied(sequence: number): Promise<void>;\n\t/** The change was not made, and the file still holds its `before`. */\n\tabandoned(sequence: number): Promise<void>;\n\t/**\n\t * Records that `by`, such as a command, is about to change files the journal cannot see it\n\t * change, and that the stored `manifest` holds what they hold now. Each change it makes is\n\t * then recorded as its own entry, prepared and applied at once, before `observed`. If the run\n\t * stops first, the next run settles it: every file of the manifest that changed since is\n\t * recorded as changed by `by`. A file it created is not in the manifest, and is then missed.\n\t */\n\tobserve(manifest: string, by: string): Promise<number>;\n\t/**\n\t * Every change the observation found is recorded. `measure`, what capturing and comparing the\n\t * files cost, is kept with it.\n\t */\n\tobserved(sequence: number, measure?: ObservationMeasure): Promise<void>;\n\t/** How many bytes of content this run stored that the store did not hold yet. */\n\treadonly storedBytes: number;\n\t/** The path of every change it recorded as applied, in order: each one reached a file. */\n\treadonly appliedPaths: readonly string[];\n\t/**\n\t * What the files this run changed hold now, as one hash of their paths and states. A file back\n\t * to what it held before the run's first change to it counts as unchanged, so a change undone\n\t * gives the state from before it.\n\t */\n\tcontentState(): string;\n\t/**\n\t * Whether a change it recorded may have reached a file: one prepared and not abandoned. It\n\t * holds whatever happens to the record afterwards.\n\t */\n\treadonly changed: boolean;\n\t/**\n\t * Ends the run and releases the workspace for the next writer. A run that `changed` nothing\n\t * leaves the history, and the workspace is back at the run before it. If removing it fails,\n\t * the store still treats it as gone, and the next run or move finishes removing it.\n\t */\n\tfinish(status: Exclude<RunStatus, 'running' | 'interrupted'>): Promise<void>;\n}\n\n/** The point before any run: going there takes the workspace back to how it was. */\nexport const historyStart = 'start';\n\n/** A file a move left as it was, because it did not hold what the history expected there. */\nexport interface MoveConflict {\n\tpath: string;\n\t/** The run whose change could not be undone or redone. */\n\trunId: string;\n}\n\n/**\n * One move of the workspace through its history, kept so an agent that runs next can tell\n * that its conversation is about a state the workspace has left.\n */\nexport interface Revision {\n\t/** Counts the moves of this workspace, from 1. */\n\trevision: number;\n\t/** The run the workspace was at; absent for the start of the history. */\n\tfrom?: string;\n\t/** The run the workspace is at now; absent for the start of the history. */\n\tto?: string;\n\tat: string;\n\t/** Why the user went there, when the host asked. */\n\treason?: string;\n\t/** Every file reached the state the history expected. Otherwise `conflicts` names those that did not. */\n\tcomplete: boolean;\n\tconflicts: MoveConflict[];\n}\n\nexport interface MoveOptions {\n\t/** Why the user is moving the workspace, recorded with the move. */\n\treason?: string;\n\t/**\n\t * Where the workspace must be for the move to happen: a run, or `historyStart`. Checked under\n\t * the workspace lock, so a move confirmed against one state never runs from another.\n\t */\n\tfrom?: string;\n}\n\n/** There is nothing to undo at the start of the history, or nothing to redo at its tip. */\nexport class NothingToMoveError extends Error {\n\tconstructor(direction: 'undo' | 'redo') {\n\t\tsuper(direction === 'undo' ? 'There is no run to undo' : 'There is no run to redo');\n\t\tthis.name = 'NothingToMoveError';\n\t}\n}\n\n/**\n * Where the runs that write to one workspace keep what is needed to undo them, outside the\n * workspace itself. Only one run writes to a workspace at a time.\n */\nexport interface RecoveryStore {\n\t/**\n\t * Starts recording a run and holds the workspace for it until `finish`. The new run's parent\n\t * is the run the workspace is at, and the workspace is at the new run from then on. Runs a\n\t * dead process left `running` are settled first.\n\t *\n\t * @throws {WorkspaceBusyError} when another live run is writing to the workspace.\n\t */\n\tstartRun(): Promise<RunJournal>;\n\t/**\n\t * Every run, oldest first, and the run the workspace is at (`head`), absent before the first.\n\t * A run still recorded as `running` whose process is gone is listed as `interrupted`.\n\t */\n\tlistRuns(): Promise<{ head?: string; runs: RunRecord[] }>;\n\t/**\n\t * Moves the workspace to the state right after `target`, or to how it was before any run\n\t * with `historyStart`. It undoes runs one by one back to the closest common ancestor, then\n\t * redoes runs one by one forward to `target`. Each file is written only if it holds the\n\t * state the history expects there; one that does not is left as it is and reported, and the\n\t * move is then not `complete`. A move the process did not finish is finished by the next\n\t * move or run.\n\t *\n\t * @throws {WorkspaceBusyError} when a run or another move holds the workspace.\n\t * @throws {WorkspaceMovedError} when `from` was given and the workspace is elsewhere.\n\t * @throws {UnknownRunError} when `target` is not in the history.\n\t * @throws {HistoryExpiredError} when retention chained `target` into a later run.\n\t */\n\tgoTo(target: string, options?: MoveOptions): Promise<Revision>;\n\t/**\n\t * Moves the workspace to the run before the one it is at.\n\t *\n\t * @throws {NothingToMoveError} at the start of the history.\n\t */\n\tundo(options?: MoveOptions): Promise<Revision>;\n\t/**\n\t * Moves the workspace to the next run: towards where it was most recently, or the newest\n\t * branch.\n\t *\n\t * @throws {NothingToMoveError} when no run follows the one it is at.\n\t */\n\tredo(options?: MoveOptions): Promise<Revision>;\n\t/** Every move of the workspace, oldest first. */\n\tlistRevisions(): Promise<Revision[]>;\n\t/**\n\t * A run's record and its changes, in the order they were prepared.\n\t *\n\t * @throws {UnknownRunError} when `runId` was never a run of this workspace.\n\t * @throws {HistoryExpiredError} when retention removed `runId` or chained it into a later run.\n\t */\n\treadRun(runId: string): Promise<{ record: RunRecord; entries: JournalEntry[] }>;\n\t/** The bytes a hash names. */\n\treadContent(hash: string): Promise<Buffer>;\n}\n\n/** A move was asked from one point of the history, and the workspace is at another. */\nexport class WorkspaceMovedError extends Error {\n\t/** Where the move expected the workspace: a run, or `historyStart`. */\n\treadonly expected: string;\n\t/** Where the workspace is: a run, or `historyStart`. */\n\treadonly actual: string;\n\n\tconstructor(expected: string, actual: string) {\n\t\tconst name = (point: string) => (point === historyStart ? 'the start' : `run ${point}`);\n\t\tsuper(`The workspace is no longer at ${name(expected)}: it is at ${name(actual)}`);\n\t\tthis.name = 'WorkspaceMovedError';\n\t\tthis.expected = expected;\n\t\tthis.actual = actual;\n\t}\n}\n\nexport class WorkspaceBusyError extends Error {\n\treadonly runId: string;\n\n\tconstructor(runId: string) {\n\t\tsuper(`The workspace is busy: ${runId} is writing to it`);\n\t\tthis.name = 'WorkspaceBusyError';\n\t\tthis.runId = runId;\n\t}\n}\n"]}
1
+ {"version":3,"file":"recoveryStore.js","sourceRoot":"","sources":["../../../src/recovery/domain/recoveryStore.ts"],"names":[],"mappings":"AAgKA,oFAAoF;AACpF,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC;AAyCpC,2FAA2F;AAC3F,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAC5C,YAAY,SAA0B;QACrC,KAAK,CAAC,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC;QACpF,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IAClC,CAAC;CACD;AAqED,uFAAuF;AACvF,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC7C,uEAAuE;IAC9D,QAAQ,CAAS;IAC1B,wDAAwD;IAC/C,MAAM,CAAS;IAExB,YAAY,QAAgB,EAAE,MAAc;QAC3C,MAAM,IAAI,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,YAAY,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC;QACxF,KAAK,CAAC,iCAAiC,IAAI,CAAC,QAAQ,CAAC,cAAc,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACnF,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;QAClC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,CAAC;CACD;AAED,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IACnC,KAAK,CAAS;IAEvB,YAAY,KAAa;QACxB,KAAK,CAAC,0BAA0B,KAAK,mBAAmB,CAAC,CAAC;QAC1D,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACpB,CAAC;CACD","sourcesContent":["/**\n * What one path held at one moment: nothing, or a regular file with this content and mode.\n * `hash` names the content in the store, so the bytes themselves never travel in a record.\n */\nexport type FileState = { exists: false } | { exists: true; hash: string; mode: number };\n\n/**\n * One change to one file. It is recorded as `prepared` before the file is touched, and only\n * then applied, so a run that stops halfway always leaves a record of what it may have done.\n * `abandoned` means the change was prepared but never happened.\n */\nexport interface JournalEntry {\n\tsequence: number;\n\t/** The file's real path, absolute: a run may write to more than one root of its workspace. */\n\tpath: string;\n\tbefore: FileState;\n\tafter: FileState;\n\t/** Folders the change creates for a new file, outermost first: undo removes them if empty. */\n\tcreatedFolders?: string[];\n\t/**\n\t * The command whose run the change was seen around, for a change no tool of the harness made\n\t * itself. It was found by comparing the file before and after, not journaled before it was made.\n\t */\n\tobservedBy?: string;\n\tstatus: 'prepared' | 'applied' | 'abandoned';\n}\n\n/**\n * What a group of files held right before something outside the journal ran, such as a\n * command: the content of each, by its real path, absolute. It is stored as content, and the\n * journal names its hash before the command starts.\n */\nexport interface Manifest {\n\tfiles: Record<string, FileState>;\n\t/**\n\t * Covered paths it could not copy, such as a folder, a link or a file too large: what they\n\t * held is unknown, so a change to one can be named but not undone.\n\t */\n\tuncopyable?: string[];\n}\n\n/**\n * A path a command changed into, or from, something the history cannot copy, such as a file\n * over the size limit: what it held before or after is unknown. Undo and redo leave it as it is,\n * and report it as a conflict.\n */\nexport interface UnrecordedChange {\n\t/** The real path, absolute. */\n\tpath: string;\n\t/** The command it was seen around. */\n\tby: string;\n}\n\n/** What one observation cost: before and after together. */\nexport interface ObservationMeasure {\n\t/** Files captured before, and compared after. */\n\tfiles: number;\n\tbytesRead: number;\n\t/** New content stored, which the store did not hold already. */\n\tbytesStored: number;\n\t/** Spent capturing and comparing, not running what was observed. */\n\tdurationMs: number;\n}\n\n/**\n * `interrupted` is a run whose process died while it was writing. The next run to take the\n * workspace settles each change it left `prepared`, from what the file holds now.\n */\nexport type RunStatus = 'running' | 'completed' | 'failed' | 'cancelled' | 'interrupted';\n\nexport interface RunRecord {\n\trunId: string;\n\t/** The real path of the workspace root the run wrote to. */\n\troot: string;\n\t/**\n\t * The run the workspace was at when this one started, so runs form a tree: going back to an\n\t * earlier run and writing again starts a new branch. Absent for the first run.\n\t */\n\tparentRunId?: string;\n\t/**\n\t * Runs whose changes retention chained into this one, oldest first. They are no longer in\n\t * the history: this run now goes back to where the oldest of them started.\n\t */\n\tabsorbed?: string[];\n\t/** The journal file that holds the run's changes. Absent while it is the first one written. */\n\tjournal?: string;\n\tstartedAt: string;\n\tfinishedAt?: string;\n\tstatus: RunStatus;\n\t/**\n\t * It finished with an observation still open, such as a command whose changes could not be\n\t * compared after it ran: its journal may not hold all it changed yet. The next run or move\n\t * settles it, as for an interrupted run, and keeps its status.\n\t */\n\tunsettled?: true;\n\t/** The process that ran it, so a run whose process died can be told from a live one. */\n\tpid: number;\n}\n\n/**\n * The recovery record of one run that writes. Every method returns only once what it records\n * is on disk: a change is never made before the record that would undo it exists. Once a step\n * fails to reach the journal, the journal refuses every later step, because a line written\n * after a broken one could not be read back.\n */\nexport interface RunJournal {\n\treadonly runId: string;\n\t/** Stores `content` and returns the hash that names it. Content stored before is reused. */\n\tsaveContent(content: Buffer): Promise<string>;\n\t/** Records a change about to be made, and returns its sequence number. */\n\tprepare(change: Omit<JournalEntry, 'sequence' | 'status'>): Promise<number>;\n\t/** The change was made. */\n\tapplied(sequence: number): Promise<void>;\n\t/** The change was not made, and the file still holds its `before`. */\n\tabandoned(sequence: number): Promise<void>;\n\t/**\n\t * Records that `by`, such as a command, is about to change files the journal cannot see it\n\t * change, and that the stored `manifest` holds what they hold now. Each change it makes is\n\t * then recorded as its own entry, prepared and applied at once, before `observed`. If the run\n\t * stops first, the next run settles it: every file of the manifest that changed since is\n\t * recorded as changed by `by`. A file it created is not in the manifest, and is then missed.\n\t */\n\tobserve(manifest: string, by: string): Promise<number>;\n\t/**\n\t * Every change the observation found is recorded. `measure`, what capturing and comparing the\n\t * files cost, is kept with it.\n\t */\n\tobserved(sequence: number, measure?: ObservationMeasure): Promise<void>;\n\t/**\n\t * Records that `by` changed `path` into, or from, something the history cannot copy. A move\n\t * over the run leaves it as it is, and is not complete.\n\t */\n\tunrecorded(path: string, by: string): Promise<void>;\n\t/** How many bytes of content this run stored that the store did not hold yet. */\n\treadonly storedBytes: number;\n\t/** The path of every change it recorded as applied, in order: each one reached a file. */\n\treadonly appliedPaths: readonly string[];\n\t/**\n\t * What the files this run changed hold now, as one hash of their paths and states. A file back\n\t * to what it held before the run's first change to it counts as unchanged, so a change undone\n\t * gives the state from before it.\n\t */\n\tcontentState(): string;\n\t/**\n\t * Whether a change it recorded may have reached a file: one prepared and not abandoned, or one\n\t * it could not copy. It holds whatever happens to the record afterwards, except once `finish`\n\t * settled an observation the run left open and found that the run changed nothing: the run\n\t * left the history, so it is `false`.\n\t */\n\treadonly changed: boolean;\n\t/**\n\t * Ends the run and releases the workspace for the next writer. A run that `changed` nothing\n\t * leaves the history, and the workspace is back at the run before it. If removing it fails,\n\t * the store still treats it as gone, and the next run or move finishes removing it. A run that\n\t * ends with an observation open is settled from its manifest now, or by the next run or move\n\t * if that fails.\n\t */\n\tfinish(status: Exclude<RunStatus, 'running' | 'interrupted'>): Promise<void>;\n}\n\n/** The point before any run: going there takes the workspace back to how it was. */\nexport const historyStart = 'start';\n\n/**\n * A file a move left as it was: it did not hold what the history expected there, or a command\n * changed it into, or from, something the history cannot copy.\n */\nexport interface MoveConflict {\n\tpath: string;\n\t/** The run whose change could not be undone or redone. */\n\trunId: string;\n}\n\n/**\n * One move of the workspace through its history, kept so an agent that runs next can tell\n * that its conversation is about a state the workspace has left.\n */\nexport interface Revision {\n\t/** Counts the moves of this workspace, from 1. */\n\trevision: number;\n\t/** The run the workspace was at; absent for the start of the history. */\n\tfrom?: string;\n\t/** The run the workspace is at now; absent for the start of the history. */\n\tto?: string;\n\tat: string;\n\t/** Why the user went there, when the host asked. */\n\treason?: string;\n\t/** Every file reached the state the history expected. Otherwise `conflicts` names those that did not. */\n\tcomplete: boolean;\n\tconflicts: MoveConflict[];\n}\n\nexport interface MoveOptions {\n\t/** Why the user is moving the workspace, recorded with the move. */\n\treason?: string;\n\t/**\n\t * Where the workspace must be for the move to happen: a run, or `historyStart`. Checked under\n\t * the workspace lock, so a move confirmed against one state never runs from another.\n\t */\n\tfrom?: string;\n}\n\n/** There is nothing to undo at the start of the history, or nothing to redo at its tip. */\nexport class NothingToMoveError extends Error {\n\tconstructor(direction: 'undo' | 'redo') {\n\t\tsuper(direction === 'undo' ? 'There is no run to undo' : 'There is no run to redo');\n\t\tthis.name = 'NothingToMoveError';\n\t}\n}\n\n/**\n * Where the runs that write to one workspace keep what is needed to undo them, outside the\n * workspace itself. Only one run writes to a workspace at a time.\n */\nexport interface RecoveryStore {\n\t/**\n\t * The history on disk this store keeps, from the real paths of its root and its state\n\t * directory: two stores opened over one history, under any spelling of those paths, have the\n\t * same one.\n\t */\n\treadonly workspaceId: string;\n\t/**\n\t * Starts recording a run and holds the workspace for it until `finish`. The new run's parent\n\t * is the run the workspace is at, and the workspace is at the new run from then on. Runs a\n\t * dead process left `running` are settled first.\n\t *\n\t * @throws {WorkspaceBusyError} when another live run is writing to the workspace.\n\t */\n\tstartRun(): Promise<RunJournal>;\n\t/**\n\t * Every run, oldest first, and the run the workspace is at (`head`), absent before the first.\n\t * A run still recorded as `running` whose process is gone is listed as `interrupted`.\n\t */\n\tlistRuns(): Promise<{ head?: string; runs: RunRecord[] }>;\n\t/**\n\t * Moves the workspace to the state right after `target`, or to how it was before any run\n\t * with `historyStart`. It undoes runs one by one back to the closest common ancestor, then\n\t * redoes runs one by one forward to `target`. Each file is written only if it holds the\n\t * state the history expects there; one that does not is left as it is and reported, and the\n\t * move is then not `complete`. A move the process did not finish is finished by the next\n\t * move or run.\n\t *\n\t * @throws {WorkspaceBusyError} when a run or another move holds the workspace.\n\t * @throws {WorkspaceMovedError} when `from` was given and the workspace is elsewhere.\n\t * @throws {UnknownRunError} when `target` is not in the history.\n\t * @throws {HistoryExpiredError} when retention chained `target` into a later run.\n\t */\n\tgoTo(target: string, options?: MoveOptions): Promise<Revision>;\n\t/**\n\t * Moves the workspace to the run before the one it is at.\n\t *\n\t * @throws {NothingToMoveError} at the start of the history.\n\t */\n\tundo(options?: MoveOptions): Promise<Revision>;\n\t/**\n\t * Moves the workspace to the next run: towards where it was most recently, or the newest\n\t * branch.\n\t *\n\t * @throws {NothingToMoveError} when no run follows the one it is at.\n\t */\n\tredo(options?: MoveOptions): Promise<Revision>;\n\t/** Every move of the workspace, oldest first. */\n\tlistRevisions(): Promise<Revision[]>;\n\t/**\n\t * A run's record, its changes, in the order they were prepared, and the changes it could not\n\t * copy.\n\t *\n\t * @throws {UnknownRunError} when `runId` was never a run of this workspace.\n\t * @throws {HistoryExpiredError} when retention removed `runId` or chained it into a later run.\n\t */\n\treadRun(\n\t\trunId: string,\n\t): Promise<{ record: RunRecord; entries: JournalEntry[]; unrecorded: UnrecordedChange[] }>;\n\t/** The bytes a hash names. */\n\treadContent(hash: string): Promise<Buffer>;\n}\n\n/** A move was asked from one point of the history, and the workspace is at another. */\nexport class WorkspaceMovedError extends Error {\n\t/** Where the move expected the workspace: a run, or `historyStart`. */\n\treadonly expected: string;\n\t/** Where the workspace is: a run, or `historyStart`. */\n\treadonly actual: string;\n\n\tconstructor(expected: string, actual: string) {\n\t\tconst name = (point: string) => (point === historyStart ? 'the start' : `run ${point}`);\n\t\tsuper(`The workspace is no longer at ${name(expected)}: it is at ${name(actual)}`);\n\t\tthis.name = 'WorkspaceMovedError';\n\t\tthis.expected = expected;\n\t\tthis.actual = actual;\n\t}\n}\n\nexport class WorkspaceBusyError extends Error {\n\treadonly runId: string;\n\n\tconstructor(runId: string) {\n\t\tsuper(`The workspace is busy: ${runId} is writing to it`);\n\t\tthis.name = 'WorkspaceBusyError';\n\t\tthis.runId = runId;\n\t}\n}\n"]}
@@ -9,17 +9,26 @@ const binaryProbeBytes = 8000;
9
9
  * repository.
10
10
  */
11
11
  export async function showChanges(store, runId, { maxLines = 1000, visible } = {}) {
12
- const { record, entries } = await store.readRun(runId);
12
+ const { record, entries, unrecorded } = await store.readRun(runId);
13
13
  const all = changedFiles(record, entries);
14
14
  const changes = [];
15
15
  for (const change of all)
16
16
  if (!visible || (await visible(change.path)))
17
17
  changes.push(change);
18
+ const uncopied = [];
19
+ for (const change of unrecorded) {
20
+ if (!visible || (await visible(change.path)))
21
+ uncopied.push(change);
22
+ }
18
23
  const output = [];
19
- const hidden = all.length - changes.length;
24
+ const hidden = all.length + unrecorded.length - changes.length - uncopied.length;
20
25
  if (hidden > 0) {
21
26
  output.push(`# ${String(hidden)} changed ${hidden === 1 ? 'file is' : 'files are'} not shown: you may not read ${hidden === 1 ? 'it' : 'them'}`);
22
27
  }
28
+ // Named first: no diff can show them, and they must not be cut away with the diffs.
29
+ for (const { path, by } of uncopied) {
30
+ output.push(`# ${displayPath(record.root, path)}: changed while the command "${by}" ran, into or from something the history cannot copy (a folder, a link, or a file over 64 MB); undo leaves it as it is`);
31
+ }
23
32
  for (const [index, change] of changes.entries()) {
24
33
  const name = displayPath(record.root, change.path);
25
34
  const lines = await renderFile(store, name, change);
@@ -1 +1 @@
1
- {"version":3,"file":"runChanges.js","sourceRoot":"","sources":["../../../src/recovery/domain/runChanges.ts"],"names":[],"mappings":"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","sourcesContent":["import { unifiedDiff } from '#src/diff/domain/unifiedDiff';\nimport { type FileChange, fileChanges, sameState } from './fileChanges.ts';\nimport type { JournalEntry, RecoveryStore, RunRecord } from './recoveryStore.ts';\n\nconst binaryProbeBytes = 8000;\n\nexport interface ShowChangesOptions {\n\t/**\n\t * The most lines of diff shown. What does not fit is named instead, in at most two more lines.\n\t */\n\tmaxLines?: number;\n\t/**\n\t * Whether a file, by its real path, may be shown. A file it may not show is neither shown nor\n\t * named: only how many there were is said. Defaults to every file.\n\t */\n\tvisible?: (path: string) => Promise<boolean>;\n}\n\n/**\n * What a run changed, as a git-style diff per file, in the order the run first touched each one.\n * It shows each file from before the run's first change to after its last, so a file the run\n * created and then deleted does not appear. A change a live run has prepared but not made yet\n * is named, not shown. It reports the run's recorded effects, not a comparison of the whole\n * repository.\n */\nexport async function showChanges(\n\tstore: RecoveryStore,\n\trunId: string,\n\t{ maxLines = 1000, visible }: ShowChangesOptions = {},\n): Promise<string> {\n\tconst { record, entries } = await store.readRun(runId);\n\tconst all = changedFiles(record, entries);\n\tconst changes: FileChange[] = [];\n\tfor (const change of all) if (!visible || (await visible(change.path))) changes.push(change);\n\tconst output: string[] = [];\n\tconst hidden = all.length - changes.length;\n\tif (hidden > 0) {\n\t\toutput.push(\n\t\t\t`# ${String(hidden)} changed ${hidden === 1 ? 'file is' : 'files are'} not shown: you may not read ${hidden === 1 ? 'it' : 'them'}`,\n\t\t);\n\t}\n\n\tfor (const [index, change] of changes.entries()) {\n\t\tconst name = displayPath(record.root, change.path);\n\t\tconst lines = await renderFile(store, name, change);\n\t\tconst room = maxLines - output.length;\n\t\tif (lines.length <= room) {\n\t\t\toutput.push(...lines);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst rest = changes.slice(index + 1).map(next => displayPath(record.root, next.path));\n\t\tconst shown = Math.max(0, room);\n\t\toutput.push(...lines.slice(0, shown));\n\t\toutput.push(`... ${String(lines.length - shown)} more lines of the diff of ${name} not shown`);\n\t\tif (rest.length > 0) {\n\t\t\toutput.push(`... ${String(rest.length)} more changed files not shown: ${rest.join(', ')}`);\n\t\t}\n\t\tbreak;\n\t}\n\n\treturn output.join('\\n');\n}\n\n/**\n * The files a run changed, in the order it first touched each one. A file left as it was, such\n * as one the run created and then deleted, is not a change; one it may or may not have changed,\n * or is changing now, is.\n */\nexport function changedFiles(record: RunRecord, entries: JournalEntry[]): FileChange[] {\n\treturn fileChanges(entries, record.status !== 'running').filter(\n\t\tchange => change.uncertain || change.pending || !sameState(change.before, change.after),\n\t);\n}\n\nasync function renderFile(\n\tstore: RecoveryStore,\n\tname: string,\n\tchange: FileChange,\n): Promise<string[]> {\n\tconst { before, after } = change;\n\tconst lines: string[] = [];\n\tif (change.uncertain) {\n\t\tlines.push(`# ${name}: the run stopped while changing this file; it may not hold this change`);\n\t}\n\tif (change.observedBy.length > 0) {\n\t\tconst commands = change.observedBy.map(command => `\"${command}\"`).join(', ');\n\t\tlines.push(\n\t\t\t`# ${name}: changed while the command ${commands} ran, seen by comparing before and after`,\n\t\t);\n\t}\n\tif (change.pending) {\n\t\tlines.push(`# ${name}: the run is changing this file now; that change is not shown`);\n\t\tif (sameState(before, after)) return lines;\n\t}\n\tlines.push(`diff --git a/${name} b/${name}`);\n\tif (!before.exists && after.exists) lines.push(`new file mode ${gitMode(after.mode)}`);\n\tif (before.exists && !after.exists) lines.push(`deleted file mode ${gitMode(before.mode)}`);\n\tif (before.exists && after.exists && before.mode !== after.mode) {\n\t\tlines.push(`old mode ${gitMode(before.mode)}`, `new mode ${gitMode(after.mode)}`);\n\t}\n\tif (before.exists && after.exists && before.hash === after.hash) return lines;\n\n\tconst oldContent = before.exists ? await store.readContent(before.hash) : Buffer.alloc(0);\n\tconst newContent = after.exists ? await store.readContent(after.hash) : Buffer.alloc(0);\n\tconst oldName = before.exists ? `a/${name}` : '/dev/null';\n\tconst newName = after.exists ? `b/${name}` : '/dev/null';\n\n\tif (!isText(oldContent) || !isText(newContent)) {\n\t\tlines.push(`Binary files ${oldName} and ${newName} differ`);\n\t\treturn lines;\n\t}\n\tconst hunks = unifiedDiff(oldContent.toString('utf8'), newContent.toString('utf8'));\n\t// An empty file created or deleted has no hunks, as in git.\n\tif (hunks !== '') lines.push(`--- ${oldName}`, `+++ ${newName}`, ...hunks.split('\\n'));\n\treturn lines;\n}\n\n/**\n * Git's test, a NUL byte near the start, plus content that is not valid UTF-8: decoding would\n * replace its bytes, and two different files could then show no difference.\n */\nexport function isText(content: Buffer): boolean {\n\tif (content.subarray(0, binaryProbeBytes).includes(0)) return false;\n\treturn Buffer.from(content.toString('utf8'), 'utf8').equals(content);\n}\n\n/** A regular file's mode as git writes it, such as `100644`. */\nfunction gitMode(mode: number): string {\n\treturn (0o100000 | mode).toString(8);\n}\n\n/**\n * Relative to the run's root when inside it; a file in another root keeps its real path. Both\n * are real absolute paths, with `/` as the separator on every supported platform.\n */\nexport function displayPath(root: string, path: string): string {\n\treturn path.startsWith(`${root}/`) ? path.slice(root.length + 1) : path;\n}\n"]}
1
+ {"version":3,"file":"runChanges.js","sourceRoot":"","sources":["../../../src/recovery/domain/runChanges.ts"],"names":[],"mappings":"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","sourcesContent":["import { unifiedDiff } from '#src/diff/domain/unifiedDiff';\nimport { type FileChange, fileChanges, sameState } from './fileChanges.ts';\nimport type { JournalEntry, RecoveryStore, RunRecord } from './recoveryStore.ts';\n\nconst binaryProbeBytes = 8000;\n\nexport interface ShowChangesOptions {\n\t/**\n\t * The most lines of diff shown. What does not fit is named instead, in at most two more lines.\n\t */\n\tmaxLines?: number;\n\t/**\n\t * Whether a file, by its real path, may be shown. A file it may not show is neither shown nor\n\t * named: only how many there were is said. Defaults to every file.\n\t */\n\tvisible?: (path: string) => Promise<boolean>;\n}\n\n/**\n * What a run changed, as a git-style diff per file, in the order the run first touched each one.\n * It shows each file from before the run's first change to after its last, so a file the run\n * created and then deleted does not appear. A change a live run has prepared but not made yet\n * is named, not shown. It reports the run's recorded effects, not a comparison of the whole\n * repository.\n */\nexport async function showChanges(\n\tstore: RecoveryStore,\n\trunId: string,\n\t{ maxLines = 1000, visible }: ShowChangesOptions = {},\n): Promise<string> {\n\tconst { record, entries, unrecorded } = await store.readRun(runId);\n\tconst all = changedFiles(record, entries);\n\tconst changes: FileChange[] = [];\n\tfor (const change of all) if (!visible || (await visible(change.path))) changes.push(change);\n\tconst uncopied: typeof unrecorded = [];\n\tfor (const change of unrecorded) {\n\t\tif (!visible || (await visible(change.path))) uncopied.push(change);\n\t}\n\tconst output: string[] = [];\n\tconst hidden = all.length + unrecorded.length - changes.length - uncopied.length;\n\tif (hidden > 0) {\n\t\toutput.push(\n\t\t\t`# ${String(hidden)} changed ${hidden === 1 ? 'file is' : 'files are'} not shown: you may not read ${hidden === 1 ? 'it' : 'them'}`,\n\t\t);\n\t}\n\n\t// Named first: no diff can show them, and they must not be cut away with the diffs.\n\tfor (const { path, by } of uncopied) {\n\t\toutput.push(\n\t\t\t`# ${displayPath(record.root, path)}: changed while the command \"${by}\" ran, into or from something the history cannot copy (a folder, a link, or a file over 64 MB); undo leaves it as it is`,\n\t\t);\n\t}\n\tfor (const [index, change] of changes.entries()) {\n\t\tconst name = displayPath(record.root, change.path);\n\t\tconst lines = await renderFile(store, name, change);\n\t\tconst room = maxLines - output.length;\n\t\tif (lines.length <= room) {\n\t\t\toutput.push(...lines);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst rest = changes.slice(index + 1).map(next => displayPath(record.root, next.path));\n\t\tconst shown = Math.max(0, room);\n\t\toutput.push(...lines.slice(0, shown));\n\t\toutput.push(`... ${String(lines.length - shown)} more lines of the diff of ${name} not shown`);\n\t\tif (rest.length > 0) {\n\t\t\toutput.push(`... ${String(rest.length)} more changed files not shown: ${rest.join(', ')}`);\n\t\t}\n\t\tbreak;\n\t}\n\n\treturn output.join('\\n');\n}\n\n/**\n * The files a run changed, in the order it first touched each one. A file left as it was, such\n * as one the run created and then deleted, is not a change; one it may or may not have changed,\n * or is changing now, is.\n */\nexport function changedFiles(record: RunRecord, entries: JournalEntry[]): FileChange[] {\n\treturn fileChanges(entries, record.status !== 'running').filter(\n\t\tchange => change.uncertain || change.pending || !sameState(change.before, change.after),\n\t);\n}\n\nasync function renderFile(\n\tstore: RecoveryStore,\n\tname: string,\n\tchange: FileChange,\n): Promise<string[]> {\n\tconst { before, after } = change;\n\tconst lines: string[] = [];\n\tif (change.uncertain) {\n\t\tlines.push(`# ${name}: the run stopped while changing this file; it may not hold this change`);\n\t}\n\tif (change.observedBy.length > 0) {\n\t\tconst commands = change.observedBy.map(command => `\"${command}\"`).join(', ');\n\t\tlines.push(\n\t\t\t`# ${name}: changed while the command ${commands} ran, seen by comparing before and after`,\n\t\t);\n\t}\n\tif (change.pending) {\n\t\tlines.push(`# ${name}: the run is changing this file now; that change is not shown`);\n\t\tif (sameState(before, after)) return lines;\n\t}\n\tlines.push(`diff --git a/${name} b/${name}`);\n\tif (!before.exists && after.exists) lines.push(`new file mode ${gitMode(after.mode)}`);\n\tif (before.exists && !after.exists) lines.push(`deleted file mode ${gitMode(before.mode)}`);\n\tif (before.exists && after.exists && before.mode !== after.mode) {\n\t\tlines.push(`old mode ${gitMode(before.mode)}`, `new mode ${gitMode(after.mode)}`);\n\t}\n\tif (before.exists && after.exists && before.hash === after.hash) return lines;\n\n\tconst oldContent = before.exists ? await store.readContent(before.hash) : Buffer.alloc(0);\n\tconst newContent = after.exists ? await store.readContent(after.hash) : Buffer.alloc(0);\n\tconst oldName = before.exists ? `a/${name}` : '/dev/null';\n\tconst newName = after.exists ? `b/${name}` : '/dev/null';\n\n\tif (!isText(oldContent) || !isText(newContent)) {\n\t\tlines.push(`Binary files ${oldName} and ${newName} differ`);\n\t\treturn lines;\n\t}\n\tconst hunks = unifiedDiff(oldContent.toString('utf8'), newContent.toString('utf8'));\n\t// An empty file created or deleted has no hunks, as in git.\n\tif (hunks !== '') lines.push(`--- ${oldName}`, `+++ ${newName}`, ...hunks.split('\\n'));\n\treturn lines;\n}\n\n/**\n * Git's test, a NUL byte near the start, plus content that is not valid UTF-8: decoding would\n * replace its bytes, and two different files could then show no difference.\n */\nexport function isText(content: Buffer): boolean {\n\tif (content.subarray(0, binaryProbeBytes).includes(0)) return false;\n\treturn Buffer.from(content.toString('utf8'), 'utf8').equals(content);\n}\n\n/** A regular file's mode as git writes it, such as `100644`. */\nfunction gitMode(mode: number): string {\n\treturn (0o100000 | mode).toString(8);\n}\n\n/**\n * Relative to the run's root when inside it; a file in another root keeps its real path. Both\n * are real absolute paths, with `/` as the separator on every supported platform.\n */\nexport function displayPath(root: string, path: string): string {\n\treturn path.startsWith(`${root}/`) ? path.slice(root.length + 1) : path;\n}\n"]}