codekin 0.6.2 → 0.6.4

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 (103) hide show
  1. package/README.md +3 -2
  2. package/dist/assets/index-BRB_Ksyk.js +182 -0
  3. package/dist/assets/index-Q2WSVlHo.css +1 -0
  4. package/dist/index.html +2 -2
  5. package/package.json +3 -3
  6. package/server/dist/auth-routes.js +6 -0
  7. package/server/dist/auth-routes.js.map +1 -1
  8. package/server/dist/claude-process.js +3 -2
  9. package/server/dist/claude-process.js.map +1 -1
  10. package/server/dist/commit-event-handler.d.ts +2 -1
  11. package/server/dist/commit-event-handler.js +4 -3
  12. package/server/dist/commit-event-handler.js.map +1 -1
  13. package/server/dist/commit-event-hooks.d.ts +11 -0
  14. package/server/dist/commit-event-hooks.js +61 -11
  15. package/server/dist/commit-event-hooks.js.map +1 -1
  16. package/server/dist/config.d.ts +7 -0
  17. package/server/dist/config.js +19 -1
  18. package/server/dist/config.js.map +1 -1
  19. package/server/dist/docs-routes.js +4 -7
  20. package/server/dist/docs-routes.js.map +1 -1
  21. package/server/dist/json-parse.d.ts +8 -0
  22. package/server/dist/json-parse.js +9 -0
  23. package/server/dist/json-parse.js.map +1 -0
  24. package/server/dist/opencode-process.d.ts +8 -0
  25. package/server/dist/opencode-process.js +63 -5
  26. package/server/dist/opencode-process.js.map +1 -1
  27. package/server/dist/orchestrator-learning-router.d.ts +8 -0
  28. package/server/dist/orchestrator-learning-router.js +126 -0
  29. package/server/dist/orchestrator-learning-router.js.map +1 -0
  30. package/server/dist/orchestrator-learning.d.ts +0 -5
  31. package/server/dist/orchestrator-learning.js +2 -9
  32. package/server/dist/orchestrator-learning.js.map +1 -1
  33. package/server/dist/orchestrator-memory-router.d.ts +10 -0
  34. package/server/dist/orchestrator-memory-router.js +148 -0
  35. package/server/dist/orchestrator-memory-router.js.map +1 -0
  36. package/server/dist/orchestrator-memory.js +2 -1
  37. package/server/dist/orchestrator-memory.js.map +1 -1
  38. package/server/dist/orchestrator-monitor.d.ts +6 -0
  39. package/server/dist/orchestrator-monitor.js +14 -0
  40. package/server/dist/orchestrator-monitor.js.map +1 -1
  41. package/server/dist/orchestrator-reports.d.ts +0 -4
  42. package/server/dist/orchestrator-reports.js +8 -10
  43. package/server/dist/orchestrator-reports.js.map +1 -1
  44. package/server/dist/orchestrator-routes.d.ts +2 -2
  45. package/server/dist/orchestrator-routes.js +11 -506
  46. package/server/dist/orchestrator-routes.js.map +1 -1
  47. package/server/dist/orchestrator-session-router.d.ts +13 -0
  48. package/server/dist/orchestrator-session-router.js +312 -0
  49. package/server/dist/orchestrator-session-router.js.map +1 -0
  50. package/server/dist/process-coordinator.d.ts +83 -0
  51. package/server/dist/process-coordinator.js +167 -0
  52. package/server/dist/process-coordinator.js.map +1 -0
  53. package/server/dist/prompt-router.js +2 -1
  54. package/server/dist/prompt-router.js.map +1 -1
  55. package/server/dist/session-archive.js +2 -1
  56. package/server/dist/session-archive.js.map +1 -1
  57. package/server/dist/session-lifecycle.js +21 -37
  58. package/server/dist/session-lifecycle.js.map +1 -1
  59. package/server/dist/session-manager.d.ts +4 -0
  60. package/server/dist/session-manager.js +64 -75
  61. package/server/dist/session-manager.js.map +1 -1
  62. package/server/dist/session-persistence.js +3 -1
  63. package/server/dist/session-persistence.js.map +1 -1
  64. package/server/dist/session-routes.d.ts +18 -0
  65. package/server/dist/session-routes.js +56 -11
  66. package/server/dist/session-routes.js.map +1 -1
  67. package/server/dist/stepflow-handler.js +5 -1
  68. package/server/dist/stepflow-handler.js.map +1 -1
  69. package/server/dist/stepflow-prompt.js +16 -12
  70. package/server/dist/stepflow-prompt.js.map +1 -1
  71. package/server/dist/tsconfig.tsbuildinfo +1 -1
  72. package/server/dist/types.d.ts +6 -0
  73. package/server/dist/types.js +1 -0
  74. package/server/dist/types.js.map +1 -1
  75. package/server/dist/upload-routes.d.ts +5 -0
  76. package/server/dist/upload-routes.js +48 -11
  77. package/server/dist/upload-routes.js.map +1 -1
  78. package/server/dist/version-check.js +3 -2
  79. package/server/dist/version-check.js.map +1 -1
  80. package/server/dist/webhook-rate-limiter.js +9 -1
  81. package/server/dist/webhook-rate-limiter.js.map +1 -1
  82. package/server/dist/webhook-workspace.d.ts +12 -0
  83. package/server/dist/webhook-workspace.js +126 -3
  84. package/server/dist/webhook-workspace.js.map +1 -1
  85. package/server/dist/workflow-engine.d.ts +80 -7
  86. package/server/dist/workflow-engine.js +209 -28
  87. package/server/dist/workflow-engine.js.map +1 -1
  88. package/server/dist/workflow-loader.d.ts +6 -1
  89. package/server/dist/workflow-loader.js +97 -27
  90. package/server/dist/workflow-loader.js.map +1 -1
  91. package/server/dist/workflow-routes.d.ts +13 -0
  92. package/server/dist/workflow-routes.js +106 -4
  93. package/server/dist/workflow-routes.js.map +1 -1
  94. package/server/dist/ws-origin-check.d.ts +11 -0
  95. package/server/dist/ws-origin-check.js +20 -0
  96. package/server/dist/ws-origin-check.js.map +1 -0
  97. package/server/dist/ws-rate-limit.d.ts +19 -0
  98. package/server/dist/ws-rate-limit.js +30 -0
  99. package/server/dist/ws-rate-limit.js.map +1 -0
  100. package/server/dist/ws-server.js +41 -22
  101. package/server/dist/ws-server.js.map +1 -1
  102. package/dist/assets/index-C8GlUCii.js +0 -182
  103. package/dist/assets/index-DgeUVGjz.css +0 -1
@@ -0,0 +1,312 @@
1
+ /**
2
+ * Session lifecycle, child session management, reports, and dashboard routes
3
+ * for the orchestrator.
4
+ */
5
+ import { Router } from 'express';
6
+ import { resolve } from 'path';
7
+ import { existsSync, statSync, realpathSync } from 'fs';
8
+ import { ensureOrchestratorRunning, getOrchestratorSessionId, getOrCreateOrchestratorId } from './orchestrator-manager.js';
9
+ import { getAgentDisplayName, REPOS_ROOT, resolveRepoPathInRoot } from './config.js';
10
+ import { scanRepoReports, readReport, getReportsSince } from './orchestrator-reports.js';
11
+ // ---------------------------------------------------------------------------
12
+ // Per-IP rate limiter for child-session spawn (mirrors auth-routes pattern).
13
+ // Each spawn allocates a real subprocess, so we cap aggressively per IP and
14
+ // hard-cap the tracking map to bound memory under DoS conditions.
15
+ // ---------------------------------------------------------------------------
16
+ /** Maximum tracked IPs in the spawn rate-limiter map (matches PR #418 cap). */
17
+ const SPAWN_RATE_MAP_MAX_SIZE = 10_000;
18
+ function createSpawnRateLimiter(maxRequests, windowMs) {
19
+ const ipTimestamps = new Map();
20
+ // Periodic cleanup of stale entries to bound memory growth.
21
+ const cleanup = setInterval(() => {
22
+ const now = Date.now();
23
+ for (const [ip, timestamps] of ipTimestamps) {
24
+ const recent = timestamps.filter(t => now - t < windowMs);
25
+ if (recent.length === 0)
26
+ ipTimestamps.delete(ip);
27
+ else
28
+ ipTimestamps.set(ip, recent);
29
+ }
30
+ }, Math.max(60_000, windowMs));
31
+ if (cleanup.unref)
32
+ cleanup.unref();
33
+ return (req, res, next) => {
34
+ const ip = req.ip || req.socket.remoteAddress || 'unknown';
35
+ const now = Date.now();
36
+ const timestamps = (ipTimestamps.get(ip) ?? []).filter(t => now - t < windowMs);
37
+ if (timestamps.length >= maxRequests) {
38
+ ipTimestamps.set(ip, timestamps);
39
+ return res.status(429).json({ error: 'Too Many Requests', retryAfter: Math.ceil(windowMs / 1000) });
40
+ }
41
+ // Reject new IPs once the map is full (DoS protection, matches PR #418).
42
+ if (timestamps.length === 0 && ipTimestamps.size >= SPAWN_RATE_MAP_MAX_SIZE) {
43
+ return res.status(429).json({ error: 'Too Many Requests', retryAfter: Math.ceil(windowMs / 1000) });
44
+ }
45
+ timestamps.push(now);
46
+ ipTimestamps.set(ip, timestamps);
47
+ next();
48
+ };
49
+ }
50
+ // ---------------------------------------------------------------------------
51
+ // Router factory
52
+ // ---------------------------------------------------------------------------
53
+ export function createSessionRouter(verifyOrchestratorAuth, sessions, memory, children, monitorRef) {
54
+ const router = Router();
55
+ // 20 spawns per 5 minutes per IP — child sessions allocate real subprocesses,
56
+ // so this is intentionally tight. Tune via the constants if needed.
57
+ const spawnRateLimiter = createSpawnRateLimiter(20, 5 * 60_000);
58
+ // -------------------------------------------------------------------------
59
+ // Session lifecycle
60
+ // -------------------------------------------------------------------------
61
+ /** Get orchestrator session status. */
62
+ router.get('/api/orchestrator/status', (req, res) => {
63
+ if (!verifyOrchestratorAuth(req))
64
+ return res.status(401).json({ error: 'Unauthorized' });
65
+ const sessionId = getOrchestratorSessionId(sessions);
66
+ if (!sessionId) {
67
+ return res.json({ sessionId: null, status: 'stopped', agentName: getAgentDisplayName() });
68
+ }
69
+ const session = sessions.get(sessionId);
70
+ const status = session?.claudeProcess?.isAlive() ? 'active' : 'idle';
71
+ res.json({
72
+ sessionId,
73
+ status,
74
+ childSessions: children.activeCount(),
75
+ agentName: getAgentDisplayName(),
76
+ });
77
+ });
78
+ /** Ensure orchestrator is running and return its session ID. */
79
+ router.post('/api/orchestrator/start', (req, res) => {
80
+ if (!verifyOrchestratorAuth(req))
81
+ return res.status(401).json({ error: 'Unauthorized' });
82
+ try {
83
+ const sessionId = ensureOrchestratorRunning(sessions);
84
+ res.json({ sessionId, status: 'active', agentName: getAgentDisplayName() });
85
+ }
86
+ catch (err) {
87
+ console.error('[orchestrator] Failed to start:', err);
88
+ res.status(500).json({ error: `Failed to start Agent ${getAgentDisplayName()}` });
89
+ }
90
+ });
91
+ // -------------------------------------------------------------------------
92
+ // Reports
93
+ // -------------------------------------------------------------------------
94
+ /** Scan reports for a single repo. */
95
+ router.get('/api/orchestrator/reports', (req, res) => {
96
+ if (!verifyOrchestratorAuth(req))
97
+ return res.status(401).json({ error: 'Unauthorized' });
98
+ const repoPath = req.query.repo;
99
+ const since = req.query.since;
100
+ if (repoPath) {
101
+ const resolvedRepoPath = resolveRepoPathInRoot(repoPath);
102
+ if (!resolvedRepoPath) {
103
+ return res.status(400).json({ error: 'Invalid repo path: must be an existing directory under the configured repos root' });
104
+ }
105
+ const reports = scanRepoReports(resolvedRepoPath);
106
+ res.json({ reports });
107
+ }
108
+ else if (since) {
109
+ const repoItems = memory.list({ memoryType: 'repo_context' });
110
+ const repoPaths = repoItems.map(r => r.scope).filter((s) => !!s);
111
+ const reports = getReportsSince(repoPaths, since);
112
+ res.json({ reports });
113
+ }
114
+ else {
115
+ res.status(400).json({ error: 'Provide ?repo=<path> or ?since=<YYYY-MM-DD>' });
116
+ }
117
+ });
118
+ /** Read a specific report's content. */
119
+ router.get('/api/orchestrator/reports/read', (req, res) => {
120
+ if (!verifyOrchestratorAuth(req))
121
+ return res.status(401).json({ error: 'Unauthorized' });
122
+ const filePath = req.query.path;
123
+ if (!filePath)
124
+ return res.status(400).json({ error: 'Provide ?path=<filePath>' });
125
+ const report = readReport(filePath);
126
+ if (!report)
127
+ return res.status(404).json({ error: 'Report not found' });
128
+ res.json({ report });
129
+ });
130
+ // -------------------------------------------------------------------------
131
+ // Child sessions
132
+ // -------------------------------------------------------------------------
133
+ /** List child sessions. */
134
+ router.get('/api/orchestrator/children', (req, res) => {
135
+ if (!verifyOrchestratorAuth(req))
136
+ return res.status(401).json({ error: 'Unauthorized' });
137
+ res.json({ children: children.list() });
138
+ });
139
+ /** Spawn a child session. */
140
+ router.post('/api/orchestrator/children', spawnRateLimiter, async (req, res) => {
141
+ if (!verifyOrchestratorAuth(req))
142
+ return res.status(401).json({ error: 'Unauthorized' });
143
+ const { repo, task, branchName, completionPolicy, deployAfter, useWorktree, model, allowedTools } = req.body;
144
+ if (!repo || !task || !branchName) {
145
+ return res.status(400).json({ error: 'Missing required fields: repo, task, branchName' });
146
+ }
147
+ // Validate branchName to prevent prompt injection
148
+ if (!/^[a-zA-Z0-9][a-zA-Z0-9/_.-]*$/.test(branchName)) {
149
+ return res.status(400).json({ error: 'Invalid branchName: only alphanumeric, /, _, ., and - are allowed' });
150
+ }
151
+ // Validate allowedTools if provided: must be an array of strings
152
+ if (allowedTools !== undefined) {
153
+ if (!Array.isArray(allowedTools) || !allowedTools.every((t) => typeof t === 'string')) {
154
+ return res.status(400).json({ error: 'Invalid allowedTools: must be an array of strings' });
155
+ }
156
+ }
157
+ // Validate repo path: must exist and be a directory
158
+ const absRepo = resolve(repo);
159
+ if (!existsSync(absRepo) || !statSync(absRepo).isDirectory()) {
160
+ return res.status(400).json({ error: 'Invalid repo path: directory does not exist' });
161
+ }
162
+ // Use realpathSync to resolve symlinks before boundary check (prevents symlink bypass)
163
+ const resolvedRepo = realpathSync(absRepo);
164
+ if (!resolvedRepo.startsWith(REPOS_ROOT + '/') && resolvedRepo !== REPOS_ROOT) {
165
+ return res.status(400).json({ error: 'Invalid repo path: must be under configured repos root' });
166
+ }
167
+ try {
168
+ const child = await children.spawn({
169
+ repo,
170
+ task,
171
+ branchName,
172
+ completionPolicy: completionPolicy ?? 'pr',
173
+ deployAfter: deployAfter ?? false,
174
+ useWorktree: useWorktree ?? true,
175
+ model,
176
+ allowedTools,
177
+ });
178
+ res.json({ child });
179
+ }
180
+ catch (err) {
181
+ res.status(503).json({ error: err instanceof Error ? err.message : 'Failed to spawn child session' });
182
+ }
183
+ });
184
+ /** Get a specific child session. */
185
+ router.get('/api/orchestrator/children/:id', (req, res) => {
186
+ if (!verifyOrchestratorAuth(req))
187
+ return res.status(401).json({ error: 'Unauthorized' });
188
+ const child = children.get(req.params.id);
189
+ if (!child)
190
+ return res.status(404).json({ error: 'Child session not found' });
191
+ res.json({ child });
192
+ });
193
+ // -------------------------------------------------------------------------
194
+ // Session prompts & approvals
195
+ // -------------------------------------------------------------------------
196
+ /** Get all sessions with pending prompts (waiting for approval or answer). */
197
+ router.get('/api/orchestrator/sessions/pending-prompts', (req, res) => {
198
+ if (!verifyOrchestratorAuth(req))
199
+ return res.status(401).json({ error: 'Unauthorized' });
200
+ res.json({ sessions: sessions.getPendingPrompts() });
201
+ });
202
+ /** Approve or deny a pending prompt in any session. */
203
+ router.post('/api/orchestrator/sessions/:id/respond', (req, res) => {
204
+ if (!verifyOrchestratorAuth(req))
205
+ return res.status(401).json({ error: 'Unauthorized' });
206
+ const sessionId = req.params.id;
207
+ const { requestId, value } = req.body;
208
+ if (!value) {
209
+ return res.status(400).json({ error: 'Missing required field: value (e.g. "allow", "deny", or answer text)' });
210
+ }
211
+ const session = sessions.get(sessionId);
212
+ if (!session)
213
+ return res.status(404).json({ error: 'Session not found' });
214
+ // Verify there's actually a pending prompt (optionally for the specific requestId)
215
+ const hasPending = requestId
216
+ ? (session.pendingToolApprovals.has(requestId) || session.pendingControlRequests.has(requestId))
217
+ : (session.pendingToolApprovals.size > 0 || session.pendingControlRequests.size > 0);
218
+ if (!hasPending) {
219
+ return res.status(409).json({ error: 'No pending prompt to respond to' });
220
+ }
221
+ // Capture prompt details before responding (response clears them)
222
+ let promptToolName = 'unknown';
223
+ let promptType = 'permission';
224
+ if (requestId) {
225
+ const toolApproval = session.pendingToolApprovals.get(requestId);
226
+ const controlReq = session.pendingControlRequests.get(requestId);
227
+ if (toolApproval) {
228
+ promptToolName = toolApproval.toolName;
229
+ promptType = toolApproval.toolName === 'AskUserQuestion' ? 'question' : 'permission';
230
+ }
231
+ else if (controlReq) {
232
+ promptToolName = controlReq.toolName;
233
+ promptType = controlReq.toolName === 'AskUserQuestion' ? 'question' : 'permission';
234
+ }
235
+ }
236
+ sessions.sendPromptResponse(sessionId, value, requestId);
237
+ // Broadcast a notification to the orchestrator channel
238
+ const orchestratorId = getOrCreateOrchestratorId();
239
+ const orchestratorSession = sessions.get(orchestratorId);
240
+ if (orchestratorSession && orchestratorSession.clients.size > 0) {
241
+ const actionLabel = promptType === 'question'
242
+ ? `answered question from ${promptToolName}`
243
+ : `responded "${value}" to ${promptToolName}`;
244
+ const notifMsg = {
245
+ type: 'system_message',
246
+ subtype: 'info',
247
+ text: `[${getAgentDisplayName()}] ${actionLabel} in session "${session.name}"`,
248
+ };
249
+ for (const ws of orchestratorSession.clients) {
250
+ ws.send(JSON.stringify(notifMsg));
251
+ }
252
+ }
253
+ res.json({ ok: true });
254
+ });
255
+ // -------------------------------------------------------------------------
256
+ // Session cleanup & listing
257
+ // -------------------------------------------------------------------------
258
+ /** List all sessions (unfiltered, includes source field). */
259
+ router.get('/api/orchestrator/sessions', (req, res) => {
260
+ if (!verifyOrchestratorAuth(req))
261
+ return res.status(401).json({ error: 'Unauthorized' });
262
+ res.json({ sessions: sessions.listAll() });
263
+ });
264
+ /** Delete all automated sessions (source: workflow, webhook, stepflow, agent). */
265
+ router.delete('/api/orchestrator/sessions/cleanup', (req, res) => {
266
+ if (!verifyOrchestratorAuth(req))
267
+ return res.status(401).json({ error: 'Unauthorized' });
268
+ const automatedSources = new Set(['workflow', 'webhook', 'stepflow', 'agent']);
269
+ const toDelete = sessions.listAll().filter((s) => automatedSources.has(s.source ?? ''));
270
+ let deleted = 0;
271
+ for (const s of toDelete) {
272
+ if (sessions.delete(s.id))
273
+ deleted++;
274
+ }
275
+ res.json({ deleted });
276
+ });
277
+ /** Delete a specific session by ID. */
278
+ router.delete('/api/orchestrator/sessions/:id', (req, res) => {
279
+ if (!verifyOrchestratorAuth(req))
280
+ return res.status(401).json({ error: 'Unauthorized' });
281
+ const success = sessions.delete(req.params.id);
282
+ if (!success)
283
+ return res.status(404).json({ error: 'Session not found' });
284
+ res.json({ deleted: true });
285
+ });
286
+ // -------------------------------------------------------------------------
287
+ // Dashboard stats
288
+ // -------------------------------------------------------------------------
289
+ /** Get summary stats for the dashboard header. */
290
+ router.get('/api/orchestrator/dashboard', (req, res) => {
291
+ if (!verifyOrchestratorAuth(req))
292
+ return res.status(401).json({ error: 'Unauthorized' });
293
+ const repoItems = memory.list({ memoryType: 'repo_context' });
294
+ const pendingNotifications = monitorRef?.current?.getPending() ?? [];
295
+ const activeChildren = children.activeCount();
296
+ const trustRecords = memory.listTrustRecords();
297
+ const autoApproved = trustRecords.filter(t => t.effectiveLevel !== 'ask').length;
298
+ res.json({
299
+ stats: {
300
+ managedRepos: repoItems.length,
301
+ pendingNotifications: pendingNotifications.length,
302
+ activeChildSessions: activeChildren,
303
+ totalChildSessions: children.list().length,
304
+ trustRecords: trustRecords.length,
305
+ autoApprovedActions: autoApproved,
306
+ memoryItems: memory.list().length,
307
+ },
308
+ });
309
+ });
310
+ return router;
311
+ }
312
+ //# sourceMappingURL=orchestrator-session-router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator-session-router.js","sourceRoot":"","sources":["../orchestrator-session-router.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAEhC,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAC9B,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,IAAI,CAAA;AAEvD,OAAO,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAA;AAC1H,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACpF,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAKxF,8EAA8E;AAC9E,6EAA6E;AAC7E,4EAA4E;AAC5E,kEAAkE;AAClE,8EAA8E;AAE9E,+EAA+E;AAC/E,MAAM,uBAAuB,GAAG,MAAM,CAAA;AAEtC,SAAS,sBAAsB,CAAC,WAAmB,EAAE,QAAgB;IACnE,MAAM,YAAY,GAAG,IAAI,GAAG,EAAoB,CAAA;IAEhD,4DAA4D;IAC5D,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE;QAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,KAAK,MAAM,CAAC,EAAE,EAAE,UAAU,CAAC,IAAI,YAAY,EAAE,CAAC;YAC5C,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAA;YACzD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;gBAAE,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;;gBAC3C,YAAY,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;QACnC,CAAC;IACH,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAA;IAC9B,IAAI,OAAO,CAAC,KAAK;QAAE,OAAO,CAAC,KAAK,EAAE,CAAA;IAElC,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACxB,MAAM,EAAE,GAAG,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,aAAa,IAAI,SAAS,CAAA;QAC1D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,MAAM,UAAU,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAA;QAE/E,IAAI,UAAU,CAAC,MAAM,IAAI,WAAW,EAAE,CAAC;YACrC,YAAY,CAAC,GAAG,CAAC,EAAE,EAAE,UAAU,CAAC,CAAA;YAChC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,CAAA;QACrG,CAAC;QAED,yEAAyE;QACzE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,YAAY,CAAC,IAAI,IAAI,uBAAuB,EAAE,CAAC;YAC5E,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,EAAE,CAAC,CAAA;QACrG,CAAC;QAED,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACpB,YAAY,CAAC,GAAG,CAAC,EAAE,EAAE,UAAU,CAAC,CAAA;QAChC,IAAI,EAAE,CAAA;IACR,CAAC,CAAA;AACH,CAAC;AAsBD,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E,MAAM,UAAU,mBAAmB,CACjC,sBAAiD,EACjD,QAAwB,EACxB,MAA0B,EAC1B,QAAkC,EAClC,UAAoD;IAEpD,MAAM,MAAM,GAAG,MAAM,EAAE,CAAA;IACvB,8EAA8E;IAC9E,oEAAoE;IACpE,MAAM,gBAAgB,GAAG,sBAAsB,CAAC,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC,CAAA;IAE/D,4EAA4E;IAC5E,oBAAoB;IACpB,4EAA4E;IAE5E,uCAAuC;IACvC,MAAM,CAAC,GAAG,CAAC,0BAA0B,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QAClD,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAA;QAExF,MAAM,SAAS,GAAG,wBAAwB,CAAC,QAAQ,CAAC,CAAA;QACpD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,mBAAmB,EAAE,EAAE,CAAC,CAAA;QAC3F,CAAC;QAED,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QACvC,MAAM,MAAM,GAAG,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAA;QACpE,GAAG,CAAC,IAAI,CAAC;YACP,SAAS;YACT,MAAM;YACN,aAAa,EAAE,QAAQ,CAAC,WAAW,EAAE;YACrC,SAAS,EAAE,mBAAmB,EAAE;SACjC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,gEAAgE;IAChE,MAAM,CAAC,IAAI,CAAC,yBAAyB,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QAClD,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAA;QAExF,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,yBAAyB,CAAC,QAAQ,CAAC,CAAA;YACrD,GAAG,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,mBAAmB,EAAE,EAAE,CAAC,CAAA;QAC7E,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,GAAG,CAAC,CAAA;YACrD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,yBAAyB,mBAAmB,EAAE,EAAE,EAAE,CAAC,CAAA;QACnF,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,4EAA4E;IAC5E,UAAU;IACV,4EAA4E;IAE5E,sCAAsC;IACtC,MAAM,CAAC,GAAG,CAAC,2BAA2B,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACnD,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAA;QAExF,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,IAA0B,CAAA;QACrD,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,KAA2B,CAAA;QAEnD,IAAI,QAAQ,EAAE,CAAC;YACb,MAAM,gBAAgB,GAAG,qBAAqB,CAAC,QAAQ,CAAC,CAAA;YACxD,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBACtB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kFAAkF,EAAE,CAAC,CAAA;YAC5H,CAAC;YACD,MAAM,OAAO,GAAG,eAAe,CAAC,gBAAgB,CAAC,CAAA;YACjD,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,CAAA;QACvB,CAAC;aAAM,IAAI,KAAK,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,cAAc,EAAE,CAAC,CAAA;YAC7D,MAAM,SAAS,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;YAC7E,MAAM,OAAO,GAAG,eAAe,CAAC,SAAS,EAAE,KAAK,CAAC,CAAA;YACjD,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,CAAA;QACvB,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,6CAA6C,EAAE,CAAC,CAAA;QAChF,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,wCAAwC;IACxC,MAAM,CAAC,GAAG,CAAC,gCAAgC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACxD,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAA;QAExF,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,IAA0B,CAAA;QACrD,IAAI,CAAC,QAAQ;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAC,CAAA;QAEjF,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAA;QACnC,IAAI,CAAC,MAAM;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,CAAA;QAEvE,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,CAAA;IACtB,CAAC,CAAC,CAAA;IAEF,4EAA4E;IAC5E,iBAAiB;IACjB,4EAA4E;IAE5E,2BAA2B;IAC3B,MAAM,CAAC,GAAG,CAAC,4BAA4B,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACpD,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAA;QAExF,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;IACzC,CAAC,CAAC,CAAA;IAEF,6BAA6B;IAC7B,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE,gBAAgB,EAAE,KAAK,EAAE,GAA6D,EAAE,GAAG,EAAE,EAAE;QACvI,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAA;QAExF,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,gBAAgB,EAAE,WAAW,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,GAAG,CAAC,IAAI,CAAA;QAC5G,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAClC,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,iDAAiD,EAAE,CAAC,CAAA;QAC3F,CAAC;QAED,kDAAkD;QAClD,IAAI,CAAC,+BAA+B,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YACtD,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mEAAmE,EAAE,CAAC,CAAA;QAC7G,CAAC;QAED,iEAAiE;QACjE,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YAC/B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,EAAE,CAAC;gBAC/F,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mDAAmD,EAAE,CAAC,CAAA;YAC7F,CAAC;QACH,CAAC;QAED,oDAAoD;QACpD,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;QAC7B,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC;YAC7D,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,6CAA6C,EAAE,CAAC,CAAA;QACvF,CAAC;QACD,uFAAuF;QACvF,MAAM,YAAY,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;QAC1C,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,UAAU,GAAG,GAAG,CAAC,IAAI,YAAY,KAAK,UAAU,EAAE,CAAC;YAC9E,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,wDAAwD,EAAE,CAAC,CAAA;QAClG,CAAC;QAED,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC;gBACjC,IAAI;gBACJ,IAAI;gBACJ,UAAU;gBACV,gBAAgB,EAAE,gBAAgB,IAAI,IAAI;gBAC1C,WAAW,EAAE,WAAW,IAAI,KAAK;gBACjC,WAAW,EAAE,WAAW,IAAI,IAAI;gBAChC,KAAK;gBACL,YAAY;aACb,CAAC,CAAA;YACF,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;QACrB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,+BAA+B,EAAE,CAAC,CAAA;QACvG,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,oCAAoC;IACpC,MAAM,CAAC,GAAG,CAAC,gCAAgC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACxD,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAA;QAExF,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QACzC,IAAI,CAAC,KAAK;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC,CAAA;QAE7E,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;IACrB,CAAC,CAAC,CAAA;IAEF,4EAA4E;IAC5E,8BAA8B;IAC9B,4EAA4E;IAE5E,8EAA8E;IAC9E,MAAM,CAAC,GAAG,CAAC,4CAA4C,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACpE,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAA;QAExF,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAA;IACtD,CAAC,CAAC,CAAA;IAEF,uDAAuD;IACvD,MAAM,CAAC,IAAI,CAAC,wCAAwC,EAAE,CAAC,GAAyD,EAAE,GAAG,EAAE,EAAE;QACvH,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAA;QAExF,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAA;QAC/B,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,IAAI,CAAA;QACrC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,sEAAsE,EAAE,CAAC,CAAA;QAChH,CAAC;QAED,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QACvC,IAAI,CAAC,OAAO;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAA;QAEzE,mFAAmF;QACnF,MAAM,UAAU,GAAG,SAAS;YAC1B,CAAC,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,sBAAsB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAChG,CAAC,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC,IAAI,GAAG,CAAC,IAAI,OAAO,CAAC,sBAAsB,CAAC,IAAI,GAAG,CAAC,CAAC,CAAA;QAEtF,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,iCAAiC,EAAE,CAAC,CAAA;QAC3E,CAAC;QAED,kEAAkE;QAClE,IAAI,cAAc,GAAG,SAAS,CAAA;QAC9B,IAAI,UAAU,GAA8B,YAAY,CAAA;QACxD,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,YAAY,GAAG,OAAO,CAAC,oBAAoB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;YAChE,MAAM,UAAU,GAAG,OAAO,CAAC,sBAAsB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;YAChE,IAAI,YAAY,EAAE,CAAC;gBACjB,cAAc,GAAG,YAAY,CAAC,QAAQ,CAAA;gBACtC,UAAU,GAAG,YAAY,CAAC,QAAQ,KAAK,iBAAiB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,CAAA;YACtF,CAAC;iBAAM,IAAI,UAAU,EAAE,CAAC;gBACtB,cAAc,GAAG,UAAU,CAAC,QAAQ,CAAA;gBACpC,UAAU,GAAG,UAAU,CAAC,QAAQ,KAAK,iBAAiB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,CAAA;YACpF,CAAC;QACH,CAAC;QAED,QAAQ,CAAC,kBAAkB,CAAC,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC,CAAA;QAExD,uDAAuD;QACvD,MAAM,cAAc,GAAG,yBAAyB,EAAE,CAAA;QAClD,MAAM,mBAAmB,GAAG,QAAQ,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;QACxD,IAAI,mBAAmB,IAAI,mBAAmB,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YAChE,MAAM,WAAW,GAAG,UAAU,KAAK,UAAU;gBAC3C,CAAC,CAAC,0BAA0B,cAAc,EAAE;gBAC5C,CAAC,CAAC,cAAc,KAAK,QAAQ,cAAc,EAAE,CAAA;YAC/C,MAAM,QAAQ,GAAG;gBACf,IAAI,EAAE,gBAAyB;gBAC/B,OAAO,EAAE,MAAe;gBACxB,IAAI,EAAE,IAAI,mBAAmB,EAAE,KAAK,WAAW,gBAAgB,OAAO,CAAC,IAAI,GAAG;aAC/E,CAAA;YACD,KAAK,MAAM,EAAE,IAAI,mBAAmB,CAAC,OAAO,EAAE,CAAC;gBAC7C,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAA;YACnC,CAAC;QACH,CAAC;QAED,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAA;IACxB,CAAC,CAAC,CAAA;IAEF,4EAA4E;IAC5E,4BAA4B;IAC5B,4EAA4E;IAE5E,6DAA6D;IAC7D,MAAM,CAAC,GAAG,CAAC,4BAA4B,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACpD,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAA;QAExF,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;IAC5C,CAAC,CAAC,CAAA;IAEF,kFAAkF;IAClF,MAAM,CAAC,MAAM,CAAC,oCAAoC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QAC/D,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAA;QAExF,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC,CAAA;QAC9E,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAA;QAEvF,IAAI,OAAO,GAAG,CAAC,CAAA;QACf,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;gBAAE,OAAO,EAAE,CAAA;QACtC,CAAC;QAED,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC,CAAA;IACvB,CAAC,CAAC,CAAA;IAEF,uCAAuC;IACvC,MAAM,CAAC,MAAM,CAAC,gCAAgC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QAC3D,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAA;QAExF,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QAC9C,IAAI,CAAC,OAAO;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAA;QAEzE,GAAG,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;IAC7B,CAAC,CAAC,CAAA;IAEF,4EAA4E;IAC5E,kBAAkB;IAClB,4EAA4E;IAE5E,kDAAkD;IAClD,MAAM,CAAC,GAAG,CAAC,6BAA6B,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACrD,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAA;QAExF,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,cAAc,EAAE,CAAC,CAAA;QAC7D,MAAM,oBAAoB,GAAG,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAA;QACpE,MAAM,cAAc,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAA;QAC7C,MAAM,YAAY,GAAG,MAAM,CAAC,gBAAgB,EAAE,CAAA;QAC9C,MAAM,YAAY,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,cAAc,KAAK,KAAK,CAAC,CAAC,MAAM,CAAA;QAEhF,GAAG,CAAC,IAAI,CAAC;YACP,KAAK,EAAE;gBACL,YAAY,EAAE,SAAS,CAAC,MAAM;gBAC9B,oBAAoB,EAAE,oBAAoB,CAAC,MAAM;gBACjD,mBAAmB,EAAE,cAAc;gBACnC,kBAAkB,EAAE,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM;gBAC1C,YAAY,EAAE,YAAY,CAAC,MAAM;gBACjC,mBAAmB,EAAE,YAAY;gBACjC,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM;aAClC;SACF,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IAEF,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Unified process lifecycle coordinator for a single session.
3
+ *
4
+ * Replaces the scattered flag/timer pattern (_restartTimer, _isStarting,
5
+ * _processGeneration, _apiRetry.timer) with a single promise-chain mutex
6
+ * and centralized timer management.
7
+ *
8
+ * Every lifecycle transition (start, stop, reconfigure) is serialized through
9
+ * the mutex so at most one operation is in-flight at a time. All pending
10
+ * timers (restart, API retry) are cancelled on any new transition.
11
+ */
12
+ export interface ProcessCoordinatorDeps {
13
+ /** Spawn the Claude process. Returns true on success. */
14
+ startProcess(sessionId: string): boolean;
15
+ /** Stop the process and wait for it to fully exit. */
16
+ stopProcessAndWait(sessionId: string): Promise<void>;
17
+ }
18
+ export declare class ProcessCoordinator {
19
+ private readonly sessionId;
20
+ private readonly deps;
21
+ /** Monotonically increasing counter. Bumped on every start. Scheduled
22
+ * timers capture the generation at schedule time and bail if it changed. */
23
+ private generation;
24
+ /** Promise chain that serializes lifecycle operations. */
25
+ private chain;
26
+ /** Active lifecycle timers, keyed by purpose (e.g. 'restart', 'apiRetry'). */
27
+ private timers;
28
+ /** True when the user (or idle reaper / delete / shutdown) explicitly stopped
29
+ * the process. Prevents scheduled restarts from firing. */
30
+ private _userStopped;
31
+ constructor(sessionId: string, deps: ProcessCoordinatorDeps);
32
+ /**
33
+ * Request a process start. If another operation is in-flight, waits for it.
34
+ * If the process is already running, the underlying startProcess decides
35
+ * whether to replace it (current behavior: kills old, spawns new).
36
+ */
37
+ requestStart(): Promise<boolean>;
38
+ /**
39
+ * Request a full stop. Cancels all timers, marks userStopped.
40
+ */
41
+ requestStop(): Promise<void>;
42
+ /**
43
+ * Stop the current process, apply a config change, then start a new one.
44
+ * Used by setModel, setProvider, setPermissionMode.
45
+ *
46
+ * @param apply — callback that mutates session config (model, provider, etc.)
47
+ * before the new process is spawned.
48
+ */
49
+ requestReconfigure(apply: () => void): Promise<boolean>;
50
+ /**
51
+ * Schedule a delayed restart (crash recovery). The timer is automatically
52
+ * cancelled if any other lifecycle transition happens before it fires.
53
+ *
54
+ * @param onBeforeStart — optional callback invoked after the timer fires but
55
+ * before startProcess. Return false to abort the restart.
56
+ * @param onAfterStart — optional callback invoked after startProcess completes
57
+ * successfully, for post-restart context injection.
58
+ */
59
+ scheduleRestart(delayMs: number, onBeforeStart?: () => boolean, onAfterStart?: () => void): void;
60
+ /**
61
+ * Schedule an API retry. Tied to the current generation — if the process
62
+ * restarts or stops before the timer fires, the retry is silently dropped.
63
+ */
64
+ scheduleApiRetry(delayMs: number, sendRetry: () => void): void;
65
+ /** Cancel a pending API retry timer (e.g. on successful result). */
66
+ cancelApiRetry(): void;
67
+ /**
68
+ * Cancel all timers and mark as user-stopped. Used by delete() and
69
+ * shutdown() which handle process killing themselves.
70
+ */
71
+ teardown(): void;
72
+ /** Current generation counter. */
73
+ get currentGeneration(): number;
74
+ /** Whether the user has explicitly stopped the process. */
75
+ get isUserStopped(): boolean;
76
+ /** Clear the userStopped flag (e.g. when sendInput reactivates an idle-reaped session). */
77
+ clearUserStopped(): void;
78
+ private enqueue;
79
+ private doStart;
80
+ private doStop;
81
+ private setTimer;
82
+ private cancelAllTimers;
83
+ }
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Unified process lifecycle coordinator for a single session.
3
+ *
4
+ * Replaces the scattered flag/timer pattern (_restartTimer, _isStarting,
5
+ * _processGeneration, _apiRetry.timer) with a single promise-chain mutex
6
+ * and centralized timer management.
7
+ *
8
+ * Every lifecycle transition (start, stop, reconfigure) is serialized through
9
+ * the mutex so at most one operation is in-flight at a time. All pending
10
+ * timers (restart, API retry) are cancelled on any new transition.
11
+ */
12
+ export class ProcessCoordinator {
13
+ sessionId;
14
+ deps;
15
+ /** Monotonically increasing counter. Bumped on every start. Scheduled
16
+ * timers capture the generation at schedule time and bail if it changed. */
17
+ generation = 0;
18
+ /** Promise chain that serializes lifecycle operations. */
19
+ chain = Promise.resolve();
20
+ /** Active lifecycle timers, keyed by purpose (e.g. 'restart', 'apiRetry'). */
21
+ timers = new Map();
22
+ /** True when the user (or idle reaper / delete / shutdown) explicitly stopped
23
+ * the process. Prevents scheduled restarts from firing. */
24
+ _userStopped = false;
25
+ constructor(sessionId, deps) {
26
+ this.sessionId = sessionId;
27
+ this.deps = deps;
28
+ }
29
+ // ---------------------------------------------------------------------------
30
+ // Public intent-based API
31
+ // ---------------------------------------------------------------------------
32
+ /**
33
+ * Request a process start. If another operation is in-flight, waits for it.
34
+ * If the process is already running, the underlying startProcess decides
35
+ * whether to replace it (current behavior: kills old, spawns new).
36
+ */
37
+ requestStart() {
38
+ this._userStopped = false;
39
+ return this.enqueue('start', () => this.doStart());
40
+ }
41
+ /**
42
+ * Request a full stop. Cancels all timers, marks userStopped.
43
+ */
44
+ requestStop() {
45
+ this._userStopped = true;
46
+ this.cancelAllTimers();
47
+ return this.enqueue('stop', () => this.doStop());
48
+ }
49
+ /**
50
+ * Stop the current process, apply a config change, then start a new one.
51
+ * Used by setModel, setProvider, setPermissionMode.
52
+ *
53
+ * @param apply — callback that mutates session config (model, provider, etc.)
54
+ * before the new process is spawned.
55
+ */
56
+ requestReconfigure(apply) {
57
+ this.cancelAllTimers();
58
+ return this.enqueue('reconfigure', async () => {
59
+ await this.doStop();
60
+ apply();
61
+ this._userStopped = false;
62
+ return this.doStart();
63
+ });
64
+ }
65
+ /**
66
+ * Schedule a delayed restart (crash recovery). The timer is automatically
67
+ * cancelled if any other lifecycle transition happens before it fires.
68
+ *
69
+ * @param onBeforeStart — optional callback invoked after the timer fires but
70
+ * before startProcess. Return false to abort the restart.
71
+ * @param onAfterStart — optional callback invoked after startProcess completes
72
+ * successfully, for post-restart context injection.
73
+ */
74
+ scheduleRestart(delayMs, onBeforeStart, onAfterStart) {
75
+ if (this._userStopped)
76
+ return;
77
+ const gen = this.generation;
78
+ this.setTimer('restart', delayMs, () => {
79
+ if (this.generation !== gen || this._userStopped)
80
+ return;
81
+ // Enqueue through the mutex so it doesn't race with other operations
82
+ void this.enqueue('scheduledRestart', async () => {
83
+ // Re-check after acquiring the mutex
84
+ if (this.generation !== gen || this._userStopped)
85
+ return false;
86
+ if (onBeforeStart && !onBeforeStart())
87
+ return false;
88
+ const started = await this.doStart();
89
+ if (started && onAfterStart)
90
+ onAfterStart();
91
+ return started;
92
+ });
93
+ });
94
+ }
95
+ /**
96
+ * Schedule an API retry. Tied to the current generation — if the process
97
+ * restarts or stops before the timer fires, the retry is silently dropped.
98
+ */
99
+ scheduleApiRetry(delayMs, sendRetry) {
100
+ const gen = this.generation;
101
+ this.setTimer('apiRetry', delayMs, () => {
102
+ if (this.generation !== gen || this._userStopped)
103
+ return;
104
+ sendRetry();
105
+ });
106
+ }
107
+ /** Cancel a pending API retry timer (e.g. on successful result). */
108
+ cancelApiRetry() {
109
+ const timer = this.timers.get('apiRetry');
110
+ if (timer) {
111
+ clearTimeout(timer);
112
+ this.timers.delete('apiRetry');
113
+ }
114
+ }
115
+ /**
116
+ * Cancel all timers and mark as user-stopped. Used by delete() and
117
+ * shutdown() which handle process killing themselves.
118
+ */
119
+ teardown() {
120
+ this._userStopped = true;
121
+ this.cancelAllTimers();
122
+ }
123
+ /** Current generation counter. */
124
+ get currentGeneration() { return this.generation; }
125
+ /** Whether the user has explicitly stopped the process. */
126
+ get isUserStopped() { return this._userStopped; }
127
+ /** Clear the userStopped flag (e.g. when sendInput reactivates an idle-reaped session). */
128
+ clearUserStopped() { this._userStopped = false; }
129
+ // ---------------------------------------------------------------------------
130
+ // Internal machinery
131
+ // ---------------------------------------------------------------------------
132
+ enqueue(label, op) {
133
+ this.cancelAllTimers();
134
+ let resolve;
135
+ let reject;
136
+ const result = new Promise((res, rej) => { resolve = res; reject = rej; });
137
+ this.chain = this.chain
138
+ .then(() => {
139
+ console.log(`[coordinator] ${this.sessionId} enqueue=${label} gen=${this.generation}`);
140
+ return op().then(resolve, reject);
141
+ })
142
+ .catch((err) => { console.warn(`[coordinator] ${this.sessionId} enqueue=${label} swallowed error:`, err); });
143
+ return result;
144
+ }
145
+ async doStart() {
146
+ this.generation++;
147
+ return this.deps.startProcess(this.sessionId);
148
+ }
149
+ async doStop() {
150
+ await this.deps.stopProcessAndWait(this.sessionId);
151
+ }
152
+ setTimer(key, delayMs, callback) {
153
+ const existing = this.timers.get(key);
154
+ if (existing)
155
+ clearTimeout(existing);
156
+ this.timers.set(key, setTimeout(() => {
157
+ this.timers.delete(key);
158
+ callback();
159
+ }, delayMs));
160
+ }
161
+ cancelAllTimers() {
162
+ for (const timer of this.timers.values())
163
+ clearTimeout(timer);
164
+ this.timers.clear();
165
+ }
166
+ }
167
+ //# sourceMappingURL=process-coordinator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-coordinator.js","sourceRoot":"","sources":["../process-coordinator.ts"],"names":[],"mappings":"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"}
@@ -10,6 +10,7 @@
10
10
  */
11
11
  import { randomUUID } from 'crypto';
12
12
  import { ApprovalManager } from './approval-manager.js';
13
+ import { jsonParse } from './json-parse.js';
13
14
  export class PromptRouter {
14
15
  deps;
15
16
  constructor(deps) {
@@ -473,7 +474,7 @@ export class PromptRouter {
473
474
  if (typeof value === 'string') {
474
475
  // Try parsing as JSON answers map (multi-question flow)
475
476
  try {
476
- const parsed = JSON.parse(value);
477
+ const parsed = jsonParse(value);
477
478
  if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
478
479
  answers = parsed;
479
480
  }