@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/read.ts
CHANGED
|
@@ -9,7 +9,7 @@ import type { Component } from "@oh-my-pi/pi-tui";
|
|
|
9
9
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
10
10
|
import { getRemoteDir, logger, prompt, readImageMetadata, untilAborted } from "@oh-my-pi/pi-utils";
|
|
11
11
|
import { LRUCache } from "lru-cache/raw";
|
|
12
|
-
import
|
|
12
|
+
import { z } from "zod/v4";
|
|
13
13
|
import {
|
|
14
14
|
canonicalSnapshotKey,
|
|
15
15
|
getFileSnapshotStore,
|
|
@@ -38,7 +38,12 @@ import { fileHyperlink, renderCodeCell, renderMarkdownCell, renderStatusLine, tr
|
|
|
38
38
|
import { CachedOutputBlock, markFramedBlockComponent } from "../tui/output-block";
|
|
39
39
|
import { buildLineEntriesWithBlockContext, type LineEntry, lineEntriesToPlainText } from "../utils/block-context";
|
|
40
40
|
import { resolveFileDisplayMode } from "../utils/file-display-mode";
|
|
41
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
ImageInputTooLargeError,
|
|
43
|
+
loadImageInput,
|
|
44
|
+
MAX_IMAGE_INPUT_BYTES,
|
|
45
|
+
webpExclusionForModel,
|
|
46
|
+
} from "../utils/image-loading";
|
|
42
47
|
import { convertFileWithMarkit } from "../utils/markit";
|
|
43
48
|
import { buildDirectoryTree, type DirectoryTree } from "../workspace-tree";
|
|
44
49
|
import { type ArchiveReader, formatArchiveEntryLines, openArchive, parseArchivePathCandidates } from "./archive-reader";
|
|
@@ -1974,6 +1979,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
|
|
|
1974
1979
|
maxBytes: MAX_IMAGE_SIZE,
|
|
1975
1980
|
resolvedPath: absolutePath,
|
|
1976
1981
|
detectedMimeType: mimeType,
|
|
1982
|
+
excludeWebP: webpExclusionForModel(this.session.getActiveModel?.()),
|
|
1977
1983
|
});
|
|
1978
1984
|
if (!imageInput) {
|
|
1979
1985
|
throw new ToolError(`Read image file [${mimeType}] failed: unsupported image format.`);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import { type MermaidAsciiRenderOptions, prompt, renderMermaidAscii } from "@oh-my-pi/pi-utils";
|
|
3
|
-
import
|
|
3
|
+
import { z } from "zod/v4";
|
|
4
4
|
import renderMermaidDescription from "../prompts/tools/render-mermaid.md" with { type: "text" };
|
|
5
5
|
import type { ToolSession } from "./index";
|
|
6
6
|
|
package/src/tools/renderers.ts
CHANGED
|
@@ -44,18 +44,14 @@ export type ToolRenderer = {
|
|
|
44
44
|
/** Render without background box, inline in the response flow */
|
|
45
45
|
inline?: boolean;
|
|
46
46
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* pending preview streams top-anchored append-shaped rows the result
|
|
54
|
-
* render preserves (task context/assignment, write content), which stay
|
|
55
|
-
* commit-eligible so a call taller than the viewport scrolls into history
|
|
56
|
-
* instead of reading as cut off.
|
|
47
|
+
* Whether pending-call rows are provisional: useful on screen while a tool is
|
|
48
|
+
* streaming, but not durable transcript history. `true` means every pending
|
|
49
|
+
* shape is provisional. `"collapsed"` means only the collapsed pending shape
|
|
50
|
+
* is provisional; expanded rendering is top-anchored/append-shaped enough to
|
|
51
|
+
* let the transcript commit its settled prefix. Absent = the pending preview
|
|
52
|
+
* streams rows the result render preserves.
|
|
57
53
|
*/
|
|
58
|
-
provisionalPendingPreview?: boolean;
|
|
54
|
+
provisionalPendingPreview?: boolean | "collapsed";
|
|
59
55
|
};
|
|
60
56
|
|
|
61
57
|
export const toolRenderers: Record<string, ToolRenderer> = {
|
|
@@ -24,7 +24,7 @@ import path from "node:path";
|
|
|
24
24
|
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
25
25
|
import type { FetchImpl } from "@oh-my-pi/pi-ai";
|
|
26
26
|
import { $env, $flag, getAgentDir, getInstallId, logger, VERSION } from "@oh-my-pi/pi-utils";
|
|
27
|
-
import
|
|
27
|
+
import { z } from "zod/v4";
|
|
28
28
|
import type { Settings } from "..";
|
|
29
29
|
import type { ToolSession } from "./index";
|
|
30
30
|
|
|
@@ -206,10 +206,11 @@ export function openAutoQaDb(): Database | null {
|
|
|
206
206
|
if (cachedDb) return cachedDb;
|
|
207
207
|
try {
|
|
208
208
|
const db = new Database(getAutoQaDbPath());
|
|
209
|
+
// Install the busy handler BEFORE any lock-taking statement. See #2421.
|
|
210
|
+
db.run("PRAGMA busy_timeout = 5000");
|
|
209
211
|
db.run(`
|
|
210
212
|
PRAGMA journal_mode=WAL;
|
|
211
213
|
PRAGMA synchronous=NORMAL;
|
|
212
|
-
PRAGMA busy_timeout=5000;
|
|
213
214
|
CREATE TABLE IF NOT EXISTS grievances (
|
|
214
215
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
215
216
|
model TEXT NOT NULL,
|
package/src/tools/resolve.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, untilAborted } from "@oh-my-pi/pi-utils";
|
|
5
|
-
import
|
|
5
|
+
import { z } from "zod/v4";
|
|
6
6
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
7
7
|
import type { Theme } from "../modes/theme/theme";
|
|
8
8
|
import resolveDescription from "../prompts/tools/resolve.md" with { type: "text" };
|
package/src/tools/review.ts
CHANGED
|
@@ -12,7 +12,7 @@ import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
|
12
12
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
13
13
|
import { Container, Text } from "@oh-my-pi/pi-tui";
|
|
14
14
|
import { isRecord } from "@oh-my-pi/pi-utils";
|
|
15
|
-
import
|
|
15
|
+
import { z } from "zod/v4";
|
|
16
16
|
import type { Theme, ThemeColor } from "../modes/theme/theme";
|
|
17
17
|
import { subprocessToolRegistry } from "../task/subprocess-tool-registry";
|
|
18
18
|
import type { ReviewFinding } from "../task/types";
|
|
@@ -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 { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
7
7
|
import type { Theme } from "../modes/theme/theme";
|
|
8
8
|
import searchToolBm25Description from "../prompts/tools/search-tool-bm25.md" with { type: "text" };
|
package/src/tools/search.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { type GrepMatch, GrepOutputMode, type GrepResult, grep } from "@oh-my-pi
|
|
|
7
7
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
8
8
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
9
9
|
import { prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
10
|
-
import
|
|
10
|
+
import { z } from "zod/v4";
|
|
11
11
|
import { recordFileSnapshot } from "../edit/file-snapshot-store";
|
|
12
12
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
13
13
|
import type { LocalProtocolOptions } from "../internal-urls/local-protocol";
|
package/src/tools/ssh.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
3
3
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
4
|
-
import
|
|
4
|
+
import { z } from "zod/v4";
|
|
5
5
|
import type { SSHHost } from "../capability/ssh";
|
|
6
6
|
import { sshCapability } from "../capability/ssh";
|
|
7
7
|
import { loadCapability } from "../discovery";
|
|
@@ -346,7 +346,8 @@ export const sshToolRenderer = {
|
|
|
346
346
|
});
|
|
347
347
|
},
|
|
348
348
|
mergeCallAndResult: true,
|
|
349
|
-
//
|
|
350
|
-
// shifts while args stream
|
|
351
|
-
|
|
349
|
+
// Collapsed pending preview caps the command to a viewport-sized tail window
|
|
350
|
+
// that shifts while args stream. Expanded output is top-anchored enough for
|
|
351
|
+
// the transcript to commit its settled prefix.
|
|
352
|
+
provisionalPendingPreview: "collapsed",
|
|
352
353
|
};
|
package/src/tools/todo.ts
CHANGED
|
@@ -3,12 +3,12 @@ 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
5
|
import chalk from "chalk";
|
|
6
|
-
import
|
|
6
|
+
import { z } from "zod/v4";
|
|
7
7
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
8
8
|
import type { Theme } from "../modes/theme/theme";
|
|
9
9
|
import todoDescription from "../prompts/tools/todo.md" with { type: "text" };
|
|
10
10
|
import type { ToolSession } from "../sdk";
|
|
11
|
-
import type { SessionEntry } from "../session/session-
|
|
11
|
+
import type { SessionEntry } from "../session/session-entries";
|
|
12
12
|
import { framedBlock, renderStatusLine, renderTreeList } from "../tui";
|
|
13
13
|
import { formatErrorDetail, PREVIEW_LIMITS } from "./render-utils";
|
|
14
14
|
|
package/src/tools/tts.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
// Ported from NousResearch/hermes-agent (MIT) — tools/tts_tool.py L167-171, L896-959.
|
|
2
|
+
// The xAI Grok Voice path below is preserved intact; a local on-device neural TTS
|
|
3
|
+
// backend (Kokoro-82M via kokoro-js on the shared ONNX worker) is layered on behind
|
|
4
|
+
// the `providers.tts` switch.
|
|
2
5
|
|
|
3
6
|
import type { AgentToolResult } from "@oh-my-pi/pi-agent-core";
|
|
4
7
|
import { type ApiKey, ProviderHttpError, withAuth } from "@oh-my-pi/pi-ai";
|
|
5
|
-
import
|
|
8
|
+
import { z } from "zod/v4";
|
|
9
|
+
import { settings } from "../config/settings";
|
|
6
10
|
import type { CustomTool, CustomToolContext } from "../extensibility/custom-tools/types";
|
|
7
11
|
import { ohMyPiXAIUserAgent, resolveXAIHttpCredentials } from "../lib/xai-http";
|
|
12
|
+
import { DEFAULT_TTS_LOCAL_MODEL_KEY, DEFAULT_TTS_VOICE, isTtsLocalModelKey, KOKORO_VOICES } from "../tts/models";
|
|
13
|
+
import { ttsClient } from "../tts/tts-client";
|
|
14
|
+
import { encodeWav } from "../tts/wav";
|
|
8
15
|
import { formatPathRelativeToCwd, resolveToCwd } from "./path-utils";
|
|
9
16
|
|
|
10
17
|
// Hermes tts_tool.py L167-171
|
|
@@ -22,6 +29,7 @@ const formatVoiceList = (): string =>
|
|
|
22
29
|
XAI_BUILTIN_VOICES.map(v => (v === DEFAULT_XAI_VOICE_ID ? `${v} (default)` : v)).join(", ");
|
|
23
30
|
|
|
24
31
|
type TtsCodec = "mp3" | "wav";
|
|
32
|
+
type TtsBackend = "local" | "xai";
|
|
25
33
|
|
|
26
34
|
const ttsSchema = z.object({
|
|
27
35
|
text: z.string().min(1).max(XAI_MAX_TEXT_LENGTH),
|
|
@@ -36,16 +44,200 @@ interface TtsToolDetails {
|
|
|
36
44
|
bytes: number;
|
|
37
45
|
voiceId: string;
|
|
38
46
|
codec: TtsCodec;
|
|
47
|
+
backend: TtsBackend;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Pick the synthesis backend. Pure for testability.
|
|
52
|
+
*
|
|
53
|
+
* - `xai` / `local` are honored verbatim (the xAI path still surfaces its own
|
|
54
|
+
* "no credentials" error when creds are missing).
|
|
55
|
+
* - `auto` prefers the local on-device backend, except when the caller asked for
|
|
56
|
+
* an `.mp3` and xAI credentials exist — only the cloud path can emit MP3, so we
|
|
57
|
+
* route there to satisfy the requested container rather than substituting WAV.
|
|
58
|
+
*/
|
|
59
|
+
export function resolveTtsBackend(opts: { preference: string; wantsMp3: boolean; hasXaiCreds: boolean }): TtsBackend {
|
|
60
|
+
if (opts.preference === "xai") return "xai";
|
|
61
|
+
if (opts.preference === "local") return "local";
|
|
62
|
+
if (opts.wantsMp3 && opts.hasXaiCreds) return "xai";
|
|
63
|
+
return "local";
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Resolve the on-disk path for local synthesis. Local output is always WAV (no
|
|
68
|
+
* MP3 encoder is bundled), so an `.mp3` (or any non-`.wav`) request is rewritten
|
|
69
|
+
* to a sibling `.wav` and flagged so the tool result can note the substitution.
|
|
70
|
+
*/
|
|
71
|
+
export function resolveLocalWavPath(outputPath: string): { wavPath: string; substituted: boolean } {
|
|
72
|
+
const lower = outputPath.toLowerCase();
|
|
73
|
+
if (lower.endsWith(".wav")) return { wavPath: outputPath, substituted: false };
|
|
74
|
+
const slash = Math.max(outputPath.lastIndexOf("/"), outputPath.lastIndexOf("\\"));
|
|
75
|
+
const dot = outputPath.lastIndexOf(".");
|
|
76
|
+
const base = dot > slash ? outputPath.slice(0, dot) : outputPath;
|
|
77
|
+
return { wavPath: `${base}.wav`, substituted: true };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function readStringSetting(key: "providers.tts" | "tts.localModel" | "tts.localVoice"): string | undefined {
|
|
81
|
+
try {
|
|
82
|
+
const value = settings.get(key);
|
|
83
|
+
return typeof value === "string" ? value : undefined;
|
|
84
|
+
} catch {
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function synthesizeXai(
|
|
90
|
+
params: z.infer<typeof ttsSchema>,
|
|
91
|
+
ctx: CustomToolContext,
|
|
92
|
+
outputPath: string,
|
|
93
|
+
displayPath: string,
|
|
94
|
+
codec: TtsCodec,
|
|
95
|
+
signal: AbortSignal | undefined,
|
|
96
|
+
): Promise<AgentToolResult<TtsToolDetails, typeof ttsSchema>> {
|
|
97
|
+
const creds = await resolveXAIHttpCredentials(ctx.modelRegistry);
|
|
98
|
+
if (!creds) {
|
|
99
|
+
return {
|
|
100
|
+
isError: true,
|
|
101
|
+
content: [
|
|
102
|
+
{
|
|
103
|
+
type: "text",
|
|
104
|
+
text: "No xAI credentials. Run /login → xAI Grok OAuth (SuperGrok Subscription) or set XAI_API_KEY.",
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const voiceId = params.voice_id;
|
|
111
|
+
const language = params.language;
|
|
112
|
+
const sampleRate = params.sample_rate ?? DEFAULT_XAI_SAMPLE_RATE;
|
|
113
|
+
const bitRate = params.bit_rate ?? DEFAULT_XAI_BIT_RATE;
|
|
114
|
+
|
|
115
|
+
const payload: Record<string, unknown> = {
|
|
116
|
+
text: params.text,
|
|
117
|
+
voice_id: voiceId,
|
|
118
|
+
language,
|
|
119
|
+
};
|
|
120
|
+
// Hermes tts_tool.py L926-940 — only send output_format when caller overrides a default.
|
|
121
|
+
const codecOverridden = codec !== "mp3";
|
|
122
|
+
const sampleRateOverridden = sampleRate !== DEFAULT_XAI_SAMPLE_RATE;
|
|
123
|
+
const bitRateOverridden = codec === "mp3" && bitRate !== DEFAULT_XAI_BIT_RATE;
|
|
124
|
+
if (codecOverridden || sampleRateOverridden || bitRateOverridden) {
|
|
125
|
+
const fmt: Record<string, unknown> = { codec };
|
|
126
|
+
if (sampleRate) fmt.sample_rate = sampleRate;
|
|
127
|
+
if (codec === "mp3" && bitRate) fmt.bit_rate = bitRate;
|
|
128
|
+
payload.output_format = fmt;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Compose the caller signal with a 60 s timeout fence.
|
|
132
|
+
const timeoutSignal = AbortSignal.timeout(60_000);
|
|
133
|
+
const combinedSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
134
|
+
|
|
135
|
+
const sessionId = ctx.sessionManager.getSessionId();
|
|
136
|
+
const apiKey: ApiKey = ctx.modelRegistry.resolver(creds.provider, {
|
|
137
|
+
sessionId,
|
|
138
|
+
baseUrl: creds.baseURL,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
let response: Response;
|
|
142
|
+
try {
|
|
143
|
+
response = await withAuth(
|
|
144
|
+
apiKey,
|
|
145
|
+
async key => {
|
|
146
|
+
const resp = await fetch(`${creds.baseURL}/tts`, {
|
|
147
|
+
method: "POST",
|
|
148
|
+
headers: {
|
|
149
|
+
Authorization: `Bearer ${key}`,
|
|
150
|
+
"Content-Type": "application/json",
|
|
151
|
+
"User-Agent": ohMyPiXAIUserAgent(),
|
|
152
|
+
},
|
|
153
|
+
body: JSON.stringify(payload),
|
|
154
|
+
signal: combinedSignal,
|
|
155
|
+
});
|
|
156
|
+
if (!resp.ok) {
|
|
157
|
+
const detail = await resp.text();
|
|
158
|
+
throw new ProviderHttpError(`xAI TTS failed (${resp.status}): ${detail.slice(0, 300)}`, resp.status, {
|
|
159
|
+
headers: resp.headers,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
return resp;
|
|
163
|
+
},
|
|
164
|
+
{ signal: combinedSignal },
|
|
165
|
+
);
|
|
166
|
+
} catch (error) {
|
|
167
|
+
const status = (error as { status?: unknown }).status;
|
|
168
|
+
if (error instanceof Error && typeof status === "number") {
|
|
169
|
+
return {
|
|
170
|
+
isError: true,
|
|
171
|
+
content: [{ type: "text", text: error.message }],
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
177
|
+
await Bun.write(outputPath, bytes);
|
|
178
|
+
return {
|
|
179
|
+
content: [
|
|
180
|
+
{
|
|
181
|
+
type: "text",
|
|
182
|
+
text: `Saved ${bytes.length} bytes to ${displayPath} (voice=${voiceId}, codec=${codec}, backend=xai).`,
|
|
183
|
+
},
|
|
184
|
+
],
|
|
185
|
+
details: { bytes: bytes.length, voiceId, codec, backend: "xai" },
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async function synthesizeLocal(
|
|
190
|
+
params: z.infer<typeof ttsSchema>,
|
|
191
|
+
cwd: string,
|
|
192
|
+
outputPath: string,
|
|
193
|
+
signal: AbortSignal | undefined,
|
|
194
|
+
): Promise<AgentToolResult<TtsToolDetails, typeof ttsSchema>> {
|
|
195
|
+
const modelSetting = readStringSetting("tts.localModel");
|
|
196
|
+
const modelKey = modelSetting && isTtsLocalModelKey(modelSetting) ? modelSetting : DEFAULT_TTS_LOCAL_MODEL_KEY;
|
|
197
|
+
const voice = readStringSetting("tts.localVoice") || DEFAULT_TTS_VOICE;
|
|
198
|
+
|
|
199
|
+
const audio = await ttsClient.synthesize(modelKey, params.text, { voice, signal });
|
|
200
|
+
if (!audio) {
|
|
201
|
+
return {
|
|
202
|
+
isError: true,
|
|
203
|
+
content: [
|
|
204
|
+
{
|
|
205
|
+
type: "text",
|
|
206
|
+
text: `Local TTS synthesis failed (model=${modelKey}). The on-device worker may be unavailable or the model download was interrupted.`,
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const { wavPath, substituted } = resolveLocalWavPath(outputPath);
|
|
213
|
+
const wav = encodeWav(audio.pcm, audio.sampleRate);
|
|
214
|
+
await Bun.write(wavPath, wav);
|
|
215
|
+
const displayPath = formatPathRelativeToCwd(wavPath, cwd);
|
|
216
|
+
const note = substituted
|
|
217
|
+
? ` No local MP3 encoder is bundled, so WAV (PCM16) was written instead of the requested container.`
|
|
218
|
+
: "";
|
|
219
|
+
return {
|
|
220
|
+
content: [
|
|
221
|
+
{
|
|
222
|
+
type: "text",
|
|
223
|
+
text: `Saved ${wav.length} bytes to ${displayPath} (voice=${modelKey}/${voice}, codec=wav, backend=local, ${audio.sampleRate} Hz).${note}`,
|
|
224
|
+
},
|
|
225
|
+
],
|
|
226
|
+
details: { bytes: wav.length, voiceId: `${modelKey}/${voice}`, codec: "wav", backend: "local" },
|
|
227
|
+
};
|
|
39
228
|
}
|
|
40
229
|
|
|
41
230
|
export const ttsTool: CustomTool<typeof ttsSchema, TtsToolDetails> = {
|
|
42
231
|
name: "tts",
|
|
43
|
-
label: "
|
|
232
|
+
label: "Speech Generation",
|
|
44
233
|
strict: false,
|
|
45
234
|
approval: "write",
|
|
46
235
|
description:
|
|
47
|
-
|
|
48
|
-
|
|
236
|
+
"Generate a speech audio file from text and write it to output_path. Two backends, selected by the providers.tts setting (auto|local|xai): " +
|
|
237
|
+
`local = on-device neural TTS (Kokoro-82M via the bundled ONNX runtime, no network, output is always WAV/PCM16; voice set by the tts.localVoice setting — ${KOKORO_VOICES.map(v => (v.id === DEFAULT_TTS_VOICE ? `${v.id} (default)` : v.id)).join(", ")}); ` +
|
|
238
|
+
`xai = xAI Grok Voice cloud (built-in voices: ${formatVoiceList()}; custom voice IDs accepted; MP3 or WAV). ` +
|
|
239
|
+
"auto prefers local, but routes an .mp3 request to xAI when credentials exist (only the cloud path emits MP3); " +
|
|
240
|
+
"otherwise an .mp3 path is written as a sibling .wav. xAI codec is inferred from the output_path suffix. " +
|
|
49
241
|
`Max ${XAI_MAX_TEXT_LENGTH.toLocaleString("en-US")} characters.`,
|
|
50
242
|
parameters: ttsSchema,
|
|
51
243
|
async execute(
|
|
@@ -55,99 +247,18 @@ export const ttsTool: CustomTool<typeof ttsSchema, TtsToolDetails> = {
|
|
|
55
247
|
ctx: CustomToolContext,
|
|
56
248
|
signal?: AbortSignal,
|
|
57
249
|
): Promise<AgentToolResult<TtsToolDetails, typeof ttsSchema>> {
|
|
58
|
-
const creds = await resolveXAIHttpCredentials(ctx.modelRegistry);
|
|
59
|
-
if (!creds) {
|
|
60
|
-
return {
|
|
61
|
-
isError: true,
|
|
62
|
-
content: [
|
|
63
|
-
{
|
|
64
|
-
type: "text",
|
|
65
|
-
text: "No xAI credentials. Run /login → xAI Grok OAuth (SuperGrok Subscription) or set XAI_API_KEY.",
|
|
66
|
-
},
|
|
67
|
-
],
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
250
|
const cwd = ctx.sessionManager.getCwd();
|
|
72
251
|
const outputPath = resolveToCwd(params.output_path, cwd);
|
|
73
252
|
const displayPath = formatPathRelativeToCwd(outputPath, cwd);
|
|
74
253
|
const codec: TtsCodec = outputPath.toLowerCase().endsWith(".wav") ? "wav" : "mp3";
|
|
75
|
-
const voiceId = params.voice_id;
|
|
76
|
-
const language = params.language;
|
|
77
|
-
const sampleRate = params.sample_rate ?? DEFAULT_XAI_SAMPLE_RATE;
|
|
78
|
-
const bitRate = params.bit_rate ?? DEFAULT_XAI_BIT_RATE;
|
|
79
|
-
|
|
80
|
-
const payload: Record<string, unknown> = {
|
|
81
|
-
text: params.text,
|
|
82
|
-
voice_id: voiceId,
|
|
83
|
-
language,
|
|
84
|
-
};
|
|
85
|
-
// Hermes tts_tool.py L926-940 — only send output_format when caller overrides a default.
|
|
86
|
-
const codecOverridden = codec !== "mp3";
|
|
87
|
-
const sampleRateOverridden = sampleRate !== DEFAULT_XAI_SAMPLE_RATE;
|
|
88
|
-
const bitRateOverridden = codec === "mp3" && bitRate !== DEFAULT_XAI_BIT_RATE;
|
|
89
|
-
if (codecOverridden || sampleRateOverridden || bitRateOverridden) {
|
|
90
|
-
const fmt: Record<string, unknown> = { codec };
|
|
91
|
-
if (sampleRate) fmt.sample_rate = sampleRate;
|
|
92
|
-
if (codec === "mp3" && bitRate) fmt.bit_rate = bitRate;
|
|
93
|
-
payload.output_format = fmt;
|
|
94
|
-
}
|
|
95
254
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
let response: Response;
|
|
107
|
-
try {
|
|
108
|
-
response = await withAuth(
|
|
109
|
-
apiKey,
|
|
110
|
-
async key => {
|
|
111
|
-
const resp = await fetch(`${creds.baseURL}/tts`, {
|
|
112
|
-
method: "POST",
|
|
113
|
-
headers: {
|
|
114
|
-
Authorization: `Bearer ${key}`,
|
|
115
|
-
"Content-Type": "application/json",
|
|
116
|
-
"User-Agent": ohMyPiXAIUserAgent(),
|
|
117
|
-
},
|
|
118
|
-
body: JSON.stringify(payload),
|
|
119
|
-
signal: combinedSignal,
|
|
120
|
-
});
|
|
121
|
-
if (!resp.ok) {
|
|
122
|
-
const detail = await resp.text();
|
|
123
|
-
throw new ProviderHttpError(`xAI TTS failed (${resp.status}): ${detail.slice(0, 300)}`, resp.status, {
|
|
124
|
-
headers: resp.headers,
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
return resp;
|
|
128
|
-
},
|
|
129
|
-
{ signal: combinedSignal },
|
|
130
|
-
);
|
|
131
|
-
} catch (error) {
|
|
132
|
-
const status = (error as { status?: unknown }).status;
|
|
133
|
-
if (error instanceof Error && typeof status === "number") {
|
|
134
|
-
return {
|
|
135
|
-
isError: true,
|
|
136
|
-
content: [{ type: "text", text: error.message }],
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
throw error;
|
|
140
|
-
}
|
|
141
|
-
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
142
|
-
await Bun.write(outputPath, bytes);
|
|
143
|
-
return {
|
|
144
|
-
content: [
|
|
145
|
-
{
|
|
146
|
-
type: "text",
|
|
147
|
-
text: `Saved ${bytes.length} bytes to ${displayPath} (voice=${voiceId}, codec=${codec}).`,
|
|
148
|
-
},
|
|
149
|
-
],
|
|
150
|
-
details: { bytes: bytes.length, voiceId, codec },
|
|
151
|
-
};
|
|
255
|
+
const preference = readStringSetting("providers.tts") ?? "auto";
|
|
256
|
+
// Only resolve xAI creds when they can affect routing (skip for an explicit local preference).
|
|
257
|
+
const hasXaiCreds =
|
|
258
|
+
preference === "local" ? false : (await resolveXAIHttpCredentials(ctx.modelRegistry)) !== null;
|
|
259
|
+
const backend = resolveTtsBackend({ preference, wantsMp3: codec === "mp3", hasXaiCreds });
|
|
260
|
+
|
|
261
|
+
if (backend === "local") return synthesizeLocal(params, cwd, outputPath, signal);
|
|
262
|
+
return synthesizeXai(params, ctx, outputPath, displayPath, codec, signal);
|
|
152
263
|
},
|
|
153
264
|
};
|
package/src/tools/write.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { formatHashlineHeader, stripHashlinePrefixes } from "@oh-my-pi/hashline"
|
|
|
6
6
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core";
|
|
7
7
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
8
8
|
import { isEnoent, isRecord, prompt, untilAborted } from "@oh-my-pi/pi-utils";
|
|
9
|
-
import
|
|
9
|
+
import { z } from "zod/v4";
|
|
10
10
|
|
|
11
11
|
import { canonicalSnapshotKey, getFileSnapshotStore } from "../edit/file-snapshot-store";
|
|
12
12
|
import { normalizeToLF } from "../edit/normalize";
|
|
@@ -35,7 +35,7 @@ import {
|
|
|
35
35
|
import { invalidateFsScanAfterWrite } from "./fs-cache-invalidation";
|
|
36
36
|
import { type OutputMeta, outputMeta } from "./output-meta";
|
|
37
37
|
import { formatPathRelativeToCwd, isInternalUrlPath } from "./path-utils";
|
|
38
|
-
import { enforcePlanModeWrite, resolvePlanPath } from "./plan-mode-guard";
|
|
38
|
+
import { enforcePlanModeWrite, resolvePlanPath, unwrapHashlineHeaderPath } from "./plan-mode-guard";
|
|
39
39
|
import {
|
|
40
40
|
cachedRenderedString,
|
|
41
41
|
createRenderedStringCache,
|
|
@@ -63,6 +63,7 @@ import { ToolError } from "./tool-errors";
|
|
|
63
63
|
import { toolResult } from "./tool-result";
|
|
64
64
|
|
|
65
65
|
const LOOSE_HASHLINE_HEADER_RE = /^\s*\[[^#\r\n]+#[^ \t\r\n]*\]\s*$/;
|
|
66
|
+
const EXECUTABLE_NOTICE = "[Notice: Made executable via chmod +x]";
|
|
66
67
|
|
|
67
68
|
let fflateModulePromise: Promise<typeof import("fflate")> | undefined;
|
|
68
69
|
async function loadFflate(): Promise<typeof import("fflate")> {
|
|
@@ -818,11 +819,20 @@ export class WriteTool implements AgentTool<typeof writeSchema, WriteToolDetails
|
|
|
818
819
|
}
|
|
819
820
|
async execute(
|
|
820
821
|
_toolCallId: string,
|
|
821
|
-
{ path, content }: WriteParams,
|
|
822
|
+
{ path: rawPath, content }: WriteParams,
|
|
822
823
|
signal?: AbortSignal,
|
|
823
824
|
_onUpdate?: AgentToolUpdateCallback<WriteToolDetails>,
|
|
824
825
|
context?: AgentToolContext,
|
|
825
826
|
): Promise<AgentToolResult<WriteToolDetails>> {
|
|
827
|
+
// Strip a hashline `[path#TAG]` wrapper up front so every downstream
|
|
828
|
+
// decision (scheme routing, internal-URL handler dispatch, plan-mode
|
|
829
|
+
// guard, plan path resolution, ACP bridge routing) sees the same
|
|
830
|
+
// filesystem target. Without this, a model that pastes a `read`
|
|
831
|
+
// header as the `path` arg would slip past `isInternalUrlPath`
|
|
832
|
+
// (which fails on a leading `[`) and the bridge router would send a
|
|
833
|
+
// `[local://scratch.md#ABCD]` write to the editor instead of the
|
|
834
|
+
// session-local sandbox.
|
|
835
|
+
const path = unwrapHashlineHeaderPath(rawPath);
|
|
826
836
|
return untilAborted(signal, async () => {
|
|
827
837
|
// Strip hashline display prefixes ([PATH#HASH] + LINE:) if the model copied them from read output
|
|
828
838
|
const { text: cleanContent, stripped } = stripWriteContent(this.session, content);
|
|
@@ -932,6 +942,9 @@ export class WriteTool implements AgentTool<typeof writeSchema, WriteToolDetails
|
|
|
932
942
|
if (stripped) {
|
|
933
943
|
resultText += `\nNote: auto-stripped hashline display prefixes from content before writing.`;
|
|
934
944
|
}
|
|
945
|
+
if (madeExecutable) {
|
|
946
|
+
resultText += `\n${EXECUTABLE_NOTICE}`;
|
|
947
|
+
}
|
|
935
948
|
return {
|
|
936
949
|
content: [{ type: "text", text: resultText }],
|
|
937
950
|
details: { resolvedPath: absolutePath, madeExecutable: madeExecutable || undefined },
|
|
@@ -950,6 +963,9 @@ export class WriteTool implements AgentTool<typeof writeSchema, WriteToolDetails
|
|
|
950
963
|
if (stripped) {
|
|
951
964
|
resultText += `\nNote: auto-stripped hashline display prefixes from content before writing.`;
|
|
952
965
|
}
|
|
966
|
+
if (madeExecutable) {
|
|
967
|
+
resultText += `\n${EXECUTABLE_NOTICE}`;
|
|
968
|
+
}
|
|
953
969
|
if (!diagnostics) {
|
|
954
970
|
return {
|
|
955
971
|
content: [{ type: "text", text: resultText }],
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { getTinyModelsCacheDir } from "@oh-my-pi/pi-utils";
|
|
4
|
+
import { getTtsLocalModelSpec } from "./models";
|
|
5
|
+
import { isTtsRuntimeCached } from "./runtime";
|
|
6
|
+
import { ttsClient } from "./tts-client";
|
|
7
|
+
|
|
8
|
+
export interface TtsDownloadProgress {
|
|
9
|
+
stage: string;
|
|
10
|
+
/** Integer 0–100 download percent when known. */
|
|
11
|
+
percent?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Whether the selected local TTS model and the side Kokoro runtime are already
|
|
16
|
+
* present. transformers.js stores `main`-revision files at
|
|
17
|
+
* `<cacheDir>/<repo>/...`, so any `.onnx` weight under the repo dir means the
|
|
18
|
+
* model weights can load without a network fetch; the Kokoro package runtime is
|
|
19
|
+
* version-keyed separately and must also exist before setup can report ready.
|
|
20
|
+
*/
|
|
21
|
+
export async function isTtsModelCached(modelKey: string): Promise<boolean> {
|
|
22
|
+
const spec = getTtsLocalModelSpec(modelKey);
|
|
23
|
+
if (!spec) return false;
|
|
24
|
+
const repoDir = path.join(getTinyModelsCacheDir(), ...spec.repo.split("/"));
|
|
25
|
+
try {
|
|
26
|
+
const entries = await fs.readdir(repoDir, { recursive: true });
|
|
27
|
+
const hasWeights = entries.some(entry => typeof entry === "string" && entry.endsWith(".onnx"));
|
|
28
|
+
return hasWeights && (await isTtsRuntimeCached());
|
|
29
|
+
} catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Ensure the selected local TTS model is downloaded into the transformers.js
|
|
36
|
+
* cache (and warm in the worker), streaming integer-percent Hub progress. The
|
|
37
|
+
* worker resolves the request once every model file is cached. Returns `false`
|
|
38
|
+
* if the worker is unavailable or the download failed.
|
|
39
|
+
*/
|
|
40
|
+
export async function downloadTtsModel(
|
|
41
|
+
modelKey: string,
|
|
42
|
+
onProgress?: (progress: TtsDownloadProgress) => void,
|
|
43
|
+
signal?: AbortSignal,
|
|
44
|
+
): Promise<boolean> {
|
|
45
|
+
const spec = getTtsLocalModelSpec(modelKey);
|
|
46
|
+
if (!spec) return false;
|
|
47
|
+
onProgress?.({ stage: `Preparing ${spec.label}...` });
|
|
48
|
+
return ttsClient.downloadModel(spec.key, {
|
|
49
|
+
signal,
|
|
50
|
+
onProgress: event => {
|
|
51
|
+
if (event.status === "ready" || event.status === "done") {
|
|
52
|
+
onProgress?.({ stage: `${spec.label} ready`, percent: 100 });
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const percent =
|
|
56
|
+
typeof event.total === "number" && event.total > 0 && typeof event.loaded === "number"
|
|
57
|
+
? Math.round((event.loaded / event.total) * 100)
|
|
58
|
+
: typeof event.progress === "number"
|
|
59
|
+
? Math.round(event.progress)
|
|
60
|
+
: undefined;
|
|
61
|
+
onProgress?.({ stage: `Downloading ${spec.label}`, percent });
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
}
|
package/src/tts/index.ts
ADDED