@kolisachint/hoocode-agent 0.4.44 → 0.4.45

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.
@@ -24,6 +24,27 @@ const SPINNER_INTERVAL_MS = 80;
24
24
  const RAIL = "▎";
25
25
  /** Cells in the deterministic progress bar (matches the design's 14-cell track). */
26
26
  const PROGRESS_CELLS = 14;
27
+ const VIEW_ORDER = ["flat", "subagents", "teams"];
28
+ const VIEW_LABEL = { flat: "tasks", subagents: "subagents", teams: "teams" };
29
+ /** Owner glyphs: main agent a filled diamond, spawned subagents a plus-in-circle, team roles a triangle. */
30
+ const AGENT_GLYPH = { main: "◆", subagent: "⊕", role: "▸" };
31
+ const AGENT_GLYPH_COLOR = {
32
+ main: "accent",
33
+ subagent: "accent",
34
+ role: "borderAccent",
35
+ };
36
+ /** Color for an agent's lifecycle `[state]` tag (mirrors the design's .ast-* classes). */
37
+ const AGENT_STATE_COLOR = {
38
+ active: "warning",
39
+ running: "warning",
40
+ done: "success",
41
+ queued: "dim",
42
+ idle: "dim",
43
+ waiting: "mdLink",
44
+ failed: "error",
45
+ };
46
+ /** Two-cell indent under a group header, with a faint vertical guide. */
47
+ const GROUP_INDENT_PLAIN = "│ ";
27
48
  const STATE_PRESENTATION = {
28
49
  working: { icon: "◐", label: "working", color: "warning" },
29
50
  reviewed: { icon: "✓", label: "reviewed", color: "success" },
@@ -93,12 +114,23 @@ function progressBar(done, active, total) {
93
114
  styled: theme.fg("success", fill) + theme.fg("dim", track),
94
115
  };
95
116
  }
117
+ /**
118
+ * View switcher rendered at the right edge of the ledger header: the three
119
+ * lens labels joined by `·`, the active one in bold accent. Purely an
120
+ * indicator in the TUI — the bound key cycles it (see app.tasks.cycleView).
121
+ */
122
+ function formatViewSwitcher(view) {
123
+ const plain = VIEW_ORDER.map((v) => VIEW_LABEL[v]).join(" · ");
124
+ const styled = VIEW_ORDER.map((v) => v === view ? theme.bold(theme.fg("accent", VIEW_LABEL[v])) : theme.fg("dim", VIEW_LABEL[v])).join(theme.fg("dim", " · "));
125
+ return { plain, styled };
126
+ }
96
127
  /**
97
128
  * Ledger header: a state stamp (◐ working / ✓ reviewed / ✗ stopped) + a
98
129
  * deterministic progress bar and done/total count on the left, and the per-turn
99
- * token + elapsed + cost delta (summed across the tasks below) on the right.
130
+ * token + elapsed + cost delta (summed across the tasks below) plus the view
131
+ * switcher on the right.
100
132
  */
101
- function formatHeader(tasks, width, state, totalSecs) {
133
+ function formatHeader(tasks, width, state, totalSecs, view) {
102
134
  const total = tasks.length;
103
135
  const done = tasks.filter((t) => t.status === "done").length;
104
136
  const active = tasks.filter((t) => t.status === "in_progress").length;
@@ -134,14 +166,29 @@ function formatHeader(tasks, width, state, totalSecs) {
134
166
  theme.fg("muted", elapsed) +
135
167
  (showCost ? theme.fg("dim", " · ") + theme.bold(costStr) : "");
136
168
  }
169
+ // Right cluster: turn delta, then the view switcher at the far edge. The
170
+ // switcher is the first thing dropped when the terminal narrows; the turn
171
+ // delta next; the stamp/count survive to the end.
172
+ const switcher = formatViewSwitcher(view);
173
+ const rightVariants = [];
137
174
  if (turnPlain) {
138
- if (visibleWidth(leftFullPlain) + 2 + visibleWidth(turnPlain) <= width) {
139
- const pad = Math.max(2, width - visibleWidth(leftFullPlain) - visibleWidth(turnPlain));
140
- return leftFull + " ".repeat(pad) + turnText;
175
+ rightVariants.push({
176
+ plain: `${turnPlain} ${switcher.plain}`,
177
+ styled: `${turnText} ${switcher.styled}`,
178
+ });
179
+ rightVariants.push({ plain: turnPlain, styled: turnText });
180
+ }
181
+ else {
182
+ rightVariants.push(switcher);
183
+ }
184
+ for (const right of rightVariants) {
185
+ if (visibleWidth(leftFullPlain) + 2 + visibleWidth(right.plain) <= width) {
186
+ const pad = Math.max(2, width - visibleWidth(leftFullPlain) - visibleWidth(right.plain));
187
+ return leftFull + " ".repeat(pad) + right.styled;
141
188
  }
142
- if (visibleWidth(leftMinPlain) + 2 + visibleWidth(turnPlain) <= width) {
143
- const pad = Math.max(2, width - visibleWidth(leftMinPlain) - visibleWidth(turnPlain));
144
- return leftMin + " ".repeat(pad) + turnText;
189
+ if (visibleWidth(leftMinPlain) + 2 + visibleWidth(right.plain) <= width) {
190
+ const pad = Math.max(2, width - visibleWidth(leftMinPlain) - visibleWidth(right.plain));
191
+ return leftMin + " ".repeat(pad) + right.styled;
145
192
  }
146
193
  }
147
194
  if (visibleWidth(leftFullPlain) <= width)
@@ -157,16 +204,20 @@ function formatTokens(count) {
157
204
  return `${Math.round(count / 1000)}k`;
158
205
  return `${(count / 1000000).toFixed(1)}M`;
159
206
  }
160
- function formatTaskLine(task, width, frame, idColWidth) {
207
+ function formatTaskLine(task, width, frame, idColWidth, options = {}) {
161
208
  const isProgress = task.status === "in_progress";
162
209
  const iconGlyph = isProgress
163
210
  ? (SPINNER_FRAMES[frame] ?? TASK_STATUS_ICON.in_progress)
164
211
  : TASK_STATUS_ICON[task.status];
165
212
  const icon = theme.fg(taskStatusColor(task.status), iconGlyph);
213
+ // In grouped views the group header already carries the row's origin, so the
214
+ // source glyph and tag are suppressed; the rows sit on a faint indent guide.
215
+ const grouped = options.grouped === true;
216
+ const indent = grouped ? theme.fg("borderMuted", GROUP_INDENT_PLAIN) : "";
166
217
  // Source marker between the status icon and the id. Reserve the cell (blank) for
167
218
  // plain tasks so ids stay column-aligned whether or not a glyph is present.
168
219
  const sourceGlyph = task.source ? TASK_SOURCE_GLYPH[task.source] : " ";
169
- const styledSource = task.source ? theme.fg("dim", sourceGlyph) : " ";
220
+ const styledSource = !grouped && task.source ? theme.fg("dim", sourceGlyph) : grouped ? "" : " ";
170
221
  // Right-pad the id to the shared column width so titles line up across rows even
171
222
  // when ids differ in digit count (#1 vs #10). Padding is plain spaces inside the
172
223
  // dim styling, so it adds no visible color.
@@ -174,7 +225,7 @@ function formatTaskLine(task, width, frame, idColWidth) {
174
225
  // Source tag prefixed to the title: the subagent mode (e.g. "[explore]") for
175
226
  // subagent rows, "[MCP]" for MCP rows. Drawn in accent so it reads as the row's
176
227
  // origin label, parallel to the chat's `Agent [explore]` / `MCP [server › tool]`.
177
- const tag = task.source === "mcp" ? "[MCP]" : task.subagentMode ? `[${task.subagentMode}]` : "";
228
+ const tag = grouped ? "" : task.source === "mcp" ? "[MCP]" : task.subagentMode ? `[${task.subagentMode}]` : "";
178
229
  const styledTag = tag ? `${theme.fg("accent", tag)} ` : "";
179
230
  const title = task.title;
180
231
  // The id recedes (dim); the title carries the line. Done titles fade to muted
@@ -239,12 +290,90 @@ function formatTaskLine(task, width, frame, idColWidth) {
239
290
  // truncateToWidth measures visible width (ANSI-aware), so the styled left can be
240
291
  // truncated against the full left budget directly. Subtracting the prefix here
241
292
  // (as a prior version did) truncated titles early and unevenly per id width.
242
- const left = truncateToWidth(`${icon} ${styledSource} ${styledId} ${styledTag}${styledTitle}`, leftWidth, "…");
293
+ const leftBody = grouped
294
+ ? `${indent}${icon} ${styledId} ${styledTitle}`
295
+ : `${icon} ${styledSource} ${styledId} ${styledTag}${styledTitle}`;
296
+ const left = truncateToWidth(leftBody, leftWidth, "…");
243
297
  if (!rightPlain)
244
298
  return left;
245
299
  const pad = Math.max(1, width - visibleWidth(left) - visibleWidth(rightPlain));
246
300
  return left + " ".repeat(pad) + rightStyled;
247
301
  }
302
+ /** Fallback group metadata when a task's owner has no roster entry. */
303
+ function defaultAgentMeta(id) {
304
+ return id === "main"
305
+ ? { id, name: "main", role: "orchestrator", kind: "main" }
306
+ : { id, name: id, role: "subagent", kind: "subagent" };
307
+ }
308
+ /**
309
+ * Partition the flat task list into owner groups. An explicit task.agent wins;
310
+ * otherwise a subagent-sourced task falls into a generic "subagent" group and
311
+ * everything else into "main". Group order is deterministic — main first, then
312
+ * roster order, then stragglers — never reordered by status.
313
+ */
314
+ function groupTasks(tasks, agents) {
315
+ const meta = new Map(agents.map((a) => [a.id, a]));
316
+ const groups = new Map();
317
+ for (const task of tasks) {
318
+ const owner = task.agent ?? (task.source === "subagent" ? "subagent" : "main");
319
+ const items = groups.get(owner);
320
+ if (items)
321
+ items.push(task);
322
+ else
323
+ groups.set(owner, [task]);
324
+ }
325
+ const order = [];
326
+ if (groups.has("main"))
327
+ order.push("main");
328
+ for (const agent of agents) {
329
+ if (groups.has(agent.id) && !order.includes(agent.id))
330
+ order.push(agent.id);
331
+ }
332
+ for (const id of groups.keys()) {
333
+ if (!order.includes(id))
334
+ order.push(id);
335
+ }
336
+ return order.map((id) => ({
337
+ id,
338
+ meta: meta.get(id) ?? defaultAgentMeta(id),
339
+ items: groups.get(id) ?? [],
340
+ }));
341
+ }
342
+ /**
343
+ * Group header for the grouped views: owner glyph + bold name + role, the
344
+ * agent's lifecycle `[state]` tag, an optional handoff arrow (teams), then the
345
+ * agent's own token/cost totals + done/total on the right. Mirrors the footer's
346
+ * "every number accounted for" stance, but per agent.
347
+ */
348
+ function formatGroupHeader(meta, items, width) {
349
+ const glyph = theme.fg(AGENT_GLYPH_COLOR[meta.kind], AGENT_GLYPH[meta.kind] ?? AGENT_GLYPH.subagent);
350
+ const name = theme.bold(meta.name);
351
+ // Roles read as a dim "· role" suffix for spawned/team agents; the main
352
+ // orchestrator's role sits brighter (muted), matching the design's .grp-role.
353
+ const role = meta.role
354
+ ? meta.kind === "main"
355
+ ? ` ${theme.fg("muted", meta.role)}`
356
+ : theme.fg("dim", ` · ${meta.role}`)
357
+ : "";
358
+ const state = meta.state ? ` ${theme.fg(AGENT_STATE_COLOR[meta.state] ?? "dim", `[${meta.state}]`)}` : "";
359
+ const handoff = meta.handoff ? ` ${theme.fg("dim", meta.handoff)}` : "";
360
+ const done = items.filter((t) => t.status === "done").length;
361
+ const countPlain = `${done}/${items.length}`;
362
+ const count = theme.fg("muted", `${done}`) + theme.fg("dim", "/") + theme.fg("muted", `${items.length}`);
363
+ const stats = meta.stats;
364
+ let rightPlain = countPlain;
365
+ let rightStyled = count;
366
+ if (stats && (stats.input > 0 || stats.output > 0 || stats.cost > 0)) {
367
+ const statsPlain = `↑${formatTokens(stats.input)} ↓${formatTokens(stats.output)} · $${stats.cost.toFixed(3)}`;
368
+ rightPlain = `${statsPlain} ${countPlain}`;
369
+ rightStyled = `${theme.fg("dim", statsPlain)} ${count}`;
370
+ }
371
+ const rightWidth = visibleWidth(rightPlain) + 1;
372
+ const leftWidth = Math.max(0, width - rightWidth);
373
+ const left = truncateToWidth(`${glyph} ${name}${role}${state}${handoff}`, leftWidth, "…");
374
+ const pad = Math.max(1, width - visibleWidth(left) - visibleWidth(rightPlain));
375
+ return left + " ".repeat(pad) + rightStyled;
376
+ }
248
377
  /**
249
378
  * Task panel rendered just above the editor prompt.
250
379
  *
@@ -257,6 +386,11 @@ function formatTaskLine(task, width, frame, idColWidth) {
257
386
  * - A single-cell source glyph (⚙ subagent / ⧉ MCP) sits before the id so the two
258
387
  * kinds of background work are distinguishable, and a text origin tag is shown
259
388
  * before the title: the subagent mode (e.g. "[explore]") or "[MCP]".
389
+ * - Three views over the same list (cycled via app.tasks.cycleView, shown as a
390
+ * `tasks · subagents · teams` switcher in the header): flat, grouped by owning
391
+ * agent (subagents), or grouped by named role-agent with handoffs (teams).
392
+ * Grouped rows drop their origin glyph/tag (the group header carries it) and
393
+ * sit on a faint `│` indent guide.
260
394
  * - LIFO within the window: newest tasks appear at the bottom (closest to the prompt).
261
395
  * - Finished tasks carry their wall-clock cost and stay visible until the next
262
396
  * user message arrives (see taskStore.reset()), not the moment they finish.
@@ -266,12 +400,27 @@ export class TaskPanelComponent {
266
400
  ui;
267
401
  frame = 0;
268
402
  animationTimer = null;
403
+ view = "flat";
269
404
  constructor(ui) {
270
405
  this.ui = ui ?? null;
271
406
  }
272
407
  invalidate() {
273
408
  // No cached rendering state.
274
409
  }
410
+ getView() {
411
+ return this.view;
412
+ }
413
+ setView(view) {
414
+ this.view = view;
415
+ this.ui?.requestRender();
416
+ }
417
+ /** Advance to the next view lens (flat → subagents → teams → flat). */
418
+ cycleView() {
419
+ const idx = VIEW_ORDER.indexOf(this.view);
420
+ this.view = VIEW_ORDER[(idx + 1) % VIEW_ORDER.length] ?? "flat";
421
+ this.ui?.requestRender();
422
+ return this.view;
423
+ }
275
424
  /** Run the spinner timer only while a task is active, ticking re-renders. */
276
425
  ensureAnimation(active) {
277
426
  if (active && this.ui && !this.animationTimer) {
@@ -310,9 +459,21 @@ export class TaskPanelComponent {
310
459
  // Width of the id column, sized to the widest id on screen, so every title
311
460
  // starts at the same column regardless of digit count (#1 vs #10 vs #100).
312
461
  const idColWidth = tasks.reduce((max, t) => Math.max(max, `#${t.id}`.length), 0);
313
- const lines = [gutter + formatHeader(tasks, inner, state, totalSecs)];
314
- for (const task of tasks) {
315
- lines.push(gutter + formatTaskLine(task, inner, this.frame, idColWidth));
462
+ const lines = [gutter + formatHeader(tasks, inner, state, totalSecs, this.view)];
463
+ if (this.view === "flat") {
464
+ for (const task of tasks) {
465
+ lines.push(gutter + formatTaskLine(task, inner, this.frame, idColWidth));
466
+ }
467
+ return lines;
468
+ }
469
+ // Grouped lenses (subagents / teams): a header line per owning agent, its
470
+ // tasks indented beneath on a faint guide. Hierarchy is carried by the
471
+ // indent and owner glyph alone — no fills, no boxes.
472
+ for (const group of groupTasks(tasks, taskStore.agents())) {
473
+ lines.push(gutter + formatGroupHeader(group.meta, group.items, inner));
474
+ for (const task of group.items) {
475
+ lines.push(gutter + formatTaskLine(task, inner, this.frame, idColWidth, { grouped: true }));
476
+ }
316
477
  }
317
478
  return lines;
318
479
  }
@@ -1 +1 @@
1
- {"version":3,"file":"task-panel.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/task-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Component, TUI } from \"@kolisachint/hoocode-tui\";\nimport { truncateToWidth, visibleWidth } from \"@kolisachint/hoocode-tui\";\nimport type { Task, TaskSource, TaskStatus } from \"../../../core/task-store.js\";\nimport { taskStore } from \"../../../core/task-store.js\";\nimport { theme } from \"../theme/theme.js\";\n\nconst TASK_STATUS_ICON: Record<TaskStatus, string> = {\n\tpending: \"●\",\n\tin_progress: \"◐\",\n\tdone: \"✓\",\n\tfailed: \"✗\",\n};\n\n/**\n * A single-cell source marker placed before the id so a subagent row and an MCP\n * row are distinguishable at a glance. Plain tasks reserve the cell (blank) to keep\n * the id column aligned. The glyph says *where the work came from* at a glance; the\n * row also carries a text origin tag before the title (see formatTaskLine).\n */\nconst TASK_SOURCE_GLYPH: Record<TaskSource, string> = {\n\tsubagent: \"⚙\",\n\tmcp: \"⧉\",\n};\n\n/** Braille spinner frames + cadence, matched to the TUI Loader so the active row animates in step. */\nconst SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"];\nconst SPINNER_INTERVAL_MS = 80;\n\n/** A thin colored left rail groups the pane without a box, the way the design's `border-left` does. */\nconst RAIL = \"▎\";\n\n/** Cells in the deterministic progress bar (matches the design's 14-cell track). */\nconst PROGRESS_CELLS = 14;\n\n/** Overall pane state, derived from the task statuses. Drives the rail color + header stamp. */\ntype PanelState = \"working\" | \"reviewed\" | \"stopped\";\n\ninterface StatePresentation {\n\treadonly icon: string;\n\treadonly label: string;\n\treadonly color: \"warning\" | \"success\" | \"error\";\n}\n\nconst STATE_PRESENTATION: Record<PanelState, StatePresentation> = {\n\tworking: { icon: \"◐\", label: \"working\", color: \"warning\" },\n\treviewed: { icon: \"✓\", label: \"reviewed\", color: \"success\" },\n\tstopped: { icon: \"✗\", label: \"stopped\", color: \"error\" },\n};\n\nfunction panelState(tasks: readonly Task[]): PanelState {\n\tif (tasks.some((t) => t.status === \"failed\")) return \"stopped\";\n\tconst active = tasks.some((t) => t.status === \"in_progress\" || t.status === \"pending\");\n\treturn active ? \"working\" : \"reviewed\";\n}\n\nfunction taskStatusColor(status: TaskStatus): \"dim\" | \"warning\" | \"success\" | \"error\" {\n\tswitch (status) {\n\t\tcase \"in_progress\":\n\t\t\treturn \"warning\";\n\t\tcase \"done\":\n\t\t\treturn \"success\";\n\t\tcase \"failed\":\n\t\t\treturn \"error\";\n\t\tdefault:\n\t\t\treturn \"dim\";\n\t}\n}\n\n/** Format a duration in seconds into a compact, terminal-friendly string. */\nfunction formatDuration(secs: number): string {\n\tconst s = Math.max(0, secs);\n\tif (s < 10) return `${s.toFixed(1)}s`;\n\tif (s < 60) return `${Math.round(s)}s`;\n\tconst mins = Math.floor(s / 60);\n\tconst rem = Math.round(s % 60);\n\treturn `${mins}m${rem.toString().padStart(2, \"0\")}s`;\n}\n\n/** Wall-clock time a task occupied, derived from its create/update stamps. */\nfunction taskElapsedSecs(task: Task): number {\n\treturn Math.max(0, (task.updatedAt - task.createdAt) / 1000);\n}\n\n/** Sum the token + cost usage reported by the tasks shown this turn. */\nfunction sumTurnUsage(tasks: readonly Task[]): { input: number; output: number; cost: number } | null {\n\tlet input = 0;\n\tlet output = 0;\n\tlet cost = 0;\n\tfor (const task of tasks) {\n\t\tif (!task.usage) continue;\n\t\tinput += task.usage.input;\n\t\toutput += task.usage.output;\n\t\tcost += task.usage.cost;\n\t}\n\tif (input === 0 && output === 0 && cost === 0) return null;\n\treturn { input, output, cost };\n}\n\n/**\n * Deterministic block-glyph progress bar: a heavy run (━) for the completed\n * fraction over a dim track. In-progress tasks count as half, so the bar moves\n * the moment work starts. Fraction is the only input — no animation, no guess.\n */\nfunction progressBar(done: number, active: number, total: number): { plain: string; styled: string } {\n\tconst ratio = total > 0 ? Math.max(0, Math.min(1, (done + active * 0.5) / total)) : 0;\n\tconst filled = Math.round(ratio * PROGRESS_CELLS);\n\tconst fill = \"━\".repeat(filled);\n\tconst track = \"━\".repeat(PROGRESS_CELLS - filled);\n\treturn {\n\t\tplain: fill + track,\n\t\tstyled: theme.fg(\"success\", fill) + theme.fg(\"dim\", track),\n\t};\n}\n\n/**\n * Ledger header: a state stamp (◐ working / ✓ reviewed / ✗ stopped) + a\n * deterministic progress bar and done/total count on the left, and the per-turn\n * token + elapsed + cost delta (summed across the tasks below) on the right.\n */\nfunction formatHeader(tasks: readonly Task[], width: number, state: PanelState, totalSecs: number): string {\n\tconst total = tasks.length;\n\tconst done = tasks.filter((t) => t.status === \"done\").length;\n\tconst active = tasks.filter((t) => t.status === \"in_progress\").length;\n\n\tconst { icon, label, color } = STATE_PRESENTATION[state];\n\tconst stampPlain = `${icon} ${label.toUpperCase()}`;\n\tconst stamp = `${theme.fg(color, icon)} ${theme.bold(theme.fg(color, label.toUpperCase()))}`;\n\n\tconst bar = progressBar(done, active, total);\n\tconst countPlain = `${done}/${total}`;\n\tconst count = theme.fg(\"muted\", `${done}`) + theme.fg(\"dim\", \"/\") + theme.fg(\"muted\", `${total}`);\n\n\t// Left cluster has a full form (stamp · bar · count) and a compact fallback\n\t// (stamp · count) that drops the bar when the terminal is too narrow.\n\tconst leftFullPlain = `${stampPlain} ${bar.plain} ${countPlain}`;\n\tconst leftFull = `${stamp} ${bar.styled} ${count}`;\n\tconst leftMinPlain = `${stampPlain} ${countPlain}`;\n\tconst leftMin = `${stamp} ${count}`;\n\n\tconst turn = sumTurnUsage(tasks);\n\tlet turnPlain = \"\";\n\tlet turnText = \"\";\n\tif (turn) {\n\t\tconst inTok = formatTokens(turn.input);\n\t\tconst outTok = formatTokens(turn.output);\n\t\tconst elapsed = formatDuration(totalSecs);\n\t\tconst showCost = turn.cost > 0;\n\t\tconst costStr = showCost ? `$${turn.cost.toFixed(3)}` : \"\";\n\t\tturnPlain = `turn ↑${inTok} ↓${outTok} · ${elapsed}${showCost ? ` · ${costStr}` : \"\"}`;\n\t\t// Turn delta: muted framing, numbers one step brighter (bold), separators dim.\n\t\tturnText =\n\t\t\ttheme.fg(\"muted\", \"turn ↑\") +\n\t\t\ttheme.bold(inTok) +\n\t\t\ttheme.fg(\"muted\", \" ↓\") +\n\t\t\ttheme.bold(outTok) +\n\t\t\ttheme.fg(\"dim\", \" · \") +\n\t\t\ttheme.fg(\"muted\", elapsed) +\n\t\t\t(showCost ? theme.fg(\"dim\", \" · \") + theme.bold(costStr) : \"\");\n\t}\n\n\tif (turnPlain) {\n\t\tif (visibleWidth(leftFullPlain) + 2 + visibleWidth(turnPlain) <= width) {\n\t\t\tconst pad = Math.max(2, width - visibleWidth(leftFullPlain) - visibleWidth(turnPlain));\n\t\t\treturn leftFull + \" \".repeat(pad) + turnText;\n\t\t}\n\t\tif (visibleWidth(leftMinPlain) + 2 + visibleWidth(turnPlain) <= width) {\n\t\t\tconst pad = Math.max(2, width - visibleWidth(leftMinPlain) - visibleWidth(turnPlain));\n\t\t\treturn leftMin + \" \".repeat(pad) + turnText;\n\t\t}\n\t}\n\tif (visibleWidth(leftFullPlain) <= width) return leftFull;\n\treturn truncateToWidth(leftMin, width, \"…\");\n}\n\nfunction formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\treturn `${(count / 1000000).toFixed(1)}M`;\n}\n\nfunction formatTaskLine(task: Task, width: number, frame: number, idColWidth: number): string {\n\tconst isProgress = task.status === \"in_progress\";\n\tconst iconGlyph = isProgress\n\t\t? (SPINNER_FRAMES[frame] ?? TASK_STATUS_ICON.in_progress)\n\t\t: TASK_STATUS_ICON[task.status];\n\tconst icon = theme.fg(taskStatusColor(task.status), iconGlyph);\n\n\t// Source marker between the status icon and the id. Reserve the cell (blank) for\n\t// plain tasks so ids stay column-aligned whether or not a glyph is present.\n\tconst sourceGlyph = task.source ? TASK_SOURCE_GLYPH[task.source] : \" \";\n\tconst styledSource = task.source ? theme.fg(\"dim\", sourceGlyph) : \" \";\n\n\t// Right-pad the id to the shared column width so titles line up across rows even\n\t// when ids differ in digit count (#1 vs #10). Padding is plain spaces inside the\n\t// dim styling, so it adds no visible color.\n\tconst idLabel = `#${task.id}`.padEnd(idColWidth);\n\t// Source tag prefixed to the title: the subagent mode (e.g. \"[explore]\") for\n\t// subagent rows, \"[MCP]\" for MCP rows. Drawn in accent so it reads as the row's\n\t// origin label, parallel to the chat's `Agent [explore]` / `MCP [server › tool]`.\n\tconst tag = task.source === \"mcp\" ? \"[MCP]\" : task.subagentMode ? `[${task.subagentMode}]` : \"\";\n\tconst styledTag = tag ? `${theme.fg(\"accent\", tag)} ` : \"\";\n\tconst title = task.title;\n\t// The id recedes (dim); the title carries the line. Done titles fade to muted\n\t// (settled work), pending dim (not started), active goes bold, failed turns red.\n\tconst styledId = theme.fg(\"dim\", idLabel);\n\tlet styledTitle: string;\n\tswitch (task.status) {\n\t\tcase \"done\":\n\t\t\tstyledTitle = theme.fg(\"muted\", title);\n\t\t\tbreak;\n\t\tcase \"pending\":\n\t\t\tstyledTitle = theme.fg(\"dim\", title);\n\t\t\tbreak;\n\t\tcase \"failed\":\n\t\t\tstyledTitle = theme.fg(\"error\", title);\n\t\t\tbreak;\n\t\tcase \"in_progress\":\n\t\t\tstyledTitle = theme.bold(title);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tstyledTitle = title;\n\t}\n\n\t// Right column: settled rows carry their audit stamp (tokens + elapsed); the\n\t// active row reads `running…`, pending rows read `queued`.\n\tlet rightPlain = \"\";\n\tlet rightStyled = \"\";\n\tif (task.status === \"done\" || task.status === \"failed\") {\n\t\tconst parts: string[] = [];\n\t\tlet tokenText = \"\";\n\t\tif (task.usage) {\n\t\t\tconst totalTok = task.usage.input + task.usage.output;\n\t\t\tif (totalTok > 0) tokenText = formatTokens(totalTok);\n\t\t}\n\t\tconst elapsed = formatDuration(taskElapsedSecs(task));\n\t\tif (tokenText) {\n\t\t\tparts.push(tokenText, elapsed);\n\t\t\trightStyled = theme.fg(\"muted\", tokenText) + theme.fg(\"dim\", ` · ${elapsed}`);\n\t\t} else {\n\t\t\tparts.push(elapsed);\n\t\t\trightStyled = theme.fg(\"dim\", elapsed);\n\t\t}\n\t\trightPlain = parts.join(\" · \");\n\t} else if (task.status === \"in_progress\") {\n\t\trightPlain = \"running…\";\n\t\trightStyled = theme.fg(\"warning\", rightPlain);\n\t} else if (task.status === \"pending\") {\n\t\trightPlain = \"queued\";\n\t\trightStyled = theme.fg(\"dim\", rightPlain);\n\t}\n\n\t// A warning note (e.g. inherited-model fallback, exhaustion skip) takes over the\n\t// right column as a ⚠ cue, replacing the usage/status stamp for that row.\n\tif (task.note) {\n\t\trightPlain = `⚠ ${task.note}`;\n\t\trightStyled = theme.fg(\"warning\", rightPlain);\n\t}\n\n\tconst rightWidth = rightPlain ? visibleWidth(rightPlain) + 1 : 0;\n\tconst leftWidth = Math.max(0, width - rightWidth);\n\n\t// truncateToWidth measures visible width (ANSI-aware), so the styled left can be\n\t// truncated against the full left budget directly. Subtracting the prefix here\n\t// (as a prior version did) truncated titles early and unevenly per id width.\n\tconst left = truncateToWidth(`${icon} ${styledSource} ${styledId} ${styledTag}${styledTitle}`, leftWidth, \"…\");\n\n\tif (!rightPlain) return left;\n\n\tconst pad = Math.max(1, width - visibleWidth(left) - visibleWidth(rightPlain));\n\treturn left + \" \".repeat(pad) + rightStyled;\n}\n\n/**\n * Task panel rendered just above the editor prompt.\n *\n * - A state-colored left rail groups the pane (working=warning, reviewed=success,\n * stopped=error) without drawing a box.\n * - A ledger header tops the list: a state stamp + deterministic progress bar +\n * done/total count on the left, the per-turn token/elapsed/cost delta on the right.\n * - Shows all tasks with all statuses (pending / in_progress / done / failed).\n * The active row animates a braille spinner; pending rows read `queued`.\n * - A single-cell source glyph (⚙ subagent / ⧉ MCP) sits before the id so the two\n * kinds of background work are distinguishable, and a text origin tag is shown\n * before the title: the subagent mode (e.g. \"[explore]\") or \"[MCP]\".\n * - LIFO within the window: newest tasks appear at the bottom (closest to the prompt).\n * - Finished tasks carry their wall-clock cost and stay visible until the next\n * user message arrives (see taskStore.reset()), not the moment they finish.\n * - Collapses to zero lines when there are no tasks.\n */\nexport class TaskPanelComponent implements Component {\n\tprivate readonly ui: TUI | null;\n\tprivate frame = 0;\n\tprivate animationTimer: ReturnType<typeof setInterval> | null = null;\n\n\tconstructor(ui?: TUI) {\n\t\tthis.ui = ui ?? null;\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached rendering state.\n\t}\n\n\t/** Run the spinner timer only while a task is active, ticking re-renders. */\n\tprivate ensureAnimation(active: boolean): void {\n\t\tif (active && this.ui && !this.animationTimer) {\n\t\t\tthis.animationTimer = setInterval(() => {\n\t\t\t\tthis.frame = (this.frame + 1) % SPINNER_FRAMES.length;\n\t\t\t\tthis.ui?.requestRender();\n\t\t\t}, SPINNER_INTERVAL_MS);\n\t\t\tthis.animationTimer.unref?.();\n\t\t} else if (!active && this.animationTimer) {\n\t\t\tclearInterval(this.animationTimer);\n\t\t\tthis.animationTimer = null;\n\t\t\tthis.frame = 0;\n\t\t}\n\t}\n\n\t/** Stop the spinner timer. Call on teardown. */\n\tdispose(): void {\n\t\tif (this.animationTimer) {\n\t\t\tclearInterval(this.animationTimer);\n\t\t\tthis.animationTimer = null;\n\t\t}\n\t}\n\n\trender(width: number): string[] {\n\t\tconst tasks = taskStore.list();\n\t\tif (tasks.length === 0) {\n\t\t\tthis.ensureAnimation(false);\n\t\t\treturn [];\n\t\t}\n\n\t\tconst hasActive = tasks.some((t) => t.status === \"in_progress\");\n\t\tthis.ensureAnimation(hasActive);\n\n\t\tconst state = panelState(tasks);\n\t\tconst totalSecs = tasks.reduce((sum, t) => sum + taskElapsedSecs(t), 0);\n\t\tconst railColor = STATE_PRESENTATION[state].color;\n\t\tconst gutter = `${theme.fg(railColor, RAIL)} `;\n\t\tconst inner = Math.max(0, width - visibleWidth(RAIL) - 1);\n\n\t\t// Width of the id column, sized to the widest id on screen, so every title\n\t\t// starts at the same column regardless of digit count (#1 vs #10 vs #100).\n\t\tconst idColWidth = tasks.reduce((max, t) => Math.max(max, `#${t.id}`.length), 0);\n\n\t\tconst lines: string[] = [gutter + formatHeader(tasks, inner, state, totalSecs)];\n\t\tfor (const task of tasks) {\n\t\t\tlines.push(gutter + formatTaskLine(task, inner, this.frame, idColWidth));\n\t\t}\n\t\treturn lines;\n\t}\n}\n"]}
1
+ {"version":3,"file":"task-panel.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/task-panel.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AASzE,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAExD,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C,MAAM,gBAAgB,GAA+B;IACpD,OAAO,EAAE,KAAG;IACZ,WAAW,EAAE,KAAG;IAChB,IAAI,EAAE,KAAG;IACT,MAAM,EAAE,KAAG;CACX,CAAC;AAEF;;;;;GAKG;AACH,MAAM,iBAAiB,GAA+B;IACrD,QAAQ,EAAE,KAAG;IACb,GAAG,EAAE,KAAG;CACR,CAAC;AAEF,sGAAsG;AACtG,MAAM,cAAc,GAAG,CAAC,KAAG,EAAE,KAAG,EAAE,KAAG,EAAE,KAAG,EAAE,KAAG,EAAE,KAAG,EAAE,KAAG,EAAE,KAAG,EAAE,KAAG,EAAE,KAAG,CAAC,CAAC;AAC1E,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAE/B,uGAAuG;AACvG,MAAM,IAAI,GAAG,KAAG,CAAC;AAEjB,oFAAoF;AACpF,MAAM,cAAc,GAAG,EAAE,CAAC;AAU1B,MAAM,UAAU,GAA6B,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;AAC5E,MAAM,UAAU,GAAkC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AAE5G,4GAA4G;AAC5G,MAAM,WAAW,GAAkC,EAAE,IAAI,EAAE,KAAG,EAAE,QAAQ,EAAE,KAAG,EAAE,IAAI,EAAE,KAAG,EAAE,CAAC;AAC3F,MAAM,iBAAiB,GAAsC;IAC5D,IAAI,EAAE,QAAQ;IACd,QAAQ,EAAE,QAAQ;IAClB,IAAI,EAAE,cAAc;CACpB,CAAC;AAEF,0FAA0F;AAC1F,MAAM,iBAAiB,GAAuC;IAC7D,MAAM,EAAE,SAAS;IACjB,OAAO,EAAE,SAAS;IAClB,IAAI,EAAE,SAAS;IACf,MAAM,EAAE,KAAK;IACb,IAAI,EAAE,KAAK;IACX,OAAO,EAAE,QAAQ;IACjB,MAAM,EAAE,OAAO;CACf,CAAC;AAEF,yEAAyE;AACzE,MAAM,kBAAkB,GAAG,MAAI,CAAC;AAWhC,MAAM,kBAAkB,GAA0C;IACjE,OAAO,EAAE,EAAE,IAAI,EAAE,KAAG,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE;IAC1D,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAG,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE;IAC5D,OAAO,EAAE,EAAE,IAAI,EAAE,KAAG,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE;CACxD,CAAC;AAEF,SAAS,UAAU,CAAC,KAAsB,EAAc;IACvD,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/D,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,aAAa,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;IACvF,OAAO,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC;AAAA,CACvC;AAED,SAAS,eAAe,CAAC,MAAkB,EAA2C;IACrF,QAAQ,MAAM,EAAE,CAAC;QAChB,KAAK,aAAa;YACjB,OAAO,SAAS,CAAC;QAClB,KAAK,MAAM;YACV,OAAO,SAAS,CAAC;QAClB,KAAK,QAAQ;YACZ,OAAO,OAAO,CAAC;QAChB;YACC,OAAO,KAAK,CAAC;IACf,CAAC;AAAA,CACD;AAED,6EAA6E;AAC7E,SAAS,cAAc,CAAC,IAAY,EAAU;IAC7C,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC,GAAG,EAAE;QAAE,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IACtC,IAAI,CAAC,GAAG,EAAE;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC;IACvC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;IAChC,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;IAC/B,OAAO,GAAG,IAAI,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC;AAAA,CACrD;AAED,8EAA8E;AAC9E,SAAS,eAAe,CAAC,IAAU,EAAU;IAC5C,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,CAAC;AAAA,CAC7D;AAED,wEAAwE;AACxE,SAAS,YAAY,CAAC,KAAsB,EAA0D;IACrG,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,SAAS;QAC1B,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;QAC1B,MAAM,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;QAC5B,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;IACD,IAAI,KAAK,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3D,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAAA,CAC/B;AAED;;;;GAIG;AACH,SAAS,WAAW,CAAC,IAAY,EAAE,MAAc,EAAE,KAAa,EAAqC;IACpG,MAAM,KAAK,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,IAAI,GAAG,MAAM,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACtF,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,cAAc,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG,KAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAChC,MAAM,KAAK,GAAG,KAAG,CAAC,MAAM,CAAC,cAAc,GAAG,MAAM,CAAC,CAAC;IAClD,OAAO;QACN,KAAK,EAAE,IAAI,GAAG,KAAK;QACnB,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC;KAC1D,CAAC;AAAA,CACF;AAED;;;;GAIG;AACH,SAAS,kBAAkB,CAAC,IAAmB,EAAqC;IACnF,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAK,CAAC,CAAC;IAC/D,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CACnC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAC3F,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAK,CAAC,CAAC,CAAC;IAC/B,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;AAAA,CACzB;AAED;;;;;GAKG;AACH,SAAS,YAAY,CACpB,KAAsB,EACtB,KAAa,EACb,KAAiB,EACjB,SAAiB,EACjB,IAAmB,EACV;IACT,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC;IAC3B,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;IAC7D,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,aAAa,CAAC,CAAC,MAAM,CAAC;IAEtE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACzD,MAAM,UAAU,GAAG,GAAG,IAAI,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;IACpD,MAAM,KAAK,GAAG,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,CAAC;IAE7F,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;IAC7C,MAAM,UAAU,GAAG,GAAG,IAAI,IAAI,KAAK,EAAE,CAAC;IACtC,MAAM,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;IAElG,8EAA4E;IAC5E,uEAAsE;IACtE,MAAM,aAAa,GAAG,GAAG,UAAU,KAAK,GAAG,CAAC,KAAK,IAAI,UAAU,EAAE,CAAC;IAClE,MAAM,QAAQ,GAAG,GAAG,KAAK,KAAK,GAAG,CAAC,MAAM,IAAI,KAAK,EAAE,CAAC;IACpD,MAAM,YAAY,GAAG,GAAG,UAAU,IAAI,UAAU,EAAE,CAAC;IACnD,MAAM,OAAO,GAAG,GAAG,KAAK,IAAI,KAAK,EAAE,CAAC;IAEpC,MAAM,IAAI,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACjC,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,IAAI,QAAQ,GAAG,EAAE,CAAC;IAClB,IAAI,IAAI,EAAE,CAAC;QACV,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;QAC1C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,GAAG,CAAC,CAAC;QAC/B,MAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,SAAS,GAAG,WAAS,KAAK,OAAK,MAAM,OAAM,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,OAAM,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QACvF,+EAA+E;QAC/E,QAAQ;YACP,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,UAAQ,CAAC;gBAC3B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;gBACjB,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAI,CAAC;gBACvB,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC;gBAClB,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAK,CAAC;gBACtB,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC;gBAC1B,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAK,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,yEAAyE;IACzE,0EAA0E;IAC1E,kDAAkD;IAClD,MAAM,QAAQ,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,aAAa,GAA6C,EAAE,CAAC;IACnE,IAAI,SAAS,EAAE,CAAC;QACf,aAAa,CAAC,IAAI,CAAC;YAClB,KAAK,EAAE,GAAG,SAAS,KAAK,QAAQ,CAAC,KAAK,EAAE;YACxC,MAAM,EAAE,GAAG,QAAQ,KAAK,QAAQ,CAAC,MAAM,EAAE;SACzC,CAAC,CAAC;QACH,aAAa,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC5D,CAAC;SAAM,CAAC;QACP,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;QACnC,IAAI,YAAY,CAAC,aAAa,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC;YAC1E,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,YAAY,CAAC,aAAa,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;YACzF,OAAO,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;QAClD,CAAC;QACD,IAAI,YAAY,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC;YACzE,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,YAAY,CAAC,YAAY,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;YACxF,OAAO,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;QACjD,CAAC;IACF,CAAC;IACD,IAAI,YAAY,CAAC,aAAa,CAAC,IAAI,KAAK;QAAE,OAAO,QAAQ,CAAC;IAC1D,OAAO,eAAe,CAAC,OAAO,EAAE,KAAK,EAAE,KAAG,CAAC,CAAC;AAAA,CAC5C;AAED,SAAS,YAAY,CAAC,KAAa,EAAU;IAC5C,IAAI,KAAK,GAAG,IAAI;QAAE,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;IAC1C,IAAI,KAAK,GAAG,KAAK;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IAC1D,IAAI,KAAK,GAAG,OAAO;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC;IAC3D,OAAO,GAAG,CAAC,KAAK,GAAG,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;AAAA,CAC1C;AAED,SAAS,cAAc,CACtB,IAAU,EACV,KAAa,EACb,KAAa,EACb,UAAkB,EAClB,OAAO,GAA0B,EAAE,EAC1B;IACT,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,KAAK,aAAa,CAAC;IACjD,MAAM,SAAS,GAAG,UAAU;QAC3B,CAAC,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,IAAI,gBAAgB,CAAC,WAAW,CAAC;QACzD,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC;IAE/D,6EAA6E;IAC7E,6EAA6E;IAC7E,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,KAAK,IAAI,CAAC;IACzC,MAAM,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,aAAa,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE1E,iFAAiF;IACjF,4EAA4E;IAC5E,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IACvE,MAAM,YAAY,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;IAEjG,iFAAiF;IACjF,iFAAiF;IACjF,4CAA4C;IAC5C,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACjD,6EAA6E;IAC7E,gFAAgF;IAChF,oFAAkF;IAClF,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,KAAK,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/G,MAAM,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IACzB,8EAA8E;IAC9E,iFAAiF;IACjF,MAAM,QAAQ,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IAC1C,IAAI,WAAmB,CAAC;IACxB,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;QACrB,KAAK,MAAM;YACV,WAAW,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YACvC,MAAM;QACP,KAAK,SAAS;YACb,WAAW,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;YACrC,MAAM;QACP,KAAK,QAAQ;YACZ,WAAW,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YACvC,MAAM;QACP,KAAK,aAAa;YACjB,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAChC,MAAM;QACP;YACC,WAAW,GAAG,KAAK,CAAC;IACtB,CAAC;IAED,6EAA6E;IAC7E,6DAA2D;IAC3D,IAAI,UAAU,GAAG,EAAE,CAAC;IACpB,IAAI,WAAW,GAAG,EAAE,CAAC;IACrB,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,IAAI,IAAI,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACxD,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,IAAI,SAAS,GAAG,EAAE,CAAC;QACnB,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAChB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;YACtD,IAAI,QAAQ,GAAG,CAAC;gBAAE,SAAS,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;QACtD,CAAC;QACD,MAAM,OAAO,GAAG,cAAc,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;QACtD,IAAI,SAAS,EAAE,CAAC;YACf,KAAK,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;YAC/B,WAAW,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,SAAS,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAM,OAAO,EAAE,CAAC,CAAC;QAC/E,CAAC;aAAM,CAAC;YACP,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YACpB,WAAW,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACxC,CAAC;QACD,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,MAAK,CAAC,CAAC;IAChC,CAAC;SAAM,IAAI,IAAI,CAAC,MAAM,KAAK,aAAa,EAAE,CAAC;QAC1C,UAAU,GAAG,YAAU,CAAC;QACxB,WAAW,GAAG,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAC/C,CAAC;SAAM,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QACtC,UAAU,GAAG,QAAQ,CAAC;QACtB,WAAW,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;IAC3C,CAAC;IAED,iFAAiF;IACjF,4EAA0E;IAC1E,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QACf,UAAU,GAAG,OAAK,IAAI,CAAC,IAAI,EAAE,CAAC;QAC9B,WAAW,GAAG,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACjE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,UAAU,CAAC,CAAC;IAElD,iFAAiF;IACjF,+EAA+E;IAC/E,6EAA6E;IAC7E,MAAM,QAAQ,GAAG,OAAO;QACvB,CAAC,CAAC,GAAG,MAAM,GAAG,IAAI,IAAI,QAAQ,IAAI,WAAW,EAAE;QAC/C,CAAC,CAAC,GAAG,IAAI,IAAI,YAAY,IAAI,QAAQ,IAAI,SAAS,GAAG,WAAW,EAAE,CAAC;IACpE,MAAM,IAAI,GAAG,eAAe,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAG,CAAC,CAAC;IAEvD,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAE7B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC;IAC/E,OAAO,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC;AAAA,CAC5C;AAED,uEAAuE;AACvE,SAAS,gBAAgB,CAAC,EAAU,EAAa;IAChD,OAAO,EAAE,KAAK,MAAM;QACnB,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE;QAC1D,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AAAA,CACxD;AAED;;;;;GAKG;AACH,SAAS,UAAU,CAClB,KAAsB,EACtB,MAA4B,EAC4B;IACxD,MAAM,IAAI,GAAG,IAAI,GAAG,CAAoB,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IACtE,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAC/E,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;;YACvB,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAChC,CAAC;IACD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC3C,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC5B,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC7E,CAAC;IACD,KAAK,MAAM,EAAE,IAAI,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;QAChC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;YAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACzB,EAAE;QACF,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,gBAAgB,CAAC,EAAE,CAAC;QAC1C,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE;KAC3B,CAAC,CAAC,CAAC;AAAA,CACJ;AAED;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,IAAe,EAAE,KAAsB,EAAE,KAAa,EAAU;IAC1F,MAAM,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC;IACrG,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnC,yEAAwE;IACxE,8EAA8E;IAC9E,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI;QACrB,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM;YACrB,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE;YACpC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QACrC,CAAC,CAAC,EAAE,CAAC;IACN,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,EAAE,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1G,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAExE,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;IAC7D,MAAM,UAAU,GAAG,GAAG,IAAI,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;IAC7C,MAAM,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC;IACzG,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;IACzB,IAAI,UAAU,GAAG,UAAU,CAAC;IAC5B,IAAI,WAAW,GAAG,KAAK,CAAC;IACxB,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,EAAE,CAAC;QACtE,MAAM,UAAU,GAAG,MAAI,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,OAAK,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,QAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9G,UAAU,GAAG,GAAG,UAAU,KAAK,UAAU,EAAE,CAAC;QAC5C,WAAW,GAAG,GAAG,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,KAAK,KAAK,EAAE,CAAC;IAC1D,CAAC;IAED,MAAM,UAAU,GAAG,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAChD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,UAAU,CAAC,CAAC;IAClD,MAAM,IAAI,GAAG,eAAe,CAAC,GAAG,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,OAAO,EAAE,EAAE,SAAS,EAAE,KAAG,CAAC,CAAC;IAC1F,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC;IAC/E,OAAO,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC;AAAA,CAC5C;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,OAAO,kBAAkB;IACb,EAAE,CAAa;IACxB,KAAK,GAAG,CAAC,CAAC;IACV,cAAc,GAA0C,IAAI,CAAC;IAC7D,IAAI,GAAkB,MAAM,CAAC;IAErC,YAAY,EAAQ,EAAE;QACrB,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,IAAI,CAAC;IAAA,CACrB;IAED,UAAU,GAAS;QAClB,6BAA6B;IADV,CAEnB;IAED,OAAO,GAAkB;QACxB,OAAO,IAAI,CAAC,IAAI,CAAC;IAAA,CACjB;IAED,OAAO,CAAC,IAAmB,EAAQ;QAClC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,EAAE,EAAE,aAAa,EAAE,CAAC;IAAA,CACzB;IAED,6EAAuE;IACvE,SAAS,GAAkB;QAC1B,MAAM,GAAG,GAAG,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC;QAChE,IAAI,CAAC,EAAE,EAAE,aAAa,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC,IAAI,CAAC;IAAA,CACjB;IAED,6EAA6E;IACrE,eAAe,CAAC,MAAe,EAAQ;QAC9C,IAAI,MAAM,IAAI,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YAC/C,IAAI,CAAC,cAAc,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;gBACvC,IAAI,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,cAAc,CAAC,MAAM,CAAC;gBACtD,IAAI,CAAC,EAAE,EAAE,aAAa,EAAE,CAAC;YAAA,CACzB,EAAE,mBAAmB,CAAC,CAAC;YACxB,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,EAAE,CAAC;QAC/B,CAAC;aAAM,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YAC3C,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACnC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;YAC3B,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;QAChB,CAAC;IAAA,CACD;IAED,gDAAgD;IAChD,OAAO,GAAS;QACf,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACzB,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACnC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC5B,CAAC;IAAA,CACD;IAED,MAAM,CAAC,KAAa,EAAY;QAC/B,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC;QAC/B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;YAC5B,OAAO,EAAE,CAAC;QACX,CAAC;QAED,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,aAAa,CAAC,CAAC;QAChE,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;QAEhC,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QAChC,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACxE,MAAM,SAAS,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC;QAClD,MAAM,MAAM,GAAG,GAAG,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC;QAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAE1D,2EAA2E;QAC3E,2EAA2E;QAC3E,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAEjF,MAAM,KAAK,GAAa,CAAC,MAAM,GAAG,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3F,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC1B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBAC1B,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,cAAc,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC;YAC1E,CAAC;YACD,OAAO,KAAK,CAAC;QACd,CAAC;QAED,0EAA0E;QAC1E,uEAAuE;QACvE,uDAAqD;QACrD,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC;YAC3D,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;YACvE,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;gBAChC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,cAAc,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAC7F,CAAC;QACF,CAAC;QACD,OAAO,KAAK,CAAC;IAAA,CACb;CACD","sourcesContent":["import type { Component, TUI } from \"@kolisachint/hoocode-tui\";\nimport { truncateToWidth, visibleWidth } from \"@kolisachint/hoocode-tui\";\nimport type {\n\tTask,\n\tTaskAgent,\n\tTaskAgentKind,\n\tTaskAgentState,\n\tTaskSource,\n\tTaskStatus,\n} from \"../../../core/task-store.js\";\nimport { taskStore } from \"../../../core/task-store.js\";\nimport type { ThemeColor } from \"../theme/theme.js\";\nimport { theme } from \"../theme/theme.js\";\n\nconst TASK_STATUS_ICON: Record<TaskStatus, string> = {\n\tpending: \"●\",\n\tin_progress: \"◐\",\n\tdone: \"✓\",\n\tfailed: \"✗\",\n};\n\n/**\n * A single-cell source marker placed before the id so a subagent row and an MCP\n * row are distinguishable at a glance. Plain tasks reserve the cell (blank) to keep\n * the id column aligned. The glyph says *where the work came from* at a glance; the\n * row also carries a text origin tag before the title (see formatTaskLine).\n */\nconst TASK_SOURCE_GLYPH: Record<TaskSource, string> = {\n\tsubagent: \"⚙\",\n\tmcp: \"⧉\",\n};\n\n/** Braille spinner frames + cadence, matched to the TUI Loader so the active row animates in step. */\nconst SPINNER_FRAMES = [\"⠋\", \"⠙\", \"⠹\", \"⠸\", \"⠼\", \"⠴\", \"⠦\", \"⠧\", \"⠇\", \"⠏\"];\nconst SPINNER_INTERVAL_MS = 80;\n\n/** A thin colored left rail groups the pane without a box, the way the design's `border-left` does. */\nconst RAIL = \"▎\";\n\n/** Cells in the deterministic progress bar (matches the design's 14-cell track). */\nconst PROGRESS_CELLS = 14;\n\n/**\n * How the same task list is presented:\n * - flat → one ungrouped list (default)\n * - subagents → grouped by owning agent (◆ main orchestrator + ⊕ workers)\n * - teams → grouped by named role-agent (▸), with handoff arrows\n */\nexport type TaskPanelView = \"flat\" | \"subagents\" | \"teams\";\n\nconst VIEW_ORDER: readonly TaskPanelView[] = [\"flat\", \"subagents\", \"teams\"];\nconst VIEW_LABEL: Record<TaskPanelView, string> = { flat: \"tasks\", subagents: \"subagents\", teams: \"teams\" };\n\n/** Owner glyphs: main agent a filled diamond, spawned subagents a plus-in-circle, team roles a triangle. */\nconst AGENT_GLYPH: Record<TaskAgentKind, string> = { main: \"◆\", subagent: \"⊕\", role: \"▸\" };\nconst AGENT_GLYPH_COLOR: Record<TaskAgentKind, ThemeColor> = {\n\tmain: \"accent\",\n\tsubagent: \"accent\",\n\trole: \"borderAccent\",\n};\n\n/** Color for an agent's lifecycle `[state]` tag (mirrors the design's .ast-* classes). */\nconst AGENT_STATE_COLOR: Record<TaskAgentState, ThemeColor> = {\n\tactive: \"warning\",\n\trunning: \"warning\",\n\tdone: \"success\",\n\tqueued: \"dim\",\n\tidle: \"dim\",\n\twaiting: \"mdLink\",\n\tfailed: \"error\",\n};\n\n/** Two-cell indent under a group header, with a faint vertical guide. */\nconst GROUP_INDENT_PLAIN = \"│ \";\n\n/** Overall pane state, derived from the task statuses. Drives the rail color + header stamp. */\ntype PanelState = \"working\" | \"reviewed\" | \"stopped\";\n\ninterface StatePresentation {\n\treadonly icon: string;\n\treadonly label: string;\n\treadonly color: \"warning\" | \"success\" | \"error\";\n}\n\nconst STATE_PRESENTATION: Record<PanelState, StatePresentation> = {\n\tworking: { icon: \"◐\", label: \"working\", color: \"warning\" },\n\treviewed: { icon: \"✓\", label: \"reviewed\", color: \"success\" },\n\tstopped: { icon: \"✗\", label: \"stopped\", color: \"error\" },\n};\n\nfunction panelState(tasks: readonly Task[]): PanelState {\n\tif (tasks.some((t) => t.status === \"failed\")) return \"stopped\";\n\tconst active = tasks.some((t) => t.status === \"in_progress\" || t.status === \"pending\");\n\treturn active ? \"working\" : \"reviewed\";\n}\n\nfunction taskStatusColor(status: TaskStatus): \"dim\" | \"warning\" | \"success\" | \"error\" {\n\tswitch (status) {\n\t\tcase \"in_progress\":\n\t\t\treturn \"warning\";\n\t\tcase \"done\":\n\t\t\treturn \"success\";\n\t\tcase \"failed\":\n\t\t\treturn \"error\";\n\t\tdefault:\n\t\t\treturn \"dim\";\n\t}\n}\n\n/** Format a duration in seconds into a compact, terminal-friendly string. */\nfunction formatDuration(secs: number): string {\n\tconst s = Math.max(0, secs);\n\tif (s < 10) return `${s.toFixed(1)}s`;\n\tif (s < 60) return `${Math.round(s)}s`;\n\tconst mins = Math.floor(s / 60);\n\tconst rem = Math.round(s % 60);\n\treturn `${mins}m${rem.toString().padStart(2, \"0\")}s`;\n}\n\n/** Wall-clock time a task occupied, derived from its create/update stamps. */\nfunction taskElapsedSecs(task: Task): number {\n\treturn Math.max(0, (task.updatedAt - task.createdAt) / 1000);\n}\n\n/** Sum the token + cost usage reported by the tasks shown this turn. */\nfunction sumTurnUsage(tasks: readonly Task[]): { input: number; output: number; cost: number } | null {\n\tlet input = 0;\n\tlet output = 0;\n\tlet cost = 0;\n\tfor (const task of tasks) {\n\t\tif (!task.usage) continue;\n\t\tinput += task.usage.input;\n\t\toutput += task.usage.output;\n\t\tcost += task.usage.cost;\n\t}\n\tif (input === 0 && output === 0 && cost === 0) return null;\n\treturn { input, output, cost };\n}\n\n/**\n * Deterministic block-glyph progress bar: a heavy run (━) for the completed\n * fraction over a dim track. In-progress tasks count as half, so the bar moves\n * the moment work starts. Fraction is the only input — no animation, no guess.\n */\nfunction progressBar(done: number, active: number, total: number): { plain: string; styled: string } {\n\tconst ratio = total > 0 ? Math.max(0, Math.min(1, (done + active * 0.5) / total)) : 0;\n\tconst filled = Math.round(ratio * PROGRESS_CELLS);\n\tconst fill = \"━\".repeat(filled);\n\tconst track = \"━\".repeat(PROGRESS_CELLS - filled);\n\treturn {\n\t\tplain: fill + track,\n\t\tstyled: theme.fg(\"success\", fill) + theme.fg(\"dim\", track),\n\t};\n}\n\n/**\n * View switcher rendered at the right edge of the ledger header: the three\n * lens labels joined by `·`, the active one in bold accent. Purely an\n * indicator in the TUI — the bound key cycles it (see app.tasks.cycleView).\n */\nfunction formatViewSwitcher(view: TaskPanelView): { plain: string; styled: string } {\n\tconst plain = VIEW_ORDER.map((v) => VIEW_LABEL[v]).join(\" · \");\n\tconst styled = VIEW_ORDER.map((v) =>\n\t\tv === view ? theme.bold(theme.fg(\"accent\", VIEW_LABEL[v])) : theme.fg(\"dim\", VIEW_LABEL[v]),\n\t).join(theme.fg(\"dim\", \" · \"));\n\treturn { plain, styled };\n}\n\n/**\n * Ledger header: a state stamp (◐ working / ✓ reviewed / ✗ stopped) + a\n * deterministic progress bar and done/total count on the left, and the per-turn\n * token + elapsed + cost delta (summed across the tasks below) plus the view\n * switcher on the right.\n */\nfunction formatHeader(\n\ttasks: readonly Task[],\n\twidth: number,\n\tstate: PanelState,\n\ttotalSecs: number,\n\tview: TaskPanelView,\n): string {\n\tconst total = tasks.length;\n\tconst done = tasks.filter((t) => t.status === \"done\").length;\n\tconst active = tasks.filter((t) => t.status === \"in_progress\").length;\n\n\tconst { icon, label, color } = STATE_PRESENTATION[state];\n\tconst stampPlain = `${icon} ${label.toUpperCase()}`;\n\tconst stamp = `${theme.fg(color, icon)} ${theme.bold(theme.fg(color, label.toUpperCase()))}`;\n\n\tconst bar = progressBar(done, active, total);\n\tconst countPlain = `${done}/${total}`;\n\tconst count = theme.fg(\"muted\", `${done}`) + theme.fg(\"dim\", \"/\") + theme.fg(\"muted\", `${total}`);\n\n\t// Left cluster has a full form (stamp · bar · count) and a compact fallback\n\t// (stamp · count) that drops the bar when the terminal is too narrow.\n\tconst leftFullPlain = `${stampPlain} ${bar.plain} ${countPlain}`;\n\tconst leftFull = `${stamp} ${bar.styled} ${count}`;\n\tconst leftMinPlain = `${stampPlain} ${countPlain}`;\n\tconst leftMin = `${stamp} ${count}`;\n\n\tconst turn = sumTurnUsage(tasks);\n\tlet turnPlain = \"\";\n\tlet turnText = \"\";\n\tif (turn) {\n\t\tconst inTok = formatTokens(turn.input);\n\t\tconst outTok = formatTokens(turn.output);\n\t\tconst elapsed = formatDuration(totalSecs);\n\t\tconst showCost = turn.cost > 0;\n\t\tconst costStr = showCost ? `$${turn.cost.toFixed(3)}` : \"\";\n\t\tturnPlain = `turn ↑${inTok} ↓${outTok} · ${elapsed}${showCost ? ` · ${costStr}` : \"\"}`;\n\t\t// Turn delta: muted framing, numbers one step brighter (bold), separators dim.\n\t\tturnText =\n\t\t\ttheme.fg(\"muted\", \"turn ↑\") +\n\t\t\ttheme.bold(inTok) +\n\t\t\ttheme.fg(\"muted\", \" ↓\") +\n\t\t\ttheme.bold(outTok) +\n\t\t\ttheme.fg(\"dim\", \" · \") +\n\t\t\ttheme.fg(\"muted\", elapsed) +\n\t\t\t(showCost ? theme.fg(\"dim\", \" · \") + theme.bold(costStr) : \"\");\n\t}\n\n\t// Right cluster: turn delta, then the view switcher at the far edge. The\n\t// switcher is the first thing dropped when the terminal narrows; the turn\n\t// delta next; the stamp/count survive to the end.\n\tconst switcher = formatViewSwitcher(view);\n\tconst rightVariants: Array<{ plain: string; styled: string }> = [];\n\tif (turnPlain) {\n\t\trightVariants.push({\n\t\t\tplain: `${turnPlain} ${switcher.plain}`,\n\t\t\tstyled: `${turnText} ${switcher.styled}`,\n\t\t});\n\t\trightVariants.push({ plain: turnPlain, styled: turnText });\n\t} else {\n\t\trightVariants.push(switcher);\n\t}\n\n\tfor (const right of rightVariants) {\n\t\tif (visibleWidth(leftFullPlain) + 2 + visibleWidth(right.plain) <= width) {\n\t\t\tconst pad = Math.max(2, width - visibleWidth(leftFullPlain) - visibleWidth(right.plain));\n\t\t\treturn leftFull + \" \".repeat(pad) + right.styled;\n\t\t}\n\t\tif (visibleWidth(leftMinPlain) + 2 + visibleWidth(right.plain) <= width) {\n\t\t\tconst pad = Math.max(2, width - visibleWidth(leftMinPlain) - visibleWidth(right.plain));\n\t\t\treturn leftMin + \" \".repeat(pad) + right.styled;\n\t\t}\n\t}\n\tif (visibleWidth(leftFullPlain) <= width) return leftFull;\n\treturn truncateToWidth(leftMin, width, \"…\");\n}\n\nfunction formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\treturn `${(count / 1000000).toFixed(1)}M`;\n}\n\nfunction formatTaskLine(\n\ttask: Task,\n\twidth: number,\n\tframe: number,\n\tidColWidth: number,\n\toptions: { grouped?: boolean } = {},\n): string {\n\tconst isProgress = task.status === \"in_progress\";\n\tconst iconGlyph = isProgress\n\t\t? (SPINNER_FRAMES[frame] ?? TASK_STATUS_ICON.in_progress)\n\t\t: TASK_STATUS_ICON[task.status];\n\tconst icon = theme.fg(taskStatusColor(task.status), iconGlyph);\n\n\t// In grouped views the group header already carries the row's origin, so the\n\t// source glyph and tag are suppressed; the rows sit on a faint indent guide.\n\tconst grouped = options.grouped === true;\n\tconst indent = grouped ? theme.fg(\"borderMuted\", GROUP_INDENT_PLAIN) : \"\";\n\n\t// Source marker between the status icon and the id. Reserve the cell (blank) for\n\t// plain tasks so ids stay column-aligned whether or not a glyph is present.\n\tconst sourceGlyph = task.source ? TASK_SOURCE_GLYPH[task.source] : \" \";\n\tconst styledSource = !grouped && task.source ? theme.fg(\"dim\", sourceGlyph) : grouped ? \"\" : \" \";\n\n\t// Right-pad the id to the shared column width so titles line up across rows even\n\t// when ids differ in digit count (#1 vs #10). Padding is plain spaces inside the\n\t// dim styling, so it adds no visible color.\n\tconst idLabel = `#${task.id}`.padEnd(idColWidth);\n\t// Source tag prefixed to the title: the subagent mode (e.g. \"[explore]\") for\n\t// subagent rows, \"[MCP]\" for MCP rows. Drawn in accent so it reads as the row's\n\t// origin label, parallel to the chat's `Agent [explore]` / `MCP [server › tool]`.\n\tconst tag = grouped ? \"\" : task.source === \"mcp\" ? \"[MCP]\" : task.subagentMode ? `[${task.subagentMode}]` : \"\";\n\tconst styledTag = tag ? `${theme.fg(\"accent\", tag)} ` : \"\";\n\tconst title = task.title;\n\t// The id recedes (dim); the title carries the line. Done titles fade to muted\n\t// (settled work), pending dim (not started), active goes bold, failed turns red.\n\tconst styledId = theme.fg(\"dim\", idLabel);\n\tlet styledTitle: string;\n\tswitch (task.status) {\n\t\tcase \"done\":\n\t\t\tstyledTitle = theme.fg(\"muted\", title);\n\t\t\tbreak;\n\t\tcase \"pending\":\n\t\t\tstyledTitle = theme.fg(\"dim\", title);\n\t\t\tbreak;\n\t\tcase \"failed\":\n\t\t\tstyledTitle = theme.fg(\"error\", title);\n\t\t\tbreak;\n\t\tcase \"in_progress\":\n\t\t\tstyledTitle = theme.bold(title);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tstyledTitle = title;\n\t}\n\n\t// Right column: settled rows carry their audit stamp (tokens + elapsed); the\n\t// active row reads `running…`, pending rows read `queued`.\n\tlet rightPlain = \"\";\n\tlet rightStyled = \"\";\n\tif (task.status === \"done\" || task.status === \"failed\") {\n\t\tconst parts: string[] = [];\n\t\tlet tokenText = \"\";\n\t\tif (task.usage) {\n\t\t\tconst totalTok = task.usage.input + task.usage.output;\n\t\t\tif (totalTok > 0) tokenText = formatTokens(totalTok);\n\t\t}\n\t\tconst elapsed = formatDuration(taskElapsedSecs(task));\n\t\tif (tokenText) {\n\t\t\tparts.push(tokenText, elapsed);\n\t\t\trightStyled = theme.fg(\"muted\", tokenText) + theme.fg(\"dim\", ` · ${elapsed}`);\n\t\t} else {\n\t\t\tparts.push(elapsed);\n\t\t\trightStyled = theme.fg(\"dim\", elapsed);\n\t\t}\n\t\trightPlain = parts.join(\" · \");\n\t} else if (task.status === \"in_progress\") {\n\t\trightPlain = \"running…\";\n\t\trightStyled = theme.fg(\"warning\", rightPlain);\n\t} else if (task.status === \"pending\") {\n\t\trightPlain = \"queued\";\n\t\trightStyled = theme.fg(\"dim\", rightPlain);\n\t}\n\n\t// A warning note (e.g. inherited-model fallback, exhaustion skip) takes over the\n\t// right column as a ⚠ cue, replacing the usage/status stamp for that row.\n\tif (task.note) {\n\t\trightPlain = `⚠ ${task.note}`;\n\t\trightStyled = theme.fg(\"warning\", rightPlain);\n\t}\n\n\tconst rightWidth = rightPlain ? visibleWidth(rightPlain) + 1 : 0;\n\tconst leftWidth = Math.max(0, width - rightWidth);\n\n\t// truncateToWidth measures visible width (ANSI-aware), so the styled left can be\n\t// truncated against the full left budget directly. Subtracting the prefix here\n\t// (as a prior version did) truncated titles early and unevenly per id width.\n\tconst leftBody = grouped\n\t\t? `${indent}${icon} ${styledId} ${styledTitle}`\n\t\t: `${icon} ${styledSource} ${styledId} ${styledTag}${styledTitle}`;\n\tconst left = truncateToWidth(leftBody, leftWidth, \"…\");\n\n\tif (!rightPlain) return left;\n\n\tconst pad = Math.max(1, width - visibleWidth(left) - visibleWidth(rightPlain));\n\treturn left + \" \".repeat(pad) + rightStyled;\n}\n\n/** Fallback group metadata when a task's owner has no roster entry. */\nfunction defaultAgentMeta(id: string): TaskAgent {\n\treturn id === \"main\"\n\t\t? { id, name: \"main\", role: \"orchestrator\", kind: \"main\" }\n\t\t: { id, name: id, role: \"subagent\", kind: \"subagent\" };\n}\n\n/**\n * Partition the flat task list into owner groups. An explicit task.agent wins;\n * otherwise a subagent-sourced task falls into a generic \"subagent\" group and\n * everything else into \"main\". Group order is deterministic — main first, then\n * roster order, then stragglers — never reordered by status.\n */\nfunction groupTasks(\n\ttasks: readonly Task[],\n\tagents: readonly TaskAgent[],\n): Array<{ id: string; meta: TaskAgent; items: Task[] }> {\n\tconst meta = new Map<string, TaskAgent>(agents.map((a) => [a.id, a]));\n\tconst groups = new Map<string, Task[]>();\n\tfor (const task of tasks) {\n\t\tconst owner = task.agent ?? (task.source === \"subagent\" ? \"subagent\" : \"main\");\n\t\tconst items = groups.get(owner);\n\t\tif (items) items.push(task);\n\t\telse groups.set(owner, [task]);\n\t}\n\tconst order: string[] = [];\n\tif (groups.has(\"main\")) order.push(\"main\");\n\tfor (const agent of agents) {\n\t\tif (groups.has(agent.id) && !order.includes(agent.id)) order.push(agent.id);\n\t}\n\tfor (const id of groups.keys()) {\n\t\tif (!order.includes(id)) order.push(id);\n\t}\n\treturn order.map((id) => ({\n\t\tid,\n\t\tmeta: meta.get(id) ?? defaultAgentMeta(id),\n\t\titems: groups.get(id) ?? [],\n\t}));\n}\n\n/**\n * Group header for the grouped views: owner glyph + bold name + role, the\n * agent's lifecycle `[state]` tag, an optional handoff arrow (teams), then the\n * agent's own token/cost totals + done/total on the right. Mirrors the footer's\n * \"every number accounted for\" stance, but per agent.\n */\nfunction formatGroupHeader(meta: TaskAgent, items: readonly Task[], width: number): string {\n\tconst glyph = theme.fg(AGENT_GLYPH_COLOR[meta.kind], AGENT_GLYPH[meta.kind] ?? AGENT_GLYPH.subagent);\n\tconst name = theme.bold(meta.name);\n\t// Roles read as a dim \"· role\" suffix for spawned/team agents; the main\n\t// orchestrator's role sits brighter (muted), matching the design's .grp-role.\n\tconst role = meta.role\n\t\t? meta.kind === \"main\"\n\t\t\t? ` ${theme.fg(\"muted\", meta.role)}`\n\t\t\t: theme.fg(\"dim\", ` · ${meta.role}`)\n\t\t: \"\";\n\tconst state = meta.state ? ` ${theme.fg(AGENT_STATE_COLOR[meta.state] ?? \"dim\", `[${meta.state}]`)}` : \"\";\n\tconst handoff = meta.handoff ? ` ${theme.fg(\"dim\", meta.handoff)}` : \"\";\n\n\tconst done = items.filter((t) => t.status === \"done\").length;\n\tconst countPlain = `${done}/${items.length}`;\n\tconst count = theme.fg(\"muted\", `${done}`) + theme.fg(\"dim\", \"/\") + theme.fg(\"muted\", `${items.length}`);\n\tconst stats = meta.stats;\n\tlet rightPlain = countPlain;\n\tlet rightStyled = count;\n\tif (stats && (stats.input > 0 || stats.output > 0 || stats.cost > 0)) {\n\t\tconst statsPlain = `↑${formatTokens(stats.input)} ↓${formatTokens(stats.output)} · $${stats.cost.toFixed(3)}`;\n\t\trightPlain = `${statsPlain} ${countPlain}`;\n\t\trightStyled = `${theme.fg(\"dim\", statsPlain)} ${count}`;\n\t}\n\n\tconst rightWidth = visibleWidth(rightPlain) + 1;\n\tconst leftWidth = Math.max(0, width - rightWidth);\n\tconst left = truncateToWidth(`${glyph} ${name}${role}${state}${handoff}`, leftWidth, \"…\");\n\tconst pad = Math.max(1, width - visibleWidth(left) - visibleWidth(rightPlain));\n\treturn left + \" \".repeat(pad) + rightStyled;\n}\n\n/**\n * Task panel rendered just above the editor prompt.\n *\n * - A state-colored left rail groups the pane (working=warning, reviewed=success,\n * stopped=error) without drawing a box.\n * - A ledger header tops the list: a state stamp + deterministic progress bar +\n * done/total count on the left, the per-turn token/elapsed/cost delta on the right.\n * - Shows all tasks with all statuses (pending / in_progress / done / failed).\n * The active row animates a braille spinner; pending rows read `queued`.\n * - A single-cell source glyph (⚙ subagent / ⧉ MCP) sits before the id so the two\n * kinds of background work are distinguishable, and a text origin tag is shown\n * before the title: the subagent mode (e.g. \"[explore]\") or \"[MCP]\".\n * - Three views over the same list (cycled via app.tasks.cycleView, shown as a\n * `tasks · subagents · teams` switcher in the header): flat, grouped by owning\n * agent (subagents), or grouped by named role-agent with handoffs (teams).\n * Grouped rows drop their origin glyph/tag (the group header carries it) and\n * sit on a faint `│` indent guide.\n * - LIFO within the window: newest tasks appear at the bottom (closest to the prompt).\n * - Finished tasks carry their wall-clock cost and stay visible until the next\n * user message arrives (see taskStore.reset()), not the moment they finish.\n * - Collapses to zero lines when there are no tasks.\n */\nexport class TaskPanelComponent implements Component {\n\tprivate readonly ui: TUI | null;\n\tprivate frame = 0;\n\tprivate animationTimer: ReturnType<typeof setInterval> | null = null;\n\tprivate view: TaskPanelView = \"flat\";\n\n\tconstructor(ui?: TUI) {\n\t\tthis.ui = ui ?? null;\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached rendering state.\n\t}\n\n\tgetView(): TaskPanelView {\n\t\treturn this.view;\n\t}\n\n\tsetView(view: TaskPanelView): void {\n\t\tthis.view = view;\n\t\tthis.ui?.requestRender();\n\t}\n\n\t/** Advance to the next view lens (flat → subagents → teams → flat). */\n\tcycleView(): TaskPanelView {\n\t\tconst idx = VIEW_ORDER.indexOf(this.view);\n\t\tthis.view = VIEW_ORDER[(idx + 1) % VIEW_ORDER.length] ?? \"flat\";\n\t\tthis.ui?.requestRender();\n\t\treturn this.view;\n\t}\n\n\t/** Run the spinner timer only while a task is active, ticking re-renders. */\n\tprivate ensureAnimation(active: boolean): void {\n\t\tif (active && this.ui && !this.animationTimer) {\n\t\t\tthis.animationTimer = setInterval(() => {\n\t\t\t\tthis.frame = (this.frame + 1) % SPINNER_FRAMES.length;\n\t\t\t\tthis.ui?.requestRender();\n\t\t\t}, SPINNER_INTERVAL_MS);\n\t\t\tthis.animationTimer.unref?.();\n\t\t} else if (!active && this.animationTimer) {\n\t\t\tclearInterval(this.animationTimer);\n\t\t\tthis.animationTimer = null;\n\t\t\tthis.frame = 0;\n\t\t}\n\t}\n\n\t/** Stop the spinner timer. Call on teardown. */\n\tdispose(): void {\n\t\tif (this.animationTimer) {\n\t\t\tclearInterval(this.animationTimer);\n\t\t\tthis.animationTimer = null;\n\t\t}\n\t}\n\n\trender(width: number): string[] {\n\t\tconst tasks = taskStore.list();\n\t\tif (tasks.length === 0) {\n\t\t\tthis.ensureAnimation(false);\n\t\t\treturn [];\n\t\t}\n\n\t\tconst hasActive = tasks.some((t) => t.status === \"in_progress\");\n\t\tthis.ensureAnimation(hasActive);\n\n\t\tconst state = panelState(tasks);\n\t\tconst totalSecs = tasks.reduce((sum, t) => sum + taskElapsedSecs(t), 0);\n\t\tconst railColor = STATE_PRESENTATION[state].color;\n\t\tconst gutter = `${theme.fg(railColor, RAIL)} `;\n\t\tconst inner = Math.max(0, width - visibleWidth(RAIL) - 1);\n\n\t\t// Width of the id column, sized to the widest id on screen, so every title\n\t\t// starts at the same column regardless of digit count (#1 vs #10 vs #100).\n\t\tconst idColWidth = tasks.reduce((max, t) => Math.max(max, `#${t.id}`.length), 0);\n\n\t\tconst lines: string[] = [gutter + formatHeader(tasks, inner, state, totalSecs, this.view)];\n\t\tif (this.view === \"flat\") {\n\t\t\tfor (const task of tasks) {\n\t\t\t\tlines.push(gutter + formatTaskLine(task, inner, this.frame, idColWidth));\n\t\t\t}\n\t\t\treturn lines;\n\t\t}\n\n\t\t// Grouped lenses (subagents / teams): a header line per owning agent, its\n\t\t// tasks indented beneath on a faint guide. Hierarchy is carried by the\n\t\t// indent and owner glyph alone — no fills, no boxes.\n\t\tfor (const group of groupTasks(tasks, taskStore.agents())) {\n\t\t\tlines.push(gutter + formatGroupHeader(group.meta, group.items, inner));\n\t\t\tfor (const task of group.items) {\n\t\t\t\tlines.push(gutter + formatTaskLine(task, inner, this.frame, idColWidth, { grouped: true }));\n\t\t\t}\n\t\t}\n\t\treturn lines;\n\t}\n}\n"]}