@cleocode/core 2026.3.74 → 2026.4.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 (428) hide show
  1. package/README.md +1 -1
  2. package/dist/agents/agent-schema.d.ts.map +1 -1
  3. package/dist/agents/retry.js +26 -21
  4. package/dist/agents/retry.js.map +1 -1
  5. package/dist/cant/approval.d.ts +110 -0
  6. package/dist/cant/approval.d.ts.map +1 -0
  7. package/dist/cant/approval.js +185 -0
  8. package/dist/cant/approval.js.map +1 -0
  9. package/dist/cant/context-builder.d.ts +79 -0
  10. package/dist/cant/context-builder.d.ts.map +1 -0
  11. package/dist/cant/context-builder.js +117 -0
  12. package/dist/cant/context-builder.js.map +1 -0
  13. package/dist/cant/discretion.d.ts +95 -0
  14. package/dist/cant/discretion.d.ts.map +1 -0
  15. package/dist/cant/discretion.js +116 -0
  16. package/dist/cant/discretion.js.map +1 -0
  17. package/dist/cant/index.d.ts +25 -0
  18. package/dist/cant/index.d.ts.map +1 -0
  19. package/dist/cant/index.js +23 -0
  20. package/dist/cant/index.js.map +1 -0
  21. package/dist/cant/parallel-runner.d.ts +38 -0
  22. package/dist/cant/parallel-runner.d.ts.map +1 -0
  23. package/dist/cant/parallel-runner.js +173 -0
  24. package/dist/cant/parallel-runner.js.map +1 -0
  25. package/dist/cant/types.d.ts +127 -0
  26. package/dist/cant/types.d.ts.map +1 -0
  27. package/dist/cant/types.js +11 -0
  28. package/dist/cant/types.js.map +1 -0
  29. package/dist/cant/workflow-executor.d.ts +105 -0
  30. package/dist/cant/workflow-executor.d.ts.map +1 -0
  31. package/dist/cant/workflow-executor.js +440 -0
  32. package/dist/cant/workflow-executor.js.map +1 -0
  33. package/dist/cleo.js +21 -1
  34. package/dist/cleo.js.map +1 -1
  35. package/dist/code/index.d.ts +10 -0
  36. package/dist/code/index.d.ts.map +1 -0
  37. package/dist/code/outline.d.ts +51 -0
  38. package/dist/code/outline.d.ts.map +1 -0
  39. package/dist/code/parser.d.ts +30 -0
  40. package/dist/code/parser.d.ts.map +1 -0
  41. package/dist/code/search.d.ts +42 -0
  42. package/dist/code/search.d.ts.map +1 -0
  43. package/dist/code/unfold.d.ts +44 -0
  44. package/dist/code/unfold.d.ts.map +1 -0
  45. package/dist/conduit/conduit-client.d.ts +35 -0
  46. package/dist/conduit/conduit-client.d.ts.map +1 -0
  47. package/dist/conduit/conduit-client.js +94 -0
  48. package/dist/conduit/conduit-client.js.map +1 -0
  49. package/dist/conduit/factory.d.ts +15 -0
  50. package/dist/conduit/factory.d.ts.map +1 -0
  51. package/dist/conduit/factory.js +35 -0
  52. package/dist/conduit/factory.js.map +1 -0
  53. package/dist/conduit/http-transport.d.ts +44 -0
  54. package/dist/conduit/http-transport.d.ts.map +1 -0
  55. package/dist/conduit/http-transport.js +165 -0
  56. package/dist/conduit/http-transport.js.map +1 -0
  57. package/dist/conduit/index.d.ts +15 -0
  58. package/dist/conduit/index.d.ts.map +1 -0
  59. package/dist/conduit/index.js +12 -0
  60. package/dist/conduit/index.js.map +1 -0
  61. package/dist/conduit/local-transport.d.ts +91 -0
  62. package/dist/conduit/local-transport.d.ts.map +1 -0
  63. package/dist/conduit/sse-transport.d.ts +68 -0
  64. package/dist/conduit/sse-transport.d.ts.map +1 -0
  65. package/dist/config.js +4 -3
  66. package/dist/config.js.map +1 -1
  67. package/dist/crypto/credentials.d.ts +40 -0
  68. package/dist/crypto/credentials.d.ts.map +1 -0
  69. package/dist/crypto/credentials.js +144 -0
  70. package/dist/crypto/credentials.js.map +1 -0
  71. package/dist/engine-result.d.ts +1 -1
  72. package/dist/engine-result.d.ts.map +1 -1
  73. package/dist/error-catalog.d.ts +1 -1
  74. package/dist/error-catalog.d.ts.map +1 -1
  75. package/dist/error-registry.d.ts +1 -1
  76. package/dist/error-registry.d.ts.map +1 -1
  77. package/dist/errors.d.ts +1 -1
  78. package/dist/errors.d.ts.map +1 -1
  79. package/dist/hooks/handlers/agent-hooks.d.ts.map +1 -1
  80. package/dist/hooks/handlers/agent-hooks.js +106 -0
  81. package/dist/hooks/handlers/agent-hooks.js.map +1 -0
  82. package/dist/hooks/handlers/context-hooks.d.ts.map +1 -1
  83. package/dist/hooks/handlers/context-hooks.js +111 -0
  84. package/dist/hooks/handlers/context-hooks.js.map +1 -0
  85. package/dist/hooks/handlers/error-hooks.d.ts +14 -5
  86. package/dist/hooks/handlers/error-hooks.d.ts.map +1 -1
  87. package/dist/hooks/handlers/error-hooks.js +15 -6
  88. package/dist/hooks/handlers/error-hooks.js.map +1 -1
  89. package/dist/hooks/handlers/file-hooks.d.ts.map +1 -1
  90. package/dist/hooks/handlers/file-hooks.js +35 -11
  91. package/dist/hooks/handlers/file-hooks.js.map +1 -1
  92. package/dist/hooks/handlers/handler-helpers.d.ts +41 -0
  93. package/dist/hooks/handlers/handler-helpers.d.ts.map +1 -0
  94. package/dist/hooks/handlers/handler-helpers.js +61 -0
  95. package/dist/hooks/handlers/handler-helpers.js.map +1 -0
  96. package/dist/hooks/handlers/index.js +10 -1
  97. package/dist/hooks/handlers/index.js.map +1 -1
  98. package/dist/hooks/handlers/mcp-hooks.d.ts.map +1 -1
  99. package/dist/hooks/handlers/mcp-hooks.js +88 -21
  100. package/dist/hooks/handlers/mcp-hooks.js.map +1 -1
  101. package/dist/hooks/handlers/session-hooks.d.ts.map +1 -1
  102. package/dist/hooks/handlers/session-hooks.js +5 -10
  103. package/dist/hooks/handlers/session-hooks.js.map +1 -1
  104. package/dist/hooks/handlers/task-hooks.d.ts.map +1 -1
  105. package/dist/hooks/handlers/task-hooks.js +5 -10
  106. package/dist/hooks/handlers/task-hooks.js.map +1 -1
  107. package/dist/hooks/handlers/work-capture-hooks.d.ts.map +1 -1
  108. package/dist/hooks/handlers/work-capture-hooks.js +165 -0
  109. package/dist/hooks/handlers/work-capture-hooks.js.map +1 -0
  110. package/dist/hooks/payload-schemas.js +83 -26
  111. package/dist/hooks/payload-schemas.js.map +1 -1
  112. package/dist/hooks/provider-hooks.js +37 -5
  113. package/dist/hooks/provider-hooks.js.map +1 -1
  114. package/dist/hooks/registry.js +76 -23
  115. package/dist/hooks/registry.js.map +1 -1
  116. package/dist/hooks/types.js +17 -13
  117. package/dist/hooks/types.js.map +1 -1
  118. package/dist/index.d.ts +4 -1
  119. package/dist/index.d.ts.map +1 -1
  120. package/dist/index.js +6452 -3371
  121. package/dist/index.js.map +4 -4
  122. package/dist/init.d.ts.map +1 -1
  123. package/dist/init.js +12 -0
  124. package/dist/init.js.map +1 -1
  125. package/dist/internal.d.ts +11 -1
  126. package/dist/internal.d.ts.map +1 -1
  127. package/dist/internal.js +10 -0
  128. package/dist/internal.js.map +1 -1
  129. package/dist/lib/index.d.ts +1 -0
  130. package/dist/lib/index.d.ts.map +1 -1
  131. package/dist/lib/tree-sitter-languages.d.ts +29 -0
  132. package/dist/lib/tree-sitter-languages.d.ts.map +1 -0
  133. package/dist/memory/brain-links.d.ts.map +1 -1
  134. package/dist/memory/brain-maintenance.d.ts +13 -0
  135. package/dist/memory/brain-maintenance.d.ts.map +1 -1
  136. package/dist/memory/brain-retrieval.d.ts +3 -0
  137. package/dist/memory/brain-retrieval.d.ts.map +1 -1
  138. package/dist/memory/brain-retrieval.js +5 -0
  139. package/dist/memory/brain-retrieval.js.map +1 -1
  140. package/dist/memory/decisions.d.ts.map +1 -1
  141. package/dist/mvi-helpers.d.ts +52 -0
  142. package/dist/mvi-helpers.d.ts.map +1 -0
  143. package/dist/mvi-helpers.js +74 -0
  144. package/dist/mvi-helpers.js.map +1 -0
  145. package/dist/nexus/index.js +2 -0
  146. package/dist/nexus/index.js.map +1 -1
  147. package/dist/nexus/workspace.d.ts.map +1 -1
  148. package/dist/nexus/workspace.js +355 -0
  149. package/dist/nexus/workspace.js.map +1 -0
  150. package/dist/orchestration/hierarchy.d.ts +32 -0
  151. package/dist/orchestration/hierarchy.d.ts.map +1 -0
  152. package/dist/orchestration/index.d.ts +1 -0
  153. package/dist/orchestration/index.d.ts.map +1 -1
  154. package/dist/output.d.ts +2 -2
  155. package/dist/output.d.ts.map +1 -1
  156. package/dist/output.js +40 -8
  157. package/dist/output.js.map +1 -1
  158. package/dist/pagination.d.ts +1 -1
  159. package/dist/pagination.d.ts.map +1 -1
  160. package/dist/sessions/find.d.ts +3 -0
  161. package/dist/sessions/find.d.ts.map +1 -1
  162. package/dist/sessions/find.js +3 -1
  163. package/dist/sessions/find.js.map +1 -1
  164. package/dist/sessions/index.d.ts.map +1 -1
  165. package/dist/sessions/index.js +11 -4
  166. package/dist/sessions/index.js.map +1 -1
  167. package/dist/sessions/snapshot.js +213 -0
  168. package/dist/sessions/snapshot.js.map +1 -0
  169. package/dist/store/agent-registry-accessor.d.ts +31 -0
  170. package/dist/store/agent-registry-accessor.d.ts.map +1 -0
  171. package/dist/store/agent-registry-accessor.js +169 -0
  172. package/dist/store/agent-registry-accessor.js.map +1 -0
  173. package/dist/store/converters.d.ts.map +1 -1
  174. package/dist/store/converters.js +2 -0
  175. package/dist/store/converters.js.map +1 -1
  176. package/dist/store/cross-db-cleanup.d.ts +34 -0
  177. package/dist/store/cross-db-cleanup.d.ts.map +1 -1
  178. package/dist/store/db-helpers.d.ts.map +1 -1
  179. package/dist/store/db-helpers.js +1 -0
  180. package/dist/store/db-helpers.js.map +1 -1
  181. package/dist/store/json.js +2 -2
  182. package/dist/store/safety-data-accessor.d.ts +7 -0
  183. package/dist/store/safety-data-accessor.d.ts.map +1 -1
  184. package/dist/store/safety-data-accessor.js +14 -0
  185. package/dist/store/safety-data-accessor.js.map +1 -1
  186. package/dist/store/signaldock-sqlite.d.ts +48 -0
  187. package/dist/store/signaldock-sqlite.d.ts.map +1 -0
  188. package/dist/store/signaldock-sqlite.js +178 -0
  189. package/dist/store/signaldock-sqlite.js.map +1 -0
  190. package/dist/store/sqlite-data-accessor.d.ts.map +1 -1
  191. package/dist/store/sqlite-data-accessor.js +50 -0
  192. package/dist/store/sqlite-data-accessor.js.map +1 -1
  193. package/dist/store/sqlite.d.ts.map +1 -1
  194. package/dist/store/sqlite.js +30 -1
  195. package/dist/store/sqlite.js.map +1 -1
  196. package/dist/store/task-store.d.ts.map +1 -1
  197. package/dist/store/task-store.js +2 -0
  198. package/dist/store/task-store.js.map +1 -1
  199. package/dist/store/tasks-schema.d.ts +16 -0
  200. package/dist/store/tasks-schema.d.ts.map +1 -1
  201. package/dist/store/tasks-schema.js +33 -0
  202. package/dist/store/tasks-schema.js.map +1 -1
  203. package/dist/store/validation-schemas.d.ts +32 -0
  204. package/dist/store/validation-schemas.d.ts.map +1 -1
  205. package/dist/system/health.d.ts +1 -1
  206. package/dist/system/health.d.ts.map +1 -1
  207. package/dist/system/health.js +35 -0
  208. package/dist/system/health.js.map +1 -1
  209. package/dist/task-work/index.d.ts.map +1 -1
  210. package/dist/task-work/index.js +8 -4
  211. package/dist/task-work/index.js.map +1 -1
  212. package/dist/tasks/complete.js +5 -2
  213. package/dist/tasks/complete.js.map +1 -1
  214. package/dist/tasks/find.d.ts +3 -0
  215. package/dist/tasks/find.d.ts.map +1 -1
  216. package/dist/tasks/find.js +7 -1
  217. package/dist/tasks/find.js.map +1 -1
  218. package/dist/tasks/list.d.ts +5 -2
  219. package/dist/tasks/list.d.ts.map +1 -1
  220. package/dist/tasks/list.js +9 -2
  221. package/dist/tasks/list.js.map +1 -1
  222. package/dist/tasks/show.d.ts +3 -0
  223. package/dist/tasks/show.d.ts.map +1 -1
  224. package/dist/tasks/show.js +2 -0
  225. package/dist/tasks/show.js.map +1 -1
  226. package/dist/upgrade.d.ts.map +1 -1
  227. package/dist/upgrade.js +15 -0
  228. package/dist/upgrade.js.map +1 -1
  229. package/migrations/drizzle-tasks/20260324000000_assignee-column/migration.sql +6 -0
  230. package/migrations/drizzle-tasks/20260324000000_assignee-column/snapshot.json +9 -0
  231. package/migrations/drizzle-tasks/20260327000000_agent-credentials/migration.sql +23 -0
  232. package/package.json +17 -7
  233. package/src/__tests__/cli-parity.test.js +11 -1
  234. package/src/__tests__/cli-parity.test.js.map +1 -1
  235. package/src/__tests__/cli-parity.test.ts +17 -1
  236. package/src/__tests__/human-output.test.js +11 -1
  237. package/src/__tests__/human-output.test.js.map +1 -1
  238. package/src/__tests__/human-output.test.ts +18 -1
  239. package/src/__tests__/injection-chain.test.js +3 -2
  240. package/src/__tests__/injection-chain.test.js.map +1 -1
  241. package/src/__tests__/injection-mvi-tiers.test.d.ts +2 -2
  242. package/src/__tests__/injection-mvi-tiers.test.js +15 -15
  243. package/src/__tests__/injection-mvi-tiers.test.js.map +1 -1
  244. package/src/__tests__/lafs-conformance.test.d.ts +1 -1
  245. package/src/__tests__/lafs-conformance.test.js +2 -2
  246. package/src/__tests__/sharing.test.js +19 -0
  247. package/src/__tests__/sharing.test.js.map +1 -1
  248. package/src/agents/__tests__/agent-registry.test.d.ts +12 -0
  249. package/src/agents/__tests__/agent-registry.test.d.ts.map +1 -0
  250. package/src/agents/__tests__/agent-registry.test.js +262 -0
  251. package/src/agents/__tests__/agent-registry.test.js.map +1 -0
  252. package/src/agents/__tests__/execution-learning.test.d.ts +14 -0
  253. package/src/agents/__tests__/execution-learning.test.d.ts.map +1 -0
  254. package/src/agents/__tests__/execution-learning.test.js +533 -0
  255. package/src/agents/__tests__/execution-learning.test.js.map +1 -0
  256. package/src/agents/__tests__/health-monitor.test.d.ts +10 -0
  257. package/src/agents/__tests__/health-monitor.test.d.ts.map +1 -0
  258. package/src/agents/__tests__/health-monitor.test.js +259 -0
  259. package/src/agents/__tests__/health-monitor.test.js.map +1 -0
  260. package/src/agents/__tests__/registry.test.js +27 -2
  261. package/src/agents/__tests__/registry.test.js.map +1 -1
  262. package/src/agents/agent-schema.ts +2 -5
  263. package/src/cant/__tests__/cant-agent-parse.test.ts +94 -0
  264. package/src/cant/approval.ts +218 -0
  265. package/src/cant/context-builder.ts +135 -0
  266. package/src/cant/discretion.ts +149 -0
  267. package/src/cant/index.ts +58 -0
  268. package/src/cant/parallel-runner.ts +205 -0
  269. package/src/cant/types.ts +158 -0
  270. package/src/cant/workflow-executor.ts +618 -0
  271. package/src/code/index.ts +10 -0
  272. package/src/code/outline.ts +214 -0
  273. package/src/code/parser.ts +299 -0
  274. package/src/code/search.ts +173 -0
  275. package/src/code/unfold.ts +204 -0
  276. package/src/conduit/__tests__/dual-api-e2e.test.ts +212 -0
  277. package/src/conduit/__tests__/local-credential-flow.test.ts +230 -0
  278. package/src/conduit/__tests__/local-transport.test.ts +320 -0
  279. package/src/conduit/__tests__/sse-transport.test.ts +344 -0
  280. package/src/conduit/conduit-client.ts +123 -0
  281. package/src/conduit/factory.ts +49 -0
  282. package/src/conduit/http-transport.ts +201 -0
  283. package/src/conduit/index.ts +15 -0
  284. package/src/conduit/local-transport.ts +309 -0
  285. package/src/conduit/sse-transport.ts +382 -0
  286. package/src/crypto/credentials.ts +166 -0
  287. package/src/engine-result.ts +1 -1
  288. package/src/error-catalog.ts +1 -1
  289. package/src/error-registry.ts +1 -1
  290. package/src/errors.ts +1 -1
  291. package/src/hooks/handlers/__tests__/hook-automation-e2e.test.d.ts +13 -0
  292. package/src/hooks/handlers/__tests__/hook-automation-e2e.test.d.ts.map +1 -0
  293. package/src/hooks/handlers/__tests__/hook-automation-e2e.test.js +501 -0
  294. package/src/hooks/handlers/__tests__/hook-automation-e2e.test.js.map +1 -0
  295. package/src/hooks/handlers/agent-hooks.ts +1 -30
  296. package/src/hooks/handlers/context-hooks.ts +1 -30
  297. package/src/hooks/handlers/error-hooks.ts +14 -5
  298. package/src/hooks/handlers/file-hooks.ts +1 -6
  299. package/src/hooks/handlers/handler-helpers.ts +62 -0
  300. package/src/hooks/handlers/mcp-hooks.ts +2 -14
  301. package/src/hooks/handlers/session-hooks.ts +1 -6
  302. package/src/hooks/handlers/task-hooks.ts +1 -6
  303. package/src/hooks/handlers/work-capture-hooks.ts +1 -10
  304. package/src/index.ts +12 -1
  305. package/src/init.ts +12 -0
  306. package/src/intelligence/__tests__/adaptive-validation.test.d.ts +11 -0
  307. package/src/intelligence/__tests__/adaptive-validation.test.d.ts.map +1 -0
  308. package/src/intelligence/__tests__/adaptive-validation.test.js +517 -0
  309. package/src/intelligence/__tests__/adaptive-validation.test.js.map +1 -0
  310. package/src/intelligence/__tests__/impact.test.d.ts +1 -0
  311. package/src/intelligence/__tests__/impact.test.d.ts.map +1 -1
  312. package/src/intelligence/__tests__/impact.test.js +132 -1
  313. package/src/intelligence/__tests__/impact.test.js.map +1 -1
  314. package/src/internal.ts +22 -0
  315. package/src/lib/__tests__/retry.test.d.ts +7 -0
  316. package/src/lib/__tests__/retry.test.d.ts.map +1 -0
  317. package/src/lib/__tests__/retry.test.js +225 -0
  318. package/src/lib/__tests__/retry.test.js.map +1 -0
  319. package/src/lib/index.ts +8 -0
  320. package/src/lib/tree-sitter-languages.ts +88 -0
  321. package/src/lifecycle/__tests__/chain-store.test.js +6 -0
  322. package/src/lifecycle/__tests__/chain-store.test.js.map +1 -1
  323. package/src/lifecycle/__tests__/tessera-engine.test.js +52 -0
  324. package/src/lifecycle/__tests__/tessera-engine.test.js.map +1 -1
  325. package/src/memory/__tests__/brain-automation.test.d.ts +11 -0
  326. package/src/memory/__tests__/brain-automation.test.d.ts.map +1 -0
  327. package/src/memory/__tests__/brain-automation.test.js +730 -0
  328. package/src/memory/__tests__/brain-automation.test.js.map +1 -0
  329. package/src/memory/__tests__/brain-links.test.ts +14 -0
  330. package/src/memory/__tests__/brain-retrieval.test.ts +10 -0
  331. package/src/memory/__tests__/session-memory.test.ts +17 -0
  332. package/src/memory/brain-links.ts +17 -0
  333. package/src/memory/brain-maintenance.ts +33 -1
  334. package/src/memory/brain-retrieval.ts +27 -2
  335. package/src/memory/decisions.ts +18 -2
  336. package/src/mvi-helpers.ts +81 -0
  337. package/src/nexus/workspace.ts +19 -7
  338. package/src/orchestration/hierarchy.ts +202 -0
  339. package/src/orchestration/index.ts +1 -0
  340. package/src/output.ts +43 -10
  341. package/src/pagination.ts +1 -1
  342. package/src/sessions/__tests__/session-edge-cases.test.js +20 -1
  343. package/src/sessions/__tests__/session-edge-cases.test.js.map +1 -1
  344. package/src/sessions/__tests__/session-find.test.js +1 -1
  345. package/src/sessions/__tests__/session-find.test.js.map +1 -1
  346. package/src/sessions/__tests__/session-find.test.ts +1 -1
  347. package/src/sessions/find.ts +6 -1
  348. package/src/sessions/index.ts +9 -0
  349. package/src/store/__tests__/migration-safety.test.js +3 -0
  350. package/src/store/__tests__/migration-safety.test.js.map +1 -1
  351. package/src/store/__tests__/session-store.test.js +128 -1
  352. package/src/store/__tests__/session-store.test.js.map +1 -1
  353. package/src/store/__tests__/task-store.test.js +18 -1
  354. package/src/store/__tests__/task-store.test.js.map +1 -1
  355. package/src/store/__tests__/test-db-helper.d.ts.map +1 -1
  356. package/src/store/__tests__/test-db-helper.js +12 -0
  357. package/src/store/__tests__/test-db-helper.js.map +1 -1
  358. package/src/store/agent-registry-accessor.ts +375 -0
  359. package/src/store/converters.ts +2 -0
  360. package/src/store/cross-db-cleanup.ts +175 -1
  361. package/src/store/db-helpers.ts +1 -0
  362. package/src/store/safety-data-accessor.ts +23 -0
  363. package/src/store/signaldock-sqlite.ts +429 -0
  364. package/src/store/sqlite-data-accessor.ts +72 -0
  365. package/src/store/sqlite.ts +4 -1
  366. package/src/store/task-store.ts +9 -1
  367. package/src/store/tasks-schema.ts +7 -0
  368. package/src/system/__tests__/health.test.ts +2 -2
  369. package/src/system/health.ts +54 -2
  370. package/src/task-work/index.ts +5 -0
  371. package/src/tasks/__tests__/add.test.js +19 -1
  372. package/src/tasks/__tests__/add.test.js.map +1 -1
  373. package/src/tasks/__tests__/assignee.test.d.ts +14 -0
  374. package/src/tasks/__tests__/assignee.test.d.ts.map +1 -0
  375. package/src/tasks/__tests__/assignee.test.js +125 -0
  376. package/src/tasks/__tests__/assignee.test.js.map +1 -0
  377. package/src/tasks/__tests__/assignee.test.ts +162 -0
  378. package/src/tasks/__tests__/complete-unblocks.test.js +13 -1
  379. package/src/tasks/__tests__/complete-unblocks.test.js.map +1 -1
  380. package/src/tasks/__tests__/complete.test.js +28 -7
  381. package/src/tasks/__tests__/complete.test.js.map +1 -1
  382. package/src/tasks/__tests__/epic-enforcement.test.d.ts +15 -0
  383. package/src/tasks/__tests__/epic-enforcement.test.d.ts.map +1 -0
  384. package/src/tasks/__tests__/epic-enforcement.test.js +669 -0
  385. package/src/tasks/__tests__/epic-enforcement.test.js.map +1 -0
  386. package/src/tasks/__tests__/hierarchy-policy.test.js +5 -0
  387. package/src/tasks/__tests__/hierarchy-policy.test.js.map +1 -1
  388. package/src/tasks/__tests__/minimal-test.test.d.ts +2 -0
  389. package/src/tasks/__tests__/minimal-test.test.d.ts.map +1 -0
  390. package/src/tasks/__tests__/minimal-test.test.js +25 -0
  391. package/src/tasks/__tests__/minimal-test.test.js.map +1 -0
  392. package/src/tasks/__tests__/pipeline-stage.test.d.ts +14 -0
  393. package/src/tasks/__tests__/pipeline-stage.test.d.ts.map +1 -0
  394. package/src/tasks/__tests__/pipeline-stage.test.js +277 -0
  395. package/src/tasks/__tests__/pipeline-stage.test.js.map +1 -0
  396. package/src/tasks/__tests__/update.test.js +43 -6
  397. package/src/tasks/__tests__/update.test.js.map +1 -1
  398. package/src/tasks/find.ts +11 -1
  399. package/src/tasks/list.ts +14 -3
  400. package/src/tasks/show.ts +6 -0
  401. package/src/upgrade.ts +16 -0
  402. package/dist/tasks/reparent.d.ts +0 -38
  403. package/dist/tasks/reparent.d.ts.map +0 -1
  404. package/dist/ui/injection-legacy.d.ts +0 -26
  405. package/dist/ui/injection-legacy.d.ts.map +0 -1
  406. package/dist/ui/injection-legacy.js +0 -42
  407. package/dist/ui/injection-legacy.js.map +0 -1
  408. package/src/signaldock/__tests__/claude-code-transport.test.d.ts +0 -7
  409. package/src/signaldock/__tests__/claude-code-transport.test.d.ts.map +0 -1
  410. package/src/signaldock/__tests__/claude-code-transport.test.js +0 -147
  411. package/src/signaldock/__tests__/claude-code-transport.test.js.map +0 -1
  412. package/src/signaldock/__tests__/claude-code-transport.test.ts +0 -180
  413. package/src/signaldock/__tests__/factory.test.d.ts +0 -7
  414. package/src/signaldock/__tests__/factory.test.d.ts.map +0 -1
  415. package/src/signaldock/__tests__/factory.test.js +0 -55
  416. package/src/signaldock/__tests__/factory.test.js.map +0 -1
  417. package/src/signaldock/__tests__/factory.test.ts +0 -61
  418. package/src/signaldock/__tests__/signaldock-transport.test.d.ts +0 -9
  419. package/src/signaldock/__tests__/signaldock-transport.test.d.ts.map +0 -1
  420. package/src/signaldock/__tests__/signaldock-transport.test.js +0 -321
  421. package/src/signaldock/__tests__/signaldock-transport.test.js.map +0 -1
  422. package/src/signaldock/__tests__/signaldock-transport.test.ts +0 -421
  423. package/src/signaldock/claude-code-transport.ts +0 -137
  424. package/src/signaldock/factory.ts +0 -39
  425. package/src/signaldock/index.ts +0 -28
  426. package/src/signaldock/signaldock-transport.ts +0 -194
  427. package/src/signaldock/transport.ts +0 -78
  428. package/src/signaldock/types.ts +0 -100
@@ -0,0 +1,429 @@
1
+ /**
2
+ * SQLite store for signaldock.db — local agent messaging database.
3
+ *
4
+ * Creates and manages .cleo/signaldock.db using node:sqlite directly.
5
+ * Runs the consolidated Diesel migration SQL (from signaldock-storage crate)
6
+ * to bootstrap all 22 tables for local agent infrastructure.
7
+ *
8
+ * This is the Node.js bootstrap path. In production cloud, the Rust
9
+ * signaldock-storage crate manages this DB via Diesel ORM directly.
10
+ * Locally, we create the DB here so that cleo init scaffolds the full
11
+ * .cleo/ directory with all databases ready.
12
+ *
13
+ * @task T223
14
+ */
15
+
16
+ import { existsSync, mkdirSync } from 'node:fs';
17
+ import { createRequire } from 'node:module';
18
+ import { dirname, join } from 'node:path';
19
+ import type { DatabaseSync } from 'node:sqlite';
20
+ import { getCleoDirAbsolute } from '../paths.js';
21
+
22
+ const _require = createRequire(import.meta.url);
23
+ const { DatabaseSync: DatabaseSyncClass } = _require('node:sqlite') as {
24
+ DatabaseSync: new (...args: ConstructorParameters<typeof DatabaseSync>) => DatabaseSync;
25
+ };
26
+
27
+ /** Database file name within .cleo/ directory. */
28
+ const DB_FILENAME = 'signaldock.db';
29
+
30
+ /** Schema version for signaldock databases. */
31
+ export const SIGNALDOCK_SCHEMA_VERSION = '2026.3.76';
32
+
33
+ /**
34
+ * Get the path to the signaldock.db SQLite database file.
35
+ */
36
+ export function getSignaldockDbPath(cwd?: string): string {
37
+ const cleoDir = cwd ? join(cwd, '.cleo') : getCleoDirAbsolute();
38
+ return join(cleoDir, DB_FILENAME);
39
+ }
40
+
41
+ // ---------------------------------------------------------------------------
42
+ // Embedded migration SQL — bundled so signaldock.db works in ANY project,
43
+ // not just the monorepo where crates/signaldock-storage/ exists.
44
+ // Source: crates/signaldock-storage/migrations/
45
+ // ---------------------------------------------------------------------------
46
+
47
+ /**
48
+ * Ordered migration entries. Each has a name (for tracking) and SQL.
49
+ * Add new migrations to the END of this array.
50
+ */
51
+ const EMBEDDED_MIGRATIONS: Array<{ name: string; sql: string }> = [
52
+ {
53
+ name: '2026-03-28-000000_initial',
54
+ sql: `-- Consolidated initial migration for SignalDock storage (19 sqlx migrations merged).
55
+ CREATE TABLE IF NOT EXISTS users (
56
+ id TEXT PRIMARY KEY, email TEXT NOT NULL UNIQUE, password_hash TEXT NOT NULL,
57
+ name TEXT, slug TEXT, default_agent_id TEXT, username TEXT, display_username TEXT,
58
+ email_verified INTEGER NOT NULL DEFAULT 0, image TEXT, role TEXT NOT NULL DEFAULT 'user',
59
+ banned INTEGER NOT NULL DEFAULT 0, ban_reason TEXT, ban_expires TEXT,
60
+ two_factor_enabled INTEGER NOT NULL DEFAULT 0, metadata TEXT,
61
+ created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL
62
+ );
63
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_users_slug ON users(slug);
64
+
65
+ CREATE TABLE IF NOT EXISTS organization (
66
+ id TEXT PRIMARY KEY NOT NULL, name TEXT NOT NULL, slug TEXT, logo TEXT, metadata TEXT,
67
+ owner_id TEXT, created_at INTEGER NOT NULL DEFAULT (strftime('%s','now')),
68
+ updated_at INTEGER NOT NULL DEFAULT (strftime('%s','now'))
69
+ );
70
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_organization_slug ON organization(slug);
71
+
72
+ CREATE TABLE IF NOT EXISTS agents (
73
+ id TEXT PRIMARY KEY, agent_id TEXT NOT NULL UNIQUE, name TEXT NOT NULL,
74
+ description TEXT, class TEXT NOT NULL DEFAULT 'custom',
75
+ privacy_tier TEXT NOT NULL DEFAULT 'public', owner_id TEXT REFERENCES users(id),
76
+ endpoint TEXT, webhook_secret TEXT, capabilities TEXT NOT NULL DEFAULT '[]',
77
+ skills TEXT NOT NULL DEFAULT '[]', avatar TEXT, messages_sent INTEGER NOT NULL DEFAULT 0,
78
+ messages_received INTEGER NOT NULL DEFAULT 0, conversation_count INTEGER NOT NULL DEFAULT 0,
79
+ friend_count INTEGER NOT NULL DEFAULT 0, status TEXT NOT NULL DEFAULT 'online',
80
+ last_seen INTEGER, payment_config TEXT, api_key_hash TEXT,
81
+ organization_id TEXT REFERENCES organization(id) ON DELETE SET NULL,
82
+ created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL
83
+ );
84
+ CREATE UNIQUE INDEX IF NOT EXISTS agents_agent_id_idx ON agents(agent_id);
85
+ CREATE INDEX IF NOT EXISTS agents_owner_idx ON agents(owner_id);
86
+ CREATE INDEX IF NOT EXISTS agents_class_idx ON agents(class);
87
+ CREATE INDEX IF NOT EXISTS agents_privacy_idx ON agents(privacy_tier);
88
+ CREATE INDEX IF NOT EXISTS agents_org_idx ON agents(organization_id);
89
+
90
+ CREATE TABLE IF NOT EXISTS conversations (
91
+ id TEXT PRIMARY KEY, participants TEXT NOT NULL,
92
+ visibility TEXT NOT NULL DEFAULT 'private', message_count INTEGER NOT NULL DEFAULT 0,
93
+ last_message_at INTEGER, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL
94
+ );
95
+
96
+ CREATE TABLE IF NOT EXISTS messages (
97
+ id TEXT PRIMARY KEY, conversation_id TEXT NOT NULL REFERENCES conversations(id),
98
+ from_agent_id TEXT NOT NULL, to_agent_id TEXT NOT NULL, content TEXT NOT NULL,
99
+ content_type TEXT NOT NULL DEFAULT 'text', status TEXT NOT NULL DEFAULT 'pending',
100
+ attachments TEXT NOT NULL DEFAULT '[]', group_id TEXT, metadata TEXT DEFAULT '{}',
101
+ reply_to TEXT, created_at INTEGER NOT NULL, delivered_at INTEGER, read_at INTEGER
102
+ );
103
+ CREATE INDEX IF NOT EXISTS messages_conversation_idx ON messages(conversation_id);
104
+ CREATE INDEX IF NOT EXISTS messages_from_agent_idx ON messages(from_agent_id);
105
+ CREATE INDEX IF NOT EXISTS messages_to_agent_idx ON messages(to_agent_id);
106
+ CREATE INDEX IF NOT EXISTS messages_created_at_idx ON messages(created_at);
107
+ CREATE INDEX IF NOT EXISTS idx_messages_group_id ON messages(group_id) WHERE group_id IS NOT NULL;
108
+ CREATE INDEX IF NOT EXISTS idx_messages_reply_to ON messages(reply_to) WHERE reply_to IS NOT NULL;
109
+
110
+ CREATE TABLE IF NOT EXISTS claim_codes (
111
+ id TEXT PRIMARY KEY, agent_id TEXT NOT NULL REFERENCES agents(id),
112
+ code TEXT NOT NULL UNIQUE, expires_at INTEGER NOT NULL, used_at INTEGER,
113
+ used_by TEXT REFERENCES users(id), created_at INTEGER NOT NULL
114
+ );
115
+ CREATE UNIQUE INDEX IF NOT EXISTS claim_codes_code_idx ON claim_codes(code);
116
+ CREATE INDEX IF NOT EXISTS claim_codes_agent_idx ON claim_codes(agent_id);
117
+
118
+ CREATE TABLE IF NOT EXISTS connections (
119
+ id TEXT PRIMARY KEY, agent_a TEXT NOT NULL REFERENCES agents(id),
120
+ agent_b TEXT NOT NULL REFERENCES agents(id), status TEXT NOT NULL DEFAULT 'pending',
121
+ initiated_by TEXT NOT NULL, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL
122
+ );
123
+ CREATE INDEX IF NOT EXISTS connections_agent_a_idx ON connections(agent_a);
124
+ CREATE INDEX IF NOT EXISTS connections_agent_b_idx ON connections(agent_b);
125
+
126
+ CREATE TABLE IF NOT EXISTS delivery_jobs (
127
+ id TEXT PRIMARY KEY, message_id TEXT NOT NULL, payload TEXT NOT NULL,
128
+ status TEXT NOT NULL DEFAULT 'pending', attempts INTEGER NOT NULL DEFAULT 0,
129
+ max_attempts INTEGER NOT NULL DEFAULT 6, next_attempt_at INTEGER NOT NULL,
130
+ last_error TEXT, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL
131
+ );
132
+ CREATE INDEX IF NOT EXISTS idx_delivery_jobs_status ON delivery_jobs(status, next_attempt_at);
133
+
134
+ CREATE TABLE IF NOT EXISTS dead_letters (
135
+ id TEXT PRIMARY KEY, message_id TEXT NOT NULL, job_id TEXT NOT NULL,
136
+ reason TEXT NOT NULL, attempts INTEGER NOT NULL, created_at INTEGER NOT NULL
137
+ );
138
+ CREATE INDEX IF NOT EXISTS idx_dead_letters_message ON dead_letters(message_id);
139
+
140
+ CREATE VIRTUAL TABLE IF NOT EXISTS messages_fts USING fts5(content, from_agent_id, content='messages', content_rowid='rowid');
141
+ INSERT INTO messages_fts(messages_fts) VALUES('rebuild');
142
+ CREATE TRIGGER IF NOT EXISTS messages_ai AFTER INSERT ON messages BEGIN
143
+ INSERT INTO messages_fts(rowid, content, from_agent_id) VALUES (new.rowid, new.content, new.from_agent_id);
144
+ END;
145
+ CREATE TRIGGER IF NOT EXISTS messages_ad AFTER DELETE ON messages BEGIN
146
+ INSERT INTO messages_fts(messages_fts, rowid, content, from_agent_id) VALUES('delete', old.rowid, old.content, old.from_agent_id);
147
+ END;
148
+ CREATE TRIGGER IF NOT EXISTS messages_au AFTER UPDATE ON messages BEGIN
149
+ INSERT INTO messages_fts(messages_fts, rowid, content, from_agent_id) VALUES('delete', old.rowid, old.content, old.from_agent_id);
150
+ INSERT INTO messages_fts(rowid, content, from_agent_id) VALUES (new.rowid, new.content, new.from_agent_id);
151
+ END;
152
+
153
+ CREATE TABLE IF NOT EXISTS message_pins (
154
+ id TEXT PRIMARY KEY, message_id TEXT NOT NULL, conversation_id TEXT NOT NULL,
155
+ pinned_by TEXT NOT NULL, note TEXT, created_at INTEGER NOT NULL, UNIQUE(message_id, pinned_by)
156
+ );
157
+ CREATE INDEX IF NOT EXISTS idx_pins_conversation ON message_pins(conversation_id);
158
+ CREATE INDEX IF NOT EXISTS idx_pins_agent ON message_pins(pinned_by);
159
+
160
+ CREATE TABLE IF NOT EXISTS attachments (
161
+ slug TEXT PRIMARY KEY, conversation_id TEXT NOT NULL, from_agent_id TEXT NOT NULL,
162
+ content BLOB NOT NULL, original_size INTEGER NOT NULL, compressed_size INTEGER NOT NULL,
163
+ content_hash TEXT NOT NULL, format TEXT NOT NULL DEFAULT 'text', title TEXT,
164
+ tokens INTEGER NOT NULL DEFAULT 0, expires_at INTEGER NOT NULL DEFAULT 0,
165
+ storage_key TEXT, mode TEXT NOT NULL DEFAULT 'draft',
166
+ version_count INTEGER NOT NULL DEFAULT 1, current_version INTEGER NOT NULL DEFAULT 1,
167
+ created_at INTEGER NOT NULL
168
+ );
169
+ CREATE INDEX IF NOT EXISTS attachments_conversation_idx ON attachments(conversation_id);
170
+ CREATE INDEX IF NOT EXISTS attachments_agent_idx ON attachments(from_agent_id);
171
+
172
+ CREATE TABLE IF NOT EXISTS capabilities (
173
+ id TEXT PRIMARY KEY, slug TEXT NOT NULL UNIQUE, name TEXT NOT NULL,
174
+ description TEXT NOT NULL, category TEXT NOT NULL, created_at INTEGER NOT NULL
175
+ );
176
+ CREATE TABLE IF NOT EXISTS skills (
177
+ id TEXT PRIMARY KEY, slug TEXT NOT NULL UNIQUE, name TEXT NOT NULL,
178
+ description TEXT NOT NULL, category TEXT NOT NULL, created_at INTEGER NOT NULL
179
+ );
180
+ CREATE TABLE IF NOT EXISTS agent_capabilities (
181
+ agent_id TEXT NOT NULL REFERENCES agents(id), capability_id TEXT NOT NULL REFERENCES capabilities(id),
182
+ PRIMARY KEY (agent_id, capability_id)
183
+ );
184
+ CREATE TABLE IF NOT EXISTS agent_skills (
185
+ agent_id TEXT NOT NULL REFERENCES agents(id), skill_id TEXT NOT NULL REFERENCES skills(id),
186
+ PRIMARY KEY (agent_id, skill_id)
187
+ );
188
+
189
+ CREATE TABLE IF NOT EXISTS accounts (
190
+ id TEXT PRIMARY KEY NOT NULL, user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
191
+ account_id TEXT NOT NULL, provider_id TEXT NOT NULL, access_token TEXT, refresh_token TEXT,
192
+ id_token TEXT, access_token_expires_at TEXT, refresh_token_expires_at TEXT, scope TEXT,
193
+ password TEXT, created_at TEXT NOT NULL, updated_at TEXT NOT NULL
194
+ );
195
+ CREATE INDEX IF NOT EXISTS idx_accounts_user_id ON accounts(user_id);
196
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_accounts_provider ON accounts(provider_id, account_id);
197
+
198
+ CREATE TABLE IF NOT EXISTS sessions (
199
+ id TEXT PRIMARY KEY NOT NULL, user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
200
+ token TEXT NOT NULL UNIQUE, ip_address TEXT, user_agent TEXT, expires_at TEXT NOT NULL,
201
+ active_organization_id TEXT, impersonated_by TEXT, active INTEGER NOT NULL DEFAULT 1,
202
+ created_at TEXT NOT NULL, updated_at TEXT NOT NULL
203
+ );
204
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_sessions_token ON sessions(token);
205
+ CREATE INDEX IF NOT EXISTS idx_sessions_user_id ON sessions(user_id);
206
+
207
+ CREATE TABLE IF NOT EXISTS verifications (
208
+ id TEXT PRIMARY KEY NOT NULL, identifier TEXT NOT NULL, value TEXT NOT NULL,
209
+ expires_at TEXT NOT NULL, created_at TEXT NOT NULL, updated_at TEXT NOT NULL
210
+ );
211
+ CREATE INDEX IF NOT EXISTS idx_verifications_identifier ON verifications(identifier);
212
+
213
+ CREATE TABLE IF NOT EXISTS org_agent_keys (
214
+ id TEXT PRIMARY KEY NOT NULL, organization_id TEXT NOT NULL REFERENCES organization(id) ON DELETE CASCADE,
215
+ agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
216
+ created_by TEXT NOT NULL, created_at INTEGER NOT NULL
217
+ );
218
+ CREATE INDEX IF NOT EXISTS org_agent_keys_org_idx ON org_agent_keys(organization_id);
219
+ CREATE INDEX IF NOT EXISTS org_agent_keys_agent_idx ON org_agent_keys(agent_id);
220
+
221
+ CREATE TABLE IF NOT EXISTS attachment_versions (
222
+ id TEXT PRIMARY KEY, slug TEXT NOT NULL REFERENCES attachments(slug) ON DELETE CASCADE,
223
+ version_number INTEGER NOT NULL, author_agent_id TEXT NOT NULL,
224
+ change_type TEXT NOT NULL DEFAULT 'patch', patch_text TEXT, storage_key TEXT NOT NULL,
225
+ content_hash TEXT NOT NULL, original_size INTEGER NOT NULL, compressed_size INTEGER NOT NULL,
226
+ tokens INTEGER NOT NULL, change_summary TEXT, sections_modified TEXT NOT NULL DEFAULT '[]',
227
+ tokens_added INTEGER NOT NULL DEFAULT 0, tokens_removed INTEGER NOT NULL DEFAULT 0,
228
+ created_at INTEGER NOT NULL, UNIQUE(slug, version_number)
229
+ );
230
+ CREATE INDEX IF NOT EXISTS idx_attachment_versions_slug ON attachment_versions(slug);
231
+ CREATE INDEX IF NOT EXISTS idx_attachment_versions_author ON attachment_versions(author_agent_id);
232
+
233
+ CREATE TABLE IF NOT EXISTS attachment_approvals (
234
+ id TEXT PRIMARY KEY, slug TEXT NOT NULL REFERENCES attachments(slug) ON DELETE CASCADE,
235
+ reviewer_agent_id TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'pending', comment TEXT,
236
+ version_reviewed INTEGER NOT NULL, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL,
237
+ UNIQUE(slug, reviewer_agent_id)
238
+ );
239
+ CREATE INDEX IF NOT EXISTS idx_attachment_approvals_slug ON attachment_approvals(slug);
240
+
241
+ CREATE TABLE IF NOT EXISTS attachment_contributors (
242
+ slug TEXT NOT NULL REFERENCES attachments(slug) ON DELETE CASCADE,
243
+ agent_id TEXT NOT NULL, version_count INTEGER NOT NULL DEFAULT 0,
244
+ total_tokens_added INTEGER NOT NULL DEFAULT 0, total_tokens_removed INTEGER NOT NULL DEFAULT 0,
245
+ first_contribution_at INTEGER NOT NULL, last_contribution_at INTEGER NOT NULL,
246
+ PRIMARY KEY (slug, agent_id)
247
+ );`,
248
+ },
249
+ {
250
+ name: '2026-03-30-000001_agent_connections',
251
+ sql: `-- Add transport_type to agents table for connection mode classification.
252
+ ALTER TABLE agents ADD COLUMN transport_type TEXT NOT NULL DEFAULT 'http';
253
+ CREATE INDEX idx_agents_transport_type ON agents(transport_type);
254
+
255
+ CREATE TABLE agent_connections (
256
+ id TEXT PRIMARY KEY NOT NULL, agent_id TEXT NOT NULL,
257
+ transport_type TEXT NOT NULL DEFAULT 'http', connection_id TEXT,
258
+ connected_at BIGINT NOT NULL, last_heartbeat BIGINT NOT NULL,
259
+ connection_metadata TEXT, created_at BIGINT NOT NULL,
260
+ FOREIGN KEY (agent_id) REFERENCES agents(agent_id) ON DELETE CASCADE,
261
+ UNIQUE(agent_id, connection_id)
262
+ );
263
+ CREATE INDEX idx_agent_connections_agent ON agent_connections(agent_id);
264
+ CREATE INDEX idx_agent_connections_transport ON agent_connections(transport_type);
265
+ CREATE INDEX idx_agent_connections_heartbeat ON agent_connections(last_heartbeat);`,
266
+ },
267
+ {
268
+ name: '2026-03-31-000001_agent_credentials',
269
+ sql: `-- Move agent credentials into signaldock.db agents table (T234 clean-cut).
270
+ ALTER TABLE agents ADD COLUMN api_key_encrypted TEXT;
271
+ ALTER TABLE agents ADD COLUMN api_base_url TEXT NOT NULL DEFAULT 'https://api.signaldock.io';
272
+ ALTER TABLE agents ADD COLUMN classification TEXT;
273
+ ALTER TABLE agents ADD COLUMN transport_config TEXT NOT NULL DEFAULT '{}';
274
+ ALTER TABLE agents ADD COLUMN is_active INTEGER NOT NULL DEFAULT 1;
275
+ ALTER TABLE agents ADD COLUMN last_used_at INTEGER;
276
+ CREATE INDEX IF NOT EXISTS idx_agents_is_active ON agents(is_active);
277
+ CREATE INDEX IF NOT EXISTS idx_agents_last_used ON agents(last_used_at);`,
278
+ },
279
+ ];
280
+
281
+ /**
282
+ * Ensure signaldock.db exists and has the full schema applied.
283
+ *
284
+ * Idempotent — safe to call multiple times. Uses `CREATE TABLE IF NOT EXISTS`
285
+ * and `CREATE INDEX IF NOT EXISTS` throughout.
286
+ *
287
+ * @returns Object with action ('created' | 'exists') and the database path.
288
+ */
289
+ export async function ensureSignaldockDb(
290
+ cwd?: string,
291
+ ): Promise<{ action: 'created' | 'exists'; path: string }> {
292
+ const dbPath = getSignaldockDbPath(cwd);
293
+ const alreadyExists = existsSync(dbPath);
294
+
295
+ // Ensure parent directory exists
296
+ mkdirSync(dirname(dbPath), { recursive: true });
297
+
298
+ // Open or create the database
299
+ const db = new DatabaseSyncClass(dbPath);
300
+
301
+ try {
302
+ // Set pragmas for optimal performance
303
+ db.exec('PRAGMA journal_mode = WAL');
304
+ db.exec('PRAGMA busy_timeout = 5000');
305
+ db.exec('PRAGMA synchronous = NORMAL');
306
+ db.exec('PRAGMA foreign_keys = ON');
307
+ db.exec('PRAGMA cache_size = -64000'); // 64MB
308
+
309
+ // Check if schema already applied (agents table as sentinel)
310
+ const hasSchema = (() => {
311
+ try {
312
+ const result = db
313
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='agents'")
314
+ .get() as { name: string } | undefined;
315
+ return !!result;
316
+ } catch {
317
+ return false;
318
+ }
319
+ })();
320
+
321
+ // Ensure migration tracking tables exist
322
+ db.exec(`
323
+ CREATE TABLE IF NOT EXISTS _signaldock_meta (
324
+ key TEXT PRIMARY KEY,
325
+ value TEXT NOT NULL,
326
+ updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
327
+ )
328
+ `);
329
+ db.exec(`
330
+ CREATE TABLE IF NOT EXISTS _signaldock_migrations (
331
+ name TEXT PRIMARY KEY,
332
+ applied_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
333
+ )
334
+ `);
335
+
336
+ // Apply embedded migrations (works in ANY project, not just monorepo)
337
+ for (const migration of EMBEDDED_MIGRATIONS) {
338
+ // Skip already-applied migrations
339
+ const applied = db
340
+ .prepare('SELECT name FROM _signaldock_migrations WHERE name = ?')
341
+ .get(migration.name) as { name: string } | undefined;
342
+ if (applied) continue;
343
+
344
+ db.exec('BEGIN TRANSACTION');
345
+ try {
346
+ db.exec(migration.sql);
347
+ db.prepare('INSERT INTO _signaldock_migrations (name) VALUES (?)').run(migration.name);
348
+ db.exec('COMMIT');
349
+ } catch (err) {
350
+ db.exec('ROLLBACK');
351
+ throw err;
352
+ }
353
+ }
354
+
355
+ // Record schema version
356
+ db.exec(`
357
+ INSERT OR REPLACE INTO _signaldock_meta (key, value, updated_at)
358
+ VALUES ('schema_version', '${SIGNALDOCK_SCHEMA_VERSION}', strftime('%s', 'now'))
359
+ `);
360
+
361
+ return {
362
+ action: alreadyExists && hasSchema ? 'exists' : 'created',
363
+ path: dbPath,
364
+ };
365
+ } finally {
366
+ db.close();
367
+ }
368
+ }
369
+
370
+ /**
371
+ * Check signaldock.db health — table count, WAL mode, schema version.
372
+ *
373
+ * Used by `cleo doctor` to verify signaldock.db integrity.
374
+ *
375
+ * @returns Health report object or null if DB doesn't exist.
376
+ */
377
+ export async function checkSignaldockDbHealth(cwd?: string): Promise<{
378
+ exists: boolean;
379
+ path: string;
380
+ tableCount: number;
381
+ walMode: boolean;
382
+ schemaVersion: string | null;
383
+ foreignKeysEnabled: boolean;
384
+ } | null> {
385
+ const dbPath = getSignaldockDbPath(cwd);
386
+ if (!existsSync(dbPath)) {
387
+ return {
388
+ exists: false,
389
+ path: dbPath,
390
+ tableCount: 0,
391
+ walMode: false,
392
+ schemaVersion: null,
393
+ foreignKeysEnabled: false,
394
+ };
395
+ }
396
+
397
+ const db = new DatabaseSyncClass(dbPath);
398
+ try {
399
+ const tables = db
400
+ .prepare(
401
+ "SELECT COUNT(*) as count FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'",
402
+ )
403
+ .get() as { count: number };
404
+
405
+ const journalMode = db.prepare('PRAGMA journal_mode').get() as { journal_mode: string };
406
+ const fkEnabled = db.prepare('PRAGMA foreign_keys').get() as { foreign_keys: number };
407
+
408
+ let schemaVersion: string | null = null;
409
+ try {
410
+ const meta = db
411
+ .prepare("SELECT value FROM _signaldock_meta WHERE key = 'schema_version'")
412
+ .get() as { value: string } | undefined;
413
+ schemaVersion = meta?.value ?? null;
414
+ } catch {
415
+ // Meta table may not exist
416
+ }
417
+
418
+ return {
419
+ exists: true,
420
+ path: dbPath,
421
+ tableCount: tables.count,
422
+ walMode: journalMode.journal_mode === 'wal',
423
+ schemaVersion,
424
+ foreignKeysEnabled: fkEnabled.foreign_keys === 1,
425
+ };
426
+ } finally {
427
+ db.close();
428
+ }
429
+ }
@@ -14,6 +14,7 @@
14
14
  import type { Session, Task, TaskStatus } from '@cleocode/contracts';
15
15
  import { and, eq, inArray, isNull, like, ne, notInArray, or, sql } from 'drizzle-orm';
16
16
  import { archivedTaskToRow, rowToSession, rowToTask, taskToRow } from './converters.js';
17
+ import { cleanupBrainRefsOnSessionDelete } from './cross-db-cleanup.js';
17
18
  import type {
18
19
  ArchiveFile,
19
20
  DataAccessor,
@@ -380,6 +381,8 @@ export async function createSqliteDataAccessor(cwd?: string): Promise<DataAccess
380
381
  async removeSingleSession(sessionId: string): Promise<void> {
381
382
  const db = await getDb(cwd);
382
383
  await db.delete(schema.sessions).where(eq(schema.sessions.id, sessionId)).run();
384
+ // Best-effort cross-db cleanup: nullify brain.db references to this session
385
+ void cleanupBrainRefsOnSessionDelete(sessionId, cwd);
383
386
  },
384
387
 
385
388
  // ---- Targeted query methods (Phase 2 modernization) ----
@@ -783,6 +786,7 @@ export async function createSqliteDataAccessor(cwd?: string): Promise<DataAccess
783
786
  ['createdBy', 'createdBy'],
784
787
  ['modifiedBy', 'modifiedBy'],
785
788
  ['sessionId', 'sessionId'],
789
+ ['assignee', 'assignee'],
786
790
  ];
787
791
 
788
792
  for (const [key, col] of fieldMap) {
@@ -874,6 +878,74 @@ export async function createSqliteDataAccessor(cwd?: string): Promise<DataAccess
874
878
  const meta = await getMetaValue<{ schemaVersion?: string }>(cwd, 'file_meta');
875
879
  return meta?.schemaVersion ?? null;
876
880
  },
881
+
882
+ // ---- Agent instances ----
883
+
884
+ async listAgentInstances(filters) {
885
+ const { listAgentInstances: listAgents } = await import('../agents/registry.js');
886
+ // Cast generic string filters to the specific union types expected by the agents module
887
+ return listAgents(filters as Parameters<typeof listAgents>[0], cwd);
888
+ },
889
+
890
+ async getAgentInstance(agentId) {
891
+ const { getAgentInstance: getAgent } = await import('../agents/registry.js');
892
+ return getAgent(agentId, cwd);
893
+ },
894
+
895
+ // ---- Agent task claiming ----
896
+
897
+ async claimTask(taskId: string, agentId: string): Promise<void> {
898
+ const nativeDb = getNativeTasksDb();
899
+ if (!nativeDb) {
900
+ throw new Error('Native database not initialized');
901
+ }
902
+
903
+ // Verify the task exists first
904
+ const existsRow = nativeDb.prepare('SELECT assignee FROM tasks WHERE id = ?').get(taskId) as
905
+ | { assignee: string | null }
906
+ | undefined;
907
+ if (!existsRow) {
908
+ throw new Error(`Task not found: ${taskId}`);
909
+ }
910
+
911
+ // Atomic claim: only succeeds if assignee IS NULL or already claimed by this agent.
912
+ // This prevents race conditions between concurrent agents.
913
+ const result = nativeDb
914
+ .prepare(
915
+ 'UPDATE tasks SET assignee = ?, updated_at = ? WHERE id = ? AND (assignee IS NULL OR assignee = ?)',
916
+ )
917
+ .run(agentId, new Date().toISOString(), taskId, agentId) as { changes: number };
918
+
919
+ if (result.changes === 0) {
920
+ // Row was not updated — task is claimed by a different agent
921
+ const currentRow = nativeDb
922
+ .prepare('SELECT assignee FROM tasks WHERE id = ?')
923
+ .get(taskId) as { assignee: string | null } | undefined;
924
+ throw new Error(
925
+ `Task ${taskId} is already claimed by agent: ${currentRow?.assignee ?? 'unknown'}`,
926
+ );
927
+ }
928
+ },
929
+
930
+ async unclaimTask(taskId: string): Promise<void> {
931
+ const nativeDb = getNativeTasksDb();
932
+ if (!nativeDb) {
933
+ throw new Error('Native database not initialized');
934
+ }
935
+
936
+ // Verify the task exists
937
+ const existsRow = nativeDb.prepare('SELECT id FROM tasks WHERE id = ?').get(taskId) as
938
+ | { id: string }
939
+ | undefined;
940
+ if (!existsRow) {
941
+ throw new Error(`Task not found: ${taskId}`);
942
+ }
943
+
944
+ // Clear the assignee — no-op if already null
945
+ nativeDb
946
+ .prepare('UPDATE tasks SET assignee = NULL, updated_at = ? WHERE id = ?')
947
+ .run(new Date().toISOString(), taskId);
948
+ },
877
949
  };
878
950
 
879
951
  return accessor;
@@ -403,7 +403,10 @@ export { isSqliteBusy } from './migration-manager.js';
403
403
  */
404
404
  import type { RequiredColumn } from './migration-manager.js';
405
405
 
406
- const REQUIRED_TASK_COLUMNS: RequiredColumn[] = [{ name: 'pipeline_stage', ddl: 'text' }];
406
+ const REQUIRED_TASK_COLUMNS: RequiredColumn[] = [
407
+ { name: 'pipeline_stage', ddl: 'text' },
408
+ { name: 'assignee', ddl: 'text' },
409
+ ];
407
410
 
408
411
  /**
409
412
  * Required columns that MUST exist on the sessions table before the
@@ -78,7 +78,14 @@ export async function updateTask(
78
78
 
79
79
  if (updates.title !== undefined) updateRow.title = updates.title;
80
80
  if (updates.description !== undefined) updateRow.description = updates.description;
81
- if (updates.status !== undefined) updateRow.status = updates.status;
81
+ if (updates.status !== undefined) {
82
+ updateRow.status = updates.status;
83
+ // Clear stale timestamps when status resets to non-terminal state
84
+ if (updates.status === 'pending' || updates.status === 'active') {
85
+ if (updates.cancelledAt === undefined) updateRow.cancelledAt = null;
86
+ if (updates.completedAt === undefined) updateRow.completedAt = null;
87
+ }
88
+ }
82
89
  if (updates.priority !== undefined) updateRow.priority = updates.priority;
83
90
  if (updates.type !== undefined) updateRow.type = updates.type;
84
91
  if (updates.parentId !== undefined) updateRow.parentId = updates.parentId;
@@ -99,6 +106,7 @@ export async function updateTask(
99
106
  updateRow.cancellationReason = updates.cancellationReason;
100
107
  if (updates.verification !== undefined)
101
108
  updateRow.verificationJson = JSON.stringify(updates.verification);
109
+ if (updates.assignee !== undefined) updateRow.assignee = updates.assignee;
102
110
 
103
111
  db.update(schema.tasks).set(updateRow).where(eq(schema.tasks.id, taskId)).run();
104
112
 
@@ -196,6 +196,8 @@ export const tasks = sqliteTable(
196
196
  // Not referencing lifecycle_stages.id so that stage binding works without
197
197
  // requiring a lifecycle pipeline record for every task.
198
198
  pipelineStage: text('pipeline_stage'),
199
+ /** Agent ID that has claimed/is assigned to this task. */
200
+ assignee: text('assignee'),
199
201
  },
200
202
  (table) => [
201
203
  index('idx_tasks_status').on(table.status),
@@ -205,6 +207,7 @@ export const tasks = sqliteTable(
205
207
  index('idx_tasks_priority').on(table.priority),
206
208
  index('idx_tasks_session_id').on(table.sessionId),
207
209
  index('idx_tasks_pipeline_stage').on(table.pipelineStage),
210
+ index('idx_tasks_assignee').on(table.assignee),
208
211
  // T033 composite indexes
209
212
  index('idx_tasks_parent_status').on(table.parentId, table.status),
210
213
  index('idx_tasks_status_priority').on(table.status, table.priority),
@@ -850,3 +853,7 @@ export type ReleaseManifestRow = typeof releaseManifests.$inferSelect;
850
853
  export type NewReleaseManifestRow = typeof releaseManifests.$inferInsert;
851
854
  export type ExternalTaskLinkRow = typeof externalTaskLinks.$inferSelect;
852
855
  export type NewExternalTaskLinkRow = typeof externalTaskLinks.$inferInsert;
856
+
857
+ // agent_credentials REMOVED from tasks.db — T234 clean-cut.
858
+ // Agent data (identity, credentials, capabilities, skills) now lives
859
+ // exclusively in signaldock.db. See AgentRegistryAccessor.
@@ -27,7 +27,7 @@ describe('system health audit_log checks', () => {
27
27
  await getDb(projectRoot);
28
28
  closeDb();
29
29
 
30
- const result = getSystemHealth(projectRoot);
30
+ const result = await getSystemHealth(projectRoot);
31
31
  const auditLog = result.checks.find((c) => c.name === 'audit_log');
32
32
 
33
33
  expect(auditLog).toBeDefined();
@@ -45,7 +45,7 @@ describe('system health audit_log checks', () => {
45
45
  db.close();
46
46
  expect(existsSync(dbPath)).toBe(true);
47
47
 
48
- const result = getSystemHealth(projectRoot);
48
+ const result = await getSystemHealth(projectRoot);
49
49
  const auditLog = result.checks.find((c) => c.name === 'audit_log');
50
50
 
51
51
  expect(auditLog).toBeDefined();
@@ -145,7 +145,7 @@ export interface DiagnosticsResult {
145
145
  }
146
146
 
147
147
  /** Run system health checks (SQLite-first per ADR-006). */
148
- export function getSystemHealth(projectRoot: string, opts?: { detailed?: boolean }): HealthResult {
148
+ export async function getSystemHealth(projectRoot: string, opts?: { detailed?: boolean }): Promise<HealthResult> {
149
149
  const cleoDir = join(projectRoot, '.cleo');
150
150
  const checks: HealthCheck[] = [];
151
151
 
@@ -181,6 +181,58 @@ export function getSystemHealth(projectRoot: string, opts?: { detailed?: boolean
181
181
  checks.push(checkAuditLogAvailability(dbPath));
182
182
  }
183
183
 
184
+ // Check signaldock.db with schema health (local agent messaging per T225)
185
+ const sdDbPath = join(cleoDir, 'signaldock.db');
186
+ if (existsSync(sdDbPath)) {
187
+ try {
188
+ const { DatabaseSync: SdDb } = _require('node:sqlite') as {
189
+ DatabaseSync: new (path: string) => {
190
+ prepare(sql: string): { get(...args: unknown[]): unknown };
191
+ close(): void;
192
+ };
193
+ };
194
+ const sdDb = new SdDb(sdDbPath);
195
+ try {
196
+ const tables = sdDb
197
+ .prepare(
198
+ "SELECT COUNT(*) as count FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '_signaldock%'",
199
+ )
200
+ .get() as { count: number };
201
+ const journalMode = sdDb.prepare('PRAGMA journal_mode').get() as {
202
+ journal_mode: string;
203
+ };
204
+ let schemaVersion = 'unknown';
205
+ try {
206
+ const meta = sdDb
207
+ .prepare("SELECT value FROM _signaldock_meta WHERE key = 'schema_version'")
208
+ .get() as { value: string } | undefined;
209
+ schemaVersion = meta?.value ?? 'unknown';
210
+ } catch {
211
+ /* meta table may not exist */
212
+ }
213
+ checks.push({
214
+ name: 'signaldock_db',
215
+ status: tables.count >= 20 ? 'pass' : 'warn',
216
+ message: `signaldock.db: ${tables.count} tables, ${journalMode.journal_mode}, v${schemaVersion}`,
217
+ });
218
+ } finally {
219
+ sdDb.close();
220
+ }
221
+ } catch {
222
+ checks.push({
223
+ name: 'signaldock_db',
224
+ status: 'pass',
225
+ message: `signaldock.db: ${statSync(sdDbPath).size} bytes`,
226
+ });
227
+ }
228
+ } else {
229
+ checks.push({
230
+ name: 'signaldock_db',
231
+ status: 'warn',
232
+ message: 'signaldock.db not found. Run: cleo init',
233
+ });
234
+ }
235
+
184
236
  // Check config.json (config remains JSON per ADR-006)
185
237
  const configPath = join(cleoDir, 'config.json');
186
238
  if (existsSync(configPath)) {
@@ -264,7 +316,7 @@ export async function getSystemDiagnostics(
264
316
  projectRoot: string,
265
317
  opts?: { checks?: string[] },
266
318
  ): Promise<DiagnosticsResult> {
267
- const healthResult = getSystemHealth(projectRoot, { detailed: true });
319
+ const healthResult = await getSystemHealth(projectRoot, { detailed: true });
268
320
 
269
321
  const diagChecks: DiagnosticsCheck[] = healthResult.checks.map((c) => ({
270
322
  name: c.name,