diff --git a/docs/tools/lsp.md b/docs/tools/lsp.md index ca4be1f86..f86f9e058 100644 --- a/docs/tools/lsp.md +++ b/docs/tools/lsp.md @@ -31,7 +31,7 @@ | `query` | string | No | Workspace symbol query, code-action selector/filter, or LSP method name for `action=request`. | | `new_name` | string | No | Required for `rename` and `rename_file`. | | `apply` | boolean | No | For `rename`/`rename_file`, apply unless explicitly `false`. For `code_actions`, list unless explicitly `true`. | -| `timeout` | number | No | Seconds, clamped by `clampTimeout("lsp", ...)` to `5..60`, default `20`. | +| `timeout` | number | No | Seconds, clamped by `clampTimeout("lsp", ...)` to `5..300`, default `20`. | | `payload` | string | No | JSON string for `action=request`; overrides auto-built params. | ## Outputs @@ -268,7 +268,7 @@ Same as `definition`, but sends `textDocument/implementation` and reports `imple - Background message readers persist for each live client until process exit/shutdown. ## Limits & Caps -- Tool timeout clamp: default `20`, min `5`, max `60` seconds — `TOOL_TIMEOUTS.lsp` in `packages/coding-agent/src/tools/tool-timeouts.ts`. +- Tool timeout clamp: default `20`, min `5`, max `300` seconds — `TOOL_TIMEOUTS.lsp` in `packages/coding-agent/src/tools/tool-timeouts.ts`. - LSP request default timeout inside `sendRequest()`: `30_000ms` — `DEFAULT_REQUEST_TIMEOUT_MS` in `packages/coding-agent/src/lsp/client.ts`. - Warmup initialize timeout default: `5_000ms` — `WARMUP_TIMEOUT_MS` in `packages/coding-agent/src/lsp/client.ts`. - Project-load wait fallback: `15_000ms` — `PROJECT_LOAD_TIMEOUT_MS` in `packages/coding-agent/src/lsp/client.ts`. diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index af229b38a..502248e34 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -99,6 +99,9 @@ ### Fixed - Fixed non-raw `read` line selectors returning context outside the requested inclusive range ([#5802](https://github.com/can1357/oh-my-pi/issues/5802)). +### Fixed + +- Fixed LSP requests silently clamping explicit timeouts above 60 seconds by supporting documented budgets up to 300 seconds ([#5804](https://github.com/can1357/oh-my-pi/issues/5804)). ## [17.0.2] - 2026-07-17 diff --git a/packages/coding-agent/src/lsp/types.ts b/packages/coding-agent/src/lsp/types.ts index fc262ee8a..db3f9d720 100644 --- a/packages/coding-agent/src/lsp/types.ts +++ b/packages/coding-agent/src/lsp/types.ts @@ -1,5 +1,6 @@ import type { ptree } from "@oh-my-pi/pi-utils"; import { type } from "arktype"; +import { TOOL_TIMEOUTS } from "../tools/tool-timeouts"; // ============================================================================= // Tool Schema @@ -14,7 +15,10 @@ export const lspSchema = type({ query: "string?", new_name: "string?", apply: "boolean?", - timeout: "number?", + "timeout?": type.number + .atLeast(TOOL_TIMEOUTS.lsp.min) + .atMost(TOOL_TIMEOUTS.lsp.max) + .describe("Timeout in seconds (default 20; range 5–300)."), payload: "string?", }); diff --git a/packages/coding-agent/src/tools/tool-timeouts.ts b/packages/coding-agent/src/tools/tool-timeouts.ts index 8d5342b93..102c640ec 100644 --- a/packages/coding-agent/src/tools/tool-timeouts.ts +++ b/packages/coding-agent/src/tools/tool-timeouts.ts @@ -13,7 +13,7 @@ export const TOOL_TIMEOUTS = { browser: { default: 30, min: 1, max: 300 }, ssh: { default: 60, min: 1, max: 3600 }, fetch: { default: 20, min: 1, max: 45 }, - lsp: { default: 20, min: 5, max: 60 }, + lsp: { default: 20, min: 5, max: 300 }, debug: { default: 30, min: 5, max: 300 }, } as const satisfies Record; diff --git a/packages/coding-agent/test/tools/lsp-regressions.test.ts b/packages/coding-agent/test/tools/lsp-regressions.test.ts index 0b64202ac..8a215fcc0 100644 --- a/packages/coding-agent/test/tools/lsp-regressions.test.ts +++ b/packages/coding-agent/test/tools/lsp-regressions.test.ts @@ -3,6 +3,7 @@ import * as fs from "node:fs"; import * as os from "node:os"; import * as path from "node:path"; import type { AgentToolResult, RenderResultOptions } from "@oh-my-pi/pi-agent-core"; +import { arkToWireSchema } from "@oh-my-pi/pi-ai/utils/schema"; import { preloadPluginRoots } from "@oh-my-pi/pi-coding-agent/discovery/helpers"; import { LspTool } from "@oh-my-pi/pi-coding-agent/lsp"; import * as lspClient from "@oh-my-pi/pi-coding-agent/lsp/client"; @@ -14,18 +15,19 @@ import { sortAndValidateTextEdits, } from "@oh-my-pi/pi-coding-agent/lsp/edits"; import { renderCall, renderResult } from "@oh-my-pi/pi-coding-agent/lsp/render"; -import type { - CodeAction, - CreateFile, - DeleteFile, - Diagnostic, - LspClient, - LspToolDetails, - RenameFile, - ServerConfig, - SymbolInformation, - TextDocumentEdit, - WorkspaceEdit, +import { + type CodeAction, + type CreateFile, + type DeleteFile, + type Diagnostic, + type LspClient, + type LspToolDetails, + lspSchema, + type RenameFile, + type ServerConfig, + type SymbolInformation, + type TextDocumentEdit, + type WorkspaceEdit, } from "@oh-my-pi/pi-coding-agent/lsp/types"; import { applyCodeAction, @@ -253,10 +255,20 @@ describe("lsp regressions", () => { expect(hasGlobPattern("src/main.ts")).toBe(false); }); - it("clamps LSP timeout to configured bounds", () => { + it("supports long LSP timeouts up to the advertised ceiling", () => { expect(clampTimeout("lsp")).toBe(20); expect(clampTimeout("lsp", 1)).toBe(5); - expect(clampTimeout("lsp", 1000)).toBe(60); + expect(clampTimeout("lsp", 120)).toBe(120); + expect(clampTimeout("lsp", 1000)).toBe(300); + expect(arkToWireSchema(lspSchema)).toMatchObject({ + properties: { + timeout: { + description: "Timeout in seconds (default 20; range 5–300).", + maximum: 300, + minimum: 5, + }, + }, + }); }); it("sends the LSP exit notification after shutdown completes", async () => {