open-multi-agent-kit 0.97.0 → 0.98.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -0
- package/README.md +41 -9
- package/dist/cli/args.d.ts +2 -3
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +13 -189
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/help.d.ts +3 -0
- package/dist/cli/help.d.ts.map +1 -0
- package/dist/cli/help.js +194 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/initial-message.d.ts.map +1 -1
- package/dist/cli/initial-message.js +2 -1
- package/dist/cli/initial-message.js.map +1 -1
- package/dist/cli/resource-paths.d.ts +4 -0
- package/dist/cli/resource-paths.d.ts.map +1 -0
- package/dist/cli/resource-paths.js +59 -0
- package/dist/cli/resource-paths.js.map +1 -0
- package/dist/commands/adaptorch-doctor-cli.d.ts +13 -0
- package/dist/commands/adaptorch-doctor-cli.d.ts.map +1 -0
- package/dist/commands/adaptorch-doctor-cli.js +115 -0
- package/dist/commands/adaptorch-doctor-cli.js.map +1 -0
- package/dist/commands/resource-doctor-cli.d.ts +4 -0
- package/dist/commands/resource-doctor-cli.d.ts.map +1 -1
- package/dist/commands/resource-doctor-cli.js +35 -5
- package/dist/commands/resource-doctor-cli.js.map +1 -1
- package/dist/core/adaptorch-bridge.d.ts +11 -13
- package/dist/core/adaptorch-bridge.d.ts.map +1 -1
- package/dist/core/adaptorch-bridge.js +11 -13
- package/dist/core/adaptorch-bridge.js.map +1 -1
- package/dist/core/agent-session-snapshot.d.ts +16 -0
- package/dist/core/agent-session-snapshot.d.ts.map +1 -0
- package/dist/core/agent-session-snapshot.js +111 -0
- package/dist/core/agent-session-snapshot.js.map +1 -0
- package/dist/core/agent-session.d.ts +5 -9
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +27 -219
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/agent-tool-settings.d.ts +10 -0
- package/dist/core/agent-tool-settings.d.ts.map +1 -1
- package/dist/core/agent-tool-settings.js +10 -1
- package/dist/core/agent-tool-settings.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +7 -0
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +25 -61
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/index.d.ts +1 -0
- package/dist/core/compaction/index.d.ts.map +1 -1
- package/dist/core/compaction/index.js +1 -0
- package/dist/core/compaction/index.js.map +1 -1
- package/dist/core/compaction/knowledge-triage-format.d.ts +9 -0
- package/dist/core/compaction/knowledge-triage-format.d.ts.map +1 -0
- package/dist/core/compaction/knowledge-triage-format.js +71 -0
- package/dist/core/compaction/knowledge-triage-format.js.map +1 -0
- package/dist/core/compaction/knowledge-triage-types.d.ts +7 -0
- package/dist/core/compaction/knowledge-triage-types.d.ts.map +1 -0
- package/dist/core/compaction/knowledge-triage-types.js +2 -0
- package/dist/core/compaction/knowledge-triage-types.js.map +1 -0
- package/dist/core/compaction/knowledge-triage.d.ts +29 -0
- package/dist/core/compaction/knowledge-triage.d.ts.map +1 -0
- package/dist/core/compaction/knowledge-triage.js +189 -0
- package/dist/core/compaction/knowledge-triage.js.map +1 -0
- package/dist/core/compaction/transaction.d.ts.map +1 -1
- package/dist/core/compaction/transaction.js +1 -1
- package/dist/core/compaction/transaction.js.map +1 -1
- package/dist/core/compaction/utils.d.ts +4 -0
- package/dist/core/compaction/utils.d.ts.map +1 -1
- package/dist/core/compaction/utils.js +37 -0
- package/dist/core/compaction/utils.js.map +1 -1
- package/dist/core/completion-sound-io.d.ts +14 -0
- package/dist/core/completion-sound-io.d.ts.map +1 -0
- package/dist/core/completion-sound-io.js +64 -0
- package/dist/core/completion-sound-io.js.map +1 -0
- package/dist/core/completion-sound.d.ts +4 -10
- package/dist/core/completion-sound.d.ts.map +1 -1
- package/dist/core/completion-sound.js +28 -58
- package/dist/core/completion-sound.js.map +1 -1
- package/dist/core/context-budget-relevance.d.ts +15 -2
- package/dist/core/context-budget-relevance.d.ts.map +1 -1
- package/dist/core/context-budget-relevance.js +21 -9
- package/dist/core/context-budget-relevance.js.map +1 -1
- package/dist/core/context-budget-system-prompt-items.d.ts +1 -1
- package/dist/core/context-budget-system-prompt-items.d.ts.map +1 -1
- package/dist/core/context-budget-system-prompt-items.js.map +1 -1
- package/dist/core/context-budget-system-prompt.d.ts +1 -1
- package/dist/core/context-budget-system-prompt.d.ts.map +1 -1
- package/dist/core/context-budget-system-prompt.js.map +1 -1
- package/dist/core/context-file.d.ts +23 -0
- package/dist/core/context-file.d.ts.map +1 -0
- package/dist/core/context-file.js +17 -0
- package/dist/core/context-file.js.map +1 -0
- package/dist/core/keybindings.d.ts +1 -1
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +6 -1
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/model-prompt-policy.d.ts +1 -1
- package/dist/core/model-prompt-policy.d.ts.map +1 -1
- package/dist/core/model-prompt-policy.js.map +1 -1
- package/dist/core/prompt-settlement.d.ts +6 -3
- package/dist/core/prompt-settlement.d.ts.map +1 -1
- package/dist/core/prompt-settlement.js +10 -14
- package/dist/core/prompt-settlement.js.map +1 -1
- package/dist/core/reasoning-router-promotion.d.ts +41 -1
- package/dist/core/reasoning-router-promotion.d.ts.map +1 -1
- package/dist/core/reasoning-router-promotion.js +31 -2
- package/dist/core/reasoning-router-promotion.js.map +1 -1
- package/dist/core/reasoning-router-replay-stability.d.ts +65 -0
- package/dist/core/reasoning-router-replay-stability.d.ts.map +1 -0
- package/dist/core/reasoning-router-replay-stability.js +67 -0
- package/dist/core/reasoning-router-replay-stability.js.map +1 -0
- package/dist/core/reasoning-router-v4-weights.d.ts +6 -6
- package/dist/core/reasoning-router-v4-weights.d.ts.map +1 -1
- package/dist/core/reasoning-router-v4-weights.js +3 -3
- package/dist/core/reasoning-router-v4-weights.js.map +1 -1
- package/dist/core/reasoning-router-v4.d.ts.map +1 -1
- package/dist/core/reasoning-router-v4.js +6 -6
- package/dist/core/reasoning-router-v4.js.map +1 -1
- package/dist/core/redaction.d.ts.map +1 -1
- package/dist/core/redaction.js +1 -1
- package/dist/core/redaction.js.map +1 -1
- package/dist/core/resource-admission.d.ts +2 -1
- package/dist/core/resource-admission.d.ts.map +1 -1
- package/dist/core/resource-admission.js +11 -0
- package/dist/core/resource-admission.js.map +1 -1
- package/dist/core/resource-governor-format.d.ts +1 -1
- package/dist/core/resource-governor-format.d.ts.map +1 -1
- package/dist/core/resource-governor-format.js.map +1 -1
- package/dist/core/resource-governor-settings.d.ts +6 -12
- package/dist/core/resource-governor-settings.d.ts.map +1 -1
- package/dist/core/resource-governor-settings.js.map +1 -1
- package/dist/core/resource-governor-types.d.ts +24 -0
- package/dist/core/resource-governor-types.d.ts.map +1 -0
- package/dist/core/resource-governor-types.js +15 -0
- package/dist/core/resource-governor-types.js.map +1 -0
- package/dist/core/resource-loader.d.ts +1 -7
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/resource-observation-journal.d.ts +2 -1
- package/dist/core/resource-observation-journal.d.ts.map +1 -1
- package/dist/core/resource-observation-journal.js +4 -3
- package/dist/core/resource-observation-journal.js.map +1 -1
- package/dist/core/resource-observation-report-reader.d.ts +7 -0
- package/dist/core/resource-observation-report-reader.d.ts.map +1 -0
- package/dist/core/resource-observation-report-reader.js +157 -0
- package/dist/core/resource-observation-report-reader.js.map +1 -0
- package/dist/core/resource-observation-report.d.ts +24 -0
- package/dist/core/resource-observation-report.d.ts.map +1 -0
- package/dist/core/resource-observation-report.js +101 -0
- package/dist/core/resource-observation-report.js.map +1 -0
- package/dist/core/router-feedback-collector.d.ts +1 -1
- package/dist/core/router-feedback-collector.d.ts.map +1 -1
- package/dist/core/router-feedback-collector.js.map +1 -1
- package/dist/core/session-failure-cause.d.ts +33 -0
- package/dist/core/session-failure-cause.d.ts.map +1 -0
- package/dist/core/session-failure-cause.js +141 -0
- package/dist/core/session-failure-cause.js.map +1 -0
- package/dist/core/session-system-prompt.d.ts +1 -1
- package/dist/core/session-system-prompt.d.ts.map +1 -1
- package/dist/core/session-system-prompt.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt.d.ts +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +7 -7
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/workload-classifier.d.ts +2 -10
- package/dist/core/workload-classifier.d.ts.map +1 -1
- package/dist/core/workload-classifier.js +17 -0
- package/dist/core/workload-classifier.js.map +1 -1
- package/dist/core/workload-family-matcher.d.ts +1 -1
- package/dist/core/workload-family-matcher.d.ts.map +1 -1
- package/dist/core/workload-family-matcher.js.map +1 -1
- package/dist/core/workload-shard-execution-types.d.ts +40 -0
- package/dist/core/workload-shard-execution-types.d.ts.map +1 -0
- package/dist/core/workload-shard-execution-types.js +14 -0
- package/dist/core/workload-shard-execution-types.js.map +1 -0
- package/dist/core/workload-shard-executor.d.ts +3 -27
- package/dist/core/workload-shard-executor.d.ts.map +1 -1
- package/dist/core/workload-shard-executor.js.map +1 -1
- package/dist/core/workload-shard-runner.d.ts +1 -1
- package/dist/core/workload-shard-runner.d.ts.map +1 -1
- package/dist/core/workload-shard-runner.js.map +1 -1
- package/dist/core/workload-types.d.ts +24 -0
- package/dist/core/workload-types.d.ts.map +1 -0
- package/dist/core/workload-types.js +15 -0
- package/dist/core/workload-types.js.map +1 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +27 -47
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/theme/omk-control-panel.json +1 -1
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +42 -33
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +14 -2
- package/dist/utils/clipboard.js.map +1 -1
- package/docs/adaptorch-preview-spec.md +20 -18
- package/docs/adaptorch-preview.md +9 -9
- package/docs/compaction.md +24 -3
- package/docs/correctness-wall.md +10 -10
- package/docs/docs.json +12 -0
- package/docs/environment-variables.md +9 -2
- package/docs/index.md +3 -0
- package/docs/runtime-algorithms.md +359 -0
- package/docs/sdk.md +2 -2
- package/docs/settings.md +11 -10
- package/docs/usage.md +18 -2
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +18 -18
- package/package.json +6 -6
- package/dist/core/context-budget-governor.d.ts +0 -67
- package/dist/core/context-budget-governor.d.ts.map +0 -1
- package/dist/core/context-budget-governor.js +0 -224
- package/dist/core/context-budget-governor.js.map +0 -1
- package/dist/core/lean-ctx.d.ts +0 -99
- package/dist/core/lean-ctx.d.ts.map +0 -1
- package/dist/core/lean-ctx.js +0 -225
- package/dist/core/lean-ctx.js.map +0 -1
- package/dist/core/read-anchors.d.ts +0 -110
- package/dist/core/read-anchors.d.ts.map +0 -1
- package/dist/core/read-anchors.js +0 -193
- package/dist/core/read-anchors.js.map +0 -1
- package/dist/core/recovery-checkpoint.d.ts +0 -196
- package/dist/core/recovery-checkpoint.d.ts.map +0 -1
- package/dist/core/recovery-checkpoint.js +0 -382
- package/dist/core/recovery-checkpoint.js.map +0 -1
- package/dist/core/sandbox-policy-v2.d.ts +0 -49
- package/dist/core/sandbox-policy-v2.d.ts.map +0 -1
- package/dist/core/sandbox-policy-v2.js +0 -368
- package/dist/core/sandbox-policy-v2.js.map +0 -1
- package/dist/guardrails/audit-logger.d.ts +0 -4
- package/dist/guardrails/audit-logger.d.ts.map +0 -1
- package/dist/guardrails/audit-logger.js +0 -28
- package/dist/guardrails/audit-logger.js.map +0 -1
- package/dist/guardrails/freedomd-adapter.d.ts +0 -16
- package/dist/guardrails/freedomd-adapter.d.ts.map +0 -1
- package/dist/guardrails/freedomd-adapter.js +0 -87
- package/dist/guardrails/freedomd-adapter.js.map +0 -1
- package/dist/orchestration/lane-grant-auditor.d.ts +0 -4
- package/dist/orchestration/lane-grant-auditor.d.ts.map +0 -1
- package/dist/orchestration/lane-grant-auditor.js +0 -87
- package/dist/orchestration/lane-grant-auditor.js.map +0 -1
- package/dist/types/guardrails.d.ts +0 -61
- package/dist/types/guardrails.d.ts.map +0 -1
- package/dist/types/guardrails.js +0 -9
- package/dist/types/guardrails.js.map +0 -1
- package/dist/types/lane-grant.d.ts +0 -58
- package/dist/types/lane-grant.d.ts.map +0 -1
- package/dist/types/lane-grant.js +0 -9
- package/dist/types/lane-grant.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { constants } from \"node:fs\";\nimport { access as fsAccess } from \"node:fs/promises\";\nimport type { AgentTool } from \"omk-agent-core\";\nimport { Container, Text, truncateToWidth } from \"omk-tui\";\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\ttrackDetachedChildPid,\n\tuntrackDetachedChildPid,\n} from \"../../utils/shell.ts\";\nimport { classifyShellCommand } from \"../command-safety.ts\";\nimport { isCommandSafetyDisabled } from \"../extensions/builtin/command-safety-gate.ts\";\nimport type { ExtensionContext, ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport { assertLoadoutAccess, type LoadoutAccessGuard } from \"../loadout-access-policy.ts\";\nimport { detectSandboxBackend } from \"../sandbox/backend.ts\";\nimport type { SandboxBackendStatus, SandboxPathResolver, SandboxPolicy } from \"../sandbox/policy.ts\";\nimport { buildSandboxedSpawnRequest, type SandboxedSpawnRequest } from \"../sandbox/spawn.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 bashSchema = Type.Object({\n\tcommand: Type.String({ description: \"Bash command to execute\" }),\n\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n});\n\nexport type BashToolInput = Static<typeof bashSchema>;\n\nexport interface BashToolDetails {\n\ttruncation?: TruncationResult;\n\tfullOutputPath?: string;\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 }>;\n}\n\n/**\n * Create bash operations using OMK's built-in local shell execution backend.\n *\n * This is useful for extensions that intercept user_bash and still want OMK's\n * standard local shell behavior while wrapping, sandboxing, or rewriting commands.\n */\nexport interface LocalBashOperationsOptions {\n\treadonly shellPath?: string;\n\treadonly sandboxPolicy?: BashSandboxPreflight;\n\t/** Override backend probing for embedders and deterministic tests. */\n\treadonly detectSandboxBackend?: () => SandboxBackendStatus;\n}\n\nexport function createLocalBashOperations(options?: LocalBashOperationsOptions): BashOperations {\n\tconst sandbox = options?.sandboxPolicy;\n\tlet detectedBackend: SandboxBackendStatus | undefined;\n\tconst resolveBackend = (): SandboxBackendStatus => {\n\t\tif (sandbox?.backend) return sandbox.backend;\n\t\tdetectedBackend ??= (options?.detectSandboxBackend ?? defaultBashSandboxBackend)();\n\t\treturn detectedBackend;\n\t};\n\treturn {\n\t\texec: async (command, cwd, { onData, signal, timeout, env }) => {\n\t\t\tconst { shell, args } = getShellConfig(options?.shellPath);\n\t\t\tlet spawnCommand = shell;\n\t\t\tlet spawnArgs = [...args, command];\n\t\t\tlet spawnCwd = cwd;\n\t\t\tlet spawnEnv: NodeJS.ProcessEnv = env ?? getShellEnv();\n\t\t\tif (sandbox) {\n\t\t\t\tconst backend = resolveBackend();\n\t\t\t\tconst request = buildSandboxedSpawnRequest({\n\t\t\t\t\targv: [shell, ...args, command],\n\t\t\t\t\tcwd,\n\t\t\t\t\tenv: spawnEnv,\n\t\t\t\t\tpolicy: sandbox.policy,\n\t\t\t\t\tbackend,\n\t\t\t\t\tresolver: sandbox.resolver,\n\t\t\t\t});\n\t\t\t\tsandbox.onSpawnDecision?.(request);\n\t\t\t\tif (!request.allowed) {\n\t\t\t\t\tthrow new Error(`sandbox: shell denied\\n[${request.rule}] ${request.reason}`);\n\t\t\t\t}\n\t\t\t\tspawnCommand = request.argv[0];\n\t\t\t\tspawnArgs = [...request.argv.slice(1)];\n\t\t\t\tspawnCwd = request.cwd;\n\t\t\t\tspawnEnv = request.env;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait fsAccess(spawnCwd, constants.F_OK);\n\t\t\t} catch {\n\t\t\t\tthrow new Error(`Working directory does not exist: ${spawnCwd}\\nCannot execute bash commands.`);\n\t\t\t}\n\t\t\tif (signal?.aborted) {\n\t\t\t\tthrow new Error(\"aborted\");\n\t\t\t}\n\n\t\t\t// nosemgrep: javascript.lang.security.detect-child-process.detect-child-process -- argv-array spawn; sandbox policy is applied above and shell mode is not used.\n\t\t\tconst child = spawn(spawnCommand, spawnArgs, {\n\t\t\t\tcwd: spawnCwd,\n\t\t\t\tdetached: process.platform !== \"win32\",\n\t\t\t\tenv: spawnEnv,\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\twindowsHide: true,\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 (timeout !== undefined && timeout > 0) {\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}, timeout * 1000);\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 { exitCode };\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};\n}\n\n/**\n * Sandbox preflight inputs for local bash operations. When provided to\n * {@link createLocalBashOperations}, every spawn is gated by the sandbox spawn\n * builder: shell is denied when an OS sandbox backend is missing under enforce\n * mode, the environment is filtered, and available backends wrap the spawned\n * command. When omitted, local bash behavior is unchanged.\n */\nexport interface BashSandboxPreflight {\n\t/** Policy that decides whether this spawn may proceed. */\n\tpolicy: SandboxPolicy;\n\t/**\n\t * Availability of an OS-level sandbox backend. Local operations auto-detect\n\t * macOS sandbox-exec or Linux bubblewrap when this is omitted.\n\t */\n\tbackend?: SandboxBackendStatus;\n\t/** Optional resolver used to canonicalize the working directory before the root check. */\n\tresolver?: SandboxPathResolver;\n\t/** Observer for each sandbox spawn decision (audit/enforce ledger recording). */\n\tonSpawnDecision?: (decision: SandboxedSpawnRequest) => void;\n}\n\nfunction defaultBashSandboxBackend(): SandboxBackendStatus {\n\treturn detectSandboxBackend();\n}\n\nexport interface BashSpawnContext {\n\tcommand: string;\n\tcwd: string;\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport type BashSpawnHook = (context: BashSpawnContext) => BashSpawnContext;\n\nconst SESSION_ENV_KEYS = [\"PI_SESSION_ID\", \"PI_SESSION_FILE\", \"PI_PROVIDER\", \"PI_MODEL\", \"PI_REASONING_LEVEL\"] as const;\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\t// Anti-spoof: never inherit PI_* session metadata from the parent environment.\n\tfor (const key of SESSION_ENV_KEYS) delete env[key];\n\tif (exposeSessionEnvironment && ctx?.sessionManager) {\n\t\tconst model = ctx.model;\n\t\tenv.PI_SESSION_ID = ctx.sessionManager.getSessionId();\n\t\tconst sessionFile = ctx.sessionManager.getSessionFile();\n\t\tif (sessionFile) env.PI_SESSION_FILE = sessionFile;\n\t\tif (model) {\n\t\t\tenv.PI_PROVIDER = model.provider;\n\t\t\tenv.PI_MODEL = model.id;\n\t\t}\n\t\tif (ctx.thinkingLevel) env.PI_REASONING_LEVEL = ctx.thinkingLevel;\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 session metadata as PI_* environment variables. Default: true */\n\texposeSessionEnvironment?: boolean;\n\t/** Trusted sandbox policy for local shell execution */\n\tsandboxPolicy?: BashSandboxPreflight;\n\tloadoutAccessGuard?: LoadoutAccessGuard;\n\t/** Hook to adjust command, cwd, or env before execution */\n\tspawnHook?: BashSpawnHook;\n}\n\nconst BASH_PREVIEW_LINES = 5;\nconst BASH_UPDATE_THROTTLE_MS = 100;\n\ntype 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 formatBashCall(args: { command?: string; timeout?: number } | undefined): string {\n\tconst command = str(args?.command);\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(`$ ${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\")})`;\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 function createBashToolDefinition(\n\tcwd: string,\n\toptions?: BashToolOptions,\n): ToolDefinition<typeof bashSchema, BashToolDetails | undefined, BashRenderState> {\n\tconst ops =\n\t\toptions?.operations ??\n\t\tcreateLocalBashOperations({ shellPath: options?.shellPath, sandboxPolicy: options?.sandboxPolicy });\n\tconst commandPrefix = options?.commandPrefix;\n\tconst exposeSessionEnvironment = options?.exposeSessionEnvironment ?? true;\n\tconst spawnHook = options?.spawnHook;\n\treturn {\n\t\tname: \"bash\",\n\t\tlabel: \"bash\",\n\t\tdescription: `Execute a bash 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: \"Execute bash commands (ls, grep, find, etc.)\",\n\t\tpromptGuidelines: exposeSessionEnvironment\n\t\t\t? [\"Inspect PI_* environment variables for current model and session details.\"]\n\t\t\t: undefined,\n\t\tparameters: bashSchema,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\t{ command, timeout }: { command: string; timeout?: number },\n\t\t\tsignal?: AbortSignal,\n\t\t\tonUpdate?,\n\t\t\tctx?,\n\t\t) {\n\t\t\tconst resolvedCommand = commandPrefix ? `${commandPrefix}\\n${command}` : command;\n\t\t\tconst spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook, exposeSessionEnvironment, ctx);\n\t\t\tassertLoadoutAccess(options?.loadoutAccessGuard, {\n\t\t\t\toperation: \"execute\",\n\t\t\t\ttoolName: \"bash\",\n\t\t\t\tcommand: spawnContext.command,\n\t\t\t});\n\n\t\t\t// Safety floor: re-classify the EFFECTIVE command after commandPrefix/spawnHook.\n\t\t\t// Skipped entirely in YOLO mode (env contract in isCommandSafetyDisabled).\n\t\t\tif (!isCommandSafetyDisabled()) {\n\t\t\t\tconst effectiveVerdict = classifyShellCommand(spawnContext.command);\n\t\t\t\tif (effectiveVerdict.risk === \"block\") {\n\t\t\t\t\tthrow new Error(`command-safety: blocked\\n[${effectiveVerdict.rule}] ${effectiveVerdict.reason}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst output = new OutputAccumulator({ tempFilePrefix: \"omk-bash\" });\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\toutput.append(data);\n\t\t\t\tscheduleOutputUpdate();\n\t\t\t};\n\n\t\t\tconst finishOutput = async () => {\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\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} 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\tif (err instanceof Error && err.message === \"aborted\") {\n\t\t\t\t\t\tthrow new Error(appendStatus(text, \"Command aborted\"));\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 Error(appendStatus(text, `Command timed out after ${timeoutSecs} seconds`));\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\tif (exitCode !== 0 && exitCode !== null) {\n\t\t\t\t\tthrow new Error(appendStatus(outputText, `Command exited with code ${exitCode}`));\n\t\t\t\t}\n\t\t\t\treturn { content: [{ type: \"text\", text: outputText }], details };\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(formatBashCall(args));\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\nexport function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {\n\treturn wrapToolDefinition(createBashToolDefinition(cwd, options));\n}\n"]}
|
|
1
|
+
{"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { constants } from \"node:fs\";\nimport { access as fsAccess } from \"node:fs/promises\";\nimport type { AgentTool } from \"omk-agent-core\";\nimport { Container, Text, truncateToWidth } from \"omk-tui\";\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\ttrackDetachedChildPid,\n\tuntrackDetachedChildPid,\n} from \"../../utils/shell.ts\";\nimport { DEFAULT_BUILTIN_TOOL_TIMEOUTS } from \"../agent-tool-settings.ts\";\nimport { classifyShellCommand } from \"../command-safety.ts\";\nimport { isCommandSafetyDisabled } from \"../extensions/builtin/command-safety-gate.ts\";\nimport type { ExtensionContext, ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport { assertLoadoutAccess, type LoadoutAccessGuard } from \"../loadout-access-policy.ts\";\nimport { detectSandboxBackend } from \"../sandbox/backend.ts\";\nimport type { SandboxBackendStatus, SandboxPathResolver, SandboxPolicy } from \"../sandbox/policy.ts\";\nimport { buildSandboxedSpawnRequest, type SandboxedSpawnRequest } from \"../sandbox/spawn.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 BASH_TIMEOUT_DESCRIPTION = `Timeout in seconds. Defaults to ${DEFAULT_BUILTIN_TOOL_TIMEOUTS.bash / 1000}s and the command is terminated at that bound, so raise it for long work such as large downloads, builds, or training runs.`;\n\nconst bashSchema = Type.Object({\n\tcommand: Type.String({ description: \"Bash command to execute\" }),\n\t// The stated default must track the runtime one; see bash-timeout-disclosure.test.ts.\n\ttimeout: Type.Optional(Type.Number({ description: BASH_TIMEOUT_DESCRIPTION })),\n});\n\nexport type BashToolInput = Static<typeof bashSchema>;\n\nexport interface BashToolDetails {\n\ttruncation?: TruncationResult;\n\tfullOutputPath?: string;\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 }>;\n}\n\n/**\n * Create bash operations using OMK's built-in local shell execution backend.\n *\n * This is useful for extensions that intercept user_bash and still want OMK's\n * standard local shell behavior while wrapping, sandboxing, or rewriting commands.\n */\nexport interface LocalBashOperationsOptions {\n\treadonly shellPath?: string;\n\treadonly sandboxPolicy?: BashSandboxPreflight;\n\t/** Override backend probing for embedders and deterministic tests. */\n\treadonly detectSandboxBackend?: () => SandboxBackendStatus;\n}\n\nexport function createLocalBashOperations(options?: LocalBashOperationsOptions): BashOperations {\n\tconst sandbox = options?.sandboxPolicy;\n\tlet detectedBackend: SandboxBackendStatus | undefined;\n\tconst resolveBackend = (): SandboxBackendStatus => {\n\t\tif (sandbox?.backend) return sandbox.backend;\n\t\tdetectedBackend ??= (options?.detectSandboxBackend ?? defaultBashSandboxBackend)();\n\t\treturn detectedBackend;\n\t};\n\treturn {\n\t\texec: async (command, cwd, { onData, signal, timeout, env }) => {\n\t\t\tconst { shell, args } = getShellConfig(options?.shellPath);\n\t\t\tlet spawnCommand = shell;\n\t\t\tlet spawnArgs = [...args, command];\n\t\t\tlet spawnCwd = cwd;\n\t\t\tlet spawnEnv: NodeJS.ProcessEnv = env ?? getShellEnv();\n\t\t\tif (sandbox) {\n\t\t\t\tconst backend = resolveBackend();\n\t\t\t\tconst request = buildSandboxedSpawnRequest({\n\t\t\t\t\targv: [shell, ...args, command],\n\t\t\t\t\tcwd,\n\t\t\t\t\tenv: spawnEnv,\n\t\t\t\t\tpolicy: sandbox.policy,\n\t\t\t\t\tbackend,\n\t\t\t\t\tresolver: sandbox.resolver,\n\t\t\t\t});\n\t\t\t\tsandbox.onSpawnDecision?.(request);\n\t\t\t\tif (!request.allowed) {\n\t\t\t\t\tthrow new Error(`sandbox: shell denied\\n[${request.rule}] ${request.reason}`);\n\t\t\t\t}\n\t\t\t\tspawnCommand = request.argv[0];\n\t\t\t\tspawnArgs = [...request.argv.slice(1)];\n\t\t\t\tspawnCwd = request.cwd;\n\t\t\t\tspawnEnv = request.env;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait fsAccess(spawnCwd, constants.F_OK);\n\t\t\t} catch {\n\t\t\t\tthrow new Error(`Working directory does not exist: ${spawnCwd}\\nCannot execute bash commands.`);\n\t\t\t}\n\t\t\tif (signal?.aborted) {\n\t\t\t\tthrow new Error(\"aborted\");\n\t\t\t}\n\n\t\t\t// nosemgrep: javascript.lang.security.detect-child-process.detect-child-process -- argv-array spawn; sandbox policy is applied above and shell mode is not used.\n\t\t\tconst child = spawn(spawnCommand, spawnArgs, {\n\t\t\t\tcwd: spawnCwd,\n\t\t\t\tdetached: process.platform !== \"win32\",\n\t\t\t\tenv: spawnEnv,\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\twindowsHide: true,\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 (timeout !== undefined && timeout > 0) {\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}, timeout * 1000);\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 { exitCode };\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};\n}\n\n/**\n * Sandbox preflight inputs for local bash operations. When provided to\n * {@link createLocalBashOperations}, every spawn is gated by the sandbox spawn\n * builder: shell is denied when an OS sandbox backend is missing under enforce\n * mode, the environment is filtered, and available backends wrap the spawned\n * command. When omitted, local bash behavior is unchanged.\n */\nexport interface BashSandboxPreflight {\n\t/** Policy that decides whether this spawn may proceed. */\n\tpolicy: SandboxPolicy;\n\t/**\n\t * Availability of an OS-level sandbox backend. Local operations auto-detect\n\t * macOS sandbox-exec or Linux bubblewrap when this is omitted.\n\t */\n\tbackend?: SandboxBackendStatus;\n\t/** Optional resolver used to canonicalize the working directory before the root check. */\n\tresolver?: SandboxPathResolver;\n\t/** Observer for each sandbox spawn decision (audit/enforce ledger recording). */\n\tonSpawnDecision?: (decision: SandboxedSpawnRequest) => void;\n}\n\nfunction defaultBashSandboxBackend(): SandboxBackendStatus {\n\treturn detectSandboxBackend();\n}\n\nexport interface BashSpawnContext {\n\tcommand: string;\n\tcwd: string;\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport type BashSpawnHook = (context: BashSpawnContext) => BashSpawnContext;\n\nconst SESSION_ENV_KEYS = [\"PI_SESSION_ID\", \"PI_SESSION_FILE\", \"PI_PROVIDER\", \"PI_MODEL\", \"PI_REASONING_LEVEL\"] as const;\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\t// Anti-spoof: never inherit PI_* session metadata from the parent environment.\n\tfor (const key of SESSION_ENV_KEYS) delete env[key];\n\tif (exposeSessionEnvironment && ctx?.sessionManager) {\n\t\tconst model = ctx.model;\n\t\tenv.PI_SESSION_ID = ctx.sessionManager.getSessionId();\n\t\tconst sessionFile = ctx.sessionManager.getSessionFile();\n\t\tif (sessionFile) env.PI_SESSION_FILE = sessionFile;\n\t\tif (model) {\n\t\t\tenv.PI_PROVIDER = model.provider;\n\t\t\tenv.PI_MODEL = model.id;\n\t\t}\n\t\tif (ctx.thinkingLevel) env.PI_REASONING_LEVEL = ctx.thinkingLevel;\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 session metadata as PI_* environment variables. Default: true */\n\texposeSessionEnvironment?: boolean;\n\t/** Trusted sandbox policy for local shell execution */\n\tsandboxPolicy?: BashSandboxPreflight;\n\tloadoutAccessGuard?: LoadoutAccessGuard;\n\t/** Hook to adjust command, cwd, or env before execution */\n\tspawnHook?: BashSpawnHook;\n}\n\nconst BASH_PREVIEW_LINES = 5;\nconst BASH_UPDATE_THROTTLE_MS = 100;\n\ntype 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 formatBashCall(args: { command?: string; timeout?: number } | undefined): string {\n\tconst command = str(args?.command);\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(`$ ${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, 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\")})`;\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 endTime = endedAt ?? Date.now();\n\t\tconst timing = `${options.isPartial ? \"Elapsed\" : \"Took\"} ${((endTime - startedAt) / 1000).toFixed(1)}s`;\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"muted\", timing)}`, 0, 0));\n\t}\n}\n\nexport function createBashToolDefinition(\n\tcwd: string,\n\toptions?: BashToolOptions,\n): ToolDefinition<typeof bashSchema, BashToolDetails | undefined, BashRenderState> {\n\tconst ops =\n\t\toptions?.operations ??\n\t\tcreateLocalBashOperations({ shellPath: options?.shellPath, sandboxPolicy: options?.sandboxPolicy });\n\tconst commandPrefix = options?.commandPrefix;\n\tconst exposeSessionEnvironment = options?.exposeSessionEnvironment ?? true;\n\tconst spawnHook = options?.spawnHook;\n\treturn {\n\t\tname: \"bash\",\n\t\tlabel: \"bash\",\n\t\tdescription: `Execute a bash 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: \"Execute bash commands (ls, grep, find, etc.)\",\n\t\tpromptGuidelines: exposeSessionEnvironment\n\t\t\t? [\"Inspect PI_* environment variables for current model and session details.\"]\n\t\t\t: undefined,\n\t\tparameters: bashSchema,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\t{ command, timeout }: { command: string; timeout?: number },\n\t\t\tsignal?: AbortSignal,\n\t\t\tonUpdate?,\n\t\t\tctx?,\n\t\t) {\n\t\t\tconst resolvedCommand = commandPrefix ? `${commandPrefix}\\n${command}` : command;\n\t\t\tconst spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook, exposeSessionEnvironment, ctx);\n\t\t\tassertLoadoutAccess(options?.loadoutAccessGuard, {\n\t\t\t\toperation: \"execute\",\n\t\t\t\ttoolName: \"bash\",\n\t\t\t\tcommand: spawnContext.command,\n\t\t\t});\n\n\t\t\t// Safety floor: re-classify the EFFECTIVE command after commandPrefix/spawnHook.\n\t\t\t// Skipped entirely in YOLO mode (env contract in isCommandSafetyDisabled).\n\t\t\tif (!isCommandSafetyDisabled()) {\n\t\t\t\tconst effectiveVerdict = classifyShellCommand(spawnContext.command);\n\t\t\t\tif (effectiveVerdict.risk === \"block\") {\n\t\t\t\t\tthrow new Error(`command-safety: blocked\\n[${effectiveVerdict.rule}] ${effectiveVerdict.reason}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst output = new OutputAccumulator({ tempFilePrefix: \"omk-bash\" });\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\toutput.append(data);\n\t\t\t\tscheduleOutputUpdate();\n\t\t\t};\n\n\t\t\tconst finishOutput = async () => {\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\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} 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\tif (err instanceof Error && err.message === \"aborted\") {\n\t\t\t\t\t\tthrow new Error(appendStatus(text, \"Command aborted\"));\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(\":\");\n\t\t\t\t\t\tthrow new Error(appendStatus(text, `Command timed out after ${timeoutSecs} seconds`));\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\tif (exitCode !== 0 && exitCode !== null) {\n\t\t\t\t\tthrow new Error(appendStatus(outputText, `Command exited with code ${exitCode}`));\n\t\t\t\t}\n\t\t\t\treturn { content: [{ type: \"text\", text: outputText }], details };\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(formatBashCall(args));\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 Parameters<typeof rebuildBashResultRenderComponent>[1],\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\nexport function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {\n\treturn wrapToolDefinition(createBashToolDefinition(cwd, options));\n}\n"]}
|
package/dist/core/tools/bash.js
CHANGED
|
@@ -8,6 +8,7 @@ import { truncateToVisualLines } from "../../modes/interactive/components/visual
|
|
|
8
8
|
import { theme } from "../../modes/interactive/theme/theme.js";
|
|
9
9
|
import { waitForChildProcess } from "../../utils/child-process.js";
|
|
10
10
|
import { getShellConfig, getShellEnv, killProcessTree, trackDetachedChildPid, untrackDetachedChildPid, } from "../../utils/shell.js";
|
|
11
|
+
import { DEFAULT_BUILTIN_TOOL_TIMEOUTS } from "../agent-tool-settings.js";
|
|
11
12
|
import { classifyShellCommand } from "../command-safety.js";
|
|
12
13
|
import { isCommandSafetyDisabled } from "../extensions/builtin/command-safety-gate.js";
|
|
13
14
|
import { assertLoadoutAccess } from "../loadout-access-policy.js";
|
|
@@ -17,9 +18,11 @@ import { OutputAccumulator } from "./output-accumulator.js";
|
|
|
17
18
|
import { getTextOutput, invalidArgText, str } from "./render-utils.js";
|
|
18
19
|
import { wrapToolDefinition } from "./tool-definition-wrapper.js";
|
|
19
20
|
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize } from "./truncate.js";
|
|
21
|
+
const BASH_TIMEOUT_DESCRIPTION = `Timeout in seconds. Defaults to ${DEFAULT_BUILTIN_TOOL_TIMEOUTS.bash / 1000}s and the command is terminated at that bound, so raise it for long work such as large downloads, builds, or training runs.`;
|
|
20
22
|
const bashSchema = Type.Object({
|
|
21
23
|
command: Type.String({ description: "Bash command to execute" }),
|
|
22
|
-
|
|
24
|
+
// The stated default must track the runtime one; see bash-timeout-disclosure.test.ts.
|
|
25
|
+
timeout: Type.Optional(Type.Number({ description: BASH_TIMEOUT_DESCRIPTION })),
|
|
23
26
|
});
|
|
24
27
|
export function createLocalBashOperations(options) {
|
|
25
28
|
const sandbox = options?.sandboxPolicy;
|
|
@@ -156,9 +159,6 @@ class BashResultRenderComponent extends Container {
|
|
|
156
159
|
cachedSkipped: undefined,
|
|
157
160
|
};
|
|
158
161
|
}
|
|
159
|
-
function formatDuration(ms) {
|
|
160
|
-
return `${(ms / 1000).toFixed(1)}s`;
|
|
161
|
-
}
|
|
162
162
|
function formatBashCall(args) {
|
|
163
163
|
const command = str(args?.command);
|
|
164
164
|
const timeout = args?.timeout;
|
|
@@ -226,9 +226,9 @@ function rebuildBashResultRenderComponent(component, result, options, showImages
|
|
|
226
226
|
component.addChild(new Text(`\n${theme.fg("warning", `[${warnings.join(". ")}]`)}`, 0, 0));
|
|
227
227
|
}
|
|
228
228
|
if (startedAt !== undefined) {
|
|
229
|
-
const label = options.isPartial ? "Elapsed" : "Took";
|
|
230
229
|
const endTime = endedAt ?? Date.now();
|
|
231
|
-
|
|
230
|
+
const timing = `${options.isPartial ? "Elapsed" : "Took"} ${((endTime - startedAt) / 1000).toFixed(1)}s`;
|
|
231
|
+
component.addChild(new Text(`\n${theme.fg("muted", timing)}`, 0, 0));
|
|
232
232
|
}
|
|
233
233
|
}
|
|
234
234
|
export function createBashToolDefinition(cwd, options) {
|
|
@@ -356,7 +356,7 @@ export function createBashToolDefinition(cwd, options) {
|
|
|
356
356
|
throw new Error(appendStatus(text, "Command aborted"));
|
|
357
357
|
}
|
|
358
358
|
if (err instanceof Error && err.message.startsWith("timeout:")) {
|
|
359
|
-
const timeoutSecs = err.message.split(":")
|
|
359
|
+
const [, timeoutSecs] = err.message.split(":");
|
|
360
360
|
throw new Error(appendStatus(text, `Command timed out after ${timeoutSecs} seconds`));
|
|
361
361
|
}
|
|
362
362
|
throw err;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bash.js","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { constants } from \"node:fs\";\nimport { access as fsAccess } from \"node:fs/promises\";\nimport type { AgentTool } from \"omk-agent-core\";\nimport { Container, Text, truncateToWidth } from \"omk-tui\";\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\ttrackDetachedChildPid,\n\tuntrackDetachedChildPid,\n} from \"../../utils/shell.ts\";\nimport { classifyShellCommand } from \"../command-safety.ts\";\nimport { isCommandSafetyDisabled } from \"../extensions/builtin/command-safety-gate.ts\";\nimport type { ExtensionContext, ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport { assertLoadoutAccess, type LoadoutAccessGuard } from \"../loadout-access-policy.ts\";\nimport { detectSandboxBackend } from \"../sandbox/backend.ts\";\nimport type { SandboxBackendStatus, SandboxPathResolver, SandboxPolicy } from \"../sandbox/policy.ts\";\nimport { buildSandboxedSpawnRequest, type SandboxedSpawnRequest } from \"../sandbox/spawn.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 bashSchema = Type.Object({\n\tcommand: Type.String({ description: \"Bash command to execute\" }),\n\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n});\n\nexport type BashToolInput = Static<typeof bashSchema>;\n\nexport interface BashToolDetails {\n\ttruncation?: TruncationResult;\n\tfullOutputPath?: string;\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 }>;\n}\n\n/**\n * Create bash operations using OMK's built-in local shell execution backend.\n *\n * This is useful for extensions that intercept user_bash and still want OMK's\n * standard local shell behavior while wrapping, sandboxing, or rewriting commands.\n */\nexport interface LocalBashOperationsOptions {\n\treadonly shellPath?: string;\n\treadonly sandboxPolicy?: BashSandboxPreflight;\n\t/** Override backend probing for embedders and deterministic tests. */\n\treadonly detectSandboxBackend?: () => SandboxBackendStatus;\n}\n\nexport function createLocalBashOperations(options?: LocalBashOperationsOptions): BashOperations {\n\tconst sandbox = options?.sandboxPolicy;\n\tlet detectedBackend: SandboxBackendStatus | undefined;\n\tconst resolveBackend = (): SandboxBackendStatus => {\n\t\tif (sandbox?.backend) return sandbox.backend;\n\t\tdetectedBackend ??= (options?.detectSandboxBackend ?? defaultBashSandboxBackend)();\n\t\treturn detectedBackend;\n\t};\n\treturn {\n\t\texec: async (command, cwd, { onData, signal, timeout, env }) => {\n\t\t\tconst { shell, args } = getShellConfig(options?.shellPath);\n\t\t\tlet spawnCommand = shell;\n\t\t\tlet spawnArgs = [...args, command];\n\t\t\tlet spawnCwd = cwd;\n\t\t\tlet spawnEnv: NodeJS.ProcessEnv = env ?? getShellEnv();\n\t\t\tif (sandbox) {\n\t\t\t\tconst backend = resolveBackend();\n\t\t\t\tconst request = buildSandboxedSpawnRequest({\n\t\t\t\t\targv: [shell, ...args, command],\n\t\t\t\t\tcwd,\n\t\t\t\t\tenv: spawnEnv,\n\t\t\t\t\tpolicy: sandbox.policy,\n\t\t\t\t\tbackend,\n\t\t\t\t\tresolver: sandbox.resolver,\n\t\t\t\t});\n\t\t\t\tsandbox.onSpawnDecision?.(request);\n\t\t\t\tif (!request.allowed) {\n\t\t\t\t\tthrow new Error(`sandbox: shell denied\\n[${request.rule}] ${request.reason}`);\n\t\t\t\t}\n\t\t\t\tspawnCommand = request.argv[0];\n\t\t\t\tspawnArgs = [...request.argv.slice(1)];\n\t\t\t\tspawnCwd = request.cwd;\n\t\t\t\tspawnEnv = request.env;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait fsAccess(spawnCwd, constants.F_OK);\n\t\t\t} catch {\n\t\t\t\tthrow new Error(`Working directory does not exist: ${spawnCwd}\\nCannot execute bash commands.`);\n\t\t\t}\n\t\t\tif (signal?.aborted) {\n\t\t\t\tthrow new Error(\"aborted\");\n\t\t\t}\n\n\t\t\t// nosemgrep: javascript.lang.security.detect-child-process.detect-child-process -- argv-array spawn; sandbox policy is applied above and shell mode is not used.\n\t\t\tconst child = spawn(spawnCommand, spawnArgs, {\n\t\t\t\tcwd: spawnCwd,\n\t\t\t\tdetached: process.platform !== \"win32\",\n\t\t\t\tenv: spawnEnv,\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\twindowsHide: true,\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 (timeout !== undefined && timeout > 0) {\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}, timeout * 1000);\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 { exitCode };\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};\n}\n\n/**\n * Sandbox preflight inputs for local bash operations. When provided to\n * {@link createLocalBashOperations}, every spawn is gated by the sandbox spawn\n * builder: shell is denied when an OS sandbox backend is missing under enforce\n * mode, the environment is filtered, and available backends wrap the spawned\n * command. When omitted, local bash behavior is unchanged.\n */\nexport interface BashSandboxPreflight {\n\t/** Policy that decides whether this spawn may proceed. */\n\tpolicy: SandboxPolicy;\n\t/**\n\t * Availability of an OS-level sandbox backend. Local operations auto-detect\n\t * macOS sandbox-exec or Linux bubblewrap when this is omitted.\n\t */\n\tbackend?: SandboxBackendStatus;\n\t/** Optional resolver used to canonicalize the working directory before the root check. */\n\tresolver?: SandboxPathResolver;\n\t/** Observer for each sandbox spawn decision (audit/enforce ledger recording). */\n\tonSpawnDecision?: (decision: SandboxedSpawnRequest) => void;\n}\n\nfunction defaultBashSandboxBackend(): SandboxBackendStatus {\n\treturn detectSandboxBackend();\n}\n\nexport interface BashSpawnContext {\n\tcommand: string;\n\tcwd: string;\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport type BashSpawnHook = (context: BashSpawnContext) => BashSpawnContext;\n\nconst SESSION_ENV_KEYS = [\"PI_SESSION_ID\", \"PI_SESSION_FILE\", \"PI_PROVIDER\", \"PI_MODEL\", \"PI_REASONING_LEVEL\"] as const;\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\t// Anti-spoof: never inherit PI_* session metadata from the parent environment.\n\tfor (const key of SESSION_ENV_KEYS) delete env[key];\n\tif (exposeSessionEnvironment && ctx?.sessionManager) {\n\t\tconst model = ctx.model;\n\t\tenv.PI_SESSION_ID = ctx.sessionManager.getSessionId();\n\t\tconst sessionFile = ctx.sessionManager.getSessionFile();\n\t\tif (sessionFile) env.PI_SESSION_FILE = sessionFile;\n\t\tif (model) {\n\t\t\tenv.PI_PROVIDER = model.provider;\n\t\t\tenv.PI_MODEL = model.id;\n\t\t}\n\t\tif (ctx.thinkingLevel) env.PI_REASONING_LEVEL = ctx.thinkingLevel;\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 session metadata as PI_* environment variables. Default: true */\n\texposeSessionEnvironment?: boolean;\n\t/** Trusted sandbox policy for local shell execution */\n\tsandboxPolicy?: BashSandboxPreflight;\n\tloadoutAccessGuard?: LoadoutAccessGuard;\n\t/** Hook to adjust command, cwd, or env before execution */\n\tspawnHook?: BashSpawnHook;\n}\n\nconst BASH_PREVIEW_LINES = 5;\nconst BASH_UPDATE_THROTTLE_MS = 100;\n\ntype 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 formatBashCall(args: { command?: string; timeout?: number } | undefined): string {\n\tconst command = str(args?.command);\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(`$ ${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\")})`;\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 function createBashToolDefinition(\n\tcwd: string,\n\toptions?: BashToolOptions,\n): ToolDefinition<typeof bashSchema, BashToolDetails | undefined, BashRenderState> {\n\tconst ops =\n\t\toptions?.operations ??\n\t\tcreateLocalBashOperations({ shellPath: options?.shellPath, sandboxPolicy: options?.sandboxPolicy });\n\tconst commandPrefix = options?.commandPrefix;\n\tconst exposeSessionEnvironment = options?.exposeSessionEnvironment ?? true;\n\tconst spawnHook = options?.spawnHook;\n\treturn {\n\t\tname: \"bash\",\n\t\tlabel: \"bash\",\n\t\tdescription: `Execute a bash 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: \"Execute bash commands (ls, grep, find, etc.)\",\n\t\tpromptGuidelines: exposeSessionEnvironment\n\t\t\t? [\"Inspect PI_* environment variables for current model and session details.\"]\n\t\t\t: undefined,\n\t\tparameters: bashSchema,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\t{ command, timeout }: { command: string; timeout?: number },\n\t\t\tsignal?: AbortSignal,\n\t\t\tonUpdate?,\n\t\t\tctx?,\n\t\t) {\n\t\t\tconst resolvedCommand = commandPrefix ? `${commandPrefix}\\n${command}` : command;\n\t\t\tconst spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook, exposeSessionEnvironment, ctx);\n\t\t\tassertLoadoutAccess(options?.loadoutAccessGuard, {\n\t\t\t\toperation: \"execute\",\n\t\t\t\ttoolName: \"bash\",\n\t\t\t\tcommand: spawnContext.command,\n\t\t\t});\n\n\t\t\t// Safety floor: re-classify the EFFECTIVE command after commandPrefix/spawnHook.\n\t\t\t// Skipped entirely in YOLO mode (env contract in isCommandSafetyDisabled).\n\t\t\tif (!isCommandSafetyDisabled()) {\n\t\t\t\tconst effectiveVerdict = classifyShellCommand(spawnContext.command);\n\t\t\t\tif (effectiveVerdict.risk === \"block\") {\n\t\t\t\t\tthrow new Error(`command-safety: blocked\\n[${effectiveVerdict.rule}] ${effectiveVerdict.reason}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst output = new OutputAccumulator({ tempFilePrefix: \"omk-bash\" });\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\toutput.append(data);\n\t\t\t\tscheduleOutputUpdate();\n\t\t\t};\n\n\t\t\tconst finishOutput = async () => {\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\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} 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\tif (err instanceof Error && err.message === \"aborted\") {\n\t\t\t\t\t\tthrow new Error(appendStatus(text, \"Command aborted\"));\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 Error(appendStatus(text, `Command timed out after ${timeoutSecs} seconds`));\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\tif (exitCode !== 0 && exitCode !== null) {\n\t\t\t\t\tthrow new Error(appendStatus(outputText, `Command exited with code ${exitCode}`));\n\t\t\t\t}\n\t\t\t\treturn { content: [{ type: \"text\", text: outputText }], details };\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(formatBashCall(args));\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\nexport function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {\n\treturn wrapToolDefinition(createBashToolDefinition(cwd, options));\n}\n"]}
|
|
1
|
+
{"version":3,"file":"bash.js","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"node:child_process\";\nimport { constants } from \"node:fs\";\nimport { access as fsAccess } from \"node:fs/promises\";\nimport type { AgentTool } from \"omk-agent-core\";\nimport { Container, Text, truncateToWidth } from \"omk-tui\";\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\ttrackDetachedChildPid,\n\tuntrackDetachedChildPid,\n} from \"../../utils/shell.ts\";\nimport { DEFAULT_BUILTIN_TOOL_TIMEOUTS } from \"../agent-tool-settings.ts\";\nimport { classifyShellCommand } from \"../command-safety.ts\";\nimport { isCommandSafetyDisabled } from \"../extensions/builtin/command-safety-gate.ts\";\nimport type { ExtensionContext, ToolDefinition, ToolRenderResultOptions } from \"../extensions/types.ts\";\nimport { assertLoadoutAccess, type LoadoutAccessGuard } from \"../loadout-access-policy.ts\";\nimport { detectSandboxBackend } from \"../sandbox/backend.ts\";\nimport type { SandboxBackendStatus, SandboxPathResolver, SandboxPolicy } from \"../sandbox/policy.ts\";\nimport { buildSandboxedSpawnRequest, type SandboxedSpawnRequest } from \"../sandbox/spawn.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 BASH_TIMEOUT_DESCRIPTION = `Timeout in seconds. Defaults to ${DEFAULT_BUILTIN_TOOL_TIMEOUTS.bash / 1000}s and the command is terminated at that bound, so raise it for long work such as large downloads, builds, or training runs.`;\n\nconst bashSchema = Type.Object({\n\tcommand: Type.String({ description: \"Bash command to execute\" }),\n\t// The stated default must track the runtime one; see bash-timeout-disclosure.test.ts.\n\ttimeout: Type.Optional(Type.Number({ description: BASH_TIMEOUT_DESCRIPTION })),\n});\n\nexport type BashToolInput = Static<typeof bashSchema>;\n\nexport interface BashToolDetails {\n\ttruncation?: TruncationResult;\n\tfullOutputPath?: string;\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 }>;\n}\n\n/**\n * Create bash operations using OMK's built-in local shell execution backend.\n *\n * This is useful for extensions that intercept user_bash and still want OMK's\n * standard local shell behavior while wrapping, sandboxing, or rewriting commands.\n */\nexport interface LocalBashOperationsOptions {\n\treadonly shellPath?: string;\n\treadonly sandboxPolicy?: BashSandboxPreflight;\n\t/** Override backend probing for embedders and deterministic tests. */\n\treadonly detectSandboxBackend?: () => SandboxBackendStatus;\n}\n\nexport function createLocalBashOperations(options?: LocalBashOperationsOptions): BashOperations {\n\tconst sandbox = options?.sandboxPolicy;\n\tlet detectedBackend: SandboxBackendStatus | undefined;\n\tconst resolveBackend = (): SandboxBackendStatus => {\n\t\tif (sandbox?.backend) return sandbox.backend;\n\t\tdetectedBackend ??= (options?.detectSandboxBackend ?? defaultBashSandboxBackend)();\n\t\treturn detectedBackend;\n\t};\n\treturn {\n\t\texec: async (command, cwd, { onData, signal, timeout, env }) => {\n\t\t\tconst { shell, args } = getShellConfig(options?.shellPath);\n\t\t\tlet spawnCommand = shell;\n\t\t\tlet spawnArgs = [...args, command];\n\t\t\tlet spawnCwd = cwd;\n\t\t\tlet spawnEnv: NodeJS.ProcessEnv = env ?? getShellEnv();\n\t\t\tif (sandbox) {\n\t\t\t\tconst backend = resolveBackend();\n\t\t\t\tconst request = buildSandboxedSpawnRequest({\n\t\t\t\t\targv: [shell, ...args, command],\n\t\t\t\t\tcwd,\n\t\t\t\t\tenv: spawnEnv,\n\t\t\t\t\tpolicy: sandbox.policy,\n\t\t\t\t\tbackend,\n\t\t\t\t\tresolver: sandbox.resolver,\n\t\t\t\t});\n\t\t\t\tsandbox.onSpawnDecision?.(request);\n\t\t\t\tif (!request.allowed) {\n\t\t\t\t\tthrow new Error(`sandbox: shell denied\\n[${request.rule}] ${request.reason}`);\n\t\t\t\t}\n\t\t\t\tspawnCommand = request.argv[0];\n\t\t\t\tspawnArgs = [...request.argv.slice(1)];\n\t\t\t\tspawnCwd = request.cwd;\n\t\t\t\tspawnEnv = request.env;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait fsAccess(spawnCwd, constants.F_OK);\n\t\t\t} catch {\n\t\t\t\tthrow new Error(`Working directory does not exist: ${spawnCwd}\\nCannot execute bash commands.`);\n\t\t\t}\n\t\t\tif (signal?.aborted) {\n\t\t\t\tthrow new Error(\"aborted\");\n\t\t\t}\n\n\t\t\t// nosemgrep: javascript.lang.security.detect-child-process.detect-child-process -- argv-array spawn; sandbox policy is applied above and shell mode is not used.\n\t\t\tconst child = spawn(spawnCommand, spawnArgs, {\n\t\t\t\tcwd: spawnCwd,\n\t\t\t\tdetached: process.platform !== \"win32\",\n\t\t\t\tenv: spawnEnv,\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t\twindowsHide: true,\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 (timeout !== undefined && timeout > 0) {\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}, timeout * 1000);\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 { exitCode };\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};\n}\n\n/**\n * Sandbox preflight inputs for local bash operations. When provided to\n * {@link createLocalBashOperations}, every spawn is gated by the sandbox spawn\n * builder: shell is denied when an OS sandbox backend is missing under enforce\n * mode, the environment is filtered, and available backends wrap the spawned\n * command. When omitted, local bash behavior is unchanged.\n */\nexport interface BashSandboxPreflight {\n\t/** Policy that decides whether this spawn may proceed. */\n\tpolicy: SandboxPolicy;\n\t/**\n\t * Availability of an OS-level sandbox backend. Local operations auto-detect\n\t * macOS sandbox-exec or Linux bubblewrap when this is omitted.\n\t */\n\tbackend?: SandboxBackendStatus;\n\t/** Optional resolver used to canonicalize the working directory before the root check. */\n\tresolver?: SandboxPathResolver;\n\t/** Observer for each sandbox spawn decision (audit/enforce ledger recording). */\n\tonSpawnDecision?: (decision: SandboxedSpawnRequest) => void;\n}\n\nfunction defaultBashSandboxBackend(): SandboxBackendStatus {\n\treturn detectSandboxBackend();\n}\n\nexport interface BashSpawnContext {\n\tcommand: string;\n\tcwd: string;\n\tenv: NodeJS.ProcessEnv;\n}\n\nexport type BashSpawnHook = (context: BashSpawnContext) => BashSpawnContext;\n\nconst SESSION_ENV_KEYS = [\"PI_SESSION_ID\", \"PI_SESSION_FILE\", \"PI_PROVIDER\", \"PI_MODEL\", \"PI_REASONING_LEVEL\"] as const;\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\t// Anti-spoof: never inherit PI_* session metadata from the parent environment.\n\tfor (const key of SESSION_ENV_KEYS) delete env[key];\n\tif (exposeSessionEnvironment && ctx?.sessionManager) {\n\t\tconst model = ctx.model;\n\t\tenv.PI_SESSION_ID = ctx.sessionManager.getSessionId();\n\t\tconst sessionFile = ctx.sessionManager.getSessionFile();\n\t\tif (sessionFile) env.PI_SESSION_FILE = sessionFile;\n\t\tif (model) {\n\t\t\tenv.PI_PROVIDER = model.provider;\n\t\t\tenv.PI_MODEL = model.id;\n\t\t}\n\t\tif (ctx.thinkingLevel) env.PI_REASONING_LEVEL = ctx.thinkingLevel;\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 session metadata as PI_* environment variables. Default: true */\n\texposeSessionEnvironment?: boolean;\n\t/** Trusted sandbox policy for local shell execution */\n\tsandboxPolicy?: BashSandboxPreflight;\n\tloadoutAccessGuard?: LoadoutAccessGuard;\n\t/** Hook to adjust command, cwd, or env before execution */\n\tspawnHook?: BashSpawnHook;\n}\n\nconst BASH_PREVIEW_LINES = 5;\nconst BASH_UPDATE_THROTTLE_MS = 100;\n\ntype 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 formatBashCall(args: { command?: string; timeout?: number } | undefined): string {\n\tconst command = str(args?.command);\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(`$ ${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, 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\")})`;\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 endTime = endedAt ?? Date.now();\n\t\tconst timing = `${options.isPartial ? \"Elapsed\" : \"Took\"} ${((endTime - startedAt) / 1000).toFixed(1)}s`;\n\t\tcomponent.addChild(new Text(`\\n${theme.fg(\"muted\", timing)}`, 0, 0));\n\t}\n}\n\nexport function createBashToolDefinition(\n\tcwd: string,\n\toptions?: BashToolOptions,\n): ToolDefinition<typeof bashSchema, BashToolDetails | undefined, BashRenderState> {\n\tconst ops =\n\t\toptions?.operations ??\n\t\tcreateLocalBashOperations({ shellPath: options?.shellPath, sandboxPolicy: options?.sandboxPolicy });\n\tconst commandPrefix = options?.commandPrefix;\n\tconst exposeSessionEnvironment = options?.exposeSessionEnvironment ?? true;\n\tconst spawnHook = options?.spawnHook;\n\treturn {\n\t\tname: \"bash\",\n\t\tlabel: \"bash\",\n\t\tdescription: `Execute a bash 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: \"Execute bash commands (ls, grep, find, etc.)\",\n\t\tpromptGuidelines: exposeSessionEnvironment\n\t\t\t? [\"Inspect PI_* environment variables for current model and session details.\"]\n\t\t\t: undefined,\n\t\tparameters: bashSchema,\n\t\tasync execute(\n\t\t\t_toolCallId,\n\t\t\t{ command, timeout }: { command: string; timeout?: number },\n\t\t\tsignal?: AbortSignal,\n\t\t\tonUpdate?,\n\t\t\tctx?,\n\t\t) {\n\t\t\tconst resolvedCommand = commandPrefix ? `${commandPrefix}\\n${command}` : command;\n\t\t\tconst spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook, exposeSessionEnvironment, ctx);\n\t\t\tassertLoadoutAccess(options?.loadoutAccessGuard, {\n\t\t\t\toperation: \"execute\",\n\t\t\t\ttoolName: \"bash\",\n\t\t\t\tcommand: spawnContext.command,\n\t\t\t});\n\n\t\t\t// Safety floor: re-classify the EFFECTIVE command after commandPrefix/spawnHook.\n\t\t\t// Skipped entirely in YOLO mode (env contract in isCommandSafetyDisabled).\n\t\t\tif (!isCommandSafetyDisabled()) {\n\t\t\t\tconst effectiveVerdict = classifyShellCommand(spawnContext.command);\n\t\t\t\tif (effectiveVerdict.risk === \"block\") {\n\t\t\t\t\tthrow new Error(`command-safety: blocked\\n[${effectiveVerdict.rule}] ${effectiveVerdict.reason}`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst output = new OutputAccumulator({ tempFilePrefix: \"omk-bash\" });\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\toutput.append(data);\n\t\t\t\tscheduleOutputUpdate();\n\t\t\t};\n\n\t\t\tconst finishOutput = async () => {\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\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} 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\tif (err instanceof Error && err.message === \"aborted\") {\n\t\t\t\t\t\tthrow new Error(appendStatus(text, \"Command aborted\"));\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(\":\");\n\t\t\t\t\t\tthrow new Error(appendStatus(text, `Command timed out after ${timeoutSecs} seconds`));\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\tif (exitCode !== 0 && exitCode !== null) {\n\t\t\t\t\tthrow new Error(appendStatus(outputText, `Command exited with code ${exitCode}`));\n\t\t\t\t}\n\t\t\t\treturn { content: [{ type: \"text\", text: outputText }], details };\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(formatBashCall(args));\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 Parameters<typeof rebuildBashResultRenderComponent>[1],\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\nexport function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {\n\treturn wrapToolDefinition(createBashToolDefinition(cwd, options));\n}\n"]}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { scanShellComplexity } from "./workload-shell-scan.ts";
|
|
2
|
+
import type { WorkloadClass, WorkloadClassification } from "./workload-types.ts";
|
|
2
3
|
export { scanShellComplexity };
|
|
4
|
+
export type { WorkloadClass, WorkloadClassification };
|
|
3
5
|
/**
|
|
4
6
|
* Pure workload classifier (OMK v0.97.x roadmap §9, M3/PR4).
|
|
5
7
|
*
|
|
@@ -17,16 +19,6 @@ export { scanShellComplexity };
|
|
|
17
19
|
* Classification is advisory for scheduling only — command safety remains
|
|
18
20
|
* the sole authority for whether a command may run at all (§9.4).
|
|
19
21
|
*/
|
|
20
|
-
export type WorkloadClass = "light" | "io" | "cpu" | "memory" | "heavy" | "unknown";
|
|
21
|
-
export type WorkloadCommandFamily = "node-test" | "node-build" | "typescript" | "go-test" | "rust-build" | "container-build" | "monorepo" | "archive" | "generic-process" | "unknown";
|
|
22
|
-
export type WorkloadShellComplexity = "simple-argv" | "complex-shell";
|
|
23
|
-
export interface WorkloadClassification {
|
|
24
|
-
readonly workloadClass: WorkloadClass;
|
|
25
|
-
readonly commandFamily: WorkloadCommandFamily;
|
|
26
|
-
readonly complexity: WorkloadShellComplexity;
|
|
27
|
-
readonly safeToAutoShard: boolean;
|
|
28
|
-
readonly reasonCodes: readonly string[];
|
|
29
|
-
}
|
|
30
22
|
/** Classify one shell command. Pure, deterministic, never throws. */
|
|
31
23
|
export declare function classifyWorkloadCommand(command: string): WorkloadClassification;
|
|
32
24
|
//# sourceMappingURL=workload-classifier.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workload-classifier.d.ts","sourceRoot":"","sources":["../../src/core/workload-classifier.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"workload-classifier.d.ts","sourceRoot":"","sources":["../../src/core/workload-classifier.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAEjF,OAAO,EAAE,mBAAmB,EAAE,CAAC;AAK/B,YAAY,EAAE,aAAa,EAAE,sBAAsB,EAAE,CAAC;AAEtD;;;;;;;;;;;;;;;;GAgBG;AAEH,qEAAqE;AACrE,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,sBAAsB,CAY/E","sourcesContent":["import { analyzeShellCommandV2 } from \"./command-safety-parser.ts\";\nimport { matchWorkloadFamily } from \"./workload-family-matcher.ts\";\nimport { scanShellComplexity } from \"./workload-shell-scan.ts\";\nimport type { WorkloadClass, WorkloadClassification } from \"./workload-types.ts\";\n\nexport { scanShellComplexity };\n\n// The vocabulary lives in `workload-types.ts` so the family matcher can share\n// it without importing this module back. Re-exported here because callers\n// already import these names from the classifier.\nexport type { WorkloadClass, WorkloadClassification };\n\n/**\n * Pure workload classifier (OMK v0.97.x roadmap §9, M3/PR4).\n *\n * Maps one shell command string to an immutable {@link WorkloadClassification}\n * consumed by the resource safety gate. Never throws and performs no I/O.\n *\n * Shell complexity (§9.3) uses a dedicated quote-aware scanner over the raw\n * string: the command-safety parser's coarse tokens intentionally keep\n * unspaced operators inside words (`a|b`) and strip quotes, so token\n * inspection cannot distinguish `'x<y'` from `x<y`. The §9.3 contract\n * (\"quoted literal 내부 token은 shell parser가 구분\") therefore needs this\n * scanner; argv/family detection still reuses the safety parser so both\n * gates see the same command structure (§22.2).\n *\n * Classification is advisory for scheduling only — command safety remains\n * the sole authority for whether a command may run at all (§9.4).\n */\n\n/** Classify one shell command. Pure, deterministic, never throws. */\nexport function classifyWorkloadCommand(command: string): WorkloadClassification {\n\ttry {\n\t\treturn classifyInternal(command);\n\t} catch {\n\t\treturn {\n\t\t\tworkloadClass: \"unknown\",\n\t\t\tcommandFamily: \"unknown\",\n\t\t\tcomplexity: \"complex-shell\",\n\t\t\tsafeToAutoShard: false,\n\t\t\treasonCodes: [\"classifier.parse.failed\"],\n\t\t};\n\t}\n}\n\nfunction classifyInternal(command: string): WorkloadClassification {\n\tconst complexityScan = scanShellComplexity(command);\n\tif (complexityScan.reasons.length > 0) {\n\t\t// §9.3/§12.3: complex shell is never rewritten or auto-sharded.\n\t\treturn {\n\t\t\tworkloadClass: \"unknown\",\n\t\t\tcommandFamily: \"unknown\",\n\t\t\tcomplexity: \"complex-shell\",\n\t\t\tsafeToAutoShard: false,\n\t\t\treasonCodes: complexityScan.reasons,\n\t\t};\n\t}\n\n\tconst argv = extractSimpleArgv(command);\n\tif (argv.length === 0) {\n\t\treturn {\n\t\t\tworkloadClass: \"light\",\n\t\t\tcommandFamily: \"unknown\",\n\t\t\tcomplexity: \"simple-argv\",\n\t\t\tsafeToAutoShard: false,\n\t\t\treasonCodes: [\"command.empty\"],\n\t\t};\n\t}\n\treturn matchWorkloadFamily(argv);\n}\n\nconst ENV_ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=/;\n\nfunction extractSimpleArgv(command: string): readonly string[] {\n\tconst analysis = analyzeShellCommandV2(command);\n\tconst tokens = analysis.segments[0]?.tokens ?? [];\n\t// Skip leading VAR=value prefixes (§9.2 examples run under env prefixes).\n\tlet start = 0;\n\twhile (start < tokens.length && ENV_ASSIGNMENT.test(tokens[start])) {\n\t\tstart += 1;\n\t}\n\tlet argv = tokens.slice(start);\n\t// Unwrap one runner layer so `npx vitest` classifies as vitest.\n\twhile (argv.length > 1 && (argv[0] === \"npx\" || argv[0] === \"bunx\")) {\n\t\targv = argv.slice(1);\n\t\twhile (argv.length > 0 && argv[0].startsWith(\"-\")) {\n\t\t\targv = argv.slice(1);\n\t\t}\n\t}\n\tif (argv.length > 2 && (argv[0] === \"pnpm\" || argv[0] === \"npm\" || argv[0] === \"yarn\") && argv[1] === \"exec\") {\n\t\targv = argv.slice(2);\n\t}\n\treturn argv;\n}\n"]}
|
|
@@ -2,6 +2,23 @@ import { analyzeShellCommandV2 } from "./command-safety-parser.js";
|
|
|
2
2
|
import { matchWorkloadFamily } from "./workload-family-matcher.js";
|
|
3
3
|
import { scanShellComplexity } from "./workload-shell-scan.js";
|
|
4
4
|
export { scanShellComplexity };
|
|
5
|
+
/**
|
|
6
|
+
* Pure workload classifier (OMK v0.97.x roadmap §9, M3/PR4).
|
|
7
|
+
*
|
|
8
|
+
* Maps one shell command string to an immutable {@link WorkloadClassification}
|
|
9
|
+
* consumed by the resource safety gate. Never throws and performs no I/O.
|
|
10
|
+
*
|
|
11
|
+
* Shell complexity (§9.3) uses a dedicated quote-aware scanner over the raw
|
|
12
|
+
* string: the command-safety parser's coarse tokens intentionally keep
|
|
13
|
+
* unspaced operators inside words (`a|b`) and strip quotes, so token
|
|
14
|
+
* inspection cannot distinguish `'x<y'` from `x<y`. The §9.3 contract
|
|
15
|
+
* ("quoted literal 내부 token은 shell parser가 구분") therefore needs this
|
|
16
|
+
* scanner; argv/family detection still reuses the safety parser so both
|
|
17
|
+
* gates see the same command structure (§22.2).
|
|
18
|
+
*
|
|
19
|
+
* Classification is advisory for scheduling only — command safety remains
|
|
20
|
+
* the sole authority for whether a command may run at all (§9.4).
|
|
21
|
+
*/
|
|
5
22
|
/** Classify one shell command. Pure, deterministic, never throws. */
|
|
6
23
|
export function classifyWorkloadCommand(command) {
|
|
7
24
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workload-classifier.js","sourceRoot":"","sources":["../../src/core/workload-classifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"workload-classifier.js","sourceRoot":"","sources":["../../src/core/workload-classifier.ts"],"names":[],"mappings":"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","sourcesContent":["import { analyzeShellCommandV2 } from \"./command-safety-parser.ts\";\nimport { matchWorkloadFamily } from \"./workload-family-matcher.ts\";\nimport { scanShellComplexity } from \"./workload-shell-scan.ts\";\nimport type { WorkloadClass, WorkloadClassification } from \"./workload-types.ts\";\n\nexport { scanShellComplexity };\n\n// The vocabulary lives in `workload-types.ts` so the family matcher can share\n// it without importing this module back. Re-exported here because callers\n// already import these names from the classifier.\nexport type { WorkloadClass, WorkloadClassification };\n\n/**\n * Pure workload classifier (OMK v0.97.x roadmap §9, M3/PR4).\n *\n * Maps one shell command string to an immutable {@link WorkloadClassification}\n * consumed by the resource safety gate. Never throws and performs no I/O.\n *\n * Shell complexity (§9.3) uses a dedicated quote-aware scanner over the raw\n * string: the command-safety parser's coarse tokens intentionally keep\n * unspaced operators inside words (`a|b`) and strip quotes, so token\n * inspection cannot distinguish `'x<y'` from `x<y`. The §9.3 contract\n * (\"quoted literal 내부 token은 shell parser가 구분\") therefore needs this\n * scanner; argv/family detection still reuses the safety parser so both\n * gates see the same command structure (§22.2).\n *\n * Classification is advisory for scheduling only — command safety remains\n * the sole authority for whether a command may run at all (§9.4).\n */\n\n/** Classify one shell command. Pure, deterministic, never throws. */\nexport function classifyWorkloadCommand(command: string): WorkloadClassification {\n\ttry {\n\t\treturn classifyInternal(command);\n\t} catch {\n\t\treturn {\n\t\t\tworkloadClass: \"unknown\",\n\t\t\tcommandFamily: \"unknown\",\n\t\t\tcomplexity: \"complex-shell\",\n\t\t\tsafeToAutoShard: false,\n\t\t\treasonCodes: [\"classifier.parse.failed\"],\n\t\t};\n\t}\n}\n\nfunction classifyInternal(command: string): WorkloadClassification {\n\tconst complexityScan = scanShellComplexity(command);\n\tif (complexityScan.reasons.length > 0) {\n\t\t// §9.3/§12.3: complex shell is never rewritten or auto-sharded.\n\t\treturn {\n\t\t\tworkloadClass: \"unknown\",\n\t\t\tcommandFamily: \"unknown\",\n\t\t\tcomplexity: \"complex-shell\",\n\t\t\tsafeToAutoShard: false,\n\t\t\treasonCodes: complexityScan.reasons,\n\t\t};\n\t}\n\n\tconst argv = extractSimpleArgv(command);\n\tif (argv.length === 0) {\n\t\treturn {\n\t\t\tworkloadClass: \"light\",\n\t\t\tcommandFamily: \"unknown\",\n\t\t\tcomplexity: \"simple-argv\",\n\t\t\tsafeToAutoShard: false,\n\t\t\treasonCodes: [\"command.empty\"],\n\t\t};\n\t}\n\treturn matchWorkloadFamily(argv);\n}\n\nconst ENV_ASSIGNMENT = /^[A-Za-z_][A-Za-z0-9_]*=/;\n\nfunction extractSimpleArgv(command: string): readonly string[] {\n\tconst analysis = analyzeShellCommandV2(command);\n\tconst tokens = analysis.segments[0]?.tokens ?? [];\n\t// Skip leading VAR=value prefixes (§9.2 examples run under env prefixes).\n\tlet start = 0;\n\twhile (start < tokens.length && ENV_ASSIGNMENT.test(tokens[start])) {\n\t\tstart += 1;\n\t}\n\tlet argv = tokens.slice(start);\n\t// Unwrap one runner layer so `npx vitest` classifies as vitest.\n\twhile (argv.length > 1 && (argv[0] === \"npx\" || argv[0] === \"bunx\")) {\n\t\targv = argv.slice(1);\n\t\twhile (argv.length > 0 && argv[0].startsWith(\"-\")) {\n\t\t\targv = argv.slice(1);\n\t\t}\n\t}\n\tif (argv.length > 2 && (argv[0] === \"pnpm\" || argv[0] === \"npm\" || argv[0] === \"yarn\") && argv[1] === \"exec\") {\n\t\targv = argv.slice(2);\n\t}\n\treturn argv;\n}\n"]}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { WorkloadClassification } from "./workload-
|
|
1
|
+
import type { WorkloadClassification } from "./workload-types.ts";
|
|
2
2
|
export declare function matchWorkloadFamily(argv: readonly string[]): WorkloadClassification;
|
|
3
3
|
//# sourceMappingURL=workload-family-matcher.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workload-family-matcher.d.ts","sourceRoot":"","sources":["../../src/core/workload-family-matcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,sBAAsB,EAAyB,MAAM,
|
|
1
|
+
{"version":3,"file":"workload-family-matcher.d.ts","sourceRoot":"","sources":["../../src/core/workload-family-matcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,sBAAsB,EAAyB,MAAM,qBAAqB,CAAC;AA4CxG,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,sBAAsB,CAQnF","sourcesContent":["import type { WorkloadClass, WorkloadClassification, WorkloadCommandFamily } from \"./workload-types.ts\";\n\nconst LIGHT_EXECUTABLES = new Set([\n\t\"ls\",\n\t\"cat\",\n\t\"head\",\n\t\"tail\",\n\t\"grep\",\n\t\"rg\",\n\t\"find\",\n\t\"echo\",\n\t\"pwd\",\n\t\"which\",\n\t\"wc\",\n\t\"stat\",\n\t\"file\",\n\t\"true\",\n\t\"false\",\n]);\nconst LIGHT_GIT_SUBCOMMANDS = new Set([\"status\", \"log\", \"diff\", \"show\", \"branch\", \"rev-parse\", \"blame\"]);\nconst IO_EXECUTABLES = new Set([\"cp\", \"mv\", \"rsync\", \"mkdir\", \"touch\", \"dd\"]);\nconst CPU_EXECUTABLES = new Set([\"eslint\", \"prettier\", \"biome\", \"ruff\", \"gofmt\", \"rustfmt\"]);\nconst PACKAGE_MANAGERS = new Set([\"npm\", \"pnpm\", \"yarn\", \"bun\"]);\nconst JS_TEST_RUNNERS = new Set([\"vitest\", \"jest\"]);\nconst SHARD_CONFLICT_FLAGS = [\"--shard\", \"--runInBand\", \"--coverage\", \"--watch\", \"-u\", \"--updateSnapshot\", \"-w\"];\n\nfunction classification(\n\tworkloadClass: WorkloadClass,\n\tcommandFamily: WorkloadCommandFamily,\n\toptions?: { readonly safeToAutoShard?: boolean; readonly extraReasons?: readonly string[] },\n): WorkloadClassification {\n\treturn {\n\t\tworkloadClass,\n\t\tcommandFamily,\n\t\tcomplexity: \"simple-argv\",\n\t\tsafeToAutoShard: options?.safeToAutoShard ?? false,\n\t\treasonCodes: [`family.${commandFamily}`, `class.${workloadClass}`, ...(options?.extraReasons ?? [])],\n\t};\n}\n\nfunction hasShardConflictFlag(argv: readonly string[]): boolean {\n\treturn argv.some((token) => SHARD_CONFLICT_FLAGS.some((flag) => token === flag || token.startsWith(`${flag}=`)));\n}\n\nexport function matchWorkloadFamily(argv: readonly string[]): WorkloadClassification {\n\tconst executable = basename(argv[0]);\n\tconst rest = argv.slice(1);\n\treturn (\n\t\tmatchToolchainFamily(executable, rest) ??\n\t\tmatchInfraFamily(executable, rest) ??\n\t\tmatchTierFallback(executable, rest)\n\t);\n}\n\n/** JS/TS/Go/Rust toolchains (§9.2 first block). */\nfunction matchToolchainFamily(executable: string, rest: readonly string[]): WorkloadClassification | null {\n\tif (JS_TEST_RUNNERS.has(executable)) {\n\t\t// §12.2: direct vitest/jest invocations are shard candidates unless a\n\t\t// conflicting flag changes semantics; the M5 planner enforces the rest.\n\t\tconst conflict = hasShardConflictFlag(rest);\n\t\treturn classification(\"heavy\", \"node-test\", {\n\t\t\tsafeToAutoShard: !conflict,\n\t\t\textraReasons: conflict ? [\"shard.flag.conflict\"] : [],\n\t\t});\n\t}\n\tif (PACKAGE_MANAGERS.has(executable)) {\n\t\tconst script = rest[0] === \"run\" ? rest[1] : rest[0];\n\t\tif (script === \"test\" || script === \"t\" || script?.startsWith(\"test\")) {\n\t\t\t// Opaque wrapper: the underlying runner is unknown statically (§12.1).\n\t\t\treturn classification(\"heavy\", \"node-test\", { extraReasons: [\"shard.wrapper.opaque\"] });\n\t\t}\n\t\tif (script === \"build\" || script?.startsWith(\"build\")) {\n\t\t\treturn classification(\"heavy\", \"node-build\");\n\t\t}\n\t\treturn classification(\"unknown\", \"generic-process\");\n\t}\n\tif (executable === \"tsc\") {\n\t\tconst hasProjectFlag = rest.some((token) => [\"-p\", \"--project\", \"-b\", \"--build\"].includes(token));\n\t\tconst hasFileArg = rest.some((token) => !token.startsWith(\"-\"));\n\t\tif (hasProjectFlag || !hasFileArg) {\n\t\t\treturn classification(\"heavy\", \"typescript\");\n\t\t}\n\t\treturn classification(\"cpu\", \"typescript\");\n\t}\n\tif (executable === \"go\" && rest[0] === \"test\") {\n\t\tconst packageArgs = rest.slice(1).filter((token) => !token.startsWith(\"-\"));\n\t\tconst recursive = packageArgs.some((token) => token.endsWith(\"...\"));\n\t\tif (recursive || packageArgs.length >= 2) {\n\t\t\treturn classification(\"heavy\", \"go-test\", { safeToAutoShard: recursive && !rest.includes(\"-run\") });\n\t\t}\n\t\treturn classification(\"cpu\", \"go-test\");\n\t}\n\tif (executable === \"cargo\" && [\"build\", \"test\", \"check\", \"clippy\"].includes(rest[0] ?? \"\")) {\n\t\treturn classification(\"heavy\", \"rust-build\");\n\t}\n\treturn null;\n}\n\n/** Container/monorepo/archive tooling (§9.2 second block). */\nfunction matchInfraFamily(executable: string, rest: readonly string[]): WorkloadClassification | null {\n\tif ((executable === \"docker\" || executable === \"podman\") && rest[0] === \"build\") {\n\t\treturn classification(\"heavy\", \"container-build\");\n\t}\n\tif ((executable === \"nx\" && rest[0] === \"run-many\") || (executable === \"turbo\" && rest[0] === \"run\")) {\n\t\treturn classification(\"heavy\", \"monorepo\");\n\t}\n\tif (executable === \"tar\") {\n\t\tconst flags = rest[0] ?? \"\";\n\t\tif (flags.includes(\"c\") || rest.includes(\"--create\")) {\n\t\t\treturn classification(\"heavy\", \"archive\");\n\t\t}\n\t\treturn classification(\"io\", \"archive\");\n\t}\n\tif ((executable === \"zip\" && rest.includes(\"-r\")) || (executable === \"7z\" && rest[0] === \"a\")) {\n\t\treturn classification(\"heavy\", \"archive\");\n\t}\n\treturn null;\n}\n\n/** Known light/io/cpu tiers, else unknown/generic-process. */\nfunction matchTierFallback(executable: string, rest: readonly string[]): WorkloadClassification {\n\tif (executable === \"git\" && LIGHT_GIT_SUBCOMMANDS.has(rest[0] ?? \"\")) {\n\t\treturn classification(\"light\", \"generic-process\");\n\t}\n\tif (LIGHT_EXECUTABLES.has(executable)) {\n\t\treturn classification(\"light\", \"generic-process\");\n\t}\n\tif (IO_EXECUTABLES.has(executable)) {\n\t\treturn classification(\"io\", \"generic-process\");\n\t}\n\tif (CPU_EXECUTABLES.has(executable)) {\n\t\treturn classification(\"cpu\", \"generic-process\");\n\t}\n\treturn classification(\"unknown\", \"generic-process\");\n}\n\nfunction basename(token: string): string {\n\tconst slash = token.lastIndexOf(\"/\");\n\treturn slash === -1 ? token : token.slice(slash + 1);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workload-family-matcher.js","sourceRoot":"","sources":["../../src/core/workload-family-matcher.ts"],"names":[],"mappings":"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","sourcesContent":["import type { WorkloadClass, WorkloadClassification, WorkloadCommandFamily } from \"./workload-classifier.ts\";\n\nconst LIGHT_EXECUTABLES = new Set([\n\t\"ls\",\n\t\"cat\",\n\t\"head\",\n\t\"tail\",\n\t\"grep\",\n\t\"rg\",\n\t\"find\",\n\t\"echo\",\n\t\"pwd\",\n\t\"which\",\n\t\"wc\",\n\t\"stat\",\n\t\"file\",\n\t\"true\",\n\t\"false\",\n]);\nconst LIGHT_GIT_SUBCOMMANDS = new Set([\"status\", \"log\", \"diff\", \"show\", \"branch\", \"rev-parse\", \"blame\"]);\nconst IO_EXECUTABLES = new Set([\"cp\", \"mv\", \"rsync\", \"mkdir\", \"touch\", \"dd\"]);\nconst CPU_EXECUTABLES = new Set([\"eslint\", \"prettier\", \"biome\", \"ruff\", \"gofmt\", \"rustfmt\"]);\nconst PACKAGE_MANAGERS = new Set([\"npm\", \"pnpm\", \"yarn\", \"bun\"]);\nconst JS_TEST_RUNNERS = new Set([\"vitest\", \"jest\"]);\nconst SHARD_CONFLICT_FLAGS = [\"--shard\", \"--runInBand\", \"--coverage\", \"--watch\", \"-u\", \"--updateSnapshot\", \"-w\"];\n\nfunction classification(\n\tworkloadClass: WorkloadClass,\n\tcommandFamily: WorkloadCommandFamily,\n\toptions?: { readonly safeToAutoShard?: boolean; readonly extraReasons?: readonly string[] },\n): WorkloadClassification {\n\treturn {\n\t\tworkloadClass,\n\t\tcommandFamily,\n\t\tcomplexity: \"simple-argv\",\n\t\tsafeToAutoShard: options?.safeToAutoShard ?? false,\n\t\treasonCodes: [`family.${commandFamily}`, `class.${workloadClass}`, ...(options?.extraReasons ?? [])],\n\t};\n}\n\nfunction hasShardConflictFlag(argv: readonly string[]): boolean {\n\treturn argv.some((token) => SHARD_CONFLICT_FLAGS.some((flag) => token === flag || token.startsWith(`${flag}=`)));\n}\n\nexport function matchWorkloadFamily(argv: readonly string[]): WorkloadClassification {\n\tconst executable = basename(argv[0]);\n\tconst rest = argv.slice(1);\n\treturn (\n\t\tmatchToolchainFamily(executable, rest) ??\n\t\tmatchInfraFamily(executable, rest) ??\n\t\tmatchTierFallback(executable, rest)\n\t);\n}\n\n/** JS/TS/Go/Rust toolchains (§9.2 first block). */\nfunction matchToolchainFamily(executable: string, rest: readonly string[]): WorkloadClassification | null {\n\tif (JS_TEST_RUNNERS.has(executable)) {\n\t\t// §12.2: direct vitest/jest invocations are shard candidates unless a\n\t\t// conflicting flag changes semantics; the M5 planner enforces the rest.\n\t\tconst conflict = hasShardConflictFlag(rest);\n\t\treturn classification(\"heavy\", \"node-test\", {\n\t\t\tsafeToAutoShard: !conflict,\n\t\t\textraReasons: conflict ? [\"shard.flag.conflict\"] : [],\n\t\t});\n\t}\n\tif (PACKAGE_MANAGERS.has(executable)) {\n\t\tconst script = rest[0] === \"run\" ? rest[1] : rest[0];\n\t\tif (script === \"test\" || script === \"t\" || script?.startsWith(\"test\")) {\n\t\t\t// Opaque wrapper: the underlying runner is unknown statically (§12.1).\n\t\t\treturn classification(\"heavy\", \"node-test\", { extraReasons: [\"shard.wrapper.opaque\"] });\n\t\t}\n\t\tif (script === \"build\" || script?.startsWith(\"build\")) {\n\t\t\treturn classification(\"heavy\", \"node-build\");\n\t\t}\n\t\treturn classification(\"unknown\", \"generic-process\");\n\t}\n\tif (executable === \"tsc\") {\n\t\tconst hasProjectFlag = rest.some((token) => [\"-p\", \"--project\", \"-b\", \"--build\"].includes(token));\n\t\tconst hasFileArg = rest.some((token) => !token.startsWith(\"-\"));\n\t\tif (hasProjectFlag || !hasFileArg) {\n\t\t\treturn classification(\"heavy\", \"typescript\");\n\t\t}\n\t\treturn classification(\"cpu\", \"typescript\");\n\t}\n\tif (executable === \"go\" && rest[0] === \"test\") {\n\t\tconst packageArgs = rest.slice(1).filter((token) => !token.startsWith(\"-\"));\n\t\tconst recursive = packageArgs.some((token) => token.endsWith(\"...\"));\n\t\tif (recursive || packageArgs.length >= 2) {\n\t\t\treturn classification(\"heavy\", \"go-test\", { safeToAutoShard: recursive && !rest.includes(\"-run\") });\n\t\t}\n\t\treturn classification(\"cpu\", \"go-test\");\n\t}\n\tif (executable === \"cargo\" && [\"build\", \"test\", \"check\", \"clippy\"].includes(rest[0] ?? \"\")) {\n\t\treturn classification(\"heavy\", \"rust-build\");\n\t}\n\treturn null;\n}\n\n/** Container/monorepo/archive tooling (§9.2 second block). */\nfunction matchInfraFamily(executable: string, rest: readonly string[]): WorkloadClassification | null {\n\tif ((executable === \"docker\" || executable === \"podman\") && rest[0] === \"build\") {\n\t\treturn classification(\"heavy\", \"container-build\");\n\t}\n\tif ((executable === \"nx\" && rest[0] === \"run-many\") || (executable === \"turbo\" && rest[0] === \"run\")) {\n\t\treturn classification(\"heavy\", \"monorepo\");\n\t}\n\tif (executable === \"tar\") {\n\t\tconst flags = rest[0] ?? \"\";\n\t\tif (flags.includes(\"c\") || rest.includes(\"--create\")) {\n\t\t\treturn classification(\"heavy\", \"archive\");\n\t\t}\n\t\treturn classification(\"io\", \"archive\");\n\t}\n\tif ((executable === \"zip\" && rest.includes(\"-r\")) || (executable === \"7z\" && rest[0] === \"a\")) {\n\t\treturn classification(\"heavy\", \"archive\");\n\t}\n\treturn null;\n}\n\n/** Known light/io/cpu tiers, else unknown/generic-process. */\nfunction matchTierFallback(executable: string, rest: readonly string[]): WorkloadClassification {\n\tif (executable === \"git\" && LIGHT_GIT_SUBCOMMANDS.has(rest[0] ?? \"\")) {\n\t\treturn classification(\"light\", \"generic-process\");\n\t}\n\tif (LIGHT_EXECUTABLES.has(executable)) {\n\t\treturn classification(\"light\", \"generic-process\");\n\t}\n\tif (IO_EXECUTABLES.has(executable)) {\n\t\treturn classification(\"io\", \"generic-process\");\n\t}\n\tif (CPU_EXECUTABLES.has(executable)) {\n\t\treturn classification(\"cpu\", \"generic-process\");\n\t}\n\treturn classification(\"unknown\", \"generic-process\");\n}\n\nfunction basename(token: string): string {\n\tconst slash = token.lastIndexOf(\"/\");\n\treturn slash === -1 ? token : token.slice(slash + 1);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"workload-family-matcher.js","sourceRoot":"","sources":["../../src/core/workload-family-matcher.ts"],"names":[],"mappings":"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","sourcesContent":["import type { WorkloadClass, WorkloadClassification, WorkloadCommandFamily } from \"./workload-types.ts\";\n\nconst LIGHT_EXECUTABLES = new Set([\n\t\"ls\",\n\t\"cat\",\n\t\"head\",\n\t\"tail\",\n\t\"grep\",\n\t\"rg\",\n\t\"find\",\n\t\"echo\",\n\t\"pwd\",\n\t\"which\",\n\t\"wc\",\n\t\"stat\",\n\t\"file\",\n\t\"true\",\n\t\"false\",\n]);\nconst LIGHT_GIT_SUBCOMMANDS = new Set([\"status\", \"log\", \"diff\", \"show\", \"branch\", \"rev-parse\", \"blame\"]);\nconst IO_EXECUTABLES = new Set([\"cp\", \"mv\", \"rsync\", \"mkdir\", \"touch\", \"dd\"]);\nconst CPU_EXECUTABLES = new Set([\"eslint\", \"prettier\", \"biome\", \"ruff\", \"gofmt\", \"rustfmt\"]);\nconst PACKAGE_MANAGERS = new Set([\"npm\", \"pnpm\", \"yarn\", \"bun\"]);\nconst JS_TEST_RUNNERS = new Set([\"vitest\", \"jest\"]);\nconst SHARD_CONFLICT_FLAGS = [\"--shard\", \"--runInBand\", \"--coverage\", \"--watch\", \"-u\", \"--updateSnapshot\", \"-w\"];\n\nfunction classification(\n\tworkloadClass: WorkloadClass,\n\tcommandFamily: WorkloadCommandFamily,\n\toptions?: { readonly safeToAutoShard?: boolean; readonly extraReasons?: readonly string[] },\n): WorkloadClassification {\n\treturn {\n\t\tworkloadClass,\n\t\tcommandFamily,\n\t\tcomplexity: \"simple-argv\",\n\t\tsafeToAutoShard: options?.safeToAutoShard ?? false,\n\t\treasonCodes: [`family.${commandFamily}`, `class.${workloadClass}`, ...(options?.extraReasons ?? [])],\n\t};\n}\n\nfunction hasShardConflictFlag(argv: readonly string[]): boolean {\n\treturn argv.some((token) => SHARD_CONFLICT_FLAGS.some((flag) => token === flag || token.startsWith(`${flag}=`)));\n}\n\nexport function matchWorkloadFamily(argv: readonly string[]): WorkloadClassification {\n\tconst executable = basename(argv[0]);\n\tconst rest = argv.slice(1);\n\treturn (\n\t\tmatchToolchainFamily(executable, rest) ??\n\t\tmatchInfraFamily(executable, rest) ??\n\t\tmatchTierFallback(executable, rest)\n\t);\n}\n\n/** JS/TS/Go/Rust toolchains (§9.2 first block). */\nfunction matchToolchainFamily(executable: string, rest: readonly string[]): WorkloadClassification | null {\n\tif (JS_TEST_RUNNERS.has(executable)) {\n\t\t// §12.2: direct vitest/jest invocations are shard candidates unless a\n\t\t// conflicting flag changes semantics; the M5 planner enforces the rest.\n\t\tconst conflict = hasShardConflictFlag(rest);\n\t\treturn classification(\"heavy\", \"node-test\", {\n\t\t\tsafeToAutoShard: !conflict,\n\t\t\textraReasons: conflict ? [\"shard.flag.conflict\"] : [],\n\t\t});\n\t}\n\tif (PACKAGE_MANAGERS.has(executable)) {\n\t\tconst script = rest[0] === \"run\" ? rest[1] : rest[0];\n\t\tif (script === \"test\" || script === \"t\" || script?.startsWith(\"test\")) {\n\t\t\t// Opaque wrapper: the underlying runner is unknown statically (§12.1).\n\t\t\treturn classification(\"heavy\", \"node-test\", { extraReasons: [\"shard.wrapper.opaque\"] });\n\t\t}\n\t\tif (script === \"build\" || script?.startsWith(\"build\")) {\n\t\t\treturn classification(\"heavy\", \"node-build\");\n\t\t}\n\t\treturn classification(\"unknown\", \"generic-process\");\n\t}\n\tif (executable === \"tsc\") {\n\t\tconst hasProjectFlag = rest.some((token) => [\"-p\", \"--project\", \"-b\", \"--build\"].includes(token));\n\t\tconst hasFileArg = rest.some((token) => !token.startsWith(\"-\"));\n\t\tif (hasProjectFlag || !hasFileArg) {\n\t\t\treturn classification(\"heavy\", \"typescript\");\n\t\t}\n\t\treturn classification(\"cpu\", \"typescript\");\n\t}\n\tif (executable === \"go\" && rest[0] === \"test\") {\n\t\tconst packageArgs = rest.slice(1).filter((token) => !token.startsWith(\"-\"));\n\t\tconst recursive = packageArgs.some((token) => token.endsWith(\"...\"));\n\t\tif (recursive || packageArgs.length >= 2) {\n\t\t\treturn classification(\"heavy\", \"go-test\", { safeToAutoShard: recursive && !rest.includes(\"-run\") });\n\t\t}\n\t\treturn classification(\"cpu\", \"go-test\");\n\t}\n\tif (executable === \"cargo\" && [\"build\", \"test\", \"check\", \"clippy\"].includes(rest[0] ?? \"\")) {\n\t\treturn classification(\"heavy\", \"rust-build\");\n\t}\n\treturn null;\n}\n\n/** Container/monorepo/archive tooling (§9.2 second block). */\nfunction matchInfraFamily(executable: string, rest: readonly string[]): WorkloadClassification | null {\n\tif ((executable === \"docker\" || executable === \"podman\") && rest[0] === \"build\") {\n\t\treturn classification(\"heavy\", \"container-build\");\n\t}\n\tif ((executable === \"nx\" && rest[0] === \"run-many\") || (executable === \"turbo\" && rest[0] === \"run\")) {\n\t\treturn classification(\"heavy\", \"monorepo\");\n\t}\n\tif (executable === \"tar\") {\n\t\tconst flags = rest[0] ?? \"\";\n\t\tif (flags.includes(\"c\") || rest.includes(\"--create\")) {\n\t\t\treturn classification(\"heavy\", \"archive\");\n\t\t}\n\t\treturn classification(\"io\", \"archive\");\n\t}\n\tif ((executable === \"zip\" && rest.includes(\"-r\")) || (executable === \"7z\" && rest[0] === \"a\")) {\n\t\treturn classification(\"heavy\", \"archive\");\n\t}\n\treturn null;\n}\n\n/** Known light/io/cpu tiers, else unknown/generic-process. */\nfunction matchTierFallback(executable: string, rest: readonly string[]): WorkloadClassification {\n\tif (executable === \"git\" && LIGHT_GIT_SUBCOMMANDS.has(rest[0] ?? \"\")) {\n\t\treturn classification(\"light\", \"generic-process\");\n\t}\n\tif (LIGHT_EXECUTABLES.has(executable)) {\n\t\treturn classification(\"light\", \"generic-process\");\n\t}\n\tif (IO_EXECUTABLES.has(executable)) {\n\t\treturn classification(\"io\", \"generic-process\");\n\t}\n\tif (CPU_EXECUTABLES.has(executable)) {\n\t\treturn classification(\"cpu\", \"generic-process\");\n\t}\n\treturn classification(\"unknown\", \"generic-process\");\n}\n\nfunction basename(token: string): string {\n\tconst slash = token.lastIndexOf(\"/\");\n\treturn slash === -1 ? token : token.slice(slash + 1);\n}\n"]}
|