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,108 @@
|
|
|
1
|
+
import { spawn, spawnSync } from 'child_process';
|
|
2
|
+
|
|
3
|
+
// Process/pid lifecycle helpers for background shell jobs: liveness probing,
|
|
4
|
+
// tree-kill, the module-level live-job pid registry, and the CLI-shutdown exit
|
|
5
|
+
// hook that reaps owned children. Async jobs are intentionally CLI-owned; no
|
|
6
|
+
// restart replay or daemon handoff is attempted.
|
|
7
|
+
|
|
8
|
+
export function isPidAlive(pid) {
|
|
9
|
+
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
10
|
+
try {
|
|
11
|
+
process.kill(pid, 0);
|
|
12
|
+
return true;
|
|
13
|
+
} catch {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function killProcessTree(pid, signal = 'SIGTERM') {
|
|
19
|
+
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
20
|
+
try {
|
|
21
|
+
if (process.platform === 'win32') {
|
|
22
|
+
spawn('taskkill', ['/pid', String(pid), '/t', '/f'], { windowsHide: true, stdio: 'ignore' });
|
|
23
|
+
} else {
|
|
24
|
+
try { process.kill(-pid, signal); }
|
|
25
|
+
catch { process.kill(pid, signal); }
|
|
26
|
+
// SIGKILL escalation: a background child that ignores SIGTERM must
|
|
27
|
+
// not survive (foreground treeKill / persistent _killProcessTree
|
|
28
|
+
// already do this). After a 3s grace, force-kill the group/pid.
|
|
29
|
+
// unref so this backstop never holds the host process open.
|
|
30
|
+
if (signal === 'SIGTERM') {
|
|
31
|
+
const t = setTimeout(() => {
|
|
32
|
+
try { process.kill(-pid, 'SIGKILL'); }
|
|
33
|
+
catch { try { process.kill(pid, 'SIGKILL'); } catch { /* already gone */ } }
|
|
34
|
+
}, 3000);
|
|
35
|
+
if (t.unref) t.unref();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
|
+
} catch {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Module-level tracking of live background-job pids so CLI shutdown can reap
|
|
45
|
+
// owned children. Async jobs are intentionally CLI-owned; no restart replay or
|
|
46
|
+
// daemon handoff is attempted.
|
|
47
|
+
export const _liveJobPids = new Set();
|
|
48
|
+
export const _liveJobIdsByPid = new Map();
|
|
49
|
+
let _shellJobsExitHookInstalled = false;
|
|
50
|
+
export function _registerLiveJobPid(pid, jobId = null) {
|
|
51
|
+
if (Number.isFinite(pid) && pid > 0) {
|
|
52
|
+
_liveJobPids.add(pid);
|
|
53
|
+
if (jobId) _liveJobIdsByPid.set(pid, jobId);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export function _unregisterLiveJobPid(pid) {
|
|
57
|
+
if (Number.isFinite(pid) && pid > 0) {
|
|
58
|
+
_liveJobPids.delete(pid);
|
|
59
|
+
_liveJobIdsByPid.delete(pid);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export function _killLiveJobPid(pid, { sync = false } = {}) {
|
|
63
|
+
try {
|
|
64
|
+
if (process.platform === 'win32') {
|
|
65
|
+
if (sync) {
|
|
66
|
+
spawnSync('taskkill.exe', ['/pid', String(pid), '/t', '/f'], {
|
|
67
|
+
windowsHide: true,
|
|
68
|
+
stdio: 'ignore',
|
|
69
|
+
timeout: 1500,
|
|
70
|
+
});
|
|
71
|
+
} else {
|
|
72
|
+
spawn('taskkill', ['/pid', String(pid), '/t', '/f'], { windowsHide: true, stdio: 'ignore' });
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
try { process.kill(-pid, 'SIGKILL'); }
|
|
76
|
+
catch { try { process.kill(pid, 'SIGKILL'); } catch { /* ignore */ } }
|
|
77
|
+
}
|
|
78
|
+
return true;
|
|
79
|
+
} catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
export function _sweepLiveJobsSync() {
|
|
84
|
+
for (const pid of _liveJobPids) {
|
|
85
|
+
_killLiveJobPid(pid, { sync: true });
|
|
86
|
+
}
|
|
87
|
+
_liveJobPids.clear();
|
|
88
|
+
_liveJobIdsByPid.clear();
|
|
89
|
+
}
|
|
90
|
+
function _ensureProcessListenerHeadroom(events, extra = 1) {
|
|
91
|
+
try {
|
|
92
|
+
if (typeof process.getMaxListeners !== 'function' || typeof process.setMaxListeners !== 'function') return;
|
|
93
|
+
const current = process.getMaxListeners();
|
|
94
|
+
if (current === 0) return;
|
|
95
|
+
let needed = current;
|
|
96
|
+
for (const event of events) needed = Math.max(needed, process.listenerCount(event) + extra);
|
|
97
|
+
if (needed > current) process.setMaxListeners(needed);
|
|
98
|
+
} catch { /* ignore */ }
|
|
99
|
+
}
|
|
100
|
+
export function _installShellJobsExitHook() {
|
|
101
|
+
if (_shellJobsExitHookInstalled) return;
|
|
102
|
+
_shellJobsExitHookInstalled = true;
|
|
103
|
+
_ensureProcessListenerHeadroom(['exit', 'SIGTERM', 'SIGINT', 'SIGHUP'], 1);
|
|
104
|
+
try { process.on('exit', _sweepLiveJobsSync); } catch { /* ignore */ }
|
|
105
|
+
try { process.on('SIGTERM', _sweepLiveJobsSync); } catch { /* ignore */ }
|
|
106
|
+
try { process.on('SIGINT', _sweepLiveJobsSync); } catch { /* ignore */ }
|
|
107
|
+
try { process.on('SIGHUP', _sweepLiveJobsSync); } catch { /* ignore */ }
|
|
108
|
+
}
|
|
@@ -1,19 +1,49 @@
|
|
|
1
|
-
import { spawn
|
|
2
|
-
import { closeSync, existsSync,
|
|
3
|
-
import
|
|
4
|
-
import { basename, join } from 'path';
|
|
5
|
-
import { getPluginData } from '../../config.mjs';
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import { closeSync, existsSync, openSync, readFileSync, readSync, statSync, watch as fsWatch, writeFileSync } from 'fs';
|
|
3
|
+
import { basename } from 'path';
|
|
6
4
|
import { stripAnsi } from '../shell-command.mjs';
|
|
7
5
|
import { scrubLoaderVars, scrubProviderSecrets } from '../env-scrub.mjs';
|
|
8
6
|
import {
|
|
9
7
|
normalizeToolNotifyContext,
|
|
10
8
|
notifyToolCompletion,
|
|
11
9
|
} from '../../../../shared/tool-execution-contract.mjs';
|
|
10
|
+
import {
|
|
11
|
+
renderShellCompletionEnvelope,
|
|
12
|
+
shellCompletionInstruction,
|
|
13
|
+
renderShellPromptStallEnvelope,
|
|
14
|
+
shellPromptStallInstruction,
|
|
15
|
+
} from '../../../../shared/task-notification-envelope.mjs';
|
|
12
16
|
import {
|
|
13
17
|
completeBackgroundTask,
|
|
14
18
|
notifyBackgroundTaskProgress,
|
|
19
|
+
getBackgroundTask,
|
|
15
20
|
} from '../../../../shared/background-tasks.mjs';
|
|
16
21
|
import { startChildGuardian } from '../../../../shared/child-guardian.mjs';
|
|
22
|
+
import {
|
|
23
|
+
getShellJobsDir,
|
|
24
|
+
shellJobStdoutPath,
|
|
25
|
+
shellJobStderrPath,
|
|
26
|
+
shellJobExitPath,
|
|
27
|
+
shellJobDonePath,
|
|
28
|
+
shellJobEnforcedPath,
|
|
29
|
+
resolveJobOwnerHostPid,
|
|
30
|
+
writeShellJobDetail,
|
|
31
|
+
readShellJobDetail,
|
|
32
|
+
} from './shell-job-paths.mjs';
|
|
33
|
+
import {
|
|
34
|
+
isPidAlive,
|
|
35
|
+
killProcessTree,
|
|
36
|
+
_unregisterLiveJobPid,
|
|
37
|
+
_installShellJobsExitHook,
|
|
38
|
+
_registerLiveJobPid,
|
|
39
|
+
_liveJobPids,
|
|
40
|
+
_liveJobIdsByPid,
|
|
41
|
+
_killLiveJobPid,
|
|
42
|
+
} from './shell-job-process.mjs';
|
|
43
|
+
|
|
44
|
+
// Facade re-exports: path/detail helpers and the job-not-found message moved
|
|
45
|
+
// to sibling modules; keep existing importers of shell-jobs.mjs resolving.
|
|
46
|
+
export { buildJobNotFoundMessage } from './shell-job-paths.mjs';
|
|
17
47
|
|
|
18
48
|
globalThis.__mixdogShellJobsRuntimeLoaded = true;
|
|
19
49
|
|
|
@@ -21,269 +51,9 @@ function sleep(ms) {
|
|
|
21
51
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
22
52
|
}
|
|
23
53
|
|
|
24
|
-
// One-shot sweep of stale shell-job artefacts. Each backgrounded `bash`
|
|
25
|
-
// emits five files (.json/.done/.exit/.stdout.log/.stderr.log); the .done
|
|
26
|
-
// flag is written when the job exits, so a .done file older than
|
|
27
|
-
// SHELL_JOB_STALE_MS is the invariant proof its sibling files are also
|
|
28
|
-
// safe to remove. Active and recently-completed jobs are kept so
|
|
29
|
-
// `task` status/read/wait readers still find them. Runs once per mcp child
|
|
30
|
-
// lifetime on first getShellJobsDir() call. Async dirent walk + parallel
|
|
31
|
-
// stat/unlink keeps the main event loop free; fire-and-forget so the
|
|
32
|
-
// synchronous caller receives `dir` immediately.
|
|
33
|
-
const SHELL_JOB_STALE_MS = 24 * 60 * 60 * 1000;
|
|
34
|
-
let shellJobsSwept = false;
|
|
35
|
-
async function sweepStaleShellJobs(dir) {
|
|
36
|
-
if (shellJobsSwept) return;
|
|
37
|
-
shellJobsSwept = true;
|
|
38
|
-
const cutoff = Date.now() - SHELL_JOB_STALE_MS;
|
|
39
|
-
let names;
|
|
40
|
-
try { names = await fsPromises.readdir(dir); } catch { return; }
|
|
41
|
-
const expired = [];
|
|
42
|
-
await Promise.all(names.map(async (name) => {
|
|
43
|
-
if (!name.endsWith('.done')) return;
|
|
44
|
-
const p = join(dir, name);
|
|
45
|
-
try {
|
|
46
|
-
const st = await fsPromises.stat(p);
|
|
47
|
-
if (st.mtimeMs < cutoff) expired.push(name.slice(0, -5));
|
|
48
|
-
} catch {}
|
|
49
|
-
}));
|
|
50
|
-
// Orphan reclaim: a crashed wrapper leaves <id>.json with no .done —
|
|
51
|
-
// forever. Invariant proofs of death (either suffices, both gated on the
|
|
52
|
-
// stale cutoff so a young orphan can't race its own spawn):
|
|
53
|
-
// a) deadline: the wrapper enforces timeoutMs, so an entry older than
|
|
54
|
-
// timeoutMs + grace cannot still be running — pid-reuse-proof. Only
|
|
55
|
-
// trusted on runtime proof: detail.timeoutEnforced:true (PS wrapper,
|
|
56
|
-
// unconditional) or the <id>.enforced marker the posix wrapper
|
|
57
|
-
// touches when its `timeout` branch actually runs.
|
|
58
|
-
// b) ESRCH: the recorded pid no longer exists. Alone this misses pids
|
|
59
|
-
// recycled by unrelated live processes, hence (a).
|
|
60
|
-
const ORPHAN_DEADLINE_GRACE_MS = 30 * 60_000;
|
|
61
|
-
const doneSet = new Set(names.filter(n => n.endsWith('.done')).map(n => n.slice(0, -5)));
|
|
62
|
-
await Promise.all(names.map(async (name) => {
|
|
63
|
-
if (!name.endsWith('.json')) return;
|
|
64
|
-
const jobId = name.slice(0, -5);
|
|
65
|
-
if (doneSet.has(jobId)) return;
|
|
66
|
-
const p = join(dir, name);
|
|
67
|
-
try {
|
|
68
|
-
const st = await fsPromises.stat(p);
|
|
69
|
-
if (st.mtimeMs >= cutoff) return;
|
|
70
|
-
const detail = JSON.parse(await fsPromises.readFile(p, 'utf-8'));
|
|
71
|
-
const tmo = Number(detail?.timeoutMs);
|
|
72
|
-
const enforced = detail?.timeoutEnforced === true
|
|
73
|
-
|| existsSync(join(dir, `${jobId}.enforced`));
|
|
74
|
-
const deadlinePassed = enforced
|
|
75
|
-
&& Number.isFinite(tmo) && tmo > 0
|
|
76
|
-
&& (Date.now() - st.mtimeMs) > tmo + ORPHAN_DEADLINE_GRACE_MS;
|
|
77
|
-
if (!deadlinePassed) {
|
|
78
|
-
const pid = Number(detail?.pid);
|
|
79
|
-
if (Number.isFinite(pid) && pid > 0) {
|
|
80
|
-
try { process.kill(pid, 0); return; } // alive (or EPERM → treated dead only via ESRCH below)
|
|
81
|
-
catch (e) { if (e?.code !== 'ESRCH') return; }
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
expired.push(jobId);
|
|
85
|
-
} catch {}
|
|
86
|
-
}));
|
|
87
|
-
// Owner sidecar markers carry a dynamic `.owner-<pid>` suffix, so they
|
|
88
|
-
// can't sit in the fixed-extension list — map each expired jobId to the
|
|
89
|
-
// marker name(s) actually present in this listing and unlink those too.
|
|
90
|
-
const expiredSet = new Set(expired);
|
|
91
|
-
const ownerMarkers = names.filter((n) => {
|
|
92
|
-
const i = n.lastIndexOf('.owner-');
|
|
93
|
-
return i > 0 && expiredSet.has(n.slice(0, i));
|
|
94
|
-
});
|
|
95
|
-
await Promise.all([
|
|
96
|
-
...expired.flatMap((jobId) =>
|
|
97
|
-
['.json', '.done', '.exit', '.enforced', '.exit.cmd.sh', '.exit.cmd.ps1', '.stdout.log', '.stderr.log'].map((ext) =>
|
|
98
|
-
fsPromises.unlink(join(dir, jobId + ext)).catch(() => {}),
|
|
99
|
-
),
|
|
100
|
-
),
|
|
101
|
-
...ownerMarkers.map((n) => fsPromises.unlink(join(dir, n)).catch(() => {})),
|
|
102
|
-
]);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function getShellJobsDir() {
|
|
106
|
-
const dir = join(getPluginData(), 'shell-jobs');
|
|
107
|
-
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
108
|
-
sweepStaleShellJobs(dir);
|
|
109
|
-
return dir;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function shellJobDetailPath(jobId) { return join(getShellJobsDir(), `${jobId}.json`); }
|
|
113
|
-
function shellJobStdoutPath(jobId) { return join(getShellJobsDir(), `${jobId}.stdout.log`); }
|
|
114
|
-
function shellJobStderrPath(jobId) { return join(getShellJobsDir(), `${jobId}.stderr.log`); }
|
|
115
|
-
function shellJobExitPath(jobId) { return join(getShellJobsDir(), `${jobId}.exit`); }
|
|
116
|
-
function shellJobDonePath(jobId) { return join(getShellJobsDir(), `${jobId}.done`); }
|
|
117
|
-
// Runtime proof that the posix wrapper's `timeout` branch actually ran: the
|
|
118
|
-
// wrapper touches this marker immediately before exec'ing `timeout`, so its
|
|
119
|
-
// existence is never optimistic (no spawn-time probe can guarantee the
|
|
120
|
-
// wrapper's own env/cwd resolution). PS jobs don't need it — their wrapper
|
|
121
|
-
// enforces unconditionally and records detail.timeoutEnforced:true.
|
|
122
|
-
function shellJobEnforcedPath(jobId) { return join(getShellJobsDir(), `${jobId}.enforced`); }
|
|
123
|
-
// Owner sidecar marker: a zero-byte file whose NAME encodes the owning CC host
|
|
124
|
-
// (claude.exe) pid — `<jobId>.owner-<pid>`. It lets the statusline owner-filter
|
|
125
|
-
// jobs from a SINGLE directory listing (no per-job JSON read) so the filter can
|
|
126
|
-
// precede its per-tick scan cap — otherwise other sessions' newer jobs evict
|
|
127
|
-
// this session's live jobs before filtering. Swept alongside the other
|
|
128
|
-
// artefacts.
|
|
129
|
-
function shellJobOwnerPath(jobId, pid) { return join(getShellJobsDir(), `${jobId}.owner-${pid}`); }
|
|
130
|
-
|
|
131
54
|
const JOB_STATUS_PREVIEW_MAX_BYTES = 4096;
|
|
132
55
|
const JOB_STATUS_PREVIEW_MAX_LINES = 20;
|
|
133
56
|
const JOB_STATUS_PREVIEW_MAX_CHARS = 1200;
|
|
134
|
-
|
|
135
|
-
// Resolve the CLI host pid that owns a freshly-spawned job. Standalone CLI
|
|
136
|
-
// execution is process-owned: background tasks are scoped to this CLI lifetime,
|
|
137
|
-
// and the pid stamp is only for statusline filtering / legacy sidecars.
|
|
138
|
-
function resolveJobOwnerHostPid(clientHostPid) {
|
|
139
|
-
const explicit = Number(clientHostPid);
|
|
140
|
-
if (Number.isInteger(explicit) && explicit > 0) return explicit;
|
|
141
|
-
const envPid = Number(process.env.MIXDOG_OWNER_HOST_PID);
|
|
142
|
-
if (Number.isInteger(envPid) && envPid > 0) return envPid;
|
|
143
|
-
return null;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function writeShellJobDetail(detail) {
|
|
147
|
-
// Session scope stamp: every job record is tagged with the CC host pid
|
|
148
|
-
// that owns it (the claude.exe pid). This is the SAME physical pid the
|
|
149
|
-
// statusline shim passes as --client-host-pid, so the statusline can count
|
|
150
|
-
// only its own session's jobs by exact pid equality (no heuristic). The
|
|
151
|
-
// spawn sites set detail.ownerHostPid from the per-request threaded
|
|
152
|
-
// clientHostPid (resolveJobOwnerHostPid); this fallback only stamps the
|
|
153
|
-
// single-client env value when no spawn-site pid was set, and NEVER
|
|
154
|
-
// overwrites an existing stamp — so the field round-trips through disk on
|
|
155
|
-
// refresh/kill and a correct per-terminal stamp is preserved across rewrites.
|
|
156
|
-
if (detail && detail.ownerHostPid == null) {
|
|
157
|
-
const hostPid = Number(process.env.MIXDOG_OWNER_HOST_PID);
|
|
158
|
-
if (Number.isInteger(hostPid) && hostPid > 0) detail.ownerHostPid = hostPid;
|
|
159
|
-
}
|
|
160
|
-
writeFileSync(shellJobDetailPath(detail.jobId), JSON.stringify(detail, null, 2), 'utf-8');
|
|
161
|
-
// Owner sidecar: encode the resolved owner pid in the marker filename so the
|
|
162
|
-
// statusline can owner-filter from the directory listing alone (before its
|
|
163
|
-
// scan cap). Idempotent zero-byte write — safe to repeat on refresh/kill
|
|
164
|
-
// rewrites. Skipped when no owner is known (legacy/unattributed jobs).
|
|
165
|
-
if (detail && Number.isInteger(detail.ownerHostPid) && detail.ownerHostPid > 0) {
|
|
166
|
-
try { writeFileSync(shellJobOwnerPath(detail.jobId, detail.ownerHostPid), '', 'utf-8'); }
|
|
167
|
-
catch { /* best-effort marker */ }
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function readShellJobDetail(jobId) {
|
|
172
|
-
try {
|
|
173
|
-
const p = shellJobDetailPath(jobId);
|
|
174
|
-
if (!existsSync(p)) return null;
|
|
175
|
-
return JSON.parse(readFileSync(p, 'utf-8'));
|
|
176
|
-
} catch {
|
|
177
|
-
return null;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
export function buildJobNotFoundMessage(jobId) {
|
|
182
|
-
return `Error: task not found: ${jobId}`;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function isPidAlive(pid) {
|
|
186
|
-
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
187
|
-
try {
|
|
188
|
-
process.kill(pid, 0);
|
|
189
|
-
return true;
|
|
190
|
-
} catch {
|
|
191
|
-
return false;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function killProcessTree(pid, signal = 'SIGTERM') {
|
|
196
|
-
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
197
|
-
try {
|
|
198
|
-
if (process.platform === 'win32') {
|
|
199
|
-
spawn('taskkill', ['/pid', String(pid), '/t', '/f'], { windowsHide: true, stdio: 'ignore' });
|
|
200
|
-
} else {
|
|
201
|
-
try { process.kill(-pid, signal); }
|
|
202
|
-
catch { process.kill(pid, signal); }
|
|
203
|
-
// SIGKILL escalation: a background child that ignores SIGTERM must
|
|
204
|
-
// not survive (foreground treeKill / persistent _killProcessTree
|
|
205
|
-
// already do this). After a 3s grace, force-kill the group/pid.
|
|
206
|
-
// unref so this backstop never holds the host process open.
|
|
207
|
-
if (signal === 'SIGTERM') {
|
|
208
|
-
const t = setTimeout(() => {
|
|
209
|
-
try { process.kill(-pid, 'SIGKILL'); }
|
|
210
|
-
catch { try { process.kill(pid, 'SIGKILL'); } catch { /* already gone */ } }
|
|
211
|
-
}, 3000);
|
|
212
|
-
if (t.unref) t.unref();
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
return true;
|
|
216
|
-
} catch {
|
|
217
|
-
return false;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Module-level tracking of live background-job pids so CLI shutdown can reap
|
|
222
|
-
// owned children. Async jobs are intentionally CLI-owned; no restart replay or
|
|
223
|
-
// daemon handoff is attempted.
|
|
224
|
-
const _liveJobPids = new Set();
|
|
225
|
-
const _liveJobIdsByPid = new Map();
|
|
226
|
-
let _shellJobsExitHookInstalled = false;
|
|
227
|
-
function _registerLiveJobPid(pid, jobId = null) {
|
|
228
|
-
if (Number.isFinite(pid) && pid > 0) {
|
|
229
|
-
_liveJobPids.add(pid);
|
|
230
|
-
if (jobId) _liveJobIdsByPid.set(pid, jobId);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
function _unregisterLiveJobPid(pid) {
|
|
234
|
-
if (Number.isFinite(pid) && pid > 0) {
|
|
235
|
-
_liveJobPids.delete(pid);
|
|
236
|
-
_liveJobIdsByPid.delete(pid);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
function _killLiveJobPid(pid, { sync = false } = {}) {
|
|
240
|
-
try {
|
|
241
|
-
if (process.platform === 'win32') {
|
|
242
|
-
if (sync) {
|
|
243
|
-
spawnSync('taskkill.exe', ['/pid', String(pid), '/t', '/f'], {
|
|
244
|
-
windowsHide: true,
|
|
245
|
-
stdio: 'ignore',
|
|
246
|
-
timeout: 1500,
|
|
247
|
-
});
|
|
248
|
-
} else {
|
|
249
|
-
spawn('taskkill', ['/pid', String(pid), '/t', '/f'], { windowsHide: true, stdio: 'ignore' });
|
|
250
|
-
}
|
|
251
|
-
} else {
|
|
252
|
-
try { process.kill(-pid, 'SIGKILL'); }
|
|
253
|
-
catch { try { process.kill(pid, 'SIGKILL'); } catch { /* ignore */ } }
|
|
254
|
-
}
|
|
255
|
-
return true;
|
|
256
|
-
} catch {
|
|
257
|
-
return false;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
function _sweepLiveJobsSync() {
|
|
261
|
-
for (const pid of _liveJobPids) {
|
|
262
|
-
_killLiveJobPid(pid, { sync: true });
|
|
263
|
-
}
|
|
264
|
-
_liveJobPids.clear();
|
|
265
|
-
_liveJobIdsByPid.clear();
|
|
266
|
-
}
|
|
267
|
-
function _ensureProcessListenerHeadroom(events, extra = 1) {
|
|
268
|
-
try {
|
|
269
|
-
if (typeof process.getMaxListeners !== 'function' || typeof process.setMaxListeners !== 'function') return;
|
|
270
|
-
const current = process.getMaxListeners();
|
|
271
|
-
if (current === 0) return;
|
|
272
|
-
let needed = current;
|
|
273
|
-
for (const event of events) needed = Math.max(needed, process.listenerCount(event) + extra);
|
|
274
|
-
if (needed > current) process.setMaxListeners(needed);
|
|
275
|
-
} catch { /* ignore */ }
|
|
276
|
-
}
|
|
277
|
-
function _installShellJobsExitHook() {
|
|
278
|
-
if (_shellJobsExitHookInstalled) return;
|
|
279
|
-
_shellJobsExitHookInstalled = true;
|
|
280
|
-
_ensureProcessListenerHeadroom(['exit', 'SIGTERM', 'SIGINT', 'SIGHUP'], 1);
|
|
281
|
-
try { process.on('exit', _sweepLiveJobsSync); } catch { /* ignore */ }
|
|
282
|
-
try { process.on('SIGTERM', _sweepLiveJobsSync); } catch { /* ignore */ }
|
|
283
|
-
try { process.on('SIGINT', _sweepLiveJobsSync); } catch { /* ignore */ }
|
|
284
|
-
try { process.on('SIGHUP', _sweepLiveJobsSync); } catch { /* ignore */ }
|
|
285
|
-
}
|
|
286
|
-
|
|
287
57
|
function shellQuoteSingle(s) {
|
|
288
58
|
return `'${String(s).replace(/'/g, `'\"'\"'`)}'`;
|
|
289
59
|
}
|
|
@@ -729,22 +499,21 @@ export function watchBackgroundShellJob(jobId, notifyCtx) {
|
|
|
729
499
|
const elapsedMs = Number.isFinite(startedAtMs) ? Math.max(0, finishedAtMs - startedAtMs) : null;
|
|
730
500
|
const exitCode = (typeof detail.exitCode === 'number') ? detail.exitCode : null;
|
|
731
501
|
const status = detail.status || (reason === 'timeout' ? 'running' : 'unknown');
|
|
732
|
-
const
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
elapsedMs
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
const body = lines.join('\n');
|
|
502
|
+
const body = renderShellCompletionEnvelope({
|
|
503
|
+
jobId,
|
|
504
|
+
status,
|
|
505
|
+
exitCode,
|
|
506
|
+
elapsedMs,
|
|
507
|
+
command: detail.command,
|
|
508
|
+
summary: detail.summary,
|
|
509
|
+
stdoutPreview: detail.stdoutPreview,
|
|
510
|
+
stderrPreview: detail.stderrPreview,
|
|
511
|
+
mergeStderr: detail.mergeStderr,
|
|
512
|
+
});
|
|
744
513
|
const taskStatus = status === 'completed'
|
|
745
514
|
? 'completed'
|
|
746
515
|
: (status === 'cancelled' ? 'cancelled' : 'failed');
|
|
747
|
-
const instruction =
|
|
516
|
+
const instruction = shellCompletionInstruction({ jobId, status, exitCode });
|
|
748
517
|
const completedTask = completeBackgroundTask(jobId, {
|
|
749
518
|
status: taskStatus,
|
|
750
519
|
result: shellJobPublicTaskResult(detail),
|
|
@@ -780,33 +549,38 @@ export function watchBackgroundShellJob(jobId, notifyCtx) {
|
|
|
780
549
|
if (now - lastOutputAtMs < SHELL_JOB_PROMPT_STALL_MS) return;
|
|
781
550
|
if (!looksLikeInteractivePrompt(tail.text)) return;
|
|
782
551
|
const elapsedMs = now - (Date.parse(detail.startedAt || '') || now);
|
|
783
|
-
const body =
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
'',
|
|
790
|
-
'This background shell task appears to be waiting for interactive input. Background tasks cannot answer prompts automatically; cancel it or rerun with non-interactive flags/input.',
|
|
791
|
-
tail.text ? `\n${tail.text}` : null,
|
|
792
|
-
].filter((line) => line !== null && line !== '').join('\n');
|
|
793
|
-
const instruction = `The background shell task ${jobId} appears to be waiting for interactive input; inspect the prompt, then cancel or rerun it non-interactively.`;
|
|
794
|
-
const sent = notifyBackgroundTaskProgress(jobId, {
|
|
795
|
-
text: body,
|
|
796
|
-
resultType: 'shell_task_progress',
|
|
797
|
-
instruction,
|
|
798
|
-
key: 'interactive-prompt-stall',
|
|
799
|
-
status: null,
|
|
800
|
-
}) || notifyToolCompletion({
|
|
801
|
-
surface: 'shell',
|
|
802
|
-
id: jobId,
|
|
803
|
-
status: null,
|
|
804
|
-
text: body,
|
|
805
|
-
resultType: 'shell_task_progress',
|
|
806
|
-
instruction,
|
|
807
|
-
context: ctx,
|
|
808
|
-
logPrefix: 'shell-jobs',
|
|
552
|
+
const body = renderShellPromptStallEnvelope({
|
|
553
|
+
jobId,
|
|
554
|
+
stalledMs: now - lastOutputAtMs,
|
|
555
|
+
elapsedMs,
|
|
556
|
+
command: detail.command,
|
|
557
|
+
tailText: tail.text,
|
|
809
558
|
});
|
|
559
|
+
const instruction = shellPromptStallInstruction({ jobId });
|
|
560
|
+
// Prefer the progress-notify path when a background task row exists.
|
|
561
|
+
// Its once-key ('interactive-prompt-stall') dedupe returns false after
|
|
562
|
+
// a re-arm even though the row is present — do NOT fall back to
|
|
563
|
+
// notifyToolCompletion in that case (it would fire a duplicate). Only
|
|
564
|
+
// use the completion fallback when the task row is absent (progress
|
|
565
|
+
// path unavailable).
|
|
566
|
+
const sent = getBackgroundTask(jobId)
|
|
567
|
+
? notifyBackgroundTaskProgress(jobId, {
|
|
568
|
+
text: body,
|
|
569
|
+
resultType: 'shell_task_progress',
|
|
570
|
+
instruction,
|
|
571
|
+
key: 'interactive-prompt-stall',
|
|
572
|
+
status: null,
|
|
573
|
+
})
|
|
574
|
+
: notifyToolCompletion({
|
|
575
|
+
surface: 'shell',
|
|
576
|
+
id: jobId,
|
|
577
|
+
status: null,
|
|
578
|
+
text: body,
|
|
579
|
+
resultType: 'shell_task_progress',
|
|
580
|
+
instruction,
|
|
581
|
+
context: ctx,
|
|
582
|
+
logPrefix: 'shell-jobs',
|
|
583
|
+
});
|
|
810
584
|
if (sent) promptStallNotified = true;
|
|
811
585
|
};
|
|
812
586
|
const checkDone = (reason) => {
|
|
@@ -35,8 +35,6 @@ import {
|
|
|
35
35
|
executeGlobTool,
|
|
36
36
|
executeGrepTool,
|
|
37
37
|
} from './builtin/search-tool.mjs';
|
|
38
|
-
import { executeDiagnosticsTool } from './builtin/diagnostics-tool.mjs';
|
|
39
|
-
import { executeOpenConfigTool } from './builtin/open-config-tool.mjs';
|
|
40
38
|
import {
|
|
41
39
|
configureReadRangeIndexTelemetry,
|
|
42
40
|
flushReadRangeIndexesSync,
|
|
@@ -46,6 +44,7 @@ import {
|
|
|
46
44
|
extractPdfText,
|
|
47
45
|
} from './builtin/read-special-files.mjs';
|
|
48
46
|
import { computeUnifiedDiff } from './builtin/diff-utils.mjs';
|
|
47
|
+
import { tryExecuteExternalToolAdapter, isExternalAdapterTool } from './builtin/external-tool-adapters.mjs';
|
|
49
48
|
import { formatToolStartProgress } from './progress-message.mjs';
|
|
50
49
|
import { BUILTIN_TOOLS } from './builtin/builtin-tools.mjs';
|
|
51
50
|
import { validateBuiltinArgs } from './builtin/arg-guard.mjs';
|
|
@@ -490,10 +489,6 @@ export async function executeBuiltinTool(name, args, cwd, options = {}) {
|
|
|
490
489
|
return executeTaskTool(args, options);
|
|
491
490
|
case 'read':
|
|
492
491
|
return executeReadTool(args, workDir, readStateScope, executeChildBuiltinTool, options, _readToolHelpers);
|
|
493
|
-
case 'diagnostics':
|
|
494
|
-
return executeDiagnosticsTool(args, workDir, options);
|
|
495
|
-
case 'open_config':
|
|
496
|
-
return executeOpenConfigTool();
|
|
497
492
|
case 'grep':
|
|
498
493
|
return executeGrepTool(args, workDir, executeChildBuiltinTool, readStateScope, options);
|
|
499
494
|
case 'glob':
|
|
@@ -517,6 +512,10 @@ export async function executeBuiltinTool(name, args, cwd, options = {}) {
|
|
|
517
512
|
case 'hex':
|
|
518
513
|
return executeHexTool(args, workDir, readStateScope, _readModeHelpers);
|
|
519
514
|
default:
|
|
515
|
+
{
|
|
516
|
+
const adapted = await tryExecuteExternalToolAdapter(name, args, workDir, options);
|
|
517
|
+
if (adapted !== null) return adapted;
|
|
518
|
+
}
|
|
520
519
|
return formatUnknownBuiltinToolMessage(name, args);
|
|
521
520
|
}
|
|
522
521
|
})();
|
|
@@ -531,6 +530,12 @@ export function isBuiltinTool(name) {
|
|
|
531
530
|
return BUILTIN_TOOLS.some(t => t.name === toolName);
|
|
532
531
|
}
|
|
533
532
|
|
|
533
|
+
// Re-export so the session loop can route foreign-CLI tool names
|
|
534
|
+
// (StrReplace/Write/bash variants) into executeBuiltinTool — whose default:
|
|
535
|
+
// case runs the native adapter — instead of short-circuiting to the
|
|
536
|
+
// unknown-tool redirect message at the dispatch layer.
|
|
537
|
+
export { isExternalAdapterTool };
|
|
538
|
+
|
|
534
539
|
// Test-only exports for smart truncation helpers (see
|
|
535
540
|
// scripts/test-smart-truncation.mjs). Runtime callers inside this module
|
|
536
541
|
// use the local bindings unchanged; these named exports just make the
|