@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
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { unsupportedAgentRuntimeCapability, } from "../../agent-runtime/capabilities.js";
|
|
3
3
|
import { AgentRuntimeBindingMissingError, AgentRuntimeUnavailableError, } from "../../agent-runtime/errors.js";
|
|
4
|
+
import { isAgentRuntimeBindingPersistence } from "../../sessions/runtime-binding-persistence.js";
|
|
5
|
+
import { PENDING_NATIVE_SESSION_METADATA_KEY } from "../../sessions/runtime-binding.js";
|
|
4
6
|
import { selectRequestedFastMode, selectRequestedModelProfile, selectRequestedThinkingLevel, } from "../../core/model-defaults.js";
|
|
5
7
|
import { CODEX_NATIVE_RUNTIME_CONFIG_SCHEMA, defaultCodexNativeRuntimeConfig, parseCodexNativeRuntimeConfig, } from "./config.js";
|
|
6
8
|
import { diagnoseCodexNativeRuntime, startCodexNativeAppServer, } from "./process.js";
|
|
7
9
|
import { inspectCodexThreadHistory, pageCodexThreadHistory, unavailableCodexThreadHistoryInspection, } from "./history.js";
|
|
10
|
+
import { historyReconciliationDigest, } from "../../agent-runtime/history.js";
|
|
8
11
|
import { CODEX_NATIVE_ADAPTER_ID, CodexNativeThreadController, CodexNativeThreadMissingError, } from "./thread.js";
|
|
9
12
|
import { CODEX_APP_SERVER_PROTOCOL_NAME, CODEX_APP_SERVER_SUPPORTED_RANGE, CODEX_APP_SERVER_VERSION, } from "./protocol-version.js";
|
|
10
13
|
import { CodexNativeTurnController } from "./turn.js";
|
|
@@ -13,8 +16,69 @@ import { CodexNativeResourceDelivery } from "./resource-delivery.js";
|
|
|
13
16
|
import { CODEX_NATIVE_MODEL_OPTIONS_SCHEMA, CODEX_NATIVE_MODEL_PROVIDER_ID, CODEX_NATIVE_REASONING_VALUES, CodexNativeSessionSettingsController, parseCodexNativeProfileOptions, readCodexNativeModelCatalog, readCodexNativePersistedSettings, toAgentRuntimeModelCatalog, } from "./models.js";
|
|
14
17
|
import { CodexNativeAuthController } from "./auth.js";
|
|
15
18
|
import { injectPortableHistoryIntoCodex } from "./portable-history.js";
|
|
19
|
+
import { CODEX_FIRST_USE_METADATA_KEY, CODEX_FIRST_USE_METADATA_VERSION, assertCodexNativeFirstUseMessageId, assertCodexNativeFirstUseDeliveryReceiptTurn, assertCodexNativePendingFirstUseRequest, assertCodexNativePendingFirstUseTurn, beginCodexNativeFirstUseAttempt, codexNativeFirstUseDeliveryReceipt, codexNativePendingFirstUseOwnerLiveness, endCodexNativeFirstUseAttempt, hashCanonicalCodexNativeFirstUsePrompt, isExactCodexNativeFirstUseDeliveryReplay, readCodexNativeFirstUseDeliveryReceipt, readCodexNativePendingFirstUse, } from "./first-use.js";
|
|
16
20
|
export { CODEX_NATIVE_ADAPTER_ID } from "./thread.js";
|
|
17
21
|
export const CODEX_NATIVE_ADAPTER_VERSION = "1.0.0";
|
|
22
|
+
function readPendingFirstUse(binding) {
|
|
23
|
+
try {
|
|
24
|
+
return readCodexNativePendingFirstUse(binding);
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
throw new AgentRuntimeUnavailableError(binding.runtimeInstanceId, error instanceof Error ? error.message : "The pending native Codex first-use metadata is invalid.");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function readFirstUseDeliveryReceipt(binding) {
|
|
31
|
+
try {
|
|
32
|
+
return readCodexNativeFirstUseDeliveryReceipt(binding);
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
throw new AgentRuntimeUnavailableError(binding.runtimeInstanceId, error instanceof Error ? error.message : "The delivered native Codex first-use receipt is invalid.");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function deliveryReceiptForBinding(binding) {
|
|
39
|
+
if (binding.metadata?.[CODEX_FIRST_USE_METADATA_KEY] === undefined)
|
|
40
|
+
return undefined;
|
|
41
|
+
if (binding.state === "unbound") {
|
|
42
|
+
const pending = readPendingFirstUse(binding);
|
|
43
|
+
return pending ? codexNativeFirstUseDeliveryReceipt(pending) : undefined;
|
|
44
|
+
}
|
|
45
|
+
return readFirstUseDeliveryReceipt(binding);
|
|
46
|
+
}
|
|
47
|
+
function pendingFirstUseBinding(binding, threadId, messageId, promptHash, owner) {
|
|
48
|
+
return {
|
|
49
|
+
...structuredClone(binding),
|
|
50
|
+
nativeSessionId: threadId,
|
|
51
|
+
state: "unbound",
|
|
52
|
+
protocol: CODEX_APP_SERVER_PROTOCOL_NAME,
|
|
53
|
+
protocolVersion: CODEX_APP_SERVER_VERSION,
|
|
54
|
+
adapterVersion: CODEX_NATIVE_ADAPTER_VERSION,
|
|
55
|
+
locator: { kind: "adapter-resolved" },
|
|
56
|
+
metadata: {
|
|
57
|
+
...(binding.metadata ?? {}),
|
|
58
|
+
[PENDING_NATIVE_SESSION_METADATA_KEY]: true,
|
|
59
|
+
[CODEX_FIRST_USE_METADATA_KEY]: {
|
|
60
|
+
version: CODEX_FIRST_USE_METADATA_VERSION,
|
|
61
|
+
state: "pending",
|
|
62
|
+
threadId,
|
|
63
|
+
messageId,
|
|
64
|
+
promptHash,
|
|
65
|
+
...owner,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function clearPendingFirstUse(binding) {
|
|
71
|
+
const metadata = { ...(binding.metadata ?? {}) };
|
|
72
|
+
delete metadata[CODEX_FIRST_USE_METADATA_KEY];
|
|
73
|
+
delete metadata[PENDING_NATIVE_SESSION_METADATA_KEY];
|
|
74
|
+
return {
|
|
75
|
+
...structuredClone(binding),
|
|
76
|
+
nativeSessionId: undefined,
|
|
77
|
+
state: "unbound",
|
|
78
|
+
locator: undefined,
|
|
79
|
+
metadata,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
18
82
|
function codexNativeCapabilities(structuredUserInput) {
|
|
19
83
|
return {
|
|
20
84
|
lifecycle: {
|
|
@@ -111,8 +175,10 @@ export const CODEX_NATIVE_THREAD_CAPABILITIES = codexNativeCapabilities(false);
|
|
|
111
175
|
function timestamp(seconds) {
|
|
112
176
|
return new Date(seconds * 1_000).toISOString();
|
|
113
177
|
}
|
|
114
|
-
function safeThreadMetadata(thread, previous = {}, settings = {}) {
|
|
178
|
+
function safeThreadMetadata(thread, previous = {}, settings = {}, deliveryReceipt) {
|
|
115
179
|
const { diagnosticCode: _diagnosticCode, diagnosticMessage: _diagnosticMessage, ...metadata } = previous;
|
|
180
|
+
delete metadata[CODEX_FIRST_USE_METADATA_KEY];
|
|
181
|
+
delete metadata[PENDING_NATIVE_SESSION_METADATA_KEY];
|
|
116
182
|
return {
|
|
117
183
|
...metadata,
|
|
118
184
|
...settings,
|
|
@@ -122,9 +188,11 @@ function safeThreadMetadata(thread, previous = {}, settings = {}) {
|
|
|
122
188
|
threadUpdatedAt: timestamp(thread.updatedAt),
|
|
123
189
|
threadStatus: thread.status.type,
|
|
124
190
|
modelProvider: thread.modelProvider,
|
|
191
|
+
...(deliveryReceipt ? { [CODEX_FIRST_USE_METADATA_KEY]: deliveryReceipt } : {}),
|
|
125
192
|
};
|
|
126
193
|
}
|
|
127
194
|
function bindingForThread(input) {
|
|
195
|
+
const deliveryReceipt = input.previous ? deliveryReceiptForBinding(input.previous) : undefined;
|
|
128
196
|
return {
|
|
129
197
|
...(input.previous ? structuredClone(input.previous) : {}),
|
|
130
198
|
piboSessionId: input.piboSessionId,
|
|
@@ -136,7 +204,7 @@ function bindingForThread(input) {
|
|
|
136
204
|
protocolVersion: CODEX_APP_SERVER_VERSION,
|
|
137
205
|
adapterVersion: CODEX_NATIVE_ADAPTER_VERSION,
|
|
138
206
|
locator: { kind: "adapter-resolved" },
|
|
139
|
-
metadata: safeThreadMetadata(input.thread, input.previous?.metadata, input.settings),
|
|
207
|
+
metadata: safeThreadMetadata(input.thread, input.previous?.metadata, input.settings, deliveryReceipt),
|
|
140
208
|
};
|
|
141
209
|
}
|
|
142
210
|
function validateOpenBinding(input, runtimeInstanceId) {
|
|
@@ -163,8 +231,31 @@ function validateOpenBinding(input, runtimeInstanceId) {
|
|
|
163
231
|
if (binding.state === "bound" && !binding.nativeSessionId) {
|
|
164
232
|
throw new AgentRuntimeUnavailableError(runtimeInstanceId, "The persisted Codex binding has no native thread id.");
|
|
165
233
|
}
|
|
234
|
+
if (binding.state === "bound")
|
|
235
|
+
readFirstUseDeliveryReceipt(binding);
|
|
166
236
|
if (binding.state === "unbound" && binding.nativeSessionId) {
|
|
167
|
-
|
|
237
|
+
const pending = readPendingFirstUse(binding);
|
|
238
|
+
const hasCurrentPendingMarker = binding.metadata?.[PENDING_NATIVE_SESSION_METADATA_KEY] === true;
|
|
239
|
+
const isExactParentLegacyPending = pending?.version === 1
|
|
240
|
+
&& binding.metadata?.[PENDING_NATIVE_SESSION_METADATA_KEY] === undefined;
|
|
241
|
+
if (!pending
|
|
242
|
+
|| pending.threadId !== binding.nativeSessionId
|
|
243
|
+
|| (!hasCurrentPendingMarker && !isExactParentLegacyPending)
|
|
244
|
+
|| input.piboSession.kind !== "branch") {
|
|
245
|
+
throw new AgentRuntimeUnavailableError(runtimeInstanceId, "An unbound Codex binding contains an invalid pending native thread identity.");
|
|
246
|
+
}
|
|
247
|
+
if (isExactParentLegacyPending) {
|
|
248
|
+
return {
|
|
249
|
+
...binding,
|
|
250
|
+
metadata: {
|
|
251
|
+
...(binding.metadata ?? {}),
|
|
252
|
+
[PENDING_NATIVE_SESSION_METADATA_KEY]: true,
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
else if (binding.state === "unbound" && binding.metadata?.[CODEX_FIRST_USE_METADATA_KEY] !== undefined) {
|
|
258
|
+
throw new AgentRuntimeUnavailableError(runtimeInstanceId, "An unbound Codex binding contains pending metadata without a native thread identity.");
|
|
168
259
|
}
|
|
169
260
|
return binding;
|
|
170
261
|
}
|
|
@@ -177,6 +268,8 @@ export class CodexNativeThreadSession {
|
|
|
177
268
|
structuredUserInput;
|
|
178
269
|
reloadProcess;
|
|
179
270
|
productContext;
|
|
271
|
+
bindingPersistence;
|
|
272
|
+
firstUseTestFailpoints;
|
|
180
273
|
adapterId = CODEX_NATIVE_ADAPTER_ID;
|
|
181
274
|
cwd;
|
|
182
275
|
capabilities;
|
|
@@ -198,12 +291,14 @@ export class CodexNativeThreadSession {
|
|
|
198
291
|
selectedToolNames = new Set();
|
|
199
292
|
observedToolNames = new Set();
|
|
200
293
|
toolInventoryWarning;
|
|
201
|
-
constructor(runtimeInstanceId, process, threads, settings, resourceDelivery, binding, structuredUserInput, reloadProcess, productContext) {
|
|
294
|
+
constructor(runtimeInstanceId, process, threads, settings, resourceDelivery, binding, structuredUserInput, reloadProcess, productContext, bindingPersistence, firstUseTestFailpoints) {
|
|
202
295
|
this.runtimeInstanceId = runtimeInstanceId;
|
|
203
296
|
this.settings = settings;
|
|
204
297
|
this.structuredUserInput = structuredUserInput;
|
|
205
298
|
this.reloadProcess = reloadProcess;
|
|
206
299
|
this.productContext = productContext;
|
|
300
|
+
this.bindingPersistence = bindingPersistence;
|
|
301
|
+
this.firstUseTestFailpoints = firstUseTestFailpoints;
|
|
207
302
|
this.process = process;
|
|
208
303
|
this.threads = threads;
|
|
209
304
|
this.resourceDelivery = resourceDelivery;
|
|
@@ -219,14 +314,16 @@ export class CodexNativeThreadSession {
|
|
|
219
314
|
getForkCandidates: () => this.threads.getForkCandidates(),
|
|
220
315
|
forkSession: async (entryId) => await this.runIdleOperation(async () => {
|
|
221
316
|
const result = await this.threads.fork(this.runtimeInstanceId, this.cwd, entryId, async (threadId) => await this.resourceDelivery.verifyThread(this.process.client, threadId));
|
|
222
|
-
|
|
223
|
-
|
|
317
|
+
if (result.current.nativeSessionId) {
|
|
318
|
+
this.settings.attachThread(this.threads.thread.id, this.threads.configuration);
|
|
319
|
+
this.promoteBindingFromCurrentThread();
|
|
320
|
+
}
|
|
224
321
|
return result;
|
|
225
322
|
}),
|
|
226
323
|
cloneSession: async () => await this.runIdleOperation(async () => {
|
|
227
324
|
const result = await this.threads.clone(this.runtimeInstanceId, this.cwd, async (threadId) => await this.resourceDelivery.verifyThread(this.process.client, threadId));
|
|
228
325
|
this.settings.attachThread(this.threads.thread.id, this.threads.configuration);
|
|
229
|
-
this.
|
|
326
|
+
this.promoteBindingFromCurrentThread();
|
|
230
327
|
return result;
|
|
231
328
|
}),
|
|
232
329
|
getReasoning: () => this.settings.reasoning,
|
|
@@ -256,7 +353,7 @@ export class CodexNativeThreadSession {
|
|
|
256
353
|
});
|
|
257
354
|
}
|
|
258
355
|
await this.turns.compact();
|
|
259
|
-
this.
|
|
356
|
+
this.refreshBoundBindingFromCurrentThread();
|
|
260
357
|
return {
|
|
261
358
|
native: true,
|
|
262
359
|
method: "thread/compact/start",
|
|
@@ -281,7 +378,7 @@ export class CodexNativeThreadSession {
|
|
|
281
378
|
return this.requests.pendingUserInputs;
|
|
282
379
|
}
|
|
283
380
|
getBinding() {
|
|
284
|
-
this.
|
|
381
|
+
this.refreshBoundBindingFromCurrentThread();
|
|
285
382
|
return structuredClone(this.binding);
|
|
286
383
|
}
|
|
287
384
|
subscribe(listener) {
|
|
@@ -292,12 +389,25 @@ export class CodexNativeThreadSession {
|
|
|
292
389
|
async prompt(input) {
|
|
293
390
|
this.assertActive();
|
|
294
391
|
await this.ensureFreshResourcesForTurn();
|
|
392
|
+
this.assertIdle();
|
|
295
393
|
this.operationInFlight = true;
|
|
394
|
+
let ownedFirstUseAttempt;
|
|
296
395
|
try {
|
|
297
|
-
await this.
|
|
298
|
-
|
|
396
|
+
const firstUse = await this.acquirePendingFirstUse(this.productContext?.getActiveMessage?.()?.id ?? randomUUID(), input.text);
|
|
397
|
+
ownedFirstUseAttempt = firstUse.attemptId;
|
|
398
|
+
if (!firstUse.startTurn)
|
|
399
|
+
return;
|
|
400
|
+
await this.firstUseTestFailpoints?.afterPendingBindingPersisted?.();
|
|
401
|
+
await this.turns.start(input.text, firstUse.messageId, this.settings.turnOptions);
|
|
402
|
+
await this.firstUseTestFailpoints?.afterNativeTerminalDurable?.();
|
|
403
|
+
const pending = this.binding.state === "unbound" ? readPendingFirstUse(this.binding) : undefined;
|
|
404
|
+
if (pending)
|
|
405
|
+
assertCodexNativePendingFirstUseTurn(pending, this.threads.thread);
|
|
406
|
+
this.promoteBindingFromCurrentThread();
|
|
299
407
|
}
|
|
300
408
|
finally {
|
|
409
|
+
if (ownedFirstUseAttempt)
|
|
410
|
+
endCodexNativeFirstUseAttempt(ownedFirstUseAttempt);
|
|
301
411
|
this.operationInFlight = false;
|
|
302
412
|
if (this.recycleProcessAfterInterruptedTurn && !this.disposed) {
|
|
303
413
|
this.recycleProcessAfterInterruptedTurn = false;
|
|
@@ -531,7 +641,7 @@ export class CodexNativeThreadSession {
|
|
|
531
641
|
this.requests = this.createRequestController(next.process);
|
|
532
642
|
previousRequests.dispose();
|
|
533
643
|
previousTurns.dispose();
|
|
534
|
-
this.
|
|
644
|
+
this.refreshBoundBindingFromCurrentThread();
|
|
535
645
|
this.resourceProcessUnavailable = false;
|
|
536
646
|
this.resourceWarning = undefined;
|
|
537
647
|
next = undefined;
|
|
@@ -574,7 +684,65 @@ export class CodexNativeThreadSession {
|
|
|
574
684
|
}
|
|
575
685
|
}
|
|
576
686
|
}
|
|
577
|
-
|
|
687
|
+
refreshBoundBindingFromCurrentThread() {
|
|
688
|
+
if (this.binding.state !== "bound")
|
|
689
|
+
return;
|
|
690
|
+
this.promoteBindingFromCurrentThread();
|
|
691
|
+
}
|
|
692
|
+
async acquirePendingFirstUse(messageId, prompt) {
|
|
693
|
+
if (this.binding.state !== "unbound") {
|
|
694
|
+
const receipt = readFirstUseDeliveryReceipt(this.binding);
|
|
695
|
+
if (!receipt || !isExactCodexNativeFirstUseDeliveryReplay(receipt, messageId, prompt)) {
|
|
696
|
+
return { messageId, startTurn: true };
|
|
697
|
+
}
|
|
698
|
+
const firstTurn = assertCodexNativeFirstUseDeliveryReceiptTurn(receipt, this.threads.thread);
|
|
699
|
+
this.turns.replayTerminalTurn(firstTurn);
|
|
700
|
+
return { messageId: receipt.messageId, startTurn: false };
|
|
701
|
+
}
|
|
702
|
+
assertCodexNativeFirstUseMessageId(messageId);
|
|
703
|
+
const promptHash = hashCanonicalCodexNativeFirstUsePrompt(prompt);
|
|
704
|
+
const currentPending = readPendingFirstUse(this.binding);
|
|
705
|
+
if (currentPending && this.threads.thread.turns.length > 0) {
|
|
706
|
+
assertCodexNativePendingFirstUseTurn(currentPending, this.threads.thread);
|
|
707
|
+
assertCodexNativePendingFirstUseRequest(currentPending, messageId, prompt);
|
|
708
|
+
this.promoteBindingFromCurrentThread();
|
|
709
|
+
return { messageId: currentPending.messageId, startTurn: false };
|
|
710
|
+
}
|
|
711
|
+
if (currentPending) {
|
|
712
|
+
const ownerLiveness = codexNativePendingFirstUseOwnerLiveness(currentPending);
|
|
713
|
+
if (ownerLiveness === "active") {
|
|
714
|
+
throw new Error("Native Codex first use is already owned by another live router.");
|
|
715
|
+
}
|
|
716
|
+
if (ownerLiveness === "ambiguous") {
|
|
717
|
+
throw new Error("Native Codex first-use ownership is ambiguous and cannot be replaced safely.");
|
|
718
|
+
}
|
|
719
|
+
assertCodexNativePendingFirstUseRequest(currentPending, messageId, prompt);
|
|
720
|
+
}
|
|
721
|
+
if (!isAgentRuntimeBindingPersistence(this.bindingPersistence)) {
|
|
722
|
+
throw new Error("Native Codex first use requires audited durable cross-process atomic runtime-binding CAS before turn/start.");
|
|
723
|
+
}
|
|
724
|
+
const owner = beginCodexNativeFirstUseAttempt();
|
|
725
|
+
const proposed = pendingFirstUseBinding(this.binding, this.threads.thread.id, messageId, promptHash, owner);
|
|
726
|
+
try {
|
|
727
|
+
const expectedRevision = this.binding.revision ?? 1;
|
|
728
|
+
const persisted = await this.bindingPersistence.compareAndSet(proposed, expectedRevision);
|
|
729
|
+
const pending = readPendingFirstUse(persisted);
|
|
730
|
+
if (persisted.revision !== expectedRevision + 1
|
|
731
|
+
|| persisted.state !== "unbound"
|
|
732
|
+
|| persisted.nativeSessionId !== proposed.nativeSessionId
|
|
733
|
+
|| !pending
|
|
734
|
+
|| JSON.stringify(pending) !== JSON.stringify(readPendingFirstUse(proposed))) {
|
|
735
|
+
throw new Error("Runtime-binding persistence returned an inconsistent Codex first-use claim.");
|
|
736
|
+
}
|
|
737
|
+
this.binding = structuredClone(persisted);
|
|
738
|
+
return { attemptId: owner.attemptId, messageId, startTurn: true };
|
|
739
|
+
}
|
|
740
|
+
catch (error) {
|
|
741
|
+
endCodexNativeFirstUseAttempt(owner.attemptId);
|
|
742
|
+
throw error;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
promoteBindingFromCurrentThread() {
|
|
578
746
|
this.binding = bindingForThread({
|
|
579
747
|
piboSessionId: this.binding.piboSessionId,
|
|
580
748
|
runtimeInstanceId: this.runtimeInstanceId,
|
|
@@ -589,12 +757,38 @@ export class CodexNativeThreadSession {
|
|
|
589
757
|
}
|
|
590
758
|
assertIdle() {
|
|
591
759
|
this.assertActive();
|
|
592
|
-
if (this.turns.streaming || this.resourceRefresh || this.resourceProcessUnavailable) {
|
|
760
|
+
if (this.operationInFlight || this.turns.streaming || this.resourceRefresh || this.resourceProcessUnavailable) {
|
|
593
761
|
throw new Error("Codex runtime controls can only change while the session is idle.");
|
|
594
762
|
}
|
|
595
763
|
}
|
|
596
764
|
}
|
|
597
|
-
|
|
765
|
+
const codexNativeCompleteHistoryProofs = new WeakMap();
|
|
766
|
+
const codexNativeHistoryReaders = new WeakMap();
|
|
767
|
+
const startCodexNativeHistoryProcess = startCodexNativeAppServer;
|
|
768
|
+
const readCodexNativeHistoryThread = CodexNativeThreadController.read;
|
|
769
|
+
function bindCodexNativeCompleteHistoryProof(page) {
|
|
770
|
+
const claim = page.reconciliationProof;
|
|
771
|
+
if (!claim?.complete)
|
|
772
|
+
return page;
|
|
773
|
+
const entries = Object.freeze([...claim.entries]);
|
|
774
|
+
const fullScope = Object.freeze({ entryCount: entries.length, digest: historyReconciliationDigest(entries) });
|
|
775
|
+
const proof = Object.freeze({
|
|
776
|
+
complete: true,
|
|
777
|
+
...(claim.scopeId ? { scopeId: claim.scopeId } : {}),
|
|
778
|
+
fullScope,
|
|
779
|
+
entries,
|
|
780
|
+
});
|
|
781
|
+
codexNativeCompleteHistoryProofs.set(proof, { ...(claim.scopeId ? { scopeId: claim.scopeId } : {}), fullScope });
|
|
782
|
+
return { ...page, reconciliationProof: proof };
|
|
783
|
+
}
|
|
784
|
+
export function isCodexNativeBuiltInHistoryReconciliationProof(proof) {
|
|
785
|
+
const bound = codexNativeCompleteHistoryProofs.get(proof);
|
|
786
|
+
return proof.complete
|
|
787
|
+
&& bound !== undefined
|
|
788
|
+
&& proof.scopeId === bound.scopeId
|
|
789
|
+
&& proof.fullScope === bound.fullScope;
|
|
790
|
+
}
|
|
791
|
+
class CodexNativeAgentRuntimeAdapter {
|
|
598
792
|
instanceId;
|
|
599
793
|
descriptor;
|
|
600
794
|
config;
|
|
@@ -611,6 +805,24 @@ export class CodexNativeAgentRuntimeAdapter {
|
|
|
611
805
|
};
|
|
612
806
|
this.displayName = displayName ?? this.descriptor.displayName;
|
|
613
807
|
this.enabled = enabled;
|
|
808
|
+
const historyConfig = structuredClone(this.config);
|
|
809
|
+
const historyInstanceId = this.instanceId;
|
|
810
|
+
codexNativeHistoryReaders.set(this, async (piboSessionId, workspace, threadId, includeTurns) => {
|
|
811
|
+
const process = await startCodexNativeHistoryProcess({
|
|
812
|
+
config: historyConfig,
|
|
813
|
+
runtimeInstanceId: historyInstanceId,
|
|
814
|
+
piboSessionId,
|
|
815
|
+
sessionGeneration: `history-${randomUUID()}`,
|
|
816
|
+
workspace,
|
|
817
|
+
clientVersion: CODEX_NATIVE_ADAPTER_VERSION,
|
|
818
|
+
});
|
|
819
|
+
try {
|
|
820
|
+
return await readCodexNativeHistoryThread(process.client, threadId, includeTurns);
|
|
821
|
+
}
|
|
822
|
+
finally {
|
|
823
|
+
await process.close();
|
|
824
|
+
}
|
|
825
|
+
});
|
|
614
826
|
this.authController = new CodexNativeAuthController({
|
|
615
827
|
config: this.config,
|
|
616
828
|
startProcess: async (sessionGeneration) => await startCodexNativeAppServer({
|
|
@@ -724,6 +936,14 @@ export class CodexNativeAgentRuntimeAdapter {
|
|
|
724
936
|
}
|
|
725
937
|
async openSession(input) {
|
|
726
938
|
const binding = validateOpenBinding(input, this.instanceId);
|
|
939
|
+
const activeFirstMessage = input.productContext.getActiveMessage?.();
|
|
940
|
+
const messageStartsLazyFirstUse = binding.state === "unbound"
|
|
941
|
+
&& input.piboSession.kind === "branch"
|
|
942
|
+
&& input.historyHandoff?.mode !== "import"
|
|
943
|
+
&& Boolean(activeFirstMessage?.id);
|
|
944
|
+
if (messageStartsLazyFirstUse && !isAgentRuntimeBindingPersistence(input.services?.runtimeBindingPersistence)) {
|
|
945
|
+
throw new AgentRuntimeUnavailableError(this.instanceId, "Native Codex first use requires audited durable cross-process atomic runtime-binding CAS before thread/start.");
|
|
946
|
+
}
|
|
727
947
|
const sessionGeneration = input.services?.resources?.sessionGeneration
|
|
728
948
|
?? input.services?.portableTools?.sessionGeneration
|
|
729
949
|
?? randomUUID();
|
|
@@ -801,16 +1021,50 @@ export class CodexNativeAgentRuntimeAdapter {
|
|
|
801
1021
|
? { developerInstructions: resourceDelivery.developerInstructions }
|
|
802
1022
|
: {}),
|
|
803
1023
|
};
|
|
1024
|
+
const pendingFirstUse = binding.state === "unbound" ? readPendingFirstUse(binding) : undefined;
|
|
1025
|
+
const deliveryReceipt = binding.state === "bound" ? readFirstUseDeliveryReceipt(binding) : undefined;
|
|
1026
|
+
let sessionBinding = binding;
|
|
1027
|
+
let pendingTerminalVerified = false;
|
|
804
1028
|
let threads;
|
|
805
1029
|
try {
|
|
806
|
-
threads = binding.state === "bound"
|
|
1030
|
+
threads = binding.state === "bound" || pendingFirstUse
|
|
807
1031
|
? await CodexNativeThreadController.resume(process.client, binding.nativeSessionId, input.workspace, threadSelection)
|
|
808
1032
|
: await CodexNativeThreadController.start(process.client, input.workspace, threadSelection);
|
|
809
1033
|
}
|
|
810
1034
|
catch (error) {
|
|
811
|
-
if (error instanceof CodexNativeThreadMissingError
|
|
812
|
-
|
|
813
|
-
|
|
1035
|
+
if (pendingFirstUse && error instanceof CodexNativeThreadMissingError) {
|
|
1036
|
+
const ownerLiveness = codexNativePendingFirstUseOwnerLiveness(pendingFirstUse);
|
|
1037
|
+
if (ownerLiveness === "active") {
|
|
1038
|
+
throw new AgentRuntimeUnavailableError(this.instanceId, "Native Codex first use is owned by another live router and is not yet restart-reconcilable.");
|
|
1039
|
+
}
|
|
1040
|
+
if (ownerLiveness === "ambiguous") {
|
|
1041
|
+
throw new AgentRuntimeUnavailableError(this.instanceId, "Native Codex first-use ownership is ambiguous; the pending identity was retained.");
|
|
1042
|
+
}
|
|
1043
|
+
threads = await CodexNativeThreadController.start(process.client, input.workspace, threadSelection);
|
|
1044
|
+
sessionBinding = clearPendingFirstUse(binding);
|
|
1045
|
+
}
|
|
1046
|
+
else {
|
|
1047
|
+
if (error instanceof CodexNativeThreadMissingError || !resourceDelivery.hasMcpServers)
|
|
1048
|
+
throw error;
|
|
1049
|
+
throw new Error("Codex could not initialize every selected MCP server.");
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
if (pendingFirstUse && threads.thread.turns.length > 0) {
|
|
1053
|
+
try {
|
|
1054
|
+
assertCodexNativePendingFirstUseTurn(pendingFirstUse, threads.thread);
|
|
1055
|
+
pendingTerminalVerified = true;
|
|
1056
|
+
}
|
|
1057
|
+
catch (error) {
|
|
1058
|
+
throw new AgentRuntimeUnavailableError(this.instanceId, error instanceof Error ? error.message : "The pending native Codex first turn cannot be verified.");
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
if (deliveryReceipt) {
|
|
1062
|
+
try {
|
|
1063
|
+
assertCodexNativeFirstUseDeliveryReceiptTurn(deliveryReceipt, threads.thread);
|
|
1064
|
+
}
|
|
1065
|
+
catch (error) {
|
|
1066
|
+
throw new AgentRuntimeUnavailableError(this.instanceId, error instanceof Error ? error.message : "The delivered native Codex first turn cannot be verified.");
|
|
1067
|
+
}
|
|
814
1068
|
}
|
|
815
1069
|
if (input.historyHandoff?.mode === "import") {
|
|
816
1070
|
if (binding.state === "bound") {
|
|
@@ -820,13 +1074,27 @@ export class CodexNativeAgentRuntimeAdapter {
|
|
|
820
1074
|
}
|
|
821
1075
|
settings.attachThread(threads.thread.id, threads.configuration);
|
|
822
1076
|
await resourceDelivery.verifyThread(process.client, threads.thread.id);
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
1077
|
+
const lazyFirstMessageBranch = sessionBinding.state === "unbound"
|
|
1078
|
+
&& input.piboSession.kind === "branch"
|
|
1079
|
+
&& input.historyHandoff?.mode !== "import";
|
|
1080
|
+
const openedBinding = lazyFirstMessageBranch
|
|
1081
|
+
? pendingTerminalVerified
|
|
1082
|
+
? bindingForThread({
|
|
1083
|
+
piboSessionId: input.piboSession.id,
|
|
1084
|
+
runtimeInstanceId: this.instanceId,
|
|
1085
|
+
previous: sessionBinding,
|
|
1086
|
+
thread: threads.thread,
|
|
1087
|
+
settings: settings.bindingMetadata,
|
|
1088
|
+
})
|
|
1089
|
+
: sessionBinding
|
|
1090
|
+
: bindingForThread({
|
|
1091
|
+
piboSessionId: input.piboSession.id,
|
|
1092
|
+
runtimeInstanceId: this.instanceId,
|
|
1093
|
+
previous: sessionBinding,
|
|
1094
|
+
thread: threads.thread,
|
|
1095
|
+
settings: settings.bindingMetadata,
|
|
1096
|
+
});
|
|
1097
|
+
return new CodexNativeThreadSession(this.instanceId, process, threads, settings, resourceDelivery, openedBinding, this.config.experimentalUserInput, async () => await startProcessBundle(`${sessionGeneration}-credential-${randomUUID()}`), input.productContext, input.services?.runtimeBindingPersistence, compatibility?.testOnlyFirstUseFailpoints);
|
|
830
1098
|
}
|
|
831
1099
|
catch (error) {
|
|
832
1100
|
settings?.dispose();
|
|
@@ -844,7 +1112,10 @@ export class CodexNativeAgentRuntimeAdapter {
|
|
|
844
1112
|
return unavailableCodexThreadHistoryInspection(this.instanceId, input.binding, "codex_native_history_thread_id_missing", "The Codex runtime binding has no native thread id for history lookup.");
|
|
845
1113
|
}
|
|
846
1114
|
try {
|
|
847
|
-
const
|
|
1115
|
+
const readHistory = codexNativeHistoryReaders.get(this);
|
|
1116
|
+
if (!readHistory)
|
|
1117
|
+
throw new Error("Codex built-in history reader is unavailable.");
|
|
1118
|
+
const thread = await readHistory(input.binding.piboSessionId, input.workspace, threadId, false);
|
|
848
1119
|
return inspectCodexThreadHistory(this.instanceId, input.binding, thread);
|
|
849
1120
|
}
|
|
850
1121
|
catch (error) {
|
|
@@ -867,15 +1138,18 @@ export class CodexNativeAgentRuntimeAdapter {
|
|
|
867
1138
|
};
|
|
868
1139
|
}
|
|
869
1140
|
try {
|
|
870
|
-
const
|
|
871
|
-
|
|
1141
|
+
const readHistory = codexNativeHistoryReaders.get(this);
|
|
1142
|
+
if (!readHistory)
|
|
1143
|
+
throw new Error("Codex built-in history reader is unavailable.");
|
|
1144
|
+
const thread = await readHistory(input.binding.piboSessionId, input.workspace, threadId, true);
|
|
1145
|
+
return bindCodexNativeCompleteHistoryProof(pageCodexThreadHistory({
|
|
872
1146
|
runtimeInstanceId: this.instanceId,
|
|
873
1147
|
binding: input.binding,
|
|
874
1148
|
thread,
|
|
875
1149
|
cursor: input.cursor,
|
|
876
1150
|
beforeTimestamp: input.beforeTimestamp,
|
|
877
1151
|
limit: input.limit,
|
|
878
|
-
});
|
|
1152
|
+
}));
|
|
879
1153
|
}
|
|
880
1154
|
catch (error) {
|
|
881
1155
|
if (error instanceof CodexNativeThreadMissingError) {
|
|
@@ -142,19 +142,28 @@ function serializeMessage(message, maxMessageBytes) {
|
|
|
142
142
|
function timeoutError(label) {
|
|
143
143
|
return new CodexAppServerClientError("timeout", `${label} timed out`);
|
|
144
144
|
}
|
|
145
|
-
async function waitWithTimeout(promise, timeoutMs, label) {
|
|
145
|
+
async function waitWithTimeout(promise, timeoutMs, label, signal) {
|
|
146
|
+
if (signal?.aborted)
|
|
147
|
+
throw new CodexAppServerClientError("aborted", `${label} was aborted`);
|
|
146
148
|
let timer;
|
|
149
|
+
let onAbort;
|
|
147
150
|
try {
|
|
148
151
|
return await Promise.race([
|
|
149
152
|
promise,
|
|
150
153
|
new Promise((_resolve, reject) => {
|
|
151
154
|
timer = setTimeout(() => reject(timeoutError(label)), timeoutMs);
|
|
152
155
|
}),
|
|
156
|
+
...(signal ? [new Promise((_resolve, reject) => {
|
|
157
|
+
onAbort = () => reject(new CodexAppServerClientError("aborted", `${label} was aborted`));
|
|
158
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
159
|
+
})] : []),
|
|
153
160
|
]);
|
|
154
161
|
}
|
|
155
162
|
finally {
|
|
156
163
|
if (timer)
|
|
157
164
|
clearTimeout(timer);
|
|
165
|
+
if (onAbort)
|
|
166
|
+
signal?.removeEventListener("abort", onAbort);
|
|
158
167
|
}
|
|
159
168
|
}
|
|
160
169
|
async function sleep(delayMs, signal) {
|
|
@@ -231,6 +240,8 @@ export class CodexAppServerClient {
|
|
|
231
240
|
}
|
|
232
241
|
static async start(options) {
|
|
233
242
|
validateClientOptions(options);
|
|
243
|
+
if (options.signal?.aborted)
|
|
244
|
+
throw new CodexAppServerClientError("aborted", "Codex App Server startup was aborted");
|
|
234
245
|
let previousMask;
|
|
235
246
|
if (options.fileCreationMask !== undefined && globalThis.process.platform !== "win32") {
|
|
236
247
|
previousMask = globalThis.process.umask(options.fileCreationMask);
|
|
@@ -255,9 +266,13 @@ export class CodexAppServerClient {
|
|
|
255
266
|
};
|
|
256
267
|
const client = new CodexAppServerClient(process, options, placeholder);
|
|
257
268
|
client.attachProcessListeners();
|
|
269
|
+
const onAbort = () => {
|
|
270
|
+
void client.stopProcess(false).catch(() => { });
|
|
271
|
+
};
|
|
272
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
258
273
|
const deadline = Date.now() + client.startupTimeoutMs;
|
|
259
274
|
try {
|
|
260
|
-
await waitWithTimeout(client.spawned.promise, client.startupTimeoutMs, "Codex App Server spawn");
|
|
275
|
+
await waitWithTimeout(client.spawned.promise, client.startupTimeoutMs, "Codex App Server spawn", options.signal);
|
|
261
276
|
client.state = "initializing";
|
|
262
277
|
const initializeResult = await client.requestWithRetry("initialize", {
|
|
263
278
|
clientInfo: { ...options.clientInfo },
|
|
@@ -265,11 +280,12 @@ export class CodexAppServerClient {
|
|
|
265
280
|
}, {
|
|
266
281
|
timeoutMs: Math.max(1, deadline - Date.now()),
|
|
267
282
|
retryOverloaded: true,
|
|
283
|
+
signal: options.signal,
|
|
268
284
|
}, true);
|
|
269
285
|
const initializeResponse = validateInitializeResponse(initializeResult);
|
|
270
286
|
Object.assign(client.initializeResponse, initializeResponse);
|
|
271
287
|
Object.freeze(client.initializeResponse);
|
|
272
|
-
await waitWithTimeout(client.writeMessage({ method: "initialized" }, true), Math.max(1, deadline - Date.now()), "Codex App Server initialized notification");
|
|
288
|
+
await waitWithTimeout(client.writeMessage({ method: "initialized" }, true), Math.max(1, deadline - Date.now()), "Codex App Server initialized notification", options.signal);
|
|
273
289
|
client.state = "ready";
|
|
274
290
|
return client;
|
|
275
291
|
}
|
|
@@ -279,6 +295,9 @@ export class CodexAppServerClient {
|
|
|
279
295
|
await client.stopProcess(false).catch(() => { });
|
|
280
296
|
throw normalized;
|
|
281
297
|
}
|
|
298
|
+
finally {
|
|
299
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
300
|
+
}
|
|
282
301
|
}
|
|
283
302
|
get snapshot() {
|
|
284
303
|
return {
|