@united-workforce/cli 0.5.0 → 0.6.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 (55) hide show
  1. package/dist/.build-fingerprint +1 -1
  2. package/dist/__tests__/config-text-renderer.test.d.ts +2 -0
  3. package/dist/__tests__/config-text-renderer.test.d.ts.map +1 -0
  4. package/dist/__tests__/config-text-renderer.test.js +137 -0
  5. package/dist/__tests__/config-text-renderer.test.js.map +1 -0
  6. package/dist/__tests__/issue-180-workflow-ref-removed.test.js +1 -1
  7. package/dist/__tests__/thread-agent-failure-suspended.test.d.ts +2 -0
  8. package/dist/__tests__/thread-agent-failure-suspended.test.d.ts.map +1 -0
  9. package/dist/__tests__/thread-agent-failure-suspended.test.js +332 -0
  10. package/dist/__tests__/thread-agent-failure-suspended.test.js.map +1 -0
  11. package/dist/__tests__/thread-join.test.d.ts +2 -0
  12. package/dist/__tests__/thread-join.test.d.ts.map +1 -0
  13. package/dist/__tests__/thread-join.test.js +77 -0
  14. package/dist/__tests__/thread-join.test.js.map +1 -0
  15. package/dist/__tests__/thread-poke.test.js +4 -1
  16. package/dist/__tests__/thread-poke.test.js.map +1 -1
  17. package/dist/__tests__/workflow-paths.test.d.ts +2 -0
  18. package/dist/__tests__/workflow-paths.test.d.ts.map +1 -0
  19. package/dist/__tests__/workflow-paths.test.js +261 -0
  20. package/dist/__tests__/workflow-paths.test.js.map +1 -0
  21. package/dist/cli.js +18 -1
  22. package/dist/cli.js.map +1 -1
  23. package/dist/commands/config.d.ts +5 -0
  24. package/dist/commands/config.d.ts.map +1 -1
  25. package/dist/commands/config.js +69 -3
  26. package/dist/commands/config.js.map +1 -1
  27. package/dist/commands/thread.d.ts +12 -0
  28. package/dist/commands/thread.d.ts.map +1 -1
  29. package/dist/commands/thread.js +183 -8
  30. package/dist/commands/thread.js.map +1 -1
  31. package/dist/commands/workflow.d.ts +1 -1
  32. package/dist/commands/workflow.d.ts.map +1 -1
  33. package/dist/commands/workflow.js +24 -4
  34. package/dist/commands/workflow.js.map +1 -1
  35. package/dist/output-mappers.d.ts.map +1 -1
  36. package/dist/output-mappers.js +1 -1
  37. package/dist/output-mappers.js.map +1 -1
  38. package/dist/store.d.ts +11 -0
  39. package/dist/store.d.ts.map +1 -1
  40. package/dist/store.js +20 -1
  41. package/dist/store.js.map +1 -1
  42. package/package.json +11 -11
  43. package/src/__tests__/config-text-renderer.test.ts +156 -0
  44. package/src/__tests__/issue-180-workflow-ref-removed.test.ts +1 -1
  45. package/src/__tests__/thread-agent-failure-suspended.test.ts +406 -0
  46. package/src/__tests__/thread-join.test.ts +103 -0
  47. package/src/__tests__/thread-poke.test.ts +4 -1
  48. package/src/__tests__/workflow-paths.test.ts +337 -0
  49. package/src/cli.ts +19 -0
  50. package/src/commands/config.ts +74 -3
  51. package/src/commands/thread.ts +233 -8
  52. package/src/commands/workflow.ts +29 -4
  53. package/src/output-mappers.ts +2 -1
  54. package/src/store.ts +25 -1
  55. package/LICENSE +0 -21
@@ -1 +1 @@
1
- a29148988eea25f078d4887e5c8ff66c177c93c68c4c3fb49f85148998df090a
1
+ c83fdefd2d40c71f9bd9c9ec4634e0765625ac0fb68d1013e8fffc7565d7b0d0
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=config-text-renderer.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-text-renderer.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/config-text-renderer.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,137 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { formatOutput } from "../format.js";
3
+ describe("config text renderers", () => {
4
+ describe("config list", () => {
5
+ test("renders flat key-value pairs in text format", () => {
6
+ const data = {
7
+ defaultAgent: "claude-code",
8
+ agents: {
9
+ hermes: {
10
+ command: "uwf-hermes",
11
+ args: [],
12
+ },
13
+ "claude-code": {
14
+ command: "uwf-claude-code",
15
+ args: [],
16
+ },
17
+ },
18
+ concurrency: {
19
+ maxRunning: 4,
20
+ },
21
+ };
22
+ const result = formatOutput(data, "text", "config list");
23
+ expect(result).toContain("defaultAgent");
24
+ expect(result).toContain("claude-code");
25
+ expect(result).toContain("agents.hermes.command");
26
+ expect(result).toContain("uwf-hermes");
27
+ expect(result).toContain("agents.hermes.args");
28
+ expect(result).toContain("[]");
29
+ expect(result).toContain("agents.claude-code.command");
30
+ expect(result).toContain("uwf-claude-code");
31
+ expect(result).toContain("concurrency.maxRunning");
32
+ expect(result).toContain("4");
33
+ });
34
+ test("uses dot-notation for nested keys", () => {
35
+ const data = {
36
+ agents: {
37
+ hermes: {
38
+ command: "uwf-hermes",
39
+ },
40
+ },
41
+ };
42
+ const result = formatOutput(data, "text", "config list");
43
+ expect(result).toContain("agents.hermes.command");
44
+ });
45
+ test("displays array values as JSON", () => {
46
+ const data = {
47
+ agents: {
48
+ hermes: {
49
+ args: ["--flag", "--verbose"],
50
+ },
51
+ },
52
+ };
53
+ const result = formatOutput(data, "text", "config list");
54
+ expect(result).toContain('["--flag","--verbose"]');
55
+ });
56
+ test("does not throw on empty config", () => {
57
+ const result = formatOutput({}, "text", "config list");
58
+ expect(result).toBe("");
59
+ });
60
+ test("does not throw on null/undefined data", () => {
61
+ expect(() => formatOutput(null, "text", "config list")).not.toThrow();
62
+ expect(() => formatOutput(undefined, "text", "config list")).not.toThrow();
63
+ });
64
+ });
65
+ describe("config get", () => {
66
+ test("renders scalar value as bare string", () => {
67
+ const data = { value: "claude-code" };
68
+ const result = formatOutput(data, "text", "config get");
69
+ expect(result).toBe("claude-code");
70
+ });
71
+ test("renders number value as string", () => {
72
+ const data = { value: 4 };
73
+ const result = formatOutput(data, "text", "config get");
74
+ expect(result).toBe("4");
75
+ });
76
+ test("renders object value as flattened key-value pairs", () => {
77
+ const data = {
78
+ value: {
79
+ command: "uwf-hermes",
80
+ args: [],
81
+ },
82
+ };
83
+ const result = formatOutput(data, "text", "config get");
84
+ expect(result).toContain("command");
85
+ expect(result).toContain("uwf-hermes");
86
+ expect(result).toContain("args");
87
+ expect(result).toContain("[]");
88
+ });
89
+ test("does not throw on null value", () => {
90
+ expect(() => formatOutput({ value: null }, "text", "config get")).not.toThrow();
91
+ });
92
+ test("does not throw on missing value field", () => {
93
+ expect(() => formatOutput({}, "text", "config get")).not.toThrow();
94
+ });
95
+ });
96
+ describe("config set", () => {
97
+ test("renders key = value confirmation for scalar", () => {
98
+ const data = { key: "defaultAgent", value: "hermes" };
99
+ const result = formatOutput(data, "text", "config set");
100
+ expect(result).toBe("defaultAgent = hermes");
101
+ });
102
+ test("renders key = value for array values as JSON", () => {
103
+ const data = { key: "agents.hermes.args", value: ["--verbose"] };
104
+ const result = formatOutput(data, "text", "config set");
105
+ expect(result).toBe('agents.hermes.args = ["--verbose"]');
106
+ });
107
+ test("does not throw on missing key/value", () => {
108
+ expect(() => formatOutput({}, "text", "config set")).not.toThrow();
109
+ expect(() => formatOutput(null, "text", "config set")).not.toThrow();
110
+ });
111
+ });
112
+ describe("text format fallback", () => {
113
+ test("falls back to JSON pretty-print when no renderer registered", () => {
114
+ const data = { hello: "world" };
115
+ const result = formatOutput(data, "text", "unknown command");
116
+ expect(result).toBe(JSON.stringify(data, null, 2));
117
+ });
118
+ test("falls back to JSON pretty-print when commandPath is null", () => {
119
+ const data = { hello: "world" };
120
+ const result = formatOutput(data, "text", undefined);
121
+ expect(result).toBe(JSON.stringify(data, null, 2));
122
+ });
123
+ });
124
+ describe("json and yaml formats unaffected", () => {
125
+ test("json format still works with commandPath", () => {
126
+ const data = { key: "value" };
127
+ const result = formatOutput(data, "json", "config list");
128
+ expect(result).toBe(JSON.stringify(data));
129
+ });
130
+ test("yaml format still works with commandPath", () => {
131
+ const data = { key: "value" };
132
+ const result = formatOutput(data, "yaml", "config list");
133
+ expect(result).toContain("key: value");
134
+ });
135
+ });
136
+ });
137
+ //# sourceMappingURL=config-text-renderer.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-text-renderer.test.js","sourceRoot":"","sources":["../../src/__tests__/config-text-renderer.test.ts"],"names":[],"mappings":"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"}
@@ -27,7 +27,7 @@ describe("issue #180 — _workflowRef ghost parameter cleanup", () => {
27
27
  for (const match of source.matchAll(callRe)) {
28
28
  callSites.push(match[1]);
29
29
  }
30
- expect(callSites.length).toBe(3);
30
+ expect(callSites.length).toBe(4);
31
31
  for (const args of callSites) {
32
32
  const argCount = args
33
33
  .split(",")
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=thread-agent-failure-suspended.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thread-agent-failure-suspended.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/thread-agent-failure-suspended.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,332 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { dirname, join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { putSchema } from "@ocas/core";
7
+ import { openStore } from "@ocas/fs";
8
+ import { afterEach, beforeEach, describe, expect, test } from "vitest";
9
+ import { registerUwfSchemas } from "../schemas.js";
10
+ import { seedThreads } from "./thread-test-helpers.js";
11
+ const OUTPUT_SCHEMA = {
12
+ type: "object",
13
+ properties: {
14
+ $status: { type: "string" },
15
+ note: { type: "string" },
16
+ },
17
+ required: ["$status"],
18
+ additionalProperties: false,
19
+ };
20
+ const THREAD_ID = "01AGENTFAILSUSPEND00000";
21
+ let tmpDir;
22
+ let savedOcasHome;
23
+ beforeEach(async () => {
24
+ savedOcasHome = process.env.OCAS_HOME;
25
+ tmpDir = await mkdtemp(join(tmpdir(), "cli-uwf-agent-fail-suspend-"));
26
+ });
27
+ afterEach(async () => {
28
+ if (savedOcasHome === undefined) {
29
+ delete process.env.OCAS_HOME;
30
+ }
31
+ else {
32
+ process.env.OCAS_HOME = savedOcasHome;
33
+ }
34
+ await rm(tmpDir, { recursive: true, force: true });
35
+ });
36
+ async function setupThread() {
37
+ const casDir = join(tmpDir, "cas");
38
+ await mkdir(casDir, { recursive: true });
39
+ const store = await openStore(casDir);
40
+ const schemas = await registerUwfSchemas(store);
41
+ const outputSchemaHash = await putSchema(store, OUTPUT_SCHEMA);
42
+ const workflowHash = await store.cas.put(schemas.workflow, {
43
+ name: "test-agent-fail",
44
+ description: "agent failure suspend test",
45
+ roles: {
46
+ worker: {
47
+ description: "Worker role",
48
+ goal: "Work",
49
+ capabilities: [],
50
+ procedure: "work",
51
+ output: "result",
52
+ frontmatter: outputSchemaHash,
53
+ },
54
+ reviewer: {
55
+ description: "Reviewer role",
56
+ goal: "Review",
57
+ capabilities: [],
58
+ procedure: "review",
59
+ output: "result",
60
+ frontmatter: outputSchemaHash,
61
+ },
62
+ },
63
+ graph: {
64
+ $START: {
65
+ new: { role: "worker", prompt: "Start work", location: null },
66
+ resume: { role: "worker", prompt: "Resume work", location: null },
67
+ },
68
+ worker: {
69
+ ok: { role: "reviewer", prompt: "Review the work", location: null },
70
+ },
71
+ reviewer: { done: { role: "$END", prompt: "Done", location: null } },
72
+ },
73
+ });
74
+ const startHash = await store.cas.put(schemas.startNode, {
75
+ workflow: workflowHash,
76
+ prompt: "Test agent failure task",
77
+ cwd: tmpDir,
78
+ });
79
+ process.env.OCAS_HOME = casDir;
80
+ await seedThreads(tmpDir, { [THREAD_ID]: startHash });
81
+ // Build a successful step output to be used by agents
82
+ const newOutputHash = await store.cas.put(outputSchemaHash, {
83
+ $status: "ok",
84
+ note: "success output",
85
+ });
86
+ const newDetailHash = await store.cas.put(schemas.text, "success detail");
87
+ const successStepHash = await store.cas.put(schemas.stepNode, {
88
+ start: startHash,
89
+ prev: null,
90
+ role: "worker",
91
+ output: newOutputHash,
92
+ detail: newDetailHash,
93
+ agent: "mock-agent",
94
+ edgePrompt: "Start work",
95
+ startedAtMs: 1716600000000,
96
+ completedAtMs: 1716600001000,
97
+ cwd: tmpDir,
98
+ assembledPrompt: null,
99
+ usage: null,
100
+ });
101
+ // Build a failed step output (isError: true) — the agent created the CAS node but reports failure
102
+ const failedOutputHash = await store.cas.put(outputSchemaHash, {
103
+ $status: "error",
104
+ note: "validation failed",
105
+ });
106
+ const failedDetailHash = await store.cas.put(schemas.text, "failed detail");
107
+ const failedStepHash = await store.cas.put(schemas.stepNode, {
108
+ start: startHash,
109
+ prev: null,
110
+ role: "worker",
111
+ output: failedOutputHash,
112
+ detail: failedDetailHash,
113
+ agent: "mock-agent",
114
+ edgePrompt: "Start work",
115
+ startedAtMs: 1716600000000,
116
+ completedAtMs: 1716600001000,
117
+ cwd: tmpDir,
118
+ assembledPrompt: null,
119
+ usage: null,
120
+ });
121
+ const successAdapterJson = JSON.stringify({
122
+ stepHash: successStepHash,
123
+ detailHash: newDetailHash,
124
+ role: "worker",
125
+ frontmatter: { $status: "ok", note: "success output" },
126
+ body: "",
127
+ startedAtMs: 1716600000000,
128
+ completedAtMs: 1716600001000,
129
+ usage: null,
130
+ });
131
+ const failedAdapterJson = JSON.stringify({
132
+ stepHash: failedStepHash,
133
+ detailHash: failedDetailHash,
134
+ role: "worker",
135
+ frontmatter: { $status: "error", note: "validation failed" },
136
+ body: "",
137
+ startedAtMs: 1716600000000,
138
+ completedAtMs: 1716600001000,
139
+ usage: null,
140
+ isError: true,
141
+ errorMessage: "frontmatter validation exhausted retries",
142
+ });
143
+ // Mock agent that succeeds
144
+ const mockAgentPath = join(tmpDir, "mock-agent.sh");
145
+ await writeFile(mockAgentPath, `#!/bin/sh\necho '${successAdapterJson}'\n`, { mode: 0o755 });
146
+ // Agent that crashes with non-zero exit code (fatal failure)
147
+ const failingAgentPath = join(tmpDir, "failing-agent.sh");
148
+ await writeFile(failingAgentPath, `#!/bin/sh\necho "boom" >&2\nexit 7\n`, { mode: 0o755 });
149
+ // Agent that returns isError: true (recoverable failure)
150
+ const recoverableFailAgentPath = join(tmpDir, "recoverable-fail-agent.sh");
151
+ await writeFile(recoverableFailAgentPath, `#!/bin/sh\necho '${failedAdapterJson}'\n`, {
152
+ mode: 0o755,
153
+ });
154
+ const configPath = join(tmpDir, "config.yaml");
155
+ await writeFile(configPath, `defaultAgent: uwf-hermes\nagentOverrides: null\nagents:\n uwf-hermes:\n command: uwf-hermes\n`);
156
+ return {
157
+ casDir,
158
+ startHash,
159
+ workflowHash,
160
+ mockAgentPath,
161
+ failingAgentPath,
162
+ recoverableFailAgentPath,
163
+ };
164
+ }
165
+ function runUwf(args, casDir) {
166
+ const cliPath = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "dist", "cli.js");
167
+ const formatArgs = args.includes("--format") ? args : ["--format", "raw-json", ...args];
168
+ try {
169
+ const stdout = execFileSync(process.execPath, [cliPath, ...formatArgs], {
170
+ encoding: "utf8",
171
+ stdio: ["ignore", "pipe", "pipe"],
172
+ env: {
173
+ ...process.env,
174
+ UWF_HOME: tmpDir,
175
+ OCAS_HOME: casDir,
176
+ },
177
+ cwd: tmpDir,
178
+ timeout: 30000,
179
+ });
180
+ return { stdout, stderr: "", status: 0 };
181
+ }
182
+ catch (error) {
183
+ const err = error;
184
+ return {
185
+ stdout: typeof err.stdout === "string" ? err.stdout : (err.stdout?.toString("utf8") ?? ""),
186
+ stderr: typeof err.stderr === "string" ? err.stderr : (err.stderr?.toString("utf8") ?? ""),
187
+ status: err.status ?? 1,
188
+ };
189
+ }
190
+ }
191
+ // ── Spec 1: Recoverable agent failure (isError: true) → suspended ─────────
192
+ describe("recoverable agent failure suspends thread", () => {
193
+ test("CLI output has status=suspended when agent returns isError=true", async () => {
194
+ const { casDir, recoverableFailAgentPath } = await setupThread();
195
+ const result = runUwf(["thread", "exec", THREAD_ID, "--agent", recoverableFailAgentPath], casDir);
196
+ // exec envelope: { threadId, workflowHash, steps: [...] }
197
+ const envelope = JSON.parse(result.stdout.trim());
198
+ const stepOutput = envelope.steps[0];
199
+ expect(stepOutput.status).toBe("suspended");
200
+ });
201
+ test("CLI output has suspendedRole set to the failing role", async () => {
202
+ const { casDir, recoverableFailAgentPath } = await setupThread();
203
+ const result = runUwf(["thread", "exec", THREAD_ID, "--agent", recoverableFailAgentPath], casDir);
204
+ const envelope = JSON.parse(result.stdout.trim());
205
+ const stepOutput = envelope.steps[0];
206
+ expect(stepOutput.suspendedRole).toBe("worker");
207
+ });
208
+ test("CLI output has suspendMessage set to the error message", async () => {
209
+ const { casDir, recoverableFailAgentPath } = await setupThread();
210
+ const result = runUwf(["thread", "exec", THREAD_ID, "--agent", recoverableFailAgentPath], casDir);
211
+ const envelope = JSON.parse(result.stdout.trim());
212
+ const stepOutput = envelope.steps[0];
213
+ expect(stepOutput.suspendMessage).toBe("frontmatter validation exhausted retries");
214
+ });
215
+ test("thread head is NOT advanced on recoverable failure", async () => {
216
+ const { casDir, startHash, recoverableFailAgentPath } = await setupThread();
217
+ runUwf(["thread", "exec", THREAD_ID, "--agent", recoverableFailAgentPath], casDir);
218
+ const { createUwfStore, getThread } = await import("../store.js");
219
+ const uwf = await createUwfStore(tmpDir);
220
+ const entry = getThread(uwf.varStore, THREAD_ID);
221
+ // Head should still be the start hash (not advanced)
222
+ expect(entry?.head).toBe(startHash);
223
+ });
224
+ test("thread index entry is persisted as suspended via markThreadSuspended", async () => {
225
+ const { casDir, recoverableFailAgentPath } = await setupThread();
226
+ runUwf(["thread", "exec", THREAD_ID, "--agent", recoverableFailAgentPath], casDir);
227
+ const { createUwfStore, getThread } = await import("../store.js");
228
+ const uwf = await createUwfStore(tmpDir);
229
+ const entry = getThread(uwf.varStore, THREAD_ID);
230
+ expect(entry?.status).toBe("suspended");
231
+ expect(entry?.suspendedRole).toBe("worker");
232
+ expect(entry?.suspendMessage).toBe("frontmatter validation exhausted retries");
233
+ });
234
+ test("uwf thread list --status suspended includes the thread", async () => {
235
+ const { casDir, recoverableFailAgentPath } = await setupThread();
236
+ runUwf(["thread", "exec", THREAD_ID, "--agent", recoverableFailAgentPath], casDir);
237
+ const listResult = runUwf(["thread", "list", "--status", "suspended"], casDir);
238
+ expect(listResult.stdout).toContain(THREAD_ID);
239
+ });
240
+ test("error field is included in StepOutput for backward compatibility", async () => {
241
+ const { casDir, recoverableFailAgentPath } = await setupThread();
242
+ const result = runUwf(["thread", "exec", THREAD_ID, "--agent", recoverableFailAgentPath], casDir);
243
+ const envelope = JSON.parse(result.stdout.trim());
244
+ const stepOutput = envelope.steps[0];
245
+ // The exec envelope includes status=suspended with suspend fields;
246
+ // the internal StepOutput also carries error { stepHash, message } but
247
+ // toThreadExecPayload only maps status/suspendedRole/suspendMessage.
248
+ // Verify the mapped fields are correct.
249
+ expect(stepOutput.status).toBe("suspended");
250
+ expect(stepOutput.suspendedRole).toBe("worker");
251
+ expect(stepOutput.suspendMessage).toBe("frontmatter validation exhausted retries");
252
+ });
253
+ });
254
+ // ── Spec 2: Fatal agent failure (command crash) → suspended ───────────────
255
+ describe("fatal agent failure suspends thread", () => {
256
+ test("thread status is suspended after agent crash", async () => {
257
+ const { casDir, failingAgentPath } = await setupThread();
258
+ runUwf(["thread", "exec", THREAD_ID, "--agent", failingAgentPath], casDir);
259
+ const { createUwfStore, getThread } = await import("../store.js");
260
+ const uwf = await createUwfStore(tmpDir);
261
+ const entry = getThread(uwf.varStore, THREAD_ID);
262
+ expect(entry?.status).toBe("suspended");
263
+ });
264
+ test("thread index has suspendedRole and suspendMessage after fatal failure", async () => {
265
+ const { casDir, failingAgentPath } = await setupThread();
266
+ runUwf(["thread", "exec", THREAD_ID, "--agent", failingAgentPath], casDir);
267
+ const { createUwfStore, getThread } = await import("../store.js");
268
+ const uwf = await createUwfStore(tmpDir);
269
+ const entry = getThread(uwf.varStore, THREAD_ID);
270
+ expect(entry?.suspendedRole).toBe("worker");
271
+ expect(entry?.suspendMessage).toContain("agent command failed");
272
+ });
273
+ test("thread head is NOT advanced after fatal failure", async () => {
274
+ const { casDir, startHash, failingAgentPath } = await setupThread();
275
+ runUwf(["thread", "exec", THREAD_ID, "--agent", failingAgentPath], casDir);
276
+ const { createUwfStore, getThread } = await import("../store.js");
277
+ const uwf = await createUwfStore(tmpDir);
278
+ const entry = getThread(uwf.varStore, THREAD_ID);
279
+ expect(entry?.head).toBe(startHash);
280
+ });
281
+ test("uwf thread list --status suspended includes thread after crash", async () => {
282
+ const { casDir, failingAgentPath } = await setupThread();
283
+ runUwf(["thread", "exec", THREAD_ID, "--agent", failingAgentPath], casDir);
284
+ const listResult = runUwf(["thread", "list", "--status", "suspended"], casDir);
285
+ expect(listResult.stdout).toContain(THREAD_ID);
286
+ });
287
+ test("CLI process exits with non-zero exit code after fatal failure", async () => {
288
+ const { casDir, failingAgentPath } = await setupThread();
289
+ const result = runUwf(["thread", "exec", THREAD_ID, "--agent", failingAgentPath], casDir);
290
+ expect(result.status).not.toBe(0);
291
+ });
292
+ });
293
+ // ── Spec 3: Suspended thread from agent failure can be resumed ────────────
294
+ describe("agent-failure-suspended thread can be resumed", () => {
295
+ test("thread resume is accepted for agent-failure suspended thread", async () => {
296
+ const { casDir, recoverableFailAgentPath, mockAgentPath } = await setupThread();
297
+ // First: cause a recoverable failure → thread becomes suspended
298
+ runUwf(["thread", "exec", THREAD_ID, "--agent", recoverableFailAgentPath], casDir);
299
+ // Verify it's suspended
300
+ const { createUwfStore, getThread } = await import("../store.js");
301
+ const uwf = await createUwfStore(tmpDir);
302
+ const entry = getThread(uwf.varStore, THREAD_ID);
303
+ expect(entry?.status).toBe("suspended");
304
+ // Resume with a different (successful) agent
305
+ const resumeResult = runUwf([
306
+ "thread",
307
+ "resume",
308
+ THREAD_ID,
309
+ "-p",
310
+ "try again with correct params",
311
+ "--agent",
312
+ mockAgentPath,
313
+ ], casDir);
314
+ expect(resumeResult.status).toBe(0);
315
+ const resumeOutput = JSON.parse(resumeResult.stdout.trim());
316
+ // After successful resume, thread should not be suspended
317
+ expect(resumeOutput.status).not.toBe("suspended");
318
+ });
319
+ test("re-failure after resume returns to suspended (not idle)", async () => {
320
+ const { casDir, recoverableFailAgentPath } = await setupThread();
321
+ // First: cause a recoverable failure → suspended
322
+ runUwf(["thread", "exec", THREAD_ID, "--agent", recoverableFailAgentPath], casDir);
323
+ // Resume with same failing agent → should suspend again
324
+ const resumeResult = runUwf(["thread", "resume", THREAD_ID, "-p", "try again", "--agent", recoverableFailAgentPath], casDir);
325
+ // Resume with recoverable failure agent — the resume itself runs cmdThreadStepOnce
326
+ // which should report suspended status
327
+ const resumeOutput = JSON.parse(resumeResult.stdout.trim());
328
+ expect(resumeOutput.status).toBe("suspended");
329
+ expect(resumeOutput.suspendedRole).toBe("worker");
330
+ });
331
+ });
332
+ //# sourceMappingURL=thread-agent-failure-suspended.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thread-agent-failure-suspended.test.js","sourceRoot":"","sources":["../../src/__tests__/thread-agent-failure-suspended.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=thread-join.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thread-join.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/thread-join.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,77 @@
1
+ import { mkdir, rm } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { createThreadIndexEntry } from "@united-workforce/protocol";
5
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
6
+ import { createMarker, deleteMarker, getProcessStartTime, isThreadRunning, } from "../background/index.js";
7
+ import { cmdThreadJoin } from "../commands/thread.js";
8
+ import { makeUwfStore, seedThread } from "./thread-test-helpers.js";
9
+ describe("cmdThreadJoin", () => {
10
+ let storageRoot;
11
+ let savedOcasHome;
12
+ beforeEach(async () => {
13
+ savedOcasHome = process.env.OCAS_HOME;
14
+ storageRoot = join(tmpdir(), `uwf-test-join-${Date.now()}-${Math.random().toString(36).slice(2)}`);
15
+ await mkdir(storageRoot, { recursive: true });
16
+ });
17
+ afterEach(async () => {
18
+ if (savedOcasHome !== undefined) {
19
+ process.env.OCAS_HOME = savedOcasHome;
20
+ }
21
+ else {
22
+ delete process.env.OCAS_HOME;
23
+ }
24
+ await rm(storageRoot, { recursive: true, force: true });
25
+ });
26
+ it("throws when thread does not exist", async () => {
27
+ await makeUwfStore(storageRoot);
28
+ const threadId = "01JF0000000000NOTEXIST0";
29
+ await expect(cmdThreadJoin(storageRoot, threadId, null)).rejects.toThrow(/thread not found|process\.exit/);
30
+ });
31
+ it("times out when thread keeps running", async () => {
32
+ const threadId = "01JF0000000000TESTJOIN03";
33
+ await makeUwfStore(storageRoot);
34
+ // Seed a thread so existence check passes
35
+ const uwf = await makeUwfStore(storageRoot);
36
+ const head = (await uwf.store.cas.put(uwf.schemas.text, "join-timeout-test"));
37
+ await seedThread(storageRoot, threadId, createThreadIndexEntry(head));
38
+ // Create a running marker with our PID (it will stay alive)
39
+ await createMarker(storageRoot, {
40
+ thread: threadId,
41
+ workflow: "AAAAAAAAAAAAA",
42
+ pid: process.pid,
43
+ startedAt: Date.now(),
44
+ processStartTime: getProcessStartTime(process.pid),
45
+ });
46
+ // Timeout after 100ms — should fail because marker never disappears
47
+ await expect(cmdThreadJoin(storageRoot, threadId, 100)).rejects.toThrow(/join timed out|process\.exit/);
48
+ // Cleanup
49
+ await deleteMarker(storageRoot, threadId);
50
+ });
51
+ it("poll loop exits when marker is removed", async () => {
52
+ const threadId = "01JF0000000000TESTJOIN04";
53
+ const uwf = await makeUwfStore(storageRoot);
54
+ const head = (await uwf.store.cas.put(uwf.schemas.text, "join-poll-test"));
55
+ await seedThread(storageRoot, threadId, createThreadIndexEntry(head));
56
+ // Create a running marker
57
+ await createMarker(storageRoot, {
58
+ thread: threadId,
59
+ workflow: "AAAAAAAAAAAAA",
60
+ pid: process.pid,
61
+ startedAt: Date.now(),
62
+ processStartTime: getProcessStartTime(process.pid),
63
+ });
64
+ // Confirm marker is valid
65
+ expect(await isThreadRunning(storageRoot, threadId)).not.toBeNull();
66
+ // Remove it after a short delay — simulates background worker finishing
67
+ setTimeout(() => {
68
+ deleteMarker(storageRoot, threadId);
69
+ }, 300);
70
+ // cmdThreadJoin will poll and wait. It will exit the poll loop after marker
71
+ // disappears, then try to resolve workflow from head. Our simple text node
72
+ // won't resolve, so it will fail — but the key test is that the poll loop
73
+ // DID exit (it didn't time out). We use a generous timeout to prove this.
74
+ await expect(cmdThreadJoin(storageRoot, threadId, 5000)).rejects.toThrow(/failed to resolve workflow|process\.exit/);
75
+ });
76
+ });
77
+ //# sourceMappingURL=thread-join.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thread-join.test.js","sourceRoot":"","sources":["../../src/__tests__/thread-join.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,sBAAsB,EAAE,MAAM,4BAA4B,CAAC;AACpE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,mBAAmB,EACnB,eAAe,GAChB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEpE,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,IAAI,WAAmB,CAAC;IACxB,IAAI,aAAiC,CAAC;IAEtC,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;QACtC,WAAW,GAAG,IAAI,CAChB,MAAM,EAAE,EACR,iBAAiB,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CACrE,CAAC;QACF,MAAM,KAAK,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;YAChC,OAAO,CAAC,GAAG,CAAC,SAAS,GAAG,aAAa,CAAC;QACxC,CAAC;aAAM,CAAC;YACN,OAAO,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;QAC/B,CAAC;QACD,MAAM,EAAE,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,YAAY,CAAC,WAAW,CAAC,CAAC;QAChC,MAAM,QAAQ,GAAG,yBAAqC,CAAC;QACvD,MAAM,MAAM,CAAC,aAAa,CAAC,WAAW,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CACtE,gCAAgC,CACjC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;QACnD,MAAM,QAAQ,GAAG,0BAAsC,CAAC;QACxD,MAAM,YAAY,CAAC,WAAW,CAAC,CAAC;QAChC,0CAA0C;QAC1C,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,WAAW,CAAC,CAAC;QAC5C,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAW,CAAC;QACxF,MAAM,UAAU,CAAC,WAAW,EAAE,QAAQ,EAAE,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAC;QAEtE,4DAA4D;QAC5D,MAAM,YAAY,CAAC,WAAW,EAAE;YAC9B,MAAM,EAAE,QAAQ;YAChB,QAAQ,EAAE,eAAyB;YACnC,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,gBAAgB,EAAE,mBAAmB,CAAC,OAAO,CAAC,GAAG,CAAC;SACnD,CAAC,CAAC;QAEH,oEAAoE;QACpE,MAAM,MAAM,CAAC,aAAa,CAAC,WAAW,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CACrE,8BAA8B,CAC/B,CAAC;QAEF,UAAU;QACV,MAAM,YAAY,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACtD,MAAM,QAAQ,GAAG,0BAAsC,CAAC;QACxD,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,WAAW,CAAC,CAAC;QAC5C,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAW,CAAC;QACrF,MAAM,UAAU,CAAC,WAAW,EAAE,QAAQ,EAAE,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAC;QAEtE,0BAA0B;QAC1B,MAAM,YAAY,CAAC,WAAW,EAAE;YAC9B,MAAM,EAAE,QAAQ;YAChB,QAAQ,EAAE,eAAyB;YACnC,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,gBAAgB,EAAE,mBAAmB,CAAC,OAAO,CAAC,GAAG,CAAC;SACnD,CAAC,CAAC;QAEH,0BAA0B;QAC1B,MAAM,CAAC,MAAM,eAAe,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QAEpE,wEAAwE;QACxE,UAAU,CAAC,GAAG,EAAE;YACd,YAAY,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;QACtC,CAAC,EAAE,GAAG,CAAC,CAAC;QAER,4EAA4E;QAC5E,2EAA2E;QAC3E,0EAA0E;QAC1E,0EAA0E;QAC1E,MAAM,MAAM,CAAC,aAAa,CAAC,WAAW,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CACtE,0CAA0C,CAC3C,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}