mixdog 0.9.147 → 0.9.149
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 +23 -3
- package/scripts/build-voice-runtime-unix.sh +101 -0
- package/scripts/embedding-quality-probe.mjs +96 -0
- package/scripts/generate-voice-runtime-manifest.mjs +300 -0
- package/scripts/generate-voice-runtime-manifest.test.mjs +135 -0
- package/scripts/native-binary-arch.mjs +30 -13
- package/scripts/native-binary-arch.test.mjs +40 -4
- package/scripts/plan-voice-runtime-release.mjs +73 -0
- package/scripts/prepare-native-assets.mjs +50 -9
- package/scripts/prune-embedding-runtime.mjs +3 -3
- package/scripts/sync-voice-runtime-manifest.mjs +46 -0
- package/scripts/verify-release-assets.mjs +106 -6
- package/scripts/verify-voice-runtime-release.mjs +89 -0
- package/scripts/voice-runtime-config.json +52 -0
- package/src/app.mjs +42 -5
- package/src/headless-exec.mjs +58 -32
- package/src/headless-exec.test.mjs +74 -13
- package/src/rules/lead/01-general.md +2 -0
- package/src/rules/shared/10-tool-workflow.md +7 -4
- package/src/rules/shared/30-exploration.md +5 -1
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +28 -9
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +6 -1
- package/src/runtime/agent/orchestrator/config.mjs +53 -18
- package/src/runtime/agent/orchestrator/mcp/cancellation.test.mjs +139 -0
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +34 -5
- package/src/runtime/agent/orchestrator/mcp/client.mjs +94 -10
- package/src/runtime/agent/orchestrator/providers/anthropic-betas.mjs +5 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs +98 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.test.mjs +90 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-leaked-toolcall.mjs +67 -16
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +4 -82
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +0 -5
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +62 -121
- package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.mjs +48 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.test.mjs +215 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +44 -48
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +54 -31
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +56 -2
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +34 -2
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +61 -1
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +61 -15
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +120 -1
- package/src/runtime/agent/orchestrator/providers/lib/env-utils.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/lib/stream-outcome.mjs +0 -3
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +187 -84
- package/src/runtime/agent/orchestrator/providers/openai-codex-identity.test.mjs +32 -9
- package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +78 -33
- package/src/runtime/agent/orchestrator/providers/openai-codex-model.mjs +21 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +36 -36
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +16 -6
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +50 -213
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +135 -126
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +20 -11
- package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +112 -36
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +220 -50
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +112 -9
- package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +130 -60
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +28 -10
- package/src/runtime/agent/orchestrator/providers/registry.mjs +122 -95
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +69 -3
- package/src/runtime/agent/orchestrator/providers/tool-stream-state.mjs +4 -22
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +7 -14
- package/src/runtime/agent/orchestrator/session/compact-policy.test.mjs +128 -0
- package/src/runtime/agent/orchestrator/session/context-compaction-policy.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +27 -48
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +19 -14
- package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +9 -43
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +43 -5
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +15 -2
- package/src/runtime/agent/orchestrator/session/manager/pending-messages-persistence.test.mjs +325 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +599 -195
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +22 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +9 -2
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +26 -16
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +23 -11
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +118 -3
- package/src/runtime/agent/orchestrator/session/store-summary-reader.test.mjs +332 -0
- package/src/runtime/agent/orchestrator/session/store.mjs +38 -1
- package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +27 -6
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +12 -20
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +291 -128
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +46 -9
- package/src/runtime/agent/orchestrator/tools/builtin/edit-encoding-eol.test.mjs +224 -0
- package/src/runtime/agent/orchestrator/tools/builtin/edit-sequential-occupation.test.mjs +53 -3
- package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +255 -25
- package/src/runtime/agent/orchestrator/tools/builtin/find-search-budget.test.mjs +87 -0
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +132 -9
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.test.mjs +10 -0
- package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-pattern-fanout.mjs +29 -3
- package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool-integrity.test.mjs +14 -0
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +180 -31
- package/src/runtime/agent/orchestrator/tools/builtin/local-search-telemetry.mjs +4 -1
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +32 -5
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-health.test.mjs +5 -2
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-runner.mjs +10 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +38 -10
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +12 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +31 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +11 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-windows.mjs +22 -4
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +6 -8
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +25 -24
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +152 -133
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +114 -42
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +129 -15
- package/src/runtime/agent/orchestrator/tools/code-graph/loose-file-target.test.mjs +72 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/project-root.mjs +7 -8
- package/src/runtime/agent/orchestrator/tools/code-graph/search-references.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +201 -19
- package/src/runtime/agent/orchestrator/tools/code-graph/span.mjs +26 -3
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +10 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-search-scope.test.mjs +55 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/text-mask.mjs +115 -25
- package/src/runtime/agent/orchestrator/tools/code-graph/text-span-mask.test.mjs +268 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +3 -3
- package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.mjs +296 -46
- package/src/runtime/agent/orchestrator/tools/lib/native-spawn-client.test.mjs +104 -0
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +136 -0
- package/src/runtime/agent/orchestrator/tools/patch/dispatch.mjs +127 -38
- package/src/runtime/agent/orchestrator/tools/patch/engine-contract-gate.test.mjs +661 -0
- package/src/runtime/agent/orchestrator/tools/patch/matcher.mjs +350 -9
- package/src/runtime/agent/orchestrator/tools/patch/native-server.mjs +418 -44
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +27 -3
- package/src/runtime/agent/orchestrator/tools/patch/patch-codec-eol.test.mjs +552 -0
- package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +65 -15
- package/src/runtime/agent/orchestrator/tools/patch-manifest.json +11 -11
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +284 -109
- package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +51 -21
- package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +44 -27
- package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +6 -1
- package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +11 -11
- package/src/runtime/attachments/store.mjs +11 -3
- package/src/runtime/channels/data/voice-runtime-manifest.json +46 -70
- package/src/runtime/channels/lib/event-queue.mjs +51 -41
- package/src/runtime/channels/lib/scheduler.mjs +23 -28
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +6 -17
- package/src/runtime/channels/lib/voice-runtime-fetcher.test.mjs +17 -0
- package/src/runtime/channels/lib/webhook.mjs +39 -20
- package/src/runtime/channels/lib/whisper-server.mjs +46 -1
- package/src/runtime/channels/lib/worker-main.mjs +3 -1
- package/src/runtime/media/adapters/gemini-video.mjs +20 -4
- package/src/runtime/media/jobs.mjs +13 -0
- package/src/runtime/media/renditions.mjs +7 -1
- package/src/runtime/media/store.mjs +42 -22
- package/src/runtime/memory/index.mjs +4 -9
- package/src/runtime/memory/lib/agent-ipc.mjs +1 -6
- package/src/runtime/memory/lib/archive-security.test.mjs +21 -1
- package/src/runtime/memory/lib/core-memory-store.mjs +28 -50
- package/src/runtime/memory/lib/embedding-model-config.mjs +37 -1
- package/src/runtime/memory/lib/embedding-model-config.test.mjs +33 -0
- package/src/runtime/memory/lib/embedding-provider.mjs +34 -12
- package/src/runtime/memory/lib/embedding-worker.mjs +37 -5
- package/src/runtime/memory/lib/ko-morph.mjs +46 -15
- package/src/runtime/memory/lib/memory-action-handlers.mjs +18 -1
- package/src/runtime/memory/lib/memory-cycle1.mjs +1 -18
- package/src/runtime/memory/lib/memory-cycle2-gate.mjs +1 -18
- package/src/runtime/memory/lib/memory-cycle2-mutations.mjs +98 -45
- package/src/runtime/memory/lib/memory-cycle2-shared.mjs +71 -0
- package/src/runtime/memory/lib/memory-cycle2-shared.test.mjs +78 -0
- package/src/runtime/memory/lib/memory-cycle2.mjs +37 -3
- package/src/runtime/memory/lib/memory-cycle3.mjs +1 -1
- package/src/runtime/memory/lib/memory-embedding-migration.test.mjs +50 -0
- package/src/runtime/memory/lib/memory-recall-store.mjs +9 -24
- package/src/runtime/memory/lib/memory.mjs +50 -12
- package/src/runtime/memory/lib/pg/process.mjs +1 -10
- package/src/runtime/memory/lib/pg/supervisor.mjs +1 -9
- package/src/runtime/memory/lib/query-handlers.mjs +2 -4
- package/src/runtime/memory/lib/session-ingest-runtime.mjs +2 -2
- package/src/runtime/shared/background-tasks.mjs +15 -0
- package/src/runtime/shared/background-tasks.test.mjs +78 -0
- package/src/runtime/shared/child-guardian.mjs +2 -10
- package/src/runtime/shared/pid-liveness.mjs +27 -0
- package/src/runtime/shared/pristine-execution-contract.json +0 -1
- package/src/runtime/shared/runtime-root.mjs +1 -11
- package/src/runtime/shared/service-discovery.mjs +1 -17
- package/src/runtime/shared/singleton-owner.mjs +1 -11
- package/src/runtime/shared/staged-update.mjs +2 -12
- package/src/runtime/shared/tool-status.mjs +5 -1
- package/src/runtime/shared/tool-surface.mjs +15 -7
- package/src/runtime/shared/turn-snapshot-store.mjs +168 -0
- package/src/runtime/shared/turn-snapshot.mjs +137 -13
- package/src/runtime/shared/turn-worktree-snapshot.mjs +110 -18
- package/src/runtime/shared/webhook-session-run.mjs +16 -2
- package/src/runtime/web-search/lib/http-fetch.mjs +34 -4
- package/src/runtime/web-search/lib/state.mjs +15 -15
- package/src/runtime/web-search/lib/web-tools.mjs +46 -5
- package/src/session-runtime/context-status.mjs +4 -12
- package/src/session-runtime/runtime-core.mjs +20 -1
- package/src/session-runtime/session-lifecycle.mjs +9 -0
- package/src/standalone/agent-dispatch-broker.mjs +33 -2
- package/src/standalone/agent-tool/job-task-reconcile.mjs +78 -0
- package/src/standalone/agent-tool/notify.mjs +3 -1
- package/src/standalone/agent-tool/spawn-flow.mjs +52 -70
- package/src/standalone/agent-tool/tag-registry.mjs +116 -33
- package/src/standalone/agent-tool/tool-def.mjs +2 -2
- package/src/standalone/agent-tool/worker-index.mjs +8 -11
- package/src/standalone/agent-tool/worker-rows.mjs +4 -1
- package/src/standalone/agent-tool.mjs +20 -55
- package/src/standalone/agent-watchdog-registry.mjs +28 -4
- package/src/standalone/channel-client.mjs +47 -15
- package/src/standalone/channel-transport.mjs +44 -25
- package/src/standalone/daemon.mjs +31 -2
- package/src/standalone/hook-bus/config.mjs +25 -9
- package/src/standalone/hook-bus/handlers.mjs +16 -52
- package/src/standalone/hook-bus.mjs +29 -23
- package/src/standalone/memory-runtime-proxy.mjs +1 -16
- package/src/standalone/session-client.mjs +86 -23
- package/src/standalone/session-protocol.mjs +0 -2
- package/src/standalone/session-runtime-host-health.test.mjs +5 -4
- package/src/standalone/session-runtime-host.mjs +101 -6
- package/src/standalone/session-runtime-record.mjs +1 -5
- package/src/standalone/session-runtime-worker.mjs +52 -57
- package/src/standalone/session-service.mjs +4 -1
- package/src/standalone/session-transport.mjs +56 -25
- package/src/tui/App.jsx +40 -49
- package/src/tui/app/app-view.jsx +14 -21
- package/src/tui/app/core-memory-picker.mjs +29 -16
- package/src/tui/app/create-app-pickers.mjs +15 -36
- package/src/tui/app/extension-pickers.mjs +60 -49
- package/src/tui/app/maintenance-pickers.mjs +100 -57
- package/src/tui/app/message-selector.mjs +6 -5
- package/src/tui/app/model-picker.mjs +30 -13
- package/src/tui/app/onboarding-steps.mjs +21 -12
- package/src/tui/app/panel-epoch.mjs +56 -0
- package/src/tui/app/panel-epoch.test.mjs +1009 -0
- package/src/tui/app/panel-surface.mjs +98 -0
- package/src/tui/app/project-picker.mjs +30 -29
- package/src/tui/app/prompt-submit.mjs +192 -97
- package/src/tui/app/prompt-submit.test.mjs +206 -0
- package/src/tui/app/provider-setup-picker.mjs +74 -43
- package/src/tui/app/resume-picker.mjs +11 -8
- package/src/tui/app/route-pickers.mjs +45 -28
- package/src/tui/app/settings-picker.mjs +69 -18
- package/src/tui/app/shell-layout.mjs +1 -3
- package/src/tui/app/slash-commands.mjs +0 -2
- package/src/tui/app/slash-dispatch.mjs +4 -2
- package/src/tui/app/text-entry-policy.mjs +25 -0
- package/src/tui/app/theme-effort-pickers.mjs +11 -16
- package/src/tui/app/usage-context-panels.mjs +21 -25
- package/src/tui/app/use-global-key-input.mjs +3 -2
- package/src/tui/app/use-prompt-draft-flow.mjs +6 -18
- package/src/tui/app/use-prompt-handlers.mjs +3 -2
- package/src/tui/app/use-welcome-prompt-hint.mjs +17 -3
- package/src/tui/components/Picker.jsx +1 -1
- package/src/tui/components/TextEntryPanel.jsx +7 -2
- package/src/tui/dist/index.mjs +652 -561
- package/src/tui/lib/voice-setup.mjs +1 -2
- package/src/tui/session/agent-job-feed.mjs +73 -11
- package/src/tui/session/session-api-ext.mjs +5 -6
- package/src/tui/session/session-api.mjs +1 -0
- package/src/tui/session/tool-approval.mjs +15 -5
- package/src/tui/session/tui-steering-persist.mjs +33 -19
- package/src/tui/session/tui-steering-persist.test.mjs +104 -0
- package/src/tui/session-local.mjs +0 -22
- package/scripts/.tmp-coverage-report.mjs +0 -96
- package/src/runtime/agent/orchestrator/tools/builtin/shell-monitor.mjs +0 -57
- package/src/runtime/agent/orchestrator/tools/builtin/shell-monitor.test.mjs +0 -57
|
@@ -61,6 +61,90 @@ function _maskJsRegexLiteral(src, out, start) {
|
|
|
61
61
|
return j;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
// Interpolation dialect of a string literal, decided when the literal opens:
|
|
65
|
+
// 'dollar-brace' — `${expr}` (JS/TS backticks, kotlin "…")
|
|
66
|
+
// 'brace' — `{expr}` (python f-strings, C# $"…", `{{` = literal)
|
|
67
|
+
// 'bash' — `$(cmd)` / `${var}` inside double quotes
|
|
68
|
+
// null = the whole literal is opaque text. Without this only JS backticks had
|
|
69
|
+
// their interpolations analysed, so calls inside python/C#/kotlin/bash
|
|
70
|
+
// interpolation were masked away entirely.
|
|
71
|
+
function _stringLiteralPrefix(src, quoteIndex, allowed) {
|
|
72
|
+
let k = quoteIndex - 1;
|
|
73
|
+
let prefix = '';
|
|
74
|
+
while (k >= 0 && allowed.test(src[k]) && prefix.length < 2) {
|
|
75
|
+
prefix = src[k] + prefix;
|
|
76
|
+
k -= 1;
|
|
77
|
+
}
|
|
78
|
+
// A prefix glued to a longer identifier is not a string prefix.
|
|
79
|
+
if (prefix && k >= 0 && _isWordChar(src[k])) return '';
|
|
80
|
+
return prefix;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function _stringInterpKind(lang, delim, src, quoteIndex) {
|
|
84
|
+
const triple = delim === '"""' || delim === "'''";
|
|
85
|
+
if (delim === '`') return _isJsLike(lang) ? 'dollar-brace' : null;
|
|
86
|
+
// Kotlin templates work the same in "…" and in """…""" raw strings.
|
|
87
|
+
if (lang === 'kotlin' && (delim === '"' || triple)) return 'dollar-brace';
|
|
88
|
+
if (lang === 'bash' && delim === '"') return 'bash';
|
|
89
|
+
if (lang === 'python' && (triple || delim === '"' || delim === '\'')) {
|
|
90
|
+
const prefix = _stringLiteralPrefix(src, quoteIndex, /[fFrRbBuU]/);
|
|
91
|
+
return /[fF]/.test(prefix) ? 'brace' : null;
|
|
92
|
+
}
|
|
93
|
+
if (lang === 'csharp' && (delim === '"' || triple)) {
|
|
94
|
+
const prefix = _stringLiteralPrefix(src, quoteIndex, /[$@]/);
|
|
95
|
+
return prefix.includes('$') ? 'brace' : null;
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Interpolation handling shared by single- and triple-quoted frames. Returns
|
|
101
|
+
// the next index when it consumed bytes, or -1 when the character is ordinary
|
|
102
|
+
// string text.
|
|
103
|
+
function _handleInterpolationInString(src, out, i, frame, stack) {
|
|
104
|
+
const kind = frame?.interp;
|
|
105
|
+
if (!kind) return -1;
|
|
106
|
+
if (kind === 'brace' && src[i] === '{' && src[i + 1] === '{') {
|
|
107
|
+
// `{{` is an escaped literal brace inside an f-string / $"…".
|
|
108
|
+
if (src[i] !== '\n') out[i] = ' ';
|
|
109
|
+
if (src[i + 1] !== '\n') out[i + 1] = ' ';
|
|
110
|
+
return i + 2;
|
|
111
|
+
}
|
|
112
|
+
const opened = _interpolationOpenerAt(src, i, kind);
|
|
113
|
+
if (opened) {
|
|
114
|
+
// Enter interpolation: the expression bytes stay intact.
|
|
115
|
+
stack.push({ kind: 'interp', depth: 1, open: opened.open, close: opened.close });
|
|
116
|
+
return i + opened.length;
|
|
117
|
+
}
|
|
118
|
+
// Bare `$name` (kotlin template, bash expansion) is a real reference, so its
|
|
119
|
+
// identifier bytes stay visible; only the `$` is masked. JS template literals
|
|
120
|
+
// have no bare form — `$foo` there is literal text.
|
|
121
|
+
if ((kind === 'bash' || (kind === 'dollar-brace' && frame.lang === 'kotlin'))
|
|
122
|
+
&& src[i] === '$' && _isWordStartChar(src[i + 1])) {
|
|
123
|
+
if (src[i] !== '\n') out[i] = ' ';
|
|
124
|
+
let j = i + 1;
|
|
125
|
+
while (j < src.length && _isWordChar(src[j])) j += 1;
|
|
126
|
+
return j;
|
|
127
|
+
}
|
|
128
|
+
return -1;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Interpolation opener at `i` for a frame of the given dialect.
|
|
132
|
+
function _interpolationOpenerAt(src, i, kind) {
|
|
133
|
+
if (kind === 'dollar-brace') {
|
|
134
|
+
return src.startsWith('${', i) ? { length: 2, open: '{', close: '}' } : null;
|
|
135
|
+
}
|
|
136
|
+
if (kind === 'brace') {
|
|
137
|
+
// `{{` is an escaped literal brace — handled by the caller.
|
|
138
|
+
if (src[i] !== '{' || src[i + 1] === '{') return null;
|
|
139
|
+
return { length: 1, open: '{', close: '}' };
|
|
140
|
+
}
|
|
141
|
+
if (kind === 'bash') {
|
|
142
|
+
if (src.startsWith('$(', i)) return { length: 2, open: '(', close: ')' };
|
|
143
|
+
if (src.startsWith('${', i)) return { length: 2, open: '{', close: '}' };
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
64
148
|
export function _maskNonCodeText(text, lang) {
|
|
65
149
|
const src = String(text || '');
|
|
66
150
|
const out = src.split('');
|
|
@@ -69,9 +153,11 @@ export function _maskNonCodeText(text, lang) {
|
|
|
69
153
|
// Stack of scanner frames. Top describes current state:
|
|
70
154
|
// { kind: 'string', delim } — inside single-line string literal (mask body)
|
|
71
155
|
// { kind: 'triple', delim } — inside triple-quote string (mask body)
|
|
72
|
-
// { kind: 'interp',
|
|
73
|
-
//
|
|
74
|
-
//
|
|
156
|
+
// { kind: 'interp', depth, open, close }
|
|
157
|
+
// — inside a string interpolation
|
|
158
|
+
// (`${…}`, f-string `{…}`, bash `$( … )`;
|
|
159
|
+
// code mode, bytes preserved so caller
|
|
160
|
+
// analysis sees fn-calls inside)
|
|
75
161
|
// Empty stack = top-level code.
|
|
76
162
|
const stack = [];
|
|
77
163
|
const top = () => (stack.length ? stack[stack.length - 1] : null);
|
|
@@ -104,6 +190,13 @@ export function _maskNonCodeText(text, lang) {
|
|
|
104
190
|
prevToken = 'value';
|
|
105
191
|
continue;
|
|
106
192
|
}
|
|
193
|
+
// Triple-quoted literals interpolate too (python f"""…""", kotlin """…""").
|
|
194
|
+
const nextIndex = _handleInterpolationInString(src, out, i, t, stack);
|
|
195
|
+
if (nextIndex >= 0) {
|
|
196
|
+
i = nextIndex;
|
|
197
|
+
prevToken = 'expr';
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
107
200
|
if (src[i] !== '\n') out[i] = ' ';
|
|
108
201
|
i++;
|
|
109
202
|
continue;
|
|
@@ -127,12 +220,13 @@ export function _maskNonCodeText(text, lang) {
|
|
|
127
220
|
}
|
|
128
221
|
if (t && t.kind === 'string') {
|
|
129
222
|
const d = t.delim;
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
223
|
+
{
|
|
224
|
+
const nextIndex = _handleInterpolationInString(src, out, i, t, stack);
|
|
225
|
+
if (nextIndex >= 0) {
|
|
226
|
+
i = nextIndex;
|
|
227
|
+
prevToken = 'expr';
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
136
230
|
}
|
|
137
231
|
// In bash single-quotes `'...'`, backslash is literal (no escape) — the
|
|
138
232
|
// string closes at the first `'`. Skip the escape consumption there so
|
|
@@ -165,23 +259,19 @@ export function _maskNonCodeText(text, lang) {
|
|
|
165
259
|
continue;
|
|
166
260
|
}
|
|
167
261
|
if (t && t.kind === 'interp') {
|
|
168
|
-
// Code mode inside
|
|
169
|
-
//
|
|
170
|
-
if (src[i] ===
|
|
171
|
-
t.
|
|
262
|
+
// Code mode inside the interpolation. Bytes preserved; track the frame's
|
|
263
|
+
// own delimiter depth so masking resumes once the expression closes.
|
|
264
|
+
if (src[i] === t.open) {
|
|
265
|
+
t.depth++;
|
|
172
266
|
prevToken = 'expr';
|
|
173
267
|
i++;
|
|
174
268
|
continue;
|
|
175
269
|
}
|
|
176
|
-
if (src[i] ===
|
|
177
|
-
t.
|
|
270
|
+
if (src[i] === t.close) {
|
|
271
|
+
t.depth--;
|
|
178
272
|
i++;
|
|
179
|
-
if (t.
|
|
180
|
-
|
|
181
|
-
prevToken = 'value';
|
|
182
|
-
} else {
|
|
183
|
-
prevToken = 'value';
|
|
184
|
-
}
|
|
273
|
+
if (t.depth === 0) stack.pop();
|
|
274
|
+
prevToken = 'value';
|
|
185
275
|
continue;
|
|
186
276
|
}
|
|
187
277
|
if (_supportsSlashComments(lang) && src.startsWith('/*', i)) {
|
|
@@ -205,7 +295,7 @@ export function _maskNonCodeText(text, lang) {
|
|
|
205
295
|
}
|
|
206
296
|
if (src[i] === '"' || (_supportsSingleQuoteStrings(lang) && src[i] === '\'') || (_supportsBacktickStrings(lang) && src[i] === '`')) {
|
|
207
297
|
if (src[i] !== '\n') out[i] = ' ';
|
|
208
|
-
stack.push({ kind: 'string', delim: src[i], lang });
|
|
298
|
+
stack.push({ kind: 'string', delim: src[i], lang, interp: _stringInterpKind(lang, src[i], src, i) });
|
|
209
299
|
i++;
|
|
210
300
|
continue;
|
|
211
301
|
}
|
|
@@ -298,7 +388,7 @@ export function _maskNonCodeText(text, lang) {
|
|
|
298
388
|
if (i + 1 < out.length) out[i + 1] = ' ';
|
|
299
389
|
if (i + 2 < out.length) out[i + 2] = ' ';
|
|
300
390
|
i += 3;
|
|
301
|
-
stack.push({ kind: 'triple', delim: "'''" });
|
|
391
|
+
stack.push({ kind: 'triple', delim: "'''", lang, interp: _stringInterpKind(lang, "'''", src, i - 3) });
|
|
302
392
|
continue;
|
|
303
393
|
}
|
|
304
394
|
if (_supportsTripleDoubleQuoteStrings(lang) && src.startsWith('"""', i)) {
|
|
@@ -306,7 +396,7 @@ export function _maskNonCodeText(text, lang) {
|
|
|
306
396
|
if (i + 1 < out.length) out[i + 1] = ' ';
|
|
307
397
|
if (i + 2 < out.length) out[i + 2] = ' ';
|
|
308
398
|
i += 3;
|
|
309
|
-
stack.push({ kind: 'triple', delim: '"""' });
|
|
399
|
+
stack.push({ kind: 'triple', delim: '"""', lang, interp: _stringInterpKind(lang, '"""', src, i - 3) });
|
|
310
400
|
continue;
|
|
311
401
|
}
|
|
312
402
|
if (src[i] === '/' && _isJsLike(lang) && prevToken === 'expr') {
|
|
@@ -316,7 +406,7 @@ export function _maskNonCodeText(text, lang) {
|
|
|
316
406
|
}
|
|
317
407
|
if (src[i] === '"' || (_supportsSingleQuoteStrings(lang) && src[i] === '\'') || (_supportsBacktickStrings(lang) && src[i] === '`')) {
|
|
318
408
|
if (src[i] !== '\n') out[i] = ' ';
|
|
319
|
-
stack.push({ kind: 'string', delim: src[i], lang });
|
|
409
|
+
stack.push({ kind: 'string', delim: src[i], lang, interp: _stringInterpKind(lang, src[i], src, i) });
|
|
320
410
|
i++;
|
|
321
411
|
continue;
|
|
322
412
|
}
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
|
|
4
|
+
import { _inferSpanEndByIndent } from './span.mjs';
|
|
5
|
+
import { _maskNonCodeText } from './text-mask.mjs';
|
|
6
|
+
import { _atJsRegexPosition, _extractCallees, _jsBraceKindAt } from './search.mjs';
|
|
7
|
+
|
|
8
|
+
test('indentation span inference keeps a multiline arrow signature whole', () => {
|
|
9
|
+
const lines = [
|
|
10
|
+
'const handler = (',
|
|
11
|
+
' a,',
|
|
12
|
+
' b,',
|
|
13
|
+
') => {',
|
|
14
|
+
' return a + b;',
|
|
15
|
+
'};',
|
|
16
|
+
];
|
|
17
|
+
assert.equal(_inferSpanEndByIndent(lines, 1), 6);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('indentation span inference still ends at a plain matching closer', () => {
|
|
21
|
+
const lines = [
|
|
22
|
+
'const rows = [',
|
|
23
|
+
' 1,',
|
|
24
|
+
' 2,',
|
|
25
|
+
'];',
|
|
26
|
+
'const other = 3;',
|
|
27
|
+
];
|
|
28
|
+
assert.equal(_inferSpanEndByIndent(lines, 1), 4);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('python f-string interpolation keeps its call visible', () => {
|
|
32
|
+
const masked = _maskNonCodeText('x = f"total {compute_total(rows)} done"\n', 'python');
|
|
33
|
+
assert.match(masked, /compute_total\(rows\)/);
|
|
34
|
+
assert.doesNotMatch(masked, /done/);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('C# interpolated string exposes the call and keeps {{ }} literal', () => {
|
|
38
|
+
const masked = _maskNonCodeText('var s = $"{Compute(x)} {{literal}}";\n', 'csharp');
|
|
39
|
+
assert.match(masked, /Compute\(x\)/);
|
|
40
|
+
assert.doesNotMatch(masked, /literal/);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('kotlin string template exposes the call', () => {
|
|
44
|
+
const masked = _maskNonCodeText('val s = "n=${computeTotal(rows)}"\n', 'kotlin');
|
|
45
|
+
assert.match(masked, /computeTotal\(rows\)/);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('bash command substitution inside double quotes stays code', () => {
|
|
49
|
+
const masked = _maskNonCodeText('echo "value $(compute_total rows) done"\n', 'bash');
|
|
50
|
+
assert.match(masked, /compute_total rows/);
|
|
51
|
+
assert.doesNotMatch(masked, /value/);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('a plain python string is still fully masked', () => {
|
|
55
|
+
const masked = _maskNonCodeText('x = "total {compute_total(rows)} done"\n', 'python');
|
|
56
|
+
assert.doesNotMatch(masked, /compute_total/);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('a python triple-quoted f-string interpolates', () => {
|
|
60
|
+
const masked = _maskNonCodeText('x = f"""head {compute(rows)} tail"""\n', 'python');
|
|
61
|
+
assert.match(masked, /compute\(rows\)/);
|
|
62
|
+
assert.doesNotMatch(masked, /head/);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('a kotlin raw string template interpolates', () => {
|
|
66
|
+
const masked = _maskNonCodeText('val s = """head ${compute(rows)} tail"""\n', 'kotlin');
|
|
67
|
+
assert.match(masked, /compute\(rows\)/);
|
|
68
|
+
assert.doesNotMatch(masked, /head/);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('a kotlin bare $name keeps the identifier but a JS template does not', () => {
|
|
72
|
+
const kotlin = _maskNonCodeText('val s = "id=$userId done"\n', 'kotlin');
|
|
73
|
+
assert.match(kotlin, /userId/);
|
|
74
|
+
assert.doesNotMatch(kotlin, /done/);
|
|
75
|
+
|
|
76
|
+
// JS template literals have no bare form: `$userId` is literal text.
|
|
77
|
+
const js = _maskNonCodeText('const s = `id=$userId`;\n', 'javascript');
|
|
78
|
+
assert.doesNotMatch(js, /userId/);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('a non-matching same-indent closer does not end the span', () => {
|
|
82
|
+
const lines = [
|
|
83
|
+
'const handler = {',
|
|
84
|
+
' run: (',
|
|
85
|
+
' ) => 1,',
|
|
86
|
+
')',
|
|
87
|
+
'};',
|
|
88
|
+
];
|
|
89
|
+
assert.equal(_inferSpanEndByIndent(lines, 1), 5);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('a division after an object literal is not read as a regex', () => {
|
|
93
|
+
const text = 'function f(){ const x = {} / 2; } outside();';
|
|
94
|
+
assert.equal(_atJsRegexPosition(text, text.indexOf('/ 2')), false);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test('a statement-position regex after a closing brace is recognized', () => {
|
|
98
|
+
const text = '}\n/re/.test(x)';
|
|
99
|
+
assert.equal(_atJsRegexPosition(text, text.indexOf('/re/')), true);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('a postfix increment before a slash stays division', () => {
|
|
103
|
+
const text = 'const y = a++ / b;';
|
|
104
|
+
assert.equal(_atJsRegexPosition(text, text.indexOf('/ b')), false);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('brace kinds separate object literals from blocks', () => {
|
|
108
|
+
assert.equal(_jsBraceKindAt('const x = {}', 10), 'object');
|
|
109
|
+
assert.equal(_jsBraceKindAt('call({})', 5), 'object');
|
|
110
|
+
assert.equal(_jsBraceKindAt('return {}', 7), 'object');
|
|
111
|
+
// Property value: the scanner passes the ENCLOSING brace kind, which is
|
|
112
|
+
// what separates `{ key: {…} }` from a labeled block.
|
|
113
|
+
assert.equal(_jsBraceKindAt('const y = { a: {} }', 15, null, 'object'), 'object');
|
|
114
|
+
// Grammatical position, not a character list: unary `!` and binary `-`.
|
|
115
|
+
assert.equal(_jsBraceKindAt('!{}', 1), 'object');
|
|
116
|
+
assert.equal(_jsBraceKindAt('1 - {}', 4), 'object');
|
|
117
|
+
assert.equal(_jsBraceKindAt('function f() {}', 13), 'block');
|
|
118
|
+
assert.equal(_jsBraceKindAt('const f = () => {}', 16), 'block');
|
|
119
|
+
assert.equal(_jsBraceKindAt('if (x) {}', 7), 'block');
|
|
120
|
+
assert.equal(_jsBraceKindAt('do {} while (x)', 3), 'block');
|
|
121
|
+
assert.equal(_jsBraceKindAt('if (x) {} else {}', 15), 'block');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
test('object-literal divisions after ! and - do not swallow the rest of the file', () => {
|
|
125
|
+
for (const body of ['!{} / 2;', '1 - {} / 2;']) {
|
|
126
|
+
const rel = 'probe-op.mjs';
|
|
127
|
+
const text = `function f(){ ${body} } outside();\n`;
|
|
128
|
+
const node = {
|
|
129
|
+
rel,
|
|
130
|
+
abs: rel,
|
|
131
|
+
lang: 'javascript',
|
|
132
|
+
fingerprint: '',
|
|
133
|
+
symbols: [],
|
|
134
|
+
topLevelTypes: [],
|
|
135
|
+
resolvedImports: [],
|
|
136
|
+
rawImports: [],
|
|
137
|
+
};
|
|
138
|
+
const graph = {
|
|
139
|
+
nodes: new Map([[rel, node]]),
|
|
140
|
+
reverse: new Map(),
|
|
141
|
+
_sourceTextCache: new Map([[rel, { fingerprint: '', text }]]),
|
|
142
|
+
_sourceLinesCache: new Map(),
|
|
143
|
+
_maskedLinesCache: new Map(),
|
|
144
|
+
};
|
|
145
|
+
const rows = _extractCallees(
|
|
146
|
+
graph,
|
|
147
|
+
{ rel, lang: 'javascript', line: 1, col: 1, declarationLike: true },
|
|
148
|
+
'.',
|
|
149
|
+
{ callerSymbol: 'f' },
|
|
150
|
+
);
|
|
151
|
+
assert.deepEqual(rows.map((row) => row.name), [], `body: ${body}`);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test('a labeled block is a block, an object property and a ternary are values', () => {
|
|
156
|
+
assert.equal(_jsBraceKindAt('label: {}', 7, null, 'block'), 'block');
|
|
157
|
+
assert.equal(_jsBraceKindAt('({ key: {} })', 8, null, 'object'), 'object');
|
|
158
|
+
assert.equal(_jsBraceKindAt('const v = c ? a : {};', 18, null, 'block'), 'object');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('a case label with a ternary expression still opens a block', () => {
|
|
162
|
+
const src = 'switch (v) { case c ? a : b: {} }';
|
|
163
|
+
assert.equal(_jsBraceKindAt(src, src.indexOf('{}'), null, 'block'), 'block');
|
|
164
|
+
const plain = 'switch (v) { case 1: {} }';
|
|
165
|
+
assert.equal(_jsBraceKindAt(plain, plain.indexOf('{}'), null, 'block'), 'block');
|
|
166
|
+
const ternary = 'const v = c ? a : {};';
|
|
167
|
+
assert.equal(_jsBraceKindAt(ternary, ternary.indexOf('{}'), null, 'block'), 'object');
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test('a case-label block keeps the callee behind it', () => {
|
|
171
|
+
const rel = 'probe-case.mjs';
|
|
172
|
+
const text = 'function f(){ switch (v) { case c ? a : b: {} /[}]/.test(x); tail(); } }\n';
|
|
173
|
+
const node = {
|
|
174
|
+
rel,
|
|
175
|
+
abs: rel,
|
|
176
|
+
lang: 'javascript',
|
|
177
|
+
fingerprint: '',
|
|
178
|
+
symbols: [],
|
|
179
|
+
topLevelTypes: [],
|
|
180
|
+
resolvedImports: [],
|
|
181
|
+
rawImports: [],
|
|
182
|
+
};
|
|
183
|
+
const graph = {
|
|
184
|
+
nodes: new Map([[rel, node]]),
|
|
185
|
+
reverse: new Map(),
|
|
186
|
+
_sourceTextCache: new Map([[rel, { fingerprint: '', text }]]),
|
|
187
|
+
_sourceLinesCache: new Map(),
|
|
188
|
+
_maskedLinesCache: new Map(),
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const names = _extractCallees(
|
|
192
|
+
graph,
|
|
193
|
+
{ rel, lang: 'javascript', line: 1, col: 1, declarationLike: true },
|
|
194
|
+
'.',
|
|
195
|
+
{ callerSymbol: 'f' },
|
|
196
|
+
).map((row) => row.name);
|
|
197
|
+
assert.ok(names.includes('tail'), `expected 'tail' in ${JSON.stringify(names)}`);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test('a labeled block keeps the regex after it and the calls behind it', () => {
|
|
201
|
+
const rel = 'probe-label.mjs';
|
|
202
|
+
const text = 'function f(){ label: {} /[}]/.test(x); inside(); }\n';
|
|
203
|
+
const node = {
|
|
204
|
+
rel,
|
|
205
|
+
abs: rel,
|
|
206
|
+
lang: 'javascript',
|
|
207
|
+
fingerprint: '',
|
|
208
|
+
symbols: [],
|
|
209
|
+
topLevelTypes: [],
|
|
210
|
+
resolvedImports: [],
|
|
211
|
+
rawImports: [],
|
|
212
|
+
};
|
|
213
|
+
const graph = {
|
|
214
|
+
nodes: new Map([[rel, node]]),
|
|
215
|
+
reverse: new Map(),
|
|
216
|
+
_sourceTextCache: new Map([[rel, { fingerprint: '', text }]]),
|
|
217
|
+
_sourceLinesCache: new Map(),
|
|
218
|
+
_maskedLinesCache: new Map(),
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const rows = _extractCallees(
|
|
222
|
+
graph,
|
|
223
|
+
{ rel, lang: 'javascript', line: 1, col: 1, declarationLike: true },
|
|
224
|
+
'.',
|
|
225
|
+
{ callerSymbol: 'f' },
|
|
226
|
+
);
|
|
227
|
+
const names = rows.map((row) => row.name);
|
|
228
|
+
assert.ok(names.includes('test'), `expected 'test' in ${JSON.stringify(names)}`);
|
|
229
|
+
assert.ok(names.includes('inside'), `expected 'inside' in ${JSON.stringify(names)}`);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test('a multiline object-literal division does not swallow the rest of the file', () => {
|
|
233
|
+
// Probe: the `/` sits on its own line after `{}`. A line-break heuristic
|
|
234
|
+
// reads it as a regex, runs past the closing brace and attributes the
|
|
235
|
+
// following top-level call as a callee of f.
|
|
236
|
+
const rel = 'probe.mjs';
|
|
237
|
+
const text = 'function f(){ const x = {}\n/ 2; } outside();\n';
|
|
238
|
+
const node = {
|
|
239
|
+
rel,
|
|
240
|
+
abs: rel,
|
|
241
|
+
lang: 'javascript',
|
|
242
|
+
fingerprint: '',
|
|
243
|
+
symbols: [],
|
|
244
|
+
topLevelTypes: [],
|
|
245
|
+
resolvedImports: [],
|
|
246
|
+
rawImports: [],
|
|
247
|
+
};
|
|
248
|
+
const graph = {
|
|
249
|
+
nodes: new Map([[rel, node]]),
|
|
250
|
+
reverse: new Map(),
|
|
251
|
+
_sourceTextCache: new Map([[rel, { fingerprint: '', text }]]),
|
|
252
|
+
_sourceLinesCache: new Map(),
|
|
253
|
+
_maskedLinesCache: new Map(),
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
// The discriminator: a bare line break says "regex", the tracked brace
|
|
257
|
+
// kind says "division". The scanner uses the tracked kind.
|
|
258
|
+
assert.equal(_atJsRegexPosition(text, text.indexOf('/ 2')), true);
|
|
259
|
+
assert.equal(_atJsRegexPosition(text, text.indexOf('/ 2'), 'object'), false);
|
|
260
|
+
|
|
261
|
+
const rows = _extractCallees(
|
|
262
|
+
graph,
|
|
263
|
+
{ rel, lang: 'javascript', line: 1, col: 1, declarationLike: true },
|
|
264
|
+
'.',
|
|
265
|
+
{ callerSymbol: 'f' },
|
|
266
|
+
);
|
|
267
|
+
assert.deepEqual(rows.map((row) => row.name), []);
|
|
268
|
+
});
|
|
@@ -3,12 +3,12 @@ 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: 'Read-only; safe to batch in parallel. Source-file structure, symbol relations, and flow. Exact identifiers route directly to find_symbol/references/callers/callees; symbol-name keywords use symbol_search/search. Text, literals, and regex belong to grep. File modes use files[]; symbol modes use symbols[].
|
|
6
|
+
description: 'Read-only; safe to batch in parallel. Source-file structure, symbol relations, and flow. mode:symbols with files[] is the cheap direct outline of known source files: declarations with line numbers, no file body. Exact identifiers route directly to find_symbol/references/callers/callees; symbol-name keywords use symbol_search/search. Text, literals, and regex belong to grep. File modes use files[]; symbol modes use symbols[]. find_symbol returns declaration/body; references returns declaration/usages plus optional body; callers/callees return locations.',
|
|
7
7
|
inputSchema: {
|
|
8
8
|
type: 'object',
|
|
9
9
|
properties: {
|
|
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, and impact. symbols with files[] returns an optionally filtered file outline; remaining modes use symbols[].' },
|
|
11
|
-
files: { anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }], description: '
|
|
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, and impact. symbols with files[] returns an optionally filtered file outline of those exact files. It needs no prior search; remaining modes use symbols[].' },
|
|
11
|
+
files: { anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }], description: 'Source path(s), project-relative or absolute; required by file modes, optional to scope symbol modes.' },
|
|
12
12
|
symbols: { anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }], description: 'Exact identifiers or symbol-name keywords; batch in one symbols[] call; required by symbol modes, optional filter for mode:symbols.' },
|
|
13
13
|
body: { type: 'boolean', description: 'Include declaration body: find_symbol defaults true; references is opt-in.' },
|
|
14
14
|
limit: { type: 'integer', minimum: 1, maximum: 500, description: 'Maximum results; modes may enforce lower caps.' },
|