@renseiai/agentfactory 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +125 -0
  3. package/dist/src/config/index.d.ts +3 -0
  4. package/dist/src/config/index.d.ts.map +1 -0
  5. package/dist/src/config/index.js +1 -0
  6. package/dist/src/config/repository-config.d.ts +44 -0
  7. package/dist/src/config/repository-config.d.ts.map +1 -0
  8. package/dist/src/config/repository-config.js +88 -0
  9. package/dist/src/config/repository-config.test.d.ts +2 -0
  10. package/dist/src/config/repository-config.test.d.ts.map +1 -0
  11. package/dist/src/config/repository-config.test.js +249 -0
  12. package/dist/src/deployment/deployment-checker.d.ts +110 -0
  13. package/dist/src/deployment/deployment-checker.d.ts.map +1 -0
  14. package/dist/src/deployment/deployment-checker.js +242 -0
  15. package/dist/src/deployment/index.d.ts +3 -0
  16. package/dist/src/deployment/index.d.ts.map +1 -0
  17. package/dist/src/deployment/index.js +2 -0
  18. package/dist/src/frontend/index.d.ts +2 -0
  19. package/dist/src/frontend/index.d.ts.map +1 -0
  20. package/dist/src/frontend/index.js +1 -0
  21. package/dist/src/frontend/types.d.ts +106 -0
  22. package/dist/src/frontend/types.d.ts.map +1 -0
  23. package/dist/src/frontend/types.js +11 -0
  24. package/dist/src/governor/decision-engine.d.ts +52 -0
  25. package/dist/src/governor/decision-engine.d.ts.map +1 -0
  26. package/dist/src/governor/decision-engine.js +220 -0
  27. package/dist/src/governor/decision-engine.test.d.ts +2 -0
  28. package/dist/src/governor/decision-engine.test.d.ts.map +1 -0
  29. package/dist/src/governor/decision-engine.test.js +629 -0
  30. package/dist/src/governor/event-bus.d.ts +43 -0
  31. package/dist/src/governor/event-bus.d.ts.map +1 -0
  32. package/dist/src/governor/event-bus.js +8 -0
  33. package/dist/src/governor/event-deduplicator.d.ts +43 -0
  34. package/dist/src/governor/event-deduplicator.d.ts.map +1 -0
  35. package/dist/src/governor/event-deduplicator.js +53 -0
  36. package/dist/src/governor/event-driven-governor.d.ts +131 -0
  37. package/dist/src/governor/event-driven-governor.d.ts.map +1 -0
  38. package/dist/src/governor/event-driven-governor.js +379 -0
  39. package/dist/src/governor/event-driven-governor.test.d.ts +2 -0
  40. package/dist/src/governor/event-driven-governor.test.d.ts.map +1 -0
  41. package/dist/src/governor/event-driven-governor.test.js +673 -0
  42. package/dist/src/governor/event-types.d.ts +78 -0
  43. package/dist/src/governor/event-types.d.ts.map +1 -0
  44. package/dist/src/governor/event-types.js +32 -0
  45. package/dist/src/governor/governor-types.d.ts +82 -0
  46. package/dist/src/governor/governor-types.d.ts.map +1 -0
  47. package/dist/src/governor/governor-types.js +21 -0
  48. package/dist/src/governor/governor.d.ts +100 -0
  49. package/dist/src/governor/governor.d.ts.map +1 -0
  50. package/dist/src/governor/governor.js +262 -0
  51. package/dist/src/governor/governor.test.d.ts +2 -0
  52. package/dist/src/governor/governor.test.d.ts.map +1 -0
  53. package/dist/src/governor/governor.test.js +514 -0
  54. package/dist/src/governor/human-touchpoints.d.ts +131 -0
  55. package/dist/src/governor/human-touchpoints.d.ts.map +1 -0
  56. package/dist/src/governor/human-touchpoints.js +251 -0
  57. package/dist/src/governor/human-touchpoints.test.d.ts +2 -0
  58. package/dist/src/governor/human-touchpoints.test.d.ts.map +1 -0
  59. package/dist/src/governor/human-touchpoints.test.js +366 -0
  60. package/dist/src/governor/in-memory-event-bus.d.ts +29 -0
  61. package/dist/src/governor/in-memory-event-bus.d.ts.map +1 -0
  62. package/dist/src/governor/in-memory-event-bus.js +79 -0
  63. package/dist/src/governor/index.d.ts +14 -0
  64. package/dist/src/governor/index.d.ts.map +1 -0
  65. package/dist/src/governor/index.js +13 -0
  66. package/dist/src/governor/override-parser.d.ts +60 -0
  67. package/dist/src/governor/override-parser.d.ts.map +1 -0
  68. package/dist/src/governor/override-parser.js +98 -0
  69. package/dist/src/governor/override-parser.test.d.ts +2 -0
  70. package/dist/src/governor/override-parser.test.d.ts.map +1 -0
  71. package/dist/src/governor/override-parser.test.js +312 -0
  72. package/dist/src/governor/platform-adapter.d.ts +69 -0
  73. package/dist/src/governor/platform-adapter.d.ts.map +1 -0
  74. package/dist/src/governor/platform-adapter.js +11 -0
  75. package/dist/src/governor/processing-state.d.ts +66 -0
  76. package/dist/src/governor/processing-state.d.ts.map +1 -0
  77. package/dist/src/governor/processing-state.js +43 -0
  78. package/dist/src/governor/processing-state.test.d.ts +2 -0
  79. package/dist/src/governor/processing-state.test.d.ts.map +1 -0
  80. package/dist/src/governor/processing-state.test.js +96 -0
  81. package/dist/src/governor/top-of-funnel.d.ts +118 -0
  82. package/dist/src/governor/top-of-funnel.d.ts.map +1 -0
  83. package/dist/src/governor/top-of-funnel.js +168 -0
  84. package/dist/src/governor/top-of-funnel.test.d.ts +2 -0
  85. package/dist/src/governor/top-of-funnel.test.d.ts.map +1 -0
  86. package/dist/src/governor/top-of-funnel.test.js +331 -0
  87. package/dist/src/index.d.ts +11 -0
  88. package/dist/src/index.d.ts.map +1 -0
  89. package/dist/src/index.js +10 -0
  90. package/dist/src/linear-cli.d.ts +38 -0
  91. package/dist/src/linear-cli.d.ts.map +1 -0
  92. package/dist/src/linear-cli.js +674 -0
  93. package/dist/src/logger.d.ts +117 -0
  94. package/dist/src/logger.d.ts.map +1 -0
  95. package/dist/src/logger.js +430 -0
  96. package/dist/src/manifest/generate.d.ts +20 -0
  97. package/dist/src/manifest/generate.d.ts.map +1 -0
  98. package/dist/src/manifest/generate.js +65 -0
  99. package/dist/src/manifest/index.d.ts +4 -0
  100. package/dist/src/manifest/index.d.ts.map +1 -0
  101. package/dist/src/manifest/index.js +2 -0
  102. package/dist/src/manifest/route-manifest.d.ts +34 -0
  103. package/dist/src/manifest/route-manifest.d.ts.map +1 -0
  104. package/dist/src/manifest/route-manifest.js +148 -0
  105. package/dist/src/orchestrator/activity-emitter.d.ts +119 -0
  106. package/dist/src/orchestrator/activity-emitter.d.ts.map +1 -0
  107. package/dist/src/orchestrator/activity-emitter.js +306 -0
  108. package/dist/src/orchestrator/api-activity-emitter.d.ts +167 -0
  109. package/dist/src/orchestrator/api-activity-emitter.d.ts.map +1 -0
  110. package/dist/src/orchestrator/api-activity-emitter.js +417 -0
  111. package/dist/src/orchestrator/heartbeat-writer.d.ts +57 -0
  112. package/dist/src/orchestrator/heartbeat-writer.d.ts.map +1 -0
  113. package/dist/src/orchestrator/heartbeat-writer.js +137 -0
  114. package/dist/src/orchestrator/index.d.ts +20 -0
  115. package/dist/src/orchestrator/index.d.ts.map +1 -0
  116. package/dist/src/orchestrator/index.js +22 -0
  117. package/dist/src/orchestrator/log-analyzer.d.ts +160 -0
  118. package/dist/src/orchestrator/log-analyzer.d.ts.map +1 -0
  119. package/dist/src/orchestrator/log-analyzer.js +572 -0
  120. package/dist/src/orchestrator/log-config.d.ts +39 -0
  121. package/dist/src/orchestrator/log-config.d.ts.map +1 -0
  122. package/dist/src/orchestrator/log-config.js +45 -0
  123. package/dist/src/orchestrator/orchestrator.d.ts +316 -0
  124. package/dist/src/orchestrator/orchestrator.d.ts.map +1 -0
  125. package/dist/src/orchestrator/orchestrator.js +3290 -0
  126. package/dist/src/orchestrator/parse-work-result.d.ts +16 -0
  127. package/dist/src/orchestrator/parse-work-result.d.ts.map +1 -0
  128. package/dist/src/orchestrator/parse-work-result.js +135 -0
  129. package/dist/src/orchestrator/parse-work-result.test.d.ts +2 -0
  130. package/dist/src/orchestrator/parse-work-result.test.d.ts.map +1 -0
  131. package/dist/src/orchestrator/parse-work-result.test.js +234 -0
  132. package/dist/src/orchestrator/progress-logger.d.ts +72 -0
  133. package/dist/src/orchestrator/progress-logger.d.ts.map +1 -0
  134. package/dist/src/orchestrator/progress-logger.js +135 -0
  135. package/dist/src/orchestrator/session-logger.d.ts +159 -0
  136. package/dist/src/orchestrator/session-logger.d.ts.map +1 -0
  137. package/dist/src/orchestrator/session-logger.js +275 -0
  138. package/dist/src/orchestrator/state-recovery.d.ts +96 -0
  139. package/dist/src/orchestrator/state-recovery.d.ts.map +1 -0
  140. package/dist/src/orchestrator/state-recovery.js +302 -0
  141. package/dist/src/orchestrator/state-types.d.ts +165 -0
  142. package/dist/src/orchestrator/state-types.d.ts.map +1 -0
  143. package/dist/src/orchestrator/state-types.js +7 -0
  144. package/dist/src/orchestrator/stream-parser.d.ts +151 -0
  145. package/dist/src/orchestrator/stream-parser.d.ts.map +1 -0
  146. package/dist/src/orchestrator/stream-parser.js +137 -0
  147. package/dist/src/orchestrator/types.d.ts +232 -0
  148. package/dist/src/orchestrator/types.d.ts.map +1 -0
  149. package/dist/src/orchestrator/types.js +4 -0
  150. package/dist/src/orchestrator/validate-git-remote.test.d.ts +2 -0
  151. package/dist/src/orchestrator/validate-git-remote.test.d.ts.map +1 -0
  152. package/dist/src/orchestrator/validate-git-remote.test.js +61 -0
  153. package/dist/src/providers/a2a-auth.d.ts +81 -0
  154. package/dist/src/providers/a2a-auth.d.ts.map +1 -0
  155. package/dist/src/providers/a2a-auth.js +188 -0
  156. package/dist/src/providers/a2a-auth.test.d.ts +2 -0
  157. package/dist/src/providers/a2a-auth.test.d.ts.map +1 -0
  158. package/dist/src/providers/a2a-auth.test.js +232 -0
  159. package/dist/src/providers/a2a-provider.d.ts +254 -0
  160. package/dist/src/providers/a2a-provider.d.ts.map +1 -0
  161. package/dist/src/providers/a2a-provider.integration.test.d.ts +9 -0
  162. package/dist/src/providers/a2a-provider.integration.test.d.ts.map +1 -0
  163. package/dist/src/providers/a2a-provider.integration.test.js +665 -0
  164. package/dist/src/providers/a2a-provider.js +811 -0
  165. package/dist/src/providers/a2a-provider.test.d.ts +2 -0
  166. package/dist/src/providers/a2a-provider.test.d.ts.map +1 -0
  167. package/dist/src/providers/a2a-provider.test.js +681 -0
  168. package/dist/src/providers/amp-provider.d.ts +20 -0
  169. package/dist/src/providers/amp-provider.d.ts.map +1 -0
  170. package/dist/src/providers/amp-provider.js +24 -0
  171. package/dist/src/providers/claude-provider.d.ts +18 -0
  172. package/dist/src/providers/claude-provider.d.ts.map +1 -0
  173. package/dist/src/providers/claude-provider.js +437 -0
  174. package/dist/src/providers/codex-provider.d.ts +133 -0
  175. package/dist/src/providers/codex-provider.d.ts.map +1 -0
  176. package/dist/src/providers/codex-provider.js +381 -0
  177. package/dist/src/providers/codex-provider.test.d.ts +2 -0
  178. package/dist/src/providers/codex-provider.test.d.ts.map +1 -0
  179. package/dist/src/providers/codex-provider.test.js +387 -0
  180. package/dist/src/providers/index.d.ts +44 -0
  181. package/dist/src/providers/index.d.ts.map +1 -0
  182. package/dist/src/providers/index.js +85 -0
  183. package/dist/src/providers/spring-ai-provider.d.ts +90 -0
  184. package/dist/src/providers/spring-ai-provider.d.ts.map +1 -0
  185. package/dist/src/providers/spring-ai-provider.integration.test.d.ts +13 -0
  186. package/dist/src/providers/spring-ai-provider.integration.test.d.ts.map +1 -0
  187. package/dist/src/providers/spring-ai-provider.integration.test.js +351 -0
  188. package/dist/src/providers/spring-ai-provider.js +317 -0
  189. package/dist/src/providers/spring-ai-provider.test.d.ts +2 -0
  190. package/dist/src/providers/spring-ai-provider.test.d.ts.map +1 -0
  191. package/dist/src/providers/spring-ai-provider.test.js +200 -0
  192. package/dist/src/providers/types.d.ts +165 -0
  193. package/dist/src/providers/types.d.ts.map +1 -0
  194. package/dist/src/providers/types.js +13 -0
  195. package/dist/src/templates/adapters.d.ts +51 -0
  196. package/dist/src/templates/adapters.d.ts.map +1 -0
  197. package/dist/src/templates/adapters.js +104 -0
  198. package/dist/src/templates/adapters.test.d.ts +2 -0
  199. package/dist/src/templates/adapters.test.d.ts.map +1 -0
  200. package/dist/src/templates/adapters.test.js +165 -0
  201. package/dist/src/templates/agent-definition.d.ts +85 -0
  202. package/dist/src/templates/agent-definition.d.ts.map +1 -0
  203. package/dist/src/templates/agent-definition.js +97 -0
  204. package/dist/src/templates/agent-definition.test.d.ts +2 -0
  205. package/dist/src/templates/agent-definition.test.d.ts.map +1 -0
  206. package/dist/src/templates/agent-definition.test.js +209 -0
  207. package/dist/src/templates/index.d.ts +14 -0
  208. package/dist/src/templates/index.d.ts.map +1 -0
  209. package/dist/src/templates/index.js +11 -0
  210. package/dist/src/templates/loader.d.ts +41 -0
  211. package/dist/src/templates/loader.d.ts.map +1 -0
  212. package/dist/src/templates/loader.js +114 -0
  213. package/dist/src/templates/registry.d.ts +80 -0
  214. package/dist/src/templates/registry.d.ts.map +1 -0
  215. package/dist/src/templates/registry.js +177 -0
  216. package/dist/src/templates/registry.test.d.ts +2 -0
  217. package/dist/src/templates/registry.test.d.ts.map +1 -0
  218. package/dist/src/templates/registry.test.js +198 -0
  219. package/dist/src/templates/renderer.d.ts +29 -0
  220. package/dist/src/templates/renderer.d.ts.map +1 -0
  221. package/dist/src/templates/renderer.js +35 -0
  222. package/dist/src/templates/strategy-templates.test.d.ts +2 -0
  223. package/dist/src/templates/strategy-templates.test.d.ts.map +1 -0
  224. package/dist/src/templates/strategy-templates.test.js +619 -0
  225. package/dist/src/templates/types.d.ts +233 -0
  226. package/dist/src/templates/types.d.ts.map +1 -0
  227. package/dist/src/templates/types.js +127 -0
  228. package/dist/src/templates/types.test.d.ts +2 -0
  229. package/dist/src/templates/types.test.d.ts.map +1 -0
  230. package/dist/src/templates/types.test.js +232 -0
  231. package/dist/src/tools/index.d.ts +6 -0
  232. package/dist/src/tools/index.d.ts.map +1 -0
  233. package/dist/src/tools/index.js +3 -0
  234. package/dist/src/tools/linear-runner.d.ts +34 -0
  235. package/dist/src/tools/linear-runner.d.ts.map +1 -0
  236. package/dist/src/tools/linear-runner.js +700 -0
  237. package/dist/src/tools/plugins/linear.d.ts +9 -0
  238. package/dist/src/tools/plugins/linear.d.ts.map +1 -0
  239. package/dist/src/tools/plugins/linear.js +138 -0
  240. package/dist/src/tools/registry.d.ts +9 -0
  241. package/dist/src/tools/registry.d.ts.map +1 -0
  242. package/dist/src/tools/registry.js +18 -0
  243. package/dist/src/tools/types.d.ts +18 -0
  244. package/dist/src/tools/types.d.ts.map +1 -0
  245. package/dist/src/tools/types.js +1 -0
  246. package/package.json +78 -0
@@ -0,0 +1,167 @@
1
+ /**
2
+ * API-based Activity Emitter
3
+ *
4
+ * Emits Claude stream events to Linear via the agent app's API endpoint.
5
+ * This is used when the orchestrator runs remotely (as a worker) and needs
6
+ * to proxy activities through the Vercel app which has OAuth tokens.
7
+ *
8
+ * The agent app API endpoint (/api/sessions/[id]/activity) retrieves the
9
+ * OAuth token from Redis using the session's organizationId and forwards
10
+ * the activity to Linear's Agent API.
11
+ *
12
+ * Mapping:
13
+ * - assistant message → response (persisted, user-directed communication)
14
+ * - tool_use → action (ephemeral)
15
+ * - tool_result → action (ephemeral)
16
+ * - result → response (persisted)
17
+ * - error → error (persisted)
18
+ */
19
+ /** Configuration for the API activity emitter */
20
+ export interface ApiActivityEmitterConfig {
21
+ /** Linear session ID */
22
+ sessionId: string;
23
+ /** Worker ID for authentication */
24
+ workerId: string;
25
+ /** API base URL (e.g., https://agent.rensei.dev) */
26
+ apiBaseUrl: string;
27
+ /** API authentication key */
28
+ apiKey: string;
29
+ /** Minimum interval between activities in ms (default: 500ms) */
30
+ minInterval?: number;
31
+ /** Maximum length for tool outputs before truncation (default: 2000) */
32
+ maxOutputLength?: number;
33
+ /** Whether to include timestamps in activities (default: false) */
34
+ includeTimestamps?: boolean;
35
+ /** Optional callback when an activity is emitted */
36
+ onActivityEmitted?: (type: string, content: string) => void;
37
+ /** Optional callback when an activity is throttled */
38
+ onActivityThrottled?: (type: string, content: string) => void;
39
+ /** Optional callback when API call fails */
40
+ onActivityError?: (type: string, error: Error) => void;
41
+ /** Optional callback when a progress update is posted */
42
+ onProgressPosted?: (milestone: string, message: string) => void;
43
+ /** Optional callback when session ownership is revoked (403 from another worker) */
44
+ onOwnershipRevoked?: () => void;
45
+ }
46
+ /** Progress milestone types */
47
+ export type ProgressMilestone = 'claimed' | 'worktree' | 'started' | 'running' | 'tests' | 'pr' | 'completed' | 'failed' | 'stopped' | 'resumed';
48
+ /**
49
+ * API Activity Emitter
50
+ *
51
+ * Handles rate-limited emission of Claude events to Linear via API proxy.
52
+ */
53
+ export declare class ApiActivityEmitter {
54
+ private readonly sessionId;
55
+ private workerId;
56
+ private readonly apiBaseUrl;
57
+ private readonly apiKey;
58
+ private readonly minInterval;
59
+ private readonly maxOutputLength;
60
+ private readonly includeTimestamps;
61
+ private readonly onActivityEmitted?;
62
+ private readonly onActivityThrottled?;
63
+ private readonly onActivityError?;
64
+ private readonly onProgressPosted?;
65
+ private readonly onOwnershipRevoked?;
66
+ /** Circuit breaker: set when 403 ownership error is received */
67
+ private ownershipRevoked;
68
+ private lastEmitTime;
69
+ private queue;
70
+ private flushTimer;
71
+ private isProcessing;
72
+ private readonly reportedToolErrors;
73
+ constructor(config: ApiActivityEmitterConfig);
74
+ /**
75
+ * Update the worker ID used for API requests.
76
+ * Called after worker re-registration to ensure activities are attributed
77
+ * to the new worker ID and pass ownership checks.
78
+ */
79
+ updateWorkerId(newWorkerId: string): void;
80
+ /**
81
+ * Get the current worker ID
82
+ */
83
+ getWorkerId(): string;
84
+ /**
85
+ * Check if ownership has been revoked (circuit breaker tripped)
86
+ */
87
+ isOwnershipRevoked(): boolean;
88
+ /**
89
+ * Emit a thought activity (persistent by default for visibility in Linear)
90
+ */
91
+ emitThought(content: string, ephemeral?: boolean): Promise<void>;
92
+ /**
93
+ * Emit a tool use activity (ephemeral by default)
94
+ */
95
+ emitToolUse(tool: string, input: Record<string, unknown>, ephemeral?: boolean): Promise<void>;
96
+ /**
97
+ * Emit a response activity (persisted)
98
+ */
99
+ emitResponse(content: string): Promise<void>;
100
+ /**
101
+ * Emit an error activity (persisted)
102
+ */
103
+ emitError(error: Error | string): Promise<void>;
104
+ /**
105
+ * Post a progress update comment to the Linear issue thread.
106
+ * Unlike activities which are ephemeral, progress updates are
107
+ * persisted as comments and visible in the issue thread.
108
+ *
109
+ * @param milestone - The type of progress milestone (e.g., 'started', 'completed')
110
+ * @param message - The progress message to post
111
+ */
112
+ postProgress(milestone: ProgressMilestone, message: string): Promise<boolean>;
113
+ /**
114
+ * Report a tool error as a Linear issue for tracking and improvement.
115
+ * Creates a bug in the Agent project backlog via API.
116
+ *
117
+ * @param toolName - Name of the tool that errored
118
+ * @param errorMessage - The error message
119
+ * @param context - Additional context about the error
120
+ * @returns The created issue, or null if creation failed or was deduplicated
121
+ */
122
+ reportToolError(toolName: string, errorMessage: string, context?: {
123
+ issueIdentifier?: string;
124
+ additionalContext?: Record<string, unknown>;
125
+ }): Promise<{
126
+ id: string;
127
+ identifier: string;
128
+ url: string;
129
+ } | null>;
130
+ /**
131
+ * Queue an activity for emission with rate limiting
132
+ */
133
+ private queueActivity;
134
+ /**
135
+ * Process queued activities
136
+ */
137
+ private processQueue;
138
+ /**
139
+ * Merge consecutive similar activities in the queue
140
+ */
141
+ private mergeQueuedActivities;
142
+ /**
143
+ * Emit a single activity to Linear via API
144
+ */
145
+ private emitActivity;
146
+ /**
147
+ * Summarize tool input for display
148
+ */
149
+ private summarizeToolInput;
150
+ /**
151
+ * Truncate long output strings
152
+ */
153
+ private truncateOutput;
154
+ /**
155
+ * Flush all pending activities immediately
156
+ */
157
+ flush(): Promise<void>;
158
+ /**
159
+ * Helper delay function
160
+ */
161
+ private delay;
162
+ }
163
+ /**
164
+ * Create an API activity emitter instance
165
+ */
166
+ export declare function createApiActivityEmitter(config: ApiActivityEmitterConfig): ApiActivityEmitter;
167
+ //# sourceMappingURL=api-activity-emitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-activity-emitter.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/api-activity-emitter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,iDAAiD;AACjD,MAAM,WAAW,wBAAwB;IACvC,wBAAwB;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAA;IAChB,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAA;IAClB,6BAA6B;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,wEAAwE;IACxE,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,mEAAmE;IACnE,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,oDAAoD;IACpD,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3D,sDAAsD;IACtD,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7D,4CAA4C;IAC5C,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;IACtD,yDAAyD;IACzD,gBAAgB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/D,oFAAoF;IACpF,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAA;CAChC;AAED,+BAA+B;AAC/B,MAAM,MAAM,iBAAiB,GACzB,SAAS,GACT,UAAU,GACV,SAAS,GACT,SAAS,GACT,OAAO,GACP,IAAI,GACJ,WAAW,GACX,QAAQ,GACR,SAAS,GACT,SAAS,CAAA;AAcb;;;;GAIG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAQ;IACxB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAQ;IACpC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAQ;IACxC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAyC;IAC5E,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAyC;IAC9E,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAsC;IACvE,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAA8C;IAChF,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAY;IAEhD,gEAAgE;IAChE,OAAO,CAAC,gBAAgB,CAAQ;IAEhC,OAAO,CAAC,YAAY,CAAI;IACxB,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,UAAU,CAA6C;IAC/D,OAAO,CAAC,YAAY,CAAQ;IAE5B,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAyB;gBAEhD,MAAM,EAAE,wBAAwB;IAe5C;;;;OAIG;IACH,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAIzC;;OAEG;IACH,WAAW,IAAI,MAAM;IAIrB;;OAEG;IACH,kBAAkB,IAAI,OAAO;IAI7B;;OAEG;IACG,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,UAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAQpE;;OAEG;IACG,WAAW,CACf,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,SAAS,UAAO,GACf,OAAO,CAAC,IAAI,CAAC;IAWhB;;OAEG;IACG,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQlD;;OAEG;IACG,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASrD;;;;;;;OAOG;IACG,YAAY,CAChB,SAAS,EAAE,iBAAiB,EAC5B,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,OAAO,CAAC;IA0CnB;;;;;;;;OAQG;IACG,eAAe,CACnB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE;QACR,eAAe,CAAC,EAAE,MAAM,CAAA;QACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAC5C,GACA,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAkDlE;;OAEG;YACW,aAAa;IAiB3B;;OAEG;YACW,YAAY;IA+B1B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAiD7B;;OAEG;YACW,YAAY;IAwD1B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAoC1B;;OAEG;IACH,OAAO,CAAC,cAAc;IAQtB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ5B;;OAEG;IACH,OAAO,CAAC,KAAK;CAGd;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,wBAAwB,GAC/B,kBAAkB,CAEpB"}
@@ -0,0 +1,417 @@
1
+ /**
2
+ * API-based Activity Emitter
3
+ *
4
+ * Emits Claude stream events to Linear via the agent app's API endpoint.
5
+ * This is used when the orchestrator runs remotely (as a worker) and needs
6
+ * to proxy activities through the Vercel app which has OAuth tokens.
7
+ *
8
+ * The agent app API endpoint (/api/sessions/[id]/activity) retrieves the
9
+ * OAuth token from Redis using the session's organizationId and forwards
10
+ * the activity to Linear's Agent API.
11
+ *
12
+ * Mapping:
13
+ * - assistant message → response (persisted, user-directed communication)
14
+ * - tool_use → action (ephemeral)
15
+ * - tool_result → action (ephemeral)
16
+ * - result → response (persisted)
17
+ * - error → error (persisted)
18
+ */
19
+ const DEFAULT_MIN_INTERVAL = 500;
20
+ const DEFAULT_MAX_OUTPUT_LENGTH = 2000;
21
+ /**
22
+ * API Activity Emitter
23
+ *
24
+ * Handles rate-limited emission of Claude events to Linear via API proxy.
25
+ */
26
+ export class ApiActivityEmitter {
27
+ sessionId;
28
+ workerId; // Mutable to allow update after worker re-registration
29
+ apiBaseUrl;
30
+ apiKey;
31
+ minInterval;
32
+ maxOutputLength;
33
+ includeTimestamps;
34
+ onActivityEmitted;
35
+ onActivityThrottled;
36
+ onActivityError;
37
+ onProgressPosted;
38
+ onOwnershipRevoked;
39
+ /** Circuit breaker: set when 403 ownership error is received */
40
+ ownershipRevoked = false;
41
+ lastEmitTime = 0;
42
+ queue = [];
43
+ flushTimer = null;
44
+ isProcessing = false;
45
+ // Track reported tool error signatures for deduplication
46
+ reportedToolErrors = new Set();
47
+ constructor(config) {
48
+ this.sessionId = config.sessionId;
49
+ this.workerId = config.workerId;
50
+ this.apiBaseUrl = config.apiBaseUrl.replace(/\/$/, ''); // Remove trailing slash
51
+ this.apiKey = config.apiKey;
52
+ this.minInterval = config.minInterval ?? DEFAULT_MIN_INTERVAL;
53
+ this.maxOutputLength = config.maxOutputLength ?? DEFAULT_MAX_OUTPUT_LENGTH;
54
+ this.includeTimestamps = config.includeTimestamps ?? false;
55
+ this.onActivityEmitted = config.onActivityEmitted;
56
+ this.onActivityThrottled = config.onActivityThrottled;
57
+ this.onActivityError = config.onActivityError;
58
+ this.onProgressPosted = config.onProgressPosted;
59
+ this.onOwnershipRevoked = config.onOwnershipRevoked;
60
+ }
61
+ /**
62
+ * Update the worker ID used for API requests.
63
+ * Called after worker re-registration to ensure activities are attributed
64
+ * to the new worker ID and pass ownership checks.
65
+ */
66
+ updateWorkerId(newWorkerId) {
67
+ this.workerId = newWorkerId;
68
+ }
69
+ /**
70
+ * Get the current worker ID
71
+ */
72
+ getWorkerId() {
73
+ return this.workerId;
74
+ }
75
+ /**
76
+ * Check if ownership has been revoked (circuit breaker tripped)
77
+ */
78
+ isOwnershipRevoked() {
79
+ return this.ownershipRevoked;
80
+ }
81
+ /**
82
+ * Emit a thought activity (persistent by default for visibility in Linear)
83
+ */
84
+ async emitThought(content, ephemeral = false) {
85
+ await this.queueActivity({
86
+ type: 'thought',
87
+ content,
88
+ ephemeral,
89
+ });
90
+ }
91
+ /**
92
+ * Emit a tool use activity (ephemeral by default)
93
+ */
94
+ async emitToolUse(tool, input, ephemeral = true) {
95
+ const inputSummary = this.summarizeToolInput(tool, input);
96
+ await this.queueActivity({
97
+ type: 'action',
98
+ content: `${tool}: ${inputSummary}`,
99
+ ephemeral,
100
+ toolName: tool,
101
+ toolInput: input,
102
+ });
103
+ }
104
+ /**
105
+ * Emit a response activity (persisted)
106
+ */
107
+ async emitResponse(content) {
108
+ await this.queueActivity({
109
+ type: 'response',
110
+ content,
111
+ ephemeral: false,
112
+ });
113
+ }
114
+ /**
115
+ * Emit an error activity (persisted)
116
+ */
117
+ async emitError(error) {
118
+ const message = error instanceof Error ? error.message : error;
119
+ await this.queueActivity({
120
+ type: 'error',
121
+ content: message,
122
+ ephemeral: false,
123
+ });
124
+ }
125
+ /**
126
+ * Post a progress update comment to the Linear issue thread.
127
+ * Unlike activities which are ephemeral, progress updates are
128
+ * persisted as comments and visible in the issue thread.
129
+ *
130
+ * @param milestone - The type of progress milestone (e.g., 'started', 'completed')
131
+ * @param message - The progress message to post
132
+ */
133
+ async postProgress(milestone, message) {
134
+ if (this.ownershipRevoked)
135
+ return false;
136
+ try {
137
+ const response = await fetch(`${this.apiBaseUrl}/api/sessions/${this.sessionId}/progress`, {
138
+ method: 'POST',
139
+ headers: {
140
+ 'Content-Type': 'application/json',
141
+ Authorization: `Bearer ${this.apiKey}`,
142
+ },
143
+ body: JSON.stringify({
144
+ workerId: this.workerId,
145
+ milestone,
146
+ message,
147
+ }),
148
+ });
149
+ if (!response.ok) {
150
+ const errorText = await response.text();
151
+ throw new Error(`API error ${response.status}: ${errorText}`);
152
+ }
153
+ const result = (await response.json());
154
+ if (result.posted) {
155
+ this.onProgressPosted?.(milestone, message);
156
+ }
157
+ else {
158
+ console.warn(`Progress not posted: ${result.reason ?? 'unknown'}`);
159
+ }
160
+ return result.posted;
161
+ }
162
+ catch (error) {
163
+ const err = error instanceof Error ? error : new Error(String(error));
164
+ console.error(`Failed to post progress (${milestone}):`, err);
165
+ this.onActivityError?.('progress', err);
166
+ return false;
167
+ }
168
+ }
169
+ /**
170
+ * Report a tool error as a Linear issue for tracking and improvement.
171
+ * Creates a bug in the Agent project backlog via API.
172
+ *
173
+ * @param toolName - Name of the tool that errored
174
+ * @param errorMessage - The error message
175
+ * @param context - Additional context about the error
176
+ * @returns The created issue, or null if creation failed or was deduplicated
177
+ */
178
+ async reportToolError(toolName, errorMessage, context) {
179
+ // Deduplicate using tool name + first 100 chars of error
180
+ const signature = `${toolName}:${errorMessage.substring(0, 100)}`;
181
+ if (this.reportedToolErrors.has(signature)) {
182
+ return null;
183
+ }
184
+ this.reportedToolErrors.add(signature);
185
+ try {
186
+ const response = await fetch(`${this.apiBaseUrl}/api/sessions/${this.sessionId}/tool-error`, {
187
+ method: 'POST',
188
+ headers: {
189
+ 'Content-Type': 'application/json',
190
+ Authorization: `Bearer ${this.apiKey}`,
191
+ },
192
+ body: JSON.stringify({
193
+ workerId: this.workerId,
194
+ toolName,
195
+ errorMessage,
196
+ context,
197
+ }),
198
+ });
199
+ if (!response.ok) {
200
+ const errorText = await response.text();
201
+ throw new Error(`API error ${response.status}: ${errorText}`);
202
+ }
203
+ const result = (await response.json());
204
+ if (result.created && result.issue) {
205
+ return result.issue;
206
+ }
207
+ return null;
208
+ }
209
+ catch (error) {
210
+ const err = error instanceof Error ? error : new Error(String(error));
211
+ console.error('[ApiActivityEmitter] Failed to report tool error:', err);
212
+ this.onActivityError?.('tool-error', err);
213
+ return null;
214
+ }
215
+ }
216
+ /**
217
+ * Queue an activity for emission with rate limiting
218
+ */
219
+ async queueActivity(activity) {
220
+ if (this.ownershipRevoked)
221
+ return;
222
+ this.queue.push(activity);
223
+ // Schedule flush if not already scheduled
224
+ if (!this.flushTimer && !this.isProcessing) {
225
+ const timeSinceLastEmit = Date.now() - this.lastEmitTime;
226
+ const delay = Math.max(0, this.minInterval - timeSinceLastEmit);
227
+ this.flushTimer = setTimeout(() => {
228
+ this.flushTimer = null;
229
+ this.processQueue();
230
+ }, delay);
231
+ }
232
+ }
233
+ /**
234
+ * Process queued activities
235
+ */
236
+ async processQueue() {
237
+ if (this.isProcessing || this.queue.length === 0)
238
+ return;
239
+ this.isProcessing = true;
240
+ try {
241
+ // Merge similar consecutive activities to reduce API calls
242
+ const merged = this.mergeQueuedActivities();
243
+ for (const activity of merged) {
244
+ await this.emitActivity(activity);
245
+ this.lastEmitTime = Date.now();
246
+ // Small delay between emissions to avoid rate limits
247
+ if (merged.length > 1) {
248
+ await this.delay(100);
249
+ }
250
+ }
251
+ }
252
+ finally {
253
+ this.isProcessing = false;
254
+ // If more activities were queued during processing, schedule another flush
255
+ if (this.queue.length > 0 && !this.flushTimer) {
256
+ this.flushTimer = setTimeout(() => {
257
+ this.flushTimer = null;
258
+ this.processQueue();
259
+ }, this.minInterval);
260
+ }
261
+ }
262
+ }
263
+ /**
264
+ * Merge consecutive similar activities in the queue
265
+ */
266
+ mergeQueuedActivities() {
267
+ const activities = [...this.queue];
268
+ this.queue = [];
269
+ if (activities.length <= 1)
270
+ return activities;
271
+ const merged = [];
272
+ let current = activities[0];
273
+ for (let i = 1; i < activities.length; i++) {
274
+ const next = activities[i];
275
+ // Merge consecutive thoughts
276
+ if (current.type === 'thought' &&
277
+ next.type === 'thought' &&
278
+ current.ephemeral === next.ephemeral) {
279
+ current = {
280
+ ...current,
281
+ content: `${current.content}\n\n${next.content}`,
282
+ };
283
+ this.onActivityThrottled?.('thought', next.content);
284
+ continue;
285
+ }
286
+ // Merge consecutive tool results for same tool
287
+ if (current.type === 'action' &&
288
+ next.type === 'action' &&
289
+ current.toolName === next.toolName &&
290
+ current.ephemeral === next.ephemeral) {
291
+ current = {
292
+ ...current,
293
+ content: `${current.content}\n${next.content}`,
294
+ };
295
+ this.onActivityThrottled?.('action', next.content);
296
+ continue;
297
+ }
298
+ merged.push(current);
299
+ current = next;
300
+ }
301
+ merged.push(current);
302
+ return merged;
303
+ }
304
+ /**
305
+ * Emit a single activity to Linear via API
306
+ */
307
+ async emitActivity(activity) {
308
+ if (this.ownershipRevoked)
309
+ return;
310
+ try {
311
+ const content = this.includeTimestamps
312
+ ? `[${new Date().toISOString()}] ${activity.content}`
313
+ : activity.content;
314
+ const response = await fetch(`${this.apiBaseUrl}/api/sessions/${this.sessionId}/activity`, {
315
+ method: 'POST',
316
+ headers: {
317
+ 'Content-Type': 'application/json',
318
+ 'Authorization': `Bearer ${this.apiKey}`,
319
+ },
320
+ body: JSON.stringify({
321
+ workerId: this.workerId,
322
+ activity: {
323
+ type: activity.type,
324
+ content,
325
+ toolName: activity.toolName,
326
+ toolInput: activity.toolInput,
327
+ },
328
+ }),
329
+ });
330
+ if (!response.ok) {
331
+ const errorText = await response.text();
332
+ throw new Error(`API error ${response.status}: ${errorText}`);
333
+ }
334
+ const result = (await response.json());
335
+ if (!result.forwarded) {
336
+ console.warn(`Activity not forwarded: ${result.reason ?? 'unknown'}`);
337
+ }
338
+ this.onActivityEmitted?.(activity.type, content);
339
+ }
340
+ catch (error) {
341
+ const err = error instanceof Error ? error : new Error(String(error));
342
+ // Circuit breaker: stop emitting if session ownership was revoked
343
+ if (err.message.includes('403') && err.message.includes('owned by another worker')) {
344
+ this.ownershipRevoked = true;
345
+ console.warn(`[${this.sessionId}] Session ownership revoked — stopping activity emission`);
346
+ this.onOwnershipRevoked?.();
347
+ return;
348
+ }
349
+ console.error(`Failed to emit ${activity.type} activity via API:`, err);
350
+ this.onActivityError?.(activity.type, err);
351
+ }
352
+ }
353
+ /**
354
+ * Summarize tool input for display
355
+ */
356
+ summarizeToolInput(tool, input) {
357
+ // Tool-specific summaries
358
+ switch (tool) {
359
+ case 'Read':
360
+ return String(input.file_path || input.path || 'file');
361
+ case 'Write':
362
+ return String(input.file_path || input.path || 'file');
363
+ case 'Edit':
364
+ return String(input.file_path || input.path || 'file');
365
+ case 'Grep':
366
+ return `"${input.pattern}" in ${input.path || '.'}`;
367
+ case 'Glob':
368
+ return String(input.pattern || '*');
369
+ case 'Bash':
370
+ const cmd = String(input.command || '');
371
+ return cmd.length > 50 ? cmd.substring(0, 47) + '...' : cmd;
372
+ case 'Task':
373
+ return String(input.description || input.prompt || 'task');
374
+ default:
375
+ // Generic: show first string value or truncated JSON
376
+ const firstStringValue = Object.values(input).find((v) => typeof v === 'string');
377
+ if (firstStringValue) {
378
+ return firstStringValue.length > 50
379
+ ? firstStringValue.substring(0, 47) + '...'
380
+ : firstStringValue;
381
+ }
382
+ const json = JSON.stringify(input);
383
+ return json.length > 50 ? json.substring(0, 47) + '...' : json;
384
+ }
385
+ }
386
+ /**
387
+ * Truncate long output strings
388
+ */
389
+ truncateOutput(output) {
390
+ if (output.length <= this.maxOutputLength)
391
+ return output;
392
+ return (output.substring(0, this.maxOutputLength) +
393
+ `\n\n... (truncated ${output.length - this.maxOutputLength} chars)`);
394
+ }
395
+ /**
396
+ * Flush all pending activities immediately
397
+ */
398
+ async flush() {
399
+ if (this.flushTimer) {
400
+ clearTimeout(this.flushTimer);
401
+ this.flushTimer = null;
402
+ }
403
+ await this.processQueue();
404
+ }
405
+ /**
406
+ * Helper delay function
407
+ */
408
+ delay(ms) {
409
+ return new Promise((resolve) => setTimeout(resolve, ms));
410
+ }
411
+ }
412
+ /**
413
+ * Create an API activity emitter instance
414
+ */
415
+ export function createApiActivityEmitter(config) {
416
+ return new ApiActivityEmitter(config);
417
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Heartbeat Writer
3
+ *
4
+ * Periodically writes heartbeat state to the .agent/ directory.
5
+ * Uses atomic writes (temp file + rename) to prevent corruption.
6
+ */
7
+ import type { HeartbeatActivityType, HeartbeatWriterConfig } from './state-types.js';
8
+ /**
9
+ * HeartbeatWriter periodically writes heartbeat state to enable crash detection
10
+ */
11
+ export declare class HeartbeatWriter {
12
+ private readonly config;
13
+ private readonly heartbeatPath;
14
+ private intervalHandle;
15
+ private lastActivityType;
16
+ private lastActivityTimestamp;
17
+ private toolCallsCount;
18
+ private currentOperation;
19
+ private stopped;
20
+ constructor(config: HeartbeatWriterConfig);
21
+ /**
22
+ * Start the heartbeat writer
23
+ * Immediately writes the first heartbeat, then starts the interval
24
+ */
25
+ start(): void;
26
+ /**
27
+ * Stop the heartbeat writer
28
+ * Should be called when the agent exits
29
+ */
30
+ stop(): void;
31
+ /**
32
+ * Update the last activity type
33
+ * Call this when the agent does something
34
+ */
35
+ updateActivity(type: HeartbeatActivityType, operation?: string): void;
36
+ /**
37
+ * Record a tool call
38
+ */
39
+ recordToolCall(toolName: string): void;
40
+ /**
41
+ * Record thinking activity
42
+ */
43
+ recordThinking(): void;
44
+ /**
45
+ * Write the heartbeat file atomically
46
+ */
47
+ private writeHeartbeat;
48
+ }
49
+ /**
50
+ * Create a heartbeat writer for an agent
51
+ */
52
+ export declare function createHeartbeatWriter(config: HeartbeatWriterConfig): HeartbeatWriter;
53
+ /**
54
+ * Parse environment variable for heartbeat interval
55
+ */
56
+ export declare function getHeartbeatIntervalFromEnv(): number;
57
+ //# sourceMappingURL=heartbeat-writer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"heartbeat-writer.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/heartbeat-writer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAEV,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,kBAAkB,CAAA;AAKzB;;GAEG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiC;IACxD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAQ;IACtC,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,gBAAgB,CAAgC;IACxD,OAAO,CAAC,qBAAqB,CAAQ;IACrC,OAAO,CAAC,cAAc,CAAI;IAC1B,OAAO,CAAC,gBAAgB,CAAsB;IAC9C,OAAO,CAAC,OAAO,CAAQ;gBAEX,MAAM,EAAE,qBAAqB;IASzC;;;OAGG;IACH,KAAK,IAAI,IAAI;IA2Bb;;;OAGG;IACH,IAAI,IAAI,IAAI;IAQZ;;;OAGG;IACH,cAAc,CAAC,IAAI,EAAE,qBAAqB,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;IASrE;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAItC;;OAEG;IACH,cAAc,IAAI,IAAI;IAItB;;OAEG;IACH,OAAO,CAAC,cAAc;CAyBvB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,eAAe,CAEpF;AAED;;GAEG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,CASpD"}