perstack 0.0.124 → 0.0.127
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 +2741 -108
- package/dist/bin/cli.js.map +1 -1
- package/package.json +6 -5
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
|
|
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
|
|
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
|
|
20168
|
-
|
|
20169
|
-
|
|
20170
|
-
|
|
20171
|
-
|
|
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
|
-
|
|
20296
|
-
|
|
20297
|
-
|
|
20298
|
-
|
|
20299
|
-
|
|
20300
|
-
|
|
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.
|
|
21095
|
+
var version$1 = "0.0.138";
|
|
21001
21096
|
//#endregion
|
|
21002
21097
|
//#region ../../packages/runtime/src/helpers/usage.ts
|
|
21003
21098
|
function createEmptyUsage() {
|
|
@@ -92386,22 +92481,22 @@ var import_dist = (/* @__PURE__ */ __commonJSMin(((exports) => {
|
|
|
92386
92481
|
}
|
|
92387
92482
|
exports.dedent = dedent;
|
|
92388
92483
|
})))();
|
|
92389
|
-
function getEnvironmentSection(
|
|
92390
|
-
const lines = [`-
|
|
92484
|
+
function getEnvironmentSection() {
|
|
92485
|
+
const lines = [`- Platform: ${os.platform()} ${os.release()} (${os.arch()})`];
|
|
92391
92486
|
if (process.env.PERSTACK_SANDBOX === "1") lines.push("- Sandbox: This is an isolated container environment (Ubuntu). You can freely install packages with `sudo apt-get install` and run arbitrary commands without affecting the host system.");
|
|
92392
92487
|
return `Environment:\n${lines.join("\n")}`;
|
|
92393
92488
|
}
|
|
92394
|
-
function getDelegateMetaInstruction(
|
|
92489
|
+
function getDelegateMetaInstruction() {
|
|
92395
92490
|
return import_dist.dedent`
|
|
92396
92491
|
Before starting work, investigate the workspace and understand the current state. Then use the todo tool to create a plan of action. Work through the todos step by step, marking each completed as you go.
|
|
92397
92492
|
|
|
92398
92493
|
When the task is complete, call attemptCompletion with a result parameter containing your final response.
|
|
92399
92494
|
When you cannot help, call attemptCompletion without a result.
|
|
92400
92495
|
|
|
92401
|
-
${getEnvironmentSection(
|
|
92496
|
+
${getEnvironmentSection()}
|
|
92402
92497
|
`;
|
|
92403
92498
|
}
|
|
92404
|
-
function getCoordinatorMetaInstruction(
|
|
92499
|
+
function getCoordinatorMetaInstruction() {
|
|
92405
92500
|
return import_dist.dedent`
|
|
92406
92501
|
Your role:
|
|
92407
92502
|
- Act as the coordinator for the given task: define the task, set goals, and delegate.
|
|
@@ -92437,14 +92532,14 @@ function getCoordinatorMetaInstruction(startedAt) {
|
|
|
92437
92532
|
When the task is complete, call attemptCompletion with a result parameter containing your final response.
|
|
92438
92533
|
When you cannot help, call attemptCompletion without a result.
|
|
92439
92534
|
|
|
92440
|
-
${getEnvironmentSection(
|
|
92535
|
+
${getEnvironmentSection()}
|
|
92441
92536
|
`;
|
|
92442
92537
|
}
|
|
92443
|
-
function createInstructionMessage(expert
|
|
92538
|
+
function createInstructionMessage(expert) {
|
|
92444
92539
|
const preamble = import_dist.dedent`
|
|
92445
92540
|
You are Perstack, an AI expert that tackles tasks requested by users by utilizing all available tools.
|
|
92446
92541
|
|
|
92447
|
-
${isCoordinatorExpert(expert.name) ? getCoordinatorMetaInstruction(
|
|
92542
|
+
${isCoordinatorExpert(expert.name) ? getCoordinatorMetaInstruction() : getDelegateMetaInstruction()}
|
|
92448
92543
|
`;
|
|
92449
92544
|
const contents = [{
|
|
92450
92545
|
id: createId(),
|
|
@@ -92502,7 +92597,7 @@ async function initLogic({ setting, checkpoint }) {
|
|
|
92502
92597
|
if (!setting.model) throw new Error("Model is not resolved");
|
|
92503
92598
|
return startRun(setting, checkpoint, {
|
|
92504
92599
|
initialCheckpoint: checkpoint,
|
|
92505
|
-
inputMessages: [createInstructionMessage(expert
|
|
92600
|
+
inputMessages: [createInstructionMessage(expert), createUserMessage([{
|
|
92506
92601
|
type: "textPart",
|
|
92507
92602
|
text: setting.input.text
|
|
92508
92603
|
}])],
|
|
@@ -93264,8 +93359,12 @@ var DelegationExecutor = class {
|
|
|
93264
93359
|
}
|
|
93265
93360
|
});
|
|
93266
93361
|
if (parentOptions?.storeEvent) {
|
|
93267
|
-
await parentOptions.storeEvent(startRunEvent).catch(() => {
|
|
93268
|
-
|
|
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")
|
|
93284
|
-
|
|
93285
|
-
|
|
93286
|
-
|
|
93287
|
-
|
|
93288
|
-
|
|
93289
|
-
|
|
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;
|
|
@@ -121123,7 +121243,7 @@ function useDelegationTree() {
|
|
|
121123
121243
|
formattedCachedInputTokens: formatTokenCount(state.jobCachedInputTokens),
|
|
121124
121244
|
formattedOutputTokens: formatTokenCount(state.jobOutputTokens),
|
|
121125
121245
|
providerName: state.providerName,
|
|
121126
|
-
cacheHitRate: state.jobInputTokens
|
|
121246
|
+
cacheHitRate: state.jobInputTokens > 0 ? (state.jobCachedInputTokens / state.jobInputTokens * 100).toFixed(2) : "0.00"
|
|
121127
121247
|
};
|
|
121128
121248
|
}
|
|
121129
121249
|
//#endregion
|
|
@@ -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/
|
|
121256
|
-
const
|
|
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
|
|
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,75 @@ const InterfacePanel = ({ query, runStatus, onSubmit, delegationTreeState, runni
|
|
|
121268
121387
|
borderBottom: false,
|
|
121269
121388
|
borderLeft: false,
|
|
121270
121389
|
borderRight: false,
|
|
121271
|
-
children: [
|
|
121272
|
-
|
|
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: "
|
|
121275
|
-
})
|
|
121276
|
-
|
|
121277
|
-
|
|
121278
|
-
|
|
121279
|
-
|
|
121280
|
-
|
|
121281
|
-
|
|
121282
|
-
|
|
121283
|
-
|
|
121284
|
-
|
|
121285
|
-
|
|
121286
|
-
|
|
121287
|
-
|
|
121288
|
-
|
|
121289
|
-
|
|
121290
|
-
|
|
121291
|
-
|
|
121292
|
-
|
|
121293
|
-
|
|
121294
|
-
|
|
121295
|
-
|
|
121296
|
-
|
|
121297
|
-
|
|
121298
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
121299
|
-
|
|
121300
|
-
|
|
121301
|
-
|
|
121302
|
-
|
|
121303
|
-
|
|
121304
|
-
|
|
121305
|
-
|
|
121306
|
-
|
|
121307
|
-
|
|
121308
|
-
|
|
121309
|
-
|
|
121310
|
-
|
|
121311
|
-
|
|
121312
|
-
|
|
121313
|
-
|
|
121314
|
-
|
|
121315
|
-
|
|
121316
|
-
|
|
121317
|
-
|
|
121318
|
-
|
|
121319
|
-
|
|
121320
|
-
|
|
121321
|
-
|
|
121322
|
-
|
|
121323
|
-
|
|
121324
|
-
|
|
121325
|
-
|
|
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: [
|
|
121445
|
+
"In ",
|
|
121446
|
+
formattedInputTokens,
|
|
121447
|
+
" (Cached ",
|
|
121448
|
+
formattedCachedInputTokens,
|
|
121449
|
+
", Cache Hit",
|
|
121450
|
+
" ",
|
|
121451
|
+
cacheHitRate,
|
|
121452
|
+
"%)"
|
|
121453
|
+
] }),
|
|
121454
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [" · Out ", formattedOutputTokens] }),
|
|
121455
|
+
formattedReasoningTokens !== "0" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [" · Reasoning ", formattedReasoningTokens] }) : null
|
|
121456
|
+
]
|
|
121457
|
+
})
|
|
121458
|
+
] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DelegationTree, { state: delegationTreeState })]
|
|
121329
121459
|
});
|
|
121330
121460
|
};
|
|
121331
121461
|
//#endregion
|
|
@@ -121585,6 +121715,2500 @@ function renderExecution(params) {
|
|
|
121585
121715
|
};
|
|
121586
121716
|
}
|
|
121587
121717
|
//#endregion
|
|
121718
|
+
//#region ../../packages/tui-components/src/log-viewer/build-run-tree.ts
|
|
121719
|
+
function parseExpertName(expertKey) {
|
|
121720
|
+
try {
|
|
121721
|
+
return parseExpertKey(expertKey).name;
|
|
121722
|
+
} catch {
|
|
121723
|
+
return expertKey;
|
|
121724
|
+
}
|
|
121725
|
+
}
|
|
121726
|
+
function createTreeNode(runId, expertKey, model, parentRunId) {
|
|
121727
|
+
return {
|
|
121728
|
+
runId,
|
|
121729
|
+
expertName: parseExpertName(expertKey),
|
|
121730
|
+
expertKey,
|
|
121731
|
+
status: "running",
|
|
121732
|
+
actionLabel: "",
|
|
121733
|
+
actionFileArg: void 0,
|
|
121734
|
+
contextWindowUsage: 0,
|
|
121735
|
+
model,
|
|
121736
|
+
parentRunId,
|
|
121737
|
+
childRunIds: [],
|
|
121738
|
+
totalTokens: 0,
|
|
121739
|
+
inputTokens: 0,
|
|
121740
|
+
outputTokens: 0,
|
|
121741
|
+
cachedInputTokens: 0
|
|
121742
|
+
};
|
|
121743
|
+
}
|
|
121744
|
+
function addNodeToTree(state, node) {
|
|
121745
|
+
state.nodes.set(node.runId, node);
|
|
121746
|
+
if (node.parentRunId) {
|
|
121747
|
+
const parent = state.nodes.get(node.parentRunId);
|
|
121748
|
+
if (parent && !parent.childRunIds.includes(node.runId)) parent.childRunIds.push(node.runId);
|
|
121749
|
+
} else if (!state.rootRunId) state.rootRunId = node.runId;
|
|
121750
|
+
}
|
|
121751
|
+
function accumulateTokens(node, usage) {
|
|
121752
|
+
node.totalTokens += usage.totalTokens;
|
|
121753
|
+
node.inputTokens += usage.inputTokens;
|
|
121754
|
+
node.outputTokens += usage.outputTokens;
|
|
121755
|
+
node.cachedInputTokens += usage.cachedInputTokens;
|
|
121756
|
+
}
|
|
121757
|
+
function extractQueryFromStartRun(event) {
|
|
121758
|
+
if (event.type !== "startRun") return void 0;
|
|
121759
|
+
for (const msg of event.inputMessages) if (msg.type === "userMessage") {
|
|
121760
|
+
for (const part of msg.contents) if ("text" in part && part.text) return part.text.replace(/\n/g, " ");
|
|
121761
|
+
}
|
|
121762
|
+
}
|
|
121763
|
+
/**
|
|
121764
|
+
* Build a delegation tree from a list of RunEvents.
|
|
121765
|
+
*
|
|
121766
|
+
* Unlike the live execution tree (use-delegation-tree.ts), this does NOT merge
|
|
121767
|
+
* resume runs into their original runs. Each resume run becomes a separate node
|
|
121768
|
+
* in the tree, so that its checkpoints and events are individually accessible.
|
|
121769
|
+
*
|
|
121770
|
+
* The tree structure for a delegate-and-resume cycle looks like:
|
|
121771
|
+
*
|
|
121772
|
+
* Coordinator (initial run)
|
|
121773
|
+
* ├── Worker-A (delegate)
|
|
121774
|
+
* └── Worker-B (delegate)
|
|
121775
|
+
* Coordinator (resumed run) ← separate node, same expertKey
|
|
121776
|
+
*
|
|
121777
|
+
* Resume nodes are tracked in `resumeNodes` so the graph renderer can draw
|
|
121778
|
+
* merge lines (`|/`) before them.
|
|
121779
|
+
*/
|
|
121780
|
+
function buildRunTreeFromEvents(events) {
|
|
121781
|
+
const treeState = createInitialDelegationTreeState();
|
|
121782
|
+
const runQueries = /* @__PURE__ */ new Map();
|
|
121783
|
+
const runStats = /* @__PURE__ */ new Map();
|
|
121784
|
+
const delegationGroups = /* @__PURE__ */ new Map();
|
|
121785
|
+
const currentDelegatingParent = /* @__PURE__ */ new Map();
|
|
121786
|
+
const parentToChildren = /* @__PURE__ */ new Map();
|
|
121787
|
+
const parentCompletedChildren = /* @__PURE__ */ new Map();
|
|
121788
|
+
const awaitingResume = /* @__PURE__ */ new Set();
|
|
121789
|
+
const resumeNodes = /* @__PURE__ */ new Map();
|
|
121790
|
+
const runIdToNodeId = /* @__PURE__ */ new Map();
|
|
121791
|
+
function resolveParentNodeId(runId) {
|
|
121792
|
+
return runIdToNodeId.get(runId) ?? runId;
|
|
121793
|
+
}
|
|
121794
|
+
function recordStats(runId, stepNumber) {
|
|
121795
|
+
const stats = runStats.get(runId) ?? {
|
|
121796
|
+
eventCount: 0,
|
|
121797
|
+
stepCount: 0
|
|
121798
|
+
};
|
|
121799
|
+
stats.eventCount++;
|
|
121800
|
+
if (stepNumber > stats.stepCount) stats.stepCount = stepNumber;
|
|
121801
|
+
runStats.set(runId, stats);
|
|
121802
|
+
}
|
|
121803
|
+
function onChildCompleted(childRunId) {
|
|
121804
|
+
const childNode = treeState.nodes.get(childRunId);
|
|
121805
|
+
if (!childNode?.parentRunId) return;
|
|
121806
|
+
const parentRunId = childNode.parentRunId;
|
|
121807
|
+
const count = (parentCompletedChildren.get(parentRunId) ?? 0) + 1;
|
|
121808
|
+
parentCompletedChildren.set(parentRunId, count);
|
|
121809
|
+
if (count >= (parentToChildren.get(parentRunId)?.size ?? 0)) awaitingResume.add(parentRunId);
|
|
121810
|
+
}
|
|
121811
|
+
for (const event of events) {
|
|
121812
|
+
recordStats(event.runId, event.stepNumber);
|
|
121813
|
+
switch (event.type) {
|
|
121814
|
+
case "startRun": {
|
|
121815
|
+
const parentRawRunId = event.initialCheckpoint.delegatedBy?.runId;
|
|
121816
|
+
const parentRunId = parentRawRunId ? resolveParentNodeId(parentRawRunId) : void 0;
|
|
121817
|
+
const node = createTreeNode(event.runId, event.expertKey, event.model, parentRunId);
|
|
121818
|
+
node.contextWindowUsage = event.initialCheckpoint.contextWindowUsage ?? 0;
|
|
121819
|
+
addNodeToTree(treeState, node);
|
|
121820
|
+
runIdToNodeId.set(event.runId, event.runId);
|
|
121821
|
+
if (parentRunId) {
|
|
121822
|
+
const children = parentToChildren.get(parentRunId) ?? /* @__PURE__ */ new Set();
|
|
121823
|
+
children.add(event.runId);
|
|
121824
|
+
parentToChildren.set(parentRunId, children);
|
|
121825
|
+
const currentGroup = currentDelegatingParent.get(parentRunId);
|
|
121826
|
+
if (currentGroup) currentGroup.push(event.runId);
|
|
121827
|
+
}
|
|
121828
|
+
const query = extractQueryFromStartRun(event);
|
|
121829
|
+
if (query) runQueries.set(event.runId, query);
|
|
121830
|
+
break;
|
|
121831
|
+
}
|
|
121832
|
+
case "resumeFromStop": {
|
|
121833
|
+
let originalRunId;
|
|
121834
|
+
for (const candidateRunId of awaitingResume) {
|
|
121835
|
+
const candidateNode = treeState.nodes.get(candidateRunId);
|
|
121836
|
+
if (candidateNode && candidateNode.expertKey === event.expertKey) {
|
|
121837
|
+
originalRunId = candidateRunId;
|
|
121838
|
+
break;
|
|
121839
|
+
}
|
|
121840
|
+
}
|
|
121841
|
+
if (!originalRunId) {
|
|
121842
|
+
for (const [nodeId, node] of treeState.nodes) if (node.expertKey === event.expertKey && node.status === "suspending") {
|
|
121843
|
+
originalRunId = nodeId;
|
|
121844
|
+
break;
|
|
121845
|
+
}
|
|
121846
|
+
}
|
|
121847
|
+
if (originalRunId) awaitingResume.delete(originalRunId);
|
|
121848
|
+
const parentRunId = (originalRunId ? treeState.nodes.get(originalRunId) : void 0)?.parentRunId;
|
|
121849
|
+
const node = createTreeNode(event.runId, event.expertKey, event.model, parentRunId);
|
|
121850
|
+
if (event.checkpoint.contextWindowUsage !== void 0) node.contextWindowUsage = event.checkpoint.contextWindowUsage;
|
|
121851
|
+
addNodeToTree(treeState, node);
|
|
121852
|
+
if (!parentRunId && originalRunId) {
|
|
121853
|
+
const origNode = treeState.nodes.get(originalRunId);
|
|
121854
|
+
if (origNode && !origNode.childRunIds.includes(event.runId)) origNode.childRunIds.push(event.runId);
|
|
121855
|
+
}
|
|
121856
|
+
runIdToNodeId.set(event.runId, event.runId);
|
|
121857
|
+
if (originalRunId) resumeNodes.set(event.runId, originalRunId);
|
|
121858
|
+
if (originalRunId) currentDelegatingParent.delete(originalRunId);
|
|
121859
|
+
break;
|
|
121860
|
+
}
|
|
121861
|
+
case "stopRunByDelegate": {
|
|
121862
|
+
const node = treeState.nodes.get(event.runId);
|
|
121863
|
+
if (node) {
|
|
121864
|
+
node.status = "suspending";
|
|
121865
|
+
if (event.checkpoint.contextWindowUsage !== void 0) node.contextWindowUsage = event.checkpoint.contextWindowUsage;
|
|
121866
|
+
const newGroup = [];
|
|
121867
|
+
const groups = delegationGroups.get(event.runId) ?? [];
|
|
121868
|
+
groups.push(newGroup);
|
|
121869
|
+
delegationGroups.set(event.runId, groups);
|
|
121870
|
+
currentDelegatingParent.set(event.runId, newGroup);
|
|
121871
|
+
}
|
|
121872
|
+
break;
|
|
121873
|
+
}
|
|
121874
|
+
case "callTools": {
|
|
121875
|
+
const node = treeState.nodes.get(event.runId);
|
|
121876
|
+
if (node) accumulateTokens(node, event.usage);
|
|
121877
|
+
break;
|
|
121878
|
+
}
|
|
121879
|
+
case "completeRun": {
|
|
121880
|
+
const node = treeState.nodes.get(event.runId);
|
|
121881
|
+
if (node) {
|
|
121882
|
+
node.status = "completed";
|
|
121883
|
+
accumulateTokens(node, event.usage);
|
|
121884
|
+
if (event.checkpoint.contextWindowUsage !== void 0) node.contextWindowUsage = event.checkpoint.contextWindowUsage;
|
|
121885
|
+
}
|
|
121886
|
+
onChildCompleted(event.runId);
|
|
121887
|
+
break;
|
|
121888
|
+
}
|
|
121889
|
+
case "stopRunByError": {
|
|
121890
|
+
const node = treeState.nodes.get(event.runId);
|
|
121891
|
+
if (node) {
|
|
121892
|
+
node.status = "error";
|
|
121893
|
+
if (event.checkpoint.contextWindowUsage !== void 0) node.contextWindowUsage = event.checkpoint.contextWindowUsage;
|
|
121894
|
+
}
|
|
121895
|
+
onChildCompleted(event.runId);
|
|
121896
|
+
break;
|
|
121897
|
+
}
|
|
121898
|
+
case "retry": {
|
|
121899
|
+
const node = treeState.nodes.get(event.runId);
|
|
121900
|
+
if (node) accumulateTokens(node, event.usage);
|
|
121901
|
+
break;
|
|
121902
|
+
}
|
|
121903
|
+
case "continueToNextStep": {
|
|
121904
|
+
const node = treeState.nodes.get(event.runId);
|
|
121905
|
+
if (node && event.nextCheckpoint.contextWindowUsage !== void 0) node.contextWindowUsage = event.nextCheckpoint.contextWindowUsage;
|
|
121906
|
+
break;
|
|
121907
|
+
}
|
|
121908
|
+
case "resolveToolResults": break;
|
|
121909
|
+
}
|
|
121910
|
+
}
|
|
121911
|
+
return {
|
|
121912
|
+
treeState,
|
|
121913
|
+
runQueries,
|
|
121914
|
+
runStats,
|
|
121915
|
+
delegationGroups,
|
|
121916
|
+
resumeNodes
|
|
121917
|
+
};
|
|
121918
|
+
}
|
|
121919
|
+
//#endregion
|
|
121920
|
+
//#region ../../packages/tui-components/src/log-viewer/components/log-info-content.tsx
|
|
121921
|
+
function formatDuration$1(startedAt, finishedAt) {
|
|
121922
|
+
const end = finishedAt ?? Date.now();
|
|
121923
|
+
const seconds = Math.floor((end - startedAt) / 1e3);
|
|
121924
|
+
if (seconds < 60) return `${seconds}s`;
|
|
121925
|
+
const minutes = Math.floor(seconds / 60);
|
|
121926
|
+
const remainingSeconds = seconds % 60;
|
|
121927
|
+
if (minutes < 60) return `${minutes}m ${remainingSeconds}s`;
|
|
121928
|
+
return `${Math.floor(minutes / 60)}h ${minutes % 60}m`;
|
|
121929
|
+
}
|
|
121930
|
+
function formatShortDate$1(timestamp) {
|
|
121931
|
+
const d = new Date(timestamp);
|
|
121932
|
+
return `${d.getMonth() + 1}/${d.getDate()} ${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
|
|
121933
|
+
}
|
|
121934
|
+
function getUsageIcon$1(ratio) {
|
|
121935
|
+
const percent = ratio * 100;
|
|
121936
|
+
if (percent >= 75) return USAGE_INDICATORS.FULL;
|
|
121937
|
+
if (percent >= 50) return USAGE_INDICATORS.HIGH;
|
|
121938
|
+
if (percent >= 25) return USAGE_INDICATORS.MEDIUM;
|
|
121939
|
+
if (percent >= 5) return USAGE_INDICATORS.LOW;
|
|
121940
|
+
return USAGE_INDICATORS.EMPTY;
|
|
121941
|
+
}
|
|
121942
|
+
function statusColor$3(status) {
|
|
121943
|
+
if (status === "completed") return colors.primary;
|
|
121944
|
+
if (status === "error") return colors.destructive;
|
|
121945
|
+
if (status === "suspending") return colors.primary;
|
|
121946
|
+
return colors.muted;
|
|
121947
|
+
}
|
|
121948
|
+
function JobInfoContent({ job }) {
|
|
121949
|
+
const duration = formatDuration$1(job.startedAt, job.finishedAt);
|
|
121950
|
+
const cacheRate = job.usage.inputTokens > 0 ? (job.usage.cachedInputTokens ?? 0) / job.usage.inputTokens * 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
|
+
(job.usage.cachedInputTokens ?? 0) > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
121993
|
+
" ",
|
|
121994
|
+
"(Cached ",
|
|
121995
|
+
formatTokenCount(job.usage.cachedInputTokens ?? 0),
|
|
121996
|
+
", Cache Hit",
|
|
121997
|
+
" ",
|
|
121998
|
+
cacheRate.toFixed(2),
|
|
121999
|
+
"%)"
|
|
122000
|
+
] }) : null,
|
|
122001
|
+
" · Out ",
|
|
122002
|
+
formatTokenCount(job.usage.outputTokens)
|
|
122003
|
+
]
|
|
122004
|
+
})
|
|
122005
|
+
] });
|
|
122006
|
+
}
|
|
122007
|
+
function RunInfoContent({ run, treeNode, providerName, checkpointCount, eventCount }) {
|
|
122008
|
+
const parts = [];
|
|
122009
|
+
if (providerName) parts.push(providerName);
|
|
122010
|
+
if (treeNode?.model) parts.push(treeNode.model);
|
|
122011
|
+
if (checkpointCount !== void 0) parts.push(`${checkpointCount} steps`);
|
|
122012
|
+
if (eventCount !== void 0) parts.push(`${eventCount} events`);
|
|
122013
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
122014
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122015
|
+
wrap: "truncate",
|
|
122016
|
+
children: [
|
|
122017
|
+
run.expertKey ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122018
|
+
bold: true,
|
|
122019
|
+
children: run.expertKey
|
|
122020
|
+
}) : null,
|
|
122021
|
+
run.expertKey ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122022
|
+
dimColor: true,
|
|
122023
|
+
children: " · "
|
|
122024
|
+
}) : null,
|
|
122025
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122026
|
+
color: treeNode ? statusColor$3(treeNode.status) : void 0,
|
|
122027
|
+
children: treeNode ? treeNode.status : "unknown"
|
|
122028
|
+
})
|
|
122029
|
+
]
|
|
122030
|
+
}),
|
|
122031
|
+
parts.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122032
|
+
dimColor: true,
|
|
122033
|
+
wrap: "truncate",
|
|
122034
|
+
children: parts.join(" · ")
|
|
122035
|
+
}) : null,
|
|
122036
|
+
treeNode ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122037
|
+
dimColor: true,
|
|
122038
|
+
wrap: "truncate",
|
|
122039
|
+
children: [
|
|
122040
|
+
"Tokens: In ",
|
|
122041
|
+
formatTokenCount(treeNode.inputTokens),
|
|
122042
|
+
treeNode.cachedInputTokens > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
122043
|
+
" ",
|
|
122044
|
+
"(Cached ",
|
|
122045
|
+
formatTokenCount(treeNode.cachedInputTokens),
|
|
122046
|
+
", Cache Hit",
|
|
122047
|
+
" ",
|
|
122048
|
+
(treeNode.inputTokens > 0 ? treeNode.cachedInputTokens / treeNode.inputTokens * 100 : 0).toFixed(2),
|
|
122049
|
+
"%)"
|
|
122050
|
+
] }) : null,
|
|
122051
|
+
" · Out ",
|
|
122052
|
+
formatTokenCount(treeNode.outputTokens)
|
|
122053
|
+
]
|
|
122054
|
+
}) : null
|
|
122055
|
+
] });
|
|
122056
|
+
}
|
|
122057
|
+
function CheckpointInfoContent({ checkpoint }) {
|
|
122058
|
+
const cp = checkpoint;
|
|
122059
|
+
const usageIcon = cp.contextWindowUsage !== void 0 ? ` ${getUsageIcon$1(cp.contextWindowUsage)}` : "";
|
|
122060
|
+
const usagePercent = cp.contextWindowUsage !== void 0 ? ` ${(cp.contextWindowUsage * 100).toFixed(1)}%` : "";
|
|
122061
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
122062
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122063
|
+
wrap: "truncate",
|
|
122064
|
+
children: [
|
|
122065
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122066
|
+
bold: true,
|
|
122067
|
+
children: ["Step ", cp.stepNumber]
|
|
122068
|
+
}),
|
|
122069
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122070
|
+
dimColor: true,
|
|
122071
|
+
children: " · "
|
|
122072
|
+
}),
|
|
122073
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122074
|
+
color: statusColor$3(cp.status),
|
|
122075
|
+
children: cp.status
|
|
122076
|
+
}),
|
|
122077
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122078
|
+
dimColor: true,
|
|
122079
|
+
children: " · "
|
|
122080
|
+
}),
|
|
122081
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cp.expert.key }),
|
|
122082
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122083
|
+
dimColor: true,
|
|
122084
|
+
children: " · "
|
|
122085
|
+
}),
|
|
122086
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cp.id })
|
|
122087
|
+
]
|
|
122088
|
+
}),
|
|
122089
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122090
|
+
dimColor: true,
|
|
122091
|
+
wrap: "truncate",
|
|
122092
|
+
children: [
|
|
122093
|
+
cp.messages.length,
|
|
122094
|
+
" msgs",
|
|
122095
|
+
usageIcon ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
122096
|
+
" ·",
|
|
122097
|
+
usageIcon,
|
|
122098
|
+
usagePercent
|
|
122099
|
+
] }) : null,
|
|
122100
|
+
" · ",
|
|
122101
|
+
"Tokens: In ",
|
|
122102
|
+
formatTokenCount(cp.usage.inputTokens),
|
|
122103
|
+
" · Out",
|
|
122104
|
+
" ",
|
|
122105
|
+
formatTokenCount(cp.usage.outputTokens)
|
|
122106
|
+
]
|
|
122107
|
+
}),
|
|
122108
|
+
cp.error ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122109
|
+
color: colors.destructive,
|
|
122110
|
+
wrap: "truncate",
|
|
122111
|
+
children: [
|
|
122112
|
+
"Error: ",
|
|
122113
|
+
cp.error.name,
|
|
122114
|
+
": ",
|
|
122115
|
+
cp.error.message
|
|
122116
|
+
]
|
|
122117
|
+
}) : null,
|
|
122118
|
+
cp.delegateTo && cp.delegateTo.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122119
|
+
dimColor: true,
|
|
122120
|
+
wrap: "truncate",
|
|
122121
|
+
children: ["Delegates: ", cp.delegateTo.map((d) => d.expert.key).join(", ")]
|
|
122122
|
+
}) : null
|
|
122123
|
+
] });
|
|
122124
|
+
}
|
|
122125
|
+
function EventInfoContent({ event }) {
|
|
122126
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
122127
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122128
|
+
wrap: "truncate",
|
|
122129
|
+
children: [
|
|
122130
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122131
|
+
bold: true,
|
|
122132
|
+
children: event.type
|
|
122133
|
+
}),
|
|
122134
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122135
|
+
dimColor: true,
|
|
122136
|
+
children: " · "
|
|
122137
|
+
}),
|
|
122138
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: ["Step ", event.stepNumber] }),
|
|
122139
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122140
|
+
dimColor: true,
|
|
122141
|
+
children: " · "
|
|
122142
|
+
}),
|
|
122143
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.expertKey }),
|
|
122144
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122145
|
+
dimColor: true,
|
|
122146
|
+
children: " · "
|
|
122147
|
+
}),
|
|
122148
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.id })
|
|
122149
|
+
]
|
|
122150
|
+
}),
|
|
122151
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122152
|
+
dimColor: true,
|
|
122153
|
+
wrap: "truncate",
|
|
122154
|
+
children: [
|
|
122155
|
+
formatShortDate$1(event.timestamp),
|
|
122156
|
+
" · run:",
|
|
122157
|
+
event.runId
|
|
122158
|
+
]
|
|
122159
|
+
}),
|
|
122160
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventSummaryLine, { event })
|
|
122161
|
+
] });
|
|
122162
|
+
}
|
|
122163
|
+
function EventSummaryLine({ event }) {
|
|
122164
|
+
switch (event.type) {
|
|
122165
|
+
case "callTools": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122166
|
+
dimColor: true,
|
|
122167
|
+
wrap: "truncate",
|
|
122168
|
+
children: ["Tools: ", event.toolCalls.map((tc) => `${tc.skillName}/${tc.toolName}`).join(", ")]
|
|
122169
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122170
|
+
dimColor: true,
|
|
122171
|
+
wrap: "truncate",
|
|
122172
|
+
children: [
|
|
122173
|
+
"Tokens: In ",
|
|
122174
|
+
formatTokenCount(event.usage.inputTokens),
|
|
122175
|
+
" · Out",
|
|
122176
|
+
" ",
|
|
122177
|
+
formatTokenCount(event.usage.outputTokens)
|
|
122178
|
+
]
|
|
122179
|
+
})] });
|
|
122180
|
+
case "completeRun": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122181
|
+
dimColor: true,
|
|
122182
|
+
wrap: "truncate",
|
|
122183
|
+
children: ["Result: ", event.text]
|
|
122184
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122185
|
+
dimColor: true,
|
|
122186
|
+
wrap: "truncate",
|
|
122187
|
+
children: [
|
|
122188
|
+
"Tokens: In ",
|
|
122189
|
+
formatTokenCount(event.usage.inputTokens),
|
|
122190
|
+
" · Out",
|
|
122191
|
+
" ",
|
|
122192
|
+
formatTokenCount(event.usage.outputTokens)
|
|
122193
|
+
]
|
|
122194
|
+
})] });
|
|
122195
|
+
case "stopRunByError": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122196
|
+
color: colors.destructive,
|
|
122197
|
+
wrap: "truncate",
|
|
122198
|
+
children: [
|
|
122199
|
+
"Error: ",
|
|
122200
|
+
event.error.name,
|
|
122201
|
+
": ",
|
|
122202
|
+
event.error.message
|
|
122203
|
+
]
|
|
122204
|
+
});
|
|
122205
|
+
case "retry": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122206
|
+
color: colors.warn,
|
|
122207
|
+
wrap: "truncate",
|
|
122208
|
+
children: ["Reason: ", event.reason]
|
|
122209
|
+
});
|
|
122210
|
+
case "startRun": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122211
|
+
dimColor: true,
|
|
122212
|
+
wrap: "truncate",
|
|
122213
|
+
children: [
|
|
122214
|
+
"Model: ",
|
|
122215
|
+
event.model,
|
|
122216
|
+
" · ",
|
|
122217
|
+
event.inputMessages.length,
|
|
122218
|
+
" input messages"
|
|
122219
|
+
]
|
|
122220
|
+
});
|
|
122221
|
+
case "stopRunByDelegate": return event.checkpoint.delegateTo ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122222
|
+
dimColor: true,
|
|
122223
|
+
wrap: "truncate",
|
|
122224
|
+
children: ["Delegates: ", event.checkpoint.delegateTo.map((d) => d.expert.key).join(", ")]
|
|
122225
|
+
}) : null;
|
|
122226
|
+
case "startGeneration": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122227
|
+
dimColor: true,
|
|
122228
|
+
wrap: "truncate",
|
|
122229
|
+
children: [event.messages.length, " messages"]
|
|
122230
|
+
});
|
|
122231
|
+
default: return null;
|
|
122232
|
+
}
|
|
122233
|
+
}
|
|
122234
|
+
//#endregion
|
|
122235
|
+
//#region ../../packages/tui-components/src/log-viewer/screens/checkpoint-detail.tsx
|
|
122236
|
+
const CheckpointDetailScreen = ({ checkpoint, onBack }) => {
|
|
122237
|
+
useInput((char, key) => {
|
|
122238
|
+
if (key.escape || char === "b") onBack();
|
|
122239
|
+
});
|
|
122240
|
+
const cp = checkpoint;
|
|
122241
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122242
|
+
flexDirection: "column",
|
|
122243
|
+
flexGrow: 1,
|
|
122244
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122245
|
+
color: colors.accent,
|
|
122246
|
+
bold: true,
|
|
122247
|
+
children: "Checkpoint Detail"
|
|
122248
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122249
|
+
color: colors.muted,
|
|
122250
|
+
children: " b:Back q:Quit"
|
|
122251
|
+
})] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122252
|
+
flexDirection: "column",
|
|
122253
|
+
paddingX: 1,
|
|
122254
|
+
children: [
|
|
122255
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122256
|
+
gap: 1,
|
|
122257
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122258
|
+
color: colors.accent,
|
|
122259
|
+
children: "ID:"
|
|
122260
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cp.id })]
|
|
122261
|
+
}),
|
|
122262
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122263
|
+
gap: 1,
|
|
122264
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122265
|
+
color: colors.accent,
|
|
122266
|
+
children: "Status:"
|
|
122267
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122268
|
+
color: cp.status === "completed" ? colors.success : cp.status === "proceeding" ? colors.accent : colors.destructive,
|
|
122269
|
+
children: cp.status
|
|
122270
|
+
})]
|
|
122271
|
+
}),
|
|
122272
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122273
|
+
gap: 1,
|
|
122274
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122275
|
+
color: colors.accent,
|
|
122276
|
+
children: "Step:"
|
|
122277
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cp.stepNumber })]
|
|
122278
|
+
}),
|
|
122279
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122280
|
+
gap: 1,
|
|
122281
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122282
|
+
color: colors.accent,
|
|
122283
|
+
children: "Expert:"
|
|
122284
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
|
|
122285
|
+
cp.expert.name,
|
|
122286
|
+
" (",
|
|
122287
|
+
cp.expert.key,
|
|
122288
|
+
"@",
|
|
122289
|
+
cp.expert.version,
|
|
122290
|
+
")"
|
|
122291
|
+
] })]
|
|
122292
|
+
}),
|
|
122293
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122294
|
+
gap: 1,
|
|
122295
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122296
|
+
color: colors.accent,
|
|
122297
|
+
children: "Messages:"
|
|
122298
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cp.messages.length })]
|
|
122299
|
+
}),
|
|
122300
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122301
|
+
gap: 1,
|
|
122302
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122303
|
+
color: colors.accent,
|
|
122304
|
+
children: "Input Tokens:"
|
|
122305
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cp.usage.inputTokens.toLocaleString() })]
|
|
122306
|
+
}),
|
|
122307
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122308
|
+
gap: 1,
|
|
122309
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122310
|
+
color: colors.accent,
|
|
122311
|
+
children: "Output Tokens:"
|
|
122312
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cp.usage.outputTokens.toLocaleString() })]
|
|
122313
|
+
}),
|
|
122314
|
+
cp.contextWindow && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122315
|
+
gap: 1,
|
|
122316
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122317
|
+
color: colors.accent,
|
|
122318
|
+
children: "Context Window:"
|
|
122319
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cp.contextWindow.toLocaleString() })]
|
|
122320
|
+
}),
|
|
122321
|
+
cp.contextWindowUsage !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122322
|
+
gap: 1,
|
|
122323
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122324
|
+
color: colors.accent,
|
|
122325
|
+
children: "Context Usage:"
|
|
122326
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [(cp.contextWindowUsage * 100).toFixed(1), "%"] })]
|
|
122327
|
+
}),
|
|
122328
|
+
cp.error && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122329
|
+
flexDirection: "column",
|
|
122330
|
+
children: [
|
|
122331
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122332
|
+
color: colors.destructive,
|
|
122333
|
+
bold: true,
|
|
122334
|
+
children: "Error:"
|
|
122335
|
+
}),
|
|
122336
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122337
|
+
color: colors.destructive,
|
|
122338
|
+
children: [
|
|
122339
|
+
cp.error.name,
|
|
122340
|
+
": ",
|
|
122341
|
+
truncateText(cp.error.message, 100)
|
|
122342
|
+
]
|
|
122343
|
+
}),
|
|
122344
|
+
cp.error.statusCode && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122345
|
+
color: colors.muted,
|
|
122346
|
+
children: ["Status Code: ", cp.error.statusCode]
|
|
122347
|
+
}),
|
|
122348
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122349
|
+
color: colors.muted,
|
|
122350
|
+
children: ["Retryable: ", cp.error.isRetryable ? "yes" : "no"]
|
|
122351
|
+
})
|
|
122352
|
+
]
|
|
122353
|
+
}),
|
|
122354
|
+
cp.delegateTo && cp.delegateTo.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122355
|
+
flexDirection: "column",
|
|
122356
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122357
|
+
color: colors.accent,
|
|
122358
|
+
bold: true,
|
|
122359
|
+
children: "Delegating to:"
|
|
122360
|
+
}), cp.delegateTo.map((d) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122361
|
+
color: colors.muted,
|
|
122362
|
+
children: [
|
|
122363
|
+
"- ",
|
|
122364
|
+
d.expert.key,
|
|
122365
|
+
": ",
|
|
122366
|
+
truncateText(d.query, 60)
|
|
122367
|
+
]
|
|
122368
|
+
}, d.toolCallId))]
|
|
122369
|
+
}),
|
|
122370
|
+
cp.delegatedBy && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122371
|
+
flexDirection: "column",
|
|
122372
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122373
|
+
color: colors.accent,
|
|
122374
|
+
bold: true,
|
|
122375
|
+
children: "Delegated by:"
|
|
122376
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122377
|
+
color: colors.muted,
|
|
122378
|
+
children: [
|
|
122379
|
+
cp.delegatedBy.expert.key,
|
|
122380
|
+
" via ",
|
|
122381
|
+
cp.delegatedBy.toolName
|
|
122382
|
+
]
|
|
122383
|
+
})]
|
|
122384
|
+
}),
|
|
122385
|
+
cp.pendingToolCalls && cp.pendingToolCalls.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122386
|
+
gap: 1,
|
|
122387
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122388
|
+
color: colors.accent,
|
|
122389
|
+
children: "Pending Tool Calls:"
|
|
122390
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cp.pendingToolCalls.length })]
|
|
122391
|
+
})
|
|
122392
|
+
]
|
|
122393
|
+
})]
|
|
122394
|
+
});
|
|
122395
|
+
};
|
|
122396
|
+
//#endregion
|
|
122397
|
+
//#region ../../packages/tui-components/src/log-viewer/screens/event-detail.tsx
|
|
122398
|
+
const EventDetailScreen = ({ event, onBack }) => {
|
|
122399
|
+
useInput((char, key) => {
|
|
122400
|
+
if (key.escape || char === "b") onBack();
|
|
122401
|
+
});
|
|
122402
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122403
|
+
flexDirection: "column",
|
|
122404
|
+
flexGrow: 1,
|
|
122405
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122406
|
+
color: colors.accent,
|
|
122407
|
+
bold: true,
|
|
122408
|
+
children: "Event Detail"
|
|
122409
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122410
|
+
color: colors.muted,
|
|
122411
|
+
children: " b:Back q:Quit"
|
|
122412
|
+
})] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122413
|
+
flexDirection: "column",
|
|
122414
|
+
paddingX: 1,
|
|
122415
|
+
children: [
|
|
122416
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122417
|
+
gap: 1,
|
|
122418
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122419
|
+
color: colors.accent,
|
|
122420
|
+
children: "ID:"
|
|
122421
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.id })]
|
|
122422
|
+
}),
|
|
122423
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122424
|
+
gap: 1,
|
|
122425
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122426
|
+
color: colors.accent,
|
|
122427
|
+
children: "Type:"
|
|
122428
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122429
|
+
bold: true,
|
|
122430
|
+
children: event.type
|
|
122431
|
+
})]
|
|
122432
|
+
}),
|
|
122433
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122434
|
+
gap: 1,
|
|
122435
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122436
|
+
color: colors.accent,
|
|
122437
|
+
children: "Step:"
|
|
122438
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.stepNumber })]
|
|
122439
|
+
}),
|
|
122440
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122441
|
+
gap: 1,
|
|
122442
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122443
|
+
color: colors.accent,
|
|
122444
|
+
children: "Expert:"
|
|
122445
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.expertKey })]
|
|
122446
|
+
}),
|
|
122447
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122448
|
+
gap: 1,
|
|
122449
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122450
|
+
color: colors.accent,
|
|
122451
|
+
children: "Run:"
|
|
122452
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.runId })]
|
|
122453
|
+
}),
|
|
122454
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122455
|
+
gap: 1,
|
|
122456
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122457
|
+
color: colors.accent,
|
|
122458
|
+
children: "Job:"
|
|
122459
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.jobId })]
|
|
122460
|
+
}),
|
|
122461
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122462
|
+
gap: 1,
|
|
122463
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122464
|
+
color: colors.accent,
|
|
122465
|
+
children: "Timestamp:"
|
|
122466
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: formatTimestamp$1(event.timestamp) })]
|
|
122467
|
+
}),
|
|
122468
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
122469
|
+
marginTop: 1,
|
|
122470
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122471
|
+
color: colors.accent,
|
|
122472
|
+
bold: true,
|
|
122473
|
+
children: "Payload:"
|
|
122474
|
+
})
|
|
122475
|
+
}),
|
|
122476
|
+
renderEventPayload(event)
|
|
122477
|
+
]
|
|
122478
|
+
})]
|
|
122479
|
+
});
|
|
122480
|
+
};
|
|
122481
|
+
function renderEventPayload(event) {
|
|
122482
|
+
switch (event.type) {
|
|
122483
|
+
case "startRun": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122484
|
+
flexDirection: "column",
|
|
122485
|
+
children: [
|
|
122486
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122487
|
+
gap: 1,
|
|
122488
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122489
|
+
color: colors.muted,
|
|
122490
|
+
children: "Model:"
|
|
122491
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.model })]
|
|
122492
|
+
}),
|
|
122493
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122494
|
+
gap: 1,
|
|
122495
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122496
|
+
color: colors.muted,
|
|
122497
|
+
children: "Input Messages:"
|
|
122498
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.inputMessages.length })]
|
|
122499
|
+
}),
|
|
122500
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122501
|
+
gap: 1,
|
|
122502
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122503
|
+
color: colors.muted,
|
|
122504
|
+
children: "Checkpoint Status:"
|
|
122505
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.initialCheckpoint.status })]
|
|
122506
|
+
})
|
|
122507
|
+
]
|
|
122508
|
+
});
|
|
122509
|
+
case "callTools": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122510
|
+
flexDirection: "column",
|
|
122511
|
+
children: [
|
|
122512
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122513
|
+
gap: 1,
|
|
122514
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122515
|
+
color: colors.muted,
|
|
122516
|
+
children: "Tool Calls:"
|
|
122517
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.toolCalls.length })]
|
|
122518
|
+
}),
|
|
122519
|
+
event.toolCalls.map((tc) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122520
|
+
flexDirection: "column",
|
|
122521
|
+
marginLeft: 2,
|
|
122522
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122523
|
+
color: colors.primary,
|
|
122524
|
+
children: [
|
|
122525
|
+
tc.skillName,
|
|
122526
|
+
"/",
|
|
122527
|
+
tc.toolName,
|
|
122528
|
+
" (",
|
|
122529
|
+
tc.id,
|
|
122530
|
+
")"
|
|
122531
|
+
]
|
|
122532
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122533
|
+
color: colors.muted,
|
|
122534
|
+
children: truncateText(JSON.stringify(tc.args), 120)
|
|
122535
|
+
})]
|
|
122536
|
+
}, tc.id)),
|
|
122537
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122538
|
+
gap: 1,
|
|
122539
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122540
|
+
color: colors.muted,
|
|
122541
|
+
children: "Input Tokens:"
|
|
122542
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.usage.inputTokens.toLocaleString() })]
|
|
122543
|
+
}),
|
|
122544
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122545
|
+
gap: 1,
|
|
122546
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122547
|
+
color: colors.muted,
|
|
122548
|
+
children: "Output Tokens:"
|
|
122549
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.usage.outputTokens.toLocaleString() })]
|
|
122550
|
+
})
|
|
122551
|
+
]
|
|
122552
|
+
});
|
|
122553
|
+
case "completeRun": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122554
|
+
flexDirection: "column",
|
|
122555
|
+
children: [
|
|
122556
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122557
|
+
gap: 1,
|
|
122558
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122559
|
+
color: colors.muted,
|
|
122560
|
+
children: "Result:"
|
|
122561
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: truncateText(event.text, 200) })]
|
|
122562
|
+
}),
|
|
122563
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122564
|
+
gap: 1,
|
|
122565
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122566
|
+
color: colors.muted,
|
|
122567
|
+
children: "Input Tokens:"
|
|
122568
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.usage.inputTokens.toLocaleString() })]
|
|
122569
|
+
}),
|
|
122570
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122571
|
+
gap: 1,
|
|
122572
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122573
|
+
color: colors.muted,
|
|
122574
|
+
children: "Output Tokens:"
|
|
122575
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.usage.outputTokens.toLocaleString() })]
|
|
122576
|
+
})
|
|
122577
|
+
]
|
|
122578
|
+
});
|
|
122579
|
+
case "stopRunByError": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122580
|
+
flexDirection: "column",
|
|
122581
|
+
children: [
|
|
122582
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122583
|
+
gap: 1,
|
|
122584
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122585
|
+
color: colors.destructive,
|
|
122586
|
+
children: "Error:"
|
|
122587
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122588
|
+
color: colors.destructive,
|
|
122589
|
+
children: [
|
|
122590
|
+
event.error.name,
|
|
122591
|
+
": ",
|
|
122592
|
+
event.error.message
|
|
122593
|
+
]
|
|
122594
|
+
})]
|
|
122595
|
+
}),
|
|
122596
|
+
event.error.statusCode && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122597
|
+
gap: 1,
|
|
122598
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122599
|
+
color: colors.muted,
|
|
122600
|
+
children: "Status Code:"
|
|
122601
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.error.statusCode })]
|
|
122602
|
+
}),
|
|
122603
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122604
|
+
gap: 1,
|
|
122605
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122606
|
+
color: colors.muted,
|
|
122607
|
+
children: "Retryable:"
|
|
122608
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.error.isRetryable ? "yes" : "no" })]
|
|
122609
|
+
})
|
|
122610
|
+
]
|
|
122611
|
+
});
|
|
122612
|
+
case "retry": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
122613
|
+
flexDirection: "column",
|
|
122614
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122615
|
+
gap: 1,
|
|
122616
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122617
|
+
color: colors.warn,
|
|
122618
|
+
children: "Reason:"
|
|
122619
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.reason })]
|
|
122620
|
+
})
|
|
122621
|
+
});
|
|
122622
|
+
case "startGeneration": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
122623
|
+
flexDirection: "column",
|
|
122624
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122625
|
+
gap: 1,
|
|
122626
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122627
|
+
color: colors.muted,
|
|
122628
|
+
children: "Messages:"
|
|
122629
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.messages.length })]
|
|
122630
|
+
})
|
|
122631
|
+
});
|
|
122632
|
+
case "stopRunByDelegate": return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122633
|
+
flexDirection: "column",
|
|
122634
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122635
|
+
gap: 1,
|
|
122636
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122637
|
+
color: colors.muted,
|
|
122638
|
+
children: "Step:"
|
|
122639
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: event.step.stepNumber })]
|
|
122640
|
+
}), event.checkpoint.delegateTo && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122641
|
+
flexDirection: "column",
|
|
122642
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122643
|
+
color: colors.muted,
|
|
122644
|
+
children: "Delegates:"
|
|
122645
|
+
}), event.checkpoint.delegateTo.map((d) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122646
|
+
color: colors.primary,
|
|
122647
|
+
children: [
|
|
122648
|
+
"- ",
|
|
122649
|
+
d.expert.key,
|
|
122650
|
+
": ",
|
|
122651
|
+
truncateText(d.query, 60)
|
|
122652
|
+
]
|
|
122653
|
+
}, d.toolCallId))]
|
|
122654
|
+
})]
|
|
122655
|
+
});
|
|
122656
|
+
default: return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122657
|
+
color: colors.muted,
|
|
122658
|
+
children: truncateText(JSON.stringify(event, null, 2), 500)
|
|
122659
|
+
});
|
|
122660
|
+
}
|
|
122661
|
+
}
|
|
122662
|
+
//#endregion
|
|
122663
|
+
//#region ../../packages/tui-components/src/log-viewer/screens/job-detail.tsx
|
|
122664
|
+
const JobDetailScreen = ({ job, onBack, onViewRuns }) => {
|
|
122665
|
+
useInput((char, key) => {
|
|
122666
|
+
if (key.escape || char === "b") {
|
|
122667
|
+
onBack();
|
|
122668
|
+
return;
|
|
122669
|
+
}
|
|
122670
|
+
if (key.return || char === "r") onViewRuns(job);
|
|
122671
|
+
});
|
|
122672
|
+
const statusColor = job.status === "completed" ? colors.success : job.status === "running" ? colors.accent : colors.destructive;
|
|
122673
|
+
const duration = job.finishedAt && job.startedAt ? `${((job.finishedAt - job.startedAt) / 1e3).toFixed(1)}s` : "running...";
|
|
122674
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122675
|
+
flexDirection: "column",
|
|
122676
|
+
flexGrow: 1,
|
|
122677
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122678
|
+
color: colors.accent,
|
|
122679
|
+
bold: true,
|
|
122680
|
+
children: "Job Detail"
|
|
122681
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122682
|
+
color: colors.muted,
|
|
122683
|
+
children: " Enter/r:View Runs b:Back q:Quit"
|
|
122684
|
+
})] }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122685
|
+
flexDirection: "column",
|
|
122686
|
+
paddingX: 1,
|
|
122687
|
+
children: [
|
|
122688
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122689
|
+
gap: 1,
|
|
122690
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122691
|
+
color: colors.accent,
|
|
122692
|
+
children: "ID:"
|
|
122693
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: job.id })]
|
|
122694
|
+
}),
|
|
122695
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122696
|
+
gap: 1,
|
|
122697
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122698
|
+
color: colors.accent,
|
|
122699
|
+
children: "Status:"
|
|
122700
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122701
|
+
color: statusColor,
|
|
122702
|
+
children: job.status
|
|
122703
|
+
})]
|
|
122704
|
+
}),
|
|
122705
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122706
|
+
gap: 1,
|
|
122707
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122708
|
+
color: colors.accent,
|
|
122709
|
+
children: "Expert:"
|
|
122710
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: job.coordinatorExpertKey })]
|
|
122711
|
+
}),
|
|
122712
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122713
|
+
gap: 1,
|
|
122714
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122715
|
+
color: colors.accent,
|
|
122716
|
+
children: "Version:"
|
|
122717
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: job.runtimeVersion })]
|
|
122718
|
+
}),
|
|
122719
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122720
|
+
gap: 1,
|
|
122721
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122722
|
+
color: colors.accent,
|
|
122723
|
+
children: "Steps:"
|
|
122724
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: job.totalSteps })]
|
|
122725
|
+
}),
|
|
122726
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122727
|
+
gap: 1,
|
|
122728
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122729
|
+
color: colors.accent,
|
|
122730
|
+
children: "Duration:"
|
|
122731
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: duration })]
|
|
122732
|
+
}),
|
|
122733
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122734
|
+
gap: 1,
|
|
122735
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122736
|
+
color: colors.accent,
|
|
122737
|
+
children: "Started:"
|
|
122738
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: formatTimestamp$1(job.startedAt) })]
|
|
122739
|
+
}),
|
|
122740
|
+
job.finishedAt && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122741
|
+
gap: 1,
|
|
122742
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122743
|
+
color: colors.accent,
|
|
122744
|
+
children: "Finished:"
|
|
122745
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: formatTimestamp$1(job.finishedAt) })]
|
|
122746
|
+
}),
|
|
122747
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122748
|
+
gap: 1,
|
|
122749
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122750
|
+
color: colors.accent,
|
|
122751
|
+
children: "Input Tokens:"
|
|
122752
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: job.usage.inputTokens.toLocaleString() })]
|
|
122753
|
+
}),
|
|
122754
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122755
|
+
gap: 1,
|
|
122756
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122757
|
+
color: colors.accent,
|
|
122758
|
+
children: "Output Tokens:"
|
|
122759
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: job.usage.outputTokens.toLocaleString() })]
|
|
122760
|
+
})
|
|
122761
|
+
]
|
|
122762
|
+
})]
|
|
122763
|
+
});
|
|
122764
|
+
};
|
|
122765
|
+
//#endregion
|
|
122766
|
+
//#region ../../packages/tui-components/src/log-viewer/screens/job-list.tsx
|
|
122767
|
+
const MAX_VISIBLE = 10;
|
|
122768
|
+
function formatDuration(startedAt, finishedAt) {
|
|
122769
|
+
const end = finishedAt ?? Date.now();
|
|
122770
|
+
const seconds = Math.floor((end - startedAt) / 1e3);
|
|
122771
|
+
if (seconds < 60) return `${seconds}s`;
|
|
122772
|
+
const minutes = Math.floor(seconds / 60);
|
|
122773
|
+
const remainingSeconds = seconds % 60;
|
|
122774
|
+
if (minutes < 60) return `${minutes}m ${remainingSeconds}s`;
|
|
122775
|
+
return `${Math.floor(minutes / 60)}h ${minutes % 60}m`;
|
|
122776
|
+
}
|
|
122777
|
+
function formatShortDate(timestamp) {
|
|
122778
|
+
const d = new Date(timestamp);
|
|
122779
|
+
return `${d.getMonth() + 1}/${d.getDate()} ${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
|
|
122780
|
+
}
|
|
122781
|
+
function statusColor$2(status) {
|
|
122782
|
+
if (status === "completed") return colors.success;
|
|
122783
|
+
if (status === "running" || status === "proceeding" || status === "init") return colors.accent;
|
|
122784
|
+
return colors.destructive;
|
|
122785
|
+
}
|
|
122786
|
+
const JobListScreen = ({ items, onSelectJob, onViewJobDetail, onSelectedChange }) => {
|
|
122787
|
+
const { selectedIndex, handleNavigation } = useListNavigation({
|
|
122788
|
+
items,
|
|
122789
|
+
onSelect: (item) => onSelectJob(item.job)
|
|
122790
|
+
});
|
|
122791
|
+
useInput((char, key) => {
|
|
122792
|
+
if (char === "d" && items[selectedIndex]) {
|
|
122793
|
+
onViewJobDetail(items[selectedIndex].job);
|
|
122794
|
+
return;
|
|
122795
|
+
}
|
|
122796
|
+
handleNavigation(char, key);
|
|
122797
|
+
});
|
|
122798
|
+
const selectedItem = items[selectedIndex];
|
|
122799
|
+
(0, import_react.useEffect)(() => {
|
|
122800
|
+
onSelectedChange(selectedItem?.job);
|
|
122801
|
+
}, [selectedItem, onSelectedChange]);
|
|
122802
|
+
const { scrollOffset, displayItems } = (0, import_react.useMemo)(() => {
|
|
122803
|
+
const offset = Math.max(0, Math.min(selectedIndex - MAX_VISIBLE + 1, items.length - MAX_VISIBLE));
|
|
122804
|
+
return {
|
|
122805
|
+
scrollOffset: offset,
|
|
122806
|
+
displayItems: items.slice(offset, offset + MAX_VISIBLE)
|
|
122807
|
+
};
|
|
122808
|
+
}, [items, selectedIndex]);
|
|
122809
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122810
|
+
flexDirection: "column",
|
|
122811
|
+
flexGrow: 1,
|
|
122812
|
+
children: [
|
|
122813
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [
|
|
122814
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122815
|
+
color: colors.accent,
|
|
122816
|
+
bold: true,
|
|
122817
|
+
children: "Jobs"
|
|
122818
|
+
}),
|
|
122819
|
+
items.length > MAX_VISIBLE && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122820
|
+
color: colors.muted,
|
|
122821
|
+
children: [
|
|
122822
|
+
" ",
|
|
122823
|
+
"(",
|
|
122824
|
+
selectedIndex + 1,
|
|
122825
|
+
"/",
|
|
122826
|
+
items.length,
|
|
122827
|
+
")"
|
|
122828
|
+
]
|
|
122829
|
+
}),
|
|
122830
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122831
|
+
color: colors.muted,
|
|
122832
|
+
children: " Enter:Select d:Detail q:Quit"
|
|
122833
|
+
})
|
|
122834
|
+
] }),
|
|
122835
|
+
scrollOffset > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122836
|
+
color: colors.muted,
|
|
122837
|
+
children: "..."
|
|
122838
|
+
}),
|
|
122839
|
+
displayItems.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122840
|
+
color: colors.muted,
|
|
122841
|
+
children: "No jobs found"
|
|
122842
|
+
}) : displayItems.map((item, i) => {
|
|
122843
|
+
const isSelected = scrollOffset + i === selectedIndex;
|
|
122844
|
+
const { job, query } = item;
|
|
122845
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
122846
|
+
flexDirection: "column",
|
|
122847
|
+
children: [
|
|
122848
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122849
|
+
color: isSelected ? colors.accent : colors.muted,
|
|
122850
|
+
children: isSelected ? " ▸ " : " "
|
|
122851
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122852
|
+
wrap: "truncate",
|
|
122853
|
+
children: [
|
|
122854
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122855
|
+
bold: true,
|
|
122856
|
+
children: job.coordinatorExpertKey
|
|
122857
|
+
}),
|
|
122858
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122859
|
+
dimColor: true,
|
|
122860
|
+
children: " · "
|
|
122861
|
+
}),
|
|
122862
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122863
|
+
color: statusColor$2(job.status),
|
|
122864
|
+
children: job.status
|
|
122865
|
+
}),
|
|
122866
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122867
|
+
dimColor: true,
|
|
122868
|
+
children: " · "
|
|
122869
|
+
}),
|
|
122870
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [job.totalSteps, " steps"] }),
|
|
122871
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122872
|
+
dimColor: true,
|
|
122873
|
+
children: " · "
|
|
122874
|
+
}),
|
|
122875
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: formatDuration(job.startedAt, job.finishedAt) }),
|
|
122876
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122877
|
+
dimColor: true,
|
|
122878
|
+
children: " "
|
|
122879
|
+
}),
|
|
122880
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122881
|
+
dimColor: true,
|
|
122882
|
+
children: formatShortDate(job.startedAt)
|
|
122883
|
+
})
|
|
122884
|
+
]
|
|
122885
|
+
})] }),
|
|
122886
|
+
query ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
122887
|
+
paddingLeft: 3,
|
|
122888
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122889
|
+
dimColor: true,
|
|
122890
|
+
wrap: "truncate",
|
|
122891
|
+
children: [
|
|
122892
|
+
">",
|
|
122893
|
+
" ",
|
|
122894
|
+
query
|
|
122895
|
+
]
|
|
122896
|
+
})
|
|
122897
|
+
}) : null,
|
|
122898
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
122899
|
+
paddingLeft: 3,
|
|
122900
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
122901
|
+
dimColor: true,
|
|
122902
|
+
wrap: "truncate",
|
|
122903
|
+
children: [
|
|
122904
|
+
"Tokens: In ",
|
|
122905
|
+
formatTokenCount(job.usage.inputTokens),
|
|
122906
|
+
" · Out",
|
|
122907
|
+
" ",
|
|
122908
|
+
formatTokenCount(job.usage.outputTokens)
|
|
122909
|
+
]
|
|
122910
|
+
})
|
|
122911
|
+
})
|
|
122912
|
+
]
|
|
122913
|
+
}, job.id);
|
|
122914
|
+
}),
|
|
122915
|
+
scrollOffset + MAX_VISIBLE < items.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
122916
|
+
color: colors.muted,
|
|
122917
|
+
children: "..."
|
|
122918
|
+
})
|
|
122919
|
+
]
|
|
122920
|
+
});
|
|
122921
|
+
};
|
|
122922
|
+
//#endregion
|
|
122923
|
+
//#region ../../packages/tui-components/src/hooks/use-terminal-height.ts
|
|
122924
|
+
/**
|
|
122925
|
+
* Returns the current terminal height in rows and updates on resize.
|
|
122926
|
+
*/
|
|
122927
|
+
function useTerminalHeight() {
|
|
122928
|
+
const { stdout } = useStdout();
|
|
122929
|
+
const [height, setHeight] = (0, import_react.useState)(() => stdout?.rows ?? 24);
|
|
122930
|
+
(0, import_react.useEffect)(() => {
|
|
122931
|
+
if (!stdout) return;
|
|
122932
|
+
const onResize = () => {
|
|
122933
|
+
setHeight(stdout.rows);
|
|
122934
|
+
};
|
|
122935
|
+
stdout.on("resize", onResize);
|
|
122936
|
+
return () => {
|
|
122937
|
+
stdout.off("resize", onResize);
|
|
122938
|
+
};
|
|
122939
|
+
}, [stdout]);
|
|
122940
|
+
return height;
|
|
122941
|
+
}
|
|
122942
|
+
/**
|
|
122943
|
+
* Compute the number of list items that fit on screen.
|
|
122944
|
+
*
|
|
122945
|
+
* @param terminalHeight - Total terminal rows
|
|
122946
|
+
* @param reservedLines - Lines used by headers, tab bars, bottom panel, etc.
|
|
122947
|
+
* @param linesPerItem - How many terminal lines each list item occupies
|
|
122948
|
+
* @param minItems - Minimum items to show regardless of terminal size
|
|
122949
|
+
*/
|
|
122950
|
+
function computeVisibleItems(terminalHeight, reservedLines, linesPerItem, minItems = 3) {
|
|
122951
|
+
const available = terminalHeight - reservedLines;
|
|
122952
|
+
return Math.max(minItems, Math.floor(available / linesPerItem));
|
|
122953
|
+
}
|
|
122954
|
+
//#endregion
|
|
122955
|
+
//#region ../../packages/tui-components/src/log-viewer/screens/activity-list.tsx
|
|
122956
|
+
function buildActivities(events) {
|
|
122957
|
+
const activities = [];
|
|
122958
|
+
for (const event of events) switch (event.type) {
|
|
122959
|
+
case "startRun":
|
|
122960
|
+
activities.push({
|
|
122961
|
+
label: `Started run with model ${event.model}`,
|
|
122962
|
+
detail: `${event.inputMessages.length} input messages`,
|
|
122963
|
+
color: colors.accent,
|
|
122964
|
+
timestamp: event.timestamp,
|
|
122965
|
+
event
|
|
122966
|
+
});
|
|
122967
|
+
break;
|
|
122968
|
+
case "callTools": {
|
|
122969
|
+
const toolNames = event.toolCalls.map((tc) => tc.toolName).join(", ");
|
|
122970
|
+
const totalTokens = event.usage.inputTokens + event.usage.outputTokens;
|
|
122971
|
+
activities.push({
|
|
122972
|
+
label: `Called tools: ${toolNames}`,
|
|
122973
|
+
detail: `${event.toolCalls.length} call(s) · ${formatTokenCount(totalTokens)} tokens`,
|
|
122974
|
+
color: "magenta",
|
|
122975
|
+
timestamp: event.timestamp,
|
|
122976
|
+
event
|
|
122977
|
+
});
|
|
122978
|
+
break;
|
|
122979
|
+
}
|
|
122980
|
+
case "completeRun": {
|
|
122981
|
+
const resultPreview = event.text.length > 80 ? `${event.text.slice(0, 80)}...` : event.text;
|
|
122982
|
+
activities.push({
|
|
122983
|
+
label: "Run completed",
|
|
122984
|
+
detail: resultPreview,
|
|
122985
|
+
color: colors.success,
|
|
122986
|
+
timestamp: event.timestamp,
|
|
122987
|
+
event
|
|
122988
|
+
});
|
|
122989
|
+
break;
|
|
122990
|
+
}
|
|
122991
|
+
case "stopRunByError":
|
|
122992
|
+
activities.push({
|
|
122993
|
+
label: `Error: ${event.error.name}`,
|
|
122994
|
+
detail: event.error.message,
|
|
122995
|
+
color: colors.destructive,
|
|
122996
|
+
timestamp: event.timestamp,
|
|
122997
|
+
event
|
|
122998
|
+
});
|
|
122999
|
+
break;
|
|
123000
|
+
case "stopRunByDelegate": {
|
|
123001
|
+
const delegates = event.checkpoint.delegateTo?.map((d) => d.expert.key).join(", ");
|
|
123002
|
+
activities.push({
|
|
123003
|
+
label: `Delegated to ${delegates ?? "unknown"}`,
|
|
123004
|
+
color: colors.warn,
|
|
123005
|
+
timestamp: event.timestamp,
|
|
123006
|
+
event
|
|
123007
|
+
});
|
|
123008
|
+
break;
|
|
123009
|
+
}
|
|
123010
|
+
case "retry":
|
|
123011
|
+
activities.push({
|
|
123012
|
+
label: `Retried: ${event.reason}`,
|
|
123013
|
+
color: colors.warn,
|
|
123014
|
+
timestamp: event.timestamp,
|
|
123015
|
+
event
|
|
123016
|
+
});
|
|
123017
|
+
break;
|
|
123018
|
+
case "resumeFromStop":
|
|
123019
|
+
activities.push({
|
|
123020
|
+
label: "Resumed from delegation",
|
|
123021
|
+
detail: `Step ${event.checkpoint.stepNumber}`,
|
|
123022
|
+
color: colors.accent,
|
|
123023
|
+
timestamp: event.timestamp,
|
|
123024
|
+
event
|
|
123025
|
+
});
|
|
123026
|
+
break;
|
|
123027
|
+
case "resolveToolResults":
|
|
123028
|
+
activities.push({
|
|
123029
|
+
label: `Resolved ${event.toolResults.length} tool result(s)`,
|
|
123030
|
+
color: colors.primary,
|
|
123031
|
+
timestamp: event.timestamp,
|
|
123032
|
+
event
|
|
123033
|
+
});
|
|
123034
|
+
break;
|
|
123035
|
+
default: break;
|
|
123036
|
+
}
|
|
123037
|
+
return activities;
|
|
123038
|
+
}
|
|
123039
|
+
//#endregion
|
|
123040
|
+
//#region ../../packages/tui-components/src/log-viewer/screens/run-detail.tsx
|
|
123041
|
+
const TAB_ORDER = [
|
|
123042
|
+
"activities",
|
|
123043
|
+
"checkpoints",
|
|
123044
|
+
"events"
|
|
123045
|
+
];
|
|
123046
|
+
const TAB_LABELS = {
|
|
123047
|
+
activities: "Activities",
|
|
123048
|
+
checkpoints: "Checkpoints",
|
|
123049
|
+
events: "Events"
|
|
123050
|
+
};
|
|
123051
|
+
const RESERVED_LINES$1 = 11;
|
|
123052
|
+
const LINES_PER_ITEM$1 = 2;
|
|
123053
|
+
function statusColor$1(status) {
|
|
123054
|
+
if (status === "completed") return colors.success;
|
|
123055
|
+
if (status === "proceeding" || status === "init") return colors.accent;
|
|
123056
|
+
return colors.destructive;
|
|
123057
|
+
}
|
|
123058
|
+
function getUsageIcon(ratio) {
|
|
123059
|
+
const percent = ratio * 100;
|
|
123060
|
+
if (percent >= 75) return USAGE_INDICATORS.FULL;
|
|
123061
|
+
if (percent >= 50) return USAGE_INDICATORS.HIGH;
|
|
123062
|
+
if (percent >= 25) return USAGE_INDICATORS.MEDIUM;
|
|
123063
|
+
if (percent >= 5) return USAGE_INDICATORS.LOW;
|
|
123064
|
+
return USAGE_INDICATORS.EMPTY;
|
|
123065
|
+
}
|
|
123066
|
+
function eventTypeColor(type) {
|
|
123067
|
+
switch (type) {
|
|
123068
|
+
case "startRun": return colors.accent;
|
|
123069
|
+
case "completeRun": return colors.success;
|
|
123070
|
+
case "stopRunByError": return colors.destructive;
|
|
123071
|
+
case "stopRunByDelegate":
|
|
123072
|
+
case "stopRunByInteractiveTool": return colors.warn;
|
|
123073
|
+
case "callTools": return "magenta";
|
|
123074
|
+
case "retry": return colors.destructive;
|
|
123075
|
+
default: return colors.muted;
|
|
123076
|
+
}
|
|
123077
|
+
}
|
|
123078
|
+
function formatTime(timestamp) {
|
|
123079
|
+
const d = new Date(timestamp);
|
|
123080
|
+
return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}:${String(d.getSeconds()).padStart(2, "0")}`;
|
|
123081
|
+
}
|
|
123082
|
+
function getEventSummary(event) {
|
|
123083
|
+
switch (event.type) {
|
|
123084
|
+
case "callTools": return `Tools: ${event.toolCalls.map((tc) => `${tc.skillName}/${tc.toolName}`).join(", ")}`;
|
|
123085
|
+
case "completeRun": return `Result: ${event.text}`;
|
|
123086
|
+
case "stopRunByError": return `${event.error.name}: ${event.error.message}`;
|
|
123087
|
+
case "retry": return `Reason: ${event.reason}`;
|
|
123088
|
+
case "startRun": return `Model: ${event.model}`;
|
|
123089
|
+
case "stopRunByDelegate": return event.checkpoint.delegateTo ? `Delegates: ${event.checkpoint.delegateTo.map((d) => d.expert.key).join(", ")}` : void 0;
|
|
123090
|
+
default: return;
|
|
123091
|
+
}
|
|
123092
|
+
}
|
|
123093
|
+
const RunDetailScreen = ({ run, checkpoints, events, initialTab, onSelectCheckpoint, onSelectEvent, onBack, onTabChange, onSelectedCheckpointChange, onSelectedEventChange }) => {
|
|
123094
|
+
const [activeTab, setActiveTab] = (0, import_react.useState)(initialTab);
|
|
123095
|
+
const maxVisible = computeVisibleItems(useTerminalHeight(), RESERVED_LINES$1, LINES_PER_ITEM$1);
|
|
123096
|
+
const activities = (0, import_react.useMemo)(() => buildActivities(events), [events]);
|
|
123097
|
+
const checkpointNav = useListNavigation({
|
|
123098
|
+
items: checkpoints,
|
|
123099
|
+
onSelect: onSelectCheckpoint,
|
|
123100
|
+
onBack
|
|
123101
|
+
});
|
|
123102
|
+
const eventNav = useListNavigation({
|
|
123103
|
+
items: events,
|
|
123104
|
+
onSelect: onSelectEvent,
|
|
123105
|
+
onBack
|
|
123106
|
+
});
|
|
123107
|
+
const activityNav = useListNavigation({
|
|
123108
|
+
items: activities,
|
|
123109
|
+
onSelect: (activity) => onSelectEvent(activity.event),
|
|
123110
|
+
onBack
|
|
123111
|
+
});
|
|
123112
|
+
const selectedCheckpoint = checkpoints[checkpointNav.selectedIndex];
|
|
123113
|
+
const selectedEvent = events[eventNav.selectedIndex];
|
|
123114
|
+
const selectedActivity = activities[activityNav.selectedIndex];
|
|
123115
|
+
(0, import_react.useEffect)(() => {
|
|
123116
|
+
if (activeTab === "checkpoints") {
|
|
123117
|
+
onSelectedCheckpointChange(selectedCheckpoint);
|
|
123118
|
+
onSelectedEventChange(void 0);
|
|
123119
|
+
} else if (activeTab === "events") {
|
|
123120
|
+
onSelectedEventChange(selectedEvent);
|
|
123121
|
+
onSelectedCheckpointChange(void 0);
|
|
123122
|
+
} else {
|
|
123123
|
+
onSelectedEventChange(selectedActivity?.event);
|
|
123124
|
+
onSelectedCheckpointChange(void 0);
|
|
123125
|
+
}
|
|
123126
|
+
}, [
|
|
123127
|
+
activeTab,
|
|
123128
|
+
selectedCheckpoint,
|
|
123129
|
+
selectedEvent,
|
|
123130
|
+
selectedActivity,
|
|
123131
|
+
onSelectedCheckpointChange,
|
|
123132
|
+
onSelectedEventChange
|
|
123133
|
+
]);
|
|
123134
|
+
const switchTab = (0, import_react.useCallback)((tab) => {
|
|
123135
|
+
setActiveTab(tab);
|
|
123136
|
+
onTabChange(tab);
|
|
123137
|
+
}, [onTabChange]);
|
|
123138
|
+
useInput((char, key) => {
|
|
123139
|
+
if (char === "1") {
|
|
123140
|
+
switchTab("activities");
|
|
123141
|
+
return;
|
|
123142
|
+
}
|
|
123143
|
+
if (char === "2") {
|
|
123144
|
+
switchTab("checkpoints");
|
|
123145
|
+
return;
|
|
123146
|
+
}
|
|
123147
|
+
if (char === "3") {
|
|
123148
|
+
switchTab("events");
|
|
123149
|
+
return;
|
|
123150
|
+
}
|
|
123151
|
+
if (key.tab) {
|
|
123152
|
+
const currentIndex = TAB_ORDER.indexOf(activeTab);
|
|
123153
|
+
switchTab(TAB_ORDER[key.shift ? (currentIndex - 1 + TAB_ORDER.length) % TAB_ORDER.length : (currentIndex + 1) % TAB_ORDER.length]);
|
|
123154
|
+
return;
|
|
123155
|
+
}
|
|
123156
|
+
if (activeTab === "checkpoints") checkpointNav.handleNavigation(char, key);
|
|
123157
|
+
else if (activeTab === "events") eventNav.handleNavigation(char, key);
|
|
123158
|
+
else activityNav.handleNavigation(char, key);
|
|
123159
|
+
});
|
|
123160
|
+
const shortRunId = run.runId.length > 12 ? `${run.runId.slice(0, 12)}...` : run.runId;
|
|
123161
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
123162
|
+
flexDirection: "column",
|
|
123163
|
+
flexGrow: 1,
|
|
123164
|
+
children: [
|
|
123165
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [
|
|
123166
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123167
|
+
color: colors.accent,
|
|
123168
|
+
bold: true,
|
|
123169
|
+
children: "Run"
|
|
123170
|
+
}),
|
|
123171
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
123172
|
+
color: colors.muted,
|
|
123173
|
+
children: [
|
|
123174
|
+
" (",
|
|
123175
|
+
shortRunId,
|
|
123176
|
+
")"
|
|
123177
|
+
]
|
|
123178
|
+
}),
|
|
123179
|
+
run.expertKey ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
123180
|
+
color: colors.muted,
|
|
123181
|
+
children: [" ", run.expertKey]
|
|
123182
|
+
}) : null
|
|
123183
|
+
] }),
|
|
123184
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [TAB_ORDER.map((tab, i) => {
|
|
123185
|
+
const isActive = tab === activeTab;
|
|
123186
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123187
|
+
color: colors.muted,
|
|
123188
|
+
children: " | "
|
|
123189
|
+
}) : null, /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
123190
|
+
color: isActive ? colors.accent : colors.muted,
|
|
123191
|
+
bold: isActive,
|
|
123192
|
+
children: [
|
|
123193
|
+
i + 1,
|
|
123194
|
+
":",
|
|
123195
|
+
TAB_LABELS[tab]
|
|
123196
|
+
]
|
|
123197
|
+
})] }, tab);
|
|
123198
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123199
|
+
color: colors.muted,
|
|
123200
|
+
children: " Tab:Switch Enter:Select b:Back q:Quit"
|
|
123201
|
+
})] }),
|
|
123202
|
+
activeTab === "activities" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ActivitiesContent, {
|
|
123203
|
+
activities,
|
|
123204
|
+
selectedIndex: activityNav.selectedIndex,
|
|
123205
|
+
maxVisible
|
|
123206
|
+
}),
|
|
123207
|
+
activeTab === "checkpoints" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CheckpointsContent, {
|
|
123208
|
+
checkpoints,
|
|
123209
|
+
selectedIndex: checkpointNav.selectedIndex,
|
|
123210
|
+
maxVisible
|
|
123211
|
+
}),
|
|
123212
|
+
activeTab === "events" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventsContent, {
|
|
123213
|
+
events,
|
|
123214
|
+
selectedIndex: eventNav.selectedIndex,
|
|
123215
|
+
maxVisible
|
|
123216
|
+
})
|
|
123217
|
+
]
|
|
123218
|
+
});
|
|
123219
|
+
};
|
|
123220
|
+
function ActivitiesContent({ activities, selectedIndex, maxVisible }) {
|
|
123221
|
+
const { scrollOffset, displayItems } = (0, import_react.useMemo)(() => {
|
|
123222
|
+
const offset = Math.max(0, Math.min(selectedIndex - maxVisible + 1, activities.length - maxVisible));
|
|
123223
|
+
return {
|
|
123224
|
+
scrollOffset: offset,
|
|
123225
|
+
displayItems: activities.slice(offset, offset + maxVisible)
|
|
123226
|
+
};
|
|
123227
|
+
}, [
|
|
123228
|
+
activities,
|
|
123229
|
+
selectedIndex,
|
|
123230
|
+
maxVisible
|
|
123231
|
+
]);
|
|
123232
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
123233
|
+
flexDirection: "column",
|
|
123234
|
+
flexGrow: 1,
|
|
123235
|
+
children: [
|
|
123236
|
+
activities.length > maxVisible && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
123237
|
+
color: colors.muted,
|
|
123238
|
+
children: [
|
|
123239
|
+
"[",
|
|
123240
|
+
selectedIndex + 1,
|
|
123241
|
+
"/",
|
|
123242
|
+
activities.length,
|
|
123243
|
+
"]"
|
|
123244
|
+
]
|
|
123245
|
+
}),
|
|
123246
|
+
scrollOffset > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123247
|
+
color: colors.muted,
|
|
123248
|
+
children: "..."
|
|
123249
|
+
}),
|
|
123250
|
+
displayItems.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123251
|
+
color: colors.muted,
|
|
123252
|
+
children: "No activities found"
|
|
123253
|
+
}) : displayItems.map((activity, i) => {
|
|
123254
|
+
const isSelected = scrollOffset + i === selectedIndex;
|
|
123255
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
123256
|
+
flexDirection: "column",
|
|
123257
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123258
|
+
color: isSelected ? colors.accent : colors.muted,
|
|
123259
|
+
children: isSelected ? " ▸ " : " "
|
|
123260
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
123261
|
+
wrap: "truncate",
|
|
123262
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123263
|
+
color: activity.color,
|
|
123264
|
+
bold: isSelected,
|
|
123265
|
+
children: activity.label
|
|
123266
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
123267
|
+
dimColor: true,
|
|
123268
|
+
children: [" ", formatTime(activity.timestamp)]
|
|
123269
|
+
})]
|
|
123270
|
+
})] }), activity.detail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
123271
|
+
paddingLeft: 3,
|
|
123272
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123273
|
+
dimColor: true,
|
|
123274
|
+
wrap: "truncate",
|
|
123275
|
+
children: activity.detail
|
|
123276
|
+
})
|
|
123277
|
+
}) : null]
|
|
123278
|
+
}, activity.event.id);
|
|
123279
|
+
}),
|
|
123280
|
+
scrollOffset + maxVisible < activities.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123281
|
+
color: colors.muted,
|
|
123282
|
+
children: "..."
|
|
123283
|
+
})
|
|
123284
|
+
]
|
|
123285
|
+
});
|
|
123286
|
+
}
|
|
123287
|
+
function CheckpointsContent({ checkpoints, selectedIndex, maxVisible }) {
|
|
123288
|
+
const { scrollOffset, displayItems } = (0, import_react.useMemo)(() => {
|
|
123289
|
+
const offset = Math.max(0, Math.min(selectedIndex - maxVisible + 1, checkpoints.length - maxVisible));
|
|
123290
|
+
return {
|
|
123291
|
+
scrollOffset: offset,
|
|
123292
|
+
displayItems: checkpoints.slice(offset, offset + maxVisible)
|
|
123293
|
+
};
|
|
123294
|
+
}, [
|
|
123295
|
+
checkpoints,
|
|
123296
|
+
selectedIndex,
|
|
123297
|
+
maxVisible
|
|
123298
|
+
]);
|
|
123299
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
123300
|
+
flexDirection: "column",
|
|
123301
|
+
flexGrow: 1,
|
|
123302
|
+
children: [
|
|
123303
|
+
checkpoints.length > maxVisible && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
123304
|
+
color: colors.muted,
|
|
123305
|
+
children: [
|
|
123306
|
+
"[",
|
|
123307
|
+
selectedIndex + 1,
|
|
123308
|
+
"/",
|
|
123309
|
+
checkpoints.length,
|
|
123310
|
+
"]"
|
|
123311
|
+
]
|
|
123312
|
+
}),
|
|
123313
|
+
scrollOffset > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123314
|
+
color: colors.muted,
|
|
123315
|
+
children: "..."
|
|
123316
|
+
}),
|
|
123317
|
+
displayItems.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123318
|
+
color: colors.muted,
|
|
123319
|
+
children: "No checkpoints found"
|
|
123320
|
+
}) : displayItems.map((cp, i) => {
|
|
123321
|
+
const isSelected = scrollOffset + i === selectedIndex;
|
|
123322
|
+
const hasUsage = cp.contextWindowUsage !== void 0;
|
|
123323
|
+
const hasDelegates = cp.delegateTo && cp.delegateTo.length > 0;
|
|
123324
|
+
const hasError = !!cp.error;
|
|
123325
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
123326
|
+
flexDirection: "column",
|
|
123327
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123328
|
+
color: isSelected ? colors.accent : colors.muted,
|
|
123329
|
+
children: isSelected ? " ▸ " : " "
|
|
123330
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
123331
|
+
wrap: "truncate",
|
|
123332
|
+
children: [
|
|
123333
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
123334
|
+
bold: true,
|
|
123335
|
+
children: ["Step ", cp.stepNumber]
|
|
123336
|
+
}),
|
|
123337
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123338
|
+
dimColor: true,
|
|
123339
|
+
children: " · "
|
|
123340
|
+
}),
|
|
123341
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123342
|
+
color: statusColor$1(cp.status),
|
|
123343
|
+
children: cp.status
|
|
123344
|
+
}),
|
|
123345
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123346
|
+
dimColor: true,
|
|
123347
|
+
children: " · "
|
|
123348
|
+
}),
|
|
123349
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: cp.expert.key }),
|
|
123350
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123351
|
+
dimColor: true,
|
|
123352
|
+
children: " · "
|
|
123353
|
+
}),
|
|
123354
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [cp.messages.length, " msgs"] }),
|
|
123355
|
+
hasUsage ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123356
|
+
dimColor: true,
|
|
123357
|
+
children: " · "
|
|
123358
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
|
|
123359
|
+
getUsageIcon(cp.contextWindowUsage),
|
|
123360
|
+
" ",
|
|
123361
|
+
(cp.contextWindowUsage * 100).toFixed(0),
|
|
123362
|
+
"%"
|
|
123363
|
+
] })] }) : null
|
|
123364
|
+
]
|
|
123365
|
+
})] }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
123366
|
+
paddingLeft: 3,
|
|
123367
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
123368
|
+
dimColor: true,
|
|
123369
|
+
wrap: "truncate",
|
|
123370
|
+
children: [
|
|
123371
|
+
"Tokens: In ",
|
|
123372
|
+
formatTokenCount(cp.usage.inputTokens),
|
|
123373
|
+
" · Out",
|
|
123374
|
+
" ",
|
|
123375
|
+
formatTokenCount(cp.usage.outputTokens),
|
|
123376
|
+
hasDelegates ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [" · Delegates: ", cp.delegateTo.map((d) => d.expert.key).join(", ")] }) : null,
|
|
123377
|
+
hasError ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
123378
|
+
color: colors.destructive,
|
|
123379
|
+
children: [" · ", cp.error.name]
|
|
123380
|
+
}) : null
|
|
123381
|
+
]
|
|
123382
|
+
})
|
|
123383
|
+
})]
|
|
123384
|
+
}, cp.id);
|
|
123385
|
+
}),
|
|
123386
|
+
scrollOffset + maxVisible < checkpoints.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123387
|
+
color: colors.muted,
|
|
123388
|
+
children: "..."
|
|
123389
|
+
})
|
|
123390
|
+
]
|
|
123391
|
+
});
|
|
123392
|
+
}
|
|
123393
|
+
function EventsContent({ events, selectedIndex, maxVisible }) {
|
|
123394
|
+
const { scrollOffset, displayItems } = (0, import_react.useMemo)(() => {
|
|
123395
|
+
const offset = Math.max(0, Math.min(selectedIndex - maxVisible + 1, events.length - maxVisible));
|
|
123396
|
+
return {
|
|
123397
|
+
scrollOffset: offset,
|
|
123398
|
+
displayItems: events.slice(offset, offset + maxVisible)
|
|
123399
|
+
};
|
|
123400
|
+
}, [
|
|
123401
|
+
events,
|
|
123402
|
+
selectedIndex,
|
|
123403
|
+
maxVisible
|
|
123404
|
+
]);
|
|
123405
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
123406
|
+
flexDirection: "column",
|
|
123407
|
+
flexGrow: 1,
|
|
123408
|
+
children: [
|
|
123409
|
+
events.length > maxVisible && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
123410
|
+
color: colors.muted,
|
|
123411
|
+
children: [
|
|
123412
|
+
"[",
|
|
123413
|
+
selectedIndex + 1,
|
|
123414
|
+
"/",
|
|
123415
|
+
events.length,
|
|
123416
|
+
"]"
|
|
123417
|
+
]
|
|
123418
|
+
}),
|
|
123419
|
+
scrollOffset > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123420
|
+
color: colors.muted,
|
|
123421
|
+
children: "..."
|
|
123422
|
+
}),
|
|
123423
|
+
displayItems.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123424
|
+
color: colors.muted,
|
|
123425
|
+
children: "No events found"
|
|
123426
|
+
}) : displayItems.map((ev, i) => {
|
|
123427
|
+
const isSelected = scrollOffset + i === selectedIndex;
|
|
123428
|
+
const summary = getEventSummary(ev);
|
|
123429
|
+
const hasUsage = "usage" in ev && ev.usage;
|
|
123430
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
123431
|
+
flexDirection: "column",
|
|
123432
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123433
|
+
color: isSelected ? colors.accent : colors.muted,
|
|
123434
|
+
children: isSelected ? " ▸ " : " "
|
|
123435
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
123436
|
+
wrap: "truncate",
|
|
123437
|
+
children: [
|
|
123438
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
123439
|
+
bold: true,
|
|
123440
|
+
children: ["Step ", ev.stepNumber]
|
|
123441
|
+
}),
|
|
123442
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123443
|
+
dimColor: true,
|
|
123444
|
+
children: " · "
|
|
123445
|
+
}),
|
|
123446
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123447
|
+
color: eventTypeColor(ev.type),
|
|
123448
|
+
children: ev.type
|
|
123449
|
+
}),
|
|
123450
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123451
|
+
dimColor: true,
|
|
123452
|
+
children: " · "
|
|
123453
|
+
}),
|
|
123454
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: ev.expertKey }),
|
|
123455
|
+
hasUsage ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123456
|
+
dimColor: true,
|
|
123457
|
+
children: " · "
|
|
123458
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
123459
|
+
dimColor: true,
|
|
123460
|
+
children: [
|
|
123461
|
+
"In",
|
|
123462
|
+
" ",
|
|
123463
|
+
formatTokenCount(ev.usage.inputTokens),
|
|
123464
|
+
" ",
|
|
123465
|
+
"· Out",
|
|
123466
|
+
" ",
|
|
123467
|
+
formatTokenCount(ev.usage.outputTokens)
|
|
123468
|
+
]
|
|
123469
|
+
})] }) : null,
|
|
123470
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123471
|
+
dimColor: true,
|
|
123472
|
+
children: " "
|
|
123473
|
+
}),
|
|
123474
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123475
|
+
dimColor: true,
|
|
123476
|
+
children: formatTime(ev.timestamp)
|
|
123477
|
+
})
|
|
123478
|
+
]
|
|
123479
|
+
})] }), summary ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box, {
|
|
123480
|
+
paddingLeft: 3,
|
|
123481
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123482
|
+
dimColor: true,
|
|
123483
|
+
wrap: "truncate",
|
|
123484
|
+
children: summary
|
|
123485
|
+
})
|
|
123486
|
+
}) : null]
|
|
123487
|
+
}, ev.id);
|
|
123488
|
+
}),
|
|
123489
|
+
scrollOffset + maxVisible < events.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123490
|
+
color: colors.muted,
|
|
123491
|
+
children: "..."
|
|
123492
|
+
})
|
|
123493
|
+
]
|
|
123494
|
+
});
|
|
123495
|
+
}
|
|
123496
|
+
//#endregion
|
|
123497
|
+
//#region ../../packages/tui-components/src/log-viewer/build-graph-lines.ts
|
|
123498
|
+
/**
|
|
123499
|
+
* Build git-graph style lines from a DelegationTreeState.
|
|
123500
|
+
*
|
|
123501
|
+
* Uses delegation groups to distinguish sequential vs parallel delegations.
|
|
123502
|
+
* Uses resumeNodes to show resume runs as separate nodes after merge points.
|
|
123503
|
+
* Each column/lane gets a stable `colorIndex` so the renderer can color
|
|
123504
|
+
* parent and child lanes differently.
|
|
123505
|
+
*/
|
|
123506
|
+
function buildGraphLines(treeState, runQueries, delegationGroups, resumeNodes) {
|
|
123507
|
+
if (!treeState.rootRunId) return [];
|
|
123508
|
+
const root = treeState.nodes.get(treeState.rootRunId);
|
|
123509
|
+
if (!root) return [];
|
|
123510
|
+
const resumeSet = resumeNodes ? new Set(resumeNodes.keys()) : /* @__PURE__ */ new Set();
|
|
123511
|
+
const lines = [];
|
|
123512
|
+
let nextCol = 0;
|
|
123513
|
+
const freePool = [];
|
|
123514
|
+
function allocCol() {
|
|
123515
|
+
if (freePool.length > 0) {
|
|
123516
|
+
freePool.sort((a, b) => a - b);
|
|
123517
|
+
return freePool.shift();
|
|
123518
|
+
}
|
|
123519
|
+
return nextCol++;
|
|
123520
|
+
}
|
|
123521
|
+
function freeCol(col) {
|
|
123522
|
+
freePool.push(col);
|
|
123523
|
+
}
|
|
123524
|
+
const activeCols = /* @__PURE__ */ new Set();
|
|
123525
|
+
const colColors = /* @__PURE__ */ new Map();
|
|
123526
|
+
let nextColorIndex = 0;
|
|
123527
|
+
function assignColor(col) {
|
|
123528
|
+
const ci = nextColorIndex++;
|
|
123529
|
+
colColors.set(col, ci);
|
|
123530
|
+
return ci;
|
|
123531
|
+
}
|
|
123532
|
+
function getColor(col) {
|
|
123533
|
+
return colColors.get(col) ?? -1;
|
|
123534
|
+
}
|
|
123535
|
+
/** Get the max column index we need to render. */
|
|
123536
|
+
function maxCol(extraCols) {
|
|
123537
|
+
let max = -1;
|
|
123538
|
+
for (const c of activeCols) if (c > max) max = c;
|
|
123539
|
+
if (extraCols) {
|
|
123540
|
+
for (const c of extraCols) if (c > max) max = c;
|
|
123541
|
+
}
|
|
123542
|
+
return max;
|
|
123543
|
+
}
|
|
123544
|
+
/** Render a line given active columns, with optional special markers. */
|
|
123545
|
+
function render(markers) {
|
|
123546
|
+
const mc = maxCol(markers?.keys());
|
|
123547
|
+
if (mc < 0) return {
|
|
123548
|
+
graph: "",
|
|
123549
|
+
segments: []
|
|
123550
|
+
};
|
|
123551
|
+
let graph = "";
|
|
123552
|
+
const segments = [];
|
|
123553
|
+
for (let c = 0; c <= mc; c++) {
|
|
123554
|
+
const marker = markers?.get(c);
|
|
123555
|
+
if (marker) {
|
|
123556
|
+
const text = `${marker} `;
|
|
123557
|
+
graph += text;
|
|
123558
|
+
segments.push({
|
|
123559
|
+
text,
|
|
123560
|
+
colorIndex: getColor(c)
|
|
123561
|
+
});
|
|
123562
|
+
} else if (activeCols.has(c)) {
|
|
123563
|
+
const text = "| ";
|
|
123564
|
+
graph += text;
|
|
123565
|
+
segments.push({
|
|
123566
|
+
text,
|
|
123567
|
+
colorIndex: getColor(c)
|
|
123568
|
+
});
|
|
123569
|
+
} else {
|
|
123570
|
+
const text = " ";
|
|
123571
|
+
graph += text;
|
|
123572
|
+
segments.push({
|
|
123573
|
+
text,
|
|
123574
|
+
colorIndex: -1
|
|
123575
|
+
});
|
|
123576
|
+
}
|
|
123577
|
+
}
|
|
123578
|
+
return {
|
|
123579
|
+
graph,
|
|
123580
|
+
segments
|
|
123581
|
+
};
|
|
123582
|
+
}
|
|
123583
|
+
/**
|
|
123584
|
+
* Split a node's children into:
|
|
123585
|
+
* - delegateGroups: groups of children delegated together (from delegationGroups)
|
|
123586
|
+
* - resumeChildren: children that are resume runs (process in order after delegate groups)
|
|
123587
|
+
* - otherChildren: children that are neither delegated nor resume (ungrouped)
|
|
123588
|
+
*/
|
|
123589
|
+
const resumeChildrenOf = /* @__PURE__ */ new Map();
|
|
123590
|
+
if (resumeNodes) for (const [resumeId, originalId] of resumeNodes) {
|
|
123591
|
+
const list = resumeChildrenOf.get(originalId) ?? [];
|
|
123592
|
+
list.push(resumeId);
|
|
123593
|
+
resumeChildrenOf.set(originalId, list);
|
|
123594
|
+
}
|
|
123595
|
+
function splitChildren(node) {
|
|
123596
|
+
const allGrouped = /* @__PURE__ */ new Set();
|
|
123597
|
+
const delegateGroups = [];
|
|
123598
|
+
if (delegationGroups) {
|
|
123599
|
+
const groups = delegationGroups.get(node.runId);
|
|
123600
|
+
if (groups) for (const group of groups) {
|
|
123601
|
+
delegateGroups.push(group);
|
|
123602
|
+
for (const id of group) allGrouped.add(id);
|
|
123603
|
+
}
|
|
123604
|
+
}
|
|
123605
|
+
const resumeChildren = resumeChildrenOf.get(node.runId) ?? [];
|
|
123606
|
+
const allResumeIds = new Set(resumeSet);
|
|
123607
|
+
const otherChildren = [];
|
|
123608
|
+
for (const childId of node.childRunIds) {
|
|
123609
|
+
if (allGrouped.has(childId)) continue;
|
|
123610
|
+
if (allResumeIds.has(childId)) continue;
|
|
123611
|
+
otherChildren.push(childId);
|
|
123612
|
+
}
|
|
123613
|
+
if (delegateGroups.length === 0 && otherChildren.length > 0) {
|
|
123614
|
+
delegateGroups.push(otherChildren);
|
|
123615
|
+
return {
|
|
123616
|
+
delegateGroups,
|
|
123617
|
+
resumeChildren,
|
|
123618
|
+
otherChildren: []
|
|
123619
|
+
};
|
|
123620
|
+
}
|
|
123621
|
+
return {
|
|
123622
|
+
delegateGroups,
|
|
123623
|
+
resumeChildren,
|
|
123624
|
+
otherChildren
|
|
123625
|
+
};
|
|
123626
|
+
}
|
|
123627
|
+
/**
|
|
123628
|
+
* Emit lines for a node on a shared lane without freeing the column afterward.
|
|
123629
|
+
* Used when multiple children share a single lane (commit-chain style).
|
|
123630
|
+
*/
|
|
123631
|
+
function emitSubtreeInline(node, col) {
|
|
123632
|
+
activeCols.add(col);
|
|
123633
|
+
const isResume = resumeSet.has(node.runId);
|
|
123634
|
+
const { graph, segments } = render(new Map([[col, "*"]]));
|
|
123635
|
+
lines.push({
|
|
123636
|
+
kind: "node",
|
|
123637
|
+
node,
|
|
123638
|
+
graph,
|
|
123639
|
+
graphSegments: segments,
|
|
123640
|
+
query: runQueries.get(node.runId),
|
|
123641
|
+
isResume
|
|
123642
|
+
});
|
|
123643
|
+
const { delegateGroups, resumeChildren, otherChildren } = splitChildren(node);
|
|
123644
|
+
if (!(delegateGroups.some((g) => g.length > 0) || resumeChildren.length > 0 || otherChildren.length > 0)) return;
|
|
123645
|
+
emitChildGroups(node, col, delegateGroups, resumeChildren, otherChildren);
|
|
123646
|
+
}
|
|
123647
|
+
/**
|
|
123648
|
+
* Shared logic for processing delegate groups, resume children, and other children.
|
|
123649
|
+
*/
|
|
123650
|
+
function emitChildGroups(_node, col, delegateGroups, resumeChildren, otherChildren) {
|
|
123651
|
+
for (const group of delegateGroups) {
|
|
123652
|
+
const children = group.map((id) => treeState.nodes.get(id)).filter((n) => n !== void 0);
|
|
123653
|
+
if (children.length === 0) continue;
|
|
123654
|
+
const cc = allocCol();
|
|
123655
|
+
assignColor(cc);
|
|
123656
|
+
activeCols.add(cc);
|
|
123657
|
+
const forkMarkers = /* @__PURE__ */ new Map();
|
|
123658
|
+
forkMarkers.set(cc, "\\");
|
|
123659
|
+
const fork = render(forkMarkers);
|
|
123660
|
+
lines.push({
|
|
123661
|
+
kind: "connector",
|
|
123662
|
+
graph: fork.graph,
|
|
123663
|
+
graphSegments: fork.segments
|
|
123664
|
+
});
|
|
123665
|
+
for (const child of children) emitSubtreeInline(child, cc);
|
|
123666
|
+
activeCols.add(cc);
|
|
123667
|
+
const mergeMarkers = /* @__PURE__ */ new Map();
|
|
123668
|
+
mergeMarkers.set(cc, "/");
|
|
123669
|
+
const merge = render(mergeMarkers);
|
|
123670
|
+
lines.push({
|
|
123671
|
+
kind: "connector",
|
|
123672
|
+
graph: merge.graph,
|
|
123673
|
+
graphSegments: merge.segments
|
|
123674
|
+
});
|
|
123675
|
+
activeCols.delete(cc);
|
|
123676
|
+
if (!freePool.includes(cc)) freeCol(cc);
|
|
123677
|
+
}
|
|
123678
|
+
for (const resumeId of resumeChildren) {
|
|
123679
|
+
const resumeNode = treeState.nodes.get(resumeId);
|
|
123680
|
+
if (!resumeNode) continue;
|
|
123681
|
+
emitSubtreeInline(resumeNode, col);
|
|
123682
|
+
}
|
|
123683
|
+
if (otherChildren.length > 0) {
|
|
123684
|
+
const children = otherChildren.map((id) => treeState.nodes.get(id)).filter((n) => n !== void 0);
|
|
123685
|
+
const cc = allocCol();
|
|
123686
|
+
assignColor(cc);
|
|
123687
|
+
activeCols.add(cc);
|
|
123688
|
+
const forkMarkers = /* @__PURE__ */ new Map();
|
|
123689
|
+
forkMarkers.set(cc, "\\");
|
|
123690
|
+
const fork = render(forkMarkers);
|
|
123691
|
+
lines.push({
|
|
123692
|
+
kind: "connector",
|
|
123693
|
+
graph: fork.graph,
|
|
123694
|
+
graphSegments: fork.segments
|
|
123695
|
+
});
|
|
123696
|
+
for (const child of children) emitSubtreeInline(child, cc);
|
|
123697
|
+
activeCols.add(cc);
|
|
123698
|
+
const mergeMarkers = /* @__PURE__ */ new Map();
|
|
123699
|
+
mergeMarkers.set(cc, "/");
|
|
123700
|
+
const merge = render(mergeMarkers);
|
|
123701
|
+
lines.push({
|
|
123702
|
+
kind: "connector",
|
|
123703
|
+
graph: merge.graph,
|
|
123704
|
+
graphSegments: merge.segments
|
|
123705
|
+
});
|
|
123706
|
+
activeCols.delete(cc);
|
|
123707
|
+
if (!freePool.includes(cc)) freeCol(cc);
|
|
123708
|
+
}
|
|
123709
|
+
}
|
|
123710
|
+
/**
|
|
123711
|
+
* Recursively emit lines for a subtree (top-level entry point that frees column on exit).
|
|
123712
|
+
*/
|
|
123713
|
+
function emitSubtree(node, col) {
|
|
123714
|
+
emitSubtreeInline(node, col);
|
|
123715
|
+
activeCols.delete(col);
|
|
123716
|
+
if (!freePool.includes(col)) freeCol(col);
|
|
123717
|
+
}
|
|
123718
|
+
const rootCol = allocCol();
|
|
123719
|
+
assignColor(rootCol);
|
|
123720
|
+
emitSubtree(root, rootCol);
|
|
123721
|
+
for (const [nodeId, node] of treeState.nodes) if (!lines.some((l) => l.kind === "node" && l.node.runId === nodeId)) lines.push({
|
|
123722
|
+
kind: "node",
|
|
123723
|
+
node,
|
|
123724
|
+
graph: "* ",
|
|
123725
|
+
graphSegments: [{
|
|
123726
|
+
text: "* ",
|
|
123727
|
+
colorIndex: -1
|
|
123728
|
+
}],
|
|
123729
|
+
query: runQueries.get(nodeId),
|
|
123730
|
+
isResume: resumeSet.has(nodeId)
|
|
123731
|
+
});
|
|
123732
|
+
return lines;
|
|
123733
|
+
}
|
|
123734
|
+
//#endregion
|
|
123735
|
+
//#region ../../packages/tui-components/src/log-viewer/screens/run-list.tsx
|
|
123736
|
+
const RESERVED_LINES = 9;
|
|
123737
|
+
const LINES_PER_ITEM = 1;
|
|
123738
|
+
/**
|
|
123739
|
+
* Color palette for graph lanes. Each lane gets a color by its colorIndex mod palette length.
|
|
123740
|
+
* These are chosen to be distinct and readable on both dark and light terminals.
|
|
123741
|
+
*/
|
|
123742
|
+
const LANE_COLORS = [
|
|
123743
|
+
"cyan",
|
|
123744
|
+
"magenta",
|
|
123745
|
+
"yellow",
|
|
123746
|
+
"blue",
|
|
123747
|
+
"green",
|
|
123748
|
+
"red",
|
|
123749
|
+
"white"
|
|
123750
|
+
];
|
|
123751
|
+
function laneColor(colorIndex) {
|
|
123752
|
+
if (colorIndex < 0) return colors.muted;
|
|
123753
|
+
return LANE_COLORS[colorIndex % LANE_COLORS.length];
|
|
123754
|
+
}
|
|
123755
|
+
function statusIcon(status) {
|
|
123756
|
+
switch (status) {
|
|
123757
|
+
case "completed": return "✓";
|
|
123758
|
+
case "error": return "✗";
|
|
123759
|
+
case "running": return "○";
|
|
123760
|
+
case "suspending": return "⎇";
|
|
123761
|
+
default: return " ";
|
|
123762
|
+
}
|
|
123763
|
+
}
|
|
123764
|
+
function statusColor(status) {
|
|
123765
|
+
switch (status) {
|
|
123766
|
+
case "completed": return colors.primary;
|
|
123767
|
+
case "error": return colors.destructive;
|
|
123768
|
+
case "suspending": return colors.primary;
|
|
123769
|
+
default: return colors.muted;
|
|
123770
|
+
}
|
|
123771
|
+
}
|
|
123772
|
+
/** Render graph segments as colored <Text> elements. */
|
|
123773
|
+
function GraphPrefix({ segments, nodeStatus }) {
|
|
123774
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: segments.map((seg, i) => {
|
|
123775
|
+
if (seg.text.startsWith("*") && nodeStatus) {
|
|
123776
|
+
const icon = statusIcon(nodeStatus);
|
|
123777
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
123778
|
+
color: statusColor(nodeStatus),
|
|
123779
|
+
children: [icon, seg.text.slice(1)]
|
|
123780
|
+
}, i);
|
|
123781
|
+
}
|
|
123782
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123783
|
+
color: laneColor(seg.colorIndex),
|
|
123784
|
+
children: seg.text
|
|
123785
|
+
}, i);
|
|
123786
|
+
}) });
|
|
123787
|
+
}
|
|
123788
|
+
const RunListScreen = ({ job, treeState, runQueries, delegationGroups, resumeNodes, onSelectRun, onBack, onSelectedChange }) => {
|
|
123789
|
+
const maxVisible = computeVisibleItems(useTerminalHeight(), RESERVED_LINES, LINES_PER_ITEM);
|
|
123790
|
+
const graphLines = (0, import_react.useMemo)(() => buildGraphLines(treeState, runQueries, delegationGroups, resumeNodes), [
|
|
123791
|
+
treeState,
|
|
123792
|
+
runQueries,
|
|
123793
|
+
delegationGroups,
|
|
123794
|
+
resumeNodes
|
|
123795
|
+
]);
|
|
123796
|
+
const selectableIndices = (0, import_react.useMemo)(() => graphLines.map((line, idx) => line.kind === "node" ? idx : -1).filter((idx) => idx >= 0), [graphLines]);
|
|
123797
|
+
const [selectedPos, setSelectedPos] = (0, import_react.useState)(0);
|
|
123798
|
+
const selectedLineIndex = selectableIndices[selectedPos];
|
|
123799
|
+
const selectedLine = selectedLineIndex !== void 0 ? graphLines[selectedLineIndex] : void 0;
|
|
123800
|
+
const selectedNode = selectedLine?.kind === "node" ? selectedLine.node : void 0;
|
|
123801
|
+
(0, import_react.useEffect)(() => {
|
|
123802
|
+
onSelectedChange(selectedNode);
|
|
123803
|
+
}, [selectedNode, onSelectedChange]);
|
|
123804
|
+
const handleSelect = (0, import_react.useCallback)(() => {
|
|
123805
|
+
if (selectedNode) onSelectRun(selectedNode);
|
|
123806
|
+
}, [selectedNode, onSelectRun]);
|
|
123807
|
+
useInput((char, key) => {
|
|
123808
|
+
if (key.upArrow && selectableIndices.length > 0) {
|
|
123809
|
+
setSelectedPos((prev) => Math.max(0, prev - 1));
|
|
123810
|
+
return;
|
|
123811
|
+
}
|
|
123812
|
+
if (key.downArrow && selectableIndices.length > 0) {
|
|
123813
|
+
setSelectedPos((prev) => Math.min(selectableIndices.length - 1, prev + 1));
|
|
123814
|
+
return;
|
|
123815
|
+
}
|
|
123816
|
+
if (key.return) {
|
|
123817
|
+
handleSelect();
|
|
123818
|
+
return;
|
|
123819
|
+
}
|
|
123820
|
+
if (key.escape || char === "b") onBack();
|
|
123821
|
+
});
|
|
123822
|
+
const { scrollOffset, displayLines } = (0, import_react.useMemo)(() => {
|
|
123823
|
+
if (graphLines.length <= maxVisible) return {
|
|
123824
|
+
scrollOffset: 0,
|
|
123825
|
+
displayLines: graphLines
|
|
123826
|
+
};
|
|
123827
|
+
const rawOffset = (selectedLineIndex ?? 0) - Math.floor(maxVisible / 2);
|
|
123828
|
+
const offset = Math.max(0, Math.min(rawOffset, graphLines.length - maxVisible));
|
|
123829
|
+
return {
|
|
123830
|
+
scrollOffset: offset,
|
|
123831
|
+
displayLines: graphLines.slice(offset, offset + maxVisible)
|
|
123832
|
+
};
|
|
123833
|
+
}, [
|
|
123834
|
+
graphLines,
|
|
123835
|
+
selectedLineIndex,
|
|
123836
|
+
maxVisible
|
|
123837
|
+
]);
|
|
123838
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
123839
|
+
flexDirection: "column",
|
|
123840
|
+
flexGrow: 1,
|
|
123841
|
+
children: [
|
|
123842
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, { children: [
|
|
123843
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123844
|
+
color: colors.accent,
|
|
123845
|
+
bold: true,
|
|
123846
|
+
children: "Runs"
|
|
123847
|
+
}),
|
|
123848
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
123849
|
+
color: colors.muted,
|
|
123850
|
+
children: [
|
|
123851
|
+
" (",
|
|
123852
|
+
job.coordinatorExpertKey,
|
|
123853
|
+
")"
|
|
123854
|
+
]
|
|
123855
|
+
}),
|
|
123856
|
+
selectableIndices.length > maxVisible && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
123857
|
+
color: colors.muted,
|
|
123858
|
+
children: [
|
|
123859
|
+
" ",
|
|
123860
|
+
"[",
|
|
123861
|
+
selectedPos + 1,
|
|
123862
|
+
"/",
|
|
123863
|
+
selectableIndices.length,
|
|
123864
|
+
"]"
|
|
123865
|
+
]
|
|
123866
|
+
}),
|
|
123867
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123868
|
+
color: colors.muted,
|
|
123869
|
+
children: " Enter:Select b:Back q:Quit"
|
|
123870
|
+
})
|
|
123871
|
+
] }),
|
|
123872
|
+
scrollOffset > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123873
|
+
color: colors.muted,
|
|
123874
|
+
children: "..."
|
|
123875
|
+
}),
|
|
123876
|
+
displayLines.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123877
|
+
color: colors.muted,
|
|
123878
|
+
children: "No runs found"
|
|
123879
|
+
}) : displayLines.map((line, i) => {
|
|
123880
|
+
const actualIndex = scrollOffset + i;
|
|
123881
|
+
if (line.kind === "connector") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
123882
|
+
wrap: "truncate",
|
|
123883
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: " " }), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GraphPrefix, { segments: line.graphSegments })]
|
|
123884
|
+
}, `c-${actualIndex}`);
|
|
123885
|
+
const isSelected = actualIndex === selectedLineIndex;
|
|
123886
|
+
const nameColor = statusColor(line.node.status);
|
|
123887
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
123888
|
+
wrap: "truncate",
|
|
123889
|
+
children: [
|
|
123890
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123891
|
+
color: isSelected ? colors.accent : colors.muted,
|
|
123892
|
+
children: isSelected ? " > " : " "
|
|
123893
|
+
}),
|
|
123894
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(GraphPrefix, {
|
|
123895
|
+
segments: line.graphSegments,
|
|
123896
|
+
nodeStatus: line.node.status
|
|
123897
|
+
}),
|
|
123898
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123899
|
+
bold: true,
|
|
123900
|
+
color: nameColor,
|
|
123901
|
+
children: line.node.expertName
|
|
123902
|
+
}),
|
|
123903
|
+
line.isResume ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123904
|
+
color: colors.muted,
|
|
123905
|
+
children: " (resumed)"
|
|
123906
|
+
}) : null,
|
|
123907
|
+
line.query ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
123908
|
+
dimColor: true,
|
|
123909
|
+
children: [" ", line.query]
|
|
123910
|
+
}) : null
|
|
123911
|
+
]
|
|
123912
|
+
}, line.node.runId);
|
|
123913
|
+
}),
|
|
123914
|
+
scrollOffset + maxVisible < graphLines.length && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
123915
|
+
color: colors.muted,
|
|
123916
|
+
children: "..."
|
|
123917
|
+
})
|
|
123918
|
+
]
|
|
123919
|
+
});
|
|
123920
|
+
};
|
|
123921
|
+
//#endregion
|
|
123922
|
+
//#region ../../packages/tui-components/src/log-viewer/app.tsx
|
|
123923
|
+
async function extractJobQuery(fetcher, job) {
|
|
123924
|
+
try {
|
|
123925
|
+
const runs = await fetcher.getRuns(job.id);
|
|
123926
|
+
if (runs.length === 0) return void 0;
|
|
123927
|
+
const firstRun = runs[0];
|
|
123928
|
+
const startRunEvent = await fetcher.getFirstEventForRun(job.id, firstRun.runId, "startRun");
|
|
123929
|
+
if (!startRunEvent) return void 0;
|
|
123930
|
+
return extractQueryFromStartRun(startRunEvent);
|
|
123931
|
+
} catch {
|
|
123932
|
+
return;
|
|
123933
|
+
}
|
|
123934
|
+
}
|
|
123935
|
+
async function buildRunTree(fetcher, jobId) {
|
|
123936
|
+
return buildRunTreeFromEvents(await fetcher.getTreeEventsForJob(jobId));
|
|
123937
|
+
}
|
|
123938
|
+
const LogViewerApp = ({ fetcher, initialJobId, initialRunId }) => {
|
|
123939
|
+
const { exit } = useApp();
|
|
123940
|
+
const [screen, setScreen] = (0, import_react.useState)({ type: "jobList" });
|
|
123941
|
+
const [jobItems, setJobItems] = (0, import_react.useState)([]);
|
|
123942
|
+
const [loading, setLoading] = (0, import_react.useState)(true);
|
|
123943
|
+
const [error, setError] = (0, import_react.useState)();
|
|
123944
|
+
const [selectedJob, setSelectedJob] = (0, import_react.useState)();
|
|
123945
|
+
const [selectedRun, setSelectedRun] = (0, import_react.useState)();
|
|
123946
|
+
const [selectedTreeNode, setSelectedTreeNode] = (0, import_react.useState)();
|
|
123947
|
+
const [selectedCheckpoint, setSelectedCheckpoint] = (0, import_react.useState)();
|
|
123948
|
+
const [selectedEvent, setSelectedEvent] = (0, import_react.useState)();
|
|
123949
|
+
useInput((char) => {
|
|
123950
|
+
if (char === "q") exit();
|
|
123951
|
+
});
|
|
123952
|
+
(0, import_react.useEffect)(() => {
|
|
123953
|
+
const loadInitial = async () => {
|
|
123954
|
+
try {
|
|
123955
|
+
setLoading(true);
|
|
123956
|
+
if (initialJobId && initialRunId) {
|
|
123957
|
+
const job = await fetcher.getJob(initialJobId);
|
|
123958
|
+
if (!job) {
|
|
123959
|
+
setError(`Job ${initialJobId} not found`);
|
|
123960
|
+
setLoading(false);
|
|
123961
|
+
return;
|
|
123962
|
+
}
|
|
123963
|
+
const [runCheckpoints, runEvents] = await Promise.all([fetcher.getCheckpointsForRun(initialJobId, initialRunId), fetcher.getEvents(initialJobId, initialRunId)]);
|
|
123964
|
+
setScreen({
|
|
123965
|
+
type: "runDetail",
|
|
123966
|
+
job,
|
|
123967
|
+
run: {
|
|
123968
|
+
jobId: initialJobId,
|
|
123969
|
+
runId: initialRunId
|
|
123970
|
+
},
|
|
123971
|
+
checkpoints: runCheckpoints,
|
|
123972
|
+
events: runEvents,
|
|
123973
|
+
tab: "activities"
|
|
123974
|
+
});
|
|
123975
|
+
setLoading(false);
|
|
123976
|
+
return;
|
|
123977
|
+
}
|
|
123978
|
+
if (initialJobId) {
|
|
123979
|
+
const job = await fetcher.getJob(initialJobId);
|
|
123980
|
+
if (!job) {
|
|
123981
|
+
setError(`Job ${initialJobId} not found`);
|
|
123982
|
+
setLoading(false);
|
|
123983
|
+
return;
|
|
123984
|
+
}
|
|
123985
|
+
const { treeState, runQueries, runStats, delegationGroups, resumeNodes } = await buildRunTree(fetcher, initialJobId);
|
|
123986
|
+
setScreen({
|
|
123987
|
+
type: "runList",
|
|
123988
|
+
job,
|
|
123989
|
+
treeState,
|
|
123990
|
+
runQueries,
|
|
123991
|
+
runStats,
|
|
123992
|
+
delegationGroups,
|
|
123993
|
+
resumeNodes
|
|
123994
|
+
});
|
|
123995
|
+
setLoading(false);
|
|
123996
|
+
return;
|
|
123997
|
+
}
|
|
123998
|
+
const latest = await fetcher.getLatestJob();
|
|
123999
|
+
if (latest) setJobItems([{
|
|
124000
|
+
job: latest,
|
|
124001
|
+
query: await extractJobQuery(fetcher, latest)
|
|
124002
|
+
}]);
|
|
124003
|
+
else setJobItems([]);
|
|
124004
|
+
setLoading(false);
|
|
124005
|
+
} catch (err) {
|
|
124006
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
124007
|
+
setLoading(false);
|
|
124008
|
+
}
|
|
124009
|
+
};
|
|
124010
|
+
loadInitial();
|
|
124011
|
+
}, [
|
|
124012
|
+
fetcher,
|
|
124013
|
+
initialJobId,
|
|
124014
|
+
initialRunId
|
|
124015
|
+
]);
|
|
124016
|
+
const handleChatSubmit = (0, import_react.useCallback)((_query) => {}, []);
|
|
124017
|
+
const navigateToJobDetail = (0, import_react.useCallback)((job) => {
|
|
124018
|
+
setScreen({
|
|
124019
|
+
type: "jobDetail",
|
|
124020
|
+
job
|
|
124021
|
+
});
|
|
124022
|
+
}, []);
|
|
124023
|
+
const navigateToRunList = (0, import_react.useCallback)(async (job) => {
|
|
124024
|
+
setLoading(true);
|
|
124025
|
+
try {
|
|
124026
|
+
const { treeState, runQueries, runStats, delegationGroups, resumeNodes } = await buildRunTree(fetcher, job.id);
|
|
124027
|
+
setScreen({
|
|
124028
|
+
type: "runList",
|
|
124029
|
+
job,
|
|
124030
|
+
treeState,
|
|
124031
|
+
runQueries,
|
|
124032
|
+
runStats,
|
|
124033
|
+
delegationGroups,
|
|
124034
|
+
resumeNodes
|
|
124035
|
+
});
|
|
124036
|
+
} catch (err) {
|
|
124037
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
124038
|
+
}
|
|
124039
|
+
setLoading(false);
|
|
124040
|
+
}, [fetcher]);
|
|
124041
|
+
const navigateToRunDetail = (0, import_react.useCallback)(async (job, run, tab = "activities") => {
|
|
124042
|
+
setLoading(true);
|
|
124043
|
+
try {
|
|
124044
|
+
const [runCheckpoints, runEvents] = await Promise.all([fetcher.getCheckpointsForRun(job.id, run.runId), fetcher.getEvents(job.id, run.runId)]);
|
|
124045
|
+
setScreen({
|
|
124046
|
+
type: "runDetail",
|
|
124047
|
+
job,
|
|
124048
|
+
run,
|
|
124049
|
+
checkpoints: runCheckpoints,
|
|
124050
|
+
events: runEvents,
|
|
124051
|
+
tab
|
|
124052
|
+
});
|
|
124053
|
+
} catch (err) {
|
|
124054
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
124055
|
+
}
|
|
124056
|
+
setLoading(false);
|
|
124057
|
+
}, [fetcher]);
|
|
124058
|
+
const navigateToCheckpointDetail = (0, import_react.useCallback)((job, run, checkpoint) => {
|
|
124059
|
+
setScreen({
|
|
124060
|
+
type: "checkpointDetail",
|
|
124061
|
+
job,
|
|
124062
|
+
run,
|
|
124063
|
+
checkpoint
|
|
124064
|
+
});
|
|
124065
|
+
}, []);
|
|
124066
|
+
const navigateToEventDetail = (0, import_react.useCallback)((job, run, event) => {
|
|
124067
|
+
setScreen({
|
|
124068
|
+
type: "eventDetail",
|
|
124069
|
+
job,
|
|
124070
|
+
run,
|
|
124071
|
+
event
|
|
124072
|
+
});
|
|
124073
|
+
}, []);
|
|
124074
|
+
const screenRef = (0, import_react.useRef)(screen);
|
|
124075
|
+
screenRef.current = screen;
|
|
124076
|
+
const handleRunSelectedChange = (0, import_react.useCallback)((node) => {
|
|
124077
|
+
const currentScreen = screenRef.current;
|
|
124078
|
+
if (currentScreen.type !== "runList") return;
|
|
124079
|
+
setSelectedTreeNode(node);
|
|
124080
|
+
setSelectedRun(node ? {
|
|
124081
|
+
jobId: currentScreen.job.id,
|
|
124082
|
+
runId: node.runId,
|
|
124083
|
+
expertKey: node.expertKey
|
|
124084
|
+
} : void 0);
|
|
124085
|
+
}, []);
|
|
124086
|
+
const renderInfoContent = () => {
|
|
124087
|
+
switch (screen.type) {
|
|
124088
|
+
case "jobList": return selectedJob ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(JobInfoContent, { job: selectedJob }) : null;
|
|
124089
|
+
case "jobDetail": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(JobInfoContent, { job: screen.job });
|
|
124090
|
+
case "runList": {
|
|
124091
|
+
if (!selectedTreeNode) return null;
|
|
124092
|
+
const stats = screen.runStats.get(selectedTreeNode.runId);
|
|
124093
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RunInfoContent, {
|
|
124094
|
+
job: screen.job,
|
|
124095
|
+
run: selectedRun,
|
|
124096
|
+
treeNode: selectedTreeNode,
|
|
124097
|
+
providerName: screen.treeState.providerName,
|
|
124098
|
+
checkpointCount: stats?.stepCount,
|
|
124099
|
+
eventCount: stats?.eventCount
|
|
124100
|
+
});
|
|
124101
|
+
}
|
|
124102
|
+
case "runDetail":
|
|
124103
|
+
if (screen.tab === "checkpoints" && selectedCheckpoint) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CheckpointInfoContent, { checkpoint: selectedCheckpoint });
|
|
124104
|
+
if ((screen.tab === "events" || screen.tab === "activities") && selectedEvent) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventInfoContent, { event: selectedEvent });
|
|
124105
|
+
return null;
|
|
124106
|
+
case "checkpointDetail": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CheckpointInfoContent, { checkpoint: screen.checkpoint });
|
|
124107
|
+
case "eventDetail": return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventInfoContent, { event: screen.event });
|
|
124108
|
+
default: return null;
|
|
124109
|
+
}
|
|
124110
|
+
};
|
|
124111
|
+
if (error) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
124112
|
+
flexDirection: "column",
|
|
124113
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, {
|
|
124114
|
+
color: colors.destructive,
|
|
124115
|
+
children: ["Error: ", error]
|
|
124116
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
124117
|
+
color: colors.muted,
|
|
124118
|
+
children: "Press q to quit"
|
|
124119
|
+
})]
|
|
124120
|
+
});
|
|
124121
|
+
if (loading) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
124122
|
+
color: colors.muted,
|
|
124123
|
+
children: "Loading..."
|
|
124124
|
+
});
|
|
124125
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
124126
|
+
flexDirection: "column",
|
|
124127
|
+
flexGrow: 1,
|
|
124128
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box, {
|
|
124129
|
+
flexDirection: "column",
|
|
124130
|
+
flexGrow: 1,
|
|
124131
|
+
children: [
|
|
124132
|
+
screen.type === "jobList" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(JobListScreen, {
|
|
124133
|
+
items: jobItems,
|
|
124134
|
+
onSelectJob: (job) => navigateToRunList(job),
|
|
124135
|
+
onViewJobDetail: navigateToJobDetail,
|
|
124136
|
+
onSelectedChange: setSelectedJob
|
|
124137
|
+
}),
|
|
124138
|
+
screen.type === "jobDetail" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(JobDetailScreen, {
|
|
124139
|
+
job: screen.job,
|
|
124140
|
+
onBack: () => setScreen({ type: "jobList" }),
|
|
124141
|
+
onViewRuns: navigateToRunList
|
|
124142
|
+
}),
|
|
124143
|
+
screen.type === "runList" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RunListScreen, {
|
|
124144
|
+
job: screen.job,
|
|
124145
|
+
treeState: screen.treeState,
|
|
124146
|
+
runQueries: screen.runQueries,
|
|
124147
|
+
delegationGroups: screen.delegationGroups,
|
|
124148
|
+
resumeNodes: screen.resumeNodes,
|
|
124149
|
+
onSelectRun: (node) => {
|
|
124150
|
+
const run = {
|
|
124151
|
+
jobId: screen.job.id,
|
|
124152
|
+
runId: node.runId,
|
|
124153
|
+
expertKey: node.expertKey
|
|
124154
|
+
};
|
|
124155
|
+
navigateToRunDetail(screen.job, run);
|
|
124156
|
+
},
|
|
124157
|
+
onBack: () => setScreen({ type: "jobList" }),
|
|
124158
|
+
onSelectedChange: handleRunSelectedChange
|
|
124159
|
+
}),
|
|
124160
|
+
screen.type === "runDetail" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RunDetailScreen, {
|
|
124161
|
+
job: screen.job,
|
|
124162
|
+
run: screen.run,
|
|
124163
|
+
checkpoints: screen.checkpoints,
|
|
124164
|
+
events: screen.events,
|
|
124165
|
+
initialTab: screen.tab,
|
|
124166
|
+
onSelectCheckpoint: (cp) => navigateToCheckpointDetail(screen.job, screen.run, cp),
|
|
124167
|
+
onSelectEvent: (ev) => navigateToEventDetail(screen.job, screen.run, ev),
|
|
124168
|
+
onBack: () => navigateToRunList(screen.job),
|
|
124169
|
+
onTabChange: (tab) => {
|
|
124170
|
+
setScreen({
|
|
124171
|
+
...screen,
|
|
124172
|
+
tab
|
|
124173
|
+
});
|
|
124174
|
+
},
|
|
124175
|
+
onSelectedCheckpointChange: setSelectedCheckpoint,
|
|
124176
|
+
onSelectedEventChange: setSelectedEvent
|
|
124177
|
+
}),
|
|
124178
|
+
screen.type === "checkpointDetail" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CheckpointDetailScreen, {
|
|
124179
|
+
job: screen.job,
|
|
124180
|
+
run: screen.run,
|
|
124181
|
+
checkpoint: screen.checkpoint,
|
|
124182
|
+
onBack: () => navigateToRunDetail(screen.job, screen.run, "checkpoints")
|
|
124183
|
+
}),
|
|
124184
|
+
screen.type === "eventDetail" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EventDetailScreen, {
|
|
124185
|
+
job: screen.job,
|
|
124186
|
+
run: screen.run,
|
|
124187
|
+
event: screen.event,
|
|
124188
|
+
onBack: () => navigateToRunDetail(screen.job, screen.run, "events")
|
|
124189
|
+
})
|
|
124190
|
+
]
|
|
124191
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BottomPanel, {
|
|
124192
|
+
onSubmit: handleChatSubmit,
|
|
124193
|
+
inputPlaceholder: "Ask AI about this log...",
|
|
124194
|
+
children: renderInfoContent() ?? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, {
|
|
124195
|
+
color: colors.muted,
|
|
124196
|
+
children: "Select an item to view details"
|
|
124197
|
+
})
|
|
124198
|
+
})]
|
|
124199
|
+
});
|
|
124200
|
+
};
|
|
124201
|
+
//#endregion
|
|
124202
|
+
//#region ../../packages/tui-components/src/log-viewer/render.tsx
|
|
124203
|
+
async function renderLogViewer(params) {
|
|
124204
|
+
const { waitUntilExit } = render(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LogViewerApp, {
|
|
124205
|
+
fetcher: params.fetcher,
|
|
124206
|
+
initialJobId: params.initialJobId,
|
|
124207
|
+
initialRunId: params.initialRunId
|
|
124208
|
+
}));
|
|
124209
|
+
await waitUntilExit();
|
|
124210
|
+
}
|
|
124211
|
+
//#endregion
|
|
121588
124212
|
//#region ../../packages/tui-components/src/selection/app.tsx
|
|
121589
124213
|
const selectionReducer = (_state, action) => {
|
|
121590
124214
|
switch (action.type) {
|
|
@@ -121891,7 +124515,7 @@ async function startHandler(expertKey, query, options, handlerOptions) {
|
|
|
121891
124515
|
//#endregion
|
|
121892
124516
|
//#region package.json
|
|
121893
124517
|
var name = "perstack";
|
|
121894
|
-
var version = "0.0.
|
|
124518
|
+
var version = "0.0.127";
|
|
121895
124519
|
var description = "PerStack CLI";
|
|
121896
124520
|
//#endregion
|
|
121897
124521
|
//#region bin/cli.ts
|
|
@@ -121926,7 +124550,16 @@ program.command("run").description("Run Perstack with JSON output").argument("<e
|
|
|
121926
124550
|
lockfile
|
|
121927
124551
|
});
|
|
121928
124552
|
});
|
|
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) =>
|
|
124553
|
+
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) => {
|
|
124554
|
+
const hasOutputFlags = options.json || options.pretty || options.summary || options.messages || options.text;
|
|
124555
|
+
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;
|
|
124556
|
+
if (hasOutputFlags || hasFilterFlags) await logHandler(options);
|
|
124557
|
+
else await renderLogViewer({
|
|
124558
|
+
fetcher: createLogDataFetcher(createStorageAdapter(process.env.PERSTACK_STORAGE_PATH ?? `${process.cwd()}/perstack`)),
|
|
124559
|
+
initialJobId: options.job,
|
|
124560
|
+
initialRunId: options.run
|
|
124561
|
+
});
|
|
124562
|
+
});
|
|
121930
124563
|
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
124564
|
await installHandler({
|
|
121932
124565
|
configPath: await findConfigPath(options.config),
|