@probelabs/visor 0.1.107 → 0.1.112
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 +6 -0
- package/defaults/task-refinement.yaml +7 -3
- package/defaults/visor.tests.yaml +13 -2
- package/defaults/visor.yaml +1 -0
- package/dist/663.index.js +3 -2
- package/dist/80.index.js +3 -2
- package/dist/ai-review-service.d.ts +13 -9
- package/dist/ai-review-service.d.ts.map +1 -1
- package/dist/cli-main.d.ts.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/debug-visualizer/ws-server.d.ts +7 -1
- package/dist/debug-visualizer/ws-server.d.ts.map +1 -1
- package/dist/defaults/task-refinement.yaml +7 -3
- package/dist/defaults/visor.tests.yaml +13 -2
- package/dist/defaults/visor.yaml +1 -0
- package/dist/docs/advanced-ai.md +60 -1
- package/dist/docs/ai-configuration.md +67 -0
- package/dist/docs/ai-custom-tools-usage.md +261 -0
- package/dist/docs/ai-custom-tools.md +392 -0
- package/dist/docs/bot-transports-rfc.md +23 -0
- package/dist/docs/configuration.md +21 -0
- package/dist/docs/engine-pause-resume-rfc.md +192 -0
- package/dist/docs/lifecycle-hooks.md +253 -0
- package/dist/docs/liquid-templates.md +143 -0
- package/dist/docs/providers/git-checkout.md +589 -0
- package/dist/docs/recipes.md +458 -5
- package/dist/docs/rfc/git-checkout-step.md +601 -0
- package/dist/docs/rfc/on_init-hook.md +1294 -0
- package/dist/docs/rfc/workspace-isolation.md +216 -0
- package/dist/docs/router-patterns.md +339 -0
- package/dist/event-bus/types.d.ts +14 -0
- package/dist/event-bus/types.d.ts.map +1 -1
- package/dist/examples/ai-custom-tools-example.yaml +206 -0
- package/dist/examples/ai-custom-tools-simple.yaml +76 -0
- package/dist/examples/git-checkout-basic.yaml +32 -0
- package/dist/examples/git-checkout-compare.yaml +59 -0
- package/dist/examples/git-checkout-cross-repo.yaml +76 -0
- package/dist/examples/on-init-import-demo.yaml +179 -0
- package/dist/examples/reusable-tools.yaml +92 -0
- package/dist/examples/reusable-workflows.yaml +88 -0
- package/dist/examples/session-reuse-self.yaml +81 -0
- package/dist/examples/slack-simple-chat.yaml +775 -0
- package/dist/failure-condition-evaluator.d.ts +2 -0
- package/dist/failure-condition-evaluator.d.ts.map +1 -1
- package/dist/frontends/github-frontend.d.ts +20 -0
- package/dist/frontends/github-frontend.d.ts.map +1 -1
- package/dist/frontends/host.d.ts +4 -0
- package/dist/frontends/host.d.ts.map +1 -1
- package/dist/frontends/slack-frontend.d.ts +58 -0
- package/dist/frontends/slack-frontend.d.ts.map +1 -0
- package/dist/generated/config-schema.d.ts +409 -41
- package/dist/generated/config-schema.d.ts.map +1 -1
- package/dist/generated/config-schema.json +436 -47
- package/dist/github-comments.d.ts +2 -0
- package/dist/github-comments.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +83587 -56085
- package/dist/liquid-extensions.d.ts.map +1 -1
- package/dist/logger.d.ts +1 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/output/traces/{run-2025-11-21T11-50-46-505Z.ndjson → run-2026-01-21T05-37-24-446Z.ndjson} +91 -91
- package/dist/output/traces/run-2026-01-21T05-38-18-580Z.ndjson +1067 -0
- package/dist/output-formatters.d.ts.map +1 -1
- package/dist/providers/ai-check-provider.d.ts +12 -0
- package/dist/providers/ai-check-provider.d.ts.map +1 -1
- package/dist/providers/check-provider-registry.d.ts.map +1 -1
- package/dist/providers/check-provider.interface.d.ts +9 -0
- package/dist/providers/check-provider.interface.d.ts.map +1 -1
- package/dist/providers/command-check-provider.d.ts.map +1 -1
- package/dist/providers/custom-tool-executor.d.ts.map +1 -1
- package/dist/providers/git-checkout-provider.d.ts +25 -0
- package/dist/providers/git-checkout-provider.d.ts.map +1 -0
- package/dist/providers/http-client-provider.d.ts +3 -0
- package/dist/providers/http-client-provider.d.ts.map +1 -1
- package/dist/providers/human-input-check-provider.d.ts +2 -0
- package/dist/providers/human-input-check-provider.d.ts.map +1 -1
- package/dist/providers/log-check-provider.d.ts.map +1 -1
- package/dist/providers/mcp-check-provider.d.ts +1 -1
- package/dist/providers/mcp-check-provider.d.ts.map +1 -1
- package/dist/providers/mcp-custom-sse-server.d.ts +66 -0
- package/dist/providers/mcp-custom-sse-server.d.ts.map +1 -0
- package/dist/providers/memory-check-provider.d.ts.map +1 -1
- package/dist/providers/script-check-provider.d.ts.map +1 -1
- package/dist/providers/workflow-check-provider.d.ts.map +1 -1
- package/dist/reviewer.d.ts.map +1 -1
- package/dist/sdk/check-provider-registry-534KL5HT.mjs +27 -0
- package/dist/sdk/chunk-23L3QRYX.mjs +16872 -0
- package/dist/sdk/chunk-23L3QRYX.mjs.map +1 -0
- package/dist/sdk/{chunk-OOZITMRU.mjs → chunk-3OMWVM6J.mjs} +11 -1
- package/dist/sdk/{chunk-OOZITMRU.mjs.map → chunk-3OMWVM6J.mjs.map} +1 -1
- package/dist/sdk/{chunk-37ZSCMFC.mjs → chunk-7UK3NIIT.mjs} +2 -2
- package/dist/sdk/{chunk-VMPLF6FT.mjs → chunk-AGIZJ4UZ.mjs} +50 -4
- package/dist/sdk/chunk-AGIZJ4UZ.mjs.map +1 -0
- package/dist/sdk/{chunk-IEO6CFLG.mjs → chunk-AIVFBIS4.mjs} +161 -5
- package/dist/sdk/chunk-AIVFBIS4.mjs.map +1 -0
- package/dist/sdk/chunk-AK6BVWIT.mjs +426 -0
- package/dist/sdk/chunk-AK6BVWIT.mjs.map +1 -0
- package/dist/sdk/chunk-AUT26LHW.mjs +139 -0
- package/dist/sdk/chunk-AUT26LHW.mjs.map +1 -0
- package/dist/sdk/chunk-BOVFH3LI.mjs +232 -0
- package/dist/sdk/chunk-BOVFH3LI.mjs.map +1 -0
- package/dist/sdk/chunk-HTOKWMPO.mjs +157 -0
- package/dist/sdk/chunk-HTOKWMPO.mjs.map +1 -0
- package/dist/sdk/{chunk-6Y4YTKCF.mjs → chunk-NAW3DB3I.mjs} +2 -2
- package/dist/sdk/{chunk-OWUVOILT.mjs → chunk-QR7MOMJH.mjs} +4 -3
- package/dist/sdk/{chunk-OWUVOILT.mjs.map → chunk-QR7MOMJH.mjs.map} +1 -1
- package/dist/sdk/{chunk-PTL3K3PN.mjs → chunk-QY2XYPEV.mjs} +488 -60
- package/dist/sdk/chunk-QY2XYPEV.mjs.map +1 -0
- package/dist/sdk/{chunk-OZJ263FM.mjs → chunk-SIWNBRTK.mjs} +29 -215
- package/dist/sdk/chunk-SIWNBRTK.mjs.map +1 -0
- package/dist/sdk/command-executor-TYUV6HUS.mjs +14 -0
- package/dist/sdk/{config-M4ZNO6NU.mjs → config-YNC2EOOT.mjs} +5 -3
- package/dist/sdk/{failure-condition-evaluator-NBO5YRXW.mjs → failure-condition-evaluator-YGTF2GHG.mjs} +6 -5
- package/dist/sdk/{github-frontend-4AWRJT7D.mjs → github-frontend-SIAEOCON.mjs} +190 -12
- package/dist/sdk/github-frontend-SIAEOCON.mjs.map +1 -0
- package/dist/sdk/{host-7GBC3S7L.mjs → host-DXUYTNMU.mjs} +5 -2
- package/dist/sdk/host-DXUYTNMU.mjs.map +1 -0
- package/dist/sdk/{liquid-extensions-C7EG3YKH.mjs → liquid-extensions-PKWCKK7E.mjs} +5 -4
- package/dist/sdk/memory-store-XGBB7LX7.mjs +12 -0
- package/dist/sdk/prompt-state-YRJY6QAL.mjs +16 -0
- package/dist/sdk/{renderer-schema-6RF26VUS.mjs → renderer-schema-LPKN5UJS.mjs} +3 -2
- package/dist/sdk/{renderer-schema-6RF26VUS.mjs.map → renderer-schema-LPKN5UJS.mjs.map} +1 -1
- package/dist/sdk/{routing-RP56JTV2.mjs → routing-6N45MJ4F.mjs} +7 -6
- package/dist/sdk/sdk.d.mts +219 -5
- package/dist/sdk/sdk.d.ts +219 -5
- package/dist/sdk/sdk.js +21329 -14908
- package/dist/sdk/sdk.js.map +1 -1
- package/dist/sdk/sdk.mjs +407 -12874
- package/dist/sdk/sdk.mjs.map +1 -1
- package/dist/sdk/{session-registry-N5FFYFTM.mjs → session-registry-4E6YRQ77.mjs} +2 -2
- package/dist/sdk/session-registry-4E6YRQ77.mjs.map +1 -0
- package/dist/sdk/slack-frontend-BVKW3GD5.mjs +735 -0
- package/dist/sdk/slack-frontend-BVKW3GD5.mjs.map +1 -0
- package/dist/sdk/{tracer-init-WP4X46IF.mjs → tracer-init-GSLPPLCD.mjs} +2 -2
- package/dist/sdk/tracer-init-GSLPPLCD.mjs.map +1 -0
- package/dist/sdk/workflow-registry-R6KSACFR.mjs +12 -0
- package/dist/sdk/workflow-registry-R6KSACFR.mjs.map +1 -0
- package/dist/slack/adapter.d.ts +36 -0
- package/dist/slack/adapter.d.ts.map +1 -0
- package/dist/slack/cache-prewarmer.d.ts +31 -0
- package/dist/slack/cache-prewarmer.d.ts.map +1 -0
- package/dist/slack/client.d.ts +77 -0
- package/dist/slack/client.d.ts.map +1 -0
- package/dist/slack/markdown.d.ts +45 -0
- package/dist/slack/markdown.d.ts.map +1 -0
- package/dist/slack/prompt-state.d.ts +33 -0
- package/dist/slack/prompt-state.d.ts.map +1 -0
- package/dist/slack/rate-limiter.d.ts +56 -0
- package/dist/slack/rate-limiter.d.ts.map +1 -0
- package/dist/slack/signature.d.ts +2 -0
- package/dist/slack/signature.d.ts.map +1 -0
- package/dist/slack/socket-runner.d.ts +42 -0
- package/dist/slack/socket-runner.d.ts.map +1 -0
- package/dist/slack/thread-cache.d.ts +51 -0
- package/dist/slack/thread-cache.d.ts.map +1 -0
- package/dist/state-machine/context/build-engine-context.d.ts +8 -0
- package/dist/state-machine/context/build-engine-context.d.ts.map +1 -1
- package/dist/state-machine/dispatch/execution-invoker.d.ts.map +1 -1
- package/dist/state-machine/dispatch/foreach-processor.d.ts.map +1 -1
- package/dist/state-machine/dispatch/on-init-handlers.d.ts +43 -0
- package/dist/state-machine/dispatch/on-init-handlers.d.ts.map +1 -0
- package/dist/state-machine/dispatch/stats-manager.d.ts.map +1 -1
- package/dist/state-machine/dispatch/template-renderer.d.ts.map +1 -1
- package/dist/state-machine/runner.d.ts +6 -0
- package/dist/state-machine/runner.d.ts.map +1 -1
- package/dist/state-machine/states/level-dispatch.d.ts.map +1 -1
- package/dist/state-machine/states/plan-ready.d.ts.map +1 -1
- package/dist/state-machine/states/routing.d.ts.map +1 -1
- package/dist/state-machine/states/wave-planning.d.ts.map +1 -1
- package/dist/state-machine/workflow-projection.d.ts.map +1 -1
- package/dist/state-machine-execution-engine.d.ts +21 -9
- package/dist/state-machine-execution-engine.d.ts.map +1 -1
- package/dist/telemetry/state-capture.d.ts +5 -0
- package/dist/telemetry/state-capture.d.ts.map +1 -1
- package/dist/test-runner/core/flow-stage.d.ts.map +1 -1
- package/dist/test-runner/core/test-execution-wrapper.d.ts.map +1 -1
- package/dist/test-runner/evaluators.d.ts +37 -4
- package/dist/test-runner/evaluators.d.ts.map +1 -1
- package/dist/test-runner/index.d.ts +7 -0
- package/dist/test-runner/index.d.ts.map +1 -1
- package/dist/test-runner/recorders/slack-recorder.d.ts +17 -0
- package/dist/test-runner/recorders/slack-recorder.d.ts.map +1 -0
- package/dist/test-runner/validator.d.ts.map +1 -1
- package/dist/traces/{run-2025-11-21T11-50-46-505Z.ndjson → run-2026-01-21T05-37-24-446Z.ndjson} +91 -91
- package/dist/traces/run-2026-01-21T05-38-18-580Z.ndjson +1067 -0
- package/dist/types/bot.d.ts +109 -0
- package/dist/types/bot.d.ts.map +1 -0
- package/dist/types/cli.d.ts +4 -0
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/config.d.ts +182 -5
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/engine.d.ts +5 -0
- package/dist/types/engine.d.ts.map +1 -1
- package/dist/types/git-checkout.d.ts +76 -0
- package/dist/types/git-checkout.d.ts.map +1 -0
- package/dist/utils/json-text-extractor.d.ts +17 -0
- package/dist/utils/json-text-extractor.d.ts.map +1 -0
- package/dist/utils/sandbox.d.ts +10 -0
- package/dist/utils/sandbox.d.ts.map +1 -1
- package/dist/utils/template-context.d.ts +1 -0
- package/dist/utils/template-context.d.ts.map +1 -1
- package/dist/utils/tracer-init.d.ts.map +1 -1
- package/dist/utils/workspace-manager.d.ts +118 -0
- package/dist/utils/workspace-manager.d.ts.map +1 -0
- package/dist/utils/worktree-cleanup.d.ts +33 -0
- package/dist/utils/worktree-cleanup.d.ts.map +1 -0
- package/dist/utils/worktree-manager.d.ts +153 -0
- package/dist/utils/worktree-manager.d.ts.map +1 -0
- package/dist/webhook-server.d.ts.map +1 -1
- package/dist/workflow-executor.d.ts.map +1 -1
- package/dist/workflow-registry.d.ts.map +1 -1
- package/package.json +5 -4
- package/dist/output/traces/run-2025-11-21T11-51-33-674Z.ndjson +0 -839
- package/dist/sdk/chunk-IEO6CFLG.mjs.map +0 -1
- package/dist/sdk/chunk-JEHPDJIF.mjs +0 -223
- package/dist/sdk/chunk-JEHPDJIF.mjs.map +0 -1
- package/dist/sdk/chunk-OZJ263FM.mjs.map +0 -1
- package/dist/sdk/chunk-PTL3K3PN.mjs.map +0 -1
- package/dist/sdk/chunk-VMPLF6FT.mjs.map +0 -1
- package/dist/sdk/github-frontend-4AWRJT7D.mjs.map +0 -1
- package/dist/sdk/host-7GBC3S7L.mjs.map +0 -1
- package/dist/sdk/memory-store-GJACZC2A.mjs +0 -11
- package/dist/sdk/workflow-registry-2YIIXQCK.mjs +0 -11
- package/dist/traces/run-2025-11-21T11-51-33-674Z.ndjson +0 -839
- /package/dist/sdk/{config-M4ZNO6NU.mjs.map → check-provider-registry-534KL5HT.mjs.map} +0 -0
- /package/dist/sdk/{chunk-37ZSCMFC.mjs.map → chunk-7UK3NIIT.mjs.map} +0 -0
- /package/dist/sdk/{chunk-6Y4YTKCF.mjs.map → chunk-NAW3DB3I.mjs.map} +0 -0
- /package/dist/sdk/{failure-condition-evaluator-NBO5YRXW.mjs.map → command-executor-TYUV6HUS.mjs.map} +0 -0
- /package/dist/sdk/{liquid-extensions-C7EG3YKH.mjs.map → config-YNC2EOOT.mjs.map} +0 -0
- /package/dist/sdk/{memory-store-GJACZC2A.mjs.map → failure-condition-evaluator-YGTF2GHG.mjs.map} +0 -0
- /package/dist/sdk/{routing-RP56JTV2.mjs.map → liquid-extensions-PKWCKK7E.mjs.map} +0 -0
- /package/dist/sdk/{session-registry-N5FFYFTM.mjs.map → memory-store-XGBB7LX7.mjs.map} +0 -0
- /package/dist/sdk/{tracer-init-WP4X46IF.mjs.map → prompt-state-YRJY6QAL.mjs.map} +0 -0
- /package/dist/sdk/{workflow-registry-2YIIXQCK.mjs.map → routing-6N45MJ4F.mjs.map} +0 -0
package/dist/docs/recipes.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
## 📚 Examples & Recipes
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
### Minimal `.visor.yaml` starter
|
|
4
4
|
```yaml
|
|
5
5
|
version: "1.0"
|
|
6
6
|
steps:
|
|
@@ -15,7 +15,460 @@ steps:
|
|
|
15
15
|
npx husky add .husky/pre-commit "npx -y @probelabs/visor@latest --tags local,fast --output table || exit 1"
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
### Chat-like workflows (human-input + ai)
|
|
19
|
+
|
|
20
|
+
Minimal chat loop (CLI/SDK):
|
|
21
|
+
|
|
22
|
+
```yaml
|
|
23
|
+
version: "1.0"
|
|
24
|
+
|
|
25
|
+
checks:
|
|
26
|
+
ask:
|
|
27
|
+
type: human-input
|
|
28
|
+
group: chat
|
|
29
|
+
prompt: |
|
|
30
|
+
Please type your message.
|
|
31
|
+
|
|
32
|
+
reply:
|
|
33
|
+
type: ai
|
|
34
|
+
group: chat
|
|
35
|
+
depends_on: ask
|
|
36
|
+
ai:
|
|
37
|
+
disableTools: true
|
|
38
|
+
allowedTools: []
|
|
39
|
+
system_prompt: "You are general assistant, follow user instructions."
|
|
40
|
+
prompt: |
|
|
41
|
+
You are a concise, friendly assistant.
|
|
42
|
+
|
|
43
|
+
Conversation so far (oldest → newest):
|
|
44
|
+
{% assign history = '' | chat_history: 'ask', 'reply' %}
|
|
45
|
+
{% for m in history %}
|
|
46
|
+
{{ m.role | capitalize }}: {{ m.text }}
|
|
47
|
+
{% endfor %}
|
|
48
|
+
|
|
49
|
+
Latest user message:
|
|
50
|
+
{{ outputs['ask'].text }}
|
|
51
|
+
|
|
52
|
+
Reply naturally. Keep it short (1–2 sentences).
|
|
53
|
+
|
|
54
|
+
guarantee: "(output?.text ?? '').length > 0"
|
|
55
|
+
on_success:
|
|
56
|
+
goto: ask
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Notes:
|
|
60
|
+
- `ask` (human-input) produces `{ text, ts }` by default.
|
|
61
|
+
- `reply` (ai) responds and loops back to `ask`.
|
|
62
|
+
- `chat_history('ask','reply')` merges both histories by timestamp with roles:
|
|
63
|
+
- `type: human-input` → `role: "user"`
|
|
64
|
+
- `type: ai` → `role: "assistant"`
|
|
65
|
+
|
|
66
|
+
Slack chat using the same pattern:
|
|
67
|
+
|
|
68
|
+
```yaml
|
|
69
|
+
version: "1.0"
|
|
70
|
+
|
|
71
|
+
slack:
|
|
72
|
+
version: "v1"
|
|
73
|
+
mentions: all
|
|
74
|
+
threads: required
|
|
75
|
+
|
|
76
|
+
frontends:
|
|
77
|
+
- name: slack
|
|
78
|
+
config:
|
|
79
|
+
summary:
|
|
80
|
+
enabled: false
|
|
81
|
+
|
|
82
|
+
checks:
|
|
83
|
+
ask:
|
|
84
|
+
type: human-input
|
|
85
|
+
group: chat
|
|
86
|
+
prompt: |
|
|
87
|
+
Please type your message. (Posted only when the workflow is waiting.)
|
|
88
|
+
|
|
89
|
+
reply:
|
|
90
|
+
type: ai
|
|
91
|
+
group: chat
|
|
92
|
+
depends_on: ask
|
|
93
|
+
ai:
|
|
94
|
+
disableTools: true
|
|
95
|
+
allowedTools: []
|
|
96
|
+
# For chat-style Slack flows you can optionally turn off
|
|
97
|
+
# automatic PR/issue + Slack XML context and rely solely on
|
|
98
|
+
# chat_history + conversation objects:
|
|
99
|
+
# skip_transport_context: true
|
|
100
|
+
prompt: |
|
|
101
|
+
You are a concise, friendly assistant.
|
|
102
|
+
|
|
103
|
+
Conversation so far (oldest → newest):
|
|
104
|
+
{% assign history = '' | chat_history: 'ask', 'reply' %}
|
|
105
|
+
{% for m in history %}
|
|
106
|
+
{{ m.role | capitalize }}: {{ m.text }}
|
|
107
|
+
{% endfor %}
|
|
108
|
+
|
|
109
|
+
Latest user message:
|
|
110
|
+
{{ outputs['ask'].text }}
|
|
111
|
+
|
|
112
|
+
Reply naturally. Keep it short (1–2 sentences).
|
|
113
|
+
|
|
114
|
+
guarantee: "(output?.text ?? '').length > 0"
|
|
115
|
+
on_success:
|
|
116
|
+
goto: ask
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Runtime behavior:
|
|
120
|
+
- First Slack message in a thread:
|
|
121
|
+
- Treated as `ask` input.
|
|
122
|
+
- `reply` posts into the same thread.
|
|
123
|
+
- Engine loops to `ask`, posts a prompt, and saves a snapshot.
|
|
124
|
+
- Next Slack message in the same thread:
|
|
125
|
+
- Resumes from snapshot.
|
|
126
|
+
- `ask` consumes the new message.
|
|
127
|
+
- `reply` posts a new answer and loops again.
|
|
128
|
+
|
|
129
|
+
Accessing normalized conversation context in prompts:
|
|
130
|
+
|
|
131
|
+
```liquid
|
|
132
|
+
{% if conversation %}
|
|
133
|
+
Transport: {{ conversation.transport }} {# 'slack', 'github', ... #}
|
|
134
|
+
Thread: {{ conversation.thread.id }}
|
|
135
|
+
{% for m in conversation.messages %}
|
|
136
|
+
{{ m.user }} ({{ m.role }}): {{ m.text }}
|
|
137
|
+
{% endfor %}
|
|
138
|
+
{% endif %}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
- Under Slack, `conversation` and `slack.conversation` are the same
|
|
142
|
+
normalized object.
|
|
143
|
+
- Under GitHub (PR/issue), `conversation` is built from the body +
|
|
144
|
+
comment history using the same `{ role, user, text, timestamp }`
|
|
145
|
+
structure.
|
|
146
|
+
|
|
147
|
+
Customizing chat_history (roles, text, limits):
|
|
148
|
+
|
|
149
|
+
```liquid
|
|
150
|
+
{% assign history = '' |
|
|
151
|
+
chat_history:
|
|
152
|
+
'ask',
|
|
153
|
+
'clarify',
|
|
154
|
+
'reply',
|
|
155
|
+
direction: 'asc',
|
|
156
|
+
limit: 50,
|
|
157
|
+
text: {
|
|
158
|
+
default_field: 'text',
|
|
159
|
+
by_step: {
|
|
160
|
+
'summarize': 'summary.text'
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
roles: {
|
|
164
|
+
by_step: {
|
|
165
|
+
'summarize': 'system'
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
role_map: 'ask=user,reply=assistant'
|
|
169
|
+
%}
|
|
170
|
+
{% for m in history %}
|
|
171
|
+
[{{ m.step }}][{{ m.role }}] {{ m.text }}
|
|
172
|
+
{% endfor %}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Quick reference:
|
|
176
|
+
- `direction: 'asc' | 'desc'`, `limit: N`
|
|
177
|
+
- `text.default_field`, `text.by_step[step]`
|
|
178
|
+
- `roles.by_step[step]`, `roles.by_type[type]`, `roles.default`
|
|
179
|
+
- `role_map: 'step=role,other=role'` as a compact override
|
|
180
|
+
|
|
181
|
+
See also:
|
|
182
|
+
- `docs/human-input-provider.md`
|
|
183
|
+
- `docs/liquid-templates.md` (Chat History Helper)
|
|
184
|
+
- `docs/output-history.md`
|
|
185
|
+
- `examples/slack-simple-chat.yaml`
|
|
186
|
+
|
|
187
|
+
### Advanced routing & contracts (general patterns)
|
|
188
|
+
|
|
189
|
+
#### Inner loops vs. closing the loop
|
|
190
|
+
|
|
191
|
+
- **Close the loop**: Leaf steps use `on_success: goto: <entry-step>` to end the workflow and return to a single top-level `human-input`. Each new event (Slack message, webhook, CLI run) starts a fresh execution.
|
|
192
|
+
- **Close the loop**: Leaf steps use `on_success: goto: <entry-step>` to end the workflow and return to a single top-level `human-input`. Each new event (Slack message, webhook, CLI run) starts a fresh execution.
|
|
193
|
+
- **Inner loop**: Add a local `human-input` and route inside a sub‑flow:
|
|
194
|
+
- Example shape: `router → section-confirm → section-answer → section-confirm`.
|
|
195
|
+
- Use a control field (e.g. `output.done === true`) in `transitions` to exit the section back to the top-level entry step.
|
|
196
|
+
- This pattern is transport-agnostic and works for Slack, GitHub, HTTP workflows, etc.
|
|
197
|
+
- See: `examples/slack-simple-chat.yaml` for a concrete implementation of both patterns.
|
|
198
|
+
|
|
199
|
+
#### Declarative routing (`transitions`) instead of JS
|
|
200
|
+
|
|
201
|
+
- Prefer `on_success.transitions` / `on_finish.transitions` for branching:
|
|
202
|
+
```yaml
|
|
203
|
+
on_success:
|
|
204
|
+
transitions:
|
|
205
|
+
- when: "output && output.intent === 'chat'"
|
|
206
|
+
to: chat-answer
|
|
207
|
+
- when: "output && output.intent === 'project_help'"
|
|
208
|
+
to: project-intent
|
|
209
|
+
```
|
|
210
|
+
- Reserve `goto_js` / `run_js` for legacy or very dynamic use cases.
|
|
211
|
+
- More details: `docs/guides/fault-management-and-contracts.md`, `docs/loop-routing-refactor.md`.
|
|
212
|
+
|
|
213
|
+
- Pattern A — central router + transitions (explicit routing):
|
|
214
|
+
- Use a single “router” step that sets control fields (e.g. `output.intent`, `output.kind`).
|
|
215
|
+
- Declare all branching in one place via `on_success.transitions` on the router:
|
|
216
|
+
```yaml
|
|
217
|
+
router:
|
|
218
|
+
type: ai
|
|
219
|
+
on_success:
|
|
220
|
+
transitions:
|
|
221
|
+
- when: "output.intent === 'chat'"
|
|
222
|
+
to: chat-answer
|
|
223
|
+
- when: "output.intent === 'status'"
|
|
224
|
+
to: status-answer
|
|
225
|
+
|
|
226
|
+
chat-answer:
|
|
227
|
+
depends_on: [router]
|
|
228
|
+
if: "outputs['router']?.intent === 'chat'"
|
|
229
|
+
|
|
230
|
+
status-answer:
|
|
231
|
+
depends_on: [router]
|
|
232
|
+
if: "outputs['router']?.intent === 'status'"
|
|
233
|
+
```
|
|
234
|
+
- Good when you want a single, centralized view of routing logic. Use `if` on branches for readability and to skip branches cleanly; reserve `assume` for hard dependency checks only.
|
|
235
|
+
|
|
236
|
+
- Pattern B — distributed routing via `depends_on` + `if`:
|
|
237
|
+
- Omit transitions entirely and let each branch decide whether it should run:
|
|
238
|
+
```yaml
|
|
239
|
+
router:
|
|
240
|
+
type: ai
|
|
241
|
+
# no on_success.transitions
|
|
242
|
+
|
|
243
|
+
chat-answer:
|
|
244
|
+
depends_on: [router]
|
|
245
|
+
if: "outputs['router']?.intent === 'chat'"
|
|
246
|
+
|
|
247
|
+
status-answer:
|
|
248
|
+
depends_on: [router]
|
|
249
|
+
if: "outputs['router']?.intent === 'status'"
|
|
250
|
+
```
|
|
251
|
+
- The DAG (`depends_on`) defines possible flows; `if` conditions select the active branch(es) per run.
|
|
252
|
+
- This works well when routing is simple or when you prefer fully local branch declarations over a central router table.
|
|
253
|
+
|
|
254
|
+
#### Criticality + `assume` + `guarantee` (recommended layout)
|
|
255
|
+
|
|
256
|
+
- Apply to **any** workflow, not just chat:
|
|
257
|
+
- `external` – step changes **external state**:
|
|
258
|
+
- Examples: GitHub comments/labels, HTTP POST/PUT/PATCH/DELETE, ticket creation, updating CI/CD or incident systems, filesystem writes in a shared location.
|
|
259
|
+
- If someone can look elsewhere and see a change after this step, it’s usually `external`.
|
|
260
|
+
- `internal` – step changes **the workflow’s control-plane**:
|
|
261
|
+
- Examples: forEach parents that fan out work; steps with `on_* transitions/goto` that decide what runs next; script/memory steps that set flags used by `if`/`assume`/`guarantee`.
|
|
262
|
+
- If it mostly “steers” the run (not user-facing output), treat it as `internal`.
|
|
263
|
+
- `policy` – step enforces **org or safety rules**:
|
|
264
|
+
- Examples: permission checks (who may deploy/label), change windows, compliance checks (branches, commit format, DCO/CLA).
|
|
265
|
+
- Often used to gate `external` steps (e.g. only label when policy passes).
|
|
266
|
+
- `info` – read-only / non-critical:
|
|
267
|
+
- Examples: summaries, hints, dashboards, advisory AI steps that do not gate other critical steps and do not mutate anything directly.
|
|
268
|
+
- For `internal` / `external` steps, group fields in this order:
|
|
269
|
+
```yaml
|
|
270
|
+
some-step:
|
|
271
|
+
type: ai | script | command | ...
|
|
272
|
+
group: ...
|
|
273
|
+
depends_on: [...]
|
|
274
|
+
criticality: internal # or external / policy / info
|
|
275
|
+
assume:
|
|
276
|
+
- "upstream condition" # never reference this step's own output here
|
|
277
|
+
guarantee: "output?.field != null" # assertions about this step's output
|
|
278
|
+
schema: # JSON Schema when output is structured
|
|
279
|
+
...
|
|
280
|
+
```
|
|
281
|
+
- Use `assume` for preconditions about upstream state (memory, env, `outputs[...]`).
|
|
282
|
+
- Use `guarantee` for postconditions about this step’s own output (shape, control flags, size caps).
|
|
283
|
+
- For `info` steps, contracts are recommended but optional; keep `assume` + `guarantee` adjacent when present.
|
|
284
|
+
- More details: `docs/guides/criticality-modes.md`, `docs/guides/fault-management-and-contracts.md`.
|
|
285
|
+
|
|
286
|
+
#### JSON Schemas instead of `schema: plain`
|
|
287
|
+
|
|
288
|
+
- For structured outputs (routers, script integrations, control signals), prefer real JSON Schema:
|
|
289
|
+
```yaml
|
|
290
|
+
router-step:
|
|
291
|
+
schema:
|
|
292
|
+
type: object
|
|
293
|
+
properties:
|
|
294
|
+
intent:
|
|
295
|
+
type: string
|
|
296
|
+
enum: [chat, summarize, escalate]
|
|
297
|
+
target:
|
|
298
|
+
type: string
|
|
299
|
+
required: [intent]
|
|
300
|
+
```
|
|
301
|
+
- For text responses, it can still be useful to wrap in an object:
|
|
302
|
+
```yaml
|
|
303
|
+
answer:
|
|
304
|
+
schema:
|
|
305
|
+
type: object
|
|
306
|
+
properties:
|
|
307
|
+
text: { type: string }
|
|
308
|
+
required: [text]
|
|
309
|
+
guarantee: "(output?.text ?? '').length > 0"
|
|
310
|
+
```
|
|
311
|
+
- Use `schema: plain` only when output shape is genuinely unconstrained.
|
|
312
|
+
|
|
313
|
+
Tip: When you define a JSON Schema, you generally do **not** need to tell the model “respond only as JSON”; describe the semantics in the prompt and let the renderer/schema enforce shape.
|
|
314
|
+
|
|
315
|
+
#### Expression style (`assume`, `guarantee`, `when`)
|
|
316
|
+
|
|
317
|
+
- Prefer clear, concise expressions:
|
|
318
|
+
- `outputs['router']?.intent === 'chat'`
|
|
319
|
+
- `!!outputs['status-fetch']?.project`
|
|
320
|
+
- `output?.done === true`
|
|
321
|
+
- Avoid noisy fallbacks like `(outputs['x']?.kind ?? '') === 'status'` when `outputs['x']?.kind === 'status'` is equivalent.
|
|
322
|
+
- These conventions apply uniformly to any provider (`ai`, `command`, `script`, `github`, `http_client`, etc).
|
|
323
|
+
|
|
324
|
+
### Command step best practices
|
|
325
|
+
|
|
326
|
+
When using `type: command` steps:
|
|
327
|
+
|
|
328
|
+
**Avoid external tool dependencies** like `jq`, `yq`, `python`, etc.:
|
|
329
|
+
- They may not be installed in all environments (GitHub Actions, Docker, CI)
|
|
330
|
+
- Use `transform_js` to parse and transform output instead
|
|
331
|
+
- Keep shell commands simple: `grep`, `sed`, `awk`, `sort`, `head` are universally available
|
|
332
|
+
|
|
333
|
+
```yaml
|
|
334
|
+
# Bad - requires jq
|
|
335
|
+
extract-data:
|
|
336
|
+
type: command
|
|
337
|
+
exec: |
|
|
338
|
+
echo "$TEXT" | grep -oE '[A-Z]+-[0-9]+' | jq -R -s 'split("\n")'
|
|
339
|
+
parseJson: true
|
|
340
|
+
|
|
341
|
+
# Good - use transform_js for parsing
|
|
342
|
+
extract-data:
|
|
343
|
+
type: command
|
|
344
|
+
exec: |
|
|
345
|
+
echo "$TEXT" | grep -oE '[A-Z]+-[0-9]+' | sort -u
|
|
346
|
+
transform_js: |
|
|
347
|
+
const lines = (output || '').trim().split('\n').filter(Boolean);
|
|
348
|
+
return { data: lines, count: lines.length };
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
**Prefer line-separated output** over JSON from shell:
|
|
352
|
+
- Simple to parse with `transform_js`
|
|
353
|
+
- No need for `parseJson: true`
|
|
354
|
+
- More robust across different shells/environments
|
|
355
|
+
|
|
356
|
+
**Use transform_js for structured output**:
|
|
357
|
+
- The sandbox provides `output` (command stdout as string)
|
|
358
|
+
- Return an object with the fields you need
|
|
359
|
+
- Works consistently across all environments
|
|
360
|
+
|
|
361
|
+
### Testing workflows with `--no-mocks`
|
|
362
|
+
|
|
363
|
+
The `--no-mocks` flag runs your test cases with real providers instead of injecting mock responses. This is essential for:
|
|
364
|
+
|
|
365
|
+
1. **Debugging integration issues** - See actual API responses and errors
|
|
366
|
+
2. **Capturing realistic mock data** - Get real output to copy into your test cases
|
|
367
|
+
3. **Validating credentials** - Verify environment variables are set correctly
|
|
368
|
+
4. **Developing new workflows** - Build tests incrementally with real data
|
|
369
|
+
|
|
370
|
+
#### Basic usage
|
|
371
|
+
|
|
372
|
+
```bash
|
|
373
|
+
# Run all test cases with real providers
|
|
374
|
+
visor test --config my-workflow.yaml --no-mocks
|
|
375
|
+
|
|
376
|
+
# Run a specific test case with real providers
|
|
377
|
+
visor test --config my-workflow.yaml --no-mocks --only "my-test-case"
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
#### Suggested mocks output
|
|
381
|
+
|
|
382
|
+
When running with `--no-mocks`, Visor captures each step's output and prints it as YAML you can copy directly into your test case:
|
|
383
|
+
|
|
384
|
+
```
|
|
385
|
+
🔴 NO-MOCKS MODE: Running with real providers (no mock injection)
|
|
386
|
+
Step outputs will be captured and printed as suggested mocks
|
|
387
|
+
|
|
388
|
+
... test execution ...
|
|
389
|
+
|
|
390
|
+
📋 Suggested mocks (copy to your test case):
|
|
391
|
+
mocks:
|
|
392
|
+
extract-keys:
|
|
393
|
+
data:
|
|
394
|
+
- PROJ-123
|
|
395
|
+
- DEV-456
|
|
396
|
+
count: 2
|
|
397
|
+
fetch-issues:
|
|
398
|
+
data:
|
|
399
|
+
- key: PROJ-123
|
|
400
|
+
summary: Fix authentication bug
|
|
401
|
+
status: In Progress
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
Copy the YAML under `mocks:` into your test case's `mocks:` section.
|
|
405
|
+
|
|
406
|
+
#### Workflow for building tests
|
|
407
|
+
|
|
408
|
+
1. **Start with a minimal test case** (no mocks):
|
|
409
|
+
```yaml
|
|
410
|
+
tests:
|
|
411
|
+
cases:
|
|
412
|
+
- name: my-new-test
|
|
413
|
+
event: manual
|
|
414
|
+
fixture: local.minimal
|
|
415
|
+
workflow_input:
|
|
416
|
+
text: "Fix bug PROJ-123"
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
2. **Run with `--no-mocks`** to capture real outputs:
|
|
420
|
+
```bash
|
|
421
|
+
visor test --config workflow.yaml --no-mocks --only "my-new-test"
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
3. **Copy the suggested mocks** into your test case:
|
|
425
|
+
```yaml
|
|
426
|
+
tests:
|
|
427
|
+
cases:
|
|
428
|
+
- name: my-new-test
|
|
429
|
+
event: manual
|
|
430
|
+
fixture: local.minimal
|
|
431
|
+
workflow_input:
|
|
432
|
+
text: "Fix bug PROJ-123"
|
|
433
|
+
mocks:
|
|
434
|
+
extract-keys:
|
|
435
|
+
data: ["PROJ-123"]
|
|
436
|
+
count: 1
|
|
437
|
+
# ... rest of captured mocks
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
4. **Add assertions** based on the real data:
|
|
441
|
+
```yaml
|
|
442
|
+
expect:
|
|
443
|
+
workflow_output:
|
|
444
|
+
- path: issue_count
|
|
445
|
+
equals: 1
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
5. **Run normally** to verify mocks work:
|
|
449
|
+
```bash
|
|
450
|
+
visor test --config workflow.yaml --only "my-new-test"
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
#### Debugging with `--no-mocks`
|
|
454
|
+
|
|
455
|
+
When a test fails with mocks, use `--no-mocks` to see what's actually happening:
|
|
456
|
+
|
|
457
|
+
```bash
|
|
458
|
+
# See real API responses and errors
|
|
459
|
+
visor test --config workflow.yaml --no-mocks --only "failing-test"
|
|
460
|
+
|
|
461
|
+
# Common issues revealed:
|
|
462
|
+
# - Missing or expired credentials
|
|
463
|
+
# - API endpoint changes
|
|
464
|
+
# - Unexpected response formats
|
|
465
|
+
# - Network/timeout issues
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
The real error messages and responses help identify whether the issue is with your mocks or the actual integration.
|
|
469
|
+
|
|
470
|
+
### More examples
|
|
471
|
+
|
|
472
|
+
- `docs/NPM_USAGE.md` – CLI usage and flags
|
|
473
|
+
- `GITHUB_CHECKS.md` – Checks, outputs, and workflow integration
|
|
474
|
+
- `examples/` – MCP, Jira, and advanced configs
|