@unigent/cli 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +470 -0
  3. package/dist/cli.js +132 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/command.js +44 -0
  6. package/dist/command.js.map +1 -0
  7. package/dist/environment.js +20 -0
  8. package/dist/environment.js.map +1 -0
  9. package/dist/error_message.js +59 -0
  10. package/dist/error_message.js.map +1 -0
  11. package/dist/format.js +38 -0
  12. package/dist/format.js.map +1 -0
  13. package/dist/node_version.js +9 -0
  14. package/dist/node_version.js.map +1 -0
  15. package/dist/protocol.js +72 -0
  16. package/dist/protocol.js.map +1 -0
  17. package/dist/register.js +78 -0
  18. package/dist/register.js.map +1 -0
  19. package/dist/script_runtime.js +29 -0
  20. package/dist/script_runtime.js.map +1 -0
  21. package/dist/tui/app.js +453 -0
  22. package/dist/tui/app.js.map +1 -0
  23. package/dist/tui/clipboard.js +31 -0
  24. package/dist/tui/clipboard.js.map +1 -0
  25. package/dist/tui/runner.js +116 -0
  26. package/dist/tui/runner.js.map +1 -0
  27. package/dist/tui/scroll.js +33 -0
  28. package/dist/tui/scroll.js.map +1 -0
  29. package/dist/tui/store.js +99 -0
  30. package/dist/tui/store.js.map +1 -0
  31. package/dist/tui/theme.js +35 -0
  32. package/dist/tui/theme.js.map +1 -0
  33. package/dist/tui/tree.js +267 -0
  34. package/dist/tui/tree.js.map +1 -0
  35. package/dist/tui.js +63 -0
  36. package/dist/tui.js.map +1 -0
  37. package/node_modules/@unigent/core/dist/adapter_process.d.ts +25 -0
  38. package/node_modules/@unigent/core/dist/adapter_process.d.ts.map +1 -0
  39. package/node_modules/@unigent/core/dist/adapter_process.js +46 -0
  40. package/node_modules/@unigent/core/dist/adapter_process.js.map +1 -0
  41. package/node_modules/@unigent/core/dist/args.d.ts +17 -0
  42. package/node_modules/@unigent/core/dist/args.d.ts.map +1 -0
  43. package/node_modules/@unigent/core/dist/args.js +233 -0
  44. package/node_modules/@unigent/core/dist/args.js.map +1 -0
  45. package/node_modules/@unigent/core/dist/backend.d.ts +83 -0
  46. package/node_modules/@unigent/core/dist/backend.d.ts.map +1 -0
  47. package/node_modules/@unigent/core/dist/backend.js +2 -0
  48. package/node_modules/@unigent/core/dist/backend.js.map +1 -0
  49. package/node_modules/@unigent/core/dist/checkpoint.d.ts +26 -0
  50. package/node_modules/@unigent/core/dist/checkpoint.d.ts.map +1 -0
  51. package/node_modules/@unigent/core/dist/checkpoint.js +107 -0
  52. package/node_modules/@unigent/core/dist/checkpoint.js.map +1 -0
  53. package/node_modules/@unigent/core/dist/checkpoint_fingerprint.d.ts +4 -0
  54. package/node_modules/@unigent/core/dist/checkpoint_fingerprint.d.ts.map +1 -0
  55. package/node_modules/@unigent/core/dist/checkpoint_fingerprint.js +41 -0
  56. package/node_modules/@unigent/core/dist/checkpoint_fingerprint.js.map +1 -0
  57. package/node_modules/@unigent/core/dist/completion.d.ts +9 -0
  58. package/node_modules/@unigent/core/dist/completion.d.ts.map +1 -0
  59. package/node_modules/@unigent/core/dist/completion.js +4 -0
  60. package/node_modules/@unigent/core/dist/completion.js.map +1 -0
  61. package/node_modules/@unigent/core/dist/errors.d.ts +43 -0
  62. package/node_modules/@unigent/core/dist/errors.d.ts.map +1 -0
  63. package/node_modules/@unigent/core/dist/errors.js +48 -0
  64. package/node_modules/@unigent/core/dist/errors.js.map +1 -0
  65. package/node_modules/@unigent/core/dist/events.d.ts +101 -0
  66. package/node_modules/@unigent/core/dist/events.d.ts.map +1 -0
  67. package/node_modules/@unigent/core/dist/events.js +105 -0
  68. package/node_modules/@unigent/core/dist/events.js.map +1 -0
  69. package/node_modules/@unigent/core/dist/index.d.ts +19 -0
  70. package/node_modules/@unigent/core/dist/index.d.ts.map +1 -0
  71. package/node_modules/@unigent/core/dist/index.js +10 -0
  72. package/node_modules/@unigent/core/dist/index.js.map +1 -0
  73. package/node_modules/@unigent/core/dist/json_schema_validator.d.ts +8 -0
  74. package/node_modules/@unigent/core/dist/json_schema_validator.d.ts.map +1 -0
  75. package/node_modules/@unigent/core/dist/json_schema_validator.js +27 -0
  76. package/node_modules/@unigent/core/dist/json_schema_validator.js.map +1 -0
  77. package/node_modules/@unigent/core/dist/mcp.d.ts +12 -0
  78. package/node_modules/@unigent/core/dist/mcp.d.ts.map +1 -0
  79. package/node_modules/@unigent/core/dist/mcp.js +117 -0
  80. package/node_modules/@unigent/core/dist/mcp.js.map +1 -0
  81. package/node_modules/@unigent/core/dist/runtime.d.ts +109 -0
  82. package/node_modules/@unigent/core/dist/runtime.d.ts.map +1 -0
  83. package/node_modules/@unigent/core/dist/runtime.js +976 -0
  84. package/node_modules/@unigent/core/dist/runtime.js.map +1 -0
  85. package/node_modules/@unigent/core/dist/schema.d.ts +15 -0
  86. package/node_modules/@unigent/core/dist/schema.d.ts.map +1 -0
  87. package/node_modules/@unigent/core/dist/schema.js +71 -0
  88. package/node_modules/@unigent/core/dist/schema.js.map +1 -0
  89. package/node_modules/@unigent/core/dist/source_tools.d.ts +7 -0
  90. package/node_modules/@unigent/core/dist/source_tools.d.ts.map +1 -0
  91. package/node_modules/@unigent/core/dist/source_tools.js +450 -0
  92. package/node_modules/@unigent/core/dist/source_tools.js.map +1 -0
  93. package/node_modules/@unigent/core/dist/tool.d.ts +41 -0
  94. package/node_modules/@unigent/core/dist/tool.d.ts.map +1 -0
  95. package/node_modules/@unigent/core/dist/tool.js +43 -0
  96. package/node_modules/@unigent/core/dist/tool.js.map +1 -0
  97. package/node_modules/@unigent/core/dist/tools.d.ts +7 -0
  98. package/node_modules/@unigent/core/dist/tools.d.ts.map +1 -0
  99. package/node_modules/@unigent/core/dist/tools.js +3 -0
  100. package/node_modules/@unigent/core/dist/tools.js.map +1 -0
  101. package/node_modules/@unigent/core/dist/trace.d.ts +82 -0
  102. package/node_modules/@unigent/core/dist/trace.d.ts.map +1 -0
  103. package/node_modules/@unigent/core/dist/trace.js +235 -0
  104. package/node_modules/@unigent/core/dist/trace.js.map +1 -0
  105. package/node_modules/@unigent/core/dist/usage.d.ts +26 -0
  106. package/node_modules/@unigent/core/dist/usage.d.ts.map +1 -0
  107. package/node_modules/@unigent/core/dist/usage.js +47 -0
  108. package/node_modules/@unigent/core/dist/usage.js.map +1 -0
  109. package/node_modules/@unigent/core/package.json +68 -0
  110. package/package.json +68 -0
@@ -0,0 +1,116 @@
1
+ import { spawn } from "node:child_process";
2
+ import { createInterface } from "node:readline";
3
+ import { Readable } from "node:stream";
4
+ import { childProcessEnvironment } from "../environment.js";
5
+ import { parseTraceRecord, TRACE_TRANSPORT_ENVIRONMENT_VARIABLE } from "../protocol.js";
6
+ const INTERRUPT_GRACE_MILLISECONDS = 1500;
7
+ function attachChildStreams(child, currentGeneration, activeGeneration, store) {
8
+ child.stdout?.on("data", (chunk) => {
9
+ if (currentGeneration === activeGeneration()) {
10
+ store.pushOutput("stdout", chunk.toString("utf8"));
11
+ }
12
+ });
13
+ child.stderr?.on("data", (chunk) => {
14
+ if (currentGeneration === activeGeneration()) {
15
+ store.pushOutput("stderr", chunk.toString("utf8"));
16
+ }
17
+ });
18
+ const [, , , traceOutput] = child.stdio;
19
+ if (!(traceOutput instanceof Readable)) {
20
+ return;
21
+ }
22
+ const lines = createInterface({ input: traceOutput, crlfDelay: Number.POSITIVE_INFINITY });
23
+ lines.on("line", (line) => {
24
+ if (currentGeneration !== activeGeneration()) {
25
+ return;
26
+ }
27
+ const record = parseTraceRecord(line);
28
+ if (record !== undefined) {
29
+ store.pushEvent(record.event);
30
+ }
31
+ });
32
+ }
33
+ function settleChild(store, abortRequested, code, signal) {
34
+ if (abortRequested || signal === "SIGINT" || signal === "SIGKILL") {
35
+ store.settle("cancelled");
36
+ }
37
+ else if (code === 0) {
38
+ store.settle("succeeded");
39
+ }
40
+ else {
41
+ store.settle("failed", `Script exited with code ${code ?? 1}`);
42
+ }
43
+ }
44
+ function createScriptRunner(options, store) {
45
+ let activeChild;
46
+ let generation = 0;
47
+ let abortRequested = false;
48
+ let forceTimer;
49
+ const clearForceTimer = () => {
50
+ if (forceTimer !== undefined) {
51
+ clearTimeout(forceTimer);
52
+ forceTimer = undefined;
53
+ }
54
+ };
55
+ const stop = (force) => {
56
+ if (activeChild === undefined || activeChild.exitCode !== null) {
57
+ return;
58
+ }
59
+ activeChild.kill(force ? "SIGKILL" : "SIGINT");
60
+ if (!force) {
61
+ abortRequested = true;
62
+ clearForceTimer();
63
+ forceTimer = setTimeout(() => stop(true), INTERRUPT_GRACE_MILLISECONDS);
64
+ }
65
+ };
66
+ const start = () => {
67
+ stop(true);
68
+ clearForceTimer();
69
+ generation += 1;
70
+ abortRequested = false;
71
+ const currentGeneration = generation;
72
+ store.start();
73
+ const developmentPreload = options.developmentLoader !== undefined && options.runtime.kind === "node"
74
+ ? ["--import", options.developmentLoader]
75
+ : [];
76
+ const child = spawn(options.runtime.executable, [
77
+ ...options.runtime.arguments,
78
+ ...developmentPreload,
79
+ "--import",
80
+ options.registerEntry,
81
+ options.sourceFile,
82
+ ...options.scriptArguments,
83
+ ], {
84
+ env: childProcessEnvironment({
85
+ [TRACE_TRANSPORT_ENVIRONMENT_VARIABLE]: "3",
86
+ }),
87
+ stdio: ["ignore", "pipe", "pipe", "pipe"],
88
+ });
89
+ activeChild = child;
90
+ attachChildStreams(child, currentGeneration, () => generation, store);
91
+ child.once("error", (error) => {
92
+ if (currentGeneration === generation) {
93
+ store.settle("failed", error.message);
94
+ }
95
+ });
96
+ child.once("exit", (code, signal) => {
97
+ if (currentGeneration !== generation) {
98
+ return;
99
+ }
100
+ clearForceTimer();
101
+ activeChild = undefined;
102
+ settleChild(store, abortRequested, code, signal);
103
+ });
104
+ };
105
+ return {
106
+ start,
107
+ abort: () => stop(false),
108
+ dispose: () => {
109
+ generation += 1;
110
+ stop(true);
111
+ clearForceTimer();
112
+ },
113
+ };
114
+ }
115
+ export { createScriptRunner };
116
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/tui/runner.ts"],"names":[],"mappings":"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","sourcesContent":["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { createInterface } from \"node:readline\";\nimport { Readable } from \"node:stream\";\nimport { childProcessEnvironment } from \"../environment.js\";\nimport { parseTraceRecord, TRACE_TRANSPORT_ENVIRONMENT_VARIABLE } from \"../protocol.js\";\nimport type { RuntimeInvocation } from \"../script_runtime.js\";\nimport type { TuiStore } from \"./store.js\";\n\ninterface ScriptRunnerOptions {\n readonly runtime: RuntimeInvocation;\n readonly registerEntry: string;\n readonly developmentLoader: string | undefined;\n readonly sourceFile: string;\n readonly scriptArguments: readonly string[];\n}\n\ninterface ScriptRunner {\n readonly start: () => void;\n readonly abort: () => void;\n readonly dispose: () => void;\n}\n\nconst INTERRUPT_GRACE_MILLISECONDS = 1500;\n\nfunction attachChildStreams(\n child: ChildProcess,\n currentGeneration: number,\n activeGeneration: () => number,\n store: TuiStore,\n): void {\n child.stdout?.on(\"data\", (chunk: Buffer) => {\n if (currentGeneration === activeGeneration()) {\n store.pushOutput(\"stdout\", chunk.toString(\"utf8\"));\n }\n });\n child.stderr?.on(\"data\", (chunk: Buffer) => {\n if (currentGeneration === activeGeneration()) {\n store.pushOutput(\"stderr\", chunk.toString(\"utf8\"));\n }\n });\n const [, , , traceOutput] = child.stdio;\n if (!(traceOutput instanceof Readable)) {\n return;\n }\n const lines = createInterface({ input: traceOutput, crlfDelay: Number.POSITIVE_INFINITY });\n lines.on(\"line\", (line) => {\n if (currentGeneration !== activeGeneration()) {\n return;\n }\n const record = parseTraceRecord(line);\n if (record !== undefined) {\n store.pushEvent(record.event);\n }\n });\n}\n\nfunction settleChild(\n store: TuiStore,\n abortRequested: boolean,\n code: number | null,\n signal: NodeJS.Signals | null,\n): void {\n if (abortRequested || signal === \"SIGINT\" || signal === \"SIGKILL\") {\n store.settle(\"cancelled\");\n } else if (code === 0) {\n store.settle(\"succeeded\");\n } else {\n store.settle(\"failed\", `Script exited with code ${code ?? 1}`);\n }\n}\n\nfunction createScriptRunner(options: ScriptRunnerOptions, store: TuiStore): ScriptRunner {\n let activeChild: ChildProcess | undefined;\n let generation = 0;\n let abortRequested = false;\n let forceTimer: ReturnType<typeof setTimeout> | undefined;\n\n const clearForceTimer = (): void => {\n if (forceTimer !== undefined) {\n clearTimeout(forceTimer);\n forceTimer = undefined;\n }\n };\n\n const stop = (force: boolean): void => {\n if (activeChild === undefined || activeChild.exitCode !== null) {\n return;\n }\n activeChild.kill(force ? \"SIGKILL\" : \"SIGINT\");\n if (!force) {\n abortRequested = true;\n clearForceTimer();\n forceTimer = setTimeout(() => stop(true), INTERRUPT_GRACE_MILLISECONDS);\n }\n };\n\n const start = (): void => {\n stop(true);\n clearForceTimer();\n generation += 1;\n abortRequested = false;\n const currentGeneration = generation;\n store.start();\n const developmentPreload =\n options.developmentLoader !== undefined && options.runtime.kind === \"node\"\n ? [\"--import\", options.developmentLoader]\n : [];\n const child = spawn(\n options.runtime.executable,\n [\n ...options.runtime.arguments,\n ...developmentPreload,\n \"--import\",\n options.registerEntry,\n options.sourceFile,\n ...options.scriptArguments,\n ],\n {\n env: childProcessEnvironment({\n [TRACE_TRANSPORT_ENVIRONMENT_VARIABLE]: \"3\",\n }),\n stdio: [\"ignore\", \"pipe\", \"pipe\", \"pipe\"],\n },\n );\n activeChild = child;\n attachChildStreams(child, currentGeneration, () => generation, store);\n child.once(\"error\", (error) => {\n if (currentGeneration === generation) {\n store.settle(\"failed\", error.message);\n }\n });\n child.once(\"exit\", (code, signal) => {\n if (currentGeneration !== generation) {\n return;\n }\n clearForceTimer();\n activeChild = undefined;\n settleChild(store, abortRequested, code, signal);\n });\n };\n\n return {\n start,\n abort: (): void => stop(false),\n dispose: (): void => {\n generation += 1;\n stop(true);\n clearForceTimer();\n },\n };\n}\n\nexport { createScriptRunner };\n"]}
@@ -0,0 +1,33 @@
1
+ const DEFAULT_MAXIMUM_LINES = 14;
2
+ const DEFAULT_STREAK_WINDOW_MILLISECONDS = 160;
3
+ const DEFAULT_RAMP_LENGTH = 12;
4
+ const EASE_IN_EXPONENT = 1.5;
5
+ class NaturalScrollAcceleration {
6
+ lastTick = 0;
7
+ streak = 0;
8
+ base;
9
+ maximum;
10
+ streakWindowMilliseconds;
11
+ rampLength;
12
+ constructor(options = {}) {
13
+ this.base = options.base ?? 2;
14
+ this.maximum = options.maximum ?? DEFAULT_MAXIMUM_LINES;
15
+ this.streakWindowMilliseconds =
16
+ options.streakWindowMilliseconds ?? DEFAULT_STREAK_WINDOW_MILLISECONDS;
17
+ this.rampLength = options.rampLength ?? DEFAULT_RAMP_LENGTH;
18
+ }
19
+ tick(now = Date.now()) {
20
+ const elapsed = now - this.lastTick;
21
+ this.lastTick = now;
22
+ this.streak =
23
+ elapsed > this.streakWindowMilliseconds ? 0 : Math.min(this.streak + 1, this.rampLength);
24
+ const progress = this.streak / this.rampLength;
25
+ return Math.round(this.base + (this.maximum - this.base) * progress ** EASE_IN_EXPONENT);
26
+ }
27
+ reset() {
28
+ this.lastTick = 0;
29
+ this.streak = 0;
30
+ }
31
+ }
32
+ export { NaturalScrollAcceleration };
33
+ //# sourceMappingURL=scroll.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scroll.js","sourceRoot":"","sources":["../../src/tui/scroll.ts"],"names":[],"mappings":"AAYA,MAAM,qBAAqB,GAAG,EAAE,CAAC;AACjC,MAAM,kCAAkC,GAAG,GAAG,CAAC;AAC/C,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAC/B,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAE7B,MAAM,yBAAyB;IACrB,QAAQ,GAAG,CAAC,CAAC;IACb,MAAM,GAAG,CAAC,CAAC;IACF,IAAI,CAAS;IACb,OAAO,CAAS;IAChB,wBAAwB,CAAS;IACjC,UAAU,CAAS;IAEpC,YAAmB,UAAgC,EAAE;QACnD,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC;QAC9B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,qBAAqB,CAAC;QACxD,IAAI,CAAC,wBAAwB;YAC3B,OAAO,CAAC,wBAAwB,IAAI,kCAAkC,CAAC;QACzE,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,mBAAmB,CAAC;IAC9D,CAAC;IAEM,IAAI,CAAC,MAAc,IAAI,CAAC,GAAG,EAAE;QAClC,MAAM,OAAO,GAAG,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC;QACpC,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC;QACpB,IAAI,CAAC,MAAM;YACT,OAAO,GAAG,IAAI,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAC3F,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC;QAC/C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,QAAQ,IAAI,gBAAgB,CAAC,CAAC;IAC3F,CAAC;IAEM,KAAK;QACV,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;QAClB,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IAClB,CAAC;CACF;AAED,OAAO,EAAE,yBAAyB,EAAE,CAAC","sourcesContent":["interface ScrollAcceleration {\n readonly tick: (now?: number) => number;\n readonly reset: () => void;\n}\n\ninterface NaturalScrollOptions {\n readonly base?: number;\n readonly maximum?: number;\n readonly streakWindowMilliseconds?: number;\n readonly rampLength?: number;\n}\n\nconst DEFAULT_MAXIMUM_LINES = 14;\nconst DEFAULT_STREAK_WINDOW_MILLISECONDS = 160;\nconst DEFAULT_RAMP_LENGTH = 12;\nconst EASE_IN_EXPONENT = 1.5;\n\nclass NaturalScrollAcceleration implements ScrollAcceleration {\n private lastTick = 0;\n private streak = 0;\n private readonly base: number;\n private readonly maximum: number;\n private readonly streakWindowMilliseconds: number;\n private readonly rampLength: number;\n\n public constructor(options: NaturalScrollOptions = {}) {\n this.base = options.base ?? 2;\n this.maximum = options.maximum ?? DEFAULT_MAXIMUM_LINES;\n this.streakWindowMilliseconds =\n options.streakWindowMilliseconds ?? DEFAULT_STREAK_WINDOW_MILLISECONDS;\n this.rampLength = options.rampLength ?? DEFAULT_RAMP_LENGTH;\n }\n\n public tick(now: number = Date.now()): number {\n const elapsed = now - this.lastTick;\n this.lastTick = now;\n this.streak =\n elapsed > this.streakWindowMilliseconds ? 0 : Math.min(this.streak + 1, this.rampLength);\n const progress = this.streak / this.rampLength;\n return Math.round(this.base + (this.maximum - this.base) * progress ** EASE_IN_EXPONENT);\n }\n\n public reset(): void {\n this.lastTick = 0;\n this.streak = 0;\n }\n}\n\nexport { NaturalScrollAcceleration };\n"]}
@@ -0,0 +1,99 @@
1
+ import { TraceProjection } from "@unigent/core/trace";
2
+ const COALESCE_MILLISECONDS = 32;
3
+ const MAX_OUTPUT_CHARACTERS = 1_000_000;
4
+ function appendBounded(current, chunk) {
5
+ const combined = current + chunk;
6
+ if (combined.length <= MAX_OUTPUT_CHARACTERS) {
7
+ return combined;
8
+ }
9
+ return `[earlier output truncated]\n${combined.slice(-MAX_OUTPUT_CHARACTERS)}`;
10
+ }
11
+ function projectSnapshot(sourceFile, state) {
12
+ return {
13
+ sourceFile,
14
+ status: state.status,
15
+ stdout: state.stdout,
16
+ stderr: state.stderr,
17
+ error: state.error,
18
+ runNumber: state.runNumber,
19
+ startedAt: state.startedAt,
20
+ revision: state.revision,
21
+ ...state.projection.snapshot(),
22
+ };
23
+ }
24
+ function createTuiStore(sourceFile) {
25
+ const state = {
26
+ projection: new TraceProjection(),
27
+ status: "running",
28
+ stdout: "",
29
+ stderr: "",
30
+ error: undefined,
31
+ runNumber: 1,
32
+ startedAt: Date.now(),
33
+ revision: 0,
34
+ };
35
+ let snapshot = projectSnapshot(sourceFile, state);
36
+ const listeners = new Set();
37
+ let timer;
38
+ const rebuild = () => {
39
+ snapshot = projectSnapshot(sourceFile, state);
40
+ for (const listener of listeners) {
41
+ listener();
42
+ }
43
+ };
44
+ const flush = () => {
45
+ timer = undefined;
46
+ rebuild();
47
+ };
48
+ const schedule = () => {
49
+ timer ??= setTimeout(flush, COALESCE_MILLISECONDS);
50
+ };
51
+ const flushNow = () => {
52
+ if (timer !== undefined) {
53
+ clearTimeout(timer);
54
+ timer = undefined;
55
+ }
56
+ rebuild();
57
+ };
58
+ return {
59
+ subscribe(listener) {
60
+ listeners.add(listener);
61
+ return () => {
62
+ listeners.delete(listener);
63
+ };
64
+ },
65
+ getSnapshot: () => snapshot,
66
+ start() {
67
+ state.projection = new TraceProjection();
68
+ state.status = "running";
69
+ state.stdout = "";
70
+ state.stderr = "";
71
+ state.error = undefined;
72
+ state.runNumber += 1;
73
+ state.startedAt = Date.now();
74
+ state.revision += 1;
75
+ flushNow();
76
+ },
77
+ pushEvent(event) {
78
+ state.projection.append(event);
79
+ state.revision += 1;
80
+ schedule();
81
+ },
82
+ pushOutput(stream, chunk) {
83
+ if (stream === "stdout") {
84
+ state.stdout = appendBounded(state.stdout, chunk);
85
+ }
86
+ else {
87
+ state.stderr = appendBounded(state.stderr, chunk);
88
+ }
89
+ schedule();
90
+ },
91
+ settle(nextStatus, nextError) {
92
+ state.status = nextStatus;
93
+ state.error = nextError;
94
+ flushNow();
95
+ },
96
+ };
97
+ }
98
+ export { createTuiStore };
99
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/tui/store.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentEvent } from \"@unigent/core\";\nimport { TraceProjection, type TraceProjectionSnapshot } from \"@unigent/core/trace\";\n\ninterface TuiSnapshot extends TraceProjectionSnapshot {\n readonly sourceFile: string;\n readonly status: \"running\" | \"succeeded\" | \"failed\" | \"cancelled\";\n readonly stdout: string;\n readonly stderr: string;\n readonly error: string | undefined;\n readonly runNumber: number;\n readonly startedAt: number;\n readonly revision: number;\n}\n\ninterface TuiStore {\n readonly subscribe: (listener: () => void) => () => void;\n readonly getSnapshot: () => TuiSnapshot;\n readonly start: () => void;\n readonly pushEvent: (event: AgentEvent) => void;\n readonly pushOutput: (stream: \"stdout\" | \"stderr\", chunk: string) => void;\n readonly settle: (status: \"succeeded\" | \"failed\" | \"cancelled\", error?: string) => void;\n}\n\ninterface MutableTuiState {\n projection: TraceProjection;\n status: TuiSnapshot[\"status\"];\n stdout: string;\n stderr: string;\n error: string | undefined;\n runNumber: number;\n startedAt: number;\n revision: number;\n}\n\nconst COALESCE_MILLISECONDS = 32;\nconst MAX_OUTPUT_CHARACTERS = 1_000_000;\n\nfunction appendBounded(current: string, chunk: string): string {\n const combined = current + chunk;\n if (combined.length <= MAX_OUTPUT_CHARACTERS) {\n return combined;\n }\n return `[earlier output truncated]\\n${combined.slice(-MAX_OUTPUT_CHARACTERS)}`;\n}\n\nfunction projectSnapshot(sourceFile: string, state: MutableTuiState): TuiSnapshot {\n return {\n sourceFile,\n status: state.status,\n stdout: state.stdout,\n stderr: state.stderr,\n error: state.error,\n runNumber: state.runNumber,\n startedAt: state.startedAt,\n revision: state.revision,\n ...state.projection.snapshot(),\n };\n}\n\nfunction createTuiStore(sourceFile: string): TuiStore {\n const state: MutableTuiState = {\n projection: new TraceProjection(),\n status: \"running\",\n stdout: \"\",\n stderr: \"\",\n error: undefined,\n runNumber: 1,\n startedAt: Date.now(),\n revision: 0,\n };\n let snapshot = projectSnapshot(sourceFile, state);\n const listeners = new Set<() => void>();\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const rebuild = (): void => {\n snapshot = projectSnapshot(sourceFile, state);\n for (const listener of listeners) {\n listener();\n }\n };\n const flush = (): void => {\n timer = undefined;\n rebuild();\n };\n const schedule = (): void => {\n timer ??= setTimeout(flush, COALESCE_MILLISECONDS);\n };\n const flushNow = (): void => {\n if (timer !== undefined) {\n clearTimeout(timer);\n timer = undefined;\n }\n rebuild();\n };\n\n return {\n subscribe(listener: () => void): () => void {\n listeners.add(listener);\n return (): void => {\n listeners.delete(listener);\n };\n },\n getSnapshot: (): TuiSnapshot => snapshot,\n start(): void {\n state.projection = new TraceProjection();\n state.status = \"running\";\n state.stdout = \"\";\n state.stderr = \"\";\n state.error = undefined;\n state.runNumber += 1;\n state.startedAt = Date.now();\n state.revision += 1;\n flushNow();\n },\n pushEvent(event: AgentEvent): void {\n state.projection.append(event);\n state.revision += 1;\n schedule();\n },\n pushOutput(stream: \"stdout\" | \"stderr\", chunk: string): void {\n if (stream === \"stdout\") {\n state.stdout = appendBounded(state.stdout, chunk);\n } else {\n state.stderr = appendBounded(state.stderr, chunk);\n }\n schedule();\n },\n settle(nextStatus: \"succeeded\" | \"failed\" | \"cancelled\", nextError?: string): void {\n state.status = nextStatus;\n state.error = nextError;\n flushNow();\n },\n };\n}\n\nexport type { TuiSnapshot, TuiStore };\nexport { createTuiStore };\n"]}
@@ -0,0 +1,35 @@
1
+ const DARK = {
2
+ background: "#090c12",
3
+ surface: "#0d1119",
4
+ selected: "#17233b",
5
+ userSurface: "#131923",
6
+ foreground: "#d6dde8",
7
+ muted: "#748094",
8
+ border: "#273244",
9
+ accent: "#82aaff",
10
+ success: "#7bd88f",
11
+ warning: "#f2cc60",
12
+ error: "#ff6b81",
13
+ reasoning: "#c099ff",
14
+ tool: "#ffc777",
15
+ };
16
+ const LIGHT = {
17
+ background: "#ffffff",
18
+ surface: "#f7f9fc",
19
+ selected: "#dce9ff",
20
+ userSurface: "#f1f4f8",
21
+ foreground: "#20242c",
22
+ muted: "#657084",
23
+ border: "#d4dbe5",
24
+ accent: "#175cd3",
25
+ success: "#16794a",
26
+ warning: "#956000",
27
+ error: "#c62840",
28
+ reasoning: "#7147b8",
29
+ tool: "#965b00",
30
+ };
31
+ function paletteFor(mode) {
32
+ return mode === "light" ? LIGHT : DARK;
33
+ }
34
+ export { paletteFor };
35
+ //# sourceMappingURL=theme.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"theme.js","sourceRoot":"","sources":["../../src/tui/theme.ts"],"names":[],"mappings":"AAkBA,MAAM,IAAI,GAAY;IACpB,UAAU,EAAE,SAAS;IACrB,OAAO,EAAE,SAAS;IAClB,QAAQ,EAAE,SAAS;IACnB,WAAW,EAAE,SAAS;IACtB,UAAU,EAAE,SAAS;IACrB,KAAK,EAAE,SAAS;IAChB,MAAM,EAAE,SAAS;IACjB,MAAM,EAAE,SAAS;IACjB,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,SAAS;IAChB,SAAS,EAAE,SAAS;IACpB,IAAI,EAAE,SAAS;CAChB,CAAC;AAEF,MAAM,KAAK,GAAY;IACrB,UAAU,EAAE,SAAS;IACrB,OAAO,EAAE,SAAS;IAClB,QAAQ,EAAE,SAAS;IACnB,WAAW,EAAE,SAAS;IACtB,UAAU,EAAE,SAAS;IACrB,KAAK,EAAE,SAAS;IAChB,MAAM,EAAE,SAAS;IACjB,MAAM,EAAE,SAAS;IACjB,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,SAAS;IAChB,SAAS,EAAE,SAAS;IACpB,IAAI,EAAE,SAAS;CAChB,CAAC;AAEF,SAAS,UAAU,CAAC,IAAe;IACjC,OAAO,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AACzC,CAAC;AAGD,OAAO,EAAE,UAAU,EAAE,CAAC","sourcesContent":["type ThemeMode = \"dark\" | \"light\";\n\ninterface Palette {\n readonly background: string;\n readonly surface: string;\n readonly selected: string;\n readonly userSurface: string;\n readonly foreground: string;\n readonly muted: string;\n readonly border: string;\n readonly accent: string;\n readonly success: string;\n readonly warning: string;\n readonly error: string;\n readonly reasoning: string;\n readonly tool: string;\n}\n\nconst DARK: Palette = {\n background: \"#090c12\",\n surface: \"#0d1119\",\n selected: \"#17233b\",\n userSurface: \"#131923\",\n foreground: \"#d6dde8\",\n muted: \"#748094\",\n border: \"#273244\",\n accent: \"#82aaff\",\n success: \"#7bd88f\",\n warning: \"#f2cc60\",\n error: \"#ff6b81\",\n reasoning: \"#c099ff\",\n tool: \"#ffc777\",\n};\n\nconst LIGHT: Palette = {\n background: \"#ffffff\",\n surface: \"#f7f9fc\",\n selected: \"#dce9ff\",\n userSurface: \"#f1f4f8\",\n foreground: \"#20242c\",\n muted: \"#657084\",\n border: \"#d4dbe5\",\n accent: \"#175cd3\",\n success: \"#16794a\",\n warning: \"#956000\",\n error: \"#c62840\",\n reasoning: \"#7147b8\",\n tool: \"#965b00\",\n};\n\nfunction paletteFor(mode: ThemeMode): Palette {\n return mode === \"light\" ? LIGHT : DARK;\n}\n\nexport type { Palette, ThemeMode };\nexport { paletteFor };\n"]}
@@ -0,0 +1,267 @@
1
+ import { combineUsage } from "@unigent/core";
2
+ import { formatCost, formatDuration, formatTokens } from "../format.js";
3
+ const MAX_SUMMARY_TOOL_NAMES = 3;
4
+ const EMPTY_USAGE = {
5
+ inputTokens: 0,
6
+ outputTokens: 0,
7
+ totalTokens: 0,
8
+ calls: 0,
9
+ };
10
+ function nodeMetrics(node) {
11
+ const parts = [];
12
+ if (node.repairs > 0) {
13
+ parts.push(`↻${node.repairs}`);
14
+ }
15
+ if (node.durationMs !== undefined) {
16
+ parts.push(formatDuration(node.durationMs));
17
+ }
18
+ if (node.usage.totalTokens > 0) {
19
+ parts.push(formatTokens(node.usage.totalTokens));
20
+ }
21
+ const cost = formatCost(node.usage.costUsd);
22
+ if (cost.length > 0) {
23
+ parts.push(cost);
24
+ }
25
+ return parts.join(" ");
26
+ }
27
+ function aggregateUsage(nodes) {
28
+ return nodes.reduce((usage, node) => combineUsage(usage, node.usage), EMPTY_USAGE);
29
+ }
30
+ function scopeMetrics(nodes) {
31
+ const usage = aggregateUsage(nodes);
32
+ const parts = [`${nodes.length} ${nodes.length === 1 ? "run" : "runs"}`];
33
+ if (usage.totalTokens > 0) {
34
+ parts.push(formatTokens(usage.totalTokens));
35
+ }
36
+ const cost = formatCost(usage.costUsd);
37
+ if (cost.length > 0) {
38
+ parts.push(cost);
39
+ }
40
+ return parts.join(" ");
41
+ }
42
+ function aggregateOutcome(nodes) {
43
+ if (nodes.some((node) => node.outcome === "running")) {
44
+ return "running";
45
+ }
46
+ if (nodes.some((node) => node.outcome === "failed")) {
47
+ return "failed";
48
+ }
49
+ return nodes.some((node) => node.outcome === "cancelled") ? "cancelled" : "succeeded";
50
+ }
51
+ function summarizeTools(node) {
52
+ const counts = new Map();
53
+ for (const child of node.children) {
54
+ if (child.kind === "tool") {
55
+ counts.set(child.name, (counts.get(child.name) ?? 0) + 1);
56
+ }
57
+ }
58
+ const summaries = [...counts].map(([name, count]) => (count === 1 ? name : `${name} ×${count}`));
59
+ if (summaries.length === 0) {
60
+ return;
61
+ }
62
+ const visible = summaries.slice(0, MAX_SUMMARY_TOOL_NAMES);
63
+ const hidden = summaries.length - visible.length;
64
+ return `tools ${visible.join(" · ")}${hidden > 0 ? ` · +${hidden}` : ""}`;
65
+ }
66
+ function nestedRuns(node) {
67
+ return node.children.flatMap((child) => (child.kind === "run" ? [child] : nestedRuns(child)));
68
+ }
69
+ function visibleChildren(node, expandedRunSpanIds) {
70
+ if (node.kind === "tool") {
71
+ return node.children;
72
+ }
73
+ return node.children.flatMap((child) => {
74
+ if (child.kind === "run" || expandedRunSpanIds.has(node.spanId)) {
75
+ return [child];
76
+ }
77
+ return nestedRuns(child);
78
+ });
79
+ }
80
+ function addRootToOutline(container, root) {
81
+ let current = container;
82
+ const path = [];
83
+ for (const segment of root.scopePath) {
84
+ path.push(segment);
85
+ let group = current.scopes.get(segment);
86
+ if (group === undefined) {
87
+ group = { name: segment, path: [...path], items: [], scopes: new Map() };
88
+ current.scopes.set(segment, group);
89
+ current.items.push({ kind: "scope", group });
90
+ }
91
+ current = group;
92
+ }
93
+ current.items.push({ kind: "node", node: root });
94
+ }
95
+ function rootsInGroup(group) {
96
+ return group.items.flatMap((item) => item.kind === "node" ? [item.node] : rootsInGroup(item.group));
97
+ }
98
+ function collectSpanIds(nodes) {
99
+ const spanIds = [];
100
+ const visit = (node) => {
101
+ spanIds.push(node.spanId);
102
+ for (const child of node.children) {
103
+ visit(child);
104
+ }
105
+ };
106
+ nodes.forEach(visit);
107
+ return spanIds;
108
+ }
109
+ function scopeRow(group, prefix) {
110
+ const nodes = rootsInGroup(group);
111
+ return {
112
+ spanId: `scope:${JSON.stringify(group.path)}`,
113
+ name: group.name,
114
+ kind: "scope",
115
+ prefix,
116
+ metrics: scopeMetrics(nodes),
117
+ outcome: aggregateOutcome(nodes),
118
+ toolSummary: undefined,
119
+ hasTools: false,
120
+ toolsExpanded: false,
121
+ selectable: true,
122
+ scopePath: group.path,
123
+ runCount: nodes.length,
124
+ usage: aggregateUsage(nodes),
125
+ nodes,
126
+ selectedSpanIds: collectSpanIds(nodes),
127
+ };
128
+ }
129
+ function displayNodeName(node) {
130
+ return node.kind === "run" && node.name === node.scopePath.at(-1)
131
+ ? (node.agent ?? node.name)
132
+ : node.name;
133
+ }
134
+ function nodeRow(node, prefix, expandedRunSpanIds) {
135
+ return {
136
+ spanId: node.spanId,
137
+ name: displayNodeName(node),
138
+ kind: node.kind,
139
+ prefix,
140
+ metrics: nodeMetrics(node),
141
+ outcome: node.outcome,
142
+ toolSummary: node.kind === "run" ? summarizeTools(node) : undefined,
143
+ hasTools: node.kind === "run" && node.children.some((child) => child.kind === "tool"),
144
+ toolsExpanded: node.kind === "run" && expandedRunSpanIds.has(node.spanId),
145
+ selectable: node.kind === "run",
146
+ scopePath: node.scopePath,
147
+ runCount: node.kind === "run" ? 1 : 0,
148
+ usage: node.usage,
149
+ nodes: [node],
150
+ selectedSpanIds: collectSpanIds([node]),
151
+ };
152
+ }
153
+ function rowPrefix(ancestorPrefix, isLast, isRoot) {
154
+ if (isRoot) {
155
+ return "";
156
+ }
157
+ return `${ancestorPrefix}${isLast ? "└─ " : "├─ "}`;
158
+ }
159
+ function descendantPrefix(ancestorPrefix, isLast, isRoot) {
160
+ if (isRoot) {
161
+ return "";
162
+ }
163
+ return `${ancestorPrefix}${isLast ? " " : "│ "}`;
164
+ }
165
+ function flattenTraceTree(tree, expandedRunSpanIds = new Set()) {
166
+ const rows = [];
167
+ const outline = { items: [], scopes: new Map() };
168
+ tree.roots.forEach((root) => {
169
+ addRootToOutline(outline, root);
170
+ });
171
+ const visitNode = (node, ancestorPrefix, isLast, isRoot) => {
172
+ const prefix = rowPrefix(ancestorPrefix, isLast, isRoot);
173
+ rows.push(nodeRow(node, prefix, expandedRunSpanIds));
174
+ const childPrefix = descendantPrefix(ancestorPrefix, isLast, isRoot);
175
+ const children = visibleChildren(node, expandedRunSpanIds);
176
+ children.forEach((child, index) => {
177
+ visitNode(child, childPrefix, index === children.length - 1, false);
178
+ });
179
+ };
180
+ function visitItem(item, index, itemCount, ancestorPrefix, parentIsRoot) {
181
+ const isLast = index === itemCount - 1;
182
+ if (item.kind === "node") {
183
+ visitNode(item.node, ancestorPrefix, isLast, parentIsRoot);
184
+ return;
185
+ }
186
+ rows.push(scopeRow(item.group, rowPrefix(ancestorPrefix, isLast, parentIsRoot)));
187
+ visitItems(item.group.items, descendantPrefix(ancestorPrefix, isLast, parentIsRoot), false);
188
+ }
189
+ function visitItems(items, ancestorPrefix, parentIsRoot) {
190
+ for (const [index, item] of items.entries()) {
191
+ visitItem(item, index, items.length, ancestorPrefix, parentIsRoot);
192
+ }
193
+ }
194
+ visitItems(outline.items, "", true);
195
+ return rows;
196
+ }
197
+ function nestedNodes(nodes) {
198
+ return nodes.flatMap((node) => [node, ...nestedNodes(node.children)]);
199
+ }
200
+ function diagnosticNodeName(node) {
201
+ return node.scopePath.length > 0 ? node.scopePath.join("/") : displayNodeName(node);
202
+ }
203
+ function diagnosticsForRow(row) {
204
+ const diagnostics = [];
205
+ for (const node of nestedNodes(row.nodes)) {
206
+ if (node.repairs > 0) {
207
+ diagnostics.push({
208
+ kind: "repair",
209
+ label: `${diagnosticNodeName(node)} · repairs`,
210
+ value: `${node.repairs} repair ${node.repairs === 1 ? "attempt" : "attempts"}`,
211
+ });
212
+ }
213
+ for (const log of node.logs) {
214
+ diagnostics.push({
215
+ kind: "log",
216
+ label: `${diagnosticNodeName(node)} · ${log.level}`,
217
+ value: log.message,
218
+ });
219
+ }
220
+ for (const annotation of node.annotations) {
221
+ diagnostics.push({
222
+ kind: "annotation",
223
+ label: `${diagnosticNodeName(node)} · annotation`,
224
+ value: annotation,
225
+ });
226
+ }
227
+ for (const checkpoint of node.checkpoints) {
228
+ diagnostics.push({
229
+ kind: "checkpoint",
230
+ label: `${diagnosticNodeName(node)} · checkpoint ${checkpoint.action}`,
231
+ value: checkpoint.key,
232
+ });
233
+ }
234
+ if (node.error !== undefined) {
235
+ diagnostics.push({
236
+ kind: "error",
237
+ label: `${diagnosticNodeName(node)} · error`,
238
+ value: node.error,
239
+ });
240
+ }
241
+ }
242
+ return diagnostics;
243
+ }
244
+ function environmentLabel(tree, selectedRow) {
245
+ const nodes = nestedNodes(selectedRow?.nodes ?? tree.roots).filter((node) => node.kind === "run");
246
+ const backends = new Set(nodes.flatMap((node) => (node.backend === undefined ? [] : [node.backend])));
247
+ const models = new Set(nodes.flatMap((node) => (node.model === undefined ? [] : [node.model])));
248
+ if (backends.size === 0 && models.size === 0) {
249
+ return "waiting for agent";
250
+ }
251
+ if (backends.size === 1 && models.size === 1) {
252
+ return `${[...backends][0]}/${[...models][0]}`;
253
+ }
254
+ const parts = [
255
+ backends.size === 1 ? [...backends][0] : `${backends.size} backends`,
256
+ models.size === 1 ? [...models][0] : `${models.size} models`,
257
+ ];
258
+ return parts.filter((part) => part !== undefined).join(" · ");
259
+ }
260
+ function focusLabel(row) {
261
+ if (row === undefined) {
262
+ return;
263
+ }
264
+ return row.kind === "scope" ? row.scopePath.join("/") : row.name;
265
+ }
266
+ export { diagnosticsForRow, environmentLabel, flattenTraceTree, focusLabel };
267
+ //# sourceMappingURL=tree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tree.js","sourceRoot":"","sources":["../../src/tui/tree.ts"],"names":[],"mappings":"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","sourcesContent":["import { type AgentUsage, combineUsage } from \"@unigent/core\";\nimport type { TraceNode, TraceTree } from \"@unigent/core/trace\";\nimport { formatCost, formatDuration, formatTokens } from \"../format.js\";\n\ntype TreeRowKind = TraceNode[\"kind\"] | \"scope\";\n\ninterface TreeRow {\n readonly spanId: string;\n readonly name: string;\n readonly kind: TreeRowKind;\n readonly prefix: string;\n readonly metrics: string;\n readonly outcome: TraceNode[\"outcome\"];\n readonly toolSummary: string | undefined;\n readonly hasTools: boolean;\n readonly toolsExpanded: boolean;\n readonly selectable: boolean;\n readonly scopePath: readonly string[];\n readonly runCount: number;\n readonly usage: AgentUsage;\n readonly nodes: readonly TraceNode[];\n readonly selectedSpanIds: readonly string[];\n}\n\ninterface TreeDiagnostic {\n readonly kind: \"annotation\" | \"checkpoint\" | \"error\" | \"log\" | \"repair\";\n readonly label: string;\n readonly value: unknown;\n}\n\ninterface MutableScopeGroup {\n readonly name: string;\n readonly path: readonly string[];\n readonly items: OutlineItem[];\n readonly scopes: Map<string, MutableScopeGroup>;\n}\n\ntype OutlineItem =\n | { readonly kind: \"node\"; readonly node: TraceNode }\n | { readonly kind: \"scope\"; readonly group: MutableScopeGroup };\n\ninterface OutlineContainer {\n readonly items: OutlineItem[];\n readonly scopes: Map<string, MutableScopeGroup>;\n}\n\nconst MAX_SUMMARY_TOOL_NAMES = 3;\nconst EMPTY_USAGE: AgentUsage = {\n inputTokens: 0,\n outputTokens: 0,\n totalTokens: 0,\n calls: 0,\n};\n\nfunction nodeMetrics(node: TraceNode): string {\n const parts: string[] = [];\n if (node.repairs > 0) {\n parts.push(`↻${node.repairs}`);\n }\n if (node.durationMs !== undefined) {\n parts.push(formatDuration(node.durationMs));\n }\n if (node.usage.totalTokens > 0) {\n parts.push(formatTokens(node.usage.totalTokens));\n }\n const cost = formatCost(node.usage.costUsd);\n if (cost.length > 0) {\n parts.push(cost);\n }\n return parts.join(\" \");\n}\n\nfunction aggregateUsage(nodes: readonly TraceNode[]): AgentUsage {\n return nodes.reduce((usage, node) => combineUsage(usage, node.usage), EMPTY_USAGE);\n}\n\nfunction scopeMetrics(nodes: readonly TraceNode[]): string {\n const usage = aggregateUsage(nodes);\n const parts = [`${nodes.length} ${nodes.length === 1 ? \"run\" : \"runs\"}`];\n if (usage.totalTokens > 0) {\n parts.push(formatTokens(usage.totalTokens));\n }\n const cost = formatCost(usage.costUsd);\n if (cost.length > 0) {\n parts.push(cost);\n }\n return parts.join(\" \");\n}\n\nfunction aggregateOutcome(nodes: readonly TraceNode[]): TraceNode[\"outcome\"] {\n if (nodes.some((node) => node.outcome === \"running\")) {\n return \"running\";\n }\n if (nodes.some((node) => node.outcome === \"failed\")) {\n return \"failed\";\n }\n return nodes.some((node) => node.outcome === \"cancelled\") ? \"cancelled\" : \"succeeded\";\n}\n\nfunction summarizeTools(node: TraceNode): string | undefined {\n const counts = new Map<string, number>();\n for (const child of node.children) {\n if (child.kind === \"tool\") {\n counts.set(child.name, (counts.get(child.name) ?? 0) + 1);\n }\n }\n const summaries = [...counts].map(([name, count]) => (count === 1 ? name : `${name} ×${count}`));\n if (summaries.length === 0) {\n return;\n }\n const visible = summaries.slice(0, MAX_SUMMARY_TOOL_NAMES);\n const hidden = summaries.length - visible.length;\n return `tools ${visible.join(\" · \")}${hidden > 0 ? ` · +${hidden}` : \"\"}`;\n}\n\nfunction nestedRuns(node: TraceNode): readonly TraceNode[] {\n return node.children.flatMap((child) => (child.kind === \"run\" ? [child] : nestedRuns(child)));\n}\n\nfunction visibleChildren(\n node: TraceNode,\n expandedRunSpanIds: ReadonlySet<string>,\n): readonly TraceNode[] {\n if (node.kind === \"tool\") {\n return node.children;\n }\n return node.children.flatMap((child) => {\n if (child.kind === \"run\" || expandedRunSpanIds.has(node.spanId)) {\n return [child];\n }\n return nestedRuns(child);\n });\n}\n\nfunction addRootToOutline(container: OutlineContainer, root: TraceNode): void {\n let current = container;\n const path: string[] = [];\n for (const segment of root.scopePath) {\n path.push(segment);\n let group = current.scopes.get(segment);\n if (group === undefined) {\n group = { name: segment, path: [...path], items: [], scopes: new Map() };\n current.scopes.set(segment, group);\n current.items.push({ kind: \"scope\", group });\n }\n current = group;\n }\n current.items.push({ kind: \"node\", node: root });\n}\n\nfunction rootsInGroup(group: MutableScopeGroup): readonly TraceNode[] {\n return group.items.flatMap((item) =>\n item.kind === \"node\" ? [item.node] : rootsInGroup(item.group),\n );\n}\n\nfunction collectSpanIds(nodes: readonly TraceNode[]): readonly string[] {\n const spanIds: string[] = [];\n const visit = (node: TraceNode): void => {\n spanIds.push(node.spanId);\n for (const child of node.children) {\n visit(child);\n }\n };\n nodes.forEach(visit);\n return spanIds;\n}\n\nfunction scopeRow(group: MutableScopeGroup, prefix: string): TreeRow {\n const nodes = rootsInGroup(group);\n return {\n spanId: `scope:${JSON.stringify(group.path)}`,\n name: group.name,\n kind: \"scope\",\n prefix,\n metrics: scopeMetrics(nodes),\n outcome: aggregateOutcome(nodes),\n toolSummary: undefined,\n hasTools: false,\n toolsExpanded: false,\n selectable: true,\n scopePath: group.path,\n runCount: nodes.length,\n usage: aggregateUsage(nodes),\n nodes,\n selectedSpanIds: collectSpanIds(nodes),\n };\n}\n\nfunction displayNodeName(node: TraceNode): string {\n return node.kind === \"run\" && node.name === node.scopePath.at(-1)\n ? (node.agent ?? node.name)\n : node.name;\n}\n\nfunction nodeRow(\n node: TraceNode,\n prefix: string,\n expandedRunSpanIds: ReadonlySet<string>,\n): TreeRow {\n return {\n spanId: node.spanId,\n name: displayNodeName(node),\n kind: node.kind,\n prefix,\n metrics: nodeMetrics(node),\n outcome: node.outcome,\n toolSummary: node.kind === \"run\" ? summarizeTools(node) : undefined,\n hasTools: node.kind === \"run\" && node.children.some((child) => child.kind === \"tool\"),\n toolsExpanded: node.kind === \"run\" && expandedRunSpanIds.has(node.spanId),\n selectable: node.kind === \"run\",\n scopePath: node.scopePath,\n runCount: node.kind === \"run\" ? 1 : 0,\n usage: node.usage,\n nodes: [node],\n selectedSpanIds: collectSpanIds([node]),\n };\n}\n\nfunction rowPrefix(ancestorPrefix: string, isLast: boolean, isRoot: boolean): string {\n if (isRoot) {\n return \"\";\n }\n return `${ancestorPrefix}${isLast ? \"└─ \" : \"├─ \"}`;\n}\n\nfunction descendantPrefix(ancestorPrefix: string, isLast: boolean, isRoot: boolean): string {\n if (isRoot) {\n return \"\";\n }\n return `${ancestorPrefix}${isLast ? \" \" : \"│ \"}`;\n}\n\nfunction flattenTraceTree(\n tree: TraceTree,\n expandedRunSpanIds: ReadonlySet<string> = new Set(),\n): readonly TreeRow[] {\n const rows: TreeRow[] = [];\n const outline: OutlineContainer = { items: [], scopes: new Map() };\n tree.roots.forEach((root) => {\n addRootToOutline(outline, root);\n });\n\n const visitNode = (\n node: TraceNode,\n ancestorPrefix: string,\n isLast: boolean,\n isRoot: boolean,\n ): void => {\n const prefix = rowPrefix(ancestorPrefix, isLast, isRoot);\n rows.push(nodeRow(node, prefix, expandedRunSpanIds));\n const childPrefix = descendantPrefix(ancestorPrefix, isLast, isRoot);\n const children = visibleChildren(node, expandedRunSpanIds);\n children.forEach((child, index) => {\n visitNode(child, childPrefix, index === children.length - 1, false);\n });\n };\n\n function visitItem(\n item: OutlineItem,\n index: number,\n itemCount: number,\n ancestorPrefix: string,\n parentIsRoot: boolean,\n ): void {\n const isLast = index === itemCount - 1;\n if (item.kind === \"node\") {\n visitNode(item.node, ancestorPrefix, isLast, parentIsRoot);\n return;\n }\n rows.push(scopeRow(item.group, rowPrefix(ancestorPrefix, isLast, parentIsRoot)));\n visitItems(item.group.items, descendantPrefix(ancestorPrefix, isLast, parentIsRoot), false);\n }\n\n function visitItems(\n items: readonly OutlineItem[],\n ancestorPrefix: string,\n parentIsRoot: boolean,\n ): void {\n for (const [index, item] of items.entries()) {\n visitItem(item, index, items.length, ancestorPrefix, parentIsRoot);\n }\n }\n\n visitItems(outline.items, \"\", true);\n return rows;\n}\n\nfunction nestedNodes(nodes: readonly TraceNode[]): readonly TraceNode[] {\n return nodes.flatMap((node) => [node, ...nestedNodes(node.children)]);\n}\n\nfunction diagnosticNodeName(node: TraceNode): string {\n return node.scopePath.length > 0 ? node.scopePath.join(\"/\") : displayNodeName(node);\n}\n\nfunction diagnosticsForRow(row: TreeRow): readonly TreeDiagnostic[] {\n const diagnostics: TreeDiagnostic[] = [];\n for (const node of nestedNodes(row.nodes)) {\n if (node.repairs > 0) {\n diagnostics.push({\n kind: \"repair\",\n label: `${diagnosticNodeName(node)} · repairs`,\n value: `${node.repairs} repair ${node.repairs === 1 ? \"attempt\" : \"attempts\"}`,\n });\n }\n for (const log of node.logs) {\n diagnostics.push({\n kind: \"log\",\n label: `${diagnosticNodeName(node)} · ${log.level}`,\n value: log.message,\n });\n }\n for (const annotation of node.annotations) {\n diagnostics.push({\n kind: \"annotation\",\n label: `${diagnosticNodeName(node)} · annotation`,\n value: annotation,\n });\n }\n for (const checkpoint of node.checkpoints) {\n diagnostics.push({\n kind: \"checkpoint\",\n label: `${diagnosticNodeName(node)} · checkpoint ${checkpoint.action}`,\n value: checkpoint.key,\n });\n }\n if (node.error !== undefined) {\n diagnostics.push({\n kind: \"error\",\n label: `${diagnosticNodeName(node)} · error`,\n value: node.error,\n });\n }\n }\n return diagnostics;\n}\n\nfunction environmentLabel(tree: TraceTree, selectedRow?: TreeRow): string {\n const nodes = nestedNodes(selectedRow?.nodes ?? tree.roots).filter((node) => node.kind === \"run\");\n const backends = new Set(\n nodes.flatMap((node) => (node.backend === undefined ? [] : [node.backend])),\n );\n const models = new Set(nodes.flatMap((node) => (node.model === undefined ? [] : [node.model])));\n if (backends.size === 0 && models.size === 0) {\n return \"waiting for agent\";\n }\n if (backends.size === 1 && models.size === 1) {\n return `${[...backends][0]}/${[...models][0]}`;\n }\n const parts = [\n backends.size === 1 ? [...backends][0] : `${backends.size} backends`,\n models.size === 1 ? [...models][0] : `${models.size} models`,\n ];\n return parts.filter((part): part is string => part !== undefined).join(\" · \");\n}\n\nfunction focusLabel(row: TreeRow | undefined): string | undefined {\n if (row === undefined) {\n return;\n }\n return row.kind === \"scope\" ? row.scopePath.join(\"/\") : row.name;\n}\n\nexport type { TreeDiagnostic, TreeRow };\nexport { diagnosticsForRow, environmentLabel, flattenTraceTree, focusLabel };\n"]}