@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
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) {
|
|
@@ -1592,6 +1597,18 @@ export async function runRootCommand(
|
|
|
1592
1597
|
stdoutIsTTY: process.stdout.isTTY,
|
|
1593
1598
|
});
|
|
1594
1599
|
|
|
1600
|
+
// Startup changelog is only consumed by interactive mode below; kick the
|
|
1601
|
+
// CHANGELOG.md parse off now so it overlaps session creation instead of
|
|
1602
|
+
// serializing after it.
|
|
1603
|
+
const startupChangelogPromise = isInteractive
|
|
1604
|
+
? logger.time(
|
|
1605
|
+
"main:getChangelogForDisplay",
|
|
1606
|
+
getChangelogForDisplay,
|
|
1607
|
+
parsedArgs,
|
|
1608
|
+
settingsInstance.get("startup.changelogMode"),
|
|
1609
|
+
)
|
|
1610
|
+
: undefined;
|
|
1611
|
+
|
|
1595
1612
|
const { session, setToolUIContext, modelFallbackMessage, lspServers, mcpManager } = await createSession({
|
|
1596
1613
|
...sessionOptions,
|
|
1597
1614
|
eventBus,
|
|
@@ -1651,12 +1668,7 @@ export async function runRootCommand(
|
|
|
1651
1668
|
await runRpcMode(session, mode === "rpc-ui" ? setToolUIContext : undefined, eventBus, rpcInput);
|
|
1652
1669
|
} else if (isInteractive) {
|
|
1653
1670
|
const versionCheckPromise = checkForNewVersion(VERSION).catch(() => undefined);
|
|
1654
|
-
const startupChangelog = await
|
|
1655
|
-
"main:getChangelogForDisplay",
|
|
1656
|
-
getChangelogForDisplay,
|
|
1657
|
-
parsedArgs,
|
|
1658
|
-
settingsInstance.get("startup.changelogMode"),
|
|
1659
|
-
);
|
|
1671
|
+
const startupChangelog = await startupChangelogPromise;
|
|
1660
1672
|
|
|
1661
1673
|
const modelScopeNotification = buildModelScopeNotification(
|
|
1662
1674
|
scopedModels,
|
package/src/mcp/config-writer.ts
CHANGED
|
@@ -7,8 +7,8 @@ import { randomUUID } from "node:crypto";
|
|
|
7
7
|
import * as fs from "node:fs";
|
|
8
8
|
import * as path from "node:path";
|
|
9
9
|
import { isEnoent } from "@oh-my-pi/pi-utils";
|
|
10
|
+
import { withFileLock } from "@oh-my-pi/pi-utils/file-lock";
|
|
10
11
|
import { invalidate as invalidateFsCache } from "../capability/fs";
|
|
11
|
-
import { withFileLock } from "../config/file-lock";
|
|
12
12
|
|
|
13
13
|
import { validateServerConfig } from "./config";
|
|
14
14
|
import { MCP_CONFIG_SCHEMA_URL, type MCPConfigFile, type MCPServerConfig } from "./types";
|
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;
|
|
@@ -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
|
});
|
|
@@ -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
|
);
|
|
@@ -3,16 +3,37 @@ import { replaceTabs } from "../../tools/render-utils";
|
|
|
3
3
|
import { getMarkdownTheme, theme } from "../theme/theme";
|
|
4
4
|
import { DynamicBorder } from "./dynamic-border";
|
|
5
5
|
|
|
6
|
-
type BtwPanelState = "running" | "complete" | "aborted" | "error";
|
|
6
|
+
type BtwPanelState = "running" | "complete" | "branching" | "aborted" | "error";
|
|
7
7
|
|
|
8
8
|
interface BtwPanelComponentOptions {
|
|
9
9
|
question: string;
|
|
10
10
|
tui: TUI;
|
|
11
|
+
canBranch?: () => boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
class BtwFooter implements Component {
|
|
15
|
+
#getLine: () => string;
|
|
16
|
+
#line: string | undefined;
|
|
17
|
+
#text: Text | undefined;
|
|
18
|
+
|
|
19
|
+
constructor(getLine: () => string) {
|
|
20
|
+
this.#getLine = getLine;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
render(width: number): readonly string[] {
|
|
24
|
+
const line = this.#getLine();
|
|
25
|
+
if (line !== this.#line || !this.#text) {
|
|
26
|
+
this.#line = line;
|
|
27
|
+
this.#text = new Text(line, 1, 0);
|
|
28
|
+
}
|
|
29
|
+
return this.#text.render(width);
|
|
30
|
+
}
|
|
11
31
|
}
|
|
12
32
|
|
|
13
33
|
export class BtwPanelComponent extends Container {
|
|
14
34
|
#question: string;
|
|
15
35
|
#tui: TUI;
|
|
36
|
+
#canBranch: (() => boolean) | undefined;
|
|
16
37
|
#state: BtwPanelState = "running";
|
|
17
38
|
#answer = "";
|
|
18
39
|
#errorMessage: string | undefined;
|
|
@@ -23,6 +44,7 @@ export class BtwPanelComponent extends Container {
|
|
|
23
44
|
super();
|
|
24
45
|
this.#question = options.question;
|
|
25
46
|
this.#tui = options.tui;
|
|
47
|
+
this.#canBranch = options.canBranch;
|
|
26
48
|
this.#rebuild();
|
|
27
49
|
}
|
|
28
50
|
|
|
@@ -47,6 +69,14 @@ export class BtwPanelComponent extends Container {
|
|
|
47
69
|
this.#rebuild();
|
|
48
70
|
}
|
|
49
71
|
|
|
72
|
+
/** Shows that the completed answer is being promoted into the chat session. */
|
|
73
|
+
markBranching(): void {
|
|
74
|
+
if (this.#closed) return;
|
|
75
|
+
this.#state = "branching";
|
|
76
|
+
this.#errorMessage = undefined;
|
|
77
|
+
this.#rebuild();
|
|
78
|
+
}
|
|
79
|
+
|
|
50
80
|
markAborted(): void {
|
|
51
81
|
if (this.#closed) return;
|
|
52
82
|
this.#state = "aborted";
|
|
@@ -86,7 +116,7 @@ export class BtwPanelComponent extends Container {
|
|
|
86
116
|
this.addChild(new Spacer(1));
|
|
87
117
|
this.addChild(this.#contentComponent());
|
|
88
118
|
this.addChild(new Spacer(1));
|
|
89
|
-
this.addChild(new
|
|
119
|
+
this.addChild(new BtwFooter(() => this.#footerLine()));
|
|
90
120
|
this.addChild(new Spacer(1));
|
|
91
121
|
this.addChild(new DynamicBorder(str => theme.fg("dim", str)));
|
|
92
122
|
// Component-scoped: a rebuild replaces only this panel's own children
|
|
@@ -100,8 +130,15 @@ export class BtwPanelComponent extends Container {
|
|
|
100
130
|
switch (this.#state) {
|
|
101
131
|
case "running":
|
|
102
132
|
return theme.fg("muted", "Esc cancel /btw");
|
|
103
|
-
case "complete":
|
|
104
|
-
|
|
133
|
+
case "complete": {
|
|
134
|
+
if (!this.isCopyable()) return theme.fg("muted", "Esc dismiss");
|
|
135
|
+
const actions = ["c copy"];
|
|
136
|
+
if (this.#canBranch?.() ?? this.isBranchable()) actions.push("b branch to chat");
|
|
137
|
+
actions.push("Esc dismiss");
|
|
138
|
+
return theme.fg("muted", actions.join(" · "));
|
|
139
|
+
}
|
|
140
|
+
case "branching":
|
|
141
|
+
return theme.fg("muted", `${theme.status.pending} Branching to chat…`);
|
|
105
142
|
case "aborted":
|
|
106
143
|
return theme.fg("warning", `${theme.status.warning} Cancelled · Esc dismiss`);
|
|
107
144
|
case "error":
|
|
@@ -190,6 +190,7 @@ export class ChatTranscriptBuilder {
|
|
|
190
190
|
this.#readGroup = new ReadToolGroupComponent({
|
|
191
191
|
showContentPreview: settings.get("read.toolResultPreview"),
|
|
192
192
|
});
|
|
193
|
+
this.#readGroup.setToolActivityVisible(!settings.get("display.hideToolActivity"));
|
|
193
194
|
this.#trackExpandable(this.#readGroup);
|
|
194
195
|
this.container.addChild(this.#readGroup);
|
|
195
196
|
}
|
|
@@ -321,6 +322,7 @@ export class ChatTranscriptBuilder {
|
|
|
321
322
|
proseOnlyThinking,
|
|
322
323
|
);
|
|
323
324
|
assistantComponent.setImagesVisible(settings.get("terminal.showImages"));
|
|
325
|
+
assistantComponent.setToolResultImagesVisible(!settings.get("display.hideToolActivity"));
|
|
324
326
|
this.#trackExpandable(assistantComponent);
|
|
325
327
|
this.container.addChild(assistantComponent);
|
|
326
328
|
|
|
@@ -353,6 +355,7 @@ export class ChatTranscriptBuilder {
|
|
|
353
355
|
proseOnlyThinking,
|
|
354
356
|
);
|
|
355
357
|
component.setImagesVisible(settings.get("terminal.showImages"));
|
|
358
|
+
component.setToolResultImagesVisible(!settings.get("display.hideToolActivity"));
|
|
356
359
|
this.#trackExpandable(component);
|
|
357
360
|
this.container.addChild(component);
|
|
358
361
|
};
|
|
@@ -401,6 +404,7 @@ export class ChatTranscriptBuilder {
|
|
|
401
404
|
this.deps.cwd,
|
|
402
405
|
content.id,
|
|
403
406
|
);
|
|
407
|
+
component.setToolActivityVisible(!settings.get("display.hideToolActivity"));
|
|
404
408
|
this.#trackExpandable(component);
|
|
405
409
|
this.container.addChild(component);
|
|
406
410
|
|
|
@@ -31,6 +31,7 @@ type ConfigurableEditorAction = Extract<
|
|
|
31
31
|
| "app.model.select"
|
|
32
32
|
| "app.model.selectTemporary"
|
|
33
33
|
| "app.tools.expand"
|
|
34
|
+
| "app.tools.toggleVisibility"
|
|
34
35
|
| "app.thinking.toggle"
|
|
35
36
|
| "app.editor.external"
|
|
36
37
|
| "app.history.search"
|
|
@@ -53,6 +54,7 @@ const DEFAULT_ACTION_KEYS: Record<ConfigurableEditorAction, KeyId[]> = {
|
|
|
53
54
|
"app.model.select": ["alt+m"],
|
|
54
55
|
"app.model.selectTemporary": ["alt+p"],
|
|
55
56
|
"app.tools.expand": ["ctrl+o"],
|
|
57
|
+
"app.tools.toggleVisibility": ["ctrl+shift+o"],
|
|
56
58
|
"app.thinking.toggle": ["ctrl+t"],
|
|
57
59
|
"app.editor.external": ["ctrl+g"],
|
|
58
60
|
"app.history.search": ["ctrl+r"],
|
|
@@ -550,6 +552,7 @@ export class CustomEditor extends Editor {
|
|
|
550
552
|
onCycleModelBackward?: () => void;
|
|
551
553
|
onSelectModel?: () => void;
|
|
552
554
|
onExpandTools?: () => void;
|
|
555
|
+
onToggleToolActivity?: () => void;
|
|
553
556
|
onToggleThinking?: () => void;
|
|
554
557
|
onExternalEditor?: () => void;
|
|
555
558
|
onHistorySearch?: () => void;
|
|
@@ -910,6 +913,12 @@ export class CustomEditor extends Editor {
|
|
|
910
913
|
return;
|
|
911
914
|
}
|
|
912
915
|
|
|
916
|
+
// Intercept configured tool activity visibility toggle
|
|
917
|
+
if (this.#matchesAction(canonical, "app.tools.toggleVisibility") && this.onToggleToolActivity) {
|
|
918
|
+
this.onToggleToolActivity();
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
|
|
913
922
|
// Intercept configured backward model cycling (check before forward cycling)
|
|
914
923
|
if (this.#matchesAction(canonical, "app.model.cycleBackward") && this.onCycleModelBackward) {
|
|
915
924
|
this.onCycleModelBackward();
|
|
@@ -332,6 +332,7 @@ export class ReadToolGroupComponent extends Container implements ToolExecutionHa
|
|
|
332
332
|
#usageBatchByToolCallId = new Map<string, string>();
|
|
333
333
|
#text: Text;
|
|
334
334
|
#expanded = false;
|
|
335
|
+
#toolActivityVisible = true;
|
|
335
336
|
#showContentPreview: boolean;
|
|
336
337
|
// A read group accretes entries across multiple assistant completions for as
|
|
337
338
|
// long as the run of reads is uninterrupted. While it is the active group it
|
|
@@ -353,6 +354,10 @@ export class ReadToolGroupComponent extends Container implements ToolExecutionHa
|
|
|
353
354
|
this.#updateDisplay();
|
|
354
355
|
}
|
|
355
356
|
|
|
357
|
+
override render(width: number): readonly string[] {
|
|
358
|
+
if (!this.#toolActivityVisible) return [];
|
|
359
|
+
return super.render(width);
|
|
360
|
+
}
|
|
356
361
|
isTranscriptBlockFinalized(): boolean {
|
|
357
362
|
if (this.#sealed) return true;
|
|
358
363
|
if (!this.#finalized) return false;
|
|
@@ -496,6 +501,11 @@ export class ReadToolGroupComponent extends Container implements ToolExecutionHa
|
|
|
496
501
|
this.#updateDisplay();
|
|
497
502
|
}
|
|
498
503
|
|
|
504
|
+
setToolActivityVisible(visible: boolean): void {
|
|
505
|
+
this.#toolActivityVisible = visible;
|
|
506
|
+
super.invalidate();
|
|
507
|
+
}
|
|
508
|
+
|
|
499
509
|
getComponent(): Component {
|
|
500
510
|
return this;
|
|
501
511
|
}
|
|
@@ -1785,7 +1785,7 @@ export class StatusLineComponent implements Component {
|
|
|
1785
1785
|
: separatorDef.endCaps.left
|
|
1786
1786
|
: "";
|
|
1787
1787
|
const capPrefix = separatorDef.endCaps?.useBgAsFg ? bgAnsi.replace("\x1b[48;", "\x1b[38;") : bgAnsi + sepAnsi;
|
|
1788
|
-
const capText = cap ? `${capPrefix}${cap}\x1b[0m` : "";
|
|
1788
|
+
const capText = cap ? `${capPrefix}${this.#focusedAgentId ? "\x1b[22m" : ""}${cap}\x1b[0m` : "";
|
|
1789
1789
|
|
|
1790
1790
|
let content = bgAnsi + fgAnsi;
|
|
1791
1791
|
content += ` ${parts.join(` ${sepAnsi}${sep}${fgAnsi} `)} `;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Text } from "@oh-my-pi/pi-tui";
|
|
2
|
+
import { theme } from "../../modes/theme/theme";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Dim transcript marker for tool calls stripped from the resolved branch
|
|
6
|
+
* (failed/retried turns, results on sibling branches). It is tool activity,
|
|
7
|
+
* so it hides and reappears with the `display.hideToolActivity` toggle.
|
|
8
|
+
*/
|
|
9
|
+
export class StrippedToolCallsPlaceholder extends Text {
|
|
10
|
+
#toolActivityVisible: boolean;
|
|
11
|
+
|
|
12
|
+
constructor(strippedToolCalls: number, toolActivityVisible: boolean) {
|
|
13
|
+
super(
|
|
14
|
+
theme.fg(
|
|
15
|
+
"dim",
|
|
16
|
+
theme.italic(
|
|
17
|
+
`${strippedToolCalls} tool call${strippedToolCalls === 1 ? "" : "s"} elided — no result on this branch`,
|
|
18
|
+
),
|
|
19
|
+
),
|
|
20
|
+
1,
|
|
21
|
+
0,
|
|
22
|
+
);
|
|
23
|
+
this.#toolActivityVisible = toolActivityVisible;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
setToolActivityVisible(visible: boolean): void {
|
|
27
|
+
this.#toolActivityVisible = visible;
|
|
28
|
+
this.invalidate();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
override render(width: number): readonly string[] {
|
|
32
|
+
if (!this.#toolActivityVisible) return [];
|
|
33
|
+
return super.render(width);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -246,6 +246,7 @@ export interface ToolExecutionHandle extends Component {
|
|
|
246
246
|
): void;
|
|
247
247
|
setArgsComplete(toolCallId?: string): void;
|
|
248
248
|
setExpanded(expanded: boolean): void;
|
|
249
|
+
setToolActivityVisible(visible: boolean): void;
|
|
249
250
|
/** Freeze the block as final history: stop spinners and let it commit to scrollback. */
|
|
250
251
|
seal(): void;
|
|
251
252
|
}
|
|
@@ -287,6 +288,7 @@ export class ToolExecutionComponent extends Container implements NativeScrollbac
|
|
|
287
288
|
#toolLabel: string;
|
|
288
289
|
#args: any;
|
|
289
290
|
#expanded = false;
|
|
291
|
+
#toolActivityVisible = true;
|
|
290
292
|
#showImages: boolean;
|
|
291
293
|
#editFuzzyThreshold: number | undefined;
|
|
292
294
|
#editAllowFuzzy: boolean | undefined;
|
|
@@ -901,6 +903,11 @@ export class ToolExecutionComponent extends Container implements NativeScrollbac
|
|
|
901
903
|
this.#updateDisplay();
|
|
902
904
|
}
|
|
903
905
|
|
|
906
|
+
setToolActivityVisible(visible: boolean): void {
|
|
907
|
+
this.#toolActivityVisible = visible;
|
|
908
|
+
super.invalidate();
|
|
909
|
+
}
|
|
910
|
+
|
|
904
911
|
setShowImages(show: boolean): void {
|
|
905
912
|
this.#showImages = show;
|
|
906
913
|
this.#updateDisplay();
|
|
@@ -960,6 +967,7 @@ export class ToolExecutionComponent extends Container implements NativeScrollbac
|
|
|
960
967
|
}
|
|
961
968
|
|
|
962
969
|
override render(width: number): readonly string[] {
|
|
970
|
+
if (!this.#toolActivityVisible) return [];
|
|
963
971
|
const lines = super.render(width);
|
|
964
972
|
// Update the paint-tracking flags after `super.render(width)` — the
|
|
965
973
|
// override runs on every compose the parent Container performs, so a
|