@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,216 @@
1
+ # Isolated Workspace with Full Run Separation
2
+
3
+ ## Goal
4
+
5
+ Provide **full isolation between parallel visor runs** with human-readable project names. Each run gets its own workspace in `/tmp` containing worktrees for all projects, ensuring no interference between concurrent executions.
6
+
7
+ ## Proposed Structure
8
+
9
+ ```
10
+ /tmp/visor-workspaces/<sessionId>/
11
+ ├── visor2/ → worktree of main project (full isolated copy)
12
+ ├── tyk-docs/ → worktree of tyk-docs repo (isolated)
13
+ ├── tyk-gateway/ → worktree of tyk-gateway repo (isolated)
14
+ └── ...
15
+ ```
16
+
17
+ **Key Design Decisions**:
18
+ - **Workspace ID**: Use `sessionId` (UUID from EngineContext)
19
+ - **Location**: `/tmp/visor-workspaces/` (configurable via `VISOR_WORKSPACE_PATH` env var)
20
+ - **Main project**: Worktree created automatically on run start (not symlink - full isolation)
21
+ - **Checkout projects**: Worktrees with human-readable names (repo name extracted from `owner/repo`)
22
+ - **workingDirectory**: All steps use workspace path as their working directory
23
+
24
+ ## Why /tmp Instead of .visor/?
25
+
26
+ Git doesn't allow worktrees inside the same repository tree. Since `.visor/` is inside the project, we can't create a worktree of the main project there. Using `/tmp` (external path) solves this:
27
+ - Worktrees can be created for any repo including the main project
28
+ - Full isolation - each run has its own copy of everything
29
+ - Clean separation from the project directory
30
+
31
+ ## Implementation Plan
32
+
33
+ ### 1. Create WorkspaceManager class
34
+
35
+ **File**: `src/utils/workspace-manager.ts`
36
+
37
+ ```typescript
38
+ export class WorkspaceManager {
39
+ private sessionId: string;
40
+ private basePath: string; // /tmp/visor-workspaces
41
+ private workspacePath: string; // /tmp/visor-workspaces/<sessionId>
42
+ private originalPath: string; // Original cwd where visor was invoked
43
+
44
+ // Singleton per session
45
+ static getInstance(sessionId: string, originalPath: string, config?: WorkspaceConfig): WorkspaceManager
46
+
47
+ // Initialize workspace and create main project worktree
48
+ async initialize(): Promise<WorkspaceInfo>
49
+
50
+ // Add a project (called by git-checkout, creates symlink to worktree)
51
+ async addProject(projectName: string, worktreePath: string): Promise<string>
52
+
53
+ // Get workspace path (for use as workingDirectory)
54
+ getWorkspacePath(): string
55
+
56
+ // Cleanup workspace on run end
57
+ async cleanup(): Promise<void>
58
+ }
59
+ ```
60
+
61
+ **Key behaviors**:
62
+ - `initialize()`:
63
+ 1. Creates `/tmp/visor-workspaces/<sessionId>/`
64
+ 2. Creates worktree of main project at `<workspace>/<project-name>/`
65
+ 3. Registers cleanup handlers
66
+ - `addProject()`: Creates symlink from human-readable name to actual worktree path
67
+ - `extractProjectName()`: Helper to get repo name from `owner/repo` format
68
+
69
+ ### 2. Initialize workspace at engine startup
70
+
71
+ **File**: `src/state-machine/context/build-engine-context.ts`
72
+
73
+ At the start of `buildEngineContextForRun()`:
74
+ 1. Create WorkspaceManager instance with `sessionId`
75
+ 2. Initialize workspace (creates main project worktree)
76
+ 3. Set `context.workingDirectory` to workspace path
77
+ 4. Store original path in `context.originalWorkingDirectory`
78
+
79
+ ```typescript
80
+ // In buildEngineContextForRun():
81
+ const workspace = WorkspaceManager.getInstance(sessionId, workingDirectory);
82
+ await workspace.initialize();
83
+
84
+ return {
85
+ // ...
86
+ workingDirectory: workspace.getWorkspacePath(), // All steps use this
87
+ originalWorkingDirectory: workingDirectory, // For reference
88
+ workspace, // For git-checkout to add projects
89
+ };
90
+ ```
91
+
92
+ ### 3. Integrate git-checkout with workspace
93
+
94
+ **File**: `src/providers/git-checkout-provider.ts`
95
+
96
+ After creating worktree, add it to workspace:
97
+ ```typescript
98
+ // After worktree creation:
99
+ const workspace = (context as any)?.workspace;
100
+ if (workspace?.isEnabled()) {
101
+ const projectName = extractProjectName(resolvedRepository);
102
+ const workspacePath = await workspace.addProject(projectName, worktree.path);
103
+ output.workspace_path = workspacePath;
104
+ }
105
+ ```
106
+
107
+ ### 4. Update types
108
+
109
+ **File**: `src/types/git-checkout.ts`
110
+ ```typescript
111
+ export interface GitCheckoutOutput {
112
+ // ... existing fields ...
113
+ workspace_path?: string; // Path within workspace (human-readable)
114
+ }
115
+ ```
116
+
117
+ **File**: `src/types/engine.ts` (or wherever EngineContext is defined)
118
+ ```typescript
119
+ export interface EngineContext {
120
+ // ... existing fields ...
121
+ originalWorkingDirectory: string; // Where visor was invoked
122
+ workspace?: WorkspaceManager; // Workspace manager instance
123
+ }
124
+ ```
125
+
126
+ ### 5. Handle duplicate project names
127
+
128
+ When multiple checkouts have the same repo name:
129
+ - First: `tyk-docs`
130
+ - Second: `tyk-docs-2`
131
+ - Or use `description` if provided
132
+
133
+ ### 6. Cleanup integration
134
+
135
+ **Automatic cleanup on**:
136
+ - Normal run completion
137
+ - SIGINT/SIGTERM
138
+ - Uncaught exceptions
139
+
140
+ **Cleanup actions**:
141
+ 1. Remove main project worktree via `git worktree remove`
142
+ 2. Remove workspace directory
143
+ 3. Note: git-checkout worktrees are managed separately by WorktreeManager
144
+
145
+ ### 7. Configuration
146
+
147
+ ```typescript
148
+ // Environment variable
149
+ VISOR_WORKSPACE_PATH=/custom/path // Override base path
150
+
151
+ // Future: config file option
152
+ workspace:
153
+ enabled: true
154
+ base_path: /tmp/visor-workspaces
155
+ cleanup_on_exit: true
156
+ ```
157
+
158
+ ## Files to Create/Modify
159
+
160
+ | File | Action | Description |
161
+ |------|--------|-------------|
162
+ | `src/utils/workspace-manager.ts` | Create | WorkspaceManager class |
163
+ | `src/state-machine/context/build-engine-context.ts` | Modify | Initialize workspace, set workingDirectory |
164
+ | `src/providers/git-checkout-provider.ts` | Modify | Add projects to workspace |
165
+ | `src/types/git-checkout.ts` | Modify | Add `workspace_path` to output |
166
+ | `src/types/engine.ts` | Modify | Add workspace fields to EngineContext |
167
+ | `tests/unit/workspace-manager.test.ts` | Create | Unit tests |
168
+
169
+ ## Example Flow
170
+
171
+ ```
172
+ 1. User runs: visor --check code-review
173
+ └─ cwd: /home/user/my-project
174
+
175
+ 2. Engine starts:
176
+ └─ sessionId: abc-123-uuid
177
+ └─ WorkspaceManager.initialize()
178
+ └─ Creates: /tmp/visor-workspaces/abc-123-uuid/
179
+ └─ Creates worktree: /tmp/visor-workspaces/abc-123-uuid/my-project/
180
+ └─ workingDirectory = /tmp/visor-workspaces/abc-123-uuid/
181
+
182
+ 3. git-checkout step runs:
183
+ └─ Creates worktree in .visor/worktrees/worktrees/tyk-docs-main-xyz789
184
+ └─ workspace.addProject("tyk-docs", worktreePath)
185
+ └─ Creates symlink: /tmp/visor-workspaces/abc-123-uuid/tyk-docs/
186
+ └─ Output: { path: ".visor/worktrees/...", workspace_path: "/tmp/.../tyk-docs" }
187
+
188
+ 4. AI step runs:
189
+ └─ workingDirectory = /tmp/visor-workspaces/abc-123-uuid/
190
+ └─ Sees: my-project/, tyk-docs/
191
+ └─ Can access all projects with human-readable names
192
+
193
+ 5. Run completes:
194
+ └─ workspace.cleanup()
195
+ └─ Removes worktree: my-project/
196
+ └─ Removes directory: /tmp/visor-workspaces/abc-123-uuid/
197
+ ```
198
+
199
+ ## Benefits
200
+
201
+ 1. **Full isolation**: Parallel runs can't interfere (separate workspaces)
202
+ 2. **Human-readable paths**: `tyk-docs/` instead of `tyk-tyk-docs-main-ab12cd34/`
203
+ 3. **Unified workspace**: All projects for a run accessible from one directory
204
+ 4. **Automatic workingDirectory**: All steps operate in isolated scope
205
+ 5. **No git-in-git issues**: Workspace is in /tmp, outside any repository
206
+ 6. **Clean cleanup**: Just remove the workspace directory
207
+
208
+ ## Trade-offs
209
+
210
+ 1. **Disk usage**: Each run creates a worktree of main project (uses git's efficient storage, but still some overhead)
211
+
212
+ 2. **Startup time**: Small delay to create main project worktree
213
+
214
+ 3. **Windows**: Git worktrees work on Windows, but paths might need adjustment
215
+
216
+ 4. **Debugging**: Workspace is in /tmp - might be cleared on reboot. Consider `cleanup_on_exit: false` option for debugging.
@@ -0,0 +1,339 @@
1
+ # Router Steps, Transitions, and `depends_on`
2
+
3
+ This document describes recommended patterns for “router” checks (AI or script steps that classify intent and decide where to go next), and how they should interact with `depends_on`, `transitions`, and loops.
4
+
5
+ The goal is:
6
+
7
+ - predictable routing (no accidental loops),
8
+ - a clean mental model for control‑flow vs data dependencies,
9
+ - and good DX for authoring chat‑like and org‑automation workflows.
10
+
11
+ Examples referenced here:
12
+
13
+ - `examples/slack-simple-chat.yaml` (Slack org assistant: `ask` → `route-intent` → branches)
14
+ - `docs/recipes.md` (general routing patterns and chat examples)
15
+
16
+ ---
17
+
18
+ ## Concepts: control vs data edges
19
+
20
+ There are three main “edges” between checks:
21
+
22
+ - **Data dependency**: `depends_on`
23
+ - Meaning: “B needs A’s output to compute.”
24
+ - Engine impact: A is always scheduled before B in the DAG; forward‑run uses this to pull parents into new waves.
25
+
26
+ - **Control‑flow edge**: `on_success.transitions` / `on_fail.transitions` / `on_finish.transitions` and `goto`
27
+ - Meaning: “when A finishes (and a condition holds), *schedule* B.”
28
+ - Engine impact: emits `ForwardRunRequested` events; WavePlanning then plans a subgraph for the target and its dependencies.
29
+
30
+ - **Guards**: `if` and `assume`
31
+ - `if`: soft gate. B is *skipped* when the condition is false.
32
+ - `assume`: hard precondition. If B runs and the condition is false, that’s a contract violation.
33
+
34
+ Best DX comes from keeping these roles distinct:
35
+
36
+ - Use `depends_on` for real data prerequisites.
37
+ - Use `transitions` / `goto` for routing and loops.
38
+ - Use `if` / `assume` for gating semantics, not for wiring the graph.
39
+
40
+ ---
41
+
42
+ ## Anti‑pattern: router as both dependency and transition source
43
+
44
+ **Shape (simplified):**
45
+
46
+ ```yaml
47
+ ask:
48
+ type: human-input
49
+ group: chat
50
+
51
+ route-intent:
52
+ type: ai
53
+ group: chat
54
+ depends_on: [ask]
55
+ on_success:
56
+ transitions:
57
+ - when: "output.intent === 'capabilities'"
58
+ to: capabilities-answer
59
+
60
+ capabilities-answer:
61
+ type: ai
62
+ group: chat
63
+ depends_on: [route-intent]
64
+ if: "outputs['route-intent']?.intent === 'capabilities'"
65
+ on_success:
66
+ goto: ask
67
+ ```
68
+
69
+ Here, the router (`route-intent`) is connected to the branch (`capabilities-answer`) **twice**:
70
+
71
+ - as a data dependency: `capabilities-answer.depends_on: [route-intent]`
72
+ - as a control‑flow source: `route-intent.on_success.transitions → capabilities-answer`
73
+
74
+ ### Why this is a problem
75
+
76
+ When `route-intent` finishes, `transitions` fire and emit a `ForwardRunRequested(target='capabilities-answer')`.
77
+
78
+ WavePlanning then:
79
+
80
+ 1. Builds the forward‑run subset starting from the target.
81
+ 2. Adds **all transitive dependencies** of that target via `depends_on`.
82
+ - `capabilities-answer.depends_on: [route-intent]`
83
+ - `route-intent.depends_on: [ask]`
84
+ 3. Schedules a wave that re‑runs `route-intent` (and `ask`), then `capabilities-answer`.
85
+
86
+ Each time `route-intent` re‑runs, its `transitions` fire again, enqueueing another forward‑run to `capabilities-answer`, which again pulls `route-intent` in as a dependency. This repeats until `routing.max_loops` is hit.
87
+
88
+ **Symptoms:**
89
+
90
+ - For a single “what can you do?” message, you may see multiple runs of:
91
+ - `ask`
92
+ - `route-intent`
93
+ - and repeated routing logs, until the loop budget is exceeded.
94
+ - In tests with non‑zero `routing.max_loops`, strict `exactly` expectations on router calls fail (router runs 6+ times instead of once).
95
+
96
+ This is exactly the pattern we saw in the Slack org‑assistant flow when:
97
+
98
+ - `route-intent` used `on_success.transitions`, **and**
99
+ - branches like `capabilities-answer` had `depends_on: [route-intent]`, **and**
100
+ - leaf steps used `goto: ask` to close the loop.
101
+
102
+ ---
103
+
104
+ ## Recommended pattern: router as pure control‑plane
105
+
106
+ ### Core idea
107
+
108
+ Treat router checks as **control‑plane only**:
109
+
110
+ - Router:
111
+ - `depends_on` its true *inputs* (e.g. `ask`, context fetch steps).
112
+ - Uses `on_success.transitions` to select branches.
113
+ - Branches:
114
+ - **Do not** `depends_on` the router.
115
+ - Instead, they **read** the router’s output from `outputs['router']` and gate with `if` / `assume`.
116
+
117
+ This gives a clean, acyclic structure:
118
+
119
+ ```yaml
120
+ ask:
121
+ type: human-input
122
+ group: chat
123
+
124
+ route-intent:
125
+ type: ai
126
+ group: chat
127
+ depends_on: [ask]
128
+ ai:
129
+ disableTools: true
130
+ allowedTools: []
131
+ schema:
132
+ type: object
133
+ properties:
134
+ intent:
135
+ type: string
136
+ enum: [chat, capabilities]
137
+ required: [intent]
138
+ on_success:
139
+ transitions:
140
+ - when: "output.intent === 'capabilities'"
141
+ to: capabilities-answer
142
+ - when: "output.intent === 'chat'"
143
+ to: chat-answer
144
+
145
+ capabilities-answer:
146
+ type: ai
147
+ group: chat
148
+ # no depends_on: [route-intent]
149
+ if: "outputs['route-intent']?.intent === 'capabilities'"
150
+ ai:
151
+ disableTools: true
152
+ allowedTools: []
153
+ prompt: |
154
+ Explain briefly what you can help with.
155
+ on_success:
156
+ goto: ask
157
+
158
+ chat-answer:
159
+ type: ai
160
+ group: chat
161
+ # no depends_on: [route-intent]
162
+ if: "outputs['route-intent']?.intent === 'chat'"
163
+ prompt: |
164
+ You are a concise, friendly assistant.
165
+ Latest user message: {{ outputs['ask'].text }}
166
+ on_success:
167
+ goto: ask
168
+ ```
169
+
170
+ **Engine behavior:**
171
+
172
+ - Initial wave:
173
+ - DAG: `ask` → `route-intent`
174
+ - Router runs once, then emits transitions.
175
+ - Forward‑run for `capabilities-answer`:
176
+ - Only includes the branch and any *real* dependencies the branch declares (e.g. context fetch, project status).
177
+ - Does **not** pull `route-intent` back in, because the branch no longer `depends_on` it.
178
+ - Loops:
179
+ - `capabilities-answer` closing to `ask` via `goto: ask` is explicit and controlled.
180
+ - Router still runs once per loop cycle (when `ask` completes), but is not re‑run just because the branch was targeted by a transition.
181
+
182
+ This pattern avoids the “router as both dependency and transition source” cycle that caused loops in Slack.
183
+
184
+ ---
185
+
186
+ ## When to use `depends_on` vs `transitions` vs `if` / `assume`
187
+
188
+ ### Use `depends_on` for real data prerequisites
189
+
190
+ Examples:
191
+
192
+ ```yaml
193
+ project-status-fetch:
194
+ type: script
195
+ criticality: external
196
+ depends_on: [project-intent]
197
+
198
+ project-status-answer:
199
+ type: ai
200
+ depends_on: [project-status-fetch]
201
+ assume:
202
+ - "!!outputs['project-status-fetch']?.project"
203
+ ```
204
+
205
+ - `project-status-answer` truly needs `project-status-fetch`’s output to behave; this is a data edge.
206
+ - `assume` documents and enforces a precondition on that data.
207
+
208
+ ### Use `transitions` / `goto` for control‑flow
209
+
210
+ Examples:
211
+
212
+ ```yaml
213
+ route-intent:
214
+ type: ai
215
+ on_success:
216
+ transitions:
217
+ - when: "output.intent === 'status'"
218
+ to: project-status-answer
219
+ - when: "output.intent === 'deployment'"
220
+ to: project-deploy-confirm
221
+
222
+ project-deploy-answer:
223
+ type: ai
224
+ on_success:
225
+ transitions:
226
+ - when: "output.done === true"
227
+ to: ask
228
+ - when: "output.done === false"
229
+ to: project-deploy-confirm
230
+ ```
231
+
232
+ - Router edges are expressed only via `transitions`.
233
+ - Inner “deployment helper” loop is controlled by transitions on `done`.
234
+
235
+ ### Use `if` and `assume` for semantics, not wiring
236
+
237
+ Examples:
238
+
239
+ ```yaml
240
+ capabilities-answer:
241
+ # Soft gate: skip unless router chose 'capabilities'
242
+ if: "outputs['route-intent']?.intent === 'capabilities'"
243
+
244
+ project-status-answer:
245
+ # Hard precondition: fetch must have produced a project
246
+ assume:
247
+ - "!!outputs['project-status-fetch']?.project"
248
+ ```
249
+
250
+ Guidelines:
251
+
252
+ - Use `if` to conditionally *skip* a step when a control field doesn’t match.
253
+ - Use `assume` to assert invariants when the step *does* run.
254
+ - Do **not** rely on `assume` to implement routing; that’s what `transitions` and `if` are for.
255
+
256
+ ---
257
+
258
+ ## Migration: from router‑as‑dependency to router‑as‑control‑plane
259
+
260
+ If you have existing workflows where:
261
+
262
+ - router checks use `on_success.transitions`, and
263
+ - branches also `depends_on` the router, and
264
+ - leaf steps loop back via `goto: <entry-step>`,
265
+
266
+ then you’re in the “double‑edge” pattern and may hit the same kind of loops we saw in Slack.
267
+
268
+ ### Migration steps
269
+
270
+ 1. **Identify router checks**
271
+ - Look for AI/script steps that:
272
+ - read from a single human‑input or transport context, and
273
+ - fan‑out into multiple branches via `transitions`.
274
+
275
+ 2. **Remove router from branch `depends_on`**
276
+ - For each branch:
277
+ - Remove `depends_on: [router]`.
278
+ - Keep other dependencies (e.g. context fetch, sub‑routers).
279
+
280
+ 3. **Add `if` / `assume` guards on branches**
281
+ - Replace router‑dependency semantics with guards:
282
+
283
+ ```yaml
284
+ chat-answer:
285
+ # was: depends_on: [route-intent]
286
+ if: "outputs['route-intent']?.intent === 'chat'"
287
+ ```
288
+
289
+ 4. **Keep explicit loops**
290
+ - Leave `goto: ask` and inner loops (`project-deploy-confirm` ↔ `project-deploy-answer`) as they are.
291
+ - The loop behavior remains explicit and is no longer entangled with router dependencies.
292
+
293
+ 5. **Increase `routing.max_loops` and add strict call expectations**
294
+ - In YAML tests, set a reasonable `routing.max_loops` (e.g. 5–10).
295
+ - Assert `exactly` call counts for:
296
+ - router(s),
297
+ - branches,
298
+ - entry steps.
299
+ - This surfaces unintentional loops as test failures instead of silently relying on the loop budget.
300
+
301
+ ---
302
+
303
+ ## Backward compatibility considerations
304
+
305
+ The recommendations above are **config‑level**; you can adopt them without changing engine semantics.
306
+
307
+ However, future engine changes to reinforce this model could be backward‑incompatible for configs that rely on the old pattern. For example:
308
+
309
+ - If we introduce special handling to **avoid re‑running routers** during forward‑runs when the target already depends on them via `transitions`, then:
310
+ - Workflows that implicitly relied on “router re‑runs as dependents” would see different call counts.
311
+ - Some side‑effects triggered on every router run might fire fewer times.
312
+
313
+ To keep migration safe:
314
+
315
+ - Prefer the “router as control‑plane only” pattern for new workflows.
316
+ - Gradually refactor existing ones (Slack, GitHub, HTTP automations) to:
317
+ - remove `depends_on` edges from router → branch,
318
+ - keep `transitions` as the single source of truth for routing,
319
+ - use `if` / `assume` on branches for intent gating.
320
+
321
+ Once most configs follow this pattern, engine‑level improvements (e.g. smarter forward‑run for routers) can be introduced with minimal behavioral change for users.
322
+
323
+ ---
324
+
325
+ ## Summary
326
+
327
+ - **Do**:
328
+ - Use routers (`route-intent`, `project-intent`, …) as control‑plane steps:
329
+ - `depends_on` only real inputs,
330
+ - route with `transitions`.
331
+ - Keep branches free of router `depends_on`; gate them with `if` / `assume`.
332
+ - Use `depends_on` only for real data dependencies.
333
+ - Use `goto`/`transitions` for loops and control‑flow.
334
+
335
+ - **Avoid**:
336
+ - Wiring the same edge both via `depends_on` and `transitions`.
337
+ - Using `assume` as a routing mechanism.
338
+
339
+ Following this pattern produces clearer configs, avoids accidental router loops, and matches how the state‑machine’s forward‑run planner is intended to be used.***
@@ -26,6 +26,20 @@ export type IntegrationEvent = {
26
26
  stack?: string;
27
27
  name?: string;
28
28
  };
29
+ } | {
30
+ type: 'HumanInputRequested';
31
+ checkId: string;
32
+ prompt: string;
33
+ channel?: string;
34
+ threadTs?: string;
35
+ threadKey?: string;
36
+ } | {
37
+ type: 'SnapshotSaved';
38
+ checkId: string;
39
+ channel?: string;
40
+ threadTs?: string;
41
+ threadKey?: string;
42
+ filePath: string;
29
43
  } | {
30
44
  type: 'CommentPosted';
31
45
  threadKey?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["file:///home/runner/work/visor/visor/src/event-bus/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GACxB;IACE,IAAI,EAAE,sBAAsB,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,QAAQ,GAAG,aAAa,GAAG,WAAW,GAAG,WAAW,CAAC;IAC7D,UAAU,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IAC3D,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,GAAG,EAAE,CAAA;KAAE,CAAC;IAClF,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GACD;IACE,IAAI,EAAE,kBAAkB,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GACD;IACE,IAAI,EAAE,sBAAsB,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5D,GACD;IACE,IAAI,EAAE,eAAe,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5D,CAAC;AAEN,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,GAAG;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAElB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,CAAC,CAAC;CACZ;AAED,MAAM,MAAM,QAAQ,GAAG,GAAG,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["file:///home/runner/work/visor/visor/src/event-bus/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GACxB;IACE,IAAI,EAAE,sBAAsB,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,QAAQ,GAAG,aAAa,GAAG,WAAW,GAAG,WAAW,CAAC;IAC7D,UAAU,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IAC3D,MAAM,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,GAAG,EAAE,CAAA;KAAE,CAAC;IAClF,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GACD;IACE,IAAI,EAAE,kBAAkB,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GACD;IACE,IAAI,EAAE,sBAAsB,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5D,GACD;IACE,IAAI,EAAE,qBAAqB,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IAEf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GACD;IACE,IAAI,EAAE,eAAe,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB,GACD;IACE,IAAI,EAAE,eAAe,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5D,CAAC;AAEN,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,GAAG;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAElB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,CAAC,CAAC;CACZ;AAED,MAAM,MAAM,QAAQ,GAAG,GAAG,CAAC"}