@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
package/src/tools/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { InMemorySnapshotStore } from "@oh-my-pi/hashline";
|
|
2
2
|
import type { AgentTelemetryConfig, AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
3
|
-
import type { FetchImpl, ToolChoice } from "@oh-my-pi/pi-ai";
|
|
3
|
+
import type { FetchImpl, Model, ToolChoice } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
5
5
|
import type { AsyncJobManager } from "../async/job-manager";
|
|
6
6
|
import type { Rule } from "../capability/rule";
|
|
@@ -22,9 +22,11 @@ import type { AgentRegistry } from "../registry/agent-registry";
|
|
|
22
22
|
import type { ArtifactManager } from "../session/artifacts";
|
|
23
23
|
import type { ClientBridge } from "../session/client-bridge";
|
|
24
24
|
import type { CustomMessage } from "../session/messages";
|
|
25
|
+
import type { UsageStatistics } from "../session/session-entries";
|
|
25
26
|
import type { ToolChoiceQueue } from "../session/tool-choice-queue";
|
|
26
27
|
import { TaskTool } from "../task";
|
|
27
28
|
import type { AgentOutputManager } from "../task/output-manager";
|
|
29
|
+
import { canSpawnAtDepth } from "../task/types";
|
|
28
30
|
import { countToolsForAutoDiscovery, resolveEffectiveToolDiscoveryMode } from "../tool-discovery/mode";
|
|
29
31
|
import type { DiscoverableTool, DiscoverableToolSearchIndex } from "../tool-discovery/tool-index";
|
|
30
32
|
import type { EventBus } from "../utils/event-bus";
|
|
@@ -44,6 +46,8 @@ import { GithubTool } from "./gh";
|
|
|
44
46
|
import { InspectImageTool } from "./inspect-image";
|
|
45
47
|
import { IrcTool, isIrcEnabled } from "./irc";
|
|
46
48
|
import { JobTool } from "./job";
|
|
49
|
+
import { LearnTool } from "./learn";
|
|
50
|
+
import { ManageSkillTool } from "./manage-skill";
|
|
47
51
|
import { MemoryEditTool } from "./memory-edit";
|
|
48
52
|
import { MemoryRecallTool } from "./memory-recall";
|
|
49
53
|
import { MemoryReflectTool } from "./memory-reflect";
|
|
@@ -82,6 +86,8 @@ export * from "./image-gen";
|
|
|
82
86
|
export * from "./inspect-image";
|
|
83
87
|
export * from "./irc";
|
|
84
88
|
export * from "./job";
|
|
89
|
+
export * from "./learn";
|
|
90
|
+
export * from "./manage-skill";
|
|
85
91
|
export * from "./memory-edit";
|
|
86
92
|
export * from "./memory-recall";
|
|
87
93
|
export * from "./memory-reflect";
|
|
@@ -144,6 +150,13 @@ export interface ToolSession {
|
|
|
144
150
|
cwd: string;
|
|
145
151
|
/** Whether UI is available */
|
|
146
152
|
hasUI: boolean;
|
|
153
|
+
/**
|
|
154
|
+
* Suppress the spawn specialization/coordination advisory appended to `task`
|
|
155
|
+
* results. Set by internal/programmatic callers (e.g. the commit agent's
|
|
156
|
+
* file-analysis fan-out) whose results are consumed by code — not by a model
|
|
157
|
+
* orchestrating further spawns — so the nudge would only be noise.
|
|
158
|
+
*/
|
|
159
|
+
suppressSpawnAdvisory?: boolean;
|
|
147
160
|
/** Optional fetch implementation injected into the URL read pipeline (tests, proxies). Defaults to global fetch. */
|
|
148
161
|
fetch?: FetchImpl;
|
|
149
162
|
/** Skip Python kernel availability check and warmup */
|
|
@@ -217,6 +230,8 @@ export interface ToolSession {
|
|
|
217
230
|
getModelString?: () => string | undefined;
|
|
218
231
|
/** Get the current session model string, regardless of how it was chosen */
|
|
219
232
|
getActiveModelString?: () => string | undefined;
|
|
233
|
+
/** Get the current session model object (provider/api capabilities), regardless of how it was chosen. */
|
|
234
|
+
getActiveModel?: () => Model | undefined;
|
|
220
235
|
/** Auth storage for passing to subagents (avoids re-discovery) */
|
|
221
236
|
authStorage?: import("../session/auth-storage").AuthStorage;
|
|
222
237
|
/** Model registry for passing to subagents (avoids re-discovery) */
|
|
@@ -253,7 +268,7 @@ export interface ToolSession {
|
|
|
253
268
|
/** Goal runtime for the active agent session. */
|
|
254
269
|
getGoalRuntime?: () => GoalRuntime | undefined;
|
|
255
270
|
/** Get cumulative session usage statistics (input/output tokens, cost). */
|
|
256
|
-
getUsageStatistics?: () =>
|
|
271
|
+
getUsageStatistics?: () => UsageStatistics;
|
|
257
272
|
/** Current per-turn token budget {total, spent, hard} for the eval `budget` helper. */
|
|
258
273
|
getTurnBudget?: () => { total: number | null; spent: number; hard: boolean };
|
|
259
274
|
/** Record output tokens consumed by an eval-spawned subagent toward the current turn budget. */
|
|
@@ -428,6 +443,8 @@ export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
|
|
|
428
443
|
retain: MemoryRetainTool.createIf,
|
|
429
444
|
recall: MemoryRecallTool.createIf,
|
|
430
445
|
reflect: MemoryReflectTool.createIf,
|
|
446
|
+
learn: LearnTool.createIf,
|
|
447
|
+
manage_skill: ManageSkillTool.createIf,
|
|
431
448
|
};
|
|
432
449
|
|
|
433
450
|
export const HIDDEN_TOOLS: Record<string, ToolFactory> = {
|
|
@@ -508,6 +525,21 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
508
525
|
if (!requestedTools.includes(name)) requestedTools.push(name);
|
|
509
526
|
}
|
|
510
527
|
}
|
|
528
|
+
// Auto-learn tools are gated by `autolearn.enabled` but, like the memory
|
|
529
|
+
// tools above, must also be force-included into an explicit requestedTools
|
|
530
|
+
// list so a restricted top-level session whose controller/guidance is
|
|
531
|
+
// active still exposes the tools the nudge points at. Gated to top-level
|
|
532
|
+
// (taskDepth 0): the controller only runs there, so a subagent's explicit
|
|
533
|
+
// tool whitelist must never be silently widened with write-capable tools.
|
|
534
|
+
if (session.settings.get("autolearn.enabled") && (session.taskDepth ?? 0) === 0) {
|
|
535
|
+
if (!requestedTools.includes("manage_skill")) requestedTools.push("manage_skill");
|
|
536
|
+
if (
|
|
537
|
+
["hindsight", "mnemopi", "local"].includes(session.settings.get("memory.backend") ?? "") &&
|
|
538
|
+
!requestedTools.includes("learn")
|
|
539
|
+
) {
|
|
540
|
+
requestedTools.push("learn");
|
|
541
|
+
}
|
|
542
|
+
}
|
|
511
543
|
}
|
|
512
544
|
// Resolve effective tool discovery mode.
|
|
513
545
|
// tools.discoveryMode controls the new modes; mcp.discoveryMode remains a back-compat alias for "mcp-only".
|
|
@@ -541,10 +573,16 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
541
573
|
if (name === "retain" || name === "recall" || name === "reflect") {
|
|
542
574
|
return ["hindsight", "mnemopi"].includes(session.settings.get("memory.backend") ?? "");
|
|
543
575
|
}
|
|
576
|
+
if (name === "manage_skill") return session.settings.get("autolearn.enabled") && (session.taskDepth ?? 0) === 0;
|
|
577
|
+
if (name === "learn") {
|
|
578
|
+
return (
|
|
579
|
+
session.settings.get("autolearn.enabled") &&
|
|
580
|
+
(session.taskDepth ?? 0) === 0 &&
|
|
581
|
+
["hindsight", "mnemopi", "local"].includes(session.settings.get("memory.backend") ?? "")
|
|
582
|
+
);
|
|
583
|
+
}
|
|
544
584
|
if (name === "task") {
|
|
545
|
-
|
|
546
|
-
const currentDepth = session.taskDepth ?? 0;
|
|
547
|
-
return maxDepth < 0 || currentDepth < maxDepth;
|
|
585
|
+
return canSpawnAtDepth(session.settings.get("task.maxRecursionDepth") ?? 2, session.taskDepth ?? 0);
|
|
548
586
|
}
|
|
549
587
|
return true;
|
|
550
588
|
};
|
|
@@ -2,7 +2,7 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
|
|
|
2
2
|
import { instrumentedCompleteSimple, resolveTelemetry } from "@oh-my-pi/pi-agent-core";
|
|
3
3
|
import { type Api, completeSimple, type Model } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
5
|
-
import
|
|
5
|
+
import { z } from "zod/v4";
|
|
6
6
|
import { extractTextContent } from "../commit/utils";
|
|
7
7
|
|
|
8
8
|
import { expandRoleAlias, getModelMatchPreferences, resolveModelFromString } from "../config/model-resolver";
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
type LoadedImageInput,
|
|
14
14
|
loadImageInput,
|
|
15
15
|
MAX_IMAGE_INPUT_BYTES,
|
|
16
|
+
webpExclusionForModel,
|
|
16
17
|
} from "../utils/image-loading";
|
|
17
18
|
import type { ToolSession } from "./index";
|
|
18
19
|
import { ToolError } from "./tool-errors";
|
|
@@ -109,6 +110,7 @@ export class InspectImageTool implements AgentTool<typeof inspectImageSchema, In
|
|
|
109
110
|
cwd: this.session.cwd,
|
|
110
111
|
autoResize: this.session.settings.get("images.autoResize"),
|
|
111
112
|
maxBytes: MAX_IMAGE_INPUT_BYTES,
|
|
113
|
+
excludeWebP: webpExclusionForModel(model),
|
|
112
114
|
});
|
|
113
115
|
} catch (error) {
|
|
114
116
|
if (error instanceof ImageInputTooLargeError) {
|
package/src/tools/irc.ts
CHANGED
|
@@ -12,13 +12,14 @@
|
|
|
12
12
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
13
13
|
import { type Component, Text } from "@oh-my-pi/pi-tui";
|
|
14
14
|
import { formatAge, formatDuration, prompt } from "@oh-my-pi/pi-utils";
|
|
15
|
-
import
|
|
15
|
+
import { z } from "zod/v4";
|
|
16
16
|
import type { Settings } from "../config/settings";
|
|
17
17
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
18
18
|
import { IrcBus, type IrcDeliveryReceipt, type IrcMessage } from "../irc/bus";
|
|
19
19
|
import type { Theme } from "../modes/theme/theme";
|
|
20
20
|
import ircDescription from "../prompts/tools/irc.md" with { type: "text" };
|
|
21
21
|
import type { AgentRegistry } from "../registry/agent-registry";
|
|
22
|
+
import { canSpawnAtDepth } from "../task/types";
|
|
22
23
|
import { Ellipsis, renderStatusLine, renderTreeList, truncateToWidth } from "../tui";
|
|
23
24
|
import type { ToolSession } from ".";
|
|
24
25
|
import {
|
|
@@ -41,8 +42,10 @@ const DEFAULT_IRC_TIMEOUT_MS = 120_000;
|
|
|
41
42
|
*/
|
|
42
43
|
export function isIrcEnabled(settings: Settings, taskDepth: number): boolean {
|
|
43
44
|
if (taskDepth > 0) return true;
|
|
45
|
+
// Top-level session: peers exist only if it can still spawn subagents — the
|
|
46
|
+
// same capacity gate the task tool uses, reused here to avoid drift.
|
|
44
47
|
const maxDepth = settings.get("task.maxRecursionDepth") ?? 2;
|
|
45
|
-
return maxDepth
|
|
48
|
+
return canSpawnAtDepth(maxDepth, taskDepth);
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
const ircSchema = z.object({
|
|
@@ -66,6 +69,7 @@ interface IrcPeerInfo {
|
|
|
66
69
|
parentId?: string;
|
|
67
70
|
unread: number;
|
|
68
71
|
lastActivity: number;
|
|
72
|
+
activity?: string;
|
|
69
73
|
}
|
|
70
74
|
|
|
71
75
|
export interface IrcDetails {
|
|
@@ -146,6 +150,7 @@ export class IrcTool implements AgentTool<typeof ircSchema, IrcDetails> {
|
|
|
146
150
|
parentId: ref.parentId,
|
|
147
151
|
unread: bus.unreadCount(ref.id),
|
|
148
152
|
lastActivity: ref.lastActivity,
|
|
153
|
+
activity: ref.activity,
|
|
149
154
|
}));
|
|
150
155
|
const lines: string[] = [];
|
|
151
156
|
if (peers.length === 0) {
|
|
@@ -154,6 +159,7 @@ export class IrcTool implements AgentTool<typeof ircSchema, IrcDetails> {
|
|
|
154
159
|
lines.push(`${peers.length} peer(s):`);
|
|
155
160
|
for (const peer of peers) {
|
|
156
161
|
const extras = [
|
|
162
|
+
peer.activity || undefined,
|
|
157
163
|
peer.unread > 0 ? `unread ${peer.unread}` : undefined,
|
|
158
164
|
peer.parentId ? `parent ${peer.parentId}` : undefined,
|
|
159
165
|
`active ${formatDuration(Date.now() - peer.lastActivity)} ago`,
|
|
@@ -673,7 +679,9 @@ function renderListResult(details: Partial<IrcDetails>, expanded: boolean, theme
|
|
|
673
679
|
const kindText = peer.parentId ? `${peer.kind}${theme.sep.dot}of ${peer.parentId}` : peer.kind;
|
|
674
680
|
const unread = peer.unread > 0 ? ` ${formatBadge(`${peer.unread} unread`, "warning", theme)}` : "";
|
|
675
681
|
const age = messageAge(peer.lastActivity);
|
|
676
|
-
|
|
682
|
+
const activity = peer.activity ? ` ${theme.fg("dim", replaceTabs(peer.activity))}` : "";
|
|
683
|
+
const name = theme.fg("dim", replaceTabs(peer.displayName));
|
|
684
|
+
return `${peerStatusBadge(peer.status, theme)} ${theme.bold(replaceTabs(peer.id))} ${name} ${theme.fg("dim", kindText)}${activity}${unread}${age ? ` ${theme.fg("dim", age)}` : ""}`;
|
|
677
685
|
},
|
|
678
686
|
},
|
|
679
687
|
theme,
|
package/src/tools/job.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
|
|
|
2
2
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
3
3
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
4
4
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
5
|
-
import
|
|
5
|
+
import { z } from "zod/v4";
|
|
6
6
|
import type { AsyncJob, AsyncJobManager } from "../async";
|
|
7
7
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
8
8
|
import { shimmerEnabled, shimmerText } from "../modes/theme/shimmer";
|
|
@@ -184,9 +184,16 @@ export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
|
|
|
184
184
|
return this.#buildResult(manager, [...cancelledJobs, ...jobsToWatch], cancelOutcomes);
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
-
// Wait until at least one running job finishes, the wait
|
|
187
|
+
// Wait until at least one running job finishes, the wait window elapses,
|
|
188
|
+
// or the call is aborted. With `async.pollWaitDuration` set to `smart`,
|
|
189
|
+
// the window adapts: it starts at the ladder floor and climbs as the agent
|
|
190
|
+
// polls in a tight loop, then resets to the floor once the agent steps
|
|
191
|
+
// away from polling (see AsyncJobManager.nextPollWaitMs). Any fixed value
|
|
192
|
+
// waits that exact duration every time.
|
|
188
193
|
const racePromises: Promise<unknown>[] = runningJobs.map(j => j.promise);
|
|
189
|
-
const
|
|
194
|
+
const pollSetting = this.session.settings.get("async.pollWaitDuration");
|
|
195
|
+
const smartPoll = pollSetting === "smart";
|
|
196
|
+
const waitMs = smartPoll ? manager.nextPollWaitMs(ownerId) : parseWaitDurationMs(pollSetting);
|
|
190
197
|
const { promise: timeoutPromise, resolve: timeoutResolve } = Promise.withResolvers<void>();
|
|
191
198
|
const timeoutHandle = setTimeout(() => timeoutResolve(), waitMs);
|
|
192
199
|
racePromises.push(timeoutPromise);
|
|
@@ -232,6 +239,11 @@ export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
|
|
|
232
239
|
manager.unwatchJobs(watchedJobIds);
|
|
233
240
|
clearTimeout(timeoutHandle);
|
|
234
241
|
if (progressTimer) clearInterval(progressTimer);
|
|
242
|
+
if (smartPoll) {
|
|
243
|
+
// Reset the idle-gap clock: escalate if the agent polls again soon,
|
|
244
|
+
// drop back to the floor once it goes quiet for a while.
|
|
245
|
+
manager.recordPollWaitEnd(ownerId);
|
|
246
|
+
}
|
|
235
247
|
}
|
|
236
248
|
|
|
237
249
|
return this.#buildResult(manager, allTrackedJobs, cancelOutcomes);
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import type { AgentTool, AgentToolResult } from "@oh-my-pi/pi-agent-core";
|
|
2
|
+
import { z } from "zod/v4";
|
|
3
|
+
import { sanitizeSkillName, writeManagedSkill } from "../autolearn/managed-skills";
|
|
4
|
+
import { isNameClaimedByAuthoredSkill } from "../extensibility/skills";
|
|
5
|
+
import { localBackend } from "../memory-backend/local-backend";
|
|
6
|
+
import learnDescription from "../prompts/tools/learn.md" with { type: "text" };
|
|
7
|
+
import type { ToolSession } from ".";
|
|
8
|
+
|
|
9
|
+
const learnSchema = z.object({
|
|
10
|
+
memory: z.string().describe("the durable, self-contained lesson to remember (what, when, why)"),
|
|
11
|
+
context: z.string().describe("optional source context for the lesson").optional(),
|
|
12
|
+
skill: z
|
|
13
|
+
.object({
|
|
14
|
+
action: z.enum(["create", "update"]),
|
|
15
|
+
name: z.string().describe("kebab-case skill name"),
|
|
16
|
+
description: z.string().describe("one-line description of when to use the skill"),
|
|
17
|
+
body: z.string().describe("the SKILL.md body in markdown (no frontmatter)"),
|
|
18
|
+
})
|
|
19
|
+
.describe("also create or enhance a managed skill in the same call")
|
|
20
|
+
.optional(),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export type LearnParams = z.infer<typeof learnSchema>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Orchestrating "learn" tool: persists a lesson to long-term memory and,
|
|
27
|
+
* given a `skill` payload, mints/enhances a managed skill via the shared
|
|
28
|
+
* `writeManagedSkill` primitive. Gated behind `autolearn.enabled` plus a live
|
|
29
|
+
* memory backend — `hindsight`/`mnemopi` (remote/SQLite) or `local` (the
|
|
30
|
+
* file-based rollout backend, where lessons append to `learned.md`).
|
|
31
|
+
*/
|
|
32
|
+
export class LearnTool implements AgentTool<typeof learnSchema> {
|
|
33
|
+
readonly name = "learn";
|
|
34
|
+
readonly approval = (args: unknown) =>
|
|
35
|
+
(args as Partial<LearnParams>).skill || this.session.settings.get("memory.backend") === "local"
|
|
36
|
+
? "write"
|
|
37
|
+
: "read";
|
|
38
|
+
readonly label = "Learn";
|
|
39
|
+
readonly description = learnDescription;
|
|
40
|
+
readonly parameters = learnSchema;
|
|
41
|
+
readonly strict = true;
|
|
42
|
+
readonly loadMode = "essential" as const;
|
|
43
|
+
readonly summary = "Capture a reusable lesson to memory (and optionally a managed skill)";
|
|
44
|
+
|
|
45
|
+
constructor(private readonly session: ToolSession) {}
|
|
46
|
+
|
|
47
|
+
static createIf(session: ToolSession): LearnTool | null {
|
|
48
|
+
if (!session.settings.get("autolearn.enabled")) return null;
|
|
49
|
+
const backend = session.settings.get("memory.backend");
|
|
50
|
+
if (backend !== "hindsight" && backend !== "mnemopi" && backend !== "local") return null;
|
|
51
|
+
return new LearnTool(session);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async execute(_id: string, params: LearnParams): Promise<AgentToolResult> {
|
|
55
|
+
// 1) Persist or queue the lesson to long-term memory (mirrors MemoryRetainTool).
|
|
56
|
+
const backend = this.session.settings.get("memory.backend");
|
|
57
|
+
let memoryMessage = "Lesson stored";
|
|
58
|
+
if (backend === "mnemopi") {
|
|
59
|
+
const state = this.session.getMnemopiSessionState?.();
|
|
60
|
+
if (!state) {
|
|
61
|
+
throw new Error("Mnemopi backend is not initialised for this session.");
|
|
62
|
+
}
|
|
63
|
+
const id = state.rememberScoped(params.memory, {
|
|
64
|
+
source: "coding-agent-learn",
|
|
65
|
+
importance: 0.8,
|
|
66
|
+
metadata: {
|
|
67
|
+
session_id: state.sessionId,
|
|
68
|
+
cwd: state.session.sessionManager.getCwd(),
|
|
69
|
+
context: params.context ?? null,
|
|
70
|
+
tool: "learn",
|
|
71
|
+
},
|
|
72
|
+
scope: "bank",
|
|
73
|
+
extract: true,
|
|
74
|
+
extractEntities: true,
|
|
75
|
+
veracity: "tool",
|
|
76
|
+
memoryType: "fact",
|
|
77
|
+
});
|
|
78
|
+
// rememberScoped returns undefined when the retain failed (closed DB /
|
|
79
|
+
// disk error); mirror mnemopiBackend.save and fail loudly rather than
|
|
80
|
+
// reporting (and minting a skill for) a lesson that was silently dropped.
|
|
81
|
+
if (!id) {
|
|
82
|
+
throw new Error("Mnemopi did not store the lesson (no memory id returned).");
|
|
83
|
+
}
|
|
84
|
+
} else if (backend === "local") {
|
|
85
|
+
const result = await localBackend.save?.(
|
|
86
|
+
{ agentDir: this.session.settings.getAgentDir(), cwd: this.session.settings.getCwd() },
|
|
87
|
+
{ content: params.memory, context: params.context, source: "coding-agent-learn", importance: 0.8 },
|
|
88
|
+
);
|
|
89
|
+
if (!result || result.stored === 0) {
|
|
90
|
+
throw new Error("Lesson was empty after sanitization; nothing stored.");
|
|
91
|
+
}
|
|
92
|
+
} else {
|
|
93
|
+
const state = this.session.getHindsightSessionState?.();
|
|
94
|
+
if (!state) {
|
|
95
|
+
throw new Error("Hindsight backend is not initialised for this session.");
|
|
96
|
+
}
|
|
97
|
+
state.enqueueRetain(params.memory, params.context);
|
|
98
|
+
memoryMessage = "Lesson queued for retention";
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 2) Optionally mint/enhance a managed skill. A failure here is surfaced
|
|
102
|
+
// as a partial outcome — the lesson is already stored or queued.
|
|
103
|
+
if (params.skill) {
|
|
104
|
+
// A managed skill resolves below any authored skill of the same name, so
|
|
105
|
+
// minting one under a claimed name writes a file that never surfaces. The
|
|
106
|
+
// lesson is already stored/queued; refuse the skill rather than report a
|
|
107
|
+
// false "Created" (mirrors ManageSkillTool).
|
|
108
|
+
let safeSkillName: string | undefined;
|
|
109
|
+
try {
|
|
110
|
+
safeSkillName = sanitizeSkillName(params.skill.name);
|
|
111
|
+
} catch {
|
|
112
|
+
safeSkillName = undefined;
|
|
113
|
+
}
|
|
114
|
+
if (params.skill.action === "create" && safeSkillName && isNameClaimedByAuthoredSkill(safeSkillName)) {
|
|
115
|
+
return {
|
|
116
|
+
content: [
|
|
117
|
+
{
|
|
118
|
+
type: "text",
|
|
119
|
+
text: `${memoryMessage}. Did not create managed skill "${params.skill.name}": an authored skill of that name already exists, and managed skills cannot override authored ones. Choose a different name.`,
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
isError: true,
|
|
123
|
+
details: { skill: null, shadowed: true },
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
await writeManagedSkill(params.skill);
|
|
128
|
+
} catch (err) {
|
|
129
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
130
|
+
throw new Error(`${memoryMessage}, but the managed skill could not be written: ${reason}`);
|
|
131
|
+
}
|
|
132
|
+
const verb = params.skill.action === "create" ? "Created" : "Updated";
|
|
133
|
+
return {
|
|
134
|
+
content: [{ type: "text", text: `${memoryMessage}. ${verb} managed skill "${params.skill.name}".` }],
|
|
135
|
+
details: { skill: params.skill.name },
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
content: [{ type: "text", text: `${memoryMessage}.` }],
|
|
141
|
+
details: { skill: null },
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import type { AgentTool, AgentToolResult } from "@oh-my-pi/pi-agent-core";
|
|
3
|
+
import { z } from "zod/v4";
|
|
4
|
+
import {
|
|
5
|
+
deleteManagedSkill,
|
|
6
|
+
getManagedSkillsDir,
|
|
7
|
+
sanitizeSkillName,
|
|
8
|
+
writeManagedSkill,
|
|
9
|
+
} from "../autolearn/managed-skills";
|
|
10
|
+
import { isNameClaimedByAuthoredSkill } from "../extensibility/skills";
|
|
11
|
+
import manageSkillDescription from "../prompts/tools/manage-skill.md" with { type: "text" };
|
|
12
|
+
import type { ToolSession } from ".";
|
|
13
|
+
|
|
14
|
+
const manageSkillSchema = z
|
|
15
|
+
.object({
|
|
16
|
+
action: z.enum(["create", "update", "delete"]),
|
|
17
|
+
name: z.string().describe("kebab-case skill name"),
|
|
18
|
+
description: z
|
|
19
|
+
.string()
|
|
20
|
+
.describe("one-line description of when to use the skill (required for create/update)")
|
|
21
|
+
.optional(),
|
|
22
|
+
body: z
|
|
23
|
+
.string()
|
|
24
|
+
.describe("the SKILL.md body in markdown, no frontmatter (required for create/update)")
|
|
25
|
+
.optional(),
|
|
26
|
+
})
|
|
27
|
+
// Enforce the action/field contract at validation time rather than only in
|
|
28
|
+
// execute. Kept as a cross-field refine (not a discriminated union) so the
|
|
29
|
+
// wire schema stays a single root object — strict structured-output mode and
|
|
30
|
+
// the Anthropic tool-schema builder both require that.
|
|
31
|
+
.refine(p => p.action === "delete" || (p.description !== undefined && p.body !== undefined), {
|
|
32
|
+
message: '"create" and "update" require both "description" and "body".',
|
|
33
|
+
path: ["description"],
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export type ManageSkillParams = z.infer<typeof manageSkillSchema>;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Direct create/update/delete of isolated managed skills. Gated behind
|
|
40
|
+
* `autolearn.enabled`; backend-independent (the skill side is standalone).
|
|
41
|
+
*/
|
|
42
|
+
export class ManageSkillTool implements AgentTool<typeof manageSkillSchema> {
|
|
43
|
+
readonly name = "manage_skill";
|
|
44
|
+
readonly approval = "write" as const;
|
|
45
|
+
readonly label = "Manage Skill";
|
|
46
|
+
readonly description = manageSkillDescription;
|
|
47
|
+
readonly parameters = manageSkillSchema;
|
|
48
|
+
readonly strict = true;
|
|
49
|
+
readonly loadMode = "essential" as const;
|
|
50
|
+
readonly summary = "Create, update, or delete an isolated managed skill";
|
|
51
|
+
|
|
52
|
+
// No session state needed: createIf reads settings; writes target the
|
|
53
|
+
// home-based managed-skills dir directly.
|
|
54
|
+
static createIf(session: ToolSession): ManageSkillTool | null {
|
|
55
|
+
if (!session.settings.get("autolearn.enabled")) return null;
|
|
56
|
+
return new ManageSkillTool();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async execute(_id: string, params: ManageSkillParams): Promise<AgentToolResult> {
|
|
60
|
+
if (params.action === "delete") {
|
|
61
|
+
await deleteManagedSkill(params.name);
|
|
62
|
+
return {
|
|
63
|
+
content: [{ type: "text", text: `Deleted managed skill "${params.name}".` }],
|
|
64
|
+
details: { action: "delete", name: params.name },
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Defensive narrowing: the schema refine already rejects create/update
|
|
69
|
+
// without both fields, so this is unreachable for valid input — it only
|
|
70
|
+
// proves the strings are present to `writeManagedSkill`'s typed contract.
|
|
71
|
+
if (!params.description || !params.body) {
|
|
72
|
+
throw new Error(`"${params.action}" requires both "description" and "body".`);
|
|
73
|
+
}
|
|
74
|
+
// A managed skill resolves below any authored skill of the same name
|
|
75
|
+
// (authored always wins in discovery), so creating one under a name an
|
|
76
|
+
// authored skill already claims writes a file that never surfaces. Refuse
|
|
77
|
+
// up front rather than report a false "Created". `sanitizeSkillName`
|
|
78
|
+
// normalizes to the on-disk name the discovery scan compares against.
|
|
79
|
+
if (params.action === "create" && isNameClaimedByAuthoredSkill(sanitizeSkillName(params.name))) {
|
|
80
|
+
return {
|
|
81
|
+
content: [
|
|
82
|
+
{
|
|
83
|
+
type: "text",
|
|
84
|
+
text: `Cannot create managed skill "${params.name}": an authored skill of that name already exists, and managed skills cannot override authored ones. Choose a different name.`,
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
isError: true,
|
|
88
|
+
details: { action: "create", name: params.name, shadowed: true },
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
const { path: skillPath } = await writeManagedSkill({
|
|
92
|
+
action: params.action,
|
|
93
|
+
name: params.name,
|
|
94
|
+
description: params.description,
|
|
95
|
+
body: params.body,
|
|
96
|
+
});
|
|
97
|
+
const relativePath = path.relative(getManagedSkillsDir(), skillPath);
|
|
98
|
+
const verb = params.action === "create" ? "Created" : "Updated";
|
|
99
|
+
return {
|
|
100
|
+
content: [{ type: "text", text: `${verb} managed skill "${params.name}" (managed-skills/${relativePath}).` }],
|
|
101
|
+
details: { action: params.action, name: params.name },
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
}
|
package/src/tools/memory-edit.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AgentTool, AgentToolResult } from "@oh-my-pi/pi-agent-core";
|
|
2
|
-
import
|
|
2
|
+
import { z } from "zod/v4";
|
|
3
3
|
import memoryEditDescription from "../prompts/tools/memory-edit.md" with { type: "text" };
|
|
4
4
|
import type { ToolSession } from ".";
|
|
5
5
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentTool, AgentToolResult } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import { logger, untilAborted } from "@oh-my-pi/pi-utils";
|
|
3
|
-
import
|
|
3
|
+
import { z } from "zod/v4";
|
|
4
4
|
import { formatCurrentTime, formatMemories } from "../hindsight/content";
|
|
5
5
|
import recallDescription from "../prompts/tools/recall.md" with { type: "text" };
|
|
6
6
|
import type { ToolSession } from ".";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentTool, AgentToolResult } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import { logger, untilAborted } from "@oh-my-pi/pi-utils";
|
|
3
|
-
import
|
|
3
|
+
import { z } from "zod/v4";
|
|
4
4
|
import { ensureBankExists } from "../hindsight/bank";
|
|
5
5
|
import reflectDescription from "../prompts/tools/reflect.md" with { type: "text" };
|
|
6
6
|
import type { ToolSession } from ".";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
+
import { HL_FILE_HASH_LENGTH, HL_FILE_HASH_SEP, HL_FILE_PREFIX, HL_FILE_SUFFIX } from "@oh-my-pi/hashline";
|
|
3
4
|
import { resolveLocalRoot, resolveLocalUrlToPath, resolveVaultUrlToPath } from "../internal-urls";
|
|
4
5
|
import type { ToolSession } from ".";
|
|
5
6
|
import { normalizeLocalScheme, resolveToCwd } from "./path-utils";
|
|
@@ -7,6 +8,7 @@ import { ToolError } from "./tool-errors";
|
|
|
7
8
|
|
|
8
9
|
const VAULT_SCHEME_PREFIX = "vault:";
|
|
9
10
|
const LOCAL_SCHEME_PREFIX = "local:";
|
|
11
|
+
const HL_TRAILING_TAG_RE = new RegExp(`${HL_FILE_HASH_SEP}[0-9A-Fa-f]{${HL_FILE_HASH_LENGTH}}$`);
|
|
10
12
|
|
|
11
13
|
/** Resolve the absolute path of the session's `local://` artifact sandbox.
|
|
12
14
|
* Returns `null` when the session has no artifact wiring (e.g. tests). */
|
|
@@ -30,29 +32,57 @@ function isWithinRoot(absolutePath: string, root: string): boolean {
|
|
|
30
32
|
return absolutePath.startsWith(sep);
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
/**
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
35
|
+
/** Strip the hashline `[path#TAG]` wrapper from a write/edit target so the inner
|
|
36
|
+
* filesystem path drives both authorization and resolution. Only unwraps inputs
|
|
37
|
+
* that match the strict hashline header shape (`[path]` or `[path#XXXX]` with a
|
|
38
|
+
* 4-hex tag); anything else returns the original string so the downstream
|
|
39
|
+
* resolver surfaces the real error. Exported for callers (e.g. `write`) that
|
|
40
|
+
* make scheme/bridge-routing decisions before {@link resolvePlanPath} runs. */
|
|
41
|
+
export function unwrapHashlineHeaderPath(targetPath: string): string {
|
|
42
|
+
const trimmed = targetPath.trimEnd();
|
|
43
|
+
if (
|
|
44
|
+
trimmed.length < HL_FILE_PREFIX.length + HL_FILE_SUFFIX.length ||
|
|
45
|
+
trimmed[0] !== HL_FILE_PREFIX ||
|
|
46
|
+
trimmed[trimmed.length - 1] !== HL_FILE_SUFFIX
|
|
47
|
+
) {
|
|
48
|
+
return targetPath;
|
|
49
|
+
}
|
|
50
|
+
const inner = trimmed.slice(HL_FILE_PREFIX.length, trimmed.length - HL_FILE_SUFFIX.length);
|
|
51
|
+
const tagMatch = HL_TRAILING_TAG_RE.exec(inner);
|
|
52
|
+
const pathPart = tagMatch ? inner.slice(0, tagMatch.index) : inner;
|
|
53
|
+
// A valid header is exactly `PATH` or `PATH#XXXX`; reject any other shape
|
|
54
|
+
// (selectors, non-hex tags, embedded `#`) so we never silently rewrite a
|
|
55
|
+
// path the model did not author.
|
|
56
|
+
if (pathPart.length === 0 || pathPart.includes(HL_FILE_HASH_SEP)) return targetPath;
|
|
57
|
+
return pathPart;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** True when `targetPath` resolves into the session-local artifact sandbox.
|
|
61
|
+
* Routes through {@link resolvePlanPath} so the guard and the eventual write
|
|
62
|
+
* always agree on the absolute target (including bracketed hashline headers,
|
|
63
|
+
* `local://` URLs, and bare absolute paths). Files inside the sandbox are not
|
|
64
|
+
* part of the working tree, so plan mode treats them as freely writable
|
|
65
|
+
* scratch/plan space. */
|
|
38
66
|
function targetsLocalSandbox(session: ToolSession, targetPath: string): boolean {
|
|
39
|
-
const normalized = normalizeLocalScheme(targetPath);
|
|
40
|
-
if (normalized.startsWith(LOCAL_SCHEME_PREFIX)) return true;
|
|
41
|
-
if (!path.isAbsolute(normalized)) return false;
|
|
42
67
|
const root = localSandboxRoot(session);
|
|
43
68
|
if (!root) return false;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
69
|
+
let resolved: string;
|
|
70
|
+
try {
|
|
71
|
+
resolved = resolvePlanPath(session, targetPath);
|
|
72
|
+
} catch {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
if (!path.isAbsolute(resolved)) return false;
|
|
76
|
+
const absolute = path.resolve(resolved);
|
|
77
|
+
if (isWithinRoot(absolute, root)) return true;
|
|
78
|
+
// Compare realpath-normalized forms so that `/tmp/…` vs `/private/tmp/…`
|
|
79
|
+
// (macOS) and other symlink-collapsed roots both resolve to the same
|
|
80
|
+
// sandbox identity.
|
|
49
81
|
try {
|
|
50
82
|
const realRoot = fs.realpathSync.native(root);
|
|
51
|
-
if (isWithinRoot(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const realParent = fs.realpathSync.native(path.dirname(resolved));
|
|
55
|
-
return isWithinRoot(path.join(realParent, path.basename(resolved)), realRoot);
|
|
83
|
+
if (isWithinRoot(absolute, realRoot)) return true;
|
|
84
|
+
const realParent = fs.realpathSync.native(path.dirname(absolute));
|
|
85
|
+
return isWithinRoot(path.join(realParent, path.basename(absolute)), realRoot);
|
|
56
86
|
} catch {
|
|
57
87
|
return false;
|
|
58
88
|
}
|
|
@@ -61,9 +91,13 @@ function targetsLocalSandbox(session: ToolSession, targetPath: string): boolean
|
|
|
61
91
|
/**
|
|
62
92
|
* Resolve a write/edit target to its absolute filesystem path, honoring the
|
|
63
93
|
* `local://` and `vault://` schemes. Plain paths resolve against the session cwd.
|
|
94
|
+
* Bracketed hashline headers (`[path#TAG]`) are unwrapped first so the inner
|
|
95
|
+
* filesystem path drives resolution — keeping the plan-mode guard and the
|
|
96
|
+
* eventual write in lockstep.
|
|
64
97
|
*/
|
|
65
98
|
export function resolvePlanPath(session: ToolSession, targetPath: string): string {
|
|
66
|
-
const
|
|
99
|
+
const unwrapped = unwrapHashlineHeaderPath(targetPath);
|
|
100
|
+
const normalized = normalizeLocalScheme(unwrapped);
|
|
67
101
|
if (normalized.startsWith(LOCAL_SCHEME_PREFIX)) {
|
|
68
102
|
return resolveLocalUrlToPath(normalized, {
|
|
69
103
|
getArtifactsDir: session.getArtifactsDir,
|