@pasko70/pibo 3.0.1 → 3.1.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/README.md +20 -6
- package/compute-image/Dockerfile +47 -0
- package/compute-image/Dockerfile.dockerignore +5 -0
- package/dist/agent-runtime/context-build.js +26 -2
- package/dist/agent-runtime/contract.js +78 -28
- package/dist/agent-runtime/portable-history.js +19 -9
- package/dist/agent-runtime/registry.js +8 -2
- package/dist/agent-runtime/routed-session.js +120 -15
- package/dist/agent-runtimes/codex-native/adapter.js +12 -0
- package/dist/agent-runtimes/codex-native/resource-delivery.js +35 -7
- package/dist/agent-runtimes/omp/adapter.js +3 -7
- package/dist/agent-runtimes/pi/adapter.js +5 -3
- package/dist/agent-runtimes/pi/routed-session.js +59 -12
- package/dist/agent-runtimes/pi/runtime.js +6 -0
- package/dist/apps/chat/agent-profiles.js +31 -2
- package/dist/apps/chat/agent-store.js +98 -6
- package/dist/apps/chat/chat-request-normalizers.js +31 -0
- package/dist/apps/chat/data/chat-data-mappers.js +61 -11
- package/dist/apps/chat/data/project-service.js +119 -15
- package/dist/apps/chat/data/room-service.js +21 -0
- package/dist/apps/chat/data/session-query-service.js +33 -10
- package/dist/apps/chat/data/timeline-query-service.js +60 -35
- package/dist/apps/chat/loop-api.js +10 -2
- package/dist/apps/chat/output-compactor.js +144 -21
- package/dist/apps/chat/output-event-policy.js +179 -2
- package/dist/apps/chat/stream.js +16 -7
- package/dist/apps/chat/trace-v2.js +1 -0
- package/dist/apps/chat/web-app.js +317 -86
- package/dist/apps/chat-ui/assets/{dist-BDIATCQt.js → dist-CUQJqggN.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-mqCviI-c.js → dist-ClUlQWYN.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-JnW4v8UE.js → dist-Djul9BmZ.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-lRiLP9rr.js → dist-GD0JGdCi.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BBDMeU5-.js → dist-W5HOqHym.js} +1 -1
- package/dist/apps/chat-ui/assets/index-BcjkX-iP.js +228 -0
- package/dist/apps/chat-ui/assets/index-CmqRSbBU.css +1 -0
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/index-JvrPUGvI.js +43 -0
- package/dist/apps/chat-vscode-web/index.html +1 -1
- package/dist/apps/cli-ui/InkSessionApp.js +111 -12
- package/dist/apps/cli-ui/cliSessionsCommand.js +34 -8
- package/dist/cli-session/localSessionSource.js +153 -161
- package/dist/cli.js +7 -11
- package/dist/compute/cli.js +35 -16
- package/dist/compute/docker.js +341 -94
- package/dist/config/config.js +3 -0
- package/dist/core/context-build.js +7 -16
- package/dist/core/output-persistence-retry.js +484 -0
- package/dist/core/output-render-sequence.js +331 -0
- package/dist/core/profiles.js +24 -0
- package/dist/core/provider-recovery.js +7 -1
- package/dist/core/session-model.js +37 -1
- package/dist/core/session-router.js +78 -110
- package/dist/data/ingest-service.js +91 -9
- package/dist/data/navigation-store.js +5 -5
- package/dist/data/pibo-store.js +8 -1
- package/dist/data/schema.js +243 -3
- package/dist/data/session-store.js +3 -3
- package/dist/debug/agents.js +91 -89
- package/dist/debug/index.js +46 -2
- package/dist/debug/pty.js +127 -48
- package/dist/gateway/cli.js +4 -0
- package/dist/gateway/web.js +8 -8
- package/dist/local/client.js +3 -2
- package/dist/loops/channel.js +3 -1
- package/dist/loops/cli.js +2 -1
- package/dist/loops/service.js +90 -11
- package/dist/loops/store.js +100 -74
- package/dist/mcp/agent-context.js +13 -26
- package/dist/mcp/commands/info.js +3 -1
- package/dist/mcp/config-command.js +46 -2
- package/dist/mcp/config.js +18 -4
- package/dist/plugins/context-files-store.js +36 -6
- package/dist/plugins/context-files.js +52 -1
- package/dist/plugins/registry.js +2 -0
- package/dist/plugins/user-profile-resources.js +22 -0
- package/dist/previews/base-url.js +34 -0
- package/dist/previews/config.js +2 -32
- package/dist/ralph/store.js +5 -0
- package/dist/reliability/store.js +378 -106
- package/dist/session-ui/terminalRows.js +8 -4
- package/dist/sessions/pibo-data-store.js +145 -0
- package/dist/sessions/store.js +42 -0
- package/dist/setup/cli.js +422 -6
- package/dist/setup/installation-profiles.js +464 -0
- package/dist/shared/deterministic-digest.js +94 -0
- package/dist/shared/trace-engine.js +54 -0
- package/dist/shared/trace-event-projection.js +124 -67
- package/dist/shared/trace-history.js +47 -12
- package/dist/shared/trace-live-reducer.js +23 -4
- package/dist/shared/trace-nodes.js +17 -28
- package/dist/shared/trace-order.js +41 -5
- package/dist/shared/trace-page-merge.js +44 -14
- package/dist/shared/trace-patch-nodes.js +2 -0
- package/dist/shared/trace-subagent-links.js +4 -1
- package/dist/shared/trace-tool-identity.js +15 -8
- package/dist/signals/projector.js +28 -6
- package/dist/subagents/observation-query.js +121 -0
- package/dist/subagents/tool.js +7 -3
- package/dist/tools/agent-browser-leases.js +58 -23
- package/dist/tools/agent-browser-wrapper.js +1 -1
- package/dist/tools/browser-use-leases.js +129 -16
- package/dist/tools/browser-use-wrapper.js +1 -1
- package/dist/tools/index.js +27 -11
- package/dist/tools/python-runtime.js +10 -3
- package/dist/tools/registry.js +1 -1
- package/dist/tools/runtime/node-backend.js +16 -5
- package/dist/tools/runtime/node-worker-source.js +112 -33
- package/dist/tools/runtime/python-backend.js +16 -5
- package/dist/tools/runtime/python-worker-source.js +167 -16
- package/dist/tools/runtime/registry.js +23 -10
- package/dist/user-skills/store.js +0 -1
- package/docs/ops/vscode-extension-release.md +9 -1
- package/npm-shrinkwrap.json +9 -2
- package/package.json +9 -2
- package/scripts/docker-entrypoint.sh +46 -0
- package/scripts/prepare-agent-browser-wrapper.sh +70 -0
- package/scripts/prepare-browser-use-wrapper.sh +255 -0
- package/dist/apps/chat-ui/assets/index-0XOOVxLP.js +0 -228
- package/dist/apps/chat-ui/assets/index-DV7_CgsC.css +0 -1
- package/dist/apps/chat-vscode-web/assets/index-Dtw2Z7jz.js +0 -43
- package/dist/apps/vscode-artifacts/latest.vsix +0 -0
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-3.0.1.vsix +0 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
const RENDER_SEQUENCE_SLOTS_PER_MILLISECOND = 1_000;
|
|
3
|
+
const MAX_TRACKED_SESSIONS = 1_024;
|
|
4
|
+
const MAX_RECENT_SEGMENTS_PER_SESSION = 1_024;
|
|
5
|
+
const MAX_RECENT_TOOL_INVOCATIONS_PER_SESSION = 1_024;
|
|
6
|
+
export function outputRenderHighWaterStore(value) {
|
|
7
|
+
if (!value || typeof value !== "object")
|
|
8
|
+
return undefined;
|
|
9
|
+
const candidate = value;
|
|
10
|
+
return typeof candidate.claimOutputRenderSequence === "function"
|
|
11
|
+
&& typeof candidate.observeOutputRenderSequence === "function"
|
|
12
|
+
? candidate
|
|
13
|
+
: undefined;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Assigns one monotonic position to each conceptual output segment. Deltas and
|
|
17
|
+
* lifecycle events for the same segment reuse the first position.
|
|
18
|
+
*/
|
|
19
|
+
export class OutputRenderSequencer {
|
|
20
|
+
sessions = new Map();
|
|
21
|
+
now;
|
|
22
|
+
highWaterStore;
|
|
23
|
+
constructor(nowOrOptions = Date.now) {
|
|
24
|
+
if (typeof nowOrOptions === "function") {
|
|
25
|
+
this.now = nowOrOptions;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
this.now = nowOrOptions.now ?? Date.now;
|
|
29
|
+
this.highWaterStore = nowOrOptions.highWaterStore;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
position(event) {
|
|
33
|
+
const state = this.sessionState(event.piboSessionId);
|
|
34
|
+
this.trackActiveTurn(state, event);
|
|
35
|
+
const eventWithToolIdentity = this.positionToolInvocation(state, event);
|
|
36
|
+
const key = outputRenderSegmentKey(eventWithToolIdentity, state.activeEventId);
|
|
37
|
+
const supplied = validRenderSequence(eventWithToolIdentity.renderSequence)
|
|
38
|
+
? eventWithToolIdentity.renderSequence
|
|
39
|
+
: undefined;
|
|
40
|
+
const existing = supplied ?? (key ? state.positions.get(key) : undefined);
|
|
41
|
+
const renderSequence = existing ?? this.nextSequence(event.piboSessionId, state);
|
|
42
|
+
state.lastSequence = Math.max(state.lastSequence, renderSequence);
|
|
43
|
+
if (supplied !== undefined)
|
|
44
|
+
this.highWaterStore?.observeOutputRenderSequence(event.piboSessionId, supplied);
|
|
45
|
+
if (key && !state.positions.has(key))
|
|
46
|
+
state.positions.set(key, renderSequence);
|
|
47
|
+
const positioned = eventWithToolIdentity.renderSequence === renderSequence
|
|
48
|
+
? eventWithToolIdentity
|
|
49
|
+
: { ...eventWithToolIdentity, renderSequence };
|
|
50
|
+
if (event.type === "message_finished" || event.type === "session_error") {
|
|
51
|
+
const eventId = "eventId" in event ? event.eventId : undefined;
|
|
52
|
+
if ((eventId && state.activeEventId === eventId) || (event.type === "session_error" && !eventId)) {
|
|
53
|
+
state.activeEventId = undefined;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const segmentEventId = "eventId" in positioned && positioned.eventId ? positioned.eventId : state.activeEventId;
|
|
57
|
+
this.updateSegmentLifecycle(state, positioned, key, segmentEventId);
|
|
58
|
+
this.trimSessions();
|
|
59
|
+
return positioned;
|
|
60
|
+
}
|
|
61
|
+
disposeSession(piboSessionId) {
|
|
62
|
+
this.sessions.delete(piboSessionId);
|
|
63
|
+
}
|
|
64
|
+
disposeAll() {
|
|
65
|
+
this.sessions.clear();
|
|
66
|
+
}
|
|
67
|
+
debugState() {
|
|
68
|
+
let positionCount = 0;
|
|
69
|
+
let toolInvocationCount = 0;
|
|
70
|
+
let completedSessionCount = 0;
|
|
71
|
+
for (const state of this.sessions.values()) {
|
|
72
|
+
positionCount += state.positions.size;
|
|
73
|
+
for (const invocations of state.toolInvocations.values())
|
|
74
|
+
toolInvocationCount += invocations.length;
|
|
75
|
+
if (sessionCanBeEvicted(state))
|
|
76
|
+
completedSessionCount += 1;
|
|
77
|
+
}
|
|
78
|
+
return { sessionCount: this.sessions.size, completedSessionCount, sessions: [...this.sessions.keys()], positionCount, toolInvocationCount };
|
|
79
|
+
}
|
|
80
|
+
sessionState(piboSessionId) {
|
|
81
|
+
const existing = this.sessions.get(piboSessionId);
|
|
82
|
+
if (existing) {
|
|
83
|
+
this.sessions.delete(piboSessionId);
|
|
84
|
+
this.sessions.set(piboSessionId, existing);
|
|
85
|
+
return existing;
|
|
86
|
+
}
|
|
87
|
+
const state = {
|
|
88
|
+
positions: new Map(),
|
|
89
|
+
segmentEventIds: new Map(),
|
|
90
|
+
activeSegments: new Set(),
|
|
91
|
+
completedSegments: new Set(),
|
|
92
|
+
toolInvocations: new Map(),
|
|
93
|
+
lastSequence: 0,
|
|
94
|
+
};
|
|
95
|
+
this.sessions.set(piboSessionId, state);
|
|
96
|
+
return state;
|
|
97
|
+
}
|
|
98
|
+
positionToolInvocation(state, event) {
|
|
99
|
+
if (!isToolIdentityEvent(event) || !event.toolCallId)
|
|
100
|
+
return event;
|
|
101
|
+
const eventId = event.eventId ?? state.activeEventId;
|
|
102
|
+
const counterKey = toolInvocationCounterKey(eventId, event.toolCallId);
|
|
103
|
+
let invocations = state.toolInvocations.get(counterKey);
|
|
104
|
+
if (!invocations) {
|
|
105
|
+
invocations = [];
|
|
106
|
+
state.toolInvocations.set(counterKey, invocations);
|
|
107
|
+
}
|
|
108
|
+
let invocation;
|
|
109
|
+
const transition = eventId ? {
|
|
110
|
+
piboSessionId: event.piboSessionId,
|
|
111
|
+
eventId,
|
|
112
|
+
toolCallId: event.toolCallId,
|
|
113
|
+
eventType: event.type,
|
|
114
|
+
...(event.type === "tool_call" ? { callFingerprint: toolCallFingerprint(event) } : {}),
|
|
115
|
+
} : undefined;
|
|
116
|
+
if (validToolInvocationOrdinal(event.toolInvocationOrdinal)) {
|
|
117
|
+
if (eventId)
|
|
118
|
+
this.highWaterStore?.observeOutputToolInvocationOrdinal?.(event.piboSessionId, eventId, event.toolCallId, event.toolInvocationOrdinal);
|
|
119
|
+
if (transition)
|
|
120
|
+
this.highWaterStore?.observeOutputToolInvocation?.({ ...transition, ordinal: event.toolInvocationOrdinal });
|
|
121
|
+
invocation = invocations.find((candidate) => candidate.ordinal === event.toolInvocationOrdinal);
|
|
122
|
+
if (!invocation) {
|
|
123
|
+
invocation = { ordinal: event.toolInvocationOrdinal, seen: new Set(), closed: false, persistedIdentity: true };
|
|
124
|
+
invocations.push(invocation);
|
|
125
|
+
}
|
|
126
|
+
invocation.persistedIdentity = true;
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
const latest = invocations.at(-1);
|
|
130
|
+
if (!latest) {
|
|
131
|
+
invocation = this.createToolInvocation(invocations, event.piboSessionId, eventId, event.toolCallId, transition);
|
|
132
|
+
}
|
|
133
|
+
else if (event.type === "tool_call" && (latest.seen.has("tool_call") || (latest.closed && latest.persistedIdentity))) {
|
|
134
|
+
invocation = this.createToolInvocation(invocations, event.piboSessionId, eventId, event.toolCallId, transition);
|
|
135
|
+
}
|
|
136
|
+
else if (event.type === "tool_execution_started" && latest.closed && latest.seen.has("tool_execution_started")) {
|
|
137
|
+
invocation = this.createToolInvocation(invocations, event.piboSessionId, eventId, event.toolCallId, transition);
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
invocation = latest;
|
|
141
|
+
if (transition)
|
|
142
|
+
this.highWaterStore?.observeOutputToolInvocation?.({ ...transition, ordinal: latest.ordinal });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
invocation.seen.add(event.type);
|
|
146
|
+
if (event.type === "tool_execution_finished")
|
|
147
|
+
invocation.closed = true;
|
|
148
|
+
this.trimToolInvocations(state);
|
|
149
|
+
const needsEventId = !event.eventId && eventId !== undefined;
|
|
150
|
+
return event.toolInvocationOrdinal === invocation.ordinal && !needsEventId
|
|
151
|
+
? event
|
|
152
|
+
: { ...event, ...(needsEventId ? { eventId } : {}), toolInvocationOrdinal: invocation.ordinal };
|
|
153
|
+
}
|
|
154
|
+
trimToolInvocations(state) {
|
|
155
|
+
let total = 0;
|
|
156
|
+
for (const invocations of state.toolInvocations.values())
|
|
157
|
+
total += invocations.length;
|
|
158
|
+
while (total > MAX_RECENT_TOOL_INVOCATIONS_PER_SESSION) {
|
|
159
|
+
let removed = false;
|
|
160
|
+
for (const [key, invocations] of state.toolInvocations) {
|
|
161
|
+
const closedIndex = invocations.findIndex((invocation) => invocation.closed);
|
|
162
|
+
if (closedIndex === -1)
|
|
163
|
+
continue;
|
|
164
|
+
invocations.splice(closedIndex, 1);
|
|
165
|
+
total -= 1;
|
|
166
|
+
if (!invocations.length)
|
|
167
|
+
state.toolInvocations.delete(key);
|
|
168
|
+
removed = true;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
if (!removed)
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
createToolInvocation(invocations, piboSessionId, eventId, toolCallId, transition) {
|
|
176
|
+
const localNext = invocations.reduce((maximum, invocation) => Math.max(maximum, invocation.ordinal), -1) + 1;
|
|
177
|
+
const ordinal = transition
|
|
178
|
+
? this.highWaterStore?.claimOrAttachOutputToolInvocation?.(transition)
|
|
179
|
+
?? this.highWaterStore?.claimOutputToolInvocationOrdinal?.(piboSessionId, eventId, toolCallId)
|
|
180
|
+
?? localNext
|
|
181
|
+
: localNext;
|
|
182
|
+
return createInvocation(invocations, ordinal);
|
|
183
|
+
}
|
|
184
|
+
updateSegmentLifecycle(state, event, key, segmentEventId) {
|
|
185
|
+
if (key && !state.segmentEventIds.has(key)) {
|
|
186
|
+
state.segmentEventIds.set(key, segmentEventId);
|
|
187
|
+
}
|
|
188
|
+
if (event.type === "message_finished" || event.type === "session_error") {
|
|
189
|
+
const boundaryEventId = event.eventId;
|
|
190
|
+
for (const segmentKey of state.positions.keys()) {
|
|
191
|
+
if (event.type === "session_error" && !boundaryEventId)
|
|
192
|
+
this.completeSegment(state, segmentKey);
|
|
193
|
+
else if (boundaryEventId && state.segmentEventIds.get(segmentKey) === boundaryEventId)
|
|
194
|
+
this.completeSegment(state, segmentKey);
|
|
195
|
+
}
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (!key)
|
|
199
|
+
return;
|
|
200
|
+
if (segmentCompletesWith(event))
|
|
201
|
+
this.completeSegment(state, key);
|
|
202
|
+
else if (segmentRemainsActive(event)) {
|
|
203
|
+
state.completedSegments.delete(key);
|
|
204
|
+
state.activeSegments.add(key);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
completeSegment(state, key) {
|
|
208
|
+
state.activeSegments.delete(key);
|
|
209
|
+
state.completedSegments.delete(key);
|
|
210
|
+
state.completedSegments.add(key);
|
|
211
|
+
while (state.completedSegments.size > MAX_RECENT_SEGMENTS_PER_SESSION) {
|
|
212
|
+
const oldest = state.completedSegments.values().next().value;
|
|
213
|
+
if (oldest === undefined)
|
|
214
|
+
break;
|
|
215
|
+
state.completedSegments.delete(oldest);
|
|
216
|
+
if (!state.activeSegments.has(oldest)) {
|
|
217
|
+
state.positions.delete(oldest);
|
|
218
|
+
state.segmentEventIds.delete(oldest);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
trimSessions() {
|
|
223
|
+
let removable = [...this.sessions.entries()].filter(([, state]) => sessionCanBeEvicted(state));
|
|
224
|
+
while (removable.length > MAX_TRACKED_SESSIONS) {
|
|
225
|
+
const [sessionId] = removable.shift();
|
|
226
|
+
this.sessions.delete(sessionId);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
trackActiveTurn(state, event) {
|
|
230
|
+
if (event.type === "message_started" && event.eventId)
|
|
231
|
+
state.activeEventId = event.eventId;
|
|
232
|
+
}
|
|
233
|
+
nextSequence(piboSessionId, state) {
|
|
234
|
+
const wallClockSequence = Math.floor(this.now()) * RENDER_SEQUENCE_SLOTS_PER_MILLISECOND;
|
|
235
|
+
const minimum = Math.max(wallClockSequence, state.lastSequence + 1);
|
|
236
|
+
const next = this.highWaterStore?.claimOutputRenderSequence(piboSessionId, minimum) ?? minimum;
|
|
237
|
+
state.lastSequence = Math.max(state.lastSequence, next);
|
|
238
|
+
return next;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function createInvocation(invocations, ordinal) {
|
|
242
|
+
const invocation = { ordinal, seen: new Set(), closed: false, persistedIdentity: false };
|
|
243
|
+
invocations.push(invocation);
|
|
244
|
+
return invocation;
|
|
245
|
+
}
|
|
246
|
+
function segmentRemainsActive(event) {
|
|
247
|
+
return event.type === "message_started"
|
|
248
|
+
|| event.type === "assistant_delta"
|
|
249
|
+
|| event.type === "thinking_started"
|
|
250
|
+
|| event.type === "thinking_delta"
|
|
251
|
+
|| event.type === "tool_call"
|
|
252
|
+
|| event.type === "tool_execution_started"
|
|
253
|
+
|| event.type === "tool_execution_updated";
|
|
254
|
+
}
|
|
255
|
+
function segmentCompletesWith(event) {
|
|
256
|
+
return event.type === "assistant_message"
|
|
257
|
+
|| event.type === "thinking_finished"
|
|
258
|
+
|| event.type === "tool_execution_finished"
|
|
259
|
+
|| event.type === "execution_result"
|
|
260
|
+
|| event.type === "compaction_end"
|
|
261
|
+
|| event.type === "approval_resolved"
|
|
262
|
+
|| event.type === "user_input_resolved";
|
|
263
|
+
}
|
|
264
|
+
function sessionCanBeEvicted(state) {
|
|
265
|
+
return state.activeEventId === undefined && state.activeSegments.size === 0;
|
|
266
|
+
}
|
|
267
|
+
export function validToolInvocationOrdinal(value) {
|
|
268
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
269
|
+
}
|
|
270
|
+
function isToolIdentityEvent(event) {
|
|
271
|
+
return event.type === "tool_call"
|
|
272
|
+
|| event.type === "tool_execution_started"
|
|
273
|
+
|| event.type === "tool_execution_updated"
|
|
274
|
+
|| event.type === "tool_execution_finished"
|
|
275
|
+
|| event.type === "subagent_session";
|
|
276
|
+
}
|
|
277
|
+
function toolInvocationCounterKey(eventId, toolCallId) {
|
|
278
|
+
return JSON.stringify([eventId ?? "unscoped", toolCallId]);
|
|
279
|
+
}
|
|
280
|
+
function toolCallFingerprint(event) {
|
|
281
|
+
return createHash("sha256")
|
|
282
|
+
.update(JSON.stringify([event.toolName, event.argsComplete, event.args]))
|
|
283
|
+
.digest("hex");
|
|
284
|
+
}
|
|
285
|
+
export function validRenderSequence(value) {
|
|
286
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
287
|
+
}
|
|
288
|
+
function outputRenderSegmentKey(event, activeEventId) {
|
|
289
|
+
const eventId = "eventId" in event && event.eventId ? event.eventId : activeEventId;
|
|
290
|
+
switch (event.type) {
|
|
291
|
+
case "message_queued":
|
|
292
|
+
case "message_steered":
|
|
293
|
+
return eventId ? `user:${eventId}` : undefined;
|
|
294
|
+
case "message_started":
|
|
295
|
+
case "message_finished":
|
|
296
|
+
return eventId ? `turn:${eventId}` : undefined;
|
|
297
|
+
case "assistant_delta":
|
|
298
|
+
case "assistant_message":
|
|
299
|
+
return `assistant:${eventId ?? "active"}:${event.assistantIndex ?? event.contentIndex ?? 0}`;
|
|
300
|
+
case "thinking_started":
|
|
301
|
+
case "thinking_delta":
|
|
302
|
+
case "thinking_finished":
|
|
303
|
+
return `reasoning:${eventId ?? "active"}:${event.thinkingIndex ?? event.contentIndex ?? 0}`;
|
|
304
|
+
case "tool_call":
|
|
305
|
+
case "tool_execution_started":
|
|
306
|
+
case "tool_execution_updated":
|
|
307
|
+
case "tool_execution_finished":
|
|
308
|
+
return `tool:${eventId ?? "active"}:${event.toolCallId}:${event.toolInvocationOrdinal ?? 0}`;
|
|
309
|
+
case "subagent_session":
|
|
310
|
+
return event.toolCallId ? `tool:${eventId ?? "active"}:${event.toolCallId}:${event.toolInvocationOrdinal ?? 0}` : undefined;
|
|
311
|
+
case "compaction_start":
|
|
312
|
+
case "compaction_end":
|
|
313
|
+
return `compaction:${eventId ?? "active"}:${event.compactionIndex ?? 0}`;
|
|
314
|
+
case "approval_requested":
|
|
315
|
+
return `approval:${eventId ?? "active"}:${event.request.requestId}`;
|
|
316
|
+
case "approval_resolved":
|
|
317
|
+
return `approval:${eventId ?? "active"}:${event.requestId}`;
|
|
318
|
+
case "user_input_requested":
|
|
319
|
+
return `user-input:${eventId ?? "active"}:${event.request.requestId}`;
|
|
320
|
+
case "user_input_resolved":
|
|
321
|
+
return `user-input:${eventId ?? "active"}:${event.requestId}`;
|
|
322
|
+
case "assistant_usage":
|
|
323
|
+
return `usage:${eventId ?? "active"}:${event.usageIndex ?? 0}`;
|
|
324
|
+
case "session_error":
|
|
325
|
+
return `error:${eventId ?? "active"}`;
|
|
326
|
+
case "execution_result":
|
|
327
|
+
return `execution:${eventId ?? event.action}`;
|
|
328
|
+
default:
|
|
329
|
+
return undefined;
|
|
330
|
+
}
|
|
331
|
+
}
|
package/dist/core/profiles.js
CHANGED
|
@@ -13,10 +13,14 @@ export function normalizeToolProfile(tool) {
|
|
|
13
13
|
}
|
|
14
14
|
export const DEFAULT_AGENT_RUNTIME_INSTANCE_ID = "pi";
|
|
15
15
|
export const DEFAULT_BUILTIN_TOOL_NAMES = ["read", "bash", "edit", "write"];
|
|
16
|
+
function cloneModelProfiles(models) {
|
|
17
|
+
return models.map((model) => ({ ...model }));
|
|
18
|
+
}
|
|
16
19
|
function cloneSubagentProfile(subagent) {
|
|
17
20
|
return {
|
|
18
21
|
...subagent,
|
|
19
22
|
...(subagent.model ? { model: { ...subagent.model } } : {}),
|
|
23
|
+
...(subagent.modelFallbacks ? { modelFallbacks: cloneModelProfiles(subagent.modelFallbacks) } : {}),
|
|
20
24
|
...(subagent.runtimeOptions ? { runtimeOptions: structuredClone(subagent.runtimeOptions) } : {}),
|
|
21
25
|
};
|
|
22
26
|
}
|
|
@@ -28,6 +32,7 @@ export class InitialSessionContext {
|
|
|
28
32
|
parentSessionId;
|
|
29
33
|
model;
|
|
30
34
|
mainModel;
|
|
35
|
+
mainModelFallbacks;
|
|
31
36
|
subagentModel;
|
|
32
37
|
thinkingLevel;
|
|
33
38
|
mainThinkingLevel;
|
|
@@ -41,6 +46,7 @@ export class InitialSessionContext {
|
|
|
41
46
|
mcpServers;
|
|
42
47
|
piPackages;
|
|
43
48
|
contextFiles;
|
|
49
|
+
diagnostics;
|
|
44
50
|
builtinTools;
|
|
45
51
|
builtinToolNames;
|
|
46
52
|
autoContextFiles;
|
|
@@ -54,6 +60,7 @@ export class InitialSessionContext {
|
|
|
54
60
|
this.parentSessionId = options.parentSessionId;
|
|
55
61
|
this.model = options.model ? { ...options.model } : undefined;
|
|
56
62
|
this.mainModel = options.mainModel ? { ...options.mainModel } : undefined;
|
|
63
|
+
this.mainModelFallbacks = cloneModelProfiles(options.mainModelFallbacks ?? []);
|
|
57
64
|
this.subagentModel = options.subagentModel ? { ...options.subagentModel } : undefined;
|
|
58
65
|
this.thinkingLevel = options.thinkingLevel;
|
|
59
66
|
this.mainThinkingLevel = options.mainThinkingLevel;
|
|
@@ -67,6 +74,7 @@ export class InitialSessionContext {
|
|
|
67
74
|
this.mcpServers = [...(options.mcpServers ?? [])];
|
|
68
75
|
this.piPackages = [...(options.piPackages ?? [])];
|
|
69
76
|
this.contextFiles = [...(options.contextFiles ?? [])];
|
|
77
|
+
this.diagnostics = (options.diagnostics ?? []).map((diagnostic) => ({ ...diagnostic }));
|
|
70
78
|
this.builtinTools = options.builtinTools ?? "default";
|
|
71
79
|
this.builtinToolNames = [...(options.builtinToolNames ?? DEFAULT_BUILTIN_TOOL_NAMES)];
|
|
72
80
|
this.autoContextFiles = options.autoContextFiles ?? true;
|
|
@@ -82,6 +90,7 @@ export class InitialSessionContextBuilder {
|
|
|
82
90
|
parentSessionId;
|
|
83
91
|
model;
|
|
84
92
|
mainModel;
|
|
93
|
+
mainModelFallbacks = [];
|
|
85
94
|
subagentModel;
|
|
86
95
|
thinkingLevel;
|
|
87
96
|
mainThinkingLevel;
|
|
@@ -95,6 +104,7 @@ export class InitialSessionContextBuilder {
|
|
|
95
104
|
mcpServers = [];
|
|
96
105
|
piPackages = [];
|
|
97
106
|
contextFiles = [];
|
|
107
|
+
diagnostics = [];
|
|
98
108
|
builtinTools = "default";
|
|
99
109
|
builtinToolNames = [...DEFAULT_BUILTIN_TOOL_NAMES];
|
|
100
110
|
autoContextFiles = true;
|
|
@@ -124,6 +134,10 @@ export class InitialSessionContextBuilder {
|
|
|
124
134
|
this.mainModel = { ...model };
|
|
125
135
|
return this;
|
|
126
136
|
}
|
|
137
|
+
withMainModelFallbacks(models) {
|
|
138
|
+
this.mainModelFallbacks = cloneModelProfiles(models);
|
|
139
|
+
return this;
|
|
140
|
+
}
|
|
127
141
|
withSubagentModel(model) {
|
|
128
142
|
this.subagentModel = { ...model };
|
|
129
143
|
return this;
|
|
@@ -220,6 +234,14 @@ export class InitialSessionContextBuilder {
|
|
|
220
234
|
this.contextFiles.push(...contextFiles);
|
|
221
235
|
return this;
|
|
222
236
|
}
|
|
237
|
+
addDiagnostic(diagnostic) {
|
|
238
|
+
this.diagnostics.push({ ...diagnostic });
|
|
239
|
+
return this;
|
|
240
|
+
}
|
|
241
|
+
addDiagnostics(diagnostics) {
|
|
242
|
+
this.diagnostics.push(...diagnostics.map((diagnostic) => ({ ...diagnostic })));
|
|
243
|
+
return this;
|
|
244
|
+
}
|
|
223
245
|
createSession() {
|
|
224
246
|
return new InitialSessionContext({
|
|
225
247
|
profileName: this.profileName,
|
|
@@ -229,6 +251,7 @@ export class InitialSessionContextBuilder {
|
|
|
229
251
|
parentSessionId: this.parentSessionId,
|
|
230
252
|
model: this.model,
|
|
231
253
|
mainModel: this.mainModel,
|
|
254
|
+
mainModelFallbacks: this.mainModelFallbacks,
|
|
232
255
|
subagentModel: this.subagentModel,
|
|
233
256
|
thinkingLevel: this.thinkingLevel,
|
|
234
257
|
mainThinkingLevel: this.mainThinkingLevel,
|
|
@@ -242,6 +265,7 @@ export class InitialSessionContextBuilder {
|
|
|
242
265
|
mcpServers: this.mcpServers,
|
|
243
266
|
piPackages: this.piPackages,
|
|
244
267
|
contextFiles: this.contextFiles,
|
|
268
|
+
diagnostics: this.diagnostics,
|
|
245
269
|
builtinTools: this.builtinTools,
|
|
246
270
|
builtinToolNames: this.builtinToolNames,
|
|
247
271
|
autoContextFiles: this.autoContextFiles,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isRetryableAssistantError } from "@earendil-works/pi-ai";
|
|
2
|
-
import { classifySessionErrorMessage } from "./session-errors.js";
|
|
2
|
+
import { classifySessionErrorMessage, normalizeSessionErrorDetails } from "./session-errors.js";
|
|
3
3
|
export const PIBO_PROVIDER_RECOVERY_MESSAGE_TYPE = "pibo-provider-recovery-resume";
|
|
4
4
|
export const PIBO_PROVIDER_RECOVERY_PROMPT = "Continue the interrupted task autonomously from the existing session state. The previous provider request failed transiently. Do not wait for more user input, ask the user to repeat the request, or mention this recovery message unless the failure affects the result.";
|
|
5
5
|
const DEFAULT_RECOVERY_BASE_DELAY_MS = 2_000;
|
|
@@ -44,6 +44,12 @@ export function isRetryablePiboProviderError(error) {
|
|
|
44
44
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
45
45
|
return isRetryablePiboAssistantError({ stopReason: "error", errorMessage });
|
|
46
46
|
}
|
|
47
|
+
export function isPiboProviderFallbackError(message, details) {
|
|
48
|
+
const normalized = normalizeSessionErrorDetails(message, details);
|
|
49
|
+
return normalized.origin === "provider"
|
|
50
|
+
&& normalized.category !== "context_overflow"
|
|
51
|
+
&& normalized.category !== "runtime_abort";
|
|
52
|
+
}
|
|
47
53
|
export async function waitForPiboProviderRecovery(attempt, settings, signal) {
|
|
48
54
|
if (signal?.aborted)
|
|
49
55
|
throw new PiboProviderRecoveryCancelledError();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { selectRequestedModelProfile } from "./model-defaults.js";
|
|
1
|
+
import { selectRequestedModelProfile, sanitizeModelProfile } from "./model-defaults.js";
|
|
2
2
|
import { InitialSessionContext } from "./profiles.js";
|
|
3
|
+
export const PIBO_INITIAL_MODEL_FALLBACKS_METADATA_KEY = "initialModelFallbacks";
|
|
3
4
|
export function resolvePiboSessionActiveModel(input) {
|
|
4
5
|
if (input.piboSession.activeModel)
|
|
5
6
|
return cloneModelProfile(input.piboSession.activeModel);
|
|
@@ -8,6 +9,21 @@ export function resolvePiboSessionActiveModel(input) {
|
|
|
8
9
|
: profileWithSessionIds(input.profile, input.piboSession.piSessionId);
|
|
9
10
|
return selectRequestedModelProfile(sessionProfile, input.modelDefaults ?? {});
|
|
10
11
|
}
|
|
12
|
+
export function resolvePiboSessionModelFallbacks(input) {
|
|
13
|
+
const persisted = input.piboSession.metadata?.[PIBO_INITIAL_MODEL_FALLBACKS_METADATA_KEY];
|
|
14
|
+
const configured = persisted !== undefined
|
|
15
|
+
? sanitizeModelFallbacks(persisted)
|
|
16
|
+
: input.parentPiSessionId
|
|
17
|
+
? []
|
|
18
|
+
: sanitizeModelFallbacks(input.profile.mainModelFallbacks);
|
|
19
|
+
return configured.filter((model) => !sameModel(model, input.activeModel));
|
|
20
|
+
}
|
|
21
|
+
export function withPiboSessionModelFallbacksMetadata(metadata, models) {
|
|
22
|
+
return {
|
|
23
|
+
...(metadata ?? {}),
|
|
24
|
+
[PIBO_INITIAL_MODEL_FALLBACKS_METADATA_KEY]: sanitizeModelFallbacks(models),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
11
27
|
function profileWithSessionIds(profile, piSessionId, parentPiSessionId) {
|
|
12
28
|
return new InitialSessionContext({
|
|
13
29
|
profileName: profile.profileName,
|
|
@@ -15,6 +31,7 @@ function profileWithSessionIds(profile, piSessionId, parentPiSessionId) {
|
|
|
15
31
|
parentSessionId: parentPiSessionId,
|
|
16
32
|
model: profile.model,
|
|
17
33
|
mainModel: profile.mainModel,
|
|
34
|
+
mainModelFallbacks: profile.mainModelFallbacks,
|
|
18
35
|
subagentModel: profile.subagentModel,
|
|
19
36
|
thinkingLevel: profile.thinkingLevel,
|
|
20
37
|
mainThinkingLevel: profile.mainThinkingLevel,
|
|
@@ -27,6 +44,7 @@ function profileWithSessionIds(profile, piSessionId, parentPiSessionId) {
|
|
|
27
44
|
subagents: profile.subagents,
|
|
28
45
|
mcpServers: profile.mcpServers,
|
|
29
46
|
contextFiles: profile.contextFiles,
|
|
47
|
+
diagnostics: profile.diagnostics,
|
|
30
48
|
piPackages: profile.piPackages,
|
|
31
49
|
builtinTools: profile.builtinTools,
|
|
32
50
|
builtinToolNames: profile.builtinToolNames,
|
|
@@ -38,3 +56,21 @@ function profileWithSessionIds(profile, piSessionId, parentPiSessionId) {
|
|
|
38
56
|
function cloneModelProfile(model) {
|
|
39
57
|
return { provider: model.provider, id: model.id };
|
|
40
58
|
}
|
|
59
|
+
function sanitizeModelFallbacks(value) {
|
|
60
|
+
if (!Array.isArray(value))
|
|
61
|
+
return [];
|
|
62
|
+
const seen = new Set();
|
|
63
|
+
return value.flatMap((item) => {
|
|
64
|
+
const model = sanitizeModelProfile(item);
|
|
65
|
+
if (!model)
|
|
66
|
+
return [];
|
|
67
|
+
const key = `${model.provider}\u0000${model.id}`;
|
|
68
|
+
if (seen.has(key))
|
|
69
|
+
return [];
|
|
70
|
+
seen.add(key);
|
|
71
|
+
return [model];
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function sameModel(left, right) {
|
|
75
|
+
return Boolean(right && left.provider === right.provider && left.id === right.id);
|
|
76
|
+
}
|