mixdog 0.9.2 → 0.9.4
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/package.json +8 -3
- package/scripts/anthropic-maxtokens-test.mjs +119 -0
- package/scripts/bench/lead-review-tasks-r3.json +20 -0
- package/scripts/bench/lead-review-tasks.json +20 -0
- package/scripts/bench/r4-mixed-tasks.json +20 -0
- package/scripts/bench/review-tasks.json +20 -0
- package/scripts/bench/round-codex.json +114 -0
- package/scripts/bench/round-mixdog-lead-r3.json +269 -0
- package/scripts/bench/round-mixdog-lead.json +269 -0
- package/scripts/bench/round-mixdog.json +126 -0
- package/scripts/bench/round-r10-bigsample.json +679 -0
- package/scripts/bench/round-r11-codexalign.json +257 -0
- package/scripts/bench/round-r4-codex.json +114 -0
- package/scripts/bench/round-r4-mixed.json +225 -0
- package/scripts/bench/round-r5-gpt-lead.json +259 -0
- package/scripts/bench/round-r6-codex.json +114 -0
- package/scripts/bench/round-r6-solo.json +257 -0
- package/scripts/bench/round-r7-full.json +254 -0
- package/scripts/bench/round-r8-fulldefault.json +255 -0
- package/scripts/bench-run.mjs +215 -29
- package/scripts/build-tui.mjs +13 -1
- package/scripts/explore-bench.mjs +124 -0
- package/scripts/freevar-smoke.mjs +95 -0
- package/scripts/hook-bus-test.mjs +191 -0
- package/scripts/internal-comms-bench.mjs +1 -0
- package/scripts/internal-comms-smoke.mjs +10 -9
- package/scripts/mouse-probe.mjs +45 -0
- package/scripts/output-style-bench.mjs +13 -6
- package/scripts/output-style-smoke.mjs +4 -4
- package/scripts/path-suffix-test.mjs +57 -0
- package/scripts/provider-toolcall-test.mjs +7 -3
- package/scripts/recall-bench.mjs +207 -0
- package/scripts/recall-usecase-cases.json +18 -0
- package/scripts/recall-usecase-probe.json +6 -0
- package/scripts/session-bench.mjs +152 -6
- package/scripts/tool-smoke.mjs +30 -67
- package/scripts/tui-render-smoke.mjs +90 -0
- package/scripts/webhook-smoke.mjs +208 -0
- package/src/agents/debugger/AGENT.md +5 -2
- package/src/agents/heavy-worker/AGENT.md +21 -11
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +3 -2
- package/src/agents/worker/AGENT.md +21 -11
- package/src/lib/rules-builder.cjs +4 -0
- package/src/mixdog-session-runtime.mjs +933 -3731
- package/src/output-styles/default.md +34 -9
- package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
- package/src/output-styles/minimal.md +4 -1
- package/src/output-styles/simple.md +22 -7
- package/src/repl.mjs +5 -5
- package/src/rules/agent/00-common.md +2 -0
- package/src/rules/agent/30-explorer.md +8 -11
- package/src/rules/lead/lead-brief.md +12 -0
- package/src/rules/lead/lead-tool.md +2 -9
- package/src/rules/shared/01-tool.md +11 -5
- package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +25 -0
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +100 -23
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +6 -15
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +362 -0
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +410 -0
- package/src/runtime/agent/orchestrator/agent-trace.mjs +16 -735
- package/src/runtime/agent/orchestrator/config.mjs +69 -2
- package/src/runtime/agent/orchestrator/context/collect.mjs +51 -0
- package/src/runtime/agent/orchestrator/mcp/client.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +63 -21
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +209 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +489 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +97 -1343
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +78 -10
- package/src/runtime/agent/orchestrator/providers/api-usage.mjs +1 -13
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +81 -0
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +248 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +303 -0
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +505 -0
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +44 -1014
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +18 -4
- package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +86 -11
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +348 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +54 -20
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +19 -12
- package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +58 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +368 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +760 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +41 -1142
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +732 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +303 -2119
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +140 -995
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +227 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +436 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +1105 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +38 -12
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +7 -8
- package/src/runtime/agent/orchestrator/session/compact/budget.mjs +288 -0
- package/src/runtime/agent/orchestrator/session/compact/constants.mjs +85 -0
- package/src/runtime/agent/orchestrator/session/compact/engine.mjs +749 -0
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +82 -0
- package/src/runtime/agent/orchestrator/session/compact/summary-schema.mjs +315 -0
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +643 -0
- package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +326 -0
- package/src/runtime/agent/orchestrator/session/compact.mjs +40 -2282
- package/src/runtime/agent/orchestrator/session/loop/compact-debug.mjs +28 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +274 -0
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +38 -0
- package/src/runtime/agent/orchestrator/session/loop/env.mjs +14 -0
- package/src/runtime/agent/orchestrator/session/loop/hidden-agents.mjs +21 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +47 -0
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +182 -0
- package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +63 -0
- package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +100 -0
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +52 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +218 -0
- package/src/runtime/agent/orchestrator/session/loop/transcript-repair.mjs +101 -0
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +35 -0
- package/src/runtime/agent/orchestrator/session/loop.mjs +409 -1304
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +230 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +149 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -0
- package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +406 -0
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +80 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +303 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +210 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +226 -2114
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +189 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +74 -179
- package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +70 -20
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +22 -2
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +33 -42
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
- package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
- package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +29 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +126 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +81 -87
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +161 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-process.mjs +108 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +78 -304
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +11 -6
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +303 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +43 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +382 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +551 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +295 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +158 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/lang-predicates.mjs +128 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/memory-cache.mjs +66 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +44 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1080 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/source-access.mjs +81 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +19 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +280 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +347 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/code-graph.mjs +36 -4277
- package/src/runtime/agent/orchestrator/tools/graph-binary-fetcher.mjs +6 -3
- package/src/runtime/agent/orchestrator/tools/patch/constants.mjs +9 -0
- package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +171 -0
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +471 -0
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +436 -0
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +342 -0
- package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +359 -0
- package/src/runtime/agent/orchestrator/tools/patch/paths.mjs +340 -0
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +643 -0
- package/src/runtime/agent/orchestrator/tools/patch.mjs +36 -2959
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +1 -23
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +10 -74
- package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
- package/src/runtime/agent/orchestrator/tools/shell-state.mjs +154 -0
- package/src/runtime/channels/backends/discord-access.mjs +32 -0
- package/src/runtime/channels/backends/discord-attachments.mjs +65 -0
- package/src/runtime/channels/backends/discord-gateway.mjs +233 -0
- package/src/runtime/channels/backends/discord.mjs +12 -292
- package/src/runtime/channels/index.mjs +241 -894
- package/src/runtime/channels/lib/backend-dispatch.mjs +44 -0
- package/src/runtime/channels/lib/boot-profile.mjs +23 -0
- package/src/runtime/channels/lib/crash-log.mjs +106 -0
- package/src/runtime/channels/lib/event-pipeline.mjs +18 -1
- package/src/runtime/channels/lib/event-queue.mjs +63 -4
- package/src/runtime/channels/lib/inbound-routing.mjs +111 -0
- package/src/runtime/channels/lib/index-drop-trace.mjs +72 -0
- package/src/runtime/channels/lib/output-forwarder.mjs +9 -1
- package/src/runtime/channels/lib/owner-heartbeat.mjs +75 -0
- package/src/runtime/channels/lib/parent-bridge.mjs +88 -0
- package/src/runtime/channels/lib/runtime-paths.mjs +14 -4
- package/src/runtime/channels/lib/session-discovery.mjs +56 -4
- package/src/runtime/channels/lib/telegram-format.mjs +19 -22
- package/src/runtime/channels/lib/tool-dispatch.mjs +158 -0
- package/src/runtime/channels/lib/tool-format.mjs +1 -1
- package/src/runtime/channels/lib/transcript-discovery.mjs +4 -4
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -3
- package/src/runtime/channels/lib/voice-transcription.mjs +179 -0
- package/src/runtime/channels/lib/webhook/deliveries.mjs +312 -0
- package/src/runtime/channels/lib/webhook/log.mjs +42 -0
- package/src/runtime/channels/lib/webhook/ngrok.mjs +181 -0
- package/src/runtime/channels/lib/webhook/signature.mjs +60 -0
- package/src/runtime/channels/lib/webhook.mjs +36 -570
- package/src/runtime/channels/lib/whisper-language.mjs +42 -0
- package/src/runtime/channels/tool-defs.mjs +11 -130
- package/src/runtime/memory/index.mjs +258 -2050
- package/src/runtime/memory/lib/core-memory-store.mjs +351 -1
- package/src/runtime/memory/lib/cycle-llm-adapters.mjs +58 -0
- package/src/runtime/memory/lib/cycle-scheduler.mjs +497 -0
- package/src/runtime/memory/lib/cycle-signatures.mjs +34 -0
- package/src/runtime/memory/lib/embedding-warmup.mjs +58 -0
- package/src/runtime/memory/lib/http-wire.mjs +57 -0
- package/src/runtime/memory/lib/memory-config-flags.mjs +91 -0
- package/src/runtime/memory/lib/memory-cycle.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +515 -0
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +324 -0
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +18 -0
- package/src/runtime/memory/lib/memory-cycle2.mjs +72 -837
- package/src/runtime/memory/lib/memory-embed.mjs +149 -0
- package/src/runtime/memory/lib/memory-process-lock.mjs +162 -0
- package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +24 -0
- package/src/runtime/memory/lib/memory-recall-store.mjs +22 -2
- package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
- package/src/runtime/memory/lib/memory.mjs +20 -0
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
- package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
- package/src/runtime/memory/lib/query-handlers.mjs +780 -0
- package/src/runtime/memory/lib/recall-format.mjs +238 -0
- package/src/runtime/memory/lib/runtime-fetcher.mjs +8 -3
- package/src/runtime/memory/lib/transcript-ingest.mjs +425 -0
- package/src/runtime/memory/tool-defs.mjs +6 -14
- package/src/runtime/search/lib/http-fetch.mjs +274 -0
- package/src/runtime/search/lib/ssrf-guard.mjs +333 -0
- package/src/runtime/search/lib/web-tools.mjs +24 -602
- package/src/runtime/shared/abort-controller.mjs +1 -1
- package/src/runtime/shared/atomic-file.mjs +26 -1
- package/src/runtime/shared/background-tasks.mjs +2 -3
- package/src/runtime/shared/buffered-appender.mjs +149 -0
- package/src/runtime/shared/launcher-control.mjs +2 -2
- package/src/runtime/shared/task-notification-envelope.mjs +98 -0
- package/src/runtime/shared/tool-execution-contract.mjs +2 -2
- package/src/runtime/shared/tool-primitives.mjs +308 -0
- package/src/runtime/shared/tool-result-summary.mjs +515 -0
- package/src/runtime/shared/tool-surface.mjs +80 -898
- package/src/runtime/shared/transcript-writer.mjs +52 -2
- package/src/runtime/shared/update-checker.mjs +7 -4
- package/src/session-runtime/config-helpers.mjs +291 -0
- package/src/session-runtime/config-lifecycle.mjs +232 -0
- package/src/session-runtime/cwd-plugins.mjs +226 -0
- package/src/session-runtime/effort.mjs +128 -0
- package/src/session-runtime/fs-utils.mjs +10 -0
- package/src/session-runtime/mcp-glue.mjs +177 -0
- package/src/session-runtime/model-capabilities.mjs +130 -0
- package/src/session-runtime/model-recency.mjs +111 -0
- package/src/session-runtime/native-search.mjs +247 -0
- package/src/session-runtime/output-styles.mjs +126 -0
- package/src/session-runtime/plugin-mcp.mjs +114 -0
- package/src/session-runtime/prewarm.mjs +142 -0
- package/src/session-runtime/provider-models.mjs +278 -0
- package/src/session-runtime/provider-usage.mjs +120 -0
- package/src/session-runtime/quick-model-rows.mjs +170 -0
- package/src/session-runtime/quick-search-models.mjs +46 -0
- package/src/session-runtime/session-hooks.mjs +93 -0
- package/src/session-runtime/session-text.mjs +100 -0
- package/src/session-runtime/settings-api.mjs +319 -0
- package/src/session-runtime/statusline-route.mjs +35 -0
- package/src/session-runtime/tool-catalog.mjs +720 -0
- package/src/session-runtime/tool-defs.mjs +84 -0
- package/src/session-runtime/warmup-schedulers.mjs +201 -0
- package/src/session-runtime/workflow.mjs +358 -0
- package/src/standalone/agent-tool/helpers.mjs +237 -0
- package/src/standalone/agent-tool/notify.mjs +107 -0
- package/src/standalone/agent-tool/provider-init.mjs +143 -0
- package/src/standalone/agent-tool/render.mjs +152 -0
- package/src/standalone/agent-tool/tool-def.mjs +55 -0
- package/src/standalone/agent-tool.mjs +155 -677
- package/src/standalone/channel-worker.mjs +7 -9
- package/src/standalone/explore-tool.mjs +40 -12
- package/src/standalone/hook-bus/config.mjs +207 -0
- package/src/standalone/hook-bus/constants.mjs +90 -0
- package/src/standalone/hook-bus/handlers.mjs +481 -0
- package/src/standalone/hook-bus/payload.mjs +31 -0
- package/src/standalone/hook-bus/rules.mjs +77 -0
- package/src/standalone/hook-bus.mjs +110 -746
- package/src/standalone/memory-runtime-proxy.mjs +7 -0
- package/src/standalone/opencode-go-login.mjs +125 -0
- package/src/standalone/provider-admin.mjs +15 -19
- package/src/standalone/usage-dashboard.mjs +3 -1
- package/src/tui/App.jsx +1163 -7571
- package/src/tui/app/app-format.mjs +206 -0
- package/src/tui/app/channel-pickers.mjs +510 -0
- package/src/tui/app/clipboard.mjs +67 -0
- package/src/tui/app/core-memory-picker.mjs +210 -0
- package/src/tui/app/extension-pickers.mjs +506 -0
- package/src/tui/app/input-parsers.mjs +193 -0
- package/src/tui/app/maintenance-pickers.mjs +324 -0
- package/src/tui/app/model-options.mjs +330 -0
- package/src/tui/app/model-picker.mjs +365 -0
- package/src/tui/app/onboarding-steps.mjs +400 -0
- package/src/tui/app/project-picker.mjs +247 -0
- package/src/tui/app/provider-setup-picker.mjs +580 -0
- package/src/tui/app/resume-picker.mjs +55 -0
- package/src/tui/app/route-pickers.mjs +419 -0
- package/src/tui/app/settings-picker.mjs +490 -0
- package/src/tui/app/slash-commands.mjs +101 -0
- package/src/tui/app/slash-dispatch.mjs +427 -0
- package/src/tui/app/text-layout.mjs +46 -0
- package/src/tui/app/theme-effort-pickers.mjs +154 -0
- package/src/tui/app/transcript-window.mjs +671 -0
- package/src/tui/app/use-mouse-input.mjs +460 -0
- package/src/tui/app/use-prompt-handlers.mjs +310 -0
- package/src/tui/app/use-transcript-scroll.mjs +510 -0
- package/src/tui/app/use-transcript-window.mjs +589 -0
- package/src/tui/components/ConfirmBar.jsx +1 -1
- package/src/tui/components/Picker.jsx +32 -4
- package/src/tui/components/PromptInput.jsx +259 -80
- package/src/tui/components/SlashCommandPalette.jsx +8 -1
- package/src/tui/components/StatusLine.jsx +63 -12
- package/src/tui/components/TextEntryPanel.jsx +11 -0
- package/src/tui/components/ToolExecution.jsx +56 -588
- package/src/tui/components/TranscriptItem.jsx +105 -0
- package/src/tui/components/UsagePanel.jsx +18 -4
- package/src/tui/components/prompt-input/edit-helpers.mjs +72 -0
- package/src/tui/components/prompt-input/voice-indicator.mjs +39 -0
- package/src/tui/components/tool-execution/ResultBody.jsx +56 -0
- package/src/tui/components/tool-execution/surface-detail.mjs +405 -0
- package/src/tui/components/tool-execution/text-format.mjs +161 -0
- package/src/tui/components/tool-output-format.mjs +2 -2
- package/src/tui/display-width.mjs +20 -3
- package/src/tui/dist/index.mjs +18034 -17188
- package/src/tui/engine/agent-envelope.mjs +296 -0
- package/src/tui/engine/agent-job-feed.mjs +133 -0
- package/src/tui/engine/boot-profile.mjs +21 -0
- package/src/tui/engine/labels.mjs +67 -0
- package/src/tui/engine/notice-text.mjs +112 -0
- package/src/tui/engine/notification-plan.mjs +76 -0
- package/src/tui/engine/queue-helpers.mjs +161 -0
- package/src/tui/engine/render-timing.mjs +17 -0
- package/src/tui/engine/session-stats.mjs +46 -0
- package/src/tui/engine/tool-approval.mjs +94 -0
- package/src/tui/engine/tool-call-fields.mjs +23 -0
- package/src/tui/engine/tool-card-results.mjs +234 -0
- package/src/tui/engine/tool-result-status.mjs +135 -0
- package/src/tui/engine/tool-result-text.mjs +126 -0
- package/src/tui/engine.mjs +405 -1385
- package/src/tui/figures.mjs +5 -0
- package/src/tui/index.jsx +105 -0
- package/src/tui/input-editing.mjs +60 -10
- package/src/tui/keyboard-protocol.mjs +2 -2
- package/src/tui/lib/voice-recorder.mjs +35 -19
- package/src/tui/markdown/format-token.mjs +11 -9
- package/src/tui/paste-attachments.mjs +38 -0
- package/src/tui/statusline-ansi-bridge.mjs +11 -3
- package/src/tui/theme.mjs +6 -0
- package/src/tui/themes/base.mjs +2 -2
- package/src/tui/themes/kanagawa.mjs +4 -4
- package/src/tui/themes/teal.mjs +4 -5
- package/src/tui/themes/utils.mjs +1 -1
- package/src/ui/statusline-agents.mjs +213 -0
- package/src/ui/statusline-format.mjs +146 -0
- package/src/ui/statusline-segments.mjs +148 -0
- package/src/ui/statusline.mjs +77 -462
- package/src/ui/tool-card.mjs +0 -1
- package/src/vendor/statusline/bin/statusline-route.mjs +15 -2
- package/src/workflows/default/WORKFLOW.md +16 -9
- package/src/workflows/sequential/WORKFLOW.md +16 -11
- package/src/workflows/solo/WORKFLOW.md +5 -1
- package/vendor/ink/build/display-width.js +19 -3
- package/vendor/ink/build/ink.js +103 -6
- package/vendor/ink/build/log-update.js +17 -3
- package/vendor/ink/build/wrap-text.js +125 -0
- package/scripts/_test-folder-dialog.mjs +0 -30
- package/scripts/fix-brief-fn.mjs +0 -35
- package/scripts/fix-format-tool-surface.mjs +0 -24
- package/scripts/fix-tool-exec-visible.mjs +0 -42
- package/scripts/patch-agent-brief.mjs +0 -48
- package/scripts/patch-app.mjs +0 -21
- package/scripts/patch-app2.mjs +0 -18
- package/scripts/patch-dist-brief.mjs +0 -96
- package/scripts/patch-tool-exec.mjs +0 -70
- package/src/examples/schedules/SCHEDULE.example.md +0 -32
- package/src/examples/webhooks/WEBHOOK.example.md +0 -40
- package/src/runtime/agent/orchestrator/session/manager.reactive-persist.test.mjs +0 -107
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +0 -143
- package/src/runtime/agent/orchestrator/tools/builtin/diagnostics-tool.mjs +0 -285
- package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
- package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
- package/src/runtime/shared/tool-execution-contract.test.mjs +0 -183
- package/src/standalone/agent-task-status.test.mjs +0 -76
- package/src/tui/components/tool-output-format.test.mjs +0 -399
- package/src/tui/display-width.test.mjs +0 -35
- package/src/tui/engine-runtime-notification.test.mjs +0 -115
- package/src/tui/engine-tool-result-text.test.mjs +0 -75
- package/src/tui/markdown/format-token.test.mjs +0 -354
- package/src/tui/markdown/render-ansi.test.mjs +0 -108
- package/src/tui/markdown/stream-fence.test.mjs +0 -26
- package/src/tui/markdown/streaming-markdown.test.mjs +0 -70
- package/src/tui/prompt-history-store.test.mjs +0 -52
- package/src/tui/statusline-ansi-bridge.test.mjs +0 -159
- package/src/tui/transcript-tool-failures.test.mjs +0 -111
- package/src/ui/markdown.test.mjs +0 -70
- package/src/ui/statusline-context-label.test.mjs +0 -15
- package/src/vendor/statusline/bin/statusline-lib.mjs +0 -186
- package/src/vendor/statusline/bin/statusline-route.test.mjs +0 -80
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session summary index: the compact `session-summaries.json` sidecar that
|
|
3
|
+
* lists every session's lightweight metadata (preview, counts, lifecycle) so
|
|
4
|
+
* the status aggregator and session listers avoid parsing every full session
|
|
5
|
+
* file. Extracted from store.mjs verbatim; store.mjs re-exports these so
|
|
6
|
+
* importers stay unchanged.
|
|
7
|
+
*/
|
|
8
|
+
import { mkdirSync } from 'fs';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
import { getPluginData } from '../config.mjs';
|
|
11
|
+
import { updateJsonAtomicSync, writeJsonAtomicSync } from '../../../shared/atomic-file.mjs';
|
|
12
|
+
|
|
13
|
+
export const SESSION_SUMMARY_INDEX_VERSION = 1;
|
|
14
|
+
|
|
15
|
+
export function summaryIndexPath() {
|
|
16
|
+
const dir = getPluginData();
|
|
17
|
+
mkdirSync(dir, { recursive: true });
|
|
18
|
+
return join(dir, 'session-summaries.json');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function _messageText(content) {
|
|
22
|
+
if (typeof content === 'string') return content;
|
|
23
|
+
if (Array.isArray(content)) {
|
|
24
|
+
return content.map((part) => {
|
|
25
|
+
if (typeof part === 'string') return part;
|
|
26
|
+
if (part?.type === 'text') return part.text || '';
|
|
27
|
+
if (typeof part?.text === 'string') return part.text;
|
|
28
|
+
try { return JSON.stringify(part); } catch { return ''; }
|
|
29
|
+
}).filter(Boolean).join('\n');
|
|
30
|
+
}
|
|
31
|
+
if (content && typeof content === 'object') {
|
|
32
|
+
if (typeof content.text === 'string') return content.text;
|
|
33
|
+
try { return JSON.stringify(content); } catch { return ''; }
|
|
34
|
+
}
|
|
35
|
+
return '';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function _cleanPreview(text, max = 240) {
|
|
39
|
+
const value = String(text || '').replace(/\s+/g, ' ').trim();
|
|
40
|
+
return value.length > max ? value.slice(0, max).replace(/\s+\S*$/, '').trim() : value;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function _isPreviewNoise(text) {
|
|
44
|
+
const value = String(text || '').trim();
|
|
45
|
+
if (!value) return true;
|
|
46
|
+
if (value.startsWith('<system-reminder>')) return true;
|
|
47
|
+
if (/^Reference files:/i.test(value)) return true;
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function _sessionPreview(session) {
|
|
52
|
+
const messages = Array.isArray(session?.messages) ? session.messages : [];
|
|
53
|
+
let first = '';
|
|
54
|
+
for (const m of messages) {
|
|
55
|
+
if (m?.role !== 'user') continue;
|
|
56
|
+
const text = _cleanPreview(_messageText(m.content));
|
|
57
|
+
if (_isPreviewNoise(text)) continue;
|
|
58
|
+
if (!first) first = text;
|
|
59
|
+
}
|
|
60
|
+
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
61
|
+
const m = messages[i];
|
|
62
|
+
if (m?.role !== 'user') continue;
|
|
63
|
+
const text = _cleanPreview(_messageText(m.content));
|
|
64
|
+
if (_isPreviewNoise(text)) continue;
|
|
65
|
+
return text || first;
|
|
66
|
+
}
|
|
67
|
+
return first;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function _sessionMessageCount(session) {
|
|
71
|
+
const messages = Array.isArray(session?.messages) ? session.messages : [];
|
|
72
|
+
let count = 0;
|
|
73
|
+
for (const m of messages) {
|
|
74
|
+
if (m && (m.role === 'user' || m.role === 'assistant')) count++;
|
|
75
|
+
}
|
|
76
|
+
return count;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function _positiveNumber(value, fallback = 0) {
|
|
80
|
+
const n = Number(value);
|
|
81
|
+
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function _sessionSummary(session) {
|
|
85
|
+
if (!session?.id) return null;
|
|
86
|
+
return {
|
|
87
|
+
id: String(session.id),
|
|
88
|
+
updatedAt: _positiveNumber(session.updatedAt, Date.now()),
|
|
89
|
+
createdAt: _positiveNumber(session.createdAt, 0),
|
|
90
|
+
lastHeartbeatAt: _positiveNumber(session.lastHeartbeatAt, 0),
|
|
91
|
+
closed: session.closed === true,
|
|
92
|
+
status: String(session.status || (session.closed === true ? 'closed' : 'idle')),
|
|
93
|
+
owner: session.owner || 'user',
|
|
94
|
+
agent: session.agent || null,
|
|
95
|
+
sourceType: session.sourceType || null,
|
|
96
|
+
sourceName: session.sourceName || null,
|
|
97
|
+
scopeKey: session.scopeKey || null,
|
|
98
|
+
ownerSessionId: session.ownerSessionId || null,
|
|
99
|
+
clientHostPid: _positiveNumber(session.clientHostPid, 0) || null,
|
|
100
|
+
cwd: session.cwd || '',
|
|
101
|
+
provider: session.provider || null,
|
|
102
|
+
model: session.model || null,
|
|
103
|
+
agentTag: session.agentTag || null,
|
|
104
|
+
task_id: session.task_id || session.taskId || null,
|
|
105
|
+
permission: session.permission || null,
|
|
106
|
+
toolPermission: session.toolPermission || null,
|
|
107
|
+
messageCount: _sessionMessageCount(session),
|
|
108
|
+
preview: _sessionPreview(session),
|
|
109
|
+
generation: typeof session.generation === 'number' ? session.generation : 0,
|
|
110
|
+
implicitBashSessionId: session.implicitBashSessionId || null,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function _normalizeSummaryRow(row) {
|
|
115
|
+
if (!row?.id || typeof row.id !== 'string' || !/^[A-Za-z0-9_-]+$/.test(row.id)) return null;
|
|
116
|
+
return {
|
|
117
|
+
id: row.id,
|
|
118
|
+
updatedAt: _positiveNumber(row.updatedAt, 0),
|
|
119
|
+
createdAt: _positiveNumber(row.createdAt, 0),
|
|
120
|
+
lastHeartbeatAt: _positiveNumber(row.lastHeartbeatAt, 0),
|
|
121
|
+
closed: row.closed === true,
|
|
122
|
+
status: String(row.status || (row.closed === true ? 'closed' : 'idle')),
|
|
123
|
+
owner: row.owner || 'user',
|
|
124
|
+
agent: row.agent || null,
|
|
125
|
+
sourceType: row.sourceType || null,
|
|
126
|
+
sourceName: row.sourceName || null,
|
|
127
|
+
scopeKey: row.scopeKey || null,
|
|
128
|
+
ownerSessionId: row.ownerSessionId || null,
|
|
129
|
+
clientHostPid: _positiveNumber(row.clientHostPid, 0) || null,
|
|
130
|
+
cwd: row.cwd || '',
|
|
131
|
+
provider: row.provider || null,
|
|
132
|
+
model: row.model || null,
|
|
133
|
+
agentTag: row.agentTag || null,
|
|
134
|
+
task_id: row.task_id || null,
|
|
135
|
+
permission: row.permission || null,
|
|
136
|
+
toolPermission: row.toolPermission || null,
|
|
137
|
+
messageCount: Math.max(0, Math.floor(Number(row.messageCount) || 0)),
|
|
138
|
+
preview: _cleanPreview(row.preview || ''),
|
|
139
|
+
generation: typeof row.generation === 'number' ? row.generation : 0,
|
|
140
|
+
implicitBashSessionId: row.implicitBashSessionId || null,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function _normalizeSummaryIndex(raw) {
|
|
145
|
+
const rows = Array.isArray(raw?.rows) ? raw.rows.map(_normalizeSummaryRow).filter(Boolean) : [];
|
|
146
|
+
rows.sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
147
|
+
return { version: SESSION_SUMMARY_INDEX_VERSION, updatedAt: _positiveNumber(raw?.updatedAt, 0), rows };
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function _writeSummaryIndex(rows) {
|
|
151
|
+
const cleanRows = (rows || []).map(_normalizeSummaryRow).filter(Boolean)
|
|
152
|
+
.sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
153
|
+
writeJsonAtomicSync(summaryIndexPath(), {
|
|
154
|
+
version: SESSION_SUMMARY_INDEX_VERSION,
|
|
155
|
+
updatedAt: Date.now(),
|
|
156
|
+
rows: cleanRows,
|
|
157
|
+
}, { compact: true, lock: true });
|
|
158
|
+
return cleanRows;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function _upsertSessionSummary(session) {
|
|
162
|
+
const row = _sessionSummary(session);
|
|
163
|
+
if (!row) return;
|
|
164
|
+
try {
|
|
165
|
+
updateJsonAtomicSync(summaryIndexPath(), (cur) => {
|
|
166
|
+
const index = _normalizeSummaryIndex(cur);
|
|
167
|
+
const rows = index.rows.filter((r) => r.id !== row.id);
|
|
168
|
+
const cleanRow = _normalizeSummaryRow(row);
|
|
169
|
+
if (!cleanRow) return undefined;
|
|
170
|
+
const existing = index.rows.find((r) => r.id === row.id) || null;
|
|
171
|
+
if (existing && JSON.stringify(existing) === JSON.stringify(cleanRow)) return undefined;
|
|
172
|
+
rows.push(cleanRow);
|
|
173
|
+
rows.sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
174
|
+
return { version: SESSION_SUMMARY_INDEX_VERSION, updatedAt: Date.now(), rows };
|
|
175
|
+
}, { compact: true, lock: true });
|
|
176
|
+
} catch { /* summary index is best-effort */ }
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function _removeSessionSummary(id) {
|
|
180
|
+
if (!id) return;
|
|
181
|
+
try {
|
|
182
|
+
updateJsonAtomicSync(summaryIndexPath(), (cur) => {
|
|
183
|
+
const index = _normalizeSummaryIndex(cur);
|
|
184
|
+
const rows = index.rows.filter((r) => r.id !== id);
|
|
185
|
+
if (rows.length === index.rows.length) return undefined;
|
|
186
|
+
return { version: SESSION_SUMMARY_INDEX_VERSION, updatedAt: Date.now(), rows };
|
|
187
|
+
}, { compact: true, lock: true });
|
|
188
|
+
} catch { /* summary index is best-effort */ }
|
|
189
|
+
}
|
|
@@ -10,11 +10,29 @@ import { join } from 'path';
|
|
|
10
10
|
import { Worker } from 'worker_threads';
|
|
11
11
|
import { getPluginData } from '../config.mjs';
|
|
12
12
|
import { isAgentOwner } from '../agent-owner.mjs';
|
|
13
|
-
import { renameWithRetrySync
|
|
13
|
+
import { renameWithRetrySync } from '../../../shared/atomic-file.mjs';
|
|
14
14
|
import { sanitizeContentForStoredHistory } from '../providers/media-normalization.mjs';
|
|
15
|
+
import {
|
|
16
|
+
summaryIndexPath,
|
|
17
|
+
_sessionSummary,
|
|
18
|
+
_normalizeSummaryIndex,
|
|
19
|
+
_writeSummaryIndex,
|
|
20
|
+
_upsertSessionSummary,
|
|
21
|
+
_removeSessionSummary,
|
|
22
|
+
} from './store-summary-index.mjs';
|
|
23
|
+
// Facade re-export: summary-index API moved to store-summary-index.mjs; keep
|
|
24
|
+
// prior importers of store.mjs unchanged.
|
|
25
|
+
export {
|
|
26
|
+
SESSION_SUMMARY_INDEX_VERSION,
|
|
27
|
+
summaryIndexPath,
|
|
28
|
+
_sessionSummary,
|
|
29
|
+
_normalizeSummaryIndex,
|
|
30
|
+
_writeSummaryIndex,
|
|
31
|
+
_upsertSessionSummary,
|
|
32
|
+
_removeSessionSummary,
|
|
33
|
+
} from './store-summary-index.mjs';
|
|
15
34
|
|
|
16
35
|
const _lastSaveError = new Map(); // id -> { message, at }
|
|
17
|
-
const SESSION_SUMMARY_INDEX_VERSION = 1;
|
|
18
36
|
|
|
19
37
|
// The live in-memory session (and every model request)
|
|
20
38
|
// retains attached image bytes across turns so multi-turn recognition works.
|
|
@@ -71,11 +89,6 @@ function sessionPath(id) {
|
|
|
71
89
|
}
|
|
72
90
|
return join(getStoreDir(), `${id}.json`);
|
|
73
91
|
}
|
|
74
|
-
function summaryIndexPath() {
|
|
75
|
-
const dir = getPluginData();
|
|
76
|
-
mkdirSync(dir, { recursive: true });
|
|
77
|
-
return join(dir, 'session-summaries.json');
|
|
78
|
-
}
|
|
79
92
|
/**
|
|
80
93
|
* Ensure generation/closed defaults on every session object.
|
|
81
94
|
* Older persisted sessions predate these fields; we normalise at load and save.
|
|
@@ -88,176 +101,6 @@ function _ensureLifecycleFields(session) {
|
|
|
88
101
|
return session;
|
|
89
102
|
}
|
|
90
103
|
|
|
91
|
-
function _messageText(content) {
|
|
92
|
-
if (typeof content === 'string') return content;
|
|
93
|
-
if (Array.isArray(content)) {
|
|
94
|
-
return content.map((part) => {
|
|
95
|
-
if (typeof part === 'string') return part;
|
|
96
|
-
if (part?.type === 'text') return part.text || '';
|
|
97
|
-
if (typeof part?.text === 'string') return part.text;
|
|
98
|
-
try { return JSON.stringify(part); } catch { return ''; }
|
|
99
|
-
}).filter(Boolean).join('\n');
|
|
100
|
-
}
|
|
101
|
-
if (content && typeof content === 'object') {
|
|
102
|
-
if (typeof content.text === 'string') return content.text;
|
|
103
|
-
try { return JSON.stringify(content); } catch { return ''; }
|
|
104
|
-
}
|
|
105
|
-
return '';
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function _cleanPreview(text, max = 240) {
|
|
109
|
-
const value = String(text || '').replace(/\s+/g, ' ').trim();
|
|
110
|
-
return value.length > max ? value.slice(0, max).replace(/\s+\S*$/, '').trim() : value;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function _isPreviewNoise(text) {
|
|
114
|
-
const value = String(text || '').trim();
|
|
115
|
-
if (!value) return true;
|
|
116
|
-
if (value.startsWith('<system-reminder>')) return true;
|
|
117
|
-
if (/^Reference files:/i.test(value)) return true;
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function _sessionPreview(session) {
|
|
122
|
-
const messages = Array.isArray(session?.messages) ? session.messages : [];
|
|
123
|
-
let first = '';
|
|
124
|
-
for (const m of messages) {
|
|
125
|
-
if (m?.role !== 'user') continue;
|
|
126
|
-
const text = _cleanPreview(_messageText(m.content));
|
|
127
|
-
if (_isPreviewNoise(text)) continue;
|
|
128
|
-
if (!first) first = text;
|
|
129
|
-
}
|
|
130
|
-
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
131
|
-
const m = messages[i];
|
|
132
|
-
if (m?.role !== 'user') continue;
|
|
133
|
-
const text = _cleanPreview(_messageText(m.content));
|
|
134
|
-
if (_isPreviewNoise(text)) continue;
|
|
135
|
-
return text || first;
|
|
136
|
-
}
|
|
137
|
-
return first;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function _sessionMessageCount(session) {
|
|
141
|
-
const messages = Array.isArray(session?.messages) ? session.messages : [];
|
|
142
|
-
let count = 0;
|
|
143
|
-
for (const m of messages) {
|
|
144
|
-
if (m && (m.role === 'user' || m.role === 'assistant')) count++;
|
|
145
|
-
}
|
|
146
|
-
return count;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function _positiveNumber(value, fallback = 0) {
|
|
150
|
-
const n = Number(value);
|
|
151
|
-
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function _sessionSummary(session) {
|
|
155
|
-
if (!session?.id) return null;
|
|
156
|
-
return {
|
|
157
|
-
id: String(session.id),
|
|
158
|
-
updatedAt: _positiveNumber(session.updatedAt, Date.now()),
|
|
159
|
-
createdAt: _positiveNumber(session.createdAt, 0),
|
|
160
|
-
lastHeartbeatAt: _positiveNumber(session.lastHeartbeatAt, 0),
|
|
161
|
-
closed: session.closed === true,
|
|
162
|
-
status: String(session.status || (session.closed === true ? 'closed' : 'idle')),
|
|
163
|
-
owner: session.owner || 'user',
|
|
164
|
-
agent: session.agent || null,
|
|
165
|
-
sourceType: session.sourceType || null,
|
|
166
|
-
sourceName: session.sourceName || null,
|
|
167
|
-
scopeKey: session.scopeKey || null,
|
|
168
|
-
ownerSessionId: session.ownerSessionId || null,
|
|
169
|
-
clientHostPid: _positiveNumber(session.clientHostPid, 0) || null,
|
|
170
|
-
cwd: session.cwd || '',
|
|
171
|
-
provider: session.provider || null,
|
|
172
|
-
model: session.model || null,
|
|
173
|
-
agentTag: session.agentTag || null,
|
|
174
|
-
task_id: session.task_id || session.taskId || null,
|
|
175
|
-
permission: session.permission || null,
|
|
176
|
-
toolPermission: session.toolPermission || null,
|
|
177
|
-
messageCount: _sessionMessageCount(session),
|
|
178
|
-
preview: _sessionPreview(session),
|
|
179
|
-
generation: typeof session.generation === 'number' ? session.generation : 0,
|
|
180
|
-
implicitBashSessionId: session.implicitBashSessionId || null,
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function _normalizeSummaryRow(row) {
|
|
185
|
-
if (!row?.id || typeof row.id !== 'string' || !/^[A-Za-z0-9_-]+$/.test(row.id)) return null;
|
|
186
|
-
return {
|
|
187
|
-
id: row.id,
|
|
188
|
-
updatedAt: _positiveNumber(row.updatedAt, 0),
|
|
189
|
-
createdAt: _positiveNumber(row.createdAt, 0),
|
|
190
|
-
lastHeartbeatAt: _positiveNumber(row.lastHeartbeatAt, 0),
|
|
191
|
-
closed: row.closed === true,
|
|
192
|
-
status: String(row.status || (row.closed === true ? 'closed' : 'idle')),
|
|
193
|
-
owner: row.owner || 'user',
|
|
194
|
-
agent: row.agent || null,
|
|
195
|
-
sourceType: row.sourceType || null,
|
|
196
|
-
sourceName: row.sourceName || null,
|
|
197
|
-
scopeKey: row.scopeKey || null,
|
|
198
|
-
ownerSessionId: row.ownerSessionId || null,
|
|
199
|
-
clientHostPid: _positiveNumber(row.clientHostPid, 0) || null,
|
|
200
|
-
cwd: row.cwd || '',
|
|
201
|
-
provider: row.provider || null,
|
|
202
|
-
model: row.model || null,
|
|
203
|
-
agentTag: row.agentTag || null,
|
|
204
|
-
task_id: row.task_id || null,
|
|
205
|
-
permission: row.permission || null,
|
|
206
|
-
toolPermission: row.toolPermission || null,
|
|
207
|
-
messageCount: Math.max(0, Math.floor(Number(row.messageCount) || 0)),
|
|
208
|
-
preview: _cleanPreview(row.preview || ''),
|
|
209
|
-
generation: typeof row.generation === 'number' ? row.generation : 0,
|
|
210
|
-
implicitBashSessionId: row.implicitBashSessionId || null,
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
function _normalizeSummaryIndex(raw) {
|
|
215
|
-
const rows = Array.isArray(raw?.rows) ? raw.rows.map(_normalizeSummaryRow).filter(Boolean) : [];
|
|
216
|
-
rows.sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
217
|
-
return { version: SESSION_SUMMARY_INDEX_VERSION, updatedAt: _positiveNumber(raw?.updatedAt, 0), rows };
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function _writeSummaryIndex(rows) {
|
|
221
|
-
const cleanRows = (rows || []).map(_normalizeSummaryRow).filter(Boolean)
|
|
222
|
-
.sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
223
|
-
writeJsonAtomicSync(summaryIndexPath(), {
|
|
224
|
-
version: SESSION_SUMMARY_INDEX_VERSION,
|
|
225
|
-
updatedAt: Date.now(),
|
|
226
|
-
rows: cleanRows,
|
|
227
|
-
}, { compact: true, lock: true });
|
|
228
|
-
return cleanRows;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function _upsertSessionSummary(session) {
|
|
232
|
-
const row = _sessionSummary(session);
|
|
233
|
-
if (!row) return;
|
|
234
|
-
try {
|
|
235
|
-
updateJsonAtomicSync(summaryIndexPath(), (cur) => {
|
|
236
|
-
const index = _normalizeSummaryIndex(cur);
|
|
237
|
-
const rows = index.rows.filter((r) => r.id !== row.id);
|
|
238
|
-
const cleanRow = _normalizeSummaryRow(row);
|
|
239
|
-
if (!cleanRow) return undefined;
|
|
240
|
-
const existing = index.rows.find((r) => r.id === row.id) || null;
|
|
241
|
-
if (existing && JSON.stringify(existing) === JSON.stringify(cleanRow)) return undefined;
|
|
242
|
-
rows.push(cleanRow);
|
|
243
|
-
rows.sort((a, b) => (b.updatedAt || 0) - (a.updatedAt || 0));
|
|
244
|
-
return { version: SESSION_SUMMARY_INDEX_VERSION, updatedAt: Date.now(), rows };
|
|
245
|
-
}, { compact: true, lock: true });
|
|
246
|
-
} catch { /* summary index is best-effort */ }
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function _removeSessionSummary(id) {
|
|
250
|
-
if (!id) return;
|
|
251
|
-
try {
|
|
252
|
-
updateJsonAtomicSync(summaryIndexPath(), (cur) => {
|
|
253
|
-
const index = _normalizeSummaryIndex(cur);
|
|
254
|
-
const rows = index.rows.filter((r) => r.id !== id);
|
|
255
|
-
if (rows.length === index.rows.length) return undefined;
|
|
256
|
-
return { version: SESSION_SUMMARY_INDEX_VERSION, updatedAt: Date.now(), rows };
|
|
257
|
-
}, { compact: true, lock: true });
|
|
258
|
-
} catch { /* summary index is best-effort */ }
|
|
259
|
-
}
|
|
260
|
-
|
|
261
104
|
/** Module-level map tracking in-flight saves per session ID to prevent concurrent write corruption. */
|
|
262
105
|
const _savePending = new Map();
|
|
263
106
|
|
|
@@ -520,17 +363,34 @@ function _shouldDrop(id, opts) {
|
|
|
520
363
|
// ran on every save). Fast-path a regex scan; only fall back to a full
|
|
521
364
|
// parse if the lightweight scan is inconclusive.
|
|
522
365
|
const closedMatch = /"closed"\s*:\s*(true|false)/.exec(raw);
|
|
523
|
-
if (closedMatch && closedMatch[1] === 'false') return false;
|
|
524
366
|
if (closedMatch && closedMatch[1] === 'true') {
|
|
525
367
|
const genMatch = /"generation"\s*:\s*(-?\d+)/.exec(raw);
|
|
526
368
|
const diskGen = genMatch ? Number(genMatch[1]) : 0;
|
|
527
369
|
return diskGen >= expected;
|
|
528
370
|
}
|
|
371
|
+
if (closedMatch && closedMatch[1] === 'false') {
|
|
372
|
+
// Not closed — still check the generation-as-ownership-counter
|
|
373
|
+
// rule (see full-parse branch below) via the same fast regex
|
|
374
|
+
// scan, so bumpSessionGeneration()'s detach bump is honoured
|
|
375
|
+
// without paying for a full JSON.parse on every save.
|
|
376
|
+
const genMatch = /"generation"\s*:\s*(-?\d+)/.exec(raw);
|
|
377
|
+
const diskGen = genMatch ? Number(genMatch[1]) : 0;
|
|
378
|
+
return diskGen > expected;
|
|
379
|
+
}
|
|
529
380
|
// No `closed` key found by scan — not a tombstone, so nothing to drop.
|
|
530
381
|
if (!closedMatch && !/"closed"/.test(raw)) return false;
|
|
531
382
|
const onDisk = JSON.parse(raw);
|
|
532
383
|
const diskGen = typeof onDisk.generation === 'number' ? onDisk.generation : 0;
|
|
533
|
-
|
|
384
|
+
if (onDisk.closed === true) return diskGen >= expected;
|
|
385
|
+
// Not closed, but `generation` also doubles as an ownership counter:
|
|
386
|
+
// normal in-place saves (updateSession/appendMessage/etc.) never bump
|
|
387
|
+
// it, only closeSession()-family calls do (markSessionClosed and its
|
|
388
|
+
// non-tombstoning sibling bumpSessionGeneration). So if disk
|
|
389
|
+
// generation is strictly greater than what this write expected,
|
|
390
|
+
// ownership moved on (session was detached-closed and possibly
|
|
391
|
+
// resumed) after our turn started — drop the stale write rather than
|
|
392
|
+
// let it clobber whatever happened after the handoff.
|
|
393
|
+
return diskGen > expected;
|
|
534
394
|
} catch {
|
|
535
395
|
return false;
|
|
536
396
|
}
|
|
@@ -666,6 +526,41 @@ export function markSessionClosed(id, reason = 'manual') {
|
|
|
666
526
|
return newGen;
|
|
667
527
|
}
|
|
668
528
|
|
|
529
|
+
/**
|
|
530
|
+
* Bump a session's generation WITHOUT planting a closed:true tombstone.
|
|
531
|
+
* Used by closeSession(id, reason, { tombstone: false }) — the runtime side
|
|
532
|
+
* (heartbeat, bash shells, controller, in-memory entry) is detached, but the
|
|
533
|
+
* session file itself stays valid/resumable. The generation bump alone is
|
|
534
|
+
* what protects it from a late save race: any saveSession() still in flight
|
|
535
|
+
* from the detached turn was issued with the OLD generation as its
|
|
536
|
+
* `expectedGeneration`, so once we bump the on-disk generation here, that
|
|
537
|
+
* late write's own _shouldDrop() check (generation-as-ownership-counter
|
|
538
|
+
* rule, see below) sees disk generation > expected and drops itself instead
|
|
539
|
+
* of clobbering whatever the resumed session writes next.
|
|
540
|
+
* Returns the new generation, or null if the session file doesn't exist.
|
|
541
|
+
*/
|
|
542
|
+
export function bumpSessionGeneration(id, reason = 'detach') {
|
|
543
|
+
_clearDebounce(id);
|
|
544
|
+
const existing = loadSession(id);
|
|
545
|
+
if (!existing) return null;
|
|
546
|
+
const newGen = (typeof existing.generation === 'number' ? existing.generation : 0) + 1;
|
|
547
|
+
const detached = { ...existing, generation: newGen, updatedAt: Date.now(), detachedReason: reason };
|
|
548
|
+
const target = sessionPath(id);
|
|
549
|
+
const tmp = target + '.' + randomBytes(6).toString('hex') + '.tmp';
|
|
550
|
+
try {
|
|
551
|
+
writeFileSync(tmp, JSON.stringify(_sessionForDisk(detached)), 'utf-8');
|
|
552
|
+
_renameWithRetrySync(tmp, target);
|
|
553
|
+
} catch {
|
|
554
|
+
try { unlinkSync(tmp); } catch { /* ignore */ }
|
|
555
|
+
return null;
|
|
556
|
+
}
|
|
557
|
+
_savePending.delete(id);
|
|
558
|
+
_clearLiveSession(id);
|
|
559
|
+
_deleteHeartbeat(id);
|
|
560
|
+
_upsertSessionSummary(detached);
|
|
561
|
+
return newGen;
|
|
562
|
+
}
|
|
563
|
+
|
|
669
564
|
export function loadSession(id) {
|
|
670
565
|
const path = sessionPath(id);
|
|
671
566
|
// Read-your-writes: if a save is pending (debouncing, scheduled, or queued
|
|
@@ -181,9 +181,9 @@ export const PROVIDER_WS_ACQUIRE_TIMEOUT_MS = resolveTimeoutMs(
|
|
|
181
181
|
{ minMs: 5_000, maxMs: PROVIDER_MAX_BEFORE_WARN_MS },
|
|
182
182
|
);
|
|
183
183
|
|
|
184
|
-
// Single inter-chunk idle timer, matching
|
|
185
|
-
//
|
|
186
|
-
//
|
|
184
|
+
// Single inter-chunk idle timer (300s), matching the upstream WS provider's
|
|
185
|
+
// default stream idle timeout. Mixdog resets one idle timer on every received
|
|
186
|
+
// WS frame (openai-oauth-ws messageHandler resets on every parsed event). There
|
|
187
187
|
// is deliberately no separate "first-meaningful" watchdog — a live stream
|
|
188
188
|
// (including server-side reasoning ACKed via response.created) keeps this timer
|
|
189
189
|
// fresh, and only true socket silence trips it. Env-tunable.
|
|
@@ -85,7 +85,7 @@ function isStringOrStringArray(v) {
|
|
|
85
85
|
return true;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
function hasUnsupportedRipgrepRegex(value) {
|
|
88
|
+
export function hasUnsupportedRipgrepRegex(value) {
|
|
89
89
|
const values = Array.isArray(value) ? value : [value];
|
|
90
90
|
return values.some((item) => {
|
|
91
91
|
if (typeof item !== 'string') return false;
|
|
@@ -99,6 +99,30 @@ function hasMultipleAbsoluteWindowsPaths(value) {
|
|
|
99
99
|
return Array.isArray(matches) && matches.length > 1;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
// Rescue for hasMultipleAbsoluteWindowsPaths: a single string arg that packs
|
|
103
|
+
// multiple absolute Windows paths together (space/comma/semicolon separated,
|
|
104
|
+
// e.g. "C:\a\b C:\c\d" or "C:\a\b, C:\c\d") is a common model mistake meaning
|
|
105
|
+
// two separate search roots, not one malformed path. Split on each drive-
|
|
106
|
+
// letter boundary and trim separator debris instead of hard-erroring;
|
|
107
|
+
// executeGrepTool's own array fan-out (search-tool.mjs) already knows how to
|
|
108
|
+
// run one grep per path and join the results. Returns null when the value
|
|
109
|
+
// does not cleanly resolve to 2+ non-empty segments.
|
|
110
|
+
function splitMultipleAbsoluteWindowsPaths(value) {
|
|
111
|
+
if (typeof value !== 'string') return null;
|
|
112
|
+
const starts = [];
|
|
113
|
+
const re = /[A-Za-z]:[\\/]/g;
|
|
114
|
+
let m;
|
|
115
|
+
while ((m = re.exec(value)) !== null) starts.push(m.index);
|
|
116
|
+
if (starts.length < 2) return null;
|
|
117
|
+
const parts = [];
|
|
118
|
+
for (let i = 0; i < starts.length; i++) {
|
|
119
|
+
const end = i + 1 < starts.length ? starts[i + 1] : value.length;
|
|
120
|
+
const part = value.slice(starts[i], end).trim().replace(/[,;]+$/, '').trim();
|
|
121
|
+
if (part) parts.push(part);
|
|
122
|
+
}
|
|
123
|
+
return parts.length > 1 ? parts : null;
|
|
124
|
+
}
|
|
125
|
+
|
|
102
126
|
function hasWindowsDrivePath(value) {
|
|
103
127
|
return typeof value === 'string' && /[A-Za-z]:[\\/]/.test(value);
|
|
104
128
|
}
|
|
@@ -124,9 +148,15 @@ function coerceIntegerString(v) {
|
|
|
124
148
|
return n;
|
|
125
149
|
}
|
|
126
150
|
|
|
151
|
+
// Range-shaped strings ("1,40", "1-40", "1:40") are a common model mistake
|
|
152
|
+
// when trying to pass a line range through a single-integer field. They are
|
|
153
|
+
// not coerced (ambiguous which bound is intended); instead the rejection
|
|
154
|
+
// tells the model exactly what to do instead.
|
|
155
|
+
const RANGE_SHAPED_INT_STRING = /^\d+\s*[,\-:]\s*\d+$/;
|
|
156
|
+
|
|
127
157
|
// Mutates a[field] in place when it is a lossless integer string, then
|
|
128
158
|
// validates the (possibly coerced) value against [min, max].
|
|
129
|
-
function checkIntInRange(a, field, min, max) {
|
|
159
|
+
function checkIntInRange(a, field, min, max, opts = {}) {
|
|
130
160
|
let value = a[field];
|
|
131
161
|
if (value === undefined || value === null) return null;
|
|
132
162
|
if (typeof value === 'string') {
|
|
@@ -136,8 +166,20 @@ function checkIntInRange(a, field, min, max) {
|
|
|
136
166
|
a[field] = coerced;
|
|
137
167
|
}
|
|
138
168
|
}
|
|
169
|
+
if (typeof value === 'string' && RANGE_SHAPED_INT_STRING.test(value.trim())) {
|
|
170
|
+
return `Error: builtin arg "${field}" takes a single integer; for a line range use offset+limit as separate integers or "path#L<start>-L<end>" in the path arg (got "${value}")`;
|
|
171
|
+
}
|
|
139
172
|
if (!isFiniteInt(value)) {
|
|
140
|
-
return `Error: builtin arg "${field}" must be a finite integer (got ${describeType(value)})`;
|
|
173
|
+
return `Error: builtin arg "${field}" must be a finite integer (got ${describeType(value)}); expected a single integer like 40`;
|
|
174
|
+
}
|
|
175
|
+
// Soft-cap fields (grep -A/-B/-C/context): silently clamp out-of-range
|
|
176
|
+
// instead of erroring — a model-guessed "300" for a 200-line cap is not
|
|
177
|
+
// a shape violation worth a retry turn, just clamp to the boundary.
|
|
178
|
+
if (opts.clamp) {
|
|
179
|
+
if (value < min) { a[field] = min; return null; }
|
|
180
|
+
if (value > max) { a[field] = max; return null; }
|
|
181
|
+
a[field] = value;
|
|
182
|
+
return null;
|
|
141
183
|
}
|
|
142
184
|
if (value < min) {
|
|
143
185
|
return `Error: builtin arg "${field}" must be >= ${min} (got ${value})`;
|
|
@@ -216,10 +258,11 @@ function guardGrep(a) {
|
|
|
216
258
|
if (hasOwn(a, k) && !isStringOrStringArray(a[k])) {
|
|
217
259
|
return `Error: grep arg "${k}" must be string or string[] (got ${describeType(a[k])})`;
|
|
218
260
|
}
|
|
219
|
-
if (hasOwn(a, k) && hasUnsupportedRipgrepRegex(a[k])) {
|
|
220
|
-
return `Error: grep arg "${k}" uses regex syntax ripgrep does not support here (lookaround/backrefs). Use plain pattern arrays or simpler regex.`;
|
|
221
|
-
}
|
|
222
261
|
}
|
|
262
|
+
// Lookaround/backreference patterns are no longer hard-rejected here:
|
|
263
|
+
// executeGrepTool (search-tool.mjs) detects them at runtime and routes to
|
|
264
|
+
// rg's PCRE2 engine (-P/--pcre2) when the installed rg build supports it,
|
|
265
|
+
// falling back to this same error text only when PCRE2 is unavailable.
|
|
223
266
|
for (const k of globKeys) {
|
|
224
267
|
if (hasOwn(a, k) && !isStringOrStringArray(a[k])) {
|
|
225
268
|
return `Error: grep arg "${k}" must be string or string[] (got ${describeType(a[k])})`;
|
|
@@ -231,7 +274,11 @@ function guardGrep(a) {
|
|
|
231
274
|
return `Error: grep arg "${k}" must be string or string[] (got ${describeType(a[k])})`;
|
|
232
275
|
}
|
|
233
276
|
if (hasOwn(a, k) && hasMultipleAbsoluteWindowsPaths(a[k])) {
|
|
234
|
-
|
|
277
|
+
const split = splitMultipleAbsoluteWindowsPaths(a[k]);
|
|
278
|
+
if (!split) {
|
|
279
|
+
return `Error: grep arg "${k}" contains multiple absolute paths in one string. Use one common parent path plus glob, or separate grep calls.`;
|
|
280
|
+
}
|
|
281
|
+
a[k] = split;
|
|
235
282
|
}
|
|
236
283
|
}
|
|
237
284
|
for (const k of ['head_limit', 'offset']) {
|
|
@@ -239,7 +286,7 @@ function guardGrep(a) {
|
|
|
239
286
|
if (err) return err;
|
|
240
287
|
}
|
|
241
288
|
for (const k of ['-A', '-B', '-C', 'context']) {
|
|
242
|
-
const err = checkIntInRange(a, k, 0, GREP_CONTEXT_MAX);
|
|
289
|
+
const err = checkIntInRange(a, k, 0, GREP_CONTEXT_MAX, { clamp: true });
|
|
243
290
|
if (err) return err;
|
|
244
291
|
}
|
|
245
292
|
// output_mode / mode enum
|
|
@@ -291,13 +338,19 @@ function applyLineContextWindow(obj, labelPrefix) {
|
|
|
291
338
|
if (!hasLine) {
|
|
292
339
|
return `Error: read arg "${labelPrefix}context" requires "${labelPrefix}line" to compute a window`;
|
|
293
340
|
}
|
|
341
|
+
if (typeof obj.line === 'string' && RANGE_SHAPED_INT_STRING.test(obj.line.trim())) {
|
|
342
|
+
return `Error: read arg "${labelPrefix}line" takes a single integer; for a line range use offset+limit as separate integers or "path#L<start>-L<end>" in the path arg (got "${obj.line}")`;
|
|
343
|
+
}
|
|
294
344
|
if (!isFiniteInt(obj.line) || obj.line < 1) {
|
|
295
|
-
return `Error: read arg "${labelPrefix}line" must be a finite integer >= 1 (got ${describeType(obj.line)})`;
|
|
345
|
+
return `Error: read arg "${labelPrefix}line" must be a finite integer >= 1 (got ${describeType(obj.line)}); expected a single integer like 40`;
|
|
296
346
|
}
|
|
297
347
|
let ctx = 0;
|
|
298
348
|
if (hasContext) {
|
|
349
|
+
if (typeof obj.context === 'string' && RANGE_SHAPED_INT_STRING.test(obj.context.trim())) {
|
|
350
|
+
return `Error: read arg "${labelPrefix}context" takes a single integer; for a line range use offset+limit as separate integers or "path#L<start>-L<end>" in the path arg (got "${obj.context}")`;
|
|
351
|
+
}
|
|
299
352
|
if (!isFiniteInt(obj.context) || obj.context < 0) {
|
|
300
|
-
return `Error: read arg "${labelPrefix}context" must be a non-negative finite integer (got ${describeType(obj.context)})`;
|
|
353
|
+
return `Error: read arg "${labelPrefix}context" must be a non-negative finite integer (got ${describeType(obj.context)}); expected a single integer like 40`;
|
|
301
354
|
}
|
|
302
355
|
ctx = obj.context;
|
|
303
356
|
}
|
|
@@ -381,13 +434,6 @@ function guardRead(a) {
|
|
|
381
434
|
return null;
|
|
382
435
|
}
|
|
383
436
|
|
|
384
|
-
function guardDiagnostics(a) {
|
|
385
|
-
if (hasOwn(a, 'path') && a.path !== undefined && a.path !== null && !isNonEmptyString(a.path)) {
|
|
386
|
-
return `Error: diagnostics arg "path" must be a non-empty string (got ${describeType(a.path)})`;
|
|
387
|
-
}
|
|
388
|
-
return null;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
437
|
function guardShell(a) {
|
|
392
438
|
if (!hasOwn(a, 'command')) {
|
|
393
439
|
return 'Error: shell requires "command"';
|
|
@@ -458,8 +504,13 @@ function guardList(a) {
|
|
|
458
504
|
}
|
|
459
505
|
|
|
460
506
|
function guardFind(a) {
|
|
461
|
-
|
|
462
|
-
|
|
507
|
+
// query accepts string or string[] (query[] fans out per-lookup downstream).
|
|
508
|
+
const queryOk = hasOwn(a, 'query') && (
|
|
509
|
+
(typeof a.query === 'string' && a.query.trim().length > 0)
|
|
510
|
+
|| (Array.isArray(a.query) && a.query.length > 0 && a.query.every((q) => typeof q === 'string' && q.trim().length > 0))
|
|
511
|
+
);
|
|
512
|
+
if (!queryOk) {
|
|
513
|
+
return `Error: find requires non-empty string (or string[]) "query" (got ${describeType(a.query)})`;
|
|
463
514
|
}
|
|
464
515
|
if (hasOwn(a, 'path') && !isString(a.path)) {
|
|
465
516
|
return `Error: find arg "path" must be a string (got ${describeType(a.path)})`;
|
|
@@ -541,7 +592,6 @@ function guardCodeGraph(a) {
|
|
|
541
592
|
const GUARDS = {
|
|
542
593
|
grep: guardGrep,
|
|
543
594
|
read: guardRead,
|
|
544
|
-
diagnostics: guardDiagnostics,
|
|
545
595
|
shell: guardShell,
|
|
546
596
|
task: guardTask,
|
|
547
597
|
list: guardList,
|