@oh-my-pi/pi-coding-agent 15.10.10 → 15.10.12
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 +142 -7
- package/dist/cli.js +23108 -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 +2 -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/cli-commands.d.ts +12 -0
- package/dist/types/commands/launch.d.ts +5 -1
- package/dist/types/commands/read.d.ts +1 -1
- package/dist/types/commands/usage.d.ts +25 -0
- package/dist/types/config/api-key-resolver.d.ts +3 -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 +8 -219
- package/dist/types/config/model-resolver.d.ts +34 -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 +41 -8
- package/dist/types/config/settings.d.ts +8 -1
- package/dist/types/debug/log-viewer.d.ts +1 -1
- package/dist/types/debug/raw-sse.d.ts +1 -1
- package/dist/types/edit/hashline/noop-loop-guard.d.ts +72 -0
- 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/eval/py/executor.d.ts +5 -0
- package/dist/types/eval/py/kernel.d.ts +6 -1
- package/dist/types/eval/py/runtime.d.ts +9 -0
- package/dist/types/exec/bash-executor.d.ts +2 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/extensions/runner.d.ts +3 -2
- package/dist/types/extensibility/extensions/types.d.ts +6 -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/memory-backend/index.d.ts +1 -0
- package/dist/types/memory-backend/runtime.d.ts +4 -0
- package/dist/types/memory-backend/types.d.ts +66 -1
- 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 +25 -6
- 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/index.d.ts +3 -3
- package/dist/types/modes/interactive-mode.d.ts +8 -3
- package/dist/types/modes/oauth-manual-input.d.ts +7 -0
- package/dist/types/modes/rpc/rpc-client.d.ts +39 -2
- package/dist/types/modes/rpc/rpc-mode.d.ts +31 -2
- package/dist/types/modes/rpc/rpc-subagents.d.ts +24 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +75 -1
- package/dist/types/modes/setup-wizard/index.d.ts +5 -1
- package/dist/types/modes/setup-wizard/lazy.d.ts +2 -0
- 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 +4 -1
- package/dist/types/secrets/index.d.ts +1 -1
- package/dist/types/secrets/obfuscator.d.ts +8 -2
- package/dist/types/session/agent-session.d.ts +15 -3
- package/dist/types/session/auth-broker-config.d.ts +4 -0
- package/dist/types/session/session-manager.d.ts +1 -1
- package/dist/types/session/streaming-output.d.ts +23 -0
- package/dist/types/slash-commands/acp-builtins.d.ts +16 -0
- package/dist/types/slash-commands/builtin-registry.d.ts +1 -0
- package/dist/types/slash-commands/helpers/stats-dashboard.d.ts +13 -0
- package/dist/types/slash-commands/types.d.ts +1 -1
- package/dist/types/ssh/connection-manager.d.ts +8 -0
- package/dist/types/system-prompt.d.ts +2 -0
- package/dist/types/task/executor.d.ts +1 -0
- package/dist/types/task/index.d.ts +2 -2
- package/dist/types/task/parallel.d.ts +2 -2
- package/dist/types/task/types.d.ts +8 -0
- package/dist/types/task/worktree.d.ts +2 -0
- package/dist/types/thinking.d.ts +4 -0
- package/dist/types/tiny/title-client.d.ts +11 -0
- package/dist/types/tiny/title-protocol.d.ts +1 -0
- 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/index.d.ts +6 -0
- package/dist/types/tools/sqlite-reader.d.ts +3 -0
- package/dist/types/tui/output-block.d.ts +3 -3
- package/dist/types/utils/changelog.d.ts +8 -0
- package/dist/types/utils/git.d.ts +15 -2
- package/dist/types/utils/title-generator.d.ts +3 -2
- 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/auto-thinking/classifier.ts +1 -0
- 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 +4 -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 +2 -1
- package/src/cli/usage-cli.ts +603 -0
- package/src/cli-commands.ts +30 -0
- package/src/cli.ts +76 -13
- package/src/commands/complete.ts +1 -1
- package/src/commands/launch.ts +5 -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 +4 -3
- package/src/config/api-key-resolver.ts +8 -6
- package/src/config/append-only-context-mode.ts +6 -12
- package/src/config/model-discovery.ts +554 -0
- package/src/config/model-registry.ts +320 -1041
- package/src/config/model-resolver.ts +173 -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 +61 -19
- package/src/config/settings.ts +98 -4
- 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/edit/diff.ts +47 -3
- package/src/edit/hashline/block-resolver.ts +20 -1
- package/src/edit/hashline/diff.ts +36 -1
- package/src/edit/hashline/execute.ts +47 -4
- package/src/edit/hashline/noop-loop-guard.ts +99 -0
- 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 +3 -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 +40 -22
- package/src/eval/js/shared/runtime.ts +1 -1
- package/src/eval/py/executor.ts +29 -7
- package/src/eval/py/index.ts +6 -3
- package/src/eval/py/kernel.ts +43 -4
- package/src/eval/py/runner.py +107 -3
- package/src/eval/py/runtime.ts +37 -0
- package/src/exec/bash-executor.ts +85 -4
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +3 -1
- package/src/extensibility/extensions/get-commands-handler.ts +2 -1
- package/src/extensibility/extensions/runner.ts +6 -1
- package/src/extensibility/extensions/types.ts +6 -2
- package/src/extensibility/plugins/legacy-pi-compat.ts +20 -3
- package/src/hindsight/bank.ts +17 -2
- 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 +11 -11
- 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 +183 -23
- package/src/mcp/json-rpc.ts +35 -5
- package/src/mcp/transports/stdio.ts +7 -1
- package/src/memories/index.ts +4 -1
- package/src/memory-backend/index.ts +1 -0
- package/src/memory-backend/local-backend.ts +9 -0
- package/src/memory-backend/off-backend.ts +9 -0
- package/src/memory-backend/runtime.ts +66 -0
- package/src/memory-backend/types.ts +81 -1
- package/src/mnemopi/backend.ts +176 -7
- package/src/mnemopi/state.ts +38 -2
- package/src/modes/acp/acp-agent.ts +119 -11
- package/src/modes/components/agent-dashboard.ts +10 -7
- package/src/modes/components/assistant-message.ts +32 -28
- 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 +4 -2
- 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 +4 -2
- 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 +119 -35
- package/src/modes/components/tiny-title-download-progress.ts +1 -1
- package/src/modes/components/transcript-container.ts +258 -53
- 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 +11 -4
- package/src/modes/controllers/event-controller.ts +73 -4
- package/src/modes/controllers/input-controller.ts +2 -1
- package/src/modes/controllers/mcp-command-controller.ts +39 -4
- package/src/modes/controllers/selector-controller.ts +1 -1
- package/src/modes/controllers/streaming-reveal.ts +85 -18
- package/src/modes/index.ts +3 -21
- package/src/modes/interactive-mode.ts +42 -18
- package/src/modes/oauth-manual-input.ts +30 -3
- package/src/modes/rpc/rpc-client.ts +154 -3
- package/src/modes/rpc/rpc-mode.ts +97 -12
- package/src/modes/rpc/rpc-subagents.ts +265 -0
- package/src/modes/rpc/rpc-types.ts +81 -1
- package/src/modes/setup-wizard/index.ts +12 -2
- package/src/modes/setup-wizard/lazy.ts +16 -0
- 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 +4 -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 +13 -24
- 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 +5 -7
- 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 +1 -1
- package/src/sdk.ts +31 -11
- package/src/secrets/index.ts +8 -1
- package/src/secrets/obfuscator.ts +39 -18
- package/src/session/agent-session.ts +223 -64
- package/src/session/auth-broker-config.ts +30 -1
- package/src/session/session-manager.ts +2 -2
- package/src/session/streaming-output.ts +188 -11
- package/src/slash-commands/acp-builtins.ts +24 -0
- package/src/slash-commands/builtin-registry.ts +40 -0
- package/src/slash-commands/helpers/stats-dashboard.ts +85 -0
- package/src/slash-commands/types.ts +1 -1
- package/src/ssh/connection-manager.ts +27 -0
- package/src/system-prompt.ts +14 -0
- package/src/task/commands.ts +2 -1
- package/src/task/executor.ts +74 -65
- package/src/task/index.ts +146 -68
- package/src/task/parallel.ts +3 -3
- package/src/task/render.ts +20 -5
- package/src/task/types.ts +9 -0
- package/src/task/worktree.ts +64 -56
- package/src/thinking.ts +9 -1
- package/src/tiny/title-client.ts +60 -16
- package/src/tiny/title-protocol.ts +1 -1
- package/src/tiny/worker.ts +6 -4
- 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 +100 -18
- 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 +14 -13
- package/src/tools/index.ts +13 -1
- package/src/tools/inspect-image.ts +1 -0
- 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 +38 -8
- 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/commit-message-generator.ts +1 -0
- package/src/utils/file-mentions.ts +2 -1
- package/src/utils/git.ts +267 -13
- package/src/utils/title-generator.ts +24 -5
- 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/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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Memory backend abstraction.
|
|
3
3
|
*
|
|
4
|
-
* Backends are mutually exclusive — `await resolveMemoryBackend(settings)`
|
|
4
|
+
* Backends are mutually exclusive — `await resolveMemoryBackend(settings)` returns
|
|
5
5
|
* exactly one. Implementations MUST be self-contained: they own the per-session
|
|
6
6
|
* state they create in `start()` and tear it down on `clear()`.
|
|
7
7
|
*/
|
|
@@ -15,6 +15,73 @@ import type { AgentSession } from "../session/agent-session";
|
|
|
15
15
|
|
|
16
16
|
export type MemoryBackendId = "off" | "local" | "hindsight" | "mnemopi";
|
|
17
17
|
|
|
18
|
+
export interface MemoryBackendStatus {
|
|
19
|
+
backend: MemoryBackendId;
|
|
20
|
+
active: boolean;
|
|
21
|
+
writable: boolean;
|
|
22
|
+
searchable: boolean;
|
|
23
|
+
scope?: string;
|
|
24
|
+
retainBank?: string;
|
|
25
|
+
recallBanks?: string[];
|
|
26
|
+
workingCount?: number;
|
|
27
|
+
episodicCount?: number;
|
|
28
|
+
tripleCount?: number;
|
|
29
|
+
lastMemory?: string;
|
|
30
|
+
lastRecall?: boolean;
|
|
31
|
+
database?: string;
|
|
32
|
+
message?: string;
|
|
33
|
+
error?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface MemoryBackendSearchOptions {
|
|
37
|
+
limit?: number;
|
|
38
|
+
/** Best-effort abort signal. Backends may only observe it before/after an underlying recall call. */
|
|
39
|
+
signal?: AbortSignal;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface MemoryBackendSearchItem {
|
|
43
|
+
id?: string;
|
|
44
|
+
content: string;
|
|
45
|
+
source?: string;
|
|
46
|
+
timestamp?: string;
|
|
47
|
+
score?: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface MemoryBackendSearchResult {
|
|
51
|
+
backend: MemoryBackendId;
|
|
52
|
+
query: string;
|
|
53
|
+
count: number;
|
|
54
|
+
items: MemoryBackendSearchItem[];
|
|
55
|
+
message?: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface MemoryBackendSaveInput {
|
|
59
|
+
content: string;
|
|
60
|
+
context?: string;
|
|
61
|
+
source?: string;
|
|
62
|
+
importance?: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface MemoryBackendSaveResult {
|
|
66
|
+
backend: MemoryBackendId;
|
|
67
|
+
stored: number;
|
|
68
|
+
ids?: string[];
|
|
69
|
+
queued?: boolean;
|
|
70
|
+
message?: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface MemoryBackendOperationContext {
|
|
74
|
+
agentDir: string;
|
|
75
|
+
cwd: string;
|
|
76
|
+
session?: AgentSession;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface MemoryRuntimeContext {
|
|
80
|
+
status(): Promise<MemoryBackendStatus>;
|
|
81
|
+
search(query: string, options?: MemoryBackendSearchOptions): Promise<MemoryBackendSearchResult>;
|
|
82
|
+
save(input: string | MemoryBackendSaveInput): Promise<MemoryBackendSaveResult>;
|
|
83
|
+
}
|
|
84
|
+
|
|
18
85
|
export interface MemoryBackendStartOptions {
|
|
19
86
|
session: AgentSession;
|
|
20
87
|
settings: Settings;
|
|
@@ -53,6 +120,19 @@ export interface MemoryBackend {
|
|
|
53
120
|
/** Force consolidation/retain to happen now (slash `/memory enqueue`). */
|
|
54
121
|
enqueue(agentDir: string, cwd: string, session?: AgentSession): Promise<void>;
|
|
55
122
|
|
|
123
|
+
/** Structured state for UI, slash commands, and extensions. */
|
|
124
|
+
status?(context: MemoryBackendOperationContext): Promise<MemoryBackendStatus>;
|
|
125
|
+
|
|
126
|
+
/** Explicit user-facing semantic/lexical search. */
|
|
127
|
+
search?(
|
|
128
|
+
context: MemoryBackendOperationContext,
|
|
129
|
+
query: string,
|
|
130
|
+
options?: MemoryBackendSearchOptions,
|
|
131
|
+
): Promise<MemoryBackendSearchResult>;
|
|
132
|
+
|
|
133
|
+
/** Explicit user-facing save operation. */
|
|
134
|
+
save?(context: MemoryBackendOperationContext, input: MemoryBackendSaveInput): Promise<MemoryBackendSaveResult>;
|
|
135
|
+
|
|
56
136
|
/** Render backend-specific memory statistics as markdown (`/memory stats`). */
|
|
57
137
|
stats?(agentDir: string, cwd: string, session?: AgentSession): Promise<string | undefined>;
|
|
58
138
|
|
package/src/mnemopi/backend.ts
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
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
|
-
|
|
9
8
|
import type { ModelRegistry } from "../config/model-registry";
|
|
10
9
|
import { resolveRoleSelection } from "../config/model-resolver";
|
|
11
|
-
import type {
|
|
10
|
+
import type {
|
|
11
|
+
MemoryBackend,
|
|
12
|
+
MemoryBackendSaveInput,
|
|
13
|
+
MemoryBackendSearchItem,
|
|
14
|
+
MemoryBackendStartOptions,
|
|
15
|
+
MemoryBackendStatus,
|
|
16
|
+
} from "../memory-backend/types";
|
|
12
17
|
import memoryConsolidationPrompt from "../prompts/system/memory-consolidation-system.md" with { type: "text" };
|
|
13
18
|
import memoryExtractionPrompt from "../prompts/system/memory-extraction-system.md" with { type: "text" };
|
|
14
19
|
import type { AgentSession } from "../session/agent-session";
|
|
@@ -25,10 +30,25 @@ import {
|
|
|
25
30
|
getMnemopiScopedBanks,
|
|
26
31
|
getMnemopiScopedDbPaths,
|
|
27
32
|
getMnemopiSessionState,
|
|
33
|
+
loadMnemopi,
|
|
34
|
+
loadMnemopiCore,
|
|
28
35
|
MnemopiSessionState,
|
|
36
|
+
requireMnemopi,
|
|
37
|
+
requireMnemopiCore,
|
|
29
38
|
setMnemopiSessionState,
|
|
30
39
|
} from "./state";
|
|
31
40
|
|
|
41
|
+
// `/diagnose` is the only user of this subpath; load it lazily alongside the
|
|
42
|
+
// loaders in ./state to keep mnemopi off the CLI startup module graph.
|
|
43
|
+
let mnemopiDiagnoseMod: typeof MnemopiDiagnoseNs | undefined;
|
|
44
|
+
|
|
45
|
+
async function loadMnemopiDiagnose(): Promise<typeof MnemopiDiagnoseNs> {
|
|
46
|
+
if (!mnemopiDiagnoseMod) {
|
|
47
|
+
mnemopiDiagnoseMod = await import("@oh-my-pi/pi-mnemopi/diagnose");
|
|
48
|
+
}
|
|
49
|
+
return mnemopiDiagnoseMod;
|
|
50
|
+
}
|
|
51
|
+
|
|
32
52
|
const STATIC_INSTRUCTIONS = [
|
|
33
53
|
"# Memory",
|
|
34
54
|
"This agent has local Mnemopi long-term memory.",
|
|
@@ -68,6 +88,7 @@ export const mnemopiBackend: MemoryBackend = {
|
|
|
68
88
|
|
|
69
89
|
try {
|
|
70
90
|
const config = await loadMnemopiConfigWithProviders(settings, agentDir, modelRegistry, sessionId);
|
|
91
|
+
await Promise.all([loadMnemopi(), loadMnemopiCore()]);
|
|
71
92
|
const state = new MnemopiSessionState({ sessionId, config, session });
|
|
72
93
|
const previous = setMnemopiSessionState(session, state);
|
|
73
94
|
previous?.dispose();
|
|
@@ -97,6 +118,7 @@ export const mnemopiBackend: MemoryBackend = {
|
|
|
97
118
|
previous?.dispose();
|
|
98
119
|
const config = previous?.config ?? (session ? loadMnemopiConfig(session.settings, agentDir) : undefined);
|
|
99
120
|
if (!config) return;
|
|
121
|
+
await loadMnemopiCore();
|
|
100
122
|
await removeDbFiles(getMnemopiScopedDbPaths(config));
|
|
101
123
|
},
|
|
102
124
|
|
|
@@ -110,6 +132,7 @@ export const mnemopiBackend: MemoryBackend = {
|
|
|
110
132
|
session.modelRegistry,
|
|
111
133
|
session.sessionId,
|
|
112
134
|
);
|
|
135
|
+
await Promise.all([loadMnemopi(), loadMnemopiCore()]);
|
|
113
136
|
state = new MnemopiSessionState({ sessionId: session.sessionId, config, session });
|
|
114
137
|
setMnemopiSessionState(session, state);
|
|
115
138
|
}
|
|
@@ -124,6 +147,7 @@ export const mnemopiBackend: MemoryBackend = {
|
|
|
124
147
|
},
|
|
125
148
|
|
|
126
149
|
async stats(agentDir, _cwd, session): Promise<string | undefined> {
|
|
150
|
+
await Promise.all([loadMnemopi(), loadMnemopiCore()]);
|
|
127
151
|
const { targets, owned } = createStatsTargets(agentDir, session);
|
|
128
152
|
try {
|
|
129
153
|
if (targets.length === 0) return undefined;
|
|
@@ -137,6 +161,7 @@ export const mnemopiBackend: MemoryBackend = {
|
|
|
137
161
|
const state = getMnemopiSessionState(session);
|
|
138
162
|
const config = state?.config ?? (session ? loadMnemopiConfig(session.settings, agentDir) : undefined);
|
|
139
163
|
if (!config) return undefined;
|
|
164
|
+
const [{ inspectDatabase }] = await Promise.all([loadMnemopiDiagnose(), loadMnemopiCore()]);
|
|
140
165
|
const banks = getMnemopiScopedBanks(config);
|
|
141
166
|
const dbPaths = getMnemopiScopedDbPaths(config);
|
|
142
167
|
const summaries = dbPaths.map((dbPath, index) => ({
|
|
@@ -146,6 +171,101 @@ export const mnemopiBackend: MemoryBackend = {
|
|
|
146
171
|
return renderMnemopiDiagnostics(summaries);
|
|
147
172
|
},
|
|
148
173
|
|
|
174
|
+
async status({ agentDir, session }): Promise<MemoryBackendStatus> {
|
|
175
|
+
const state = getMnemopiSessionState(session);
|
|
176
|
+
const primary = state?.aliasOf ?? state;
|
|
177
|
+
if (!primary) {
|
|
178
|
+
return {
|
|
179
|
+
backend: "mnemopi",
|
|
180
|
+
active: false,
|
|
181
|
+
writable: false,
|
|
182
|
+
searchable: false,
|
|
183
|
+
message: "Mnemopi backend is not initialised for this session.",
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const { targets, owned } = createStatsTargets(agentDir, session);
|
|
188
|
+
try {
|
|
189
|
+
if (targets.length === 0) {
|
|
190
|
+
return {
|
|
191
|
+
backend: "mnemopi",
|
|
192
|
+
active: false,
|
|
193
|
+
writable: false,
|
|
194
|
+
searchable: false,
|
|
195
|
+
message: "Mnemopi backend is configured but not initialised for this session.",
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
return summarizeMnemopiStatus(targets, session);
|
|
199
|
+
} finally {
|
|
200
|
+
for (const memory of owned) memory.close();
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
async search({ session }, query, options) {
|
|
205
|
+
const state = getMnemopiSessionState(session);
|
|
206
|
+
const primary = state?.aliasOf ?? state;
|
|
207
|
+
if (!primary) {
|
|
208
|
+
return {
|
|
209
|
+
backend: "mnemopi",
|
|
210
|
+
query,
|
|
211
|
+
count: 0,
|
|
212
|
+
items: [],
|
|
213
|
+
message: "Mnemopi backend is not initialised for this session.",
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
if (options?.signal?.aborted) {
|
|
217
|
+
return { backend: "mnemopi", query, count: 0, items: [], message: "Search aborted." };
|
|
218
|
+
}
|
|
219
|
+
const limit = clampLimit(options?.limit);
|
|
220
|
+
const results = (await primary.recallResultsScoped(query)).slice(0, limit);
|
|
221
|
+
if (options?.signal?.aborted) {
|
|
222
|
+
return { backend: "mnemopi", query, count: 0, items: [], message: "Search aborted." };
|
|
223
|
+
}
|
|
224
|
+
const items: MemoryBackendSearchItem[] = results.map(result => ({
|
|
225
|
+
id: result.id,
|
|
226
|
+
content: result.content,
|
|
227
|
+
source: result.source ?? undefined,
|
|
228
|
+
timestamp: result.timestamp ?? undefined,
|
|
229
|
+
score: result.score,
|
|
230
|
+
}));
|
|
231
|
+
return { backend: "mnemopi", query, count: items.length, items };
|
|
232
|
+
},
|
|
233
|
+
|
|
234
|
+
async save({ cwd, session }, input: MemoryBackendSaveInput) {
|
|
235
|
+
const state = getMnemopiSessionState(session);
|
|
236
|
+
const primary = state?.aliasOf ?? state;
|
|
237
|
+
if (!primary) {
|
|
238
|
+
return {
|
|
239
|
+
backend: "mnemopi",
|
|
240
|
+
stored: 0,
|
|
241
|
+
message: "Mnemopi backend is not initialised for this session.",
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
const content = input.content.trim();
|
|
245
|
+
if (!content) return { backend: "mnemopi", stored: 0, message: "Memory content is empty." };
|
|
246
|
+
const id = primary.rememberScoped(content, {
|
|
247
|
+
source: input.source || "coding-agent-memory-command",
|
|
248
|
+
importance: normalizeImportance(input.importance),
|
|
249
|
+
metadata: {
|
|
250
|
+
session_id: primary.sessionId,
|
|
251
|
+
cwd,
|
|
252
|
+
context: input.context ?? null,
|
|
253
|
+
operation: "memory.save",
|
|
254
|
+
},
|
|
255
|
+
scope: "bank",
|
|
256
|
+
extract: true,
|
|
257
|
+
extractEntities: true,
|
|
258
|
+
veracity: "user",
|
|
259
|
+
memoryType: "fact",
|
|
260
|
+
});
|
|
261
|
+
return {
|
|
262
|
+
backend: "mnemopi",
|
|
263
|
+
stored: id ? 1 : 0,
|
|
264
|
+
ids: id ? [id] : [],
|
|
265
|
+
message: id ? undefined : "Mnemopi did not return a stored memory id.",
|
|
266
|
+
};
|
|
267
|
+
},
|
|
268
|
+
|
|
149
269
|
async preCompactionContext(messages, _settings, session): Promise<string | undefined> {
|
|
150
270
|
const state = getMnemopiSessionState(session);
|
|
151
271
|
return await state?.recallForCompaction(messages);
|
|
@@ -179,6 +299,7 @@ function createStatsTargets(
|
|
|
179
299
|
|
|
180
300
|
function createStatsMemory(config: MnemopiBackendConfig, bank: string): Mnemopi {
|
|
181
301
|
const providerOptions = config.providerOptions as Record<string, unknown>;
|
|
302
|
+
const { Mnemopi } = requireMnemopi();
|
|
182
303
|
return new Mnemopi({
|
|
183
304
|
dbPath: resolveBankDbPath(config, bank),
|
|
184
305
|
bank,
|
|
@@ -193,6 +314,7 @@ function createStatsMemory(config: MnemopiBackendConfig, bank: string): Mnemopi
|
|
|
193
314
|
function resolveBankDbPath(config: MnemopiBackendConfig, bank: string): string {
|
|
194
315
|
const sharedBank = config.globalBank ?? config.baseBank ?? "default";
|
|
195
316
|
if (bank === sharedBank) return config.dbPath;
|
|
317
|
+
const { BankManager } = requireMnemopiCore();
|
|
196
318
|
return new BankManager(path.dirname(config.dbPath)).getBankDbPath(bank);
|
|
197
319
|
}
|
|
198
320
|
|
|
@@ -225,6 +347,52 @@ function renderMnemopiStats(targets: readonly MnemopiStatsTarget[]): string {
|
|
|
225
347
|
return lines.join("\n");
|
|
226
348
|
}
|
|
227
349
|
|
|
350
|
+
function summarizeMnemopiStatus(
|
|
351
|
+
targets: readonly MnemopiStatsTarget[],
|
|
352
|
+
session: AgentSession | undefined,
|
|
353
|
+
): MemoryBackendStatus {
|
|
354
|
+
let workingCount = 0;
|
|
355
|
+
let episodicCount = 0;
|
|
356
|
+
let tripleCount = 0;
|
|
357
|
+
let lastMemory: string | undefined;
|
|
358
|
+
let database: string | undefined;
|
|
359
|
+
for (const target of targets) {
|
|
360
|
+
const stats = target.memory.getStats();
|
|
361
|
+
workingCount += statCount(stats.beam.working_memory);
|
|
362
|
+
episodicCount += statCount(stats.beam.episodic_memory);
|
|
363
|
+
tripleCount += stats.beam.triples.total;
|
|
364
|
+
lastMemory ??= stats.last_memory ?? undefined;
|
|
365
|
+
database ??= stats.database ? shortenPath(stats.database) : undefined;
|
|
366
|
+
}
|
|
367
|
+
const state = getMnemopiSessionState(session);
|
|
368
|
+
const primary = state?.aliasOf ?? state;
|
|
369
|
+
return {
|
|
370
|
+
backend: "mnemopi",
|
|
371
|
+
active: true,
|
|
372
|
+
writable: true,
|
|
373
|
+
searchable: true,
|
|
374
|
+
scope: primary?.config.scoping,
|
|
375
|
+
retainBank: primary?.getScopedRetainTarget().bank ?? targets[0]?.bank,
|
|
376
|
+
recallBanks: primary?.getScopedRecallTargets().map(target => target.bank) ?? targets.map(target => target.bank),
|
|
377
|
+
workingCount,
|
|
378
|
+
episodicCount,
|
|
379
|
+
tripleCount,
|
|
380
|
+
lastMemory,
|
|
381
|
+
lastRecall: Boolean(primary?.lastRecallSnippet),
|
|
382
|
+
database,
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function clampLimit(limit: number | undefined): number {
|
|
387
|
+
if (!Number.isFinite(limit)) return 10;
|
|
388
|
+
return Math.max(1, Math.min(50, Math.trunc(limit ?? 10)));
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
function normalizeImportance(value: number | undefined): number {
|
|
392
|
+
if (!Number.isFinite(value)) return 0.75;
|
|
393
|
+
return Math.max(0, Math.min(1, value ?? 0.75));
|
|
394
|
+
}
|
|
395
|
+
|
|
228
396
|
function renderMnemopiDiagnostics(entries: readonly { bank: string; summary: DiagnosticSummary }[]): string {
|
|
229
397
|
const lines = [
|
|
230
398
|
"# Mnemopi Memory Diagnostics",
|
|
@@ -321,8 +489,8 @@ async function resolveMnemopiProviderOptions(
|
|
|
321
489
|
return {
|
|
322
490
|
...base,
|
|
323
491
|
llm: async (prompt, opts) => {
|
|
324
|
-
const
|
|
325
|
-
if (!
|
|
492
|
+
const hasApiKey = await modelRegistry.getApiKey(model, sessionId);
|
|
493
|
+
if (!hasApiKey) {
|
|
326
494
|
logger.warn("Mnemopi: smol completion requested but no current API key is available.", {
|
|
327
495
|
provider: model.provider,
|
|
328
496
|
model: model.id,
|
|
@@ -338,6 +506,7 @@ async function resolveMnemopiProviderOptions(
|
|
|
338
506
|
apiKey: modelRegistry.resolver(model.provider, {
|
|
339
507
|
sessionId,
|
|
340
508
|
baseUrl: model.baseUrl,
|
|
509
|
+
modelId: model.id,
|
|
341
510
|
}),
|
|
342
511
|
maxTokens: opts?.maxTokens,
|
|
343
512
|
temperature: opts?.temperature,
|
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
|
|
|
@@ -71,7 +71,12 @@ import {
|
|
|
71
71
|
type SessionInfo as StoredSessionInfo,
|
|
72
72
|
type UsageStatistics,
|
|
73
73
|
} from "../../session/session-manager";
|
|
74
|
-
import {
|
|
74
|
+
import {
|
|
75
|
+
ACP_BUILTIN_RESERVED_NAMES,
|
|
76
|
+
ACP_BUILTIN_SLASH_COMMANDS,
|
|
77
|
+
executeAcpBuiltinSlashCommand,
|
|
78
|
+
isAcpBuiltinShadowedName,
|
|
79
|
+
} from "../../slash-commands/acp-builtins";
|
|
75
80
|
import { AUTO_THINKING, parseConfiguredThinkingLevel } from "../../thinking";
|
|
76
81
|
import { normalizeLocalScheme } from "../../tools/path-utils";
|
|
77
82
|
import { runResolveInvocation } from "../../tools/resolve";
|
|
@@ -117,6 +122,7 @@ type PromptQueueState = {
|
|
|
117
122
|
promise: Promise<void>;
|
|
118
123
|
release: (() => void) | undefined;
|
|
119
124
|
};
|
|
125
|
+
type PromptLifecycleError = Error & { readonly code: "ACP_SESSION_CLOSED" };
|
|
120
126
|
|
|
121
127
|
type PromptTurnState = {
|
|
122
128
|
userMessageId: string;
|
|
@@ -158,6 +164,9 @@ type ManagedSessionRecord = {
|
|
|
158
164
|
// Installed inside `#scheduleBootstrapUpdates` (post-race-guard); released
|
|
159
165
|
// in `#disposeSessionRecord`. Lives independent of any prompt turn.
|
|
160
166
|
lifetimeUnsubscribe: (() => void) | undefined;
|
|
167
|
+
closedError: PromptLifecycleError | undefined;
|
|
168
|
+
promptEventHandlers: Set<Promise<void>>;
|
|
169
|
+
extensionUserMessageTasks: Set<Promise<void>>;
|
|
161
170
|
};
|
|
162
171
|
|
|
163
172
|
type ReplayableMessage = {
|
|
@@ -594,7 +603,23 @@ export class AcpAgent implements Agent {
|
|
|
594
603
|
const record = this.#getSessionRecord(params.sessionId);
|
|
595
604
|
const activeTurn = record.promptTurn;
|
|
596
605
|
if (activeTurn && !activeTurn.settled && record.session.isStreaming) {
|
|
597
|
-
|
|
606
|
+
// New prompt arrived while the previous turn is still in-flight (e.g. the
|
|
607
|
+
// client sent a message immediately after pressing stop, before or without
|
|
608
|
+
// a preceding session/cancel notification). Implicitly cancel the running
|
|
609
|
+
// turn so the new prompt can queue behind the abort cleanup — identical to
|
|
610
|
+
// what cancel() does when called explicitly. #beginCancelCleanup is
|
|
611
|
+
// idempotent, so a concurrent session/cancel notification is harmless.
|
|
612
|
+
// Mirror cancel()'s timeout handling: if abort() hangs past the cleanup
|
|
613
|
+
// timeout, close the managed session instead of leaving it registered
|
|
614
|
+
// with a still-streaming AgentSession. The queued prompt below observes
|
|
615
|
+
// the same cleanup rejection and fails accordingly.
|
|
616
|
+
this.#beginCancelCleanup(record, activeTurn).catch(async (error: unknown) => {
|
|
617
|
+
logger.warn("ACP cancel cleanup timed out; closing session", {
|
|
618
|
+
sessionId: record.session.sessionId,
|
|
619
|
+
error,
|
|
620
|
+
});
|
|
621
|
+
await this.#closeManagedSession(params.sessionId, record);
|
|
622
|
+
});
|
|
598
623
|
}
|
|
599
624
|
return await this.#queuePrompt(record, async () => {
|
|
600
625
|
const previousTurn = record.promptTurn;
|
|
@@ -607,6 +632,7 @@ export class AcpAgent implements Agent {
|
|
|
607
632
|
await previousTurn.promise.catch(() => undefined);
|
|
608
633
|
await previousTurn.cleanup;
|
|
609
634
|
}
|
|
635
|
+
this.#throwIfRecordClosed(record);
|
|
610
636
|
|
|
611
637
|
const converted = this.#convertPromptBlocks(params.prompt);
|
|
612
638
|
const pendingPrompt = Promise.withResolvers<PromptResponse>();
|
|
@@ -623,7 +649,7 @@ export class AcpAgent implements Agent {
|
|
|
623
649
|
};
|
|
624
650
|
|
|
625
651
|
record.promptTurn.unsubscribe = record.session.subscribe(event => {
|
|
626
|
-
|
|
652
|
+
this.#trackPromptEvent(record, event);
|
|
627
653
|
});
|
|
628
654
|
|
|
629
655
|
this.#runPromptOrCommand(record, converted.text, converted.images).catch((error: unknown) => {
|
|
@@ -643,6 +669,7 @@ export class AcpAgent implements Agent {
|
|
|
643
669
|
release: releaseQueue,
|
|
644
670
|
};
|
|
645
671
|
await previousQueue.promise;
|
|
672
|
+
this.#throwIfRecordClosed(record);
|
|
646
673
|
try {
|
|
647
674
|
return await run();
|
|
648
675
|
} finally {
|
|
@@ -653,6 +680,55 @@ export class AcpAgent implements Agent {
|
|
|
653
680
|
}
|
|
654
681
|
}
|
|
655
682
|
|
|
683
|
+
#throwIfRecordClosed(record: ManagedSessionRecord): void {
|
|
684
|
+
if (record.closedError) {
|
|
685
|
+
throw record.closedError;
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
#createPromptLifecycleError(message: string): PromptLifecycleError {
|
|
690
|
+
return Object.assign(new Error(message), { code: "ACP_SESSION_CLOSED" as const });
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
#trackPromptEvent(record: ManagedSessionRecord, event: AgentSessionEvent): void {
|
|
694
|
+
const handling = this.#handlePromptEvent(record, event).catch((error: unknown) => {
|
|
695
|
+
logger.warn("ACP prompt event handler failed", { error });
|
|
696
|
+
});
|
|
697
|
+
record.promptEventHandlers.add(handling);
|
|
698
|
+
void handling.finally(() => {
|
|
699
|
+
record.promptEventHandlers.delete(handling);
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
async #waitForPromptEventHandlers(record: ManagedSessionRecord): Promise<void> {
|
|
704
|
+
while (record.promptEventHandlers.size > 0) {
|
|
705
|
+
await Promise.allSettled(Array.from(record.promptEventHandlers));
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
#trackExtensionUserMessage(record: ManagedSessionRecord, task: Promise<void>): void {
|
|
710
|
+
const tracked = task.catch((error: unknown) => {
|
|
711
|
+
logger.warn("ACP extension sendUserMessage failed", { error });
|
|
712
|
+
});
|
|
713
|
+
record.extensionUserMessageTasks.add(tracked);
|
|
714
|
+
void tracked.finally(() => {
|
|
715
|
+
record.extensionUserMessageTasks.delete(tracked);
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
async #waitForExtensionUserMessages(
|
|
720
|
+
record: ManagedSessionRecord,
|
|
721
|
+
baseline: ReadonlySet<Promise<void>>,
|
|
722
|
+
): Promise<void> {
|
|
723
|
+
while (true) {
|
|
724
|
+
const pending = Array.from(record.extensionUserMessageTasks).filter(task => !baseline.has(task));
|
|
725
|
+
if (pending.length === 0) {
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
await Promise.allSettled(pending);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
656
732
|
async #runPromptOrCommand(record: ManagedSessionRecord, text: string, images: AgentImageContent[]): Promise<void> {
|
|
657
733
|
const skillResult = await this.#tryRunSkillCommand(record, text);
|
|
658
734
|
if (skillResult) {
|
|
@@ -699,7 +775,18 @@ export class AcpAgent implements Agent {
|
|
|
699
775
|
return;
|
|
700
776
|
}
|
|
701
777
|
|
|
702
|
-
|
|
778
|
+
const extensionPromptBaseline = new Set(record.extensionUserMessageTasks);
|
|
779
|
+
const agentInvoked = await record.session.prompt(text, { images });
|
|
780
|
+
// Extension and custom-TS commands are handled locally inside session.prompt().
|
|
781
|
+
// An ACP extension command can still call pi.sendUserMessage(), which starts
|
|
782
|
+
// an async nested prompt through the extension runtime. Keep the ACP turn
|
|
783
|
+
// subscribed until those scheduled prompts and their event handlers drain;
|
|
784
|
+
// only then is `false` proof that the slash command was purely local.
|
|
785
|
+
if (!agentInvoked) {
|
|
786
|
+
await this.#waitForExtensionUserMessages(record, extensionPromptBaseline);
|
|
787
|
+
await this.#waitForPromptEventHandlers(record);
|
|
788
|
+
this.#finishPrompt(record, { stopReason: "end_turn" });
|
|
789
|
+
}
|
|
703
790
|
}
|
|
704
791
|
|
|
705
792
|
async #tryRunSkillCommand(record: ManagedSessionRecord, text: string): Promise<boolean> {
|
|
@@ -991,6 +1078,9 @@ export class AcpAgent implements Agent {
|
|
|
991
1078
|
liveMessageProgress: undefined,
|
|
992
1079
|
toolArgsById: new Map(),
|
|
993
1080
|
extensionsConfigured: false,
|
|
1081
|
+
closedError: undefined,
|
|
1082
|
+
promptEventHandlers: new Set(),
|
|
1083
|
+
extensionUserMessageTasks: new Set(),
|
|
994
1084
|
lifetimeUnsubscribe: undefined,
|
|
995
1085
|
};
|
|
996
1086
|
}
|
|
@@ -1582,10 +1672,12 @@ export class AcpAgent implements Agent {
|
|
|
1582
1672
|
commands.push(command);
|
|
1583
1673
|
};
|
|
1584
1674
|
|
|
1585
|
-
// Advertise in the order dispatch resolves them
|
|
1586
|
-
//
|
|
1587
|
-
//
|
|
1588
|
-
// commands
|
|
1675
|
+
// Advertise in the order dispatch resolves them (mirrors AgentSession
|
|
1676
|
+
// dispatch: builtins → skills → extensions → custom TS → file-based).
|
|
1677
|
+
// `appendCommand` dedupes by name so earlier entries win; extension
|
|
1678
|
+
// commands therefore correctly shadow custom TS commands of the same
|
|
1679
|
+
// name, matching the runtime behaviour of #tryExecuteExtensionCommand
|
|
1680
|
+
// running before #tryExecuteCustomCommand.
|
|
1589
1681
|
for (const command of ACP_BUILTIN_SLASH_COMMANDS) {
|
|
1590
1682
|
appendCommand(command);
|
|
1591
1683
|
}
|
|
@@ -1600,6 +1692,20 @@ export class AcpAgent implements Agent {
|
|
|
1600
1692
|
}
|
|
1601
1693
|
}
|
|
1602
1694
|
|
|
1695
|
+
for (const command of session.extensionRunner?.getRegisteredCommands(ACP_BUILTIN_RESERVED_NAMES) ?? []) {
|
|
1696
|
+
// Reserved-set filtering in getRegisteredCommands only covers exact
|
|
1697
|
+
// names; colon-namespaced names whose prefix is a builtin (e.g.
|
|
1698
|
+
// `model:foo`) would still dispatch to the builtin in ACP.
|
|
1699
|
+
if (isAcpBuiltinShadowedName(command.name)) {
|
|
1700
|
+
continue;
|
|
1701
|
+
}
|
|
1702
|
+
appendCommand({
|
|
1703
|
+
name: command.name,
|
|
1704
|
+
description: command.description ?? "(extension command)",
|
|
1705
|
+
input: { hint: "arguments" },
|
|
1706
|
+
});
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1603
1709
|
for (const command of session.customCommands) {
|
|
1604
1710
|
appendCommand({
|
|
1605
1711
|
name: command.command.name,
|
|
@@ -2069,9 +2175,7 @@ export class AcpAgent implements Agent {
|
|
|
2069
2175
|
});
|
|
2070
2176
|
},
|
|
2071
2177
|
sendUserMessage: (content, options) => {
|
|
2072
|
-
record.session.sendUserMessage(content, options)
|
|
2073
|
-
logger.warn("ACP extension sendUserMessage failed", { error });
|
|
2074
|
-
});
|
|
2178
|
+
this.#trackExtensionUserMessage(record, record.session.sendUserMessage(content, options));
|
|
2075
2179
|
},
|
|
2076
2180
|
appendEntry: (customType, data) => {
|
|
2077
2181
|
record.session.sessionManager.appendCustomEntry(customType, data);
|
|
@@ -2224,6 +2328,7 @@ export class AcpAgent implements Agent {
|
|
|
2224
2328
|
}
|
|
2225
2329
|
|
|
2226
2330
|
async #closeManagedSession(sessionId: string, record: ManagedSessionRecord): Promise<void> {
|
|
2331
|
+
record.closedError ??= this.#createPromptLifecycleError("ACP session closed before queued prompt could run");
|
|
2227
2332
|
this.#sessions.delete(sessionId);
|
|
2228
2333
|
await this.#cancelPromptForClose(record);
|
|
2229
2334
|
await this.#disposeSessionRecord(record);
|
|
@@ -2279,6 +2384,9 @@ export class AcpAgent implements Agent {
|
|
|
2279
2384
|
await Promise.all(
|
|
2280
2385
|
records.map(async ([sessionId, record]) => {
|
|
2281
2386
|
try {
|
|
2387
|
+
record.closedError ??= this.#createPromptLifecycleError(
|
|
2388
|
+
"ACP agent disposed before queued prompt could run",
|
|
2389
|
+
);
|
|
2282
2390
|
await this.#cancelPromptForClose(record);
|
|
2283
2391
|
await this.#disposeSessionRecord(record);
|
|
2284
2392
|
} catch (error) {
|