mixdog 0.9.3 → 0.9.5
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/README.md +112 -38
- package/package.json +10 -3
- 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/r5-orchestrated-task.json +7 -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-r13-clientmeta.json +464 -0
- package/scripts/bench/round-r14-betafeatures.json +466 -0
- package/scripts/bench/round-r15-fulldefault.json +462 -0
- package/scripts/bench/round-r16-sessionid.json +466 -0
- package/scripts/bench/round-r17-wirebytes.json +456 -0
- package/scripts/bench/round-r18-prewarm.json +468 -0
- package/scripts/bench/round-r19-clean.json +472 -0
- package/scripts/bench/round-r20-prewarm-clean.json +475 -0
- package/scripts/bench/round-r21-delta-retry.json +473 -0
- package/scripts/bench/round-r22-full-probe.json +693 -0
- package/scripts/bench/round-r23-itemprobe.json +701 -0
- package/scripts/bench/round-r24-shapefix.json +677 -0
- package/scripts/bench/round-r25-serial.json +464 -0
- package/scripts/bench/round-r26-parallel3.json +671 -0
- package/scripts/bench/round-r27-parallel10.json +894 -0
- package/scripts/bench/round-r28-parallel10-stagger.json +882 -0
- package/scripts/bench/round-r29-parallel10-stagger166.json +886 -0
- package/scripts/bench/round-r30-instid.json +253 -0
- package/scripts/bench/round-r31-upgradeprobe.json +256 -0
- package/scripts/bench/round-r32-vs-codex-lead.json +254 -0
- package/scripts/bench/round-r33-vs-codex-codex.json +115 -0
- package/scripts/bench/round-r34-orchestrated.json +120 -0
- package/scripts/bench/round-r35-orchestrated-codex.json +61 -0
- package/scripts/bench/round-r36-orchestrated-capped.json +128 -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 +251 -32
- package/scripts/freevar-smoke.mjs +95 -0
- package/scripts/internal-comms-bench.mjs +3 -4
- package/scripts/internal-comms-smoke.mjs +10 -9
- package/scripts/model-catalog-audit.mjs +209 -0
- package/scripts/model-list-sanitize-test.mjs +37 -0
- 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/provider-toolcall-test.mjs +7 -3
- package/scripts/recall-bench.mjs +76 -13
- package/scripts/recall-quality-cases.json +12 -0
- package/scripts/recall-usecase-cases.json +18 -0
- package/scripts/session-bench.mjs +152 -6
- package/scripts/tool-smoke.mjs +25 -65
- package/scripts/tui-render-smoke.mjs +90 -0
- package/scripts/webhook-smoke.mjs +208 -0
- package/src/agents/debugger/AGENT.md +4 -1
- package/src/agents/heavy-worker/AGENT.md +9 -8
- package/src/agents/maintainer/AGENT.md +4 -0
- package/src/agents/reviewer/AGENT.md +2 -1
- package/src/agents/scheduler-task/AGENT.md +2 -3
- package/src/agents/webhook-handler/AGENT.md +2 -3
- package/src/agents/worker/AGENT.md +10 -7
- package/src/app.mjs +12 -1
- package/src/headless-role.mjs +7 -1
- package/src/lib/rules-builder.cjs +4 -0
- package/src/mixdog-session-runtime.mjs +647 -2056
- package/src/output-styles/default.md +30 -9
- package/src/output-styles/{oneline.md → extreme-minimal.md} +5 -4
- package/src/output-styles/minimal.md +8 -6
- package/src/output-styles/simple.md +21 -7
- package/src/rules/agent/00-common.md +6 -3
- package/src/rules/agent/30-explorer.md +16 -5
- package/src/rules/lead/01-general.md +5 -5
- package/src/rules/lead/lead-brief.md +15 -0
- package/src/rules/lead/lead-tool.md +6 -15
- package/src/rules/shared/01-tool.md +17 -21
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +8 -3
- 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/providers/anthropic-effort.mjs +62 -20
- 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 +81 -1281
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +607 -0
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -3
- 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 +43 -1013
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +17 -3
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +105 -11
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +356 -0
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +108 -0
- 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 +40 -1143
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +740 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +349 -2131
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +143 -1002
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +229 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-events.mjs +67 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +465 -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/provider-catalog-cache.mjs +80 -0
- 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-policy.mjs +14 -2
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -3
- package/src/runtime/agent/orchestrator/session/loop/recall-fasttrack.mjs +275 -0
- package/src/runtime/agent/orchestrator/session/loop/steering-ladder.mjs +173 -0
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +58 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +239 -0
- package/src/runtime/agent/orchestrator/session/loop.mjs +278 -402
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +471 -0
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +7 -4
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +12 -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/usage-metrics.mjs +210 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +166 -1087
- 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 +20 -1
- 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 +35 -44
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +40 -0
- 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 -92
- 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 +28 -265
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +0 -6
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +57 -3
- package/src/runtime/agent/orchestrator/tools/code-graph/keyword-match.mjs +82 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +10 -122
- package/src/runtime/agent/orchestrator/tools/code-graph/text-columns.mjs +45 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +6 -6
- 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 +0 -21
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +9 -72
- package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +77 -0
- 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 +27 -318
- package/src/runtime/channels/backends/telegram.mjs +8 -12
- package/src/runtime/channels/index.mjs +247 -701
- package/src/runtime/channels/lib/backend-dispatch.mjs +46 -0
- package/src/runtime/channels/lib/config.mjs +37 -149
- package/src/runtime/channels/lib/event-pipeline.mjs +22 -5
- package/src/runtime/channels/lib/event-queue.mjs +78 -13
- package/src/runtime/channels/lib/inbound-routing.mjs +74 -0
- package/src/runtime/channels/lib/interaction-workflows.mjs +5 -113
- package/src/runtime/channels/lib/output-forwarder.mjs +1 -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/scheduler.mjs +27 -113
- package/src/runtime/channels/lib/session-discovery.mjs +56 -4
- 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 +313 -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 +43 -616
- package/src/runtime/channels/tool-defs.mjs +11 -130
- package/src/runtime/memory/index.mjs +210 -1948
- package/src/runtime/memory/lib/core-memory-store.mjs +5 -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/embedding-warmup.mjs +58 -0
- package/src/runtime/memory/lib/ko-morph.mjs +195 -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 +24 -842
- 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-store.mjs +69 -12
- package/src/runtime/memory/lib/memory-text-utils.mjs +46 -0
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -1
- package/src/runtime/memory/lib/query-handlers.mjs +802 -0
- package/src/runtime/memory/lib/recall-format.mjs +55 -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 +5 -13
- 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/atomic-file.mjs +26 -1
- package/src/runtime/shared/config.mjs +14 -4
- package/src/runtime/shared/launcher-control.mjs +2 -2
- package/src/runtime/shared/markdown-frontmatter.mjs +19 -0
- package/src/runtime/shared/schedules-store.mjs +13 -3
- 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 +23 -0
- package/src/runtime/shared/update-checker.mjs +7 -4
- package/src/session-runtime/config-helpers.mjs +119 -2
- package/src/session-runtime/config-lifecycle.mjs +232 -0
- package/src/session-runtime/cwd-plugins.mjs +226 -0
- package/src/session-runtime/mcp-glue.mjs +177 -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 +11 -9
- 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 +205 -0
- package/src/session-runtime/quick-search-models.mjs +47 -0
- package/src/session-runtime/session-hooks.mjs +93 -0
- package/src/session-runtime/settings-api.mjs +352 -0
- package/src/session-runtime/tool-catalog.mjs +29 -29
- package/src/session-runtime/tool-defs.mjs +84 -0
- package/src/session-runtime/warmup-schedulers.mjs +201 -0
- package/src/session-runtime/workflow.mjs +1 -1
- 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 +138 -669
- package/src/standalone/channel-admin.mjs +102 -90
- package/src/standalone/channel-worker.mjs +4 -7
- package/src/standalone/explore-tool.mjs +64 -14
- 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 +77 -870
- package/src/standalone/memory-runtime-proxy.mjs +7 -0
- package/src/standalone/opencode-go-login.mjs +5 -1
- package/src/standalone/provider-admin.mjs +1 -16
- package/src/standalone/usage-dashboard.mjs +3 -1
- package/src/tui/App.jsx +1059 -8110
- package/src/tui/app/app-format.mjs +213 -0
- package/src/tui/app/channel-pickers.mjs +508 -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 +356 -0
- package/src/tui/app/model-options.mjs +334 -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 +489 -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 +677 -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 +512 -0
- package/src/tui/app/use-transcript-window.mjs +607 -0
- package/src/tui/components/ConfirmBar.jsx +10 -7
- package/src/tui/components/Picker.jsx +64 -15
- package/src/tui/components/PromptInput.jsx +33 -102
- package/src/tui/components/SlashCommandPalette.jsx +8 -1
- package/src/tui/components/StatusLine.jsx +69 -15
- package/src/tui/components/TextEntryPanel.jsx +11 -0
- package/src/tui/components/ToolExecution.jsx +52 -594
- 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/display-width.mjs +20 -3
- package/src/tui/dist/index.mjs +13553 -12384
- package/src/tui/engine/agent-job-feed.mjs +133 -0
- package/src/tui/engine/notification-plan.mjs +76 -0
- package/src/tui/engine/render-timing.mjs +17 -0
- package/src/tui/engine/tool-approval.mjs +94 -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.mjs +170 -574
- package/src/tui/figures.mjs +5 -0
- package/src/tui/index.jsx +65 -1
- package/src/tui/input-editing.mjs +2 -2
- package/src/tui/markdown/format-token.mjs +4 -1
- package/src/tui/statusline-ansi-bridge.mjs +11 -3
- package/src/tui/theme.mjs +6 -0
- 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 -501
- 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 -18
- package/src/workflows/sequential/WORKFLOW.md +16 -18
- package/vendor/ink/build/display-width.js +19 -3
- package/vendor/ink/build/ink.js +112 -7
- 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/external-tool-adapters.test.mjs +0 -162
- package/src/runtime/agent/orchestrator/tools/builtin/open-config-tool.mjs +0 -26
- package/src/runtime/channels/lib/holidays.mjs +0 -138
- package/src/runtime/shared/channel-notification-routing.test.mjs +0 -45
- package/src/runtime/shared/task-notification-envelope.test.mjs +0 -107
- 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/input-editing.selection.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/paste-fix.test.mjs +0 -119
- 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
|
@@ -1,8 +1,6 @@
|
|
|
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 {
|
|
@@ -21,6 +19,31 @@ import {
|
|
|
21
19
|
getBackgroundTask,
|
|
22
20
|
} from '../../../../shared/background-tasks.mjs';
|
|
23
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';
|
|
24
47
|
|
|
25
48
|
globalThis.__mixdogShellJobsRuntimeLoaded = true;
|
|
26
49
|
|
|
@@ -28,269 +51,9 @@ function sleep(ms) {
|
|
|
28
51
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
29
52
|
}
|
|
30
53
|
|
|
31
|
-
// One-shot sweep of stale shell-job artefacts. Each backgrounded `bash`
|
|
32
|
-
// emits five files (.json/.done/.exit/.stdout.log/.stderr.log); the .done
|
|
33
|
-
// flag is written when the job exits, so a .done file older than
|
|
34
|
-
// SHELL_JOB_STALE_MS is the invariant proof its sibling files are also
|
|
35
|
-
// safe to remove. Active and recently-completed jobs are kept so
|
|
36
|
-
// `task` status/read/wait readers still find them. Runs once per mcp child
|
|
37
|
-
// lifetime on first getShellJobsDir() call. Async dirent walk + parallel
|
|
38
|
-
// stat/unlink keeps the main event loop free; fire-and-forget so the
|
|
39
|
-
// synchronous caller receives `dir` immediately.
|
|
40
|
-
const SHELL_JOB_STALE_MS = 24 * 60 * 60 * 1000;
|
|
41
|
-
let shellJobsSwept = false;
|
|
42
|
-
async function sweepStaleShellJobs(dir) {
|
|
43
|
-
if (shellJobsSwept) return;
|
|
44
|
-
shellJobsSwept = true;
|
|
45
|
-
const cutoff = Date.now() - SHELL_JOB_STALE_MS;
|
|
46
|
-
let names;
|
|
47
|
-
try { names = await fsPromises.readdir(dir); } catch { return; }
|
|
48
|
-
const expired = [];
|
|
49
|
-
await Promise.all(names.map(async (name) => {
|
|
50
|
-
if (!name.endsWith('.done')) return;
|
|
51
|
-
const p = join(dir, name);
|
|
52
|
-
try {
|
|
53
|
-
const st = await fsPromises.stat(p);
|
|
54
|
-
if (st.mtimeMs < cutoff) expired.push(name.slice(0, -5));
|
|
55
|
-
} catch {}
|
|
56
|
-
}));
|
|
57
|
-
// Orphan reclaim: a crashed wrapper leaves <id>.json with no .done —
|
|
58
|
-
// forever. Invariant proofs of death (either suffices, both gated on the
|
|
59
|
-
// stale cutoff so a young orphan can't race its own spawn):
|
|
60
|
-
// a) deadline: the wrapper enforces timeoutMs, so an entry older than
|
|
61
|
-
// timeoutMs + grace cannot still be running — pid-reuse-proof. Only
|
|
62
|
-
// trusted on runtime proof: detail.timeoutEnforced:true (PS wrapper,
|
|
63
|
-
// unconditional) or the <id>.enforced marker the posix wrapper
|
|
64
|
-
// touches when its `timeout` branch actually runs.
|
|
65
|
-
// b) ESRCH: the recorded pid no longer exists. Alone this misses pids
|
|
66
|
-
// recycled by unrelated live processes, hence (a).
|
|
67
|
-
const ORPHAN_DEADLINE_GRACE_MS = 30 * 60_000;
|
|
68
|
-
const doneSet = new Set(names.filter(n => n.endsWith('.done')).map(n => n.slice(0, -5)));
|
|
69
|
-
await Promise.all(names.map(async (name) => {
|
|
70
|
-
if (!name.endsWith('.json')) return;
|
|
71
|
-
const jobId = name.slice(0, -5);
|
|
72
|
-
if (doneSet.has(jobId)) return;
|
|
73
|
-
const p = join(dir, name);
|
|
74
|
-
try {
|
|
75
|
-
const st = await fsPromises.stat(p);
|
|
76
|
-
if (st.mtimeMs >= cutoff) return;
|
|
77
|
-
const detail = JSON.parse(await fsPromises.readFile(p, 'utf-8'));
|
|
78
|
-
const tmo = Number(detail?.timeoutMs);
|
|
79
|
-
const enforced = detail?.timeoutEnforced === true
|
|
80
|
-
|| existsSync(join(dir, `${jobId}.enforced`));
|
|
81
|
-
const deadlinePassed = enforced
|
|
82
|
-
&& Number.isFinite(tmo) && tmo > 0
|
|
83
|
-
&& (Date.now() - st.mtimeMs) > tmo + ORPHAN_DEADLINE_GRACE_MS;
|
|
84
|
-
if (!deadlinePassed) {
|
|
85
|
-
const pid = Number(detail?.pid);
|
|
86
|
-
if (Number.isFinite(pid) && pid > 0) {
|
|
87
|
-
try { process.kill(pid, 0); return; } // alive (or EPERM → treated dead only via ESRCH below)
|
|
88
|
-
catch (e) { if (e?.code !== 'ESRCH') return; }
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
expired.push(jobId);
|
|
92
|
-
} catch {}
|
|
93
|
-
}));
|
|
94
|
-
// Owner sidecar markers carry a dynamic `.owner-<pid>` suffix, so they
|
|
95
|
-
// can't sit in the fixed-extension list — map each expired jobId to the
|
|
96
|
-
// marker name(s) actually present in this listing and unlink those too.
|
|
97
|
-
const expiredSet = new Set(expired);
|
|
98
|
-
const ownerMarkers = names.filter((n) => {
|
|
99
|
-
const i = n.lastIndexOf('.owner-');
|
|
100
|
-
return i > 0 && expiredSet.has(n.slice(0, i));
|
|
101
|
-
});
|
|
102
|
-
await Promise.all([
|
|
103
|
-
...expired.flatMap((jobId) =>
|
|
104
|
-
['.json', '.done', '.exit', '.enforced', '.exit.cmd.sh', '.exit.cmd.ps1', '.stdout.log', '.stderr.log'].map((ext) =>
|
|
105
|
-
fsPromises.unlink(join(dir, jobId + ext)).catch(() => {}),
|
|
106
|
-
),
|
|
107
|
-
),
|
|
108
|
-
...ownerMarkers.map((n) => fsPromises.unlink(join(dir, n)).catch(() => {})),
|
|
109
|
-
]);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function getShellJobsDir() {
|
|
113
|
-
const dir = join(getPluginData(), 'shell-jobs');
|
|
114
|
-
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
115
|
-
sweepStaleShellJobs(dir);
|
|
116
|
-
return dir;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function shellJobDetailPath(jobId) { return join(getShellJobsDir(), `${jobId}.json`); }
|
|
120
|
-
function shellJobStdoutPath(jobId) { return join(getShellJobsDir(), `${jobId}.stdout.log`); }
|
|
121
|
-
function shellJobStderrPath(jobId) { return join(getShellJobsDir(), `${jobId}.stderr.log`); }
|
|
122
|
-
function shellJobExitPath(jobId) { return join(getShellJobsDir(), `${jobId}.exit`); }
|
|
123
|
-
function shellJobDonePath(jobId) { return join(getShellJobsDir(), `${jobId}.done`); }
|
|
124
|
-
// Runtime proof that the posix wrapper's `timeout` branch actually ran: the
|
|
125
|
-
// wrapper touches this marker immediately before exec'ing `timeout`, so its
|
|
126
|
-
// existence is never optimistic (no spawn-time probe can guarantee the
|
|
127
|
-
// wrapper's own env/cwd resolution). PS jobs don't need it — their wrapper
|
|
128
|
-
// enforces unconditionally and records detail.timeoutEnforced:true.
|
|
129
|
-
function shellJobEnforcedPath(jobId) { return join(getShellJobsDir(), `${jobId}.enforced`); }
|
|
130
|
-
// Owner sidecar marker: a zero-byte file whose NAME encodes the owning CC host
|
|
131
|
-
// (claude.exe) pid — `<jobId>.owner-<pid>`. It lets the statusline owner-filter
|
|
132
|
-
// jobs from a SINGLE directory listing (no per-job JSON read) so the filter can
|
|
133
|
-
// precede its per-tick scan cap — otherwise other sessions' newer jobs evict
|
|
134
|
-
// this session's live jobs before filtering. Swept alongside the other
|
|
135
|
-
// artefacts.
|
|
136
|
-
function shellJobOwnerPath(jobId, pid) { return join(getShellJobsDir(), `${jobId}.owner-${pid}`); }
|
|
137
|
-
|
|
138
54
|
const JOB_STATUS_PREVIEW_MAX_BYTES = 4096;
|
|
139
55
|
const JOB_STATUS_PREVIEW_MAX_LINES = 20;
|
|
140
56
|
const JOB_STATUS_PREVIEW_MAX_CHARS = 1200;
|
|
141
|
-
|
|
142
|
-
// Resolve the CLI host pid that owns a freshly-spawned job. Standalone CLI
|
|
143
|
-
// execution is process-owned: background tasks are scoped to this CLI lifetime,
|
|
144
|
-
// and the pid stamp is only for statusline filtering / legacy sidecars.
|
|
145
|
-
function resolveJobOwnerHostPid(clientHostPid) {
|
|
146
|
-
const explicit = Number(clientHostPid);
|
|
147
|
-
if (Number.isInteger(explicit) && explicit > 0) return explicit;
|
|
148
|
-
const envPid = Number(process.env.MIXDOG_OWNER_HOST_PID);
|
|
149
|
-
if (Number.isInteger(envPid) && envPid > 0) return envPid;
|
|
150
|
-
return null;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function writeShellJobDetail(detail) {
|
|
154
|
-
// Session scope stamp: every job record is tagged with the CC host pid
|
|
155
|
-
// that owns it (the claude.exe pid). This is the SAME physical pid the
|
|
156
|
-
// statusline shim passes as --client-host-pid, so the statusline can count
|
|
157
|
-
// only its own session's jobs by exact pid equality (no heuristic). The
|
|
158
|
-
// spawn sites set detail.ownerHostPid from the per-request threaded
|
|
159
|
-
// clientHostPid (resolveJobOwnerHostPid); this fallback only stamps the
|
|
160
|
-
// single-client env value when no spawn-site pid was set, and NEVER
|
|
161
|
-
// overwrites an existing stamp — so the field round-trips through disk on
|
|
162
|
-
// refresh/kill and a correct per-terminal stamp is preserved across rewrites.
|
|
163
|
-
if (detail && detail.ownerHostPid == null) {
|
|
164
|
-
const hostPid = Number(process.env.MIXDOG_OWNER_HOST_PID);
|
|
165
|
-
if (Number.isInteger(hostPid) && hostPid > 0) detail.ownerHostPid = hostPid;
|
|
166
|
-
}
|
|
167
|
-
writeFileSync(shellJobDetailPath(detail.jobId), JSON.stringify(detail, null, 2), 'utf-8');
|
|
168
|
-
// Owner sidecar: encode the resolved owner pid in the marker filename so the
|
|
169
|
-
// statusline can owner-filter from the directory listing alone (before its
|
|
170
|
-
// scan cap). Idempotent zero-byte write — safe to repeat on refresh/kill
|
|
171
|
-
// rewrites. Skipped when no owner is known (legacy/unattributed jobs).
|
|
172
|
-
if (detail && Number.isInteger(detail.ownerHostPid) && detail.ownerHostPid > 0) {
|
|
173
|
-
try { writeFileSync(shellJobOwnerPath(detail.jobId, detail.ownerHostPid), '', 'utf-8'); }
|
|
174
|
-
catch { /* best-effort marker */ }
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function readShellJobDetail(jobId) {
|
|
179
|
-
try {
|
|
180
|
-
const p = shellJobDetailPath(jobId);
|
|
181
|
-
if (!existsSync(p)) return null;
|
|
182
|
-
return JSON.parse(readFileSync(p, 'utf-8'));
|
|
183
|
-
} catch {
|
|
184
|
-
return null;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
export function buildJobNotFoundMessage(jobId) {
|
|
189
|
-
return `Error: task not found: ${jobId}`;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function isPidAlive(pid) {
|
|
193
|
-
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
194
|
-
try {
|
|
195
|
-
process.kill(pid, 0);
|
|
196
|
-
return true;
|
|
197
|
-
} catch {
|
|
198
|
-
return false;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function killProcessTree(pid, signal = 'SIGTERM') {
|
|
203
|
-
if (!Number.isFinite(pid) || pid <= 0) return false;
|
|
204
|
-
try {
|
|
205
|
-
if (process.platform === 'win32') {
|
|
206
|
-
spawn('taskkill', ['/pid', String(pid), '/t', '/f'], { windowsHide: true, stdio: 'ignore' });
|
|
207
|
-
} else {
|
|
208
|
-
try { process.kill(-pid, signal); }
|
|
209
|
-
catch { process.kill(pid, signal); }
|
|
210
|
-
// SIGKILL escalation: a background child that ignores SIGTERM must
|
|
211
|
-
// not survive (foreground treeKill / persistent _killProcessTree
|
|
212
|
-
// already do this). After a 3s grace, force-kill the group/pid.
|
|
213
|
-
// unref so this backstop never holds the host process open.
|
|
214
|
-
if (signal === 'SIGTERM') {
|
|
215
|
-
const t = setTimeout(() => {
|
|
216
|
-
try { process.kill(-pid, 'SIGKILL'); }
|
|
217
|
-
catch { try { process.kill(pid, 'SIGKILL'); } catch { /* already gone */ } }
|
|
218
|
-
}, 3000);
|
|
219
|
-
if (t.unref) t.unref();
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
return true;
|
|
223
|
-
} catch {
|
|
224
|
-
return false;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Module-level tracking of live background-job pids so CLI shutdown can reap
|
|
229
|
-
// owned children. Async jobs are intentionally CLI-owned; no restart replay or
|
|
230
|
-
// daemon handoff is attempted.
|
|
231
|
-
const _liveJobPids = new Set();
|
|
232
|
-
const _liveJobIdsByPid = new Map();
|
|
233
|
-
let _shellJobsExitHookInstalled = false;
|
|
234
|
-
function _registerLiveJobPid(pid, jobId = null) {
|
|
235
|
-
if (Number.isFinite(pid) && pid > 0) {
|
|
236
|
-
_liveJobPids.add(pid);
|
|
237
|
-
if (jobId) _liveJobIdsByPid.set(pid, jobId);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
function _unregisterLiveJobPid(pid) {
|
|
241
|
-
if (Number.isFinite(pid) && pid > 0) {
|
|
242
|
-
_liveJobPids.delete(pid);
|
|
243
|
-
_liveJobIdsByPid.delete(pid);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
function _killLiveJobPid(pid, { sync = false } = {}) {
|
|
247
|
-
try {
|
|
248
|
-
if (process.platform === 'win32') {
|
|
249
|
-
if (sync) {
|
|
250
|
-
spawnSync('taskkill.exe', ['/pid', String(pid), '/t', '/f'], {
|
|
251
|
-
windowsHide: true,
|
|
252
|
-
stdio: 'ignore',
|
|
253
|
-
timeout: 1500,
|
|
254
|
-
});
|
|
255
|
-
} else {
|
|
256
|
-
spawn('taskkill', ['/pid', String(pid), '/t', '/f'], { windowsHide: true, stdio: 'ignore' });
|
|
257
|
-
}
|
|
258
|
-
} else {
|
|
259
|
-
try { process.kill(-pid, 'SIGKILL'); }
|
|
260
|
-
catch { try { process.kill(pid, 'SIGKILL'); } catch { /* ignore */ } }
|
|
261
|
-
}
|
|
262
|
-
return true;
|
|
263
|
-
} catch {
|
|
264
|
-
return false;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
function _sweepLiveJobsSync() {
|
|
268
|
-
for (const pid of _liveJobPids) {
|
|
269
|
-
_killLiveJobPid(pid, { sync: true });
|
|
270
|
-
}
|
|
271
|
-
_liveJobPids.clear();
|
|
272
|
-
_liveJobIdsByPid.clear();
|
|
273
|
-
}
|
|
274
|
-
function _ensureProcessListenerHeadroom(events, extra = 1) {
|
|
275
|
-
try {
|
|
276
|
-
if (typeof process.getMaxListeners !== 'function' || typeof process.setMaxListeners !== 'function') return;
|
|
277
|
-
const current = process.getMaxListeners();
|
|
278
|
-
if (current === 0) return;
|
|
279
|
-
let needed = current;
|
|
280
|
-
for (const event of events) needed = Math.max(needed, process.listenerCount(event) + extra);
|
|
281
|
-
if (needed > current) process.setMaxListeners(needed);
|
|
282
|
-
} catch { /* ignore */ }
|
|
283
|
-
}
|
|
284
|
-
function _installShellJobsExitHook() {
|
|
285
|
-
if (_shellJobsExitHookInstalled) return;
|
|
286
|
-
_shellJobsExitHookInstalled = true;
|
|
287
|
-
_ensureProcessListenerHeadroom(['exit', 'SIGTERM', 'SIGINT', 'SIGHUP'], 1);
|
|
288
|
-
try { process.on('exit', _sweepLiveJobsSync); } catch { /* ignore */ }
|
|
289
|
-
try { process.on('SIGTERM', _sweepLiveJobsSync); } catch { /* ignore */ }
|
|
290
|
-
try { process.on('SIGINT', _sweepLiveJobsSync); } catch { /* ignore */ }
|
|
291
|
-
try { process.on('SIGHUP', _sweepLiveJobsSync); } catch { /* ignore */ }
|
|
292
|
-
}
|
|
293
|
-
|
|
294
57
|
function shellQuoteSingle(s) {
|
|
295
58
|
return `'${String(s).replace(/'/g, `'\"'\"'`)}'`;
|
|
296
59
|
}
|
|
@@ -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,
|
|
@@ -491,10 +489,6 @@ export async function executeBuiltinTool(name, args, cwd, options = {}) {
|
|
|
491
489
|
return executeTaskTool(args, options);
|
|
492
490
|
case 'read':
|
|
493
491
|
return executeReadTool(args, workDir, readStateScope, executeChildBuiltinTool, options, _readToolHelpers);
|
|
494
|
-
case 'diagnostics':
|
|
495
|
-
return executeDiagnosticsTool(args, workDir, options);
|
|
496
|
-
case 'open_config':
|
|
497
|
-
return executeOpenConfigTool();
|
|
498
492
|
case 'grep':
|
|
499
493
|
return executeGrepTool(args, workDir, executeChildBuiltinTool, readStateScope, options);
|
|
500
494
|
case 'glob':
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
} from './search.mjs';
|
|
39
39
|
|
|
40
40
|
const CODE_GRAPH_BATCHABLE_MODES = new Set(['symbol', 'find_symbol', 'symbol_search', 'callers', 'callees', 'references']);
|
|
41
|
+
const CODE_GRAPH_FILE_BATCHABLE_MODES = new Set(['imports', 'dependents', 'related', 'impact', 'symbols', 'overview']);
|
|
41
42
|
|
|
42
43
|
function _collectGraphSymbolList(args) {
|
|
43
44
|
const split = (s) => String(s || '').split(/[,\s]+/).map((t) => t.trim()).filter(Boolean);
|
|
@@ -48,6 +49,23 @@ function _collectGraphSymbolList(args) {
|
|
|
48
49
|
])];
|
|
49
50
|
}
|
|
50
51
|
|
|
52
|
+
const CODE_GRAPH_FILE_BATCH_CAP = 20;
|
|
53
|
+
|
|
54
|
+
function _collectGraphFileList(args) {
|
|
55
|
+
const split = (s) => String(s || '').split(/,+/).map((t) => t.trim()).filter(Boolean);
|
|
56
|
+
const list = [...new Set([
|
|
57
|
+
...(Array.isArray(args?.files) ? args.files.map((f) => String(f || '').trim()).filter(Boolean) : []),
|
|
58
|
+
...(typeof args?.files === 'string' ? split(args.files) : []),
|
|
59
|
+
...(typeof args?.file === 'string' && args.file.trim() ? [args.file.trim()] : []),
|
|
60
|
+
])];
|
|
61
|
+
if (list.length > CODE_GRAPH_FILE_BATCH_CAP) {
|
|
62
|
+
const capped = list.slice(0, CODE_GRAPH_FILE_BATCH_CAP);
|
|
63
|
+
capped._capped = true;
|
|
64
|
+
return capped;
|
|
65
|
+
}
|
|
66
|
+
return list;
|
|
67
|
+
}
|
|
68
|
+
|
|
51
69
|
async function codeGraph(args, cwd, signal = null, options = {}) {
|
|
52
70
|
let mode = String(args?.mode || '').trim();
|
|
53
71
|
if (!mode) throw new Error('code_graph: "mode" is required');
|
|
@@ -208,11 +226,27 @@ async function codeGraph(args, cwd, signal = null, options = {}) {
|
|
|
208
226
|
}
|
|
209
227
|
|
|
210
228
|
if (mode === 'symbol_search') {
|
|
211
|
-
const keyword = String(args?.symbol || '').trim();
|
|
212
|
-
if (!keyword) throw new Error('code_graph symbol_search: "symbol" is required.');
|
|
213
229
|
const language = String(args?.language || '').trim() || null;
|
|
214
230
|
const limit = Math.max(1, Math.min(100, Number(args?.limit || 30)));
|
|
215
|
-
|
|
231
|
+
const symbolsList = Array.isArray(args?.symbols)
|
|
232
|
+
? args.symbols.map((s) => String(s || '').trim()).filter(Boolean)
|
|
233
|
+
: [];
|
|
234
|
+
const keyword = String(args?.symbol || '').trim();
|
|
235
|
+
const keywords = symbolsList.length ? symbolsList : (keyword ? [keyword] : []);
|
|
236
|
+
if (!keywords.length) throw new Error('code_graph symbol_search: "symbol" (or "symbols[]") is required.');
|
|
237
|
+
if (keywords.length === 1) {
|
|
238
|
+
return _searchSymbolsByKeyword(graph, keywords[0], cwd, { language, limit });
|
|
239
|
+
}
|
|
240
|
+
// Batch: merge results across symbols, dedupe identical result blocks.
|
|
241
|
+
const seen = new Set();
|
|
242
|
+
const sections = [];
|
|
243
|
+
for (const kw of keywords) {
|
|
244
|
+
const result = _searchSymbolsByKeyword(graph, kw, cwd, { language, limit });
|
|
245
|
+
if (seen.has(result)) continue;
|
|
246
|
+
seen.add(result);
|
|
247
|
+
sections.push(`# symbol_search: ${kw}\n${result}`);
|
|
248
|
+
}
|
|
249
|
+
return sections.join('\n\n');
|
|
216
250
|
}
|
|
217
251
|
|
|
218
252
|
if (mode === 'references') {
|
|
@@ -468,6 +502,26 @@ export async function executeCodeGraphTool(name, args, cwd, signal = null, optio
|
|
|
468
502
|
return dispatchOne({ ...args, symbol: symbolList[0], symbols: undefined });
|
|
469
503
|
}
|
|
470
504
|
}
|
|
505
|
+
if (CODE_GRAPH_FILE_BATCHABLE_MODES.has(batchMode)) {
|
|
506
|
+
const fileList = _collectGraphFileList(args);
|
|
507
|
+
if (fileList.length > 1) {
|
|
508
|
+
const capped = fileList._capped;
|
|
509
|
+
return (async () => {
|
|
510
|
+
const sections = [];
|
|
511
|
+
for (const f of fileList) {
|
|
512
|
+
let body;
|
|
513
|
+
try { body = await dispatchOne({ ...args, file: f, files: undefined }); }
|
|
514
|
+
catch (e) { body = `Error: ${e?.message || String(e)}`; }
|
|
515
|
+
sections.push(`# ${batchMode} ${f}\n${body}`);
|
|
516
|
+
}
|
|
517
|
+
if (capped) sections.push(`Note: file list capped at ${CODE_GRAPH_FILE_BATCH_CAP} entries.`);
|
|
518
|
+
return sections.join('\n\n');
|
|
519
|
+
})();
|
|
520
|
+
}
|
|
521
|
+
if (fileList.length === 1 && args?.file !== fileList[0]) {
|
|
522
|
+
return dispatchOne({ ...args, file: fileList[0], files: undefined });
|
|
523
|
+
}
|
|
524
|
+
}
|
|
471
525
|
return dispatchOne(args);
|
|
472
526
|
}
|
|
473
527
|
default: throw new Error(`Unknown code-graph tool: ${name}`);
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Keyword-to-symbol fuzzy matching: tokenization, token-aligned contiguous
|
|
2
|
+
// match, ordered token match, and ranking key. Pure string helpers with no
|
|
3
|
+
// graph/cache state. Extracted from search.mjs.
|
|
4
|
+
|
|
5
|
+
export function _keywordSymbolSortKey(symbolName, keyword) {
|
|
6
|
+
const lowerName = String(symbolName || '').toLowerCase();
|
|
7
|
+
const lowerKey = String(keyword || '').toLowerCase();
|
|
8
|
+
const idx = lowerName.indexOf(lowerKey);
|
|
9
|
+
if (idx < 0) return null;
|
|
10
|
+
const atStart = idx === 0 ? 0 : 1;
|
|
11
|
+
return [lowerName.length, atStart, idx, symbolName];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function _tokenizeKeyword(s) {
|
|
15
|
+
return String(s || '')
|
|
16
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
17
|
+
.split(/[^a-zA-Z0-9]+/)
|
|
18
|
+
.filter(Boolean)
|
|
19
|
+
.map((t) => t.toLowerCase());
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function _tokenStartOffsets(sym) {
|
|
23
|
+
const starts = new Set();
|
|
24
|
+
let prevAlnum = false;
|
|
25
|
+
let prevUpper = false;
|
|
26
|
+
for (let i = 0; i < sym.length; i += 1) {
|
|
27
|
+
const c = sym[i];
|
|
28
|
+
const isAlnum = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9');
|
|
29
|
+
if (!isAlnum) { prevAlnum = false; prevUpper = false; continue; }
|
|
30
|
+
const isUpper = c >= 'A' && c <= 'Z';
|
|
31
|
+
if (!prevAlnum) starts.add(i);
|
|
32
|
+
else if (isUpper && !prevUpper) starts.add(i);
|
|
33
|
+
prevAlnum = true;
|
|
34
|
+
prevUpper = isUpper;
|
|
35
|
+
}
|
|
36
|
+
return starts;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function _contiguousMatchTokenAligned(sym, lowerKey) {
|
|
40
|
+
const len = lowerKey.length;
|
|
41
|
+
if (!len) return false;
|
|
42
|
+
const symLower = sym.toLowerCase();
|
|
43
|
+
const starts = _tokenStartOffsets(sym);
|
|
44
|
+
let lead = 0;
|
|
45
|
+
while (lead < len) {
|
|
46
|
+
const c = lowerKey[lead];
|
|
47
|
+
const isAlnum = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
|
|
48
|
+
if (isAlnum) break;
|
|
49
|
+
lead += 1;
|
|
50
|
+
}
|
|
51
|
+
let from = 0;
|
|
52
|
+
for (;;) {
|
|
53
|
+
const idx = symLower.indexOf(lowerKey, from);
|
|
54
|
+
if (idx < 0) return false;
|
|
55
|
+
const end = idx + len;
|
|
56
|
+
const effectiveIdx = idx + lead;
|
|
57
|
+
if (starts.has(effectiveIdx)) return true;
|
|
58
|
+
let interiorBoundary = false;
|
|
59
|
+
for (const s of starts) {
|
|
60
|
+
if (s > effectiveIdx && s < end) { interiorBoundary = true; break; }
|
|
61
|
+
}
|
|
62
|
+
if (!interiorBoundary) return true;
|
|
63
|
+
from = idx + 1;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function _orderedTokenMatch(symLower, tokens) {
|
|
68
|
+
let from = 0;
|
|
69
|
+
for (const t of tokens) {
|
|
70
|
+
const i = symLower.indexOf(t, from);
|
|
71
|
+
if (i < 0) return false;
|
|
72
|
+
from = i + t.length;
|
|
73
|
+
}
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function _keywordMatchesSymbolName(name, lowerKey, keyTokens) {
|
|
78
|
+
const sym = String(name || '').trim();
|
|
79
|
+
if (!sym || !lowerKey) return false;
|
|
80
|
+
if (_contiguousMatchTokenAligned(sym, lowerKey)) return true;
|
|
81
|
+
return keyTokens.length >= 2 && _orderedTokenMatch(sym.toLowerCase(), keyTokens);
|
|
82
|
+
}
|
|
@@ -19,6 +19,16 @@ import {
|
|
|
19
19
|
} from './symbol-index.mjs';
|
|
20
20
|
import { CODE_GRAPH_MAX_FILES } from './constants.mjs';
|
|
21
21
|
import { _inferSpanEndByIndent } from './span.mjs';
|
|
22
|
+
import {
|
|
23
|
+
_toByteColumn,
|
|
24
|
+
_byteColToCharCol,
|
|
25
|
+
_nearestEnclosingSymbol,
|
|
26
|
+
} from './text-columns.mjs';
|
|
27
|
+
import {
|
|
28
|
+
_keywordSymbolSortKey,
|
|
29
|
+
_tokenizeKeyword,
|
|
30
|
+
_keywordMatchesSymbolName,
|
|
31
|
+
} from './keyword-match.mjs';
|
|
22
32
|
|
|
23
33
|
export function _extractCallees(graph, declHit, _cwd, { cap = 200, callerSymbol = null, language = null } = {}) {
|
|
24
34
|
if (!declHit || !_CALLEES_BRACE_LANGS.has(declHit.lang)) return [];
|
|
@@ -535,85 +545,6 @@ function _resolveCalleeDeclaration(graph, name, { language = null, preferRel = n
|
|
|
535
545
|
return _pickCalleeDeclHit(_findSymbolHits(graph, name, { language }), preferRel);
|
|
536
546
|
}
|
|
537
547
|
|
|
538
|
-
function _keywordSymbolSortKey(symbolName, keyword) {
|
|
539
|
-
const lowerName = String(symbolName || '').toLowerCase();
|
|
540
|
-
const lowerKey = String(keyword || '').toLowerCase();
|
|
541
|
-
const idx = lowerName.indexOf(lowerKey);
|
|
542
|
-
if (idx < 0) return null;
|
|
543
|
-
const atStart = idx === 0 ? 0 : 1;
|
|
544
|
-
return [lowerName.length, atStart, idx, symbolName];
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
function _tokenizeKeyword(s) {
|
|
548
|
-
return String(s || '')
|
|
549
|
-
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
550
|
-
.split(/[^a-zA-Z0-9]+/)
|
|
551
|
-
.filter(Boolean)
|
|
552
|
-
.map((t) => t.toLowerCase());
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
function _tokenStartOffsets(sym) {
|
|
556
|
-
const starts = new Set();
|
|
557
|
-
let prevAlnum = false;
|
|
558
|
-
let prevUpper = false;
|
|
559
|
-
for (let i = 0; i < sym.length; i += 1) {
|
|
560
|
-
const c = sym[i];
|
|
561
|
-
const isAlnum = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9');
|
|
562
|
-
if (!isAlnum) { prevAlnum = false; prevUpper = false; continue; }
|
|
563
|
-
const isUpper = c >= 'A' && c <= 'Z';
|
|
564
|
-
if (!prevAlnum) starts.add(i);
|
|
565
|
-
else if (isUpper && !prevUpper) starts.add(i);
|
|
566
|
-
prevAlnum = true;
|
|
567
|
-
prevUpper = isUpper;
|
|
568
|
-
}
|
|
569
|
-
return starts;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
function _contiguousMatchTokenAligned(sym, lowerKey) {
|
|
573
|
-
const len = lowerKey.length;
|
|
574
|
-
if (!len) return false;
|
|
575
|
-
const symLower = sym.toLowerCase();
|
|
576
|
-
const starts = _tokenStartOffsets(sym);
|
|
577
|
-
let lead = 0;
|
|
578
|
-
while (lead < len) {
|
|
579
|
-
const c = lowerKey[lead];
|
|
580
|
-
const isAlnum = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
|
|
581
|
-
if (isAlnum) break;
|
|
582
|
-
lead += 1;
|
|
583
|
-
}
|
|
584
|
-
let from = 0;
|
|
585
|
-
for (;;) {
|
|
586
|
-
const idx = symLower.indexOf(lowerKey, from);
|
|
587
|
-
if (idx < 0) return false;
|
|
588
|
-
const end = idx + len;
|
|
589
|
-
const effectiveIdx = idx + lead;
|
|
590
|
-
if (starts.has(effectiveIdx)) return true;
|
|
591
|
-
let interiorBoundary = false;
|
|
592
|
-
for (const s of starts) {
|
|
593
|
-
if (s > effectiveIdx && s < end) { interiorBoundary = true; break; }
|
|
594
|
-
}
|
|
595
|
-
if (!interiorBoundary) return true;
|
|
596
|
-
from = idx + 1;
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
function _orderedTokenMatch(symLower, tokens) {
|
|
601
|
-
let from = 0;
|
|
602
|
-
for (const t of tokens) {
|
|
603
|
-
const i = symLower.indexOf(t, from);
|
|
604
|
-
if (i < 0) return false;
|
|
605
|
-
from = i + t.length;
|
|
606
|
-
}
|
|
607
|
-
return true;
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
function _keywordMatchesSymbolName(name, lowerKey, keyTokens) {
|
|
611
|
-
const sym = String(name || '').trim();
|
|
612
|
-
if (!sym || !lowerKey) return false;
|
|
613
|
-
if (_contiguousMatchTokenAligned(sym, lowerKey)) return true;
|
|
614
|
-
return keyTokens.length >= 2 && _orderedTokenMatch(sym.toLowerCase(), keyTokens);
|
|
615
|
-
}
|
|
616
|
-
|
|
617
548
|
function _nativeSymbolHit(node, sym) {
|
|
618
549
|
const line = Number(sym?.line ?? sym?.startLine);
|
|
619
550
|
if (!Number.isFinite(line) || line < 1) return null;
|
|
@@ -1009,49 +940,6 @@ function _referenceKind(line, symbol, lang = null) {
|
|
|
1009
940
|
return 'reference';
|
|
1010
941
|
}
|
|
1011
942
|
|
|
1012
|
-
function _toByteColumn(lineText, charCol) {
|
|
1013
|
-
if (!Number.isFinite(charCol) || charCol < 1) return charCol;
|
|
1014
|
-
const prefix = String(lineText || '').slice(0, charCol - 1);
|
|
1015
|
-
return Buffer.byteLength(prefix, 'utf8') + 1;
|
|
1016
|
-
}
|
|
1017
|
-
|
|
1018
|
-
function _byteColToCharCol(lineText, byteCol) {
|
|
1019
|
-
if (!Number.isFinite(byteCol) || byteCol < 1) return 1;
|
|
1020
|
-
const s = String(lineText || '');
|
|
1021
|
-
let bytes = 0;
|
|
1022
|
-
let k = 0;
|
|
1023
|
-
while (k < s.length && bytes < byteCol - 1) {
|
|
1024
|
-
const cp = s.codePointAt(k);
|
|
1025
|
-
bytes += Buffer.byteLength(String.fromCodePoint(cp), 'utf8');
|
|
1026
|
-
k += cp > 0xFFFF ? 2 : 1;
|
|
1027
|
-
}
|
|
1028
|
-
return k + 1;
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
function _nearestEnclosingSymbol(node, sourceText, lineNumber, col = null) {
|
|
1032
|
-
const FUNCTION_LIKE = new Set([
|
|
1033
|
-
'function', 'method', 'arrow', 'class', 'generator', 'fn', 'async-function',
|
|
1034
|
-
'constructor', 'record', 'local-function',
|
|
1035
|
-
]);
|
|
1036
|
-
const symbols = Array.isArray(node?.symbols) ? node.symbols : [];
|
|
1037
|
-
const inRange = (item) => {
|
|
1038
|
-
if (item.line > lineNumber || Number(item.endLine) < lineNumber) return false;
|
|
1039
|
-
if (col != null) {
|
|
1040
|
-
const sl = Number(item.startLine);
|
|
1041
|
-
const sc = Number(item.startCol);
|
|
1042
|
-
const ec = Number(item.endCol);
|
|
1043
|
-
if (Number.isFinite(sl) && sl === lineNumber && Number.isFinite(sc) && col < sc) return false;
|
|
1044
|
-
if (Number(item.endLine) === lineNumber && Number.isFinite(ec) && col > ec) return false;
|
|
1045
|
-
}
|
|
1046
|
-
return true;
|
|
1047
|
-
};
|
|
1048
|
-
const candidates = symbols
|
|
1049
|
-
.filter(inRange)
|
|
1050
|
-
.sort((a, b) => (b.line - a.line) || ((Number(b.startCol) || 0) - (Number(a.startCol) || 0)));
|
|
1051
|
-
const fn = candidates.find((item) => FUNCTION_LIKE.has(String(item.kind || '').toLowerCase()));
|
|
1052
|
-
return fn || candidates[0] || null;
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
943
|
function _collectCallerEntries(graph, symbol, referenceText) {
|
|
1056
944
|
const entries = _parseReferenceEntries(referenceText);
|
|
1057
945
|
const detailed = [];
|