@oh-my-pi/pi-coding-agent 15.10.9 → 15.10.11
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 +117 -0
- package/dist/cli.js +23087 -0
- package/dist/tokenizers.linux-x64-gnu-xcjh3jwk.node +0 -0
- package/dist/types/async/job-manager.d.ts +18 -0
- package/dist/types/cli/args.d.ts +1 -1
- package/dist/types/cli/dry-balance-cli.d.ts +1 -1
- package/dist/types/cli/gallery-cli.d.ts +1 -1
- package/dist/types/cli/gallery-fixtures/types.d.ts +1 -1
- package/dist/types/cli/usage-cli.d.ts +72 -0
- package/dist/types/commands/launch.d.ts +1 -1
- package/dist/types/commands/read.d.ts +1 -1
- package/dist/types/commands/usage.d.ts +25 -0
- package/dist/types/config/append-only-context-mode.d.ts +2 -1
- package/dist/types/config/model-discovery.d.ts +55 -0
- package/dist/types/config/model-registry.d.ts +20 -219
- package/dist/types/config/model-resolver.d.ts +16 -10
- package/dist/types/config/model-roles.d.ts +28 -0
- package/dist/types/config/models-config-schema.d.ts +523 -42
- package/dist/types/config/models-config.d.ts +385 -0
- package/dist/types/config/settings-schema.d.ts +12 -16
- package/dist/types/config/settings.d.ts +1 -1
- package/dist/types/debug/log-viewer.d.ts +1 -1
- package/dist/types/debug/raw-sse.d.ts +1 -1
- package/dist/types/debug/terminal-info.d.ts +0 -1
- package/dist/types/eval/backend.d.ts +0 -2
- package/dist/types/eval/idle-timeout.d.ts +0 -4
- package/dist/types/eval/js/shared/rewrite-imports.d.ts +6 -6
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/extensions/types.d.ts +3 -3
- package/dist/types/hindsight/mental-models.d.ts +17 -8
- package/dist/types/internal-urls/artifact-protocol.d.ts +2 -2
- package/dist/types/internal-urls/types.d.ts +1 -1
- package/dist/types/lsp/edits.d.ts +9 -0
- package/dist/types/lsp/index.d.ts +2 -2
- package/dist/types/lsp/types.d.ts +2 -0
- package/dist/types/lsp/utils.d.ts +3 -0
- package/dist/types/mcp/json-rpc.d.ts +5 -0
- package/dist/types/mnemopi/state.d.ts +11 -1
- package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
- package/dist/types/modes/components/assistant-message.d.ts +3 -1
- package/dist/types/modes/components/bash-execution.d.ts +1 -1
- package/dist/types/modes/components/copy-selector.d.ts +1 -1
- package/dist/types/modes/components/dynamic-border.d.ts +1 -1
- package/dist/types/modes/components/extensions/extension-dashboard.d.ts +1 -1
- package/dist/types/modes/components/extensions/extension-list.d.ts +1 -1
- package/dist/types/modes/components/extensions/inspector-panel.d.ts +1 -1
- package/dist/types/modes/components/footer.d.ts +1 -1
- package/dist/types/modes/components/hook-editor.d.ts +5 -0
- package/dist/types/modes/components/hook-input.d.ts +4 -0
- package/dist/types/modes/components/hook-selector.d.ts +1 -1
- package/dist/types/modes/components/model-selector.d.ts +1 -1
- package/dist/types/modes/components/plan-review-overlay.d.ts +1 -1
- package/dist/types/modes/components/session-observer-overlay.d.ts +1 -1
- package/dist/types/modes/components/session-selector.d.ts +1 -1
- package/dist/types/modes/components/status-line/component.d.ts +1 -1
- package/dist/types/modes/components/tiny-title-download-progress.d.ts +1 -1
- package/dist/types/modes/components/transcript-container.d.ts +31 -26
- package/dist/types/modes/components/tree-selector.d.ts +1 -1
- package/dist/types/modes/components/user-message-selector.d.ts +1 -1
- package/dist/types/modes/components/user-message.d.ts +2 -1
- package/dist/types/modes/components/visual-truncate.d.ts +1 -1
- package/dist/types/modes/components/welcome.d.ts +19 -3
- package/dist/types/modes/controllers/mcp-command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/streaming-reveal.d.ts +1 -1
- package/dist/types/modes/interactive-mode.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/types.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +1 -1
- package/dist/types/modes/setup-wizard/wizard-overlay.d.ts +1 -1
- package/dist/types/modes/types.d.ts +2 -1
- package/dist/types/session/agent-session.d.ts +1 -1
- package/dist/types/session/auth-broker-config.d.ts +4 -0
- package/dist/types/session/session-manager.d.ts +1 -1
- package/dist/types/slash-commands/helpers/stats-dashboard.d.ts +13 -0
- package/dist/types/ssh/connection-manager.d.ts +8 -0
- package/dist/types/task/discovery.d.ts +1 -2
- package/dist/types/task/parallel.d.ts +2 -2
- package/dist/types/task/worktree.d.ts +2 -0
- package/dist/types/tiny/title-client.d.ts +1 -1
- package/dist/types/tools/ask.d.ts +4 -0
- package/dist/types/tools/conflict-detect.d.ts +16 -0
- package/dist/types/tools/github-cache.d.ts +7 -0
- package/dist/types/tools/sqlite-reader.d.ts +3 -0
- package/dist/types/tools/todo.d.ts +2 -0
- package/dist/types/tui/output-block.d.ts +3 -3
- package/dist/types/utils/changelog.d.ts +8 -0
- package/dist/types/web/scrapers/readthedocs.d.ts +3 -0
- package/dist/types/web/scrapers/types.d.ts +12 -0
- package/dist/types/web/search/providers/codex.d.ts +1 -1
- package/dist/types/web/search/providers/gemini.d.ts +1 -1
- package/examples/extensions/tools.ts +5 -4
- package/package.json +14 -11
- package/scripts/build-binary.ts +18 -23
- package/scripts/bundle-dist.ts +81 -0
- package/scripts/{dev-launch → omp} +1 -1
- package/scripts/{dev-launch-preload.ts → omp.ts} +1 -1
- package/src/async/job-manager.ts +57 -3
- package/src/autoresearch/dashboard.ts +1 -1
- package/src/autoresearch/prompt-setup.md +6 -6
- package/src/autoresearch/prompt.md +6 -6
- package/src/capability/fs.ts +10 -0
- package/src/cli/args.ts +1 -1
- package/src/cli/auth-gateway-cli.ts +1 -3
- package/src/cli/dry-balance-cli.ts +1 -1
- package/src/cli/gallery-cli.ts +1 -1
- package/src/cli/gallery-fixtures/fs.ts +1 -1
- package/src/cli/gallery-fixtures/types.ts +5 -1
- package/src/cli/list-models.ts +7 -12
- package/src/cli/usage-cli.ts +603 -0
- package/src/cli-commands.ts +1 -0
- package/src/cli.ts +69 -5
- package/src/commands/complete.ts +1 -1
- package/src/commands/launch.ts +1 -1
- package/src/commands/read.ts +6 -3
- package/src/commands/usage.ts +35 -0
- package/src/commit/agentic/agent.ts +1 -1
- package/src/commit/model-selection.ts +1 -1
- package/src/config/append-only-context-mode.ts +6 -12
- package/src/config/model-discovery.ts +554 -0
- package/src/config/model-registry.ts +308 -1025
- package/src/config/model-resolver.ts +113 -156
- package/src/config/model-roles.ts +74 -0
- package/src/config/models-config-schema.ts +57 -8
- package/src/config/models-config.ts +129 -0
- package/src/config/settings-schema.ts +18 -14
- package/src/config/settings.ts +37 -1
- package/src/dap/client.ts +124 -37
- package/src/dap/session.ts +259 -158
- package/src/debug/log-viewer.ts +1 -1
- package/src/debug/raw-sse.ts +1 -1
- package/src/debug/terminal-info.ts +0 -3
- package/src/edit/diff.ts +95 -18
- package/src/edit/hashline/block-resolver.ts +20 -1
- package/src/edit/hashline/diff.ts +36 -1
- package/src/edit/hashline/execute.ts +8 -2
- package/src/edit/index.ts +16 -1
- package/src/edit/modes/patch.ts +52 -0
- package/src/edit/modes/replace.ts +56 -22
- package/src/edit/notebook.ts +22 -2
- package/src/edit/renderer.ts +36 -10
- package/src/eval/__tests__/completion-bridge.test.ts +1 -1
- package/src/eval/backend.ts +0 -2
- package/src/eval/completion-bridge.ts +2 -1
- package/src/eval/idle-timeout.ts +2 -9
- package/src/eval/js/context-manager.ts +6 -8
- package/src/eval/js/executor.ts +6 -2
- package/src/eval/js/index.ts +0 -2
- package/src/eval/js/shared/helpers.ts +5 -6
- package/src/eval/js/shared/local-module-loader.ts +1 -1
- package/src/eval/js/shared/prelude.txt +62 -1
- package/src/eval/js/shared/rewrite-imports.ts +49 -23
- package/src/eval/js/shared/runtime.ts +1 -1
- package/src/eval/py/index.ts +0 -2
- package/src/eval/py/kernel.ts +19 -0
- package/src/eval/py/runner.py +107 -3
- package/src/exec/bash-executor.ts +3 -1
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +3 -1
- package/src/extensibility/extensions/types.ts +3 -2
- package/src/extensibility/plugins/legacy-pi-compat.ts +20 -3
- package/src/hindsight/mental-models.ts +59 -12
- package/src/hindsight/state.ts +6 -1
- package/src/internal-urls/artifact-protocol.ts +11 -2
- package/src/internal-urls/docs-index.generated.ts +10 -10
- package/src/internal-urls/issue-pr-protocol.ts +12 -5
- package/src/internal-urls/router.ts +1 -1
- package/src/internal-urls/types.ts +1 -1
- package/src/lib/xai-http.ts +1 -1
- package/src/lsp/client.ts +118 -38
- package/src/lsp/clients/biome-client.ts +101 -39
- package/src/lsp/edits.ts +143 -95
- package/src/lsp/index.ts +31 -22
- package/src/lsp/render.ts +1 -1
- package/src/lsp/types.ts +2 -0
- package/src/lsp/utils.ts +28 -10
- package/src/main.ts +165 -17
- package/src/mcp/json-rpc.ts +35 -5
- package/src/mcp/transports/stdio.ts +7 -1
- package/src/memories/index.ts +2 -1
- package/src/mnemopi/backend.ts +25 -3
- package/src/mnemopi/state.ts +38 -2
- package/src/modes/components/agent-dashboard.ts +10 -7
- package/src/modes/components/assistant-message.ts +19 -13
- package/src/modes/components/bash-execution.ts +1 -1
- package/src/modes/components/copy-selector.ts +1 -1
- package/src/modes/components/diff.ts +13 -2
- package/src/modes/components/dynamic-border.ts +12 -3
- package/src/modes/components/extensions/extension-dashboard.ts +8 -5
- package/src/modes/components/extensions/extension-list.ts +1 -1
- package/src/modes/components/extensions/inspector-panel.ts +1 -1
- package/src/modes/components/footer.ts +1 -1
- package/src/modes/components/history-search.ts +1 -1
- package/src/modes/components/hook-editor.ts +8 -0
- package/src/modes/components/hook-input.ts +8 -0
- package/src/modes/components/hook-selector.ts +2 -2
- package/src/modes/components/model-selector.ts +66 -54
- package/src/modes/components/plan-review-overlay.ts +1 -1
- package/src/modes/components/session-observer-overlay.ts +2 -2
- package/src/modes/components/session-selector.ts +1 -1
- package/src/modes/components/settings-selector.ts +5 -1
- package/src/modes/components/status-line/component.ts +1 -1
- package/src/modes/components/tiny-title-download-progress.ts +1 -1
- package/src/modes/components/transcript-container.ts +373 -141
- package/src/modes/components/tree-selector.ts +3 -3
- package/src/modes/components/user-message-selector.ts +1 -1
- package/src/modes/components/user-message.ts +17 -5
- package/src/modes/components/visual-truncate.ts +1 -1
- package/src/modes/components/welcome.ts +108 -26
- package/src/modes/controllers/command-controller.ts +10 -3
- package/src/modes/controllers/event-controller.ts +73 -49
- package/src/modes/controllers/input-controller.ts +5 -5
- package/src/modes/controllers/mcp-command-controller.ts +1 -1
- package/src/modes/controllers/selector-controller.ts +1 -5
- package/src/modes/controllers/streaming-reveal.ts +85 -18
- package/src/modes/interactive-mode.ts +5 -19
- package/src/modes/setup-wizard/scenes/glyph.ts +1 -1
- package/src/modes/setup-wizard/scenes/providers.ts +1 -1
- package/src/modes/setup-wizard/scenes/sign-in.ts +1 -1
- package/src/modes/setup-wizard/scenes/theme.ts +1 -1
- package/src/modes/setup-wizard/scenes/types.ts +1 -1
- package/src/modes/setup-wizard/scenes/web-search.ts +1 -1
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/types.ts +2 -1
- package/src/prompts/agents/explore.md +2 -2
- package/src/prompts/agents/librarian.md +1 -2
- package/src/prompts/agents/oracle.md +1 -1
- package/src/prompts/agents/plan.md +5 -5
- package/src/prompts/agents/task.md +5 -5
- package/src/prompts/ci-green-request.md +5 -7
- package/src/prompts/goals/goal-budget-limit.md +2 -2
- package/src/prompts/goals/goal-continuation.md +4 -4
- package/src/prompts/goals/goal-mode-active.md +1 -1
- package/src/prompts/memories/read-path.md +1 -1
- package/src/prompts/memories/stage_one_system.md +2 -2
- package/src/prompts/review-custom-request.md +1 -1
- package/src/prompts/system/agent-creation-architect.md +2 -2
- package/src/prompts/system/auto-continue.md +1 -1
- package/src/prompts/system/background-tan-dispatch.md +1 -1
- package/src/prompts/system/btw-user.md +2 -2
- package/src/prompts/system/commit-message-system.md +13 -1
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/eager-todo.md +2 -2
- package/src/prompts/system/irc-incoming.md +1 -1
- package/src/prompts/system/manual-continue.md +1 -1
- package/src/prompts/system/omfg-user.md +3 -4
- package/src/prompts/system/orchestrate-notice.md +9 -9
- package/src/prompts/system/plan-mode-active.md +4 -4
- package/src/prompts/system/plan-mode-subagent.md +4 -5
- package/src/prompts/system/plan-mode-tool-decision-reminder.md +1 -1
- package/src/prompts/system/project-prompt.md +2 -2
- package/src/prompts/system/subagent-system-prompt.md +4 -4
- package/src/prompts/system/system-prompt.md +15 -26
- package/src/prompts/system/title-system.md +2 -2
- package/src/prompts/system/ttsr-tool-reminder.md +1 -1
- package/src/prompts/system/workflow-notice.md +1 -1
- package/src/prompts/tools/ast-edit.md +1 -1
- package/src/prompts/tools/ast-grep.md +2 -2
- package/src/prompts/tools/bash.md +8 -10
- package/src/prompts/tools/browser.md +7 -7
- package/src/prompts/tools/debug.md +1 -1
- package/src/prompts/tools/eval.md +3 -3
- package/src/prompts/tools/find.md +0 -1
- package/src/prompts/tools/github.md +8 -7
- package/src/prompts/tools/goal.md +1 -1
- package/src/prompts/tools/image-gen.md +1 -1
- package/src/prompts/tools/inspect-image-system.md +1 -1
- package/src/prompts/tools/irc.md +15 -15
- package/src/prompts/tools/lsp.md +2 -2
- package/src/prompts/tools/patch.md +2 -2
- package/src/prompts/tools/read.md +3 -4
- package/src/prompts/tools/recall.md +1 -1
- package/src/prompts/tools/reflect.md +1 -1
- package/src/prompts/tools/render-mermaid.md +2 -2
- package/src/prompts/tools/replace.md +4 -10
- package/src/prompts/tools/rewind.md +2 -2
- package/src/prompts/tools/search-tool-bm25.md +1 -9
- package/src/prompts/tools/search.md +0 -1
- package/src/prompts/tools/ssh.md +0 -4
- package/src/prompts/tools/task.md +2 -3
- package/src/prompts/tools/todo.md +6 -2
- package/src/sdk.ts +23 -10
- package/src/session/agent-session.ts +44 -10
- package/src/session/auth-broker-config.ts +30 -1
- package/src/session/session-manager.ts +2 -2
- package/src/session/streaming-output.ts +23 -2
- package/src/slash-commands/builtin-registry.ts +20 -0
- package/src/slash-commands/helpers/stats-dashboard.ts +85 -0
- package/src/ssh/connection-manager.ts +27 -0
- package/src/task/commands.ts +2 -1
- package/src/task/discovery.ts +17 -24
- package/src/task/executor.ts +61 -53
- package/src/task/index.ts +137 -60
- package/src/task/parallel.ts +3 -3
- package/src/task/render.ts +2 -2
- package/src/task/worktree.ts +64 -56
- package/src/thinking.ts +2 -1
- package/src/tiny/title-client.ts +32 -14
- package/src/tools/archive-reader.ts +30 -2
- package/src/tools/ask.ts +104 -21
- package/src/tools/ast-edit.ts +25 -5
- package/src/tools/auto-generated-guard.ts +20 -3
- package/src/tools/bash-interactive.ts +27 -7
- package/src/tools/bash.ts +54 -13
- package/src/tools/browser/launch.ts +11 -2
- package/src/tools/browser/readable.ts +19 -2
- package/src/tools/browser/registry.ts +4 -1
- package/src/tools/browser/render.ts +2 -2
- package/src/tools/browser/tab-supervisor.ts +55 -16
- package/src/tools/conflict-detect.ts +50 -4
- package/src/tools/debug.ts +1 -1
- package/src/tools/eval-render.ts +5 -5
- package/src/tools/eval.ts +0 -2
- package/src/tools/fetch.ts +33 -10
- package/src/tools/gh-cache-invalidation.ts +63 -8
- package/src/tools/gh-renderer.ts +1 -1
- package/src/tools/gh.ts +172 -29
- package/src/tools/github-cache.ts +70 -6
- package/src/tools/image-gen.ts +3 -9
- package/src/tools/irc.ts +5 -1
- package/src/tools/job.ts +1 -1
- package/src/tools/read.ts +202 -61
- package/src/tools/render-utils.ts +3 -3
- package/src/tools/resolve.ts +1 -1
- package/src/tools/search.ts +92 -29
- package/src/tools/sqlite-reader.ts +17 -5
- package/src/tools/ssh.ts +8 -8
- package/src/tools/todo.ts +51 -12
- package/src/tools/write.ts +118 -18
- package/src/tui/output-block.ts +4 -4
- package/src/utils/changelog.ts +27 -1
- package/src/utils/file-mentions.ts +2 -1
- package/src/web/scrapers/arxiv.ts +1 -1
- package/src/web/scrapers/go-pkg.ts +1 -1
- package/src/web/scrapers/iacr.ts +1 -1
- package/src/web/scrapers/readthedocs.ts +1 -1
- package/src/web/scrapers/twitter.ts +2 -1
- package/src/web/scrapers/types.ts +87 -8
- package/src/web/scrapers/wikipedia.ts +1 -1
- package/src/web/scrapers/youtube.ts +6 -1
- package/src/web/search/index.ts +1 -1
- package/src/web/search/providers/anthropic.ts +8 -2
- package/src/web/search/providers/codex.ts +2 -1
- package/src/web/search/providers/gemini.ts +2 -3
- package/src/web/search/render.ts +8 -6
- package/dist/types/config/model-equivalence.d.ts +0 -24
- package/dist/types/config/model-id-affixes.d.ts +0 -12
- package/dist/types/config/model-provider-priority.d.ts +0 -1
- package/dist/types/exec/idle-timeout-watchdog.d.ts +0 -18
- package/src/config/model-equivalence.ts +0 -875
- package/src/config/model-id-affixes.ts +0 -81
- package/src/config/model-provider-priority.ts +0 -56
- package/src/exec/idle-timeout-watchdog.ts +0 -126
package/src/main.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { EventLoopKeepalive } from "@oh-my-pi/pi-agent-core";
|
|
|
11
11
|
import type { ImageContent } from "@oh-my-pi/pi-ai";
|
|
12
12
|
import {
|
|
13
13
|
$env,
|
|
14
|
+
getLogPath,
|
|
14
15
|
getProjectDir,
|
|
15
16
|
logger,
|
|
16
17
|
normalizePathForComparison,
|
|
@@ -28,7 +29,7 @@ import { runListModelsCommand } from "./cli/list-models";
|
|
|
28
29
|
import { selectSession } from "./cli/session-picker";
|
|
29
30
|
import { applyStartupCwd } from "./cli/startup-cwd";
|
|
30
31
|
import { findConfigFile } from "./config";
|
|
31
|
-
import { ModelRegistry
|
|
32
|
+
import { ModelRegistry } from "./config/model-registry";
|
|
32
33
|
import {
|
|
33
34
|
getModelMatchPreferences,
|
|
34
35
|
resolveCliModel,
|
|
@@ -36,6 +37,7 @@ import {
|
|
|
36
37
|
resolveModelScope,
|
|
37
38
|
type ScopedModel,
|
|
38
39
|
} from "./config/model-resolver";
|
|
40
|
+
import { ModelsConfigFile } from "./config/models-config";
|
|
39
41
|
import { getDefault, type SettingPath, Settings, settings } from "./config/settings";
|
|
40
42
|
import { initializeWithSettings } from "./discovery";
|
|
41
43
|
import {
|
|
@@ -49,6 +51,7 @@ import { ExtensionRunner } from "./extensibility/extensions/runner";
|
|
|
49
51
|
import type { ExtensionUIContext } from "./extensibility/extensions/types";
|
|
50
52
|
import { scheduleMarketplaceAutoUpdate } from "./extensibility/plugins/marketplace-auto-update";
|
|
51
53
|
import type { MCPManager } from "./mcp";
|
|
54
|
+
import { WelcomeComponent } from "./modes/components/welcome";
|
|
52
55
|
import { InteractiveMode } from "./modes/interactive-mode";
|
|
53
56
|
import type { PrintModeOptions } from "./modes/print-mode";
|
|
54
57
|
import { CURRENT_SETUP_VERSION } from "./modes/setup-version";
|
|
@@ -67,8 +70,14 @@ import { resolveResumableSession, type SessionInfo, SessionManager } from "./ses
|
|
|
67
70
|
import { resolvePromptInput } from "./system-prompt";
|
|
68
71
|
import { initTelemetryExport, isTelemetryExportEnabled } from "./telemetry-export";
|
|
69
72
|
import { AUTO_THINKING } from "./thinking";
|
|
70
|
-
import type
|
|
71
|
-
import {
|
|
73
|
+
import { discoverStartupLspServers, type LspStartupServerInfo } from "./tools";
|
|
74
|
+
import {
|
|
75
|
+
getChangelogPath,
|
|
76
|
+
getNewEntries,
|
|
77
|
+
parseChangelog,
|
|
78
|
+
readLastChangelogVersion,
|
|
79
|
+
writeLastChangelogVersion,
|
|
80
|
+
} from "./utils/changelog";
|
|
72
81
|
import { EventBus } from "./utils/event-bus";
|
|
73
82
|
|
|
74
83
|
type RunAcpMode = (createSession: AcpSessionFactory) => Promise<never>;
|
|
@@ -78,6 +87,44 @@ type RunRpcMode = (
|
|
|
78
87
|
setToolUIContext?: (uiContext: ExtensionUIContext, hasUI: boolean) => void,
|
|
79
88
|
) => Promise<never>;
|
|
80
89
|
|
|
90
|
+
function maybeShowStartupSplash(options: {
|
|
91
|
+
isInteractive: boolean;
|
|
92
|
+
resuming: boolean;
|
|
93
|
+
quiet: boolean;
|
|
94
|
+
version: string;
|
|
95
|
+
setupPending: boolean;
|
|
96
|
+
modelName?: string;
|
|
97
|
+
providerName?: string;
|
|
98
|
+
lspServers?: LspStartupServerInfo[];
|
|
99
|
+
}): void {
|
|
100
|
+
if (!options.isInteractive) return;
|
|
101
|
+
if (options.resuming || options.quiet) return;
|
|
102
|
+
if ($env.PI_TIMING) return;
|
|
103
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) return;
|
|
104
|
+
// First-run launches go straight into the setup wizard, which paints its own
|
|
105
|
+
// splash — keep the minimal two-line notice there.
|
|
106
|
+
if (options.setupPending) {
|
|
107
|
+
process.stdout.write(`${chalk.dim(`omp ${options.version}`)}\n${chalk.dim("Initializing session…")}\n`);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
// Render the same welcome box the TUI paints first: recent sessions as a
|
|
111
|
+
// loading placeholder (the fixed slot count keeps the box height stable) and
|
|
112
|
+
// the logo held on the intro animation's first frame so the in-TUI intro
|
|
113
|
+
// continues from the frame shown here. Clearing the screen first puts the
|
|
114
|
+
// box at the same origin the TUI's first full paint (clearScrollback) uses,
|
|
115
|
+
// so the live welcome replaces this frame in place without shifting.
|
|
116
|
+
const welcome = new WelcomeComponent(
|
|
117
|
+
options.version,
|
|
118
|
+
options.modelName ?? "",
|
|
119
|
+
options.providerName ?? "",
|
|
120
|
+
null,
|
|
121
|
+
options.lspServers ?? [],
|
|
122
|
+
);
|
|
123
|
+
welcome.holdIntroFirstFrame();
|
|
124
|
+
const lines = welcome.render(process.stdout.columns || 80);
|
|
125
|
+
process.stdout.write(`\x1b[2J\x1b[H\x1b[3J\n${lines.join("\n")}\n`);
|
|
126
|
+
}
|
|
127
|
+
|
|
81
128
|
async function checkForNewVersion(currentVersion: string): Promise<string | undefined> {
|
|
82
129
|
if (!settings.get("startup.checkUpdate")) {
|
|
83
130
|
return;
|
|
@@ -143,15 +190,79 @@ function applyAcpDefaultSettingOverrides(targetSettings: Settings = settings): v
|
|
|
143
190
|
|
|
144
191
|
async function readPipedInput(): Promise<string | undefined> {
|
|
145
192
|
if (process.stdin.isTTY !== false) return undefined;
|
|
193
|
+
// stdin is a pipe: a producer that never writes nor closes would block
|
|
194
|
+
// startup forever with zero output. Say what we're blocked on after 1s.
|
|
195
|
+
const notice = setTimeout(() => {
|
|
196
|
+
process.stderr.write(`${chalk.dim("Reading prompt from piped stdin (waiting for EOF; ctrl+c to abort)…")}\n`);
|
|
197
|
+
}, 1000);
|
|
198
|
+
notice.unref?.();
|
|
146
199
|
try {
|
|
147
200
|
const text = await Bun.stdin.text();
|
|
148
201
|
if (text.trim().length === 0) return undefined;
|
|
149
202
|
return text;
|
|
150
203
|
} catch {
|
|
151
204
|
return undefined;
|
|
205
|
+
} finally {
|
|
206
|
+
clearTimeout(notice);
|
|
152
207
|
}
|
|
153
208
|
}
|
|
154
209
|
|
|
210
|
+
// ---------------------------------------------------------------------------
|
|
211
|
+
// Startup watchdog
|
|
212
|
+
// ---------------------------------------------------------------------------
|
|
213
|
+
// Speculative-hang reporter: until startup hands off to a mode runner, print a
|
|
214
|
+
// stderr line every 10s naming the deepest in-flight startup phase. Turns
|
|
215
|
+
// zero-output indefinite hangs (stuck discovery read, network wait, stdin
|
|
216
|
+
// pipe) into self-diagnosing reports instead of "it just hangs" (see the
|
|
217
|
+
// PI_DEBUG_STARTUP markers for the synchronous-hang counterpart).
|
|
218
|
+
|
|
219
|
+
const STARTUP_WATCHDOG_INTERVAL_MS = 10_000;
|
|
220
|
+
let startupWatchdogTimer: NodeJS.Timeout | undefined;
|
|
221
|
+
let startupWatchdogActive = false;
|
|
222
|
+
let startupWatchdogStartedAt = 0;
|
|
223
|
+
|
|
224
|
+
function armStartupWatchdog(): void {
|
|
225
|
+
if (startupWatchdogTimer) return;
|
|
226
|
+
startupWatchdogTimer = setInterval(() => {
|
|
227
|
+
const elapsed = Math.round((Date.now() - startupWatchdogStartedAt) / 1000);
|
|
228
|
+
const phase = logger.openSpanPath().join(" > ") || "module load / pre-phase work";
|
|
229
|
+
process.stderr.write(
|
|
230
|
+
`${chalk.yellow(`Still starting after ${elapsed}s`)}${chalk.dim(` — phase: ${phase}`)}\n` +
|
|
231
|
+
`${chalk.dim(` logs: ${getLogPath()} · re-run with PI_DEBUG_STARTUP=1 for streaming phase markers`)}\n`,
|
|
232
|
+
);
|
|
233
|
+
}, STARTUP_WATCHDOG_INTERVAL_MS);
|
|
234
|
+
startupWatchdogTimer.unref?.();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function disarmStartupWatchdog(): void {
|
|
238
|
+
if (!startupWatchdogTimer) return;
|
|
239
|
+
clearInterval(startupWatchdogTimer);
|
|
240
|
+
startupWatchdogTimer = undefined;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** Begin watching startup (idempotent). */
|
|
244
|
+
function startStartupWatchdog(): void {
|
|
245
|
+
startupWatchdogActive = true;
|
|
246
|
+
startupWatchdogStartedAt = Date.now();
|
|
247
|
+
armStartupWatchdog();
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** Permanently stop watching: a mode runner now owns the terminal. */
|
|
251
|
+
function stopStartupWatchdog(): void {
|
|
252
|
+
startupWatchdogActive = false;
|
|
253
|
+
disarmStartupWatchdog();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** Pause while an interactive prompt legitimately waits on the user. */
|
|
257
|
+
function pauseStartupWatchdog(): void {
|
|
258
|
+
disarmStartupWatchdog();
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** Resume after an interactive prompt, if startup is still being watched. */
|
|
262
|
+
function resumeStartupWatchdog(): void {
|
|
263
|
+
if (startupWatchdogActive) armStartupWatchdog();
|
|
264
|
+
}
|
|
265
|
+
|
|
155
266
|
export interface InteractiveModeNotify {
|
|
156
267
|
kind: "warn" | "error" | "info";
|
|
157
268
|
message: string;
|
|
@@ -361,12 +472,14 @@ async function promptForkSession(session: SessionInfo): Promise<SessionPromptRes
|
|
|
361
472
|
return "unavailable";
|
|
362
473
|
}
|
|
363
474
|
const message = `Session found in different project: ${session.cwd}. Fork into current directory? [y/N] `;
|
|
475
|
+
pauseStartupWatchdog();
|
|
364
476
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
365
477
|
try {
|
|
366
478
|
const answer = (await rl.question(message)).trim().toLowerCase();
|
|
367
479
|
return answer === "y" || answer === "yes" ? "accepted" : "declined";
|
|
368
480
|
} finally {
|
|
369
481
|
rl.close();
|
|
482
|
+
resumeStartupWatchdog();
|
|
370
483
|
}
|
|
371
484
|
}
|
|
372
485
|
|
|
@@ -375,12 +488,14 @@ async function promptMoveSession(session: SessionInfo): Promise<SessionPromptRes
|
|
|
375
488
|
return "unavailable";
|
|
376
489
|
}
|
|
377
490
|
const message = `Session's directory no longer exists (${session.cwd}). Move (re-root) it into the current directory? [Y/n] `;
|
|
491
|
+
pauseStartupWatchdog();
|
|
378
492
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
379
493
|
try {
|
|
380
494
|
const answer = (await rl.question(message)).trim().toLowerCase();
|
|
381
495
|
return answer === "" || answer === "y" || answer === "yes" ? "accepted" : "declined";
|
|
382
496
|
} finally {
|
|
383
497
|
rl.close();
|
|
498
|
+
resumeStartupWatchdog();
|
|
384
499
|
}
|
|
385
500
|
}
|
|
386
501
|
|
|
@@ -437,7 +552,7 @@ async function getChangelogForDisplay(parsed: Args): Promise<string | undefined>
|
|
|
437
552
|
return undefined;
|
|
438
553
|
}
|
|
439
554
|
|
|
440
|
-
const lastVersion =
|
|
555
|
+
const lastVersion = await readLastChangelogVersion();
|
|
441
556
|
if (lastVersion === VERSION) {
|
|
442
557
|
// Steady state: user already saw the current version's changelog. Skip the file read + parse.
|
|
443
558
|
return undefined;
|
|
@@ -448,15 +563,13 @@ async function getChangelogForDisplay(parsed: Args): Promise<string | undefined>
|
|
|
448
563
|
|
|
449
564
|
if (!lastVersion) {
|
|
450
565
|
if (entries.length > 0) {
|
|
451
|
-
|
|
452
|
-
await flushChangelogVersion();
|
|
566
|
+
await writeLastChangelogVersion(VERSION);
|
|
453
567
|
return entries.map(e => e.content).join("\n\n");
|
|
454
568
|
}
|
|
455
569
|
} else {
|
|
456
570
|
const newEntries = getNewEntries(entries, lastVersion);
|
|
457
571
|
if (newEntries.length > 0) {
|
|
458
|
-
|
|
459
|
-
await flushChangelogVersion();
|
|
572
|
+
await writeLastChangelogVersion(VERSION);
|
|
460
573
|
return newEntries.map(e => e.content).join("\n\n");
|
|
461
574
|
}
|
|
462
575
|
}
|
|
@@ -464,14 +577,6 @@ async function getChangelogForDisplay(parsed: Args): Promise<string | undefined>
|
|
|
464
577
|
return undefined;
|
|
465
578
|
}
|
|
466
579
|
|
|
467
|
-
async function flushChangelogVersion(): Promise<void> {
|
|
468
|
-
try {
|
|
469
|
-
await settings.flush();
|
|
470
|
-
} catch (error: unknown) {
|
|
471
|
-
logger.warn("Failed to persist lastChangelogVersion", { error });
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
580
|
/** Resolves CLI session flags into an existing, forked, in-memory, or cancelled session manager. */
|
|
476
581
|
export async function createSessionManager(
|
|
477
582
|
parsed: Args,
|
|
@@ -792,6 +897,7 @@ export async function runRootCommand(
|
|
|
792
897
|
deps: RunRootCommandDependencies = {},
|
|
793
898
|
): Promise<void> {
|
|
794
899
|
logger.startTiming();
|
|
900
|
+
startStartupWatchdog();
|
|
795
901
|
|
|
796
902
|
// Initialize theme early with defaults (CLI commands need symbols)
|
|
797
903
|
// Will be re-initialized with user preferences later
|
|
@@ -803,7 +909,7 @@ export async function runRootCommand(
|
|
|
803
909
|
const notifs: (InteractiveModeNotify | null)[] = [];
|
|
804
910
|
|
|
805
911
|
// Create AuthStorage and ModelRegistry upfront
|
|
806
|
-
const authStorage = await logger.time("
|
|
912
|
+
const authStorage = await logger.time("discoverAuthStorage", deps.discoverAuthStorage ?? discoverAuthStorage);
|
|
807
913
|
const modelRegistry = new ModelRegistry(authStorage);
|
|
808
914
|
|
|
809
915
|
if (parsedArgs.version) {
|
|
@@ -991,10 +1097,12 @@ export async function runRootCommand(
|
|
|
991
1097
|
}
|
|
992
1098
|
startInAllScope = true;
|
|
993
1099
|
}
|
|
1100
|
+
pauseStartupWatchdog();
|
|
994
1101
|
const selected = await logger.time("selectSession", selectSession, folderSessions, {
|
|
995
1102
|
allSessions: preloadedAllSessions,
|
|
996
1103
|
startInAllScope,
|
|
997
1104
|
});
|
|
1105
|
+
resumeStartupWatchdog();
|
|
998
1106
|
if (!selected) {
|
|
999
1107
|
process.stdout.write(`${chalk.dim("No session selected")}\n`);
|
|
1000
1108
|
return;
|
|
@@ -1086,6 +1194,7 @@ export async function runRootCommand(
|
|
|
1086
1194
|
});
|
|
1087
1195
|
// Branch-only protocol runner: keep ACP server code out of normal interactive startup.
|
|
1088
1196
|
const runAcpMode = deps.runAcpMode ?? (await import("./modes/acp/acp-mode")).runAcpMode;
|
|
1197
|
+
stopStartupWatchdog();
|
|
1089
1198
|
await runAcpMode(createAcpSession);
|
|
1090
1199
|
} else {
|
|
1091
1200
|
// Resolve extension-registered CLI flags before creating the session so a
|
|
@@ -1119,6 +1228,42 @@ export async function runRootCommand(
|
|
|
1119
1228
|
stdinContent: pipedInput,
|
|
1120
1229
|
});
|
|
1121
1230
|
|
|
1231
|
+
// Resolve the model the session will most likely start with so the splash
|
|
1232
|
+
// box matches the final welcome screen (the raw role selector, e.g.
|
|
1233
|
+
// "anthropic/claude-fable-5:high", is wider than the left column and would
|
|
1234
|
+
// collapse the box into the single-column layout).
|
|
1235
|
+
let splashModel = sessionOptions.model;
|
|
1236
|
+
if (!splashModel) {
|
|
1237
|
+
const remembered = settingsInstance.getModelRole("default");
|
|
1238
|
+
if (remembered) {
|
|
1239
|
+
splashModel = resolveModelRoleValue(remembered, modelRegistry.getAll(), {
|
|
1240
|
+
settings: settingsInstance,
|
|
1241
|
+
matchPreferences: modelMatchPreferences,
|
|
1242
|
+
modelRegistry,
|
|
1243
|
+
}).model;
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
// Mirror createAgentSession's startup LSP discovery (sync and cheap: root
|
|
1247
|
+
// markers + binary lookup) so the splash lists the same servers the live
|
|
1248
|
+
// welcome screen will show.
|
|
1249
|
+
const splashLspServers =
|
|
1250
|
+
(sessionOptions.enableLsp ?? true)
|
|
1251
|
+
? discoverStartupLspServers(
|
|
1252
|
+
sessionOptions.cwd ?? cwd,
|
|
1253
|
+
settingsInstance.get("lsp.lazy") ? "available" : "connecting",
|
|
1254
|
+
)
|
|
1255
|
+
: [];
|
|
1256
|
+
maybeShowStartupSplash({
|
|
1257
|
+
isInteractive,
|
|
1258
|
+
resuming: Boolean(parsedArgs.continue || parsedArgs.resume || parsedArgs.fork),
|
|
1259
|
+
quiet: settingsInstance.get("startup.quiet"),
|
|
1260
|
+
version: VERSION,
|
|
1261
|
+
setupPending: deps.forceSetupWizard === true || settingsInstance.get("setupVersion") < CURRENT_SETUP_VERSION,
|
|
1262
|
+
modelName: splashModel?.name,
|
|
1263
|
+
providerName: splashModel?.provider,
|
|
1264
|
+
lspServers: splashLspServers,
|
|
1265
|
+
});
|
|
1266
|
+
|
|
1122
1267
|
const { session, setToolUIContext, modelFallbackMessage, lspServers, mcpManager } = await createSession({
|
|
1123
1268
|
...sessionOptions,
|
|
1124
1269
|
eventBus,
|
|
@@ -1152,6 +1297,7 @@ export async function runRootCommand(
|
|
|
1152
1297
|
if (mode === "rpc" || mode === "rpc-ui") {
|
|
1153
1298
|
// Branch-only protocol runner: keep RPC host code out of normal interactive startup.
|
|
1154
1299
|
const runRpcMode: RunRpcMode = (await import("./modes/rpc/rpc-mode")).runRpcMode;
|
|
1300
|
+
stopStartupWatchdog();
|
|
1155
1301
|
await runRpcMode(session, mode === "rpc-ui" ? setToolUIContext : undefined);
|
|
1156
1302
|
} else if (isInteractive) {
|
|
1157
1303
|
const versionCheckPromise = checkForNewVersion(VERSION).catch(() => undefined);
|
|
@@ -1175,6 +1321,7 @@ export async function runRootCommand(
|
|
|
1175
1321
|
}
|
|
1176
1322
|
}
|
|
1177
1323
|
|
|
1324
|
+
stopStartupWatchdog();
|
|
1178
1325
|
logger.endTiming();
|
|
1179
1326
|
await runInteractiveMode(
|
|
1180
1327
|
session,
|
|
@@ -1194,6 +1341,7 @@ export async function runRootCommand(
|
|
|
1194
1341
|
);
|
|
1195
1342
|
} else {
|
|
1196
1343
|
// Branch-only single-shot runner: keep print-mode code out of normal interactive startup.
|
|
1344
|
+
stopStartupWatchdog();
|
|
1197
1345
|
const runPrintMode: RunPrintMode = (await import("./modes/print-mode")).runPrintMode;
|
|
1198
1346
|
await runPrintMode(session, {
|
|
1199
1347
|
mode,
|
package/src/mcp/json-rpc.ts
CHANGED
|
@@ -6,6 +6,28 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
8
8
|
|
|
9
|
+
/** Hard ceiling on a single MCP HTTP request when the caller provides no signal. */
|
|
10
|
+
const MCP_DEFAULT_TIMEOUT_MS = 60_000;
|
|
11
|
+
|
|
12
|
+
const SENSITIVE_QUERY_PARAM = /key|token|secret|auth/i;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Redact credential-bearing query params (e.g. `exaApiKey`) so failed
|
|
16
|
+
* requests never write secrets to the persistent log file.
|
|
17
|
+
*/
|
|
18
|
+
export function redactUrlForLog(url: string): string {
|
|
19
|
+
try {
|
|
20
|
+
const parsed = new URL(url);
|
|
21
|
+
for (const name of parsed.searchParams.keys()) {
|
|
22
|
+
if (SENSITIVE_QUERY_PARAM.test(name)) parsed.searchParams.set(name, "[redacted]");
|
|
23
|
+
}
|
|
24
|
+
return parsed.toString();
|
|
25
|
+
} catch {
|
|
26
|
+
// Unparseable URL — drop the query string entirely rather than risk leaking it.
|
|
27
|
+
return url.split("?")[0];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
9
31
|
/** Parse SSE response format (lines starting with "data: ") */
|
|
10
32
|
export function parseSSE(text: string): unknown {
|
|
11
33
|
const lines = text.split("\n");
|
|
@@ -13,8 +35,12 @@ export function parseSSE(text: string): unknown {
|
|
|
13
35
|
if (line.startsWith("data: ")) {
|
|
14
36
|
const data = line.slice(6).trim();
|
|
15
37
|
if (data === "[DONE]") continue;
|
|
16
|
-
|
|
17
|
-
|
|
38
|
+
try {
|
|
39
|
+
const result = JSON.parse(data) as unknown;
|
|
40
|
+
if (result) return result;
|
|
41
|
+
} catch {
|
|
42
|
+
// Non-JSON data line (keep-alive/comment) — skip and keep scanning.
|
|
43
|
+
}
|
|
18
44
|
}
|
|
19
45
|
}
|
|
20
46
|
// Fallback: try parsing entire response as JSON
|
|
@@ -71,12 +97,12 @@ export async function callMCP<T = unknown>(
|
|
|
71
97
|
Accept: "application/json, text/event-stream",
|
|
72
98
|
},
|
|
73
99
|
body: JSON.stringify(body),
|
|
74
|
-
signal: options?.signal,
|
|
100
|
+
signal: options?.signal ?? AbortSignal.timeout(MCP_DEFAULT_TIMEOUT_MS),
|
|
75
101
|
});
|
|
76
102
|
|
|
77
103
|
if (!response.ok) {
|
|
78
104
|
const errorMsg = `MCP request failed: ${response.status} ${response.statusText}`;
|
|
79
|
-
logger.error(errorMsg, { url, method, params });
|
|
105
|
+
logger.error(errorMsg, { url: redactUrlForLog(url), method, params });
|
|
80
106
|
throw new Error(errorMsg);
|
|
81
107
|
}
|
|
82
108
|
|
|
@@ -84,7 +110,11 @@ export async function callMCP<T = unknown>(
|
|
|
84
110
|
const result = parseSSE(text) as JsonRpcResponse<T> | null;
|
|
85
111
|
|
|
86
112
|
if (!result) {
|
|
87
|
-
logger.error("Failed to parse MCP response", {
|
|
113
|
+
logger.error("Failed to parse MCP response", {
|
|
114
|
+
url: redactUrlForLog(url),
|
|
115
|
+
method,
|
|
116
|
+
responseText: text.slice(0, 500),
|
|
117
|
+
});
|
|
88
118
|
throw new Error("Failed to parse MCP response");
|
|
89
119
|
}
|
|
90
120
|
|
|
@@ -133,6 +133,12 @@ function resolveComSpec(env: Record<string, string | undefined>): string {
|
|
|
133
133
|
return comspec && comspec.length > 0 ? comspec : "cmd.exe";
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
/** `cmd /s /c` strips one outer quote pair; keep inner argv quotes intact. */
|
|
137
|
+
function buildCmdExeCommand(command: string, args: readonly string[]): string {
|
|
138
|
+
const quotedCommand = [command, ...args].map(quoteCmdArg).join(" ");
|
|
139
|
+
return `"${quotedCommand}"`;
|
|
140
|
+
}
|
|
141
|
+
|
|
136
142
|
/** Resolve the subprocess argv used to launch an MCP stdio server. */
|
|
137
143
|
export async function resolveStdioSpawnCommand(
|
|
138
144
|
config: MCPStdioServerConfig,
|
|
@@ -146,7 +152,7 @@ export async function resolveStdioSpawnCommand(
|
|
|
146
152
|
if (!isWindowsBatchCommand(resolvedCommand)) return { cmd: [resolvedCommand, ...args] };
|
|
147
153
|
|
|
148
154
|
return {
|
|
149
|
-
cmd: [resolveComSpec(options.env), "/d", "/s", "/c",
|
|
155
|
+
cmd: [resolveComSpec(options.env), "/d", "/s", "/c", buildCmdExeCommand(resolvedCommand, args)],
|
|
150
156
|
};
|
|
151
157
|
}
|
|
152
158
|
|
package/src/memories/index.ts
CHANGED
|
@@ -3,7 +3,8 @@ import type * as fsNode from "node:fs";
|
|
|
3
3
|
import * as fs from "node:fs/promises";
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
6
|
-
import { type ApiKey,
|
|
6
|
+
import { type ApiKey, completeSimple, Effort, type Model } from "@oh-my-pi/pi-ai";
|
|
7
|
+
import { clampThinkingLevelForModel } from "@oh-my-pi/pi-catalog/model-thinking";
|
|
7
8
|
import { getAgentDbPath, getMemoriesDir, logger, parseJsonlLenient, prompt } from "@oh-my-pi/pi-utils";
|
|
8
9
|
|
|
9
10
|
import type { ModelRegistry } from "../config/model-registry";
|
package/src/mnemopi/backend.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { rm } from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { completeSimple } from "@oh-my-pi/pi-ai";
|
|
4
|
-
import { Mnemopi } from "@oh-my-pi/pi-mnemopi";
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
4
|
+
import type { Mnemopi } from "@oh-my-pi/pi-mnemopi";
|
|
5
|
+
import type * as MnemopiDiagnoseNs from "@oh-my-pi/pi-mnemopi/diagnose";
|
|
6
|
+
import type { DiagnosticSummary } from "@oh-my-pi/pi-mnemopi/diagnose";
|
|
7
7
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
8
8
|
|
|
9
9
|
import type { ModelRegistry } from "../config/model-registry";
|
|
@@ -25,10 +25,25 @@ import {
|
|
|
25
25
|
getMnemopiScopedBanks,
|
|
26
26
|
getMnemopiScopedDbPaths,
|
|
27
27
|
getMnemopiSessionState,
|
|
28
|
+
loadMnemopi,
|
|
29
|
+
loadMnemopiCore,
|
|
28
30
|
MnemopiSessionState,
|
|
31
|
+
requireMnemopi,
|
|
32
|
+
requireMnemopiCore,
|
|
29
33
|
setMnemopiSessionState,
|
|
30
34
|
} from "./state";
|
|
31
35
|
|
|
36
|
+
// `/diagnose` is the only user of this subpath; load it lazily alongside the
|
|
37
|
+
// loaders in ./state to keep mnemopi off the CLI startup module graph.
|
|
38
|
+
let mnemopiDiagnoseMod: typeof MnemopiDiagnoseNs | undefined;
|
|
39
|
+
|
|
40
|
+
async function loadMnemopiDiagnose(): Promise<typeof MnemopiDiagnoseNs> {
|
|
41
|
+
if (!mnemopiDiagnoseMod) {
|
|
42
|
+
mnemopiDiagnoseMod = await import("@oh-my-pi/pi-mnemopi/diagnose");
|
|
43
|
+
}
|
|
44
|
+
return mnemopiDiagnoseMod;
|
|
45
|
+
}
|
|
46
|
+
|
|
32
47
|
const STATIC_INSTRUCTIONS = [
|
|
33
48
|
"# Memory",
|
|
34
49
|
"This agent has local Mnemopi long-term memory.",
|
|
@@ -68,6 +83,7 @@ export const mnemopiBackend: MemoryBackend = {
|
|
|
68
83
|
|
|
69
84
|
try {
|
|
70
85
|
const config = await loadMnemopiConfigWithProviders(settings, agentDir, modelRegistry, sessionId);
|
|
86
|
+
await Promise.all([loadMnemopi(), loadMnemopiCore()]);
|
|
71
87
|
const state = new MnemopiSessionState({ sessionId, config, session });
|
|
72
88
|
const previous = setMnemopiSessionState(session, state);
|
|
73
89
|
previous?.dispose();
|
|
@@ -97,6 +113,7 @@ export const mnemopiBackend: MemoryBackend = {
|
|
|
97
113
|
previous?.dispose();
|
|
98
114
|
const config = previous?.config ?? (session ? loadMnemopiConfig(session.settings, agentDir) : undefined);
|
|
99
115
|
if (!config) return;
|
|
116
|
+
await loadMnemopiCore();
|
|
100
117
|
await removeDbFiles(getMnemopiScopedDbPaths(config));
|
|
101
118
|
},
|
|
102
119
|
|
|
@@ -110,6 +127,7 @@ export const mnemopiBackend: MemoryBackend = {
|
|
|
110
127
|
session.modelRegistry,
|
|
111
128
|
session.sessionId,
|
|
112
129
|
);
|
|
130
|
+
await Promise.all([loadMnemopi(), loadMnemopiCore()]);
|
|
113
131
|
state = new MnemopiSessionState({ sessionId: session.sessionId, config, session });
|
|
114
132
|
setMnemopiSessionState(session, state);
|
|
115
133
|
}
|
|
@@ -124,6 +142,7 @@ export const mnemopiBackend: MemoryBackend = {
|
|
|
124
142
|
},
|
|
125
143
|
|
|
126
144
|
async stats(agentDir, _cwd, session): Promise<string | undefined> {
|
|
145
|
+
await Promise.all([loadMnemopi(), loadMnemopiCore()]);
|
|
127
146
|
const { targets, owned } = createStatsTargets(agentDir, session);
|
|
128
147
|
try {
|
|
129
148
|
if (targets.length === 0) return undefined;
|
|
@@ -137,6 +156,7 @@ export const mnemopiBackend: MemoryBackend = {
|
|
|
137
156
|
const state = getMnemopiSessionState(session);
|
|
138
157
|
const config = state?.config ?? (session ? loadMnemopiConfig(session.settings, agentDir) : undefined);
|
|
139
158
|
if (!config) return undefined;
|
|
159
|
+
const [{ inspectDatabase }] = await Promise.all([loadMnemopiDiagnose(), loadMnemopiCore()]);
|
|
140
160
|
const banks = getMnemopiScopedBanks(config);
|
|
141
161
|
const dbPaths = getMnemopiScopedDbPaths(config);
|
|
142
162
|
const summaries = dbPaths.map((dbPath, index) => ({
|
|
@@ -179,6 +199,7 @@ function createStatsTargets(
|
|
|
179
199
|
|
|
180
200
|
function createStatsMemory(config: MnemopiBackendConfig, bank: string): Mnemopi {
|
|
181
201
|
const providerOptions = config.providerOptions as Record<string, unknown>;
|
|
202
|
+
const { Mnemopi } = requireMnemopi();
|
|
182
203
|
return new Mnemopi({
|
|
183
204
|
dbPath: resolveBankDbPath(config, bank),
|
|
184
205
|
bank,
|
|
@@ -193,6 +214,7 @@ function createStatsMemory(config: MnemopiBackendConfig, bank: string): Mnemopi
|
|
|
193
214
|
function resolveBankDbPath(config: MnemopiBackendConfig, bank: string): string {
|
|
194
215
|
const sharedBank = config.globalBank ?? config.baseBank ?? "default";
|
|
195
216
|
if (bank === sharedBank) return config.dbPath;
|
|
217
|
+
const { BankManager } = requireMnemopiCore();
|
|
196
218
|
return new BankManager(path.dirname(config.dbPath)).getBankDbPath(bank);
|
|
197
219
|
}
|
|
198
220
|
|
package/src/mnemopi/state.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { dirname } from "node:path";
|
|
2
2
|
import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
3
|
+
import type * as MnemopiNs from "@oh-my-pi/pi-mnemopi";
|
|
4
|
+
import type { Mnemopi, RecallResult } from "@oh-my-pi/pi-mnemopi";
|
|
5
|
+
import type * as MnemopiCoreNs from "@oh-my-pi/pi-mnemopi/core";
|
|
5
6
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
6
7
|
import {
|
|
7
8
|
composeRecallQuery,
|
|
@@ -13,6 +14,39 @@ import { extractMessages } from "../hindsight/transcript";
|
|
|
13
14
|
import type { AgentSession, AgentSessionEvent } from "../session/agent-session";
|
|
14
15
|
import type { MnemopiBackendConfig, MnemopiScoping } from "./config";
|
|
15
16
|
|
|
17
|
+
// The mnemopi package pulls the embeddings stack; keep it off the CLI startup
|
|
18
|
+
// module graph by loading it lazily at the async boundaries that need it.
|
|
19
|
+
let mnemopiMod: typeof MnemopiNs | undefined;
|
|
20
|
+
let mnemopiCoreMod: typeof MnemopiCoreNs | undefined;
|
|
21
|
+
|
|
22
|
+
/** Lazily load `@oh-my-pi/pi-mnemopi` (memoized). */
|
|
23
|
+
export async function loadMnemopi(): Promise<typeof MnemopiNs> {
|
|
24
|
+
if (!mnemopiMod) {
|
|
25
|
+
mnemopiMod = await import("@oh-my-pi/pi-mnemopi");
|
|
26
|
+
}
|
|
27
|
+
return mnemopiMod;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Lazily load `@oh-my-pi/pi-mnemopi/core` (memoized). */
|
|
31
|
+
export async function loadMnemopiCore(): Promise<typeof MnemopiCoreNs> {
|
|
32
|
+
if (!mnemopiCoreMod) {
|
|
33
|
+
mnemopiCoreMod = await import("@oh-my-pi/pi-mnemopi/core");
|
|
34
|
+
}
|
|
35
|
+
return mnemopiCoreMod;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Sync access for code below an async boundary that already awaited {@link loadMnemopi}. */
|
|
39
|
+
export function requireMnemopi(): typeof MnemopiNs {
|
|
40
|
+
if (!mnemopiMod) throw new Error("Mnemopi module not loaded; await loadMnemopi() first.");
|
|
41
|
+
return mnemopiMod;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Sync access for code below an async boundary that already awaited {@link loadMnemopiCore}. */
|
|
45
|
+
export function requireMnemopiCore(): typeof MnemopiCoreNs {
|
|
46
|
+
if (!mnemopiCoreMod) throw new Error("Mnemopi core module not loaded; await loadMnemopiCore() first.");
|
|
47
|
+
return mnemopiCoreMod;
|
|
48
|
+
}
|
|
49
|
+
|
|
16
50
|
const kMnemopiSessionState = Symbol("mnemopi.sessionState");
|
|
17
51
|
|
|
18
52
|
interface AgentSessionWithMnemopiState extends AgentSession {
|
|
@@ -460,6 +494,7 @@ function escapeRegExp(text: string): string {
|
|
|
460
494
|
}
|
|
461
495
|
function createMemory(config: MnemopiBackendConfig, bank: string): Mnemopi {
|
|
462
496
|
const providerOptions = config.providerOptions as Record<string, unknown>;
|
|
497
|
+
const { Mnemopi } = requireMnemopi();
|
|
463
498
|
return new Mnemopi({
|
|
464
499
|
dbPath: resolveBankDbPath(config, bank),
|
|
465
500
|
bank,
|
|
@@ -474,6 +509,7 @@ function createMemory(config: MnemopiBackendConfig, bank: string): Mnemopi {
|
|
|
474
509
|
function resolveBankDbPath(config: MnemopiBackendConfig, bank: string): string {
|
|
475
510
|
const sharedBank = config.globalBank ?? config.baseBank ?? "default";
|
|
476
511
|
if (bank === sharedBank) return config.dbPath;
|
|
512
|
+
const { BankManager } = requireMnemopiCore();
|
|
477
513
|
return new BankManager(dirname(config.dbPath)).getBankDbPath(bank);
|
|
478
514
|
}
|
|
479
515
|
|
|
@@ -194,7 +194,7 @@ class AgentListPane implements Component {
|
|
|
194
194
|
private readonly maxVisible: number,
|
|
195
195
|
) {}
|
|
196
196
|
|
|
197
|
-
render(width: number): string[] {
|
|
197
|
+
render(width: number): readonly string[] {
|
|
198
198
|
const lines: string[] = [];
|
|
199
199
|
const searchPrefix = theme.fg("muted", "Search: ");
|
|
200
200
|
const searchText = this.searchQuery || theme.fg("dim", "type to filter");
|
|
@@ -255,7 +255,7 @@ class AgentInspectorPane implements Component {
|
|
|
255
255
|
private readonly effectiveResolution: ModelResolution | undefined,
|
|
256
256
|
) {}
|
|
257
257
|
|
|
258
|
-
render(width: number): string[] {
|
|
258
|
+
render(width: number): readonly string[] {
|
|
259
259
|
if (!this.agent) {
|
|
260
260
|
return [theme.fg("muted", "Select an agent"), theme.fg("dim", "to inspect settings")];
|
|
261
261
|
}
|
|
@@ -314,7 +314,7 @@ class TwoColumnBody implements Component {
|
|
|
314
314
|
private readonly maxHeight: number,
|
|
315
315
|
) {}
|
|
316
316
|
|
|
317
|
-
render(width: number): string[] {
|
|
317
|
+
render(width: number): readonly string[] {
|
|
318
318
|
const leftWidth = Math.floor(width * 0.5);
|
|
319
319
|
const rightWidth = width - leftWidth - 3;
|
|
320
320
|
const leftLines = this.leftPane.render(leftWidth);
|
|
@@ -507,7 +507,7 @@ export class AgentDashboard extends Container {
|
|
|
507
507
|
return Math.max(3, this.#computeBodyHeight() - 3);
|
|
508
508
|
}
|
|
509
509
|
|
|
510
|
-
override render(width: number): string[] {
|
|
510
|
+
override render(width: number): readonly string[] {
|
|
511
511
|
// Rebuild when terminal geometry changes so the full-screen overlay
|
|
512
512
|
// re-fits on resize.
|
|
513
513
|
if (this.#terminalRows() !== this.#builtRows || this.#uiWidth() !== this.#builtCols) {
|
|
@@ -516,10 +516,13 @@ export class AgentDashboard extends Container {
|
|
|
516
516
|
const lines = super.render(width);
|
|
517
517
|
// Pad to the full viewport so every state (list, edit, create) covers the
|
|
518
518
|
// screen as a true full-screen view instead of letting the transcript peek
|
|
519
|
-
// through below it.
|
|
519
|
+
// through below it. Copy before padding — the container's render result is
|
|
520
|
+
// component-owned and must not be mutated.
|
|
520
521
|
const rows = this.#terminalRows();
|
|
521
|
-
|
|
522
|
-
|
|
522
|
+
if (lines.length >= rows) return lines;
|
|
523
|
+
const padded = lines.slice();
|
|
524
|
+
while (padded.length < rows) padded.push("");
|
|
525
|
+
return padded;
|
|
523
526
|
}
|
|
524
527
|
|
|
525
528
|
#clampSelection(): void {
|