@pasko70/pibo 2.5.1 → 3.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/context/pibo-native-tooling.md +1 -0
- package/dist/agent-runtime/contract.js +2 -1
- package/dist/agent-runtime/history.js +53 -1
- package/dist/agent-runtime/routed-session.js +100 -26
- package/dist/agent-runtimes/codex-native/adapter.js +307 -31
- package/dist/agent-runtimes/codex-native/client.js +22 -3
- package/dist/agent-runtimes/codex-native/first-use.js +294 -0
- package/dist/agent-runtimes/codex-native/history.js +26 -7
- package/dist/agent-runtimes/codex-native/models.js +48 -1
- package/dist/agent-runtimes/codex-native/process.js +15 -1
- package/dist/agent-runtimes/codex-native/thread.js +75 -31
- package/dist/agent-runtimes/codex-native/turn.js +30 -0
- package/dist/agent-runtimes/history-proof.js +8 -0
- package/dist/agent-runtimes/omp/adapter.js +61 -14
- package/dist/agent-runtimes/omp/history.js +189 -34
- package/dist/agent-runtimes/omp/thread.js +82 -36
- package/dist/agent-runtimes/pi/adapter.js +30 -3
- package/dist/agent-runtimes/pi/history.js +66 -5
- package/dist/agent-runtimes/pi/openai-response-observer.js +102 -0
- package/dist/agent-runtimes/pi/routed-session.js +344 -37
- package/dist/apps/chat/agent-store.js +3 -0
- package/dist/apps/chat/chat-api-routes.js +1 -1
- package/dist/apps/chat/chat-files.js +130 -9
- package/dist/apps/chat/chat-request-normalizers.js +6 -0
- package/dist/apps/chat/chat-settings-routes.js +27 -2
- package/dist/apps/chat/chat-speech.js +100 -0
- package/dist/apps/chat/data/chat-data-mappers.js +25 -5
- package/dist/apps/chat/data/timeline-query-service.js +89 -1
- package/dist/apps/chat/trace-v2.js +210 -8
- package/dist/apps/chat/trace.js +3 -0
- package/dist/apps/chat/web-app.js +108 -3
- package/dist/apps/chat-ui/assets/{dist-GS8xUdfg.js → dist-BBDMeU5-.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-BpVSf6ZV.js → dist-BDIATCQt.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CRv7fzi3.js → dist-JnW4v8UE.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CbBXbGVi.js → dist-lRiLP9rr.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-HC0xQ92C.js → dist-mqCviI-c.js} +1 -1
- package/dist/apps/chat-ui/assets/index-0XOOVxLP.js +228 -0
- package/dist/apps/chat-ui/assets/index-DV7_CgsC.css +1 -0
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/index-Dtw2Z7jz.js +43 -0
- package/dist/apps/chat-vscode-web/assets/{index-CF1-9PKU.css → index-SluZr_-r.css} +1 -1
- package/dist/apps/chat-vscode-web/index.html +2 -2
- package/dist/apps/vscode-artifacts/latest.vsix +0 -0
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-3.0.0.vsix +0 -0
- package/dist/cli.js +49 -5
- package/dist/config/config.js +59 -8
- package/dist/core/preview-server-settings.js +20 -0
- package/dist/core/profiles.js +1 -0
- package/dist/core/session-router.js +476 -44
- package/dist/core/user-settings.js +10 -0
- package/dist/data/payload-store.js +57 -1
- package/dist/data/schema.js +18 -1
- package/dist/debug/trace.js +5 -0
- package/dist/gateway/client.js +368 -65
- package/dist/gateway/protocol.js +2 -0
- package/dist/gateway/server.js +8 -0
- package/dist/gateway/web.js +2 -0
- package/dist/index.js +1 -1
- package/dist/loops/store.js +6 -4
- package/dist/mcp/config.js +89 -17
- package/dist/mcp/daemon-client.js +473 -153
- package/dist/mcp/daemon.js +401 -37
- package/dist/plugins/builtin.js +2 -2
- package/dist/plugins/codex-native.js +2 -0
- package/dist/plugins/registry.js +260 -1
- package/dist/previews/cli.js +345 -0
- package/dist/previews/config.js +61 -0
- package/dist/previews/manager.js +644 -0
- package/dist/previews/network.js +198 -0
- package/dist/previews/plugin.js +11 -0
- package/dist/previews/process-supervisor.js +38 -0
- package/dist/previews/proxy.js +387 -0
- package/dist/previews/store.js +750 -0
- package/dist/previews/types.js +1 -0
- package/dist/previews/web-app.js +446 -0
- package/dist/runs/registry.js +22 -0
- package/dist/runs/tools.js +46 -7
- package/dist/session-ui/terminalRows.js +187 -13
- package/dist/sessions/pibo-data-store.js +37 -0
- package/dist/sessions/runtime-binding-persistence.js +34 -0
- package/dist/sessions/runtime-binding.js +6 -0
- package/dist/sessions/sqlite-store.js +37 -0
- package/dist/shared/trace-engine.js +12 -64
- package/dist/shared/trace-event-projection.js +16 -0
- package/dist/shared/trace-history.js +684 -184
- package/dist/shared/trace-limits.js +7 -0
- package/dist/shared/trace-live-patch.js +61 -0
- package/dist/shared/trace-tool-identity.js +43 -0
- package/dist/speech/openai-codex-realtime-call-proxy.js +248 -0
- package/dist/speech/openai-codex.js +407 -0
- package/dist/speech/types.js +8 -0
- package/dist/subagents/context.js +5 -3
- package/dist/subagents/observations.js +94 -6
- package/dist/subagents/tool.js +103 -20
- package/dist/tools/codex-image-artifacts.js +24 -0
- package/dist/tools/codex-image-generation.js +4 -22
- package/dist/web/channel.js +88 -4
- package/dist/web/http.js +2 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/dist/apps/chat-ui/assets/index-0WZI2phJ.css +0 -1
- package/dist/apps/chat-ui/assets/index-DT5jCQBP.js +0 -228
- package/dist/apps/chat-vscode-web/assets/index-DvTSSvzN.js +0 -43
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-2.5.1.vsix +0 -0
|
@@ -1,5 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { historyReconciliationDigest } from "../../agent-runtime/history.js";
|
|
2
|
+
import { TRACE_RECONCILIATION_ENTRY_CAP } from "../../shared/trace-limits.js";
|
|
3
|
+
import { OMP_ADAPTER_ID, OMP_ADAPTER_VERSION } from "./thread.js";
|
|
4
|
+
const OMP_HISTORY_CURSOR_PREFIX = "omp-history:";
|
|
5
|
+
const OMP_HISTORY_PAGE_LIMIT = 200;
|
|
6
|
+
const DEFAULT_HISTORY_LIMIT = 100;
|
|
7
|
+
class OmpHistoryResponseError extends Error {
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "OmpHistoryResponseError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
3
13
|
function isRecord(value) {
|
|
4
14
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
5
15
|
}
|
|
@@ -16,15 +26,26 @@ export function inspectOmpHistory(input, runtimeInstanceId) {
|
|
|
16
26
|
};
|
|
17
27
|
}
|
|
18
28
|
function messageEntryRole(message) {
|
|
19
|
-
if (isRecord(message)) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return role;
|
|
24
|
-
}
|
|
29
|
+
if (isRecord(message) && typeof message.role === "string") {
|
|
30
|
+
const role = message.role;
|
|
31
|
+
if (role === "user" || role === "assistant" || role === "tool" || role === "system")
|
|
32
|
+
return role;
|
|
25
33
|
}
|
|
26
34
|
return "system";
|
|
27
35
|
}
|
|
36
|
+
function messageString(message, keys) {
|
|
37
|
+
if (!isRecord(message))
|
|
38
|
+
return undefined;
|
|
39
|
+
for (const key of keys) {
|
|
40
|
+
const value = message[key];
|
|
41
|
+
if (typeof value === "string" && value.trim())
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
function messageEntryId(message) {
|
|
47
|
+
return messageString(message, ["entryId", "id", "messageId"]);
|
|
48
|
+
}
|
|
28
49
|
function messageText(message) {
|
|
29
50
|
if (typeof message === "string")
|
|
30
51
|
return message;
|
|
@@ -44,48 +65,187 @@ function messageText(message) {
|
|
|
44
65
|
}
|
|
45
66
|
return "";
|
|
46
67
|
}
|
|
47
|
-
function
|
|
68
|
+
function jsonValue(value, depth = 0) {
|
|
69
|
+
if (depth > 32)
|
|
70
|
+
return "[maximum depth reached]";
|
|
71
|
+
if (value === null || typeof value === "boolean" || typeof value === "string")
|
|
72
|
+
return value;
|
|
73
|
+
if (typeof value === "number")
|
|
74
|
+
return Number.isFinite(value) ? value : String(value);
|
|
75
|
+
if (Array.isArray(value))
|
|
76
|
+
return value.map((child) => jsonValue(child, depth + 1));
|
|
77
|
+
if (isRecord(value)) {
|
|
78
|
+
const result = {};
|
|
79
|
+
for (const [key, child] of Object.entries(value)) {
|
|
80
|
+
if (child !== undefined)
|
|
81
|
+
result[key] = jsonValue(child, depth + 1);
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
return String(value);
|
|
86
|
+
}
|
|
87
|
+
function toHistoryEntry(message, sequence, binding, historyScopeId) {
|
|
48
88
|
const role = messageEntryRole(message);
|
|
49
|
-
const content = messageText(message);
|
|
89
|
+
const content = messageText(message) || "[empty message]";
|
|
90
|
+
const nativeEntryId = messageEntryId(message);
|
|
91
|
+
const nativeTurnId = messageString(message, ["turnId", "nativeTurnId"]);
|
|
92
|
+
const nativeSessionId = binding.nativeSessionId ?? "omp";
|
|
93
|
+
const historyPosition = `omp:${nativeSessionId}:message:${sequence}`;
|
|
94
|
+
const toolCallId = role === "tool"
|
|
95
|
+
? messageString(message, ["toolCallId", "callId"]) ?? `omp-tool:${nativeSessionId}:${sequence}`
|
|
96
|
+
: undefined;
|
|
97
|
+
const toolName = role === "tool" ? messageString(message, ["toolName", "name"]) ?? "OMP Tool" : undefined;
|
|
50
98
|
return {
|
|
51
|
-
id:
|
|
99
|
+
id: `omp:${nativeSessionId}:message:${sequence}`,
|
|
52
100
|
type: "message",
|
|
53
101
|
source: "native",
|
|
54
102
|
createdAt: isRecord(message) && typeof message.timestamp === "string"
|
|
55
103
|
? message.timestamp
|
|
56
|
-
: new Date().toISOString(),
|
|
104
|
+
: new Date(0).toISOString(),
|
|
57
105
|
sequence,
|
|
106
|
+
historyPosition,
|
|
107
|
+
historyScopeId,
|
|
108
|
+
...(nativeTurnId ? { nativeTurnId } : {}),
|
|
109
|
+
...(nativeEntryId ? { nativeEntryId } : {}),
|
|
58
110
|
role,
|
|
59
|
-
content
|
|
111
|
+
content,
|
|
112
|
+
...(toolCallId ? {
|
|
113
|
+
toolCallId,
|
|
114
|
+
toolName,
|
|
115
|
+
result: isRecord(message) && message.result !== undefined ? jsonValue(message.result) : content,
|
|
116
|
+
isError: isRecord(message) && message.isError === true,
|
|
117
|
+
status: isRecord(message) && message.isError === true ? "error" : "complete",
|
|
118
|
+
} : {}),
|
|
60
119
|
};
|
|
61
120
|
}
|
|
62
|
-
|
|
121
|
+
function selectedLimit(value) {
|
|
122
|
+
if (value === undefined)
|
|
123
|
+
return DEFAULT_HISTORY_LIMIT;
|
|
124
|
+
if (!Number.isSafeInteger(value) || value <= 0)
|
|
125
|
+
throw new OmpHistoryResponseError("OMP native history limit must be a positive integer.");
|
|
126
|
+
return Math.min(value, OMP_HISTORY_PAGE_LIMIT);
|
|
127
|
+
}
|
|
128
|
+
function encodeCursor(cursor) {
|
|
129
|
+
return `${OMP_HISTORY_CURSOR_PREFIX}${Buffer.from(JSON.stringify(cursor), "utf8").toString("base64url")}`;
|
|
130
|
+
}
|
|
131
|
+
function decodeCursor(value, nativeSessionId) {
|
|
132
|
+
if (!value)
|
|
133
|
+
return undefined;
|
|
134
|
+
if (!value.startsWith(OMP_HISTORY_CURSOR_PREFIX))
|
|
135
|
+
throw new OmpHistoryResponseError("Invalid OMP native history cursor.");
|
|
63
136
|
try {
|
|
137
|
+
const parsed = JSON.parse(Buffer.from(value.slice(OMP_HISTORY_CURSOR_PREFIX.length), "base64url").toString("utf8"));
|
|
138
|
+
if (parsed.v !== 1
|
|
139
|
+
|| parsed.nativeSessionId !== nativeSessionId
|
|
140
|
+
|| !Number.isSafeInteger(parsed.beforeIndex)
|
|
141
|
+
|| Number(parsed.beforeIndex) < 0)
|
|
142
|
+
throw new Error("invalid cursor");
|
|
143
|
+
return parsed;
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
throw new OmpHistoryResponseError("Invalid OMP native history cursor.");
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function messagesPageData(result) {
|
|
150
|
+
const data = isRecord(result) ? result.data : undefined;
|
|
151
|
+
if (!isRecord(data)
|
|
152
|
+
|| !Array.isArray(data.messages)
|
|
153
|
+
|| !Number.isSafeInteger(data.totalMessages)
|
|
154
|
+
|| Number(data.totalMessages) < 0)
|
|
155
|
+
throw new OmpHistoryResponseError("OMP returned an invalid native history page.");
|
|
156
|
+
return {
|
|
157
|
+
messages: data.messages,
|
|
158
|
+
...(typeof data.nextCursor === "string" && data.nextCursor.length > 0 ? { nextCursor: data.nextCursor } : {}),
|
|
159
|
+
totalMessages: Number(data.totalMessages),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
async function readCompleteOmpMessages(client) {
|
|
163
|
+
const pages = [];
|
|
164
|
+
const seenCursors = new Set();
|
|
165
|
+
let cursor;
|
|
166
|
+
let expectedTotal;
|
|
167
|
+
let collected = 0;
|
|
168
|
+
let pageRequests = 0;
|
|
169
|
+
do {
|
|
170
|
+
pageRequests += 1;
|
|
171
|
+
if (pageRequests > TRACE_RECONCILIATION_ENTRY_CAP + 1) {
|
|
172
|
+
throw new OmpHistoryResponseError("OMP native history exceeded the bounded provider page sequence.");
|
|
173
|
+
}
|
|
64
174
|
const result = await client.request({
|
|
65
175
|
type: "get_messages_page",
|
|
66
|
-
...(
|
|
67
|
-
|
|
176
|
+
...(cursor ? { cursor } : {}),
|
|
177
|
+
limit: OMP_HISTORY_PAGE_LIMIT,
|
|
68
178
|
}, "get_messages_page");
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
179
|
+
const page = messagesPageData(result);
|
|
180
|
+
expectedTotal ??= page.totalMessages;
|
|
181
|
+
if (page.totalMessages !== expectedTotal) {
|
|
182
|
+
throw new OmpHistoryResponseError("OMP native history changed while its proof was collected.");
|
|
183
|
+
}
|
|
184
|
+
if (page.messages.length > OMP_HISTORY_PAGE_LIMIT) {
|
|
185
|
+
throw new OmpHistoryResponseError("OMP native history exceeded the requested provider page bound.");
|
|
186
|
+
}
|
|
187
|
+
if (expectedTotal > TRACE_RECONCILIATION_ENTRY_CAP) {
|
|
188
|
+
throw new OmpHistoryResponseError(`OMP native history exceeds the ${TRACE_RECONCILIATION_ENTRY_CAP}-entry proof bound.`);
|
|
189
|
+
}
|
|
190
|
+
if (collected + page.messages.length > expectedTotal) {
|
|
191
|
+
throw new OmpHistoryResponseError("OMP native history pages exceed their declared total.");
|
|
72
192
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
193
|
+
pages.push(page.messages);
|
|
194
|
+
collected += page.messages.length;
|
|
195
|
+
if (collected === expectedTotal) {
|
|
196
|
+
if (page.nextCursor) {
|
|
197
|
+
throw new OmpHistoryResponseError("OMP native history cursor extends beyond its declared total.");
|
|
198
|
+
}
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
if (!page.nextCursor || seenCursors.has(page.nextCursor)) {
|
|
202
|
+
throw new OmpHistoryResponseError("OMP native history cannot prove a complete bounded page sequence.");
|
|
203
|
+
}
|
|
204
|
+
seenCursors.add(page.nextCursor);
|
|
205
|
+
cursor = page.nextCursor;
|
|
206
|
+
} while (true);
|
|
207
|
+
if (collected !== expectedTotal)
|
|
208
|
+
throw new OmpHistoryResponseError("OMP native history proof is incomplete.");
|
|
209
|
+
return pages.reverse().flat();
|
|
210
|
+
}
|
|
211
|
+
export async function readOmpHistory(client, input, runtimeInstanceId, binding) {
|
|
212
|
+
const nativeSessionId = binding.nativeSessionId ?? "omp";
|
|
213
|
+
const historyScopeId = `omp:${runtimeInstanceId}:${nativeSessionId}`;
|
|
214
|
+
try {
|
|
215
|
+
const messages = await readCompleteOmpMessages(client);
|
|
216
|
+
const allEntries = messages.map((message, sequence) => toHistoryEntry(message, sequence, binding, historyScopeId));
|
|
217
|
+
const beforeTimestampMs = input.beforeTimestamp === undefined ? undefined : Date.parse(input.beforeTimestamp);
|
|
218
|
+
if (beforeTimestampMs !== undefined && Number.isNaN(beforeTimestampMs)) {
|
|
219
|
+
throw new OmpHistoryResponseError("OMP native history beforeTimestamp is invalid.");
|
|
77
220
|
}
|
|
221
|
+
const filtered = beforeTimestampMs === undefined
|
|
222
|
+
? allEntries
|
|
223
|
+
: allEntries.filter((entry) => Date.parse(entry.createdAt) < beforeTimestampMs);
|
|
224
|
+
const cursor = decodeCursor(input.cursor, nativeSessionId);
|
|
225
|
+
const end = Math.min(cursor?.beforeIndex ?? filtered.length, filtered.length);
|
|
226
|
+
const limit = selectedLimit(input.limit);
|
|
227
|
+
const start = Math.max(0, end - limit);
|
|
228
|
+
const entries = filtered.slice(start, end);
|
|
78
229
|
return {
|
|
79
230
|
runtimeInstanceId,
|
|
80
231
|
adapterId: OMP_ADAPTER_ID,
|
|
81
232
|
source: "native",
|
|
82
233
|
entries,
|
|
83
|
-
|
|
84
|
-
|
|
234
|
+
reconciliationProof: {
|
|
235
|
+
complete: true,
|
|
236
|
+
scopeId: historyScopeId,
|
|
237
|
+
fullScope: { entryCount: allEntries.length, digest: historyReconciliationDigest(allEntries) },
|
|
238
|
+
entries: allEntries,
|
|
239
|
+
},
|
|
240
|
+
orderOffset: start,
|
|
241
|
+
...(start > 0 ? { nextCursor: encodeCursor({ v: 1, nativeSessionId, beforeIndex: start }) } : {}),
|
|
242
|
+
hasMore: start > 0,
|
|
85
243
|
};
|
|
86
244
|
}
|
|
87
|
-
catch {
|
|
88
|
-
|
|
245
|
+
catch (error) {
|
|
246
|
+
if (error instanceof OmpHistoryResponseError)
|
|
247
|
+
throw error;
|
|
248
|
+
throw new OmpHistoryResponseError("OMP native history could not establish a complete bounded proof.");
|
|
89
249
|
}
|
|
90
250
|
}
|
|
91
251
|
function emptyPage(runtimeInstanceId) {
|
|
@@ -94,15 +254,10 @@ function emptyPage(runtimeInstanceId) {
|
|
|
94
254
|
adapterId: OMP_ADAPTER_ID,
|
|
95
255
|
source: "native",
|
|
96
256
|
entries: [],
|
|
257
|
+
reconciliationProof: { complete: false, entries: [] },
|
|
97
258
|
hasMore: false,
|
|
98
259
|
};
|
|
99
260
|
}
|
|
100
261
|
export function emptyOmpHistoryPage(runtimeInstanceId) {
|
|
101
|
-
return
|
|
102
|
-
runtimeInstanceId,
|
|
103
|
-
adapterId: OMP_ADAPTER_ID,
|
|
104
|
-
source: "native",
|
|
105
|
-
entries: [],
|
|
106
|
-
hasMore: false,
|
|
107
|
-
};
|
|
262
|
+
return emptyPage(runtimeInstanceId);
|
|
108
263
|
}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
+
import { OmpRpcResponseError } from "./client.js";
|
|
1
2
|
import { OMP_RPC_PROTOCOL_NAME, OMP_RPC_PROTOCOL_VERSION } from "./protocol-types.js";
|
|
2
3
|
export const OMP_ADAPTER_ID = "orp";
|
|
3
4
|
export const OMP_ADAPTER_VERSION = "1.0.0";
|
|
5
|
+
function isUnsupportedForkCommand(error) {
|
|
6
|
+
if (!(error instanceof OmpRpcResponseError))
|
|
7
|
+
return false;
|
|
8
|
+
return /unknown_command|unsupported_command|method_not_found|(?:unknown|unsupported|unrecognized|invalid)\s+(?:rpc\s+)?(?:command|method)|(?:command|method)\s+(?:is\s+)?(?:unknown|unsupported|unrecognized|not implemented|not found)/i
|
|
9
|
+
.test(`${error.errorCode ?? ""} ${error.error}`);
|
|
10
|
+
}
|
|
4
11
|
/**
|
|
5
12
|
* OMP session-file lifecycle controller. OMP owns its session files (JSONL
|
|
6
13
|
* session transcripts) under the isolated `PI_CODING_AGENT_DIR` sessions dir.
|
|
@@ -11,7 +18,6 @@ export class OmpThreadController {
|
|
|
11
18
|
client;
|
|
12
19
|
cwd;
|
|
13
20
|
snapshot;
|
|
14
|
-
forkCandidatesCache = [];
|
|
15
21
|
constructor(client, cwd, initial) {
|
|
16
22
|
this.client = client;
|
|
17
23
|
this.cwd = cwd;
|
|
@@ -56,49 +62,89 @@ export class OmpThreadController {
|
|
|
56
62
|
const snapshot = this.getSessionSnapshot(runtimeInstanceId);
|
|
57
63
|
return [{ ...snapshot, messageCount: this.snapshot.messageCount, name: this.snapshot.sessionName }];
|
|
58
64
|
}
|
|
59
|
-
/** Fetch
|
|
60
|
-
async loadForkCandidates(
|
|
65
|
+
/** Fetch fork candidates from current OMP RPC names with legacy branch compatibility. */
|
|
66
|
+
async loadForkCandidates(_runtimeInstanceId) {
|
|
67
|
+
let result;
|
|
61
68
|
try {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
text: typeof rec.text === "string" ? rec.text : "",
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
this.forkCandidatesCache = candidates;
|
|
80
|
-
return candidates;
|
|
81
|
-
}
|
|
69
|
+
result = await this.client.request({ type: "get_fork_messages" }, "get_fork_messages");
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (!isUnsupportedForkCommand(error))
|
|
73
|
+
throw error;
|
|
74
|
+
try {
|
|
75
|
+
result = await this.client.request({ type: "get_branch_messages" }, "get_branch_messages");
|
|
76
|
+
}
|
|
77
|
+
catch (legacyError) {
|
|
78
|
+
if (isUnsupportedForkCommand(legacyError))
|
|
79
|
+
return [];
|
|
80
|
+
throw legacyError;
|
|
82
81
|
}
|
|
83
82
|
}
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
const data = result["data"];
|
|
84
|
+
if (!data || typeof data !== "object" || Array.isArray(data) || !("messages" in data))
|
|
85
|
+
return [];
|
|
86
|
+
const messages = data.messages;
|
|
87
|
+
if (!Array.isArray(messages))
|
|
88
|
+
return [];
|
|
89
|
+
const candidates = [];
|
|
90
|
+
const seenEntryIds = new Set();
|
|
91
|
+
for (const entry of messages) {
|
|
92
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry))
|
|
93
|
+
continue;
|
|
94
|
+
const record = entry;
|
|
95
|
+
if (typeof record.entryId !== "string" || !record.entryId.trim() || seenEntryIds.has(record.entryId))
|
|
96
|
+
continue;
|
|
97
|
+
seenEntryIds.add(record.entryId);
|
|
98
|
+
candidates.push({
|
|
99
|
+
entryId: record.entryId,
|
|
100
|
+
text: typeof record.text === "string" ? record.text : "",
|
|
101
|
+
});
|
|
86
102
|
}
|
|
87
|
-
|
|
88
|
-
return [];
|
|
89
|
-
}
|
|
90
|
-
/** Sync accessor for the SPI (get_available_commands via a warm-up load). */
|
|
91
|
-
cachedForkCandidates() {
|
|
92
|
-
return this.forkCandidatesCache;
|
|
103
|
+
return candidates;
|
|
93
104
|
}
|
|
94
105
|
async forkSession(runtimeInstanceId, entryId) {
|
|
106
|
+
if (!entryId.trim())
|
|
107
|
+
throw new Error("OMP session fork requires a native user-message id.");
|
|
108
|
+
const previousState = structuredClone(this.snapshot);
|
|
95
109
|
const previous = structuredClone(this.getSessionSnapshot(runtimeInstanceId));
|
|
96
|
-
|
|
110
|
+
let result;
|
|
111
|
+
try {
|
|
112
|
+
result = await this.client.request({ type: "fork", entryId }, "fork");
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
if (!isUnsupportedForkCommand(error))
|
|
116
|
+
throw error;
|
|
117
|
+
result = await this.client.request({ type: "branch", entryId }, "branch");
|
|
118
|
+
}
|
|
97
119
|
const data = result["data"];
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
120
|
+
const record = data && typeof data === "object" && !Array.isArray(data) ? data : undefined;
|
|
121
|
+
const cancelled = record?.cancelled === true;
|
|
122
|
+
try {
|
|
123
|
+
await this.refresh();
|
|
124
|
+
const current = this.getSessionSnapshot(runtimeInstanceId);
|
|
125
|
+
if (!cancelled && current.nativeSessionId === previous.nativeSessionId) {
|
|
126
|
+
throw new Error("OMP session fork returned the source native session id.");
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
previous,
|
|
130
|
+
current,
|
|
131
|
+
cancelled,
|
|
132
|
+
summaryEntryId: entryId,
|
|
133
|
+
...(typeof record?.text === "string" ? { selectedText: record.text } : {}),
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
if (!cancelled && previousState.sessionFile) {
|
|
138
|
+
try {
|
|
139
|
+
await this.client.request({ type: "switch_session", sessionPath: previousState.sessionFile }, "switch_session");
|
|
140
|
+
await this.refresh();
|
|
141
|
+
}
|
|
142
|
+
catch (rollbackError) {
|
|
143
|
+
throw new AggregateError([error, rollbackError], "OMP session fork failed and the source session could not be restored.");
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
102
148
|
}
|
|
103
149
|
async switchSession(runtimeInstanceId, sessionPath) {
|
|
104
150
|
const previous = structuredClone(this.getSessionSnapshot(runtimeInstanceId));
|
|
@@ -8,11 +8,36 @@ import { unsupportedAgentRuntimeCapability, } from "../../agent-runtime/capabili
|
|
|
8
8
|
import { RoutedSession as PiRoutedSession } from "./routed-session.js";
|
|
9
9
|
import { loadModelCatalog as loadPiModelCatalog, piAgentRuntimeModelCatalog, } from "./model-catalog.js";
|
|
10
10
|
import { inspectPiAgentRuntimeHistory, readPiAgentRuntimeHistory, } from "./history.js";
|
|
11
|
+
import { historyReconciliationDigest, } from "../../agent-runtime/history.js";
|
|
11
12
|
import { PiAgentRuntimeAuthController } from "./auth.js";
|
|
12
13
|
import { importPortableHistoryIntoPi } from "./portable-history.js";
|
|
13
14
|
import { piIntentTracingEnabled } from "./intent-tracing.js";
|
|
15
|
+
import { isWebSearchProviderTool } from "../../tools/web-search.js";
|
|
14
16
|
const PI_ADAPTER_ID = "pi";
|
|
15
17
|
export const PI_PROTOCOL_VERSION = "0.84.2";
|
|
18
|
+
const piCompleteHistoryProofs = new WeakMap();
|
|
19
|
+
function bindPiCompleteHistoryProof(page) {
|
|
20
|
+
const claim = page.reconciliationProof;
|
|
21
|
+
if (!claim?.complete)
|
|
22
|
+
return page;
|
|
23
|
+
const entries = Object.freeze([...claim.entries]);
|
|
24
|
+
const fullScope = Object.freeze({ entryCount: entries.length, digest: historyReconciliationDigest(entries) });
|
|
25
|
+
const proof = Object.freeze({
|
|
26
|
+
complete: true,
|
|
27
|
+
...(claim.scopeId ? { scopeId: claim.scopeId } : {}),
|
|
28
|
+
fullScope,
|
|
29
|
+
entries,
|
|
30
|
+
});
|
|
31
|
+
piCompleteHistoryProofs.set(proof, { ...(claim.scopeId ? { scopeId: claim.scopeId } : {}), fullScope });
|
|
32
|
+
return { ...page, reconciliationProof: proof };
|
|
33
|
+
}
|
|
34
|
+
export function isPiBuiltInHistoryReconciliationProof(proof) {
|
|
35
|
+
const bound = piCompleteHistoryProofs.get(proof);
|
|
36
|
+
return proof.complete
|
|
37
|
+
&& bound !== undefined
|
|
38
|
+
&& proof.scopeId === bound.scopeId
|
|
39
|
+
&& proof.fullScope === bound.fullScope;
|
|
40
|
+
}
|
|
16
41
|
export const PI_AGENT_RUNTIME_CAPABILITIES = {
|
|
17
42
|
lifecycle: {
|
|
18
43
|
persistent: true,
|
|
@@ -251,7 +276,7 @@ class PiAgentRuntimeSession {
|
|
|
251
276
|
bindingNativeSessionId;
|
|
252
277
|
nativePresenceExpected;
|
|
253
278
|
disposed = false;
|
|
254
|
-
constructor(runtimeInstanceId, piboSessionId, runtime, binding, initialFastMode) {
|
|
279
|
+
constructor(runtimeInstanceId, piboSessionId, runtime, binding, initialFastMode, providerWebSearchEnabled) {
|
|
255
280
|
this.runtimeInstanceId = runtimeInstanceId;
|
|
256
281
|
this.piboSessionId = piboSessionId;
|
|
257
282
|
this.runtime = runtime;
|
|
@@ -260,6 +285,8 @@ class PiAgentRuntimeSession {
|
|
|
260
285
|
this.bindingNativeSessionId = runtime.session.sessionId;
|
|
261
286
|
this.nativePresenceExpected = runtime.session.sessionManager.buildSessionContext().messages.length > 0;
|
|
262
287
|
this.routed = new PiRoutedSession(piboSessionId, runtime, (event) => this.handlePiboEvent(event), PiboPluginRegistry.create(), true, undefined, initialFastMode, undefined, undefined, (state) => this.handleEngineState(state));
|
|
288
|
+
if (providerWebSearchEnabled)
|
|
289
|
+
this.routed.enableProviderWebSearchObservation();
|
|
263
290
|
this.controls = this.createControls();
|
|
264
291
|
this.compatibilityHandle = this.createCompatibilityHandle();
|
|
265
292
|
}
|
|
@@ -517,7 +544,7 @@ class PiAgentRuntimeAdapter {
|
|
|
517
544
|
return await inspectPiAgentRuntimeHistory(this.instanceId, input);
|
|
518
545
|
}
|
|
519
546
|
async readHistory(input) {
|
|
520
|
-
return await readPiAgentRuntimeHistory(this.instanceId, input);
|
|
547
|
+
return bindPiCompleteHistoryProof(await readPiAgentRuntimeHistory(this.instanceId, input));
|
|
521
548
|
}
|
|
522
549
|
validateProfile(input) {
|
|
523
550
|
const diagnostics = [];
|
|
@@ -658,7 +685,7 @@ class PiAgentRuntimeAdapter {
|
|
|
658
685
|
&& runtime.session.sessionManager.buildSessionContext().messages.length > 0,
|
|
659
686
|
},
|
|
660
687
|
};
|
|
661
|
-
return new PiAgentRuntimeSession(this.instanceId, input.piboSession.id, runtime, binding, compatibility?.initialFastMode ?? false);
|
|
688
|
+
return new PiAgentRuntimeSession(this.instanceId, input.piboSession.id, runtime, binding, compatibility?.initialFastMode ?? false, profile.tools.some((tool) => tool.enabled !== false && isWebSearchProviderTool(tool)));
|
|
662
689
|
}
|
|
663
690
|
loadModelCatalog() {
|
|
664
691
|
const now = Date.now();
|
|
@@ -3,6 +3,8 @@ import { closeSync, existsSync, openSync, readFileSync, readSync, readdirSync, s
|
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { parseSessionEntries, SessionManager } from "@earendil-works/pi-coding-agent";
|
|
6
|
+
import { historyReconciliationDigest } from "../../agent-runtime/history.js";
|
|
7
|
+
import { TRACE_RECONCILIATION_ENTRY_CAP } from "../../shared/trace-limits.js";
|
|
6
8
|
export const PI_HISTORY_TAIL_MAX_BYTES = 2 * 1024 * 1024;
|
|
7
9
|
export const PI_HISTORY_PAGE_MAX_BYTES = 512 * 1024;
|
|
8
10
|
export const PI_HISTORY_SCAN_MAX_BYTES = 16 * 1024 * 1024;
|
|
@@ -76,11 +78,35 @@ export async function readPiAgentRuntimeHistory(runtimeInstanceId, input) {
|
|
|
76
78
|
beforeTimestamp: input.beforeTimestamp ?? decoded.beforeTimestamp,
|
|
77
79
|
limit: input.limit,
|
|
78
80
|
});
|
|
81
|
+
const historyScopeId = `pi:${runtimeInstanceId}:${input.binding.nativeSessionId ?? "unbound"}`;
|
|
82
|
+
const completeProof = inspection.sizeBytes !== undefined && inspection.sizeBytes <= PI_HISTORY_SCAN_MAX_BYTES
|
|
83
|
+
? readPiTranscriptEntriesWithPositions(inspection.locator.value, PI_HISTORY_SCAN_MAX_BYTES, TRACE_RECONCILIATION_ENTRY_CAP)
|
|
84
|
+
: undefined;
|
|
85
|
+
const proofEntries = completeProof
|
|
86
|
+
? piSessionEntriesToAgentRuntimeHistoryEntries(completeProof.entries, completeProof.entryPositions, historyScopeId)
|
|
87
|
+
: undefined;
|
|
88
|
+
const proofEntriesByPosition = proofEntries
|
|
89
|
+
? new Map(proofEntries.map((entry) => [entry.historyPosition, entry]))
|
|
90
|
+
: undefined;
|
|
91
|
+
const entries = proofEntriesByPosition
|
|
92
|
+
? page.entryPositions.flatMap((position) => {
|
|
93
|
+
const entry = proofEntriesByPosition.get(position);
|
|
94
|
+
return entry ? [entry] : [];
|
|
95
|
+
})
|
|
96
|
+
: piSessionEntriesToAgentRuntimeHistoryEntries(page.entries, page.entryPositions, historyScopeId);
|
|
79
97
|
return {
|
|
80
98
|
runtimeInstanceId,
|
|
81
99
|
adapterId: "pi",
|
|
82
100
|
source: "native",
|
|
83
|
-
entries
|
|
101
|
+
entries,
|
|
102
|
+
reconciliationProof: completeProof && proofEntries
|
|
103
|
+
? {
|
|
104
|
+
complete: true,
|
|
105
|
+
scopeId: historyScopeId,
|
|
106
|
+
fullScope: { entryCount: proofEntries.length, digest: historyReconciliationDigest(proofEntries) },
|
|
107
|
+
entries: proofEntries,
|
|
108
|
+
}
|
|
109
|
+
: { complete: false, scopeId: historyScopeId, entries },
|
|
84
110
|
orderOffset: page.startByte,
|
|
85
111
|
nextCursor: page.hasOlder && page.nextBeforeByte !== undefined
|
|
86
112
|
? encodePiHistoryCursor({ beforeByte: page.nextBeforeByte, beforeTimestamp: input.beforeTimestamp ?? decoded.beforeTimestamp })
|
|
@@ -89,11 +115,12 @@ export async function readPiAgentRuntimeHistory(runtimeInstanceId, input) {
|
|
|
89
115
|
inspection,
|
|
90
116
|
};
|
|
91
117
|
}
|
|
92
|
-
export function piSessionEntriesToAgentRuntimeHistoryEntries(entries) {
|
|
118
|
+
export function piSessionEntriesToAgentRuntimeHistoryEntries(entries, historyPositions = [], historyScopeId) {
|
|
93
119
|
const normalized = [];
|
|
94
120
|
let nativeTurnId;
|
|
95
121
|
for (let sequence = 0; sequence < entries.length; sequence += 1) {
|
|
96
122
|
const entry = entries[sequence];
|
|
123
|
+
const historyPosition = historyPositions[sequence];
|
|
97
124
|
if (entry.type === "session_info" && entry.name) {
|
|
98
125
|
normalized.push({
|
|
99
126
|
id: `pi:${entry.id}`,
|
|
@@ -101,6 +128,8 @@ export function piSessionEntriesToAgentRuntimeHistoryEntries(entries) {
|
|
|
101
128
|
source: "native",
|
|
102
129
|
createdAt: entry.timestamp,
|
|
103
130
|
sequence,
|
|
131
|
+
...(historyPosition ? { historyPosition } : {}),
|
|
132
|
+
...(historyScopeId ? { historyScopeId } : {}),
|
|
104
133
|
nativeEntryId: entry.id,
|
|
105
134
|
name: entry.name,
|
|
106
135
|
});
|
|
@@ -117,6 +146,8 @@ export function piSessionEntriesToAgentRuntimeHistoryEntries(entries) {
|
|
|
117
146
|
source: "native",
|
|
118
147
|
createdAt: entry.timestamp,
|
|
119
148
|
sequence,
|
|
149
|
+
...(historyPosition ? { historyPosition } : {}),
|
|
150
|
+
...(historyScopeId ? { historyScopeId } : {}),
|
|
120
151
|
nativeTurnId,
|
|
121
152
|
nativeEntryId: entry.id,
|
|
122
153
|
role: "user",
|
|
@@ -132,6 +163,8 @@ export function piSessionEntriesToAgentRuntimeHistoryEntries(entries) {
|
|
|
132
163
|
source: "native",
|
|
133
164
|
createdAt: entry.timestamp,
|
|
134
165
|
sequence,
|
|
166
|
+
...(historyPosition ? { historyPosition } : {}),
|
|
167
|
+
...(historyScopeId ? { historyScopeId } : {}),
|
|
135
168
|
nativeTurnId,
|
|
136
169
|
nativeEntryId: entry.id,
|
|
137
170
|
role: "assistant",
|
|
@@ -153,6 +186,8 @@ export function piSessionEntriesToAgentRuntimeHistoryEntries(entries) {
|
|
|
153
186
|
source: "native",
|
|
154
187
|
createdAt: entry.timestamp,
|
|
155
188
|
sequence,
|
|
189
|
+
...(historyPosition ? { historyPosition } : {}),
|
|
190
|
+
...(historyScopeId ? { historyScopeId } : {}),
|
|
156
191
|
nativeTurnId,
|
|
157
192
|
nativeEntryId: entry.id,
|
|
158
193
|
role: "tool",
|
|
@@ -228,7 +263,7 @@ export function readPiTranscriptTailEntries(path, maxBytes = PI_HISTORY_TAIL_MAX
|
|
|
228
263
|
}
|
|
229
264
|
export function readPiTranscriptHistoryPage(path, input = {}) {
|
|
230
265
|
if (!existsSync(path))
|
|
231
|
-
return { entries: [], hasOlder: false, scannedBytes: 0, startByte: 0, endByte: 0 };
|
|
266
|
+
return { entries: [], entryPositions: [], hasOlder: false, scannedBytes: 0, startByte: 0, endByte: 0 };
|
|
232
267
|
const stats = statSync(path);
|
|
233
268
|
const fileSize = Math.max(0, stats.size);
|
|
234
269
|
const initialEnd = input.beforeByte === undefined
|
|
@@ -261,7 +296,7 @@ export function readPiTranscriptHistoryPage(path, input = {}) {
|
|
|
261
296
|
const entry = parsed[entryIndex];
|
|
262
297
|
if (beforeTime !== undefined && entryTimestampMs(entry) >= beforeTime)
|
|
263
298
|
continue;
|
|
264
|
-
entries.push({ entry, startByte: record.startByte });
|
|
299
|
+
entries.push({ entry, startByte: record.startByte, parsedIndex: entryIndex });
|
|
265
300
|
if (entries.length >= limit)
|
|
266
301
|
break;
|
|
267
302
|
}
|
|
@@ -271,8 +306,10 @@ export function readPiTranscriptHistoryPage(path, input = {}) {
|
|
|
271
306
|
closeSync(fd);
|
|
272
307
|
}
|
|
273
308
|
const nextBeforeByte = cursorEnd > 0 ? cursorEnd : undefined;
|
|
309
|
+
const orderedEntries = entries.reverse();
|
|
274
310
|
return {
|
|
275
|
-
entries:
|
|
311
|
+
entries: orderedEntries.map((item) => item.entry),
|
|
312
|
+
entryPositions: orderedEntries.map((item) => `pi-byte:${item.startByte}:${item.parsedIndex}`),
|
|
276
313
|
nextBeforeByte,
|
|
277
314
|
hasOlder: nextBeforeByte !== undefined,
|
|
278
315
|
scannedBytes,
|
|
@@ -280,6 +317,30 @@ export function readPiTranscriptHistoryPage(path, input = {}) {
|
|
|
280
317
|
endByte: initialEnd,
|
|
281
318
|
};
|
|
282
319
|
}
|
|
320
|
+
function readPiTranscriptEntriesWithPositions(path, maxBytes, maxEntries) {
|
|
321
|
+
if (statSync(path).size > maxBytes)
|
|
322
|
+
return undefined;
|
|
323
|
+
const content = readFileSync(path);
|
|
324
|
+
if (content.length > maxBytes)
|
|
325
|
+
return undefined;
|
|
326
|
+
const entries = [];
|
|
327
|
+
const entryPositions = [];
|
|
328
|
+
let lineStart = 0;
|
|
329
|
+
for (let cursor = 0; cursor <= content.length; cursor += 1) {
|
|
330
|
+
if (cursor < content.length && content[cursor] !== 0x0a)
|
|
331
|
+
continue;
|
|
332
|
+
const line = content.subarray(lineStart, cursor).toString("utf8");
|
|
333
|
+
const parsed = parseTranscriptLine(line);
|
|
334
|
+
for (let parsedIndex = 0; parsedIndex < parsed.length; parsedIndex += 1) {
|
|
335
|
+
if (entries.length >= maxEntries)
|
|
336
|
+
return undefined;
|
|
337
|
+
entries.push(parsed[parsedIndex]);
|
|
338
|
+
entryPositions.push(`pi-byte:${lineStart}:${parsedIndex}`);
|
|
339
|
+
}
|
|
340
|
+
lineStart = cursor + 1;
|
|
341
|
+
}
|
|
342
|
+
return { entries, entryPositions };
|
|
343
|
+
}
|
|
283
344
|
function normalizePiAssistantContent(content) {
|
|
284
345
|
if (typeof content === "string")
|
|
285
346
|
return [{ type: "text", text: content }];
|