perstack 0.0.124 → 0.0.126

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/dist/bin/cli.js CHANGED
@@ -5,7 +5,7 @@ import { t as require_token_error } from "../token-error-Bru5BVnt.js";
5
5
  import fs, { constants, lstat, mkdir, open, readFile, stat, writeFile } from "node:fs/promises";
6
6
  import path, { dirname, extname } from "node:path";
7
7
  import * as fs$2 from "node:fs";
8
- import fs$1, { existsSync, readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
8
+ import fs$1, { closeSync, existsSync, openSync, readFileSync, readSync, readdirSync, realpathSync, statSync } from "node:fs";
9
9
  import os from "node:os";
10
10
  import { execFile, execFileSync } from "node:child_process";
11
11
  import { promisify } from "node:util";
@@ -20157,20 +20157,59 @@ function getCheckpointsByJobId$1(jobId) {
20157
20157
  const checkpointDir = getCheckpointDir(jobId);
20158
20158
  if (!existsSync(checkpointDir)) return [];
20159
20159
  const files = readdirSync(checkpointDir).filter((file) => file.endsWith(".json"));
20160
- const checkpoints = [];
20160
+ const fileEntries = [];
20161
20161
  for (const file of files) try {
20162
+ const mtime = statSync(path.resolve(checkpointDir, file)).mtimeMs;
20163
+ fileEntries.push({
20164
+ file,
20165
+ mtime
20166
+ });
20167
+ } catch {
20168
+ fileEntries.push({
20169
+ file,
20170
+ mtime: 0
20171
+ });
20172
+ }
20173
+ fileEntries.sort((a, b) => a.mtime - b.mtime);
20174
+ const checkpoints = [];
20175
+ for (const { file } of fileEntries) try {
20162
20176
  const content = readFileSync(path.resolve(checkpointDir, file), "utf-8");
20163
20177
  checkpoints.push(checkpointSchema.parse(JSON.parse(content)));
20164
20178
  } catch {}
20165
- return checkpoints.sort((a, b) => {
20179
+ return checkpoints;
20180
+ }
20181
+ function getCheckpointsByRunId(jobId, runId) {
20182
+ const checkpointDir = getCheckpointDir(jobId);
20183
+ if (!existsSync(checkpointDir)) return [];
20184
+ const files = readdirSync(checkpointDir).filter((file) => file.endsWith(".json"));
20185
+ const runIdPattern = `"runId":"${runId}"`;
20186
+ const matchingFiles = [];
20187
+ const headerBuf = Buffer.alloc(256);
20188
+ for (const file of files) {
20189
+ const filePath = path.resolve(checkpointDir, file);
20166
20190
  try {
20167
- const aStat = statSync(path.resolve(checkpointDir, `${a.id}.json`));
20168
- const bStat = statSync(path.resolve(checkpointDir, `${b.id}.json`));
20169
- return aStat.mtimeMs - bStat.mtimeMs;
20170
- } catch {
20171
- return 0;
20172
- }
20173
- });
20191
+ const fd = openSync(filePath, "r");
20192
+ try {
20193
+ const bytesRead = readSync(fd, headerBuf, 0, 256, 0);
20194
+ if (headerBuf.toString("utf-8", 0, bytesRead).includes(runIdPattern)) {
20195
+ const mtime = statSync(filePath).mtimeMs;
20196
+ matchingFiles.push({
20197
+ file,
20198
+ mtime
20199
+ });
20200
+ }
20201
+ } finally {
20202
+ closeSync(fd);
20203
+ }
20204
+ } catch {}
20205
+ }
20206
+ matchingFiles.sort((a, b) => a.mtime - b.mtime);
20207
+ const checkpoints = [];
20208
+ for (const { file } of matchingFiles) try {
20209
+ const content = readFileSync(path.resolve(checkpointDir, file), "utf-8");
20210
+ checkpoints.push(checkpointSchema.parse(JSON.parse(content)));
20211
+ } catch {}
20212
+ return checkpoints;
20174
20213
  }
20175
20214
  //#endregion
20176
20215
  //#region ../../packages/filesystem/src/run-setting.ts
@@ -20207,7 +20246,7 @@ async function defaultStoreEvent(event) {
20207
20246
  await mkdir(runDir, { recursive: true });
20208
20247
  await writeFile(eventPath, JSON.stringify(event));
20209
20248
  }
20210
- function getEventContents(jobId, runId, maxStepNumber) {
20249
+ function getEventContents(jobId, runId, maxStepNumber, typeFilter) {
20211
20250
  const runDir = defaultGetRunDir(jobId, runId);
20212
20251
  if (!existsSync(runDir)) return [];
20213
20252
  const eventFiles = readdirSync(runDir).filter((file) => file.startsWith("event-")).map((file) => {
@@ -20218,7 +20257,7 @@ function getEventContents(jobId, runId, maxStepNumber) {
20218
20257
  stepNumber: Number(step),
20219
20258
  type
20220
20259
  };
20221
- }).filter((e) => maxStepNumber === void 0 || e.stepNumber <= maxStepNumber).sort((a, b) => a.timestamp - b.timestamp);
20260
+ }).filter((e) => maxStepNumber === void 0 || e.stepNumber <= maxStepNumber).filter((e) => !typeFilter || typeFilter.has(e.type)).sort((a, b) => a.timestamp - b.timestamp);
20222
20261
  const events = [];
20223
20262
  for (const { file } of eventFiles) try {
20224
20263
  const content = readFileSync(path.resolve(runDir, file), "utf-8");
@@ -20226,6 +20265,44 @@ function getEventContents(jobId, runId, maxStepNumber) {
20226
20265
  } catch {}
20227
20266
  return events;
20228
20267
  }
20268
+ /**
20269
+ * Get a single event by type from a run (returns the first match by timestamp).
20270
+ * Much faster than loading all events when you only need one.
20271
+ */
20272
+ function getFirstEvent(jobId, runId, typeFilter) {
20273
+ const runDir = defaultGetRunDir(jobId, runId);
20274
+ if (!existsSync(runDir)) return;
20275
+ const files = readdirSync(runDir).filter((file) => file.startsWith("event-") && file.includes(`-${typeFilter}.json`)).sort();
20276
+ if (files.length === 0) return void 0;
20277
+ try {
20278
+ const content = readFileSync(path.resolve(runDir, files[0]), "utf-8");
20279
+ return JSON.parse(content);
20280
+ } catch {
20281
+ return;
20282
+ }
20283
+ }
20284
+ /**
20285
+ * Get event stats from filenames only (no file content reading).
20286
+ * Returns total event count and max step number.
20287
+ */
20288
+ function getEventStats(jobId, runId) {
20289
+ const runDir = defaultGetRunDir(jobId, runId);
20290
+ if (!existsSync(runDir)) return {
20291
+ eventCount: 0,
20292
+ maxStep: 0
20293
+ };
20294
+ const eventFiles = readdirSync(runDir).filter((file) => file.startsWith("event-"));
20295
+ let maxStep = 0;
20296
+ for (const file of eventFiles) {
20297
+ const parts = file.split(".")[0].split("-");
20298
+ const step = Number(parts[2]);
20299
+ if (step > maxStep) maxStep = step;
20300
+ }
20301
+ return {
20302
+ eventCount: eventFiles.length,
20303
+ maxStep
20304
+ };
20305
+ }
20229
20306
  function getRunIdsByJobId(jobId) {
20230
20307
  const runsDir = path.resolve(getJobDir(jobId), "runs");
20231
20308
  if (!existsSync(runsDir)) return [];
@@ -20284,6 +20361,9 @@ function createLogDataFetcher(storage) {
20284
20361
  async getCheckpoints(jobId) {
20285
20362
  return storage.getCheckpointsByJobId(jobId);
20286
20363
  },
20364
+ async getCheckpointsForRun(jobId, runId) {
20365
+ return storage.getCheckpointsByRunId(jobId, runId);
20366
+ },
20287
20367
  async getCheckpoint(jobId, checkpointId) {
20288
20368
  return storage.retrieveCheckpoint(jobId, checkpointId);
20289
20369
  },
@@ -20292,12 +20372,23 @@ function createLogDataFetcher(storage) {
20292
20372
  },
20293
20373
  async getAllEventsForJob(jobId) {
20294
20374
  const runs = await this.getRuns(jobId);
20295
- const allEvents = [];
20296
- for (const run of runs) {
20297
- const events = await storage.getEventContents(jobId, run.runId);
20298
- allEvents.push(...events);
20299
- }
20300
- return allEvents.sort((a, b) => a.timestamp - b.timestamp);
20375
+ return (await Promise.all(runs.map((run) => storage.getEventContents(jobId, run.runId)))).flat().sort((a, b) => a.timestamp - b.timestamp);
20376
+ },
20377
+ async getTreeEventsForJob(jobId) {
20378
+ const treeEventTypes = new Set([
20379
+ "startRun",
20380
+ "resumeFromStop",
20381
+ "stopRunByDelegate",
20382
+ "callTools",
20383
+ "completeRun",
20384
+ "stopRunByError",
20385
+ "retry"
20386
+ ]);
20387
+ const runIds = storage.getRunIdsByJobId(jobId);
20388
+ return (await Promise.all(runIds.map((runId) => storage.getEventContents(jobId, runId, void 0, treeEventTypes)))).flat().sort((a, b) => a.timestamp - b.timestamp);
20389
+ },
20390
+ async getFirstEventForRun(jobId, runId, typeFilter) {
20391
+ return storage.getFirstEvent(jobId, runId, typeFilter);
20301
20392
  }
20302
20393
  };
20303
20394
  }
@@ -20316,9 +20407,13 @@ function createStorageAdapter(basePath) {
20316
20407
  getAllJobs: async () => getAllJobs$1(),
20317
20408
  retrieveJob: async (jobId) => retrieveJob(jobId),
20318
20409
  getCheckpointsByJobId: async (jobId) => getCheckpointsByJobId$1(jobId),
20410
+ getCheckpointsByRunId: async (jobId, runId) => getCheckpointsByRunId(jobId, runId),
20319
20411
  retrieveCheckpoint: async (jobId, checkpointId) => defaultRetrieveCheckpoint(jobId, checkpointId),
20320
- getEventContents: async (jobId, runId, maxStep) => getEventContents(jobId, runId, maxStep),
20412
+ getEventContents: async (jobId, runId, maxStep, typeFilter) => getEventContents(jobId, runId, maxStep, typeFilter),
20413
+ getFirstEvent: async (jobId, runId, typeFilter) => getFirstEvent(jobId, runId, typeFilter),
20414
+ getEventStats: (jobId, runId) => getEventStats(jobId, runId),
20321
20415
  getAllRuns: async () => getAllRuns$1(),
20416
+ getRunIdsByJobId: (jobId) => getRunIdsByJobId(jobId),
20322
20417
  getJobIds: () => {
20323
20418
  const jobsDir = path.join(basePath, "jobs");
20324
20419
  if (!existsSync(jobsDir)) return [];
@@ -20613,7 +20708,7 @@ function formatSummarySection(summary) {
20613
20708
  }
20614
20709
  function formatEvent(event, verbose) {
20615
20710
  const lines = [];
20616
- const time = formatTime(event.timestamp);
20711
+ const time = formatTime$1(event.timestamp);
20617
20712
  const header = `[Step ${event.stepNumber}] ${event.type}${" ".repeat(Math.max(0, 24 - event.type.length))}${time}`;
20618
20713
  lines.push(header);
20619
20714
  if (event.type === "startRun" && "inputMessages" in event) {
@@ -20674,7 +20769,7 @@ function extractTextContent$1(contents) {
20674
20769
  function formatTimestamp$2(ts) {
20675
20770
  return new Date(ts).toISOString().replace("T", " ").replace(/\.\d{3}Z$/, "");
20676
20771
  }
20677
- function formatTime(ts) {
20772
+ function formatTime$1(ts) {
20678
20773
  return new Date(ts).toISOString().slice(11, 19);
20679
20774
  }
20680
20775
  //#endregion
@@ -20997,7 +21092,7 @@ async function expertVersionsHandler(scopeName, options) {
20997
21092
  }
20998
21093
  //#endregion
20999
21094
  //#region ../../packages/runtime/package.json
21000
- var version$1 = "0.0.136";
21095
+ var version$1 = "0.0.137";
21001
21096
  //#endregion
21002
21097
  //#region ../../packages/runtime/src/helpers/usage.ts
21003
21098
  function createEmptyUsage() {
@@ -93264,8 +93359,12 @@ var DelegationExecutor = class {
93264
93359
  }
93265
93360
  });
93266
93361
  if (parentOptions?.storeEvent) {
93267
- await parentOptions.storeEvent(startRunEvent).catch(() => {});
93268
- await parentOptions.storeEvent(stopRunByErrorEvent).catch(() => {});
93362
+ await parentOptions.storeEvent(startRunEvent).catch((e) => {
93363
+ console.warn(`Failed to store startRun event for ${expert.key}: ${e}`);
93364
+ });
93365
+ await parentOptions.storeEvent(stopRunByErrorEvent).catch((e) => {
93366
+ console.warn(`Failed to store stopRunByError event for ${expert.key}: ${e}`);
93367
+ });
93269
93368
  }
93270
93369
  if (parentOptions?.eventListener) {
93271
93370
  parentOptions.eventListener(startRunEvent);
@@ -93280,14 +93379,17 @@ var DelegationExecutor = class {
93280
93379
  deltaUsage: createEmptyUsage()
93281
93380
  };
93282
93381
  }
93283
- if (resultCheckpoint.status !== "completed") return {
93284
- toolCallId,
93285
- toolName,
93286
- expertKey: expert.key,
93287
- text: `Delegation to ${expert.key} ended with status: ${resultCheckpoint.status}`,
93288
- stepNumber: resultCheckpoint.stepNumber,
93289
- deltaUsage: resultCheckpoint.usage
93290
- };
93382
+ if (resultCheckpoint.status !== "completed") {
93383
+ const errorDetail = resultCheckpoint.error ? `: ${resultCheckpoint.error.name}: ${resultCheckpoint.error.message}` : "";
93384
+ return {
93385
+ toolCallId,
93386
+ toolName,
93387
+ expertKey: expert.key,
93388
+ text: `Delegation to ${expert.key} ended with status: ${resultCheckpoint.status}${errorDetail}`,
93389
+ stepNumber: resultCheckpoint.stepNumber,
93390
+ deltaUsage: resultCheckpoint.usage
93391
+ };
93392
+ }
93291
93393
  return this.extractDelegationResult(resultCheckpoint, toolCallId, toolName, expert.key);
93292
93394
  }
93293
93395
  extractDelegationResult(checkpoint, toolCallId, toolName, expertKey) {
@@ -118997,6 +119099,12 @@ const useInput = (inputHandler, options = {}) => {
118997
119099
  */
118998
119100
  const useApp = () => (0, import_react.useContext)(AppContext);
118999
119101
  //#endregion
119102
+ //#region ../../node_modules/.bun/ink@6.8.0+26a211c426f3f87c/node_modules/ink/build/hooks/use-stdout.js
119103
+ /**
119104
+ `useStdout` is a React hook that exposes the stdout stream where Ink renders your app.
119105
+ */
119106
+ const useStdout = () => (0, import_react.useContext)(StdoutContext);
119107
+ //#endregion
119000
119108
  //#region ../../packages/tui-components/src/utils/event-queue.ts
119001
119109
  const defaultErrorLogger = (message, error) => {
119002
119110
  console.error(message, error);
@@ -120979,7 +121087,10 @@ function processDelegationTreeEvent(state, event) {
120979
121087
  model: event.model,
120980
121088
  parentRunId,
120981
121089
  childRunIds: [],
120982
- totalTokens: 0
121090
+ totalTokens: 0,
121091
+ inputTokens: 0,
121092
+ outputTokens: 0,
121093
+ cachedInputTokens: 0
120983
121094
  };
120984
121095
  state.nodes.set(event.runId, node);
120985
121096
  if (parentRunId) {
@@ -121035,6 +121146,9 @@ function processDelegationTreeEvent(state, event) {
121035
121146
  node.actionLabel = label;
121036
121147
  node.actionFileArg = fileArg;
121037
121148
  node.totalTokens += event.usage.totalTokens;
121149
+ node.inputTokens += event.usage.inputTokens;
121150
+ node.outputTokens += event.usage.outputTokens;
121151
+ node.cachedInputTokens += event.usage.cachedInputTokens;
121038
121152
  state.jobTotalTokens += event.usage.totalTokens;
121039
121153
  state.jobReasoningTokens += event.usage.reasoningTokens;
121040
121154
  state.jobInputTokens += event.usage.inputTokens;
@@ -121062,6 +121176,9 @@ function processDelegationTreeEvent(state, event) {
121062
121176
  node.actionLabel = "Completed";
121063
121177
  node.actionFileArg = void 0;
121064
121178
  node.totalTokens += event.usage.totalTokens;
121179
+ node.inputTokens += event.usage.inputTokens;
121180
+ node.outputTokens += event.usage.outputTokens;
121181
+ node.cachedInputTokens += event.usage.cachedInputTokens;
121065
121182
  state.jobTotalTokens += event.usage.totalTokens;
121066
121183
  state.jobReasoningTokens += event.usage.reasoningTokens;
121067
121184
  state.jobInputTokens += event.usage.inputTokens;
@@ -121095,6 +121212,9 @@ function processDelegationTreeEvent(state, event) {
121095
121212
  const node = state.nodes.get(nodeId);
121096
121213
  if (node) {
121097
121214
  node.totalTokens += event.usage.totalTokens;
121215
+ node.inputTokens += event.usage.inputTokens;
121216
+ node.outputTokens += event.usage.outputTokens;
121217
+ node.cachedInputTokens += event.usage.cachedInputTokens;
121098
121218
  state.jobTotalTokens += event.usage.totalTokens;
121099
121219
  state.jobReasoningTokens += event.usage.reasoningTokens;
121100
121220
  state.jobInputTokens += event.usage.inputTokens;
@@ -121154,7 +121274,7 @@ const useSpinner = ({ isActive }) => {
121154
121274
  };
121155
121275
  //#endregion
121156
121276
  //#region ../../packages/tui-components/src/execution/components/delegation-tree.tsx
121157
- function getUsageIcon(percent) {
121277
+ function getUsageIcon$2(percent) {
121158
121278
  if (percent >= 75) return USAGE_INDICATORS.FULL;
121159
121279
  if (percent >= 50) return USAGE_INDICATORS.HIGH;
121160
121280
  if (percent >= 25) return USAGE_INDICATORS.MEDIUM;
@@ -121199,7 +121319,7 @@ function TreeNodeLine({ flatNode, spinner }) {
121199
121319
  break;
121200
121320
  }
121201
121321
  const usagePercent = (node.contextWindowUsage * 100).toFixed(1);
121202
- const usageIcon = getUsageIcon(node.contextWindowUsage * 100);
121322
+ const usageIcon = getUsageIcon$2(node.contextWindowUsage * 100);
121203
121323
  const showUsage = node.status !== "completed";
121204
121324
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
121205
121325
  wrap: "truncate",
@@ -121252,14 +121372,13 @@ const DelegationTree = ({ state }) => {
121252
121372
  }, flatNode.node.runId)) });
121253
121373
  };
121254
121374
  //#endregion
121255
- //#region ../../packages/tui-components/src/execution/components/interface-panel.tsx
121256
- const InterfacePanel = ({ query, runStatus, onSubmit, delegationTreeState, runningCount, waitingCount, formattedReasoningTokens, formattedInputTokens, formattedCachedInputTokens, formattedOutputTokens, providerName, cacheHitRate, elapsedTime }) => {
121375
+ //#region ../../packages/tui-components/src/components/bottom-panel.tsx
121376
+ const BottomPanel = ({ children, onSubmit, canSubmit = true, inputPlaceholder }) => {
121257
121377
  const { input, handleInput } = useTextInput({
121258
121378
  onSubmit,
121259
- canSubmit: runStatus !== "running"
121379
+ canSubmit
121260
121380
  });
121261
121381
  useInput(handleInput);
121262
- const isWaiting = runStatus === "waiting";
121263
121382
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
121264
121383
  flexDirection: "column",
121265
121384
  borderStyle: "single",
@@ -121268,64 +121387,74 @@ const InterfacePanel = ({ query, runStatus, onSubmit, delegationTreeState, runni
121268
121387
  borderBottom: false,
121269
121388
  borderLeft: false,
121270
121389
  borderRight: false,
121271
- children: [
121272
- isWaiting ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
121390
+ children: [children, /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
121391
+ color: colors.muted,
121392
+ children: "> "
121393
+ }), input ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: input }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
121394
+ color: colors.accent,
121395
+ children: "_"
121396
+ })] }) : inputPlaceholder ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
121397
+ color: colors.muted,
121398
+ children: [inputPlaceholder, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
121273
121399
  color: colors.accent,
121274
- children: "Waiting for query..."
121275
- }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
121276
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
121277
- color: colors.success,
121278
- wrap: "truncate",
121279
- children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
121280
- bold: true,
121281
- children: "Query: "
121282
- }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: query || "–" })]
121283
- }),
121284
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
121285
- dimColor: true,
121286
- children: [
121287
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [runningCount, " running"] }),
121288
- waitingCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
121289
- " · ",
121290
- waitingCount,
121291
- " waiting"
121292
- ] }) : null,
121293
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: " · " }),
121294
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: elapsedTime }),
121295
- providerName ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [" · ", providerName] }) : null
121296
- ]
121297
- }),
121298
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
121299
- dimColor: true,
121300
- children: [
121301
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: "Tokens: " }),
121302
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: ["In ", formattedInputTokens] }),
121303
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [" · Out ", formattedOutputTokens] }),
121304
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
121305
- " ",
121306
- "· Cache ",
121307
- formattedCachedInputTokens,
121308
- "/",
121309
- cacheHitRate,
121310
- "%"
121311
- ] }),
121312
- formattedReasoningTokens !== "0" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [" · Reasoning ", formattedReasoningTokens] }) : null
121313
- ]
121314
- })
121315
- ] }),
121316
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DelegationTree, { state: delegationTreeState }),
121317
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
121318
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
121319
- color: colors.muted,
121320
- children: "> "
121321
- }),
121322
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: input }),
121323
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
121324
- color: colors.accent,
121325
- children: "_"
121326
- })
121327
- ] })
121328
- ]
121400
+ children: "_"
121401
+ })]
121402
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
121403
+ color: colors.accent,
121404
+ children: "_"
121405
+ })] })]
121406
+ });
121407
+ };
121408
+ //#endregion
121409
+ //#region ../../packages/tui-components/src/execution/components/interface-panel.tsx
121410
+ const InterfacePanel = ({ query, runStatus, onSubmit, delegationTreeState, runningCount, waitingCount, formattedReasoningTokens, formattedInputTokens, formattedCachedInputTokens, formattedOutputTokens, providerName, cacheHitRate, elapsedTime }) => {
121411
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(BottomPanel, {
121412
+ onSubmit,
121413
+ canSubmit: runStatus !== "running",
121414
+ children: [runStatus === "waiting" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
121415
+ color: colors.accent,
121416
+ children: "Waiting for query..."
121417
+ }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
121418
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
121419
+ color: colors.success,
121420
+ wrap: "truncate",
121421
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
121422
+ bold: true,
121423
+ children: "Query: "
121424
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: query || "–" })]
121425
+ }),
121426
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
121427
+ dimColor: true,
121428
+ children: [
121429
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [runningCount, " running"] }),
121430
+ waitingCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
121431
+ " · ",
121432
+ waitingCount,
121433
+ " waiting"
121434
+ ] }) : null,
121435
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: " · " }),
121436
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: elapsedTime }),
121437
+ providerName ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [" · ", providerName] }) : null
121438
+ ]
121439
+ }),
121440
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
121441
+ dimColor: true,
121442
+ children: [
121443
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: "Tokens: " }),
121444
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: ["In ", formattedInputTokens] }),
121445
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [" · Out ", formattedOutputTokens] }),
121446
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
121447
+ " ",
121448
+ Cache ",
121449
+ formattedCachedInputTokens,
121450
+ "/",
121451
+ cacheHitRate,
121452
+ "%"
121453
+ ] }),
121454
+ formattedReasoningTokens !== "0" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [" · Reasoning ", formattedReasoningTokens] }) : null
121455
+ ]
121456
+ })
121457
+ ] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DelegationTree, { state: delegationTreeState })]
121329
121458
  });
121330
121459
  };
121331
121460
  //#endregion
@@ -121585,6 +121714,2499 @@ function renderExecution(params) {
121585
121714
  };
121586
121715
  }
121587
121716
  //#endregion
121717
+ //#region ../../packages/tui-components/src/log-viewer/build-run-tree.ts
121718
+ function parseExpertName(expertKey) {
121719
+ try {
121720
+ return parseExpertKey(expertKey).name;
121721
+ } catch {
121722
+ return expertKey;
121723
+ }
121724
+ }
121725
+ function createTreeNode(runId, expertKey, model, parentRunId) {
121726
+ return {
121727
+ runId,
121728
+ expertName: parseExpertName(expertKey),
121729
+ expertKey,
121730
+ status: "running",
121731
+ actionLabel: "",
121732
+ actionFileArg: void 0,
121733
+ contextWindowUsage: 0,
121734
+ model,
121735
+ parentRunId,
121736
+ childRunIds: [],
121737
+ totalTokens: 0,
121738
+ inputTokens: 0,
121739
+ outputTokens: 0,
121740
+ cachedInputTokens: 0
121741
+ };
121742
+ }
121743
+ function addNodeToTree(state, node) {
121744
+ state.nodes.set(node.runId, node);
121745
+ if (node.parentRunId) {
121746
+ const parent = state.nodes.get(node.parentRunId);
121747
+ if (parent && !parent.childRunIds.includes(node.runId)) parent.childRunIds.push(node.runId);
121748
+ } else if (!state.rootRunId) state.rootRunId = node.runId;
121749
+ }
121750
+ function accumulateTokens(node, usage) {
121751
+ node.totalTokens += usage.totalTokens;
121752
+ node.inputTokens += usage.inputTokens;
121753
+ node.outputTokens += usage.outputTokens;
121754
+ node.cachedInputTokens += usage.cachedInputTokens;
121755
+ }
121756
+ function extractQueryFromStartRun(event) {
121757
+ if (event.type !== "startRun") return void 0;
121758
+ for (const msg of event.inputMessages) if (msg.type === "userMessage") {
121759
+ for (const part of msg.contents) if ("text" in part && part.text) return part.text.replace(/\n/g, " ");
121760
+ }
121761
+ }
121762
+ /**
121763
+ * Build a delegation tree from a list of RunEvents.
121764
+ *
121765
+ * Unlike the live execution tree (use-delegation-tree.ts), this does NOT merge
121766
+ * resume runs into their original runs. Each resume run becomes a separate node
121767
+ * in the tree, so that its checkpoints and events are individually accessible.
121768
+ *
121769
+ * The tree structure for a delegate-and-resume cycle looks like:
121770
+ *
121771
+ * Coordinator (initial run)
121772
+ * ├── Worker-A (delegate)
121773
+ * └── Worker-B (delegate)
121774
+ * Coordinator (resumed run) ← separate node, same expertKey
121775
+ *
121776
+ * Resume nodes are tracked in `resumeNodes` so the graph renderer can draw
121777
+ * merge lines (`|/`) before them.
121778
+ */
121779
+ function buildRunTreeFromEvents(events) {
121780
+ const treeState = createInitialDelegationTreeState();
121781
+ const runQueries = /* @__PURE__ */ new Map();
121782
+ const runStats = /* @__PURE__ */ new Map();
121783
+ const delegationGroups = /* @__PURE__ */ new Map();
121784
+ const currentDelegatingParent = /* @__PURE__ */ new Map();
121785
+ const parentToChildren = /* @__PURE__ */ new Map();
121786
+ const parentCompletedChildren = /* @__PURE__ */ new Map();
121787
+ const awaitingResume = /* @__PURE__ */ new Set();
121788
+ const resumeNodes = /* @__PURE__ */ new Map();
121789
+ const runIdToNodeId = /* @__PURE__ */ new Map();
121790
+ function resolveParentNodeId(runId) {
121791
+ return runIdToNodeId.get(runId) ?? runId;
121792
+ }
121793
+ function recordStats(runId, stepNumber) {
121794
+ const stats = runStats.get(runId) ?? {
121795
+ eventCount: 0,
121796
+ stepCount: 0
121797
+ };
121798
+ stats.eventCount++;
121799
+ if (stepNumber > stats.stepCount) stats.stepCount = stepNumber;
121800
+ runStats.set(runId, stats);
121801
+ }
121802
+ function onChildCompleted(childRunId) {
121803
+ const childNode = treeState.nodes.get(childRunId);
121804
+ if (!childNode?.parentRunId) return;
121805
+ const parentRunId = childNode.parentRunId;
121806
+ const count = (parentCompletedChildren.get(parentRunId) ?? 0) + 1;
121807
+ parentCompletedChildren.set(parentRunId, count);
121808
+ if (count >= (parentToChildren.get(parentRunId)?.size ?? 0)) awaitingResume.add(parentRunId);
121809
+ }
121810
+ for (const event of events) {
121811
+ recordStats(event.runId, event.stepNumber);
121812
+ switch (event.type) {
121813
+ case "startRun": {
121814
+ const parentRawRunId = event.initialCheckpoint.delegatedBy?.runId;
121815
+ const parentRunId = parentRawRunId ? resolveParentNodeId(parentRawRunId) : void 0;
121816
+ const node = createTreeNode(event.runId, event.expertKey, event.model, parentRunId);
121817
+ node.contextWindowUsage = event.initialCheckpoint.contextWindowUsage ?? 0;
121818
+ addNodeToTree(treeState, node);
121819
+ runIdToNodeId.set(event.runId, event.runId);
121820
+ if (parentRunId) {
121821
+ const children = parentToChildren.get(parentRunId) ?? /* @__PURE__ */ new Set();
121822
+ children.add(event.runId);
121823
+ parentToChildren.set(parentRunId, children);
121824
+ const currentGroup = currentDelegatingParent.get(parentRunId);
121825
+ if (currentGroup) currentGroup.push(event.runId);
121826
+ }
121827
+ const query = extractQueryFromStartRun(event);
121828
+ if (query) runQueries.set(event.runId, query);
121829
+ break;
121830
+ }
121831
+ case "resumeFromStop": {
121832
+ let originalRunId;
121833
+ for (const candidateRunId of awaitingResume) {
121834
+ const candidateNode = treeState.nodes.get(candidateRunId);
121835
+ if (candidateNode && candidateNode.expertKey === event.expertKey) {
121836
+ originalRunId = candidateRunId;
121837
+ break;
121838
+ }
121839
+ }
121840
+ if (!originalRunId) {
121841
+ for (const [nodeId, node] of treeState.nodes) if (node.expertKey === event.expertKey && node.status === "suspending") {
121842
+ originalRunId = nodeId;
121843
+ break;
121844
+ }
121845
+ }
121846
+ if (originalRunId) awaitingResume.delete(originalRunId);
121847
+ const parentRunId = (originalRunId ? treeState.nodes.get(originalRunId) : void 0)?.parentRunId;
121848
+ const node = createTreeNode(event.runId, event.expertKey, event.model, parentRunId);
121849
+ if (event.checkpoint.contextWindowUsage !== void 0) node.contextWindowUsage = event.checkpoint.contextWindowUsage;
121850
+ addNodeToTree(treeState, node);
121851
+ if (!parentRunId && originalRunId) {
121852
+ const origNode = treeState.nodes.get(originalRunId);
121853
+ if (origNode && !origNode.childRunIds.includes(event.runId)) origNode.childRunIds.push(event.runId);
121854
+ }
121855
+ runIdToNodeId.set(event.runId, event.runId);
121856
+ if (originalRunId) resumeNodes.set(event.runId, originalRunId);
121857
+ if (originalRunId) currentDelegatingParent.delete(originalRunId);
121858
+ break;
121859
+ }
121860
+ case "stopRunByDelegate": {
121861
+ const node = treeState.nodes.get(event.runId);
121862
+ if (node) {
121863
+ node.status = "suspending";
121864
+ if (event.checkpoint.contextWindowUsage !== void 0) node.contextWindowUsage = event.checkpoint.contextWindowUsage;
121865
+ const newGroup = [];
121866
+ const groups = delegationGroups.get(event.runId) ?? [];
121867
+ groups.push(newGroup);
121868
+ delegationGroups.set(event.runId, groups);
121869
+ currentDelegatingParent.set(event.runId, newGroup);
121870
+ }
121871
+ break;
121872
+ }
121873
+ case "callTools": {
121874
+ const node = treeState.nodes.get(event.runId);
121875
+ if (node) accumulateTokens(node, event.usage);
121876
+ break;
121877
+ }
121878
+ case "completeRun": {
121879
+ const node = treeState.nodes.get(event.runId);
121880
+ if (node) {
121881
+ node.status = "completed";
121882
+ accumulateTokens(node, event.usage);
121883
+ if (event.checkpoint.contextWindowUsage !== void 0) node.contextWindowUsage = event.checkpoint.contextWindowUsage;
121884
+ }
121885
+ onChildCompleted(event.runId);
121886
+ break;
121887
+ }
121888
+ case "stopRunByError": {
121889
+ const node = treeState.nodes.get(event.runId);
121890
+ if (node) {
121891
+ node.status = "error";
121892
+ if (event.checkpoint.contextWindowUsage !== void 0) node.contextWindowUsage = event.checkpoint.contextWindowUsage;
121893
+ }
121894
+ onChildCompleted(event.runId);
121895
+ break;
121896
+ }
121897
+ case "retry": {
121898
+ const node = treeState.nodes.get(event.runId);
121899
+ if (node) accumulateTokens(node, event.usage);
121900
+ break;
121901
+ }
121902
+ case "continueToNextStep": {
121903
+ const node = treeState.nodes.get(event.runId);
121904
+ if (node && event.nextCheckpoint.contextWindowUsage !== void 0) node.contextWindowUsage = event.nextCheckpoint.contextWindowUsage;
121905
+ break;
121906
+ }
121907
+ case "resolveToolResults": break;
121908
+ }
121909
+ }
121910
+ return {
121911
+ treeState,
121912
+ runQueries,
121913
+ runStats,
121914
+ delegationGroups,
121915
+ resumeNodes
121916
+ };
121917
+ }
121918
+ //#endregion
121919
+ //#region ../../packages/tui-components/src/log-viewer/components/log-info-content.tsx
121920
+ function formatDuration$1(startedAt, finishedAt) {
121921
+ const end = finishedAt ?? Date.now();
121922
+ const seconds = Math.floor((end - startedAt) / 1e3);
121923
+ if (seconds < 60) return `${seconds}s`;
121924
+ const minutes = Math.floor(seconds / 60);
121925
+ const remainingSeconds = seconds % 60;
121926
+ if (minutes < 60) return `${minutes}m ${remainingSeconds}s`;
121927
+ return `${Math.floor(minutes / 60)}h ${minutes % 60}m`;
121928
+ }
121929
+ function formatShortDate$1(timestamp) {
121930
+ const d = new Date(timestamp);
121931
+ return `${d.getMonth() + 1}/${d.getDate()} ${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
121932
+ }
121933
+ function getUsageIcon$1(ratio) {
121934
+ const percent = ratio * 100;
121935
+ if (percent >= 75) return USAGE_INDICATORS.FULL;
121936
+ if (percent >= 50) return USAGE_INDICATORS.HIGH;
121937
+ if (percent >= 25) return USAGE_INDICATORS.MEDIUM;
121938
+ if (percent >= 5) return USAGE_INDICATORS.LOW;
121939
+ return USAGE_INDICATORS.EMPTY;
121940
+ }
121941
+ function statusColor$3(status) {
121942
+ if (status === "completed") return colors.primary;
121943
+ if (status === "error") return colors.destructive;
121944
+ if (status === "suspending") return colors.primary;
121945
+ return colors.muted;
121946
+ }
121947
+ function JobInfoContent({ job }) {
121948
+ const duration = formatDuration$1(job.startedAt, job.finishedAt);
121949
+ const totalInput = job.usage.inputTokens + (job.usage.cachedInputTokens ?? 0);
121950
+ const cacheRate = totalInput > 0 ? (job.usage.cachedInputTokens ?? 0) / totalInput * 100 : 0;
121951
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
121952
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
121953
+ wrap: "truncate",
121954
+ children: [
121955
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
121956
+ bold: true,
121957
+ children: job.coordinatorExpertKey
121958
+ }),
121959
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
121960
+ dimColor: true,
121961
+ children: " · "
121962
+ }),
121963
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
121964
+ color: statusColor$3(job.status),
121965
+ children: job.status
121966
+ }),
121967
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
121968
+ dimColor: true,
121969
+ children: " · "
121970
+ }),
121971
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: job.id })
121972
+ ]
121973
+ }),
121974
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
121975
+ dimColor: true,
121976
+ wrap: "truncate",
121977
+ children: [
121978
+ job.totalSteps,
121979
+ " steps · ",
121980
+ duration,
121981
+ " · Started ",
121982
+ formatShortDate$1(job.startedAt),
121983
+ job.finishedAt ? ` · Finished ${formatShortDate$1(job.finishedAt)}` : ""
121984
+ ]
121985
+ }),
121986
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
121987
+ dimColor: true,
121988
+ wrap: "truncate",
121989
+ children: [
121990
+ "Tokens: In ",
121991
+ formatTokenCount(job.usage.inputTokens),
121992
+ " · Out",
121993
+ " ",
121994
+ formatTokenCount(job.usage.outputTokens),
121995
+ (job.usage.cachedInputTokens ?? 0) > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
121996
+ " · Cache ",
121997
+ formatTokenCount(job.usage.cachedInputTokens ?? 0),
121998
+ "/",
121999
+ cacheRate.toFixed(2),
122000
+ "%"
122001
+ ] }) : null
122002
+ ]
122003
+ })
122004
+ ] });
122005
+ }
122006
+ function RunInfoContent({ run, treeNode, providerName, checkpointCount, eventCount }) {
122007
+ const parts = [];
122008
+ if (providerName) parts.push(providerName);
122009
+ if (treeNode?.model) parts.push(treeNode.model);
122010
+ if (checkpointCount !== void 0) parts.push(`${checkpointCount} steps`);
122011
+ if (eventCount !== void 0) parts.push(`${eventCount} events`);
122012
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
122013
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122014
+ wrap: "truncate",
122015
+ children: [
122016
+ run.expertKey ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122017
+ bold: true,
122018
+ children: run.expertKey
122019
+ }) : null,
122020
+ run.expertKey ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122021
+ dimColor: true,
122022
+ children: " · "
122023
+ }) : null,
122024
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122025
+ color: treeNode ? statusColor$3(treeNode.status) : void 0,
122026
+ children: treeNode ? treeNode.status : "unknown"
122027
+ })
122028
+ ]
122029
+ }),
122030
+ parts.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122031
+ dimColor: true,
122032
+ wrap: "truncate",
122033
+ children: parts.join(" · ")
122034
+ }) : null,
122035
+ treeNode ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122036
+ dimColor: true,
122037
+ wrap: "truncate",
122038
+ children: [
122039
+ "Tokens: In ",
122040
+ formatTokenCount(treeNode.inputTokens),
122041
+ " · Out",
122042
+ " ",
122043
+ formatTokenCount(treeNode.outputTokens),
122044
+ treeNode.cachedInputTokens > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
122045
+ " · Cache ",
122046
+ formatTokenCount(treeNode.cachedInputTokens),
122047
+ "/",
122048
+ (treeNode.cachedInputTokens / (treeNode.inputTokens + treeNode.cachedInputTokens) * 100).toFixed(2),
122049
+ "%"
122050
+ ] }) : null
122051
+ ]
122052
+ }) : null
122053
+ ] });
122054
+ }
122055
+ function CheckpointInfoContent({ checkpoint }) {
122056
+ const cp = checkpoint;
122057
+ const usageIcon = cp.contextWindowUsage !== void 0 ? ` ${getUsageIcon$1(cp.contextWindowUsage)}` : "";
122058
+ const usagePercent = cp.contextWindowUsage !== void 0 ? ` ${(cp.contextWindowUsage * 100).toFixed(1)}%` : "";
122059
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
122060
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122061
+ wrap: "truncate",
122062
+ children: [
122063
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122064
+ bold: true,
122065
+ children: ["Step ", cp.stepNumber]
122066
+ }),
122067
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122068
+ dimColor: true,
122069
+ children: " · "
122070
+ }),
122071
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122072
+ color: statusColor$3(cp.status),
122073
+ children: cp.status
122074
+ }),
122075
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122076
+ dimColor: true,
122077
+ children: " · "
122078
+ }),
122079
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cp.expert.key }),
122080
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122081
+ dimColor: true,
122082
+ children: " · "
122083
+ }),
122084
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cp.id })
122085
+ ]
122086
+ }),
122087
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122088
+ dimColor: true,
122089
+ wrap: "truncate",
122090
+ children: [
122091
+ cp.messages.length,
122092
+ " msgs",
122093
+ usageIcon ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
122094
+ " ·",
122095
+ usageIcon,
122096
+ usagePercent
122097
+ ] }) : null,
122098
+ " · ",
122099
+ "Tokens: In ",
122100
+ formatTokenCount(cp.usage.inputTokens),
122101
+ " · Out",
122102
+ " ",
122103
+ formatTokenCount(cp.usage.outputTokens)
122104
+ ]
122105
+ }),
122106
+ cp.error ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122107
+ color: colors.destructive,
122108
+ wrap: "truncate",
122109
+ children: [
122110
+ "Error: ",
122111
+ cp.error.name,
122112
+ ": ",
122113
+ cp.error.message
122114
+ ]
122115
+ }) : null,
122116
+ cp.delegateTo && cp.delegateTo.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122117
+ dimColor: true,
122118
+ wrap: "truncate",
122119
+ children: ["Delegates: ", cp.delegateTo.map((d) => d.expert.key).join(", ")]
122120
+ }) : null
122121
+ ] });
122122
+ }
122123
+ function EventInfoContent({ event }) {
122124
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
122125
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122126
+ wrap: "truncate",
122127
+ children: [
122128
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122129
+ bold: true,
122130
+ children: event.type
122131
+ }),
122132
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122133
+ dimColor: true,
122134
+ children: " · "
122135
+ }),
122136
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: ["Step ", event.stepNumber] }),
122137
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122138
+ dimColor: true,
122139
+ children: " · "
122140
+ }),
122141
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.expertKey }),
122142
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122143
+ dimColor: true,
122144
+ children: " · "
122145
+ }),
122146
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.id })
122147
+ ]
122148
+ }),
122149
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122150
+ dimColor: true,
122151
+ wrap: "truncate",
122152
+ children: [
122153
+ formatShortDate$1(event.timestamp),
122154
+ " · run:",
122155
+ event.runId
122156
+ ]
122157
+ }),
122158
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventSummaryLine, { event })
122159
+ ] });
122160
+ }
122161
+ function EventSummaryLine({ event }) {
122162
+ switch (event.type) {
122163
+ case "callTools": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122164
+ dimColor: true,
122165
+ wrap: "truncate",
122166
+ children: ["Tools: ", event.toolCalls.map((tc) => `${tc.skillName}/${tc.toolName}`).join(", ")]
122167
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122168
+ dimColor: true,
122169
+ wrap: "truncate",
122170
+ children: [
122171
+ "Tokens: In ",
122172
+ formatTokenCount(event.usage.inputTokens),
122173
+ " · Out",
122174
+ " ",
122175
+ formatTokenCount(event.usage.outputTokens)
122176
+ ]
122177
+ })] });
122178
+ case "completeRun": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122179
+ dimColor: true,
122180
+ wrap: "truncate",
122181
+ children: ["Result: ", event.text]
122182
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122183
+ dimColor: true,
122184
+ wrap: "truncate",
122185
+ children: [
122186
+ "Tokens: In ",
122187
+ formatTokenCount(event.usage.inputTokens),
122188
+ " · Out",
122189
+ " ",
122190
+ formatTokenCount(event.usage.outputTokens)
122191
+ ]
122192
+ })] });
122193
+ case "stopRunByError": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122194
+ color: colors.destructive,
122195
+ wrap: "truncate",
122196
+ children: [
122197
+ "Error: ",
122198
+ event.error.name,
122199
+ ": ",
122200
+ event.error.message
122201
+ ]
122202
+ });
122203
+ case "retry": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122204
+ color: colors.warn,
122205
+ wrap: "truncate",
122206
+ children: ["Reason: ", event.reason]
122207
+ });
122208
+ case "startRun": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122209
+ dimColor: true,
122210
+ wrap: "truncate",
122211
+ children: [
122212
+ "Model: ",
122213
+ event.model,
122214
+ " · ",
122215
+ event.inputMessages.length,
122216
+ " input messages"
122217
+ ]
122218
+ });
122219
+ case "stopRunByDelegate": return event.checkpoint.delegateTo ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122220
+ dimColor: true,
122221
+ wrap: "truncate",
122222
+ children: ["Delegates: ", event.checkpoint.delegateTo.map((d) => d.expert.key).join(", ")]
122223
+ }) : null;
122224
+ case "startGeneration": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122225
+ dimColor: true,
122226
+ wrap: "truncate",
122227
+ children: [event.messages.length, " messages"]
122228
+ });
122229
+ default: return null;
122230
+ }
122231
+ }
122232
+ //#endregion
122233
+ //#region ../../packages/tui-components/src/log-viewer/screens/checkpoint-detail.tsx
122234
+ const CheckpointDetailScreen = ({ checkpoint, onBack }) => {
122235
+ useInput((char, key) => {
122236
+ if (key.escape || char === "b") onBack();
122237
+ });
122238
+ const cp = checkpoint;
122239
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122240
+ flexDirection: "column",
122241
+ flexGrow: 1,
122242
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122243
+ color: colors.accent,
122244
+ bold: true,
122245
+ children: "Checkpoint Detail"
122246
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122247
+ color: colors.muted,
122248
+ children: " b:Back q:Quit"
122249
+ })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122250
+ flexDirection: "column",
122251
+ paddingX: 1,
122252
+ children: [
122253
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122254
+ gap: 1,
122255
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122256
+ color: colors.accent,
122257
+ children: "ID:"
122258
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cp.id })]
122259
+ }),
122260
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122261
+ gap: 1,
122262
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122263
+ color: colors.accent,
122264
+ children: "Status:"
122265
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122266
+ color: cp.status === "completed" ? colors.success : cp.status === "proceeding" ? colors.accent : colors.destructive,
122267
+ children: cp.status
122268
+ })]
122269
+ }),
122270
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122271
+ gap: 1,
122272
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122273
+ color: colors.accent,
122274
+ children: "Step:"
122275
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cp.stepNumber })]
122276
+ }),
122277
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122278
+ gap: 1,
122279
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122280
+ color: colors.accent,
122281
+ children: "Expert:"
122282
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
122283
+ cp.expert.name,
122284
+ " (",
122285
+ cp.expert.key,
122286
+ "@",
122287
+ cp.expert.version,
122288
+ ")"
122289
+ ] })]
122290
+ }),
122291
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122292
+ gap: 1,
122293
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122294
+ color: colors.accent,
122295
+ children: "Messages:"
122296
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cp.messages.length })]
122297
+ }),
122298
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122299
+ gap: 1,
122300
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122301
+ color: colors.accent,
122302
+ children: "Input Tokens:"
122303
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cp.usage.inputTokens.toLocaleString() })]
122304
+ }),
122305
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122306
+ gap: 1,
122307
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122308
+ color: colors.accent,
122309
+ children: "Output Tokens:"
122310
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cp.usage.outputTokens.toLocaleString() })]
122311
+ }),
122312
+ cp.contextWindow && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122313
+ gap: 1,
122314
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122315
+ color: colors.accent,
122316
+ children: "Context Window:"
122317
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cp.contextWindow.toLocaleString() })]
122318
+ }),
122319
+ cp.contextWindowUsage !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122320
+ gap: 1,
122321
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122322
+ color: colors.accent,
122323
+ children: "Context Usage:"
122324
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [(cp.contextWindowUsage * 100).toFixed(1), "%"] })]
122325
+ }),
122326
+ cp.error && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122327
+ flexDirection: "column",
122328
+ children: [
122329
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122330
+ color: colors.destructive,
122331
+ bold: true,
122332
+ children: "Error:"
122333
+ }),
122334
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122335
+ color: colors.destructive,
122336
+ children: [
122337
+ cp.error.name,
122338
+ ": ",
122339
+ truncateText(cp.error.message, 100)
122340
+ ]
122341
+ }),
122342
+ cp.error.statusCode && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122343
+ color: colors.muted,
122344
+ children: ["Status Code: ", cp.error.statusCode]
122345
+ }),
122346
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122347
+ color: colors.muted,
122348
+ children: ["Retryable: ", cp.error.isRetryable ? "yes" : "no"]
122349
+ })
122350
+ ]
122351
+ }),
122352
+ cp.delegateTo && cp.delegateTo.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122353
+ flexDirection: "column",
122354
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122355
+ color: colors.accent,
122356
+ bold: true,
122357
+ children: "Delegating to:"
122358
+ }), cp.delegateTo.map((d) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122359
+ color: colors.muted,
122360
+ children: [
122361
+ "- ",
122362
+ d.expert.key,
122363
+ ": ",
122364
+ truncateText(d.query, 60)
122365
+ ]
122366
+ }, d.toolCallId))]
122367
+ }),
122368
+ cp.delegatedBy && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122369
+ flexDirection: "column",
122370
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122371
+ color: colors.accent,
122372
+ bold: true,
122373
+ children: "Delegated by:"
122374
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122375
+ color: colors.muted,
122376
+ children: [
122377
+ cp.delegatedBy.expert.key,
122378
+ " via ",
122379
+ cp.delegatedBy.toolName
122380
+ ]
122381
+ })]
122382
+ }),
122383
+ cp.pendingToolCalls && cp.pendingToolCalls.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122384
+ gap: 1,
122385
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122386
+ color: colors.accent,
122387
+ children: "Pending Tool Calls:"
122388
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cp.pendingToolCalls.length })]
122389
+ })
122390
+ ]
122391
+ })]
122392
+ });
122393
+ };
122394
+ //#endregion
122395
+ //#region ../../packages/tui-components/src/log-viewer/screens/event-detail.tsx
122396
+ const EventDetailScreen = ({ event, onBack }) => {
122397
+ useInput((char, key) => {
122398
+ if (key.escape || char === "b") onBack();
122399
+ });
122400
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122401
+ flexDirection: "column",
122402
+ flexGrow: 1,
122403
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122404
+ color: colors.accent,
122405
+ bold: true,
122406
+ children: "Event Detail"
122407
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122408
+ color: colors.muted,
122409
+ children: " b:Back q:Quit"
122410
+ })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122411
+ flexDirection: "column",
122412
+ paddingX: 1,
122413
+ children: [
122414
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122415
+ gap: 1,
122416
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122417
+ color: colors.accent,
122418
+ children: "ID:"
122419
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.id })]
122420
+ }),
122421
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122422
+ gap: 1,
122423
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122424
+ color: colors.accent,
122425
+ children: "Type:"
122426
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122427
+ bold: true,
122428
+ children: event.type
122429
+ })]
122430
+ }),
122431
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122432
+ gap: 1,
122433
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122434
+ color: colors.accent,
122435
+ children: "Step:"
122436
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.stepNumber })]
122437
+ }),
122438
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122439
+ gap: 1,
122440
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122441
+ color: colors.accent,
122442
+ children: "Expert:"
122443
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.expertKey })]
122444
+ }),
122445
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122446
+ gap: 1,
122447
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122448
+ color: colors.accent,
122449
+ children: "Run:"
122450
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.runId })]
122451
+ }),
122452
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122453
+ gap: 1,
122454
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122455
+ color: colors.accent,
122456
+ children: "Job:"
122457
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.jobId })]
122458
+ }),
122459
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122460
+ gap: 1,
122461
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122462
+ color: colors.accent,
122463
+ children: "Timestamp:"
122464
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: formatTimestamp$1(event.timestamp) })]
122465
+ }),
122466
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
122467
+ marginTop: 1,
122468
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122469
+ color: colors.accent,
122470
+ bold: true,
122471
+ children: "Payload:"
122472
+ })
122473
+ }),
122474
+ renderEventPayload(event)
122475
+ ]
122476
+ })]
122477
+ });
122478
+ };
122479
+ function renderEventPayload(event) {
122480
+ switch (event.type) {
122481
+ case "startRun": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122482
+ flexDirection: "column",
122483
+ children: [
122484
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122485
+ gap: 1,
122486
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122487
+ color: colors.muted,
122488
+ children: "Model:"
122489
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.model })]
122490
+ }),
122491
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122492
+ gap: 1,
122493
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122494
+ color: colors.muted,
122495
+ children: "Input Messages:"
122496
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.inputMessages.length })]
122497
+ }),
122498
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122499
+ gap: 1,
122500
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122501
+ color: colors.muted,
122502
+ children: "Checkpoint Status:"
122503
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.initialCheckpoint.status })]
122504
+ })
122505
+ ]
122506
+ });
122507
+ case "callTools": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122508
+ flexDirection: "column",
122509
+ children: [
122510
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122511
+ gap: 1,
122512
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122513
+ color: colors.muted,
122514
+ children: "Tool Calls:"
122515
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.toolCalls.length })]
122516
+ }),
122517
+ event.toolCalls.map((tc) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122518
+ flexDirection: "column",
122519
+ marginLeft: 2,
122520
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122521
+ color: colors.primary,
122522
+ children: [
122523
+ tc.skillName,
122524
+ "/",
122525
+ tc.toolName,
122526
+ " (",
122527
+ tc.id,
122528
+ ")"
122529
+ ]
122530
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122531
+ color: colors.muted,
122532
+ children: truncateText(JSON.stringify(tc.args), 120)
122533
+ })]
122534
+ }, tc.id)),
122535
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122536
+ gap: 1,
122537
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122538
+ color: colors.muted,
122539
+ children: "Input Tokens:"
122540
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.usage.inputTokens.toLocaleString() })]
122541
+ }),
122542
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122543
+ gap: 1,
122544
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122545
+ color: colors.muted,
122546
+ children: "Output Tokens:"
122547
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.usage.outputTokens.toLocaleString() })]
122548
+ })
122549
+ ]
122550
+ });
122551
+ case "completeRun": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122552
+ flexDirection: "column",
122553
+ children: [
122554
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122555
+ gap: 1,
122556
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122557
+ color: colors.muted,
122558
+ children: "Result:"
122559
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: truncateText(event.text, 200) })]
122560
+ }),
122561
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122562
+ gap: 1,
122563
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122564
+ color: colors.muted,
122565
+ children: "Input Tokens:"
122566
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.usage.inputTokens.toLocaleString() })]
122567
+ }),
122568
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122569
+ gap: 1,
122570
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122571
+ color: colors.muted,
122572
+ children: "Output Tokens:"
122573
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.usage.outputTokens.toLocaleString() })]
122574
+ })
122575
+ ]
122576
+ });
122577
+ case "stopRunByError": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122578
+ flexDirection: "column",
122579
+ children: [
122580
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122581
+ gap: 1,
122582
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122583
+ color: colors.destructive,
122584
+ children: "Error:"
122585
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122586
+ color: colors.destructive,
122587
+ children: [
122588
+ event.error.name,
122589
+ ": ",
122590
+ event.error.message
122591
+ ]
122592
+ })]
122593
+ }),
122594
+ event.error.statusCode && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122595
+ gap: 1,
122596
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122597
+ color: colors.muted,
122598
+ children: "Status Code:"
122599
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.error.statusCode })]
122600
+ }),
122601
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122602
+ gap: 1,
122603
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122604
+ color: colors.muted,
122605
+ children: "Retryable:"
122606
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.error.isRetryable ? "yes" : "no" })]
122607
+ })
122608
+ ]
122609
+ });
122610
+ case "retry": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
122611
+ flexDirection: "column",
122612
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122613
+ gap: 1,
122614
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122615
+ color: colors.warn,
122616
+ children: "Reason:"
122617
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.reason })]
122618
+ })
122619
+ });
122620
+ case "startGeneration": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
122621
+ flexDirection: "column",
122622
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122623
+ gap: 1,
122624
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122625
+ color: colors.muted,
122626
+ children: "Messages:"
122627
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.messages.length })]
122628
+ })
122629
+ });
122630
+ case "stopRunByDelegate": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122631
+ flexDirection: "column",
122632
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122633
+ gap: 1,
122634
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122635
+ color: colors.muted,
122636
+ children: "Step:"
122637
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.step.stepNumber })]
122638
+ }), event.checkpoint.delegateTo && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122639
+ flexDirection: "column",
122640
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122641
+ color: colors.muted,
122642
+ children: "Delegates:"
122643
+ }), event.checkpoint.delegateTo.map((d) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122644
+ color: colors.primary,
122645
+ children: [
122646
+ "- ",
122647
+ d.expert.key,
122648
+ ": ",
122649
+ truncateText(d.query, 60)
122650
+ ]
122651
+ }, d.toolCallId))]
122652
+ })]
122653
+ });
122654
+ default: return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122655
+ color: colors.muted,
122656
+ children: truncateText(JSON.stringify(event, null, 2), 500)
122657
+ });
122658
+ }
122659
+ }
122660
+ //#endregion
122661
+ //#region ../../packages/tui-components/src/log-viewer/screens/job-detail.tsx
122662
+ const JobDetailScreen = ({ job, onBack, onViewRuns }) => {
122663
+ useInput((char, key) => {
122664
+ if (key.escape || char === "b") {
122665
+ onBack();
122666
+ return;
122667
+ }
122668
+ if (key.return || char === "r") onViewRuns(job);
122669
+ });
122670
+ const statusColor = job.status === "completed" ? colors.success : job.status === "running" ? colors.accent : colors.destructive;
122671
+ const duration = job.finishedAt && job.startedAt ? `${((job.finishedAt - job.startedAt) / 1e3).toFixed(1)}s` : "running...";
122672
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122673
+ flexDirection: "column",
122674
+ flexGrow: 1,
122675
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122676
+ color: colors.accent,
122677
+ bold: true,
122678
+ children: "Job Detail"
122679
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122680
+ color: colors.muted,
122681
+ children: " Enter/r:View Runs b:Back q:Quit"
122682
+ })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122683
+ flexDirection: "column",
122684
+ paddingX: 1,
122685
+ children: [
122686
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122687
+ gap: 1,
122688
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122689
+ color: colors.accent,
122690
+ children: "ID:"
122691
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: job.id })]
122692
+ }),
122693
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122694
+ gap: 1,
122695
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122696
+ color: colors.accent,
122697
+ children: "Status:"
122698
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122699
+ color: statusColor,
122700
+ children: job.status
122701
+ })]
122702
+ }),
122703
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122704
+ gap: 1,
122705
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122706
+ color: colors.accent,
122707
+ children: "Expert:"
122708
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: job.coordinatorExpertKey })]
122709
+ }),
122710
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122711
+ gap: 1,
122712
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122713
+ color: colors.accent,
122714
+ children: "Version:"
122715
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: job.runtimeVersion })]
122716
+ }),
122717
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122718
+ gap: 1,
122719
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122720
+ color: colors.accent,
122721
+ children: "Steps:"
122722
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: job.totalSteps })]
122723
+ }),
122724
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122725
+ gap: 1,
122726
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122727
+ color: colors.accent,
122728
+ children: "Duration:"
122729
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: duration })]
122730
+ }),
122731
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122732
+ gap: 1,
122733
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122734
+ color: colors.accent,
122735
+ children: "Started:"
122736
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: formatTimestamp$1(job.startedAt) })]
122737
+ }),
122738
+ job.finishedAt && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122739
+ gap: 1,
122740
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122741
+ color: colors.accent,
122742
+ children: "Finished:"
122743
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: formatTimestamp$1(job.finishedAt) })]
122744
+ }),
122745
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122746
+ gap: 1,
122747
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122748
+ color: colors.accent,
122749
+ children: "Input Tokens:"
122750
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: job.usage.inputTokens.toLocaleString() })]
122751
+ }),
122752
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122753
+ gap: 1,
122754
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122755
+ color: colors.accent,
122756
+ children: "Output Tokens:"
122757
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: job.usage.outputTokens.toLocaleString() })]
122758
+ })
122759
+ ]
122760
+ })]
122761
+ });
122762
+ };
122763
+ //#endregion
122764
+ //#region ../../packages/tui-components/src/log-viewer/screens/job-list.tsx
122765
+ const MAX_VISIBLE = 10;
122766
+ function formatDuration(startedAt, finishedAt) {
122767
+ const end = finishedAt ?? Date.now();
122768
+ const seconds = Math.floor((end - startedAt) / 1e3);
122769
+ if (seconds < 60) return `${seconds}s`;
122770
+ const minutes = Math.floor(seconds / 60);
122771
+ const remainingSeconds = seconds % 60;
122772
+ if (minutes < 60) return `${minutes}m ${remainingSeconds}s`;
122773
+ return `${Math.floor(minutes / 60)}h ${minutes % 60}m`;
122774
+ }
122775
+ function formatShortDate(timestamp) {
122776
+ const d = new Date(timestamp);
122777
+ return `${d.getMonth() + 1}/${d.getDate()} ${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
122778
+ }
122779
+ function statusColor$2(status) {
122780
+ if (status === "completed") return colors.success;
122781
+ if (status === "running" || status === "proceeding" || status === "init") return colors.accent;
122782
+ return colors.destructive;
122783
+ }
122784
+ const JobListScreen = ({ items, onSelectJob, onViewJobDetail, onSelectedChange }) => {
122785
+ const { selectedIndex, handleNavigation } = useListNavigation({
122786
+ items,
122787
+ onSelect: (item) => onSelectJob(item.job)
122788
+ });
122789
+ useInput((char, key) => {
122790
+ if (char === "d" && items[selectedIndex]) {
122791
+ onViewJobDetail(items[selectedIndex].job);
122792
+ return;
122793
+ }
122794
+ handleNavigation(char, key);
122795
+ });
122796
+ const selectedItem = items[selectedIndex];
122797
+ (0, import_react.useEffect)(() => {
122798
+ onSelectedChange(selectedItem?.job);
122799
+ }, [selectedItem, onSelectedChange]);
122800
+ const { scrollOffset, displayItems } = (0, import_react.useMemo)(() => {
122801
+ const offset = Math.max(0, Math.min(selectedIndex - MAX_VISIBLE + 1, items.length - MAX_VISIBLE));
122802
+ return {
122803
+ scrollOffset: offset,
122804
+ displayItems: items.slice(offset, offset + MAX_VISIBLE)
122805
+ };
122806
+ }, [items, selectedIndex]);
122807
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122808
+ flexDirection: "column",
122809
+ flexGrow: 1,
122810
+ children: [
122811
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [
122812
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122813
+ color: colors.accent,
122814
+ bold: true,
122815
+ children: "Jobs"
122816
+ }),
122817
+ items.length > MAX_VISIBLE && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122818
+ color: colors.muted,
122819
+ children: [
122820
+ " ",
122821
+ "(",
122822
+ selectedIndex + 1,
122823
+ "/",
122824
+ items.length,
122825
+ ")"
122826
+ ]
122827
+ }),
122828
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122829
+ color: colors.muted,
122830
+ children: " Enter:Select d:Detail q:Quit"
122831
+ })
122832
+ ] }),
122833
+ scrollOffset > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122834
+ color: colors.muted,
122835
+ children: "..."
122836
+ }),
122837
+ displayItems.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122838
+ color: colors.muted,
122839
+ children: "No jobs found"
122840
+ }) : displayItems.map((item, i) => {
122841
+ const isSelected = scrollOffset + i === selectedIndex;
122842
+ const { job, query } = item;
122843
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
122844
+ flexDirection: "column",
122845
+ children: [
122846
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122847
+ color: isSelected ? colors.accent : colors.muted,
122848
+ children: isSelected ? " ▸ " : " "
122849
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122850
+ wrap: "truncate",
122851
+ children: [
122852
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122853
+ bold: true,
122854
+ children: job.coordinatorExpertKey
122855
+ }),
122856
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122857
+ dimColor: true,
122858
+ children: " · "
122859
+ }),
122860
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122861
+ color: statusColor$2(job.status),
122862
+ children: job.status
122863
+ }),
122864
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122865
+ dimColor: true,
122866
+ children: " · "
122867
+ }),
122868
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [job.totalSteps, " steps"] }),
122869
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122870
+ dimColor: true,
122871
+ children: " · "
122872
+ }),
122873
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: formatDuration(job.startedAt, job.finishedAt) }),
122874
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122875
+ dimColor: true,
122876
+ children: " "
122877
+ }),
122878
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122879
+ dimColor: true,
122880
+ children: formatShortDate(job.startedAt)
122881
+ })
122882
+ ]
122883
+ })] }),
122884
+ query ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
122885
+ paddingLeft: 3,
122886
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122887
+ dimColor: true,
122888
+ wrap: "truncate",
122889
+ children: [
122890
+ ">",
122891
+ " ",
122892
+ query
122893
+ ]
122894
+ })
122895
+ }) : null,
122896
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
122897
+ paddingLeft: 3,
122898
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
122899
+ dimColor: true,
122900
+ wrap: "truncate",
122901
+ children: [
122902
+ "Tokens: In ",
122903
+ formatTokenCount(job.usage.inputTokens),
122904
+ " · Out",
122905
+ " ",
122906
+ formatTokenCount(job.usage.outputTokens)
122907
+ ]
122908
+ })
122909
+ })
122910
+ ]
122911
+ }, job.id);
122912
+ }),
122913
+ scrollOffset + MAX_VISIBLE < items.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
122914
+ color: colors.muted,
122915
+ children: "..."
122916
+ })
122917
+ ]
122918
+ });
122919
+ };
122920
+ //#endregion
122921
+ //#region ../../packages/tui-components/src/hooks/use-terminal-height.ts
122922
+ /**
122923
+ * Returns the current terminal height in rows and updates on resize.
122924
+ */
122925
+ function useTerminalHeight() {
122926
+ const { stdout } = useStdout();
122927
+ const [height, setHeight] = (0, import_react.useState)(() => stdout?.rows ?? 24);
122928
+ (0, import_react.useEffect)(() => {
122929
+ if (!stdout) return;
122930
+ const onResize = () => {
122931
+ setHeight(stdout.rows);
122932
+ };
122933
+ stdout.on("resize", onResize);
122934
+ return () => {
122935
+ stdout.off("resize", onResize);
122936
+ };
122937
+ }, [stdout]);
122938
+ return height;
122939
+ }
122940
+ /**
122941
+ * Compute the number of list items that fit on screen.
122942
+ *
122943
+ * @param terminalHeight - Total terminal rows
122944
+ * @param reservedLines - Lines used by headers, tab bars, bottom panel, etc.
122945
+ * @param linesPerItem - How many terminal lines each list item occupies
122946
+ * @param minItems - Minimum items to show regardless of terminal size
122947
+ */
122948
+ function computeVisibleItems(terminalHeight, reservedLines, linesPerItem, minItems = 3) {
122949
+ const available = terminalHeight - reservedLines;
122950
+ return Math.max(minItems, Math.floor(available / linesPerItem));
122951
+ }
122952
+ //#endregion
122953
+ //#region ../../packages/tui-components/src/log-viewer/screens/activity-list.tsx
122954
+ function buildActivities(events) {
122955
+ const activities = [];
122956
+ for (const event of events) switch (event.type) {
122957
+ case "startRun":
122958
+ activities.push({
122959
+ label: `Started run with model ${event.model}`,
122960
+ detail: `${event.inputMessages.length} input messages`,
122961
+ color: colors.accent,
122962
+ timestamp: event.timestamp,
122963
+ event
122964
+ });
122965
+ break;
122966
+ case "callTools": {
122967
+ const toolNames = event.toolCalls.map((tc) => tc.toolName).join(", ");
122968
+ const totalTokens = event.usage.inputTokens + event.usage.outputTokens;
122969
+ activities.push({
122970
+ label: `Called tools: ${toolNames}`,
122971
+ detail: `${event.toolCalls.length} call(s) · ${formatTokenCount(totalTokens)} tokens`,
122972
+ color: "magenta",
122973
+ timestamp: event.timestamp,
122974
+ event
122975
+ });
122976
+ break;
122977
+ }
122978
+ case "completeRun": {
122979
+ const resultPreview = event.text.length > 80 ? `${event.text.slice(0, 80)}...` : event.text;
122980
+ activities.push({
122981
+ label: "Run completed",
122982
+ detail: resultPreview,
122983
+ color: colors.success,
122984
+ timestamp: event.timestamp,
122985
+ event
122986
+ });
122987
+ break;
122988
+ }
122989
+ case "stopRunByError":
122990
+ activities.push({
122991
+ label: `Error: ${event.error.name}`,
122992
+ detail: event.error.message,
122993
+ color: colors.destructive,
122994
+ timestamp: event.timestamp,
122995
+ event
122996
+ });
122997
+ break;
122998
+ case "stopRunByDelegate": {
122999
+ const delegates = event.checkpoint.delegateTo?.map((d) => d.expert.key).join(", ");
123000
+ activities.push({
123001
+ label: `Delegated to ${delegates ?? "unknown"}`,
123002
+ color: colors.warn,
123003
+ timestamp: event.timestamp,
123004
+ event
123005
+ });
123006
+ break;
123007
+ }
123008
+ case "retry":
123009
+ activities.push({
123010
+ label: `Retried: ${event.reason}`,
123011
+ color: colors.warn,
123012
+ timestamp: event.timestamp,
123013
+ event
123014
+ });
123015
+ break;
123016
+ case "resumeFromStop":
123017
+ activities.push({
123018
+ label: "Resumed from delegation",
123019
+ detail: `Step ${event.checkpoint.stepNumber}`,
123020
+ color: colors.accent,
123021
+ timestamp: event.timestamp,
123022
+ event
123023
+ });
123024
+ break;
123025
+ case "resolveToolResults":
123026
+ activities.push({
123027
+ label: `Resolved ${event.toolResults.length} tool result(s)`,
123028
+ color: colors.primary,
123029
+ timestamp: event.timestamp,
123030
+ event
123031
+ });
123032
+ break;
123033
+ default: break;
123034
+ }
123035
+ return activities;
123036
+ }
123037
+ //#endregion
123038
+ //#region ../../packages/tui-components/src/log-viewer/screens/run-detail.tsx
123039
+ const TAB_ORDER = [
123040
+ "activities",
123041
+ "checkpoints",
123042
+ "events"
123043
+ ];
123044
+ const TAB_LABELS = {
123045
+ activities: "Activities",
123046
+ checkpoints: "Checkpoints",
123047
+ events: "Events"
123048
+ };
123049
+ const RESERVED_LINES$1 = 11;
123050
+ const LINES_PER_ITEM$1 = 2;
123051
+ function statusColor$1(status) {
123052
+ if (status === "completed") return colors.success;
123053
+ if (status === "proceeding" || status === "init") return colors.accent;
123054
+ return colors.destructive;
123055
+ }
123056
+ function getUsageIcon(ratio) {
123057
+ const percent = ratio * 100;
123058
+ if (percent >= 75) return USAGE_INDICATORS.FULL;
123059
+ if (percent >= 50) return USAGE_INDICATORS.HIGH;
123060
+ if (percent >= 25) return USAGE_INDICATORS.MEDIUM;
123061
+ if (percent >= 5) return USAGE_INDICATORS.LOW;
123062
+ return USAGE_INDICATORS.EMPTY;
123063
+ }
123064
+ function eventTypeColor(type) {
123065
+ switch (type) {
123066
+ case "startRun": return colors.accent;
123067
+ case "completeRun": return colors.success;
123068
+ case "stopRunByError": return colors.destructive;
123069
+ case "stopRunByDelegate":
123070
+ case "stopRunByInteractiveTool": return colors.warn;
123071
+ case "callTools": return "magenta";
123072
+ case "retry": return colors.destructive;
123073
+ default: return colors.muted;
123074
+ }
123075
+ }
123076
+ function formatTime(timestamp) {
123077
+ const d = new Date(timestamp);
123078
+ return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}:${String(d.getSeconds()).padStart(2, "0")}`;
123079
+ }
123080
+ function getEventSummary(event) {
123081
+ switch (event.type) {
123082
+ case "callTools": return `Tools: ${event.toolCalls.map((tc) => `${tc.skillName}/${tc.toolName}`).join(", ")}`;
123083
+ case "completeRun": return `Result: ${event.text}`;
123084
+ case "stopRunByError": return `${event.error.name}: ${event.error.message}`;
123085
+ case "retry": return `Reason: ${event.reason}`;
123086
+ case "startRun": return `Model: ${event.model}`;
123087
+ case "stopRunByDelegate": return event.checkpoint.delegateTo ? `Delegates: ${event.checkpoint.delegateTo.map((d) => d.expert.key).join(", ")}` : void 0;
123088
+ default: return;
123089
+ }
123090
+ }
123091
+ const RunDetailScreen = ({ run, checkpoints, events, initialTab, onSelectCheckpoint, onSelectEvent, onBack, onTabChange, onSelectedCheckpointChange, onSelectedEventChange }) => {
123092
+ const [activeTab, setActiveTab] = (0, import_react.useState)(initialTab);
123093
+ const maxVisible = computeVisibleItems(useTerminalHeight(), RESERVED_LINES$1, LINES_PER_ITEM$1);
123094
+ const activities = (0, import_react.useMemo)(() => buildActivities(events), [events]);
123095
+ const checkpointNav = useListNavigation({
123096
+ items: checkpoints,
123097
+ onSelect: onSelectCheckpoint,
123098
+ onBack
123099
+ });
123100
+ const eventNav = useListNavigation({
123101
+ items: events,
123102
+ onSelect: onSelectEvent,
123103
+ onBack
123104
+ });
123105
+ const activityNav = useListNavigation({
123106
+ items: activities,
123107
+ onSelect: (activity) => onSelectEvent(activity.event),
123108
+ onBack
123109
+ });
123110
+ const selectedCheckpoint = checkpoints[checkpointNav.selectedIndex];
123111
+ const selectedEvent = events[eventNav.selectedIndex];
123112
+ const selectedActivity = activities[activityNav.selectedIndex];
123113
+ (0, import_react.useEffect)(() => {
123114
+ if (activeTab === "checkpoints") {
123115
+ onSelectedCheckpointChange(selectedCheckpoint);
123116
+ onSelectedEventChange(void 0);
123117
+ } else if (activeTab === "events") {
123118
+ onSelectedEventChange(selectedEvent);
123119
+ onSelectedCheckpointChange(void 0);
123120
+ } else {
123121
+ onSelectedEventChange(selectedActivity?.event);
123122
+ onSelectedCheckpointChange(void 0);
123123
+ }
123124
+ }, [
123125
+ activeTab,
123126
+ selectedCheckpoint,
123127
+ selectedEvent,
123128
+ selectedActivity,
123129
+ onSelectedCheckpointChange,
123130
+ onSelectedEventChange
123131
+ ]);
123132
+ const switchTab = (0, import_react.useCallback)((tab) => {
123133
+ setActiveTab(tab);
123134
+ onTabChange(tab);
123135
+ }, [onTabChange]);
123136
+ useInput((char, key) => {
123137
+ if (char === "1") {
123138
+ switchTab("activities");
123139
+ return;
123140
+ }
123141
+ if (char === "2") {
123142
+ switchTab("checkpoints");
123143
+ return;
123144
+ }
123145
+ if (char === "3") {
123146
+ switchTab("events");
123147
+ return;
123148
+ }
123149
+ if (key.tab) {
123150
+ const currentIndex = TAB_ORDER.indexOf(activeTab);
123151
+ switchTab(TAB_ORDER[key.shift ? (currentIndex - 1 + TAB_ORDER.length) % TAB_ORDER.length : (currentIndex + 1) % TAB_ORDER.length]);
123152
+ return;
123153
+ }
123154
+ if (activeTab === "checkpoints") checkpointNav.handleNavigation(char, key);
123155
+ else if (activeTab === "events") eventNav.handleNavigation(char, key);
123156
+ else activityNav.handleNavigation(char, key);
123157
+ });
123158
+ const shortRunId = run.runId.length > 12 ? `${run.runId.slice(0, 12)}...` : run.runId;
123159
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
123160
+ flexDirection: "column",
123161
+ flexGrow: 1,
123162
+ children: [
123163
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [
123164
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123165
+ color: colors.accent,
123166
+ bold: true,
123167
+ children: "Run"
123168
+ }),
123169
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
123170
+ color: colors.muted,
123171
+ children: [
123172
+ " (",
123173
+ shortRunId,
123174
+ ")"
123175
+ ]
123176
+ }),
123177
+ run.expertKey ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
123178
+ color: colors.muted,
123179
+ children: [" ", run.expertKey]
123180
+ }) : null
123181
+ ] }),
123182
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [TAB_ORDER.map((tab, i) => {
123183
+ const isActive = tab === activeTab;
123184
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123185
+ color: colors.muted,
123186
+ children: " | "
123187
+ }) : null, /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
123188
+ color: isActive ? colors.accent : colors.muted,
123189
+ bold: isActive,
123190
+ children: [
123191
+ i + 1,
123192
+ ":",
123193
+ TAB_LABELS[tab]
123194
+ ]
123195
+ })] }, tab);
123196
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123197
+ color: colors.muted,
123198
+ children: " Tab:Switch Enter:Select b:Back q:Quit"
123199
+ })] }),
123200
+ activeTab === "activities" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ActivitiesContent, {
123201
+ activities,
123202
+ selectedIndex: activityNav.selectedIndex,
123203
+ maxVisible
123204
+ }),
123205
+ activeTab === "checkpoints" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CheckpointsContent, {
123206
+ checkpoints,
123207
+ selectedIndex: checkpointNav.selectedIndex,
123208
+ maxVisible
123209
+ }),
123210
+ activeTab === "events" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventsContent, {
123211
+ events,
123212
+ selectedIndex: eventNav.selectedIndex,
123213
+ maxVisible
123214
+ })
123215
+ ]
123216
+ });
123217
+ };
123218
+ function ActivitiesContent({ activities, selectedIndex, maxVisible }) {
123219
+ const { scrollOffset, displayItems } = (0, import_react.useMemo)(() => {
123220
+ const offset = Math.max(0, Math.min(selectedIndex - maxVisible + 1, activities.length - maxVisible));
123221
+ return {
123222
+ scrollOffset: offset,
123223
+ displayItems: activities.slice(offset, offset + maxVisible)
123224
+ };
123225
+ }, [
123226
+ activities,
123227
+ selectedIndex,
123228
+ maxVisible
123229
+ ]);
123230
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
123231
+ flexDirection: "column",
123232
+ flexGrow: 1,
123233
+ children: [
123234
+ activities.length > maxVisible && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
123235
+ color: colors.muted,
123236
+ children: [
123237
+ "[",
123238
+ selectedIndex + 1,
123239
+ "/",
123240
+ activities.length,
123241
+ "]"
123242
+ ]
123243
+ }),
123244
+ scrollOffset > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123245
+ color: colors.muted,
123246
+ children: "..."
123247
+ }),
123248
+ displayItems.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123249
+ color: colors.muted,
123250
+ children: "No activities found"
123251
+ }) : displayItems.map((activity, i) => {
123252
+ const isSelected = scrollOffset + i === selectedIndex;
123253
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
123254
+ flexDirection: "column",
123255
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123256
+ color: isSelected ? colors.accent : colors.muted,
123257
+ children: isSelected ? " ▸ " : " "
123258
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
123259
+ wrap: "truncate",
123260
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123261
+ color: activity.color,
123262
+ bold: isSelected,
123263
+ children: activity.label
123264
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
123265
+ dimColor: true,
123266
+ children: [" ", formatTime(activity.timestamp)]
123267
+ })]
123268
+ })] }), activity.detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
123269
+ paddingLeft: 3,
123270
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123271
+ dimColor: true,
123272
+ wrap: "truncate",
123273
+ children: activity.detail
123274
+ })
123275
+ }) : null]
123276
+ }, activity.event.id);
123277
+ }),
123278
+ scrollOffset + maxVisible < activities.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123279
+ color: colors.muted,
123280
+ children: "..."
123281
+ })
123282
+ ]
123283
+ });
123284
+ }
123285
+ function CheckpointsContent({ checkpoints, selectedIndex, maxVisible }) {
123286
+ const { scrollOffset, displayItems } = (0, import_react.useMemo)(() => {
123287
+ const offset = Math.max(0, Math.min(selectedIndex - maxVisible + 1, checkpoints.length - maxVisible));
123288
+ return {
123289
+ scrollOffset: offset,
123290
+ displayItems: checkpoints.slice(offset, offset + maxVisible)
123291
+ };
123292
+ }, [
123293
+ checkpoints,
123294
+ selectedIndex,
123295
+ maxVisible
123296
+ ]);
123297
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
123298
+ flexDirection: "column",
123299
+ flexGrow: 1,
123300
+ children: [
123301
+ checkpoints.length > maxVisible && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
123302
+ color: colors.muted,
123303
+ children: [
123304
+ "[",
123305
+ selectedIndex + 1,
123306
+ "/",
123307
+ checkpoints.length,
123308
+ "]"
123309
+ ]
123310
+ }),
123311
+ scrollOffset > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123312
+ color: colors.muted,
123313
+ children: "..."
123314
+ }),
123315
+ displayItems.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123316
+ color: colors.muted,
123317
+ children: "No checkpoints found"
123318
+ }) : displayItems.map((cp, i) => {
123319
+ const isSelected = scrollOffset + i === selectedIndex;
123320
+ const hasUsage = cp.contextWindowUsage !== void 0;
123321
+ const hasDelegates = cp.delegateTo && cp.delegateTo.length > 0;
123322
+ const hasError = !!cp.error;
123323
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
123324
+ flexDirection: "column",
123325
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123326
+ color: isSelected ? colors.accent : colors.muted,
123327
+ children: isSelected ? " ▸ " : " "
123328
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
123329
+ wrap: "truncate",
123330
+ children: [
123331
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
123332
+ bold: true,
123333
+ children: ["Step ", cp.stepNumber]
123334
+ }),
123335
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123336
+ dimColor: true,
123337
+ children: " · "
123338
+ }),
123339
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123340
+ color: statusColor$1(cp.status),
123341
+ children: cp.status
123342
+ }),
123343
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123344
+ dimColor: true,
123345
+ children: " · "
123346
+ }),
123347
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cp.expert.key }),
123348
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123349
+ dimColor: true,
123350
+ children: " · "
123351
+ }),
123352
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [cp.messages.length, " msgs"] }),
123353
+ hasUsage ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123354
+ dimColor: true,
123355
+ children: " · "
123356
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
123357
+ getUsageIcon(cp.contextWindowUsage),
123358
+ " ",
123359
+ (cp.contextWindowUsage * 100).toFixed(0),
123360
+ "%"
123361
+ ] })] }) : null
123362
+ ]
123363
+ })] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
123364
+ paddingLeft: 3,
123365
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
123366
+ dimColor: true,
123367
+ wrap: "truncate",
123368
+ children: [
123369
+ "Tokens: In ",
123370
+ formatTokenCount(cp.usage.inputTokens),
123371
+ " · Out",
123372
+ " ",
123373
+ formatTokenCount(cp.usage.outputTokens),
123374
+ hasDelegates ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [" · Delegates: ", cp.delegateTo.map((d) => d.expert.key).join(", ")] }) : null,
123375
+ hasError ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
123376
+ color: colors.destructive,
123377
+ children: [" · ", cp.error.name]
123378
+ }) : null
123379
+ ]
123380
+ })
123381
+ })]
123382
+ }, cp.id);
123383
+ }),
123384
+ scrollOffset + maxVisible < checkpoints.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123385
+ color: colors.muted,
123386
+ children: "..."
123387
+ })
123388
+ ]
123389
+ });
123390
+ }
123391
+ function EventsContent({ events, selectedIndex, maxVisible }) {
123392
+ const { scrollOffset, displayItems } = (0, import_react.useMemo)(() => {
123393
+ const offset = Math.max(0, Math.min(selectedIndex - maxVisible + 1, events.length - maxVisible));
123394
+ return {
123395
+ scrollOffset: offset,
123396
+ displayItems: events.slice(offset, offset + maxVisible)
123397
+ };
123398
+ }, [
123399
+ events,
123400
+ selectedIndex,
123401
+ maxVisible
123402
+ ]);
123403
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
123404
+ flexDirection: "column",
123405
+ flexGrow: 1,
123406
+ children: [
123407
+ events.length > maxVisible && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
123408
+ color: colors.muted,
123409
+ children: [
123410
+ "[",
123411
+ selectedIndex + 1,
123412
+ "/",
123413
+ events.length,
123414
+ "]"
123415
+ ]
123416
+ }),
123417
+ scrollOffset > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123418
+ color: colors.muted,
123419
+ children: "..."
123420
+ }),
123421
+ displayItems.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123422
+ color: colors.muted,
123423
+ children: "No events found"
123424
+ }) : displayItems.map((ev, i) => {
123425
+ const isSelected = scrollOffset + i === selectedIndex;
123426
+ const summary = getEventSummary(ev);
123427
+ const hasUsage = "usage" in ev && ev.usage;
123428
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
123429
+ flexDirection: "column",
123430
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123431
+ color: isSelected ? colors.accent : colors.muted,
123432
+ children: isSelected ? " ▸ " : " "
123433
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
123434
+ wrap: "truncate",
123435
+ children: [
123436
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
123437
+ bold: true,
123438
+ children: ["Step ", ev.stepNumber]
123439
+ }),
123440
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123441
+ dimColor: true,
123442
+ children: " · "
123443
+ }),
123444
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123445
+ color: eventTypeColor(ev.type),
123446
+ children: ev.type
123447
+ }),
123448
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123449
+ dimColor: true,
123450
+ children: " · "
123451
+ }),
123452
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: ev.expertKey }),
123453
+ hasUsage ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123454
+ dimColor: true,
123455
+ children: " · "
123456
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
123457
+ dimColor: true,
123458
+ children: [
123459
+ "In",
123460
+ " ",
123461
+ formatTokenCount(ev.usage.inputTokens),
123462
+ " ",
123463
+ "· Out",
123464
+ " ",
123465
+ formatTokenCount(ev.usage.outputTokens)
123466
+ ]
123467
+ })] }) : null,
123468
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123469
+ dimColor: true,
123470
+ children: " "
123471
+ }),
123472
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123473
+ dimColor: true,
123474
+ children: formatTime(ev.timestamp)
123475
+ })
123476
+ ]
123477
+ })] }), summary ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
123478
+ paddingLeft: 3,
123479
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123480
+ dimColor: true,
123481
+ wrap: "truncate",
123482
+ children: summary
123483
+ })
123484
+ }) : null]
123485
+ }, ev.id);
123486
+ }),
123487
+ scrollOffset + maxVisible < events.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123488
+ color: colors.muted,
123489
+ children: "..."
123490
+ })
123491
+ ]
123492
+ });
123493
+ }
123494
+ //#endregion
123495
+ //#region ../../packages/tui-components/src/log-viewer/build-graph-lines.ts
123496
+ /**
123497
+ * Build git-graph style lines from a DelegationTreeState.
123498
+ *
123499
+ * Uses delegation groups to distinguish sequential vs parallel delegations.
123500
+ * Uses resumeNodes to show resume runs as separate nodes after merge points.
123501
+ * Each column/lane gets a stable `colorIndex` so the renderer can color
123502
+ * parent and child lanes differently.
123503
+ */
123504
+ function buildGraphLines(treeState, runQueries, delegationGroups, resumeNodes) {
123505
+ if (!treeState.rootRunId) return [];
123506
+ const root = treeState.nodes.get(treeState.rootRunId);
123507
+ if (!root) return [];
123508
+ const resumeSet = resumeNodes ? new Set(resumeNodes.keys()) : /* @__PURE__ */ new Set();
123509
+ const lines = [];
123510
+ let nextCol = 0;
123511
+ const freePool = [];
123512
+ function allocCol() {
123513
+ if (freePool.length > 0) {
123514
+ freePool.sort((a, b) => a - b);
123515
+ return freePool.shift();
123516
+ }
123517
+ return nextCol++;
123518
+ }
123519
+ function freeCol(col) {
123520
+ freePool.push(col);
123521
+ }
123522
+ const activeCols = /* @__PURE__ */ new Set();
123523
+ const colColors = /* @__PURE__ */ new Map();
123524
+ let nextColorIndex = 0;
123525
+ function assignColor(col) {
123526
+ const ci = nextColorIndex++;
123527
+ colColors.set(col, ci);
123528
+ return ci;
123529
+ }
123530
+ function getColor(col) {
123531
+ return colColors.get(col) ?? -1;
123532
+ }
123533
+ /** Get the max column index we need to render. */
123534
+ function maxCol(extraCols) {
123535
+ let max = -1;
123536
+ for (const c of activeCols) if (c > max) max = c;
123537
+ if (extraCols) {
123538
+ for (const c of extraCols) if (c > max) max = c;
123539
+ }
123540
+ return max;
123541
+ }
123542
+ /** Render a line given active columns, with optional special markers. */
123543
+ function render(markers) {
123544
+ const mc = maxCol(markers?.keys());
123545
+ if (mc < 0) return {
123546
+ graph: "",
123547
+ segments: []
123548
+ };
123549
+ let graph = "";
123550
+ const segments = [];
123551
+ for (let c = 0; c <= mc; c++) {
123552
+ const marker = markers?.get(c);
123553
+ if (marker) {
123554
+ const text = `${marker} `;
123555
+ graph += text;
123556
+ segments.push({
123557
+ text,
123558
+ colorIndex: getColor(c)
123559
+ });
123560
+ } else if (activeCols.has(c)) {
123561
+ const text = "| ";
123562
+ graph += text;
123563
+ segments.push({
123564
+ text,
123565
+ colorIndex: getColor(c)
123566
+ });
123567
+ } else {
123568
+ const text = " ";
123569
+ graph += text;
123570
+ segments.push({
123571
+ text,
123572
+ colorIndex: -1
123573
+ });
123574
+ }
123575
+ }
123576
+ return {
123577
+ graph,
123578
+ segments
123579
+ };
123580
+ }
123581
+ /**
123582
+ * Split a node's children into:
123583
+ * - delegateGroups: groups of children delegated together (from delegationGroups)
123584
+ * - resumeChildren: children that are resume runs (process in order after delegate groups)
123585
+ * - otherChildren: children that are neither delegated nor resume (ungrouped)
123586
+ */
123587
+ const resumeChildrenOf = /* @__PURE__ */ new Map();
123588
+ if (resumeNodes) for (const [resumeId, originalId] of resumeNodes) {
123589
+ const list = resumeChildrenOf.get(originalId) ?? [];
123590
+ list.push(resumeId);
123591
+ resumeChildrenOf.set(originalId, list);
123592
+ }
123593
+ function splitChildren(node) {
123594
+ const allGrouped = /* @__PURE__ */ new Set();
123595
+ const delegateGroups = [];
123596
+ if (delegationGroups) {
123597
+ const groups = delegationGroups.get(node.runId);
123598
+ if (groups) for (const group of groups) {
123599
+ delegateGroups.push(group);
123600
+ for (const id of group) allGrouped.add(id);
123601
+ }
123602
+ }
123603
+ const resumeChildren = resumeChildrenOf.get(node.runId) ?? [];
123604
+ const allResumeIds = new Set(resumeSet);
123605
+ const otherChildren = [];
123606
+ for (const childId of node.childRunIds) {
123607
+ if (allGrouped.has(childId)) continue;
123608
+ if (allResumeIds.has(childId)) continue;
123609
+ otherChildren.push(childId);
123610
+ }
123611
+ if (delegateGroups.length === 0 && otherChildren.length > 0) {
123612
+ delegateGroups.push(otherChildren);
123613
+ return {
123614
+ delegateGroups,
123615
+ resumeChildren,
123616
+ otherChildren: []
123617
+ };
123618
+ }
123619
+ return {
123620
+ delegateGroups,
123621
+ resumeChildren,
123622
+ otherChildren
123623
+ };
123624
+ }
123625
+ /**
123626
+ * Emit lines for a node on a shared lane without freeing the column afterward.
123627
+ * Used when multiple children share a single lane (commit-chain style).
123628
+ */
123629
+ function emitSubtreeInline(node, col) {
123630
+ activeCols.add(col);
123631
+ const isResume = resumeSet.has(node.runId);
123632
+ const { graph, segments } = render(new Map([[col, "*"]]));
123633
+ lines.push({
123634
+ kind: "node",
123635
+ node,
123636
+ graph,
123637
+ graphSegments: segments,
123638
+ query: runQueries.get(node.runId),
123639
+ isResume
123640
+ });
123641
+ const { delegateGroups, resumeChildren, otherChildren } = splitChildren(node);
123642
+ if (!(delegateGroups.some((g) => g.length > 0) || resumeChildren.length > 0 || otherChildren.length > 0)) return;
123643
+ emitChildGroups(node, col, delegateGroups, resumeChildren, otherChildren);
123644
+ }
123645
+ /**
123646
+ * Shared logic for processing delegate groups, resume children, and other children.
123647
+ */
123648
+ function emitChildGroups(_node, col, delegateGroups, resumeChildren, otherChildren) {
123649
+ for (const group of delegateGroups) {
123650
+ const children = group.map((id) => treeState.nodes.get(id)).filter((n) => n !== void 0);
123651
+ if (children.length === 0) continue;
123652
+ const cc = allocCol();
123653
+ assignColor(cc);
123654
+ activeCols.add(cc);
123655
+ const forkMarkers = /* @__PURE__ */ new Map();
123656
+ forkMarkers.set(cc, "\\");
123657
+ const fork = render(forkMarkers);
123658
+ lines.push({
123659
+ kind: "connector",
123660
+ graph: fork.graph,
123661
+ graphSegments: fork.segments
123662
+ });
123663
+ for (const child of children) emitSubtreeInline(child, cc);
123664
+ activeCols.add(cc);
123665
+ const mergeMarkers = /* @__PURE__ */ new Map();
123666
+ mergeMarkers.set(cc, "/");
123667
+ const merge = render(mergeMarkers);
123668
+ lines.push({
123669
+ kind: "connector",
123670
+ graph: merge.graph,
123671
+ graphSegments: merge.segments
123672
+ });
123673
+ activeCols.delete(cc);
123674
+ if (!freePool.includes(cc)) freeCol(cc);
123675
+ }
123676
+ for (const resumeId of resumeChildren) {
123677
+ const resumeNode = treeState.nodes.get(resumeId);
123678
+ if (!resumeNode) continue;
123679
+ emitSubtreeInline(resumeNode, col);
123680
+ }
123681
+ if (otherChildren.length > 0) {
123682
+ const children = otherChildren.map((id) => treeState.nodes.get(id)).filter((n) => n !== void 0);
123683
+ const cc = allocCol();
123684
+ assignColor(cc);
123685
+ activeCols.add(cc);
123686
+ const forkMarkers = /* @__PURE__ */ new Map();
123687
+ forkMarkers.set(cc, "\\");
123688
+ const fork = render(forkMarkers);
123689
+ lines.push({
123690
+ kind: "connector",
123691
+ graph: fork.graph,
123692
+ graphSegments: fork.segments
123693
+ });
123694
+ for (const child of children) emitSubtreeInline(child, cc);
123695
+ activeCols.add(cc);
123696
+ const mergeMarkers = /* @__PURE__ */ new Map();
123697
+ mergeMarkers.set(cc, "/");
123698
+ const merge = render(mergeMarkers);
123699
+ lines.push({
123700
+ kind: "connector",
123701
+ graph: merge.graph,
123702
+ graphSegments: merge.segments
123703
+ });
123704
+ activeCols.delete(cc);
123705
+ if (!freePool.includes(cc)) freeCol(cc);
123706
+ }
123707
+ }
123708
+ /**
123709
+ * Recursively emit lines for a subtree (top-level entry point that frees column on exit).
123710
+ */
123711
+ function emitSubtree(node, col) {
123712
+ emitSubtreeInline(node, col);
123713
+ activeCols.delete(col);
123714
+ if (!freePool.includes(col)) freeCol(col);
123715
+ }
123716
+ const rootCol = allocCol();
123717
+ assignColor(rootCol);
123718
+ emitSubtree(root, rootCol);
123719
+ for (const [nodeId, node] of treeState.nodes) if (!lines.some((l) => l.kind === "node" && l.node.runId === nodeId)) lines.push({
123720
+ kind: "node",
123721
+ node,
123722
+ graph: "* ",
123723
+ graphSegments: [{
123724
+ text: "* ",
123725
+ colorIndex: -1
123726
+ }],
123727
+ query: runQueries.get(nodeId),
123728
+ isResume: resumeSet.has(nodeId)
123729
+ });
123730
+ return lines;
123731
+ }
123732
+ //#endregion
123733
+ //#region ../../packages/tui-components/src/log-viewer/screens/run-list.tsx
123734
+ const RESERVED_LINES = 9;
123735
+ const LINES_PER_ITEM = 1;
123736
+ /**
123737
+ * Color palette for graph lanes. Each lane gets a color by its colorIndex mod palette length.
123738
+ * These are chosen to be distinct and readable on both dark and light terminals.
123739
+ */
123740
+ const LANE_COLORS = [
123741
+ "cyan",
123742
+ "magenta",
123743
+ "yellow",
123744
+ "blue",
123745
+ "green",
123746
+ "red",
123747
+ "white"
123748
+ ];
123749
+ function laneColor(colorIndex) {
123750
+ if (colorIndex < 0) return colors.muted;
123751
+ return LANE_COLORS[colorIndex % LANE_COLORS.length];
123752
+ }
123753
+ function statusIcon(status) {
123754
+ switch (status) {
123755
+ case "completed": return "✓";
123756
+ case "error": return "✗";
123757
+ case "running": return "○";
123758
+ case "suspending": return "⎇";
123759
+ default: return " ";
123760
+ }
123761
+ }
123762
+ function statusColor(status) {
123763
+ switch (status) {
123764
+ case "completed": return colors.primary;
123765
+ case "error": return colors.destructive;
123766
+ case "suspending": return colors.primary;
123767
+ default: return colors.muted;
123768
+ }
123769
+ }
123770
+ /** Render graph segments as colored <Text> elements. */
123771
+ function GraphPrefix({ segments, nodeStatus }) {
123772
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: segments.map((seg, i) => {
123773
+ if (seg.text.startsWith("*") && nodeStatus) {
123774
+ const icon = statusIcon(nodeStatus);
123775
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
123776
+ color: statusColor(nodeStatus),
123777
+ children: [icon, seg.text.slice(1)]
123778
+ }, i);
123779
+ }
123780
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123781
+ color: laneColor(seg.colorIndex),
123782
+ children: seg.text
123783
+ }, i);
123784
+ }) });
123785
+ }
123786
+ const RunListScreen = ({ job, treeState, runQueries, delegationGroups, resumeNodes, onSelectRun, onBack, onSelectedChange }) => {
123787
+ const maxVisible = computeVisibleItems(useTerminalHeight(), RESERVED_LINES, LINES_PER_ITEM);
123788
+ const graphLines = (0, import_react.useMemo)(() => buildGraphLines(treeState, runQueries, delegationGroups, resumeNodes), [
123789
+ treeState,
123790
+ runQueries,
123791
+ delegationGroups,
123792
+ resumeNodes
123793
+ ]);
123794
+ const selectableIndices = (0, import_react.useMemo)(() => graphLines.map((line, idx) => line.kind === "node" ? idx : -1).filter((idx) => idx >= 0), [graphLines]);
123795
+ const [selectedPos, setSelectedPos] = (0, import_react.useState)(0);
123796
+ const selectedLineIndex = selectableIndices[selectedPos];
123797
+ const selectedLine = selectedLineIndex !== void 0 ? graphLines[selectedLineIndex] : void 0;
123798
+ const selectedNode = selectedLine?.kind === "node" ? selectedLine.node : void 0;
123799
+ (0, import_react.useEffect)(() => {
123800
+ onSelectedChange(selectedNode);
123801
+ }, [selectedNode, onSelectedChange]);
123802
+ const handleSelect = (0, import_react.useCallback)(() => {
123803
+ if (selectedNode) onSelectRun(selectedNode);
123804
+ }, [selectedNode, onSelectRun]);
123805
+ useInput((char, key) => {
123806
+ if (key.upArrow && selectableIndices.length > 0) {
123807
+ setSelectedPos((prev) => Math.max(0, prev - 1));
123808
+ return;
123809
+ }
123810
+ if (key.downArrow && selectableIndices.length > 0) {
123811
+ setSelectedPos((prev) => Math.min(selectableIndices.length - 1, prev + 1));
123812
+ return;
123813
+ }
123814
+ if (key.return) {
123815
+ handleSelect();
123816
+ return;
123817
+ }
123818
+ if (key.escape || char === "b") onBack();
123819
+ });
123820
+ const { scrollOffset, displayLines } = (0, import_react.useMemo)(() => {
123821
+ if (graphLines.length <= maxVisible) return {
123822
+ scrollOffset: 0,
123823
+ displayLines: graphLines
123824
+ };
123825
+ const rawOffset = (selectedLineIndex ?? 0) - Math.floor(maxVisible / 2);
123826
+ const offset = Math.max(0, Math.min(rawOffset, graphLines.length - maxVisible));
123827
+ return {
123828
+ scrollOffset: offset,
123829
+ displayLines: graphLines.slice(offset, offset + maxVisible)
123830
+ };
123831
+ }, [
123832
+ graphLines,
123833
+ selectedLineIndex,
123834
+ maxVisible
123835
+ ]);
123836
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
123837
+ flexDirection: "column",
123838
+ flexGrow: 1,
123839
+ children: [
123840
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [
123841
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123842
+ color: colors.accent,
123843
+ bold: true,
123844
+ children: "Runs"
123845
+ }),
123846
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
123847
+ color: colors.muted,
123848
+ children: [
123849
+ " (",
123850
+ job.coordinatorExpertKey,
123851
+ ")"
123852
+ ]
123853
+ }),
123854
+ selectableIndices.length > maxVisible && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
123855
+ color: colors.muted,
123856
+ children: [
123857
+ " ",
123858
+ "[",
123859
+ selectedPos + 1,
123860
+ "/",
123861
+ selectableIndices.length,
123862
+ "]"
123863
+ ]
123864
+ }),
123865
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123866
+ color: colors.muted,
123867
+ children: " Enter:Select b:Back q:Quit"
123868
+ })
123869
+ ] }),
123870
+ scrollOffset > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123871
+ color: colors.muted,
123872
+ children: "..."
123873
+ }),
123874
+ displayLines.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123875
+ color: colors.muted,
123876
+ children: "No runs found"
123877
+ }) : displayLines.map((line, i) => {
123878
+ const actualIndex = scrollOffset + i;
123879
+ if (line.kind === "connector") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
123880
+ wrap: "truncate",
123881
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: " " }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GraphPrefix, { segments: line.graphSegments })]
123882
+ }, `c-${actualIndex}`);
123883
+ const isSelected = actualIndex === selectedLineIndex;
123884
+ const nameColor = statusColor(line.node.status);
123885
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
123886
+ wrap: "truncate",
123887
+ children: [
123888
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123889
+ color: isSelected ? colors.accent : colors.muted,
123890
+ children: isSelected ? " > " : " "
123891
+ }),
123892
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GraphPrefix, {
123893
+ segments: line.graphSegments,
123894
+ nodeStatus: line.node.status
123895
+ }),
123896
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123897
+ bold: true,
123898
+ color: nameColor,
123899
+ children: line.node.expertName
123900
+ }),
123901
+ line.isResume ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123902
+ color: colors.muted,
123903
+ children: " (resumed)"
123904
+ }) : null,
123905
+ line.query ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
123906
+ dimColor: true,
123907
+ children: [" ", line.query]
123908
+ }) : null
123909
+ ]
123910
+ }, line.node.runId);
123911
+ }),
123912
+ scrollOffset + maxVisible < graphLines.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
123913
+ color: colors.muted,
123914
+ children: "..."
123915
+ })
123916
+ ]
123917
+ });
123918
+ };
123919
+ //#endregion
123920
+ //#region ../../packages/tui-components/src/log-viewer/app.tsx
123921
+ async function extractJobQuery(fetcher, job) {
123922
+ try {
123923
+ const runs = await fetcher.getRuns(job.id);
123924
+ if (runs.length === 0) return void 0;
123925
+ const firstRun = runs[0];
123926
+ const startRunEvent = await fetcher.getFirstEventForRun(job.id, firstRun.runId, "startRun");
123927
+ if (!startRunEvent) return void 0;
123928
+ return extractQueryFromStartRun(startRunEvent);
123929
+ } catch {
123930
+ return;
123931
+ }
123932
+ }
123933
+ async function buildRunTree(fetcher, jobId) {
123934
+ return buildRunTreeFromEvents(await fetcher.getTreeEventsForJob(jobId));
123935
+ }
123936
+ const LogViewerApp = ({ fetcher, initialJobId, initialRunId }) => {
123937
+ const { exit } = useApp();
123938
+ const [screen, setScreen] = (0, import_react.useState)({ type: "jobList" });
123939
+ const [jobItems, setJobItems] = (0, import_react.useState)([]);
123940
+ const [loading, setLoading] = (0, import_react.useState)(true);
123941
+ const [error, setError] = (0, import_react.useState)();
123942
+ const [selectedJob, setSelectedJob] = (0, import_react.useState)();
123943
+ const [selectedRun, setSelectedRun] = (0, import_react.useState)();
123944
+ const [selectedTreeNode, setSelectedTreeNode] = (0, import_react.useState)();
123945
+ const [selectedCheckpoint, setSelectedCheckpoint] = (0, import_react.useState)();
123946
+ const [selectedEvent, setSelectedEvent] = (0, import_react.useState)();
123947
+ useInput((char) => {
123948
+ if (char === "q") exit();
123949
+ });
123950
+ (0, import_react.useEffect)(() => {
123951
+ const loadInitial = async () => {
123952
+ try {
123953
+ setLoading(true);
123954
+ if (initialJobId && initialRunId) {
123955
+ const job = await fetcher.getJob(initialJobId);
123956
+ if (!job) {
123957
+ setError(`Job ${initialJobId} not found`);
123958
+ setLoading(false);
123959
+ return;
123960
+ }
123961
+ const [runCheckpoints, runEvents] = await Promise.all([fetcher.getCheckpointsForRun(initialJobId, initialRunId), fetcher.getEvents(initialJobId, initialRunId)]);
123962
+ setScreen({
123963
+ type: "runDetail",
123964
+ job,
123965
+ run: {
123966
+ jobId: initialJobId,
123967
+ runId: initialRunId
123968
+ },
123969
+ checkpoints: runCheckpoints,
123970
+ events: runEvents,
123971
+ tab: "activities"
123972
+ });
123973
+ setLoading(false);
123974
+ return;
123975
+ }
123976
+ if (initialJobId) {
123977
+ const job = await fetcher.getJob(initialJobId);
123978
+ if (!job) {
123979
+ setError(`Job ${initialJobId} not found`);
123980
+ setLoading(false);
123981
+ return;
123982
+ }
123983
+ const { treeState, runQueries, runStats, delegationGroups, resumeNodes } = await buildRunTree(fetcher, initialJobId);
123984
+ setScreen({
123985
+ type: "runList",
123986
+ job,
123987
+ treeState,
123988
+ runQueries,
123989
+ runStats,
123990
+ delegationGroups,
123991
+ resumeNodes
123992
+ });
123993
+ setLoading(false);
123994
+ return;
123995
+ }
123996
+ const latest = await fetcher.getLatestJob();
123997
+ if (latest) setJobItems([{
123998
+ job: latest,
123999
+ query: await extractJobQuery(fetcher, latest)
124000
+ }]);
124001
+ else setJobItems([]);
124002
+ setLoading(false);
124003
+ } catch (err) {
124004
+ setError(err instanceof Error ? err.message : String(err));
124005
+ setLoading(false);
124006
+ }
124007
+ };
124008
+ loadInitial();
124009
+ }, [
124010
+ fetcher,
124011
+ initialJobId,
124012
+ initialRunId
124013
+ ]);
124014
+ const handleChatSubmit = (0, import_react.useCallback)((_query) => {}, []);
124015
+ const navigateToJobDetail = (0, import_react.useCallback)((job) => {
124016
+ setScreen({
124017
+ type: "jobDetail",
124018
+ job
124019
+ });
124020
+ }, []);
124021
+ const navigateToRunList = (0, import_react.useCallback)(async (job) => {
124022
+ setLoading(true);
124023
+ try {
124024
+ const { treeState, runQueries, runStats, delegationGroups, resumeNodes } = await buildRunTree(fetcher, job.id);
124025
+ setScreen({
124026
+ type: "runList",
124027
+ job,
124028
+ treeState,
124029
+ runQueries,
124030
+ runStats,
124031
+ delegationGroups,
124032
+ resumeNodes
124033
+ });
124034
+ } catch (err) {
124035
+ setError(err instanceof Error ? err.message : String(err));
124036
+ }
124037
+ setLoading(false);
124038
+ }, [fetcher]);
124039
+ const navigateToRunDetail = (0, import_react.useCallback)(async (job, run, tab = "activities") => {
124040
+ setLoading(true);
124041
+ try {
124042
+ const [runCheckpoints, runEvents] = await Promise.all([fetcher.getCheckpointsForRun(job.id, run.runId), fetcher.getEvents(job.id, run.runId)]);
124043
+ setScreen({
124044
+ type: "runDetail",
124045
+ job,
124046
+ run,
124047
+ checkpoints: runCheckpoints,
124048
+ events: runEvents,
124049
+ tab
124050
+ });
124051
+ } catch (err) {
124052
+ setError(err instanceof Error ? err.message : String(err));
124053
+ }
124054
+ setLoading(false);
124055
+ }, [fetcher]);
124056
+ const navigateToCheckpointDetail = (0, import_react.useCallback)((job, run, checkpoint) => {
124057
+ setScreen({
124058
+ type: "checkpointDetail",
124059
+ job,
124060
+ run,
124061
+ checkpoint
124062
+ });
124063
+ }, []);
124064
+ const navigateToEventDetail = (0, import_react.useCallback)((job, run, event) => {
124065
+ setScreen({
124066
+ type: "eventDetail",
124067
+ job,
124068
+ run,
124069
+ event
124070
+ });
124071
+ }, []);
124072
+ const screenRef = (0, import_react.useRef)(screen);
124073
+ screenRef.current = screen;
124074
+ const handleRunSelectedChange = (0, import_react.useCallback)((node) => {
124075
+ const currentScreen = screenRef.current;
124076
+ if (currentScreen.type !== "runList") return;
124077
+ setSelectedTreeNode(node);
124078
+ setSelectedRun(node ? {
124079
+ jobId: currentScreen.job.id,
124080
+ runId: node.runId,
124081
+ expertKey: node.expertKey
124082
+ } : void 0);
124083
+ }, []);
124084
+ const renderInfoContent = () => {
124085
+ switch (screen.type) {
124086
+ case "jobList": return selectedJob ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(JobInfoContent, { job: selectedJob }) : null;
124087
+ case "jobDetail": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(JobInfoContent, { job: screen.job });
124088
+ case "runList": {
124089
+ if (!selectedTreeNode) return null;
124090
+ const stats = screen.runStats.get(selectedTreeNode.runId);
124091
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RunInfoContent, {
124092
+ job: screen.job,
124093
+ run: selectedRun,
124094
+ treeNode: selectedTreeNode,
124095
+ providerName: screen.treeState.providerName,
124096
+ checkpointCount: stats?.stepCount,
124097
+ eventCount: stats?.eventCount
124098
+ });
124099
+ }
124100
+ case "runDetail":
124101
+ if (screen.tab === "checkpoints" && selectedCheckpoint) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CheckpointInfoContent, { checkpoint: selectedCheckpoint });
124102
+ if ((screen.tab === "events" || screen.tab === "activities") && selectedEvent) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventInfoContent, { event: selectedEvent });
124103
+ return null;
124104
+ case "checkpointDetail": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CheckpointInfoContent, { checkpoint: screen.checkpoint });
124105
+ case "eventDetail": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventInfoContent, { event: screen.event });
124106
+ default: return null;
124107
+ }
124108
+ };
124109
+ if (error) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
124110
+ flexDirection: "column",
124111
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
124112
+ color: colors.destructive,
124113
+ children: ["Error: ", error]
124114
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
124115
+ color: colors.muted,
124116
+ children: "Press q to quit"
124117
+ })]
124118
+ });
124119
+ if (loading) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
124120
+ color: colors.muted,
124121
+ children: "Loading..."
124122
+ });
124123
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
124124
+ flexDirection: "column",
124125
+ flexGrow: 1,
124126
+ children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
124127
+ flexDirection: "column",
124128
+ flexGrow: 1,
124129
+ children: [
124130
+ screen.type === "jobList" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(JobListScreen, {
124131
+ items: jobItems,
124132
+ onSelectJob: (job) => navigateToRunList(job),
124133
+ onViewJobDetail: navigateToJobDetail,
124134
+ onSelectedChange: setSelectedJob
124135
+ }),
124136
+ screen.type === "jobDetail" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(JobDetailScreen, {
124137
+ job: screen.job,
124138
+ onBack: () => setScreen({ type: "jobList" }),
124139
+ onViewRuns: navigateToRunList
124140
+ }),
124141
+ screen.type === "runList" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RunListScreen, {
124142
+ job: screen.job,
124143
+ treeState: screen.treeState,
124144
+ runQueries: screen.runQueries,
124145
+ delegationGroups: screen.delegationGroups,
124146
+ resumeNodes: screen.resumeNodes,
124147
+ onSelectRun: (node) => {
124148
+ const run = {
124149
+ jobId: screen.job.id,
124150
+ runId: node.runId,
124151
+ expertKey: node.expertKey
124152
+ };
124153
+ navigateToRunDetail(screen.job, run);
124154
+ },
124155
+ onBack: () => setScreen({ type: "jobList" }),
124156
+ onSelectedChange: handleRunSelectedChange
124157
+ }),
124158
+ screen.type === "runDetail" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RunDetailScreen, {
124159
+ job: screen.job,
124160
+ run: screen.run,
124161
+ checkpoints: screen.checkpoints,
124162
+ events: screen.events,
124163
+ initialTab: screen.tab,
124164
+ onSelectCheckpoint: (cp) => navigateToCheckpointDetail(screen.job, screen.run, cp),
124165
+ onSelectEvent: (ev) => navigateToEventDetail(screen.job, screen.run, ev),
124166
+ onBack: () => navigateToRunList(screen.job),
124167
+ onTabChange: (tab) => {
124168
+ setScreen({
124169
+ ...screen,
124170
+ tab
124171
+ });
124172
+ },
124173
+ onSelectedCheckpointChange: setSelectedCheckpoint,
124174
+ onSelectedEventChange: setSelectedEvent
124175
+ }),
124176
+ screen.type === "checkpointDetail" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CheckpointDetailScreen, {
124177
+ job: screen.job,
124178
+ run: screen.run,
124179
+ checkpoint: screen.checkpoint,
124180
+ onBack: () => navigateToRunDetail(screen.job, screen.run, "checkpoints")
124181
+ }),
124182
+ screen.type === "eventDetail" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventDetailScreen, {
124183
+ job: screen.job,
124184
+ run: screen.run,
124185
+ event: screen.event,
124186
+ onBack: () => navigateToRunDetail(screen.job, screen.run, "events")
124187
+ })
124188
+ ]
124189
+ }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BottomPanel, {
124190
+ onSubmit: handleChatSubmit,
124191
+ inputPlaceholder: "Ask AI about this log...",
124192
+ children: renderInfoContent() ?? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
124193
+ color: colors.muted,
124194
+ children: "Select an item to view details"
124195
+ })
124196
+ })]
124197
+ });
124198
+ };
124199
+ //#endregion
124200
+ //#region ../../packages/tui-components/src/log-viewer/render.tsx
124201
+ async function renderLogViewer(params) {
124202
+ const { waitUntilExit } = render(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LogViewerApp, {
124203
+ fetcher: params.fetcher,
124204
+ initialJobId: params.initialJobId,
124205
+ initialRunId: params.initialRunId
124206
+ }));
124207
+ await waitUntilExit();
124208
+ }
124209
+ //#endregion
121588
124210
  //#region ../../packages/tui-components/src/selection/app.tsx
121589
124211
  const selectionReducer = (_state, action) => {
121590
124212
  switch (action.type) {
@@ -121891,7 +124513,7 @@ async function startHandler(expertKey, query, options, handlerOptions) {
121891
124513
  //#endregion
121892
124514
  //#region package.json
121893
124515
  var name = "perstack";
121894
- var version = "0.0.124";
124516
+ var version = "0.0.126";
121895
124517
  var description = "PerStack CLI";
121896
124518
  //#endregion
121897
124519
  //#region bin/cli.ts
@@ -121926,7 +124548,16 @@ program.command("run").description("Run Perstack with JSON output").argument("<e
121926
124548
  lockfile
121927
124549
  });
121928
124550
  });
121929
- program.command("log").description("View execution history and events for debugging").option("--job <jobId>", "Show events for a specific job").option("--run <runId>", "Show events for a specific run").option("--checkpoint <checkpointId>", "Show checkpoint details").option("--step <step>", "Filter by step number (e.g., 5, >5, 1-10)").option("--type <type>", "Filter by event type").option("--errors", "Show error-related events only").option("--tools", "Show tool call events only").option("--delegations", "Show delegation events only").option("--filter <expression>", "Simple filter expression").option("--json", "Output as JSON").option("--pretty", "Pretty-print JSON output").option("--verbose", "Show full event details").option("--take <n>", "Number of events to display (default: 100, use 0 for all)", (val) => parsePositiveInt(val, "--take")).option("--offset <n>", "Number of events to skip (default: 0)", (val) => parsePositiveInt(val, "--offset")).option("--context <n>", "Include N events before/after matches", (val) => parsePositiveInt(val, "--context")).option("--messages", "Show message history for checkpoint").option("--summary", "Show summarized view").action((options) => logHandler(options));
124551
+ program.command("log").description("View execution history and events for debugging").option("--job <jobId>", "Show events for a specific job").option("--run <runId>", "Show events for a specific run").option("--checkpoint <checkpointId>", "Show checkpoint details").option("--step <step>", "Filter by step number (e.g., 5, >5, 1-10)").option("--type <type>", "Filter by event type").option("--errors", "Show error-related events only").option("--tools", "Show tool call events only").option("--delegations", "Show delegation events only").option("--filter <expression>", "Simple filter expression").option("--json", "Output as JSON").option("--pretty", "Pretty-print JSON output").option("--verbose", "Show full event details").option("--take <n>", "Number of events to display (default: 100, use 0 for all)", (val) => parsePositiveInt(val, "--take")).option("--offset <n>", "Number of events to skip (default: 0)", (val) => parsePositiveInt(val, "--offset")).option("--context <n>", "Include N events before/after matches", (val) => parsePositiveInt(val, "--context")).option("--messages", "Show message history for checkpoint").option("--summary", "Show summarized view").option("--text", "Force text output mode (skip interactive TUI)").action(async (options) => {
124552
+ const hasOutputFlags = options.json || options.pretty || options.summary || options.messages || options.text;
124553
+ const hasFilterFlags = options.step || options.type || options.errors || options.tools || options.delegations || options.filter || options.verbose || options.take !== void 0 || options.offset !== void 0 || options.context !== void 0;
124554
+ if (hasOutputFlags || hasFilterFlags) await logHandler(options);
124555
+ else await renderLogViewer({
124556
+ fetcher: createLogDataFetcher(createStorageAdapter(process.env.PERSTACK_STORAGE_PATH ?? `${process.cwd()}/perstack`)),
124557
+ initialJobId: options.job,
124558
+ initialRunId: options.run
124559
+ });
124560
+ });
121930
124561
  program.command("install").description("Generate perstack.lock with tool definitions for faster startup").option("--config <configPath>", "Path to perstack.toml config file").option("--env-path <path>", "Path to the environment file (can be specified multiple times)", (value, previous) => previous.concat(value), []).action(async (options) => {
121931
124562
  await installHandler({
121932
124563
  configPath: await findConfigPath(options.config),