@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,632 @@
1
+ import { mkdir, mkdtemp, rm } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { bootstrap, putSchema } from "@ocas/core";
5
+ import { openStore } from "@ocas/fs";
6
+ import type { CasRef } from "@united-workforce/protocol";
7
+ import { afterEach, beforeEach, describe, expect, test } from "vitest";
8
+ import { cmdStepRead } from "../commands/step.js";
9
+ import { registerUwfSchemas } from "../schemas.js";
10
+
11
+ // ── schemas used in tests ────────────────────────────────────────────────────
12
+
13
+ const TURN_SCHEMA = {
14
+ title: "hermes-turn",
15
+ type: "object" as const,
16
+ required: ["index", "role", "content"],
17
+ properties: {
18
+ index: { type: "integer" as const },
19
+ role: { type: "string" as const },
20
+ content: { type: "string" as const },
21
+ toolCalls: {
22
+ anyOf: [
23
+ { type: "array" as const, items: { type: "object" as const } },
24
+ { type: "null" as const },
25
+ ],
26
+ },
27
+ reasoning: { anyOf: [{ type: "string" as const }, { type: "null" as const }] },
28
+ },
29
+ additionalProperties: false,
30
+ };
31
+
32
+ const DETAIL_SCHEMA = {
33
+ title: "hermes-detail",
34
+ type: "object" as const,
35
+ required: ["sessionId", "model", "duration", "turnCount", "turns"],
36
+ properties: {
37
+ sessionId: { type: "string" as const },
38
+ model: { type: "string" as const },
39
+ duration: { type: "integer" as const },
40
+ turnCount: { type: "integer" as const },
41
+ turns: {
42
+ type: "array" as const,
43
+ items: { type: "string" as const, format: "ocas_ref" },
44
+ },
45
+ },
46
+ additionalProperties: false,
47
+ };
48
+
49
+ // ── helpers ───────────────────────────────────────────────────────────────────
50
+
51
+ async function registerDetailSchemas(store: Awaited<ReturnType<typeof openStore>>) {
52
+ await bootstrap(store);
53
+ const [turn, detail] = await Promise.all([
54
+ putSchema(store, TURN_SCHEMA),
55
+ putSchema(store, DETAIL_SCHEMA),
56
+ ]);
57
+ return { turn, detail };
58
+ }
59
+
60
+ function generateContent(size: number, prefix = "Content"): string {
61
+ const base = `${prefix} `;
62
+ const repeat = Math.ceil(size / base.length);
63
+ return base.repeat(repeat).slice(0, size);
64
+ }
65
+
66
+ // ── fixture ───────────────────────────────────────────────────────────────────
67
+
68
+ let tmpDir: string;
69
+ let originalEnv: string | undefined;
70
+
71
+ beforeEach(async () => {
72
+ tmpDir = await mkdtemp(join(tmpdir(), "cli-uwf-step-read-test-"));
73
+ originalEnv = process.env.OCAS_HOME;
74
+ });
75
+
76
+ afterEach(async () => {
77
+ await rm(tmpDir, { recursive: true, force: true });
78
+ // Restore original environment
79
+ if (originalEnv === undefined) {
80
+ delete process.env.OCAS_HOME;
81
+ } else {
82
+ process.env.OCAS_HOME = originalEnv;
83
+ }
84
+ });
85
+
86
+ // ── step read tests ───────────────────────────────────────────────────────────
87
+
88
+ describe("step read", () => {
89
+ test("test 1: basic single-step read with 3 turns", async () => {
90
+ const casDir = join(tmpDir, "cas");
91
+ process.env.OCAS_HOME = casDir;
92
+ await mkdir(casDir, { recursive: true });
93
+ process.env.OCAS_HOME = casDir;
94
+ process.env.OCAS_HOME = casDir;
95
+ const store = await openStore(casDir);
96
+ const schemas = await registerUwfSchemas(store);
97
+ const detailSchemas = await registerDetailSchemas(store);
98
+
99
+ const workflowHash = await store.cas.put(schemas.workflow, {
100
+ name: "test-wf",
101
+ description: "desc",
102
+ roles: {
103
+ worker: {
104
+ description: "Worker",
105
+ goal: "You are a worker agent.",
106
+ capabilities: [],
107
+ procedure: "Do the work.",
108
+ output: "Summarize the work.",
109
+ meta: "placeholder00" as CasRef,
110
+ },
111
+ },
112
+ conditions: {},
113
+ graph: {},
114
+ });
115
+
116
+ const startHash = await store.cas.put(schemas.startNode, {
117
+ workflow: workflowHash,
118
+ prompt: "Test task",
119
+ });
120
+
121
+ const outputHash = await store.cas.put(schemas.workflow, {
122
+ name: "out",
123
+ description: "",
124
+ roles: {},
125
+ conditions: {},
126
+ graph: {},
127
+ });
128
+
129
+ // Create 3 turns
130
+ const turnHashes: CasRef[] = [];
131
+ for (let i = 1; i <= 3; i++) {
132
+ const content = `Turn ${i} content with some text to make it readable.`;
133
+ const turnHash = await store.cas.put(detailSchemas.turn, {
134
+ index: i - 1,
135
+ role: "assistant",
136
+ content,
137
+ toolCalls: null,
138
+ reasoning: null,
139
+ });
140
+ turnHashes.push(turnHash);
141
+ }
142
+
143
+ const detailHash = await store.cas.put(detailSchemas.detail, {
144
+ sessionId: "session-1",
145
+ model: "test-model",
146
+ duration: 1000,
147
+ turnCount: 3,
148
+ turns: turnHashes,
149
+ });
150
+
151
+ const stepHash = await store.cas.put(schemas.stepNode, {
152
+ start: startHash,
153
+ prev: null,
154
+ role: "worker",
155
+ output: outputHash,
156
+ detail: detailHash,
157
+ agent: "uwf-test",
158
+ startedAtMs: 1000000000000,
159
+ completedAtMs: 1000000005000,
160
+ assembledPrompt: null,
161
+ });
162
+
163
+ // Read step with large quota
164
+ const markdown = await cmdStepRead(tmpDir, stepHash, 10000, false);
165
+
166
+ // Assert structure
167
+ expect(markdown).toContain(`# Step ${stepHash}`);
168
+ expect(markdown).toContain("**Role:** worker");
169
+ expect(markdown).toContain("**Agent:** uwf-test");
170
+ expect(markdown).toContain("## Turn 1");
171
+ expect(markdown).toContain("## Turn 2");
172
+ expect(markdown).toContain("## Turn 3");
173
+ expect(markdown).toContain("Turn 1 content with some text to make it readable.");
174
+ expect(markdown).toContain("Turn 2 content with some text to make it readable.");
175
+ expect(markdown).toContain("Turn 3 content with some text to make it readable.");
176
+ });
177
+
178
+ test("test 2: quota enforcement - multiple turns", async () => {
179
+ const casDir = join(tmpDir, "cas");
180
+ process.env.OCAS_HOME = casDir;
181
+ await mkdir(casDir, { recursive: true });
182
+ process.env.OCAS_HOME = casDir;
183
+ const store = await openStore(casDir);
184
+ const schemas = await registerUwfSchemas(store);
185
+ const detailSchemas = await registerDetailSchemas(store);
186
+
187
+ const workflowHash = await store.cas.put(schemas.workflow, {
188
+ name: "test-wf",
189
+ description: "desc",
190
+ roles: {
191
+ worker: {
192
+ description: "Worker",
193
+ goal: "You are a worker agent.",
194
+ capabilities: [],
195
+ procedure: "Do the work.",
196
+ output: "Summarize the work.",
197
+ meta: "placeholder00" as CasRef,
198
+ },
199
+ },
200
+ conditions: {},
201
+ graph: {},
202
+ });
203
+
204
+ const startHash = await store.cas.put(schemas.startNode, {
205
+ workflow: workflowHash,
206
+ prompt: "Test task",
207
+ });
208
+
209
+ const outputHash = await store.cas.put(schemas.workflow, {
210
+ name: "out",
211
+ description: "",
212
+ roles: {},
213
+ conditions: {},
214
+ graph: {},
215
+ });
216
+
217
+ // Create 4 turns of ~300 chars each
218
+ const turnHashes: CasRef[] = [];
219
+ for (let i = 1; i <= 4; i++) {
220
+ const content = generateContent(300, `Turn${i}`);
221
+ const turnHash = await store.cas.put(detailSchemas.turn, {
222
+ index: i - 1,
223
+ role: "assistant",
224
+ content,
225
+ toolCalls: null,
226
+ reasoning: null,
227
+ });
228
+ turnHashes.push(turnHash);
229
+ }
230
+
231
+ const detailHash = await store.cas.put(detailSchemas.detail, {
232
+ sessionId: "session-1",
233
+ model: "test-model",
234
+ duration: 1000,
235
+ turnCount: 4,
236
+ turns: turnHashes,
237
+ });
238
+
239
+ const stepHash = await store.cas.put(schemas.stepNode, {
240
+ start: startHash,
241
+ prev: null,
242
+ role: "worker",
243
+ output: outputHash,
244
+ detail: detailHash,
245
+ agent: "uwf-test",
246
+ startedAtMs: 1000000000000,
247
+ completedAtMs: 1000000005000,
248
+ assembledPrompt: null,
249
+ });
250
+
251
+ // Read step with limited quota (700 chars)
252
+ const markdown = await cmdStepRead(tmpDir, stepHash, 700, false);
253
+
254
+ // Assert only most recent turns fit
255
+ expect(markdown).toContain(`# Step ${stepHash}`);
256
+ // Should have skip hint
257
+ expect(markdown).toContain("Earlier turns omitted");
258
+ // Should include at least Turn 4 (most recent)
259
+ expect(markdown).toContain("Turn4");
260
+ // Total length should respect quota (with tolerance for structural overhead)
261
+ expect(markdown.length).toBeLessThanOrEqual(900); // 700 quota + 200 buffer tolerance
262
+ });
263
+
264
+ test("test 3: minimal quota edge case - always show at least one turn", async () => {
265
+ const casDir = join(tmpDir, "cas");
266
+ process.env.OCAS_HOME = casDir;
267
+ await mkdir(casDir, { recursive: true });
268
+ process.env.OCAS_HOME = casDir;
269
+ const store = await openStore(casDir);
270
+ const schemas = await registerUwfSchemas(store);
271
+ const detailSchemas = await registerDetailSchemas(store);
272
+
273
+ const workflowHash = await store.cas.put(schemas.workflow, {
274
+ name: "test-wf",
275
+ description: "desc",
276
+ roles: {
277
+ worker: {
278
+ description: "Worker",
279
+ goal: "You are a worker agent.",
280
+ capabilities: [],
281
+ procedure: "Do the work.",
282
+ output: "Summarize the work.",
283
+ meta: "placeholder00" as CasRef,
284
+ },
285
+ },
286
+ conditions: {},
287
+ graph: {},
288
+ });
289
+
290
+ const startHash = await store.cas.put(schemas.startNode, {
291
+ workflow: workflowHash,
292
+ prompt: "Test task",
293
+ });
294
+
295
+ const outputHash = await store.cas.put(schemas.workflow, {
296
+ name: "out",
297
+ description: "",
298
+ roles: {},
299
+ conditions: {},
300
+ graph: {},
301
+ });
302
+
303
+ // Create 1 turn of 500 chars
304
+ const content = generateContent(500, "LongTurn");
305
+ const turnHash = await store.cas.put(detailSchemas.turn, {
306
+ index: 0,
307
+ role: "assistant",
308
+ content,
309
+ toolCalls: null,
310
+ reasoning: null,
311
+ });
312
+
313
+ const detailHash = await store.cas.put(detailSchemas.detail, {
314
+ sessionId: "session-1",
315
+ model: "test-model",
316
+ duration: 1000,
317
+ turnCount: 1,
318
+ turns: [turnHash],
319
+ });
320
+
321
+ const stepHash = await store.cas.put(schemas.stepNode, {
322
+ start: startHash,
323
+ prev: null,
324
+ role: "worker",
325
+ output: outputHash,
326
+ detail: detailHash,
327
+ agent: "uwf-test",
328
+ startedAtMs: 1000000000000,
329
+ completedAtMs: 1000000005000,
330
+ assembledPrompt: null,
331
+ });
332
+
333
+ // Read step with minimal quota (1 char)
334
+ const markdown = await cmdStepRead(tmpDir, stepHash, 1, false);
335
+
336
+ // Assert at least one turn is always shown
337
+ expect(markdown).toContain("LongTurn");
338
+ expect(markdown.length).toBeGreaterThan(1);
339
+ });
340
+
341
+ test("test 4: step with no detail field", async () => {
342
+ const casDir = join(tmpDir, "cas");
343
+ process.env.OCAS_HOME = casDir;
344
+ await mkdir(casDir, { recursive: true });
345
+ process.env.OCAS_HOME = casDir;
346
+ const store = await openStore(casDir);
347
+ const schemas = await registerUwfSchemas(store);
348
+
349
+ const workflowHash = await store.cas.put(schemas.workflow, {
350
+ name: "test-wf",
351
+ description: "desc",
352
+ roles: {
353
+ worker: {
354
+ description: "Worker",
355
+ goal: "You are a worker agent.",
356
+ capabilities: [],
357
+ procedure: "Do the work.",
358
+ output: "Summarize the work.",
359
+ meta: "placeholder00" as CasRef,
360
+ },
361
+ },
362
+ conditions: {},
363
+ graph: {},
364
+ });
365
+
366
+ const startHash = await store.cas.put(schemas.startNode, {
367
+ workflow: workflowHash,
368
+ prompt: "Test task",
369
+ });
370
+
371
+ const outputHash = await store.cas.put(schemas.workflow, {
372
+ name: "out",
373
+ description: "",
374
+ roles: {},
375
+ conditions: {},
376
+ graph: {},
377
+ });
378
+
379
+ const stepHash = await store.cas.put(schemas.stepNode, {
380
+ start: startHash,
381
+ prev: null,
382
+ role: "worker",
383
+ output: outputHash,
384
+ detail: null,
385
+ agent: "uwf-test",
386
+ startedAtMs: 1000000000000,
387
+ completedAtMs: 1000000005000,
388
+ assembledPrompt: null,
389
+ });
390
+
391
+ // Read step - should return metadata only (no error)
392
+ const markdown = await cmdStepRead(tmpDir, stepHash, 4000, false);
393
+
394
+ // Assert metadata is present
395
+ expect(markdown).toContain(`# Step ${stepHash}`);
396
+ expect(markdown).toContain("**Role:** worker");
397
+ expect(markdown).toContain("**Agent:** uwf-test");
398
+ // Should not have turn sections
399
+ expect(markdown).not.toContain("## Turn");
400
+ });
401
+
402
+ test("test 5: step with detail but no turns array", async () => {
403
+ const casDir = join(tmpDir, "cas");
404
+ process.env.OCAS_HOME = casDir;
405
+ await mkdir(casDir, { recursive: true });
406
+ process.env.OCAS_HOME = casDir;
407
+ const store = await openStore(casDir);
408
+ const schemas = await registerUwfSchemas(store);
409
+ await registerDetailSchemas(store);
410
+
411
+ const workflowHash = await store.cas.put(schemas.workflow, {
412
+ name: "test-wf",
413
+ description: "desc",
414
+ roles: {
415
+ worker: {
416
+ description: "Worker",
417
+ goal: "You are a worker agent.",
418
+ capabilities: [],
419
+ procedure: "Do the work.",
420
+ output: "Summarize the work.",
421
+ meta: "placeholder00" as CasRef,
422
+ },
423
+ },
424
+ conditions: {},
425
+ graph: {},
426
+ });
427
+
428
+ const startHash = await store.cas.put(schemas.startNode, {
429
+ workflow: workflowHash,
430
+ prompt: "Test task",
431
+ });
432
+
433
+ const outputHash = await store.cas.put(schemas.workflow, {
434
+ name: "out",
435
+ description: "",
436
+ roles: {},
437
+ conditions: {},
438
+ graph: {},
439
+ });
440
+
441
+ // Create detail with different schema (no turns)
442
+ const SIMPLE_DETAIL_SCHEMA = {
443
+ title: "simple-detail",
444
+ type: "object" as const,
445
+ required: ["sessionId"],
446
+ properties: {
447
+ sessionId: { type: "string" as const },
448
+ },
449
+ additionalProperties: false,
450
+ };
451
+
452
+ await bootstrap(store);
453
+ const simpleDetailType = await putSchema(store, SIMPLE_DETAIL_SCHEMA);
454
+ const detailHash = await store.cas.put(simpleDetailType, {
455
+ sessionId: "session-1",
456
+ });
457
+
458
+ const stepHash = await store.cas.put(schemas.stepNode, {
459
+ start: startHash,
460
+ prev: null,
461
+ role: "worker",
462
+ output: outputHash,
463
+ detail: detailHash,
464
+ agent: "uwf-test",
465
+ startedAtMs: 1000000000000,
466
+ completedAtMs: 1000000005000,
467
+ assembledPrompt: null,
468
+ });
469
+
470
+ // Read step - should return metadata only (no error)
471
+ const markdown = await cmdStepRead(tmpDir, stepHash, 4000, false);
472
+
473
+ // Assert metadata is present
474
+ expect(markdown).toContain(`# Step ${stepHash}`);
475
+ expect(markdown).toContain("**Role:** worker");
476
+ // Should not have turn sections
477
+ expect(markdown).not.toContain("## Turn");
478
+ });
479
+
480
+ test("test 6: displays role and tool calls in turn body", async () => {
481
+ const casDir = join(tmpDir, "cas");
482
+ process.env.OCAS_HOME = casDir;
483
+ await mkdir(casDir, { recursive: true });
484
+ process.env.OCAS_HOME = casDir;
485
+ const store = await openStore(casDir);
486
+ const schemas = await registerUwfSchemas(store);
487
+ const detailSchemas = await registerDetailSchemas(store);
488
+
489
+ const workflowHash = await store.cas.put(schemas.workflow, {
490
+ name: "test-wf",
491
+ description: "desc",
492
+ roles: {
493
+ worker: {
494
+ description: "Worker",
495
+ goal: "You are a worker agent.",
496
+ capabilities: [],
497
+ procedure: "Do the work.",
498
+ output: "Summarize the work.",
499
+ meta: "placeholder00" as CasRef,
500
+ },
501
+ },
502
+ conditions: {},
503
+ graph: {},
504
+ });
505
+
506
+ const startHash = await store.cas.put(schemas.startNode, {
507
+ workflow: workflowHash,
508
+ prompt: "Test task",
509
+ });
510
+
511
+ const outputHash = await store.cas.put(schemas.workflow, {
512
+ name: "out",
513
+ description: "",
514
+ roles: {},
515
+ conditions: {},
516
+ graph: {},
517
+ });
518
+
519
+ const turnHash = await store.cas.put(detailSchemas.turn, {
520
+ index: 0,
521
+ role: "assistant",
522
+ content: "",
523
+ toolCalls: [{ name: "terminal", args: '{"command":"echo hi"}' }],
524
+ reasoning: null,
525
+ });
526
+
527
+ const detailHash = await store.cas.put(detailSchemas.detail, {
528
+ sessionId: "session-1",
529
+ model: "test-model",
530
+ duration: 1000,
531
+ turnCount: 1,
532
+ turns: [turnHash],
533
+ });
534
+
535
+ const stepHash = await store.cas.put(schemas.stepNode, {
536
+ start: startHash,
537
+ prev: null,
538
+ role: "worker",
539
+ output: outputHash,
540
+ detail: detailHash,
541
+ agent: "uwf-hermes",
542
+ startedAtMs: 1000000000000,
543
+ completedAtMs: 1000000005000,
544
+ assembledPrompt: null,
545
+ });
546
+
547
+ const markdown = await cmdStepRead(tmpDir, stepHash, 4000, false);
548
+
549
+ expect(markdown).toContain("**Turn role:** assistant");
550
+ expect(markdown).toContain("**terminal**");
551
+ expect(markdown).toContain('{"command":"echo hi"}');
552
+ });
553
+
554
+ test("test 7: turn content with special characters", async () => {
555
+ const casDir = join(tmpDir, "cas");
556
+ process.env.OCAS_HOME = casDir;
557
+ await mkdir(casDir, { recursive: true });
558
+ process.env.OCAS_HOME = casDir;
559
+ const store = await openStore(casDir);
560
+ const schemas = await registerUwfSchemas(store);
561
+ const detailSchemas = await registerDetailSchemas(store);
562
+
563
+ const workflowHash = await store.cas.put(schemas.workflow, {
564
+ name: "test-wf",
565
+ description: "desc",
566
+ roles: {
567
+ worker: {
568
+ description: "Worker",
569
+ goal: "You are a worker agent.",
570
+ capabilities: [],
571
+ procedure: "Do the work.",
572
+ output: "Summarize the work.",
573
+ meta: "placeholder00" as CasRef,
574
+ },
575
+ },
576
+ conditions: {},
577
+ graph: {},
578
+ });
579
+
580
+ const startHash = await store.cas.put(schemas.startNode, {
581
+ workflow: workflowHash,
582
+ prompt: "Test task",
583
+ });
584
+
585
+ const outputHash = await store.cas.put(schemas.workflow, {
586
+ name: "out",
587
+ description: "",
588
+ roles: {},
589
+ conditions: {},
590
+ graph: {},
591
+ });
592
+
593
+ // Create turn with special markdown characters
594
+ const content = "This has `backticks`, **bold**, *italic*, and [links](http://example.com)";
595
+ const turnHash = await store.cas.put(detailSchemas.turn, {
596
+ index: 0,
597
+ role: "assistant",
598
+ content,
599
+ toolCalls: null,
600
+ reasoning: null,
601
+ });
602
+
603
+ const detailHash = await store.cas.put(detailSchemas.detail, {
604
+ sessionId: "session-1",
605
+ model: "test-model",
606
+ duration: 1000,
607
+ turnCount: 1,
608
+ turns: [turnHash],
609
+ });
610
+
611
+ const stepHash = await store.cas.put(schemas.stepNode, {
612
+ start: startHash,
613
+ prev: null,
614
+ role: "worker",
615
+ output: outputHash,
616
+ detail: detailHash,
617
+ agent: "uwf-test",
618
+ startedAtMs: 1000000000000,
619
+ completedAtMs: 1000000005000,
620
+ assembledPrompt: null,
621
+ });
622
+
623
+ // Read step
624
+ const markdown = await cmdStepRead(tmpDir, stepHash, 4000, false);
625
+
626
+ // Assert content is rendered correctly without corruption
627
+ expect(markdown).toContain("`backticks`");
628
+ expect(markdown).toContain("**bold**");
629
+ expect(markdown).toContain("*italic*");
630
+ expect(markdown).toContain("[links](http://example.com)");
631
+ });
632
+ });