@unerr-ai/unerr 0.1.0 → 0.1.2

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.
Files changed (553) hide show
  1. package/dist/__tests__/architecture-guard.test.js +122 -0
  2. package/dist/__tests__/arg-validator.test.js +205 -0
  3. package/dist/__tests__/ast-extractor.test.js +203 -0
  4. package/dist/__tests__/auto-bootstrap.test.js +280 -0
  5. package/dist/__tests__/background-indexer.test.js +228 -0
  6. package/dist/__tests__/blast-radius-engine.test.js +200 -0
  7. package/dist/__tests__/bridge-isolation.test.js +37 -0
  8. package/dist/__tests__/budget-enforcer.test.js +53 -0
  9. package/dist/__tests__/cfg-test-detection-perf.test.js +82 -0
  10. package/dist/__tests__/change-narrative.test.js +190 -0
  11. package/dist/__tests__/check-commit.test.js +258 -0
  12. package/dist/__tests__/checksum.test.js +34 -0
  13. package/dist/__tests__/commit-watcher.test.js +154 -0
  14. package/dist/__tests__/community-detection.test.js +179 -0
  15. package/dist/__tests__/community-tools.test.js +299 -0
  16. package/dist/__tests__/components.test.js +449 -0
  17. package/dist/__tests__/compression-log.test.js +174 -0
  18. package/dist/__tests__/compression-quality-monitor.test.js +40 -0
  19. package/dist/__tests__/config-healer.test.js +165 -0
  20. package/dist/__tests__/context-ledger.test.js +58 -0
  21. package/dist/__tests__/convention-detector.test.js +99 -0
  22. package/dist/__tests__/convention-learner.test.js +86 -0
  23. package/dist/__tests__/correction-detector.test.js +330 -0
  24. package/dist/__tests__/daemon-autostart-install.test.js +283 -0
  25. package/dist/__tests__/daemon-bridge.test.js +222 -0
  26. package/dist/__tests__/daemon-dashboard.test.js +202 -0
  27. package/dist/__tests__/daemon-registry.test.js +240 -0
  28. package/dist/__tests__/daemon-supervisor.test.js +318 -0
  29. package/dist/__tests__/daemon-version-check.test.js +275 -0
  30. package/dist/__tests__/decision-point-detector.test.js +98 -0
  31. package/dist/__tests__/deep-link.test.js +143 -0
  32. package/dist/__tests__/disallowed-tools.test.js +115 -0
  33. package/dist/__tests__/drift-tracker.test.js +582 -0
  34. package/dist/__tests__/durability-scorer.test.js +152 -0
  35. package/dist/__tests__/efficiency-tracker.test.js +65 -0
  36. package/dist/__tests__/enrich.test.js +144 -0
  37. package/dist/__tests__/entity-rewind.test.js +248 -0
  38. package/dist/__tests__/ephemeral.test.js +111 -0
  39. package/dist/__tests__/exploration-cost.test.js +93 -0
  40. package/dist/__tests__/fact-generator.test.js +197 -0
  41. package/dist/__tests__/file-l0-graph.test.js +244 -0
  42. package/dist/__tests__/file-logger.test.js +82 -0
  43. package/dist/__tests__/file-outline.test.js +141 -0
  44. package/dist/__tests__/file-read-protocol.test.js +188 -0
  45. package/dist/__tests__/format-encoder.test.js +233 -0
  46. package/dist/__tests__/git-attribution.test.js +259 -0
  47. package/dist/__tests__/graph-temporal-joiner.test.js +219 -0
  48. package/dist/__tests__/health-grade-enhanced.test.js +138 -0
  49. package/dist/__tests__/health-map-data.test.js +173 -0
  50. package/dist/__tests__/helpers/mcp-harness.js +45 -0
  51. package/dist/__tests__/helpers/mcp-harness.test.js +68 -0
  52. package/dist/__tests__/hook-dedup.test.js +112 -0
  53. package/dist/__tests__/hook-runner.test.js +253 -0
  54. package/dist/__tests__/indexer-cfg.test.js +185 -0
  55. package/dist/__tests__/indexer-cross-file.test.js +172 -0
  56. package/dist/__tests__/indexer-extraction.test.js +245 -0
  57. package/dist/__tests__/indexer-incremental.test.js +232 -0
  58. package/dist/__tests__/indexer-language-expansion.test.js +165 -0
  59. package/dist/__tests__/init-push.test.js +131 -0
  60. package/dist/__tests__/instruction-writer.test.js +179 -0
  61. package/dist/__tests__/intelligence-integration.test.js +217 -0
  62. package/dist/__tests__/intent-correlator.test.js +175 -0
  63. package/dist/__tests__/intent-detector.test.js +235 -0
  64. package/dist/__tests__/intent-encoder.test.js +167 -0
  65. package/dist/__tests__/java-build-tool-detection.test.js +174 -0
  66. package/dist/__tests__/layer3-sprint-q.test.js +160 -0
  67. package/dist/__tests__/layer3-sprint-r.test.js +91 -0
  68. package/dist/__tests__/layer3-sprint-s.test.js +183 -0
  69. package/dist/__tests__/layer3-sprint-t.test.js +201 -0
  70. package/dist/__tests__/layer3-sprint-u.test.js +174 -0
  71. package/dist/__tests__/layer4-sprint-ba2.test.js +354 -0
  72. package/dist/__tests__/layer4-sprint-ba4.test.js +84 -0
  73. package/dist/__tests__/layer4-sprint-vs.test.js +105 -0
  74. package/dist/__tests__/ledger-chains.test.js +162 -0
  75. package/dist/__tests__/lifecycle-machine.test.js +226 -0
  76. package/dist/__tests__/local-chat-provider.test.js +170 -0
  77. package/dist/__tests__/local-convention-detector.test.js +308 -0
  78. package/dist/__tests__/local-embeddings.test.js +422 -0
  79. package/dist/__tests__/local-graph.test.js +540 -0
  80. package/dist/__tests__/local-indexer.test.js +228 -0
  81. package/dist/__tests__/local-intelligence-l3.test.js +332 -0
  82. package/dist/__tests__/local-llm.test.js +253 -0
  83. package/dist/__tests__/local-mode-offline.test.js +187 -0
  84. package/dist/__tests__/local-mode-stats.test.js +273 -0
  85. package/dist/__tests__/local-mode-tui.test.js +343 -0
  86. package/dist/__tests__/local-parse.test.js +199 -0
  87. package/dist/__tests__/log-tailer.test.js +208 -0
  88. package/dist/__tests__/loop-breaker.test.js +276 -0
  89. package/dist/__tests__/loop-miner.test.js +226 -0
  90. package/dist/__tests__/mcp-config.test.js +126 -0
  91. package/dist/__tests__/mcp-content-json.test.js +10 -0
  92. package/dist/__tests__/mcp-envelope.test.js +124 -0
  93. package/dist/__tests__/metrics-store.test.js +223 -0
  94. package/dist/__tests__/native-watcher.test.js +191 -0
  95. package/dist/__tests__/navigation-hooks-agent-aware.test.js +145 -0
  96. package/dist/__tests__/negative-knowledge.test.js +116 -0
  97. package/dist/__tests__/network-boundary.test.js +190 -0
  98. package/dist/__tests__/network-firewall.test.js +112 -0
  99. package/dist/__tests__/nudge-invariants.test.js +160 -0
  100. package/dist/__tests__/nudge-v2.test.js +225 -0
  101. package/dist/__tests__/offline-rewind.test.js +251 -0
  102. package/dist/__tests__/open-threads.test.js +89 -0
  103. package/dist/__tests__/output-compressor.test.js +93 -0
  104. package/dist/__tests__/pending-violations.test.js +112 -0
  105. package/dist/__tests__/persistence-effectiveness.test.js +143 -0
  106. package/dist/__tests__/provider-factory.test.js +42 -0
  107. package/dist/__tests__/providers.test.js +24 -0
  108. package/dist/__tests__/proxy.test.js +314 -0
  109. package/dist/__tests__/query-router.test.js +1018 -0
  110. package/dist/__tests__/reasoning-quality-route.test.js +138 -0
  111. package/dist/__tests__/redactor.test.js +120 -0
  112. package/dist/__tests__/resource-monitor.test.js +57 -0
  113. package/dist/__tests__/response-envelope.test.js +100 -0
  114. package/dist/__tests__/risk-classifier.test.js +101 -0
  115. package/dist/__tests__/risk-signal-scope.test.js +75 -0
  116. package/dist/__tests__/rule-evaluator.test.js +280 -0
  117. package/dist/__tests__/scip-decoder.test.js +49 -0
  118. package/dist/__tests__/scip-downloader.test.js +201 -0
  119. package/dist/__tests__/scip-merger.test.js +103 -0
  120. package/dist/__tests__/search-index.test.js +422 -0
  121. package/dist/__tests__/semantic-enrichment.test.js +360 -0
  122. package/dist/__tests__/session-brief-builder.test.js +187 -0
  123. package/dist/__tests__/session-context.test.js +221 -0
  124. package/dist/__tests__/session-continuity.test.js +144 -0
  125. package/dist/__tests__/session-dedup.test.js +74 -0
  126. package/dist/__tests__/session-event-wiring.test.js +206 -0
  127. package/dist/__tests__/session-events.test.js +149 -0
  128. package/dist/__tests__/session-legend.test.js +20 -0
  129. package/dist/__tests__/session-persistence.test.js +131 -0
  130. package/dist/__tests__/session-resume-block.test.js +107 -0
  131. package/dist/__tests__/session-resume.test.js +97 -0
  132. package/dist/__tests__/session-summary-writer.test.js +134 -0
  133. package/dist/__tests__/shadow-ledger.test.js +203 -0
  134. package/dist/__tests__/shell-classifier.test.js +151 -0
  135. package/dist/__tests__/shell-compression-floor.test.js +189 -0
  136. package/dist/__tests__/shell-compression-v2.test.js +339 -0
  137. package/dist/__tests__/shell-compressor.test.js +35 -0
  138. package/dist/__tests__/shell-hooks.test.js +128 -0
  139. package/dist/__tests__/shell-strategies.test.js +644 -0
  140. package/dist/__tests__/shell-tee.test.js +133 -0
  141. package/dist/__tests__/signal-dedup.test.js +158 -0
  142. package/dist/__tests__/signal-reinforcer.test.js +77 -0
  143. package/dist/__tests__/signal-scorer.test.js +251 -0
  144. package/dist/__tests__/signal-show-store.test.js +108 -0
  145. package/dist/__tests__/smart-truncate.test.js +215 -0
  146. package/dist/__tests__/snapshot-v2.test.js +113 -0
  147. package/dist/__tests__/sprint-l1-local-mode.test.js +130 -0
  148. package/dist/__tests__/sprint-l10-boot.test.js +220 -0
  149. package/dist/__tests__/sprint-l9-offline-commands.test.js +189 -0
  150. package/dist/__tests__/sprint-q-persistent-context.test.js +198 -0
  151. package/dist/__tests__/sprint-s1-wiring.test.js +215 -0
  152. package/dist/__tests__/sprint-s2-wiring.test.js +256 -0
  153. package/dist/__tests__/sprint-s3-wiring.test.js +195 -0
  154. package/dist/__tests__/sprint-s4-wiring.test.js +213 -0
  155. package/dist/__tests__/sprint-s6-hooks.test.js +222 -0
  156. package/dist/__tests__/sprint-s7-persistent.test.js +263 -0
  157. package/dist/__tests__/sprint-s8-value.test.js +167 -0
  158. package/dist/__tests__/sprint-s9-behavioral.test.js +179 -0
  159. package/dist/__tests__/sprint3-intelligence.test.js +297 -0
  160. package/dist/__tests__/sprint5-mcp-server.test.js +136 -0
  161. package/dist/__tests__/startup-display.test.js +302 -0
  162. package/dist/__tests__/startup-log-file.test.js +97 -0
  163. package/dist/__tests__/stash-manager.test.js +229 -0
  164. package/dist/__tests__/state-detector.test.js +92 -0
  165. package/dist/__tests__/status-dashboard.test.js +142 -0
  166. package/dist/__tests__/temporal-facts.test.js +292 -0
  167. package/dist/__tests__/temporal-routes.test.js +142 -0
  168. package/dist/__tests__/test-detector.test.js +174 -0
  169. package/dist/__tests__/theme.test.js +72 -0
  170. package/dist/__tests__/timeline-agents.test.js +122 -0
  171. package/dist/__tests__/timeline-bootstrap.test.js +176 -0
  172. package/dist/__tests__/timeline-filters.test.js +193 -0
  173. package/dist/__tests__/timeline-markers.test.js +151 -0
  174. package/dist/__tests__/timeline-routes.test.js +156 -0
  175. package/dist/__tests__/timeline-store.test.js +171 -0
  176. package/dist/__tests__/token-counter.test.js +86 -0
  177. package/dist/__tests__/token-estimator.test.js +96 -0
  178. package/dist/__tests__/token-flow-api.test.js +239 -0
  179. package/dist/__tests__/token-flow-instrumentation.test.js +437 -0
  180. package/dist/__tests__/token-flow-persistence.test.js +356 -0
  181. package/dist/__tests__/token-flow-routes.test.js +199 -0
  182. package/dist/__tests__/token-flow.test.js +695 -0
  183. package/dist/__tests__/tool-clusters.test.js +177 -0
  184. package/dist/__tests__/transport-mux.test.js +283 -0
  185. package/dist/__tests__/turn-segmenter.test.js +166 -0
  186. package/dist/__tests__/uninstall.test.js +141 -0
  187. package/dist/__tests__/warm-start-policy.test.js +271 -0
  188. package/dist/__tests__/wire-cap-nudge.test.js +77 -0
  189. package/dist/__tests__/worker-pool.test.js +101 -0
  190. package/dist/behaviors/agent-llm-bridge.js +166 -0
  191. package/dist/behaviors/architecture-guard.js +256 -0
  192. package/dist/behaviors/auto-doc.js +247 -0
  193. package/dist/behaviors/cascade-guard.js +289 -0
  194. package/dist/behaviors/change-narrative.js +270 -0
  195. package/dist/behaviors/convention-drift.js +290 -0
  196. package/dist/behaviors/framework.js +235 -0
  197. package/dist/behaviors/guard-formatter.js +44 -0
  198. package/dist/behaviors/incomplete-work.js +270 -0
  199. package/dist/behaviors/loop-breaker.js +300 -0
  200. package/dist/behaviors/session-continuity.js +208 -0
  201. package/dist/cli.js +996 -710
  202. package/dist/commands/branches.js +97 -0
  203. package/dist/commands/check-commit.js +225 -0
  204. package/dist/commands/compress-output.js +64 -0
  205. package/dist/commands/config-verify.js +243 -0
  206. package/dist/commands/daemon.js +905 -0
  207. package/dist/commands/dashboard.js +52 -0
  208. package/dist/commands/debug.js +200 -0
  209. package/dist/commands/enrich.js +184 -0
  210. package/dist/commands/exec.js +233 -0
  211. package/dist/commands/gain.js +156 -0
  212. package/dist/commands/hook.js +88 -0
  213. package/dist/commands/index.js +88 -0
  214. package/dist/commands/init.js +74 -0
  215. package/dist/commands/install.js +505 -0
  216. package/dist/commands/learn.js +116 -0
  217. package/dist/commands/manifest.js +193 -0
  218. package/dist/commands/rewind.js +103 -0
  219. package/dist/commands/serve.js +19 -0
  220. package/dist/commands/setup-wizard.js +414 -0
  221. package/dist/commands/skills.js +64 -0
  222. package/dist/commands/stats.js +20 -0
  223. package/dist/commands/status.js +654 -0
  224. package/dist/commands/timeline.js +139 -0
  225. package/dist/commands/uninstall.js +230 -0
  226. package/dist/components/App.js +109 -0
  227. package/dist/components/Banner.js +12 -0
  228. package/dist/components/ConfirmPrompt.js +25 -0
  229. package/dist/components/DriftSummary.js +23 -0
  230. package/dist/components/GradeBadge.js +15 -0
  231. package/dist/components/HealthCard.js +18 -0
  232. package/dist/components/InkSpinner.js +22 -0
  233. package/dist/components/InputBox.js +17 -0
  234. package/dist/components/KeyValue.js +13 -0
  235. package/dist/components/MessageList.js +14 -0
  236. package/dist/components/ProgressBar.js +26 -0
  237. package/dist/components/Section.js +16 -0
  238. package/dist/components/SessionSummaryCard.js +73 -0
  239. package/dist/components/StartupDisplay.js +24 -0
  240. package/dist/components/StatusDashboard.js +57 -0
  241. package/dist/components/StatusLine.js +8 -0
  242. package/dist/components/StepLine.js +22 -0
  243. package/dist/components/Theme.js +20 -0
  244. package/dist/components/ToolProgress.js +8 -0
  245. package/dist/components/ViolationList.js +21 -0
  246. package/dist/components/render.js +13 -0
  247. package/dist/config/agent-registry.js +237 -0
  248. package/dist/config/claude-settings-hooks.js +304 -0
  249. package/dist/config/hook-installer.js +65 -0
  250. package/dist/config/instruction-writer.js +388 -0
  251. package/dist/config/mcp-config-writer.js +266 -0
  252. package/dist/config/settings.js +174 -0
  253. package/dist/config/tool-detector.js +42 -0
  254. package/dist/config/value-surfacing.js +119 -0
  255. package/dist/core/context-assembly.js +108 -0
  256. package/dist/core/conversation.js +33 -0
  257. package/dist/core/local-chat-provider.js +475 -0
  258. package/dist/core/provider-factory.js +55 -0
  259. package/dist/core/providers.js +90 -0
  260. package/dist/core/query-engine.js +174 -0
  261. package/dist/daemon/api.js +312 -0
  262. package/dist/daemon/autostart.js +119 -0
  263. package/dist/daemon/bootstrap.js +39 -0
  264. package/dist/daemon/client.js +164 -0
  265. package/dist/daemon/detect-ci.js +81 -0
  266. package/dist/daemon/platform-linux.js +146 -0
  267. package/dist/daemon/platform-macos.js +134 -0
  268. package/dist/daemon/platform-windows.js +116 -0
  269. package/dist/daemon/process-manager.js +299 -0
  270. package/dist/daemon/protocol.js +23 -0
  271. package/dist/daemon/registry.js +270 -0
  272. package/dist/daemon/settings-schema.js +72 -0
  273. package/dist/daemon/system-health.js +134 -0
  274. package/dist/daemon/version-checker.js +262 -0
  275. package/dist/daemon/warm-start.js +223 -0
  276. package/dist/entrypoints/cli.js +1043 -0
  277. package/dist/entrypoints/daemon.js +380 -0
  278. package/dist/entrypoints/repl.js +147 -0
  279. package/dist/hooks/adapters/claude-code.js +90 -0
  280. package/dist/hooks/adapters/cline.js +100 -0
  281. package/dist/hooks/adapters/cursor.js +98 -0
  282. package/dist/hooks/hook-dedup.js +79 -0
  283. package/dist/hooks/hook-runner.js +113 -0
  284. package/dist/hooks/navigation-hooks.js +175 -0
  285. package/dist/hooks/prompt-hooks.js +63 -0
  286. package/dist/hooks/shell-hooks.js +47 -0
  287. package/dist/ignore.js +111 -0
  288. package/dist/intelligence/approach-suggester.js +61 -0
  289. package/dist/intelligence/ast-extractor.js +2615 -0
  290. package/dist/intelligence/ast-worker.js +34 -0
  291. package/dist/intelligence/background-indexer.js +121 -0
  292. package/dist/intelligence/blast-radius.js +200 -0
  293. package/dist/intelligence/community-detection.js +691 -0
  294. package/dist/intelligence/community-detector.js +184 -0
  295. package/dist/intelligence/computation-scheduler.js +75 -0
  296. package/dist/intelligence/confidence-propagation.js +47 -0
  297. package/dist/intelligence/convention-detector.js +242 -0
  298. package/dist/intelligence/convention-learner.js +205 -0
  299. package/dist/intelligence/convention-matcher.js +205 -0
  300. package/dist/intelligence/cozo-schema.js +376 -0
  301. package/dist/intelligence/decision-point-detector.js +90 -0
  302. package/dist/intelligence/deep-dive-tools.js +586 -0
  303. package/dist/intelligence/durability-scorer.js +84 -0
  304. package/dist/intelligence/exploration-cost.js +204 -0
  305. package/dist/intelligence/exploration-pattern-tracker.js +61 -0
  306. package/dist/intelligence/fact-generator.js +322 -0
  307. package/dist/intelligence/facts-schema.js +90 -0
  308. package/dist/intelligence/file-intelligence.js +59 -0
  309. package/dist/intelligence/graph-holder.js +220 -0
  310. package/dist/intelligence/graph-temporal-joiner.js +238 -0
  311. package/dist/intelligence/health-grade.js +423 -0
  312. package/dist/intelligence/health-grader.js +200 -0
  313. package/dist/intelligence/health-map-data.js +259 -0
  314. package/dist/intelligence/import-symbols.js +136 -0
  315. package/dist/intelligence/incremental-indexer.js +658 -0
  316. package/dist/intelligence/indexer/centrality.js +62 -0
  317. package/dist/intelligence/indexer/cfg-context.js +95 -0
  318. package/dist/intelligence/indexer/confidence.js +34 -0
  319. package/dist/intelligence/indexer/cross-file-resolver.js +104 -0
  320. package/dist/intelligence/indexer/edge-repair.js +89 -0
  321. package/dist/intelligence/indexer/entity-key.js +17 -0
  322. package/dist/intelligence/indexer/export-map.js +132 -0
  323. package/dist/intelligence/indexer/git-cochange.js +128 -0
  324. package/dist/intelligence/indexer/graph-patch.js +147 -0
  325. package/dist/intelligence/indexer/incremental.js +78 -0
  326. package/dist/intelligence/indexer/ingest.js +160 -0
  327. package/dist/intelligence/indexer/language-detect.js +226 -0
  328. package/dist/intelligence/indexer/metadata.js +63 -0
  329. package/dist/intelligence/indexer/mutation-tracker.js +79 -0
  330. package/dist/intelligence/indexer/orchestrator.js +155 -0
  331. package/dist/intelligence/indexer/plugin-interface.js +31 -0
  332. package/dist/intelligence/indexer/plugins/csharp.js +440 -0
  333. package/dist/intelligence/indexer/plugins/go.js +335 -0
  334. package/dist/intelligence/indexer/plugins/java.js +370 -0
  335. package/dist/intelligence/indexer/plugins/python.js +358 -0
  336. package/dist/intelligence/indexer/plugins/regex-fallback.js +82 -0
  337. package/dist/intelligence/indexer/plugins/ruby.js +290 -0
  338. package/dist/intelligence/indexer/plugins/rust.js +484 -0
  339. package/dist/intelligence/indexer/plugins/tier2-generic.js +310 -0
  340. package/dist/intelligence/indexer/plugins/typescript.js +456 -0
  341. package/dist/intelligence/indexer/resource-monitor.js +93 -0
  342. package/dist/intelligence/indexer/scip/decoder.js +253 -0
  343. package/dist/intelligence/indexer/scip/detector.js +232 -0
  344. package/dist/intelligence/indexer/scip/downloader.js +427 -0
  345. package/dist/intelligence/indexer/scip/fallback.js +34 -0
  346. package/dist/intelligence/indexer/scip/merger.js +109 -0
  347. package/dist/intelligence/indexer/scip/orchestrator.js +433 -0
  348. package/dist/intelligence/indexer/scip/runner.js +98 -0
  349. package/dist/intelligence/indexer/snapshot.js +66 -0
  350. package/dist/intelligence/indexer/test-detector.js +196 -0
  351. package/dist/intelligence/indexer/watch-integration.js +61 -0
  352. package/dist/intelligence/indexer/worker.js +85 -0
  353. package/dist/intelligence/local-convention-detector.js +437 -0
  354. package/dist/intelligence/local-embeddings.js +190 -0
  355. package/dist/intelligence/local-graph.js +1946 -0
  356. package/dist/intelligence/local-indexer.js +1575 -0
  357. package/dist/intelligence/local-llm.js +163 -0
  358. package/dist/intelligence/local-rule-generator.js +154 -0
  359. package/dist/intelligence/local-snapshot.js +213 -0
  360. package/dist/intelligence/negative-knowledge.js +103 -0
  361. package/dist/intelligence/persistent-db.js +85 -0
  362. package/dist/intelligence/query-router.js +2556 -0
  363. package/dist/intelligence/risk-classifier.js +116 -0
  364. package/dist/intelligence/rule-evaluator.js +380 -0
  365. package/dist/intelligence/rule-generator.js +49 -0
  366. package/dist/intelligence/search-index.js +173 -0
  367. package/dist/intelligence/semantic/docstring-extractor.js +67 -0
  368. package/dist/intelligence/semantic/embedding-store.js +52 -0
  369. package/dist/intelligence/semantic/enrichment-orchestrator.js +48 -0
  370. package/dist/intelligence/semantic/git-message-miner.js +114 -0
  371. package/dist/intelligence/semantic/identifier-tokenizer.js +51 -0
  372. package/dist/intelligence/semantic/node2vec-embeddings.js +71 -0
  373. package/dist/intelligence/semantic/node2vec-walks.js +103 -0
  374. package/dist/intelligence/semantic/path-domain-inference.js +112 -0
  375. package/dist/intelligence/semantic/similarity-engine.js +60 -0
  376. package/dist/intelligence/semantic/tfidf-vectors.js +88 -0
  377. package/dist/intelligence/session-brief-builder.js +159 -0
  378. package/dist/intelligence/session-context.js +221 -0
  379. package/dist/intelligence/session-health-monitor.js +211 -0
  380. package/dist/intelligence/session-narrative.js +197 -0
  381. package/dist/intelligence/session-pattern-analyzer.js +218 -0
  382. package/dist/intelligence/signal-scorer.js +390 -0
  383. package/dist/intelligence/signal-show-store.js +182 -0
  384. package/dist/intelligence/smart-truncate.js +158 -0
  385. package/dist/intelligence/subgraph-cache.js +88 -0
  386. package/dist/intelligence/temporal-facts.js +494 -0
  387. package/dist/intelligence/token-estimator.js +100 -0
  388. package/dist/intelligence/tool-injector.js +87 -0
  389. package/dist/intelligence/tree-sitter-loader.js +71 -0
  390. package/dist/intelligence/worker-pool.js +116 -0
  391. package/dist/proxy/arg-validator.js +79 -0
  392. package/dist/proxy/auto-bootstrap.js +167 -0
  393. package/dist/proxy/bridge.js +147 -0
  394. package/dist/proxy/budget-enforcer.js +70 -0
  395. package/dist/proxy/compression-quality-monitor.js +160 -0
  396. package/dist/proxy/compression-stats.js +51 -0
  397. package/dist/proxy/context-rot-detector.js +137 -0
  398. package/dist/proxy/drift-detector.js +139 -0
  399. package/dist/proxy/efficiency-tracker.js +79 -0
  400. package/dist/proxy/fact-ranking.js +154 -0
  401. package/dist/proxy/format-encoder.js +266 -0
  402. package/dist/proxy/http-transport.js +90 -0
  403. package/dist/proxy/lifecycle-actor.js +55 -0
  404. package/dist/proxy/lifecycle-machine.js +187 -0
  405. package/dist/proxy/log-tailer.js +265 -0
  406. package/dist/proxy/model-pricing.js +98 -0
  407. package/dist/proxy/network-firewall.js +141 -0
  408. package/dist/proxy/nudge-state.js +93 -0
  409. package/dist/proxy/output-compressor.js +185 -0
  410. package/dist/proxy/pid-lock.js +291 -0
  411. package/dist/proxy/proxy-context.js +11 -0
  412. package/dist/proxy/proxy.js +2633 -0
  413. package/dist/proxy/response-enrichment.js +32 -0
  414. package/dist/proxy/response-envelope.js +313 -0
  415. package/dist/proxy/session-dedup.js +82 -0
  416. package/dist/proxy/session-legend.js +30 -0
  417. package/dist/proxy/session-persistence.js +210 -0
  418. package/dist/proxy/session-resume.js +94 -0
  419. package/dist/proxy/session-stats.js +513 -0
  420. package/dist/proxy/shell-classifier.js +1346 -0
  421. package/dist/proxy/shell-compression-log.js +93 -0
  422. package/dist/proxy/shell-compressor.js +390 -0
  423. package/dist/proxy/shell-graph-boost.js +202 -0
  424. package/dist/proxy/shell-monitor-map.js +18 -0
  425. package/dist/proxy/shell-stats.js +54 -0
  426. package/dist/proxy/shell-strategies/cloud.js +215 -0
  427. package/dist/proxy/shell-strategies/diff.js +159 -0
  428. package/dist/proxy/shell-strategies/error-diagnostic.js +796 -0
  429. package/dist/proxy/shell-strategies/filter-dsl.js +358 -0
  430. package/dist/proxy/shell-strategies/git-status.js +177 -0
  431. package/dist/proxy/shell-strategies/key-value.js +193 -0
  432. package/dist/proxy/shell-strategies/log-text.js +154 -0
  433. package/dist/proxy/shell-strategies/omni.js +188 -0
  434. package/dist/proxy/shell-strategies/progress.js +55 -0
  435. package/dist/proxy/shell-strategies/redact.js +76 -0
  436. package/dist/proxy/shell-strategies/structured.js +241 -0
  437. package/dist/proxy/shell-strategies/tabular.js +243 -0
  438. package/dist/proxy/shell-strategies/test-results-types.js +13 -0
  439. package/dist/proxy/shell-strategies/test-results.js +784 -0
  440. package/dist/proxy/shell-strategies/tree-paths.js +144 -0
  441. package/dist/proxy/shell-strategies/yaml.js +182 -0
  442. package/dist/proxy/shell-tee.js +111 -0
  443. package/dist/proxy/signal-dedup.js +171 -0
  444. package/dist/proxy/startup-renderer.js +158 -0
  445. package/dist/proxy/task-token-display.js +38 -0
  446. package/dist/proxy/token-counter.js +61 -0
  447. package/dist/proxy/tool-clusters.js +273 -0
  448. package/dist/proxy/tool-definitions.js +525 -0
  449. package/dist/proxy/transport-mux.js +229 -0
  450. package/dist/proxy/wire-cap.js +268 -0
  451. package/dist/schemas/api/skills.js +19 -0
  452. package/dist/schemas/common/errors.js +7 -0
  453. package/dist/schemas/common/headers.js +5 -0
  454. package/dist/schemas/entities/edge.js +25 -0
  455. package/dist/schemas/entities/entity.js +22 -0
  456. package/dist/schemas/entities/rule.js +18 -0
  457. package/dist/schemas/index.js +14 -0
  458. package/dist/server/event-bus.js +59 -0
  459. package/dist/server/http.js +156 -0
  460. package/dist/server/middleware.js +70 -0
  461. package/dist/server/routes/drift.js +97 -0
  462. package/dist/server/routes/intelligence.js +1217 -0
  463. package/dist/server/routes/reasoning-quality.js +444 -0
  464. package/dist/server/routes/session.js +86 -0
  465. package/dist/server/routes/stream.js +120 -0
  466. package/dist/server/routes/system.js +73 -0
  467. package/dist/server/routes/temporal.js +170 -0
  468. package/dist/server/routes/timeline.js +232 -0
  469. package/dist/server/routes/token-flow.js +403 -0
  470. package/dist/skills/effectiveness-tracker.js +93 -0
  471. package/dist/skills/local-pack.js +380 -0
  472. package/dist/skills/resolver.js +495 -0
  473. package/dist/state-detector.js +83 -0
  474. package/dist/timeline/intent-detector.js +263 -0
  475. package/dist/timeline/loop-miner.js +140 -0
  476. package/dist/timeline/open-threads.js +49 -0
  477. package/dist/timeline/signal-reinforcer.js +62 -0
  478. package/dist/timeline/timeline-bootstrap.js +151 -0
  479. package/dist/timeline/timeline-store.js +618 -0
  480. package/dist/tools/coding/bash.js +49 -0
  481. package/dist/tools/coding/file-edit.js +72 -0
  482. package/dist/tools/coding/file-outline.js +227 -0
  483. package/dist/tools/coding/file-read-protocol.js +425 -0
  484. package/dist/tools/coding/file-read.js +35 -0
  485. package/dist/tools/coding/file-write.js +43 -0
  486. package/dist/tools/coding/glob-tool.js +109 -0
  487. package/dist/tools/coding/grep.js +162 -0
  488. package/dist/tools/coding/index.js +27 -0
  489. package/dist/tools/intelligence/index.js +269 -0
  490. package/dist/tools/intelligence/record-fact.js +48 -0
  491. package/dist/tools/intelligence/timeline-markers.js +130 -0
  492. package/dist/tools/registry.js +47 -0
  493. package/dist/tools/types.js +8 -0
  494. package/dist/tracking/auto-snapshot-triggers.js +246 -0
  495. package/dist/tracking/branch-context.js +115 -0
  496. package/dist/tracking/branch-snapshot.js +217 -0
  497. package/dist/tracking/causal-bridge.js +317 -0
  498. package/dist/tracking/circuit-breaker.js +147 -0
  499. package/dist/tracking/commit-watcher.js +114 -0
  500. package/dist/tracking/context-ledger.js +119 -0
  501. package/dist/tracking/correction-detector.js +324 -0
  502. package/dist/tracking/drift-tracker.js +874 -0
  503. package/dist/tracking/durability-tracker.js +94 -0
  504. package/dist/tracking/entity-rewind.js +200 -0
  505. package/dist/tracking/file-hash-state.js +114 -0
  506. package/dist/tracking/git-attribution.js +132 -0
  507. package/dist/tracking/git-trailers.js +171 -0
  508. package/dist/tracking/intelligence-counter.js +46 -0
  509. package/dist/tracking/intent-correlator.js +202 -0
  510. package/dist/tracking/intent-encoder.js +52 -0
  511. package/dist/tracking/intent-token-tracker.js +159 -0
  512. package/dist/tracking/ledger-archiver.js +94 -0
  513. package/dist/tracking/ledger-chains.js +245 -0
  514. package/dist/tracking/metrics-store.js +361 -0
  515. package/dist/tracking/native-watcher.js +131 -0
  516. package/dist/tracking/offline-rewind.js +295 -0
  517. package/dist/tracking/pending-violations.js +74 -0
  518. package/dist/tracking/persistence-effectiveness.js +167 -0
  519. package/dist/tracking/prompt-durability.js +202 -0
  520. package/dist/tracking/quality-signals.js +213 -0
  521. package/dist/tracking/redactor.js +73 -0
  522. package/dist/tracking/rewind-engine.js +161 -0
  523. package/dist/tracking/session-history.js +128 -0
  524. package/dist/tracking/session-receipt.js +88 -0
  525. package/dist/tracking/session-summary-writer.js +157 -0
  526. package/dist/tracking/shadow-ledger.js +321 -0
  527. package/dist/tracking/stash-manager.js +258 -0
  528. package/dist/tracking/timeline-fork.js +213 -0
  529. package/dist/tracking/timeline.js +69 -0
  530. package/dist/tracking/token-flow.js +276 -0
  531. package/dist/tracking/turn-segmenter.js +122 -0
  532. package/dist/tracking/weekly-accumulator.js +179 -0
  533. package/dist/tracking/working-snapshots.js +188 -0
  534. package/dist/tracking/workspace-manifest.js +176 -0
  535. package/dist/transport/http.js +102 -0
  536. package/dist/ui/assets/index-BsMTQdhX.js +10 -0
  537. package/dist/ui/index.html +1 -1
  538. package/dist/utils/counterfactual.js +65 -0
  539. package/dist/utils/deep-link.js +34 -0
  540. package/dist/utils/detect.js +193 -0
  541. package/dist/utils/exec.js +73 -0
  542. package/dist/utils/file-logger.js +87 -0
  543. package/dist/utils/format-error.js +29 -0
  544. package/dist/utils/git.js +181 -0
  545. package/dist/utils/log.js +57 -0
  546. package/dist/utils/logger.js +35 -0
  547. package/dist/utils/mcp-content-json.js +8 -0
  548. package/dist/utils/session-logger.js +154 -0
  549. package/dist/utils/startup-log.js +512 -0
  550. package/dist/utils/ui.js +56 -0
  551. package/package.json +5 -3
  552. package/scripts/postinstall.mjs +312 -0
  553. package/dist/ui/assets/index-B-0HTtUR.js +0 -10
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Intent Detector (ST-4) — cross-session task stitching.
3
+ *
4
+ * Walks closed turns + recorded session_files + markers in timeline.db and
5
+ * groups sessions into `intents`. A session attaches to an intent if EITHER:
6
+ *
7
+ * a) it emitted a `mark_intent` whose text matches an existing intent's
8
+ * title (anchored stitch, source="agent_marker"); OR
9
+ * b) its file set has Jaccard overlap > 0.4 with an intent's file_set AND
10
+ * the intent was last active within the freshness window (default 14 d).
11
+ *
12
+ * Otherwise a new intent is created with source="file_jaccard". After each
13
+ * run, intents inactive for > 21 d move to "dormant".
14
+ *
15
+ * Pure functions are exported for testing; the orchestrator `runIntentStitch`
16
+ * does the I/O against CozoTimelineStore.
17
+ */
18
+ import { randomUUID } from "node:crypto";
19
+ const DEFAULT_JACCARD = 0.4;
20
+ const DEFAULT_FRESHNESS_MS = 14 * 24 * 60 * 60_000;
21
+ const DEFAULT_DORMANT_MS = 21 * 24 * 60 * 60_000;
22
+ export function jaccard(a, b) {
23
+ if (a.size === 0 && b.size === 0)
24
+ return 0;
25
+ let intersection = 0;
26
+ const [small, big] = a.size <= b.size ? [a, b] : [b, a];
27
+ for (const v of small)
28
+ if (big.has(v))
29
+ intersection += 1;
30
+ const union = a.size + b.size - intersection;
31
+ return union === 0 ? 0 : intersection / union;
32
+ }
33
+ export function hashFileSet(files) {
34
+ const sorted = [...new Set(files)].sort();
35
+ // Tiny deterministic hash — enough to dedupe identical file sets without
36
+ // hauling in a crypto dep. Stable across processes since it's data-only.
37
+ let h = 0;
38
+ for (const f of sorted) {
39
+ for (let i = 0; i < f.length; i++) {
40
+ h = (h * 31 + f.charCodeAt(i)) | 0;
41
+ }
42
+ h = (h * 31 + 0x5f) | 0;
43
+ }
44
+ return `${sorted.length}-${(h >>> 0).toString(36)}`;
45
+ }
46
+ /**
47
+ * Pure stitch — given session summaries + existing intents (with their session
48
+ * lists), return updated intents + new attachments. Does NOT call the DB.
49
+ */
50
+ export function stitchIntents(sessions, existingIntents, existingAttachments, opts = {}) {
51
+ const jaccardThreshold = opts.jaccardThreshold ?? DEFAULT_JACCARD;
52
+ const freshnessMs = opts.freshnessMs ?? DEFAULT_FRESHNESS_MS;
53
+ const dormantAfterMs = opts.dormantAfterMs ?? DEFAULT_DORMANT_MS;
54
+ const now = opts.nowMs ?? Date.now();
55
+ const attachedSessions = new Set(existingAttachments.map((a) => a.session_id));
56
+ const titleIndex = new Map();
57
+ for (const i of existingIntents) {
58
+ if (i.title.length > 0)
59
+ titleIndex.set(i.title.toLowerCase(), i);
60
+ }
61
+ // Working copy of intents; we mutate file_set + last_active_at.
62
+ const intents = new Map();
63
+ for (const i of existingIntents) {
64
+ let files = [];
65
+ try {
66
+ const parsed = JSON.parse(i.file_set || "[]");
67
+ if (Array.isArray(parsed))
68
+ files = parsed.map((x) => String(x));
69
+ }
70
+ catch {
71
+ files = [];
72
+ }
73
+ intents.set(i.intent_id, { ...i, _filesSet: new Set(files) });
74
+ }
75
+ const newAttachments = [];
76
+ // Stitch each session, oldest-first so deterministic merge order.
77
+ const ordered = [...sessions].sort((a, b) => a.started_at - b.started_at);
78
+ for (const s of ordered) {
79
+ if (attachedSessions.has(s.session_id))
80
+ continue;
81
+ // 1) marker anchor
82
+ if (s.intent_text && s.intent_text.length > 0) {
83
+ const existing = titleIndex.get(s.intent_text.toLowerCase());
84
+ if (existing) {
85
+ attachSession(intents.get(existing.intent_id), s);
86
+ newAttachments.push({
87
+ intent_id: existing.intent_id,
88
+ session_id: s.session_id,
89
+ });
90
+ attachedSessions.add(s.session_id);
91
+ continue;
92
+ }
93
+ const created = createIntent(s, s.intent_text, "agent_marker");
94
+ intents.set(created.intent_id, created);
95
+ titleIndex.set(s.intent_text.toLowerCase(), created);
96
+ newAttachments.push({
97
+ intent_id: created.intent_id,
98
+ session_id: s.session_id,
99
+ });
100
+ attachedSessions.add(s.session_id);
101
+ continue;
102
+ }
103
+ // 2) Jaccard
104
+ let best = null;
105
+ for (const i of intents.values()) {
106
+ if (s.started_at - i.last_active_at > freshnessMs)
107
+ continue;
108
+ const score = jaccard(s.files, i._filesSet);
109
+ if (score >= jaccardThreshold && (best === null || score > best.score)) {
110
+ best = { intent: i, score };
111
+ }
112
+ }
113
+ if (best) {
114
+ attachSession(best.intent, s);
115
+ newAttachments.push({
116
+ intent_id: best.intent.intent_id,
117
+ session_id: s.session_id,
118
+ });
119
+ attachedSessions.add(s.session_id);
120
+ continue;
121
+ }
122
+ // 3) Create fresh
123
+ const created = createIntent(s, deriveTitle(s.files), "file_jaccard");
124
+ intents.set(created.intent_id, created);
125
+ if (created.title.length > 0)
126
+ titleIndex.set(created.title.toLowerCase(), created);
127
+ newAttachments.push({
128
+ intent_id: created.intent_id,
129
+ session_id: s.session_id,
130
+ });
131
+ attachedSessions.add(s.session_id);
132
+ }
133
+ // Pass — dormant transitions
134
+ const dormantTransitions = [];
135
+ for (const i of intents.values()) {
136
+ if (i.status === "active" && now - i.last_active_at > dormantAfterMs) {
137
+ i.status = "dormant";
138
+ dormantTransitions.push(i.intent_id);
139
+ }
140
+ }
141
+ return {
142
+ intents: [...intents.values()].map((i) => stripWorkingFields(i)),
143
+ attachments: newAttachments,
144
+ dormantTransitions,
145
+ };
146
+ }
147
+ function attachSession(intent, session) {
148
+ for (const f of session.files)
149
+ intent._filesSet.add(f);
150
+ intent.last_active_at = Math.max(intent.last_active_at, session.last_active_at);
151
+ intent.confidence = Math.min(1, intent.confidence + 0.05);
152
+ intent.file_set = JSON.stringify([...intent._filesSet].sort());
153
+ intent.file_set_hash = hashFileSet(intent._filesSet);
154
+ }
155
+ function createIntent(session, title, source) {
156
+ const filesSorted = [...session.files].sort();
157
+ return {
158
+ intent_id: randomUUID(),
159
+ title,
160
+ started_at: session.started_at,
161
+ last_active_at: session.last_active_at,
162
+ file_set: JSON.stringify(filesSorted),
163
+ file_set_hash: hashFileSet(filesSorted),
164
+ status: "active",
165
+ confidence: source === "agent_marker" ? 0.8 : 0.5,
166
+ source,
167
+ _filesSet: new Set(session.files),
168
+ };
169
+ }
170
+ function stripWorkingFields(i) {
171
+ const { _filesSet, ...row } = i;
172
+ void _filesSet;
173
+ return row;
174
+ }
175
+ function deriveTitle(files) {
176
+ if (files.size === 0)
177
+ return "Misc";
178
+ // Pick the most common directory prefix as a title hint.
179
+ const dirCounts = new Map();
180
+ for (const f of files) {
181
+ const dir = f.split("/").slice(0, -1).join("/") || "/";
182
+ dirCounts.set(dir, (dirCounts.get(dir) ?? 0) + 1);
183
+ }
184
+ let bestDir = "";
185
+ let bestCount = -1;
186
+ for (const [d, c] of dirCounts) {
187
+ if (c > bestCount) {
188
+ bestDir = d;
189
+ bestCount = c;
190
+ }
191
+ }
192
+ return bestDir ? `Work in ${bestDir}` : "Misc";
193
+ }
194
+ /**
195
+ * Build session summaries from turns + markers fetched from the store.
196
+ */
197
+ export function buildSessionSummaries(turns, markers, filesBySession) {
198
+ const intentBySession = new Map();
199
+ for (const m of markers) {
200
+ if (m.type === "mark_intent" && !intentBySession.has(m.session_id)) {
201
+ intentBySession.set(m.session_id, m.text);
202
+ }
203
+ }
204
+ const byId = new Map();
205
+ for (const t of turns) {
206
+ let s = byId.get(t.session_id);
207
+ if (!s) {
208
+ s = {
209
+ session_id: t.session_id,
210
+ started_at: t.started_at,
211
+ last_active_at: t.ended_at,
212
+ files: filesBySession.get(t.session_id) ?? new Set(),
213
+ intent_text: intentBySession.get(t.session_id),
214
+ };
215
+ byId.set(t.session_id, s);
216
+ }
217
+ else {
218
+ s.started_at = Math.min(s.started_at, t.started_at);
219
+ s.last_active_at = Math.max(s.last_active_at, t.ended_at);
220
+ }
221
+ }
222
+ return [...byId.values()];
223
+ }
224
+ /**
225
+ * IO orchestrator — fetches state from the store, runs stitchIntents, writes
226
+ * back. Safe to call repeatedly (idempotent: previously-attached sessions are
227
+ * skipped).
228
+ */
229
+ export async function runIntentStitch(store, opts = {}) {
230
+ const turns = await store.listTurns({ limit: 500 });
231
+ const markers = await store.listMarkers({ limit: 1000 });
232
+ const sessionIds = new Set(turns.map((t) => t.session_id));
233
+ const filesBySession = new Map();
234
+ for (const sid of sessionIds) {
235
+ const files = await store.getSessionFiles(sid);
236
+ filesBySession.set(sid, new Set(files));
237
+ }
238
+ const summaries = buildSessionSummaries(turns, markers, filesBySession);
239
+ const existingIntents = await store.listIntents({ limit: 500 });
240
+ const existingAttachments = [];
241
+ for (const i of existingIntents) {
242
+ const sessions = await store.listIntentSessions(i.intent_id);
243
+ for (const sid of sessions) {
244
+ existingAttachments.push({ intent_id: i.intent_id, session_id: sid });
245
+ }
246
+ }
247
+ const result = stitchIntents(summaries, existingIntents, existingAttachments, opts);
248
+ let created = 0;
249
+ const existingIds = new Set(existingIntents.map((i) => i.intent_id));
250
+ for (const i of result.intents) {
251
+ await store.upsertIntent(i);
252
+ if (!existingIds.has(i.intent_id))
253
+ created += 1;
254
+ }
255
+ for (const a of result.attachments) {
256
+ await store.attachSession(a.intent_id, a.session_id);
257
+ }
258
+ return {
259
+ created,
260
+ attached: result.attachments.length,
261
+ dormant: result.dormantTransitions.length,
262
+ };
263
+ }
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Loop Miner (ST-3a).
3
+ *
4
+ * Pure functions over shadow-ledger entries. Surfaces two confusion shapes:
5
+ * 1. File-read loops — same file read ≥5 times within a 10-minute window
6
+ * with no edit in between. Signal that the agent is rereading instead of
7
+ * progressing.
8
+ * 2. Query-search loops — same `search_code` query issued ≥3 times within
9
+ * a 20-minute window. Signal of a gap in the agent's mental model (the
10
+ * function was renamed, the symbol doesn't exist, etc.).
11
+ *
12
+ * Read-only: never writes anything. Callers (the dashboard route, the future
13
+ * insights panel) pass in entries from `ShadowLedger.getRecentEntries()`.
14
+ */
15
+ const READ_TOOLS = new Set(["file_read", "file_outline", "get_file", "Read"]);
16
+ const EDIT_TOOLS = new Set([
17
+ "Edit",
18
+ "Write",
19
+ "MultiEdit",
20
+ "NotebookEdit",
21
+ "edit_file",
22
+ "write_file",
23
+ ]);
24
+ const DEFAULT_READ_WINDOW_MS = 10 * 60_000;
25
+ const DEFAULT_QUERY_WINDOW_MS = 20 * 60_000;
26
+ const DEFAULT_READ_THRESHOLD = 5;
27
+ const DEFAULT_QUERY_THRESHOLD = 3;
28
+ function entryFilePath(entry) {
29
+ const fp = entry.args_summary?.file_path ??
30
+ entry.args_summary?.path;
31
+ return typeof fp === "string" && fp.length > 0 ? fp : null;
32
+ }
33
+ function entryTsMs(entry) {
34
+ return Date.parse(entry.ts);
35
+ }
36
+ export function detectFileReadLoops(entries, opts = {}) {
37
+ const window = opts.readWindowMs ?? DEFAULT_READ_WINDOW_MS;
38
+ const threshold = opts.readThreshold ?? DEFAULT_READ_THRESHOLD;
39
+ const now = opts.nowMs ?? maxTs(entries) ?? Date.now();
40
+ const cutoff = now - window;
41
+ const grouped = new Map();
42
+ for (const e of entries) {
43
+ const ts = entryTsMs(e);
44
+ if (!Number.isFinite(ts) || ts < cutoff)
45
+ continue;
46
+ const fp = entryFilePath(e);
47
+ if (!fp)
48
+ continue;
49
+ const key = `${e.session_id}::${fp}`;
50
+ let bucket = grouped.get(key);
51
+ if (!bucket) {
52
+ bucket = { reads: [], lastEditTs: 0, session_id: e.session_id };
53
+ grouped.set(key, bucket);
54
+ }
55
+ if (EDIT_TOOLS.has(e.tool)) {
56
+ // Edit clears the read run — only reads AFTER the last edit count.
57
+ bucket.reads = [];
58
+ bucket.lastEditTs = ts;
59
+ continue;
60
+ }
61
+ if (READ_TOOLS.has(e.tool)) {
62
+ bucket.reads.push(e);
63
+ }
64
+ }
65
+ const out = [];
66
+ for (const [key, bucket] of grouped) {
67
+ if (bucket.reads.length < threshold)
68
+ continue;
69
+ const fp = key.split("::").slice(1).join("::");
70
+ const first = bucket.reads[0];
71
+ const last = bucket.reads[bucket.reads.length - 1];
72
+ out.push({
73
+ kind: "file_reread",
74
+ file_path: fp,
75
+ count: bucket.reads.length,
76
+ first_ts: first.ts,
77
+ last_ts: last.ts,
78
+ session_id: bucket.session_id,
79
+ });
80
+ }
81
+ return out.sort((a, b) => b.count - a.count);
82
+ }
83
+ export function detectQueryLoops(entries, opts = {}) {
84
+ const window = opts.queryWindowMs ?? DEFAULT_QUERY_WINDOW_MS;
85
+ const threshold = opts.queryThreshold ?? DEFAULT_QUERY_THRESHOLD;
86
+ const now = opts.nowMs ?? maxTs(entries) ?? Date.now();
87
+ const cutoff = now - window;
88
+ const grouped = new Map();
89
+ for (const e of entries) {
90
+ if (e.tool !== "search_code")
91
+ continue;
92
+ const ts = entryTsMs(e);
93
+ if (!Number.isFinite(ts) || ts < cutoff)
94
+ continue;
95
+ const q = e.args_summary?.query;
96
+ if (typeof q !== "string" || q.length === 0)
97
+ continue;
98
+ const key = `${e.session_id}::${q}`;
99
+ const bucket = grouped.get(key) ?? [];
100
+ bucket.push(e);
101
+ grouped.set(key, bucket);
102
+ }
103
+ const out = [];
104
+ for (const [key, bucket] of grouped) {
105
+ if (bucket.length < threshold)
106
+ continue;
107
+ const query = key.split("::").slice(1).join("::");
108
+ const first = bucket[0];
109
+ const last = bucket[bucket.length - 1];
110
+ out.push({
111
+ kind: "search_repeat",
112
+ query,
113
+ count: bucket.length,
114
+ first_ts: first.ts,
115
+ last_ts: last.ts,
116
+ session_id: first.session_id,
117
+ });
118
+ }
119
+ return out.sort((a, b) => b.count - a.count);
120
+ }
121
+ /**
122
+ * Convenience aggregator — runs both detectors and returns the union sorted by
123
+ * recency.
124
+ */
125
+ export function detectLoops(entries, opts = {}) {
126
+ const all = [
127
+ ...detectFileReadLoops(entries, opts),
128
+ ...detectQueryLoops(entries, opts),
129
+ ];
130
+ return all.sort((a, b) => Date.parse(b.last_ts) - Date.parse(a.last_ts));
131
+ }
132
+ function maxTs(entries) {
133
+ let max = Number.NEGATIVE_INFINITY;
134
+ for (const e of entries) {
135
+ const t = entryTsMs(e);
136
+ if (Number.isFinite(t) && t > max)
137
+ max = t;
138
+ }
139
+ return Number.isFinite(max) ? max : null;
140
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Open Threads (ST-3a).
3
+ *
4
+ * Reads markers from timeline.db and returns mark_blocker rows that do not yet
5
+ * have a matching mark_resolution (matched by blocker_ref → marker_id).
6
+ * Resume strip + insights panel use this to surface unfinished work.
7
+ *
8
+ * Read-only: never writes anything.
9
+ */
10
+ /**
11
+ * Compute open threads from a pre-fetched list of markers. Pure — testable
12
+ * without a database. Resolutions whose blocker_ref points at a blocker outside
13
+ * this list are still counted (lookup by id, not membership).
14
+ */
15
+ export function computeOpenThreads(markers) {
16
+ const resolvedRefs = new Set();
17
+ for (const m of markers) {
18
+ if (m.type === "mark_resolution" && m.blocker_ref.length > 0) {
19
+ resolvedRefs.add(m.blocker_ref);
20
+ }
21
+ }
22
+ const out = [];
23
+ for (const m of markers) {
24
+ if (m.type !== "mark_blocker")
25
+ continue;
26
+ if (resolvedRefs.has(m.marker_id))
27
+ continue;
28
+ out.push({
29
+ marker_id: m.marker_id,
30
+ text: m.text,
31
+ session_id: m.session_id,
32
+ turn_id: m.turn_id,
33
+ ts: m.ts,
34
+ file_path: m.file_path,
35
+ });
36
+ }
37
+ return out.sort((a, b) => b.ts - a.ts);
38
+ }
39
+ /**
40
+ * Async convenience wrapper: pulls markers from the store, then runs
41
+ * computeOpenThreads. Limit caps the underlying marker fetch (default 500).
42
+ */
43
+ export async function getOpenThreads(store, opts = {}) {
44
+ const markers = await store.listMarkers({
45
+ sessionId: opts.sessionId,
46
+ limit: opts.limit ?? 500,
47
+ });
48
+ return computeOpenThreads(markers);
49
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Signal Reinforcer (ST-5).
3
+ *
4
+ * Reinforce / contradict derived timeline signals (hot files, loops, co-changes,
5
+ * conventions) in `timeline.db`. Never touches `facts.db` — Layer 9 reinforcement
6
+ * stays on its own path.
7
+ *
8
+ * Confidence clamps to [0, 1]. Reinforcement history is unbounded in storage but
9
+ * the API + UI surface only the most recent 10 events. Stale signals (no
10
+ * reinforcement in `staleAfterMs`) are evicted by `pruneStaleSignals`.
11
+ */
12
+ import { randomUUID } from "node:crypto";
13
+ const DEFAULT_STALE_MS = 14 * 24 * 60 * 60_000;
14
+ /**
15
+ * Reinforce (or contradict) a signal. Identity is either an explicit
16
+ * `signal_id` or a `(type, scope)` natural key — the function upserts a row
17
+ * if it doesn't exist yet, then appends a reinforcement event.
18
+ */
19
+ export async function reinforceSignal(store, identity, delta, source, opts = {}) {
20
+ const now = opts.nowMs ?? Date.now();
21
+ const signal = await resolveSignal(store, identity);
22
+ if (!signal) {
23
+ const created = {
24
+ signal_id: "signal_id" in identity ? identity.signal_id : randomUUID(),
25
+ type: "type" in identity ? identity.type : "unknown",
26
+ scope: "scope" in identity ? identity.scope : "",
27
+ content: "type" in identity ? (identity.content ?? "") : "",
28
+ confidence: clamp01(0.5 + delta),
29
+ first_seen_at: now,
30
+ last_seen_at: now,
31
+ };
32
+ await store.upsertSignal(created);
33
+ await store.appendReinforcement(created.signal_id, now, delta, source);
34
+ return created;
35
+ }
36
+ signal.confidence = clamp01(signal.confidence + delta);
37
+ signal.last_seen_at = now;
38
+ await store.upsertSignal(signal);
39
+ await store.appendReinforcement(signal.signal_id, now, delta, source);
40
+ return signal;
41
+ }
42
+ async function resolveSignal(store, identity) {
43
+ if ("signal_id" in identity) {
44
+ return store.getSignal(identity.signal_id);
45
+ }
46
+ const all = await store.listSignals({ type: identity.type, limit: 500 });
47
+ return all.find((s) => s.scope === identity.scope) ?? null;
48
+ }
49
+ export async function pruneStaleSignals(store, opts = {}) {
50
+ const now = opts.nowMs ?? Date.now();
51
+ const cutoff = now - (opts.staleAfterMs ?? DEFAULT_STALE_MS);
52
+ return store.deleteSignalsBefore(cutoff);
53
+ }
54
+ function clamp01(v) {
55
+ if (!Number.isFinite(v))
56
+ return 0;
57
+ if (v < 0)
58
+ return 0;
59
+ if (v > 1)
60
+ return 1;
61
+ return v;
62
+ }
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Timeline subsystem bootstrap.
3
+ *
4
+ * Wires together the three pieces of the Timeline Layer:
5
+ * 1. CozoTimelineStore (timeline.db)
6
+ * 2. The TurnSegmenter embedded in the active ShadowLedger
7
+ * 3. A turn-close listener that computes a rollup and upserts it into turns
8
+ *
9
+ * Kill-switch: `UNERR_TIMELINE_V2=0` short-circuits everything — no db is
10
+ * opened, no listener attaches, the existing proxy/mcp-server code paths are
11
+ * unaffected. Same is true if startup throws — the bootstrap swallows errors
12
+ * after logging so the host process never crashes on timeline issues.
13
+ *
14
+ * No reads or writes to graph.db / facts.db. No edits to the shadow ledger.
15
+ */
16
+ import { CozoTimelineStore } from "./timeline-store.js";
17
+ const EDIT_TOOLS = new Set([
18
+ "Edit",
19
+ "Write",
20
+ "MultiEdit",
21
+ "NotebookEdit",
22
+ "edit_file",
23
+ "write_file",
24
+ ]);
25
+ function defaultLog(level, msg) {
26
+ process.stderr.write(`[unerr:timeline] ${level.toUpperCase()}: ${msg}\n`);
27
+ }
28
+ /**
29
+ * Start the timeline subsystem. Returns null when disabled via env, or when
30
+ * initialisation fails (after logging the error). Callers should treat null
31
+ * as "timeline is off" and continue normally.
32
+ */
33
+ export async function startTimelineBootstrap(opts) {
34
+ if (process.env.UNERR_TIMELINE_V2 === "0")
35
+ return null;
36
+ const log = opts.log ?? defaultLog;
37
+ let store;
38
+ try {
39
+ store = await CozoTimelineStore.create(opts.projectRoot);
40
+ }
41
+ catch (err) {
42
+ log("warn", `init failed: ${err instanceof Error ? err.message : String(err)}`);
43
+ return null;
44
+ }
45
+ const segmenter = opts.ledger.getTurnSegmenter();
46
+ const unsubscribe = segmenter.onTurnClose((event) => {
47
+ const entries = opts.ledger
48
+ .getRecentEntries(100)
49
+ .filter((e) => e.turn_id === event.turn_id);
50
+ const rollup = computeTurnRollup(event, entries);
51
+ store.upsertTurn(rollup).catch((err) => {
52
+ log("warn", `upsertTurn failed for ${rollup.turn_id}: ${err instanceof Error ? err.message : String(err)}`);
53
+ });
54
+ // ST-4: persist distinct files touched in this turn so the intent stitcher
55
+ // has a file set to Jaccard against.
56
+ const files = collectFilePaths(entries);
57
+ if (files.length > 0) {
58
+ store
59
+ .recordSessionFiles(event.session_id, files)
60
+ .catch((err) => {
61
+ log("warn", `recordSessionFiles failed for ${event.session_id}: ${err instanceof Error ? err.message : String(err)}`);
62
+ });
63
+ }
64
+ // UX-2: best-effort agent name capture. Lazy resolver so late identity
65
+ // (after MCP `initialize`) still gets recorded.
66
+ const agentName = opts.getAgentName?.();
67
+ if (agentName) {
68
+ store
69
+ .setSessionAgent(event.session_id, agentName)
70
+ .catch((err) => {
71
+ log("warn", `setSessionAgent failed for ${event.session_id}: ${err instanceof Error ? err.message : String(err)}`);
72
+ });
73
+ }
74
+ });
75
+ log("info", `active (db ${store.dbPath})`);
76
+ return {
77
+ store,
78
+ stop: () => {
79
+ unsubscribe();
80
+ store.close();
81
+ },
82
+ };
83
+ }
84
+ /**
85
+ * Build a turn rollup from a close event + the entries that belong to it.
86
+ * Exported for testing.
87
+ */
88
+ export function computeTurnRollup(event, entries) {
89
+ const tsValues = entries
90
+ .map((e) => Date.parse(e.ts))
91
+ .filter((n) => Number.isFinite(n));
92
+ const started_at = tsValues.length > 0 ? Math.min(...tsValues) : event.closed_at;
93
+ const filePaths = new Set();
94
+ let edit_count = 0;
95
+ let intentText;
96
+ const opened_by = entries[0]?.turn_confidence ?? "first_call";
97
+ for (const e of entries) {
98
+ const fp = e.args_summary?.file_path ??
99
+ e.args_summary?.path;
100
+ if (typeof fp === "string" && fp.length > 0)
101
+ filePaths.add(fp);
102
+ if (EDIT_TOOLS.has(e.tool))
103
+ edit_count += 1;
104
+ if (!intentText &&
105
+ e.tool === "mark_intent" &&
106
+ typeof e.args_summary?.text === "string") {
107
+ intentText = e.args_summary.text;
108
+ }
109
+ }
110
+ const title = intentText ?? deriveTitleFromFiles(filePaths);
111
+ return {
112
+ turn_id: event.turn_id,
113
+ session_id: event.session_id,
114
+ started_at,
115
+ ended_at: event.closed_at,
116
+ opened_by,
117
+ closed_reason: event.reason,
118
+ tool_count: entries.length,
119
+ file_count: filePaths.size,
120
+ edit_count,
121
+ title,
122
+ outcome: "unknown",
123
+ };
124
+ }
125
+ /** Extract distinct file paths touched by these entries. */
126
+ export function collectFilePaths(entries) {
127
+ const set = new Set();
128
+ for (const e of entries) {
129
+ const fp = e.args_summary?.file_path ??
130
+ e.args_summary?.path;
131
+ if (typeof fp === "string" && fp.length > 0)
132
+ set.add(fp);
133
+ }
134
+ return [...set];
135
+ }
136
+ function deriveTitleFromFiles(paths) {
137
+ if (paths.size === 0)
138
+ return "";
139
+ // Pick the shortest path as a rough proxy for "most important file" (entry
140
+ // points, index files, top-level modules tend to be shorter). Cheap heuristic
141
+ // until ST-3 adds a smarter title miner.
142
+ let shortest = null;
143
+ for (const p of paths) {
144
+ if (shortest === null || p.length < shortest.length)
145
+ shortest = p;
146
+ }
147
+ if (!shortest)
148
+ return "";
149
+ const base = shortest.split("/").pop() ?? shortest;
150
+ return base;
151
+ }