@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
package/src/dap/session.ts
CHANGED
|
@@ -76,8 +76,14 @@ interface DapSession {
|
|
|
76
76
|
functionBreakpoints: DapFunctionBreakpointRecord[];
|
|
77
77
|
instructionBreakpoints: DapInstructionBreakpoint[];
|
|
78
78
|
dataBreakpoints: DapDataBreakpoint[];
|
|
79
|
-
|
|
79
|
+
/** Serializes breakpoint mutations — see #serializeBreakpointMutation. */
|
|
80
|
+
breakpointMutationQueue: Promise<void>;
|
|
81
|
+
/** Recent output chunks; trimmed from the front when over MAX_OUTPUT_BYTES. */
|
|
82
|
+
outputChunks: string[];
|
|
83
|
+
/** Cumulative bytes of output ever received (reported in summaries). */
|
|
80
84
|
outputBytes: number;
|
|
85
|
+
/** Bytes currently buffered in outputChunks. */
|
|
86
|
+
outputBufferedBytes: number;
|
|
81
87
|
outputTruncated: boolean;
|
|
82
88
|
stop: DapStopLocation;
|
|
83
89
|
threads: DapThread[];
|
|
@@ -175,10 +181,31 @@ function normalizePath(filePath: string): string {
|
|
|
175
181
|
|
|
176
182
|
function truncateOutput(session: DapSession, output: string): void {
|
|
177
183
|
if (!output) return;
|
|
178
|
-
|
|
179
|
-
session.
|
|
180
|
-
|
|
181
|
-
|
|
184
|
+
const bytes = Buffer.byteLength(output, "utf-8");
|
|
185
|
+
session.outputChunks.push(output);
|
|
186
|
+
session.outputBytes += bytes;
|
|
187
|
+
session.outputBufferedBytes += bytes;
|
|
188
|
+
// Trim whole chunks from the front, but only while the remainder still
|
|
189
|
+
// holds a full MAX_OUTPUT_BYTES tail — dropping the front chunk whenever
|
|
190
|
+
// the total exceeded the cap could retain far less than the cap (e.g.
|
|
191
|
+
// [120KB, 10KB] would keep only 10KB). Recomputing one big string's byte
|
|
192
|
+
// length per 1KB trim iteration was O(n^2) inside the event dispatch loop.
|
|
193
|
+
while (session.outputChunks.length > 1) {
|
|
194
|
+
const frontBytes = Buffer.byteLength(session.outputChunks[0], "utf-8");
|
|
195
|
+
if (session.outputBufferedBytes - frontBytes < MAX_OUTPUT_BYTES) break;
|
|
196
|
+
session.outputChunks.shift();
|
|
197
|
+
session.outputBufferedBytes -= frontBytes;
|
|
198
|
+
session.outputTruncated = true;
|
|
199
|
+
}
|
|
200
|
+
if (session.outputBufferedBytes > MAX_OUTPUT_BYTES) {
|
|
201
|
+
// Byte-slice the front chunk's head so exactly the cap remains (a torn
|
|
202
|
+
// code point at the cut decodes as U+FFFD, acceptable for log output).
|
|
203
|
+
const front = session.outputChunks[0];
|
|
204
|
+
const frontBytes = Buffer.byteLength(front, "utf-8");
|
|
205
|
+
const excess = session.outputBufferedBytes - MAX_OUTPUT_BYTES;
|
|
206
|
+
const kept = Buffer.from(front, "utf-8").subarray(excess).toString("utf-8");
|
|
207
|
+
session.outputChunks[0] = kept;
|
|
208
|
+
session.outputBufferedBytes += Buffer.byteLength(kept, "utf-8") - frontBytes;
|
|
182
209
|
session.outputTruncated = true;
|
|
183
210
|
}
|
|
184
211
|
}
|
|
@@ -368,6 +395,26 @@ export class DapSessionManager {
|
|
|
368
395
|
}
|
|
369
396
|
}
|
|
370
397
|
|
|
398
|
+
/**
|
|
399
|
+
* Serialize breakpoint mutations per session: every mutator does a
|
|
400
|
+
* read-modify-write of session state around an await, and the adapter-side
|
|
401
|
+
* set*Breakpoints request replaces the whole list — concurrent mutations
|
|
402
|
+
* would silently drop each other's breakpoints on both sides.
|
|
403
|
+
*/
|
|
404
|
+
#serializeBreakpointMutation<T>(session: DapSession, mutate: () => Promise<T>, signal?: AbortSignal): Promise<T> {
|
|
405
|
+
const run = session.breakpointMutationQueue.then(() => {
|
|
406
|
+
// A mutation can sit behind several queued 30s predecessors; honor a
|
|
407
|
+
// caller abort at dequeue instead of running a request nobody awaits.
|
|
408
|
+
if (signal?.aborted) throw signal.reason instanceof Error ? signal.reason : new Error("Aborted");
|
|
409
|
+
return mutate();
|
|
410
|
+
});
|
|
411
|
+
session.breakpointMutationQueue = run.then(
|
|
412
|
+
() => undefined,
|
|
413
|
+
() => undefined,
|
|
414
|
+
);
|
|
415
|
+
return run;
|
|
416
|
+
}
|
|
417
|
+
|
|
371
418
|
async setBreakpoint(
|
|
372
419
|
file: string,
|
|
373
420
|
line: number,
|
|
@@ -376,99 +423,123 @@ export class DapSessionManager {
|
|
|
376
423
|
timeoutMs: number = 30_000,
|
|
377
424
|
) {
|
|
378
425
|
const session = this.#touchActiveSession();
|
|
379
|
-
|
|
380
|
-
const current = [...(session.breakpoints.get(sourcePath) ?? [])];
|
|
381
|
-
const deduped = current.filter(entry => entry.line !== line);
|
|
382
|
-
deduped.push({ verified: false, line, condition });
|
|
383
|
-
deduped.sort((left, right) => left.line - right.line);
|
|
384
|
-
const response = await this.#sendRequestWithConfig<{ breakpoints?: DapBreakpoint[] }>(
|
|
426
|
+
return this.#serializeBreakpointMutation(
|
|
385
427
|
session,
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
}
|
|
428
|
+
async () => {
|
|
429
|
+
const sourcePath = normalizePath(file);
|
|
430
|
+
const current = [...(session.breakpoints.get(sourcePath) ?? [])];
|
|
431
|
+
const deduped = current.filter(entry => entry.line !== line);
|
|
432
|
+
deduped.push({ verified: false, line, condition });
|
|
433
|
+
deduped.sort((left, right) => left.line - right.line);
|
|
434
|
+
const response = await this.#sendRequestWithConfig<{ breakpoints?: DapBreakpoint[] }>(
|
|
435
|
+
session,
|
|
436
|
+
"setBreakpoints",
|
|
437
|
+
{
|
|
438
|
+
source: { path: sourcePath, name: path.basename(sourcePath) },
|
|
439
|
+
breakpoints: deduped.map<DapSourceBreakpoint>(entry => ({
|
|
440
|
+
line: entry.line,
|
|
441
|
+
...(entry.condition ? { condition: entry.condition } : {}),
|
|
442
|
+
})),
|
|
443
|
+
},
|
|
444
|
+
signal,
|
|
445
|
+
timeoutMs,
|
|
446
|
+
);
|
|
447
|
+
session.breakpoints.set(sourcePath, this.#mapSourceBreakpoints(deduped, response?.breakpoints));
|
|
448
|
+
return {
|
|
449
|
+
snapshot: buildSummary(session),
|
|
450
|
+
breakpoints: session.breakpoints.get(sourcePath) ?? [],
|
|
451
|
+
sourcePath,
|
|
452
|
+
};
|
|
393
453
|
},
|
|
394
454
|
signal,
|
|
395
|
-
timeoutMs,
|
|
396
455
|
);
|
|
397
|
-
session.breakpoints.set(sourcePath, this.#mapSourceBreakpoints(deduped, response?.breakpoints));
|
|
398
|
-
return {
|
|
399
|
-
snapshot: buildSummary(session),
|
|
400
|
-
breakpoints: session.breakpoints.get(sourcePath) ?? [],
|
|
401
|
-
sourcePath,
|
|
402
|
-
};
|
|
403
456
|
}
|
|
404
457
|
|
|
405
458
|
async removeBreakpoint(file: string, line: number, signal?: AbortSignal, timeoutMs: number = 30_000) {
|
|
406
459
|
const session = this.#touchActiveSession();
|
|
407
|
-
|
|
408
|
-
const current = [...(session.breakpoints.get(sourcePath) ?? [])].filter(entry => entry.line !== line);
|
|
409
|
-
const response = await this.#sendRequestWithConfig<{ breakpoints?: DapBreakpoint[] }>(
|
|
460
|
+
return this.#serializeBreakpointMutation(
|
|
410
461
|
session,
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
462
|
+
async () => {
|
|
463
|
+
const sourcePath = normalizePath(file);
|
|
464
|
+
const current = [...(session.breakpoints.get(sourcePath) ?? [])].filter(entry => entry.line !== line);
|
|
465
|
+
const response = await this.#sendRequestWithConfig<{ breakpoints?: DapBreakpoint[] }>(
|
|
466
|
+
session,
|
|
467
|
+
"setBreakpoints",
|
|
468
|
+
{
|
|
469
|
+
source: { path: sourcePath, name: path.basename(sourcePath) },
|
|
470
|
+
breakpoints: current.map<DapSourceBreakpoint>(entry => ({
|
|
471
|
+
line: entry.line,
|
|
472
|
+
...(entry.condition ? { condition: entry.condition } : {}),
|
|
473
|
+
})),
|
|
474
|
+
},
|
|
475
|
+
signal,
|
|
476
|
+
timeoutMs,
|
|
477
|
+
);
|
|
478
|
+
if (current.length === 0) {
|
|
479
|
+
session.breakpoints.delete(sourcePath);
|
|
480
|
+
} else {
|
|
481
|
+
session.breakpoints.set(sourcePath, this.#mapSourceBreakpoints(current, response?.breakpoints));
|
|
482
|
+
}
|
|
483
|
+
return {
|
|
484
|
+
snapshot: buildSummary(session),
|
|
485
|
+
breakpoints: session.breakpoints.get(sourcePath) ?? [],
|
|
486
|
+
sourcePath,
|
|
487
|
+
};
|
|
418
488
|
},
|
|
419
489
|
signal,
|
|
420
|
-
timeoutMs,
|
|
421
490
|
);
|
|
422
|
-
if (current.length === 0) {
|
|
423
|
-
session.breakpoints.delete(sourcePath);
|
|
424
|
-
} else {
|
|
425
|
-
session.breakpoints.set(sourcePath, this.#mapSourceBreakpoints(current, response?.breakpoints));
|
|
426
|
-
}
|
|
427
|
-
return {
|
|
428
|
-
snapshot: buildSummary(session),
|
|
429
|
-
breakpoints: session.breakpoints.get(sourcePath) ?? [],
|
|
430
|
-
sourcePath,
|
|
431
|
-
};
|
|
432
491
|
}
|
|
433
492
|
|
|
434
493
|
async setFunctionBreakpoint(name: string, condition?: string, signal?: AbortSignal, timeoutMs: number = 30_000) {
|
|
435
494
|
const session = this.#touchActiveSession();
|
|
436
|
-
|
|
437
|
-
current.push({ verified: false, name, condition });
|
|
438
|
-
current.sort((left, right) => left.name.localeCompare(right.name));
|
|
439
|
-
const response = await this.#sendRequestWithConfig<{ breakpoints?: DapBreakpoint[] }>(
|
|
495
|
+
return this.#serializeBreakpointMutation(
|
|
440
496
|
session,
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
497
|
+
async () => {
|
|
498
|
+
const current = session.functionBreakpoints.filter(entry => entry.name !== name);
|
|
499
|
+
current.push({ verified: false, name, condition });
|
|
500
|
+
current.sort((left, right) => left.name.localeCompare(right.name));
|
|
501
|
+
const response = await this.#sendRequestWithConfig<{ breakpoints?: DapBreakpoint[] }>(
|
|
502
|
+
session,
|
|
503
|
+
"setFunctionBreakpoints",
|
|
504
|
+
{
|
|
505
|
+
breakpoints: current.map<DapFunctionBreakpoint>(entry => ({
|
|
506
|
+
name: entry.name,
|
|
507
|
+
...(entry.condition ? { condition: entry.condition } : {}),
|
|
508
|
+
})),
|
|
509
|
+
},
|
|
510
|
+
signal,
|
|
511
|
+
timeoutMs,
|
|
512
|
+
);
|
|
513
|
+
session.functionBreakpoints = this.#mapFunctionBreakpoints(current, response?.breakpoints);
|
|
514
|
+
return { snapshot: buildSummary(session), breakpoints: session.functionBreakpoints };
|
|
447
515
|
},
|
|
448
516
|
signal,
|
|
449
|
-
timeoutMs,
|
|
450
517
|
);
|
|
451
|
-
session.functionBreakpoints = this.#mapFunctionBreakpoints(current, response?.breakpoints);
|
|
452
|
-
return { snapshot: buildSummary(session), breakpoints: session.functionBreakpoints };
|
|
453
518
|
}
|
|
454
519
|
|
|
455
520
|
async removeFunctionBreakpoint(name: string, signal?: AbortSignal, timeoutMs: number = 30_000) {
|
|
456
521
|
const session = this.#touchActiveSession();
|
|
457
|
-
|
|
458
|
-
const response = await this.#sendRequestWithConfig<{ breakpoints?: DapBreakpoint[] }>(
|
|
522
|
+
return this.#serializeBreakpointMutation(
|
|
459
523
|
session,
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
524
|
+
async () => {
|
|
525
|
+
const current = session.functionBreakpoints.filter(entry => entry.name !== name);
|
|
526
|
+
const response = await this.#sendRequestWithConfig<{ breakpoints?: DapBreakpoint[] }>(
|
|
527
|
+
session,
|
|
528
|
+
"setFunctionBreakpoints",
|
|
529
|
+
{
|
|
530
|
+
breakpoints: current.map<DapFunctionBreakpoint>(entry => ({
|
|
531
|
+
name: entry.name,
|
|
532
|
+
...(entry.condition ? { condition: entry.condition } : {}),
|
|
533
|
+
})),
|
|
534
|
+
},
|
|
535
|
+
signal,
|
|
536
|
+
timeoutMs,
|
|
537
|
+
);
|
|
538
|
+
session.functionBreakpoints = this.#mapFunctionBreakpoints(current, response?.breakpoints);
|
|
539
|
+
return { snapshot: buildSummary(session), breakpoints: session.functionBreakpoints };
|
|
466
540
|
},
|
|
467
541
|
signal,
|
|
468
|
-
timeoutMs,
|
|
469
542
|
);
|
|
470
|
-
session.functionBreakpoints = this.#mapFunctionBreakpoints(current, response?.breakpoints);
|
|
471
|
-
return { snapshot: buildSummary(session), breakpoints: session.functionBreakpoints };
|
|
472
543
|
}
|
|
473
544
|
|
|
474
545
|
async setInstructionBreakpoint(
|
|
@@ -480,31 +551,37 @@ export class DapSessionManager {
|
|
|
480
551
|
timeoutMs: number = 30_000,
|
|
481
552
|
) {
|
|
482
553
|
const session = this.#touchActiveSession();
|
|
483
|
-
|
|
484
|
-
entry => entry.instructionReference !== instructionReference || entry.offset !== offset,
|
|
485
|
-
);
|
|
486
|
-
current.push({ instructionReference, offset, condition, hitCondition });
|
|
487
|
-
current.sort((left, right) => {
|
|
488
|
-
const referenceOrder = left.instructionReference.localeCompare(right.instructionReference);
|
|
489
|
-
if (referenceOrder !== 0) {
|
|
490
|
-
return referenceOrder;
|
|
491
|
-
}
|
|
492
|
-
return (left.offset ?? 0) - (right.offset ?? 0);
|
|
493
|
-
});
|
|
494
|
-
const response = await this.#sendRequestWithConfig<{ breakpoints?: DapBreakpoint[] }>(
|
|
554
|
+
return this.#serializeBreakpointMutation(
|
|
495
555
|
session,
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
556
|
+
async () => {
|
|
557
|
+
const current = session.instructionBreakpoints.filter(
|
|
558
|
+
entry => entry.instructionReference !== instructionReference || entry.offset !== offset,
|
|
559
|
+
);
|
|
560
|
+
current.push({ instructionReference, offset, condition, hitCondition });
|
|
561
|
+
current.sort((left, right) => {
|
|
562
|
+
const referenceOrder = left.instructionReference.localeCompare(right.instructionReference);
|
|
563
|
+
if (referenceOrder !== 0) {
|
|
564
|
+
return referenceOrder;
|
|
565
|
+
}
|
|
566
|
+
return (left.offset ?? 0) - (right.offset ?? 0);
|
|
567
|
+
});
|
|
568
|
+
const response = await this.#sendRequestWithConfig<{ breakpoints?: DapBreakpoint[] }>(
|
|
569
|
+
session,
|
|
570
|
+
"setInstructionBreakpoints",
|
|
571
|
+
{
|
|
572
|
+
breakpoints: current,
|
|
573
|
+
} satisfies DapSetInstructionBreakpointsArguments,
|
|
574
|
+
signal,
|
|
575
|
+
timeoutMs,
|
|
576
|
+
);
|
|
577
|
+
session.instructionBreakpoints = current;
|
|
578
|
+
return {
|
|
579
|
+
snapshot: buildSummary(session),
|
|
580
|
+
breakpoints: this.#mapInstructionBreakpoints(current, response?.breakpoints),
|
|
581
|
+
};
|
|
582
|
+
},
|
|
500
583
|
signal,
|
|
501
|
-
timeoutMs,
|
|
502
584
|
);
|
|
503
|
-
session.instructionBreakpoints = current;
|
|
504
|
-
return {
|
|
505
|
-
snapshot: buildSummary(session),
|
|
506
|
-
breakpoints: this.#mapInstructionBreakpoints(current, response?.breakpoints),
|
|
507
|
-
};
|
|
508
585
|
}
|
|
509
586
|
|
|
510
587
|
async removeInstructionBreakpoint(
|
|
@@ -514,29 +591,35 @@ export class DapSessionManager {
|
|
|
514
591
|
timeoutMs: number = 30_000,
|
|
515
592
|
) {
|
|
516
593
|
const session = this.#touchActiveSession();
|
|
517
|
-
|
|
518
|
-
if (entry.instructionReference !== instructionReference) {
|
|
519
|
-
return true;
|
|
520
|
-
}
|
|
521
|
-
if (offset === undefined) {
|
|
522
|
-
return false;
|
|
523
|
-
}
|
|
524
|
-
return entry.offset !== offset;
|
|
525
|
-
});
|
|
526
|
-
const response = await this.#sendRequestWithConfig<{ breakpoints?: DapBreakpoint[] }>(
|
|
594
|
+
return this.#serializeBreakpointMutation(
|
|
527
595
|
session,
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
596
|
+
async () => {
|
|
597
|
+
const current = session.instructionBreakpoints.filter(entry => {
|
|
598
|
+
if (entry.instructionReference !== instructionReference) {
|
|
599
|
+
return true;
|
|
600
|
+
}
|
|
601
|
+
if (offset === undefined) {
|
|
602
|
+
return false;
|
|
603
|
+
}
|
|
604
|
+
return entry.offset !== offset;
|
|
605
|
+
});
|
|
606
|
+
const response = await this.#sendRequestWithConfig<{ breakpoints?: DapBreakpoint[] }>(
|
|
607
|
+
session,
|
|
608
|
+
"setInstructionBreakpoints",
|
|
609
|
+
{
|
|
610
|
+
breakpoints: current,
|
|
611
|
+
} satisfies DapSetInstructionBreakpointsArguments,
|
|
612
|
+
signal,
|
|
613
|
+
timeoutMs,
|
|
614
|
+
);
|
|
615
|
+
session.instructionBreakpoints = current;
|
|
616
|
+
return {
|
|
617
|
+
snapshot: buildSummary(session),
|
|
618
|
+
breakpoints: this.#mapInstructionBreakpoints(current, response?.breakpoints),
|
|
619
|
+
};
|
|
620
|
+
},
|
|
532
621
|
signal,
|
|
533
|
-
timeoutMs,
|
|
534
622
|
);
|
|
535
|
-
session.instructionBreakpoints = current;
|
|
536
|
-
return {
|
|
537
|
-
snapshot: buildSummary(session),
|
|
538
|
-
breakpoints: this.#mapInstructionBreakpoints(current, response?.breakpoints),
|
|
539
|
-
};
|
|
540
623
|
}
|
|
541
624
|
|
|
542
625
|
async dataBreakpointInfo(
|
|
@@ -570,42 +653,54 @@ export class DapSessionManager {
|
|
|
570
653
|
timeoutMs: number = 30_000,
|
|
571
654
|
) {
|
|
572
655
|
const session = this.#touchActiveSession();
|
|
573
|
-
|
|
574
|
-
current.push({ dataId, accessType, condition, hitCondition });
|
|
575
|
-
current.sort((left, right) => left.dataId.localeCompare(right.dataId));
|
|
576
|
-
const response = await this.#sendRequestWithConfig<{ breakpoints?: DapBreakpoint[] }>(
|
|
656
|
+
return this.#serializeBreakpointMutation(
|
|
577
657
|
session,
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
658
|
+
async () => {
|
|
659
|
+
const current = session.dataBreakpoints.filter(entry => entry.dataId !== dataId);
|
|
660
|
+
current.push({ dataId, accessType, condition, hitCondition });
|
|
661
|
+
current.sort((left, right) => left.dataId.localeCompare(right.dataId));
|
|
662
|
+
const response = await this.#sendRequestWithConfig<{ breakpoints?: DapBreakpoint[] }>(
|
|
663
|
+
session,
|
|
664
|
+
"setDataBreakpoints",
|
|
665
|
+
{
|
|
666
|
+
breakpoints: current,
|
|
667
|
+
} satisfies DapSetDataBreakpointsArguments,
|
|
668
|
+
signal,
|
|
669
|
+
timeoutMs,
|
|
670
|
+
);
|
|
671
|
+
session.dataBreakpoints = current;
|
|
672
|
+
return {
|
|
673
|
+
snapshot: buildSummary(session),
|
|
674
|
+
breakpoints: this.#mapDataBreakpoints(current, response?.breakpoints),
|
|
675
|
+
};
|
|
676
|
+
},
|
|
582
677
|
signal,
|
|
583
|
-
timeoutMs,
|
|
584
678
|
);
|
|
585
|
-
session.dataBreakpoints = current;
|
|
586
|
-
return {
|
|
587
|
-
snapshot: buildSummary(session),
|
|
588
|
-
breakpoints: this.#mapDataBreakpoints(current, response?.breakpoints),
|
|
589
|
-
};
|
|
590
679
|
}
|
|
591
680
|
|
|
592
681
|
async removeDataBreakpoint(dataId: string, signal?: AbortSignal, timeoutMs: number = 30_000) {
|
|
593
682
|
const session = this.#touchActiveSession();
|
|
594
|
-
|
|
595
|
-
const response = await this.#sendRequestWithConfig<{ breakpoints?: DapBreakpoint[] }>(
|
|
683
|
+
return this.#serializeBreakpointMutation(
|
|
596
684
|
session,
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
breakpoints
|
|
600
|
-
|
|
685
|
+
async () => {
|
|
686
|
+
const current = session.dataBreakpoints.filter(entry => entry.dataId !== dataId);
|
|
687
|
+
const response = await this.#sendRequestWithConfig<{ breakpoints?: DapBreakpoint[] }>(
|
|
688
|
+
session,
|
|
689
|
+
"setDataBreakpoints",
|
|
690
|
+
{
|
|
691
|
+
breakpoints: current,
|
|
692
|
+
} satisfies DapSetDataBreakpointsArguments,
|
|
693
|
+
signal,
|
|
694
|
+
timeoutMs,
|
|
695
|
+
);
|
|
696
|
+
session.dataBreakpoints = current;
|
|
697
|
+
return {
|
|
698
|
+
snapshot: buildSummary(session),
|
|
699
|
+
breakpoints: this.#mapDataBreakpoints(current, response?.breakpoints),
|
|
700
|
+
};
|
|
701
|
+
},
|
|
601
702
|
signal,
|
|
602
|
-
timeoutMs,
|
|
603
703
|
);
|
|
604
|
-
session.dataBreakpoints = current;
|
|
605
|
-
return {
|
|
606
|
-
snapshot: buildSummary(session),
|
|
607
|
-
breakpoints: this.#mapDataBreakpoints(current, response?.breakpoints),
|
|
608
|
-
};
|
|
609
704
|
}
|
|
610
705
|
|
|
611
706
|
async disassemble(
|
|
@@ -756,21 +851,25 @@ export class DapSessionManager {
|
|
|
756
851
|
|
|
757
852
|
async pause(signal?: AbortSignal, timeoutMs: number = 30_000): Promise<DapSessionSummary> {
|
|
758
853
|
const session = this.#touchActiveSession();
|
|
759
|
-
|
|
854
|
+
// status is mutated by the event reader between awaits; check through a
|
|
855
|
+
// closure so TS does not carry stale narrowing from the early return.
|
|
856
|
+
const isStopped = () => session.status === "stopped";
|
|
857
|
+
if (isStopped()) {
|
|
760
858
|
return buildSummary(session);
|
|
761
859
|
}
|
|
762
860
|
const threadId = await this.#resolveThreadId(session, signal, timeoutMs);
|
|
861
|
+
// Subscribe BEFORE sending pause: the stopped event can arrive in the
|
|
862
|
+
// same chunk as the response and would otherwise be dispatched before
|
|
863
|
+
// the waiter subscribes, burning the whole timeout.
|
|
864
|
+
const stoppedPromise = session.client.waitForEvent<DapStoppedEventBody>("stopped", undefined, signal, timeoutMs);
|
|
865
|
+
stoppedPromise.catch(() => {});
|
|
763
866
|
await this.#sendRequestWithConfig(session, "pause", { threadId } satisfies DapPauseArguments, signal, timeoutMs);
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
session.client.waitForEvent<DapStoppedEventBody>("stopped", undefined, signal, timeoutMs),
|
|
771
|
-
);
|
|
772
|
-
} catch {
|
|
773
|
-
// Timeout or abort — report current state regardless
|
|
867
|
+
if (!isStopped()) {
|
|
868
|
+
try {
|
|
869
|
+
await untilAborted(signal, stoppedPromise);
|
|
870
|
+
} catch {
|
|
871
|
+
// Timeout or abort — report current state regardless
|
|
872
|
+
}
|
|
774
873
|
}
|
|
775
874
|
return buildSummary(session);
|
|
776
875
|
}
|
|
@@ -884,16 +983,16 @@ export class DapSessionManager {
|
|
|
884
983
|
|
|
885
984
|
getOutput(limitBytes?: number): DapOutputSnapshot {
|
|
886
985
|
const session = this.#touchActiveSession();
|
|
887
|
-
|
|
888
|
-
|
|
986
|
+
const output = session.outputChunks.join("");
|
|
987
|
+
if (!limitBytes || limitBytes <= 0 || session.outputBufferedBytes <= limitBytes) {
|
|
988
|
+
return { snapshot: buildSummary(session), output };
|
|
889
989
|
}
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
remaining -= Buffer.byteLength(session.output[sliceStart] ?? "", "utf-8");
|
|
990
|
+
// Byte-slice the tail once; a torn code point at the cut decodes as U+FFFD.
|
|
991
|
+
const buffer = Buffer.from(output, "utf-8");
|
|
992
|
+
if (buffer.length <= limitBytes) {
|
|
993
|
+
return { snapshot: buildSummary(session), output };
|
|
895
994
|
}
|
|
896
|
-
return { snapshot: buildSummary(session), output:
|
|
995
|
+
return { snapshot: buildSummary(session), output: buffer.subarray(buffer.length - limitBytes).toString("utf-8") };
|
|
897
996
|
}
|
|
898
997
|
|
|
899
998
|
async terminate(signal?: AbortSignal, timeoutMs: number = 30_000): Promise<DapSessionSummary | null> {
|
|
@@ -973,8 +1072,10 @@ export class DapSessionManager {
|
|
|
973
1072
|
functionBreakpoints: [],
|
|
974
1073
|
instructionBreakpoints: [],
|
|
975
1074
|
dataBreakpoints: [],
|
|
976
|
-
|
|
1075
|
+
breakpointMutationQueue: Promise.resolve(),
|
|
1076
|
+
outputChunks: [],
|
|
977
1077
|
outputBytes: 0,
|
|
1078
|
+
outputBufferedBytes: 0,
|
|
978
1079
|
outputTruncated: false,
|
|
979
1080
|
stop: {},
|
|
980
1081
|
threads: [],
|
package/src/debug/log-viewer.ts
CHANGED
|
@@ -602,7 +602,7 @@ export class DebugLogViewerComponent implements Component {
|
|
|
602
602
|
// no cached child state
|
|
603
603
|
}
|
|
604
604
|
|
|
605
|
-
render(width: number): string[] {
|
|
605
|
+
render(width: number): readonly string[] {
|
|
606
606
|
this.#lastRenderWidth = Math.max(20, width);
|
|
607
607
|
this.#ensureCursorVisible();
|
|
608
608
|
|
package/src/debug/raw-sse.ts
CHANGED
|
@@ -147,7 +147,7 @@ export class RawSseViewerComponent implements Component {
|
|
|
147
147
|
|
|
148
148
|
invalidate(): void {}
|
|
149
149
|
|
|
150
|
-
render(width: number): string[] {
|
|
150
|
+
render(width: number): readonly string[] {
|
|
151
151
|
this.#lastRenderWidth = Math.max(MIN_VIEWER_WIDTH, width);
|
|
152
152
|
this.#followIfNeeded();
|
|
153
153
|
|
package/src/edit/diff.ts
CHANGED
|
@@ -74,6 +74,49 @@ function isDiffChangeRow(row: string | undefined): boolean {
|
|
|
74
74
|
return row !== undefined && (row.startsWith("+") || row.startsWith("-"));
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
/** Blank row separating non-contiguous regions of a numbered diff. */
|
|
78
|
+
const DIFF_GAP_ROW = "";
|
|
79
|
+
|
|
80
|
+
/** Old-file line number of a source-visible row (`-` or context); `+`/gap/other rows yield undefined. */
|
|
81
|
+
function parseSourceRowLineNumber(row: string): number | undefined {
|
|
82
|
+
const parsed = parseNumberedDiffRow(row);
|
|
83
|
+
return parsed === undefined || parsed.prefix === "+" ? undefined : parsed.lineNumber;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Drop gap rows that no longer separate anything. Context rows are inserted
|
|
88
|
+
* one at a time, each adding its own gap rows from a snapshot of the diff, so
|
|
89
|
+
* the raw result can contain adjacent gap rows, gap rows whose neighbors
|
|
90
|
+
* became contiguous after a later insert filled the hole, and gap rows at the
|
|
91
|
+
* diff edges. The sweep keeps a gap row only when it sits between two
|
|
92
|
+
* source-numbered rows (old-file coordinates — the same numbering the
|
|
93
|
+
* insertion gap test uses) that are actually non-contiguous, and never keeps
|
|
94
|
+
* two in a row.
|
|
95
|
+
*/
|
|
96
|
+
function normalizeDiffGapRows(rows: string[]): void {
|
|
97
|
+
const kept: string[] = [];
|
|
98
|
+
for (let i = 0; i < rows.length; i++) {
|
|
99
|
+
const row = rows[i];
|
|
100
|
+
if (row !== DIFF_GAP_ROW) {
|
|
101
|
+
kept.push(row);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (kept.length === 0 || kept[kept.length - 1] === DIFF_GAP_ROW) continue;
|
|
105
|
+
let before: number | undefined;
|
|
106
|
+
for (let j = kept.length - 1; j >= 0 && before === undefined; j--) {
|
|
107
|
+
before = parseSourceRowLineNumber(kept[j]);
|
|
108
|
+
}
|
|
109
|
+
let after: number | undefined;
|
|
110
|
+
for (let j = i + 1; j < rows.length && after === undefined; j++) {
|
|
111
|
+
if (rows[j] === DIFF_GAP_ROW) continue;
|
|
112
|
+
after = parseSourceRowLineNumber(rows[j]);
|
|
113
|
+
}
|
|
114
|
+
if (before === undefined || after === undefined || after <= before + 1) continue;
|
|
115
|
+
kept.push(row);
|
|
116
|
+
}
|
|
117
|
+
if (kept.length !== rows.length) rows.splice(0, rows.length, ...kept);
|
|
118
|
+
}
|
|
119
|
+
|
|
77
120
|
function adjustedContextInsertIndex(rows: readonly string[], index: number): number {
|
|
78
121
|
let start = index;
|
|
79
122
|
while (start > 0 && isDiffChangeRow(rows[start - 1])) start--;
|
|
@@ -108,13 +151,13 @@ function insertBracketContextRows(
|
|
|
108
151
|
}
|
|
109
152
|
|
|
110
153
|
const chunk: string[] = [];
|
|
111
|
-
if (previousSourceLine !== undefined && lineNumber > previousSourceLine + 1) chunk.push(
|
|
154
|
+
if (previousSourceLine !== undefined && lineNumber > previousSourceLine + 1) chunk.push(DIFF_GAP_ROW);
|
|
112
155
|
chunk.push(row);
|
|
113
|
-
if (nextSourceLine !== undefined && nextSourceLine > lineNumber + 1) chunk.push(
|
|
156
|
+
if (nextSourceLine !== undefined && nextSourceLine > lineNumber + 1) chunk.push(DIFF_GAP_ROW);
|
|
114
157
|
|
|
115
158
|
const adjustedIndex = adjustedContextInsertIndex(rows, insertIndex);
|
|
116
159
|
rows.splice(adjustedIndex, 0, ...chunk);
|
|
117
|
-
|
|
160
|
+
seenRows.add(row);
|
|
118
161
|
}
|
|
119
162
|
}
|
|
120
163
|
|
|
@@ -179,6 +222,7 @@ function addMatchingBracketContextRows(
|
|
|
179
222
|
if (!contextRows.has(oldLineNumber)) contextRows.set(oldLineNumber, text);
|
|
180
223
|
}
|
|
181
224
|
insertBracketContextRows(rows, contextRows, seenRows);
|
|
225
|
+
normalizeDiffGapRows(rows);
|
|
182
226
|
}
|
|
183
227
|
|
|
184
228
|
/**
|
|
@@ -8,7 +8,26 @@
|
|
|
8
8
|
import type { BlockResolver } from "@oh-my-pi/hashline";
|
|
9
9
|
import { blockRangeAt } from "@oh-my-pi/pi-natives";
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* `blockRangeAt` runs a full synchronous tree-sitter parse of `text` per
|
|
13
|
+
* call, and streaming previews re-resolve the same (text, line) every
|
|
14
|
+
* streamed chunk. Memoize by content: identical text + line always yields the
|
|
15
|
+
* same span. FIFO-bounded; hashing the text is orders of magnitude cheaper
|
|
16
|
+
* than re-parsing it.
|
|
17
|
+
*/
|
|
18
|
+
const resolutionCache = new Map<string, { start: number; end: number } | null>();
|
|
19
|
+
const RESOLUTION_CACHE_MAX = 512;
|
|
20
|
+
|
|
11
21
|
export const nativeBlockResolver: BlockResolver = ({ path, text, line }) => {
|
|
22
|
+
const key = `${Bun.hash(text).toString(36)}:${text.length}:${line}:${path}`;
|
|
23
|
+
const cached = resolutionCache.get(key);
|
|
24
|
+
if (cached !== undefined) return cached;
|
|
12
25
|
const range = blockRangeAt({ code: text, path, line });
|
|
13
|
-
|
|
26
|
+
const result = range ? { start: range.startLine, end: range.endLine } : null;
|
|
27
|
+
if (resolutionCache.size >= RESOLUTION_CACHE_MAX) {
|
|
28
|
+
const oldest = resolutionCache.keys().next().value;
|
|
29
|
+
if (oldest !== undefined) resolutionCache.delete(oldest);
|
|
30
|
+
}
|
|
31
|
+
resolutionCache.set(key, result);
|
|
32
|
+
return result;
|
|
14
33
|
};
|