@unerr-ai/unerr 0.1.6 → 0.1.7

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 (218) hide show
  1. package/README.md +70 -194
  2. package/dist/cli.js +39149 -36991
  3. package/package.json +9 -2
  4. package/dist/__tests__/architecture-guard.test.js +0 -122
  5. package/dist/__tests__/arg-validator.test.js +0 -205
  6. package/dist/__tests__/ast-extractor.test.js +0 -203
  7. package/dist/__tests__/auto-bootstrap.test.js +0 -280
  8. package/dist/__tests__/background-indexer.test.js +0 -228
  9. package/dist/__tests__/blast-radius-engine.test.js +0 -200
  10. package/dist/__tests__/bridge-isolation.test.js +0 -37
  11. package/dist/__tests__/budget-enforcer.test.js +0 -53
  12. package/dist/__tests__/cfg-test-detection-perf.test.js +0 -82
  13. package/dist/__tests__/change-narrative.test.js +0 -190
  14. package/dist/__tests__/check-commit.test.js +0 -258
  15. package/dist/__tests__/checksum.test.js +0 -34
  16. package/dist/__tests__/commit-watcher.test.js +0 -154
  17. package/dist/__tests__/community-detection.test.js +0 -179
  18. package/dist/__tests__/community-tools.test.js +0 -299
  19. package/dist/__tests__/components.test.js +0 -449
  20. package/dist/__tests__/compression-log.test.js +0 -174
  21. package/dist/__tests__/compression-quality-monitor.test.js +0 -40
  22. package/dist/__tests__/config-healer.test.js +0 -165
  23. package/dist/__tests__/context-ledger.test.js +0 -58
  24. package/dist/__tests__/convention-detector.test.js +0 -99
  25. package/dist/__tests__/convention-learner.test.js +0 -86
  26. package/dist/__tests__/correction-detector.test.js +0 -330
  27. package/dist/__tests__/daemon-autostart-install.test.js +0 -283
  28. package/dist/__tests__/daemon-bridge.test.js +0 -222
  29. package/dist/__tests__/daemon-dashboard.test.js +0 -202
  30. package/dist/__tests__/daemon-registry.test.js +0 -240
  31. package/dist/__tests__/daemon-supervisor.test.js +0 -318
  32. package/dist/__tests__/daemon-version-check.test.js +0 -275
  33. package/dist/__tests__/decision-point-detector.test.js +0 -98
  34. package/dist/__tests__/deep-link.test.js +0 -143
  35. package/dist/__tests__/disallowed-tools.test.js +0 -115
  36. package/dist/__tests__/drift-tracker.test.js +0 -582
  37. package/dist/__tests__/durability-scorer.test.js +0 -152
  38. package/dist/__tests__/efficiency-tracker.test.js +0 -65
  39. package/dist/__tests__/enrich.test.js +0 -144
  40. package/dist/__tests__/entity-rewind.test.js +0 -248
  41. package/dist/__tests__/ephemeral.test.js +0 -111
  42. package/dist/__tests__/exploration-cost.test.js +0 -93
  43. package/dist/__tests__/fact-generator.test.js +0 -197
  44. package/dist/__tests__/file-l0-graph.test.js +0 -244
  45. package/dist/__tests__/file-logger.test.js +0 -82
  46. package/dist/__tests__/file-outline.test.js +0 -141
  47. package/dist/__tests__/file-read-protocol.test.js +0 -188
  48. package/dist/__tests__/format-encoder.test.js +0 -233
  49. package/dist/__tests__/git-attribution.test.js +0 -259
  50. package/dist/__tests__/graph-temporal-joiner.test.js +0 -219
  51. package/dist/__tests__/health-grade-enhanced.test.js +0 -138
  52. package/dist/__tests__/health-map-data.test.js +0 -173
  53. package/dist/__tests__/helpers/mcp-harness.js +0 -45
  54. package/dist/__tests__/helpers/mcp-harness.test.js +0 -68
  55. package/dist/__tests__/hook-dedup.test.js +0 -112
  56. package/dist/__tests__/hook-runner.test.js +0 -253
  57. package/dist/__tests__/indexer-cfg.test.js +0 -185
  58. package/dist/__tests__/indexer-cross-file.test.js +0 -172
  59. package/dist/__tests__/indexer-extraction.test.js +0 -245
  60. package/dist/__tests__/indexer-incremental.test.js +0 -232
  61. package/dist/__tests__/indexer-language-expansion.test.js +0 -165
  62. package/dist/__tests__/init-push.test.js +0 -131
  63. package/dist/__tests__/instruction-writer.test.js +0 -179
  64. package/dist/__tests__/intelligence-integration.test.js +0 -217
  65. package/dist/__tests__/intent-correlator.test.js +0 -175
  66. package/dist/__tests__/intent-detector.test.js +0 -235
  67. package/dist/__tests__/intent-encoder.test.js +0 -167
  68. package/dist/__tests__/java-build-tool-detection.test.js +0 -174
  69. package/dist/__tests__/layer3-sprint-q.test.js +0 -160
  70. package/dist/__tests__/layer3-sprint-r.test.js +0 -91
  71. package/dist/__tests__/layer3-sprint-s.test.js +0 -183
  72. package/dist/__tests__/layer3-sprint-t.test.js +0 -201
  73. package/dist/__tests__/layer3-sprint-u.test.js +0 -174
  74. package/dist/__tests__/layer4-sprint-ba2.test.js +0 -354
  75. package/dist/__tests__/layer4-sprint-ba4.test.js +0 -84
  76. package/dist/__tests__/layer4-sprint-vs.test.js +0 -105
  77. package/dist/__tests__/ledger-chains.test.js +0 -162
  78. package/dist/__tests__/lifecycle-machine.test.js +0 -226
  79. package/dist/__tests__/local-chat-provider.test.js +0 -170
  80. package/dist/__tests__/local-convention-detector.test.js +0 -308
  81. package/dist/__tests__/local-embeddings.test.js +0 -422
  82. package/dist/__tests__/local-graph.test.js +0 -540
  83. package/dist/__tests__/local-indexer.test.js +0 -228
  84. package/dist/__tests__/local-intelligence-l3.test.js +0 -332
  85. package/dist/__tests__/local-llm.test.js +0 -253
  86. package/dist/__tests__/local-mode-offline.test.js +0 -187
  87. package/dist/__tests__/local-mode-stats.test.js +0 -273
  88. package/dist/__tests__/local-mode-tui.test.js +0 -343
  89. package/dist/__tests__/local-parse.test.js +0 -199
  90. package/dist/__tests__/log-tailer.test.js +0 -208
  91. package/dist/__tests__/loop-breaker.test.js +0 -276
  92. package/dist/__tests__/loop-miner.test.js +0 -226
  93. package/dist/__tests__/mcp-config.test.js +0 -126
  94. package/dist/__tests__/mcp-content-json.test.js +0 -10
  95. package/dist/__tests__/mcp-envelope.test.js +0 -124
  96. package/dist/__tests__/metrics-store.test.js +0 -223
  97. package/dist/__tests__/native-watcher.test.js +0 -191
  98. package/dist/__tests__/navigation-hooks-agent-aware.test.js +0 -145
  99. package/dist/__tests__/negative-knowledge.test.js +0 -116
  100. package/dist/__tests__/network-boundary.test.js +0 -190
  101. package/dist/__tests__/network-firewall.test.js +0 -112
  102. package/dist/__tests__/nudge-invariants.test.js +0 -160
  103. package/dist/__tests__/nudge-v2.test.js +0 -225
  104. package/dist/__tests__/offline-rewind.test.js +0 -251
  105. package/dist/__tests__/open-threads.test.js +0 -89
  106. package/dist/__tests__/output-compressor.test.js +0 -93
  107. package/dist/__tests__/pending-violations.test.js +0 -112
  108. package/dist/__tests__/persistence-effectiveness.test.js +0 -143
  109. package/dist/__tests__/provider-factory.test.js +0 -42
  110. package/dist/__tests__/providers.test.js +0 -24
  111. package/dist/__tests__/proxy.test.js +0 -314
  112. package/dist/__tests__/query-router.test.js +0 -1018
  113. package/dist/__tests__/reasoning-quality-route.test.js +0 -138
  114. package/dist/__tests__/redactor.test.js +0 -120
  115. package/dist/__tests__/resource-monitor.test.js +0 -57
  116. package/dist/__tests__/response-envelope.test.js +0 -100
  117. package/dist/__tests__/risk-classifier.test.js +0 -101
  118. package/dist/__tests__/risk-signal-scope.test.js +0 -75
  119. package/dist/__tests__/rule-evaluator.test.js +0 -280
  120. package/dist/__tests__/scip-decoder.test.js +0 -49
  121. package/dist/__tests__/scip-downloader.test.js +0 -201
  122. package/dist/__tests__/scip-merger.test.js +0 -103
  123. package/dist/__tests__/search-index.test.js +0 -422
  124. package/dist/__tests__/semantic-enrichment.test.js +0 -360
  125. package/dist/__tests__/session-brief-builder.test.js +0 -187
  126. package/dist/__tests__/session-context.test.js +0 -221
  127. package/dist/__tests__/session-continuity.test.js +0 -144
  128. package/dist/__tests__/session-dedup.test.js +0 -74
  129. package/dist/__tests__/session-event-wiring.test.js +0 -206
  130. package/dist/__tests__/session-events.test.js +0 -149
  131. package/dist/__tests__/session-legend.test.js +0 -20
  132. package/dist/__tests__/session-persistence.test.js +0 -131
  133. package/dist/__tests__/session-resume-block.test.js +0 -107
  134. package/dist/__tests__/session-resume.test.js +0 -97
  135. package/dist/__tests__/session-summary-writer.test.js +0 -134
  136. package/dist/__tests__/shadow-ledger.test.js +0 -203
  137. package/dist/__tests__/shell-classifier.test.js +0 -151
  138. package/dist/__tests__/shell-compression-floor.test.js +0 -189
  139. package/dist/__tests__/shell-compression-v2.test.js +0 -339
  140. package/dist/__tests__/shell-compressor.test.js +0 -35
  141. package/dist/__tests__/shell-hooks.test.js +0 -128
  142. package/dist/__tests__/shell-strategies.test.js +0 -644
  143. package/dist/__tests__/shell-tee.test.js +0 -133
  144. package/dist/__tests__/signal-dedup.test.js +0 -158
  145. package/dist/__tests__/signal-reinforcer.test.js +0 -77
  146. package/dist/__tests__/signal-scorer.test.js +0 -251
  147. package/dist/__tests__/signal-show-store.test.js +0 -108
  148. package/dist/__tests__/smart-truncate.test.js +0 -215
  149. package/dist/__tests__/snapshot-v2.test.js +0 -113
  150. package/dist/__tests__/sprint-l1-local-mode.test.js +0 -130
  151. package/dist/__tests__/sprint-l10-boot.test.js +0 -220
  152. package/dist/__tests__/sprint-l9-offline-commands.test.js +0 -189
  153. package/dist/__tests__/sprint-q-persistent-context.test.js +0 -198
  154. package/dist/__tests__/sprint-s1-wiring.test.js +0 -215
  155. package/dist/__tests__/sprint-s2-wiring.test.js +0 -256
  156. package/dist/__tests__/sprint-s3-wiring.test.js +0 -195
  157. package/dist/__tests__/sprint-s4-wiring.test.js +0 -213
  158. package/dist/__tests__/sprint-s6-hooks.test.js +0 -222
  159. package/dist/__tests__/sprint-s7-persistent.test.js +0 -263
  160. package/dist/__tests__/sprint-s8-value.test.js +0 -167
  161. package/dist/__tests__/sprint-s9-behavioral.test.js +0 -179
  162. package/dist/__tests__/sprint3-intelligence.test.js +0 -297
  163. package/dist/__tests__/sprint5-mcp-server.test.js +0 -136
  164. package/dist/__tests__/startup-display.test.js +0 -302
  165. package/dist/__tests__/startup-log-file.test.js +0 -97
  166. package/dist/__tests__/stash-manager.test.js +0 -229
  167. package/dist/__tests__/state-detector.test.js +0 -92
  168. package/dist/__tests__/status-dashboard.test.js +0 -142
  169. package/dist/__tests__/temporal-facts.test.js +0 -292
  170. package/dist/__tests__/temporal-routes.test.js +0 -142
  171. package/dist/__tests__/test-detector.test.js +0 -174
  172. package/dist/__tests__/theme.test.js +0 -72
  173. package/dist/__tests__/timeline-agents.test.js +0 -122
  174. package/dist/__tests__/timeline-bootstrap.test.js +0 -176
  175. package/dist/__tests__/timeline-filters.test.js +0 -193
  176. package/dist/__tests__/timeline-markers.test.js +0 -151
  177. package/dist/__tests__/timeline-routes.test.js +0 -156
  178. package/dist/__tests__/timeline-store.test.js +0 -171
  179. package/dist/__tests__/token-counter.test.js +0 -86
  180. package/dist/__tests__/token-estimator.test.js +0 -96
  181. package/dist/__tests__/token-flow-api.test.js +0 -239
  182. package/dist/__tests__/token-flow-instrumentation.test.js +0 -437
  183. package/dist/__tests__/token-flow-persistence.test.js +0 -356
  184. package/dist/__tests__/token-flow-routes.test.js +0 -199
  185. package/dist/__tests__/token-flow.test.js +0 -695
  186. package/dist/__tests__/tool-clusters.test.js +0 -177
  187. package/dist/__tests__/transport-mux.test.js +0 -283
  188. package/dist/__tests__/turn-segmenter.test.js +0 -166
  189. package/dist/__tests__/uninstall.test.js +0 -141
  190. package/dist/__tests__/warm-start-policy.test.js +0 -271
  191. package/dist/__tests__/wire-cap-nudge.test.js +0 -77
  192. package/dist/__tests__/worker-pool.test.js +0 -101
  193. package/dist/ui/assets/index-7gl3mIuY.css +0 -1
  194. package/dist/ui/assets/index-CX4FCWGT.js +0 -10
  195. package/dist/ui/assets/rolldown-runtime-S-ySWqyJ.js +0 -1
  196. package/dist/ui/assets/vis-network-NIJHUFI3.js +0 -908
  197. package/dist/ui/fonts/jetbrains-mono-latin-400-normal.woff +0 -0
  198. package/dist/ui/icon-wordmark.png +0 -0
  199. package/dist/ui/icon-wordmark.svg +0 -30
  200. package/dist/ui/icon.png +0 -0
  201. package/dist/ui/icon.svg +0 -25
  202. package/dist/ui/index.html +0 -15
  203. package/dist/ui/prototype-sandbox/index.html +0 -257
  204. package/dist/ui/screenshots/activity.png +0 -0
  205. package/dist/ui/screenshots/code-base-intelligence.png +0 -0
  206. package/dist/ui/screenshots/dashboard.png +0 -0
  207. package/dist/ui/screenshots/project-memory.png +0 -0
  208. package/dist/ui/screenshots/reasoning-quality.png +0 -0
  209. package/dist/ui/screenshots/reasoning-session.png +0 -0
  210. package/dist/ui/screenshots/token-session.png +0 -0
  211. package/dist/ui/screenshots/token-trace-main.png +0 -0
  212. package/dist/ui/screenshots/token-turn.png +0 -0
  213. package/dist/ui/unerr-wordmark.png +0 -0
  214. package/dist/ui/unerr-wordmark.svg +0 -9
  215. package/dist/ui/unerr.png +0 -0
  216. package/dist/ui/unerr.svg +0 -25
  217. package/dist/ui/web-app-manifest-192x192.png +0 -0
  218. package/dist/ui/web-app-manifest-512x512.png +0 -0
@@ -1,92 +0,0 @@
1
- /**
2
- * P10-TEST-02: State Machine Tests — unit tests for smart default state detector.
3
- */
4
- import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
5
- import { tmpdir } from "node:os";
6
- import { join } from "node:path";
7
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
8
- import { detectState } from "../state-detector.js";
9
- let tempDir;
10
- let unerrDir;
11
- beforeEach(() => {
12
- tempDir = join(tmpdir(), `unerr-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
13
- unerrDir = join(tempDir, ".unerr");
14
- mkdirSync(join(unerrDir, "state"), { recursive: true });
15
- });
16
- afterEach(() => {
17
- try {
18
- rmSync(tempDir, { recursive: true, force: true });
19
- }
20
- catch {
21
- /* ignore */
22
- }
23
- });
24
- function makeConfig(repoId = "repo-123") {
25
- writeFileSync(join(unerrDir, "config.json"), JSON.stringify({ repoId }), "utf-8");
26
- }
27
- function makePid(pid) {
28
- mkdirSync(join(unerrDir, "state"), { recursive: true });
29
- writeFileSync(join(unerrDir, "state", "proxy.pid"), String(pid), "utf-8");
30
- }
31
- function baseDeps(overrides) {
32
- return {
33
- cwd: tempDir,
34
- isGitRepo: () => true,
35
- ...overrides,
36
- };
37
- }
38
- describe("detectState", () => {
39
- it("returns not_git_repo when not inside a git repo", async () => {
40
- const result = await detectState(baseDeps({ isGitRepo: () => false }));
41
- expect(result.state).toBe("not_git_repo");
42
- });
43
- it("returns needs_setup when no .unerr/config.json", async () => {
44
- const result = await detectState(baseDeps());
45
- expect(result.state).toBe("needs_setup");
46
- });
47
- it("returns needs_setup when config.json is invalid JSON", async () => {
48
- writeFileSync(join(unerrDir, "config.json"), "not json", "utf-8");
49
- const result = await detectState(baseDeps());
50
- expect(result.state).toBe("needs_setup");
51
- });
52
- it("returns already_running when PID is alive", async () => {
53
- makeConfig();
54
- makePid(process.pid);
55
- const result = await detectState(baseDeps());
56
- expect(result.state).toBe("already_running");
57
- expect(result.pid).toBe(process.pid);
58
- expect(result.repoId).toBe("repo-123");
59
- });
60
- it("returns stale_pid when PID is dead and cleans up", async () => {
61
- makeConfig();
62
- const deadPid = 9999999;
63
- makePid(deadPid);
64
- const result = await detectState(baseDeps());
65
- expect(result.state).toBe("stale_pid");
66
- expect(result.pid).toBe(deadPid);
67
- expect(existsSync(join(unerrDir, "state", "proxy.pid"))).toBe(false);
68
- });
69
- it("returns needs_pull when no snapshot exists", async () => {
70
- makeConfig();
71
- const result = await detectState(baseDeps());
72
- expect(["needs_pull", "stale_graph", "ready"]).toContain(result.state);
73
- expect(result.repoId).toBe("repo-123");
74
- });
75
- it("returns ready when everything is configured", async () => {
76
- makeConfig();
77
- const result = await detectState(baseDeps());
78
- expect(result.repoId).toBe("repo-123");
79
- expect(["needs_pull", "stale_graph", "ready"]).toContain(result.state);
80
- });
81
- it("preserves repoId from config", async () => {
82
- makeConfig("my-special-repo");
83
- const result = await detectState(baseDeps());
84
- expect(result.repoId).toBe("my-special-repo");
85
- });
86
- it("state transitions follow priority order", async () => {
87
- const result = await detectState(baseDeps({
88
- isGitRepo: () => false,
89
- }));
90
- expect(result.state).toBe("not_git_repo");
91
- });
92
- });
@@ -1,142 +0,0 @@
1
- /**
2
- * Tests for StatusDashboard Ink component (Task 1.5).
3
- *
4
- * Tests validate:
5
- * - Repo name, branch, commits ahead/behind display
6
- * - Proxy state (running/stopped) with color
7
- * - Graph stats (entity count, edge count, age)
8
- * - Drift summary
9
- * - Health grade with score
10
- * - Live session stats with local rate bar
11
- * - Latency display with budget warning
12
- * - Deep link display
13
- */
14
- import { render } from "ink-testing-library";
15
- import React from "react";
16
- import { describe, expect, it } from "vitest";
17
- import { StatusDashboard, } from "../components/StatusDashboard.js";
18
- import { ThemeProvider } from "../components/Theme.js";
19
- function renderStatus(data) {
20
- return render(React.createElement(ThemeProvider, null, React.createElement(StatusDashboard, { data })));
21
- }
22
- const baseData = {
23
- repoName: "acme/monorepo",
24
- repoId: "repo_abc123",
25
- branch: "main",
26
- branchDetail: "main (3 ahead, 1 behind)",
27
- proxyStatus: "Running (PID 12345)",
28
- proxyRunning: true,
29
- graphInfo: "2,341 entities, 1,892 edges (pulled 2h ago)",
30
- };
31
- describe("StatusDashboard", () => {
32
- it("renders section header", () => {
33
- const { lastFrame } = renderStatus(baseData);
34
- expect(lastFrame()).toContain("unerr status");
35
- });
36
- it("renders repo name and ID", () => {
37
- const { lastFrame } = renderStatus(baseData);
38
- const frame = lastFrame() ?? "";
39
- expect(frame).toContain("acme/monorepo");
40
- expect(frame).toContain("repo_abc123");
41
- });
42
- it("renders branch with detail", () => {
43
- const { lastFrame } = renderStatus(baseData);
44
- expect(lastFrame()).toContain("main (3 ahead, 1 behind)");
45
- });
46
- it("renders proxy running status", () => {
47
- const { lastFrame } = renderStatus(baseData);
48
- expect(lastFrame()).toContain("Running (PID 12345)");
49
- });
50
- it("renders proxy not running", () => {
51
- const { lastFrame } = renderStatus({
52
- ...baseData,
53
- proxyStatus: "Not running",
54
- proxyRunning: false,
55
- });
56
- expect(lastFrame()).toContain("Not running");
57
- });
58
- it("renders graph info", () => {
59
- const { lastFrame } = renderStatus(baseData);
60
- expect(lastFrame()).toContain("2,341 entities");
61
- expect(lastFrame()).toContain("1,892 edges");
62
- });
63
- it("renders health grade", () => {
64
- const { lastFrame } = renderStatus({
65
- ...baseData,
66
- healthGrade: "B+",
67
- healthScore: 78,
68
- });
69
- const frame = lastFrame() ?? "";
70
- expect(frame).toContain("B+");
71
- expect(frame).toContain("78/100");
72
- });
73
- it("renders drift summary", () => {
74
- const { lastFrame } = renderStatus({
75
- ...baseData,
76
- drift: { modified: 5, added: 2, deleted: 1 },
77
- });
78
- const frame = lastFrame() ?? "";
79
- expect(frame).toContain("5 modified");
80
- expect(frame).toContain("2 added");
81
- expect(frame).toContain("1 deleted");
82
- });
83
- it("renders live session tool calls", () => {
84
- const { lastFrame } = renderStatus({
85
- ...baseData,
86
- liveToolCalls: { local: 80 },
87
- });
88
- const frame = lastFrame() ?? "";
89
- expect(frame).toContain("80 calls");
90
- expect(frame).toContain("all local");
91
- expect(frame).toContain("Local rate");
92
- });
93
- it("renders latency stats", () => {
94
- const { lastFrame } = renderStatus({
95
- ...baseData,
96
- latency: {
97
- localP50: 0.8,
98
- localP99: 3.2,
99
- },
100
- });
101
- const frame = lastFrame() ?? "";
102
- expect(frame).toContain("0.8ms");
103
- });
104
- it("shows budget warning when local p99 exceeds 5ms", () => {
105
- const { lastFrame } = renderStatus({
106
- ...baseData,
107
- latency: {
108
- localP50: 2.0,
109
- localP99: 7.5,
110
- localBudgetExceeded: true,
111
- },
112
- });
113
- expect(lastFrame()).toContain("⚠ >5ms");
114
- });
115
- it("renders deep link", () => {
116
- const { lastFrame } = renderStatus({
117
- ...baseData,
118
- deepLink: "https://app.unerr.dev/r/repo_abc123?view=drift",
119
- });
120
- expect(lastFrame()).toContain("https://app.unerr.dev/r/repo_abc123");
121
- });
122
- it("renders indexing status", () => {
123
- const { lastFrame } = renderStatus({
124
- ...baseData,
125
- indexingStatus: "Ready",
126
- });
127
- expect(lastFrame()).toContain("Ready");
128
- });
129
- it("renders minimal data gracefully", () => {
130
- const { lastFrame } = renderStatus({
131
- repoName: "(no repo)",
132
- branch: "unknown",
133
- proxyStatus: "Not running",
134
- proxyRunning: false,
135
- graphInfo: "No local graph",
136
- });
137
- const frame = lastFrame() ?? "";
138
- expect(frame).toContain("(no repo)");
139
- expect(frame).toContain("No local graph");
140
- expect(frame).not.toContain("undefined");
141
- });
142
- });
@@ -1,292 +0,0 @@
1
- import { beforeEach, describe, expect, it } from "vitest";
2
- import { initFactsSchema } from "../intelligence/facts-schema.js";
3
- import { TemporalFactStore, } from "../intelligence/temporal-facts.js";
4
- async function createTestDb() {
5
- const cozoModule = await import("cozo-node");
6
- const CozoDbConstructor = cozoModule.default
7
- ? cozoModule.default.CozoDb
8
- : cozoModule.CozoDb;
9
- return new CozoDbConstructor("mem", "");
10
- }
11
- describe("TemporalFactStore", () => {
12
- let db;
13
- let store;
14
- beforeEach(async () => {
15
- db = await createTestDb();
16
- await initFactsSchema(db);
17
- store = TemporalFactStore.fromDb(db);
18
- });
19
- describe("createFact", () => {
20
- it("creates a new fact and returns fact_id", async () => {
21
- const input = {
22
- fact_type: "semantic",
23
- scope: "project",
24
- subject: "auth-module",
25
- content: "The auth module uses JWT tokens for session management",
26
- source: "agent_explicit",
27
- };
28
- const factId = await store.createFact(input);
29
- expect(factId).toBeDefined();
30
- expect(factId.length).toBe(36);
31
- });
32
- it("deduplicates on (fact_type, scope, subject) match", async () => {
33
- const input = {
34
- fact_type: "semantic",
35
- scope: "project",
36
- subject: "database",
37
- content: "We use PostgreSQL",
38
- source: "agent_explicit",
39
- };
40
- const id1 = await store.createFact(input);
41
- const id2 = await store.createFact({
42
- ...input,
43
- content: "We use PostgreSQL for all data",
44
- });
45
- expect(id2).toBe(id1);
46
- });
47
- it("truncates content at 280 characters", async () => {
48
- const longContent = "x".repeat(500);
49
- const factId = await store.createFact({
50
- fact_type: "procedural",
51
- scope: "project",
52
- subject: "testing",
53
- content: longContent,
54
- source: "agent_explicit",
55
- });
56
- expect(factId).toBeDefined();
57
- });
58
- });
59
- describe("convention fact type", () => {
60
- it("creates and recalls a convention fact with slow decay", async () => {
61
- const factId = await store.createFact({
62
- fact_type: "convention",
63
- scope: "project",
64
- subject: "api-standards",
65
- content: "All API handlers must return structured JSON responses",
66
- source: "agent_explicit",
67
- });
68
- expect(factId).toBeDefined();
69
- const facts = await store.recallByScope("project");
70
- const convention = facts.find((f) => f.fact_type === "convention");
71
- expect(convention).toBeDefined();
72
- expect(convention.content).toBe("All API handlers must return structured JSON responses");
73
- expect(convention.effective_confidence).toBeGreaterThan(0.9);
74
- });
75
- it("includes convention in getFactHealth by_type", async () => {
76
- await store.createFact({
77
- fact_type: "convention",
78
- scope: "project",
79
- subject: "naming",
80
- content: "Use camelCase for all function names",
81
- source: "agent_explicit",
82
- });
83
- const health = await store.getFactHealth();
84
- expect(health.by_type.convention).toBe(1);
85
- });
86
- });
87
- describe("recallByScope", () => {
88
- it("recalls facts matching scope with decay applied", async () => {
89
- await store.createFact({
90
- fact_type: "semantic",
91
- scope: "src/proxy/proxy.ts",
92
- subject: "proxy",
93
- content: "The proxy module owns all MCP communication",
94
- source: "agent_explicit",
95
- });
96
- const facts = await store.recallByScope("src/proxy/proxy.ts");
97
- expect(facts.length).toBe(1);
98
- expect(facts[0].content).toBe("The proxy module owns all MCP communication");
99
- expect(facts[0].effective_confidence).toBeGreaterThan(0);
100
- });
101
- it("returns empty array for unknown scope", async () => {
102
- const facts = await store.recallByScope("nonexistent/file.ts");
103
- expect(facts).toEqual([]);
104
- });
105
- });
106
- describe("recallBySubject", () => {
107
- it("recalls facts matching subject", async () => {
108
- await store.createFact({
109
- fact_type: "semantic",
110
- scope: "project",
111
- subject: "src/auth.ts::login",
112
- content: "Login function requires rate limiting",
113
- source: "agent_explicit",
114
- });
115
- const facts = await store.recallBySubject("src/auth.ts::login");
116
- expect(facts.length).toBe(1);
117
- expect(facts[0].subject).toBe("src/auth.ts::login");
118
- });
119
- });
120
- describe("recallNegative", () => {
121
- it("recalls all negative facts regardless of scope", async () => {
122
- await store.createFact({
123
- fact_type: "negative",
124
- scope: "project",
125
- subject: "testing",
126
- content: "Don't mock CozoDB in integration tests",
127
- source: "negative_knowledge",
128
- });
129
- await store.createFact({
130
- fact_type: "semantic",
131
- scope: "project",
132
- subject: "architecture",
133
- content: "Semantic fact that should not appear",
134
- source: "agent_explicit",
135
- });
136
- const facts = await store.recallNegative();
137
- expect(facts.length).toBe(1);
138
- expect(facts[0].fact_type).toBe("negative");
139
- });
140
- });
141
- describe("reinforceFact", () => {
142
- it("increases reinforcement_count", async () => {
143
- const factId = await store.createFact({
144
- fact_type: "semantic",
145
- scope: "src/index.ts",
146
- subject: "arch",
147
- content: "Microservices architecture",
148
- source: "agent_explicit",
149
- });
150
- await store.reinforceFact(factId, {
151
- session_id: "sess-1",
152
- action: "reinforced",
153
- timestamp: Date.now(),
154
- });
155
- const facts = await store.recallByScope("src/index.ts", 0);
156
- const fact = facts.find((f) => f.fact_id === factId);
157
- expect(fact).toBeDefined();
158
- expect(fact.reinforcement_count).toBe(2);
159
- });
160
- it("increases effective confidence via evidence factor", async () => {
161
- const factId = await store.createFact({
162
- fact_type: "semantic",
163
- scope: "src/util.ts",
164
- subject: "utils",
165
- content: "Utility functions are pure",
166
- source: "convention_detector",
167
- });
168
- const before = await store.recallByScope("src/util.ts", 0);
169
- const confBefore = before[0].effective_confidence;
170
- await store.reinforceFact(factId, {
171
- session_id: "s2",
172
- action: "reinforced",
173
- timestamp: Date.now(),
174
- });
175
- await store.reinforceFact(factId, {
176
- session_id: "s3",
177
- action: "reinforced",
178
- timestamp: Date.now(),
179
- });
180
- const after = await store.recallByScope("src/util.ts", 0);
181
- const confAfter = after[0].effective_confidence;
182
- expect(confAfter).toBeGreaterThan(confBefore);
183
- });
184
- });
185
- describe("contradictFact", () => {
186
- it("halves base_confidence", async () => {
187
- const factId = await store.createFact({
188
- fact_type: "semantic",
189
- scope: "src/db.ts",
190
- subject: "db",
191
- content: "We use MySQL",
192
- source: "agent_explicit",
193
- base_confidence: 0.9,
194
- });
195
- await store.contradictFact(factId, "Actually using PostgreSQL");
196
- const facts = await store.recallByScope("src/db.ts", 0);
197
- const fact = facts.find((f) => f.fact_id === factId);
198
- expect(fact).toBeDefined();
199
- expect(fact.base_confidence).toBeCloseTo(0.45, 1);
200
- });
201
- });
202
- describe("getFactHealth", () => {
203
- it("returns health summary with correct counts", async () => {
204
- await store.createFact({
205
- fact_type: "semantic",
206
- scope: "project",
207
- subject: "a",
208
- content: "Fact A",
209
- source: "agent_explicit",
210
- });
211
- await store.createFact({
212
- fact_type: "negative",
213
- scope: "project",
214
- subject: "b",
215
- content: "Fact B",
216
- source: "negative_knowledge",
217
- });
218
- const health = await store.getFactHealth();
219
- expect(health.total).toBe(2);
220
- expect(health.by_type.semantic).toBe(1);
221
- expect(health.by_type.negative).toBe(1);
222
- expect(health.avg_confidence).toBeGreaterThan(0);
223
- });
224
- });
225
- describe("pruneDecayed", () => {
226
- it("removes facts below prune threshold", async () => {
227
- const factId = await store.createFact({
228
- fact_type: "semantic",
229
- scope: "src/old.ts",
230
- subject: "prune-target",
231
- content: "Will be contradicted into oblivion",
232
- source: "agent_explicit",
233
- base_confidence: 0.1,
234
- });
235
- // Contradict multiple times to drive confidence below threshold
236
- await store.contradictFact(factId, "wrong");
237
- await store.contradictFact(factId, "still wrong");
238
- await store.contradictFact(factId, "really wrong");
239
- // 0.1 * 0.5^3 = 0.0125, evidence_factor = 1/3 = 0.33
240
- // effective = 0.0125 * 1.0 * 0.33 = 0.004 — well below 0.05
241
- const pruned = await store.pruneDecayed(0.05);
242
- expect(pruned).toBe(1);
243
- });
244
- });
245
- describe("recallForFile", () => {
246
- it("combines scope + subject + negative facts", async () => {
247
- await store.createFact({
248
- fact_type: "semantic",
249
- scope: "src/auth.ts",
250
- subject: "auth",
251
- content: "Auth uses bcrypt for password hashing",
252
- source: "agent_explicit",
253
- });
254
- const negId = await store.createFact({
255
- fact_type: "negative",
256
- scope: "project",
257
- subject: "security",
258
- content: "Never store passwords in plain text",
259
- source: "negative_knowledge",
260
- base_confidence: 0.95,
261
- });
262
- // Reinforce to push evidence_factor above threshold boundary
263
- await store.reinforceFact(negId, {
264
- session_id: "s2",
265
- action: "reinforced",
266
- timestamp: Date.now(),
267
- });
268
- await store.reinforceFact(negId, {
269
- session_id: "s3",
270
- action: "reinforced",
271
- timestamp: Date.now(),
272
- });
273
- await store.createFact({
274
- fact_type: "procedural",
275
- scope: "src/unrelated.ts",
276
- subject: "unrelated",
277
- content: "This should not appear",
278
- source: "session_analysis",
279
- });
280
- const facts = await store.recallForFile("src/auth.ts", ["auth"]);
281
- expect(facts.length).toBe(2);
282
- const types = facts.map((f) => f.fact_type);
283
- expect(types).toContain("semantic");
284
- expect(types).toContain("negative");
285
- });
286
- });
287
- describe("recordInteraction", () => {
288
- it("records entity interaction without error", async () => {
289
- await expect(store.recordInteraction("src/auth.ts::login", "session-abc", "read", "get_function", "success")).resolves.not.toThrow();
290
- });
291
- });
292
- });
@@ -1,142 +0,0 @@
1
- import { Hono } from "hono";
2
- import { describe, expect, it, vi } from "vitest";
3
- import { createTemporalRoutes, } from "../server/routes/temporal.js";
4
- function makeFact(overrides = {}) {
5
- return {
6
- fact_id: "fact-001",
7
- fact_type: "semantic",
8
- scope: "project",
9
- subject: "architecture",
10
- content: "We use microservices",
11
- base_confidence: 0.9,
12
- effective_confidence: 0.85,
13
- reinforcement_count: 3,
14
- created_at: Date.now() - 86400000,
15
- last_reinforced_at: Date.now() - 3600000,
16
- last_contradicted_at: 0,
17
- source: "agent_explicit",
18
- ...overrides,
19
- };
20
- }
21
- function makeSession(overrides = {}) {
22
- return {
23
- session_id: "sess-test",
24
- written_at: new Date().toISOString(),
25
- started_at: new Date(Date.now() - 3600000).toISOString(),
26
- ended_at: new Date().toISOString(),
27
- duration_ms: 3600000,
28
- tool_calls: 15,
29
- chains: 4,
30
- files_modified: ["src/a.ts"],
31
- entities_touched: ["src/a.ts::fn"],
32
- tools_used: { get_function: 10 },
33
- feature_areas: ["src"],
34
- facts_recorded: 1,
35
- facts_surfaced: ["f1"],
36
- revert_count: 0,
37
- rot_score: 0.1,
38
- token_estimate: 8000,
39
- branch: "main",
40
- ...overrides,
41
- };
42
- }
43
- function createTestDeps(overrides = {}) {
44
- return {
45
- factStore: {
46
- recallByScope: vi.fn().mockResolvedValue([makeFact()]),
47
- getFactHealth: vi.fn().mockResolvedValue({
48
- total: 5,
49
- active: 4,
50
- decayed: 1,
51
- by_type: { semantic: 2, procedural: 1, negative: 1, episodic: 1 },
52
- avg_confidence: 0.72,
53
- }),
54
- reinforceFact: vi.fn().mockResolvedValue(undefined),
55
- contradictFact: vi.fn().mockResolvedValue(undefined),
56
- },
57
- loadRecentSessions: vi.fn().mockReturnValue([makeSession()]),
58
- emitEvent: vi.fn(),
59
- ...overrides,
60
- };
61
- }
62
- describe("temporal routes", () => {
63
- describe("GET /api/facts", () => {
64
- it("returns facts with default filters", async () => {
65
- const deps = createTestDeps();
66
- const app = new Hono();
67
- app.route("", createTemporalRoutes(deps));
68
- const res = await app.request("/api/facts");
69
- expect(res.status).toBe(200);
70
- const body = await res.json();
71
- expect(body.facts.length).toBe(1);
72
- expect(body.facts[0].fact_id).toBe("fact-001");
73
- expect(body.total).toBe(1);
74
- });
75
- it("returns empty when factStore is null", async () => {
76
- const deps = createTestDeps({ factStore: null });
77
- const app = new Hono();
78
- app.route("", createTemporalRoutes(deps));
79
- const res = await app.request("/api/facts");
80
- expect(res.status).toBe(200);
81
- const body = await res.json();
82
- expect(body.facts).toEqual([]);
83
- });
84
- });
85
- describe("GET /api/facts/health", () => {
86
- it("returns health summary", async () => {
87
- const deps = createTestDeps();
88
- const app = new Hono();
89
- app.route("", createTemporalRoutes(deps));
90
- const res = await app.request("/api/facts/health");
91
- expect(res.status).toBe(200);
92
- const body = await res.json();
93
- expect(body.total).toBe(5);
94
- expect(body.active).toBe(4);
95
- expect(body.avg_confidence).toBe(0.72);
96
- });
97
- });
98
- describe("GET /api/sessions", () => {
99
- it("returns recent sessions", async () => {
100
- const deps = createTestDeps();
101
- const app = new Hono();
102
- app.route("", createTemporalRoutes(deps));
103
- const res = await app.request("/api/sessions");
104
- expect(res.status).toBe(200);
105
- const body = await res.json();
106
- expect(body.sessions.length).toBe(1);
107
- expect(body.sessions[0].session_id).toBe("sess-test");
108
- });
109
- });
110
- describe("POST /api/facts/:id/reinforce", () => {
111
- it("reinforces a fact and emits event", async () => {
112
- const deps = createTestDeps();
113
- const app = new Hono();
114
- app.route("", createTemporalRoutes(deps));
115
- const res = await app.request("/api/facts/fact-001/reinforce", {
116
- method: "POST",
117
- });
118
- expect(res.status).toBe(200);
119
- const body = await res.json();
120
- expect(body.success).toBe(true);
121
- expect(body.action).toBe("reinforced");
122
- expect(deps.factStore.reinforceFact).toHaveBeenCalledWith("fact-001", expect.objectContaining({ action: "reinforced" }));
123
- expect(deps.emitEvent).toHaveBeenCalledWith("fact:reinforced", expect.objectContaining({ fact_id: "fact-001" }));
124
- });
125
- });
126
- describe("DELETE /api/facts/:id", () => {
127
- it("dismisses a fact and emits event", async () => {
128
- const deps = createTestDeps();
129
- const app = new Hono();
130
- app.route("", createTemporalRoutes(deps));
131
- const res = await app.request("/api/facts/fact-001", {
132
- method: "DELETE",
133
- });
134
- expect(res.status).toBe(200);
135
- const body = await res.json();
136
- expect(body.success).toBe(true);
137
- expect(body.action).toBe("dismissed");
138
- expect(deps.factStore.contradictFact).toHaveBeenCalledWith("fact-001", "Manually dismissed from dashboard");
139
- expect(deps.emitEvent).toHaveBeenCalledWith("fact:expired", expect.objectContaining({ fact_id: "fact-001" }));
140
- });
141
- });
142
- });