@ai-setting/roy-plugin-task-show 0.8.9 → 0.9.5

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 (47) hide show
  1. package/dist/cli-tasks-adapter.d.ts +2 -17
  2. package/dist/cli-tasks-adapter.d.ts.map +1 -1
  3. package/dist/cli-tasks-adapter.js +7 -57
  4. package/dist/cli-tasks-adapter.js.map +1 -1
  5. package/dist/index.d.ts +0 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +0 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/operations-cache.d.ts +21 -0
  10. package/dist/operations-cache.d.ts.map +1 -1
  11. package/dist/operations-cache.js +45 -0
  12. package/dist/operations-cache.js.map +1 -1
  13. package/dist/plugin.d.ts +22 -74
  14. package/dist/plugin.d.ts.map +1 -1
  15. package/dist/plugin.js +174 -173
  16. package/dist/plugin.js.map +1 -1
  17. package/dist/server.d.ts +15 -31
  18. package/dist/server.d.ts.map +1 -1
  19. package/dist/server.js +42 -206
  20. package/dist/server.js.map +1 -1
  21. package/dist/task-detail-mermaid.d.ts +9 -0
  22. package/dist/task-detail-mermaid.d.ts.map +1 -1
  23. package/dist/task-detail-mermaid.js +43 -3
  24. package/dist/task-detail-mermaid.js.map +1 -1
  25. package/dist/task-session-store.d.ts +54 -94
  26. package/dist/task-session-store.d.ts.map +1 -1
  27. package/dist/task-session-store.js +137 -177
  28. package/dist/task-session-store.js.map +1 -1
  29. package/dist/tasks-tree-cache.d.ts +17 -0
  30. package/dist/tasks-tree-cache.d.ts.map +1 -1
  31. package/dist/tasks-tree-cache.js +39 -0
  32. package/dist/tasks-tree-cache.js.map +1 -1
  33. package/dist/tracing/decorator.d.ts +48 -0
  34. package/dist/tracing/decorator.d.ts.map +1 -0
  35. package/dist/tracing/decorator.js +310 -0
  36. package/dist/tracing/decorator.js.map +1 -0
  37. package/dist/types.d.ts +28 -0
  38. package/dist/types.d.ts.map +1 -1
  39. package/dist/types.js +9 -0
  40. package/dist/types.js.map +1 -1
  41. package/package.json +4 -2
  42. package/plugin.json +2 -2
  43. package/public/app.js +45 -1
  44. package/public/index.html +3 -0
  45. package/public/session-forest.js +97 -0
  46. package/public/style.css +0 -45
  47. package/public/task-operations.js +7 -0
@@ -0,0 +1,310 @@
1
+ /**
2
+ * @fileoverview @TracedAs decorator stub for the roy-plugin-task-show v0.9.0.
3
+ *
4
+ * The plugin is a standalone npm package with `dependencies: {}`, so it
5
+ * cannot import the host's `roy-agent-core` tracing module statically.
6
+ * This module provides a minimal local `@TracedAs(name, options)`
7
+ * decorator that:
8
+ *
9
+ * 1. At module load, locates a writable `spans.db` (SQLite) using:
10
+ * a. `process.env.ROY_TRACE_DB` if set
11
+ * b. `~/.config/roy-agent/spans.db` (host default)
12
+ * If none of these resolve to a writable file, the decorator
13
+ * degrades to a no-op that just calls the wrapped function.
14
+ *
15
+ * 2. Wraps the target method so that every invocation writes a
16
+ * single row to the `spans` table mirroring the host's
17
+ * OpenTelemetry-compatible schema:
18
+ *
19
+ * trace_id (16 bytes hex)
20
+ * span_id (8 bytes hex, unique per row)
21
+ * parent_span_id (always NULL for top-level spans)
22
+ * name (the span_name passed to @TracedAs)
23
+ * kind (always "internal")
24
+ * status ("ok" or "error")
25
+ * start_time (Unix epoch ms)
26
+ * end_time (Unix epoch ms)
27
+ * duration_ms (end - start)
28
+ * created_at (now)
29
+ *
30
+ * If the DB write throws (locked, missing schema, permission
31
+ * denied), the error is swallowed — the decorator must never
32
+ * break the wrapped function. This is critical for tests,
33
+ * CI, and any environment where the host spans.db is absent.
34
+ *
35
+ * 3. Generates a process-lifetime `trace_id` (16 bytes hex) at
36
+ * module init. The host's spans.db queries by `name`, so a
37
+ * shared trace_id is sufficient.
38
+ *
39
+ * The plugin runs in the Bun runtime (declared in `package.json
40
+ * engines.bun`), so we use `bun:sqlite` for the writer. If Bun is
41
+ * not available, the decorator degrades to no-op (tests don't
42
+ * need to write spans to pass).
43
+ *
44
+ * Why not import from `@ai-setting/roy-agent-core`? See the
45
+ * plan doc §3.1 / review op #20182: keeping the plugin zero-dep
46
+ * is a hard contract. A 90-line local stub is cheaper than
47
+ * carrying a runtime dep that may not exist in every install
48
+ * target (e.g. the plugin's own test suite).
49
+ */
50
+ import { existsSync, mkdirSync } from "node:fs";
51
+ import { dirname, join } from "node:path";
52
+ import { homedir } from "node:os";
53
+ let cachedDb = null;
54
+ let resolvedSpansDbPath = null;
55
+ /**
56
+ * Process-lifetime trace_id (16 random bytes hex). The host's
57
+ * trace_check queries spans by `name`, so a single trace_id per
58
+ * process is sufficient and matches the host's per-process model.
59
+ */
60
+ const PROCESS_TRACE_ID = (() => {
61
+ const bytes = new Uint8Array(16);
62
+ const c = globalThis.crypto;
63
+ if (c && typeof c.getRandomValues === "function") {
64
+ c.getRandomValues(bytes);
65
+ }
66
+ else {
67
+ for (let i = 0; i < bytes.length; i += 1)
68
+ bytes[i] = Math.floor(Math.random() * 256);
69
+ }
70
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
71
+ })();
72
+ /**
73
+ * Generate a unique 8-byte hex span_id per call.
74
+ */
75
+ function newSpanId() {
76
+ const bytes = new Uint8Array(8);
77
+ const c = globalThis.crypto;
78
+ if (c && typeof c.getRandomValues === "function") {
79
+ c.getRandomValues(bytes);
80
+ }
81
+ else {
82
+ for (let i = 0; i < bytes.length; i += 1)
83
+ bytes[i] = Math.floor(Math.random() * 256);
84
+ }
85
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
86
+ }
87
+ /**
88
+ * Locate the spans.db path. Returns null if no candidate exists
89
+ * or is writable. The lookup is cached after the first successful
90
+ * resolution.
91
+ */
92
+ function resolveSpansDbPath() {
93
+ if (resolvedSpansDbPath !== null)
94
+ return resolvedSpansDbPath;
95
+ const candidates = [];
96
+ if (process.env.ROY_TRACE_DB)
97
+ candidates.push(process.env.ROY_TRACE_DB);
98
+ if (process.env.ROY_AGENT_DATA) {
99
+ candidates.push(join(process.env.ROY_AGENT_DATA, "spans.db"));
100
+ }
101
+ candidates.push(join(homedir(), ".config", "roy-agent", "spans.db"));
102
+ for (const c of candidates) {
103
+ if (c && existsSync(c)) {
104
+ resolvedSpansDbPath = c;
105
+ return c;
106
+ }
107
+ }
108
+ return null;
109
+ }
110
+ /**
111
+ * Lazily open the spans.db. Returns null if Bun is not available,
112
+ * the file cannot be opened, or the `spans` table does not exist.
113
+ */
114
+ function getDb() {
115
+ if (cachedDb)
116
+ return cachedDb;
117
+ if (typeof globalThis.Bun === "undefined")
118
+ return null;
119
+ const path = resolveSpansDbPath();
120
+ if (!path)
121
+ return null;
122
+ try {
123
+ const { Database } = require("bun:sqlite");
124
+ const db = new Database(path);
125
+ try {
126
+ db.exec("SELECT 1 FROM span LIMIT 0");
127
+ }
128
+ catch {
129
+ db.close();
130
+ return null;
131
+ }
132
+ cachedDb = db;
133
+ return cachedDb;
134
+ }
135
+ catch {
136
+ return null;
137
+ }
138
+ }
139
+ /**
140
+ * Write a single span row. Never throws — decorator must not
141
+ * break the wrapped function.
142
+ */
143
+ function writeSpan(row) {
144
+ const db = getDb();
145
+ if (!db)
146
+ return;
147
+ const spanId = newSpanId();
148
+ const durationMs = row.endTime - row.startTime;
149
+ try {
150
+ db.prepare(`INSERT INTO span (trace_id, span_id, parent_span_id, name, kind, status, start_time, end_time, attributes, time_created)
151
+ VALUES (?, ?, NULL, ?, 'internal', ?, ?, ?, ?, ?)`).run(PROCESS_TRACE_ID, spanId, row.name, row.status, row.startTime, row.endTime, JSON.stringify({ duration_ms: durationMs, ...(row.errorMessage ? { error: row.errorMessage } : {}) }), Date.now());
152
+ }
153
+ catch {
154
+ // Swallow — see file header.
155
+ }
156
+ }
157
+ /**
158
+ * @TracedAs(name, options) decorator factory.
159
+ *
160
+ * Usage:
161
+ * @TracedAs("server.home.render", { recordParams: false, recordResult: false })
162
+ * private async sendIndex(res: http.ServerResponse): Promise<void> { ... }
163
+ *
164
+ * If the spans.db is unreachable (no host, no env, schema mismatch),
165
+ * the decorator becomes a transparent passthrough — the wrapped
166
+ * function is called with its original `this` and arguments.
167
+ */
168
+ export function TracedAs(name, _options) {
169
+ return function (_target, propertyKey, descriptor) {
170
+ const originalFn = descriptor.value;
171
+ if (!originalFn)
172
+ return descriptor;
173
+ const spanName = name || propertyKey;
174
+ const wrapped = function (...args) {
175
+ const startTime = Date.now();
176
+ let result;
177
+ try {
178
+ result = originalFn.apply(this, args);
179
+ }
180
+ catch (err) {
181
+ const endTime = Date.now();
182
+ writeSpan({
183
+ name: spanName,
184
+ status: "error",
185
+ startTime,
186
+ endTime,
187
+ errorMessage: err instanceof Error ? err.message : String(err),
188
+ });
189
+ throw err;
190
+ }
191
+ if (result && typeof result.then === "function") {
192
+ return Promise.resolve(result).then((value) => {
193
+ writeSpan({
194
+ name: spanName,
195
+ status: "ok",
196
+ startTime,
197
+ endTime: Date.now(),
198
+ });
199
+ return value;
200
+ }, (err) => {
201
+ writeSpan({
202
+ name: spanName,
203
+ status: "error",
204
+ startTime,
205
+ endTime: Date.now(),
206
+ errorMessage: err instanceof Error ? err.message : String(err),
207
+ });
208
+ throw err;
209
+ });
210
+ }
211
+ writeSpan({
212
+ name: spanName,
213
+ status: "ok",
214
+ startTime,
215
+ endTime: Date.now(),
216
+ });
217
+ return result;
218
+ };
219
+ wrapped.__tracedAsName = spanName;
220
+ return {
221
+ ...descriptor,
222
+ value: wrapped,
223
+ };
224
+ };
225
+ }
226
+ /**
227
+ * For tests: reset the module-level db cache so test fixtures
228
+ * can simulate a fresh host install. Not used in production.
229
+ */
230
+ export function __resetTracedAsCacheForTests() {
231
+ if (cachedDb) {
232
+ try {
233
+ cachedDb.close();
234
+ }
235
+ catch {
236
+ // ignore
237
+ }
238
+ }
239
+ cachedDb = null;
240
+ resolvedSpansDbPath = null;
241
+ }
242
+ /**
243
+ * For tests: returns the resolved spans.db path or null. Allows
244
+ * tests to assert the decorator located the host DB.
245
+ */
246
+ export function __getResolvedSpansDbPathForTests() {
247
+ return resolveSpansDbPath();
248
+ }
249
+ /**
250
+ * Ensure the parent dir of a candidate spans.db path exists
251
+ * (used by tests that point at a temp file). Best-effort.
252
+ */
253
+ export function __ensureSpansDbDirForTests(path) {
254
+ try {
255
+ mkdirSync(dirname(path), { recursive: true });
256
+ }
257
+ catch {
258
+ // ignore
259
+ }
260
+ }
261
+ /**
262
+ * `withTrace(name, fn)` — manual instrumentation helper for free
263
+ * functions (which TS decorators cannot decorate). Runs `fn` and
264
+ * records a span around the invocation. Mirrors the @TracedAs
265
+ * contract: never throws, no-ops when spans.db is unavailable.
266
+ *
267
+ * Used by `renderSessionForestPage` and `encodeMermaidLabelText`
268
+ * (top-level exports that cannot carry a class-method decorator).
269
+ */
270
+ export async function withTrace(name, fn) {
271
+ const startTime = Date.now();
272
+ try {
273
+ const result = await fn();
274
+ writeSpan({ name, status: "ok", startTime, endTime: Date.now() });
275
+ return result;
276
+ }
277
+ catch (err) {
278
+ writeSpan({
279
+ name,
280
+ status: "error",
281
+ startTime,
282
+ endTime: Date.now(),
283
+ errorMessage: err instanceof Error ? err.message : String(err),
284
+ });
285
+ throw err;
286
+ }
287
+ }
288
+ /**
289
+ * Synchronous variant of `withTrace`. Used for top-level
290
+ * non-async exports.
291
+ */
292
+ export function withTraceSync(name, fn) {
293
+ const startTime = Date.now();
294
+ try {
295
+ const result = fn();
296
+ writeSpan({ name, status: "ok", startTime, endTime: Date.now() });
297
+ return result;
298
+ }
299
+ catch (err) {
300
+ writeSpan({
301
+ name,
302
+ status: "error",
303
+ startTime,
304
+ endTime: Date.now(),
305
+ errorMessage: err instanceof Error ? err.message : String(err),
306
+ });
307
+ throw err;
308
+ }
309
+ }
310
+ //# sourceMappingURL=decorator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decorator.js","sourceRoot":"","sources":["../../src/tracing/decorator.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -109,6 +109,29 @@ export interface TaskShowConfig {
109
109
  cliTimeoutMs?: number;
110
110
  /** v0.7.0+: Maximum stdout bytes accepted from the CLI. Default 1 MiB. */
111
111
  cliMaxBytes?: number;
112
+ /**
113
+ * v0.8.10+: Hard cap on the operations cache (one entry per task id).
114
+ * Older stale entries are dropped first when the cap is exceeded.
115
+ * Default 256.
116
+ */
117
+ operationsCacheMaxEntries?: number;
118
+ /**
119
+ * v0.8.10+: Hard cap on the tasks-tree cache (one entry per unique
120
+ * filter combination). Default 64.
121
+ */
122
+ tasksTreeCacheMaxEntries?: number;
123
+ /**
124
+ * v0.8.10+: Grace period for `tool:before.execute` entries that never
125
+ * receive a matching `tool:after.execute` (e.g. crashed tool, host
126
+ * bug). Entries older than this are swept on the next `before` hook
127
+ * call. Default 60_000 ms (1 minute). Set to 0 to disable.
128
+ */
129
+ pendingStartTtlMs?: number;
130
+ /**
131
+ * v0.8.10+: FIFO cap on the `finalizedTaskIds` dedup set so it cannot
132
+ * grow unbounded in long-lived hosts. Default 4096.
133
+ */
134
+ maxFinalizedTaskIds?: number;
112
135
  }
113
136
  /**
114
137
  * Default configuration. Keeping these as a single source of truth simplifies
@@ -117,6 +140,11 @@ export interface TaskShowConfig {
117
140
  * v0.5.0+: SSE replaces the previous `env.notify` mechanism. URL injection
118
141
  * is no longer supported — visualization reach-out is via the SSE event
119
142
  * stream broadcast by the local HTTP service.
143
+ *
144
+ * v0.8.10+: Heap-bounded defaults (Task #2537). `pendingStartTtlMs`,
145
+ * `operationsCacheMaxEntries`, `tasksTreeCacheMaxEntries`, and
146
+ * `maxFinalizedTaskIds` keep the plugin's in-memory Maps and Sets
147
+ * bounded so a long-lived CLI session cannot OOM at 4 GiB V8 heap.
120
148
  */
121
149
  export declare const DEFAULT_CONFIG: TaskShowConfig;
122
150
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js CHANGED
@@ -12,6 +12,11 @@
12
12
  * v0.5.0+: SSE replaces the previous `env.notify` mechanism. URL injection
13
13
  * is no longer supported — visualization reach-out is via the SSE event
14
14
  * stream broadcast by the local HTTP service.
15
+ *
16
+ * v0.8.10+: Heap-bounded defaults (Task #2537). `pendingStartTtlMs`,
17
+ * `operationsCacheMaxEntries`, `tasksTreeCacheMaxEntries`, and
18
+ * `maxFinalizedTaskIds` keep the plugin's in-memory Maps and Sets
19
+ * bounded so a long-lived CLI session cannot OOM at 4 GiB V8 heap.
15
20
  */
16
21
  export const DEFAULT_CONFIG = {
17
22
  port: 7788,
@@ -19,6 +24,10 @@ export const DEFAULT_CONFIG = {
19
24
  autoStart: true,
20
25
  maxStoredTasks: 50,
21
26
  publicDir: "public",
27
+ operationsCacheMaxEntries: 256,
28
+ tasksTreeCacheMaxEntries: 64,
29
+ pendingStartTtlMs: 60_000,
30
+ maxFinalizedTaskIds: 4096,
22
31
  };
23
32
  /**
24
33
  * Heartbeat interval (ms) — sent as a `:keep-alive` SSE comment so reverse
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA+GH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,cAAc,GAAmB;IAC5C,IAAI,EAAE,IAAI;IACV,IAAI,EAAE,SAAS;IACf,SAAS,EAAE,IAAI;IACf,cAAc,EAAE,EAAE;IAClB,SAAS,EAAE,QAAQ;CACpB,CAAC;AAgFF;;;;GAIG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,KAAK,CAAC;AAE/C;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,iBAAiB,CAAC"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAsIH;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,cAAc,GAAmB;IAC5C,IAAI,EAAE,IAAI;IACV,IAAI,EAAE,SAAS;IACf,SAAS,EAAE,IAAI;IACf,cAAc,EAAE,EAAE;IAClB,SAAS,EAAE,QAAQ;IACnB,yBAAyB,EAAE,GAAG;IAC9B,wBAAwB,EAAE,EAAE;IAC5B,iBAAiB,EAAE,MAAM;IACzB,mBAAmB,EAAE,IAAI;CAC1B,CAAC;AAgFF;;;;GAIG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,KAAK,CAAC;AAE/C;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,iBAAiB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-setting/roy-plugin-task-show",
3
- "version": "0.8.9",
3
+ "version": "0.9.5",
4
4
  "description": "roy-agent plugin: visualize task solving process via tool call flow on a local web service",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -20,7 +20,9 @@
20
20
  "start:demo": "bun run scripts/run-demo.ts",
21
21
  "verify": "bun run scripts/verify-service.ts",
22
22
  "verify:installed": "bun run scripts/verify-installed.ts",
23
- "verify:published": "bun run scripts/verify-published.ts"
23
+ "verify:published": "bun run scripts/verify-published.ts",
24
+ "verify:heap": "bun run scripts/verify-heap-bounded.ts",
25
+ "stress:heap": "bun --expose-gc run scripts/stress-heap.ts"
24
26
  },
25
27
  "keywords": [
26
28
  "roy-agent",
package/plugin.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@ai-setting/roy-plugin-task-show",
3
- "version": "0.8.9",
3
+ "version": "0.9.5",
4
4
  "type": "tool-plugin",
5
- "description": "Visualize the tool call chain of a task on a local web service with real-time SSE updates. v0.5.0+: page refreshes stream over GET /api/events (Server-Sent Events). Subscribes to tool:before.execute, tool:after.execute, task:before.create, task:after.create, task:after.complete (preferred, 2026-07-10+), and task:after.update (legacy fallback). v0.6.11: Mermaid re-rendering is delegated to a self-contained controller (public/mermaid-renderer.js) that prevents the SVG→raw-source regression on async updates and surfaces recoverable .mermaid-error states. v0.6.12: Task lifecycle pipeline (operations timeline) server now emits data-task-id on the pipeline section; client preserves it on swap, so the page actually fetches /api/tasks/<id>/operations and renders the 7-op timeline (previously silently bailed). v0.7.0: Home page redesigned as a hierarchical task tree (driven by `roy-agent tasks tree --json`); new /api/tasks/tree endpoint with status / priority / type / root-id filters, expand/collapse UI, search, and live 30s polling. v0.8.0: per-task page Mermaid area now renders the hierarchical 'Task lifecycle + tools' view — each operation record owns a subgraph that nests its tool calls, with click callbacks (`window.__toolClick`) that scroll-into-view + highlight + auto-expand the matching row in the tool-call table below. Operation record descriptions (`description` + `processDescription`) are now always rendered inline (no `<details>` collapse) so the user sees the lifecycle state at a glance; a fallback `<details>` kicks in only for descriptions longer than 600 chars. v0.8.1: hotfix for two pre-existing bugs in v0.8.0 (browser smoke test surfaced after merge). (a) Mermaid click directives were emitted as `click t1 __toolClick(1)` (missing `call` keyword) — Mermaid 10's parser rejects this with `got 'PS'`. Fixed to `click t1 call __toolClick(1)` (the v10 grammar requires `call` to invoke a callback with arguments). (b) `buildMermaidSource` lived inside the `attachTaskPageTimeline` IIFE but was also called from a listener in the `attachToolClickBridge` IIFE — sibling IIFEs cannot see each other's locals, so the listener threw `ReferenceError: buildMermaidSource is not defined` and the Mermaid diagram silently failed to re-render after `task-show:lifecycle-ops-loaded`. Fixed by hoisting the function (and its three helpers) to script top-level so both IIFEs can see it via the script-wide closure; the function is also exposed on `window.buildMermaidSource` for tests + tooling. v0.8.3: tree-display fix (Task #2426). The home page used to look like a flat list of root tasks because `autoExpandFirstLevels(..., 2)` only opened the first 2 levels — 30/47 roots were leaf nodes and the remaining 17 collapsed to one level so grandchildren were never visible. Default expand depth is now 3 (root + child + grandchild + great-grandchild are visible on first paint), the summary line now shows per-depth count pills (root / child / grandchild / great-grandchild / level-N), each `tree-row` carries a `data-depth` attribute so CSS can paint coloured left rails per level, and the duplicated 'Live tool-call sessions (legacy view)' panel that made the page look like both a flat table AND a tree is now hidden behind `#legacy-sessions[hidden]` (kept for future debug-toggle restoration). v0.8.4: cli-eacces-compat hotfix (Task #2499). The CLI bundle `packages/cli/dist/bin/roy-agent.js` ships with `-rw-rw-r--` (no execute bit); the plugin now invokes `roy-agent` via `node <path>` from the resolved CLI path so EACCES is avoided even when the script lacks the executable bit. Resolution order: explicit `cfg.royAgentCliPath` override `$ROY_AGENT_CLI` env sibling-repo .js monorepo .js → bare `roy-agent` (PATH). v0.8.5: CLI resolution priority fix. v0.8.4's resolution order preferred sibling-repo / monorepo `.js` files over the globally-installed `roy-agent` binary, which meant stale scripts in a sibling checkout could shadow a freshly-updated global install. v0.8.5 flips the order: explicit override → `$ROY_AGENT_CLI` env → **`roy-agent` on PATH** (looked up via `which`/`where`) sibling-repo .js (dev) monorepo .js (dev) → literal `roy-agent`. The lookup is exported as a free function `resolveRoyAgentCliPath()` (plus a `findRoyAgentOnPath()` helper) so the new priority order can be unit-tested in isolation. v0.8.7: home page redesigned around a process-scoped ancestor chain. The plugin now records every `task:after.create` payload into a `TaskSessionStore`, exposes it via a new `/api/tasks/ancestor-chain?leafId=N` endpoint, and renders the home page in a fixed top-to-bottom order: (1) ancestor chain — root leaf, each link tagged with its parent_task_id and status, (2) Task lifecycle + tools — Mermaid diagram of the leaf's tool calls, (3) Task lifecycle pipeline — operations timeline placeholder, (4) a help/legend section that intentionally follows the pipeline so the pipeline is never the trailing element. When no leaf has been recorded yet, the home page falls back to the v0.8.x cross-host tasks tree so the page is never blank. v0.8.8: home page is now a task-list view (click-to-detail). The home page renders ONLY the ancestor chain; every chain item (root → leaf, leaf included) is a clickable link to /task/<id>. The Mermaid lifecycle diagram, the operations pipeline, the tool-calls table, and the raw JSON dump all live on the per-task detail page; the home page no longer loads mermaid.js or initialises the Mermaid renderer. The leaf is no longer special-cased as <strong> (it was a 'current view' marker in v0.8.7) v0.8.8 promotes it to <a href='/task/<id>'> so the leaf is reachable via click, matching the rest of the chain. v0.8.9: home page ancestor chain rewritten as a real nested <ul>/<li> tree (24px indent per level via CSS, root/parent/→ text markers removed); detail page lifecycle pipeline section moved to immediately follow the Mermaid lifecycle diagram (was after the tool-call stats and tool calls table); extractJsonEnvelope hardened to a balanced-brace scanner so trailing CLI/plugin lifecycle log lines no longer break the JSON envelope and the /api/tasks/<id>/operations endpoint stops reporting cli_failed for normal CLI runs.",
5
+ "description": "Visualize the tool call chain of a task on a local web service with real-time SSE updates. v0.9.0: Session-scoped home page (only show tasks created after plugin load + their external ancestors), with per-row 「显示全部栏位」 toggle and lazy-loaded operations timeline; per-task Mermaid labels now correctly render CJK / mixed-Latin / emoji text (encoded as \\uXXXX before emission, decoded by the browser); detail page layout reordered to lifecycle → pipeline → stats → toolcalls → rawjson. v0.5.0+: page refreshes stream over GET /api/events (Server-Sent Events). Subscribes to tool:before.execute, tool:after.execute, task:before.create, task:after.create, task:after.complete (preferred, 2026-07-10+), and task:after.update (legacy fallback). v0.6.11: Mermaid re-rendering is delegated to a self-contained controller (public/mermaid-renderer.js) that prevents the SVG→raw-source regression on async updates and surfaces recoverable .mermaid-error states. v0.6.12: Task lifecycle pipeline (operations timeline) server now emits data-task-id on the pipeline section; client preserves it on swap, so the page actually fetches /api/tasks/<id>/operations and renders the 7-op timeline (previously silently bailed). v0.7.0: Home page redesigned as a hierarchical task tree (driven by `roy-agent tasks tree --json`); new /api/tasks/tree endpoint with status / priority / type / root-id filters, expand/collapse UI, search, and live 30s polling. v0.8.0: per-task page Mermaid area now renders the hierarchical 'Task lifecycle + tools' view — each operation record owns a subgraph that nests its tool calls, with click callbacks (`window.__toolClick`) that scroll-into-view + highlight + auto-expand the matching row in the tool-call table below. Operation record descriptions (`description` + `processDescription`) are now always rendered inline (no `<details>` collapse) so the user sees the lifecycle state at a glance; a fallback `<details>` kicks in only for descriptions longer than 600 chars. v0.8.1: hotfix for two pre-existing bugs in v0.8.0 (browser smoke test surfaced after merge). (a) Mermaid click directives were emitted as `click t1 __toolClick(1)` (missing `call` keyword) — Mermaid 10's parser rejects this with `got 'PS'`. Fixed to `click t1 call __toolClick(1)` (the v10 grammar requires `call` to invoke a callback with arguments). (b) `buildMermaidSource` lived inside the `attachTaskPageTimeline` IIFE but was also called from a listener in the `attachToolClickBridge` IIFE — sibling IIFEs cannot see each other's locals, so the listener threw `ReferenceError: buildMermaidSource is not defined` and the Mermaid diagram silently failed to re-render after `task-show:lifecycle-ops-loaded`. Fixed by hoisting the function (and its three helpers) to script top-level so both IIFEs can see it via the script-wide closure; the function is also exposed on `window.buildMermaidSource` for tests + tooling. v0.8.3: tree-display fix (Task #2426). The home page used to look like a flat list of root tasks because `autoExpandFirstLevels(..., 2)` only opened the first 2 levels — 30/47 roots were leaf nodes and the remaining 17 collapsed to one level so grandchildren were never visible. Default expand depth is now 3 (root + child + grandchild + great-grandchild are visible on first paint), the summary line now shows per-depth count pills (root / child / grandchild / great-grandchild / level-N), each `tree-row` carries a `data-depth` attribute so CSS can paint coloured left rails per level, and the duplicated 'Live tool-call sessions (legacy view)' panel that made the page look like both a flat table AND a tree is now hidden behind `#legacy-sessions[hidden]` (kept for future debug-toggle restoration). v0.8.10: bug-fix release (Task #2537 + Task #2534). (a) Heap-bounded plugin caches: OperationsCache and TasksTreeCache now enforce a hard maxEntries cap (default 256 / 64). Oldest stale entries are evicted before inserting a new one, so long-lived roy-agent sessions (BackgroundTaskManager + MemorySessionStore) no longer leak Map entries through the plugin's per-task caches see Task #2537 for the heap-unbounded-state REDGREEN repro. (b) Mermaid CJK font-family: server.ts renderTaskPage now configures mermaid.initialize({ themeVariables: { fontFamily: '\"PingFang SC\", \"Microsoft YaHei\", \"Noto Sans CJK SC\", \"Source Han Sans CN\", \"WenQuanYi Micro Hei\", sans-serif' } }) so Chinese node labels render correctly in browsers that have at least one of those fonts installed (see Task #2534).",
6
6
  "main": "dist/index.js",
7
7
  "hooks": [
8
8
  {
package/public/app.js CHANGED
@@ -247,7 +247,7 @@ function buildMermaidSource(session, operations) {
247
247
  // Top-level helpers used by buildMermaidSource. Kept here so both
248
248
  // IIFEs can call them via the script-wide closure.
249
249
  function toolLabel(t) {
250
- const head = escapeMermaid(`${t.sequence}. ${t.toolName}${t.hasAttachment ? " \ud83d\udcce" : ""}`);
250
+ const head = encodeMermaidLabelTextClient(`${t.sequence}. ${t.toolName}${t.hasAttachment ? " \ud83d\udcce" : ""}`);
251
251
  return `${head}<br/><small>${t.success ? "ok" : "FAIL"} \u00b7 ${t.durationMs}ms</small>`;
252
252
  }
253
253
 
@@ -263,6 +263,35 @@ function escapeMermaid(s) {
263
263
  return String(s).replace(/[<>"#]/g, "").replace(/[^A-Za-z0-9_.\-]/g, "_");
264
264
  }
265
265
 
266
+ /**
267
+ * v0.9.0 client-side mirror of `encodeMermaidLabelText` from
268
+ * `src/task-detail-mermaid.ts`. MUST stay byte-for-byte identical
269
+ * to the server implementation; the parity test in
270
+ * `test/mermaid-cjk-encoding-client.test.ts` enforces this.
271
+ */
272
+ function encodeMermaidLabelTextClient(s) {
273
+ var cleaned = String(s == null ? "" : s)
274
+ .replace(/["\r\n\t]+/g, " ")
275
+ .replace(/\s+/g, " ")
276
+ .trim();
277
+ var out = "";
278
+ for (var i = 0; i < cleaned.length; i++) {
279
+ var ch = cleaned[i];
280
+ var code = ch.codePointAt ? ch.codePointAt(0) : (ch.charCodeAt ? ch.charCodeAt(0) : undefined);
281
+ if (code === undefined) continue;
282
+ if (code < 0x80) { out += ch; continue; }
283
+ if (code <= 0xFFFF) {
284
+ out += "\\u" + code.toString(16).padStart(4, "0");
285
+ } else {
286
+ var v = code - 0x10000;
287
+ var hi = 0xD800 + (v >> 10);
288
+ var lo = 0xDC00 + (v & 0x3FF);
289
+ out += "\\u" + hi.toString(16).padStart(4, "0") + "\\u" + lo.toString(16).padStart(4, "0");
290
+ }
291
+ }
292
+ return out;
293
+ }
294
+
266
295
  // ---------------------------------------------------------------------------
267
296
  // v0.8.2 (fix/mermaid-readable-labels) — human-readable Mermaid
268
297
  // phase labels. This block mirrors `src/task-detail-mermaid.ts` so
@@ -481,6 +510,21 @@ function getPhaseLabel(op) {
481
510
  if (typeof window !== "undefined") {
482
511
  window.buildMermaidSource = buildMermaidSource;
483
512
  window.getPhaseLabel = getPhaseLabel;
513
+ window.encodeMermaidLabelTextClient = encodeMermaidLabelTextClient;
514
+ window.toolLabelClient = function (t) { return toolLabel(t); };
515
+ // v0.9.0 invariant: when invoked with a server encoder, the client
516
+ // encoder must produce byte-for-byte identical output. Drift is
517
+ // surfaced as a hard throw so SSR HTML and dynamic re-render stay
518
+ // in sync.
519
+ window.__cjkParityCheck = function (serverEncode) {
520
+ var cases = ["Phase 3: Plan", "\u4efb\u52a1\u7ba1\u7406", "Phase 1: \u4efb\u52a1\u7ba1\u7406"];
521
+ for (var i = 0; i < cases.length; i++) {
522
+ if (encodeMermaidLabelTextClient(cases[i]) !== serverEncode(cases[i])) {
523
+ throw new Error("CJK parity broken: " + JSON.stringify(cases[i]));
524
+ }
525
+ }
526
+ return true;
527
+ };
484
528
  }
485
529
  /**
486
530
  * v0.8.0+ Mermaid ↔ tool-table bridge.
package/public/index.html CHANGED
@@ -80,6 +80,9 @@
80
80
  </section>
81
81
 
82
82
  <script src="/static/app.js"></script>
83
+ <script src="/static/mermaid-renderer.js"></script>
83
84
  <script src="/static/tasks-tree.js"></script>
85
+ <script src="/static/task-operations.js"></script>
86
+ <script src="/static/session-forest.js"></script>
84
87
  </body>
85
88
  </html>
@@ -0,0 +1,97 @@
1
+ /**
2
+ * @fileoverview Client-side controller for the v0.9.0 session-scoped forest.
3
+ *
4
+ * - Wires each `[data-toggle="<id>"]` button to its matching
5
+ * `details[data-details="<id>"]` block (the "显示全部栏位" toggle).
6
+ * - Lazy-loads `/api/tasks/<id>/operations` into each
7
+ * `ul.session-operations[data-task-operations="<id>"]` placeholder,
8
+ * reusing `renderPipelineHtml` from `task-operations.js`.
9
+ *
10
+ * Vanilla JS, no framework dependency. Bootstraps on DOMContentLoaded.
11
+ */
12
+ (function () {
13
+ "use strict";
14
+
15
+ function esc(s) {
16
+ return String(s == null ? "" : s)
17
+ .replace(/&/g, "&amp;")
18
+ .replace(/</g, "&lt;")
19
+ .replace(/>/g, "&gt;")
20
+ .replace(/"/g, "&quot;")
21
+ .replace(/'/g, "&#39;");
22
+ }
23
+
24
+ function wireToggle(button) {
25
+ var id = button.getAttribute("data-toggle");
26
+ if (!id) return;
27
+ var target = document.querySelector('[data-details="' + cssEscape(id) + '"]');
28
+ if (!target) return;
29
+ button.addEventListener("click", function () {
30
+ target.open = !target.open;
31
+ });
32
+ }
33
+
34
+ function cssEscape(s) {
35
+ if (window.CSS && typeof window.CSS.escape === "function") {
36
+ return window.CSS.escape(s);
37
+ }
38
+ return String(s).replace(/[^a-zA-Z0-9_-]/g, function (c) {
39
+ return "\\" + c;
40
+ });
41
+ }
42
+
43
+ function loadOperationsFor(placeholder) {
44
+ var id = placeholder.getAttribute("data-task-operations");
45
+ if (!id) return;
46
+ placeholder.innerHTML = '<p class="empty">Loading operations…</p>';
47
+ fetch("/api/tasks/" + encodeURIComponent(id) + "/operations")
48
+ .then(function (r) {
49
+ if (!r.ok) throw new Error("HTTP " + r.status);
50
+ return r.json();
51
+ })
52
+ .then(function (envelope) {
53
+ if (typeof window.renderPipelineHtml === "function") {
54
+ placeholder.innerHTML = window.renderPipelineHtml(envelope.operations || [], envelope.stale, Number(id));
55
+ } else {
56
+ // Fallback: render a minimal list
57
+ var html = "<ol class=\"ops-fallback\">";
58
+ (envelope.operations || []).forEach(function (op) {
59
+ html += "<li>" + esc(op.title || op.milestoneType || "op") + "</li>";
60
+ });
61
+ html += "</ol>";
62
+ placeholder.innerHTML = html;
63
+ }
64
+ })
65
+ .catch(function (err) {
66
+ placeholder.innerHTML = '<p class="empty error">Failed to load operations: ' + esc(err && err.message || err) + '</p>';
67
+ });
68
+ }
69
+
70
+ function boot() {
71
+ var section = document.querySelector('[data-session-forest="1"]');
72
+ if (!section) return;
73
+ var buttons = section.querySelectorAll('[data-toggle]');
74
+ for (var i = 0; i < buttons.length; i++) wireToggle(buttons[i]);
75
+ var placeholders = section.querySelectorAll('[data-task-operations]');
76
+ for (var j = 0; j < placeholders.length; j++) loadOperationsFor(placeholders[j]);
77
+ }
78
+
79
+ if (typeof window !== "undefined") {
80
+ window.__sessionForestBoot = boot;
81
+ }
82
+
83
+ if (typeof document !== "undefined") {
84
+ if (document.readyState === "loading") {
85
+ document.addEventListener("DOMContentLoaded", boot);
86
+ } else {
87
+ boot();
88
+ }
89
+ } else if (typeof window !== "undefined" && window.document) {
90
+ // jsdom eval fallback: window may exist but document may not be in scope
91
+ if (window.document.readyState === "loading") {
92
+ window.document.addEventListener("DOMContentLoaded", boot);
93
+ } else {
94
+ boot();
95
+ }
96
+ }
97
+ })();