@oh-my-pi/pi-coding-agent 15.10.10 → 15.10.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +142 -7
- package/dist/cli.js +23108 -0
- package/dist/tokenizers.linux-x64-gnu-xcjh3jwk.node +0 -0
- package/dist/types/async/job-manager.d.ts +18 -0
- package/dist/types/cli/args.d.ts +2 -1
- package/dist/types/cli/dry-balance-cli.d.ts +1 -1
- package/dist/types/cli/gallery-cli.d.ts +1 -1
- package/dist/types/cli/gallery-fixtures/types.d.ts +1 -1
- package/dist/types/cli/usage-cli.d.ts +72 -0
- package/dist/types/cli-commands.d.ts +12 -0
- package/dist/types/commands/launch.d.ts +5 -1
- package/dist/types/commands/read.d.ts +1 -1
- package/dist/types/commands/usage.d.ts +25 -0
- package/dist/types/config/api-key-resolver.d.ts +3 -0
- package/dist/types/config/append-only-context-mode.d.ts +2 -1
- package/dist/types/config/model-discovery.d.ts +55 -0
- package/dist/types/config/model-registry.d.ts +8 -219
- package/dist/types/config/model-resolver.d.ts +34 -10
- package/dist/types/config/model-roles.d.ts +28 -0
- package/dist/types/config/models-config-schema.d.ts +523 -42
- package/dist/types/config/models-config.d.ts +385 -0
- package/dist/types/config/settings-schema.d.ts +41 -8
- package/dist/types/config/settings.d.ts +8 -1
- package/dist/types/debug/log-viewer.d.ts +1 -1
- package/dist/types/debug/raw-sse.d.ts +1 -1
- package/dist/types/edit/hashline/noop-loop-guard.d.ts +72 -0
- package/dist/types/eval/backend.d.ts +0 -2
- package/dist/types/eval/idle-timeout.d.ts +0 -4
- package/dist/types/eval/js/shared/rewrite-imports.d.ts +6 -6
- package/dist/types/eval/py/executor.d.ts +5 -0
- package/dist/types/eval/py/kernel.d.ts +6 -1
- package/dist/types/eval/py/runtime.d.ts +9 -0
- package/dist/types/exec/bash-executor.d.ts +2 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/extensions/runner.d.ts +3 -2
- package/dist/types/extensibility/extensions/types.d.ts +6 -3
- package/dist/types/hindsight/mental-models.d.ts +17 -8
- package/dist/types/internal-urls/artifact-protocol.d.ts +2 -2
- package/dist/types/internal-urls/types.d.ts +1 -1
- package/dist/types/lsp/edits.d.ts +9 -0
- package/dist/types/lsp/index.d.ts +2 -2
- package/dist/types/lsp/types.d.ts +2 -0
- package/dist/types/lsp/utils.d.ts +3 -0
- package/dist/types/mcp/json-rpc.d.ts +5 -0
- package/dist/types/memory-backend/index.d.ts +1 -0
- package/dist/types/memory-backend/runtime.d.ts +4 -0
- package/dist/types/memory-backend/types.d.ts +66 -1
- package/dist/types/mnemopi/state.d.ts +11 -1
- package/dist/types/modes/components/agent-dashboard.d.ts +1 -1
- package/dist/types/modes/components/assistant-message.d.ts +3 -1
- package/dist/types/modes/components/bash-execution.d.ts +1 -1
- package/dist/types/modes/components/copy-selector.d.ts +1 -1
- package/dist/types/modes/components/dynamic-border.d.ts +1 -1
- package/dist/types/modes/components/extensions/extension-dashboard.d.ts +1 -1
- package/dist/types/modes/components/extensions/extension-list.d.ts +1 -1
- package/dist/types/modes/components/extensions/inspector-panel.d.ts +1 -1
- package/dist/types/modes/components/footer.d.ts +1 -1
- package/dist/types/modes/components/hook-editor.d.ts +5 -0
- package/dist/types/modes/components/hook-input.d.ts +4 -0
- package/dist/types/modes/components/hook-selector.d.ts +1 -1
- package/dist/types/modes/components/model-selector.d.ts +1 -1
- package/dist/types/modes/components/plan-review-overlay.d.ts +1 -1
- package/dist/types/modes/components/session-observer-overlay.d.ts +1 -1
- package/dist/types/modes/components/session-selector.d.ts +1 -1
- package/dist/types/modes/components/status-line/component.d.ts +1 -1
- package/dist/types/modes/components/tiny-title-download-progress.d.ts +1 -1
- package/dist/types/modes/components/transcript-container.d.ts +25 -6
- package/dist/types/modes/components/tree-selector.d.ts +1 -1
- package/dist/types/modes/components/user-message-selector.d.ts +1 -1
- package/dist/types/modes/components/user-message.d.ts +2 -1
- package/dist/types/modes/components/visual-truncate.d.ts +1 -1
- package/dist/types/modes/components/welcome.d.ts +19 -3
- package/dist/types/modes/controllers/mcp-command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/streaming-reveal.d.ts +1 -1
- package/dist/types/modes/index.d.ts +3 -3
- package/dist/types/modes/interactive-mode.d.ts +8 -3
- package/dist/types/modes/oauth-manual-input.d.ts +7 -0
- package/dist/types/modes/rpc/rpc-client.d.ts +39 -2
- package/dist/types/modes/rpc/rpc-mode.d.ts +31 -2
- package/dist/types/modes/rpc/rpc-subagents.d.ts +24 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +75 -1
- package/dist/types/modes/setup-wizard/index.d.ts +5 -1
- package/dist/types/modes/setup-wizard/lazy.d.ts +2 -0
- package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/types.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +1 -1
- package/dist/types/modes/setup-wizard/wizard-overlay.d.ts +1 -1
- package/dist/types/modes/types.d.ts +4 -1
- package/dist/types/secrets/index.d.ts +1 -1
- package/dist/types/secrets/obfuscator.d.ts +8 -2
- package/dist/types/session/agent-session.d.ts +15 -3
- package/dist/types/session/auth-broker-config.d.ts +4 -0
- package/dist/types/session/session-manager.d.ts +1 -1
- package/dist/types/session/streaming-output.d.ts +23 -0
- package/dist/types/slash-commands/acp-builtins.d.ts +16 -0
- package/dist/types/slash-commands/builtin-registry.d.ts +1 -0
- package/dist/types/slash-commands/helpers/stats-dashboard.d.ts +13 -0
- package/dist/types/slash-commands/types.d.ts +1 -1
- package/dist/types/ssh/connection-manager.d.ts +8 -0
- package/dist/types/system-prompt.d.ts +2 -0
- package/dist/types/task/executor.d.ts +1 -0
- package/dist/types/task/index.d.ts +2 -2
- package/dist/types/task/parallel.d.ts +2 -2
- package/dist/types/task/types.d.ts +8 -0
- package/dist/types/task/worktree.d.ts +2 -0
- package/dist/types/thinking.d.ts +4 -0
- package/dist/types/tiny/title-client.d.ts +11 -0
- package/dist/types/tiny/title-protocol.d.ts +1 -0
- package/dist/types/tools/ask.d.ts +4 -0
- package/dist/types/tools/conflict-detect.d.ts +16 -0
- package/dist/types/tools/github-cache.d.ts +7 -0
- package/dist/types/tools/index.d.ts +6 -0
- package/dist/types/tools/sqlite-reader.d.ts +3 -0
- package/dist/types/tui/output-block.d.ts +3 -3
- package/dist/types/utils/changelog.d.ts +8 -0
- package/dist/types/utils/git.d.ts +15 -2
- package/dist/types/utils/title-generator.d.ts +3 -2
- package/dist/types/web/scrapers/readthedocs.d.ts +3 -0
- package/dist/types/web/scrapers/types.d.ts +12 -0
- package/dist/types/web/search/providers/codex.d.ts +1 -1
- package/dist/types/web/search/providers/gemini.d.ts +1 -1
- package/examples/extensions/tools.ts +5 -4
- package/package.json +14 -11
- package/scripts/build-binary.ts +18 -23
- package/scripts/bundle-dist.ts +81 -0
- package/scripts/{dev-launch → omp} +1 -1
- package/scripts/{dev-launch-preload.ts → omp.ts} +1 -1
- package/src/async/job-manager.ts +57 -3
- package/src/auto-thinking/classifier.ts +1 -0
- package/src/autoresearch/dashboard.ts +1 -1
- package/src/autoresearch/prompt-setup.md +6 -6
- package/src/autoresearch/prompt.md +6 -6
- package/src/capability/fs.ts +10 -0
- package/src/cli/args.ts +4 -1
- package/src/cli/auth-gateway-cli.ts +1 -3
- package/src/cli/dry-balance-cli.ts +1 -1
- package/src/cli/gallery-cli.ts +1 -1
- package/src/cli/gallery-fixtures/fs.ts +1 -1
- package/src/cli/gallery-fixtures/types.ts +5 -1
- package/src/cli/list-models.ts +2 -1
- package/src/cli/usage-cli.ts +603 -0
- package/src/cli-commands.ts +30 -0
- package/src/cli.ts +76 -13
- package/src/commands/complete.ts +1 -1
- package/src/commands/launch.ts +5 -1
- package/src/commands/read.ts +6 -3
- package/src/commands/usage.ts +35 -0
- package/src/commit/agentic/agent.ts +1 -1
- package/src/commit/model-selection.ts +4 -3
- package/src/config/api-key-resolver.ts +8 -6
- package/src/config/append-only-context-mode.ts +6 -12
- package/src/config/model-discovery.ts +554 -0
- package/src/config/model-registry.ts +320 -1041
- package/src/config/model-resolver.ts +173 -156
- package/src/config/model-roles.ts +74 -0
- package/src/config/models-config-schema.ts +57 -8
- package/src/config/models-config.ts +129 -0
- package/src/config/settings-schema.ts +61 -19
- package/src/config/settings.ts +98 -4
- package/src/dap/client.ts +124 -37
- package/src/dap/session.ts +259 -158
- package/src/debug/log-viewer.ts +1 -1
- package/src/debug/raw-sse.ts +1 -1
- package/src/edit/diff.ts +47 -3
- package/src/edit/hashline/block-resolver.ts +20 -1
- package/src/edit/hashline/diff.ts +36 -1
- package/src/edit/hashline/execute.ts +47 -4
- package/src/edit/hashline/noop-loop-guard.ts +99 -0
- package/src/edit/index.ts +16 -1
- package/src/edit/modes/patch.ts +52 -0
- package/src/edit/modes/replace.ts +56 -22
- package/src/edit/notebook.ts +22 -2
- package/src/edit/renderer.ts +36 -10
- package/src/eval/__tests__/completion-bridge.test.ts +1 -1
- package/src/eval/backend.ts +0 -2
- package/src/eval/completion-bridge.ts +3 -1
- package/src/eval/idle-timeout.ts +2 -9
- package/src/eval/js/context-manager.ts +6 -8
- package/src/eval/js/executor.ts +6 -2
- package/src/eval/js/index.ts +0 -2
- package/src/eval/js/shared/helpers.ts +5 -6
- package/src/eval/js/shared/local-module-loader.ts +1 -1
- package/src/eval/js/shared/prelude.txt +62 -1
- package/src/eval/js/shared/rewrite-imports.ts +40 -22
- package/src/eval/js/shared/runtime.ts +1 -1
- package/src/eval/py/executor.ts +29 -7
- package/src/eval/py/index.ts +6 -3
- package/src/eval/py/kernel.ts +43 -4
- package/src/eval/py/runner.py +107 -3
- package/src/eval/py/runtime.ts +37 -0
- package/src/exec/bash-executor.ts +85 -4
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +3 -1
- package/src/extensibility/extensions/get-commands-handler.ts +2 -1
- package/src/extensibility/extensions/runner.ts +6 -1
- package/src/extensibility/extensions/types.ts +6 -2
- package/src/extensibility/plugins/legacy-pi-compat.ts +20 -3
- package/src/hindsight/bank.ts +17 -2
- package/src/hindsight/mental-models.ts +59 -12
- package/src/hindsight/state.ts +6 -1
- package/src/internal-urls/artifact-protocol.ts +11 -2
- package/src/internal-urls/docs-index.generated.ts +11 -11
- package/src/internal-urls/issue-pr-protocol.ts +12 -5
- package/src/internal-urls/router.ts +1 -1
- package/src/internal-urls/types.ts +1 -1
- package/src/lib/xai-http.ts +1 -1
- package/src/lsp/client.ts +118 -38
- package/src/lsp/clients/biome-client.ts +101 -39
- package/src/lsp/edits.ts +143 -95
- package/src/lsp/index.ts +31 -22
- package/src/lsp/render.ts +1 -1
- package/src/lsp/types.ts +2 -0
- package/src/lsp/utils.ts +28 -10
- package/src/main.ts +183 -23
- package/src/mcp/json-rpc.ts +35 -5
- package/src/mcp/transports/stdio.ts +7 -1
- package/src/memories/index.ts +4 -1
- package/src/memory-backend/index.ts +1 -0
- package/src/memory-backend/local-backend.ts +9 -0
- package/src/memory-backend/off-backend.ts +9 -0
- package/src/memory-backend/runtime.ts +66 -0
- package/src/memory-backend/types.ts +81 -1
- package/src/mnemopi/backend.ts +176 -7
- package/src/mnemopi/state.ts +38 -2
- package/src/modes/acp/acp-agent.ts +119 -11
- package/src/modes/components/agent-dashboard.ts +10 -7
- package/src/modes/components/assistant-message.ts +32 -28
- package/src/modes/components/bash-execution.ts +1 -1
- package/src/modes/components/copy-selector.ts +1 -1
- package/src/modes/components/diff.ts +13 -2
- package/src/modes/components/dynamic-border.ts +12 -3
- package/src/modes/components/extensions/extension-dashboard.ts +8 -5
- package/src/modes/components/extensions/extension-list.ts +1 -1
- package/src/modes/components/extensions/inspector-panel.ts +1 -1
- package/src/modes/components/footer.ts +4 -2
- package/src/modes/components/history-search.ts +1 -1
- package/src/modes/components/hook-editor.ts +8 -0
- package/src/modes/components/hook-input.ts +8 -0
- package/src/modes/components/hook-selector.ts +2 -2
- package/src/modes/components/model-selector.ts +4 -2
- package/src/modes/components/plan-review-overlay.ts +1 -1
- package/src/modes/components/session-observer-overlay.ts +2 -2
- package/src/modes/components/session-selector.ts +1 -1
- package/src/modes/components/settings-selector.ts +5 -1
- package/src/modes/components/status-line/component.ts +119 -35
- package/src/modes/components/tiny-title-download-progress.ts +1 -1
- package/src/modes/components/transcript-container.ts +258 -53
- package/src/modes/components/tree-selector.ts +3 -3
- package/src/modes/components/user-message-selector.ts +1 -1
- package/src/modes/components/user-message.ts +17 -5
- package/src/modes/components/visual-truncate.ts +1 -1
- package/src/modes/components/welcome.ts +108 -26
- package/src/modes/controllers/command-controller.ts +11 -4
- package/src/modes/controllers/event-controller.ts +73 -4
- package/src/modes/controllers/input-controller.ts +2 -1
- package/src/modes/controllers/mcp-command-controller.ts +39 -4
- package/src/modes/controllers/selector-controller.ts +1 -1
- package/src/modes/controllers/streaming-reveal.ts +85 -18
- package/src/modes/index.ts +3 -21
- package/src/modes/interactive-mode.ts +42 -18
- package/src/modes/oauth-manual-input.ts +30 -3
- package/src/modes/rpc/rpc-client.ts +154 -3
- package/src/modes/rpc/rpc-mode.ts +97 -12
- package/src/modes/rpc/rpc-subagents.ts +265 -0
- package/src/modes/rpc/rpc-types.ts +81 -1
- package/src/modes/setup-wizard/index.ts +12 -2
- package/src/modes/setup-wizard/lazy.ts +16 -0
- package/src/modes/setup-wizard/scenes/glyph.ts +1 -1
- package/src/modes/setup-wizard/scenes/providers.ts +1 -1
- package/src/modes/setup-wizard/scenes/sign-in.ts +1 -1
- package/src/modes/setup-wizard/scenes/theme.ts +1 -1
- package/src/modes/setup-wizard/scenes/types.ts +1 -1
- package/src/modes/setup-wizard/scenes/web-search.ts +1 -1
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/types.ts +4 -1
- package/src/prompts/agents/explore.md +2 -2
- package/src/prompts/agents/librarian.md +1 -2
- package/src/prompts/agents/oracle.md +1 -1
- package/src/prompts/agents/plan.md +5 -5
- package/src/prompts/agents/task.md +5 -5
- package/src/prompts/ci-green-request.md +5 -7
- package/src/prompts/goals/goal-budget-limit.md +2 -2
- package/src/prompts/goals/goal-continuation.md +4 -4
- package/src/prompts/goals/goal-mode-active.md +1 -1
- package/src/prompts/memories/read-path.md +1 -1
- package/src/prompts/memories/stage_one_system.md +2 -2
- package/src/prompts/review-custom-request.md +1 -1
- package/src/prompts/system/agent-creation-architect.md +2 -2
- package/src/prompts/system/auto-continue.md +1 -1
- package/src/prompts/system/background-tan-dispatch.md +1 -1
- package/src/prompts/system/btw-user.md +2 -2
- package/src/prompts/system/commit-message-system.md +13 -1
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/eager-todo.md +2 -2
- package/src/prompts/system/irc-incoming.md +1 -1
- package/src/prompts/system/manual-continue.md +1 -1
- package/src/prompts/system/omfg-user.md +3 -4
- package/src/prompts/system/orchestrate-notice.md +9 -9
- package/src/prompts/system/plan-mode-active.md +4 -4
- package/src/prompts/system/plan-mode-subagent.md +4 -5
- package/src/prompts/system/plan-mode-tool-decision-reminder.md +1 -1
- package/src/prompts/system/project-prompt.md +2 -2
- package/src/prompts/system/subagent-system-prompt.md +4 -4
- package/src/prompts/system/system-prompt.md +13 -24
- package/src/prompts/system/title-system.md +2 -2
- package/src/prompts/system/ttsr-tool-reminder.md +1 -1
- package/src/prompts/system/workflow-notice.md +1 -1
- package/src/prompts/tools/ast-edit.md +1 -1
- package/src/prompts/tools/ast-grep.md +2 -2
- package/src/prompts/tools/bash.md +5 -7
- package/src/prompts/tools/browser.md +7 -7
- package/src/prompts/tools/debug.md +1 -1
- package/src/prompts/tools/eval.md +3 -3
- package/src/prompts/tools/find.md +0 -1
- package/src/prompts/tools/github.md +8 -7
- package/src/prompts/tools/goal.md +1 -1
- package/src/prompts/tools/image-gen.md +1 -1
- package/src/prompts/tools/inspect-image-system.md +1 -1
- package/src/prompts/tools/irc.md +15 -15
- package/src/prompts/tools/lsp.md +2 -2
- package/src/prompts/tools/patch.md +2 -2
- package/src/prompts/tools/read.md +3 -4
- package/src/prompts/tools/recall.md +1 -1
- package/src/prompts/tools/reflect.md +1 -1
- package/src/prompts/tools/render-mermaid.md +2 -2
- package/src/prompts/tools/replace.md +4 -10
- package/src/prompts/tools/rewind.md +2 -2
- package/src/prompts/tools/search-tool-bm25.md +1 -9
- package/src/prompts/tools/search.md +0 -1
- package/src/prompts/tools/ssh.md +0 -4
- package/src/prompts/tools/task.md +2 -3
- package/src/prompts/tools/todo.md +1 -1
- package/src/sdk.ts +31 -11
- package/src/secrets/index.ts +8 -1
- package/src/secrets/obfuscator.ts +39 -18
- package/src/session/agent-session.ts +223 -64
- package/src/session/auth-broker-config.ts +30 -1
- package/src/session/session-manager.ts +2 -2
- package/src/session/streaming-output.ts +188 -11
- package/src/slash-commands/acp-builtins.ts +24 -0
- package/src/slash-commands/builtin-registry.ts +40 -0
- package/src/slash-commands/helpers/stats-dashboard.ts +85 -0
- package/src/slash-commands/types.ts +1 -1
- package/src/ssh/connection-manager.ts +27 -0
- package/src/system-prompt.ts +14 -0
- package/src/task/commands.ts +2 -1
- package/src/task/executor.ts +74 -65
- package/src/task/index.ts +146 -68
- package/src/task/parallel.ts +3 -3
- package/src/task/render.ts +20 -5
- package/src/task/types.ts +9 -0
- package/src/task/worktree.ts +64 -56
- package/src/thinking.ts +9 -1
- package/src/tiny/title-client.ts +60 -16
- package/src/tiny/title-protocol.ts +1 -1
- package/src/tiny/worker.ts +6 -4
- package/src/tools/archive-reader.ts +30 -2
- package/src/tools/ask.ts +104 -21
- package/src/tools/ast-edit.ts +25 -5
- package/src/tools/auto-generated-guard.ts +20 -3
- package/src/tools/bash-interactive.ts +27 -7
- package/src/tools/bash.ts +100 -18
- package/src/tools/browser/launch.ts +11 -2
- package/src/tools/browser/readable.ts +19 -2
- package/src/tools/browser/registry.ts +4 -1
- package/src/tools/browser/render.ts +2 -2
- package/src/tools/browser/tab-supervisor.ts +55 -16
- package/src/tools/conflict-detect.ts +50 -4
- package/src/tools/debug.ts +1 -1
- package/src/tools/eval-render.ts +5 -5
- package/src/tools/eval.ts +0 -2
- package/src/tools/fetch.ts +33 -10
- package/src/tools/gh-cache-invalidation.ts +63 -8
- package/src/tools/gh-renderer.ts +1 -1
- package/src/tools/gh.ts +172 -29
- package/src/tools/github-cache.ts +70 -6
- package/src/tools/image-gen.ts +14 -13
- package/src/tools/index.ts +13 -1
- package/src/tools/inspect-image.ts +1 -0
- package/src/tools/irc.ts +5 -1
- package/src/tools/job.ts +1 -1
- package/src/tools/read.ts +202 -61
- package/src/tools/render-utils.ts +3 -3
- package/src/tools/resolve.ts +1 -1
- package/src/tools/search.ts +92 -29
- package/src/tools/sqlite-reader.ts +17 -5
- package/src/tools/ssh.ts +8 -8
- package/src/tools/todo.ts +38 -8
- package/src/tools/write.ts +118 -18
- package/src/tui/output-block.ts +4 -4
- package/src/utils/changelog.ts +27 -1
- package/src/utils/commit-message-generator.ts +1 -0
- package/src/utils/file-mentions.ts +2 -1
- package/src/utils/git.ts +267 -13
- package/src/utils/title-generator.ts +24 -5
- package/src/web/scrapers/arxiv.ts +1 -1
- package/src/web/scrapers/go-pkg.ts +1 -1
- package/src/web/scrapers/iacr.ts +1 -1
- package/src/web/scrapers/readthedocs.ts +1 -1
- package/src/web/scrapers/twitter.ts +2 -1
- package/src/web/scrapers/types.ts +87 -8
- package/src/web/scrapers/wikipedia.ts +1 -1
- package/src/web/scrapers/youtube.ts +6 -1
- package/src/web/search/index.ts +1 -1
- package/src/web/search/providers/codex.ts +2 -1
- package/src/web/search/providers/gemini.ts +2 -3
- package/src/web/search/render.ts +8 -6
- package/dist/types/config/model-equivalence.d.ts +0 -24
- package/dist/types/config/model-id-affixes.d.ts +0 -12
- package/dist/types/config/model-provider-priority.d.ts +0 -1
- package/dist/types/exec/idle-timeout-watchdog.d.ts +0 -18
- package/src/config/model-equivalence.ts +0 -875
- package/src/config/model-id-affixes.ts +0 -81
- package/src/config/model-provider-priority.ts +0 -56
- package/src/exec/idle-timeout-watchdog.ts +0 -126
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* models.json config file handle and provider configuration validation.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Api, ModelSpec } from "@oh-my-pi/pi-ai/types";
|
|
6
|
+
import { ConfigFile } from "./config-file";
|
|
7
|
+
import {
|
|
8
|
+
type ModelsConfig,
|
|
9
|
+
ModelsConfigSchema,
|
|
10
|
+
type ProviderAuthMode,
|
|
11
|
+
type ProviderDiscovery,
|
|
12
|
+
} from "./models-config-schema";
|
|
13
|
+
|
|
14
|
+
export type ProviderValidationMode = "models-config" | "runtime-register";
|
|
15
|
+
|
|
16
|
+
export interface ProviderValidationModel {
|
|
17
|
+
id: string;
|
|
18
|
+
api?: Api;
|
|
19
|
+
contextWindow?: number;
|
|
20
|
+
maxTokens?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ProviderValidationConfig {
|
|
24
|
+
baseUrl?: string;
|
|
25
|
+
headers?: Record<string, string>;
|
|
26
|
+
apiKey?: string;
|
|
27
|
+
api?: Api;
|
|
28
|
+
auth?: ProviderAuthMode;
|
|
29
|
+
oauthConfigured?: boolean;
|
|
30
|
+
discovery?: ProviderDiscovery;
|
|
31
|
+
compat?: ModelSpec<Api>["compat"];
|
|
32
|
+
disableStrictTools?: boolean;
|
|
33
|
+
modelOverrides?: Record<string, unknown>;
|
|
34
|
+
models: ProviderValidationModel[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function validateProviderConfiguration(
|
|
38
|
+
providerName: string,
|
|
39
|
+
config: ProviderValidationConfig,
|
|
40
|
+
mode: ProviderValidationMode,
|
|
41
|
+
): void {
|
|
42
|
+
const hasProviderApi = !!config.api;
|
|
43
|
+
const models = config.models;
|
|
44
|
+
|
|
45
|
+
if (models.length === 0) {
|
|
46
|
+
if (mode === "models-config") {
|
|
47
|
+
const hasModelOverrides = config.modelOverrides && Object.keys(config.modelOverrides).length > 0;
|
|
48
|
+
if (
|
|
49
|
+
!config.baseUrl &&
|
|
50
|
+
!config.headers &&
|
|
51
|
+
!config.compat &&
|
|
52
|
+
!config.apiKey &&
|
|
53
|
+
!config.disableStrictTools &&
|
|
54
|
+
!hasModelOverrides &&
|
|
55
|
+
!config.discovery
|
|
56
|
+
) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Provider ${providerName}: must specify "baseUrl", "headers", "apiKey", "compat", "disableStrictTools", "modelOverrides", "discovery", or "models"`,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
} else {
|
|
63
|
+
if (!config.baseUrl) {
|
|
64
|
+
throw new Error(`Provider ${providerName}: "baseUrl" is required when defining custom models.`);
|
|
65
|
+
}
|
|
66
|
+
const requiresAuth =
|
|
67
|
+
mode === "runtime-register"
|
|
68
|
+
? !config.apiKey && !config.oauthConfigured
|
|
69
|
+
: !config.apiKey && (config.auth ?? "apiKey") !== "none";
|
|
70
|
+
if (requiresAuth) {
|
|
71
|
+
throw new Error(
|
|
72
|
+
mode === "runtime-register"
|
|
73
|
+
? `Provider ${providerName}: "apiKey" or "oauth" is required when defining models.`
|
|
74
|
+
: `Provider ${providerName}: "apiKey" is required when defining custom models unless auth is "none".`,
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (mode === "models-config" && config.discovery && !config.api && config.discovery.type !== "proxy") {
|
|
80
|
+
throw new Error(`Provider ${providerName}: "api" is required when discovery is enabled at provider level.`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
for (const modelDef of models) {
|
|
84
|
+
if (!hasProviderApi && !modelDef.api) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
mode === "runtime-register"
|
|
87
|
+
? `Provider ${providerName}, model ${modelDef.id}: no "api" specified.`
|
|
88
|
+
: `Provider ${providerName}, model ${modelDef.id}: no "api" specified. Set at provider or model level.`,
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
if (!modelDef.id) {
|
|
92
|
+
throw new Error(`Provider ${providerName}: model missing "id"`);
|
|
93
|
+
}
|
|
94
|
+
if (mode === "models-config") {
|
|
95
|
+
if (modelDef.contextWindow !== undefined && modelDef.contextWindow <= 0) {
|
|
96
|
+
throw new Error(`Provider ${providerName}, model ${modelDef.id}: invalid contextWindow`);
|
|
97
|
+
}
|
|
98
|
+
if (modelDef.maxTokens !== undefined && modelDef.maxTokens <= 0) {
|
|
99
|
+
throw new Error(`Provider ${providerName}, model ${modelDef.id}: invalid maxTokens`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export const ModelsConfigFile = new ConfigFile<ModelsConfig>("models", ModelsConfigSchema).withValidation(
|
|
106
|
+
"models",
|
|
107
|
+
config => {
|
|
108
|
+
const providers = config.providers ?? {};
|
|
109
|
+
for (const providerName in providers) {
|
|
110
|
+
const providerConfig = providers[providerName];
|
|
111
|
+
validateProviderConfiguration(
|
|
112
|
+
providerName,
|
|
113
|
+
{
|
|
114
|
+
baseUrl: providerConfig.baseUrl,
|
|
115
|
+
headers: providerConfig.headers,
|
|
116
|
+
apiKey: providerConfig.apiKey,
|
|
117
|
+
api: providerConfig.api as Api | undefined,
|
|
118
|
+
auth: (providerConfig.auth ?? "apiKey") as ProviderAuthMode,
|
|
119
|
+
discovery: providerConfig.discovery as ProviderDiscovery | undefined,
|
|
120
|
+
compat: providerConfig.compat,
|
|
121
|
+
disableStrictTools: providerConfig.disableStrictTools,
|
|
122
|
+
modelOverrides: providerConfig.modelOverrides,
|
|
123
|
+
models: (providerConfig.models ?? []) as ProviderValidationModel[],
|
|
124
|
+
},
|
|
125
|
+
"models-config",
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
);
|
|
@@ -151,7 +151,7 @@ export type AnyUiMetadata = UiBase & {
|
|
|
151
151
|
|
|
152
152
|
interface BooleanDef {
|
|
153
153
|
type: "boolean";
|
|
154
|
-
default: boolean;
|
|
154
|
+
default: boolean | undefined;
|
|
155
155
|
ui?: UiBoolean;
|
|
156
156
|
}
|
|
157
157
|
|
|
@@ -246,7 +246,11 @@ export const DEFAULT_BASH_INTERCEPTOR_RULES: BashInterceptorRule[] = [
|
|
|
246
246
|
message: "Use the `edit` tool instead of awk -i inplace. It provides diff preview and fuzzy matching.",
|
|
247
247
|
},
|
|
248
248
|
{
|
|
249
|
-
|
|
249
|
+
// `>` must sit outside quoted regions (so `echo "a -> b"` passes) and be
|
|
250
|
+
// followed by a plausible filename — including `$VAR` targets; `>|`
|
|
251
|
+
// (clobber) counts as a redirect; `>&2`/`2>&1` style fd duplication is
|
|
252
|
+
// not matched.
|
|
253
|
+
pattern: "^\\s*(echo|printf|cat\\s*<<)\\s+(?:[^\"'>]|\"[^\"]*\"|'[^']*')*(?<!\\|)>{1,2}\\|?\\s*[$\\w./~\"'-]",
|
|
250
254
|
tool: "write",
|
|
251
255
|
message: "Use the `write` tool instead of echo/cat redirection. It handles encoding and provides confirmation.",
|
|
252
256
|
},
|
|
@@ -256,7 +260,6 @@ export const SETTINGS_SCHEMA = {
|
|
|
256
260
|
// ────────────────────────────────────────────────────────────────────────
|
|
257
261
|
// General settings (no UI)
|
|
258
262
|
// ────────────────────────────────────────────────────────────────────────
|
|
259
|
-
lastChangelogVersion: { type: "string", default: undefined },
|
|
260
263
|
setupVersion: { type: "number", default: 0 },
|
|
261
264
|
|
|
262
265
|
// Auth broker — credentials proxied through a remote `omp auth-broker serve`
|
|
@@ -876,7 +879,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
876
879
|
|
|
877
880
|
"retry.maxRetries": {
|
|
878
881
|
type: "number",
|
|
879
|
-
default:
|
|
882
|
+
default: 10,
|
|
880
883
|
ui: {
|
|
881
884
|
tab: "model",
|
|
882
885
|
label: "Retry Attempts",
|
|
@@ -891,7 +894,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
891
894
|
},
|
|
892
895
|
},
|
|
893
896
|
|
|
894
|
-
"retry.baseDelayMs": { type: "number", default:
|
|
897
|
+
"retry.baseDelayMs": { type: "number", default: 500 },
|
|
895
898
|
"retry.maxDelayMs": {
|
|
896
899
|
type: "number",
|
|
897
900
|
default: 5 * 60 * 1000,
|
|
@@ -1974,6 +1977,17 @@ export const SETTINGS_SCHEMA = {
|
|
|
1974
1977
|
ui: { tab: "editing", label: "LSP", description: "Enable the lsp tool for language server protocol" },
|
|
1975
1978
|
},
|
|
1976
1979
|
|
|
1980
|
+
"lsp.lazy": {
|
|
1981
|
+
type: "boolean",
|
|
1982
|
+
default: true,
|
|
1983
|
+
ui: {
|
|
1984
|
+
tab: "editing",
|
|
1985
|
+
label: "Lazy LSP Startup",
|
|
1986
|
+
description:
|
|
1987
|
+
"Start language servers on first use (lsp tool or editing a matching file type) instead of at session startup",
|
|
1988
|
+
},
|
|
1989
|
+
},
|
|
1990
|
+
|
|
1977
1991
|
"lsp.formatOnWrite": {
|
|
1978
1992
|
type: "boolean",
|
|
1979
1993
|
default: false,
|
|
@@ -2053,6 +2067,20 @@ export const SETTINGS_SCHEMA = {
|
|
|
2053
2067
|
type: "number",
|
|
2054
2068
|
default: 4 * 1024 * 1024,
|
|
2055
2069
|
},
|
|
2070
|
+
"shellMinimizer.sourceOutlineLevel": {
|
|
2071
|
+
type: "enum",
|
|
2072
|
+
values: ["default", "aggressive"] as const,
|
|
2073
|
+
default: "default",
|
|
2074
|
+
ui: {
|
|
2075
|
+
tab: "editing",
|
|
2076
|
+
label: "Shell Minimizer Source Outline",
|
|
2077
|
+
description: "Source outline mode for cat/read of source files: default or aggressive",
|
|
2078
|
+
},
|
|
2079
|
+
},
|
|
2080
|
+
"shellMinimizer.legacyFilters": {
|
|
2081
|
+
type: "boolean",
|
|
2082
|
+
default: undefined,
|
|
2083
|
+
},
|
|
2056
2084
|
|
|
2057
2085
|
// Eval (per-backend toggles; add more as new backends ship, e.g. eval.ts)
|
|
2058
2086
|
"eval.py": {
|
|
@@ -2086,6 +2114,16 @@ export const SETTINGS_SCHEMA = {
|
|
|
2086
2114
|
description: "Whether to keep IPython kernel alive across calls",
|
|
2087
2115
|
},
|
|
2088
2116
|
},
|
|
2117
|
+
"python.interpreter": {
|
|
2118
|
+
type: "string",
|
|
2119
|
+
default: "",
|
|
2120
|
+
ui: {
|
|
2121
|
+
tab: "editing",
|
|
2122
|
+
label: "Python Interpreter",
|
|
2123
|
+
description:
|
|
2124
|
+
"Optional path to an exact Python executable. When set, automatic Python runtime discovery is skipped.",
|
|
2125
|
+
},
|
|
2126
|
+
},
|
|
2089
2127
|
|
|
2090
2128
|
// ────────────────────────────────────────────────────────────────────────
|
|
2091
2129
|
// Tools
|
|
@@ -3243,21 +3281,23 @@ type Schema = typeof SETTINGS_SCHEMA;
|
|
|
3243
3281
|
export type SettingPath = keyof Schema;
|
|
3244
3282
|
|
|
3245
3283
|
/** Infer the value type for a setting path */
|
|
3246
|
-
export type SettingValue<P extends SettingPath> = Schema[P] extends { type: "boolean" }
|
|
3247
|
-
? boolean
|
|
3248
|
-
: Schema[P] extends { type: "
|
|
3249
|
-
?
|
|
3250
|
-
: Schema[P] extends { type: "
|
|
3251
|
-
?
|
|
3252
|
-
: Schema[P] extends { type: "
|
|
3253
|
-
?
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
: Schema[P] extends { type: "
|
|
3284
|
+
export type SettingValue<P extends SettingPath> = Schema[P] extends { type: "boolean"; default: undefined }
|
|
3285
|
+
? boolean | undefined
|
|
3286
|
+
: Schema[P] extends { type: "boolean" }
|
|
3287
|
+
? boolean
|
|
3288
|
+
: Schema[P] extends { type: "string" }
|
|
3289
|
+
? string | undefined
|
|
3290
|
+
: Schema[P] extends { type: "number" }
|
|
3291
|
+
? number
|
|
3292
|
+
: Schema[P] extends { type: "enum"; values: infer V }
|
|
3293
|
+
? V extends readonly string[]
|
|
3294
|
+
? V[number]
|
|
3295
|
+
: never
|
|
3296
|
+
: Schema[P] extends { type: "array"; default: infer D }
|
|
3259
3297
|
? D
|
|
3260
|
-
:
|
|
3298
|
+
: Schema[P] extends { type: "record"; default: infer D }
|
|
3299
|
+
? D
|
|
3300
|
+
: never;
|
|
3261
3301
|
|
|
3262
3302
|
/** Get the default value for a setting path */
|
|
3263
3303
|
export function getDefault<P extends SettingPath>(path: P): SettingValue<P> {
|
|
@@ -3447,6 +3487,8 @@ export interface ShellMinimizerSettings {
|
|
|
3447
3487
|
only: string[];
|
|
3448
3488
|
except: string[];
|
|
3449
3489
|
maxCaptureBytes: number;
|
|
3490
|
+
sourceOutlineLevel: "default" | "aggressive";
|
|
3491
|
+
legacyFilters: boolean | undefined;
|
|
3450
3492
|
}
|
|
3451
3493
|
|
|
3452
3494
|
/** Map group prefix -> typed settings interface */
|
package/src/config/settings.ts
CHANGED
|
@@ -17,6 +17,7 @@ import * as path from "node:path";
|
|
|
17
17
|
import {
|
|
18
18
|
getAgentDbPath,
|
|
19
19
|
getAgentDir,
|
|
20
|
+
getLastChangelogVersionPath,
|
|
20
21
|
getProjectDir,
|
|
21
22
|
isEnoent,
|
|
22
23
|
logger,
|
|
@@ -25,7 +26,7 @@ import {
|
|
|
25
26
|
} from "@oh-my-pi/pi-utils";
|
|
26
27
|
import { YAML } from "bun";
|
|
27
28
|
import { type Settings as SettingsCapabilityItem, settingsCapability } from "../capability/settings";
|
|
28
|
-
import type { ModelRole } from "../config/model-
|
|
29
|
+
import type { ModelRole } from "../config/model-roles";
|
|
29
30
|
import { loadCapability } from "../discovery";
|
|
30
31
|
import { isLightTheme, setAutoThemeMapping, setColorBlindMode, setSymbolPreset } from "../modes/theme/theme";
|
|
31
32
|
import { AgentStorage } from "../session/agent-storage";
|
|
@@ -63,6 +64,8 @@ export interface SettingsOptions {
|
|
|
63
64
|
inMemory?: boolean;
|
|
64
65
|
/** Initial overrides */
|
|
65
66
|
overrides?: Partial<Record<SettingPath, unknown>>;
|
|
67
|
+
/** Extra config.yml-style overlays loaded after global/project settings */
|
|
68
|
+
configFiles?: string[];
|
|
66
69
|
}
|
|
67
70
|
|
|
68
71
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -115,10 +118,12 @@ type PathScopedStringArrayEntry = {
|
|
|
115
118
|
providers?: unknown;
|
|
116
119
|
};
|
|
117
120
|
|
|
121
|
+
function expandTilde(p: string): string {
|
|
122
|
+
return p === "~" ? os.homedir() : p.startsWith("~/") ? path.join(os.homedir(), p.slice(2)) : p;
|
|
123
|
+
}
|
|
124
|
+
|
|
118
125
|
function normalizePathPrefix(prefix: string): string {
|
|
119
|
-
|
|
120
|
-
prefix === "~" ? os.homedir() : prefix.startsWith("~/") ? path.join(os.homedir(), prefix.slice(2)) : prefix;
|
|
121
|
-
return path.resolve(expanded);
|
|
126
|
+
return path.resolve(expandTilde(prefix));
|
|
122
127
|
}
|
|
123
128
|
|
|
124
129
|
function pathMatchesPrefix(cwd: string, prefix: string): boolean {
|
|
@@ -192,10 +197,13 @@ export class Settings {
|
|
|
192
197
|
#agentDir: string;
|
|
193
198
|
#storage: AgentStorage | null = null;
|
|
194
199
|
|
|
200
|
+
#configFiles: string[] = [];
|
|
195
201
|
/** Global settings from config.yml */
|
|
196
202
|
#global: RawSettings = {};
|
|
197
203
|
/** Project settings from .claude/settings.yml etc */
|
|
198
204
|
#project: RawSettings = {};
|
|
205
|
+
/** Extra config.yml-style overlays passed by CLI */
|
|
206
|
+
#configOverlay: RawSettings = {};
|
|
199
207
|
/** Runtime overrides (not persisted) */
|
|
200
208
|
#overrides: RawSettings = {};
|
|
201
209
|
/** Merged view (global + project + overrides) */
|
|
@@ -206,6 +214,9 @@ export class Settings {
|
|
|
206
214
|
/** Paths modified during this session (for partial save) */
|
|
207
215
|
#modified = new Set<string>();
|
|
208
216
|
|
|
217
|
+
/** Legacy `lastChangelogVersion` captured from config.yml during migration (now a marker file). */
|
|
218
|
+
#legacyLastChangelogVersion?: string;
|
|
219
|
+
|
|
209
220
|
/** Pending save (debounced) */
|
|
210
221
|
#saveTimer?: NodeJS.Timeout;
|
|
211
222
|
#savePromise?: Promise<void>;
|
|
@@ -217,6 +228,7 @@ export class Settings {
|
|
|
217
228
|
this.#cwd = path.normalize(options.cwd ?? getProjectDir());
|
|
218
229
|
this.#agentDir = path.normalize(options.agentDir ?? getAgentDir());
|
|
219
230
|
this.#configPath = options.inMemory ? null : path.join(this.#agentDir, "config.yml");
|
|
231
|
+
this.#configFiles = options.configFiles?.map(file => path.resolve(this.#cwd, expandTilde(file))) ?? [];
|
|
220
232
|
this.#persist = !options.inMemory;
|
|
221
233
|
|
|
222
234
|
if (options.overrides) {
|
|
@@ -252,6 +264,7 @@ export class Settings {
|
|
|
252
264
|
},
|
|
253
265
|
error => {
|
|
254
266
|
globalInstance = null;
|
|
267
|
+
globalInstancePromise = null;
|
|
255
268
|
clearBoundSettingsMethods();
|
|
256
269
|
throw error;
|
|
257
270
|
},
|
|
@@ -299,6 +312,14 @@ export class Settings {
|
|
|
299
312
|
return resolved as SettingValue<P>;
|
|
300
313
|
}
|
|
301
314
|
|
|
315
|
+
/**
|
|
316
|
+
* Whether `path` has an explicitly configured value (global config, project
|
|
317
|
+
* config, or runtime override) rather than falling back to the schema default.
|
|
318
|
+
*/
|
|
319
|
+
isConfigured(path: SettingPath): boolean {
|
|
320
|
+
return getByPath(this.#merged, SETTING_PATH_SEGMENTS[path]) !== undefined;
|
|
321
|
+
}
|
|
322
|
+
|
|
302
323
|
/**
|
|
303
324
|
* Set a setting value (sync).
|
|
304
325
|
* Updates global settings and queues a background save.
|
|
@@ -382,6 +403,8 @@ export class Settings {
|
|
|
382
403
|
cloned.#storage = this.#storage;
|
|
383
404
|
cloned.#global = structuredClone(this.#global);
|
|
384
405
|
cloned.#project = this.#persist ? await cloned.#loadProjectSettings() : structuredClone(this.#project);
|
|
406
|
+
cloned.#configFiles = [...this.#configFiles];
|
|
407
|
+
cloned.#configOverlay = structuredClone(this.#configOverlay);
|
|
385
408
|
cloned.#overrides = structuredClone(this.#overrides);
|
|
386
409
|
cloned.#rebuildMerged();
|
|
387
410
|
cloned.#fireAllHooks();
|
|
@@ -549,9 +572,11 @@ export class Settings {
|
|
|
549
572
|
this.#storage = await AgentStorage.open(getAgentDbPath(this.#agentDir));
|
|
550
573
|
await this.#migrateFromLegacy();
|
|
551
574
|
this.#global = await this.#loadYaml(this.#configPath!);
|
|
575
|
+
await this.#seedLastChangelogVersionMarker();
|
|
552
576
|
}
|
|
553
577
|
|
|
554
578
|
this.#project = await projectPromise;
|
|
579
|
+
this.#configOverlay = await this.#loadConfigOverlays();
|
|
555
580
|
|
|
556
581
|
// Build merged view (global → project → overrides; project wins over global)
|
|
557
582
|
this.#rebuildMerged();
|
|
@@ -589,6 +614,43 @@ export class Settings {
|
|
|
589
614
|
}
|
|
590
615
|
}
|
|
591
616
|
|
|
617
|
+
async #loadConfigOverlays(): Promise<RawSettings> {
|
|
618
|
+
let merged: RawSettings = {};
|
|
619
|
+
for (const filePath of this.#configFiles) {
|
|
620
|
+
merged = this.#deepMerge(merged, await this.#loadOverlayYaml(filePath));
|
|
621
|
+
}
|
|
622
|
+
return merged;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Strict loader for explicit `--config` overlays: unlike `#loadYaml`,
|
|
627
|
+
* missing or malformed files are hard errors so a typo'd path cannot
|
|
628
|
+
* silently fall back to the persistent settings.
|
|
629
|
+
*/
|
|
630
|
+
async #loadOverlayYaml(filePath: string): Promise<RawSettings> {
|
|
631
|
+
let content: string;
|
|
632
|
+
try {
|
|
633
|
+
content = await Bun.file(filePath).text();
|
|
634
|
+
} catch (error) {
|
|
635
|
+
throw new Error(
|
|
636
|
+
isEnoent(error)
|
|
637
|
+
? `Config overlay not found: ${filePath}`
|
|
638
|
+
: `Failed to read config overlay ${filePath}: ${String(error)}`,
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
let parsed: unknown;
|
|
642
|
+
try {
|
|
643
|
+
parsed = YAML.parse(content);
|
|
644
|
+
} catch (error) {
|
|
645
|
+
throw new Error(`Failed to parse config overlay ${filePath}: ${String(error)}`);
|
|
646
|
+
}
|
|
647
|
+
if (parsed === null || parsed === undefined) return {};
|
|
648
|
+
if (typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
649
|
+
throw new Error(`Config overlay must be a YAML mapping: ${filePath}`);
|
|
650
|
+
}
|
|
651
|
+
return this.#migrateRawSettings(parsed as RawSettings);
|
|
652
|
+
}
|
|
653
|
+
|
|
592
654
|
async #migrateFromLegacy(): Promise<void> {
|
|
593
655
|
if (!this.#configPath) return;
|
|
594
656
|
|
|
@@ -642,6 +704,16 @@ export class Settings {
|
|
|
642
704
|
delete raw.queueMode;
|
|
643
705
|
}
|
|
644
706
|
|
|
707
|
+
// lastChangelogVersion moved out of config.yml into the
|
|
708
|
+
// <agentDir>/last-changelog-version marker file so version bumps no
|
|
709
|
+
// longer dirty user-tracked configs. Capture for marker seeding (see
|
|
710
|
+
// #seedLastChangelogVersionMarker), then strip the key — the next
|
|
711
|
+
// config save drops it from disk.
|
|
712
|
+
if (typeof raw.lastChangelogVersion === "string") {
|
|
713
|
+
this.#legacyLastChangelogVersion ??= raw.lastChangelogVersion;
|
|
714
|
+
}
|
|
715
|
+
delete raw.lastChangelogVersion;
|
|
716
|
+
|
|
645
717
|
// ask.timeout: ms -> seconds (if value > 1000, it's old ms format)
|
|
646
718
|
if (raw.ask && typeof (raw.ask as Record<string, unknown>).timeout === "number") {
|
|
647
719
|
const oldValue = (raw.ask as Record<string, unknown>).timeout as number;
|
|
@@ -803,6 +875,27 @@ export class Settings {
|
|
|
803
875
|
return raw;
|
|
804
876
|
}
|
|
805
877
|
|
|
878
|
+
/**
|
|
879
|
+
* One-time migration: seed the last-changelog-version marker file from the
|
|
880
|
+
* legacy config.yml key. An existing marker always wins — it is the newer
|
|
881
|
+
* source of truth.
|
|
882
|
+
*/
|
|
883
|
+
async #seedLastChangelogVersionMarker(): Promise<void> {
|
|
884
|
+
const legacy = this.#legacyLastChangelogVersion;
|
|
885
|
+
if (!legacy) return;
|
|
886
|
+
const markerPath = getLastChangelogVersionPath(this.#agentDir);
|
|
887
|
+
try {
|
|
888
|
+
if ((await Bun.file(markerPath).text()).trim()) return;
|
|
889
|
+
} catch (error) {
|
|
890
|
+
if (!isEnoent(error)) return;
|
|
891
|
+
}
|
|
892
|
+
try {
|
|
893
|
+
await Bun.write(markerPath, legacy);
|
|
894
|
+
} catch (error) {
|
|
895
|
+
logger.warn("Settings: failed to seed last-changelog-version marker", { error: String(error) });
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
806
899
|
// ─────────────────────────────────────────────────────────────────────────
|
|
807
900
|
// Saving
|
|
808
901
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -862,6 +955,7 @@ export class Settings {
|
|
|
862
955
|
|
|
863
956
|
#rebuildMerged(): void {
|
|
864
957
|
this.#merged = this.#deepMerge(this.#deepMerge({}, this.#global), this.#project);
|
|
958
|
+
this.#merged = this.#deepMerge(this.#merged, this.#configOverlay);
|
|
865
959
|
this.#merged = this.#deepMerge(this.#merged, this.#overrides);
|
|
866
960
|
this.#resolvedCache.clear();
|
|
867
961
|
}
|
package/src/dap/client.ts
CHANGED
|
@@ -29,32 +29,67 @@ type DapReverseRequestHandler = (args: unknown) => unknown | Promise<unknown>;
|
|
|
29
29
|
|
|
30
30
|
const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
// Reused for all full decodes; each decode() resets state, so a single
|
|
33
|
+
// instance is safe and avoids per-message TextDecoder allocation.
|
|
34
|
+
const MESSAGE_DECODER = new TextDecoder("utf-8");
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Locate the `\r\n\r\n` header terminator across the pending chunk list.
|
|
38
|
+
* Returns the absolute byte index of the first `\r`, or -1 when not present.
|
|
39
|
+
* Equivalent to scanning the contiguous concatenation of the chunks.
|
|
40
|
+
*/
|
|
41
|
+
function findHeaderEndInChunks(chunks: Buffer[]): number {
|
|
42
|
+
let global = 0;
|
|
43
|
+
let b0 = -1;
|
|
44
|
+
let b1 = -1;
|
|
45
|
+
let b2 = -1;
|
|
46
|
+
for (const chunk of chunks) {
|
|
47
|
+
for (let i = 0; i < chunk.length; i++) {
|
|
48
|
+
const b3 = chunk[i];
|
|
49
|
+
if (b0 === 13 && b1 === 10 && b2 === 13 && b3 === 10) {
|
|
50
|
+
return global - 3;
|
|
51
|
+
}
|
|
52
|
+
b0 = b1;
|
|
53
|
+
b1 = b2;
|
|
54
|
+
b2 = b3;
|
|
55
|
+
global++;
|
|
36
56
|
}
|
|
37
57
|
}
|
|
38
58
|
return -1;
|
|
39
59
|
}
|
|
40
60
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
/** Copy the byte range [from, to) out of the pending chunk list into one Buffer. */
|
|
62
|
+
function copyChunkRange(chunks: Buffer[], from: number, to: number): Buffer {
|
|
63
|
+
const out = Buffer.allocUnsafe(to - from);
|
|
64
|
+
let global = 0;
|
|
65
|
+
let written = 0;
|
|
66
|
+
for (const chunk of chunks) {
|
|
67
|
+
const chunkEnd = global + chunk.length;
|
|
68
|
+
if (chunkEnd > from && global < to) {
|
|
69
|
+
const start = Math.max(from, global) - global;
|
|
70
|
+
const end = Math.min(to, chunkEnd) - global;
|
|
71
|
+
chunk.copy(out, written, start, end);
|
|
72
|
+
written += end - start;
|
|
73
|
+
}
|
|
74
|
+
global = chunkEnd;
|
|
75
|
+
if (global >= to) break;
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Drop the first `count` bytes from the pending chunk list in place. */
|
|
81
|
+
function dropChunkFront(chunks: Buffer[], count: number): void {
|
|
82
|
+
let removed = 0;
|
|
83
|
+
while (chunks.length > 0) {
|
|
84
|
+
const head = chunks[0];
|
|
85
|
+
if (removed + head.length <= count) {
|
|
86
|
+
removed += head.length;
|
|
87
|
+
chunks.shift();
|
|
88
|
+
} else {
|
|
89
|
+
chunks[0] = head.subarray(count - removed);
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
58
93
|
}
|
|
59
94
|
|
|
60
95
|
async function writeMessage(sink: DapWriteSink, message: DapRequestMessage | DapResponseMessage): Promise<void> {
|
|
@@ -81,7 +116,7 @@ export class DapClient {
|
|
|
81
116
|
readonly #socket?: { end(): void };
|
|
82
117
|
#requestSeq = 0;
|
|
83
118
|
#pendingRequests = new Map<number, DapPendingRequest>();
|
|
84
|
-
#messageBuffer = Buffer.alloc(0);
|
|
119
|
+
#messageBuffer: Buffer = Buffer.alloc(0);
|
|
85
120
|
#isReading = false;
|
|
86
121
|
#disposed = false;
|
|
87
122
|
#lastActivity = Date.now();
|
|
@@ -416,32 +451,84 @@ export class DapClient {
|
|
|
416
451
|
if (this.#isReading) return;
|
|
417
452
|
this.#isReading = true;
|
|
418
453
|
const reader = this.#readable.getReader();
|
|
454
|
+
|
|
455
|
+
// Incoming bytes are buffered as a list of chunks and only joined when a
|
|
456
|
+
// full message is framed (mirrors the LSP reader) — concatenating the
|
|
457
|
+
// accumulator on every read is O(n^2) for messages spanning many reads.
|
|
458
|
+
const pendingChunks: Buffer[] = [];
|
|
459
|
+
let pendingLen = 0;
|
|
460
|
+
if (this.#messageBuffer.length > 0) {
|
|
461
|
+
pendingChunks.push(this.#messageBuffer);
|
|
462
|
+
pendingLen = this.#messageBuffer.length;
|
|
463
|
+
}
|
|
464
|
+
|
|
419
465
|
try {
|
|
420
466
|
while (true) {
|
|
421
467
|
const { done, value } = await reader.read();
|
|
422
468
|
if (done) break;
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
469
|
+
|
|
470
|
+
pendingChunks.push(Buffer.from(value));
|
|
471
|
+
pendingLen += value.length;
|
|
472
|
+
|
|
473
|
+
// Drain every complete message currently buffered.
|
|
474
|
+
while (true) {
|
|
475
|
+
const headerEnd = findHeaderEndInChunks(pendingChunks);
|
|
476
|
+
if (headerEnd === -1) break;
|
|
477
|
+
|
|
478
|
+
const headerText = MESSAGE_DECODER.decode(copyChunkRange(pendingChunks, 0, headerEnd));
|
|
479
|
+
const contentLengthMatch = headerText.match(/Content-Length: (\d+)/i);
|
|
480
|
+
if (!contentLengthMatch) {
|
|
481
|
+
// Non-protocol bytes (e.g. an adapter printing to stdout).
|
|
482
|
+
// Drop past the bogus terminator and resync instead of
|
|
483
|
+
// stalling on the same junk header forever.
|
|
484
|
+
logger.warn("DAP framing resync: header block without Content-Length", {
|
|
485
|
+
adapter: this.adapter.name,
|
|
486
|
+
header: headerText.slice(0, 200),
|
|
487
|
+
});
|
|
488
|
+
dropChunkFront(pendingChunks, headerEnd + 4);
|
|
489
|
+
pendingLen -= headerEnd + 4;
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const contentLength = Number.parseInt(contentLengthMatch[1], 10);
|
|
494
|
+
const messageStart = headerEnd + 4; // Skip \r\n\r\n
|
|
495
|
+
const messageEnd = messageStart + contentLength;
|
|
496
|
+
if (pendingLen < messageEnd) break;
|
|
497
|
+
|
|
498
|
+
const messageText = MESSAGE_DECODER.decode(copyChunkRange(pendingChunks, messageStart, messageEnd));
|
|
499
|
+
dropChunkFront(pendingChunks, messageEnd);
|
|
500
|
+
pendingLen -= messageEnd;
|
|
430
501
|
this.#lastActivity = Date.now();
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
502
|
+
|
|
503
|
+
// A malformed message must not kill the reader — later
|
|
504
|
+
// messages are still well-framed.
|
|
505
|
+
try {
|
|
506
|
+
const message = JSON.parse(messageText) as DapResponseMessage | DapEventMessage | DapRequestMessage;
|
|
507
|
+
if (message.type === "response") {
|
|
508
|
+
this.#handleResponse(message);
|
|
509
|
+
} else if (message.type === "event") {
|
|
510
|
+
await this.#dispatchEvent(message);
|
|
511
|
+
} else {
|
|
512
|
+
await this.#handleAdapterRequest(message);
|
|
513
|
+
}
|
|
514
|
+
} catch (error) {
|
|
515
|
+
logger.warn("DAP message handling failed", {
|
|
516
|
+
adapter: this.adapter.name,
|
|
517
|
+
error: toErrorMessage(error),
|
|
518
|
+
});
|
|
437
519
|
}
|
|
438
|
-
parsed = parseMessage(workingBuffer);
|
|
439
520
|
}
|
|
440
|
-
this.#messageBuffer = workingBuffer;
|
|
441
521
|
}
|
|
442
522
|
} catch (error) {
|
|
443
523
|
this.#rejectPendingRequests(new Error(`DAP connection closed: ${toErrorMessage(error)}`));
|
|
444
524
|
} finally {
|
|
525
|
+
// Persist any unparsed remainder so a restarted reader resumes mid-message.
|
|
526
|
+
this.#messageBuffer =
|
|
527
|
+
pendingChunks.length === 0
|
|
528
|
+
? Buffer.alloc(0)
|
|
529
|
+
: pendingChunks.length === 1
|
|
530
|
+
? pendingChunks[0]
|
|
531
|
+
: Buffer.concat(pendingChunks, pendingLen);
|
|
445
532
|
reader.releaseLock();
|
|
446
533
|
this.#isReading = false;
|
|
447
534
|
}
|