@united-workforce/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +221 -0
- package/dist/__tests__/adapter-json-roundtrip.test.d.ts +2 -0
- package/dist/__tests__/adapter-json-roundtrip.test.d.ts.map +1 -0
- package/dist/__tests__/adapter-json-roundtrip.test.js +147 -0
- package/dist/__tests__/adapter-json-roundtrip.test.js.map +1 -0
- package/dist/__tests__/config.test.d.ts +2 -0
- package/dist/__tests__/config.test.d.ts.map +1 -0
- package/dist/__tests__/config.test.js +685 -0
- package/dist/__tests__/config.test.js.map +1 -0
- package/dist/__tests__/current-role.test.d.ts +2 -0
- package/dist/__tests__/current-role.test.d.ts.map +1 -0
- package/dist/__tests__/current-role.test.js +401 -0
- package/dist/__tests__/current-role.test.js.map +1 -0
- package/dist/__tests__/e2e-mock-agent.test.d.ts +2 -0
- package/dist/__tests__/e2e-mock-agent.test.d.ts.map +1 -0
- package/dist/__tests__/e2e-mock-agent.test.js +401 -0
- package/dist/__tests__/e2e-mock-agent.test.js.map +1 -0
- package/dist/__tests__/include-tag.test.d.ts +2 -0
- package/dist/__tests__/include-tag.test.d.ts.map +1 -0
- package/dist/__tests__/include-tag.test.js +69 -0
- package/dist/__tests__/include-tag.test.js.map +1 -0
- package/dist/__tests__/log.test.d.ts +2 -0
- package/dist/__tests__/log.test.d.ts.map +1 -0
- package/dist/__tests__/log.test.js +161 -0
- package/dist/__tests__/log.test.js.map +1 -0
- package/dist/__tests__/moderator-evaluate.test.d.ts +2 -0
- package/dist/__tests__/moderator-evaluate.test.d.ts.map +1 -0
- package/dist/__tests__/moderator-evaluate.test.js +170 -0
- package/dist/__tests__/moderator-evaluate.test.js.map +1 -0
- package/dist/__tests__/preload.d.ts +3 -0
- package/dist/__tests__/preload.d.ts.map +1 -0
- package/dist/__tests__/preload.js +6 -0
- package/dist/__tests__/preload.js.map +1 -0
- package/dist/__tests__/prompt.test.d.ts +2 -0
- package/dist/__tests__/prompt.test.d.ts.map +1 -0
- package/dist/__tests__/prompt.test.js +111 -0
- package/dist/__tests__/prompt.test.js.map +1 -0
- package/dist/__tests__/resolve-head-hash.test.d.ts +2 -0
- package/dist/__tests__/resolve-head-hash.test.d.ts.map +1 -0
- package/dist/__tests__/resolve-head-hash.test.js +66 -0
- package/dist/__tests__/resolve-head-hash.test.js.map +1 -0
- package/dist/__tests__/setup-agent-discovery.test.d.ts +2 -0
- package/dist/__tests__/setup-agent-discovery.test.d.ts.map +1 -0
- package/dist/__tests__/setup-agent-discovery.test.js +119 -0
- package/dist/__tests__/setup-agent-discovery.test.js.map +1 -0
- package/dist/__tests__/setup-complexity.test.d.ts +2 -0
- package/dist/__tests__/setup-complexity.test.d.ts.map +1 -0
- package/dist/__tests__/setup-complexity.test.js +314 -0
- package/dist/__tests__/setup-complexity.test.js.map +1 -0
- package/dist/__tests__/setup-validate.test.d.ts +2 -0
- package/dist/__tests__/setup-validate.test.d.ts.map +1 -0
- package/dist/__tests__/setup-validate.test.js +108 -0
- package/dist/__tests__/setup-validate.test.js.map +1 -0
- package/dist/__tests__/solve-issue-tea-worktree.test.d.ts +2 -0
- package/dist/__tests__/solve-issue-tea-worktree.test.d.ts.map +1 -0
- package/dist/__tests__/solve-issue-tea-worktree.test.js +107 -0
- package/dist/__tests__/solve-issue-tea-worktree.test.js.map +1 -0
- package/dist/__tests__/spawn-agent-json.test.d.ts +2 -0
- package/dist/__tests__/spawn-agent-json.test.d.ts.map +1 -0
- package/dist/__tests__/spawn-agent-json.test.js +79 -0
- package/dist/__tests__/spawn-agent-json.test.js.map +1 -0
- package/dist/__tests__/step-read.test.d.ts +2 -0
- package/dist/__tests__/step-read.test.d.ts.map +1 -0
- package/dist/__tests__/step-read.test.js +561 -0
- package/dist/__tests__/step-read.test.js.map +1 -0
- package/dist/__tests__/step-show-json.test.d.ts +2 -0
- package/dist/__tests__/step-show-json.test.d.ts.map +1 -0
- package/dist/__tests__/step-show-json.test.js +311 -0
- package/dist/__tests__/step-show-json.test.js.map +1 -0
- package/dist/__tests__/step-timing.test.d.ts +2 -0
- package/dist/__tests__/step-timing.test.d.ts.map +1 -0
- package/dist/__tests__/step-timing.test.js +345 -0
- package/dist/__tests__/step-timing.test.js.map +1 -0
- package/dist/__tests__/store-global-cas.test.d.ts +2 -0
- package/dist/__tests__/store-global-cas.test.d.ts.map +1 -0
- package/dist/__tests__/store-global-cas.test.js +235 -0
- package/dist/__tests__/store-global-cas.test.js.map +1 -0
- package/dist/__tests__/store-storage-root.test.d.ts +2 -0
- package/dist/__tests__/store-storage-root.test.d.ts.map +1 -0
- package/dist/__tests__/store-storage-root.test.js +43 -0
- package/dist/__tests__/store-storage-root.test.js.map +1 -0
- package/dist/__tests__/store-unified-threads.test.d.ts +2 -0
- package/dist/__tests__/store-unified-threads.test.d.ts.map +1 -0
- package/dist/__tests__/store-unified-threads.test.js +189 -0
- package/dist/__tests__/store-unified-threads.test.js.map +1 -0
- package/dist/__tests__/thread-cancel-status.test.d.ts +2 -0
- package/dist/__tests__/thread-cancel-status.test.d.ts.map +1 -0
- package/dist/__tests__/thread-cancel-status.test.js +111 -0
- package/dist/__tests__/thread-cancel-status.test.js.map +1 -0
- package/dist/__tests__/thread-list-filters.test.d.ts +2 -0
- package/dist/__tests__/thread-list-filters.test.d.ts.map +1 -0
- package/dist/__tests__/thread-list-filters.test.js +442 -0
- package/dist/__tests__/thread-list-filters.test.js.map +1 -0
- package/dist/__tests__/thread-location.test.d.ts +2 -0
- package/dist/__tests__/thread-location.test.d.ts.map +1 -0
- package/dist/__tests__/thread-location.test.js +159 -0
- package/dist/__tests__/thread-location.test.js.map +1 -0
- package/dist/__tests__/thread-read-quota.test.d.ts +2 -0
- package/dist/__tests__/thread-read-quota.test.d.ts.map +1 -0
- package/dist/__tests__/thread-read-quota.test.js +546 -0
- package/dist/__tests__/thread-read-quota.test.js.map +1 -0
- package/dist/__tests__/thread-read-xml-tags.test.d.ts +2 -0
- package/dist/__tests__/thread-read-xml-tags.test.d.ts.map +1 -0
- package/dist/__tests__/thread-read-xml-tags.test.js +610 -0
- package/dist/__tests__/thread-read-xml-tags.test.js.map +1 -0
- package/dist/__tests__/thread-resume.test.d.ts +2 -0
- package/dist/__tests__/thread-resume.test.d.ts.map +1 -0
- package/dist/__tests__/thread-resume.test.js +592 -0
- package/dist/__tests__/thread-resume.test.js.map +1 -0
- package/dist/__tests__/thread-show-status.test.d.ts +2 -0
- package/dist/__tests__/thread-show-status.test.d.ts.map +1 -0
- package/dist/__tests__/thread-show-status.test.js +267 -0
- package/dist/__tests__/thread-show-status.test.js.map +1 -0
- package/dist/__tests__/thread-start-cwd-cli.test.d.ts +2 -0
- package/dist/__tests__/thread-start-cwd-cli.test.d.ts.map +1 -0
- package/dist/__tests__/thread-start-cwd-cli.test.js +130 -0
- package/dist/__tests__/thread-start-cwd-cli.test.js.map +1 -0
- package/dist/__tests__/thread-step-count.test.d.ts +2 -0
- package/dist/__tests__/thread-step-count.test.d.ts.map +1 -0
- package/dist/__tests__/thread-step-count.test.js +55 -0
- package/dist/__tests__/thread-step-count.test.js.map +1 -0
- package/dist/__tests__/thread-suspend-step.test.d.ts +2 -0
- package/dist/__tests__/thread-suspend-step.test.d.ts.map +1 -0
- package/dist/__tests__/thread-suspend-step.test.js +155 -0
- package/dist/__tests__/thread-suspend-step.test.js.map +1 -0
- package/dist/__tests__/thread-suspended-display.test.d.ts +2 -0
- package/dist/__tests__/thread-suspended-display.test.d.ts.map +1 -0
- package/dist/__tests__/thread-suspended-display.test.js +247 -0
- package/dist/__tests__/thread-suspended-display.test.js.map +1 -0
- package/dist/__tests__/thread-test-helpers.d.ts +4 -0
- package/dist/__tests__/thread-test-helpers.d.ts.map +1 -0
- package/dist/__tests__/thread-test-helpers.js +23 -0
- package/dist/__tests__/thread-test-helpers.js.map +1 -0
- package/dist/__tests__/thread.test.d.ts +2 -0
- package/dist/__tests__/thread.test.d.ts.map +1 -0
- package/dist/__tests__/thread.test.js +883 -0
- package/dist/__tests__/thread.test.js.map +1 -0
- package/dist/__tests__/validate-semantic.test.d.ts +2 -0
- package/dist/__tests__/validate-semantic.test.d.ts.map +1 -0
- package/dist/__tests__/validate-semantic.test.js +408 -0
- package/dist/__tests__/validate-semantic.test.js.map +1 -0
- package/dist/__tests__/workflow-resolution.test.d.ts +2 -0
- package/dist/__tests__/workflow-resolution.test.d.ts.map +1 -0
- package/dist/__tests__/workflow-resolution.test.js +308 -0
- package/dist/__tests__/workflow-resolution.test.js.map +1 -0
- package/dist/background/background.d.ts +38 -0
- package/dist/background/background.d.ts.map +1 -0
- package/dist/background/background.js +123 -0
- package/dist/background/background.js.map +1 -0
- package/dist/background/index.d.ts +3 -0
- package/dist/background/index.d.ts.map +1 -0
- package/dist/background/index.js +2 -0
- package/dist/background/index.js.map +1 -0
- package/dist/background/types.d.ts +9 -0
- package/dist/background/types.d.ts.map +1 -0
- package/dist/background/types.js +2 -0
- package/dist/background/types.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +535 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/config.d.ts +41 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +252 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/log.d.ts +26 -0
- package/dist/commands/log.d.ts.map +1 -0
- package/dist/commands/log.js +79 -0
- package/dist/commands/log.js.map +1 -0
- package/dist/commands/prompt.d.ts +6 -0
- package/dist/commands/prompt.d.ts.map +1 -0
- package/dist/commands/prompt.js +67 -0
- package/dist/commands/prompt.js.map +1 -0
- package/dist/commands/setup.d.ts +73 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +522 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/shared.d.ts +31 -0
- package/dist/commands/shared.d.ts.map +1 -0
- package/dist/commands/shared.js +154 -0
- package/dist/commands/shared.js.map +1 -0
- package/dist/commands/step.d.ts +18 -0
- package/dist/commands/step.d.ts.map +1 -0
- package/dist/commands/step.js +257 -0
- package/dist/commands/step.js.map +1 -0
- package/dist/commands/thread-time-parser.d.ts +6 -0
- package/dist/commands/thread-time-parser.d.ts.map +1 -0
- package/dist/commands/thread-time-parser.js +22 -0
- package/dist/commands/thread-time-parser.js.map +1 -0
- package/dist/commands/thread.d.ts +38 -0
- package/dist/commands/thread.d.ts.map +1 -0
- package/dist/commands/thread.js +1087 -0
- package/dist/commands/thread.js.map +1 -0
- package/dist/commands/workflow.d.ts +24 -0
- package/dist/commands/workflow.d.ts.map +1 -0
- package/dist/commands/workflow.js +138 -0
- package/dist/commands/workflow.js.map +1 -0
- package/dist/format.d.ts +3 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +10 -0
- package/dist/format.js.map +1 -0
- package/dist/include.d.ts +12 -0
- package/dist/include.d.ts.map +1 -0
- package/dist/include.js +35 -0
- package/dist/include.js.map +1 -0
- package/dist/moderator/__tests__/evaluate.test.d.ts +2 -0
- package/dist/moderator/__tests__/evaluate.test.d.ts.map +1 -0
- package/dist/moderator/__tests__/evaluate.test.js +167 -0
- package/dist/moderator/__tests__/evaluate.test.js.map +1 -0
- package/dist/moderator/evaluate.d.ts +6 -0
- package/dist/moderator/evaluate.d.ts.map +1 -0
- package/dist/moderator/evaluate.js +65 -0
- package/dist/moderator/evaluate.js.map +1 -0
- package/dist/moderator/index.d.ts +4 -0
- package/dist/moderator/index.d.ts.map +1 -0
- package/dist/moderator/index.js +3 -0
- package/dist/moderator/index.js.map +1 -0
- package/dist/moderator/types.d.ts +25 -0
- package/dist/moderator/types.d.ts.map +1 -0
- package/dist/moderator/types.js +4 -0
- package/dist/moderator/types.js.map +1 -0
- package/dist/schemas.d.ts +16 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +17 -0
- package/dist/schemas.js.map +1 -0
- package/dist/store.d.ts +77 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +392 -0
- package/dist/store.js.map +1 -0
- package/dist/validate-semantic.d.ts +7 -0
- package/dist/validate-semantic.d.ts.map +1 -0
- package/dist/validate-semantic.js +263 -0
- package/dist/validate-semantic.js.map +1 -0
- package/dist/validate.d.ts +16 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +115 -0
- package/dist/validate.js.map +1 -0
- package/package.json +44 -0
- package/src/__tests__/adapter-json-roundtrip.test.ts +181 -0
- package/src/__tests__/config.test.ts +740 -0
- package/src/__tests__/current-role.test.ts +438 -0
- package/src/__tests__/e2e-mock-agent.test.ts +498 -0
- package/src/__tests__/fixtures/e2e-completed-resume.mock.yaml +15 -0
- package/src/__tests__/fixtures/e2e-count.mock.yaml +19 -0
- package/src/__tests__/fixtures/e2e-count.workflow.yaml +45 -0
- package/src/__tests__/fixtures/e2e-linear.mock.yaml +13 -0
- package/src/__tests__/fixtures/e2e-linear.workflow.yaml +32 -0
- package/src/__tests__/fixtures/e2e-loop.mock.yaml +25 -0
- package/src/__tests__/fixtures/e2e-loop.workflow.yaml +36 -0
- package/src/__tests__/fixtures/e2e-mismatch.mock.yaml +16 -0
- package/src/__tests__/fixtures/e2e-mustache.mock.yaml +15 -0
- package/src/__tests__/fixtures/e2e-mustache.workflow.yaml +34 -0
- package/src/__tests__/fixtures/e2e-suspend.mock.yaml +14 -0
- package/src/__tests__/fixtures/e2e-suspend.workflow.yaml +24 -0
- package/src/__tests__/include-tag.test.ts +84 -0
- package/src/__tests__/log.test.ts +181 -0
- package/src/__tests__/moderator-evaluate.test.ts +186 -0
- package/src/__tests__/preload.ts +7 -0
- package/src/__tests__/prompt.test.ts +129 -0
- package/src/__tests__/resolve-head-hash.test.ts +86 -0
- package/src/__tests__/setup-agent-discovery.test.ts +167 -0
- package/src/__tests__/setup-complexity.test.ts +381 -0
- package/src/__tests__/setup-validate.test.ts +148 -0
- package/src/__tests__/solve-issue-tea-worktree.test.ts +144 -0
- package/src/__tests__/spawn-agent-json.test.ts +100 -0
- package/src/__tests__/step-read.test.ts +632 -0
- package/src/__tests__/step-show-json.test.ts +373 -0
- package/src/__tests__/step-timing.test.ts +392 -0
- package/src/__tests__/store-global-cas.test.ts +308 -0
- package/src/__tests__/store-storage-root.test.ts +49 -0
- package/src/__tests__/store-unified-threads.test.ts +235 -0
- package/src/__tests__/thread-cancel-status.test.ts +138 -0
- package/src/__tests__/thread-list-filters.test.ts +572 -0
- package/src/__tests__/thread-location.test.ts +186 -0
- package/src/__tests__/thread-read-quota.test.ts +613 -0
- package/src/__tests__/thread-read-xml-tags.test.ts +717 -0
- package/src/__tests__/thread-resume.test.ts +710 -0
- package/src/__tests__/thread-show-status.test.ts +317 -0
- package/src/__tests__/thread-start-cwd-cli.test.ts +164 -0
- package/src/__tests__/thread-step-count.test.ts +70 -0
- package/src/__tests__/thread-suspend-step.test.ts +181 -0
- package/src/__tests__/thread-suspended-display.test.ts +287 -0
- package/src/__tests__/thread-test-helpers.ts +37 -0
- package/src/__tests__/thread.test.ts +1025 -0
- package/src/__tests__/validate-semantic.test.ts +474 -0
- package/src/__tests__/workflow-resolution.test.ts +421 -0
- package/src/background/background.ts +147 -0
- package/src/background/index.ts +11 -0
- package/src/background/types.ts +9 -0
- package/src/cli.ts +692 -0
- package/src/commands/config.ts +304 -0
- package/src/commands/log.ts +116 -0
- package/src/commands/prompt.ts +81 -0
- package/src/commands/setup.ts +603 -0
- package/src/commands/shared.ts +227 -0
- package/src/commands/step.ts +343 -0
- package/src/commands/thread-time-parser.ts +23 -0
- package/src/commands/thread.ts +1575 -0
- package/src/commands/workflow.ts +213 -0
- package/src/format.ts +12 -0
- package/src/include.ts +37 -0
- package/src/moderator/__tests__/evaluate.test.ts +199 -0
- package/src/moderator/evaluate.ts +80 -0
- package/src/moderator/index.ts +7 -0
- package/src/moderator/types.ts +24 -0
- package/src/schemas.ts +26 -0
- package/src/store.ts +479 -0
- package/src/validate-semantic.ts +304 -0
- package/src/validate.ts +137 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { putSchema } from "@ocas/core";
|
|
5
|
+
import { describe, expect, test } from "vitest";
|
|
6
|
+
import { createMarker, deleteMarker } from "../background/index.js";
|
|
7
|
+
import { cmdThreadShow, cmdThreadStart } from "../commands/thread.js";
|
|
8
|
+
import { completeThread, createUwfStore, loadAllThreads, setThread } from "../store.js";
|
|
9
|
+
const OUTPUT_SCHEMA = {
|
|
10
|
+
type: "object",
|
|
11
|
+
properties: {
|
|
12
|
+
$status: { type: "string" },
|
|
13
|
+
question: { type: "string" },
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
const TEST_WORKFLOW_YAML = `
|
|
17
|
+
name: test-status
|
|
18
|
+
description: Test workflow for status field
|
|
19
|
+
roles:
|
|
20
|
+
planner:
|
|
21
|
+
description: Plans the work
|
|
22
|
+
goal: Plan implementation
|
|
23
|
+
capabilities: ["planning"]
|
|
24
|
+
procedure: Plan
|
|
25
|
+
output: |
|
|
26
|
+
$status: "ready"
|
|
27
|
+
frontmatter:
|
|
28
|
+
type: object
|
|
29
|
+
required: ["$status"]
|
|
30
|
+
properties:
|
|
31
|
+
$status: { type: string, enum: ["ready"] }
|
|
32
|
+
graph:
|
|
33
|
+
$START:
|
|
34
|
+
_:
|
|
35
|
+
role: planner
|
|
36
|
+
prompt: "Plan the work"
|
|
37
|
+
location: null
|
|
38
|
+
planner:
|
|
39
|
+
ready:
|
|
40
|
+
role: $END
|
|
41
|
+
prompt: "Done"
|
|
42
|
+
location: null
|
|
43
|
+
`;
|
|
44
|
+
const SUSPEND_WORKFLOW_YAML = `
|
|
45
|
+
name: test-suspend-status
|
|
46
|
+
description: Test workflow for suspended status
|
|
47
|
+
roles:
|
|
48
|
+
worker:
|
|
49
|
+
description: Worker role
|
|
50
|
+
goal: Work
|
|
51
|
+
capabilities: ["coding"]
|
|
52
|
+
procedure: Work
|
|
53
|
+
output: |
|
|
54
|
+
$status: "needs_input"
|
|
55
|
+
question: "Which API?"
|
|
56
|
+
frontmatter:
|
|
57
|
+
oneOf:
|
|
58
|
+
- type: object
|
|
59
|
+
required: ["$status", "question"]
|
|
60
|
+
properties:
|
|
61
|
+
$status: { const: "needs_input" }
|
|
62
|
+
question: { type: string }
|
|
63
|
+
graph:
|
|
64
|
+
$START:
|
|
65
|
+
_:
|
|
66
|
+
role: worker
|
|
67
|
+
prompt: "Start work"
|
|
68
|
+
location: null
|
|
69
|
+
worker:
|
|
70
|
+
needs_input:
|
|
71
|
+
role: $SUSPEND
|
|
72
|
+
prompt: "Please clarify: {{{question}}}"
|
|
73
|
+
location: null
|
|
74
|
+
`;
|
|
75
|
+
async function insertStepNode(storageRoot, threadId, role, outputPayload) {
|
|
76
|
+
const uwf = await createUwfStore(storageRoot);
|
|
77
|
+
const index = loadAllThreads(uwf.varStore);
|
|
78
|
+
const headEntry = index[threadId];
|
|
79
|
+
if (headEntry === undefined)
|
|
80
|
+
throw new Error(`thread ${threadId} not in index`);
|
|
81
|
+
const head = headEntry.head;
|
|
82
|
+
const outputSchemaHash = await putSchema(uwf.store, OUTPUT_SCHEMA);
|
|
83
|
+
const outputHash = await uwf.store.cas.put(outputSchemaHash, outputPayload);
|
|
84
|
+
const detailHash = await uwf.store.cas.put(uwf.schemas.text, "detail-placeholder");
|
|
85
|
+
const headNode = uwf.store.cas.get(head);
|
|
86
|
+
if (headNode === null)
|
|
87
|
+
throw new Error(`head ${head} not found`);
|
|
88
|
+
const isStart = headNode.type === uwf.schemas.startNode;
|
|
89
|
+
const startHash = isStart ? head : headNode.payload.start;
|
|
90
|
+
const stepHash = (await uwf.store.cas.put(uwf.schemas.stepNode, {
|
|
91
|
+
start: startHash,
|
|
92
|
+
prev: isStart ? null : head,
|
|
93
|
+
role,
|
|
94
|
+
output: outputHash,
|
|
95
|
+
detail: detailHash,
|
|
96
|
+
agent: "uwf-test",
|
|
97
|
+
edgePrompt: "edge",
|
|
98
|
+
startedAtMs: Date.now(),
|
|
99
|
+
completedAtMs: Date.now() + 1,
|
|
100
|
+
cwd: "/tmp",
|
|
101
|
+
assembledPrompt: null,
|
|
102
|
+
}));
|
|
103
|
+
setThread(uwf.varStore, threadId, {
|
|
104
|
+
head: stepHash,
|
|
105
|
+
status: "idle",
|
|
106
|
+
suspendedRole: null,
|
|
107
|
+
suspendMessage: null,
|
|
108
|
+
completedAt: null,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
describe("thread show status field", () => {
|
|
112
|
+
let tmpDir;
|
|
113
|
+
let storageRoot;
|
|
114
|
+
async function setupTestEnv() {
|
|
115
|
+
tmpDir = join(tmpdir(), `uwf-test-status-${Date.now()}`);
|
|
116
|
+
storageRoot = join(tmpDir, "storage");
|
|
117
|
+
await mkdir(storageRoot, { recursive: true });
|
|
118
|
+
}
|
|
119
|
+
async function teardown() {
|
|
120
|
+
if (tmpDir) {
|
|
121
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
test("active idle thread shows status 'idle'", async () => {
|
|
125
|
+
await setupTestEnv();
|
|
126
|
+
const workflowPath = join(tmpDir, "test-status.yaml");
|
|
127
|
+
await writeFile(workflowPath, TEST_WORKFLOW_YAML, "utf8");
|
|
128
|
+
// Create a thread
|
|
129
|
+
const startResult = await cmdThreadStart(storageRoot, workflowPath, "test prompt", tmpDir);
|
|
130
|
+
const threadId = startResult.thread;
|
|
131
|
+
// Show the thread (should be idle)
|
|
132
|
+
const result = await cmdThreadShow(storageRoot, threadId);
|
|
133
|
+
expect(result.status).toBe("idle");
|
|
134
|
+
expect(result.done).toBe(false);
|
|
135
|
+
expect(result.background).toBe(null);
|
|
136
|
+
expect(result.thread).toBe(threadId);
|
|
137
|
+
await teardown();
|
|
138
|
+
});
|
|
139
|
+
test("active running thread shows status 'running'", async () => {
|
|
140
|
+
await setupTestEnv();
|
|
141
|
+
const workflowPath = join(tmpDir, "test-status.yaml");
|
|
142
|
+
await writeFile(workflowPath, TEST_WORKFLOW_YAML, "utf8");
|
|
143
|
+
// Create a thread
|
|
144
|
+
const startResult = await cmdThreadStart(storageRoot, workflowPath, "test prompt", tmpDir);
|
|
145
|
+
const threadId = startResult.thread;
|
|
146
|
+
const workflow = startResult.workflow;
|
|
147
|
+
// Create a running marker
|
|
148
|
+
await createMarker(storageRoot, {
|
|
149
|
+
thread: threadId,
|
|
150
|
+
workflow,
|
|
151
|
+
pid: process.pid,
|
|
152
|
+
startedAt: Date.now(),
|
|
153
|
+
});
|
|
154
|
+
try {
|
|
155
|
+
const result = await cmdThreadShow(storageRoot, threadId);
|
|
156
|
+
expect(result.status).toBe("running");
|
|
157
|
+
expect(result.done).toBe(false);
|
|
158
|
+
expect(result.background).toBe(null);
|
|
159
|
+
expect(result.thread).toBe(threadId);
|
|
160
|
+
}
|
|
161
|
+
finally {
|
|
162
|
+
// Cleanup: delete marker
|
|
163
|
+
await deleteMarker(storageRoot, threadId);
|
|
164
|
+
await teardown();
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
test("completed thread shows status 'completed'", async () => {
|
|
168
|
+
await setupTestEnv();
|
|
169
|
+
const workflowPath = join(tmpDir, "test-status.yaml");
|
|
170
|
+
await writeFile(workflowPath, TEST_WORKFLOW_YAML, "utf8");
|
|
171
|
+
// Create a thread
|
|
172
|
+
const startResult = await cmdThreadStart(storageRoot, workflowPath, "test prompt", tmpDir);
|
|
173
|
+
const threadId = startResult.thread;
|
|
174
|
+
const _workflow = startResult.workflow;
|
|
175
|
+
// Get the head hash before moving to history
|
|
176
|
+
const uwfForIndex = await createUwfStore(storageRoot);
|
|
177
|
+
const index = loadAllThreads(uwfForIndex.varStore);
|
|
178
|
+
const head = index[threadId].head;
|
|
179
|
+
if (!head)
|
|
180
|
+
throw new Error("Thread not found in index");
|
|
181
|
+
completeThread(uwfForIndex.varStore, threadId, "completed");
|
|
182
|
+
const result = await cmdThreadShow(storageRoot, threadId);
|
|
183
|
+
expect(result.status).toBe("completed");
|
|
184
|
+
expect(result.done).toBe(true);
|
|
185
|
+
expect(result.background).toBe(null);
|
|
186
|
+
expect(result.thread).toBe(threadId);
|
|
187
|
+
await teardown();
|
|
188
|
+
});
|
|
189
|
+
test("cancelled thread shows status 'cancelled'", async () => {
|
|
190
|
+
await setupTestEnv();
|
|
191
|
+
const workflowPath = join(tmpDir, "test-status.yaml");
|
|
192
|
+
await writeFile(workflowPath, TEST_WORKFLOW_YAML, "utf8");
|
|
193
|
+
// Create a thread
|
|
194
|
+
const startResult = await cmdThreadStart(storageRoot, workflowPath, "test prompt", tmpDir);
|
|
195
|
+
const threadId = startResult.thread;
|
|
196
|
+
const _workflow = startResult.workflow;
|
|
197
|
+
// Get the head hash before moving to history
|
|
198
|
+
const uwfForIndex = await createUwfStore(storageRoot);
|
|
199
|
+
const index = loadAllThreads(uwfForIndex.varStore);
|
|
200
|
+
const head = index[threadId].head;
|
|
201
|
+
if (!head)
|
|
202
|
+
throw new Error("Thread not found in index");
|
|
203
|
+
completeThread(uwfForIndex.varStore, threadId, "cancelled");
|
|
204
|
+
const result = await cmdThreadShow(storageRoot, threadId);
|
|
205
|
+
expect(result.status).toBe("cancelled");
|
|
206
|
+
expect(result.done).toBe(true);
|
|
207
|
+
expect(result.background).toBe(null);
|
|
208
|
+
expect(result.thread).toBe(threadId);
|
|
209
|
+
await teardown();
|
|
210
|
+
});
|
|
211
|
+
test("legacy completed thread without reason shows status 'completed'", async () => {
|
|
212
|
+
await setupTestEnv();
|
|
213
|
+
const workflowPath = join(tmpDir, "test-status.yaml");
|
|
214
|
+
await writeFile(workflowPath, TEST_WORKFLOW_YAML, "utf8");
|
|
215
|
+
// Create a thread
|
|
216
|
+
const startResult = await cmdThreadStart(storageRoot, workflowPath, "test prompt", tmpDir);
|
|
217
|
+
const threadId = startResult.thread;
|
|
218
|
+
const _workflow = startResult.workflow;
|
|
219
|
+
// Get the head hash before moving to history
|
|
220
|
+
const uwfForIndex = await createUwfStore(storageRoot);
|
|
221
|
+
const index = loadAllThreads(uwfForIndex.varStore);
|
|
222
|
+
const head = index[threadId].head;
|
|
223
|
+
if (!head)
|
|
224
|
+
throw new Error("Thread not found in index");
|
|
225
|
+
completeThread(uwfForIndex.varStore, threadId, "completed");
|
|
226
|
+
const result = await cmdThreadShow(storageRoot, threadId);
|
|
227
|
+
expect(result.status).toBe("completed");
|
|
228
|
+
expect(result.done).toBe(true);
|
|
229
|
+
expect(result.background).toBe(null);
|
|
230
|
+
await teardown();
|
|
231
|
+
});
|
|
232
|
+
test("active suspended thread shows status 'suspended'", async () => {
|
|
233
|
+
await setupTestEnv();
|
|
234
|
+
const casDir = join(tmpDir, "cas");
|
|
235
|
+
await mkdir(casDir, { recursive: true });
|
|
236
|
+
const originalCasDir = process.env.OCAS_HOME;
|
|
237
|
+
process.env.OCAS_HOME = casDir;
|
|
238
|
+
try {
|
|
239
|
+
const workflowPath = join(tmpDir, "test-suspend-status.yaml");
|
|
240
|
+
await writeFile(workflowPath, SUSPEND_WORKFLOW_YAML, "utf8");
|
|
241
|
+
const startResult = await cmdThreadStart(storageRoot, workflowPath, "test prompt", tmpDir);
|
|
242
|
+
const threadId = startResult.thread;
|
|
243
|
+
await insertStepNode(storageRoot, threadId, "worker", {
|
|
244
|
+
$status: "needs_input",
|
|
245
|
+
question: "Which API?",
|
|
246
|
+
});
|
|
247
|
+
const result = await cmdThreadShow(storageRoot, threadId);
|
|
248
|
+
expect(result.status).toBe("suspended");
|
|
249
|
+
expect(result.done).toBe(false);
|
|
250
|
+
expect(result.currentRole).toBe(null);
|
|
251
|
+
expect(result.suspendedRole).toBe("worker");
|
|
252
|
+
expect(result.suspendMessage).toBe("Please clarify: Which API?");
|
|
253
|
+
expect(result.background).toBe(null);
|
|
254
|
+
expect(result.thread).toBe(threadId);
|
|
255
|
+
}
|
|
256
|
+
finally {
|
|
257
|
+
if (originalCasDir === undefined) {
|
|
258
|
+
delete process.env.OCAS_HOME;
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
process.env.OCAS_HOME = originalCasDir;
|
|
262
|
+
}
|
|
263
|
+
await teardown();
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
//# sourceMappingURL=thread-show-status.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thread-show-status.test.js","sourceRoot":"","sources":["../../src/__tests__/thread-show-status.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thread-start-cwd-cli.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/thread-start-cwd-cli.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { mkdir, 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 { describe, expect, test } from "vitest";
|
|
7
|
+
import { cmdThreadStart } from "../commands/thread.js";
|
|
8
|
+
import { createUwfStore, getThread } from "../store.js";
|
|
9
|
+
describe("thread start --cwd CLI option", () => {
|
|
10
|
+
let tmpDir;
|
|
11
|
+
let storageRoot;
|
|
12
|
+
let casDir;
|
|
13
|
+
let originalEnv;
|
|
14
|
+
async function setupTestEnv() {
|
|
15
|
+
tmpDir = join(tmpdir(), `uwf-test-cwd-cli-${Date.now()}`);
|
|
16
|
+
storageRoot = join(tmpDir, "storage");
|
|
17
|
+
casDir = join(tmpDir, "cas");
|
|
18
|
+
await mkdir(storageRoot, { recursive: true });
|
|
19
|
+
await mkdir(casDir, { recursive: true });
|
|
20
|
+
// Set OCAS_HOME for this test
|
|
21
|
+
originalEnv = process.env.OCAS_HOME;
|
|
22
|
+
process.env.OCAS_HOME = casDir;
|
|
23
|
+
}
|
|
24
|
+
async function teardown() {
|
|
25
|
+
if (tmpDir) {
|
|
26
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
27
|
+
}
|
|
28
|
+
// Restore original environment
|
|
29
|
+
if (originalEnv === undefined) {
|
|
30
|
+
delete process.env.OCAS_HOME;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
process.env.OCAS_HOME = originalEnv;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function createTestWorkflow() {
|
|
37
|
+
const workflowYaml = `
|
|
38
|
+
name: test-cwd-cli
|
|
39
|
+
description: Test workflow for CLI cwd option
|
|
40
|
+
roles:
|
|
41
|
+
planner:
|
|
42
|
+
description: Plans the work
|
|
43
|
+
goal: Plan implementation
|
|
44
|
+
capabilities: ["planning"]
|
|
45
|
+
procedure: Plan
|
|
46
|
+
output: |
|
|
47
|
+
$status: "ready"
|
|
48
|
+
frontmatter:
|
|
49
|
+
type: object
|
|
50
|
+
required: ["$status"]
|
|
51
|
+
properties:
|
|
52
|
+
$status: { type: string, enum: ["ready"] }
|
|
53
|
+
graph:
|
|
54
|
+
$START:
|
|
55
|
+
_:
|
|
56
|
+
role: planner
|
|
57
|
+
prompt: "Plan the work"
|
|
58
|
+
location: null
|
|
59
|
+
planner:
|
|
60
|
+
ready:
|
|
61
|
+
role: $END
|
|
62
|
+
prompt: "Done"
|
|
63
|
+
location: null
|
|
64
|
+
`;
|
|
65
|
+
const workflowPath = join(tmpDir, "test-cwd-cli.yaml");
|
|
66
|
+
await writeFile(workflowPath, workflowYaml, "utf8");
|
|
67
|
+
return workflowPath;
|
|
68
|
+
}
|
|
69
|
+
async function getStartNodeCwd(threadId) {
|
|
70
|
+
const uwf = await createUwfStore(storageRoot);
|
|
71
|
+
const entry = getThread(uwf.varStore, threadId);
|
|
72
|
+
const headHash = entry.head;
|
|
73
|
+
expect(headHash).toBeDefined();
|
|
74
|
+
const startNode = uwf.store.cas.get(headHash);
|
|
75
|
+
expect(startNode).not.toBe(null);
|
|
76
|
+
expect(startNode?.type).toBe(uwf.schemas.startNode);
|
|
77
|
+
const startPayload = startNode?.payload;
|
|
78
|
+
return startPayload.cwd;
|
|
79
|
+
}
|
|
80
|
+
test("thread start with custom cwd via cmdThreadStart", async () => {
|
|
81
|
+
await setupTestEnv();
|
|
82
|
+
const workflowPath = await createTestWorkflow();
|
|
83
|
+
const testCwd = "/test/custom/path";
|
|
84
|
+
const result = await cmdThreadStart(storageRoot, workflowPath, "test prompt", tmpDir, testCwd);
|
|
85
|
+
expect(result.thread).toBeDefined();
|
|
86
|
+
const actualCwd = await getStartNodeCwd(result.thread);
|
|
87
|
+
expect(actualCwd).toBe(testCwd);
|
|
88
|
+
await teardown();
|
|
89
|
+
});
|
|
90
|
+
test("thread start without cwd defaults to process.cwd()", async () => {
|
|
91
|
+
await setupTestEnv();
|
|
92
|
+
const workflowPath = await createTestWorkflow();
|
|
93
|
+
// Call without cwd parameter (it defaults to process.cwd())
|
|
94
|
+
const result = await cmdThreadStart(storageRoot, workflowPath, "test prompt", tmpDir);
|
|
95
|
+
expect(result.thread).toBeDefined();
|
|
96
|
+
const actualCwd = await getStartNodeCwd(result.thread);
|
|
97
|
+
expect(actualCwd).toBe(process.cwd());
|
|
98
|
+
await teardown();
|
|
99
|
+
});
|
|
100
|
+
test("thread start with relative path fails", async () => {
|
|
101
|
+
await setupTestEnv();
|
|
102
|
+
const workflowPath = await createTestWorkflow();
|
|
103
|
+
await expect(cmdThreadStart(storageRoot, workflowPath, "test", tmpDir, "relative/path")).rejects.toThrow();
|
|
104
|
+
await teardown();
|
|
105
|
+
});
|
|
106
|
+
test("CLI accepts --cwd option without error", async () => {
|
|
107
|
+
await setupTestEnv();
|
|
108
|
+
const workflowPath = await createTestWorkflow();
|
|
109
|
+
const testCwd = "/test/cli/path";
|
|
110
|
+
const pkgRoot = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
|
|
111
|
+
const uwfBin = join(pkgRoot, "dist", "cli.js");
|
|
112
|
+
// Register the workflow
|
|
113
|
+
execFileSync(process.execPath, [uwfBin, "workflow", "add", workflowPath], {
|
|
114
|
+
env: { ...process.env, UWF_HOME: storageRoot, OCAS_HOME: casDir },
|
|
115
|
+
encoding: "utf8",
|
|
116
|
+
});
|
|
117
|
+
// Verify CLI accepts --cwd option (no error thrown)
|
|
118
|
+
const output = execFileSync(process.execPath, [uwfBin, "thread", "start", "test-cwd-cli", "-p", "test prompt", "--cwd", testCwd], {
|
|
119
|
+
env: { ...process.env, UWF_HOME: storageRoot, OCAS_HOME: casDir },
|
|
120
|
+
encoding: "utf8",
|
|
121
|
+
});
|
|
122
|
+
const result = JSON.parse(output);
|
|
123
|
+
expect(result.thread).toBeDefined();
|
|
124
|
+
expect(result.workflow).toBeDefined();
|
|
125
|
+
// The fact that we got here without throwing means CLI accepted the --cwd option
|
|
126
|
+
// The actual cwd functionality is tested by the other tests using cmdThreadStart directly
|
|
127
|
+
await teardown();
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
//# sourceMappingURL=thread-start-cwd-cli.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thread-start-cwd-cli.test.js","sourceRoot":"","sources":["../../src/__tests__/thread-start-cwd-cli.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thread-step-count.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/thread-step-count.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { describe, expect, test } from "vitest";
|
|
5
|
+
import { validateCount } from "../commands/thread.js";
|
|
6
|
+
const CLI_PATH = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "dist", "cli.js");
|
|
7
|
+
function runCli(args) {
|
|
8
|
+
try {
|
|
9
|
+
const stdout = execFileSync("node", [CLI_PATH, ...args], {
|
|
10
|
+
encoding: "utf8",
|
|
11
|
+
env: { ...process.env, UWF_HOME: "/tmp/uwf-test-nonexistent" },
|
|
12
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
13
|
+
});
|
|
14
|
+
return { stdout, stderr: "", exitCode: 0 };
|
|
15
|
+
}
|
|
16
|
+
catch (e) {
|
|
17
|
+
const err = e;
|
|
18
|
+
return {
|
|
19
|
+
stdout: err.stdout ?? "",
|
|
20
|
+
stderr: err.stderr ?? "",
|
|
21
|
+
exitCode: err.status ?? 1,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
describe("thread exec --count CLI parsing", { timeout: 30_000 }, () => {
|
|
26
|
+
test("--help shows -c/--count option", () => {
|
|
27
|
+
const result = runCli(["thread", "exec", "--help"]);
|
|
28
|
+
const combined = result.stdout + result.stderr;
|
|
29
|
+
expect(combined).toContain("--count");
|
|
30
|
+
expect(combined).toContain("-c");
|
|
31
|
+
});
|
|
32
|
+
test("description says 'one or more steps'", () => {
|
|
33
|
+
const result = runCli(["thread", "exec", "--help"]);
|
|
34
|
+
const combined = result.stdout + result.stderr;
|
|
35
|
+
expect(combined).toContain("one or more steps");
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
describe("validateCount", () => {
|
|
39
|
+
test("count=0 throws validation error", () => {
|
|
40
|
+
expect(() => validateCount(0)).toThrow("positive integer");
|
|
41
|
+
});
|
|
42
|
+
test("negative count throws validation error", () => {
|
|
43
|
+
expect(() => validateCount(-1)).toThrow("positive integer");
|
|
44
|
+
});
|
|
45
|
+
test("non-integer count throws validation error", () => {
|
|
46
|
+
expect(() => validateCount(1.5)).toThrow("positive integer");
|
|
47
|
+
});
|
|
48
|
+
test("count=1 passes validation", () => {
|
|
49
|
+
expect(() => validateCount(1)).not.toThrow();
|
|
50
|
+
});
|
|
51
|
+
test("count=3 passes validation", () => {
|
|
52
|
+
expect(() => validateCount(3)).not.toThrow();
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
//# sourceMappingURL=thread-step-count.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thread-step-count.test.js","sourceRoot":"","sources":["../../src/__tests__/thread-step-count.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thread-suspend-step.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/thread-suspend-step.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,155 @@
|
|
|
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 { cmdThreadShow } from "../commands/thread.js";
|
|
10
|
+
import { registerUwfSchemas } from "../schemas.js";
|
|
11
|
+
import { seedThreads } from "./thread-test-helpers.js";
|
|
12
|
+
const OUTPUT_SCHEMA = {
|
|
13
|
+
type: "object",
|
|
14
|
+
properties: {
|
|
15
|
+
$status: { type: "string" },
|
|
16
|
+
question: { type: "string" },
|
|
17
|
+
},
|
|
18
|
+
required: ["$status"],
|
|
19
|
+
additionalProperties: false,
|
|
20
|
+
};
|
|
21
|
+
let tmpDir;
|
|
22
|
+
beforeEach(async () => {
|
|
23
|
+
tmpDir = await mkdtemp(join(tmpdir(), "cli-uwf-suspend-step-test-"));
|
|
24
|
+
});
|
|
25
|
+
afterEach(async () => {
|
|
26
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
27
|
+
});
|
|
28
|
+
describe("suspend step CAS chain and threads.yaml metadata", () => {
|
|
29
|
+
test("thread exec records suspend step in CAS and suspend metadata in threads.yaml", async () => {
|
|
30
|
+
const casDir = join(tmpDir, "cas");
|
|
31
|
+
await mkdir(casDir, { recursive: true });
|
|
32
|
+
const originalCasDir = process.env.OCAS_HOME;
|
|
33
|
+
process.env.OCAS_HOME = casDir;
|
|
34
|
+
try {
|
|
35
|
+
const store = await openStore(casDir);
|
|
36
|
+
const schemas = await registerUwfSchemas(store);
|
|
37
|
+
const outputSchemaHash = await putSchema(store, OUTPUT_SCHEMA);
|
|
38
|
+
const workflowHash = await store.cas.put(schemas.workflow, {
|
|
39
|
+
name: "test-suspend-step",
|
|
40
|
+
description: "suspend step integration test",
|
|
41
|
+
roles: {
|
|
42
|
+
worker: {
|
|
43
|
+
description: "Worker role",
|
|
44
|
+
goal: "Work",
|
|
45
|
+
capabilities: [],
|
|
46
|
+
procedure: "work",
|
|
47
|
+
output: "result",
|
|
48
|
+
frontmatter: outputSchemaHash,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
graph: {
|
|
52
|
+
$START: { _: { role: "worker", prompt: "Start work", location: null } },
|
|
53
|
+
worker: {
|
|
54
|
+
needs_input: {
|
|
55
|
+
role: "$SUSPEND",
|
|
56
|
+
prompt: "Please clarify: {{{question}}}",
|
|
57
|
+
location: null,
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
const startHash = await store.cas.put(schemas.startNode, {
|
|
63
|
+
workflow: workflowHash,
|
|
64
|
+
prompt: "Test suspend task",
|
|
65
|
+
cwd: tmpDir,
|
|
66
|
+
});
|
|
67
|
+
const threadId = "01SUSPENDSTEPTEST0000000";
|
|
68
|
+
await seedThreads(tmpDir, { [threadId]: startHash });
|
|
69
|
+
const outputHash = await store.cas.put(outputSchemaHash, {
|
|
70
|
+
$status: "needs_input",
|
|
71
|
+
question: "Which API?",
|
|
72
|
+
});
|
|
73
|
+
const detailHash = await store.cas.put(schemas.text, "mock detail");
|
|
74
|
+
const startedAtMs = 1716600000000;
|
|
75
|
+
const completedAtMs = 1716600001500;
|
|
76
|
+
const stepHash = await store.cas.put(schemas.stepNode, {
|
|
77
|
+
start: startHash,
|
|
78
|
+
prev: null,
|
|
79
|
+
role: "worker",
|
|
80
|
+
output: outputHash,
|
|
81
|
+
detail: detailHash,
|
|
82
|
+
agent: "uwf-mock",
|
|
83
|
+
edgePrompt: "Start work",
|
|
84
|
+
startedAtMs,
|
|
85
|
+
completedAtMs,
|
|
86
|
+
cwd: tmpDir,
|
|
87
|
+
assembledPrompt: null,
|
|
88
|
+
});
|
|
89
|
+
const mockAgentPath = join(tmpDir, "mock-agent.sh");
|
|
90
|
+
const adapterJson = JSON.stringify({
|
|
91
|
+
stepHash,
|
|
92
|
+
detailHash,
|
|
93
|
+
role: "worker",
|
|
94
|
+
frontmatter: { $status: "needs_input", question: "Which API?" },
|
|
95
|
+
body: "",
|
|
96
|
+
startedAtMs,
|
|
97
|
+
completedAtMs,
|
|
98
|
+
});
|
|
99
|
+
await writeFile(mockAgentPath, `#!/bin/sh\necho '${adapterJson}'\n`, { mode: 0o755 });
|
|
100
|
+
const configPath = join(tmpDir, "config.yaml");
|
|
101
|
+
await writeFile(configPath, `defaultAgent: uwf-hermes\ndefaultModel: test-model\nagentOverrides: null\nagents: {}\nproviders: {}\nmodels: {}\n`);
|
|
102
|
+
const cliPath = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "dist", "cli.js");
|
|
103
|
+
const stdout = execFileSync(process.execPath, [cliPath, "thread", "exec", threadId, "--agent", mockAgentPath], {
|
|
104
|
+
encoding: "utf8",
|
|
105
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
106
|
+
env: {
|
|
107
|
+
...process.env,
|
|
108
|
+
UWF_HOME: tmpDir,
|
|
109
|
+
OCAS_HOME: casDir,
|
|
110
|
+
},
|
|
111
|
+
cwd: tmpDir,
|
|
112
|
+
timeout: 30000,
|
|
113
|
+
});
|
|
114
|
+
const cliOutput = JSON.parse(stdout.trim());
|
|
115
|
+
expect(cliOutput.status).toBe("suspended");
|
|
116
|
+
expect(cliOutput.head).toBe(stepHash);
|
|
117
|
+
expect(cliOutput.suspendedRole).toBe("worker");
|
|
118
|
+
expect(cliOutput.suspendMessage).toBe("Please clarify: Which API?");
|
|
119
|
+
const storeAfter = await openStore(casDir);
|
|
120
|
+
const stepNode = storeAfter.cas.get(cliOutput.head);
|
|
121
|
+
expect(stepNode).not.toBeNull();
|
|
122
|
+
const payload = stepNode.payload;
|
|
123
|
+
expect(payload.role).toBe("worker");
|
|
124
|
+
expect(payload.output).toBe(outputHash);
|
|
125
|
+
const outputNode = storeAfter.cas.get(outputHash);
|
|
126
|
+
expect(outputNode?.payload).toEqual({
|
|
127
|
+
$status: "needs_input",
|
|
128
|
+
question: "Which API?",
|
|
129
|
+
});
|
|
130
|
+
const { createUwfStore, getThread } = await import("../store.js");
|
|
131
|
+
const uwf = await createUwfStore(tmpDir);
|
|
132
|
+
const threadEntry = getThread(uwf.varStore, threadId);
|
|
133
|
+
expect(threadEntry).toEqual({
|
|
134
|
+
head: stepHash,
|
|
135
|
+
status: "suspended",
|
|
136
|
+
suspendedRole: "worker",
|
|
137
|
+
suspendMessage: "Please clarify: Which API?",
|
|
138
|
+
completedAt: null,
|
|
139
|
+
});
|
|
140
|
+
const showResult = await cmdThreadShow(tmpDir, threadId);
|
|
141
|
+
expect(showResult.status).toBe("suspended");
|
|
142
|
+
expect(showResult.suspendMessage).toBe("Please clarify: Which API?");
|
|
143
|
+
expect(showResult.suspendedRole).toBe("worker");
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
if (originalCasDir === undefined) {
|
|
147
|
+
delete process.env.OCAS_HOME;
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
process.env.OCAS_HOME = originalCasDir;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
//# sourceMappingURL=thread-suspend-step.test.js.map
|