@ai-setting/roy-plugin-task-show 0.1.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 +263 -0
- package/dist/collector.d.ts +104 -0
- package/dist/collector.d.ts.map +1 -0
- package/dist/collector.js +247 -0
- package/dist/collector.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +107 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +332 -0
- package/dist/plugin.js.map +1 -0
- package/dist/server.d.ts +79 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +570 -0
- package/dist/server.js.map +1 -0
- package/dist/types.d.ts +96 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +20 -0
- package/dist/types.js.map +1 -0
- package/dist/url-injector.d.ts +53 -0
- package/dist/url-injector.d.ts.map +1 -0
- package/dist/url-injector.js +69 -0
- package/dist/url-injector.js.map +1 -0
- package/package.json +70 -0
- package/plugin.json +62 -0
- package/public/app.js +133 -0
- package/public/index.html +34 -0
- package/public/style.css +240 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Types shared across the task-show plugin.
|
|
3
|
+
*
|
|
4
|
+
* The plugin keeps its own model of what constitutes a "task solving trace"
|
|
5
|
+
* (a sequence of tool calls with metadata) and exposes helpers used by both
|
|
6
|
+
* the data collector, the HTTP server, and the visualization frontend.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* A single tool invocation captured by the `tool:after.execute` hook.
|
|
10
|
+
*
|
|
11
|
+
* Fields mirror the payload the global hook manager emits:
|
|
12
|
+
* { tool: { name, ... }, args, context, result }
|
|
13
|
+
* plus a few derived fields (timestamp, durationMs, sequence) that make the
|
|
14
|
+
* frontend rendering straightforward.
|
|
15
|
+
*/
|
|
16
|
+
export interface ToolCallRecord {
|
|
17
|
+
/** Zero-based sequence number within the task. */
|
|
18
|
+
sequence: number;
|
|
19
|
+
/** Name of the tool that was invoked (e.g. `read_file`, `bash`). */
|
|
20
|
+
toolName: string;
|
|
21
|
+
/** Arbitrary JSON-serializable arguments that were passed to the tool. */
|
|
22
|
+
args: Record<string, unknown>;
|
|
23
|
+
/** Whether the tool reported success. */
|
|
24
|
+
success: boolean;
|
|
25
|
+
/** Truncated output text. We intentionally do not store the entire result body. */
|
|
26
|
+
outputPreview: string;
|
|
27
|
+
/** Error message (if the tool failed). */
|
|
28
|
+
error?: string;
|
|
29
|
+
/** Execution duration in milliseconds. */
|
|
30
|
+
durationMs: number;
|
|
31
|
+
/** Unix ms when the call finished. */
|
|
32
|
+
timestamp: number;
|
|
33
|
+
/** Iteration index within the agent loop (if exposed by the hook context). */
|
|
34
|
+
iteration?: number;
|
|
35
|
+
/** Optional metadata bag carried over from the tool result. */
|
|
36
|
+
metadata?: Record<string, unknown>;
|
|
37
|
+
/** If the input args look like image-related content, we tag the call so the UI can render a badge. */
|
|
38
|
+
hasAttachment?: boolean;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A complete task session — the visualization root.
|
|
42
|
+
*/
|
|
43
|
+
export interface TaskSession {
|
|
44
|
+
/** Task ID as exposed by the TaskComponent / getCurrentTaskId(). */
|
|
45
|
+
taskId: number;
|
|
46
|
+
/** Human-readable title (best-effort, often empty for very short tasks). */
|
|
47
|
+
title: string;
|
|
48
|
+
/** Session start time. */
|
|
49
|
+
startedAt: number;
|
|
50
|
+
/** Session end time (set when the task transitions to a terminal status). */
|
|
51
|
+
endedAt?: number;
|
|
52
|
+
/** Current lifecycle status. */
|
|
53
|
+
status: "running" | "completed" | "failed" | "cancelled" | "unknown";
|
|
54
|
+
/** Ordered list of tool calls. */
|
|
55
|
+
toolCalls: ToolCallRecord[];
|
|
56
|
+
/** Cached URL the visualization page is served at. */
|
|
57
|
+
visualizationUrl?: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Minimal PluginEnv surface we depend on.
|
|
61
|
+
*
|
|
62
|
+
* We avoid importing private types from the host project's barrels so the
|
|
63
|
+
* plugin stays loosely coupled. Tests can substitute a fake.
|
|
64
|
+
*/
|
|
65
|
+
export interface PluginEnvLike {
|
|
66
|
+
registerHook?: (def: {
|
|
67
|
+
point: string;
|
|
68
|
+
priority?: number;
|
|
69
|
+
name?: string;
|
|
70
|
+
handler: (ctx: unknown) => unknown | Promise<unknown>;
|
|
71
|
+
}) => void;
|
|
72
|
+
getComponent?: (name: string) => unknown;
|
|
73
|
+
getConfig?: (key: string) => unknown;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Configuration consumed by the plugin.
|
|
77
|
+
*
|
|
78
|
+
* Mirrors the schema described in plugin.json (which is what the loader passes
|
|
79
|
+
* through `config`).
|
|
80
|
+
*/
|
|
81
|
+
export interface TaskShowConfig {
|
|
82
|
+
port: number;
|
|
83
|
+
host: string;
|
|
84
|
+
autoStart: boolean;
|
|
85
|
+
maxStoredTasks: number;
|
|
86
|
+
urlInjectEnabled: boolean;
|
|
87
|
+
publicDir: string;
|
|
88
|
+
/** Custom client-side logger prefix (handy for tests). */
|
|
89
|
+
logPrefix?: string;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Default configuration. Keeping these as a single source of truth simplifies
|
|
93
|
+
* the loader in plugin.json.
|
|
94
|
+
*/
|
|
95
|
+
export declare const DEFAULT_CONFIG: TaskShowConfig;
|
|
96
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;GAOG;AACH,MAAM,WAAW,cAAc;IAC7B,kDAAkD;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,yCAAyC;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,mFAAmF;IACnF,aAAa,EAAE,MAAM,CAAC;IACtB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,UAAU,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,uGAAuG;IACvG,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;IACd,0BAA0B;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,MAAM,EAAE,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,GAAG,SAAS,CAAC;IACrE,kCAAkC;IAClC,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,sDAAsD;IACtD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE;QACnB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KACvD,KAAK,IAAI,CAAC;IACX,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACzC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CACtC;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,eAAO,MAAM,cAAc,EAAE,cAO5B,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Types shared across the task-show plugin.
|
|
3
|
+
*
|
|
4
|
+
* The plugin keeps its own model of what constitutes a "task solving trace"
|
|
5
|
+
* (a sequence of tool calls with metadata) and exposes helpers used by both
|
|
6
|
+
* the data collector, the HTTP server, and the visualization frontend.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Default configuration. Keeping these as a single source of truth simplifies
|
|
10
|
+
* the loader in plugin.json.
|
|
11
|
+
*/
|
|
12
|
+
export const DEFAULT_CONFIG = {
|
|
13
|
+
port: 7788,
|
|
14
|
+
host: "127.0.0.1",
|
|
15
|
+
autoStart: true,
|
|
16
|
+
maxStoredTasks: 50,
|
|
17
|
+
urlInjectEnabled: true,
|
|
18
|
+
publicDir: "public",
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAyFH;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAmB;IAC5C,IAAI,EAAE,IAAI;IACV,IAAI,EAAE,WAAW;IACjB,SAAS,EAAE,IAAI;IACf,cAAc,EAAE,EAAE;IAClB,gBAAgB,EAAE,IAAI;IACtB,SAAS,EAAE,QAAQ;CACpB,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Mutates a ToolResult output to append a visualization URL.
|
|
3
|
+
*
|
|
4
|
+
* The roy-agent core emits ToolResult objects like:
|
|
5
|
+
* { success, output, error?, metadata? }
|
|
6
|
+
*
|
|
7
|
+
* We mutate `result.output` in-place by appending a markdown link with the
|
|
8
|
+
* per-task URL. The mutation is **non-destructive** — we never alter or trim
|
|
9
|
+
* the original content, only append after a divider so LLM agents can still
|
|
10
|
+
* parse the response.
|
|
11
|
+
*/
|
|
12
|
+
import type { TaskShowConfig } from "./types.js";
|
|
13
|
+
/**
|
|
14
|
+
* Build the public URL the visualization lives at. Exposed so that unit tests
|
|
15
|
+
* (and the plugin entry point) can produce identical strings.
|
|
16
|
+
*/
|
|
17
|
+
export declare function buildVisualizationUrl(cfg: Pick<TaskShowConfig, "host" | "port">, taskId: number | string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Optional banner linking to the index page (used when the plugin wants to
|
|
20
|
+
* mention "see all recent tasks" alongside a single URL).
|
|
21
|
+
*/
|
|
22
|
+
export declare function buildIndexUrl(cfg: Pick<TaskShowConfig, "host" | "port">): string;
|
|
23
|
+
/**
|
|
24
|
+
* Result of an injection attempt — we want callers (especially tests) to be
|
|
25
|
+
* able to verify whether a result was actually modified.
|
|
26
|
+
*/
|
|
27
|
+
export interface InjectionResult {
|
|
28
|
+
/** True iff the output text was actually mutated. */
|
|
29
|
+
mutated: boolean;
|
|
30
|
+
/** The final visualization URL we appended. */
|
|
31
|
+
url?: string;
|
|
32
|
+
/** The full output AFTER mutation. */
|
|
33
|
+
output?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Append the visualization URL to a tool result.
|
|
37
|
+
*
|
|
38
|
+
* - The mutation only runs when `enabled` is true.
|
|
39
|
+
* - The hook context shape is intentionally permissive (any) because the
|
|
40
|
+
* ToolResult shape across versions can drift slightly. We only depend on
|
|
41
|
+
* a string `output`.
|
|
42
|
+
*/
|
|
43
|
+
export declare function injectVisualizationUrl(opts: {
|
|
44
|
+
ctx?: any;
|
|
45
|
+
cfg: TaskShowConfig;
|
|
46
|
+
taskId: number;
|
|
47
|
+
}): InjectionResult;
|
|
48
|
+
/**
|
|
49
|
+
* Helper for tests / non-tool callers — given a raw string output, returns a
|
|
50
|
+
* new string with the banner appended. Does not mutate the input.
|
|
51
|
+
*/
|
|
52
|
+
export declare function appendVisualizationBanner(output: string, cfg: Pick<TaskShowConfig, "host" | "port">, taskId: number | string, extraLines?: string[]): string;
|
|
53
|
+
//# sourceMappingURL=url-injector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url-injector.d.ts","sourceRoot":"","sources":["../src/url-injector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAAC,EAC1C,MAAM,EAAE,MAAM,GAAG,MAAM,GACtB,MAAM,CAIR;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAAC,GACzC,MAAM,CAGR;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,qDAAqD;IACrD,OAAO,EAAE,OAAO,CAAC;IACjB,+CAA+C;IAC/C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,GAAG,EAAE,cAAc,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,eAAe,CAyBlB;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAAC,EAC1C,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,UAAU,GAAE,MAAM,EAAO,GACxB,MAAM,CAMR"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Mutates a ToolResult output to append a visualization URL.
|
|
3
|
+
*
|
|
4
|
+
* The roy-agent core emits ToolResult objects like:
|
|
5
|
+
* { success, output, error?, metadata? }
|
|
6
|
+
*
|
|
7
|
+
* We mutate `result.output` in-place by appending a markdown link with the
|
|
8
|
+
* per-task URL. The mutation is **non-destructive** — we never alter or trim
|
|
9
|
+
* the original content, only append after a divider so LLM agents can still
|
|
10
|
+
* parse the response.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Build the public URL the visualization lives at. Exposed so that unit tests
|
|
14
|
+
* (and the plugin entry point) can produce identical strings.
|
|
15
|
+
*/
|
|
16
|
+
export function buildVisualizationUrl(cfg, taskId) {
|
|
17
|
+
// Pretty prefix shown to humans: http(s)://host:port
|
|
18
|
+
const host = cfg.host === "0.0.0.0" ? "localhost" : cfg.host;
|
|
19
|
+
return `http://${host}:${cfg.port}/task/${encodeURIComponent(String(taskId))}`;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Optional banner linking to the index page (used when the plugin wants to
|
|
23
|
+
* mention "see all recent tasks" alongside a single URL).
|
|
24
|
+
*/
|
|
25
|
+
export function buildIndexUrl(cfg) {
|
|
26
|
+
const host = cfg.host === "0.0.0.0" ? "localhost" : cfg.host;
|
|
27
|
+
return `http://${host}:${cfg.port}/`;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Append the visualization URL to a tool result.
|
|
31
|
+
*
|
|
32
|
+
* - The mutation only runs when `enabled` is true.
|
|
33
|
+
* - The hook context shape is intentionally permissive (any) because the
|
|
34
|
+
* ToolResult shape across versions can drift slightly. We only depend on
|
|
35
|
+
* a string `output`.
|
|
36
|
+
*/
|
|
37
|
+
export function injectVisualizationUrl(opts) {
|
|
38
|
+
const { ctx, cfg, taskId } = opts;
|
|
39
|
+
if (!cfg.urlInjectEnabled)
|
|
40
|
+
return { mutated: false };
|
|
41
|
+
if (!ctx)
|
|
42
|
+
return { mutated: false };
|
|
43
|
+
const result = ctx?.result ?? ctx?.toolResult ?? null;
|
|
44
|
+
if (!result || typeof result.output !== "string") {
|
|
45
|
+
return { mutated: false };
|
|
46
|
+
}
|
|
47
|
+
const url = buildVisualizationUrl(cfg, taskId);
|
|
48
|
+
const banner = `\n\n---\n📊 **可视化工具调用链路**: <${url}>\n` +
|
|
49
|
+
`(浏览器访问即可查看 mermaid 流程图:每个工具调用、参数、结果摘要、耗时)`;
|
|
50
|
+
const newOutput = result.output + banner;
|
|
51
|
+
result.output = newOutput;
|
|
52
|
+
// Make sure the URL is also recorded in metadata — some chat clients
|
|
53
|
+
// prefer to render markdown from a structured field.
|
|
54
|
+
if (typeof result.metadata === "object" && result.metadata !== null) {
|
|
55
|
+
result.metadata.task_visualization_url = url;
|
|
56
|
+
}
|
|
57
|
+
return { mutated: true, url, output: newOutput };
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Helper for tests / non-tool callers — given a raw string output, returns a
|
|
61
|
+
* new string with the banner appended. Does not mutate the input.
|
|
62
|
+
*/
|
|
63
|
+
export function appendVisualizationBanner(output, cfg, taskId, extraLines = []) {
|
|
64
|
+
const url = buildVisualizationUrl(cfg, taskId);
|
|
65
|
+
const banner = `\n\n---\n📊 **可视化工具调用链路**: <${url}>` +
|
|
66
|
+
(extraLines.length ? "\n" + extraLines.map((l) => `> ${l}`).join("\n") : "");
|
|
67
|
+
return output + banner;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=url-injector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url-injector.js","sourceRoot":"","sources":["../src/url-injector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CACnC,GAA0C,EAC1C,MAAuB;IAEvB,qDAAqD;IACrD,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;IAC7D,OAAO,UAAU,IAAI,IAAI,GAAG,CAAC,IAAI,SAAS,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;AACjF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAC3B,GAA0C;IAE1C,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;IAC7D,OAAO,UAAU,IAAI,IAAI,GAAG,CAAC,IAAI,GAAG,CAAC;AACvC,CAAC;AAeD;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAItC;IACC,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAElC,IAAI,CAAC,GAAG,CAAC,gBAAgB;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACrD,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAEpC,MAAM,MAAM,GAAG,GAAG,EAAE,MAAM,IAAI,GAAG,EAAE,UAAU,IAAI,IAAI,CAAC;IACtD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;QACjD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC5B,CAAC;IAED,MAAM,GAAG,GAAG,qBAAqB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC/C,MAAM,MAAM,GACV,+BAA+B,GAAG,KAAK;QACvC,2CAA2C,CAAC;IAC9C,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;IACzC,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC;IAE1B,qEAAqE;IACrE,qDAAqD;IACrD,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;QACnE,MAAM,CAAC,QAAoC,CAAC,sBAAsB,GAAG,GAAG,CAAC;IAC5E,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AACnD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CACvC,MAAc,EACd,GAA0C,EAC1C,MAAuB,EACvB,aAAuB,EAAE;IAEzB,MAAM,GAAG,GAAG,qBAAqB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC/C,MAAM,MAAM,GACV,+BAA+B,GAAG,GAAG;QACrC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC/E,OAAO,MAAM,GAAG,MAAM,CAAC;AACzB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ai-setting/roy-plugin-task-show",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "roy-agent plugin: visualize task solving process via tool call flow on a local web service",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"module": "dist/index.js",
|
|
8
|
+
"types": "dist/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"public",
|
|
12
|
+
"plugin.json",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc -p tsconfig.json",
|
|
17
|
+
"dev": "tsc -p tsconfig.json --watch",
|
|
18
|
+
"test": "bun test",
|
|
19
|
+
"start:demo": "bun run scripts/run-demo.ts",
|
|
20
|
+
"verify": "bun run scripts/verify-service.ts",
|
|
21
|
+
"publish:local": "bun run scripts/publish.ts --dry-run --verify",
|
|
22
|
+
"publish:dry": "bun run scripts/publish.ts --dry-run",
|
|
23
|
+
"publish": "bun run scripts/publish.ts --tag latest",
|
|
24
|
+
"publish:verify": "bun run scripts/publish.ts --tag latest --verify"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"roy-agent",
|
|
28
|
+
"plugin",
|
|
29
|
+
"visualization",
|
|
30
|
+
"task-show",
|
|
31
|
+
"mermaid",
|
|
32
|
+
"tools",
|
|
33
|
+
"hook"
|
|
34
|
+
],
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=18",
|
|
37
|
+
"bun": ">=1.0.0"
|
|
38
|
+
},
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"dependencies": {},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/bun": "latest",
|
|
43
|
+
"@types/node": "^20.0.0",
|
|
44
|
+
"typescript": "^5.4.0"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"@ai-setting/roy-agent-core": "*"
|
|
48
|
+
},
|
|
49
|
+
"peerDependenciesMeta": {
|
|
50
|
+
"@ai-setting/roy-agent-core": {
|
|
51
|
+
"optional": true
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public",
|
|
56
|
+
"registry": "https://registry.npmjs.org/"
|
|
57
|
+
},
|
|
58
|
+
"repository": {
|
|
59
|
+
"type": "git",
|
|
60
|
+
"url": "git+https://github.com/ai-setting/roy-plugin-task-visualize.git"
|
|
61
|
+
},
|
|
62
|
+
"homepage": "https://github.com/ai-setting/roy-plugin-task-visualize#readme",
|
|
63
|
+
"bugs": {
|
|
64
|
+
"url": "https://github.com/ai-setting/roy-plugin-task-visualize/issues"
|
|
65
|
+
},
|
|
66
|
+
"author": {
|
|
67
|
+
"name": "ai-setting",
|
|
68
|
+
"email": "ai-setting@users.noreply.github.com"
|
|
69
|
+
}
|
|
70
|
+
}
|
package/plugin.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ai-setting/roy-plugin-task-show",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "tool-plugin",
|
|
5
|
+
"description": "Visualize the tool call chain of a task on a local web service and inject the URL into tool results",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"hooks": [
|
|
8
|
+
{
|
|
9
|
+
"point": "tool:after.execute",
|
|
10
|
+
"purpose": "Collect every tool invocation: tool name, args, result, timing, success"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"point": "task:after.update",
|
|
14
|
+
"purpose": "When a task transitions to a terminal status (completed/failed), mark the session and emit the visualization URL"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"commands": [
|
|
18
|
+
{
|
|
19
|
+
"name": "task-show",
|
|
20
|
+
"description": "Open the local visualization service in your browser (alias of the configured port)"
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"config": {
|
|
24
|
+
"port": {
|
|
25
|
+
"type": "number",
|
|
26
|
+
"default": 7788,
|
|
27
|
+
"description": "HTTP port the local visualization service binds to"
|
|
28
|
+
},
|
|
29
|
+
"host": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"default": "127.0.0.1",
|
|
32
|
+
"description": "HTTP host the local visualization service binds to"
|
|
33
|
+
},
|
|
34
|
+
"autoStart": {
|
|
35
|
+
"type": "boolean",
|
|
36
|
+
"default": true,
|
|
37
|
+
"description": "Start the HTTP server when the plugin initializes"
|
|
38
|
+
},
|
|
39
|
+
"maxStoredTasks": {
|
|
40
|
+
"type": "number",
|
|
41
|
+
"default": 50,
|
|
42
|
+
"description": "Maximum number of completed tasks to keep in memory (older ones are evicted)"
|
|
43
|
+
},
|
|
44
|
+
"urlInjectEnabled": {
|
|
45
|
+
"type": "boolean",
|
|
46
|
+
"default": true,
|
|
47
|
+
"description": "Append a \ud83d\udcca visualization URL to the last tool result when a task completes"
|
|
48
|
+
},
|
|
49
|
+
"publicDir": {
|
|
50
|
+
"type": "string",
|
|
51
|
+
"default": "public",
|
|
52
|
+
"description": "Directory holding the static visualization frontend (HTML/JS/CSS)"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"roy-agent": {
|
|
56
|
+
"minVersion": "0.6.0",
|
|
57
|
+
"extends": "BasePlugin",
|
|
58
|
+
"registerAs": "task-show"
|
|
59
|
+
},
|
|
60
|
+
"repository": "github:ai-setting/roy-plugin-task-visualize",
|
|
61
|
+
"bugs": "https://github.com/ai-setting/roy-plugin-task-visualize/issues"
|
|
62
|
+
}
|
package/public/app.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/* ------------------------------------------------------------------------- */
|
|
2
|
+
/* roy-plugin-task-show — small progressive enhancements */
|
|
3
|
+
/* ------------------------------------------------------------------------- */
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Allow each <tr data-args="..."> to expand a JSON dialog on click.
|
|
7
|
+
* `data-args` carries a single-line JSON string; we pretty-print it on demand.
|
|
8
|
+
*/
|
|
9
|
+
(function attachArgsExpander() {
|
|
10
|
+
const rows = document.querySelectorAll('tr[data-args]');
|
|
11
|
+
rows.forEach((row) => {
|
|
12
|
+
row.style.cursor = "zoom-in";
|
|
13
|
+
row.addEventListener("click", () => {
|
|
14
|
+
const raw = row.getAttribute("data-args") || "";
|
|
15
|
+
let parsed;
|
|
16
|
+
try {
|
|
17
|
+
parsed = JSON.parse(raw);
|
|
18
|
+
} catch {
|
|
19
|
+
parsed = raw;
|
|
20
|
+
}
|
|
21
|
+
openDialog({
|
|
22
|
+
title: row.querySelector("td:nth-child(2)")?.textContent?.trim() || "Tool call",
|
|
23
|
+
body: JSON.stringify(parsed, null, 2),
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
function openDialog({ title, body }) {
|
|
29
|
+
const overlay = document.createElement("div");
|
|
30
|
+
overlay.style.cssText = [
|
|
31
|
+
"position:fixed",
|
|
32
|
+
"inset:0",
|
|
33
|
+
"background:rgba(0,0,0,0.6)",
|
|
34
|
+
"display:flex",
|
|
35
|
+
"align-items:center",
|
|
36
|
+
"justify-content:center",
|
|
37
|
+
"z-index:9999",
|
|
38
|
+
"padding:24px",
|
|
39
|
+
].join(";");
|
|
40
|
+
const box = document.createElement("div");
|
|
41
|
+
box.style.cssText = [
|
|
42
|
+
"background:#0f172a",
|
|
43
|
+
"color:#e2e8f0",
|
|
44
|
+
"border-radius:10px",
|
|
45
|
+
"padding:18px",
|
|
46
|
+
"max-width:780px",
|
|
47
|
+
"width:100%",
|
|
48
|
+
"max-height:80vh",
|
|
49
|
+
"overflow:auto",
|
|
50
|
+
"box-shadow:0 12px 40px rgba(0,0,0,0.5)",
|
|
51
|
+
].join(";");
|
|
52
|
+
box.innerHTML =
|
|
53
|
+
`<header style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px">` +
|
|
54
|
+
`<strong style="font-size:15px">${escapeHtml(title)}</strong>` +
|
|
55
|
+
`<button id="dlg-close" style="background:transparent;color:#94a3b8;border:none;font-size:18px;cursor:pointer">×</button>` +
|
|
56
|
+
`</header>` +
|
|
57
|
+
`<pre style="white-space:pre-wrap">${escapeHtml(body)}</pre>`;
|
|
58
|
+
overlay.appendChild(box);
|
|
59
|
+
overlay.addEventListener("click", (e) => {
|
|
60
|
+
if (e.target === overlay) close();
|
|
61
|
+
});
|
|
62
|
+
box.querySelector("#dlg-close").addEventListener("click", close);
|
|
63
|
+
document.body.appendChild(overlay);
|
|
64
|
+
|
|
65
|
+
function close() {
|
|
66
|
+
overlay.remove();
|
|
67
|
+
document.removeEventListener("keydown", onKey);
|
|
68
|
+
}
|
|
69
|
+
function onKey(e) {
|
|
70
|
+
if (e.key === "Escape") close();
|
|
71
|
+
}
|
|
72
|
+
document.addEventListener("keydown", onKey);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function escapeHtml(s) {
|
|
76
|
+
return String(s)
|
|
77
|
+
.replace(/&/g, "&")
|
|
78
|
+
.replace(/</g, "<")
|
|
79
|
+
.replace(/>/g, ">")
|
|
80
|
+
.replace(/"/g, """)
|
|
81
|
+
.replace(/'/g, "'");
|
|
82
|
+
}
|
|
83
|
+
})();
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Poll the session summary every 3 seconds while a task is running so the
|
|
87
|
+
* index page (and the per-task page when status changes) updates without a
|
|
88
|
+
* manual refresh. Lightweight: single GET, no debouncing.
|
|
89
|
+
*/
|
|
90
|
+
(function attachLiveRefresh() {
|
|
91
|
+
const banner = document.querySelector('[data-live-refresh]');
|
|
92
|
+
if (!banner) return;
|
|
93
|
+
const taskId = banner.getAttribute('data-task-id') || '';
|
|
94
|
+
const initialStatus = banner.getAttribute('data-status') || 'running';
|
|
95
|
+
|
|
96
|
+
async function poll() {
|
|
97
|
+
try {
|
|
98
|
+
const url = taskId ? `/api/sessions/${encodeURIComponent(taskId)}` : '/api/sessions';
|
|
99
|
+
const res = await fetch(url, { headers: { accept: 'application/json' } });
|
|
100
|
+
if (!res.ok) return;
|
|
101
|
+
const data = await res.json();
|
|
102
|
+
if (Array.isArray(data)) {
|
|
103
|
+
// Index page — refresh the table cells without losing scroll position.
|
|
104
|
+
const tbody = document.querySelector('.sessions tbody');
|
|
105
|
+
if (!tbody) return;
|
|
106
|
+
tbody.innerHTML = data
|
|
107
|
+
.map((s) => {
|
|
108
|
+
const tools = Array.from(new Set((s.toolCalls || []).map((c) => c.toolName))).map((t) => `<code>${t}</code>`).join(' ');
|
|
109
|
+
return (
|
|
110
|
+
'<tr>' +
|
|
111
|
+
`<td><a href="/task/${s.taskId}">#${s.taskId}</a></td>` +
|
|
112
|
+
`<td><span class="badge badge-${s.status}">${s.status}</span></td>` +
|
|
113
|
+
`<td>${(s.toolCalls || []).length} calls · ${(s.toolCalls || []).reduce((a, c) => a + c.durationMs, 0)} ms</td>` +
|
|
114
|
+
`<td>${(s.title || '(no title)')}</td>` +
|
|
115
|
+
`<td>${new Date(s.startedAt).toISOString().replace('T', ' ').slice(0, 19)}</td>` +
|
|
116
|
+
`<td class="tools">${tools}</td>` +
|
|
117
|
+
'</tr>'
|
|
118
|
+
);
|
|
119
|
+
})
|
|
120
|
+
.join('');
|
|
121
|
+
} else if (data && data.taskId != null) {
|
|
122
|
+
if (data.status !== initialStatus) {
|
|
123
|
+
// Reload to pick up the new status badge & flow chart.
|
|
124
|
+
location.reload();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
} catch (e) {
|
|
128
|
+
// network blip — try again next tick
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
setInterval(poll, 3000);
|
|
133
|
+
})();
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<title>roy-plugin-task-show</title>
|
|
6
|
+
<link rel="stylesheet" href="/static/style.css" />
|
|
7
|
+
</head>
|
|
8
|
+
<body data-live-refresh>
|
|
9
|
+
<header class="topbar">
|
|
10
|
+
<h1>📊 roy-plugin-task-show</h1>
|
|
11
|
+
<p class="meta">
|
|
12
|
+
Standalone landing page served by the visualization service. The plugin
|
|
13
|
+
replaces the index when sessions exist.
|
|
14
|
+
</p>
|
|
15
|
+
</header>
|
|
16
|
+
<section class="panel">
|
|
17
|
+
<h2>No sessions yet</h2>
|
|
18
|
+
<p class="empty">
|
|
19
|
+
The <code>roy-agent</code> host process has not recorded any tool
|
|
20
|
+
calls yet. Trigger a session — for example by sending a message in
|
|
21
|
+
<code>interactive</code> mode — and refresh this page.
|
|
22
|
+
</p>
|
|
23
|
+
<p>
|
|
24
|
+
Useful endpoints while you wait:
|
|
25
|
+
<ul>
|
|
26
|
+
<li><a href="/api/sessions">/api/sessions</a> — JSON list of recorded sessions.</li>
|
|
27
|
+
<li><a href="/static/style.css">/static/style.css</a> — bundled CSS.</li>
|
|
28
|
+
<li><a href="/static/app.js">/static/app.js</a> — bundled JS.</li>
|
|
29
|
+
</ul>
|
|
30
|
+
</p>
|
|
31
|
+
</section>
|
|
32
|
+
<script src="/static/app.js"></script>
|
|
33
|
+
</body>
|
|
34
|
+
</html>
|