@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 +310 -84
- package/dist/collector.d.ts +19 -3
- package/dist/collector.d.ts.map +1 -1
- package/dist/collector.js +42 -7
- package/dist/collector.js.map +1 -1
- package/dist/event-bus.d.ts +98 -0
- package/dist/event-bus.d.ts.map +1 -0
- package/dist/event-bus.js +210 -0
- package/dist/event-bus.js.map +1 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/plugin.d.ts +69 -28
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +238 -94
- package/dist/plugin.js.map +1 -1
- package/dist/server.d.ts +16 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +104 -53
- package/dist/server.js.map +1 -1
- package/dist/types.d.ts +79 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +15 -1
- package/dist/types.js.map +1 -1
- package/dist/url-injector.d.ts +15 -37
- package/dist/url-injector.d.ts.map +1 -1
- package/dist/url-injector.js +15 -47
- package/dist/url-injector.js.map +1 -1
- package/package.json +2 -1
- package/plugin.json +33 -11
- package/public/app.js +409 -57
- package/public/index.html +28 -10
- package/public/style.css +49 -1
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Server-Sent Events (SSE) event bus.
|
|
3
|
+
*
|
|
4
|
+
* The bus maintains a Set of connected SSE clients (each is an
|
|
5
|
+
* `http.ServerResponse` already configured for SSE). When the plugin
|
|
6
|
+
* records a tool call or a task status change, it calls
|
|
7
|
+
* `eventBus.broadcast(event)` and every connected client receives a
|
|
8
|
+
* standard SSE `data:` frame.
|
|
9
|
+
*
|
|
10
|
+
* The bus also handles client disconnect cleanup and periodic keep-alive
|
|
11
|
+
* pings (so reverse proxies / load balancers don't drop the connection).
|
|
12
|
+
*
|
|
13
|
+
* The implementation is intentionally tiny — no external dependencies, no
|
|
14
|
+
* abstractions beyond what the plugin needs. Each subscriber is identified
|
|
15
|
+
* only by a numeric id (assigned on `subscribe`) so `unsubscribe` is
|
|
16
|
+
* idempotent and safe to call multiple times.
|
|
17
|
+
*/
|
|
18
|
+
import type * as http from "node:http";
|
|
19
|
+
import type { TaskEvent } from "./types.js";
|
|
20
|
+
import { SSE_HEARTBEAT_INTERVAL_MS as HEARTBEAT_MS } from "./types.js";
|
|
21
|
+
/**
|
|
22
|
+
* Callback invoked by the bus on each broadcast — useful for tests that
|
|
23
|
+
* don't want to spin up a real HTTP server + EventSource.
|
|
24
|
+
*/
|
|
25
|
+
export type EventListener = (event: TaskEvent) => void;
|
|
26
|
+
/**
|
|
27
|
+
* Connection handle returned by `subscribe()`. Pass to `unsubscribe()`
|
|
28
|
+
* when the client disconnects.
|
|
29
|
+
*/
|
|
30
|
+
export interface SubscriberHandle {
|
|
31
|
+
/** Numeric id assigned at subscribe time. */
|
|
32
|
+
readonly id: number;
|
|
33
|
+
}
|
|
34
|
+
export declare class EventBus {
|
|
35
|
+
private clients;
|
|
36
|
+
private listeners;
|
|
37
|
+
private nextId;
|
|
38
|
+
private heartbeatTimer;
|
|
39
|
+
/**
|
|
40
|
+
* Register an HTTP response as a new SSE subscriber.
|
|
41
|
+
*
|
|
42
|
+
* Caller is responsible for setting up SSE headers on `res` BEFORE
|
|
43
|
+
* calling this method (use `SSEClient.writeHeaders`). The bus will:
|
|
44
|
+
* - register a `close` listener that auto-removes the subscriber
|
|
45
|
+
* - start a periodic keep-alive ping (if not already running)
|
|
46
|
+
*
|
|
47
|
+
* Returns a handle that can be passed to `unsubscribe()` for explicit
|
|
48
|
+
* removal (the close listener handles cleanup automatically on socket
|
|
49
|
+
* close).
|
|
50
|
+
*/
|
|
51
|
+
subscribe(res: http.ServerResponse, label?: string): SubscriberHandle;
|
|
52
|
+
/**
|
|
53
|
+
* Remove a subscriber (idempotent — safe to call multiple times).
|
|
54
|
+
*/
|
|
55
|
+
unsubscribe(handle: SubscriberHandle): void;
|
|
56
|
+
/**
|
|
57
|
+
* Broadcast an event to every connected client and registered listener.
|
|
58
|
+
*
|
|
59
|
+
* `event` is serialized with `JSON.stringify` and sent as a single SSE
|
|
60
|
+
* `data:` frame. Clients with closed sockets are silently dropped from
|
|
61
|
+
* the subscriber set on the next write attempt.
|
|
62
|
+
*
|
|
63
|
+
* Exceptions from individual client writes are caught and logged — one
|
|
64
|
+
* broken subscriber must not break the rest.
|
|
65
|
+
*/
|
|
66
|
+
broadcast(event: TaskEvent): void;
|
|
67
|
+
/**
|
|
68
|
+
* Register an in-process event listener (test-only path).
|
|
69
|
+
* Returns a function that unregisters the listener.
|
|
70
|
+
*/
|
|
71
|
+
addListener(fn: EventListener): () => void;
|
|
72
|
+
/** Number of currently-connected SSE clients (for tests / health checks). */
|
|
73
|
+
size(): number;
|
|
74
|
+
/** True if there are no subscribers. */
|
|
75
|
+
isEmpty(): boolean;
|
|
76
|
+
/** Force-close every subscriber (called on dispose). */
|
|
77
|
+
closeAll(): void;
|
|
78
|
+
private maybeStartHeartbeat;
|
|
79
|
+
private maybeStopHeartbeat;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Format a TaskEvent as an SSE frame. Uses `data: <json>\n\n` per the SSE
|
|
83
|
+
* spec (https://html.spec.whatwg.org/multipage/server-sent-events.html).
|
|
84
|
+
*
|
|
85
|
+
* Optionally a caller can supply an `id:` line so clients can resume from
|
|
86
|
+
* the last event after a reconnect — currently we omit it (not needed
|
|
87
|
+
* for the plugin's use case where the frontend re-fetches the full state
|
|
88
|
+
* snapshot on reconnect).
|
|
89
|
+
*/
|
|
90
|
+
export declare function formatSSEFrame(event: TaskEvent): string;
|
|
91
|
+
/**
|
|
92
|
+
* Write the standard SSE response headers and the initial comment line.
|
|
93
|
+
* Helper for `server.ts` to keep the handler readable.
|
|
94
|
+
*/
|
|
95
|
+
export declare function writeSSEHeaders(res: http.ServerResponse): void;
|
|
96
|
+
/** Re-export heartbeat constant for convenience. */
|
|
97
|
+
export { HEARTBEAT_MS };
|
|
98
|
+
//# sourceMappingURL=event-bus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-bus.d.ts","sourceRoot":"","sources":["../src/event-bus.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,KAAK,IAAI,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,yBAAyB,IAAI,YAAY,EAAE,MAAM,YAAY,CAAC;AAMvE;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;AAEvD;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB;AAyBD,qBAAa,QAAQ;IACnB,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,cAAc,CAA+C;IAErE;;;;;;;;;;;OAWG;IACH,SAAS,CAAC,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB;IAoBrE;;OAEG;IACH,WAAW,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI;IAe3C;;;;;;;;;OASG;IACH,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IA8BjC;;;OAGG;IACH,WAAW,CAAC,EAAE,EAAE,aAAa,GAAG,MAAM,IAAI;IAK1C,6EAA6E;IAC7E,IAAI,IAAI,MAAM;IAId,wCAAwC;IACxC,OAAO,IAAI,OAAO;IAIlB,wDAAwD;IACxD,QAAQ,IAAI,IAAI;IAiBhB,OAAO,CAAC,mBAAmB;IAoB3B,OAAO,CAAC,kBAAkB;CAM3B;AAMD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAGvD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,IAAI,CAAC,cAAc,GAAG,IAAI,CAW9D;AAED,oDAAoD;AACpD,OAAO,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Server-Sent Events (SSE) event bus.
|
|
3
|
+
*
|
|
4
|
+
* The bus maintains a Set of connected SSE clients (each is an
|
|
5
|
+
* `http.ServerResponse` already configured for SSE). When the plugin
|
|
6
|
+
* records a tool call or a task status change, it calls
|
|
7
|
+
* `eventBus.broadcast(event)` and every connected client receives a
|
|
8
|
+
* standard SSE `data:` frame.
|
|
9
|
+
*
|
|
10
|
+
* The bus also handles client disconnect cleanup and periodic keep-alive
|
|
11
|
+
* pings (so reverse proxies / load balancers don't drop the connection).
|
|
12
|
+
*
|
|
13
|
+
* The implementation is intentionally tiny — no external dependencies, no
|
|
14
|
+
* abstractions beyond what the plugin needs. Each subscriber is identified
|
|
15
|
+
* only by a numeric id (assigned on `subscribe`) so `unsubscribe` is
|
|
16
|
+
* idempotent and safe to call multiple times.
|
|
17
|
+
*/
|
|
18
|
+
import { SSE_HEARTBEAT_INTERVAL_MS as HEARTBEAT_MS } from "./types.js";
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// EventBus
|
|
21
|
+
// ============================================================================
|
|
22
|
+
export class EventBus {
|
|
23
|
+
clients = new Set();
|
|
24
|
+
listeners = new Set();
|
|
25
|
+
nextId = 1;
|
|
26
|
+
heartbeatTimer = null;
|
|
27
|
+
/**
|
|
28
|
+
* Register an HTTP response as a new SSE subscriber.
|
|
29
|
+
*
|
|
30
|
+
* Caller is responsible for setting up SSE headers on `res` BEFORE
|
|
31
|
+
* calling this method (use `SSEClient.writeHeaders`). The bus will:
|
|
32
|
+
* - register a `close` listener that auto-removes the subscriber
|
|
33
|
+
* - start a periodic keep-alive ping (if not already running)
|
|
34
|
+
*
|
|
35
|
+
* Returns a handle that can be passed to `unsubscribe()` for explicit
|
|
36
|
+
* removal (the close listener handles cleanup automatically on socket
|
|
37
|
+
* close).
|
|
38
|
+
*/
|
|
39
|
+
subscribe(res, label) {
|
|
40
|
+
const id = this.nextId++;
|
|
41
|
+
const client = {
|
|
42
|
+
id,
|
|
43
|
+
res,
|
|
44
|
+
connectedAt: Date.now(),
|
|
45
|
+
...(label !== undefined ? { label } : {}),
|
|
46
|
+
};
|
|
47
|
+
this.clients.add(client);
|
|
48
|
+
// Auto-cleanup on socket close.
|
|
49
|
+
res.on("close", () => {
|
|
50
|
+
this.clients.delete(client);
|
|
51
|
+
this.maybeStopHeartbeat();
|
|
52
|
+
});
|
|
53
|
+
this.maybeStartHeartbeat();
|
|
54
|
+
return { id };
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Remove a subscriber (idempotent — safe to call multiple times).
|
|
58
|
+
*/
|
|
59
|
+
unsubscribe(handle) {
|
|
60
|
+
for (const c of this.clients) {
|
|
61
|
+
if (c.id === handle.id) {
|
|
62
|
+
this.clients.delete(c);
|
|
63
|
+
try {
|
|
64
|
+
c.res.end();
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// socket may already be closed
|
|
68
|
+
}
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
this.maybeStopHeartbeat();
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Broadcast an event to every connected client and registered listener.
|
|
76
|
+
*
|
|
77
|
+
* `event` is serialized with `JSON.stringify` and sent as a single SSE
|
|
78
|
+
* `data:` frame. Clients with closed sockets are silently dropped from
|
|
79
|
+
* the subscriber set on the next write attempt.
|
|
80
|
+
*
|
|
81
|
+
* Exceptions from individual client writes are caught and logged — one
|
|
82
|
+
* broken subscriber must not break the rest.
|
|
83
|
+
*/
|
|
84
|
+
broadcast(event) {
|
|
85
|
+
const frame = formatSSEFrame(event);
|
|
86
|
+
// Drop dead clients up front.
|
|
87
|
+
const dead = [];
|
|
88
|
+
for (const c of this.clients) {
|
|
89
|
+
if (c.res.writableEnded || c.res.destroyed) {
|
|
90
|
+
dead.push(c);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
c.res.write(frame);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
dead.push(c);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
for (const d of dead)
|
|
101
|
+
this.clients.delete(d);
|
|
102
|
+
// Listeners (for tests / in-process subscribers).
|
|
103
|
+
for (const l of this.listeners) {
|
|
104
|
+
try {
|
|
105
|
+
l(event);
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// Listener errors must not break the broadcast.
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
this.maybeStopHeartbeat();
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Register an in-process event listener (test-only path).
|
|
115
|
+
* Returns a function that unregisters the listener.
|
|
116
|
+
*/
|
|
117
|
+
addListener(fn) {
|
|
118
|
+
this.listeners.add(fn);
|
|
119
|
+
return () => this.listeners.delete(fn);
|
|
120
|
+
}
|
|
121
|
+
/** Number of currently-connected SSE clients (for tests / health checks). */
|
|
122
|
+
size() {
|
|
123
|
+
return this.clients.size;
|
|
124
|
+
}
|
|
125
|
+
/** True if there are no subscribers. */
|
|
126
|
+
isEmpty() {
|
|
127
|
+
return this.clients.size === 0;
|
|
128
|
+
}
|
|
129
|
+
/** Force-close every subscriber (called on dispose). */
|
|
130
|
+
closeAll() {
|
|
131
|
+
for (const c of this.clients) {
|
|
132
|
+
try {
|
|
133
|
+
c.res.end();
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// ignore
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
this.clients.clear();
|
|
140
|
+
this.listeners.clear();
|
|
141
|
+
this.maybeStopHeartbeat();
|
|
142
|
+
}
|
|
143
|
+
// ---------------------------------------------------------------------
|
|
144
|
+
// Internals
|
|
145
|
+
// ---------------------------------------------------------------------
|
|
146
|
+
maybeStartHeartbeat() {
|
|
147
|
+
if (this.heartbeatTimer)
|
|
148
|
+
return;
|
|
149
|
+
if (this.clients.size === 0)
|
|
150
|
+
return;
|
|
151
|
+
this.heartbeatTimer = setInterval(() => {
|
|
152
|
+
for (const c of this.clients) {
|
|
153
|
+
if (c.res.writableEnded || c.res.destroyed)
|
|
154
|
+
continue;
|
|
155
|
+
try {
|
|
156
|
+
// SSE comment lines start with `:` and are ignored by EventSource.
|
|
157
|
+
c.res.write(":keep-alive\n\n");
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
// ignore — next broadcast will reap
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}, HEARTBEAT_MS);
|
|
164
|
+
// Don't keep the event loop alive just for heartbeats.
|
|
165
|
+
if (typeof this.heartbeatTimer.unref === "function") {
|
|
166
|
+
this.heartbeatTimer.unref();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
maybeStopHeartbeat() {
|
|
170
|
+
if (this.heartbeatTimer && this.clients.size === 0) {
|
|
171
|
+
clearInterval(this.heartbeatTimer);
|
|
172
|
+
this.heartbeatTimer = null;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// ============================================================================
|
|
177
|
+
// Helpers
|
|
178
|
+
// ============================================================================
|
|
179
|
+
/**
|
|
180
|
+
* Format a TaskEvent as an SSE frame. Uses `data: <json>\n\n` per the SSE
|
|
181
|
+
* spec (https://html.spec.whatwg.org/multipage/server-sent-events.html).
|
|
182
|
+
*
|
|
183
|
+
* Optionally a caller can supply an `id:` line so clients can resume from
|
|
184
|
+
* the last event after a reconnect — currently we omit it (not needed
|
|
185
|
+
* for the plugin's use case where the frontend re-fetches the full state
|
|
186
|
+
* snapshot on reconnect).
|
|
187
|
+
*/
|
|
188
|
+
export function formatSSEFrame(event) {
|
|
189
|
+
const json = JSON.stringify(event);
|
|
190
|
+
return `event: ${event.type}\ndata: ${json}\n\n`;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Write the standard SSE response headers and the initial comment line.
|
|
194
|
+
* Helper for `server.ts` to keep the handler readable.
|
|
195
|
+
*/
|
|
196
|
+
export function writeSSEHeaders(res) {
|
|
197
|
+
res.statusCode = 200;
|
|
198
|
+
res.setHeader("Content-Type", "text/event-stream; charset=utf-8");
|
|
199
|
+
res.setHeader("Cache-Control", "no-cache, no-transform");
|
|
200
|
+
res.setHeader("Connection", "keep-alive");
|
|
201
|
+
res.setHeader("X-Accel-Buffering", "no"); // disable nginx buffering
|
|
202
|
+
// Some browsers / proxies require a 2KB initial response to flush headers.
|
|
203
|
+
res.write(":ok\n\n");
|
|
204
|
+
if (typeof res.flushHeaders === "function") {
|
|
205
|
+
res.flushHeaders();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/** Re-export heartbeat constant for convenience. */
|
|
209
|
+
export { HEARTBEAT_MS };
|
|
210
|
+
//# sourceMappingURL=event-bus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-bus.js","sourceRoot":"","sources":["../src/event-bus.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -9,12 +9,13 @@
|
|
|
9
9
|
export { TaskShowPlugin, createTaskShowPlugin, } from "./plugin.js";
|
|
10
10
|
export type { TaskShowPluginInterface } from "./plugin.js";
|
|
11
11
|
export { DEFAULT_CONFIG, } from "./types.js";
|
|
12
|
-
export type { TaskShowConfig, PluginEnvLike, TaskSession, ToolCallRecord, } from "./types.js";
|
|
12
|
+
export type { TaskShowConfig, PluginEnvLike, TaskSession, ToolCallRecord, TaskEvent, TaskEventType, TaskCreatedEventData, TaskUpdatedEventData, TaskCompletedEventData, ToolRecordedEventData, TypedTaskEvent, } from "./types.js";
|
|
13
13
|
export { ToolCallCollector, } from "./collector.js";
|
|
14
14
|
export { TaskShowServer, } from "./server.js";
|
|
15
15
|
export type { ServerInfo } from "./server.js";
|
|
16
|
-
export {
|
|
17
|
-
export type {
|
|
16
|
+
export { EventBus, formatSSEFrame, writeSSEHeaders, } from "./event-bus.js";
|
|
17
|
+
export type { SubscriberHandle, EventListener } from "./event-bus.js";
|
|
18
|
+
export { buildVisualizationUrl, buildIndexUrl, } from "./url-injector.js";
|
|
18
19
|
import TaskShowPlugin from "./plugin.js";
|
|
19
20
|
export default TaskShowPlugin;
|
|
20
21
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,cAAc,EACd,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAE3D,OAAO,EACL,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,cAAc,EACd,aAAa,EACb,WAAW,EACX,cAAc,GACf,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,cAAc,GACf,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,cAAc,EACd,oBAAoB,GACrB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAE3D,OAAO,EACL,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,cAAc,EACd,aAAa,EACb,WAAW,EACX,cAAc,EACd,SAAS,EACT,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,cAAc,GACf,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,cAAc,GACf,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,EACL,QAAQ,EACR,cAAc,EACd,eAAe,GAChB,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEtE,OAAO,EACL,qBAAqB,EACrB,aAAa,GACd,MAAM,mBAAmB,CAAC;AAE3B,OAAO,cAAc,MAAM,aAAa,CAAC;AACzC,eAAe,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,8 @@ export { TaskShowPlugin, createTaskShowPlugin, } from "./plugin.js";
|
|
|
10
10
|
export { DEFAULT_CONFIG, } from "./types.js";
|
|
11
11
|
export { ToolCallCollector, } from "./collector.js";
|
|
12
12
|
export { TaskShowServer, } from "./server.js";
|
|
13
|
-
export {
|
|
13
|
+
export { EventBus, formatSSEFrame, writeSSEHeaders, } from "./event-bus.js";
|
|
14
|
+
export { buildVisualizationUrl, buildIndexUrl, } from "./url-injector.js";
|
|
14
15
|
import TaskShowPlugin from "./plugin.js";
|
|
15
16
|
export default TaskShowPlugin;
|
|
16
17
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,cAAc,EACd,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,cAAc,GACf,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,cAAc,EACd,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,cAAc,GACf,MAAM,YAAY,CAAC;AAepB,OAAO,EACL,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,cAAc,GACf,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,QAAQ,EACR,cAAc,EACd,eAAe,GAChB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,qBAAqB,EACrB,aAAa,GACd,MAAM,mBAAmB,CAAC;AAE3B,OAAO,cAAc,MAAM,aAAa,CAAC;AACzC,eAAe,cAAc,CAAC"}
|
package/dist/plugin.d.ts
CHANGED
|
@@ -4,16 +4,37 @@
|
|
|
4
4
|
* Lifecycle (mirrors `ReminderPlugin` / `TaskTagPlugin`):
|
|
5
5
|
* 1. Constructor accepts a config (defaults merged in).
|
|
6
6
|
* 2. `init(env)` registers hooks on the global hook manager:
|
|
7
|
-
* - `tool:
|
|
8
|
-
* - `
|
|
7
|
+
* - `tool:before.execute` → record per-call start timestamp
|
|
8
|
+
* - `tool:after.execute` → record each tool call
|
|
9
|
+
* - `task:before.create` → open a new TaskSession
|
|
10
|
+
* - `task:after.create` → mark session ready
|
|
11
|
+
* - `task:after.update` → freeze + broadcast status update
|
|
12
|
+
* - `task:after.complete` → freeze + broadcast terminal status
|
|
9
13
|
* 3. `dispose()` stops the HTTP service & releases listeners.
|
|
10
14
|
*
|
|
11
15
|
* The plugin is intentionally lean: all the heavy lifting lives in
|
|
12
|
-
* `collector.ts`, `server.ts`, and `
|
|
16
|
+
* `collector.ts`, `server.ts`, and `event-bus.ts`. That keeps this file
|
|
13
17
|
* readable and makes the surface area easy to test.
|
|
18
|
+
*
|
|
19
|
+
* ## Visualization reach-out (v0.5.0+)
|
|
20
|
+
*
|
|
21
|
+
* v0.5.0 replaces the previous `env.notify({type:"visualization_ready"})`
|
|
22
|
+
* mechanism with a Server-Sent Events stream served by the local HTTP
|
|
23
|
+
* service. The frontend subscribes to `GET /api/events` and receives a
|
|
24
|
+
* real-time push every time:
|
|
25
|
+
*
|
|
26
|
+
* - a new task is created (`task.created`)
|
|
27
|
+
* - a tool call is recorded (`tool.recorded`)
|
|
28
|
+
* - a task status changes (`task.updated`)
|
|
29
|
+
* - a task transitions to terminal status (`task.completed`)
|
|
30
|
+
*
|
|
31
|
+
* This eliminates the host-side NotificationChannel plumbing and works
|
|
32
|
+
* with any roy-agent host — including ones that pre-date the
|
|
33
|
+
* NotificationChannel abstraction.
|
|
14
34
|
*/
|
|
15
35
|
import type { PluginEnvLike, TaskShowConfig } from "./types.js";
|
|
16
36
|
import { ToolCallCollector } from "./collector.js";
|
|
37
|
+
import { EventBus } from "./event-bus.js";
|
|
17
38
|
/**
|
|
18
39
|
* Public, friendly type used in README + tests.
|
|
19
40
|
*
|
|
@@ -31,8 +52,15 @@ export interface TaskShowPluginInterface {
|
|
|
31
52
|
getServerPort(): number | null;
|
|
32
53
|
/** accessor for tests / health checks */
|
|
33
54
|
getCollector(): ToolCallCollector;
|
|
55
|
+
/** accessor for tests / health checks */
|
|
56
|
+
getEventBus(): EventBus;
|
|
34
57
|
/** accessor for tests that want to bypass the hook manager entirely */
|
|
35
58
|
getConfig(): TaskShowConfig;
|
|
59
|
+
/**
|
|
60
|
+
* Compute the URL the page would be served at. Helpful in tests where
|
|
61
|
+
* `init()` was skipped.
|
|
62
|
+
*/
|
|
63
|
+
getVisualizationUrl(taskId: number): string;
|
|
36
64
|
}
|
|
37
65
|
/**
|
|
38
66
|
* Main plugin class. Decorated with the static `roy-agent` metadata block
|
|
@@ -40,15 +68,14 @@ export interface TaskShowPluginInterface {
|
|
|
40
68
|
*/
|
|
41
69
|
export declare class TaskShowPlugin implements TaskShowPluginInterface {
|
|
42
70
|
readonly name = "roy-plugin-task-show";
|
|
43
|
-
readonly version = "0.
|
|
44
|
-
readonly description = "Visually trace a task's tool-call chain on a local HTTP service
|
|
71
|
+
readonly version = "0.5.0";
|
|
72
|
+
readonly description = "Visually trace a task's tool-call chain on a local HTTP service. v0.5.0+ uses Server-Sent Events for real-time page updates (no host NotificationChannel required).";
|
|
45
73
|
private readonly cfg;
|
|
46
74
|
private readonly collector;
|
|
47
75
|
private readonly server;
|
|
76
|
+
private readonly eventBus;
|
|
48
77
|
private env;
|
|
49
78
|
private disposed;
|
|
50
|
-
/** Last successful `tool:after.execute` result, used to inject the URL on task completion. */
|
|
51
|
-
private lastResultByTask;
|
|
52
79
|
/**
|
|
53
80
|
* `tool:before.execute` → `tool:after.execute` start-time buffer.
|
|
54
81
|
* Keyed by a fingerprint derived from (taskId, toolName, argsHash) so
|
|
@@ -66,8 +93,8 @@ export declare class TaskShowPlugin implements TaskShowPluginInterface {
|
|
|
66
93
|
* (which can happen on newer hosts where both hook points fire on
|
|
67
94
|
* completion). Cleared on dispose().
|
|
68
95
|
*/
|
|
69
|
-
private
|
|
70
|
-
/**
|
|
96
|
+
private finalizedTaskIds;
|
|
97
|
+
/** Cached list of hook points we registered, so dispose() can be defensive even if the env lacks unregister. */
|
|
71
98
|
private registered;
|
|
72
99
|
constructor(config?: Partial<TaskShowConfig>);
|
|
73
100
|
/**
|
|
@@ -82,7 +109,9 @@ export declare class TaskShowPlugin implements TaskShowPluginInterface {
|
|
|
82
109
|
dispose(): Promise<void>;
|
|
83
110
|
getServerPort(): number | null;
|
|
84
111
|
getCollector(): ToolCallCollector;
|
|
112
|
+
getEventBus(): EventBus;
|
|
85
113
|
getConfig(): TaskShowConfig;
|
|
114
|
+
getVisualizationUrl(taskId: number): string;
|
|
86
115
|
/**
|
|
87
116
|
* Public function exposed so unit tests can simulate the
|
|
88
117
|
* `tool:before.execute` payload (without going through the global hook
|
|
@@ -100,21 +129,35 @@ export declare class TaskShowPlugin implements TaskShowPluginInterface {
|
|
|
100
129
|
*/
|
|
101
130
|
onToolAfterExecute(ctx: any, metadata?: Record<string, unknown>): Promise<void>;
|
|
102
131
|
/**
|
|
103
|
-
* Public function exposed for tests; mirrors the `task:
|
|
104
|
-
* payload.
|
|
132
|
+
* Public function exposed for tests; mirrors the `task:before.create`
|
|
133
|
+
* payload. We use it to mint a fresh TaskSession before any tool call
|
|
134
|
+
* is recorded (gives us a stable taskId/title even if no tool fires).
|
|
105
135
|
*/
|
|
136
|
+
onTaskBeforeCreate(ctx: any): Promise<void>;
|
|
137
|
+
/**
|
|
138
|
+
* Public function exposed for tests; mirrors the `task:after.create`
|
|
139
|
+
* payload. Right now this is mostly a no-op (session was already opened
|
|
140
|
+
* in `onTaskBeforeCreate`); the hook is wired so the frontend can
|
|
141
|
+
* distinguish "task opened" from "task started executing".
|
|
142
|
+
*
|
|
143
|
+
* Hosts that only emit `task:after.create` (no before) are also
|
|
144
|
+
* supported — in that case the session is opened here.
|
|
145
|
+
*/
|
|
146
|
+
onTaskAfterCreate(ctx: any): Promise<void>;
|
|
106
147
|
/**
|
|
107
148
|
* Public function exposed for tests; mirrors BOTH the preferred
|
|
108
|
-
* `task:after.complete` payload
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
149
|
+
* `task:after.complete` payload AND the legacy `task:after.update`
|
|
150
|
+
* payload.
|
|
151
|
+
*
|
|
152
|
+
* - `task:after.complete` (preferred, terminal):
|
|
153
|
+
* - `data.terminalStatus` is explicit; no filtering needed.
|
|
154
|
+
* - `task:after.update` (legacy):
|
|
155
|
+
* - `data.task.status` carries the new status. We treat it as a
|
|
156
|
+
* generic status update — terminal vs non-terminal decides which
|
|
157
|
+
* event type we broadcast.
|
|
112
158
|
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
* - Legacy fallback (pre-2026-07-10 roy-agent): `task:after.update`
|
|
116
|
-
* - only `data.task` is provided; we filter for terminal statuses
|
|
117
|
-
* the same way the previous implementation did.
|
|
159
|
+
* An internal dedup set guarantees that when both hook points fire
|
|
160
|
+
* (newer hosts), we broadcast the terminal event exactly once.
|
|
118
161
|
*/
|
|
119
162
|
onTaskAfterComplete(ctx: any): Promise<void>;
|
|
120
163
|
/**
|
|
@@ -124,15 +167,13 @@ export declare class TaskShowPlugin implements TaskShowPluginInterface {
|
|
|
124
167
|
*/
|
|
125
168
|
onTaskAfterUpdate(ctx: any): Promise<void>;
|
|
126
169
|
/**
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
* Compute the URL the page would be served at. Helpful in tests where
|
|
133
|
-
* `init()` was skipped.
|
|
170
|
+
* Construct a TaskEvent and push it to every connected SSE client.
|
|
171
|
+
*
|
|
172
|
+
* The broadcast is fire-and-forget; SSE write failures are absorbed by
|
|
173
|
+
* the bus itself, so this method never throws. Centralizing the event
|
|
174
|
+
* construction here keeps every hook handler symmetric.
|
|
134
175
|
*/
|
|
135
|
-
|
|
176
|
+
private broadcast;
|
|
136
177
|
/**
|
|
137
178
|
* Subscribe to the hook points we care about. We try to use the env's
|
|
138
179
|
* `registerHook` first (the modern, type-safe plugin API), then fall back
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"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"}
|