@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
|
@@ -0,0 +1,775 @@
|
|
|
1
|
+
# Slack Org Assistant — intent routing + state-machine
|
|
2
|
+
#
|
|
3
|
+
# ASCII Flow Overview
|
|
4
|
+
# ====================
|
|
5
|
+
#
|
|
6
|
+
# Slack thread message
|
|
7
|
+
# │
|
|
8
|
+
# ▼
|
|
9
|
+
# ask (human-input, info)
|
|
10
|
+
# │
|
|
11
|
+
# ▼
|
|
12
|
+
# route-intent (ai, internal)
|
|
13
|
+
# │
|
|
14
|
+
# ├─ intent = chat
|
|
15
|
+
# │ │
|
|
16
|
+
# │ ▼
|
|
17
|
+
# │ chat-answer (ai, info)
|
|
18
|
+
# │ │
|
|
19
|
+
# │ └─ on_success → ask
|
|
20
|
+
# │
|
|
21
|
+
# ├─ intent = thread_summary
|
|
22
|
+
# │ │
|
|
23
|
+
# │ ▼
|
|
24
|
+
# │ summarize-thread (ai, info; uses slack.conversation)
|
|
25
|
+
# │ │
|
|
26
|
+
# │ └─ on_success → ask
|
|
27
|
+
# │
|
|
28
|
+
# ├─ intent = faq
|
|
29
|
+
# │ │
|
|
30
|
+
# │ ▼
|
|
31
|
+
# │ faq-answer (ai, info)
|
|
32
|
+
# │ │
|
|
33
|
+
# │ └─ on_success → ask
|
|
34
|
+
# │
|
|
35
|
+
# └─ intent = project_help
|
|
36
|
+
# │
|
|
37
|
+
# ▼
|
|
38
|
+
# project-intent (ai, internal)
|
|
39
|
+
# │
|
|
40
|
+
# ├─ project-status-fetch (script, external mock)
|
|
41
|
+
# ├─ project-deploy-log (script, external mock)
|
|
42
|
+
# │
|
|
43
|
+
# ├─ kind = status
|
|
44
|
+
# │ │
|
|
45
|
+
# │ └─ project-status-answer (ai, info)
|
|
46
|
+
# │ └─ on_success → ask
|
|
47
|
+
# │
|
|
48
|
+
# ├─ kind = deployment
|
|
49
|
+
# │ │
|
|
50
|
+
# │ ├─ project-deploy-confirm (human-input, inner loop)
|
|
51
|
+
# │ │ ↕
|
|
52
|
+
# │ │ project-deploy-answer (ai, info; done=false → confirm, done=true → ask)
|
|
53
|
+
# │ │
|
|
54
|
+
# │ └─ (on exit) on_success → ask
|
|
55
|
+
# │
|
|
56
|
+
# └─ kind = owner
|
|
57
|
+
# │
|
|
58
|
+
# └─ project-owner-answer (ai, info)
|
|
59
|
+
# └─ on_success → ask
|
|
60
|
+
#
|
|
61
|
+
# Mermaid State Flow (high-level)
|
|
62
|
+
# ```mermaid
|
|
63
|
+
# stateDiagram-v2
|
|
64
|
+
# [*] --> ask
|
|
65
|
+
# ask --> route_intent
|
|
66
|
+
#
|
|
67
|
+
# route_intent --> chat_answer : intent == "chat"
|
|
68
|
+
# route_intent --> summarize_thread : intent == "thread_summary"
|
|
69
|
+
# route_intent --> faq_answer : intent == "faq"
|
|
70
|
+
# route_intent --> capabilities_answer : intent == "capabilities"
|
|
71
|
+
# route_intent --> project_intent : intent == "project_help"
|
|
72
|
+
#
|
|
73
|
+
# chat_answer --> ask
|
|
74
|
+
# summarize_thread --> ask
|
|
75
|
+
# faq_answer --> ask
|
|
76
|
+
# capabilities_answer --> ask
|
|
77
|
+
#
|
|
78
|
+
# project_intent --> project_status_fetch
|
|
79
|
+
# project_intent --> project_deploy_log
|
|
80
|
+
#
|
|
81
|
+
# project_intent --> project_status_answer : kind == "status"
|
|
82
|
+
# project_status_answer --> ask
|
|
83
|
+
#
|
|
84
|
+
# project_intent --> project_deploy_confirm : kind == "deployment"
|
|
85
|
+
# project_deploy_confirm --> project_deploy_answer
|
|
86
|
+
# project_deploy_answer --> project_deploy_confirm : done == false
|
|
87
|
+
# project_deploy_answer --> ask : done == true
|
|
88
|
+
#
|
|
89
|
+
# project_intent --> project_owner_answer : kind == "owner"
|
|
90
|
+
# project_owner_answer --> ask
|
|
91
|
+
# ```
|
|
92
|
+
#
|
|
93
|
+
# How to run (Socket Mode):
|
|
94
|
+
# SLACK_APP_TOKEN=xapp-... SLACK_BOT_TOKEN=xoxb-... \
|
|
95
|
+
# node dist/index.js --config examples/slack-simple-chat.yaml --slack
|
|
96
|
+
#
|
|
97
|
+
# Behaviour:
|
|
98
|
+
# - First user message in a Slack thread is treated as the first human_input
|
|
99
|
+
# (no prompt posted).
|
|
100
|
+
# - `route-intent` (AI) classifies the request into one of several intents:
|
|
101
|
+
# * free-form chat
|
|
102
|
+
# * summarize this Slack thread
|
|
103
|
+
# * project-specific help (status/deploy/owner)
|
|
104
|
+
# * FAQ / “how do I …?”
|
|
105
|
+
# - Based on that intent, the workflow routes to specialised sub-flows:
|
|
106
|
+
# * `chat-answer` — conversational answer using chat_history
|
|
107
|
+
# * `summarize-thread` — summary of the current Slack thread
|
|
108
|
+
# * `project-intent` → `project-*` answers (nested routing)
|
|
109
|
+
# * `faq-answer` — generic FAQ-style reply
|
|
110
|
+
# - Leaf steps then route back to `ask`, so each new Slack message re-enters
|
|
111
|
+
# the top-level router with full context.
|
|
112
|
+
|
|
113
|
+
version: '1.0'
|
|
114
|
+
|
|
115
|
+
slack:
|
|
116
|
+
# Optional Slack runtime hints for socket mode; safe to omit
|
|
117
|
+
version: "v1"
|
|
118
|
+
mentions: all # accept DM 'message' events as well
|
|
119
|
+
threads: required # require threaded replies for stable resume
|
|
120
|
+
# For tests and debugging we allow router/json outputs to be posted as raw JSON.
|
|
121
|
+
# In production you typically leave this unset/false so only text replies appear.
|
|
122
|
+
show_raw_output: true
|
|
123
|
+
|
|
124
|
+
checks:
|
|
125
|
+
# 1) Human entry point (top-level loop)
|
|
126
|
+
# - Single source of truth for user messages in this workflow.
|
|
127
|
+
# - In Slack, the first message in a thread is treated as this input.
|
|
128
|
+
ask:
|
|
129
|
+
type: human-input
|
|
130
|
+
group: chat
|
|
131
|
+
criticality: info
|
|
132
|
+
prompt: |
|
|
133
|
+
Hi there! What can I do for you?
|
|
134
|
+
(This prompt is only posted when the workflow is waiting.)
|
|
135
|
+
|
|
136
|
+
# 2) Top-level intent router (AI, control-plane)
|
|
137
|
+
# - Reads the latest `ask` output and classifies user intent.
|
|
138
|
+
# - Drives routing into chat / summary / project / FAQ branches via transitions.
|
|
139
|
+
route-intent:
|
|
140
|
+
type: ai
|
|
141
|
+
group: chat
|
|
142
|
+
depends_on: ask
|
|
143
|
+
criticality: internal
|
|
144
|
+
assume:
|
|
145
|
+
- "outputs['ask'] != null"
|
|
146
|
+
guarantee: "output?.intent != null"
|
|
147
|
+
schema:
|
|
148
|
+
type: object
|
|
149
|
+
properties:
|
|
150
|
+
intent:
|
|
151
|
+
type: string
|
|
152
|
+
enum: [chat, thread_summary, project_help, faq, capabilities]
|
|
153
|
+
project:
|
|
154
|
+
type: string
|
|
155
|
+
description: Optional inferred project/service name
|
|
156
|
+
topic:
|
|
157
|
+
type: string
|
|
158
|
+
description: Optional free-form topic/subject
|
|
159
|
+
required: [intent]
|
|
160
|
+
ai:
|
|
161
|
+
disableTools: true
|
|
162
|
+
allowedTools: []
|
|
163
|
+
system_prompt: "You are general assistant, follow user instructions."
|
|
164
|
+
prompt: |
|
|
165
|
+
You are an internal assistant for an engineering organisation. Classify the
|
|
166
|
+
latest Slack message and decide what kind of automation should run.
|
|
167
|
+
|
|
168
|
+
Conversation so far (oldest → newest):
|
|
169
|
+
{% assign history = '' | chat_history: 'ask', 'chat-answer' %}
|
|
170
|
+
{% for m in history %}
|
|
171
|
+
{{ m.role | capitalize }}: {{ m.text }}
|
|
172
|
+
{% endfor %}
|
|
173
|
+
|
|
174
|
+
Latest user message:
|
|
175
|
+
{{ outputs['ask'].text }}
|
|
176
|
+
|
|
177
|
+
Choose the most appropriate intent (stored in the `intent` field):
|
|
178
|
+
- "chat" → general Q&A or small talk
|
|
179
|
+
- "thread_summary" → user wants a summary of the current Slack thread
|
|
180
|
+
- "project_help" → user asks about a specific internal project/service
|
|
181
|
+
- "faq" → generic "how do I …?" style question
|
|
182
|
+
- "capabilities" → user asks what this assistant/workflow can do
|
|
183
|
+
You may optionally also record:
|
|
184
|
+
- `project`: inferred project/service name, if any
|
|
185
|
+
- `topic`: short free-form topic or question summary
|
|
186
|
+
on_success:
|
|
187
|
+
transitions:
|
|
188
|
+
- when: "output.intent === 'thread_summary'"
|
|
189
|
+
to: summarize-thread
|
|
190
|
+
- when: "output.intent === 'project_help'"
|
|
191
|
+
to: project-intent
|
|
192
|
+
- when: "output.intent === 'faq'"
|
|
193
|
+
to: faq-answer
|
|
194
|
+
- when: "output.intent === 'capabilities'"
|
|
195
|
+
to: capabilities-answer
|
|
196
|
+
- when: "output.intent === 'chat'"
|
|
197
|
+
to: chat-answer
|
|
198
|
+
|
|
199
|
+
# 2a) Capabilities / self-describe branch
|
|
200
|
+
# - Answers "what can you do?" style questions.
|
|
201
|
+
capabilities-answer:
|
|
202
|
+
type: ai
|
|
203
|
+
group: chat
|
|
204
|
+
depends_on: [route-intent]
|
|
205
|
+
criticality: info
|
|
206
|
+
if: "outputs['route-intent']?.intent === 'capabilities'"
|
|
207
|
+
guarantee: "(output?.text ?? '').length > 0"
|
|
208
|
+
schema:
|
|
209
|
+
type: object
|
|
210
|
+
properties:
|
|
211
|
+
text:
|
|
212
|
+
type: string
|
|
213
|
+
required: [text]
|
|
214
|
+
ai:
|
|
215
|
+
disableTools: true
|
|
216
|
+
allowedTools: []
|
|
217
|
+
system_prompt: "You explain clearly and concisely what this workflow and assistant can do."
|
|
218
|
+
prompt: |
|
|
219
|
+
The user is asking about your capabilities.
|
|
220
|
+
|
|
221
|
+
Explain, in plain language, what kinds of things this workflow can help with, such as:
|
|
222
|
+
- General Q&A and discussion about the repo or topic.
|
|
223
|
+
- Summarising ongoing conversations or tickets.
|
|
224
|
+
- Routing project/service questions into status, deployment help, or ownership guidance.
|
|
225
|
+
- Answering internal how-to / FAQ style questions.
|
|
226
|
+
|
|
227
|
+
Keep the answer short (1–2 paragraphs + a concise bullet list).
|
|
228
|
+
on_success:
|
|
229
|
+
goto: ask
|
|
230
|
+
|
|
231
|
+
# 3a) Chat branch (general Q&A)
|
|
232
|
+
# - Handles free-form chat/workflow questions using `chat_history` for context.
|
|
233
|
+
chat-answer:
|
|
234
|
+
type: ai
|
|
235
|
+
group: chat
|
|
236
|
+
depends_on: [route-intent]
|
|
237
|
+
criticality: info
|
|
238
|
+
if: "outputs['route-intent']?.intent === 'chat'"
|
|
239
|
+
guarantee: "(output?.text ?? '').length > 0"
|
|
240
|
+
schema:
|
|
241
|
+
type: object
|
|
242
|
+
properties:
|
|
243
|
+
text:
|
|
244
|
+
type: string
|
|
245
|
+
required: [text]
|
|
246
|
+
ai:
|
|
247
|
+
disableTools: true
|
|
248
|
+
allowedTools: []
|
|
249
|
+
system_prompt: "You are general assistant, follow user instructions."
|
|
250
|
+
prompt: |
|
|
251
|
+
You are a concise, friendly assistant helping colleagues in Slack.
|
|
252
|
+
|
|
253
|
+
Conversation so far (oldest → newest):
|
|
254
|
+
{% assign history = '' | chat_history: 'ask', 'chat-answer' %}
|
|
255
|
+
{% for m in history %}
|
|
256
|
+
{{ m.role | capitalize }}: {{ m.text }}
|
|
257
|
+
{% endfor %}
|
|
258
|
+
|
|
259
|
+
Latest user message:
|
|
260
|
+
{{ outputs['ask'].text }}
|
|
261
|
+
|
|
262
|
+
Reply naturally. Keep it short (1–3 sentences). No code fences unless the
|
|
263
|
+
user explicitly asks for code.
|
|
264
|
+
on_success:
|
|
265
|
+
goto: ask
|
|
266
|
+
|
|
267
|
+
# 3b) Thread summary branch
|
|
268
|
+
# - Summarises the current conversation using normalized `conversation`/`slack.conversation`.
|
|
269
|
+
# - Useful for "summarize this thread / discussion" style requests.
|
|
270
|
+
summarize-thread:
|
|
271
|
+
type: ai
|
|
272
|
+
group: chat
|
|
273
|
+
depends_on: [route-intent]
|
|
274
|
+
criticality: info
|
|
275
|
+
if: "outputs['route-intent']?.intent === 'thread_summary'"
|
|
276
|
+
guarantee: "(output?.text ?? '').length > 0"
|
|
277
|
+
schema:
|
|
278
|
+
type: object
|
|
279
|
+
properties:
|
|
280
|
+
text:
|
|
281
|
+
type: string
|
|
282
|
+
required: [text]
|
|
283
|
+
ai:
|
|
284
|
+
disableTools: true
|
|
285
|
+
allowedTools: []
|
|
286
|
+
system_prompt: "You summarise Slack threads for busy engineers."
|
|
287
|
+
prompt: |
|
|
288
|
+
Summarise the current Slack thread for the user.
|
|
289
|
+
|
|
290
|
+
{% if slack.conversation %}
|
|
291
|
+
Thread context:
|
|
292
|
+
{% for m in slack.conversation.messages %}
|
|
293
|
+
- [{{ m.user }}] {{ m.text }}
|
|
294
|
+
{% endfor %}
|
|
295
|
+
{% else %}
|
|
296
|
+
There is no additional thread history available; use only the latest message.
|
|
297
|
+
{% endif %}
|
|
298
|
+
|
|
299
|
+
Produce:
|
|
300
|
+
- 3–5 bullet points capturing the key discussion
|
|
301
|
+
- a one-line tl;dr at the end, starting with "TL;DR:"
|
|
302
|
+
on_success:
|
|
303
|
+
goto: ask
|
|
304
|
+
|
|
305
|
+
# 3c) FAQ branch (how-to / internal workflows)
|
|
306
|
+
# - Targets "how do I …?" questions with concise, actionable guidance.
|
|
307
|
+
faq-answer:
|
|
308
|
+
type: ai
|
|
309
|
+
group: chat
|
|
310
|
+
depends_on: [route-intent]
|
|
311
|
+
criticality: info
|
|
312
|
+
if: "outputs['route-intent']?.intent === 'faq'"
|
|
313
|
+
guarantee: "(output?.text ?? '').length > 0"
|
|
314
|
+
schema:
|
|
315
|
+
type: object
|
|
316
|
+
properties:
|
|
317
|
+
text:
|
|
318
|
+
type: string
|
|
319
|
+
required: [text]
|
|
320
|
+
ai:
|
|
321
|
+
disableTools: true
|
|
322
|
+
allowedTools: []
|
|
323
|
+
system_prompt: "You answer internal FAQs and give practical, concise instructions."
|
|
324
|
+
prompt: |
|
|
325
|
+
You are answering internal FAQs for an engineering org. Be concrete, assume
|
|
326
|
+
modern tooling, and prefer step-by-step instructions.
|
|
327
|
+
|
|
328
|
+
Question from Slack:
|
|
329
|
+
{{ outputs['ask'].text }}
|
|
330
|
+
|
|
331
|
+
Answer briefly (1–2 short paragraphs + a short checklist if helpful).
|
|
332
|
+
on_success:
|
|
333
|
+
goto: ask
|
|
334
|
+
|
|
335
|
+
# 3d) Project-help branch (nested routing)
|
|
336
|
+
# - Handles project/service-specific questions.
|
|
337
|
+
# - Sub-routes to status / deployment helper / ownership answers.
|
|
338
|
+
project-intent:
|
|
339
|
+
type: ai
|
|
340
|
+
group: chat
|
|
341
|
+
depends_on: [route-intent]
|
|
342
|
+
criticality: internal
|
|
343
|
+
if: "outputs['route-intent']?.intent === 'project_help'"
|
|
344
|
+
guarantee: "output?.project != null && output?.kind != null"
|
|
345
|
+
schema:
|
|
346
|
+
type: object
|
|
347
|
+
properties:
|
|
348
|
+
project:
|
|
349
|
+
type: string
|
|
350
|
+
description: Inferred project/service name
|
|
351
|
+
kind:
|
|
352
|
+
type: string
|
|
353
|
+
enum: [status, deployment, owner]
|
|
354
|
+
required: [project, kind]
|
|
355
|
+
ai:
|
|
356
|
+
disableTools: true
|
|
357
|
+
allowedTools: []
|
|
358
|
+
system_prompt: "You route requests about internal projects to the right helper."
|
|
359
|
+
prompt: |
|
|
360
|
+
A user is asking about an internal project or service.
|
|
361
|
+
|
|
362
|
+
Latest user message:
|
|
363
|
+
{{ outputs['ask'].text }}
|
|
364
|
+
|
|
365
|
+
Categorise the request into a `kind`:
|
|
366
|
+
- "status" → user wants current status / health of a project
|
|
367
|
+
- "deployment" → user asks about how/where to deploy or current deployment state
|
|
368
|
+
- "owner" → user wants to know who owns / maintains the project
|
|
369
|
+
Also infer a `project` name if possible (or a short alias the org
|
|
370
|
+
would recognise). Store these into the schema fields; do not explain
|
|
371
|
+
the internal representation to the user.
|
|
372
|
+
on_success:
|
|
373
|
+
transitions:
|
|
374
|
+
- when: "output && output.kind === 'status'"
|
|
375
|
+
to: project-status-answer
|
|
376
|
+
- when: "output && output.kind === 'deployment'"
|
|
377
|
+
to: project-deploy-confirm
|
|
378
|
+
- when: "output && output.kind === 'owner'"
|
|
379
|
+
to: project-owner-answer
|
|
380
|
+
|
|
381
|
+
# Project status answer
|
|
382
|
+
# - Uses `project-status-fetch` mock integration and the router output.
|
|
383
|
+
project-status-answer:
|
|
384
|
+
type: ai
|
|
385
|
+
group: chat
|
|
386
|
+
depends_on: [project-intent, project-status-fetch]
|
|
387
|
+
criticality: info
|
|
388
|
+
if: "outputs['project-intent']?.kind === 'status'"
|
|
389
|
+
assume:
|
|
390
|
+
- "!!outputs['project-status-fetch']?.project"
|
|
391
|
+
guarantee: "(output?.text ?? '').length > 0"
|
|
392
|
+
schema:
|
|
393
|
+
type: object
|
|
394
|
+
properties:
|
|
395
|
+
text:
|
|
396
|
+
type: string
|
|
397
|
+
required: [text]
|
|
398
|
+
ai:
|
|
399
|
+
disableTools: true
|
|
400
|
+
allowedTools: []
|
|
401
|
+
system_prompt: "You provide high-level status summaries for internal projects."
|
|
402
|
+
prompt: |
|
|
403
|
+
Provide a short, high-level status update for the internal project below.
|
|
404
|
+
Assume this is a mock environment – make up reasonable but clearly generic details.
|
|
405
|
+
|
|
406
|
+
Project: {{ outputs['project-status-fetch'].project | default: "unknown-service" }}
|
|
407
|
+
|
|
408
|
+
Internal status snapshot (mocked):
|
|
409
|
+
- Health: {{ outputs['project-status-fetch'].status | default: "unknown" }}
|
|
410
|
+
- Last deploy: {{ outputs['project-status-fetch'].last_deploy | default: "unknown" }}
|
|
411
|
+
|
|
412
|
+
Answer in 2–3 sentences, focusing on current status, recent changes, and any
|
|
413
|
+
obvious next steps for the requester.
|
|
414
|
+
on_success:
|
|
415
|
+
goto: ask
|
|
416
|
+
|
|
417
|
+
# Deployment progress log (chat)
|
|
418
|
+
# - Sends a short, crafted message to the user when entering the deployment helper.
|
|
419
|
+
# - Because type=log and group=chat, Slack will post this into the thread.
|
|
420
|
+
deployment-progress-log:
|
|
421
|
+
type: log
|
|
422
|
+
group: chat
|
|
423
|
+
criticality: info
|
|
424
|
+
depends_on: [project-intent]
|
|
425
|
+
assume:
|
|
426
|
+
- "outputs['project-intent']?.kind === 'deployment'"
|
|
427
|
+
level: info
|
|
428
|
+
message: |
|
|
429
|
+
Preparing deployment helper for {{ outputs['project-intent'].project | default: "unknown-service" }}…
|
|
430
|
+
|
|
431
|
+
# Inner deployment helper loop: confirm ↔ answer until done
|
|
432
|
+
# - `project-deploy-confirm` / `project-deploy-answer` form a local loop.
|
|
433
|
+
# - `done=true` exits back to the top-level `ask`.
|
|
434
|
+
project-deploy-confirm:
|
|
435
|
+
type: human-input
|
|
436
|
+
group: chat
|
|
437
|
+
depends_on: [project-intent]
|
|
438
|
+
criticality: info
|
|
439
|
+
if: "outputs['project-intent']?.kind === 'deployment'"
|
|
440
|
+
prompt: |
|
|
441
|
+
You are in the deployment helper for {{ outputs['project-intent'].project | default: "unknown-service" }}.
|
|
442
|
+
Ask a deployment-related question or type "done" when you are finished with deployment help.
|
|
443
|
+
|
|
444
|
+
project-deploy-answer:
|
|
445
|
+
type: ai
|
|
446
|
+
group: chat
|
|
447
|
+
depends_on: [project-intent, project-deploy-confirm]
|
|
448
|
+
criticality: info
|
|
449
|
+
if: "outputs['project-intent']?.kind === 'deployment' && !!outputs['project-deploy-confirm']"
|
|
450
|
+
guarantee: "(output?.text ?? '').length > 0"
|
|
451
|
+
schema:
|
|
452
|
+
type: object
|
|
453
|
+
properties:
|
|
454
|
+
text:
|
|
455
|
+
type: string
|
|
456
|
+
done:
|
|
457
|
+
type: boolean
|
|
458
|
+
required: [text, done]
|
|
459
|
+
ai:
|
|
460
|
+
disableTools: true
|
|
461
|
+
allowedTools: []
|
|
462
|
+
system_prompt: "You guide engineers through safe deployments for internal services."
|
|
463
|
+
prompt: |
|
|
464
|
+
The user is asking about deployment for this project:
|
|
465
|
+
{{ outputs['project-intent'].project | default: "unknown-service" }}
|
|
466
|
+
|
|
467
|
+
Give a short, generic deployment playbook:
|
|
468
|
+
- required pre-checks
|
|
469
|
+
- how to deploy (mocked commands / steps)
|
|
470
|
+
- how to verify success
|
|
471
|
+
|
|
472
|
+
Keep it organisation-agnostic and under ~8 bullet points.
|
|
473
|
+
|
|
474
|
+
Also decide whether the deployment helper should keep going or exit back
|
|
475
|
+
to the main assistant:
|
|
476
|
+
- set done = true if the user is finished with deployment questions
|
|
477
|
+
- set done = false if the user is likely to have follow-up questions
|
|
478
|
+
|
|
479
|
+
on_success:
|
|
480
|
+
transitions:
|
|
481
|
+
- when: "output?.done === true"
|
|
482
|
+
to: ask
|
|
483
|
+
- when: "output?.done === false"
|
|
484
|
+
to: project-deploy-confirm
|
|
485
|
+
|
|
486
|
+
# Project ownership / team information answer
|
|
487
|
+
project-owner-answer:
|
|
488
|
+
type: ai
|
|
489
|
+
group: chat
|
|
490
|
+
depends_on: [project-intent]
|
|
491
|
+
criticality: info
|
|
492
|
+
if: "outputs['project-intent']?.kind === 'owner'"
|
|
493
|
+
guarantee: "(output?.text ?? '').length > 0"
|
|
494
|
+
schema:
|
|
495
|
+
type: object
|
|
496
|
+
properties:
|
|
497
|
+
text:
|
|
498
|
+
type: string
|
|
499
|
+
required: [text]
|
|
500
|
+
ai:
|
|
501
|
+
disableTools: true
|
|
502
|
+
allowedTools: []
|
|
503
|
+
system_prompt: "You explain ownership patterns and how to find the right team."
|
|
504
|
+
prompt: |
|
|
505
|
+
The user wants to know who owns this project:
|
|
506
|
+
{{ outputs['project-intent'].project | default: "unknown-service" }}
|
|
507
|
+
|
|
508
|
+
Since this is a mock configuration, you cannot know the real owner. Explain briefly:
|
|
509
|
+
- how ownership is usually tracked (e.g. service catalog, CODEOWNERS)
|
|
510
|
+
- how the user could look it up
|
|
511
|
+
- any conventions your (imaginary) org uses.
|
|
512
|
+
|
|
513
|
+
Keep the answer short (1–2 paragraphs).
|
|
514
|
+
on_success:
|
|
515
|
+
goto: ask
|
|
516
|
+
|
|
517
|
+
# 4) Mock external integrations for project flows
|
|
518
|
+
#
|
|
519
|
+
# These `script` steps emulate calls to external systems, but only return
|
|
520
|
+
# structured data (no real network calls). They demonstrate how to:
|
|
521
|
+
# - mark external-ish logic (`criticality: external`)
|
|
522
|
+
# - use `assume` to guard preconditions
|
|
523
|
+
# - define JSON Schemas for integration responses.
|
|
524
|
+
|
|
525
|
+
# Emulate reading project status from an external system (service catalog / status API).
|
|
526
|
+
project-status-fetch:
|
|
527
|
+
type: script
|
|
528
|
+
group: integrations
|
|
529
|
+
criticality: external
|
|
530
|
+
depends_on: [project-intent]
|
|
531
|
+
assume:
|
|
532
|
+
- "!!outputs['project-intent']?.project"
|
|
533
|
+
content: |
|
|
534
|
+
const project = (outputs['project-intent'] && outputs['project-intent'].project) || 'unknown-service';
|
|
535
|
+
// In a real organisation this would call an external status API.
|
|
536
|
+
// Here we just mock a stable response.
|
|
537
|
+
return {
|
|
538
|
+
project,
|
|
539
|
+
status: 'green',
|
|
540
|
+
last_deploy: '2025-11-20T12:00:00Z'
|
|
541
|
+
};
|
|
542
|
+
schema:
|
|
543
|
+
type: object
|
|
544
|
+
properties:
|
|
545
|
+
project:
|
|
546
|
+
type: string
|
|
547
|
+
status:
|
|
548
|
+
type: string
|
|
549
|
+
last_deploy:
|
|
550
|
+
type: string
|
|
551
|
+
required: [project, status]
|
|
552
|
+
|
|
553
|
+
# Emulate writing a deployment note or updating a ticket in an external system.
|
|
554
|
+
project-deploy-log:
|
|
555
|
+
type: script
|
|
556
|
+
group: integrations
|
|
557
|
+
criticality: external
|
|
558
|
+
depends_on: [project-intent]
|
|
559
|
+
assume:
|
|
560
|
+
- "!!outputs['project-intent']?.project"
|
|
561
|
+
content: |
|
|
562
|
+
const project = (outputs['project-intent'] && outputs['project-intent'].project) || 'unknown-service';
|
|
563
|
+
const kind = (outputs['project-intent'] && outputs['project-intent'].kind) || 'deployment';
|
|
564
|
+
// In a real system this might POST to a change-management API.
|
|
565
|
+
// We just return a structured record to demonstrate contracts.
|
|
566
|
+
return {
|
|
567
|
+
project,
|
|
568
|
+
kind,
|
|
569
|
+
recorded: true
|
|
570
|
+
};
|
|
571
|
+
schema:
|
|
572
|
+
type: object
|
|
573
|
+
properties:
|
|
574
|
+
project:
|
|
575
|
+
type: string
|
|
576
|
+
kind:
|
|
577
|
+
type: string
|
|
578
|
+
recorded:
|
|
579
|
+
type: boolean
|
|
580
|
+
required: [project, kind, recorded]
|
|
581
|
+
|
|
582
|
+
tests:
|
|
583
|
+
defaults:
|
|
584
|
+
strict: true
|
|
585
|
+
ai_provider: mock
|
|
586
|
+
|
|
587
|
+
cases:
|
|
588
|
+
- name: chat-history-two-turns
|
|
589
|
+
description: Ensure chat_history merges ask/chat-answer with correct roles/order for a single turn.
|
|
590
|
+
flow:
|
|
591
|
+
- name: turns
|
|
592
|
+
event: manual
|
|
593
|
+
fixture: local.minimal
|
|
594
|
+
routing:
|
|
595
|
+
max_loops: 0
|
|
596
|
+
mocks:
|
|
597
|
+
ask[]:
|
|
598
|
+
- "Hello 1"
|
|
599
|
+
route-intent[]:
|
|
600
|
+
- intent: "chat"
|
|
601
|
+
chat-answer[]:
|
|
602
|
+
- text: "Reply 1"
|
|
603
|
+
expect:
|
|
604
|
+
calls:
|
|
605
|
+
- step: ask
|
|
606
|
+
exactly: 1
|
|
607
|
+
- step: route-intent
|
|
608
|
+
exactly: 1
|
|
609
|
+
- step: chat-answer
|
|
610
|
+
exactly: 1
|
|
611
|
+
prompts:
|
|
612
|
+
- step: chat-answer
|
|
613
|
+
index: last
|
|
614
|
+
contains:
|
|
615
|
+
- "User: Hello 1"
|
|
616
|
+
outputs:
|
|
617
|
+
# Ensure assistant reply is present in outputs.history['chat-answer']
|
|
618
|
+
- step: chat-answer
|
|
619
|
+
index: 0
|
|
620
|
+
path: text
|
|
621
|
+
equals: "Reply 1"
|
|
622
|
+
|
|
623
|
+
- name: router-capabilities
|
|
624
|
+
description: Route to capabilities/self-describe branch when intent=capabilities.
|
|
625
|
+
flow:
|
|
626
|
+
- name: capabilities
|
|
627
|
+
event: manual
|
|
628
|
+
fixture: local.minimal
|
|
629
|
+
routing:
|
|
630
|
+
max_loops: 0
|
|
631
|
+
mocks:
|
|
632
|
+
ask[]:
|
|
633
|
+
- "What can you do?"
|
|
634
|
+
route-intent[]:
|
|
635
|
+
- intent: "capabilities"
|
|
636
|
+
capabilities-answer[]:
|
|
637
|
+
- text: "I can help with summaries and project questions."
|
|
638
|
+
expect:
|
|
639
|
+
calls:
|
|
640
|
+
- step: ask
|
|
641
|
+
exactly: 1
|
|
642
|
+
- step: route-intent
|
|
643
|
+
exactly: 1
|
|
644
|
+
- step: capabilities-answer
|
|
645
|
+
exactly: 1
|
|
646
|
+
|
|
647
|
+
- name: router-thread-summary
|
|
648
|
+
description: Route to thread-summary branch when intent=thread_summary.
|
|
649
|
+
flow:
|
|
650
|
+
- name: thread-summary
|
|
651
|
+
event: manual
|
|
652
|
+
fixture: local.minimal
|
|
653
|
+
routing:
|
|
654
|
+
max_loops: 0
|
|
655
|
+
mocks:
|
|
656
|
+
ask[]:
|
|
657
|
+
- "Summarize the discussion"
|
|
658
|
+
route-intent[]:
|
|
659
|
+
- intent: "thread_summary"
|
|
660
|
+
summarize-thread[]:
|
|
661
|
+
- text: "Summary text"
|
|
662
|
+
expect:
|
|
663
|
+
calls:
|
|
664
|
+
- step: ask
|
|
665
|
+
exactly: 1
|
|
666
|
+
- step: route-intent
|
|
667
|
+
exactly: 1
|
|
668
|
+
- step: summarize-thread
|
|
669
|
+
exactly: 1
|
|
670
|
+
|
|
671
|
+
- name: router-project-status
|
|
672
|
+
description: Route to project status path and use status fetch output.
|
|
673
|
+
flow:
|
|
674
|
+
- name: project-status
|
|
675
|
+
event: manual
|
|
676
|
+
fixture: local.minimal
|
|
677
|
+
routing:
|
|
678
|
+
max_loops: 0
|
|
679
|
+
mocks:
|
|
680
|
+
ask[]:
|
|
681
|
+
- "What is the status of checkout-service?"
|
|
682
|
+
route-intent[]:
|
|
683
|
+
- intent: "project_help"
|
|
684
|
+
project-intent[]:
|
|
685
|
+
- project: "checkout-service"
|
|
686
|
+
kind: "status"
|
|
687
|
+
project-status-fetch[]:
|
|
688
|
+
- project: "checkout-service"
|
|
689
|
+
status: "green"
|
|
690
|
+
last_deploy: "2025-11-20T12:00:00Z"
|
|
691
|
+
project-status-answer[]:
|
|
692
|
+
- text: "Status reply"
|
|
693
|
+
expect:
|
|
694
|
+
calls:
|
|
695
|
+
- step: ask
|
|
696
|
+
exactly: 1
|
|
697
|
+
- step: route-intent
|
|
698
|
+
exactly: 1
|
|
699
|
+
- step: project-intent
|
|
700
|
+
exactly: 1
|
|
701
|
+
- step: project-status-fetch
|
|
702
|
+
exactly: 1
|
|
703
|
+
- step: project-status-answer
|
|
704
|
+
exactly: 1
|
|
705
|
+
|
|
706
|
+
- name: deployment-branch-routing
|
|
707
|
+
description: Route to deployment helper branch, log progress, and run confirm + answer once.
|
|
708
|
+
flow:
|
|
709
|
+
- name: deploy-loop
|
|
710
|
+
event: manual
|
|
711
|
+
fixture: local.minimal
|
|
712
|
+
routing:
|
|
713
|
+
# Set max_loops: 0 to test single pass through the deployment flow.
|
|
714
|
+
# The goto: ask in project-deploy-answer would otherwise trigger loops.
|
|
715
|
+
max_loops: 0
|
|
716
|
+
mocks:
|
|
717
|
+
ask[]:
|
|
718
|
+
- "Help me deploy service"
|
|
719
|
+
route-intent[]:
|
|
720
|
+
- intent: "project_help"
|
|
721
|
+
project-intent[]:
|
|
722
|
+
- project: "svc-api"
|
|
723
|
+
kind: "deployment"
|
|
724
|
+
project-deploy-confirm[]:
|
|
725
|
+
- "How do I deploy?"
|
|
726
|
+
project-deploy-answer[]:
|
|
727
|
+
- text: "First deployment answer"
|
|
728
|
+
done: true
|
|
729
|
+
expect:
|
|
730
|
+
calls:
|
|
731
|
+
- step: ask
|
|
732
|
+
exactly: 1
|
|
733
|
+
- step: route-intent
|
|
734
|
+
exactly: 1
|
|
735
|
+
- step: project-intent
|
|
736
|
+
exactly: 1
|
|
737
|
+
- step: deployment-progress-log
|
|
738
|
+
exactly: 1
|
|
739
|
+
- step: project-deploy-confirm
|
|
740
|
+
exactly: 1
|
|
741
|
+
- step: project-deploy-answer
|
|
742
|
+
exactly: 1
|
|
743
|
+
- step: project-status-fetch
|
|
744
|
+
exactly: 1
|
|
745
|
+
- step: project-deploy-log
|
|
746
|
+
exactly: 1
|
|
747
|
+
|
|
748
|
+
- name: capabilities-loop-routing
|
|
749
|
+
description: Ensure capabilities intent does not cause router to loop when routing is enabled.
|
|
750
|
+
flow:
|
|
751
|
+
- name: capabilities-loop
|
|
752
|
+
event: manual
|
|
753
|
+
fixture: local.minimal
|
|
754
|
+
routing:
|
|
755
|
+
# Non-zero loop budget; we assert exact call counts to detect unintended loops.
|
|
756
|
+
max_loops: 5
|
|
757
|
+
mocks:
|
|
758
|
+
ask[]:
|
|
759
|
+
- "What can you do?"
|
|
760
|
+
route-intent[]:
|
|
761
|
+
- intent: "capabilities"
|
|
762
|
+
capabilities-answer[]:
|
|
763
|
+
- text: "I can help with summaries and project questions."
|
|
764
|
+
expect:
|
|
765
|
+
calls:
|
|
766
|
+
- step: ask
|
|
767
|
+
# In this manual test, routing transitions are exercised
|
|
768
|
+
# fully, so the router loops through the capabilities
|
|
769
|
+
# branch twice before the loop budget stops a third goto.
|
|
770
|
+
# We assert the exact call counts to keep this deterministic.
|
|
771
|
+
exactly: 3
|
|
772
|
+
- step: route-intent
|
|
773
|
+
exactly: 3
|
|
774
|
+
- step: capabilities-answer
|
|
775
|
+
exactly: 3
|