toolnet-memory 0.2.0

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 (329) hide show
  1. package/.env.example +28 -0
  2. package/README.md +147 -0
  3. package/bin/toolnet-memory +415 -0
  4. package/dist/packages/cli/commands.js +1 -0
  5. package/dist/packages/cli/index.js +1 -0
  6. package/dist/packages/sdk/client.js +78 -0
  7. package/dist/packages/sdk/index.js +1 -0
  8. package/dist/packages/sdk/types.js +1 -0
  9. package/dist/src/api/index.js +1 -0
  10. package/dist/src/api/routes/health.js +1 -0
  11. package/dist/src/api/routes/index.js +1 -0
  12. package/dist/src/api/routes/memory.js +1 -0
  13. package/dist/src/api/routes/projects.js +1 -0
  14. package/dist/src/api/routes/search.js +1 -0
  15. package/dist/src/api/server.js +1 -0
  16. package/dist/src/capture/activity-capture.js +21 -0
  17. package/dist/src/capture/command-capture.js +12 -0
  18. package/dist/src/capture/error-capture.js +14 -0
  19. package/dist/src/capture/event-deduplicator.js +28 -0
  20. package/dist/src/capture/event-queue.js +20 -0
  21. package/dist/src/capture/file-capture.js +23 -0
  22. package/dist/src/capture/index.js +8 -0
  23. package/dist/src/capture/session-capture.js +18 -0
  24. package/dist/src/capture/tool-capture.js +13 -0
  25. package/dist/src/code-intelligence/analysis/analysis-engine.js +26 -0
  26. package/dist/src/code-intelligence/analysis/dead-code-analyzer.js +127 -0
  27. package/dist/src/code-intelligence/analysis/dependency-analyzer.js +66 -0
  28. package/dist/src/code-intelligence/analysis/index.js +4 -0
  29. package/dist/src/code-intelligence/analysis/types.js +1 -0
  30. package/dist/src/code-intelligence/architecture/architecture-engine.js +48 -0
  31. package/dist/src/code-intelligence/architecture/clusters.js +322 -0
  32. package/dist/src/code-intelligence/architecture/entry-points.js +150 -0
  33. package/dist/src/code-intelligence/architecture/hotspots.js +136 -0
  34. package/dist/src/code-intelligence/architecture/index.js +6 -0
  35. package/dist/src/code-intelligence/architecture/layers.js +197 -0
  36. package/dist/src/code-intelligence/architecture/types.js +1 -0
  37. package/dist/src/code-intelligence/chunks/index.js +2 -0
  38. package/dist/src/code-intelligence/chunks/smart-chunker.js +125 -0
  39. package/dist/src/code-intelligence/chunks/types.js +1 -0
  40. package/dist/src/code-intelligence/git/git-diff.js +109 -0
  41. package/dist/src/code-intelligence/git/index.js +1 -0
  42. package/dist/src/code-intelligence/graph/architecture.js +26 -0
  43. package/dist/src/code-intelligence/graph/graph-builder.js +213 -0
  44. package/dist/src/code-intelligence/graph/graph-store.js +57 -0
  45. package/dist/src/code-intelligence/graph/index.js +1 -0
  46. package/dist/src/code-intelligence/graph/relationships.js +1 -0
  47. package/dist/src/code-intelligence/graph/trace.js +65 -0
  48. package/dist/src/code-intelligence/impact/blast-radius.js +160 -0
  49. package/dist/src/code-intelligence/impact/change-mapper.js +69 -0
  50. package/dist/src/code-intelligence/impact/edit-tool-detector.js +92 -0
  51. package/dist/src/code-intelligence/impact/impact-analyzer.js +60 -0
  52. package/dist/src/code-intelligence/impact/impact-guard.js +33 -0
  53. package/dist/src/code-intelligence/impact/index.js +5 -0
  54. package/dist/src/code-intelligence/incremental/file-hash.js +8 -0
  55. package/dist/src/code-intelligence/incremental/incremental-indexer.js +106 -0
  56. package/dist/src/code-intelligence/incremental/manifest-builder.js +19 -0
  57. package/dist/src/code-intelligence/incremental/manifest.js +31 -0
  58. package/dist/src/code-intelligence/index.js +23 -0
  59. package/dist/src/code-intelligence/indexer/file-watcher.js +1 -0
  60. package/dist/src/code-intelligence/indexer/incremental-indexer.js +1 -0
  61. package/dist/src/code-intelligence/indexer/index.js +1 -0
  62. package/dist/src/code-intelligence/indexer/repository-indexer.js +31 -0
  63. package/dist/src/code-intelligence/indexer/repository-scanner.js +50 -0
  64. package/dist/src/code-intelligence/parsers/index.js +1 -0
  65. package/dist/src/code-intelligence/parsers/tree-sitter/index.js +1 -0
  66. package/dist/src/code-intelligence/parsers/tree-sitter/languages.js +1 -0
  67. package/dist/src/code-intelligence/parsers/tree-sitter/parser.js +1 -0
  68. package/dist/src/code-intelligence/parsers/typescript-parser.js +190 -0
  69. package/dist/src/code-intelligence/query/graph-query-engine.js +275 -0
  70. package/dist/src/code-intelligence/query/index.js +2 -0
  71. package/dist/src/code-intelligence/query/types.js +1 -0
  72. package/dist/src/code-intelligence/resolution/index.js +2 -0
  73. package/dist/src/code-intelligence/resolution/types.js +1 -0
  74. package/dist/src/code-intelligence/resolution/typescript-resolver.js +316 -0
  75. package/dist/src/code-intelligence/rich/index.js +1 -0
  76. package/dist/src/code-intelligence/rich/rich-graph-enricher.js +463 -0
  77. package/dist/src/code-intelligence/semantic/index.js +2 -0
  78. package/dist/src/code-intelligence/semantic/semantic-code-engine.js +213 -0
  79. package/dist/src/code-intelligence/semantic/source-priority.js +22 -0
  80. package/dist/src/code-intelligence/symbols/index.js +1 -0
  81. package/dist/src/code-intelligence/symbols/reference-resolver.js +21 -0
  82. package/dist/src/code-intelligence/symbols/symbol-extractor.js +1 -0
  83. package/dist/src/code-intelligence/test-analysis.js +72 -0
  84. package/dist/src/code-intelligence/test-architecture.js +67 -0
  85. package/dist/src/code-intelligence/test-impact.js +47 -0
  86. package/dist/src/code-intelligence/test-incremental.js +24 -0
  87. package/dist/src/code-intelligence/test-index.js +39 -0
  88. package/dist/src/code-intelligence/test-resolution.js +54 -0
  89. package/dist/src/code-intelligence/test-rich-graph.js +48 -0
  90. package/dist/src/code-intelligence/test-semantic.js +57 -0
  91. package/dist/src/code-intelligence/test-visualization.js +36 -0
  92. package/dist/src/code-intelligence/types.js +1 -0
  93. package/dist/src/code-intelligence/visualization/index.js +2 -0
  94. package/dist/src/code-intelligence/visualization/types.js +1 -0
  95. package/dist/src/code-intelligence/visualization/visualization-builder.js +104 -0
  96. package/dist/src/core/config.js +66 -0
  97. package/dist/src/core/global-env.js +70 -0
  98. package/dist/src/core/importance.js +30 -0
  99. package/dist/src/core/index.js +5 -0
  100. package/dist/src/core/memory-engine.js +183 -0
  101. package/dist/src/core/project-manager.js +243 -0
  102. package/dist/src/core/types.js +1 -0
  103. package/dist/src/embeddings/batch-embedder.js +1 -0
  104. package/dist/src/embeddings/cache.js +1 -0
  105. package/dist/src/embeddings/embedder.js +51 -0
  106. package/dist/src/embeddings/index.js +4 -0
  107. package/dist/src/embeddings/local.js +37 -0
  108. package/dist/src/embeddings/provider.js +1 -0
  109. package/dist/src/embeddings/remote.js +76 -0
  110. package/dist/src/embeddings/test-connection.js +16 -0
  111. package/dist/src/hooks/after-edit.js +3 -0
  112. package/dist/src/hooks/after-tool.js +3 -0
  113. package/dist/src/hooks/auto-context.js +68 -0
  114. package/dist/src/hooks/auto-impact-guard.js +94 -0
  115. package/dist/src/hooks/auto-retrieval.js +27 -0
  116. package/dist/src/hooks/before-agent.js +18 -0
  117. package/dist/src/hooks/before-compact.js +3 -0
  118. package/dist/src/hooks/before-edit.js +20 -0
  119. package/dist/src/hooks/index.js +11 -0
  120. package/dist/src/hooks/runtime.js +110 -0
  121. package/dist/src/hooks/session-end.js +3 -0
  122. package/dist/src/hooks/session-start.js +3 -0
  123. package/dist/src/index.js +22 -0
  124. package/dist/src/mcp/bootstrap.js +68 -0
  125. package/dist/src/mcp/context.js +1 -0
  126. package/dist/src/mcp/index.js +3 -0
  127. package/dist/src/mcp/server.js +45 -0
  128. package/dist/src/mcp/tools/analyze-impact.js +41 -0
  129. package/dist/src/mcp/tools/dead-code.js +55 -0
  130. package/dist/src/mcp/tools/find-callers.js +24 -0
  131. package/dist/src/mcp/tools/find-dependencies.js +57 -0
  132. package/dist/src/mcp/tools/find-symbol.js +7 -0
  133. package/dist/src/mcp/tools/get-architecture.js +11 -0
  134. package/dist/src/mcp/tools/graph-dependents.js +24 -0
  135. package/dist/src/mcp/tools/graph-neighborhood.js +58 -0
  136. package/dist/src/mcp/tools/graph-path.js +43 -0
  137. package/dist/src/mcp/tools/graph-query-utils.js +32 -0
  138. package/dist/src/mcp/tools/impact-guard.js +34 -0
  139. package/dist/src/mcp/tools/index-repository.js +1 -0
  140. package/dist/src/mcp/tools/index.js +20 -0
  141. package/dist/src/mcp/tools/memory-decisions.js +1 -0
  142. package/dist/src/mcp/tools/memory-forget.js +14 -0
  143. package/dist/src/mcp/tools/memory-recent.js +1 -0
  144. package/dist/src/mcp/tools/memory-remember.js +33 -0
  145. package/dist/src/mcp/tools/memory-rules.js +1 -0
  146. package/dist/src/mcp/tools/memory-search.js +18 -0
  147. package/dist/src/mcp/tools/memory-todos.js +1 -0
  148. package/dist/src/mcp/tools/project-context.js +25 -0
  149. package/dist/src/mcp/tools/search-code.js +21 -0
  150. package/dist/src/mcp/tools/semantic-code-search.js +33 -0
  151. package/dist/src/mcp/tools/snapshot-create.js +13 -0
  152. package/dist/src/mcp/tools/snapshot-list.js +7 -0
  153. package/dist/src/mcp/tools/snapshot-restore.js +13 -0
  154. package/dist/src/mcp/tools/trace-calls.js +37 -0
  155. package/dist/src/memory/activity.js +5 -0
  156. package/dist/src/memory/conflict-detector.js +105 -0
  157. package/dist/src/memory/consolidator.js +104 -0
  158. package/dist/src/memory/decay.js +59 -0
  159. package/dist/src/memory/decisions.js +3 -0
  160. package/dist/src/memory/deduplicate.js +25 -0
  161. package/dist/src/memory/index.js +11 -0
  162. package/dist/src/memory/maintenance.js +16 -0
  163. package/dist/src/memory/remember.js +3 -0
  164. package/dist/src/memory/rules.js +3 -0
  165. package/dist/src/memory/search.js +3 -0
  166. package/dist/src/memory/summaries.js +1 -0
  167. package/dist/src/memory/todos.js +3 -0
  168. package/dist/src/processor/decision-extractor.js +13 -0
  169. package/dist/src/processor/importance-scorer.js +22 -0
  170. package/dist/src/processor/index.js +7 -0
  171. package/dist/src/processor/memory-extractor.js +59 -0
  172. package/dist/src/processor/memory-processor.js +105 -0
  173. package/dist/src/processor/rule-extractor.js +13 -0
  174. package/dist/src/processor/summarizer.js +71 -0
  175. package/dist/src/processor/todo-extractor.js +12 -0
  176. package/dist/src/production/config-check.js +34 -0
  177. package/dist/src/production/doctor.js +58 -0
  178. package/dist/src/production/full-index.js +114 -0
  179. package/dist/src/production/health.js +58 -0
  180. package/dist/src/production/index-pipeline.js +151 -0
  181. package/dist/src/production/index.js +4 -0
  182. package/dist/src/production/project-lock.js +95 -0
  183. package/dist/src/production/project-status.js +16 -0
  184. package/dist/src/production/recovery.js +15 -0
  185. package/dist/src/production/snapshot-cli.js +58 -0
  186. package/dist/src/project-manual/cli.js +77 -0
  187. package/dist/src/project-manual/index.js +2 -0
  188. package/dist/src/project-manual/manager.js +147 -0
  189. package/dist/src/project-manual/types.js +1 -0
  190. package/dist/src/retrieval/bm25/index.js +1 -0
  191. package/dist/src/retrieval/bm25/search.js +1 -0
  192. package/dist/src/retrieval/bm25.js +30 -0
  193. package/dist/src/retrieval/context-budget.js +1 -0
  194. package/dist/src/retrieval/context-builder.js +22 -0
  195. package/dist/src/retrieval/graph/index.js +1 -0
  196. package/dist/src/retrieval/graph/search.js +1 -0
  197. package/dist/src/retrieval/hybrid/hybrid-search.js +1 -0
  198. package/dist/src/retrieval/hybrid/index.js +1 -0
  199. package/dist/src/retrieval/hybrid-search.js +68 -0
  200. package/dist/src/retrieval/importance.js +16 -0
  201. package/dist/src/retrieval/index.js +12 -0
  202. package/dist/src/retrieval/query-analyzer.js +32 -0
  203. package/dist/src/retrieval/query-classifier.js +1 -0
  204. package/dist/src/retrieval/query-expansion.js +1 -0
  205. package/dist/src/retrieval/recency.js +12 -0
  206. package/dist/src/retrieval/reranker/index.js +1 -0
  207. package/dist/src/retrieval/reranker/reranker.js +1 -0
  208. package/dist/src/retrieval/retrieval-engine.js +17 -0
  209. package/dist/src/retrieval/tokenizer.js +9 -0
  210. package/dist/src/retrieval/type-priority.js +10 -0
  211. package/dist/src/retrieval/types.js +1 -0
  212. package/dist/src/retrieval/vector/index.js +4 -0
  213. package/dist/src/retrieval/vector/metadata-filter.js +1 -0
  214. package/dist/src/retrieval/vector/persistence.js +67 -0
  215. package/dist/src/retrieval/vector/search.js +12 -0
  216. package/dist/src/retrieval/vector/test-persistence.js +36 -0
  217. package/dist/src/retrieval/vector/upsert.js +40 -0
  218. package/dist/src/retrieval/vector/vector-store.js +89 -0
  219. package/dist/src/retrieval/vector-hybrid-engine.js +91 -0
  220. package/dist/src/runtime/bootstrap.js +28 -0
  221. package/dist/src/runtime/graceful.js +17 -0
  222. package/dist/src/runtime/index.js +3 -0
  223. package/dist/src/runtime/toolnet-memory-runtime.js +156 -0
  224. package/dist/src/security/encryption.js +1 -0
  225. package/dist/src/security/file-filter.js +30 -0
  226. package/dist/src/security/index.js +3 -0
  227. package/dist/src/security/sanitizer.js +49 -0
  228. package/dist/src/security/secret-scanner.js +56 -0
  229. package/dist/src/session/agy/adapter.js +159 -0
  230. package/dist/src/session/agy/cli.js +87 -0
  231. package/dist/src/session/agy/hook-installer.js +62 -0
  232. package/dist/src/session/agy/hook.js +167 -0
  233. package/dist/src/session/agy/index.js +5 -0
  234. package/dist/src/session/agy/project-resolver.js +27 -0
  235. package/dist/src/session/agy/recovery.js +112 -0
  236. package/dist/src/session/agy/transcript.js +191 -0
  237. package/dist/src/session/codex/adapter.js +138 -0
  238. package/dist/src/session/codex/cli.js +236 -0
  239. package/dist/src/session/codex/context-hook-installer.js +70 -0
  240. package/dist/src/session/codex/context-hook.js +69 -0
  241. package/dist/src/session/codex/discovery.js +138 -0
  242. package/dist/src/session/codex/index.js +6 -0
  243. package/dist/src/session/codex/notify-installer.js +135 -0
  244. package/dist/src/session/codex/project-resolver.js +25 -0
  245. package/dist/src/session/codex/recovery.js +27 -0
  246. package/dist/src/session/codex/rollout.js +186 -0
  247. package/dist/src/session/core.js +239 -0
  248. package/dist/src/session/identity.js +57 -0
  249. package/dist/src/session/index.js +9 -0
  250. package/dist/src/session/learner/cli.js +62 -0
  251. package/dist/src/session/learner/extractor.js +461 -0
  252. package/dist/src/session/learner/index.js +4 -0
  253. package/dist/src/session/learner/journal.js +159 -0
  254. package/dist/src/session/learner/learner.js +124 -0
  255. package/dist/src/session/learner/types.js +1 -0
  256. package/dist/src/session/opencode/adapter.js +553 -0
  257. package/dist/src/session/opencode/cli.js +121 -0
  258. package/dist/src/session/opencode/index.js +2 -0
  259. package/dist/src/session/opencode/plugin-installer.js +230 -0
  260. package/dist/src/session/store.js +242 -0
  261. package/dist/src/session/types.js +1 -0
  262. package/dist/src/session/utils.js +47 -0
  263. package/dist/src/session/wal.js +303 -0
  264. package/dist/src/snapshot/index.js +2 -0
  265. package/dist/src/snapshot/manager.js +130 -0
  266. package/dist/src/snapshot/test-snapshot.js +28 -0
  267. package/dist/src/snapshot/types.js +1 -0
  268. package/dist/src/storage/architecture-store.js +37 -0
  269. package/dist/src/storage/cleanup-health.js +20 -0
  270. package/dist/src/storage/code-analysis-store.js +35 -0
  271. package/dist/src/storage/code-chunk-store.js +25 -0
  272. package/dist/src/storage/code-graph-store.js +24 -0
  273. package/dist/src/storage/code-manifest-store.js +24 -0
  274. package/dist/src/storage/code-vector-store.js +25 -0
  275. package/dist/src/storage/github/client.js +1 -0
  276. package/dist/src/storage/github/index.js +1 -0
  277. package/dist/src/storage/google-drive/client.js +1 -0
  278. package/dist/src/storage/google-drive/index.js +1 -0
  279. package/dist/src/storage/huggingface/client.js +116 -0
  280. package/dist/src/storage/huggingface/index.js +1 -0
  281. package/dist/src/storage/index.js +16 -0
  282. package/dist/src/storage/list-bucket-raw.js +14 -0
  283. package/dist/src/storage/list-bucket-tree.js +38 -0
  284. package/dist/src/storage/local/client.js +99 -0
  285. package/dist/src/storage/local/index.js +1 -0
  286. package/dist/src/storage/memory-store.js +30 -0
  287. package/dist/src/storage/project/cleanup-layout-v1.js +72 -0
  288. package/dist/src/storage/project/folder.js +15 -0
  289. package/dist/src/storage/project/index.js +3 -0
  290. package/dist/src/storage/project/layout.js +74 -0
  291. package/dist/src/storage/project/migrate-layout-v2.js +101 -0
  292. package/dist/src/storage/project/migrate.js +73 -0
  293. package/dist/src/storage/project/scoped-provider.js +165 -0
  294. package/dist/src/storage/provider.js +26 -0
  295. package/dist/src/storage/retrying-provider.js +23 -0
  296. package/dist/src/storage/test-connection.js +32 -0
  297. package/dist/src/storage/type-resolution-store.js +26 -0
  298. package/dist/src/storage/types.js +1 -0
  299. package/dist/src/storage/vector-store.js +30 -0
  300. package/dist/src/storage/visualization-store.js +25 -0
  301. package/dist/src/sync/compressor.js +1 -0
  302. package/dist/src/sync/conflict-resolver.js +1 -0
  303. package/dist/src/sync/downloader.js +1 -0
  304. package/dist/src/sync/index.js +1 -0
  305. package/dist/src/sync/sync-engine.js +1 -0
  306. package/dist/src/sync/upload-queue.js +1 -0
  307. package/dist/src/utils/retry.js +29 -0
  308. package/dist/src/visualization/server.js +270 -0
  309. package/dist/src/work-continuity/brief-cache.js +107 -0
  310. package/dist/src/work-continuity/brief.js +349 -0
  311. package/dist/src/work-continuity/cli.js +96 -0
  312. package/dist/src/work-continuity/context-cli.js +106 -0
  313. package/dist/src/work-continuity/context-runtime-cli.js +88 -0
  314. package/dist/src/work-continuity/extractor.js +339 -0
  315. package/dist/src/work-continuity/handoff.js +185 -0
  316. package/dist/src/work-continuity/index.js +14 -0
  317. package/dist/src/work-continuity/injection.js +84 -0
  318. package/dist/src/work-continuity/journal.js +49 -0
  319. package/dist/src/work-continuity/learner.js +127 -0
  320. package/dist/src/work-continuity/reducer.js +316 -0
  321. package/dist/src/work-continuity/semantic-extractor.js +360 -0
  322. package/dist/src/work-continuity/semantic-journal.js +55 -0
  323. package/dist/src/work-continuity/semantic-learner.js +125 -0
  324. package/dist/src/work-continuity/semantic-reducer.js +268 -0
  325. package/dist/src/work-continuity/semantic-types.js +1 -0
  326. package/dist/src/work-continuity/types.js +1 -0
  327. package/dist/visualization/public/index.html +2896 -0
  328. package/dist/visualization/public/vendor/3d-force-graph.min.js +5 -0
  329. package/package.json +69 -0
@@ -0,0 +1,124 @@
1
+ import { closeSync, existsSync, openSync, readSync, statSync, } from "node:fs";
2
+ import { extractLearnedMemories, } from "./extractor.js";
3
+ import { SessionMemoryJournal, } from "./journal.js";
4
+ function readEvents(filePath, offset) {
5
+ if (!existsSync(filePath)) {
6
+ return {
7
+ events: [],
8
+ nextOffset: offset,
9
+ };
10
+ }
11
+ const size = statSync(filePath).size;
12
+ let start = Number.isFinite(offset)
13
+ ? Math.max(0, offset)
14
+ : 0;
15
+ /*
16
+ * Local WAL was rebuilt.
17
+ */
18
+ if (start >
19
+ size) {
20
+ start =
21
+ 0;
22
+ }
23
+ if (start ===
24
+ size) {
25
+ return {
26
+ events: [],
27
+ nextOffset: size,
28
+ };
29
+ }
30
+ const length = size -
31
+ start;
32
+ const buffer = Buffer.alloc(length);
33
+ const fd = openSync(filePath, "r");
34
+ try {
35
+ readSync(fd, buffer, 0, length, start);
36
+ }
37
+ finally {
38
+ closeSync(fd);
39
+ }
40
+ const text = buffer.toString("utf8");
41
+ const lastNewline = text.lastIndexOf("\n");
42
+ if (lastNewline <
43
+ 0) {
44
+ return {
45
+ events: [],
46
+ nextOffset: start,
47
+ };
48
+ }
49
+ const complete = text.slice(0, lastNewline +
50
+ 1);
51
+ const events = complete
52
+ .split("\n")
53
+ .filter(Boolean)
54
+ .flatMap(line => {
55
+ try {
56
+ return [
57
+ JSON.parse(line),
58
+ ];
59
+ }
60
+ catch {
61
+ return [];
62
+ }
63
+ });
64
+ return {
65
+ events,
66
+ nextOffset: start +
67
+ Buffer.byteLength(complete, "utf8"),
68
+ };
69
+ }
70
+ export class SessionMemoryLearner {
71
+ options;
72
+ journal;
73
+ constructor(options) {
74
+ this.options = options;
75
+ this.journal =
76
+ new SessionMemoryJournal(options.storage);
77
+ }
78
+ async learnNew() {
79
+ const state = this.options
80
+ .wal
81
+ .loadState();
82
+ const rawOffset = Number(state.sourceCursors["memory.learner.offset"] ??
83
+ 0);
84
+ const offset = Number.isFinite(rawOffset)
85
+ ? rawOffset
86
+ : 0;
87
+ const read = readEvents(this.options
88
+ .wal
89
+ .eventsFile, offset);
90
+ if (read.events.length ===
91
+ 0) {
92
+ return {
93
+ scannedEvents: 0,
94
+ candidates: 0,
95
+ journalWritten: false,
96
+ nextOffset: read.nextOffset,
97
+ };
98
+ }
99
+ const candidates = extractLearnedMemories(this.options
100
+ .identity, read.events);
101
+ let journalWritten = false;
102
+ if (candidates.length >
103
+ 0) {
104
+ const key = await this.journal
105
+ .write(this.options
106
+ .identity, read.events, candidates);
107
+ journalWritten =
108
+ Boolean(key);
109
+ }
110
+ /*
111
+ * Advance only after immutable learning journal succeeds.
112
+ * If journal write throws, next flush retries same range.
113
+ */
114
+ this.options
115
+ .wal
116
+ .setSourceCursor("memory.learner.offset", read.nextOffset);
117
+ return {
118
+ scannedEvents: read.events.length,
119
+ candidates: candidates.length,
120
+ journalWritten,
121
+ nextOffset: read.nextOffset,
122
+ };
123
+ }
124
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,553 @@
1
+ import { existsSync, } from "node:fs";
2
+ import { homedir, } from "node:os";
3
+ import { isAbsolute, join, relative, resolve, } from "node:path";
4
+ import { DatabaseSync, } from "node:sqlite";
5
+ import { SessionCore, } from "../core.js";
6
+ export function defaultOpenCodeDbPath() {
7
+ return (process.env
8
+ .OPENCODE_DB_PATH ??
9
+ join(homedir(), ".local", "share", "opencode", "opencode.db"));
10
+ }
11
+ function valueString(value) {
12
+ return typeof value ===
13
+ "string"
14
+ ? value
15
+ : "";
16
+ }
17
+ function valueNumber(value) {
18
+ if (typeof value ===
19
+ "number" &&
20
+ Number.isFinite(value)) {
21
+ return value;
22
+ }
23
+ if (typeof value ===
24
+ "bigint") {
25
+ return Number(value);
26
+ }
27
+ if (typeof value ===
28
+ "string") {
29
+ const parsed = Number(value);
30
+ if (Number.isFinite(parsed)) {
31
+ return parsed;
32
+ }
33
+ }
34
+ return 0;
35
+ }
36
+ function jsonObject(value) {
37
+ if (value &&
38
+ typeof value ===
39
+ "object" &&
40
+ !Buffer.isBuffer(value)) {
41
+ return value;
42
+ }
43
+ if (typeof value !==
44
+ "string") {
45
+ return {};
46
+ }
47
+ try {
48
+ const parsed = JSON.parse(value);
49
+ if (parsed &&
50
+ typeof parsed ===
51
+ "object" &&
52
+ !Array.isArray(parsed)) {
53
+ return parsed;
54
+ }
55
+ }
56
+ catch {
57
+ // Non JSON data.
58
+ }
59
+ return {};
60
+ }
61
+ function toIso(value) {
62
+ let time = valueNumber(value);
63
+ if (time <=
64
+ 0) {
65
+ return new Date()
66
+ .toISOString();
67
+ }
68
+ /*
69
+ * Seconds → milliseconds.
70
+ */
71
+ if (time <
72
+ 100_000_000_000) {
73
+ time *=
74
+ 1000;
75
+ }
76
+ const date = new Date(time);
77
+ return Number.isNaN(date.getTime())
78
+ ? new Date()
79
+ .toISOString()
80
+ : date.toISOString();
81
+ }
82
+ function isInsideProject(projectRoot, candidate) {
83
+ if (!candidate) {
84
+ return false;
85
+ }
86
+ const root = resolve(projectRoot);
87
+ const target = resolve(candidate);
88
+ if (root ===
89
+ target) {
90
+ return true;
91
+ }
92
+ const rel = relative(root, target);
93
+ return (rel !==
94
+ "" &&
95
+ rel !==
96
+ ".." &&
97
+ !rel.startsWith(`..${process.platform ===
98
+ "win32"
99
+ ? "\\"
100
+ : "/"}`) &&
101
+ !isAbsolute(rel));
102
+ }
103
+ function decodeCursor(value) {
104
+ if (!value) {
105
+ return {
106
+ time: -1,
107
+ id: "",
108
+ };
109
+ }
110
+ try {
111
+ const parsed = JSON.parse(value);
112
+ return {
113
+ time: typeof parsed.time ===
114
+ "number"
115
+ ? parsed.time
116
+ : -1,
117
+ id: typeof parsed.id ===
118
+ "string"
119
+ ? parsed.id
120
+ : "",
121
+ };
122
+ }
123
+ catch {
124
+ return {
125
+ time: -1,
126
+ id: "",
127
+ };
128
+ }
129
+ }
130
+ function encodeCursor(value) {
131
+ return JSON.stringify(value);
132
+ }
133
+ function openDatabase(dbPath) {
134
+ if (!existsSync(dbPath)) {
135
+ throw new Error(`OpenCode database not found: ${dbPath}`);
136
+ }
137
+ const db = new DatabaseSync(dbPath, {
138
+ readOnly: true,
139
+ });
140
+ db.exec("PRAGMA query_only = ON");
141
+ db.exec("PRAGMA busy_timeout = 3000");
142
+ return db;
143
+ }
144
+ function sessionRow(db, nativeSessionId) {
145
+ const row = db.prepare(`
146
+ SELECT *
147
+ FROM "session"
148
+ WHERE id = ?
149
+ LIMIT 1
150
+ `).get(nativeSessionId);
151
+ if (!row) {
152
+ throw new Error(`OpenCode session not found: ${nativeSessionId}`);
153
+ }
154
+ return row;
155
+ }
156
+ function sessionBelongsToProject(db, row, project) {
157
+ const directDirectory = valueString(row.directory);
158
+ if (directDirectory &&
159
+ isInsideProject(project.rootPath, directDirectory)) {
160
+ return true;
161
+ }
162
+ const projectId = valueString(row.project_id);
163
+ if (projectId) {
164
+ try {
165
+ const projectRow = db.prepare(`
166
+ SELECT *
167
+ FROM "project"
168
+ WHERE id = ?
169
+ LIMIT 1
170
+ `).get(projectId);
171
+ if (projectRow) {
172
+ for (const field of [
173
+ "worktree",
174
+ "directory",
175
+ "path",
176
+ ]) {
177
+ const candidate = valueString(projectRow[field]);
178
+ if (candidate &&
179
+ isInsideProject(project.rootPath, candidate)) {
180
+ return true;
181
+ }
182
+ }
183
+ }
184
+ }
185
+ catch {
186
+ // Older schema.
187
+ }
188
+ try {
189
+ const directories = db.prepare(`
190
+ SELECT directory
191
+ FROM "project_directory"
192
+ WHERE project_id = ?
193
+ `).all(projectId);
194
+ if (directories.some(item => isInsideProject(project.rootPath, valueString(item.directory)))) {
195
+ return true;
196
+ }
197
+ }
198
+ catch {
199
+ // Older schema.
200
+ }
201
+ }
202
+ /*
203
+ * Last fallback:
204
+ * inspect message metadata path.root/cwd,
205
+ * but not text content.
206
+ */
207
+ try {
208
+ const rows = db.prepare(`
209
+ SELECT data
210
+ FROM "message"
211
+ WHERE session_id = ?
212
+ ORDER BY time_created DESC
213
+ LIMIT 20
214
+ `).all(valueString(row.id));
215
+ for (const message of rows) {
216
+ const data = jsonObject(message.data);
217
+ const path = data.path &&
218
+ typeof data.path ===
219
+ "object"
220
+ ? data.path
221
+ : {};
222
+ for (const candidate of [
223
+ valueString(path.cwd),
224
+ valueString(path.root),
225
+ ]) {
226
+ if (candidate &&
227
+ isInsideProject(project.rootPath, candidate)) {
228
+ return true;
229
+ }
230
+ }
231
+ }
232
+ }
233
+ catch {
234
+ // ignore
235
+ }
236
+ return false;
237
+ }
238
+ function fetchRowsAfter(db, table, nativeSessionId, cursor) {
239
+ const sql = `
240
+ SELECT *,
241
+ COALESCE(
242
+ time_updated,
243
+ time_created,
244
+ 0
245
+ ) AS __clock
246
+ FROM "${table}"
247
+ WHERE session_id = ?
248
+ AND (
249
+ COALESCE(
250
+ time_updated,
251
+ time_created,
252
+ 0
253
+ ) > ?
254
+ OR (
255
+ COALESCE(
256
+ time_updated,
257
+ time_created,
258
+ 0
259
+ ) = ?
260
+ AND id > ?
261
+ )
262
+ )
263
+ ORDER BY
264
+ COALESCE(
265
+ time_updated,
266
+ time_created,
267
+ 0
268
+ ) ASC,
269
+ id ASC
270
+ `;
271
+ return db.prepare(sql).all(nativeSessionId, cursor.time, cursor.time, cursor.id);
272
+ }
273
+ function cursorFromRows(rows, fallback) {
274
+ const last = rows[rows.length -
275
+ 1];
276
+ if (!last) {
277
+ return fallback;
278
+ }
279
+ return {
280
+ time: valueNumber(last.__clock),
281
+ id: valueString(last.id),
282
+ };
283
+ }
284
+ function messageEvent(dbPath, row) {
285
+ const data = jsonObject(row.data);
286
+ const role = valueString(data.role);
287
+ const clock = valueNumber(row.__clock);
288
+ const id = valueString(row.id);
289
+ let type = "message";
290
+ if (role ===
291
+ "user") {
292
+ type =
293
+ "user_prompt";
294
+ }
295
+ else if (role ===
296
+ "assistant") {
297
+ type =
298
+ "assistant_message";
299
+ }
300
+ return {
301
+ clock,
302
+ order: 0,
303
+ event: {
304
+ type,
305
+ timestamp: toIso(clock),
306
+ role: role ||
307
+ undefined,
308
+ sourceEventId: `message:${id}:${clock}`,
309
+ sourceSequence: `${clock}:${id}`,
310
+ data: {
311
+ messageId: id,
312
+ ...data,
313
+ },
314
+ provenance: {
315
+ source: "opencode",
316
+ sourcePath: dbPath,
317
+ sourceTable: "message",
318
+ sourceRowId: id,
319
+ sourceOffset: `${clock}:${id}`,
320
+ },
321
+ },
322
+ };
323
+ }
324
+ function partEvent(dbPath, row) {
325
+ const data = jsonObject(row.data);
326
+ const partType = valueString(data.type);
327
+ const clock = valueNumber(row.__clock);
328
+ const id = valueString(row.id);
329
+ const messageId = valueString(row.message_id);
330
+ let type = "message_part";
331
+ if (partType ===
332
+ "tool") {
333
+ type =
334
+ "tool_call";
335
+ }
336
+ else if (partType ===
337
+ "snapshot") {
338
+ type =
339
+ "artifact";
340
+ }
341
+ return {
342
+ clock,
343
+ order: 1,
344
+ event: {
345
+ type,
346
+ timestamp: toIso(clock),
347
+ sourceEventId: `part:${id}:${clock}`,
348
+ sourceSequence: `${clock}:${id}`,
349
+ data: {
350
+ partId: id,
351
+ messageId,
352
+ ...data,
353
+ },
354
+ provenance: {
355
+ source: "opencode",
356
+ sourcePath: dbPath,
357
+ sourceTable: "part",
358
+ sourceRowId: id,
359
+ sourceOffset: `${clock}:${id}`,
360
+ },
361
+ },
362
+ };
363
+ }
364
+ export async function syncOpenCodeSession(options) {
365
+ const dbPath = options.dbPath ??
366
+ defaultOpenCodeDbPath();
367
+ const db = openDatabase(dbPath);
368
+ try {
369
+ const session = sessionRow(db, options
370
+ .nativeSessionId);
371
+ if (!sessionBelongsToProject(db, session, options.project)) {
372
+ throw new Error([
373
+ "OpenCode session does not belong to current ToolNet project.",
374
+ `Session: ${options.nativeSessionId}`,
375
+ `Project: ${options.project.rootPath}`,
376
+ `Session directory: ${valueString(session.directory) || "unknown"}`,
377
+ ].join(" "));
378
+ }
379
+ const core = new SessionCore({
380
+ project: options.project,
381
+ storage: options.storage,
382
+ agent: "opencode",
383
+ nativeSessionId: options.nativeSessionId,
384
+ title: valueString(session.title) ||
385
+ undefined,
386
+ metadata: {
387
+ source: "opencode.db",
388
+ openCodeProjectId: valueString(session.project_id) ||
389
+ undefined,
390
+ directory: valueString(session.directory) ||
391
+ undefined,
392
+ },
393
+ });
394
+ const state = core.status();
395
+ const messageCursor = decodeCursor(state.sourceCursors["opencode.message"]);
396
+ const partCursor = decodeCursor(state.sourceCursors["opencode.part"]);
397
+ const messages = fetchRowsAfter(db, "message", options
398
+ .nativeSessionId, messageCursor);
399
+ const parts = fetchRowsAfter(db, "part", options
400
+ .nativeSessionId, partCursor);
401
+ const timed = [];
402
+ if (state.lastSequence ===
403
+ 0) {
404
+ const created = valueNumber(session
405
+ .time_created);
406
+ timed.push({
407
+ clock: created,
408
+ order: -1,
409
+ event: {
410
+ type: "session_start",
411
+ timestamp: toIso(created),
412
+ sourceEventId: `session:${options.nativeSessionId}:created:${created}`,
413
+ data: {
414
+ title: valueString(session.title) ||
415
+ undefined,
416
+ directory: valueString(session.directory) ||
417
+ undefined,
418
+ openCodeProjectId: valueString(session.project_id) ||
419
+ undefined,
420
+ },
421
+ provenance: {
422
+ source: "opencode",
423
+ sourcePath: dbPath,
424
+ sourceTable: "session",
425
+ sourceRowId: options
426
+ .nativeSessionId,
427
+ },
428
+ },
429
+ });
430
+ }
431
+ timed.push(...messages.map(row => messageEvent(dbPath, row)));
432
+ timed.push(...parts.map(row => partEvent(dbPath, row)));
433
+ const updated = valueNumber(session
434
+ .time_updated) ||
435
+ valueNumber(session
436
+ .time_created);
437
+ if (options.compacted) {
438
+ timed.push({
439
+ clock: updated,
440
+ order: 98,
441
+ event: {
442
+ type: "session_compact",
443
+ timestamp: toIso(updated),
444
+ sourceEventId: `session:${options.nativeSessionId}:compact:${updated}`,
445
+ data: {},
446
+ provenance: {
447
+ source: "opencode",
448
+ },
449
+ },
450
+ });
451
+ }
452
+ if (options.error) {
453
+ timed.push({
454
+ clock: updated,
455
+ order: 99,
456
+ event: {
457
+ type: "error",
458
+ timestamp: toIso(updated),
459
+ sourceEventId: `session:${options.nativeSessionId}:error:${updated}`,
460
+ data: {
461
+ source: "session.error",
462
+ },
463
+ provenance: {
464
+ source: "opencode",
465
+ },
466
+ },
467
+ });
468
+ }
469
+ else if (options.idle) {
470
+ timed.push({
471
+ clock: updated,
472
+ order: 100,
473
+ event: {
474
+ type: "session_idle",
475
+ timestamp: toIso(updated),
476
+ sourceEventId: `session:${options.nativeSessionId}:idle:${updated}`,
477
+ data: {},
478
+ provenance: {
479
+ source: "opencode",
480
+ },
481
+ },
482
+ });
483
+ }
484
+ timed.sort((left, right) => (left.clock -
485
+ right.clock) ||
486
+ (left.order -
487
+ right.order));
488
+ const recorded = core.recordMany(timed.map(item => item.event));
489
+ const nextMessageCursor = cursorFromRows(messages, messageCursor);
490
+ const nextPartCursor = cursorFromRows(parts, partCursor);
491
+ core.setSourceCursor("opencode.message", encodeCursor(nextMessageCursor));
492
+ core.setSourceCursor("opencode.part", encodeCursor(nextPartCursor));
493
+ const flushed = await core.flush();
494
+ return {
495
+ nativeSessionId: options
496
+ .nativeSessionId,
497
+ importedMessages: messages.length,
498
+ importedParts: parts.length,
499
+ recordedEvents: recorded.length,
500
+ eventCount: flushed.eventCount,
501
+ chunkCount: flushed.chunkCount,
502
+ status: flushed.status,
503
+ };
504
+ }
505
+ finally {
506
+ db.close();
507
+ }
508
+ }
509
+ export async function recoverOpenCodeProject(options) {
510
+ const dbPath = options.dbPath ??
511
+ defaultOpenCodeDbPath();
512
+ const db = openDatabase(dbPath);
513
+ const sessionIds = [];
514
+ try {
515
+ const rows = db.prepare(`
516
+ SELECT *
517
+ FROM "session"
518
+ ORDER BY
519
+ COALESCE(
520
+ time_updated,
521
+ time_created,
522
+ 0
523
+ ) DESC
524
+ `).all();
525
+ for (const row of rows) {
526
+ if (!sessionBelongsToProject(db, row, options.project)) {
527
+ continue;
528
+ }
529
+ const id = valueString(row.id);
530
+ if (id) {
531
+ sessionIds.push(id);
532
+ }
533
+ if (sessionIds.length >=
534
+ (options.limit ??
535
+ 100)) {
536
+ break;
537
+ }
538
+ }
539
+ }
540
+ finally {
541
+ db.close();
542
+ }
543
+ const results = [];
544
+ for (const nativeSessionId of sessionIds) {
545
+ results.push(await syncOpenCodeSession({
546
+ project: options.project,
547
+ storage: options.storage,
548
+ nativeSessionId,
549
+ dbPath,
550
+ }));
551
+ }
552
+ return results;
553
+ }