@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.
Files changed (235) hide show
  1. package/README.md +6 -0
  2. package/defaults/task-refinement.yaml +7 -3
  3. package/defaults/visor.tests.yaml +13 -2
  4. package/defaults/visor.yaml +1 -0
  5. package/dist/663.index.js +3 -2
  6. package/dist/80.index.js +3 -2
  7. package/dist/ai-review-service.d.ts +13 -9
  8. package/dist/ai-review-service.d.ts.map +1 -1
  9. package/dist/cli-main.d.ts.map +1 -1
  10. package/dist/cli.d.ts.map +1 -1
  11. package/dist/config.d.ts.map +1 -1
  12. package/dist/debug-visualizer/ws-server.d.ts +7 -1
  13. package/dist/debug-visualizer/ws-server.d.ts.map +1 -1
  14. package/dist/defaults/task-refinement.yaml +7 -3
  15. package/dist/defaults/visor.tests.yaml +13 -2
  16. package/dist/defaults/visor.yaml +1 -0
  17. package/dist/docs/advanced-ai.md +60 -1
  18. package/dist/docs/ai-configuration.md +67 -0
  19. package/dist/docs/ai-custom-tools-usage.md +261 -0
  20. package/dist/docs/ai-custom-tools.md +392 -0
  21. package/dist/docs/bot-transports-rfc.md +23 -0
  22. package/dist/docs/configuration.md +21 -0
  23. package/dist/docs/engine-pause-resume-rfc.md +192 -0
  24. package/dist/docs/lifecycle-hooks.md +253 -0
  25. package/dist/docs/liquid-templates.md +143 -0
  26. package/dist/docs/providers/git-checkout.md +589 -0
  27. package/dist/docs/recipes.md +458 -5
  28. package/dist/docs/rfc/git-checkout-step.md +601 -0
  29. package/dist/docs/rfc/on_init-hook.md +1294 -0
  30. package/dist/docs/rfc/workspace-isolation.md +216 -0
  31. package/dist/docs/router-patterns.md +339 -0
  32. package/dist/event-bus/types.d.ts +14 -0
  33. package/dist/event-bus/types.d.ts.map +1 -1
  34. package/dist/examples/ai-custom-tools-example.yaml +206 -0
  35. package/dist/examples/ai-custom-tools-simple.yaml +76 -0
  36. package/dist/examples/git-checkout-basic.yaml +32 -0
  37. package/dist/examples/git-checkout-compare.yaml +59 -0
  38. package/dist/examples/git-checkout-cross-repo.yaml +76 -0
  39. package/dist/examples/on-init-import-demo.yaml +179 -0
  40. package/dist/examples/reusable-tools.yaml +92 -0
  41. package/dist/examples/reusable-workflows.yaml +88 -0
  42. package/dist/examples/session-reuse-self.yaml +81 -0
  43. package/dist/examples/slack-simple-chat.yaml +775 -0
  44. package/dist/failure-condition-evaluator.d.ts +2 -0
  45. package/dist/failure-condition-evaluator.d.ts.map +1 -1
  46. package/dist/frontends/github-frontend.d.ts +20 -0
  47. package/dist/frontends/github-frontend.d.ts.map +1 -1
  48. package/dist/frontends/host.d.ts +4 -0
  49. package/dist/frontends/host.d.ts.map +1 -1
  50. package/dist/frontends/slack-frontend.d.ts +58 -0
  51. package/dist/frontends/slack-frontend.d.ts.map +1 -0
  52. package/dist/generated/config-schema.d.ts +409 -41
  53. package/dist/generated/config-schema.d.ts.map +1 -1
  54. package/dist/generated/config-schema.json +436 -47
  55. package/dist/github-comments.d.ts +2 -0
  56. package/dist/github-comments.d.ts.map +1 -1
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +83587 -56085
  59. package/dist/liquid-extensions.d.ts.map +1 -1
  60. package/dist/logger.d.ts +1 -0
  61. package/dist/logger.d.ts.map +1 -1
  62. package/dist/output/traces/{run-2025-11-21T11-50-46-505Z.ndjson → run-2026-01-21T05-37-24-446Z.ndjson} +91 -91
  63. package/dist/output/traces/run-2026-01-21T05-38-18-580Z.ndjson +1067 -0
  64. package/dist/output-formatters.d.ts.map +1 -1
  65. package/dist/providers/ai-check-provider.d.ts +12 -0
  66. package/dist/providers/ai-check-provider.d.ts.map +1 -1
  67. package/dist/providers/check-provider-registry.d.ts.map +1 -1
  68. package/dist/providers/check-provider.interface.d.ts +9 -0
  69. package/dist/providers/check-provider.interface.d.ts.map +1 -1
  70. package/dist/providers/command-check-provider.d.ts.map +1 -1
  71. package/dist/providers/custom-tool-executor.d.ts.map +1 -1
  72. package/dist/providers/git-checkout-provider.d.ts +25 -0
  73. package/dist/providers/git-checkout-provider.d.ts.map +1 -0
  74. package/dist/providers/http-client-provider.d.ts +3 -0
  75. package/dist/providers/http-client-provider.d.ts.map +1 -1
  76. package/dist/providers/human-input-check-provider.d.ts +2 -0
  77. package/dist/providers/human-input-check-provider.d.ts.map +1 -1
  78. package/dist/providers/log-check-provider.d.ts.map +1 -1
  79. package/dist/providers/mcp-check-provider.d.ts +1 -1
  80. package/dist/providers/mcp-check-provider.d.ts.map +1 -1
  81. package/dist/providers/mcp-custom-sse-server.d.ts +66 -0
  82. package/dist/providers/mcp-custom-sse-server.d.ts.map +1 -0
  83. package/dist/providers/memory-check-provider.d.ts.map +1 -1
  84. package/dist/providers/script-check-provider.d.ts.map +1 -1
  85. package/dist/providers/workflow-check-provider.d.ts.map +1 -1
  86. package/dist/reviewer.d.ts.map +1 -1
  87. package/dist/sdk/check-provider-registry-534KL5HT.mjs +27 -0
  88. package/dist/sdk/chunk-23L3QRYX.mjs +16872 -0
  89. package/dist/sdk/chunk-23L3QRYX.mjs.map +1 -0
  90. package/dist/sdk/{chunk-OOZITMRU.mjs → chunk-3OMWVM6J.mjs} +11 -1
  91. package/dist/sdk/{chunk-OOZITMRU.mjs.map → chunk-3OMWVM6J.mjs.map} +1 -1
  92. package/dist/sdk/{chunk-37ZSCMFC.mjs → chunk-7UK3NIIT.mjs} +2 -2
  93. package/dist/sdk/{chunk-VMPLF6FT.mjs → chunk-AGIZJ4UZ.mjs} +50 -4
  94. package/dist/sdk/chunk-AGIZJ4UZ.mjs.map +1 -0
  95. package/dist/sdk/{chunk-IEO6CFLG.mjs → chunk-AIVFBIS4.mjs} +161 -5
  96. package/dist/sdk/chunk-AIVFBIS4.mjs.map +1 -0
  97. package/dist/sdk/chunk-AK6BVWIT.mjs +426 -0
  98. package/dist/sdk/chunk-AK6BVWIT.mjs.map +1 -0
  99. package/dist/sdk/chunk-AUT26LHW.mjs +139 -0
  100. package/dist/sdk/chunk-AUT26LHW.mjs.map +1 -0
  101. package/dist/sdk/chunk-BOVFH3LI.mjs +232 -0
  102. package/dist/sdk/chunk-BOVFH3LI.mjs.map +1 -0
  103. package/dist/sdk/chunk-HTOKWMPO.mjs +157 -0
  104. package/dist/sdk/chunk-HTOKWMPO.mjs.map +1 -0
  105. package/dist/sdk/{chunk-6Y4YTKCF.mjs → chunk-NAW3DB3I.mjs} +2 -2
  106. package/dist/sdk/{chunk-OWUVOILT.mjs → chunk-QR7MOMJH.mjs} +4 -3
  107. package/dist/sdk/{chunk-OWUVOILT.mjs.map → chunk-QR7MOMJH.mjs.map} +1 -1
  108. package/dist/sdk/{chunk-PTL3K3PN.mjs → chunk-QY2XYPEV.mjs} +488 -60
  109. package/dist/sdk/chunk-QY2XYPEV.mjs.map +1 -0
  110. package/dist/sdk/{chunk-OZJ263FM.mjs → chunk-SIWNBRTK.mjs} +29 -215
  111. package/dist/sdk/chunk-SIWNBRTK.mjs.map +1 -0
  112. package/dist/sdk/command-executor-TYUV6HUS.mjs +14 -0
  113. package/dist/sdk/{config-M4ZNO6NU.mjs → config-YNC2EOOT.mjs} +5 -3
  114. package/dist/sdk/{failure-condition-evaluator-NBO5YRXW.mjs → failure-condition-evaluator-YGTF2GHG.mjs} +6 -5
  115. package/dist/sdk/{github-frontend-4AWRJT7D.mjs → github-frontend-SIAEOCON.mjs} +190 -12
  116. package/dist/sdk/github-frontend-SIAEOCON.mjs.map +1 -0
  117. package/dist/sdk/{host-7GBC3S7L.mjs → host-DXUYTNMU.mjs} +5 -2
  118. package/dist/sdk/host-DXUYTNMU.mjs.map +1 -0
  119. package/dist/sdk/{liquid-extensions-C7EG3YKH.mjs → liquid-extensions-PKWCKK7E.mjs} +5 -4
  120. package/dist/sdk/memory-store-XGBB7LX7.mjs +12 -0
  121. package/dist/sdk/prompt-state-YRJY6QAL.mjs +16 -0
  122. package/dist/sdk/{renderer-schema-6RF26VUS.mjs → renderer-schema-LPKN5UJS.mjs} +3 -2
  123. package/dist/sdk/{renderer-schema-6RF26VUS.mjs.map → renderer-schema-LPKN5UJS.mjs.map} +1 -1
  124. package/dist/sdk/{routing-RP56JTV2.mjs → routing-6N45MJ4F.mjs} +7 -6
  125. package/dist/sdk/sdk.d.mts +219 -5
  126. package/dist/sdk/sdk.d.ts +219 -5
  127. package/dist/sdk/sdk.js +21329 -14908
  128. package/dist/sdk/sdk.js.map +1 -1
  129. package/dist/sdk/sdk.mjs +407 -12874
  130. package/dist/sdk/sdk.mjs.map +1 -1
  131. package/dist/sdk/{session-registry-N5FFYFTM.mjs → session-registry-4E6YRQ77.mjs} +2 -2
  132. package/dist/sdk/session-registry-4E6YRQ77.mjs.map +1 -0
  133. package/dist/sdk/slack-frontend-BVKW3GD5.mjs +735 -0
  134. package/dist/sdk/slack-frontend-BVKW3GD5.mjs.map +1 -0
  135. package/dist/sdk/{tracer-init-WP4X46IF.mjs → tracer-init-GSLPPLCD.mjs} +2 -2
  136. package/dist/sdk/tracer-init-GSLPPLCD.mjs.map +1 -0
  137. package/dist/sdk/workflow-registry-R6KSACFR.mjs +12 -0
  138. package/dist/sdk/workflow-registry-R6KSACFR.mjs.map +1 -0
  139. package/dist/slack/adapter.d.ts +36 -0
  140. package/dist/slack/adapter.d.ts.map +1 -0
  141. package/dist/slack/cache-prewarmer.d.ts +31 -0
  142. package/dist/slack/cache-prewarmer.d.ts.map +1 -0
  143. package/dist/slack/client.d.ts +77 -0
  144. package/dist/slack/client.d.ts.map +1 -0
  145. package/dist/slack/markdown.d.ts +45 -0
  146. package/dist/slack/markdown.d.ts.map +1 -0
  147. package/dist/slack/prompt-state.d.ts +33 -0
  148. package/dist/slack/prompt-state.d.ts.map +1 -0
  149. package/dist/slack/rate-limiter.d.ts +56 -0
  150. package/dist/slack/rate-limiter.d.ts.map +1 -0
  151. package/dist/slack/signature.d.ts +2 -0
  152. package/dist/slack/signature.d.ts.map +1 -0
  153. package/dist/slack/socket-runner.d.ts +42 -0
  154. package/dist/slack/socket-runner.d.ts.map +1 -0
  155. package/dist/slack/thread-cache.d.ts +51 -0
  156. package/dist/slack/thread-cache.d.ts.map +1 -0
  157. package/dist/state-machine/context/build-engine-context.d.ts +8 -0
  158. package/dist/state-machine/context/build-engine-context.d.ts.map +1 -1
  159. package/dist/state-machine/dispatch/execution-invoker.d.ts.map +1 -1
  160. package/dist/state-machine/dispatch/foreach-processor.d.ts.map +1 -1
  161. package/dist/state-machine/dispatch/on-init-handlers.d.ts +43 -0
  162. package/dist/state-machine/dispatch/on-init-handlers.d.ts.map +1 -0
  163. package/dist/state-machine/dispatch/stats-manager.d.ts.map +1 -1
  164. package/dist/state-machine/dispatch/template-renderer.d.ts.map +1 -1
  165. package/dist/state-machine/runner.d.ts +6 -0
  166. package/dist/state-machine/runner.d.ts.map +1 -1
  167. package/dist/state-machine/states/level-dispatch.d.ts.map +1 -1
  168. package/dist/state-machine/states/plan-ready.d.ts.map +1 -1
  169. package/dist/state-machine/states/routing.d.ts.map +1 -1
  170. package/dist/state-machine/states/wave-planning.d.ts.map +1 -1
  171. package/dist/state-machine/workflow-projection.d.ts.map +1 -1
  172. package/dist/state-machine-execution-engine.d.ts +21 -9
  173. package/dist/state-machine-execution-engine.d.ts.map +1 -1
  174. package/dist/telemetry/state-capture.d.ts +5 -0
  175. package/dist/telemetry/state-capture.d.ts.map +1 -1
  176. package/dist/test-runner/core/flow-stage.d.ts.map +1 -1
  177. package/dist/test-runner/core/test-execution-wrapper.d.ts.map +1 -1
  178. package/dist/test-runner/evaluators.d.ts +37 -4
  179. package/dist/test-runner/evaluators.d.ts.map +1 -1
  180. package/dist/test-runner/index.d.ts +7 -0
  181. package/dist/test-runner/index.d.ts.map +1 -1
  182. package/dist/test-runner/recorders/slack-recorder.d.ts +17 -0
  183. package/dist/test-runner/recorders/slack-recorder.d.ts.map +1 -0
  184. package/dist/test-runner/validator.d.ts.map +1 -1
  185. package/dist/traces/{run-2025-11-21T11-50-46-505Z.ndjson → run-2026-01-21T05-37-24-446Z.ndjson} +91 -91
  186. package/dist/traces/run-2026-01-21T05-38-18-580Z.ndjson +1067 -0
  187. package/dist/types/bot.d.ts +109 -0
  188. package/dist/types/bot.d.ts.map +1 -0
  189. package/dist/types/cli.d.ts +4 -0
  190. package/dist/types/cli.d.ts.map +1 -1
  191. package/dist/types/config.d.ts +182 -5
  192. package/dist/types/config.d.ts.map +1 -1
  193. package/dist/types/engine.d.ts +5 -0
  194. package/dist/types/engine.d.ts.map +1 -1
  195. package/dist/types/git-checkout.d.ts +76 -0
  196. package/dist/types/git-checkout.d.ts.map +1 -0
  197. package/dist/utils/json-text-extractor.d.ts +17 -0
  198. package/dist/utils/json-text-extractor.d.ts.map +1 -0
  199. package/dist/utils/sandbox.d.ts +10 -0
  200. package/dist/utils/sandbox.d.ts.map +1 -1
  201. package/dist/utils/template-context.d.ts +1 -0
  202. package/dist/utils/template-context.d.ts.map +1 -1
  203. package/dist/utils/tracer-init.d.ts.map +1 -1
  204. package/dist/utils/workspace-manager.d.ts +118 -0
  205. package/dist/utils/workspace-manager.d.ts.map +1 -0
  206. package/dist/utils/worktree-cleanup.d.ts +33 -0
  207. package/dist/utils/worktree-cleanup.d.ts.map +1 -0
  208. package/dist/utils/worktree-manager.d.ts +153 -0
  209. package/dist/utils/worktree-manager.d.ts.map +1 -0
  210. package/dist/webhook-server.d.ts.map +1 -1
  211. package/dist/workflow-executor.d.ts.map +1 -1
  212. package/dist/workflow-registry.d.ts.map +1 -1
  213. package/package.json +5 -4
  214. package/dist/output/traces/run-2025-11-21T11-51-33-674Z.ndjson +0 -839
  215. package/dist/sdk/chunk-IEO6CFLG.mjs.map +0 -1
  216. package/dist/sdk/chunk-JEHPDJIF.mjs +0 -223
  217. package/dist/sdk/chunk-JEHPDJIF.mjs.map +0 -1
  218. package/dist/sdk/chunk-OZJ263FM.mjs.map +0 -1
  219. package/dist/sdk/chunk-PTL3K3PN.mjs.map +0 -1
  220. package/dist/sdk/chunk-VMPLF6FT.mjs.map +0 -1
  221. package/dist/sdk/github-frontend-4AWRJT7D.mjs.map +0 -1
  222. package/dist/sdk/host-7GBC3S7L.mjs.map +0 -1
  223. package/dist/sdk/memory-store-GJACZC2A.mjs +0 -11
  224. package/dist/sdk/workflow-registry-2YIIXQCK.mjs +0 -11
  225. package/dist/traces/run-2025-11-21T11-51-33-674Z.ndjson +0 -839
  226. /package/dist/sdk/{config-M4ZNO6NU.mjs.map → check-provider-registry-534KL5HT.mjs.map} +0 -0
  227. /package/dist/sdk/{chunk-37ZSCMFC.mjs.map → chunk-7UK3NIIT.mjs.map} +0 -0
  228. /package/dist/sdk/{chunk-6Y4YTKCF.mjs.map → chunk-NAW3DB3I.mjs.map} +0 -0
  229. /package/dist/sdk/{failure-condition-evaluator-NBO5YRXW.mjs.map → command-executor-TYUV6HUS.mjs.map} +0 -0
  230. /package/dist/sdk/{liquid-extensions-C7EG3YKH.mjs.map → config-YNC2EOOT.mjs.map} +0 -0
  231. /package/dist/sdk/{memory-store-GJACZC2A.mjs.map → failure-condition-evaluator-YGTF2GHG.mjs.map} +0 -0
  232. /package/dist/sdk/{routing-RP56JTV2.mjs.map → liquid-extensions-PKWCKK7E.mjs.map} +0 -0
  233. /package/dist/sdk/{session-registry-N5FFYFTM.mjs.map → memory-store-XGBB7LX7.mjs.map} +0 -0
  234. /package/dist/sdk/{tracer-init-WP4X46IF.mjs.map → prompt-state-YRJY6QAL.mjs.map} +0 -0
  235. /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