@united-workforce/cli 0.2.1-rc.9 → 0.4.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.
- package/README.md +15 -8
- package/dist/__tests__/adapter-json-roundtrip.test.js +1 -1
- package/dist/__tests__/adapter-json-roundtrip.test.js.map +1 -1
- package/dist/__tests__/agent-resolution-llm-free.test.d.ts +2 -0
- package/dist/__tests__/agent-resolution-llm-free.test.d.ts.map +1 -0
- package/dist/__tests__/agent-resolution-llm-free.test.js +30 -0
- package/dist/__tests__/agent-resolution-llm-free.test.js.map +1 -0
- package/dist/__tests__/build-step-entry.test.d.ts +2 -0
- package/dist/__tests__/build-step-entry.test.d.ts.map +1 -0
- package/dist/__tests__/build-step-entry.test.js +173 -0
- package/dist/__tests__/build-step-entry.test.js.map +1 -0
- package/dist/__tests__/clear-thread-failed-attempts.test.d.ts +2 -0
- package/dist/__tests__/clear-thread-failed-attempts.test.d.ts.map +1 -0
- package/dist/__tests__/clear-thread-failed-attempts.test.js +93 -0
- package/dist/__tests__/clear-thread-failed-attempts.test.js.map +1 -0
- package/dist/__tests__/config.test.js +26 -302
- package/dist/__tests__/config.test.js.map +1 -1
- package/dist/__tests__/current-role.test.js +7 -6
- package/dist/__tests__/current-role.test.js.map +1 -1
- package/dist/__tests__/e2e-mock-agent.test.js +20 -23
- package/dist/__tests__/e2e-mock-agent.test.js.map +1 -1
- package/dist/__tests__/issue-180-workflow-ref-removed.test.d.ts +2 -0
- package/dist/__tests__/issue-180-workflow-ref-removed.test.d.ts.map +1 -0
- package/dist/__tests__/issue-180-workflow-ref-removed.test.js +40 -0
- package/dist/__tests__/issue-180-workflow-ref-removed.test.js.map +1 -0
- package/dist/__tests__/moderator-evaluate.test.js +9 -50
- package/dist/__tests__/moderator-evaluate.test.js.map +1 -1
- package/dist/__tests__/pid-recycling.test.d.ts +2 -0
- package/dist/__tests__/pid-recycling.test.d.ts.map +1 -0
- package/dist/__tests__/pid-recycling.test.js +271 -0
- package/dist/__tests__/pid-recycling.test.js.map +1 -0
- package/dist/__tests__/prompt.test.js +321 -0
- package/dist/__tests__/prompt.test.js.map +1 -1
- package/dist/__tests__/resolve-head-hash.test.js +4 -4
- package/dist/__tests__/resolve-head-hash.test.js.map +1 -1
- package/dist/__tests__/setup-agent-discovery.test.js +21 -30
- package/dist/__tests__/setup-agent-discovery.test.js.map +1 -1
- package/dist/__tests__/setup-complexity.test.js +2 -168
- package/dist/__tests__/setup-complexity.test.js.map +1 -1
- package/dist/__tests__/setup-no-llm.test.d.ts +2 -0
- package/dist/__tests__/setup-no-llm.test.d.ts.map +1 -0
- package/dist/__tests__/setup-no-llm.test.js +52 -0
- package/dist/__tests__/setup-no-llm.test.js.map +1 -0
- package/dist/__tests__/solve-issue-tea-worktree.test.js +24 -27
- package/dist/__tests__/solve-issue-tea-worktree.test.js.map +1 -1
- package/dist/__tests__/step-ask.test.d.ts +2 -0
- package/dist/__tests__/step-ask.test.d.ts.map +1 -0
- package/dist/__tests__/step-ask.test.js +499 -0
- package/dist/__tests__/step-ask.test.js.map +1 -0
- package/dist/__tests__/step-show-json.test.js +1 -0
- package/dist/__tests__/step-show-json.test.js.map +1 -1
- package/dist/__tests__/step-timing.test.js +2 -0
- package/dist/__tests__/step-timing.test.js.map +1 -1
- package/dist/__tests__/store-global-cas.test.js +2 -2
- package/dist/__tests__/store-global-cas.test.js.map +1 -1
- package/dist/__tests__/store-unified-threads.test.js +9 -9
- package/dist/__tests__/store-unified-threads.test.js.map +1 -1
- package/dist/__tests__/thread-cancel-status.test.js +6 -6
- package/dist/__tests__/thread-cancel-status.test.js.map +1 -1
- package/dist/__tests__/thread-list-filters.test.js +344 -9
- package/dist/__tests__/thread-list-filters.test.js.map +1 -1
- package/dist/__tests__/thread-poke.test.d.ts +2 -0
- package/dist/__tests__/thread-poke.test.d.ts.map +1 -0
- package/dist/__tests__/thread-poke.test.js +412 -0
- package/dist/__tests__/thread-poke.test.js.map +1 -0
- package/dist/__tests__/thread-resume.test.js +10 -14
- package/dist/__tests__/thread-resume.test.js.map +1 -1
- package/dist/__tests__/thread-show-status.test.js +17 -28
- package/dist/__tests__/thread-show-status.test.js.map +1 -1
- package/dist/__tests__/thread-suspend-step.test.js +8 -14
- package/dist/__tests__/thread-suspend-step.test.js.map +1 -1
- package/dist/__tests__/thread-suspended-display.test.js +10 -22
- package/dist/__tests__/thread-suspended-display.test.js.map +1 -1
- package/dist/__tests__/thread.test.js +4 -4
- package/dist/__tests__/thread.test.js.map +1 -1
- package/dist/__tests__/validate-semantic.test.js +49 -21
- package/dist/__tests__/validate-semantic.test.js.map +1 -1
- package/dist/__tests__/workflow-list-recursive.test.d.ts +2 -0
- package/dist/__tests__/workflow-list-recursive.test.d.ts.map +1 -0
- package/dist/__tests__/workflow-list-recursive.test.js +283 -0
- package/dist/__tests__/workflow-list-recursive.test.js.map +1 -0
- package/dist/__tests__/workflow-resolution.test.js +36 -21
- package/dist/__tests__/workflow-resolution.test.js.map +1 -1
- package/dist/__tests__/workflow-show-resolution.test.d.ts +2 -0
- package/dist/__tests__/workflow-show-resolution.test.d.ts.map +1 -0
- package/dist/__tests__/workflow-show-resolution.test.js +210 -0
- package/dist/__tests__/workflow-show-resolution.test.js.map +1 -0
- package/dist/__tests__/workflow-validate.test.d.ts +2 -0
- package/dist/__tests__/workflow-validate.test.d.ts.map +1 -0
- package/dist/__tests__/workflow-validate.test.js +687 -0
- package/dist/__tests__/workflow-validate.test.js.map +1 -0
- package/dist/background/background.d.ts +22 -1
- package/dist/background/background.d.ts.map +1 -1
- package/dist/background/background.js +83 -6
- package/dist/background/background.js.map +1 -1
- package/dist/background/index.d.ts +1 -1
- package/dist/background/index.d.ts.map +1 -1
- package/dist/background/index.js +1 -1
- package/dist/background/index.js.map +1 -1
- package/dist/background/types.d.ts +1 -0
- package/dist/background/types.d.ts.map +1 -1
- package/dist/cli.js +66 -31
- package/dist/cli.js.map +1 -1
- package/dist/commands/config.d.ts +3 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +7 -33
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/prompt.d.ts.map +1 -1
- package/dist/commands/prompt.js +15 -2
- package/dist/commands/prompt.js.map +1 -1
- package/dist/commands/setup.d.ts +7 -39
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +27 -302
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/step.d.ts +44 -1
- package/dist/commands/step.d.ts.map +1 -1
- package/dist/commands/step.js +255 -11
- package/dist/commands/step.js.map +1 -1
- package/dist/commands/thread.d.ts +16 -3
- package/dist/commands/thread.d.ts.map +1 -1
- package/dist/commands/thread.js +379 -140
- package/dist/commands/thread.js.map +1 -1
- package/dist/commands/workflow.d.ts +9 -1
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +130 -6
- package/dist/commands/workflow.js.map +1 -1
- package/dist/moderator/__tests__/evaluate.test.js +31 -17
- package/dist/moderator/__tests__/evaluate.test.js.map +1 -1
- package/dist/moderator/evaluate.d.ts.map +1 -1
- package/dist/moderator/evaluate.js +4 -16
- package/dist/moderator/evaluate.js.map +1 -1
- package/dist/moderator/index.d.ts +1 -2
- package/dist/moderator/index.d.ts.map +1 -1
- package/dist/moderator/index.js +0 -1
- package/dist/moderator/index.js.map +1 -1
- package/dist/moderator/types.d.ts +6 -10
- package/dist/moderator/types.d.ts.map +1 -1
- package/dist/moderator/types.js +1 -3
- package/dist/moderator/types.js.map +1 -1
- package/dist/schemas.d.ts +2 -0
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +5 -3
- package/dist/schemas.js.map +1 -1
- package/dist/store.d.ts +28 -9
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +75 -16
- package/dist/store.js.map +1 -1
- package/dist/validate-semantic.d.ts.map +1 -1
- package/dist/validate-semantic.js +83 -66
- package/dist/validate-semantic.js.map +1 -1
- package/dist/validate.d.ts +6 -0
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +24 -0
- package/dist/validate.js.map +1 -1
- package/package.json +8 -10
- package/src/__tests__/adapter-json-roundtrip.test.ts +1 -1
- package/src/__tests__/agent-resolution-llm-free.test.ts +39 -0
- package/src/__tests__/build-step-entry.test.ts +203 -0
- package/src/__tests__/clear-thread-failed-attempts.test.ts +122 -0
- package/src/__tests__/config.test.ts +33 -321
- package/src/__tests__/current-role.test.ts +7 -6
- package/src/__tests__/e2e-mock-agent.test.ts +20 -23
- package/src/__tests__/fixtures/e2e-count.workflow.yaml +1 -0
- package/src/__tests__/fixtures/e2e-linear.workflow.yaml +1 -0
- package/src/__tests__/fixtures/{e2e-mustache.workflow.yaml → e2e-liquid.workflow.yaml} +3 -2
- package/src/__tests__/fixtures/e2e-loop.workflow.yaml +1 -0
- package/src/__tests__/fixtures/e2e-suspend.mock.yaml +2 -2
- package/src/__tests__/fixtures/e2e-suspend.workflow.yaml +6 -10
- package/src/__tests__/issue-180-workflow-ref-removed.test.ts +43 -0
- package/src/__tests__/moderator-evaluate.test.ts +9 -52
- package/src/__tests__/pid-recycling.test.ts +328 -0
- package/src/__tests__/prompt.test.ts +397 -0
- package/src/__tests__/resolve-head-hash.test.ts +4 -4
- package/src/__tests__/setup-agent-discovery.test.ts +26 -51
- package/src/__tests__/setup-complexity.test.ts +1 -203
- package/src/__tests__/setup-no-llm.test.ts +68 -0
- package/src/__tests__/solve-issue-tea-worktree.test.ts +24 -30
- package/src/__tests__/step-ask.test.ts +670 -0
- package/src/__tests__/step-show-json.test.ts +1 -0
- package/src/__tests__/step-timing.test.ts +2 -0
- package/src/__tests__/store-global-cas.test.ts +2 -2
- package/src/__tests__/store-unified-threads.test.ts +9 -9
- package/src/__tests__/thread-cancel-status.test.ts +6 -6
- package/src/__tests__/thread-list-filters.test.ts +434 -8
- package/src/__tests__/thread-poke.test.ts +545 -0
- package/src/__tests__/thread-resume.test.ts +10 -14
- package/src/__tests__/thread-show-status.test.ts +17 -29
- package/src/__tests__/thread-suspend-step.test.ts +8 -14
- package/src/__tests__/thread-suspended-display.test.ts +10 -22
- package/src/__tests__/thread.test.ts +4 -4
- package/src/__tests__/validate-semantic.test.ts +59 -31
- package/src/__tests__/workflow-list-recursive.test.ts +370 -0
- package/src/__tests__/workflow-resolution.test.ts +39 -21
- package/src/__tests__/workflow-show-resolution.test.ts +285 -0
- package/src/__tests__/workflow-validate.test.ts +806 -0
- package/src/background/background.ts +88 -6
- package/src/background/index.ts +2 -0
- package/src/background/types.ts +1 -0
- package/src/cli.ts +97 -47
- package/src/commands/config.ts +7 -35
- package/src/commands/prompt.ts +15 -2
- package/src/commands/setup.ts +29 -357
- package/src/commands/step.ts +339 -12
- package/src/commands/thread.ts +463 -169
- package/src/commands/workflow.ts +159 -4
- package/src/moderator/__tests__/evaluate.test.ts +34 -17
- package/src/moderator/evaluate.ts +5 -17
- package/src/moderator/index.ts +1 -6
- package/src/moderator/types.ts +6 -14
- package/src/schemas.ts +13 -3
- package/src/store.ts +86 -20
- package/src/validate-semantic.ts +109 -78
- package/src/validate.ts +27 -0
- package/dist/__tests__/setup-validate.test.d.ts +0 -2
- package/dist/__tests__/setup-validate.test.d.ts.map +0 -1
- package/dist/__tests__/setup-validate.test.js +0 -108
- package/dist/__tests__/setup-validate.test.js.map +0 -1
- package/src/__tests__/setup-validate.test.ts +0 -148
- /package/src/__tests__/fixtures/{e2e-mustache.mock.yaml → e2e-liquid.mock.yaml} +0 -0
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { mkdir, mkdtemp, readFile, 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 type {
|
|
9
|
+
CasRef,
|
|
10
|
+
StepNodePayload,
|
|
11
|
+
ThreadId,
|
|
12
|
+
ThreadIndexEntry,
|
|
13
|
+
} from "@united-workforce/protocol";
|
|
14
|
+
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
|
15
|
+
import { registerUwfSchemas } from "../schemas.js";
|
|
16
|
+
import { seedThreads } from "./thread-test-helpers.js";
|
|
17
|
+
|
|
18
|
+
const OUTPUT_SCHEMA = {
|
|
19
|
+
type: "object" as const,
|
|
20
|
+
properties: {
|
|
21
|
+
$status: { type: "string" as const },
|
|
22
|
+
note: { type: "string" as const },
|
|
23
|
+
},
|
|
24
|
+
required: ["$status"],
|
|
25
|
+
additionalProperties: false,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const THREAD_ID = "01POKESTEPTEST00000000" as ThreadId;
|
|
29
|
+
|
|
30
|
+
let tmpDir: string;
|
|
31
|
+
|
|
32
|
+
beforeEach(async () => {
|
|
33
|
+
tmpDir = await mkdtemp(join(tmpdir(), "cli-uwf-poke-test-"));
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
afterEach(async () => {
|
|
37
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
type SetupResult = {
|
|
41
|
+
casDir: string;
|
|
42
|
+
oldStepHash: CasRef;
|
|
43
|
+
oldStepPrev: CasRef | null;
|
|
44
|
+
oldStepCompletedAtMs: number;
|
|
45
|
+
startHash: CasRef;
|
|
46
|
+
workflowHash: CasRef;
|
|
47
|
+
mockAgentPath: string;
|
|
48
|
+
failingAgentPath: string;
|
|
49
|
+
promptCapturePath: string;
|
|
50
|
+
envCapturePath: string;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
type SetupOpts = {
|
|
54
|
+
threadStatus: ThreadIndexEntry["status"];
|
|
55
|
+
multipleSteps: boolean;
|
|
56
|
+
newCompletedAtMs: number;
|
|
57
|
+
newStatus: string;
|
|
58
|
+
// The agent name to record in the head StepNode.agent field. Defaults to mockAgentPath.
|
|
59
|
+
stepAgentNameOverride: string | null;
|
|
60
|
+
// Whether to seed an actual head StepNode (false → only StartNode is the head).
|
|
61
|
+
withHeadStep: boolean;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
async function setupThread(opts: Partial<SetupOpts> = {}): Promise<SetupResult> {
|
|
65
|
+
const cfg: SetupOpts = {
|
|
66
|
+
threadStatus: opts.threadStatus ?? "idle",
|
|
67
|
+
multipleSteps: opts.multipleSteps ?? false,
|
|
68
|
+
newCompletedAtMs: opts.newCompletedAtMs ?? 1716600005000,
|
|
69
|
+
newStatus: opts.newStatus ?? "ok",
|
|
70
|
+
stepAgentNameOverride: opts.stepAgentNameOverride ?? null,
|
|
71
|
+
withHeadStep: opts.withHeadStep ?? true,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const casDir = join(tmpDir, "cas");
|
|
75
|
+
await mkdir(casDir, { recursive: true });
|
|
76
|
+
|
|
77
|
+
const store = await openStore(casDir);
|
|
78
|
+
const schemas = await registerUwfSchemas(store);
|
|
79
|
+
const outputSchemaHash = await putSchema(store, OUTPUT_SCHEMA);
|
|
80
|
+
|
|
81
|
+
const workflowHash = await store.cas.put(schemas.workflow, {
|
|
82
|
+
name: "test-poke",
|
|
83
|
+
description: "poke command integration test",
|
|
84
|
+
roles: {
|
|
85
|
+
worker: {
|
|
86
|
+
description: "Worker role",
|
|
87
|
+
goal: "Work",
|
|
88
|
+
capabilities: [],
|
|
89
|
+
procedure: "work",
|
|
90
|
+
output: "result",
|
|
91
|
+
frontmatter: outputSchemaHash,
|
|
92
|
+
},
|
|
93
|
+
reviewer: {
|
|
94
|
+
description: "Reviewer role",
|
|
95
|
+
goal: "Review",
|
|
96
|
+
capabilities: [],
|
|
97
|
+
procedure: "review",
|
|
98
|
+
output: "result",
|
|
99
|
+
frontmatter: outputSchemaHash,
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
graph: {
|
|
103
|
+
$START: {
|
|
104
|
+
new: { role: "worker", prompt: "Start work", location: null },
|
|
105
|
+
resume: { role: "worker", prompt: "Resume the work", location: null },
|
|
106
|
+
},
|
|
107
|
+
worker: {
|
|
108
|
+
ok: { role: "reviewer", prompt: "Review the work", location: null },
|
|
109
|
+
},
|
|
110
|
+
reviewer: { done: { role: "$END", prompt: "Done", location: null } },
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const startHash = await store.cas.put(schemas.startNode, {
|
|
115
|
+
workflow: workflowHash,
|
|
116
|
+
prompt: "Test poke task",
|
|
117
|
+
cwd: tmpDir,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
process.env.OCAS_HOME = casDir;
|
|
121
|
+
|
|
122
|
+
// Paths for mock agent and capture files (set early so we can use mockAgentPath as the recorded agent name)
|
|
123
|
+
const promptCapturePath = join(tmpDir, "captured-prompt.txt");
|
|
124
|
+
const envCapturePath = join(tmpDir, "captured-env.txt");
|
|
125
|
+
const mockAgentPath = join(tmpDir, "mock-agent.sh");
|
|
126
|
+
const failingAgentPath = join(tmpDir, "failing-agent.sh");
|
|
127
|
+
|
|
128
|
+
// Build head StepNode chain
|
|
129
|
+
let oldStepPrev: CasRef | null = null;
|
|
130
|
+
if (cfg.multipleSteps) {
|
|
131
|
+
// First step: prev=null
|
|
132
|
+
const firstOutputHash = await store.cas.put(outputSchemaHash, { $status: "ok" });
|
|
133
|
+
const firstDetailHash = await store.cas.put(schemas.text, "first detail");
|
|
134
|
+
const firstStepHash = await store.cas.put(schemas.stepNode, {
|
|
135
|
+
start: startHash,
|
|
136
|
+
prev: null,
|
|
137
|
+
role: "worker",
|
|
138
|
+
output: firstOutputHash,
|
|
139
|
+
detail: firstDetailHash,
|
|
140
|
+
agent: cfg.stepAgentNameOverride ?? mockAgentPath,
|
|
141
|
+
edgePrompt: "Start work",
|
|
142
|
+
startedAtMs: 1716600000000,
|
|
143
|
+
completedAtMs: 1716600001000,
|
|
144
|
+
cwd: tmpDir,
|
|
145
|
+
assembledPrompt: null,
|
|
146
|
+
usage: null,
|
|
147
|
+
});
|
|
148
|
+
oldStepPrev = firstStepHash;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
let oldStepHash: CasRef = startHash;
|
|
152
|
+
const oldStepCompletedAtMs = 1716600002000;
|
|
153
|
+
if (cfg.withHeadStep) {
|
|
154
|
+
const outputHash = await store.cas.put(outputSchemaHash, { $status: "ok" });
|
|
155
|
+
const detailHash = await store.cas.put(schemas.text, "head step detail");
|
|
156
|
+
oldStepHash = await store.cas.put(schemas.stepNode, {
|
|
157
|
+
start: startHash,
|
|
158
|
+
prev: oldStepPrev,
|
|
159
|
+
role: "worker",
|
|
160
|
+
output: outputHash,
|
|
161
|
+
detail: detailHash,
|
|
162
|
+
agent: cfg.stepAgentNameOverride ?? mockAgentPath,
|
|
163
|
+
edgePrompt: "Start work",
|
|
164
|
+
startedAtMs: 1716600001500,
|
|
165
|
+
completedAtMs: oldStepCompletedAtMs,
|
|
166
|
+
cwd: tmpDir,
|
|
167
|
+
assembledPrompt: null,
|
|
168
|
+
usage: null,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Seed thread index entry. For "running" we let the test create the marker separately.
|
|
173
|
+
await seedThreads(tmpDir, {
|
|
174
|
+
[THREAD_ID]: {
|
|
175
|
+
head: oldStepHash,
|
|
176
|
+
status: cfg.threadStatus,
|
|
177
|
+
suspendedRole: cfg.threadStatus === "suspended" ? "worker" : null,
|
|
178
|
+
suspendMessage: cfg.threadStatus === "suspended" ? "Please clarify" : null,
|
|
179
|
+
completedAt:
|
|
180
|
+
cfg.threadStatus === "end" || cfg.threadStatus === "cancelled"
|
|
181
|
+
? oldStepCompletedAtMs
|
|
182
|
+
: null,
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Mock agent always emits a stepNode keyed off the current thread head (which we
|
|
187
|
+
// observe through OCAS_HOME). The script writes prompt/env captures and then prints
|
|
188
|
+
// an adapter JSON that references a pre-built stepHash.
|
|
189
|
+
// We pre-build the agent's stepHash with prev=oldStepHash (normal append behaviour).
|
|
190
|
+
const newOutputHash = await store.cas.put(outputSchemaHash, {
|
|
191
|
+
$status: cfg.newStatus,
|
|
192
|
+
note: "poked output",
|
|
193
|
+
});
|
|
194
|
+
const newDetailHash = await store.cas.put(schemas.text, "poked detail");
|
|
195
|
+
const agentStepHash = await store.cas.put(schemas.stepNode, {
|
|
196
|
+
start: startHash,
|
|
197
|
+
prev: cfg.withHeadStep ? oldStepHash : null,
|
|
198
|
+
role: "worker",
|
|
199
|
+
output: newOutputHash,
|
|
200
|
+
detail: newDetailHash,
|
|
201
|
+
agent: "mock-agent-output",
|
|
202
|
+
edgePrompt: "poke prompt placeholder",
|
|
203
|
+
startedAtMs: cfg.newCompletedAtMs - 100,
|
|
204
|
+
completedAtMs: cfg.newCompletedAtMs,
|
|
205
|
+
cwd: tmpDir,
|
|
206
|
+
assembledPrompt: null,
|
|
207
|
+
usage: null,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const adapterJson = JSON.stringify({
|
|
211
|
+
stepHash: agentStepHash,
|
|
212
|
+
detailHash: newDetailHash,
|
|
213
|
+
role: "worker",
|
|
214
|
+
frontmatter: { $status: cfg.newStatus, note: "poked output" },
|
|
215
|
+
body: "",
|
|
216
|
+
startedAtMs: cfg.newCompletedAtMs - 100,
|
|
217
|
+
completedAtMs: cfg.newCompletedAtMs,
|
|
218
|
+
usage: null,
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
await writeFile(
|
|
222
|
+
mockAgentPath,
|
|
223
|
+
`#!/bin/sh
|
|
224
|
+
prompt=""
|
|
225
|
+
while [ $# -gt 0 ]; do
|
|
226
|
+
if [ "$1" = "--prompt" ]; then
|
|
227
|
+
prompt="$2"
|
|
228
|
+
shift 2
|
|
229
|
+
else
|
|
230
|
+
shift
|
|
231
|
+
fi
|
|
232
|
+
done
|
|
233
|
+
printf '%s' "$prompt" > '${promptCapturePath}'
|
|
234
|
+
printf 'OCAS_HOME=%s\\n' "$OCAS_HOME" > '${envCapturePath}'
|
|
235
|
+
echo '${adapterJson}'
|
|
236
|
+
`,
|
|
237
|
+
{ mode: 0o755 },
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
await writeFile(
|
|
241
|
+
failingAgentPath,
|
|
242
|
+
`#!/bin/sh
|
|
243
|
+
echo "boom" >&2
|
|
244
|
+
exit 7
|
|
245
|
+
`,
|
|
246
|
+
{ mode: 0o755 },
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
const configPath = join(tmpDir, "config.yaml");
|
|
250
|
+
await writeFile(
|
|
251
|
+
configPath,
|
|
252
|
+
`defaultAgent: uwf-hermes\nagentOverrides: null\nagents:\n uwf-hermes:\n command: uwf-hermes\n`,
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
casDir,
|
|
257
|
+
oldStepHash,
|
|
258
|
+
oldStepPrev,
|
|
259
|
+
oldStepCompletedAtMs,
|
|
260
|
+
startHash,
|
|
261
|
+
workflowHash,
|
|
262
|
+
mockAgentPath,
|
|
263
|
+
failingAgentPath,
|
|
264
|
+
promptCapturePath,
|
|
265
|
+
envCapturePath,
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function runUwf(
|
|
270
|
+
args: string[],
|
|
271
|
+
casDir: string,
|
|
272
|
+
): { stdout: string; stderr: string; status: number } {
|
|
273
|
+
const cliPath = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "dist", "cli.js");
|
|
274
|
+
try {
|
|
275
|
+
const stdout = execFileSync(process.execPath, [cliPath, ...args], {
|
|
276
|
+
encoding: "utf8",
|
|
277
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
278
|
+
env: {
|
|
279
|
+
...process.env,
|
|
280
|
+
UWF_HOME: tmpDir,
|
|
281
|
+
OCAS_HOME: casDir,
|
|
282
|
+
},
|
|
283
|
+
cwd: tmpDir,
|
|
284
|
+
timeout: 30000,
|
|
285
|
+
});
|
|
286
|
+
return { stdout, stderr: "", status: 0 };
|
|
287
|
+
} catch (error) {
|
|
288
|
+
const err = error as NodeJS.ErrnoException & {
|
|
289
|
+
stdout?: string | Buffer;
|
|
290
|
+
stderr?: string | Buffer;
|
|
291
|
+
status?: number;
|
|
292
|
+
};
|
|
293
|
+
return {
|
|
294
|
+
stdout: typeof err.stdout === "string" ? err.stdout : (err.stdout?.toString("utf8") ?? ""),
|
|
295
|
+
stderr: typeof err.stderr === "string" ? err.stderr : (err.stderr?.toString("utf8") ?? ""),
|
|
296
|
+
status: err.status ?? 1,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ── Group 1: CLI argument validation ───────────────────────────────────────
|
|
302
|
+
|
|
303
|
+
describe("uwf thread poke - CLI argument validation", () => {
|
|
304
|
+
test("1.1 missing -p flag exits non-zero", async () => {
|
|
305
|
+
const { casDir } = await setupThread();
|
|
306
|
+
const result = runUwf(["thread", "poke", THREAD_ID], casDir);
|
|
307
|
+
expect(result.status).not.toBe(0);
|
|
308
|
+
expect(result.stderr.toLowerCase()).toMatch(/required|missing|prompt/);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
test("1.2 -p without --agent succeeds", async () => {
|
|
312
|
+
const { casDir } = await setupThread();
|
|
313
|
+
const result = runUwf(["thread", "poke", THREAD_ID, "-p", "do it again"], casDir);
|
|
314
|
+
expect(result.status).toBe(0);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
test("1.3 -p with --agent succeeds", async () => {
|
|
318
|
+
const { casDir, mockAgentPath } = await setupThread();
|
|
319
|
+
const result = runUwf(
|
|
320
|
+
["thread", "poke", THREAD_ID, "-p", "do it again", "--agent", mockAgentPath],
|
|
321
|
+
casDir,
|
|
322
|
+
);
|
|
323
|
+
expect(result.status).toBe(0);
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
// ── Group 2: Guard errors ──────────────────────────────────────────────────
|
|
328
|
+
|
|
329
|
+
describe("uwf thread poke - guard errors", () => {
|
|
330
|
+
test("2.1 thread not found", async () => {
|
|
331
|
+
const { casDir } = await setupThread();
|
|
332
|
+
const result = runUwf(["thread", "poke", "01NOSUCHTHREAD0000000A", "-p", "prompt"], casDir);
|
|
333
|
+
expect(result.status).not.toBe(0);
|
|
334
|
+
expect(result.stderr.toLowerCase()).toMatch(/not found|not active/);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
test("2.2 thread running rejects poke", async () => {
|
|
338
|
+
const { casDir, workflowHash } = await setupThread();
|
|
339
|
+
// Create background marker to simulate running
|
|
340
|
+
const { createMarker, getProcessStartTime } = await import("../background/index.js");
|
|
341
|
+
await createMarker(tmpDir, {
|
|
342
|
+
thread: THREAD_ID,
|
|
343
|
+
workflow: workflowHash,
|
|
344
|
+
pid: process.pid,
|
|
345
|
+
startedAt: Date.now(),
|
|
346
|
+
processStartTime: getProcessStartTime(process.pid),
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
const result = runUwf(["thread", "poke", THREAD_ID, "-p", "prompt"], casDir);
|
|
350
|
+
expect(result.status).not.toBe(0);
|
|
351
|
+
expect(result.stderr.toLowerCase()).toContain("already executing");
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
test("2.3 completed thread rejects poke", async () => {
|
|
355
|
+
const { casDir } = await setupThread({ threadStatus: "end" });
|
|
356
|
+
const result = runUwf(["thread", "poke", THREAD_ID, "-p", "prompt"], casDir);
|
|
357
|
+
expect(result.status).not.toBe(0);
|
|
358
|
+
expect(result.stderr.toLowerCase()).toMatch(/cannot be poked|end/);
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
test("2.4 cancelled thread rejects poke", async () => {
|
|
362
|
+
const { casDir } = await setupThread({ threadStatus: "cancelled" });
|
|
363
|
+
const result = runUwf(["thread", "poke", THREAD_ID, "-p", "prompt"], casDir);
|
|
364
|
+
expect(result.status).not.toBe(0);
|
|
365
|
+
expect(result.stderr.toLowerCase()).toMatch(/cannot be poked|cancelled/);
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
test("2.5 thread head is StartNode (no StepNode) rejects poke", async () => {
|
|
369
|
+
const { casDir } = await setupThread({ withHeadStep: false });
|
|
370
|
+
const result = runUwf(["thread", "poke", THREAD_ID, "-p", "prompt"], casDir);
|
|
371
|
+
expect(result.status).not.toBe(0);
|
|
372
|
+
expect(result.stderr.toLowerCase()).toMatch(/no step|cannot be poked/);
|
|
373
|
+
});
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
// ── Group 3: Success happy path ────────────────────────────────────────────
|
|
377
|
+
|
|
378
|
+
describe("uwf thread poke - success", () => {
|
|
379
|
+
test("3.1, 3.4 idle thread → new head differs from old, thread index updated", async () => {
|
|
380
|
+
const { casDir, oldStepHash, mockAgentPath } = await setupThread();
|
|
381
|
+
const result = runUwf(
|
|
382
|
+
["thread", "poke", THREAD_ID, "-p", "redo", "--agent", mockAgentPath],
|
|
383
|
+
casDir,
|
|
384
|
+
);
|
|
385
|
+
expect(result.status).toBe(0);
|
|
386
|
+
const cliOutput = JSON.parse(result.stdout.trim());
|
|
387
|
+
expect(cliOutput.head).not.toBe(oldStepHash);
|
|
388
|
+
|
|
389
|
+
const { createUwfStore, getThread } = await import("../store.js");
|
|
390
|
+
const uwf = await createUwfStore(tmpDir);
|
|
391
|
+
const entry = getThread(uwf.varStore, THREAD_ID);
|
|
392
|
+
expect(entry?.head).toBe(cliOutput.head);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
test("3.2 new step's prev equals old head's prev (replace, not append)", async () => {
|
|
396
|
+
const { casDir, oldStepPrev, mockAgentPath } = await setupThread({ multipleSteps: true });
|
|
397
|
+
const result = runUwf(
|
|
398
|
+
["thread", "poke", THREAD_ID, "-p", "redo", "--agent", mockAgentPath],
|
|
399
|
+
casDir,
|
|
400
|
+
);
|
|
401
|
+
expect(result.status).toBe(0);
|
|
402
|
+
const cliOutput = JSON.parse(result.stdout.trim());
|
|
403
|
+
|
|
404
|
+
const { createUwfStore } = await import("../store.js");
|
|
405
|
+
const uwf = await createUwfStore(tmpDir);
|
|
406
|
+
const node = uwf.store.cas.get(cliOutput.head as CasRef);
|
|
407
|
+
expect(node).not.toBeNull();
|
|
408
|
+
expect(node?.type).toBe(uwf.schemas.stepNode);
|
|
409
|
+
const payload = node?.payload as StepNodePayload;
|
|
410
|
+
expect(payload.prev).toBe(oldStepPrev);
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
test("3.2b new step's prev is null when old head was the first step", async () => {
|
|
414
|
+
// multipleSteps:false means oldHead.prev = null
|
|
415
|
+
const { casDir, mockAgentPath } = await setupThread({ multipleSteps: false });
|
|
416
|
+
const result = runUwf(
|
|
417
|
+
["thread", "poke", THREAD_ID, "-p", "redo", "--agent", mockAgentPath],
|
|
418
|
+
casDir,
|
|
419
|
+
);
|
|
420
|
+
expect(result.status).toBe(0);
|
|
421
|
+
const cliOutput = JSON.parse(result.stdout.trim());
|
|
422
|
+
|
|
423
|
+
const { createUwfStore } = await import("../store.js");
|
|
424
|
+
const uwf = await createUwfStore(tmpDir);
|
|
425
|
+
const node = uwf.store.cas.get(cliOutput.head as CasRef);
|
|
426
|
+
const payload = node?.payload as StepNodePayload;
|
|
427
|
+
expect(payload.prev).toBeNull();
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
test("3.3 new step's completedAtMs is later than old", async () => {
|
|
431
|
+
const { casDir, oldStepCompletedAtMs, mockAgentPath } = await setupThread();
|
|
432
|
+
const result = runUwf(
|
|
433
|
+
["thread", "poke", THREAD_ID, "-p", "redo", "--agent", mockAgentPath],
|
|
434
|
+
casDir,
|
|
435
|
+
);
|
|
436
|
+
expect(result.status).toBe(0);
|
|
437
|
+
const cliOutput = JSON.parse(result.stdout.trim());
|
|
438
|
+
|
|
439
|
+
const { createUwfStore } = await import("../store.js");
|
|
440
|
+
const uwf = await createUwfStore(tmpDir);
|
|
441
|
+
const node = uwf.store.cas.get(cliOutput.head as CasRef);
|
|
442
|
+
const payload = node?.payload as StepNodePayload;
|
|
443
|
+
expect(payload.completedAtMs).toBeGreaterThan(oldStepCompletedAtMs);
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
test("3.5 status remains idle after poke (no completion/suspend)", async () => {
|
|
447
|
+
const { casDir, mockAgentPath } = await setupThread();
|
|
448
|
+
const result = runUwf(
|
|
449
|
+
["thread", "poke", THREAD_ID, "-p", "redo", "--agent", mockAgentPath],
|
|
450
|
+
casDir,
|
|
451
|
+
);
|
|
452
|
+
expect(result.status).toBe(0);
|
|
453
|
+
const cliOutput = JSON.parse(result.stdout.trim());
|
|
454
|
+
expect(cliOutput.status).toBe("idle");
|
|
455
|
+
expect(cliOutput.done).toBe(false);
|
|
456
|
+
expect(cliOutput.suspendedRole).toBeNull();
|
|
457
|
+
expect(cliOutput.suspendMessage).toBeNull();
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
test("3.6 currentRole unchanged after poke (no moderator re-route)", async () => {
|
|
461
|
+
// Before poke: idle thread with worker step having $status=ok → moderator would route to reviewer.
|
|
462
|
+
// After poke (mock returns same $status=ok), moderator routing remains the same.
|
|
463
|
+
const { casDir, mockAgentPath } = await setupThread();
|
|
464
|
+
const result = runUwf(
|
|
465
|
+
["thread", "poke", THREAD_ID, "-p", "redo", "--agent", mockAgentPath],
|
|
466
|
+
casDir,
|
|
467
|
+
);
|
|
468
|
+
expect(result.status).toBe(0);
|
|
469
|
+
const cliOutput = JSON.parse(result.stdout.trim());
|
|
470
|
+
expect(cliOutput.currentRole).toBe("reviewer");
|
|
471
|
+
});
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
// ── Group 4: Agent resolution ──────────────────────────────────────────────
|
|
475
|
+
|
|
476
|
+
describe("uwf thread poke - agent resolution", () => {
|
|
477
|
+
test("4.1 without --agent, agent command read from head step's agent field", async () => {
|
|
478
|
+
// Head step's agent field points at mockAgentPath (default in setupThread)
|
|
479
|
+
const { casDir, promptCapturePath } = await setupThread();
|
|
480
|
+
const result = runUwf(["thread", "poke", THREAD_ID, "-p", "redo"], casDir);
|
|
481
|
+
expect(result.status).toBe(0);
|
|
482
|
+
const captured = await readFile(promptCapturePath, "utf8");
|
|
483
|
+
expect(captured).toBe("redo");
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
test("4.2 with --agent, explicit override is used", async () => {
|
|
487
|
+
// Head step records "uwf-mock" (which is not a real binary). Override with mockAgentPath.
|
|
488
|
+
const { casDir, mockAgentPath } = await setupThread({ stepAgentNameOverride: "uwf-mock" });
|
|
489
|
+
const result = runUwf(
|
|
490
|
+
["thread", "poke", THREAD_ID, "-p", "redo", "--agent", mockAgentPath],
|
|
491
|
+
casDir,
|
|
492
|
+
);
|
|
493
|
+
expect(result.status).toBe(0);
|
|
494
|
+
});
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
// ── Group 5: Prompt passthrough ────────────────────────────────────────────
|
|
498
|
+
|
|
499
|
+
describe("uwf thread poke - prompt passthrough", () => {
|
|
500
|
+
test("5.1 -p value is passed to agent as --prompt", async () => {
|
|
501
|
+
const { casDir, mockAgentPath, promptCapturePath } = await setupThread();
|
|
502
|
+
const supplement = "Use the REST API instead.";
|
|
503
|
+
const result = runUwf(
|
|
504
|
+
["thread", "poke", THREAD_ID, "-p", supplement, "--agent", mockAgentPath],
|
|
505
|
+
casDir,
|
|
506
|
+
);
|
|
507
|
+
expect(result.status).toBe(0);
|
|
508
|
+
const captured = await readFile(promptCapturePath, "utf8");
|
|
509
|
+
expect(captured).toBe(supplement);
|
|
510
|
+
});
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
// ── Group 6: Edge cases ────────────────────────────────────────────────────
|
|
514
|
+
|
|
515
|
+
describe("uwf thread poke - edge cases", () => {
|
|
516
|
+
test("6.1 poke succeeds on suspended thread", async () => {
|
|
517
|
+
const { casDir, oldStepHash, mockAgentPath } = await setupThread({
|
|
518
|
+
threadStatus: "suspended",
|
|
519
|
+
});
|
|
520
|
+
const result = runUwf(
|
|
521
|
+
["thread", "poke", THREAD_ID, "-p", "redo", "--agent", mockAgentPath],
|
|
522
|
+
casDir,
|
|
523
|
+
);
|
|
524
|
+
expect(result.status).toBe(0);
|
|
525
|
+
const cliOutput = JSON.parse(result.stdout.trim());
|
|
526
|
+
expect(cliOutput.head).not.toBe(oldStepHash);
|
|
527
|
+
expect(cliOutput.status).toBe("idle");
|
|
528
|
+
expect(cliOutput.suspendedRole).toBeNull();
|
|
529
|
+
expect(cliOutput.suspendMessage).toBeNull();
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
test("6.2 agent failure leaves thread head unchanged", async () => {
|
|
533
|
+
const { casDir, oldStepHash, failingAgentPath } = await setupThread();
|
|
534
|
+
const result = runUwf(
|
|
535
|
+
["thread", "poke", THREAD_ID, "-p", "redo", "--agent", failingAgentPath],
|
|
536
|
+
casDir,
|
|
537
|
+
);
|
|
538
|
+
expect(result.status).not.toBe(0);
|
|
539
|
+
|
|
540
|
+
const { createUwfStore, getThread } = await import("../store.js");
|
|
541
|
+
const uwf = await createUwfStore(tmpDir);
|
|
542
|
+
const entry = getThread(uwf.varStore, THREAD_ID);
|
|
543
|
+
expect(entry?.head).toBe(oldStepHash);
|
|
544
|
+
});
|
|
545
|
+
});
|
|
@@ -75,11 +75,6 @@ async function setupSuspendedThread(mode: MockAgentMode): Promise<{
|
|
|
75
75
|
resume: { role: "worker", prompt: "Resume the work", location: null },
|
|
76
76
|
},
|
|
77
77
|
worker: {
|
|
78
|
-
needs_input: {
|
|
79
|
-
role: "$SUSPEND",
|
|
80
|
-
prompt: "Please clarify: {{{question}}}",
|
|
81
|
-
location: null,
|
|
82
|
-
},
|
|
83
78
|
ok: { role: "reviewer", prompt: "Review the work", location: null },
|
|
84
79
|
},
|
|
85
80
|
reviewer: { done: { role: "$END", prompt: "Done", location: null } },
|
|
@@ -95,9 +90,9 @@ async function setupSuspendedThread(mode: MockAgentMode): Promise<{
|
|
|
95
90
|
process.env.OCAS_HOME = casDir;
|
|
96
91
|
await seedThreads(tmpDir, { [THREAD_ID]: startHash });
|
|
97
92
|
|
|
98
|
-
const outputHash = await store.cas.put(
|
|
99
|
-
$status: "
|
|
100
|
-
|
|
93
|
+
const outputHash = await store.cas.put(schemas.suspendOutput, {
|
|
94
|
+
$status: "$SUSPEND",
|
|
95
|
+
reason: SUSPEND_MESSAGE,
|
|
101
96
|
});
|
|
102
97
|
const detailHash = await store.cas.put(schemas.text, "mock detail");
|
|
103
98
|
|
|
@@ -132,14 +127,15 @@ async function setupSuspendedThread(mode: MockAgentMode): Promise<{
|
|
|
132
127
|
const mockAgentPath = join(tmpDir, "mock-agent.sh");
|
|
133
128
|
|
|
134
129
|
const frontmatter =
|
|
135
|
-
mode === "suspend" ? { $status: "
|
|
130
|
+
mode === "suspend" ? { $status: "$SUSPEND", reason: SUSPEND_MESSAGE } : { $status: "ok" };
|
|
131
|
+
const frontmatterSchema = mode === "suspend" ? schemas.suspendOutput : outputSchemaHash;
|
|
136
132
|
|
|
137
133
|
const adapterJson = JSON.stringify({
|
|
138
134
|
stepHash: await store.cas.put(schemas.stepNode, {
|
|
139
135
|
start: startHash,
|
|
140
136
|
prev: stepHash,
|
|
141
137
|
role: "worker",
|
|
142
|
-
output: await store.cas.put(
|
|
138
|
+
output: await store.cas.put(frontmatterSchema, frontmatter),
|
|
143
139
|
detail: detailHash,
|
|
144
140
|
agent: "uwf-mock",
|
|
145
141
|
edgePrompt: "resume prompt placeholder",
|
|
@@ -177,7 +173,7 @@ echo '${adapterJson}'
|
|
|
177
173
|
const configPath = join(tmpDir, "config.yaml");
|
|
178
174
|
await writeFile(
|
|
179
175
|
configPath,
|
|
180
|
-
`defaultAgent: uwf-hermes\
|
|
176
|
+
`defaultAgent: uwf-hermes\nagentOverrides: null\nagents:\n uwf-hermes:\n command: uwf-hermes\n`,
|
|
181
177
|
);
|
|
182
178
|
|
|
183
179
|
return { casDir, mockAgentPath, promptCapturePath };
|
|
@@ -338,7 +334,7 @@ describe("uwf thread resume", () => {
|
|
|
338
334
|
}
|
|
339
335
|
});
|
|
340
336
|
|
|
341
|
-
test("multiple suspend/resume cycles", async () => {
|
|
337
|
+
test("multiple suspend/resume cycles", { timeout: 15_000 }, async () => {
|
|
342
338
|
const originalCasDir = process.env.OCAS_HOME;
|
|
343
339
|
const { casDir, mockAgentPath, promptCapturePath } = await setupSuspendedThread("suspend");
|
|
344
340
|
process.env.OCAS_HOME = casDir;
|
|
@@ -537,7 +533,7 @@ describe("uwf thread resume - completed threads", () => {
|
|
|
537
533
|
await seedThreads(tmpDir, {
|
|
538
534
|
[THREAD_ID]: {
|
|
539
535
|
head: reviewerStepHash,
|
|
540
|
-
status: "
|
|
536
|
+
status: "end",
|
|
541
537
|
suspendedRole: null,
|
|
542
538
|
suspendMessage: null,
|
|
543
539
|
completedAt: 1716600002000,
|
|
@@ -599,7 +595,7 @@ echo '${adapterJson}'
|
|
|
599
595
|
const configPath = join(tmpDir, "config.yaml");
|
|
600
596
|
await writeFile(
|
|
601
597
|
configPath,
|
|
602
|
-
`defaultAgent: uwf-hermes\
|
|
598
|
+
`defaultAgent: uwf-hermes\nagentOverrides: null\nagents:\n uwf-hermes:\n command: uwf-hermes\n`,
|
|
603
599
|
);
|
|
604
600
|
|
|
605
601
|
const result = runUwf(
|