Files
shannon/apps/worker/src/ai/extensions/bash-timeout/index.ts
T
ezl-keygraph 1ce250d6a5 feat: multi-provider model support, SARIF output, and exploit-mode fixes (#402)
* feat(worker): record token, cache, and turn usage per agent

* feat: replace model tiers with a single SHANNON_AI_MODEL across five providers

* feat(cli): rebuild the setup wizard for provider and model selection

* docs: document single-model selection and supported providers

* feat(worker): use chat completions for OpenAI behind a custom base URL

* feat: add SHANNON_AI_OPENAI_FORMAT to pick the wire API for OpenAI gateways

* refactor(cli): drop endpoint path hints from the gateway format picker

* feat(worker): enable pi in-session provider retry with retry-after backoff

* refactor(worker): hand provider error classification to pi and drop the Anthropic ladders

* refactor: remove the subscription retry preset and pipeline config section

* fix(worker): validate Bedrock credentials with the same live probe as other providers

* feat(worker): render the report from structured findings instead of agent-written markdown

* fix(worker): dispose the credential probe session on every path

* fix(worker): refuse to replace the assembled report with an empty one

* refactor(worker): catch post-processing throws across the whole finalization block

* revert(worker): drop the report zero-findings guard

* docs(worker): correct the retry split and Bedrock credential claims

* docs: regenerate llms-full.txt from current sources

* feat(cli): build and run the npx flow from a clone

* refactor(cli): flatten the setup summary output

* feat(cli): reject runs with more than one provider configured

* fix(worker): say a rejected bash call never ran

* chore(cli): drop grok-4.3 and gpt-5.6-luna from the setup suggestions

* feat(worker): capture structured finding locations for SARIF output

* fix(worker): enumerate queue confidence so the report inherits it verbatim

* feat(worker): give the reporting phase a mode-specific output schema

* feat(worker): emit a SARIF 2.1.0 log for exploitative runs

* fix(worker): correct SARIF locations and defer fingerprinting to the upload action

* fix(worker): drop the confidence suffix from the analysis-mode summary list

* feat(worker): give exploit findings a dedicated code location field

* feat(worker): carry structured code locations from the vuln queue to the report

* fix(worker): join code locations from the vuln queue instead of re-asking agents

* fix(worker): spell out the finding_id to category mapping in the tool schema

* feat: drop Google/Gemini as a supported AI provider

* fix(worker): stop asking the report agent for code locations

* docs: correct the provider list and drop the removed rate-limit settings

* docs: add provider cyber safeguards and suggested models per provider

* docs: document the SARIF output and the report rating thresholds
2026-07-30 19:31:52 +05:30

48 lines
1.9 KiB
TypeScript

/**
* pi extension: enforce a bounded timeout on every `bash` tool call.
*
* pi's built-in bash tool accepts an optional `timeout` (in seconds) but applies
* NO default and NO upper bound — an unbounded command (e.g. a `playwright-cli`
* browser action that never returns) hangs the agent indefinitely. This extension
* registers a `tool_call` pre-execution handler that blocks any `bash` invocation
* that omits `timeout` or sets it above the maximum, returning a message that tells
* the model how to re-run the command correctly.
*/
import type { ExtensionAPI, ToolCallEvent, ToolCallEventResult } from '@earendil-works/pi-coding-agent';
import { isToolCallEventType } from '@earendil-works/pi-coding-agent';
/** Recommended timeout (seconds) suggested to the model when it omits one. */
const DEFAULT_TIMEOUT_SECONDS = 120;
/** Hard upper bound (seconds) a single bash command may run. */
const MAX_TIMEOUT_SECONDS = 600;
function evaluateBashTimeout(timeout: number | undefined): ToolCallEventResult | undefined {
const hasValidTimeout = typeof timeout === 'number' && Number.isFinite(timeout) && timeout > 0;
if (!hasValidTimeout) {
return {
block: true,
reason: `A timeout in seconds is required for the bash tool. The bash tool was not executed. Use the default of ${DEFAULT_TIMEOUT_SECONDS} seconds, or up to a maximum of ${MAX_TIMEOUT_SECONDS} seconds.`,
};
}
if (timeout > MAX_TIMEOUT_SECONDS) {
return {
block: true,
reason: `bash 'timeout' ${timeout}s exceeds max ${MAX_TIMEOUT_SECONDS}s. Default ${DEFAULT_TIMEOUT_SECONDS}s, max ${MAX_TIMEOUT_SECONDS}s.`,
};
}
return undefined;
}
export default function bashTimeoutExtension(pi: ExtensionAPI): void {
pi.on('tool_call', (event: ToolCallEvent): ToolCallEventResult | undefined => {
if (!isToolCallEventType('bash', event)) {
return undefined;
}
return evaluateBashTimeout(event.input.timeout);
});
}