@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,320 @@
1
+ /**
2
+ * LocalTransport test suite.
3
+ *
4
+ * Tests the in-process SQLite transport for fully offline agent messaging.
5
+ * Uses a temporary signaldock.db created via ensureSignaldockDb().
6
+ *
7
+ * @see packages/core/src/conduit/local-transport.ts
8
+ * @task T213
9
+ */
10
+
11
+ import { existsSync, mkdirSync, rmSync } from 'node:fs';
12
+ import { tmpdir } from 'node:os';
13
+ import { join } from 'node:path';
14
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
15
+ import { ensureSignaldockDb } from '../../store/signaldock-sqlite.js';
16
+ import { LocalTransport } from '../local-transport.js';
17
+
18
+ // ============================================================================
19
+ // Test helpers
20
+ // ============================================================================
21
+
22
+ let testDir: string;
23
+ let originalCwd: string;
24
+
25
+ /** Create a temporary directory with a valid signaldock.db for testing. */
26
+ async function setupTestDb(): Promise<string> {
27
+ const dir = join(
28
+ tmpdir(),
29
+ `local-transport-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
30
+ );
31
+ mkdirSync(join(dir, '.cleo'), { recursive: true });
32
+ await ensureSignaldockDb(dir);
33
+ return dir;
34
+ }
35
+
36
+ /** Standard connect config for tests. */
37
+ function testConfig(agentId = 'test-agent') {
38
+ return {
39
+ agentId,
40
+ apiKey: 'sk_test_fake',
41
+ apiBaseUrl: 'http://localhost:4000',
42
+ };
43
+ }
44
+
45
+ // ============================================================================
46
+ // Test suite
47
+ // ============================================================================
48
+
49
+ describe('LocalTransport', () => {
50
+ beforeEach(async () => {
51
+ originalCwd = process.cwd();
52
+ testDir = await setupTestDb();
53
+ process.chdir(testDir);
54
+ });
55
+
56
+ afterEach(() => {
57
+ process.chdir(originalCwd);
58
+ if (testDir && existsSync(testDir)) {
59
+ rmSync(testDir, { recursive: true, force: true });
60
+ }
61
+ });
62
+
63
+ // --------------------------------------------------------------------------
64
+ // Connection
65
+ // --------------------------------------------------------------------------
66
+
67
+ describe('connect', () => {
68
+ it('connects when signaldock.db exists', async () => {
69
+ const transport = new LocalTransport();
70
+ await transport.connect(testConfig());
71
+ await transport.disconnect();
72
+ });
73
+
74
+ it('throws when signaldock.db is missing', async () => {
75
+ const emptyDir = join(tmpdir(), `empty-${Date.now()}`);
76
+ mkdirSync(join(emptyDir, '.cleo'), { recursive: true });
77
+ process.chdir(emptyDir);
78
+
79
+ const transport = new LocalTransport();
80
+ await expect(transport.connect(testConfig())).rejects.toThrow('signaldock.db not found');
81
+
82
+ rmSync(emptyDir, { recursive: true, force: true });
83
+ });
84
+
85
+ it('has name "local"', () => {
86
+ const transport = new LocalTransport();
87
+ expect(transport.name).toBe('local');
88
+ });
89
+ });
90
+
91
+ // --------------------------------------------------------------------------
92
+ // Push
93
+ // --------------------------------------------------------------------------
94
+
95
+ describe('push', () => {
96
+ it('stores a direct message and returns a message ID', async () => {
97
+ const transport = new LocalTransport();
98
+ await transport.connect(testConfig('sender'));
99
+
100
+ const result = await transport.push('receiver', 'hello from sender');
101
+ expect(result.messageId).toBeDefined();
102
+ expect(typeof result.messageId).toBe('string');
103
+ expect(result.messageId.length).toBeGreaterThan(0);
104
+
105
+ await transport.disconnect();
106
+ });
107
+
108
+ it('stores a message in a conversation', async () => {
109
+ const sender = new LocalTransport();
110
+ const receiver = new LocalTransport();
111
+ await sender.connect(testConfig('sender'));
112
+ await receiver.connect(testConfig('receiver'));
113
+
114
+ // First push creates a DM conversation, then use that ID
115
+ const dm = await sender.push('receiver', 'setup');
116
+ const messages = await receiver.poll();
117
+ expect(messages).toHaveLength(1);
118
+ const convId = messages[0].threadId;
119
+ expect(convId).toBeDefined();
120
+
121
+ // Now send within that conversation
122
+ const result = await sender.push('receiver', 'group msg', {
123
+ conversationId: convId,
124
+ });
125
+ expect(result.messageId).toBeDefined();
126
+
127
+ await sender.disconnect();
128
+ await receiver.disconnect();
129
+ });
130
+
131
+ it('throws when not connected', async () => {
132
+ const transport = new LocalTransport();
133
+ await expect(transport.push('to', 'content')).rejects.toThrow('not connected');
134
+ });
135
+ });
136
+
137
+ // --------------------------------------------------------------------------
138
+ // Poll
139
+ // --------------------------------------------------------------------------
140
+
141
+ describe('poll', () => {
142
+ it('returns messages addressed to the connected agent', async () => {
143
+ const sender = new LocalTransport();
144
+ const receiver = new LocalTransport();
145
+
146
+ await sender.connect(testConfig('sender'));
147
+ await receiver.connect(testConfig('receiver'));
148
+
149
+ await sender.push('receiver', 'message 1');
150
+ await sender.push('receiver', 'message 2');
151
+
152
+ const messages = await receiver.poll();
153
+ expect(messages).toHaveLength(2);
154
+ expect(messages[0].from).toBe('sender');
155
+ expect(messages[0].content).toBe('message 1');
156
+ expect(messages[1].content).toBe('message 2');
157
+
158
+ await sender.disconnect();
159
+ await receiver.disconnect();
160
+ });
161
+
162
+ it('returns empty array when no messages', async () => {
163
+ const transport = new LocalTransport();
164
+ await transport.connect(testConfig('lonely'));
165
+
166
+ const messages = await transport.poll();
167
+ expect(messages).toHaveLength(0);
168
+
169
+ await transport.disconnect();
170
+ });
171
+
172
+ it('respects limit parameter', async () => {
173
+ const sender = new LocalTransport();
174
+ const receiver = new LocalTransport();
175
+
176
+ await sender.connect(testConfig('sender'));
177
+ await receiver.connect(testConfig('receiver'));
178
+
179
+ for (let i = 0; i < 5; i++) {
180
+ await sender.push('receiver', `msg ${i}`);
181
+ }
182
+
183
+ const messages = await receiver.poll({ limit: 2 });
184
+ expect(messages).toHaveLength(2);
185
+
186
+ await sender.disconnect();
187
+ await receiver.disconnect();
188
+ });
189
+
190
+ it('returns messages oldest first', async () => {
191
+ const sender = new LocalTransport();
192
+ const receiver = new LocalTransport();
193
+
194
+ await sender.connect(testConfig('sender'));
195
+ await receiver.connect(testConfig('receiver'));
196
+
197
+ await sender.push('receiver', 'first');
198
+ await sender.push('receiver', 'second');
199
+ await sender.push('receiver', 'third');
200
+
201
+ const messages = await receiver.poll();
202
+ expect(messages[0].content).toBe('first');
203
+ expect(messages[2].content).toBe('third');
204
+
205
+ await sender.disconnect();
206
+ await receiver.disconnect();
207
+ });
208
+ });
209
+
210
+ // --------------------------------------------------------------------------
211
+ // Ack
212
+ // --------------------------------------------------------------------------
213
+
214
+ describe('ack', () => {
215
+ it('marks messages as delivered so they are not re-polled', async () => {
216
+ const sender = new LocalTransport();
217
+ const receiver = new LocalTransport();
218
+
219
+ await sender.connect(testConfig('sender'));
220
+ await receiver.connect(testConfig('receiver'));
221
+
222
+ await sender.push('receiver', 'ack me');
223
+ const before = await receiver.poll();
224
+ expect(before).toHaveLength(1);
225
+
226
+ await receiver.ack([before[0].id]);
227
+
228
+ const after = await receiver.poll();
229
+ expect(after).toHaveLength(0);
230
+
231
+ await sender.disconnect();
232
+ await receiver.disconnect();
233
+ });
234
+
235
+ it('handles empty messageIds array', async () => {
236
+ const transport = new LocalTransport();
237
+ await transport.connect(testConfig());
238
+ await transport.ack([]);
239
+ await transport.disconnect();
240
+ });
241
+ });
242
+
243
+ // --------------------------------------------------------------------------
244
+ // Subscribe
245
+ // --------------------------------------------------------------------------
246
+
247
+ describe('subscribe', () => {
248
+ it('notifies subscribers on push', async () => {
249
+ const transport = new LocalTransport();
250
+ await transport.connect(testConfig('agent'));
251
+
252
+ const received: string[] = [];
253
+ const unsub = transport.subscribe((msg) => {
254
+ received.push(msg.content);
255
+ });
256
+
257
+ await transport.push('someone', 'hello');
258
+ expect(received).toContain('hello');
259
+
260
+ unsub();
261
+ await transport.disconnect();
262
+ });
263
+
264
+ it('returns unsubscribe function that stops notifications', async () => {
265
+ const transport = new LocalTransport();
266
+ await transport.connect(testConfig('agent'));
267
+
268
+ const received: string[] = [];
269
+ const unsub = transport.subscribe((msg) => {
270
+ received.push(msg.content);
271
+ });
272
+
273
+ await transport.push('someone', 'before');
274
+ unsub();
275
+ await transport.push('someone', 'after');
276
+
277
+ expect(received).toContain('before');
278
+ expect(received).not.toContain('after');
279
+
280
+ await transport.disconnect();
281
+ });
282
+ });
283
+
284
+ // --------------------------------------------------------------------------
285
+ // Disconnect
286
+ // --------------------------------------------------------------------------
287
+
288
+ describe('disconnect', () => {
289
+ it('clears state and is safe to call twice', async () => {
290
+ const transport = new LocalTransport();
291
+ await transport.connect(testConfig());
292
+ await transport.disconnect();
293
+ await transport.disconnect(); // Should not throw
294
+ });
295
+
296
+ it('makes subsequent operations throw', async () => {
297
+ const transport = new LocalTransport();
298
+ await transport.connect(testConfig());
299
+ await transport.disconnect();
300
+ await expect(transport.poll()).rejects.toThrow('not connected');
301
+ });
302
+ });
303
+
304
+ // --------------------------------------------------------------------------
305
+ // Static
306
+ // --------------------------------------------------------------------------
307
+
308
+ describe('isAvailable', () => {
309
+ it('returns true when signaldock.db exists', () => {
310
+ expect(LocalTransport.isAvailable(testDir)).toBe(true);
311
+ });
312
+
313
+ it('returns false when signaldock.db is missing', () => {
314
+ const emptyDir = join(tmpdir(), `no-db-${Date.now()}`);
315
+ mkdirSync(emptyDir, { recursive: true });
316
+ expect(LocalTransport.isAvailable(emptyDir)).toBe(false);
317
+ rmSync(emptyDir, { recursive: true, force: true });
318
+ });
319
+ });
320
+ });
@@ -0,0 +1,344 @@
1
+ /**
2
+ * SseTransport test scenarios.
3
+ *
4
+ * Tests for the Server-Sent Events transport implementation.
5
+ * SseTransport extends HTTP polling with real-time push via SSE,
6
+ * falling back to HTTP polling when SSE is unavailable.
7
+ *
8
+ * @see packages/contracts/src/transport.ts — Transport interface
9
+ * @see packages/contracts/src/conduit.ts — ConduitState types
10
+ * @task T216
11
+ */
12
+
13
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
14
+
15
+ import { SseTransport } from '../sse-transport.js';
16
+
17
+ // ============================================================================
18
+ // Test helpers
19
+ // ============================================================================
20
+
21
+ /** Mock SSE event source that simulates server-sent events. */
22
+ function createMockEventSource() {
23
+ const handlers: Record<string, ((event: MessageEvent) => void)[]> = {};
24
+ return {
25
+ addEventListener: vi.fn((type: string, handler: (event: MessageEvent) => void) => {
26
+ handlers[type] = handlers[type] || [];
27
+ handlers[type].push(handler);
28
+ }),
29
+ removeEventListener: vi.fn(),
30
+ close: vi.fn(),
31
+ readyState: 0, // CONNECTING
32
+ /** Simulate receiving an SSE event. */
33
+ _emit(type: string, data: string) {
34
+ for (const h of handlers[type] || []) {
35
+ h(new MessageEvent(type, { data }));
36
+ }
37
+ },
38
+ /** Simulate connection open. */
39
+ _open() {
40
+ this.readyState = 1; // OPEN
41
+ for (const h of handlers['open'] || []) {
42
+ h(new MessageEvent('open', { data: '' }));
43
+ }
44
+ },
45
+ /** Simulate connection error. */
46
+ _error() {
47
+ this.readyState = 2; // CLOSED
48
+ for (const h of handlers['error'] || []) {
49
+ h(new MessageEvent('error', { data: '' }));
50
+ }
51
+ },
52
+ };
53
+ }
54
+
55
+ /** Standard test config for SseTransport. */
56
+ const TEST_CONFIG = {
57
+ agentId: 'test-agent',
58
+ apiKey: 'sk_live_test123',
59
+ apiBaseUrl: 'https://api.signaldock.io',
60
+ sseEndpoint: 'https://api.signaldock.io/sse',
61
+ };
62
+
63
+ // ============================================================================
64
+ // Connection lifecycle
65
+ // ============================================================================
66
+
67
+ describe('SseTransport', () => {
68
+ describe('connect', () => {
69
+ it.todo('should establish SSE connection to sseEndpoint');
70
+ // Expected behavior:
71
+ // - Creates EventSource to config.sseEndpoint with auth headers
72
+ // - Sets transport name to 'sse'
73
+ // - Transitions state: disconnected → connecting → connected
74
+ // - Resolves when EventSource 'open' event fires
75
+
76
+ it.todo('should fall back to HTTP polling when SSE endpoint is unreachable');
77
+ // Expected behavior:
78
+ // - Attempts SSE connection
79
+ // - On connection error, transitions to HTTP polling mode
80
+ // - State: disconnected → connecting → connected (via HTTP fallback)
81
+ // - Logs warning: "SSE unavailable, falling back to HTTP polling"
82
+ // - poll() still works via HTTP
83
+
84
+ it.todo('should include auth headers in SSE connection');
85
+ // Expected behavior:
86
+ // - EventSource URL includes ?token=<apiKey> (SSE doesn't support custom headers)
87
+ // - Or uses a pre-flight auth endpoint to get a session token
88
+ // - X-Agent-Id is conveyed via query param or pre-flight
89
+
90
+ it.todo('should reject connect when no sseEndpoint or apiBaseUrl is configured');
91
+ // Expected behavior:
92
+ // - Throws Error with message indicating missing config
93
+ // - State remains 'disconnected'
94
+
95
+ it('rejects connect when no sseEndpoint or apiBaseUrl provided', async () => {
96
+ const transport = new SseTransport();
97
+ await expect(
98
+ transport.connect({ agentId: 'test', apiKey: 'key', apiBaseUrl: '' }),
99
+ ).rejects.toThrow('requires');
100
+ });
101
+
102
+ it.todo('should reject connect when already connected');
103
+ // Expected behavior:
104
+ // - Second connect() call throws or is no-op
105
+ // - Does not create duplicate EventSource instances
106
+
107
+ it('has name "sse"', () => {
108
+ const transport = new SseTransport();
109
+ expect(transport.name).toBe('sse');
110
+ });
111
+ });
112
+
113
+ // ============================================================================
114
+ // Disconnect
115
+ // ============================================================================
116
+
117
+ describe('disconnect', () => {
118
+ it.todo('should close SSE connection and clear state');
119
+ // Expected behavior:
120
+ // - Calls EventSource.close()
121
+ // - Clears internal state
122
+ // - State: connected → disconnected
123
+ // - Subsequent push/poll calls throw "not connected"
124
+
125
+ it.todo('should be idempotent when already disconnected');
126
+ // Expected behavior:
127
+ // - No error when calling disconnect() on already disconnected transport
128
+ // - State remains 'disconnected'
129
+
130
+ it('is safe to call disconnect when not connected', async () => {
131
+ const transport = new SseTransport();
132
+ await transport.disconnect(); // Should not throw
133
+ await transport.disconnect(); // Still should not throw
134
+ });
135
+
136
+ it.todo('should cancel any pending poll timers');
137
+ // Expected behavior:
138
+ // - If HTTP fallback polling is active, clears interval
139
+ // - No orphaned timers after disconnect
140
+ });
141
+
142
+ // ============================================================================
143
+ // Reconnect
144
+ // ============================================================================
145
+
146
+ describe('reconnect', () => {
147
+ it.todo('should automatically reconnect on SSE connection drop');
148
+ // Expected behavior:
149
+ // - When EventSource fires 'error' and readyState becomes CLOSED
150
+ // - State: connected → reconnecting
151
+ // - Exponential backoff: 1s, 2s, 4s, 8s, max 30s
152
+ // - On successful reconnect: state → connected
153
+ // - Messages received during reconnect are not lost (server-side cursor)
154
+
155
+ it.todo('should fall back to HTTP polling after N failed SSE reconnects');
156
+ // Expected behavior:
157
+ // - After 3 failed SSE reconnect attempts
158
+ // - Permanently switches to HTTP polling mode for this session
159
+ // - State: reconnecting → connected (via HTTP)
160
+ // - Logs: "SSE reconnect failed 3 times, switching to HTTP polling"
161
+
162
+ it.todo('should preserve message cursor across reconnect');
163
+ // Expected behavior:
164
+ // - Tracks last received message ID or timestamp
165
+ // - Reconnect URL includes ?lastEventId=<cursor>
166
+ // - Server replays missed messages from cursor
167
+ // - No duplicate messages delivered to consumer
168
+
169
+ it.todo('should emit state change events during reconnect cycle');
170
+ // Expected behavior:
171
+ // - onStateChange fires: connected → reconnecting → connected
172
+ // - Or: connected → reconnecting → error (if max retries exceeded)
173
+ });
174
+
175
+ // ============================================================================
176
+ // Message receive (SSE mode)
177
+ // ============================================================================
178
+
179
+ describe('message receive via SSE', () => {
180
+ it.todo('should deliver incoming SSE messages via poll() return');
181
+ // Expected behavior:
182
+ // - SSE 'message' events are buffered internally
183
+ // - poll() returns buffered messages and clears buffer
184
+ // - Messages conform to ConduitMessage interface
185
+
186
+ it.todo('should parse SSE data field as JSON ConduitMessage');
187
+ // Expected behavior:
188
+ // - SSE data: {"id":"msg-1","from":"agent-a","content":"hello","timestamp":"..."}
189
+ // - Parsed into ConduitMessage with all fields populated
190
+
191
+ it.todo('should handle malformed SSE data gracefully');
192
+ // Expected behavior:
193
+ // - Invalid JSON in SSE data field does not crash transport
194
+ // - Malformed messages are logged and skipped
195
+ // - poll() returns only valid messages
196
+
197
+ it.todo('should filter self-sent messages');
198
+ // Expected behavior:
199
+ // - Messages where from === config.agentId are excluded from poll()
200
+ // - Prevents echo of own messages
201
+
202
+ it.todo('should support SSE event types for different message categories');
203
+ // Expected behavior:
204
+ // - 'message' event: standard agent messages
205
+ // - 'heartbeat' event: keep-alive (ignored in poll output)
206
+ // - 'system' event: system notifications (e.g., agent online/offline)
207
+ });
208
+
209
+ // ============================================================================
210
+ // Send with SSE down (fallback to HTTP)
211
+ // ============================================================================
212
+
213
+ describe('send (push)', () => {
214
+ it.todo('should send messages via HTTP POST regardless of SSE state');
215
+ // Expected behavior:
216
+ // - push() always uses HTTP POST (SSE is receive-only)
217
+ // - POST /conversations/{conversationId}/messages or /agents/{to}/messages
218
+ // - Returns { messageId } from response
219
+ // - Works whether SSE is connected, reconnecting, or fallen back to HTTP
220
+
221
+ it.todo('should send messages when SSE is connected');
222
+ // Expected behavior:
223
+ // - SSE connection is active for receiving
224
+ // - push() uses HTTP POST for sending (SSE is unidirectional)
225
+ // - Both channels work simultaneously
226
+
227
+ it.todo('should send messages when SSE is down and in HTTP fallback mode');
228
+ // Expected behavior:
229
+ // - SSE connection has failed, transport is in HTTP polling mode
230
+ // - push() still works via HTTP POST
231
+ // - No difference in send behavior between SSE and HTTP modes
232
+
233
+ it.todo('should retry failed sends with exponential backoff');
234
+ // Expected behavior:
235
+ // - On HTTP 5xx or network error, retry up to 3 times
236
+ // - Backoff: 500ms, 1s, 2s
237
+ // - On 4xx (client error), fail immediately (no retry)
238
+ // - Returns error after max retries
239
+
240
+ it.todo('should throw when not connected');
241
+ // Expected behavior:
242
+ // - push() before connect() throws "Transport not connected"
243
+ // - push() after disconnect() throws "Transport not connected"
244
+
245
+ it('throws when push is called before connect', async () => {
246
+ const transport = new SseTransport();
247
+ await expect(transport.push('to', 'msg')).rejects.toThrow('not connected');
248
+ });
249
+
250
+ it('throws when poll is called before connect', async () => {
251
+ const transport = new SseTransport();
252
+ await expect(transport.poll()).rejects.toThrow('not connected');
253
+ });
254
+
255
+ it('throws when ack is called before connect', async () => {
256
+ const transport = new SseTransport();
257
+ await expect(transport.ack(['id'])).rejects.toThrow('not connected');
258
+ });
259
+ });
260
+
261
+ // ============================================================================
262
+ // Poll (hybrid mode)
263
+ // ============================================================================
264
+
265
+ describe('poll', () => {
266
+ it.todo('should return SSE-buffered messages when SSE is active');
267
+ // Expected behavior:
268
+ // - In SSE mode, poll() drains the internal message buffer
269
+ // - Does NOT make an HTTP request (messages arrive via SSE push)
270
+ // - Returns empty array if no new messages since last poll
271
+
272
+ it.todo('should fall back to HTTP polling when SSE is down');
273
+ // Expected behavior:
274
+ // - In HTTP fallback mode, poll() makes GET /messages/peek
275
+ // - Behaves identically to HttpTransport.poll()
276
+ // - Respects limit and since options
277
+
278
+ it.todo('should respect since parameter for cursor-based retrieval');
279
+ // Expected behavior:
280
+ // - poll({ since: '2026-03-30T20:00:00Z' }) returns only newer messages
281
+ // - In SSE mode: filters buffer by timestamp
282
+ // - In HTTP mode: passes as query param
283
+ });
284
+
285
+ // ============================================================================
286
+ // Acknowledge
287
+ // ============================================================================
288
+
289
+ describe('ack', () => {
290
+ it.todo('should acknowledge messages via HTTP POST');
291
+ // Expected behavior:
292
+ // - ack(['msg-1', 'msg-2']) sends POST /messages/ack
293
+ // - Works in both SSE and HTTP modes (always HTTP for ack)
294
+ // - Acknowledged messages are not returned by subsequent poll()
295
+ });
296
+
297
+ // ============================================================================
298
+ // Heartbeat
299
+ // ============================================================================
300
+
301
+ describe('heartbeat', () => {
302
+ it.todo('should send heartbeat via HTTP POST');
303
+ // Expected behavior:
304
+ // - POST /agents/{agentId}/heartbeat
305
+ // - Works in both SSE and HTTP modes
306
+ // - Does not depend on SSE connection state
307
+
308
+ it.todo('should detect SSE connection health from heartbeat response');
309
+ // Expected behavior:
310
+ // - Heartbeat response may include SSE connection status
311
+ // - If server reports SSE session expired, trigger reconnect
312
+ });
313
+
314
+ // ============================================================================
315
+ // Edge cases
316
+ // ============================================================================
317
+
318
+ describe('edge cases', () => {
319
+ it.todo('should handle rapid connect/disconnect cycles');
320
+ // Expected behavior:
321
+ // - connect() → disconnect() → connect() in quick succession
322
+ // - No resource leaks (EventSource properly closed)
323
+ // - Final state is consistent
324
+
325
+ it.todo('should handle server-initiated SSE close');
326
+ // Expected behavior:
327
+ // - Server sends SSE close/shutdown event
328
+ // - Transport transitions to reconnecting or HTTP fallback
329
+ // - No unhandled errors
330
+
331
+ it.todo('should handle network going offline then online');
332
+ // Expected behavior:
333
+ // - SSE connection drops (network offline)
334
+ // - Reconnect attempts fail (network offline)
335
+ // - When network returns, reconnect succeeds
336
+ // - Messages buffered server-side are delivered
337
+
338
+ it.todo('should not leak EventSource instances on repeated reconnects');
339
+ // Expected behavior:
340
+ // - Each reconnect properly closes the previous EventSource
341
+ // - No accumulation of open connections
342
+ // - Memory usage stays constant
343
+ });
344
+ });