@caupulican/pi-adaptative 0.81.32 → 0.81.37
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 +46 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/README.md +69 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts +53 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts.map +1 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.js +1282 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.js.map +1 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/index.ts +1453 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/templates/builder-validator.json +14 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/templates/claude-trio.json +32 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/templates/full-provider-review.json +15 -0
- package/dist/bundled-resources/extensions/tmux-agent-manager/templates/provider-prompt-smoke.json +14 -0
- package/dist/bundled-resources/prompts/extensionify.md +5 -4
- package/dist/bundled-resources/prompts/skillify.md +5 -5
- package/dist/bundled-resources/skills/pi-harness-learning/SKILL.md +6 -3
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +10 -8
- package/dist/cli/args.js.map +1 -1
- package/dist/config.d.ts +5 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +10 -2
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +12 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +42 -2
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/autonomy/approval-gate.d.ts.map +1 -1
- package/dist/core/autonomy/approval-gate.js +2 -0
- package/dist/core/autonomy/approval-gate.js.map +1 -1
- package/dist/core/autonomy/foreground-envelope.d.ts.map +1 -1
- package/dist/core/autonomy/foreground-envelope.js +2 -0
- package/dist/core/autonomy/foreground-envelope.js.map +1 -1
- package/dist/core/autonomy/gates.d.ts.map +1 -1
- package/dist/core/autonomy/gates.js +9 -2
- package/dist/core/autonomy/gates.js.map +1 -1
- package/dist/core/autonomy/risk-assessment.d.ts.map +1 -1
- package/dist/core/autonomy/risk-assessment.js +2 -0
- package/dist/core/autonomy/risk-assessment.js.map +1 -1
- package/dist/core/background-lane-controller.d.ts +12 -3
- package/dist/core/background-lane-controller.d.ts.map +1 -1
- package/dist/core/background-lane-controller.js +67 -26
- package/dist/core/background-lane-controller.js.map +1 -1
- package/dist/core/bash-execution-controller.d.ts +9 -4
- package/dist/core/bash-execution-controller.d.ts.map +1 -1
- package/dist/core/bash-execution-controller.js +7 -6
- package/dist/core/bash-execution-controller.js.map +1 -1
- package/dist/core/bash-executor.d.ts +2 -0
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +10 -5
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/context/context-store-retention.d.ts +4 -1
- package/dist/core/context/context-store-retention.d.ts.map +1 -1
- package/dist/core/context/context-store-retention.js +42 -151
- package/dist/core/context/context-store-retention.js.map +1 -1
- package/dist/core/context-pipeline.d.ts.map +1 -1
- package/dist/core/context-pipeline.js +6 -6
- package/dist/core/context-pipeline.js.map +1 -1
- package/dist/core/default-tool-surface.d.ts +7 -0
- package/dist/core/default-tool-surface.d.ts.map +1 -1
- package/dist/core/default-tool-surface.js +29 -10
- package/dist/core/default-tool-surface.js.map +1 -1
- package/dist/core/doctor.d.ts +10 -10
- package/dist/core/doctor.d.ts.map +1 -1
- package/dist/core/doctor.js +30 -20
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/exec.d.ts +2 -0
- package/dist/core/exec.d.ts.map +1 -1
- package/dist/core/exec.js +26 -39
- package/dist/core/exec.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +9 -2
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +105 -21
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/types.d.ts +13 -3
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js +3 -0
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/footer-data-provider.d.ts.map +1 -1
- package/dist/core/footer-data-provider.js +2 -0
- package/dist/core/footer-data-provider.js.map +1 -1
- package/dist/core/goals/goal-state.d.ts +10 -0
- package/dist/core/goals/goal-state.d.ts.map +1 -1
- package/dist/core/goals/goal-state.js +37 -0
- package/dist/core/goals/goal-state.js.map +1 -1
- package/dist/core/goals/goal-tool-core.d.ts +11 -0
- package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
- package/dist/core/goals/goal-tool-core.js +51 -1
- package/dist/core/goals/goal-tool-core.js.map +1 -1
- package/dist/core/improvement-loop.d.ts.map +1 -1
- package/dist/core/improvement-loop.js +26 -5
- package/dist/core/improvement-loop.js.map +1 -1
- package/dist/core/memory/memory-manager.d.ts.map +1 -1
- package/dist/core/memory/memory-manager.js +1 -0
- package/dist/core/memory/memory-manager.js.map +1 -1
- package/dist/core/model-capability.d.ts.map +1 -1
- package/dist/core/model-capability.js +2 -0
- package/dist/core/model-capability.js.map +1 -1
- package/dist/core/model-router/tool-escalation.d.ts.map +1 -1
- package/dist/core/model-router/tool-escalation.js +17 -3
- package/dist/core/model-router/tool-escalation.js.map +1 -1
- package/dist/core/models/fitness-store.d.ts.map +1 -1
- package/dist/core/models/fitness-store.js +1 -1
- package/dist/core/models/fitness-store.js.map +1 -1
- package/dist/core/models/local-runtime.d.ts +6 -4
- package/dist/core/models/local-runtime.d.ts.map +1 -1
- package/dist/core/models/local-runtime.js +108 -36
- package/dist/core/models/local-runtime.js.map +1 -1
- package/dist/core/output-guard.d.ts.map +1 -1
- package/dist/core/output-guard.js +23 -8
- package/dist/core/output-guard.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +44 -59
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/profile-resource-selection.d.ts +16 -0
- package/dist/core/profile-resource-selection.d.ts.map +1 -1
- package/dist/core/profile-resource-selection.js +41 -0
- package/dist/core/profile-resource-selection.js.map +1 -1
- package/dist/core/python-runtime.d.ts +46 -0
- package/dist/core/python-runtime.d.ts.map +1 -0
- package/dist/core/python-runtime.js +157 -0
- package/dist/core/python-runtime.js.map +1 -0
- package/dist/core/reflection-controller.d.ts.map +1 -1
- package/dist/core/reflection-controller.js +18 -1
- package/dist/core/reflection-controller.js.map +1 -1
- package/dist/core/reload-blockers.d.ts +1 -0
- package/dist/core/reload-blockers.d.ts.map +1 -1
- package/dist/core/reload-blockers.js +6 -2
- package/dist/core/reload-blockers.js.map +1 -1
- package/dist/core/resource-loader.d.ts +2 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +50 -28
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/runtime-builder.d.ts +4 -0
- package/dist/core/runtime-builder.d.ts.map +1 -1
- package/dist/core/runtime-builder.js +31 -9
- package/dist/core/runtime-builder.js.map +1 -1
- package/dist/core/security/untrusted-boundary.d.ts +2 -2
- package/dist/core/security/untrusted-boundary.d.ts.map +1 -1
- package/dist/core/security/untrusted-boundary.js +4 -3
- package/dist/core/security/untrusted-boundary.js.map +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +3 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +9 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +13 -2
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tasks/session-task-state.d.ts +10 -0
- package/dist/core/tasks/session-task-state.d.ts.map +1 -0
- package/dist/core/tasks/session-task-state.js +29 -0
- package/dist/core/tasks/session-task-state.js.map +1 -0
- package/dist/core/tasks/task-command.d.ts +30 -0
- package/dist/core/tasks/task-command.d.ts.map +1 -0
- package/dist/core/tasks/task-command.js +79 -0
- package/dist/core/tasks/task-command.js.map +1 -0
- package/dist/core/tasks/task-state.d.ts +71 -0
- package/dist/core/tasks/task-state.d.ts.map +1 -0
- package/dist/core/tasks/task-state.js +372 -0
- package/dist/core/tasks/task-state.js.map +1 -0
- package/dist/core/tool-selection/tool-selection-controller.d.ts.map +1 -1
- package/dist/core/tool-selection/tool-selection-controller.js +1 -1
- package/dist/core/tool-selection/tool-selection-controller.js.map +1 -1
- package/dist/core/toolkit/script-runner.d.ts.map +1 -1
- package/dist/core/toolkit/script-runner.js +58 -24
- package/dist/core/toolkit/script-runner.js.map +1 -1
- package/dist/core/tools/bash.d.ts +20 -2
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +132 -220
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/extensionify.d.ts +2 -1
- package/dist/core/tools/extensionify.d.ts.map +1 -1
- package/dist/core/tools/extensionify.js +11 -8
- package/dist/core/tools/extensionify.js.map +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js +57 -48
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/git-filter.d.ts.map +1 -1
- package/dist/core/tools/git-filter.js +20 -32
- package/dist/core/tools/git-filter.js.map +1 -1
- package/dist/core/tools/goal.d.ts +1 -1
- package/dist/core/tools/goal.d.ts.map +1 -1
- package/dist/core/tools/goal.js +10 -3
- package/dist/core/tools/goal.js.map +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js +92 -80
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +9 -6
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +24 -9
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts +2 -0
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +7 -4
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/core/tools/python.d.ts +64 -0
- package/dist/core/tools/python.d.ts.map +1 -0
- package/dist/core/tools/python.js +241 -0
- package/dist/core/tools/python.js.map +1 -0
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +4 -3
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/shell-contract-router.d.ts +13 -0
- package/dist/core/tools/shell-contract-router.d.ts.map +1 -0
- package/dist/core/tools/shell-contract-router.js +295 -0
- package/dist/core/tools/shell-contract-router.js.map +1 -0
- package/dist/core/tools/task-steps.d.ts +46 -0
- package/dist/core/tools/task-steps.d.ts.map +1 -0
- package/dist/core/tools/task-steps.js +160 -0
- package/dist/core/tools/task-steps.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +2 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +23 -4
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +18 -4
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/auth-dialogs-controller.d.ts +10 -0
- package/dist/modes/interactive/auth-dialogs-controller.d.ts.map +1 -1
- package/dist/modes/interactive/auth-dialogs-controller.js +94 -27
- package/dist/modes/interactive/auth-dialogs-controller.js.map +1 -1
- package/dist/modes/interactive/auto-learn-controller.d.ts.map +1 -1
- package/dist/modes/interactive/auto-learn-controller.js +2 -1
- package/dist/modes/interactive/auto-learn-controller.js.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts +1 -0
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.js +8 -0
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +6 -15
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/login-dialog.d.ts +3 -1
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
- package/dist/modes/interactive/components/login-dialog.js +20 -8
- package/dist/modes/interactive/components/login-dialog.js.map +1 -1
- package/dist/modes/interactive/components/profile-resource-editor.d.ts +4 -1
- package/dist/modes/interactive/components/profile-resource-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/profile-resource-editor.js +22 -39
- package/dist/modes/interactive/components/profile-resource-editor.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +1 -1
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/config-backup.d.ts +1 -0
- package/dist/modes/interactive/config-backup.d.ts.map +1 -1
- package/dist/modes/interactive/config-backup.js +14 -3
- package/dist/modes/interactive/config-backup.js.map +1 -1
- package/dist/modes/interactive/editor-overlay-host.d.ts +12 -0
- package/dist/modes/interactive/editor-overlay-host.d.ts.map +1 -1
- package/dist/modes/interactive/editor-overlay-host.js +55 -0
- package/dist/modes/interactive/editor-overlay-host.js.map +1 -1
- package/dist/modes/interactive/extension-ui-host.d.ts +2 -1
- package/dist/modes/interactive/extension-ui-host.d.ts.map +1 -1
- package/dist/modes/interactive/extension-ui-host.js +161 -96
- package/dist/modes/interactive/extension-ui-host.js.map +1 -1
- package/dist/modes/interactive/external-editor.d.ts.map +1 -1
- package/dist/modes/interactive/external-editor.js +23 -28
- package/dist/modes/interactive/external-editor.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +26 -8
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/profile-menu-controller.d.ts +5 -1
- package/dist/modes/interactive/profile-menu-controller.d.ts.map +1 -1
- package/dist/modes/interactive/profile-menu-controller.js +118 -24
- package/dist/modes/interactive/profile-menu-controller.js.map +1 -1
- package/dist/modes/interactive/resource-shell-commands.d.ts +1 -0
- package/dist/modes/interactive/resource-shell-commands.d.ts.map +1 -1
- package/dist/modes/interactive/resource-shell-commands.js +5 -1
- package/dist/modes/interactive/resource-shell-commands.js.map +1 -1
- package/dist/modes/interactive/session-flow-commands.d.ts +10 -0
- package/dist/modes/interactive/session-flow-commands.d.ts.map +1 -1
- package/dist/modes/interactive/session-flow-commands.js +143 -7
- package/dist/modes/interactive/session-flow-commands.js.map +1 -1
- package/dist/modes/interactive/session-io-commands.d.ts.map +1 -1
- package/dist/modes/interactive/session-io-commands.js +80 -50
- package/dist/modes/interactive/session-io-commands.js.map +1 -1
- package/dist/modes/interactive/startup-checks.d.ts.map +1 -1
- package/dist/modes/interactive/startup-checks.js +14 -17
- package/dist/modes/interactive/startup-checks.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js +20 -19
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +10 -19
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/utils/child-process.d.ts +13 -7
- package/dist/utils/child-process.d.ts.map +1 -1
- package/dist/utils/child-process.js +56 -3
- package/dist/utils/child-process.js.map +1 -1
- package/dist/utils/clipboard-image.d.ts.map +1 -1
- package/dist/utils/clipboard-image.js +3 -2
- package/dist/utils/clipboard-image.js.map +1 -1
- package/dist/utils/clipboard.d.ts.map +1 -1
- package/dist/utils/clipboard.js +17 -10
- package/dist/utils/clipboard.js.map +1 -1
- package/dist/utils/external-editor-command.d.ts +8 -0
- package/dist/utils/external-editor-command.d.ts.map +1 -0
- package/dist/utils/external-editor-command.js +60 -0
- package/dist/utils/external-editor-command.js.map +1 -0
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +10 -2
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/shell.d.ts +6 -8
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +61 -70
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/tools-manager.d.ts +23 -3
- package/dist/utils/tools-manager.d.ts.map +1 -1
- package/dist/utils/tools-manager.js +117 -21
- package/dist/utils/tools-manager.js.map +1 -1
- package/dist/utils/work-directory.d.ts +36 -0
- package/dist/utils/work-directory.d.ts.map +1 -0
- package/dist/utils/work-directory.js +462 -0
- package/dist/utils/work-directory.js.map +1 -0
- package/docs/index.md +4 -0
- package/docs/python.md +43 -0
- package/docs/resources.md +2 -2
- package/docs/rpc.md +1 -1
- package/docs/settings.md +7 -3
- package/docs/task-steps.md +90 -0
- package/docs/termux.md +7 -0
- package/docs/tmux-agent-manager.md +48 -0
- package/docs/usage.md +5 -6
- package/docs/windows.md +104 -8
- package/docs/work-directory.md +41 -0
- 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/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 +32 -37
- package/package.json +9 -7
- package/scripts/postinstall-python-runtime.mjs +20 -0
package/dist/core/tools/bash.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { constants } from "node:fs";
|
|
2
|
-
import { access as fsAccess
|
|
3
|
-
import { relative as relativePath, resolve as resolvePath } from "node:path";
|
|
2
|
+
import { access as fsAccess } from "node:fs/promises";
|
|
4
3
|
import { createSilenceWatchdog } from "@caupulican/pi-agent-core";
|
|
5
4
|
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, } from "@caupulican/pi-agent-core/node";
|
|
6
5
|
import { Container, Text, truncateToWidth } from "@caupulican/pi-tui";
|
|
@@ -9,45 +8,54 @@ import { Type } from "typebox";
|
|
|
9
8
|
import { keyHint } from "../../modes/interactive/components/keybinding-hints.js";
|
|
10
9
|
import { truncateToVisualLines } from "../../modes/interactive/components/visual-truncate.js";
|
|
11
10
|
import { theme } from "../../modes/interactive/theme/theme.js";
|
|
12
|
-
import {
|
|
13
|
-
import { getShellConfig, getShellEnv,
|
|
11
|
+
import { waitForChildProcessWithTermination } from "../../utils/child-process.js";
|
|
12
|
+
import { getPlatformShellToolName, getShellConfig, getShellEnv, prefixPowerShellCommand, trackDetachedChildPid, untrackDetachedChildPid, } from "../../utils/shell.js";
|
|
14
13
|
import { withExclusiveMutationBarrier } from "./file-mutation-queue.js";
|
|
15
14
|
import { classifyGitCommand, executeFilteredGit } from "./git-filter.js";
|
|
16
15
|
import { OutputAccumulator } from "./output-accumulator.js";
|
|
17
16
|
import { getTextOutput, invalidArgText, str } from "./render-utils.js";
|
|
17
|
+
import { routeShellContract } from "./shell-contract-router.js";
|
|
18
18
|
import { wrapToolDefinition } from "./tool-definition-wrapper.js";
|
|
19
|
-
/**
|
|
19
|
+
/** Low-level silence bound retained for direct shell-operation consumers. Agent tool calls always pass a wall-clock bound. */
|
|
20
20
|
const DEFAULT_COMMAND_SILENCE_MS = 600_000;
|
|
21
|
+
/** Agent-facing wall-clock bound: continuously producing output must not make a command immortal. */
|
|
22
|
+
export const DEFAULT_COMMAND_TIMEOUT_SECONDS = 120;
|
|
23
|
+
export const MAX_COMMAND_TIMEOUT_SECONDS = 3600;
|
|
24
|
+
const MIN_COMMAND_TIMEOUT_SECONDS = 0.1;
|
|
21
25
|
let commandSilenceMsOverride;
|
|
22
|
-
|
|
26
|
+
let commandTimeoutMsOverride;
|
|
27
|
+
/** Test hook: override the low-level silence threshold. Pass undefined to restore the default. */
|
|
23
28
|
export function setCommandSilenceMsForTests(ms) {
|
|
24
29
|
commandSilenceMsOverride = ms;
|
|
25
30
|
}
|
|
31
|
+
/** Test hook: override the agent tool's default wall-clock bound. Pass undefined to restore it. */
|
|
32
|
+
export function setCommandTimeoutMsForTests(ms) {
|
|
33
|
+
commandTimeoutMsOverride = ms;
|
|
34
|
+
}
|
|
35
|
+
export function resolveCommandTimeoutSeconds(timeout) {
|
|
36
|
+
if (typeof timeout !== "number" || !Number.isFinite(timeout) || timeout <= 0) {
|
|
37
|
+
return DEFAULT_COMMAND_TIMEOUT_SECONDS;
|
|
38
|
+
}
|
|
39
|
+
return Math.max(MIN_COMMAND_TIMEOUT_SECONDS, Math.min(timeout, MAX_COMMAND_TIMEOUT_SECONDS));
|
|
40
|
+
}
|
|
26
41
|
const bashSchema = Type.Object({
|
|
27
|
-
command: Type.String({ description: "
|
|
42
|
+
command: Type.String({ description: "Shell command to execute" }),
|
|
28
43
|
timeout: Type.Optional(Type.Number({
|
|
29
|
-
description:
|
|
44
|
+
description: `Wall-clock timeout in seconds. Defaults to ${DEFAULT_COMMAND_TIMEOUT_SECONDS}; positive overrides are capped at ${MAX_COMMAND_TIMEOUT_SECONDS}. Zero or negative values use the default.`,
|
|
30
45
|
})),
|
|
31
46
|
});
|
|
32
|
-
|
|
33
|
-
* Create bash operations using pi's built-in local shell execution backend.
|
|
34
|
-
*
|
|
35
|
-
* This is useful for extensions that intercept user_bash and still want pi's
|
|
36
|
-
* standard local shell behavior while wrapping or rewriting commands.
|
|
37
|
-
*/
|
|
38
|
-
export function createLocalBashOperations(options) {
|
|
47
|
+
function createLocalShellOperations(shellName, options) {
|
|
39
48
|
return {
|
|
40
49
|
exec: async (command, cwd, { onData, signal, timeout, env }) => {
|
|
41
|
-
const { shell, args } = getShellConfig(options?.shellPath);
|
|
50
|
+
const { shell, args } = getShellConfig(options?.shellPath, shellName);
|
|
42
51
|
try {
|
|
43
52
|
await fsAccess(cwd, constants.F_OK);
|
|
44
53
|
}
|
|
45
54
|
catch {
|
|
46
|
-
throw new Error(`Working directory does not exist: ${cwd}\nCannot execute
|
|
55
|
+
throw new Error(`Working directory does not exist: ${cwd}\nCannot execute ${shellName} commands.`);
|
|
47
56
|
}
|
|
48
|
-
if (signal?.aborted)
|
|
57
|
+
if (signal?.aborted)
|
|
49
58
|
throw new Error("aborted");
|
|
50
|
-
}
|
|
51
59
|
const child = spawn(shell, [...args, command], {
|
|
52
60
|
cwd,
|
|
53
61
|
detached: process.platform !== "win32",
|
|
@@ -57,16 +65,8 @@ export function createLocalBashOperations(options) {
|
|
|
57
65
|
});
|
|
58
66
|
if (child.pid)
|
|
59
67
|
trackDetachedChildPid(child.pid);
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const onAbort = () => {
|
|
63
|
-
if (child.pid)
|
|
64
|
-
killProcessTree(child.pid);
|
|
65
|
-
};
|
|
66
|
-
// A command that keeps producing output must never be killed by this mechanism:
|
|
67
|
-
// silence bounds mute-ness, not duration. It arms when the caller left `timeout`
|
|
68
|
-
// unset or non-positive (treated as unset) — an explicit positive timeout means
|
|
69
|
-
// the model took responsibility for the wall clock.
|
|
68
|
+
const terminationController = new AbortController();
|
|
69
|
+
const onAbort = () => terminationController.abort();
|
|
70
70
|
let silenceKilled = false;
|
|
71
71
|
const silenceMs = commandSilenceMsOverride ?? DEFAULT_COMMAND_SILENCE_MS;
|
|
72
72
|
const silenceWatchdog = (timeout === undefined || timeout <= 0) && silenceMs > 0
|
|
@@ -74,8 +74,7 @@ export function createLocalBashOperations(options) {
|
|
|
74
74
|
silenceMs,
|
|
75
75
|
onSilence: () => {
|
|
76
76
|
silenceKilled = true;
|
|
77
|
-
|
|
78
|
-
killProcessTree(child.pid);
|
|
77
|
+
terminationController.abort();
|
|
79
78
|
},
|
|
80
79
|
})
|
|
81
80
|
: undefined;
|
|
@@ -84,50 +83,72 @@ export function createLocalBashOperations(options) {
|
|
|
84
83
|
onData(data);
|
|
85
84
|
};
|
|
86
85
|
try {
|
|
87
|
-
// Set timeout if provided.
|
|
88
|
-
if (timeout !== undefined && timeout > 0) {
|
|
89
|
-
timeoutHandle = setTimeout(() => {
|
|
90
|
-
timedOut = true;
|
|
91
|
-
if (child.pid)
|
|
92
|
-
killProcessTree(child.pid);
|
|
93
|
-
}, timeout * 1000);
|
|
94
|
-
}
|
|
95
|
-
// Stream stdout and stderr.
|
|
96
86
|
child.stdout?.on("data", onChunk);
|
|
97
87
|
child.stderr?.on("data", onChunk);
|
|
98
|
-
// Handle abort signal by killing the entire process tree.
|
|
99
88
|
if (signal) {
|
|
100
89
|
if (signal.aborted)
|
|
101
90
|
onAbort();
|
|
102
91
|
else
|
|
103
92
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
104
93
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
94
|
+
const terminal = await waitForChildProcessWithTermination(child, {
|
|
95
|
+
signal: terminationController.signal,
|
|
96
|
+
timeoutMs: timeout !== undefined && timeout > 0 ? timeout * 1000 : undefined,
|
|
97
|
+
killGraceMs: 2_000,
|
|
98
|
+
});
|
|
99
|
+
if (signal?.aborted)
|
|
109
100
|
throw new Error("aborted");
|
|
110
|
-
|
|
111
|
-
if (timedOut) {
|
|
101
|
+
if (terminal.reason === "timeout")
|
|
112
102
|
throw new Error(`timeout:${timeout}`);
|
|
113
|
-
|
|
114
|
-
if (silenceKilled) {
|
|
103
|
+
if (silenceKilled)
|
|
115
104
|
throw new Error(`silence:${silenceMs / 1000}`);
|
|
116
|
-
}
|
|
117
|
-
return { exitCode };
|
|
105
|
+
return { exitCode: terminal.code };
|
|
118
106
|
}
|
|
119
107
|
finally {
|
|
120
108
|
silenceWatchdog?.disarm();
|
|
121
109
|
if (child.pid)
|
|
122
110
|
untrackDetachedChildPid(child.pid);
|
|
123
|
-
if (timeoutHandle)
|
|
124
|
-
clearTimeout(timeoutHandle);
|
|
125
111
|
if (signal)
|
|
126
112
|
signal.removeEventListener("abort", onAbort);
|
|
127
113
|
}
|
|
128
114
|
},
|
|
129
115
|
};
|
|
130
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Create bash operations using pi's built-in local shell execution backend.
|
|
119
|
+
*
|
|
120
|
+
* This is useful for extensions that intercept user_bash and still want pi's
|
|
121
|
+
* standard local shell behavior while wrapping or rewriting commands.
|
|
122
|
+
*/
|
|
123
|
+
export function createLocalBashOperations(options) {
|
|
124
|
+
return createLocalShellOperations("bash", options);
|
|
125
|
+
}
|
|
126
|
+
/** Create PowerShell operations using pi's built-in local execution backend. */
|
|
127
|
+
export function createLocalPowerShellOperations(options) {
|
|
128
|
+
return createLocalShellOperations("powershell", options);
|
|
129
|
+
}
|
|
130
|
+
/** Create the platform shell backend without requiring callers or the model to choose a shell. */
|
|
131
|
+
export function createLocalPlatformShellOperations(options = {}, platform = process.platform) {
|
|
132
|
+
const operations = options.operations ??
|
|
133
|
+
createLocalShellOperations(getPlatformShellToolName(platform), { shellPath: options.shellPath });
|
|
134
|
+
return {
|
|
135
|
+
async exec(command, cwd, execOptions) {
|
|
136
|
+
let resolvedCommand = command;
|
|
137
|
+
if (platform === "win32") {
|
|
138
|
+
const route = routeShellContract(command, platform);
|
|
139
|
+
if (route.kind === "unsupported")
|
|
140
|
+
throw new Error(route.error);
|
|
141
|
+
if (route.kind === "powershell")
|
|
142
|
+
resolvedCommand = route.command;
|
|
143
|
+
}
|
|
144
|
+
if (options.commandPrefix)
|
|
145
|
+
resolvedCommand = `${options.commandPrefix}\n${resolvedCommand}`;
|
|
146
|
+
if (platform === "win32")
|
|
147
|
+
resolvedCommand = prefixPowerShellCommand(resolvedCommand);
|
|
148
|
+
return operations.exec(resolvedCommand, cwd, execOptions);
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
131
152
|
function resolveSpawnContext(command, cwd, spawnHook) {
|
|
132
153
|
const baseContext = { command, cwd, env: { ...getShellEnv() } };
|
|
133
154
|
return spawnHook ? spawnHook(baseContext) : baseContext;
|
|
@@ -145,12 +166,13 @@ class BashResultRenderComponent extends Container {
|
|
|
145
166
|
function formatDuration(ms) {
|
|
146
167
|
return `${(ms / 1000).toFixed(1)}s`;
|
|
147
168
|
}
|
|
148
|
-
function formatBashCall(args) {
|
|
169
|
+
function formatBashCall(args, shellName) {
|
|
149
170
|
const command = str(args?.command);
|
|
150
171
|
const timeout = args?.timeout;
|
|
151
172
|
const timeoutSuffix = timeout ? theme.fg("muted", ` (timeout ${timeout}s)`) : "";
|
|
152
173
|
const commandDisplay = command === null ? invalidArgText(theme) : command ? command : theme.fg("toolOutput", "...");
|
|
153
|
-
|
|
174
|
+
const prompt = shellName === "powershell" ? "PS>" : "$";
|
|
175
|
+
return theme.fg("toolTitle", theme.bold(`${prompt} ${commandDisplay}`)) + timeoutSuffix;
|
|
154
176
|
}
|
|
155
177
|
function rebuildBashResultRenderComponent(component, result, options, showImages, startedAt, endedAt) {
|
|
156
178
|
const state = component.state;
|
|
@@ -222,158 +244,40 @@ function rebuildBashResultRenderComponent(component, result, options, showImages
|
|
|
222
244
|
component.addChild(new Text(`\n${theme.fg("muted", `${label} ${formatDuration(endTime - startedAt)}`)}`, 0, 0));
|
|
223
245
|
}
|
|
224
246
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
return { optimized: false };
|
|
232
|
-
}
|
|
233
|
-
// Reject if there are shell operators or pipes/redirects
|
|
234
|
-
const shellOperators = ["|", ">", "<", "&", ";", "\n", "\r", "$", "`", "(", ")", "*", "?", "[", "]"];
|
|
235
|
-
if (shellOperators.some((op) => trimmed.includes(op))) {
|
|
236
|
-
return { optimized: false };
|
|
237
|
-
}
|
|
238
|
-
// Simple tokenizer split by whitespace
|
|
239
|
-
const args = trimmed.split(/\s+/);
|
|
240
|
-
if (args.length === 0) {
|
|
241
|
-
return { optimized: false };
|
|
242
|
-
}
|
|
243
|
-
const cmd = args[0];
|
|
244
|
-
const unquote = (s) => {
|
|
245
|
-
if ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'"))) {
|
|
246
|
-
return s.slice(1, -1);
|
|
247
|
-
}
|
|
248
|
-
return s;
|
|
249
|
-
};
|
|
250
|
-
const resolveToCwd = (p, base) => {
|
|
251
|
-
const clean = unquote(p);
|
|
252
|
-
if (clean.startsWith("/"))
|
|
253
|
-
return clean;
|
|
254
|
-
return resolvePath(base, clean);
|
|
255
|
-
};
|
|
256
|
-
try {
|
|
257
|
-
if (cmd === "cat") {
|
|
258
|
-
if (args.length === 2 && !args[1].startsWith("-")) {
|
|
259
|
-
const filePath = resolveToCwd(args[1], cwd);
|
|
260
|
-
const content = await fsReadFile(filePath, "utf-8");
|
|
261
|
-
return { optimized: true, output: content, exitCode: 0 };
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
else if (cmd === "head") {
|
|
265
|
-
if (args.length === 2 && !args[1].startsWith("-")) {
|
|
266
|
-
const filePath = resolveToCwd(args[1], cwd);
|
|
267
|
-
const content = await fsReadFile(filePath, "utf-8");
|
|
268
|
-
const lines = content.split("\n").slice(0, 10).join("\n");
|
|
269
|
-
return { optimized: true, output: lines, exitCode: 0 };
|
|
270
|
-
}
|
|
271
|
-
else if (args.length === 4 && args[1] === "-n" && !args[3].startsWith("-")) {
|
|
272
|
-
const count = parseInt(args[2], 10);
|
|
273
|
-
if (!Number.isNaN(count) && count >= 0) {
|
|
274
|
-
const filePath = resolveToCwd(args[3], cwd);
|
|
275
|
-
const content = await fsReadFile(filePath, "utf-8");
|
|
276
|
-
const lines = content.split("\n").slice(0, count).join("\n");
|
|
277
|
-
return { optimized: true, output: lines, exitCode: 0 };
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
else if (cmd === "tail") {
|
|
282
|
-
if (args.length === 2 && !args[1].startsWith("-")) {
|
|
283
|
-
const filePath = resolveToCwd(args[1], cwd);
|
|
284
|
-
const content = await fsReadFile(filePath, "utf-8");
|
|
285
|
-
const allLines = content.split("\n");
|
|
286
|
-
const lines = allLines.slice(Math.max(0, allLines.length - 10)).join("\n");
|
|
287
|
-
return { optimized: true, output: lines, exitCode: 0 };
|
|
288
|
-
}
|
|
289
|
-
else if (args.length === 4 && args[1] === "-n" && !args[3].startsWith("-")) {
|
|
290
|
-
const count = parseInt(args[2], 10);
|
|
291
|
-
if (!Number.isNaN(count) && count >= 0) {
|
|
292
|
-
const filePath = resolveToCwd(args[3], cwd);
|
|
293
|
-
const content = await fsReadFile(filePath, "utf-8");
|
|
294
|
-
const allLines = content.split("\n");
|
|
295
|
-
const lines = allLines.slice(Math.max(0, allLines.length - count)).join("\n");
|
|
296
|
-
return { optimized: true, output: lines, exitCode: 0 };
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
else if (cmd === "ls") {
|
|
301
|
-
if (args.length === 1 || (args.length === 2 && !args[1].startsWith("-"))) {
|
|
302
|
-
const targetDir = args.length === 2 ? resolveToCwd(args[1], cwd) : cwd;
|
|
303
|
-
const entries = await fsReaddir(targetDir);
|
|
304
|
-
entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
|
|
305
|
-
const results = [];
|
|
306
|
-
for (const entry of entries) {
|
|
307
|
-
let suffix = "";
|
|
308
|
-
try {
|
|
309
|
-
const entryStat = await fsStat(resolvePath(targetDir, entry));
|
|
310
|
-
if (entryStat.isDirectory())
|
|
311
|
-
suffix = "/";
|
|
312
|
-
}
|
|
313
|
-
catch { }
|
|
314
|
-
results.push(entry + suffix);
|
|
315
|
-
}
|
|
316
|
-
return { optimized: true, output: results.join("\n"), exitCode: 0 };
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
else if (cmd === "grep" || cmd === "rg") {
|
|
320
|
-
if (args.length === 3 && !args[1].startsWith("-") && !args[2].startsWith("-")) {
|
|
321
|
-
const patternStr = unquote(args[1]);
|
|
322
|
-
if (/[\\^$.|?*+()[\]{}]/.test(patternStr))
|
|
323
|
-
return { optimized: false };
|
|
324
|
-
const filePath = resolveToCwd(args[2], cwd);
|
|
325
|
-
const content = await fsReadFile(filePath, "utf-8");
|
|
326
|
-
const lines = content.split("\n");
|
|
327
|
-
const matches = [];
|
|
328
|
-
for (let i = 0; i < lines.length; i++) {
|
|
329
|
-
if (lines[i].includes(patternStr)) {
|
|
330
|
-
matches.push(lines[i]);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
return { optimized: true, output: matches.join("\n"), exitCode: matches.length > 0 ? 0 : 1 };
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
else if (cmd === "find") {
|
|
337
|
-
if (args.length === 1 || (args.length === 2 && !args[1].startsWith("-"))) {
|
|
338
|
-
const searchPath = args.length === 2 ? resolveToCwd(args[1], cwd) : cwd;
|
|
339
|
-
const walk = async (dir) => {
|
|
340
|
-
let results = [];
|
|
341
|
-
const entries = await fsReaddir(dir, { withFileTypes: true });
|
|
342
|
-
for (const entry of entries) {
|
|
343
|
-
const full = resolvePath(dir, entry.name);
|
|
344
|
-
results.push(full);
|
|
345
|
-
if (entry.isDirectory()) {
|
|
346
|
-
results = results.concat(await walk(full));
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
return results;
|
|
350
|
-
};
|
|
351
|
-
const all = (await walk(searchPath)).map((p) => relativePath(searchPath, p).replace(/\\/g, "/"));
|
|
352
|
-
return { optimized: true, output: all.join("\n"), exitCode: 0 };
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
catch {
|
|
357
|
-
return { optimized: false };
|
|
358
|
-
}
|
|
359
|
-
return { optimized: false };
|
|
360
|
-
}
|
|
361
|
-
export function createBashToolDefinition(cwd, options) {
|
|
362
|
-
const ops = options?.operations ?? createLocalBashOperations({ shellPath: options?.shellPath });
|
|
247
|
+
function createShellToolDefinition(cwd, backendShell, contractPlatform, options) {
|
|
248
|
+
const toolName = "bash";
|
|
249
|
+
const ops = options?.operations ??
|
|
250
|
+
(backendShell === "powershell"
|
|
251
|
+
? createLocalPowerShellOperations({ shellPath: options?.shellPath })
|
|
252
|
+
: createLocalBashOperations({ shellPath: options?.shellPath }));
|
|
363
253
|
const commandPrefix = options?.commandPrefix;
|
|
364
254
|
const spawnHook = options?.spawnHook;
|
|
365
|
-
const
|
|
366
|
-
const canFilterCommand =
|
|
255
|
+
const hasExecutionOverrides = Boolean(options?.operations || options?.shellPath || commandPrefix || spawnHook);
|
|
256
|
+
const canFilterCommand = !hasExecutionOverrides;
|
|
257
|
+
const routesWindowsContract = contractPlatform === "win32";
|
|
258
|
+
const contractDescription = routesWindowsContract
|
|
259
|
+
? "Execute Pi's stable Bash-like command contract in the current working directory. On Windows, a finite deterministic router converts supported simple commands to PowerShell; unsupported Bash constructs fail closed instead of being guessed."
|
|
260
|
+
: "Execute a Bash command in the current working directory.";
|
|
367
261
|
return {
|
|
368
|
-
name:
|
|
369
|
-
label:
|
|
370
|
-
description:
|
|
371
|
-
promptSnippet:
|
|
262
|
+
name: toolName,
|
|
263
|
+
label: toolName,
|
|
264
|
+
description: `${contractDescription} 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. Commands have a ${DEFAULT_COMMAND_TIMEOUT_SECONDS}-second wall-clock default, including commands that keep producing output; use a positive timeout only when a scoped operation justifies a larger bound (maximum ${MAX_COMMAND_TIMEOUT_SECONDS} seconds).`,
|
|
265
|
+
promptSnippet: routesWindowsContract
|
|
266
|
+
? "Execute simple Bash-like commands; Pi routes supported forms deterministically to PowerShell on Windows"
|
|
267
|
+
: "Execute Bash commands (ls, grep, find, etc.)",
|
|
268
|
+
promptGuidelines: routesWindowsContract
|
|
269
|
+
? [
|
|
270
|
+
"Use the bash tool's portable simple-command contract on Windows; do not write PowerShell or ask the user to choose a shell.",
|
|
271
|
+
"Use one simple command per call. The deterministic router rejects pipelines, redirection, expansion, shell chaining, nested shells, and unsupported Bash forms; use dedicated read/edit/search tools or separate calls instead.",
|
|
272
|
+
"Supported Bash-like file commands are converted with literal-path PowerShell operations; verify targets before recursive rm, cp, or mv calls.",
|
|
273
|
+
"Keep searches scoped and purpose-driven: discover paths first, pass an explicit root and filters, prefer rg over broad find, and increase the timeout only for a justified bounded search.",
|
|
274
|
+
]
|
|
275
|
+
: [
|
|
276
|
+
"Keep searches scoped and purpose-driven: discover paths first, pass an explicit root and filters, prefer rg over broad find, and increase the timeout only for a justified bounded search.",
|
|
277
|
+
],
|
|
372
278
|
parameters: bashSchema,
|
|
373
279
|
async execute(_toolCallId, { command, timeout }, signal, onUpdate, _ctx) {
|
|
374
|
-
const
|
|
375
|
-
const spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook);
|
|
376
|
-
const output = new OutputAccumulator({ tempFilePrefix: "pi-bash" });
|
|
280
|
+
const output = new OutputAccumulator({ tempFilePrefix: `pi-${toolName}` });
|
|
377
281
|
let updateTimer;
|
|
378
282
|
let updateDirty = false;
|
|
379
283
|
let lastUpdateAt = 0;
|
|
@@ -476,11 +380,14 @@ export function createBashToolDefinition(cwd, options) {
|
|
|
476
380
|
return { text, details };
|
|
477
381
|
};
|
|
478
382
|
const appendStatus = (text, status) => `${text ? `${text}\n\n` : ""}${status}`;
|
|
383
|
+
const effectiveTimeoutSeconds = typeof timeout === "number" && Number.isFinite(timeout) && timeout > 0
|
|
384
|
+
? resolveCommandTimeoutSeconds(timeout)
|
|
385
|
+
: (commandTimeoutMsOverride ?? DEFAULT_COMMAND_TIMEOUT_SECONDS * 1000) / 1000;
|
|
479
386
|
try {
|
|
480
387
|
if (canFilterCommand) {
|
|
481
|
-
const classification = classifyGitCommand(command,
|
|
388
|
+
const classification = classifyGitCommand(command, getShellEnv());
|
|
482
389
|
if (classification.eligible && classification.subcommand) {
|
|
483
|
-
const res = await executeFilteredGit(cwd, classification.subcommand, classification.globalOptions || [], classification.subcommandArgs || [], { signal, timeout });
|
|
390
|
+
const res = await executeFilteredGit(cwd, classification.subcommand, classification.globalOptions || [], classification.subcommandArgs || [], { signal, timeout: effectiveTimeoutSeconds });
|
|
484
391
|
if (res.exitCode !== -100) {
|
|
485
392
|
output.append(res.rawBytes ?? Buffer.from(res.rawOut, "utf-8"));
|
|
486
393
|
const snapshot = await finishOutput();
|
|
@@ -501,27 +408,25 @@ export function createBashToolDefinition(cwd, options) {
|
|
|
501
408
|
}
|
|
502
409
|
}
|
|
503
410
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
if (optResult.exitCode !== 0 && optResult.exitCode !== undefined) {
|
|
511
|
-
throw new Error(appendStatus(outputText, `Command exited with code ${optResult.exitCode}`));
|
|
512
|
-
}
|
|
513
|
-
return { content: [{ type: "text", text: outputText }], details };
|
|
514
|
-
}
|
|
411
|
+
let backendCommand = command;
|
|
412
|
+
if (routesWindowsContract) {
|
|
413
|
+
const route = routeShellContract(command, contractPlatform);
|
|
414
|
+
if (route.kind === "unsupported")
|
|
415
|
+
throw new Error(route.error);
|
|
416
|
+
backendCommand = route.command;
|
|
515
417
|
}
|
|
418
|
+
const commandWithPrefix = commandPrefix ? `${commandPrefix}\n${backendCommand}` : backendCommand;
|
|
419
|
+
const resolvedCommand = backendShell === "powershell" ? prefixPowerShellCommand(commandWithPrefix) : commandWithPrefix;
|
|
420
|
+
const spawnContext = resolveSpawnContext(resolvedCommand, cwd, spawnHook);
|
|
516
421
|
let exitCode;
|
|
517
422
|
try {
|
|
518
|
-
//
|
|
519
|
-
// actual
|
|
423
|
+
// Shell commands cannot statically declare which files they mutate, so the
|
|
424
|
+
// actual execution takes the coarse exclusive barrier: it waits for
|
|
520
425
|
// in-flight edit/write mutations to drain and blocks new ones meanwhile.
|
|
521
426
|
const result = await withExclusiveMutationBarrier(() => ops.exec(spawnContext.command, spawnContext.cwd, {
|
|
522
427
|
onData: handleData,
|
|
523
428
|
signal,
|
|
524
|
-
timeout,
|
|
429
|
+
timeout: effectiveTimeoutSeconds,
|
|
525
430
|
env: spawnContext.env,
|
|
526
431
|
}));
|
|
527
432
|
exitCode = result.exitCode;
|
|
@@ -538,7 +443,10 @@ export function createBashToolDefinition(cwd, options) {
|
|
|
538
443
|
}
|
|
539
444
|
if (err instanceof Error && err.message.startsWith("silence:")) {
|
|
540
445
|
const secs = err.message.split(":")[1];
|
|
541
|
-
|
|
446
|
+
const recovery = backendShell === "bash"
|
|
447
|
+
? "re-run it with an explicit timeout, or run it in the background with '&'."
|
|
448
|
+
: "re-run it with an explicit timeout.";
|
|
449
|
+
throw new Error(appendStatus(text, `Command killed after ${secs}s of silence (no output). If the command is legitimately quiet for long stretches, ${recovery}`));
|
|
542
450
|
}
|
|
543
451
|
throw err;
|
|
544
452
|
}
|
|
@@ -561,7 +469,7 @@ export function createBashToolDefinition(cwd, options) {
|
|
|
561
469
|
state.endedAt = undefined;
|
|
562
470
|
}
|
|
563
471
|
const text = context.lastComponent ?? new Text("", 0, 0);
|
|
564
|
-
text.setText(formatBashCall(args));
|
|
472
|
+
text.setText(formatBashCall(args, toolName));
|
|
565
473
|
return text;
|
|
566
474
|
},
|
|
567
475
|
renderResult(result, options, _theme, context) {
|
|
@@ -583,6 +491,10 @@ export function createBashToolDefinition(cwd, options) {
|
|
|
583
491
|
},
|
|
584
492
|
};
|
|
585
493
|
}
|
|
494
|
+
export function createBashToolDefinition(cwd, options) {
|
|
495
|
+
const platform = options?.platform ?? process.platform;
|
|
496
|
+
return createShellToolDefinition(cwd, getPlatformShellToolName(platform), platform, options);
|
|
497
|
+
}
|
|
586
498
|
export function createBashTool(cwd, options) {
|
|
587
499
|
return wrapToolDefinition(createBashToolDefinition(cwd, options));
|
|
588
500
|
}
|