@pasko70/pibo 1.16.5 → 2.0.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/agent-runtime/auth.js +28 -0
- package/dist/agent-runtime/capabilities.js +221 -0
- package/dist/agent-runtime/context-build.js +313 -0
- package/dist/agent-runtime/contract.js +45 -0
- package/dist/agent-runtime/errors.js +57 -0
- package/dist/agent-runtime/events.js +1 -0
- package/dist/agent-runtime/history.js +1 -0
- package/dist/agent-runtime/profile-validation.js +109 -0
- package/dist/agent-runtime/registry.js +608 -0
- package/dist/agent-runtime/resource-files.js +87 -0
- package/dist/agent-runtime/resource-service.js +635 -0
- package/dist/agent-runtime/resources.js +1 -0
- package/dist/agent-runtime/routed-session.js +931 -0
- package/dist/agent-runtime/testing/contract.js +51 -0
- package/dist/agent-runtime/testing/fake-adapter.js +192 -0
- package/dist/agent-runtime/types.js +3 -0
- package/dist/agent-runtimes/codex-native/adapter.js +905 -0
- package/dist/agent-runtimes/codex-native/auth.js +476 -0
- package/dist/agent-runtimes/codex-native/client.js +734 -0
- package/dist/agent-runtimes/codex-native/config.js +152 -0
- package/dist/agent-runtimes/codex-native/history.js +283 -0
- package/dist/agent-runtimes/codex-native/mcp-stdio-launcher.js +89 -0
- package/dist/agent-runtimes/codex-native/models.js +581 -0
- package/dist/agent-runtimes/codex-native/process.js +459 -0
- package/dist/agent-runtimes/codex-native/protocol-types.js +1 -0
- package/dist/agent-runtimes/codex-native/protocol-version.js +7 -0
- package/dist/agent-runtimes/codex-native/redaction.js +31 -0
- package/dist/agent-runtimes/codex-native/requests.js +378 -0
- package/dist/agent-runtimes/codex-native/resource-delivery.js +445 -0
- package/dist/agent-runtimes/codex-native/thread.js +388 -0
- package/dist/agent-runtimes/codex-native/turn.js +849 -0
- package/dist/agent-runtimes/pi/adapter.js +653 -0
- package/dist/agent-runtimes/pi/auth.js +455 -0
- package/dist/agent-runtimes/pi/history.js +640 -0
- package/dist/agent-runtimes/pi/model-catalog.js +87 -0
- package/dist/agent-runtimes/pi/routed-session.js +1464 -0
- package/dist/agent-runtimes/pi/runtime.js +534 -0
- package/dist/agent-runtimes/pi/tool-compiler.js +69 -0
- package/dist/apps/chat/agent-profiles.js +40 -23
- package/dist/apps/chat/agent-store.js +111 -64
- package/dist/apps/chat/chat-api-routes.js +1 -1
- package/dist/apps/chat/chat-request-normalizers.js +47 -0
- package/dist/apps/chat/data/chat-data-mappers.js +25 -9
- package/dist/apps/chat/data/history-query-service.js +108 -0
- package/dist/apps/chat/data/session-query-service.js +24 -3
- package/dist/apps/chat/data/timeline-query-service.js +3 -3
- package/dist/apps/chat/model-catalog.js +1 -55
- package/dist/apps/chat/provider-auth-actions.js +171 -23
- package/dist/apps/chat/stream.js +12 -0
- package/dist/apps/chat/trace-v2.js +1 -0
- package/dist/apps/chat/trace.js +126 -415
- package/dist/apps/chat/web-app.js +582 -157
- package/dist/apps/chat-ui/assets/{dist-QXwINWYY.js → dist-9v2ZXT6V.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CvZNkkOi.js → dist-B1LVAytl.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-_SP-MjLq.js → dist-C6LPDFXU.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-Bad4Yz4P.js → dist-DVtXxfHc.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-xy-zomwl.js → dist-DW4Qrfbd.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DI4i80O2.js → dist-DmbsHdMC.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-D17yAUqp.js → dist-DyvwuSFO.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-Bc3Vbgjh.js → dist-I26VB0QO.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BzqH7w58.js → dist-RtHbJRPV.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CMDJ-WZm.js → dist-sZgJkWZb.js} +1 -1
- package/dist/apps/chat-ui/assets/index-BPbNvcZe.css +1 -0
- package/dist/apps/chat-ui/assets/index-CIOf2uL2.js +237 -0
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/index-1ivRyjFs.js +41 -0
- package/dist/apps/chat-vscode-web/index.html +1 -1
- package/dist/apps/vscode-artifacts/latest.vsix +0 -0
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-2.0.0.vsix +0 -0
- package/dist/auth/login-actions.js +5 -251
- package/dist/cli-session/localSessionSource.js +8 -1
- package/dist/core/context-build.js +124 -20
- package/dist/core/default-profile.js +8 -0
- package/dist/core/profiles.js +30 -3
- package/dist/core/routed-session.js +5 -1446
- package/dist/core/runtime.js +5 -561
- package/dist/core/session-router.js +637 -83
- package/dist/data/cli.js +20 -3
- package/dist/data/schema.js +104 -1
- package/dist/data/session-store.js +24 -1
- package/dist/debug/events.js +25 -10
- package/dist/debug/failures.js +23 -3
- package/dist/debug/index.js +24 -6
- package/dist/debug/messages.js +87 -7
- package/dist/debug/persisted-payloads.js +31 -0
- package/dist/debug/pty.js +7 -6
- package/dist/debug/runtime-binding.js +29 -0
- package/dist/debug/session.js +137 -0
- package/dist/debug/summary.js +12 -0
- package/dist/debug/telemetry.js +28 -0
- package/dist/debug/tools.js +12 -2
- package/dist/debug/trace.js +154 -76
- package/dist/gateway/server.js +87 -2
- package/dist/gateway/tool.js +5 -5
- package/dist/index.js +21 -2
- package/dist/loops/tools.js +13 -12
- package/dist/mcp/agent-context.js +13 -4
- package/dist/mcp/client.js +10 -6
- package/dist/mcp/config.js +5 -2
- package/dist/mcp/runtime-session.js +166 -0
- package/dist/plugins/builtin.js +191 -53
- package/dist/plugins/codex-native.js +29 -0
- package/dist/plugins/registry.js +66 -2
- package/dist/runs/tools.js +26 -25
- package/dist/session-ui/terminalRows.js +2 -2
- package/dist/sessions/pibo-data-store.js +125 -16
- package/dist/sessions/runtime-binding.js +107 -0
- package/dist/sessions/sqlite-store.js +239 -18
- package/dist/sessions/store.js +86 -7
- package/dist/shared/trace-engine.js +18 -9
- package/dist/shared/trace-event-projection.js +29 -11
- package/dist/shared/{trace-transcript.js → trace-history.js} +193 -195
- package/dist/shared/trace-order.js +9 -4
- package/dist/shared/trace-page-merge.js +1 -1
- package/dist/signals/projector.js +20 -2
- package/dist/signals/registry.js +11 -0
- package/dist/subagents/tool.js +8 -6
- package/dist/tools/codex-browser.js +20 -19
- package/dist/tools/codex-compat.js +10 -9
- package/dist/tools/codex-image-generation.js +8 -6
- package/dist/tools/contract.js +45 -0
- package/dist/tools/credential-registry.js +182 -0
- package/dist/tools/mcp-bridge.js +511 -0
- package/dist/tools/payload-writer.js +17 -0
- package/dist/tools/runtime/tool.js +10 -9
- package/dist/tools/schema.js +9 -0
- package/dist/tools/session-service.js +193 -0
- package/dist/tools/session-tool-set.js +85 -0
- package/dist/web-annotations/tools.js +17 -16
- package/docs/README.md +11 -0
- package/package.json +3 -2
- package/skills/builtin/pibo-agent-runtime-adapter/SKILL.md +183 -0
- package/skills/builtin/pibo-agent-runtime-adapter/evals/README.md +10 -0
- package/skills/builtin/pibo-agent-runtime-adapter/evals/evals.json +45 -0
- package/skills/builtin/pibo-agent-runtime-adapter/evals/fixtures/orion-full-harness.md +60 -0
- package/skills/builtin/pibo-agent-runtime-adapter/evals/fixtures/relay-partial-harness.md +52 -0
- package/skills/builtin/pibo-agent-runtime-adapter/references/capabilities-and-designer.md +194 -0
- package/skills/builtin/pibo-agent-runtime-adapter/references/history-debug-and-security.md +213 -0
- package/skills/builtin/pibo-agent-runtime-adapter/references/interfaces-and-registration.md +214 -0
- package/skills/builtin/pibo-agent-runtime-adapter/references/lifecycle-bindings-and-events.md +193 -0
- package/skills/builtin/pibo-agent-runtime-adapter/references/portable-delivery-and-native-behavior.md +226 -0
- package/skills/builtin/pibo-agent-runtime-adapter/references/testing-migration-and-validation.md +256 -0
- package/dist/apps/chat-ui/assets/index-C45B0rw7.js +0 -237
- package/dist/apps/chat-ui/assets/index-E0RmuCkY.css +0 -1
- package/dist/apps/chat-vscode-web/assets/index-D-tSsXLG.js +0 -41
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-1.16.5.vsix +0 -0
|
@@ -0,0 +1,1464 @@
|
|
|
1
|
+
import { SessionManager, shouldCompact } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { PiboSteeringUnavailableError } from "../../core/events.js";
|
|
3
|
+
import { getOpenAiCodexProviderUsageForActiveModel } from "../../auth/openai-codex-usage.js";
|
|
4
|
+
import { normalizeSessionErrorDetails, runtimeSessionErrorDetails } from "../../core/session-errors.js";
|
|
5
|
+
import { expandInlineSkills } from "../../core/skill-expansion.js";
|
|
6
|
+
import { PIBO_CONTEXT_GUARD_RESUME_MESSAGE_TYPE, PIBO_CONTEXT_GUARD_RESUME_PROMPT, cancelPiboAssistantContextGuardRecovery, claimPiboAssistantContextGuardRecovery, waitForPiboAssistantContextGuardRecovery, } from "../../core/context-guard.js";
|
|
7
|
+
import { PIBO_PROVIDER_RECOVERY_MESSAGE_TYPE, PIBO_PROVIDER_RECOVERY_PROMPT, PiboProviderRecoveryCancelledError, isRetryablePiboAssistantError, isRetryablePiboProviderError, resolvePiboProviderRecoverySettings, waitForPiboProviderRecovery, } from "../../core/provider-recovery.js";
|
|
8
|
+
import { PiAgentRuntimeAuthController } from "./auth.js";
|
|
9
|
+
import { loadModelCatalog as loadPiModelCatalog } from "./model-catalog.js";
|
|
10
|
+
import { PIBO_TRANSCRIPT_INTEGRITY_RESUME_MESSAGE_TYPE, PIBO_TRANSCRIPT_INTEGRITY_RESUME_PROMPT, PiboTranscriptIntegrityError, claimPiboTranscriptIntegrityContinuation, settlePiboTranscriptIntegrityContinuation, } from "../../core/transcript-integrity.js";
|
|
11
|
+
const FAST_SERVICE_TIER = "priority";
|
|
12
|
+
const RUN_REMINDER_CAPABILITY_TOOLS = new Set([
|
|
13
|
+
"pibo_run_status",
|
|
14
|
+
"pibo_run_wait",
|
|
15
|
+
"pibo_run_read",
|
|
16
|
+
"pibo_run_cancel",
|
|
17
|
+
"pibo_run_ack",
|
|
18
|
+
]);
|
|
19
|
+
function modelSupportsFastServiceTier(model) {
|
|
20
|
+
if (!model)
|
|
21
|
+
return false;
|
|
22
|
+
if (model.api === "openai-codex-responses")
|
|
23
|
+
return true;
|
|
24
|
+
return model.api === "openai-responses" && (model.provider === "openai" || model.provider === "openai-codex");
|
|
25
|
+
}
|
|
26
|
+
function withFastServiceTier(payload) {
|
|
27
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload))
|
|
28
|
+
return payload;
|
|
29
|
+
return { ...payload, service_tier: FAST_SERVICE_TIER };
|
|
30
|
+
}
|
|
31
|
+
function withFastServiceTierOption(options) {
|
|
32
|
+
return { ...(options ?? {}), serviceTier: FAST_SERVICE_TIER };
|
|
33
|
+
}
|
|
34
|
+
function errorMessage(error) {
|
|
35
|
+
return error instanceof Error ? error.message : String(error);
|
|
36
|
+
}
|
|
37
|
+
function isAssistantMessage(message) {
|
|
38
|
+
return Boolean(message && typeof message === "object" && message.role === "assistant");
|
|
39
|
+
}
|
|
40
|
+
function numberValue(value) {
|
|
41
|
+
return typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
42
|
+
}
|
|
43
|
+
export function normalizeAssistantUsageEvent(piboSessionId, message) {
|
|
44
|
+
const assistant = message;
|
|
45
|
+
if (!assistant?.usage || typeof assistant.usage !== "object")
|
|
46
|
+
return undefined;
|
|
47
|
+
const usage = assistant.usage;
|
|
48
|
+
const inputTokens = numberValue(usage.inputTokens) ?? numberValue(usage.input);
|
|
49
|
+
const outputTokens = numberValue(usage.outputTokens) ?? numberValue(usage.output);
|
|
50
|
+
const cacheReadTokens = numberValue(usage.cacheRead);
|
|
51
|
+
const cacheWriteTokens = numberValue(usage.cacheWrite);
|
|
52
|
+
const reportedTotal = numberValue(usage.totalTokens);
|
|
53
|
+
const normalizedTotal = reportedTotal ?? [inputTokens, outputTokens, cacheReadTokens, cacheWriteTokens]
|
|
54
|
+
.filter((value) => value !== undefined)
|
|
55
|
+
.reduce((sum, value) => sum + value, 0);
|
|
56
|
+
if (normalizedTotal <= 0 && inputTokens === undefined && outputTokens === undefined && cacheReadTokens === undefined && cacheWriteTokens === undefined)
|
|
57
|
+
return undefined;
|
|
58
|
+
return {
|
|
59
|
+
type: "assistant_usage",
|
|
60
|
+
piboSessionId,
|
|
61
|
+
...(inputTokens !== undefined ? { inputTokens } : {}),
|
|
62
|
+
...(outputTokens !== undefined ? { outputTokens } : {}),
|
|
63
|
+
...(cacheReadTokens !== undefined ? { cacheReadTokens } : {}),
|
|
64
|
+
...(cacheWriteTokens !== undefined ? { cacheWriteTokens } : {}),
|
|
65
|
+
totalTokens: Math.max(0, normalizedTotal),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function stringValue(value) {
|
|
69
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
70
|
+
}
|
|
71
|
+
function parseEmbeddedProviderError(message) {
|
|
72
|
+
const jsonStart = message.indexOf("{");
|
|
73
|
+
if (jsonStart === -1)
|
|
74
|
+
return undefined;
|
|
75
|
+
try {
|
|
76
|
+
const parsed = JSON.parse(message.slice(jsonStart));
|
|
77
|
+
const error = parsed.error;
|
|
78
|
+
if (!error || typeof error !== "object")
|
|
79
|
+
return undefined;
|
|
80
|
+
return {
|
|
81
|
+
providerType: stringValue(error.type),
|
|
82
|
+
providerCode: stringValue(error.code),
|
|
83
|
+
providerParam: stringValue(error.param),
|
|
84
|
+
providerMessage: stringValue(error.message),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function assistantErrorDetails(message, context) {
|
|
92
|
+
const rawError = typeof message.errorMessage === "string" ? message.errorMessage : "";
|
|
93
|
+
const providerError = parseEmbeddedProviderError(rawError);
|
|
94
|
+
const usage = message.usage && typeof message.usage === "object" ? message.usage : undefined;
|
|
95
|
+
const contextTokens = numberValue(usage?.totalTokens);
|
|
96
|
+
const details = normalizeSessionErrorDetails(rawError, {
|
|
97
|
+
...(providerError?.providerCode === "context_length_exceeded" ? { category: "context_overflow", errorClass: "provider_context", code: "context_length_exceeded" } : {}),
|
|
98
|
+
...providerError,
|
|
99
|
+
api: stringValue(message.api),
|
|
100
|
+
provider: stringValue(message.provider),
|
|
101
|
+
model: stringValue(message.model),
|
|
102
|
+
contextWindow: context?.contextWindow,
|
|
103
|
+
contextTokens,
|
|
104
|
+
});
|
|
105
|
+
return Object.values(details).some((value) => value !== undefined) ? details : undefined;
|
|
106
|
+
}
|
|
107
|
+
function formatAssistantError(message, context) {
|
|
108
|
+
const rawError = typeof message.errorMessage === "string" && message.errorMessage.length > 0 ? message.errorMessage : "Assistant message failed.";
|
|
109
|
+
const details = assistantErrorDetails(message, context);
|
|
110
|
+
if (details?.category !== "context_overflow")
|
|
111
|
+
return rawError;
|
|
112
|
+
const lines = ["Context window exceeded while sending the model request."];
|
|
113
|
+
if (details.providerMessage)
|
|
114
|
+
lines.push(`Provider message: ${details.providerMessage}`);
|
|
115
|
+
if (details.providerCode)
|
|
116
|
+
lines.push(`Provider code: ${details.providerCode}`);
|
|
117
|
+
if (details.providerParam)
|
|
118
|
+
lines.push(`Provider param: ${details.providerParam}`);
|
|
119
|
+
if (details.api || details.provider || details.model) {
|
|
120
|
+
lines.push(`Model: ${[details.api, details.provider, details.model].filter(Boolean).join(" / ")}`);
|
|
121
|
+
}
|
|
122
|
+
if (details.contextTokens !== undefined || details.contextWindow !== undefined) {
|
|
123
|
+
const usage = details.contextWindow
|
|
124
|
+
? `${details.contextTokens ?? "unknown"} / ${details.contextWindow} tokens`
|
|
125
|
+
: `${details.contextTokens} tokens`;
|
|
126
|
+
lines.push(`Context usage: ${usage}`);
|
|
127
|
+
}
|
|
128
|
+
lines.push("Automatic overflow compaction will be attempted if auto-compaction is enabled.");
|
|
129
|
+
return lines.join("\n");
|
|
130
|
+
}
|
|
131
|
+
function messageContentIndex(candidate) {
|
|
132
|
+
return typeof candidate.assistantMessageEvent?.contentIndex === "number"
|
|
133
|
+
? candidate.assistantMessageEvent.contentIndex
|
|
134
|
+
: undefined;
|
|
135
|
+
}
|
|
136
|
+
function promptSource(source) {
|
|
137
|
+
return source === "user" || source === "ui" ? "interactive" : "rpc";
|
|
138
|
+
}
|
|
139
|
+
function lastTextPartFromMessage(message) {
|
|
140
|
+
if (!message || typeof message !== "object")
|
|
141
|
+
return undefined;
|
|
142
|
+
const content = message.content;
|
|
143
|
+
if (!Array.isArray(content))
|
|
144
|
+
return undefined;
|
|
145
|
+
for (let index = content.length - 1; index >= 0; index -= 1) {
|
|
146
|
+
const part = content[index];
|
|
147
|
+
if (!part || typeof part !== "object")
|
|
148
|
+
continue;
|
|
149
|
+
const candidate = part;
|
|
150
|
+
if (candidate.type === "text" && typeof candidate.text === "string" && candidate.text.length > 0) {
|
|
151
|
+
return { text: candidate.text, contentIndex: index };
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
function toolCallFromMessage(message, contentIndex) {
|
|
157
|
+
if (!message || typeof message !== "object" || typeof contentIndex !== "number")
|
|
158
|
+
return undefined;
|
|
159
|
+
const content = message.content;
|
|
160
|
+
if (!Array.isArray(content))
|
|
161
|
+
return undefined;
|
|
162
|
+
const candidate = content[contentIndex];
|
|
163
|
+
if (!candidate || typeof candidate !== "object")
|
|
164
|
+
return undefined;
|
|
165
|
+
const toolCall = candidate;
|
|
166
|
+
if (toolCall.type !== "toolCall" || typeof toolCall.id !== "string" || typeof toolCall.name !== "string") {
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
return { id: toolCall.id, name: toolCall.name, args: toolCall.arguments ?? {} };
|
|
170
|
+
}
|
|
171
|
+
function toolCallFromAssistantEvent(candidate) {
|
|
172
|
+
const eventToolCall = candidate.assistantMessageEvent?.toolCall;
|
|
173
|
+
if (eventToolCall && typeof eventToolCall.id === "string" && typeof eventToolCall.name === "string") {
|
|
174
|
+
return { id: eventToolCall.id, name: eventToolCall.name, args: eventToolCall.arguments ?? {} };
|
|
175
|
+
}
|
|
176
|
+
return toolCallFromMessage(candidate.message, candidate.assistantMessageEvent?.contentIndex);
|
|
177
|
+
}
|
|
178
|
+
function normalizeToolCallEvent(piboSessionId, candidate) {
|
|
179
|
+
if (candidate.type === "message_update" &&
|
|
180
|
+
(candidate.assistantMessageEvent?.type === "toolcall_start" ||
|
|
181
|
+
candidate.assistantMessageEvent?.type === "toolcall_end")) {
|
|
182
|
+
const toolCall = toolCallFromAssistantEvent(candidate);
|
|
183
|
+
if (!toolCall)
|
|
184
|
+
return undefined;
|
|
185
|
+
return {
|
|
186
|
+
type: "tool_call",
|
|
187
|
+
piboSessionId,
|
|
188
|
+
toolCallId: toolCall.id,
|
|
189
|
+
toolName: toolCall.name,
|
|
190
|
+
args: toolCall.args,
|
|
191
|
+
argsComplete: candidate.assistantMessageEvent.type === "toolcall_end",
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
return undefined;
|
|
195
|
+
}
|
|
196
|
+
function normalizeToolExecutionEvent(piboSessionId, candidate) {
|
|
197
|
+
if (typeof candidate.toolCallId !== "string" || typeof candidate.toolName !== "string") {
|
|
198
|
+
return undefined;
|
|
199
|
+
}
|
|
200
|
+
if (candidate.type === "tool_execution_start") {
|
|
201
|
+
return {
|
|
202
|
+
type: "tool_execution_started",
|
|
203
|
+
piboSessionId,
|
|
204
|
+
toolCallId: candidate.toolCallId,
|
|
205
|
+
toolName: candidate.toolName,
|
|
206
|
+
args: candidate.args,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
if (candidate.type === "tool_execution_update") {
|
|
210
|
+
return {
|
|
211
|
+
type: "tool_execution_updated",
|
|
212
|
+
piboSessionId,
|
|
213
|
+
toolCallId: candidate.toolCallId,
|
|
214
|
+
toolName: candidate.toolName,
|
|
215
|
+
args: candidate.args,
|
|
216
|
+
partialResult: candidate.partialResult,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
if (candidate.type === "tool_execution_end") {
|
|
220
|
+
return {
|
|
221
|
+
type: "tool_execution_finished",
|
|
222
|
+
piboSessionId,
|
|
223
|
+
toolCallId: candidate.toolCallId,
|
|
224
|
+
toolName: candidate.toolName,
|
|
225
|
+
result: candidate.result,
|
|
226
|
+
isError: candidate.isError === true,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
231
|
+
function normalizeProviderWebSearchEvent(piboSessionId, candidate) {
|
|
232
|
+
const item = recordValue(candidate.item) ?? recordValue(candidate.assistantMessageEvent?.item);
|
|
233
|
+
const itemType = stringValue(item?.type);
|
|
234
|
+
const rawEventType = stringValue(candidate.type) ?? stringValue(candidate.assistantMessageEvent?.type);
|
|
235
|
+
const rawType = rawEventType?.toLowerCase().includes("web_search") ? rawEventType : itemType;
|
|
236
|
+
if (!rawType || !rawType.toLowerCase().includes("web_search"))
|
|
237
|
+
return undefined;
|
|
238
|
+
const action = recordValue(candidate.action) ?? recordValue(candidate.assistantMessageEvent?.action) ?? recordValue(item?.action);
|
|
239
|
+
const status = providerWebSearchStatus(rawEventType ?? rawType, candidate.status ?? candidate.assistantMessageEvent?.status ?? item?.status);
|
|
240
|
+
if (!status)
|
|
241
|
+
return undefined;
|
|
242
|
+
const rawId = stringValue(candidate.toolCallId) ??
|
|
243
|
+
stringValue(candidate.item_id) ??
|
|
244
|
+
stringValue(item?.id) ??
|
|
245
|
+
stringValue(candidate.assistantMessageEvent?.toolCall?.id) ??
|
|
246
|
+
(typeof candidate.assistantMessageEvent?.contentIndex === "number"
|
|
247
|
+
? `content-${candidate.assistantMessageEvent.contentIndex}`
|
|
248
|
+
: undefined) ??
|
|
249
|
+
"active";
|
|
250
|
+
const toolCallId = rawId.startsWith("provider:web_search:") ? rawId : `provider:web_search:${rawId}`;
|
|
251
|
+
const query = stringValue(candidate.query) ??
|
|
252
|
+
stringValue(candidate.assistantMessageEvent?.query) ??
|
|
253
|
+
stringValue(action?.query) ??
|
|
254
|
+
stringValue(item?.query) ??
|
|
255
|
+
stringValue(item?.search_query);
|
|
256
|
+
const sources = firstArray(candidate.sources, candidate.assistantMessageEvent?.sources, item?.sources, item?.results, item?.citations);
|
|
257
|
+
const args = { providerTool: "web_search", ...(query ? { query } : {}) };
|
|
258
|
+
if (status === "running") {
|
|
259
|
+
return {
|
|
260
|
+
type: "tool_execution_started",
|
|
261
|
+
piboSessionId,
|
|
262
|
+
toolCallId,
|
|
263
|
+
toolName: "web_search",
|
|
264
|
+
args,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
const error = candidate.error ?? candidate.assistantMessageEvent?.error ?? item?.error;
|
|
268
|
+
return {
|
|
269
|
+
type: "tool_execution_finished",
|
|
270
|
+
piboSessionId,
|
|
271
|
+
toolCallId,
|
|
272
|
+
toolName: "web_search",
|
|
273
|
+
result: status === "error"
|
|
274
|
+
? (error ?? "Web search failed")
|
|
275
|
+
: {
|
|
276
|
+
...(query ? { query } : {}),
|
|
277
|
+
...(sources ? { sources, sourceCount: sources.length } : {}),
|
|
278
|
+
},
|
|
279
|
+
isError: status === "error",
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
function providerWebSearchStatus(rawType, rawStatus) {
|
|
283
|
+
const type = rawType.toLowerCase();
|
|
284
|
+
const status = stringValue(rawStatus)?.toLowerCase();
|
|
285
|
+
if (type.includes("failed") || type.includes("error") || status === "failed" || status === "error")
|
|
286
|
+
return "error";
|
|
287
|
+
if (type.includes("completed") || type.includes("done") || type.includes("end") || status === "completed" || status === "done")
|
|
288
|
+
return "done";
|
|
289
|
+
if (type.includes("started") || type.includes("added") || type.includes("in_progress") || type.includes("searching") || status === "in_progress" || status === "running" || status === "searching")
|
|
290
|
+
return "running";
|
|
291
|
+
return undefined;
|
|
292
|
+
}
|
|
293
|
+
function recordValue(value) {
|
|
294
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
295
|
+
}
|
|
296
|
+
function firstArray(...values) {
|
|
297
|
+
for (const value of values) {
|
|
298
|
+
if (Array.isArray(value))
|
|
299
|
+
return value;
|
|
300
|
+
}
|
|
301
|
+
return undefined;
|
|
302
|
+
}
|
|
303
|
+
export function normalizePiEvent(piboSessionId, event, context) {
|
|
304
|
+
if (!event || typeof event !== "object")
|
|
305
|
+
return undefined;
|
|
306
|
+
const candidate = event;
|
|
307
|
+
const providerToolEvent = normalizeProviderWebSearchEvent(piboSessionId, candidate);
|
|
308
|
+
if (providerToolEvent)
|
|
309
|
+
return providerToolEvent;
|
|
310
|
+
if (candidate.type === "message_update" &&
|
|
311
|
+
candidate.assistantMessageEvent?.type === "text_delta" &&
|
|
312
|
+
typeof candidate.assistantMessageEvent.delta === "string") {
|
|
313
|
+
return {
|
|
314
|
+
type: "assistant_delta",
|
|
315
|
+
piboSessionId,
|
|
316
|
+
contentIndex: messageContentIndex(candidate),
|
|
317
|
+
text: candidate.assistantMessageEvent.delta,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
if (candidate.type === "message_update" &&
|
|
321
|
+
candidate.assistantMessageEvent?.type === "thinking_start") {
|
|
322
|
+
return { type: "thinking_started", piboSessionId, contentIndex: messageContentIndex(candidate) };
|
|
323
|
+
}
|
|
324
|
+
if (candidate.type === "message_update" &&
|
|
325
|
+
candidate.assistantMessageEvent?.type === "thinking_delta" &&
|
|
326
|
+
typeof candidate.assistantMessageEvent.delta === "string") {
|
|
327
|
+
return { type: "thinking_delta", piboSessionId, contentIndex: messageContentIndex(candidate), text: candidate.assistantMessageEvent.delta };
|
|
328
|
+
}
|
|
329
|
+
if (candidate.type === "message_update" && candidate.assistantMessageEvent?.type === "thinking_end") {
|
|
330
|
+
const text = typeof candidate.assistantMessageEvent.content === "string" ? candidate.assistantMessageEvent.content : undefined;
|
|
331
|
+
return text === undefined
|
|
332
|
+
? { type: "thinking_finished", piboSessionId, contentIndex: messageContentIndex(candidate) }
|
|
333
|
+
: { type: "thinking_finished", piboSessionId, contentIndex: messageContentIndex(candidate), text };
|
|
334
|
+
}
|
|
335
|
+
const toolCallEvent = normalizeToolCallEvent(piboSessionId, candidate);
|
|
336
|
+
if (toolCallEvent)
|
|
337
|
+
return toolCallEvent;
|
|
338
|
+
const toolExecutionEvent = normalizeToolExecutionEvent(piboSessionId, candidate);
|
|
339
|
+
if (toolExecutionEvent)
|
|
340
|
+
return toolExecutionEvent;
|
|
341
|
+
if (candidate.type === "message_end") {
|
|
342
|
+
const message = candidate.message;
|
|
343
|
+
const role = message?.role;
|
|
344
|
+
if (role === "assistant") {
|
|
345
|
+
if (message?.stopReason === "error" || typeof message?.errorMessage === "string") {
|
|
346
|
+
return {
|
|
347
|
+
type: "session_error",
|
|
348
|
+
piboSessionId,
|
|
349
|
+
error: formatAssistantError(message, context),
|
|
350
|
+
errorDetails: assistantErrorDetails(message, context),
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
const textPart = lastTextPartFromMessage(candidate.message);
|
|
354
|
+
if (textPart) {
|
|
355
|
+
return {
|
|
356
|
+
type: "assistant_message",
|
|
357
|
+
piboSessionId,
|
|
358
|
+
contentIndex: textPart.contentIndex,
|
|
359
|
+
text: textPart.text,
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return undefined;
|
|
365
|
+
}
|
|
366
|
+
function estimateContextTokens(messages) {
|
|
367
|
+
let chars = 0;
|
|
368
|
+
for (const msg of messages) {
|
|
369
|
+
if (!msg || typeof msg !== "object")
|
|
370
|
+
continue;
|
|
371
|
+
const message = msg;
|
|
372
|
+
if (message.role === "user") {
|
|
373
|
+
const content = message.content;
|
|
374
|
+
if (typeof content === "string") {
|
|
375
|
+
chars += content.length;
|
|
376
|
+
}
|
|
377
|
+
else if (Array.isArray(content)) {
|
|
378
|
+
for (const block of content) {
|
|
379
|
+
if (block && typeof block === "object" && "type" in block && block.type === "text" && "text" in block && typeof block.text === "string") {
|
|
380
|
+
chars += block.text.length;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
else if (message.role === "assistant") {
|
|
386
|
+
const content = message.content;
|
|
387
|
+
if (Array.isArray(content)) {
|
|
388
|
+
for (const block of content) {
|
|
389
|
+
if (!block || typeof block !== "object")
|
|
390
|
+
continue;
|
|
391
|
+
if ("type" in block) {
|
|
392
|
+
if (block.type === "text" && "text" in block && typeof block.text === "string")
|
|
393
|
+
chars += block.text.length;
|
|
394
|
+
if (block.type === "thinking" && "thinking" in block && typeof block.thinking === "string")
|
|
395
|
+
chars += block.thinking.length;
|
|
396
|
+
if (block.type === "toolCall" && "name" in block && "arguments" in block) {
|
|
397
|
+
chars += String(block.name).length + JSON.stringify(block.arguments).length;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
else if (message.role === "toolResult" || message.role === "custom") {
|
|
404
|
+
const content = message.content;
|
|
405
|
+
if (typeof content === "string") {
|
|
406
|
+
chars += content.length;
|
|
407
|
+
}
|
|
408
|
+
else if (Array.isArray(content)) {
|
|
409
|
+
for (const block of content) {
|
|
410
|
+
if (block && typeof block === "object" && "type" in block && block.type === "text" && "text" in block && typeof block.text === "string") {
|
|
411
|
+
chars += block.text.length;
|
|
412
|
+
}
|
|
413
|
+
if (block && typeof block === "object" && "type" in block && block.type === "image") {
|
|
414
|
+
chars += 4800; // approximate image size
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return Math.ceil(chars / 4);
|
|
421
|
+
}
|
|
422
|
+
export class RoutedSession {
|
|
423
|
+
piboSessionId;
|
|
424
|
+
runtime;
|
|
425
|
+
emit;
|
|
426
|
+
pluginRegistry;
|
|
427
|
+
forwardPiEvents;
|
|
428
|
+
onPiEventTelemetry;
|
|
429
|
+
onSessionOperation;
|
|
430
|
+
onKillChildren;
|
|
431
|
+
onStateChange;
|
|
432
|
+
onMessagesInterrupted;
|
|
433
|
+
messagePreflight;
|
|
434
|
+
queue = [];
|
|
435
|
+
processing = false;
|
|
436
|
+
disposed = false;
|
|
437
|
+
disposePromise;
|
|
438
|
+
runtimeDisposePromise;
|
|
439
|
+
forceDisposalStarted = false;
|
|
440
|
+
drainPromise;
|
|
441
|
+
fastMode = false;
|
|
442
|
+
fastModePatchedAgents = new WeakSet();
|
|
443
|
+
activeMessage;
|
|
444
|
+
activeExecutionEvent;
|
|
445
|
+
activeAssistantIndex;
|
|
446
|
+
nextAssistantIndex = 0;
|
|
447
|
+
activeThinkingIndex;
|
|
448
|
+
nextThinkingIndex = 0;
|
|
449
|
+
pendingAssistantError;
|
|
450
|
+
pendingAssistantErrorRetryable = false;
|
|
451
|
+
activeMessageFailed = false;
|
|
452
|
+
providerRecoveryCancelled = false;
|
|
453
|
+
providerRecoveryAbortController;
|
|
454
|
+
activeCapabilityScope;
|
|
455
|
+
unsubscribe;
|
|
456
|
+
recoverySession;
|
|
457
|
+
isContinuePatched = false;
|
|
458
|
+
compatibilityAuthController;
|
|
459
|
+
constructor(piboSessionId, runtime, emit, pluginRegistry, forwardPiEvents, onPiEventTelemetry, initialFastMode, onSessionOperation, onKillChildren, onStateChange, onMessagesInterrupted, messagePreflight) {
|
|
460
|
+
this.piboSessionId = piboSessionId;
|
|
461
|
+
this.runtime = runtime;
|
|
462
|
+
this.emit = emit;
|
|
463
|
+
this.pluginRegistry = pluginRegistry;
|
|
464
|
+
this.forwardPiEvents = forwardPiEvents;
|
|
465
|
+
this.onPiEventTelemetry = onPiEventTelemetry;
|
|
466
|
+
this.onSessionOperation = onSessionOperation;
|
|
467
|
+
this.onKillChildren = onKillChildren;
|
|
468
|
+
this.onStateChange = onStateChange;
|
|
469
|
+
this.onMessagesInterrupted = onMessagesInterrupted;
|
|
470
|
+
this.messagePreflight = messagePreflight;
|
|
471
|
+
this.fastMode = initialFastMode && this.fastModeSupported();
|
|
472
|
+
this.bindRuntimeSession();
|
|
473
|
+
this.patchFastModeProviderRequest();
|
|
474
|
+
this.patchAgentContinue();
|
|
475
|
+
this.runtime.setRebindSession(async () => {
|
|
476
|
+
this.bindRuntimeSession();
|
|
477
|
+
this.patchFastModeProviderRequest();
|
|
478
|
+
this.patchAgentContinue();
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
patchFastModeProviderRequest() {
|
|
482
|
+
const agent = this.runtime.session.agent;
|
|
483
|
+
if (!agent || typeof agent !== "object")
|
|
484
|
+
return;
|
|
485
|
+
if (this.fastModePatchedAgents.has(agent))
|
|
486
|
+
return;
|
|
487
|
+
this.fastModePatchedAgents.add(agent);
|
|
488
|
+
const originalStreamFn = typeof agent.streamFn === "function" ? agent.streamFn.bind(agent) : undefined;
|
|
489
|
+
if (originalStreamFn) {
|
|
490
|
+
agent.streamFn = (model, context, options) => {
|
|
491
|
+
const nextOptions = this.shouldUseFastServiceTier(model) ? withFastServiceTierOption(options) : options;
|
|
492
|
+
return originalStreamFn(model, context, nextOptions);
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
const originalOnPayload = agent.onPayload?.bind(agent);
|
|
496
|
+
agent.onPayload = async (payload, model) => {
|
|
497
|
+
const useFastTier = this.shouldUseFastServiceTier(model);
|
|
498
|
+
const payloadForHooks = useFastTier ? withFastServiceTier(payload) : payload;
|
|
499
|
+
const nextPayload = originalOnPayload ? await originalOnPayload(payloadForHooks, model) : payloadForHooks;
|
|
500
|
+
return useFastTier ? withFastServiceTier(nextPayload) : nextPayload;
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
shouldUseFastServiceTier(model) {
|
|
504
|
+
if (this.getFastModeResult().mode !== "fast")
|
|
505
|
+
return false;
|
|
506
|
+
return modelSupportsFastServiceTier(model) || modelSupportsFastServiceTier(this.runtime.session.model);
|
|
507
|
+
}
|
|
508
|
+
patchAgentContinue() {
|
|
509
|
+
if (this.isContinuePatched)
|
|
510
|
+
return;
|
|
511
|
+
this.isContinuePatched = true;
|
|
512
|
+
const agent = this.runtime.session.agent;
|
|
513
|
+
if (!agent)
|
|
514
|
+
return;
|
|
515
|
+
const originalContinue = agent.continue.bind(agent);
|
|
516
|
+
const session = this.runtime.session;
|
|
517
|
+
agent.continue = async () => {
|
|
518
|
+
const model = session.model;
|
|
519
|
+
if (!model)
|
|
520
|
+
return originalContinue();
|
|
521
|
+
const contextWindow = model.contextWindow ?? 0;
|
|
522
|
+
const settings = session.settingsManager.getCompactionSettings();
|
|
523
|
+
if (!settings.enabled)
|
|
524
|
+
return originalContinue();
|
|
525
|
+
let contextTokens = null;
|
|
526
|
+
const contextUsage = session.getContextUsage();
|
|
527
|
+
if (contextUsage && contextUsage.tokens !== null) {
|
|
528
|
+
contextTokens = contextUsage.tokens;
|
|
529
|
+
}
|
|
530
|
+
else {
|
|
531
|
+
// Fallback: estimate tokens from current messages when no post-compaction
|
|
532
|
+
// assistant usage is available yet. This prevents context overflow between
|
|
533
|
+
// tool calls after a compaction.
|
|
534
|
+
contextTokens = estimateContextTokens(session.messages);
|
|
535
|
+
}
|
|
536
|
+
if (contextTokens === null)
|
|
537
|
+
return originalContinue();
|
|
538
|
+
if (shouldCompact(contextTokens, contextWindow, settings)) {
|
|
539
|
+
await session.compact();
|
|
540
|
+
}
|
|
541
|
+
return originalContinue();
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
bindRuntimeSession() {
|
|
545
|
+
this.unsubscribe?.();
|
|
546
|
+
const session = this.runtime.session;
|
|
547
|
+
if (this.recoverySession && this.recoverySession !== session) {
|
|
548
|
+
this.cancelProviderRecovery();
|
|
549
|
+
cancelPiboAssistantContextGuardRecovery(this.recoverySession, new Error("Context guard recovery cancelled because the Pi session changed"));
|
|
550
|
+
}
|
|
551
|
+
this.recoverySession = session;
|
|
552
|
+
claimPiboAssistantContextGuardRecovery(session);
|
|
553
|
+
this.unsubscribe = session.subscribe((event) => {
|
|
554
|
+
this.onPiEventTelemetry?.(this.piboSessionId, event, { status: this.getStatus(), activeEventId: this.activeMessage?.id ?? this.activeExecutionEvent?.id });
|
|
555
|
+
const model = this.runtime.session.model;
|
|
556
|
+
const normalized = normalizePiEvent(this.piboSessionId, event, { contextWindow: numberValue(model?.contextWindow) });
|
|
557
|
+
const candidate = event && typeof event === "object" ? event : undefined;
|
|
558
|
+
const assistantMessageEnded = candidate?.type === "message_end" && isAssistantMessage(candidate.message);
|
|
559
|
+
const usageEvent = assistantMessageEnded ? normalizeAssistantUsageEvent(this.piboSessionId, candidate?.message) : undefined;
|
|
560
|
+
if (usageEvent)
|
|
561
|
+
this.emit(this.withActiveMessage(usageEvent));
|
|
562
|
+
// Pi gets the first chance to recover through its short retry/compaction loop.
|
|
563
|
+
// Keep the final error pending so the routed turn can continue durable recovery.
|
|
564
|
+
if (assistantMessageEnded && normalized?.type === "session_error") {
|
|
565
|
+
this.pendingAssistantError = this.withActiveMessage(normalized);
|
|
566
|
+
this.pendingAssistantErrorRetryable = isRetryablePiboAssistantError(candidate.message);
|
|
567
|
+
}
|
|
568
|
+
else {
|
|
569
|
+
if (assistantMessageEnded) {
|
|
570
|
+
this.pendingAssistantError = undefined;
|
|
571
|
+
this.pendingAssistantErrorRetryable = false;
|
|
572
|
+
}
|
|
573
|
+
if (normalized)
|
|
574
|
+
this.emit(this.withActiveMessage(normalized));
|
|
575
|
+
}
|
|
576
|
+
if (this.forwardPiEvents) {
|
|
577
|
+
this.emit({ type: "pi_event", piboSessionId: this.piboSessionId, event });
|
|
578
|
+
}
|
|
579
|
+
this.handleCompactionEvent(event);
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
flushPendingAssistantError() {
|
|
583
|
+
if (!this.pendingAssistantError)
|
|
584
|
+
return;
|
|
585
|
+
this.activeMessageFailed = true;
|
|
586
|
+
this.emit(this.pendingAssistantError);
|
|
587
|
+
this.pendingAssistantError = undefined;
|
|
588
|
+
this.pendingAssistantErrorRetryable = false;
|
|
589
|
+
}
|
|
590
|
+
takePendingAssistantError() {
|
|
591
|
+
const pending = this.pendingAssistantError;
|
|
592
|
+
this.pendingAssistantError = undefined;
|
|
593
|
+
this.pendingAssistantErrorRetryable = false;
|
|
594
|
+
return pending;
|
|
595
|
+
}
|
|
596
|
+
cancelProviderRecovery() {
|
|
597
|
+
this.providerRecoveryCancelled = true;
|
|
598
|
+
this.providerRecoveryAbortController?.abort();
|
|
599
|
+
this.providerRecoveryAbortController = undefined;
|
|
600
|
+
}
|
|
601
|
+
async waitForPiAgentSettlement(session) {
|
|
602
|
+
const waitForIdle = session.waitForIdle;
|
|
603
|
+
if (waitForIdle)
|
|
604
|
+
await waitForIdle.call(session);
|
|
605
|
+
}
|
|
606
|
+
async resumeTranscriptIntegrityRecovery(session) {
|
|
607
|
+
const reports = claimPiboTranscriptIntegrityContinuation(session);
|
|
608
|
+
if (reports.length === 0)
|
|
609
|
+
return;
|
|
610
|
+
try {
|
|
611
|
+
this.pendingAssistantError = undefined;
|
|
612
|
+
this.pendingAssistantErrorRetryable = false;
|
|
613
|
+
await session.sendCustomMessage({
|
|
614
|
+
customType: PIBO_TRANSCRIPT_INTEGRITY_RESUME_MESSAGE_TYPE,
|
|
615
|
+
content: [{ type: "text", text: PIBO_TRANSCRIPT_INTEGRITY_RESUME_PROMPT }],
|
|
616
|
+
display: false,
|
|
617
|
+
details: { repairIds: reports.map((report) => report.repairId) },
|
|
618
|
+
}, { triggerTurn: true });
|
|
619
|
+
await this.waitForPiAgentSettlement(session);
|
|
620
|
+
await this.resumeContextGuardRecovery(session);
|
|
621
|
+
const pendingError = this.takePendingAssistantError();
|
|
622
|
+
if (pendingError) {
|
|
623
|
+
throw new PiboTranscriptIntegrityError(`Transcript integrity continuation failed: ${pendingError.error}`);
|
|
624
|
+
}
|
|
625
|
+
settlePiboTranscriptIntegrityContinuation(session, "completed");
|
|
626
|
+
}
|
|
627
|
+
catch (error) {
|
|
628
|
+
settlePiboTranscriptIntegrityContinuation(session, "failed", error);
|
|
629
|
+
throw error instanceof PiboTranscriptIntegrityError
|
|
630
|
+
? error
|
|
631
|
+
: new PiboTranscriptIntegrityError(`Transcript integrity continuation failed: ${errorMessage(error)}`);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
async resumeContextGuardRecovery(session) {
|
|
635
|
+
while (await waitForPiboAssistantContextGuardRecovery(session)) {
|
|
636
|
+
try {
|
|
637
|
+
await session.sendCustomMessage({
|
|
638
|
+
customType: PIBO_CONTEXT_GUARD_RESUME_MESSAGE_TYPE,
|
|
639
|
+
content: [{ type: "text", text: PIBO_CONTEXT_GUARD_RESUME_PROMPT }],
|
|
640
|
+
display: false,
|
|
641
|
+
}, { triggerTurn: true });
|
|
642
|
+
await this.waitForPiAgentSettlement(session);
|
|
643
|
+
}
|
|
644
|
+
catch (error) {
|
|
645
|
+
const resumeError = error instanceof Error ? error : new Error(String(error));
|
|
646
|
+
cancelPiboAssistantContextGuardRecovery(session, resumeError);
|
|
647
|
+
throw resumeError;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
async recoverTransientProviderErrors(session) {
|
|
652
|
+
let attempt = 0;
|
|
653
|
+
while (this.pendingAssistantError && this.pendingAssistantErrorRetryable) {
|
|
654
|
+
if (this.providerRecoveryCancelled)
|
|
655
|
+
throw new PiboProviderRecoveryCancelledError();
|
|
656
|
+
const settings = resolvePiboProviderRecoverySettings(session.settingsManager);
|
|
657
|
+
if (!settings.enabled)
|
|
658
|
+
return;
|
|
659
|
+
attempt += 1;
|
|
660
|
+
this.pendingAssistantError = undefined;
|
|
661
|
+
this.pendingAssistantErrorRetryable = false;
|
|
662
|
+
const controller = new AbortController();
|
|
663
|
+
this.providerRecoveryAbortController = controller;
|
|
664
|
+
try {
|
|
665
|
+
await waitForPiboProviderRecovery(attempt, settings, controller.signal);
|
|
666
|
+
}
|
|
667
|
+
finally {
|
|
668
|
+
if (this.providerRecoveryAbortController === controller) {
|
|
669
|
+
this.providerRecoveryAbortController = undefined;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
if (this.providerRecoveryCancelled || this.disposed || this.runtime.session !== session || !this.activeMessage) {
|
|
673
|
+
throw new PiboProviderRecoveryCancelledError();
|
|
674
|
+
}
|
|
675
|
+
try {
|
|
676
|
+
await session.sendCustomMessage({
|
|
677
|
+
customType: PIBO_PROVIDER_RECOVERY_MESSAGE_TYPE,
|
|
678
|
+
content: [{ type: "text", text: PIBO_PROVIDER_RECOVERY_PROMPT }],
|
|
679
|
+
display: false,
|
|
680
|
+
details: { attempt },
|
|
681
|
+
}, { triggerTurn: true });
|
|
682
|
+
await this.resumeContextGuardRecovery(session);
|
|
683
|
+
if (this.providerRecoveryCancelled)
|
|
684
|
+
throw new PiboProviderRecoveryCancelledError();
|
|
685
|
+
}
|
|
686
|
+
catch (error) {
|
|
687
|
+
if (error instanceof PiboProviderRecoveryCancelledError)
|
|
688
|
+
throw error;
|
|
689
|
+
if (!isRetryablePiboProviderError(error))
|
|
690
|
+
throw error;
|
|
691
|
+
const message = errorMessage(error);
|
|
692
|
+
this.pendingAssistantError = {
|
|
693
|
+
type: "session_error",
|
|
694
|
+
piboSessionId: this.piboSessionId,
|
|
695
|
+
eventId: this.activeMessage.id,
|
|
696
|
+
error: message,
|
|
697
|
+
errorDetails: runtimeSessionErrorDetails(message),
|
|
698
|
+
};
|
|
699
|
+
this.pendingAssistantErrorRetryable = true;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
handleCompactionEvent(event) {
|
|
704
|
+
if (!event || typeof event !== "object")
|
|
705
|
+
return;
|
|
706
|
+
const candidate = event;
|
|
707
|
+
const eventId = this.activeMessage?.id ?? this.activeExecutionEvent?.id;
|
|
708
|
+
if (candidate.type === "compaction_start") {
|
|
709
|
+
this.emit(this.withActiveMessage({
|
|
710
|
+
type: "compaction_start",
|
|
711
|
+
piboSessionId: this.piboSessionId,
|
|
712
|
+
eventId,
|
|
713
|
+
reason: typeof candidate.reason === "string" ? candidate.reason : "unknown",
|
|
714
|
+
}));
|
|
715
|
+
}
|
|
716
|
+
if (candidate.type === "compaction_end") {
|
|
717
|
+
if (candidate.result && candidate.aborted !== true) {
|
|
718
|
+
// Reset assistant message indices so the next assistant response starts
|
|
719
|
+
// fresh after compaction, matching the reduced agent context.
|
|
720
|
+
this.activeAssistantIndex = undefined;
|
|
721
|
+
this.nextAssistantIndex = 0;
|
|
722
|
+
this.activeThinkingIndex = undefined;
|
|
723
|
+
this.nextThinkingIndex = 0;
|
|
724
|
+
}
|
|
725
|
+
this.emit(this.withActiveMessage({
|
|
726
|
+
type: "compaction_end",
|
|
727
|
+
piboSessionId: this.piboSessionId,
|
|
728
|
+
eventId,
|
|
729
|
+
reason: typeof candidate.reason === "string" ? candidate.reason : "unknown",
|
|
730
|
+
result: candidate.result,
|
|
731
|
+
aborted: candidate.aborted === true,
|
|
732
|
+
errorMessage: typeof candidate.errorMessage === "string" ? candidate.errorMessage : undefined,
|
|
733
|
+
}));
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
enqueueMessage(event) {
|
|
737
|
+
this.assertActive();
|
|
738
|
+
this.queue.push({ kind: "message", event });
|
|
739
|
+
const output = {
|
|
740
|
+
type: "message_queued",
|
|
741
|
+
piboSessionId: this.piboSessionId,
|
|
742
|
+
eventId: event.id,
|
|
743
|
+
queuedMessages: this.queue.length,
|
|
744
|
+
text: event.text,
|
|
745
|
+
source: event.source,
|
|
746
|
+
provenance: event.provenance,
|
|
747
|
+
};
|
|
748
|
+
this.emit(output);
|
|
749
|
+
this.onStateChange?.({ processing: this.processing, queuedMessages: this.queue.length, disposed: this.disposed });
|
|
750
|
+
this.startDrain();
|
|
751
|
+
return output;
|
|
752
|
+
}
|
|
753
|
+
async steerMessage(event) {
|
|
754
|
+
this.assertActive();
|
|
755
|
+
const activeMessage = this.activeMessage;
|
|
756
|
+
if (!activeMessage || !this.processing || !this.runtime.session.isStreaming) {
|
|
757
|
+
throw new PiboSteeringUnavailableError();
|
|
758
|
+
}
|
|
759
|
+
const session = this.runtime.session;
|
|
760
|
+
const expandedText = expandInlineSkills(event.text, session.resourceLoader.getSkills().skills);
|
|
761
|
+
try {
|
|
762
|
+
await session.steer(expandedText);
|
|
763
|
+
}
|
|
764
|
+
catch (error) {
|
|
765
|
+
throw new PiboSteeringUnavailableError(`The active session could not accept steering: ${errorMessage(error)}`, { cause: error });
|
|
766
|
+
}
|
|
767
|
+
const output = {
|
|
768
|
+
type: "message_steered",
|
|
769
|
+
piboSessionId: this.piboSessionId,
|
|
770
|
+
eventId: event.id,
|
|
771
|
+
activeEventId: activeMessage.id,
|
|
772
|
+
text: event.text,
|
|
773
|
+
source: event.source,
|
|
774
|
+
};
|
|
775
|
+
this.emit(output);
|
|
776
|
+
return output;
|
|
777
|
+
}
|
|
778
|
+
async executeAction(event) {
|
|
779
|
+
this.assertActive();
|
|
780
|
+
if (event.action === "compact") {
|
|
781
|
+
return this.enqueueCompactAction(event);
|
|
782
|
+
}
|
|
783
|
+
const result = await this.runAction(event);
|
|
784
|
+
if (isSessionOperationResult(result))
|
|
785
|
+
await this.onSessionOperation?.(result, event);
|
|
786
|
+
const output = {
|
|
787
|
+
type: "execution_result",
|
|
788
|
+
piboSessionId: this.piboSessionId,
|
|
789
|
+
eventId: event.id,
|
|
790
|
+
action: event.action,
|
|
791
|
+
result,
|
|
792
|
+
};
|
|
793
|
+
this.emit(output);
|
|
794
|
+
return output;
|
|
795
|
+
}
|
|
796
|
+
getActiveMessage() {
|
|
797
|
+
if (!this.activeMessage)
|
|
798
|
+
return undefined;
|
|
799
|
+
return { id: this.activeMessage.id, source: this.activeMessage.source, provenance: this.activeMessage.provenance };
|
|
800
|
+
}
|
|
801
|
+
getStatus() {
|
|
802
|
+
const enabledTools = this.runtime.session.getActiveToolNames();
|
|
803
|
+
const thinkingLevel = this.runtime.session.thinkingLevel;
|
|
804
|
+
const settingsManager = this.runtime.session.settingsManager;
|
|
805
|
+
return {
|
|
806
|
+
piboSessionId: this.piboSessionId,
|
|
807
|
+
queuedMessages: this.queue.length,
|
|
808
|
+
processing: this.disposed ? false : this.processing,
|
|
809
|
+
streaming: this.disposed ? false : this.runtime.session.isStreaming,
|
|
810
|
+
activeTools: enabledTools,
|
|
811
|
+
enabledTools,
|
|
812
|
+
cwd: this.runtime.cwd,
|
|
813
|
+
disposed: this.disposed,
|
|
814
|
+
thinkingLevel,
|
|
815
|
+
fastMode: this.getFastModeResult().mode === "fast",
|
|
816
|
+
...(settingsManager ? {
|
|
817
|
+
retry: {
|
|
818
|
+
...settingsManager.getRetrySettings(),
|
|
819
|
+
provider: settingsManager.getProviderRetrySettings(),
|
|
820
|
+
},
|
|
821
|
+
} : {}),
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
async getStatusSnapshot() {
|
|
825
|
+
const contextUsage = this.getContextUsage();
|
|
826
|
+
const providerUsage = await this.getProviderUsage();
|
|
827
|
+
return {
|
|
828
|
+
...this.getStatus(),
|
|
829
|
+
activeModel: this.getActiveModel(),
|
|
830
|
+
contextUsage: contextUsage
|
|
831
|
+
? {
|
|
832
|
+
tokens: contextUsage.tokens ?? undefined,
|
|
833
|
+
contextWindow: contextUsage.contextWindow ?? undefined,
|
|
834
|
+
percent: contextUsage.percent ?? undefined,
|
|
835
|
+
}
|
|
836
|
+
: contextUsage,
|
|
837
|
+
...(providerUsage ? { providerUsage } : {}),
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
getContextUsage() {
|
|
841
|
+
return this.runtime.session.getContextUsage();
|
|
842
|
+
}
|
|
843
|
+
getActiveModel() {
|
|
844
|
+
const model = this.runtime.session.model;
|
|
845
|
+
return model ? { provider: model.provider, id: model.id } : undefined;
|
|
846
|
+
}
|
|
847
|
+
async getProviderUsage() {
|
|
848
|
+
try {
|
|
849
|
+
return await getOpenAiCodexProviderUsageForActiveModel(this.getActiveModel());
|
|
850
|
+
}
|
|
851
|
+
catch {
|
|
852
|
+
return undefined;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
removeQueuedMessages(predicate) {
|
|
856
|
+
this.assertActive();
|
|
857
|
+
const removedMessages = [];
|
|
858
|
+
for (let index = this.queue.length - 1; index >= 0; index -= 1) {
|
|
859
|
+
const item = this.queue[index];
|
|
860
|
+
if (item.kind !== "message" || !predicate(item.event))
|
|
861
|
+
continue;
|
|
862
|
+
this.queue.splice(index, 1);
|
|
863
|
+
removedMessages.push(item.event);
|
|
864
|
+
}
|
|
865
|
+
removedMessages.reverse();
|
|
866
|
+
this.notifyMessagesInterrupted(removedMessages, "queued message removed");
|
|
867
|
+
return removedMessages.length;
|
|
868
|
+
}
|
|
869
|
+
restoreMessageCapabilityScope() {
|
|
870
|
+
const active = this.activeCapabilityScope;
|
|
871
|
+
if (!active)
|
|
872
|
+
return;
|
|
873
|
+
this.activeCapabilityScope = undefined;
|
|
874
|
+
active.session.setActiveToolsByName(active.restoreToolNames);
|
|
875
|
+
}
|
|
876
|
+
getCurrentSession() {
|
|
877
|
+
return this.createSessionSnapshot();
|
|
878
|
+
}
|
|
879
|
+
async listSessions() {
|
|
880
|
+
const manager = this.runtime.session.sessionManager;
|
|
881
|
+
const sessions = await SessionManager.list(this.runtime.cwd, manager.getSessionDir());
|
|
882
|
+
return sessions.map((session) => ({
|
|
883
|
+
path: session.path,
|
|
884
|
+
id: session.id,
|
|
885
|
+
cwd: session.cwd,
|
|
886
|
+
name: session.name,
|
|
887
|
+
parentSessionPath: session.parentSessionPath,
|
|
888
|
+
created: session.created.toISOString(),
|
|
889
|
+
modified: session.modified.toISOString(),
|
|
890
|
+
messageCount: session.messageCount,
|
|
891
|
+
firstMessage: session.firstMessage,
|
|
892
|
+
}));
|
|
893
|
+
}
|
|
894
|
+
getForkCandidates() {
|
|
895
|
+
return this.runtime.session.getUserMessagesForForking();
|
|
896
|
+
}
|
|
897
|
+
async forkSession(entryId) {
|
|
898
|
+
this.assertActive();
|
|
899
|
+
const previous = this.createSessionSnapshot();
|
|
900
|
+
const result = await this.runtime.fork(entryId);
|
|
901
|
+
return {
|
|
902
|
+
piboSessionId: this.piboSessionId,
|
|
903
|
+
previous,
|
|
904
|
+
current: this.createSessionSnapshot(),
|
|
905
|
+
cancelled: result.cancelled,
|
|
906
|
+
selectedText: result.selectedText,
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
async cloneSession() {
|
|
910
|
+
this.assertActive();
|
|
911
|
+
const leafId = this.runtime.session.sessionManager.getLeafId();
|
|
912
|
+
if (!leafId) {
|
|
913
|
+
throw new Error("Cannot clone session: no current entry selected");
|
|
914
|
+
}
|
|
915
|
+
const previous = this.createSessionSnapshot();
|
|
916
|
+
const result = await this.runtime.fork(leafId, { position: "at" });
|
|
917
|
+
return {
|
|
918
|
+
piboSessionId: this.piboSessionId,
|
|
919
|
+
previous,
|
|
920
|
+
current: this.createSessionSnapshot(),
|
|
921
|
+
cancelled: result.cancelled,
|
|
922
|
+
};
|
|
923
|
+
}
|
|
924
|
+
getSessionTree() {
|
|
925
|
+
this.assertActive();
|
|
926
|
+
return {
|
|
927
|
+
current: this.createSessionSnapshot(),
|
|
928
|
+
tree: normalizeSessionTree(this.runtime.session.sessionManager.getTree()),
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
async navigateSessionTree(params) {
|
|
932
|
+
this.assertActive();
|
|
933
|
+
const previous = this.createSessionSnapshot();
|
|
934
|
+
const result = await this.runtime.session.navigateTree(params.entryId, {
|
|
935
|
+
summarize: params.summarize,
|
|
936
|
+
customInstructions: params.customInstructions,
|
|
937
|
+
replaceInstructions: params.replaceInstructions,
|
|
938
|
+
label: params.label,
|
|
939
|
+
});
|
|
940
|
+
return {
|
|
941
|
+
piboSessionId: this.piboSessionId,
|
|
942
|
+
previous,
|
|
943
|
+
current: this.createSessionSnapshot(),
|
|
944
|
+
cancelled: result.cancelled,
|
|
945
|
+
editorText: result.editorText,
|
|
946
|
+
summaryEntryId: result.summaryEntry?.id,
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
async switchSession(params) {
|
|
950
|
+
this.assertActive();
|
|
951
|
+
const previous = this.createSessionSnapshot();
|
|
952
|
+
const result = await this.runtime.switchSession(params.sessionFile, { cwdOverride: params.cwdOverride });
|
|
953
|
+
return {
|
|
954
|
+
piboSessionId: this.piboSessionId,
|
|
955
|
+
previous,
|
|
956
|
+
current: this.createSessionSnapshot(),
|
|
957
|
+
cancelled: result.cancelled,
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
async setModel(model) {
|
|
961
|
+
this.assertActive();
|
|
962
|
+
const resolved = this.runtime.session.modelRegistry.find(model.provider, model.id);
|
|
963
|
+
if (!resolved)
|
|
964
|
+
throw new Error(`Unknown model ${model.provider}/${model.id}`);
|
|
965
|
+
await this.runtime.session.setModel(resolved);
|
|
966
|
+
return { provider: resolved.provider, id: resolved.id };
|
|
967
|
+
}
|
|
968
|
+
setThinkingLevel(level) {
|
|
969
|
+
this.assertActive();
|
|
970
|
+
this.runtime.session.setThinkingLevel(level);
|
|
971
|
+
return this.getThinkingResult();
|
|
972
|
+
}
|
|
973
|
+
cycleThinkingLevel() {
|
|
974
|
+
this.assertActive();
|
|
975
|
+
this.runtime.session.cycleThinkingLevel();
|
|
976
|
+
return this.getThinkingResult();
|
|
977
|
+
}
|
|
978
|
+
getFastMode() {
|
|
979
|
+
this.assertActive();
|
|
980
|
+
return this.getFastModeResult();
|
|
981
|
+
}
|
|
982
|
+
setFastMode(enabled) {
|
|
983
|
+
this.assertActive();
|
|
984
|
+
const before = this.getFastModeResult().mode;
|
|
985
|
+
if (this.fastModeSupported())
|
|
986
|
+
this.fastMode = enabled;
|
|
987
|
+
const current = this.getFastModeResult();
|
|
988
|
+
return { ...current, changed: before !== current.mode };
|
|
989
|
+
}
|
|
990
|
+
async compact(customInstructions) {
|
|
991
|
+
this.assertActive();
|
|
992
|
+
return await this.runtime.session.compact(customInstructions);
|
|
993
|
+
}
|
|
994
|
+
async dispose() {
|
|
995
|
+
if (this.disposePromise)
|
|
996
|
+
return this.disposePromise;
|
|
997
|
+
this.disposePromise = this.disposeUnsafe();
|
|
998
|
+
return this.disposePromise;
|
|
999
|
+
}
|
|
1000
|
+
forceDispose(reason = "session disposal timed out") {
|
|
1001
|
+
this.transitionToDisposed(reason);
|
|
1002
|
+
if (this.forceDisposalStarted)
|
|
1003
|
+
return;
|
|
1004
|
+
this.forceDisposalStarted = true;
|
|
1005
|
+
const abort = this.runtime.session.abort;
|
|
1006
|
+
if (abort) {
|
|
1007
|
+
try {
|
|
1008
|
+
void Promise.resolve(abort.call(this.runtime.session)).catch(() => { });
|
|
1009
|
+
}
|
|
1010
|
+
catch {
|
|
1011
|
+
// Forced runtime disposal must still run when abort throws synchronously.
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
void this.disposeRuntime().catch(() => { });
|
|
1015
|
+
}
|
|
1016
|
+
transitionToDisposed(reason) {
|
|
1017
|
+
if (this.disposed)
|
|
1018
|
+
return false;
|
|
1019
|
+
void this.compatibilityAuthController?.dispose();
|
|
1020
|
+
const activeMessage = this.activeMessage;
|
|
1021
|
+
this.notifyMessagesInterrupted(this.activeAndQueuedMessages(), reason);
|
|
1022
|
+
if (activeMessage) {
|
|
1023
|
+
const error = `Session disposed while a message was active: ${reason}`;
|
|
1024
|
+
this.emit({
|
|
1025
|
+
type: "session_error",
|
|
1026
|
+
piboSessionId: this.piboSessionId,
|
|
1027
|
+
eventId: activeMessage.id,
|
|
1028
|
+
error,
|
|
1029
|
+
errorDetails: runtimeSessionErrorDetails(error),
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
this.cancelProviderRecovery();
|
|
1033
|
+
this.queue.length = 0;
|
|
1034
|
+
this.onStateChange?.({ processing: false, queuedMessages: this.queue.length, disposed: true });
|
|
1035
|
+
this.unsubscribe?.();
|
|
1036
|
+
this.unsubscribe = undefined;
|
|
1037
|
+
if (this.recoverySession) {
|
|
1038
|
+
this.cancelContextGuardRecovery(`Context guard recovery cancelled because ${reason}`);
|
|
1039
|
+
this.recoverySession = undefined;
|
|
1040
|
+
}
|
|
1041
|
+
this.disposed = true;
|
|
1042
|
+
return true;
|
|
1043
|
+
}
|
|
1044
|
+
disposeRuntime() {
|
|
1045
|
+
if (!this.runtimeDisposePromise) {
|
|
1046
|
+
try {
|
|
1047
|
+
this.runtimeDisposePromise = Promise.resolve(this.runtime.dispose());
|
|
1048
|
+
}
|
|
1049
|
+
catch (error) {
|
|
1050
|
+
this.runtimeDisposePromise = Promise.reject(error);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
return this.runtimeDisposePromise;
|
|
1054
|
+
}
|
|
1055
|
+
async disposeUnsafe() {
|
|
1056
|
+
if (!this.transitionToDisposed("session disposed"))
|
|
1057
|
+
return;
|
|
1058
|
+
const abort = this.runtime.session.abort;
|
|
1059
|
+
try {
|
|
1060
|
+
if (abort)
|
|
1061
|
+
await Promise.allSettled([Promise.resolve().then(() => abort.call(this.runtime.session))]);
|
|
1062
|
+
await this.drainPromise;
|
|
1063
|
+
}
|
|
1064
|
+
finally {
|
|
1065
|
+
await this.disposeRuntime();
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
async kill() {
|
|
1069
|
+
this.notifyMessagesInterrupted(this.activeAndQueuedMessages(), "session killed");
|
|
1070
|
+
this.cancelProviderRecovery();
|
|
1071
|
+
this.queue.length = 0;
|
|
1072
|
+
this.onStateChange?.({ processing: this.processing, queuedMessages: this.queue.length, disposed: this.disposed });
|
|
1073
|
+
this.cancelContextGuardRecovery("Context guard recovery cancelled because the routed session was killed");
|
|
1074
|
+
await this.runtime.session.abort();
|
|
1075
|
+
return this.piboSessionId;
|
|
1076
|
+
}
|
|
1077
|
+
async cancelMessage(eventId) {
|
|
1078
|
+
this.assertActive();
|
|
1079
|
+
const queuedIndex = this.queue.findIndex((item) => item.event.id === eventId);
|
|
1080
|
+
if (queuedIndex >= 0) {
|
|
1081
|
+
const [removed] = this.queue.splice(queuedIndex, 1);
|
|
1082
|
+
if (removed?.kind === "message")
|
|
1083
|
+
this.notifyMessagesInterrupted([removed.event], "message cancelled");
|
|
1084
|
+
this.onStateChange?.({ processing: this.processing, queuedMessages: this.queue.length, disposed: this.disposed });
|
|
1085
|
+
return true;
|
|
1086
|
+
}
|
|
1087
|
+
if (this.activeMessage?.id === eventId) {
|
|
1088
|
+
this.notifyMessagesInterrupted([this.activeMessage], "message cancelled");
|
|
1089
|
+
this.cancelProviderRecovery();
|
|
1090
|
+
this.cancelContextGuardRecovery("Context guard recovery cancelled with the active message");
|
|
1091
|
+
await this.runtime.session.abort();
|
|
1092
|
+
return true;
|
|
1093
|
+
}
|
|
1094
|
+
return false;
|
|
1095
|
+
}
|
|
1096
|
+
startDrain() {
|
|
1097
|
+
if (this.drainPromise)
|
|
1098
|
+
return;
|
|
1099
|
+
const drain = this.drain();
|
|
1100
|
+
this.drainPromise = drain;
|
|
1101
|
+
void drain.finally(() => {
|
|
1102
|
+
if (this.drainPromise === drain)
|
|
1103
|
+
this.drainPromise = undefined;
|
|
1104
|
+
});
|
|
1105
|
+
}
|
|
1106
|
+
async drain() {
|
|
1107
|
+
if (this.processing || this.disposed)
|
|
1108
|
+
return;
|
|
1109
|
+
this.processing = true;
|
|
1110
|
+
this.onStateChange?.({ processing: this.processing, queuedMessages: this.queue.length, disposed: this.disposed });
|
|
1111
|
+
try {
|
|
1112
|
+
while (this.queue.length > 0 && !this.disposed) {
|
|
1113
|
+
const item = this.queue.shift();
|
|
1114
|
+
this.onStateChange?.({ processing: this.processing, queuedMessages: this.queue.length, disposed: this.disposed });
|
|
1115
|
+
if (item.kind === "compact") {
|
|
1116
|
+
await this.processQueuedCompact(item.event);
|
|
1117
|
+
}
|
|
1118
|
+
else {
|
|
1119
|
+
await this.processQueuedMessage(item.event);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
}
|
|
1123
|
+
finally {
|
|
1124
|
+
this.processing = false;
|
|
1125
|
+
this.onStateChange?.({ processing: this.processing, queuedMessages: this.queue.length, disposed: this.disposed });
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
async processQueuedMessage(event) {
|
|
1129
|
+
try {
|
|
1130
|
+
const preflight = await this.messagePreflight?.(event);
|
|
1131
|
+
if (this.disposed)
|
|
1132
|
+
return;
|
|
1133
|
+
if (preflight && !preflight.allowed) {
|
|
1134
|
+
this.emit({
|
|
1135
|
+
type: "session_error",
|
|
1136
|
+
piboSessionId: this.piboSessionId,
|
|
1137
|
+
eventId: event.id,
|
|
1138
|
+
error: preflight.reason ?? "Queued message is no longer authorized",
|
|
1139
|
+
errorDetails: {
|
|
1140
|
+
category: "loop_lifecycle",
|
|
1141
|
+
errorClass: "runtime_abort",
|
|
1142
|
+
code: preflight.code ?? "message_preflight_rejected",
|
|
1143
|
+
origin: "runtime",
|
|
1144
|
+
retryable: false,
|
|
1145
|
+
},
|
|
1146
|
+
provenance: event.provenance,
|
|
1147
|
+
});
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
const session = this.runtime.session;
|
|
1151
|
+
await this.resumeTranscriptIntegrityRecovery(session);
|
|
1152
|
+
if (this.disposed)
|
|
1153
|
+
return;
|
|
1154
|
+
this.emit({
|
|
1155
|
+
type: "message_started",
|
|
1156
|
+
piboSessionId: this.piboSessionId,
|
|
1157
|
+
eventId: event.id,
|
|
1158
|
+
text: event.text,
|
|
1159
|
+
source: event.source,
|
|
1160
|
+
provenance: event.provenance,
|
|
1161
|
+
});
|
|
1162
|
+
this.activeMessage = event;
|
|
1163
|
+
this.providerRecoveryCancelled = false;
|
|
1164
|
+
this.pendingAssistantError = undefined;
|
|
1165
|
+
this.pendingAssistantErrorRetryable = false;
|
|
1166
|
+
this.activeMessageFailed = false;
|
|
1167
|
+
this.activeAssistantIndex = undefined;
|
|
1168
|
+
this.nextAssistantIndex = 0;
|
|
1169
|
+
this.activeThinkingIndex = undefined;
|
|
1170
|
+
this.nextThinkingIndex = 0;
|
|
1171
|
+
this.applyMessageCapabilityScope(event, session);
|
|
1172
|
+
const expandedText = expandInlineSkills(event.text, session.resourceLoader.getSkills().skills);
|
|
1173
|
+
await session.prompt(expandedText, { source: promptSource(event.source) });
|
|
1174
|
+
if (this.disposed)
|
|
1175
|
+
return;
|
|
1176
|
+
await this.waitForPiAgentSettlement(session);
|
|
1177
|
+
if (this.disposed)
|
|
1178
|
+
return;
|
|
1179
|
+
await this.resumeContextGuardRecovery(session);
|
|
1180
|
+
if (this.disposed)
|
|
1181
|
+
return;
|
|
1182
|
+
await this.recoverTransientProviderErrors(session);
|
|
1183
|
+
if (this.disposed)
|
|
1184
|
+
return;
|
|
1185
|
+
this.flushPendingAssistantError();
|
|
1186
|
+
if (!this.activeMessageFailed) {
|
|
1187
|
+
this.emit({
|
|
1188
|
+
type: "message_finished",
|
|
1189
|
+
piboSessionId: this.piboSessionId,
|
|
1190
|
+
eventId: event.id,
|
|
1191
|
+
source: event.source,
|
|
1192
|
+
provenance: event.provenance,
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
catch (error) {
|
|
1197
|
+
if (error instanceof PiboProviderRecoveryCancelledError || this.disposed)
|
|
1198
|
+
return;
|
|
1199
|
+
const message = errorMessage(error);
|
|
1200
|
+
this.emit({
|
|
1201
|
+
type: "session_error",
|
|
1202
|
+
piboSessionId: this.piboSessionId,
|
|
1203
|
+
eventId: event.id,
|
|
1204
|
+
error: message,
|
|
1205
|
+
errorDetails: runtimeSessionErrorDetails(message),
|
|
1206
|
+
provenance: event.provenance,
|
|
1207
|
+
});
|
|
1208
|
+
}
|
|
1209
|
+
finally {
|
|
1210
|
+
this.restoreMessageCapabilityScope();
|
|
1211
|
+
this.activeMessage = undefined;
|
|
1212
|
+
this.providerRecoveryCancelled = false;
|
|
1213
|
+
this.pendingAssistantError = undefined;
|
|
1214
|
+
this.pendingAssistantErrorRetryable = false;
|
|
1215
|
+
this.activeMessageFailed = false;
|
|
1216
|
+
this.activeAssistantIndex = undefined;
|
|
1217
|
+
this.nextAssistantIndex = 0;
|
|
1218
|
+
this.activeThinkingIndex = undefined;
|
|
1219
|
+
this.nextThinkingIndex = 0;
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
applyMessageCapabilityScope(event, session) {
|
|
1223
|
+
if (event.capabilityScope !== "run-reminder")
|
|
1224
|
+
return;
|
|
1225
|
+
const restoreToolNames = session.getActiveToolNames();
|
|
1226
|
+
const scopedToolNames = restoreToolNames.filter((name) => RUN_REMINDER_CAPABILITY_TOOLS.has(name));
|
|
1227
|
+
this.activeCapabilityScope = { session, restoreToolNames };
|
|
1228
|
+
session.setActiveToolsByName(scopedToolNames);
|
|
1229
|
+
}
|
|
1230
|
+
async processQueuedCompact(event) {
|
|
1231
|
+
this.activeExecutionEvent = event;
|
|
1232
|
+
try {
|
|
1233
|
+
await this.resumeTranscriptIntegrityRecovery(this.runtime.session);
|
|
1234
|
+
const result = await this.runAction(event);
|
|
1235
|
+
if (this.disposed)
|
|
1236
|
+
return;
|
|
1237
|
+
this.emit({
|
|
1238
|
+
type: "execution_result",
|
|
1239
|
+
piboSessionId: this.piboSessionId,
|
|
1240
|
+
eventId: event.id,
|
|
1241
|
+
action: event.action,
|
|
1242
|
+
result,
|
|
1243
|
+
});
|
|
1244
|
+
}
|
|
1245
|
+
catch (error) {
|
|
1246
|
+
if (this.disposed)
|
|
1247
|
+
return;
|
|
1248
|
+
const message = errorMessage(error);
|
|
1249
|
+
this.emit({
|
|
1250
|
+
type: "session_error",
|
|
1251
|
+
piboSessionId: this.piboSessionId,
|
|
1252
|
+
eventId: event.id,
|
|
1253
|
+
error: message,
|
|
1254
|
+
errorDetails: runtimeSessionErrorDetails(message),
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1257
|
+
finally {
|
|
1258
|
+
this.activeExecutionEvent = undefined;
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
enqueueCompactAction(event) {
|
|
1262
|
+
this.queue.push({ kind: "compact", event });
|
|
1263
|
+
const output = {
|
|
1264
|
+
type: "execution_result",
|
|
1265
|
+
piboSessionId: this.piboSessionId,
|
|
1266
|
+
eventId: event.id,
|
|
1267
|
+
action: event.action,
|
|
1268
|
+
result: { queued: true, queuedMessages: this.queue.length },
|
|
1269
|
+
};
|
|
1270
|
+
this.emit(output);
|
|
1271
|
+
this.onStateChange?.({ processing: this.processing, queuedMessages: this.queue.length, disposed: this.disposed });
|
|
1272
|
+
this.startDrain();
|
|
1273
|
+
return output;
|
|
1274
|
+
}
|
|
1275
|
+
getCompatibilityAuthController() {
|
|
1276
|
+
this.compatibilityAuthController ??= new PiAgentRuntimeAuthController(() => loadPiModelCatalog(process.cwd()), () => { });
|
|
1277
|
+
return this.compatibilityAuthController;
|
|
1278
|
+
}
|
|
1279
|
+
async runAction(event) {
|
|
1280
|
+
const action = event.action;
|
|
1281
|
+
const gatewayAction = this.pluginRegistry.getGatewayAction(action);
|
|
1282
|
+
if (!gatewayAction) {
|
|
1283
|
+
throw new Error(`Unknown execution action "${action}"`);
|
|
1284
|
+
}
|
|
1285
|
+
return await gatewayAction.execute({
|
|
1286
|
+
piboSessionId: this.piboSessionId,
|
|
1287
|
+
runtimeInstanceId: "pi",
|
|
1288
|
+
runtimeAuthRequired: true,
|
|
1289
|
+
getStatus: () => this.getStatus(),
|
|
1290
|
+
getStatusSnapshot: () => this.getStatusSnapshot(),
|
|
1291
|
+
getContextUsage: () => this.getContextUsage(),
|
|
1292
|
+
getActiveModel: () => this.getActiveModel(),
|
|
1293
|
+
getModelCatalog: async () => undefined,
|
|
1294
|
+
getRuntimeAuthStatus: async () => await this.getCompatibilityAuthController().getStatus(),
|
|
1295
|
+
startRuntimeAuth: async (input) => await this.getCompatibilityAuthController().start(input),
|
|
1296
|
+
completeRuntimeAuth: async (input) => await this.getCompatibilityAuthController().complete(input),
|
|
1297
|
+
cancelRuntimeAuth: async (input) => await this.getCompatibilityAuthController().cancel(input),
|
|
1298
|
+
logoutRuntimeAuth: async (input) => await this.getCompatibilityAuthController().logout(input),
|
|
1299
|
+
getProviderUsage: () => this.getProviderUsage(),
|
|
1300
|
+
clearQueue: () => this.clearQueue(),
|
|
1301
|
+
abort: async () => {
|
|
1302
|
+
if (this.activeMessage)
|
|
1303
|
+
this.notifyMessagesInterrupted([this.activeMessage], "abort requested");
|
|
1304
|
+
this.cancelContextGuardRecovery("Context guard recovery cancelled by abort");
|
|
1305
|
+
await this.runtime.session.abort();
|
|
1306
|
+
},
|
|
1307
|
+
dispose: () => this.dispose(),
|
|
1308
|
+
getCurrentSession: () => this.getCurrentSession(),
|
|
1309
|
+
listSessions: () => this.listSessions(),
|
|
1310
|
+
getForkCandidates: () => this.getForkCandidates(),
|
|
1311
|
+
forkSession: (entryId) => this.forkSession(entryId),
|
|
1312
|
+
cloneSession: () => this.cloneSession(),
|
|
1313
|
+
getSessionTree: () => this.getSessionTree(),
|
|
1314
|
+
navigateSessionTree: (params) => this.navigateSessionTree(params),
|
|
1315
|
+
switchSession: (params) => this.switchSession(params),
|
|
1316
|
+
getThinkingLevel: () => this.getThinkingResult(),
|
|
1317
|
+
setThinkingLevel: (level) => this.setThinkingLevel(level),
|
|
1318
|
+
cycleThinkingLevel: () => this.cycleThinkingLevel(),
|
|
1319
|
+
getFastMode: () => this.getFastMode(),
|
|
1320
|
+
setFastMode: (enabled) => this.setFastMode(enabled),
|
|
1321
|
+
setModel: (model) => this.setModel(model),
|
|
1322
|
+
compact: (customInstructions) => this.compact(customInstructions),
|
|
1323
|
+
respondToApproval: async () => { throw new Error("Pi does not expose runtime approval responses."); },
|
|
1324
|
+
respondToUserInput: async () => { throw new Error("Pi does not expose structured runtime user input."); },
|
|
1325
|
+
kill: async () => {
|
|
1326
|
+
const killed = [await this.kill()];
|
|
1327
|
+
let cancelledRuns = [];
|
|
1328
|
+
if (this.onKillChildren) {
|
|
1329
|
+
const children = await this.onKillChildren(this.piboSessionId);
|
|
1330
|
+
killed.push(...children.killed);
|
|
1331
|
+
cancelledRuns = children.cancelledRuns;
|
|
1332
|
+
}
|
|
1333
|
+
return { killed, cancelledRuns };
|
|
1334
|
+
},
|
|
1335
|
+
killAll: async () => {
|
|
1336
|
+
const killed = [await this.kill()];
|
|
1337
|
+
let cancelledRuns = [];
|
|
1338
|
+
if (this.onKillChildren) {
|
|
1339
|
+
const children = await this.onKillChildren(this.piboSessionId, { includeRuns: true });
|
|
1340
|
+
killed.push(...children.killed);
|
|
1341
|
+
cancelledRuns = children.cancelledRuns;
|
|
1342
|
+
}
|
|
1343
|
+
return { killed, cancelledRuns };
|
|
1344
|
+
},
|
|
1345
|
+
}, event);
|
|
1346
|
+
}
|
|
1347
|
+
cancelContextGuardRecovery(message) {
|
|
1348
|
+
const session = this.recoverySession ?? this.runtime.session;
|
|
1349
|
+
cancelPiboAssistantContextGuardRecovery(session, new Error(message));
|
|
1350
|
+
session.abortCompaction?.();
|
|
1351
|
+
}
|
|
1352
|
+
assertActive() {
|
|
1353
|
+
if (this.disposed) {
|
|
1354
|
+
throw new Error(`Session "${this.piboSessionId}" has been disposed`);
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
getThinkingResult() {
|
|
1358
|
+
return {
|
|
1359
|
+
level: this.runtime.session.thinkingLevel,
|
|
1360
|
+
availableLevels: this.runtime.session.getAvailableThinkingLevels(),
|
|
1361
|
+
supported: this.runtime.session.supportsThinking(),
|
|
1362
|
+
};
|
|
1363
|
+
}
|
|
1364
|
+
getFastModeResult() {
|
|
1365
|
+
const supported = this.fastModeSupported();
|
|
1366
|
+
return { mode: supported && this.fastMode ? "fast" : "normal", supported };
|
|
1367
|
+
}
|
|
1368
|
+
fastModeSupported() {
|
|
1369
|
+
return this.runtime.session.supportsThinking() && modelSupportsFastServiceTier(this.runtime.session.model);
|
|
1370
|
+
}
|
|
1371
|
+
clearQueue() {
|
|
1372
|
+
const cleared = this.queue.length;
|
|
1373
|
+
const removedMessages = this.queue.flatMap((item) => item.kind === "message" ? [item.event] : []);
|
|
1374
|
+
this.queue.length = 0;
|
|
1375
|
+
this.notifyMessagesInterrupted(removedMessages, "queue cleared");
|
|
1376
|
+
this.onStateChange?.({ processing: this.processing, queuedMessages: this.queue.length, disposed: this.disposed });
|
|
1377
|
+
return cleared;
|
|
1378
|
+
}
|
|
1379
|
+
activeAndQueuedMessages() {
|
|
1380
|
+
const messages = this.queue.flatMap((item) => item.kind === "message" ? [item.event] : []);
|
|
1381
|
+
return this.activeMessage ? [this.activeMessage, ...messages] : messages;
|
|
1382
|
+
}
|
|
1383
|
+
notifyMessagesInterrupted(messages, reason) {
|
|
1384
|
+
if (messages.length === 0)
|
|
1385
|
+
return;
|
|
1386
|
+
this.onMessagesInterrupted?.(messages, reason);
|
|
1387
|
+
}
|
|
1388
|
+
createSessionSnapshot() {
|
|
1389
|
+
const session = this.runtime.session;
|
|
1390
|
+
const manager = session.sessionManager;
|
|
1391
|
+
return {
|
|
1392
|
+
piSessionId: session.sessionId,
|
|
1393
|
+
sessionFile: session.sessionFile,
|
|
1394
|
+
leafId: manager.getLeafId(),
|
|
1395
|
+
cwd: this.runtime.cwd,
|
|
1396
|
+
sessionName: session.sessionName,
|
|
1397
|
+
parentSessionFile: manager.getHeader()?.parentSession,
|
|
1398
|
+
};
|
|
1399
|
+
}
|
|
1400
|
+
withActiveMessage(event) {
|
|
1401
|
+
if (this.activeMessage?.id && event.type === "assistant_delta") {
|
|
1402
|
+
const assistantIndex = this.activeAssistantIndex ?? this.nextAssistantIndex;
|
|
1403
|
+
if (this.activeAssistantIndex === undefined) {
|
|
1404
|
+
this.nextAssistantIndex += 1;
|
|
1405
|
+
this.activeAssistantIndex = assistantIndex;
|
|
1406
|
+
}
|
|
1407
|
+
return { ...event, eventId: this.activeMessage.id, assistantIndex };
|
|
1408
|
+
}
|
|
1409
|
+
if (this.activeMessage?.id && event.type === "assistant_message") {
|
|
1410
|
+
const assistantIndex = this.activeAssistantIndex ?? this.nextAssistantIndex;
|
|
1411
|
+
if (this.activeAssistantIndex === undefined) {
|
|
1412
|
+
this.nextAssistantIndex += 1;
|
|
1413
|
+
}
|
|
1414
|
+
this.activeAssistantIndex = undefined;
|
|
1415
|
+
return { ...event, eventId: this.activeMessage.id, assistantIndex };
|
|
1416
|
+
}
|
|
1417
|
+
if (this.activeMessage?.id && event.type === "thinking_started") {
|
|
1418
|
+
const thinkingIndex = this.nextThinkingIndex;
|
|
1419
|
+
this.nextThinkingIndex += 1;
|
|
1420
|
+
this.activeThinkingIndex = thinkingIndex;
|
|
1421
|
+
return { ...event, eventId: this.activeMessage.id, thinkingIndex };
|
|
1422
|
+
}
|
|
1423
|
+
if (this.activeMessage?.id && (event.type === "thinking_delta" || event.type === "thinking_finished")) {
|
|
1424
|
+
const thinkingIndex = this.activeThinkingIndex ?? this.nextThinkingIndex;
|
|
1425
|
+
if (this.activeThinkingIndex === undefined) {
|
|
1426
|
+
this.nextThinkingIndex += 1;
|
|
1427
|
+
this.activeThinkingIndex = thinkingIndex;
|
|
1428
|
+
}
|
|
1429
|
+
const output = { ...event, eventId: this.activeMessage.id, thinkingIndex };
|
|
1430
|
+
if (event.type === "thinking_finished")
|
|
1431
|
+
this.activeThinkingIndex = undefined;
|
|
1432
|
+
return output;
|
|
1433
|
+
}
|
|
1434
|
+
if (this.activeMessage?.id &&
|
|
1435
|
+
(event.type === "assistant_usage" ||
|
|
1436
|
+
event.type === "compaction_start" ||
|
|
1437
|
+
event.type === "compaction_end" ||
|
|
1438
|
+
event.type === "tool_call" ||
|
|
1439
|
+
event.type === "tool_execution_started" ||
|
|
1440
|
+
event.type === "tool_execution_updated" ||
|
|
1441
|
+
event.type === "tool_execution_finished" ||
|
|
1442
|
+
event.type === "session_error" ||
|
|
1443
|
+
event.type === "execution_result")) {
|
|
1444
|
+
return { ...event, eventId: this.activeMessage.id };
|
|
1445
|
+
}
|
|
1446
|
+
return event;
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
function normalizeSessionTree(nodes) {
|
|
1450
|
+
return nodes.map((node) => ({
|
|
1451
|
+
entry: JSON.parse(JSON.stringify(node.entry)),
|
|
1452
|
+
children: normalizeSessionTree(node.children),
|
|
1453
|
+
label: node.label,
|
|
1454
|
+
labelTimestamp: node.labelTimestamp,
|
|
1455
|
+
}));
|
|
1456
|
+
}
|
|
1457
|
+
function isSessionOperationResult(value) {
|
|
1458
|
+
if (!value || typeof value !== "object")
|
|
1459
|
+
return false;
|
|
1460
|
+
const candidate = value;
|
|
1461
|
+
return (typeof candidate.piboSessionId === "string" &&
|
|
1462
|
+
Boolean(candidate.current) &&
|
|
1463
|
+
typeof candidate.current?.piSessionId === "string");
|
|
1464
|
+
}
|