@united-workforce/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +221 -0
  3. package/dist/__tests__/adapter-json-roundtrip.test.d.ts +2 -0
  4. package/dist/__tests__/adapter-json-roundtrip.test.d.ts.map +1 -0
  5. package/dist/__tests__/adapter-json-roundtrip.test.js +147 -0
  6. package/dist/__tests__/adapter-json-roundtrip.test.js.map +1 -0
  7. package/dist/__tests__/config.test.d.ts +2 -0
  8. package/dist/__tests__/config.test.d.ts.map +1 -0
  9. package/dist/__tests__/config.test.js +685 -0
  10. package/dist/__tests__/config.test.js.map +1 -0
  11. package/dist/__tests__/current-role.test.d.ts +2 -0
  12. package/dist/__tests__/current-role.test.d.ts.map +1 -0
  13. package/dist/__tests__/current-role.test.js +401 -0
  14. package/dist/__tests__/current-role.test.js.map +1 -0
  15. package/dist/__tests__/e2e-mock-agent.test.d.ts +2 -0
  16. package/dist/__tests__/e2e-mock-agent.test.d.ts.map +1 -0
  17. package/dist/__tests__/e2e-mock-agent.test.js +401 -0
  18. package/dist/__tests__/e2e-mock-agent.test.js.map +1 -0
  19. package/dist/__tests__/include-tag.test.d.ts +2 -0
  20. package/dist/__tests__/include-tag.test.d.ts.map +1 -0
  21. package/dist/__tests__/include-tag.test.js +69 -0
  22. package/dist/__tests__/include-tag.test.js.map +1 -0
  23. package/dist/__tests__/log.test.d.ts +2 -0
  24. package/dist/__tests__/log.test.d.ts.map +1 -0
  25. package/dist/__tests__/log.test.js +161 -0
  26. package/dist/__tests__/log.test.js.map +1 -0
  27. package/dist/__tests__/moderator-evaluate.test.d.ts +2 -0
  28. package/dist/__tests__/moderator-evaluate.test.d.ts.map +1 -0
  29. package/dist/__tests__/moderator-evaluate.test.js +170 -0
  30. package/dist/__tests__/moderator-evaluate.test.js.map +1 -0
  31. package/dist/__tests__/preload.d.ts +3 -0
  32. package/dist/__tests__/preload.d.ts.map +1 -0
  33. package/dist/__tests__/preload.js +6 -0
  34. package/dist/__tests__/preload.js.map +1 -0
  35. package/dist/__tests__/prompt.test.d.ts +2 -0
  36. package/dist/__tests__/prompt.test.d.ts.map +1 -0
  37. package/dist/__tests__/prompt.test.js +111 -0
  38. package/dist/__tests__/prompt.test.js.map +1 -0
  39. package/dist/__tests__/resolve-head-hash.test.d.ts +2 -0
  40. package/dist/__tests__/resolve-head-hash.test.d.ts.map +1 -0
  41. package/dist/__tests__/resolve-head-hash.test.js +66 -0
  42. package/dist/__tests__/resolve-head-hash.test.js.map +1 -0
  43. package/dist/__tests__/setup-agent-discovery.test.d.ts +2 -0
  44. package/dist/__tests__/setup-agent-discovery.test.d.ts.map +1 -0
  45. package/dist/__tests__/setup-agent-discovery.test.js +119 -0
  46. package/dist/__tests__/setup-agent-discovery.test.js.map +1 -0
  47. package/dist/__tests__/setup-complexity.test.d.ts +2 -0
  48. package/dist/__tests__/setup-complexity.test.d.ts.map +1 -0
  49. package/dist/__tests__/setup-complexity.test.js +314 -0
  50. package/dist/__tests__/setup-complexity.test.js.map +1 -0
  51. package/dist/__tests__/setup-validate.test.d.ts +2 -0
  52. package/dist/__tests__/setup-validate.test.d.ts.map +1 -0
  53. package/dist/__tests__/setup-validate.test.js +108 -0
  54. package/dist/__tests__/setup-validate.test.js.map +1 -0
  55. package/dist/__tests__/solve-issue-tea-worktree.test.d.ts +2 -0
  56. package/dist/__tests__/solve-issue-tea-worktree.test.d.ts.map +1 -0
  57. package/dist/__tests__/solve-issue-tea-worktree.test.js +107 -0
  58. package/dist/__tests__/solve-issue-tea-worktree.test.js.map +1 -0
  59. package/dist/__tests__/spawn-agent-json.test.d.ts +2 -0
  60. package/dist/__tests__/spawn-agent-json.test.d.ts.map +1 -0
  61. package/dist/__tests__/spawn-agent-json.test.js +79 -0
  62. package/dist/__tests__/spawn-agent-json.test.js.map +1 -0
  63. package/dist/__tests__/step-read.test.d.ts +2 -0
  64. package/dist/__tests__/step-read.test.d.ts.map +1 -0
  65. package/dist/__tests__/step-read.test.js +561 -0
  66. package/dist/__tests__/step-read.test.js.map +1 -0
  67. package/dist/__tests__/step-show-json.test.d.ts +2 -0
  68. package/dist/__tests__/step-show-json.test.d.ts.map +1 -0
  69. package/dist/__tests__/step-show-json.test.js +311 -0
  70. package/dist/__tests__/step-show-json.test.js.map +1 -0
  71. package/dist/__tests__/step-timing.test.d.ts +2 -0
  72. package/dist/__tests__/step-timing.test.d.ts.map +1 -0
  73. package/dist/__tests__/step-timing.test.js +345 -0
  74. package/dist/__tests__/step-timing.test.js.map +1 -0
  75. package/dist/__tests__/store-global-cas.test.d.ts +2 -0
  76. package/dist/__tests__/store-global-cas.test.d.ts.map +1 -0
  77. package/dist/__tests__/store-global-cas.test.js +235 -0
  78. package/dist/__tests__/store-global-cas.test.js.map +1 -0
  79. package/dist/__tests__/store-storage-root.test.d.ts +2 -0
  80. package/dist/__tests__/store-storage-root.test.d.ts.map +1 -0
  81. package/dist/__tests__/store-storage-root.test.js +43 -0
  82. package/dist/__tests__/store-storage-root.test.js.map +1 -0
  83. package/dist/__tests__/store-unified-threads.test.d.ts +2 -0
  84. package/dist/__tests__/store-unified-threads.test.d.ts.map +1 -0
  85. package/dist/__tests__/store-unified-threads.test.js +189 -0
  86. package/dist/__tests__/store-unified-threads.test.js.map +1 -0
  87. package/dist/__tests__/thread-cancel-status.test.d.ts +2 -0
  88. package/dist/__tests__/thread-cancel-status.test.d.ts.map +1 -0
  89. package/dist/__tests__/thread-cancel-status.test.js +111 -0
  90. package/dist/__tests__/thread-cancel-status.test.js.map +1 -0
  91. package/dist/__tests__/thread-list-filters.test.d.ts +2 -0
  92. package/dist/__tests__/thread-list-filters.test.d.ts.map +1 -0
  93. package/dist/__tests__/thread-list-filters.test.js +442 -0
  94. package/dist/__tests__/thread-list-filters.test.js.map +1 -0
  95. package/dist/__tests__/thread-location.test.d.ts +2 -0
  96. package/dist/__tests__/thread-location.test.d.ts.map +1 -0
  97. package/dist/__tests__/thread-location.test.js +159 -0
  98. package/dist/__tests__/thread-location.test.js.map +1 -0
  99. package/dist/__tests__/thread-read-quota.test.d.ts +2 -0
  100. package/dist/__tests__/thread-read-quota.test.d.ts.map +1 -0
  101. package/dist/__tests__/thread-read-quota.test.js +546 -0
  102. package/dist/__tests__/thread-read-quota.test.js.map +1 -0
  103. package/dist/__tests__/thread-read-xml-tags.test.d.ts +2 -0
  104. package/dist/__tests__/thread-read-xml-tags.test.d.ts.map +1 -0
  105. package/dist/__tests__/thread-read-xml-tags.test.js +610 -0
  106. package/dist/__tests__/thread-read-xml-tags.test.js.map +1 -0
  107. package/dist/__tests__/thread-resume.test.d.ts +2 -0
  108. package/dist/__tests__/thread-resume.test.d.ts.map +1 -0
  109. package/dist/__tests__/thread-resume.test.js +592 -0
  110. package/dist/__tests__/thread-resume.test.js.map +1 -0
  111. package/dist/__tests__/thread-show-status.test.d.ts +2 -0
  112. package/dist/__tests__/thread-show-status.test.d.ts.map +1 -0
  113. package/dist/__tests__/thread-show-status.test.js +267 -0
  114. package/dist/__tests__/thread-show-status.test.js.map +1 -0
  115. package/dist/__tests__/thread-start-cwd-cli.test.d.ts +2 -0
  116. package/dist/__tests__/thread-start-cwd-cli.test.d.ts.map +1 -0
  117. package/dist/__tests__/thread-start-cwd-cli.test.js +130 -0
  118. package/dist/__tests__/thread-start-cwd-cli.test.js.map +1 -0
  119. package/dist/__tests__/thread-step-count.test.d.ts +2 -0
  120. package/dist/__tests__/thread-step-count.test.d.ts.map +1 -0
  121. package/dist/__tests__/thread-step-count.test.js +55 -0
  122. package/dist/__tests__/thread-step-count.test.js.map +1 -0
  123. package/dist/__tests__/thread-suspend-step.test.d.ts +2 -0
  124. package/dist/__tests__/thread-suspend-step.test.d.ts.map +1 -0
  125. package/dist/__tests__/thread-suspend-step.test.js +155 -0
  126. package/dist/__tests__/thread-suspend-step.test.js.map +1 -0
  127. package/dist/__tests__/thread-suspended-display.test.d.ts +2 -0
  128. package/dist/__tests__/thread-suspended-display.test.d.ts.map +1 -0
  129. package/dist/__tests__/thread-suspended-display.test.js +247 -0
  130. package/dist/__tests__/thread-suspended-display.test.js.map +1 -0
  131. package/dist/__tests__/thread-test-helpers.d.ts +4 -0
  132. package/dist/__tests__/thread-test-helpers.d.ts.map +1 -0
  133. package/dist/__tests__/thread-test-helpers.js +23 -0
  134. package/dist/__tests__/thread-test-helpers.js.map +1 -0
  135. package/dist/__tests__/thread.test.d.ts +2 -0
  136. package/dist/__tests__/thread.test.d.ts.map +1 -0
  137. package/dist/__tests__/thread.test.js +883 -0
  138. package/dist/__tests__/thread.test.js.map +1 -0
  139. package/dist/__tests__/validate-semantic.test.d.ts +2 -0
  140. package/dist/__tests__/validate-semantic.test.d.ts.map +1 -0
  141. package/dist/__tests__/validate-semantic.test.js +408 -0
  142. package/dist/__tests__/validate-semantic.test.js.map +1 -0
  143. package/dist/__tests__/workflow-resolution.test.d.ts +2 -0
  144. package/dist/__tests__/workflow-resolution.test.d.ts.map +1 -0
  145. package/dist/__tests__/workflow-resolution.test.js +308 -0
  146. package/dist/__tests__/workflow-resolution.test.js.map +1 -0
  147. package/dist/background/background.d.ts +38 -0
  148. package/dist/background/background.d.ts.map +1 -0
  149. package/dist/background/background.js +123 -0
  150. package/dist/background/background.js.map +1 -0
  151. package/dist/background/index.d.ts +3 -0
  152. package/dist/background/index.d.ts.map +1 -0
  153. package/dist/background/index.js +2 -0
  154. package/dist/background/index.js.map +1 -0
  155. package/dist/background/types.d.ts +9 -0
  156. package/dist/background/types.d.ts.map +1 -0
  157. package/dist/background/types.js +2 -0
  158. package/dist/background/types.js.map +1 -0
  159. package/dist/cli.d.ts +3 -0
  160. package/dist/cli.d.ts.map +1 -0
  161. package/dist/cli.js +535 -0
  162. package/dist/cli.js.map +1 -0
  163. package/dist/commands/config.d.ts +41 -0
  164. package/dist/commands/config.d.ts.map +1 -0
  165. package/dist/commands/config.js +252 -0
  166. package/dist/commands/config.js.map +1 -0
  167. package/dist/commands/log.d.ts +26 -0
  168. package/dist/commands/log.d.ts.map +1 -0
  169. package/dist/commands/log.js +79 -0
  170. package/dist/commands/log.js.map +1 -0
  171. package/dist/commands/prompt.d.ts +6 -0
  172. package/dist/commands/prompt.d.ts.map +1 -0
  173. package/dist/commands/prompt.js +67 -0
  174. package/dist/commands/prompt.js.map +1 -0
  175. package/dist/commands/setup.d.ts +73 -0
  176. package/dist/commands/setup.d.ts.map +1 -0
  177. package/dist/commands/setup.js +522 -0
  178. package/dist/commands/setup.js.map +1 -0
  179. package/dist/commands/shared.d.ts +31 -0
  180. package/dist/commands/shared.d.ts.map +1 -0
  181. package/dist/commands/shared.js +154 -0
  182. package/dist/commands/shared.js.map +1 -0
  183. package/dist/commands/step.d.ts +18 -0
  184. package/dist/commands/step.d.ts.map +1 -0
  185. package/dist/commands/step.js +257 -0
  186. package/dist/commands/step.js.map +1 -0
  187. package/dist/commands/thread-time-parser.d.ts +6 -0
  188. package/dist/commands/thread-time-parser.d.ts.map +1 -0
  189. package/dist/commands/thread-time-parser.js +22 -0
  190. package/dist/commands/thread-time-parser.js.map +1 -0
  191. package/dist/commands/thread.d.ts +38 -0
  192. package/dist/commands/thread.d.ts.map +1 -0
  193. package/dist/commands/thread.js +1087 -0
  194. package/dist/commands/thread.js.map +1 -0
  195. package/dist/commands/workflow.d.ts +24 -0
  196. package/dist/commands/workflow.d.ts.map +1 -0
  197. package/dist/commands/workflow.js +138 -0
  198. package/dist/commands/workflow.js.map +1 -0
  199. package/dist/format.d.ts +3 -0
  200. package/dist/format.d.ts.map +1 -0
  201. package/dist/format.js +10 -0
  202. package/dist/format.js.map +1 -0
  203. package/dist/include.d.ts +12 -0
  204. package/dist/include.d.ts.map +1 -0
  205. package/dist/include.js +35 -0
  206. package/dist/include.js.map +1 -0
  207. package/dist/moderator/__tests__/evaluate.test.d.ts +2 -0
  208. package/dist/moderator/__tests__/evaluate.test.d.ts.map +1 -0
  209. package/dist/moderator/__tests__/evaluate.test.js +167 -0
  210. package/dist/moderator/__tests__/evaluate.test.js.map +1 -0
  211. package/dist/moderator/evaluate.d.ts +6 -0
  212. package/dist/moderator/evaluate.d.ts.map +1 -0
  213. package/dist/moderator/evaluate.js +65 -0
  214. package/dist/moderator/evaluate.js.map +1 -0
  215. package/dist/moderator/index.d.ts +4 -0
  216. package/dist/moderator/index.d.ts.map +1 -0
  217. package/dist/moderator/index.js +3 -0
  218. package/dist/moderator/index.js.map +1 -0
  219. package/dist/moderator/types.d.ts +25 -0
  220. package/dist/moderator/types.d.ts.map +1 -0
  221. package/dist/moderator/types.js +4 -0
  222. package/dist/moderator/types.js.map +1 -0
  223. package/dist/schemas.d.ts +16 -0
  224. package/dist/schemas.d.ts.map +1 -0
  225. package/dist/schemas.js +17 -0
  226. package/dist/schemas.js.map +1 -0
  227. package/dist/store.d.ts +77 -0
  228. package/dist/store.d.ts.map +1 -0
  229. package/dist/store.js +392 -0
  230. package/dist/store.js.map +1 -0
  231. package/dist/validate-semantic.d.ts +7 -0
  232. package/dist/validate-semantic.d.ts.map +1 -0
  233. package/dist/validate-semantic.js +263 -0
  234. package/dist/validate-semantic.js.map +1 -0
  235. package/dist/validate.d.ts +16 -0
  236. package/dist/validate.d.ts.map +1 -0
  237. package/dist/validate.js +115 -0
  238. package/dist/validate.js.map +1 -0
  239. package/package.json +44 -0
  240. package/src/__tests__/adapter-json-roundtrip.test.ts +181 -0
  241. package/src/__tests__/config.test.ts +740 -0
  242. package/src/__tests__/current-role.test.ts +438 -0
  243. package/src/__tests__/e2e-mock-agent.test.ts +498 -0
  244. package/src/__tests__/fixtures/e2e-completed-resume.mock.yaml +15 -0
  245. package/src/__tests__/fixtures/e2e-count.mock.yaml +19 -0
  246. package/src/__tests__/fixtures/e2e-count.workflow.yaml +45 -0
  247. package/src/__tests__/fixtures/e2e-linear.mock.yaml +13 -0
  248. package/src/__tests__/fixtures/e2e-linear.workflow.yaml +32 -0
  249. package/src/__tests__/fixtures/e2e-loop.mock.yaml +25 -0
  250. package/src/__tests__/fixtures/e2e-loop.workflow.yaml +36 -0
  251. package/src/__tests__/fixtures/e2e-mismatch.mock.yaml +16 -0
  252. package/src/__tests__/fixtures/e2e-mustache.mock.yaml +15 -0
  253. package/src/__tests__/fixtures/e2e-mustache.workflow.yaml +34 -0
  254. package/src/__tests__/fixtures/e2e-suspend.mock.yaml +14 -0
  255. package/src/__tests__/fixtures/e2e-suspend.workflow.yaml +24 -0
  256. package/src/__tests__/include-tag.test.ts +84 -0
  257. package/src/__tests__/log.test.ts +181 -0
  258. package/src/__tests__/moderator-evaluate.test.ts +186 -0
  259. package/src/__tests__/preload.ts +7 -0
  260. package/src/__tests__/prompt.test.ts +129 -0
  261. package/src/__tests__/resolve-head-hash.test.ts +86 -0
  262. package/src/__tests__/setup-agent-discovery.test.ts +167 -0
  263. package/src/__tests__/setup-complexity.test.ts +381 -0
  264. package/src/__tests__/setup-validate.test.ts +148 -0
  265. package/src/__tests__/solve-issue-tea-worktree.test.ts +144 -0
  266. package/src/__tests__/spawn-agent-json.test.ts +100 -0
  267. package/src/__tests__/step-read.test.ts +632 -0
  268. package/src/__tests__/step-show-json.test.ts +373 -0
  269. package/src/__tests__/step-timing.test.ts +392 -0
  270. package/src/__tests__/store-global-cas.test.ts +308 -0
  271. package/src/__tests__/store-storage-root.test.ts +49 -0
  272. package/src/__tests__/store-unified-threads.test.ts +235 -0
  273. package/src/__tests__/thread-cancel-status.test.ts +138 -0
  274. package/src/__tests__/thread-list-filters.test.ts +572 -0
  275. package/src/__tests__/thread-location.test.ts +186 -0
  276. package/src/__tests__/thread-read-quota.test.ts +613 -0
  277. package/src/__tests__/thread-read-xml-tags.test.ts +717 -0
  278. package/src/__tests__/thread-resume.test.ts +710 -0
  279. package/src/__tests__/thread-show-status.test.ts +317 -0
  280. package/src/__tests__/thread-start-cwd-cli.test.ts +164 -0
  281. package/src/__tests__/thread-step-count.test.ts +70 -0
  282. package/src/__tests__/thread-suspend-step.test.ts +181 -0
  283. package/src/__tests__/thread-suspended-display.test.ts +287 -0
  284. package/src/__tests__/thread-test-helpers.ts +37 -0
  285. package/src/__tests__/thread.test.ts +1025 -0
  286. package/src/__tests__/validate-semantic.test.ts +474 -0
  287. package/src/__tests__/workflow-resolution.test.ts +421 -0
  288. package/src/background/background.ts +147 -0
  289. package/src/background/index.ts +11 -0
  290. package/src/background/types.ts +9 -0
  291. package/src/cli.ts +692 -0
  292. package/src/commands/config.ts +304 -0
  293. package/src/commands/log.ts +116 -0
  294. package/src/commands/prompt.ts +81 -0
  295. package/src/commands/setup.ts +603 -0
  296. package/src/commands/shared.ts +227 -0
  297. package/src/commands/step.ts +343 -0
  298. package/src/commands/thread-time-parser.ts +23 -0
  299. package/src/commands/thread.ts +1575 -0
  300. package/src/commands/workflow.ts +213 -0
  301. package/src/format.ts +12 -0
  302. package/src/include.ts +37 -0
  303. package/src/moderator/__tests__/evaluate.test.ts +199 -0
  304. package/src/moderator/evaluate.ts +80 -0
  305. package/src/moderator/index.ts +7 -0
  306. package/src/moderator/types.ts +24 -0
  307. package/src/schemas.ts +26 -0
  308. package/src/store.ts +479 -0
  309. package/src/validate-semantic.ts +304 -0
  310. package/src/validate.ts +137 -0
@@ -0,0 +1,438 @@
1
+ import { mkdir, rm, writeFile } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { putSchema } from "@ocas/core";
5
+ import type { CasRef, ThreadId } from "@united-workforce/protocol";
6
+ import { describe, expect, test } from "vitest";
7
+ import { createMarker, deleteMarker } from "../background/index.js";
8
+ import { cmdThreadList, cmdThreadShow, cmdThreadStart } from "../commands/thread.js";
9
+ import { completeThread, createUwfStore, loadActiveThreads, setThread } from "../store.js";
10
+
11
+ const OUTPUT_SCHEMA = {
12
+ type: "object" as const,
13
+ properties: {
14
+ $status: { type: "string" as const },
15
+ },
16
+ };
17
+
18
+ const SIMPLE_WORKFLOW_YAML = `
19
+ name: test-current-role
20
+ description: Test workflow for currentRole
21
+ roles:
22
+ roleA:
23
+ description: First role
24
+ goal: Do A
25
+ capabilities: ["coding"]
26
+ procedure: Do A
27
+ output: |
28
+ $status: "ready"
29
+ frontmatter:
30
+ type: object
31
+ required: ["$status"]
32
+ properties:
33
+ $status: { type: string, enum: ["ready", "not-ready"] }
34
+ roleB:
35
+ description: Second role
36
+ goal: Do B
37
+ capabilities: ["coding"]
38
+ procedure: Do B
39
+ output: |
40
+ $status: "done"
41
+ frontmatter:
42
+ type: object
43
+ required: ["$status"]
44
+ properties:
45
+ $status: { type: string, enum: ["done"] }
46
+ graph:
47
+ $START:
48
+ _:
49
+ role: roleA
50
+ prompt: "Do A"
51
+ location: null
52
+ roleA:
53
+ ready:
54
+ role: roleB
55
+ prompt: "Do B"
56
+ location: null
57
+ not-ready:
58
+ role: roleA
59
+ prompt: "Try again"
60
+ location: null
61
+ roleB:
62
+ done:
63
+ role: $END
64
+ prompt: "Done"
65
+ location: null
66
+ `;
67
+
68
+ const CONDITIONAL_WORKFLOW_YAML = `
69
+ name: test-conditional-role
70
+ description: Conditional routing workflow
71
+ roles:
72
+ roleA:
73
+ description: First role
74
+ goal: Do A
75
+ capabilities: ["coding"]
76
+ procedure: Do A
77
+ output: |
78
+ $status: "pass"
79
+ frontmatter:
80
+ type: object
81
+ required: ["$status"]
82
+ properties:
83
+ $status: { type: string, enum: ["pass", "fail"] }
84
+ roleB:
85
+ description: Pass role
86
+ goal: Do B
87
+ capabilities: ["coding"]
88
+ procedure: Do B
89
+ output: |
90
+ $status: "done"
91
+ frontmatter:
92
+ type: object
93
+ required: ["$status"]
94
+ properties:
95
+ $status: { type: string, enum: ["done"] }
96
+ roleC:
97
+ description: Fail role
98
+ goal: Do C
99
+ capabilities: ["coding"]
100
+ procedure: Do C
101
+ output: |
102
+ $status: "done"
103
+ frontmatter:
104
+ type: object
105
+ required: ["$status"]
106
+ properties:
107
+ $status: { type: string, enum: ["done"] }
108
+ graph:
109
+ $START:
110
+ _:
111
+ role: roleA
112
+ prompt: "Do A"
113
+ location: null
114
+ roleA:
115
+ pass:
116
+ role: roleB
117
+ prompt: "Do B (pass)"
118
+ location: null
119
+ fail:
120
+ role: roleC
121
+ prompt: "Do C (fail)"
122
+ location: null
123
+ roleB:
124
+ done:
125
+ role: $END
126
+ prompt: "Done"
127
+ location: null
128
+ roleC:
129
+ done:
130
+ role: $END
131
+ prompt: "Done"
132
+ location: null
133
+ `;
134
+
135
+ const SINGLE_ROLE_WORKFLOW_YAML = `
136
+ name: test-single-role
137
+ description: Single role that goes to END
138
+ roles:
139
+ worker:
140
+ description: Worker
141
+ goal: Work
142
+ capabilities: ["coding"]
143
+ procedure: Work
144
+ output: |
145
+ $status: "done"
146
+ frontmatter:
147
+ type: object
148
+ required: ["$status"]
149
+ properties:
150
+ $status: { type: string, enum: ["done"] }
151
+ graph:
152
+ $START:
153
+ _:
154
+ role: worker
155
+ prompt: "Work"
156
+ location: null
157
+ worker:
158
+ done:
159
+ role: $END
160
+ prompt: "Done"
161
+ location: null
162
+ `;
163
+
164
+ /** Helper: insert a completed step node after the current head. */
165
+ async function insertStepNode(
166
+ storageRoot: string,
167
+ threadId: ThreadId,
168
+ role: string,
169
+ outputPayload: Record<string, unknown>,
170
+ ): Promise<void> {
171
+ const uwf = await createUwfStore(storageRoot);
172
+ const index = loadActiveThreads(uwf.varStore);
173
+ const headEntry = index[threadId];
174
+ if (headEntry === undefined) throw new Error(`thread ${threadId} not in index`);
175
+ const head = headEntry.head;
176
+
177
+ const outputSchemaHash = await putSchema(uwf.store, OUTPUT_SCHEMA);
178
+ const outputHash = await uwf.store.cas.put(outputSchemaHash, outputPayload);
179
+
180
+ // Use text schema for detail (simple placeholder)
181
+ const detailHash = await uwf.store.cas.put(uwf.schemas.text, "detail-placeholder");
182
+
183
+ // Resolve start hash from head
184
+ const headNode = uwf.store.cas.get(head);
185
+ if (headNode === null) throw new Error(`head ${head} not found`);
186
+ const isStart = headNode.type === uwf.schemas.startNode;
187
+ const startHash = isStart ? head : (headNode.payload as { start: CasRef }).start;
188
+
189
+ const stepHash = (await uwf.store.cas.put(uwf.schemas.stepNode, {
190
+ start: startHash,
191
+ prev: isStart ? null : head,
192
+ role,
193
+ output: outputHash,
194
+ detail: detailHash,
195
+ agent: "uwf-test",
196
+ edgePrompt: `Do ${role}`,
197
+ startedAtMs: Date.now(),
198
+ completedAtMs: Date.now() + 1,
199
+ cwd: storageRoot,
200
+ assembledPrompt: null,
201
+ })) as CasRef;
202
+
203
+ setThread(uwf.varStore, threadId, {
204
+ head: stepHash,
205
+ status: "idle",
206
+ suspendedRole: null,
207
+ suspendMessage: null,
208
+ completedAt: null,
209
+ });
210
+ }
211
+
212
+ describe("currentRole field", () => {
213
+ let tmpDir: string;
214
+ let storageRoot: string;
215
+ let casDir: string;
216
+ let originalEnv: string | undefined;
217
+
218
+ async function setup() {
219
+ tmpDir = join(
220
+ tmpdir(),
221
+ `uwf-test-current-role-${Date.now()}-${Math.random().toString(36).slice(2)}`,
222
+ );
223
+ storageRoot = join(tmpDir, "storage");
224
+ casDir = join(tmpDir, "cas");
225
+ await mkdir(storageRoot, { recursive: true });
226
+ await mkdir(casDir, { recursive: true });
227
+
228
+ // Set OCAS_HOME for this test
229
+ originalEnv = process.env.OCAS_HOME;
230
+ process.env.OCAS_HOME = casDir;
231
+ }
232
+
233
+ async function teardown() {
234
+ if (tmpDir) {
235
+ await rm(tmpDir, { recursive: true, force: true });
236
+ }
237
+ // Restore original environment
238
+ if (originalEnv === undefined) {
239
+ delete process.env.OCAS_HOME;
240
+ } else {
241
+ process.env.OCAS_HOME = originalEnv;
242
+ }
243
+ }
244
+
245
+ // T1: idle at start — currentRole = first role from graph
246
+ test("thread show — idle at start returns first role as currentRole", async () => {
247
+ await setup();
248
+ try {
249
+ const wf = join(tmpDir, "test-current-role.yaml");
250
+ await writeFile(wf, SIMPLE_WORKFLOW_YAML, "utf8");
251
+ const { thread } = await cmdThreadStart(storageRoot, wf, "test", tmpDir);
252
+
253
+ const result = await cmdThreadShow(storageRoot, thread as ThreadId);
254
+ expect(result.status).toBe("idle");
255
+ expect(result.currentRole).toBe("roleA");
256
+ } finally {
257
+ await teardown();
258
+ }
259
+ });
260
+
261
+ // T2: idle after one step — currentRole = next role
262
+ test("thread show — idle after step returns next role as currentRole", async () => {
263
+ await setup();
264
+ try {
265
+ const wf = join(tmpDir, "test-current-role.yaml");
266
+ await writeFile(wf, SIMPLE_WORKFLOW_YAML, "utf8");
267
+ const { thread } = await cmdThreadStart(storageRoot, wf, "test", tmpDir);
268
+
269
+ await insertStepNode(storageRoot, thread as ThreadId, "roleA", { $status: "ready" });
270
+
271
+ const result = await cmdThreadShow(storageRoot, thread as ThreadId);
272
+ expect(result.status).toBe("idle");
273
+ expect(result.currentRole).toBe("roleB");
274
+ } finally {
275
+ await teardown();
276
+ }
277
+ });
278
+
279
+ // T3: completed → currentRole = null
280
+ test("thread show — completed thread returns null currentRole", async () => {
281
+ await setup();
282
+ try {
283
+ const wf = join(tmpDir, "test-current-role.yaml");
284
+ await writeFile(wf, SIMPLE_WORKFLOW_YAML, "utf8");
285
+ const { thread } = await cmdThreadStart(storageRoot, wf, "test", tmpDir);
286
+ const tid = thread as ThreadId;
287
+
288
+ const uwfForIndex = await createUwfStore(storageRoot);
289
+ loadActiveThreads(uwfForIndex.varStore)[tid]!.head;
290
+ completeThread(uwfForIndex.varStore, tid, "completed");
291
+
292
+ const result = await cmdThreadShow(storageRoot, tid);
293
+ expect(result.status).toBe("completed");
294
+ expect(result.currentRole).toBe(null);
295
+ } finally {
296
+ await teardown();
297
+ }
298
+ });
299
+
300
+ // T4: cancelled → currentRole = null
301
+ test("thread show — cancelled thread returns null currentRole", async () => {
302
+ await setup();
303
+ try {
304
+ const wf = join(tmpDir, "test-current-role.yaml");
305
+ await writeFile(wf, SIMPLE_WORKFLOW_YAML, "utf8");
306
+ const { thread } = await cmdThreadStart(storageRoot, wf, "test", tmpDir);
307
+ const tid = thread as ThreadId;
308
+
309
+ const uwfForIndex = await createUwfStore(storageRoot);
310
+ loadActiveThreads(uwfForIndex.varStore)[tid]!.head;
311
+ completeThread(uwfForIndex.varStore, tid, "cancelled");
312
+
313
+ const result = await cmdThreadShow(storageRoot, tid);
314
+ expect(result.status).toBe("cancelled");
315
+ expect(result.currentRole).toBe(null);
316
+ } finally {
317
+ await teardown();
318
+ }
319
+ });
320
+
321
+ // T5: running → currentRole = role being executed
322
+ test("thread show — running thread returns current role", async () => {
323
+ await setup();
324
+ try {
325
+ const wf = join(tmpDir, "test-current-role.yaml");
326
+ await writeFile(wf, SIMPLE_WORKFLOW_YAML, "utf8");
327
+ const { thread, workflow } = await cmdThreadStart(storageRoot, wf, "test", tmpDir);
328
+ const tid = thread as ThreadId;
329
+
330
+ await createMarker(storageRoot, {
331
+ thread: tid,
332
+ workflow,
333
+ pid: process.pid,
334
+ startedAt: Date.now(),
335
+ });
336
+
337
+ try {
338
+ const result = await cmdThreadShow(storageRoot, tid);
339
+ expect(result.status).toBe("running");
340
+ expect(result.currentRole).toBe("roleA");
341
+ } finally {
342
+ await deleteMarker(storageRoot, tid);
343
+ }
344
+ } finally {
345
+ await teardown();
346
+ }
347
+ });
348
+
349
+ // T6: thread list — mixed statuses with correct currentRole
350
+ test("thread list — returns correct currentRole for each status", async () => {
351
+ await setup();
352
+ try {
353
+ const wf = join(tmpDir, "test-current-role.yaml");
354
+ await writeFile(wf, SIMPLE_WORKFLOW_YAML, "utf8");
355
+
356
+ // idle thread
357
+ const idle = await cmdThreadStart(storageRoot, wf, "idle", tmpDir);
358
+ const idleId = idle.thread as ThreadId;
359
+
360
+ // completed thread
361
+ const comp = await cmdThreadStart(storageRoot, wf, "completed", tmpDir);
362
+ const compId = comp.thread as ThreadId;
363
+ const uwfForIndex = await createUwfStore(storageRoot);
364
+ const _compHead = loadActiveThreads(uwfForIndex.varStore)[compId]!.head;
365
+ completeThread(uwfForIndex.varStore, compId, "completed");
366
+
367
+ const list = await cmdThreadList(storageRoot, null, null, null, 0, 100);
368
+
369
+ const idleItem = list.find((i) => i.thread === idleId);
370
+ expect(idleItem).toBeDefined();
371
+ expect(idleItem!.currentRole).toBe("roleA");
372
+
373
+ const compItem = list.find((i) => i.thread === compId);
374
+ expect(compItem).toBeDefined();
375
+ expect(compItem!.currentRole).toBe(null);
376
+ } finally {
377
+ await teardown();
378
+ }
379
+ });
380
+
381
+ // T7: thread list — idle at start has correct currentRole
382
+ test("thread list — idle thread at start has correct currentRole", async () => {
383
+ await setup();
384
+ try {
385
+ const wf = join(tmpDir, "test-current-role.yaml");
386
+ await writeFile(wf, SIMPLE_WORKFLOW_YAML, "utf8");
387
+ const { thread } = await cmdThreadStart(storageRoot, wf, "test", tmpDir);
388
+
389
+ const list = await cmdThreadList(storageRoot, null, null, null, 0, 100);
390
+ const item = list.find((i) => i.thread === (thread as ThreadId));
391
+ expect(item).toBeDefined();
392
+ expect(item!.currentRole).toBe("roleA");
393
+ } finally {
394
+ await teardown();
395
+ }
396
+ });
397
+
398
+ // T8: conditional routing — $status=pass vs fail
399
+ test("thread show — conditional routing selects correct next role", async () => {
400
+ await setup();
401
+ try {
402
+ const wf = join(tmpDir, "test-conditional-role.yaml");
403
+ await writeFile(wf, CONDITIONAL_WORKFLOW_YAML, "utf8");
404
+
405
+ // pass path
406
+ const t1 = await cmdThreadStart(storageRoot, wf, "pass test", tmpDir);
407
+ await insertStepNode(storageRoot, t1.thread as ThreadId, "roleA", { $status: "pass" });
408
+ const r1 = await cmdThreadShow(storageRoot, t1.thread as ThreadId);
409
+ expect(r1.currentRole).toBe("roleB");
410
+
411
+ // fail path
412
+ const t2 = await cmdThreadStart(storageRoot, wf, "fail test", tmpDir);
413
+ await insertStepNode(storageRoot, t2.thread as ThreadId, "roleA", { $status: "fail" });
414
+ const r2 = await cmdThreadShow(storageRoot, t2.thread as ThreadId);
415
+ expect(r2.currentRole).toBe("roleC");
416
+ } finally {
417
+ await teardown();
418
+ }
419
+ });
420
+
421
+ // T9: next role is $END → currentRole = null
422
+ test("thread show — when next is $END, currentRole is null", async () => {
423
+ await setup();
424
+ try {
425
+ const wf = join(tmpDir, "test-single-role.yaml");
426
+ await writeFile(wf, SINGLE_ROLE_WORKFLOW_YAML, "utf8");
427
+
428
+ const { thread } = await cmdThreadStart(storageRoot, wf, "test", tmpDir);
429
+ // worker → done maps to $END
430
+ await insertStepNode(storageRoot, thread as ThreadId, "worker", { $status: "done" });
431
+
432
+ const result = await cmdThreadShow(storageRoot, thread as ThreadId);
433
+ expect(result.currentRole).toBe(null);
434
+ } finally {
435
+ await teardown();
436
+ }
437
+ });
438
+ });