@unerr-ai/unerr 0.1.0 → 0.1.1

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 +992 -706
  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 +299 -0
  553. package/dist/ui/assets/index-B-0HTtUR.js +0 -10
@@ -0,0 +1,190 @@
1
+ /**
2
+ * Sprint L5.2 — Network Boundary Contract Tests
3
+ *
4
+ * Static contract tests verifying that every cloud-dependent service
5
+ * is structurally disabled (null / NullProxy) in Local Mode. These
6
+ * tests catch regressions at the contract level — even if a future
7
+ * code change introduces a network call, these tests will catch it.
8
+ *
9
+ * A security-conscious user reading these tests should be convinced
10
+ * that their data never leaves their machine in Local Mode.
11
+ *
12
+ * CONTRACT: TL-1, TL-3, TL-15
13
+ */
14
+ import { afterEach, describe, expect, it, vi } from "vitest";
15
+ import { getBlockedCount, resetBlockedCount, seal, unseal, } from "../proxy/network-firewall.js";
16
+ // ── Shared Mock Graph ──────────────────────────────────────────────
17
+ function createMockGraph() {
18
+ const mockEntity = {
19
+ key: "fn::test",
20
+ kind: "function",
21
+ name: "testFn",
22
+ file_path: "src/test.ts",
23
+ start_line: 1,
24
+ end_line: 10,
25
+ signature: "(): void",
26
+ body: "function testFn() {}",
27
+ fan_in: 2,
28
+ fan_out: 1,
29
+ risk_level: "normal",
30
+ };
31
+ const mockDb = {
32
+ run: vi.fn((_query) => ({
33
+ rows: [],
34
+ })),
35
+ };
36
+ return {
37
+ db: mockDb,
38
+ getEntity: vi.fn().mockReturnValue(mockEntity),
39
+ getCallersOf: vi.fn().mockReturnValue([]),
40
+ getCalleesOf: vi.fn().mockReturnValue([]),
41
+ getEntitiesByFile: vi.fn().mockReturnValue([mockEntity]),
42
+ searchEntities: vi.fn().mockReturnValue([mockEntity]),
43
+ getImports: vi.fn().mockReturnValue([]),
44
+ hasRules: vi.fn().mockReturnValue(true),
45
+ getRules: vi.fn().mockReturnValue([]),
46
+ getPatterns: vi
47
+ .fn()
48
+ .mockReturnValue([
49
+ { name: "test-pattern", type: "naming", adherence: 0.9 },
50
+ ]),
51
+ hasJustifications: vi.fn().mockReturnValue(true),
52
+ getBusinessContext: vi.fn().mockReturnValue(null),
53
+ getConventions: vi.fn().mockReturnValue([]),
54
+ getCrossBoundaryLinks: vi.fn().mockReturnValue([]),
55
+ getCriticalNodes: vi.fn().mockReturnValue([]),
56
+ getDriftEntitiesForFile: vi.fn().mockReturnValue([]),
57
+ upsertDriftEntity: vi.fn(),
58
+ removeDriftEntity: vi.fn(),
59
+ clearDriftOverlay: vi.fn(),
60
+ getDriftSummary: vi
61
+ .fn()
62
+ .mockReturnValue({ added: 0, modified: 0, deleted: 0, total: 0 }),
63
+ healthCheck: vi.fn().mockReturnValue({ status: "ok", latencyMs: 1 }),
64
+ isLoaded: vi.fn().mockReturnValue(true),
65
+ loadSnapshot: vi.fn(),
66
+ loadRules: vi.fn(),
67
+ loadPatterns: vi.fn(),
68
+ loadJustifications: vi.fn(),
69
+ applyDelta: vi.fn().mockReturnValue({
70
+ applied: 0,
71
+ deleted: 0,
72
+ edges: 0,
73
+ justifications: 0,
74
+ overlayExpired: 0,
75
+ }),
76
+ getLocalProjectStats: vi.fn().mockReturnValue({
77
+ fileCount: 100,
78
+ entityCount: 500,
79
+ edgeCount: 1200,
80
+ communityCount: 10,
81
+ ruleCount: 5,
82
+ }),
83
+ getDeepDiveProjectState: vi.fn().mockReturnValue("none"),
84
+ persistCorrections: vi.fn(),
85
+ getRuleHealthSummary: vi.fn().mockReturnValue(null),
86
+ getRuleExceptions: vi.fn().mockReturnValue([]),
87
+ };
88
+ }
89
+ // ── Test Suite ─────────────────────────────────────────────────────
90
+ describe("Network Boundary Contracts (L5.2)", () => {
91
+ afterEach(() => {
92
+ unseal();
93
+ resetBlockedCount();
94
+ });
95
+ it("local-routed tools resolve without network calls", async () => {
96
+ seal();
97
+ const { QueryRouter } = await import("../intelligence/query-router.js");
98
+ const graph = createMockGraph();
99
+ const router = new QueryRouter(graph);
100
+ router.setMode("local", "Local Mode");
101
+ await router.execute("get_function", { key: "fn::test" });
102
+ await router.execute("search_code", { query: "test" });
103
+ await router.execute("get_callers", { key: "fn::test" });
104
+ await router.execute("get_conventions", {});
105
+ expect(getBlockedCount()).toBe(0);
106
+ });
107
+ it("Local Mode QueryRouter handles errors locally (TL-15)", async () => {
108
+ seal();
109
+ const { QueryRouter } = await import("../intelligence/query-router.js");
110
+ const graph = createMockGraph();
111
+ graph.getEntity = vi.fn().mockImplementation(() => {
112
+ throw new Error("Simulated local failure");
113
+ });
114
+ const router = new QueryRouter(graph);
115
+ router.setMode("local", "Local Mode");
116
+ const result = await router.execute("get_function", { key: "nonexistent" });
117
+ expect(result._meta.source).toBe("local");
118
+ expect(result.content).toHaveProperty("error");
119
+ expect(getBlockedCount()).toBe(0);
120
+ });
121
+ it("NetworkFirewall rejects app.unerr.dev when sealed", async () => {
122
+ seal();
123
+ const result = await globalThis
124
+ .fetch("https://app.unerr.dev/api/repos/test/profile")
125
+ .catch((e) => e);
126
+ expect(result).toBeInstanceOf(Error);
127
+ expect(result.message).toContain("[NetworkFirewall]");
128
+ expect(getBlockedCount()).toBe(1);
129
+ });
130
+ it("NetworkFirewall rejects api.anthropic.com when sealed (no allowlist)", async () => {
131
+ seal();
132
+ const result = await globalThis
133
+ .fetch("https://api.anthropic.com/v1/messages")
134
+ .catch((e) => e);
135
+ expect(result).toBeInstanceOf(Error);
136
+ expect(result.message).toContain("[NetworkFirewall]");
137
+ expect(getBlockedCount()).toBe(1);
138
+ });
139
+ it("NetworkFirewall allows localhost (Ollama, LM Studio) when sealed", async () => {
140
+ seal();
141
+ const localhostUrls = [
142
+ "http://localhost:11434/api/tags",
143
+ "http://127.0.0.1:1234/v1/models",
144
+ "http://localhost:8080/health",
145
+ ];
146
+ for (const url of localhostUrls) {
147
+ const result = await globalThis.fetch(url).catch((e) => e);
148
+ if (result instanceof Error) {
149
+ expect(result.message).not.toContain("[NetworkFirewall]");
150
+ }
151
+ }
152
+ expect(getBlockedCount()).toBe(0);
153
+ });
154
+ it("local-routed tools resolve from CozoDB with zero network calls", async () => {
155
+ seal();
156
+ const { QueryRouter } = await import("../intelligence/query-router.js");
157
+ const graph = createMockGraph();
158
+ const router = new QueryRouter(graph);
159
+ router.setMode("local", "Local Mode");
160
+ const localOnlyTools = [
161
+ { name: "get_function", args: { key: "fn::test" } },
162
+ { name: "get_class", args: { key: "fn::test" } },
163
+ { name: "get_file", args: { key: "src/test.ts" } },
164
+ { name: "get_callers", args: { key: "fn::test" } },
165
+ { name: "get_callees", args: { key: "fn::test" } },
166
+ { name: "get_imports", args: { file_path: "src/test.ts" } },
167
+ { name: "search_code", args: { query: "test" } },
168
+ { name: "get_rules", args: {} },
169
+ { name: "get_business_context", args: { key: "fn::test" } },
170
+ { name: "get_conventions", args: {} },
171
+ { name: "get_cross_boundary_links", args: {} },
172
+ { name: "get_critical_nodes", args: {} },
173
+ ];
174
+ for (const tool of localOnlyTools) {
175
+ const result = await router.execute(tool.name, tool.args);
176
+ expect(result._meta.source).toBe("local");
177
+ }
178
+ expect(getBlockedCount()).toBe(0);
179
+ });
180
+ it("get_project_stats returns from local CozoDB (not cloud API)", async () => {
181
+ seal();
182
+ const { QueryRouter } = await import("../intelligence/query-router.js");
183
+ const graph = createMockGraph();
184
+ const router = new QueryRouter(graph);
185
+ router.setMode("local", "Local Mode");
186
+ const result = await router.execute("get_project_stats", {});
187
+ expect(result._meta.source).toBe("local");
188
+ expect(getBlockedCount()).toBe(0);
189
+ });
190
+ });
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Tests for Sprint L8.4 — Network Firewall allowlist & anthropic-direct exception.
3
+ */
4
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
+ import { addAllowedHost, addAllowedUrl, getBlockedCount, isSealed, resetBlockedCount, seal, unseal, } from "../proxy/network-firewall.js";
6
+ describe("NetworkFirewall (L8.4)", () => {
7
+ beforeEach(() => {
8
+ unseal();
9
+ resetBlockedCount();
10
+ });
11
+ afterEach(() => {
12
+ unseal();
13
+ resetBlockedCount();
14
+ });
15
+ it("addAllowedHost before seal allows that host", async () => {
16
+ addAllowedHost("api.anthropic.com");
17
+ seal();
18
+ expect(isSealed()).toBe(true);
19
+ // Should NOT block api.anthropic.com
20
+ const resp = await globalThis
21
+ .fetch("https://api.anthropic.com/v1/messages")
22
+ .catch((e) => e);
23
+ // If it's a network error (server not reachable), that's fine — it wasn't BLOCKED
24
+ if (resp instanceof Error) {
25
+ expect(resp.message).not.toContain("[NetworkFirewall]");
26
+ }
27
+ expect(getBlockedCount()).toBe(0);
28
+ });
29
+ it("addAllowedUrl extracts hostname and allows it", async () => {
30
+ addAllowedUrl("http://localhost:11434");
31
+ seal();
32
+ // localhost is always allowed, but this tests the URL parsing path
33
+ expect(isSealed()).toBe(true);
34
+ expect(getBlockedCount()).toBe(0);
35
+ });
36
+ it("blocks non-allowlisted hosts after seal", async () => {
37
+ seal();
38
+ const result = await globalThis
39
+ .fetch("https://app.unerr.dev/api/health")
40
+ .catch((e) => e);
41
+ expect(result).toBeInstanceOf(Error);
42
+ expect(result.message).toContain("[NetworkFirewall]");
43
+ expect(result.message).toContain("app.unerr.dev");
44
+ expect(getBlockedCount()).toBe(1);
45
+ });
46
+ it("app.unerr.dev is NEVER in allowlist", async () => {
47
+ addAllowedHost("api.anthropic.com");
48
+ seal();
49
+ const result = await globalThis
50
+ .fetch("https://app.unerr.dev/api/health")
51
+ .catch((e) => e);
52
+ expect(result).toBeInstanceOf(Error);
53
+ expect(result.message).toContain("[NetworkFirewall]");
54
+ expect(getBlockedCount()).toBe(1);
55
+ });
56
+ it("addAllowedHost throws after seal (immutability)", () => {
57
+ seal();
58
+ expect(() => addAllowedHost("evil.com")).toThrow("firewall is already sealed");
59
+ });
60
+ it("addAllowedUrl throws after seal (immutability)", () => {
61
+ seal();
62
+ expect(() => addAllowedUrl("https://evil.com")).toThrow("firewall is already sealed");
63
+ });
64
+ it("localhost always allowed without explicit allowlist", async () => {
65
+ seal();
66
+ const result = await globalThis
67
+ .fetch("http://localhost:11434/api/tags")
68
+ .catch((e) => e);
69
+ // Should not be a firewall error (may be ECONNREFUSED if Ollama isn't running)
70
+ if (result instanceof Error) {
71
+ expect(result.message).not.toContain("[NetworkFirewall]");
72
+ }
73
+ expect(getBlockedCount()).toBe(0);
74
+ });
75
+ it("seal() with allowedBaseUrls still works (backwards compat)", async () => {
76
+ seal(["http://localhost:1234"]);
77
+ expect(isSealed()).toBe(true);
78
+ expect(getBlockedCount()).toBe(0);
79
+ });
80
+ it("anthropic-direct: api.anthropic.com allowed, other hosts blocked", async () => {
81
+ addAllowedHost("api.anthropic.com");
82
+ seal();
83
+ // Anthropic allowed
84
+ const anthropicResult = await globalThis
85
+ .fetch("https://api.anthropic.com/v1/messages")
86
+ .catch((e) => e);
87
+ if (anthropicResult instanceof Error) {
88
+ expect(anthropicResult.message).not.toContain("[NetworkFirewall]");
89
+ }
90
+ // Random host blocked
91
+ const otherResult = await globalThis
92
+ .fetch("https://evil.example.com/steal")
93
+ .catch((e) => e);
94
+ expect(otherResult).toBeInstanceOf(Error);
95
+ expect(otherResult.message).toContain("[NetworkFirewall]");
96
+ expect(getBlockedCount()).toBe(1);
97
+ });
98
+ it("unseal clears allowlist and restores fetch", () => {
99
+ addAllowedHost("api.anthropic.com");
100
+ seal();
101
+ expect(isSealed()).toBe(true);
102
+ unseal();
103
+ expect(isSealed()).toBe(false);
104
+ // After unseal + re-seal, anthropic should be blocked (allowlist cleared)
105
+ seal();
106
+ // No addAllowedHost this time — anthropic should be blocked
107
+ void globalThis
108
+ .fetch("https://api.anthropic.com/v1/messages")
109
+ .catch(() => { });
110
+ expect(getBlockedCount()).toBe(1);
111
+ });
112
+ });
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Nudge clarity invariants — block any regression to the abstract-placeholder
3
+ * or hedge-verb patterns documented in CLAUDE.md "Writing nudges and hints".
4
+ *
5
+ * Anything the agent reads as advisory (Consider/Verify/Review/Check) or
6
+ * abstract (`:N`, `<name>`) creates retry loops or silent drops. These tests
7
+ * fail loud if either pattern returns.
8
+ */
9
+ import { readFileSync } from "node:fs";
10
+ import { join } from "node:path";
11
+ import { describe, expect, it } from "vitest";
12
+ import { SIGNAL_PREFIX_LEGEND } from "../proxy/response-envelope.js";
13
+ const HEDGE_VERBS = [
14
+ /\bConsider\b/,
15
+ /\bVerify\b/,
16
+ /\bReview\b/,
17
+ /\bCheck\b/, // followed by space (avoid "Checked", "checkbox" in identifiers)
18
+ /\bmay want to\b/i,
19
+ ];
20
+ const ROOT = join(__dirname, "..");
21
+ function readSource(rel) {
22
+ return readFileSync(join(ROOT, rel), "utf-8");
23
+ }
24
+ /**
25
+ * Extract every `action: "..."` string literal from a source file. Skips
26
+ * action: undefined and action: variable-reference forms.
27
+ */
28
+ function extractActionLiterals(src) {
29
+ const out = [];
30
+ // Match: action: "..." or action: `...`
31
+ const re = /action:\s*(?:"([^"\\]*(?:\\.[^"\\]*)*)"|`([^`\\]*(?:\\.[^`\\]*)*)`)/g;
32
+ let m;
33
+ m = re.exec(src);
34
+ while (m !== null) {
35
+ const literal = m[1] ?? m[2];
36
+ if (literal)
37
+ out.push(literal);
38
+ m = re.exec(src);
39
+ }
40
+ return out;
41
+ }
42
+ describe("nudge invariants — SIGNAL_PREFIX_LEGEND", () => {
43
+ it("contains no literal `:N` cursor placeholder", () => {
44
+ // The page hint format example must use concrete-value language, not :N.
45
+ // Concrete shape uses <nextValue>, <remaining>, etc. as descriptive
46
+ // placeholders inside angle brackets — those are documentation form,
47
+ // not emission form.
48
+ expect(SIGNAL_PREFIX_LEGEND).not.toMatch(/:N\b/);
49
+ });
50
+ it("contains no bare `<name>` parameter placeholder", () => {
51
+ // Angle-bracket descriptors are allowed in format documentation
52
+ // (<tool>, <cursorArg>, <nextValue>) but the literal "<name>" was the
53
+ // pre-rewrite stand-in for an entity that never got substituted.
54
+ expect(SIGNAL_PREFIX_LEGEND).not.toContain("<name>");
55
+ });
56
+ it("uses imperative verbs, not hedge verbs", () => {
57
+ for (const re of HEDGE_VERBS) {
58
+ expect(SIGNAL_PREFIX_LEGEND).not.toMatch(re);
59
+ }
60
+ });
61
+ });
62
+ describe("nudge invariants — signal-scorer action strings", () => {
63
+ const src = readSource("intelligence/signal-scorer.ts");
64
+ const actions = extractActionLiterals(src);
65
+ it("scorer file has multiple action: literals (sanity check)", () => {
66
+ expect(actions.length).toBeGreaterThan(5);
67
+ });
68
+ it("no action string uses hedge verbs", () => {
69
+ for (const action of actions) {
70
+ for (const re of HEDGE_VERBS) {
71
+ if (re.test(action)) {
72
+ throw new Error(`signal-scorer action uses hedge verb (${re}): "${action}"`);
73
+ }
74
+ }
75
+ }
76
+ });
77
+ it("no action string uses the deictic phrase 'this entity / pattern / file'", () => {
78
+ for (const action of actions) {
79
+ // The audit explicitly identified 'this entity', 'this pattern',
80
+ // 'this file' as the recurring deictic anti-pattern.
81
+ expect(action).not.toMatch(/\bthis (entity|pattern|file)\b/);
82
+ }
83
+ });
84
+ });
85
+ describe("nudge invariants — isError reaches the agent's MCP context", () => {
86
+ const src = readSource("proxy/proxy.ts");
87
+ const lines = src.split("\n");
88
+ /**
89
+ * For every line that writes a `[unerr]` error to stderr, the surrounding
90
+ * window (next 12 lines) must include `isError: true`. This couples the
91
+ * human-debug channel (stderr → .unerr/logs/) with the agent-context
92
+ * channel (MCP CallToolResult.isError). The user's original concern:
93
+ * "make sure isError writes to err stream AND err stream gets into the
94
+ * coding agent context" — codified.
95
+ */
96
+ it("every stderr error log in proxy.ts pairs with an isError:true on the wire", () => {
97
+ const orphans = [];
98
+ for (let i = 0; i < lines.length; i++) {
99
+ const raw = lines[i];
100
+ if (!raw)
101
+ continue;
102
+ // Match the stderr error pattern we standardized on.
103
+ if (!/process\.stderr\.write\([\s\S]*\[unerr\][^)]*(failed|threw|disabled|validation)/i.test(raw))
104
+ continue;
105
+ // Look ahead up to 12 lines for `isError: true`.
106
+ const window = lines.slice(i, Math.min(lines.length, i + 13)).join("\n");
107
+ if (!/isError:\s*true/.test(window)) {
108
+ orphans.push({ line: i + 1, text: raw.trim().slice(0, 100) });
109
+ }
110
+ }
111
+ if (orphans.length > 0) {
112
+ const msg = orphans
113
+ .map((o) => ` proxy.ts:${o.line} → ${o.text}`)
114
+ .join("\n");
115
+ throw new Error(`${orphans.length} stderr error log(s) in proxy.ts do not set isError:true within 12 lines. Pair every human-debug log with a wire isError so the agent sees the failure:\n${msg}`);
116
+ }
117
+ });
118
+ it("contains the expected error-routing sites (sanity check)", () => {
119
+ // Documents the four MCP error paths that must surface isError to MCP
120
+ // clients. If anyone removes a path, this fires.
121
+ expect(src).toMatch(/record_fact failed/);
122
+ expect(src).toMatch(/recall_facts failed/);
123
+ expect(src).toMatch(/tools\/call validation failed for/);
124
+ expect(src).toMatch(/router\.execute\(\$\{name\}\) threw/);
125
+ });
126
+ });
127
+ describe("nudge invariants — wire-cap nudges", () => {
128
+ const src = readSource("proxy/wire-cap.ts");
129
+ it("the buildPageHint template uses a numeric cursor (no `:N` in code)", () => {
130
+ // The page-hint template now interpolates `${nextCursor}`. If anyone
131
+ // ever reverts to a literal `:N`, this catches it.
132
+ expect(src).not.toMatch(/\$\{cursorArg\}:N/);
133
+ expect(src).not.toMatch(/`ur\|pg \$\{toolName\}[^`]*:N[`\s—]/);
134
+ });
135
+ it("PER_TOOL_CAPS.filterHint values are concrete (no `<name>` / `:T` / `:V` placeholders)", () => {
136
+ // Page-hint format: `ur|pg <tool> +N — <cursor>:<n>/<filterHint>`. The
137
+ // filterHint is appended verbatim. Literal placeholders (`<name>`, `:T`,
138
+ // `:V`) train the agent to paste the placeholder instead of substituting
139
+ // a real value — the exact anti-pattern this audit eliminated. Concrete
140
+ // values or pipe-separated enums only.
141
+ const capMatches = src.matchAll(/filterHint:\s*"([^"]+)"/g);
142
+ const offenders = [];
143
+ for (const match of capMatches) {
144
+ const hint = match[1] ?? "";
145
+ // Forbid angle-bracket placeholders (`<name>`, `<entity>`, etc).
146
+ if (/<[^>]+>/.test(hint)) {
147
+ offenders.push(`${hint} (contains <…> placeholder)`);
148
+ continue;
149
+ }
150
+ // Forbid single-letter trailing placeholders (`fact_type:T`, `:V`).
151
+ // A single-uppercase-letter value never reads as concrete.
152
+ if (/:[A-Z](\s|$|\/|\|)/.test(hint)) {
153
+ offenders.push(`${hint} (single-letter placeholder)`);
154
+ }
155
+ }
156
+ if (offenders.length > 0) {
157
+ throw new Error(`PER_TOOL_CAPS filterHint values must be concrete:\n${offenders.map((o) => ` - ${o}`).join("\n")}`);
158
+ }
159
+ });
160
+ });