@united-workforce/cli 0.3.0 → 0.5.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 (319) hide show
  1. package/README.md +45 -11
  2. package/dist/.build-fingerprint +1 -0
  3. package/dist/__tests__/adapter-json-roundtrip.test.js +17 -7
  4. package/dist/__tests__/adapter-json-roundtrip.test.js.map +1 -1
  5. package/dist/__tests__/agent-resolution-llm-free.test.d.ts +2 -0
  6. package/dist/__tests__/agent-resolution-llm-free.test.d.ts.map +1 -0
  7. package/dist/__tests__/agent-resolution-llm-free.test.js +30 -0
  8. package/dist/__tests__/agent-resolution-llm-free.test.js.map +1 -0
  9. package/dist/__tests__/build-step-entry.test.d.ts +2 -0
  10. package/dist/__tests__/build-step-entry.test.d.ts.map +1 -0
  11. package/dist/__tests__/build-step-entry.test.js +173 -0
  12. package/dist/__tests__/build-step-entry.test.js.map +1 -0
  13. package/dist/__tests__/clear-thread-failed-attempts.test.d.ts +2 -0
  14. package/dist/__tests__/clear-thread-failed-attempts.test.d.ts.map +1 -0
  15. package/dist/__tests__/clear-thread-failed-attempts.test.js +93 -0
  16. package/dist/__tests__/clear-thread-failed-attempts.test.js.map +1 -0
  17. package/dist/__tests__/concurrency.test.d.ts +2 -0
  18. package/dist/__tests__/concurrency.test.d.ts.map +1 -0
  19. package/dist/__tests__/concurrency.test.js +196 -0
  20. package/dist/__tests__/concurrency.test.js.map +1 -0
  21. package/dist/__tests__/config.test.js +26 -302
  22. package/dist/__tests__/config.test.js.map +1 -1
  23. package/dist/__tests__/current-role.test.js +7 -6
  24. package/dist/__tests__/current-role.test.js.map +1 -1
  25. package/dist/__tests__/e2e-mock-agent.test.js +43 -30
  26. package/dist/__tests__/e2e-mock-agent.test.js.map +1 -1
  27. package/dist/__tests__/format-text-default.test.d.ts +2 -0
  28. package/dist/__tests__/format-text-default.test.d.ts.map +1 -0
  29. package/dist/__tests__/format-text-default.test.js +43 -0
  30. package/dist/__tests__/format-text-default.test.js.map +1 -0
  31. package/dist/__tests__/format-text-registry.test.d.ts +2 -0
  32. package/dist/__tests__/format-text-registry.test.d.ts.map +1 -0
  33. package/dist/__tests__/format-text-registry.test.js +158 -0
  34. package/dist/__tests__/format-text-registry.test.js.map +1 -0
  35. package/dist/__tests__/issue-180-workflow-ref-removed.test.d.ts +2 -0
  36. package/dist/__tests__/issue-180-workflow-ref-removed.test.d.ts.map +1 -0
  37. package/dist/__tests__/issue-180-workflow-ref-removed.test.js +40 -0
  38. package/dist/__tests__/issue-180-workflow-ref-removed.test.js.map +1 -0
  39. package/dist/__tests__/log-text-renderer.test.d.ts +2 -0
  40. package/dist/__tests__/log-text-renderer.test.d.ts.map +1 -0
  41. package/dist/__tests__/log-text-renderer.test.js +265 -0
  42. package/dist/__tests__/log-text-renderer.test.js.map +1 -0
  43. package/dist/__tests__/moderator-evaluate.test.js +9 -50
  44. package/dist/__tests__/moderator-evaluate.test.js.map +1 -1
  45. package/dist/__tests__/output-mapper-thread-list-startedat.test.d.ts +2 -0
  46. package/dist/__tests__/output-mapper-thread-list-startedat.test.d.ts.map +1 -0
  47. package/dist/__tests__/output-mapper-thread-list-startedat.test.js +102 -0
  48. package/dist/__tests__/output-mapper-thread-list-startedat.test.js.map +1 -0
  49. package/dist/__tests__/output-mapper-workflow-add.test.d.ts +2 -0
  50. package/dist/__tests__/output-mapper-workflow-add.test.d.ts.map +1 -0
  51. package/dist/__tests__/output-mapper-workflow-add.test.js +22 -0
  52. package/dist/__tests__/output-mapper-workflow-add.test.js.map +1 -0
  53. package/dist/__tests__/pid-recycling.test.d.ts +2 -0
  54. package/dist/__tests__/pid-recycling.test.d.ts.map +1 -0
  55. package/dist/__tests__/pid-recycling.test.js +273 -0
  56. package/dist/__tests__/pid-recycling.test.js.map +1 -0
  57. package/dist/__tests__/prompt.test.js +365 -2
  58. package/dist/__tests__/prompt.test.js.map +1 -1
  59. package/dist/__tests__/resolve-head-hash.test.js +12 -4
  60. package/dist/__tests__/resolve-head-hash.test.js.map +1 -1
  61. package/dist/__tests__/setup-agent-discovery.test.js +21 -30
  62. package/dist/__tests__/setup-agent-discovery.test.js.map +1 -1
  63. package/dist/__tests__/setup-complexity.test.js +2 -168
  64. package/dist/__tests__/setup-complexity.test.js.map +1 -1
  65. package/dist/__tests__/setup-no-llm.test.d.ts +2 -0
  66. package/dist/__tests__/setup-no-llm.test.d.ts.map +1 -0
  67. package/dist/__tests__/setup-no-llm.test.js +52 -0
  68. package/dist/__tests__/setup-no-llm.test.js.map +1 -0
  69. package/dist/__tests__/solve-issue-tea-worktree.test.js +27 -28
  70. package/dist/__tests__/solve-issue-tea-worktree.test.js.map +1 -1
  71. package/dist/__tests__/step-ask.test.d.ts +2 -0
  72. package/dist/__tests__/step-ask.test.d.ts.map +1 -0
  73. package/dist/__tests__/step-ask.test.js +507 -0
  74. package/dist/__tests__/step-ask.test.js.map +1 -0
  75. package/dist/__tests__/step-show-json.test.js +1 -0
  76. package/dist/__tests__/step-show-json.test.js.map +1 -1
  77. package/dist/__tests__/step-timing.test.js +2 -0
  78. package/dist/__tests__/step-timing.test.js.map +1 -1
  79. package/dist/__tests__/store-global-cas.test.js +2 -2
  80. package/dist/__tests__/store-global-cas.test.js.map +1 -1
  81. package/dist/__tests__/store-unified-threads.test.js +28 -26
  82. package/dist/__tests__/store-unified-threads.test.js.map +1 -1
  83. package/dist/__tests__/thread-cancel-status.test.js +25 -19
  84. package/dist/__tests__/thread-cancel-status.test.js.map +1 -1
  85. package/dist/__tests__/thread-cancel-text-renderer.test.d.ts +2 -0
  86. package/dist/__tests__/thread-cancel-text-renderer.test.d.ts.map +1 -0
  87. package/dist/__tests__/thread-cancel-text-renderer.test.js +110 -0
  88. package/dist/__tests__/thread-cancel-text-renderer.test.js.map +1 -0
  89. package/dist/__tests__/thread-list-filters.test.js +354 -17
  90. package/dist/__tests__/thread-list-filters.test.js.map +1 -1
  91. package/dist/__tests__/thread-list-template-ms-date.test.d.ts +2 -0
  92. package/dist/__tests__/thread-list-template-ms-date.test.d.ts.map +1 -0
  93. package/dist/__tests__/thread-list-template-ms-date.test.js +102 -0
  94. package/dist/__tests__/thread-list-template-ms-date.test.js.map +1 -0
  95. package/dist/__tests__/thread-list-workflow-corrupt.test.d.ts +2 -0
  96. package/dist/__tests__/thread-list-workflow-corrupt.test.d.ts.map +1 -0
  97. package/dist/__tests__/thread-list-workflow-corrupt.test.js +157 -0
  98. package/dist/__tests__/thread-list-workflow-corrupt.test.js.map +1 -0
  99. package/dist/__tests__/thread-poke.test.d.ts +2 -0
  100. package/dist/__tests__/thread-poke.test.d.ts.map +1 -0
  101. package/dist/__tests__/thread-poke.test.js +422 -0
  102. package/dist/__tests__/thread-poke.test.js.map +1 -0
  103. package/dist/__tests__/thread-read-xml-tags.test.js +10 -9
  104. package/dist/__tests__/thread-read-xml-tags.test.js.map +1 -1
  105. package/dist/__tests__/thread-resume.test.js +21 -15
  106. package/dist/__tests__/thread-resume.test.js.map +1 -1
  107. package/dist/__tests__/thread-show-status.test.js +17 -28
  108. package/dist/__tests__/thread-show-status.test.js.map +1 -1
  109. package/dist/__tests__/thread-start-cwd-cli.test.js +15 -3
  110. package/dist/__tests__/thread-start-cwd-cli.test.js.map +1 -1
  111. package/dist/__tests__/thread-stop-text-renderer.test.d.ts +2 -0
  112. package/dist/__tests__/thread-stop-text-renderer.test.d.ts.map +1 -0
  113. package/dist/__tests__/thread-stop-text-renderer.test.js +148 -0
  114. package/dist/__tests__/thread-stop-text-renderer.test.js.map +1 -0
  115. package/dist/__tests__/thread-suspend-step.test.js +13 -16
  116. package/dist/__tests__/thread-suspend-step.test.js.map +1 -1
  117. package/dist/__tests__/thread-suspended-display.test.js +10 -22
  118. package/dist/__tests__/thread-suspended-display.test.js.map +1 -1
  119. package/dist/__tests__/thread-test-helpers.d.ts +7 -0
  120. package/dist/__tests__/thread-test-helpers.d.ts.map +1 -1
  121. package/dist/__tests__/thread-test-helpers.js +13 -0
  122. package/dist/__tests__/thread-test-helpers.js.map +1 -1
  123. package/dist/__tests__/thread.test.js +15 -13
  124. package/dist/__tests__/thread.test.js.map +1 -1
  125. package/dist/__tests__/validate-semantic.test.js +105 -23
  126. package/dist/__tests__/validate-semantic.test.js.map +1 -1
  127. package/dist/__tests__/workflow-list-recursive.test.d.ts +2 -0
  128. package/dist/__tests__/workflow-list-recursive.test.d.ts.map +1 -0
  129. package/dist/__tests__/workflow-list-recursive.test.js +286 -0
  130. package/dist/__tests__/workflow-list-recursive.test.js.map +1 -0
  131. package/dist/__tests__/workflow-resolution.test.js +46 -28
  132. package/dist/__tests__/workflow-resolution.test.js.map +1 -1
  133. package/dist/__tests__/workflow-show-resolution.test.d.ts +2 -0
  134. package/dist/__tests__/workflow-show-resolution.test.d.ts.map +1 -0
  135. package/dist/__tests__/workflow-show-resolution.test.js +213 -0
  136. package/dist/__tests__/workflow-show-resolution.test.js.map +1 -0
  137. package/dist/__tests__/workflow-validate.test.d.ts +2 -0
  138. package/dist/__tests__/workflow-validate.test.d.ts.map +1 -0
  139. package/dist/__tests__/workflow-validate.test.js +707 -0
  140. package/dist/__tests__/workflow-validate.test.js.map +1 -0
  141. package/dist/__tests__/write-envelope.test.d.ts +2 -0
  142. package/dist/__tests__/write-envelope.test.d.ts.map +1 -0
  143. package/dist/__tests__/write-envelope.test.js +201 -0
  144. package/dist/__tests__/write-envelope.test.js.map +1 -0
  145. package/dist/background/background.d.ts +22 -1
  146. package/dist/background/background.d.ts.map +1 -1
  147. package/dist/background/background.js +83 -6
  148. package/dist/background/background.js.map +1 -1
  149. package/dist/background/index.d.ts +1 -1
  150. package/dist/background/index.d.ts.map +1 -1
  151. package/dist/background/index.js +1 -1
  152. package/dist/background/index.js.map +1 -1
  153. package/dist/background/types.d.ts +1 -0
  154. package/dist/background/types.d.ts.map +1 -1
  155. package/dist/cli.js +120 -62
  156. package/dist/cli.js.map +1 -1
  157. package/dist/commands/config.d.ts +3 -1
  158. package/dist/commands/config.d.ts.map +1 -1
  159. package/dist/commands/config.js +17 -31
  160. package/dist/commands/config.js.map +1 -1
  161. package/dist/commands/prompt.d.ts.map +1 -1
  162. package/dist/commands/prompt.js +57 -31
  163. package/dist/commands/prompt.js.map +1 -1
  164. package/dist/commands/setup.d.ts +12 -39
  165. package/dist/commands/setup.d.ts.map +1 -1
  166. package/dist/commands/setup.js +72 -303
  167. package/dist/commands/setup.js.map +1 -1
  168. package/dist/commands/step.d.ts +44 -1
  169. package/dist/commands/step.d.ts.map +1 -1
  170. package/dist/commands/step.js +255 -11
  171. package/dist/commands/step.js.map +1 -1
  172. package/dist/commands/thread.d.ts +16 -3
  173. package/dist/commands/thread.d.ts.map +1 -1
  174. package/dist/commands/thread.js +423 -142
  175. package/dist/commands/thread.js.map +1 -1
  176. package/dist/commands/workflow.d.ts +9 -1
  177. package/dist/commands/workflow.d.ts.map +1 -1
  178. package/dist/commands/workflow.js +126 -6
  179. package/dist/commands/workflow.js.map +1 -1
  180. package/dist/concurrency/concurrency.d.ts +34 -0
  181. package/dist/concurrency/concurrency.d.ts.map +1 -0
  182. package/dist/concurrency/concurrency.js +216 -0
  183. package/dist/concurrency/concurrency.js.map +1 -0
  184. package/dist/concurrency/index.d.ts +3 -0
  185. package/dist/concurrency/index.d.ts.map +1 -0
  186. package/dist/concurrency/index.js +2 -0
  187. package/dist/concurrency/index.js.map +1 -0
  188. package/dist/concurrency/types.d.ts +19 -0
  189. package/dist/concurrency/types.d.ts.map +1 -0
  190. package/dist/concurrency/types.js +2 -0
  191. package/dist/concurrency/types.js.map +1 -0
  192. package/dist/format.d.ts +69 -2
  193. package/dist/format.d.ts.map +1 -1
  194. package/dist/format.js +198 -1
  195. package/dist/format.js.map +1 -1
  196. package/dist/moderator/__tests__/evaluate.test.js +31 -17
  197. package/dist/moderator/__tests__/evaluate.test.js.map +1 -1
  198. package/dist/moderator/evaluate.d.ts.map +1 -1
  199. package/dist/moderator/evaluate.js +4 -16
  200. package/dist/moderator/evaluate.js.map +1 -1
  201. package/dist/moderator/index.d.ts +1 -2
  202. package/dist/moderator/index.d.ts.map +1 -1
  203. package/dist/moderator/index.js +0 -1
  204. package/dist/moderator/index.js.map +1 -1
  205. package/dist/moderator/types.d.ts +6 -10
  206. package/dist/moderator/types.d.ts.map +1 -1
  207. package/dist/moderator/types.js +1 -3
  208. package/dist/moderator/types.js.map +1 -1
  209. package/dist/output-mappers.d.ts +122 -0
  210. package/dist/output-mappers.d.ts.map +1 -0
  211. package/dist/output-mappers.js +134 -0
  212. package/dist/output-mappers.js.map +1 -0
  213. package/dist/schemas.d.ts +6 -1
  214. package/dist/schemas.d.ts.map +1 -1
  215. package/dist/schemas.js +34 -5
  216. package/dist/schemas.js.map +1 -1
  217. package/dist/store.d.ts +28 -9
  218. package/dist/store.d.ts.map +1 -1
  219. package/dist/store.js +75 -16
  220. package/dist/store.js.map +1 -1
  221. package/dist/text-renderers.d.ts +30 -0
  222. package/dist/text-renderers.d.ts.map +1 -0
  223. package/dist/text-renderers.js +251 -0
  224. package/dist/text-renderers.js.map +1 -0
  225. package/dist/validate-semantic.d.ts.map +1 -1
  226. package/dist/validate-semantic.js +95 -61
  227. package/dist/validate-semantic.js.map +1 -1
  228. package/dist/validate.d.ts +6 -0
  229. package/dist/validate.d.ts.map +1 -1
  230. package/dist/validate.js +24 -0
  231. package/dist/validate.js.map +1 -1
  232. package/examples/brainstorm.yaml +130 -0
  233. package/examples/debate.yaml +169 -0
  234. package/examples/socratic-questioning.yaml +112 -0
  235. package/package.json +9 -10
  236. package/src/__tests__/adapter-json-roundtrip.test.ts +16 -7
  237. package/src/__tests__/agent-resolution-llm-free.test.ts +39 -0
  238. package/src/__tests__/build-step-entry.test.ts +203 -0
  239. package/src/__tests__/clear-thread-failed-attempts.test.ts +122 -0
  240. package/src/__tests__/concurrency.test.ts +266 -0
  241. package/src/__tests__/config.test.ts +33 -321
  242. package/src/__tests__/current-role.test.ts +7 -6
  243. package/src/__tests__/e2e-mock-agent.test.ts +65 -30
  244. package/src/__tests__/fixtures/e2e-count.workflow.yaml +1 -0
  245. package/src/__tests__/fixtures/e2e-linear.workflow.yaml +1 -0
  246. package/src/__tests__/fixtures/{e2e-mustache.workflow.yaml → e2e-liquid.workflow.yaml} +3 -2
  247. package/src/__tests__/fixtures/e2e-loop.workflow.yaml +1 -0
  248. package/src/__tests__/fixtures/e2e-suspend.mock.yaml +2 -2
  249. package/src/__tests__/fixtures/e2e-suspend.workflow.yaml +6 -10
  250. package/src/__tests__/format-text-default.test.ts +49 -0
  251. package/src/__tests__/format-text-registry.test.ts +173 -0
  252. package/src/__tests__/issue-180-workflow-ref-removed.test.ts +43 -0
  253. package/src/__tests__/log-text-renderer.test.ts +294 -0
  254. package/src/__tests__/moderator-evaluate.test.ts +9 -52
  255. package/src/__tests__/output-mapper-thread-list-startedat.test.ts +124 -0
  256. package/src/__tests__/output-mapper-workflow-add.test.ts +24 -0
  257. package/src/__tests__/pid-recycling.test.ts +329 -0
  258. package/src/__tests__/prompt.test.ts +443 -2
  259. package/src/__tests__/resolve-head-hash.test.ts +11 -4
  260. package/src/__tests__/setup-agent-discovery.test.ts +26 -51
  261. package/src/__tests__/setup-complexity.test.ts +1 -203
  262. package/src/__tests__/setup-no-llm.test.ts +68 -0
  263. package/src/__tests__/solve-issue-tea-worktree.test.ts +27 -31
  264. package/src/__tests__/step-ask.test.ts +677 -0
  265. package/src/__tests__/step-show-json.test.ts +1 -0
  266. package/src/__tests__/step-timing.test.ts +2 -0
  267. package/src/__tests__/store-global-cas.test.ts +2 -2
  268. package/src/__tests__/store-unified-threads.test.ts +30 -27
  269. package/src/__tests__/thread-cancel-status.test.ts +27 -20
  270. package/src/__tests__/thread-cancel-text-renderer.test.ts +125 -0
  271. package/src/__tests__/thread-list-filters.test.ts +443 -17
  272. package/src/__tests__/thread-list-template-ms-date.test.ts +110 -0
  273. package/src/__tests__/thread-list-workflow-corrupt.test.ts +198 -0
  274. package/src/__tests__/thread-poke.test.ts +554 -0
  275. package/src/__tests__/thread-read-xml-tags.test.ts +9 -11
  276. package/src/__tests__/thread-resume.test.ts +20 -15
  277. package/src/__tests__/thread-show-status.test.ts +17 -29
  278. package/src/__tests__/thread-start-cwd-cli.test.ts +15 -3
  279. package/src/__tests__/thread-stop-text-renderer.test.ts +168 -0
  280. package/src/__tests__/thread-suspend-step.test.ts +13 -16
  281. package/src/__tests__/thread-suspended-display.test.ts +10 -22
  282. package/src/__tests__/thread-test-helpers.ts +15 -1
  283. package/src/__tests__/thread.test.ts +14 -14
  284. package/src/__tests__/validate-semantic.test.ts +118 -33
  285. package/src/__tests__/workflow-list-recursive.test.ts +370 -0
  286. package/src/__tests__/workflow-resolution.test.ts +48 -29
  287. package/src/__tests__/workflow-show-resolution.test.ts +286 -0
  288. package/src/__tests__/workflow-validate.test.ts +828 -0
  289. package/src/__tests__/write-envelope.test.ts +257 -0
  290. package/src/background/background.ts +88 -6
  291. package/src/background/index.ts +2 -0
  292. package/src/background/types.ts +1 -0
  293. package/src/cli.ts +184 -77
  294. package/src/commands/config.ts +16 -33
  295. package/src/commands/prompt.ts +57 -31
  296. package/src/commands/setup.ts +80 -358
  297. package/src/commands/step.ts +339 -12
  298. package/src/commands/thread.ts +511 -171
  299. package/src/commands/workflow.ts +155 -4
  300. package/src/concurrency/concurrency.ts +245 -0
  301. package/src/concurrency/index.ts +10 -0
  302. package/src/concurrency/types.ts +19 -0
  303. package/src/format.ts +282 -2
  304. package/src/moderator/__tests__/evaluate.test.ts +34 -17
  305. package/src/moderator/evaluate.ts +5 -17
  306. package/src/moderator/index.ts +1 -6
  307. package/src/moderator/types.ts +6 -14
  308. package/src/output-mappers.ts +254 -0
  309. package/src/schemas.ts +51 -5
  310. package/src/store.ts +86 -20
  311. package/src/text-renderers.ts +355 -0
  312. package/src/validate-semantic.ts +125 -73
  313. package/src/validate.ts +27 -0
  314. package/dist/__tests__/setup-validate.test.d.ts +0 -2
  315. package/dist/__tests__/setup-validate.test.d.ts.map +0 -1
  316. package/dist/__tests__/setup-validate.test.js +0 -108
  317. package/dist/__tests__/setup-validate.test.js.map +0 -1
  318. package/src/__tests__/setup-validate.test.ts +0 -148
  319. /package/src/__tests__/fixtures/{e2e-mustache.mock.yaml → e2e-liquid.mock.yaml} +0 -0
@@ -230,11 +230,11 @@ describe("Global CAS directory", () => {
230
230
  const { createThreadIndexEntry } = await import("@united-workforce/protocol");
231
231
 
232
232
  setThread(uwf.varStore, threadId, createThreadIndexEntry(headHash));
233
- completeThread(uwf.varStore, threadId, "completed");
233
+ completeThread(uwf.varStore, threadId, "end");
234
234
 
235
235
  const entry = getThread(uwf.varStore, threadId);
236
236
  expect(entry?.head).toBe(headHash);
237
- expect(entry?.status).toBe("completed");
237
+ expect(entry?.status).toBe("end");
238
238
 
239
239
  const { access } = await import("node:fs/promises");
240
240
  await access(join(globalCasDir, "vars"));
@@ -1,23 +1,17 @@
1
- import { mkdir, mkdtemp } from "node:fs/promises";
1
+ import { mkdtemp, rm } from "node:fs/promises";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import type { CasRef, ThreadId } from "@united-workforce/protocol";
5
- import { describe, expect, test } from "vitest";
5
+ import { afterEach, beforeEach, describe, expect, test } from "vitest";
6
6
  import {
7
7
  completeThread,
8
- createUwfStore,
8
+ type createUwfStore,
9
9
  getThread,
10
10
  loadActiveThreads,
11
11
  loadHistoryThreads,
12
12
  setThread,
13
13
  } from "../store.js";
14
-
15
- async function makeUwfStore(storageRoot: string) {
16
- const casDir = join(storageRoot, "cas");
17
- await mkdir(casDir, { recursive: true });
18
- process.env.OCAS_HOME = casDir;
19
- return createUwfStore(storageRoot);
20
- }
14
+ import { makeUwfStore } from "./thread-test-helpers.js";
21
15
 
22
16
  async function seedThreadHead(
23
17
  uwf: Awaited<ReturnType<typeof createUwfStore>>,
@@ -26,9 +20,25 @@ async function seedThreadHead(
26
20
  return (await uwf.store.cas.put(uwf.schemas.text, label)) as CasRef;
27
21
  }
28
22
 
23
+ let tmpDir: string;
24
+ let savedOcasHome: string | undefined;
25
+
26
+ beforeEach(async () => {
27
+ savedOcasHome = process.env.OCAS_HOME;
28
+ tmpDir = await mkdtemp(join(tmpdir(), "uwf-store-test-"));
29
+ });
30
+
31
+ afterEach(async () => {
32
+ if (savedOcasHome === undefined) {
33
+ delete process.env.OCAS_HOME;
34
+ } else {
35
+ process.env.OCAS_HOME = savedOcasHome;
36
+ }
37
+ await rm(tmpDir, { recursive: true, force: true });
38
+ });
39
+
29
40
  describe("unified thread storage", () => {
30
41
  test("loadActiveThreads excludes completed threads", async () => {
31
- const tmpDir = await mkdtemp(join(tmpdir(), "uwf-active-test-"));
32
42
  const uwf = await makeUwfStore(tmpDir);
33
43
 
34
44
  const threadId1 = "01JTEST000000000000ACTIVE1" as ThreadId;
@@ -46,7 +56,7 @@ describe("unified thread storage", () => {
46
56
 
47
57
  setThread(uwf.varStore, threadId2, {
48
58
  head: head2,
49
- status: "completed",
59
+ status: "end",
50
60
  suspendedRole: null,
51
61
  suspendMessage: null,
52
62
  completedAt: Date.now(),
@@ -59,7 +69,6 @@ describe("unified thread storage", () => {
59
69
  });
60
70
 
61
71
  test("loadActiveThreads excludes cancelled threads", async () => {
62
- const tmpDir = await mkdtemp(join(tmpdir(), "uwf-active-test-"));
63
72
  const uwf = await makeUwfStore(tmpDir);
64
73
 
65
74
  const threadId1 = "01JTEST000000000000ACTIVE3" as ThreadId;
@@ -90,7 +99,6 @@ describe("unified thread storage", () => {
90
99
  });
91
100
 
92
101
  test("loadHistoryThreads only returns completed and cancelled", async () => {
93
- const tmpDir = await mkdtemp(join(tmpdir(), "uwf-history-test-"));
94
102
  const uwf = await makeUwfStore(tmpDir);
95
103
 
96
104
  const threadId1 = "01JTEST000000000000HISTOR1" as ThreadId;
@@ -110,7 +118,7 @@ describe("unified thread storage", () => {
110
118
 
111
119
  setThread(uwf.varStore, threadId2, {
112
120
  head: head2,
113
- status: "completed",
121
+ status: "end",
114
122
  suspendedRole: null,
115
123
  suspendMessage: null,
116
124
  completedAt: Date.now(),
@@ -132,7 +140,6 @@ describe("unified thread storage", () => {
132
140
  });
133
141
 
134
142
  test("completeThread marks thread as completed", async () => {
135
- const tmpDir = await mkdtemp(join(tmpdir(), "uwf-complete-test-"));
136
143
  const uwf = await makeUwfStore(tmpDir);
137
144
  const threadId = "01JTEST000000000000COMPLE1" as ThreadId;
138
145
  const head = await seedThreadHead(uwf, "active-head");
@@ -145,17 +152,16 @@ describe("unified thread storage", () => {
145
152
  completedAt: null,
146
153
  });
147
154
 
148
- completeThread(uwf.varStore, threadId, "completed");
155
+ completeThread(uwf.varStore, threadId, "end");
149
156
 
150
157
  const entry = getThread(uwf.varStore, threadId);
151
158
  expect(entry).not.toBeNull();
152
- expect(entry?.status).toBe("completed");
159
+ expect(entry?.status).toBe("end");
153
160
  expect(entry?.completedAt).toBeDefined();
154
161
  expect(entry?.completedAt).toBeGreaterThan(0);
155
162
  });
156
163
 
157
164
  test("completeThread marks thread as cancelled", async () => {
158
- const tmpDir = await mkdtemp(join(tmpdir(), "uwf-complete-test-"));
159
165
  const uwf = await makeUwfStore(tmpDir);
160
166
  const threadId = "01JTEST000000000000COMPLE2" as ThreadId;
161
167
  const head = await seedThreadHead(uwf, "active-head");
@@ -178,7 +184,6 @@ describe("unified thread storage", () => {
178
184
  });
179
185
 
180
186
  test("completeThread clears suspend metadata", async () => {
181
- const tmpDir = await mkdtemp(join(tmpdir(), "uwf-complete-test-"));
182
187
  const uwf = await makeUwfStore(tmpDir);
183
188
  const threadId = "01JTEST000000000000COMPLE3" as ThreadId;
184
189
  const head = await seedThreadHead(uwf, "suspended-head");
@@ -191,29 +196,27 @@ describe("unified thread storage", () => {
191
196
  completedAt: null,
192
197
  });
193
198
 
194
- completeThread(uwf.varStore, threadId, "completed");
199
+ completeThread(uwf.varStore, threadId, "end");
195
200
 
196
201
  const entry = getThread(uwf.varStore, threadId);
197
202
  expect(entry).not.toBeNull();
198
- expect(entry?.status).toBe("completed");
203
+ expect(entry?.status).toBe("end");
199
204
  expect(entry?.suspendedRole).toBeNull();
200
205
  expect(entry?.suspendMessage).toBeNull();
201
206
  });
202
207
 
203
208
  test("completeThread handles non-existent thread gracefully", async () => {
204
- const tmpDir = await mkdtemp(join(tmpdir(), "uwf-complete-test-"));
205
209
  const uwf = await makeUwfStore(tmpDir);
206
210
  const threadId = "01JTEST000000000000NOEXIST" as ThreadId;
207
211
 
208
212
  // Should not throw
209
- completeThread(uwf.varStore, threadId, "completed");
213
+ completeThread(uwf.varStore, threadId, "end");
210
214
 
211
215
  const entry = getThread(uwf.varStore, threadId);
212
216
  expect(entry).toBeNull();
213
217
  });
214
218
 
215
219
  test("status and completedAt tags are persisted and loaded", async () => {
216
- const tmpDir = await mkdtemp(join(tmpdir(), "uwf-tags-test-"));
217
220
  const uwf = await makeUwfStore(tmpDir);
218
221
  const threadId = "01JTEST000000000000TAGTEST" as ThreadId;
219
222
  const head = await seedThreadHead(uwf, "test-head");
@@ -221,7 +224,7 @@ describe("unified thread storage", () => {
221
224
 
222
225
  setThread(uwf.varStore, threadId, {
223
226
  head,
224
- status: "completed",
227
+ status: "end",
225
228
  suspendedRole: null,
226
229
  suspendMessage: null,
227
230
  completedAt: now,
@@ -229,7 +232,7 @@ describe("unified thread storage", () => {
229
232
 
230
233
  const entry = getThread(uwf.varStore, threadId);
231
234
  expect(entry).not.toBeNull();
232
- expect(entry?.status).toBe("completed");
235
+ expect(entry?.status).toBe("end");
233
236
  expect(entry?.completedAt).toBe(now);
234
237
  });
235
238
  });
@@ -1,22 +1,16 @@
1
- import { mkdir, mkdtemp } from "node:fs/promises";
1
+ import { mkdtemp, rm } from "node:fs/promises";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import type { CasRef, ThreadId } from "@united-workforce/protocol";
5
- import { describe, expect, test } from "vitest";
5
+ import { afterEach, beforeEach, describe, expect, test } from "vitest";
6
6
  import {
7
7
  completeThread,
8
- createUwfStore,
8
+ type createUwfStore,
9
9
  getThread,
10
10
  loadHistoryThreads,
11
11
  setThread,
12
12
  } from "../store.js";
13
-
14
- async function makeUwfStore(storageRoot: string) {
15
- const casDir = join(storageRoot, "cas");
16
- await mkdir(casDir, { recursive: true });
17
- process.env.OCAS_HOME = casDir;
18
- return createUwfStore(storageRoot);
19
- }
13
+ import { makeUwfStore } from "./thread-test-helpers.js";
20
14
 
21
15
  async function seedHistoryHead(
22
16
  uwf: Awaited<ReturnType<typeof createUwfStore>>,
@@ -25,9 +19,25 @@ async function seedHistoryHead(
25
19
  return (await uwf.store.cas.put(uwf.schemas.text, label)) as CasRef;
26
20
  }
27
21
 
22
+ let tmpDir: string;
23
+ let savedOcasHome: string | undefined;
24
+
25
+ beforeEach(async () => {
26
+ savedOcasHome = process.env.OCAS_HOME;
27
+ tmpDir = await mkdtemp(join(tmpdir(), "uwf-cancel-test-"));
28
+ });
29
+
30
+ afterEach(async () => {
31
+ if (savedOcasHome === undefined) {
32
+ delete process.env.OCAS_HOME;
33
+ } else {
34
+ process.env.OCAS_HOME = savedOcasHome;
35
+ }
36
+ await rm(tmpDir, { recursive: true, force: true });
37
+ });
38
+
28
39
  describe("thread cancel status", () => {
29
40
  test("cancelled thread has status 'cancelled'", async () => {
30
- const tmpDir = await mkdtemp(join(tmpdir(), "uwf-cancel-test-"));
31
41
  const threadId = "01JTEST000000000000CANCEL1" as ThreadId;
32
42
  const uwf = await makeUwfStore(tmpDir);
33
43
  const head = await seedHistoryHead(uwf, "cancelled-head");
@@ -48,7 +58,6 @@ describe("thread cancel status", () => {
48
58
  });
49
59
 
50
60
  test("completed thread has status 'completed'", async () => {
51
- const tmpDir = await mkdtemp(join(tmpdir(), "uwf-cancel-test-"));
52
61
  const threadId = "01JTEST000000000000CANCEL2" as ThreadId;
53
62
  const uwf = await makeUwfStore(tmpDir);
54
63
  const head = await seedHistoryHead(uwf, "completed-head");
@@ -61,15 +70,14 @@ describe("thread cancel status", () => {
61
70
  completedAt: null,
62
71
  });
63
72
 
64
- completeThread(uwf.varStore, threadId, "completed");
73
+ completeThread(uwf.varStore, threadId, "end");
65
74
 
66
75
  const entry = getThread(uwf.varStore, threadId);
67
76
  expect(entry).not.toBeNull();
68
- expect(entry?.status).toBe("completed");
77
+ expect(entry?.status).toBe("end");
69
78
  });
70
79
 
71
80
  test("loadHistoryThreads returns completed and cancelled", async () => {
72
- const tmpDir = await mkdtemp(join(tmpdir(), "uwf-cancel-test-"));
73
81
  const uwf = await makeUwfStore(tmpDir);
74
82
  const head1 = await seedHistoryHead(uwf, "head1");
75
83
  const head2 = await seedHistoryHead(uwf, "head2");
@@ -82,7 +90,7 @@ describe("thread cancel status", () => {
82
90
  suspendMessage: null,
83
91
  completedAt: null,
84
92
  });
85
- completeThread(uwf.varStore, threadId1, "completed");
93
+ completeThread(uwf.varStore, threadId1, "end");
86
94
 
87
95
  const threadId2 = "01JTEST000000000000CANCEL5" as ThreadId;
88
96
  setThread(uwf.varStore, threadId2, {
@@ -99,11 +107,10 @@ describe("thread cancel status", () => {
99
107
  const statuses = Object.values(history)
100
108
  .map((entry) => entry.status)
101
109
  .sort();
102
- expect(statuses).toEqual(["cancelled", "completed"]);
110
+ expect(statuses).toEqual(["cancelled", "end"]);
103
111
  });
104
112
 
105
113
  test("mixed completed and cancelled entries preserve distinct statuses", async () => {
106
- const tmpDir = await mkdtemp(join(tmpdir(), "uwf-cancel-test-"));
107
114
  const uwf = await makeUwfStore(tmpDir);
108
115
  const head1 = await seedHistoryHead(uwf, "head1");
109
116
  const head2 = await seedHistoryHead(uwf, "head2");
@@ -116,7 +123,7 @@ describe("thread cancel status", () => {
116
123
  suspendMessage: null,
117
124
  completedAt: null,
118
125
  });
119
- completeThread(uwf.varStore, threadId1, "completed");
126
+ completeThread(uwf.varStore, threadId1, "end");
120
127
 
121
128
  const threadId2 = "01JTEST000000000000CANCEL7" as ThreadId;
122
129
  setThread(uwf.varStore, threadId2, {
@@ -133,6 +140,6 @@ describe("thread cancel status", () => {
133
140
  const statuses = Object.values(history)
134
141
  .map((entry) => entry.status)
135
142
  .sort();
136
- expect(statuses).toEqual(["cancelled", "completed"]);
143
+ expect(statuses).toEqual(["cancelled", "end"]);
137
144
  });
138
145
  });
@@ -0,0 +1,125 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { formatOutput, getTextRenderer, TEXT_RENDERERS } from "../format.js";
3
+ import { renderThreadCancel } from "../text-renderers.js";
4
+
5
+ describe("thread cancel — text renderer registration", () => {
6
+ test("TEXT_RENDERERS contains 'thread cancel'", () => {
7
+ expect(getTextRenderer("thread cancel")).toBeDefined();
8
+ expect(typeof getTextRenderer("thread cancel")).toBe("function");
9
+ });
10
+
11
+ test("TEXT_RENDERERS['thread cancel'] is the same reference as renderThreadCancel", () => {
12
+ expect(TEXT_RENDERERS["thread cancel"]).toBe(renderThreadCancel);
13
+ });
14
+
15
+ test("renderThreadCancel is exported from text-renderers.ts", () => {
16
+ expect(typeof renderThreadCancel).toBe("function");
17
+ });
18
+ });
19
+
20
+ describe("renderThreadCancel — output shape", () => {
21
+ test("returns a string for full payload", () => {
22
+ const out = renderThreadCancel({
23
+ thread: "01JTEST000000000000CANCEL1",
24
+ cancelled: true,
25
+ });
26
+ expect(typeof out).toBe("string");
27
+ });
28
+
29
+ test("includes the cancelled thread's ULID", () => {
30
+ const out = renderThreadCancel({
31
+ thread: "01JTEST000000000000CANCEL1",
32
+ cancelled: true,
33
+ });
34
+ expect(out).toContain("01JTEST000000000000CANCEL1");
35
+ });
36
+
37
+ test("indicates cancelled status (Status: cancelled OR Cancelled: yes)", () => {
38
+ const out = renderThreadCancel({
39
+ thread: "01JTEST000000000000CANCEL1",
40
+ cancelled: true,
41
+ });
42
+ // accept either rendering style
43
+ const lower = out.toLowerCase();
44
+ const hasCancelMarker = lower.includes("cancelled") || lower.includes("yes");
45
+ expect(hasCancelMarker).toBe(true);
46
+ });
47
+
48
+ test("does NOT begin with '{' or '[' (not raw JSON)", () => {
49
+ const out = renderThreadCancel({
50
+ thread: "01JTEST000000000000CANCEL1",
51
+ cancelled: true,
52
+ });
53
+ const trimmed = out.trimStart();
54
+ expect(trimmed.startsWith("{")).toBe(false);
55
+ expect(trimmed.startsWith("[")).toBe(false);
56
+ });
57
+
58
+ test("does NOT contain literal 'undefined'", () => {
59
+ const out = renderThreadCancel({
60
+ thread: "01JTEST000000000000CANCEL1",
61
+ cancelled: true,
62
+ });
63
+ expect(out).not.toContain("undefined");
64
+ });
65
+ });
66
+
67
+ describe("renderThreadCancel — partial / missing data", () => {
68
+ test("missing 'cancelled' field — returns string, no throw, no 'undefined'", () => {
69
+ const out = renderThreadCancel({ thread: "01JTEST000000000000CANCEL1" });
70
+ expect(typeof out).toBe("string");
71
+ expect(out).not.toContain("undefined");
72
+ });
73
+
74
+ test("missing 'thread' field — returns string, no throw, no 'undefined'", () => {
75
+ const out = renderThreadCancel({ cancelled: true });
76
+ expect(typeof out).toBe("string");
77
+ expect(out).not.toContain("undefined");
78
+ });
79
+
80
+ test("empty object — returns string, no throw, no 'undefined'", () => {
81
+ const out = renderThreadCancel({});
82
+ expect(typeof out).toBe("string");
83
+ expect(out).not.toContain("undefined");
84
+ });
85
+
86
+ test("null payload — returns string, no throw", () => {
87
+ expect(() => renderThreadCancel(null)).not.toThrow();
88
+ const out = renderThreadCancel(null);
89
+ expect(typeof out).toBe("string");
90
+ expect(out).not.toContain("undefined");
91
+ });
92
+
93
+ test("non-object payload (string) — returns string, no throw", () => {
94
+ expect(() => renderThreadCancel("oops")).not.toThrow();
95
+ const out = renderThreadCancel("oops");
96
+ expect(typeof out).toBe("string");
97
+ expect(out).not.toContain("undefined");
98
+ });
99
+ });
100
+
101
+ describe("formatOutput integration — thread cancel", () => {
102
+ test("formatOutput(data, 'text', 'thread cancel') uses renderer", () => {
103
+ const data = { thread: "01JTEST000000000000CANCEL1", cancelled: true };
104
+ const out = formatOutput(data, "text", "thread cancel");
105
+ expect(typeof out).toBe("string");
106
+ expect(out).not.toContain("undefined");
107
+ expect(out.trimStart().startsWith("{")).toBe(false);
108
+ expect(out).toContain("01JTEST000000000000CANCEL1");
109
+ });
110
+
111
+ test("formatOutput(data, 'json', 'thread cancel') still emits parseable JSON", () => {
112
+ const data = { thread: "01JTEST000000000000CANCEL1", cancelled: true };
113
+ const out = formatOutput(data, "json", "thread cancel");
114
+ const parsed = JSON.parse(out);
115
+ expect(parsed).toEqual(data);
116
+ });
117
+
118
+ test("formatOutput(data, 'yaml', 'thread cancel') still emits YAML", () => {
119
+ const data = { thread: "01JTEST000000000000CANCEL1", cancelled: true };
120
+ const out = formatOutput(data, "yaml", "thread cancel");
121
+ expect(typeof out).toBe("string");
122
+ expect(out).toContain("thread:");
123
+ expect(out).toContain("cancelled:");
124
+ });
125
+ });