@viraatdas/rudder 2.12.9 → 2.12.11

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 (43) hide show
  1. package/README.md +103 -23
  2. package/dist/backends.js +56 -6
  3. package/dist/backends.js.map +1 -1
  4. package/dist/board/board.js +2 -2
  5. package/dist/board/daemon.d.ts +5 -1
  6. package/dist/board/daemon.js +26 -2
  7. package/dist/board/daemon.js.map +1 -1
  8. package/dist/brain.d.ts +1 -0
  9. package/dist/brain.js +49 -7
  10. package/dist/brain.js.map +1 -1
  11. package/dist/codex-binary.d.ts +4 -1
  12. package/dist/codex-binary.js +25 -6
  13. package/dist/codex-binary.js.map +1 -1
  14. package/dist/effort.js +5 -0
  15. package/dist/effort.js.map +1 -1
  16. package/dist/graph.js +6 -1
  17. package/dist/graph.js.map +1 -1
  18. package/dist/handoff.d.ts +83 -0
  19. package/dist/handoff.js +466 -0
  20. package/dist/handoff.js.map +1 -0
  21. package/dist/main.js +15 -2
  22. package/dist/main.js.map +1 -1
  23. package/dist/migration.js +1 -1
  24. package/dist/migration.js.map +1 -1
  25. package/dist/native/darwin-arm64/rudder-native +0 -0
  26. package/dist/native/darwin-x64/rudder-native +0 -0
  27. package/dist/native/linux-x64/rudder-native +0 -0
  28. package/dist/native-agents.js +21 -4
  29. package/dist/native-agents.js.map +1 -1
  30. package/dist/planner.js +9 -2
  31. package/dist/planner.js.map +1 -1
  32. package/dist/run-manager.js +13 -1
  33. package/dist/run-manager.js.map +1 -1
  34. package/dist/scheduler.js +10 -2
  35. package/dist/scheduler.js.map +1 -1
  36. package/dist/state.js +0 -1
  37. package/dist/state.js.map +1 -1
  38. package/dist/surfaces.d.ts +15 -0
  39. package/dist/surfaces.js +8 -0
  40. package/dist/surfaces.js.map +1 -1
  41. package/dist/types.d.ts +22 -4
  42. package/dist/types.js.map +1 -1
  43. package/package.json +1 -1
@@ -0,0 +1,466 @@
1
+ // ---------------------------------------------------------------------------
2
+ // CONVERSATION HANDOFF (push side).
3
+ //
4
+ // `rudder handoff "<next step>"` is meant to be run from INSIDE the chat you are
5
+ // already having — a plain `claude`/`codex` session in another terminal, or the
6
+ // agent in this one. It queues that conversation for the dashboard, which forks it
7
+ // into an agent pane holding the entire discussion: the files you looked at, the
8
+ // decisions you made, the plan you agreed on. Nothing is retyped.
9
+ //
10
+ // The queue is a directory of small JSON files under `.rudder/handoffs/`. The
11
+ // dashboard drains it on its poll (native/src/handoff.rs), so this command works
12
+ // whether Rudder is already open or is started afterwards.
13
+ // ---------------------------------------------------------------------------
14
+ import { execFileSync } from "node:child_process";
15
+ import fs from "node:fs";
16
+ import os from "node:os";
17
+ import path from "node:path";
18
+ import { randomUUID } from "node:crypto";
19
+ import { findRepoRoot } from "./git.js";
20
+ import { encodeClaudeProjectsCwd } from "./migration.js";
21
+ /** Transcripts grow to tens of megabytes; the opening prompt is at the top. */
22
+ const TITLE_SCAN_BYTES = 256 * 1024;
23
+ export function parseHandoffArgs(args) {
24
+ const words = [];
25
+ let sessionId;
26
+ let backend;
27
+ let here = false;
28
+ let list = false;
29
+ for (let i = 0; i < args.length; i += 1) {
30
+ const arg = args[i] ?? "";
31
+ if (arg === "--here" || arg === "--main") {
32
+ here = true;
33
+ continue;
34
+ }
35
+ if (arg === "--list" || arg === "--ls") {
36
+ list = true;
37
+ continue;
38
+ }
39
+ if (arg === "--claude" || arg === "--codex" || arg === "--opencode") {
40
+ backend = arg.slice(2);
41
+ continue;
42
+ }
43
+ if (arg === "--session" || arg === "-s") {
44
+ sessionId = args[i + 1];
45
+ i += 1;
46
+ continue;
47
+ }
48
+ if (arg.startsWith("--session=")) {
49
+ sessionId = arg.slice("--session=".length);
50
+ continue;
51
+ }
52
+ words.push(arg);
53
+ }
54
+ return { sessionId, backend, instruction: words.join(" ").trim(), here, list };
55
+ }
56
+ /**
57
+ * A session id is spliced onto a `claude`/`codex` command line by the dashboard.
58
+ * Accept only id-shaped strings — the same rule the Rust side enforces on read.
59
+ */
60
+ export function isValidSessionId(value) {
61
+ const trimmed = value.trim();
62
+ return trimmed.length >= 8 && trimmed.length <= 128 && /^[A-Za-z0-9_-]+$/.test(trimmed);
63
+ }
64
+ /**
65
+ * Drop the wrapper blocks Claude Code injects around real user text
66
+ * (`<system-reminder>…`, slash-command echoes, the local-command caveat) and
67
+ * collapse what is left onto one line. Mirrors strip_wrapper_blocks in
68
+ * native/src/handoff.rs.
69
+ */
70
+ export function stripWrapperBlocks(text) {
71
+ const kept = [];
72
+ let closing;
73
+ for (const raw of text.split("\n")) {
74
+ const line = raw.trim();
75
+ if (closing) {
76
+ if (line.endsWith(closing)) {
77
+ closing = undefined;
78
+ }
79
+ continue;
80
+ }
81
+ if (!line || line.startsWith("Caveat:")) {
82
+ continue;
83
+ }
84
+ // Wrapper tags may carry attributes: <teammate-message teammate_id="lead">.
85
+ const opening = /^<([A-Za-z0-9_-]+)(\s[^>]*)?>/.exec(line);
86
+ if (opening) {
87
+ const close = `</${opening[1]}>`;
88
+ if (!line.includes(close)) {
89
+ closing = close;
90
+ }
91
+ continue;
92
+ }
93
+ kept.push(line);
94
+ }
95
+ return kept.join(" ").split(/\s+/).filter(Boolean).join(" ");
96
+ }
97
+ /** The conversation's opening user prompt: what a human would call the chat. */
98
+ export function firstUserPrompt(raw) {
99
+ for (const line of raw.split("\n")) {
100
+ const prompt = userPromptFromLine(line);
101
+ if (prompt) {
102
+ return prompt;
103
+ }
104
+ }
105
+ return undefined;
106
+ }
107
+ /**
108
+ * One transcript entry's user prose, or undefined when it is not a real user turn.
109
+ * Skips sidechain (subagent) turns, meta entries, and the wrapper blocks Claude
110
+ * Code injects around user text.
111
+ */
112
+ function userPromptFromLine(line) {
113
+ let value;
114
+ try {
115
+ value = JSON.parse(line);
116
+ }
117
+ catch {
118
+ return undefined;
119
+ }
120
+ if (value.type !== "user" || value.isSidechain === true || value.isMeta === true) {
121
+ return undefined;
122
+ }
123
+ const message = value.message;
124
+ const content = message?.content;
125
+ let text = "";
126
+ if (typeof content === "string") {
127
+ text = content;
128
+ }
129
+ else if (Array.isArray(content)) {
130
+ text = content
131
+ .filter((block) => Boolean(block) && block.type === "text")
132
+ .map((block) => block.text)
133
+ .join(" ");
134
+ }
135
+ return stripWrapperBlocks(text) || undefined;
136
+ }
137
+ /**
138
+ * What the listing needs from a transcript, read from its head.
139
+ *
140
+ * `interactive` is whether a human talked to a REPL: Claude records its session
141
+ * mode at start, and the one-shot `claude -p` calls Rudder makes internally (task
142
+ * titles, completion notes) do not — which is how they stay out of the listing.
143
+ */
144
+ export function scanTranscriptHead(raw) {
145
+ let interactive = false;
146
+ for (const line of raw.split("\n")) {
147
+ if (/"type"\s*:\s*"(mode|permission-mode)"/.test(line)) {
148
+ interactive = true;
149
+ }
150
+ const title = userPromptFromLine(line);
151
+ if (title) {
152
+ return { title, interactive };
153
+ }
154
+ }
155
+ return undefined;
156
+ }
157
+ function readHead(file, maxBytes) {
158
+ let handle;
159
+ try {
160
+ handle = fs.openSync(file, "r");
161
+ const buffer = Buffer.alloc(maxBytes);
162
+ const read = fs.readSync(handle, buffer, 0, maxBytes, 0);
163
+ return buffer.subarray(0, read).toString("utf8");
164
+ }
165
+ catch {
166
+ return "";
167
+ }
168
+ finally {
169
+ if (handle !== undefined) {
170
+ fs.closeSync(handle);
171
+ }
172
+ }
173
+ }
174
+ export function claudeProjectsDir() {
175
+ return path.join(os.homedir(), ".claude", "projects");
176
+ }
177
+ /**
178
+ * Recent Claude conversations recorded for `cwd` and its subdirectories, newest
179
+ * first. Claude names each project folder after the directory the session ran in,
180
+ * so a chat started in `src/` lives in a sibling folder — still this repo's.
181
+ * Rudder's own agent sessions (under `.rudder-worktrees`) are not candidates.
182
+ */
183
+ export function recentClaudeConversations(cwd, limit = 10, projects = claudeProjectsDir(), exclude = new Set()) {
184
+ const encoded = encodeClaudeProjectsCwd(path.resolve(cwd));
185
+ let dirs;
186
+ try {
187
+ dirs = fs.readdirSync(projects);
188
+ }
189
+ catch {
190
+ return [];
191
+ }
192
+ const files = [];
193
+ for (const name of dirs) {
194
+ const inRepo = name === encoded || name.startsWith(`${encoded}-`);
195
+ if (!inRepo || name.includes("-rudder-worktrees-")) {
196
+ continue;
197
+ }
198
+ let sessions;
199
+ try {
200
+ sessions = fs.readdirSync(path.join(projects, name));
201
+ }
202
+ catch {
203
+ continue;
204
+ }
205
+ for (const session of sessions) {
206
+ if (!session.endsWith(".jsonl")) {
207
+ continue;
208
+ }
209
+ const file = path.join(projects, name, session);
210
+ try {
211
+ files.push({ file, modifiedMs: fs.statSync(file).mtimeMs });
212
+ }
213
+ catch {
214
+ // Raced with a delete; skip it.
215
+ }
216
+ }
217
+ }
218
+ files.sort((left, right) => right.modifiedMs - left.modifiedMs);
219
+ const interactive = [];
220
+ const all = [];
221
+ // Open a few times the requested count: the newest transcripts are often
222
+ // Rudder's own one-shot calls, and dropping them must not empty the list.
223
+ for (const { file, modifiedMs } of files.slice(0, limit * 4)) {
224
+ const sessionId = path.basename(file, ".jsonl");
225
+ if (!isValidSessionId(sessionId) || exclude.has(sessionId)) {
226
+ continue;
227
+ }
228
+ const head = scanTranscriptHead(readHead(file, TITLE_SCAN_BYTES));
229
+ if (!head) {
230
+ continue;
231
+ }
232
+ const candidate = { sessionId, title: head.title, modifiedMs, path: file };
233
+ if (head.interactive && interactive.length < limit) {
234
+ interactive.push(candidate);
235
+ }
236
+ if (all.length < limit) {
237
+ all.push(candidate);
238
+ }
239
+ }
240
+ // A noisy listing beats an empty one if the interactive marker ever disappears.
241
+ return interactive.length > 0 ? interactive : all;
242
+ }
243
+ export function claudeTranscriptPath(cwd, sessionId, projects = claudeProjectsDir()) {
244
+ const direct = path.join(projects, encodeClaudeProjectsCwd(path.resolve(cwd)), `${sessionId}.jsonl`);
245
+ if (fs.existsSync(direct)) {
246
+ return direct;
247
+ }
248
+ // A chat started in a subdirectory (or a differently-encoded path) still belongs
249
+ // to this machine; find it by id rather than refusing the handoff.
250
+ let dirs;
251
+ try {
252
+ dirs = fs.readdirSync(projects);
253
+ }
254
+ catch {
255
+ return undefined;
256
+ }
257
+ for (const name of dirs) {
258
+ const candidate = path.join(projects, name, `${sessionId}.jsonl`);
259
+ if (fs.existsSync(candidate)) {
260
+ return candidate;
261
+ }
262
+ }
263
+ return undefined;
264
+ }
265
+ /**
266
+ * The conversation this command is running inside. Claude Code exports its session
267
+ * id to every process it spawns, which is the whole trick: an agent asked to "hand
268
+ * this off to rudder" can run the command with no arguments and mean THIS chat.
269
+ * Falls back to the newest transcript recorded for the directory.
270
+ */
271
+ export function resolveClaudeSessionId(cwd, env = process.env) {
272
+ const fromEnv = (env.CLAUDE_CODE_SESSION_ID ?? env.CLAUDE_SESSION_ID ?? "").trim();
273
+ if (isValidSessionId(fromEnv)) {
274
+ return fromEnv;
275
+ }
276
+ return recentClaudeConversations(cwd, 1)[0]?.sessionId;
277
+ }
278
+ /**
279
+ * Newest opencode session for `cwd`. opencode keeps sessions in a database and
280
+ * ships the query, so Rudder asks it rather than reading a schema it does not own.
281
+ * Running this from inside an opencode chat resolves to THAT chat: it is the one
282
+ * that was updated most recently.
283
+ */
284
+ export function resolveOpencodeSessionId(cwd) {
285
+ return recentOpencodeConversations(cwd, 1)[0]?.sessionId;
286
+ }
287
+ export function recentOpencodeConversations(cwd, limit = 10) {
288
+ let raw;
289
+ try {
290
+ raw = execFileSync("opencode", ["session", "list", "--format", "json", "-n", String(limit * 4)], {
291
+ encoding: "utf8",
292
+ stdio: ["ignore", "pipe", "ignore"],
293
+ timeout: 10_000,
294
+ });
295
+ }
296
+ catch {
297
+ return [];
298
+ }
299
+ return parseOpencodeSessions(raw, cwd, limit);
300
+ }
301
+ export function parseOpencodeSessions(raw, cwd, limit = 10) {
302
+ let sessions;
303
+ try {
304
+ sessions = JSON.parse(raw);
305
+ }
306
+ catch {
307
+ return [];
308
+ }
309
+ if (!Array.isArray(sessions)) {
310
+ return [];
311
+ }
312
+ const root = path.resolve(cwd);
313
+ return sessions
314
+ .filter((session) => Boolean(session) && typeof session === "object")
315
+ .filter((session) => {
316
+ const directory = session.directory;
317
+ return typeof directory === "string" && path.resolve(directory).startsWith(root);
318
+ })
319
+ .filter((session) => typeof session.id === "string" && isValidSessionId(session.id))
320
+ .map((session) => ({
321
+ sessionId: String(session.id),
322
+ title: typeof session.title === "string" ? session.title.trim() : "",
323
+ modifiedMs: typeof session.updated === "number" ? session.updated : 0,
324
+ path: typeof session.directory === "string" ? session.directory : cwd,
325
+ }))
326
+ .filter((candidate) => candidate.title.length > 0)
327
+ .sort((left, right) => right.modifiedMs - left.modifiedMs)
328
+ .slice(0, limit);
329
+ }
330
+ /** Newest Codex session recorded for `cwd`. Codex sessions live in one global tree. */
331
+ export function resolveCodexSessionId(cwd, root = path.join(os.homedir(), ".codex", "sessions")) {
332
+ const target = path.resolve(cwd);
333
+ let best;
334
+ const walk = (dir, depth) => {
335
+ if (depth > 8) {
336
+ return;
337
+ }
338
+ let entries;
339
+ try {
340
+ entries = fs.readdirSync(dir, { withFileTypes: true });
341
+ }
342
+ catch {
343
+ return;
344
+ }
345
+ for (const entry of entries) {
346
+ const full = path.join(dir, entry.name);
347
+ if (entry.isDirectory()) {
348
+ walk(full, depth + 1);
349
+ continue;
350
+ }
351
+ if (!entry.name.endsWith(".jsonl")) {
352
+ continue;
353
+ }
354
+ const head = readHead(full, 64 * 1024);
355
+ if (!head.includes(`"cwd":"${target}"`) && !head.includes(`"cwd": "${target}"`)) {
356
+ continue;
357
+ }
358
+ const id = codexSessionIdFrom(head) ?? path.basename(entry.name, ".jsonl");
359
+ if (!isValidSessionId(id)) {
360
+ continue;
361
+ }
362
+ let modifiedMs = 0;
363
+ try {
364
+ modifiedMs = fs.statSync(full).mtimeMs;
365
+ }
366
+ catch {
367
+ continue;
368
+ }
369
+ if (!best || modifiedMs > best.modifiedMs) {
370
+ best = { id, modifiedMs };
371
+ }
372
+ }
373
+ };
374
+ walk(root, 0);
375
+ return best?.id;
376
+ }
377
+ function codexSessionIdFrom(head) {
378
+ const match = /"id"\s*:\s*"([0-9a-fA-F-]{8,})"/.exec(head);
379
+ return match?.[1];
380
+ }
381
+ /** `.rudder/` lives at the dashboard root, never inside an agent's workspace. */
382
+ export function dashboardRoot(cwd = process.cwd()) {
383
+ const root = findRepoRoot(cwd);
384
+ const marker = `${path.sep}.rudder-worktrees${path.sep}`;
385
+ const index = root.indexOf(marker);
386
+ return index === -1 ? root : root.slice(0, index);
387
+ }
388
+ export function handoffQueueDir(repoRoot) {
389
+ return path.join(repoRoot, ".rudder", "handoffs");
390
+ }
391
+ export function writeHandoffRequest(repoRoot, request) {
392
+ const dir = handoffQueueDir(repoRoot);
393
+ fs.mkdirSync(dir, { recursive: true });
394
+ // Filename order is queue order on the reading side, so lead with the stamp.
395
+ const file = path.join(dir, `${request.createdAt}-${request.requestId.slice(0, 8)}.json`);
396
+ const temp = `${file}.${process.pid}.tmp`;
397
+ fs.writeFileSync(temp, `${JSON.stringify(request, null, 2)}\n`);
398
+ fs.renameSync(temp, file);
399
+ return file;
400
+ }
401
+ export async function runHandoff(args, backendFlag) {
402
+ const parsed = parseHandoffArgs(args);
403
+ const cwd = process.cwd();
404
+ const repoRoot = dashboardRoot(cwd);
405
+ if (parsed.list) {
406
+ const candidates = [...recentClaudeConversations(repoRoot, 10), ...recentOpencodeConversations(repoRoot, 10)]
407
+ .sort((left, right) => right.modifiedMs - left.modifiedMs)
408
+ .slice(0, 10);
409
+ if (candidates.length === 0) {
410
+ console.log("No conversations recorded for this repository yet.");
411
+ return;
412
+ }
413
+ console.log("Recent conversations in this repository:\n");
414
+ for (const candidate of candidates) {
415
+ const when = new Date(candidate.modifiedMs).toLocaleString();
416
+ console.log(` ${candidate.sessionId} ${when}`);
417
+ console.log(` ${candidate.title.slice(0, 100)}`);
418
+ }
419
+ console.log('\nHand one off with: rudder handoff --session <id> "<next step>"');
420
+ return;
421
+ }
422
+ const backend = parsed.backend ??
423
+ (backendFlag === "codex" || backendFlag === "opencode" ? backendFlag : undefined) ??
424
+ (process.env.CODEX_SESSION_ID || process.env.CODEX_HOME_SESSION ? "codex" : "claude");
425
+ let sessionId = parsed.sessionId?.trim();
426
+ if (!sessionId) {
427
+ // With no id, "the conversation this command is running inside" is the newest
428
+ // one this backend recorded for the directory.
429
+ sessionId =
430
+ backend === "codex"
431
+ ? resolveCodexSessionId(cwd)
432
+ : backend === "opencode"
433
+ ? resolveOpencodeSessionId(cwd)
434
+ : resolveClaudeSessionId(cwd);
435
+ }
436
+ if (!sessionId) {
437
+ throw new Error(`No ${backend} conversation found to hand off. Run this from inside the chat, or pass --session <id> (list them with \`rudder handoff --list\`).`);
438
+ }
439
+ if (!isValidSessionId(sessionId)) {
440
+ throw new Error(`Not a session id: ${sessionId}`);
441
+ }
442
+ let title;
443
+ if (backend === "claude") {
444
+ const transcript = claudeTranscriptPath(cwd, sessionId);
445
+ if (!transcript) {
446
+ throw new Error(`No Claude transcript found for session ${sessionId}. List the ones Rudder can see with \`rudder handoff --list\`.`);
447
+ }
448
+ title = firstUserPrompt(readHead(transcript, TITLE_SCAN_BYTES));
449
+ }
450
+ const request = {
451
+ requestId: randomUUID(),
452
+ sessionId,
453
+ backend,
454
+ target: parsed.here ? "here" : "worker",
455
+ instruction: parsed.instruction || undefined,
456
+ title,
457
+ sourceCwd: cwd,
458
+ createdAt: Date.now(),
459
+ };
460
+ writeHandoffRequest(repoRoot, request);
461
+ const where = request.target === "here" ? "the main checkout" : "an isolated worker";
462
+ const label = title ? `“${title.slice(0, 60)}”` : `session ${sessionId.slice(0, 8)}…`;
463
+ console.log(`Queued ${label} for handoff into ${where}.`);
464
+ console.log("The dashboard forks the conversation (the original chat is untouched) within a second; run `rudder` if it is not open.");
465
+ }
466
+ //# sourceMappingURL=handoff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handoff.js","sourceRoot":"","sources":["../src/handoff.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,oCAAoC;AACpC,EAAE;AACF,iFAAiF;AACjF,gFAAgF;AAChF,mFAAmF;AACnF,iFAAiF;AACjF,kEAAkE;AAClE,EAAE;AACF,8EAA8E;AAC9E,iFAAiF;AACjF,2DAA2D;AAC3D,8EAA8E;AAE9E,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AA+BzD,+EAA+E;AAC/E,MAAM,gBAAgB,GAAG,GAAG,GAAG,IAAI,CAAC;AAEpC,MAAM,UAAU,gBAAgB,CAAC,IAAc;IAC7C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,SAA6B,CAAC;IAClC,IAAI,OAAmC,CAAC;IACxC,IAAI,IAAI,GAAG,KAAK,CAAC;IACjB,IAAI,IAAI,GAAG,KAAK,CAAC;IACjB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1B,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;YACzC,IAAI,GAAG,IAAI,CAAC;YACZ,SAAS;QACX,CAAC;QACD,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;YACvC,IAAI,GAAG,IAAI,CAAC;YACZ,SAAS;QACX,CAAC;QACD,IAAI,GAAG,KAAK,UAAU,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,YAAY,EAAE,CAAC;YACpE,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAmB,CAAC;YACzC,SAAS;QACX,CAAC;QACD,IAAI,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACxC,SAAS,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACxB,CAAC,IAAI,CAAC,CAAC;YACP,SAAS;QACX,CAAC;QACD,IAAI,GAAG,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YACjC,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;YAC3C,SAAS;QACX,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACjF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,OAAO,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,OAAO,CAAC,MAAM,IAAI,GAAG,IAAI,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAC1F,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,IAAI,OAA2B,CAAC;IAChC,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QACxB,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC3B,OAAO,GAAG,SAAS,CAAC;YACtB,CAAC;YACD,SAAS;QACX,CAAC;QACD,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACxC,SAAS;QACX,CAAC;QACD,4EAA4E;QAC5E,MAAM,OAAO,GAAG,+BAA+B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3D,IAAI,OAAO,EAAE,CAAC;YACZ,MAAM,KAAK,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;YACjC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC1B,OAAO,GAAG,KAAK,CAAC;YAClB,CAAC;YACD,SAAS;QACX,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/D,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;GAIG;AACH,SAAS,kBAAkB,CAAC,IAAY;IACtC,IAAI,KAA8B,CAAC;IACnC,IAAI,CAAC;QACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAA4B,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,WAAW,KAAK,IAAI,IAAI,KAAK,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;QACjF,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,OAAO,GAAG,KAAK,CAAC,OAA4C,CAAC;IACnE,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,CAAC;IACjC,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,IAAI,GAAG,OAAO,CAAC;IACjB,CAAC;SAAM,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAClC,IAAI,GAAG,OAAO;aACX,MAAM,CAAC,CAAC,KAAK,EAA2C,EAAE,CACzD,OAAO,CAAC,KAAK,CAAC,IAAK,KAA2B,CAAC,IAAI,KAAK,MAAM,CAC/D;aACA,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;aAC1B,IAAI,CAAC,GAAG,CAAC,CAAC;IACf,CAAC;IACD,OAAO,kBAAkB,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC;AAC/C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAW;IAC5C,IAAI,WAAW,GAAG,KAAK,CAAC;IACxB,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,IAAI,uCAAuC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACvD,WAAW,GAAG,IAAI,CAAC;QACrB,CAAC;QACD,MAAM,KAAK,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC;QAChC,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY,EAAE,QAAgB;IAC9C,IAAI,MAA0B,CAAC;IAC/B,IAAI,CAAC;QACH,MAAM,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAChC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACtC,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC;QACzD,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;YAAS,CAAC;QACT,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;AACxD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CACvC,GAAW,EACX,KAAK,GAAG,EAAE,EACV,QAAQ,GAAG,iBAAiB,EAAE,EAC9B,UAA+B,IAAI,GAAG,EAAE;IAExC,MAAM,OAAO,GAAG,uBAAuB,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3D,IAAI,IAAc,CAAC;IACnB,IAAI,CAAC;QACH,IAAI,GAAG,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,KAAK,GAA2C,EAAE,CAAC;IACzD,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC;QAClE,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,CAAC;YACnD,SAAS;QACX,CAAC;QACD,IAAI,QAAkB,CAAC;QACvB,IAAI,CAAC;YACH,QAAQ,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC;QACvD,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAChC,SAAS;YACX,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;YAChD,IAAI,CAAC;gBACH,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;YAC9D,CAAC;YAAC,MAAM,CAAC;gBACP,gCAAgC;YAClC,CAAC;QACH,CAAC;IACH,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC;IAChE,MAAM,WAAW,GAA4B,EAAE,CAAC;IAChD,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,yEAAyE;IACzE,0EAA0E;IAC1E,KAAK,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,EAAE,CAAC;QAC7D,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAChD,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3D,SAAS;QACX,CAAC;QACD,MAAM,IAAI,GAAG,kBAAkB,CAAC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC;QAClE,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,SAAS;QACX,CAAC;QACD,MAAM,SAAS,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QAC3E,IAAI,IAAI,CAAC,WAAW,IAAI,WAAW,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC;YACnD,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC9B,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC;YACvB,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IACD,gFAAgF;IAChF,OAAO,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,GAAW,EAAE,SAAiB,EAAE,QAAQ,GAAG,iBAAiB,EAAE;IACjG,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,uBAAuB,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,SAAS,QAAQ,CAAC,CAAC;IACrG,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,iFAAiF;IACjF,mEAAmE;IACnE,IAAI,IAAc,CAAC;IACnB,IAAI,CAAC;QACH,IAAI,GAAG,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;QACxB,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,EAAE,GAAG,SAAS,QAAQ,CAAC,CAAC;QAClE,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC7B,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,GAAW,EAAE,MAAyB,OAAO,CAAC,GAAG;IACtF,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,sBAAsB,IAAI,GAAG,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACnF,IAAI,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9B,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,OAAO,yBAAyB,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC;AACzD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAAW;IAClD,OAAO,2BAA2B,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,GAAW,EAAE,KAAK,GAAG,EAAE;IACjE,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,YAAY,CAAC,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE;YAC/F,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;YACnC,OAAO,EAAE,MAAM;SAChB,CAAC,CAAC;IACL,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,qBAAqB,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,GAAW,EAAE,GAAW,EAAE,KAAK,GAAG,EAAE;IACxE,IAAI,QAAiB,CAAC;IACtB,IAAI,CAAC;QACH,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC/B,OAAO,QAAQ;SACZ,MAAM,CAAC,CAAC,OAAO,EAAsC,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,OAAO,KAAK,QAAQ,CAAC;SACxG,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE;QAClB,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACpC,OAAO,OAAO,SAAS,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IACnF,CAAC,CAAC;SACD,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,OAAO,CAAC,EAAE,KAAK,QAAQ,IAAI,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;SACnF,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7B,KAAK,EAAE,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE;QACpE,UAAU,EAAE,OAAO,OAAO,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACrE,IAAI,EAAE,OAAO,OAAO,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG;KACtE,CAAC,CAAC;SACF,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;SACjD,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;SACzD,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AACrB,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,qBAAqB,CAAC,GAAW,EAAE,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,UAAU,CAAC;IACrG,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,IAAoD,CAAC;IACzD,MAAM,IAAI,GAAG,CAAC,GAAW,EAAE,KAAa,EAAQ,EAAE;QAChD,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACd,OAAO;QACT,CAAC;QACD,IAAI,OAAoB,CAAC;QACzB,IAAI,CAAC;YACH,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACzD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,IAAI,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;gBACtB,SAAS;YACX,CAAC;YACD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACnC,SAAS;YACX,CAAC;YACD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;YACvC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,MAAM,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,MAAM,GAAG,CAAC,EAAE,CAAC;gBAChF,SAAS;YACX,CAAC;YACD,MAAM,EAAE,GAAG,kBAAkB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YAC3E,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC1B,SAAS;YACX,CAAC;YACD,IAAI,UAAU,GAAG,CAAC,CAAC;YACnB,IAAI,CAAC;gBACH,UAAU,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC;YACzC,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,IAAI,CAAC,IAAI,IAAI,UAAU,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;gBAC1C,IAAI,GAAG,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC;YAC5B,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IACF,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACd,OAAO,IAAI,EAAE,EAAE,CAAC;AAClB,CAAC;AAED,SAAS,kBAAkB,CAAC,IAAY;IACtC,MAAM,KAAK,GAAG,iCAAiC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3D,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,aAAa,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAC/C,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,oBAAoB,IAAI,CAAC,GAAG,EAAE,CAAC;IACzD,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,QAAgB,EAAE,OAAuB;IAC3E,MAAM,GAAG,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC;IACtC,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,6EAA6E;IAC7E,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;IAC1F,MAAM,IAAI,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,MAAM,CAAC;IAC1C,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAChE,EAAE,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC1B,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAc,EAAE,WAAoB;IACnE,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAEpC,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;QAChB,MAAM,UAAU,GAAG,CAAC,GAAG,yBAAyB,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,GAAG,2BAA2B,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;aAC1G,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;aACzD,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAChB,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,OAAO,CAAC,GAAG,CAAC,oDAAoD,CAAC,CAAC;YAClE,OAAO;QACT,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAC;QAC1D,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,cAAc,EAAE,CAAC;YAC7D,OAAO,CAAC,GAAG,CAAC,KAAK,SAAS,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC,CAAC;YACjD,OAAO,CAAC,GAAG,CAAC,OAAO,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QACtD,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,kEAAkE,CAAC,CAAC;QAChF,OAAO;IACT,CAAC;IAED,MAAM,OAAO,GACX,MAAM,CAAC,OAAO;QACd,CAAC,WAAW,KAAK,OAAO,IAAI,WAAW,KAAK,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;QACjF,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAExF,IAAI,SAAS,GAAG,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC;IACzC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,8EAA8E;QAC9E,+CAA+C;QAC/C,SAAS;YACP,OAAO,KAAK,OAAO;gBACjB,CAAC,CAAC,qBAAqB,CAAC,GAAG,CAAC;gBAC5B,CAAC,CAAC,OAAO,KAAK,UAAU;oBACtB,CAAC,CAAC,wBAAwB,CAAC,GAAG,CAAC;oBAC/B,CAAC,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,MAAM,OAAO,oIAAoI,CAClJ,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,qBAAqB,SAAS,EAAE,CAAC,CAAC;IACpD,CAAC;IAED,IAAI,KAAyB,CAAC;IAC9B,IAAI,OAAO,KAAK,QAAQ,EAAE,CAAC;QACzB,MAAM,UAAU,GAAG,oBAAoB,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QACxD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CACb,0CAA0C,SAAS,gEAAgE,CACpH,CAAC;QACJ,CAAC;QACD,KAAK,GAAG,eAAe,CAAC,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,MAAM,OAAO,GAAmB;QAC9B,SAAS,EAAE,UAAU,EAAE;QACvB,SAAS;QACT,OAAO;QACP,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;QACvC,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,SAAS;QAC5C,KAAK;QACL,SAAS,EAAE,GAAG;QACd,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAC;IACF,mBAAmB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAEvC,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,oBAAoB,CAAC;IACrF,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC;IACtF,OAAO,CAAC,GAAG,CAAC,UAAU,KAAK,qBAAqB,KAAK,GAAG,CAAC,CAAC;IAC1D,OAAO,CAAC,GAAG,CACT,wHAAwH,CACzH,CAAC;AACJ,CAAC"}
package/dist/main.js CHANGED
@@ -8,6 +8,7 @@ import { runCloudCommand } from "./cloud.js";
8
8
  import { printContextAudit } from "./context-audit.js";
9
9
  import { currentBranch, findRepoRoot } from "./git.js";
10
10
  import { runGc } from "./gc.js";
11
+ import { runHandoff } from "./handoff.js";
11
12
  import { ensureBoardRunning } from "./daemon.js";
12
13
  import { createNodeWorkspace, currentJjChangeId, currentOpId, ensureColocated, ensureJj, undoLast, undoToOp, } from "./jj.js";
13
14
  import { graphPath, mirrorPlanIntoGraph, updateGraph } from "./graph.js";
@@ -293,6 +294,12 @@ export async function main() {
293
294
  await runContextCommand(parsed);
294
295
  return;
295
296
  }
297
+ case "handoff": {
298
+ // Run from inside a live claude/codex chat: queues THIS conversation for
299
+ // the dashboard, which forks it into an agent pane with all of its context.
300
+ await runHandoff(parsed.args, parsed.flags.backend);
301
+ return;
302
+ }
296
303
  default: {
297
304
  await maybeOnboard();
298
305
  await startRun({
@@ -515,7 +522,7 @@ function readValue(argv, index, flag) {
515
522
  return value;
516
523
  }
517
524
  function normalizeBackend(value) {
518
- if (value === "claude" || value === "codex" || value === "acpx") {
525
+ if (value === "claude" || value === "codex" || value === "acpx" || value === "opencode") {
519
526
  return value;
520
527
  }
521
528
  throw new Error(`Unknown backend: ${value}`);
@@ -938,6 +945,12 @@ Planner:
938
945
  rudder fanout "task" [--n N] Fan out N variant agents on one task, then a judge picks/merges the best (default N=3)
939
946
  rudder context audit Audit agent context files for size, duplication, secrets, and injection text
940
947
 
948
+ Conversation handoff:
949
+ rudder handoff "<next step>" Hand THIS chat to Rudder: it forks the conversation into a worker that already knows everything
950
+ rudder handoff --here "..." Continue it in the main checkout instead of an isolated workspace
951
+ rudder handoff --opencode "..." Hand off an opencode chat (default is the claude one; --codex for codex)
952
+ rudder handoff --list List recent conversations in this repo with their session ids
953
+
941
954
  Memory:
942
955
  rudder remember "<insight>" Append a durable cross-cutting decision to DECISIONS.md (shared, jj-tracked)
943
956
  rudder share "<token/context>" Append gitignored shared context to RUDDER_SHARED.md for all agents
@@ -972,7 +985,7 @@ Options:
972
985
  --worktree Always isolate in a worktree/workspace
973
986
  --queue Queue mode (reserved)
974
987
  -m, --model <model> Backend model
975
- -b, --backend <backend> claude or codex
988
+ -b, --backend <backend> claude, codex, or opencode
976
989
  -C, --cwd <dir> Run from another directory
977
990
  -p, --port <port> Board server port (default 4774)
978
991
  --n <count> Number of fan-out variants for rudder fanout (default 3)