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
|
@@ -29,6 +29,7 @@ export function buildGrepCacheKey(parts) {
|
|
|
29
29
|
multilineMode,
|
|
30
30
|
fileType,
|
|
31
31
|
onlyMatching,
|
|
32
|
+
pcre2,
|
|
32
33
|
} = parts;
|
|
33
34
|
return [
|
|
34
35
|
'grep',
|
|
@@ -46,6 +47,7 @@ export function buildGrepCacheKey(parts) {
|
|
|
46
47
|
multilineMode ? 'm1' : 'm0',
|
|
47
48
|
onlyMatching ? 'o1' : 'o0',
|
|
48
49
|
Array.isArray(fileType) ? fileType.join('\x01') : (fileType || ''),
|
|
50
|
+
pcre2 ? 'p1' : 'p0',
|
|
49
51
|
].join('|');
|
|
50
52
|
}
|
|
51
53
|
|
|
@@ -64,6 +66,7 @@ export function buildGrepRgArgs(parts) {
|
|
|
64
66
|
fileType,
|
|
65
67
|
onlyMatching,
|
|
66
68
|
fixedStrings = false,
|
|
69
|
+
pcre2 = false,
|
|
67
70
|
} = parts;
|
|
68
71
|
// `--hidden`: search dotfiles/dot-dirs (.github, .mixdog) that
|
|
69
72
|
// rg skips by default. The DEFAULT_IGNORE_GLOBS below still exclude .git and
|
|
@@ -84,6 +87,11 @@ export function buildGrepRgArgs(parts) {
|
|
|
84
87
|
}
|
|
85
88
|
if (caseInsensitive) rgArgs.push('-i');
|
|
86
89
|
if (fixedStrings) rgArgs.push('-F');
|
|
90
|
+
// PCRE2 engine: opt-in only when the caller already confirmed (via
|
|
91
|
+
// rgSupportsPcre2() in search-tool.mjs) that the installed rg binary was
|
|
92
|
+
// built with PCRE2 support. Enables lookaround/backreference patterns
|
|
93
|
+
// that the default Rust regex engine rejects outright.
|
|
94
|
+
if (pcre2) rgArgs.push('-P');
|
|
87
95
|
if (multilineMode) rgArgs.push('-U', '--multiline-dotall');
|
|
88
96
|
if (Array.isArray(fileType)) {
|
|
89
97
|
for (const t of fileType) if (t) rgArgs.push('--type', t);
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search path diagnostics: ENOENT recovery + path-shape helpers shared by
|
|
3
|
+
* grep/glob in search-tool.mjs. Extracted verbatim so search-tool.mjs stays a
|
|
4
|
+
* thin executor; search-tool.mjs re-exports these for unchanged importers.
|
|
5
|
+
*/
|
|
6
|
+
import { statSync } from 'fs';
|
|
7
|
+
import { isAbsolute, resolve } from 'path';
|
|
8
|
+
import { findBySuffixStrip, findFileByBasename } from './path-diagnostics.mjs';
|
|
9
|
+
import { normalizeOutputPath, resolveAgainstCwd } from './path-utils.mjs';
|
|
10
|
+
|
|
11
|
+
// Deterministic ENOENT recovery: when a grep path does not exist, surface
|
|
12
|
+
// indexed files that share the missing path's basename, turning a guessed or
|
|
13
|
+
// misplaced path (e.g. session/result-compression.mjs vs the real
|
|
14
|
+
// tools/result-compression.mjs) into the actual file in one step. Exact
|
|
15
|
+
// basename only — no stem/token fuzzing — so the hint is high-signal and
|
|
16
|
+
// noise-free. Invariant: every ENOENT runs the same basename lookup; there is
|
|
17
|
+
// no "guessed a lot" branch. Returns '' (appends nothing) when no same-named
|
|
18
|
+
// indexed file exists or the glob child is unavailable.
|
|
19
|
+
export async function _suggestIndexedPaths(missingPath, executeChildBuiltinTool, workDir) {
|
|
20
|
+
if (typeof executeChildBuiltinTool !== 'function') return '';
|
|
21
|
+
const base = String(missingPath).replace(/\\/g, '/').split('/').pop();
|
|
22
|
+
// Skip when there is no usable basename or it carries glob magic (the
|
|
23
|
+
// pattern, not a literal filename, would not map to a real file).
|
|
24
|
+
if (!base || /[*?[\]{}]/.test(base)) return '';
|
|
25
|
+
try {
|
|
26
|
+
const out = await executeChildBuiltinTool('glob', { pattern: `**/${base}`, head_limit: 6 }, workDir);
|
|
27
|
+
if (typeof out !== 'string') return '';
|
|
28
|
+
// Drop ONLY the exact diagnostic forms glob emits: the empty-result line
|
|
29
|
+
// ("(no files found ...", "(no entries after offset ...") and the
|
|
30
|
+
// suffix/warning lines ("... [N more entries]", "... [warning] ...").
|
|
31
|
+
// A broad startsWith('(' / '...' / 'Error') would wrongly drop real
|
|
32
|
+
// paths like ErrorBoundary.mjs or a "(draft)/x.mjs" leading segment.
|
|
33
|
+
const hits = out.split('\n')
|
|
34
|
+
.filter((s) => s && !/^\(no (?:files found|entries\b)/.test(s) && !/^\.\.\. \[/.test(s))
|
|
35
|
+
.map((s) => s.trim())
|
|
36
|
+
.filter(Boolean)
|
|
37
|
+
.slice(0, 5);
|
|
38
|
+
return hits.length ? `\n[path not found here; same-named indexed file(s): ${hits.join(', ')}]` : '';
|
|
39
|
+
} catch {
|
|
40
|
+
return '';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Models occasionally glue a quoted segment onto an otherwise-unquoted path
|
|
45
|
+
// (e.g. `C:/Project/mixdog/"src/x.mjs"` — prefix bare, trailing segment
|
|
46
|
+
// wrapped in literal quote chars from a mis-escaped interpolation). But
|
|
47
|
+
// quote chars CAN be legit path content (POSIX `src/Bob's/file.mjs`), so
|
|
48
|
+
// stripping is existence-guarded: keep the original if it stats, only fall
|
|
49
|
+
// back to the stripped variant otherwise. Cost is one statSync and only on
|
|
50
|
+
// the rare quote-containing path.
|
|
51
|
+
export function stripEmbeddedPathQuotes(p) {
|
|
52
|
+
if (typeof p !== 'string' || !p) return p;
|
|
53
|
+
if (!p.includes('"') && !p.includes("'")) return p;
|
|
54
|
+
try { statSync(p); return p; } catch { /* fall through to stripped */ }
|
|
55
|
+
return p.replace(/['"]/g, '');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Reuse read's own ENOENT recovery (path-diagnostics findBySuffixStrip /
|
|
59
|
+
// findFileByBasename — the exact fs scans read-single-tool.mjs already runs
|
|
60
|
+
// for its "same filename exists at" hint) so a grep/glob path miss redirects
|
|
61
|
+
// to the real location instead of dead-ending. No new fs scan is added here;
|
|
62
|
+
// this only calls the two helpers read already calls, with a caller-supplied
|
|
63
|
+
// action verb ("Search"/"List") in place of read's "Read". Gated to genuine
|
|
64
|
+
// not-found codes: EACCES/EPERM etc. keep their real failure semantics and
|
|
65
|
+
// must not trigger same-basename guidance or the BFS scan.
|
|
66
|
+
const NOT_FOUND_CODES = new Set(['ENOENT', 'ENOTDIR']);
|
|
67
|
+
export function buildNotFoundHint(workDir, missingPath, actionVerb, errCode = 'ENOENT') {
|
|
68
|
+
if (!NOT_FOUND_CODES.has(String(errCode || 'ENOENT'))) return '';
|
|
69
|
+
const suffixHit = findBySuffixStrip(workDir, missingPath);
|
|
70
|
+
if (suffixHit) {
|
|
71
|
+
return ` Not found at this path; the same filename exists at: "${normalizeOutputPath(suffixHit)}". ${actionVerb} that path directly.`;
|
|
72
|
+
}
|
|
73
|
+
const elsewhere = findFileByBasename(workDir, missingPath);
|
|
74
|
+
if (elsewhere.length) {
|
|
75
|
+
return ` Not found at this path; the same filename exists at: ${elsewhere.map((p) => `"${normalizeOutputPath(p)}"`).join(', ')}. ${actionVerb} that path directly.`;
|
|
76
|
+
}
|
|
77
|
+
return '';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function relativePathPrefix(pathPrefix, workDir) {
|
|
81
|
+
if (!workDir) return pathPrefix;
|
|
82
|
+
const cwdFwd = workDir.replace(/\\/g, '/').replace(/\/+$/, '');
|
|
83
|
+
const absFwd = String(pathPrefix || '').replace(/\\/g, '/');
|
|
84
|
+
const haystack = process.platform === 'win32' ? absFwd.toLocaleLowerCase() : absFwd;
|
|
85
|
+
const needle = process.platform === 'win32' ? cwdFwd.toLocaleLowerCase() : cwdFwd;
|
|
86
|
+
if (haystack.startsWith(needle + '/') || haystack === needle) {
|
|
87
|
+
return absFwd.slice(cwdFwd.length + 1) || '.';
|
|
88
|
+
}
|
|
89
|
+
return pathPrefix;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function relativeSearchResultPath(path, workDir) {
|
|
93
|
+
const normalizedWorkDir = normalizeOutputPath(workDir);
|
|
94
|
+
const normalizedAbs = normalizeOutputPath(path);
|
|
95
|
+
if (normalizedAbs.startsWith(normalizedWorkDir + '/') || normalizedAbs.startsWith(normalizedWorkDir + '\\')) {
|
|
96
|
+
return normalizedAbs.slice(normalizedWorkDir.length + 1);
|
|
97
|
+
}
|
|
98
|
+
return normalizedAbs;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function resolveSearchScope(root, workDir) {
|
|
102
|
+
return isAbsolute(root) ? resolve(root) : resolveAgainstCwd(root, workDir);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function isUncOrSmbPath(path) {
|
|
106
|
+
if (typeof path !== 'string' || !path) return false;
|
|
107
|
+
return path.startsWith('\\\\') || path.startsWith('//');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function uncRefusalMessage(toolName, original, resolved) {
|
|
111
|
+
const shown = normalizeOutputPath(resolved || original || '');
|
|
112
|
+
return `Error: ${toolName} refuses UNC/SMB path ${JSON.stringify(shown)}; remote share access is blocked to prevent NTLM credential leaks`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function basePathDiagnostic(basePaths, workDir) {
|
|
116
|
+
return basePaths.map((basePath) => {
|
|
117
|
+
const resolved = resolveSearchScope(basePath, workDir);
|
|
118
|
+
try {
|
|
119
|
+
const st = statSync(resolved);
|
|
120
|
+
return `${normalizeOutputPath(basePath)}: ${st.isDirectory() ? 'path exists (dir)' : 'path exists (file)'}`;
|
|
121
|
+
} catch (err) {
|
|
122
|
+
return `${normalizeOutputPath(basePath)}: path does not exist (${err?.code || 'ENOENT'})`
|
|
123
|
+
+ buildNotFoundHint(workDir, resolved, 'Search', err?.code);
|
|
124
|
+
}
|
|
125
|
+
}).join('; ');
|
|
126
|
+
}
|
|
@@ -13,13 +13,37 @@ import {
|
|
|
13
13
|
normalizeSearchPattern,
|
|
14
14
|
resolveAgainstCwd,
|
|
15
15
|
} from './path-utils.mjs';
|
|
16
|
+
import {
|
|
17
|
+
_suggestIndexedPaths,
|
|
18
|
+
basePathDiagnostic,
|
|
19
|
+
buildNotFoundHint,
|
|
20
|
+
isUncOrSmbPath,
|
|
21
|
+
relativePathPrefix,
|
|
22
|
+
relativeSearchResultPath,
|
|
23
|
+
resolveSearchScope,
|
|
24
|
+
stripEmbeddedPathQuotes,
|
|
25
|
+
uncRefusalMessage,
|
|
26
|
+
} from './search-path-diagnostics.mjs';
|
|
27
|
+
// Facade re-export: path-diagnostic helpers moved to search-path-diagnostics.mjs;
|
|
28
|
+
// keep prior importers of search-tool.mjs unchanged.
|
|
29
|
+
export {
|
|
30
|
+
_suggestIndexedPaths,
|
|
31
|
+
basePathDiagnostic,
|
|
32
|
+
buildNotFoundHint,
|
|
33
|
+
isUncOrSmbPath,
|
|
34
|
+
relativePathPrefix,
|
|
35
|
+
relativeSearchResultPath,
|
|
36
|
+
resolveSearchScope,
|
|
37
|
+
stripEmbeddedPathQuotes,
|
|
38
|
+
uncRefusalMessage,
|
|
39
|
+
} from './search-path-diagnostics.mjs';
|
|
16
40
|
import {
|
|
17
41
|
buildGlobCacheKey,
|
|
18
42
|
buildGrepCacheKey,
|
|
19
43
|
buildGrepRgArgs,
|
|
20
44
|
DEFAULT_IGNORE_GLOBS,
|
|
21
45
|
} from './search-builders.mjs';
|
|
22
|
-
import { runRg, runRgWindowedLines } from './rg-runner.mjs';
|
|
46
|
+
import { runRg, runRgWindowedLines, rgSupportsPcre2 } from './rg-runner.mjs';
|
|
23
47
|
import { markScopedCacheIncomplete } from '../../session/cache/scoped-cache-outcome.mjs';
|
|
24
48
|
import {
|
|
25
49
|
groupGrepContentByFile,
|
|
@@ -34,7 +58,7 @@ import {
|
|
|
34
58
|
statPathsForMtime,
|
|
35
59
|
} from './cache-layers.mjs';
|
|
36
60
|
import { recordReadSnapshot } from './read-snapshot-runtime.mjs';
|
|
37
|
-
import { applyGrepContextLeadPolicy, GREP_CONTEXT_MAX } from './arg-guard.mjs';
|
|
61
|
+
import { applyGrepContextLeadPolicy, GREP_CONTEXT_MAX, hasUnsupportedRipgrepRegex } from './arg-guard.mjs';
|
|
38
62
|
|
|
39
63
|
// Default surrounding-lines window applied by output_mode:'content_with_context'
|
|
40
64
|
// when the caller does not pass an explicit -A/-B/-C/context. Sized to cover a
|
|
@@ -49,51 +73,6 @@ function expandLegacyEscapedAlternationPattern(rawPattern) {
|
|
|
49
73
|
return parts.length > 1 ? parts : null;
|
|
50
74
|
}
|
|
51
75
|
|
|
52
|
-
// Deterministic ENOENT recovery: when a grep path does not exist, surface
|
|
53
|
-
// indexed files that share the missing path's basename, turning a guessed or
|
|
54
|
-
// misplaced path (e.g. session/result-compression.mjs vs the real
|
|
55
|
-
// tools/result-compression.mjs) into the actual file in one step. Exact
|
|
56
|
-
// basename only — no stem/token fuzzing — so the hint is high-signal and
|
|
57
|
-
// noise-free. Invariant: every ENOENT runs the same basename lookup; there is
|
|
58
|
-
// no "guessed a lot" branch. Returns '' (appends nothing) when no same-named
|
|
59
|
-
// indexed file exists or the glob child is unavailable.
|
|
60
|
-
async function _suggestIndexedPaths(missingPath, executeChildBuiltinTool, workDir) {
|
|
61
|
-
if (typeof executeChildBuiltinTool !== 'function') return '';
|
|
62
|
-
const base = String(missingPath).replace(/\\/g, '/').split('/').pop();
|
|
63
|
-
// Skip when there is no usable basename or it carries glob magic (the
|
|
64
|
-
// pattern, not a literal filename, would not map to a real file).
|
|
65
|
-
if (!base || /[*?[\]{}]/.test(base)) return '';
|
|
66
|
-
try {
|
|
67
|
-
const out = await executeChildBuiltinTool('glob', { pattern: `**/${base}`, head_limit: 6 }, workDir);
|
|
68
|
-
if (typeof out !== 'string') return '';
|
|
69
|
-
// Drop ONLY the exact diagnostic forms glob emits: the empty-result line
|
|
70
|
-
// ("(no files found ...", "(no entries after offset ...") and the
|
|
71
|
-
// suffix/warning lines ("... [N more entries]", "... [warning] ...").
|
|
72
|
-
// A broad startsWith('(' / '...' / 'Error') would wrongly drop real
|
|
73
|
-
// paths like ErrorBoundary.mjs or a "(draft)/x.mjs" leading segment.
|
|
74
|
-
const hits = out.split('\n')
|
|
75
|
-
.filter((s) => s && !/^\(no (?:files found|entries\b)/.test(s) && !/^\.\.\. \[/.test(s))
|
|
76
|
-
.map((s) => s.trim())
|
|
77
|
-
.filter(Boolean)
|
|
78
|
-
.slice(0, 5);
|
|
79
|
-
return hits.length ? `\n[path not found here; same-named indexed file(s): ${hits.join(', ')}]` : '';
|
|
80
|
-
} catch {
|
|
81
|
-
return '';
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function relativePathPrefix(pathPrefix, workDir) {
|
|
86
|
-
if (!workDir) return pathPrefix;
|
|
87
|
-
const cwdFwd = workDir.replace(/\\/g, '/').replace(/\/+$/, '');
|
|
88
|
-
const absFwd = String(pathPrefix || '').replace(/\\/g, '/');
|
|
89
|
-
const haystack = process.platform === 'win32' ? absFwd.toLocaleLowerCase() : absFwd;
|
|
90
|
-
const needle = process.platform === 'win32' ? cwdFwd.toLocaleLowerCase() : cwdFwd;
|
|
91
|
-
if (haystack.startsWith(needle + '/') || haystack === needle) {
|
|
92
|
-
return absFwd.slice(cwdFwd.length + 1) || '.';
|
|
93
|
-
}
|
|
94
|
-
return pathPrefix;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
76
|
function relativeGrepLine(line, workDir, pathOnly = false, outputMode = 'content', filenameOmitted = false) {
|
|
98
77
|
const normalized = normalizeGrepLine(line, pathOnly, outputMode, filenameOmitted);
|
|
99
78
|
if (!workDir) return normalized;
|
|
@@ -112,15 +91,6 @@ function relativeGrepLine(line, workDir, pathOnly = false, outputMode = 'content
|
|
|
112
91
|
return normalized;
|
|
113
92
|
}
|
|
114
93
|
|
|
115
|
-
function relativeSearchResultPath(path, workDir) {
|
|
116
|
-
const normalizedWorkDir = normalizeOutputPath(workDir);
|
|
117
|
-
const normalizedAbs = normalizeOutputPath(path);
|
|
118
|
-
if (normalizedAbs.startsWith(normalizedWorkDir + '/') || normalizedAbs.startsWith(normalizedWorkDir + '\\')) {
|
|
119
|
-
return normalizedAbs.slice(normalizedWorkDir.length + 1);
|
|
120
|
-
}
|
|
121
|
-
return normalizedAbs;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
94
|
function uniqueStrings(values) {
|
|
125
95
|
return Array.from(new Set(values.filter((value) => typeof value === 'string' && value)));
|
|
126
96
|
}
|
|
@@ -196,32 +166,6 @@ function isRedundantAllFilesGlob(value) {
|
|
|
196
166
|
return g === '**/*' || g === '**';
|
|
197
167
|
}
|
|
198
168
|
|
|
199
|
-
function resolveSearchScope(root, workDir) {
|
|
200
|
-
return isAbsolute(root) ? resolve(root) : resolveAgainstCwd(root, workDir);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
function isUncOrSmbPath(path) {
|
|
204
|
-
if (typeof path !== 'string' || !path) return false;
|
|
205
|
-
return path.startsWith('\\\\') || path.startsWith('//');
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
function uncRefusalMessage(toolName, original, resolved) {
|
|
209
|
-
const shown = normalizeOutputPath(resolved || original || '');
|
|
210
|
-
return `Error: ${toolName} refuses UNC/SMB path ${JSON.stringify(shown)}; remote share access is blocked to prevent NTLM credential leaks`;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function basePathDiagnostic(basePaths, workDir) {
|
|
214
|
-
return basePaths.map((basePath) => {
|
|
215
|
-
const resolved = resolveSearchScope(basePath, workDir);
|
|
216
|
-
try {
|
|
217
|
-
const st = statSync(resolved);
|
|
218
|
-
return `${normalizeOutputPath(basePath)}: ${st.isDirectory() ? 'path exists (dir)' : 'path exists (file)'}`;
|
|
219
|
-
} catch (err) {
|
|
220
|
-
return `${normalizeOutputPath(basePath)}: path does not exist (${err?.code || 'ENOENT'})`;
|
|
221
|
-
}
|
|
222
|
-
}).join('; ');
|
|
223
|
-
}
|
|
224
|
-
|
|
225
169
|
function grepMissingPatternMessage() {
|
|
226
170
|
return 'Error: grep requires pattern.';
|
|
227
171
|
}
|
|
@@ -275,11 +219,37 @@ function formatGrepOutput({ windowed, totalWindowed, totalKnown, headLimit, offs
|
|
|
275
219
|
|
|
276
220
|
export async function executeGrepTool(args, workDir, executeChildBuiltinTool, readStateScope = null, options = {}) {
|
|
277
221
|
args = normalizeGrepArgs(args);
|
|
222
|
+
// Fan-out guard: batch multiple string paths sequentially, mirroring
|
|
223
|
+
// code_graph files[] batching. Recursive calls pass a single string
|
|
224
|
+
// path, so recursion bottoms out after one level.
|
|
225
|
+
if (Array.isArray(args.path)) {
|
|
226
|
+
const GREP_PATH_CAP = 10;
|
|
227
|
+
const seen = new Set();
|
|
228
|
+
const list = args.path
|
|
229
|
+
.map(p => typeof p === 'string' ? p.trim() : '')
|
|
230
|
+
.filter(p => p && !seen.has(p) && seen.add(p));
|
|
231
|
+
if (list.length > 1) {
|
|
232
|
+
const capped = list.slice(0, GREP_PATH_CAP);
|
|
233
|
+
const parts = [];
|
|
234
|
+
for (const p of capped) {
|
|
235
|
+
let body;
|
|
236
|
+
try {
|
|
237
|
+
body = await executeGrepTool({ ...args, path: p }, workDir, executeChildBuiltinTool, readStateScope, options);
|
|
238
|
+
} catch (err) {
|
|
239
|
+
body = `Error: ${err && err.message ? err.message : err}`;
|
|
240
|
+
}
|
|
241
|
+
parts.push(`# grep ${p}\n${body}`);
|
|
242
|
+
}
|
|
243
|
+
if (list.length > GREP_PATH_CAP) parts.push(`[capped at ${GREP_PATH_CAP} of ${list.length} paths]`);
|
|
244
|
+
return parts.join('\n\n');
|
|
245
|
+
}
|
|
246
|
+
args.path = list[0];
|
|
247
|
+
}
|
|
278
248
|
// Shape context immediately before deriving rg flags. This keeps the
|
|
279
249
|
// Lead-direct MCP path and direct executeGrepTool callers on the same
|
|
280
250
|
// policy even if they bypass or race the outer builtin arg guard.
|
|
281
251
|
applyGrepContextLeadPolicy(args);
|
|
282
|
-
args.path = normalizeInputPath(args.path);
|
|
252
|
+
args.path = stripEmbeddedPathQuotes(normalizeInputPath(args.path));
|
|
283
253
|
args.pattern = coerceShapeFlex(args.pattern);
|
|
284
254
|
args.glob = coerceShapeFlex(args.glob);
|
|
285
255
|
const rawPattern = args.pattern;
|
|
@@ -302,13 +272,32 @@ export async function executeGrepTool(args, workDir, executeChildBuiltinTool, re
|
|
|
302
272
|
|
|
303
273
|
const GREP_MULTILINE_PATTERN_CAP = 5;
|
|
304
274
|
const GREP_ARRAY_PATTERN_CAP = 20;
|
|
305
|
-
|
|
275
|
+
// Rescue: a pattern containing a literal two-char "\n" (backslash + n)
|
|
276
|
+
// outside multiline mode makes rg hard-error with "the literal '\"\\n\"'
|
|
277
|
+
// is not allowed in a regex" — rg requires -U/--multiline before a
|
|
278
|
+
// pattern is allowed to match across a newline. Rather than reject-and-
|
|
279
|
+
// retry, auto-enable multiline whenever any pattern carries a literal
|
|
280
|
+
// \n; an explicit multiline:true from the caller still wins outright.
|
|
281
|
+
const patternsWantMultiline = patterns.some((p) => /\\n/.test(p));
|
|
282
|
+
const multilineMode = args.multiline === true || patternsWantMultiline;
|
|
306
283
|
if (multilineMode && patterns.length > GREP_MULTILINE_PATTERN_CAP) {
|
|
307
284
|
return `Error: multiline:true with more than ${GREP_MULTILINE_PATTERN_CAP} patterns is not allowed (got ${patterns.length}); split into separate grep calls`;
|
|
308
285
|
}
|
|
309
286
|
if (patterns.length > GREP_ARRAY_PATTERN_CAP) {
|
|
310
287
|
return `Error: pattern array exceeds the ${GREP_ARRAY_PATTERN_CAP}-pattern cap (got ${patterns.length}); split into separate grep calls`;
|
|
311
288
|
}
|
|
289
|
+
// Rescue: lookaround/backreference patterns are rejected by rg's default
|
|
290
|
+
// Rust regex engine ("look-around ... is not supported" / "backreferences
|
|
291
|
+
// ... not supported"). rg builds compiled with the optional `pcre2`
|
|
292
|
+
// feature accept the same syntax via -P/--pcre2. Probe capability once
|
|
293
|
+
// (cached) and route there instead of hard-erroring; when the installed
|
|
294
|
+
// rg has no PCRE2 support, fall through unchanged and let the existing
|
|
295
|
+
// arg-guard rejection (or rg's own runtime error) stand.
|
|
296
|
+
const patternsWantPcre2 = hasUnsupportedRipgrepRegex(patterns);
|
|
297
|
+
const pcre2Mode = patternsWantPcre2 && await rgSupportsPcre2();
|
|
298
|
+
if (patternsWantPcre2 && !pcre2Mode) {
|
|
299
|
+
return `Error: grep pattern uses regex syntax ripgrep does not support here (lookaround/backrefs), and the installed rg build has no PCRE2 support (-P unavailable). Use plain pattern arrays or simpler regex.`;
|
|
300
|
+
}
|
|
312
301
|
|
|
313
302
|
let searchPath = args.path || '.';
|
|
314
303
|
const rawGlob = args.glob;
|
|
@@ -453,6 +442,7 @@ export async function executeGrepTool(args, workDir, executeChildBuiltinTool, re
|
|
|
453
442
|
multilineMode,
|
|
454
443
|
onlyMatching: args['-o'] === true,
|
|
455
444
|
fileType,
|
|
445
|
+
pcre2: pcre2Mode,
|
|
456
446
|
});
|
|
457
447
|
// Single-file grep registers a whole-file read snapshot (parity with
|
|
458
448
|
// apply_patch), satisfying the read-before-edit guard while keeping drift
|
|
@@ -478,6 +468,8 @@ export async function executeGrepTool(args, workDir, executeChildBuiltinTool, re
|
|
|
478
468
|
try { grepStat = statSync(grepResolvedPath); }
|
|
479
469
|
catch (err) {
|
|
480
470
|
const msg = `Error: path does not exist: ${normalizeOutputPath(grepResolvedPath)} (${err?.code || 'ENOENT'})`;
|
|
471
|
+
const hint = buildNotFoundHint(workDir, grepResolvedPath, 'Search', err?.code);
|
|
472
|
+
if (hint) return msg + hint;
|
|
481
473
|
return msg + await _suggestIndexedPaths(grepResolvedPath, executeChildBuiltinTool, workDir);
|
|
482
474
|
}
|
|
483
475
|
const filenameOmitted = grepStat.isFile();
|
|
@@ -514,6 +506,7 @@ export async function executeGrepTool(args, workDir, executeChildBuiltinTool, re
|
|
|
514
506
|
multilineMode,
|
|
515
507
|
fileType,
|
|
516
508
|
onlyMatching: args['-o'] === true,
|
|
509
|
+
pcre2: pcre2Mode,
|
|
517
510
|
});
|
|
518
511
|
let windowed;
|
|
519
512
|
let totalWindowed = 0;
|
|
@@ -731,8 +724,8 @@ export async function executeGrepTool(args, workDir, executeChildBuiltinTool, re
|
|
|
731
724
|
export async function executeGlobTool(args, workDir, options = {}) {
|
|
732
725
|
args = normalizeGlobArgs(args);
|
|
733
726
|
args.path = Array.isArray(args.path)
|
|
734
|
-
? args.path.map((p) => normalizeInputPath(p)).filter((p) => typeof p === 'string' && p)
|
|
735
|
-
: normalizeInputPath(args.path);
|
|
727
|
+
? args.path.map((p) => stripEmbeddedPathQuotes(normalizeInputPath(p))).filter((p) => typeof p === 'string' && p)
|
|
728
|
+
: stripEmbeddedPathQuotes(normalizeInputPath(args.path));
|
|
736
729
|
if (Array.isArray(args.path) && args.path.length === 0) {
|
|
737
730
|
return 'Error: path array must contain at least one base directory';
|
|
738
731
|
}
|
|
@@ -861,8 +854,9 @@ export async function executeGlobTool(args, workDir, options = {}) {
|
|
|
861
854
|
rgArgs.push('.');
|
|
862
855
|
try { statSync(rgCwd); }
|
|
863
856
|
catch (err) {
|
|
857
|
+
const hint = buildNotFoundHint(workDir, rgCwd, 'Search', err?.code);
|
|
864
858
|
return {
|
|
865
|
-
error: `path does not exist: ${normalizeOutputPath(rgCwd)} (${err?.code || 'ENOENT'})`,
|
|
859
|
+
error: `path does not exist: ${normalizeOutputPath(rgCwd)} (${err?.code || 'ENOENT'})${hint}`,
|
|
866
860
|
paths: [],
|
|
867
861
|
stdoutTruncated: false,
|
|
868
862
|
};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
2
|
+
import * as fsPromises from 'fs/promises';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { getPluginData } from '../../config.mjs';
|
|
5
|
+
|
|
6
|
+
// One-shot sweep of stale shell-job artefacts. Each backgrounded `bash`
|
|
7
|
+
// emits five files (.json/.done/.exit/.stdout.log/.stderr.log); the .done
|
|
8
|
+
// flag is written when the job exits, so a .done file older than
|
|
9
|
+
// SHELL_JOB_STALE_MS is the invariant proof its sibling files are also
|
|
10
|
+
// safe to remove. Active and recently-completed jobs are kept so
|
|
11
|
+
// `task` status/read/wait readers still find them. Runs once per mcp child
|
|
12
|
+
// lifetime on first getShellJobsDir() call. Async dirent walk + parallel
|
|
13
|
+
// stat/unlink keeps the main event loop free; fire-and-forget so the
|
|
14
|
+
// synchronous caller receives `dir` immediately.
|
|
15
|
+
const SHELL_JOB_STALE_MS = 24 * 60 * 60 * 1000;
|
|
16
|
+
let shellJobsSwept = false;
|
|
17
|
+
async function sweepStaleShellJobs(dir) {
|
|
18
|
+
if (shellJobsSwept) return;
|
|
19
|
+
shellJobsSwept = true;
|
|
20
|
+
const cutoff = Date.now() - SHELL_JOB_STALE_MS;
|
|
21
|
+
let names;
|
|
22
|
+
try { names = await fsPromises.readdir(dir); } catch { return; }
|
|
23
|
+
const expired = [];
|
|
24
|
+
await Promise.all(names.map(async (name) => {
|
|
25
|
+
if (!name.endsWith('.done')) return;
|
|
26
|
+
const p = join(dir, name);
|
|
27
|
+
try {
|
|
28
|
+
const st = await fsPromises.stat(p);
|
|
29
|
+
if (st.mtimeMs < cutoff) expired.push(name.slice(0, -5));
|
|
30
|
+
} catch {}
|
|
31
|
+
}));
|
|
32
|
+
// Orphan reclaim: a crashed wrapper leaves <id>.json with no .done —
|
|
33
|
+
// forever. Invariant proofs of death (either suffices, both gated on the
|
|
34
|
+
// stale cutoff so a young orphan can't race its own spawn):
|
|
35
|
+
// a) deadline: the wrapper enforces timeoutMs, so an entry older than
|
|
36
|
+
// timeoutMs + grace cannot still be running — pid-reuse-proof. Only
|
|
37
|
+
// trusted on runtime proof: detail.timeoutEnforced:true (PS wrapper,
|
|
38
|
+
// unconditional) or the <id>.enforced marker the posix wrapper
|
|
39
|
+
// touches when its `timeout` branch actually runs.
|
|
40
|
+
// b) ESRCH: the recorded pid no longer exists. Alone this misses pids
|
|
41
|
+
// recycled by unrelated live processes, hence (a).
|
|
42
|
+
const ORPHAN_DEADLINE_GRACE_MS = 30 * 60_000;
|
|
43
|
+
const doneSet = new Set(names.filter(n => n.endsWith('.done')).map(n => n.slice(0, -5)));
|
|
44
|
+
await Promise.all(names.map(async (name) => {
|
|
45
|
+
if (!name.endsWith('.json')) return;
|
|
46
|
+
const jobId = name.slice(0, -5);
|
|
47
|
+
if (doneSet.has(jobId)) return;
|
|
48
|
+
const p = join(dir, name);
|
|
49
|
+
try {
|
|
50
|
+
const st = await fsPromises.stat(p);
|
|
51
|
+
if (st.mtimeMs >= cutoff) return;
|
|
52
|
+
const detail = JSON.parse(await fsPromises.readFile(p, 'utf-8'));
|
|
53
|
+
const tmo = Number(detail?.timeoutMs);
|
|
54
|
+
const enforced = detail?.timeoutEnforced === true
|
|
55
|
+
|| existsSync(join(dir, `${jobId}.enforced`));
|
|
56
|
+
const deadlinePassed = enforced
|
|
57
|
+
&& Number.isFinite(tmo) && tmo > 0
|
|
58
|
+
&& (Date.now() - st.mtimeMs) > tmo + ORPHAN_DEADLINE_GRACE_MS;
|
|
59
|
+
if (!deadlinePassed) {
|
|
60
|
+
const pid = Number(detail?.pid);
|
|
61
|
+
if (Number.isFinite(pid) && pid > 0) {
|
|
62
|
+
try { process.kill(pid, 0); return; } // alive (or EPERM → treated dead only via ESRCH below)
|
|
63
|
+
catch (e) { if (e?.code !== 'ESRCH') return; }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
expired.push(jobId);
|
|
67
|
+
} catch {}
|
|
68
|
+
}));
|
|
69
|
+
// Owner sidecar markers carry a dynamic `.owner-<pid>` suffix, so they
|
|
70
|
+
// can't sit in the fixed-extension list — map each expired jobId to the
|
|
71
|
+
// marker name(s) actually present in this listing and unlink those too.
|
|
72
|
+
const expiredSet = new Set(expired);
|
|
73
|
+
const ownerMarkers = names.filter((n) => {
|
|
74
|
+
const i = n.lastIndexOf('.owner-');
|
|
75
|
+
return i > 0 && expiredSet.has(n.slice(0, i));
|
|
76
|
+
});
|
|
77
|
+
await Promise.all([
|
|
78
|
+
...expired.flatMap((jobId) =>
|
|
79
|
+
['.json', '.done', '.exit', '.enforced', '.exit.cmd.sh', '.exit.cmd.ps1', '.stdout.log', '.stderr.log'].map((ext) =>
|
|
80
|
+
fsPromises.unlink(join(dir, jobId + ext)).catch(() => {}),
|
|
81
|
+
),
|
|
82
|
+
),
|
|
83
|
+
...ownerMarkers.map((n) => fsPromises.unlink(join(dir, n)).catch(() => {})),
|
|
84
|
+
]);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function getShellJobsDir() {
|
|
88
|
+
const dir = join(getPluginData(), 'shell-jobs');
|
|
89
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
90
|
+
sweepStaleShellJobs(dir);
|
|
91
|
+
return dir;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function shellJobDetailPath(jobId) { return join(getShellJobsDir(), `${jobId}.json`); }
|
|
95
|
+
export function shellJobStdoutPath(jobId) { return join(getShellJobsDir(), `${jobId}.stdout.log`); }
|
|
96
|
+
export function shellJobStderrPath(jobId) { return join(getShellJobsDir(), `${jobId}.stderr.log`); }
|
|
97
|
+
export function shellJobExitPath(jobId) { return join(getShellJobsDir(), `${jobId}.exit`); }
|
|
98
|
+
export function shellJobDonePath(jobId) { return join(getShellJobsDir(), `${jobId}.done`); }
|
|
99
|
+
// Runtime proof that the posix wrapper's `timeout` branch actually ran: the
|
|
100
|
+
// wrapper touches this marker immediately before exec'ing `timeout`, so its
|
|
101
|
+
// existence is never optimistic (no spawn-time probe can guarantee the
|
|
102
|
+
// wrapper's own env/cwd resolution). PS jobs don't need it — their wrapper
|
|
103
|
+
// enforces unconditionally and records detail.timeoutEnforced:true.
|
|
104
|
+
export function shellJobEnforcedPath(jobId) { return join(getShellJobsDir(), `${jobId}.enforced`); }
|
|
105
|
+
// Owner sidecar marker: a zero-byte file whose NAME encodes the owning CC host
|
|
106
|
+
// (claude.exe) pid — `<jobId>.owner-<pid>`. It lets the statusline owner-filter
|
|
107
|
+
// jobs from a SINGLE directory listing (no per-job JSON read) so the filter can
|
|
108
|
+
// precede its per-tick scan cap — otherwise other sessions' newer jobs evict
|
|
109
|
+
// this session's live jobs before filtering. Swept alongside the other
|
|
110
|
+
// artefacts.
|
|
111
|
+
export function shellJobOwnerPath(jobId, pid) { return join(getShellJobsDir(), `${jobId}.owner-${pid}`); }
|
|
112
|
+
|
|
113
|
+
// Resolve the CLI host pid that owns a freshly-spawned job. Standalone CLI
|
|
114
|
+
// execution is process-owned: background tasks are scoped to this CLI lifetime,
|
|
115
|
+
// and the pid stamp is only for statusline filtering / legacy sidecars.
|
|
116
|
+
export function resolveJobOwnerHostPid(clientHostPid) {
|
|
117
|
+
const explicit = Number(clientHostPid);
|
|
118
|
+
if (Number.isInteger(explicit) && explicit > 0) return explicit;
|
|
119
|
+
const envPid = Number(process.env.MIXDOG_OWNER_HOST_PID);
|
|
120
|
+
if (Number.isInteger(envPid) && envPid > 0) return envPid;
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function writeShellJobDetail(detail) {
|
|
125
|
+
// Session scope stamp: every job record is tagged with the CC host pid
|
|
126
|
+
// that owns it (the claude.exe pid). This is the SAME physical pid the
|
|
127
|
+
// statusline shim passes as --client-host-pid, so the statusline can count
|
|
128
|
+
// only its own session's jobs by exact pid equality (no heuristic). The
|
|
129
|
+
// spawn sites set detail.ownerHostPid from the per-request threaded
|
|
130
|
+
// clientHostPid (resolveJobOwnerHostPid); this fallback only stamps the
|
|
131
|
+
// single-client env value when no spawn-site pid was set, and NEVER
|
|
132
|
+
// overwrites an existing stamp — so the field round-trips through disk on
|
|
133
|
+
// refresh/kill and a correct per-terminal stamp is preserved across rewrites.
|
|
134
|
+
if (detail && detail.ownerHostPid == null) {
|
|
135
|
+
const hostPid = Number(process.env.MIXDOG_OWNER_HOST_PID);
|
|
136
|
+
if (Number.isInteger(hostPid) && hostPid > 0) detail.ownerHostPid = hostPid;
|
|
137
|
+
}
|
|
138
|
+
writeFileSync(shellJobDetailPath(detail.jobId), JSON.stringify(detail, null, 2), 'utf-8');
|
|
139
|
+
// Owner sidecar: encode the resolved owner pid in the marker filename so the
|
|
140
|
+
// statusline can owner-filter from the directory listing alone (before its
|
|
141
|
+
// scan cap). Idempotent zero-byte write — safe to repeat on refresh/kill
|
|
142
|
+
// rewrites. Skipped when no owner is known (legacy/unattributed jobs).
|
|
143
|
+
if (detail && Number.isInteger(detail.ownerHostPid) && detail.ownerHostPid > 0) {
|
|
144
|
+
try { writeFileSync(shellJobOwnerPath(detail.jobId, detail.ownerHostPid), '', 'utf-8'); }
|
|
145
|
+
catch { /* best-effort marker */ }
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function readShellJobDetail(jobId) {
|
|
150
|
+
try {
|
|
151
|
+
const p = shellJobDetailPath(jobId);
|
|
152
|
+
if (!existsSync(p)) return null;
|
|
153
|
+
return JSON.parse(readFileSync(p, 'utf-8'));
|
|
154
|
+
} catch {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function buildJobNotFoundMessage(jobId) {
|
|
160
|
+
return `Error: task not found: ${jobId}`;
|
|
161
|
+
}
|