mixdog 0.9.95 → 0.9.97
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/LICENSES/Apache-2.0.txt +201 -0
- package/LICENSES/MIT.txt +56 -0
- package/LICENSES/codex-NOTICE.txt +6 -0
- package/NOTICE.md +46 -9
- package/README.md +0 -17
- package/package.json +16 -12
- package/scripts/.tmp-coverage-report.mjs +96 -0
- package/scripts/build-tui.mjs +17 -1
- package/scripts/{channel-daemon-stub.mjs → daemon-stub.mjs} +18 -12
- package/scripts/fixtures/patch-replay-corpus.json +98 -0
- package/src/defaults/mixdog-config.template.json +5 -8
- package/src/defaults/skills/setup/SKILL.md +1 -1
- package/src/lib/keychain-cjs.cjs +1 -1
- package/src/lib/mixdog-debug.cjs +2 -3
- package/src/rules/agent/30-explorer.md +7 -4
- package/src/rules/lead/01-general.md +1 -2
- package/src/rules/lead/lead-tool.md +0 -3
- package/src/rules/shared/01-tool.md +21 -29
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +18 -2
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +31 -1
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +6 -17
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +12 -1
- package/src/runtime/agent/orchestrator/agent-trace-io.mjs +4 -13
- package/src/runtime/agent/orchestrator/config.mjs +246 -67
- package/src/runtime/agent/orchestrator/mcp/client.mjs +104 -41
- package/src/runtime/agent/orchestrator/mcp/reconnect-singleflight.mjs +23 -0
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +113 -14
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +5 -1
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +32 -11
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +89 -2
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +12 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +19 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +144 -39
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +22 -8
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +15 -1
- package/src/runtime/agent/orchestrator/providers/openai-transport-policy.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +3 -4
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +3 -6
- package/src/runtime/agent/orchestrator/providers/registry.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +67 -7
- package/src/runtime/agent/orchestrator/providers/stream-json-pool.mjs +291 -0
- package/src/runtime/agent/orchestrator/providers/stream-json-worker.mjs +21 -0
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +36 -10
- package/src/runtime/agent/orchestrator/session/compact.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +22 -33
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +13 -0
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +0 -24
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +21 -29
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +7 -10
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +45 -25
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +2 -3
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +142 -84
- package/src/runtime/agent/orchestrator/session/manager/prefetch-bridge.mjs +12 -6
- package/src/runtime/agent/orchestrator/session/manager/runtime-loaders.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +31 -1
- package/src/runtime/agent/orchestrator/session/manager/session-lock.mjs +38 -2
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +29 -9
- package/src/runtime/agent/orchestrator/session/manager.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/result-classification.mjs +65 -0
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +86 -8
- package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +10 -3
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +71 -42
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +4 -33
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +4 -5
- package/src/runtime/agent/orchestrator/stall-policy.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/bash-session.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +35 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +116 -26
- package/src/runtime/agent/orchestrator/tools/builtin/binary-file.mjs +92 -19
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +8 -9
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/device-paths.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/fs-reachability.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-context-expander.mjs +6 -15
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +12 -26
- package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +83 -33
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +42 -45
- package/src/runtime/agent/orchestrator/tools/builtin/rg-runner.mjs +27 -9
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +7 -0
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +17 -39
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +73 -0
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-paths.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/shell-job-spawn.mjs +0 -2
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +3 -8
- package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +30 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +142 -26
- package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +47 -19
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +3 -2
- package/src/runtime/agent/orchestrator/tools/env-scrub.mjs +1 -2
- package/src/runtime/agent/orchestrator/tools/lib/pwsh-standby-pool.mjs +83 -13
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +39 -1
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +94 -20
- package/src/runtime/agent/orchestrator/tools/patch/parsing.mjs +16 -0
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +108 -12
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +12 -11
- package/src/runtime/agent/orchestrator/tools/patch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +63 -9
- package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +2 -2
- package/src/runtime/channels/lib/config.mjs +34 -34
- package/src/runtime/channels/lib/inbound-handler.mjs +8 -8
- package/src/runtime/channels/lib/interaction-handlers.mjs +6 -6
- package/src/runtime/channels/lib/memory-client.mjs +31 -157
- package/src/runtime/channels/lib/output-forwarder.mjs +16 -16
- package/src/runtime/channels/lib/owned-runtime.mjs +60 -60
- package/src/runtime/channels/lib/owner-heartbeat.mjs +1 -1
- package/src/runtime/channels/lib/{backend-dispatch.mjs → provider-dispatch.mjs} +6 -6
- package/src/runtime/channels/lib/runtime-paths.mjs +4 -30
- package/src/runtime/channels/lib/scheduler.mjs +2 -2
- package/src/runtime/channels/lib/status-snapshot.mjs +3 -3
- package/src/runtime/channels/lib/tool-dispatch.mjs +4 -4
- package/src/runtime/channels/lib/worker-bootstrap.mjs +1 -1
- package/src/runtime/channels/lib/worker-ipc.mjs +7 -7
- package/src/runtime/channels/lib/worker-main.mjs +38 -38
- package/src/runtime/channels/{backends → providers}/discord-access.mjs +1 -1
- package/src/runtime/channels/{backends → providers}/discord-gateway.mjs +1 -1
- package/src/runtime/channels/{backends → providers}/discord.mjs +3 -3
- package/src/runtime/channels/{backends → providers}/telegram.mjs +12 -12
- package/src/runtime/channels/tool-defs.mjs +1 -1
- package/src/runtime/memory/index.mjs +76 -248
- package/src/runtime/memory/lib/agent-ipc.mjs +146 -81
- package/src/runtime/memory/lib/compact-vector-cache.mjs +88 -0
- package/src/runtime/memory/lib/core-memory-store.mjs +18 -1
- package/src/runtime/memory/lib/cycle-llm-adapters.mjs +13 -34
- package/src/runtime/memory/lib/embedding-provider.mjs +47 -26
- package/src/runtime/memory/lib/embedding-worker.mjs +30 -34
- package/src/runtime/memory/lib/http-router.mjs +9 -6
- package/src/runtime/memory/lib/ko-morph.mjs +3 -20
- package/src/runtime/memory/lib/memory-action-handlers.mjs +2 -1
- package/src/runtime/memory/lib/memory-config-flags.mjs +1 -1
- package/src/runtime/memory/lib/memory-cycle1.mjs +1 -0
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +4 -2
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +1 -0
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +4 -1
- package/src/runtime/memory/lib/memory-cycle3.mjs +3 -1
- package/src/runtime/memory/lib/memory-daemon-lifecycle.mjs +5 -0
- package/src/runtime/memory/lib/memory-log.mjs +1 -1
- package/src/runtime/memory/lib/memory-process-lock.mjs +5 -60
- package/src/runtime/memory/lib/memory.mjs +1 -1
- package/src/runtime/memory/lib/pg/process.mjs +32 -9
- package/src/runtime/memory/lib/session-ingest.mjs +1 -1
- package/src/runtime/memory/tool-defs.mjs +1 -5
- package/src/runtime/search/index.mjs +2 -2
- package/src/runtime/search/tool-defs.mjs +2 -2
- package/src/runtime/shared/abort-race.mjs +70 -0
- package/src/runtime/shared/agent-route-config.mjs +119 -0
- package/src/runtime/shared/atomic-file.mjs +80 -4
- package/src/runtime/shared/channel-notification-routing.mjs +2 -2
- package/src/runtime/shared/child-guardian.mjs +204 -108
- package/src/runtime/shared/child-spawn-gate.mjs +139 -72
- package/src/runtime/shared/config.mjs +120 -47
- package/src/runtime/shared/llm/index.mjs +1 -1
- package/src/runtime/shared/owner-fair-gate.mjs +134 -0
- package/src/runtime/shared/resource-admission.mjs +150 -100
- package/src/runtime/shared/stream-progress.mjs +6 -0
- package/src/runtime/shared/tool-execution-owner.mjs +12 -0
- package/src/runtime/shared/tool-result-summary.mjs +1 -1
- package/src/runtime/shared/tool-surface.mjs +91 -27
- package/src/runtime/shared/tool-workload-gates.mjs +29 -0
- package/src/runtime/shared/turn-snapshot.mjs +47 -6
- package/src/runtime/shared/turn-worktree-snapshot.mjs +14 -2
- package/src/session-runtime/channel-config-api.mjs +5 -5
- package/src/session-runtime/config-helpers.mjs +39 -37
- package/src/session-runtime/config-lifecycle.mjs +27 -27
- package/src/session-runtime/lifecycle-api.mjs +22 -3
- package/src/session-runtime/memory-runtime-prewarm.mjs +15 -0
- package/src/session-runtime/model-route-api.mjs +7 -5
- package/src/session-runtime/prewarm.mjs +1 -1
- package/src/session-runtime/provider-auth-api.mjs +0 -7
- package/src/session-runtime/provider-models.mjs +1 -1
- package/src/session-runtime/remote-control.mjs +6 -7
- package/src/session-runtime/runtime-core.mjs +63 -39
- package/src/session-runtime/runtime-tunables.mjs +4 -4
- package/src/session-runtime/self-update.mjs +1 -1
- package/src/session-runtime/session-lifecycle.mjs +45 -17
- package/src/session-runtime/session-title.mjs +1 -1
- package/src/session-runtime/session-turn-api.mjs +96 -24
- package/src/session-runtime/settings-api.mjs +12 -24
- package/src/session-runtime/tool-defs.mjs +3 -3
- package/src/session-runtime/workflow-agents-api.mjs +55 -81
- package/src/session-runtime/workflow.mjs +14 -24
- package/src/standalone/agent-dispatch-broker.mjs +198 -0
- package/src/standalone/agent-tool/helpers.mjs +2 -2
- package/src/standalone/agent-tool/spawn-flow.mjs +2 -0
- package/src/standalone/agent-tool/spawn-preset.mjs +11 -21
- package/src/standalone/agent-tool/tool-def.mjs +0 -14
- package/src/standalone/channel-admin.mjs +53 -64
- package/src/standalone/{channel-daemon-client.mjs → channel-client.mjs} +33 -19
- package/src/standalone/{channel-daemon-transport.mjs → channel-transport.mjs} +127 -46
- package/src/standalone/channel-worker.mjs +124 -647
- package/src/standalone/{backend-daemon.mjs → daemon.mjs} +270 -154
- package/src/standalone/explore-tool.mjs +187 -70
- package/src/standalone/fair-call-scheduler.mjs +264 -0
- package/src/standalone/memory-runtime-proxy.mjs +58 -1
- package/src/standalone/session-client.mjs +1134 -0
- package/src/standalone/session-protocol.mjs +192 -0
- package/src/standalone/session-runtime-pool.mjs +393 -0
- package/src/standalone/session-runtime-record.mjs +20 -0
- package/src/standalone/session-runtime-worker.mjs +203 -0
- package/src/standalone/{engine-daemon-service.mjs → session-service.mjs} +481 -224
- package/src/standalone/{engine-daemon-transport.mjs → session-transport.mjs} +390 -121
- package/src/standalone/session-wire.mjs +55 -0
- package/src/tui/App.jsx +34 -21
- package/src/tui/app/app-format.mjs +1 -1
- package/src/tui/app/app-view.jsx +5 -2
- package/src/tui/app/channel-pickers.mjs +41 -41
- package/src/tui/app/core-memory-picker.mjs +19 -19
- package/src/tui/app/doctor.mjs +5 -11
- package/src/tui/app/input-parsers.mjs +2 -2
- package/src/tui/app/message-selector.mjs +103 -0
- package/src/tui/app/onboarding-steps.mjs +3 -4
- package/src/tui/app/project-picker.mjs +80 -56
- package/src/tui/app/prompt-submit.mjs +48 -43
- package/src/tui/app/resume-picker.mjs +2 -2
- package/src/tui/app/route-pickers.mjs +3 -1
- package/src/tui/app/settings-picker.mjs +31 -33
- package/src/tui/app/transcript-window.mjs +38 -0
- package/src/tui/app/use-prompt-draft-flow.mjs +5 -7
- package/src/tui/app/use-prompt-handlers.mjs +86 -21
- package/src/tui/app/use-prompt-hint.mjs +3 -2
- package/src/tui/app/use-prompt-queue-history.mjs +10 -3
- package/src/tui/app/use-transcript-scroll.mjs +10 -3
- package/src/tui/app/use-transcript-window.mjs +50 -17
- package/src/tui/components/PromptInput.jsx +71 -38
- package/src/tui/components/Spinner.jsx +1 -1
- package/src/tui/components/ToolExecution.jsx +1 -1
- package/src/tui/components/prompt-input/edit-helpers.mjs +9 -11
- package/src/tui/components/prompt-input/escape-policy.mjs +42 -0
- package/src/tui/components/prompt-input/immediate-render.mjs +0 -10
- package/src/tui/components/prompt-input/interrupt-policy.mjs +10 -0
- package/src/tui/components/prompt-input/restore-policy.mjs +10 -0
- package/src/tui/components/tool-execution/surface-detail.mjs +1 -1
- package/src/tui/dist/index.mjs +1031 -1676
- package/src/tui/hooks/useSession.mjs +16 -0
- package/src/tui/index.jsx +12 -13
- package/src/tui/{engine → session}/agent-envelope.mjs +3 -3
- package/src/tui/{engine → session}/agent-job-feed.mjs +4 -4
- package/src/tui/{engine → session}/boot-profile.mjs +2 -2
- package/src/tui/{engine → session}/context-state.mjs +3 -3
- package/src/tui/{engine → session}/labels.mjs +2 -2
- package/src/tui/{engine → session}/live-share.mjs +38 -4
- package/src/tui/{engine → session}/notice-text.mjs +2 -2
- package/src/tui/{engine → session}/notification-plan.mjs +3 -3
- package/src/tui/session/oauth-flows.mjs +140 -0
- package/src/tui/{engine → session}/prompt-history.mjs +3 -3
- package/src/tui/{engine → session}/queue-helpers.mjs +2 -2
- package/src/tui/{engine → session}/render-timing.mjs +2 -2
- package/src/tui/{engine → session}/session-api-ext.mjs +30 -20
- package/src/tui/{engine → session}/session-api.mjs +110 -93
- package/src/tui/{engine → session}/session-flow.mjs +8 -2
- package/src/tui/{engine → session}/session-stats.mjs +3 -3
- package/src/tui/{engine → session}/tool-approval.mjs +3 -3
- package/src/tui/{engine → session}/tool-call-fields.mjs +2 -2
- package/src/tui/{engine → session}/tool-card-results.mjs +8 -6
- package/src/tui/{engine → session}/tool-result-status.mjs +5 -5
- package/src/tui/{engine → session}/tool-result-text.mjs +13 -3
- package/src/tui/{engine → session}/transcript-spill.mjs +34 -34
- package/src/tui/{engine → session}/turn.mjs +38 -176
- package/src/tui/{engine-local-session.mjs → session-local.mjs} +103 -103
- package/src/tui/session.mjs +22 -0
- package/src/tui/spinner-meta.mjs +4 -4
- package/src/tui/spinner-verbs.mjs +51 -3
- package/src/ui/statusline-segments.mjs +1 -1
- package/src/ui/statusline.mjs +1 -1
- package/vendor/ink/build/components/App.js +10 -1
- package/src/session-runtime/memory-daemon-probe.mjs +0 -61
- package/src/standalone/engine-daemon-client.mjs +0 -894
- package/src/standalone/engine-daemon-local-bridge.mjs +0 -20
- package/src/standalone/engine-daemon-protocol.mjs +0 -33
- package/src/tui/engine/turn-watchdog.mjs +0 -92
- package/src/tui/engine.mjs +0 -22
- package/src/tui/hooks/useEngine.mjs +0 -16
- /package/src/runtime/agent/orchestrator/session/compact/{engine.mjs → runner.mjs} +0 -0
- /package/src/runtime/channels/{backends → providers}/discord-attachments.mjs +0 -0
- /package/src/tui/{engine → session}/agent-response-tail.mjs +0 -0
- /package/src/tui/{engine → session}/frame-batched-store.mjs +0 -0
- /package/src/tui/{engine → session}/tui-steering-persist.mjs +0 -0
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// code-graph.mjs.
|
|
5
5
|
import { resolve as pathResolve, isAbsolute, relative as pathRelative, basename as pathBasename } from 'node:path';
|
|
6
6
|
import { homedir as osHomedir } from 'node:os';
|
|
7
|
-
import {
|
|
7
|
+
import { existsSync, statSync } from 'node:fs';
|
|
8
8
|
import { normalizeInputPath, toDisplayPath } from '../builtin.mjs';
|
|
9
9
|
import { findFileByBasename } from '../builtin/path-diagnostics.mjs';
|
|
10
10
|
import { markScopedCacheIncomplete } from '../../session/cache/scoped-cache-outcome.mjs';
|
|
@@ -40,7 +40,9 @@ import {
|
|
|
40
40
|
_CALLEES_BRACE_LANGS,
|
|
41
41
|
_formatRelated,
|
|
42
42
|
_formatImpact,
|
|
43
|
+
_impactSourceNodes,
|
|
43
44
|
_resolveReferenceLanguageNode,
|
|
45
|
+
_prewarmSourceTextNodes,
|
|
44
46
|
_prewarmReferenceSourceText,
|
|
45
47
|
_cheapReferenceSearch,
|
|
46
48
|
_formatCallerReferences,
|
|
@@ -51,7 +53,8 @@ import {
|
|
|
51
53
|
|
|
52
54
|
const CODE_GRAPH_BATCHABLE_MODES = new Set(['symbol', 'find_symbol', 'symbol_search', 'callers', 'callees', 'references']);
|
|
53
55
|
const CODE_GRAPH_FILE_BATCHABLE_MODES = new Set(['imports', 'dependents', 'related', 'impact', 'symbols', 'overview']);
|
|
54
|
-
const
|
|
56
|
+
const CODE_GRAPH_MAX_BATCH_SYMBOLS = 20;
|
|
57
|
+
const CODE_GRAPH_FILE_BATCH_CAP = 20;
|
|
55
58
|
|
|
56
59
|
function _collectGraphSymbolList(args) {
|
|
57
60
|
const split = (s) => String(s || '').split(/[,\s]+/).map((t) => t.trim()).filter(Boolean);
|
|
@@ -60,18 +63,21 @@ function _collectGraphSymbolList(args) {
|
|
|
60
63
|
...(typeof args?.symbols === 'string' ? split(args.symbols) : []),
|
|
61
64
|
...(typeof args?.symbol === 'string' ? split(args.symbol) : []),
|
|
62
65
|
])];
|
|
63
|
-
|
|
64
|
-
const capped = list.slice(0, CODE_GRAPH_SYMBOL_BATCH_CAP);
|
|
65
|
-
capped._omitted = list.slice(CODE_GRAPH_SYMBOL_BATCH_CAP);
|
|
66
|
-
return capped;
|
|
66
|
+
return list;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
const CODE_GRAPH_FILE_BATCH_CAP = 20;
|
|
70
69
|
const _AGGREGATE_FILE_WILDCARD_RE = /[*?[\]{}]/;
|
|
71
70
|
const ROOT_FEDERATED_MODES = new Set([
|
|
72
71
|
'overview', 'symbol', 'find_symbol', 'symbol_search', 'search',
|
|
73
72
|
'references', 'callers', 'callees', 'symbols', 'prewarm',
|
|
74
73
|
]);
|
|
74
|
+
// Fan-out cap when federation targets are DISCOVERED (immediate child project
|
|
75
|
+
// roots of a sentinel-free cwd) rather than registered. Bounds the cost of
|
|
76
|
+
// answering a query at a multi-repo parent; registered roots are unaffected.
|
|
77
|
+
const CODE_GRAPH_DISCOVERED_FEDERATION_CAP = (() => {
|
|
78
|
+
const raw = parseInt(process.env.MIXDOG_CODE_GRAPH_FEDERATION_CAP ?? '', 10);
|
|
79
|
+
return Number.isFinite(raw) && raw > 0 ? raw : 8;
|
|
80
|
+
})();
|
|
75
81
|
|
|
76
82
|
export async function _runCodeGraphFederation(roots, runOne, projectArgs) {
|
|
77
83
|
return Promise.all((roots || []).map(async (root) => {
|
|
@@ -132,6 +138,25 @@ function _hasAggregateFileArgs(args) {
|
|
|
132
138
|
|| (typeof args?.files === 'string' && args.files.trim());
|
|
133
139
|
}
|
|
134
140
|
|
|
141
|
+
// Aggregate anchors that ALL resolve to the cwd itself ('.', './', the cwd
|
|
142
|
+
// path) add no scope — they mean "search here". Detected so the call can take
|
|
143
|
+
// the plain-cwd route, which adopts a sentinel-free single tree (a vendored
|
|
144
|
+
// reference checkout) as its own root while still refusing an unbounded or
|
|
145
|
+
// multi-project parent. Without this, `files:"."` turned every reference tree
|
|
146
|
+
// into a hard "not inside a project" refusal.
|
|
147
|
+
function _aggregateAnchorsAreCwd(args, baseCwd) {
|
|
148
|
+
if (!_hasAggregateFileArgs(args)) return false;
|
|
149
|
+
const files = _collectGraphFileList(args);
|
|
150
|
+
if (files.length === 0) return false;
|
|
151
|
+
return files.every((file) => {
|
|
152
|
+
const trimmed = String(file || '').trim();
|
|
153
|
+
if (!trimmed || _AGGREGATE_FILE_WILDCARD_RE.test(trimmed)) return false;
|
|
154
|
+
try {
|
|
155
|
+
return pathResolve(isAbsolute(trimmed) ? trimmed : pathResolve(baseCwd, trimmed)) === pathResolve(baseCwd);
|
|
156
|
+
} catch { return false; }
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
135
160
|
// An invalid caller cwd may be recovered for an explicit files aggregate only
|
|
136
161
|
// when every supplied anchor points at the same detectable project. Do not use
|
|
137
162
|
// the batch cap here: an omitted anchor could belong to another project.
|
|
@@ -156,9 +181,35 @@ function _resolveAggregateFileProjectRoot(args, baseCwd) {
|
|
|
156
181
|
const root = isDirectory ? _findDirProjectRoot(abs) : _resolveFileProjectRoot(abs);
|
|
157
182
|
if (!root) return null;
|
|
158
183
|
roots.add(pathResolve(root));
|
|
159
|
-
if (roots.size > 1) return null;
|
|
160
184
|
}
|
|
161
|
-
|
|
185
|
+
if (roots.size === 0) return null;
|
|
186
|
+
if (roots.size === 1) return [...roots][0];
|
|
187
|
+
// Monorepo anchors legitimately resolve to DIFFERENT sentinels: a workspace
|
|
188
|
+
// package (apps/desktop/package.json) is nearer than the repo root, so one
|
|
189
|
+
// call spanning `apps/desktop/...` and `src/...` yields two roots even though
|
|
190
|
+
// both live in exactly one project. When one candidate contains every other,
|
|
191
|
+
// that outermost root IS the single detectable project — adopt it instead of
|
|
192
|
+
// refusing. Genuinely unrelated trees share no such candidate and still fail.
|
|
193
|
+
return _outermostContainingRoot([...roots]);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// The candidate that contains (or equals) every other candidate, else null.
|
|
197
|
+
// Only an EXISTING candidate can win: a bare common ancestor that has no
|
|
198
|
+
// sentinel of its own is never adopted as a project root.
|
|
199
|
+
function _outermostContainingRoot(roots) {
|
|
200
|
+
for (const candidate of roots) {
|
|
201
|
+
if (roots.every((root) => _isSameOrInside(root, candidate))) return candidate;
|
|
202
|
+
}
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function _isSameOrInside(child, parent) {
|
|
207
|
+
try {
|
|
208
|
+
const rel = pathRelative(pathResolve(parent), pathResolve(child));
|
|
209
|
+
return rel === '' || (!!rel && !rel.startsWith('..') && !isAbsolute(rel));
|
|
210
|
+
} catch {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
162
213
|
}
|
|
163
214
|
|
|
164
215
|
// Aggregate recovery resolves relative anchors against the caller's original
|
|
@@ -326,13 +377,19 @@ async function codeGraph(args, cwd, signal = null, options = {}) {
|
|
|
326
377
|
const depsAll = [...(graph.reverse.get(depRel) || [])].sort();
|
|
327
378
|
if (!depsAll.length) return '(no dependents)';
|
|
328
379
|
const deps = depsAll.slice(0, GRAPH_LIST_CAP);
|
|
380
|
+
await _prewarmSourceTextNodes(
|
|
381
|
+
graph,
|
|
382
|
+
deps.map((dep) => graph.nodes.get(dep)).filter(Boolean),
|
|
383
|
+
{ signal },
|
|
384
|
+
);
|
|
329
385
|
const basename = depRel.split('/').pop();
|
|
330
386
|
const stem = basename.replace(/\.[^/.]+$/, '');
|
|
331
387
|
const enriched = deps.map((dep) => {
|
|
332
388
|
const depNode = graph.nodes.get(dep);
|
|
333
389
|
if (!depNode) return dep;
|
|
334
|
-
|
|
335
|
-
|
|
390
|
+
const cached = graph._sourceTextCache?.get(depNode.rel);
|
|
391
|
+
if (!cached || cached.fingerprint !== (depNode.fingerprint || '')) return dep;
|
|
392
|
+
const text = cached.text;
|
|
336
393
|
const linesArr = text.split(/\r?\n/);
|
|
337
394
|
for (let i = 0; i < linesArr.length; i++) {
|
|
338
395
|
const ln = linesArr[i];
|
|
@@ -358,6 +415,8 @@ async function codeGraph(args, cwd, signal = null, options = {}) {
|
|
|
358
415
|
if (mode === 'impact') {
|
|
359
416
|
if (!node) return _appendSameBasenameHint(`Error: code_graph impact: file not found in graph: ${normFile || '(missing file)'}`, normFile, graph);
|
|
360
417
|
const targetSymbol = String(args?.symbol || '').trim();
|
|
418
|
+
await _prewarmSourceTextNodes(graph, [node], { signal });
|
|
419
|
+
await _prewarmSourceTextNodes(graph, _impactSourceNodes(node, graph, targetSymbol), { signal });
|
|
361
420
|
return _formatImpact(node, graph, cwd, targetSymbol);
|
|
362
421
|
}
|
|
363
422
|
|
|
@@ -376,6 +435,7 @@ async function codeGraph(args, cwd, signal = null, options = {}) {
|
|
|
376
435
|
if (!_CALLEES_BRACE_LANGS.has(declHit.lang)) {
|
|
377
436
|
return `(callees unsupported for ${declHit.lang})`;
|
|
378
437
|
}
|
|
438
|
+
await _prewarmSourceTextNodes(graph, [graph.nodes.get(declHit.rel)].filter(Boolean), { signal });
|
|
379
439
|
const rows = _extractCallees(graph, declHit, cwd, {
|
|
380
440
|
cap: 200,
|
|
381
441
|
callerSymbol: symbol,
|
|
@@ -389,9 +449,11 @@ async function codeGraph(args, cwd, signal = null, options = {}) {
|
|
|
389
449
|
|
|
390
450
|
if (mode === 'symbols') {
|
|
391
451
|
if (!node) return _appendSameBasenameHint(`Error: code_graph symbols: file not found in graph: ${normFile || '(missing file)'}`, normFile, graph);
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
return
|
|
452
|
+
await _prewarmSourceTextNodes(graph, [node], { signal });
|
|
453
|
+
const cached = graph._sourceTextCache?.get(node.rel);
|
|
454
|
+
return cached && cached.fingerprint === (node.fingerprint || '')
|
|
455
|
+
? _extractSymbolsCheap(cached.text, node.lang)
|
|
456
|
+
: '(no symbols)';
|
|
395
457
|
}
|
|
396
458
|
|
|
397
459
|
if (mode === 'find_symbol') {
|
|
@@ -400,6 +462,15 @@ async function codeGraph(args, cwd, signal = null, options = {}) {
|
|
|
400
462
|
const language = String(args?.language || '').trim() || null;
|
|
401
463
|
const limit = Math.max(1, Math.min(50, Number(args?.limit || 20)));
|
|
402
464
|
if (rel && !node) return _appendSameBasenameHint(`Error: code_graph find_symbol: file not found in graph: ${normFile || '(missing file)'}`, normFile, graph);
|
|
465
|
+
if (args?.body !== false) {
|
|
466
|
+
const hits = _findSymbolHits(graph, symbol, { language });
|
|
467
|
+
const primary = hits.find((hit) => hit.declarationLike) || hits[0];
|
|
468
|
+
await _prewarmSourceTextNodes(
|
|
469
|
+
graph,
|
|
470
|
+
[primary?.rel ? graph.nodes.get(primary.rel) : null].filter(Boolean),
|
|
471
|
+
{ signal },
|
|
472
|
+
);
|
|
473
|
+
}
|
|
403
474
|
return _findSymbolAcrossGraph(graph, symbol, cwd, { language, limit, fileRel: rel, body: args?.body !== false });
|
|
404
475
|
}
|
|
405
476
|
|
|
@@ -412,6 +483,14 @@ async function codeGraph(args, cwd, signal = null, options = {}) {
|
|
|
412
483
|
const keyword = String(args?.symbol || '').trim();
|
|
413
484
|
const keywords = symbolsList.length ? symbolsList : (keyword ? [keyword] : []);
|
|
414
485
|
if (!keywords.length) throw new Error('code_graph symbol_search: "symbol" (or "symbols[]") is required.');
|
|
486
|
+
// Native graph symbols answer without source text. Nodes lacking native
|
|
487
|
+
// symbols fall back to cheap text extraction, so warm exactly that subset
|
|
488
|
+
// asynchronously before the synchronous formatter scans it.
|
|
489
|
+
await _prewarmSourceTextNodes(
|
|
490
|
+
graph,
|
|
491
|
+
[...graph.nodes.values()].filter((candidate) => !Array.isArray(candidate?.symbols) || candidate.symbols.length === 0),
|
|
492
|
+
{ signal },
|
|
493
|
+
);
|
|
415
494
|
if (keywords.length === 1) {
|
|
416
495
|
return _searchSymbolsByKeyword(graph, keywords[0], cwd, { language, limit });
|
|
417
496
|
}
|
|
@@ -438,6 +517,7 @@ async function codeGraph(args, cwd, signal = null, options = {}) {
|
|
|
438
517
|
}
|
|
439
518
|
}
|
|
440
519
|
const narrowedByCaller = Boolean(rel || scopeRelPrefix || explicitLanguage);
|
|
520
|
+
if (node) await _prewarmSourceTextNodes(graph, [node], { signal });
|
|
441
521
|
const resolved = _resolveReferenceLanguageNode(graph, symbol, rel, cwd, explicitLanguage);
|
|
442
522
|
if (rel && resolved.kind === 'file-not-found') {
|
|
443
523
|
return _appendSameBasenameHint(`Error: code_graph references: file not found in graph: ${normFile || '(missing file)'}`, normFile, graph);
|
|
@@ -452,7 +532,7 @@ async function codeGraph(args, cwd, signal = null, options = {}) {
|
|
|
452
532
|
const userLimit = Number.isFinite(rawLimit) && rawLimit > 0
|
|
453
533
|
? Math.min(500, Math.floor(rawLimit))
|
|
454
534
|
: null;
|
|
455
|
-
const _refNodes = await _prewarmReferenceSourceText(graph, symbol, lang);
|
|
535
|
+
const _refNodes = await _prewarmReferenceSourceText(graph, symbol, lang, { signal });
|
|
456
536
|
const refResult = _cheapReferenceSearch(graph, symbol, cwd, { language: lang, limit: userLimit, fileRel: rel, scopeRelPrefix, nodes: _refNodes });
|
|
457
537
|
return narrowedByCaller ? refResult : _augmentNoHitDiagnostic(refResult, '(no references)', graph, cwd, symbol);
|
|
458
538
|
}
|
|
@@ -468,6 +548,7 @@ async function codeGraph(args, cwd, signal = null, options = {}) {
|
|
|
468
548
|
}
|
|
469
549
|
}
|
|
470
550
|
const narrowedByCaller = Boolean(rel || scopeRelPrefix || explicitLanguage);
|
|
551
|
+
if (node) await _prewarmSourceTextNodes(graph, [node], { signal });
|
|
471
552
|
const resolved = _resolveReferenceLanguageNode(graph, symbol, rel, cwd, explicitLanguage);
|
|
472
553
|
if (rel && resolved.kind === 'file-not-found') {
|
|
473
554
|
return _appendSameBasenameHint(`Error: code_graph callers: file not found in graph: ${normFile || '(missing file)'}`, normFile, graph);
|
|
@@ -482,7 +563,7 @@ async function codeGraph(args, cwd, signal = null, options = {}) {
|
|
|
482
563
|
const userLimit = Number.isFinite(rawLimit) && rawLimit > 0
|
|
483
564
|
? Math.min(500, Math.floor(rawLimit))
|
|
484
565
|
: null;
|
|
485
|
-
const _callerNodes = await _prewarmReferenceSourceText(graph, symbol, lang);
|
|
566
|
+
const _callerNodes = await _prewarmReferenceSourceText(graph, symbol, lang, { signal });
|
|
486
567
|
const depth = Math.max(1, Math.min(5, Math.floor(Number(args?.depth) || 1)));
|
|
487
568
|
if (depth > 1) {
|
|
488
569
|
return _formatTransitiveCallers(graph, symbol, cwd, { language: lang, depth, page: args?.page });
|
|
@@ -527,12 +608,23 @@ async function findSymbolTool(args, cwd, signal = null, options = {}) {
|
|
|
527
608
|
if (!symbol) {
|
|
528
609
|
if (fileRel) {
|
|
529
610
|
const node = graph.nodes.get(fileRel);
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
return
|
|
611
|
+
await _prewarmSourceTextNodes(graph, [node], { signal });
|
|
612
|
+
const cached = graph._sourceTextCache?.get(node.rel);
|
|
613
|
+
return cached && cached.fingerprint === (node.fingerprint || '')
|
|
614
|
+
? _extractSymbolsCheap(cached.text, node.lang)
|
|
615
|
+
: '(no symbols)';
|
|
533
616
|
}
|
|
534
617
|
throw new Error('find_symbol: provide "symbol" (to locate) or "file" (to list its symbols).');
|
|
535
618
|
}
|
|
619
|
+
if (args?.body !== false) {
|
|
620
|
+
const hits = _findSymbolHits(graph, symbol, { language });
|
|
621
|
+
const primary = hits.find((hit) => hit.declarationLike) || hits[0];
|
|
622
|
+
await _prewarmSourceTextNodes(
|
|
623
|
+
graph,
|
|
624
|
+
[primary?.rel ? graph.nodes.get(primary.rel) : null].filter(Boolean),
|
|
625
|
+
{ signal },
|
|
626
|
+
);
|
|
627
|
+
}
|
|
536
628
|
return _findSymbolAcrossGraph(graph, symbol, cwd, { language, limit, fileRel, body: args?.body !== false });
|
|
537
629
|
}
|
|
538
630
|
|
|
@@ -583,6 +675,7 @@ export async function resolveSymbolReadSpan(cwd, { symbol, path = null, language
|
|
|
583
675
|
}
|
|
584
676
|
if (!Number.isFinite(endLine) || endLine < startLine) {
|
|
585
677
|
const node = graph.nodes.get(primary.rel);
|
|
678
|
+
await _prewarmSourceTextNodes(graph, [node].filter(Boolean));
|
|
586
679
|
const srcText = node ? _getSourceTextForNode(graph, node) : null;
|
|
587
680
|
const inferred = srcText ? _inferSpanEndByIndent(srcText.split('\n'), startLine) : null;
|
|
588
681
|
if (inferred) {
|
|
@@ -624,6 +717,11 @@ async function executeCodeGraphToolRaw(name, args, cwd, signal = null, options =
|
|
|
624
717
|
args = { ...args };
|
|
625
718
|
delete args.file;
|
|
626
719
|
}
|
|
720
|
+
if (symbolMode && _aggregateAnchorsAreCwd(args, baseCwd)) {
|
|
721
|
+
args = { ...args };
|
|
722
|
+
delete args.files;
|
|
723
|
+
delete args.file;
|
|
724
|
+
}
|
|
627
725
|
const fileArg = (args && typeof args.file === 'string' && args.file.trim()) ? args.file.trim() : '';
|
|
628
726
|
const hasAggregateFileArgs = _hasAggregateFileArgs(args);
|
|
629
727
|
let effectiveCwd = baseCwd;
|
|
@@ -647,6 +745,20 @@ async function executeCodeGraphToolRaw(name, args, cwd, signal = null, options =
|
|
|
647
745
|
? []
|
|
648
746
|
: collectTrustedCodeGraphRoots(baseCwd)
|
|
649
747
|
.filter((root) => pathResolve(root) !== pathResolve(baseCwd));
|
|
748
|
+
// Trust registration is a ROUTING PREFERENCE, not an admission gate. A
|
|
749
|
+
// sentinel-free cwd whose children are obvious project roots (a refs/ folder
|
|
750
|
+
// of checkouts, a multi-repo parent) is answerable: federate over those
|
|
751
|
+
// children instead of refusing the call. Cost stays bounded by the existing
|
|
752
|
+
// per-project graph timeout and the federation fan-out cap — the same way the
|
|
753
|
+
// reference CLIs bound a wide search (time/output caps, never a scope
|
|
754
|
+
// refusal). Registered roots keep priority; discovered children only fill in
|
|
755
|
+
// when registration yields nothing.
|
|
756
|
+
const federationRootsAtBase = trustedRootsAtBase.length
|
|
757
|
+
? trustedRootsAtBase
|
|
758
|
+
: (baseProjectRoot
|
|
759
|
+
? []
|
|
760
|
+
: _childProjectRoots(baseCwd, { cap: CODE_GRAPH_DISCOVERED_FEDERATION_CAP })
|
|
761
|
+
.filter((root) => pathResolve(root) !== pathResolve(baseCwd)));
|
|
650
762
|
// A sentinel-free cwd (multi-repo parent, vendored reference tree) is still
|
|
651
763
|
// routable when trusted project roots live under it — federate over those
|
|
652
764
|
// instead of refusing the call outright.
|
|
@@ -656,9 +768,12 @@ async function executeCodeGraphToolRaw(name, args, cwd, signal = null, options =
|
|
|
656
768
|
&& !fileArg
|
|
657
769
|
&& !hasAggregateFileArgs
|
|
658
770
|
&& ROOT_FEDERATED_MODES.has(rawModeAtBase)
|
|
659
|
-
&&
|
|
771
|
+
&& federationRootsAtBase.length > 0;
|
|
660
772
|
if (filesystemRootCwd || parentDotFederation || sentinelFreeFederation) {
|
|
661
|
-
|
|
773
|
+
// A filesystem root stays on the REGISTERED set only: fanning out over
|
|
774
|
+
// every project directory on a whole drive is a different cost class than
|
|
775
|
+
// fanning out over one folder's children.
|
|
776
|
+
const trustedRoots = filesystemRootCwd ? trustedRootsAtBase : federationRootsAtBase;
|
|
662
777
|
const files = exactDotFederation ? [] : aggregateFilesAtBase;
|
|
663
778
|
const rawMode = rawModeAtBase;
|
|
664
779
|
const canFederate = files.length > 0 || ROOT_FEDERATED_MODES.has(rawMode);
|
|
@@ -818,7 +933,8 @@ async function executeCodeGraphToolRaw(name, args, cwd, signal = null, options =
|
|
|
818
933
|
? findSymbolTool(_stripEmptyArgs(a), effectiveCwd, signal, options)
|
|
819
934
|
: codeGraph(a, effectiveCwd, signal, options));
|
|
820
935
|
if (CODE_GRAPH_BATCHABLE_MODES.has(batchMode)) {
|
|
821
|
-
const
|
|
936
|
+
const collectedSymbols = _collectGraphSymbolList(args);
|
|
937
|
+
const symbolList = collectedSymbols.slice(0, CODE_GRAPH_MAX_BATCH_SYMBOLS);
|
|
822
938
|
if (symbolList.length > 1) {
|
|
823
939
|
return (async () => {
|
|
824
940
|
// Concurrent fan-out: the underlying graph build is single-flight
|
|
@@ -831,10 +947,10 @@ async function executeCodeGraphToolRaw(name, args, cwd, signal = null, options =
|
|
|
831
947
|
catch (e) { body = `Error: ${e?.message || String(e)}`; }
|
|
832
948
|
return `# ${batchMode} ${sym}\n${body}`;
|
|
833
949
|
}));
|
|
834
|
-
if (symbolList.
|
|
950
|
+
if (collectedSymbols.length > symbolList.length) {
|
|
835
951
|
sections.push(
|
|
836
|
-
`
|
|
837
|
-
|
|
952
|
+
`[arg-guard] notice: symbol list capped at ${CODE_GRAPH_MAX_BATCH_SYMBOLS} `
|
|
953
|
+
+ `(${collectedSymbols.length - symbolList.length} omitted)`,
|
|
838
954
|
);
|
|
839
955
|
}
|
|
840
956
|
return sections.join('\n\n');
|
|
@@ -847,8 +963,8 @@ async function executeCodeGraphToolRaw(name, args, cwd, signal = null, options =
|
|
|
847
963
|
if (CODE_GRAPH_FILE_BATCHABLE_MODES.has(batchMode)) {
|
|
848
964
|
const fileList = _collectGraphFileList(args);
|
|
849
965
|
if (fileList.length > 1) {
|
|
850
|
-
const capped = fileList._capped;
|
|
851
966
|
return (async () => {
|
|
967
|
+
const capped = fileList._capped;
|
|
852
968
|
const sections = await Promise.all(fileList.map(async (f) => {
|
|
853
969
|
let body;
|
|
854
970
|
try { body = await dispatchOne({ ...args, file: f, files: undefined }); }
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
// Symbol search / callers / callees / references / impact query layer over a
|
|
3
3
|
// built graph. Pure over {graph,cwd,args}; owns no cache state. Extracted
|
|
4
4
|
// verbatim from code-graph.mjs.
|
|
5
|
-
import { readFileSync } from 'node:fs';
|
|
6
5
|
import { _isJsLike } from './lang-predicates.mjs';
|
|
7
6
|
import { _maskNonCodeText } from './text-mask.mjs';
|
|
8
7
|
import {
|
|
@@ -61,6 +60,18 @@ function _collectImpactSymbols(node, graph) {
|
|
|
61
60
|
return [...names];
|
|
62
61
|
}
|
|
63
62
|
|
|
63
|
+
export function _impactSourceNodes(node, graph, targetSymbol = '') {
|
|
64
|
+
const symbols = targetSymbol ? [targetSymbol] : _collectImpactSymbols(node, graph).slice(0, 8);
|
|
65
|
+
const out = [];
|
|
66
|
+
const seen = new Set();
|
|
67
|
+
for (const candidate of [node, ...symbols.flatMap((symbol) => _lookupCandidateNodes(graph, symbol, node?.lang || null))]) {
|
|
68
|
+
if (!candidate?.rel || seen.has(candidate.rel)) continue;
|
|
69
|
+
seen.add(candidate.rel);
|
|
70
|
+
out.push(candidate);
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
|
|
64
75
|
function _buildImpactSummary(node, graph, cwd, targetSymbol = '') {
|
|
65
76
|
const imports = node.resolvedImports.map((p) => _graphRel(p, cwd));
|
|
66
77
|
const dependents = [...(graph.reverse.get(node.rel) || [])].sort();
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
// Symbol search / callers / callees / references / impact query layer over a
|
|
2
2
|
// built graph. Pure over {graph,cwd,args}; owns no cache state. Extracted
|
|
3
3
|
// verbatim from code-graph.mjs.
|
|
4
|
-
import {
|
|
4
|
+
import { readFile } from 'node:fs/promises';
|
|
5
|
+
import { codeGraphSourceIoAdmission } from '../../../../shared/tool-workload-gates.mjs';
|
|
5
6
|
import { _isJsLike } from './lang-predicates.mjs';
|
|
6
7
|
import { _maskNonCodeText } from './text-mask.mjs';
|
|
7
8
|
import {
|
|
@@ -30,7 +31,7 @@ import {
|
|
|
30
31
|
_keywordMatchesSymbolName,
|
|
31
32
|
} from './keyword-match.mjs';
|
|
32
33
|
|
|
33
|
-
export { _formatRelated, _formatImpact, _findSymbolAcrossGraph, _resolveReferenceLanguageNode, _formatCallerReferences, _formatTransitiveCallers } from './search-references.mjs';
|
|
34
|
+
export { _formatRelated, _formatImpact, _impactSourceNodes, _findSymbolAcrossGraph, _resolveReferenceLanguageNode, _formatCallerReferences, _formatTransitiveCallers } from './search-references.mjs';
|
|
34
35
|
export function _extractCallees(graph, declHit, _cwd, { cap = 200, callerSymbol = null, language = null } = {}) {
|
|
35
36
|
if (!declHit || !_CALLEES_BRACE_LANGS.has(declHit.lang)) return [];
|
|
36
37
|
const declNode = graph.nodes.get(declHit.rel);
|
|
@@ -253,37 +254,64 @@ export function _formatCalleeRow(row) {
|
|
|
253
254
|
const enclosing = row.enclosing ? `(in ${row.enclosing})` : '(in ?)';
|
|
254
255
|
return `${row.name}\t${callsite}\t${decl}\t${enclosing}`;
|
|
255
256
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
257
|
+
const CODE_GRAPH_SOURCE_READ_CONCURRENCY = Math.max(
|
|
258
|
+
1,
|
|
259
|
+
Math.min(32, Math.floor(Number(process.env.MIXDOG_CODE_GRAPH_SOURCE_READ_CONCURRENCY) || 8)),
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
export async function _prewarmSourceTextNodes(graph, nodes, {
|
|
263
|
+
concurrency = CODE_GRAPH_SOURCE_READ_CONCURRENCY,
|
|
264
|
+
readFileImpl = readFile,
|
|
265
|
+
signal = null,
|
|
266
|
+
ownerKey = null,
|
|
267
|
+
} = {}) {
|
|
268
|
+
const sourceNodes = [];
|
|
269
|
+
const seen = new Set();
|
|
270
|
+
for (const node of Array.isArray(nodes) ? nodes : []) {
|
|
271
|
+
if (!node?.rel || !node?.abs || seen.has(node.rel)) continue;
|
|
272
|
+
seen.add(node.rel);
|
|
273
|
+
sourceNodes.push(node);
|
|
274
|
+
}
|
|
263
275
|
const uncached = [];
|
|
264
|
-
for (const node of
|
|
276
|
+
for (const node of sourceNodes) {
|
|
265
277
|
const cached = graph._sourceTextCache?.get(node.rel);
|
|
266
278
|
if (!cached || cached.fingerprint !== (node.fingerprint || '')) {
|
|
267
279
|
uncached.push(node);
|
|
268
280
|
}
|
|
269
281
|
}
|
|
270
|
-
if (uncached.length === 0) return candidateNodes;
|
|
271
|
-
const { readFile } = await import('fs/promises');
|
|
272
|
-
const concurrency = 64;
|
|
273
282
|
let next = 0;
|
|
274
|
-
async
|
|
275
|
-
while (
|
|
283
|
+
const worker = async () => {
|
|
284
|
+
while (!signal?.aborted) {
|
|
276
285
|
const index = next++;
|
|
277
286
|
if (index >= uncached.length) return;
|
|
278
287
|
const node = uncached[index];
|
|
279
288
|
try {
|
|
280
|
-
const text = await
|
|
289
|
+
const text = await codeGraphSourceIoAdmission.run(
|
|
290
|
+
ownerKey,
|
|
291
|
+
() => readFileImpl(node.abs, 'utf8'),
|
|
292
|
+
{ signal },
|
|
293
|
+
);
|
|
281
294
|
graph._sourceTextCache?.set(node.rel, { fingerprint: node.fingerprint || '', text });
|
|
282
|
-
} catch { /* skip unreadable file */ }
|
|
295
|
+
} catch { /* skip unreadable/aborted file */ }
|
|
283
296
|
}
|
|
297
|
+
};
|
|
298
|
+
const workerCount = Math.min(
|
|
299
|
+
Math.max(1, Math.floor(Number(concurrency) || CODE_GRAPH_SOURCE_READ_CONCURRENCY)),
|
|
300
|
+
Math.max(1, uncached.length),
|
|
301
|
+
);
|
|
302
|
+
if (uncached.length > 0) {
|
|
303
|
+
await Promise.all(Array.from({ length: workerCount }, worker));
|
|
284
304
|
}
|
|
285
|
-
|
|
286
|
-
|
|
305
|
+
return sourceNodes;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export async function _prewarmReferenceSourceText(graph, symbol, language, options = {}) {
|
|
309
|
+
const candidateNodes = _lookupCandidateNodes(graph, symbol, language);
|
|
310
|
+
// Return the resolved candidate set so the immediately-following
|
|
311
|
+
// _cheapReferenceSearch (references/callers dispatch) can reuse it instead
|
|
312
|
+
// of recomputing _lookupCandidateNodes for the same (symbol, language) —
|
|
313
|
+
// which on a token-index miss is a full-graph scan run twice per symbol.
|
|
314
|
+
await _prewarmSourceTextNodes(graph, candidateNodes, options);
|
|
287
315
|
return candidateNodes;
|
|
288
316
|
}
|
|
289
317
|
|
|
@@ -3,17 +3,18 @@ export const CODE_GRAPH_TOOL_DEFS = [
|
|
|
3
3
|
name: 'code_graph',
|
|
4
4
|
title: 'Code Graph',
|
|
5
5
|
annotations: { title: 'Code Graph', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false, compressible: false, compressibleLossless: true },
|
|
6
|
-
description: '
|
|
6
|
+
description: 'Source-file structure, symbol relations, and flow. File modes take files[]; symbol modes take symbols[] — exact identifiers via find_symbol/references/callers/callees, keywords via symbol_search/search (symbol-index terms). Unsupported target arrays are omitted, never mixed.',
|
|
7
7
|
inputSchema: {
|
|
8
8
|
type: 'object',
|
|
9
9
|
properties: {
|
|
10
10
|
mode: { type: 'string', enum: ['overview', 'imports', 'dependents', 'related', 'impact', 'symbols', 'find_symbol', 'symbol_search', 'search', 'references', 'callers', 'callees'], description: 'File modes={overview,imports,dependents,related,impact}; symbols with files[]=file outline; the rest are symbol modes.' },
|
|
11
11
|
files: { anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' }, minItems: 1 }], description: 'Source file path(s); supported targets only.' },
|
|
12
|
-
symbols: { anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' }, minItems: 1 }], description: 'Exact identifiers or keywords; batch multiple in one symbols[].' },
|
|
12
|
+
symbols: { anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' }, minItems: 1 }], description: 'Exact identifiers or keywords; keywords are symbol-index terms; batch multiple in one symbols[].' },
|
|
13
13
|
body: { type: 'boolean', description: 'Include body.' },
|
|
14
14
|
limit: { type: 'number', minimum: 1, description: 'Max results.' },
|
|
15
15
|
depth: { type: 'number', minimum: 1, maximum: 5, description: 'Caller depth.' },
|
|
16
16
|
page: { type: 'number', minimum: 1, description: 'Caller page.' },
|
|
17
|
+
cwd: { type: 'string', description: 'Explicit root.' },
|
|
17
18
|
},
|
|
18
19
|
required: ['mode'],
|
|
19
20
|
additionalProperties: false,
|
|
@@ -120,8 +120,7 @@ export function scrubRuntimeRootVars(env) {
|
|
|
120
120
|
// Daemon IDENTITY leak: user-facing children must not inherit host/worker
|
|
121
121
|
// ownership or the daemon's terminal-lead PID. Internal daemon/worker
|
|
122
122
|
// spawns set every identity value explicitly after this boundary.
|
|
123
|
-
delete env.
|
|
124
|
-
delete env.MIXDOG_CHANNEL_DAEMON;
|
|
123
|
+
delete env.MIXDOG_DAEMON_HOST;
|
|
125
124
|
delete env.MIXDOG_WORKER_MODE;
|
|
126
125
|
delete env.MIXDOG_DAEMON_SPAWNED_FOR;
|
|
127
126
|
delete env.MIXDOG_SUPERVISOR_PID;
|