@oh-my-pi/pi-coding-agent 17.2.3 → 17.2.5
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 +92 -0
- package/dist/{CHANGELOG-c5hpqt9r.md → CHANGELOG-q2w43aw3.md} +92 -0
- package/dist/cli.js +3979 -3548
- package/dist/{template-8vdv6xb4.js → template-c2hyaytt.js} +8 -26
- package/dist/types/advisor/advise-tool.d.ts +6 -0
- package/dist/types/advisor/runtime.d.ts +4 -4
- package/dist/types/capability/mcp.d.ts +9 -0
- 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/help-extra.d.ts +2 -0
- package/dist/types/cli/models-cli.d.ts +1 -1
- 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 +6 -1
- 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 +70 -22
- package/dist/types/cursor-bridge-tools.d.ts +1 -1
- package/dist/types/cursor.d.ts +7 -4
- package/dist/types/discovery/helpers.d.ts +6 -0
- package/dist/types/discovery/omp-extension-roots.d.ts +21 -2
- 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/extensions/loader.d.ts +6 -2
- 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/extensibility/plugins/marketplace/registry.d.ts +2 -2
- package/dist/types/launch/broker.d.ts +1 -1
- package/dist/types/launch/client.d.ts +6 -3
- package/dist/types/launch/ensure.d.ts +8 -0
- package/dist/types/launch/paths.d.ts +3 -2
- package/dist/types/launch/protocol.d.ts +2 -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/mcp/request-id.d.ts +19 -0
- package/dist/types/mcp/types.d.ts +14 -0
- package/dist/types/memory-backend/index.d.ts +1 -0
- package/dist/types/memory-backend/messages.d.ts +15 -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/custom-editor.d.ts +2 -1
- package/dist/types/modes/components/model-browser.d.ts +7 -5
- package/dist/types/modes/components/model-picker.d.ts +9 -3
- package/dist/types/modes/components/read-tool-group.d.ts +11 -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/input-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/types.d.ts +2 -1
- package/dist/types/modes/workflow.d.ts +2 -1
- package/dist/types/registry/agent-lifecycle.d.ts +14 -5
- package/dist/types/registry/agent-registry.d.ts +4 -3
- package/dist/types/sdk.d.ts +4 -2
- package/dist/types/secrets/index.d.ts +7 -9
- package/dist/types/session/agent-session-types.d.ts +3 -1
- package/dist/types/session/agent-session.d.ts +6 -1
- package/dist/types/session/indexed-session-storage.d.ts +1 -0
- package/dist/types/session/model-controls.d.ts +0 -1
- package/dist/types/session/session-advisors.d.ts +8 -1
- package/dist/types/session/session-context.d.ts +2 -1
- package/dist/types/session/session-maintenance.d.ts +2 -0
- package/dist/types/session/session-manager.d.ts +16 -5
- package/dist/types/session/session-stats.d.ts +10 -0
- package/dist/types/session/session-storage.d.ts +15 -3
- package/dist/types/system-prompt.d.ts +6 -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/default-renderer.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/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/scripts/legacy-pi-virtual-module.ts +12 -2
- package/src/advisor/advise-tool.ts +18 -0
- package/src/advisor/runtime.ts +6 -6
- package/src/capability/mcp.ts +7 -0
- 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/help-extra.ts +89 -0
- package/src/cli/models-cli.ts +13 -16
- 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/collab/host.ts +1 -1
- 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 +19 -9
- package/src/config/mcp-schema.json +5 -0
- package/src/config/model-discovery.ts +30 -13
- package/src/config/model-registry.ts +16 -6
- 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 +73 -21
- package/src/cursor-bridge-tools.ts +1 -1
- package/src/cursor.ts +10 -14
- package/src/discovery/builtin.ts +10 -0
- package/src/discovery/helpers.ts +10 -0
- package/src/discovery/mcp-json.ts +11 -1
- package/src/discovery/omp-extension-roots.ts +79 -20
- package/src/discovery/omp-plugins.ts +10 -1
- 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/template.js +8 -26
- package/src/extensibility/extensions/loader.ts +66 -28
- 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/plugins/marketplace/registry.ts +4 -8
- package/src/extensibility/skills.ts +11 -0
- package/src/extensibility/utils.ts +10 -2
- package/src/launch/broker.ts +1 -1
- package/src/launch/client.ts +32 -13
- package/src/launch/ensure.ts +73 -0
- package/src/launch/paths.ts +3 -6
- package/src/launch/protocol.ts +2 -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 +31 -24
- package/src/mcp/config.ts +1 -0
- package/src/mcp/oauth-flow.ts +11 -1
- package/src/mcp/request-id.ts +24 -0
- package/src/mcp/transports/http.ts +4 -2
- package/src/mcp/transports/sse.ts +4 -2
- package/src/mcp/transports/stdio.ts +4 -2
- package/src/mcp/types.ts +15 -0
- package/src/memory-backend/index.ts +1 -0
- package/src/memory-backend/messages.ts +19 -0
- package/src/mnemopi/embed-client.ts +49 -2
- package/src/mnemopi/state.ts +57 -23
- package/src/modes/components/agent-hub.ts +1 -1
- package/src/modes/components/assistant-message.ts +11 -0
- package/src/modes/components/chat-transcript-builder.ts +4 -0
- package/src/modes/components/custom-editor.ts +10 -1
- package/src/modes/components/model-browser.ts +25 -17
- package/src/modes/components/model-picker.ts +10 -6
- package/src/modes/components/read-tool-group.ts +36 -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 +85 -20
- package/src/modes/components/tree-selector.ts +46 -48
- package/src/modes/controllers/command-controller.ts +2 -2
- package/src/modes/controllers/event-controller.ts +199 -10
- package/src/modes/controllers/input-controller.ts +39 -1
- package/src/modes/controllers/mcp-command-controller.ts +11 -5
- package/src/modes/controllers/selector-controller.ts +47 -6
- package/src/modes/interactive-mode.ts +2 -0
- package/src/modes/types.ts +6 -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/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/registry/agent-lifecycle.ts +36 -11
- package/src/registry/agent-registry.ts +12 -5
- package/src/sdk.ts +102 -35
- package/src/secrets/index.ts +16 -18
- package/src/session/agent-session-types.ts +3 -1
- package/src/session/agent-session.ts +23 -2
- package/src/session/agent-storage.ts +6 -4
- package/src/session/history-storage.ts +4 -2
- package/src/session/indexed-session-storage.ts +9 -0
- package/src/session/messages.ts +31 -7
- package/src/session/model-controls.ts +0 -1
- package/src/session/prewalk.ts +24 -2
- package/src/session/session-advisors.ts +21 -3
- package/src/session/session-context.ts +16 -12
- package/src/session/session-dump-format.ts +1 -1
- package/src/session/session-maintenance.ts +41 -4
- package/src/session/session-manager.ts +181 -94
- 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/session-tools.ts +25 -23
- package/src/session/stream-guards.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +29 -26
- package/src/system-prompt.ts +14 -4
- package/src/task/executor.ts +8 -1
- package/src/task/index.ts +23 -8
- package/src/task/spawn-policy.ts +14 -0
- package/src/thinking.ts +2 -8
- package/src/tiny/text.ts +12 -0
- package/src/tools/ast-grep.ts +10 -4
- package/src/tools/browser/cmux/cmux-tab.ts +5 -10
- 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/default-renderer.ts +19 -4
- package/src/tools/glob.ts +9 -2
- package/src/tools/grep.ts +12 -6
- package/src/tools/index.ts +10 -6
- 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 +7 -2
- 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/src/lsp/types.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { ptree } from "@oh-my-pi/pi-utils";
|
|
2
1
|
import { type } from "arktype";
|
|
3
2
|
import { TOOL_TIMEOUTS } from "../tools/tool-timeouts";
|
|
4
3
|
|
|
@@ -370,6 +369,33 @@ export interface ServerConfig {
|
|
|
370
369
|
createClient?: LinterClientFactory;
|
|
371
370
|
}
|
|
372
371
|
|
|
372
|
+
// =============================================================================
|
|
373
|
+
// Transport
|
|
374
|
+
// =============================================================================
|
|
375
|
+
|
|
376
|
+
/** Minimal write sink for the server-bound byte stream (satisfied by `Bun.FileSink` and the mux socket adapter). */
|
|
377
|
+
export interface LspWriteSink {
|
|
378
|
+
write(data: string | Uint8Array): number | Promise<number>;
|
|
379
|
+
flush(): number | void | Promise<number | void>;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Byte transport carrying one LSP JSON-RPC link. Structurally satisfied by
|
|
384
|
+
* `ptree.ChildProcess<"pipe">` (local server spawn) and by the socket adapter
|
|
385
|
+
* in `mux/daemon.ts` (broker-shared server). `exited` may reject (ptree kill).
|
|
386
|
+
*/
|
|
387
|
+
export interface LspTransport {
|
|
388
|
+
readonly stdin: LspWriteSink;
|
|
389
|
+
readonly stdout: ReadableStream<Uint8Array>;
|
|
390
|
+
readonly exited: Promise<number>;
|
|
391
|
+
readonly exitCode: number | null;
|
|
392
|
+
readonly pid?: number;
|
|
393
|
+
/** Present and true on broker-shared mux links; `lsp reload` uses it to request a shared-server restart. */
|
|
394
|
+
readonly sharedMux?: boolean;
|
|
395
|
+
kill(): void;
|
|
396
|
+
peekStderr(): string;
|
|
397
|
+
}
|
|
398
|
+
|
|
373
399
|
// =============================================================================
|
|
374
400
|
// Client State
|
|
375
401
|
// =============================================================================
|
|
@@ -401,7 +427,7 @@ export interface LspClient {
|
|
|
401
427
|
name: string;
|
|
402
428
|
cwd: string;
|
|
403
429
|
config: ServerConfig;
|
|
404
|
-
proc:
|
|
430
|
+
proc: LspTransport;
|
|
405
431
|
requestId: number;
|
|
406
432
|
diagnostics: Map<string, PublishedDiagnostics>;
|
|
407
433
|
diagnosticsVersion: number;
|
package/src/main.ts
CHANGED
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
type ScopedModel,
|
|
42
42
|
} from "./config/model-resolver";
|
|
43
43
|
import { ModelsConfigFile } from "./config/models-config";
|
|
44
|
+
import { serviceTierSettingToTier } from "./config/service-tier";
|
|
44
45
|
import { getDefault, type SettingPath, Settings, type SettingValue, settings } from "./config/settings";
|
|
45
46
|
import { initializeWithSettings } from "./discovery";
|
|
46
47
|
import {
|
|
@@ -188,8 +189,9 @@ function applyAcpDefaultSettingOverrides(targetSettings: Settings = settings): v
|
|
|
188
189
|
applyDefaultSettingOverrides(HOST_DEFAULTED_SETTING_PATHS, targetSettings);
|
|
189
190
|
}
|
|
190
191
|
|
|
191
|
-
|
|
192
|
-
|
|
192
|
+
/** Reads a non-TTY stdin stream as prompt text. */
|
|
193
|
+
export async function readPipedInput(): Promise<string | undefined> {
|
|
194
|
+
if (process.stdin.isTTY === true) return undefined;
|
|
193
195
|
// stdin is a pipe: a producer that never writes nor closes would block
|
|
194
196
|
// startup forever with zero output. Say what we're blocked on after 1s.
|
|
195
197
|
const notice = setTimeout(() => {
|
|
@@ -862,6 +864,9 @@ export async function buildSessionOptions(
|
|
|
862
864
|
autoApprove: parsed.autoApprove ?? false,
|
|
863
865
|
};
|
|
864
866
|
const restoringSession = Boolean(parsed.continue || parsed.resume || isForeignSessionImport(parsed));
|
|
867
|
+
if (parsed.serviceTier !== undefined) {
|
|
868
|
+
options.openAIServiceTier = serviceTierSettingToTier(parsed.serviceTier) ?? null;
|
|
869
|
+
}
|
|
865
870
|
const cliDirs = parsed.addDir ?? [];
|
|
866
871
|
const settingsDirs = activeSettings.get("workspace.additionalDirectories");
|
|
867
872
|
if (cliDirs.length > 0 || settingsDirs.length > 0) {
|
|
@@ -1110,14 +1115,13 @@ export async function buildSessionOptions(
|
|
|
1110
1115
|
}
|
|
1111
1116
|
|
|
1112
1117
|
// Additional extension paths from CLI
|
|
1113
|
-
const cliExtensionPaths =
|
|
1118
|
+
const cliExtensionPaths = [...(parsed.extensions ?? []), ...(parsed.hooks ?? [])];
|
|
1114
1119
|
if (cliExtensionPaths.length > 0) {
|
|
1115
1120
|
options.additionalExtensionPaths = cliExtensionPaths;
|
|
1116
1121
|
}
|
|
1117
1122
|
|
|
1118
1123
|
if (parsed.noExtensions) {
|
|
1119
1124
|
options.disableExtensionDiscovery = true;
|
|
1120
|
-
options.additionalExtensionPaths = [];
|
|
1121
1125
|
}
|
|
1122
1126
|
|
|
1123
1127
|
return options;
|
|
@@ -1151,10 +1155,6 @@ export async function runRootCommand(
|
|
|
1151
1155
|
|
|
1152
1156
|
const notifs: (InteractiveModeNotify | null)[] = [];
|
|
1153
1157
|
|
|
1154
|
-
// Create AuthStorage and ModelRegistry upfront
|
|
1155
|
-
const authStorage = await logger.time("discoverAuthStorage", deps.discoverAuthStorage ?? discoverAuthStorage);
|
|
1156
|
-
const modelRegistry = logger.time("modelRegistry:init", () => new ModelRegistry(authStorage));
|
|
1157
|
-
|
|
1158
1158
|
if (parsedArgs.version) {
|
|
1159
1159
|
writeStartupNotice(parsedArgs, `${VERSION}\n`);
|
|
1160
1160
|
process.exit(0);
|
|
@@ -1197,15 +1197,31 @@ export async function runRootCommand(
|
|
|
1197
1197
|
// Register CLI-provided extension package paths (`--extension`, `--hook`) so
|
|
1198
1198
|
// the `omp-plugins` discovery provider can surface their `skills/`, `hooks/`,
|
|
1199
1199
|
// `tools/`, `commands/`, `rules/`, `prompts/`, and `.mcp.json` sub-trees.
|
|
1200
|
-
//
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
}
|
|
1200
|
+
// Explicit roots remain authorized under `--no-extensions`; only ambient
|
|
1201
|
+
// extension discovery is disabled.
|
|
1202
|
+
const cliExtensions = [...(parsedArgs.extensions ?? []), ...(parsedArgs.hooks ?? [])];
|
|
1203
|
+
injectOmpExtensionCliRoots(cliExtensions, home, getProjectDir(), {
|
|
1204
|
+
mode: parsedArgs.noExtensions ? "explicit-only" : "merge",
|
|
1205
|
+
replace: true,
|
|
1206
|
+
});
|
|
1207
1207
|
|
|
1208
1208
|
let cwd = getProjectDir();
|
|
1209
|
+
// Classify the host before opening auth or settings storage so every
|
|
1210
|
+
// session-critical database connection picks the right busy timeout.
|
|
1211
|
+
// See getDbBusyTimeoutMs().
|
|
1212
|
+
const isProtocolMode = mode === "rpc" || mode === "rpc-ui" || mode === "acp";
|
|
1213
|
+
// Protocol modes own stdin; treating it as prompt text would consume JSON-RPC frames before their transports start.
|
|
1214
|
+
const pipedInput = isProtocolMode ? undefined : await logger.time("readPipedInput", readPipedInput);
|
|
1215
|
+
const autoPrint = pipedInput !== undefined && !parsedArgs.print && parsedArgs.mode === undefined;
|
|
1216
|
+
const isInteractive = !parsedArgs.print && !autoPrint && parsedArgs.mode === undefined;
|
|
1217
|
+
// Only the interactive host renders a focusable Agent Hub / subagent session
|
|
1218
|
+
// tree; declare it so headless subagent optimizations (e.g. skipping replan
|
|
1219
|
+
// title refresh) can tell a focusable process from a print/RPC/eval one.
|
|
1220
|
+
setInteractiveHost(isInteractive);
|
|
1221
|
+
// Create AuthStorage and ModelRegistry upfront
|
|
1222
|
+
const authStorage = await logger.time("discoverAuthStorage", deps.discoverAuthStorage ?? discoverAuthStorage);
|
|
1223
|
+
const modelRegistry = logger.time("modelRegistry:init", () => new ModelRegistry(authStorage));
|
|
1224
|
+
|
|
1209
1225
|
const settingsInstance =
|
|
1210
1226
|
deps.settings ?? (await logger.time("settings:init", Settings.init, { cwd, configFiles: parsedArgs.config }));
|
|
1211
1227
|
if (parsedArgs.approvalMode) {
|
|
@@ -1228,15 +1244,6 @@ export async function runRootCommand(
|
|
|
1228
1244
|
if (parsedArgs.noTitle || parsedArgs.mode === "rpc" || parsedArgs.mode === "rpc-ui" || parsedArgs.mode === "acp") {
|
|
1229
1245
|
Bun.env.PI_NO_TITLE = "1";
|
|
1230
1246
|
}
|
|
1231
|
-
const isProtocolMode = mode === "rpc" || mode === "rpc-ui" || mode === "acp";
|
|
1232
|
-
// Protocol modes own stdin; treating it as prompt text would consume JSON-RPC frames before their transports start.
|
|
1233
|
-
const pipedInput = isProtocolMode ? undefined : await logger.time("readPipedInput", readPipedInput);
|
|
1234
|
-
const autoPrint = pipedInput !== undefined && !parsedArgs.print && parsedArgs.mode === undefined;
|
|
1235
|
-
const isInteractive = !parsedArgs.print && !autoPrint && parsedArgs.mode === undefined;
|
|
1236
|
-
// Only the interactive host renders a focusable Agent Hub / subagent session
|
|
1237
|
-
// tree; declare it so headless subagent optimizations (e.g. skipping replan
|
|
1238
|
-
// title refresh) can tell a focusable process from a print/RPC/eval one.
|
|
1239
|
-
setInteractiveHost(isInteractive);
|
|
1240
1247
|
|
|
1241
1248
|
// Initialize discovery system with settings for provider persistence
|
|
1242
1249
|
logger.time("initializeWithSettings", initializeWithSettings, settingsInstance);
|
package/src/mcp/config.ts
CHANGED
package/src/mcp/oauth-flow.ts
CHANGED
|
@@ -489,12 +489,22 @@ export class MCPOAuthFlow extends OAuthCallbackFlow {
|
|
|
489
489
|
}
|
|
490
490
|
|
|
491
491
|
const data = (await response.json()) as {
|
|
492
|
-
access_token
|
|
492
|
+
access_token?: string;
|
|
493
493
|
refresh_token?: string;
|
|
494
494
|
expires_in?: number;
|
|
495
495
|
token_type?: string;
|
|
496
|
+
error?: string;
|
|
497
|
+
error_description?: string;
|
|
496
498
|
};
|
|
497
499
|
|
|
500
|
+
// Some providers (e.g. the Slack Web API) signal failure with HTTP 200 and
|
|
501
|
+
// an `{ ok: false, error }` body. Accepting such a response would store an
|
|
502
|
+
// empty access token and only surface `invalid_token` on a later request.
|
|
503
|
+
if (typeof data.access_token !== "string" || data.access_token.length === 0) {
|
|
504
|
+
const providerError = data.error_description ?? data.error;
|
|
505
|
+
throw new Error(`Token exchange returned no access token${providerError ? `: ${providerError}` : ""}`);
|
|
506
|
+
}
|
|
507
|
+
|
|
498
508
|
// Calculate expiry timestamp
|
|
499
509
|
const expiresIn = data.expires_in ?? 3600; // Default to 1 hour
|
|
500
510
|
const expires = Date.now() + expiresIn * 1000;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Snowflake } from "@oh-my-pi/pi-utils";
|
|
2
|
+
import type { MCPRequestIdFormat } from "./types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Per-transport allocator for outgoing JSON-RPC request ids.
|
|
6
|
+
*
|
|
7
|
+
* JSON-RPC 2.0 permits String and Number ids equally, but the wider MCP
|
|
8
|
+
* ecosystem (reference SDKs included) issues incrementing integers, so servers
|
|
9
|
+
* are best tested against numbers — Apple's `xcrun mcpbridge` even logs
|
|
10
|
+
* `mcpbridge.DecodeError Code=1` and never answers a string id. Integers are
|
|
11
|
+
* therefore the default. `"string"` opts a server back into collision-resistant
|
|
12
|
+
* snowflake ids.
|
|
13
|
+
*
|
|
14
|
+
* The counter is per instance and never reset, so ids stay unique for the life
|
|
15
|
+
* of the transport even though a reconnect rejects and clears every pending
|
|
16
|
+
* request.
|
|
17
|
+
*/
|
|
18
|
+
export class RequestIdAllocator {
|
|
19
|
+
#previousNumeric = 0;
|
|
20
|
+
|
|
21
|
+
next(format: MCPRequestIdFormat | undefined): string | number {
|
|
22
|
+
return format === "string" ? Snowflake.next() : ++this.#previousNumeric;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Based on MCP spec 2025-03-26.
|
|
6
6
|
*/
|
|
7
7
|
import * as AIError from "@oh-my-pi/pi-ai/error";
|
|
8
|
-
import { logger, readSseJson
|
|
8
|
+
import { logger, readSseJson } from "@oh-my-pi/pi-utils";
|
|
9
9
|
import type {
|
|
10
10
|
JsonRpcError,
|
|
11
11
|
JsonRpcMessage,
|
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
MCPTransport,
|
|
18
18
|
} from "../../mcp/types";
|
|
19
19
|
import { toJsonRpcError } from "../../mcp/types";
|
|
20
|
+
import { RequestIdAllocator } from "../request-id";
|
|
20
21
|
import { createMCPTimeout, getNeverAbortSignal, isMCPTimeoutEnabled, resolveMCPTimeoutMs } from "../timeout";
|
|
21
22
|
|
|
22
23
|
const HTTP_SSE_CONNECT_TIMEOUT_MS = 1_000;
|
|
@@ -42,6 +43,7 @@ export class HttpTransport implements MCPTransport {
|
|
|
42
43
|
#connected = false;
|
|
43
44
|
#sessionId: string | null = null;
|
|
44
45
|
#sseConnection: AbortController | null = null;
|
|
46
|
+
readonly #requestIds = new RequestIdAllocator();
|
|
45
47
|
|
|
46
48
|
onClose?: () => void;
|
|
47
49
|
onError?: (error: Error) => void;
|
|
@@ -209,7 +211,7 @@ export class HttpTransport implements MCPTransport {
|
|
|
209
211
|
throw new Error("Transport not connected");
|
|
210
212
|
}
|
|
211
213
|
|
|
212
|
-
const id =
|
|
214
|
+
const id = this.#requestIds.next(this.config.requestIdFormat);
|
|
213
215
|
const body = {
|
|
214
216
|
jsonrpc: "2.0" as const,
|
|
215
217
|
id,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as AIError from "@oh-my-pi/pi-ai/error";
|
|
2
|
-
import { logger, readSseEvents
|
|
2
|
+
import { logger, readSseEvents } from "@oh-my-pi/pi-utils";
|
|
3
3
|
import type {
|
|
4
4
|
JsonRpcError,
|
|
5
5
|
JsonRpcMessage,
|
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
MCPTransport,
|
|
11
11
|
} from "../../mcp/types";
|
|
12
12
|
import { toJsonRpcError } from "../../mcp/types";
|
|
13
|
+
import { RequestIdAllocator } from "../request-id";
|
|
13
14
|
import { createMCPTimeout, getNeverAbortSignal, resolveMCPTimeoutMs } from "../timeout";
|
|
14
15
|
|
|
15
16
|
interface MCPTimeoutOperation {
|
|
@@ -32,6 +33,7 @@ export class LegacySseTransport implements MCPTransport {
|
|
|
32
33
|
#sseConnection: AbortController | null = null;
|
|
33
34
|
#pending = new Map<string | number, PendingLegacySseRequest>();
|
|
34
35
|
#config: MCPSseServerConfig;
|
|
36
|
+
readonly #requestIds = new RequestIdAllocator();
|
|
35
37
|
|
|
36
38
|
onClose?: () => void;
|
|
37
39
|
onError?: (error: Error) => void;
|
|
@@ -194,7 +196,7 @@ export class LegacySseTransport implements MCPTransport {
|
|
|
194
196
|
throw new Error("Transport not connected");
|
|
195
197
|
}
|
|
196
198
|
|
|
197
|
-
const id =
|
|
199
|
+
const id = this.#requestIds.next(this.#config.requestIdFormat);
|
|
198
200
|
const body = {
|
|
199
201
|
jsonrpc: "2.0" as const,
|
|
200
202
|
id,
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import * as fs from "node:fs/promises";
|
|
9
9
|
import * as path from "node:path";
|
|
10
|
-
import { getProjectDir, readJsonl
|
|
10
|
+
import { getProjectDir, readJsonl } from "@oh-my-pi/pi-utils";
|
|
11
11
|
import type { Subprocess } from "bun";
|
|
12
12
|
import { hostHasInheritableConsole } from "../../eval/py/spawn-options";
|
|
13
13
|
import type {
|
|
@@ -20,6 +20,7 @@ import type {
|
|
|
20
20
|
MCPTransport,
|
|
21
21
|
} from "../../mcp/types";
|
|
22
22
|
import { toJsonRpcError } from "../../mcp/types";
|
|
23
|
+
import { RequestIdAllocator } from "../request-id";
|
|
23
24
|
import { isMCPTimeoutEnabled, resolveMCPTimeoutMs } from "../timeout";
|
|
24
25
|
|
|
25
26
|
/** Subprocess argv and platform-derived spawn flags for an MCP stdio server. */
|
|
@@ -551,6 +552,7 @@ export class StdioTransport implements MCPTransport {
|
|
|
551
552
|
* actually spawned into its own session may target it.
|
|
552
553
|
*/
|
|
553
554
|
#detached = false;
|
|
555
|
+
readonly #requestIds = new RequestIdAllocator();
|
|
554
556
|
|
|
555
557
|
onClose?: () => void;
|
|
556
558
|
onError?: (error: Error) => void;
|
|
@@ -734,7 +736,7 @@ export class StdioTransport implements MCPTransport {
|
|
|
734
736
|
throw new Error("Transport not connected");
|
|
735
737
|
}
|
|
736
738
|
|
|
737
|
-
const id =
|
|
739
|
+
const id = this.#requestIds.next(this.config.requestIdFormat);
|
|
738
740
|
const request = {
|
|
739
741
|
jsonrpc: "2.0" as const,
|
|
740
742
|
id,
|
package/src/mcp/types.ts
CHANGED
|
@@ -59,12 +59,27 @@ export interface MCPAuthConfig {
|
|
|
59
59
|
resource?: string;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
/** Encoding used for outgoing JSON-RPC request ids. */
|
|
63
|
+
export type MCPRequestIdFormat = "string" | "number";
|
|
64
|
+
|
|
62
65
|
/** Base server config with shared options */
|
|
63
66
|
interface MCPServerConfigBase {
|
|
64
67
|
/** Whether this server is enabled (default: true) */
|
|
65
68
|
enabled?: boolean;
|
|
66
69
|
/** MCP request timeout in milliseconds (default: 30000, 0 to disable) */
|
|
67
70
|
timeout?: number;
|
|
71
|
+
/**
|
|
72
|
+
* Encoding for outgoing JSON-RPC request ids (default: `"number"`).
|
|
73
|
+
*
|
|
74
|
+
* Set `"string"` for servers that need collision-resistant snowflake string
|
|
75
|
+
* ids instead of per-transport integers. See `RequestIdAllocator` in
|
|
76
|
+
* `./request-id`.
|
|
77
|
+
*
|
|
78
|
+
* OMP-specific, so only the OMP-owned discovery providers parse it (native,
|
|
79
|
+
* standalone `mcp.json`, OMP plugins). Providers that translate another
|
|
80
|
+
* tool's config do not, since the key is not part of those formats.
|
|
81
|
+
*/
|
|
82
|
+
requestIdFormat?: MCPRequestIdFormat;
|
|
68
83
|
/** Authentication configuration (optional) */
|
|
69
84
|
auth?: MCPAuthConfig;
|
|
70
85
|
/** OAuth configuration for servers requiring explicit client credentials */
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { MemoryBackendId } from "./types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Fallback text for `/memory stats` and `/memory diagnose` when the active
|
|
5
|
+
* backend's hook is missing or returns nothing.
|
|
6
|
+
*
|
|
7
|
+
* The `off` backend isn't a real backend a user picked among several
|
|
8
|
+
* stats-capable options — it's the no-op state memory falls back to by
|
|
9
|
+
* default — so it gets its own message instead of the generic
|
|
10
|
+
* "not available for the X backend" template, which would otherwise read as
|
|
11
|
+
* the self-contradictory "not available for the off backend".
|
|
12
|
+
*
|
|
13
|
+
* Shared by both the TUI (`CommandController.handleMemoryCommand`) and the
|
|
14
|
+
* ACP/RPC slash-command handler so the two surfaces stay consistent.
|
|
15
|
+
*/
|
|
16
|
+
export function memoryStatsUnavailableMessage(backendId: MemoryBackendId, action: "stats" | "diagnose"): string {
|
|
17
|
+
if (backendId === "off") return "Memory backend is off — there is nothing to show.";
|
|
18
|
+
return `Memory ${action} is not available for the ${backendId} backend.`;
|
|
19
|
+
}
|
|
@@ -84,6 +84,22 @@ export interface MnemopiSubprocessEmbeddingModel {
|
|
|
84
84
|
embed(texts: string[], batchSize?: number): AsyncIterable<number[][]>;
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Upper bound on a steady-state embed IPC round-trip. Initialization is
|
|
89
|
+
* intentionally exempt: bundled installs may spend several minutes installing
|
|
90
|
+
* fastembed and bootstrapping the model, and killing that worker can strand the
|
|
91
|
+
* runtime install lock. Once initialization succeeds, a longer embed stall
|
|
92
|
+
* means a hung native runtime (issue #4792) that would otherwise pin whatever
|
|
93
|
+
* awaits the embed — a turn's memory recall or the headless shutdown
|
|
94
|
+
* consolidation — indefinitely, leaving the process alive with an unreaped
|
|
95
|
+
* `__omp_worker_mnemopi_embed` child (issue #7352). On expiry the embed fails
|
|
96
|
+
* and the worker is SIGKILL-reaped so the next request respawns a fresh one.
|
|
97
|
+
*/
|
|
98
|
+
const EMBED_REQUEST_TIMEOUT_MS = 120_000;
|
|
99
|
+
|
|
100
|
+
/** Race marker for {@link MnemopiEmbedClient.#awaitRequest}. */
|
|
101
|
+
const REQUEST_TIMED_OUT = Symbol("mnemopi.embed.timedOut");
|
|
102
|
+
|
|
87
103
|
export class MnemopiEmbedClient {
|
|
88
104
|
#worker: MnemopiEmbedWorkerHandle | null = null;
|
|
89
105
|
#unsubscribeMessage: (() => void) | null = null;
|
|
@@ -91,9 +107,14 @@ export class MnemopiEmbedClient {
|
|
|
91
107
|
#pending = new Map<string, PendingRequest>();
|
|
92
108
|
#nextRequestId = 0;
|
|
93
109
|
#spawnWorker: () => MnemopiEmbedWorkerHandle;
|
|
110
|
+
#requestTimeoutMs: number;
|
|
94
111
|
|
|
95
|
-
constructor(
|
|
112
|
+
constructor(
|
|
113
|
+
spawnWorker: () => MnemopiEmbedWorkerHandle = spawnMnemopiEmbedWorker,
|
|
114
|
+
requestTimeoutMs: number = EMBED_REQUEST_TIMEOUT_MS,
|
|
115
|
+
) {
|
|
96
116
|
this.#spawnWorker = spawnWorker;
|
|
117
|
+
this.#requestTimeoutMs = requestTimeoutMs;
|
|
97
118
|
}
|
|
98
119
|
|
|
99
120
|
/**
|
|
@@ -166,7 +187,7 @@ export class MnemopiEmbedClient {
|
|
|
166
187
|
// worker's "embed before init" guard. Worker `ensureLoaded` is
|
|
167
188
|
// idempotent so steady-state embeds pay no extra cost.
|
|
168
189
|
worker.send({ type: "embed", id, model, cacheDir, texts, batchSize });
|
|
169
|
-
const result = await promise;
|
|
190
|
+
const result = await this.#awaitRequest(promise);
|
|
170
191
|
if (result instanceof Error) throw result;
|
|
171
192
|
return result;
|
|
172
193
|
} finally {
|
|
@@ -174,6 +195,32 @@ export class MnemopiEmbedClient {
|
|
|
174
195
|
}
|
|
175
196
|
}
|
|
176
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Await one steady-state embed reply, bounded by
|
|
200
|
+
* {@link EMBED_REQUEST_TIMEOUT_MS}. The timeout timer is `unref`'d so a
|
|
201
|
+
* pending request never keeps the parent event loop alive on its own (the
|
|
202
|
+
* awaiting caller does). On expiry the wedged worker is SIGKILL-reaped via
|
|
203
|
+
* {@link terminate} — faulting any other in-flight request and letting the
|
|
204
|
+
* next call respawn a fresh child — before the request rejects, so a hung
|
|
205
|
+
* native runtime cannot pin a turn's recall or shutdown consolidation
|
|
206
|
+
* forever (issue #7352).
|
|
207
|
+
*/
|
|
208
|
+
async #awaitRequest<T>(promise: Promise<T>): Promise<T> {
|
|
209
|
+
const { promise: timedOut, resolve: fire } = Promise.withResolvers<typeof REQUEST_TIMED_OUT>();
|
|
210
|
+
const timer = setTimeout(() => fire(REQUEST_TIMED_OUT), this.#requestTimeoutMs);
|
|
211
|
+
timer.unref();
|
|
212
|
+
try {
|
|
213
|
+
const winner = await Promise.race([promise, timedOut]);
|
|
214
|
+
if (winner === REQUEST_TIMED_OUT) {
|
|
215
|
+
void this.terminate();
|
|
216
|
+
throw new Error("mnemopi embed worker request timed out");
|
|
217
|
+
}
|
|
218
|
+
return winner;
|
|
219
|
+
} finally {
|
|
220
|
+
clearTimeout(timer);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
177
224
|
async *#streamEmbed(
|
|
178
225
|
model: MnemopiEmbedModelId,
|
|
179
226
|
cacheDir: string | undefined,
|
package/src/mnemopi/state.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type * as MnemopiNs from "@oh-my-pi/pi-mnemopi";
|
|
|
4
4
|
import type { Mnemopi, RecallResult } from "@oh-my-pi/pi-mnemopi";
|
|
5
5
|
import type * as MnemopiCoreNs from "@oh-my-pi/pi-mnemopi/core";
|
|
6
6
|
import type { LocalModelInitializer } from "@oh-my-pi/pi-mnemopi/core";
|
|
7
|
-
import { logger } from "@oh-my-pi/pi-utils";
|
|
7
|
+
import { logger, toError } from "@oh-my-pi/pi-utils";
|
|
8
8
|
import {
|
|
9
9
|
composeRecallQuery,
|
|
10
10
|
formatCurrentTime,
|
|
@@ -386,6 +386,8 @@ export class MnemopiSessionState {
|
|
|
386
386
|
const merged: RecallResult[] = [];
|
|
387
387
|
const byId = new Map<string, number>();
|
|
388
388
|
const byContent = new Map<string, number>();
|
|
389
|
+
const failures: Array<{ bank: string; error: Error }> = [];
|
|
390
|
+
let successfulTargets = 0;
|
|
389
391
|
const sharedFallbackQuery = deriveSharedRecallFallbackQuery(
|
|
390
392
|
query,
|
|
391
393
|
this.scoped.retain.bank,
|
|
@@ -394,24 +396,35 @@ export class MnemopiSessionState {
|
|
|
394
396
|
for (const target of this.scoped.recall) {
|
|
395
397
|
const queries =
|
|
396
398
|
target.bank === this.scoped.global?.bank && sharedFallbackQuery ? [query, sharedFallbackQuery] : [query];
|
|
399
|
+
let targetSucceeded = false;
|
|
397
400
|
try {
|
|
398
401
|
for (const recallQuery of queries) {
|
|
399
402
|
const results = await target.memory.recallEnhanced(recallQuery, this.config.recallLimit, {
|
|
400
403
|
includeFacts: true,
|
|
401
404
|
channelId: target.bank,
|
|
402
405
|
});
|
|
406
|
+
targetSucceeded = true;
|
|
403
407
|
for (const result of results) {
|
|
404
408
|
mergeRecallResult(merged, byId, byContent, result);
|
|
405
409
|
}
|
|
406
410
|
}
|
|
407
411
|
} catch (error) {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
}
|
|
412
|
+
const failure = toError(error);
|
|
413
|
+
failures.push({ bank: target.bank, error: failure });
|
|
414
|
+
logger.warn("Mnemopi: scoped recall target failed", {
|
|
415
|
+
bank: target.bank,
|
|
416
|
+
error: failure.message,
|
|
417
|
+
});
|
|
414
418
|
}
|
|
419
|
+
if (targetSucceeded) successfulTargets++;
|
|
420
|
+
}
|
|
421
|
+
if (successfulTargets === 0 && failures.length > 0) {
|
|
422
|
+
if (failures.length === 1) throw failures[0].error;
|
|
423
|
+
const details = failures.map(({ bank, error }) => `${bank}: ${error.message}`).join("; ");
|
|
424
|
+
throw new AggregateError(
|
|
425
|
+
failures.map(({ error }) => error),
|
|
426
|
+
`Mnemopi recall failed for all scoped targets (${details})`,
|
|
427
|
+
);
|
|
415
428
|
}
|
|
416
429
|
merged.sort(compareRecallResults);
|
|
417
430
|
if (merged.length > this.config.recallLimit) merged.length = this.config.recallLimit;
|
|
@@ -575,7 +588,16 @@ export class MnemopiSessionState {
|
|
|
575
588
|
if (!lastUser) return;
|
|
576
589
|
const query = composeRecallQuery(lastUser.content, messages, this.config.recallContextTurns);
|
|
577
590
|
const truncated = truncateRecallQuery(query, lastUser.content, this.config.recallMaxQueryChars);
|
|
578
|
-
|
|
591
|
+
let context: string | undefined;
|
|
592
|
+
try {
|
|
593
|
+
context = await this.recallForContext(truncated);
|
|
594
|
+
} catch (error) {
|
|
595
|
+
logger.warn("Mnemopi: auto-recall failed", {
|
|
596
|
+
bank: this.config.bank,
|
|
597
|
+
error: toError(error).message,
|
|
598
|
+
});
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
579
601
|
this.hasRecalledForFirstTurn = true;
|
|
580
602
|
if (!context) return;
|
|
581
603
|
this.lastRecallSnippet = context;
|
|
@@ -639,17 +661,26 @@ export class MnemopiSessionState {
|
|
|
639
661
|
* tokens on memories that will be wiped on the next line is wasted work
|
|
640
662
|
* (PR #2327 review).
|
|
641
663
|
*
|
|
642
|
-
* `timeoutMs` caps
|
|
643
|
-
* (the user-visible `/quit
|
|
644
|
-
*
|
|
645
|
-
* hit, dispose returns immediately and detaches the
|
|
646
|
-
* consolidate; the SQLite handles are closed in the
|
|
647
|
-
* the consolidate settles so writes never race a closed handle,
|
|
648
|
-
* any pending embeddings are SIGKILL'd along with the embed worker
|
|
664
|
+
* `timeoutMs` caps both synchronous SQLite lock waits during final retention
|
|
665
|
+
* and the asynchronous consolidation drain (the user-visible `/quit`,
|
|
666
|
+
* `/exit`, and print paths pass this so disposal stays within their shutdown
|
|
667
|
+
* budget). When the cap is hit, dispose returns immediately and detaches the
|
|
668
|
+
* still-in-flight consolidate; the SQLite handles are closed in the
|
|
669
|
+
* background once the consolidate settles so writes never race a closed handle,
|
|
670
|
+
* and any pending embeddings are SIGKILL'd along with the embed worker
|
|
649
671
|
* (a tolerable loss — working memory rows are durable; only the
|
|
650
672
|
* episodic promotion / embedding for the LAST few turns is skipped,
|
|
651
673
|
* and `maybeRetainOnAgentEnd` has already retained earlier turns).
|
|
652
674
|
*/
|
|
675
|
+
#boundOwnedBusyTimeout(timeoutMs: number): void {
|
|
676
|
+
// SQLite lock waits block the JS thread, so a Promise race cannot interrupt
|
|
677
|
+
// them. consolidate() flushes every owned bank, so bound each one — not just
|
|
678
|
+
// the retain bank — or a locked shared bank (per-project-tagged) still stalls
|
|
679
|
+
// teardown for Mnemopi's default 5s busy timeout (#7351 review).
|
|
680
|
+
const busyTimeoutMs = Math.max(1, Math.floor(timeoutMs));
|
|
681
|
+
for (const memory of this.scoped.owned) memory.beam.db.exec(`PRAGMA busy_timeout=${busyTimeoutMs}`);
|
|
682
|
+
}
|
|
683
|
+
|
|
653
684
|
async dispose(options: { consolidate?: boolean; timeoutMs?: number } = {}): Promise<void> {
|
|
654
685
|
this.unsubscribe?.();
|
|
655
686
|
this.unsubscribe = undefined;
|
|
@@ -661,19 +692,22 @@ export class MnemopiSessionState {
|
|
|
661
692
|
closeOwned();
|
|
662
693
|
return;
|
|
663
694
|
}
|
|
695
|
+
const { timeoutMs } = options;
|
|
696
|
+
const boundedTimeoutMs = timeoutMs !== undefined && timeoutMs > 0 ? timeoutMs : undefined;
|
|
697
|
+
const deadline = boundedTimeoutMs !== undefined ? performance.now() + boundedTimeoutMs : undefined;
|
|
698
|
+
if (boundedTimeoutMs !== undefined) this.#boundOwnedBusyTimeout(boundedTimeoutMs);
|
|
664
699
|
const consolidatePromise = this.consolidate({ full: false, extract: false, sleep: false }).catch(
|
|
665
700
|
(error: unknown) => {
|
|
666
701
|
logger.warn("Mnemopi: consolidation on dispose failed.", { error: String(error) });
|
|
667
702
|
},
|
|
668
703
|
);
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
const
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
if (winner === TIMED_OUT) {
|
|
704
|
+
if (deadline !== undefined) {
|
|
705
|
+
const remainingMs = deadline - performance.now();
|
|
706
|
+
const completed =
|
|
707
|
+
remainingMs > 0
|
|
708
|
+
? await Promise.race([consolidatePromise.then(() => true), Bun.sleep(remainingMs).then(() => false)])
|
|
709
|
+
: false;
|
|
710
|
+
if (!completed) {
|
|
677
711
|
logger.warn("Mnemopi: consolidate-on-dispose exceeded shutdown budget; detaching to background.", {
|
|
678
712
|
timeoutMs,
|
|
679
713
|
});
|
|
@@ -636,7 +636,7 @@ export class AgentHubOverlayComponent extends Container {
|
|
|
636
636
|
if (ref.status === "running" && ref.session) {
|
|
637
637
|
await ref.session.abort({ reason: USER_INTERRUPT_LABEL });
|
|
638
638
|
}
|
|
639
|
-
await this.#lifecycle().release(ref.id, ref);
|
|
639
|
+
await this.#lifecycle().release(ref.id, ref, { tombstone: true });
|
|
640
640
|
} catch (error) {
|
|
641
641
|
logger.warn("Agent hub: kill failed", { id: ref.id, error: String(error) });
|
|
642
642
|
this.#notice = error instanceof Error ? error.message : String(error);
|
|
@@ -185,6 +185,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
185
185
|
#toolImagesByCallId = new Map<string, ImageContent[]>();
|
|
186
186
|
#convertedKittyImages = new Map<string, ImageContent>();
|
|
187
187
|
#showImages = true;
|
|
188
|
+
#showToolResultImages = true;
|
|
188
189
|
#kittyConversionsInFlight = new Set<string>();
|
|
189
190
|
#transcriptBlockFinalized: boolean;
|
|
190
191
|
/**
|
|
@@ -578,6 +579,15 @@ export class AssistantMessageComponent extends Container {
|
|
|
578
579
|
}
|
|
579
580
|
}
|
|
580
581
|
|
|
582
|
+
/** Toggle only images produced by tool results; assistant-native images remain governed by setImagesVisible. */
|
|
583
|
+
setToolResultImagesVisible(visible: boolean): void {
|
|
584
|
+
if (this.#showToolResultImages === visible) return;
|
|
585
|
+
this.#showToolResultImages = visible;
|
|
586
|
+
if (this.#lastMessage) {
|
|
587
|
+
this.updateContent(this.#lastMessage, { transient: this.#lastUpdateTransient });
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
581
591
|
setToolResultImages(toolCallId: string, images: ImageContent[]): void {
|
|
582
592
|
if (!toolCallId) return;
|
|
583
593
|
const validImages = images.filter(img => img.type === "image" && img.data && img.mimeType);
|
|
@@ -649,6 +659,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
649
659
|
}
|
|
650
660
|
|
|
651
661
|
#renderToolImages(): void {
|
|
662
|
+
if (!this.#showToolResultImages) return;
|
|
652
663
|
const entries = Array.from(this.#toolImagesByCallId.entries()).flatMap(([toolCallId, images]) =>
|
|
653
664
|
images.map((image, index) => ({ image, key: `${toolCallId}:${index}` })),
|
|
654
665
|
);
|