attocode 0.1.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 (488) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/LICENSE +21 -0
  3. package/README.md +164 -0
  4. package/dist/src/adapters.d.ts +83 -0
  5. package/dist/src/adapters.d.ts.map +1 -0
  6. package/dist/src/adapters.js +221 -0
  7. package/dist/src/adapters.js.map +1 -0
  8. package/dist/src/agent-tools/index.d.ts +7 -0
  9. package/dist/src/agent-tools/index.d.ts.map +1 -0
  10. package/dist/src/agent-tools/index.js +8 -0
  11. package/dist/src/agent-tools/index.js.map +1 -0
  12. package/dist/src/agent-tools/lsp-file-tools.d.ts +33 -0
  13. package/dist/src/agent-tools/lsp-file-tools.d.ts.map +1 -0
  14. package/dist/src/agent-tools/lsp-file-tools.js +200 -0
  15. package/dist/src/agent-tools/lsp-file-tools.js.map +1 -0
  16. package/dist/src/agent.d.ts +667 -0
  17. package/dist/src/agent.d.ts.map +1 -0
  18. package/dist/src/agent.js +2824 -0
  19. package/dist/src/agent.js.map +1 -0
  20. package/dist/src/cli.d.ts +36 -0
  21. package/dist/src/cli.d.ts.map +1 -0
  22. package/dist/src/cli.js +176 -0
  23. package/dist/src/cli.js.map +1 -0
  24. package/dist/src/commands/handler.d.ts +22 -0
  25. package/dist/src/commands/handler.d.ts.map +1 -0
  26. package/dist/src/commands/handler.js +1320 -0
  27. package/dist/src/commands/handler.js.map +1 -0
  28. package/dist/src/commands/init.d.ts +7 -0
  29. package/dist/src/commands/init.d.ts.map +1 -0
  30. package/dist/src/commands/init.js +153 -0
  31. package/dist/src/commands/init.js.map +1 -0
  32. package/dist/src/commands/types.d.ts +70 -0
  33. package/dist/src/commands/types.d.ts.map +1 -0
  34. package/dist/src/commands/types.js +8 -0
  35. package/dist/src/commands/types.js.map +1 -0
  36. package/dist/src/config.d.ts +22 -0
  37. package/dist/src/config.d.ts.map +1 -0
  38. package/dist/src/config.js +25 -0
  39. package/dist/src/config.js.map +1 -0
  40. package/dist/src/core/index.d.ts +32 -0
  41. package/dist/src/core/index.d.ts.map +1 -0
  42. package/dist/src/core/index.js +35 -0
  43. package/dist/src/core/index.js.map +1 -0
  44. package/dist/src/core/process-handlers.d.ts +43 -0
  45. package/dist/src/core/process-handlers.d.ts.map +1 -0
  46. package/dist/src/core/process-handlers.js +117 -0
  47. package/dist/src/core/process-handlers.js.map +1 -0
  48. package/dist/src/core/protocol/bridge.d.ts +117 -0
  49. package/dist/src/core/protocol/bridge.d.ts.map +1 -0
  50. package/dist/src/core/protocol/bridge.js +149 -0
  51. package/dist/src/core/protocol/bridge.js.map +1 -0
  52. package/dist/src/core/protocol/index.d.ts +8 -0
  53. package/dist/src/core/protocol/index.d.ts.map +1 -0
  54. package/dist/src/core/protocol/index.js +8 -0
  55. package/dist/src/core/protocol/index.js.map +1 -0
  56. package/dist/src/core/protocol/types.d.ts +539 -0
  57. package/dist/src/core/protocol/types.d.ts.map +1 -0
  58. package/dist/src/core/protocol/types.js +149 -0
  59. package/dist/src/core/protocol/types.js.map +1 -0
  60. package/dist/src/core/queues/atomic-counter.d.ts +36 -0
  61. package/dist/src/core/queues/atomic-counter.d.ts.map +1 -0
  62. package/dist/src/core/queues/atomic-counter.js +46 -0
  63. package/dist/src/core/queues/atomic-counter.js.map +1 -0
  64. package/dist/src/core/queues/event-queue.d.ts +126 -0
  65. package/dist/src/core/queues/event-queue.d.ts.map +1 -0
  66. package/dist/src/core/queues/event-queue.js +208 -0
  67. package/dist/src/core/queues/event-queue.js.map +1 -0
  68. package/dist/src/core/queues/index.d.ts +12 -0
  69. package/dist/src/core/queues/index.d.ts.map +1 -0
  70. package/dist/src/core/queues/index.js +15 -0
  71. package/dist/src/core/queues/index.js.map +1 -0
  72. package/dist/src/core/queues/submission-queue.d.ts +116 -0
  73. package/dist/src/core/queues/submission-queue.d.ts.map +1 -0
  74. package/dist/src/core/queues/submission-queue.js +236 -0
  75. package/dist/src/core/queues/submission-queue.js.map +1 -0
  76. package/dist/src/costs/index.d.ts +22 -0
  77. package/dist/src/costs/index.d.ts.map +1 -0
  78. package/dist/src/costs/index.js +22 -0
  79. package/dist/src/costs/index.js.map +1 -0
  80. package/dist/src/costs/model-registry.d.ts +80 -0
  81. package/dist/src/costs/model-registry.d.ts.map +1 -0
  82. package/dist/src/costs/model-registry.js +237 -0
  83. package/dist/src/costs/model-registry.js.map +1 -0
  84. package/dist/src/costs/types.d.ts +50 -0
  85. package/dist/src/costs/types.d.ts.map +1 -0
  86. package/dist/src/costs/types.js +2 -0
  87. package/dist/src/costs/types.js.map +1 -0
  88. package/dist/src/defaults.d.ts +114 -0
  89. package/dist/src/defaults.d.ts.map +1 -0
  90. package/dist/src/defaults.js +457 -0
  91. package/dist/src/defaults.js.map +1 -0
  92. package/dist/src/first-run.d.ts +35 -0
  93. package/dist/src/first-run.d.ts.map +1 -0
  94. package/dist/src/first-run.js +94 -0
  95. package/dist/src/first-run.js.map +1 -0
  96. package/dist/src/hello.d.ts +2 -0
  97. package/dist/src/hello.d.ts.map +1 -0
  98. package/dist/src/hello.js +4 -0
  99. package/dist/src/hello.js.map +1 -0
  100. package/dist/src/integrations/agent-registry.d.ts +160 -0
  101. package/dist/src/integrations/agent-registry.d.ts.map +1 -0
  102. package/dist/src/integrations/agent-registry.js +446 -0
  103. package/dist/src/integrations/agent-registry.js.map +1 -0
  104. package/dist/src/integrations/auto-compaction.d.ts +177 -0
  105. package/dist/src/integrations/auto-compaction.d.ts.map +1 -0
  106. package/dist/src/integrations/auto-compaction.js +428 -0
  107. package/dist/src/integrations/auto-compaction.js.map +1 -0
  108. package/dist/src/integrations/cancellation.d.ts +162 -0
  109. package/dist/src/integrations/cancellation.d.ts.map +1 -0
  110. package/dist/src/integrations/cancellation.js +339 -0
  111. package/dist/src/integrations/cancellation.js.map +1 -0
  112. package/dist/src/integrations/codebase-context.d.ts +319 -0
  113. package/dist/src/integrations/codebase-context.d.ts.map +1 -0
  114. package/dist/src/integrations/codebase-context.js +816 -0
  115. package/dist/src/integrations/codebase-context.js.map +1 -0
  116. package/dist/src/integrations/compaction.d.ts +192 -0
  117. package/dist/src/integrations/compaction.d.ts.map +1 -0
  118. package/dist/src/integrations/compaction.js +376 -0
  119. package/dist/src/integrations/compaction.js.map +1 -0
  120. package/dist/src/integrations/context-engineering.d.ts +246 -0
  121. package/dist/src/integrations/context-engineering.d.ts.map +1 -0
  122. package/dist/src/integrations/context-engineering.js +394 -0
  123. package/dist/src/integrations/context-engineering.js.map +1 -0
  124. package/dist/src/integrations/diff-utils.d.ts +105 -0
  125. package/dist/src/integrations/diff-utils.d.ts.map +1 -0
  126. package/dist/src/integrations/diff-utils.js +497 -0
  127. package/dist/src/integrations/diff-utils.js.map +1 -0
  128. package/dist/src/integrations/economics.d.ts +192 -0
  129. package/dist/src/integrations/economics.d.ts.map +1 -0
  130. package/dist/src/integrations/economics.js +431 -0
  131. package/dist/src/integrations/economics.js.map +1 -0
  132. package/dist/src/integrations/execution-policy.d.ts +189 -0
  133. package/dist/src/integrations/execution-policy.d.ts.map +1 -0
  134. package/dist/src/integrations/execution-policy.js +352 -0
  135. package/dist/src/integrations/execution-policy.js.map +1 -0
  136. package/dist/src/integrations/file-change-tracker.d.ts +161 -0
  137. package/dist/src/integrations/file-change-tracker.d.ts.map +1 -0
  138. package/dist/src/integrations/file-change-tracker.js +520 -0
  139. package/dist/src/integrations/file-change-tracker.js.map +1 -0
  140. package/dist/src/integrations/hierarchical-config.d.ts +212 -0
  141. package/dist/src/integrations/hierarchical-config.d.ts.map +1 -0
  142. package/dist/src/integrations/hierarchical-config.js +484 -0
  143. package/dist/src/integrations/hierarchical-config.js.map +1 -0
  144. package/dist/src/integrations/hooks.d.ts +114 -0
  145. package/dist/src/integrations/hooks.d.ts.map +1 -0
  146. package/dist/src/integrations/hooks.js +326 -0
  147. package/dist/src/integrations/hooks.js.map +1 -0
  148. package/dist/src/integrations/ignore.d.ts +143 -0
  149. package/dist/src/integrations/ignore.d.ts.map +1 -0
  150. package/dist/src/integrations/ignore.js +417 -0
  151. package/dist/src/integrations/ignore.js.map +1 -0
  152. package/dist/src/integrations/image-renderer.d.ts +119 -0
  153. package/dist/src/integrations/image-renderer.d.ts.map +1 -0
  154. package/dist/src/integrations/image-renderer.js +306 -0
  155. package/dist/src/integrations/image-renderer.js.map +1 -0
  156. package/dist/src/integrations/index.d.ts +42 -0
  157. package/dist/src/integrations/index.d.ts.map +1 -0
  158. package/dist/src/integrations/index.js +73 -0
  159. package/dist/src/integrations/index.js.map +1 -0
  160. package/dist/src/integrations/lsp.d.ts +196 -0
  161. package/dist/src/integrations/lsp.d.ts.map +1 -0
  162. package/dist/src/integrations/lsp.js +582 -0
  163. package/dist/src/integrations/lsp.js.map +1 -0
  164. package/dist/src/integrations/mcp-client.d.ts +270 -0
  165. package/dist/src/integrations/mcp-client.d.ts.map +1 -0
  166. package/dist/src/integrations/mcp-client.js +698 -0
  167. package/dist/src/integrations/mcp-client.js.map +1 -0
  168. package/dist/src/integrations/mcp-tool-search.d.ts +77 -0
  169. package/dist/src/integrations/mcp-tool-search.d.ts.map +1 -0
  170. package/dist/src/integrations/mcp-tool-search.js +220 -0
  171. package/dist/src/integrations/mcp-tool-search.js.map +1 -0
  172. package/dist/src/integrations/memory.d.ts +108 -0
  173. package/dist/src/integrations/memory.d.ts.map +1 -0
  174. package/dist/src/integrations/memory.js +288 -0
  175. package/dist/src/integrations/memory.js.map +1 -0
  176. package/dist/src/integrations/multi-agent.d.ts +150 -0
  177. package/dist/src/integrations/multi-agent.d.ts.map +1 -0
  178. package/dist/src/integrations/multi-agent.js +306 -0
  179. package/dist/src/integrations/multi-agent.js.map +1 -0
  180. package/dist/src/integrations/observability.d.ts +162 -0
  181. package/dist/src/integrations/observability.d.ts.map +1 -0
  182. package/dist/src/integrations/observability.js +406 -0
  183. package/dist/src/integrations/observability.js.map +1 -0
  184. package/dist/src/integrations/openrouter-pricing.d.ts +42 -0
  185. package/dist/src/integrations/openrouter-pricing.d.ts.map +1 -0
  186. package/dist/src/integrations/openrouter-pricing.js +124 -0
  187. package/dist/src/integrations/openrouter-pricing.js.map +1 -0
  188. package/dist/src/integrations/pending-plan.d.ts +171 -0
  189. package/dist/src/integrations/pending-plan.d.ts.map +1 -0
  190. package/dist/src/integrations/pending-plan.js +244 -0
  191. package/dist/src/integrations/pending-plan.js.map +1 -0
  192. package/dist/src/integrations/persistence.d.ts +48 -0
  193. package/dist/src/integrations/persistence.d.ts.map +1 -0
  194. package/dist/src/integrations/persistence.js +196 -0
  195. package/dist/src/integrations/persistence.js.map +1 -0
  196. package/dist/src/integrations/planning.d.ts +96 -0
  197. package/dist/src/integrations/planning.d.ts.map +1 -0
  198. package/dist/src/integrations/planning.js +338 -0
  199. package/dist/src/integrations/planning.js.map +1 -0
  200. package/dist/src/integrations/pty-shell.d.ts +169 -0
  201. package/dist/src/integrations/pty-shell.d.ts.map +1 -0
  202. package/dist/src/integrations/pty-shell.js +367 -0
  203. package/dist/src/integrations/pty-shell.js.map +1 -0
  204. package/dist/src/integrations/react.d.ts +139 -0
  205. package/dist/src/integrations/react.d.ts.map +1 -0
  206. package/dist/src/integrations/react.js +273 -0
  207. package/dist/src/integrations/react.js.map +1 -0
  208. package/dist/src/integrations/resources.d.ts +177 -0
  209. package/dist/src/integrations/resources.d.ts.map +1 -0
  210. package/dist/src/integrations/resources.js +311 -0
  211. package/dist/src/integrations/resources.js.map +1 -0
  212. package/dist/src/integrations/result-synthesizer.d.ts +389 -0
  213. package/dist/src/integrations/result-synthesizer.d.ts.map +1 -0
  214. package/dist/src/integrations/result-synthesizer.js +951 -0
  215. package/dist/src/integrations/result-synthesizer.js.map +1 -0
  216. package/dist/src/integrations/routing.d.ts +117 -0
  217. package/dist/src/integrations/routing.d.ts.map +1 -0
  218. package/dist/src/integrations/routing.js +347 -0
  219. package/dist/src/integrations/routing.js.map +1 -0
  220. package/dist/src/integrations/rules.d.ts +131 -0
  221. package/dist/src/integrations/rules.d.ts.map +1 -0
  222. package/dist/src/integrations/rules.js +284 -0
  223. package/dist/src/integrations/rules.js.map +1 -0
  224. package/dist/src/integrations/safety.d.ts +142 -0
  225. package/dist/src/integrations/safety.d.ts.map +1 -0
  226. package/dist/src/integrations/safety.js +342 -0
  227. package/dist/src/integrations/safety.js.map +1 -0
  228. package/dist/src/integrations/sandbox/basic.d.ts +74 -0
  229. package/dist/src/integrations/sandbox/basic.d.ts.map +1 -0
  230. package/dist/src/integrations/sandbox/basic.js +310 -0
  231. package/dist/src/integrations/sandbox/basic.js.map +1 -0
  232. package/dist/src/integrations/sandbox/docker.d.ts +94 -0
  233. package/dist/src/integrations/sandbox/docker.d.ts.map +1 -0
  234. package/dist/src/integrations/sandbox/docker.js +293 -0
  235. package/dist/src/integrations/sandbox/docker.js.map +1 -0
  236. package/dist/src/integrations/sandbox/index.d.ts +182 -0
  237. package/dist/src/integrations/sandbox/index.d.ts.map +1 -0
  238. package/dist/src/integrations/sandbox/index.js +382 -0
  239. package/dist/src/integrations/sandbox/index.js.map +1 -0
  240. package/dist/src/integrations/sandbox/landlock.d.ts +59 -0
  241. package/dist/src/integrations/sandbox/landlock.d.ts.map +1 -0
  242. package/dist/src/integrations/sandbox/landlock.js +326 -0
  243. package/dist/src/integrations/sandbox/landlock.js.map +1 -0
  244. package/dist/src/integrations/sandbox/seatbelt.d.ts +68 -0
  245. package/dist/src/integrations/sandbox/seatbelt.d.ts.map +1 -0
  246. package/dist/src/integrations/sandbox/seatbelt.js +298 -0
  247. package/dist/src/integrations/sandbox/seatbelt.js.map +1 -0
  248. package/dist/src/integrations/semantic-cache.d.ts +178 -0
  249. package/dist/src/integrations/semantic-cache.d.ts.map +1 -0
  250. package/dist/src/integrations/semantic-cache.js +372 -0
  251. package/dist/src/integrations/semantic-cache.js.map +1 -0
  252. package/dist/src/integrations/session-store.d.ts +183 -0
  253. package/dist/src/integrations/session-store.d.ts.map +1 -0
  254. package/dist/src/integrations/session-store.js +345 -0
  255. package/dist/src/integrations/session-store.js.map +1 -0
  256. package/dist/src/integrations/shared-blackboard.d.ts +403 -0
  257. package/dist/src/integrations/shared-blackboard.d.ts.map +1 -0
  258. package/dist/src/integrations/shared-blackboard.js +710 -0
  259. package/dist/src/integrations/shared-blackboard.js.map +1 -0
  260. package/dist/src/integrations/skills.d.ts +171 -0
  261. package/dist/src/integrations/skills.d.ts.map +1 -0
  262. package/dist/src/integrations/skills.js +403 -0
  263. package/dist/src/integrations/skills.js.map +1 -0
  264. package/dist/src/integrations/smart-decomposer.d.ts +322 -0
  265. package/dist/src/integrations/smart-decomposer.d.ts.map +1 -0
  266. package/dist/src/integrations/smart-decomposer.js +856 -0
  267. package/dist/src/integrations/smart-decomposer.js.map +1 -0
  268. package/dist/src/integrations/sourcegraph.d.ts +169 -0
  269. package/dist/src/integrations/sourcegraph.d.ts.map +1 -0
  270. package/dist/src/integrations/sourcegraph.js +379 -0
  271. package/dist/src/integrations/sourcegraph.js.map +1 -0
  272. package/dist/src/integrations/sqlite-store.d.ts +518 -0
  273. package/dist/src/integrations/sqlite-store.d.ts.map +1 -0
  274. package/dist/src/integrations/sqlite-store.js +1423 -0
  275. package/dist/src/integrations/sqlite-store.js.map +1 -0
  276. package/dist/src/integrations/streaming.d.ts +102 -0
  277. package/dist/src/integrations/streaming.d.ts.map +1 -0
  278. package/dist/src/integrations/streaming.js +362 -0
  279. package/dist/src/integrations/streaming.js.map +1 -0
  280. package/dist/src/integrations/thread-manager.d.ts +199 -0
  281. package/dist/src/integrations/thread-manager.d.ts.map +1 -0
  282. package/dist/src/integrations/thread-manager.js +357 -0
  283. package/dist/src/integrations/thread-manager.js.map +1 -0
  284. package/dist/src/main.d.ts +26 -0
  285. package/dist/src/main.d.ts.map +1 -0
  286. package/dist/src/main.js +170 -0
  287. package/dist/src/main.js.map +1 -0
  288. package/dist/src/modes/index.d.ts +10 -0
  289. package/dist/src/modes/index.d.ts.map +1 -0
  290. package/dist/src/modes/index.js +10 -0
  291. package/dist/src/modes/index.js.map +1 -0
  292. package/dist/src/modes/repl.d.ts +19 -0
  293. package/dist/src/modes/repl.d.ts.map +1 -0
  294. package/dist/src/modes/repl.js +393 -0
  295. package/dist/src/modes/repl.js.map +1 -0
  296. package/dist/src/modes/tui.d.ts +29 -0
  297. package/dist/src/modes/tui.d.ts.map +1 -0
  298. package/dist/src/modes/tui.js +272 -0
  299. package/dist/src/modes/tui.js.map +1 -0
  300. package/dist/src/modes.d.ts +179 -0
  301. package/dist/src/modes.d.ts.map +1 -0
  302. package/dist/src/modes.js +385 -0
  303. package/dist/src/modes.js.map +1 -0
  304. package/dist/src/observability/tracer.d.ts +111 -0
  305. package/dist/src/observability/tracer.d.ts.map +1 -0
  306. package/dist/src/observability/tracer.js +300 -0
  307. package/dist/src/observability/tracer.js.map +1 -0
  308. package/dist/src/observability/types.d.ts +271 -0
  309. package/dist/src/observability/types.d.ts.map +1 -0
  310. package/dist/src/observability/types.js +24 -0
  311. package/dist/src/observability/types.js.map +1 -0
  312. package/dist/src/paths.d.ts +101 -0
  313. package/dist/src/paths.d.ts.map +1 -0
  314. package/dist/src/paths.js +148 -0
  315. package/dist/src/paths.js.map +1 -0
  316. package/dist/src/persistence/index.d.ts +38 -0
  317. package/dist/src/persistence/index.d.ts.map +1 -0
  318. package/dist/src/persistence/index.js +48 -0
  319. package/dist/src/persistence/index.js.map +1 -0
  320. package/dist/src/persistence/migrator.d.ts +135 -0
  321. package/dist/src/persistence/migrator.d.ts.map +1 -0
  322. package/dist/src/persistence/migrator.js +303 -0
  323. package/dist/src/persistence/migrator.js.map +1 -0
  324. package/dist/src/persistence/schema.d.ts +101 -0
  325. package/dist/src/persistence/schema.d.ts.map +1 -0
  326. package/dist/src/persistence/schema.js +395 -0
  327. package/dist/src/persistence/schema.js.map +1 -0
  328. package/dist/src/providers/adapters/anthropic.d.ts +20 -0
  329. package/dist/src/providers/adapters/anthropic.d.ts.map +1 -0
  330. package/dist/src/providers/adapters/anthropic.js +124 -0
  331. package/dist/src/providers/adapters/anthropic.js.map +1 -0
  332. package/dist/src/providers/adapters/mock.d.ts +25 -0
  333. package/dist/src/providers/adapters/mock.d.ts.map +1 -0
  334. package/dist/src/providers/adapters/mock.js +133 -0
  335. package/dist/src/providers/adapters/mock.js.map +1 -0
  336. package/dist/src/providers/adapters/openai.d.ts +21 -0
  337. package/dist/src/providers/adapters/openai.d.ts.map +1 -0
  338. package/dist/src/providers/adapters/openai.js +126 -0
  339. package/dist/src/providers/adapters/openai.js.map +1 -0
  340. package/dist/src/providers/adapters/openrouter.d.ts +49 -0
  341. package/dist/src/providers/adapters/openrouter.d.ts.map +1 -0
  342. package/dist/src/providers/adapters/openrouter.js +363 -0
  343. package/dist/src/providers/adapters/openrouter.js.map +1 -0
  344. package/dist/src/providers/provider.d.ts +54 -0
  345. package/dist/src/providers/provider.d.ts.map +1 -0
  346. package/dist/src/providers/provider.js +111 -0
  347. package/dist/src/providers/provider.js.map +1 -0
  348. package/dist/src/providers/resilient-fetch.d.ts +99 -0
  349. package/dist/src/providers/resilient-fetch.d.ts.map +1 -0
  350. package/dist/src/providers/resilient-fetch.js +208 -0
  351. package/dist/src/providers/resilient-fetch.js.map +1 -0
  352. package/dist/src/providers/types.d.ts +227 -0
  353. package/dist/src/providers/types.d.ts.map +1 -0
  354. package/dist/src/providers/types.js +24 -0
  355. package/dist/src/providers/types.js.map +1 -0
  356. package/dist/src/session-picker.d.ts +28 -0
  357. package/dist/src/session-picker.d.ts.map +1 -0
  358. package/dist/src/session-picker.js +256 -0
  359. package/dist/src/session-picker.js.map +1 -0
  360. package/dist/src/test-sqlite.d.ts +2 -0
  361. package/dist/src/test-sqlite.d.ts.map +1 -0
  362. package/dist/src/test-sqlite.js +114 -0
  363. package/dist/src/test-sqlite.js.map +1 -0
  364. package/dist/src/tools/agent.d.ts +44 -0
  365. package/dist/src/tools/agent.d.ts.map +1 -0
  366. package/dist/src/tools/agent.js +110 -0
  367. package/dist/src/tools/agent.js.map +1 -0
  368. package/dist/src/tools/bash.d.ts +52 -0
  369. package/dist/src/tools/bash.d.ts.map +1 -0
  370. package/dist/src/tools/bash.js +141 -0
  371. package/dist/src/tools/bash.js.map +1 -0
  372. package/dist/src/tools/file.d.ts +47 -0
  373. package/dist/src/tools/file.d.ts.map +1 -0
  374. package/dist/src/tools/file.js +263 -0
  375. package/dist/src/tools/file.js.map +1 -0
  376. package/dist/src/tools/permission.d.ts +43 -0
  377. package/dist/src/tools/permission.d.ts.map +1 -0
  378. package/dist/src/tools/permission.js +216 -0
  379. package/dist/src/tools/permission.js.map +1 -0
  380. package/dist/src/tools/registry.d.ts +63 -0
  381. package/dist/src/tools/registry.d.ts.map +1 -0
  382. package/dist/src/tools/registry.js +250 -0
  383. package/dist/src/tools/registry.js.map +1 -0
  384. package/dist/src/tools/standard.d.ts +57 -0
  385. package/dist/src/tools/standard.d.ts.map +1 -0
  386. package/dist/src/tools/standard.js +113 -0
  387. package/dist/src/tools/standard.js.map +1 -0
  388. package/dist/src/tools/types.d.ts +146 -0
  389. package/dist/src/tools/types.d.ts.map +1 -0
  390. package/dist/src/tools/types.js +28 -0
  391. package/dist/src/tools/types.js.map +1 -0
  392. package/dist/src/tools/undo.d.ts +71 -0
  393. package/dist/src/tools/undo.d.ts.map +1 -0
  394. package/dist/src/tools/undo.js +123 -0
  395. package/dist/src/tools/undo.js.map +1 -0
  396. package/dist/src/tracing/cache-boundary-tracker.d.ts +189 -0
  397. package/dist/src/tracing/cache-boundary-tracker.d.ts.map +1 -0
  398. package/dist/src/tracing/cache-boundary-tracker.js +411 -0
  399. package/dist/src/tracing/cache-boundary-tracker.js.map +1 -0
  400. package/dist/src/tracing/trace-collector.d.ts +274 -0
  401. package/dist/src/tracing/trace-collector.d.ts.map +1 -0
  402. package/dist/src/tracing/trace-collector.js +727 -0
  403. package/dist/src/tracing/trace-collector.js.map +1 -0
  404. package/dist/src/tracing/types.d.ts +657 -0
  405. package/dist/src/tracing/types.d.ts.map +1 -0
  406. package/dist/src/tracing/types.js +39 -0
  407. package/dist/src/tracing/types.js.map +1 -0
  408. package/dist/src/tricks/failure-evidence.d.ts +268 -0
  409. package/dist/src/tricks/failure-evidence.d.ts.map +1 -0
  410. package/dist/src/tricks/failure-evidence.js +544 -0
  411. package/dist/src/tricks/failure-evidence.js.map +1 -0
  412. package/dist/src/tricks/json-utils.d.ts +77 -0
  413. package/dist/src/tricks/json-utils.d.ts.map +1 -0
  414. package/dist/src/tricks/json-utils.js +247 -0
  415. package/dist/src/tricks/json-utils.js.map +1 -0
  416. package/dist/src/tricks/kv-cache-context.d.ts +227 -0
  417. package/dist/src/tricks/kv-cache-context.d.ts.map +1 -0
  418. package/dist/src/tricks/kv-cache-context.js +377 -0
  419. package/dist/src/tricks/kv-cache-context.js.map +1 -0
  420. package/dist/src/tricks/recitation.d.ts +208 -0
  421. package/dist/src/tricks/recitation.d.ts.map +1 -0
  422. package/dist/src/tricks/recitation.js +374 -0
  423. package/dist/src/tricks/recitation.js.map +1 -0
  424. package/dist/src/tricks/reversible-compaction.d.ts +251 -0
  425. package/dist/src/tricks/reversible-compaction.d.ts.map +1 -0
  426. package/dist/src/tricks/reversible-compaction.js +555 -0
  427. package/dist/src/tricks/reversible-compaction.js.map +1 -0
  428. package/dist/src/tricks/serialization-diversity.d.ts +197 -0
  429. package/dist/src/tricks/serialization-diversity.d.ts.map +1 -0
  430. package/dist/src/tricks/serialization-diversity.js +460 -0
  431. package/dist/src/tricks/serialization-diversity.js.map +1 -0
  432. package/dist/src/tui/app.d.ts +42 -0
  433. package/dist/src/tui/app.d.ts.map +1 -0
  434. package/dist/src/tui/app.js +1076 -0
  435. package/dist/src/tui/app.js.map +1 -0
  436. package/dist/src/tui/components/ApprovalDialog.d.ts +28 -0
  437. package/dist/src/tui/components/ApprovalDialog.d.ts.map +1 -0
  438. package/dist/src/tui/components/ApprovalDialog.js +59 -0
  439. package/dist/src/tui/components/ApprovalDialog.js.map +1 -0
  440. package/dist/src/tui/components/InputArea.d.ts +35 -0
  441. package/dist/src/tui/components/InputArea.d.ts.map +1 -0
  442. package/dist/src/tui/components/InputArea.js +144 -0
  443. package/dist/src/tui/components/InputArea.js.map +1 -0
  444. package/dist/src/tui/components/MessageItem.d.ts +28 -0
  445. package/dist/src/tui/components/MessageItem.d.ts.map +1 -0
  446. package/dist/src/tui/components/MessageItem.js +27 -0
  447. package/dist/src/tui/components/MessageItem.js.map +1 -0
  448. package/dist/src/tui/components/ScrollableBox.d.ts +41 -0
  449. package/dist/src/tui/components/ScrollableBox.d.ts.map +1 -0
  450. package/dist/src/tui/components/ScrollableBox.js +101 -0
  451. package/dist/src/tui/components/ScrollableBox.js.map +1 -0
  452. package/dist/src/tui/components/ToolCallItem.d.ts +33 -0
  453. package/dist/src/tui/components/ToolCallItem.d.ts.map +1 -0
  454. package/dist/src/tui/components/ToolCallItem.js +91 -0
  455. package/dist/src/tui/components/ToolCallItem.js.map +1 -0
  456. package/dist/src/tui/components/index.d.ts +13 -0
  457. package/dist/src/tui/components/index.d.ts.map +1 -0
  458. package/dist/src/tui/components/index.js +15 -0
  459. package/dist/src/tui/components/index.js.map +1 -0
  460. package/dist/src/tui/event-display.d.ts +19 -0
  461. package/dist/src/tui/event-display.d.ts.map +1 -0
  462. package/dist/src/tui/event-display.js +178 -0
  463. package/dist/src/tui/event-display.js.map +1 -0
  464. package/dist/src/tui/index.d.ts +105 -0
  465. package/dist/src/tui/index.d.ts.map +1 -0
  466. package/dist/src/tui/index.js +214 -0
  467. package/dist/src/tui/index.js.map +1 -0
  468. package/dist/src/tui/input/CommandPalette.d.ts +55 -0
  469. package/dist/src/tui/input/CommandPalette.d.ts.map +1 -0
  470. package/dist/src/tui/input/CommandPalette.js +135 -0
  471. package/dist/src/tui/input/CommandPalette.js.map +1 -0
  472. package/dist/src/tui/input/index.d.ts +7 -0
  473. package/dist/src/tui/input/index.d.ts.map +1 -0
  474. package/dist/src/tui/input/index.js +7 -0
  475. package/dist/src/tui/input/index.js.map +1 -0
  476. package/dist/src/tui/theme/index.d.ts +45 -0
  477. package/dist/src/tui/theme/index.d.ts.map +1 -0
  478. package/dist/src/tui/theme/index.js +215 -0
  479. package/dist/src/tui/theme/index.js.map +1 -0
  480. package/dist/src/tui/types.d.ts +214 -0
  481. package/dist/src/tui/types.d.ts.map +1 -0
  482. package/dist/src/tui/types.js +27 -0
  483. package/dist/src/tui/types.js.map +1 -0
  484. package/dist/src/types.d.ts +905 -0
  485. package/dist/src/types.d.ts.map +1 -0
  486. package/dist/src/types.js +9 -0
  487. package/dist/src/types.js.map +1 -0
  488. package/package.json +89 -0
@@ -0,0 +1,1423 @@
1
+ /**
2
+ * SQLite Session Store
3
+ *
4
+ * Alternative backend for session persistence using SQLite.
5
+ * Provides better query performance and ACID guarantees.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * const store = await createSQLiteStore({ dbPath: '.agent/sessions.db' });
10
+ * await store.createSession('My Session');
11
+ * await store.appendMessage({ role: 'user', content: 'Hello' });
12
+ * ```
13
+ */
14
+ import Database from 'better-sqlite3';
15
+ import { join, dirname } from 'node:path';
16
+ import { mkdir } from 'node:fs/promises';
17
+ import { existsSync, readFileSync } from 'node:fs';
18
+ import { applyMigrations, getMigrationStatus, detectFeatures, } from '../persistence/schema.js';
19
+ // =============================================================================
20
+ // SQLITE STORE
21
+ // =============================================================================
22
+ /**
23
+ * SQLite-backed session store.
24
+ */
25
+ export class SQLiteStore {
26
+ db;
27
+ config;
28
+ currentSessionId = null;
29
+ listeners = [];
30
+ /** Available schema features (detected after migration) */
31
+ features = {
32
+ core: false,
33
+ costs: false,
34
+ hierarchy: false,
35
+ compaction: false,
36
+ fileChanges: false,
37
+ goals: false,
38
+ workerResults: false,
39
+ pendingPlans: false,
40
+ };
41
+ // Prepared statements for performance
42
+ // Goal-related statements are optional (only prepared if feature is available)
43
+ stmts;
44
+ constructor(config = {}) {
45
+ this.config = {
46
+ baseDir: config.baseDir || '.agent/sessions',
47
+ dbPath: config.dbPath || join(config.baseDir || '.agent/sessions', 'sessions.db'),
48
+ autoSave: config.autoSave ?? true,
49
+ maxSessions: config.maxSessions || 50,
50
+ walMode: config.walMode ?? true,
51
+ };
52
+ // Initialize database
53
+ this.db = new Database(this.config.dbPath);
54
+ // Enable WAL mode for better concurrent access
55
+ if (this.config.walMode) {
56
+ this.db.pragma('journal_mode = WAL');
57
+ }
58
+ // Enable foreign keys
59
+ this.db.pragma('foreign_keys = ON');
60
+ }
61
+ /**
62
+ * Initialize the store (create tables, prepare statements).
63
+ */
64
+ async initialize() {
65
+ // Ensure directory exists
66
+ const dbDir = dirname(this.config.dbPath);
67
+ if (!existsSync(dbDir)) {
68
+ await mkdir(dbDir, { recursive: true });
69
+ }
70
+ // Apply embedded migrations (no file I/O needed)
71
+ const isDebug = process.env.DEBUG || process.argv.includes('--debug');
72
+ if (isDebug) {
73
+ const status = getMigrationStatus(this.db);
74
+ process.stderr.write(`[DEBUG] [SQLite] DB version: ${status.currentVersion}, latest: ${status.latestVersion}, pending: ${status.pendingCount}\n`);
75
+ }
76
+ const result = applyMigrations(this.db);
77
+ if (isDebug && result.applied > 0) {
78
+ process.stderr.write(`[DEBUG] [SQLite] Applied ${result.applied} migrations: ${result.appliedMigrations.join(', ')}\n`);
79
+ }
80
+ // Detect available features after migrations
81
+ this.features = detectFeatures(this.db);
82
+ if (isDebug) {
83
+ const enabled = Object.entries(this.features)
84
+ .filter(([, v]) => v)
85
+ .map(([k]) => k);
86
+ process.stderr.write(`[DEBUG] [SQLite] Features: ${enabled.join(', ')}\n`);
87
+ }
88
+ // Prepare statements for available features
89
+ this.prepareStatements();
90
+ }
91
+ /**
92
+ * Prepare SQL statements for reuse.
93
+ */
94
+ prepareStatements() {
95
+ this.stmts = {
96
+ insertSession: this.db.prepare(`
97
+ INSERT INTO sessions (id, name, created_at, last_active_at, message_count, token_count)
98
+ VALUES (@id, @name, @createdAt, @lastActiveAt, @messageCount, @tokenCount)
99
+ `),
100
+ updateSession: this.db.prepare(`
101
+ UPDATE sessions SET
102
+ name = COALESCE(@name, name),
103
+ last_active_at = COALESCE(@lastActiveAt, last_active_at),
104
+ message_count = COALESCE(@messageCount, message_count),
105
+ token_count = COALESCE(@tokenCount, token_count),
106
+ summary = COALESCE(@summary, summary)
107
+ WHERE id = @id
108
+ `),
109
+ deleteSession: this.db.prepare(`DELETE FROM sessions WHERE id = ?`),
110
+ getSession: this.db.prepare(`
111
+ SELECT id, name, created_at as createdAt,
112
+ COALESCE(last_active_at, created_at) as lastActiveAt,
113
+ message_count as messageCount, token_count as tokenCount, summary,
114
+ parent_session_id as parentSessionId, session_type as sessionType,
115
+ prompt_tokens as promptTokens, completion_tokens as completionTokens,
116
+ cost_usd as costUsd
117
+ FROM sessions WHERE id = ?
118
+ `),
119
+ listSessions: this.db.prepare(`
120
+ SELECT id, name, created_at as createdAt,
121
+ COALESCE(last_active_at, created_at) as lastActiveAt,
122
+ message_count as messageCount, token_count as tokenCount, summary,
123
+ parent_session_id as parentSessionId, session_type as sessionType,
124
+ prompt_tokens as promptTokens, completion_tokens as completionTokens,
125
+ cost_usd as costUsd
126
+ FROM sessions ORDER BY COALESCE(last_active_at, created_at) DESC
127
+ `),
128
+ insertEntry: this.db.prepare(`
129
+ INSERT INTO entries (session_id, timestamp, type, data)
130
+ VALUES (@sessionId, @timestamp, @type, @data)
131
+ `),
132
+ getEntries: this.db.prepare(`
133
+ SELECT timestamp, type, data FROM entries
134
+ WHERE session_id = ? ORDER BY id ASC
135
+ `),
136
+ insertToolCall: this.db.prepare(`
137
+ INSERT INTO tool_calls (id, session_id, name, arguments, status, created_at)
138
+ VALUES (@id, @sessionId, @name, @arguments, @status, @createdAt)
139
+ `),
140
+ updateToolCall: this.db.prepare(`
141
+ UPDATE tool_calls SET
142
+ status = @status,
143
+ result = @result,
144
+ error = @error,
145
+ duration_ms = @durationMs,
146
+ completed_at = @completedAt
147
+ WHERE id = @id
148
+ `),
149
+ insertCheckpoint: this.db.prepare(`
150
+ INSERT INTO checkpoints (id, session_id, state_json, created_at, description)
151
+ VALUES (@id, @sessionId, @stateJson, @createdAt, @description)
152
+ `),
153
+ getLatestCheckpoint: this.db.prepare(`
154
+ SELECT id, session_id as sessionId, state_json as stateJson,
155
+ created_at as createdAt, description
156
+ FROM checkpoints WHERE session_id = ?
157
+ ORDER BY created_at DESC, rowid DESC LIMIT 1
158
+ `),
159
+ // Cost tracking statements
160
+ insertUsageLog: this.db.prepare(`
161
+ INSERT INTO usage_logs (session_id, model_id, prompt_tokens, completion_tokens, cost_usd, timestamp)
162
+ VALUES (@sessionId, @modelId, @promptTokens, @completionTokens, @costUsd, @timestamp)
163
+ `),
164
+ updateSessionCosts: this.db.prepare(`
165
+ UPDATE sessions SET
166
+ prompt_tokens = COALESCE(prompt_tokens, 0) + @promptTokens,
167
+ completion_tokens = COALESCE(completion_tokens, 0) + @completionTokens,
168
+ cost_usd = COALESCE(cost_usd, 0) + @costUsd
169
+ WHERE id = @sessionId
170
+ `),
171
+ getSessionUsage: this.db.prepare(`
172
+ SELECT
173
+ COALESCE(SUM(prompt_tokens), 0) as promptTokens,
174
+ COALESCE(SUM(completion_tokens), 0) as completionTokens,
175
+ COALESCE(SUM(cost_usd), 0) as costUsd
176
+ FROM usage_logs WHERE session_id = ?
177
+ `),
178
+ // Session hierarchy statements
179
+ insertChildSession: this.db.prepare(`
180
+ INSERT INTO sessions (id, name, created_at, last_active_at, message_count, token_count, parent_session_id, session_type)
181
+ VALUES (@id, @name, @createdAt, @lastActiveAt, @messageCount, @tokenCount, @parentSessionId, @sessionType)
182
+ `),
183
+ getChildSessions: this.db.prepare(`
184
+ SELECT id, name, created_at as createdAt, last_active_at as lastActiveAt,
185
+ message_count as messageCount, token_count as tokenCount, summary,
186
+ parent_session_id as parentSessionId, session_type as sessionType,
187
+ prompt_tokens as promptTokens, completion_tokens as completionTokens,
188
+ cost_usd as costUsd
189
+ FROM sessions WHERE parent_session_id = ?
190
+ ORDER BY created_at ASC
191
+ `),
192
+ getSessionTree: this.db.prepare(`
193
+ WITH RECURSIVE session_tree AS (
194
+ SELECT id, name, created_at as createdAt, last_active_at as lastActiveAt,
195
+ message_count as messageCount, token_count as tokenCount, summary,
196
+ parent_session_id as parentSessionId, session_type as sessionType,
197
+ prompt_tokens as promptTokens, completion_tokens as completionTokens,
198
+ cost_usd as costUsd, 0 as depth
199
+ FROM sessions WHERE id = ?
200
+ UNION ALL
201
+ SELECT s.id, s.name, s.created_at, s.last_active_at,
202
+ s.message_count, s.token_count, s.summary,
203
+ s.parent_session_id, s.session_type,
204
+ s.prompt_tokens, s.completion_tokens,
205
+ s.cost_usd, st.depth + 1
206
+ FROM sessions s
207
+ INNER JOIN session_tree st ON s.parent_session_id = st.id
208
+ )
209
+ SELECT * FROM session_tree ORDER BY depth, createdAt ASC
210
+ `),
211
+ };
212
+ // Goal integrity statements - only prepare if feature is available
213
+ if (this.features.goals) {
214
+ this.stmts.insertGoal = this.db.prepare(`
215
+ INSERT INTO goals (id, session_id, goal_text, status, priority, parent_goal_id,
216
+ progress_current, progress_total, created_at, updated_at, metadata)
217
+ VALUES (@id, @sessionId, @goalText, @status, @priority, @parentGoalId,
218
+ @progressCurrent, @progressTotal, @createdAt, @updatedAt, @metadata)
219
+ `);
220
+ this.stmts.updateGoal = this.db.prepare(`
221
+ UPDATE goals SET
222
+ goal_text = COALESCE(@goalText, goal_text),
223
+ status = COALESCE(@status, status),
224
+ priority = COALESCE(@priority, priority),
225
+ progress_current = COALESCE(@progressCurrent, progress_current),
226
+ progress_total = COALESCE(@progressTotal, progress_total),
227
+ updated_at = @updatedAt,
228
+ completed_at = @completedAt,
229
+ metadata = COALESCE(@metadata, metadata)
230
+ WHERE id = @id
231
+ `);
232
+ this.stmts.getGoal = this.db.prepare(`
233
+ SELECT id, session_id as sessionId, goal_text as goalText, status, priority,
234
+ parent_goal_id as parentGoalId, progress_current as progressCurrent,
235
+ progress_total as progressTotal, created_at as createdAt,
236
+ updated_at as updatedAt, completed_at as completedAt, metadata
237
+ FROM goals WHERE id = ?
238
+ `);
239
+ this.stmts.listGoals = this.db.prepare(`
240
+ SELECT id, session_id as sessionId, goal_text as goalText, status, priority,
241
+ parent_goal_id as parentGoalId, progress_current as progressCurrent,
242
+ progress_total as progressTotal, created_at as createdAt,
243
+ updated_at as updatedAt, completed_at as completedAt, metadata
244
+ FROM goals WHERE session_id = ? ORDER BY priority ASC, created_at ASC
245
+ `);
246
+ this.stmts.listActiveGoals = this.db.prepare(`
247
+ SELECT id, session_id as sessionId, goal_text as goalText, status, priority,
248
+ parent_goal_id as parentGoalId, progress_current as progressCurrent,
249
+ progress_total as progressTotal, created_at as createdAt,
250
+ updated_at as updatedAt, completed_at as completedAt, metadata
251
+ FROM goals WHERE session_id = ? AND status = 'active'
252
+ ORDER BY priority ASC, created_at ASC
253
+ `);
254
+ this.stmts.insertJuncture = this.db.prepare(`
255
+ INSERT INTO junctures (session_id, goal_id, type, description, outcome,
256
+ importance, context, created_at)
257
+ VALUES (@sessionId, @goalId, @type, @description, @outcome,
258
+ @importance, @context, @createdAt)
259
+ `);
260
+ this.stmts.listJunctures = this.db.prepare(`
261
+ SELECT id, session_id as sessionId, goal_id as goalId, type, description,
262
+ outcome, importance, context, created_at as createdAt
263
+ FROM junctures WHERE session_id = ? ORDER BY created_at DESC
264
+ `);
265
+ }
266
+ // Worker result statements - only prepare if feature is available
267
+ if (this.features.workerResults) {
268
+ this.stmts.insertWorkerResult = this.db.prepare(`
269
+ INSERT INTO worker_results (id, session_id, worker_id, task_description, model_used,
270
+ status, summary, full_output, artifacts, metrics, error,
271
+ created_at, completed_at)
272
+ VALUES (@id, @sessionId, @workerId, @taskDescription, @modelUsed,
273
+ @status, @summary, @fullOutput, @artifacts, @metrics, @error,
274
+ @createdAt, @completedAt)
275
+ `);
276
+ this.stmts.updateWorkerResult = this.db.prepare(`
277
+ UPDATE worker_results SET
278
+ status = COALESCE(@status, status),
279
+ summary = COALESCE(@summary, summary),
280
+ full_output = COALESCE(@fullOutput, full_output),
281
+ artifacts = COALESCE(@artifacts, artifacts),
282
+ metrics = COALESCE(@metrics, metrics),
283
+ error = @error,
284
+ completed_at = @completedAt
285
+ WHERE id = @id
286
+ `);
287
+ this.stmts.getWorkerResult = this.db.prepare(`
288
+ SELECT id, session_id as sessionId, worker_id as workerId,
289
+ task_description as taskDescription, model_used as modelUsed,
290
+ status, summary, full_output as fullOutput, artifacts, metrics,
291
+ error, created_at as createdAt, completed_at as completedAt
292
+ FROM worker_results WHERE id = ?
293
+ `);
294
+ this.stmts.listWorkerResults = this.db.prepare(`
295
+ SELECT id, session_id as sessionId, worker_id as workerId,
296
+ task_description as taskDescription, model_used as modelUsed,
297
+ status, summary, full_output as fullOutput, artifacts, metrics,
298
+ error, created_at as createdAt, completed_at as completedAt
299
+ FROM worker_results WHERE session_id = ?
300
+ ORDER BY created_at DESC
301
+ `);
302
+ this.stmts.listPendingWorkerResults = this.db.prepare(`
303
+ SELECT id, session_id as sessionId, worker_id as workerId,
304
+ task_description as taskDescription, model_used as modelUsed,
305
+ status, summary, created_at as createdAt
306
+ FROM worker_results WHERE session_id = ? AND status = 'pending'
307
+ ORDER BY created_at ASC
308
+ `);
309
+ }
310
+ // Pending plan statements (optional - only if pendingPlans feature available)
311
+ if (this.features.pendingPlans) {
312
+ this.stmts.insertPendingPlan = this.db.prepare(`
313
+ INSERT INTO pending_plans (id, session_id, task, proposed_changes, exploration_summary, status, created_at, updated_at)
314
+ VALUES (@id, @sessionId, @task, @proposedChanges, @explorationSummary, @status, @createdAt, @updatedAt)
315
+ `);
316
+ this.stmts.updatePendingPlan = this.db.prepare(`
317
+ UPDATE pending_plans SET
318
+ proposed_changes = @proposedChanges,
319
+ exploration_summary = @explorationSummary,
320
+ status = @status,
321
+ updated_at = @updatedAt
322
+ WHERE id = @id
323
+ `);
324
+ this.stmts.getPendingPlan = this.db.prepare(`
325
+ SELECT id, session_id as sessionId, task, proposed_changes as proposedChanges,
326
+ exploration_summary as explorationSummary, status, created_at as createdAt, updated_at as updatedAt
327
+ FROM pending_plans WHERE session_id = ? AND status = 'pending'
328
+ ORDER BY created_at DESC LIMIT 1
329
+ `);
330
+ this.stmts.deletePendingPlan = this.db.prepare(`
331
+ DELETE FROM pending_plans WHERE id = ?
332
+ `);
333
+ }
334
+ }
335
+ // ===========================================================================
336
+ // SESSION MANAGEMENT (compatible with SessionStore interface)
337
+ // ===========================================================================
338
+ /**
339
+ * Create a new session.
340
+ */
341
+ createSession(name) {
342
+ const id = `session-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
343
+ const now = new Date().toISOString();
344
+ this.stmts.insertSession.run({
345
+ id,
346
+ name: name || null,
347
+ createdAt: now,
348
+ lastActiveAt: now,
349
+ messageCount: 0,
350
+ tokenCount: 0,
351
+ });
352
+ this.currentSessionId = id;
353
+ // Prune old sessions
354
+ this.pruneOldSessions();
355
+ this.emit({ type: 'session.created', sessionId: id });
356
+ return id;
357
+ }
358
+ /**
359
+ * Get current session ID.
360
+ */
361
+ getCurrentSessionId() {
362
+ return this.currentSessionId;
363
+ }
364
+ /**
365
+ * Set current session ID.
366
+ */
367
+ setCurrentSessionId(sessionId) {
368
+ this.currentSessionId = sessionId;
369
+ }
370
+ /**
371
+ * Append an entry to the current session.
372
+ */
373
+ appendEntry(entry) {
374
+ if (!this.currentSessionId) {
375
+ this.createSession();
376
+ }
377
+ const timestamp = new Date().toISOString();
378
+ this.stmts.insertEntry.run({
379
+ sessionId: this.currentSessionId,
380
+ timestamp,
381
+ type: entry.type,
382
+ data: JSON.stringify(entry.data),
383
+ });
384
+ // Update session metadata
385
+ if (entry.type === 'message') {
386
+ const msg = entry.data;
387
+ // Set summary from first user message (for session picker display)
388
+ if (msg.role === 'user' && typeof msg.content === 'string') {
389
+ // Only set if no summary yet
390
+ const session = this.stmts.getSession.get(this.currentSessionId);
391
+ if (session && !session.summary) {
392
+ // Extract first line or first ~50 chars as summary
393
+ const firstLine = msg.content.split('\n')[0].trim();
394
+ const summary = firstLine.length > 60 ? firstLine.slice(0, 57) + '...' : firstLine;
395
+ this.db.prepare(`UPDATE sessions SET summary = ? WHERE id = ?`).run(summary, this.currentSessionId);
396
+ }
397
+ }
398
+ this.db.prepare(`
399
+ UPDATE sessions SET
400
+ last_active_at = ?,
401
+ message_count = message_count + 1
402
+ WHERE id = ?
403
+ `).run(timestamp, this.currentSessionId);
404
+ }
405
+ else {
406
+ this.db.prepare(`
407
+ UPDATE sessions SET last_active_at = ? WHERE id = ?
408
+ `).run(timestamp, this.currentSessionId);
409
+ }
410
+ this.emit({ type: 'entry.appended', sessionId: this.currentSessionId, entryType: entry.type });
411
+ }
412
+ /**
413
+ * Append a message to the current session.
414
+ */
415
+ appendMessage(message) {
416
+ this.appendEntry({ type: 'message', data: message });
417
+ }
418
+ /**
419
+ * Append a tool call to the current session.
420
+ */
421
+ appendToolCall(toolCall) {
422
+ this.appendEntry({ type: 'tool_call', data: toolCall });
423
+ // Also insert into tool_calls table for fast lookup
424
+ if ('id' in toolCall && toolCall.id) {
425
+ this.stmts.insertToolCall.run({
426
+ id: toolCall.id,
427
+ sessionId: this.currentSessionId,
428
+ name: toolCall.name,
429
+ arguments: JSON.stringify(toolCall.arguments),
430
+ status: 'pending',
431
+ createdAt: new Date().toISOString(),
432
+ });
433
+ }
434
+ }
435
+ /**
436
+ * Append a tool result to the current session.
437
+ */
438
+ appendToolResult(callId, result) {
439
+ this.appendEntry({ type: 'tool_result', data: { callId, result } });
440
+ // Update tool_calls table
441
+ this.stmts.updateToolCall.run({
442
+ id: callId,
443
+ status: 'success',
444
+ result: JSON.stringify(result),
445
+ error: null,
446
+ durationMs: null,
447
+ completedAt: new Date().toISOString(),
448
+ });
449
+ }
450
+ /**
451
+ * Append a compaction summary.
452
+ */
453
+ appendCompaction(summary, compactedCount) {
454
+ this.appendEntry({
455
+ type: 'compaction',
456
+ data: { summary, compactedCount, compactedAt: new Date().toISOString() },
457
+ });
458
+ }
459
+ /**
460
+ * Load a session by ID.
461
+ */
462
+ loadSession(sessionId) {
463
+ const rows = this.stmts.getEntries.all(sessionId);
464
+ const entries = rows.map(row => ({
465
+ timestamp: row.timestamp,
466
+ type: row.type,
467
+ data: JSON.parse(row.data),
468
+ }));
469
+ this.currentSessionId = sessionId;
470
+ this.emit({ type: 'session.loaded', sessionId, entryCount: entries.length });
471
+ return entries;
472
+ }
473
+ /**
474
+ * Reconstruct messages from session entries.
475
+ */
476
+ loadSessionMessages(sessionId) {
477
+ const entries = this.loadSession(sessionId);
478
+ const messages = [];
479
+ for (const entry of entries) {
480
+ if (entry.type === 'message') {
481
+ messages.push(entry.data);
482
+ }
483
+ else if (entry.type === 'compaction') {
484
+ const compaction = entry.data;
485
+ messages.push({
486
+ role: 'system',
487
+ content: `[Previous conversation summary]\n${compaction.summary}`,
488
+ });
489
+ }
490
+ }
491
+ return messages;
492
+ }
493
+ /**
494
+ * Delete a session.
495
+ */
496
+ deleteSession(sessionId) {
497
+ this.stmts.deleteSession.run(sessionId);
498
+ if (this.currentSessionId === sessionId) {
499
+ this.currentSessionId = null;
500
+ }
501
+ this.emit({ type: 'session.deleted', sessionId });
502
+ }
503
+ /**
504
+ * List all sessions.
505
+ */
506
+ listSessions() {
507
+ return this.stmts.listSessions.all();
508
+ }
509
+ /**
510
+ * Get the most recent session.
511
+ */
512
+ getRecentSession() {
513
+ const sessions = this.listSessions();
514
+ return sessions[0] || null;
515
+ }
516
+ /**
517
+ * Get session metadata by ID.
518
+ */
519
+ getSessionMetadata(sessionId) {
520
+ return this.stmts.getSession.get(sessionId);
521
+ }
522
+ /**
523
+ * Update session metadata.
524
+ */
525
+ updateSessionMetadata(sessionId, updates) {
526
+ this.stmts.updateSession.run({
527
+ id: sessionId,
528
+ name: updates.name || null,
529
+ lastActiveAt: null,
530
+ messageCount: null,
531
+ tokenCount: updates.tokenCount || null,
532
+ summary: updates.summary || null,
533
+ });
534
+ }
535
+ // ===========================================================================
536
+ // SQLITE-SPECIFIC FEATURES
537
+ // ===========================================================================
538
+ /**
539
+ * Save a checkpoint for state restoration.
540
+ */
541
+ saveCheckpoint(state, description) {
542
+ if (!this.currentSessionId) {
543
+ this.createSession();
544
+ }
545
+ const id = `ckpt-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
546
+ this.stmts.insertCheckpoint.run({
547
+ id,
548
+ sessionId: this.currentSessionId,
549
+ stateJson: JSON.stringify(state),
550
+ createdAt: new Date().toISOString(),
551
+ description: description || null,
552
+ });
553
+ return id;
554
+ }
555
+ /**
556
+ * Load the latest checkpoint for a session.
557
+ */
558
+ loadLatestCheckpoint(sessionId) {
559
+ const row = this.stmts.getLatestCheckpoint.get(sessionId);
560
+ if (!row)
561
+ return null;
562
+ return {
563
+ id: row.id,
564
+ state: JSON.parse(row.stateJson),
565
+ createdAt: row.createdAt,
566
+ description: row.description ?? undefined,
567
+ };
568
+ }
569
+ /**
570
+ * Query entries with SQL (advanced usage).
571
+ */
572
+ query(sql, params = []) {
573
+ return this.db.prepare(sql).all(...params);
574
+ }
575
+ /**
576
+ * Get database statistics.
577
+ */
578
+ getStats() {
579
+ const sessionCount = this.db.prepare('SELECT COUNT(*) as count FROM sessions').get().count;
580
+ const entryCount = this.db.prepare('SELECT COUNT(*) as count FROM entries').get().count;
581
+ const toolCallCount = this.db.prepare('SELECT COUNT(*) as count FROM tool_calls').get().count;
582
+ const checkpointCount = this.db.prepare('SELECT COUNT(*) as count FROM checkpoints').get().count;
583
+ const dbSizeBytes = this.db.prepare('SELECT page_count * page_size as size FROM pragma_page_count(), pragma_page_size()').get().size;
584
+ return { sessionCount, entryCount, toolCallCount, checkpointCount, dbSizeBytes };
585
+ }
586
+ // ===========================================================================
587
+ // COST TRACKING
588
+ // ===========================================================================
589
+ /**
590
+ * Log API usage for cost tracking.
591
+ * Inserts a usage log entry and updates session totals atomically.
592
+ */
593
+ logUsage(usage) {
594
+ // Use transaction to ensure atomicity of insert + update
595
+ this.db.transaction(() => {
596
+ // Insert into usage_logs table
597
+ this.stmts.insertUsageLog.run({
598
+ sessionId: usage.sessionId,
599
+ modelId: usage.modelId,
600
+ promptTokens: usage.promptTokens,
601
+ completionTokens: usage.completionTokens,
602
+ costUsd: usage.costUsd,
603
+ timestamp: usage.timestamp,
604
+ });
605
+ // Update session totals
606
+ this.stmts.updateSessionCosts.run({
607
+ sessionId: usage.sessionId,
608
+ promptTokens: usage.promptTokens,
609
+ completionTokens: usage.completionTokens,
610
+ costUsd: usage.costUsd,
611
+ });
612
+ })();
613
+ }
614
+ /**
615
+ * Get aggregated usage for a session.
616
+ */
617
+ getSessionUsage(sessionId) {
618
+ const result = this.stmts.getSessionUsage.get(sessionId);
619
+ return result || { promptTokens: 0, completionTokens: 0, costUsd: 0 };
620
+ }
621
+ // ===========================================================================
622
+ // SESSION HIERARCHY
623
+ // ===========================================================================
624
+ /**
625
+ * Create a child session linked to a parent.
626
+ */
627
+ createChildSession(parentId, name, type = 'subagent') {
628
+ const id = `session-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
629
+ const now = new Date().toISOString();
630
+ this.stmts.insertChildSession.run({
631
+ id,
632
+ name: name || null,
633
+ createdAt: now,
634
+ lastActiveAt: now,
635
+ messageCount: 0,
636
+ tokenCount: 0,
637
+ parentSessionId: parentId,
638
+ sessionType: type,
639
+ });
640
+ this.emit({ type: 'session.created', sessionId: id });
641
+ return id;
642
+ }
643
+ /**
644
+ * Get all direct child sessions of a parent.
645
+ */
646
+ getChildSessions(parentId) {
647
+ return this.stmts.getChildSessions.all(parentId);
648
+ }
649
+ /**
650
+ * Get the full session tree starting from a root session.
651
+ * Uses a recursive CTE to traverse the hierarchy.
652
+ */
653
+ getSessionTree(rootId) {
654
+ const rows = this.stmts.getSessionTree.all(rootId);
655
+ // Remove the depth field from results (used only for ordering)
656
+ return rows.map(({ depth, ...session }) => session);
657
+ }
658
+ // ===========================================================================
659
+ // GOAL INTEGRITY
660
+ // ===========================================================================
661
+ /**
662
+ * Check if goals feature is available.
663
+ */
664
+ hasGoalsFeature() {
665
+ return this.features.goals;
666
+ }
667
+ /**
668
+ * Create a new goal for the current session.
669
+ * Goals persist outside of context and survive compaction.
670
+ * Returns undefined if goals feature is not available.
671
+ */
672
+ createGoal(goalText, options = {}) {
673
+ if (!this.features.goals || !this.stmts.insertGoal) {
674
+ return undefined;
675
+ }
676
+ if (!this.currentSessionId) {
677
+ this.createSession();
678
+ }
679
+ const id = `goal-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
680
+ const now = new Date().toISOString();
681
+ this.stmts.insertGoal.run({
682
+ id,
683
+ sessionId: this.currentSessionId,
684
+ goalText,
685
+ status: 'active',
686
+ priority: options.priority ?? 2,
687
+ parentGoalId: options.parentGoalId ?? null,
688
+ progressCurrent: 0,
689
+ progressTotal: options.progressTotal ?? null,
690
+ createdAt: now,
691
+ updatedAt: now,
692
+ metadata: options.metadata ? JSON.stringify(options.metadata) : null,
693
+ });
694
+ return id;
695
+ }
696
+ /**
697
+ * Update a goal's status or progress.
698
+ */
699
+ updateGoal(goalId, updates) {
700
+ if (!this.features.goals || !this.stmts.updateGoal) {
701
+ return;
702
+ }
703
+ const now = new Date().toISOString();
704
+ this.stmts.updateGoal.run({
705
+ id: goalId,
706
+ goalText: updates.goalText ?? null,
707
+ status: updates.status ?? null,
708
+ priority: updates.priority ?? null,
709
+ progressCurrent: updates.progressCurrent ?? null,
710
+ progressTotal: updates.progressTotal ?? null,
711
+ updatedAt: now,
712
+ completedAt: updates.status === 'completed' || updates.status === 'abandoned' ? now : null,
713
+ metadata: updates.metadata ? JSON.stringify(updates.metadata) : null,
714
+ });
715
+ }
716
+ /**
717
+ * Mark a goal as completed.
718
+ */
719
+ completeGoal(goalId) {
720
+ this.updateGoal(goalId, { status: 'completed' });
721
+ }
722
+ /**
723
+ * Get a goal by ID.
724
+ */
725
+ getGoal(goalId) {
726
+ if (!this.features.goals || !this.stmts.getGoal) {
727
+ return undefined;
728
+ }
729
+ return this.stmts.getGoal.get(goalId);
730
+ }
731
+ /**
732
+ * List all goals for a session.
733
+ */
734
+ listGoals(sessionId) {
735
+ if (!this.features.goals || !this.stmts.listGoals) {
736
+ return [];
737
+ }
738
+ const sid = sessionId ?? this.currentSessionId;
739
+ if (!sid)
740
+ return [];
741
+ return this.stmts.listGoals.all(sid);
742
+ }
743
+ /**
744
+ * List active goals for a session.
745
+ */
746
+ listActiveGoals(sessionId) {
747
+ if (!this.features.goals || !this.stmts.listActiveGoals) {
748
+ return [];
749
+ }
750
+ const sid = sessionId ?? this.currentSessionId;
751
+ if (!sid)
752
+ return [];
753
+ return this.stmts.listActiveGoals.all(sid);
754
+ }
755
+ /**
756
+ * Get a summary of the current goals for context injection.
757
+ * This is what gets recited to maintain goal awareness.
758
+ */
759
+ getGoalsSummary(sessionId) {
760
+ if (!this.features.goals) {
761
+ return 'Goals feature not available.';
762
+ }
763
+ const goals = this.listActiveGoals(sessionId);
764
+ if (goals.length === 0) {
765
+ return 'No active goals.';
766
+ }
767
+ const lines = ['Active Goals:'];
768
+ for (const goal of goals) {
769
+ const progress = goal.progressTotal
770
+ ? ` (${goal.progressCurrent}/${goal.progressTotal})`
771
+ : '';
772
+ const priority = goal.priority === 1 ? ' [HIGH]' : goal.priority === 3 ? ' [low]' : '';
773
+ lines.push(`• ${goal.goalText}${progress}${priority}`);
774
+ }
775
+ return lines.join('\n');
776
+ }
777
+ /**
778
+ * Log a critical juncture (decision, failure, breakthrough, pivot).
779
+ * Returns -1 if goals feature is not available.
780
+ */
781
+ logJuncture(type, description, options = {}) {
782
+ if (!this.features.goals || !this.stmts.insertJuncture) {
783
+ return -1;
784
+ }
785
+ if (!this.currentSessionId) {
786
+ this.createSession();
787
+ }
788
+ const result = this.stmts.insertJuncture.run({
789
+ sessionId: this.currentSessionId,
790
+ goalId: options.goalId ?? null,
791
+ type,
792
+ description,
793
+ outcome: options.outcome ?? null,
794
+ importance: options.importance ?? 2,
795
+ context: options.context ? JSON.stringify(options.context) : null,
796
+ createdAt: new Date().toISOString(),
797
+ });
798
+ return Number(result.lastInsertRowid);
799
+ }
800
+ /**
801
+ * List junctures for a session.
802
+ */
803
+ listJunctures(sessionId, limit) {
804
+ if (!this.features.goals || !this.stmts.listJunctures) {
805
+ return [];
806
+ }
807
+ const sid = sessionId ?? this.currentSessionId;
808
+ if (!sid)
809
+ return [];
810
+ const junctures = this.stmts.listJunctures.all(sid);
811
+ return limit ? junctures.slice(0, limit) : junctures;
812
+ }
813
+ /**
814
+ * Get recent critical junctures for context.
815
+ */
816
+ getJuncturesSummary(sessionId, limit = 5) {
817
+ if (!this.features.goals) {
818
+ return '';
819
+ }
820
+ const junctures = this.listJunctures(sessionId, limit);
821
+ if (junctures.length === 0) {
822
+ return '';
823
+ }
824
+ const lines = ['Recent Key Moments:'];
825
+ for (const j of junctures) {
826
+ const icon = j.type === 'failure' ? '✗' : j.type === 'breakthrough' ? '★' :
827
+ j.type === 'decision' ? '→' : '↻';
828
+ lines.push(`${icon} [${j.type}] ${j.description}`);
829
+ if (j.outcome) {
830
+ lines.push(` └─ ${j.outcome}`);
831
+ }
832
+ }
833
+ return lines.join('\n');
834
+ }
835
+ // ===========================================================================
836
+ // WORKER RESULTS
837
+ // ===========================================================================
838
+ /**
839
+ * Check if worker results feature is available.
840
+ */
841
+ hasWorkerResultsFeature() {
842
+ return this.features.workerResults;
843
+ }
844
+ /**
845
+ * Create a pending worker result entry.
846
+ * Call this when spawning a worker to reserve the result slot.
847
+ * Returns the result ID for later reference.
848
+ */
849
+ createWorkerResult(workerId, taskDescription, modelUsed) {
850
+ if (!this.features.workerResults || !this.stmts.insertWorkerResult) {
851
+ return undefined;
852
+ }
853
+ if (!this.currentSessionId) {
854
+ this.createSession();
855
+ }
856
+ const id = `wr-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
857
+ const now = new Date().toISOString();
858
+ this.stmts.insertWorkerResult.run({
859
+ id,
860
+ sessionId: this.currentSessionId,
861
+ workerId,
862
+ taskDescription,
863
+ modelUsed: modelUsed ?? null,
864
+ status: 'pending',
865
+ summary: null,
866
+ fullOutput: null,
867
+ artifacts: null,
868
+ metrics: null,
869
+ error: null,
870
+ createdAt: now,
871
+ completedAt: null,
872
+ });
873
+ return id;
874
+ }
875
+ /**
876
+ * Complete a worker result with output.
877
+ * Stores full output in database, generates summary for context injection.
878
+ */
879
+ completeWorkerResult(resultId, output) {
880
+ if (!this.features.workerResults || !this.stmts.updateWorkerResult) {
881
+ return undefined;
882
+ }
883
+ const now = new Date().toISOString();
884
+ // Auto-generate summary if not provided (first 200 chars)
885
+ const summary = output.summary ?? this.generateResultSummary(output.fullOutput);
886
+ this.stmts.updateWorkerResult.run({
887
+ id: resultId,
888
+ status: 'success',
889
+ summary,
890
+ fullOutput: output.fullOutput,
891
+ artifacts: output.artifacts ? JSON.stringify(output.artifacts) : null,
892
+ metrics: output.metrics ? JSON.stringify(output.metrics) : null,
893
+ error: null,
894
+ completedAt: now,
895
+ });
896
+ // Return reference for context injection
897
+ const result = this.getWorkerResult(resultId);
898
+ return result ? this.toResultRef(result) : undefined;
899
+ }
900
+ /**
901
+ * Mark a worker result as failed.
902
+ */
903
+ failWorkerResult(resultId, error) {
904
+ if (!this.features.workerResults || !this.stmts.updateWorkerResult) {
905
+ return;
906
+ }
907
+ this.stmts.updateWorkerResult.run({
908
+ id: resultId,
909
+ status: 'error',
910
+ summary: `Failed: ${error.slice(0, 100)}`,
911
+ fullOutput: null,
912
+ artifacts: null,
913
+ metrics: null,
914
+ error,
915
+ completedAt: new Date().toISOString(),
916
+ });
917
+ }
918
+ /**
919
+ * Get a worker result by ID (includes full output).
920
+ */
921
+ getWorkerResult(resultId) {
922
+ if (!this.features.workerResults || !this.stmts.getWorkerResult) {
923
+ return undefined;
924
+ }
925
+ return this.stmts.getWorkerResult.get(resultId);
926
+ }
927
+ /**
928
+ * Get a lightweight reference to a worker result (for context injection).
929
+ * Does NOT include full output - that stays in database.
930
+ */
931
+ getWorkerResultRef(resultId) {
932
+ const result = this.getWorkerResult(resultId);
933
+ return result ? this.toResultRef(result) : undefined;
934
+ }
935
+ /**
936
+ * List all worker results for a session.
937
+ */
938
+ listWorkerResults(sessionId) {
939
+ if (!this.features.workerResults || !this.stmts.listWorkerResults) {
940
+ return [];
941
+ }
942
+ const sid = sessionId ?? this.currentSessionId;
943
+ if (!sid)
944
+ return [];
945
+ return this.stmts.listWorkerResults.all(sid);
946
+ }
947
+ /**
948
+ * List pending worker results (workers still running).
949
+ */
950
+ listPendingWorkerResults(sessionId) {
951
+ if (!this.features.workerResults || !this.stmts.listPendingWorkerResults) {
952
+ return [];
953
+ }
954
+ const sid = sessionId ?? this.currentSessionId;
955
+ if (!sid)
956
+ return [];
957
+ return this.stmts.listPendingWorkerResults.all(sid);
958
+ }
959
+ /**
960
+ * Get a summary of worker results for context injection.
961
+ * Returns lightweight references, not full outputs.
962
+ */
963
+ getWorkerResultsSummary(sessionId) {
964
+ if (!this.features.workerResults) {
965
+ return '';
966
+ }
967
+ const results = this.listWorkerResults(sessionId);
968
+ if (results.length === 0) {
969
+ return '';
970
+ }
971
+ const lines = ['Worker Results:'];
972
+ for (const r of results.slice(0, 10)) {
973
+ const status = r.status === 'success' ? '✓' : r.status === 'error' ? '✗' : '⏳';
974
+ const task = r.taskDescription.length > 50
975
+ ? r.taskDescription.slice(0, 47) + '...'
976
+ : r.taskDescription;
977
+ lines.push(`${status} [${r.id}] ${task}`);
978
+ if (r.summary) {
979
+ lines.push(` └─ ${r.summary}`);
980
+ }
981
+ }
982
+ if (results.length > 10) {
983
+ lines.push(` ... and ${results.length - 10} more`);
984
+ }
985
+ return lines.join('\n');
986
+ }
987
+ /**
988
+ * Convert a full WorkerResult to a lightweight reference.
989
+ */
990
+ toResultRef(result) {
991
+ return {
992
+ id: result.id,
993
+ workerId: result.workerId,
994
+ taskDescription: result.taskDescription,
995
+ status: result.status,
996
+ summary: result.summary,
997
+ modelUsed: result.modelUsed,
998
+ retrievalHint: `Full output available: store.getWorkerResult('${result.id}')`,
999
+ };
1000
+ }
1001
+ /**
1002
+ * Generate a brief summary from full output.
1003
+ */
1004
+ generateResultSummary(fullOutput) {
1005
+ const firstLine = fullOutput.split('\n')[0].trim();
1006
+ if (firstLine.length <= 150) {
1007
+ return firstLine;
1008
+ }
1009
+ return firstLine.slice(0, 147) + '...';
1010
+ }
1011
+ // ===========================================================================
1012
+ // PENDING PLANS (Plan Mode Support)
1013
+ // ===========================================================================
1014
+ /**
1015
+ * Check if pending plans feature is available.
1016
+ */
1017
+ hasPendingPlansFeature() {
1018
+ return this.features.pendingPlans;
1019
+ }
1020
+ /**
1021
+ * Save a pending plan to the database.
1022
+ */
1023
+ savePendingPlan(plan, sessionId) {
1024
+ if (!this.features.pendingPlans || !this.stmts.insertPendingPlan) {
1025
+ return;
1026
+ }
1027
+ const sid = sessionId ?? this.currentSessionId;
1028
+ if (!sid)
1029
+ return;
1030
+ const now = new Date().toISOString();
1031
+ // Check if plan already exists
1032
+ const existing = this.getPendingPlan(sid);
1033
+ if (existing && existing.id === plan.id) {
1034
+ // Update existing plan
1035
+ this.stmts.updatePendingPlan?.run({
1036
+ id: plan.id,
1037
+ proposedChanges: JSON.stringify(plan.proposedChanges),
1038
+ explorationSummary: plan.explorationSummary || null,
1039
+ status: plan.status,
1040
+ updatedAt: now,
1041
+ });
1042
+ }
1043
+ else {
1044
+ // Delete any existing pending plan first
1045
+ if (existing) {
1046
+ this.stmts.deletePendingPlan?.run(existing.id);
1047
+ }
1048
+ // Insert new plan
1049
+ this.stmts.insertPendingPlan.run({
1050
+ id: plan.id,
1051
+ sessionId: sid,
1052
+ task: plan.task,
1053
+ proposedChanges: JSON.stringify(plan.proposedChanges),
1054
+ explorationSummary: plan.explorationSummary || null,
1055
+ status: plan.status,
1056
+ createdAt: plan.createdAt,
1057
+ updatedAt: now,
1058
+ });
1059
+ }
1060
+ }
1061
+ /**
1062
+ * Get the pending plan for a session.
1063
+ * Returns the most recent pending plan, or null if none.
1064
+ */
1065
+ getPendingPlan(sessionId) {
1066
+ if (!this.features.pendingPlans || !this.stmts.getPendingPlan) {
1067
+ return null;
1068
+ }
1069
+ const sid = sessionId ?? this.currentSessionId;
1070
+ if (!sid)
1071
+ return null;
1072
+ const row = this.stmts.getPendingPlan.get(sid);
1073
+ if (!row)
1074
+ return null;
1075
+ return {
1076
+ id: row.id,
1077
+ task: row.task,
1078
+ createdAt: row.createdAt,
1079
+ updatedAt: row.updatedAt,
1080
+ proposedChanges: JSON.parse(row.proposedChanges),
1081
+ explorationSummary: row.explorationSummary || '',
1082
+ status: row.status,
1083
+ sessionId: row.sessionId,
1084
+ };
1085
+ }
1086
+ /**
1087
+ * Update the status of a pending plan.
1088
+ */
1089
+ updatePlanStatus(planId, status) {
1090
+ if (!this.features.pendingPlans || !this.stmts.updatePendingPlan) {
1091
+ return;
1092
+ }
1093
+ const now = new Date().toISOString();
1094
+ // We need to get the plan first to preserve other fields
1095
+ // Since we update by id, we do a direct update here
1096
+ this.db.prepare(`
1097
+ UPDATE pending_plans SET status = ?, updated_at = ? WHERE id = ?
1098
+ `).run(status, now, planId);
1099
+ }
1100
+ /**
1101
+ * Delete a pending plan.
1102
+ */
1103
+ deletePendingPlan(planId) {
1104
+ if (!this.features.pendingPlans || !this.stmts.deletePendingPlan) {
1105
+ return;
1106
+ }
1107
+ this.stmts.deletePendingPlan.run(planId);
1108
+ }
1109
+ // ===========================================================================
1110
+ // SESSION MANIFEST (Handoff Support)
1111
+ // ===========================================================================
1112
+ /**
1113
+ * Export a complete session manifest for handoff.
1114
+ * Contains all information needed for another agent/human to pick up the work.
1115
+ */
1116
+ exportSessionManifest(sessionId) {
1117
+ const sid = sessionId ?? this.currentSessionId;
1118
+ if (!sid)
1119
+ return undefined;
1120
+ const session = this.getSessionMetadata(sid);
1121
+ if (!session)
1122
+ return undefined;
1123
+ // Collect all session state
1124
+ const goals = this.listGoals(sid);
1125
+ const activeGoals = goals.filter(g => g.status === 'active');
1126
+ const completedGoals = goals.filter(g => g.status === 'completed');
1127
+ const junctures = this.listJunctures(sid, 20);
1128
+ const workerResults = this.listWorkerResults(sid);
1129
+ const entries = this.loadSession(sid);
1130
+ // Count message types from entries
1131
+ let messageCount = entries.filter(e => e.type === 'message').length;
1132
+ const toolCallCount = entries.filter(e => e.type === 'tool_call').length;
1133
+ const compactionCount = entries.filter(e => e.type === 'compaction').length;
1134
+ // If no messages in entries, check the latest checkpoint (where messages are actually stored)
1135
+ if (messageCount === 0) {
1136
+ const checkpoint = this.loadLatestCheckpoint(sid);
1137
+ if (checkpoint?.state?.messages && Array.isArray(checkpoint.state.messages)) {
1138
+ messageCount = checkpoint.state.messages.length;
1139
+ }
1140
+ }
1141
+ return {
1142
+ version: '1.0',
1143
+ exportedAt: new Date().toISOString(),
1144
+ session: {
1145
+ id: session.id,
1146
+ name: session.name,
1147
+ createdAt: session.createdAt,
1148
+ lastActiveAt: session.lastActiveAt,
1149
+ summary: session.summary,
1150
+ },
1151
+ state: {
1152
+ messageCount,
1153
+ toolCallCount,
1154
+ compactionCount,
1155
+ tokenCount: session.tokenCount,
1156
+ costUsd: session.costUsd,
1157
+ },
1158
+ goals: {
1159
+ active: activeGoals.map(g => ({
1160
+ id: g.id,
1161
+ text: g.goalText,
1162
+ priority: g.priority,
1163
+ progress: g.progressTotal
1164
+ ? `${g.progressCurrent}/${g.progressTotal}`
1165
+ : undefined,
1166
+ })),
1167
+ completed: completedGoals.map(g => ({
1168
+ id: g.id,
1169
+ text: g.goalText,
1170
+ completedAt: g.completedAt,
1171
+ })),
1172
+ },
1173
+ keyMoments: junctures.map(j => ({
1174
+ type: j.type,
1175
+ description: j.description,
1176
+ outcome: j.outcome,
1177
+ createdAt: j.createdAt,
1178
+ })),
1179
+ workerResults: workerResults.map(r => ({
1180
+ id: r.id,
1181
+ task: r.taskDescription,
1182
+ status: r.status,
1183
+ summary: r.summary,
1184
+ model: r.modelUsed,
1185
+ })),
1186
+ resumption: {
1187
+ currentSessionId: sid,
1188
+ canResume: true,
1189
+ hint: 'Load this session with /load ' + sid.slice(-8),
1190
+ },
1191
+ };
1192
+ }
1193
+ /**
1194
+ * Export session as human-readable markdown.
1195
+ * Suitable for printing, sharing, or reviewing offline.
1196
+ */
1197
+ exportSessionMarkdown(sessionId) {
1198
+ const manifest = this.exportSessionManifest(sessionId);
1199
+ if (!manifest)
1200
+ return '# Session Not Found\n';
1201
+ const lines = [];
1202
+ // Header
1203
+ lines.push(`# Session Handoff: ${manifest.session.name || manifest.session.id}`);
1204
+ lines.push('');
1205
+ lines.push(`> Exported: ${manifest.exportedAt}`);
1206
+ lines.push(`> Session ID: \`${manifest.session.id}\``);
1207
+ lines.push('');
1208
+ // Summary
1209
+ if (manifest.session.summary) {
1210
+ lines.push('## Summary');
1211
+ lines.push('');
1212
+ lines.push(manifest.session.summary);
1213
+ lines.push('');
1214
+ }
1215
+ // State
1216
+ lines.push('## Session State');
1217
+ lines.push('');
1218
+ lines.push(`- Messages: ${manifest.state.messageCount}`);
1219
+ lines.push(`- Tool Calls: ${manifest.state.toolCallCount}`);
1220
+ lines.push(`- Compactions: ${manifest.state.compactionCount}`);
1221
+ lines.push(`- Tokens: ${manifest.state.tokenCount?.toLocaleString() ?? 'N/A'}`);
1222
+ if (manifest.state.costUsd) {
1223
+ lines.push(`- Cost: $${manifest.state.costUsd.toFixed(4)}`);
1224
+ }
1225
+ lines.push('');
1226
+ // Active Goals
1227
+ if (manifest.goals.active.length > 0) {
1228
+ lines.push('## Active Goals');
1229
+ lines.push('');
1230
+ for (const goal of manifest.goals.active) {
1231
+ const priority = goal.priority === 1 ? ' **[HIGH]**' : goal.priority === 3 ? ' [low]' : '';
1232
+ const progress = goal.progress ? ` (${goal.progress})` : '';
1233
+ lines.push(`- [ ] ${goal.text}${progress}${priority}`);
1234
+ }
1235
+ lines.push('');
1236
+ }
1237
+ // Completed Goals
1238
+ if (manifest.goals.completed.length > 0) {
1239
+ lines.push('## Completed Goals');
1240
+ lines.push('');
1241
+ for (const goal of manifest.goals.completed) {
1242
+ lines.push(`- [x] ${goal.text}`);
1243
+ }
1244
+ lines.push('');
1245
+ }
1246
+ // Key Moments
1247
+ if (manifest.keyMoments.length > 0) {
1248
+ lines.push('## Key Moments');
1249
+ lines.push('');
1250
+ for (const moment of manifest.keyMoments) {
1251
+ const icon = moment.type === 'failure' ? '❌' :
1252
+ moment.type === 'breakthrough' ? '⭐' :
1253
+ moment.type === 'decision' ? '→' : '↻';
1254
+ lines.push(`### ${icon} ${moment.type.charAt(0).toUpperCase() + moment.type.slice(1)}`);
1255
+ lines.push('');
1256
+ lines.push(moment.description);
1257
+ if (moment.outcome) {
1258
+ lines.push('');
1259
+ lines.push(`**Outcome:** ${moment.outcome}`);
1260
+ }
1261
+ lines.push('');
1262
+ }
1263
+ }
1264
+ // Worker Results
1265
+ if (manifest.workerResults.length > 0) {
1266
+ lines.push('## Worker Results');
1267
+ lines.push('');
1268
+ for (const result of manifest.workerResults) {
1269
+ const status = result.status === 'success' ? '✅' :
1270
+ result.status === 'error' ? '❌' : '⏳';
1271
+ lines.push(`- ${status} **${result.task}**`);
1272
+ if (result.summary) {
1273
+ lines.push(` - ${result.summary}`);
1274
+ }
1275
+ if (result.model) {
1276
+ lines.push(` - Model: ${result.model}`);
1277
+ }
1278
+ }
1279
+ lines.push('');
1280
+ }
1281
+ // Resumption
1282
+ lines.push('## How to Resume');
1283
+ lines.push('');
1284
+ lines.push('```bash');
1285
+ lines.push(`attocode --load ${manifest.resumption.currentSessionId}`);
1286
+ lines.push('```');
1287
+ lines.push('');
1288
+ lines.push('Or within attocode:');
1289
+ lines.push('```');
1290
+ lines.push(manifest.resumption.hint);
1291
+ lines.push('```');
1292
+ return lines.join('\n');
1293
+ }
1294
+ // ===========================================================================
1295
+ // MIGRATION
1296
+ // ===========================================================================
1297
+ /**
1298
+ * Migrate sessions from JSONL format to SQLite.
1299
+ */
1300
+ async migrateFromJSONL(jsonlDir) {
1301
+ const indexPath = join(jsonlDir, 'index.json');
1302
+ let migrated = 0;
1303
+ let failed = 0;
1304
+ if (!existsSync(indexPath)) {
1305
+ return { migrated: 0, failed: 0 };
1306
+ }
1307
+ try {
1308
+ const indexContent = readFileSync(indexPath, 'utf-8');
1309
+ const index = JSON.parse(indexContent);
1310
+ for (const meta of index.sessions) {
1311
+ try {
1312
+ // Check if already migrated
1313
+ const existing = this.getSessionMetadata(meta.id);
1314
+ if (existing) {
1315
+ continue;
1316
+ }
1317
+ // Insert session metadata
1318
+ this.stmts.insertSession.run({
1319
+ id: meta.id,
1320
+ name: meta.name || null,
1321
+ createdAt: meta.createdAt,
1322
+ lastActiveAt: meta.lastActiveAt,
1323
+ messageCount: meta.messageCount,
1324
+ tokenCount: meta.tokenCount,
1325
+ });
1326
+ // Load and migrate entries
1327
+ const sessionPath = join(jsonlDir, `${meta.id}.jsonl`);
1328
+ if (existsSync(sessionPath)) {
1329
+ const content = readFileSync(sessionPath, 'utf-8');
1330
+ for (const line of content.split('\n')) {
1331
+ if (line.trim()) {
1332
+ try {
1333
+ const entry = JSON.parse(line);
1334
+ this.stmts.insertEntry.run({
1335
+ sessionId: meta.id,
1336
+ timestamp: entry.timestamp,
1337
+ type: entry.type,
1338
+ data: JSON.stringify(entry.data),
1339
+ });
1340
+ }
1341
+ catch {
1342
+ // Skip corrupted lines
1343
+ }
1344
+ }
1345
+ }
1346
+ }
1347
+ migrated++;
1348
+ }
1349
+ catch (err) {
1350
+ console.error(`Failed to migrate session ${meta.id}:`, err);
1351
+ failed++;
1352
+ }
1353
+ }
1354
+ }
1355
+ catch (err) {
1356
+ console.error('Failed to read JSONL index:', err);
1357
+ }
1358
+ return { migrated, failed };
1359
+ }
1360
+ // ===========================================================================
1361
+ // LIFECYCLE
1362
+ // ===========================================================================
1363
+ /**
1364
+ * Prune old sessions if over limit.
1365
+ */
1366
+ pruneOldSessions() {
1367
+ const sessions = this.listSessions();
1368
+ if (sessions.length > this.config.maxSessions) {
1369
+ const toDelete = sessions.slice(this.config.maxSessions);
1370
+ for (const session of toDelete) {
1371
+ this.stmts.deleteSession.run(session.id);
1372
+ }
1373
+ }
1374
+ }
1375
+ /**
1376
+ * Subscribe to events.
1377
+ */
1378
+ on(listener) {
1379
+ this.listeners.push(listener);
1380
+ return () => {
1381
+ const idx = this.listeners.indexOf(listener);
1382
+ if (idx >= 0)
1383
+ this.listeners.splice(idx, 1);
1384
+ };
1385
+ }
1386
+ /**
1387
+ * Emit an event.
1388
+ */
1389
+ emit(event) {
1390
+ for (const listener of this.listeners) {
1391
+ try {
1392
+ listener(event);
1393
+ }
1394
+ catch {
1395
+ // Ignore listener errors
1396
+ }
1397
+ }
1398
+ }
1399
+ /**
1400
+ * Close the database connection.
1401
+ */
1402
+ close() {
1403
+ this.db.close();
1404
+ }
1405
+ /**
1406
+ * Cleanup - same as close for compatibility.
1407
+ */
1408
+ async cleanup() {
1409
+ this.close();
1410
+ }
1411
+ }
1412
+ // =============================================================================
1413
+ // FACTORY
1414
+ // =============================================================================
1415
+ /**
1416
+ * Create and initialize a SQLite session store.
1417
+ */
1418
+ export async function createSQLiteStore(config) {
1419
+ const store = new SQLiteStore(config);
1420
+ await store.initialize();
1421
+ return store;
1422
+ }
1423
+ //# sourceMappingURL=sqlite-store.js.map