@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,438 @@
|
|
|
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 type { CasRef, ThreadId } from "@united-workforce/protocol";
|
|
6
|
+
import { describe, expect, test } from "vitest";
|
|
7
|
+
import { createMarker, deleteMarker } from "../background/index.js";
|
|
8
|
+
import { cmdThreadList, cmdThreadShow, cmdThreadStart } from "../commands/thread.js";
|
|
9
|
+
import { completeThread, createUwfStore, loadActiveThreads, setThread } from "../store.js";
|
|
10
|
+
|
|
11
|
+
const OUTPUT_SCHEMA = {
|
|
12
|
+
type: "object" as const,
|
|
13
|
+
properties: {
|
|
14
|
+
$status: { type: "string" as const },
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const SIMPLE_WORKFLOW_YAML = `
|
|
19
|
+
name: test-current-role
|
|
20
|
+
description: Test workflow for currentRole
|
|
21
|
+
roles:
|
|
22
|
+
roleA:
|
|
23
|
+
description: First role
|
|
24
|
+
goal: Do A
|
|
25
|
+
capabilities: ["coding"]
|
|
26
|
+
procedure: Do A
|
|
27
|
+
output: |
|
|
28
|
+
$status: "ready"
|
|
29
|
+
frontmatter:
|
|
30
|
+
type: object
|
|
31
|
+
required: ["$status"]
|
|
32
|
+
properties:
|
|
33
|
+
$status: { type: string, enum: ["ready", "not-ready"] }
|
|
34
|
+
roleB:
|
|
35
|
+
description: Second role
|
|
36
|
+
goal: Do B
|
|
37
|
+
capabilities: ["coding"]
|
|
38
|
+
procedure: Do B
|
|
39
|
+
output: |
|
|
40
|
+
$status: "done"
|
|
41
|
+
frontmatter:
|
|
42
|
+
type: object
|
|
43
|
+
required: ["$status"]
|
|
44
|
+
properties:
|
|
45
|
+
$status: { type: string, enum: ["done"] }
|
|
46
|
+
graph:
|
|
47
|
+
$START:
|
|
48
|
+
_:
|
|
49
|
+
role: roleA
|
|
50
|
+
prompt: "Do A"
|
|
51
|
+
location: null
|
|
52
|
+
roleA:
|
|
53
|
+
ready:
|
|
54
|
+
role: roleB
|
|
55
|
+
prompt: "Do B"
|
|
56
|
+
location: null
|
|
57
|
+
not-ready:
|
|
58
|
+
role: roleA
|
|
59
|
+
prompt: "Try again"
|
|
60
|
+
location: null
|
|
61
|
+
roleB:
|
|
62
|
+
done:
|
|
63
|
+
role: $END
|
|
64
|
+
prompt: "Done"
|
|
65
|
+
location: null
|
|
66
|
+
`;
|
|
67
|
+
|
|
68
|
+
const CONDITIONAL_WORKFLOW_YAML = `
|
|
69
|
+
name: test-conditional-role
|
|
70
|
+
description: Conditional routing workflow
|
|
71
|
+
roles:
|
|
72
|
+
roleA:
|
|
73
|
+
description: First role
|
|
74
|
+
goal: Do A
|
|
75
|
+
capabilities: ["coding"]
|
|
76
|
+
procedure: Do A
|
|
77
|
+
output: |
|
|
78
|
+
$status: "pass"
|
|
79
|
+
frontmatter:
|
|
80
|
+
type: object
|
|
81
|
+
required: ["$status"]
|
|
82
|
+
properties:
|
|
83
|
+
$status: { type: string, enum: ["pass", "fail"] }
|
|
84
|
+
roleB:
|
|
85
|
+
description: Pass role
|
|
86
|
+
goal: Do B
|
|
87
|
+
capabilities: ["coding"]
|
|
88
|
+
procedure: Do B
|
|
89
|
+
output: |
|
|
90
|
+
$status: "done"
|
|
91
|
+
frontmatter:
|
|
92
|
+
type: object
|
|
93
|
+
required: ["$status"]
|
|
94
|
+
properties:
|
|
95
|
+
$status: { type: string, enum: ["done"] }
|
|
96
|
+
roleC:
|
|
97
|
+
description: Fail role
|
|
98
|
+
goal: Do C
|
|
99
|
+
capabilities: ["coding"]
|
|
100
|
+
procedure: Do C
|
|
101
|
+
output: |
|
|
102
|
+
$status: "done"
|
|
103
|
+
frontmatter:
|
|
104
|
+
type: object
|
|
105
|
+
required: ["$status"]
|
|
106
|
+
properties:
|
|
107
|
+
$status: { type: string, enum: ["done"] }
|
|
108
|
+
graph:
|
|
109
|
+
$START:
|
|
110
|
+
_:
|
|
111
|
+
role: roleA
|
|
112
|
+
prompt: "Do A"
|
|
113
|
+
location: null
|
|
114
|
+
roleA:
|
|
115
|
+
pass:
|
|
116
|
+
role: roleB
|
|
117
|
+
prompt: "Do B (pass)"
|
|
118
|
+
location: null
|
|
119
|
+
fail:
|
|
120
|
+
role: roleC
|
|
121
|
+
prompt: "Do C (fail)"
|
|
122
|
+
location: null
|
|
123
|
+
roleB:
|
|
124
|
+
done:
|
|
125
|
+
role: $END
|
|
126
|
+
prompt: "Done"
|
|
127
|
+
location: null
|
|
128
|
+
roleC:
|
|
129
|
+
done:
|
|
130
|
+
role: $END
|
|
131
|
+
prompt: "Done"
|
|
132
|
+
location: null
|
|
133
|
+
`;
|
|
134
|
+
|
|
135
|
+
const SINGLE_ROLE_WORKFLOW_YAML = `
|
|
136
|
+
name: test-single-role
|
|
137
|
+
description: Single role that goes to END
|
|
138
|
+
roles:
|
|
139
|
+
worker:
|
|
140
|
+
description: Worker
|
|
141
|
+
goal: Work
|
|
142
|
+
capabilities: ["coding"]
|
|
143
|
+
procedure: Work
|
|
144
|
+
output: |
|
|
145
|
+
$status: "done"
|
|
146
|
+
frontmatter:
|
|
147
|
+
type: object
|
|
148
|
+
required: ["$status"]
|
|
149
|
+
properties:
|
|
150
|
+
$status: { type: string, enum: ["done"] }
|
|
151
|
+
graph:
|
|
152
|
+
$START:
|
|
153
|
+
_:
|
|
154
|
+
role: worker
|
|
155
|
+
prompt: "Work"
|
|
156
|
+
location: null
|
|
157
|
+
worker:
|
|
158
|
+
done:
|
|
159
|
+
role: $END
|
|
160
|
+
prompt: "Done"
|
|
161
|
+
location: null
|
|
162
|
+
`;
|
|
163
|
+
|
|
164
|
+
/** Helper: insert a completed step node after the current head. */
|
|
165
|
+
async function insertStepNode(
|
|
166
|
+
storageRoot: string,
|
|
167
|
+
threadId: ThreadId,
|
|
168
|
+
role: string,
|
|
169
|
+
outputPayload: Record<string, unknown>,
|
|
170
|
+
): Promise<void> {
|
|
171
|
+
const uwf = await createUwfStore(storageRoot);
|
|
172
|
+
const index = loadActiveThreads(uwf.varStore);
|
|
173
|
+
const headEntry = index[threadId];
|
|
174
|
+
if (headEntry === undefined) throw new Error(`thread ${threadId} not in index`);
|
|
175
|
+
const head = headEntry.head;
|
|
176
|
+
|
|
177
|
+
const outputSchemaHash = await putSchema(uwf.store, OUTPUT_SCHEMA);
|
|
178
|
+
const outputHash = await uwf.store.cas.put(outputSchemaHash, outputPayload);
|
|
179
|
+
|
|
180
|
+
// Use text schema for detail (simple placeholder)
|
|
181
|
+
const detailHash = await uwf.store.cas.put(uwf.schemas.text, "detail-placeholder");
|
|
182
|
+
|
|
183
|
+
// Resolve start hash from head
|
|
184
|
+
const headNode = uwf.store.cas.get(head);
|
|
185
|
+
if (headNode === null) throw new Error(`head ${head} not found`);
|
|
186
|
+
const isStart = headNode.type === uwf.schemas.startNode;
|
|
187
|
+
const startHash = isStart ? head : (headNode.payload as { start: CasRef }).start;
|
|
188
|
+
|
|
189
|
+
const stepHash = (await uwf.store.cas.put(uwf.schemas.stepNode, {
|
|
190
|
+
start: startHash,
|
|
191
|
+
prev: isStart ? null : head,
|
|
192
|
+
role,
|
|
193
|
+
output: outputHash,
|
|
194
|
+
detail: detailHash,
|
|
195
|
+
agent: "uwf-test",
|
|
196
|
+
edgePrompt: `Do ${role}`,
|
|
197
|
+
startedAtMs: Date.now(),
|
|
198
|
+
completedAtMs: Date.now() + 1,
|
|
199
|
+
cwd: storageRoot,
|
|
200
|
+
assembledPrompt: null,
|
|
201
|
+
})) as CasRef;
|
|
202
|
+
|
|
203
|
+
setThread(uwf.varStore, threadId, {
|
|
204
|
+
head: stepHash,
|
|
205
|
+
status: "idle",
|
|
206
|
+
suspendedRole: null,
|
|
207
|
+
suspendMessage: null,
|
|
208
|
+
completedAt: null,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
describe("currentRole field", () => {
|
|
213
|
+
let tmpDir: string;
|
|
214
|
+
let storageRoot: string;
|
|
215
|
+
let casDir: string;
|
|
216
|
+
let originalEnv: string | undefined;
|
|
217
|
+
|
|
218
|
+
async function setup() {
|
|
219
|
+
tmpDir = join(
|
|
220
|
+
tmpdir(),
|
|
221
|
+
`uwf-test-current-role-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
222
|
+
);
|
|
223
|
+
storageRoot = join(tmpDir, "storage");
|
|
224
|
+
casDir = join(tmpDir, "cas");
|
|
225
|
+
await mkdir(storageRoot, { recursive: true });
|
|
226
|
+
await mkdir(casDir, { recursive: true });
|
|
227
|
+
|
|
228
|
+
// Set OCAS_HOME for this test
|
|
229
|
+
originalEnv = process.env.OCAS_HOME;
|
|
230
|
+
process.env.OCAS_HOME = casDir;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async function teardown() {
|
|
234
|
+
if (tmpDir) {
|
|
235
|
+
await rm(tmpDir, { recursive: true, force: true });
|
|
236
|
+
}
|
|
237
|
+
// Restore original environment
|
|
238
|
+
if (originalEnv === undefined) {
|
|
239
|
+
delete process.env.OCAS_HOME;
|
|
240
|
+
} else {
|
|
241
|
+
process.env.OCAS_HOME = originalEnv;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// T1: idle at start — currentRole = first role from graph
|
|
246
|
+
test("thread show — idle at start returns first role as currentRole", async () => {
|
|
247
|
+
await setup();
|
|
248
|
+
try {
|
|
249
|
+
const wf = join(tmpDir, "test-current-role.yaml");
|
|
250
|
+
await writeFile(wf, SIMPLE_WORKFLOW_YAML, "utf8");
|
|
251
|
+
const { thread } = await cmdThreadStart(storageRoot, wf, "test", tmpDir);
|
|
252
|
+
|
|
253
|
+
const result = await cmdThreadShow(storageRoot, thread as ThreadId);
|
|
254
|
+
expect(result.status).toBe("idle");
|
|
255
|
+
expect(result.currentRole).toBe("roleA");
|
|
256
|
+
} finally {
|
|
257
|
+
await teardown();
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// T2: idle after one step — currentRole = next role
|
|
262
|
+
test("thread show — idle after step returns next role as currentRole", async () => {
|
|
263
|
+
await setup();
|
|
264
|
+
try {
|
|
265
|
+
const wf = join(tmpDir, "test-current-role.yaml");
|
|
266
|
+
await writeFile(wf, SIMPLE_WORKFLOW_YAML, "utf8");
|
|
267
|
+
const { thread } = await cmdThreadStart(storageRoot, wf, "test", tmpDir);
|
|
268
|
+
|
|
269
|
+
await insertStepNode(storageRoot, thread as ThreadId, "roleA", { $status: "ready" });
|
|
270
|
+
|
|
271
|
+
const result = await cmdThreadShow(storageRoot, thread as ThreadId);
|
|
272
|
+
expect(result.status).toBe("idle");
|
|
273
|
+
expect(result.currentRole).toBe("roleB");
|
|
274
|
+
} finally {
|
|
275
|
+
await teardown();
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// T3: completed → currentRole = null
|
|
280
|
+
test("thread show — completed thread returns null currentRole", async () => {
|
|
281
|
+
await setup();
|
|
282
|
+
try {
|
|
283
|
+
const wf = join(tmpDir, "test-current-role.yaml");
|
|
284
|
+
await writeFile(wf, SIMPLE_WORKFLOW_YAML, "utf8");
|
|
285
|
+
const { thread } = await cmdThreadStart(storageRoot, wf, "test", tmpDir);
|
|
286
|
+
const tid = thread as ThreadId;
|
|
287
|
+
|
|
288
|
+
const uwfForIndex = await createUwfStore(storageRoot);
|
|
289
|
+
loadActiveThreads(uwfForIndex.varStore)[tid]!.head;
|
|
290
|
+
completeThread(uwfForIndex.varStore, tid, "completed");
|
|
291
|
+
|
|
292
|
+
const result = await cmdThreadShow(storageRoot, tid);
|
|
293
|
+
expect(result.status).toBe("completed");
|
|
294
|
+
expect(result.currentRole).toBe(null);
|
|
295
|
+
} finally {
|
|
296
|
+
await teardown();
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// T4: cancelled → currentRole = null
|
|
301
|
+
test("thread show — cancelled thread returns null currentRole", async () => {
|
|
302
|
+
await setup();
|
|
303
|
+
try {
|
|
304
|
+
const wf = join(tmpDir, "test-current-role.yaml");
|
|
305
|
+
await writeFile(wf, SIMPLE_WORKFLOW_YAML, "utf8");
|
|
306
|
+
const { thread } = await cmdThreadStart(storageRoot, wf, "test", tmpDir);
|
|
307
|
+
const tid = thread as ThreadId;
|
|
308
|
+
|
|
309
|
+
const uwfForIndex = await createUwfStore(storageRoot);
|
|
310
|
+
loadActiveThreads(uwfForIndex.varStore)[tid]!.head;
|
|
311
|
+
completeThread(uwfForIndex.varStore, tid, "cancelled");
|
|
312
|
+
|
|
313
|
+
const result = await cmdThreadShow(storageRoot, tid);
|
|
314
|
+
expect(result.status).toBe("cancelled");
|
|
315
|
+
expect(result.currentRole).toBe(null);
|
|
316
|
+
} finally {
|
|
317
|
+
await teardown();
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// T5: running → currentRole = role being executed
|
|
322
|
+
test("thread show — running thread returns current role", async () => {
|
|
323
|
+
await setup();
|
|
324
|
+
try {
|
|
325
|
+
const wf = join(tmpDir, "test-current-role.yaml");
|
|
326
|
+
await writeFile(wf, SIMPLE_WORKFLOW_YAML, "utf8");
|
|
327
|
+
const { thread, workflow } = await cmdThreadStart(storageRoot, wf, "test", tmpDir);
|
|
328
|
+
const tid = thread as ThreadId;
|
|
329
|
+
|
|
330
|
+
await createMarker(storageRoot, {
|
|
331
|
+
thread: tid,
|
|
332
|
+
workflow,
|
|
333
|
+
pid: process.pid,
|
|
334
|
+
startedAt: Date.now(),
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
try {
|
|
338
|
+
const result = await cmdThreadShow(storageRoot, tid);
|
|
339
|
+
expect(result.status).toBe("running");
|
|
340
|
+
expect(result.currentRole).toBe("roleA");
|
|
341
|
+
} finally {
|
|
342
|
+
await deleteMarker(storageRoot, tid);
|
|
343
|
+
}
|
|
344
|
+
} finally {
|
|
345
|
+
await teardown();
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// T6: thread list — mixed statuses with correct currentRole
|
|
350
|
+
test("thread list — returns correct currentRole for each status", async () => {
|
|
351
|
+
await setup();
|
|
352
|
+
try {
|
|
353
|
+
const wf = join(tmpDir, "test-current-role.yaml");
|
|
354
|
+
await writeFile(wf, SIMPLE_WORKFLOW_YAML, "utf8");
|
|
355
|
+
|
|
356
|
+
// idle thread
|
|
357
|
+
const idle = await cmdThreadStart(storageRoot, wf, "idle", tmpDir);
|
|
358
|
+
const idleId = idle.thread as ThreadId;
|
|
359
|
+
|
|
360
|
+
// completed thread
|
|
361
|
+
const comp = await cmdThreadStart(storageRoot, wf, "completed", tmpDir);
|
|
362
|
+
const compId = comp.thread as ThreadId;
|
|
363
|
+
const uwfForIndex = await createUwfStore(storageRoot);
|
|
364
|
+
const _compHead = loadActiveThreads(uwfForIndex.varStore)[compId]!.head;
|
|
365
|
+
completeThread(uwfForIndex.varStore, compId, "completed");
|
|
366
|
+
|
|
367
|
+
const list = await cmdThreadList(storageRoot, null, null, null, 0, 100);
|
|
368
|
+
|
|
369
|
+
const idleItem = list.find((i) => i.thread === idleId);
|
|
370
|
+
expect(idleItem).toBeDefined();
|
|
371
|
+
expect(idleItem!.currentRole).toBe("roleA");
|
|
372
|
+
|
|
373
|
+
const compItem = list.find((i) => i.thread === compId);
|
|
374
|
+
expect(compItem).toBeDefined();
|
|
375
|
+
expect(compItem!.currentRole).toBe(null);
|
|
376
|
+
} finally {
|
|
377
|
+
await teardown();
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
// T7: thread list — idle at start has correct currentRole
|
|
382
|
+
test("thread list — idle thread at start has correct currentRole", async () => {
|
|
383
|
+
await setup();
|
|
384
|
+
try {
|
|
385
|
+
const wf = join(tmpDir, "test-current-role.yaml");
|
|
386
|
+
await writeFile(wf, SIMPLE_WORKFLOW_YAML, "utf8");
|
|
387
|
+
const { thread } = await cmdThreadStart(storageRoot, wf, "test", tmpDir);
|
|
388
|
+
|
|
389
|
+
const list = await cmdThreadList(storageRoot, null, null, null, 0, 100);
|
|
390
|
+
const item = list.find((i) => i.thread === (thread as ThreadId));
|
|
391
|
+
expect(item).toBeDefined();
|
|
392
|
+
expect(item!.currentRole).toBe("roleA");
|
|
393
|
+
} finally {
|
|
394
|
+
await teardown();
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// T8: conditional routing — $status=pass vs fail
|
|
399
|
+
test("thread show — conditional routing selects correct next role", async () => {
|
|
400
|
+
await setup();
|
|
401
|
+
try {
|
|
402
|
+
const wf = join(tmpDir, "test-conditional-role.yaml");
|
|
403
|
+
await writeFile(wf, CONDITIONAL_WORKFLOW_YAML, "utf8");
|
|
404
|
+
|
|
405
|
+
// pass path
|
|
406
|
+
const t1 = await cmdThreadStart(storageRoot, wf, "pass test", tmpDir);
|
|
407
|
+
await insertStepNode(storageRoot, t1.thread as ThreadId, "roleA", { $status: "pass" });
|
|
408
|
+
const r1 = await cmdThreadShow(storageRoot, t1.thread as ThreadId);
|
|
409
|
+
expect(r1.currentRole).toBe("roleB");
|
|
410
|
+
|
|
411
|
+
// fail path
|
|
412
|
+
const t2 = await cmdThreadStart(storageRoot, wf, "fail test", tmpDir);
|
|
413
|
+
await insertStepNode(storageRoot, t2.thread as ThreadId, "roleA", { $status: "fail" });
|
|
414
|
+
const r2 = await cmdThreadShow(storageRoot, t2.thread as ThreadId);
|
|
415
|
+
expect(r2.currentRole).toBe("roleC");
|
|
416
|
+
} finally {
|
|
417
|
+
await teardown();
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
// T9: next role is $END → currentRole = null
|
|
422
|
+
test("thread show — when next is $END, currentRole is null", async () => {
|
|
423
|
+
await setup();
|
|
424
|
+
try {
|
|
425
|
+
const wf = join(tmpDir, "test-single-role.yaml");
|
|
426
|
+
await writeFile(wf, SINGLE_ROLE_WORKFLOW_YAML, "utf8");
|
|
427
|
+
|
|
428
|
+
const { thread } = await cmdThreadStart(storageRoot, wf, "test", tmpDir);
|
|
429
|
+
// worker → done maps to $END
|
|
430
|
+
await insertStepNode(storageRoot, thread as ThreadId, "worker", { $status: "done" });
|
|
431
|
+
|
|
432
|
+
const result = await cmdThreadShow(storageRoot, thread as ThreadId);
|
|
433
|
+
expect(result.currentRole).toBe(null);
|
|
434
|
+
} finally {
|
|
435
|
+
await teardown();
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
});
|