agent-embassy 1.6.1 → 1.7.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/CHANGELOG.md +20 -0
- package/README.md +9 -9
- package/README.zh-CN.md +9 -9
- package/SECURITY.md +34 -77
- package/dist/src/gateway/acp-client.d.ts +117 -0
- package/dist/src/gateway/acp-client.js +382 -0
- package/dist/src/gateway/acp-client.js.map +1 -0
- package/dist/src/gateway/acp-provider.d.ts +65 -0
- package/dist/src/gateway/acp-provider.js +163 -0
- package/dist/src/gateway/acp-provider.js.map +1 -0
- package/dist/src/gateway/claude-helper-protocol.d.ts +3 -1
- package/dist/src/gateway/claude-helper-protocol.js +15 -5
- package/dist/src/gateway/claude-helper-protocol.js.map +1 -1
- package/dist/src/gateway/claude-helper-supervisor.d.ts +3 -1
- package/dist/src/gateway/claude-helper-supervisor.js +16 -2
- package/dist/src/gateway/claude-helper-supervisor.js.map +1 -1
- package/dist/src/gateway/claude-helper.js +4 -3
- package/dist/src/gateway/claude-helper.js.map +1 -1
- package/dist/src/gateway/claude-peer.d.ts +1 -1
- package/dist/src/gateway/claude-peer.js +11 -13
- package/dist/src/gateway/claude-peer.js.map +1 -1
- package/dist/src/gateway/claude-runtime.d.ts +0 -20
- package/dist/src/gateway/claude-runtime.js +6 -127
- package/dist/src/gateway/claude-runtime.js.map +1 -1
- package/dist/src/gateway/cli-copy.en.d.ts +1 -1
- package/dist/src/gateway/cli-copy.en.js +2 -2
- package/dist/src/gateway/cli-copy.zh-CN.d.ts +1 -1
- package/dist/src/gateway/cli-copy.zh-CN.js +2 -2
- package/dist/src/gateway/cli.d.ts +1 -1
- package/dist/src/gateway/cli.js +26 -5
- package/dist/src/gateway/cli.js.map +1 -1
- package/dist/src/gateway/codex-app-server.d.ts +5 -74
- package/dist/src/gateway/codex-app-server.js +9 -599
- package/dist/src/gateway/codex-app-server.js.map +1 -1
- package/dist/src/gateway/codex-local-transport.d.ts +8 -26
- package/dist/src/gateway/codex-local-transport.js +26 -76
- package/dist/src/gateway/codex-local-transport.js.map +1 -1
- package/dist/src/gateway/compatibility.d.ts +6 -66
- package/dist/src/gateway/compatibility.js +14 -296
- package/dist/src/gateway/compatibility.js.map +1 -1
- package/dist/src/gateway/config.d.ts +9 -0
- package/dist/src/gateway/config.js +6 -2
- package/dist/src/gateway/config.js.map +1 -1
- package/dist/src/gateway/control.d.ts +16 -1
- package/dist/src/gateway/control.js +89 -57
- package/dist/src/gateway/control.js.map +1 -1
- package/dist/src/gateway/dashboard-copy.d.ts +1 -1
- package/dist/src/gateway/dashboard-copy.en.d.ts +14 -44
- package/dist/src/gateway/dashboard-copy.en.js +37 -67
- package/dist/src/gateway/dashboard-copy.en.js.map +1 -1
- package/dist/src/gateway/dashboard-copy.js +14 -44
- package/dist/src/gateway/dashboard-copy.js.map +1 -1
- package/dist/src/gateway/dashboard-copy.zh-CN.d.ts +14 -44
- package/dist/src/gateway/dashboard-copy.zh-CN.js +37 -67
- package/dist/src/gateway/dashboard-copy.zh-CN.js.map +1 -1
- package/dist/src/gateway/dashboard-model.d.ts +38 -76
- package/dist/src/gateway/dashboard-model.js +158 -256
- package/dist/src/gateway/dashboard-model.js.map +1 -1
- package/dist/src/gateway/dashboard.d.ts +0 -3
- package/dist/src/gateway/dashboard.js +75 -76
- package/dist/src/gateway/dashboard.js.map +1 -1
- package/dist/src/gateway/deepseek-detect.d.ts +7 -6
- package/dist/src/gateway/deepseek-detect.js +31 -115
- package/dist/src/gateway/deepseek-detect.js.map +1 -1
- package/dist/src/gateway/live-dashboard-app/app.js +166 -621
- package/dist/src/gateway/live-dashboard-command.js +2 -4
- package/dist/src/gateway/live-dashboard-command.js.map +1 -1
- package/dist/src/gateway/live-dashboard-http.d.ts +1 -2
- package/dist/src/gateway/live-dashboard-http.js +14 -15
- package/dist/src/gateway/live-dashboard-http.js.map +1 -1
- package/dist/src/gateway/progress-watch-machine.d.ts +1 -4
- package/dist/src/gateway/progress-watch-machine.js.map +1 -1
- package/dist/src/gateway/provenance-envelope.d.ts +3 -2
- package/dist/src/gateway/provenance-envelope.js +44 -11
- package/dist/src/gateway/provenance-envelope.js.map +1 -1
- package/dist/src/gateway/providers.d.ts +12 -85
- package/dist/src/gateway/providers.js +72 -835
- package/dist/src/gateway/providers.js.map +1 -1
- package/dist/src/gateway/server.d.ts +8 -9
- package/dist/src/gateway/server.js +51 -99
- package/dist/src/gateway/server.js.map +1 -1
- package/dist/src/gateway/service.d.ts +22 -49
- package/dist/src/gateway/service.js +196 -437
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/store.d.ts +20 -29
- package/dist/src/gateway/store.js +281 -827
- package/dist/src/gateway/store.js.map +1 -1
- package/dist/src/gateway/types.d.ts +55 -55
- package/dist/src/gateway/types.js +49 -91
- package/dist/src/gateway/types.js.map +1 -1
- package/docs/CONFIGURATION.md +11 -10
- package/docs/CONFIGURATION.zh-CN.md +10 -9
- package/docs/DASHBOARD.md +4 -38
- package/docs/DASHBOARD.zh-CN.md +4 -2
- package/docs/GATEWAY-ARCHITECTURE.md +61 -119
- package/package.json +1 -1
- package/skills/embassy-peer/SKILL.md +6 -6
|
@@ -1,43 +1,20 @@
|
|
|
1
1
|
import { Buffer } from "node:buffer";
|
|
2
|
-
import { readdir, stat } from "node:fs/promises";
|
|
3
|
-
import { isAbsolute } from "node:path";
|
|
4
2
|
import WebSocket from "ws";
|
|
5
|
-
import { sharesCompatibilityMajor } from "./compatibility.js";
|
|
6
3
|
/**
|
|
7
|
-
*
|
|
4
|
+
* Closed App Server method surface used by the gateway.
|
|
8
5
|
*
|
|
9
6
|
* `turn/steer` is exposed only through the exact Claude-to-Codex `STEER:`
|
|
10
7
|
* contract. It queues input at the attested next tool-call boundary and never
|
|
11
8
|
* authorizes an interrupt or a generic/public JSON-RPC escape hatch.
|
|
12
9
|
*/
|
|
13
10
|
export const CODEX_APP_SERVER_V1_METHODS = [
|
|
14
|
-
"account/rateLimits/read",
|
|
15
|
-
"model/list",
|
|
16
|
-
"thread/archive",
|
|
17
11
|
"thread/loaded/list",
|
|
18
12
|
"thread/resume",
|
|
19
|
-
"thread/start",
|
|
20
13
|
"thread/unsubscribe",
|
|
21
14
|
"turn/start",
|
|
22
15
|
"turn/steer",
|
|
23
16
|
"turn/interrupt",
|
|
24
17
|
];
|
|
25
|
-
export const CODEX_PROBE_MODEL_PREFERENCE = ["gpt-5.6-luna"];
|
|
26
|
-
export const CODEX_PROBE_EFFORT = ["none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"];
|
|
27
|
-
export const CODEX_WRITE_PROBE_INPUT = "Reply with exactly OK. Do not use tools.";
|
|
28
|
-
/** Exact App Server builds whose writable v2 schema was reviewed. */
|
|
29
|
-
export const CODEX_APP_SERVER_WRITABLE_VERSIONS = ["0.147.0"];
|
|
30
|
-
export function codexWriteProbeFailure(safeErrorCode, evidence = {}) {
|
|
31
|
-
return {
|
|
32
|
-
...(evidence.archivedThreadCount === 1 ? { archivedThreadCount: 1 } : {}),
|
|
33
|
-
outcome: "fail",
|
|
34
|
-
safeErrorCode,
|
|
35
|
-
settingsEchoObserved: evidence.settingsEchoObserved ?? false,
|
|
36
|
-
...(evidence.tokenCount === undefined
|
|
37
|
-
? {}
|
|
38
|
-
: { tokenCount: evidence.tokenCount }),
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
18
|
export class CodexConnectorError extends Error {
|
|
42
19
|
ambiguous;
|
|
43
20
|
code;
|
|
@@ -263,9 +240,6 @@ export class WebSocketDuplexTransport {
|
|
|
263
240
|
}
|
|
264
241
|
}
|
|
265
242
|
const V1_METHOD_SET = new Set(CODEX_APP_SERVER_V1_METHODS);
|
|
266
|
-
const PROBE_ONLY_METHODS = new Set([
|
|
267
|
-
"account/rateLimits/read", "model/list", "thread/archive", "thread/start",
|
|
268
|
-
]);
|
|
269
243
|
const OUTPUT_NOTIFICATION_OPT_OUTS = [
|
|
270
244
|
"item/started",
|
|
271
245
|
"item/agentMessage/delta",
|
|
@@ -275,14 +249,6 @@ const OUTPUT_NOTIFICATION_OPT_OUTS = [
|
|
|
275
249
|
"turn/diff/updated",
|
|
276
250
|
"turn/plan/updated",
|
|
277
251
|
];
|
|
278
|
-
const PROBE_PASSIVE_ITEM_TYPES = new Set(["agentMessage", "reasoning", "userMessage"]);
|
|
279
|
-
const PROBE_TOOL_NOTIFICATION_METHODS = new Set([
|
|
280
|
-
"item/autoApprovalReview/completed", "item/autoApprovalReview/started",
|
|
281
|
-
"item/commandExecution/terminalInteraction",
|
|
282
|
-
"item/fileChange/outputDelta", "item/fileChange/patchUpdated",
|
|
283
|
-
"item/mcpToolCall/progress",
|
|
284
|
-
]);
|
|
285
|
-
const MAX_PROBE_TOKEN_COUNT = 1_000_000_000;
|
|
286
252
|
const APPROVAL_REQUEST_METHODS = new Set([
|
|
287
253
|
"item/commandExecution/requestApproval",
|
|
288
254
|
"item/fileChange/requestApproval",
|
|
@@ -301,56 +267,6 @@ function validOpaqueId(value, maximumLength = 256) {
|
|
|
301
267
|
value.length <= maximumLength &&
|
|
302
268
|
/^[A-Za-z0-9][A-Za-z0-9._:-]*$/u.test(value));
|
|
303
269
|
}
|
|
304
|
-
function validUuidV7(value) {
|
|
305
|
-
return /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(value);
|
|
306
|
-
}
|
|
307
|
-
function parseLoadedThreadIds(value) {
|
|
308
|
-
if (!isRecord(value) ||
|
|
309
|
-
!Array.isArray(value.data) ||
|
|
310
|
-
(value.nextCursor !== undefined && value.nextCursor !== null)) {
|
|
311
|
-
return null;
|
|
312
|
-
}
|
|
313
|
-
if (value.data.length > 100_000 ||
|
|
314
|
-
!value.data.every((threadId) => typeof threadId === "string" && validOpaqueId(threadId)) ||
|
|
315
|
-
new Set(value.data).size !== value.data.length) {
|
|
316
|
-
return null;
|
|
317
|
-
}
|
|
318
|
-
return value.data;
|
|
319
|
-
}
|
|
320
|
-
function rateLimitConstrained(value) {
|
|
321
|
-
if (!isRecord(value) || !isRecord(value.rateLimits))
|
|
322
|
-
return null;
|
|
323
|
-
const byLimit = value.rateLimitsByLimitId;
|
|
324
|
-
const snapshot = isRecord(byLimit) && isRecord(byLimit.codex)
|
|
325
|
-
? byLimit.codex
|
|
326
|
-
: value.rateLimits;
|
|
327
|
-
const percent = (container, key) => {
|
|
328
|
-
if (container === null || container === undefined)
|
|
329
|
-
return Number.NaN;
|
|
330
|
-
if (!isRecord(container) || !Number.isSafeInteger(container[key]))
|
|
331
|
-
return null;
|
|
332
|
-
const result = container[key];
|
|
333
|
-
return result >= 0 && result <= 100 ? result : null;
|
|
334
|
-
};
|
|
335
|
-
const remaining = percent(snapshot.individualLimit, "remainingPercent");
|
|
336
|
-
const primary = percent(snapshot.primary, "usedPercent");
|
|
337
|
-
const secondary = percent(snapshot.secondary, "usedPercent");
|
|
338
|
-
if ([remaining, primary, secondary].includes(null))
|
|
339
|
-
return null;
|
|
340
|
-
if ((snapshot.rateLimitReachedType !== null &&
|
|
341
|
-
snapshot.rateLimitReachedType !== undefined) ||
|
|
342
|
-
snapshot.spendControlReached === true) {
|
|
343
|
-
return true;
|
|
344
|
-
}
|
|
345
|
-
if (snapshot.spendControlReached !== null &&
|
|
346
|
-
snapshot.spendControlReached !== undefined &&
|
|
347
|
-
snapshot.spendControlReached !== false) {
|
|
348
|
-
return null;
|
|
349
|
-
}
|
|
350
|
-
return ((!Number.isNaN(remaining) && remaining <= 5) ||
|
|
351
|
-
(!Number.isNaN(primary) && primary >= 95) ||
|
|
352
|
-
(!Number.isNaN(secondary) && secondary >= 95));
|
|
353
|
-
}
|
|
354
270
|
function validateRoute(route) {
|
|
355
271
|
if (!validOpaqueId(route.threadId) ||
|
|
356
272
|
!validOpaqueId(route.endpointGeneration, 128)) {
|
|
@@ -369,17 +285,10 @@ function validateClientInfo(clientInfo) {
|
|
|
369
285
|
return { ...clientInfo };
|
|
370
286
|
}
|
|
371
287
|
function validateCompatibility(compatibility, route) {
|
|
372
|
-
if (compatibility.endpointGeneration !== route.endpointGeneration
|
|
373
|
-
compatibility.protocol !== "app-server-v2-stable" ||
|
|
374
|
-
compatibility.steering?.method !== "turn/steer" ||
|
|
375
|
-
compatibility.steering.requestSchema !== "expected-turn-id-text-v1" ||
|
|
376
|
-
compatibility.steering.deliveryBoundary !== "next-tool-call-boundary" ||
|
|
377
|
-
!(CODEX_APP_SERVER_WRITABLE_VERSIONS.some((version) => version === compatibility.appServerVersion) ||
|
|
378
|
-
(compatibility.observedSchemaCandidate === true &&
|
|
379
|
-
CODEX_APP_SERVER_WRITABLE_VERSIONS.some((version) => sharesCompatibilityMajor(version, compatibility.appServerVersion))))) {
|
|
288
|
+
if (compatibility.endpointGeneration !== route.endpointGeneration) {
|
|
380
289
|
throw new CodexConnectorError("INVALID_CONFIGURATION");
|
|
381
290
|
}
|
|
382
|
-
return { ...compatibility
|
|
291
|
+
return { ...compatibility };
|
|
383
292
|
}
|
|
384
293
|
function parseRouteStatus(value) {
|
|
385
294
|
if (!isRecord(value) || typeof value.type !== "string")
|
|
@@ -440,8 +349,6 @@ export class CodexAppServerConnector {
|
|
|
440
349
|
nextRequestId = 1;
|
|
441
350
|
ownsActiveTurn = false;
|
|
442
351
|
pending = new Map();
|
|
443
|
-
probeAttempted = false;
|
|
444
|
-
probeRuntime = null;
|
|
445
352
|
queue = [];
|
|
446
353
|
revision = 0;
|
|
447
354
|
routeStatus = "unknown";
|
|
@@ -452,7 +359,6 @@ export class CodexAppServerConnector {
|
|
|
452
359
|
turnWatchdogTimer;
|
|
453
360
|
unlisten = [];
|
|
454
361
|
clientInfo;
|
|
455
|
-
compatibility;
|
|
456
362
|
maxDeadlineMs;
|
|
457
363
|
maxFrameBytes;
|
|
458
364
|
maxInputBytes;
|
|
@@ -466,27 +372,14 @@ export class CodexAppServerConnector {
|
|
|
466
372
|
turnWatchdogMs;
|
|
467
373
|
route;
|
|
468
374
|
transport;
|
|
469
|
-
writeCompatibilityProbe;
|
|
470
|
-
writesEnabled;
|
|
471
375
|
constructor(options) {
|
|
472
376
|
this.route = validateRoute(options.route);
|
|
473
|
-
|
|
474
|
-
if (typeof options.writesEnabled !== "boolean") {
|
|
475
|
-
throw new CodexConnectorError("INVALID_CONFIGURATION");
|
|
476
|
-
}
|
|
477
|
-
if (options.writesEnabled &&
|
|
478
|
-
!(CODEX_APP_SERVER_WRITABLE_VERSIONS.includes(options.compatibility.appServerVersion) ||
|
|
479
|
-
(options.compatibility.observedSchemaCandidate === true &&
|
|
480
|
-
!options.compatibility.appServerVersion.includes("-")))) {
|
|
481
|
-
throw new CodexConnectorError("INVALID_CONFIGURATION");
|
|
482
|
-
}
|
|
483
|
-
this.writesEnabled = options.writesEnabled;
|
|
484
|
-
this.writeCompatibilityProbe = options.writeCompatibilityProbe === true;
|
|
377
|
+
validateCompatibility(options.compatibility, this.route);
|
|
485
378
|
this.transport = options.transport;
|
|
486
379
|
this.clientInfo = validateClientInfo(options.clientInfo ?? {
|
|
487
380
|
name: "agent_embassy_gateway",
|
|
488
381
|
title: "Embassy Gateway",
|
|
489
|
-
version: "1.
|
|
382
|
+
version: "1.7.0",
|
|
490
383
|
});
|
|
491
384
|
this.maxFrameBytes = positiveInteger(options.maxFrameBytes ?? DEFAULT_MAX_FRAME_BYTES, "INVALID_CONFIGURATION");
|
|
492
385
|
this.maxDeadlineMs = positiveInteger(options.maxDeadlineMs ?? DEFAULT_MAX_DEADLINE_MS, "INVALID_CONFIGURATION");
|
|
@@ -502,7 +395,6 @@ export class CodexAppServerConnector {
|
|
|
502
395
|
this.unlisten.push(this.transport.onMessage((payload) => this.handlePayload(payload)), this.transport.onClose(() => this.handleDisconnect()), this.transport.onError(() => this.protocolFault("TRANSPORT_CLOSED")));
|
|
503
396
|
}
|
|
504
397
|
observation() {
|
|
505
|
-
const writeBlockCode = this.currentWriteBlockCode();
|
|
506
398
|
return {
|
|
507
399
|
connection: this.connection,
|
|
508
400
|
eventSequence: this.eventSequence,
|
|
@@ -510,18 +402,14 @@ export class CodexAppServerConnector {
|
|
|
510
402
|
queueDepth: this.queue.length,
|
|
511
403
|
requestInFlight: this.inFlightMethod !== null,
|
|
512
404
|
routeStatus: this.routeStatus,
|
|
513
|
-
writableReady: this.isWritableReady(),
|
|
514
|
-
writeBlockCode,
|
|
515
405
|
};
|
|
516
406
|
}
|
|
517
407
|
guard() {
|
|
518
|
-
const writableReady = this.isWritableReady();
|
|
519
408
|
return {
|
|
520
409
|
...this.route,
|
|
521
410
|
activeTurnId: this.activeTurnId,
|
|
522
411
|
revision: this.revision,
|
|
523
412
|
status: this.routeStatus,
|
|
524
|
-
writableReady,
|
|
525
413
|
};
|
|
526
414
|
}
|
|
527
415
|
async observeLoadedThread(guard) {
|
|
@@ -589,10 +477,7 @@ export class CodexAppServerConnector {
|
|
|
589
477
|
throw new CodexConnectorError("THREAD_NOT_OBSERVED");
|
|
590
478
|
}
|
|
591
479
|
if (this.routeStatus !== "unknown" &&
|
|
592
|
-
this.routeStatus !== "not_loaded"
|
|
593
|
-
!(this.currentWriteBlockCode() !== null &&
|
|
594
|
-
this.routeStatus === "idle" &&
|
|
595
|
-
this.activeTurnId === null)) {
|
|
480
|
+
this.routeStatus !== "not_loaded") {
|
|
596
481
|
throw new CodexConnectorError("ROUTE_BUSY");
|
|
597
482
|
}
|
|
598
483
|
const statusEpoch = this.statusEpoch;
|
|
@@ -624,10 +509,6 @@ export class CodexAppServerConnector {
|
|
|
624
509
|
}
|
|
625
510
|
}
|
|
626
511
|
this.emit("thread_resumed");
|
|
627
|
-
const writeBlockCode = this.currentWriteBlockCode();
|
|
628
|
-
if (writeBlockCode !== null) {
|
|
629
|
-
this.emit("route_write_blocked", { errorCode: writeBlockCode });
|
|
630
|
-
}
|
|
631
512
|
}
|
|
632
513
|
catch (error) {
|
|
633
514
|
const normalized = this.normalizeError(error, true);
|
|
@@ -702,7 +583,6 @@ export class CodexAppServerConnector {
|
|
|
702
583
|
throw new CodexConnectorError("THREAD_NOT_OBSERVED");
|
|
703
584
|
}
|
|
704
585
|
if (input.steer === true && this.routeStatus === "active") {
|
|
705
|
-
this.assertWritableReady();
|
|
706
586
|
const message = this.validateMessage(input);
|
|
707
587
|
if (this.activeTurnId === null || this.inFlightMethod !== null) {
|
|
708
588
|
this.acceptedMessageIds.delete(message.messageId);
|
|
@@ -715,7 +595,6 @@ export class CodexAppServerConnector {
|
|
|
715
595
|
this.routeStatus === "active" ||
|
|
716
596
|
this.routeStatus === "waiting_approval" ||
|
|
717
597
|
this.routeStatus === "interrupting") {
|
|
718
|
-
this.assertWritableReady();
|
|
719
598
|
const message = this.validateMessage(input);
|
|
720
599
|
if (input.steer === true) {
|
|
721
600
|
this.acceptedMessageIds.delete(message.messageId);
|
|
@@ -724,7 +603,6 @@ export class CodexAppServerConnector {
|
|
|
724
603
|
this.enqueue(message);
|
|
725
604
|
return { disposition: "queued", observation: this.observation() };
|
|
726
605
|
}
|
|
727
|
-
this.assertWritableReady();
|
|
728
606
|
const message = this.validateMessage(input);
|
|
729
607
|
if (this.routeStatus !== "idle") {
|
|
730
608
|
this.acceptedMessageIds.delete(message.messageId);
|
|
@@ -796,318 +674,14 @@ export class CodexAppServerConnector {
|
|
|
796
674
|
this.handleDisconnect();
|
|
797
675
|
}
|
|
798
676
|
}
|
|
799
|
-
/**
|
|
800
|
-
* Run the one fixed disposable-thread write probe. The method never exposes
|
|
801
|
-
* JSON-RPC, native IDs, content, or route state, and every failure settles as
|
|
802
|
-
* a safe result so compatibility discovery cannot take down the broker.
|
|
803
|
-
*/
|
|
804
|
-
async runWriteCompatibilityProbe(input) {
|
|
805
|
-
if (this.probeAttempted) {
|
|
806
|
-
return codexWriteProbeFailure("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
807
|
-
}
|
|
808
|
-
this.probeAttempted = true;
|
|
809
|
-
let threadId = null;
|
|
810
|
-
let archivedThreadCount;
|
|
811
|
-
let result = codexWriteProbeFailure("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
812
|
-
try {
|
|
813
|
-
if (!this.writeCompatibilityProbe ||
|
|
814
|
-
this.connection !== "ready" ||
|
|
815
|
-
this.inFlightMethod !== null ||
|
|
816
|
-
this.pending.size !== 0 ||
|
|
817
|
-
!isAbsolute(input.cwd) ||
|
|
818
|
-
!Array.isArray(input.forbiddenThreadIds) ||
|
|
819
|
-
!input.forbiddenThreadIds.every((value) => typeof value === "string" && validOpaqueId(value))) {
|
|
820
|
-
return result;
|
|
821
|
-
}
|
|
822
|
-
const beforeStat = await stat(input.cwd, { bigint: true });
|
|
823
|
-
const beforeEntries = await readdir(input.cwd);
|
|
824
|
-
if (!beforeStat.isDirectory() ||
|
|
825
|
-
beforeStat.uid !== BigInt(process.getuid?.() ?? -1) ||
|
|
826
|
-
(beforeStat.mode & 511n) !== 448n ||
|
|
827
|
-
beforeEntries.length !== 0) {
|
|
828
|
-
return result;
|
|
829
|
-
}
|
|
830
|
-
const limits = rateLimitConstrained(await this.request("account/rateLimits/read", null));
|
|
831
|
-
if (limits === null)
|
|
832
|
-
return result;
|
|
833
|
-
if (limits) {
|
|
834
|
-
return codexWriteProbeFailure("CODEX_WRITE_PROBE_RATE_LIMIT_CONSTRAINED");
|
|
835
|
-
}
|
|
836
|
-
const selection = this.selectProbeModel(await this.request("model/list", {
|
|
837
|
-
includeHidden: true,
|
|
838
|
-
limit: 100,
|
|
839
|
-
}).catch(() => null));
|
|
840
|
-
if (selection === null) {
|
|
841
|
-
return codexWriteProbeFailure("CODEX_WRITE_PROBE_MODEL_PIN_UNAVAILABLE");
|
|
842
|
-
}
|
|
843
|
-
const [model, effort] = selection;
|
|
844
|
-
const loadedBefore = parseLoadedThreadIds(await this.request("thread/loaded/list", {}));
|
|
845
|
-
if (loadedBefore === null)
|
|
846
|
-
return result;
|
|
847
|
-
const runtime = {
|
|
848
|
-
expectedCwd: input.cwd,
|
|
849
|
-
expectedEffort: effort,
|
|
850
|
-
expectedModel: model,
|
|
851
|
-
failCode: null,
|
|
852
|
-
phase: "awaiting_start",
|
|
853
|
-
resolveTurn: null,
|
|
854
|
-
settingsEchoObserved: false,
|
|
855
|
-
settingsThreadId: null,
|
|
856
|
-
threadId: null,
|
|
857
|
-
tokenCount: null,
|
|
858
|
-
turnId: null,
|
|
859
|
-
};
|
|
860
|
-
this.probeRuntime = runtime;
|
|
861
|
-
const started = await this.request("thread/start", {
|
|
862
|
-
allowProviderModelFallback: false,
|
|
863
|
-
approvalPolicy: "never",
|
|
864
|
-
cwd: input.cwd,
|
|
865
|
-
dynamicTools: [],
|
|
866
|
-
environments: [],
|
|
867
|
-
ephemeral: false,
|
|
868
|
-
model,
|
|
869
|
-
runtimeWorkspaceRoots: [],
|
|
870
|
-
sandbox: "read-only",
|
|
871
|
-
selectedCapabilityRoots: [],
|
|
872
|
-
});
|
|
873
|
-
const candidateThreadId = this.probeCleanupThreadId(started, loadedBefore, input.forbiddenThreadIds);
|
|
874
|
-
if (candidateThreadId === null ||
|
|
875
|
-
!this.validateProbeThread(started, input.cwd, model, candidateThreadId)) {
|
|
876
|
-
throw new CodexConnectorError("RESULT_SCHEMA_MISMATCH", true);
|
|
877
|
-
}
|
|
878
|
-
threadId = candidateThreadId;
|
|
879
|
-
runtime.threadId = candidateThreadId;
|
|
880
|
-
if (runtime.settingsThreadId !== null &&
|
|
881
|
-
runtime.settingsThreadId !== candidateThreadId) {
|
|
882
|
-
runtime.failCode = "CODEX_WRITE_PROBE_THREAD_SETUP_FAILED";
|
|
883
|
-
}
|
|
884
|
-
if (runtime.failCode === null) {
|
|
885
|
-
const turnResult = await this.request("turn/start", {
|
|
886
|
-
approvalPolicy: "never",
|
|
887
|
-
cwd: input.cwd,
|
|
888
|
-
effort,
|
|
889
|
-
environments: [],
|
|
890
|
-
input: [{ text: CODEX_WRITE_PROBE_INPUT, type: "text" }],
|
|
891
|
-
model,
|
|
892
|
-
runtimeWorkspaceRoots: [],
|
|
893
|
-
sandboxPolicy: { networkAccess: false, type: "readOnly" },
|
|
894
|
-
threadId,
|
|
895
|
-
});
|
|
896
|
-
const turn = isRecord(turnResult) ? parseTurn(turnResult.turn) : null;
|
|
897
|
-
if (turn === null ||
|
|
898
|
-
!isRecord(turnResult) ||
|
|
899
|
-
!isRecord(turnResult.turn) ||
|
|
900
|
-
!Array.isArray(turnResult.turn.items) ||
|
|
901
|
-
(turn.status !== "inProgress" && turn.status !== "completed")) {
|
|
902
|
-
throw new CodexConnectorError("RESULT_SCHEMA_MISMATCH", true);
|
|
903
|
-
}
|
|
904
|
-
if (runtime.turnId !== null && runtime.turnId !== turn.id) {
|
|
905
|
-
throw new CodexConnectorError("RESULT_SCHEMA_MISMATCH", true);
|
|
906
|
-
}
|
|
907
|
-
runtime.turnId = turn.id;
|
|
908
|
-
await this.waitForProbeTurn(runtime);
|
|
909
|
-
}
|
|
910
|
-
if (runtime.failCode !== null) {
|
|
911
|
-
result = codexWriteProbeFailure(runtime.failCode, {
|
|
912
|
-
settingsEchoObserved: runtime.settingsEchoObserved,
|
|
913
|
-
...(runtime.tokenCount === null
|
|
914
|
-
? {}
|
|
915
|
-
: { tokenCount: runtime.tokenCount }),
|
|
916
|
-
});
|
|
917
|
-
}
|
|
918
|
-
else if (runtime.phase === "terminal" &&
|
|
919
|
-
runtime.tokenCount !== null) {
|
|
920
|
-
const afterStat = await stat(input.cwd, { bigint: true });
|
|
921
|
-
const afterEntries = await readdir(input.cwd);
|
|
922
|
-
if (afterEntries.length === 0 &&
|
|
923
|
-
afterStat.dev === beforeStat.dev &&
|
|
924
|
-
afterStat.ino === beforeStat.ino &&
|
|
925
|
-
afterStat.mtimeNs === beforeStat.mtimeNs &&
|
|
926
|
-
afterStat.uid === beforeStat.uid &&
|
|
927
|
-
afterStat.mode === beforeStat.mode) {
|
|
928
|
-
result = {
|
|
929
|
-
archivedThreadCount: 1,
|
|
930
|
-
outcome: "pass",
|
|
931
|
-
settingsEchoObserved: runtime.settingsEchoObserved,
|
|
932
|
-
tokenCount: runtime.tokenCount,
|
|
933
|
-
};
|
|
934
|
-
}
|
|
935
|
-
else {
|
|
936
|
-
result = codexWriteProbeFailure("CODEX_WRITE_PROBE_TOOL_ACTIVITY_OBSERVED", {
|
|
937
|
-
settingsEchoObserved: runtime.settingsEchoObserved,
|
|
938
|
-
tokenCount: runtime.tokenCount,
|
|
939
|
-
});
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
catch (error) {
|
|
944
|
-
const normalized = this.normalizeError(error, true);
|
|
945
|
-
const runtime = this.probeRuntime;
|
|
946
|
-
result = codexWriteProbeFailure(normalized.code === "REQUEST_TIMEOUT"
|
|
947
|
-
? "CODEX_WRITE_PROBE_TIMEOUT"
|
|
948
|
-
: "CODEX_WRITE_PROBE_THREAD_SETUP_FAILED", {
|
|
949
|
-
settingsEchoObserved: runtime?.settingsEchoObserved ?? false,
|
|
950
|
-
...(runtime?.tokenCount === null || runtime?.tokenCount === undefined
|
|
951
|
-
? {}
|
|
952
|
-
: { tokenCount: runtime.tokenCount }),
|
|
953
|
-
});
|
|
954
|
-
}
|
|
955
|
-
finally {
|
|
956
|
-
const runtime = this.probeRuntime;
|
|
957
|
-
if (threadId !== null &&
|
|
958
|
-
runtime !== null &&
|
|
959
|
-
runtime.turnId !== null &&
|
|
960
|
-
(result.outcome === "fail" || runtime.failCode !== null)) {
|
|
961
|
-
try {
|
|
962
|
-
await this.request("turn/interrupt", {
|
|
963
|
-
threadId,
|
|
964
|
-
turnId: runtime.turnId,
|
|
965
|
-
});
|
|
966
|
-
}
|
|
967
|
-
catch {
|
|
968
|
-
// Archival remains the one required cleanup proof below.
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
let cleanupUnconfirmed = runtime !== null && threadId === null;
|
|
972
|
-
if (threadId !== null) {
|
|
973
|
-
try {
|
|
974
|
-
const archived = await this.request("thread/archive", { threadId });
|
|
975
|
-
if (!isRecord(archived) || Object.keys(archived).length !== 0) {
|
|
976
|
-
throw new CodexConnectorError("RESULT_SCHEMA_MISMATCH");
|
|
977
|
-
}
|
|
978
|
-
archivedThreadCount = 1;
|
|
979
|
-
const unsubscribed = await this.request("thread/unsubscribe", {
|
|
980
|
-
threadId,
|
|
981
|
-
});
|
|
982
|
-
if (!isRecord(unsubscribed) ||
|
|
983
|
-
(unsubscribed.status !== "unsubscribed" &&
|
|
984
|
-
unsubscribed.status !== "notSubscribed" &&
|
|
985
|
-
unsubscribed.status !== "notLoaded")) {
|
|
986
|
-
throw new CodexConnectorError("RESULT_SCHEMA_MISMATCH");
|
|
987
|
-
}
|
|
988
|
-
const loadedAfter = parseLoadedThreadIds(await this.request("thread/loaded/list", {}));
|
|
989
|
-
if (loadedAfter === null || loadedAfter.includes(threadId)) {
|
|
990
|
-
throw new CodexConnectorError("RESULT_SCHEMA_MISMATCH");
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
catch {
|
|
994
|
-
cleanupUnconfirmed = true;
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
const tokenCount = runtime?.tokenCount ?? result.tokenCount;
|
|
998
|
-
const evidence = {
|
|
999
|
-
...(archivedThreadCount === undefined ? {} : { archivedThreadCount }),
|
|
1000
|
-
settingsEchoObserved: runtime?.settingsEchoObserved ?? result.settingsEchoObserved,
|
|
1001
|
-
...(tokenCount === undefined ? {} : { tokenCount }),
|
|
1002
|
-
};
|
|
1003
|
-
if (cleanupUnconfirmed || (result.outcome === "pass" && archivedThreadCount !== 1)) {
|
|
1004
|
-
result = codexWriteProbeFailure("CODEX_WRITE_PROBE_CLEANUP_UNCONFIRMED", evidence);
|
|
1005
|
-
}
|
|
1006
|
-
else if (runtime?.failCode !== null && runtime?.failCode !== undefined) {
|
|
1007
|
-
result = codexWriteProbeFailure(runtime.failCode, evidence);
|
|
1008
|
-
}
|
|
1009
|
-
if (result.outcome === "fail") {
|
|
1010
|
-
result = codexWriteProbeFailure(result.safeErrorCode, evidence);
|
|
1011
|
-
}
|
|
1012
|
-
this.probeRuntime = null;
|
|
1013
|
-
}
|
|
1014
|
-
return result;
|
|
1015
|
-
}
|
|
1016
|
-
selectProbeModel(value) {
|
|
1017
|
-
if (!isRecord(value) || !Array.isArray(value.data) || value.data.length > 100) {
|
|
1018
|
-
return null;
|
|
1019
|
-
}
|
|
1020
|
-
const data = value.data;
|
|
1021
|
-
for (const model of CODEX_PROBE_MODEL_PREFERENCE) {
|
|
1022
|
-
const candidate = data.find((entry) => isRecord(entry) &&
|
|
1023
|
-
entry.model === model &&
|
|
1024
|
-
entry.hidden === false);
|
|
1025
|
-
if (!isRecord(candidate) || !Array.isArray(candidate.supportedReasoningEfforts)) {
|
|
1026
|
-
continue;
|
|
1027
|
-
}
|
|
1028
|
-
const supported = candidate.supportedReasoningEfforts;
|
|
1029
|
-
const effort = CODEX_PROBE_EFFORT.find((preference) => supported.some((option) => isRecord(option) && option.reasoningEffort === preference));
|
|
1030
|
-
if (effort !== undefined)
|
|
1031
|
-
return [model, effort];
|
|
1032
|
-
}
|
|
1033
|
-
return null;
|
|
1034
|
-
}
|
|
1035
|
-
probeCleanupThreadId(value, loadedBefore, forbiddenThreadIds) {
|
|
1036
|
-
if (!isRecord(value) ||
|
|
1037
|
-
!isRecord(value.thread) ||
|
|
1038
|
-
typeof value.thread.id !== "string" ||
|
|
1039
|
-
!validUuidV7(value.thread.id) ||
|
|
1040
|
-
value.thread.id === this.route.threadId ||
|
|
1041
|
-
loadedBefore.includes(value.thread.id) ||
|
|
1042
|
-
forbiddenThreadIds.includes(value.thread.id)) {
|
|
1043
|
-
return null;
|
|
1044
|
-
}
|
|
1045
|
-
return value.thread.id;
|
|
1046
|
-
}
|
|
1047
|
-
validateProbeThread(value, cwd, model, threadId) {
|
|
1048
|
-
if (!isRecord(value) || !isRecord(value.thread))
|
|
1049
|
-
return false;
|
|
1050
|
-
const roots = value.runtimeWorkspaceRoots;
|
|
1051
|
-
const networkAccess = isRecord(value.sandbox)
|
|
1052
|
-
? value.sandbox.networkAccess
|
|
1053
|
-
: undefined;
|
|
1054
|
-
return (value.approvalPolicy === "never" &&
|
|
1055
|
-
value.cwd === cwd &&
|
|
1056
|
-
value.model === model &&
|
|
1057
|
-
isRecord(value.sandbox) &&
|
|
1058
|
-
value.sandbox.type === "readOnly" &&
|
|
1059
|
-
(networkAccess === undefined || networkAccess === false) &&
|
|
1060
|
-
(roots === undefined || (Array.isArray(roots) && roots.length === 0)) &&
|
|
1061
|
-
value.thread.id === threadId &&
|
|
1062
|
-
value.thread.cwd === cwd &&
|
|
1063
|
-
value.thread.ephemeral === false &&
|
|
1064
|
-
isRecord(value.thread.status) &&
|
|
1065
|
-
value.thread.status.type === "idle" &&
|
|
1066
|
-
Array.isArray(value.thread.turns) &&
|
|
1067
|
-
value.thread.turns.length === 0);
|
|
1068
|
-
}
|
|
1069
|
-
async waitForProbeTurn(runtime) {
|
|
1070
|
-
if (runtime.failCode !== null ||
|
|
1071
|
-
(runtime.phase === "terminal" && runtime.tokenCount !== null)) {
|
|
1072
|
-
return;
|
|
1073
|
-
}
|
|
1074
|
-
let timer;
|
|
1075
|
-
await new Promise((resolve) => {
|
|
1076
|
-
runtime.resolveTurn = resolve;
|
|
1077
|
-
timer = setTimeout(resolve, this.turnWatchdogMs);
|
|
1078
|
-
});
|
|
1079
|
-
runtime.resolveTurn = null;
|
|
1080
|
-
if (timer !== undefined)
|
|
1081
|
-
clearTimeout(timer);
|
|
1082
|
-
if (runtime.failCode === null &&
|
|
1083
|
-
!(runtime.phase === "terminal" && runtime.tokenCount !== null)) {
|
|
1084
|
-
runtime.failCode = "CODEX_WRITE_PROBE_TIMEOUT";
|
|
1085
|
-
}
|
|
1086
|
-
}
|
|
1087
|
-
settleProbeRuntime() {
|
|
1088
|
-
const runtime = this.probeRuntime;
|
|
1089
|
-
if (runtime === null || runtime.resolveTurn === null)
|
|
1090
|
-
return;
|
|
1091
|
-
if (runtime.failCode !== null ||
|
|
1092
|
-
(runtime.phase === "terminal" && runtime.tokenCount !== null)) {
|
|
1093
|
-
runtime.resolveTurn();
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
settleProbeConnectionLoss() {
|
|
1097
|
-
if (this.probeRuntime === null)
|
|
1098
|
-
return;
|
|
1099
|
-
this.probeRuntime.failCode ??= "CODEX_WRITE_PROBE_THREAD_SETUP_FAILED";
|
|
1100
|
-
this.settleProbeRuntime();
|
|
1101
|
-
}
|
|
1102
677
|
async initialize() {
|
|
1103
678
|
try {
|
|
1104
679
|
const result = await this.request("initialize", {
|
|
1105
680
|
capabilities: {
|
|
1106
681
|
// `thread/resume.excludeTurns` is field-gated behind this capability
|
|
1107
|
-
//
|
|
1108
|
-
// remains closed and exposes no experimental method.
|
|
682
|
+
// and is the only reason Embassy enables the experimental API.
|
|
1109
683
|
experimentalApi: true,
|
|
1110
|
-
optOutNotificationMethods: OUTPUT_NOTIFICATION_OPT_OUTS
|
|
684
|
+
optOutNotificationMethods: OUTPUT_NOTIFICATION_OPT_OUTS,
|
|
1111
685
|
},
|
|
1112
686
|
clientInfo: this.clientInfo,
|
|
1113
687
|
});
|
|
@@ -1173,7 +747,6 @@ export class CodexAppServerConnector {
|
|
|
1173
747
|
}
|
|
1174
748
|
async startMessage(message) {
|
|
1175
749
|
this.assertNoRequest();
|
|
1176
|
-
this.assertWritableReady();
|
|
1177
750
|
if (this.messageExpired(message)) {
|
|
1178
751
|
this.expireMessage(message);
|
|
1179
752
|
return "expired";
|
|
@@ -1272,7 +845,6 @@ export class CodexAppServerConnector {
|
|
|
1272
845
|
*/
|
|
1273
846
|
async steerMessage(message) {
|
|
1274
847
|
this.assertNoRequest();
|
|
1275
|
-
this.assertWritableReady();
|
|
1276
848
|
if (this.messageExpired(message)) {
|
|
1277
849
|
this.expireMessage(message);
|
|
1278
850
|
return "expired";
|
|
@@ -1321,7 +893,6 @@ export class CodexAppServerConnector {
|
|
|
1321
893
|
if (this.drainScheduled ||
|
|
1322
894
|
this.connection !== "ready" ||
|
|
1323
895
|
this.routeStatus !== "idle" ||
|
|
1324
|
-
this.currentWriteBlockCode() !== null ||
|
|
1325
896
|
this.inFlightMethod !== null ||
|
|
1326
897
|
this.queue.length === 0) {
|
|
1327
898
|
return;
|
|
@@ -1335,7 +906,6 @@ export class CodexAppServerConnector {
|
|
|
1335
906
|
this.dropExpiredQueuedMessages();
|
|
1336
907
|
if (this.connection !== "ready" ||
|
|
1337
908
|
this.routeStatus !== "idle" ||
|
|
1338
|
-
this.currentWriteBlockCode() !== null ||
|
|
1339
909
|
this.inFlightMethod !== null ||
|
|
1340
910
|
this.queue.length === 0) {
|
|
1341
911
|
return;
|
|
@@ -1357,8 +927,7 @@ export class CodexAppServerConnector {
|
|
|
1357
927
|
}
|
|
1358
928
|
if (guard.revision !== this.revision ||
|
|
1359
929
|
guard.status !== this.routeStatus ||
|
|
1360
|
-
guard.activeTurnId !== this.activeTurnId
|
|
1361
|
-
guard.writableReady !== this.isWritableReady()) {
|
|
930
|
+
guard.activeTurnId !== this.activeTurnId) {
|
|
1362
931
|
throw new CodexConnectorError("ROUTE_CAS_MISMATCH");
|
|
1363
932
|
}
|
|
1364
933
|
}
|
|
@@ -1374,17 +943,6 @@ export class CodexAppServerConnector {
|
|
|
1374
943
|
throw new CodexConnectorError("ROUTE_BUSY");
|
|
1375
944
|
}
|
|
1376
945
|
}
|
|
1377
|
-
currentWriteBlockCode() {
|
|
1378
|
-
return this.writesEnabled ? null : "WRITES_DISABLED";
|
|
1379
|
-
}
|
|
1380
|
-
isWritableReady() {
|
|
1381
|
-
return this.connection === "ready" && this.currentWriteBlockCode() === null;
|
|
1382
|
-
}
|
|
1383
|
-
assertWritableReady() {
|
|
1384
|
-
const code = this.currentWriteBlockCode();
|
|
1385
|
-
if (code !== null)
|
|
1386
|
-
throw new CodexConnectorError(code);
|
|
1387
|
-
}
|
|
1388
946
|
/**
|
|
1389
947
|
* Refresh the exact thread on this same connection immediately before a
|
|
1390
948
|
* `turn/start`. This confirms the registered task is still the observed idle
|
|
@@ -1392,7 +950,6 @@ export class CodexAppServerConnector {
|
|
|
1392
950
|
* policy.
|
|
1393
951
|
*/
|
|
1394
952
|
async refreshExactRouteBoundary() {
|
|
1395
|
-
this.assertWritableReady();
|
|
1396
953
|
if (this.routeStatus !== "starting" ||
|
|
1397
954
|
this.activeMessageId === null ||
|
|
1398
955
|
this.activeTurnId !== null ||
|
|
@@ -1421,7 +978,6 @@ export class CodexAppServerConnector {
|
|
|
1421
978
|
this.setRouteStatus(refreshedStatus);
|
|
1422
979
|
throw new CodexConnectorError("ROUTE_NOT_READY");
|
|
1423
980
|
}
|
|
1424
|
-
this.assertWritableReady();
|
|
1425
981
|
if (this.statusEpoch !== statusEpoch ||
|
|
1426
982
|
this.routeStatus !== "starting" ||
|
|
1427
983
|
this.activeMessageId === null ||
|
|
@@ -1475,11 +1031,6 @@ export class CodexAppServerConnector {
|
|
|
1475
1031
|
if (method !== "initialize" && !V1_METHOD_SET.has(method)) {
|
|
1476
1032
|
return Promise.reject(new CodexConnectorError("METHOD_NOT_ALLOWED"));
|
|
1477
1033
|
}
|
|
1478
|
-
if (method !== "initialize" &&
|
|
1479
|
-
PROBE_ONLY_METHODS.has(method) &&
|
|
1480
|
-
!this.writeCompatibilityProbe) {
|
|
1481
|
-
return Promise.reject(new CodexConnectorError("METHOD_NOT_ALLOWED"));
|
|
1482
|
-
}
|
|
1483
1034
|
if (method === "initialize") {
|
|
1484
1035
|
if (this.initializeRequested || this.connection !== "connecting") {
|
|
1485
1036
|
return Promise.reject(new CodexConnectorError("METHOD_NOT_ALLOWED"));
|
|
@@ -1596,8 +1147,6 @@ export class CodexAppServerConnector {
|
|
|
1596
1147
|
pending.resolve(parsed.result);
|
|
1597
1148
|
}
|
|
1598
1149
|
handleNotification(method, params) {
|
|
1599
|
-
if (this.handleProbeNotification(method, params))
|
|
1600
|
-
return;
|
|
1601
1150
|
if (method === "item/completed") {
|
|
1602
1151
|
this.handleCompletedItem(params);
|
|
1603
1152
|
return;
|
|
@@ -1827,14 +1376,6 @@ export class CodexAppServerConnector {
|
|
|
1827
1376
|
// item/model/tool payloads are neither normalized nor retained.
|
|
1828
1377
|
}
|
|
1829
1378
|
handleServerRequest(method, params) {
|
|
1830
|
-
if (this.probeRuntime !== null &&
|
|
1831
|
-
isRecord(params) &&
|
|
1832
|
-
params.threadId === this.probeRuntime.threadId) {
|
|
1833
|
-
this.probeRuntime.failCode ??=
|
|
1834
|
-
"CODEX_WRITE_PROBE_TOOL_ACTIVITY_OBSERVED";
|
|
1835
|
-
this.settleProbeRuntime();
|
|
1836
|
-
return;
|
|
1837
|
-
}
|
|
1838
1379
|
if (!APPROVAL_REQUEST_METHODS.has(method)) {
|
|
1839
1380
|
this.emit("server_request_ignored");
|
|
1840
1381
|
return;
|
|
@@ -1859,135 +1400,6 @@ export class CodexAppServerConnector {
|
|
|
1859
1400
|
// Intentionally no JSON-RPC response: the gateway is not an approval UI or
|
|
1860
1401
|
// authority. The owning Desktop client must resolve the request.
|
|
1861
1402
|
}
|
|
1862
|
-
handleProbeNotification(method, params) {
|
|
1863
|
-
const runtime = this.probeRuntime;
|
|
1864
|
-
if (runtime === null)
|
|
1865
|
-
return false;
|
|
1866
|
-
const fail = (code) => {
|
|
1867
|
-
runtime.failCode ??= code;
|
|
1868
|
-
this.settleProbeRuntime();
|
|
1869
|
-
};
|
|
1870
|
-
if (method === "model/rerouted") {
|
|
1871
|
-
fail("CODEX_WRITE_PROBE_MODEL_REROUTED");
|
|
1872
|
-
return true;
|
|
1873
|
-
}
|
|
1874
|
-
if (!isRecord(params))
|
|
1875
|
-
return false;
|
|
1876
|
-
const observeTurnId = (value) => {
|
|
1877
|
-
if (typeof value !== "string" || !validOpaqueId(value))
|
|
1878
|
-
return false;
|
|
1879
|
-
if (runtime.turnId !== null && runtime.turnId !== value)
|
|
1880
|
-
return false;
|
|
1881
|
-
runtime.turnId = value;
|
|
1882
|
-
return true;
|
|
1883
|
-
};
|
|
1884
|
-
if (method === "thread/settings/updated") {
|
|
1885
|
-
runtime.settingsEchoObserved = true;
|
|
1886
|
-
const settingsThreadId = params.threadId;
|
|
1887
|
-
const settings = params.threadSettings;
|
|
1888
|
-
const sandbox = isRecord(settings) ? settings.sandboxPolicy : null;
|
|
1889
|
-
if (typeof settingsThreadId !== "string" ||
|
|
1890
|
-
!validUuidV7(settingsThreadId) ||
|
|
1891
|
-
(runtime.threadId !== null && runtime.threadId !== settingsThreadId) ||
|
|
1892
|
-
(runtime.settingsThreadId !== null &&
|
|
1893
|
-
runtime.settingsThreadId !== settingsThreadId) ||
|
|
1894
|
-
!isRecord(settings) ||
|
|
1895
|
-
settings.cwd !== runtime.expectedCwd ||
|
|
1896
|
-
settings.model !== runtime.expectedModel ||
|
|
1897
|
-
settings.approvalPolicy !== "never" ||
|
|
1898
|
-
(settings.effort !== undefined &&
|
|
1899
|
-
settings.effort !== runtime.expectedEffort) ||
|
|
1900
|
-
!isRecord(sandbox) ||
|
|
1901
|
-
sandbox.type !== "readOnly" ||
|
|
1902
|
-
(sandbox.networkAccess !== undefined &&
|
|
1903
|
-
sandbox.networkAccess !== false)) {
|
|
1904
|
-
fail("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
1905
|
-
}
|
|
1906
|
-
else {
|
|
1907
|
-
runtime.settingsThreadId = settingsThreadId;
|
|
1908
|
-
}
|
|
1909
|
-
return true;
|
|
1910
|
-
}
|
|
1911
|
-
if (runtime.threadId === null || params.threadId !== runtime.threadId) {
|
|
1912
|
-
return false;
|
|
1913
|
-
}
|
|
1914
|
-
if (PROBE_TOOL_NOTIFICATION_METHODS.has(method)) {
|
|
1915
|
-
fail("CODEX_WRITE_PROBE_TOOL_ACTIVITY_OBSERVED");
|
|
1916
|
-
return true;
|
|
1917
|
-
}
|
|
1918
|
-
if (method === "thread/tokenUsage/updated") {
|
|
1919
|
-
const totalTokens = observeTurnId(params.turnId) &&
|
|
1920
|
-
isRecord(params.tokenUsage) &&
|
|
1921
|
-
isRecord(params.tokenUsage.last)
|
|
1922
|
-
? params.tokenUsage.last.totalTokens
|
|
1923
|
-
: null;
|
|
1924
|
-
if (!Number.isSafeInteger(totalTokens) ||
|
|
1925
|
-
totalTokens < 0 ||
|
|
1926
|
-
totalTokens > MAX_PROBE_TOKEN_COUNT) {
|
|
1927
|
-
fail("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
1928
|
-
}
|
|
1929
|
-
else {
|
|
1930
|
-
runtime.tokenCount = totalTokens;
|
|
1931
|
-
this.settleProbeRuntime();
|
|
1932
|
-
}
|
|
1933
|
-
return true;
|
|
1934
|
-
}
|
|
1935
|
-
if (method === "turn/started" || method === "turn/completed") {
|
|
1936
|
-
const turn = parseTurn(params.turn);
|
|
1937
|
-
if (turn === null ||
|
|
1938
|
-
!isRecord(params.turn) ||
|
|
1939
|
-
!Array.isArray(params.turn.items) ||
|
|
1940
|
-
!observeTurnId(turn.id)) {
|
|
1941
|
-
fail("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
1942
|
-
}
|
|
1943
|
-
else if (method === "turn/started") {
|
|
1944
|
-
if (turn.status !== "inProgress" ||
|
|
1945
|
-
runtime.phase !== "awaiting_start") {
|
|
1946
|
-
fail("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
1947
|
-
}
|
|
1948
|
-
else {
|
|
1949
|
-
runtime.phase = "started";
|
|
1950
|
-
}
|
|
1951
|
-
}
|
|
1952
|
-
else if (turn.status === "completed" &&
|
|
1953
|
-
runtime.phase === "item_completed") {
|
|
1954
|
-
runtime.phase = "terminal";
|
|
1955
|
-
this.settleProbeRuntime();
|
|
1956
|
-
}
|
|
1957
|
-
else {
|
|
1958
|
-
fail("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
1959
|
-
}
|
|
1960
|
-
return true;
|
|
1961
|
-
}
|
|
1962
|
-
if (method === "item/started" || method === "item/completed") {
|
|
1963
|
-
const observedAt = method === "item/started" ? params.startedAtMs : params.completedAtMs;
|
|
1964
|
-
const itemType = isRecord(params.item) ? params.item.type : null;
|
|
1965
|
-
if (typeof itemType === "string" &&
|
|
1966
|
-
!PROBE_PASSIVE_ITEM_TYPES.has(itemType)) {
|
|
1967
|
-
fail("CODEX_WRITE_PROBE_TOOL_ACTIVITY_OBSERVED");
|
|
1968
|
-
return true;
|
|
1969
|
-
}
|
|
1970
|
-
if (!observeTurnId(params.turnId) ||
|
|
1971
|
-
!isRecord(params.item) ||
|
|
1972
|
-
typeof itemType !== "string" ||
|
|
1973
|
-
!Number.isSafeInteger(observedAt) ||
|
|
1974
|
-
observedAt < 0 ||
|
|
1975
|
-
(runtime.phase !== "started" && runtime.phase !== "item_completed")) {
|
|
1976
|
-
fail("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
1977
|
-
}
|
|
1978
|
-
else if (method === "item/completed" &&
|
|
1979
|
-
itemType === "agentMessage") {
|
|
1980
|
-
if (runtime.phase !== "started") {
|
|
1981
|
-
fail("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
1982
|
-
}
|
|
1983
|
-
else {
|
|
1984
|
-
runtime.phase = "item_completed";
|
|
1985
|
-
}
|
|
1986
|
-
}
|
|
1987
|
-
return true;
|
|
1988
|
-
}
|
|
1989
|
-
return false;
|
|
1990
|
-
}
|
|
1991
1403
|
handleCompletedItem(params) {
|
|
1992
1404
|
if (!isRecord(params) ||
|
|
1993
1405
|
typeof params.threadId !== "string") {
|
|
@@ -2131,7 +1543,6 @@ export class CodexAppServerConnector {
|
|
|
2131
1543
|
return;
|
|
2132
1544
|
this.faulting = true;
|
|
2133
1545
|
this.connection = "faulted";
|
|
2134
|
-
this.settleProbeConnectionLoss();
|
|
2135
1546
|
this.initialized = false;
|
|
2136
1547
|
this.routeStatus = "stale";
|
|
2137
1548
|
this.selectedThreadObserved = false;
|
|
@@ -2152,7 +1563,6 @@ export class CodexAppServerConnector {
|
|
|
2152
1563
|
if (this.connection === "closed" || this.connection === "faulted")
|
|
2153
1564
|
return;
|
|
2154
1565
|
this.connection = "closed";
|
|
2155
|
-
this.settleProbeConnectionLoss();
|
|
2156
1566
|
this.initialized = false;
|
|
2157
1567
|
this.routeStatus = "stale";
|
|
2158
1568
|
this.selectedThreadObserved = false;
|