@pasko70/pibo 2.5.2 → 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 +304 -30
- 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/process.js +14 -0
- package/dist/agent-runtimes/codex-native/thread.js +59 -25
- package/dist/agent-runtimes/codex-native/turn.js +27 -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/chat-api-routes.js +1 -1
- package/dist/apps/chat/chat-files.js +130 -9
- 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-DTWJf37A.js → dist-BBDMeU5-.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CkYWKnvy.js → dist-BDIATCQt.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DZ8Wpj-H.js → dist-JnW4v8UE.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-Dmblz15N.js → dist-lRiLP9rr.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DjcwoyNY.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/session-router.js +457 -37
- 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 +259 -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-DsgTm1Aw.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.2.vsix +0 -0
|
@@ -6,6 +6,7 @@ import { OmpRpcClient, OmpRpcResponseError } from "./client.js";
|
|
|
6
6
|
import { defaultOmpRuntimeConfig, OMP_RUNTIME_CONFIG_SCHEMA, parseOmpRuntimeConfig } from "./config.js";
|
|
7
7
|
import { OmpHostToolBridge } from "./host-tools.js";
|
|
8
8
|
import { emptyOmpHistoryPage, inspectOmpHistory, readOmpHistory } from "./history.js";
|
|
9
|
+
import { historyReconciliationDigest, } from "../../agent-runtime/history.js";
|
|
9
10
|
import { OMP_MODEL_OPTIONS_SCHEMA, OMP_MODEL_PROVIDER_ID, OMP_REASONING_VALUES, parseOmpReasoning, readOmpModelCatalog, setOmpModel, } from "./models.js";
|
|
10
11
|
import { buildOmpProcessEnvironment, diagnoseOmpRuntime, disposeOmpSessionPaths, prepareOmpSessionPaths, resetOmpNativeSession, resolveOmpCommand, } from "./process.js";
|
|
11
12
|
import { OmpResourceDelivery } from "./resource-delivery.js";
|
|
@@ -14,6 +15,32 @@ import { OmpRpcTurnController } from "./turn.js";
|
|
|
14
15
|
export { OMP_ADAPTER_ID } from "./thread.js";
|
|
15
16
|
export const OMP_RUNTIME_PROTOCOL_NAME = "omp-rpc";
|
|
16
17
|
export const OMP_RUNTIME_SUPPORTED_RANGE = "2";
|
|
18
|
+
const ompCompleteHistoryProofs = new WeakMap();
|
|
19
|
+
const ompRpcRequestImplementation = OmpRpcClient.prototype.request;
|
|
20
|
+
const ompBuiltInHistoryClients = new WeakMap();
|
|
21
|
+
const ompBuiltInAdapterHistoryClients = new WeakMap();
|
|
22
|
+
function bindOmpCompleteHistoryProof(page) {
|
|
23
|
+
const claim = page.reconciliationProof;
|
|
24
|
+
if (!claim?.complete)
|
|
25
|
+
return page;
|
|
26
|
+
const entries = Object.freeze([...claim.entries]);
|
|
27
|
+
const fullScope = Object.freeze({ entryCount: entries.length, digest: historyReconciliationDigest(entries) });
|
|
28
|
+
const proof = Object.freeze({
|
|
29
|
+
complete: true,
|
|
30
|
+
...(claim.scopeId ? { scopeId: claim.scopeId } : {}),
|
|
31
|
+
fullScope,
|
|
32
|
+
entries,
|
|
33
|
+
});
|
|
34
|
+
ompCompleteHistoryProofs.set(proof, { ...(claim.scopeId ? { scopeId: claim.scopeId } : {}), fullScope });
|
|
35
|
+
return { ...page, reconciliationProof: proof };
|
|
36
|
+
}
|
|
37
|
+
export function isOmpBuiltInHistoryReconciliationProof(proof) {
|
|
38
|
+
const bound = ompCompleteHistoryProofs.get(proof);
|
|
39
|
+
return proof.complete
|
|
40
|
+
&& bound !== undefined
|
|
41
|
+
&& proof.scopeId === bound.scopeId
|
|
42
|
+
&& proof.fullScope === bound.fullScope;
|
|
43
|
+
}
|
|
17
44
|
function ompCapabilities() {
|
|
18
45
|
return {
|
|
19
46
|
lifecycle: {
|
|
@@ -179,6 +206,7 @@ export class OmpSession {
|
|
|
179
206
|
thread;
|
|
180
207
|
hostTools;
|
|
181
208
|
resourceDelivery;
|
|
209
|
+
forkCandidatesRequest;
|
|
182
210
|
constructor(runtimeInstanceId, bundle, config, emitWarning, adapter) {
|
|
183
211
|
this.runtimeInstanceId = runtimeInstanceId;
|
|
184
212
|
this.bundle = bundle;
|
|
@@ -197,12 +225,11 @@ export class OmpSession {
|
|
|
197
225
|
this.controls = {
|
|
198
226
|
getCurrentSession: () => this.thread.getSessionSnapshot(this.runtimeInstanceId),
|
|
199
227
|
listSessions: () => this.thread.listSessions(this.runtimeInstanceId),
|
|
200
|
-
getForkCandidates: () => this.
|
|
228
|
+
getForkCandidates: () => this.getForkCandidates(),
|
|
201
229
|
forkSession: async (entryId) => await this.runIdleOperation(async () => {
|
|
202
|
-
const previous = this.thread.getSessionSnapshot(this.runtimeInstanceId);
|
|
203
230
|
const result = await this.thread.forkSession(this.runtimeInstanceId, entryId);
|
|
204
231
|
this.updateBinding();
|
|
205
|
-
return
|
|
232
|
+
return result;
|
|
206
233
|
}),
|
|
207
234
|
getReasoning: () => parseOmpReasoning(undefined),
|
|
208
235
|
setReasoning: (value) => {
|
|
@@ -245,9 +272,6 @@ export class OmpSession {
|
|
|
245
272
|
adapterId: OMP_ADAPTER_ID,
|
|
246
273
|
};
|
|
247
274
|
}
|
|
248
|
-
forkCandidates() {
|
|
249
|
-
return this.thread.cachedForkCandidates();
|
|
250
|
-
}
|
|
251
275
|
async setOmpModel(provider, modelId) {
|
|
252
276
|
const info = await setOmpModel(this.client, provider, modelId);
|
|
253
277
|
return info;
|
|
@@ -315,7 +339,7 @@ export class OmpSession {
|
|
|
315
339
|
listener(event);
|
|
316
340
|
}
|
|
317
341
|
async prompt(input) {
|
|
318
|
-
this.
|
|
342
|
+
this.assertIdle();
|
|
319
343
|
this.operationInFlight = true;
|
|
320
344
|
try {
|
|
321
345
|
await this.turn.prompt(input.text);
|
|
@@ -369,6 +393,19 @@ export class OmpSession {
|
|
|
369
393
|
if (this.disposed)
|
|
370
394
|
throw new Error("OMP session is disposed.");
|
|
371
395
|
}
|
|
396
|
+
async getForkCandidates() {
|
|
397
|
+
if (this.forkCandidatesRequest)
|
|
398
|
+
return await this.forkCandidatesRequest;
|
|
399
|
+
const request = this.runIdleOperation(async () => await this.thread.loadForkCandidates(this.runtimeInstanceId));
|
|
400
|
+
this.forkCandidatesRequest = request;
|
|
401
|
+
try {
|
|
402
|
+
return await request;
|
|
403
|
+
}
|
|
404
|
+
finally {
|
|
405
|
+
if (this.forkCandidatesRequest === request)
|
|
406
|
+
this.forkCandidatesRequest = undefined;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
372
409
|
async runIdleOperation(operation) {
|
|
373
410
|
this.assertIdle();
|
|
374
411
|
this.operationInFlight = true;
|
|
@@ -380,7 +417,7 @@ export class OmpSession {
|
|
|
380
417
|
}
|
|
381
418
|
}
|
|
382
419
|
}
|
|
383
|
-
|
|
420
|
+
class OmpAgentRuntimeAdapter {
|
|
384
421
|
instanceId;
|
|
385
422
|
descriptor;
|
|
386
423
|
config;
|
|
@@ -473,6 +510,10 @@ export class OmpAgentRuntimeAdapter {
|
|
|
473
510
|
const session = new OmpSession(this.instanceId, bundle, this.parsed, (m) => {
|
|
474
511
|
// Warning surfaced via session events is delivered by the turn controller.
|
|
475
512
|
}, this);
|
|
513
|
+
const historyClient = Object.freeze({
|
|
514
|
+
request: ((...args) => Reflect.apply(ompRpcRequestImplementation, client, args)),
|
|
515
|
+
});
|
|
516
|
+
ompBuiltInHistoryClients.set(session, { owner: this, client: historyClient });
|
|
476
517
|
session.setPiboSessionId(input.piboSession.id);
|
|
477
518
|
// Wire host tools after session construction so the executor is available.
|
|
478
519
|
const portableTools = input.services?.portableTools;
|
|
@@ -521,8 +562,6 @@ export class OmpAgentRuntimeAdapter {
|
|
|
521
562
|
// (bindNativeSessionId below still sets the binding.)
|
|
522
563
|
}
|
|
523
564
|
}
|
|
524
|
-
// Prime fork candidates (get_branch_messages) for the sync SPI.
|
|
525
|
-
void threads.loadForkCandidates(this.instanceId);
|
|
526
565
|
}
|
|
527
566
|
catch (error) {
|
|
528
567
|
await client.dispose();
|
|
@@ -537,11 +576,18 @@ export class OmpAgentRuntimeAdapter {
|
|
|
537
576
|
}
|
|
538
577
|
/** Record the live session so adapter-level reads can route to it. */
|
|
539
578
|
attachLiveSession(session) {
|
|
579
|
+
const history = ompBuiltInHistoryClients.get(session);
|
|
580
|
+
if (!history || history.owner !== this) {
|
|
581
|
+
throw new AgentRuntimeUnavailableError(this.instanceId, "OMP history requires a session created by this built-in adapter instance.");
|
|
582
|
+
}
|
|
540
583
|
this.live = session;
|
|
584
|
+
ompBuiltInAdapterHistoryClients.set(this, history.client);
|
|
541
585
|
}
|
|
542
586
|
detachLiveSession(session) {
|
|
543
|
-
if (this.live
|
|
544
|
-
|
|
587
|
+
if (this.live !== session)
|
|
588
|
+
return;
|
|
589
|
+
this.live = undefined;
|
|
590
|
+
ompBuiltInAdapterHistoryClients.delete(this);
|
|
545
591
|
}
|
|
546
592
|
async listModels() {
|
|
547
593
|
if (this.live) {
|
|
@@ -579,8 +625,9 @@ export class OmpAgentRuntimeAdapter {
|
|
|
579
625
|
return inspectOmpHistory(input, this.instanceId);
|
|
580
626
|
}
|
|
581
627
|
async readHistory(input) {
|
|
582
|
-
|
|
583
|
-
|
|
628
|
+
const historyClient = ompBuiltInAdapterHistoryClients.get(this);
|
|
629
|
+
if (historyClient) {
|
|
630
|
+
return bindOmpCompleteHistoryProof(await readOmpHistory(historyClient, input, this.instanceId, input.binding));
|
|
584
631
|
}
|
|
585
632
|
return emptyOmpHistoryPage(this.instanceId);
|
|
586
633
|
}
|
|
@@ -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();
|