@code-yeongyu/senpi 2026.8.6 → 2026.8.7
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 +34 -0
- package/dist/core/agent-session.d.ts +10 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +46 -38
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/extensions/builtin/bash-timeout/index.d.ts +2 -2
- package/dist/core/extensions/builtin/bash-timeout/index.d.ts.map +1 -1
- package/dist/core/extensions/builtin/bash-timeout/index.js +17 -20
- package/dist/core/extensions/builtin/bash-timeout/index.js.map +1 -1
- package/dist/core/extensions/builtin/bash-timeout/timeout.d.ts +7 -12
- package/dist/core/extensions/builtin/bash-timeout/timeout.d.ts.map +1 -1
- package/dist/core/extensions/builtin/bash-timeout/timeout.js +11 -19
- package/dist/core/extensions/builtin/bash-timeout/timeout.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/bash.d.ts.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/bash.js +22 -10
- package/dist/core/extensions/builtin/terminal/tools/bash.js.map +1 -1
- package/dist/core/extensions/builtin/terminal/tools/foreground-window.d.ts +11 -0
- package/dist/core/extensions/builtin/terminal/tools/foreground-window.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/tools/foreground-window.js +16 -0
- package/dist/core/extensions/builtin/terminal/tools/foreground-window.js.map +1 -0
- package/dist/core/extensions/builtin/terminal/tools/sleep-wait.d.ts +28 -0
- package/dist/core/extensions/builtin/terminal/tools/sleep-wait.d.ts.map +1 -0
- package/dist/core/extensions/builtin/terminal/tools/sleep-wait.js +75 -0
- package/dist/core/extensions/builtin/terminal/tools/sleep-wait.js.map +1 -0
- package/dist/core/extensions/builtin/tps.d.ts.map +1 -1
- package/dist/core/extensions/builtin/tps.js +3 -3
- package/dist/core/extensions/builtin/tps.js.map +1 -1
- package/dist/core/retry-fallback/hint-policy.d.ts +14 -0
- package/dist/core/retry-fallback/hint-policy.d.ts.map +1 -1
- package/dist/core/retry-fallback/hint-policy.js +14 -0
- package/dist/core/retry-fallback/hint-policy.js.map +1 -1
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +17 -0
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
- package/node_modules/@code-yeongyu/senpi-codemode/src/tool/render.ts +2 -1
- package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/huggingface.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/package.json +1 -1
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +6 -6
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "../../types.ts";
|
|
2
|
-
export type { BashTimeoutDefaults, BashToolInputLike
|
|
3
|
-
export { applyBashTimeout, BASH_DEFAULT_TIMEOUT_SECONDS, BASH_MAX_TIMEOUT_SECONDS, buildBashTimeoutPrompt, resolveBashTimeoutDefaults,
|
|
2
|
+
export type { BashTimeoutDefaults, BashToolInputLike } from "./timeout.ts";
|
|
3
|
+
export { applyBashTimeout, BASH_DEFAULT_TIMEOUT_SECONDS, BASH_MAX_TIMEOUT_SECONDS, buildBashTimeoutPrompt, resolveBashTimeoutDefaults, } from "./timeout.ts";
|
|
4
4
|
export default function bashTimeoutExtension(pi: ExtensionAPI): void;
|
|
5
5
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/bash-timeout/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,gBAAgB,CAAC;AAWrE,YAAY,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/bash-timeout/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,gBAAgB,CAAC;AAWrE,YAAY,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAC3E,OAAO,EACN,gBAAgB,EAChB,4BAA4B,EAC5B,wBAAwB,EACxB,sBAAsB,EACtB,0BAA0B,GAC1B,MAAM,cAAc,CAAC;AAEtB,MAAM,CAAC,OAAO,UAAU,oBAAoB,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CA2BnE"}
|
|
@@ -1,36 +1,33 @@
|
|
|
1
1
|
import { isAnthropicBashEnabled } from "../anthropic-bash/index.js";
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import { resolveForegroundWindowSeconds } from "../terminal/tools/foreground-window.js";
|
|
3
|
+
import { applyBashTimeout, buildBashTimeoutPrompt, resolveBashTimeoutDefaults, } from "./timeout.js";
|
|
4
|
+
export { applyBashTimeout, BASH_DEFAULT_TIMEOUT_SECONDS, BASH_MAX_TIMEOUT_SECONDS, buildBashTimeoutPrompt, resolveBashTimeoutDefaults, } from "./timeout.js";
|
|
4
5
|
export default function bashTimeoutExtension(pi) {
|
|
5
6
|
const env = typeof process !== "undefined" ? process.env : {};
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Native Anthropic bash replaces the PTY `bash` tool, and the terminal
|
|
10
|
-
* extension steps aside with it — so nothing implements the cache-deadline
|
|
11
|
-
* detach. Advertising a cache ceiling there would promise behavior that
|
|
12
|
-
* cannot happen, so the budget only applies while the PTY tool is live.
|
|
13
|
-
*/
|
|
14
|
-
const resolveBudget = (ctx) => {
|
|
15
|
-
if (isAnthropicBashEnabled() && ctx?.model?.api === "anthropic-messages")
|
|
16
|
-
return undefined;
|
|
17
|
-
return ctx?.getPromptCacheSafeWaitSeconds?.();
|
|
18
|
-
};
|
|
19
|
-
pi.on("tool_call", async (event, ctx) => {
|
|
7
|
+
const defaults = resolveBashTimeoutDefaults(env);
|
|
8
|
+
pi.on("tool_call", async (event) => {
|
|
20
9
|
if (event.toolName !== "bash")
|
|
21
10
|
return;
|
|
22
|
-
effective = resolveEffectiveBashTimeouts(baseDefaults, resolveBudget(ctx));
|
|
23
11
|
const input = event.input;
|
|
24
|
-
const updated = applyBashTimeout(input,
|
|
12
|
+
const updated = applyBashTimeout(input, defaults);
|
|
25
13
|
if (updated !== input) {
|
|
26
14
|
const timeout = updated.timeout;
|
|
27
15
|
if (timeout !== undefined)
|
|
28
16
|
input.timeout = timeout;
|
|
29
17
|
}
|
|
30
18
|
});
|
|
19
|
+
/**
|
|
20
|
+
* Native Anthropic bash replaces the PTY `bash` tool and the terminal
|
|
21
|
+
* extension steps aside with it, so nothing implements the auto-detach the
|
|
22
|
+
* policy would otherwise advertise.
|
|
23
|
+
*/
|
|
24
|
+
const resolveWindow = (ctx) => {
|
|
25
|
+
if (isAnthropicBashEnabled() && ctx?.model?.api === "anthropic-messages")
|
|
26
|
+
return undefined;
|
|
27
|
+
return resolveForegroundWindowSeconds(env);
|
|
28
|
+
};
|
|
31
29
|
pi.on("before_agent_start", async (event, ctx) => {
|
|
32
|
-
|
|
33
|
-
return { systemPrompt: `${event.systemPrompt}${buildBashTimeoutPrompt(effective)}` };
|
|
30
|
+
return { systemPrompt: `${event.systemPrompt}${buildBashTimeoutPrompt(defaults, resolveWindow(ctx))}` };
|
|
34
31
|
});
|
|
35
32
|
}
|
|
36
33
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/bash-timeout/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/bash-timeout/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,8BAA8B,EAAE,MAAM,wCAAwC,CAAC;AAExF,OAAO,EACN,gBAAgB,EAEhB,sBAAsB,EACtB,0BAA0B,GAC1B,MAAM,cAAc,CAAC;AAGtB,OAAO,EACN,gBAAgB,EAChB,4BAA4B,EAC5B,wBAAwB,EACxB,sBAAsB,EACtB,0BAA0B,GAC1B,MAAM,cAAc,CAAC;AAEtB,MAAM,CAAC,OAAO,UAAU,oBAAoB,CAAC,EAAgB;IAC5D,MAAM,GAAG,GAAG,OAAO,OAAO,KAAK,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9D,MAAM,QAAQ,GAAG,0BAA0B,CAAC,GAAG,CAAC,CAAC;IAEjD,EAAE,CAAC,EAAE,CAAC,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QAClC,IAAI,KAAK,CAAC,QAAQ,KAAK,MAAM;YAAE,OAAO;QACtC,MAAM,KAAK,GAAG,KAAK,CAAC,KAA0B,CAAC;QAC/C,MAAM,OAAO,GAAG,gBAAgB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QAClD,IAAI,OAAO,KAAK,KAAK,EAAE,CAAC;YACvB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;YAChC,IAAI,OAAO,KAAK,SAAS;gBAAE,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC;QACpD,CAAC;IACF,CAAC,CAAC,CAAC;IAEH;;;;OAIG;IACH,MAAM,aAAa,GAAG,CAAC,GAAiC,EAAsB,EAAE;QAC/E,IAAI,sBAAsB,EAAE,IAAI,GAAG,EAAE,KAAK,EAAE,GAAG,KAAK,oBAAoB;YAAE,OAAO,SAAS,CAAC;QAC3F,OAAO,8BAA8B,CAAC,GAAG,CAAC,CAAC;IAC5C,CAAC,CAAC;IAEF,EAAE,CAAC,EAAE,CAAC,oBAAoB,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAChD,OAAO,EAAE,YAAY,EAAE,GAAG,KAAK,CAAC,YAAY,GAAG,sBAAsB,CAAC,QAAQ,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC;IACzG,CAAC,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import type { ExtensionAPI, ExtensionContext } from \"../../types.ts\";\nimport { isAnthropicBashEnabled } from \"../anthropic-bash/index.ts\";\nimport { resolveForegroundWindowSeconds } from \"../terminal/tools/foreground-window.ts\";\n\nimport {\n\tapplyBashTimeout,\n\ttype BashToolInputLike,\n\tbuildBashTimeoutPrompt,\n\tresolveBashTimeoutDefaults,\n} from \"./timeout.ts\";\n\nexport type { BashTimeoutDefaults, BashToolInputLike } from \"./timeout.ts\";\nexport {\n\tapplyBashTimeout,\n\tBASH_DEFAULT_TIMEOUT_SECONDS,\n\tBASH_MAX_TIMEOUT_SECONDS,\n\tbuildBashTimeoutPrompt,\n\tresolveBashTimeoutDefaults,\n} from \"./timeout.ts\";\n\nexport default function bashTimeoutExtension(pi: ExtensionAPI): void {\n\tconst env = typeof process !== \"undefined\" ? process.env : {};\n\tconst defaults = resolveBashTimeoutDefaults(env);\n\n\tpi.on(\"tool_call\", async (event) => {\n\t\tif (event.toolName !== \"bash\") return;\n\t\tconst input = event.input as BashToolInputLike;\n\t\tconst updated = applyBashTimeout(input, defaults);\n\t\tif (updated !== input) {\n\t\t\tconst timeout = updated.timeout;\n\t\t\tif (timeout !== undefined) input.timeout = timeout;\n\t\t}\n\t});\n\n\t/**\n\t * Native Anthropic bash replaces the PTY `bash` tool and the terminal\n\t * extension steps aside with it, so nothing implements the auto-detach the\n\t * policy would otherwise advertise.\n\t */\n\tconst resolveWindow = (ctx: ExtensionContext | undefined): number | undefined => {\n\t\tif (isAnthropicBashEnabled() && ctx?.model?.api === \"anthropic-messages\") return undefined;\n\t\treturn resolveForegroundWindowSeconds(env);\n\t};\n\n\tpi.on(\"before_agent_start\", async (event, ctx) => {\n\t\treturn { systemPrompt: `${event.systemPrompt}${buildBashTimeoutPrompt(defaults, resolveWindow(ctx))}` };\n\t});\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export declare const BASH_DEFAULT_TIMEOUT_SECONDS =
|
|
2
|
-
export declare const BASH_MAX_TIMEOUT_SECONDS =
|
|
1
|
+
export declare const BASH_DEFAULT_TIMEOUT_SECONDS = 1800;
|
|
2
|
+
export declare const BASH_MAX_TIMEOUT_SECONDS = 1800;
|
|
3
3
|
export interface BashTimeoutDefaults {
|
|
4
4
|
defaultSeconds: number;
|
|
5
5
|
maxSeconds: number;
|
|
@@ -12,17 +12,12 @@ export interface BashToolInputLike {
|
|
|
12
12
|
[key: string]: unknown;
|
|
13
13
|
}
|
|
14
14
|
export declare function applyBashTimeout<TInput extends BashToolInputLike>(input: TInput, defaults: BashTimeoutDefaults): TInput;
|
|
15
|
-
export interface EffectiveBashTimeouts extends BashTimeoutDefaults {
|
|
16
|
-
cacheCapped?: boolean;
|
|
17
|
-
}
|
|
18
15
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* value, and the terminal detach deadline consistent.
|
|
16
|
+
* `foregroundWindowSeconds` is `undefined` when no PTY bash tool is live (native
|
|
17
|
+
* Anthropic bash replaces it). Nothing implements auto-detach then, so the
|
|
18
|
+
* policy must not promise it — a prompt that describes impossible behavior is
|
|
19
|
+
* worse than a silent one.
|
|
24
20
|
*/
|
|
25
|
-
export declare function
|
|
26
|
-
export declare function buildBashTimeoutPrompt(defaults: EffectiveBashTimeouts): string;
|
|
21
|
+
export declare function buildBashTimeoutPrompt(defaults: BashTimeoutDefaults, foregroundWindowSeconds?: number): string;
|
|
27
22
|
export {};
|
|
28
23
|
//# sourceMappingURL=timeout.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeout.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/bash-timeout/timeout.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,4BAA4B,
|
|
1
|
+
{"version":3,"file":"timeout.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/bash-timeout/timeout.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,4BAA4B,OAAO,CAAC;AACjD,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAE7C,MAAM,WAAW,mBAAmB;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,KAAK,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AASlD,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,OAAO,GAAG,mBAAmB,CAM5E;AAED,MAAM,WAAW,iBAAiB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED,wBAAgB,gBAAgB,CAAC,MAAM,SAAS,iBAAiB,EAChE,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,mBAAmB,GAC3B,MAAM,CAMR;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,mBAAmB,EAAE,uBAAuB,CAAC,EAAE,MAAM,GAAG,MAAM,CAO9G"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export const BASH_DEFAULT_TIMEOUT_SECONDS =
|
|
2
|
-
export const BASH_MAX_TIMEOUT_SECONDS =
|
|
1
|
+
export const BASH_DEFAULT_TIMEOUT_SECONDS = 1800;
|
|
2
|
+
export const BASH_MAX_TIMEOUT_SECONDS = 1800;
|
|
3
3
|
function parsePositiveInt(value) {
|
|
4
4
|
if (!value)
|
|
5
5
|
return undefined;
|
|
@@ -23,24 +23,16 @@ export function applyBashTimeout(input, defaults) {
|
|
|
23
23
|
return input;
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* value, and the terminal detach deadline consistent.
|
|
26
|
+
* `foregroundWindowSeconds` is `undefined` when no PTY bash tool is live (native
|
|
27
|
+
* Anthropic bash replaces it). Nothing implements auto-detach then, so the
|
|
28
|
+
* policy must not promise it — a prompt that describes impossible behavior is
|
|
29
|
+
* worse than a silent one.
|
|
31
30
|
*/
|
|
32
|
-
export function
|
|
33
|
-
if (safeWaitSeconds === undefined || safeWaitSeconds >= defaults.maxSeconds) {
|
|
34
|
-
return { ...defaults, cacheCapped: false };
|
|
35
|
-
}
|
|
36
|
-
const maxSeconds = Math.min(defaults.maxSeconds, safeWaitSeconds);
|
|
37
|
-
return { defaultSeconds: Math.min(defaults.defaultSeconds, maxSeconds), maxSeconds, cacheCapped: true };
|
|
38
|
-
}
|
|
39
|
-
export function buildBashTimeoutPrompt(defaults) {
|
|
31
|
+
export function buildBashTimeoutPrompt(defaults, foregroundWindowSeconds) {
|
|
40
32
|
const minutes = (seconds) => (seconds % 60 === 0 ? `${seconds / 60} min` : `${seconds}s`);
|
|
41
|
-
const
|
|
42
|
-
?
|
|
43
|
-
:
|
|
44
|
-
return `\n## Bash Tool Timeout Policy\n\nThe \`bash\` tool
|
|
33
|
+
const detachRules = foregroundWindowSeconds === undefined
|
|
34
|
+
? ""
|
|
35
|
+
: `\n- Foreground blocking stops at the ~${foregroundWindowSeconds}s window. A command still running then auto-detaches alive to a background session with a \`bash_id\` and keeps running until it exits, hits the kill deadline, or is stopped with \`kill_bash\`.\n- Completion arrives automatically as a notification carrying the exit status and output tail, so end your turn rather than poll. Use \`bash_output\` only for a midpoint peek.`;
|
|
36
|
+
return `\n## Bash Tool Timeout Policy\n\nThe \`bash\` tool's \`timeout\` parameter is the process kill deadline, not how long you wait for output: the command is killed when it reaches the deadline.\n\n- Default timeout: ${defaults.defaultSeconds}s (${minutes(defaults.defaultSeconds)}). Applied automatically when you do not set \`timeout\`.\n- Recommended maximum timeout: ${defaults.maxSeconds}s (${minutes(defaults.maxSeconds)}). Explicit \`timeout\` values are preserved because different hosts may use different timeout units.${detachRules}\n- Waiting on an observable condition (a log line, a CI check, a server coming up) belongs to \`monitor({command, filter})\`, never a foreground \`sleep\` or poll loop.\n- Sessions started with \`run_in_background: true\` ignore \`timeout\` and live until exit or \`kill_bash\`.\n`;
|
|
45
37
|
}
|
|
46
38
|
//# sourceMappingURL=timeout.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeout.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/bash-timeout/timeout.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,4BAA4B,GAAG,
|
|
1
|
+
{"version":3,"file":"timeout.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/bash-timeout/timeout.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,CAAC;AACjD,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC;AAS7C,SAAS,gBAAgB,CAAC,KAAyB;IAClD,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC1C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAC9D,OAAO,MAAM,CAAC;AACf,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,GAAY;IACtD,MAAM,EAAE,+BAA+B,EAAE,cAAc,EAAE,2BAA2B,EAAE,UAAU,EAAE,GAAG,GAAG,CAAC;IACzG,MAAM,cAAc,GAAG,gBAAgB,CAAC,cAAc,CAAC,IAAI,4BAA4B,CAAC;IACxF,MAAM,MAAM,GAAG,gBAAgB,CAAC,UAAU,CAAC,IAAI,wBAAwB,CAAC;IACxE,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACpD,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,CAAC;AACvC,CAAC;AAQD,MAAM,UAAU,gBAAgB,CAC/B,KAAa,EACb,QAA6B;IAE7B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IAC9B,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;QAC3C,OAAO,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,cAAc,EAAE,CAAC;IACvD,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,QAA6B,EAAE,uBAAgC;IACrG,MAAM,OAAO,GAAG,CAAC,OAAe,EAAU,EAAE,CAAC,CAAC,OAAO,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC;IAC1G,MAAM,WAAW,GAChB,uBAAuB,KAAK,SAAS;QACpC,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,yCAAyC,uBAAuB,oXAAoX,CAAC;IACzb,OAAO,wNAAwN,QAAQ,CAAC,cAAc,MAAM,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,6FAA6F,QAAQ,CAAC,UAAU,MAAM,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,wGAAwG,WAAW,2RAA2R,CAAC;AAC9zB,CAAC","sourcesContent":["export const BASH_DEFAULT_TIMEOUT_SECONDS = 1800;\nexport const BASH_MAX_TIMEOUT_SECONDS = 1800;\n\nexport interface BashTimeoutDefaults {\n\tdefaultSeconds: number;\n\tmaxSeconds: number;\n}\n\ntype EnvLike = Record<string, string | undefined>;\n\nfunction parsePositiveInt(value: string | undefined): number | undefined {\n\tif (!value) return undefined;\n\tconst parsed = Number.parseInt(value, 10);\n\tif (!Number.isFinite(parsed) || parsed <= 0) return undefined;\n\treturn parsed;\n}\n\nexport function resolveBashTimeoutDefaults(env: EnvLike): BashTimeoutDefaults {\n\tconst { PI_BASH_DEFAULT_TIMEOUT_SECONDS: defaultTimeout, PI_BASH_MAX_TIMEOUT_SECONDS: maxTimeout } = env;\n\tconst defaultSeconds = parsePositiveInt(defaultTimeout) ?? BASH_DEFAULT_TIMEOUT_SECONDS;\n\tconst rawMax = parsePositiveInt(maxTimeout) ?? BASH_MAX_TIMEOUT_SECONDS;\n\tconst maxSeconds = Math.max(rawMax, defaultSeconds);\n\treturn { defaultSeconds, maxSeconds };\n}\n\nexport interface BashToolInputLike {\n\tcommand: string;\n\ttimeout?: number;\n\t[key: string]: unknown;\n}\n\nexport function applyBashTimeout<TInput extends BashToolInputLike>(\n\tinput: TInput,\n\tdefaults: BashTimeoutDefaults,\n): TInput {\n\tconst current = input.timeout;\n\tif (current === undefined || current <= 0) {\n\t\treturn { ...input, timeout: defaults.defaultSeconds };\n\t}\n\treturn input;\n}\n\n/**\n * `foregroundWindowSeconds` is `undefined` when no PTY bash tool is live (native\n * Anthropic bash replaces it). Nothing implements auto-detach then, so the\n * policy must not promise it — a prompt that describes impossible behavior is\n * worse than a silent one.\n */\nexport function buildBashTimeoutPrompt(defaults: BashTimeoutDefaults, foregroundWindowSeconds?: number): string {\n\tconst minutes = (seconds: number): string => (seconds % 60 === 0 ? `${seconds / 60} min` : `${seconds}s`);\n\tconst detachRules =\n\t\tforegroundWindowSeconds === undefined\n\t\t\t? \"\"\n\t\t\t: `\\n- Foreground blocking stops at the ~${foregroundWindowSeconds}s window. A command still running then auto-detaches alive to a background session with a \\`bash_id\\` and keeps running until it exits, hits the kill deadline, or is stopped with \\`kill_bash\\`.\\n- Completion arrives automatically as a notification carrying the exit status and output tail, so end your turn rather than poll. Use \\`bash_output\\` only for a midpoint peek.`;\n\treturn `\\n## Bash Tool Timeout Policy\\n\\nThe \\`bash\\` tool's \\`timeout\\` parameter is the process kill deadline, not how long you wait for output: the command is killed when it reaches the deadline.\\n\\n- Default timeout: ${defaults.defaultSeconds}s (${minutes(defaults.defaultSeconds)}). Applied automatically when you do not set \\`timeout\\`.\\n- Recommended maximum timeout: ${defaults.maxSeconds}s (${minutes(defaults.maxSeconds)}). Explicit \\`timeout\\` values are preserved because different hosts may use different timeout units.${detachRules}\\n- Waiting on an observable condition (a log line, a CI check, a server coming up) belongs to \\`monitor({command, filter})\\`, never a foreground \\`sleep\\` or poll loop.\\n- Sessions started with \\`run_in_background: true\\` ignore \\`timeout\\` and live until exit or \\`kill_bash\\`.\\n`;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/terminal/tools/bash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAWjE,OAAO,EAAe,KAAK,mBAAmB,EAAE,KAAK,kBAAkB,EAAc,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/terminal/tools/bash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAWjE,OAAO,EAAe,KAAK,mBAAmB,EAAE,KAAK,kBAAkB,EAAc,MAAM,cAAc,CAAC;AAM1G,eAAO,MAAM,aAAa;;;;;;;EAiBxB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,aAAa,CAAC,CAAC;AAcxD,MAAM,WAAW,gBAAgB;IAChC,QAAQ,IAAI,IAAI,CAAC;IACjB,KAAK,IAAI,IAAI,CAAC;IACd,OAAO,IAAI,IAAI,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,IAAI,EAAE,UAAU,SAA0B,GAAG,gBAAgB,CA2C/G;AAID,KAAK,gBAAgB,GAAG;IACvB,cAAc,CAAC,EAAE;QAAE,YAAY,EAAE,MAAM,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IAC1F,KAAK,CAAC,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,GAAG,CAAC,EAAE,MAAM,CAAC;CACb,CAAC;AAwRF,mDAAmD;AACnD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,mBAAmB;;;;;;;;;;;;;;yBAU1C,MAAM,SACZ,YAAY,WACV,WAAW,aACT,uBAAuB,YACxB,gBAAgB,GACxB,OAAO,CAAC,kBAAkB,CAAC;EAQ/B"}
|
|
@@ -3,11 +3,13 @@ import { formatTerminalToolOutput } from "../output-format.js";
|
|
|
3
3
|
import { BACKGROUND_START_GRACE_MS, DEFAULT_COLS, DEFAULT_ROWS, FOREGROUND_ENV_OVERRIDES, KILLED_SESSION_EXIT_GRACE_MS, TERMINAL_BASH_TOOL, } from "../shared.js";
|
|
4
4
|
import { errorResult, textResult } from "./context.js";
|
|
5
5
|
import { createForegroundDetachGate } from "./foreground-detach.js";
|
|
6
|
+
import { resolveForegroundWindowSeconds, SLEEP_WAIT_WINDOW_SECONDS } from "./foreground-window.js";
|
|
7
|
+
import { classifySleepWait } from "./sleep-wait.js";
|
|
6
8
|
import { describeExit, spawnCommandSession } from "./spawn.js";
|
|
7
9
|
export const ptyBashSchema = Type.Object({
|
|
8
10
|
command: Type.String({ description: "Shell command to execute in a PTY-backed session." }),
|
|
9
11
|
timeout: Type.Optional(Type.Number({
|
|
10
|
-
description: "
|
|
12
|
+
description: "Process kill deadline in seconds (default 1800). Foreground blocking stops at the ~60s window: a command still running then auto-detaches to a live background session instead of being killed. Ignored for run_in_background sessions (they live until exit or kill_bash).",
|
|
11
13
|
})),
|
|
12
14
|
description: Type.Optional(Type.String({ description: "Short human-readable label for this command." })),
|
|
13
15
|
run_in_background: Type.Optional(Type.Boolean({
|
|
@@ -134,16 +136,21 @@ function raceExitWithKillGrace(runtime, signal, timeoutMs) {
|
|
|
134
136
|
}
|
|
135
137
|
});
|
|
136
138
|
}
|
|
137
|
-
|
|
139
|
+
/**
|
|
140
|
+
* How long the model blocks before a still-running command is handed to a live
|
|
141
|
+
* background session. A sleep-wait detaches almost immediately: its remaining
|
|
142
|
+
* time is pure waiting, which the completion notification delivers for free.
|
|
143
|
+
*/
|
|
144
|
+
function resolveAutoDetachDelayMs(ctx, input, sleepWait) {
|
|
138
145
|
if (ctx.timeoutAction === "kill")
|
|
139
146
|
return undefined;
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
147
|
+
const windowSeconds = sleepWait
|
|
148
|
+
? Math.min(SLEEP_WAIT_WINDOW_SECONDS, resolveForegroundWindowSeconds(process.env))
|
|
149
|
+
: resolveForegroundWindowSeconds(process.env);
|
|
143
150
|
const timeoutSeconds = input.timeout !== undefined && Number.isFinite(input.timeout) ? input.timeout : Infinity;
|
|
144
|
-
if (timeoutSeconds <=
|
|
151
|
+
if (timeoutSeconds <= windowSeconds)
|
|
145
152
|
return undefined;
|
|
146
|
-
return Math.max(0, Math.trunc(
|
|
153
|
+
return Math.max(0, Math.trunc(windowSeconds * 1000));
|
|
147
154
|
}
|
|
148
155
|
function scheduleDetachedSweep(ctx, id, runtime, timeoutMs, startedAt) {
|
|
149
156
|
if (timeoutMs === undefined)
|
|
@@ -193,7 +200,8 @@ async function runForeground(ctx, input, cols, rows, signal, cwd, onUpdate, exec
|
|
|
193
200
|
const updateEmitter = onUpdate ? createThrottledEmitter(emitOutputUpdate) : undefined;
|
|
194
201
|
onUpdate?.({ content: [], details: undefined });
|
|
195
202
|
const unsubscribeOutput = onUpdate ? runtime.onOutput(() => updateEmitter?.schedule()) : undefined;
|
|
196
|
-
const
|
|
203
|
+
const sleepWait = classifySleepWait(input.command);
|
|
204
|
+
const detachDelayMs = resolveAutoDetachDelayMs(ctx, input, sleepWait);
|
|
197
205
|
let outcome;
|
|
198
206
|
if (detachDelayMs === undefined) {
|
|
199
207
|
// Interrupt means "stop now": SIGKILL the whole process group in one shot.
|
|
@@ -257,11 +265,15 @@ async function runForeground(ctx, input, cols, rows, signal, cwd, onUpdate, exec
|
|
|
257
265
|
const timeoutNote = input.timeout !== undefined && Number.isFinite(input.timeout)
|
|
258
266
|
? `not killed; the original ${input.timeout}s timeout still applies`
|
|
259
267
|
: "not killed; it will run until exit or kill_bash";
|
|
260
|
-
|
|
268
|
+
const guidance = sleepWait
|
|
269
|
+
? `This command is a wait (${sleepWait.seconds}s sleep), so it detached immediately; the wait continues in the background. Do nothing and end your turn — completion will be reported automatically with exit status and output tail. Do NOT poll bash_output({ bash_id: "${id}" }) for it. When you are waiting for a pattern in a command's output, launch it with monitor({ command, filter }) instead so matching lines arrive as events. Use kill_bash({ bash_id: "${id}" }) to stop this session.`
|
|
270
|
+
: `Continue other work; completion will be reported automatically with exit status and output tail. Use bash_output({ bash_id: "${id}" }) only to peek at new output. monitor cannot attach to this session; use it for future event-driven launches. Use kill_bash({ bash_id: "${id}" }) to stop this session.`;
|
|
271
|
+
return textResult(`Command is still running; auto-detached to background with ID: ${id} (${timeoutNote}).\n\nPartial output:\n${partialOutput}\n\n${guidance}`, {
|
|
261
272
|
details: {
|
|
262
273
|
bash_id: id,
|
|
263
274
|
background: true,
|
|
264
275
|
auto_detached: true,
|
|
276
|
+
...(sleepWait ? { sleep_wait: true } : {}),
|
|
265
277
|
status: "running",
|
|
266
278
|
...(delta.droppedChars > 0 ? { droppedChars: delta.droppedChars } : {}),
|
|
267
279
|
},
|
|
@@ -322,7 +334,7 @@ export function createPtyBashTool(ctx) {
|
|
|
322
334
|
return {
|
|
323
335
|
name: TERMINAL_BASH_TOOL,
|
|
324
336
|
label: "bash",
|
|
325
|
-
description: "Execute a shell command in a persistent PTY-backed session. Set run_in_background:true for long-lived or interactive sessions; steer them with bash_input, snapshot with bash_output, tear down with kill_bash. To wait on observable state (a build finishing, a server coming up, a log line), never run sleep or poll loops — subscribe with the monitor tool instead. Foreground timeout is
|
|
337
|
+
description: "Execute a shell command in a persistent PTY-backed session. Set run_in_background:true for long-lived or interactive sessions; steer them with bash_input, snapshot with bash_output, tear down with kill_bash. To wait on observable state (a build finishing, a server coming up, a log line), never run sleep or poll loops — subscribe with the monitor tool instead. Foreground blocking stops at the ~60s window and a still-running command auto-detaches to a live background session; `timeout` is the process kill deadline in seconds.",
|
|
326
338
|
promptSnippet: "Run shell commands; run_in_background:true for long-lived/interactive PTY sessions",
|
|
327
339
|
promptGuidelines: ["Inspect PI_* environment variables for current model and session details."],
|
|
328
340
|
parameters: ptyBashSchema,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bash.js","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/terminal/tools/bash.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Static, Type } from \"typebox\";\nimport type { AgentToolUpdateCallback } from \"../../../types.ts\";\nimport { formatTerminalToolOutput } from \"../output-format.ts\";\nimport type { TerminalRuntimeSession } from \"../runtime-session.ts\";\nimport {\n\tBACKGROUND_START_GRACE_MS,\n\tDEFAULT_COLS,\n\tDEFAULT_ROWS,\n\tFOREGROUND_ENV_OVERRIDES,\n\tKILLED_SESSION_EXIT_GRACE_MS,\n\tTERMINAL_BASH_TOOL,\n} from \"../shared.ts\";\nimport { errorResult, type TerminalToolContext, type TerminalToolResult, textResult } from \"./context.ts\";\nimport { createForegroundDetachGate } from \"./foreground-detach.ts\";\nimport { describeExit, spawnCommandSession } from \"./spawn.ts\";\n\nexport const ptyBashSchema = Type.Object({\n\tcommand: Type.String({ description: \"Shell command to execute in a PTY-backed session.\" }),\n\ttimeout: Type.Optional(\n\t\tType.Number({\n\t\t\tdescription:\n\t\t\t\t\"Foreground kill deadline in seconds. Ignored for run_in_background sessions (they live until exit or kill_bash).\",\n\t\t}),\n\t),\n\tdescription: Type.Optional(Type.String({ description: \"Short human-readable label for this command.\" })),\n\trun_in_background: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"Run as a persistent background session. Returns a bash_id immediately; query it with bash_output, steer with bash_input, tear down with kill_bash.\",\n\t\t}),\n\t),\n\tcols: Type.Optional(Type.Number({ description: \"PTY width in columns (default 120).\" })),\n\trows: Type.Optional(Type.Number({ description: \"PTY height in rows (default 40).\" })),\n});\n\nexport type PtyBashInput = Static<typeof ptyBashSchema>;\n\nfunction resolveDimension(value: number | undefined, fallback: number): number {\n\tif (value === undefined || !Number.isFinite(value) || value < 1) return fallback;\n\treturn Math.trunc(value);\n}\n\nfunction delay(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nconst MAX_TIMEOUT_MS = 2_147_483_647;\nconst BASH_UPDATE_THROTTLE_MS = 100;\n\nexport interface ThrottledEmitter {\n\tschedule(): void;\n\tflush(): void;\n\tdispose(): void;\n}\n\n/**\n * Emit immediately, then coalesce subsequent schedules into one trailing update.\n * `dispose` intentionally discards a pending update so callers can stop cleanly.\n */\nexport function createThrottledEmitter(emit: () => void, throttleMs = BASH_UPDATE_THROTTLE_MS): ThrottledEmitter {\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tlet dirty = false;\n\tlet lastEmissionAt: number | undefined;\n\n\tconst emitIfDirty = () => {\n\t\tif (!dirty) return;\n\t\tdirty = false;\n\t\tlastEmissionAt = Date.now();\n\t\temit();\n\t};\n\n\tconst clearTimer = () => {\n\t\tif (timer !== undefined) {\n\t\t\tclearTimeout(timer);\n\t\t\ttimer = undefined;\n\t\t}\n\t};\n\n\treturn {\n\t\tschedule() {\n\t\t\tdirty = true;\n\t\t\tif (timer !== undefined) return;\n\t\t\tconst elapsed = lastEmissionAt === undefined ? throttleMs : Date.now() - lastEmissionAt;\n\t\t\tif (elapsed >= throttleMs) {\n\t\t\t\temitIfDirty();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttimer = setTimeout(() => {\n\t\t\t\ttimer = undefined;\n\t\t\t\temitIfDirty();\n\t\t\t}, throttleMs - elapsed);\n\t\t},\n\t\tflush() {\n\t\t\tclearTimer();\n\t\t\temitIfDirty();\n\t\t},\n\t\tdispose() {\n\t\t\tclearTimer();\n\t\t\tdirty = false;\n\t\t\tlastEmissionAt = undefined;\n\t\t},\n\t};\n}\n\ntype ForegroundWaitOutcome = \"exited\" | \"abort_grace\" | \"timeout_grace\";\n\ntype SessionEnvSource = {\n\tsessionManager?: { getSessionId: () => string; getSessionFile: () => string | undefined };\n\tmodel?: { provider: string; id: string };\n\tthinkingLevel?: string;\n\tcwd?: string;\n};\n\nfunction sessionEnvOverrides(ctx: TerminalToolContext, execCtx?: SessionEnvSource): Record<string, string> {\n\tconst session = execCtx ?? ctx.getSessionContext?.();\n\tif (!session?.sessionManager) return {};\n\tconst env: Record<string, string> = {};\n\tenv.PI_SESSION_ID = session.sessionManager.getSessionId();\n\tconst sessionFile = session.sessionManager.getSessionFile();\n\tif (sessionFile) env.PI_SESSION_FILE = sessionFile;\n\tif (session.model) {\n\t\tenv.PI_PROVIDER = session.model.provider;\n\t\tenv.PI_MODEL = session.model.id;\n\t}\n\tif (session.thinkingLevel) env.PI_REASONING_LEVEL = session.thinkingLevel;\n\treturn env;\n}\n\n/**\n * Wait for the session's exit to settle, but stop waiting a bounded grace after\n * the session has been killed. The native wait joins the PTY reader thread,\n * which blocks while any surviving descendant holds the PTY open — without the\n * grace, an aborted or timed-out command can pin the agent forever.\n */\nfunction raceExitWithKillGrace(\n\truntime: TerminalRuntimeSession,\n\tsignal: AbortSignal | undefined,\n\ttimeoutMs: number | undefined,\n): Promise<ForegroundWaitOutcome> {\n\treturn new Promise((resolve, reject) => {\n\t\tlet settled = false;\n\t\tlet abortGraceTimer: ReturnType<typeof setTimeout> | undefined;\n\t\tlet timeoutGraceTimer: ReturnType<typeof setTimeout> | undefined;\n\t\tlet armAbortGrace: (() => void) | undefined;\n\t\tconst settle = (finish: () => void) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tif (abortGraceTimer) clearTimeout(abortGraceTimer);\n\t\t\tif (timeoutGraceTimer) clearTimeout(timeoutGraceTimer);\n\t\t\tif (signal && armAbortGrace) signal.removeEventListener(\"abort\", armAbortGrace);\n\t\t\tfinish();\n\t\t};\n\t\truntime.session.waitExit().then(\n\t\t\t() => settle(() => resolve(\"exited\")),\n\t\t\t(error: unknown) => settle(() => reject(error instanceof Error ? error : new Error(String(error)))),\n\t\t);\n\t\tif (timeoutMs !== undefined) {\n\t\t\tconst timeoutGraceDeadlineMs = timeoutMs + KILLED_SESSION_EXIT_GRACE_MS;\n\t\t\t// Beyond the 32-bit setTimeout range the deadline cannot be represented;\n\t\t\t// keep the unbounded wait rather than firing a false early timeout.\n\t\t\tif (timeoutGraceDeadlineMs <= MAX_TIMEOUT_MS) {\n\t\t\t\ttimeoutGraceTimer = setTimeout(() => settle(() => resolve(\"timeout_grace\")), timeoutGraceDeadlineMs);\n\t\t\t}\n\t\t}\n\t\tif (signal) {\n\t\t\tarmAbortGrace = () => {\n\t\t\t\tabortGraceTimer ??= setTimeout(() => settle(() => resolve(\"abort_grace\")), KILLED_SESSION_EXIT_GRACE_MS);\n\t\t\t};\n\t\t\tif (signal.aborted) armAbortGrace();\n\t\t\telse signal.addEventListener(\"abort\", armAbortGrace, { once: true });\n\t\t}\n\t});\n}\n\nfunction resolveAutoDetachDelayMs(ctx: TerminalToolContext, input: PtyBashInput): number | undefined {\n\tif (ctx.timeoutAction === \"kill\") return undefined;\n\tconst budgetSeconds = ctx.getSessionContext?.()?.getPromptCacheSafeWaitSeconds?.();\n\tif (budgetSeconds === undefined || !Number.isFinite(budgetSeconds)) return undefined;\n\tconst timeoutSeconds = input.timeout !== undefined && Number.isFinite(input.timeout) ? input.timeout : Infinity;\n\tif (timeoutSeconds <= budgetSeconds) return undefined;\n\treturn Math.max(0, Math.trunc(budgetSeconds * 1000));\n}\n\nfunction scheduleDetachedSweep(\n\tctx: TerminalToolContext,\n\tid: string,\n\truntime: TerminalRuntimeSession,\n\ttimeoutMs: number | undefined,\n\tstartedAt: number,\n): void {\n\tif (timeoutMs === undefined) return;\n\tconst deadlineMs = timeoutMs + KILLED_SESSION_EXIT_GRACE_MS;\n\tif (!Number.isFinite(deadlineMs) || deadlineMs > MAX_TIMEOUT_MS || runtime.exited) return;\n\n\tconst remainingMs = Math.max(0, deadlineMs - (Date.now() - startedAt));\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tlet unsubscribeExit: (() => void) | undefined;\n\tconst clearSweep = () => {\n\t\tif (timer !== undefined) clearTimeout(timer);\n\t\ttimer = undefined;\n\t\tunsubscribeExit?.();\n\t\tunsubscribeExit = undefined;\n\t};\n\tunsubscribeExit = runtime.session.onExit(clearSweep);\n\tif (runtime.exited) {\n\t\tclearSweep();\n\t\treturn;\n\t}\n\ttimer = setTimeout(() => {\n\t\tclearSweep();\n\t\tif (!runtime.exited) void ctx.manager.stop(id).catch(() => {});\n\t}, remainingMs);\n}\n\nasync function runForeground(\n\tctx: TerminalToolContext,\n\tinput: PtyBashInput,\n\tcols: number,\n\trows: number,\n\tsignal: AbortSignal | undefined,\n\tcwd: string | undefined,\n\tonUpdate: AgentToolUpdateCallback | undefined,\n\texecCtx?: SessionEnvSource,\n): Promise<TerminalToolResult> {\n\tif (signal?.aborted) return errorResult(\"Command aborted\");\n\tconst timeoutMs = input.timeout !== undefined ? Math.trunc(input.timeout * 1000) : undefined;\n\tconst { id, runtime } = await spawnCommandSession(ctx, {\n\t\tcommand: input.command,\n\t\tcols,\n\t\trows,\n\t\ttimeoutMs,\n\t\tcwd,\n\t\tenvOverrides: { ...sessionEnvOverrides(ctx, execCtx), ...FOREGROUND_ENV_OVERRIDES },\n\t});\n\tconst startedAt = Date.now();\n\tconst activity = `running ${input.command.slice(0, 80)}`;\n\tconst emitOutputUpdate = () => {\n\t\tconst text = formatTerminalToolOutput(runtime.fullOutput()).text.slice(-2000);\n\t\tonUpdate?.({ content: [{ type: \"text\", text }], details: { progress: { activity, startedAt } } });\n\t};\n\tconst updateEmitter = onUpdate ? createThrottledEmitter(emitOutputUpdate) : undefined;\n\tonUpdate?.({ content: [], details: undefined });\n\tconst unsubscribeOutput = onUpdate ? runtime.onOutput(() => updateEmitter?.schedule()) : undefined;\n\n\tconst detachDelayMs = resolveAutoDetachDelayMs(ctx, input);\n\tlet outcome: ForegroundWaitOutcome | \"detached\";\n\tif (detachDelayMs === undefined) {\n\t\t// Interrupt means \"stop now\": SIGKILL the whole process group in one shot.\n\t\t// kill() is one-shot idempotent, so a gentle SIGTERM first would block any\n\t\t// escalation, and a SIGTERM-ignoring command would pin the agent forever.\n\t\tconst onAbort = () => runtime.session.kill(\"SIGKILL\");\n\t\tif (signal) {\n\t\t\tif (signal.aborted) onAbort();\n\t\t\telse signal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t}\n\t\ttry {\n\t\t\toutcome = await raceExitWithKillGrace(runtime, signal, timeoutMs);\n\t\t} finally {\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\tunsubscribeOutput?.();\n\t\t\tupdateEmitter?.flush();\n\t\t\tupdateEmitter?.dispose();\n\t\t}\n\t} else {\n\t\tconst abortGate = new AbortController();\n\t\tconst onAbort = () => {\n\t\t\truntime.session.kill(\"SIGKILL\");\n\t\t\tabortGate.abort();\n\t\t};\n\t\tif (signal) {\n\t\t\tif (signal.aborted) onAbort();\n\t\t\telse signal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t}\n\t\tconst detachGate = createForegroundDetachGate({\n\t\t\truntime,\n\t\t\tsignal,\n\t\t\tdelayMs: detachDelayMs,\n\t\t\tremoveAbortListener: () => signal?.removeEventListener(\"abort\", onAbort),\n\t\t});\n\t\ttry {\n\t\t\toutcome = await Promise.race([\n\t\t\t\traceExitWithKillGrace(runtime, abortGate.signal, timeoutMs),\n\t\t\t\tdetachGate.detached.then(() => \"detached\" as const),\n\t\t\t]);\n\t\t} finally {\n\t\t\tdetachGate.cancel();\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\tunsubscribeOutput?.();\n\t\t\tupdateEmitter?.flush();\n\t\t\tupdateEmitter?.dispose();\n\t\t}\n\t}\n\n\tif (outcome === \"detached\") {\n\t\tscheduleDetachedSweep(ctx, id, runtime, timeoutMs, startedAt);\n\t\tif (ctx.onBackgroundExit) runtime.session.onExit(() => ctx.onBackgroundExit?.(id, runtime));\n\t\tconst delta = runtime.readDelta();\n\t\tconst partialOutput = formatTerminalToolOutput(delta.text).text || \"(no output yet)\";\n\t\tconst timeoutNote =\n\t\t\tinput.timeout !== undefined && Number.isFinite(input.timeout)\n\t\t\t\t? `not killed; the original ${input.timeout}s timeout still applies`\n\t\t\t\t: \"not killed; it will run until exit or kill_bash\";\n\t\treturn textResult(\n\t\t\t`Command is still running; auto-detached to background with ID: ${id} (${timeoutNote}).\\n\\nPartial output:\\n${partialOutput}\\n\\nContinue other work; completion will be reported automatically with exit status and output tail. Use bash_output({ bash_id: \"${id}\" }) only to peek at new output. monitor cannot attach to this session; use it for future event-driven launches. Use kill_bash({ bash_id: \"${id}\" }) to stop this session.`,\n\t\t\t{\n\t\t\t\tdetails: {\n\t\t\t\t\tbash_id: id,\n\t\t\t\t\tbackground: true,\n\t\t\t\t\tauto_detached: true,\n\t\t\t\t\tstatus: \"running\",\n\t\t\t\t\t...(delta.droppedChars > 0 ? { droppedChars: delta.droppedChars } : {}),\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t}\n\n\tif (outcome !== \"exited\") {\n\t\t// Sweep the never-settling session: the bounded registry stop marks it\n\t\t// `stopping`, so later /exit teardown cannot hang on its exit wait.\n\t\tvoid ctx.manager.stop(id).catch(() => {});\n\t}\n\tconst formatted = formatTerminalToolOutput(runtime.fullOutput());\n\tconst output = formatted.text;\n\t// Aborted runs report \"Command aborted\" (core bash parity) whether the exit\n\t// settled normally or the kill grace released the wait. `outcome` matters\n\t// beyond that only for the timeout grace, where exitResult is still null.\n\tif (signal?.aborted) {\n\t\treturn errorResult(`${output ? `${output}\\n\\n` : \"\"}Command aborted`);\n\t}\n\tconst exit = runtime.exitResult;\n\tif (outcome === \"timeout_grace\" || exit?.timedOut) {\n\t\treturn errorResult(`${output ? `${output}\\n\\n` : \"\"}Command timed out after ${input.timeout} seconds`);\n\t}\n\tconst status = describeExit(runtime);\n\tif (exit && exit.exitCode !== 0 && exit.exitCode !== null) {\n\t\treturn errorResult(`${output ? `${output}\\n\\n` : \"\"}Command exited with code ${exit.exitCode}`);\n\t}\n\treturn textResult(output || \"(no output)\", {\n\t\tdetails: {\n\t\t\tstatus,\n\t\t\t...(formatted.truncated ? { truncation: formatted.truncation } : {}),\n\t\t},\n\t});\n}\n\nasync function runBackground(\n\tctx: TerminalToolContext,\n\tinput: PtyBashInput,\n\tcols: number,\n\trows: number,\n\tcwd: string | undefined,\n\texecCtx?: SessionEnvSource,\n): Promise<TerminalToolResult> {\n\t// Background sessions are NEVER killed by `timeout` (bash-timeout injects a default into\n\t// every bash call); they live until exit or kill_bash, so no timeoutMs is passed.\n\tconst { id, runtime } = await spawnCommandSession(ctx, {\n\t\tcommand: input.command,\n\t\tcols,\n\t\trows,\n\t\tcwd,\n\t\tenvOverrides: sessionEnvOverrides(ctx, execCtx),\n\t});\n\tif (ctx.onBackgroundExit) runtime.session.onExit(() => ctx.onBackgroundExit?.(id, runtime));\n\n\t// Capture any output the command emits within a short grace window (or its exit).\n\tawait Promise.race([delay(BACKGROUND_START_GRACE_MS), runtime.session.waitExit()]);\n\tconst early = formatTerminalToolOutput(runtime.readDelta().text).text;\n\tconst header = `Command running in background with ID: ${id}`;\n\tconst backendNote =\n\t\truntime.backend === \"pipe-fallback\"\n\t\t\t? \"\\n(native PTY unavailable — running via pipe fallback; bash_input/bash_resize are limited.)\"\n\t\t\t: \"\";\n\tconst earlyBlock = early ? `\\n\\n${early}` : \"\";\n\treturn textResult(`${header}${backendNote}${earlyBlock}`, { details: { bash_id: id, background: true } });\n}\n\n/** Build the PTY-backed `bash` tool definition. */\nexport function createPtyBashTool(ctx: TerminalToolContext) {\n\treturn {\n\t\tname: TERMINAL_BASH_TOOL,\n\t\tlabel: \"bash\",\n\t\tdescription:\n\t\t\t\"Execute a shell command in a persistent PTY-backed session. Set run_in_background:true for long-lived or interactive sessions; steer them with bash_input, snapshot with bash_output, tear down with kill_bash. To wait on observable state (a build finishing, a server coming up, a log line), never run sleep or poll loops — subscribe with the monitor tool instead. Foreground timeout is a kill deadline in seconds.\",\n\t\tpromptSnippet: \"Run shell commands; run_in_background:true for long-lived/interactive PTY sessions\",\n\t\tpromptGuidelines: [\"Inspect PI_* environment variables for current model and session details.\"],\n\t\tparameters: ptyBashSchema,\n\t\tasync execute(\n\t\t\t_toolCallId: string,\n\t\t\tinput: PtyBashInput,\n\t\t\tsignal?: AbortSignal,\n\t\t\tonUpdate?: AgentToolUpdateCallback,\n\t\t\texecCtx?: SessionEnvSource,\n\t\t): Promise<TerminalToolResult> {\n\t\t\tconst cols = resolveDimension(input.cols, ctx.defaultCols || DEFAULT_COLS);\n\t\t\tconst rows = resolveDimension(input.rows, ctx.defaultRows || DEFAULT_ROWS);\n\t\t\tconst cwd = execCtx?.cwd;\n\t\t\tif (input.run_in_background) return runBackground(ctx, input, cols, rows, cwd, execCtx);\n\t\t\treturn runForeground(ctx, input, cols, rows, signal, cwd, onUpdate, execCtx);\n\t\t},\n\t};\n}\n"]}
|
|
1
|
+
{"version":3,"file":"bash.js","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/terminal/tools/bash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,IAAI,EAAE,MAAM,SAAS,CAAC;AAE5C,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAE/D,OAAO,EACN,yBAAyB,EACzB,YAAY,EACZ,YAAY,EACZ,wBAAwB,EACxB,4BAA4B,EAC5B,kBAAkB,GAClB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,WAAW,EAAqD,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1G,OAAO,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACpE,OAAO,EAAE,8BAA8B,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnG,OAAO,EAAE,iBAAiB,EAAgC,MAAM,iBAAiB,CAAC;AAClF,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAE/D,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAC,MAAM,CAAC;IACxC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,mDAAmD,EAAE,CAAC;IAC1F,OAAO,EAAE,IAAI,CAAC,QAAQ,CACrB,IAAI,CAAC,MAAM,CAAC;QACX,WAAW,EACV,6QAA6Q;KAC9Q,CAAC,CACF;IACD,WAAW,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,8CAA8C,EAAE,CAAC,CAAC;IACxG,iBAAiB,EAAE,IAAI,CAAC,QAAQ,CAC/B,IAAI,CAAC,OAAO,CAAC;QACZ,WAAW,EACV,oJAAoJ;KACrJ,CAAC,CACF;IACD,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,qCAAqC,EAAE,CAAC,CAAC;IACxF,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,kCAAkC,EAAE,CAAC,CAAC;CACrF,CAAC,CAAC;AAIH,SAAS,gBAAgB,CAAC,KAAyB,EAAE,QAAgB;IACpE,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IACjF,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,KAAK,CAAC,EAAU;IACxB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,cAAc,GAAG,aAAa,CAAC;AACrC,MAAM,uBAAuB,GAAG,GAAG,CAAC;AAQpC;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAgB,EAAE,UAAU,GAAG,uBAAuB;IAC5F,IAAI,KAAgD,CAAC;IACrD,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,IAAI,cAAkC,CAAC;IAEvC,MAAM,WAAW,GAAG,GAAG,EAAE;QACxB,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,KAAK,GAAG,KAAK,CAAC;QACd,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC5B,IAAI,EAAE,CAAC;IACR,CAAC,CAAC;IAEF,MAAM,UAAU,GAAG,GAAG,EAAE;QACvB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACzB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,KAAK,GAAG,SAAS,CAAC;QACnB,CAAC;IACF,CAAC,CAAC;IAEF,OAAO;QACN,QAAQ;YACP,KAAK,GAAG,IAAI,CAAC;YACb,IAAI,KAAK,KAAK,SAAS;gBAAE,OAAO;YAChC,MAAM,OAAO,GAAG,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,cAAc,CAAC;YACxF,IAAI,OAAO,IAAI,UAAU,EAAE,CAAC;gBAC3B,WAAW,EAAE,CAAC;gBACd,OAAO;YACR,CAAC;YACD,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBACvB,KAAK,GAAG,SAAS,CAAC;gBAClB,WAAW,EAAE,CAAC;YACf,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC;QAC1B,CAAC;QACD,KAAK;YACJ,UAAU,EAAE,CAAC;YACb,WAAW,EAAE,CAAC;QACf,CAAC;QACD,OAAO;YACN,UAAU,EAAE,CAAC;YACb,KAAK,GAAG,KAAK,CAAC;YACd,cAAc,GAAG,SAAS,CAAC;QAC5B,CAAC;KACD,CAAC;AACH,CAAC;AAWD,SAAS,mBAAmB,CAAC,GAAwB,EAAE,OAA0B;IAChF,MAAM,OAAO,GAAG,OAAO,IAAI,GAAG,CAAC,iBAAiB,EAAE,EAAE,CAAC;IACrD,IAAI,CAAC,OAAO,EAAE,cAAc;QAAE,OAAO,EAAE,CAAC;IACxC,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,GAAG,CAAC,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC,YAAY,EAAE,CAAC;IAC1D,MAAM,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,cAAc,EAAE,CAAC;IAC5D,IAAI,WAAW;QAAE,GAAG,CAAC,eAAe,GAAG,WAAW,CAAC;IACnD,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QACnB,GAAG,CAAC,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC;QACzC,GAAG,CAAC,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;IACjC,CAAC;IACD,IAAI,OAAO,CAAC,aAAa;QAAE,GAAG,CAAC,kBAAkB,GAAG,OAAO,CAAC,aAAa,CAAC;IAC1E,OAAO,GAAG,CAAC;AACZ,CAAC;AAED;;;;;GAKG;AACH,SAAS,qBAAqB,CAC7B,OAA+B,EAC/B,MAA+B,EAC/B,SAA6B;IAE7B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtC,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,eAA0D,CAAC;QAC/D,IAAI,iBAA4D,CAAC;QACjE,IAAI,aAAuC,CAAC;QAC5C,MAAM,MAAM,GAAG,CAAC,MAAkB,EAAE,EAAE;YACrC,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,IAAI,eAAe;gBAAE,YAAY,CAAC,eAAe,CAAC,CAAC;YACnD,IAAI,iBAAiB;gBAAE,YAAY,CAAC,iBAAiB,CAAC,CAAC;YACvD,IAAI,MAAM,IAAI,aAAa;gBAAE,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;YAChF,MAAM,EAAE,CAAC;QACV,CAAC,CAAC;QACF,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,CAC9B,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,EACrC,CAAC,KAAc,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CACnG,CAAC;QACF,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,sBAAsB,GAAG,SAAS,GAAG,4BAA4B,CAAC;YACxE,yEAAyE;YACzE,oEAAoE;YACpE,IAAI,sBAAsB,IAAI,cAAc,EAAE,CAAC;gBAC9C,iBAAiB,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,EAAE,sBAAsB,CAAC,CAAC;YACtG,CAAC;QACF,CAAC;QACD,IAAI,MAAM,EAAE,CAAC;YACZ,aAAa,GAAG,GAAG,EAAE;gBACpB,eAAe,KAAK,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,EAAE,4BAA4B,CAAC,CAAC;YAC1G,CAAC,CAAC;YACF,IAAI,MAAM,CAAC,OAAO;gBAAE,aAAa,EAAE,CAAC;;gBAC/B,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,aAAa,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACtE,CAAC;IACF,CAAC,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,wBAAwB,CAChC,GAAwB,EACxB,KAAmB,EACnB,SAA8C;IAE9C,IAAI,GAAG,CAAC,aAAa,KAAK,MAAM;QAAE,OAAO,SAAS,CAAC;IACnD,MAAM,aAAa,GAAG,SAAS;QAC9B,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,yBAAyB,EAAE,8BAA8B,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAClF,CAAC,CAAC,8BAA8B,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC/C,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;IAChH,IAAI,cAAc,IAAI,aAAa;QAAE,OAAO,SAAS,CAAC;IACtD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,SAAS,qBAAqB,CAC7B,GAAwB,EACxB,EAAU,EACV,OAA+B,EAC/B,SAA6B,EAC7B,SAAiB;IAEjB,IAAI,SAAS,KAAK,SAAS;QAAE,OAAO;IACpC,MAAM,UAAU,GAAG,SAAS,GAAG,4BAA4B,CAAC;IAC5D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,UAAU,GAAG,cAAc,IAAI,OAAO,CAAC,MAAM;QAAE,OAAO;IAE1F,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,CAAC,CAAC;IACvE,IAAI,KAAgD,CAAC;IACrD,IAAI,eAAyC,CAAC;IAC9C,MAAM,UAAU,GAAG,GAAG,EAAE;QACvB,IAAI,KAAK,KAAK,SAAS;YAAE,YAAY,CAAC,KAAK,CAAC,CAAC;QAC7C,KAAK,GAAG,SAAS,CAAC;QAClB,eAAe,EAAE,EAAE,CAAC;QACpB,eAAe,GAAG,SAAS,CAAC;IAC7B,CAAC,CAAC;IACF,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACrD,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACpB,UAAU,EAAE,CAAC;QACb,OAAO;IACR,CAAC;IACD,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;QACvB,UAAU,EAAE,CAAC;QACb,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,KAAK,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAChE,CAAC,EAAE,WAAW,CAAC,CAAC;AACjB,CAAC;AAED,KAAK,UAAU,aAAa,CAC3B,GAAwB,EACxB,KAAmB,EACnB,IAAY,EACZ,IAAY,EACZ,MAA+B,EAC/B,GAAuB,EACvB,QAA6C,EAC7C,OAA0B;IAE1B,IAAI,MAAM,EAAE,OAAO;QAAE,OAAO,WAAW,CAAC,iBAAiB,CAAC,CAAC;IAC3D,MAAM,SAAS,GAAG,KAAK,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC7F,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,MAAM,mBAAmB,CAAC,GAAG,EAAE;QACtD,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,IAAI;QACJ,IAAI;QACJ,SAAS;QACT,GAAG;QACH,YAAY,EAAE,EAAE,GAAG,mBAAmB,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,GAAG,wBAAwB,EAAE;KACnF,CAAC,CAAC;IACH,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,MAAM,QAAQ,GAAG,WAAW,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IACzD,MAAM,gBAAgB,GAAG,GAAG,EAAE;QAC7B,MAAM,IAAI,GAAG,wBAAwB,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC;QAC9E,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,CAAC;IACnG,CAAC,CAAC;IACF,MAAM,aAAa,GAAG,QAAQ,CAAC,CAAC,CAAC,sBAAsB,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACtF,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;IAChD,MAAM,iBAAiB,GAAG,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,aAAa,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAEnG,MAAM,SAAS,GAAG,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACnD,MAAM,aAAa,GAAG,wBAAwB,CAAC,GAAG,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IACtE,IAAI,OAA2C,CAAC;IAChD,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;QACjC,2EAA2E;QAC3E,2EAA2E;QAC3E,0EAA0E;QAC1E,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtD,IAAI,MAAM,EAAE,CAAC;YACZ,IAAI,MAAM,CAAC,OAAO;gBAAE,OAAO,EAAE,CAAC;;gBACzB,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAChE,CAAC;QACD,IAAI,CAAC;YACJ,OAAO,GAAG,MAAM,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;QACnE,CAAC;gBAAS,CAAC;YACV,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC9C,iBAAiB,EAAE,EAAE,CAAC;YACtB,aAAa,EAAE,KAAK,EAAE,CAAC;YACvB,aAAa,EAAE,OAAO,EAAE,CAAC;QAC1B,CAAC;IACF,CAAC;SAAM,CAAC;QACP,MAAM,SAAS,GAAG,IAAI,eAAe,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,GAAG,EAAE;YACpB,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAChC,SAAS,CAAC,KAAK,EAAE,CAAC;QACnB,CAAC,CAAC;QACF,IAAI,MAAM,EAAE,CAAC;YACZ,IAAI,MAAM,CAAC,OAAO;gBAAE,OAAO,EAAE,CAAC;;gBACzB,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAChE,CAAC;QACD,MAAM,UAAU,GAAG,0BAA0B,CAAC;YAC7C,OAAO;YACP,MAAM;YACN,OAAO,EAAE,aAAa;YACtB,mBAAmB,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC;SACxE,CAAC,CAAC;QACH,IAAI,CAAC;YACJ,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;gBAC5B,qBAAqB,CAAC,OAAO,EAAE,SAAS,CAAC,MAAM,EAAE,SAAS,CAAC;gBAC3D,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,UAAmB,CAAC;aACnD,CAAC,CAAC;QACJ,CAAC;gBAAS,CAAC;YACV,UAAU,CAAC,MAAM,EAAE,CAAC;YACpB,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC9C,iBAAiB,EAAE,EAAE,CAAC;YACtB,aAAa,EAAE,KAAK,EAAE,CAAC;YACvB,aAAa,EAAE,OAAO,EAAE,CAAC;QAC1B,CAAC;IACF,CAAC;IAED,IAAI,OAAO,KAAK,UAAU,EAAE,CAAC;QAC5B,qBAAqB,CAAC,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;QAC9D,IAAI,GAAG,CAAC,gBAAgB;YAAE,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC;QAC5F,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;QAClC,MAAM,aAAa,GAAG,wBAAwB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,iBAAiB,CAAC;QACrF,MAAM,WAAW,GAChB,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC;YAC5D,CAAC,CAAC,4BAA4B,KAAK,CAAC,OAAO,yBAAyB;YACpE,CAAC,CAAC,iDAAiD,CAAC;QACtD,MAAM,QAAQ,GAAG,SAAS;YACzB,CAAC,CAAC,2BAA2B,SAAS,CAAC,OAAO,8NAA8N,EAAE,4LAA4L,EAAE,4BAA4B;YACxe,CAAC,CAAC,gIAAgI,EAAE,8IAA8I,EAAE,4BAA4B,CAAC;QAClT,OAAO,UAAU,CAChB,kEAAkE,EAAE,KAAK,WAAW,0BAA0B,aAAa,OAAO,QAAQ,EAAE,EAC5I;YACC,OAAO,EAAE;gBACR,OAAO,EAAE,EAAE;gBACX,UAAU,EAAE,IAAI;gBAChB,aAAa,EAAE,IAAI;gBACnB,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC1C,MAAM,EAAE,SAAS;gBACjB,GAAG,CAAC,KAAK,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACvE;SACD,CACD,CAAC;IACH,CAAC;IAED,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC1B,uEAAuE;QACvE,oEAAoE;QACpE,KAAK,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IAC3C,CAAC;IACD,MAAM,SAAS,GAAG,wBAAwB,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IACjE,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC;IAC9B,4EAA4E;IAC5E,0EAA0E;IAC1E,0EAA0E;IAC1E,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;QACrB,OAAO,WAAW,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,MAAM,CAAC,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC;IACvE,CAAC;IACD,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC;IAChC,IAAI,OAAO,KAAK,eAAe,IAAI,IAAI,EAAE,QAAQ,EAAE,CAAC;QACnD,OAAO,WAAW,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,MAAM,CAAC,CAAC,CAAC,EAAE,2BAA2B,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;IACxG,CAAC;IACD,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IACrC,IAAI,IAAI,IAAI,IAAI,CAAC,QAAQ,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QAC3D,OAAO,WAAW,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,MAAM,CAAC,CAAC,CAAC,EAAE,4BAA4B,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IACjG,CAAC;IACD,OAAO,UAAU,CAAC,MAAM,IAAI,aAAa,EAAE;QAC1C,OAAO,EAAE;YACR,MAAM;YACN,GAAG,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACpE;KACD,CAAC,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,aAAa,CAC3B,GAAwB,EACxB,KAAmB,EACnB,IAAY,EACZ,IAAY,EACZ,GAAuB,EACvB,OAA0B;IAE1B,yFAAyF;IACzF,kFAAkF;IAClF,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,GAAG,MAAM,mBAAmB,CAAC,GAAG,EAAE;QACtD,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,IAAI;QACJ,IAAI;QACJ,GAAG;QACH,YAAY,EAAE,mBAAmB,CAAC,GAAG,EAAE,OAAO,CAAC;KAC/C,CAAC,CAAC;IACH,IAAI,GAAG,CAAC,gBAAgB;QAAE,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC;IAE5F,kFAAkF;IAClF,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,yBAAyB,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;IACnF,MAAM,KAAK,GAAG,wBAAwB,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC;IACtE,MAAM,MAAM,GAAG,0CAA0C,EAAE,EAAE,CAAC;IAC9D,MAAM,WAAW,GAChB,OAAO,CAAC,OAAO,KAAK,eAAe;QAClC,CAAC,CAAC,6FAA6F;QAC/F,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/C,OAAO,UAAU,CAAC,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;AAC3G,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,iBAAiB,CAAC,GAAwB;IACzD,OAAO;QACN,IAAI,EAAE,kBAAkB;QACxB,KAAK,EAAE,MAAM;QACb,WAAW,EACV,mhBAAmhB;QACphB,aAAa,EAAE,oFAAoF;QACnG,gBAAgB,EAAE,CAAC,2EAA2E,CAAC;QAC/F,UAAU,EAAE,aAAa;QACzB,KAAK,CAAC,OAAO,CACZ,WAAmB,EACnB,KAAmB,EACnB,MAAoB,EACpB,QAAkC,EAClC,OAA0B;YAE1B,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,WAAW,IAAI,YAAY,CAAC,CAAC;YAC3E,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,WAAW,IAAI,YAAY,CAAC,CAAC;YAC3E,MAAM,GAAG,GAAG,OAAO,EAAE,GAAG,CAAC;YACzB,IAAI,KAAK,CAAC,iBAAiB;gBAAE,OAAO,aAAa,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;YACxF,OAAO,aAAa,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC9E,CAAC;KACD,CAAC;AACH,CAAC","sourcesContent":["import { type Static, Type } from \"typebox\";\nimport type { AgentToolUpdateCallback } from \"../../../types.ts\";\nimport { formatTerminalToolOutput } from \"../output-format.ts\";\nimport type { TerminalRuntimeSession } from \"../runtime-session.ts\";\nimport {\n\tBACKGROUND_START_GRACE_MS,\n\tDEFAULT_COLS,\n\tDEFAULT_ROWS,\n\tFOREGROUND_ENV_OVERRIDES,\n\tKILLED_SESSION_EXIT_GRACE_MS,\n\tTERMINAL_BASH_TOOL,\n} from \"../shared.ts\";\nimport { errorResult, type TerminalToolContext, type TerminalToolResult, textResult } from \"./context.ts\";\nimport { createForegroundDetachGate } from \"./foreground-detach.ts\";\nimport { resolveForegroundWindowSeconds, SLEEP_WAIT_WINDOW_SECONDS } from \"./foreground-window.ts\";\nimport { classifySleepWait, type SleepWaitClassification } from \"./sleep-wait.ts\";\nimport { describeExit, spawnCommandSession } from \"./spawn.ts\";\n\nexport const ptyBashSchema = Type.Object({\n\tcommand: Type.String({ description: \"Shell command to execute in a PTY-backed session.\" }),\n\ttimeout: Type.Optional(\n\t\tType.Number({\n\t\t\tdescription:\n\t\t\t\t\"Process kill deadline in seconds (default 1800). Foreground blocking stops at the ~60s window: a command still running then auto-detaches to a live background session instead of being killed. Ignored for run_in_background sessions (they live until exit or kill_bash).\",\n\t\t}),\n\t),\n\tdescription: Type.Optional(Type.String({ description: \"Short human-readable label for this command.\" })),\n\trun_in_background: Type.Optional(\n\t\tType.Boolean({\n\t\t\tdescription:\n\t\t\t\t\"Run as a persistent background session. Returns a bash_id immediately; query it with bash_output, steer with bash_input, tear down with kill_bash.\",\n\t\t}),\n\t),\n\tcols: Type.Optional(Type.Number({ description: \"PTY width in columns (default 120).\" })),\n\trows: Type.Optional(Type.Number({ description: \"PTY height in rows (default 40).\" })),\n});\n\nexport type PtyBashInput = Static<typeof ptyBashSchema>;\n\nfunction resolveDimension(value: number | undefined, fallback: number): number {\n\tif (value === undefined || !Number.isFinite(value) || value < 1) return fallback;\n\treturn Math.trunc(value);\n}\n\nfunction delay(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nconst MAX_TIMEOUT_MS = 2_147_483_647;\nconst BASH_UPDATE_THROTTLE_MS = 100;\n\nexport interface ThrottledEmitter {\n\tschedule(): void;\n\tflush(): void;\n\tdispose(): void;\n}\n\n/**\n * Emit immediately, then coalesce subsequent schedules into one trailing update.\n * `dispose` intentionally discards a pending update so callers can stop cleanly.\n */\nexport function createThrottledEmitter(emit: () => void, throttleMs = BASH_UPDATE_THROTTLE_MS): ThrottledEmitter {\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tlet dirty = false;\n\tlet lastEmissionAt: number | undefined;\n\n\tconst emitIfDirty = () => {\n\t\tif (!dirty) return;\n\t\tdirty = false;\n\t\tlastEmissionAt = Date.now();\n\t\temit();\n\t};\n\n\tconst clearTimer = () => {\n\t\tif (timer !== undefined) {\n\t\t\tclearTimeout(timer);\n\t\t\ttimer = undefined;\n\t\t}\n\t};\n\n\treturn {\n\t\tschedule() {\n\t\t\tdirty = true;\n\t\t\tif (timer !== undefined) return;\n\t\t\tconst elapsed = lastEmissionAt === undefined ? throttleMs : Date.now() - lastEmissionAt;\n\t\t\tif (elapsed >= throttleMs) {\n\t\t\t\temitIfDirty();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttimer = setTimeout(() => {\n\t\t\t\ttimer = undefined;\n\t\t\t\temitIfDirty();\n\t\t\t}, throttleMs - elapsed);\n\t\t},\n\t\tflush() {\n\t\t\tclearTimer();\n\t\t\temitIfDirty();\n\t\t},\n\t\tdispose() {\n\t\t\tclearTimer();\n\t\t\tdirty = false;\n\t\t\tlastEmissionAt = undefined;\n\t\t},\n\t};\n}\n\ntype ForegroundWaitOutcome = \"exited\" | \"abort_grace\" | \"timeout_grace\";\n\ntype SessionEnvSource = {\n\tsessionManager?: { getSessionId: () => string; getSessionFile: () => string | undefined };\n\tmodel?: { provider: string; id: string };\n\tthinkingLevel?: string;\n\tcwd?: string;\n};\n\nfunction sessionEnvOverrides(ctx: TerminalToolContext, execCtx?: SessionEnvSource): Record<string, string> {\n\tconst session = execCtx ?? ctx.getSessionContext?.();\n\tif (!session?.sessionManager) return {};\n\tconst env: Record<string, string> = {};\n\tenv.PI_SESSION_ID = session.sessionManager.getSessionId();\n\tconst sessionFile = session.sessionManager.getSessionFile();\n\tif (sessionFile) env.PI_SESSION_FILE = sessionFile;\n\tif (session.model) {\n\t\tenv.PI_PROVIDER = session.model.provider;\n\t\tenv.PI_MODEL = session.model.id;\n\t}\n\tif (session.thinkingLevel) env.PI_REASONING_LEVEL = session.thinkingLevel;\n\treturn env;\n}\n\n/**\n * Wait for the session's exit to settle, but stop waiting a bounded grace after\n * the session has been killed. The native wait joins the PTY reader thread,\n * which blocks while any surviving descendant holds the PTY open — without the\n * grace, an aborted or timed-out command can pin the agent forever.\n */\nfunction raceExitWithKillGrace(\n\truntime: TerminalRuntimeSession,\n\tsignal: AbortSignal | undefined,\n\ttimeoutMs: number | undefined,\n): Promise<ForegroundWaitOutcome> {\n\treturn new Promise((resolve, reject) => {\n\t\tlet settled = false;\n\t\tlet abortGraceTimer: ReturnType<typeof setTimeout> | undefined;\n\t\tlet timeoutGraceTimer: ReturnType<typeof setTimeout> | undefined;\n\t\tlet armAbortGrace: (() => void) | undefined;\n\t\tconst settle = (finish: () => void) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tif (abortGraceTimer) clearTimeout(abortGraceTimer);\n\t\t\tif (timeoutGraceTimer) clearTimeout(timeoutGraceTimer);\n\t\t\tif (signal && armAbortGrace) signal.removeEventListener(\"abort\", armAbortGrace);\n\t\t\tfinish();\n\t\t};\n\t\truntime.session.waitExit().then(\n\t\t\t() => settle(() => resolve(\"exited\")),\n\t\t\t(error: unknown) => settle(() => reject(error instanceof Error ? error : new Error(String(error)))),\n\t\t);\n\t\tif (timeoutMs !== undefined) {\n\t\t\tconst timeoutGraceDeadlineMs = timeoutMs + KILLED_SESSION_EXIT_GRACE_MS;\n\t\t\t// Beyond the 32-bit setTimeout range the deadline cannot be represented;\n\t\t\t// keep the unbounded wait rather than firing a false early timeout.\n\t\t\tif (timeoutGraceDeadlineMs <= MAX_TIMEOUT_MS) {\n\t\t\t\ttimeoutGraceTimer = setTimeout(() => settle(() => resolve(\"timeout_grace\")), timeoutGraceDeadlineMs);\n\t\t\t}\n\t\t}\n\t\tif (signal) {\n\t\t\tarmAbortGrace = () => {\n\t\t\t\tabortGraceTimer ??= setTimeout(() => settle(() => resolve(\"abort_grace\")), KILLED_SESSION_EXIT_GRACE_MS);\n\t\t\t};\n\t\t\tif (signal.aborted) armAbortGrace();\n\t\t\telse signal.addEventListener(\"abort\", armAbortGrace, { once: true });\n\t\t}\n\t});\n}\n\n/**\n * How long the model blocks before a still-running command is handed to a live\n * background session. A sleep-wait detaches almost immediately: its remaining\n * time is pure waiting, which the completion notification delivers for free.\n */\nfunction resolveAutoDetachDelayMs(\n\tctx: TerminalToolContext,\n\tinput: PtyBashInput,\n\tsleepWait: SleepWaitClassification | undefined,\n): number | undefined {\n\tif (ctx.timeoutAction === \"kill\") return undefined;\n\tconst windowSeconds = sleepWait\n\t\t? Math.min(SLEEP_WAIT_WINDOW_SECONDS, resolveForegroundWindowSeconds(process.env))\n\t\t: resolveForegroundWindowSeconds(process.env);\n\tconst timeoutSeconds = input.timeout !== undefined && Number.isFinite(input.timeout) ? input.timeout : Infinity;\n\tif (timeoutSeconds <= windowSeconds) return undefined;\n\treturn Math.max(0, Math.trunc(windowSeconds * 1000));\n}\n\nfunction scheduleDetachedSweep(\n\tctx: TerminalToolContext,\n\tid: string,\n\truntime: TerminalRuntimeSession,\n\ttimeoutMs: number | undefined,\n\tstartedAt: number,\n): void {\n\tif (timeoutMs === undefined) return;\n\tconst deadlineMs = timeoutMs + KILLED_SESSION_EXIT_GRACE_MS;\n\tif (!Number.isFinite(deadlineMs) || deadlineMs > MAX_TIMEOUT_MS || runtime.exited) return;\n\n\tconst remainingMs = Math.max(0, deadlineMs - (Date.now() - startedAt));\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tlet unsubscribeExit: (() => void) | undefined;\n\tconst clearSweep = () => {\n\t\tif (timer !== undefined) clearTimeout(timer);\n\t\ttimer = undefined;\n\t\tunsubscribeExit?.();\n\t\tunsubscribeExit = undefined;\n\t};\n\tunsubscribeExit = runtime.session.onExit(clearSweep);\n\tif (runtime.exited) {\n\t\tclearSweep();\n\t\treturn;\n\t}\n\ttimer = setTimeout(() => {\n\t\tclearSweep();\n\t\tif (!runtime.exited) void ctx.manager.stop(id).catch(() => {});\n\t}, remainingMs);\n}\n\nasync function runForeground(\n\tctx: TerminalToolContext,\n\tinput: PtyBashInput,\n\tcols: number,\n\trows: number,\n\tsignal: AbortSignal | undefined,\n\tcwd: string | undefined,\n\tonUpdate: AgentToolUpdateCallback | undefined,\n\texecCtx?: SessionEnvSource,\n): Promise<TerminalToolResult> {\n\tif (signal?.aborted) return errorResult(\"Command aborted\");\n\tconst timeoutMs = input.timeout !== undefined ? Math.trunc(input.timeout * 1000) : undefined;\n\tconst { id, runtime } = await spawnCommandSession(ctx, {\n\t\tcommand: input.command,\n\t\tcols,\n\t\trows,\n\t\ttimeoutMs,\n\t\tcwd,\n\t\tenvOverrides: { ...sessionEnvOverrides(ctx, execCtx), ...FOREGROUND_ENV_OVERRIDES },\n\t});\n\tconst startedAt = Date.now();\n\tconst activity = `running ${input.command.slice(0, 80)}`;\n\tconst emitOutputUpdate = () => {\n\t\tconst text = formatTerminalToolOutput(runtime.fullOutput()).text.slice(-2000);\n\t\tonUpdate?.({ content: [{ type: \"text\", text }], details: { progress: { activity, startedAt } } });\n\t};\n\tconst updateEmitter = onUpdate ? createThrottledEmitter(emitOutputUpdate) : undefined;\n\tonUpdate?.({ content: [], details: undefined });\n\tconst unsubscribeOutput = onUpdate ? runtime.onOutput(() => updateEmitter?.schedule()) : undefined;\n\n\tconst sleepWait = classifySleepWait(input.command);\n\tconst detachDelayMs = resolveAutoDetachDelayMs(ctx, input, sleepWait);\n\tlet outcome: ForegroundWaitOutcome | \"detached\";\n\tif (detachDelayMs === undefined) {\n\t\t// Interrupt means \"stop now\": SIGKILL the whole process group in one shot.\n\t\t// kill() is one-shot idempotent, so a gentle SIGTERM first would block any\n\t\t// escalation, and a SIGTERM-ignoring command would pin the agent forever.\n\t\tconst onAbort = () => runtime.session.kill(\"SIGKILL\");\n\t\tif (signal) {\n\t\t\tif (signal.aborted) onAbort();\n\t\t\telse signal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t}\n\t\ttry {\n\t\t\toutcome = await raceExitWithKillGrace(runtime, signal, timeoutMs);\n\t\t} finally {\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\tunsubscribeOutput?.();\n\t\t\tupdateEmitter?.flush();\n\t\t\tupdateEmitter?.dispose();\n\t\t}\n\t} else {\n\t\tconst abortGate = new AbortController();\n\t\tconst onAbort = () => {\n\t\t\truntime.session.kill(\"SIGKILL\");\n\t\t\tabortGate.abort();\n\t\t};\n\t\tif (signal) {\n\t\t\tif (signal.aborted) onAbort();\n\t\t\telse signal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t}\n\t\tconst detachGate = createForegroundDetachGate({\n\t\t\truntime,\n\t\t\tsignal,\n\t\t\tdelayMs: detachDelayMs,\n\t\t\tremoveAbortListener: () => signal?.removeEventListener(\"abort\", onAbort),\n\t\t});\n\t\ttry {\n\t\t\toutcome = await Promise.race([\n\t\t\t\traceExitWithKillGrace(runtime, abortGate.signal, timeoutMs),\n\t\t\t\tdetachGate.detached.then(() => \"detached\" as const),\n\t\t\t]);\n\t\t} finally {\n\t\t\tdetachGate.cancel();\n\t\t\tsignal?.removeEventListener(\"abort\", onAbort);\n\t\t\tunsubscribeOutput?.();\n\t\t\tupdateEmitter?.flush();\n\t\t\tupdateEmitter?.dispose();\n\t\t}\n\t}\n\n\tif (outcome === \"detached\") {\n\t\tscheduleDetachedSweep(ctx, id, runtime, timeoutMs, startedAt);\n\t\tif (ctx.onBackgroundExit) runtime.session.onExit(() => ctx.onBackgroundExit?.(id, runtime));\n\t\tconst delta = runtime.readDelta();\n\t\tconst partialOutput = formatTerminalToolOutput(delta.text).text || \"(no output yet)\";\n\t\tconst timeoutNote =\n\t\t\tinput.timeout !== undefined && Number.isFinite(input.timeout)\n\t\t\t\t? `not killed; the original ${input.timeout}s timeout still applies`\n\t\t\t\t: \"not killed; it will run until exit or kill_bash\";\n\t\tconst guidance = sleepWait\n\t\t\t? `This command is a wait (${sleepWait.seconds}s sleep), so it detached immediately; the wait continues in the background. Do nothing and end your turn — completion will be reported automatically with exit status and output tail. Do NOT poll bash_output({ bash_id: \"${id}\" }) for it. When you are waiting for a pattern in a command's output, launch it with monitor({ command, filter }) instead so matching lines arrive as events. Use kill_bash({ bash_id: \"${id}\" }) to stop this session.`\n\t\t\t: `Continue other work; completion will be reported automatically with exit status and output tail. Use bash_output({ bash_id: \"${id}\" }) only to peek at new output. monitor cannot attach to this session; use it for future event-driven launches. Use kill_bash({ bash_id: \"${id}\" }) to stop this session.`;\n\t\treturn textResult(\n\t\t\t`Command is still running; auto-detached to background with ID: ${id} (${timeoutNote}).\\n\\nPartial output:\\n${partialOutput}\\n\\n${guidance}`,\n\t\t\t{\n\t\t\t\tdetails: {\n\t\t\t\t\tbash_id: id,\n\t\t\t\t\tbackground: true,\n\t\t\t\t\tauto_detached: true,\n\t\t\t\t\t...(sleepWait ? { sleep_wait: true } : {}),\n\t\t\t\t\tstatus: \"running\",\n\t\t\t\t\t...(delta.droppedChars > 0 ? { droppedChars: delta.droppedChars } : {}),\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t}\n\n\tif (outcome !== \"exited\") {\n\t\t// Sweep the never-settling session: the bounded registry stop marks it\n\t\t// `stopping`, so later /exit teardown cannot hang on its exit wait.\n\t\tvoid ctx.manager.stop(id).catch(() => {});\n\t}\n\tconst formatted = formatTerminalToolOutput(runtime.fullOutput());\n\tconst output = formatted.text;\n\t// Aborted runs report \"Command aborted\" (core bash parity) whether the exit\n\t// settled normally or the kill grace released the wait. `outcome` matters\n\t// beyond that only for the timeout grace, where exitResult is still null.\n\tif (signal?.aborted) {\n\t\treturn errorResult(`${output ? `${output}\\n\\n` : \"\"}Command aborted`);\n\t}\n\tconst exit = runtime.exitResult;\n\tif (outcome === \"timeout_grace\" || exit?.timedOut) {\n\t\treturn errorResult(`${output ? `${output}\\n\\n` : \"\"}Command timed out after ${input.timeout} seconds`);\n\t}\n\tconst status = describeExit(runtime);\n\tif (exit && exit.exitCode !== 0 && exit.exitCode !== null) {\n\t\treturn errorResult(`${output ? `${output}\\n\\n` : \"\"}Command exited with code ${exit.exitCode}`);\n\t}\n\treturn textResult(output || \"(no output)\", {\n\t\tdetails: {\n\t\t\tstatus,\n\t\t\t...(formatted.truncated ? { truncation: formatted.truncation } : {}),\n\t\t},\n\t});\n}\n\nasync function runBackground(\n\tctx: TerminalToolContext,\n\tinput: PtyBashInput,\n\tcols: number,\n\trows: number,\n\tcwd: string | undefined,\n\texecCtx?: SessionEnvSource,\n): Promise<TerminalToolResult> {\n\t// Background sessions are NEVER killed by `timeout` (bash-timeout injects a default into\n\t// every bash call); they live until exit or kill_bash, so no timeoutMs is passed.\n\tconst { id, runtime } = await spawnCommandSession(ctx, {\n\t\tcommand: input.command,\n\t\tcols,\n\t\trows,\n\t\tcwd,\n\t\tenvOverrides: sessionEnvOverrides(ctx, execCtx),\n\t});\n\tif (ctx.onBackgroundExit) runtime.session.onExit(() => ctx.onBackgroundExit?.(id, runtime));\n\n\t// Capture any output the command emits within a short grace window (or its exit).\n\tawait Promise.race([delay(BACKGROUND_START_GRACE_MS), runtime.session.waitExit()]);\n\tconst early = formatTerminalToolOutput(runtime.readDelta().text).text;\n\tconst header = `Command running in background with ID: ${id}`;\n\tconst backendNote =\n\t\truntime.backend === \"pipe-fallback\"\n\t\t\t? \"\\n(native PTY unavailable — running via pipe fallback; bash_input/bash_resize are limited.)\"\n\t\t\t: \"\";\n\tconst earlyBlock = early ? `\\n\\n${early}` : \"\";\n\treturn textResult(`${header}${backendNote}${earlyBlock}`, { details: { bash_id: id, background: true } });\n}\n\n/** Build the PTY-backed `bash` tool definition. */\nexport function createPtyBashTool(ctx: TerminalToolContext) {\n\treturn {\n\t\tname: TERMINAL_BASH_TOOL,\n\t\tlabel: \"bash\",\n\t\tdescription:\n\t\t\t\"Execute a shell command in a persistent PTY-backed session. Set run_in_background:true for long-lived or interactive sessions; steer them with bash_input, snapshot with bash_output, tear down with kill_bash. To wait on observable state (a build finishing, a server coming up, a log line), never run sleep or poll loops — subscribe with the monitor tool instead. Foreground blocking stops at the ~60s window and a still-running command auto-detaches to a live background session; `timeout` is the process kill deadline in seconds.\",\n\t\tpromptSnippet: \"Run shell commands; run_in_background:true for long-lived/interactive PTY sessions\",\n\t\tpromptGuidelines: [\"Inspect PI_* environment variables for current model and session details.\"],\n\t\tparameters: ptyBashSchema,\n\t\tasync execute(\n\t\t\t_toolCallId: string,\n\t\t\tinput: PtyBashInput,\n\t\t\tsignal?: AbortSignal,\n\t\t\tonUpdate?: AgentToolUpdateCallback,\n\t\t\texecCtx?: SessionEnvSource,\n\t\t): Promise<TerminalToolResult> {\n\t\t\tconst cols = resolveDimension(input.cols, ctx.defaultCols || DEFAULT_COLS);\n\t\t\tconst rows = resolveDimension(input.rows, ctx.defaultRows || DEFAULT_ROWS);\n\t\t\tconst cwd = execCtx?.cwd;\n\t\t\tif (input.run_in_background) return runBackground(ctx, input, cols, rows, cwd, execCtx);\n\t\t\treturn runForeground(ctx, input, cols, rows, signal, cwd, onUpdate, execCtx);\n\t\t},\n\t};\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The foreground window is how long a bash tool call may block the model, which
|
|
3
|
+
* is a different concern from `timeout` — the deadline that kills the process.
|
|
4
|
+
* A command still running at the window is handed to a live background session
|
|
5
|
+
* instead of being killed, so the turn is freed without losing the work.
|
|
6
|
+
*/
|
|
7
|
+
export declare const DEFAULT_FOREGROUND_WINDOW_SECONDS = 60;
|
|
8
|
+
/** A wait has nothing to show in the foreground; detach it almost at once. */
|
|
9
|
+
export declare const SLEEP_WAIT_WINDOW_SECONDS = 5;
|
|
10
|
+
export declare function resolveForegroundWindowSeconds(env: Record<string, string | undefined>): number;
|
|
11
|
+
//# sourceMappingURL=foreground-window.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"foreground-window.d.ts","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/terminal/tools/foreground-window.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,iCAAiC,KAAK,CAAC;AACpD,8EAA8E;AAC9E,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,MAAM,CAI9F"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The foreground window is how long a bash tool call may block the model, which
|
|
3
|
+
* is a different concern from `timeout` — the deadline that kills the process.
|
|
4
|
+
* A command still running at the window is handed to a live background session
|
|
5
|
+
* instead of being killed, so the turn is freed without losing the work.
|
|
6
|
+
*/
|
|
7
|
+
export const DEFAULT_FOREGROUND_WINDOW_SECONDS = 60;
|
|
8
|
+
/** A wait has nothing to show in the foreground; detach it almost at once. */
|
|
9
|
+
export const SLEEP_WAIT_WINDOW_SECONDS = 5;
|
|
10
|
+
export function resolveForegroundWindowSeconds(env) {
|
|
11
|
+
const parsed = Number.parseFloat(env.PI_BASH_FOREGROUND_SECONDS ?? "");
|
|
12
|
+
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
13
|
+
return DEFAULT_FOREGROUND_WINDOW_SECONDS;
|
|
14
|
+
return parsed;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=foreground-window.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"foreground-window.js","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/terminal/tools/foreground-window.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,iCAAiC,GAAG,EAAE,CAAC;AACpD,8EAA8E;AAC9E,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC;AAE3C,MAAM,UAAU,8BAA8B,CAAC,GAAuC;IACrF,MAAM,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,0BAA0B,IAAI,EAAE,CAAC,CAAC;IACvE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC;QAAE,OAAO,iCAAiC,CAAC;IACtF,OAAO,MAAM,CAAC;AACf,CAAC","sourcesContent":["/**\n * The foreground window is how long a bash tool call may block the model, which\n * is a different concern from `timeout` — the deadline that kills the process.\n * A command still running at the window is handed to a live background session\n * instead of being killed, so the turn is freed without losing the work.\n */\n\nexport const DEFAULT_FOREGROUND_WINDOW_SECONDS = 60;\n/** A wait has nothing to show in the foreground; detach it almost at once. */\nexport const SLEEP_WAIT_WINDOW_SECONDS = 5;\n\nexport function resolveForegroundWindowSeconds(env: Record<string, string | undefined>): number {\n\tconst parsed = Number.parseFloat(env.PI_BASH_FOREGROUND_SECONDS ?? \"\");\n\tif (!Number.isFinite(parsed) || parsed <= 0) return DEFAULT_FOREGROUND_WINDOW_SECONDS;\n\treturn parsed;\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects commands whose whole purpose is to *wait* — `sleep 270; git log`,
|
|
3
|
+
* `for ...; do ...; sleep 5; done`, a bare `sleep 30`. Such a command should not
|
|
4
|
+
* hold the foreground: it detaches early so the session can keep working while
|
|
5
|
+
* the wait finishes in the background and its completion notification lands.
|
|
6
|
+
*
|
|
7
|
+
* Settle sleeps (`pkill; sleep 1`, a 2s server warm-up) are deliberately NOT
|
|
8
|
+
* sleep-waits: they are short by construction and detaching them would cost a
|
|
9
|
+
* notification round-trip for nothing.
|
|
10
|
+
*/
|
|
11
|
+
/** Minimum `sleep` seconds for a non-loop command to count as a wait. */
|
|
12
|
+
export declare const SLEEP_WAIT_THRESHOLD_SECONDS = 10;
|
|
13
|
+
/** Minimum per-iteration `sleep` seconds for a polling loop to count as a wait. */
|
|
14
|
+
export declare const SLEEP_WAIT_LOOP_THRESHOLD_SECONDS = 2;
|
|
15
|
+
export type SleepWaitRule = "R1" | "R2" | "R3" | "R4";
|
|
16
|
+
export interface SleepWaitClassification {
|
|
17
|
+
readonly kind: "sleep-wait";
|
|
18
|
+
/** R1 pure, R2 leading chain, R3 polling loop, R4 trailing. */
|
|
19
|
+
readonly rule: SleepWaitRule;
|
|
20
|
+
/** Longest `sleep` argument that made the command match. */
|
|
21
|
+
readonly seconds: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Classify a bash command as a sleep-wait, or `undefined` when it is ordinary
|
|
25
|
+
* work (including short settle sleeps below the thresholds).
|
|
26
|
+
*/
|
|
27
|
+
export declare function classifySleepWait(command: string): SleepWaitClassification | undefined;
|
|
28
|
+
//# sourceMappingURL=sleep-wait.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sleep-wait.d.ts","sourceRoot":"","sources":["../../../../../../src/core/extensions/builtin/terminal/tools/sleep-wait.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,yEAAyE;AACzE,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAC/C,mFAAmF;AACnF,eAAO,MAAM,iCAAiC,IAAI,CAAC;AAEnD,MAAM,MAAM,aAAa,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEtD,MAAM,WAAW,uBAAuB;IACvC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAuCD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,uBAAuB,GAAG,SAAS,CActF"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects commands whose whole purpose is to *wait* — `sleep 270; git log`,
|
|
3
|
+
* `for ...; do ...; sleep 5; done`, a bare `sleep 30`. Such a command should not
|
|
4
|
+
* hold the foreground: it detaches early so the session can keep working while
|
|
5
|
+
* the wait finishes in the background and its completion notification lands.
|
|
6
|
+
*
|
|
7
|
+
* Settle sleeps (`pkill; sleep 1`, a 2s server warm-up) are deliberately NOT
|
|
8
|
+
* sleep-waits: they are short by construction and detaching them would cost a
|
|
9
|
+
* notification round-trip for nothing.
|
|
10
|
+
*/
|
|
11
|
+
/** Minimum `sleep` seconds for a non-loop command to count as a wait. */
|
|
12
|
+
export const SLEEP_WAIT_THRESHOLD_SECONDS = 10;
|
|
13
|
+
/** Minimum per-iteration `sleep` seconds for a polling loop to count as a wait. */
|
|
14
|
+
export const SLEEP_WAIT_LOOP_THRESHOLD_SECONDS = 2;
|
|
15
|
+
/** `env A=1 bash -lc '...'` wrappers hide the real command from every rule below. */
|
|
16
|
+
const SHELL_WRAPPER = /^(?:env\s+(?:[\w.]+=[^\s]*\s+)*)?(?:\/(?:usr\/)?bin\/)?(?:ba|z|da|k)?sh\s+-[a-z]*c\s+/i;
|
|
17
|
+
/** Power management takes a `sleep` argument that has nothing to do with waiting. */
|
|
18
|
+
const POWER_MANAGEMENT = /\b(?:pmset|systemsetup|caffeinate|displaysleep|disksleep)\b/;
|
|
19
|
+
/** `sleep N` as a real command word — never `./sleepless 300` or `--sleep=3`. */
|
|
20
|
+
const SLEEP_CALL = /(?<![\w.\-/])(?:\/(?:usr\/)?bin\/)?sleep\s+(\d+(?:\.\d+)?)/g;
|
|
21
|
+
const PURE = /^sleep\s+\d+(?:\.\d+)?$/;
|
|
22
|
+
const LEADING = /^\(?\s*(?:\/(?:usr\/)?bin\/)?sleep\s+\d+(?:\.\d+)?\s*(?:[;&|]|$)/;
|
|
23
|
+
const LOOP = /\b(?:while|until|for)\b[\s\S]*?(?<![\w.\-/])(?:\/(?:usr\/)?bin\/)?sleep\s+\d/;
|
|
24
|
+
const TRAILING = /[;&|]\s*(?:\/(?:usr\/)?bin\/)?sleep\s+\d+(?:\.\d+)?\s*\)?\s*$/;
|
|
25
|
+
function unwrap(command) {
|
|
26
|
+
let current = command.trim();
|
|
27
|
+
for (let depth = 0; depth < 3; depth += 1) {
|
|
28
|
+
const stripped = current.replace(SHELL_WRAPPER, "");
|
|
29
|
+
if (stripped === current)
|
|
30
|
+
break;
|
|
31
|
+
current = stripped.trim();
|
|
32
|
+
const quote = current[0];
|
|
33
|
+
if ((quote === "'" || quote === '"') && current.endsWith(quote) && current.length > 1) {
|
|
34
|
+
current = current.slice(1, -1).trim();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return current;
|
|
38
|
+
}
|
|
39
|
+
function longestSleepSeconds(command) {
|
|
40
|
+
SLEEP_CALL.lastIndex = 0;
|
|
41
|
+
let longest;
|
|
42
|
+
for (const match of command.matchAll(SLEEP_CALL)) {
|
|
43
|
+
const seconds = Number.parseFloat(match[1] ?? "");
|
|
44
|
+
if (!Number.isFinite(seconds))
|
|
45
|
+
continue;
|
|
46
|
+
if (longest === undefined || seconds > longest)
|
|
47
|
+
longest = seconds;
|
|
48
|
+
}
|
|
49
|
+
return longest;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Classify a bash command as a sleep-wait, or `undefined` when it is ordinary
|
|
53
|
+
* work (including short settle sleeps below the thresholds).
|
|
54
|
+
*/
|
|
55
|
+
export function classifySleepWait(command) {
|
|
56
|
+
if (POWER_MANAGEMENT.test(command))
|
|
57
|
+
return undefined;
|
|
58
|
+
const unwrapped = unwrap(command);
|
|
59
|
+
const seconds = longestSleepSeconds(unwrapped);
|
|
60
|
+
if (seconds === undefined)
|
|
61
|
+
return undefined;
|
|
62
|
+
if (LOOP.test(unwrapped)) {
|
|
63
|
+
return seconds >= SLEEP_WAIT_LOOP_THRESHOLD_SECONDS ? { kind: "sleep-wait", rule: "R3", seconds } : undefined;
|
|
64
|
+
}
|
|
65
|
+
if (seconds < SLEEP_WAIT_THRESHOLD_SECONDS)
|
|
66
|
+
return undefined;
|
|
67
|
+
if (PURE.test(unwrapped))
|
|
68
|
+
return { kind: "sleep-wait", rule: "R1", seconds };
|
|
69
|
+
if (LEADING.test(unwrapped))
|
|
70
|
+
return { kind: "sleep-wait", rule: "R2", seconds };
|
|
71
|
+
if (TRAILING.test(unwrapped))
|
|
72
|
+
return { kind: "sleep-wait", rule: "R4", seconds };
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=sleep-wait.js.map
|