@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,373 @@
1
+ import { mkdir, mkdtemp, rm } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { bootstrap, type Hash, type JSONSchema, putSchema } from "@ocas/core";
5
+ import { openStore } from "@ocas/fs";
6
+ import type { CasRef, StepNodePayload } from "@united-workforce/protocol";
7
+ import { afterEach, beforeEach, describe, expect, test } from "vitest";
8
+ import { cmdStepShow } from "../commands/step.js";
9
+ import { formatOutput } from "../format.js";
10
+ import { registerUwfSchemas } from "../schemas.js";
11
+
12
+ const TURN_SCHEMA: JSONSchema = {
13
+ title: "test-turn",
14
+ type: "object",
15
+ required: ["index", "role", "content"],
16
+ properties: {
17
+ index: { type: "integer" },
18
+ role: { type: "string", enum: ["assistant", "tool"] },
19
+ content: { type: "string" },
20
+ toolCalls: {
21
+ anyOf: [
22
+ {
23
+ type: "array",
24
+ items: {
25
+ type: "object",
26
+ required: ["name", "args"],
27
+ properties: {
28
+ name: { type: "string" },
29
+ args: { type: "string" },
30
+ },
31
+ additionalProperties: false,
32
+ },
33
+ },
34
+ { type: "null" },
35
+ ],
36
+ },
37
+ },
38
+ additionalProperties: false,
39
+ };
40
+
41
+ const DETAIL_SCHEMA: JSONSchema = {
42
+ title: "test-detail",
43
+ type: "object",
44
+ required: ["turns"],
45
+ properties: {
46
+ turns: {
47
+ type: "array",
48
+ items: { type: "string", format: "ocas_ref" },
49
+ },
50
+ },
51
+ additionalProperties: false,
52
+ };
53
+
54
+ type TestSetup = {
55
+ store: Awaited<ReturnType<typeof openStore>>;
56
+ schemas: {
57
+ workflow: Hash;
58
+ startNode: Hash;
59
+ stepNode: Hash;
60
+ text: Hash;
61
+ };
62
+ turnType: Hash;
63
+ detailType: Hash;
64
+ };
65
+
66
+ async function setupTest(casDir: string): Promise<TestSetup> {
67
+ const store = await openStore(casDir);
68
+ await bootstrap(store);
69
+ const schemas = await registerUwfSchemas(store);
70
+ const [turnType, detailType] = await Promise.all([
71
+ putSchema(store, TURN_SCHEMA),
72
+ putSchema(store, DETAIL_SCHEMA),
73
+ ]);
74
+ return { store, schemas, turnType, detailType };
75
+ }
76
+
77
+ async function createTestStep(
78
+ setup: TestSetup,
79
+ turnPayloads: Array<{
80
+ index: number;
81
+ role: string;
82
+ content: string;
83
+ toolCalls: Array<{ name: string; args: string }> | null;
84
+ }>,
85
+ ): Promise<CasRef> {
86
+ const { store, schemas, turnType, detailType } = setup;
87
+
88
+ // Create turn nodes
89
+ const turnHashes: CasRef[] = [];
90
+ for (const payload of turnPayloads) {
91
+ const turnHash = await store.cas.put(turnType, payload);
92
+ turnHashes.push(turnHash);
93
+ }
94
+
95
+ // Create detail node
96
+ const detailHash = await store.cas.put(detailType, { turns: turnHashes });
97
+
98
+ // Create dummy start node
99
+ const startHash = await store.cas.put(schemas.startNode, {
100
+ workflow: "0000000000000" as CasRef,
101
+ prompt: "test prompt",
102
+ cwd: "/tmp",
103
+ });
104
+
105
+ // Create dummy output node
106
+ const outputHash = await store.cas.put(schemas.text, { $status: "done" });
107
+
108
+ // Create step node
109
+ const stepPayload: StepNodePayload = {
110
+ prev: null,
111
+ start: startHash,
112
+ role: "test-role",
113
+ agent: "test-agent",
114
+ output: outputHash,
115
+ detail: detailHash,
116
+ edgePrompt: "",
117
+ startedAtMs: Date.now(),
118
+ completedAtMs: Date.now() + 1000,
119
+ assembledPrompt: null,
120
+ cwd: "/tmp",
121
+ usage: null,
122
+ };
123
+ return store.cas.put(schemas.stepNode, stepPayload);
124
+ }
125
+
126
+ describe("cmdStepShow JSON serialization", () => {
127
+ let testDir: string;
128
+ let casDir: string;
129
+ let originalEnv: string | undefined;
130
+
131
+ beforeEach(async () => {
132
+ testDir = await mkdtemp(join(tmpdir(), "uwf-test-"));
133
+ casDir = join(testDir, "cas");
134
+ await mkdir(casDir, { recursive: true });
135
+ originalEnv = process.env.OCAS_HOME;
136
+ process.env.OCAS_HOME = casDir;
137
+ });
138
+
139
+ afterEach(async () => {
140
+ await rm(testDir, { recursive: true, force: true });
141
+ if (originalEnv === undefined) {
142
+ delete process.env.OCAS_HOME;
143
+ } else {
144
+ process.env.OCAS_HOME = originalEnv;
145
+ }
146
+ });
147
+
148
+ test("escapes newlines in tool call args", async () => {
149
+ const setup = await setupTest(casDir);
150
+ const stepHash = await createTestStep(setup, [
151
+ {
152
+ index: 0,
153
+ role: "assistant",
154
+ content: "Running command",
155
+ toolCalls: [
156
+ {
157
+ name: "Bash",
158
+ args: "echo 'line1'\necho 'line2'",
159
+ },
160
+ ],
161
+ },
162
+ ]);
163
+
164
+ const result = await cmdStepShow(testDir, stepHash);
165
+ const jsonOutput = formatOutput(result, "json");
166
+
167
+ expect(() => JSON.parse(jsonOutput)).not.toThrow();
168
+ expect(jsonOutput).toContain("\\n");
169
+
170
+ const parsed = JSON.parse(jsonOutput);
171
+ expect(parsed.turns[0].toolCalls[0].args).toContain("\n");
172
+ });
173
+
174
+ test("escapes tabs in tool call args", async () => {
175
+ const setup = await setupTest(casDir);
176
+ const stepHash = await createTestStep(setup, [
177
+ {
178
+ index: 0,
179
+ role: "assistant",
180
+ content: "",
181
+ toolCalls: [
182
+ {
183
+ name: "Bash",
184
+ args: "cat <<EOF\nfield1\tfield2\tfield3\nEOF",
185
+ },
186
+ ],
187
+ },
188
+ ]);
189
+
190
+ const result = await cmdStepShow(testDir, stepHash);
191
+ const jsonOutput = formatOutput(result, "json");
192
+
193
+ expect(() => JSON.parse(jsonOutput)).not.toThrow();
194
+ expect(jsonOutput).toContain("\\t");
195
+ });
196
+
197
+ test("escapes carriage returns", async () => {
198
+ const setup = await setupTest(casDir);
199
+ const stepHash = await createTestStep(setup, [
200
+ {
201
+ index: 0,
202
+ role: "assistant",
203
+ content: "Committing changes",
204
+ toolCalls: [
205
+ {
206
+ name: "Bash",
207
+ args: 'git commit -m "First line\r\nSecond line"',
208
+ },
209
+ ],
210
+ },
211
+ ]);
212
+
213
+ const result = await cmdStepShow(testDir, stepHash);
214
+ const jsonOutput = formatOutput(result, "json");
215
+
216
+ expect(() => JSON.parse(jsonOutput)).not.toThrow();
217
+ expect(jsonOutput).toContain("\\r\\n");
218
+ });
219
+
220
+ test("escapes backslashes and quotes", async () => {
221
+ const setup = await setupTest(casDir);
222
+ const stepHash = await createTestStep(setup, [
223
+ {
224
+ index: 0,
225
+ role: "assistant",
226
+ content: "",
227
+ toolCalls: [
228
+ {
229
+ name: "Bash",
230
+ args: 'echo "He said \\"hello\\""',
231
+ },
232
+ ],
233
+ },
234
+ ]);
235
+
236
+ const result = await cmdStepShow(testDir, stepHash);
237
+ const jsonOutput = formatOutput(result, "json");
238
+
239
+ expect(() => JSON.parse(jsonOutput)).not.toThrow();
240
+ const parsed = JSON.parse(jsonOutput);
241
+ expect(parsed.turns).toBeDefined();
242
+ });
243
+
244
+ test("handles Unicode control characters", async () => {
245
+ const setup = await setupTest(casDir);
246
+ const stepHash = await createTestStep(setup, [
247
+ {
248
+ index: 0,
249
+ role: "assistant",
250
+ content: "",
251
+ toolCalls: [
252
+ {
253
+ name: "Bash",
254
+ args: "echo '\u0001\u001F'",
255
+ },
256
+ ],
257
+ },
258
+ ]);
259
+
260
+ const result = await cmdStepShow(testDir, stepHash);
261
+ const jsonOutput = formatOutput(result, "json");
262
+
263
+ expect(() => JSON.parse(jsonOutput)).not.toThrow();
264
+ });
265
+
266
+ test("handles nested CAS refs with control characters", async () => {
267
+ const setup = await setupTest(casDir);
268
+ const stepHash = await createTestStep(setup, [
269
+ {
270
+ index: 0,
271
+ role: "assistant",
272
+ content: "First turn\nwith newline",
273
+ toolCalls: [
274
+ {
275
+ name: "Bash",
276
+ args: "cmd1\nline2",
277
+ },
278
+ ],
279
+ },
280
+ {
281
+ index: 1,
282
+ role: "assistant",
283
+ content: "Second turn\twith tab",
284
+ toolCalls: null,
285
+ },
286
+ ]);
287
+
288
+ const result = await cmdStepShow(testDir, stepHash);
289
+ const jsonOutput = formatOutput(result, "json");
290
+
291
+ expect(() => JSON.parse(jsonOutput)).not.toThrow();
292
+ const parsed = JSON.parse(jsonOutput);
293
+ expect(parsed.turns).toHaveLength(2);
294
+ });
295
+
296
+ test("YAML output format is unaffected", async () => {
297
+ const setup = await setupTest(casDir);
298
+ const stepHash = await createTestStep(setup, [
299
+ {
300
+ index: 0,
301
+ role: "assistant",
302
+ content: "Running command",
303
+ toolCalls: [
304
+ {
305
+ name: "Bash",
306
+ args: "echo 'line1'\necho 'line2'",
307
+ },
308
+ ],
309
+ },
310
+ ]);
311
+
312
+ const result = await cmdStepShow(testDir, stepHash);
313
+ const yamlOutput = formatOutput(result, "yaml");
314
+
315
+ expect(yamlOutput).toContain("turns:");
316
+ expect(yamlOutput.length).toBeGreaterThan(0);
317
+ });
318
+
319
+ test("handles empty and null values", async () => {
320
+ const setup = await setupTest(casDir);
321
+ const stepHash = await createTestStep(setup, [
322
+ {
323
+ index: 0,
324
+ role: "assistant",
325
+ content: "",
326
+ toolCalls: null,
327
+ },
328
+ ]);
329
+
330
+ const result = await cmdStepShow(testDir, stepHash);
331
+ const jsonOutput = formatOutput(result, "json");
332
+
333
+ expect(() => JSON.parse(jsonOutput)).not.toThrow();
334
+ const parsed = JSON.parse(jsonOutput);
335
+ expect(parsed.turns).toBeDefined();
336
+ });
337
+
338
+ test("handles large step with multiple tool calls", async () => {
339
+ const setup = await setupTest(casDir);
340
+
341
+ const turns = [];
342
+ for (let i = 0; i < 25; i++) {
343
+ turns.push({
344
+ index: i,
345
+ role: "assistant" as const,
346
+ content: `Turn ${i}\nwith newline`,
347
+ toolCalls: [
348
+ {
349
+ name: "Bash",
350
+ args: `command${i}\nline2\tfield${i}`,
351
+ },
352
+ {
353
+ name: "Read",
354
+ args: `/path/to/file${i}`,
355
+ },
356
+ ],
357
+ });
358
+ }
359
+
360
+ const stepHash = await createTestStep(setup, turns);
361
+
362
+ const startTime = Date.now();
363
+ const result = await cmdStepShow(testDir, stepHash);
364
+ const jsonOutput = formatOutput(result, "json");
365
+ const duration = Date.now() - startTime;
366
+
367
+ expect(duration).toBeLessThan(2000);
368
+ expect(() => JSON.parse(jsonOutput)).not.toThrow();
369
+
370
+ const parsed = JSON.parse(jsonOutput);
371
+ expect(parsed.turns).toHaveLength(25);
372
+ });
373
+ });