@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
|
@@ -4,7 +4,7 @@ import { join } from "node:path";
|
|
|
4
4
|
import { putSchema } from "@ocas/core";
|
|
5
5
|
import type { CasRef, ThreadId } from "@united-workforce/protocol";
|
|
6
6
|
import { describe, expect, test } from "vitest";
|
|
7
|
-
import { createMarker, deleteMarker } from "../background/index.js";
|
|
7
|
+
import { createMarker, deleteMarker, getProcessStartTime } from "../background/index.js";
|
|
8
8
|
import { cmdThreadList, cmdThreadShow, cmdThreadStart } from "../commands/thread.js";
|
|
9
9
|
import { completeThread, createUwfStore, loadActiveThreads, setThread } from "../store.js";
|
|
10
10
|
|
|
@@ -307,10 +307,10 @@ describe("currentRole field", () => {
|
|
|
307
307
|
|
|
308
308
|
const uwfForIndex = await createUwfStore(storageRoot);
|
|
309
309
|
loadActiveThreads(uwfForIndex.varStore)[tid]!.head;
|
|
310
|
-
completeThread(uwfForIndex.varStore, tid, "
|
|
310
|
+
completeThread(uwfForIndex.varStore, tid, "end");
|
|
311
311
|
|
|
312
312
|
const result = await cmdThreadShow(storageRoot, tid);
|
|
313
|
-
expect(result.status).toBe("
|
|
313
|
+
expect(result.status).toBe("end");
|
|
314
314
|
expect(result.currentRole).toBe(null);
|
|
315
315
|
} finally {
|
|
316
316
|
await teardown();
|
|
@@ -352,6 +352,7 @@ describe("currentRole field", () => {
|
|
|
352
352
|
workflow,
|
|
353
353
|
pid: process.pid,
|
|
354
354
|
startedAt: Date.now(),
|
|
355
|
+
processStartTime: getProcessStartTime(process.pid),
|
|
355
356
|
});
|
|
356
357
|
|
|
357
358
|
try {
|
|
@@ -378,13 +379,13 @@ describe("currentRole field", () => {
|
|
|
378
379
|
const idleId = idle.thread as ThreadId;
|
|
379
380
|
|
|
380
381
|
// completed thread
|
|
381
|
-
const comp = await cmdThreadStart(storageRoot, wf, "
|
|
382
|
+
const comp = await cmdThreadStart(storageRoot, wf, "end", tmpDir);
|
|
382
383
|
const compId = comp.thread as ThreadId;
|
|
383
384
|
const uwfForIndex = await createUwfStore(storageRoot);
|
|
384
385
|
const _compHead = loadActiveThreads(uwfForIndex.varStore)[compId]!.head;
|
|
385
|
-
completeThread(uwfForIndex.varStore, compId, "
|
|
386
|
+
completeThread(uwfForIndex.varStore, compId, "end");
|
|
386
387
|
|
|
387
|
-
const list = await cmdThreadList(storageRoot, null, null, null, 0, 100);
|
|
388
|
+
const list = await cmdThreadList(storageRoot, null, null, null, 0, 100, true);
|
|
388
389
|
|
|
389
390
|
const idleItem = list.find((i) => i.thread === idleId);
|
|
390
391
|
expect(idleItem).toBeDefined();
|
|
@@ -78,9 +78,6 @@ afterEach(async () => {
|
|
|
78
78
|
async function writeMockConfig(mockDataFixture: string): Promise<void> {
|
|
79
79
|
const config = {
|
|
80
80
|
defaultAgent: "mock",
|
|
81
|
-
defaultModel: "test",
|
|
82
|
-
providers: {},
|
|
83
|
-
models: {},
|
|
84
81
|
agentOverrides: null,
|
|
85
82
|
agents: {
|
|
86
83
|
mock: {
|
|
@@ -187,7 +184,7 @@ function getStatus(store: Awaited<ReturnType<typeof openStore>>, outputRef: CasR
|
|
|
187
184
|
|
|
188
185
|
// ── scenarios ─────────────────────────────────────────────────────────────────
|
|
189
186
|
|
|
190
|
-
describe("E2E mock-agent: full uwf pipeline", () => {
|
|
187
|
+
describe("E2E mock-agent: full uwf pipeline", { timeout: 15_000 }, () => {
|
|
191
188
|
test("1. linear workflow runs planner then worker and reaches $END", async () => {
|
|
192
189
|
await writeMockConfig("e2e-linear.mock.yaml");
|
|
193
190
|
const workflowHash = await addWorkflow("e2e-linear.workflow.yaml", "test-linear");
|
|
@@ -209,7 +206,7 @@ describe("E2E mock-agent: full uwf pipeline", () => {
|
|
|
209
206
|
// Step 2 → worker → $END (thread archived to history).
|
|
210
207
|
const step2 = execStep(threadId);
|
|
211
208
|
expect(step2.done).toBe(true);
|
|
212
|
-
expect(step2.status).toBe("
|
|
209
|
+
expect(step2.status).toBe("end");
|
|
213
210
|
expect(step2.currentRole).toBeNull();
|
|
214
211
|
|
|
215
212
|
// Verify CAS chain integrity: start → step1 → step2.
|
|
@@ -237,11 +234,11 @@ describe("E2E mock-agent: full uwf pipeline", () => {
|
|
|
237
234
|
const startNode = store.cas.get(startHash as CasRef);
|
|
238
235
|
expect((startNode!.payload as StartNodePayload).workflow).toBe(workflowHash);
|
|
239
236
|
|
|
240
|
-
// Thread is completed: status changed to "
|
|
237
|
+
// Thread is completed: status changed to "end", head updated.
|
|
241
238
|
const uwf = await createUwfStore(uwfHome);
|
|
242
239
|
const finalEntry = getThread(uwf.varStore, threadId);
|
|
243
240
|
expect(finalEntry).not.toBeNull();
|
|
244
|
-
expect(finalEntry!.status).toBe("
|
|
241
|
+
expect(finalEntry!.status).toBe("end");
|
|
245
242
|
expect(finalEntry!.head).toBe(step2.head);
|
|
246
243
|
});
|
|
247
244
|
|
|
@@ -270,7 +267,7 @@ describe("E2E mock-agent: full uwf pipeline", () => {
|
|
|
270
267
|
|
|
271
268
|
const s4 = execStep(threadId);
|
|
272
269
|
expect(s4.done).toBe(true);
|
|
273
|
-
expect(s4.status).toBe("
|
|
270
|
+
expect(s4.status).toBe("end");
|
|
274
271
|
|
|
275
272
|
// Verify the chain order and roles.
|
|
276
273
|
const store = await openStore(casDir);
|
|
@@ -302,7 +299,7 @@ describe("E2E mock-agent: full uwf pipeline", () => {
|
|
|
302
299
|
const uwf = await createUwfStore(uwfHome);
|
|
303
300
|
const finalEntry = getThread(uwf.varStore, threadId);
|
|
304
301
|
expect(finalEntry).not.toBeNull();
|
|
305
|
-
expect(finalEntry!.status).toBe("
|
|
302
|
+
expect(finalEntry!.status).toBe("end");
|
|
306
303
|
});
|
|
307
304
|
|
|
308
305
|
test("3. role mismatch in mock data makes the agent exit with an error", {
|
|
@@ -329,7 +326,7 @@ describe("E2E mock-agent: full uwf pipeline", () => {
|
|
|
329
326
|
const uwf = await createUwfStore(uwfHome);
|
|
330
327
|
const entry = getThread(uwf.varStore, threadId);
|
|
331
328
|
expect(entry).not.toBeNull();
|
|
332
|
-
expect(entry!.status).not.toBe("
|
|
329
|
+
expect(entry!.status).not.toBe("end");
|
|
333
330
|
expect(entry!.head).toBe(step1.head);
|
|
334
331
|
});
|
|
335
332
|
|
|
@@ -361,7 +358,7 @@ describe("E2E mock-agent: full uwf pipeline", () => {
|
|
|
361
358
|
const resume = runResume(threadId, "Here are the requirements");
|
|
362
359
|
expect(resume.exitCode).toBe(0);
|
|
363
360
|
const resumeOut = JSON.parse(resume.stdout.trim()) as StepOutputJson;
|
|
364
|
-
expect(resumeOut.status).toBe("
|
|
361
|
+
expect(resumeOut.status).toBe("end");
|
|
365
362
|
expect(resumeOut.done).toBe(true);
|
|
366
363
|
expect(resumeOut.currentRole).toBeNull();
|
|
367
364
|
expect(resumeOut.suspendedRole).toBeNull();
|
|
@@ -373,12 +370,12 @@ describe("E2E mock-agent: full uwf pipeline", () => {
|
|
|
373
370
|
expect(s1.role).toBe("planner");
|
|
374
371
|
expect(s2.role).toBe("planner");
|
|
375
372
|
expect(s2.prev).toBe(step1.head);
|
|
376
|
-
expect(getStatus(store, s1.output)).toBe("
|
|
373
|
+
expect(getStatus(store, s1.output)).toBe("$SUSPEND");
|
|
377
374
|
expect(getStatus(store, s2.output)).toBe("ready");
|
|
378
375
|
|
|
379
376
|
const finalEntry = getThread((await createUwfStore(uwfHome)).varStore, threadId);
|
|
380
377
|
expect(finalEntry).not.toBeNull();
|
|
381
|
-
expect(finalEntry!.status).toBe("
|
|
378
|
+
expect(finalEntry!.status).toBe("end");
|
|
382
379
|
expect(finalEntry!.head).toBe(resumeOut.head);
|
|
383
380
|
});
|
|
384
381
|
|
|
@@ -404,7 +401,7 @@ describe("E2E mock-agent: full uwf pipeline", () => {
|
|
|
404
401
|
expect(results[0].currentRole).toBe("developer");
|
|
405
402
|
expect(results[1].status).toBe("idle");
|
|
406
403
|
expect(results[1].currentRole).toBe("reviewer");
|
|
407
|
-
expect(results[2].status).toBe("
|
|
404
|
+
expect(results[2].status).toBe("end");
|
|
408
405
|
expect(results[2].done).toBe(true);
|
|
409
406
|
|
|
410
407
|
// Verify the CAS chain holds 3 step nodes in the correct order.
|
|
@@ -420,15 +417,15 @@ describe("E2E mock-agent: full uwf pipeline", () => {
|
|
|
420
417
|
|
|
421
418
|
const finalEntry = getThread((await createUwfStore(uwfHome)).varStore, threadId);
|
|
422
419
|
expect(finalEntry).not.toBeNull();
|
|
423
|
-
expect(finalEntry!.status).toBe("
|
|
420
|
+
expect(finalEntry!.status).toBe("end");
|
|
424
421
|
expect(finalEntry!.head).toBe(results[2].head);
|
|
425
422
|
});
|
|
426
423
|
|
|
427
|
-
test("6.
|
|
424
|
+
test("6. Liquid edge prompt renders planner variables into the worker step", {
|
|
428
425
|
timeout: 30_000,
|
|
429
426
|
}, async () => {
|
|
430
|
-
await writeMockConfig("e2e-
|
|
431
|
-
const workflowHash = await addWorkflow("e2e-
|
|
427
|
+
await writeMockConfig("e2e-liquid.mock.yaml");
|
|
428
|
+
const workflowHash = await addWorkflow("e2e-liquid.workflow.yaml", "test-liquid");
|
|
432
429
|
|
|
433
430
|
const start = await cmdThreadStart(uwfHome, workflowHash, "Plan the task", uwfHome, tmpDir);
|
|
434
431
|
const threadId = start.thread;
|
|
@@ -441,13 +438,13 @@ describe("E2E mock-agent: full uwf pipeline", () => {
|
|
|
441
438
|
// Step 2 → worker; the moderator renders the templated edge prompt before spawning it.
|
|
442
439
|
const step2 = execStep(threadId);
|
|
443
440
|
expect(step2.done).toBe(true);
|
|
444
|
-
expect(step2.status).toBe("
|
|
441
|
+
expect(step2.status).toBe("end");
|
|
445
442
|
|
|
446
443
|
const store = await openStore(casDir);
|
|
447
444
|
const plannerStep = getStepNode(store, step1.head);
|
|
448
445
|
expect(getStatus(store, plannerStep.output)).toBe("ready");
|
|
449
446
|
|
|
450
|
-
// The worker step's edgePrompt is the
|
|
447
|
+
// The worker step's edgePrompt is the Liquid-rendered template.
|
|
451
448
|
const workerStep = getStepNode(store, step2.head);
|
|
452
449
|
expect(workerStep.role).toBe("worker");
|
|
453
450
|
expect(workerStep.edgePrompt).toContain("fix/42-auth");
|
|
@@ -469,12 +466,12 @@ describe("E2E mock-agent: full uwf pipeline", () => {
|
|
|
469
466
|
// Step 1: planner outputs ready → $END → thread completed.
|
|
470
467
|
const step1 = execStep(threadId);
|
|
471
468
|
expect(step1.done).toBe(true);
|
|
472
|
-
expect(step1.status).toBe("
|
|
469
|
+
expect(step1.status).toBe("end");
|
|
473
470
|
|
|
474
471
|
const uwf1 = await createUwfStore(uwfHome);
|
|
475
472
|
const entry1 = getThread(uwf1.varStore, threadId);
|
|
476
473
|
expect(entry1).not.toBeNull();
|
|
477
|
-
expect(entry1!.status).toBe("
|
|
474
|
+
expect(entry1!.status).toBe("end");
|
|
478
475
|
|
|
479
476
|
// Resume the completed thread — should re-evaluate $START → planner.
|
|
480
477
|
const resumeResult = runResume(threadId, "Additional context for round 2");
|
|
@@ -484,7 +481,7 @@ describe("E2E mock-agent: full uwf pipeline", () => {
|
|
|
484
481
|
const uwf2 = await createUwfStore(uwfHome);
|
|
485
482
|
const entry2 = getThread(uwf2.varStore, threadId);
|
|
486
483
|
expect(entry2).not.toBeNull();
|
|
487
|
-
expect(entry2!.status).toBe("
|
|
484
|
+
expect(entry2!.status).toBe("end");
|
|
488
485
|
// Head should have advanced (not the same as step1).
|
|
489
486
|
expect(entry2!.head).not.toBe(step1.head);
|
|
490
487
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
version: 1
|
|
2
|
+
name: test-liquid
|
|
2
3
|
description: Planner emits template variables consumed by the worker edge prompt
|
|
3
4
|
roles:
|
|
4
5
|
planner:
|
|
@@ -30,6 +31,6 @@ graph:
|
|
|
30
31
|
new: { role: planner, prompt: 'Plan the task' }
|
|
31
32
|
resume: { role: planner, prompt: 'Review the previous run output and continue the work.' }
|
|
32
33
|
planner:
|
|
33
|
-
ready: { role: worker, prompt: 'Work on branch {{
|
|
34
|
+
ready: { role: worker, prompt: 'Work on branch {{ branch }} in {{ repoPath }}' }
|
|
34
35
|
worker:
|
|
35
36
|
done: { role: '$END', prompt: 'Complete' }
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
version: 1
|
|
1
2
|
name: test-suspend
|
|
2
3
|
description: Planner can suspend for more info or finish when ready
|
|
3
4
|
roles:
|
|
@@ -6,20 +7,15 @@ roles:
|
|
|
6
7
|
goal: Analyze the task
|
|
7
8
|
capabilities: []
|
|
8
9
|
procedure: Analyze the task and decide if more info is needed
|
|
9
|
-
output: Set $status to
|
|
10
|
+
output: Set $status to ready when done, or emit $status "$SUSPEND" (with reason) to pause for more info
|
|
10
11
|
frontmatter:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
required: [$status, reason]
|
|
16
|
-
- properties:
|
|
17
|
-
$status: { const: ready }
|
|
18
|
-
required: [$status]
|
|
12
|
+
type: object
|
|
13
|
+
required: [$status]
|
|
14
|
+
properties:
|
|
15
|
+
$status: { const: ready }
|
|
19
16
|
graph:
|
|
20
17
|
$START:
|
|
21
18
|
new: { role: planner, prompt: 'Analyze the task' }
|
|
22
19
|
resume: { role: planner, prompt: 'Review the previous run output and continue the work.' }
|
|
23
20
|
planner:
|
|
24
|
-
insufficient_info: { role: '$SUSPEND', prompt: 'Need more info: {{{reason}}}' }
|
|
25
21
|
ready: { role: '$END', prompt: 'Done' }
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { describe, expect, test } from "vitest";
|
|
4
|
+
|
|
5
|
+
const THREAD_TS_PATH = fileURLToPath(new URL("../commands/thread.ts", import.meta.url));
|
|
6
|
+
|
|
7
|
+
describe("issue #180 — _workflowRef ghost parameter cleanup", () => {
|
|
8
|
+
test("thread.ts no longer references the dead _workflowRef parameter", async () => {
|
|
9
|
+
const source = await readFile(THREAD_TS_PATH, "utf8");
|
|
10
|
+
expect(source).not.toContain("_workflowRef");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
test("resolveActiveThreadStatus is declared with exactly 4 parameters", async () => {
|
|
14
|
+
const source = await readFile(THREAD_TS_PATH, "utf8");
|
|
15
|
+
const declMatch = source.match(/async function resolveActiveThreadStatus\s*\(([\s\S]*?)\)\s*:/);
|
|
16
|
+
expect(declMatch).not.toBeNull();
|
|
17
|
+
const paramList = (declMatch as RegExpMatchArray)[1];
|
|
18
|
+
const params = paramList
|
|
19
|
+
.split(",")
|
|
20
|
+
.map((p) => p.trim())
|
|
21
|
+
.filter((p) => p.length > 0);
|
|
22
|
+
expect(params).toHaveLength(4);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("every call site of resolveActiveThreadStatus passes exactly 4 args", async () => {
|
|
26
|
+
const source = await readFile(THREAD_TS_PATH, "utf8");
|
|
27
|
+
// Capture call-site arg lists. Excludes the function declaration: it's
|
|
28
|
+
// preceded by `function ` rather than parens-following-name.
|
|
29
|
+
const callRe = /(?<!function\s)resolveActiveThreadStatus\s*\(([^)]*)\)/g;
|
|
30
|
+
const callSites: string[] = [];
|
|
31
|
+
for (const match of source.matchAll(callRe)) {
|
|
32
|
+
callSites.push(match[1]);
|
|
33
|
+
}
|
|
34
|
+
expect(callSites.length).toBe(3);
|
|
35
|
+
for (const args of callSites) {
|
|
36
|
+
const argCount = args
|
|
37
|
+
.split(",")
|
|
38
|
+
.map((a) => a.trim())
|
|
39
|
+
.filter((a) => a.length > 0).length;
|
|
40
|
+
expect(argCount).toBe(4);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -13,14 +13,14 @@ const solveIssueGraph: WorkflowPayload["graph"] = {
|
|
|
13
13
|
},
|
|
14
14
|
},
|
|
15
15
|
planner: {
|
|
16
|
-
planned: { role: "developer", prompt: "Implement the plan: {{plan}}", location: null },
|
|
16
|
+
planned: { role: "developer", prompt: "Implement the plan: {{ plan }}", location: null },
|
|
17
17
|
},
|
|
18
18
|
developer: {
|
|
19
|
-
implemented: { role: "reviewer", prompt: "Review the changes: {{summary}}", location: null },
|
|
19
|
+
implemented: { role: "reviewer", prompt: "Review the changes: {{ summary }}", location: null },
|
|
20
20
|
},
|
|
21
21
|
reviewer: {
|
|
22
22
|
approved: { role: "$END", prompt: "Done.", location: null },
|
|
23
|
-
rejected: { role: "developer", prompt: "Fix: {{comments}}", location: null },
|
|
23
|
+
rejected: { role: "developer", prompt: "Fix: {{ comments }}", location: null },
|
|
24
24
|
},
|
|
25
25
|
};
|
|
26
26
|
|
|
@@ -68,49 +68,6 @@ describe("evaluate", () => {
|
|
|
68
68
|
});
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
-
test("status-based routing (needs input → $SUSPEND)", () => {
|
|
72
|
-
const graph: Record<string, Record<string, Target>> = {
|
|
73
|
-
...solveIssueGraph,
|
|
74
|
-
reviewer: {
|
|
75
|
-
...solveIssueGraph.reviewer,
|
|
76
|
-
needs_input: { role: "$SUSPEND", prompt: "Waiting for user input.", location: null },
|
|
77
|
-
},
|
|
78
|
-
};
|
|
79
|
-
const result = evaluate(graph, "reviewer", { $status: "needs_input" });
|
|
80
|
-
expect(result).toEqual({
|
|
81
|
-
ok: true,
|
|
82
|
-
value: {
|
|
83
|
-
action: "suspend",
|
|
84
|
-
suspendedRole: "reviewer",
|
|
85
|
-
prompt: "Waiting for user input.",
|
|
86
|
-
},
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
test("$SUSPEND prompt template renders mustache variables", () => {
|
|
91
|
-
const graph: Record<string, Record<string, Target>> = {
|
|
92
|
-
reviewer: {
|
|
93
|
-
needs_input: {
|
|
94
|
-
role: "$SUSPEND",
|
|
95
|
-
prompt: "Please clarify: {{{question}}}",
|
|
96
|
-
location: null,
|
|
97
|
-
},
|
|
98
|
-
},
|
|
99
|
-
};
|
|
100
|
-
const result = evaluate(graph, "reviewer", {
|
|
101
|
-
$status: "needs_input",
|
|
102
|
-
question: "Which API endpoint?",
|
|
103
|
-
});
|
|
104
|
-
expect(result).toEqual({
|
|
105
|
-
ok: true,
|
|
106
|
-
value: {
|
|
107
|
-
action: "suspend",
|
|
108
|
-
suspendedRole: "reviewer",
|
|
109
|
-
prompt: "Please clarify: Which API endpoint?",
|
|
110
|
-
},
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
71
|
test("missing role in graph → error", () => {
|
|
115
72
|
const result = evaluate(solveIssueGraph, "unknown-role", { $status: "new" });
|
|
116
73
|
expect(result.ok).toBe(false);
|
|
@@ -127,7 +84,7 @@ describe("evaluate", () => {
|
|
|
127
84
|
}
|
|
128
85
|
});
|
|
129
86
|
|
|
130
|
-
test("
|
|
87
|
+
test("liquid template rendering with simple fields", () => {
|
|
131
88
|
const result = evaluate(solveIssueGraph, "planner", {
|
|
132
89
|
$status: "planned",
|
|
133
90
|
plan: "Add auth middleware",
|
|
@@ -142,7 +99,7 @@ describe("evaluate", () => {
|
|
|
142
99
|
});
|
|
143
100
|
});
|
|
144
101
|
|
|
145
|
-
test("
|
|
102
|
+
test("liquid does not HTML-escape prompt content", () => {
|
|
146
103
|
const result = evaluate(solveIssueGraph, "reviewer", {
|
|
147
104
|
$status: "rejected",
|
|
148
105
|
comments: 'use <T> & "Result<T, E>" types',
|
|
@@ -153,10 +110,10 @@ describe("evaluate", () => {
|
|
|
153
110
|
});
|
|
154
111
|
});
|
|
155
112
|
|
|
156
|
-
test("
|
|
113
|
+
test("liquid renders HTML content without escaping", () => {
|
|
157
114
|
const graph: Record<string, Record<string, Target>> = {
|
|
158
115
|
reviewer: {
|
|
159
|
-
rejected: { role: "developer", prompt: "Fix: {{
|
|
116
|
+
rejected: { role: "developer", prompt: "Fix: {{ comments }}", location: null },
|
|
160
117
|
},
|
|
161
118
|
};
|
|
162
119
|
const result = evaluate(graph, "reviewer", {
|
|
@@ -181,12 +138,12 @@ describe("evaluate", () => {
|
|
|
181
138
|
}
|
|
182
139
|
});
|
|
183
140
|
|
|
184
|
-
test("
|
|
141
|
+
test("liquid template with nested object paths", () => {
|
|
185
142
|
const graph: Record<string, Record<string, Target>> = {
|
|
186
143
|
reviewer: {
|
|
187
144
|
rejected: {
|
|
188
145
|
role: "developer",
|
|
189
|
-
prompt: "Address: {{review.comments}}",
|
|
146
|
+
prompt: "Address: {{ review.comments }}",
|
|
190
147
|
location: null,
|
|
191
148
|
},
|
|
192
149
|
},
|