@pasko70/pibo 1.10.0 → 1.11.0
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/apps/chat/chat-request-normalizers.js +7 -0
- package/dist/apps/chat/data/chat-data-mappers.js +2 -0
- package/dist/apps/chat/loop-api.js +11 -6
- package/dist/apps/chat/web-app.js +30 -10
- package/dist/apps/chat-ui/assets/{dist-wE9nop9V.js → dist-0E9FVJ6k.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DlATLa-U.js → dist-BvqC0hRM.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DUlaXAk7.js → dist-C2BRHNXr.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-nOLTkZrJ.js → dist-C57jNmjf.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-Y-AA2omI.js → dist-CBL8UXjd.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-D-cxLQO1.js → dist-CI-LPHjw.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-LHRs1Nhr.js → dist-CMRFkfl5.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CS7wdk0Z.js → dist-CpZPhD2y.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BwKObYnX.js → dist-CuCZ-3_K.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-GdEM8UW1.js → dist-CzC5kPWJ.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CKtT8YGm.js → dist-D9hLnn1T.js} +1 -1
- package/dist/apps/chat-ui/assets/index-BEvjPUor.js +173 -0
- package/dist/apps/chat-ui/assets/{index-C0x9nEcf.css → index-DNeE4HrG.css} +1 -1
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/index-BLZRi4Ka.js +41 -0
- package/dist/apps/chat-vscode-web/assets/index-Bf2JvJ9z.css +2 -0
- package/dist/apps/chat-vscode-web/index.html +2 -2
- package/dist/apps/vscode-artifacts/latest.vsix +0 -0
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-1.11.0.vsix +0 -0
- package/dist/auth/better-auth.js +49 -1
- package/dist/auth/cli.js +220 -0
- package/dist/auth/machine-keys.js +258 -0
- package/dist/auth/machine-session.js +123 -0
- package/dist/bin/pibo.js +0 -0
- package/dist/bin/rg.js +0 -0
- package/dist/cli-session/localSessionSource.js +34 -12
- package/dist/cli.js +6 -0
- package/dist/compute/cli.js +7 -0
- package/dist/compute/resource-health.js +61 -4
- package/dist/config/config.js +5 -0
- package/dist/core/events.js +6 -1
- package/dist/core/routed-session.js +67 -4
- package/dist/core/session-router.js +40 -6
- package/dist/data/ingest-service.js +3 -1
- package/dist/data/schema.js +4 -0
- package/dist/debug/index.js +4 -0
- package/dist/debug/trace-status.js +25 -0
- package/dist/debug/trace.js +51 -17
- package/dist/index.js +1 -0
- package/dist/loops/accounting.js +19 -0
- package/dist/loops/cli.js +22 -6
- package/dist/loops/prompts.js +4 -1
- package/dist/loops/service.js +97 -8
- package/dist/loops/store.js +128 -17
- package/dist/loops/tools.js +24 -7
- package/dist/reliability/store.js +34 -7
- package/dist/resources/cli.js +15 -0
- package/dist/resources/lifecycle.js +28 -4
- package/dist/resources/reaper.js +1 -0
- package/dist/runs/lifecycle.js +59 -0
- package/dist/runs/registry.js +47 -1
- package/dist/runs/tools.js +31 -13
- package/dist/session-ui/terminalRows.js +66 -2
- package/dist/shared/trace-async-agent-runs.js +4 -4
- package/dist/shared/trace-event-projection.js +59 -19
- package/dist/shared/trace-nodes.js +5 -0
- package/dist/shared/trace-page-merge.js +15 -0
- package/dist/shared/trace-run-notifications.js +3 -1
- package/dist/signals/projector.js +16 -3
- package/dist/tools/browser-pool.js +50 -0
- package/dist/tools/browser-use-leases.js +12 -8
- package/dist/tools/guides.js +8 -1
- package/dist/tools/index.js +1 -0
- package/package.json +2 -1
- package/skills/builtin/loop/SKILL.md +12 -3
- package/dist/apps/chat-ui/assets/index-8W_yMHQI.js +0 -173
- package/dist/apps/chat-vscode-web/assets/index-B5QK07zO.css +0 -2
- package/dist/apps/chat-vscode-web/assets/index-BAMxIaI_.js +0 -41
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export class PiboRunExecutionTimeoutError extends Error {
|
|
2
|
+
timeoutPhase;
|
|
3
|
+
constructor(message, timeoutPhase) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.timeoutPhase = timeoutPhase;
|
|
6
|
+
this.name = "PiboRunExecutionTimeoutError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export function resolveRunTimeoutMs(toolName, params) {
|
|
10
|
+
if (!params || typeof params !== "object" || Array.isArray(params))
|
|
11
|
+
return undefined;
|
|
12
|
+
const input = params;
|
|
13
|
+
if (typeof input.timeoutMs === "number")
|
|
14
|
+
return normalizePositiveTimeout(input.timeoutMs, "arguments.timeoutMs");
|
|
15
|
+
if (toolName === "bash" && typeof input.timeout === "number")
|
|
16
|
+
return normalizePositiveTimeout(input.timeout * 1000, "arguments.timeout");
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
export function foregroundServiceWarning(toolName, params, timeoutMs) {
|
|
20
|
+
if (toolName !== "bash" || timeoutMs === undefined || !params || typeof params !== "object" || Array.isArray(params))
|
|
21
|
+
return undefined;
|
|
22
|
+
const command = typeof params.command === "string" ? String(params.command) : "";
|
|
23
|
+
if (!isKnownForegroundServiceCommand(command))
|
|
24
|
+
return undefined;
|
|
25
|
+
return `Known long-lived service command is running in a finite yielded run (${timeoutMs}ms). It can start successfully and later end as timed_out. Use the managed gateway CLI, or launch a detached/background service with a separate bounded health check.`;
|
|
26
|
+
}
|
|
27
|
+
export function isConfiguredTimeoutError(error) {
|
|
28
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
29
|
+
return /(?:timed?\s*out|timeout)/i.test(message);
|
|
30
|
+
}
|
|
31
|
+
export function hasMeaningfulTimeoutOutput(value) {
|
|
32
|
+
const text = extractText(value);
|
|
33
|
+
if (!text)
|
|
34
|
+
return false;
|
|
35
|
+
return text.split(/\r?\n/).some((line) => {
|
|
36
|
+
const trimmed = line.trim();
|
|
37
|
+
return !!trimmed && !/(?:timed?\s*out|timeout|command exited with code|process exited with code)/i.test(trimmed);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function isKnownForegroundServiceCommand(command) {
|
|
41
|
+
return /(?:^|[;&|]\s*|\b)(?:pibo\s+gateway(?::web|\s+web|\s+dev)?(?:\s|$)|node\s+[^\n]*pibo(?:\.js)?\s+gateway:web(?:\s|$)|(?:\.\/)?(?:scripts\/)?docker-entrypoint\.sh\s+gateway:web(?:\s|$)|npm\s+(?:run\s+)?gateway(?::web)?(?:\s|$)|(?:npm\s+run\s+)?(?:dev|start)\s+--[^\n]*\bgateway(?::web)?\b|vite(?:\s|$))/i.test(command);
|
|
42
|
+
}
|
|
43
|
+
function normalizePositiveTimeout(value, field) {
|
|
44
|
+
if (!Number.isFinite(value) || value <= 0)
|
|
45
|
+
throw new Error(`${field} must be a positive number`);
|
|
46
|
+
return Math.max(1, Math.round(value));
|
|
47
|
+
}
|
|
48
|
+
function extractText(value) {
|
|
49
|
+
if (typeof value === "string")
|
|
50
|
+
return value;
|
|
51
|
+
if (!value || typeof value !== "object")
|
|
52
|
+
return "";
|
|
53
|
+
const candidate = value;
|
|
54
|
+
if (typeof candidate.text === "string")
|
|
55
|
+
return candidate.text;
|
|
56
|
+
if (!Array.isArray(candidate.content))
|
|
57
|
+
return "";
|
|
58
|
+
return candidate.content.map((part) => part && typeof part === "object" && typeof part.text === "string" ? String(part.text) : "").filter(Boolean).join("\n");
|
|
59
|
+
}
|
package/dist/runs/registry.js
CHANGED
|
@@ -4,6 +4,14 @@ const DEFAULT_DETACHED_TERMINAL_TTL_MS = 60 * 1000;
|
|
|
4
4
|
function now() {
|
|
5
5
|
return new Date().toISOString();
|
|
6
6
|
}
|
|
7
|
+
function runTimeoutAt(createdAt, timeoutMs) {
|
|
8
|
+
return timeoutMs === undefined ? undefined : new Date(Date.parse(createdAt) + timeoutMs).toISOString();
|
|
9
|
+
}
|
|
10
|
+
function formatTimeout(timeoutMs) {
|
|
11
|
+
if (timeoutMs === undefined)
|
|
12
|
+
return "its configured timeout";
|
|
13
|
+
return timeoutMs % 1000 === 0 ? `${timeoutMs / 1000}s timeout` : `${timeoutMs}ms timeout`;
|
|
14
|
+
}
|
|
7
15
|
function snapshot(record) {
|
|
8
16
|
const output = {
|
|
9
17
|
runId: record.runId,
|
|
@@ -18,12 +26,20 @@ function snapshot(record) {
|
|
|
18
26
|
};
|
|
19
27
|
if (record.summary)
|
|
20
28
|
output.summary = record.summary;
|
|
29
|
+
if (record.timeoutMs !== undefined)
|
|
30
|
+
output.timeoutMs = record.timeoutMs;
|
|
31
|
+
if (record.timeoutAt)
|
|
32
|
+
output.timeoutAt = record.timeoutAt;
|
|
33
|
+
if (record.timeoutPhase)
|
|
34
|
+
output.timeoutPhase = record.timeoutPhase;
|
|
35
|
+
if (record.serviceWarning)
|
|
36
|
+
output.serviceWarning = record.serviceWarning;
|
|
21
37
|
if (record.completedAt)
|
|
22
38
|
output.completedAt = record.completedAt;
|
|
23
39
|
return output;
|
|
24
40
|
}
|
|
25
41
|
function terminal(status) {
|
|
26
|
-
return status === "completed" || status === "failed" || status === "cancelled";
|
|
42
|
+
return status === "completed" || status === "failed" || status === "timed_out" || status === "cancelled";
|
|
27
43
|
}
|
|
28
44
|
export class PiboRunRegistry {
|
|
29
45
|
options;
|
|
@@ -53,6 +69,8 @@ export class PiboRunRegistry {
|
|
|
53
69
|
params: input.params,
|
|
54
70
|
retryable: input.retryable ?? false,
|
|
55
71
|
maxAttempts: input.maxAttempts ?? 1,
|
|
72
|
+
timeoutMs: input.timeoutMs,
|
|
73
|
+
serviceWarning: input.serviceWarning,
|
|
56
74
|
});
|
|
57
75
|
const record = recordFromStored(stored);
|
|
58
76
|
this.runs.set(record.runId, record);
|
|
@@ -75,6 +93,8 @@ export class PiboRunRegistry {
|
|
|
75
93
|
summary: `${input.toolName} run is running.`,
|
|
76
94
|
retryable: input.retryable ?? false,
|
|
77
95
|
maxAttempts: Math.max(1, input.maxAttempts ?? 1),
|
|
96
|
+
...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs, timeoutAt: runTimeoutAt(timestamp, input.timeoutMs) } : {}),
|
|
97
|
+
...(input.serviceWarning ? { serviceWarning: input.serviceWarning } : {}),
|
|
78
98
|
};
|
|
79
99
|
this.runs.set(runId, record);
|
|
80
100
|
const output = snapshot(record);
|
|
@@ -113,6 +133,25 @@ export class PiboRunRegistry {
|
|
|
113
133
|
this.notify({ type: "run_changed", run: output, previousStatus, reason: error });
|
|
114
134
|
return output;
|
|
115
135
|
}
|
|
136
|
+
timeOut(runId, error, timeoutPhase) {
|
|
137
|
+
const record = this.runs.get(runId);
|
|
138
|
+
if (!record || terminal(record.status))
|
|
139
|
+
return undefined;
|
|
140
|
+
const previousStatus = record.status;
|
|
141
|
+
record.status = "timed_out";
|
|
142
|
+
record.error = error;
|
|
143
|
+
record.timeoutPhase = timeoutPhase;
|
|
144
|
+
record.summary = timeoutPhase === "lifetime"
|
|
145
|
+
? `${record.toolName} run started successfully, then reached ${formatTimeout(record.timeoutMs)}.`
|
|
146
|
+
: `${record.toolName} run reached ${formatTimeout(record.timeoutMs)} before startup was confirmed.`;
|
|
147
|
+
this.finish(record);
|
|
148
|
+
this.options.store?.updateRun(runId, record);
|
|
149
|
+
if (record.jobId)
|
|
150
|
+
this.options.store?.fail(record.jobId, `run-registry:${process.pid}`, error);
|
|
151
|
+
const output = snapshot(record);
|
|
152
|
+
this.notify({ type: "run_changed", run: output, previousStatus, reason: error });
|
|
153
|
+
return output;
|
|
154
|
+
}
|
|
116
155
|
list(controllerPiboSessionId, options = {}) {
|
|
117
156
|
this.prune();
|
|
118
157
|
return [...this.runs.values()]
|
|
@@ -219,6 +258,7 @@ export class PiboRunRegistry {
|
|
|
219
258
|
const notification = {
|
|
220
259
|
completed: [],
|
|
221
260
|
failed: [],
|
|
261
|
+
timedOut: [],
|
|
222
262
|
cancelled: [],
|
|
223
263
|
running: [],
|
|
224
264
|
};
|
|
@@ -228,6 +268,8 @@ export class PiboRunRegistry {
|
|
|
228
268
|
notification.completed.push(item);
|
|
229
269
|
else if (record.status === "failed")
|
|
230
270
|
notification.failed.push(item);
|
|
271
|
+
else if (record.status === "timed_out")
|
|
272
|
+
notification.timedOut.push(item);
|
|
231
273
|
else if (record.status === "cancelled")
|
|
232
274
|
notification.cancelled.push(item);
|
|
233
275
|
else
|
|
@@ -356,5 +398,9 @@ function recordFromStored(record) {
|
|
|
356
398
|
jobId: record.jobId,
|
|
357
399
|
retryable: record.retryable,
|
|
358
400
|
maxAttempts: record.maxAttempts,
|
|
401
|
+
timeoutMs: record.timeoutMs,
|
|
402
|
+
timeoutAt: record.timeoutAt,
|
|
403
|
+
timeoutPhase: record.timeoutPhase,
|
|
404
|
+
serviceWarning: record.serviceWarning,
|
|
359
405
|
};
|
|
360
406
|
}
|
package/dist/runs/tools.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { StringEnum, Type } from "@earendil-works/pi-ai";
|
|
2
2
|
import { defineTool } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { foregroundServiceWarning, hasMeaningfulTimeoutOutput, isConfiguredTimeoutError, PiboRunExecutionTimeoutError, resolveRunTimeoutMs } from "./lifecycle.js";
|
|
3
4
|
function resultText(prefix, value) {
|
|
4
5
|
return `${prefix}\n${JSON.stringify(value, null, 2)}`;
|
|
5
6
|
}
|
|
@@ -30,7 +31,7 @@ export function createRunToolDefinitions(yieldableTools, controller) {
|
|
|
30
31
|
defineTool({
|
|
31
32
|
name: "pibo_run_start",
|
|
32
33
|
label: "Pibo Run Start",
|
|
33
|
-
description: "Start a yieldable tool as a yielded run.
|
|
34
|
+
description: "Start a yieldable tool as a yielded run. The run records its configured timeout and classifies lifetime expiry separately from command failure. Use detached only for intentional fire-and-forget work.",
|
|
34
35
|
promptSnippet: "Use pibo_run_start to run a yieldable tool in the background. It returns a runId. Use pibo_run_read for completed results and pibo_run_wait/status/list/cancel/ack to manage runs.",
|
|
35
36
|
executionMode: "parallel",
|
|
36
37
|
parameters: Type.Object({
|
|
@@ -43,25 +44,42 @@ export function createRunToolDefinitions(yieldableTools, controller) {
|
|
|
43
44
|
}),
|
|
44
45
|
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
45
46
|
const tool = requireTool(yieldableTools, params.toolName);
|
|
47
|
+
const timeoutMs = resolveRunTimeoutMs(tool.name, params.arguments);
|
|
48
|
+
const serviceWarning = foregroundServiceWarning(tool.name, params.arguments, timeoutMs);
|
|
49
|
+
let observedOutput = false;
|
|
46
50
|
const run = controller.startToolRun({
|
|
47
51
|
toolName: tool.name,
|
|
48
52
|
params: params.arguments,
|
|
49
53
|
completionPolicy: params.completionPolicy,
|
|
54
|
+
timeoutMs,
|
|
55
|
+
serviceWarning,
|
|
50
56
|
async execute() {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
try {
|
|
58
|
+
const result = await tool.execute(toolCallId, params.arguments, signal, (update) => {
|
|
59
|
+
observedOutput ||= hasMeaningfulTimeoutOutput(update);
|
|
60
|
+
onUpdate?.(update);
|
|
61
|
+
}, ctx);
|
|
62
|
+
const resultObject = result;
|
|
63
|
+
const text = textFromToolResult(resultObject);
|
|
64
|
+
if (resultObject.isError === true) {
|
|
65
|
+
if (timeoutMs !== undefined && isConfiguredTimeoutError(text ?? ""))
|
|
66
|
+
throw new PiboRunExecutionTimeoutError(text ?? `${tool.name} timed out.`, observedOutput || hasMeaningfulTimeoutOutput(text) ? "lifetime" : "startup");
|
|
67
|
+
throw new Error(text ?? `${tool.name} returned an error result.`);
|
|
68
|
+
}
|
|
69
|
+
return { text, details: resultObject.details ?? result };
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (error instanceof PiboRunExecutionTimeoutError)
|
|
73
|
+
throw error;
|
|
74
|
+
if (timeoutMs !== undefined && isConfiguredTimeoutError(error))
|
|
75
|
+
throw new PiboRunExecutionTimeoutError(error instanceof Error ? error.message : String(error), observedOutput ? "lifetime" : "startup");
|
|
76
|
+
throw error;
|
|
56
77
|
}
|
|
57
|
-
return {
|
|
58
|
-
text,
|
|
59
|
-
details: resultObject.details ?? result,
|
|
60
|
-
};
|
|
61
78
|
},
|
|
62
79
|
});
|
|
80
|
+
const prefix = serviceWarning ? `Started yielded run ${run.runId}.\nWarning: ${serviceWarning}` : `Started yielded run ${run.runId}.`;
|
|
63
81
|
return {
|
|
64
|
-
content: [{ type: "text", text: resultText(
|
|
82
|
+
content: [{ type: "text", text: resultText(prefix, run) }],
|
|
65
83
|
details: run,
|
|
66
84
|
};
|
|
67
85
|
},
|
|
@@ -132,8 +150,8 @@ export function createRunToolDefinitions(yieldableTools, controller) {
|
|
|
132
150
|
defineTool({
|
|
133
151
|
name: "pibo_run_read",
|
|
134
152
|
label: "Pibo Run Read",
|
|
135
|
-
description: "Read the terminal result or error for a yielded run.",
|
|
136
|
-
promptSnippet: "Use pibo_run_read to retrieve a completed or
|
|
153
|
+
description: "Read the terminal result, timeout reason, or error for a yielded run.",
|
|
154
|
+
promptSnippet: "Use pibo_run_read to retrieve a completed, failed, or timed_out run result. Reading terminal tracked runs consumes reminders.",
|
|
137
155
|
executionMode: "parallel",
|
|
138
156
|
parameters: Type.Object({
|
|
139
157
|
runId: Type.String({ description: "Run id returned by pibo_run_start" }),
|
|
@@ -11,7 +11,7 @@ export function buildCompactTerminalRows(traceView, options) {
|
|
|
11
11
|
.filter((item) => item.node.type !== "agent.turn" && (options.showThinking || item.node.type !== "model.reasoning"));
|
|
12
12
|
const candidates = syncThinkingToolRows(flatNodes.map((item) => createRowCandidate(item.node, item.turnId)));
|
|
13
13
|
applyCompletedTurnTiming(candidates, turnById);
|
|
14
|
-
return groupRelatedToolCandidates(candidates).map((candidate) => candidate.row);
|
|
14
|
+
return groupRelatedToolCandidates(reconcileConceptualRowCandidates(candidates)).map((candidate) => candidate.row);
|
|
15
15
|
}
|
|
16
16
|
export function findActiveTurnStartedAt(traceView) {
|
|
17
17
|
if (!traceView)
|
|
@@ -100,7 +100,71 @@ function createRowCandidate(node, turnId) {
|
|
|
100
100
|
};
|
|
101
101
|
break;
|
|
102
102
|
}
|
|
103
|
-
return {
|
|
103
|
+
return {
|
|
104
|
+
...candidate,
|
|
105
|
+
row: {
|
|
106
|
+
...candidate.row,
|
|
107
|
+
id: compactTerminalRowIdentity(node),
|
|
108
|
+
...debugFields(node),
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
export function compactTerminalRowIdentity(node) {
|
|
113
|
+
if (node.type === "tool.call" || node.type === "tool.result" || node.type === "agent.delegation") {
|
|
114
|
+
const toolCallId = node.toolCallId ?? stableKeySuffix(node.stableKey, "tool:");
|
|
115
|
+
if (toolCallId)
|
|
116
|
+
return `terminal:tool:${toolCallId}`;
|
|
117
|
+
if (node.type === "agent.delegation" && node.linkedPiboSessionId) {
|
|
118
|
+
return `terminal:delegation:${node.linkedPiboSessionId}`;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if ((node.type === "agent.async" || node.type === "yielded.run") && node.runId) {
|
|
122
|
+
return `terminal:run:${node.runId}`;
|
|
123
|
+
}
|
|
124
|
+
if (node.type === "assistant.message") {
|
|
125
|
+
const assistantId = stableKeySuffix(node.stableKey, "assistant:");
|
|
126
|
+
if (assistantId)
|
|
127
|
+
return `terminal:assistant:${assistantId}`;
|
|
128
|
+
}
|
|
129
|
+
if (node.type === "model.reasoning") {
|
|
130
|
+
const reasoningId = stableKeySuffix(node.stableKey, "reasoning:");
|
|
131
|
+
if (reasoningId)
|
|
132
|
+
return `terminal:reasoning:${reasoningId}`;
|
|
133
|
+
}
|
|
134
|
+
return node.id;
|
|
135
|
+
}
|
|
136
|
+
function stableKeySuffix(stableKey, prefix) {
|
|
137
|
+
if (!stableKey?.startsWith(prefix))
|
|
138
|
+
return undefined;
|
|
139
|
+
const suffix = stableKey.slice(prefix.length);
|
|
140
|
+
return suffix && suffix !== "undefined" ? suffix : undefined;
|
|
141
|
+
}
|
|
142
|
+
function reconcileConceptualRowCandidates(candidates) {
|
|
143
|
+
const reconciled = [];
|
|
144
|
+
const indexById = new Map();
|
|
145
|
+
for (const candidate of candidates) {
|
|
146
|
+
const existingIndex = indexById.get(candidate.row.id);
|
|
147
|
+
if (existingIndex === undefined) {
|
|
148
|
+
indexById.set(candidate.row.id, reconciled.length);
|
|
149
|
+
reconciled.push(candidate);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const existing = reconciled[existingIndex];
|
|
153
|
+
reconciled[existingIndex] = {
|
|
154
|
+
...candidate,
|
|
155
|
+
turnId: candidate.turnId ?? existing.turnId,
|
|
156
|
+
row: {
|
|
157
|
+
...existing.row,
|
|
158
|
+
...candidate.row,
|
|
159
|
+
sourceNodeIds: [...new Set([...existing.row.sourceNodeIds, ...candidate.row.sourceNodeIds])],
|
|
160
|
+
input: candidate.row.input ?? existing.row.input,
|
|
161
|
+
output: candidate.row.output ?? existing.row.output,
|
|
162
|
+
error: candidate.row.error ?? existing.row.error,
|
|
163
|
+
payloadRefs: { ...existing.row.payloadRefs, ...candidate.row.payloadRefs },
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
return reconciled;
|
|
104
168
|
}
|
|
105
169
|
function applyCompletedTurnTiming(candidates, turnById) {
|
|
106
170
|
for (const turn of turnById.values()) {
|
|
@@ -34,12 +34,12 @@ export function reconcileAsyncAgentRunStatuses(nodes) {
|
|
|
34
34
|
if (!latest)
|
|
35
35
|
continue;
|
|
36
36
|
const status = stringValue(latest.snapshot.status);
|
|
37
|
-
if (status !== "completed" && status !== "cancelled" && status !== "failed")
|
|
37
|
+
if (status !== "completed" && status !== "cancelled" && status !== "failed" && status !== "timed_out")
|
|
38
38
|
continue;
|
|
39
|
-
node.status = status === "failed" ? "error" : "done";
|
|
39
|
+
node.status = status === "failed" || status === "timed_out" ? "error" : "done";
|
|
40
40
|
node.completedAt = latest.completedAt ?? node.completedAt;
|
|
41
41
|
node.output = latest.snapshot;
|
|
42
|
-
if (status === "failed")
|
|
42
|
+
if (status === "failed" || status === "timed_out")
|
|
43
43
|
node.error = stringValue(latest.snapshot.summary) ?? node.error;
|
|
44
44
|
}
|
|
45
45
|
}
|
|
@@ -67,7 +67,7 @@ function createAsyncAgentRunNode(parent, piboSessionId, startedAt, delegation) {
|
|
|
67
67
|
title: subagentName,
|
|
68
68
|
status: asyncAgentStatus(parent, runStatus),
|
|
69
69
|
startedAt: delegation?.startedAt ?? startedAt ?? parent.startedAt,
|
|
70
|
-
completedAt: runStatus === "completed" || runStatus === "cancelled" ? parent.completedAt : undefined,
|
|
70
|
+
completedAt: runStatus === "completed" || runStatus === "cancelled" || runStatus === "failed" || runStatus === "timed_out" ? parent.completedAt : undefined,
|
|
71
71
|
summary: `Started by ${parent.title}`,
|
|
72
72
|
input: {
|
|
73
73
|
startedBy: parent.title,
|
|
@@ -5,6 +5,13 @@ import { createRunNotificationNode, parseRunNotificationText } from "./trace-run
|
|
|
5
5
|
import { findLikelyTraceChildSession, isSubagentToolName, } from "./trace-subagent-links.js";
|
|
6
6
|
export function applySingleEventToNodes(nodes, byId, piboSessionId, storedEvent, childByParent, linkedChildByToolCallId, hasPersistedTranscript, openTranscriptEventIds, sessionStatus) {
|
|
7
7
|
const payload = storedEvent.payload;
|
|
8
|
+
const confirmedUserMessage = hasPersistedTranscript ? confirmedUserMessageEchoNode(nodes, storedEvent) : undefined;
|
|
9
|
+
if (confirmedUserMessage) {
|
|
10
|
+
if (payload.type === "message_steered" && payload.activeEventId) {
|
|
11
|
+
confirmedUserMessage.parentId = messageTurnNodeId(payload.activeEventId);
|
|
12
|
+
}
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
8
15
|
if (hasPersistedTranscript &&
|
|
9
16
|
isTranscriptEchoEvent(payload) &&
|
|
10
17
|
!shouldKeepTranscriptEchoEvent(payload, openTranscriptEventIds)) {
|
|
@@ -84,8 +91,14 @@ export function applySingleEventToNodes(nodes, byId, piboSessionId, storedEvent,
|
|
|
84
91
|
return;
|
|
85
92
|
}
|
|
86
93
|
}
|
|
87
|
-
if (node.eventId
|
|
88
|
-
|
|
94
|
+
if (node.eventId) {
|
|
95
|
+
const existing = byId.get(node.id);
|
|
96
|
+
if (existing) {
|
|
97
|
+
if (node.type === "user.message" && node.parentId)
|
|
98
|
+
existing.parentId = node.parentId;
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
89
102
|
attachExecutionCommandToOpenTurn(node, byId);
|
|
90
103
|
attachAsyncAgentRunNode(node, piboSessionId, storedEvent.createdAt);
|
|
91
104
|
nodes.push(node);
|
|
@@ -174,7 +187,7 @@ export function reconcileTranscriptUserMessageTimestamps(nodes, events) {
|
|
|
174
187
|
let userCursor = 0;
|
|
175
188
|
for (const storedEvent of events) {
|
|
176
189
|
const event = storedEvent.payload;
|
|
177
|
-
if (event.type !== "message_queued" || event.source !== "user")
|
|
190
|
+
if ((event.type !== "message_queued" && event.type !== "message_steered") || event.source !== "user")
|
|
178
191
|
continue;
|
|
179
192
|
const eventId = typeof event.eventId === "string" ? event.eventId : storedEvent.eventId;
|
|
180
193
|
const text = typeof event.text === "string" ? event.text : undefined;
|
|
@@ -192,12 +205,15 @@ export function reconcileTranscriptUserMessageTimestamps(nodes, events) {
|
|
|
192
205
|
}
|
|
193
206
|
}
|
|
194
207
|
export function isConfirmedUserMessageEcho(nodes, event) {
|
|
208
|
+
return Boolean(confirmedUserMessageEchoNode(nodes, event));
|
|
209
|
+
}
|
|
210
|
+
function confirmedUserMessageEchoNode(nodes, event) {
|
|
195
211
|
const payload = event.payload;
|
|
196
|
-
if (payload.type !== "message_queued" || payload.source !== "user")
|
|
197
|
-
return
|
|
212
|
+
if ((payload.type !== "message_queued" && payload.type !== "message_steered") || payload.source !== "user")
|
|
213
|
+
return undefined;
|
|
198
214
|
const eventId = typeof payload.eventId === "string" ? payload.eventId : event.eventId;
|
|
199
215
|
const text = typeof payload.text === "string" ? payload.text : undefined;
|
|
200
|
-
return nodes.
|
|
216
|
+
return flattenTraceNodes([...nodes]).find((node) => {
|
|
201
217
|
if (node.type !== "user.message" || node.source !== "transcript")
|
|
202
218
|
return false;
|
|
203
219
|
if (eventId && (node.entryId === eventId || node.stableKey === `entry:${eventId}`))
|
|
@@ -261,7 +277,8 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
|
|
|
261
277
|
children: [],
|
|
262
278
|
};
|
|
263
279
|
switch (event.type) {
|
|
264
|
-
case "message_queued":
|
|
280
|
+
case "message_queued":
|
|
281
|
+
case "message_steered": {
|
|
265
282
|
const notification = parseRunNotificationText(event.text);
|
|
266
283
|
if (event.source === "service" && notification) {
|
|
267
284
|
return createRunNotificationNode({
|
|
@@ -277,6 +294,7 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
|
|
|
277
294
|
}
|
|
278
295
|
return {
|
|
279
296
|
...base,
|
|
297
|
+
...(event.type === "message_steered" && event.activeEventId ? { parentId: messageTurnNodeId(event.activeEventId) } : {}),
|
|
280
298
|
type: "user.message",
|
|
281
299
|
title: "User Message",
|
|
282
300
|
status: "done",
|
|
@@ -368,10 +386,12 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
|
|
|
368
386
|
children: [],
|
|
369
387
|
};
|
|
370
388
|
}
|
|
371
|
-
case "subagent_session":
|
|
389
|
+
case "subagent_session": {
|
|
390
|
+
const childPiboSessionId = nonEmptyString(event.childPiboSessionId);
|
|
391
|
+
const eventInstanceKey = traceEventInstanceKey(eventSequence, streamId, event);
|
|
372
392
|
return {
|
|
373
393
|
...base,
|
|
374
|
-
id: event.toolCallId ? `tool:${event.toolCallId}` :
|
|
394
|
+
id: event.toolCallId ? `tool:${event.toolCallId}` : `event:subagent_session:${eventInstanceKey}`,
|
|
375
395
|
eventId,
|
|
376
396
|
toolCallId: event.toolCallId,
|
|
377
397
|
type: "agent.delegation",
|
|
@@ -379,10 +399,13 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
|
|
|
379
399
|
status: sessionStatus === "running" ? "running" : "done",
|
|
380
400
|
summary: event.subagentName,
|
|
381
401
|
input: { subagentName: event.subagentName, threadKey: event.threadKey },
|
|
382
|
-
linkedPiboSessionId:
|
|
383
|
-
stableKey: event.toolCallId
|
|
402
|
+
linkedPiboSessionId: childPiboSessionId,
|
|
403
|
+
stableKey: event.toolCallId
|
|
404
|
+
? `tool:${event.toolCallId}`
|
|
405
|
+
: childPiboSessionId ? `subagent:${childPiboSessionId}` : `subagent:event:${eventInstanceKey}`,
|
|
384
406
|
children: [],
|
|
385
407
|
};
|
|
408
|
+
}
|
|
386
409
|
case "execution_result":
|
|
387
410
|
if (isInternalSessionOperation(event.action))
|
|
388
411
|
return undefined;
|
|
@@ -394,21 +417,24 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
|
|
|
394
417
|
input: { action: event.action },
|
|
395
418
|
output: event.result,
|
|
396
419
|
};
|
|
397
|
-
case "compaction_start":
|
|
420
|
+
case "compaction_start": {
|
|
421
|
+
const eventInstanceKey = traceEventInstanceKey(eventSequence, streamId, event);
|
|
398
422
|
return {
|
|
399
423
|
...base,
|
|
400
|
-
id: `event:compaction:${
|
|
424
|
+
id: `event:compaction:${eventInstanceKey}`,
|
|
401
425
|
type: "execution.compaction",
|
|
402
426
|
title: "compact",
|
|
403
427
|
status: "running",
|
|
404
428
|
summary: "Compacting",
|
|
405
429
|
input: { reason: event.reason },
|
|
406
|
-
stableKey:
|
|
430
|
+
stableKey: `compaction:${eventInstanceKey}`,
|
|
407
431
|
};
|
|
408
|
-
|
|
432
|
+
}
|
|
433
|
+
case "compaction_end": {
|
|
434
|
+
const eventInstanceKey = traceEventInstanceKey(eventSequence, streamId, event);
|
|
409
435
|
return {
|
|
410
436
|
...base,
|
|
411
|
-
id: `event:compaction:end:${
|
|
437
|
+
id: `event:compaction:end:${eventInstanceKey}`,
|
|
412
438
|
type: "execution.compaction",
|
|
413
439
|
title: "compact",
|
|
414
440
|
status: event.errorMessage ? "error" : "done",
|
|
@@ -417,8 +443,9 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
|
|
|
417
443
|
input: { reason: event.reason },
|
|
418
444
|
output: event.result,
|
|
419
445
|
error: event.errorMessage,
|
|
420
|
-
stableKey:
|
|
446
|
+
stableKey: `compaction:${eventInstanceKey}`,
|
|
421
447
|
};
|
|
448
|
+
}
|
|
422
449
|
case "session_error":
|
|
423
450
|
return {
|
|
424
451
|
...base,
|
|
@@ -435,7 +462,7 @@ function traceNodeFromEvent(piboSessionId, event, childByParent, linkedChildByTo
|
|
|
435
462
|
}
|
|
436
463
|
}
|
|
437
464
|
function mergeAssistantDeltaEvent(nodes, byId, event, sessionStatus, createdAt, eventSequence, streamId, streamFrameIndex) {
|
|
438
|
-
if (event.text.length === 0)
|
|
465
|
+
if (typeof event.text !== "string" || event.text.length === 0)
|
|
439
466
|
return;
|
|
440
467
|
const assistantId = assistantEventNodeId(event);
|
|
441
468
|
const id = assistantId ? assistantMessageNodeId(assistantId) : `event:assistant_delta:${cryptoSafeId(event)}`;
|
|
@@ -474,7 +501,7 @@ function mergeAssistantMessageEvent(target, update) {
|
|
|
474
501
|
target.completedAt = update.completedAt ?? target.completedAt;
|
|
475
502
|
}
|
|
476
503
|
function mergeThinkingDeltaEvent(nodes, byId, event, sessionStatus, createdAt, eventSequence, streamId, streamFrameIndex) {
|
|
477
|
-
if (event.text.length === 0)
|
|
504
|
+
if (typeof event.text !== "string" || event.text.length === 0)
|
|
478
505
|
return;
|
|
479
506
|
const thinkingId = thinkingEventNodeId(event);
|
|
480
507
|
const id = thinkingId ? thinkingNodeId(thinkingId) : `event:thinking_delta:${cryptoSafeId(event)}`;
|
|
@@ -642,6 +669,7 @@ function eventTraceNodeOrder(eventSequence, type, streamId, streamFrameIndex) {
|
|
|
642
669
|
function eventNodeKind(type) {
|
|
643
670
|
switch (type) {
|
|
644
671
|
case "message_queued":
|
|
672
|
+
case "message_steered":
|
|
645
673
|
return "user.message";
|
|
646
674
|
case "message_started":
|
|
647
675
|
case "message_finished":
|
|
@@ -792,6 +820,18 @@ function parseTimestamp(value) {
|
|
|
792
820
|
const timestamp = new Date(value).getTime();
|
|
793
821
|
return Number.isFinite(timestamp) ? timestamp : undefined;
|
|
794
822
|
}
|
|
823
|
+
function traceEventInstanceKey(eventSequence, streamId, event) {
|
|
824
|
+
if (eventSequence !== undefined)
|
|
825
|
+
return `sequence:${eventSequence}`;
|
|
826
|
+
if (streamId !== undefined)
|
|
827
|
+
return `stream:${streamId}`;
|
|
828
|
+
return cryptoSafeId(event);
|
|
829
|
+
}
|
|
830
|
+
function nonEmptyString(value) {
|
|
831
|
+
if (typeof value !== "string")
|
|
832
|
+
return undefined;
|
|
833
|
+
return value.trim() || undefined;
|
|
834
|
+
}
|
|
795
835
|
function cryptoSafeId(value) {
|
|
796
836
|
return base64UrlEncode(new TextEncoder().encode(JSON.stringify(value))).slice(0, 48);
|
|
797
837
|
}
|
|
@@ -22,6 +22,11 @@ export function compareTraceNodes(left, right) {
|
|
|
22
22
|
const bySameTurnPhase = compareSameTurnPhase(left, right);
|
|
23
23
|
if (bySameTurnPhase !== 0)
|
|
24
24
|
return bySameTurnPhase;
|
|
25
|
+
if (left.orderKey?.sourceRank === right.orderKey?.sourceRank) {
|
|
26
|
+
const bySameSourceOrder = compareTraceOrder(left.orderKey, right.orderKey);
|
|
27
|
+
if (bySameSourceOrder !== 0)
|
|
28
|
+
return bySameSourceOrder;
|
|
29
|
+
}
|
|
25
30
|
const byStartTime = compareOptionalIsoTime(left.startedAt, right.startedAt);
|
|
26
31
|
if (byStartTime !== 0)
|
|
27
32
|
return byStartTime;
|
|
@@ -23,6 +23,21 @@ export function mergeOlderTracePage(current, older) {
|
|
|
23
23
|
eventLimit: (current.eventLimit ?? 0) + (older.eventLimit ?? older.pageSize ?? 0),
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
|
+
export function mergeRefreshedTracePage(current, refreshed) {
|
|
27
|
+
if (current.piboSessionId !== refreshed.piboSessionId)
|
|
28
|
+
return refreshed;
|
|
29
|
+
return {
|
|
30
|
+
...refreshed,
|
|
31
|
+
nodes: mergeTraceNodes(current.nodes, refreshed.nodes),
|
|
32
|
+
rawEvents: current.rawEvents.length ? current.rawEvents : refreshed.rawEvents,
|
|
33
|
+
beforeCursor: current.beforeCursor,
|
|
34
|
+
firstEventSequence: current.firstEventSequence ?? refreshed.firstEventSequence,
|
|
35
|
+
nextBeforeSequence: current.nextBeforeSequence,
|
|
36
|
+
nextBeforeCursor: current.nextBeforeCursor,
|
|
37
|
+
hasOlderEvents: current.hasOlderEvents,
|
|
38
|
+
eventLimit: current.eventLimit ?? refreshed.eventLimit,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
26
41
|
function mergeTraceNodes(olderNodes, currentNodes) {
|
|
27
42
|
const byId = new Map();
|
|
28
43
|
for (const node of flattenTraceNodes([...olderNodes])) {
|
|
@@ -19,7 +19,7 @@ export function parseRunNotificationText(text) {
|
|
|
19
19
|
export function createRunNotificationNode(input) {
|
|
20
20
|
const runs = runNotificationRuns(input.notification);
|
|
21
21
|
const singleRun = runs.length === 1 ? runs[0] : undefined;
|
|
22
|
-
const failedCount = countRunGroup(input.notification.failed);
|
|
22
|
+
const failedCount = countRunGroup(input.notification.failed) + countRunGroup(input.notification.timedOut);
|
|
23
23
|
const runningCount = countRunGroup(input.notification.running);
|
|
24
24
|
return {
|
|
25
25
|
id: input.id,
|
|
@@ -43,6 +43,7 @@ function runNotificationRuns(notification) {
|
|
|
43
43
|
return [
|
|
44
44
|
...runGroup(notification.completed),
|
|
45
45
|
...runGroup(notification.failed),
|
|
46
|
+
...runGroup(notification.timedOut),
|
|
46
47
|
...runGroup(notification.cancelled),
|
|
47
48
|
...runGroup(notification.running),
|
|
48
49
|
];
|
|
@@ -57,6 +58,7 @@ function runNotificationSummary(notification) {
|
|
|
57
58
|
const parts = [
|
|
58
59
|
[countRunGroup(notification.completed), "completed"],
|
|
59
60
|
[countRunGroup(notification.failed), "failed"],
|
|
61
|
+
[countRunGroup(notification.timedOut), "timed out"],
|
|
60
62
|
[countRunGroup(notification.cancelled), "cancelled"],
|
|
61
63
|
[countRunGroup(notification.running), "running"],
|
|
62
64
|
]
|
|
@@ -12,7 +12,7 @@ function node(input, context) {
|
|
|
12
12
|
function runStatus(status) {
|
|
13
13
|
if (status === "completed")
|
|
14
14
|
return "done";
|
|
15
|
-
if (status === "failed")
|
|
15
|
+
if (status === "failed" || status === "timed_out")
|
|
16
16
|
return "error";
|
|
17
17
|
return status;
|
|
18
18
|
}
|
|
@@ -28,7 +28,7 @@ function settleActiveSessionNodes(piboSessionId, context, status, terminalSource
|
|
|
28
28
|
}
|
|
29
29
|
export const sessionLifecycleSignalProducer = {
|
|
30
30
|
name: "session-lifecycle",
|
|
31
|
-
accepts: (input) => ["session_created", "session_disposed", "session_processing_changed", "message_accepted", "queue_changed", "recovery", "session_interrupted", "signal_node_pruned"].includes(input.type),
|
|
31
|
+
accepts: (input) => ["session_created", "session_disposed", "session_processing_changed", "message_accepted", "message_rejected", "queue_changed", "recovery", "session_interrupted", "signal_node_pruned"].includes(input.type),
|
|
32
32
|
project(input, context) {
|
|
33
33
|
const data = input;
|
|
34
34
|
if (data.type === "session_created") {
|
|
@@ -78,6 +78,15 @@ export const sessionLifecycleSignalProducer = {
|
|
|
78
78
|
}
|
|
79
79
|
return mutations;
|
|
80
80
|
}
|
|
81
|
+
if (data.type === "message_rejected") {
|
|
82
|
+
const acceptedTurn = context.getNode(`turn:${data.piboSessionId}:${data.eventId}`);
|
|
83
|
+
return [
|
|
84
|
+
{ type: "remove_node", nodeId: `message:${data.piboSessionId}:${data.eventId}` },
|
|
85
|
+
...(acceptedTurn?.metadata?.accepted === true
|
|
86
|
+
? [{ type: "remove_node", nodeId: acceptedTurn.id }]
|
|
87
|
+
: []),
|
|
88
|
+
];
|
|
89
|
+
}
|
|
81
90
|
if (data.type === "session_disposed") {
|
|
82
91
|
return [
|
|
83
92
|
...settleActiveSessionNodes(data.piboSessionId, context, "cancelled", "session_disposed"),
|
|
@@ -133,6 +142,10 @@ export const outputSignalProducer = {
|
|
|
133
142
|
mutations.push({ type: "set_session_queue", piboSessionId, queuedMessages: event.queuedMessages });
|
|
134
143
|
mutations.push({ type: "upsert_node", node: node({ id: `message:${piboSessionId}:${event.eventId ?? context.now()}`, kind: "message", status: "queued", piboSessionId, metadata: { source: event.source } }, context) });
|
|
135
144
|
}
|
|
145
|
+
if (event.type === "message_steered") {
|
|
146
|
+
mutations.push({ type: "patch_node", nodeId: `session:${piboSessionId}`, patch: { status: "running" } });
|
|
147
|
+
mutations.push({ type: "upsert_node", node: node({ id: `message:${piboSessionId}:${event.eventId ?? context.now()}`, kind: "message", status: "done", piboSessionId, parentNodeId: event.activeEventId ? `turn:${piboSessionId}:${event.activeEventId}` : undefined, completedAt: context.now(), metadata: { source: event.source, delivery: "steer" } }, context) });
|
|
148
|
+
}
|
|
136
149
|
if (event.type === "message_started") {
|
|
137
150
|
mutations.push({ type: "patch_node", nodeId: `session:${piboSessionId}`, patch: { status: "running" } });
|
|
138
151
|
if (event.eventId) {
|
|
@@ -200,7 +213,7 @@ export const runSignalProducer = {
|
|
|
200
213
|
if (data.type !== "run_changed")
|
|
201
214
|
return [];
|
|
202
215
|
const run = data.run;
|
|
203
|
-
return [{ type: "upsert_node", node: node({ id: `run:${run.runId}`, kind: "yielded_run", status: runStatus(run.status), piboSessionId: run.controllerPiboSessionId, startedAt: run.createdAt, completedAt: run.completedAt, error: run.status === "failed" ? { message: run.summary ?? "Run failed.", source: "run" } : undefined, metadata: { runId: run.runId, toolName: run.toolName, completionPolicy: run.completionPolicy, consumed: run.consumed, summary: run.summary, previousStatus: data.previousStatus, reason: data.reason } }, context) }];
|
|
216
|
+
return [{ type: "upsert_node", node: node({ id: `run:${run.runId}`, kind: "yielded_run", status: runStatus(run.status), piboSessionId: run.controllerPiboSessionId, startedAt: run.createdAt, completedAt: run.completedAt, error: run.status === "failed" || run.status === "timed_out" ? { message: run.summary ?? "Run failed.", source: "run" } : undefined, metadata: { runId: run.runId, toolName: run.toolName, completionPolicy: run.completionPolicy, consumed: run.consumed, summary: run.summary, timeoutMs: run.timeoutMs, timeoutAt: run.timeoutAt, timeoutPhase: run.timeoutPhase, serviceWarning: run.serviceWarning, previousStatus: data.previousStatus, reason: data.reason } }, context) }];
|
|
204
217
|
},
|
|
205
218
|
};
|
|
206
219
|
export function createDefaultSignalProducers() {
|