pantheon-opencode 1.5.0-beta.9 → 1.5.1

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 (84) hide show
  1. package/AGENTS.md +14 -63
  2. package/CHANGELOG.md +326 -0
  3. package/README.md +77 -29
  4. package/README.pt-BR.md +5 -6
  5. package/commands/pantheon.md +1 -35
  6. package/package.json +2 -3
  7. package/plugin.json +1 -1
  8. package/pyproject.toml +3 -5
  9. package/scripts/doctor.mjs +260 -5
  10. package/scripts/install/plugin.mjs +9 -12
  11. package/scripts/mcp_persistence_server.py +1 -1
  12. package/scripts/sync-tui.mjs +8 -11
  13. package/src/agents/apollo.md +1 -0
  14. package/src/agents/gaia.md +1 -0
  15. package/src/agents/mnemosyne.md +11 -1
  16. package/src/agents/zeus.md +10 -52
  17. package/src/instructions/memory-protocol.instructions.md +6 -3
  18. package/src/instructions/zeus-anti-stall.instructions.md +2 -4
  19. package/src/instructions/zeus-council-synthesis.instructions.md +3 -28
  20. package/src/instructions/zeus-timeout-retry.instructions.md +3 -28
  21. package/src/mcp/mcp_persistence_server.py +1 -1
  22. package/src/mcp/memory_mcp_server.py +81 -27
  23. package/src/pantheon/background-job-board.ts +4 -4
  24. package/src/pantheon/{delegation-compaction.ts → compaction-context.ts} +10 -100
  25. package/src/pantheon/cost-command.ts +1 -2
  26. package/src/pantheon/delegation-enforce.ts +59 -14
  27. package/src/pantheon/goal-loop.ts +1 -1
  28. package/src/pantheon/goal-store.ts +1 -1
  29. package/src/pantheon/idle-continuation.ts +4 -6
  30. package/src/pantheon/model-command.ts +1 -2
  31. package/src/pantheon/native-task-status.ts +2 -2
  32. package/src/pantheon/presets.d.mts +0 -20
  33. package/src/pantheon/presets.mjs +0 -64
  34. package/src/pantheon/session-guard.ts +2 -2
  35. package/src/pantheon/session-id.ts +30 -0
  36. package/src/pantheon/task-result-guard.ts +2 -3
  37. package/src/pantheon/todo-enforcer.ts +3 -3
  38. package/src/pantheon/tool-context.ts +23 -0
  39. package/src/pantheon/v2-bridge.ts +1 -4
  40. package/src/plugin-v2.ts +8 -21
  41. package/src/plugin.ts +61 -245
  42. package/src/plugins/pantheon-hooks.ts +2 -2
  43. package/src/plugins/tui/dist/tui.d.ts +229 -103
  44. package/src/plugins/tui/dist/tui.d.ts.map +1 -1
  45. package/src/plugins/tui/dist/tui.js +495 -333
  46. package/src/plugins/tui/package-lock.json +5 -2
  47. package/src/plugins/tui/package.json +5 -2
  48. package/src/plugins/tui/src/index.tsx +710 -409
  49. package/src/routing.yml +0 -16
  50. package/src/skills/context-compression/SKILL.md +1 -1
  51. package/scripts/generate-preset-docs.mjs +0 -300
  52. package/scripts/generate-prompts.sh +0 -222
  53. package/scripts/hash_verify.py +0 -192
  54. package/scripts/install/component.mjs +0 -24
  55. package/scripts/install/migrate-config.mjs +0 -302
  56. package/scripts/test-context-compression.sh +0 -169
  57. package/src/mcp/toon_codec.py +0 -491
  58. package/src/pantheon/auto-wake.ts +0 -85
  59. package/src/pantheon/chat-reminders.ts +0 -9
  60. package/src/pantheon/comment-checker.ts +0 -48
  61. package/src/pantheon/compaction-assert.ts +0 -160
  62. package/src/pantheon/compaction-handoff.ts +0 -304
  63. package/src/pantheon/cost-tracker.ts +0 -108
  64. package/src/pantheon/delegate-manager.ts +0 -766
  65. package/src/pantheon/delegation-classifier.ts +0 -307
  66. package/src/pantheon/delegation-finalize.ts +0 -406
  67. package/src/pantheon/delegation-notify.ts +0 -212
  68. package/src/pantheon/delegation-toast.ts +0 -73
  69. package/src/pantheon/delegation.ts +0 -1337
  70. package/src/pantheon/dispatch-guard.ts +0 -109
  71. package/src/pantheon/native-probe.ts +0 -234
  72. package/src/pantheon/permission-eval.ts +0 -284
  73. package/src/pantheon/preemptive-compact.ts +0 -76
  74. package/src/pantheon/provider-capabilities.ts +0 -221
  75. package/src/pantheon/provider-stream-tracker.ts +0 -182
  76. package/src/pantheon/retry-policy.ts +0 -212
  77. package/src/pantheon/step-cap.ts +0 -110
  78. package/src/pantheon/token-opt.ts +0 -689
  79. package/src/pantheon/toon-codec.ts +0 -481
  80. package/src/pantheon/v2-events.ts +0 -148
  81. package/src/pantheon/v2-hooks.ts +0 -287
  82. package/src/pantheon/v2-tool-definitions.ts +0 -331
  83. package/src/pantheon/zeus-delegate-with-retry.ts +0 -406
  84. package/src/plugins/tui/dist/tui.tsx +0 -2853
@@ -5,7 +5,7 @@ type DelegationEntry = {
5
5
  alias: string;
6
6
  /** Parent session the job was launched from (dir name under .pantheon/delegations). */
7
7
  sessionID: string;
8
- /** Child session id (= board task id, from the `Task ID` header). The
8
+ /** Child session id (= task id, from the `Task ID` header). The
9
9
  * children channel always sets it from the child session itself; the md
10
10
  * channel parses it so child↔md matching works by taskID. */
11
11
  taskID?: string;
@@ -18,11 +18,11 @@ type DelegationEntry = {
18
18
  updatedAt: number | null;
19
19
  timedOut: boolean;
20
20
  description: string;
21
- /** True while the panel is waiting for pantheon_delegation_read. */
22
- read?: boolean;
23
21
  /** Internal provenance used to keep a finalized md report authoritative.
24
- * 'children-only' = a native task() child session with NO board report
25
- * (rendered with the distinct `[native]` tag); 'md' = board report wins. */
22
+ * 'children-only' = a native task() child session with NO delegate report
23
+ * (a native child row); 'md' = the child has a matching report;
24
+ * 'child' = the plain children channel; 'live' = the optimistic tool-event
25
+ * channel. */
26
26
  source?: 'child' | 'live' | 'md' | 'children-only';
27
27
  };
28
28
  /** Parse one delegation report md header into a structured entry.
@@ -43,19 +43,26 @@ declare function parseDelegationMarkdown(raw: string, fileAlias?: string, sessio
43
43
  declare function readDelegationEntries(dir: string): Promise<DelegationEntry[]>;
44
44
  /** Read delegation reports from EVERY session under
45
45
  * `<root>/.pantheon/delegations/<sessionID>/<alias>.md` — the panel's
46
- * HISTORY channel. Unlike the children/live channels it does NOT depend on a
47
- * resolved sessionID: with no focused session (null/placeholder), the panel
48
- * still shows the reports from all past sessions (running first, Finalized
49
- * desc — the sort applied by readDelegationEntries). Fail-open: a
50
- * missing/unreadable directory yields []. */
46
+ * ENRICHMENT source. The read is deliberately unfiltered (each entry carries
47
+ * its directory's sessionID, so a taskID match can enrich any channel);
48
+ * callers MUST scope the merged result with {@link filterDelegationsToSession}
49
+ * before rendering — only the active session's reports are shown. Entries are
50
+ * running first, Finalized desc (the sort applied by readDelegationEntries).
51
+ * Fail-open: a missing/unreadable directory yields []. */
51
52
  declare function readAllDelegationEntries(root: string): Promise<DelegationEntry[]>;
52
- /** Resolve the directory where the job board writes delegation md reports.
53
- * The board writes `.pantheon/delegations` RELATIVE to the server cwd,
54
- * which the TUI exposes as `TuiState.path.directory`. `project` does NOT
55
- * exist on `TuiState.path` (the old `state?.project ?? state?.worktree`
56
- * resolution was always undefined for the first term) and `worktree` is
57
- * `/` when there is no git (e.g. the sandbox test project) — a root of
58
- * `''` or `'/'` must fall back to `process.cwd()`. */
53
+ /** Resolve the PROJECT ROOT used by every pantheon file channel. `directory`
54
+ * wins over `worktree` (the old `resolveDelegationsDir` already did this);
55
+ * an absent/empty root or `/` (no git — e.g. the sandbox test project) falls
56
+ * back to cwd. Standardised here so the delegations md and the panel logger
57
+ * all read the SAME root (audit finding: the channels
58
+ * resolved the root independently). Pure — no I/O. */
59
+ declare function resolvePantheonRoot(state: {
60
+ directory?: string;
61
+ worktree?: string;
62
+ } | undefined, cwd?: string): string;
63
+ /** Resolve the directory where delegation md reports are written.
64
+ * The finalizer writes `.pantheon/delegations` RELATIVE to the server cwd,
65
+ * which the TUI exposes as `TuiState.path.directory`. */
59
66
  declare function resolveDelegationsDir(state: {
60
67
  directory?: string;
61
68
  worktree?: string;
@@ -74,17 +81,16 @@ declare function tuiLogPath(projectRoot: string): string;
74
81
  * mergeDelegationSources. */
75
82
  declare function compareDelegationEntries(a: DelegationEntry, b: DelegationEntry): number;
76
83
  /** Split the panel list: active jobs (running/retry, stale-marked) first,
77
- * then the most recent terminal reports, then the archived tail (paginated
78
- * in the View). Pure — so the history-only panel (no sessionID) is testable
84
+ * then the most recent terminal reports; the remaining tail is collapsed
85
+ * by the View into a single "… +N more" line. Pure — so the history-only panel (no sessionID) is testable
79
86
  * without the TUI runtime. */
80
87
  declare function splitDelegationList(all: readonly DelegationEntry[], maxRecent?: number, now?: number, staleThresholdMs?: number): {
81
88
  active: DelegationEntry[];
82
89
  recent: DelegationEntry[];
83
- archived: DelegationEntry[];
84
90
  };
85
- /** The list the panel actually renders (kept for the header count and
86
- * existing tests): active jobs first, then the most recent terminal
87
- * reports (capped) — the archived tail is rendered separately. Pure. */
91
+ /** Live-first window used by {@link ceilingDelegationList} (kept for the
92
+ * ceiling helper and existing tests): active jobs first, then the most
93
+ * recent terminal reports (capped). Pure. */
88
94
  declare function visibleDelegationList(all: readonly DelegationEntry[], maxTerminal?: number, now?: number, staleThresholdMs?: number): DelegationEntry[];
89
95
  /** Default stale-running threshold: 30 minutes. */
90
96
  declare const STALE_RUNNING_THRESHOLD_MS: number;
@@ -92,28 +98,74 @@ declare const STALE_RUNNING_THRESHOLD_MS: number;
92
98
  * considered stale. Combined with the stale-running threshold to produce the
93
99
  * display-only `stale-running` state. */
94
100
  declare const IDLE_SILENCE_MS: number;
101
+ /** Visual-only terminal retention windows. Reports remain on disk; these
102
+ * constants only control which rows enter the TUI window. */
103
+ declare const DELEGATION_DONE_RETENTION_MS: number;
104
+ declare const DELEGATION_FAILED_RETENTION_MS: number;
105
+ /** Grace window for an ABSENT child status. `api.state.session.status()` only
106
+ * carries the sessions that are currently ALIVE; every finished child of a
107
+ * long-lived session is missing from that map. A child with no status is
108
+ * therefore treated as terminal (done) — treating it as running made every
109
+ * historical child of the session show up as "active" (the "197 children,
110
+ * all active" regression). The one exception is a freshly-spawned child that
111
+ * has not been registered by the status API yet: within this window of its
112
+ * last activity (`time.updated`, falling back to `time.created`) it still
113
+ * reads as running. */
114
+ declare const DELEGATION_CHILD_STATUS_GRACE_MS: number;
115
+ /** Recency window for the children channel. `session.children` returns EVERY
116
+ * child the focused session ever spawned, so a long session accumulates
117
+ * hundreds of historical rows that would inflate the panel. A child whose
118
+ * last activity (its own time, or its matched report's Finalized timestamp)
119
+ * is older than this window is dropped BEFORE it can enter the list. 24h
120
+ * keeps the sidebar scoped to current work while still covering long
121
+ * delegations. A child with no timestamp at all is kept (fail-open). */
122
+ declare const DELEGATION_CHILDREN_RECENCY_MS: number;
123
+ /** Alias-less NATIVE task() live entries never receive a report alias (the
124
+ * task tool output carries none), so the 30s alias-less prune in
125
+ * mergeChildDelegationSources must not apply to them — 5 minutes covers a
126
+ * slow child listing while still bounding the live map. */
127
+ declare const NATIVE_LIVE_ALIASLESS_TTL_MS: number;
95
128
  /**
96
129
  * Mark a running entry as `stale-running` if it has been running longer than
97
130
  * the threshold AND has no recent activity (no `updatedAt` change in the last
98
- * `IDLE_SILENCE_MS`). This is DISPLAY-ONLY — the board state is unchanged.
131
+ * `IDLE_SILENCE_MS`). This is DISPLAY-ONLY — the persisted state is unchanged.
99
132
  *
100
133
  * A `stale-running` entry renders with a warning indicator but the underlying
101
134
  * delegation is still treated as running by the backend.
102
135
  */
103
136
  declare function markStaleIfRunning(entry: DelegationEntry, now: number, thresholdMs?: number): DelegationEntry;
104
- /** Compact elapsed-time label: "5m 12s", "1h 30m", "2d 4h" — ticks every
137
+ /** Compact elapsed-time label, single unit only: "12s"/"3m"/"1h"/"2d" — ticks every
105
138
  * second for running jobs. */
106
139
  declare function fmtElapsed(ms: number): string;
107
- /** Elapsed label for one entry: running → ticks `now - startedAt`, terminal
108
- * → fixed `updatedAt - startedAt` (em dash when no finalized timestamp). */
140
+ /** Elapsed label for one entry: an ACTIVE entry (running/retry/stale-running)
141
+ * ticks `now - startedAt`; a terminal one is fixed at
142
+ * `updatedAt - startedAt` (em dash when no finalized timestamp). */
109
143
  declare function delegationElapsed(entry: DelegationEntry, now: number): string;
110
144
  /** The activity labels shown by the animated row. Keeping this pure makes the
111
145
  * state machine testable without booting OpenCode's renderer. */
112
- type DelegationActivity = 'delegating' | 'working' | 'reading' | 'completed' | 'error' | 'cancelled';
146
+ type DelegationActivity = 'delegating' | 'working' | 'completed' | 'error' | 'cancelled';
113
147
  declare function delegationActivity(entry: DelegationEntry): DelegationActivity;
114
148
  declare function delegationActivityLabel(entry: DelegationEntry): string;
115
- /** Return a deterministic spinner frame. The View ticks this every 140ms. */
116
- declare function delegationSpinnerFrame(now: number): string;
149
+ /** Return the spinner glyph for the given frame counter. The frame counter
150
+ * is incremented every ~80ms by a dedicated animation timer, decoupled from
151
+ * the data poll cycle. Pure — no side effects. */
152
+ declare function delegationSpinnerFrame(frame: number): string;
153
+ /** Every state the row knows how to draw: the delegation lifecycle states
154
+ * derived from the children status + md reports (`completed`, `error`,
155
+ * `cancelled`, `startup_failed`, `startup_unknown`) plus the TUI-only
156
+ * states (`retry`, `stale-running`). Fase 1 deliberately omits speculative
157
+ * blocked/paused/scheduled/skipped. There is no `pending` display state: a
158
+ * pre-dispatch tool part maps to `running` in {@link reduceDelegationToolPart}. */
159
+ type DelegationDisplayState = DelegationEntry['state'];
160
+ /** Semantic tone mapped to the TUI theme at the row ({@link DelegationRow}).
161
+ * Kept separate + pure so the color channel is testable without booting the
162
+ * renderer. Every display state resolves to one of the three status colors;
163
+ * the row paints its whole content with it (see {@link DelegationRow}). */
164
+ type DelegationStateTone = 'warning' | 'error' | 'success';
165
+ /** Status → color mapping for the whole-row tone. Red = failure, green =
166
+ * terminal, yellow = in flight. The glyph remains a redundant channel so the
167
+ * state stays legible in monochrome. Display-only; no behavior change. */
168
+ declare function delegationStateTone(state: DelegationDisplayState): DelegationStateTone;
117
169
  type ToolActivity = {
118
170
  tool: string;
119
171
  summary: string;
@@ -163,36 +215,34 @@ type DelegationToolPart = {
163
215
  };
164
216
  };
165
217
  };
166
- /** One live delegation tracked in-memory, keyed by the delegate callID. */
218
+ /** One live delegation tracked in-memory, keyed by the task callID. */
167
219
  type LiveDelegationEntry = {
168
- /** Tool call id of the pantheon_delegate part (stable across events). */
220
+ /** Tool call id of the task part (stable across events). */
169
221
  callID: string;
170
222
  /** Part id (for message.part.removed cleanup). */
171
223
  partID: string;
172
224
  /** Parent session the delegation was launched from. */
173
225
  sessionID: string;
174
- tool: 'pantheon_delegate' | 'pantheon_delegation_read' | 'task';
175
- /** Agent name (from the delegate input args). */
226
+ tool: 'task';
227
+ /** Agent name (from the task input args). */
176
228
  agent: string;
177
229
  description: string;
178
- /** Known after the delegate tool completes (parsed from its output). */
230
+ /** Known once the task completes and its output carries a marker. */
179
231
  alias: string | null;
180
- /** Child session id (parsed from the delegate output). */
232
+ /** Child session id (parsed from the task output, when present). */
181
233
  taskID: string | null;
182
234
  state: 'running' | 'completed' | 'error' | 'cancelled';
183
235
  startedAt: number;
184
236
  updatedAt: number | null;
185
- /** True once a pantheon_delegation_read for this job has been observed. */
186
- read: boolean;
187
237
  };
188
238
  /** Result of parsing one tool part into lifecycle-relevant fields. */
189
239
  type ParsedDelegationToolPart = {
190
240
  callID: string;
191
241
  partID: string;
192
242
  sessionID: string;
193
- tool: 'pantheon_delegate' | 'pantheon_delegation_read' | 'task';
194
- /** null for read parts (no agent arg — the id targets an existing job). */
195
- agent: string | null;
243
+ tool: 'task';
244
+ /** Agent name from the task input args ('agent' fallback when absent). */
245
+ agent: string;
196
246
  description: string;
197
247
  status: 'pending' | 'running' | 'completed' | 'error';
198
248
  alias: string | null;
@@ -202,9 +252,8 @@ type ParsedDelegationToolPart = {
202
252
  };
203
253
  /** Extract the tool name + args from a `message.part.updated` part and
204
254
  * reduce it to what the panel needs. Returns null for anything that is
205
- * not a pantheon delegation tool part, the native `task` subagent tool
206
- * (same parentID === caller mechanism — its children render `[native]`),
207
- * or is missing its callID. */
255
+ * not the native `task` subagent tool (parentID === caller mechanism —
256
+ * its children render `nat:`), or is missing its callID. */
208
257
  declare function parseDelegationToolPart(part: DelegationToolPart, now?: number): ParsedDelegationToolPart | null;
209
258
  /** Apply one tool part to the live map. Returns true when the map changed.
210
259
  * Pure w.r.t. I/O — only mutates `map`. */
@@ -212,14 +261,14 @@ declare function reduceDelegationToolPart(map: Map<string, LiveDelegationEntry>,
212
261
  /** Remove a live entry by part id (message.part.removed) or call id.
213
262
  * Returns true when something was removed. */
214
263
  declare function removeDelegationEntry(map: Map<string, LiveDelegationEntry>, partIDOrCallID: string): boolean;
215
- /** Collect pantheon delegation + native task tool parts from a session's messages.
264
+ /** Collect native task tool parts from a session's messages.
216
265
  * Messages may carry their parts inline (duck-typed `msg.parts`); when
217
266
  * they don't, the optional `getParts(messageID)` callback is used (the TUI
218
267
  * SDK exposes `api.state.part(messageID)`). The native `task` tool spawns a
219
- * child session with parentID = caller — the same mechanism as
220
- * pantheon_delegate — so its parts feed the live-map as the native signal
221
- * (rows render `[native]` via the children channel). Pure w.r.t. I/O — used
222
- * by the mount re-scan to re-seed the live map after compaction/attach. */
268
+ * child session with parentID = caller, so its parts feed the live-map as
269
+ * the refresh signal (rows come from the children channel). Pure w.r.t.
270
+ * I/O — used by the mount re-scan to re-seed the live map after
271
+ * compaction/attach. */
223
272
  declare function collectDelegationToolParts(messages: readonly {
224
273
  id?: string;
225
274
  parts?: unknown[];
@@ -230,7 +279,7 @@ declare function collectDelegationToolParts(messages: readonly {
230
279
  * the map (0 on the second identical seed — idempotent, no extra bumps). */
231
280
  declare function seedLiveDelegationMap(map: Map<string, LiveDelegationEntry>, parts: readonly DelegationToolPart[], now?: number): number;
232
281
  /** Convert a live entry into the shared display shape. Alias falls back to
233
- * a `live-<callID>` prefix while the delegate tool has not completed yet. */
282
+ * a `live-<callID>` prefix while the task call has not completed yet. */
234
283
  declare function toDelegationEntry(live: LiveDelegationEntry): DelegationEntry;
235
284
  /** Combine the live channel with the md (historical) channel into one
236
285
  * display list. Dedupes by (sessionID, alias) — aliases are per-parent-
@@ -238,6 +287,23 @@ declare function toDelegationEntry(live: LiveDelegationEntry): DelegationEntry;
238
287
  * terminal md entry is authoritative over a live running entry for the
239
288
  * same job (it carries Finalized/timedOut/cancelled from finalize). */
240
289
  declare function mergeDelegationSources(live: readonly LiveDelegationEntry[], md: readonly DelegationEntry[]): DelegationEntry[];
290
+ /** Scope a fully-merged display list to the ACTIVE session only.
291
+ *
292
+ * The panel is session-scoped: rows from other sessions (md history under
293
+ * `.pantheon/delegations/<other-session>/`) and rows with no
294
+ * attributable session (empty sessionID) are DROPPED. The previous
295
+ * cross-session behavior rendered those rows and clicking them led to
296
+ * "Session not found" — there is no cross-session channel anymore.
297
+ *
298
+ * The md channel still feeds the merge UNFILTERED so it can ENRICH an
299
+ * active-session row with state/alias/agent (dedup by taskID), but it can
300
+ * never introduce a row for another session: this filter is applied
301
+ * ONCE, after every merge (children + live + md).
302
+ *
303
+ * Returns [] when no active session resolves (null/placeholder) — there is no
304
+ * scope to show. Native children always carry the active session id (stamped
305
+ * by `childrenToDelegationEntries`), so they survive the filter. Pure. */
306
+ declare function filterDelegationsToSession(entries: readonly DelegationEntry[], activeSessionID: string | null | undefined): DelegationEntry[];
241
307
  /** Server-aligned session id validity: opencode rejects anything not starting
242
308
  * with "ses" (SchemaError). This deliberately mirrors that exact contract —
243
309
  * nothing stricter, nothing looser — so a template placeholder ("{sessionID}"),
@@ -274,34 +340,35 @@ type TuiSessionSources = {
274
340
  * fetch (empty panel, zero errors). Pure — no I/O, no runtime required. */
275
341
  declare function resolveCurrentSessionID(sources: TuiSessionSources): string | null;
276
342
  /** THE single choke point for every `session.children` / session-API path.
277
- * Returns `{ path: { id } }` ONLY for a server-valid session id; returns
278
- * null for anything else (placeholder, empty, foreign id) so the caller
279
- * skips the call entirely instead of sending an unsubstituted placeholder
280
- * (the "%7BsessionID%7D" regression). Every session-API call site MUST go
281
- * through this function (enforced by the source-scan test in
282
- * tests/pantheon/tui-delegations.test.ts). */
343
+ * Returns the v2 SDK parameter shape `{ sessionID }` ONLY for a
344
+ * server-valid session id; returns null for anything else (placeholder,
345
+ * empty, foreign id) so the caller skips the call entirely instead of
346
+ * sending an unsubstituted placeholder (the "%7BsessionID%7D" regression).
347
+ * The TUI client is `@opencode-ai/sdk/v2`, whose session methods take a
348
+ * FLAT parameter object (`{ sessionID }`), NOT the v1 `{ path: { id } }`
349
+ * envelope — passing the v1 shape left the v2 `{sessionID}` URL template
350
+ * unsubstituted (`/session/%7BsessionID%7D/children`). Every session-API
351
+ * call site MUST go through this function (enforced by the source-scan
352
+ * test in tests/pantheon/tui-delegations.test.ts). */
283
353
  declare function safeSessionPath(id: unknown): {
284
- path: {
285
- id: string;
286
- };
354
+ sessionID: string;
287
355
  } | null;
288
- /** Build the `session.children` path ONLY from a validated session id.
356
+ /** Build the `session.children` parameters ONLY from a validated session id.
289
357
  * Delegates to {@link safeSessionPath} — the single choke point. Returns
290
- * null for null/invalid ids so the caller skips the fetch instead of
291
- * sending an unsubstituted placeholder (the "%7BsessionID%7D" regression). */
358
+ * the v2 SDK shape `{ sessionID }`; null for null/invalid ids so the caller
359
+ * skips the fetch instead of sending an unsubstituted placeholder (the
360
+ * "%7BsessionID%7D" regression). */
292
361
  declare function buildChildrenPath(id: string | null | undefined): {
293
- path: {
294
- id: string;
295
- };
362
+ sessionID: string;
296
363
  } | null;
297
364
  /** Duck-typed subset of a child Session (+ its live status type). */
298
365
  type ChildDelegationLike = {
299
- /** Child session id (= board task id). */
366
+ /** Child session id (= task id). */
300
367
  id: string;
301
368
  /** Session title — the delegate's description or prompt prefix. */
302
369
  title?: string;
303
370
  /** Agent name when the child session carries one (duck-typed; native
304
- * task() children may expose it, board reports always do). */
371
+ * task() children may expose it, md reports always do). */
305
372
  agent?: string;
306
373
  /** Status type from api.state.session.status: 'busy' | 'retry' | 'idle',
307
374
  * or undefined when the status API is unavailable. */
@@ -311,56 +378,115 @@ type ChildDelegationLike = {
311
378
  updated?: number;
312
379
  };
313
380
  };
314
- /** Map a child status type to a display state. busy/retry → running
315
- * (the child is actively working), idle → completed, unknown → running
316
- * (fail-open: a freshly-seen child is assumed active; the 1s poll + md
317
- * correct it as soon as terminal data exists). */
318
- declare function childStatusToState(status: string | undefined): 'running' | 'completed' | 'retry';
319
- /** Row tag for a delegation entry: `[native]` for native task() children
320
- * (source 'children-only' — no board report), `[pantheon:<alias>]` for board
321
- * rows ([pantheon:apo-1]) — the same tags the manager prints in
322
- * pantheon_delegation_list, so panel rows and CLI list lines match. The
323
- * panel renders the tag with a distinct style so native task() children are
324
- * visually separable from pantheon_delegate jobs. */
325
- declare function delegationTag(entry: DelegationEntry): string;
326
- /** Split a display list into native task() rows vs pantheon_delegate rows.
327
- * Native = source 'children-only' (no board report); everything else counts
328
- * as pantheon. Pure — powers the header breakdown + the hooks.log line. */
381
+ /** Map a child status type to a display state.
382
+ *
383
+ * Only `busy`/`retry` are EXPLICIT live states. `idle` is terminal and so is
384
+ * any other/unknown status — a status the panel does not recognise is not
385
+ * evidence of activity. An ABSENT status is the historical case: the status
386
+ * map (`api.state.session.status`) contains only currently-alive sessions, so
387
+ * every finished child of a long session is missing from it. Defaulting that
388
+ * to running made the whole session history render as "active" (the
389
+ * "197 children, all active" regression), so an absent status is terminal
390
+ * (done) UNLESS the child was active within `graceMs` — a just-spawned child
391
+ * that the status API has not registered yet must still render as running.
392
+ *
393
+ * @param status the `api.state.session.status(id)?.type`, or undefined
394
+ * @param time the child session time fields (`created`/`updated`)
395
+ * @param now current wall-clock ms (injectable for tests)
396
+ * @param graceMs recency window for the absent-status running assumption
397
+ * Pure — no I/O. */
398
+ declare function childStatusToState(status: string | undefined, time?: {
399
+ created?: number;
400
+ updated?: number;
401
+ }, now?: number, graceMs?: number): 'running' | 'completed' | 'retry';
402
+ /** Status-only row model: ONE glyph + ONE identity per row.
403
+ *
404
+ * A row is
405
+ * `{glyph} {alias:7} {elapsed:>5}` plus a muted description line. The short
406
+ * alias (`apo-1`) is the single identity; rows without a report alias show
407
+ * the agent instead (never both). */
408
+ type DelegationRowStatus = 'active' | 'done' | 'failed' | 'retry';
409
+ /** Map every FSM/display state to one of the 4 row kinds. reconciled never
410
+ * reaches the panel (the md parser drops it) but reads as done;
411
+ * cancelled reads as done; startup_failed reads as failed while
412
+ * startup_unknown (no error known) reads as retry. Pure. */
413
+ declare function delegationRowStatus(state: DelegationDisplayState): DelegationRowStatus;
414
+ /** The only 4 glyphs a row may show: animated active, done, failed, retry.
415
+ * Shape is redundant with color (never the only signal). Pure. */
416
+ declare const DELEGATION_ROW_GLYPHS: Readonly<Record<DelegationRowStatus, string>>;
417
+ declare function delegationRowGlyph(status: DelegationRowStatus): string;
418
+ /** Row marker (`<glyph> `) — the single state channel. `active` animates
419
+ * through the spinner for the given frame counter (incremented every ~80ms);
420
+ * every other kind is static. Pure. */
421
+ declare function delegationRowMarker(state: DelegationDisplayState, frame?: number): string;
422
+ /** ONE identity per row: the short report alias (`apo-1`); a row without a
423
+ * report alias (native task() child `native-task` / `native-<id>`, or an
424
+ * alias-less native live row `live-<callID>` kept as children-only) shows
425
+ * the agent instead — never alias + agent stacked. Pure. */
426
+ declare function delegationRowIdentity(entry: DelegationEntry): string;
427
+ /** Fixed alias cell, pad-right + hard-truncate to `width` (default 7) so
428
+ * identities line up across rows. Pure. */
429
+ declare const DELEGATION_ALIAS_WIDTH = 7;
430
+ declare function formatDelegationAlias(identity: string, width?: number): string;
431
+ /** Max description width on the row detail line — the old 180-char slice
432
+ * wrapped the sidebar; 44 keeps one readable line. */
433
+ declare const DELEGATION_DESCRIPTION_MAX = 44;
434
+ /** Truncate a description to `max` graphemes appending `…` when cut. Exact
435
+ * `max`-length text is left untouched. Grapheme-granular, so an emoji made of
436
+ * several code points is never split into mojibake at the boundary. Pure. */
437
+ declare function truncateDelegationDescription(text: string, max?: number): string;
438
+ /** Fixed elapsed-time box: right-aligned to `width` (default 5) so elapsed
439
+ * values line up across rows, e.g. ` 12s`. A label that already fills
440
+ * the box is returned untouched. Pure. */
441
+ declare const DELEGATION_ELAPSED_WIDTH = 5;
442
+ declare function formatDelegationElapsed(entry: DelegationEntry, now: number, width?: number): string;
443
+ /** Colored left half of a row line: `<marker><alias:7> ` — the state glyph
444
+ * plus the single identity, padded so the muted elapsed column aligns. The
445
+ * row renders this colored lead separately from the muted elapsed tail. Pure. */
446
+ declare function formatDelegationRowLead(entry: DelegationEntry, marker: string): string;
447
+ /** Visible-row ceiling for the panel: the remainder collapses into a single
448
+ * "… +N more" line. Live rows render first, so a running job is never hidden
449
+ * by the cap. */
450
+ declare const DELEGATION_VISIBLE_CEILING = 8;
451
+ /** Header summary: `(N active · M done)` plus `· K failed` only when K > 0.
452
+ * Active counts running/retry AND display-only stale-running (a stale row is
453
+ * still a live job — it must never read as done); failed counts
454
+ * error/startup_failed; cancelled reads as done. Pure. */
455
+ declare function formatDelegationHeader(entries: readonly DelegationEntry[]): string;
456
+ /** Cap the panel: live rows first, then most-recent retained terminal rows, at
457
+ * most `maxVisible` total. Hidden counts describe the retained render list,
458
+ * not expired history. */
459
+ declare function ceilingDelegationList(all: readonly DelegationEntry[], maxVisible?: number, now?: number): {
460
+ visible: DelegationEntry[];
461
+ hidden: number;
462
+ hiddenActive: number;
463
+ hiddenTerminal: number;
464
+ };
465
+ /** Split a display list into native task() rows vs pantheon report rows.
466
+ * Native = source 'children-only' (no delegate report); everything else counts
467
+ * as pantheon. Pure — powers the hooks.log line. */
329
468
  declare function countDelegationSources(entries: readonly DelegationEntry[]): {
330
469
  native: number;
331
470
  pantheon: number;
332
471
  total: number;
333
472
  };
334
473
  /** Diagnostic hooks.log line for a panel re-fetch, with the children
335
- * breakdown (pantheon = children WITH a board report, native = children
474
+ * breakdown (pantheon = children WITH a delegate report, native = children
336
475
  * WITHOUT one). Pure — the View logs the returned string verbatim. */
337
476
  declare function formatPanelLogLine(children: number, pantheon: number, native: number, md: number, events: number): string;
338
- /** Turn child sessions (PRIMARY) enriched with md reports into the display
339
- * list. One entry per child id (duplicates across re-fetches collapse).
340
- * The md report is matched by `Task ID` (== child.id) and supplies alias,
341
- * agent, description, terminal state and duration. A child without a
342
- * report still renders: description from its title, agent from the child
343
- * itself (fallback 'agent'), state derived from its status, startedAt from
344
- * time.created. A report-less child is a NATIVE task() child (every
345
- * child of the current session — pantheon_delegate OR the native `task()`
346
- * tool — carries parentID = caller), so it gets source 'children-only',
347
- * the internal alias 'native-task' and the `[native]` tag instead of a
348
- * board alias. The 'task nativa' description fallback keeps the row
349
- * non-empty when the child carries no title.
350
- * Terminal md state wins over the derived state; a running md defers to
351
- * the child's live status. A running child is NEVER archived — it always
352
- * lands in the active split (splitDelegationList active = running/retry).
353
- * Sorted running-first (compareDelegationEntries).
354
- * Pure — no I/O. */
355
- declare function childrenToDelegationEntries(children: readonly ChildDelegationLike[] | undefined, md: readonly DelegationEntry[], now?: number): DelegationEntry[];
477
+ declare function childrenToDelegationEntries(children: readonly ChildDelegationLike[] | undefined, md: readonly DelegationEntry[], now?: number, parentSessionID?: string): DelegationEntry[];
356
478
  /** Navigate the TUI to a child session (click/Enter on a delegation row).
357
479
  * Returns false when the route API is unavailable or the target id is
358
480
  * missing/placeholder — the row stays inert instead of crashing. Only a
359
481
  * server-valid session id ("ses...") ever reaches the router, so an
360
482
  * unsubstituted "{sessionID}" placeholder can never be routed. */
361
483
  declare function navigateToDelegationSession(route: {
362
- navigate?: (name: string, params?: Record<string, unknown>) => void;
484
+ navigate?: (name: string, params?: Record<string, unknown>) => void | PromiseLike<void>;
363
485
  } | undefined, taskID: string | undefined): boolean;
486
+ /** Build the mouse handler used by each delegation row. */
487
+ declare function createDelegationRowOpenHandler(route: {
488
+ navigate?: (name: string, params?: Record<string, unknown>) => void | PromiseLike<void>;
489
+ } | undefined, taskID: string | undefined): () => void;
364
490
  /** Plugin-level live delegation store shared with the event subscriptions
365
491
  * in `tui()`: the map of live entries + a version signal bumped on every
366
492
  * mutation. The View subscribes to the version (in an effect) to refresh the
@@ -377,5 +503,5 @@ declare const plugin: TuiPluginModule & {
377
503
  setup: () => Promise<void>;
378
504
  };
379
505
  //#endregion
380
- export { ChildDelegationLike, DelegationActivity, DelegationEntry, DelegationToolPart, IDLE_SILENCE_MS, LiveDelegationEntry, LiveDelegationStore, ParsedDelegationToolPart, STALE_RUNNING_THRESHOLD_MS, ToolActivity, TuiSessionSources, buildChildrenPath, childStatusToState, childrenToDelegationEntries, collectDelegationToolParts, compareDelegationEntries, countDelegationSources, plugin as default, delegationActivity, delegationActivityLabel, delegationElapsed, delegationSpinnerFrame, delegationTag, extractToolActivity, fmtElapsed, formatPanelLogLine, isValidSessionId, latestToolActivityFor, markStaleIfRunning, mergeChildDelegationSources, mergeDelegationSources, navigateToDelegationSession, panelLogDir, parseDelegationMarkdown, parseDelegationToolPart, readAllDelegationEntries, readDelegationEntries, reduceDelegationToolPart, removeDelegationEntry, resolveCurrentSessionID, resolveDelegationsDir, safeSessionPath, seedLiveDelegationMap, splitDelegationList, toDelegationEntry, trackToolActivity, tuiLogPath, visibleDelegationList };
506
+ export { ChildDelegationLike, DELEGATION_ALIAS_WIDTH, DELEGATION_CHILDREN_RECENCY_MS, DELEGATION_CHILD_STATUS_GRACE_MS, DELEGATION_DESCRIPTION_MAX, DELEGATION_DONE_RETENTION_MS, DELEGATION_ELAPSED_WIDTH, DELEGATION_FAILED_RETENTION_MS, DELEGATION_ROW_GLYPHS, DELEGATION_VISIBLE_CEILING, DelegationActivity, DelegationDisplayState, DelegationEntry, DelegationRowStatus, DelegationStateTone, DelegationToolPart, IDLE_SILENCE_MS, LiveDelegationEntry, LiveDelegationStore, NATIVE_LIVE_ALIASLESS_TTL_MS, ParsedDelegationToolPart, STALE_RUNNING_THRESHOLD_MS, ToolActivity, TuiSessionSources, buildChildrenPath, ceilingDelegationList, childStatusToState, childrenToDelegationEntries, collectDelegationToolParts, compareDelegationEntries, countDelegationSources, createDelegationRowOpenHandler, plugin as default, delegationActivity, delegationActivityLabel, delegationElapsed, delegationRowGlyph, delegationRowIdentity, delegationRowMarker, delegationRowStatus, delegationSpinnerFrame, delegationStateTone, extractToolActivity, filterDelegationsToSession, fmtElapsed, formatDelegationAlias, formatDelegationElapsed, formatDelegationHeader, formatDelegationRowLead, formatPanelLogLine, isValidSessionId, latestToolActivityFor, markStaleIfRunning, mergeChildDelegationSources, mergeDelegationSources, navigateToDelegationSession, panelLogDir, parseDelegationMarkdown, parseDelegationToolPart, readAllDelegationEntries, readDelegationEntries, reduceDelegationToolPart, removeDelegationEntry, resolveCurrentSessionID, resolveDelegationsDir, resolvePantheonRoot, safeSessionPath, seedLiveDelegationMap, splitDelegationList, toDelegationEntry, trackToolActivity, truncateDelegationDescription, tuiLogPath, visibleDelegationList };
381
507
  //# sourceMappingURL=tui.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tui.d.ts","names":[],"sources":["../src/index.tsx"],"mappings":";;KAu7BY;;EAEV;;EAEA;;;;EAIA;;EAEA;EACA;;EAUA;;EAEA;EACA;EACA;;EAEA;;;;EAIA;;;;;;;;;;;iBAsBc,wBACd,aACA,oBACA,qBACC;;;;;;iBAkHmB,sBAAsB,cAAc,QAAQ;;;;;;;;iBAwC5C,yBAAyB,eAAe,QAAQ;;;;;;;;iBAWtD,sBACd;EAAS;EAAoB;eAC7B;;;;;;iBAYc,YAAY;;;iBAMZ,WAAW;;;;iBAOX,yBAAyB,GAAG,iBAAiB,GAAG;;;;;iBAahD,oBACd,cAAc,mBACd,oBACA,cACA;EACG,QAAQ;EAAmB,QAAQ;EAAmB,UAAU;;;;;iBAerD,sBACd,cAAc,mBACd,sBACA,cACA,4BACC;;cAMU;;;;cAKA;;;;;;;;;iBAUG,mBACd,OAAO,iBACP,aACA,uBACC;;;iBAWa,WAAW;;;iBAcX,kBAAkB,OAAO,iBAAiB;;;KAQ9C;iBAQI,mBAAmB,OAAO,kBAAkB;iBAc5C,wBAAwB,OAAO;;iBAqB/B,uBAAuB;KAY3B;EACV;EACA;EACA;;;;;iBA0Bc,oBACd;EACE;EACA;EACA;EACA;IAAU;IAAiB,QAAQ;;GAErC;EACG;EAAmB,UAAU;;;iBAYlB,kBACd,KAAK,YAAY,eACjB,mBACA,UAAU;;iBAWI,sBACd,KAAK,YAAY,eACjB,+BACA,cACA,iBACC;;;;;;;iBAca,4BACd,mBAAmB,mBACnB,eAAe,wBACd;;KAqFS;EACV;EACA;EACA;EACA;EACA;EACA;IACE;IACA,QAAQ;IACR;IACA;IACA;MAAS;MAAgB;;;;;KAKjB;;EAEV;;EAEA;;EAEA;EACA;;EAEA;EACA;;EAEA;;EAEA;EACA;EACA;EACA;;EAEA;;;KAIU;EACV;EACA;EACA;EACA;;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;;;;;;;iBAec,wBACd,MAAM,oBACN,eACC;;;iBAgGa,yBACd,KAAK,YAAY,sBACjB,MAAM,oBACN;;;iBAiGc,sBACd,KAAK,YAAY,sBACjB;;;;;;;;;iBAoBc,2BACd;EAAqB;EAAa;iBAClC,YAAY,uDACX;;;;;iBA6Ba,sBACd,KAAK,YAAY,sBACjB,gBAAgB,sBAChB;;;iBAWc,kBAAkB,MAAM,sBAAsB;;;;;;iBAqB9C,uBACd,eAAe,uBACf,aAAa,oBACZ;;;;;;;;;iBA8Da,iBAAiB,cAAc;;;;KAOnC;;EAEV;EACA;;IAEE;MAAU;;;IAEV;MAAU;QAAY;QAAe,SAAS;;;;;;;;;;iBASlC,wBAAwB,SAAS;;;;;;;;iBAmBjC,gBAAgB;EAAgB;IAAQ;;;;;;;iBASxC,kBAAkB;EAAkC;IAAQ;;;;KAKhE;;EAEV;;EAEA;;;EAGA;;;EAGA;EACA;IACE;IACA;;;;;;;iBAQY,mBAAmB;;;;;;;iBAYnB,cAAc,OAAO;;;;iBAOrB,uBAAuB,kBAAkB;EACvD;EACA;EACA;;;;;iBAUc,mBACd,kBACA,kBACA,gBACA,YACA;;;;;;;;;;;;;;;;;;iBAsBc,4BACd,mBAAmB,mCACnB,aAAa,mBACb,eACC;;;;;;iBA4Ca,4BACd;EAAS,YAAY,cAAc,SAAS;eAC5C;;;;;KAiMU;EACV,KAAK,YAAY;;EAEjB;;EAEA;;cAugBI,QAAQ;EAAoB;EAAY,aAAa"}
1
+ {"version":3,"file":"tui.d.ts","names":[],"sources":["../src/index.tsx"],"mappings":";;KA07BY;;EAEV;;EAEA;;;;EAIA;;EAEA;EACA;;EAUA;;EAEA;EACA;EACA;;;;;;EAUA;;;;;;;;;;;iBAsBc,wBACd,aACA,oBACA,qBACC;;;;;;iBAkHmB,sBAAsB,cAAc,QAAQ;;;;;;;;;iBAyC5C,yBAAyB,eAAe,QAAQ;;;;;;;iBAUtD,oBACd;EAAS;EAAoB;eAC7B;;;;iBAUc,sBACd;EAAS;EAAoB;eAC7B;;;;;;iBAUc,YAAY;;;iBAMZ,WAAW;;;;iBAOX,yBAAyB,GAAG,iBAAiB,GAAG;;;;;iBAahD,oBACd,cAAc,mBACd,oBACA,cACA;EACG,QAAQ;EAAmB,QAAQ;;;;;iBAmCxB,sBACd,cAAc,mBACd,sBACA,cACA,4BACC;;cAMU;;;;cAKA;;;cAIA;cACA;;;;;;;;;;cAWA;;;;;;;;cASA;;;;;cAMA;;;;;;;;;iBAUG,mBACd,OAAO,iBACP,aACA,uBACC;;;iBAWa,WAAW;;;;iBAeX,kBAAkB,OAAO,iBAAiB;;;KAQ9C;iBAEI,mBAAmB,OAAO,kBAAkB;iBAa5C,wBAAwB,OAAO;;;;iBAqB/B,uBAAuB;;;;;;;KAW3B,yBAAyB;;;;;KAMzB;;;;iBAKI,oBAAoB,OAAO,yBAAyB;KAuBxD;EACV;EACA;EACA;;;;;iBAsBc,oBACd;EACE;EACA;EACA;EACA;IAAU;IAAiB,QAAQ;;GAErC;EACG;EAAmB,UAAU;;;iBAYlB,kBACd,KAAK,YAAY,eACjB,mBACA,UAAU;;iBAWI,sBACd,KAAK,YAAY,eACjB,+BACA,cACA,iBACC;;;;;;;iBAca,4BACd,mBAAmB,mBACnB,eAAe,wBACd;;KAiGS;EACV;EACA;EACA;EACA;EACA;EACA;IACE;IACA,QAAQ;IACR;IACA;IACA;MAAS;MAAgB;;;;;KAKjB;;EAEV;;EAEA;;EAEA;EACA;;EAEA;EACA;;EAEA;;EAEA;EACA;EACA;EACA;;;KAIU;EACV;EACA;EACA;EACA;;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;;;;;;iBAYc,wBACd,MAAM,oBACN,eACC;;;iBAsDa,yBACd,KAAK,YAAY,sBACjB,MAAM,oBACN;;;iBAwEc,sBACd,KAAK,YAAY,sBACjB;;;;;;;;;iBAoBc,2BACd;EAAqB;EAAa;iBAClC,YAAY,uDACX;;;;;iBAwBa,sBACd,KAAK,YAAY,sBACjB,gBAAgB,sBAChB;;;iBAWc,kBAAkB,MAAM,sBAAsB;;;;;;iBAoB9C,uBACd,eAAe,uBACf,aAAa,oBACZ;;;;;;;;;;;;;;;;;iBA0Ca,2BACd,kBAAkB,mBAClB,6CACC;;;;;;;;;iBAyCa,iBAAiB,cAAc;;;;KAOnC;;EAEV;EACA;;IAEE;MAAU;;;IAEV;MAAU;QAAY;QAAe,SAAS;;;;;;;;;;iBASlC,wBAAwB,SAAS;;;;;;;;;;;;iBAuBjC,gBAAgB;EAAgB;;;;;;;iBAUhC,kBAAkB;EAAkC;;;KAKxD;;EAEV;;EAEA;;;EAGA;;;EAGA;EACA;IACE;IACA;;;;;;;;;;;;;;;;;;;;iBAqBY,mBACd,4BACA;EAAS;EAAkB;GAC3B,cACA;;;;;;;KA6BU;;;;;iBAMI,oBAAoB,OAAO,yBAAyB;;;cAmBvD,uBAAuB,SAAS,OAAO;iBAOpC,mBAAmB,QAAQ;;;;iBAO3B,oBAAoB,OAAO,wBAAwB;;;;;iBAUnD,sBAAsB,OAAO;;;cAShC;iBAEG,sBAAsB,kBAAkB;;;cAO3C;;;;iBAoBG,8BACd,cACA;;;;cAUW;iBAEG,wBACd,OAAO,iBACP,aACA;;;;iBAUc,wBAAwB,OAAO,iBAAiB;;;;cAYnD;;;;;iBAMG,uBAAuB,kBAAkB;;;;iBAyBzC,sBACd,cAAc,mBACd,qBACA;EACG,SAAS;EAAmB;EAAgB;EAAsB;;;;;iBAevD,uBAAuB,kBAAkB;EACvD;EACA;EACA;;;;;iBAUc,mBACd,kBACA,kBACA,gBACA,YACA;iBA8Cc,4BACd,mBAAmB,mCACnB,aAAa,mBACb,cACA,2BACC;;;;;;iBA4Da,4BACd;EAEM,YAAY,cAAc,SAAS,mCAAmC;eAG5E;;iBAmBc,+BACd;EAEM,YAAY,cAAc,SAAS,mCAAmC;eAG5E;;;;;KAwLU;EACV,KAAK,YAAY;;EAEjB;;EAEA;;cAsfI,QAAQ;EAAoB;EAAY,aAAa"}