perstack 0.0.82 → 0.0.84
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 +34854 -858
- package/dist/bin/cli.js.map +1 -1
- package/dist/chunk-PR4QN5HX.js +39 -0
- package/dist/chunk-PR4QN5HX.js.map +1 -0
- package/dist/devtools-3L2BRUAG.js +3674 -0
- package/dist/devtools-3L2BRUAG.js.map +1 -0
- package/package.json +5 -13
- package/dist/chunk-FOHDMSVR.js +0 -2161
- package/dist/chunk-FOHDMSVR.js.map +0 -1
- package/dist/src/start.js +0 -3
- package/dist/src/start.js.map +0 -1
package/dist/chunk-FOHDMSVR.js
DELETED
|
@@ -1,2161 +0,0 @@
|
|
|
1
|
-
import { createId } from '@paralleldrive/cuid2';
|
|
2
|
-
import { parseWithFriendlyError, startCommandInputSchema, defaultMaxRetries, defaultTimeout, checkpointSchema, jobSchema, perstackConfigSchema, runSettingSchema } from '@perstack/core';
|
|
3
|
-
import { existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync } from 'fs';
|
|
4
|
-
import { readFile, mkdir, writeFile } from 'fs/promises';
|
|
5
|
-
import path5 from 'path';
|
|
6
|
-
import { findLockfile, loadLockfile, runtimeVersion, run } from '@perstack/runtime';
|
|
7
|
-
import { Command } from 'commander';
|
|
8
|
-
import dotenv from 'dotenv';
|
|
9
|
-
import TOML from 'smol-toml';
|
|
10
|
-
import { render, useApp, useInput, Box, Text } from 'ink';
|
|
11
|
-
import { createContext, useMemo, useReducer, useState, useEffect, useCallback, useRef, useInsertionEffect, useContext } from 'react';
|
|
12
|
-
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
13
|
-
import { useRun } from '@perstack/react';
|
|
14
|
-
|
|
15
|
-
// src/start.ts
|
|
16
|
-
function getJobsDir() {
|
|
17
|
-
return `${process.cwd()}/perstack/jobs`;
|
|
18
|
-
}
|
|
19
|
-
function getJobDir(jobId) {
|
|
20
|
-
return `${getJobsDir()}/${jobId}`;
|
|
21
|
-
}
|
|
22
|
-
function storeJob(job) {
|
|
23
|
-
const jobDir = getJobDir(job.id);
|
|
24
|
-
if (!existsSync(jobDir)) {
|
|
25
|
-
mkdirSync(jobDir, { recursive: true });
|
|
26
|
-
}
|
|
27
|
-
const jobPath = path5.resolve(jobDir, "job.json");
|
|
28
|
-
writeFileSync(jobPath, JSON.stringify(job, null, 2));
|
|
29
|
-
}
|
|
30
|
-
function retrieveJob(jobId) {
|
|
31
|
-
const jobDir = getJobDir(jobId);
|
|
32
|
-
const jobPath = path5.resolve(jobDir, "job.json");
|
|
33
|
-
if (!existsSync(jobPath)) {
|
|
34
|
-
return void 0;
|
|
35
|
-
}
|
|
36
|
-
const content = readFileSync(jobPath, "utf-8");
|
|
37
|
-
return jobSchema.parse(JSON.parse(content));
|
|
38
|
-
}
|
|
39
|
-
function getAllJobs() {
|
|
40
|
-
const jobsDir = getJobsDir();
|
|
41
|
-
if (!existsSync(jobsDir)) {
|
|
42
|
-
return [];
|
|
43
|
-
}
|
|
44
|
-
const jobDirNames = readdirSync(jobsDir, { withFileTypes: true }).filter((dir) => dir.isDirectory()).map((dir) => dir.name);
|
|
45
|
-
if (jobDirNames.length === 0) {
|
|
46
|
-
return [];
|
|
47
|
-
}
|
|
48
|
-
const jobs = [];
|
|
49
|
-
for (const jobDirName of jobDirNames) {
|
|
50
|
-
const jobPath = path5.resolve(jobsDir, jobDirName, "job.json");
|
|
51
|
-
if (!existsSync(jobPath)) {
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
try {
|
|
55
|
-
const content = readFileSync(jobPath, "utf-8");
|
|
56
|
-
jobs.push(jobSchema.parse(JSON.parse(content)));
|
|
57
|
-
} catch {
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return jobs.sort((a, b) => b.startedAt - a.startedAt);
|
|
61
|
-
}
|
|
62
|
-
function createInitialJob(jobId, expertKey, maxSteps) {
|
|
63
|
-
return {
|
|
64
|
-
id: jobId,
|
|
65
|
-
status: "running",
|
|
66
|
-
coordinatorExpertKey: expertKey,
|
|
67
|
-
runtimeVersion: "v1.0",
|
|
68
|
-
totalSteps: 0,
|
|
69
|
-
maxSteps,
|
|
70
|
-
usage: {
|
|
71
|
-
inputTokens: 0,
|
|
72
|
-
outputTokens: 0,
|
|
73
|
-
reasoningTokens: 0,
|
|
74
|
-
totalTokens: 0,
|
|
75
|
-
cachedInputTokens: 0
|
|
76
|
-
},
|
|
77
|
-
startedAt: Date.now()
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// ../../packages/filesystem/src/checkpoint.ts
|
|
82
|
-
function getCheckpointDir(jobId) {
|
|
83
|
-
return `${getJobDir(jobId)}/checkpoints`;
|
|
84
|
-
}
|
|
85
|
-
function getCheckpointPath(jobId, checkpointId) {
|
|
86
|
-
return `${getCheckpointDir(jobId)}/${checkpointId}.json`;
|
|
87
|
-
}
|
|
88
|
-
async function defaultRetrieveCheckpoint(jobId, checkpointId) {
|
|
89
|
-
const checkpointPath = getCheckpointPath(jobId, checkpointId);
|
|
90
|
-
if (!existsSync(checkpointPath)) {
|
|
91
|
-
throw new Error(`checkpoint not found: ${checkpointId}`);
|
|
92
|
-
}
|
|
93
|
-
const checkpoint = await readFile(checkpointPath, "utf8");
|
|
94
|
-
return checkpointSchema.parse(JSON.parse(checkpoint));
|
|
95
|
-
}
|
|
96
|
-
async function defaultStoreCheckpoint(checkpoint) {
|
|
97
|
-
const { id, jobId } = checkpoint;
|
|
98
|
-
const checkpointDir = getCheckpointDir(jobId);
|
|
99
|
-
await mkdir(checkpointDir, { recursive: true });
|
|
100
|
-
await writeFile(getCheckpointPath(jobId, id), JSON.stringify(checkpoint));
|
|
101
|
-
}
|
|
102
|
-
function getCheckpointsByJobId(jobId) {
|
|
103
|
-
const checkpointDir = getCheckpointDir(jobId);
|
|
104
|
-
if (!existsSync(checkpointDir)) {
|
|
105
|
-
return [];
|
|
106
|
-
}
|
|
107
|
-
const files = readdirSync(checkpointDir).filter((file) => file.endsWith(".json"));
|
|
108
|
-
const checkpoints = [];
|
|
109
|
-
for (const file of files) {
|
|
110
|
-
try {
|
|
111
|
-
const content = readFileSync(path5.resolve(checkpointDir, file), "utf-8");
|
|
112
|
-
checkpoints.push(checkpointSchema.parse(JSON.parse(content)));
|
|
113
|
-
} catch {
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
return checkpoints.sort((a, b) => a.stepNumber - b.stepNumber);
|
|
117
|
-
}
|
|
118
|
-
function defaultGetRunDir(jobId, runId) {
|
|
119
|
-
return `${process.cwd()}/perstack/jobs/${jobId}/runs/${runId}`;
|
|
120
|
-
}
|
|
121
|
-
function getAllRuns() {
|
|
122
|
-
const jobsDir = getJobsDir();
|
|
123
|
-
if (!existsSync(jobsDir)) {
|
|
124
|
-
return [];
|
|
125
|
-
}
|
|
126
|
-
const jobDirNames = readdirSync(jobsDir, { withFileTypes: true }).filter((dir) => dir.isDirectory()).map((dir) => dir.name);
|
|
127
|
-
if (jobDirNames.length === 0) {
|
|
128
|
-
return [];
|
|
129
|
-
}
|
|
130
|
-
const runs = [];
|
|
131
|
-
for (const jobDirName of jobDirNames) {
|
|
132
|
-
const runsDir = path5.resolve(jobsDir, jobDirName, "runs");
|
|
133
|
-
if (!existsSync(runsDir)) {
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
const runDirNames = readdirSync(runsDir, { withFileTypes: true }).filter((dir) => dir.isDirectory()).map((dir) => dir.name);
|
|
137
|
-
for (const runDirName of runDirNames) {
|
|
138
|
-
const runSettingPath = path5.resolve(runsDir, runDirName, "run-setting.json");
|
|
139
|
-
if (!existsSync(runSettingPath)) {
|
|
140
|
-
continue;
|
|
141
|
-
}
|
|
142
|
-
try {
|
|
143
|
-
const content = readFileSync(runSettingPath, "utf-8");
|
|
144
|
-
runs.push(runSettingSchema.parse(JSON.parse(content)));
|
|
145
|
-
} catch {
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
return runs.sort((a, b) => b.updatedAt - a.updatedAt);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// ../../packages/filesystem/src/event.ts
|
|
153
|
-
async function defaultStoreEvent(event) {
|
|
154
|
-
const { timestamp, jobId, runId, stepNumber, type } = event;
|
|
155
|
-
const runDir = defaultGetRunDir(jobId, runId);
|
|
156
|
-
const eventPath = `${runDir}/event-${timestamp}-${stepNumber}-${type}.json`;
|
|
157
|
-
await mkdir(runDir, { recursive: true });
|
|
158
|
-
await writeFile(eventPath, JSON.stringify(event));
|
|
159
|
-
}
|
|
160
|
-
function getEventContents(jobId, runId, maxStepNumber) {
|
|
161
|
-
const runDir = defaultGetRunDir(jobId, runId);
|
|
162
|
-
if (!existsSync(runDir)) {
|
|
163
|
-
return [];
|
|
164
|
-
}
|
|
165
|
-
const eventFiles = readdirSync(runDir).filter((file) => file.startsWith("event-")).map((file) => {
|
|
166
|
-
const [_, timestamp, step, type] = file.split(".")[0].split("-");
|
|
167
|
-
return { file, timestamp: Number(timestamp), stepNumber: Number(step), type };
|
|
168
|
-
}).filter((e) => maxStepNumber === void 0 || e.stepNumber <= maxStepNumber).sort((a, b) => a.timestamp - b.timestamp);
|
|
169
|
-
const events = [];
|
|
170
|
-
for (const { file } of eventFiles) {
|
|
171
|
-
try {
|
|
172
|
-
const content = readFileSync(path5.resolve(runDir, file), "utf-8");
|
|
173
|
-
events.push(JSON.parse(content));
|
|
174
|
-
} catch {
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return events;
|
|
178
|
-
}
|
|
179
|
-
function getRunIdsByJobId(jobId) {
|
|
180
|
-
const runsDir = path5.resolve(getJobDir(jobId), "runs");
|
|
181
|
-
if (!existsSync(runsDir)) {
|
|
182
|
-
return [];
|
|
183
|
-
}
|
|
184
|
-
return readdirSync(runsDir, { withFileTypes: true }).filter((dir) => dir.isDirectory()).map((dir) => dir.name);
|
|
185
|
-
}
|
|
186
|
-
function getEnv(envPath) {
|
|
187
|
-
const env = Object.fromEntries(
|
|
188
|
-
Object.entries(process.env).filter(([_, value]) => !!value).map(([key, value]) => [key, value])
|
|
189
|
-
);
|
|
190
|
-
dotenv.config({ path: envPath, processEnv: env, quiet: true });
|
|
191
|
-
return env;
|
|
192
|
-
}
|
|
193
|
-
var ALLOWED_CONFIG_HOSTS = ["raw.githubusercontent.com"];
|
|
194
|
-
async function getPerstackConfig(configPath) {
|
|
195
|
-
const configString = await findPerstackConfigString(configPath);
|
|
196
|
-
if (configString === null) {
|
|
197
|
-
throw new Error("perstack.toml not found. Create one or specify --config path.");
|
|
198
|
-
}
|
|
199
|
-
return await parsePerstackConfig(configString);
|
|
200
|
-
}
|
|
201
|
-
function isRemoteUrl(configPath) {
|
|
202
|
-
const lower = configPath.toLowerCase();
|
|
203
|
-
return lower.startsWith("https://") || lower.startsWith("http://");
|
|
204
|
-
}
|
|
205
|
-
async function fetchRemoteConfig(url) {
|
|
206
|
-
let parsed;
|
|
207
|
-
try {
|
|
208
|
-
parsed = new URL(url);
|
|
209
|
-
} catch {
|
|
210
|
-
throw new Error(`Invalid remote config URL: ${url}`);
|
|
211
|
-
}
|
|
212
|
-
if (parsed.protocol !== "https:") {
|
|
213
|
-
throw new Error("Remote config requires HTTPS");
|
|
214
|
-
}
|
|
215
|
-
if (!ALLOWED_CONFIG_HOSTS.includes(parsed.hostname)) {
|
|
216
|
-
throw new Error(`Remote config only allowed from: ${ALLOWED_CONFIG_HOSTS.join(", ")}`);
|
|
217
|
-
}
|
|
218
|
-
try {
|
|
219
|
-
const response = await fetch(url, { redirect: "error" });
|
|
220
|
-
if (!response.ok) {
|
|
221
|
-
throw new Error(`${response.status} ${response.statusText}`);
|
|
222
|
-
}
|
|
223
|
-
return await response.text();
|
|
224
|
-
} catch (error) {
|
|
225
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
226
|
-
throw new Error(`Failed to fetch remote config: ${message}`);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
async function findPerstackConfigString(configPath) {
|
|
230
|
-
if (configPath) {
|
|
231
|
-
if (isRemoteUrl(configPath)) {
|
|
232
|
-
return await fetchRemoteConfig(configPath);
|
|
233
|
-
}
|
|
234
|
-
try {
|
|
235
|
-
const tomlString = await readFile(path5.resolve(process.cwd(), configPath), "utf-8");
|
|
236
|
-
return tomlString;
|
|
237
|
-
} catch {
|
|
238
|
-
throw new Error(`Given config path "${configPath}" is not found`);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
return await findPerstackConfigStringRecursively(path5.resolve(process.cwd()));
|
|
242
|
-
}
|
|
243
|
-
async function findPerstackConfigStringRecursively(cwd) {
|
|
244
|
-
try {
|
|
245
|
-
const tomlString = await readFile(path5.resolve(cwd, "perstack.toml"), "utf-8");
|
|
246
|
-
return tomlString;
|
|
247
|
-
} catch {
|
|
248
|
-
if (cwd === path5.parse(cwd).root) {
|
|
249
|
-
return null;
|
|
250
|
-
}
|
|
251
|
-
return await findPerstackConfigStringRecursively(path5.dirname(cwd));
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
async function parsePerstackConfig(config) {
|
|
255
|
-
const toml = TOML.parse(config ?? "");
|
|
256
|
-
return parseWithFriendlyError(perstackConfigSchema, toml, "perstack.toml");
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// src/lib/provider-config.ts
|
|
260
|
-
function getProviderConfig(provider, env, providerTable) {
|
|
261
|
-
const setting = providerTable?.setting ?? {};
|
|
262
|
-
switch (provider) {
|
|
263
|
-
case "anthropic": {
|
|
264
|
-
const apiKey = env.ANTHROPIC_API_KEY;
|
|
265
|
-
if (!apiKey) throw new Error("ANTHROPIC_API_KEY is not set");
|
|
266
|
-
return {
|
|
267
|
-
providerName: "anthropic",
|
|
268
|
-
apiKey,
|
|
269
|
-
baseUrl: setting.baseUrl ?? env.ANTHROPIC_BASE_URL,
|
|
270
|
-
headers: setting.headers
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
case "google": {
|
|
274
|
-
const apiKey = env.GOOGLE_GENERATIVE_AI_API_KEY;
|
|
275
|
-
if (!apiKey) throw new Error("GOOGLE_GENERATIVE_AI_API_KEY is not set");
|
|
276
|
-
return {
|
|
277
|
-
providerName: "google",
|
|
278
|
-
apiKey,
|
|
279
|
-
baseUrl: setting.baseUrl ?? env.GOOGLE_GENERATIVE_AI_BASE_URL,
|
|
280
|
-
headers: setting.headers
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
case "openai": {
|
|
284
|
-
const apiKey = env.OPENAI_API_KEY;
|
|
285
|
-
if (!apiKey) throw new Error("OPENAI_API_KEY is not set");
|
|
286
|
-
return {
|
|
287
|
-
providerName: "openai",
|
|
288
|
-
apiKey,
|
|
289
|
-
baseUrl: setting.baseUrl ?? env.OPENAI_BASE_URL,
|
|
290
|
-
organization: setting.organization ?? env.OPENAI_ORGANIZATION,
|
|
291
|
-
project: setting.project ?? env.OPENAI_PROJECT,
|
|
292
|
-
name: setting.name,
|
|
293
|
-
headers: setting.headers
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
case "ollama": {
|
|
297
|
-
return {
|
|
298
|
-
providerName: "ollama",
|
|
299
|
-
baseUrl: setting.baseUrl ?? env.OLLAMA_BASE_URL,
|
|
300
|
-
headers: setting.headers
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
case "azure-openai": {
|
|
304
|
-
const apiKey = env.AZURE_API_KEY;
|
|
305
|
-
if (!apiKey) throw new Error("AZURE_API_KEY is not set");
|
|
306
|
-
const resourceName = setting.resourceName ?? env.AZURE_RESOURCE_NAME;
|
|
307
|
-
const baseUrl = setting.baseUrl ?? env.AZURE_BASE_URL;
|
|
308
|
-
if (!resourceName && !baseUrl) throw new Error("AZURE_RESOURCE_NAME or baseUrl is not set");
|
|
309
|
-
return {
|
|
310
|
-
providerName: "azure-openai",
|
|
311
|
-
apiKey,
|
|
312
|
-
resourceName,
|
|
313
|
-
apiVersion: setting.apiVersion ?? env.AZURE_API_VERSION,
|
|
314
|
-
baseUrl,
|
|
315
|
-
headers: setting.headers,
|
|
316
|
-
useDeploymentBasedUrls: setting.useDeploymentBasedUrls
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
case "amazon-bedrock": {
|
|
320
|
-
const accessKeyId = env.AWS_ACCESS_KEY_ID;
|
|
321
|
-
const secretAccessKey = env.AWS_SECRET_ACCESS_KEY;
|
|
322
|
-
const sessionToken = env.AWS_SESSION_TOKEN;
|
|
323
|
-
if (!accessKeyId) throw new Error("AWS_ACCESS_KEY_ID is not set");
|
|
324
|
-
if (!secretAccessKey) throw new Error("AWS_SECRET_ACCESS_KEY is not set");
|
|
325
|
-
const region = setting.region ?? env.AWS_REGION;
|
|
326
|
-
if (!region) throw new Error("AWS_REGION is not set");
|
|
327
|
-
return {
|
|
328
|
-
providerName: "amazon-bedrock",
|
|
329
|
-
accessKeyId,
|
|
330
|
-
secretAccessKey,
|
|
331
|
-
region,
|
|
332
|
-
sessionToken
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
case "google-vertex": {
|
|
336
|
-
return {
|
|
337
|
-
providerName: "google-vertex",
|
|
338
|
-
project: setting.project ?? env.GOOGLE_VERTEX_PROJECT,
|
|
339
|
-
location: setting.location ?? env.GOOGLE_VERTEX_LOCATION,
|
|
340
|
-
baseUrl: setting.baseUrl ?? env.GOOGLE_VERTEX_BASE_URL,
|
|
341
|
-
headers: setting.headers
|
|
342
|
-
};
|
|
343
|
-
}
|
|
344
|
-
case "deepseek": {
|
|
345
|
-
const apiKey = env.DEEPSEEK_API_KEY;
|
|
346
|
-
if (!apiKey) throw new Error("DEEPSEEK_API_KEY is not set");
|
|
347
|
-
return {
|
|
348
|
-
providerName: "deepseek",
|
|
349
|
-
apiKey,
|
|
350
|
-
baseUrl: setting.baseUrl ?? env.DEEPSEEK_BASE_URL,
|
|
351
|
-
headers: setting.headers
|
|
352
|
-
};
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
function getAllJobs2() {
|
|
357
|
-
return getAllJobs();
|
|
358
|
-
}
|
|
359
|
-
function getAllRuns2() {
|
|
360
|
-
return getAllRuns();
|
|
361
|
-
}
|
|
362
|
-
function getMostRecentRun() {
|
|
363
|
-
const runs = getAllRuns2();
|
|
364
|
-
if (runs.length === 0) {
|
|
365
|
-
throw new Error("No runs found");
|
|
366
|
-
}
|
|
367
|
-
return runs[0];
|
|
368
|
-
}
|
|
369
|
-
function getCheckpointsByJobId2(jobId) {
|
|
370
|
-
return getCheckpointsByJobId(jobId);
|
|
371
|
-
}
|
|
372
|
-
function getMostRecentCheckpoint(jobId) {
|
|
373
|
-
const targetJobId = jobId ?? getMostRecentRun().jobId;
|
|
374
|
-
const checkpoints = getCheckpointsByJobId2(targetJobId);
|
|
375
|
-
if (checkpoints.length === 0) {
|
|
376
|
-
throw new Error(`No checkpoints found for job ${targetJobId}`);
|
|
377
|
-
}
|
|
378
|
-
return checkpoints[checkpoints.length - 1];
|
|
379
|
-
}
|
|
380
|
-
function getRecentExperts(limit) {
|
|
381
|
-
const runs = getAllRuns2();
|
|
382
|
-
const expertMap = /* @__PURE__ */ new Map();
|
|
383
|
-
for (const setting of runs) {
|
|
384
|
-
const expertKey = setting.expertKey;
|
|
385
|
-
if (!expertMap.has(expertKey) || expertMap.get(expertKey).lastUsed < setting.updatedAt) {
|
|
386
|
-
expertMap.set(expertKey, {
|
|
387
|
-
key: expertKey,
|
|
388
|
-
name: expertKey,
|
|
389
|
-
lastUsed: setting.updatedAt
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
return Array.from(expertMap.values()).sort((a, b) => b.lastUsed - a.lastUsed).slice(0, limit);
|
|
394
|
-
}
|
|
395
|
-
function getCheckpointById(jobId, checkpointId) {
|
|
396
|
-
const checkpointPath = getCheckpointPath(jobId, checkpointId);
|
|
397
|
-
if (!existsSync(checkpointPath)) {
|
|
398
|
-
throw new Error(`Checkpoint ${checkpointId} not found in job ${jobId}`);
|
|
399
|
-
}
|
|
400
|
-
const checkpoint = readFileSync(checkpointPath, "utf-8");
|
|
401
|
-
return checkpointSchema.parse(JSON.parse(checkpoint));
|
|
402
|
-
}
|
|
403
|
-
function getCheckpointsWithDetails(jobId) {
|
|
404
|
-
return getCheckpointsByJobId2(jobId).map((cp) => ({
|
|
405
|
-
id: cp.id,
|
|
406
|
-
runId: cp.runId,
|
|
407
|
-
stepNumber: cp.stepNumber,
|
|
408
|
-
contextWindowUsage: cp.contextWindowUsage ?? 0
|
|
409
|
-
})).sort((a, b) => b.stepNumber - a.stepNumber);
|
|
410
|
-
}
|
|
411
|
-
function getAllEventContentsForJob(jobId, maxStepNumber) {
|
|
412
|
-
const runIds = getRunIdsByJobId(jobId);
|
|
413
|
-
const allEvents = [];
|
|
414
|
-
for (const runId of runIds) {
|
|
415
|
-
const events = getEventContents(jobId, runId, maxStepNumber);
|
|
416
|
-
allEvents.push(...events);
|
|
417
|
-
}
|
|
418
|
-
return allEvents.sort((a, b) => a.timestamp - b.timestamp);
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// src/lib/context.ts
|
|
422
|
-
var defaultProvider = "anthropic";
|
|
423
|
-
var defaultModel = "claude-sonnet-4-5";
|
|
424
|
-
async function resolveRunContext(input) {
|
|
425
|
-
const perstackConfig = input.perstackConfig ?? await getPerstackConfig(input.configPath);
|
|
426
|
-
let checkpoint;
|
|
427
|
-
if (input.resumeFrom) {
|
|
428
|
-
if (!input.continueJob) {
|
|
429
|
-
throw new Error("--resume-from requires --continue-job");
|
|
430
|
-
}
|
|
431
|
-
checkpoint = getCheckpointById(input.continueJob, input.resumeFrom);
|
|
432
|
-
} else if (input.continueJob) {
|
|
433
|
-
checkpoint = getMostRecentCheckpoint(input.continueJob);
|
|
434
|
-
} else if (input.continue) {
|
|
435
|
-
checkpoint = getMostRecentCheckpoint();
|
|
436
|
-
}
|
|
437
|
-
if ((input.continue || input.continueJob || input.resumeFrom) && !checkpoint) {
|
|
438
|
-
throw new Error("No checkpoint found");
|
|
439
|
-
}
|
|
440
|
-
if (checkpoint && input.expertKey && checkpoint.expert.key !== input.expertKey) {
|
|
441
|
-
throw new Error(
|
|
442
|
-
`Checkpoint expert key ${checkpoint.expert.key} does not match input expert key ${input.expertKey}`
|
|
443
|
-
);
|
|
444
|
-
}
|
|
445
|
-
const envPath = input.envPath && input.envPath.length > 0 ? input.envPath : perstackConfig.envPath ?? [".env", ".env.local"];
|
|
446
|
-
const env = getEnv(envPath);
|
|
447
|
-
const provider = input.provider ?? perstackConfig.provider?.providerName ?? defaultProvider;
|
|
448
|
-
const model = input.model ?? perstackConfig.model ?? defaultModel;
|
|
449
|
-
const providerConfig = getProviderConfig(provider, env, perstackConfig.provider);
|
|
450
|
-
const experts = Object.fromEntries(
|
|
451
|
-
Object.entries(perstackConfig.experts ?? {}).map(([name, expert]) => {
|
|
452
|
-
return [
|
|
453
|
-
name,
|
|
454
|
-
{
|
|
455
|
-
name,
|
|
456
|
-
version: expert.version ?? "1.0.0",
|
|
457
|
-
description: expert.description,
|
|
458
|
-
instruction: expert.instruction,
|
|
459
|
-
skills: expert.skills,
|
|
460
|
-
delegates: expert.delegates,
|
|
461
|
-
tags: expert.tags
|
|
462
|
-
}
|
|
463
|
-
];
|
|
464
|
-
})
|
|
465
|
-
);
|
|
466
|
-
return {
|
|
467
|
-
perstackConfig,
|
|
468
|
-
checkpoint,
|
|
469
|
-
env,
|
|
470
|
-
providerConfig,
|
|
471
|
-
model,
|
|
472
|
-
experts
|
|
473
|
-
};
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
// src/lib/interactive.ts
|
|
477
|
-
function parseInteractiveToolCallResult(query, checkpoint) {
|
|
478
|
-
const lastMessage = checkpoint.messages[checkpoint.messages.length - 1];
|
|
479
|
-
if (lastMessage.type !== "expertMessage") {
|
|
480
|
-
throw new Error("Last message is not a expert message");
|
|
481
|
-
}
|
|
482
|
-
const content = lastMessage.contents.find((c) => c.type === "toolCallPart");
|
|
483
|
-
if (!content || content.type !== "toolCallPart") {
|
|
484
|
-
throw new Error("Last message content is not a tool call part");
|
|
485
|
-
}
|
|
486
|
-
const toolCallId = content.toolCallId;
|
|
487
|
-
const toolName = content.toolName;
|
|
488
|
-
const pendingToolCall = checkpoint.pendingToolCalls?.find((tc) => tc.id === toolCallId);
|
|
489
|
-
const skillName = pendingToolCall?.skillName ?? "";
|
|
490
|
-
return {
|
|
491
|
-
interactiveToolCallResult: {
|
|
492
|
-
toolCallId,
|
|
493
|
-
toolName,
|
|
494
|
-
skillName,
|
|
495
|
-
text: query
|
|
496
|
-
}
|
|
497
|
-
};
|
|
498
|
-
}
|
|
499
|
-
function parseInteractiveToolCallResultJson(query) {
|
|
500
|
-
try {
|
|
501
|
-
const parsed = JSON.parse(query);
|
|
502
|
-
if (typeof parsed === "object" && parsed !== null && "toolCallId" in parsed && "toolName" in parsed && "skillName" in parsed && "text" in parsed) {
|
|
503
|
-
return {
|
|
504
|
-
interactiveToolCallResult: parsed
|
|
505
|
-
};
|
|
506
|
-
}
|
|
507
|
-
return null;
|
|
508
|
-
} catch {
|
|
509
|
-
return null;
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
// src/tui/utils/event-queue.ts
|
|
514
|
-
var defaultErrorLogger = (message, error) => {
|
|
515
|
-
console.error(message, error);
|
|
516
|
-
};
|
|
517
|
-
var EventQueue = class _EventQueue {
|
|
518
|
-
static MAX_PENDING_EVENTS = 1e3;
|
|
519
|
-
pendingEvents = [];
|
|
520
|
-
handler = null;
|
|
521
|
-
onError;
|
|
522
|
-
errorLogger;
|
|
523
|
-
constructor(options) {
|
|
524
|
-
this.onError = options?.onError;
|
|
525
|
-
this.errorLogger = options?.errorLogger ?? defaultErrorLogger;
|
|
526
|
-
}
|
|
527
|
-
setHandler(fn) {
|
|
528
|
-
this.handler = fn;
|
|
529
|
-
for (const event of this.pendingEvents) {
|
|
530
|
-
this.safeHandle(event);
|
|
531
|
-
}
|
|
532
|
-
this.pendingEvents.length = 0;
|
|
533
|
-
}
|
|
534
|
-
emit(event) {
|
|
535
|
-
if (this.handler) {
|
|
536
|
-
this.safeHandle(event);
|
|
537
|
-
} else {
|
|
538
|
-
if (this.pendingEvents.length >= _EventQueue.MAX_PENDING_EVENTS) {
|
|
539
|
-
this.pendingEvents.shift();
|
|
540
|
-
}
|
|
541
|
-
this.pendingEvents.push(event);
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
safeHandle(event) {
|
|
545
|
-
try {
|
|
546
|
-
this.handler?.(event);
|
|
547
|
-
} catch (error) {
|
|
548
|
-
if (this.onError) {
|
|
549
|
-
this.onError(error);
|
|
550
|
-
} else {
|
|
551
|
-
this.errorLogger("EventQueue handler error:", error);
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
};
|
|
556
|
-
var InputAreaContext = createContext(null);
|
|
557
|
-
var InputAreaProvider = InputAreaContext.Provider;
|
|
558
|
-
var useInputAreaContext = () => {
|
|
559
|
-
const context = useContext(InputAreaContext);
|
|
560
|
-
if (!context) {
|
|
561
|
-
throw new Error("useInputAreaContext must be used within InputAreaProvider");
|
|
562
|
-
}
|
|
563
|
-
return context;
|
|
564
|
-
};
|
|
565
|
-
|
|
566
|
-
// src/tui/helpers.ts
|
|
567
|
-
var truncateText = (text, maxLength) => {
|
|
568
|
-
if (text.length <= maxLength) return text;
|
|
569
|
-
return `${text.slice(0, maxLength)}...`;
|
|
570
|
-
};
|
|
571
|
-
var assertNever = (x) => {
|
|
572
|
-
throw new Error(`Unexpected value: ${x}`);
|
|
573
|
-
};
|
|
574
|
-
var formatTimestamp = (timestamp) => new Date(timestamp).toLocaleString();
|
|
575
|
-
var shortenPath = (fullPath, maxLength = 60) => {
|
|
576
|
-
if (fullPath.length <= maxLength) return fullPath;
|
|
577
|
-
const parts = fullPath.split("/");
|
|
578
|
-
if (parts.length <= 2) return fullPath;
|
|
579
|
-
const filename = parts[parts.length - 1] ?? "";
|
|
580
|
-
const parentDir = parts[parts.length - 2] ?? "";
|
|
581
|
-
const shortened = `.../${parentDir}/${filename}`;
|
|
582
|
-
if (shortened.length <= maxLength) return shortened;
|
|
583
|
-
return `.../${filename}`;
|
|
584
|
-
};
|
|
585
|
-
var summarizeOutput = (lines, maxLines) => {
|
|
586
|
-
const filtered = lines.filter((l) => l.trim());
|
|
587
|
-
const visible = filtered.slice(0, maxLines);
|
|
588
|
-
const remaining = filtered.length - visible.length;
|
|
589
|
-
return { visible, remaining };
|
|
590
|
-
};
|
|
591
|
-
|
|
592
|
-
// src/tui/constants.ts
|
|
593
|
-
var UI_CONSTANTS = {
|
|
594
|
-
MAX_VISIBLE_LIST_ITEMS: 25,
|
|
595
|
-
TRUNCATE_TEXT_MEDIUM: 80,
|
|
596
|
-
TRUNCATE_TEXT_DEFAULT: 100};
|
|
597
|
-
var RENDER_CONSTANTS = {
|
|
598
|
-
EXEC_OUTPUT_MAX_LINES: 4,
|
|
599
|
-
NEW_TODO_MAX_PREVIEW: 3,
|
|
600
|
-
LIST_DIR_MAX_ITEMS: 4
|
|
601
|
-
};
|
|
602
|
-
var INDICATOR = {
|
|
603
|
-
BULLET: "\u25CF",
|
|
604
|
-
TREE: "\u2514",
|
|
605
|
-
ELLIPSIS: "..."
|
|
606
|
-
};
|
|
607
|
-
var STOP_EVENT_TYPES = [
|
|
608
|
-
"stopRunByInteractiveTool",
|
|
609
|
-
"stopRunByDelegate",
|
|
610
|
-
"stopRunByExceededMaxSteps"
|
|
611
|
-
];
|
|
612
|
-
var KEY_BINDINGS = {
|
|
613
|
-
NAVIGATE_UP: "\u2191",
|
|
614
|
-
NAVIGATE_DOWN: "\u2193",
|
|
615
|
-
SELECT: "Enter",
|
|
616
|
-
BACK: "b",
|
|
617
|
-
ESCAPE: "Esc",
|
|
618
|
-
INPUT_MODE: "i",
|
|
619
|
-
HISTORY: "h",
|
|
620
|
-
NEW: "n",
|
|
621
|
-
CHECKPOINTS: "c",
|
|
622
|
-
EVENTS: "e",
|
|
623
|
-
RESUME: "Enter"};
|
|
624
|
-
var KEY_HINTS = {
|
|
625
|
-
NAVIGATE: `${KEY_BINDINGS.NAVIGATE_UP}${KEY_BINDINGS.NAVIGATE_DOWN}:Navigate`,
|
|
626
|
-
SELECT: `${KEY_BINDINGS.SELECT}:Select`,
|
|
627
|
-
RESUME: `${KEY_BINDINGS.RESUME}:Resume`,
|
|
628
|
-
BACK: `${KEY_BINDINGS.BACK}:Back`,
|
|
629
|
-
CANCEL: `${KEY_BINDINGS.ESCAPE}:Cancel`,
|
|
630
|
-
INPUT: `${KEY_BINDINGS.INPUT_MODE}:Input`,
|
|
631
|
-
HISTORY: `${KEY_BINDINGS.HISTORY}:History`,
|
|
632
|
-
NEW: `${KEY_BINDINGS.NEW}:New Run`,
|
|
633
|
-
CHECKPOINTS: `${KEY_BINDINGS.CHECKPOINTS}:Checkpoints`,
|
|
634
|
-
EVENTS: `${KEY_BINDINGS.EVENTS}:Events`};
|
|
635
|
-
var useLatestRef = (value) => {
|
|
636
|
-
const ref = useRef(value);
|
|
637
|
-
useInsertionEffect(() => {
|
|
638
|
-
ref.current = value;
|
|
639
|
-
});
|
|
640
|
-
return ref;
|
|
641
|
-
};
|
|
642
|
-
var useListNavigation = (options) => {
|
|
643
|
-
const { items, onSelect, onBack } = options;
|
|
644
|
-
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
645
|
-
useEffect(() => {
|
|
646
|
-
if (selectedIndex >= items.length && items.length > 0) {
|
|
647
|
-
setSelectedIndex(items.length - 1);
|
|
648
|
-
}
|
|
649
|
-
}, [items.length, selectedIndex]);
|
|
650
|
-
const handleNavigation = useCallback(
|
|
651
|
-
(inputChar, key) => {
|
|
652
|
-
if (key.upArrow && items.length > 0) {
|
|
653
|
-
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
654
|
-
return true;
|
|
655
|
-
}
|
|
656
|
-
if (key.downArrow && items.length > 0) {
|
|
657
|
-
setSelectedIndex((prev) => Math.min(items.length - 1, prev + 1));
|
|
658
|
-
return true;
|
|
659
|
-
}
|
|
660
|
-
if (key.return && items[selectedIndex]) {
|
|
661
|
-
onSelect?.(items[selectedIndex]);
|
|
662
|
-
return true;
|
|
663
|
-
}
|
|
664
|
-
if ((key.escape || inputChar === "b") && onBack) {
|
|
665
|
-
onBack();
|
|
666
|
-
return true;
|
|
667
|
-
}
|
|
668
|
-
return false;
|
|
669
|
-
},
|
|
670
|
-
[items, selectedIndex, onSelect, onBack]
|
|
671
|
-
);
|
|
672
|
-
return { selectedIndex, handleNavigation };
|
|
673
|
-
};
|
|
674
|
-
var useTextInput = (options) => {
|
|
675
|
-
const [input, setInput] = useState("");
|
|
676
|
-
const inputRef = useLatestRef(input);
|
|
677
|
-
const onSubmitRef = useLatestRef(options.onSubmit);
|
|
678
|
-
const onCancelRef = useLatestRef(options.onCancel);
|
|
679
|
-
const handleInput = useCallback(
|
|
680
|
-
(inputChar, key) => {
|
|
681
|
-
if (key.escape) {
|
|
682
|
-
setInput("");
|
|
683
|
-
onCancelRef.current?.();
|
|
684
|
-
return;
|
|
685
|
-
}
|
|
686
|
-
if (key.return && inputRef.current.trim()) {
|
|
687
|
-
onSubmitRef.current(inputRef.current.trim());
|
|
688
|
-
setInput("");
|
|
689
|
-
return;
|
|
690
|
-
}
|
|
691
|
-
if (key.backspace || key.delete) {
|
|
692
|
-
setInput((prev) => prev.slice(0, -1));
|
|
693
|
-
return;
|
|
694
|
-
}
|
|
695
|
-
if (!key.ctrl && !key.meta && inputChar) {
|
|
696
|
-
setInput((prev) => prev + inputChar);
|
|
697
|
-
}
|
|
698
|
-
},
|
|
699
|
-
[inputRef, onSubmitRef, onCancelRef]
|
|
700
|
-
);
|
|
701
|
-
const reset = useCallback(() => {
|
|
702
|
-
setInput("");
|
|
703
|
-
}, []);
|
|
704
|
-
return { input, handleInput, reset };
|
|
705
|
-
};
|
|
706
|
-
var ListBrowser = ({
|
|
707
|
-
title,
|
|
708
|
-
items,
|
|
709
|
-
renderItem,
|
|
710
|
-
onSelect,
|
|
711
|
-
emptyMessage = "No items found",
|
|
712
|
-
extraKeyHandler,
|
|
713
|
-
onBack,
|
|
714
|
-
maxItems = UI_CONSTANTS.MAX_VISIBLE_LIST_ITEMS
|
|
715
|
-
}) => {
|
|
716
|
-
const { selectedIndex, handleNavigation } = useListNavigation({
|
|
717
|
-
items,
|
|
718
|
-
onSelect,
|
|
719
|
-
onBack
|
|
720
|
-
});
|
|
721
|
-
useInput((inputChar, key) => {
|
|
722
|
-
if (extraKeyHandler?.(inputChar, key, items[selectedIndex])) return;
|
|
723
|
-
handleNavigation(inputChar, key);
|
|
724
|
-
});
|
|
725
|
-
const { scrollOffset, displayItems } = useMemo(() => {
|
|
726
|
-
const offset = Math.max(0, Math.min(selectedIndex - maxItems + 1, items.length - maxItems));
|
|
727
|
-
return {
|
|
728
|
-
scrollOffset: offset,
|
|
729
|
-
displayItems: items.slice(offset, offset + maxItems)
|
|
730
|
-
};
|
|
731
|
-
}, [items, selectedIndex, maxItems]);
|
|
732
|
-
const hasMoreAbove = scrollOffset > 0;
|
|
733
|
-
const hasMoreBelow = scrollOffset + maxItems < items.length;
|
|
734
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
735
|
-
/* @__PURE__ */ jsxs(Box, { children: [
|
|
736
|
-
/* @__PURE__ */ jsx(Text, { color: "cyan", children: title }),
|
|
737
|
-
items.length > maxItems && /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
738
|
-
" ",
|
|
739
|
-
"(",
|
|
740
|
-
selectedIndex + 1,
|
|
741
|
-
"/",
|
|
742
|
-
items.length,
|
|
743
|
-
")"
|
|
744
|
-
] })
|
|
745
|
-
] }),
|
|
746
|
-
hasMoreAbove && /* @__PURE__ */ jsx(Text, { color: "gray", children: INDICATOR.ELLIPSIS }),
|
|
747
|
-
/* @__PURE__ */ jsx(Box, { flexDirection: "column", children: displayItems.length === 0 ? /* @__PURE__ */ jsx(Text, { color: "gray", children: emptyMessage }) : displayItems.map((item, index) => {
|
|
748
|
-
const actualIndex = scrollOffset + index;
|
|
749
|
-
return renderItem(item, actualIndex === selectedIndex, actualIndex);
|
|
750
|
-
}) }),
|
|
751
|
-
hasMoreBelow && /* @__PURE__ */ jsx(Text, { color: "gray", children: INDICATOR.ELLIPSIS })
|
|
752
|
-
] });
|
|
753
|
-
};
|
|
754
|
-
var BrowsingCheckpointsInput = ({
|
|
755
|
-
job,
|
|
756
|
-
checkpoints,
|
|
757
|
-
onCheckpointSelect,
|
|
758
|
-
onCheckpointResume,
|
|
759
|
-
onBack,
|
|
760
|
-
showEventsHint = true
|
|
761
|
-
}) => /* @__PURE__ */ jsx(
|
|
762
|
-
ListBrowser,
|
|
763
|
-
{
|
|
764
|
-
title: `Checkpoints for ${job.expertKey} ${KEY_HINTS.NAVIGATE} ${KEY_HINTS.RESUME} ${showEventsHint ? KEY_HINTS.EVENTS : ""} ${KEY_HINTS.BACK}`.trim(),
|
|
765
|
-
items: checkpoints,
|
|
766
|
-
onSelect: onCheckpointResume,
|
|
767
|
-
onBack,
|
|
768
|
-
emptyMessage: "No checkpoints found",
|
|
769
|
-
extraKeyHandler: (char, _key, selected) => {
|
|
770
|
-
if (char === "e" && selected) {
|
|
771
|
-
onCheckpointSelect(selected);
|
|
772
|
-
return true;
|
|
773
|
-
}
|
|
774
|
-
return false;
|
|
775
|
-
},
|
|
776
|
-
renderItem: (cp, isSelected) => /* @__PURE__ */ jsxs(Text, { color: isSelected ? "cyan" : "gray", children: [
|
|
777
|
-
isSelected ? ">" : " ",
|
|
778
|
-
" Step ",
|
|
779
|
-
cp.stepNumber,
|
|
780
|
-
" (",
|
|
781
|
-
cp.id,
|
|
782
|
-
")"
|
|
783
|
-
] }, cp.id)
|
|
784
|
-
}
|
|
785
|
-
);
|
|
786
|
-
var BrowsingEventDetailInput = ({ event, onBack }) => {
|
|
787
|
-
useInput((input, key) => {
|
|
788
|
-
if (input === "b" || key.escape) {
|
|
789
|
-
onBack();
|
|
790
|
-
}
|
|
791
|
-
});
|
|
792
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", paddingX: 1, children: [
|
|
793
|
-
/* @__PURE__ */ jsxs(Box, { marginBottom: 1, children: [
|
|
794
|
-
/* @__PURE__ */ jsx(Text, { bold: true, children: "Event Detail" }),
|
|
795
|
-
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
796
|
-
" ",
|
|
797
|
-
KEY_HINTS.BACK
|
|
798
|
-
] })
|
|
799
|
-
] }),
|
|
800
|
-
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [
|
|
801
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
802
|
-
/* @__PURE__ */ jsx(Text, { color: "gray", children: "Type: " }),
|
|
803
|
-
/* @__PURE__ */ jsx(Text, { color: "cyan", children: event.type })
|
|
804
|
-
] }),
|
|
805
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
806
|
-
/* @__PURE__ */ jsx(Text, { color: "gray", children: "Step: " }),
|
|
807
|
-
/* @__PURE__ */ jsx(Text, { children: event.stepNumber })
|
|
808
|
-
] }),
|
|
809
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
810
|
-
/* @__PURE__ */ jsx(Text, { color: "gray", children: "Timestamp: " }),
|
|
811
|
-
/* @__PURE__ */ jsx(Text, { children: formatTimestamp(event.timestamp) })
|
|
812
|
-
] }),
|
|
813
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
814
|
-
/* @__PURE__ */ jsx(Text, { color: "gray", children: "ID: " }),
|
|
815
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: event.id })
|
|
816
|
-
] }),
|
|
817
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
818
|
-
/* @__PURE__ */ jsx(Text, { color: "gray", children: "Run ID: " }),
|
|
819
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: event.runId })
|
|
820
|
-
] })
|
|
821
|
-
] })
|
|
822
|
-
] });
|
|
823
|
-
};
|
|
824
|
-
var BrowsingEventsInput = ({
|
|
825
|
-
checkpoint,
|
|
826
|
-
events,
|
|
827
|
-
onEventSelect,
|
|
828
|
-
onBack
|
|
829
|
-
}) => /* @__PURE__ */ jsx(
|
|
830
|
-
ListBrowser,
|
|
831
|
-
{
|
|
832
|
-
title: `Events for Step ${checkpoint.stepNumber} ${KEY_HINTS.NAVIGATE} ${KEY_HINTS.SELECT} ${KEY_HINTS.BACK}`,
|
|
833
|
-
items: events,
|
|
834
|
-
onSelect: onEventSelect,
|
|
835
|
-
onBack,
|
|
836
|
-
emptyMessage: "No events found",
|
|
837
|
-
renderItem: (ev, isSelected) => /* @__PURE__ */ jsxs(Text, { color: isSelected ? "cyan" : "gray", children: [
|
|
838
|
-
isSelected ? ">" : " ",
|
|
839
|
-
" [",
|
|
840
|
-
ev.type,
|
|
841
|
-
"] Step ",
|
|
842
|
-
ev.stepNumber,
|
|
843
|
-
" (",
|
|
844
|
-
formatTimestamp(ev.timestamp),
|
|
845
|
-
")"
|
|
846
|
-
] }, ev.id)
|
|
847
|
-
}
|
|
848
|
-
);
|
|
849
|
-
var useRuntimeInfo = (options) => {
|
|
850
|
-
const [runtimeInfo, setRuntimeInfo] = useState({
|
|
851
|
-
status: "initializing",
|
|
852
|
-
runtimeVersion: options.initialConfig.runtimeVersion,
|
|
853
|
-
expertName: options.initialExpertName,
|
|
854
|
-
model: options.initialConfig.model,
|
|
855
|
-
maxSteps: options.initialConfig.maxSteps,
|
|
856
|
-
maxRetries: options.initialConfig.maxRetries,
|
|
857
|
-
timeout: options.initialConfig.timeout,
|
|
858
|
-
activeSkills: [],
|
|
859
|
-
contextWindowUsage: options.initialConfig.contextWindowUsage
|
|
860
|
-
});
|
|
861
|
-
const handleEvent = useCallback((event) => {
|
|
862
|
-
if (event.type === "initializeRuntime") {
|
|
863
|
-
setRuntimeInfo((prev) => ({
|
|
864
|
-
runtimeVersion: event.runtimeVersion,
|
|
865
|
-
expertName: event.expertName,
|
|
866
|
-
model: event.model,
|
|
867
|
-
maxSteps: event.maxSteps,
|
|
868
|
-
maxRetries: event.maxRetries,
|
|
869
|
-
timeout: event.timeout,
|
|
870
|
-
currentStep: 1,
|
|
871
|
-
status: "running",
|
|
872
|
-
query: event.query,
|
|
873
|
-
statusChangedAt: Date.now(),
|
|
874
|
-
activeSkills: [],
|
|
875
|
-
contextWindowUsage: prev.contextWindowUsage
|
|
876
|
-
}));
|
|
877
|
-
return { initialized: true };
|
|
878
|
-
}
|
|
879
|
-
if (event.type === "skillConnected") {
|
|
880
|
-
setRuntimeInfo((prev) => ({
|
|
881
|
-
...prev,
|
|
882
|
-
activeSkills: [...prev.activeSkills, event.skillName]
|
|
883
|
-
}));
|
|
884
|
-
return null;
|
|
885
|
-
}
|
|
886
|
-
if (event.type === "skillDisconnected") {
|
|
887
|
-
setRuntimeInfo((prev) => ({
|
|
888
|
-
...prev,
|
|
889
|
-
activeSkills: prev.activeSkills.filter((s) => s !== event.skillName)
|
|
890
|
-
}));
|
|
891
|
-
return null;
|
|
892
|
-
}
|
|
893
|
-
if ("stepNumber" in event) {
|
|
894
|
-
const isComplete = event.type === "completeRun";
|
|
895
|
-
const isStopped = STOP_EVENT_TYPES.includes(event.type);
|
|
896
|
-
const checkpoint = "nextCheckpoint" in event ? event.nextCheckpoint : "checkpoint" in event ? event.checkpoint : void 0;
|
|
897
|
-
setRuntimeInfo((prev) => ({
|
|
898
|
-
...prev,
|
|
899
|
-
currentStep: event.stepNumber,
|
|
900
|
-
status: isComplete ? "completed" : isStopped ? "stopped" : "running",
|
|
901
|
-
...isComplete || isStopped ? { statusChangedAt: Date.now() } : {},
|
|
902
|
-
...checkpoint?.contextWindowUsage !== void 0 ? { contextWindowUsage: checkpoint.contextWindowUsage } : {}
|
|
903
|
-
}));
|
|
904
|
-
if (isComplete) return { completed: true };
|
|
905
|
-
if (isStopped) return { stopped: true };
|
|
906
|
-
}
|
|
907
|
-
return null;
|
|
908
|
-
}, []);
|
|
909
|
-
const setExpertName = useCallback((expertName) => {
|
|
910
|
-
setRuntimeInfo((prev) => ({ ...prev, expertName }));
|
|
911
|
-
}, []);
|
|
912
|
-
const setQuery = useCallback((query) => {
|
|
913
|
-
setRuntimeInfo((prev) => ({ ...prev, query }));
|
|
914
|
-
}, []);
|
|
915
|
-
const setCurrentStep = useCallback((step) => {
|
|
916
|
-
setRuntimeInfo((prev) => ({ ...prev, currentStep: step }));
|
|
917
|
-
}, []);
|
|
918
|
-
const setContextWindowUsage = useCallback((contextWindowUsage) => {
|
|
919
|
-
setRuntimeInfo((prev) => ({ ...prev, contextWindowUsage }));
|
|
920
|
-
}, []);
|
|
921
|
-
return {
|
|
922
|
-
runtimeInfo,
|
|
923
|
-
handleEvent,
|
|
924
|
-
setExpertName,
|
|
925
|
-
setQuery,
|
|
926
|
-
setCurrentStep,
|
|
927
|
-
setContextWindowUsage
|
|
928
|
-
};
|
|
929
|
-
};
|
|
930
|
-
var useExpertSelector = (options) => {
|
|
931
|
-
const { experts, onExpertSelect, extraKeyHandler } = options;
|
|
932
|
-
const [inputMode, setInputMode] = useState(false);
|
|
933
|
-
const { selectedIndex, handleNavigation } = useListNavigation({
|
|
934
|
-
items: experts,
|
|
935
|
-
onSelect: (expert) => onExpertSelect(expert.key)
|
|
936
|
-
});
|
|
937
|
-
const { input, handleInput, reset } = useTextInput({
|
|
938
|
-
onSubmit: (value) => {
|
|
939
|
-
onExpertSelect(value);
|
|
940
|
-
setInputMode(false);
|
|
941
|
-
},
|
|
942
|
-
onCancel: () => setInputMode(false)
|
|
943
|
-
});
|
|
944
|
-
const handleKeyInput = useCallback(
|
|
945
|
-
(inputChar, key) => {
|
|
946
|
-
if (inputMode) {
|
|
947
|
-
handleInput(inputChar, key);
|
|
948
|
-
return;
|
|
949
|
-
}
|
|
950
|
-
if (handleNavigation(inputChar, key)) return;
|
|
951
|
-
if (extraKeyHandler?.(inputChar, key)) return;
|
|
952
|
-
if (inputChar === "i") {
|
|
953
|
-
reset();
|
|
954
|
-
setInputMode(true);
|
|
955
|
-
}
|
|
956
|
-
},
|
|
957
|
-
[inputMode, handleInput, handleNavigation, extraKeyHandler, reset]
|
|
958
|
-
);
|
|
959
|
-
return {
|
|
960
|
-
inputMode,
|
|
961
|
-
input,
|
|
962
|
-
selectedIndex,
|
|
963
|
-
handleKeyInput
|
|
964
|
-
};
|
|
965
|
-
};
|
|
966
|
-
var ExpertList = ({
|
|
967
|
-
experts,
|
|
968
|
-
selectedIndex,
|
|
969
|
-
showSource = false,
|
|
970
|
-
inline = false,
|
|
971
|
-
maxItems
|
|
972
|
-
}) => {
|
|
973
|
-
const displayExperts = maxItems ? experts.slice(0, maxItems) : experts;
|
|
974
|
-
if (displayExperts.length === 0) {
|
|
975
|
-
return /* @__PURE__ */ jsx(Text, { color: "gray", children: "No experts found." });
|
|
976
|
-
}
|
|
977
|
-
const items = displayExperts.map((expert, index) => /* @__PURE__ */ jsxs(Text, { color: index === selectedIndex ? "cyan" : "gray", children: [
|
|
978
|
-
index === selectedIndex ? ">" : " ",
|
|
979
|
-
" ",
|
|
980
|
-
showSource ? expert.key : expert.name,
|
|
981
|
-
showSource && expert.source && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
982
|
-
" ",
|
|
983
|
-
/* @__PURE__ */ jsxs(Text, { color: expert.source === "configured" ? "green" : "yellow", children: [
|
|
984
|
-
"[",
|
|
985
|
-
expert.source === "configured" ? "config" : "recent",
|
|
986
|
-
"]"
|
|
987
|
-
] })
|
|
988
|
-
] }),
|
|
989
|
-
inline ? " " : ""
|
|
990
|
-
] }, expert.key));
|
|
991
|
-
return inline ? /* @__PURE__ */ jsx(Box, { children: items }) : /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: items });
|
|
992
|
-
};
|
|
993
|
-
var ExpertSelectorBase = ({
|
|
994
|
-
experts,
|
|
995
|
-
hint,
|
|
996
|
-
onExpertSelect,
|
|
997
|
-
showSource = false,
|
|
998
|
-
inline = false,
|
|
999
|
-
maxItems = UI_CONSTANTS.MAX_VISIBLE_LIST_ITEMS,
|
|
1000
|
-
extraKeyHandler
|
|
1001
|
-
}) => {
|
|
1002
|
-
const { inputMode, input, selectedIndex, handleKeyInput } = useExpertSelector({
|
|
1003
|
-
experts,
|
|
1004
|
-
onExpertSelect,
|
|
1005
|
-
extraKeyHandler
|
|
1006
|
-
});
|
|
1007
|
-
useInput(handleKeyInput);
|
|
1008
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: inline ? "row" : "column", children: [
|
|
1009
|
-
!inputMode && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
1010
|
-
/* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { color: "cyan", children: hint }) }),
|
|
1011
|
-
/* @__PURE__ */ jsx(
|
|
1012
|
-
ExpertList,
|
|
1013
|
-
{
|
|
1014
|
-
experts,
|
|
1015
|
-
selectedIndex,
|
|
1016
|
-
showSource,
|
|
1017
|
-
inline,
|
|
1018
|
-
maxItems
|
|
1019
|
-
}
|
|
1020
|
-
)
|
|
1021
|
-
] }),
|
|
1022
|
-
inputMode && /* @__PURE__ */ jsxs(Box, { children: [
|
|
1023
|
-
/* @__PURE__ */ jsx(Text, { color: "gray", children: "Expert: " }),
|
|
1024
|
-
/* @__PURE__ */ jsx(Text, { color: "white", children: input }),
|
|
1025
|
-
/* @__PURE__ */ jsx(Text, { color: "cyan", children: "_" })
|
|
1026
|
-
] })
|
|
1027
|
-
] });
|
|
1028
|
-
};
|
|
1029
|
-
var BrowsingExpertsInput = ({
|
|
1030
|
-
experts,
|
|
1031
|
-
onExpertSelect,
|
|
1032
|
-
onSwitchToHistory
|
|
1033
|
-
}) => {
|
|
1034
|
-
const extraKeyHandler = useCallback(
|
|
1035
|
-
(inputChar) => {
|
|
1036
|
-
if (inputChar === "h") {
|
|
1037
|
-
onSwitchToHistory();
|
|
1038
|
-
return true;
|
|
1039
|
-
}
|
|
1040
|
-
return false;
|
|
1041
|
-
},
|
|
1042
|
-
[onSwitchToHistory]
|
|
1043
|
-
);
|
|
1044
|
-
const hint = `Experts ${KEY_HINTS.NAVIGATE} ${KEY_HINTS.SELECT} ${KEY_HINTS.INPUT} ${KEY_HINTS.HISTORY} ${KEY_HINTS.CANCEL}`;
|
|
1045
|
-
return /* @__PURE__ */ jsx(
|
|
1046
|
-
ExpertSelectorBase,
|
|
1047
|
-
{
|
|
1048
|
-
experts,
|
|
1049
|
-
hint,
|
|
1050
|
-
onExpertSelect,
|
|
1051
|
-
showSource: true,
|
|
1052
|
-
maxItems: UI_CONSTANTS.MAX_VISIBLE_LIST_ITEMS,
|
|
1053
|
-
extraKeyHandler
|
|
1054
|
-
}
|
|
1055
|
-
);
|
|
1056
|
-
};
|
|
1057
|
-
var BrowsingHistoryInput = ({
|
|
1058
|
-
jobs,
|
|
1059
|
-
onJobSelect,
|
|
1060
|
-
onJobResume,
|
|
1061
|
-
onSwitchToExperts
|
|
1062
|
-
}) => /* @__PURE__ */ jsx(
|
|
1063
|
-
ListBrowser,
|
|
1064
|
-
{
|
|
1065
|
-
title: `Job History ${KEY_HINTS.NAVIGATE} ${KEY_HINTS.RESUME} ${KEY_HINTS.CHECKPOINTS} ${KEY_HINTS.NEW}`,
|
|
1066
|
-
items: jobs,
|
|
1067
|
-
onSelect: onJobResume,
|
|
1068
|
-
emptyMessage: "No jobs found. Press n to start a new job.",
|
|
1069
|
-
extraKeyHandler: (char, _key, selected) => {
|
|
1070
|
-
if (char === "c" && selected) {
|
|
1071
|
-
onJobSelect(selected);
|
|
1072
|
-
return true;
|
|
1073
|
-
}
|
|
1074
|
-
if (char === "n") {
|
|
1075
|
-
onSwitchToExperts();
|
|
1076
|
-
return true;
|
|
1077
|
-
}
|
|
1078
|
-
return false;
|
|
1079
|
-
},
|
|
1080
|
-
renderItem: (job, isSelected) => /* @__PURE__ */ jsxs(Text, { color: isSelected ? "cyan" : "gray", children: [
|
|
1081
|
-
isSelected ? ">" : " ",
|
|
1082
|
-
" ",
|
|
1083
|
-
job.expertKey,
|
|
1084
|
-
" - ",
|
|
1085
|
-
job.totalSteps,
|
|
1086
|
-
" steps (",
|
|
1087
|
-
job.jobId,
|
|
1088
|
-
") (",
|
|
1089
|
-
formatTimestamp(job.startedAt),
|
|
1090
|
-
")"
|
|
1091
|
-
] }, job.jobId)
|
|
1092
|
-
}
|
|
1093
|
-
);
|
|
1094
|
-
var BrowserRouter = ({ inputState, showEventsHint = true }) => {
|
|
1095
|
-
const ctx = useInputAreaContext();
|
|
1096
|
-
const handleEventSelect = useCallback(
|
|
1097
|
-
(event) => {
|
|
1098
|
-
if (inputState.type === "browsingEvents") {
|
|
1099
|
-
ctx.onEventSelect(inputState, event);
|
|
1100
|
-
}
|
|
1101
|
-
},
|
|
1102
|
-
[ctx.onEventSelect, inputState]
|
|
1103
|
-
);
|
|
1104
|
-
switch (inputState.type) {
|
|
1105
|
-
case "browsingHistory":
|
|
1106
|
-
return /* @__PURE__ */ jsx(
|
|
1107
|
-
BrowsingHistoryInput,
|
|
1108
|
-
{
|
|
1109
|
-
jobs: inputState.jobs,
|
|
1110
|
-
onJobSelect: ctx.onJobSelect,
|
|
1111
|
-
onJobResume: ctx.onJobResume,
|
|
1112
|
-
onSwitchToExperts: ctx.onSwitchToExperts
|
|
1113
|
-
}
|
|
1114
|
-
);
|
|
1115
|
-
case "browsingExperts":
|
|
1116
|
-
return /* @__PURE__ */ jsx(
|
|
1117
|
-
BrowsingExpertsInput,
|
|
1118
|
-
{
|
|
1119
|
-
experts: inputState.experts,
|
|
1120
|
-
onExpertSelect: ctx.onExpertSelect,
|
|
1121
|
-
onSwitchToHistory: ctx.onSwitchToHistory
|
|
1122
|
-
}
|
|
1123
|
-
);
|
|
1124
|
-
case "browsingCheckpoints":
|
|
1125
|
-
return /* @__PURE__ */ jsx(
|
|
1126
|
-
BrowsingCheckpointsInput,
|
|
1127
|
-
{
|
|
1128
|
-
job: inputState.job,
|
|
1129
|
-
checkpoints: inputState.checkpoints,
|
|
1130
|
-
onCheckpointSelect: ctx.onCheckpointSelect,
|
|
1131
|
-
onCheckpointResume: ctx.onCheckpointResume,
|
|
1132
|
-
onBack: ctx.onBack,
|
|
1133
|
-
showEventsHint
|
|
1134
|
-
}
|
|
1135
|
-
);
|
|
1136
|
-
case "browsingEvents":
|
|
1137
|
-
return /* @__PURE__ */ jsx(
|
|
1138
|
-
BrowsingEventsInput,
|
|
1139
|
-
{
|
|
1140
|
-
checkpoint: inputState.checkpoint,
|
|
1141
|
-
events: inputState.events,
|
|
1142
|
-
onEventSelect: handleEventSelect,
|
|
1143
|
-
onBack: ctx.onBack
|
|
1144
|
-
}
|
|
1145
|
-
);
|
|
1146
|
-
case "browsingEventDetail":
|
|
1147
|
-
return /* @__PURE__ */ jsx(BrowsingEventDetailInput, { event: inputState.selectedEvent, onBack: ctx.onBack });
|
|
1148
|
-
default:
|
|
1149
|
-
return assertNever(inputState);
|
|
1150
|
-
}
|
|
1151
|
-
};
|
|
1152
|
-
var ActionRowSimple = ({
|
|
1153
|
-
indicatorColor,
|
|
1154
|
-
text,
|
|
1155
|
-
textDimColor = false
|
|
1156
|
-
}) => /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
|
|
1157
|
-
/* @__PURE__ */ jsx(Box, { paddingRight: 1, children: /* @__PURE__ */ jsx(Text, { color: indicatorColor, children: INDICATOR.BULLET }) }),
|
|
1158
|
-
/* @__PURE__ */ jsx(Text, { color: "white", dimColor: textDimColor, children: text })
|
|
1159
|
-
] }) });
|
|
1160
|
-
var ActionRow = ({ indicatorColor, label, summary, children }) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
1161
|
-
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
1162
|
-
/* @__PURE__ */ jsx(Text, { color: indicatorColor, children: INDICATOR.BULLET }),
|
|
1163
|
-
/* @__PURE__ */ jsx(Text, { color: "white", children: label }),
|
|
1164
|
-
summary && /* @__PURE__ */ jsx(Text, { color: "white", dimColor: true, children: summary })
|
|
1165
|
-
] }),
|
|
1166
|
-
/* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
|
|
1167
|
-
/* @__PURE__ */ jsx(Box, { paddingRight: 1, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: INDICATOR.TREE }) }),
|
|
1168
|
-
children
|
|
1169
|
-
] })
|
|
1170
|
-
] });
|
|
1171
|
-
var CheckpointActionRow = ({ action }) => {
|
|
1172
|
-
if (action.type === "parallelGroup") {
|
|
1173
|
-
return renderParallelGroup(action);
|
|
1174
|
-
}
|
|
1175
|
-
const actionElement = renderAction(action);
|
|
1176
|
-
if (!actionElement) return null;
|
|
1177
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
1178
|
-
action.reasoning && renderReasoning(action.reasoning),
|
|
1179
|
-
actionElement
|
|
1180
|
-
] });
|
|
1181
|
-
};
|
|
1182
|
-
function renderParallelGroup(group) {
|
|
1183
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
1184
|
-
group.reasoning && renderReasoning(group.reasoning),
|
|
1185
|
-
group.activities.map((activity, index) => {
|
|
1186
|
-
const actionElement = renderAction(activity);
|
|
1187
|
-
if (!actionElement) return null;
|
|
1188
|
-
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: actionElement }, activity.id || `${activity.type}-${index}`);
|
|
1189
|
-
})
|
|
1190
|
-
] });
|
|
1191
|
-
}
|
|
1192
|
-
function renderReasoning(text) {
|
|
1193
|
-
const lines = text.split("\n");
|
|
1194
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: "white", label: "Reasoning", children: /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: lines.map((line, idx) => /* @__PURE__ */ jsx(Text, { dimColor: true, wrap: "wrap", children: line }, `reasoning-${idx}`)) }) });
|
|
1195
|
-
}
|
|
1196
|
-
function renderAction(action) {
|
|
1197
|
-
const color = action.type === "error" || "error" in action && action.error ? "red" : "green";
|
|
1198
|
-
switch (action.type) {
|
|
1199
|
-
case "query":
|
|
1200
|
-
return renderQuery(action.text, action.runId);
|
|
1201
|
-
case "retry":
|
|
1202
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: "yellow", label: "Retry", children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: action.message || action.error }) });
|
|
1203
|
-
case "complete":
|
|
1204
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: "green", label: "Run Results", children: /* @__PURE__ */ jsx(Text, { children: action.text }) });
|
|
1205
|
-
case "attemptCompletion": {
|
|
1206
|
-
if (action.error) {
|
|
1207
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: "red", label: "Completion Failed", children: /* @__PURE__ */ jsx(Text, { color: "red", children: action.error }) });
|
|
1208
|
-
}
|
|
1209
|
-
const remaining = action.remainingTodos?.filter((t) => !t.completed) ?? [];
|
|
1210
|
-
if (remaining.length > 0) {
|
|
1211
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: "yellow", label: "Completion Blocked", children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1212
|
-
remaining.length,
|
|
1213
|
-
" remaining task",
|
|
1214
|
-
remaining.length > 1 ? "s" : ""
|
|
1215
|
-
] }) });
|
|
1216
|
-
}
|
|
1217
|
-
return /* @__PURE__ */ jsx(ActionRowSimple, { indicatorColor: "green", text: "Completion Accepted" });
|
|
1218
|
-
}
|
|
1219
|
-
case "todo":
|
|
1220
|
-
return renderTodo(action, color);
|
|
1221
|
-
case "clearTodo":
|
|
1222
|
-
return /* @__PURE__ */ jsx(ActionRowSimple, { indicatorColor: color, text: "Todo Cleared" });
|
|
1223
|
-
case "readTextFile":
|
|
1224
|
-
return renderReadTextFile(action, color);
|
|
1225
|
-
case "writeTextFile":
|
|
1226
|
-
return renderWriteTextFile(action, color);
|
|
1227
|
-
case "editTextFile":
|
|
1228
|
-
return renderEditTextFile(action, color);
|
|
1229
|
-
case "appendTextFile":
|
|
1230
|
-
return renderAppendTextFile(action, color);
|
|
1231
|
-
case "deleteFile":
|
|
1232
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: color, label: "Delete", summary: shortenPath(action.path), children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Removed" }) });
|
|
1233
|
-
case "deleteDirectory":
|
|
1234
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: color, label: "Delete Dir", summary: shortenPath(action.path), children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1235
|
-
"Removed",
|
|
1236
|
-
action.recursive ? " (recursive)" : ""
|
|
1237
|
-
] }) });
|
|
1238
|
-
case "moveFile":
|
|
1239
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: color, label: "Move", summary: shortenPath(action.source), children: /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1240
|
-
"\u2192 ",
|
|
1241
|
-
shortenPath(action.destination, 30)
|
|
1242
|
-
] }) });
|
|
1243
|
-
case "createDirectory":
|
|
1244
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: color, label: "Mkdir", summary: shortenPath(action.path), children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Created" }) });
|
|
1245
|
-
case "listDirectory":
|
|
1246
|
-
return renderListDirectory(action, color);
|
|
1247
|
-
case "getFileInfo":
|
|
1248
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: color, label: "Info", summary: shortenPath(action.path), children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Fetched" }) });
|
|
1249
|
-
case "readPdfFile":
|
|
1250
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: color, label: "PDF", summary: shortenPath(action.path), children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Read" }) });
|
|
1251
|
-
case "readImageFile":
|
|
1252
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: color, label: "Image", summary: shortenPath(action.path), children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: "Read" }) });
|
|
1253
|
-
case "exec":
|
|
1254
|
-
return renderExec(action, color);
|
|
1255
|
-
case "delegate":
|
|
1256
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: "yellow", label: action.delegateExpertKey, children: /* @__PURE__ */ jsx(
|
|
1257
|
-
Text,
|
|
1258
|
-
{
|
|
1259
|
-
dimColor: true,
|
|
1260
|
-
children: `{"query":"${truncateText(action.query, UI_CONSTANTS.TRUNCATE_TEXT_MEDIUM)}"}`
|
|
1261
|
-
}
|
|
1262
|
-
) });
|
|
1263
|
-
case "delegationComplete":
|
|
1264
|
-
return /* @__PURE__ */ jsx(
|
|
1265
|
-
ActionRowSimple,
|
|
1266
|
-
{
|
|
1267
|
-
indicatorColor: "green",
|
|
1268
|
-
text: `Delegation Complete (${action.count} delegate${action.count > 1 ? "s" : ""} returned)`
|
|
1269
|
-
}
|
|
1270
|
-
);
|
|
1271
|
-
case "interactiveTool":
|
|
1272
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: "yellow", label: `Interactive: ${action.toolName}`, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: truncateText(JSON.stringify(action.args), UI_CONSTANTS.TRUNCATE_TEXT_MEDIUM) }) });
|
|
1273
|
-
case "generalTool":
|
|
1274
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: color, label: action.toolName, children: /* @__PURE__ */ jsx(Text, { dimColor: true, children: truncateText(JSON.stringify(action.args), UI_CONSTANTS.TRUNCATE_TEXT_MEDIUM) }) });
|
|
1275
|
-
case "error":
|
|
1276
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: "red", label: action.errorName ?? "Error", children: /* @__PURE__ */ jsx(Text, { color: "red", children: action.error ?? "Unknown error" }) });
|
|
1277
|
-
default: {
|
|
1278
|
-
return null;
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
function renderTodo(action, color) {
|
|
1283
|
-
const { newTodos, completedTodos, todos } = action;
|
|
1284
|
-
const hasNewTodos = newTodos && newTodos.length > 0;
|
|
1285
|
-
const hasCompletedTodos = completedTodos && completedTodos.length > 0;
|
|
1286
|
-
if (!hasNewTodos && !hasCompletedTodos) {
|
|
1287
|
-
return /* @__PURE__ */ jsx(ActionRowSimple, { indicatorColor: color, text: "Todo No changes" });
|
|
1288
|
-
}
|
|
1289
|
-
const labelParts = [];
|
|
1290
|
-
if (hasNewTodos) {
|
|
1291
|
-
labelParts.push(`Added ${newTodos.length} task${newTodos.length > 1 ? "s" : ""}`);
|
|
1292
|
-
}
|
|
1293
|
-
if (hasCompletedTodos) {
|
|
1294
|
-
labelParts.push(
|
|
1295
|
-
`Completed ${completedTodos.length} task${completedTodos.length > 1 ? "s" : ""}`
|
|
1296
|
-
);
|
|
1297
|
-
}
|
|
1298
|
-
const label = `Todo ${labelParts.join(", ")}`;
|
|
1299
|
-
const completedTitles = hasCompletedTodos ? completedTodos.map((id) => todos.find((t) => t.id === id)?.title).filter((t) => t !== void 0) : [];
|
|
1300
|
-
if (!hasNewTodos && completedTitles.length === 0) {
|
|
1301
|
-
return /* @__PURE__ */ jsx(ActionRowSimple, { indicatorColor: color, text: label });
|
|
1302
|
-
}
|
|
1303
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: color, label, children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
1304
|
-
hasNewTodos && newTodos.slice(0, RENDER_CONSTANTS.NEW_TODO_MAX_PREVIEW).map((todo, idx) => /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1305
|
-
"\u25CB ",
|
|
1306
|
-
todo
|
|
1307
|
-
] }, `todo-${idx}`)),
|
|
1308
|
-
hasNewTodos && newTodos.length > RENDER_CONSTANTS.NEW_TODO_MAX_PREVIEW && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1309
|
-
"... +",
|
|
1310
|
-
newTodos.length - RENDER_CONSTANTS.NEW_TODO_MAX_PREVIEW,
|
|
1311
|
-
" more"
|
|
1312
|
-
] }),
|
|
1313
|
-
completedTitles.slice(0, RENDER_CONSTANTS.NEW_TODO_MAX_PREVIEW).map((title, idx) => /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1314
|
-
"\u2713 ",
|
|
1315
|
-
title
|
|
1316
|
-
] }, `completed-${idx}`)),
|
|
1317
|
-
completedTitles.length > RENDER_CONSTANTS.NEW_TODO_MAX_PREVIEW && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1318
|
-
"... +",
|
|
1319
|
-
completedTitles.length - RENDER_CONSTANTS.NEW_TODO_MAX_PREVIEW,
|
|
1320
|
-
" more"
|
|
1321
|
-
] })
|
|
1322
|
-
] }) });
|
|
1323
|
-
}
|
|
1324
|
-
function renderReadTextFile(action, color) {
|
|
1325
|
-
const { path: path6, content, from, to } = action;
|
|
1326
|
-
const lineRange = from !== void 0 && to !== void 0 ? `#${from}-${to}` : "";
|
|
1327
|
-
const lines = content?.split("\n") ?? [];
|
|
1328
|
-
return /* @__PURE__ */ jsx(
|
|
1329
|
-
ActionRow,
|
|
1330
|
-
{
|
|
1331
|
-
indicatorColor: color,
|
|
1332
|
-
label: "Read Text File",
|
|
1333
|
-
summary: `${shortenPath(path6)}${lineRange}`,
|
|
1334
|
-
children: /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: lines.map((line, idx) => /* @__PURE__ */ jsx(Box, { flexDirection: "row", gap: 1, children: /* @__PURE__ */ jsx(Text, { color: "white", dimColor: true, children: line }) }, `read-${idx}`)) })
|
|
1335
|
-
}
|
|
1336
|
-
);
|
|
1337
|
-
}
|
|
1338
|
-
function renderWriteTextFile(action, color) {
|
|
1339
|
-
const { path: path6, text } = action;
|
|
1340
|
-
const lines = text.split("\n");
|
|
1341
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: color, label: "Write Text File", summary: shortenPath(path6), children: /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: lines.map((line, idx) => /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
1342
|
-
/* @__PURE__ */ jsx(Text, { color: "green", dimColor: true, children: "+" }),
|
|
1343
|
-
/* @__PURE__ */ jsx(Text, { color: "white", dimColor: true, children: line })
|
|
1344
|
-
] }, `write-${idx}`)) }) });
|
|
1345
|
-
}
|
|
1346
|
-
function renderEditTextFile(action, color) {
|
|
1347
|
-
const { path: path6, oldText, newText } = action;
|
|
1348
|
-
const oldLines = oldText.split("\n");
|
|
1349
|
-
const newLines = newText.split("\n");
|
|
1350
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: color, label: "Edit Text File", summary: shortenPath(path6), children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
1351
|
-
oldLines.map((line, idx) => /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
1352
|
-
/* @__PURE__ */ jsx(Text, { color: "red", dimColor: true, children: "-" }),
|
|
1353
|
-
/* @__PURE__ */ jsx(Text, { color: "white", dimColor: true, children: line })
|
|
1354
|
-
] }, `old-${idx}`)),
|
|
1355
|
-
newLines.map((line, idx) => /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
1356
|
-
/* @__PURE__ */ jsx(Text, { color: "green", dimColor: true, children: "+" }),
|
|
1357
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: line })
|
|
1358
|
-
] }, `new-${idx}`))
|
|
1359
|
-
] }) });
|
|
1360
|
-
}
|
|
1361
|
-
function renderAppendTextFile(action, color) {
|
|
1362
|
-
const { path: path6, text } = action;
|
|
1363
|
-
const lines = text.split("\n");
|
|
1364
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: color, label: "Append Text File", summary: shortenPath(path6), children: /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: lines.map((line, idx) => /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 1, children: [
|
|
1365
|
-
/* @__PURE__ */ jsx(Text, { color: "green", dimColor: true, children: "+" }),
|
|
1366
|
-
/* @__PURE__ */ jsx(Text, { color: "white", dimColor: true, children: line })
|
|
1367
|
-
] }, `append-${idx}`)) }) });
|
|
1368
|
-
}
|
|
1369
|
-
function renderListDirectory(action, color) {
|
|
1370
|
-
const { path: path6, items } = action;
|
|
1371
|
-
const itemLines = items?.map((item) => `${item.type === "directory" ? "\u{1F4C1}" : "\u{1F4C4}"} ${item.name}`) ?? [];
|
|
1372
|
-
const { visible, remaining } = summarizeOutput(itemLines, RENDER_CONSTANTS.LIST_DIR_MAX_ITEMS);
|
|
1373
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: color, label: "List", summary: shortenPath(path6), children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
1374
|
-
visible.map((line, idx) => /* @__PURE__ */ jsx(Text, { dimColor: true, children: truncateText(line, UI_CONSTANTS.TRUNCATE_TEXT_DEFAULT) }, `dir-${idx}`)),
|
|
1375
|
-
remaining > 0 && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1376
|
-
"... +",
|
|
1377
|
-
remaining,
|
|
1378
|
-
" more"
|
|
1379
|
-
] })
|
|
1380
|
-
] }) });
|
|
1381
|
-
}
|
|
1382
|
-
function renderExec(action, color) {
|
|
1383
|
-
const { command, args, cwd, output } = action;
|
|
1384
|
-
const cwdPart = cwd ? ` ${shortenPath(cwd, 40)}` : "";
|
|
1385
|
-
const cmdLine = truncateText(
|
|
1386
|
-
`${command} ${args.join(" ")}${cwdPart}`,
|
|
1387
|
-
UI_CONSTANTS.TRUNCATE_TEXT_DEFAULT
|
|
1388
|
-
);
|
|
1389
|
-
const outputLines = output?.split("\n") ?? [];
|
|
1390
|
-
const { visible, remaining } = summarizeOutput(
|
|
1391
|
-
outputLines,
|
|
1392
|
-
RENDER_CONSTANTS.EXEC_OUTPUT_MAX_LINES
|
|
1393
|
-
);
|
|
1394
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: color, label: `Bash ${cmdLine}`, children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
1395
|
-
visible.map((line, idx) => /* @__PURE__ */ jsx(Text, { dimColor: true, children: truncateText(line, UI_CONSTANTS.TRUNCATE_TEXT_DEFAULT) }, `out-${idx}`)),
|
|
1396
|
-
remaining > 0 && /* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1397
|
-
"... +",
|
|
1398
|
-
remaining,
|
|
1399
|
-
" more"
|
|
1400
|
-
] })
|
|
1401
|
-
] }) });
|
|
1402
|
-
}
|
|
1403
|
-
function renderQuery(text, runId) {
|
|
1404
|
-
const lines = text.split("\n");
|
|
1405
|
-
const shortRunId = runId.slice(0, 8);
|
|
1406
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: "cyan", label: "Query", summary: `(${shortRunId})`, children: /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: lines.map((line, idx) => /* @__PURE__ */ jsx(Text, { dimColor: true, wrap: "wrap", children: line }, `query-${idx}`)) }) });
|
|
1407
|
-
}
|
|
1408
|
-
var RunSetting = ({
|
|
1409
|
-
info,
|
|
1410
|
-
eventCount,
|
|
1411
|
-
isEditing,
|
|
1412
|
-
expertName,
|
|
1413
|
-
onQuerySubmit
|
|
1414
|
-
}) => {
|
|
1415
|
-
const { input, handleInput } = useTextInput({
|
|
1416
|
-
onSubmit: onQuerySubmit ?? (() => {
|
|
1417
|
-
})
|
|
1418
|
-
});
|
|
1419
|
-
useInput(handleInput, { isActive: isEditing });
|
|
1420
|
-
const displayExpertName = expertName ?? info.expertName;
|
|
1421
|
-
const skills = info.activeSkills.length > 0 ? info.activeSkills.join(", ") : "";
|
|
1422
|
-
const step = info.currentStep !== void 0 ? String(info.currentStep) : "";
|
|
1423
|
-
const usagePercent = (info.contextWindowUsage * 100).toFixed(1);
|
|
1424
|
-
return /* @__PURE__ */ jsxs(
|
|
1425
|
-
Box,
|
|
1426
|
-
{
|
|
1427
|
-
flexDirection: "column",
|
|
1428
|
-
borderStyle: "single",
|
|
1429
|
-
borderColor: "gray",
|
|
1430
|
-
borderTop: true,
|
|
1431
|
-
borderBottom: false,
|
|
1432
|
-
borderLeft: false,
|
|
1433
|
-
borderRight: false,
|
|
1434
|
-
children: [
|
|
1435
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
1436
|
-
/* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "Perstack" }),
|
|
1437
|
-
info.runtimeVersion && /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
1438
|
-
" (v",
|
|
1439
|
-
info.runtimeVersion,
|
|
1440
|
-
")"
|
|
1441
|
-
] })
|
|
1442
|
-
] }),
|
|
1443
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
1444
|
-
/* @__PURE__ */ jsx(Text, { color: "gray", children: "Expert: " }),
|
|
1445
|
-
/* @__PURE__ */ jsx(Text, { color: "white", children: displayExpertName }),
|
|
1446
|
-
/* @__PURE__ */ jsx(Text, { color: "gray", children: " / Skills: " }),
|
|
1447
|
-
/* @__PURE__ */ jsx(Text, { color: "green", children: skills })
|
|
1448
|
-
] }),
|
|
1449
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
1450
|
-
/* @__PURE__ */ jsx(Text, { color: "gray", children: "Status: " }),
|
|
1451
|
-
/* @__PURE__ */ jsx(
|
|
1452
|
-
Text,
|
|
1453
|
-
{
|
|
1454
|
-
color: info.status === "running" ? "green" : info.status === "completed" ? "cyan" : "yellow",
|
|
1455
|
-
children: info.status
|
|
1456
|
-
}
|
|
1457
|
-
),
|
|
1458
|
-
/* @__PURE__ */ jsx(Text, { color: "gray", children: " / Step: " }),
|
|
1459
|
-
/* @__PURE__ */ jsx(Text, { color: "white", children: step }),
|
|
1460
|
-
/* @__PURE__ */ jsx(Text, { color: "gray", children: " / Events: " }),
|
|
1461
|
-
/* @__PURE__ */ jsx(Text, { color: "white", children: eventCount }),
|
|
1462
|
-
/* @__PURE__ */ jsx(Text, { color: "gray", children: " / Usage: " }),
|
|
1463
|
-
/* @__PURE__ */ jsxs(Text, { color: "white", children: [
|
|
1464
|
-
usagePercent,
|
|
1465
|
-
"%"
|
|
1466
|
-
] })
|
|
1467
|
-
] }),
|
|
1468
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
1469
|
-
/* @__PURE__ */ jsx(Text, { color: "gray", children: "Model: " }),
|
|
1470
|
-
/* @__PURE__ */ jsx(Text, { color: "white", children: info.model })
|
|
1471
|
-
] }),
|
|
1472
|
-
/* @__PURE__ */ jsxs(Box, { children: [
|
|
1473
|
-
/* @__PURE__ */ jsx(Text, { color: "gray", children: "Query: " }),
|
|
1474
|
-
isEditing ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1475
|
-
/* @__PURE__ */ jsx(Text, { color: "white", children: input }),
|
|
1476
|
-
/* @__PURE__ */ jsx(Text, { color: "cyan", children: "_" })
|
|
1477
|
-
] }) : /* @__PURE__ */ jsx(Text, { color: "white", children: info.query })
|
|
1478
|
-
] })
|
|
1479
|
-
]
|
|
1480
|
-
}
|
|
1481
|
-
);
|
|
1482
|
-
};
|
|
1483
|
-
var StreamingDisplay = ({ streaming }) => {
|
|
1484
|
-
const activeRuns = Object.entries(streaming.runs).filter(
|
|
1485
|
-
([, run]) => run.isReasoningActive || run.isRunResultActive
|
|
1486
|
-
);
|
|
1487
|
-
if (activeRuns.length === 0) return null;
|
|
1488
|
-
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", marginY: 1, children: activeRuns.map(([runId, run]) => /* @__PURE__ */ jsx(StreamingRunSection, { run }, runId)) });
|
|
1489
|
-
};
|
|
1490
|
-
function StreamingRunSection({ run }) {
|
|
1491
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
1492
|
-
run.isReasoningActive && run.reasoning !== void 0 && /* @__PURE__ */ jsx(StreamingReasoning, { expertKey: run.expertKey, text: run.reasoning }),
|
|
1493
|
-
run.isRunResultActive && run.runResult !== void 0 && /* @__PURE__ */ jsx(StreamingRunResult, { expertKey: run.expertKey, text: run.runResult })
|
|
1494
|
-
] });
|
|
1495
|
-
}
|
|
1496
|
-
function StreamingReasoning({
|
|
1497
|
-
expertKey,
|
|
1498
|
-
text
|
|
1499
|
-
}) {
|
|
1500
|
-
const lines = text.split("\n");
|
|
1501
|
-
const label = `[${formatExpertKey(expertKey)}] Reasoning...`;
|
|
1502
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: "cyan", label, children: /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: lines.map((line, idx) => /* @__PURE__ */ jsx(Text, { dimColor: true, wrap: "wrap", children: line }, `streaming-reasoning-${idx}`)) }) });
|
|
1503
|
-
}
|
|
1504
|
-
function StreamingRunResult({
|
|
1505
|
-
expertKey,
|
|
1506
|
-
text
|
|
1507
|
-
}) {
|
|
1508
|
-
const lines = text.split("\n");
|
|
1509
|
-
const label = `[${formatExpertKey(expertKey)}] Generating...`;
|
|
1510
|
-
return /* @__PURE__ */ jsx(ActionRow, { indicatorColor: "green", label, children: /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: lines.map((line, idx) => /* @__PURE__ */ jsx(Text, { wrap: "wrap", children: line }, `streaming-run-result-${idx}`)) }) });
|
|
1511
|
-
}
|
|
1512
|
-
function formatExpertKey(expertKey) {
|
|
1513
|
-
const atIndex = expertKey.lastIndexOf("@");
|
|
1514
|
-
if (atIndex > 0) {
|
|
1515
|
-
return expertKey.substring(0, atIndex);
|
|
1516
|
-
}
|
|
1517
|
-
return expertKey;
|
|
1518
|
-
}
|
|
1519
|
-
function getActivityKey(activityOrGroup, index) {
|
|
1520
|
-
return activityOrGroup.id || `activity-${index}`;
|
|
1521
|
-
}
|
|
1522
|
-
var RunBox = ({ node, isRoot }) => {
|
|
1523
|
-
if (isRoot) {
|
|
1524
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
1525
|
-
node.activities.map((activity, index) => /* @__PURE__ */ jsx(CheckpointActionRow, { action: activity }, getActivityKey(activity, index))),
|
|
1526
|
-
node.children.map((child) => /* @__PURE__ */ jsx(RunBox, { node: child, isRoot: false }, child.runId))
|
|
1527
|
-
] });
|
|
1528
|
-
}
|
|
1529
|
-
const shortRunId = node.runId.slice(0, 8);
|
|
1530
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "gray", marginLeft: 1, children: [
|
|
1531
|
-
/* @__PURE__ */ jsxs(Text, { dimColor: true, bold: true, children: [
|
|
1532
|
-
"[",
|
|
1533
|
-
node.expertKey,
|
|
1534
|
-
"] ",
|
|
1535
|
-
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
1536
|
-
"(",
|
|
1537
|
-
shortRunId,
|
|
1538
|
-
")"
|
|
1539
|
-
] })
|
|
1540
|
-
] }),
|
|
1541
|
-
node.activities.map((activity, index) => /* @__PURE__ */ jsx(CheckpointActionRow, { action: activity }, getActivityKey(activity, index))),
|
|
1542
|
-
node.children.map((child) => /* @__PURE__ */ jsx(RunBox, { node: child, isRoot: false }, child.runId))
|
|
1543
|
-
] });
|
|
1544
|
-
};
|
|
1545
|
-
function getActivityProps(activityOrGroup) {
|
|
1546
|
-
if (activityOrGroup.type === "parallelGroup") {
|
|
1547
|
-
const group = activityOrGroup;
|
|
1548
|
-
const firstActivity = group.activities[0];
|
|
1549
|
-
return {
|
|
1550
|
-
runId: group.runId,
|
|
1551
|
-
expertKey: group.expertKey,
|
|
1552
|
-
delegatedBy: firstActivity?.delegatedBy
|
|
1553
|
-
};
|
|
1554
|
-
}
|
|
1555
|
-
return activityOrGroup;
|
|
1556
|
-
}
|
|
1557
|
-
var ActivityLogPanel = ({ activities }) => {
|
|
1558
|
-
const rootNodes = useMemo(() => {
|
|
1559
|
-
const nodeMap = /* @__PURE__ */ new Map();
|
|
1560
|
-
const roots = [];
|
|
1561
|
-
const orphanChildren = /* @__PURE__ */ new Map();
|
|
1562
|
-
for (const activityOrGroup of activities) {
|
|
1563
|
-
const { runId, expertKey, delegatedBy } = getActivityProps(activityOrGroup);
|
|
1564
|
-
let node = nodeMap.get(runId);
|
|
1565
|
-
if (!node) {
|
|
1566
|
-
node = {
|
|
1567
|
-
runId,
|
|
1568
|
-
expertKey,
|
|
1569
|
-
activities: [],
|
|
1570
|
-
children: []
|
|
1571
|
-
};
|
|
1572
|
-
nodeMap.set(runId, node);
|
|
1573
|
-
const waitingChildren = orphanChildren.get(runId);
|
|
1574
|
-
if (waitingChildren) {
|
|
1575
|
-
for (const child of waitingChildren) {
|
|
1576
|
-
node.children.push(child);
|
|
1577
|
-
const rootIndex = roots.indexOf(child);
|
|
1578
|
-
if (rootIndex !== -1) {
|
|
1579
|
-
roots.splice(rootIndex, 1);
|
|
1580
|
-
}
|
|
1581
|
-
}
|
|
1582
|
-
orphanChildren.delete(runId);
|
|
1583
|
-
}
|
|
1584
|
-
if (delegatedBy) {
|
|
1585
|
-
const parentNode = nodeMap.get(delegatedBy.runId);
|
|
1586
|
-
if (parentNode) {
|
|
1587
|
-
parentNode.children.push(node);
|
|
1588
|
-
} else {
|
|
1589
|
-
const orphans = orphanChildren.get(delegatedBy.runId) ?? [];
|
|
1590
|
-
orphans.push(node);
|
|
1591
|
-
orphanChildren.set(delegatedBy.runId, orphans);
|
|
1592
|
-
roots.push(node);
|
|
1593
|
-
}
|
|
1594
|
-
} else {
|
|
1595
|
-
roots.push(node);
|
|
1596
|
-
}
|
|
1597
|
-
}
|
|
1598
|
-
node.activities.push(activityOrGroup);
|
|
1599
|
-
}
|
|
1600
|
-
return roots;
|
|
1601
|
-
}, [activities]);
|
|
1602
|
-
return /* @__PURE__ */ jsx(Box, { flexDirection: "column", children: rootNodes.map((node) => /* @__PURE__ */ jsx(RunBox, { node, isRoot: true }, node.runId)) });
|
|
1603
|
-
};
|
|
1604
|
-
var ContinueInputPanel = ({
|
|
1605
|
-
isActive,
|
|
1606
|
-
runStatus,
|
|
1607
|
-
onSubmit
|
|
1608
|
-
}) => {
|
|
1609
|
-
const { input, handleInput } = useTextInput({
|
|
1610
|
-
onSubmit: (newQuery) => {
|
|
1611
|
-
if (isActive && newQuery.trim()) {
|
|
1612
|
-
onSubmit(newQuery.trim());
|
|
1613
|
-
}
|
|
1614
|
-
}
|
|
1615
|
-
});
|
|
1616
|
-
useInput(handleInput, { isActive });
|
|
1617
|
-
if (runStatus === "running") {
|
|
1618
|
-
return null;
|
|
1619
|
-
}
|
|
1620
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "gray", children: [
|
|
1621
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
1622
|
-
/* @__PURE__ */ jsx(Text, { color: runStatus === "completed" ? "green" : "yellow", bold: true, children: runStatus === "completed" ? "Completed" : "Stopped" }),
|
|
1623
|
-
/* @__PURE__ */ jsx(Text, { color: "gray", children: " - Enter a follow-up query or wait to exit" })
|
|
1624
|
-
] }),
|
|
1625
|
-
/* @__PURE__ */ jsxs(Box, { children: [
|
|
1626
|
-
/* @__PURE__ */ jsx(Text, { color: "gray", children: "Continue: " }),
|
|
1627
|
-
/* @__PURE__ */ jsx(Text, { children: input }),
|
|
1628
|
-
/* @__PURE__ */ jsx(Text, { color: "cyan", children: "_" })
|
|
1629
|
-
] })
|
|
1630
|
-
] });
|
|
1631
|
-
};
|
|
1632
|
-
var StatusPanel = ({
|
|
1633
|
-
runtimeInfo,
|
|
1634
|
-
eventCount,
|
|
1635
|
-
runStatus
|
|
1636
|
-
}) => {
|
|
1637
|
-
if (runStatus !== "running") {
|
|
1638
|
-
return null;
|
|
1639
|
-
}
|
|
1640
|
-
return /* @__PURE__ */ jsx(RunSetting, { info: runtimeInfo, eventCount, isEditing: false });
|
|
1641
|
-
};
|
|
1642
|
-
var useExecutionState = (options) => {
|
|
1643
|
-
const { expertKey, query, config, continueTimeoutMs, historicalEvents, onReady, onComplete } = options;
|
|
1644
|
-
const { exit } = useApp();
|
|
1645
|
-
const runState = useRun();
|
|
1646
|
-
const { runtimeInfo, handleEvent, setQuery } = useRuntimeInfo({
|
|
1647
|
-
initialExpertName: expertKey,
|
|
1648
|
-
initialConfig: config
|
|
1649
|
-
});
|
|
1650
|
-
const [runStatus, setRunStatus] = useState("running");
|
|
1651
|
-
const [isAcceptingContinue, setIsAcceptingContinue] = useState(false);
|
|
1652
|
-
const timeoutRef = useRef(null);
|
|
1653
|
-
const clearTimeoutIfExists = useCallback(() => {
|
|
1654
|
-
if (timeoutRef.current) {
|
|
1655
|
-
clearTimeout(timeoutRef.current);
|
|
1656
|
-
timeoutRef.current = null;
|
|
1657
|
-
}
|
|
1658
|
-
}, []);
|
|
1659
|
-
const startExitTimeout = useCallback(() => {
|
|
1660
|
-
clearTimeoutIfExists();
|
|
1661
|
-
timeoutRef.current = setTimeout(() => {
|
|
1662
|
-
onComplete({ nextQuery: null });
|
|
1663
|
-
exit();
|
|
1664
|
-
}, continueTimeoutMs);
|
|
1665
|
-
}, [clearTimeoutIfExists, continueTimeoutMs, onComplete, exit]);
|
|
1666
|
-
useEffect(() => {
|
|
1667
|
-
setQuery(query);
|
|
1668
|
-
}, [query, setQuery]);
|
|
1669
|
-
useEffect(() => {
|
|
1670
|
-
if (historicalEvents && historicalEvents.length > 0) {
|
|
1671
|
-
runState.appendHistoricalEvents(historicalEvents);
|
|
1672
|
-
}
|
|
1673
|
-
}, [historicalEvents, runState.appendHistoricalEvents]);
|
|
1674
|
-
useEffect(() => {
|
|
1675
|
-
onReady((event) => {
|
|
1676
|
-
runState.addEvent(event);
|
|
1677
|
-
const result = handleEvent(event);
|
|
1678
|
-
if (result?.completed) {
|
|
1679
|
-
setRunStatus("completed");
|
|
1680
|
-
setIsAcceptingContinue(true);
|
|
1681
|
-
startExitTimeout();
|
|
1682
|
-
} else if (result?.stopped) {
|
|
1683
|
-
setRunStatus("stopped");
|
|
1684
|
-
setIsAcceptingContinue(true);
|
|
1685
|
-
startExitTimeout();
|
|
1686
|
-
}
|
|
1687
|
-
});
|
|
1688
|
-
}, [onReady, runState.addEvent, handleEvent, startExitTimeout]);
|
|
1689
|
-
useEffect(() => {
|
|
1690
|
-
return () => {
|
|
1691
|
-
clearTimeoutIfExists();
|
|
1692
|
-
};
|
|
1693
|
-
}, [clearTimeoutIfExists]);
|
|
1694
|
-
const handleContinueSubmit = useCallback(
|
|
1695
|
-
(newQuery) => {
|
|
1696
|
-
if (isAcceptingContinue && newQuery.trim()) {
|
|
1697
|
-
clearTimeoutIfExists();
|
|
1698
|
-
onComplete({ nextQuery: newQuery.trim() });
|
|
1699
|
-
exit();
|
|
1700
|
-
}
|
|
1701
|
-
},
|
|
1702
|
-
[isAcceptingContinue, clearTimeoutIfExists, onComplete, exit]
|
|
1703
|
-
);
|
|
1704
|
-
return {
|
|
1705
|
-
activities: runState.activities,
|
|
1706
|
-
streaming: runState.streaming,
|
|
1707
|
-
eventCount: runState.eventCount,
|
|
1708
|
-
runtimeInfo,
|
|
1709
|
-
runStatus,
|
|
1710
|
-
isAcceptingContinue,
|
|
1711
|
-
handleContinueSubmit,
|
|
1712
|
-
clearTimeout: clearTimeoutIfExists
|
|
1713
|
-
};
|
|
1714
|
-
};
|
|
1715
|
-
var ExecutionApp = (props) => {
|
|
1716
|
-
const { expertKey, query, config, continueTimeoutMs, historicalEvents, onReady, onComplete } = props;
|
|
1717
|
-
const { exit } = useApp();
|
|
1718
|
-
const state = useExecutionState({
|
|
1719
|
-
expertKey,
|
|
1720
|
-
query,
|
|
1721
|
-
config,
|
|
1722
|
-
continueTimeoutMs,
|
|
1723
|
-
historicalEvents,
|
|
1724
|
-
onReady,
|
|
1725
|
-
onComplete
|
|
1726
|
-
});
|
|
1727
|
-
useInput((input, key) => {
|
|
1728
|
-
if (key.ctrl && input === "c") {
|
|
1729
|
-
state.clearTimeout();
|
|
1730
|
-
exit();
|
|
1731
|
-
}
|
|
1732
|
-
});
|
|
1733
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
1734
|
-
/* @__PURE__ */ jsx(ActivityLogPanel, { activities: state.activities }),
|
|
1735
|
-
/* @__PURE__ */ jsx(StreamingDisplay, { streaming: state.streaming }),
|
|
1736
|
-
/* @__PURE__ */ jsx(
|
|
1737
|
-
StatusPanel,
|
|
1738
|
-
{
|
|
1739
|
-
runtimeInfo: state.runtimeInfo,
|
|
1740
|
-
eventCount: state.eventCount,
|
|
1741
|
-
runStatus: state.runStatus
|
|
1742
|
-
}
|
|
1743
|
-
),
|
|
1744
|
-
/* @__PURE__ */ jsx(
|
|
1745
|
-
ContinueInputPanel,
|
|
1746
|
-
{
|
|
1747
|
-
isActive: state.isAcceptingContinue,
|
|
1748
|
-
runStatus: state.runStatus,
|
|
1749
|
-
onSubmit: state.handleContinueSubmit
|
|
1750
|
-
}
|
|
1751
|
-
)
|
|
1752
|
-
] });
|
|
1753
|
-
};
|
|
1754
|
-
function renderExecution(params) {
|
|
1755
|
-
const eventQueue = new EventQueue();
|
|
1756
|
-
const result = new Promise((resolve, reject) => {
|
|
1757
|
-
let resolved = false;
|
|
1758
|
-
const { waitUntilExit } = render(
|
|
1759
|
-
/* @__PURE__ */ jsx(
|
|
1760
|
-
ExecutionApp,
|
|
1761
|
-
{
|
|
1762
|
-
...params,
|
|
1763
|
-
onReady: (handler) => {
|
|
1764
|
-
eventQueue.setHandler(handler);
|
|
1765
|
-
},
|
|
1766
|
-
onComplete: (result2) => {
|
|
1767
|
-
resolved = true;
|
|
1768
|
-
resolve(result2);
|
|
1769
|
-
}
|
|
1770
|
-
}
|
|
1771
|
-
)
|
|
1772
|
-
);
|
|
1773
|
-
waitUntilExit().then(() => {
|
|
1774
|
-
if (!resolved) {
|
|
1775
|
-
reject(new Error("Execution cancelled"));
|
|
1776
|
-
}
|
|
1777
|
-
}).catch(reject);
|
|
1778
|
-
});
|
|
1779
|
-
return {
|
|
1780
|
-
result,
|
|
1781
|
-
eventListener: (event) => {
|
|
1782
|
-
eventQueue.emit(event);
|
|
1783
|
-
}
|
|
1784
|
-
};
|
|
1785
|
-
}
|
|
1786
|
-
var selectionReducer = (_state, action) => {
|
|
1787
|
-
switch (action.type) {
|
|
1788
|
-
case "BROWSE_HISTORY":
|
|
1789
|
-
return { type: "browsingHistory", jobs: action.jobs };
|
|
1790
|
-
case "BROWSE_EXPERTS":
|
|
1791
|
-
return { type: "browsingExperts", experts: action.experts };
|
|
1792
|
-
case "SELECT_EXPERT":
|
|
1793
|
-
return { type: "enteringQuery", expertKey: action.expertKey };
|
|
1794
|
-
case "SELECT_JOB":
|
|
1795
|
-
return { type: "browsingCheckpoints", job: action.job, checkpoints: action.checkpoints };
|
|
1796
|
-
case "GO_BACK_FROM_CHECKPOINTS":
|
|
1797
|
-
return { type: "browsingHistory", jobs: action.jobs };
|
|
1798
|
-
default:
|
|
1799
|
-
return assertNever(action);
|
|
1800
|
-
}
|
|
1801
|
-
};
|
|
1802
|
-
var SelectionApp = (props) => {
|
|
1803
|
-
const {
|
|
1804
|
-
showHistory,
|
|
1805
|
-
initialExpertKey,
|
|
1806
|
-
initialQuery,
|
|
1807
|
-
initialCheckpoint,
|
|
1808
|
-
configuredExperts,
|
|
1809
|
-
recentExperts,
|
|
1810
|
-
historyJobs,
|
|
1811
|
-
onLoadCheckpoints,
|
|
1812
|
-
onComplete
|
|
1813
|
-
} = props;
|
|
1814
|
-
const { exit } = useApp();
|
|
1815
|
-
const allExperts = useMemo(() => {
|
|
1816
|
-
const configured = configuredExperts.map((e) => ({ ...e, source: "configured" }));
|
|
1817
|
-
const recent = recentExperts.filter((e) => !configured.some((c) => c.key === e.key)).map((e) => ({ ...e, source: "recent" }));
|
|
1818
|
-
return [...configured, ...recent];
|
|
1819
|
-
}, [configuredExperts, recentExperts]);
|
|
1820
|
-
const getInitialState = () => {
|
|
1821
|
-
if (initialExpertKey && !initialQuery) {
|
|
1822
|
-
return { type: "enteringQuery", expertKey: initialExpertKey };
|
|
1823
|
-
}
|
|
1824
|
-
if (showHistory && historyJobs.length > 0) {
|
|
1825
|
-
return { type: "browsingHistory", jobs: historyJobs };
|
|
1826
|
-
}
|
|
1827
|
-
return { type: "browsingExperts", experts: allExperts };
|
|
1828
|
-
};
|
|
1829
|
-
const [state, dispatch] = useReducer(selectionReducer, void 0, getInitialState);
|
|
1830
|
-
const [selectedCheckpoint, setSelectedCheckpoint] = useState(
|
|
1831
|
-
initialCheckpoint
|
|
1832
|
-
);
|
|
1833
|
-
useEffect(() => {
|
|
1834
|
-
if (initialExpertKey && initialQuery) {
|
|
1835
|
-
onComplete({
|
|
1836
|
-
expertKey: initialExpertKey,
|
|
1837
|
-
query: initialQuery,
|
|
1838
|
-
checkpoint: initialCheckpoint
|
|
1839
|
-
});
|
|
1840
|
-
exit();
|
|
1841
|
-
}
|
|
1842
|
-
}, [initialExpertKey, initialQuery, initialCheckpoint, onComplete, exit]);
|
|
1843
|
-
const { input: queryInput, handleInput: handleQueryInput } = useTextInput({
|
|
1844
|
-
onSubmit: (query) => {
|
|
1845
|
-
if (state.type === "enteringQuery" && query.trim()) {
|
|
1846
|
-
onComplete({
|
|
1847
|
-
expertKey: state.expertKey,
|
|
1848
|
-
query: query.trim(),
|
|
1849
|
-
checkpoint: selectedCheckpoint
|
|
1850
|
-
});
|
|
1851
|
-
exit();
|
|
1852
|
-
}
|
|
1853
|
-
}
|
|
1854
|
-
});
|
|
1855
|
-
useInput(handleQueryInput, { isActive: state.type === "enteringQuery" });
|
|
1856
|
-
const handleExpertSelect = useCallback((expertKey) => {
|
|
1857
|
-
dispatch({ type: "SELECT_EXPERT", expertKey });
|
|
1858
|
-
}, []);
|
|
1859
|
-
const handleJobSelect = useCallback(
|
|
1860
|
-
async (job) => {
|
|
1861
|
-
try {
|
|
1862
|
-
const checkpoints = await onLoadCheckpoints(job);
|
|
1863
|
-
dispatch({ type: "SELECT_JOB", job, checkpoints });
|
|
1864
|
-
} catch {
|
|
1865
|
-
dispatch({ type: "SELECT_JOB", job, checkpoints: [] });
|
|
1866
|
-
}
|
|
1867
|
-
},
|
|
1868
|
-
[onLoadCheckpoints]
|
|
1869
|
-
);
|
|
1870
|
-
const handleJobResume = useCallback(
|
|
1871
|
-
async (job) => {
|
|
1872
|
-
try {
|
|
1873
|
-
const checkpoints = await onLoadCheckpoints(job);
|
|
1874
|
-
const latestCheckpoint = checkpoints[0];
|
|
1875
|
-
if (latestCheckpoint) {
|
|
1876
|
-
setSelectedCheckpoint(latestCheckpoint);
|
|
1877
|
-
}
|
|
1878
|
-
dispatch({ type: "SELECT_EXPERT", expertKey: job.expertKey });
|
|
1879
|
-
} catch {
|
|
1880
|
-
dispatch({ type: "SELECT_EXPERT", expertKey: job.expertKey });
|
|
1881
|
-
}
|
|
1882
|
-
},
|
|
1883
|
-
[onLoadCheckpoints]
|
|
1884
|
-
);
|
|
1885
|
-
const handleCheckpointResume = useCallback(
|
|
1886
|
-
(checkpoint) => {
|
|
1887
|
-
setSelectedCheckpoint(checkpoint);
|
|
1888
|
-
if (state.type === "browsingCheckpoints") {
|
|
1889
|
-
dispatch({ type: "SELECT_EXPERT", expertKey: state.job.expertKey });
|
|
1890
|
-
}
|
|
1891
|
-
},
|
|
1892
|
-
[state]
|
|
1893
|
-
);
|
|
1894
|
-
const handleBack = useCallback(() => {
|
|
1895
|
-
if (state.type === "browsingCheckpoints") {
|
|
1896
|
-
dispatch({ type: "GO_BACK_FROM_CHECKPOINTS", jobs: historyJobs });
|
|
1897
|
-
}
|
|
1898
|
-
}, [state, historyJobs]);
|
|
1899
|
-
const handleSwitchToExperts = useCallback(() => {
|
|
1900
|
-
dispatch({ type: "BROWSE_EXPERTS", experts: allExperts });
|
|
1901
|
-
}, [allExperts]);
|
|
1902
|
-
const handleSwitchToHistory = useCallback(() => {
|
|
1903
|
-
dispatch({ type: "BROWSE_HISTORY", jobs: historyJobs });
|
|
1904
|
-
}, [historyJobs]);
|
|
1905
|
-
useInput((input, key) => {
|
|
1906
|
-
if (key.ctrl && input === "c") {
|
|
1907
|
-
exit();
|
|
1908
|
-
}
|
|
1909
|
-
});
|
|
1910
|
-
const contextValue = useMemo(
|
|
1911
|
-
() => ({
|
|
1912
|
-
onExpertSelect: handleExpertSelect,
|
|
1913
|
-
onQuerySubmit: () => {
|
|
1914
|
-
},
|
|
1915
|
-
// Not used in browser
|
|
1916
|
-
onJobSelect: handleJobSelect,
|
|
1917
|
-
onJobResume: handleJobResume,
|
|
1918
|
-
onCheckpointSelect: () => {
|
|
1919
|
-
},
|
|
1920
|
-
// Not used in selection (no event browsing)
|
|
1921
|
-
onCheckpointResume: handleCheckpointResume,
|
|
1922
|
-
onEventSelect: () => {
|
|
1923
|
-
},
|
|
1924
|
-
// Not used in selection
|
|
1925
|
-
onBack: handleBack,
|
|
1926
|
-
onSwitchToExperts: handleSwitchToExperts,
|
|
1927
|
-
onSwitchToHistory: handleSwitchToHistory
|
|
1928
|
-
}),
|
|
1929
|
-
[
|
|
1930
|
-
handleExpertSelect,
|
|
1931
|
-
handleJobSelect,
|
|
1932
|
-
handleJobResume,
|
|
1933
|
-
handleCheckpointResume,
|
|
1934
|
-
handleBack,
|
|
1935
|
-
handleSwitchToExperts,
|
|
1936
|
-
handleSwitchToHistory
|
|
1937
|
-
]
|
|
1938
|
-
);
|
|
1939
|
-
if (initialExpertKey && initialQuery) {
|
|
1940
|
-
return null;
|
|
1941
|
-
}
|
|
1942
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
1943
|
-
/* @__PURE__ */ jsx(InputAreaProvider, { value: contextValue, children: (state.type === "browsingHistory" || state.type === "browsingExperts" || state.type === "browsingCheckpoints") && /* @__PURE__ */ jsx(
|
|
1944
|
-
BrowserRouter,
|
|
1945
|
-
{
|
|
1946
|
-
inputState: state,
|
|
1947
|
-
showEventsHint: false
|
|
1948
|
-
}
|
|
1949
|
-
) }),
|
|
1950
|
-
state.type === "enteringQuery" && /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: "gray", children: [
|
|
1951
|
-
/* @__PURE__ */ jsxs(Text, { children: [
|
|
1952
|
-
/* @__PURE__ */ jsx(Text, { color: "cyan", bold: true, children: "Expert:" }),
|
|
1953
|
-
" ",
|
|
1954
|
-
/* @__PURE__ */ jsx(Text, { children: state.expertKey }),
|
|
1955
|
-
selectedCheckpoint && /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
1956
|
-
" (resuming from step ",
|
|
1957
|
-
selectedCheckpoint.stepNumber,
|
|
1958
|
-
")"
|
|
1959
|
-
] })
|
|
1960
|
-
] }),
|
|
1961
|
-
/* @__PURE__ */ jsxs(Box, { children: [
|
|
1962
|
-
/* @__PURE__ */ jsx(Text, { color: "gray", children: "Query: " }),
|
|
1963
|
-
/* @__PURE__ */ jsx(Text, { children: queryInput }),
|
|
1964
|
-
/* @__PURE__ */ jsx(Text, { color: "cyan", children: "_" })
|
|
1965
|
-
] }),
|
|
1966
|
-
/* @__PURE__ */ jsx(Text, { dimColor: true, children: "Press Enter to start" })
|
|
1967
|
-
] })
|
|
1968
|
-
] });
|
|
1969
|
-
};
|
|
1970
|
-
async function renderSelection(params) {
|
|
1971
|
-
return new Promise((resolve, reject) => {
|
|
1972
|
-
let resolved = false;
|
|
1973
|
-
const { waitUntilExit } = render(
|
|
1974
|
-
/* @__PURE__ */ jsx(
|
|
1975
|
-
SelectionApp,
|
|
1976
|
-
{
|
|
1977
|
-
...params,
|
|
1978
|
-
onComplete: (result) => {
|
|
1979
|
-
resolved = true;
|
|
1980
|
-
resolve(result);
|
|
1981
|
-
}
|
|
1982
|
-
}
|
|
1983
|
-
)
|
|
1984
|
-
);
|
|
1985
|
-
waitUntilExit().then(() => {
|
|
1986
|
-
if (!resolved) {
|
|
1987
|
-
reject(new Error("Selection cancelled"));
|
|
1988
|
-
}
|
|
1989
|
-
}).catch(reject);
|
|
1990
|
-
});
|
|
1991
|
-
}
|
|
1992
|
-
|
|
1993
|
-
// src/start.ts
|
|
1994
|
-
var CONTINUE_TIMEOUT_MS = 6e4;
|
|
1995
|
-
async function startHandler(expertKey, query, options, handlerOptions) {
|
|
1996
|
-
const input = parseWithFriendlyError(startCommandInputSchema, { expertKey, query, options });
|
|
1997
|
-
try {
|
|
1998
|
-
const { perstackConfig, checkpoint, env, providerConfig, model, experts } = await resolveRunContext({
|
|
1999
|
-
configPath: input.options.config,
|
|
2000
|
-
provider: input.options.provider,
|
|
2001
|
-
model: input.options.model,
|
|
2002
|
-
envPath: input.options.envPath,
|
|
2003
|
-
continue: input.options.continue,
|
|
2004
|
-
continueJob: input.options.continueJob,
|
|
2005
|
-
resumeFrom: input.options.resumeFrom,
|
|
2006
|
-
expertKey: input.expertKey,
|
|
2007
|
-
perstackConfig: handlerOptions?.perstackConfig
|
|
2008
|
-
});
|
|
2009
|
-
if (handlerOptions?.additionalEnv) {
|
|
2010
|
-
Object.assign(env, handlerOptions.additionalEnv(env));
|
|
2011
|
-
}
|
|
2012
|
-
const maxSteps = input.options.maxSteps ?? perstackConfig.maxSteps;
|
|
2013
|
-
const maxRetries = input.options.maxRetries ?? perstackConfig.maxRetries ?? defaultMaxRetries;
|
|
2014
|
-
const timeout = input.options.timeout ?? perstackConfig.timeout ?? defaultTimeout;
|
|
2015
|
-
const configuredExperts = Object.keys(perstackConfig.experts ?? {}).map((key) => ({
|
|
2016
|
-
key,
|
|
2017
|
-
name: key
|
|
2018
|
-
}));
|
|
2019
|
-
const recentExperts = getRecentExperts(10);
|
|
2020
|
-
const showHistory = !input.expertKey && !input.query && !checkpoint;
|
|
2021
|
-
const historyJobs = showHistory ? getAllJobs2().map((j) => ({
|
|
2022
|
-
jobId: j.id,
|
|
2023
|
-
status: j.status,
|
|
2024
|
-
expertKey: j.coordinatorExpertKey,
|
|
2025
|
-
totalSteps: j.totalSteps,
|
|
2026
|
-
startedAt: j.startedAt,
|
|
2027
|
-
finishedAt: j.finishedAt
|
|
2028
|
-
})) : [];
|
|
2029
|
-
const selection = await renderSelection({
|
|
2030
|
-
showHistory,
|
|
2031
|
-
initialExpertKey: input.expertKey,
|
|
2032
|
-
initialQuery: input.query,
|
|
2033
|
-
initialCheckpoint: checkpoint ? {
|
|
2034
|
-
id: checkpoint.id,
|
|
2035
|
-
jobId: checkpoint.jobId,
|
|
2036
|
-
runId: checkpoint.runId,
|
|
2037
|
-
stepNumber: checkpoint.stepNumber,
|
|
2038
|
-
contextWindowUsage: checkpoint.contextWindowUsage ?? 0
|
|
2039
|
-
} : void 0,
|
|
2040
|
-
configuredExperts,
|
|
2041
|
-
recentExperts,
|
|
2042
|
-
historyJobs,
|
|
2043
|
-
onLoadCheckpoints: async (j) => {
|
|
2044
|
-
const checkpoints = getCheckpointsWithDetails(j.jobId);
|
|
2045
|
-
return checkpoints.map((cp) => ({ ...cp, jobId: j.jobId }));
|
|
2046
|
-
}
|
|
2047
|
-
});
|
|
2048
|
-
if (!selection.expertKey) {
|
|
2049
|
-
console.error("Expert key is required");
|
|
2050
|
-
return;
|
|
2051
|
-
}
|
|
2052
|
-
if (!selection.query && !selection.checkpoint) {
|
|
2053
|
-
console.error("Query is required");
|
|
2054
|
-
return;
|
|
2055
|
-
}
|
|
2056
|
-
let currentCheckpoint = selection.checkpoint ? getCheckpointById(selection.checkpoint.jobId, selection.checkpoint.id) : checkpoint;
|
|
2057
|
-
if (currentCheckpoint && currentCheckpoint.expert.key !== selection.expertKey) {
|
|
2058
|
-
console.error(
|
|
2059
|
-
`Checkpoint expert key ${currentCheckpoint.expert.key} does not match input expert key ${selection.expertKey}`
|
|
2060
|
-
);
|
|
2061
|
-
return;
|
|
2062
|
-
}
|
|
2063
|
-
const lockfilePath = findLockfile();
|
|
2064
|
-
const lockfile = lockfilePath ? loadLockfile(lockfilePath) ?? void 0 : void 0;
|
|
2065
|
-
let currentQuery = selection.query;
|
|
2066
|
-
let currentJobId = currentCheckpoint?.jobId ?? input.options.jobId ?? createId();
|
|
2067
|
-
let isNextQueryInteractiveToolResult = input.options.interactiveToolCallResult ?? false;
|
|
2068
|
-
let isFirstIteration = true;
|
|
2069
|
-
const initialHistoricalEvents = currentCheckpoint ? getAllEventContentsForJob(currentCheckpoint.jobId, currentCheckpoint.stepNumber) : void 0;
|
|
2070
|
-
while (currentQuery !== null) {
|
|
2071
|
-
const historicalEvents = isFirstIteration ? initialHistoricalEvents : void 0;
|
|
2072
|
-
const runId = createId();
|
|
2073
|
-
const { result: executionResult, eventListener } = renderExecution({
|
|
2074
|
-
expertKey: selection.expertKey,
|
|
2075
|
-
query: currentQuery,
|
|
2076
|
-
config: {
|
|
2077
|
-
runtimeVersion,
|
|
2078
|
-
model,
|
|
2079
|
-
maxSteps,
|
|
2080
|
-
maxRetries,
|
|
2081
|
-
timeout,
|
|
2082
|
-
contextWindowUsage: currentCheckpoint?.contextWindowUsage ?? 0
|
|
2083
|
-
},
|
|
2084
|
-
continueTimeoutMs: CONTINUE_TIMEOUT_MS,
|
|
2085
|
-
historicalEvents
|
|
2086
|
-
});
|
|
2087
|
-
const runResult = await run(
|
|
2088
|
-
{
|
|
2089
|
-
setting: {
|
|
2090
|
-
jobId: currentJobId,
|
|
2091
|
-
runId,
|
|
2092
|
-
expertKey: selection.expertKey,
|
|
2093
|
-
input: isNextQueryInteractiveToolResult && currentCheckpoint ? parseInteractiveToolCallResult(currentQuery, currentCheckpoint) : { text: currentQuery },
|
|
2094
|
-
experts,
|
|
2095
|
-
model,
|
|
2096
|
-
providerConfig,
|
|
2097
|
-
reasoningBudget: input.options.reasoningBudget ?? perstackConfig.reasoningBudget,
|
|
2098
|
-
maxSteps: input.options.maxSteps ?? perstackConfig.maxSteps,
|
|
2099
|
-
maxRetries: input.options.maxRetries ?? perstackConfig.maxRetries,
|
|
2100
|
-
timeout: input.options.timeout ?? perstackConfig.timeout,
|
|
2101
|
-
perstackApiBaseUrl: perstackConfig.perstackApiBaseUrl,
|
|
2102
|
-
perstackApiKey: env.PERSTACK_API_KEY,
|
|
2103
|
-
perstackBaseSkillCommand: perstackConfig.perstackBaseSkillCommand,
|
|
2104
|
-
env,
|
|
2105
|
-
verbose: input.options.verbose
|
|
2106
|
-
},
|
|
2107
|
-
checkpoint: currentCheckpoint
|
|
2108
|
-
},
|
|
2109
|
-
{
|
|
2110
|
-
eventListener,
|
|
2111
|
-
storeCheckpoint: defaultStoreCheckpoint,
|
|
2112
|
-
storeEvent: defaultStoreEvent,
|
|
2113
|
-
retrieveCheckpoint: defaultRetrieveCheckpoint,
|
|
2114
|
-
storeJob,
|
|
2115
|
-
retrieveJob,
|
|
2116
|
-
createJob: createInitialJob,
|
|
2117
|
-
lockfile
|
|
2118
|
-
}
|
|
2119
|
-
);
|
|
2120
|
-
const result = await executionResult;
|
|
2121
|
-
const canContinue = runResult.status === "completed" || runResult.status === "stoppedByExceededMaxSteps" || runResult.status === "stoppedByError" || runResult.status === "stoppedByInteractiveTool";
|
|
2122
|
-
if (result.nextQuery && canContinue) {
|
|
2123
|
-
currentQuery = result.nextQuery;
|
|
2124
|
-
currentCheckpoint = runResult;
|
|
2125
|
-
currentJobId = runResult.jobId;
|
|
2126
|
-
isNextQueryInteractiveToolResult = runResult.status === "stoppedByInteractiveTool";
|
|
2127
|
-
isFirstIteration = false;
|
|
2128
|
-
} else {
|
|
2129
|
-
currentQuery = null;
|
|
2130
|
-
}
|
|
2131
|
-
}
|
|
2132
|
-
} catch (error) {
|
|
2133
|
-
if (error instanceof Error) {
|
|
2134
|
-
console.error(error.message);
|
|
2135
|
-
} else {
|
|
2136
|
-
console.error(error);
|
|
2137
|
-
}
|
|
2138
|
-
}
|
|
2139
|
-
}
|
|
2140
|
-
var startCommand = new Command().command("start").description("Start Perstack with interactive TUI").argument("[expertKey]", "Expert key to run (optional, will prompt if not provided)").argument("[query]", "Query to run (optional, will prompt if not provided)").option("--config <configPath>", "Path to perstack.toml config file").option("--provider <provider>", "Provider to use").option("--model <model>", "Model to use").option(
|
|
2141
|
-
"--reasoning-budget <budget>",
|
|
2142
|
-
"Reasoning budget for native LLM reasoning (minimal, low, medium, high, or token count)"
|
|
2143
|
-
).option(
|
|
2144
|
-
"--max-steps <maxSteps>",
|
|
2145
|
-
"Maximum number of steps to run, default is undefined (no limit)"
|
|
2146
|
-
).option("--max-retries <maxRetries>", "Maximum number of generation retries, default is 5").option(
|
|
2147
|
-
"--timeout <timeout>",
|
|
2148
|
-
"Timeout for each generation in milliseconds, default is 300000 (5 minutes)"
|
|
2149
|
-
).option("--job-id <jobId>", "Job ID for identifying the job").option(
|
|
2150
|
-
"--env-path <path>",
|
|
2151
|
-
"Path to the environment file (can be specified multiple times), default is .env and .env.local",
|
|
2152
|
-
(value, previous) => previous.concat(value),
|
|
2153
|
-
[]
|
|
2154
|
-
).option("--verbose", "Enable verbose logging").option("--continue", "Continue the most recent job with new query").option("--continue-job <jobId>", "Continue the specified job with new query").option(
|
|
2155
|
-
"--resume-from <checkpointId>",
|
|
2156
|
-
"Resume from a specific checkpoint (requires --continue or --continue-job)"
|
|
2157
|
-
).option("-i, --interactive-tool-call-result", "Query is interactive tool call result").action((expertKey, query, options) => startHandler(expertKey, query, options));
|
|
2158
|
-
|
|
2159
|
-
export { createInitialJob, defaultRetrieveCheckpoint, defaultStoreCheckpoint, defaultStoreEvent, getAllJobs, getAllRuns, getCheckpointsByJobId, getEnv, getEventContents, getPerstackConfig, parseInteractiveToolCallResult, parseInteractiveToolCallResultJson, resolveRunContext, retrieveJob, startCommand, startHandler, storeJob };
|
|
2160
|
-
//# sourceMappingURL=chunk-FOHDMSVR.js.map
|
|
2161
|
-
//# sourceMappingURL=chunk-FOHDMSVR.js.map
|