@ai-setting/roy-plugin-task-show 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,22 +1,42 @@
1
1
  # roy-plugin-task-show
2
2
 
3
3
  > A [roy-agent](https://example/roy-agent) plugin that visualizes the tool-call
4
- > chain of every task on a local web service and injects the visualization URL
5
- > back into the task's tool output.
4
+ > chain of every task on a local web service with **real-time Server-Sent
5
+ > Events** for live page updates.
6
6
 
7
7
  ## What it does
8
8
 
9
9
  When loaded into a `roy-agent` host, this plugin:
10
10
 
11
- 1. Listens to the `tool:after.execute`, `task:after.complete` (preferred) and `task:after.update` (legacy fallback) hook points.
11
+ 1. Listens to the `tool:before.execute`, `tool:after.execute`,
12
+ `task:before.create`, `task:after.create`, `task:after.complete`
13
+ (preferred) and `task:after.update` (legacy fallback) hook points.
12
14
  2. Records every tool invocation (tool name, args, result preview, duration,
13
15
  success flag, timestamp) into an in-memory collector keyed by task id.
14
- 3. When a task transitions to a terminal status (`completed` / `failed` / `timeout` / `cancelled` / `timeout`), mutates the **last** tool result to append a markdown link to the visualization page. (Status filtering is done internally — no plugin-author filtering required.)
16
+ 3. **Pushes every state change over a Server-Sent Events stream** served by
17
+ the plugin's local HTTP service. The frontend subscribes to
18
+ `GET /api/events` and re-renders the page within milliseconds of:
19
+ - a new task being created (`task.created`)
20
+ - a tool call being recorded (`tool.recorded`)
21
+ - a task status changing (`task.updated`)
22
+ - a task transitioning to a terminal status (`task.completed`)
15
23
  4. Serves the visualization on a local HTTP server (default
16
- `http://127.0.0.1:7788/`).
17
-
18
- The result: every task in the chat gets a 🔗 link the user can click to open
19
- a mermaid-powered flow chart showing exactly how the agent solved it.
24
+ `http://127.0.0.1:7788/`) — index page, per-task detail page, and the
25
+ `/api/events` SSE stream.
26
+
27
+ The result: every task gets a clickable link the user can open in a
28
+ browser. Once open, the page **stays in sync** with the running agent
29
+ loop — tool calls appear as they happen, status badges update in
30
+ real-time, and the progress bar grows with each call. No manual refresh
31
+ needed.
32
+
33
+ > **v0.5.0+** — the plugin no longer uses `env.notify`. The previous
34
+ > `env.notify({type:"visualization_ready"})` mechanism has been replaced
35
+ > with an in-process event bus + SSE stream. This makes the plugin work
36
+ > with **any** roy-agent host (no NotificationChannel required) and gives
37
+ > the frontend proper incremental updates instead of just a one-shot URL
38
+ > at the end. See [SSE Migration (v0.5.0+)](#sse-migration-v050) for the
39
+ > migration guide.
20
40
 
21
41
  ## Repository layout
22
42
 
@@ -32,17 +52,20 @@ roy-plugin-task-show/
32
52
  │ └── app.js
33
53
  ├── src/
34
54
  │ ├── index.ts ← public API (re-exports)
35
- │ ├── types.ts ← shared types & default config
55
+ │ ├── types.ts ← shared types & default config + TaskEvent
36
56
  │ ├── collector.ts ← in-memory tool-call collector
37
- │ ├── server.ts ← tiny standalone Node http server (no deps)
38
- │ ├── url-injector.ts tool-result mutation helpers
57
+ │ ├── server.ts ← tiny standalone Node http server + SSE endpoint
58
+ │ ├── event-bus.ts SSE event bus (broadcast / subscribe / keep-alive)
59
+ │ ├── url-injector.ts ← URL builders (no more mutation helpers)
39
60
  │ ├── plugin.ts ← the TaskShowPlugin class (BasePlugin-compatible)
40
61
  │ └── core-stub.d.ts ← type stub for the optional core peer dep
41
62
  ├── test/
42
63
  │ ├── collector.test.ts
43
64
  │ ├── url-injector.test.ts
44
- │ ├── server.test.ts
45
- └── plugin.test.ts
65
+ │ ├── server.test.ts ← includes SSE endpoint test
66
+ ├── plugin.test.ts ← covers all six hooks + broadcast assertions
67
+ │ ├── plugin-sse.test.ts ← dedicated SSE event-bus consumer tests
68
+ │ └── integration.test.ts ← full lifecycle through HTTP + SSE
46
69
  └── scripts/
47
70
  ├── verify-service.ts ← end-to-end check (port 7788 + curl pages)
48
71
  └── run-demo.ts ← demo that simulates a real task lifecycle
@@ -120,77 +143,266 @@ await taskShow.dispose();
120
143
 
121
144
  The plugin registers hooks for:
122
145
 
123
- - `tool:after.execute` — collects every tool call (priority 50).
124
- - `task:after.complete` — PREFERRED freezes the session and injects
125
- the visualization URL on terminal status transition. Available on
126
- `roy-agent` versions >= 2026-07-10 (priority 60).
127
- - `task:after.update` LEGACY fallback for hosts predating the new
128
- hook point. The handler also dedupes against any `task:after.complete`
129
- that fires simultaneously, so on a modern host the URL is injected
130
- exactly once (priority 60).
146
+ | Hook point | Priority | Purpose |
147
+ | ---------------------- | -------- | ------------------------------------------------------------------------ |
148
+ | `tool:before.execute` | 40 | Records per-call start timestamp for accurate `durationMs` |
149
+ | `tool:after.execute` | 50 | Records the call into the collector and broadcasts `tool.recorded` |
150
+ | `task:before.create` | 55 | Mints a fresh `TaskSession` and broadcasts `task.created` |
151
+ | `task:after.create` | 55 | Same as above (fallback for hosts that only emit `task:after.create`) |
152
+ | `task:after.complete` | 60 | **PREFERRED** broadcasts `task.completed` on terminal transition |
153
+ | `task:after.update` | 60 | Legacy fallback; broadcasts `task.updated` or `task.completed` |
154
+
155
+ When both `task:after.complete` AND `task:after.update` fire (newer
156
+ hosts do this on completion), the handler dedupes — at most one
157
+ `task.completed` event per task.
131
158
 
132
159
  ## Configuration
133
160
 
134
161
  All config lives in `plugin.json` (and is overridable at construction time):
135
162
 
136
- | key | type | default | description |
137
- | ------------------- | -------- | ----------- | -------------------------------------------------------- |
138
- | `port` | number | `7788` | HTTP port for the visualization service. |
139
- | `host` | string | `127.0.0.1` | HTTP host. Use `0.0.0.0` to allow LAN access. |
140
- | `autoStart` | boolean | `true` | Start the HTTP server when `init()` is called. |
141
- | `maxStoredTasks` | number | `50` | Max sessions kept in memory. Older ones are evicted. |
142
- | `urlInjectEnabled` | boolean | `true` | Append the visualization URL to the last tool result. |
143
- | `publicDir` | string | `public` | Directory holding the static frontend. |
163
+ | key | type | default | description |
164
+ | ---------------- | -------- | ----------- | -------------------------------------------------------- |
165
+ | `port` | number | `7788` | HTTP port for the visualization service. |
166
+ | `host` | string | `127.0.0.1` | HTTP host. Use `0.0.0.0` to allow LAN access. |
167
+ | `autoStart` | boolean | `true` | Start the HTTP server when `init()` is called. |
168
+ | `maxStoredTasks` | number | `50` | Max sessions kept in memory. Older ones are evicted. |
169
+ | `publicDir` | string | `public` | Directory holding the static frontend. |
170
+
171
+ > **v0.5.0**: the previous `urlInjectEnabled` config was removed. The
172
+ > plugin no longer mutates tool result output.
144
173
 
145
174
  Override at construction time:
146
175
 
147
176
  ```ts
148
177
  createTaskShowPlugin({
149
178
  port: 9000,
150
- urlInjectEnabled: false,
179
+ maxStoredTasks: 100,
151
180
  });
152
181
  ```
153
182
 
154
183
  ## HTTP API
155
184
 
156
- | route | description |
157
- | --------------------------- | ------------------------------------------------------ |
158
- | `GET /` | Index of recent task sessions. |
159
- | `GET /task/:taskId` | Per-task page with mermaid flow + tool-call table. |
160
- | `GET /api/sessions` | JSON list of all sessions (newest first). |
161
- | `GET /api/sessions/:taskId` | JSON detail of one session (full payload). |
162
- | `GET /static/*` | Static frontend assets (`style.css`, `app.js`). |
185
+ | route | description |
186
+ | --------------------------- | ------------------------------------------------------------ |
187
+ | `GET /` | Index of recent task sessions. |
188
+ | `GET /task/:taskId` | Per-task page with mermaid flow + tool-call table. |
189
+ | `GET /api/sessions` | JSON list of all sessions (newest first). |
190
+ | `GET /api/sessions/:taskId` | JSON detail of one session (full payload). |
191
+ | **`GET /api/events`** | **SSE stream** see [Server-Sent Events](#server-sent-events). |
192
+ | `GET /static/*` | Static frontend assets (`style.css`, `app.js`). |
163
193
 
164
194
  If the configured `port` is already in use, the service falls back to an OS
165
195
  assigned port and logs the actual URL to stderr.
166
196
 
167
- ## Tool result injection format
197
+ ## Server-Sent Events
198
+
199
+ The local HTTP service exposes `GET /api/events` as a standard Server-Sent
200
+ Events stream. Every connected client receives:
201
+
202
+ 1. An initial `snapshot` frame carrying the full list of currently-known
203
+ sessions — useful for instant render on connect / reconnect.
204
+ 2. A `task.created` frame whenever a new task is opened
205
+ (`task:before.create` or `task:after.create`).
206
+ 3. A `tool.recorded` frame after each tool call
207
+ (`tool:after.execute`).
208
+ 4. A `task.updated` frame on any non-terminal status transition
209
+ (`task:after.update` with `running` / `paused` / etc.).
210
+ 5. A `task.completed` frame on terminal transition
211
+ (`task:after.complete` or `task:after.update` with terminal status).
168
212
 
169
- When a task reaches a terminal status, the plugin appends a banner to the
170
- **last** tool result that was recorded for that task:
213
+ A 15-second keep-alive comment (`:keep-alive`) prevents reverse proxies /
214
+ load balancers from dropping the connection.
171
215
 
172
- ```text
173
- …original tool output…
216
+ ### Event shape
174
217
 
175
- ---
176
- 📊 **可视化工具调用链路**: <http://127.0.0.1:7788/task/1234>
177
- (浏览器访问即可查看 mermaid 流程图:每个工具调用、参数、结果摘要、耗时)
218
+ Every event has the same envelope:
219
+
220
+ ```ts
221
+ interface TaskEvent<T> {
222
+ type: "task.created" | "task.updated" | "task.completed" | "tool.recorded";
223
+ taskId: number;
224
+ timestamp: number; // Unix ms
225
+ pluginVersion: string; // "0.5.0"
226
+ data: T;
227
+ }
178
228
  ```
179
229
 
180
- It also sets `result.metadata.task_visualization_url` so chat clients that
181
- prefer structured fields can render the link from there.
230
+ The `data` payload differs per `type`:
231
+
232
+ ```ts
233
+ // task.created
234
+ data: { session: TaskSession }
235
+
236
+ // task.updated
237
+ data: { session: TaskSession, newStatus: TaskStatus, previousStatus?: TaskStatus }
238
+
239
+ // task.completed
240
+ data: { session: TaskSession, terminalStatus: TaskStatus }
241
+
242
+ // tool.recorded
243
+ data: { session: TaskSession, toolCall: ToolCallRecord }
244
+ ```
182
245
 
183
- The mutation is **non-destructive** — the original output is preserved, the
184
- banner is appended after a `---` divider, and LLM agents can still parse
185
- the response.
246
+ ### Subscribing from the browser
247
+
248
+ ```ts
249
+ const source = new EventSource("/api/events");
250
+ source.addEventListener("snapshot", (ev) => {
251
+ const data = JSON.parse(ev.data);
252
+ console.log("initial sessions:", data.data.sessions);
253
+ });
254
+ source.addEventListener("task.created", (ev) => {
255
+ const data = JSON.parse(ev.data);
256
+ console.log("new task:", data.data.session.taskId);
257
+ });
258
+ source.addEventListener("tool.recorded", (ev) => {
259
+ const data = JSON.parse(ev.data);
260
+ console.log("tool call:", data.data.toolCall.toolName);
261
+ });
262
+ source.addEventListener("task.completed", (ev) => {
263
+ const data = JSON.parse(ev.data);
264
+ console.log("task done:", data.data.terminalStatus);
265
+ });
266
+ // EventSource auto-reconnects on connection drop. If SSE is unavailable
267
+ // (e.g. ancient browser), the bundled frontend falls back to a 3-second
268
+ // poll of /api/sessions.
269
+ ```
270
+
271
+ ### Subscribing from Node / custom adapter
272
+
273
+ ```ts
274
+ import http from "node:http";
275
+
276
+ http.get("http://127.0.0.1:7788/api/events", (res) => {
277
+ let buf = "";
278
+ res.on("data", (chunk) => {
279
+ buf += chunk.toString("utf-8");
280
+ let idx;
281
+ while ((idx = buf.indexOf("\n\n")) !== -1) {
282
+ const frame = buf.slice(0, idx);
283
+ buf = buf.slice(idx + 2);
284
+ // frame is `event: <type>\ndata: <json>`
285
+ // ...
286
+ }
287
+ });
288
+ });
289
+ ```
290
+
291
+ ## Architecture
292
+
293
+ The plugin runs entirely inside its local HTTP server. The hook handlers
294
+ emit events into an in-process `EventBus` (`src/event-bus.ts`), and every
295
+ connected SSE client receives a frame. No host-side NotificationChannel
296
+ plumbing is required.
297
+
298
+ ```
299
+ TaskShowPlugin (tool/task hooks)
300
+
301
+ │ eventBus.broadcast({type, taskId, timestamp, data})
302
+
303
+ EventBus (in-process subscriber Set + keep-alive loop)
304
+
305
+ │ write frame to every connected http.ServerResponse
306
+
307
+ TaskShowServer.handleSSE
308
+
309
+ │ Content-Type: text/event-stream
310
+
311
+ Browser EventSource ◄──── 3-second polling fallback if SSE unavailable
312
+
313
+
314
+ DOM updates (badge / progress / timeline / mermaid re-render)
315
+ ```
316
+
317
+ The in-process bus also exposes `addListener(fn)` so custom callers
318
+ (CLI tools, IM adapters, log shippers) can subscribe to the same event
319
+ stream without going through HTTP:
320
+
321
+ ```ts
322
+ const plugin = createTaskShowPlugin();
323
+ plugin.getEventBus().addListener((event) => {
324
+ console.log("event:", event.type, event.taskId);
325
+ });
326
+ ```
327
+
328
+ ### Plugin ↔ host version correspondence
329
+
330
+ | Plugin version | Compatible host (`roy-agent`) | Notes |
331
+ | -------------- | ------------------------------------ | ------------------------------------------------------------------ |
332
+ | `0.1.0` | pre-2026-07-10 (`task:after.update`) | Mutates tool result. **Removed** in v0.4.0 plugin. |
333
+ | `0.2.0` | pre-2026-07-10 (`task:after.update`) | Same as 0.1.0; tiny refactors. |
334
+ | `0.3.0` | `>= 2026-07-10` | Adds `tool:before.execute` for self-managed timing. |
335
+ | `0.4.0` | `>= 2026-07-10` (NotificationChannel) | Switches to `env.notify` consumer; no longer mutates output. |
336
+ | **`0.5.0` (this)** | **`>= 2026-07-10`** | **Adds `task:before.create` / `task:after.create`; switches from `env.notify` to in-process EventBus + SSE.** |
337
+
338
+ ### Host design reference
339
+
340
+ The full host-side design — including the `NotificationChannel`
341
+ interface and `EventBusNotificationChannel` (default) — is documented
342
+ in the host repository:
343
+
344
+ >
345
+
346
+ ## SSE Migration (v0.5.0+)
347
+
348
+ > **TL;DR**: stop depending on `env.notify`. The plugin now pushes events
349
+ > over SSE. Connect with `new EventSource('/api/events')` (browser) or
350
+ > `http.get(...)` (Node).
351
+
352
+ ### Before (v0.4.0)
353
+
354
+ ```ts
355
+ // Plugin called env.notify({type:"visualization_ready", ...}).
356
+ // Host's EventBusNotificationChannel re-emitted on env event bus:
357
+ // env.pushEnvEvent({type:"plugin.notification.visualization_ready", ...})
358
+ // Subscribers (CLI / IM) listened on the env event bus.
359
+ ```
360
+
361
+ ### After (v0.5.0+)
362
+
363
+ ```ts
364
+ // Plugin emits via in-process EventBus; HTTP server streams frames
365
+ // over /api/events. Frontend connects with EventSource — no host
366
+ // NotificationChannel required.
367
+ const source = new EventSource("/api/events");
368
+ source.addEventListener("task.completed", (ev) => {
369
+ const { taskId, data } = JSON.parse(ev.data);
370
+ console.log(`Task ${taskId} → ${data.terminalStatus}`);
371
+ });
372
+ ```
373
+
374
+ ### Why we changed
375
+
376
+ 1. **No host dependency**: the SSE stream is served by the plugin's own
377
+ HTTP server. Works with any roy-agent host — no NotificationChannel
378
+ required.
379
+ 2. **Incremental updates**: instead of one-shot "URL injection" on
380
+ terminal status, the frontend receives a frame for *every*
381
+ meaningful change (created / recorded / updated / completed). The
382
+ page updates as the agent works, not after.
383
+ 3. **Progress bar + timeline**: with continuous updates, the frontend
384
+ can show a live progress bar (tool call count) and a real-time
385
+ timeline without manual refresh.
386
+ 4. **Simpler host contract**: removed `env.notify` plumbing from the
387
+ plugin surface. The plugin's `PluginEnvLike` type no longer mentions
388
+ `notify`.
389
+
390
+ ### Backward compatibility
391
+
392
+ The plugin no longer mutates tool result output (that mechanism was
393
+ removed in v0.4.0). Pin to `roy-plugin-task-show@0.4.x` if you depend
394
+ on the legacy `env.notify` consumer path.
186
395
 
187
396
  ## Development
188
397
 
189
398
  ```bash
190
- # Type-check + build
399
+ # Type-check (no emit)
400
+ bun run typecheck
401
+
402
+ # Build (emit dist/)
191
403
  bun run build
192
404
 
193
- # Run unit tests (collector, url-injector, server, plugin)
405
+ # Run unit tests (52 tests across 6 files)
194
406
  bun test
195
407
 
196
408
  # End-to-end check (boots the service, hits it, asserts response)
@@ -232,54 +444,68 @@ It also recognizes (in priority order):
232
444
  If no task id can be recovered, the call lands in a synthetic session so
233
445
  the visualization still works.
234
446
 
235
- ### `task:after.complete` (preferred, consumed)
236
-
237
- The plugin accepts the standard `TaskCompleteResultContext`:
447
+ ### `task:before.create` (v0.5.0+)
238
448
 
239
449
  ```ts
240
450
  {
241
- task: { id: number, status: 'completed' | 'failed' | 'cancelled' | 'timeout', title?: string },
242
- terminalStatus: 'completed' | 'failed' | 'cancelled' | 'timeout',
243
- changes: Partial<UpdateTaskOptions>,
244
- sessionId: string,
245
- tagService: TagService,
451
+ data: { task: { id: number, title?: string } }
452
+ // OR flat: { taskId: number, title?: string }
246
453
  }
247
454
  ```
248
455
 
249
- Because the host pre-filters to terminal transitions, no plugin-side status
250
- filtering is needed. The plugin dedupes by `taskId` against the legacy hook
251
- below.
456
+ If a session already exists for the task id (re-create / retry), its
457
+ `toolCalls` are preserved.
252
458
 
253
- ### `task:after.update` (legacy fallback, consumed)
459
+ ### `task:after.create` (v0.5.0+)
460
+
461
+ Same payload as `task:before.create`. Useful for hosts that don't emit
462
+ `task:before.create`; the plugin opens the session here and broadcasts
463
+ `task.created`.
254
464
 
255
- On hosts predating the `AFTER_COMPLETE` hook point, the plugin also subscribes
256
- to `TaskUpdateResultContext`:
465
+ ### `task:after.complete` (preferred, consumed)
466
+
467
+ ```ts
468
+ {
469
+ data: {
470
+ task: { id: number, status: 'completed' | 'failed' | 'cancelled', title?: string },
471
+ terminalStatus: 'completed' | 'failed' | 'cancelled',
472
+ changes: Partial<UpdateTaskOptions>,
473
+ sessionId: string,
474
+ tagService: TagService,
475
+ }
476
+ }
477
+ ```
478
+
479
+ ### `task:after.update` (legacy fallback, consumed)
257
480
 
258
481
  ```ts
259
482
  { data: { task: { id: number, status: string, title?: string }, changes, tagService } }
260
483
  ```
261
484
 
262
- The handler filters for terminal status (`completed` / `failed` / `cancelled` /
263
- `timeout`) and dedupes — if `AFTER_COMPLETE` also fires (it can, on a host
264
- that supports both hooks), the URL is injected exactly once.
485
+ The handler filters for terminal status (`completed` / `failed` /
486
+ `cancelled`) and dedupes — if `AFTER_COMPLETE` also fires (it can, on a
487
+ host that supports both hooks), only one `task.completed` event is
488
+ broadcast.
265
489
 
266
490
  ## Limitations & follow-ups
267
491
 
268
- - **Memory-only**: the collector keeps data in memory only. Restarting the
269
- host process drops everything. Persisting to a file would be a small
270
- follow-up.
271
- - **Single host**: if you launch multiple `roy-agent` instances on the same
272
- machine, change the `port` to avoid clashes (the service falls back to a
273
- random port automatically, but the URL won't be predictable).
274
- - **Mermaid CDN**: the frontend loads `mermaid@10` from jsDelivr. Air-gapped
275
- deployments should vendor a copy locally.
276
- - **No agent-level hooks**: the plugin currently does not subscribe to
277
- `agent:after.execute` because the agent tool loop already fires
278
- `tool:after.execute` per tool. If you want a "task finished" view that
279
- includes the final agent message, listen to `agent:after.execute` too.
280
- - **No file persistence**: data lives only in memory. A future improvement
281
- would be a tiny SQLite sink so a restart still shows recent tasks.
492
+ - **Memory-only**: the collector keeps data in memory only. Restarting
493
+ the host process drops everything. Persisting to a file would be a
494
+ small follow-up.
495
+ - **Single host**: if you launch multiple `roy-agent` instances on the
496
+ same machine, change the `port` to avoid clashes (the service falls
497
+ back to a random port automatically, but the URL won't be
498
+ predictable).
499
+ - **Mermaid CDN**: the frontend loads `mermaid@10` from jsDelivr.
500
+ Air-gapped deployments should vendor a copy locally.
501
+ - **No file persistence**: data lives only in memory. A future
502
+ improvement would be a tiny SQLite sink so a restart still shows
503
+ recent tasks.
504
+ - **SSE-only push**: the bundled frontend uses EventSource as the
505
+ primary update channel. A 3-second polling fallback covers older
506
+ browsers and proxies that strip SSE, but it's a degraded experience.
507
+ WebSocket support is a possible future addition.
282
508
 
283
509
  ## License
284
510
 
285
- MIT
511
+ MIT
@@ -66,9 +66,14 @@ export declare class ToolCallCollector {
66
66
  * Mark a task as completed/failed and freeze its `endedAt`. Used by the
67
67
  * `task:after.update` hook.
68
68
  *
69
- * Returns the (possibly updated) session, or undefined if no session
70
- * existed. The visualization URL is computed by the server (no hard
71
- * coupling), so this method only persists the timestamp.
69
+ * If no session exists for the task yet (e.g. the task completed without
70
+ * any recorded tool calls pure response tasks), we create a synthetic
71
+ * session on the fly so the visualization page can still render a
72
+ * meaningful "task finished" card. `startedAt` is set to one second
73
+ * before `endedAt` so the duration widget renders as ~1s rather than 0s.
74
+ *
75
+ * Returns the (possibly newly-created) session, or undefined only if
76
+ * taskId is invalid.
72
77
  */
73
78
  finalizeOnTaskUpdate(opts: {
74
79
  taskId: number;
@@ -81,6 +86,17 @@ export declare class ToolCallCollector {
81
86
  * context that happens to know the title, e.g. task:before.create).
82
87
  */
83
88
  setTaskTitle(taskId: number, title: string): void;
89
+ /**
90
+ * Insert or replace a session. Used by `task:before.create` /
91
+ * `task:after.create` to mint a fresh TaskSession before any tool call
92
+ * is recorded — gives the frontend a stable taskId/title even for tasks
93
+ * that complete without firing any tool.
94
+ *
95
+ * If a session already exists for the taskId, it is overwritten with the
96
+ * new value (callers should pass a snapshot built from the existing
97
+ * session if they want to preserve toolCalls).
98
+ */
99
+ upsertSession(session: TaskSession): void;
84
100
  /**
85
101
  * Clear all data. Useful in tests and when the plugin is disposed.
86
102
  */
@@ -1 +1 @@
1
- {"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../src/collector.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,WAAW,EAEX,cAAc,EACf,MAAM,YAAY,CAAC;AA6DpB;;;;;;;;;;;;GAYG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkC;IAC3D,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAiB;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IACpC,kFAAkF;IAClF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAa;gBAGrC,GAAG,EAAE,cAAc,EACnB,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE;IAOzD;;OAEG;IACH,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAMnD;;OAEG;IACH,YAAY,IAAI,WAAW,EAAE;IAM7B;;OAEG;IACH,IAAI,IAAI,MAAM;IAId;;;;;;;OAOG;IACH,cAAc,CAAC,IAAI,EAAE;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC9B,OAAO,EAAE,OAAO,CAAC;QACjB,aAAa,EAAE,MAAM,CAAC;QACtB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACnC,GAAG,CAAC,EAAE,GAAG,CAAC;QACV,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,GAAG,MAAM;IA6CV;;;;;;;OAOG;IACH,oBAAoB,CAAC,IAAI,EAAE;QACzB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;QACjC,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,WAAW,GAAG,SAAS;IAe3B;;;OAGG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAOjD;;OAEG;IACH,KAAK,IAAI,IAAI;IASb;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;IAoBrB;;;;;OAKG;IACH,OAAO,CAAC,YAAY;CAGrB"}
1
+ {"version":3,"file":"collector.d.ts","sourceRoot":"","sources":["../src/collector.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,WAAW,EAEX,cAAc,EACf,MAAM,YAAY,CAAC;AA6DpB;;;;;;;;;;;;GAYG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkC;IAC3D,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAiB;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IACpC,kFAAkF;IAClF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAa;gBAGrC,GAAG,EAAE,cAAc,EACnB,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE;IAOzD;;OAEG;IACH,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAMnD;;OAEG;IACH,YAAY,IAAI,WAAW,EAAE;IAM7B;;OAEG;IACH,IAAI,IAAI,MAAM;IAId;;;;;;;OAOG;IACH,cAAc,CAAC,IAAI,EAAE;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC9B,OAAO,EAAE,OAAO,CAAC;QACjB,aAAa,EAAE,MAAM,CAAC;QACtB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACnC,GAAG,CAAC,EAAE,GAAG,CAAC;QACV,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,GAAG,MAAM;IA6CV;;;;;;;;;;;;OAYG;IACH,oBAAoB,CAAC,IAAI,EAAE;QACzB,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;QACjC,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,WAAW,GAAG,SAAS;IAiC3B;;;OAGG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAOjD;;;;;;;;;OASG;IACH,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAMzC;;OAEG;IACH,KAAK,IAAI,IAAI;IASb;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;IAoBrB;;;;;OAKG;IACH,OAAO,CAAC,YAAY;CAGrB"}
package/dist/collector.js CHANGED
@@ -153,16 +153,36 @@ export class ToolCallCollector {
153
153
  * Mark a task as completed/failed and freeze its `endedAt`. Used by the
154
154
  * `task:after.update` hook.
155
155
  *
156
- * Returns the (possibly updated) session, or undefined if no session
157
- * existed. The visualization URL is computed by the server (no hard
158
- * coupling), so this method only persists the timestamp.
156
+ * If no session exists for the task yet (e.g. the task completed without
157
+ * any recorded tool calls pure response tasks), we create a synthetic
158
+ * session on the fly so the visualization page can still render a
159
+ * meaningful "task finished" card. `startedAt` is set to one second
160
+ * before `endedAt` so the duration widget renders as ~1s rather than 0s.
161
+ *
162
+ * Returns the (possibly newly-created) session, or undefined only if
163
+ * taskId is invalid.
159
164
  */
160
165
  finalizeOnTaskUpdate(opts) {
161
- const session = this.sessions.get(opts.taskId);
162
- if (!session)
163
- return undefined;
166
+ const now = opts.timestamp ?? Date.now();
167
+ let session = this.sessions.get(opts.taskId);
168
+ if (!session) {
169
+ // Synthetic session — task completed without any recorded tool call.
170
+ session = {
171
+ taskId: opts.taskId,
172
+ title: opts.title ?? "",
173
+ startedAt: now - 1000,
174
+ endedAt: now,
175
+ status: opts.newStatus,
176
+ toolCalls: [],
177
+ };
178
+ this.sessions.set(opts.taskId, session);
179
+ this.evictIfNeeded();
180
+ this.onChange?.();
181
+ this.logger.info(`Task ${opts.taskId} → ${opts.newStatus} (synthetic, no tool calls recorded)`);
182
+ return this.cloneSession(session);
183
+ }
164
184
  session.status = opts.newStatus;
165
- session.endedAt = opts.timestamp ?? Date.now();
185
+ session.endedAt = now;
166
186
  if (opts.title)
167
187
  session.title = opts.title;
168
188
  this.logger.info(`Task ${opts.taskId} → ${opts.newStatus} (${session.toolCalls.length} tool calls)`);
@@ -180,6 +200,21 @@ export class ToolCallCollector {
180
200
  session.title = title;
181
201
  this.onChange?.();
182
202
  }
203
+ /**
204
+ * Insert or replace a session. Used by `task:before.create` /
205
+ * `task:after.create` to mint a fresh TaskSession before any tool call
206
+ * is recorded — gives the frontend a stable taskId/title even for tasks
207
+ * that complete without firing any tool.
208
+ *
209
+ * If a session already exists for the taskId, it is overwritten with the
210
+ * new value (callers should pass a snapshot built from the existing
211
+ * session if they want to preserve toolCalls).
212
+ */
213
+ upsertSession(session) {
214
+ this.sessions.set(session.taskId, session);
215
+ this.evictIfNeeded();
216
+ this.onChange?.();
217
+ }
183
218
  /**
184
219
  * Clear all data. Useful in tests and when the plugin is disposed.
185
220
  */
@@ -1 +1 @@
1
- {"version":3,"file":"collector.js","sourceRoot":"","sources":["../src/collector.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"collector.js","sourceRoot":"","sources":["../src/collector.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH;;;GAGG;AACH,MAAM,UAAU;IACe;IAA7B,YAA6B,MAAc;QAAd,WAAM,GAAN,MAAM,CAAQ;IAAG,CAAC;IAC/C,IAAI,CAAC,GAAW;QACd,IAAI,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,CAAC,GAAG,EAAE,eAAe,EAAE,CAAC;YACnE,sCAAsC;YACtC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC,CAAC;QACzC,CAAC;IACH,CAAC;IACD,IAAI,CAAC,GAAW;QACd,sCAAsC;QACtC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,UAAU,GAAG,EAAE,CAAC,CAAC;IAC/C,CAAC;IACD,KAAK,CAAC,GAAW;QACf,sCAAsC;QACtC,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC,CAAC;IACjD,CAAC;CACF;AAED;;;;;;GAMG;AACH,SAAS,aAAa,CACpB,GAAQ,EACR,QAA6C;IAE7C,IAAI,OAAO,GAAG,EAAE,aAAa,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC,aAAa,CAAC;IACrE,IAAI,OAAO,QAAQ,EAAE,eAAe,KAAK,QAAQ,EAAE,CAAC;QAClD,OAAO,QAAQ,CAAC,eAAe,CAAC;IAClC,CAAC;IACD,IACE,QAAQ,EAAE,eAAe,KAAK,SAAS;QACvC,OAAQ,QAAgB,CAAC,eAAe,KAAK,QAAQ,EACrD,CAAC;QACD,OAAQ,QAAgB,CAAC,eAAe,CAAC;IAC3C,CAAC;IACD,wEAAwE;IACxE,IACE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,KAAK,SAAS;QACpD,OAAO,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,KAAK,QAAQ,EACvD,CAAC;QACD,OAAO,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC;IAC7C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,IAAI,YAAY,GAAG,aAAa,CAAC;AAEjC;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,iBAAiB;IACX,QAAQ,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC1C,GAAG,CAAiB;IACpB,MAAM,CAAa;IACpC,kFAAkF;IACjE,QAAQ,CAAc;IAEvC,YACE,GAAmB,EACnB,OAAuD;QAEvD,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,QAAQ,GAAG,OAAO,EAAE,QAAQ,CAAC;QAClC,IAAI,CAAC,MAAM,GAAG,IAAI,UAAU,CAAC,OAAO,EAAE,SAAS,IAAI,qBAAqB,CAAC,CAAC;IAC5E,CAAC;IAED;;OAEG;IACH,UAAU,CAAC,MAAc;QACvB,MAAM,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACpC,IAAI,CAAC,CAAC;YAAE,OAAO,SAAS,CAAC;QACzB,OAAO,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC9B,CAAC;IAED;;OAEG;IACH,YAAY;QACV,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;aACtC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;aAChC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC;IAC/C,CAAC;IAED;;OAEG;IACH,IAAI;QACF,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;IAC5B,CAAC;IAED;;;;;;;OAOG;IACH,cAAc,CAAC,IAYd;QACC,MAAM,MAAM,GACV,IAAI,CAAC,cAAc;YACnB,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC;YACtC,YAAY,EAAE,CAAC;QAEjB,IAAI,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACxC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG;gBACR,MAAM;gBACN,KAAK,EAAE,EAAE,EAAE,0DAA0D;gBACrE,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,MAAM,EAAE,SAAS;gBACjB,SAAS,EAAE,EAAE;aACd,CAAC;YACF,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACrC,CAAC;QAED,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC;QAC9C,MAAM,aAAa,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAElD,MAAM,MAAM,GAAmB;YAC7B,QAAQ;YACR,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,aAAa;SACd,CAAC;QACF,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAE/B,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,aAAa,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,YAAY,MAAM,EAAE,CAC5F,CAAC;QAEF,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC;QAClB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,oBAAoB,CAAC,IAKpB;QACC,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QACzC,IAAI,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC7C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,qEAAqE;YACrE,OAAO,GAAG;gBACR,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,EAAE;gBACvB,SAAS,EAAE,GAAG,GAAG,IAAI;gBACrB,OAAO,EAAE,GAAG;gBACZ,MAAM,EAAE,IAAI,CAAC,SAAS;gBACtB,SAAS,EAAE,EAAE;aACd,CAAC;YACF,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YACxC,IAAI,CAAC,aAAa,EAAE,CAAC;YACrB,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC;YAClB,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,QAAQ,IAAI,CAAC,MAAM,MAAM,IAAI,CAAC,SAAS,sCAAsC,CAC9E,CAAC;YACF,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QACpC,CAAC;QAED,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC;QAChC,OAAO,CAAC,OAAO,GAAG,GAAG,CAAC;QACtB,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QAE3C,IAAI,CAAC,MAAM,CAAC,IAAI,CACd,QAAQ,IAAI,CAAC,MAAM,MAAM,IAAI,CAAC,SAAS,KAAK,OAAO,CAAC,SAAS,CAAC,MAAM,cAAc,CACnF,CAAC;QACF,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC;QAClB,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC;IAED;;;OAGG;IACH,YAAY,CAAC,MAAc,EAAE,KAAa;QACxC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC;QACtB,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC;IACpB,CAAC;IAED;;;;;;;;;OASG;IACH,aAAa,CAAC,OAAoB;QAChC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC3C,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC;IACpB,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC;IACpB,CAAC;IAED,0EAA0E;IAC1E,UAAU;IACV,0EAA0E;IAE1E;;;;;;OAMG;IACK,aAAa;QACnB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC;QACtC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,KAAK;YAAE,OAAO;QAExC,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;aACjD,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC;aACrC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC;QAE7C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,GAAG,KAAK,CAAC;QAC5C,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;YAC1B,IAAI,OAAO,IAAI,QAAQ;gBAAE,MAAM;YAC/B,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAC/B,OAAO,IAAI,CAAC,CAAC;QACf,CAAC;QACD,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,OAAO,oBAAoB,KAAK,GAAG,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACK,YAAY,CAAC,CAAc;QACjC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAgB,CAAC;IACtD,CAAC;CACF;AAED;;;;;;GAMG;AACH,SAAS,gBAAgB,CAAC,IAA6B;IACrD,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAC;IACxB,MAAM,MAAM,GACT,IAAY,CAAC,SAAS;QACtB,IAAY,CAAC,IAAI;QACjB,IAAY,CAAC,IAAI;QACjB,IAAY,CAAC,cAAc,CAAC;IAC/B,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC7C,OAAO,kCAAkC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACzD,CAAC"}