@pantheon.ai/agents 0.0.11 → 0.0.12
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/index.js +1929 -2
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -13,6 +13,8 @@ import expandTilde from "expand-tilde";
|
|
|
13
13
|
import express, { Router } from "express";
|
|
14
14
|
import { randomUUID } from "node:crypto";
|
|
15
15
|
import { URL as URL$1 } from "node:url";
|
|
16
|
+
import blessed from "reblessed";
|
|
17
|
+
import { inspect } from "node:util";
|
|
16
18
|
|
|
17
19
|
//#region \0rolldown/runtime.js
|
|
18
20
|
var __create = Object.create;
|
|
@@ -420,7 +422,7 @@ var require_cli_options = /* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
|
420
422
|
|
|
421
423
|
//#endregion
|
|
422
424
|
//#region package.json
|
|
423
|
-
var version$1 = "0.0.
|
|
425
|
+
var version$1 = "0.0.12";
|
|
424
426
|
|
|
425
427
|
//#endregion
|
|
426
428
|
//#region src/schemas/task-list.ts
|
|
@@ -9611,6 +9613,12 @@ async function getPantheonBranch({ projectId, branchId, getOutputIfFinished = fa
|
|
|
9611
9613
|
}
|
|
9612
9614
|
return { state: "others" };
|
|
9613
9615
|
}
|
|
9616
|
+
async function getPantheonBranchInfo({ projectId, branchId }) {
|
|
9617
|
+
return await executor.execute(getProjectBranch, {
|
|
9618
|
+
projectId,
|
|
9619
|
+
branchId
|
|
9620
|
+
}, null);
|
|
9621
|
+
}
|
|
9614
9622
|
async function executeOnPantheon({ projectId, branchId, prompt, agent }) {
|
|
9615
9623
|
return (await executor.execute(createProjectExploration, { projectId }, {
|
|
9616
9624
|
shared_prompt_sequence: [prompt],
|
|
@@ -9706,6 +9714,327 @@ async function startPendingTask(provider, task, logger) {
|
|
|
9706
9714
|
logger.info(`Task ${task.id} started successfully.`);
|
|
9707
9715
|
}
|
|
9708
9716
|
|
|
9717
|
+
//#endregion
|
|
9718
|
+
//#region src/core/watch/selector.ts
|
|
9719
|
+
const finishedTaskStatuses = [
|
|
9720
|
+
"completed",
|
|
9721
|
+
"failed",
|
|
9722
|
+
"cancelled"
|
|
9723
|
+
];
|
|
9724
|
+
function isFinishedTaskStatus(status) {
|
|
9725
|
+
return finishedTaskStatuses.includes(status);
|
|
9726
|
+
}
|
|
9727
|
+
function getTaskTimestampMs(task) {
|
|
9728
|
+
switch (task.status) {
|
|
9729
|
+
case "running": return task.started_at.getTime();
|
|
9730
|
+
case "completed": return task.ended_at.getTime();
|
|
9731
|
+
case "failed": return task.ended_at?.getTime() ?? task.started_at?.getTime() ?? 0;
|
|
9732
|
+
case "cancelled": return task.cancelled_at.getTime();
|
|
9733
|
+
case "pending": return task.queued_at.getTime();
|
|
9734
|
+
}
|
|
9735
|
+
}
|
|
9736
|
+
function pickLatestRunningTask(tasks) {
|
|
9737
|
+
const running = tasks.filter((task) => task.status === "running");
|
|
9738
|
+
running.sort((a, b) => getTaskTimestampMs(b) - getTaskTimestampMs(a));
|
|
9739
|
+
return running[0];
|
|
9740
|
+
}
|
|
9741
|
+
function pickLatestFinishedTasks(tasks, limit) {
|
|
9742
|
+
const finished = tasks.filter((task) => isFinishedTaskStatus(task.status));
|
|
9743
|
+
finished.sort((a, b) => getTaskTimestampMs(b) - getTaskTimestampMs(a));
|
|
9744
|
+
return finished.slice(0, limit);
|
|
9745
|
+
}
|
|
9746
|
+
function selectSingleAgentWatchTasks(tasks, options = {}) {
|
|
9747
|
+
const finishedLimit = options.finishedLimit ?? 3;
|
|
9748
|
+
const out = [];
|
|
9749
|
+
const running = pickLatestRunningTask(tasks);
|
|
9750
|
+
if (running) out.push(running);
|
|
9751
|
+
const finished = pickLatestFinishedTasks(tasks, finishedLimit);
|
|
9752
|
+
out.push(...finished);
|
|
9753
|
+
return out;
|
|
9754
|
+
}
|
|
9755
|
+
function selectMultiAgentWatchTasks(tasks, agentNames) {
|
|
9756
|
+
const out = [];
|
|
9757
|
+
for (const agent of agentNames) {
|
|
9758
|
+
const agentTasks = tasks.filter((entry) => entry.agent === agent).map((entry) => entry.task);
|
|
9759
|
+
const running = pickLatestRunningTask(agentTasks);
|
|
9760
|
+
if (running) {
|
|
9761
|
+
out.push({
|
|
9762
|
+
agent,
|
|
9763
|
+
task: running
|
|
9764
|
+
});
|
|
9765
|
+
continue;
|
|
9766
|
+
}
|
|
9767
|
+
const latestFinished = pickLatestFinishedTasks(agentTasks, 1)[0];
|
|
9768
|
+
if (latestFinished) out.push({
|
|
9769
|
+
agent,
|
|
9770
|
+
task: latestFinished
|
|
9771
|
+
});
|
|
9772
|
+
}
|
|
9773
|
+
return out;
|
|
9774
|
+
}
|
|
9775
|
+
function selectFallbackWatchTasks(tasks, options = {}) {
|
|
9776
|
+
const agentLimit = options.agentLimit ?? 3;
|
|
9777
|
+
const byAgent = /* @__PURE__ */ new Map();
|
|
9778
|
+
for (const entry of tasks) {
|
|
9779
|
+
const existing = byAgent.get(entry.agent);
|
|
9780
|
+
if (existing) existing.push(entry.task);
|
|
9781
|
+
else byAgent.set(entry.agent, [entry.task]);
|
|
9782
|
+
}
|
|
9783
|
+
const runningCandidates = [];
|
|
9784
|
+
const finishedCandidates = [];
|
|
9785
|
+
for (const [agent, agentTasks] of byAgent) {
|
|
9786
|
+
const running = pickLatestRunningTask(agentTasks);
|
|
9787
|
+
if (running) {
|
|
9788
|
+
runningCandidates.push({
|
|
9789
|
+
agent,
|
|
9790
|
+
task: running
|
|
9791
|
+
});
|
|
9792
|
+
continue;
|
|
9793
|
+
}
|
|
9794
|
+
const latestFinished = pickLatestFinishedTasks(agentTasks, 1)[0];
|
|
9795
|
+
if (latestFinished) finishedCandidates.push({
|
|
9796
|
+
agent,
|
|
9797
|
+
task: latestFinished
|
|
9798
|
+
});
|
|
9799
|
+
}
|
|
9800
|
+
runningCandidates.sort((a, b) => getTaskTimestampMs(b.task) - getTaskTimestampMs(a.task));
|
|
9801
|
+
finishedCandidates.sort((a, b) => getTaskTimestampMs(b.task) - getTaskTimestampMs(a.task));
|
|
9802
|
+
const selected = [];
|
|
9803
|
+
const selectedAgents = /* @__PURE__ */ new Set();
|
|
9804
|
+
for (const entry of [...runningCandidates, ...finishedCandidates]) {
|
|
9805
|
+
if (selected.length >= agentLimit) break;
|
|
9806
|
+
if (selectedAgents.has(entry.agent)) continue;
|
|
9807
|
+
selectedAgents.add(entry.agent);
|
|
9808
|
+
selected.push(entry);
|
|
9809
|
+
}
|
|
9810
|
+
return selected;
|
|
9811
|
+
}
|
|
9812
|
+
|
|
9813
|
+
//#endregion
|
|
9814
|
+
//#region src/core/watch/stream.ts
|
|
9815
|
+
async function consumeOpaqueSseStream(options) {
|
|
9816
|
+
const reader = options.stream.getReader();
|
|
9817
|
+
let ended = false;
|
|
9818
|
+
let abortedByIdleTimeout = false;
|
|
9819
|
+
let buffer = "";
|
|
9820
|
+
let idleTimer;
|
|
9821
|
+
function warn(message) {
|
|
9822
|
+
options.onWarning?.(message);
|
|
9823
|
+
}
|
|
9824
|
+
function cancelReader(reason) {
|
|
9825
|
+
try {
|
|
9826
|
+
reader.cancel(reason);
|
|
9827
|
+
} catch {}
|
|
9828
|
+
}
|
|
9829
|
+
function resetIdleTimer() {
|
|
9830
|
+
if (options.idleTimeoutMs == null) return;
|
|
9831
|
+
clearTimeout(idleTimer);
|
|
9832
|
+
idleTimer = setTimeout(() => {
|
|
9833
|
+
abortedByIdleTimeout = true;
|
|
9834
|
+
cancelReader("idle_timeout");
|
|
9835
|
+
}, options.idleTimeoutMs);
|
|
9836
|
+
}
|
|
9837
|
+
resetIdleTimer();
|
|
9838
|
+
function processEventBlock(block) {
|
|
9839
|
+
const lines = block.split("\n");
|
|
9840
|
+
for (const rawLine of lines) {
|
|
9841
|
+
const line = rawLine.trimEnd();
|
|
9842
|
+
if (!line.startsWith("data:")) continue;
|
|
9843
|
+
const part = line.slice(5).trimStart();
|
|
9844
|
+
if (!part || part === "[DONE]") continue;
|
|
9845
|
+
try {
|
|
9846
|
+
options.onData(part);
|
|
9847
|
+
resetIdleTimer();
|
|
9848
|
+
} catch (error) {
|
|
9849
|
+
warn(`onData handler threw; skipping chunk. ${error instanceof Error ? error.message : String(error)}`);
|
|
9850
|
+
}
|
|
9851
|
+
}
|
|
9852
|
+
}
|
|
9853
|
+
const textDecoder = new TextDecoder();
|
|
9854
|
+
try {
|
|
9855
|
+
while (true) {
|
|
9856
|
+
if (options.signal?.aborted) {
|
|
9857
|
+
cancelReader("aborted");
|
|
9858
|
+
break;
|
|
9859
|
+
}
|
|
9860
|
+
const { value, done } = await reader.read();
|
|
9861
|
+
if (done) {
|
|
9862
|
+
ended = !abortedByIdleTimeout;
|
|
9863
|
+
break;
|
|
9864
|
+
}
|
|
9865
|
+
const decoded = textDecoder.decode(value, { stream: true });
|
|
9866
|
+
buffer += decoded.replaceAll("\r\n", "\n");
|
|
9867
|
+
while (true) {
|
|
9868
|
+
const sepIndex = buffer.indexOf("\n\n");
|
|
9869
|
+
if (sepIndex === -1) break;
|
|
9870
|
+
const block = buffer.slice(0, sepIndex);
|
|
9871
|
+
buffer = buffer.slice(sepIndex + 2);
|
|
9872
|
+
if (block.trim()) processEventBlock(block);
|
|
9873
|
+
}
|
|
9874
|
+
}
|
|
9875
|
+
} finally {
|
|
9876
|
+
clearTimeout(idleTimer);
|
|
9877
|
+
}
|
|
9878
|
+
if (buffer.trim()) warn("Stream ended with trailing incomplete SSE data; ignoring remaining buffer.");
|
|
9879
|
+
return {
|
|
9880
|
+
ended,
|
|
9881
|
+
abortedByIdleTimeout
|
|
9882
|
+
};
|
|
9883
|
+
}
|
|
9884
|
+
var WatchStepAggregator = class {
|
|
9885
|
+
maxLeadingTextChars;
|
|
9886
|
+
maxLeadingReasoningChars;
|
|
9887
|
+
maxLogLines;
|
|
9888
|
+
maxWarnings;
|
|
9889
|
+
leadingText = "";
|
|
9890
|
+
leadingReasoning = "";
|
|
9891
|
+
toolActions = /* @__PURE__ */ new Map();
|
|
9892
|
+
logLines = [];
|
|
9893
|
+
warnings = [];
|
|
9894
|
+
unknownEventCount = 0;
|
|
9895
|
+
orderCounter = 0;
|
|
9896
|
+
constructor(options = {}) {
|
|
9897
|
+
this.maxLeadingTextChars = options.maxLeadingTextChars ?? 800;
|
|
9898
|
+
this.maxLeadingReasoningChars = options.maxLeadingReasoningChars ?? 800;
|
|
9899
|
+
this.maxLogLines = options.maxLogLines ?? 200;
|
|
9900
|
+
this.maxWarnings = options.maxWarnings ?? 50;
|
|
9901
|
+
}
|
|
9902
|
+
pushLog(line) {
|
|
9903
|
+
if (!line) return;
|
|
9904
|
+
this.logLines.push(line);
|
|
9905
|
+
if (this.logLines.length > this.maxLogLines) this.logLines.splice(0, this.logLines.length - this.maxLogLines);
|
|
9906
|
+
}
|
|
9907
|
+
pushWarning(message) {
|
|
9908
|
+
if (!message) return;
|
|
9909
|
+
this.warnings.push(message);
|
|
9910
|
+
if (this.warnings.length > this.maxWarnings) this.warnings.splice(0, this.warnings.length - this.maxWarnings);
|
|
9911
|
+
}
|
|
9912
|
+
pushCommandExecutionOutput(output) {
|
|
9913
|
+
const out = output;
|
|
9914
|
+
const aggregated = typeof out?.aggregated_output === "string" ? out.aggregated_output : "";
|
|
9915
|
+
const exitCode = typeof out?.exit_code === "number" ? out.exit_code : void 0;
|
|
9916
|
+
const trimmed = aggregated.replaceAll("\r\n", "\n").trimEnd();
|
|
9917
|
+
if (trimmed) trimmed.split("\n").forEach((line) => this.pushLog(line));
|
|
9918
|
+
if (exitCode != null && exitCode !== 0) this.pushLog(`exit_code=${exitCode}`);
|
|
9919
|
+
}
|
|
9920
|
+
getOrCreateToolAction(toolCallId) {
|
|
9921
|
+
const existing = this.toolActions.get(toolCallId);
|
|
9922
|
+
if (existing) return existing;
|
|
9923
|
+
const created = {
|
|
9924
|
+
toolCallId,
|
|
9925
|
+
status: "in_progress",
|
|
9926
|
+
_order: this.orderCounter++
|
|
9927
|
+
};
|
|
9928
|
+
this.toolActions.set(toolCallId, created);
|
|
9929
|
+
return created;
|
|
9930
|
+
}
|
|
9931
|
+
pushUiChunk(chunk) {
|
|
9932
|
+
if (typeof chunk !== "object" || chunk === null) return;
|
|
9933
|
+
const type = chunk.type;
|
|
9934
|
+
if (typeof type !== "string") return;
|
|
9935
|
+
switch (type) {
|
|
9936
|
+
case "text-delta": {
|
|
9937
|
+
const delta = typeof chunk.delta === "string" ? chunk.delta : "";
|
|
9938
|
+
if (!delta) return;
|
|
9939
|
+
if (this.leadingText.length < this.maxLeadingTextChars) {
|
|
9940
|
+
const remaining = this.maxLeadingTextChars - this.leadingText.length;
|
|
9941
|
+
this.leadingText += delta.slice(0, remaining);
|
|
9942
|
+
}
|
|
9943
|
+
return;
|
|
9944
|
+
}
|
|
9945
|
+
case "reasoning-delta": {
|
|
9946
|
+
const delta = typeof chunk.delta === "string" ? chunk.delta : "";
|
|
9947
|
+
if (!delta) return;
|
|
9948
|
+
if (this.leadingReasoning.length < this.maxLeadingReasoningChars) {
|
|
9949
|
+
const remaining = this.maxLeadingReasoningChars - this.leadingReasoning.length;
|
|
9950
|
+
this.leadingReasoning += delta.slice(0, remaining);
|
|
9951
|
+
}
|
|
9952
|
+
return;
|
|
9953
|
+
}
|
|
9954
|
+
case "tool-input-start": {
|
|
9955
|
+
const toolCallId = typeof chunk.toolCallId === "string" ? chunk.toolCallId : "";
|
|
9956
|
+
if (!toolCallId) return;
|
|
9957
|
+
const toolName = typeof chunk.toolName === "string" ? chunk.toolName : void 0;
|
|
9958
|
+
const toolAction = this.getOrCreateToolAction(toolCallId);
|
|
9959
|
+
toolAction.toolName = toolName ?? toolAction.toolName;
|
|
9960
|
+
toolAction.status = "in_progress";
|
|
9961
|
+
if (toolAction.toolName !== "command_execution") this.pushLog(`→ tool ${toolAction.toolName ?? "<tool>"} (${toolCallId})`);
|
|
9962
|
+
return;
|
|
9963
|
+
}
|
|
9964
|
+
case "tool-input-delta": {
|
|
9965
|
+
const toolCallId = typeof chunk.toolCallId === "string" ? chunk.toolCallId : "";
|
|
9966
|
+
if (!toolCallId) return;
|
|
9967
|
+
const toolAction = this.getOrCreateToolAction(toolCallId);
|
|
9968
|
+
toolAction.status = "in_progress";
|
|
9969
|
+
return;
|
|
9970
|
+
}
|
|
9971
|
+
case "tool-input-available": {
|
|
9972
|
+
const toolCallId = typeof chunk.toolCallId === "string" ? chunk.toolCallId : "";
|
|
9973
|
+
if (!toolCallId) return;
|
|
9974
|
+
const toolName = typeof chunk.toolName === "string" ? chunk.toolName : void 0;
|
|
9975
|
+
const title = typeof chunk.title === "string" ? chunk.title : void 0;
|
|
9976
|
+
const input = chunk.input;
|
|
9977
|
+
const toolAction = this.getOrCreateToolAction(toolCallId);
|
|
9978
|
+
toolAction.toolName = toolName ?? toolAction.toolName;
|
|
9979
|
+
toolAction.title = title ?? toolAction.title;
|
|
9980
|
+
toolAction.input = input ?? toolAction.input;
|
|
9981
|
+
toolAction.status = "in_progress";
|
|
9982
|
+
const toolLabel = toolAction.title ? `${toolAction.toolName ?? "<tool>"}: ${toolAction.title}` : `${toolAction.toolName ?? "<tool>"}`;
|
|
9983
|
+
if (toolAction.toolName !== "command_execution") this.pushLog(`→ tool ${toolLabel} (${toolCallId})`);
|
|
9984
|
+
return;
|
|
9985
|
+
}
|
|
9986
|
+
case "tool-output-available": {
|
|
9987
|
+
const toolCallId = typeof chunk.toolCallId === "string" ? chunk.toolCallId : "";
|
|
9988
|
+
if (!toolCallId) return;
|
|
9989
|
+
const output = chunk.output;
|
|
9990
|
+
const toolAction = this.getOrCreateToolAction(toolCallId);
|
|
9991
|
+
toolAction.output = output;
|
|
9992
|
+
toolAction.status = "completed";
|
|
9993
|
+
if (toolAction.toolName === "command_execution") {
|
|
9994
|
+
this.pushCommandExecutionOutput(output);
|
|
9995
|
+
return;
|
|
9996
|
+
}
|
|
9997
|
+
this.pushLog(`← tool ${toolAction.toolName ?? "<tool>"} ok (${toolCallId})`);
|
|
9998
|
+
return;
|
|
9999
|
+
}
|
|
10000
|
+
case "tool-output-error":
|
|
10001
|
+
case "tool-input-error": {
|
|
10002
|
+
const toolCallId = typeof chunk.toolCallId === "string" ? chunk.toolCallId : "";
|
|
10003
|
+
if (!toolCallId) return;
|
|
10004
|
+
const errorText = typeof chunk.errorText === "string" ? chunk.errorText : "";
|
|
10005
|
+
const toolAction = this.getOrCreateToolAction(toolCallId);
|
|
10006
|
+
toolAction.errorText = errorText || toolAction.errorText;
|
|
10007
|
+
toolAction.status = "failed";
|
|
10008
|
+
this.pushLog(`← tool ${toolAction.toolName ?? "<tool>"} error (${toolCallId})`);
|
|
10009
|
+
return;
|
|
10010
|
+
}
|
|
10011
|
+
case "data-agent-unknown": {
|
|
10012
|
+
this.unknownEventCount++;
|
|
10013
|
+
const data = chunk.data;
|
|
10014
|
+
const reason = typeof data?.reason === "string" ? data.reason : "unknown chunk";
|
|
10015
|
+
this.pushWarning(reason);
|
|
10016
|
+
this.pushLog(`! unknown: ${reason}`);
|
|
10017
|
+
return;
|
|
10018
|
+
}
|
|
10019
|
+
default: return;
|
|
10020
|
+
}
|
|
10021
|
+
}
|
|
10022
|
+
pushUiChunks(chunks) {
|
|
10023
|
+
chunks.forEach((chunk) => this.pushUiChunk(chunk));
|
|
10024
|
+
}
|
|
10025
|
+
snapshot() {
|
|
10026
|
+
const actions = Array.from(this.toolActions.values()).sort((a, b) => a._order - b._order).map(({ _order: _unused, ...rest }) => rest);
|
|
10027
|
+
return {
|
|
10028
|
+
leadingText: this.leadingText,
|
|
10029
|
+
leadingReasoning: this.leadingReasoning,
|
|
10030
|
+
toolActions: actions,
|
|
10031
|
+
logLines: [...this.logLines],
|
|
10032
|
+
warnings: [...this.warnings],
|
|
10033
|
+
unknownEventCount: this.unknownEventCount
|
|
10034
|
+
};
|
|
10035
|
+
}
|
|
10036
|
+
};
|
|
10037
|
+
|
|
9709
10038
|
//#endregion
|
|
9710
10039
|
//#region src/core/index.ts
|
|
9711
10040
|
function normalizeSkills(value) {
|
|
@@ -32245,9 +32574,1607 @@ function createShowTasksCommand(version) {
|
|
|
32245
32574
|
});
|
|
32246
32575
|
}
|
|
32247
32576
|
|
|
32577
|
+
//#endregion
|
|
32578
|
+
//#region ../agent-stream-parser/src/utils.ts
|
|
32579
|
+
function isRecord(value) {
|
|
32580
|
+
return typeof value === "object" && value !== null;
|
|
32581
|
+
}
|
|
32582
|
+
function safeJsonParse(text) {
|
|
32583
|
+
try {
|
|
32584
|
+
return {
|
|
32585
|
+
ok: true,
|
|
32586
|
+
value: JSON.parse(text)
|
|
32587
|
+
};
|
|
32588
|
+
} catch (error) {
|
|
32589
|
+
const sanitized = sanitizeMalformedJsonLine(text);
|
|
32590
|
+
if (sanitized !== text) try {
|
|
32591
|
+
return {
|
|
32592
|
+
ok: true,
|
|
32593
|
+
value: JSON.parse(sanitized)
|
|
32594
|
+
};
|
|
32595
|
+
} catch {}
|
|
32596
|
+
return {
|
|
32597
|
+
ok: false,
|
|
32598
|
+
error
|
|
32599
|
+
};
|
|
32600
|
+
}
|
|
32601
|
+
}
|
|
32602
|
+
function sanitizeMalformedJsonLine(text) {
|
|
32603
|
+
return text.replaceAll("", "'").replaceAll("", "—").replaceAll("", "—").replaceAll(/[\u0000-\u001F]/g, "");
|
|
32604
|
+
}
|
|
32605
|
+
function toErrorMessage(error) {
|
|
32606
|
+
if (error instanceof Error) return error.message;
|
|
32607
|
+
try {
|
|
32608
|
+
return JSON.stringify(error);
|
|
32609
|
+
} catch {
|
|
32610
|
+
return String(error);
|
|
32611
|
+
}
|
|
32612
|
+
}
|
|
32613
|
+
function computeAppendDelta(previous, next) {
|
|
32614
|
+
if (next.startsWith(previous)) return {
|
|
32615
|
+
delta: next.slice(previous.length),
|
|
32616
|
+
reset: false
|
|
32617
|
+
};
|
|
32618
|
+
return {
|
|
32619
|
+
delta: next,
|
|
32620
|
+
reset: true
|
|
32621
|
+
};
|
|
32622
|
+
}
|
|
32623
|
+
|
|
32624
|
+
//#endregion
|
|
32625
|
+
//#region ../agent-stream-parser/src/codex.ts
|
|
32626
|
+
const usageSchema = z$1.object({
|
|
32627
|
+
input_tokens: z$1.number(),
|
|
32628
|
+
cached_input_tokens: z$1.number(),
|
|
32629
|
+
output_tokens: z$1.number()
|
|
32630
|
+
});
|
|
32631
|
+
const agentMessageItemSchema = z$1.object({
|
|
32632
|
+
id: z$1.string(),
|
|
32633
|
+
type: z$1.literal("agent_message"),
|
|
32634
|
+
text: z$1.string()
|
|
32635
|
+
});
|
|
32636
|
+
const reasoningItemSchema = z$1.object({
|
|
32637
|
+
id: z$1.string(),
|
|
32638
|
+
type: z$1.literal("reasoning"),
|
|
32639
|
+
text: z$1.string()
|
|
32640
|
+
});
|
|
32641
|
+
const commandExecutionItemSchema = z$1.object({
|
|
32642
|
+
id: z$1.string(),
|
|
32643
|
+
type: z$1.literal("command_execution"),
|
|
32644
|
+
command: z$1.string(),
|
|
32645
|
+
aggregated_output: z$1.string(),
|
|
32646
|
+
exit_code: z$1.preprocess((value) => value === null ? void 0 : value, z$1.number().optional()),
|
|
32647
|
+
status: z$1.enum([
|
|
32648
|
+
"in_progress",
|
|
32649
|
+
"completed",
|
|
32650
|
+
"failed"
|
|
32651
|
+
])
|
|
32652
|
+
});
|
|
32653
|
+
const mcpToolCallItemSchema = z$1.object({
|
|
32654
|
+
id: z$1.string(),
|
|
32655
|
+
type: z$1.literal("mcp_tool_call"),
|
|
32656
|
+
server: z$1.string(),
|
|
32657
|
+
tool: z$1.string(),
|
|
32658
|
+
arguments: z$1.any(),
|
|
32659
|
+
result: z$1.object({
|
|
32660
|
+
content: z$1.array(z$1.any()),
|
|
32661
|
+
structured_content: z$1.any()
|
|
32662
|
+
}).optional(),
|
|
32663
|
+
error: z$1.object({ message: z$1.string() }).optional(),
|
|
32664
|
+
status: z$1.enum([
|
|
32665
|
+
"in_progress",
|
|
32666
|
+
"completed",
|
|
32667
|
+
"failed"
|
|
32668
|
+
])
|
|
32669
|
+
});
|
|
32670
|
+
const fileChangeItemSchema = z$1.object({
|
|
32671
|
+
id: z$1.string(),
|
|
32672
|
+
type: z$1.literal("file_change"),
|
|
32673
|
+
changes: z$1.array(z$1.object({
|
|
32674
|
+
path: z$1.string(),
|
|
32675
|
+
kind: z$1.enum([
|
|
32676
|
+
"add",
|
|
32677
|
+
"delete",
|
|
32678
|
+
"update"
|
|
32679
|
+
])
|
|
32680
|
+
})),
|
|
32681
|
+
status: z$1.enum(["completed", "failed"])
|
|
32682
|
+
});
|
|
32683
|
+
const webSearchItemSchema = z$1.object({
|
|
32684
|
+
id: z$1.string(),
|
|
32685
|
+
type: z$1.literal("web_search"),
|
|
32686
|
+
query: z$1.string()
|
|
32687
|
+
});
|
|
32688
|
+
const todoListItemSchema = z$1.object({
|
|
32689
|
+
id: z$1.string(),
|
|
32690
|
+
type: z$1.literal("todo_list"),
|
|
32691
|
+
items: z$1.array(z$1.object({
|
|
32692
|
+
text: z$1.string(),
|
|
32693
|
+
completed: z$1.boolean()
|
|
32694
|
+
}))
|
|
32695
|
+
});
|
|
32696
|
+
const errorItemSchema = z$1.object({
|
|
32697
|
+
id: z$1.string(),
|
|
32698
|
+
type: z$1.literal("error"),
|
|
32699
|
+
message: z$1.string()
|
|
32700
|
+
});
|
|
32701
|
+
const threadItemSchema = z$1.discriminatedUnion("type", [
|
|
32702
|
+
agentMessageItemSchema,
|
|
32703
|
+
reasoningItemSchema,
|
|
32704
|
+
commandExecutionItemSchema,
|
|
32705
|
+
mcpToolCallItemSchema,
|
|
32706
|
+
fileChangeItemSchema,
|
|
32707
|
+
webSearchItemSchema,
|
|
32708
|
+
todoListItemSchema,
|
|
32709
|
+
errorItemSchema
|
|
32710
|
+
]);
|
|
32711
|
+
const threadEventSchema = z$1.discriminatedUnion("type", [
|
|
32712
|
+
z$1.object({
|
|
32713
|
+
type: z$1.literal("thread.started"),
|
|
32714
|
+
thread_id: z$1.string()
|
|
32715
|
+
}),
|
|
32716
|
+
z$1.object({ type: z$1.literal("turn.started") }),
|
|
32717
|
+
z$1.object({
|
|
32718
|
+
type: z$1.literal("turn.completed"),
|
|
32719
|
+
usage: usageSchema
|
|
32720
|
+
}),
|
|
32721
|
+
z$1.object({
|
|
32722
|
+
type: z$1.literal("turn.failed"),
|
|
32723
|
+
error: z$1.object({ message: z$1.string() })
|
|
32724
|
+
}),
|
|
32725
|
+
z$1.object({
|
|
32726
|
+
type: z$1.literal("item.started"),
|
|
32727
|
+
item: threadItemSchema
|
|
32728
|
+
}),
|
|
32729
|
+
z$1.object({
|
|
32730
|
+
type: z$1.literal("item.updated"),
|
|
32731
|
+
item: threadItemSchema
|
|
32732
|
+
}),
|
|
32733
|
+
z$1.object({
|
|
32734
|
+
type: z$1.literal("item.completed"),
|
|
32735
|
+
item: threadItemSchema
|
|
32736
|
+
}),
|
|
32737
|
+
z$1.object({
|
|
32738
|
+
type: z$1.literal("error"),
|
|
32739
|
+
message: z$1.string()
|
|
32740
|
+
})
|
|
32741
|
+
]);
|
|
32742
|
+
function createCodexNormalizer(options = {}) {
|
|
32743
|
+
const includeMetaEvents = options.includeMetaEvents ?? false;
|
|
32744
|
+
const activeText = /* @__PURE__ */ new Map();
|
|
32745
|
+
const activeReasoning = /* @__PURE__ */ new Map();
|
|
32746
|
+
function maybeMetaEvent(event) {
|
|
32747
|
+
if (!includeMetaEvents) return [];
|
|
32748
|
+
return [{
|
|
32749
|
+
type: "data-agent-event",
|
|
32750
|
+
data: event
|
|
32751
|
+
}];
|
|
32752
|
+
}
|
|
32753
|
+
function handleTextLike({ map, id, fullText, eventType, startType, deltaType, endType }) {
|
|
32754
|
+
const out = [];
|
|
32755
|
+
let state = map.get(id);
|
|
32756
|
+
if (!state || !state.open) {
|
|
32757
|
+
state = {
|
|
32758
|
+
lastText: "",
|
|
32759
|
+
open: true
|
|
32760
|
+
};
|
|
32761
|
+
map.set(id, state);
|
|
32762
|
+
out.push({
|
|
32763
|
+
type: startType,
|
|
32764
|
+
id
|
|
32765
|
+
});
|
|
32766
|
+
}
|
|
32767
|
+
const { delta, reset } = computeAppendDelta(state.lastText, fullText);
|
|
32768
|
+
if (delta) {
|
|
32769
|
+
out.push({
|
|
32770
|
+
type: deltaType,
|
|
32771
|
+
id,
|
|
32772
|
+
delta
|
|
32773
|
+
});
|
|
32774
|
+
state.lastText = reset ? fullText : state.lastText + delta;
|
|
32775
|
+
}
|
|
32776
|
+
if (eventType === "item.completed") {
|
|
32777
|
+
out.push({
|
|
32778
|
+
type: endType,
|
|
32779
|
+
id
|
|
32780
|
+
});
|
|
32781
|
+
state.open = false;
|
|
32782
|
+
}
|
|
32783
|
+
return out;
|
|
32784
|
+
}
|
|
32785
|
+
function normalizeItemEvent(eventType, item) {
|
|
32786
|
+
const out = [];
|
|
32787
|
+
switch (item.type) {
|
|
32788
|
+
case "agent_message": return {
|
|
32789
|
+
recognized: true,
|
|
32790
|
+
chunks: handleTextLike({
|
|
32791
|
+
map: activeText,
|
|
32792
|
+
id: item.id,
|
|
32793
|
+
fullText: item.text,
|
|
32794
|
+
eventType,
|
|
32795
|
+
startType: "text-start",
|
|
32796
|
+
deltaType: "text-delta",
|
|
32797
|
+
endType: "text-end"
|
|
32798
|
+
})
|
|
32799
|
+
};
|
|
32800
|
+
case "reasoning": return {
|
|
32801
|
+
recognized: true,
|
|
32802
|
+
chunks: handleTextLike({
|
|
32803
|
+
map: activeReasoning,
|
|
32804
|
+
id: item.id,
|
|
32805
|
+
fullText: item.text,
|
|
32806
|
+
eventType,
|
|
32807
|
+
startType: "reasoning-start",
|
|
32808
|
+
deltaType: "reasoning-delta",
|
|
32809
|
+
endType: "reasoning-end"
|
|
32810
|
+
})
|
|
32811
|
+
};
|
|
32812
|
+
case "command_execution": {
|
|
32813
|
+
const toolCallId = item.id;
|
|
32814
|
+
out.push({
|
|
32815
|
+
type: "tool-input-available",
|
|
32816
|
+
toolCallId,
|
|
32817
|
+
toolName: "command_execution",
|
|
32818
|
+
title: item.command,
|
|
32819
|
+
input: { command: item.command },
|
|
32820
|
+
dynamic: true
|
|
32821
|
+
});
|
|
32822
|
+
if (item.status === "completed") out.push({
|
|
32823
|
+
type: "tool-output-available",
|
|
32824
|
+
toolCallId,
|
|
32825
|
+
output: {
|
|
32826
|
+
exit_code: item.exit_code ?? null,
|
|
32827
|
+
aggregated_output: item.aggregated_output
|
|
32828
|
+
},
|
|
32829
|
+
dynamic: true
|
|
32830
|
+
});
|
|
32831
|
+
else if (item.status === "failed") {
|
|
32832
|
+
const exitCode = item.exit_code;
|
|
32833
|
+
out.push({
|
|
32834
|
+
type: "tool-output-error",
|
|
32835
|
+
toolCallId,
|
|
32836
|
+
errorText: exitCode != null ? `Command failed (exit_code=${exitCode}).\n${item.aggregated_output}` : `Command failed.\n${item.aggregated_output}`,
|
|
32837
|
+
dynamic: true
|
|
32838
|
+
});
|
|
32839
|
+
}
|
|
32840
|
+
return {
|
|
32841
|
+
recognized: true,
|
|
32842
|
+
chunks: out
|
|
32843
|
+
};
|
|
32844
|
+
}
|
|
32845
|
+
case "mcp_tool_call": {
|
|
32846
|
+
const toolCallId = item.id;
|
|
32847
|
+
out.push({
|
|
32848
|
+
type: "tool-input-available",
|
|
32849
|
+
toolCallId,
|
|
32850
|
+
toolName: "mcp_tool_call",
|
|
32851
|
+
title: `${item.server}.${item.tool}`,
|
|
32852
|
+
input: item.arguments,
|
|
32853
|
+
dynamic: true
|
|
32854
|
+
});
|
|
32855
|
+
if (item.status === "completed") out.push({
|
|
32856
|
+
type: "tool-output-available",
|
|
32857
|
+
toolCallId,
|
|
32858
|
+
output: item.result?.structured_content ?? null,
|
|
32859
|
+
dynamic: true
|
|
32860
|
+
});
|
|
32861
|
+
else if (item.status === "failed") out.push({
|
|
32862
|
+
type: "tool-output-error",
|
|
32863
|
+
toolCallId,
|
|
32864
|
+
errorText: item.error?.message ?? "MCP tool call failed",
|
|
32865
|
+
dynamic: true
|
|
32866
|
+
});
|
|
32867
|
+
return {
|
|
32868
|
+
recognized: true,
|
|
32869
|
+
chunks: out
|
|
32870
|
+
};
|
|
32871
|
+
}
|
|
32872
|
+
case "file_change": {
|
|
32873
|
+
const toolCallId = item.id;
|
|
32874
|
+
out.push({
|
|
32875
|
+
type: "tool-input-available",
|
|
32876
|
+
toolCallId,
|
|
32877
|
+
toolName: "file_change",
|
|
32878
|
+
input: { changes: item.changes },
|
|
32879
|
+
dynamic: true
|
|
32880
|
+
});
|
|
32881
|
+
if (item.status === "completed") out.push({
|
|
32882
|
+
type: "tool-output-available",
|
|
32883
|
+
toolCallId,
|
|
32884
|
+
output: { status: "completed" },
|
|
32885
|
+
dynamic: true
|
|
32886
|
+
});
|
|
32887
|
+
else out.push({
|
|
32888
|
+
type: "tool-output-error",
|
|
32889
|
+
toolCallId,
|
|
32890
|
+
errorText: "Patch apply failed",
|
|
32891
|
+
dynamic: true
|
|
32892
|
+
});
|
|
32893
|
+
return {
|
|
32894
|
+
recognized: true,
|
|
32895
|
+
chunks: out
|
|
32896
|
+
};
|
|
32897
|
+
}
|
|
32898
|
+
case "web_search": {
|
|
32899
|
+
const toolCallId = item.id;
|
|
32900
|
+
out.push({
|
|
32901
|
+
type: "tool-input-available",
|
|
32902
|
+
toolCallId,
|
|
32903
|
+
toolName: "web_search",
|
|
32904
|
+
title: item.query,
|
|
32905
|
+
input: { query: item.query },
|
|
32906
|
+
dynamic: true
|
|
32907
|
+
});
|
|
32908
|
+
if (eventType === "item.completed") out.push({
|
|
32909
|
+
type: "tool-output-available",
|
|
32910
|
+
toolCallId,
|
|
32911
|
+
output: null,
|
|
32912
|
+
dynamic: true
|
|
32913
|
+
});
|
|
32914
|
+
return {
|
|
32915
|
+
recognized: true,
|
|
32916
|
+
chunks: out
|
|
32917
|
+
};
|
|
32918
|
+
}
|
|
32919
|
+
case "todo_list": return {
|
|
32920
|
+
recognized: true,
|
|
32921
|
+
chunks: maybeMetaEvent({
|
|
32922
|
+
kind: "codex.todo_list",
|
|
32923
|
+
items: item.items
|
|
32924
|
+
})
|
|
32925
|
+
};
|
|
32926
|
+
case "error":
|
|
32927
|
+
out.push({
|
|
32928
|
+
type: "error",
|
|
32929
|
+
errorText: item.message
|
|
32930
|
+
});
|
|
32931
|
+
out.push(...maybeMetaEvent({
|
|
32932
|
+
kind: "codex.error",
|
|
32933
|
+
message: item.message
|
|
32934
|
+
}));
|
|
32935
|
+
return {
|
|
32936
|
+
recognized: true,
|
|
32937
|
+
chunks: out
|
|
32938
|
+
};
|
|
32939
|
+
default: return {
|
|
32940
|
+
recognized: false,
|
|
32941
|
+
chunks: []
|
|
32942
|
+
};
|
|
32943
|
+
}
|
|
32944
|
+
}
|
|
32945
|
+
function push(chunk) {
|
|
32946
|
+
const parsed = threadEventSchema.safeParse(chunk);
|
|
32947
|
+
if (!parsed.success) return {
|
|
32948
|
+
recognized: false,
|
|
32949
|
+
chunks: []
|
|
32950
|
+
};
|
|
32951
|
+
const event = parsed.data;
|
|
32952
|
+
switch (event.type) {
|
|
32953
|
+
case "thread.started": {
|
|
32954
|
+
const out = [{
|
|
32955
|
+
type: "message-metadata",
|
|
32956
|
+
messageMetadata: {
|
|
32957
|
+
threadId: event.thread_id,
|
|
32958
|
+
source: "codex"
|
|
32959
|
+
}
|
|
32960
|
+
}];
|
|
32961
|
+
out.push(...maybeMetaEvent({
|
|
32962
|
+
kind: "codex.thread.started",
|
|
32963
|
+
threadId: event.thread_id
|
|
32964
|
+
}));
|
|
32965
|
+
return {
|
|
32966
|
+
recognized: true,
|
|
32967
|
+
chunks: out
|
|
32968
|
+
};
|
|
32969
|
+
}
|
|
32970
|
+
case "turn.started": {
|
|
32971
|
+
const out = [{ type: "start-step" }];
|
|
32972
|
+
out.push(...maybeMetaEvent({ kind: "codex.turn.started" }));
|
|
32973
|
+
return {
|
|
32974
|
+
recognized: true,
|
|
32975
|
+
chunks: out
|
|
32976
|
+
};
|
|
32977
|
+
}
|
|
32978
|
+
case "turn.completed": {
|
|
32979
|
+
const out = [{ type: "finish-step" }];
|
|
32980
|
+
out.push(...maybeMetaEvent({
|
|
32981
|
+
kind: "codex.turn.completed",
|
|
32982
|
+
usage: event.usage
|
|
32983
|
+
}));
|
|
32984
|
+
return {
|
|
32985
|
+
recognized: true,
|
|
32986
|
+
chunks: out
|
|
32987
|
+
};
|
|
32988
|
+
}
|
|
32989
|
+
case "turn.failed": {
|
|
32990
|
+
const out = [{
|
|
32991
|
+
type: "error",
|
|
32992
|
+
errorText: event.error.message
|
|
32993
|
+
}, { type: "finish-step" }];
|
|
32994
|
+
out.push(...maybeMetaEvent({
|
|
32995
|
+
kind: "codex.turn.failed",
|
|
32996
|
+
message: event.error.message
|
|
32997
|
+
}));
|
|
32998
|
+
return {
|
|
32999
|
+
recognized: true,
|
|
33000
|
+
chunks: out
|
|
33001
|
+
};
|
|
33002
|
+
}
|
|
33003
|
+
case "error": {
|
|
33004
|
+
const out = [{
|
|
33005
|
+
type: "error",
|
|
33006
|
+
errorText: event.message
|
|
33007
|
+
}];
|
|
33008
|
+
out.push(...maybeMetaEvent({
|
|
33009
|
+
kind: "codex.error",
|
|
33010
|
+
message: event.message
|
|
33011
|
+
}));
|
|
33012
|
+
return {
|
|
33013
|
+
recognized: true,
|
|
33014
|
+
chunks: out
|
|
33015
|
+
};
|
|
33016
|
+
}
|
|
33017
|
+
case "item.started":
|
|
33018
|
+
case "item.updated":
|
|
33019
|
+
case "item.completed": return normalizeItemEvent(event.type, event.item);
|
|
33020
|
+
default: return {
|
|
33021
|
+
recognized: false,
|
|
33022
|
+
chunks: []
|
|
33023
|
+
};
|
|
33024
|
+
}
|
|
33025
|
+
}
|
|
33026
|
+
function finish() {
|
|
33027
|
+
const out = [];
|
|
33028
|
+
for (const [id, state] of activeText) if (state.open) {
|
|
33029
|
+
out.push({
|
|
33030
|
+
type: "text-end",
|
|
33031
|
+
id
|
|
33032
|
+
});
|
|
33033
|
+
state.open = false;
|
|
33034
|
+
}
|
|
33035
|
+
for (const [id, state] of activeReasoning) if (state.open) {
|
|
33036
|
+
out.push({
|
|
33037
|
+
type: "reasoning-end",
|
|
33038
|
+
id
|
|
33039
|
+
});
|
|
33040
|
+
state.open = false;
|
|
33041
|
+
}
|
|
33042
|
+
return out;
|
|
33043
|
+
}
|
|
33044
|
+
return {
|
|
33045
|
+
push,
|
|
33046
|
+
finish
|
|
33047
|
+
};
|
|
33048
|
+
}
|
|
33049
|
+
|
|
33050
|
+
//#endregion
|
|
33051
|
+
//#region ../agent-stream-parser/src/claude.ts
|
|
33052
|
+
const topLevelSchema = z$1.object({ type: z$1.string() }).passthrough();
|
|
33053
|
+
const streamEventSchema = z$1.object({
|
|
33054
|
+
type: z$1.string(),
|
|
33055
|
+
index: z$1.number().optional()
|
|
33056
|
+
}).passthrough();
|
|
33057
|
+
function createClaudeCodeNormalizer(options = {}) {
|
|
33058
|
+
const includeMetaEvents = options.includeMetaEvents ?? false;
|
|
33059
|
+
const textBlocks = /* @__PURE__ */ new Map();
|
|
33060
|
+
const toolBlocks = /* @__PURE__ */ new Map();
|
|
33061
|
+
let sawStreamEvents = false;
|
|
33062
|
+
function maybeMeta(event) {
|
|
33063
|
+
if (!includeMetaEvents) return [];
|
|
33064
|
+
return [{
|
|
33065
|
+
type: "data-agent-event",
|
|
33066
|
+
data: event
|
|
33067
|
+
}];
|
|
33068
|
+
}
|
|
33069
|
+
function ensureTextBlock(index) {
|
|
33070
|
+
const existing = textBlocks.get(index);
|
|
33071
|
+
if (existing?.open) return [];
|
|
33072
|
+
const id = existing?.id ?? `claude-text-${index}`;
|
|
33073
|
+
textBlocks.set(index, {
|
|
33074
|
+
id,
|
|
33075
|
+
open: true
|
|
33076
|
+
});
|
|
33077
|
+
return [{
|
|
33078
|
+
type: "text-start",
|
|
33079
|
+
id
|
|
33080
|
+
}];
|
|
33081
|
+
}
|
|
33082
|
+
function ensureToolBlock(index, block) {
|
|
33083
|
+
const existing = toolBlocks.get(index);
|
|
33084
|
+
if (existing?.open) return [];
|
|
33085
|
+
const toolCallId = block?.id ?? existing?.toolCallId ?? `claude-tool-${index}`;
|
|
33086
|
+
const toolName = block?.name ?? existing?.toolName ?? "tool";
|
|
33087
|
+
toolBlocks.set(index, {
|
|
33088
|
+
toolCallId,
|
|
33089
|
+
toolName,
|
|
33090
|
+
json: "",
|
|
33091
|
+
open: true
|
|
33092
|
+
});
|
|
33093
|
+
return [{
|
|
33094
|
+
type: "tool-input-start",
|
|
33095
|
+
toolCallId,
|
|
33096
|
+
toolName,
|
|
33097
|
+
dynamic: true
|
|
33098
|
+
}];
|
|
33099
|
+
}
|
|
33100
|
+
function handleStreamEvent(event) {
|
|
33101
|
+
if (!isRecord(event) || typeof event.type !== "string") return [];
|
|
33102
|
+
const out = [];
|
|
33103
|
+
const index = typeof event.index === "number" ? event.index : void 0;
|
|
33104
|
+
switch (event.type) {
|
|
33105
|
+
case "content_block_start": {
|
|
33106
|
+
if (index == null) return [];
|
|
33107
|
+
const block = event.content_block;
|
|
33108
|
+
if (!isRecord(block) || typeof block.type !== "string") return [];
|
|
33109
|
+
sawStreamEvents = true;
|
|
33110
|
+
if (block.type === "text") out.push(...ensureTextBlock(index));
|
|
33111
|
+
else if (block.type === "tool_use") out.push(...ensureToolBlock(index, {
|
|
33112
|
+
id: typeof block.id === "string" ? block.id : void 0,
|
|
33113
|
+
name: typeof block.name === "string" ? block.name : void 0
|
|
33114
|
+
}));
|
|
33115
|
+
return out;
|
|
33116
|
+
}
|
|
33117
|
+
case "content_block_delta": {
|
|
33118
|
+
if (index == null) return [];
|
|
33119
|
+
const delta = event.delta;
|
|
33120
|
+
if (!isRecord(delta) || typeof delta.type !== "string") return [];
|
|
33121
|
+
sawStreamEvents = true;
|
|
33122
|
+
if (delta.type === "text_delta") {
|
|
33123
|
+
const text = typeof delta.text === "string" ? delta.text : "";
|
|
33124
|
+
if (!text) return [];
|
|
33125
|
+
out.push(...ensureTextBlock(index));
|
|
33126
|
+
const id = textBlocks.get(index).id;
|
|
33127
|
+
out.push({
|
|
33128
|
+
type: "text-delta",
|
|
33129
|
+
id,
|
|
33130
|
+
delta: text
|
|
33131
|
+
});
|
|
33132
|
+
return out;
|
|
33133
|
+
}
|
|
33134
|
+
if (delta.type === "input_json_delta") {
|
|
33135
|
+
const partialJson = typeof delta.partial_json === "string" ? delta.partial_json : "";
|
|
33136
|
+
if (!partialJson) return [];
|
|
33137
|
+
out.push(...ensureToolBlock(index));
|
|
33138
|
+
const tool = toolBlocks.get(index);
|
|
33139
|
+
tool.json += partialJson;
|
|
33140
|
+
out.push({
|
|
33141
|
+
type: "tool-input-delta",
|
|
33142
|
+
toolCallId: tool.toolCallId,
|
|
33143
|
+
inputTextDelta: partialJson
|
|
33144
|
+
});
|
|
33145
|
+
return out;
|
|
33146
|
+
}
|
|
33147
|
+
return [];
|
|
33148
|
+
}
|
|
33149
|
+
case "content_block_stop": {
|
|
33150
|
+
if (index == null) return [];
|
|
33151
|
+
sawStreamEvents = true;
|
|
33152
|
+
const text = textBlocks.get(index);
|
|
33153
|
+
if (text?.open) {
|
|
33154
|
+
text.open = false;
|
|
33155
|
+
out.push({
|
|
33156
|
+
type: "text-end",
|
|
33157
|
+
id: text.id
|
|
33158
|
+
});
|
|
33159
|
+
}
|
|
33160
|
+
const tool = toolBlocks.get(index);
|
|
33161
|
+
if (tool?.open) {
|
|
33162
|
+
tool.open = false;
|
|
33163
|
+
let input = tool.json ? tool.json : {};
|
|
33164
|
+
if (typeof input === "string") {
|
|
33165
|
+
const parsed = safeJsonParse(input);
|
|
33166
|
+
if (parsed.ok) input = parsed.value;
|
|
33167
|
+
}
|
|
33168
|
+
out.push({
|
|
33169
|
+
type: "tool-input-available",
|
|
33170
|
+
toolCallId: tool.toolCallId,
|
|
33171
|
+
toolName: tool.toolName,
|
|
33172
|
+
input,
|
|
33173
|
+
dynamic: true
|
|
33174
|
+
});
|
|
33175
|
+
}
|
|
33176
|
+
return out;
|
|
33177
|
+
}
|
|
33178
|
+
default: return [];
|
|
33179
|
+
}
|
|
33180
|
+
}
|
|
33181
|
+
function handleAssistantMessage(message) {
|
|
33182
|
+
if (sawStreamEvents) return [];
|
|
33183
|
+
const blocks = Array.isArray(message.content) ? message.content : [];
|
|
33184
|
+
const out = [];
|
|
33185
|
+
blocks.forEach((block, index) => {
|
|
33186
|
+
if (!isRecord(block) || typeof block.type !== "string") return;
|
|
33187
|
+
if (block.type === "text") {
|
|
33188
|
+
const id = `claude-assistant-text-${index}`;
|
|
33189
|
+
const text = typeof block.text === "string" ? block.text : "";
|
|
33190
|
+
out.push({
|
|
33191
|
+
type: "text-start",
|
|
33192
|
+
id
|
|
33193
|
+
});
|
|
33194
|
+
if (text) out.push({
|
|
33195
|
+
type: "text-delta",
|
|
33196
|
+
id,
|
|
33197
|
+
delta: text
|
|
33198
|
+
});
|
|
33199
|
+
out.push({
|
|
33200
|
+
type: "text-end",
|
|
33201
|
+
id
|
|
33202
|
+
});
|
|
33203
|
+
}
|
|
33204
|
+
if (block.type === "tool_use") {
|
|
33205
|
+
const toolCallId = typeof block.id === "string" ? block.id : `claude-assistant-tool-${index}`;
|
|
33206
|
+
const toolName = typeof block.name === "string" ? block.name : "tool";
|
|
33207
|
+
const input = block.input;
|
|
33208
|
+
out.push({
|
|
33209
|
+
type: "tool-input-available",
|
|
33210
|
+
toolCallId,
|
|
33211
|
+
toolName,
|
|
33212
|
+
input,
|
|
33213
|
+
dynamic: true
|
|
33214
|
+
});
|
|
33215
|
+
}
|
|
33216
|
+
});
|
|
33217
|
+
return out;
|
|
33218
|
+
}
|
|
33219
|
+
function push(chunk) {
|
|
33220
|
+
const parsed = topLevelSchema.safeParse(chunk);
|
|
33221
|
+
if (!parsed.success) return {
|
|
33222
|
+
recognized: false,
|
|
33223
|
+
chunks: []
|
|
33224
|
+
};
|
|
33225
|
+
const msg = parsed.data;
|
|
33226
|
+
const out = [];
|
|
33227
|
+
switch (msg.type) {
|
|
33228
|
+
case "system": {
|
|
33229
|
+
const model = typeof msg.model === "string" ? msg.model : void 0;
|
|
33230
|
+
const sessionId = typeof msg.session_id === "string" ? msg.session_id : typeof msg.sessionId === "string" ? msg.sessionId : void 0;
|
|
33231
|
+
out.push({
|
|
33232
|
+
type: "message-metadata",
|
|
33233
|
+
messageMetadata: {
|
|
33234
|
+
source: "claude-code",
|
|
33235
|
+
model,
|
|
33236
|
+
sessionId
|
|
33237
|
+
}
|
|
33238
|
+
});
|
|
33239
|
+
out.push(...maybeMeta({
|
|
33240
|
+
kind: "claude.system",
|
|
33241
|
+
model,
|
|
33242
|
+
sessionId
|
|
33243
|
+
}));
|
|
33244
|
+
return {
|
|
33245
|
+
recognized: true,
|
|
33246
|
+
chunks: out
|
|
33247
|
+
};
|
|
33248
|
+
}
|
|
33249
|
+
case "stream_event": {
|
|
33250
|
+
sawStreamEvents = true;
|
|
33251
|
+
const event = streamEventSchema.safeParse(msg.event);
|
|
33252
|
+
if (!event.success) return {
|
|
33253
|
+
recognized: false,
|
|
33254
|
+
chunks: []
|
|
33255
|
+
};
|
|
33256
|
+
return {
|
|
33257
|
+
recognized: true,
|
|
33258
|
+
chunks: handleStreamEvent(event.data)
|
|
33259
|
+
};
|
|
33260
|
+
}
|
|
33261
|
+
case "assistant": {
|
|
33262
|
+
const message = msg.message;
|
|
33263
|
+
if (!isRecord(message)) return {
|
|
33264
|
+
recognized: false,
|
|
33265
|
+
chunks: []
|
|
33266
|
+
};
|
|
33267
|
+
return {
|
|
33268
|
+
recognized: true,
|
|
33269
|
+
chunks: handleAssistantMessage(message)
|
|
33270
|
+
};
|
|
33271
|
+
}
|
|
33272
|
+
case "result": return {
|
|
33273
|
+
recognized: true,
|
|
33274
|
+
chunks: maybeMeta({
|
|
33275
|
+
kind: "claude.result",
|
|
33276
|
+
subtype: typeof msg.subtype === "string" ? msg.subtype : void 0,
|
|
33277
|
+
isError: typeof msg.is_error === "boolean" ? msg.is_error : void 0
|
|
33278
|
+
})
|
|
33279
|
+
};
|
|
33280
|
+
default: return {
|
|
33281
|
+
recognized: false,
|
|
33282
|
+
chunks: []
|
|
33283
|
+
};
|
|
33284
|
+
}
|
|
33285
|
+
}
|
|
33286
|
+
function finish() {
|
|
33287
|
+
const out = [];
|
|
33288
|
+
for (const [index, block] of textBlocks) if (block.open) {
|
|
33289
|
+
block.open = false;
|
|
33290
|
+
out.push({
|
|
33291
|
+
type: "text-end",
|
|
33292
|
+
id: block.id
|
|
33293
|
+
});
|
|
33294
|
+
}
|
|
33295
|
+
for (const [index, tool] of toolBlocks) if (tool.open) {
|
|
33296
|
+
tool.open = false;
|
|
33297
|
+
const reason = `Stream ended before tool input completed (index=${index}).`;
|
|
33298
|
+
out.push({
|
|
33299
|
+
type: "tool-input-error",
|
|
33300
|
+
toolCallId: tool.toolCallId,
|
|
33301
|
+
toolName: tool.toolName,
|
|
33302
|
+
input: tool.json,
|
|
33303
|
+
errorText: reason,
|
|
33304
|
+
dynamic: true
|
|
33305
|
+
});
|
|
33306
|
+
}
|
|
33307
|
+
return out;
|
|
33308
|
+
}
|
|
33309
|
+
return {
|
|
33310
|
+
push,
|
|
33311
|
+
finish
|
|
33312
|
+
};
|
|
33313
|
+
}
|
|
33314
|
+
|
|
33315
|
+
//#endregion
|
|
33316
|
+
//#region ../agent-stream-parser/src/normalizer.ts
|
|
33317
|
+
function createAgentStreamNormalizer(options) {
|
|
33318
|
+
const unknownChunkPolicy = options.unknownChunkPolicy ?? "ignore";
|
|
33319
|
+
const includeMetaEvents = options.includeMetaEvents ?? false;
|
|
33320
|
+
const emitStartChunk = options.emitStartChunk ?? true;
|
|
33321
|
+
const impl = options.source === "codex" ? createCodexNormalizer({ includeMetaEvents }) : createClaudeCodeNormalizer({ includeMetaEvents });
|
|
33322
|
+
let started = false;
|
|
33323
|
+
function maybeEmitStart() {
|
|
33324
|
+
if (!emitStartChunk || started) return [];
|
|
33325
|
+
started = true;
|
|
33326
|
+
const messageMetadata = { source: options.source };
|
|
33327
|
+
if (options.messageId) return [{
|
|
33328
|
+
type: "start",
|
|
33329
|
+
messageId: options.messageId,
|
|
33330
|
+
messageMetadata
|
|
33331
|
+
}];
|
|
33332
|
+
return [{
|
|
33333
|
+
type: "start",
|
|
33334
|
+
messageMetadata
|
|
33335
|
+
}];
|
|
33336
|
+
}
|
|
33337
|
+
function onUnknownChunk(chunk, reason) {
|
|
33338
|
+
if (unknownChunkPolicy === "ignore") return [];
|
|
33339
|
+
if (unknownChunkPolicy === "throw") throw new Error(`Unknown chunk: ${reason}`);
|
|
33340
|
+
return [{
|
|
33341
|
+
type: "data-agent-unknown",
|
|
33342
|
+
data: {
|
|
33343
|
+
source: options.source,
|
|
33344
|
+
reason,
|
|
33345
|
+
chunk
|
|
33346
|
+
}
|
|
33347
|
+
}];
|
|
33348
|
+
}
|
|
33349
|
+
function push(chunk) {
|
|
33350
|
+
let parsedChunk = chunk;
|
|
33351
|
+
if (typeof chunk === "string") {
|
|
33352
|
+
const parsed = safeJsonParse(chunk);
|
|
33353
|
+
if (!parsed.ok) {
|
|
33354
|
+
const unknown = onUnknownChunk(chunk, `invalid JSON string: ${toErrorMessage(parsed.error)}`);
|
|
33355
|
+
return unknown.length > 0 ? [...maybeEmitStart(), ...unknown] : [];
|
|
33356
|
+
}
|
|
33357
|
+
parsedChunk = parsed.value;
|
|
33358
|
+
}
|
|
33359
|
+
if (!isRecord(parsedChunk) || typeof parsedChunk.type !== "string") {
|
|
33360
|
+
const unknown = onUnknownChunk(chunk, "missing object with string 'type'");
|
|
33361
|
+
return unknown.length > 0 ? [...maybeEmitStart(), ...unknown] : [];
|
|
33362
|
+
}
|
|
33363
|
+
const result = impl.push(parsedChunk);
|
|
33364
|
+
if (result.chunks.length > 0) return [...maybeEmitStart(), ...result.chunks];
|
|
33365
|
+
if (result.recognized) return [];
|
|
33366
|
+
const type = typeof parsedChunk.type === "string" ? parsedChunk.type : "<unknown>";
|
|
33367
|
+
const unknown = onUnknownChunk(chunk, `unrecognized ${options.source} chunk type: ${type}`);
|
|
33368
|
+
return unknown.length > 0 ? [...maybeEmitStart(), ...unknown] : [];
|
|
33369
|
+
}
|
|
33370
|
+
function finish() {
|
|
33371
|
+
const out = impl.finish();
|
|
33372
|
+
if (out.length === 0) return [];
|
|
33373
|
+
return [...maybeEmitStart(), ...out];
|
|
33374
|
+
}
|
|
33375
|
+
return {
|
|
33376
|
+
source: options.source,
|
|
33377
|
+
push,
|
|
33378
|
+
finish
|
|
33379
|
+
};
|
|
33380
|
+
}
|
|
33381
|
+
|
|
33382
|
+
//#endregion
|
|
33383
|
+
//#region src/cli/commands/watch/format.ts
|
|
33384
|
+
function tryExtractBashCommand(command) {
|
|
33385
|
+
const wrapper = command.trim().match(/^(?:\/\S*\/bash|bash)\s+(-\S*c)\s+([\s\S]+)$/);
|
|
33386
|
+
if (!wrapper) return void 0;
|
|
33387
|
+
const rest = wrapper[2].trimStart();
|
|
33388
|
+
if (!rest) return void 0;
|
|
33389
|
+
if (rest.startsWith("$'")) {
|
|
33390
|
+
const end = rest.indexOf("'", 2);
|
|
33391
|
+
return end === -1 ? rest.slice(2) : rest.slice(2, end);
|
|
33392
|
+
}
|
|
33393
|
+
if (rest.startsWith("'")) {
|
|
33394
|
+
const end = rest.indexOf("'", 1);
|
|
33395
|
+
return end === -1 ? rest.slice(1) : rest.slice(1, end);
|
|
33396
|
+
}
|
|
33397
|
+
if (rest.startsWith("\"")) {
|
|
33398
|
+
let out = "";
|
|
33399
|
+
let escaped = false;
|
|
33400
|
+
for (let i = 1; i < rest.length; i++) {
|
|
33401
|
+
const ch = rest[i];
|
|
33402
|
+
if (escaped) {
|
|
33403
|
+
out += ch;
|
|
33404
|
+
escaped = false;
|
|
33405
|
+
continue;
|
|
33406
|
+
}
|
|
33407
|
+
if (ch === "\\") {
|
|
33408
|
+
escaped = true;
|
|
33409
|
+
continue;
|
|
33410
|
+
}
|
|
33411
|
+
if (ch === "\"") return out;
|
|
33412
|
+
out += ch;
|
|
33413
|
+
}
|
|
33414
|
+
return out;
|
|
33415
|
+
}
|
|
33416
|
+
return rest.trim();
|
|
33417
|
+
}
|
|
33418
|
+
function formatCommandExecutionDisplayCommand(command) {
|
|
33419
|
+
const trimmed = command.trim();
|
|
33420
|
+
return tryExtractBashCommand(trimmed)?.trim() || trimmed;
|
|
33421
|
+
}
|
|
33422
|
+
function formatPreviewLine(label, text, options = {}) {
|
|
33423
|
+
const trimmed = text.trim();
|
|
33424
|
+
if (!trimmed) return void 0;
|
|
33425
|
+
const oneLine = trimmed.replaceAll(/\s+/g, " ");
|
|
33426
|
+
const max = options.maxChars ?? 220;
|
|
33427
|
+
const preview = oneLine.length > max ? oneLine.slice(0, max - 1) + "…" : oneLine;
|
|
33428
|
+
return `${label}: ${options.formatValue ? options.formatValue(preview) : preview}`;
|
|
33429
|
+
}
|
|
33430
|
+
|
|
33431
|
+
//#endregion
|
|
33432
|
+
//#region src/cli/commands/watch.ts
|
|
33433
|
+
const PANTHEON_BASE_URL = "https://pantheon-ai.tidb.ai";
|
|
33434
|
+
function ensureEnvOrExit(keys) {
|
|
33435
|
+
const missing = keys.filter((key) => !process.env[key]);
|
|
33436
|
+
if (missing.length === 0) return;
|
|
33437
|
+
for (const key of missing) console.error(`${key} environment variable is not set.`);
|
|
33438
|
+
process.exit(1);
|
|
33439
|
+
}
|
|
33440
|
+
function createDb() {
|
|
33441
|
+
return new Kysely({ dialect: new MysqlDialect({ pool: createPool({
|
|
33442
|
+
uri: process.env.DATABASE_URL,
|
|
33443
|
+
supportBigNumbers: true,
|
|
33444
|
+
bigNumberStrings: true
|
|
33445
|
+
}) }) });
|
|
33446
|
+
}
|
|
33447
|
+
function formatDurationMs(ms) {
|
|
33448
|
+
const seconds = Math.max(0, Math.floor(ms / 1e3));
|
|
33449
|
+
const minutes = Math.floor(seconds / 60);
|
|
33450
|
+
const hours = Math.floor(minutes / 60);
|
|
33451
|
+
if (hours > 0) return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
|
|
33452
|
+
if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
|
|
33453
|
+
return `${seconds}s`;
|
|
33454
|
+
}
|
|
33455
|
+
function getTaskStartTime(task) {
|
|
33456
|
+
switch (task.status) {
|
|
33457
|
+
case "pending": return task.queued_at;
|
|
33458
|
+
case "running": return task.started_at;
|
|
33459
|
+
case "completed":
|
|
33460
|
+
case "failed": return task.started_at ?? task.queued_at;
|
|
33461
|
+
case "cancelled": return task.queued_at;
|
|
33462
|
+
}
|
|
33463
|
+
}
|
|
33464
|
+
function getTaskElapsedMs(task, now = /* @__PURE__ */ new Date()) {
|
|
33465
|
+
const start = getTaskStartTime(task);
|
|
33466
|
+
if (!start) return 0;
|
|
33467
|
+
switch (task.status) {
|
|
33468
|
+
case "completed": return task.ended_at.getTime() - start.getTime();
|
|
33469
|
+
case "failed": return (task.ended_at ?? now).getTime() - start.getTime();
|
|
33470
|
+
case "cancelled": return task.cancelled_at.getTime() - start.getTime();
|
|
33471
|
+
default: return now.getTime() - start.getTime();
|
|
33472
|
+
}
|
|
33473
|
+
}
|
|
33474
|
+
function toStreamSourceFromExecuteAgent(executeAgent) {
|
|
33475
|
+
if (!executeAgent) return void 0;
|
|
33476
|
+
const normalized = executeAgent.toLowerCase();
|
|
33477
|
+
if (normalized.includes("claude")) return "claude-code";
|
|
33478
|
+
if (normalized.includes("codex")) return "codex";
|
|
33479
|
+
}
|
|
33480
|
+
function detectStreamSourceFromRawPayload(payload) {
|
|
33481
|
+
if (payload.includes("\"type\":\"thread.") || payload.includes("\"type\":\"turn.") || payload.includes("\"type\":\"item.")) return "codex";
|
|
33482
|
+
if (payload.includes("\"type\":\"message_start\"") || payload.includes("\"type\":\"message_delta\"") || payload.includes("\"type\":\"message_stop\"") || payload.includes("\"type\":\"content_block_start\"") || payload.includes("\"type\":\"content_block_delta\"") || payload.includes("\"type\":\"content_block_stop\"")) return "claude-code";
|
|
33483
|
+
}
|
|
33484
|
+
async function getAgentExecuteAgentMap(db, targets) {
|
|
33485
|
+
const agents = Array.from(new Set(targets.map((t) => t.agent)));
|
|
33486
|
+
if (agents.length === 0) return /* @__PURE__ */ new Map();
|
|
33487
|
+
const configs = await db.selectFrom("agent_project_config").select([
|
|
33488
|
+
"agent",
|
|
33489
|
+
"project_id",
|
|
33490
|
+
"execute_agent"
|
|
33491
|
+
]).where("agent", "in", agents).execute();
|
|
33492
|
+
const map = /* @__PURE__ */ new Map();
|
|
33493
|
+
for (const row of configs) map.set(`${row.agent}|${row.project_id}`, row.execute_agent);
|
|
33494
|
+
return map;
|
|
33495
|
+
}
|
|
33496
|
+
async function loadTasksByIds(db, ids) {
|
|
33497
|
+
if (ids.length === 0) return [];
|
|
33498
|
+
const rows = await db.selectFrom("task").selectAll().where("id", "in", ids).execute();
|
|
33499
|
+
const out = [];
|
|
33500
|
+
for (const row of rows) {
|
|
33501
|
+
const parsed = taskItemSchema.safeParse(row);
|
|
33502
|
+
if (!parsed.success) {
|
|
33503
|
+
console.error(`Failed to parse task row id=${row.id} agent=${row.agent} status=${row.status}: ${parsed.error.message}`);
|
|
33504
|
+
continue;
|
|
33505
|
+
}
|
|
33506
|
+
out.push({
|
|
33507
|
+
agent: row.agent,
|
|
33508
|
+
task: parsed.data
|
|
33509
|
+
});
|
|
33510
|
+
}
|
|
33511
|
+
return out;
|
|
33512
|
+
}
|
|
33513
|
+
async function loadTasksForAgent(db, agent) {
|
|
33514
|
+
const rows = await db.selectFrom("task").selectAll().where("agent", "=", agent).execute();
|
|
33515
|
+
const out = [];
|
|
33516
|
+
for (const row of rows) {
|
|
33517
|
+
const parsed = taskItemSchema.safeParse(row);
|
|
33518
|
+
if (!parsed.success) continue;
|
|
33519
|
+
out.push(parsed.data);
|
|
33520
|
+
}
|
|
33521
|
+
return out;
|
|
33522
|
+
}
|
|
33523
|
+
async function loadAllTasks(db, limit) {
|
|
33524
|
+
const rows = await db.selectFrom("task").selectAll().orderBy("queued_at", "desc").limit(limit).execute();
|
|
33525
|
+
const out = [];
|
|
33526
|
+
for (const row of rows) {
|
|
33527
|
+
const parsed = taskItemSchema.safeParse(row);
|
|
33528
|
+
if (!parsed.success) continue;
|
|
33529
|
+
out.push({
|
|
33530
|
+
agent: row.agent,
|
|
33531
|
+
task: parsed.data
|
|
33532
|
+
});
|
|
33533
|
+
}
|
|
33534
|
+
return out;
|
|
33535
|
+
}
|
|
33536
|
+
async function resolveWatchTargets(options) {
|
|
33537
|
+
const warnings = [];
|
|
33538
|
+
let selected = [];
|
|
33539
|
+
if (options.taskIds.length > 0) {
|
|
33540
|
+
const uniqueTaskIds = Array.from(new Set(options.taskIds));
|
|
33541
|
+
const found = await loadTasksByIds(options.db, uniqueTaskIds);
|
|
33542
|
+
const foundIds = new Set(found.map((t) => t.task.id));
|
|
33543
|
+
const missing = uniqueTaskIds.filter((id) => !foundIds.has(id));
|
|
33544
|
+
if (missing.length > 0) warnings.push(`Requested task IDs not found: ${missing.join(", ")}`);
|
|
33545
|
+
const byId = new Map(found.map((t) => [t.task.id, t]));
|
|
33546
|
+
selected = uniqueTaskIds.map((id) => byId.get(id)).filter(Boolean);
|
|
33547
|
+
if (selected.length === 0) warnings.push("No requested tasks found; falling back to default selection.");
|
|
33548
|
+
} else if (options.agentNames.length > 0) {
|
|
33549
|
+
const uniqueAgents = Array.from(new Set(options.agentNames));
|
|
33550
|
+
if (uniqueAgents.length === 1) {
|
|
33551
|
+
selected = selectSingleAgentWatchTasks(await loadTasksForAgent(options.db, uniqueAgents[0]), { finishedLimit: 3 }).map((task) => ({
|
|
33552
|
+
agent: uniqueAgents[0],
|
|
33553
|
+
task
|
|
33554
|
+
}));
|
|
33555
|
+
if (selected.length === 0) warnings.push(`No tasks found for agent ${uniqueAgents[0]}; falling back to default selection.`);
|
|
33556
|
+
} else {
|
|
33557
|
+
selected = selectMultiAgentWatchTasks(await Promise.all(uniqueAgents.map(async (agent) => {
|
|
33558
|
+
return (await loadTasksForAgent(options.db, agent)).map((task) => ({
|
|
33559
|
+
agent,
|
|
33560
|
+
task
|
|
33561
|
+
}));
|
|
33562
|
+
})).then((groups) => groups.flat()), uniqueAgents);
|
|
33563
|
+
const foundAgents = new Set(selected.map((t) => t.agent));
|
|
33564
|
+
const missing = uniqueAgents.filter((agent) => !foundAgents.has(agent));
|
|
33565
|
+
if (missing.length > 0) warnings.push(`No tasks found for agent(s): ${missing.join(", ")}`);
|
|
33566
|
+
if (selected.length === 0) warnings.push("No requested agent tasks found; falling back to default selection.");
|
|
33567
|
+
}
|
|
33568
|
+
} else warnings.push("No targets specified; selecting up to 3 agents automatically.");
|
|
33569
|
+
if (selected.length === 0) selected = selectFallbackWatchTasks(await loadAllTasks(options.db, 500), { agentLimit: 3 });
|
|
33570
|
+
const executeAgentMap = await getAgentExecuteAgentMap(options.db, selected);
|
|
33571
|
+
return {
|
|
33572
|
+
targets: selected.map((entry) => ({
|
|
33573
|
+
...entry,
|
|
33574
|
+
executeAgent: executeAgentMap.get(`${entry.agent}|${entry.task.project_id}`)
|
|
33575
|
+
})),
|
|
33576
|
+
warnings
|
|
33577
|
+
};
|
|
33578
|
+
}
|
|
33579
|
+
function makeStreamUrl(streamId) {
|
|
33580
|
+
return `${PANTHEON_BASE_URL}/ai_stream_proxy/v2/streams/${encodeURIComponent(streamId)}/stream?format=opaque-stream-json`;
|
|
33581
|
+
}
|
|
33582
|
+
async function buildStepSnapshot(options) {
|
|
33583
|
+
const aggregator = new WatchStepAggregator();
|
|
33584
|
+
let inferredSource;
|
|
33585
|
+
let normalizer = void 0;
|
|
33586
|
+
const url = makeStreamUrl(options.streamId);
|
|
33587
|
+
const res = await fetch(url, { headers: {
|
|
33588
|
+
Authorization: `Bearer ${process.env.PANTHEON_API_KEY}`,
|
|
33589
|
+
Accept: "text/event-stream"
|
|
33590
|
+
} });
|
|
33591
|
+
if (!res.body) return { step: aggregator.snapshot() };
|
|
33592
|
+
await consumeOpaqueSseStream({
|
|
33593
|
+
stream: res.body,
|
|
33594
|
+
idleTimeoutMs: options.idleTimeoutMs,
|
|
33595
|
+
onWarning: (warning) => aggregator.pushUiChunk({
|
|
33596
|
+
type: "data-agent-unknown",
|
|
33597
|
+
data: { reason: warning }
|
|
33598
|
+
}),
|
|
33599
|
+
onData: (payload) => {
|
|
33600
|
+
if (!normalizer) {
|
|
33601
|
+
inferredSource = options.preferredSource ?? detectStreamSourceFromRawPayload(payload) ?? "codex";
|
|
33602
|
+
normalizer = createAgentStreamNormalizer({
|
|
33603
|
+
source: inferredSource,
|
|
33604
|
+
emitStartChunk: false,
|
|
33605
|
+
includeMetaEvents: false,
|
|
33606
|
+
unknownChunkPolicy: "emit"
|
|
33607
|
+
});
|
|
33608
|
+
}
|
|
33609
|
+
const chunks = normalizer.push(payload);
|
|
33610
|
+
aggregator.pushUiChunks(chunks);
|
|
33611
|
+
}
|
|
33612
|
+
});
|
|
33613
|
+
if (normalizer) aggregator.pushUiChunks(normalizer.finish());
|
|
33614
|
+
return {
|
|
33615
|
+
step: aggregator.snapshot(),
|
|
33616
|
+
inferredSource
|
|
33617
|
+
};
|
|
33618
|
+
}
|
|
33619
|
+
async function fetchBranchSnapshot(options) {
|
|
33620
|
+
const { task } = options;
|
|
33621
|
+
if (task.status !== "running" && task.status !== "completed" && task.status !== "failed") return {};
|
|
33622
|
+
const branchId = "branch_id" in task ? task.branch_id ?? void 0 : void 0;
|
|
33623
|
+
if (!branchId) return {};
|
|
33624
|
+
const branch = await getPantheonBranchInfo({
|
|
33625
|
+
projectId: task.project_id,
|
|
33626
|
+
branchId
|
|
33627
|
+
});
|
|
33628
|
+
return {
|
|
33629
|
+
branch,
|
|
33630
|
+
stepIndex: branch.latest_snap?.step_index,
|
|
33631
|
+
streamId: branch.latest_snap?.event_stream_id ?? void 0
|
|
33632
|
+
};
|
|
33633
|
+
}
|
|
33634
|
+
function printSnapshotLineBlock(lines) {
|
|
33635
|
+
lines.forEach((line) => console.log(line));
|
|
33636
|
+
}
|
|
33637
|
+
const ANSI = {
|
|
33638
|
+
reset: "\x1B[0m",
|
|
33639
|
+
bold: "\x1B[1m",
|
|
33640
|
+
dim: "\x1B[2m",
|
|
33641
|
+
red: "\x1B[31m",
|
|
33642
|
+
green: "\x1B[32m",
|
|
33643
|
+
yellow: "\x1B[33m",
|
|
33644
|
+
cyan: "\x1B[36m",
|
|
33645
|
+
gray: "\x1B[90m"
|
|
33646
|
+
};
|
|
33647
|
+
function useAnsiStyles() {
|
|
33648
|
+
if (!process.stdout.isTTY) return false;
|
|
33649
|
+
if (process.env.NO_COLOR != null) return false;
|
|
33650
|
+
return true;
|
|
33651
|
+
}
|
|
33652
|
+
function sgrWrap(text, code, enabled) {
|
|
33653
|
+
return enabled ? `${code}${text}${ANSI.reset}` : text;
|
|
33654
|
+
}
|
|
33655
|
+
function bold(text, enabled) {
|
|
33656
|
+
return sgrWrap(text, ANSI.bold, enabled);
|
|
33657
|
+
}
|
|
33658
|
+
function dim(text, enabled) {
|
|
33659
|
+
return sgrWrap(text, ANSI.dim, enabled);
|
|
33660
|
+
}
|
|
33661
|
+
function gray(text, enabled) {
|
|
33662
|
+
return sgrWrap(text, ANSI.gray, enabled);
|
|
33663
|
+
}
|
|
33664
|
+
function red(text, enabled) {
|
|
33665
|
+
return sgrWrap(text, ANSI.red, enabled);
|
|
33666
|
+
}
|
|
33667
|
+
function green(text, enabled) {
|
|
33668
|
+
return sgrWrap(text, ANSI.green, enabled);
|
|
33669
|
+
}
|
|
33670
|
+
function yellow(text, enabled) {
|
|
33671
|
+
return sgrWrap(text, ANSI.yellow, enabled);
|
|
33672
|
+
}
|
|
33673
|
+
function cyan(text, enabled) {
|
|
33674
|
+
return sgrWrap(text, ANSI.cyan, enabled);
|
|
33675
|
+
}
|
|
33676
|
+
function kv(label, value, style) {
|
|
33677
|
+
return `${dim(`${label}:`, style)} ${value}`;
|
|
33678
|
+
}
|
|
33679
|
+
function formatTaskStatus(status, style) {
|
|
33680
|
+
switch (status) {
|
|
33681
|
+
case "running": return yellow(status, style);
|
|
33682
|
+
case "completed": return green(status, style);
|
|
33683
|
+
case "failed": return red(status, style);
|
|
33684
|
+
case "pending": return cyan(status, style);
|
|
33685
|
+
case "cancelled": return gray(status, style);
|
|
33686
|
+
}
|
|
33687
|
+
}
|
|
33688
|
+
function formatBranchStatus(status, style) {
|
|
33689
|
+
if (!status) return gray("(no branch)", style);
|
|
33690
|
+
switch (status) {
|
|
33691
|
+
case "succeed": return green(status, style);
|
|
33692
|
+
case "failed": return red(status, style);
|
|
33693
|
+
case "running":
|
|
33694
|
+
case "pending": return yellow(status, style);
|
|
33695
|
+
case "manifesting":
|
|
33696
|
+
case "ready_for_manifest": return cyan(status, style);
|
|
33697
|
+
default: return gray(status, style);
|
|
33698
|
+
}
|
|
33699
|
+
}
|
|
33700
|
+
function formatToolLabel(tool, style) {
|
|
33701
|
+
const name = tool.toolName ?? "<tool>";
|
|
33702
|
+
if (name === "command_execution") {
|
|
33703
|
+
const title = typeof tool.title === "string" ? tool.title : "";
|
|
33704
|
+
const inputCommand = typeof tool.input?.command === "string" ? tool.input.command : "";
|
|
33705
|
+
const raw = title || inputCommand;
|
|
33706
|
+
const display = raw ? formatCommandExecutionDisplayCommand(raw) : "(unknown command)";
|
|
33707
|
+
return `${bold("$", style)} ${cyan(display, style)}`;
|
|
33708
|
+
}
|
|
33709
|
+
const renderedName = bold(name, style);
|
|
33710
|
+
if (!tool.title) return renderedName;
|
|
33711
|
+
return `${renderedName}: ${tool.title}`;
|
|
33712
|
+
}
|
|
33713
|
+
function formatToolSummaryLine(tool, style) {
|
|
33714
|
+
const label = formatToolLabel(tool, style);
|
|
33715
|
+
const id = gray(tool.toolCallId, style);
|
|
33716
|
+
switch (tool.status) {
|
|
33717
|
+
case "completed": return `${green("✓", style)} ${label} (${id})`;
|
|
33718
|
+
case "failed": return `${red("✗", style)} ${label} (${id})`;
|
|
33719
|
+
default: return `${yellow("…", style)} ${label} (${id})`;
|
|
33720
|
+
}
|
|
33721
|
+
}
|
|
33722
|
+
function toDisplayLines(text) {
|
|
33723
|
+
return text.replaceAll("\r\n", "\n").split("\n");
|
|
33724
|
+
}
|
|
33725
|
+
function wrapCollapsedWhitespaceToWidth(text, width) {
|
|
33726
|
+
const collapsed = text.trim().replaceAll(/\s+/g, " ");
|
|
33727
|
+
if (!collapsed) return [];
|
|
33728
|
+
if (width <= 0) return [collapsed];
|
|
33729
|
+
const words = collapsed.split(" ");
|
|
33730
|
+
const lines = [];
|
|
33731
|
+
let current = "";
|
|
33732
|
+
function pushLongWord(word) {
|
|
33733
|
+
for (let i = 0; i < word.length; i += width) {
|
|
33734
|
+
const chunk = word.slice(i, i + width);
|
|
33735
|
+
if (chunk.length === width) lines.push(chunk);
|
|
33736
|
+
else current = chunk;
|
|
33737
|
+
}
|
|
33738
|
+
}
|
|
33739
|
+
for (const word of words) {
|
|
33740
|
+
if (!current) {
|
|
33741
|
+
if (word.length <= width) current = word;
|
|
33742
|
+
else pushLongWord(word);
|
|
33743
|
+
continue;
|
|
33744
|
+
}
|
|
33745
|
+
if (current.length + 1 + word.length <= width) {
|
|
33746
|
+
current += ` ${word}`;
|
|
33747
|
+
continue;
|
|
33748
|
+
}
|
|
33749
|
+
lines.push(current);
|
|
33750
|
+
current = "";
|
|
33751
|
+
if (word.length <= width) current = word;
|
|
33752
|
+
else pushLongWord(word);
|
|
33753
|
+
}
|
|
33754
|
+
if (current) lines.push(current);
|
|
33755
|
+
return lines;
|
|
33756
|
+
}
|
|
33757
|
+
function formatWrappedPreviewLines(label, text, options) {
|
|
33758
|
+
const trimmed = text.trim();
|
|
33759
|
+
if (!trimmed) return [];
|
|
33760
|
+
const prefixVisible = visibleLength(`${label}: `);
|
|
33761
|
+
const kept = trimLinesWithEllipsis(wrapCollapsedWhitespaceToWidth(trimmed, Math.max(10, options.widthHint - prefixVisible)), options.maxLines);
|
|
33762
|
+
const indent = " ".repeat(prefixVisible);
|
|
33763
|
+
return kept.map((line, idx) => {
|
|
33764
|
+
const value = options.formatValue ? options.formatValue(line) : line;
|
|
33765
|
+
return idx === 0 ? `${label}: ${value}` : `${indent}${value}`;
|
|
33766
|
+
});
|
|
33767
|
+
}
|
|
33768
|
+
function trimLinesWithEllipsis(lines, maxLines) {
|
|
33769
|
+
if (maxLines <= 0) return [];
|
|
33770
|
+
if (lines.length <= maxLines) return lines;
|
|
33771
|
+
const kept = lines.slice(0, maxLines);
|
|
33772
|
+
const lastIndex = kept.length - 1;
|
|
33773
|
+
const last = kept[lastIndex]?.trimEnd() ?? "";
|
|
33774
|
+
kept[lastIndex] = last ? `${last}…` : "…";
|
|
33775
|
+
return kept;
|
|
33776
|
+
}
|
|
33777
|
+
function formatToolResultPreviewLines(tool, options) {
|
|
33778
|
+
if (tool.status === "in_progress") return [];
|
|
33779
|
+
if (options.maxLines <= 0) return [];
|
|
33780
|
+
if (tool.status === "failed") {
|
|
33781
|
+
const lines = trimLinesWithEllipsis(toDisplayLines(tool.errorText ?? "(no error text)"), options.maxLines);
|
|
33782
|
+
const prefix = dim("error:", options.style);
|
|
33783
|
+
return lines.map((line, idx) => idx === 0 ? `${prefix} ${line}` : ` ${line}`);
|
|
33784
|
+
}
|
|
33785
|
+
if (tool.output == null) return [`${dim("output:", options.style)} (none)`];
|
|
33786
|
+
if (tool.toolName === "command_execution") {
|
|
33787
|
+
const output = tool.output;
|
|
33788
|
+
const exitCode = output?.exit_code ?? null;
|
|
33789
|
+
const outLines = trimLinesWithEllipsis(toDisplayLines((typeof output?.aggregated_output === "string" ? output.aggregated_output : "").trimEnd()), options.maxLines);
|
|
33790
|
+
const exitCodeLabel = typeof exitCode === "number" ? exitCode === 0 ? green(String(exitCode), options.style) : red(String(exitCode), options.style) : gray(String(exitCode), options.style);
|
|
33791
|
+
const outputPrefix = dim("output:", options.style);
|
|
33792
|
+
if (outLines.length === 0) return [`${outputPrefix} exit_code=${exitCodeLabel}`];
|
|
33793
|
+
return outLines.map((line, idx) => idx === 0 ? `${outputPrefix} exit_code=${exitCodeLabel} ${line}` : ` ${line}`);
|
|
33794
|
+
}
|
|
33795
|
+
const lines = trimLinesWithEllipsis(toDisplayLines(inspect(tool.output, {
|
|
33796
|
+
depth: 6,
|
|
33797
|
+
maxArrayLength: 50,
|
|
33798
|
+
maxStringLength: 1e3,
|
|
33799
|
+
compact: true,
|
|
33800
|
+
breakLength: Math.max(20, options.widthHint)
|
|
33801
|
+
})), options.maxLines);
|
|
33802
|
+
const outputPrefix = dim("output:", options.style);
|
|
33803
|
+
return lines.map((line, idx) => idx === 0 ? `${outputPrefix} ${line}` : ` ${line}`);
|
|
33804
|
+
}
|
|
33805
|
+
function formatToolBlocks(step, options) {
|
|
33806
|
+
const tools = options.maxTools != null ? step.toolActions.slice(-options.maxTools) : step.toolActions;
|
|
33807
|
+
if (tools.length === 0) return [` ${gray("(none)", options.style)}`];
|
|
33808
|
+
const widthHint = Math.max(20, options.widthHint);
|
|
33809
|
+
const out = [];
|
|
33810
|
+
for (const tool of tools) {
|
|
33811
|
+
out.push(` ${formatToolSummaryLine(tool, options.style)}`);
|
|
33812
|
+
formatToolResultPreviewLines(tool, {
|
|
33813
|
+
maxLines: options.maxResultLines,
|
|
33814
|
+
widthHint: widthHint - 4,
|
|
33815
|
+
style: options.style
|
|
33816
|
+
}).forEach((line) => out.push(` ${line}`));
|
|
33817
|
+
}
|
|
33818
|
+
return out.map((line) => clip(line, widthHint));
|
|
33819
|
+
}
|
|
33820
|
+
const ANSI_SGR_REGEX = /\u001b\[[0-9;]*m/g;
|
|
33821
|
+
function visibleLength(value) {
|
|
33822
|
+
return value.replaceAll(ANSI_SGR_REGEX, "").length;
|
|
33823
|
+
}
|
|
33824
|
+
function sliceSgrByVisibleWidth(value, maxVisible) {
|
|
33825
|
+
if (maxVisible <= 0) return "";
|
|
33826
|
+
let out = "";
|
|
33827
|
+
let visible = 0;
|
|
33828
|
+
for (let i = 0; i < value.length; i++) {
|
|
33829
|
+
if (visible >= maxVisible) break;
|
|
33830
|
+
const ch = value[i];
|
|
33831
|
+
if (ch === "\x1B" && value[i + 1] === "[") {
|
|
33832
|
+
const end = value.indexOf("m", i);
|
|
33833
|
+
if (end === -1) break;
|
|
33834
|
+
out += value.slice(i, end + 1);
|
|
33835
|
+
i = end;
|
|
33836
|
+
continue;
|
|
33837
|
+
}
|
|
33838
|
+
out += ch;
|
|
33839
|
+
visible += 1;
|
|
33840
|
+
}
|
|
33841
|
+
return out;
|
|
33842
|
+
}
|
|
33843
|
+
function clip(value, maxVisible) {
|
|
33844
|
+
if (maxVisible <= 0) return "";
|
|
33845
|
+
if (visibleLength(value) <= maxVisible) return value;
|
|
33846
|
+
if (maxVisible === 1) return "…";
|
|
33847
|
+
const sliced = sliceSgrByVisibleWidth(value, maxVisible - 1);
|
|
33848
|
+
return `${sliced}…${sliced.includes("\x1B[") ? ANSI.reset : ""}`;
|
|
33849
|
+
}
|
|
33850
|
+
function createWatchCommand(version) {
|
|
33851
|
+
return createCommand("watch").version(version).description("Inspect task progress via live/near-live stream data (internal tooling).").option("--tasks <ids>", "Comma-separated task IDs to watch.", parseCommaList, []).option("--agents <names>", "Comma-separated agent names to watch.", parseCommaList, []).option("--follow", "Follow mode: live updating TUI.", false).option("--idle-timeout-seconds <seconds>", "Snapshot mode: stop reading a stream after this many seconds with no new messages.", (val) => parseFloat(val), 3).action(async function() {
|
|
33852
|
+
ensureEnvOrExit(["PANTHEON_API_KEY", "DATABASE_URL"]);
|
|
33853
|
+
const options = this.opts();
|
|
33854
|
+
if (options.tasks.length > 0 && options.agents.length > 0) {
|
|
33855
|
+
console.error("Use either --tasks or --agents, not both.");
|
|
33856
|
+
process.exit(1);
|
|
33857
|
+
}
|
|
33858
|
+
const idleTimeoutMs = Math.max(0, options.idleTimeoutSeconds * 1e3);
|
|
33859
|
+
const db = createDb();
|
|
33860
|
+
try {
|
|
33861
|
+
const { targets, warnings } = await resolveWatchTargets({
|
|
33862
|
+
db,
|
|
33863
|
+
taskIds: options.tasks,
|
|
33864
|
+
agentNames: options.agents
|
|
33865
|
+
});
|
|
33866
|
+
warnings.forEach((w) => console.error(`warning: ${w}`));
|
|
33867
|
+
if (targets.length === 0) {
|
|
33868
|
+
console.error("No tasks found to watch.");
|
|
33869
|
+
process.exit(1);
|
|
33870
|
+
}
|
|
33871
|
+
if (!options.follow) {
|
|
33872
|
+
const style = useAnsiStyles();
|
|
33873
|
+
const widthHint = process.stdout.columns ?? 120;
|
|
33874
|
+
const now = /* @__PURE__ */ new Date();
|
|
33875
|
+
for (const target of targets) {
|
|
33876
|
+
const { task } = target;
|
|
33877
|
+
const elapsedMs = getTaskElapsedMs(task, now);
|
|
33878
|
+
let branchInfo = {};
|
|
33879
|
+
try {
|
|
33880
|
+
branchInfo = await fetchBranchSnapshot({ task });
|
|
33881
|
+
} catch (error) {
|
|
33882
|
+
console.error(`warning: failed to fetch branch info for task ${task.id}: ${error instanceof Error ? error.message : String(error)}`);
|
|
33883
|
+
}
|
|
33884
|
+
const preferredSource = toStreamSourceFromExecuteAgent(target.executeAgent);
|
|
33885
|
+
let stepSnapshot = void 0;
|
|
33886
|
+
let inferredSource = void 0;
|
|
33887
|
+
if (branchInfo.streamId) try {
|
|
33888
|
+
const result = await buildStepSnapshot({
|
|
33889
|
+
streamId: branchInfo.streamId,
|
|
33890
|
+
preferredSource,
|
|
33891
|
+
idleTimeoutMs
|
|
33892
|
+
});
|
|
33893
|
+
stepSnapshot = result.step;
|
|
33894
|
+
inferredSource = result.inferredSource;
|
|
33895
|
+
} catch (error) {
|
|
33896
|
+
console.error(`warning: failed to read stream for task ${task.id}: ${error instanceof Error ? error.message : String(error)}`);
|
|
33897
|
+
}
|
|
33898
|
+
const lines = [];
|
|
33899
|
+
const sep = gray(" • ", style);
|
|
33900
|
+
lines.push([
|
|
33901
|
+
kv("task", task.id, style),
|
|
33902
|
+
kv("status", formatTaskStatus(task.status, style), style),
|
|
33903
|
+
kv("agent", target.agent, style)
|
|
33904
|
+
].join(sep));
|
|
33905
|
+
const branchId = "branch_id" in task ? task.branch_id ?? void 0 : void 0;
|
|
33906
|
+
const mergedBranchLine = [
|
|
33907
|
+
kv("project", task.project_id, style),
|
|
33908
|
+
branchId ? kv("branch", branchId, style) : void 0,
|
|
33909
|
+
branchId || branchInfo.branch?.status ? kv("branch_status", formatBranchStatus(branchInfo.branch?.status, style), style) : void 0,
|
|
33910
|
+
branchInfo.stepIndex != null ? kv("step", String(branchInfo.stepIndex), style) : void 0
|
|
33911
|
+
].filter(Boolean).join(sep);
|
|
33912
|
+
if (mergedBranchLine) lines.push(mergedBranchLine);
|
|
33913
|
+
if (branchInfo.streamId) {
|
|
33914
|
+
const src = preferredSource ?? inferredSource;
|
|
33915
|
+
const mergedStreamLine = [kv("stream", branchInfo.streamId, style), src ? kv("source", src, style) : void 0].filter(Boolean).join(sep);
|
|
33916
|
+
if (mergedStreamLine) lines.push(mergedStreamLine);
|
|
33917
|
+
}
|
|
33918
|
+
const start = getTaskStartTime(task);
|
|
33919
|
+
const mergedTimingLine = [start ? kv("started", start.toISOString(), style) : void 0, kv("elapsed", formatDurationMs(elapsedMs), style)].filter(Boolean).join(sep);
|
|
33920
|
+
if (mergedTimingLine) lines.push(mergedTimingLine);
|
|
33921
|
+
if (stepSnapshot) {
|
|
33922
|
+
const reasoningLine = formatPreviewLine(dim("reasoning", style), stepSnapshot.leadingReasoning, { formatValue: (value) => gray(value, style) });
|
|
33923
|
+
if (reasoningLine) lines.push(reasoningLine);
|
|
33924
|
+
lines.push(bold("tools:", style));
|
|
33925
|
+
lines.push(...formatToolBlocks(stepSnapshot, {
|
|
33926
|
+
style,
|
|
33927
|
+
widthHint,
|
|
33928
|
+
maxResultLines: 3
|
|
33929
|
+
}));
|
|
33930
|
+
const messageLine = formatPreviewLine(dim("message", style), stepSnapshot.leadingText);
|
|
33931
|
+
if (messageLine) lines.push(messageLine);
|
|
33932
|
+
if (stepSnapshot.warnings.length > 0) lines.push(kv("warnings", yellow(`${stepSnapshot.warnings.length} (unknown=${stepSnapshot.unknownEventCount})`, style), style));
|
|
33933
|
+
} else lines.push(`${bold("tools:", style)} ${gray("(no stream)", style)}`);
|
|
33934
|
+
printSnapshotLineBlock(lines.map((line) => clip(line, widthHint)));
|
|
33935
|
+
console.log("");
|
|
33936
|
+
}
|
|
33937
|
+
return;
|
|
33938
|
+
}
|
|
33939
|
+
if (!process.stdout.isTTY) {
|
|
33940
|
+
console.error("--follow requires a TTY.");
|
|
33941
|
+
process.exit(1);
|
|
33942
|
+
}
|
|
33943
|
+
const abortController = new AbortController();
|
|
33944
|
+
const onSigInt = () => abortController.abort();
|
|
33945
|
+
process.on("SIGINT", onSigInt);
|
|
33946
|
+
const runtime = targets.map((target) => ({
|
|
33947
|
+
target,
|
|
33948
|
+
aggregator: new WatchStepAggregator({ maxLogLines: 120 }),
|
|
33949
|
+
streamDone: false
|
|
33950
|
+
}));
|
|
33951
|
+
function isTerminalBranchStatus(status) {
|
|
33952
|
+
return status === "succeed" || status === "failed";
|
|
33953
|
+
}
|
|
33954
|
+
async function startStream(rt, streamId) {
|
|
33955
|
+
rt.streamAbort?.abort();
|
|
33956
|
+
rt.streamAbort = new AbortController();
|
|
33957
|
+
rt.streamDone = false;
|
|
33958
|
+
const preferred = toStreamSourceFromExecuteAgent(rt.target.executeAgent);
|
|
33959
|
+
rt.source = preferred;
|
|
33960
|
+
const url = makeStreamUrl(streamId);
|
|
33961
|
+
(async () => {
|
|
33962
|
+
try {
|
|
33963
|
+
const res = await fetch(url, {
|
|
33964
|
+
headers: {
|
|
33965
|
+
Authorization: `Bearer ${process.env.PANTHEON_API_KEY}`,
|
|
33966
|
+
Accept: "text/event-stream"
|
|
33967
|
+
},
|
|
33968
|
+
signal: rt.streamAbort.signal
|
|
33969
|
+
});
|
|
33970
|
+
if (!res.body) {
|
|
33971
|
+
rt.lastError = "missing response body";
|
|
33972
|
+
rt.streamDone = true;
|
|
33973
|
+
return;
|
|
33974
|
+
}
|
|
33975
|
+
let normalizer;
|
|
33976
|
+
await consumeOpaqueSseStream({
|
|
33977
|
+
stream: res.body,
|
|
33978
|
+
signal: rt.streamAbort.signal,
|
|
33979
|
+
onWarning: (warning) => rt.aggregator.pushUiChunk({
|
|
33980
|
+
type: "data-agent-unknown",
|
|
33981
|
+
data: { reason: warning }
|
|
33982
|
+
}),
|
|
33983
|
+
onData: (payload) => {
|
|
33984
|
+
if (!normalizer) {
|
|
33985
|
+
rt.source = preferred ?? detectStreamSourceFromRawPayload(payload) ?? "codex";
|
|
33986
|
+
normalizer = createAgentStreamNormalizer({
|
|
33987
|
+
source: rt.source,
|
|
33988
|
+
emitStartChunk: false,
|
|
33989
|
+
includeMetaEvents: false,
|
|
33990
|
+
unknownChunkPolicy: "emit"
|
|
33991
|
+
});
|
|
33992
|
+
}
|
|
33993
|
+
rt.aggregator.pushUiChunks(normalizer.push(payload));
|
|
33994
|
+
}
|
|
33995
|
+
});
|
|
33996
|
+
if (normalizer) rt.aggregator.pushUiChunks(normalizer.finish());
|
|
33997
|
+
rt.streamDone = true;
|
|
33998
|
+
} catch (error) {
|
|
33999
|
+
if (rt.streamAbort?.signal.aborted) {
|
|
34000
|
+
rt.streamDone = true;
|
|
34001
|
+
return;
|
|
34002
|
+
}
|
|
34003
|
+
rt.lastError = error instanceof Error ? error.message : String(error);
|
|
34004
|
+
rt.streamDone = true;
|
|
34005
|
+
}
|
|
34006
|
+
})();
|
|
34007
|
+
}
|
|
34008
|
+
async function pollBranchesOnce() {
|
|
34009
|
+
await Promise.all(runtime.map(async (rt) => {
|
|
34010
|
+
const { task } = rt.target;
|
|
34011
|
+
if (task.status !== "running" && task.status !== "completed" && task.status !== "failed") return;
|
|
34012
|
+
const branchId = "branch_id" in task ? task.branch_id ?? void 0 : void 0;
|
|
34013
|
+
if (!branchId) return;
|
|
34014
|
+
try {
|
|
34015
|
+
const branch = await getPantheonBranchInfo({
|
|
34016
|
+
projectId: task.project_id,
|
|
34017
|
+
branchId
|
|
34018
|
+
});
|
|
34019
|
+
rt.branch = branch;
|
|
34020
|
+
rt.stepIndex = branch.latest_snap?.step_index;
|
|
34021
|
+
const nextStreamId = branch.latest_snap?.event_stream_id ?? void 0;
|
|
34022
|
+
if (nextStreamId && nextStreamId !== rt.streamId) {
|
|
34023
|
+
rt.streamId = nextStreamId;
|
|
34024
|
+
rt.aggregator = new WatchStepAggregator({ maxLogLines: 120 });
|
|
34025
|
+
await startStream(rt, nextStreamId);
|
|
34026
|
+
}
|
|
34027
|
+
} catch (error) {
|
|
34028
|
+
rt.lastError = error instanceof Error ? error.message : String(error);
|
|
34029
|
+
}
|
|
34030
|
+
}));
|
|
34031
|
+
}
|
|
34032
|
+
const screen = blessed.screen({
|
|
34033
|
+
smartCSR: true,
|
|
34034
|
+
title: "pantheon-agents watch"
|
|
34035
|
+
});
|
|
34036
|
+
screen.key(["q", "C-c"], () => abortController.abort());
|
|
34037
|
+
const footer = blessed.box({
|
|
34038
|
+
parent: screen,
|
|
34039
|
+
bottom: 0,
|
|
34040
|
+
left: 0,
|
|
34041
|
+
height: 1,
|
|
34042
|
+
width: "100%",
|
|
34043
|
+
tags: false,
|
|
34044
|
+
content: "Ctrl+C to exit • q to quit",
|
|
34045
|
+
style: { fg: "gray" }
|
|
34046
|
+
});
|
|
34047
|
+
const panes = runtime.map(() => blessed.box({
|
|
34048
|
+
parent: screen,
|
|
34049
|
+
top: 0,
|
|
34050
|
+
left: 0,
|
|
34051
|
+
height: "100%-1",
|
|
34052
|
+
width: "100%",
|
|
34053
|
+
border: "line",
|
|
34054
|
+
tags: false,
|
|
34055
|
+
scrollable: false,
|
|
34056
|
+
wrap: false,
|
|
34057
|
+
style: { border: { fg: "gray" } }
|
|
34058
|
+
}));
|
|
34059
|
+
let lastGlobalError;
|
|
34060
|
+
function layoutPanes(columns) {
|
|
34061
|
+
const n = panes.length;
|
|
34062
|
+
for (let i = 0; i < n; i++) {
|
|
34063
|
+
const left = Math.floor(i * columns / n);
|
|
34064
|
+
const right = Math.floor((i + 1) * columns / n);
|
|
34065
|
+
panes[i].left = left;
|
|
34066
|
+
panes[i].width = Math.max(1, right - left);
|
|
34067
|
+
}
|
|
34068
|
+
}
|
|
34069
|
+
const renderTimer = setInterval(() => {
|
|
34070
|
+
const style = useAnsiStyles();
|
|
34071
|
+
const columns = Math.max(40, screen.width ?? 120);
|
|
34072
|
+
const rows = Math.max(8, screen.height ?? 24);
|
|
34073
|
+
layoutPanes(columns);
|
|
34074
|
+
const paneWidth = Math.max(1, Math.floor(columns / runtime.length));
|
|
34075
|
+
const paneInnerWidth = Math.max(0, paneWidth - 2);
|
|
34076
|
+
const paneOuterHeight = Math.max(1, rows - 1);
|
|
34077
|
+
const maxContentLines = Math.max(0, paneOuterHeight - 2);
|
|
34078
|
+
const nextFooterParts = ["Ctrl+C to exit", "q to quit"];
|
|
34079
|
+
if (lastGlobalError) nextFooterParts.push(`error: ${lastGlobalError}`);
|
|
34080
|
+
footer.setContent(nextFooterParts.join(" • "));
|
|
34081
|
+
runtime.forEach((rt, idx) => {
|
|
34082
|
+
const { task } = rt.target;
|
|
34083
|
+
const step = rt.aggregator.snapshot();
|
|
34084
|
+
const elapsed = formatDurationMs(getTaskElapsedMs(task, /* @__PURE__ */ new Date()));
|
|
34085
|
+
const title = [
|
|
34086
|
+
rt.target.agent,
|
|
34087
|
+
`task ${task.id}`,
|
|
34088
|
+
formatTaskStatus(task.status, style),
|
|
34089
|
+
formatBranchStatus(rt.branch?.status, style),
|
|
34090
|
+
rt.stepIndex != null ? `step ${rt.stepIndex}` : void 0,
|
|
34091
|
+
`elapsed ${elapsed}`
|
|
34092
|
+
].filter(Boolean).join(gray(" • ", style));
|
|
34093
|
+
const branchId = "branch_id" in task ? task.branch_id ?? void 0 : void 0;
|
|
34094
|
+
const headerLines = [kv("project", task.project_id, style), kv("branch", branchId ? branchId : gray("(no branch)", style), style)];
|
|
34095
|
+
const reasoningLines = formatWrappedPreviewLines(dim("reasoning", style), step.leadingReasoning, {
|
|
34096
|
+
maxLines: 4,
|
|
34097
|
+
widthHint: paneInnerWidth,
|
|
34098
|
+
formatValue: (value) => gray(value, style)
|
|
34099
|
+
});
|
|
34100
|
+
const messageLine = formatPreviewLine(dim("message", style), step.leadingText);
|
|
34101
|
+
const extraLines = [];
|
|
34102
|
+
if (rt.lastError) extraLines.push(kv("error", red(rt.lastError, style), style));
|
|
34103
|
+
if (step.warnings.length > 0) extraLines.push(kv("warnings", yellow(`${step.warnings.length} (unknown=${step.unknownEventCount})`, style), style));
|
|
34104
|
+
const toolLines = step.toolActions.filter((t) => t.toolName !== "command_execution").slice(-3).map((tool) => formatToolSummaryLine(tool, style));
|
|
34105
|
+
const baseLines = [
|
|
34106
|
+
...headerLines,
|
|
34107
|
+
...reasoningLines,
|
|
34108
|
+
...toolLines,
|
|
34109
|
+
...messageLine ? [messageLine] : [],
|
|
34110
|
+
...extraLines
|
|
34111
|
+
];
|
|
34112
|
+
const remainingForLogs = Math.max(0, maxContentLines - baseLines.length);
|
|
34113
|
+
const logLines = step.logLines.slice(-remainingForLogs);
|
|
34114
|
+
panes[idx].setLabel(title);
|
|
34115
|
+
panes[idx].setContent([...baseLines, ...logLines].map((line) => clip(line, paneInnerWidth)).join("\n"));
|
|
34116
|
+
});
|
|
34117
|
+
screen.render();
|
|
34118
|
+
}, 300);
|
|
34119
|
+
let pollTimer;
|
|
34120
|
+
let doneTimer;
|
|
34121
|
+
try {
|
|
34122
|
+
await pollBranchesOnce();
|
|
34123
|
+
if (abortController.signal.aborted) return;
|
|
34124
|
+
pollTimer = setInterval(() => {
|
|
34125
|
+
pollBranchesOnce().catch((e) => {
|
|
34126
|
+
lastGlobalError = e instanceof Error ? e.message : String(e);
|
|
34127
|
+
});
|
|
34128
|
+
}, 2e3);
|
|
34129
|
+
await new Promise((resolve) => {
|
|
34130
|
+
let resolved = false;
|
|
34131
|
+
const resolveOnce = () => {
|
|
34132
|
+
if (resolved) return;
|
|
34133
|
+
resolved = true;
|
|
34134
|
+
if (doneTimer) {
|
|
34135
|
+
clearInterval(doneTimer);
|
|
34136
|
+
doneTimer = void 0;
|
|
34137
|
+
}
|
|
34138
|
+
resolve();
|
|
34139
|
+
};
|
|
34140
|
+
abortController.signal.addEventListener("abort", resolveOnce, { once: true });
|
|
34141
|
+
if (abortController.signal.aborted) {
|
|
34142
|
+
resolveOnce();
|
|
34143
|
+
return;
|
|
34144
|
+
}
|
|
34145
|
+
const terminalGraceMs = Math.max(1e3, idleTimeoutMs);
|
|
34146
|
+
let terminalSince;
|
|
34147
|
+
doneTimer = setInterval(() => {
|
|
34148
|
+
const now = Date.now();
|
|
34149
|
+
if (!runtime.every((rt) => isTerminalBranchStatus(rt.branch?.status))) {
|
|
34150
|
+
terminalSince = void 0;
|
|
34151
|
+
return;
|
|
34152
|
+
}
|
|
34153
|
+
terminalSince ??= now;
|
|
34154
|
+
if (runtime.every((rt) => !rt.streamId || rt.streamDone)) {
|
|
34155
|
+
resolveOnce();
|
|
34156
|
+
return;
|
|
34157
|
+
}
|
|
34158
|
+
if (now - terminalSince >= terminalGraceMs) resolveOnce();
|
|
34159
|
+
}, 1e3);
|
|
34160
|
+
});
|
|
34161
|
+
} finally {
|
|
34162
|
+
process.off("SIGINT", onSigInt);
|
|
34163
|
+
clearInterval(renderTimer);
|
|
34164
|
+
if (pollTimer) clearInterval(pollTimer);
|
|
34165
|
+
if (doneTimer) clearInterval(doneTimer);
|
|
34166
|
+
runtime.forEach((rt) => rt.streamAbort?.abort());
|
|
34167
|
+
screen.destroy();
|
|
34168
|
+
}
|
|
34169
|
+
} finally {
|
|
34170
|
+
await db.destroy();
|
|
34171
|
+
}
|
|
34172
|
+
});
|
|
34173
|
+
}
|
|
34174
|
+
|
|
32248
34175
|
//#endregion
|
|
32249
34176
|
//#region src/cli/index.ts
|
|
32250
|
-
const program = new Command().name("pantheon-agents").description("Pantheon agents CLI").version(version$1).showHelpAfterError().showSuggestionAfterError().addHelpCommand().addCommand(createAddTaskCommand(version$1)).addCommand(createConfigAgentCommand(version$1)).addCommand(createDeleteTaskCommand(version$1)).addCommand(createReconfigAgentCommand(version$1)).addCommand(createRunAgentCommand(version$1)).addCommand(createServerCommand(version$1)).addCommand(createShowConfigCommand(version$1)).addCommand(createShowTasksCommand(version$1));
|
|
34177
|
+
const program = new Command().name("pantheon-agents").description("Pantheon agents CLI").version(version$1).showHelpAfterError().showSuggestionAfterError().addHelpCommand().addCommand(createAddTaskCommand(version$1)).addCommand(createConfigAgentCommand(version$1)).addCommand(createDeleteTaskCommand(version$1)).addCommand(createReconfigAgentCommand(version$1)).addCommand(createRunAgentCommand(version$1)).addCommand(createServerCommand(version$1)).addCommand(createShowConfigCommand(version$1)).addCommand(createShowTasksCommand(version$1)).addCommand(createWatchCommand(version$1));
|
|
32251
34178
|
async function main() {
|
|
32252
34179
|
if (process$1.argv.length <= 2) {
|
|
32253
34180
|
program.outputHelp();
|