@oh-my-pi/pi-coding-agent 17.2.4 → 17.2.6
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 +69 -0
- package/dist/{CHANGELOG-bpmhv26t.md → CHANGELOG-gs76k6wc.md} +69 -0
- package/dist/cli.js +4438 -3962
- package/dist/{template-8vdv6xb4.js → template-c2hyaytt.js} +8 -26
- package/dist/types/cli/args.d.ts +4 -1
- package/dist/types/cli/browser-relay-cli.d.ts +15 -0
- package/dist/types/cli/command-help.d.ts +99 -0
- package/dist/types/cli/gc-cli.d.ts +1 -0
- package/dist/types/cli/help-extra.d.ts +2 -0
- package/dist/types/cli/thinking-levels.d.ts +5 -0
- package/dist/types/cli/update-cli.d.ts +13 -0
- package/dist/types/commands/browser-relay.d.ts +38 -0
- package/dist/types/commands/commit.d.ts +3 -0
- package/dist/types/commands/complete.d.ts +1 -1
- package/dist/types/commands/completions.d.ts +6 -0
- package/dist/types/commands/grep.d.ts +3 -0
- package/dist/types/commands/join.d.ts +4 -0
- package/dist/types/commands/launch-help.d.ts +171 -0
- package/dist/types/commands/launch.d.ts +5 -1
- package/dist/types/commands/models.d.ts +3 -0
- package/dist/types/commands/say.d.ts +10 -0
- package/dist/types/commands/worktree.d.ts +3 -0
- package/dist/types/config/keybindings.d.ts +5 -0
- package/dist/types/config/model-discovery.d.ts +2 -2
- package/dist/types/config/models-config-schema-bundle.d.ts +3 -0
- package/dist/types/config/models-config-schema.d.ts +2 -0
- package/dist/types/config/models-config.d.ts +1 -0
- package/dist/types/config/service-tier.d.ts +2 -0
- package/dist/types/config/settings-schema.d.ts +69 -21
- package/dist/types/cursor-bridge-tools.d.ts +1 -1
- package/dist/types/cursor.d.ts +7 -4
- package/dist/types/edit/index.d.ts +3 -3
- package/dist/types/edit/modes/replace.d.ts +22 -15
- package/dist/types/edit/renderer.d.ts +1 -1
- package/dist/types/eval/py/kernel.d.ts +3 -1
- package/dist/types/eval/py/spawn-options.d.ts +12 -16
- package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +8 -1
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +1 -1
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +2 -0
- package/dist/types/launch/broker.d.ts +1 -1
- package/dist/types/launch/client.d.ts +15 -4
- package/dist/types/launch/ensure.d.ts +8 -0
- package/dist/types/launch/paths.d.ts +1 -1
- package/dist/types/launch/protocol.d.ts +19 -2
- package/dist/types/lsp/client.d.ts +2 -0
- package/dist/types/lsp/mux/daemon.d.ts +15 -0
- package/dist/types/lsp/mux/protocol.d.ts +57 -0
- package/dist/types/lsp/mux/server.d.ts +18 -0
- package/dist/types/lsp/types.d.ts +22 -2
- package/dist/types/main.d.ts +2 -0
- package/dist/types/mnemopi/embed-client.d.ts +1 -1
- package/dist/types/mnemopi/state.d.ts +0 -23
- package/dist/types/modes/components/assistant-message.d.ts +2 -0
- package/dist/types/modes/components/btw-panel.d.ts +3 -0
- package/dist/types/modes/components/custom-editor.d.ts +2 -1
- package/dist/types/modes/components/read-tool-group.d.ts +2 -0
- package/dist/types/modes/components/stripped-tool-calls-placeholder.d.ts +12 -0
- package/dist/types/modes/components/tool-execution.d.ts +2 -0
- package/dist/types/modes/controllers/btw-controller.d.ts +2 -0
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +5 -1
- package/dist/types/modes/types.d.ts +4 -1
- package/dist/types/modes/workflow.d.ts +2 -1
- package/dist/types/sdk.d.ts +3 -1
- package/dist/types/secrets/index.d.ts +1 -1
- package/dist/types/security/contracts/schemas.d.ts +405 -403
- package/dist/types/session/agent-session-types.d.ts +8 -1
- package/dist/types/session/agent-session.d.ts +26 -2
- package/dist/types/session/indexed-session-storage.d.ts +1 -0
- package/dist/types/session/launch-completion.d.ts +10 -0
- package/dist/types/session/session-advisors.d.ts +1 -1
- package/dist/types/session/session-context.d.ts +2 -1
- package/dist/types/session/session-entries.d.ts +15 -1
- package/dist/types/session/session-maintenance.d.ts +1 -0
- package/dist/types/session/session-manager.d.ts +23 -11
- package/dist/types/session/session-stats.d.ts +10 -0
- package/dist/types/session/session-storage.d.ts +15 -3
- package/dist/types/session/yield-queue.d.ts +6 -1
- package/dist/types/system-prompt.d.ts +4 -1
- package/dist/types/task/index.d.ts +2 -1
- package/dist/types/task/spawn-policy.d.ts +5 -0
- package/dist/types/thinking.d.ts +1 -7
- package/dist/types/tools/ast-grep.d.ts +1 -1
- package/dist/types/tools/browser/registry.d.ts +2 -1
- package/dist/types/tools/browser/relay/bridge.d.ts +35 -0
- package/dist/types/tools/browser/relay/daemon.d.ts +16 -0
- package/dist/types/tools/browser/relay/kind.d.ts +19 -0
- package/dist/types/tools/browser/relay/protocol.d.ts +97 -0
- package/dist/types/tools/browser/relay/server.d.ts +35 -0
- package/dist/types/tools/browser/render.d.ts +1 -0
- package/dist/types/tools/browser.d.ts +3 -0
- package/dist/types/tools/computer/exposure.d.ts +9 -7
- package/dist/types/tools/computer/protocol.d.ts +70 -17
- package/dist/types/tools/computer/supervisor.d.ts +23 -10
- package/dist/types/tools/computer/worker-entry.d.ts +1 -0
- package/dist/types/tools/computer/worker.d.ts +42 -7
- package/dist/types/tools/computer-renderer.d.ts +7 -5
- package/dist/types/tools/computer.d.ts +25 -44
- package/dist/types/tools/context.d.ts +3 -0
- package/dist/types/tools/glob.d.ts +1 -1
- package/dist/types/tools/grep.d.ts +1 -1
- package/dist/types/tools/index.d.ts +9 -0
- package/dist/types/tools/render-utils.d.ts +2 -0
- package/dist/types/tools/{browser/run-cancellation.d.ts → run-scope.d.ts} +8 -8
- package/dist/types/tools/tool-timeouts.d.ts +5 -0
- package/dist/types/tools/xdev.d.ts +8 -0
- package/dist/types/utils/clipboard.d.ts +2 -2
- package/dist/types/web/kagi.d.ts +1 -0
- package/dist/types/web/search/index.d.ts +1 -1
- package/dist/types/web/search/providers/anthropic.d.ts +1 -0
- package/dist/types/web/search/providers/base.d.ts +2 -0
- package/dist/types/web/search/providers/brave.d.ts +1 -0
- package/dist/types/web/search/providers/browser-page.d.ts +1 -0
- package/dist/types/web/search/providers/codex.d.ts +1 -0
- package/dist/types/web/search/providers/exa.d.ts +1 -0
- package/dist/types/web/search/providers/firecrawl.d.ts +1 -0
- package/dist/types/web/search/providers/gemini.d.ts +1 -0
- package/dist/types/web/search/providers/jina.d.ts +1 -0
- package/dist/types/web/search/providers/kagi.d.ts +1 -0
- package/dist/types/web/search/providers/kimi.d.ts +1 -0
- package/dist/types/web/search/providers/parallel.d.ts +1 -0
- package/dist/types/web/search/providers/perplexity.d.ts +1 -0
- package/dist/types/web/search/providers/searxng.d.ts +1 -0
- package/dist/types/web/search/providers/tavily.d.ts +1 -0
- package/dist/types/web/search/providers/tinyfish.d.ts +1 -0
- package/dist/types/web/search/providers/utils.d.ts +6 -9
- package/dist/types/web/search/providers/zai.d.ts +1 -0
- package/dist/types/web/search/types.d.ts +4 -0
- package/package.json +12 -12
- package/src/cli/args.ts +7 -87
- package/src/cli/browser-relay-cli.ts +119 -0
- package/src/cli/command-help.ts +103 -0
- package/src/cli/completion-gen.ts +5 -5
- package/src/cli/flag-tables.ts +9 -0
- package/src/cli/gc-cli.ts +641 -21
- package/src/cli/help-extra.ts +89 -0
- package/src/cli/setup-cli.ts +13 -21
- package/src/cli/thinking-levels.ts +7 -0
- package/src/cli/update-cli.ts +26 -7
- package/src/cli-commands.ts +170 -33
- package/src/cli.ts +17 -5
- package/src/commands/acp.ts +3 -1
- package/src/commands/agents.ts +3 -2
- package/src/commands/auth-broker.ts +3 -2
- package/src/commands/auth-gateway.ts +3 -2
- package/src/commands/bench.ts +2 -3
- package/src/commands/browser-relay.ts +53 -0
- package/src/commands/cleanse.ts +2 -2
- package/src/commands/commit.ts +3 -2
- package/src/commands/complete.ts +2 -1
- package/src/commands/completions.ts +3 -2
- package/src/commands/config.ts +3 -2
- package/src/commands/dry-balance.ts +2 -2
- package/src/commands/gallery.ts +3 -2
- package/src/commands/gc.ts +3 -2
- package/src/commands/grep.ts +3 -2
- package/src/commands/grievances.ts +3 -2
- package/src/commands/install.ts +2 -2
- package/src/commands/join.ts +3 -2
- package/src/commands/launch-help.ts +116 -0
- package/src/commands/launch.ts +7 -191
- package/src/commands/models.ts +3 -2
- package/src/commands/plugin.ts +3 -2
- package/src/commands/read.ts +3 -2
- package/src/commands/say.ts +3 -2
- package/src/commands/setup.ts +3 -2
- package/src/commands/shell.ts +3 -2
- package/src/commands/ssh.ts +3 -2
- package/src/commands/stats.ts +3 -2
- package/src/commands/tiny-models.ts +2 -2
- package/src/commands/token.ts +2 -2
- package/src/commands/ttsr.ts +2 -2
- package/src/commands/update.ts +3 -2
- package/src/commands/usage.ts +3 -2
- package/src/commands/web-search.ts +3 -2
- package/src/commands/worktree.ts +3 -2
- package/src/config/keybindings.ts +5 -0
- package/src/config/model-discovery.ts +44 -27
- package/src/config/model-registry.ts +19 -13
- package/src/config/models-config-schema-bundle.ts +9 -0
- package/src/config/resolve-config-value.ts +2 -1
- package/src/config/service-tier.ts +5 -0
- package/src/config/settings-schema.ts +72 -20
- package/src/config/settings.ts +1 -1
- package/src/cursor-bridge-tools.ts +1 -1
- package/src/cursor.ts +10 -14
- package/src/edit/diff.ts +17 -8
- package/src/edit/index.ts +20 -7
- package/src/edit/modes/replace.ts +93 -40
- package/src/edit/renderer.ts +1 -1
- package/src/edit/streaming.ts +14 -17
- package/src/eval/py/kernel.ts +2 -1
- package/src/eval/py/spawn-options.ts +24 -29
- package/src/export/html/index.ts +10 -3
- package/src/export/html/template.js +8 -26
- package/src/export/share.ts +4 -0
- package/src/extensibility/extensions/wrapper.ts +1 -0
- package/src/extensibility/legacy-pi-ai-shim.ts +22 -1
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +7 -6
- package/src/extensibility/plugins/legacy-pi-compat.ts +124 -57
- package/src/extensibility/skills.ts +11 -0
- package/src/extensibility/utils.ts +10 -2
- package/src/launch/broker.ts +223 -7
- package/src/launch/client.ts +192 -21
- package/src/launch/ensure.ts +73 -0
- package/src/launch/paths.ts +1 -1
- package/src/launch/protocol.ts +54 -2
- package/src/lsp/client.ts +23 -2
- package/src/lsp/index.ts +8 -0
- package/src/lsp/mux/daemon.ts +348 -0
- package/src/lsp/mux/protocol.ts +96 -0
- package/src/lsp/mux/server.ts +797 -0
- package/src/lsp/types.ts +28 -2
- package/src/main.ts +20 -8
- package/src/mcp/config-writer.ts +1 -1
- package/src/mcp/oauth-flow.ts +11 -1
- package/src/mnemopi/embed-client.ts +49 -2
- package/src/mnemopi/state.ts +57 -23
- package/src/modes/components/assistant-message.ts +11 -0
- package/src/modes/components/btw-panel.ts +41 -4
- package/src/modes/components/chat-transcript-builder.ts +4 -0
- package/src/modes/components/custom-editor.ts +9 -0
- package/src/modes/components/read-tool-group.ts +10 -0
- package/src/modes/components/status-line/component.ts +1 -1
- package/src/modes/components/stripped-tool-calls-placeholder.ts +35 -0
- package/src/modes/components/tool-execution.ts +8 -0
- package/src/modes/components/tree-selector.ts +46 -48
- package/src/modes/controllers/btw-controller.ts +55 -7
- package/src/modes/controllers/command-controller.ts +31 -0
- package/src/modes/controllers/event-controller.ts +17 -0
- package/src/modes/controllers/input-controller.ts +63 -8
- package/src/modes/controllers/mcp-command-controller.ts +11 -5
- package/src/modes/controllers/selector-controller.ts +20 -0
- package/src/modes/interactive-mode.ts +18 -2
- package/src/modes/types.ts +9 -1
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/interactive-context-helpers.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +6 -10
- package/src/modes/workflow.ts +8 -2
- package/src/prompts/agents/init.md +1 -1
- package/src/prompts/session/launch-completion.md +1 -0
- package/src/prompts/system/plan-mode-active.md +2 -2
- package/src/prompts/system/system-prompt.md +26 -52
- package/src/prompts/system/workflow-notice.md +3 -3
- package/src/prompts/tools/ast-grep.md +1 -1
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/browser.md +1 -0
- package/src/prompts/tools/computer.md +21 -21
- package/src/prompts/tools/glob.md +1 -1
- package/src/prompts/tools/grep.md +1 -1
- package/src/prompts/tools/replace.md +5 -4
- package/src/prompts/tools/task.md +4 -4
- package/src/sdk.ts +49 -6
- package/src/secrets/index.ts +1 -1
- package/src/security/contracts/schemas.ts +205 -183
- package/src/security/contracts/validation.ts +5 -1
- package/src/security/store.ts +2 -2
- package/src/session/agent-session-types.ts +9 -1
- package/src/session/agent-session.ts +204 -15
- package/src/session/indexed-session-storage.ts +9 -0
- package/src/session/launch-completion.ts +37 -0
- package/src/session/prewalk.ts +24 -2
- package/src/session/session-advisors.ts +2 -2
- package/src/session/session-context.ts +42 -12
- package/src/session/session-dump-format.ts +1 -1
- package/src/session/session-entries.ts +17 -1
- package/src/session/session-maintenance.ts +38 -4
- package/src/session/session-manager.ts +123 -67
- package/src/session/session-paths.ts +118 -55
- package/src/session/session-stats.ts +48 -3
- package/src/session/session-storage.ts +30 -22
- package/src/session/stream-guards.ts +1 -1
- package/src/session/yield-queue.ts +121 -16
- package/src/slash-commands/builtin-registry.ts +37 -24
- package/src/system-prompt.ts +8 -3
- package/src/task/index.ts +23 -8
- package/src/task/spawn-policy.ts +14 -0
- package/src/thinking.ts +2 -8
- package/src/tools/ast-grep.ts +10 -4
- package/src/tools/browser/cmux/cmux-tab.ts +97 -14
- package/src/tools/browser/cmux/rpc.ts +2 -8
- package/src/tools/browser/registry.ts +53 -2
- package/src/tools/browser/relay/bridge.ts +945 -0
- package/src/tools/browser/relay/daemon.ts +117 -0
- package/src/tools/browser/relay/extension-assets/background.js.txt +242 -0
- package/src/tools/browser/relay/extension-assets/manifest.json.txt +14 -0
- package/src/tools/browser/relay/extension-assets/options.html.txt +53 -0
- package/src/tools/browser/relay/extension-assets/options.js.txt +23 -0
- package/src/tools/browser/relay/kind.ts +41 -0
- package/src/tools/browser/relay/protocol.ts +54 -0
- package/src/tools/browser/relay/server.ts +141 -0
- package/src/tools/browser/render.ts +4 -1
- package/src/tools/browser/shared-daemon.ts +7 -58
- package/src/tools/browser/tab-supervisor.ts +5 -4
- package/src/tools/browser/tab-worker.ts +33 -12
- package/src/tools/browser.ts +26 -1
- package/src/tools/computer/exposure.ts +11 -35
- package/src/tools/computer/protocol.ts +51 -10
- package/src/tools/computer/supervisor.ts +227 -124
- package/src/tools/computer/worker-entry.ts +11 -6
- package/src/tools/computer/worker.ts +695 -81
- package/src/tools/computer-renderer.ts +104 -65
- package/src/tools/computer.ts +134 -428
- package/src/tools/context.ts +3 -0
- package/src/tools/glob.ts +9 -2
- package/src/tools/grep.ts +12 -6
- package/src/tools/hub/launch.ts +122 -6
- package/src/tools/index.ts +9 -0
- package/src/tools/inspect-image.ts +32 -6
- package/src/tools/render-utils.ts +2 -0
- package/src/tools/{browser/run-cancellation.ts → run-scope.ts} +11 -11
- package/src/tools/tool-timeouts.ts +1 -0
- package/src/tools/xdev.ts +29 -2
- package/src/utils/clipboard.ts +40 -14
- package/src/utils/command-args.ts +26 -28
- package/src/utils/tool-choice.ts +0 -13
- package/src/web/kagi.ts +2 -1
- package/src/web/search/index.ts +18 -2
- package/src/web/search/providers/anthropic.ts +4 -1
- package/src/web/search/providers/base.ts +2 -0
- package/src/web/search/providers/brave.ts +3 -1
- package/src/web/search/providers/browser-page.ts +6 -4
- package/src/web/search/providers/codex.ts +4 -1
- package/src/web/search/providers/duckduckgo.ts +2 -1
- package/src/web/search/providers/ecosia.ts +2 -1
- package/src/web/search/providers/exa.ts +4 -2
- package/src/web/search/providers/firecrawl.ts +3 -1
- package/src/web/search/providers/gemini.ts +8 -2
- package/src/web/search/providers/google.ts +2 -1
- package/src/web/search/providers/jina.ts +12 -2
- package/src/web/search/providers/kagi.ts +3 -0
- package/src/web/search/providers/kimi.ts +5 -1
- package/src/web/search/providers/mojeek.ts +2 -1
- package/src/web/search/providers/parallel.ts +5 -1
- package/src/web/search/providers/perplexity.ts +6 -3
- package/src/web/search/providers/public.ts +1 -1
- package/src/web/search/providers/searxng.ts +11 -5
- package/src/web/search/providers/startpage.ts +10 -4
- package/src/web/search/providers/synthetic.ts +11 -5
- package/src/web/search/providers/tavily.ts +3 -1
- package/src/web/search/providers/tinyfish.ts +3 -1
- package/src/web/search/providers/utils.ts +12 -10
- package/src/web/search/providers/xai.ts +2 -2
- package/src/web/search/providers/zai.ts +18 -3
- package/src/web/search/types.ts +6 -0
- package/dist/types/config/file-lock.d.ts +0 -29
- package/src/config/file-lock.ts +0 -164
|
@@ -121,10 +121,14 @@ function parseHtmlResults(html: string): ParsedResult[] {
|
|
|
121
121
|
* any failure (network, non-OK status, challenge shell, markup drift) yields
|
|
122
122
|
* `undefined` and the caller falls back to a direct GET.
|
|
123
123
|
*/
|
|
124
|
-
async function fetchFormInputs(
|
|
124
|
+
async function fetchFormInputs(
|
|
125
|
+
fetchImpl: FetchImpl,
|
|
126
|
+
signal: AbortSignal,
|
|
127
|
+
timeoutMs?: number,
|
|
128
|
+
): Promise<Record<string, string> | undefined> {
|
|
125
129
|
let page: LoadedHtmlPage;
|
|
126
130
|
try {
|
|
127
|
-
page = await browserFetch(STARTPAGE_HOME_URL, { fetch: fetchImpl, signal });
|
|
131
|
+
page = await browserFetch(STARTPAGE_HOME_URL, { fetch: fetchImpl, signal, timeoutMs });
|
|
128
132
|
} catch (error) {
|
|
129
133
|
if (signal.aborted) throw error;
|
|
130
134
|
return undefined;
|
|
@@ -135,7 +139,7 @@ async function fetchFormInputs(fetchImpl: FetchImpl, signal: AbortSignal): Promi
|
|
|
135
139
|
|
|
136
140
|
async function callStartpageHtml(params: SearchParams): Promise<string> {
|
|
137
141
|
const fetchImpl = params.fetch ?? fetch;
|
|
138
|
-
const signal = withHardTimeout(params.signal);
|
|
142
|
+
const signal = withHardTimeout(params.signal, params.timeoutMs);
|
|
139
143
|
const withDate = params.recency ? RECENCY_TO_STARTPAGE_WITH_DATE[params.recency] : undefined;
|
|
140
144
|
// Startpage proxies Google, so the operator set works inline; rebuild via
|
|
141
145
|
// the shared scraper formatter to canonicalize aliases (domain: → site:,
|
|
@@ -143,7 +147,7 @@ async function callStartpageHtml(params: SearchParams): Promise<string> {
|
|
|
143
147
|
// queries pass through byte-identical.
|
|
144
148
|
const query = formatScraperQuery(params.query, params.parsedQuery);
|
|
145
149
|
|
|
146
|
-
const formInputs = await fetchFormInputs(fetchImpl, signal);
|
|
150
|
+
const formInputs = await fetchFormInputs(fetchImpl, signal, params.timeoutMs);
|
|
147
151
|
let page: LoadedHtmlPage;
|
|
148
152
|
if (formInputs) {
|
|
149
153
|
const form = new URLSearchParams(formInputs);
|
|
@@ -152,6 +156,7 @@ async function callStartpageHtml(params: SearchParams): Promise<string> {
|
|
|
152
156
|
page = await browserFetch(STARTPAGE_SEARCH_URL, {
|
|
153
157
|
fetch: fetchImpl,
|
|
154
158
|
signal,
|
|
159
|
+
timeoutMs: params.timeoutMs,
|
|
155
160
|
referer: STARTPAGE_HOME_URL,
|
|
156
161
|
init: { method: "POST", body: form.toString() },
|
|
157
162
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
@@ -163,6 +168,7 @@ async function callStartpageHtml(params: SearchParams): Promise<string> {
|
|
|
163
168
|
page = await browserFetch(url.href, {
|
|
164
169
|
fetch: fetchImpl,
|
|
165
170
|
signal,
|
|
171
|
+
timeoutMs: params.timeoutMs,
|
|
166
172
|
referer: STARTPAGE_HOME_URL,
|
|
167
173
|
});
|
|
168
174
|
}
|
|
@@ -43,6 +43,7 @@ async function callSyntheticSearch(
|
|
|
43
43
|
query: string,
|
|
44
44
|
signal?: AbortSignal,
|
|
45
45
|
fetchImpl: FetchImpl = fetch,
|
|
46
|
+
timeoutMs?: number,
|
|
46
47
|
): Promise<SyntheticSearchResponse> {
|
|
47
48
|
const response = await fetchImpl(SYNTHETIC_SEARCH_URL, {
|
|
48
49
|
method: "POST",
|
|
@@ -51,7 +52,7 @@ async function callSyntheticSearch(
|
|
|
51
52
|
Authorization: `Bearer ${apiKey}`,
|
|
52
53
|
},
|
|
53
54
|
body: JSON.stringify({ query }),
|
|
54
|
-
signal: withHardTimeout(signal),
|
|
55
|
+
signal: withHardTimeout(signal, timeoutMs),
|
|
55
56
|
});
|
|
56
57
|
|
|
57
58
|
if (!response.ok) {
|
|
@@ -80,10 +81,15 @@ export async function searchSynthetic(params: SearchParamsWithFetch): Promise<Se
|
|
|
80
81
|
: params.query;
|
|
81
82
|
|
|
82
83
|
const fetchImpl = params.fetch;
|
|
83
|
-
const data = await withAuth(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
const data = await withAuth(
|
|
85
|
+
keyOrResolver,
|
|
86
|
+
key => callSyntheticSearch(key, query, params.signal, fetchImpl, params.timeoutMs),
|
|
87
|
+
{
|
|
88
|
+
signal: params.signal,
|
|
89
|
+
missingKeyMessage:
|
|
90
|
+
"Synthetic credentials not found. Set SYNTHETIC_API_KEY or login with 'omp /login synthetic'.",
|
|
91
|
+
},
|
|
92
|
+
);
|
|
87
93
|
const sources: SearchSource[] = [];
|
|
88
94
|
|
|
89
95
|
for (const result of data.results ?? []) {
|
|
@@ -30,6 +30,7 @@ export interface TavilySearchParams {
|
|
|
30
30
|
/** `before:` upper bound, ISO `YYYY-MM-DD`, mapped to `end_date`. */
|
|
31
31
|
end_date?: string;
|
|
32
32
|
signal?: AbortSignal;
|
|
33
|
+
timeoutMs?: number;
|
|
33
34
|
fetch?: FetchImpl;
|
|
34
35
|
}
|
|
35
36
|
|
|
@@ -120,7 +121,7 @@ async function callTavilySearch(apiKey: string, params: TavilySearchParams): Pro
|
|
|
120
121
|
Authorization: `Bearer ${apiKey}`,
|
|
121
122
|
},
|
|
122
123
|
body: JSON.stringify(buildRequestBody(params)),
|
|
123
|
-
signal: withHardTimeout(params.signal),
|
|
124
|
+
signal: withHardTimeout(params.signal, params.timeoutMs),
|
|
124
125
|
});
|
|
125
126
|
|
|
126
127
|
if (!response.ok) {
|
|
@@ -189,6 +190,7 @@ export async function searchTavily(params: SearchParams): Promise<SearchResponse
|
|
|
189
190
|
num_results: params.numSearchResults ?? params.limit,
|
|
190
191
|
recency: params.recency,
|
|
191
192
|
signal: params.signal,
|
|
193
|
+
timeoutMs: params.timeoutMs,
|
|
192
194
|
fetch: params.fetch,
|
|
193
195
|
};
|
|
194
196
|
if (parsed.hasDirectives) {
|
|
@@ -34,6 +34,7 @@ export interface TinyFishSearchParams {
|
|
|
34
34
|
recency?: SearchParams["recency"];
|
|
35
35
|
page?: number;
|
|
36
36
|
signal?: AbortSignal;
|
|
37
|
+
timeoutMs?: number;
|
|
37
38
|
fetch?: FetchImpl;
|
|
38
39
|
}
|
|
39
40
|
|
|
@@ -78,7 +79,7 @@ async function callTinyFishSearch(apiKey: string, params: TinyFishSearchParams):
|
|
|
78
79
|
Accept: "application/json",
|
|
79
80
|
"X-API-Key": apiKey,
|
|
80
81
|
},
|
|
81
|
-
signal: withHardTimeout(params.signal),
|
|
82
|
+
signal: withHardTimeout(params.signal, params.timeoutMs),
|
|
82
83
|
});
|
|
83
84
|
|
|
84
85
|
if (!response.ok) {
|
|
@@ -117,6 +118,7 @@ export async function searchTinyFish(params: SearchParams): Promise<SearchRespon
|
|
|
117
118
|
num_results: pageSize,
|
|
118
119
|
recency: params.recency,
|
|
119
120
|
signal: params.signal,
|
|
121
|
+
timeoutMs: params.timeoutMs,
|
|
120
122
|
fetch: params.fetch,
|
|
121
123
|
};
|
|
122
124
|
const keyOrResolver: ApiKey = params.authStorage.resolver("tinyfish", {
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { AgentStorage } from "../../../session/agent-storage";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_WEB_SEARCH_TIMEOUT_SECONDS,
|
|
4
|
+
SearchProviderError,
|
|
5
|
+
type SearchProviderId,
|
|
6
|
+
type SearchSource,
|
|
7
|
+
} from "../../../web/search/types";
|
|
3
8
|
import { dateToAgeSeconds } from "../utils";
|
|
4
9
|
|
|
5
10
|
/**
|
|
@@ -44,16 +49,13 @@ export function findCredential(
|
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* Pure search APIs (brave, exa, jina, tavily, searxng, synthetic, zai)
|
|
53
|
-
* settle far faster in practice; reusing the same ceiling keeps the wiring
|
|
54
|
-
* uniform without compromising correctness.
|
|
52
|
+
* The 60-second default tolerates legitimate slow LLM-mediated responses
|
|
53
|
+
* (Anthropic web_search_20250305, Perplexity, Gemini, Codex) while bounding
|
|
54
|
+
* Windows stalls when Bun's `AbortSignal` fails to propagate. Callers may
|
|
55
|
+
* configure a longer provider deadline, capped at five minutes by the
|
|
56
|
+
* dispatcher; pure search APIs typically settle far faster.
|
|
55
57
|
*/
|
|
56
|
-
export const SEARCH_HARD_TIMEOUT_MS =
|
|
58
|
+
export const SEARCH_HARD_TIMEOUT_MS = DEFAULT_WEB_SEARCH_TIMEOUT_SECONDS * 1_000;
|
|
57
59
|
|
|
58
60
|
/**
|
|
59
61
|
* Compose a caller-supplied {@link AbortSignal} with a hard timeout so an
|
|
@@ -14,7 +14,7 @@ const XAI_WEB_SEARCH_MODEL = "grok-4.5";
|
|
|
14
14
|
// grok-4.5 defaults reasoning.effort to "high"; xAI documents "low" for
|
|
15
15
|
// latency-sensitive agentic use and simple tool calling
|
|
16
16
|
// (docs.x.ai/developers/model-capabilities/text/reasoning). Web search is
|
|
17
|
-
//
|
|
17
|
+
// latency-sensitive, so pin these calls low regardless of their configured timeout.
|
|
18
18
|
const XAI_WEB_SEARCH_REASONING_EFFORT = "low";
|
|
19
19
|
const DEFAULT_NUM_RESULTS = 10;
|
|
20
20
|
const MAX_NUM_RESULTS = 30;
|
|
@@ -139,7 +139,7 @@ async function postXAIResponses(
|
|
|
139
139
|
Authorization: `Bearer ${apiKey}`,
|
|
140
140
|
},
|
|
141
141
|
body: JSON.stringify(body),
|
|
142
|
-
signal: withHardTimeout(params.signal),
|
|
142
|
+
signal: withHardTimeout(params.signal, params.timeoutMs),
|
|
143
143
|
});
|
|
144
144
|
}
|
|
145
145
|
|
|
@@ -38,6 +38,7 @@ export interface ZaiSearchParams {
|
|
|
38
38
|
query: string;
|
|
39
39
|
num_results?: number;
|
|
40
40
|
signal?: AbortSignal;
|
|
41
|
+
timeoutMs?: number;
|
|
41
42
|
fetch?: FetchImpl;
|
|
42
43
|
authStorage: AuthStorage;
|
|
43
44
|
sessionId?: string;
|
|
@@ -121,6 +122,7 @@ async function postZaiMcp(
|
|
|
121
122
|
signal: AbortSignal | undefined,
|
|
122
123
|
fetchImpl: FetchImpl,
|
|
123
124
|
expectResponse: boolean,
|
|
125
|
+
timeoutMs?: number,
|
|
124
126
|
): Promise<ZaiMcpPostResult> {
|
|
125
127
|
const headers: Record<string, string> = {
|
|
126
128
|
Authorization: `Bearer ${apiKey}`,
|
|
@@ -144,7 +146,7 @@ async function postZaiMcp(
|
|
|
144
146
|
method: "POST",
|
|
145
147
|
headers,
|
|
146
148
|
body: JSON.stringify(body),
|
|
147
|
-
signal: withHardTimeout(signal),
|
|
149
|
+
signal: withHardTimeout(signal, timeoutMs),
|
|
148
150
|
});
|
|
149
151
|
|
|
150
152
|
if (!response.ok) {
|
|
@@ -211,6 +213,7 @@ async function callZaiTool(
|
|
|
211
213
|
args: Record<string, unknown>,
|
|
212
214
|
signal: AbortSignal | undefined,
|
|
213
215
|
fetchImpl: FetchImpl,
|
|
216
|
+
timeoutMs?: number,
|
|
214
217
|
): Promise<unknown> {
|
|
215
218
|
const initialized = await postZaiMcp(
|
|
216
219
|
apiKey,
|
|
@@ -224,12 +227,22 @@ async function callZaiTool(
|
|
|
224
227
|
signal,
|
|
225
228
|
fetchImpl,
|
|
226
229
|
true,
|
|
230
|
+
timeoutMs,
|
|
227
231
|
);
|
|
228
232
|
if (initialized.parsed !== undefined) {
|
|
229
233
|
readJsonRpcPayload(initialized.parsed);
|
|
230
234
|
}
|
|
231
235
|
|
|
232
|
-
await postZaiMcp(
|
|
236
|
+
await postZaiMcp(
|
|
237
|
+
apiKey,
|
|
238
|
+
"notifications/initialized",
|
|
239
|
+
{},
|
|
240
|
+
initialized.sessionId,
|
|
241
|
+
signal,
|
|
242
|
+
fetchImpl,
|
|
243
|
+
false,
|
|
244
|
+
timeoutMs,
|
|
245
|
+
);
|
|
233
246
|
|
|
234
247
|
const toolCall = await postZaiMcp(
|
|
235
248
|
apiKey,
|
|
@@ -242,6 +255,7 @@ async function callZaiTool(
|
|
|
242
255
|
signal,
|
|
243
256
|
fetchImpl,
|
|
244
257
|
true,
|
|
258
|
+
timeoutMs,
|
|
245
259
|
);
|
|
246
260
|
const payload = readJsonRpcPayload(toolCall.parsed);
|
|
247
261
|
const resultRecord = isRecord(payload.result) ? payload.result : null;
|
|
@@ -279,7 +293,7 @@ async function callZaiSearch(apiKey: string, params: ZaiSearchParams): Promise<u
|
|
|
279
293
|
let lastError: unknown;
|
|
280
294
|
for (let i = 0; i < attempts.length; i++) {
|
|
281
295
|
try {
|
|
282
|
-
return await callZaiTool(apiKey, attempts[i], params.signal, fetchImpl);
|
|
296
|
+
return await callZaiTool(apiKey, attempts[i], params.signal, fetchImpl, params.timeoutMs);
|
|
283
297
|
} catch (error) {
|
|
284
298
|
lastError = error;
|
|
285
299
|
const isLastAttempt = i === attempts.length - 1;
|
|
@@ -429,6 +443,7 @@ export class ZaiProvider extends SearchProvider {
|
|
|
429
443
|
query: parsed.hasDirectives ? formatQuery(parsed, ZAI_QUERY_SYNTAX) : params.query,
|
|
430
444
|
num_results: params.numSearchResults ?? params.limit,
|
|
431
445
|
signal: params.signal,
|
|
446
|
+
timeoutMs: params.timeoutMs,
|
|
432
447
|
authStorage: params.authStorage,
|
|
433
448
|
sessionId: params.sessionId,
|
|
434
449
|
fetch: fetchOverride,
|
package/src/web/search/types.ts
CHANGED
|
@@ -89,6 +89,12 @@ export const SEARCH_PROVIDER_OPTIONS = [
|
|
|
89
89
|
},
|
|
90
90
|
] as const;
|
|
91
91
|
|
|
92
|
+
/** Default hard timeout for each web-search provider transport. */
|
|
93
|
+
export const DEFAULT_WEB_SEARCH_TIMEOUT_SECONDS = 60;
|
|
94
|
+
|
|
95
|
+
/** Maximum configurable hard timeout for each web-search provider transport. */
|
|
96
|
+
export const MAX_WEB_SEARCH_TIMEOUT_SECONDS = 300;
|
|
97
|
+
|
|
92
98
|
/** Supported web search providers (every option except `auto`). */
|
|
93
99
|
export type SearchProviderId = Exclude<(typeof SEARCH_PROVIDER_OPTIONS)[number]["value"], "auto">;
|
|
94
100
|
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
export interface FileLockOptions {
|
|
2
|
-
staleMs?: number;
|
|
3
|
-
retries?: number;
|
|
4
|
-
retryDelayMs?: number;
|
|
5
|
-
}
|
|
6
|
-
interface LockInfo {
|
|
7
|
-
pid: number;
|
|
8
|
-
timestamp: number;
|
|
9
|
-
token: string;
|
|
10
|
-
}
|
|
11
|
-
declare function getLockPath(filePath: string): string;
|
|
12
|
-
declare function readLockInfo(lockPath: string): Promise<LockInfo | null>;
|
|
13
|
-
declare function isLockStale(lockPath: string, staleMs: number): Promise<boolean>;
|
|
14
|
-
declare function tryAcquireLock(lockPath: string): Promise<string | null>;
|
|
15
|
-
declare function releaseLock(lockPath: string, expectedToken?: string): Promise<void>;
|
|
16
|
-
export declare function withFileLock<T>(filePath: string, fn: () => Promise<T>, options?: FileLockOptions): Promise<T>;
|
|
17
|
-
/**
|
|
18
|
-
* Test-only handles for the internal lock primitives. These are NOT part of
|
|
19
|
-
* the public API — they exist so the contract tests can validate token-keyed
|
|
20
|
-
* release semantics and the mkdir-race window without re-implementing them.
|
|
21
|
-
*/
|
|
22
|
-
export declare const __internalsForTesting: {
|
|
23
|
-
tryAcquireLock: typeof tryAcquireLock;
|
|
24
|
-
releaseLock: typeof releaseLock;
|
|
25
|
-
readLockInfo: typeof readLockInfo;
|
|
26
|
-
isLockStale: typeof isLockStale;
|
|
27
|
-
getLockPath: typeof getLockPath;
|
|
28
|
-
};
|
|
29
|
-
export {};
|
package/src/config/file-lock.ts
DELETED
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
|
-
import * as fs from "node:fs/promises";
|
|
3
|
-
import { isEnoent, logger } from "@oh-my-pi/pi-utils";
|
|
4
|
-
|
|
5
|
-
export interface FileLockOptions {
|
|
6
|
-
staleMs?: number;
|
|
7
|
-
retries?: number;
|
|
8
|
-
retryDelayMs?: number;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const DEFAULT_OPTIONS: Required<FileLockOptions> = {
|
|
12
|
-
staleMs: 10_000,
|
|
13
|
-
retries: 50,
|
|
14
|
-
retryDelayMs: 100,
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
interface LockInfo {
|
|
18
|
-
pid: number;
|
|
19
|
-
timestamp: number;
|
|
20
|
-
token: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function getLockPath(filePath: string): string {
|
|
24
|
-
return `${filePath}.lock`;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async function writeLockInfo(lockPath: string, token: string): Promise<void> {
|
|
28
|
-
const info: LockInfo = { pid: process.pid, timestamp: Date.now(), token };
|
|
29
|
-
await Bun.write(`${lockPath}/info`, JSON.stringify(info));
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async function readLockInfo(lockPath: string): Promise<LockInfo | null> {
|
|
33
|
-
try {
|
|
34
|
-
const content = await fs.readFile(`${lockPath}/info`, "utf-8");
|
|
35
|
-
return JSON.parse(content) as LockInfo;
|
|
36
|
-
} catch {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function isProcessAlive(pid: number): boolean {
|
|
42
|
-
try {
|
|
43
|
-
process.kill(pid, 0);
|
|
44
|
-
return true;
|
|
45
|
-
} catch {
|
|
46
|
-
return false;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async function isLockStale(lockPath: string, staleMs: number): Promise<boolean> {
|
|
51
|
-
const info = await readLockInfo(lockPath);
|
|
52
|
-
if (info) {
|
|
53
|
-
if (!isProcessAlive(info.pid)) return true;
|
|
54
|
-
if (Date.now() - info.timestamp > staleMs) return true;
|
|
55
|
-
return false;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// No info file. Either the lock holder is between mkdir and writeLockInfo
|
|
59
|
-
// (fresh dir, do not reap) or the dir was already removed (also do not
|
|
60
|
-
// reap — there is nothing to clean up, and an unguarded fs.rm here would
|
|
61
|
-
// race with another contender's successful mkdir and wipe their dir).
|
|
62
|
-
try {
|
|
63
|
-
const stat = await fs.stat(lockPath);
|
|
64
|
-
return Date.now() - stat.mtimeMs > staleMs;
|
|
65
|
-
} catch (err) {
|
|
66
|
-
if (isEnoent(err)) return false;
|
|
67
|
-
throw err;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
async function tryAcquireLock(lockPath: string): Promise<string | null> {
|
|
72
|
-
try {
|
|
73
|
-
await fs.mkdir(lockPath);
|
|
74
|
-
const token = randomUUID();
|
|
75
|
-
await writeLockInfo(lockPath, token);
|
|
76
|
-
return token;
|
|
77
|
-
} catch (error) {
|
|
78
|
-
if ((error as NodeJS.ErrnoException).code === "EEXIST") {
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
throw error;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
async function releaseLock(lockPath: string, expectedToken?: string): Promise<void> {
|
|
86
|
-
try {
|
|
87
|
-
if (expectedToken !== undefined) {
|
|
88
|
-
const info = await readLockInfo(lockPath);
|
|
89
|
-
if (!info || info.token !== expectedToken) {
|
|
90
|
-
// We are not the owner. The lock either expired and was reaped
|
|
91
|
-
// or another process has reclaimed it. Do nothing — releasing
|
|
92
|
-
// here would wipe the rightful owner's lock.
|
|
93
|
-
logger.debug("file-lock: skipping release for non-owned lock", {
|
|
94
|
-
lockPath,
|
|
95
|
-
expectedToken,
|
|
96
|
-
actualToken: info?.token,
|
|
97
|
-
});
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
await fs.rm(lockPath, { recursive: true });
|
|
102
|
-
} catch {
|
|
103
|
-
// Ignore errors on release.
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
async function lockExists(lockPath: string): Promise<boolean> {
|
|
108
|
-
try {
|
|
109
|
-
await fs.stat(lockPath);
|
|
110
|
-
return true;
|
|
111
|
-
} catch (err) {
|
|
112
|
-
if (isEnoent(err)) return false;
|
|
113
|
-
throw err;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
async function acquireLock(filePath: string, options: FileLockOptions = {}): Promise<() => Promise<void>> {
|
|
118
|
-
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
119
|
-
const lockPath = getLockPath(filePath);
|
|
120
|
-
|
|
121
|
-
for (let attempt = 0; attempt < opts.retries; attempt++) {
|
|
122
|
-
const token = await tryAcquireLock(lockPath);
|
|
123
|
-
if (token !== null) {
|
|
124
|
-
return () => releaseLock(lockPath, token);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
if ((await lockExists(lockPath)) && (await isLockStale(lockPath, opts.staleMs))) {
|
|
128
|
-
// Reaping a stale lock — no token because we didn't acquire it. The
|
|
129
|
-
// rightful owner is presumed dead; rm without ownership check.
|
|
130
|
-
await releaseLock(lockPath);
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
await Bun.sleep(opts.retryDelayMs);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
throw new Error(`Failed to acquire lock for ${filePath} after ${opts.retries} attempts`);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export async function withFileLock<T>(
|
|
141
|
-
filePath: string,
|
|
142
|
-
fn: () => Promise<T>,
|
|
143
|
-
options: FileLockOptions = {},
|
|
144
|
-
): Promise<T> {
|
|
145
|
-
const release = await acquireLock(filePath, options);
|
|
146
|
-
try {
|
|
147
|
-
return await fn();
|
|
148
|
-
} finally {
|
|
149
|
-
await release();
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Test-only handles for the internal lock primitives. These are NOT part of
|
|
155
|
-
* the public API — they exist so the contract tests can validate token-keyed
|
|
156
|
-
* release semantics and the mkdir-race window without re-implementing them.
|
|
157
|
-
*/
|
|
158
|
-
export const __internalsForTesting = {
|
|
159
|
-
tryAcquireLock,
|
|
160
|
-
releaseLock,
|
|
161
|
-
readLockInfo,
|
|
162
|
-
isLockStale,
|
|
163
|
-
getLockPath,
|
|
164
|
-
};
|