@unerr-ai/unerr 0.2.1 → 0.2.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/README.md +36 -45
- package/dist/cli.js +37443 -36022
- package/package.json +2 -1
- package/dist/behaviors/agent-llm-bridge.js +0 -166
- package/dist/behaviors/architecture-guard.js +0 -256
- package/dist/behaviors/auto-doc.js +0 -247
- package/dist/behaviors/cascade-guard.js +0 -289
- package/dist/behaviors/change-narrative.js +0 -270
- package/dist/behaviors/convention-drift.js +0 -290
- package/dist/behaviors/framework.js +0 -235
- package/dist/behaviors/guard-formatter.js +0 -44
- package/dist/behaviors/incomplete-work.js +0 -270
- package/dist/behaviors/loop-breaker.js +0 -300
- package/dist/behaviors/session-continuity.js +0 -208
- package/dist/commands/branches.js +0 -97
- package/dist/commands/check-commit.js +0 -225
- package/dist/commands/compress-output.js +0 -64
- package/dist/commands/config-verify.js +0 -243
- package/dist/commands/daemon.js +0 -905
- package/dist/commands/dashboard.js +0 -52
- package/dist/commands/debug.js +0 -200
- package/dist/commands/enrich.js +0 -184
- package/dist/commands/exec.js +0 -233
- package/dist/commands/gain.js +0 -156
- package/dist/commands/hook.js +0 -88
- package/dist/commands/index.js +0 -88
- package/dist/commands/init.js +0 -74
- package/dist/commands/install.js +0 -505
- package/dist/commands/learn.js +0 -116
- package/dist/commands/manifest.js +0 -193
- package/dist/commands/rewind.js +0 -103
- package/dist/commands/serve.js +0 -19
- package/dist/commands/setup-wizard.js +0 -414
- package/dist/commands/skills.js +0 -64
- package/dist/commands/stats.js +0 -20
- package/dist/commands/status.js +0 -654
- package/dist/commands/timeline.js +0 -139
- package/dist/commands/uninstall.js +0 -230
- package/dist/components/App.js +0 -109
- package/dist/components/Banner.js +0 -12
- package/dist/components/ConfirmPrompt.js +0 -25
- package/dist/components/DriftSummary.js +0 -23
- package/dist/components/GradeBadge.js +0 -15
- package/dist/components/HealthCard.js +0 -18
- package/dist/components/InkSpinner.js +0 -22
- package/dist/components/InputBox.js +0 -17
- package/dist/components/KeyValue.js +0 -13
- package/dist/components/MessageList.js +0 -14
- package/dist/components/ProgressBar.js +0 -26
- package/dist/components/Section.js +0 -16
- package/dist/components/SessionSummaryCard.js +0 -73
- package/dist/components/StartupDisplay.js +0 -24
- package/dist/components/StatusDashboard.js +0 -57
- package/dist/components/StatusLine.js +0 -8
- package/dist/components/StepLine.js +0 -22
- package/dist/components/Theme.js +0 -20
- package/dist/components/ToolProgress.js +0 -8
- package/dist/components/ViolationList.js +0 -21
- package/dist/components/render.js +0 -13
- package/dist/config/agent-registry.js +0 -237
- package/dist/config/claude-settings-hooks.js +0 -304
- package/dist/config/hook-installer.js +0 -65
- package/dist/config/instruction-writer.js +0 -388
- package/dist/config/mcp-config-writer.js +0 -266
- package/dist/config/settings.js +0 -174
- package/dist/config/tool-detector.js +0 -42
- package/dist/config/value-surfacing.js +0 -119
- package/dist/core/context-assembly.js +0 -108
- package/dist/core/conversation.js +0 -33
- package/dist/core/local-chat-provider.js +0 -475
- package/dist/core/provider-factory.js +0 -55
- package/dist/core/providers.js +0 -90
- package/dist/core/query-engine.js +0 -174
- package/dist/daemon/api.js +0 -312
- package/dist/daemon/autostart.js +0 -119
- package/dist/daemon/bootstrap.js +0 -39
- package/dist/daemon/client.js +0 -164
- package/dist/daemon/detect-ci.js +0 -81
- package/dist/daemon/platform-linux.js +0 -146
- package/dist/daemon/platform-macos.js +0 -134
- package/dist/daemon/platform-windows.js +0 -116
- package/dist/daemon/process-manager.js +0 -299
- package/dist/daemon/protocol.js +0 -23
- package/dist/daemon/registry.js +0 -270
- package/dist/daemon/settings-schema.js +0 -72
- package/dist/daemon/system-health.js +0 -134
- package/dist/daemon/version-checker.js +0 -262
- package/dist/daemon/warm-start.js +0 -223
- package/dist/entrypoints/cli.js +0 -1043
- package/dist/entrypoints/daemon.js +0 -380
- package/dist/entrypoints/repl.js +0 -147
- package/dist/hooks/adapters/claude-code.js +0 -90
- package/dist/hooks/adapters/cline.js +0 -100
- package/dist/hooks/adapters/cursor.js +0 -98
- package/dist/hooks/hook-dedup.js +0 -79
- package/dist/hooks/hook-runner.js +0 -113
- package/dist/hooks/navigation-hooks.js +0 -175
- package/dist/hooks/prompt-hooks.js +0 -63
- package/dist/hooks/shell-hooks.js +0 -47
- package/dist/ignore.js +0 -111
- package/dist/intelligence/approach-suggester.js +0 -61
- package/dist/intelligence/ast-extractor.js +0 -2615
- package/dist/intelligence/ast-worker.js +0 -34
- package/dist/intelligence/background-indexer.js +0 -121
- package/dist/intelligence/blast-radius.js +0 -200
- package/dist/intelligence/community-detection.js +0 -691
- package/dist/intelligence/community-detector.js +0 -184
- package/dist/intelligence/computation-scheduler.js +0 -75
- package/dist/intelligence/confidence-propagation.js +0 -47
- package/dist/intelligence/convention-detector.js +0 -242
- package/dist/intelligence/convention-learner.js +0 -205
- package/dist/intelligence/convention-matcher.js +0 -205
- package/dist/intelligence/cozo-schema.js +0 -376
- package/dist/intelligence/decision-point-detector.js +0 -90
- package/dist/intelligence/deep-dive-tools.js +0 -586
- package/dist/intelligence/durability-scorer.js +0 -84
- package/dist/intelligence/exploration-cost.js +0 -204
- package/dist/intelligence/exploration-pattern-tracker.js +0 -61
- package/dist/intelligence/fact-generator.js +0 -322
- package/dist/intelligence/facts-schema.js +0 -90
- package/dist/intelligence/file-intelligence.js +0 -59
- package/dist/intelligence/graph-holder.js +0 -220
- package/dist/intelligence/graph-temporal-joiner.js +0 -238
- package/dist/intelligence/health-grade.js +0 -423
- package/dist/intelligence/health-grader.js +0 -200
- package/dist/intelligence/health-map-data.js +0 -259
- package/dist/intelligence/import-symbols.js +0 -136
- package/dist/intelligence/incremental-indexer.js +0 -658
- package/dist/intelligence/indexer/centrality.js +0 -62
- package/dist/intelligence/indexer/cfg-context.js +0 -95
- package/dist/intelligence/indexer/confidence.js +0 -34
- package/dist/intelligence/indexer/cross-file-resolver.js +0 -104
- package/dist/intelligence/indexer/edge-repair.js +0 -89
- package/dist/intelligence/indexer/entity-key.js +0 -17
- package/dist/intelligence/indexer/export-map.js +0 -132
- package/dist/intelligence/indexer/git-cochange.js +0 -128
- package/dist/intelligence/indexer/graph-patch.js +0 -147
- package/dist/intelligence/indexer/incremental.js +0 -78
- package/dist/intelligence/indexer/ingest.js +0 -160
- package/dist/intelligence/indexer/language-detect.js +0 -226
- package/dist/intelligence/indexer/metadata.js +0 -63
- package/dist/intelligence/indexer/mutation-tracker.js +0 -79
- package/dist/intelligence/indexer/orchestrator.js +0 -155
- package/dist/intelligence/indexer/plugin-interface.js +0 -31
- package/dist/intelligence/indexer/plugins/csharp.js +0 -440
- package/dist/intelligence/indexer/plugins/go.js +0 -335
- package/dist/intelligence/indexer/plugins/java.js +0 -370
- package/dist/intelligence/indexer/plugins/python.js +0 -358
- package/dist/intelligence/indexer/plugins/regex-fallback.js +0 -82
- package/dist/intelligence/indexer/plugins/ruby.js +0 -290
- package/dist/intelligence/indexer/plugins/rust.js +0 -484
- package/dist/intelligence/indexer/plugins/tier2-generic.js +0 -310
- package/dist/intelligence/indexer/plugins/typescript.js +0 -456
- package/dist/intelligence/indexer/resource-monitor.js +0 -93
- package/dist/intelligence/indexer/scip/decoder.js +0 -253
- package/dist/intelligence/indexer/scip/detector.js +0 -232
- package/dist/intelligence/indexer/scip/downloader.js +0 -427
- package/dist/intelligence/indexer/scip/fallback.js +0 -34
- package/dist/intelligence/indexer/scip/merger.js +0 -109
- package/dist/intelligence/indexer/scip/orchestrator.js +0 -433
- package/dist/intelligence/indexer/scip/runner.js +0 -98
- package/dist/intelligence/indexer/snapshot.js +0 -66
- package/dist/intelligence/indexer/test-detector.js +0 -196
- package/dist/intelligence/indexer/watch-integration.js +0 -61
- package/dist/intelligence/indexer/worker.js +0 -85
- package/dist/intelligence/local-convention-detector.js +0 -437
- package/dist/intelligence/local-embeddings.js +0 -190
- package/dist/intelligence/local-graph.js +0 -1946
- package/dist/intelligence/local-indexer.js +0 -1575
- package/dist/intelligence/local-llm.js +0 -163
- package/dist/intelligence/local-rule-generator.js +0 -154
- package/dist/intelligence/local-snapshot.js +0 -213
- package/dist/intelligence/negative-knowledge.js +0 -103
- package/dist/intelligence/persistent-db.js +0 -85
- package/dist/intelligence/query-router.js +0 -2556
- package/dist/intelligence/risk-classifier.js +0 -116
- package/dist/intelligence/rule-evaluator.js +0 -380
- package/dist/intelligence/rule-generator.js +0 -49
- package/dist/intelligence/search-index.js +0 -173
- package/dist/intelligence/semantic/docstring-extractor.js +0 -67
- package/dist/intelligence/semantic/embedding-store.js +0 -52
- package/dist/intelligence/semantic/enrichment-orchestrator.js +0 -48
- package/dist/intelligence/semantic/git-message-miner.js +0 -114
- package/dist/intelligence/semantic/identifier-tokenizer.js +0 -51
- package/dist/intelligence/semantic/node2vec-embeddings.js +0 -71
- package/dist/intelligence/semantic/node2vec-walks.js +0 -103
- package/dist/intelligence/semantic/path-domain-inference.js +0 -112
- package/dist/intelligence/semantic/similarity-engine.js +0 -60
- package/dist/intelligence/semantic/tfidf-vectors.js +0 -88
- package/dist/intelligence/session-brief-builder.js +0 -159
- package/dist/intelligence/session-context.js +0 -221
- package/dist/intelligence/session-health-monitor.js +0 -211
- package/dist/intelligence/session-narrative.js +0 -197
- package/dist/intelligence/session-pattern-analyzer.js +0 -218
- package/dist/intelligence/signal-scorer.js +0 -390
- package/dist/intelligence/signal-show-store.js +0 -182
- package/dist/intelligence/smart-truncate.js +0 -158
- package/dist/intelligence/subgraph-cache.js +0 -88
- package/dist/intelligence/temporal-facts.js +0 -494
- package/dist/intelligence/token-estimator.js +0 -100
- package/dist/intelligence/tool-injector.js +0 -87
- package/dist/intelligence/tree-sitter-loader.js +0 -71
- package/dist/intelligence/worker-pool.js +0 -116
- package/dist/proxy/arg-validator.js +0 -79
- package/dist/proxy/auto-bootstrap.js +0 -167
- package/dist/proxy/bridge.js +0 -147
- package/dist/proxy/budget-enforcer.js +0 -70
- package/dist/proxy/compression-quality-monitor.js +0 -160
- package/dist/proxy/compression-stats.js +0 -51
- package/dist/proxy/context-rot-detector.js +0 -137
- package/dist/proxy/drift-detector.js +0 -139
- package/dist/proxy/efficiency-tracker.js +0 -79
- package/dist/proxy/fact-ranking.js +0 -154
- package/dist/proxy/format-encoder.js +0 -266
- package/dist/proxy/http-transport.js +0 -90
- package/dist/proxy/lifecycle-actor.js +0 -55
- package/dist/proxy/lifecycle-machine.js +0 -187
- package/dist/proxy/log-tailer.js +0 -265
- package/dist/proxy/model-pricing.js +0 -98
- package/dist/proxy/network-firewall.js +0 -141
- package/dist/proxy/nudge-state.js +0 -93
- package/dist/proxy/output-compressor.js +0 -185
- package/dist/proxy/pid-lock.js +0 -291
- package/dist/proxy/proxy-context.js +0 -11
- package/dist/proxy/proxy.js +0 -2633
- package/dist/proxy/response-enrichment.js +0 -32
- package/dist/proxy/response-envelope.js +0 -313
- package/dist/proxy/session-dedup.js +0 -82
- package/dist/proxy/session-legend.js +0 -30
- package/dist/proxy/session-persistence.js +0 -210
- package/dist/proxy/session-resume.js +0 -94
- package/dist/proxy/session-stats.js +0 -513
- package/dist/proxy/shell-classifier.js +0 -1346
- package/dist/proxy/shell-compression-log.js +0 -93
- package/dist/proxy/shell-compressor.js +0 -390
- package/dist/proxy/shell-graph-boost.js +0 -202
- package/dist/proxy/shell-monitor-map.js +0 -18
- package/dist/proxy/shell-stats.js +0 -54
- package/dist/proxy/shell-strategies/cloud.js +0 -215
- package/dist/proxy/shell-strategies/diff.js +0 -159
- package/dist/proxy/shell-strategies/error-diagnostic.js +0 -796
- package/dist/proxy/shell-strategies/filter-dsl.js +0 -358
- package/dist/proxy/shell-strategies/git-status.js +0 -177
- package/dist/proxy/shell-strategies/key-value.js +0 -193
- package/dist/proxy/shell-strategies/log-text.js +0 -154
- package/dist/proxy/shell-strategies/omni.js +0 -188
- package/dist/proxy/shell-strategies/progress.js +0 -55
- package/dist/proxy/shell-strategies/redact.js +0 -76
- package/dist/proxy/shell-strategies/structured.js +0 -241
- package/dist/proxy/shell-strategies/tabular.js +0 -243
- package/dist/proxy/shell-strategies/test-results-types.js +0 -13
- package/dist/proxy/shell-strategies/test-results.js +0 -784
- package/dist/proxy/shell-strategies/tree-paths.js +0 -144
- package/dist/proxy/shell-strategies/yaml.js +0 -182
- package/dist/proxy/shell-tee.js +0 -111
- package/dist/proxy/signal-dedup.js +0 -171
- package/dist/proxy/startup-renderer.js +0 -158
- package/dist/proxy/task-token-display.js +0 -38
- package/dist/proxy/token-counter.js +0 -61
- package/dist/proxy/tool-clusters.js +0 -273
- package/dist/proxy/tool-definitions.js +0 -525
- package/dist/proxy/transport-mux.js +0 -229
- package/dist/proxy/wire-cap.js +0 -268
- package/dist/rules/developer.mozilla.org.json +0 -9
- package/dist/rules/github.com.json +0 -21
- package/dist/schemas/api/skills.js +0 -19
- package/dist/schemas/common/errors.js +0 -7
- package/dist/schemas/common/headers.js +0 -5
- package/dist/schemas/entities/edge.js +0 -25
- package/dist/schemas/entities/entity.js +0 -22
- package/dist/schemas/entities/rule.js +0 -18
- package/dist/schemas/index.js +0 -14
- package/dist/server/event-bus.js +0 -59
- package/dist/server/http.js +0 -156
- package/dist/server/middleware.js +0 -70
- package/dist/server/routes/drift.js +0 -97
- package/dist/server/routes/intelligence.js +0 -1217
- package/dist/server/routes/reasoning-quality.js +0 -444
- package/dist/server/routes/session.js +0 -86
- package/dist/server/routes/stream.js +0 -120
- package/dist/server/routes/system.js +0 -73
- package/dist/server/routes/temporal.js +0 -170
- package/dist/server/routes/timeline.js +0 -232
- package/dist/server/routes/token-flow.js +0 -403
- package/dist/skills/effectiveness-tracker.js +0 -93
- package/dist/skills/local-pack.js +0 -380
- package/dist/skills/resolver.js +0 -495
- package/dist/state-detector.js +0 -83
- package/dist/timeline/intent-detector.js +0 -263
- package/dist/timeline/loop-miner.js +0 -140
- package/dist/timeline/open-threads.js +0 -49
- package/dist/timeline/signal-reinforcer.js +0 -62
- package/dist/timeline/timeline-bootstrap.js +0 -151
- package/dist/timeline/timeline-store.js +0 -618
- package/dist/tools/coding/bash.js +0 -49
- package/dist/tools/coding/file-edit.js +0 -72
- package/dist/tools/coding/file-outline.js +0 -227
- package/dist/tools/coding/file-read-protocol.js +0 -425
- package/dist/tools/coding/file-read.js +0 -35
- package/dist/tools/coding/file-write.js +0 -43
- package/dist/tools/coding/glob-tool.js +0 -109
- package/dist/tools/coding/grep.js +0 -162
- package/dist/tools/coding/index.js +0 -27
- package/dist/tools/intelligence/index.js +0 -269
- package/dist/tools/intelligence/record-fact.js +0 -48
- package/dist/tools/intelligence/timeline-markers.js +0 -130
- package/dist/tools/registry.js +0 -47
- package/dist/tools/types.js +0 -8
- package/dist/tracking/auto-snapshot-triggers.js +0 -246
- package/dist/tracking/branch-context.js +0 -115
- package/dist/tracking/branch-snapshot.js +0 -217
- package/dist/tracking/causal-bridge.js +0 -317
- package/dist/tracking/circuit-breaker.js +0 -147
- package/dist/tracking/commit-watcher.js +0 -114
- package/dist/tracking/context-ledger.js +0 -119
- package/dist/tracking/correction-detector.js +0 -324
- package/dist/tracking/drift-tracker.js +0 -874
- package/dist/tracking/durability-tracker.js +0 -94
- package/dist/tracking/entity-rewind.js +0 -200
- package/dist/tracking/file-hash-state.js +0 -114
- package/dist/tracking/git-attribution.js +0 -132
- package/dist/tracking/git-trailers.js +0 -171
- package/dist/tracking/intelligence-counter.js +0 -46
- package/dist/tracking/intent-correlator.js +0 -202
- package/dist/tracking/intent-encoder.js +0 -52
- package/dist/tracking/intent-token-tracker.js +0 -159
- package/dist/tracking/ledger-archiver.js +0 -94
- package/dist/tracking/ledger-chains.js +0 -245
- package/dist/tracking/metrics-store.js +0 -361
- package/dist/tracking/native-watcher.js +0 -131
- package/dist/tracking/offline-rewind.js +0 -295
- package/dist/tracking/pending-violations.js +0 -74
- package/dist/tracking/persistence-effectiveness.js +0 -167
- package/dist/tracking/prompt-durability.js +0 -202
- package/dist/tracking/quality-signals.js +0 -213
- package/dist/tracking/redactor.js +0 -73
- package/dist/tracking/rewind-engine.js +0 -161
- package/dist/tracking/session-history.js +0 -128
- package/dist/tracking/session-receipt.js +0 -88
- package/dist/tracking/session-summary-writer.js +0 -157
- package/dist/tracking/shadow-ledger.js +0 -321
- package/dist/tracking/stash-manager.js +0 -258
- package/dist/tracking/timeline-fork.js +0 -213
- package/dist/tracking/timeline.js +0 -69
- package/dist/tracking/token-flow.js +0 -276
- package/dist/tracking/turn-segmenter.js +0 -122
- package/dist/tracking/weekly-accumulator.js +0 -179
- package/dist/tracking/working-snapshots.js +0 -188
- package/dist/tracking/workspace-manifest.js +0 -176
- package/dist/transport/http.js +0 -102
- package/dist/utils/counterfactual.js +0 -65
- package/dist/utils/deep-link.js +0 -34
- package/dist/utils/detect.js +0 -193
- package/dist/utils/exec.js +0 -73
- package/dist/utils/file-logger.js +0 -87
- package/dist/utils/format-error.js +0 -29
- package/dist/utils/git.js +0 -181
- package/dist/utils/log.js +0 -57
- package/dist/utils/logger.js +0 -35
- package/dist/utils/mcp-content-json.js +0 -8
- package/dist/utils/session-logger.js +0 -154
- package/dist/utils/startup-log.js +0 -512
- package/dist/utils/ui.js +0 -56
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tree-sitter WASM Loader — lazy initialization with grammar caching.
|
|
3
|
-
*
|
|
4
|
-
* Loads tree-sitter and language grammars on-demand. Caches parsers
|
|
5
|
-
* per-language to avoid repeated WASM compilation.
|
|
6
|
-
*
|
|
7
|
-
* Performance: first load ~20ms, subsequent parses <1ms per file.
|
|
8
|
-
*/
|
|
9
|
-
let TreeSitter = null;
|
|
10
|
-
let initPromise = null;
|
|
11
|
-
const parserCache = new Map();
|
|
12
|
-
/**
|
|
13
|
-
* Initialize the Tree-sitter runtime. Idempotent — only loads once.
|
|
14
|
-
*/
|
|
15
|
-
export async function initTreeSitter() {
|
|
16
|
-
if (TreeSitter)
|
|
17
|
-
return TreeSitter;
|
|
18
|
-
if (initPromise)
|
|
19
|
-
return initPromise;
|
|
20
|
-
initPromise = (async () => {
|
|
21
|
-
const mod = await import("web-tree-sitter");
|
|
22
|
-
const TS = mod.default;
|
|
23
|
-
await TS.init();
|
|
24
|
-
TreeSitter = TS;
|
|
25
|
-
return TS;
|
|
26
|
-
})();
|
|
27
|
-
return initPromise;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Resolve the path to a tree-sitter WASM grammar file.
|
|
31
|
-
*/
|
|
32
|
-
function resolveGrammarPath(wasmName) {
|
|
33
|
-
try {
|
|
34
|
-
return require.resolve(`tree-sitter-wasms/out/${wasmName}`);
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
const { join } = require("node:path");
|
|
38
|
-
return join(process.cwd(), "node_modules", "tree-sitter-wasms", "out", wasmName);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Get a parser for a specific language. Caches the Language + Parser.
|
|
43
|
-
*/
|
|
44
|
-
export async function getParser(wasmName) {
|
|
45
|
-
const cached = parserCache.get(wasmName);
|
|
46
|
-
if (cached)
|
|
47
|
-
return cached;
|
|
48
|
-
const TS = await initTreeSitter();
|
|
49
|
-
const grammarPath = resolveGrammarPath(wasmName);
|
|
50
|
-
const language = await TS.Language.load(grammarPath);
|
|
51
|
-
const parser = new TS();
|
|
52
|
-
parser.setLanguage(language);
|
|
53
|
-
parserCache.set(wasmName, parser);
|
|
54
|
-
return parser;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Parse source code with a cached language parser.
|
|
58
|
-
*/
|
|
59
|
-
export async function parseSource(source, wasmName) {
|
|
60
|
-
const parser = await getParser(wasmName);
|
|
61
|
-
return parser.parse(source);
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Clear parser cache (for testing or memory reclaim).
|
|
65
|
-
*/
|
|
66
|
-
export function clearParserCache() {
|
|
67
|
-
for (const parser of parserCache.values()) {
|
|
68
|
-
parser.delete();
|
|
69
|
-
}
|
|
70
|
-
parserCache.clear();
|
|
71
|
-
}
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Worker pool manager for parallel AST parsing via tinypool.
|
|
3
|
-
*
|
|
4
|
-
* Lazy-initializes a thread pool on first use. Falls back to sequential
|
|
5
|
-
* parsing if worker threads are unavailable (e.g. restricted environments).
|
|
6
|
-
*/
|
|
7
|
-
import { existsSync } from "node:fs";
|
|
8
|
-
import { cpus } from "node:os";
|
|
9
|
-
import { dirname, join } from "node:path";
|
|
10
|
-
import { fileURLToPath } from "node:url";
|
|
11
|
-
import { createModuleLogger } from "../utils/logger.js";
|
|
12
|
-
import { extractEntities } from "./ast-extractor.js";
|
|
13
|
-
const log = createModuleLogger("worker-pool");
|
|
14
|
-
let pool = null;
|
|
15
|
-
let poolFailed = false;
|
|
16
|
-
function resolveWorkerPath() {
|
|
17
|
-
const currentDir = dirname(fileURLToPath(import.meta.url));
|
|
18
|
-
const tsPath = join(currentDir, "ast-worker.ts");
|
|
19
|
-
const jsPath = join(currentDir, "ast-worker.js");
|
|
20
|
-
if (existsSync(jsPath) && !existsSync(tsPath)) {
|
|
21
|
-
return { filename: jsPath, execArgv: [] };
|
|
22
|
-
}
|
|
23
|
-
if (existsSync(tsPath)) {
|
|
24
|
-
return {
|
|
25
|
-
filename: tsPath,
|
|
26
|
-
execArgv: ["--experimental-strip-types", "--no-warnings"],
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
return null;
|
|
30
|
-
}
|
|
31
|
-
async function getPool(options) {
|
|
32
|
-
if (poolFailed)
|
|
33
|
-
return null;
|
|
34
|
-
if (pool)
|
|
35
|
-
return pool;
|
|
36
|
-
const worker = resolveWorkerPath();
|
|
37
|
-
if (!worker) {
|
|
38
|
-
log.debug("Worker file not found, using sequential parsing");
|
|
39
|
-
poolFailed = true;
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
try {
|
|
43
|
-
const { default: Tinypool } = await import("tinypool");
|
|
44
|
-
const coreCount = cpus().length;
|
|
45
|
-
pool = new Tinypool({
|
|
46
|
-
filename: worker.filename,
|
|
47
|
-
maxThreads: options?.maxThreads ?? Math.max(1, coreCount - 1),
|
|
48
|
-
minThreads: options?.minThreads ?? 1,
|
|
49
|
-
execArgv: worker.execArgv,
|
|
50
|
-
});
|
|
51
|
-
return pool;
|
|
52
|
-
}
|
|
53
|
-
catch (err) {
|
|
54
|
-
log.warn("Worker threads unavailable, falling back to sequential parsing", err);
|
|
55
|
-
poolFailed = true;
|
|
56
|
-
return null;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
function parseSequential(files) {
|
|
60
|
-
return files.map(({ filePath, content }) => {
|
|
61
|
-
const start = performance.now();
|
|
62
|
-
let entities;
|
|
63
|
-
try {
|
|
64
|
-
entities = extractEntities(content, filePath);
|
|
65
|
-
}
|
|
66
|
-
catch {
|
|
67
|
-
entities = [];
|
|
68
|
-
}
|
|
69
|
-
return {
|
|
70
|
-
filePath,
|
|
71
|
-
entities,
|
|
72
|
-
durationMs: performance.now() - start,
|
|
73
|
-
};
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
export async function parseFiles(files, options) {
|
|
77
|
-
if (files.length === 0)
|
|
78
|
-
return [];
|
|
79
|
-
const workerPool = await getPool(options);
|
|
80
|
-
if (!workerPool) {
|
|
81
|
-
return parseSequential(files);
|
|
82
|
-
}
|
|
83
|
-
const results = await Promise.all(files.map(async ({ filePath, content }) => {
|
|
84
|
-
const start = performance.now();
|
|
85
|
-
try {
|
|
86
|
-
const entities = await workerPool.run({
|
|
87
|
-
filePath,
|
|
88
|
-
content,
|
|
89
|
-
});
|
|
90
|
-
return {
|
|
91
|
-
filePath,
|
|
92
|
-
entities,
|
|
93
|
-
durationMs: performance.now() - start,
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
catch (err) {
|
|
97
|
-
log.debug("Worker error for %s: %s", filePath, err);
|
|
98
|
-
return {
|
|
99
|
-
filePath,
|
|
100
|
-
entities: [],
|
|
101
|
-
durationMs: performance.now() - start,
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
}));
|
|
105
|
-
return results;
|
|
106
|
-
}
|
|
107
|
-
export async function destroy() {
|
|
108
|
-
if (pool) {
|
|
109
|
-
await pool.destroy();
|
|
110
|
-
pool = null;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
export function resetPoolState() {
|
|
114
|
-
pool = null;
|
|
115
|
-
poolFailed = false;
|
|
116
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/** Map from canonical schema key → accepted aliases. */
|
|
2
|
-
const ALIAS_MAP = {
|
|
3
|
-
key: ["entity_name", "entity"],
|
|
4
|
-
file_path: ["file", "path"],
|
|
5
|
-
};
|
|
6
|
-
/**
|
|
7
|
-
* Rewrite alias args onto their canonical name. Mutates `args` in place
|
|
8
|
-
* (the dispatch loop owns this object). Aliases only apply when the
|
|
9
|
-
* canonical key isn't already set AND the tool's schema actually declares
|
|
10
|
-
* the canonical property — so we never invent fields on tools that don't
|
|
11
|
-
* accept them.
|
|
12
|
-
*/
|
|
13
|
-
export function normalizeArgAliases(toolDef, args) {
|
|
14
|
-
const props = toolDef.inputSchema.properties;
|
|
15
|
-
for (const [canonical, aliases] of Object.entries(ALIAS_MAP)) {
|
|
16
|
-
if (!(canonical in props))
|
|
17
|
-
continue;
|
|
18
|
-
if (args[canonical] !== undefined &&
|
|
19
|
-
args[canonical] !== null &&
|
|
20
|
-
args[canonical] !== "") {
|
|
21
|
-
continue;
|
|
22
|
-
}
|
|
23
|
-
for (const alias of aliases) {
|
|
24
|
-
const v = args[alias];
|
|
25
|
-
if (typeof v === "string" && v.trim() !== "") {
|
|
26
|
-
args[canonical] = v;
|
|
27
|
-
break;
|
|
28
|
-
}
|
|
29
|
-
if (typeof v === "number" || typeof v === "boolean") {
|
|
30
|
-
args[canonical] = v;
|
|
31
|
-
break;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Check that every `required` field on the tool's schema is present and
|
|
38
|
-
* non-empty in `args`. Returns null on success, or a structured failure
|
|
39
|
-
* the dispatch loop can serialize back to the caller.
|
|
40
|
-
*
|
|
41
|
-
* Empty-string and whitespace-only values count as missing — this is the
|
|
42
|
-
* Datalog-filter-mismatch failure mode that motivated the validator in
|
|
43
|
-
* the first place (`recall_facts({scope:""}) → 0 rows silently`).
|
|
44
|
-
*/
|
|
45
|
-
export function validateRequiredArgs(toolDef, args) {
|
|
46
|
-
const required = toolDef.inputSchema.required ?? [];
|
|
47
|
-
if (required.length === 0)
|
|
48
|
-
return null;
|
|
49
|
-
const missing = required.filter((field) => {
|
|
50
|
-
const v = args[field];
|
|
51
|
-
if (v === undefined || v === null)
|
|
52
|
-
return true;
|
|
53
|
-
if (typeof v === "string" && v.trim() === "")
|
|
54
|
-
return true;
|
|
55
|
-
return false;
|
|
56
|
-
});
|
|
57
|
-
if (missing.length === 0)
|
|
58
|
-
return null;
|
|
59
|
-
const props = toolDef.inputSchema.properties;
|
|
60
|
-
const details = missing
|
|
61
|
-
.map((m) => {
|
|
62
|
-
const desc = props[m]?.description?.trim();
|
|
63
|
-
return desc ? `${m}: ${desc}` : `${m}: required`;
|
|
64
|
-
})
|
|
65
|
-
.join("; ");
|
|
66
|
-
return {
|
|
67
|
-
error: `${toolDef.name}: missing required parameter(s): ${missing.join(", ")}`,
|
|
68
|
-
required: missing,
|
|
69
|
-
details,
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Convenience: normalize aliases then validate in one call. Use this in
|
|
74
|
-
* the MCP dispatch loop right after destructuring the request.
|
|
75
|
-
*/
|
|
76
|
-
export function aliasAndValidate(toolDef, args) {
|
|
77
|
-
normalizeArgAliases(toolDef, args);
|
|
78
|
-
return validateRequiredArgs(toolDef, args);
|
|
79
|
-
}
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PARSE Mode — regex-based entity extraction for degraded operation.
|
|
3
|
-
*
|
|
4
|
-
* Used when no CozoDB graph is available (no cozo-node binary).
|
|
5
|
-
* Provides basic entity query methods that mirror the CozoGraphStore interface.
|
|
6
|
-
*/
|
|
7
|
-
/**
|
|
8
|
-
* Extract entities from a source file using regex patterns.
|
|
9
|
-
* Intentionally simple — no tree-sitter dependency for PARSE mode.
|
|
10
|
-
*/
|
|
11
|
-
export function extractEntitiesFromSource(filePath, content) {
|
|
12
|
-
const entities = [];
|
|
13
|
-
const lines = content.split("\n");
|
|
14
|
-
for (let i = 0; i < lines.length; i++) {
|
|
15
|
-
const line = lines[i] ?? "";
|
|
16
|
-
const lineNum = i + 1;
|
|
17
|
-
// Export function / async function
|
|
18
|
-
const funcMatch = line.match(/^(?:export\s+)?(?:async\s+)?function\s+(\w+)\s*(\([^)]*\))/);
|
|
19
|
-
if (funcMatch) {
|
|
20
|
-
const name = funcMatch[1] ?? "";
|
|
21
|
-
const params = funcMatch[2] ?? "";
|
|
22
|
-
entities.push({
|
|
23
|
-
key: `${filePath}::${name}`,
|
|
24
|
-
name,
|
|
25
|
-
kind: "function",
|
|
26
|
-
file_path: filePath,
|
|
27
|
-
line_start: lineNum,
|
|
28
|
-
signature: `${name}${params}`,
|
|
29
|
-
});
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
// Arrow function: export const name = (...) =>
|
|
33
|
-
const arrowMatch = line.match(/^(?:export\s+)?(?:const|let)\s+(\w+)\s*=\s*(?:async\s+)?\(([^)]*)\)\s*(?::\s*\w[^=]*)?\s*=>/);
|
|
34
|
-
if (arrowMatch) {
|
|
35
|
-
const name = arrowMatch[1] ?? "";
|
|
36
|
-
const params = arrowMatch[2] ?? "";
|
|
37
|
-
entities.push({
|
|
38
|
-
key: `${filePath}::${name}`,
|
|
39
|
-
name,
|
|
40
|
-
kind: "function",
|
|
41
|
-
file_path: filePath,
|
|
42
|
-
line_start: lineNum,
|
|
43
|
-
signature: `${name}(${params})`,
|
|
44
|
-
});
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
// Class declaration
|
|
48
|
-
const classMatch = line.match(/^(?:export\s+)?(?:abstract\s+)?class\s+(\w+)/);
|
|
49
|
-
if (classMatch) {
|
|
50
|
-
const name = classMatch[1] ?? "";
|
|
51
|
-
entities.push({
|
|
52
|
-
key: `${filePath}::${name}`,
|
|
53
|
-
name,
|
|
54
|
-
kind: "class",
|
|
55
|
-
file_path: filePath,
|
|
56
|
-
line_start: lineNum,
|
|
57
|
-
signature: name,
|
|
58
|
-
});
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
// Interface declaration
|
|
62
|
-
const ifaceMatch = line.match(/^(?:export\s+)?interface\s+(\w+)/);
|
|
63
|
-
if (ifaceMatch) {
|
|
64
|
-
const name = ifaceMatch[1] ?? "";
|
|
65
|
-
entities.push({
|
|
66
|
-
key: `${filePath}::${name}`,
|
|
67
|
-
name,
|
|
68
|
-
kind: "interface",
|
|
69
|
-
file_path: filePath,
|
|
70
|
-
line_start: lineNum,
|
|
71
|
-
signature: name,
|
|
72
|
-
});
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
// Type alias
|
|
76
|
-
const typeMatch = line.match(/^(?:export\s+)?type\s+(\w+)\s*=/);
|
|
77
|
-
if (typeMatch) {
|
|
78
|
-
const name = typeMatch[1] ?? "";
|
|
79
|
-
entities.push({
|
|
80
|
-
key: `${filePath}::${name}`,
|
|
81
|
-
name,
|
|
82
|
-
kind: "type",
|
|
83
|
-
file_path: filePath,
|
|
84
|
-
line_start: lineNum,
|
|
85
|
-
signature: name,
|
|
86
|
-
});
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
// Method declarations inside classes (indented)
|
|
90
|
-
const methodMatch = line.match(/^\s+(?:async\s+)?(?:static\s+)?(?:private\s+|protected\s+|public\s+)?(\w+)\s*\(([^)]*)\)\s*(?::\s*\S+)?\s*\{/);
|
|
91
|
-
if (methodMatch &&
|
|
92
|
-
methodMatch[1] !== "if" &&
|
|
93
|
-
methodMatch[1] !== "for" &&
|
|
94
|
-
methodMatch[1] !== "while" &&
|
|
95
|
-
methodMatch[1] !== "switch" &&
|
|
96
|
-
methodMatch[1] !== "catch") {
|
|
97
|
-
// Try to find the enclosing class
|
|
98
|
-
const className = findEnclosingClass(lines, i);
|
|
99
|
-
const name = methodMatch[1] ?? "";
|
|
100
|
-
entities.push({
|
|
101
|
-
key: `${filePath}::${className ? `${className}.` : ""}${name}`,
|
|
102
|
-
name: className ? `${className}.${name}` : name,
|
|
103
|
-
kind: "method",
|
|
104
|
-
file_path: filePath,
|
|
105
|
-
line_start: lineNum,
|
|
106
|
-
signature: `${name}(${methodMatch[2]})`,
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
return entities;
|
|
111
|
-
}
|
|
112
|
-
function findEnclosingClass(lines, currentLine) {
|
|
113
|
-
for (let i = currentLine - 1; i >= 0; i--) {
|
|
114
|
-
const match = lines[i]?.match(/^(?:export\s+)?(?:abstract\s+)?class\s+(\w+)/);
|
|
115
|
-
if (match)
|
|
116
|
-
return match[1];
|
|
117
|
-
}
|
|
118
|
-
return null;
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* In-memory entity index for PARSE mode.
|
|
122
|
-
* Provides basic query methods that mirror the CozoGraphStore interface.
|
|
123
|
-
*/
|
|
124
|
-
export class ParseModeIndex {
|
|
125
|
-
entities = [];
|
|
126
|
-
byKey = new Map();
|
|
127
|
-
byFile = new Map();
|
|
128
|
-
addEntities(entities) {
|
|
129
|
-
for (const e of entities) {
|
|
130
|
-
this.entities.push(e);
|
|
131
|
-
this.byKey.set(e.key, e);
|
|
132
|
-
const fileEntities = this.byFile.get(e.file_path) ?? [];
|
|
133
|
-
fileEntities.push(e);
|
|
134
|
-
this.byFile.set(e.file_path, fileEntities);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
getEntity(key) {
|
|
138
|
-
return this.byKey.get(key) ?? null;
|
|
139
|
-
}
|
|
140
|
-
getEntitiesByFile(filePath) {
|
|
141
|
-
return this.byFile.get(filePath) ?? [];
|
|
142
|
-
}
|
|
143
|
-
search(query, limit = 20) {
|
|
144
|
-
const lower = query.toLowerCase();
|
|
145
|
-
const results = [];
|
|
146
|
-
for (const e of this.entities) {
|
|
147
|
-
if (e.name.toLowerCase().includes(lower) ||
|
|
148
|
-
e.key.toLowerCase().includes(lower)) {
|
|
149
|
-
results.push(e);
|
|
150
|
-
if (results.length >= limit)
|
|
151
|
-
break;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
return results;
|
|
155
|
-
}
|
|
156
|
-
getStats() {
|
|
157
|
-
return {
|
|
158
|
-
entityCount: this.entities.length,
|
|
159
|
-
fileCount: this.byFile.size,
|
|
160
|
-
};
|
|
161
|
-
}
|
|
162
|
-
clear() {
|
|
163
|
-
this.entities = [];
|
|
164
|
-
this.byKey.clear();
|
|
165
|
-
this.byFile.clear();
|
|
166
|
-
}
|
|
167
|
-
}
|
package/dist/proxy/bridge.js
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `unerr --mcp` bridge — thin stdio ↔ UDS relay with heartbeat.
|
|
3
|
-
*
|
|
4
|
-
* Sole job: forward MCP JSON-RPC frames between the IDE's stdio and the
|
|
5
|
-
* per-repo `unerr` process's UDS socket. The bridge imports **no** modules
|
|
6
|
-
* from `src/intelligence/` or `src/behaviors/` (Layer 12 DM-0 invariant) —
|
|
7
|
-
* all graph / fact / ledger work lives in the per-repo process.
|
|
8
|
-
*
|
|
9
|
-
* Heartbeat:
|
|
10
|
-
* - Sends JSON-RPC `unerr/ping` every 5 s, expects `unerr/pong` within 2 s
|
|
11
|
-
* - 3 missed heartbeats → bridge resolves with reason="daemon_dead"
|
|
12
|
-
* - Caller (cli.ts mcpBoot) prints an error and exits 1; DM-3 will add
|
|
13
|
-
* auto-spawn so the user never has to start the proxy manually.
|
|
14
|
-
*
|
|
15
|
-
* Protocol: newline-delimited JSON-RPC over UDS (matches TransportMux).
|
|
16
|
-
*/
|
|
17
|
-
import { connect } from "node:net";
|
|
18
|
-
/** stderr-only logger. stdout is MCP territory. */
|
|
19
|
-
const log = {
|
|
20
|
-
info: (msg) => process.stderr.write(`[unerr:bridge] ${msg}\n`),
|
|
21
|
-
warn: (msg) => process.stderr.write(`[unerr:bridge] WARN: ${msg}\n`),
|
|
22
|
-
};
|
|
23
|
-
const HEARTBEAT_INTERVAL_MS = 5_000;
|
|
24
|
-
const HEARTBEAT_TIMEOUT_MS = 2_000;
|
|
25
|
-
const MAX_MISSED_HEARTBEATS = 3;
|
|
26
|
-
/**
|
|
27
|
-
* Start bridging stdin/stdout to the proxy's UDS socket.
|
|
28
|
-
* Returns when the connection closes, with a reason indicating why.
|
|
29
|
-
*/
|
|
30
|
-
export function startUdsBridge(sockPath) {
|
|
31
|
-
return new Promise((resolve, reject) => {
|
|
32
|
-
const socket = connect(sockPath);
|
|
33
|
-
let heartbeatTimer;
|
|
34
|
-
let heartbeatTimeoutTimer;
|
|
35
|
-
let missedHeartbeats = 0;
|
|
36
|
-
let pendingPing = false;
|
|
37
|
-
let resolved = false;
|
|
38
|
-
function cleanup(reason) {
|
|
39
|
-
if (resolved)
|
|
40
|
-
return;
|
|
41
|
-
resolved = true;
|
|
42
|
-
if (heartbeatTimer)
|
|
43
|
-
clearInterval(heartbeatTimer);
|
|
44
|
-
if (heartbeatTimeoutTimer)
|
|
45
|
-
clearTimeout(heartbeatTimeoutTimer);
|
|
46
|
-
if (!socket.destroyed)
|
|
47
|
-
socket.destroy();
|
|
48
|
-
resolve({ reason });
|
|
49
|
-
}
|
|
50
|
-
socket.on("connect", () => {
|
|
51
|
-
log.info(`Connected to proxy at ${sockPath}`);
|
|
52
|
-
// stdin → UDS: forward MCP requests from IDE to proxy
|
|
53
|
-
process.stdin.on("data", (chunk) => {
|
|
54
|
-
if (!socket.destroyed) {
|
|
55
|
-
socket.write(chunk);
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
// UDS → stdout: forward MCP responses from proxy to IDE
|
|
59
|
-
socket.on("data", (data) => {
|
|
60
|
-
// Check if this is a heartbeat pong response
|
|
61
|
-
const str = data.toString();
|
|
62
|
-
if (str.includes('"unerr/pong"')) {
|
|
63
|
-
missedHeartbeats = 0;
|
|
64
|
-
pendingPing = false;
|
|
65
|
-
if (heartbeatTimeoutTimer) {
|
|
66
|
-
clearTimeout(heartbeatTimeoutTimer);
|
|
67
|
-
heartbeatTimeoutTimer = undefined;
|
|
68
|
-
}
|
|
69
|
-
// Filter out pong from data sent to IDE — find and remove the pong line
|
|
70
|
-
const lines = str.split("\n");
|
|
71
|
-
const filtered = lines.filter((l) => !l.includes('"unerr/pong"'));
|
|
72
|
-
const remaining = filtered.join("\n");
|
|
73
|
-
if (remaining.trim().length > 0) {
|
|
74
|
-
process.stdout.write(remaining);
|
|
75
|
-
}
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
process.stdout.write(data);
|
|
79
|
-
});
|
|
80
|
-
// Start heartbeat monitoring
|
|
81
|
-
heartbeatTimer = setInterval(() => {
|
|
82
|
-
if (socket.destroyed) {
|
|
83
|
-
cleanup("daemon_dead");
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
if (pendingPing) {
|
|
87
|
-
// Previous ping didn't get a pong
|
|
88
|
-
missedHeartbeats++;
|
|
89
|
-
if (missedHeartbeats >= MAX_MISSED_HEARTBEATS) {
|
|
90
|
-
log.warn(`${MAX_MISSED_HEARTBEATS} heartbeats missed — daemon appears dead`);
|
|
91
|
-
cleanup("daemon_dead");
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
// Send heartbeat ping as JSON-RPC notification
|
|
96
|
-
pendingPing = true;
|
|
97
|
-
const ping = JSON.stringify({
|
|
98
|
-
jsonrpc: "2.0",
|
|
99
|
-
method: "unerr/ping",
|
|
100
|
-
params: { ts: Date.now() },
|
|
101
|
-
});
|
|
102
|
-
try {
|
|
103
|
-
socket.write(`${ping}\n`);
|
|
104
|
-
}
|
|
105
|
-
catch {
|
|
106
|
-
cleanup("daemon_dead");
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
// Set timeout for this specific ping
|
|
110
|
-
heartbeatTimeoutTimer = setTimeout(() => {
|
|
111
|
-
// Ping timed out — increment missed count (handled on next interval)
|
|
112
|
-
heartbeatTimeoutTimer = undefined;
|
|
113
|
-
}, HEARTBEAT_TIMEOUT_MS);
|
|
114
|
-
}, HEARTBEAT_INTERVAL_MS);
|
|
115
|
-
});
|
|
116
|
-
socket.on("close", () => {
|
|
117
|
-
log.info("Proxy connection closed");
|
|
118
|
-
cleanup("socket_closed");
|
|
119
|
-
});
|
|
120
|
-
socket.on("error", (err) => {
|
|
121
|
-
log.warn(`Connection error: ${err.message}`);
|
|
122
|
-
if (resolved)
|
|
123
|
-
return;
|
|
124
|
-
// Connection errors before connect are fatal
|
|
125
|
-
if (!socket.connecting && !heartbeatTimer) {
|
|
126
|
-
reject(err);
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
cleanup("daemon_dead");
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
// If stdin closes (IDE disconnects), allow a grace period for pending
|
|
133
|
-
// responses before tearing down. Without this, piped input like
|
|
134
|
-
// echo '{"jsonrpc":"2.0",...}' | unerr --mcp
|
|
135
|
-
// closes stdin immediately, destroying the socket before the proxy
|
|
136
|
-
// can write its response back.
|
|
137
|
-
process.stdin.on("end", () => {
|
|
138
|
-
const grace = setTimeout(() => cleanup("stdin_closed"), 3_000);
|
|
139
|
-
socket.once("end", () => {
|
|
140
|
-
clearTimeout(grace);
|
|
141
|
-
cleanup("stdin_closed");
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
// Keep the process alive while bridging
|
|
145
|
-
process.stdin.resume();
|
|
146
|
-
});
|
|
147
|
-
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Budget Enforcer — enforces per-response token budgets.
|
|
3
|
-
*
|
|
4
|
-
* Responses exceeding the budget are smart-trimmed:
|
|
5
|
-
* 1. If text: truncate with "..." marker, preserving first/last segments
|
|
6
|
-
* 2. Adds _meta["dev.unerr/truncated"] = true
|
|
7
|
-
* 3. Adds _meta["dev.unerr/original_tokens"] with the pre-truncation count
|
|
8
|
-
*
|
|
9
|
-
* Only applies to text-heavy responses. Structured data (JSON objects with
|
|
10
|
-
* entity keys, blast radius) passes through unchanged.
|
|
11
|
-
*/
|
|
12
|
-
import { estimateTokenCount } from "../intelligence/token-estimator.js";
|
|
13
|
-
/**
|
|
14
|
-
* Enforce a token budget on text content.
|
|
15
|
-
* Returns the (possibly truncated) content and metadata.
|
|
16
|
-
*/
|
|
17
|
-
export function enforceBudget(content, budget, options = {}) {
|
|
18
|
-
const maxTokens = budget ?? options.defaultBudget ?? 4000;
|
|
19
|
-
const headRatio = options.preserveHeadRatio ?? 0.7;
|
|
20
|
-
const originalTokens = estimateTokenCount(content);
|
|
21
|
-
if (originalTokens <= maxTokens) {
|
|
22
|
-
return {
|
|
23
|
-
content,
|
|
24
|
-
truncated: false,
|
|
25
|
-
originalTokens,
|
|
26
|
-
deliveredTokens: originalTokens,
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
const lines = content.split("\n");
|
|
30
|
-
const headLines = Math.floor(lines.length * headRatio);
|
|
31
|
-
const tailLines = Math.max(3, Math.floor(lines.length * 0.1));
|
|
32
|
-
const head = lines.slice(0, headLines);
|
|
33
|
-
const tail = lines.slice(-tailLines);
|
|
34
|
-
let result = head.join("\n");
|
|
35
|
-
let tokens = estimateTokenCount(result);
|
|
36
|
-
while (tokens > maxTokens * headRatio && head.length > 10) {
|
|
37
|
-
head.pop();
|
|
38
|
-
result = head.join("\n");
|
|
39
|
-
tokens = estimateTokenCount(result);
|
|
40
|
-
}
|
|
41
|
-
const omittedCount = lines.length - head.length - tail.length;
|
|
42
|
-
const marker = `\n\n[... ${omittedCount} lines truncated (${originalTokens - tokens} tokens over budget) ...]\n\n`;
|
|
43
|
-
const tailText = tail.join("\n");
|
|
44
|
-
const truncatedContent = result + marker + tailText;
|
|
45
|
-
const deliveredTokens = estimateTokenCount(truncatedContent);
|
|
46
|
-
return {
|
|
47
|
-
content: truncatedContent,
|
|
48
|
-
truncated: true,
|
|
49
|
-
originalTokens,
|
|
50
|
-
deliveredTokens,
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Check if content is structured JSON (skip budget enforcement).
|
|
55
|
-
*/
|
|
56
|
-
export function isStructuredContent(content) {
|
|
57
|
-
if (typeof content !== "string")
|
|
58
|
-
return true;
|
|
59
|
-
const trimmed = content.trim();
|
|
60
|
-
if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
|
|
61
|
-
try {
|
|
62
|
-
JSON.parse(trimmed);
|
|
63
|
-
return true;
|
|
64
|
-
}
|
|
65
|
-
catch {
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return false;
|
|
70
|
-
}
|