@united-workforce/cli 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -11
- package/dist/.build-fingerprint +1 -0
- package/dist/__tests__/adapter-json-roundtrip.test.js +17 -7
- package/dist/__tests__/adapter-json-roundtrip.test.js.map +1 -1
- package/dist/__tests__/agent-resolution-llm-free.test.d.ts +2 -0
- package/dist/__tests__/agent-resolution-llm-free.test.d.ts.map +1 -0
- package/dist/__tests__/agent-resolution-llm-free.test.js +30 -0
- package/dist/__tests__/agent-resolution-llm-free.test.js.map +1 -0
- package/dist/__tests__/build-step-entry.test.d.ts +2 -0
- package/dist/__tests__/build-step-entry.test.d.ts.map +1 -0
- package/dist/__tests__/build-step-entry.test.js +173 -0
- package/dist/__tests__/build-step-entry.test.js.map +1 -0
- package/dist/__tests__/clear-thread-failed-attempts.test.d.ts +2 -0
- package/dist/__tests__/clear-thread-failed-attempts.test.d.ts.map +1 -0
- package/dist/__tests__/clear-thread-failed-attempts.test.js +93 -0
- package/dist/__tests__/clear-thread-failed-attempts.test.js.map +1 -0
- package/dist/__tests__/concurrency.test.d.ts +2 -0
- package/dist/__tests__/concurrency.test.d.ts.map +1 -0
- package/dist/__tests__/concurrency.test.js +196 -0
- package/dist/__tests__/concurrency.test.js.map +1 -0
- package/dist/__tests__/config.test.js +26 -302
- package/dist/__tests__/config.test.js.map +1 -1
- package/dist/__tests__/current-role.test.js +7 -6
- package/dist/__tests__/current-role.test.js.map +1 -1
- package/dist/__tests__/e2e-mock-agent.test.js +43 -30
- package/dist/__tests__/e2e-mock-agent.test.js.map +1 -1
- package/dist/__tests__/format-text-default.test.d.ts +2 -0
- package/dist/__tests__/format-text-default.test.d.ts.map +1 -0
- package/dist/__tests__/format-text-default.test.js +43 -0
- package/dist/__tests__/format-text-default.test.js.map +1 -0
- package/dist/__tests__/format-text-registry.test.d.ts +2 -0
- package/dist/__tests__/format-text-registry.test.d.ts.map +1 -0
- package/dist/__tests__/format-text-registry.test.js +158 -0
- package/dist/__tests__/format-text-registry.test.js.map +1 -0
- package/dist/__tests__/issue-180-workflow-ref-removed.test.d.ts +2 -0
- package/dist/__tests__/issue-180-workflow-ref-removed.test.d.ts.map +1 -0
- package/dist/__tests__/issue-180-workflow-ref-removed.test.js +40 -0
- package/dist/__tests__/issue-180-workflow-ref-removed.test.js.map +1 -0
- package/dist/__tests__/log-text-renderer.test.d.ts +2 -0
- package/dist/__tests__/log-text-renderer.test.d.ts.map +1 -0
- package/dist/__tests__/log-text-renderer.test.js +265 -0
- package/dist/__tests__/log-text-renderer.test.js.map +1 -0
- package/dist/__tests__/moderator-evaluate.test.js +9 -50
- package/dist/__tests__/moderator-evaluate.test.js.map +1 -1
- package/dist/__tests__/output-mapper-thread-list-startedat.test.d.ts +2 -0
- package/dist/__tests__/output-mapper-thread-list-startedat.test.d.ts.map +1 -0
- package/dist/__tests__/output-mapper-thread-list-startedat.test.js +102 -0
- package/dist/__tests__/output-mapper-thread-list-startedat.test.js.map +1 -0
- package/dist/__tests__/output-mapper-workflow-add.test.d.ts +2 -0
- package/dist/__tests__/output-mapper-workflow-add.test.d.ts.map +1 -0
- package/dist/__tests__/output-mapper-workflow-add.test.js +22 -0
- package/dist/__tests__/output-mapper-workflow-add.test.js.map +1 -0
- package/dist/__tests__/pid-recycling.test.d.ts +2 -0
- package/dist/__tests__/pid-recycling.test.d.ts.map +1 -0
- package/dist/__tests__/pid-recycling.test.js +273 -0
- package/dist/__tests__/pid-recycling.test.js.map +1 -0
- package/dist/__tests__/prompt.test.js +365 -2
- package/dist/__tests__/prompt.test.js.map +1 -1
- package/dist/__tests__/resolve-head-hash.test.js +12 -4
- package/dist/__tests__/resolve-head-hash.test.js.map +1 -1
- package/dist/__tests__/setup-agent-discovery.test.js +21 -30
- package/dist/__tests__/setup-agent-discovery.test.js.map +1 -1
- package/dist/__tests__/setup-complexity.test.js +2 -168
- package/dist/__tests__/setup-complexity.test.js.map +1 -1
- package/dist/__tests__/setup-no-llm.test.d.ts +2 -0
- package/dist/__tests__/setup-no-llm.test.d.ts.map +1 -0
- package/dist/__tests__/setup-no-llm.test.js +52 -0
- package/dist/__tests__/setup-no-llm.test.js.map +1 -0
- package/dist/__tests__/solve-issue-tea-worktree.test.js +27 -28
- package/dist/__tests__/solve-issue-tea-worktree.test.js.map +1 -1
- package/dist/__tests__/step-ask.test.d.ts +2 -0
- package/dist/__tests__/step-ask.test.d.ts.map +1 -0
- package/dist/__tests__/step-ask.test.js +507 -0
- package/dist/__tests__/step-ask.test.js.map +1 -0
- package/dist/__tests__/step-show-json.test.js +1 -0
- package/dist/__tests__/step-show-json.test.js.map +1 -1
- package/dist/__tests__/step-timing.test.js +2 -0
- package/dist/__tests__/step-timing.test.js.map +1 -1
- package/dist/__tests__/store-global-cas.test.js +2 -2
- package/dist/__tests__/store-global-cas.test.js.map +1 -1
- package/dist/__tests__/store-unified-threads.test.js +28 -26
- package/dist/__tests__/store-unified-threads.test.js.map +1 -1
- package/dist/__tests__/thread-cancel-status.test.js +25 -19
- package/dist/__tests__/thread-cancel-status.test.js.map +1 -1
- package/dist/__tests__/thread-cancel-text-renderer.test.d.ts +2 -0
- package/dist/__tests__/thread-cancel-text-renderer.test.d.ts.map +1 -0
- package/dist/__tests__/thread-cancel-text-renderer.test.js +110 -0
- package/dist/__tests__/thread-cancel-text-renderer.test.js.map +1 -0
- package/dist/__tests__/thread-list-filters.test.js +354 -17
- package/dist/__tests__/thread-list-filters.test.js.map +1 -1
- package/dist/__tests__/thread-list-template-ms-date.test.d.ts +2 -0
- package/dist/__tests__/thread-list-template-ms-date.test.d.ts.map +1 -0
- package/dist/__tests__/thread-list-template-ms-date.test.js +102 -0
- package/dist/__tests__/thread-list-template-ms-date.test.js.map +1 -0
- package/dist/__tests__/thread-list-workflow-corrupt.test.d.ts +2 -0
- package/dist/__tests__/thread-list-workflow-corrupt.test.d.ts.map +1 -0
- package/dist/__tests__/thread-list-workflow-corrupt.test.js +157 -0
- package/dist/__tests__/thread-list-workflow-corrupt.test.js.map +1 -0
- package/dist/__tests__/thread-poke.test.d.ts +2 -0
- package/dist/__tests__/thread-poke.test.d.ts.map +1 -0
- package/dist/__tests__/thread-poke.test.js +422 -0
- package/dist/__tests__/thread-poke.test.js.map +1 -0
- package/dist/__tests__/thread-read-xml-tags.test.js +10 -9
- package/dist/__tests__/thread-read-xml-tags.test.js.map +1 -1
- package/dist/__tests__/thread-resume.test.js +21 -15
- package/dist/__tests__/thread-resume.test.js.map +1 -1
- package/dist/__tests__/thread-show-status.test.js +17 -28
- package/dist/__tests__/thread-show-status.test.js.map +1 -1
- package/dist/__tests__/thread-start-cwd-cli.test.js +15 -3
- package/dist/__tests__/thread-start-cwd-cli.test.js.map +1 -1
- package/dist/__tests__/thread-stop-text-renderer.test.d.ts +2 -0
- package/dist/__tests__/thread-stop-text-renderer.test.d.ts.map +1 -0
- package/dist/__tests__/thread-stop-text-renderer.test.js +148 -0
- package/dist/__tests__/thread-stop-text-renderer.test.js.map +1 -0
- package/dist/__tests__/thread-suspend-step.test.js +13 -16
- package/dist/__tests__/thread-suspend-step.test.js.map +1 -1
- package/dist/__tests__/thread-suspended-display.test.js +10 -22
- package/dist/__tests__/thread-suspended-display.test.js.map +1 -1
- package/dist/__tests__/thread-test-helpers.d.ts +7 -0
- package/dist/__tests__/thread-test-helpers.d.ts.map +1 -1
- package/dist/__tests__/thread-test-helpers.js +13 -0
- package/dist/__tests__/thread-test-helpers.js.map +1 -1
- package/dist/__tests__/thread.test.js +15 -13
- package/dist/__tests__/thread.test.js.map +1 -1
- package/dist/__tests__/validate-semantic.test.js +105 -23
- package/dist/__tests__/validate-semantic.test.js.map +1 -1
- package/dist/__tests__/workflow-list-recursive.test.d.ts +2 -0
- package/dist/__tests__/workflow-list-recursive.test.d.ts.map +1 -0
- package/dist/__tests__/workflow-list-recursive.test.js +286 -0
- package/dist/__tests__/workflow-list-recursive.test.js.map +1 -0
- package/dist/__tests__/workflow-resolution.test.js +46 -28
- package/dist/__tests__/workflow-resolution.test.js.map +1 -1
- package/dist/__tests__/workflow-show-resolution.test.d.ts +2 -0
- package/dist/__tests__/workflow-show-resolution.test.d.ts.map +1 -0
- package/dist/__tests__/workflow-show-resolution.test.js +213 -0
- package/dist/__tests__/workflow-show-resolution.test.js.map +1 -0
- package/dist/__tests__/workflow-validate.test.d.ts +2 -0
- package/dist/__tests__/workflow-validate.test.d.ts.map +1 -0
- package/dist/__tests__/workflow-validate.test.js +707 -0
- package/dist/__tests__/workflow-validate.test.js.map +1 -0
- package/dist/__tests__/write-envelope.test.d.ts +2 -0
- package/dist/__tests__/write-envelope.test.d.ts.map +1 -0
- package/dist/__tests__/write-envelope.test.js +201 -0
- package/dist/__tests__/write-envelope.test.js.map +1 -0
- package/dist/background/background.d.ts +22 -1
- package/dist/background/background.d.ts.map +1 -1
- package/dist/background/background.js +83 -6
- package/dist/background/background.js.map +1 -1
- package/dist/background/index.d.ts +1 -1
- package/dist/background/index.d.ts.map +1 -1
- package/dist/background/index.js +1 -1
- package/dist/background/index.js.map +1 -1
- package/dist/background/types.d.ts +1 -0
- package/dist/background/types.d.ts.map +1 -1
- package/dist/cli.js +120 -62
- package/dist/cli.js.map +1 -1
- package/dist/commands/config.d.ts +3 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +17 -31
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/prompt.d.ts.map +1 -1
- package/dist/commands/prompt.js +57 -31
- package/dist/commands/prompt.js.map +1 -1
- package/dist/commands/setup.d.ts +12 -39
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +72 -303
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/step.d.ts +44 -1
- package/dist/commands/step.d.ts.map +1 -1
- package/dist/commands/step.js +255 -11
- package/dist/commands/step.js.map +1 -1
- package/dist/commands/thread.d.ts +16 -3
- package/dist/commands/thread.d.ts.map +1 -1
- package/dist/commands/thread.js +423 -142
- package/dist/commands/thread.js.map +1 -1
- package/dist/commands/workflow.d.ts +9 -1
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +126 -6
- package/dist/commands/workflow.js.map +1 -1
- package/dist/concurrency/concurrency.d.ts +34 -0
- package/dist/concurrency/concurrency.d.ts.map +1 -0
- package/dist/concurrency/concurrency.js +216 -0
- package/dist/concurrency/concurrency.js.map +1 -0
- package/dist/concurrency/index.d.ts +3 -0
- package/dist/concurrency/index.d.ts.map +1 -0
- package/dist/concurrency/index.js +2 -0
- package/dist/concurrency/index.js.map +1 -0
- package/dist/concurrency/types.d.ts +19 -0
- package/dist/concurrency/types.d.ts.map +1 -0
- package/dist/concurrency/types.js +2 -0
- package/dist/concurrency/types.js.map +1 -0
- package/dist/format.d.ts +69 -2
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +198 -1
- package/dist/format.js.map +1 -1
- package/dist/moderator/__tests__/evaluate.test.js +31 -17
- package/dist/moderator/__tests__/evaluate.test.js.map +1 -1
- package/dist/moderator/evaluate.d.ts.map +1 -1
- package/dist/moderator/evaluate.js +4 -16
- package/dist/moderator/evaluate.js.map +1 -1
- package/dist/moderator/index.d.ts +1 -2
- package/dist/moderator/index.d.ts.map +1 -1
- package/dist/moderator/index.js +0 -1
- package/dist/moderator/index.js.map +1 -1
- package/dist/moderator/types.d.ts +6 -10
- package/dist/moderator/types.d.ts.map +1 -1
- package/dist/moderator/types.js +1 -3
- package/dist/moderator/types.js.map +1 -1
- package/dist/output-mappers.d.ts +122 -0
- package/dist/output-mappers.d.ts.map +1 -0
- package/dist/output-mappers.js +134 -0
- package/dist/output-mappers.js.map +1 -0
- package/dist/schemas.d.ts +6 -1
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +34 -5
- package/dist/schemas.js.map +1 -1
- package/dist/store.d.ts +28 -9
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +75 -16
- package/dist/store.js.map +1 -1
- package/dist/text-renderers.d.ts +30 -0
- package/dist/text-renderers.d.ts.map +1 -0
- package/dist/text-renderers.js +251 -0
- package/dist/text-renderers.js.map +1 -0
- package/dist/validate-semantic.d.ts.map +1 -1
- package/dist/validate-semantic.js +95 -61
- package/dist/validate-semantic.js.map +1 -1
- package/dist/validate.d.ts +6 -0
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +24 -0
- package/dist/validate.js.map +1 -1
- package/examples/brainstorm.yaml +130 -0
- package/examples/debate.yaml +169 -0
- package/examples/socratic-questioning.yaml +112 -0
- package/package.json +9 -10
- package/src/__tests__/adapter-json-roundtrip.test.ts +16 -7
- package/src/__tests__/agent-resolution-llm-free.test.ts +39 -0
- package/src/__tests__/build-step-entry.test.ts +203 -0
- package/src/__tests__/clear-thread-failed-attempts.test.ts +122 -0
- package/src/__tests__/concurrency.test.ts +266 -0
- package/src/__tests__/config.test.ts +33 -321
- package/src/__tests__/current-role.test.ts +7 -6
- package/src/__tests__/e2e-mock-agent.test.ts +65 -30
- package/src/__tests__/fixtures/e2e-count.workflow.yaml +1 -0
- package/src/__tests__/fixtures/e2e-linear.workflow.yaml +1 -0
- package/src/__tests__/fixtures/{e2e-mustache.workflow.yaml → e2e-liquid.workflow.yaml} +3 -2
- package/src/__tests__/fixtures/e2e-loop.workflow.yaml +1 -0
- package/src/__tests__/fixtures/e2e-suspend.mock.yaml +2 -2
- package/src/__tests__/fixtures/e2e-suspend.workflow.yaml +6 -10
- package/src/__tests__/format-text-default.test.ts +49 -0
- package/src/__tests__/format-text-registry.test.ts +173 -0
- package/src/__tests__/issue-180-workflow-ref-removed.test.ts +43 -0
- package/src/__tests__/log-text-renderer.test.ts +294 -0
- package/src/__tests__/moderator-evaluate.test.ts +9 -52
- package/src/__tests__/output-mapper-thread-list-startedat.test.ts +124 -0
- package/src/__tests__/output-mapper-workflow-add.test.ts +24 -0
- package/src/__tests__/pid-recycling.test.ts +329 -0
- package/src/__tests__/prompt.test.ts +443 -2
- package/src/__tests__/resolve-head-hash.test.ts +11 -4
- package/src/__tests__/setup-agent-discovery.test.ts +26 -51
- package/src/__tests__/setup-complexity.test.ts +1 -203
- package/src/__tests__/setup-no-llm.test.ts +68 -0
- package/src/__tests__/solve-issue-tea-worktree.test.ts +27 -31
- package/src/__tests__/step-ask.test.ts +677 -0
- package/src/__tests__/step-show-json.test.ts +1 -0
- package/src/__tests__/step-timing.test.ts +2 -0
- package/src/__tests__/store-global-cas.test.ts +2 -2
- package/src/__tests__/store-unified-threads.test.ts +30 -27
- package/src/__tests__/thread-cancel-status.test.ts +27 -20
- package/src/__tests__/thread-cancel-text-renderer.test.ts +125 -0
- package/src/__tests__/thread-list-filters.test.ts +443 -17
- package/src/__tests__/thread-list-template-ms-date.test.ts +110 -0
- package/src/__tests__/thread-list-workflow-corrupt.test.ts +198 -0
- package/src/__tests__/thread-poke.test.ts +554 -0
- package/src/__tests__/thread-read-xml-tags.test.ts +9 -11
- package/src/__tests__/thread-resume.test.ts +20 -15
- package/src/__tests__/thread-show-status.test.ts +17 -29
- package/src/__tests__/thread-start-cwd-cli.test.ts +15 -3
- package/src/__tests__/thread-stop-text-renderer.test.ts +168 -0
- package/src/__tests__/thread-suspend-step.test.ts +13 -16
- package/src/__tests__/thread-suspended-display.test.ts +10 -22
- package/src/__tests__/thread-test-helpers.ts +15 -1
- package/src/__tests__/thread.test.ts +14 -14
- package/src/__tests__/validate-semantic.test.ts +118 -33
- package/src/__tests__/workflow-list-recursive.test.ts +370 -0
- package/src/__tests__/workflow-resolution.test.ts +48 -29
- package/src/__tests__/workflow-show-resolution.test.ts +286 -0
- package/src/__tests__/workflow-validate.test.ts +828 -0
- package/src/__tests__/write-envelope.test.ts +257 -0
- package/src/background/background.ts +88 -6
- package/src/background/index.ts +2 -0
- package/src/background/types.ts +1 -0
- package/src/cli.ts +184 -77
- package/src/commands/config.ts +16 -33
- package/src/commands/prompt.ts +57 -31
- package/src/commands/setup.ts +80 -358
- package/src/commands/step.ts +339 -12
- package/src/commands/thread.ts +511 -171
- package/src/commands/workflow.ts +155 -4
- package/src/concurrency/concurrency.ts +245 -0
- package/src/concurrency/index.ts +10 -0
- package/src/concurrency/types.ts +19 -0
- package/src/format.ts +282 -2
- package/src/moderator/__tests__/evaluate.test.ts +34 -17
- package/src/moderator/evaluate.ts +5 -17
- package/src/moderator/index.ts +1 -6
- package/src/moderator/types.ts +6 -14
- package/src/output-mappers.ts +254 -0
- package/src/schemas.ts +51 -5
- package/src/store.ts +86 -20
- package/src/text-renderers.ts +355 -0
- package/src/validate-semantic.ts +125 -73
- package/src/validate.ts +27 -0
- package/dist/__tests__/setup-validate.test.d.ts +0 -2
- package/dist/__tests__/setup-validate.test.d.ts.map +0 -1
- package/dist/__tests__/setup-validate.test.js +0 -108
- package/dist/__tests__/setup-validate.test.js.map +0 -1
- package/src/__tests__/setup-validate.test.ts +0 -148
- /package/src/__tests__/fixtures/{e2e-mustache.mock.yaml → e2e-liquid.mock.yaml} +0 -0
package/src/format.ts
CHANGED
|
@@ -1,12 +1,292 @@
|
|
|
1
|
+
import type { Hash, Store } from "@ocas/core";
|
|
2
|
+
import type { OutputSchemaName } from "@united-workforce/protocol";
|
|
3
|
+
import { Liquid } from "liquidjs";
|
|
1
4
|
import { stringify } from "yaml";
|
|
5
|
+
import type { UwfSchemaHashes } from "./schemas.js";
|
|
6
|
+
import {
|
|
7
|
+
renderConfigGet,
|
|
8
|
+
renderConfigList,
|
|
9
|
+
renderConfigSet,
|
|
10
|
+
renderLogList,
|
|
11
|
+
renderLogShow,
|
|
12
|
+
renderStepList,
|
|
13
|
+
renderStepShow,
|
|
14
|
+
renderThreadCancel,
|
|
15
|
+
renderThreadList,
|
|
16
|
+
renderThreadShow,
|
|
17
|
+
renderThreadStart,
|
|
18
|
+
renderThreadStop,
|
|
19
|
+
renderWorkflowList,
|
|
20
|
+
renderWorkflowShow,
|
|
21
|
+
} from "./text-renderers.js";
|
|
2
22
|
|
|
3
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Five output formats supported by the uwf CLI. `text` is the default and
|
|
25
|
+
* produces a Liquid-rendered human-readable view. `json` and `yaml` wrap the
|
|
26
|
+
* payload in an ocas envelope `{ type, value }` for self-describing output.
|
|
27
|
+
* `raw-json` and `raw-yaml` emit the bare value, preserving 0.5.0 byte-for-byte
|
|
28
|
+
* output for backward-compat consumers.
|
|
29
|
+
*/
|
|
30
|
+
export type OutputFormat = "text" | "json" | "yaml" | "raw-json" | "raw-yaml";
|
|
4
31
|
|
|
5
|
-
export
|
|
32
|
+
export const SUPPORTED_FORMATS: readonly OutputFormat[] = [
|
|
33
|
+
"text",
|
|
34
|
+
"json",
|
|
35
|
+
"yaml",
|
|
36
|
+
"raw-json",
|
|
37
|
+
"raw-yaml",
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
export function isOutputFormat(v: string): v is OutputFormat {
|
|
41
|
+
return (SUPPORTED_FORMATS as readonly string[]).includes(v);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Per-command text renderer registry. Maps a fully-qualified command path
|
|
46
|
+
* (e.g. `"thread list"`, `"workflow show"`) to a function that converts
|
|
47
|
+
* the command's payload into a human-readable string.
|
|
48
|
+
*
|
|
49
|
+
* Renderers must:
|
|
50
|
+
* - Always return a `string` (never `undefined`).
|
|
51
|
+
* - Tolerate missing/null fields without throwing.
|
|
52
|
+
*
|
|
53
|
+
* The Liquid template path inside `writeEnvelope` is the primary rendering
|
|
54
|
+
* implementation. This registry exists so callers without a CAS store
|
|
55
|
+
* (tests, library consumers) can resolve `text` rendering, and so
|
|
56
|
+
* `formatOutput(data, "text", commandPath)` returns a meaningful string.
|
|
57
|
+
*/
|
|
58
|
+
export type TextRenderer = (data: unknown) => string;
|
|
59
|
+
|
|
60
|
+
export const TEXT_RENDERERS: Record<string, TextRenderer> = {
|
|
61
|
+
"thread list": renderThreadList,
|
|
62
|
+
"thread show": renderThreadShow,
|
|
63
|
+
"thread start": renderThreadStart,
|
|
64
|
+
"thread cancel": renderThreadCancel,
|
|
65
|
+
"thread stop": renderThreadStop,
|
|
66
|
+
"workflow list": renderWorkflowList,
|
|
67
|
+
"workflow show": renderWorkflowShow,
|
|
68
|
+
"step list": renderStepList,
|
|
69
|
+
"step show": renderStepShow,
|
|
70
|
+
"config list": renderConfigList,
|
|
71
|
+
"config get": renderConfigGet,
|
|
72
|
+
"config set": renderConfigSet,
|
|
73
|
+
"log list": renderLogList,
|
|
74
|
+
"log show": renderLogShow,
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/** Look up a registered text renderer by command path. */
|
|
78
|
+
export function getTextRenderer(commandPath: string): TextRenderer | undefined {
|
|
79
|
+
return TEXT_RENDERERS[commandPath];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Register (or override) a text renderer for a command path. */
|
|
83
|
+
export function registerTextRenderer(commandPath: string, renderer: TextRenderer): void {
|
|
84
|
+
TEXT_RENDERERS[commandPath] = renderer;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Format a payload as a string in the requested output format.
|
|
89
|
+
*
|
|
90
|
+
* For `"text"`, `formatOutput` looks up the registered renderer for
|
|
91
|
+
* `commandPath` (when provided) and falls back to a JSON serialization when
|
|
92
|
+
* no renderer is registered. The result is always a `string` — never
|
|
93
|
+
* `undefined`. For `"json"` and `"yaml"` the bare value is serialized.
|
|
94
|
+
* For `"raw-json"` and `"raw-yaml"` the output is identical to `"json"` /
|
|
95
|
+
* `"yaml"` (both modes emit the bare value; envelope wrapping happens in
|
|
96
|
+
* `writeEnvelope`).
|
|
97
|
+
*
|
|
98
|
+
* Note: this is the legacy in-process formatter used by raw output paths
|
|
99
|
+
* (`thread cancel`, `step fork`, `setup`, `log/config`) and tests. Production
|
|
100
|
+
* commands with a registered output schema go through `writeEnvelope`.
|
|
101
|
+
*/
|
|
102
|
+
export function formatOutput(data: unknown, format: OutputFormat, commandPath?: string): string {
|
|
6
103
|
switch (format) {
|
|
7
104
|
case "json":
|
|
105
|
+
case "raw-json":
|
|
8
106
|
return JSON.stringify(data);
|
|
9
107
|
case "yaml":
|
|
108
|
+
case "raw-yaml":
|
|
10
109
|
return stringify(data, { aliasDuplicateObjects: false }).trimEnd();
|
|
110
|
+
case "text": {
|
|
111
|
+
if (commandPath !== undefined) {
|
|
112
|
+
const renderer = TEXT_RENDERERS[commandPath];
|
|
113
|
+
if (renderer !== undefined) {
|
|
114
|
+
return renderer(data);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Fallback: JSON pretty-printed so `formatOutput(_, "text")` never returns
|
|
118
|
+
// `"undefined"` (the bug from issue #327).
|
|
119
|
+
return JSON.stringify(data, null, 2);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const schemaHashCache = new Map<OutputSchemaName, Hash>();
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Resolve the CAS hash for an output schema by short name, caching the result
|
|
128
|
+
* for the lifetime of the process.
|
|
129
|
+
*/
|
|
130
|
+
export function resolveOutputSchemaHash(
|
|
131
|
+
outputs: Record<OutputSchemaName, Hash>,
|
|
132
|
+
schemaName: OutputSchemaName,
|
|
133
|
+
): Hash {
|
|
134
|
+
const cached = schemaHashCache.get(schemaName);
|
|
135
|
+
if (cached !== undefined) return cached;
|
|
136
|
+
const hash = outputs[schemaName];
|
|
137
|
+
if (hash === undefined) {
|
|
138
|
+
throw new Error(`output schema not registered: @uwf/output/${schemaName}`);
|
|
139
|
+
}
|
|
140
|
+
schemaHashCache.set(schemaName, hash);
|
|
141
|
+
return hash;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export type WriteEnvelopeOptions = {
|
|
145
|
+
format: OutputFormat;
|
|
146
|
+
store: Store;
|
|
147
|
+
schemas: UwfSchemaHashes;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Wrap a CLI command payload in the chosen format and write it to stdout.
|
|
152
|
+
*
|
|
153
|
+
* - `text` → Liquid template at `@ocas/template/text/<hash>` (fallback YAML envelope)
|
|
154
|
+
* - `json` → `{"type":<hash>,"value":<payload>}` (envelope JSON)
|
|
155
|
+
* - `yaml` → envelope as multi-line YAML
|
|
156
|
+
* - `raw-json` → bare `<payload>` (legacy 0.5.0 shape)
|
|
157
|
+
* - `raw-yaml` → bare `<payload>` (legacy 0.5.0 shape)
|
|
158
|
+
*/
|
|
159
|
+
export async function writeEnvelope(
|
|
160
|
+
payload: unknown,
|
|
161
|
+
schemaName: OutputSchemaName,
|
|
162
|
+
options: WriteEnvelopeOptions,
|
|
163
|
+
): Promise<void> {
|
|
164
|
+
const { format, store, schemas } = options;
|
|
165
|
+
const schemaHash = resolveOutputSchemaHash(schemas.outputs, schemaName);
|
|
166
|
+
|
|
167
|
+
let body: string;
|
|
168
|
+
switch (format) {
|
|
169
|
+
case "json":
|
|
170
|
+
body = JSON.stringify({ type: schemaHash, value: payload });
|
|
171
|
+
break;
|
|
172
|
+
case "yaml":
|
|
173
|
+
body = stringify(
|
|
174
|
+
{ type: schemaHash, value: payload },
|
|
175
|
+
{ aliasDuplicateObjects: false },
|
|
176
|
+
).trimEnd();
|
|
177
|
+
break;
|
|
178
|
+
case "raw-json":
|
|
179
|
+
body = JSON.stringify(payload);
|
|
180
|
+
break;
|
|
181
|
+
case "raw-yaml":
|
|
182
|
+
body = stringify(payload, { aliasDuplicateObjects: false }).trimEnd();
|
|
183
|
+
break;
|
|
184
|
+
case "text":
|
|
185
|
+
body = await renderEnvelopeText(store, schemaHash, payload, schemaName);
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
process.stdout.write(`${body}\n`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
let liquidEngine: Liquid | null = null;
|
|
193
|
+
|
|
194
|
+
type GraphMap = Record<string, Record<string, { role?: string }>>;
|
|
195
|
+
|
|
196
|
+
function firstRole(graph: GraphMap, current: string): string | null {
|
|
197
|
+
const transitions = graph[current];
|
|
198
|
+
if (!transitions) return null;
|
|
199
|
+
const firstKey = Object.keys(transitions)[0];
|
|
200
|
+
if (firstKey === undefined) return null;
|
|
201
|
+
const next = transitions[firstKey]?.role;
|
|
202
|
+
return typeof next === "string" ? next : null;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function buildGraphPath(graph: GraphMap, start: string, limit: number): string[] {
|
|
206
|
+
const out: string[] = [start];
|
|
207
|
+
const seen = new Set<string>([start]);
|
|
208
|
+
let cur = start;
|
|
209
|
+
while (out.length < limit) {
|
|
210
|
+
const next = firstRole(graph, cur);
|
|
211
|
+
if (next === null || next === "$END") {
|
|
212
|
+
out.push("$END");
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
if (seen.has(next)) break;
|
|
216
|
+
seen.add(next);
|
|
217
|
+
out.push(next);
|
|
218
|
+
cur = next;
|
|
219
|
+
}
|
|
220
|
+
return out;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function getLiquidEngine(): Liquid {
|
|
224
|
+
if (liquidEngine !== null) return liquidEngine;
|
|
225
|
+
const engine = new Liquid({ cache: false, strictFilters: false, strictVariables: false });
|
|
226
|
+
engine.registerFilter("keys", (input: unknown) =>
|
|
227
|
+
input !== null && typeof input === "object" ? Object.keys(input as object) : [],
|
|
228
|
+
);
|
|
229
|
+
engine.registerFilter("graph_path", (graph: unknown, start: unknown, max: unknown): string[] => {
|
|
230
|
+
if (graph === null || typeof graph !== "object") return [];
|
|
231
|
+
const limit = typeof max === "number" ? max : 5;
|
|
232
|
+
const startNode = typeof start === "string" ? start : "$START";
|
|
233
|
+
return buildGraphPath(graph as GraphMap, startNode, limit);
|
|
234
|
+
});
|
|
235
|
+
liquidEngine = engine;
|
|
236
|
+
return engine;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function readTemplateContent(store: Store, schemaHash: Hash): string | null {
|
|
240
|
+
const varName = `@ocas/template/text/${schemaHash}`;
|
|
241
|
+
const variable = store.var.get(varName);
|
|
242
|
+
if (variable === null) return null;
|
|
243
|
+
const node = store.cas.get(variable.value);
|
|
244
|
+
if (node === null) return null;
|
|
245
|
+
if (typeof node.payload !== "string") return null;
|
|
246
|
+
return node.payload;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function buildLiquidContext(payload: unknown, schemaHash: Hash): Record<string, unknown> {
|
|
250
|
+
const ctx: Record<string, unknown> = { payload, type: schemaHash };
|
|
251
|
+
if (payload !== null && typeof payload === "object" && !Array.isArray(payload)) {
|
|
252
|
+
for (const [k, v] of Object.entries(payload)) {
|
|
253
|
+
if (k !== "payload" && k !== "type") {
|
|
254
|
+
ctx[k] = v;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return ctx;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
async function renderEnvelopeText(
|
|
262
|
+
store: Store,
|
|
263
|
+
schemaHash: Hash,
|
|
264
|
+
payload: unknown,
|
|
265
|
+
schemaName: OutputSchemaName,
|
|
266
|
+
): Promise<string> {
|
|
267
|
+
const template = readTemplateContent(store, schemaHash);
|
|
268
|
+
if (template === null) {
|
|
269
|
+
process.stderr.write(
|
|
270
|
+
`warning: missing text template for @uwf/output/${schemaName} (var @ocas/template/text/${schemaHash}); falling back to YAML\n`,
|
|
271
|
+
);
|
|
272
|
+
return stringify(
|
|
273
|
+
{ type: schemaHash, value: payload },
|
|
274
|
+
{ aliasDuplicateObjects: false },
|
|
275
|
+
).trimEnd();
|
|
276
|
+
}
|
|
277
|
+
try {
|
|
278
|
+
const engine = getLiquidEngine();
|
|
279
|
+
const context = buildLiquidContext(payload, schemaHash);
|
|
280
|
+
const out = await engine.parseAndRender(template, context);
|
|
281
|
+
return out.replace(/\n+$/, "");
|
|
282
|
+
} catch (e) {
|
|
283
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
284
|
+
process.stderr.write(
|
|
285
|
+
`warning: failed to render text template for @uwf/output/${schemaName}: ${message}; falling back to YAML\n`,
|
|
286
|
+
);
|
|
287
|
+
return stringify(
|
|
288
|
+
{ type: schemaHash, value: payload },
|
|
289
|
+
{ aliasDuplicateObjects: false },
|
|
290
|
+
).trimEnd();
|
|
11
291
|
}
|
|
12
292
|
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { describe, expect, test } from "vitest";
|
|
2
2
|
import { evaluate } from "../evaluate.js";
|
|
3
|
-
import { isSuspendResult } from "../types.js";
|
|
4
3
|
|
|
5
4
|
describe("Edge prompt template variable resolution", () => {
|
|
6
5
|
test("returns error when rendered prompt is empty string", () => {
|
|
7
6
|
const graph = {
|
|
8
7
|
$START: {
|
|
9
|
-
new: { role: "classifier", prompt: "{{
|
|
8
|
+
new: { role: "classifier", prompt: "{{ userPrompt }}", location: null },
|
|
10
9
|
},
|
|
11
10
|
};
|
|
12
11
|
|
|
@@ -22,7 +21,7 @@ describe("Edge prompt template variable resolution", () => {
|
|
|
22
21
|
test("returns error when rendered prompt is whitespace-only", () => {
|
|
23
22
|
const graph = {
|
|
24
23
|
$START: {
|
|
25
|
-
new: { role: "classifier", prompt: " {{
|
|
24
|
+
new: { role: "classifier", prompt: " {{ userPrompt }} ", location: null },
|
|
26
25
|
},
|
|
27
26
|
};
|
|
28
27
|
|
|
@@ -38,7 +37,7 @@ describe("Edge prompt template variable resolution", () => {
|
|
|
38
37
|
test("succeeds when all template variables resolve to non-empty values", () => {
|
|
39
38
|
const graph = {
|
|
40
39
|
$START: {
|
|
41
|
-
new: { role: "classifier", prompt: "{{
|
|
40
|
+
new: { role: "classifier", prompt: "{{ userPrompt }}", location: null },
|
|
42
41
|
},
|
|
43
42
|
};
|
|
44
43
|
|
|
@@ -68,7 +67,7 @@ describe("Edge prompt template variable resolution", () => {
|
|
|
68
67
|
test("succeeds when prompt has mix of static text and unresolved variables", () => {
|
|
69
68
|
const graph = {
|
|
70
69
|
$START: {
|
|
71
|
-
new: { role: "classifier", prompt: "Please handle: {{
|
|
70
|
+
new: { role: "classifier", prompt: "Please handle: {{ userPrompt }}", location: null },
|
|
72
71
|
},
|
|
73
72
|
};
|
|
74
73
|
|
|
@@ -83,7 +82,7 @@ describe("Edge prompt template variable resolution", () => {
|
|
|
83
82
|
test("returns error when ALL variables missing and no static text remains", () => {
|
|
84
83
|
const graph = {
|
|
85
84
|
$START: {
|
|
86
|
-
new: { role: "classifier", prompt: "{{
|
|
85
|
+
new: { role: "classifier", prompt: "{{ a }}{{ b }}", location: null },
|
|
87
86
|
},
|
|
88
87
|
};
|
|
89
88
|
|
|
@@ -91,6 +90,24 @@ describe("Edge prompt template variable resolution", () => {
|
|
|
91
90
|
|
|
92
91
|
expect(result.ok).toBe(false);
|
|
93
92
|
});
|
|
93
|
+
|
|
94
|
+
test("does not HTML-escape characters like <, >, &", () => {
|
|
95
|
+
const graph = {
|
|
96
|
+
$START: {
|
|
97
|
+
new: { role: "classifier", prompt: "{{ content }}", location: null },
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const result = evaluate(graph, "$START", {
|
|
102
|
+
$status: "new",
|
|
103
|
+
content: "<div>Hello & welcome</div>",
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
expect(result.ok).toBe(true);
|
|
107
|
+
if (result.ok) {
|
|
108
|
+
expect(result.value.prompt).toBe("<div>Hello & welcome</div>");
|
|
109
|
+
}
|
|
110
|
+
});
|
|
94
111
|
});
|
|
95
112
|
|
|
96
113
|
describe("Moderator location resolution", () => {
|
|
@@ -108,7 +125,7 @@ describe("Moderator location resolution", () => {
|
|
|
108
125
|
const result = evaluate(graph, "planner", { $status: "ready" });
|
|
109
126
|
|
|
110
127
|
expect(result.ok).toBe(true);
|
|
111
|
-
if (result.ok
|
|
128
|
+
if (result.ok) {
|
|
112
129
|
expect(result.value.location).toBe(null);
|
|
113
130
|
}
|
|
114
131
|
});
|
|
@@ -127,18 +144,18 @@ describe("Moderator location resolution", () => {
|
|
|
127
144
|
const result = evaluate(graph, "planner", { $status: "ready" });
|
|
128
145
|
|
|
129
146
|
expect(result.ok).toBe(true);
|
|
130
|
-
if (result.ok
|
|
147
|
+
if (result.ok) {
|
|
131
148
|
expect(result.value.location).toBe("/static/path");
|
|
132
149
|
}
|
|
133
150
|
});
|
|
134
151
|
|
|
135
|
-
test("resolves
|
|
152
|
+
test("resolves liquid template location", () => {
|
|
136
153
|
const graph = {
|
|
137
154
|
planner: {
|
|
138
155
|
ready: {
|
|
139
156
|
role: "coder",
|
|
140
157
|
prompt: "Implement the code",
|
|
141
|
-
location: "{{
|
|
158
|
+
location: "{{ repoPath }}",
|
|
142
159
|
},
|
|
143
160
|
},
|
|
144
161
|
};
|
|
@@ -149,18 +166,18 @@ describe("Moderator location resolution", () => {
|
|
|
149
166
|
});
|
|
150
167
|
|
|
151
168
|
expect(result.ok).toBe(true);
|
|
152
|
-
if (result.ok
|
|
169
|
+
if (result.ok) {
|
|
153
170
|
expect(result.value.location).toBe("/home/user/repo");
|
|
154
171
|
}
|
|
155
172
|
});
|
|
156
173
|
|
|
157
|
-
test("resolves
|
|
174
|
+
test("resolves liquid template with multiple variables", () => {
|
|
158
175
|
const graph = {
|
|
159
176
|
planner: {
|
|
160
177
|
ready: {
|
|
161
178
|
role: "coder",
|
|
162
179
|
prompt: "Implement the code",
|
|
163
|
-
location: "{{
|
|
180
|
+
location: "{{ basePath }}/{{ projectName }}",
|
|
164
181
|
},
|
|
165
182
|
},
|
|
166
183
|
};
|
|
@@ -172,7 +189,7 @@ describe("Moderator location resolution", () => {
|
|
|
172
189
|
});
|
|
173
190
|
|
|
174
191
|
expect(result.ok).toBe(true);
|
|
175
|
-
if (result.ok
|
|
192
|
+
if (result.ok) {
|
|
176
193
|
expect(result.value.location).toBe("/home/user/myproject");
|
|
177
194
|
}
|
|
178
195
|
});
|
|
@@ -183,7 +200,7 @@ describe("Moderator location resolution", () => {
|
|
|
183
200
|
ready: {
|
|
184
201
|
role: "coder",
|
|
185
202
|
prompt: "Implement the code",
|
|
186
|
-
location: "{{
|
|
203
|
+
location: "{{ repoPath }}",
|
|
187
204
|
},
|
|
188
205
|
},
|
|
189
206
|
};
|
|
@@ -191,8 +208,8 @@ describe("Moderator location resolution", () => {
|
|
|
191
208
|
const result = evaluate(graph, "planner", { $status: "ready" });
|
|
192
209
|
|
|
193
210
|
expect(result.ok).toBe(true);
|
|
194
|
-
if (result.ok
|
|
195
|
-
//
|
|
211
|
+
if (result.ok) {
|
|
212
|
+
// LiquidJS renders missing variables as empty string
|
|
196
213
|
expect(result.value.location).toBe("");
|
|
197
214
|
}
|
|
198
215
|
});
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import type { Target } from "@united-workforce/protocol";
|
|
2
|
-
import
|
|
2
|
+
import { Liquid } from "liquidjs";
|
|
3
3
|
|
|
4
4
|
import type { EvaluateResult, Result } from "./types.js";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
mustache.escape = (text: string) => text;
|
|
8
|
-
|
|
9
|
-
const SUSPEND_ROLE = "$SUSPEND";
|
|
6
|
+
const engine = new Liquid();
|
|
10
7
|
|
|
11
8
|
type LastOutput = Record<string, unknown>;
|
|
12
9
|
|
|
@@ -44,7 +41,7 @@ export function evaluate(
|
|
|
44
41
|
}
|
|
45
42
|
|
|
46
43
|
try {
|
|
47
|
-
const prompt =
|
|
44
|
+
const prompt = engine.parseAndRenderSync(target.prompt, lastOutput);
|
|
48
45
|
if (prompt.trim() === "") {
|
|
49
46
|
return {
|
|
50
47
|
ok: false,
|
|
@@ -53,18 +50,9 @@ export function evaluate(
|
|
|
53
50
|
),
|
|
54
51
|
};
|
|
55
52
|
}
|
|
56
|
-
if (target.role === SUSPEND_ROLE) {
|
|
57
|
-
return {
|
|
58
|
-
ok: true,
|
|
59
|
-
value: {
|
|
60
|
-
action: "suspend",
|
|
61
|
-
suspendedRole: lastRole,
|
|
62
|
-
prompt,
|
|
63
|
-
},
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
53
|
|
|
67
|
-
const location =
|
|
54
|
+
const location =
|
|
55
|
+
target.location !== null ? engine.parseAndRenderSync(target.location, lastOutput) : null;
|
|
68
56
|
return { ok: true, value: { role: target.role, prompt, location } };
|
|
69
57
|
} catch (error) {
|
|
70
58
|
return {
|
package/src/moderator/index.ts
CHANGED
|
@@ -1,7 +1,2 @@
|
|
|
1
1
|
export { evaluate } from "./evaluate.js";
|
|
2
|
-
export type {
|
|
3
|
-
EvaluateResult,
|
|
4
|
-
EvaluateRouteResult,
|
|
5
|
-
EvaluateSuspendResult,
|
|
6
|
-
} from "./types.js";
|
|
7
|
-
export { isSuspendResult } from "./types.js";
|
|
2
|
+
export type { EvaluateResult, EvaluateRouteResult } from "./types.js";
|
package/src/moderator/types.ts
CHANGED
|
@@ -8,17 +8,9 @@ export type EvaluateRouteResult = {
|
|
|
8
8
|
location: string | null;
|
|
9
9
|
};
|
|
10
10
|
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
/** The result of moderator evaluation. */
|
|
20
|
-
export type EvaluateResult = EvaluateRouteResult | EvaluateSuspendResult;
|
|
21
|
-
|
|
22
|
-
export function isSuspendResult(result: EvaluateResult): result is EvaluateSuspendResult {
|
|
23
|
-
return "action" in result && result.action === "suspend";
|
|
24
|
-
}
|
|
11
|
+
/**
|
|
12
|
+
* The result of moderator evaluation. `$SUSPEND` is no longer a moderator
|
|
13
|
+
* concern — it is an engine-level reserved `$status` intercepted before the
|
|
14
|
+
* moderator runs.
|
|
15
|
+
*/
|
|
16
|
+
export type EvaluateResult = EvaluateRouteResult;
|