@united-workforce/cli 0.1.1 → 0.2.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/dist/__tests__/adapter-json-roundtrip.test.js +4 -1
- package/dist/__tests__/adapter-json-roundtrip.test.js.map +1 -1
- package/dist/__tests__/current-role.test.js +15 -3
- package/dist/__tests__/current-role.test.js.map +1 -1
- package/dist/__tests__/moderator-evaluate.test.js +20 -4
- package/dist/__tests__/moderator-evaluate.test.js.map +1 -1
- package/dist/__tests__/prompt.test.js +20 -42
- package/dist/__tests__/prompt.test.js.map +1 -1
- package/dist/__tests__/step-timing.test.js +8 -2
- package/dist/__tests__/step-timing.test.js.map +1 -1
- package/dist/__tests__/thread-location.test.js +15 -3
- package/dist/__tests__/thread-location.test.js.map +1 -1
- package/dist/__tests__/thread-resume.test.js +21 -6
- package/dist/__tests__/thread-resume.test.js.map +1 -1
- package/dist/__tests__/thread-show-status.test.js +10 -2
- package/dist/__tests__/thread-show-status.test.js.map +1 -1
- package/dist/__tests__/thread-start-cwd-cli.test.js +5 -1
- package/dist/__tests__/thread-start-cwd-cli.test.js.map +1 -1
- package/dist/__tests__/thread-suspend-step.test.js +4 -1
- package/dist/__tests__/thread-suspend-step.test.js.map +1 -1
- package/dist/__tests__/thread-suspended-display.test.js +12 -3
- package/dist/__tests__/thread-suspended-display.test.js.map +1 -1
- package/dist/__tests__/validate-semantic.test.js +29 -12
- package/dist/__tests__/validate-semantic.test.js.map +1 -1
- package/dist/__tests__/workflow-resolution.test.js +4 -1
- package/dist/__tests__/workflow-resolution.test.js.map +1 -1
- package/dist/cli.js +5 -17
- package/dist/cli.js.map +1 -1
- package/dist/commands/prompt.d.ts +3 -4
- package/dist/commands/prompt.d.ts.map +1 -1
- package/dist/commands/prompt.js +22 -39
- package/dist/commands/prompt.js.map +1 -1
- package/dist/commands/thread.d.ts.map +1 -1
- package/dist/commands/thread.js +3 -7
- package/dist/commands/thread.js.map +1 -1
- package/dist/moderator/__tests__/evaluate.test.js +12 -12
- package/dist/moderator/__tests__/evaluate.test.js.map +1 -1
- package/dist/moderator/evaluate.d.ts.map +1 -1
- package/dist/moderator/evaluate.js +1 -7
- package/dist/moderator/evaluate.js.map +1 -1
- package/dist/validate-semantic.d.ts.map +1 -1
- package/dist/validate-semantic.js +4 -12
- package/dist/validate-semantic.js.map +1 -1
- package/dist/validate.js +3 -3
- package/dist/validate.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/adapter-json-roundtrip.test.ts +4 -1
- package/src/__tests__/current-role.test.ts +15 -3
- package/src/__tests__/fixtures/e2e-count.workflow.yaml +2 -1
- package/src/__tests__/fixtures/e2e-linear.workflow.yaml +2 -1
- package/src/__tests__/fixtures/e2e-loop.workflow.yaml +2 -1
- package/src/__tests__/fixtures/e2e-mustache.workflow.yaml +2 -1
- package/src/__tests__/fixtures/e2e-suspend.workflow.yaml +2 -1
- package/src/__tests__/moderator-evaluate.test.ts +21 -4
- package/src/__tests__/prompt.test.ts +19 -46
- package/src/__tests__/step-timing.test.ts +8 -2
- package/src/__tests__/thread-location.test.ts +15 -3
- package/src/__tests__/thread-resume.test.ts +21 -6
- package/src/__tests__/thread-show-status.test.ts +10 -2
- package/src/__tests__/thread-start-cwd-cli.test.ts +5 -1
- package/src/__tests__/thread-suspend-step.test.ts +4 -1
- package/src/__tests__/thread-suspended-display.test.ts +12 -3
- package/src/__tests__/validate-semantic.test.ts +36 -16
- package/src/__tests__/workflow-resolution.test.ts +4 -1
- package/src/cli.ts +4 -20
- package/src/commands/prompt.ts +22 -41
- package/src/commands/thread.ts +3 -8
- package/src/moderator/__tests__/evaluate.test.ts +12 -12
- package/src/moderator/evaluate.ts +1 -6
- package/src/validate-semantic.ts +4 -13
- package/src/validate.ts +3 -3
|
@@ -58,7 +58,10 @@ describe("suspend step CAS chain and threads.yaml metadata", () => {
|
|
|
58
58
|
},
|
|
59
59
|
},
|
|
60
60
|
graph: {
|
|
61
|
-
$START: {
|
|
61
|
+
$START: {
|
|
62
|
+
new: { role: "worker", prompt: "Start work", location: null },
|
|
63
|
+
resume: { role: "worker", prompt: "Resume work", location: null },
|
|
64
|
+
},
|
|
62
65
|
worker: {
|
|
63
66
|
needs_input: {
|
|
64
67
|
role: "$SUSPEND",
|
|
@@ -55,7 +55,10 @@ describe("suspended thread display", () => {
|
|
|
55
55
|
},
|
|
56
56
|
},
|
|
57
57
|
graph: {
|
|
58
|
-
$START: {
|
|
58
|
+
$START: {
|
|
59
|
+
new: { role: "worker", prompt: "Start work", location: null },
|
|
60
|
+
resume: { role: "worker", prompt: "Resume work", location: null },
|
|
61
|
+
},
|
|
59
62
|
worker: {
|
|
60
63
|
needs_input: {
|
|
61
64
|
role: "$SUSPEND",
|
|
@@ -162,7 +165,10 @@ describe("suspended thread display", () => {
|
|
|
162
165
|
},
|
|
163
166
|
},
|
|
164
167
|
graph: {
|
|
165
|
-
$START: {
|
|
168
|
+
$START: {
|
|
169
|
+
new: { role: "worker", prompt: "Start work", location: null },
|
|
170
|
+
resume: { role: "worker", prompt: "Resume work", location: null },
|
|
171
|
+
},
|
|
166
172
|
worker: {
|
|
167
173
|
needs_input: {
|
|
168
174
|
role: "$SUSPEND",
|
|
@@ -248,7 +254,10 @@ describe("suspended thread display", () => {
|
|
|
248
254
|
},
|
|
249
255
|
},
|
|
250
256
|
graph: {
|
|
251
|
-
$START: {
|
|
257
|
+
$START: {
|
|
258
|
+
new: { role: "worker", prompt: "Start work", location: null },
|
|
259
|
+
resume: { role: "worker", prompt: "Resume work", location: null },
|
|
260
|
+
},
|
|
252
261
|
},
|
|
253
262
|
});
|
|
254
263
|
|
|
@@ -51,7 +51,10 @@ function makeWorkflow(overrides?: Partial<WorkflowPayload>): WorkflowPayload {
|
|
|
51
51
|
},
|
|
52
52
|
},
|
|
53
53
|
graph: {
|
|
54
|
-
$START: {
|
|
54
|
+
$START: {
|
|
55
|
+
new: { role: "writer", prompt: "Begin writing", location: null },
|
|
56
|
+
resume: { role: "writer", prompt: "Review previous output and continue", location: null },
|
|
57
|
+
},
|
|
55
58
|
writer: { done: { role: "reviewer", prompt: "Review this: {{{plan}}}", location: null } },
|
|
56
59
|
reviewer: {
|
|
57
60
|
approved: { role: "$END", prompt: "Done: {{{summary}}}", location: null },
|
|
@@ -135,27 +138,38 @@ describe("Suite 2: Graph Structure", () => {
|
|
|
135
138
|
expect(errors.some((e) => e.includes("$START must be defined in graph"))).toBe(true);
|
|
136
139
|
});
|
|
137
140
|
|
|
138
|
-
test("2.2 $START
|
|
141
|
+
test("2.2 $START missing resume edge", () => {
|
|
139
142
|
const wf = makeWorkflow();
|
|
140
143
|
wf.graph.$START = {
|
|
141
|
-
|
|
142
|
-
other: { role: "reviewer", prompt: "Also", location: null },
|
|
144
|
+
new: { role: "writer", prompt: "Begin", location: null },
|
|
143
145
|
};
|
|
144
146
|
const errors = validateWorkflow(wf);
|
|
145
147
|
expect(
|
|
146
|
-
errors.some((e) => e.includes('$START must have
|
|
148
|
+
errors.some((e) => e.includes('$START must have edges with statuses "new" and "resume"')),
|
|
147
149
|
).toBe(true);
|
|
148
150
|
});
|
|
149
151
|
|
|
150
|
-
test("2.3 $START
|
|
152
|
+
test("2.3 $START missing new edge", () => {
|
|
151
153
|
const wf = makeWorkflow();
|
|
152
|
-
wf.graph.$START = {
|
|
154
|
+
wf.graph.$START = {
|
|
155
|
+
resume: { role: "writer", prompt: "Resume", location: null },
|
|
156
|
+
};
|
|
153
157
|
const errors = validateWorkflow(wf);
|
|
154
158
|
expect(
|
|
155
|
-
errors.some((e) => e.includes('$START must have
|
|
159
|
+
errors.some((e) => e.includes('$START must have edges with statuses "new" and "resume"')),
|
|
156
160
|
).toBe(true);
|
|
157
161
|
});
|
|
158
162
|
|
|
163
|
+
test("2.3b $START with new and resume passes", () => {
|
|
164
|
+
const wf = makeWorkflow();
|
|
165
|
+
wf.graph.$START = {
|
|
166
|
+
new: { role: "writer", prompt: "Begin", location: null },
|
|
167
|
+
resume: { role: "writer", prompt: "Resume", location: null },
|
|
168
|
+
};
|
|
169
|
+
const errors = validateWorkflow(wf);
|
|
170
|
+
expect(errors.some((e) => e.includes("$START must have edges"))).toBe(false);
|
|
171
|
+
});
|
|
172
|
+
|
|
159
173
|
test("2.4 $END has outgoing edges", () => {
|
|
160
174
|
const wf = makeWorkflow();
|
|
161
175
|
wf.graph.$END = { _: { role: "writer", prompt: "Loop", location: null } };
|
|
@@ -193,15 +207,18 @@ describe("Suite 2: Graph Structure", () => {
|
|
|
193
207
|
});
|
|
194
208
|
|
|
195
209
|
describe("Suite 3: Status-Edge Consistency", () => {
|
|
196
|
-
test("3.1 user role using _ graph key is
|
|
210
|
+
test("3.1 user role using _ graph key is treated as an unknown status", () => {
|
|
211
|
+
// "_" is no longer special-cased — it's just a status key that does not
|
|
212
|
+
// match the role's $status enum, so it surfaces as extra/missing keys.
|
|
197
213
|
const wf = makeWorkflow();
|
|
198
214
|
wf.graph.writer = { _: { role: "reviewer", prompt: "Review", location: null } };
|
|
199
215
|
const errors = validateWorkflow(wf);
|
|
200
|
-
expect(
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
216
|
+
expect(errors.some((e) => e.includes('role "writer" graph has extra status keys: _'))).toBe(
|
|
217
|
+
true,
|
|
218
|
+
);
|
|
219
|
+
expect(errors.some((e) => e.includes('role "writer" graph is missing status keys: done'))).toBe(
|
|
220
|
+
true,
|
|
221
|
+
);
|
|
205
222
|
});
|
|
206
223
|
|
|
207
224
|
test("3.2 user role graph key not matching $status enum", () => {
|
|
@@ -240,13 +257,16 @@ describe("Suite 3: Status-Edge Consistency", () => {
|
|
|
240
257
|
).toBe(true);
|
|
241
258
|
});
|
|
242
259
|
|
|
243
|
-
test("3.5 multi-exit role with _ key", () => {
|
|
260
|
+
test("3.5 multi-exit role with _ key is treated as an unknown status", () => {
|
|
244
261
|
const wf = makeWorkflow();
|
|
245
262
|
wf.graph.reviewer = { _: { role: "$END", prompt: "Done", location: null } };
|
|
246
263
|
const errors = validateWorkflow(wf);
|
|
264
|
+
expect(errors.some((e) => e.includes('role "reviewer" graph has extra status keys: _'))).toBe(
|
|
265
|
+
true,
|
|
266
|
+
);
|
|
247
267
|
expect(
|
|
248
268
|
errors.some((e) =>
|
|
249
|
-
e.includes('role "reviewer"
|
|
269
|
+
e.includes('role "reviewer" graph is missing status keys: approved, rejected'),
|
|
250
270
|
),
|
|
251
271
|
).toBe(true);
|
|
252
272
|
});
|
|
@@ -38,7 +38,10 @@ function makeMinimalPayload(name: string, description: string): WorkflowPayload
|
|
|
38
38
|
},
|
|
39
39
|
},
|
|
40
40
|
graph: {
|
|
41
|
-
$START: {
|
|
41
|
+
$START: {
|
|
42
|
+
new: { role: "worker", prompt: "start working", location: null },
|
|
43
|
+
resume: { role: "worker", prompt: "resume working", location: null },
|
|
44
|
+
},
|
|
42
45
|
worker: { done: { role: "$END", prompt: "done", location: null } },
|
|
43
46
|
},
|
|
44
47
|
};
|
package/src/cli.ts
CHANGED
|
@@ -8,9 +8,7 @@ import {
|
|
|
8
8
|
cmdPromptAdapterDeveloping,
|
|
9
9
|
cmdPromptBootstrap,
|
|
10
10
|
cmdPromptList,
|
|
11
|
-
cmdPromptSetup,
|
|
12
11
|
cmdPromptUsage,
|
|
13
|
-
cmdPromptUsageReference,
|
|
14
12
|
cmdPromptWorkflowAuthoring,
|
|
15
13
|
} from "./commands/prompt.js";
|
|
16
14
|
import { cmdSetup, cmdSetupInteractive } from "./commands/setup.js";
|
|
@@ -509,23 +507,16 @@ prompt.addHelpCommand(false);
|
|
|
509
507
|
|
|
510
508
|
prompt
|
|
511
509
|
.command("usage")
|
|
512
|
-
.description("Print the
|
|
510
|
+
.description("Print the usage reference (CLI guide + typical workflows)")
|
|
513
511
|
.action(() => {
|
|
514
512
|
console.log(cmdPromptUsage());
|
|
515
513
|
});
|
|
516
514
|
|
|
517
515
|
prompt
|
|
518
|
-
.command("
|
|
519
|
-
.description("Print setup instructions for installing
|
|
520
|
-
.action(() => {
|
|
521
|
-
console.log(cmdPromptSetup());
|
|
522
|
-
});
|
|
523
|
-
|
|
524
|
-
prompt
|
|
525
|
-
.command("usage-reference")
|
|
526
|
-
.description("Print the usage reference (CLI guide + typical workflows)")
|
|
516
|
+
.command("bootstrap")
|
|
517
|
+
.description("Print setup instructions for installing uwf skills")
|
|
527
518
|
.action(() => {
|
|
528
|
-
console.log(
|
|
519
|
+
console.log(cmdPromptBootstrap());
|
|
529
520
|
});
|
|
530
521
|
|
|
531
522
|
prompt
|
|
@@ -542,13 +533,6 @@ prompt
|
|
|
542
533
|
console.log(cmdPromptAdapterDeveloping());
|
|
543
534
|
});
|
|
544
535
|
|
|
545
|
-
prompt
|
|
546
|
-
.command("bootstrap")
|
|
547
|
-
.description("Print the bootstrap skill YAML for Hermes agents")
|
|
548
|
-
.action(() => {
|
|
549
|
-
console.log(cmdPromptBootstrap());
|
|
550
|
-
});
|
|
551
|
-
|
|
552
536
|
prompt
|
|
553
537
|
.command("list")
|
|
554
538
|
.description("List all available prompt names")
|
package/src/commands/prompt.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
generateAdapterDevelopingReference,
|
|
3
|
-
generateBootstrapReference,
|
|
4
3
|
generateUsageReference,
|
|
5
4
|
generateWorkflowAuthoringReference,
|
|
5
|
+
VERSION,
|
|
6
6
|
} from "@united-workforce/util";
|
|
7
7
|
|
|
8
8
|
export {
|
|
9
9
|
generateAdapterDevelopingReference as cmdPromptAdapterDeveloping,
|
|
10
|
-
|
|
11
|
-
generateUsageReference as cmdPromptUsageReference,
|
|
10
|
+
generateUsageReference as cmdPromptUsage,
|
|
12
11
|
generateWorkflowAuthoringReference as cmdPromptWorkflowAuthoring,
|
|
13
12
|
};
|
|
14
13
|
|
|
@@ -16,24 +15,16 @@ const PROMPT_ENTRIES: ReadonlyArray<{ name: string; generate: () => string }> =
|
|
|
16
15
|
{ name: "usage", generate: generateUsageReference },
|
|
17
16
|
{ name: "workflow-authoring", generate: generateWorkflowAuthoringReference },
|
|
18
17
|
{ name: "adapter-developing", generate: generateAdapterDevelopingReference },
|
|
19
|
-
{ name: "bootstrap", generate: generateBootstrapReference },
|
|
20
18
|
];
|
|
21
19
|
|
|
22
20
|
export function cmdPromptList(): ReadonlyArray<string> {
|
|
23
21
|
return PROMPT_ENTRIES.map((e) => e.name);
|
|
24
22
|
}
|
|
25
23
|
|
|
26
|
-
export function
|
|
27
|
-
return
|
|
28
|
-
.map((e) => e.generate())
|
|
29
|
-
.join("\n\n---\n\n");
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function cmdPromptSetup(): string {
|
|
33
|
-
return `# uwf Skill Setup
|
|
24
|
+
export function cmdPromptBootstrap(): string {
|
|
25
|
+
return `# uwf Skill Bootstrap
|
|
34
26
|
|
|
35
|
-
|
|
36
|
-
so that you know how to use the \`uwf\` CLI for workflow orchestration.
|
|
27
|
+
Install or update the uwf (United Workforce) skills for your agent.
|
|
37
28
|
|
|
38
29
|
## Steps
|
|
39
30
|
|
|
@@ -42,40 +33,30 @@ so that you know how to use the \`uwf\` CLI for workflow orchestration.
|
|
|
42
33
|
uwf --help
|
|
43
34
|
\`\`\`
|
|
44
35
|
If not installed: \`npm install -g @united-workforce/cli\`
|
|
36
|
+
Current version: ${VERSION}
|
|
45
37
|
|
|
46
|
-
2. **
|
|
47
|
-
-
|
|
48
|
-
|
|
49
|
-
-
|
|
50
|
-
- If the version matches the current CLI version (\`uwf --version\`), you're done — no update needed.
|
|
51
|
-
- If the version is older, **delete the old skill file** before proceeding.
|
|
38
|
+
2. **Install the skills** by running each command below and creating a skill from its output:
|
|
39
|
+
- Run \`uwf prompt usage\` → create a skill named \`uwf-usage\`
|
|
40
|
+
- Run \`uwf prompt workflow-authoring\` → create a skill named \`uwf-workflow-authoring\`
|
|
41
|
+
- Run \`uwf prompt adapter-developing\` → create a skill named \`uwf-adapter-developing\`
|
|
52
42
|
|
|
53
|
-
|
|
54
|
-
\`\`\`bash
|
|
55
|
-
uwf prompt bootstrap > ~/.hermes/skills/uwf/SKILL.md
|
|
56
|
-
\`\`\`
|
|
57
|
-
The bootstrap prompt already includes complete YAML frontmatter — no editing needed.
|
|
43
|
+
Each command outputs a complete SKILL.md with YAML frontmatter — use your agent framework's skill creation API to save them.
|
|
58
44
|
|
|
59
|
-
|
|
45
|
+
3. **Verify** the skills are loadable by your agent framework.
|
|
60
46
|
|
|
61
|
-
##
|
|
47
|
+
## Updating
|
|
62
48
|
|
|
63
|
-
|
|
49
|
+
When \`uwf\` is upgraded, re-run \`uwf prompt bootstrap\` and follow the steps again.
|
|
50
|
+
The skill content is bundled with the CLI — always use \`uwf prompt <name>\` to get
|
|
51
|
+
content matching your installed version.
|
|
52
|
+
|
|
53
|
+
## Available prompts
|
|
64
54
|
|
|
65
55
|
\`\`\`bash
|
|
66
|
-
uwf prompt list
|
|
67
|
-
uwf prompt usage
|
|
68
|
-
uwf prompt workflow-authoring
|
|
69
|
-
uwf prompt adapter-developing
|
|
70
|
-
uwf prompt bootstrap > ~/.hermes/skills/uwf/SKILL.md # bootstrap skill
|
|
56
|
+
uwf prompt list # list available prompt names
|
|
57
|
+
uwf prompt usage # CLI usage guide
|
|
58
|
+
uwf prompt workflow-authoring # workflow YAML design guide
|
|
59
|
+
uwf prompt adapter-developing # building agent adapters
|
|
71
60
|
\`\`\`
|
|
72
|
-
|
|
73
|
-
## Notes
|
|
74
|
-
|
|
75
|
-
- The skill content is bundled with the CLI and versioned with it — always use
|
|
76
|
-
\`uwf prompt usage\` to get the content matching your installed version.
|
|
77
|
-
- Do NOT hand-edit the skill body. If the CLI is updated, re-run \`uwf prompt setup\`
|
|
78
|
-
and follow the steps again.
|
|
79
|
-
- When upgrading, always delete the old skill first to avoid stale instructions.
|
|
80
61
|
`;
|
|
81
62
|
}
|
package/src/commands/thread.ts
CHANGED
|
@@ -911,7 +911,7 @@ function resolveEvaluateArgs(
|
|
|
911
911
|
chain: ChainState,
|
|
912
912
|
): { lastRole: string; lastOutput: EvaluateLastOutput } {
|
|
913
913
|
if (chain.headIsStart) {
|
|
914
|
-
return { lastRole: START_ROLE, lastOutput: { [STATUS_KEY]: "
|
|
914
|
+
return { lastRole: START_ROLE, lastOutput: { [STATUS_KEY]: "new" } };
|
|
915
915
|
}
|
|
916
916
|
|
|
917
917
|
const lastStep = chain.stepsNewestFirst[0];
|
|
@@ -1037,7 +1037,6 @@ function archiveThread(uwf: UwfStore, threadId: ThreadId, _workflow: CasRef, _he
|
|
|
1037
1037
|
completeThread(uwf.varStore, threadId, "completed");
|
|
1038
1038
|
}
|
|
1039
1039
|
|
|
1040
|
-
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: orchestration function with inherent branching
|
|
1041
1040
|
export async function cmdThreadResume(
|
|
1042
1041
|
storageRoot: string,
|
|
1043
1042
|
threadId: ThreadId,
|
|
@@ -1101,7 +1100,7 @@ export async function cmdThreadResume(
|
|
|
1101
1100
|
|
|
1102
1101
|
// status === "completed"
|
|
1103
1102
|
const workflow = loadWorkflowPayload(uwf, workflowHash);
|
|
1104
|
-
const startResult = evaluate(workflow.graph, START_ROLE, {});
|
|
1103
|
+
const startResult = evaluate(workflow.graph, START_ROLE, { [STATUS_KEY]: "resume" });
|
|
1105
1104
|
if (!startResult.ok) {
|
|
1106
1105
|
fail(`failed to evaluate $START: ${startResult.error.message}`);
|
|
1107
1106
|
}
|
|
@@ -1113,11 +1112,7 @@ export async function cmdThreadResume(
|
|
|
1113
1112
|
}
|
|
1114
1113
|
|
|
1115
1114
|
const startRole = startResult.value.role;
|
|
1116
|
-
const
|
|
1117
|
-
const completedResumePrompt =
|
|
1118
|
-
supplement !== null && supplement !== ""
|
|
1119
|
-
? `${completedPromptPrefix}\n\n${supplement}`
|
|
1120
|
-
: completedPromptPrefix;
|
|
1115
|
+
const completedResumePrompt = buildResumePrompt(startResult.value.prompt, supplement);
|
|
1121
1116
|
|
|
1122
1117
|
const updatedEntry = { ...entry, status: "idle" as const, completedAt: null };
|
|
1123
1118
|
setThread(uwf.varStore, threadId, updatedEntry);
|
|
@@ -6,11 +6,11 @@ describe("Edge prompt template variable resolution", () => {
|
|
|
6
6
|
test("returns error when rendered prompt is empty string", () => {
|
|
7
7
|
const graph = {
|
|
8
8
|
$START: {
|
|
9
|
-
|
|
9
|
+
new: { role: "classifier", prompt: "{{{userPrompt}}}", location: null },
|
|
10
10
|
},
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
-
const result = evaluate(graph, "$START", {});
|
|
13
|
+
const result = evaluate(graph, "$START", { $status: "new" });
|
|
14
14
|
|
|
15
15
|
expect(result.ok).toBe(false);
|
|
16
16
|
if (!result.ok) {
|
|
@@ -22,11 +22,11 @@ describe("Edge prompt template variable resolution", () => {
|
|
|
22
22
|
test("returns error when rendered prompt is whitespace-only", () => {
|
|
23
23
|
const graph = {
|
|
24
24
|
$START: {
|
|
25
|
-
|
|
25
|
+
new: { role: "classifier", prompt: " {{{userPrompt}}} ", location: null },
|
|
26
26
|
},
|
|
27
27
|
};
|
|
28
28
|
|
|
29
|
-
const result = evaluate(graph, "$START", {});
|
|
29
|
+
const result = evaluate(graph, "$START", { $status: "new" });
|
|
30
30
|
|
|
31
31
|
expect(result.ok).toBe(false);
|
|
32
32
|
if (!result.ok) {
|
|
@@ -38,11 +38,11 @@ describe("Edge prompt template variable resolution", () => {
|
|
|
38
38
|
test("succeeds when all template variables resolve to non-empty values", () => {
|
|
39
39
|
const graph = {
|
|
40
40
|
$START: {
|
|
41
|
-
|
|
41
|
+
new: { role: "classifier", prompt: "{{{userPrompt}}}", location: null },
|
|
42
42
|
},
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
const result = evaluate(graph, "$START", { userPrompt: "Fix the bug" });
|
|
45
|
+
const result = evaluate(graph, "$START", { $status: "new", userPrompt: "Fix the bug" });
|
|
46
46
|
|
|
47
47
|
expect(result.ok).toBe(true);
|
|
48
48
|
if (result.ok) {
|
|
@@ -53,11 +53,11 @@ describe("Edge prompt template variable resolution", () => {
|
|
|
53
53
|
test("succeeds with static (no-variable) prompt", () => {
|
|
54
54
|
const graph = {
|
|
55
55
|
$START: {
|
|
56
|
-
|
|
56
|
+
new: { role: "classifier", prompt: "Classify this input", location: null },
|
|
57
57
|
},
|
|
58
58
|
};
|
|
59
59
|
|
|
60
|
-
const result = evaluate(graph, "$START", {});
|
|
60
|
+
const result = evaluate(graph, "$START", { $status: "new" });
|
|
61
61
|
|
|
62
62
|
expect(result.ok).toBe(true);
|
|
63
63
|
if (result.ok) {
|
|
@@ -68,11 +68,11 @@ describe("Edge prompt template variable resolution", () => {
|
|
|
68
68
|
test("succeeds when prompt has mix of static text and unresolved variables", () => {
|
|
69
69
|
const graph = {
|
|
70
70
|
$START: {
|
|
71
|
-
|
|
71
|
+
new: { role: "classifier", prompt: "Please handle: {{{userPrompt}}}", location: null },
|
|
72
72
|
},
|
|
73
73
|
};
|
|
74
74
|
|
|
75
|
-
const result = evaluate(graph, "$START", {});
|
|
75
|
+
const result = evaluate(graph, "$START", { $status: "new" });
|
|
76
76
|
|
|
77
77
|
expect(result.ok).toBe(true);
|
|
78
78
|
if (result.ok) {
|
|
@@ -83,11 +83,11 @@ describe("Edge prompt template variable resolution", () => {
|
|
|
83
83
|
test("returns error when ALL variables missing and no static text remains", () => {
|
|
84
84
|
const graph = {
|
|
85
85
|
$START: {
|
|
86
|
-
|
|
86
|
+
new: { role: "classifier", prompt: "{{{a}}}{{{b}}}", location: null },
|
|
87
87
|
},
|
|
88
88
|
};
|
|
89
89
|
|
|
90
|
-
const result = evaluate(graph, "$START", {});
|
|
90
|
+
const result = evaluate(graph, "$START", { $status: "new" });
|
|
91
91
|
|
|
92
92
|
expect(result.ok).toBe(false);
|
|
93
93
|
});
|
|
@@ -6,10 +6,7 @@ import type { EvaluateResult, Result } from "./types.js";
|
|
|
6
6
|
// Disable HTML escaping — prompts are plain text, not HTML.
|
|
7
7
|
mustache.escape = (text: string) => text;
|
|
8
8
|
|
|
9
|
-
const START_ROLE = "$START";
|
|
10
9
|
const SUSPEND_ROLE = "$SUSPEND";
|
|
11
|
-
// $START is a special entry node with no agent output — it always uses this key.
|
|
12
|
-
const START_STATUS = "_";
|
|
13
10
|
|
|
14
11
|
type LastOutput = Record<string, unknown>;
|
|
15
12
|
|
|
@@ -21,9 +18,7 @@ export function evaluate(
|
|
|
21
18
|
lastOutput: LastOutput,
|
|
22
19
|
): Result<EvaluateResult, Error> {
|
|
23
20
|
let status: string;
|
|
24
|
-
if (
|
|
25
|
-
status = START_STATUS;
|
|
26
|
-
} else if (typeof lastOutput[STATUS_KEY] === "string") {
|
|
21
|
+
if (typeof lastOutput[STATUS_KEY] === "string") {
|
|
27
22
|
status = lastOutput[STATUS_KEY] as string;
|
|
28
23
|
} else {
|
|
29
24
|
return {
|
package/src/validate-semantic.ts
CHANGED
|
@@ -97,9 +97,9 @@ function checkGraphStructure(payload: WorkflowPayload, errors: string[]): void {
|
|
|
97
97
|
if (!graphNodes.has("$START")) {
|
|
98
98
|
errors.push("$START must be defined in graph");
|
|
99
99
|
} else {
|
|
100
|
-
const startKeys = Object.keys(payload.graph.$START);
|
|
101
|
-
if (startKeys.
|
|
102
|
-
errors.push('$START must have
|
|
100
|
+
const startKeys = new Set(Object.keys(payload.graph.$START));
|
|
101
|
+
if (!startKeys.has("new") || !startKeys.has("resume")) {
|
|
102
|
+
errors.push('$START must have edges with statuses "new" and "resume"');
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
|
|
@@ -190,22 +190,13 @@ function checkOneOfDiscriminant(
|
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
/** Check status-edge consistency for a user role.
|
|
193
|
+
/** Check status-edge consistency for a user role. */
|
|
194
194
|
function checkStatusEdges(
|
|
195
195
|
roleName: string,
|
|
196
196
|
graphKeys: Set<string>,
|
|
197
197
|
statusSet: Set<string>,
|
|
198
198
|
errors: string[],
|
|
199
199
|
): void {
|
|
200
|
-
if (graphKeys.has("_")) {
|
|
201
|
-
errors.push(`role "${roleName}" must use explicit $status keys in graph, not "_"`);
|
|
202
|
-
return;
|
|
203
|
-
}
|
|
204
|
-
if (statusSet.has("_")) {
|
|
205
|
-
errors.push(`role "${roleName}" $status enum must use explicit values, not "_"`);
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
200
|
const extraKeys = [...graphKeys].filter((k) => !statusSet.has(k));
|
|
210
201
|
const missingKeys = [...statusSet].filter((k) => !graphKeys.has(k));
|
|
211
202
|
if (extraKeys.length > 0) {
|
package/src/validate.ts
CHANGED
|
@@ -57,13 +57,13 @@ function isGraph(value: unknown): boolean {
|
|
|
57
57
|
if (!isRecord(value)) {
|
|
58
58
|
return false;
|
|
59
59
|
}
|
|
60
|
-
return Object.
|
|
60
|
+
return Object.values(value).every((statusMap) => {
|
|
61
61
|
if (!isRecord(statusMap)) {
|
|
62
62
|
return false;
|
|
63
63
|
}
|
|
64
64
|
return Object.entries(statusMap).every(([status, target]) => {
|
|
65
|
-
// "_" is
|
|
66
|
-
if (status === "_"
|
|
65
|
+
// "_" is no longer a valid status key anywhere — $START uses "new"/"resume".
|
|
66
|
+
if (status === "_") {
|
|
67
67
|
return false;
|
|
68
68
|
}
|
|
69
69
|
return isTarget(target);
|