@oh-my-pi/pi-coding-agent 15.10.10 → 15.10.11
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 +95 -4
- package/dist/cli.js +23087 -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 +1 -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/commands/launch.d.ts +1 -1
- package/dist/types/commands/read.d.ts +1 -1
- package/dist/types/commands/usage.d.ts +25 -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 +7 -219
- package/dist/types/config/model-resolver.d.ts +16 -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 +12 -7
- package/dist/types/config/settings.d.ts +1 -1
- package/dist/types/debug/log-viewer.d.ts +1 -1
- package/dist/types/debug/raw-sse.d.ts +1 -1
- 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/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/extensions/types.d.ts +3 -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/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/interactive-mode.d.ts +1 -1
- 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 +2 -1
- package/dist/types/session/agent-session.d.ts +1 -1
- package/dist/types/session/auth-broker-config.d.ts +4 -0
- package/dist/types/session/session-manager.d.ts +1 -1
- package/dist/types/slash-commands/helpers/stats-dashboard.d.ts +13 -0
- package/dist/types/ssh/connection-manager.d.ts +8 -0
- package/dist/types/task/parallel.d.ts +2 -2
- package/dist/types/task/worktree.d.ts +2 -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/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/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/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 +1 -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 +1 -0
- package/src/cli.ts +69 -5
- package/src/commands/complete.ts +1 -1
- package/src/commands/launch.ts +1 -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 +1 -1
- package/src/config/append-only-context-mode.ts +6 -12
- package/src/config/model-discovery.ts +554 -0
- package/src/config/model-registry.ts +231 -1019
- package/src/config/model-resolver.ts +113 -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 +18 -4
- package/src/config/settings.ts +37 -1
- 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 +8 -2
- 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 +2 -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/index.ts +0 -2
- package/src/eval/py/kernel.ts +19 -0
- package/src/eval/py/runner.py +107 -3
- package/src/exec/bash-executor.ts +3 -1
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +3 -1
- package/src/extensibility/extensions/types.ts +3 -2
- package/src/extensibility/plugins/legacy-pi-compat.ts +20 -3
- 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 +8 -8
- 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 +165 -17
- package/src/mcp/json-rpc.ts +35 -5
- package/src/mcp/transports/stdio.ts +7 -1
- package/src/memories/index.ts +2 -1
- package/src/mnemopi/backend.ts +25 -3
- package/src/mnemopi/state.ts +38 -2
- package/src/modes/components/agent-dashboard.ts +10 -7
- package/src/modes/components/assistant-message.ts +19 -13
- 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 +1 -1
- 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 +1 -1
- 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 +10 -3
- package/src/modes/controllers/event-controller.ts +73 -4
- package/src/modes/controllers/input-controller.ts +1 -1
- package/src/modes/controllers/mcp-command-controller.ts +1 -1
- package/src/modes/controllers/selector-controller.ts +1 -1
- package/src/modes/controllers/streaming-reveal.ts +85 -18
- package/src/modes/interactive-mode.ts +3 -9
- 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 +2 -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 +23 -10
- package/src/session/agent-session.ts +44 -10
- package/src/session/auth-broker-config.ts +30 -1
- package/src/session/session-manager.ts +2 -2
- package/src/session/streaming-output.ts +23 -2
- package/src/slash-commands/builtin-registry.ts +20 -0
- package/src/slash-commands/helpers/stats-dashboard.ts +85 -0
- package/src/ssh/connection-manager.ts +27 -0
- package/src/task/commands.ts +2 -1
- package/src/task/executor.ts +61 -53
- package/src/task/index.ts +137 -60
- package/src/task/parallel.ts +3 -3
- package/src/task/render.ts +2 -2
- package/src/task/worktree.ts +64 -56
- package/src/thinking.ts +2 -1
- package/src/tiny/title-client.ts +26 -11
- 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 +54 -13
- 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 +3 -9
- 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/file-mentions.ts +2 -1
- 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/tools/search.ts
CHANGED
|
@@ -112,6 +112,10 @@ const INTERNAL_TOTAL_CAP = 2000;
|
|
|
112
112
|
* silently returns no matches for files larger than this; surface a warning
|
|
113
113
|
* when the caller explicitly targeted such a file so they know to chunk it. */
|
|
114
114
|
const NATIVE_GREP_MAX_FILE_BYTES = 4 * 1024 * 1024;
|
|
115
|
+
/** Wall-clock budget for a single native grep invocation. Without it, an
|
|
116
|
+
* aborted or runaway search (huge tree, network mount) keeps burning CPU on
|
|
117
|
+
* the native thread pool after the JS promise is abandoned. */
|
|
118
|
+
const SEARCH_GREP_TIMEOUT_MS = 30_000;
|
|
115
119
|
|
|
116
120
|
/**
|
|
117
121
|
* Parsed `paths` entry — a path (possibly archive-shaped) plus an optional
|
|
@@ -351,6 +355,8 @@ function makeVirtualMatch(
|
|
|
351
355
|
lineIndex: number,
|
|
352
356
|
contextBefore: number,
|
|
353
357
|
contextAfter: number,
|
|
358
|
+
lastEmittedLine: number,
|
|
359
|
+
nextMatchLine: number,
|
|
354
360
|
): GrepMatch {
|
|
355
361
|
const lineNumber = lineIndex + 1;
|
|
356
362
|
const { text, wasTruncated } = truncateLine(lines[lineIndex] ?? "", DEFAULT_MAX_COLUMN);
|
|
@@ -363,7 +369,9 @@ function makeVirtualMatch(
|
|
|
363
369
|
|
|
364
370
|
if (contextBefore > 0) {
|
|
365
371
|
const before: NonNullable<GrepMatch["contextBefore"]> = [];
|
|
366
|
-
|
|
372
|
+
// Start after the previous match's last emitted line so adjacent matches
|
|
373
|
+
// never repeat or rewind context lines (mirrors native grep's sink).
|
|
374
|
+
const start = Math.max(0, lineIndex - contextBefore, lastEmittedLine);
|
|
367
375
|
for (let idx = start; idx < lineIndex; idx++) {
|
|
368
376
|
const contextLineNumber = idx + 1;
|
|
369
377
|
if (lineAllowed(contextLineNumber, resource.ranges)) {
|
|
@@ -375,7 +383,8 @@ function makeVirtualMatch(
|
|
|
375
383
|
|
|
376
384
|
if (contextAfter > 0) {
|
|
377
385
|
const after: NonNullable<GrepMatch["contextAfter"]> = [];
|
|
378
|
-
|
|
386
|
+
// Stop before the next match line; it is emitted as a match itself.
|
|
387
|
+
const end = Math.min(lines.length - 1, lineIndex + contextAfter, nextMatchLine - 2);
|
|
379
388
|
for (let idx = lineIndex + 1; idx <= end; idx++) {
|
|
380
389
|
const contextLineNumber = idx + 1;
|
|
381
390
|
if (lineAllowed(contextLineNumber, resource.ranges)) {
|
|
@@ -388,6 +397,38 @@ function makeVirtualMatch(
|
|
|
388
397
|
return match;
|
|
389
398
|
}
|
|
390
399
|
|
|
400
|
+
/** Build matches for ascending matched line indexes with forward-only,
|
|
401
|
+
* deduplicated context windows (line numbers never repeat or go backwards
|
|
402
|
+
* within one resource). */
|
|
403
|
+
function buildVirtualMatches(
|
|
404
|
+
resource: VirtualSearchResource,
|
|
405
|
+
lines: readonly string[],
|
|
406
|
+
matchedIndexes: readonly number[],
|
|
407
|
+
contextBefore: number,
|
|
408
|
+
contextAfter: number,
|
|
409
|
+
maxCount: number,
|
|
410
|
+
): GrepMatch[] {
|
|
411
|
+
const matches: GrepMatch[] = [];
|
|
412
|
+
let lastEmittedLine = 0;
|
|
413
|
+
for (let i = 0; i < matchedIndexes.length && matches.length < maxCount; i++) {
|
|
414
|
+
const lineIndex = matchedIndexes[i];
|
|
415
|
+
const nextMatchLine = i + 1 < matchedIndexes.length ? matchedIndexes[i + 1] + 1 : Number.POSITIVE_INFINITY;
|
|
416
|
+
const match = makeVirtualMatch(
|
|
417
|
+
resource,
|
|
418
|
+
lines,
|
|
419
|
+
lineIndex,
|
|
420
|
+
contextBefore,
|
|
421
|
+
contextAfter,
|
|
422
|
+
lastEmittedLine,
|
|
423
|
+
nextMatchLine,
|
|
424
|
+
);
|
|
425
|
+
const after = match.contextAfter;
|
|
426
|
+
lastEmittedLine = after && after.length > 0 ? after[after.length - 1].lineNumber : match.lineNumber;
|
|
427
|
+
matches.push(match);
|
|
428
|
+
}
|
|
429
|
+
return matches;
|
|
430
|
+
}
|
|
431
|
+
|
|
391
432
|
function compileVirtualRegex(pattern: string, ignoreCase: boolean, multiline: boolean): RegExp {
|
|
392
433
|
const flags = `${ignoreCase ? "i" : ""}${multiline ? "gm" : ""}`;
|
|
393
434
|
try {
|
|
@@ -406,24 +447,18 @@ function searchVirtualResourceLines(
|
|
|
406
447
|
maxCount: number,
|
|
407
448
|
): { matches: GrepMatch[]; totalMatches: number; limitReached: boolean } {
|
|
408
449
|
const lines = splitSearchLines(resource.content);
|
|
409
|
-
const
|
|
410
|
-
let totalMatches = 0;
|
|
411
|
-
let limitReached = false;
|
|
450
|
+
const matchedIndexes: number[] = [];
|
|
412
451
|
|
|
413
452
|
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
414
453
|
const lineNumber = lineIndex + 1;
|
|
415
454
|
if (!lineAllowed(lineNumber, resource.ranges)) continue;
|
|
416
455
|
regex.lastIndex = 0;
|
|
417
456
|
if (!regex.test(lines[lineIndex] ?? "")) continue;
|
|
418
|
-
|
|
419
|
-
if (matches.length >= maxCount) {
|
|
420
|
-
limitReached = true;
|
|
421
|
-
continue;
|
|
422
|
-
}
|
|
423
|
-
matches.push(makeVirtualMatch(resource, lines, lineIndex, contextBefore, contextAfter));
|
|
457
|
+
matchedIndexes.push(lineIndex);
|
|
424
458
|
}
|
|
425
459
|
|
|
426
|
-
|
|
460
|
+
const matches = buildVirtualMatches(resource, lines, matchedIndexes, contextBefore, contextAfter, maxCount);
|
|
461
|
+
return { matches, totalMatches: matchedIndexes.length, limitReached: matchedIndexes.length > matches.length };
|
|
427
462
|
}
|
|
428
463
|
|
|
429
464
|
function searchVirtualResourceMultiline(
|
|
@@ -434,10 +469,8 @@ function searchVirtualResourceMultiline(
|
|
|
434
469
|
maxCount: number,
|
|
435
470
|
): { matches: GrepMatch[]; totalMatches: number; limitReached: boolean } {
|
|
436
471
|
const indexed = indexSearchLines(resource.content);
|
|
437
|
-
const matches: GrepMatch[] = [];
|
|
438
472
|
const matchedLines = new Set<number>();
|
|
439
|
-
|
|
440
|
-
let limitReached = false;
|
|
473
|
+
const matchedIndexes: number[] = [];
|
|
441
474
|
|
|
442
475
|
while (true) {
|
|
443
476
|
const match = regex.exec(resource.content);
|
|
@@ -447,12 +480,7 @@ function searchVirtualResourceMultiline(
|
|
|
447
480
|
const lineNumber = lineIndex + 1;
|
|
448
481
|
if (!matchedLines.has(lineNumber) && lineAllowed(lineNumber, resource.ranges)) {
|
|
449
482
|
matchedLines.add(lineNumber);
|
|
450
|
-
|
|
451
|
-
if (matches.length >= maxCount) {
|
|
452
|
-
limitReached = true;
|
|
453
|
-
} else {
|
|
454
|
-
matches.push(makeVirtualMatch(resource, indexed.lines, lineIndex, contextBefore, contextAfter));
|
|
455
|
-
}
|
|
483
|
+
matchedIndexes.push(lineIndex);
|
|
456
484
|
}
|
|
457
485
|
}
|
|
458
486
|
if (match[0].length === 0) {
|
|
@@ -460,7 +488,8 @@ function searchVirtualResourceMultiline(
|
|
|
460
488
|
}
|
|
461
489
|
}
|
|
462
490
|
|
|
463
|
-
|
|
491
|
+
const matches = buildVirtualMatches(resource, indexed.lines, matchedIndexes, contextBefore, contextAfter, maxCount);
|
|
492
|
+
return { matches, totalMatches: matchedIndexes.length, limitReached: matchedIndexes.length > matches.length };
|
|
464
493
|
}
|
|
465
494
|
|
|
466
495
|
function searchVirtualResources(
|
|
@@ -666,10 +695,12 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
666
695
|
const { pattern, paths: rawPaths, i, gitignore, skip } = params;
|
|
667
696
|
|
|
668
697
|
return untilAborted(signal, async () => {
|
|
669
|
-
|
|
670
|
-
|
|
698
|
+
// Preserve the pattern verbatim — leading/trailing whitespace is
|
|
699
|
+
// meaningful in regexes (indentation anchors, trailing-space matches).
|
|
700
|
+
if (!pattern.trim()) {
|
|
671
701
|
throw new ToolError("Pattern must not be empty");
|
|
672
702
|
}
|
|
703
|
+
const normalizedPattern = pattern;
|
|
673
704
|
|
|
674
705
|
const normalizedSkip =
|
|
675
706
|
skip === undefined || skip === null ? 0 : Number.isFinite(skip) ? Math.floor(skip) : Number.NaN;
|
|
@@ -729,7 +760,11 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
729
760
|
}
|
|
730
761
|
}
|
|
731
762
|
|
|
732
|
-
if (
|
|
763
|
+
if (
|
|
764
|
+
archiveUnreadable.length > 0 &&
|
|
765
|
+
searchablePaths.length === archiveUnreadable.length &&
|
|
766
|
+
virtualResources.length === 0
|
|
767
|
+
) {
|
|
733
768
|
// All inputs were archive selectors we couldn't materialize; surface the
|
|
734
769
|
// reason instead of a downstream "path not found" from the scope resolver.
|
|
735
770
|
throw new ToolError(
|
|
@@ -823,6 +858,7 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
823
858
|
filesSearched: 0,
|
|
824
859
|
limitReached: false,
|
|
825
860
|
};
|
|
861
|
+
let skippedOversizedCount = 0;
|
|
826
862
|
try {
|
|
827
863
|
if (searchablePaths.length > 0) {
|
|
828
864
|
if (exactFilePaths || multiTargets) {
|
|
@@ -852,9 +888,13 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
852
888
|
contextAfter: normalizedContextAfter,
|
|
853
889
|
maxColumns: DEFAULT_MAX_COLUMN,
|
|
854
890
|
mode: effectiveOutputMode,
|
|
891
|
+
maxCountPerFile: perFileMatchCap + 1,
|
|
892
|
+
signal,
|
|
893
|
+
timeoutMs: SEARCH_GREP_TIMEOUT_MS,
|
|
855
894
|
},
|
|
856
895
|
undefined,
|
|
857
896
|
);
|
|
897
|
+
skippedOversizedCount += targetResult.skippedOversized ?? 0;
|
|
858
898
|
limitReached = limitReached || Boolean(targetResult.limitReached);
|
|
859
899
|
totalMatches += targetResult.totalMatches;
|
|
860
900
|
filesSearched += targetResult.filesSearched;
|
|
@@ -887,15 +927,24 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
887
927
|
contextAfter: normalizedContextAfter,
|
|
888
928
|
maxColumns: DEFAULT_MAX_COLUMN,
|
|
889
929
|
mode: effectiveOutputMode,
|
|
930
|
+
maxCountPerFile: perFileMatchCap + 1,
|
|
931
|
+
signal,
|
|
932
|
+
timeoutMs: SEARCH_GREP_TIMEOUT_MS,
|
|
890
933
|
},
|
|
891
934
|
undefined,
|
|
892
935
|
);
|
|
936
|
+
skippedOversizedCount = result.skippedOversized ?? 0;
|
|
893
937
|
}
|
|
894
938
|
}
|
|
895
939
|
} catch (err) {
|
|
896
940
|
if (err instanceof Error && /^regex(?: parse)? error/i.test(err.message)) {
|
|
897
941
|
throw new ToolError(err.message.replace(/^regex(?: parse)? error:?\s*/i, "Invalid regex: "));
|
|
898
942
|
}
|
|
943
|
+
if (err instanceof Error && err.message.includes("Aborted: Timeout")) {
|
|
944
|
+
throw new ToolError(
|
|
945
|
+
`Search timed out after ${SEARCH_GREP_TIMEOUT_MS / 1000}s; narrow paths or pattern, or scope with \`find\` first`,
|
|
946
|
+
);
|
|
947
|
+
}
|
|
899
948
|
throw err;
|
|
900
949
|
}
|
|
901
950
|
const virtualResult = searchVirtualResources(
|
|
@@ -971,6 +1020,9 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
971
1020
|
}
|
|
972
1021
|
}
|
|
973
1022
|
const totalFiles = fileOrder.length;
|
|
1023
|
+
// When native grep stopped at its internal cap, files past the cap were
|
|
1024
|
+
// never surfaced — the file total is only a lower bound.
|
|
1025
|
+
const totalFilesLabel = result.limitReached ? `${totalFiles}+` : `${totalFiles}`;
|
|
974
1026
|
// Single-file scopes can't paginate — there is one file by definition.
|
|
975
1027
|
const canPaginate = isMultiScope;
|
|
976
1028
|
const skipFiles = canPaginate ? Math.min(normalizedSkip, totalFiles) : 0;
|
|
@@ -993,7 +1045,7 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
993
1045
|
}
|
|
994
1046
|
const nextSkip = skipFiles + windowFiles.length;
|
|
995
1047
|
const limitMessage = fileLimitReached
|
|
996
|
-
? `Showing files ${skipFiles + 1}-${nextSkip} of ${
|
|
1048
|
+
? `Showing files ${skipFiles + 1}-${nextSkip} of ${totalFilesLabel}. Use skip=${nextSkip} for the next page, or narrow paths/pattern.`
|
|
997
1049
|
: "";
|
|
998
1050
|
const { record: recordFile, list: fileList } = createFileRecorder();
|
|
999
1051
|
const fileMatchCounts = new Map<string, number>();
|
|
@@ -1025,6 +1077,12 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
1025
1077
|
const limitMb = Math.floor(NATIVE_GREP_MAX_FILE_BYTES / (1024 * 1024));
|
|
1026
1078
|
return `Skipped oversized files (>${limitMb}MB grep limit; split the file or narrow with \`read\`): ${oversized.join(", ")}`;
|
|
1027
1079
|
})();
|
|
1080
|
+
// Directory/multi-target scopes: native grep counts oversized skips but
|
|
1081
|
+
// cannot name them; explicit-file scopes are covered (with names) above.
|
|
1082
|
+
const oversizedScanNote =
|
|
1083
|
+
!oversizedNote && skippedOversizedCount > 0
|
|
1084
|
+
? `Skipped ${skippedOversizedCount} oversized file(s) (>${Math.floor(NATIVE_GREP_MAX_FILE_BYTES / (1024 * 1024))}MB grep limit); target them directly with \`read\``
|
|
1085
|
+
: undefined;
|
|
1028
1086
|
const archiveNote =
|
|
1029
1087
|
archiveUnreadable.length > 0
|
|
1030
1088
|
? `Skipped archive entries (search supports text members only): ${archiveUnreadable.join(", ")}`
|
|
@@ -1036,8 +1094,9 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
1036
1094
|
const missingPathsNote =
|
|
1037
1095
|
missingPathsForNote.length > 0 ? `Skipped missing paths: ${missingPathsForNote.join(", ")}` : undefined;
|
|
1038
1096
|
const warningNote =
|
|
1039
|
-
[missingPathsNote, archiveNote, oversizedNote]
|
|
1040
|
-
|
|
1097
|
+
[missingPathsNote, archiveNote, oversizedNote, oversizedScanNote]
|
|
1098
|
+
.filter((s): s is string => Boolean(s))
|
|
1099
|
+
.join("\n") || undefined;
|
|
1041
1100
|
if (selectedMatches.length === 0) {
|
|
1042
1101
|
const details: SearchToolDetails = {
|
|
1043
1102
|
scopePath,
|
|
@@ -1049,7 +1108,11 @@ export class SearchTool implements AgentTool<typeof searchSchema, SearchToolDeta
|
|
|
1049
1108
|
truncated: false,
|
|
1050
1109
|
missingPaths: missingPaths.length > 0 ? missingPaths : undefined,
|
|
1051
1110
|
};
|
|
1052
|
-
const
|
|
1111
|
+
const skipPastEnd = canPaginate && normalizedSkip > 0 && totalFiles > 0 && skipFiles >= totalFiles;
|
|
1112
|
+
const noMatchText = skipPastEnd
|
|
1113
|
+
? `No more results (${totalFilesLabel} files total; skip=${normalizedSkip} is past the end)`
|
|
1114
|
+
: "No matches found";
|
|
1115
|
+
const text = warningNote ? `${noMatchText}\n${warningNote}` : noMatchText;
|
|
1053
1116
|
return toolResult(details).text(text).done();
|
|
1054
1117
|
}
|
|
1055
1118
|
const outputLines: string[] = [];
|
|
@@ -17,6 +17,8 @@ const SQLITE_PATH_PATTERN = /\.(?:sqlite3?|db3?)(?=(?::|\?|$))/gi;
|
|
|
17
17
|
const DEFAULT_QUERY_LIMIT = 20;
|
|
18
18
|
const DEFAULT_SCHEMA_SAMPLE_LIMIT = 5;
|
|
19
19
|
const MAX_QUERY_LIMIT = 500;
|
|
20
|
+
/** Row cap for raw `?q=` SQL — protects against `SELECT *` on multi-million-row tables. */
|
|
21
|
+
export const MAX_RAW_QUERY_ROWS = 1000;
|
|
20
22
|
const MAX_RENDER_WIDTH = 120;
|
|
21
23
|
const MAX_COLUMN_WIDTH = 40;
|
|
22
24
|
const MIN_COLUMN_WIDTH = 1;
|
|
@@ -659,15 +661,25 @@ export function getRowByRowId(db: Database, table: string, key: string): Record<
|
|
|
659
661
|
.get(binding);
|
|
660
662
|
}
|
|
661
663
|
|
|
662
|
-
export function executeReadQuery(
|
|
664
|
+
export function executeReadQuery(
|
|
665
|
+
db: Database,
|
|
666
|
+
sql: string,
|
|
667
|
+
): { columns: string[]; rows: Record<string, unknown>[]; truncated: boolean } {
|
|
663
668
|
const statement = db.prepare<SqliteRow, []>(sql);
|
|
664
669
|
if (statement.paramsCount > 0) {
|
|
665
670
|
throw new ToolError("SQLite raw queries do not support bound parameters");
|
|
666
671
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
672
|
+
const columns = [...statement.columnNames];
|
|
673
|
+
const rows: SqliteRow[] = [];
|
|
674
|
+
let truncated = false;
|
|
675
|
+
for (const row of statement.iterate()) {
|
|
676
|
+
if (rows.length >= MAX_RAW_QUERY_ROWS) {
|
|
677
|
+
truncated = true;
|
|
678
|
+
break;
|
|
679
|
+
}
|
|
680
|
+
rows.push(row);
|
|
681
|
+
}
|
|
682
|
+
return { columns, rows, truncated };
|
|
671
683
|
}
|
|
672
684
|
|
|
673
685
|
export function insertRow(db: Database, table: string, data: Record<string, unknown>): void {
|
package/src/tools/ssh.ts
CHANGED
|
@@ -10,7 +10,7 @@ import type { Theme } from "../modes/theme/theme";
|
|
|
10
10
|
import sshDescriptionBase from "../prompts/tools/ssh.md" with { type: "text" };
|
|
11
11
|
import { DEFAULT_MAX_BYTES, streamTailUpdates, TailBuffer } from "../session/streaming-output";
|
|
12
12
|
import type { SSHHostInfo } from "../ssh/connection-manager";
|
|
13
|
-
import { ensureHostInfo,
|
|
13
|
+
import { ensureHostInfo, getCachedHostInfoSync } from "../ssh/connection-manager";
|
|
14
14
|
import { executeSSH } from "../ssh/ssh-executor";
|
|
15
15
|
import { renderStatusLine } from "../tui";
|
|
16
16
|
import { CachedOutputBlock, markFramedBlockComponent } from "../tui/output-block";
|
|
@@ -33,8 +33,8 @@ export interface SSHToolDetails {
|
|
|
33
33
|
meta?: OutputMeta;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
const info =
|
|
36
|
+
function formatHostEntry(host: SSHHost): string {
|
|
37
|
+
const info = getCachedHostInfoSync(host);
|
|
38
38
|
|
|
39
39
|
let shell: string;
|
|
40
40
|
if (!info) {
|
|
@@ -59,12 +59,12 @@ async function formatHostEntry(host: SSHHost): Promise<string> {
|
|
|
59
59
|
return `- ${host.name} (${host.host}) | ${shell}`;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
function formatDescription(hosts: SSHHost[]): string {
|
|
63
63
|
const baseDescription = prompt.render(sshDescriptionBase);
|
|
64
64
|
if (hosts.length === 0) {
|
|
65
65
|
return baseDescription;
|
|
66
66
|
}
|
|
67
|
-
const hostList =
|
|
67
|
+
const hostList = hosts.map(formatHostEntry).join("\n");
|
|
68
68
|
return `${baseDescription}\n\nAvailable hosts:\n${hostList}`;
|
|
69
69
|
}
|
|
70
70
|
|
|
@@ -206,7 +206,7 @@ export async function loadSshTool(session: ToolSession): Promise<SshTool | null>
|
|
|
206
206
|
const descriptionHosts = hostNames
|
|
207
207
|
.map(name => hostsByName.get(name))
|
|
208
208
|
.filter((host): host is SSHHost => host !== undefined);
|
|
209
|
-
const description =
|
|
209
|
+
const description = formatDescription(descriptionHosts);
|
|
210
210
|
|
|
211
211
|
return new SshTool(session, hostNames, hostsByName, description);
|
|
212
212
|
}
|
|
@@ -245,7 +245,7 @@ export const sshToolRenderer = {
|
|
|
245
245
|
const cmdLines = formatSshCommandLines(command, uiTheme);
|
|
246
246
|
const outputBlock = new CachedOutputBlock();
|
|
247
247
|
return markFramedBlockComponent({
|
|
248
|
-
render: (width: number): string[] =>
|
|
248
|
+
render: (width: number): readonly string[] =>
|
|
249
249
|
outputBlock.render(
|
|
250
250
|
{
|
|
251
251
|
header,
|
|
@@ -282,7 +282,7 @@ export const sshToolRenderer = {
|
|
|
282
282
|
const outputBlock = new CachedOutputBlock();
|
|
283
283
|
|
|
284
284
|
return markFramedBlockComponent({
|
|
285
|
-
render: (width: number): string[] => {
|
|
285
|
+
render: (width: number): readonly string[] => {
|
|
286
286
|
// REACTIVE: read mutable options at render time
|
|
287
287
|
const { expanded, renderContext } = options;
|
|
288
288
|
// Strip LLM-facing notice so we don't echo it next to the styled warning.
|
package/src/tools/todo.ts
CHANGED
|
@@ -285,6 +285,22 @@ function initPhases(entry: TodoOpEntryValue, errors: string[]): TodoPhase[] {
|
|
|
285
285
|
errors.push("Missing list for init operation");
|
|
286
286
|
return [];
|
|
287
287
|
}
|
|
288
|
+
// Duplicate phase names / task contents would be permanently unaddressable
|
|
289
|
+
// (every targeting op resolves the first match), so reject them up front.
|
|
290
|
+
const seenPhases = new Set<string>();
|
|
291
|
+
const seenTasks = new Set<string>();
|
|
292
|
+
for (const listEntry of entry.list) {
|
|
293
|
+
if (seenPhases.has(listEntry.phase)) {
|
|
294
|
+
errors.push(`Duplicate phase "${listEntry.phase}" in init list`);
|
|
295
|
+
}
|
|
296
|
+
seenPhases.add(listEntry.phase);
|
|
297
|
+
for (const content of listEntry.items) {
|
|
298
|
+
if (seenTasks.has(content)) {
|
|
299
|
+
errors.push(`Duplicate task "${content}" in init list`);
|
|
300
|
+
}
|
|
301
|
+
seenTasks.add(content);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
288
304
|
return entry.list.map(listEntry => ({
|
|
289
305
|
name: listEntry.phase,
|
|
290
306
|
tasks: listEntry.items.map<TodoItem>(content => ({ content, status: "pending" })),
|
|
@@ -301,6 +317,19 @@ function appendItems(phases: TodoPhase[], entry: TodoOpEntryValue, errors: strin
|
|
|
301
317
|
return phases;
|
|
302
318
|
}
|
|
303
319
|
|
|
320
|
+
// Validate the whole batch before mutating so a failing op reports every
|
|
321
|
+
// duplicate and leaves nothing half-applied.
|
|
322
|
+
const seen = new Set<string>();
|
|
323
|
+
let hasDuplicate = false;
|
|
324
|
+
for (const content of entry.items) {
|
|
325
|
+
if (seen.has(content) || findTaskByContent(phases, content)) {
|
|
326
|
+
errors.push(`Task "${content}" already exists`);
|
|
327
|
+
hasDuplicate = true;
|
|
328
|
+
}
|
|
329
|
+
seen.add(content);
|
|
330
|
+
}
|
|
331
|
+
if (hasDuplicate) return phases;
|
|
332
|
+
|
|
304
333
|
let phase = findPhaseByName(phases, entry.phase);
|
|
305
334
|
if (!phase) {
|
|
306
335
|
phase = { name: entry.phase, tasks: [] };
|
|
@@ -308,10 +337,6 @@ function appendItems(phases: TodoPhase[], entry: TodoOpEntryValue, errors: strin
|
|
|
308
337
|
}
|
|
309
338
|
|
|
310
339
|
for (const content of entry.items) {
|
|
311
|
-
if (findTaskByContent(phases, content)) {
|
|
312
|
-
errors.push(`Task "${content}" already exists`);
|
|
313
|
-
return phases;
|
|
314
|
-
}
|
|
315
340
|
phase.tasks.push({ content, status: "pending" });
|
|
316
341
|
}
|
|
317
342
|
return phases;
|
|
@@ -618,14 +643,19 @@ export class TodoTool implements AgentTool<typeof todoSchema, TodoToolDetails> {
|
|
|
618
643
|
const { phases: updated, errors } = readOnly
|
|
619
644
|
? { phases: previousPhases, errors: [] as string[] }
|
|
620
645
|
: applyParams(clonePhases(previousPhases), params);
|
|
621
|
-
|
|
622
|
-
|
|
646
|
+
// A batch with any error is discarded wholesale: persisting a
|
|
647
|
+
// half-applied batch makes the natural retry hit "already exists" for
|
|
648
|
+
// the ops that did land. State and rendered summary stay at previous.
|
|
649
|
+
const failed = errors.length > 0;
|
|
650
|
+
const effective = failed ? previousPhases : updated;
|
|
651
|
+
const completedTasks = readOnly || failed ? [] : getCompletionTransitions(previousPhases, updated);
|
|
652
|
+
if (!readOnly && !failed) this.session.setTodoPhases?.(updated);
|
|
623
653
|
const storage = this.session.getSessionFile() ? "session" : "memory";
|
|
624
|
-
const details: TodoToolDetails = { phases:
|
|
654
|
+
const details: TodoToolDetails = { phases: effective, storage };
|
|
625
655
|
if (completedTasks.length > 0) details.completedTasks = completedTasks;
|
|
626
656
|
|
|
627
657
|
return {
|
|
628
|
-
content: [{ type: "text", text: formatSummary(
|
|
658
|
+
content: [{ type: "text", text: formatSummary(effective, errors, readOnly) }],
|
|
629
659
|
details,
|
|
630
660
|
isError: errors.length > 0 ? true : undefined,
|
|
631
661
|
};
|