mixdog 0.9.1 → 0.9.3
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 +9 -1
- package/scripts/_bench-cwc.json +20 -0
- package/scripts/agent-loop-policy-test.mjs +37 -0
- package/scripts/agent-parallel-smoke.mjs +54 -10
- package/scripts/anthropic-maxtokens-test.mjs +119 -0
- package/scripts/background-task-meta-smoke.mjs +1 -1
- package/scripts/bench-run.mjs +262 -0
- package/scripts/build-tui.mjs +13 -1
- package/scripts/compact-smoke.mjs +12 -0
- package/scripts/compact-trigger-migration-smoke.mjs +67 -1
- package/scripts/explore-bench.mjs +124 -0
- package/scripts/hook-bus-test.mjs +191 -0
- package/scripts/ingest-pure-conversation-smoke.mjs +148 -0
- package/scripts/internal-comms-bench.mjs +727 -0
- package/scripts/internal-comms-smoke.mjs +75 -0
- package/scripts/lead-workflow-smoke.mjs +4 -4
- package/scripts/live-worker-smoke.mjs +9 -9
- package/scripts/output-style-bench.mjs +285 -0
- package/scripts/output-style-smoke.mjs +13 -10
- package/scripts/patch-replay.mjs +90 -0
- package/scripts/path-suffix-test.mjs +57 -0
- package/scripts/provider-stream-stall-test.mjs +276 -0
- package/scripts/provider-toolcall-test.mjs +599 -1
- package/scripts/recall-bench.mjs +207 -0
- package/scripts/routing-corpus.mjs +281 -0
- package/scripts/session-bench.mjs +1526 -0
- package/scripts/session-diag.mjs +595 -0
- package/scripts/task-bench.mjs +207 -0
- package/scripts/tool-failures.mjs +6 -6
- package/scripts/tool-smoke.mjs +310 -67
- package/scripts/toolcall-args-test.mjs +81 -0
- package/src/agents/debugger/AGENT.md +4 -4
- package/src/agents/heavy-worker/AGENT.md +20 -9
- package/src/agents/reviewer/AGENT.md +4 -4
- package/src/agents/worker/AGENT.md +17 -9
- package/src/app.mjs +10 -6
- package/src/defaults/{hidden-roles.json → agents.json} +7 -7
- package/src/examples/schedules/SCHEDULE.example.md +32 -0
- package/src/examples/webhooks/WEBHOOK.example.md +40 -0
- package/src/headless-role.mjs +14 -14
- package/src/help.mjs +1 -0
- package/src/lib/rules-builder.cjs +32 -54
- package/src/mixdog-session-runtime.mjs +1040 -2036
- package/src/output-styles/default.md +12 -7
- package/src/output-styles/minimal.md +25 -0
- package/src/output-styles/oneline.md +21 -0
- package/src/output-styles/simple.md +10 -9
- package/src/repl.mjs +17 -7
- package/src/rules/agent/00-common.md +7 -5
- package/src/rules/agent/30-explorer.md +8 -12
- package/src/rules/lead/01-general.md +3 -1
- package/src/rules/lead/lead-tool.md +13 -2
- package/src/rules/shared/01-tool.md +23 -12
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +90 -32
- package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +32 -0
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +18 -6
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +23 -20
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +48 -14
- package/src/runtime/agent/orchestrator/agent-trace.mjs +87 -12
- package/src/runtime/agent/orchestrator/config.mjs +3 -0
- package/src/runtime/agent/orchestrator/context/collect.mjs +182 -67
- package/src/runtime/agent/orchestrator/{internal-roles.mjs → internal-agents.mjs} +72 -72
- package/src/runtime/agent/orchestrator/internal-tools.mjs +13 -26
- package/src/runtime/agent/orchestrator/mcp/client.mjs +100 -18
- package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-effort.mjs +188 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-leaked-toolcall.mjs +444 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +93 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +313 -84
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +104 -38
- package/src/runtime/agent/orchestrator/providers/api-usage.mjs +28 -33
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +184 -17
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +8 -1
- package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +32 -0
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +18 -8
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +54 -20
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +227 -31
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +83 -6
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +229 -115
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +213 -33
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +18 -0
- package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +49 -17
- package/src/runtime/agent/orchestrator/providers/registry.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +22 -17
- package/src/runtime/agent/orchestrator/session/compact.mjs +560 -51
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +250 -3
- package/src/runtime/agent/orchestrator/session/loop/compact-debug.mjs +28 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +262 -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 +49 -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/tool-classify.mjs +52 -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 +513 -1000
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +227 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +235 -0
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +137 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +155 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +303 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +232 -1152
- package/src/runtime/agent/orchestrator/session/store.mjs +4 -4
- package/src/runtime/agent/orchestrator/session/tool-envelope.mjs +61 -0
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +5 -0
- package/src/runtime/agent/orchestrator/stall-policy.mjs +63 -15
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +194 -24
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.test.mjs +143 -0
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +34 -18
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +241 -0
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.test.mjs +162 -0
- package/src/runtime/agent/orchestrator/tools/builtin/fuzzy-match.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/list-formatting.mjs +10 -0
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +5 -4
- package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +42 -2
- package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +15 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-args.mjs +9 -44
- package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +14 -5
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +11 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +10 -17
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +23 -2
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +50 -39
- package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +3 -2
- package/src/runtime/agent/orchestrator/tools/builtin/tool-output-limit.mjs +10 -0
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +70 -1
- 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 +497 -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/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 +1192 -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-mask.mjs +347 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/code-graph.mjs +39 -4189
- package/src/runtime/agent/orchestrator/tools/patch.mjs +119 -5
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +1 -2
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +1 -2
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +2 -4
- package/src/runtime/channels/backends/discord.mjs +99 -9
- package/src/runtime/channels/backends/telegram.mjs +501 -0
- package/src/runtime/channels/index.mjs +437 -1439
- package/src/runtime/channels/lib/boot-profile.mjs +23 -0
- package/src/runtime/channels/lib/config.mjs +54 -2
- package/src/runtime/channels/lib/crash-log.mjs +106 -0
- package/src/runtime/channels/lib/format.mjs +4 -2
- package/src/runtime/channels/lib/index-drop-trace.mjs +72 -0
- package/src/runtime/channels/lib/output-forwarder.mjs +88 -67
- package/src/runtime/channels/lib/runtime-paths.mjs +29 -0
- package/src/runtime/channels/lib/scheduler.mjs +1 -1
- package/src/runtime/channels/lib/telegram-format.mjs +280 -0
- package/src/runtime/channels/lib/tool-format.mjs +1 -1
- package/src/runtime/channels/lib/transcript-discovery.mjs +19 -1
- package/src/runtime/channels/lib/webhook.mjs +59 -31
- package/src/runtime/channels/lib/whisper-language.mjs +42 -0
- package/src/runtime/channels/tool-defs.mjs +1 -1
- package/src/runtime/memory/index.mjs +465 -345
- package/src/runtime/memory/lib/agent-ipc.mjs +2 -2
- package/src/runtime/memory/lib/core-memory-store.mjs +352 -2
- package/src/runtime/memory/lib/cycle-signatures.mjs +34 -0
- package/src/runtime/memory/lib/http-wire.mjs +57 -0
- package/src/runtime/memory/lib/memory-cycle1.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle2.mjs +65 -9
- package/src/runtime/memory/lib/memory-cycle3.mjs +1 -1
- package/src/runtime/memory/lib/memory-recall-scope-filter.mjs +24 -0
- package/src/runtime/memory/lib/memory-retrievers.mjs +8 -0
- package/src/runtime/memory/lib/memory.mjs +121 -4
- package/src/runtime/memory/lib/pg/adapter.mjs +139 -15
- package/src/runtime/memory/lib/promotion-fingerprint.mjs +50 -0
- package/src/runtime/memory/lib/recall-format.mjs +183 -0
- package/src/runtime/memory/lib/session-ingest.mjs +107 -0
- package/src/runtime/memory/lib/trace-store.mjs +69 -22
- package/src/runtime/memory/tool-defs.mjs +10 -7
- package/src/runtime/shared/abort-controller.mjs +1 -1
- package/src/runtime/shared/background-tasks.mjs +2 -3
- package/src/runtime/shared/buffered-appender.mjs +149 -0
- package/src/runtime/shared/channel-notification-routing.mjs +12 -0
- package/src/runtime/shared/channel-notification-routing.test.mjs +45 -0
- package/src/runtime/shared/config.mjs +9 -0
- package/src/runtime/shared/llm/http-agent.mjs +12 -5
- package/src/runtime/shared/schedules-store.mjs +21 -19
- package/src/runtime/shared/task-notification-envelope.mjs +98 -0
- package/src/runtime/shared/task-notification-envelope.test.mjs +107 -0
- package/src/runtime/shared/tool-execution-contract.mjs +2 -2
- package/src/runtime/shared/tool-surface.mjs +98 -13
- package/src/runtime/shared/transcript-writer.mjs +156 -0
- package/src/runtime/shared/update-checker.mjs +214 -0
- package/src/session-runtime/config-helpers.mjs +209 -0
- package/src/session-runtime/effort.mjs +128 -0
- package/src/session-runtime/fs-utils.mjs +10 -0
- package/src/session-runtime/model-capabilities.mjs +130 -0
- package/src/session-runtime/output-styles.mjs +124 -0
- package/src/session-runtime/plugin-mcp.mjs +114 -0
- package/src/session-runtime/session-text.mjs +100 -0
- package/src/session-runtime/statusline-route.mjs +35 -0
- package/src/session-runtime/tool-catalog.mjs +720 -0
- package/src/session-runtime/workflow.mjs +358 -0
- package/src/standalone/agent-tool.mjs +302 -117
- package/src/standalone/channel-admin.mjs +133 -40
- package/src/standalone/channel-worker.mjs +10 -292
- package/src/standalone/explore-tool.mjs +12 -5
- package/src/standalone/hook-bus.mjs +165 -8
- package/src/standalone/memory-runtime-proxy.mjs +3 -1
- package/src/standalone/opencode-go-login.mjs +121 -0
- package/src/standalone/provider-admin.mjs +25 -3
- package/src/standalone/usage-dashboard.mjs +1 -1
- package/src/tui/App.jsx +2883 -778
- package/src/tui/components/ConfirmBar.jsx +47 -0
- package/src/tui/components/ContextPanel.jsx +5 -3
- package/src/tui/components/ItemRightHintOverprint.jsx +54 -0
- package/src/tui/components/Markdown.jsx +22 -98
- package/src/tui/components/Message.jsx +14 -35
- package/src/tui/components/Picker.jsx +87 -12
- package/src/tui/components/PromptInput.jsx +355 -22
- package/src/tui/components/QueuedCommands.jsx +1 -1
- package/src/tui/components/SlashCommandPalette.jsx +8 -5
- package/src/tui/components/Spinner.jsx +7 -7
- package/src/tui/components/StatusLine.jsx +40 -21
- package/src/tui/components/TextEntryPanel.jsx +51 -7
- package/src/tui/components/ToolExecution.jsx +183 -101
- package/src/tui/components/TurnDone.jsx +4 -4
- package/src/tui/components/UsagePanel.jsx +1 -1
- package/src/tui/components/tool-output-format.mjs +161 -23
- package/src/tui/components/tool-output-format.test.mjs +87 -0
- package/src/tui/display-width.mjs +69 -0
- package/src/tui/display-width.test.mjs +35 -0
- package/src/tui/dist/index.mjs +6731 -2333
- package/src/tui/engine/agent-envelope.mjs +296 -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/queue-helpers.mjs +161 -0
- package/src/tui/engine/session-stats.mjs +46 -0
- package/src/tui/engine/tool-call-fields.mjs +23 -0
- package/src/tui/engine/tool-result-text.mjs +126 -0
- package/src/tui/engine.mjs +569 -948
- package/src/tui/index.jsx +117 -7
- package/src/tui/input-editing.mjs +58 -8
- package/src/tui/input-editing.selection.test.mjs +75 -0
- package/src/tui/keyboard-protocol.mjs +42 -0
- package/src/tui/lib/voice-recorder.mjs +469 -0
- package/src/tui/markdown/format-token.mjs +128 -76
- package/src/tui/markdown/format-token.test.mjs +61 -19
- package/src/tui/markdown/measure-rendered-rows.mjs +85 -0
- package/src/tui/markdown/render-ansi.test.mjs +1 -1
- package/src/tui/markdown/streaming-markdown.mjs +167 -0
- package/src/tui/markdown/streaming-markdown.test.mjs +70 -0
- package/src/tui/markdown/table-layout.mjs +9 -9
- package/src/tui/paste-attachments.mjs +36 -9
- package/src/tui/paste-fix.test.mjs +119 -0
- package/src/tui/prompt-history-store.mjs +129 -0
- package/src/tui/prompt-history-store.test.mjs +52 -0
- package/src/tui/statusline-ansi-bridge.test.mjs +3 -3
- package/src/tui/theme.mjs +41 -657
- package/src/tui/themes/base.mjs +86 -0
- package/src/tui/themes/basic.mjs +85 -0
- package/src/tui/themes/catppuccin.mjs +72 -0
- package/src/tui/themes/dracula.mjs +70 -0
- package/src/tui/themes/everforest.mjs +71 -0
- package/src/tui/themes/gruvbox.mjs +71 -0
- package/src/tui/themes/index.mjs +71 -0
- package/src/tui/themes/indigo.mjs +78 -0
- package/src/tui/themes/kanagawa.mjs +80 -0
- package/src/tui/themes/light.mjs +81 -0
- package/src/tui/themes/nord.mjs +72 -0
- package/src/tui/themes/onedark.mjs +16 -0
- package/src/tui/themes/rosepine.mjs +70 -0
- package/src/tui/themes/teal.mjs +80 -0
- package/src/tui/themes/tokyonight.mjs +79 -0
- package/src/tui/themes/utils.mjs +106 -0
- package/src/tui/themes/warm.mjs +79 -0
- package/src/tui/transcript-tool-failures.mjs +13 -2
- package/src/ui/markdown.mjs +1 -1
- package/src/ui/model-display.mjs +2 -2
- package/src/ui/statusline.mjs +75 -27
- package/src/vendor/statusline/bin/statusline-route.mjs +5 -12
- package/src/vendor/statusline/src/gateway/claude-current.mjs +3 -3
- package/src/vendor/statusline/src/gateway/route-meta.mjs +30 -16
- package/src/workflows/default/WORKFLOW.md +46 -12
- package/src/workflows/sequential/WORKFLOW.md +51 -0
- package/src/workflows/solo/WORKFLOW.md +12 -1
- package/vendor/ink/build/display-width.js +62 -0
- package/vendor/ink/build/ink.js +100 -12
- package/vendor/ink/build/measure-text.js +4 -1
- package/vendor/ink/build/output.js +115 -9
- package/vendor/ink/build/render-node-to-output.js +4 -1
- package/vendor/ink/build/render.js +4 -0
- package/src/output-styles/extreme-simple.md +0 -20
- package/src/rules/lead/04-workflow.md +0 -51
- package/src/workflows/default/workflow.json +0 -13
- package/src/workflows/solo/workflow.json +0 -7
|
@@ -9,9 +9,16 @@ import {
|
|
|
9
9
|
normalizeToolNotifyContext,
|
|
10
10
|
notifyToolCompletion,
|
|
11
11
|
} from '../../../../shared/tool-execution-contract.mjs';
|
|
12
|
+
import {
|
|
13
|
+
renderShellCompletionEnvelope,
|
|
14
|
+
shellCompletionInstruction,
|
|
15
|
+
renderShellPromptStallEnvelope,
|
|
16
|
+
shellPromptStallInstruction,
|
|
17
|
+
} from '../../../../shared/task-notification-envelope.mjs';
|
|
12
18
|
import {
|
|
13
19
|
completeBackgroundTask,
|
|
14
20
|
notifyBackgroundTaskProgress,
|
|
21
|
+
getBackgroundTask,
|
|
15
22
|
} from '../../../../shared/background-tasks.mjs';
|
|
16
23
|
import { startChildGuardian } from '../../../../shared/child-guardian.mjs';
|
|
17
24
|
|
|
@@ -729,22 +736,21 @@ export function watchBackgroundShellJob(jobId, notifyCtx) {
|
|
|
729
736
|
const elapsedMs = Number.isFinite(startedAtMs) ? Math.max(0, finishedAtMs - startedAtMs) : null;
|
|
730
737
|
const exitCode = (typeof detail.exitCode === 'number') ? detail.exitCode : null;
|
|
731
738
|
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');
|
|
739
|
+
const body = renderShellCompletionEnvelope({
|
|
740
|
+
jobId,
|
|
741
|
+
status,
|
|
742
|
+
exitCode,
|
|
743
|
+
elapsedMs,
|
|
744
|
+
command: detail.command,
|
|
745
|
+
summary: detail.summary,
|
|
746
|
+
stdoutPreview: detail.stdoutPreview,
|
|
747
|
+
stderrPreview: detail.stderrPreview,
|
|
748
|
+
mergeStderr: detail.mergeStderr,
|
|
749
|
+
});
|
|
744
750
|
const taskStatus = status === 'completed'
|
|
745
751
|
? 'completed'
|
|
746
752
|
: (status === 'cancelled' ? 'cancelled' : 'failed');
|
|
747
|
-
const instruction =
|
|
753
|
+
const instruction = shellCompletionInstruction({ jobId, status, exitCode });
|
|
748
754
|
const completedTask = completeBackgroundTask(jobId, {
|
|
749
755
|
status: taskStatus,
|
|
750
756
|
result: shellJobPublicTaskResult(detail),
|
|
@@ -780,33 +786,38 @@ export function watchBackgroundShellJob(jobId, notifyCtx) {
|
|
|
780
786
|
if (now - lastOutputAtMs < SHELL_JOB_PROMPT_STALL_MS) return;
|
|
781
787
|
if (!looksLikeInteractivePrompt(tail.text)) return;
|
|
782
788
|
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',
|
|
789
|
+
const body = renderShellPromptStallEnvelope({
|
|
790
|
+
jobId,
|
|
791
|
+
stalledMs: now - lastOutputAtMs,
|
|
792
|
+
elapsedMs,
|
|
793
|
+
command: detail.command,
|
|
794
|
+
tailText: tail.text,
|
|
809
795
|
});
|
|
796
|
+
const instruction = shellPromptStallInstruction({ jobId });
|
|
797
|
+
// Prefer the progress-notify path when a background task row exists.
|
|
798
|
+
// Its once-key ('interactive-prompt-stall') dedupe returns false after
|
|
799
|
+
// a re-arm even though the row is present — do NOT fall back to
|
|
800
|
+
// notifyToolCompletion in that case (it would fire a duplicate). Only
|
|
801
|
+
// use the completion fallback when the task row is absent (progress
|
|
802
|
+
// path unavailable).
|
|
803
|
+
const sent = getBackgroundTask(jobId)
|
|
804
|
+
? notifyBackgroundTaskProgress(jobId, {
|
|
805
|
+
text: body,
|
|
806
|
+
resultType: 'shell_task_progress',
|
|
807
|
+
instruction,
|
|
808
|
+
key: 'interactive-prompt-stall',
|
|
809
|
+
status: null,
|
|
810
|
+
})
|
|
811
|
+
: notifyToolCompletion({
|
|
812
|
+
surface: 'shell',
|
|
813
|
+
id: jobId,
|
|
814
|
+
status: null,
|
|
815
|
+
text: body,
|
|
816
|
+
resultType: 'shell_task_progress',
|
|
817
|
+
instruction,
|
|
818
|
+
context: ctx,
|
|
819
|
+
logPrefix: 'shell-jobs',
|
|
820
|
+
});
|
|
810
821
|
if (sent) promptStallNotified = true;
|
|
811
822
|
};
|
|
812
823
|
const checkDone = (reason) => {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { countSplitLines } from './path-utils.mjs';
|
|
2
|
+
import { TOOL_OUTPUT_MAX_BYTES } from './tool-output-limit.mjs';
|
|
2
3
|
|
|
3
|
-
export const SHELL_OUTPUT_MAX_CHARS =
|
|
4
|
+
export const SHELL_OUTPUT_MAX_CHARS = TOOL_OUTPUT_MAX_BYTES;
|
|
4
5
|
|
|
5
6
|
export const SMART_BASH_MAX_LINES = 400;
|
|
6
|
-
export const SMART_BASH_MAX_BYTES =
|
|
7
|
+
export const SMART_BASH_MAX_BYTES = TOOL_OUTPUT_MAX_BYTES;
|
|
7
8
|
export const SMART_BASH_HEAD_LINES = 80;
|
|
8
9
|
export const SMART_BASH_TAIL_LINES = 80;
|
|
9
10
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Single source of truth for the model-facing tool-output byte budget.
|
|
2
|
+
// read / shell / list-tree all truncate their output to this cap so the
|
|
3
|
+
// context cost of any one tool call is bounded and consistent. Override with
|
|
4
|
+
// MIXDOG_TOOL_OUTPUT_MAX_BYTES. Line-based sub-caps (e.g. bash max lines) stay
|
|
5
|
+
// per-tool; this governs only the byte budget.
|
|
6
|
+
function _envInt(name, fallback) {
|
|
7
|
+
const v = parseInt(process.env[name], 10);
|
|
8
|
+
return Number.isFinite(v) && v > 0 ? v : fallback;
|
|
9
|
+
}
|
|
10
|
+
export const TOOL_OUTPUT_MAX_BYTES = _envInt('MIXDOG_TOOL_OUTPUT_MAX_BYTES', 50 * 1024);
|
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
extractPdfText,
|
|
47
47
|
} from './builtin/read-special-files.mjs';
|
|
48
48
|
import { computeUnifiedDiff } from './builtin/diff-utils.mjs';
|
|
49
|
+
import { tryExecuteExternalToolAdapter, isExternalAdapterTool } from './builtin/external-tool-adapters.mjs';
|
|
49
50
|
import { formatToolStartProgress } from './progress-message.mjs';
|
|
50
51
|
import { BUILTIN_TOOLS } from './builtin/builtin-tools.mjs';
|
|
51
52
|
import { validateBuiltinArgs } from './builtin/arg-guard.mjs';
|
|
@@ -277,6 +278,8 @@ const EXTERNAL_TOOL_REDIRECTS = new Map([
|
|
|
277
278
|
['strreplace', 'use `apply_patch` with a `*** Update File:` V4A section'],
|
|
278
279
|
['str_replace', 'use `apply_patch` with a `*** Update File:` V4A section'],
|
|
279
280
|
['str_replace_editor', 'use `apply_patch` with a `*** Update File:` V4A section'],
|
|
281
|
+
['search_replace', 'use `apply_patch` with a `*** Update File:` V4A section'],
|
|
282
|
+
['applypatch', 'call the `apply_patch` tool (snake_case) with a V4A patch'],
|
|
280
283
|
['createfile', 'use `apply_patch` with an `*** Add File:` V4A section'],
|
|
281
284
|
['create_file', 'use `apply_patch` with an `*** Add File:` V4A section'],
|
|
282
285
|
['deletefile', 'use `apply_patch` with a `*** Delete File:` V4A section'],
|
|
@@ -287,9 +290,15 @@ const EXTERNAL_TOOL_REDIRECTS = new Map([
|
|
|
287
290
|
['terminal', 'use the `shell` tool'],
|
|
288
291
|
['view', 'use the `read` tool'],
|
|
289
292
|
['cat', 'use the `read` tool'],
|
|
293
|
+
['read_file', 'use the `read` tool'],
|
|
290
294
|
['ls', 'use the `list` tool'],
|
|
295
|
+
['list_dir', 'use the `list` tool'],
|
|
291
296
|
['searchfiles', 'use the `grep` tool'],
|
|
297
|
+
['file_search', 'use the `find` or `glob` tool'],
|
|
298
|
+
['grep_search', 'use the `grep` tool'],
|
|
292
299
|
['codebase_search', 'use the `grep` or `code_graph` tool'],
|
|
300
|
+
['semanticsearch', 'use the `grep` or `code_graph` tool'],
|
|
301
|
+
['semantic_search', 'use the `grep` or `code_graph` tool'],
|
|
293
302
|
]);
|
|
294
303
|
|
|
295
304
|
export function canonicalizeBuiltinToolName(name) {
|
|
@@ -401,6 +410,55 @@ function capToolOutput(result, options = {}) {
|
|
|
401
410
|
return `${head}\n... [tool-output truncated: ${Math.round(bytes / 1024)} KB -> ${Math.round(cap / 1024)} KB cap (tool_output_token_limit)] ...\n${tail}`;
|
|
402
411
|
}
|
|
403
412
|
|
|
413
|
+
const _GREP_SWEEP_NUDGE_ENABLED = /^(1|true|yes|on)$/i.test(String(process.env.MIXDOG_ENABLE_GREP_SWEEP_NUDGE || ''));
|
|
414
|
+
const _GREP_SWEEP_MIN_CALLS = 20;
|
|
415
|
+
const _GREP_SWEEP_MIN_UNIQUE = 16;
|
|
416
|
+
const _grepSweepState = new Map();
|
|
417
|
+
|
|
418
|
+
function _grepSweepTerms(pattern) {
|
|
419
|
+
const raw = Array.isArray(pattern) ? pattern.join('|') : String(pattern || '');
|
|
420
|
+
return [...new Set(raw
|
|
421
|
+
.split(/[|,\s()"'`[\]{}.*+?^$\\:-]+/g)
|
|
422
|
+
.map((s) => s.trim().toLowerCase())
|
|
423
|
+
.filter((s) => s.length >= 4))];
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function _normalizeSweepPath(value) {
|
|
427
|
+
return String(value || '.')
|
|
428
|
+
.replace(/\\/g, '/')
|
|
429
|
+
.replace(/^([A-Za-z]):/, (_, d) => d.toLowerCase() + ':')
|
|
430
|
+
.replace(/\/+/g, '/')
|
|
431
|
+
.replace(/\/$/, '') || '.';
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function _grepSweepKey(args = {}) {
|
|
435
|
+
const path = _normalizeSweepPath(args.path || '.');
|
|
436
|
+
const glob = Array.isArray(args.glob) ? args.glob.join(',') : String(args.glob || '');
|
|
437
|
+
const terms = _grepSweepTerms(args.pattern).slice(0, 3).join('|');
|
|
438
|
+
return `${path}::${glob}::${terms}`;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function _maybeAppendGrepSweepNudge(toolName, args, result, scope) {
|
|
442
|
+
if (!_GREP_SWEEP_NUDGE_ENABLED || toolName !== 'grep' || typeof result !== 'string') return result;
|
|
443
|
+
const key = scope || 'global';
|
|
444
|
+
const now = Date.now();
|
|
445
|
+
let state = _grepSweepState.get(key);
|
|
446
|
+
if (!state || now - state.lastTs > 10 * 60_000) {
|
|
447
|
+
state = { count: 0, unique: new Set(), lastTs: 0, nudged: false };
|
|
448
|
+
_grepSweepState.set(key, state);
|
|
449
|
+
}
|
|
450
|
+
state.count += 1;
|
|
451
|
+
state.unique.add(_grepSweepKey(args));
|
|
452
|
+
state.lastTs = now;
|
|
453
|
+
if (_grepSweepState.size > 256) {
|
|
454
|
+
const oldest = [..._grepSweepState.entries()].sort((a, b) => (a[1].lastTs || 0) - (b[1].lastTs || 0)).slice(0, 32);
|
|
455
|
+
for (const [oldKey] of oldest) _grepSweepState.delete(oldKey);
|
|
456
|
+
}
|
|
457
|
+
if (state.nudged || state.count < _GREP_SWEEP_MIN_CALLS || state.unique.size < _GREP_SWEEP_MIN_UNIQUE) return result;
|
|
458
|
+
state.nudged = true;
|
|
459
|
+
return `${result}\n\n[grep_sweep_hint] Many distinct grep searches were used in this session. Stop rewording search terms; synthesize from existing anchors or switch tools once (code_graph/find/read known regions).`;
|
|
460
|
+
}
|
|
461
|
+
|
|
404
462
|
export async function executeBuiltinTool(name, args, cwd, options = {}) {
|
|
405
463
|
if (options.abortSignal && !options.signal) {
|
|
406
464
|
options = { ...options, signal: options.abortSignal };
|
|
@@ -460,10 +518,15 @@ export async function executeBuiltinTool(name, args, cwd, options = {}) {
|
|
|
460
518
|
case 'hex':
|
|
461
519
|
return executeHexTool(args, workDir, readStateScope, _readModeHelpers);
|
|
462
520
|
default:
|
|
521
|
+
{
|
|
522
|
+
const adapted = await tryExecuteExternalToolAdapter(name, args, workDir, options);
|
|
523
|
+
if (adapted !== null) return adapted;
|
|
524
|
+
}
|
|
463
525
|
return formatUnknownBuiltinToolMessage(name, args);
|
|
464
526
|
}
|
|
465
527
|
})();
|
|
466
|
-
|
|
528
|
+
const _withNudge = _maybeAppendGrepSweepNudge(toolName, args, _toolResult, readStateScope);
|
|
529
|
+
return capToolOutput(_withNudge, options);
|
|
467
530
|
}
|
|
468
531
|
/**
|
|
469
532
|
* Check if a tool name is a builtin tool.
|
|
@@ -473,6 +536,12 @@ export function isBuiltinTool(name) {
|
|
|
473
536
|
return BUILTIN_TOOLS.some(t => t.name === toolName);
|
|
474
537
|
}
|
|
475
538
|
|
|
539
|
+
// Re-export so the session loop can route foreign-CLI tool names
|
|
540
|
+
// (StrReplace/Write/bash variants) into executeBuiltinTool — whose default:
|
|
541
|
+
// case runs the native adapter — instead of short-circuiting to the
|
|
542
|
+
// unknown-tool redirect message at the dispatch layer.
|
|
543
|
+
export { isExternalAdapterTool };
|
|
544
|
+
|
|
476
545
|
// Test-only exports for smart truncation helpers (see
|
|
477
546
|
// scripts/test-smart-truncation.mjs). Runtime callers inside this module
|
|
478
547
|
// use the local bindings unchanged; these named exports just make the
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
// Graph build orchestration: worker-thread async build (buildCodeGraphAsync),
|
|
2
|
+
// synchronous _buildCodeGraph (imported by the prewarm worker), and the
|
|
3
|
+
// prewarm entry points. Owns the in-flight single-flight map. Extracted
|
|
4
|
+
// verbatim from code-graph.mjs.
|
|
5
|
+
//
|
|
6
|
+
// SELF-REFERENTIAL DYNAMIC IMPORT: buildCodeGraphAsync spawns the prewarm
|
|
7
|
+
// worker via `new URL('../code-graph-prewarm-worker.mjs', import.meta.url)`.
|
|
8
|
+
// That worker in turn does `import { _buildCodeGraph } from '../code-graph.mjs'`
|
|
9
|
+
// (the facade), which re-exports _buildCodeGraph from THIS module. The worker
|
|
10
|
+
// path is unchanged relative to the facade, so it keeps importing the facade —
|
|
11
|
+
// no module imports its own path. The URL below is resolved against THIS
|
|
12
|
+
// module's dir (tools/code-graph/), so it walks one level up to reach the
|
|
13
|
+
// worker at tools/.
|
|
14
|
+
import { resolve as pathResolve } from 'node:path';
|
|
15
|
+
import { Worker } from 'node:worker_threads';
|
|
16
|
+
import { acquire as acquireChildSpawnSlot } from '../../../../shared/child-spawn-gate.mjs';
|
|
17
|
+
import {
|
|
18
|
+
canonicalGraphCwd as _canonicalGraphCwd,
|
|
19
|
+
codeGraphCache as _codeGraphCache,
|
|
20
|
+
consumeCodeGraphDirtyPaths as _consumeCodeGraphDirtyPaths,
|
|
21
|
+
getCodeGraphGen as _getCodeGraphGen,
|
|
22
|
+
} from '../code-graph-state.mjs';
|
|
23
|
+
import {
|
|
24
|
+
CODE_GRAPH_TTL_MS,
|
|
25
|
+
CODE_GRAPH_MAX_FILES,
|
|
26
|
+
CODE_GRAPH_WORKER_TIMEOUT_MS,
|
|
27
|
+
SYMBOL_SCHEMA_VERSION,
|
|
28
|
+
} from './constants.mjs';
|
|
29
|
+
import {
|
|
30
|
+
_computeGraphSignature,
|
|
31
|
+
_deserializeGraph,
|
|
32
|
+
_attachGraphRuntimeCaches,
|
|
33
|
+
} from './graph-model.mjs';
|
|
34
|
+
import { _touchCodeGraphCache, _setCodeGraphCache } from './memory-cache.mjs';
|
|
35
|
+
import {
|
|
36
|
+
ensureDiskCodeGraphLoaded,
|
|
37
|
+
getDiskCodeGraphEntry,
|
|
38
|
+
_setDiskCodeGraphEntry,
|
|
39
|
+
} from './disk-cache.mjs';
|
|
40
|
+
import {
|
|
41
|
+
_runGraphManifest,
|
|
42
|
+
_runGraphWalk,
|
|
43
|
+
_runGraphFiles,
|
|
44
|
+
_fileInfoFromRustRecord,
|
|
45
|
+
_reuseFileInfo,
|
|
46
|
+
} from './graph-binary.mjs';
|
|
47
|
+
import { _lookupCandidateNodes } from './symbol-index.mjs';
|
|
48
|
+
import { _findDirProjectRoot } from './project-root.mjs';
|
|
49
|
+
|
|
50
|
+
// In-flight async builds keyed by canonical graphCwd. Same-cwd parallel
|
|
51
|
+
// callers (prewarm + cache-miss + multiple find_symbol) share one Worker
|
|
52
|
+
// spawn instead of fanning out. Entry removed on settle so the next caller
|
|
53
|
+
// after a failure can retry.
|
|
54
|
+
const _inflightAsyncBuilds = new Map();
|
|
55
|
+
|
|
56
|
+
export function prewarmCodeGraph(cwd) {
|
|
57
|
+
if (!cwd) return;
|
|
58
|
+
// Reuse the buildCodeGraphAsync single-flight path. Fire-and-forget.
|
|
59
|
+
buildCodeGraphAsync(cwd).catch(() => { /* best-effort */ });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function prewarmCodeGraphSymbols(cwd, symbols, { language = null } = {}) {
|
|
63
|
+
if (!cwd) return;
|
|
64
|
+
const wanted = (Array.isArray(symbols) ? symbols : [symbols])
|
|
65
|
+
.map((s) => String(s || '').trim())
|
|
66
|
+
.filter(Boolean);
|
|
67
|
+
buildCodeGraphAsync(cwd).then((graph) => {
|
|
68
|
+
if (!graph) return;
|
|
69
|
+
for (const symbol of wanted) {
|
|
70
|
+
try { _lookupCandidateNodes(graph, symbol, language); } catch { /* best-effort */ }
|
|
71
|
+
}
|
|
72
|
+
}).catch(() => { /* best-effort */ });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function prewarmCodeGraphIfProject(cwd) {
|
|
76
|
+
if (!cwd) return false;
|
|
77
|
+
const root = _findDirProjectRoot(cwd);
|
|
78
|
+
if (!root) return false;
|
|
79
|
+
prewarmCodeGraph(root);
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function buildCodeGraphAsync(cwd, signal = null) {
|
|
84
|
+
if (signal?.aborted) throw new Error('aborted');
|
|
85
|
+
const graphCwd = _canonicalGraphCwd(cwd);
|
|
86
|
+
const cached = _codeGraphCache.get(graphCwd);
|
|
87
|
+
if (cached?.graph && Date.now() - cached.ts < CODE_GRAPH_TTL_MS) {
|
|
88
|
+
_touchCodeGraphCache(graphCwd);
|
|
89
|
+
return cached.graph;
|
|
90
|
+
}
|
|
91
|
+
const existing = _inflightAsyncBuilds.get(graphCwd);
|
|
92
|
+
if (existing) {
|
|
93
|
+
if (!signal) return existing;
|
|
94
|
+
let onAbort = null;
|
|
95
|
+
const abortP = new Promise((_, reject) => {
|
|
96
|
+
onAbort = () => reject(new Error('aborted'));
|
|
97
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
98
|
+
});
|
|
99
|
+
const cleanup = () => {
|
|
100
|
+
if (onAbort) {
|
|
101
|
+
try { signal.removeEventListener('abort', onAbort); } catch {}
|
|
102
|
+
onAbort = null;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
return Promise.race([existing, abortP]).then(
|
|
106
|
+
(v) => { cleanup(); return v; },
|
|
107
|
+
(e) => { cleanup(); throw e; },
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
const _genAtStart = _getCodeGraphGen(graphCwd);
|
|
111
|
+
let _worker = null;
|
|
112
|
+
const promise = new Promise((resolve, reject) => {
|
|
113
|
+
let settled = false;
|
|
114
|
+
let timeout = null;
|
|
115
|
+
let _onSignalAbort = null;
|
|
116
|
+
let _releaseSlot = null;
|
|
117
|
+
const settle = (val) => {
|
|
118
|
+
if (settled) return;
|
|
119
|
+
settled = true;
|
|
120
|
+
if (timeout) { clearTimeout(timeout); timeout = null; }
|
|
121
|
+
if (_onSignalAbort && signal) {
|
|
122
|
+
try { signal.removeEventListener('abort', _onSignalAbort); } catch {}
|
|
123
|
+
_onSignalAbort = null;
|
|
124
|
+
}
|
|
125
|
+
if (_releaseSlot) { try { _releaseSlot(); } catch {} _releaseSlot = null; }
|
|
126
|
+
_inflightAsyncBuilds.delete(graphCwd);
|
|
127
|
+
if (val instanceof Error) reject(val);
|
|
128
|
+
else resolve(val);
|
|
129
|
+
};
|
|
130
|
+
acquireChildSpawnSlot(signal || null).then((release) => {
|
|
131
|
+
_releaseSlot = release;
|
|
132
|
+
if (settled) { release(); _releaseSlot = null; return; }
|
|
133
|
+
const workerUrl = new URL('../code-graph-prewarm-worker.mjs', import.meta.url);
|
|
134
|
+
_worker = new Worker(workerUrl, {
|
|
135
|
+
workerData: { cwd },
|
|
136
|
+
execArgv: [],
|
|
137
|
+
});
|
|
138
|
+
const w = _worker;
|
|
139
|
+
timeout = setTimeout(() => {
|
|
140
|
+
try { _worker?.terminate(); } catch {}
|
|
141
|
+
settle(new Error(`code-graph worker timed out after ${CODE_GRAPH_WORKER_TIMEOUT_MS}ms for cwd=${graphCwd}`));
|
|
142
|
+
}, CODE_GRAPH_WORKER_TIMEOUT_MS);
|
|
143
|
+
timeout.unref?.();
|
|
144
|
+
if (signal) {
|
|
145
|
+
_onSignalAbort = () => {
|
|
146
|
+
try { _worker?.terminate(); } catch {}
|
|
147
|
+
settle(new Error('aborted'));
|
|
148
|
+
};
|
|
149
|
+
signal.addEventListener('abort', _onSignalAbort, { once: true });
|
|
150
|
+
}
|
|
151
|
+
w.once('message', (msg) => {
|
|
152
|
+
try {
|
|
153
|
+
if (msg && msg.ok && msg.graph && typeof msg.signature === 'string') {
|
|
154
|
+
if (_getCodeGraphGen(graphCwd) === _genAtStart) {
|
|
155
|
+
_setCodeGraphCache(graphCwd, { ts: Date.now(), signature: msg.signature, graph: msg.graph });
|
|
156
|
+
_setDiskCodeGraphEntry(graphCwd, msg.graph);
|
|
157
|
+
}
|
|
158
|
+
settle(msg.graph);
|
|
159
|
+
} else {
|
|
160
|
+
settle(new Error('code-graph prewarm worker failed'));
|
|
161
|
+
}
|
|
162
|
+
} catch (e) { settle(e instanceof Error ? e : new Error(String(e))); }
|
|
163
|
+
});
|
|
164
|
+
w.once('error', (e) => settle(e instanceof Error ? e : new Error(String(e))));
|
|
165
|
+
}, (e) => settle(e instanceof Error ? e : new Error(String(e))));
|
|
166
|
+
});
|
|
167
|
+
_inflightAsyncBuilds.set(graphCwd, promise);
|
|
168
|
+
return promise;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Internal — exported (via the facade) solely for code-graph-prewarm-worker.mjs
|
|
173
|
+
* to import. NOT part of the public API. External callers should use
|
|
174
|
+
* buildCodeGraphAsync (worker-thread isolated) or the code_graph / find_symbol
|
|
175
|
+
* tools, never this synchronous form on the main event loop.
|
|
176
|
+
*/
|
|
177
|
+
export async function _buildCodeGraph(cwd) {
|
|
178
|
+
const now = Date.now();
|
|
179
|
+
let _tp = performance.now();
|
|
180
|
+
const _trace = (label) => { if (process.env.MIXDOG_GRAPH_TRACE) { const n = performance.now(); process.stderr.write(`[cg-trace] ${label}=${(n - _tp).toFixed(0)}ms\n`); _tp = n; } };
|
|
181
|
+
const graphCwd = _canonicalGraphCwd(cwd);
|
|
182
|
+
const absRoot = graphCwd;
|
|
183
|
+
const _genAtStart = _getCodeGraphGen(graphCwd);
|
|
184
|
+
const cached = _codeGraphCache.get(graphCwd);
|
|
185
|
+
let previousGraph = cached?.graph || null;
|
|
186
|
+
_consumeCodeGraphDirtyPaths(graphCwd);
|
|
187
|
+
|
|
188
|
+
// 1. Change-detect via Rust --manifest (fp/rel/size only, no parse).
|
|
189
|
+
const manifest = await _runGraphManifest(absRoot);
|
|
190
|
+
const signature = _computeGraphSignature(manifest);
|
|
191
|
+
_trace('manifest+sig');
|
|
192
|
+
const truncated = manifest.length > CODE_GRAPH_MAX_FILES;
|
|
193
|
+
const indexed = truncated ? manifest.slice(0, CODE_GRAPH_MAX_FILES) : manifest;
|
|
194
|
+
|
|
195
|
+
// 2. Memory cache hit.
|
|
196
|
+
if (cached && cached.signature === signature && now - cached.ts < CODE_GRAPH_TTL_MS) {
|
|
197
|
+
_touchCodeGraphCache(graphCwd);
|
|
198
|
+
return cached.graph;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// 3. Disk cache hit.
|
|
202
|
+
ensureDiskCodeGraphLoaded(now);
|
|
203
|
+
const diskEntry = getDiskCodeGraphEntry(graphCwd);
|
|
204
|
+
if (diskEntry?.signature === signature) {
|
|
205
|
+
const graph = _deserializeGraph(graphCwd, diskEntry);
|
|
206
|
+
if (graph) {
|
|
207
|
+
if (_getCodeGraphGen(graphCwd) === _genAtStart) {
|
|
208
|
+
_setCodeGraphCache(graphCwd, { ts: now, signature, graph });
|
|
209
|
+
}
|
|
210
|
+
return graph;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (!previousGraph && diskEntry) previousGraph = _deserializeGraph(graphCwd, diskEntry);
|
|
214
|
+
if (previousGraph && previousGraph.schemaVersion !== SYMBOL_SCHEMA_VERSION) {
|
|
215
|
+
previousGraph = null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// 4. Build fileInfos. Reuse unchanged nodes by fp; parse the rest in Rust.
|
|
219
|
+
const reusable = [];
|
|
220
|
+
const freshRels = [];
|
|
221
|
+
for (const meta of indexed) {
|
|
222
|
+
const previousNode = previousGraph?.nodes?.get(meta.rel) || null;
|
|
223
|
+
if (previousNode && previousNode.fingerprint === meta.fp) {
|
|
224
|
+
reusable.push(_reuseFileInfo(previousNode, previousGraph, absRoot));
|
|
225
|
+
} else {
|
|
226
|
+
freshRels.push(meta.rel);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
let fileInfos;
|
|
230
|
+
if (freshRels.length === 0) {
|
|
231
|
+
fileInfos = reusable;
|
|
232
|
+
} else if (reusable.length > 0 && freshRels.length <= 256) {
|
|
233
|
+
const recs = await _runGraphFiles(absRoot, freshRels, reusable);
|
|
234
|
+
const reusedByRel = new Map(reusable.map((info) => [info.rel, info]));
|
|
235
|
+
const freshSet = new Set(freshRels);
|
|
236
|
+
fileInfos = [...reusable];
|
|
237
|
+
for (const rec of recs) {
|
|
238
|
+
if (freshSet.has(rec.rel)) {
|
|
239
|
+
fileInfos.push(_fileInfoFromRustRecord(rec, absRoot));
|
|
240
|
+
} else {
|
|
241
|
+
const reusedInfo = reusedByRel.get(rec.rel);
|
|
242
|
+
if (!reusedInfo) continue;
|
|
243
|
+
const resolved = Array.isArray(rec.resolvedImports)
|
|
244
|
+
? rec.resolvedImports.filter((v) => typeof v === 'string')
|
|
245
|
+
: [];
|
|
246
|
+
reusedInfo.resolvedImports = resolved;
|
|
247
|
+
if (Array.isArray(rec.importedBy)) {
|
|
248
|
+
reusedInfo.importedBy = rec.importedBy.filter((v) => typeof v === 'string');
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
} else {
|
|
253
|
+
let recs = await _runGraphWalk(absRoot);
|
|
254
|
+
if (recs.length > CODE_GRAPH_MAX_FILES) recs = recs.slice(0, CODE_GRAPH_MAX_FILES);
|
|
255
|
+
fileInfos = recs.map((rec) => _fileInfoFromRustRecord(rec, absRoot));
|
|
256
|
+
}
|
|
257
|
+
_trace('walk+parse');
|
|
258
|
+
const nodes = new Map();
|
|
259
|
+
const reverse = new Map();
|
|
260
|
+
for (const info of fileInfos) {
|
|
261
|
+
const resolvedImportsRel = Array.isArray(info.resolvedImports) ? info.resolvedImports : [];
|
|
262
|
+
const importedBy = Array.isArray(info.importedBy) ? info.importedBy : [];
|
|
263
|
+
const node = {
|
|
264
|
+
abs: info.abs,
|
|
265
|
+
rel: info.rel,
|
|
266
|
+
lang: info.lang,
|
|
267
|
+
fingerprint: info.fingerprint,
|
|
268
|
+
rawImports: info.rawImports,
|
|
269
|
+
resolvedImportsRel,
|
|
270
|
+
resolvedImports: resolvedImportsRel.map((rel) => pathResolve(absRoot, rel)),
|
|
271
|
+
importedBy,
|
|
272
|
+
packageName: info.packageName,
|
|
273
|
+
namespaceName: info.namespaceName,
|
|
274
|
+
goPackageName: info.goPackageName,
|
|
275
|
+
topLevelTypes: info.topLevelTypes,
|
|
276
|
+
tokenSymbols: info.tokenSymbols,
|
|
277
|
+
symbols: Array.isArray(info.symbols) ? info.symbols : [],
|
|
278
|
+
};
|
|
279
|
+
nodes.set(info.rel, node);
|
|
280
|
+
for (const rel of resolvedImportsRel) {
|
|
281
|
+
if (!reverse.has(rel)) reverse.set(rel, new Set());
|
|
282
|
+
reverse.get(rel).add(node.rel);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
_trace('assemble');
|
|
286
|
+
const graph = _attachGraphRuntimeCaches({ cwd: graphCwd, nodes, reverse, schemaVersion: SYMBOL_SCHEMA_VERSION, builtAt: now, signature });
|
|
287
|
+
graph.truncated = Boolean(truncated);
|
|
288
|
+
for (const info of fileInfos) {
|
|
289
|
+
if (typeof info.sourceText === 'string') {
|
|
290
|
+
graph._sourceTextCache.set(info.rel, {
|
|
291
|
+
fingerprint: info.fingerprint || '',
|
|
292
|
+
text: info.sourceText,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
graph._symbolTokenIndexDirty = true;
|
|
297
|
+
if (_getCodeGraphGen(graphCwd) === _genAtStart) {
|
|
298
|
+
_setCodeGraphCache(graphCwd, { ts: now, signature, graph });
|
|
299
|
+
_setDiskCodeGraphEntry(graphCwd, graph);
|
|
300
|
+
_trace('cache+disk');
|
|
301
|
+
}
|
|
302
|
+
return graph;
|
|
303
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Code-graph tuning constants + cache-file name/regex patterns. Extracted
|
|
2
|
+
// verbatim from code-graph.mjs so the build/cache/search modules can share
|
|
3
|
+
// one source of truth. Behavior-identical: same env vars, same defaults.
|
|
4
|
+
|
|
5
|
+
export const CODE_GRAPH_TTL_MS = 30_000;
|
|
6
|
+
export const CODE_GRAPH_MAX_FILES = 10_000;
|
|
7
|
+
export const CODE_GRAPH_WORKER_TIMEOUT_MS = 120_000;
|
|
8
|
+
// Timeout for the native mixdog-graph binary child process (spawned per graph build).
|
|
9
|
+
export const CODE_GRAPH_BINARY_TIMEOUT_MS = Math.max(1000, Number(process.env.MIXDOG_CODE_GRAPH_BINARY_TIMEOUT_MS) || 20000);
|
|
10
|
+
// Legacy single-file cache. Kept as a constant for the one-shot migration
|
|
11
|
+
// path; new writes go into the per-cwd directory layout below.
|
|
12
|
+
export const CODE_GRAPH_DISK_FILE = 'code-graph-cache.json';
|
|
13
|
+
// Per-cwd cache: <data>/code-graph-cache/manifest.json + <hash>.json per
|
|
14
|
+
// indexed root. Avoids the unbounded single-file blob (observed >50 MB on
|
|
15
|
+
// long-running workspaces) that had to be JSON.parsed in full on every
|
|
16
|
+
// fresh process startup.
|
|
17
|
+
export const CODE_GRAPH_DISK_DIR = 'code-graph-cache';
|
|
18
|
+
export const CODE_GRAPH_DISK_MAX_ENTRIES = 24;
|
|
19
|
+
export const CODE_GRAPH_DISK_MAX_BYTES = Math.max(
|
|
20
|
+
1 * 1024 * 1024,
|
|
21
|
+
Math.floor((Number(process.env.MIXDOG_CODE_GRAPH_CACHE_MAX_MB) || 80) * 1024 * 1024),
|
|
22
|
+
);
|
|
23
|
+
// Reap writeFileAtomicSync debris only after this age (see _sweepCodeGraphCacheDir).
|
|
24
|
+
// Younger .tmp files may belong to an in-flight persist still holding the sibling .lock;
|
|
25
|
+
// DEFAULT_LOCK_TIMEOUT_MS is 8s — 120s is a safe margin for large graph JSON writes.
|
|
26
|
+
export const ORPHAN_TMP_MIN_AGE_MS = 120_000;
|
|
27
|
+
export const RE_CACHE_TMP = /^\.[0-9a-f]{16}\.json\.[0-9a-f]{24}\.tmp$/i;
|
|
28
|
+
export const RE_MANIFEST_TMP = /^\.manifest\.json\.[0-9a-f]{24}\.tmp$/i;
|
|
29
|
+
export const RE_CACHE_LOCK = /^[0-9a-f]{16}\.json\.lock$/i;
|
|
30
|
+
export const CODE_GRAPH_MEMORY_MAX_ENTRIES = Math.max(
|
|
31
|
+
1,
|
|
32
|
+
Math.floor(Number(process.env.MIXDOG_CODE_GRAPH_MEMORY_MAX_ENTRIES) || 6),
|
|
33
|
+
);
|
|
34
|
+
export const CODE_GRAPH_MEMORY_MAX_SOURCE_BYTES = Math.max(
|
|
35
|
+
1 * 1024 * 1024,
|
|
36
|
+
Math.floor((Number(process.env.MIXDOG_CODE_GRAPH_MEMORY_MAX_MB) || 48) * 1024 * 1024),
|
|
37
|
+
);
|
|
38
|
+
// Bump when the per-symbol record SHAPE changes (e.g. adding endLine). The
|
|
39
|
+
// version is folded into the cache signature so graphs built by an older
|
|
40
|
+
// binary/schema (symbols without a finite endLine) no longer match and are
|
|
41
|
+
// rebuilt instead of served — otherwise a stale cache would feed endLine-less
|
|
42
|
+
// symbols and silently defeat body-span containment in _nearestEnclosingSymbol.
|
|
43
|
+
export const SYMBOL_SCHEMA_VERSION = 'sym-range-v3-rustimports';
|