@oh-my-pi/pi-coding-agent 15.12.3 → 15.13.0
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 +347 -7
- package/dist/cli.js +1615 -1231
- package/dist/types/async/job-manager.d.ts +15 -0
- package/dist/types/autolearn/controller.d.ts +25 -0
- package/dist/types/autolearn/managed-skills.d.ts +45 -0
- package/dist/types/autoresearch/state.d.ts +1 -1
- package/dist/types/autoresearch/tools/init-experiment.d.ts +1 -1
- package/dist/types/autoresearch/tools/log-experiment.d.ts +1 -1
- package/dist/types/autoresearch/tools/run-experiment.d.ts +1 -1
- package/dist/types/autoresearch/tools/update-notes.d.ts +1 -1
- package/dist/types/autoresearch/types.d.ts +1 -1
- package/dist/types/cli/args.d.ts +19 -2
- package/dist/types/cli/models-cli.d.ts +49 -0
- package/dist/types/cli/session-picker.d.ts +1 -1
- package/dist/types/cli/setup-cli.d.ts +1 -1
- package/dist/types/cli/setup-model-picker.d.ts +14 -0
- package/dist/types/collab/protocol.d.ts +1 -1
- package/dist/types/commands/launch.d.ts +0 -3
- package/dist/types/commands/models.d.ts +33 -0
- package/dist/types/commands/say.d.ts +24 -0
- package/dist/types/commands/token.d.ts +25 -0
- package/dist/types/commit/agentic/tools/analyze-file.d.ts +1 -1
- package/dist/types/commit/agentic/tools/git-file-diff.d.ts +1 -1
- package/dist/types/commit/agentic/tools/git-hunk.d.ts +1 -1
- package/dist/types/commit/agentic/tools/git-overview.d.ts +1 -1
- package/dist/types/commit/agentic/tools/propose-changelog.d.ts +1 -1
- package/dist/types/commit/agentic/tools/propose-commit.d.ts +1 -1
- package/dist/types/commit/agentic/tools/recent-commits.d.ts +1 -1
- package/dist/types/commit/agentic/tools/schemas.d.ts +1 -1
- package/dist/types/commit/agentic/tools/split-commit.d.ts +1 -1
- package/dist/types/commit/changelog/generate.d.ts +1 -1
- package/dist/types/commit/shared-llm.d.ts +1 -1
- package/dist/types/config/keybindings.d.ts +3 -3
- package/dist/types/config/model-registry.d.ts +17 -0
- package/dist/types/config/models-config-schema.d.ts +13 -1
- package/dist/types/config/models-config.d.ts +8 -2
- package/dist/types/config/settings-schema.d.ts +281 -58
- package/dist/types/edit/hashline/params.d.ts +1 -1
- package/dist/types/edit/modes/apply-patch.d.ts +1 -1
- package/dist/types/edit/modes/patch.d.ts +1 -1
- package/dist/types/edit/modes/replace.d.ts +1 -1
- package/dist/types/export/html/index.d.ts +2 -1
- package/dist/types/extensibility/custom-commands/types.d.ts +2 -2
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -2
- package/dist/types/extensibility/extensions/model-api.d.ts +17 -0
- package/dist/types/extensibility/extensions/runner.d.ts +3 -1
- package/dist/types/extensibility/extensions/types.d.ts +49 -3
- package/dist/types/extensibility/hooks/index.d.ts +2 -1
- package/dist/types/extensibility/hooks/types.d.ts +2 -2
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +9 -0
- package/dist/types/extensibility/plugins/loader.d.ts +11 -0
- package/dist/types/extensibility/shared-events.d.ts +1 -1
- package/dist/types/extensibility/skills.d.ts +10 -0
- package/dist/types/goals/guided-setup.d.ts +18 -0
- package/dist/types/goals/state.d.ts +1 -1
- package/dist/types/goals/tools/goal-tool.d.ts +1 -1
- package/dist/types/hindsight/transcript.d.ts +1 -1
- package/dist/types/index.d.ts +5 -0
- package/dist/types/internal-urls/local-protocol.d.ts +4 -2
- package/dist/types/lsp/types.d.ts +1 -1
- package/dist/types/main.d.ts +4 -3
- package/dist/types/mcp/manager.d.ts +8 -0
- package/dist/types/mcp/startup-events.d.ts +11 -0
- package/dist/types/memories/index.d.ts +7 -0
- package/dist/types/memory-backend/local-backend.d.ts +4 -3
- package/dist/types/mnemopi/config.d.ts +28 -0
- package/dist/types/modes/acp/acp-agent.d.ts +1 -2
- package/dist/types/modes/components/agent-hub.d.ts +6 -0
- package/dist/types/modes/components/assistant-message.d.ts +1 -2
- package/dist/types/modes/components/compaction-summary-message.d.ts +15 -1
- package/dist/types/modes/components/custom-editor.d.ts +39 -1
- package/dist/types/modes/components/custom-editor.test.d.ts +1 -0
- package/dist/types/modes/components/index.d.ts +1 -0
- package/dist/types/modes/components/logout-account-selector.d.ts +8 -0
- package/dist/types/modes/components/session-selector.d.ts +1 -1
- package/dist/types/modes/components/status-line/component.d.ts +9 -5
- package/dist/types/modes/components/status-line/types.d.ts +2 -1
- package/dist/types/modes/components/tool-execution.d.ts +26 -16
- package/dist/types/modes/components/transcript-container.d.ts +23 -2
- package/dist/types/modes/components/tree-selector.d.ts +1 -1
- package/dist/types/modes/components/usage-row.d.ts +3 -0
- package/dist/types/modes/controllers/command-controller.d.ts +2 -2
- package/dist/types/modes/controllers/event-controller.d.ts +0 -17
- package/dist/types/modes/controllers/input-controller.d.ts +14 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +3 -1
- package/dist/types/modes/gradient-highlight.d.ts +9 -4
- package/dist/types/modes/image-references.d.ts +6 -0
- package/dist/types/modes/interactive-mode.d.ts +27 -6
- package/dist/types/modes/magic-keywords.d.ts +13 -1
- package/dist/types/modes/rpc/rpc-mode.d.ts +35 -1
- package/dist/types/modes/rpc/rpc-types.d.ts +9 -1
- package/dist/types/modes/runtime-init.d.ts +4 -0
- package/dist/types/modes/theme/theme.d.ts +13 -2
- package/dist/types/modes/types.d.ts +8 -7
- package/dist/types/modes/utils/ui-helpers.d.ts +1 -1
- package/dist/types/registry/agent-registry.d.ts +17 -0
- package/dist/types/secrets/obfuscator.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +28 -35
- package/dist/types/session/agent-storage.d.ts +2 -1
- package/dist/types/session/indexed-session-storage.d.ts +3 -3
- package/dist/types/session/messages.d.ts +8 -10
- package/dist/types/session/session-context.d.ts +39 -0
- package/dist/types/session/session-entries.d.ts +159 -0
- package/dist/types/session/session-listing.d.ts +69 -0
- package/dist/types/session/session-loader.d.ts +16 -0
- package/dist/types/session/session-manager.d.ts +85 -462
- package/dist/types/session/session-migrations.d.ts +12 -0
- package/dist/types/session/session-paths.d.ts +25 -0
- package/dist/types/session/session-persistence.d.ts +8 -0
- package/dist/types/session/session-storage.d.ts +11 -7
- package/dist/types/session/snapcompact-inline.d.ts +12 -1
- package/dist/types/session/snapcompact-savings-journal.d.ts +46 -0
- package/dist/types/session/tool-choice-queue.d.ts +6 -6
- package/dist/types/slash-commands/helpers/logout.d.ts +15 -0
- package/dist/types/stt/asr-client.d.ts +90 -0
- package/dist/types/stt/asr-protocol.d.ts +97 -0
- package/dist/types/stt/asr-worker.d.ts +2 -0
- package/dist/types/stt/downloader.d.ts +38 -0
- package/dist/types/stt/endpointer.d.ts +59 -0
- package/dist/types/stt/index.d.ts +5 -1
- package/dist/types/stt/models.d.ts +120 -0
- package/dist/types/stt/recorder.d.ts +17 -0
- package/dist/types/stt/stt-controller.d.ts +6 -0
- package/dist/types/stt/transcriber.d.ts +5 -7
- package/dist/types/stt/wav.d.ts +29 -0
- package/dist/types/system-prompt.d.ts +4 -0
- package/dist/types/task/executor.d.ts +2 -0
- package/dist/types/task/index.d.ts +9 -1
- package/dist/types/task/types.d.ts +37 -1
- package/dist/types/tools/ask.d.ts +1 -1
- package/dist/types/tools/ast-edit.d.ts +1 -1
- package/dist/types/tools/ast-grep.d.ts +1 -1
- package/dist/types/tools/bash.d.ts +3 -3
- package/dist/types/tools/browser/cmux/cmux-tab.d.ts +202 -0
- package/dist/types/tools/browser/cmux/rpc.d.ts +70 -0
- package/dist/types/tools/browser/cmux/socket-client.d.ts +19 -0
- package/dist/types/tools/browser/registry.d.ts +16 -3
- package/dist/types/tools/browser/render.d.ts +2 -0
- package/dist/types/tools/browser/tab-protocol.d.ts +2 -0
- package/dist/types/tools/browser/tab-supervisor.d.ts +16 -4
- package/dist/types/tools/browser.d.ts +3 -1
- package/dist/types/tools/checkpoint.d.ts +1 -1
- package/dist/types/tools/debug.d.ts +1 -1
- package/dist/types/tools/eval-render.d.ts +1 -1
- package/dist/types/tools/eval.d.ts +1 -1
- package/dist/types/tools/find.d.ts +1 -1
- package/dist/types/tools/gh.d.ts +1 -1
- package/dist/types/tools/image-gen.d.ts +1 -1
- package/dist/types/tools/index.d.ts +14 -2
- package/dist/types/tools/inspect-image.d.ts +1 -1
- package/dist/types/tools/irc.d.ts +2 -1
- package/dist/types/tools/job.d.ts +1 -1
- package/dist/types/tools/learn.d.ts +51 -0
- package/dist/types/tools/manage-skill.d.ts +40 -0
- package/dist/types/tools/memory-edit.d.ts +1 -1
- package/dist/types/tools/memory-recall.d.ts +1 -1
- package/dist/types/tools/memory-reflect.d.ts +1 -1
- package/dist/types/tools/memory-retain.d.ts +1 -1
- package/dist/types/tools/plan-mode-guard.d.ts +10 -0
- package/dist/types/tools/read.d.ts +1 -1
- package/dist/types/tools/render-mermaid.d.ts +1 -1
- package/dist/types/tools/renderers.d.ts +7 -11
- package/dist/types/tools/resolve.d.ts +1 -1
- package/dist/types/tools/review.d.ts +1 -1
- package/dist/types/tools/search-tool-bm25.d.ts +1 -1
- package/dist/types/tools/search.d.ts +1 -1
- package/dist/types/tools/ssh.d.ts +2 -2
- package/dist/types/tools/todo.d.ts +2 -2
- package/dist/types/tools/tts.d.ts +26 -1
- package/dist/types/tools/write.d.ts +2 -2
- package/dist/types/tts/downloader.d.ts +20 -0
- package/dist/types/tts/index.d.ts +8 -0
- package/dist/types/tts/models.d.ts +82 -0
- package/dist/types/tts/player.d.ts +32 -0
- package/dist/types/tts/runtime.d.ts +6 -0
- package/dist/types/tts/streaming-player.d.ts +41 -0
- package/dist/types/tts/tts-client.d.ts +93 -0
- package/dist/types/tts/tts-protocol.d.ts +95 -0
- package/dist/types/tts/tts-worker.d.ts +2 -0
- package/dist/types/tts/vocalizer.d.ts +41 -0
- package/dist/types/tts/wav.d.ts +8 -0
- package/dist/types/utils/clipboard.d.ts +4 -3
- package/dist/types/utils/image-loading.d.ts +18 -1
- package/dist/types/utils/thinking-display.d.ts +17 -0
- package/dist/types/utils/tool-choice.d.ts +8 -0
- package/dist/types/utils/tools-manager.d.ts +2 -1
- package/dist/types/utils/tools-manager.test.d.ts +1 -0
- package/dist/types/web/scrapers/github.d.ts +1 -1
- package/dist/types/web/search/index.d.ts +1 -1
- package/package.json +17 -16
- package/src/async/job-manager.ts +49 -0
- package/src/autolearn/controller.ts +139 -0
- package/src/autolearn/managed-skills.ts +257 -0
- package/src/autoresearch/state.ts +1 -1
- package/src/autoresearch/storage.ts +2 -1
- package/src/autoresearch/tools/init-experiment.ts +1 -1
- package/src/autoresearch/tools/log-experiment.ts +1 -1
- package/src/autoresearch/tools/run-experiment.ts +1 -1
- package/src/autoresearch/tools/update-notes.ts +1 -1
- package/src/autoresearch/types.ts +1 -1
- package/src/cli/args.ts +56 -10
- package/src/cli/auth-gateway-cli.ts +1 -1
- package/src/cli/bench-cli.ts +1 -1
- package/src/cli/dry-balance-cli.ts +1 -1
- package/src/cli/models-cli.ts +427 -0
- package/src/cli/session-picker.ts +2 -1
- package/src/cli/setup-cli.ts +148 -47
- package/src/cli/setup-model-picker.ts +43 -0
- package/src/cli-commands.ts +3 -0
- package/src/cli.ts +45 -13
- package/src/collab/host.ts +10 -13
- package/src/collab/protocol.ts +1 -1
- package/src/commands/launch.ts +0 -3
- package/src/commands/models.ts +61 -0
- package/src/commands/say.ts +102 -0
- package/src/commands/setup.ts +1 -1
- package/src/commands/token.ts +89 -0
- package/src/commit/agentic/tools/analyze-file.ts +4 -1
- package/src/commit/agentic/tools/git-file-diff.ts +1 -1
- package/src/commit/agentic/tools/git-hunk.ts +1 -1
- package/src/commit/agentic/tools/git-overview.ts +1 -1
- package/src/commit/agentic/tools/propose-changelog.ts +1 -1
- package/src/commit/agentic/tools/propose-commit.ts +1 -1
- package/src/commit/agentic/tools/recent-commits.ts +1 -1
- package/src/commit/agentic/tools/schemas.ts +1 -1
- package/src/commit/agentic/tools/split-commit.ts +1 -1
- package/src/commit/analysis/summary.ts +1 -1
- package/src/commit/changelog/generate.ts +1 -1
- package/src/commit/shared-llm.ts +1 -1
- package/src/config/keybindings.ts +2 -2
- package/src/config/model-discovery.ts +11 -5
- package/src/config/model-registry.ts +79 -21
- package/src/config/model-resolver.ts +2 -2
- package/src/config/models-config-schema.ts +5 -2
- package/src/config/models-config.ts +2 -1
- package/src/config/settings-schema.ts +266 -32
- package/src/config/settings.ts +10 -0
- package/src/discovery/builtin.ts +23 -1
- package/src/discovery/claude-plugins.ts +44 -5
- package/src/discovery/helpers.ts +41 -1
- package/src/edit/hashline/params.ts +1 -1
- package/src/edit/modes/apply-patch.ts +1 -1
- package/src/edit/modes/patch.ts +1 -1
- package/src/edit/modes/replace.ts +1 -1
- package/src/eval/__tests__/budget-bridge.test.ts +1 -1
- package/src/eval/agent-bridge.ts +1 -1
- package/src/eval/completion-bridge.ts +1 -1
- package/src/eval/js/shared/prelude.txt +69 -17
- package/src/export/html/index.ts +3 -6
- package/src/export/html/template.js +24 -2
- package/src/export/html/tool-views.generated.js +2 -2
- package/src/extensibility/custom-commands/loader.ts +1 -1
- package/src/extensibility/custom-commands/types.ts +2 -2
- package/src/extensibility/custom-tools/loader.ts +1 -1
- package/src/extensibility/custom-tools/types.ts +2 -2
- package/src/extensibility/extensions/loader.ts +2 -2
- package/src/extensibility/extensions/model-api.ts +41 -0
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +54 -3
- package/src/extensibility/extensions/wrapper.ts +41 -5
- package/src/extensibility/hooks/index.ts +2 -1
- package/src/extensibility/hooks/loader.ts +1 -1
- package/src/extensibility/hooks/types.ts +2 -2
- package/src/extensibility/plugins/legacy-pi-compat.ts +43 -13
- package/src/extensibility/plugins/loader.ts +30 -19
- package/src/extensibility/plugins/manager.ts +221 -90
- package/src/extensibility/shared-events.ts +1 -1
- package/src/extensibility/skills.ts +101 -5
- package/src/goals/guided-setup.ts +133 -0
- package/src/goals/state.ts +1 -1
- package/src/goals/tools/goal-tool.ts +1 -1
- package/src/hindsight/transcript.ts +1 -1
- package/src/index.ts +5 -0
- package/src/internal-urls/docs-index.generated.ts +13 -10
- package/src/internal-urls/history-protocol.ts +1 -1
- package/src/internal-urls/local-protocol.ts +29 -7
- package/src/lsp/types.ts +1 -1
- package/src/main.ts +27 -32
- package/src/mcp/config-writer.ts +7 -3
- package/src/mcp/manager.ts +11 -0
- package/src/mcp/startup-events.ts +21 -0
- package/src/mcp/transports/stdio.ts +2 -1
- package/src/memories/index.ts +149 -12
- package/src/memories/storage.ts +2 -1
- package/src/memory-backend/local-backend.ts +11 -5
- package/src/mnemopi/backend.ts +1 -0
- package/src/mnemopi/config.ts +112 -12
- package/src/modes/acp/acp-agent.ts +8 -53
- package/src/modes/acp/acp-event-mapper.ts +5 -1
- package/src/modes/components/agent-hub.ts +51 -5
- package/src/modes/components/assistant-message.ts +12 -44
- package/src/modes/components/compaction-summary-message.ts +125 -26
- package/src/modes/components/custom-editor.test.ts +96 -0
- package/src/modes/components/custom-editor.ts +164 -8
- package/src/modes/components/index.ts +1 -0
- package/src/modes/components/logout-account-selector.ts +130 -0
- package/src/modes/components/mcp-add-wizard.ts +1 -1
- package/src/modes/components/model-selector.ts +2 -2
- package/src/modes/components/session-selector.ts +1 -1
- package/src/modes/components/settings-defs.ts +7 -0
- package/src/modes/components/status-line/component.ts +54 -157
- package/src/modes/components/status-line/segments.ts +1 -1
- package/src/modes/components/status-line/types.ts +2 -1
- package/src/modes/components/tool-execution.ts +82 -43
- package/src/modes/components/transcript-container.ts +70 -1
- package/src/modes/components/tree-selector.ts +1 -1
- package/src/modes/components/usage-row.ts +18 -0
- package/src/modes/components/user-message.ts +4 -2
- package/src/modes/controllers/command-controller.ts +14 -16
- package/src/modes/controllers/event-controller.ts +101 -73
- package/src/modes/controllers/extension-ui-controller.ts +6 -0
- package/src/modes/controllers/input-controller.ts +311 -57
- package/src/modes/controllers/mcp-command-controller.ts +44 -3
- package/src/modes/controllers/selector-controller.ts +68 -12
- package/src/modes/controllers/streaming-reveal.ts +4 -3
- package/src/modes/gradient-highlight.ts +21 -9
- package/src/modes/image-references.ts +20 -0
- package/src/modes/interactive-mode.ts +288 -48
- package/src/modes/magic-keywords.ts +27 -5
- package/src/modes/rpc/rpc-mode.ts +146 -14
- package/src/modes/rpc/rpc-subagents.ts +2 -2
- package/src/modes/rpc/rpc-types.ts +8 -2
- package/src/modes/runtime-init.ts +28 -3
- package/src/modes/theme/theme.ts +99 -51
- package/src/modes/types.ts +6 -7
- package/src/modes/utils/hotkeys-markdown.ts +1 -1
- package/src/modes/utils/ui-helpers.ts +36 -7
- package/src/priority.json +5 -1
- package/src/prompts/agents/task.md +1 -0
- package/src/prompts/goals/guided-goal-interview.md +8 -0
- package/src/prompts/goals/guided-goal-system.md +12 -0
- package/src/prompts/memories/read-path.md +6 -0
- package/src/prompts/system/autolearn-guidance-learn.md +1 -0
- package/src/prompts/system/autolearn-guidance.md +7 -0
- package/src/prompts/system/autolearn-nudge.md +3 -0
- package/src/prompts/system/eager-task.md +7 -0
- package/src/prompts/system/eager-todo.md +11 -6
- package/src/prompts/system/empty-stop-retry.md +4 -6
- package/src/prompts/system/subagent-system-prompt.md +4 -0
- package/src/prompts/system/system-prompt.md +10 -5
- package/src/prompts/system/title-marker-instruction.md +1 -0
- package/src/prompts/system/title-system-marker.md +16 -0
- package/src/prompts/tools/job.md +1 -0
- package/src/prompts/tools/learn.md +7 -0
- package/src/prompts/tools/manage-skill.md +9 -0
- package/src/prompts/tools/task.md +3 -0
- package/src/registry/agent-registry.ts +30 -0
- package/src/sdk.ts +103 -43
- package/src/secrets/obfuscator.ts +1 -1
- package/src/session/agent-session.ts +331 -318
- package/src/session/agent-storage.ts +18 -9
- package/src/session/history-storage.ts +3 -2
- package/src/session/indexed-session-storage.ts +7 -10
- package/src/session/messages.ts +9 -11
- package/src/session/session-context.ts +352 -0
- package/src/session/session-dump-format.ts +4 -2
- package/src/session/session-entries.ts +194 -0
- package/src/session/session-listing.ts +588 -0
- package/src/session/session-loader.ts +106 -0
- package/src/session/session-manager.ts +968 -3064
- package/src/session/session-migrations.ts +78 -0
- package/src/session/session-paths.ts +193 -0
- package/src/session/session-persistence.ts +131 -0
- package/src/session/session-storage.ts +91 -30
- package/src/session/snapcompact-inline.ts +21 -1
- package/src/session/snapcompact-savings-journal.ts +113 -0
- package/src/session/tool-choice-queue.ts +23 -11
- package/src/slash-commands/builtin-registry.ts +40 -4
- package/src/slash-commands/helpers/logout.ts +88 -0
- package/src/stt/asr-client.ts +520 -0
- package/src/stt/asr-protocol.ts +65 -0
- package/src/stt/asr-worker.ts +790 -0
- package/src/stt/downloader.ts +107 -47
- package/src/stt/endpointer.ts +259 -0
- package/src/stt/index.ts +5 -1
- package/src/stt/models.ts +150 -0
- package/src/stt/recorder.ts +247 -60
- package/src/stt/stt-controller.ts +201 -22
- package/src/stt/transcriber.ts +37 -68
- package/src/stt/wav.ts +173 -0
- package/src/system-prompt.ts +8 -0
- package/src/task/agents.ts +1 -2
- package/src/task/executor.ts +49 -15
- package/src/task/index.ts +60 -6
- package/src/task/render.ts +83 -8
- package/src/task/types.ts +54 -1
- package/src/tools/ask.ts +9 -1
- package/src/tools/ast-edit.ts +1 -1
- package/src/tools/ast-grep.ts +1 -1
- package/src/tools/bash.ts +5 -4
- package/src/tools/browser/cmux/cmux-tab.ts +1264 -0
- package/src/tools/browser/cmux/rpc.ts +156 -0
- package/src/tools/browser/cmux/socket-client.ts +309 -0
- package/src/tools/browser/registry.ts +37 -3
- package/src/tools/browser/render.ts +6 -1
- package/src/tools/browser/tab-protocol.ts +2 -0
- package/src/tools/browser/tab-supervisor.ts +189 -18
- package/src/tools/browser/tab-worker.ts +1 -1
- package/src/tools/browser.ts +16 -1
- package/src/tools/checkpoint.ts +1 -1
- package/src/tools/debug.ts +1 -1
- package/src/tools/eval-render.ts +4 -3
- package/src/tools/eval.ts +11 -6
- package/src/tools/fetch.ts +13 -2
- package/src/tools/find.ts +1 -1
- package/src/tools/gh.ts +1 -1
- package/src/tools/github-cache.ts +2 -1
- package/src/tools/image-gen.ts +1 -1
- package/src/tools/index.ts +43 -5
- package/src/tools/inspect-image.ts +3 -1
- package/src/tools/irc.ts +11 -3
- package/src/tools/job.ts +15 -3
- package/src/tools/learn.ts +144 -0
- package/src/tools/manage-skill.ts +104 -0
- package/src/tools/memory-edit.ts +1 -1
- package/src/tools/memory-recall.ts +1 -1
- package/src/tools/memory-reflect.ts +1 -1
- package/src/tools/memory-retain.ts +1 -1
- package/src/tools/plan-mode-guard.ts +53 -19
- package/src/tools/read.ts +8 -2
- package/src/tools/render-mermaid.ts +1 -1
- package/src/tools/renderers.ts +7 -11
- package/src/tools/report-tool-issue.ts +3 -2
- package/src/tools/resolve.ts +1 -1
- package/src/tools/review.ts +1 -1
- package/src/tools/search-tool-bm25.ts +1 -1
- package/src/tools/search.ts +1 -1
- package/src/tools/ssh.ts +5 -4
- package/src/tools/todo.ts +2 -2
- package/src/tools/tts.ts +204 -93
- package/src/tools/write.ts +19 -3
- package/src/tts/downloader.ts +64 -0
- package/src/tts/index.ts +8 -0
- package/src/tts/models.ts +137 -0
- package/src/tts/player.ts +137 -0
- package/src/tts/runtime.ts +21 -0
- package/src/tts/streaming-player.ts +266 -0
- package/src/tts/tts-client.ts +647 -0
- package/src/tts/tts-protocol.ts +60 -0
- package/src/tts/tts-worker.ts +497 -0
- package/src/tts/vocalizer.ts +162 -0
- package/src/tts/wav.ts +58 -0
- package/src/utils/clipboard.ts +35 -18
- package/src/utils/image-loading.ts +35 -4
- package/src/utils/thinking-display.ts +37 -0
- package/src/utils/title-generator.ts +48 -5
- package/src/utils/tool-choice.ts +16 -0
- package/src/utils/tools-manager.test.ts +25 -0
- package/src/utils/tools-manager.ts +19 -1
- package/src/web/scrapers/github.ts +96 -0
- package/src/web/search/index.ts +14 -1
- package/src/web/search/providers/searxng.ts +13 -1
- package/dist/types/cli/list-models.d.ts +0 -30
- package/dist/types/stt/setup.d.ts +0 -18
- package/src/cli/list-models.ts +0 -194
- package/src/stt/setup.ts +0 -52
- package/src/stt/transcribe.py +0 -70
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import type { AgentRef } from "../registry/agent-registry";
|
|
13
13
|
import { AgentRegistry } from "../registry/agent-registry";
|
|
14
14
|
import { formatSessionHistoryMarkdown } from "../session/session-history-format";
|
|
15
|
-
import { loadSessionMessagesReadOnly } from "../session/session-
|
|
15
|
+
import { loadSessionMessagesReadOnly } from "../session/session-loader";
|
|
16
16
|
import type { InternalResource, InternalUrl, ProtocolHandler, UrlCompletion } from "./types";
|
|
17
17
|
|
|
18
18
|
/** Humanize a last-activity timestamp as `Ns/Nm/Nh/Nd ago`. */
|
|
@@ -26,6 +26,20 @@ function toLocalValidationError(error: unknown): Error {
|
|
|
26
26
|
const message = error instanceof Error ? error.message : String(error);
|
|
27
27
|
return new Error(message.replace("skill://", "local://"));
|
|
28
28
|
}
|
|
29
|
+
const WINDOWS_LOCAL_ROOT_MAX_CHARS = 180;
|
|
30
|
+
|
|
31
|
+
function safeSessionId(options: LocalProtocolOptions): string {
|
|
32
|
+
const raw = options.getSessionId?.() ?? "session";
|
|
33
|
+
const safe = raw.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
34
|
+
return safe.length > 0 ? safe : "session";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function shortLocalRoot(options: LocalProtocolOptions): string {
|
|
38
|
+
// Derive the short root from the stable session id, never the artifact path,
|
|
39
|
+
// so `SessionManager.moveTo()` and the resume-after-move flow keep finding
|
|
40
|
+
// the same `local://` directory the session wrote pre-move.
|
|
41
|
+
return path.join(os.tmpdir(), "omp-local", safeSessionId(options));
|
|
42
|
+
}
|
|
29
43
|
|
|
30
44
|
function getContentType(filePath: string): InternalResource["contentType"] {
|
|
31
45
|
const ext = path.extname(filePath).toLowerCase();
|
|
@@ -108,20 +122,28 @@ function extractRelativePath(url: InternalUrl): string {
|
|
|
108
122
|
return decoded;
|
|
109
123
|
}
|
|
110
124
|
|
|
111
|
-
|
|
125
|
+
/** Resolve the session-scoped local:// root, shortening long Windows artifact paths before writes hit MAX_PATH. */
|
|
126
|
+
export function resolveLocalRoot(options: LocalProtocolOptions, platform: NodeJS.Platform = process.platform): string {
|
|
112
127
|
const artifactsDir = options.getArtifactsDir?.();
|
|
113
128
|
if (artifactsDir) {
|
|
114
|
-
|
|
129
|
+
const candidate = path.resolve(artifactsDir, "local");
|
|
130
|
+
if (platform === "win32" && candidate.length >= WINDOWS_LOCAL_ROOT_MAX_CHARS) {
|
|
131
|
+
return shortLocalRoot(options);
|
|
132
|
+
}
|
|
133
|
+
return candidate;
|
|
115
134
|
}
|
|
116
135
|
|
|
117
|
-
|
|
118
|
-
const safeSessionId = sessionId.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
119
|
-
return path.join(os.tmpdir(), "omp-local", safeSessionId);
|
|
136
|
+
return path.join(os.tmpdir(), "omp-local", safeSessionId(options));
|
|
120
137
|
}
|
|
121
138
|
|
|
122
|
-
|
|
139
|
+
/** Resolve a local:// URL to an on-disk path under the active session's local root. */
|
|
140
|
+
export function resolveLocalUrlToPath(
|
|
141
|
+
input: string | InternalUrl,
|
|
142
|
+
options: LocalProtocolOptions,
|
|
143
|
+
platform: NodeJS.Platform = process.platform,
|
|
144
|
+
): string {
|
|
123
145
|
const url = typeof input === "string" ? parseLocalUrl(input) : input;
|
|
124
|
-
const localRoot = path.resolve(resolveLocalRoot(options));
|
|
146
|
+
const localRoot = path.resolve(resolveLocalRoot(options, platform));
|
|
125
147
|
const relativePath = extractRelativePath(url);
|
|
126
148
|
|
|
127
149
|
if (!relativePath) {
|
package/src/lsp/types.ts
CHANGED
package/src/main.ts
CHANGED
|
@@ -21,11 +21,10 @@ import {
|
|
|
21
21
|
} from "@oh-my-pi/pi-utils";
|
|
22
22
|
import chalk from "chalk";
|
|
23
23
|
import { reset as resetCapabilities } from "./capability";
|
|
24
|
-
import type
|
|
24
|
+
import { type Args, reportUnrecognizedFlags } from "./cli/args";
|
|
25
25
|
import { applyExtensionFlags, type ExtensionFlagSink } from "./cli/extension-flags";
|
|
26
26
|
import { processFileArguments } from "./cli/file-processor";
|
|
27
27
|
import { buildInitialMessage } from "./cli/initial-message";
|
|
28
|
-
import { runListModelsCommand } from "./cli/list-models";
|
|
29
28
|
import { selectSession } from "./cli/session-picker";
|
|
30
29
|
import { applyStartupCwd } from "./cli/startup-cwd";
|
|
31
30
|
import { findConfigFile } from "./config";
|
|
@@ -65,7 +64,8 @@ import {
|
|
|
65
64
|
} from "./sdk";
|
|
66
65
|
import type { AgentSession } from "./session/agent-session";
|
|
67
66
|
import type { AuthStorage } from "./session/auth-storage";
|
|
68
|
-
import { resolveResumableSession, type SessionInfo
|
|
67
|
+
import { resolveResumableSession, type SessionInfo } from "./session/session-listing";
|
|
68
|
+
import { SessionManager } from "./session/session-manager";
|
|
69
69
|
import { executeBuiltinSlashCommand } from "./slash-commands/builtin-registry";
|
|
70
70
|
import { discoverTitleSystemPromptFile, resolvePromptInput } from "./system-prompt";
|
|
71
71
|
import { initTelemetryExport, isTelemetryExportEnabled } from "./telemetry-export";
|
|
@@ -265,7 +265,7 @@ export async function submitInteractiveInput(
|
|
|
265
265
|
InteractiveMode,
|
|
266
266
|
"markPendingSubmissionStarted" | "finishPendingSubmission" | "showError" | "checkShutdownRequested"
|
|
267
267
|
>,
|
|
268
|
-
session: Pick<AgentSession, "prompt" | "promptCustomMessage">,
|
|
268
|
+
session: Pick<AgentSession, "prompt" | "promptCustomMessage" | "isStreaming">,
|
|
269
269
|
input: SubmittedUserInput,
|
|
270
270
|
): Promise<void> {
|
|
271
271
|
if (input.cancelled) {
|
|
@@ -274,22 +274,32 @@ export async function submitInteractiveInput(
|
|
|
274
274
|
|
|
275
275
|
try {
|
|
276
276
|
using _keepalive = new EventLoopKeepalive();
|
|
277
|
+
const streamingBehavior = session.isStreaming ? ("followUp" as const) : undefined;
|
|
277
278
|
// Continue shortcuts submit an already-started synthetic developer prompt with
|
|
278
279
|
// no optimistic user message.
|
|
279
280
|
if (!input.started && !mode.markPendingSubmissionStarted(input)) {
|
|
280
281
|
return;
|
|
281
282
|
}
|
|
282
283
|
if (input.customType) {
|
|
283
|
-
|
|
284
|
+
const message = {
|
|
284
285
|
customType: input.customType,
|
|
285
286
|
content: input.text,
|
|
286
287
|
display: input.display ?? false,
|
|
287
|
-
attribution: "agent",
|
|
288
|
-
}
|
|
288
|
+
attribution: "agent" as const,
|
|
289
|
+
};
|
|
290
|
+
await (streamingBehavior
|
|
291
|
+
? session.promptCustomMessage(message, { streamingBehavior })
|
|
292
|
+
: session.promptCustomMessage(message));
|
|
289
293
|
} else if (input.synthetic) {
|
|
294
|
+
// Synthetic continue shortcuts are hidden developer prompts. The streaming
|
|
295
|
+
// queue (#queueUserMessage) only carries user-attributed messages, so we do
|
|
296
|
+
// NOT pass streamingBehavior here: queueing would silently demote the
|
|
297
|
+
// developer directive to a visible user message. A synthetic submit while
|
|
298
|
+
// streaming keeps its prior behavior (rejected as busy) rather than changing
|
|
299
|
+
// its role.
|
|
290
300
|
await session.prompt(input.text, { synthetic: true, expandPromptTemplates: false });
|
|
291
301
|
} else {
|
|
292
|
-
await session.prompt(input.text, { images: input.images });
|
|
302
|
+
await session.prompt(input.text, { images: input.images, ...(streamingBehavior && { streamingBehavior }) });
|
|
293
303
|
}
|
|
294
304
|
} catch (error: unknown) {
|
|
295
305
|
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
@@ -920,30 +930,6 @@ export async function runRootCommand(
|
|
|
920
930
|
process.exit(0);
|
|
921
931
|
}
|
|
922
932
|
|
|
923
|
-
if (parsedArgs.listModels !== undefined) {
|
|
924
|
-
const settingsInstance = await logger.time("settings:init:list-models", Settings.init, {
|
|
925
|
-
cwd: getProjectDir(),
|
|
926
|
-
configFiles: parsedArgs.config,
|
|
927
|
-
});
|
|
928
|
-
await modelRegistry.refresh("online");
|
|
929
|
-
const cliExtensionPaths = parsedArgs.noExtensions
|
|
930
|
-
? []
|
|
931
|
-
: [...(parsedArgs.extensions ?? []), ...(parsedArgs.hooks ?? [])];
|
|
932
|
-
const settingsExtensions = settingsInstance.get("extensions") ?? [];
|
|
933
|
-
const disabledExtensionIds = settingsInstance.get("disabledExtensions") ?? [];
|
|
934
|
-
const searchPattern = typeof parsedArgs.listModels === "string" ? parsedArgs.listModels : undefined;
|
|
935
|
-
await runListModelsCommand({
|
|
936
|
-
modelRegistry,
|
|
937
|
-
cwd: getProjectDir(),
|
|
938
|
-
additionalExtensionPaths: cliExtensionPaths,
|
|
939
|
-
settingsExtensions,
|
|
940
|
-
disabledExtensionIds,
|
|
941
|
-
disableExtensionDiscovery: Boolean(parsedArgs.noExtensions),
|
|
942
|
-
searchPattern,
|
|
943
|
-
});
|
|
944
|
-
process.exit(0);
|
|
945
|
-
}
|
|
946
|
-
|
|
947
933
|
if (parsedArgs.export) {
|
|
948
934
|
let result: string;
|
|
949
935
|
try {
|
|
@@ -1222,6 +1208,15 @@ export async function runRootCommand(
|
|
|
1222
1208
|
},
|
|
1223
1209
|
};
|
|
1224
1210
|
const initialArgs = applyExtensionFlags(extensionFlagSink, rawArgs) ?? parsedArgs;
|
|
1211
|
+
// Fail fast on stale/typo flags (e.g. `omp --list-models`) now that we
|
|
1212
|
+
// know the real extension flag set. Without this check the unrecognized
|
|
1213
|
+
// token gets silently consumed and any following positional leaks as the
|
|
1214
|
+
// initial prompt — kicking off a real LLM session, MCP connection, and
|
|
1215
|
+
// tool calls (issue #2459). Exit code 2 matches the conventional
|
|
1216
|
+
// "command line usage error" convention.
|
|
1217
|
+
if (reportUnrecognizedFlags(initialArgs)) {
|
|
1218
|
+
process.exit(2);
|
|
1219
|
+
}
|
|
1225
1220
|
const processedFiles =
|
|
1226
1221
|
initialArgs.fileArgs.length > 0
|
|
1227
1222
|
? await logger.time("processFileArguments", () =>
|
package/src/mcp/config-writer.ts
CHANGED
|
@@ -67,9 +67,13 @@ export function validateServerName(name: string): string | undefined {
|
|
|
67
67
|
if (name.length > 100) {
|
|
68
68
|
return "Server name is too long (max 100 characters)";
|
|
69
69
|
}
|
|
70
|
-
// Check for invalid characters
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
// Check for invalid characters. Colon is allowed so namespaced plugin servers
|
|
71
|
+
// (e.g. "cloudflare:cloudflare-api" from a Claude Code marketplace plugin) can
|
|
72
|
+
// be persisted: the runtime already accepts colons in server names (tool names
|
|
73
|
+
// sanitize them via createMCPToolName) and `/mcp reauth` writes such names back
|
|
74
|
+
// as a user-config override that shadows the discovered entry.
|
|
75
|
+
if (!/^[a-zA-Z0-9_.:-]+$/.test(name)) {
|
|
76
|
+
return "Server name can only contain letters, numbers, dash, underscore, dot, and colon";
|
|
73
77
|
}
|
|
74
78
|
return undefined;
|
|
75
79
|
}
|
package/src/mcp/manager.ts
CHANGED
|
@@ -643,6 +643,17 @@ export class MCPManager {
|
|
|
643
643
|
return this.#sources.get(name) ?? this.#connections.get(name)?._source;
|
|
644
644
|
}
|
|
645
645
|
|
|
646
|
+
/**
|
|
647
|
+
* Get the preserved (pre-auth) config for a known server — whether currently
|
|
648
|
+
* connected or merely discovered (a connect was attempted but may have failed,
|
|
649
|
+
* e.g. an OAuth server that has not been authorized yet). Mirrors the
|
|
650
|
+
* reconnect lookup at {@link reconnectServer} so callers like `/mcp reauth`
|
|
651
|
+
* can recover a discovered server's config without re-reading config files.
|
|
652
|
+
*/
|
|
653
|
+
getServerConfig(name: string): MCPServerConfig | undefined {
|
|
654
|
+
return this.#connections.get(name)?.config ?? this.#serverConfigs.get(name);
|
|
655
|
+
}
|
|
656
|
+
|
|
646
657
|
/**
|
|
647
658
|
* Wait for a connection to complete (or fail).
|
|
648
659
|
*/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const MCP_CONNECTING_EVENT_CHANNEL = "mcp:connecting";
|
|
2
|
+
|
|
3
|
+
export type McpConnectingEvent = { serverNames: string[] };
|
|
4
|
+
|
|
5
|
+
export function formatMCPConnectingMessage(serverNames: string[]): string {
|
|
6
|
+
return `Connecting to MCP servers: ${serverNames.join(", ")}…`;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Runtime validator for the cross-module event payload. The event bus is
|
|
11
|
+
* untyped at runtime, so the subscriber verifies the shape before formatting
|
|
12
|
+
* rather than trusting a cast — a malformed emit is ignored instead of throwing.
|
|
13
|
+
*/
|
|
14
|
+
export function isMcpConnectingEvent(data: unknown): data is McpConnectingEvent {
|
|
15
|
+
return (
|
|
16
|
+
typeof data === "object" &&
|
|
17
|
+
data !== null &&
|
|
18
|
+
Array.isArray((data as { serverNames?: unknown }).serverNames) &&
|
|
19
|
+
(data as { serverNames: unknown[] }).serverNames.every(name => typeof name === "string")
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -589,7 +589,8 @@ export class StdioTransport implements MCPTransport {
|
|
|
589
589
|
}
|
|
590
590
|
|
|
591
591
|
if (this.#readLoop) {
|
|
592
|
-
await
|
|
592
|
+
// Do not block/await the read loop as it can hang indefinitely in some environments
|
|
593
|
+
this.#readLoop.catch(() => {});
|
|
593
594
|
this.#readLoop = null;
|
|
594
595
|
}
|
|
595
596
|
}
|
package/src/memories/index.ts
CHANGED
|
@@ -5,11 +5,12 @@ import * as path from "node:path";
|
|
|
5
5
|
import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
6
6
|
import { type ApiKey, completeSimple, Effort, type Model } from "@oh-my-pi/pi-ai";
|
|
7
7
|
import { clampThinkingLevelForModel } from "@oh-my-pi/pi-catalog/model-thinking";
|
|
8
|
-
import { getAgentDbPath, getMemoriesDir, logger, parseJsonlLenient, prompt } from "@oh-my-pi/pi-utils";
|
|
8
|
+
import { getAgentDbPath, getMemoriesDir, isEnoent, logger, parseJsonlLenient, prompt } from "@oh-my-pi/pi-utils";
|
|
9
9
|
|
|
10
10
|
import type { ModelRegistry } from "../config/model-registry";
|
|
11
11
|
import { getModelMatchPreferences, resolveModelRoleValue } from "../config/model-resolver";
|
|
12
12
|
import type { Settings } from "../config/settings";
|
|
13
|
+
import type { MemoryBackendSaveInput, MemoryBackendSaveResult } from "../memory-backend/types";
|
|
13
14
|
import consolidationTemplate from "../prompts/memories/consolidation.md" with { type: "text" };
|
|
14
15
|
import consolidationSystemTemplate from "../prompts/memories/consolidation_system.md" with { type: "text" };
|
|
15
16
|
import readPathTemplate from "../prompts/memories/read-path.md" with { type: "text" };
|
|
@@ -156,22 +157,31 @@ export async function buildMemoryToolDeveloperInstructions(
|
|
|
156
157
|
const cfg = loadMemoryConfig(settings);
|
|
157
158
|
if (!cfg.enabled) return undefined;
|
|
158
159
|
const memoryRoot = getMemoryRoot(agentDir, settings.getCwd());
|
|
159
|
-
const summaryPath = path.join(memoryRoot, "memory_summary.md");
|
|
160
160
|
|
|
161
|
-
let
|
|
161
|
+
let summary = "";
|
|
162
162
|
try {
|
|
163
|
-
|
|
163
|
+
summary = (await Bun.file(path.join(memoryRoot, "memory_summary.md")).text()).trim();
|
|
164
164
|
} catch {
|
|
165
|
-
|
|
165
|
+
// Missing or unreadable summary — injection is best-effort; fall through
|
|
166
|
+
// so any captured lessons still surface on their own.
|
|
166
167
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
|
|
168
|
+
const learned = await readLearnedLessons(memoryRoot);
|
|
169
|
+
if (!summary && !learned) return undefined;
|
|
170
|
+
|
|
171
|
+
const summaryOut = summary ? truncateByApproxTokens(summary, cfg.summaryInjectionTokenLimit).trim() : "";
|
|
172
|
+
// Lessons share ONE injection budget with the summary so the combined block
|
|
173
|
+
// stays within `summaryInjectionTokenLimit` (~4 chars/token, matching
|
|
174
|
+
// truncateByApproxTokens). With no summary, lessons get the whole budget.
|
|
175
|
+
// Clamp to 0: truncateByApproxTokens appends a marker, so a truncated summary
|
|
176
|
+
// can exceed `limit * 4` chars and drive the remainder negative — when the
|
|
177
|
+
// summary already fills the budget, lessons are simply dropped.
|
|
178
|
+
const learnedBudget = Math.max(0, cfg.summaryInjectionTokenLimit - Math.ceil(summaryOut.length / 4));
|
|
179
|
+
const learnedOut = learned && learnedBudget > 0 ? truncateByApproxTokens(learned, learnedBudget).trim() : "";
|
|
180
|
+
if (!summaryOut && !learnedOut) return undefined;
|
|
172
181
|
|
|
173
182
|
return prompt.render(readPathTemplate, {
|
|
174
|
-
memory_summary:
|
|
183
|
+
memory_summary: summaryOut,
|
|
184
|
+
learned: learnedOut,
|
|
175
185
|
});
|
|
176
186
|
}
|
|
177
187
|
|
|
@@ -982,6 +992,12 @@ function redactSecrets(input: string): string {
|
|
|
982
992
|
/(?:sk|pk|rk|tok|key|secret|token|password)[-_A-Za-z0-9]{12,}/g,
|
|
983
993
|
/[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}/g,
|
|
984
994
|
/(?:AKIA|ASIA)[A-Z0-9]{16}/g,
|
|
995
|
+
// Common provider token prefixes (GitHub, npm, Slack, Google).
|
|
996
|
+
/(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{20,}/g,
|
|
997
|
+
/github_pat_[A-Za-z0-9_]{20,}/g,
|
|
998
|
+
/npm_[A-Za-z0-9]{30,}/g,
|
|
999
|
+
/xox[baprs]-[A-Za-z0-9-]{10,}/g,
|
|
1000
|
+
/AIza[A-Za-z0-9_-]{30,}/g,
|
|
985
1001
|
];
|
|
986
1002
|
for (const pattern of patterns) {
|
|
987
1003
|
out = out.replace(pattern, "[REDACTED]");
|
|
@@ -1071,7 +1087,9 @@ function truncateByApproxTokens(text: string, tokenLimit: number): string {
|
|
|
1071
1087
|
|
|
1072
1088
|
function computeModelTokenBudget(model: Model, config: MemoryRuntimeConfig): number {
|
|
1073
1089
|
const maxTokens =
|
|
1074
|
-
Number.isFinite(model.contextWindow) && model.contextWindow > 0
|
|
1090
|
+
model.contextWindow !== null && Number.isFinite(model.contextWindow) && model.contextWindow > 0
|
|
1091
|
+
? model.contextWindow
|
|
1092
|
+
: config.fallbackTokenLimit;
|
|
1075
1093
|
return Math.max(2048, Math.floor(maxTokens));
|
|
1076
1094
|
}
|
|
1077
1095
|
|
|
@@ -1119,6 +1137,125 @@ export function getMemoryRoot(agentDir: string, cwd: string): string {
|
|
|
1119
1137
|
return path.join(getMemoriesDir(agentDir), encodeProjectPath(cwd));
|
|
1120
1138
|
}
|
|
1121
1139
|
|
|
1140
|
+
/**
|
|
1141
|
+
* Filename of the captured-lessons file under a project's memory root.
|
|
1142
|
+
*
|
|
1143
|
+
* Written by the `learn` tool via {@link saveLearnedLesson} and read back by
|
|
1144
|
+
* {@link buildMemoryToolDeveloperInstructions}. Deliberately distinct from the
|
|
1145
|
+
* consolidation artifacts (`MEMORY.md`, `memory_summary.md`, `skills/`) so a
|
|
1146
|
+
* consolidation pass never clobbers manually captured lessons.
|
|
1147
|
+
*/
|
|
1148
|
+
const LEARNED_LESSONS_FILE = "learned.md";
|
|
1149
|
+
/** Newest-first cap on retained lessons, bounding file growth by entry count. */
|
|
1150
|
+
const MAX_LEARNED_LESSONS = 100;
|
|
1151
|
+
/** Per-field char caps so a single huge capture can't bloat learned.md. */
|
|
1152
|
+
const MAX_LEARNED_CONTENT_CHARS = 2000;
|
|
1153
|
+
const MAX_LEARNED_CONTEXT_CHARS = 400;
|
|
1154
|
+
|
|
1155
|
+
/**
|
|
1156
|
+
* Strip prompt-injection vectors from a single line of lesson text: control/
|
|
1157
|
+
* format chars, angle brackets (`</skills>`), backticks, and `~~~` fences, then
|
|
1158
|
+
* collapse whitespace. Applied on BOTH write and read (the block renders
|
|
1159
|
+
* unescaped into the system prompt), mirroring managed-skill descriptions.
|
|
1160
|
+
*/
|
|
1161
|
+
function neutralizeInjection(text: string): string {
|
|
1162
|
+
return text
|
|
1163
|
+
.replace(/[\p{Cc}\p{Cf}]/gu, " ")
|
|
1164
|
+
.replace(/[<>`]/g, "")
|
|
1165
|
+
.replace(/~{2,}/g, "~")
|
|
1166
|
+
.replace(/\s+/g, " ")
|
|
1167
|
+
.trim();
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
/** Slice to `maxChars`, dropping a trailing unpaired high surrogate. */
|
|
1171
|
+
function boundChars(text: string, maxChars: number): string {
|
|
1172
|
+
if (text.length <= maxChars) return text;
|
|
1173
|
+
const sliced = text.slice(0, maxChars);
|
|
1174
|
+
return /[\uD800-\uDBFF]$/.test(sliced) ? sliced.slice(0, -1) : sliced;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
/**
|
|
1178
|
+
* Normalize one lesson field for storage: neutralize injection delimiters
|
|
1179
|
+
* FIRST, then redact secrets (so delimiter stripping can't reassemble a token
|
|
1180
|
+
* the redactor would have caught), then bound the length.
|
|
1181
|
+
*/
|
|
1182
|
+
function normalizeLearnedText(text: string, maxChars: number): string {
|
|
1183
|
+
return boundChars(redactSecrets(neutralizeInjection(text)).trim(), maxChars);
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
/** Per-path write chains serializing `learned.md` read-modify-write. */
|
|
1187
|
+
const learnedWriteChains = new Map<string, Promise<unknown>>();
|
|
1188
|
+
|
|
1189
|
+
/**
|
|
1190
|
+
* Append one lesson to the project's `learned.md` (newest-first, deduped,
|
|
1191
|
+
* capped, secret-redacted, injection-neutralized). The file backs the `learn`
|
|
1192
|
+
* tool when `memory.backend` is `local`.
|
|
1193
|
+
*/
|
|
1194
|
+
export async function saveLearnedLesson(
|
|
1195
|
+
agentDir: string,
|
|
1196
|
+
cwd: string,
|
|
1197
|
+
input: MemoryBackendSaveInput,
|
|
1198
|
+
): Promise<MemoryBackendSaveResult> {
|
|
1199
|
+
const content = normalizeLearnedText(input.content, MAX_LEARNED_CONTENT_CHARS);
|
|
1200
|
+
if (!content) {
|
|
1201
|
+
return { backend: "local", stored: 0, message: "Empty lesson; nothing stored." };
|
|
1202
|
+
}
|
|
1203
|
+
const context = input.context ? normalizeLearnedText(input.context, MAX_LEARNED_CONTEXT_CHARS) : "";
|
|
1204
|
+
const line = context ? `- ${content} _(context: ${context})_` : `- ${content}`;
|
|
1205
|
+
const filePath = path.join(getMemoryRoot(agentDir, cwd), LEARNED_LESSONS_FILE);
|
|
1206
|
+
|
|
1207
|
+
// Serialize the read-modify-write per file: parallel `learn` calls (sibling
|
|
1208
|
+
// subagents, or two shared tool calls in one turn) share the project memory
|
|
1209
|
+
// root, so an unguarded RMW would let the last writer drop the other's lesson.
|
|
1210
|
+
const run = (learnedWriteChains.get(filePath) ?? Promise.resolve()).then(() => appendLearnedLine(filePath, line));
|
|
1211
|
+
const guarded = run.catch(() => {});
|
|
1212
|
+
learnedWriteChains.set(filePath, guarded);
|
|
1213
|
+
try {
|
|
1214
|
+
await run;
|
|
1215
|
+
} finally {
|
|
1216
|
+
// Drop the entry once this write is the chain tail, so the map does not
|
|
1217
|
+
// retain one promise per distinct memory root for the process lifetime.
|
|
1218
|
+
if (learnedWriteChains.get(filePath) === guarded) learnedWriteChains.delete(filePath);
|
|
1219
|
+
}
|
|
1220
|
+
return { backend: "local", stored: 1, message: `Lesson saved to ${LEARNED_LESSONS_FILE}.` };
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
async function appendLearnedLine(filePath: string, line: string): Promise<void> {
|
|
1224
|
+
let existing = "";
|
|
1225
|
+
try {
|
|
1226
|
+
existing = await Bun.file(filePath).text();
|
|
1227
|
+
} catch (err) {
|
|
1228
|
+
if (!isEnoent(err)) throw err;
|
|
1229
|
+
}
|
|
1230
|
+
const prior = existing
|
|
1231
|
+
.split("\n")
|
|
1232
|
+
.map(l => l.trim())
|
|
1233
|
+
.filter(l => l.startsWith("- ") && l !== line);
|
|
1234
|
+
const lessons = [line, ...prior].slice(0, MAX_LEARNED_LESSONS);
|
|
1235
|
+
await Bun.write(filePath, `${lessons.join("\n")}\n`);
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
/**
|
|
1239
|
+
* Read `learned.md`, neutralizing each line on read too — a hand-edited or
|
|
1240
|
+
* pre-existing file bypasses write-time normalization and the block renders
|
|
1241
|
+
* unescaped into the system prompt. Returns "" when absent/unreadable.
|
|
1242
|
+
*/
|
|
1243
|
+
async function readLearnedLessons(memoryRoot: string): Promise<string> {
|
|
1244
|
+
let raw = "";
|
|
1245
|
+
try {
|
|
1246
|
+
raw = (await Bun.file(path.join(memoryRoot, LEARNED_LESSONS_FILE)).text()).trim();
|
|
1247
|
+
} catch {
|
|
1248
|
+
return "";
|
|
1249
|
+
}
|
|
1250
|
+
if (!raw) return "";
|
|
1251
|
+
// Neutralize delimiters THEN redact per line — mirrors the write path so a
|
|
1252
|
+
// hand-edited line cannot reassemble a token after delimiter stripping.
|
|
1253
|
+
return raw
|
|
1254
|
+
.split("\n")
|
|
1255
|
+
.map(line => redactSecrets(neutralizeInjection(line)))
|
|
1256
|
+
.join("\n");
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1122
1259
|
function encodeProjectPath(cwd: string): string {
|
|
1123
1260
|
return `--${cwd.replace(/^[/\\]/, "").replace(/[/\\:]/g, "-")}--`;
|
|
1124
1261
|
}
|
package/src/memories/storage.ts
CHANGED
|
@@ -46,10 +46,11 @@ function globalJobKey(cwd: string): string {
|
|
|
46
46
|
|
|
47
47
|
export function openMemoryDb(dbPath: string): Database {
|
|
48
48
|
const db = new Database(dbPath);
|
|
49
|
+
// Install the busy handler BEFORE any lock-taking statement. See #2421.
|
|
50
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
49
51
|
db.exec(`
|
|
50
52
|
PRAGMA journal_mode=WAL;
|
|
51
53
|
PRAGMA synchronous=NORMAL;
|
|
52
|
-
PRAGMA busy_timeout=5000;
|
|
53
54
|
|
|
54
55
|
CREATE TABLE IF NOT EXISTS threads (
|
|
55
56
|
id TEXT PRIMARY KEY,
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
buildMemoryToolDeveloperInstructions,
|
|
3
3
|
clearMemoryData,
|
|
4
4
|
enqueueMemoryConsolidation,
|
|
5
|
+
saveLearnedLesson,
|
|
5
6
|
startMemoryStartupTask,
|
|
6
7
|
} from "../memories";
|
|
7
8
|
import type { MemoryBackend } from "./types";
|
|
@@ -9,9 +10,10 @@ import type { MemoryBackend } from "./types";
|
|
|
9
10
|
/**
|
|
10
11
|
* Wraps the existing `memories/` module as a `MemoryBackend`.
|
|
11
12
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* The rollout-summarisation pipeline (rollouts → SQLite → memory_summary.md) is
|
|
14
|
+
* delegated unchanged. On top of it, `save()` persists `learn`-tool lessons to
|
|
15
|
+
* `learned.md` (so `status()` reports `writable: true`); structured search is
|
|
16
|
+
* still unavailable.
|
|
15
17
|
*/
|
|
16
18
|
export const localBackend: MemoryBackend = {
|
|
17
19
|
id: "local",
|
|
@@ -27,13 +29,17 @@ export const localBackend: MemoryBackend = {
|
|
|
27
29
|
async enqueue(agentDir, cwd) {
|
|
28
30
|
enqueueMemoryConsolidation(agentDir, cwd);
|
|
29
31
|
},
|
|
32
|
+
async save(context, input) {
|
|
33
|
+
return saveLearnedLesson(context.agentDir, context.cwd, input);
|
|
34
|
+
},
|
|
30
35
|
async status() {
|
|
31
36
|
return {
|
|
32
37
|
backend: "local" as const,
|
|
33
38
|
active: true,
|
|
34
|
-
writable:
|
|
39
|
+
writable: true,
|
|
35
40
|
searchable: false,
|
|
36
|
-
message:
|
|
41
|
+
message:
|
|
42
|
+
"Local rollout-summary memory is active; lessons from the `learn` tool are saved to learned.md. Structured search is not available.",
|
|
37
43
|
};
|
|
38
44
|
},
|
|
39
45
|
};
|
package/src/mnemopi/backend.ts
CHANGED