agent-embassy 1.6.0 → 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 +31 -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 -597
- 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 -84
- package/dist/src/gateway/providers.js +69 -806
- 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 -432
- 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,25 +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
|
-
throw new CodexConnectorError("INVALID_CONFIGURATION");
|
|
480
|
-
}
|
|
481
|
-
this.writesEnabled = options.writesEnabled;
|
|
482
|
-
this.writeCompatibilityProbe = options.writeCompatibilityProbe === true;
|
|
377
|
+
validateCompatibility(options.compatibility, this.route);
|
|
483
378
|
this.transport = options.transport;
|
|
484
379
|
this.clientInfo = validateClientInfo(options.clientInfo ?? {
|
|
485
380
|
name: "agent_embassy_gateway",
|
|
486
381
|
title: "Embassy Gateway",
|
|
487
|
-
version: "1.
|
|
382
|
+
version: "1.7.0",
|
|
488
383
|
});
|
|
489
384
|
this.maxFrameBytes = positiveInteger(options.maxFrameBytes ?? DEFAULT_MAX_FRAME_BYTES, "INVALID_CONFIGURATION");
|
|
490
385
|
this.maxDeadlineMs = positiveInteger(options.maxDeadlineMs ?? DEFAULT_MAX_DEADLINE_MS, "INVALID_CONFIGURATION");
|
|
@@ -500,7 +395,6 @@ export class CodexAppServerConnector {
|
|
|
500
395
|
this.unlisten.push(this.transport.onMessage((payload) => this.handlePayload(payload)), this.transport.onClose(() => this.handleDisconnect()), this.transport.onError(() => this.protocolFault("TRANSPORT_CLOSED")));
|
|
501
396
|
}
|
|
502
397
|
observation() {
|
|
503
|
-
const writeBlockCode = this.currentWriteBlockCode();
|
|
504
398
|
return {
|
|
505
399
|
connection: this.connection,
|
|
506
400
|
eventSequence: this.eventSequence,
|
|
@@ -508,18 +402,14 @@ export class CodexAppServerConnector {
|
|
|
508
402
|
queueDepth: this.queue.length,
|
|
509
403
|
requestInFlight: this.inFlightMethod !== null,
|
|
510
404
|
routeStatus: this.routeStatus,
|
|
511
|
-
writableReady: this.isWritableReady(),
|
|
512
|
-
writeBlockCode,
|
|
513
405
|
};
|
|
514
406
|
}
|
|
515
407
|
guard() {
|
|
516
|
-
const writableReady = this.isWritableReady();
|
|
517
408
|
return {
|
|
518
409
|
...this.route,
|
|
519
410
|
activeTurnId: this.activeTurnId,
|
|
520
411
|
revision: this.revision,
|
|
521
412
|
status: this.routeStatus,
|
|
522
|
-
writableReady,
|
|
523
413
|
};
|
|
524
414
|
}
|
|
525
415
|
async observeLoadedThread(guard) {
|
|
@@ -587,10 +477,7 @@ export class CodexAppServerConnector {
|
|
|
587
477
|
throw new CodexConnectorError("THREAD_NOT_OBSERVED");
|
|
588
478
|
}
|
|
589
479
|
if (this.routeStatus !== "unknown" &&
|
|
590
|
-
this.routeStatus !== "not_loaded"
|
|
591
|
-
!(this.currentWriteBlockCode() !== null &&
|
|
592
|
-
this.routeStatus === "idle" &&
|
|
593
|
-
this.activeTurnId === null)) {
|
|
480
|
+
this.routeStatus !== "not_loaded") {
|
|
594
481
|
throw new CodexConnectorError("ROUTE_BUSY");
|
|
595
482
|
}
|
|
596
483
|
const statusEpoch = this.statusEpoch;
|
|
@@ -622,10 +509,6 @@ export class CodexAppServerConnector {
|
|
|
622
509
|
}
|
|
623
510
|
}
|
|
624
511
|
this.emit("thread_resumed");
|
|
625
|
-
const writeBlockCode = this.currentWriteBlockCode();
|
|
626
|
-
if (writeBlockCode !== null) {
|
|
627
|
-
this.emit("route_write_blocked", { errorCode: writeBlockCode });
|
|
628
|
-
}
|
|
629
512
|
}
|
|
630
513
|
catch (error) {
|
|
631
514
|
const normalized = this.normalizeError(error, true);
|
|
@@ -700,7 +583,6 @@ export class CodexAppServerConnector {
|
|
|
700
583
|
throw new CodexConnectorError("THREAD_NOT_OBSERVED");
|
|
701
584
|
}
|
|
702
585
|
if (input.steer === true && this.routeStatus === "active") {
|
|
703
|
-
this.assertWritableReady();
|
|
704
586
|
const message = this.validateMessage(input);
|
|
705
587
|
if (this.activeTurnId === null || this.inFlightMethod !== null) {
|
|
706
588
|
this.acceptedMessageIds.delete(message.messageId);
|
|
@@ -713,7 +595,6 @@ export class CodexAppServerConnector {
|
|
|
713
595
|
this.routeStatus === "active" ||
|
|
714
596
|
this.routeStatus === "waiting_approval" ||
|
|
715
597
|
this.routeStatus === "interrupting") {
|
|
716
|
-
this.assertWritableReady();
|
|
717
598
|
const message = this.validateMessage(input);
|
|
718
599
|
if (input.steer === true) {
|
|
719
600
|
this.acceptedMessageIds.delete(message.messageId);
|
|
@@ -722,7 +603,6 @@ export class CodexAppServerConnector {
|
|
|
722
603
|
this.enqueue(message);
|
|
723
604
|
return { disposition: "queued", observation: this.observation() };
|
|
724
605
|
}
|
|
725
|
-
this.assertWritableReady();
|
|
726
606
|
const message = this.validateMessage(input);
|
|
727
607
|
if (this.routeStatus !== "idle") {
|
|
728
608
|
this.acceptedMessageIds.delete(message.messageId);
|
|
@@ -794,318 +674,14 @@ export class CodexAppServerConnector {
|
|
|
794
674
|
this.handleDisconnect();
|
|
795
675
|
}
|
|
796
676
|
}
|
|
797
|
-
/**
|
|
798
|
-
* Run the one fixed disposable-thread write probe. The method never exposes
|
|
799
|
-
* JSON-RPC, native IDs, content, or route state, and every failure settles as
|
|
800
|
-
* a safe result so compatibility discovery cannot take down the broker.
|
|
801
|
-
*/
|
|
802
|
-
async runWriteCompatibilityProbe(input) {
|
|
803
|
-
if (this.probeAttempted) {
|
|
804
|
-
return codexWriteProbeFailure("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
805
|
-
}
|
|
806
|
-
this.probeAttempted = true;
|
|
807
|
-
let threadId = null;
|
|
808
|
-
let archivedThreadCount;
|
|
809
|
-
let result = codexWriteProbeFailure("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
810
|
-
try {
|
|
811
|
-
if (!this.writeCompatibilityProbe ||
|
|
812
|
-
this.connection !== "ready" ||
|
|
813
|
-
this.inFlightMethod !== null ||
|
|
814
|
-
this.pending.size !== 0 ||
|
|
815
|
-
!isAbsolute(input.cwd) ||
|
|
816
|
-
!Array.isArray(input.forbiddenThreadIds) ||
|
|
817
|
-
!input.forbiddenThreadIds.every((value) => typeof value === "string" && validOpaqueId(value))) {
|
|
818
|
-
return result;
|
|
819
|
-
}
|
|
820
|
-
const beforeStat = await stat(input.cwd, { bigint: true });
|
|
821
|
-
const beforeEntries = await readdir(input.cwd);
|
|
822
|
-
if (!beforeStat.isDirectory() ||
|
|
823
|
-
beforeStat.uid !== BigInt(process.getuid?.() ?? -1) ||
|
|
824
|
-
(beforeStat.mode & 511n) !== 448n ||
|
|
825
|
-
beforeEntries.length !== 0) {
|
|
826
|
-
return result;
|
|
827
|
-
}
|
|
828
|
-
const limits = rateLimitConstrained(await this.request("account/rateLimits/read", null));
|
|
829
|
-
if (limits === null)
|
|
830
|
-
return result;
|
|
831
|
-
if (limits) {
|
|
832
|
-
return codexWriteProbeFailure("CODEX_WRITE_PROBE_RATE_LIMIT_CONSTRAINED");
|
|
833
|
-
}
|
|
834
|
-
const selection = this.selectProbeModel(await this.request("model/list", {
|
|
835
|
-
includeHidden: true,
|
|
836
|
-
limit: 100,
|
|
837
|
-
}).catch(() => null));
|
|
838
|
-
if (selection === null) {
|
|
839
|
-
return codexWriteProbeFailure("CODEX_WRITE_PROBE_MODEL_PIN_UNAVAILABLE");
|
|
840
|
-
}
|
|
841
|
-
const [model, effort] = selection;
|
|
842
|
-
const loadedBefore = parseLoadedThreadIds(await this.request("thread/loaded/list", {}));
|
|
843
|
-
if (loadedBefore === null)
|
|
844
|
-
return result;
|
|
845
|
-
const runtime = {
|
|
846
|
-
expectedCwd: input.cwd,
|
|
847
|
-
expectedEffort: effort,
|
|
848
|
-
expectedModel: model,
|
|
849
|
-
failCode: null,
|
|
850
|
-
phase: "awaiting_start",
|
|
851
|
-
resolveTurn: null,
|
|
852
|
-
settingsEchoObserved: false,
|
|
853
|
-
settingsThreadId: null,
|
|
854
|
-
threadId: null,
|
|
855
|
-
tokenCount: null,
|
|
856
|
-
turnId: null,
|
|
857
|
-
};
|
|
858
|
-
this.probeRuntime = runtime;
|
|
859
|
-
const started = await this.request("thread/start", {
|
|
860
|
-
allowProviderModelFallback: false,
|
|
861
|
-
approvalPolicy: "never",
|
|
862
|
-
cwd: input.cwd,
|
|
863
|
-
dynamicTools: [],
|
|
864
|
-
environments: [],
|
|
865
|
-
ephemeral: false,
|
|
866
|
-
model,
|
|
867
|
-
runtimeWorkspaceRoots: [],
|
|
868
|
-
sandbox: "read-only",
|
|
869
|
-
selectedCapabilityRoots: [],
|
|
870
|
-
});
|
|
871
|
-
const candidateThreadId = this.probeCleanupThreadId(started, loadedBefore, input.forbiddenThreadIds);
|
|
872
|
-
if (candidateThreadId === null ||
|
|
873
|
-
!this.validateProbeThread(started, input.cwd, model, candidateThreadId)) {
|
|
874
|
-
throw new CodexConnectorError("RESULT_SCHEMA_MISMATCH", true);
|
|
875
|
-
}
|
|
876
|
-
threadId = candidateThreadId;
|
|
877
|
-
runtime.threadId = candidateThreadId;
|
|
878
|
-
if (runtime.settingsThreadId !== null &&
|
|
879
|
-
runtime.settingsThreadId !== candidateThreadId) {
|
|
880
|
-
runtime.failCode = "CODEX_WRITE_PROBE_THREAD_SETUP_FAILED";
|
|
881
|
-
}
|
|
882
|
-
if (runtime.failCode === null) {
|
|
883
|
-
const turnResult = await this.request("turn/start", {
|
|
884
|
-
approvalPolicy: "never",
|
|
885
|
-
cwd: input.cwd,
|
|
886
|
-
effort,
|
|
887
|
-
environments: [],
|
|
888
|
-
input: [{ text: CODEX_WRITE_PROBE_INPUT, type: "text" }],
|
|
889
|
-
model,
|
|
890
|
-
runtimeWorkspaceRoots: [],
|
|
891
|
-
sandboxPolicy: { networkAccess: false, type: "readOnly" },
|
|
892
|
-
threadId,
|
|
893
|
-
});
|
|
894
|
-
const turn = isRecord(turnResult) ? parseTurn(turnResult.turn) : null;
|
|
895
|
-
if (turn === null ||
|
|
896
|
-
!isRecord(turnResult) ||
|
|
897
|
-
!isRecord(turnResult.turn) ||
|
|
898
|
-
!Array.isArray(turnResult.turn.items) ||
|
|
899
|
-
(turn.status !== "inProgress" && turn.status !== "completed")) {
|
|
900
|
-
throw new CodexConnectorError("RESULT_SCHEMA_MISMATCH", true);
|
|
901
|
-
}
|
|
902
|
-
if (runtime.turnId !== null && runtime.turnId !== turn.id) {
|
|
903
|
-
throw new CodexConnectorError("RESULT_SCHEMA_MISMATCH", true);
|
|
904
|
-
}
|
|
905
|
-
runtime.turnId = turn.id;
|
|
906
|
-
await this.waitForProbeTurn(runtime);
|
|
907
|
-
}
|
|
908
|
-
if (runtime.failCode !== null) {
|
|
909
|
-
result = codexWriteProbeFailure(runtime.failCode, {
|
|
910
|
-
settingsEchoObserved: runtime.settingsEchoObserved,
|
|
911
|
-
...(runtime.tokenCount === null
|
|
912
|
-
? {}
|
|
913
|
-
: { tokenCount: runtime.tokenCount }),
|
|
914
|
-
});
|
|
915
|
-
}
|
|
916
|
-
else if (runtime.phase === "terminal" &&
|
|
917
|
-
runtime.tokenCount !== null) {
|
|
918
|
-
const afterStat = await stat(input.cwd, { bigint: true });
|
|
919
|
-
const afterEntries = await readdir(input.cwd);
|
|
920
|
-
if (afterEntries.length === 0 &&
|
|
921
|
-
afterStat.dev === beforeStat.dev &&
|
|
922
|
-
afterStat.ino === beforeStat.ino &&
|
|
923
|
-
afterStat.mtimeNs === beforeStat.mtimeNs &&
|
|
924
|
-
afterStat.uid === beforeStat.uid &&
|
|
925
|
-
afterStat.mode === beforeStat.mode) {
|
|
926
|
-
result = {
|
|
927
|
-
archivedThreadCount: 1,
|
|
928
|
-
outcome: "pass",
|
|
929
|
-
settingsEchoObserved: runtime.settingsEchoObserved,
|
|
930
|
-
tokenCount: runtime.tokenCount,
|
|
931
|
-
};
|
|
932
|
-
}
|
|
933
|
-
else {
|
|
934
|
-
result = codexWriteProbeFailure("CODEX_WRITE_PROBE_TOOL_ACTIVITY_OBSERVED", {
|
|
935
|
-
settingsEchoObserved: runtime.settingsEchoObserved,
|
|
936
|
-
tokenCount: runtime.tokenCount,
|
|
937
|
-
});
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
catch (error) {
|
|
942
|
-
const normalized = this.normalizeError(error, true);
|
|
943
|
-
const runtime = this.probeRuntime;
|
|
944
|
-
result = codexWriteProbeFailure(normalized.code === "REQUEST_TIMEOUT"
|
|
945
|
-
? "CODEX_WRITE_PROBE_TIMEOUT"
|
|
946
|
-
: "CODEX_WRITE_PROBE_THREAD_SETUP_FAILED", {
|
|
947
|
-
settingsEchoObserved: runtime?.settingsEchoObserved ?? false,
|
|
948
|
-
...(runtime?.tokenCount === null || runtime?.tokenCount === undefined
|
|
949
|
-
? {}
|
|
950
|
-
: { tokenCount: runtime.tokenCount }),
|
|
951
|
-
});
|
|
952
|
-
}
|
|
953
|
-
finally {
|
|
954
|
-
const runtime = this.probeRuntime;
|
|
955
|
-
if (threadId !== null &&
|
|
956
|
-
runtime !== null &&
|
|
957
|
-
runtime.turnId !== null &&
|
|
958
|
-
(result.outcome === "fail" || runtime.failCode !== null)) {
|
|
959
|
-
try {
|
|
960
|
-
await this.request("turn/interrupt", {
|
|
961
|
-
threadId,
|
|
962
|
-
turnId: runtime.turnId,
|
|
963
|
-
});
|
|
964
|
-
}
|
|
965
|
-
catch {
|
|
966
|
-
// Archival remains the one required cleanup proof below.
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
let cleanupUnconfirmed = runtime !== null && threadId === null;
|
|
970
|
-
if (threadId !== null) {
|
|
971
|
-
try {
|
|
972
|
-
const archived = await this.request("thread/archive", { threadId });
|
|
973
|
-
if (!isRecord(archived) || Object.keys(archived).length !== 0) {
|
|
974
|
-
throw new CodexConnectorError("RESULT_SCHEMA_MISMATCH");
|
|
975
|
-
}
|
|
976
|
-
archivedThreadCount = 1;
|
|
977
|
-
const unsubscribed = await this.request("thread/unsubscribe", {
|
|
978
|
-
threadId,
|
|
979
|
-
});
|
|
980
|
-
if (!isRecord(unsubscribed) ||
|
|
981
|
-
(unsubscribed.status !== "unsubscribed" &&
|
|
982
|
-
unsubscribed.status !== "notSubscribed" &&
|
|
983
|
-
unsubscribed.status !== "notLoaded")) {
|
|
984
|
-
throw new CodexConnectorError("RESULT_SCHEMA_MISMATCH");
|
|
985
|
-
}
|
|
986
|
-
const loadedAfter = parseLoadedThreadIds(await this.request("thread/loaded/list", {}));
|
|
987
|
-
if (loadedAfter === null || loadedAfter.includes(threadId)) {
|
|
988
|
-
throw new CodexConnectorError("RESULT_SCHEMA_MISMATCH");
|
|
989
|
-
}
|
|
990
|
-
}
|
|
991
|
-
catch {
|
|
992
|
-
cleanupUnconfirmed = true;
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
const tokenCount = runtime?.tokenCount ?? result.tokenCount;
|
|
996
|
-
const evidence = {
|
|
997
|
-
...(archivedThreadCount === undefined ? {} : { archivedThreadCount }),
|
|
998
|
-
settingsEchoObserved: runtime?.settingsEchoObserved ?? result.settingsEchoObserved,
|
|
999
|
-
...(tokenCount === undefined ? {} : { tokenCount }),
|
|
1000
|
-
};
|
|
1001
|
-
if (cleanupUnconfirmed || (result.outcome === "pass" && archivedThreadCount !== 1)) {
|
|
1002
|
-
result = codexWriteProbeFailure("CODEX_WRITE_PROBE_CLEANUP_UNCONFIRMED", evidence);
|
|
1003
|
-
}
|
|
1004
|
-
else if (runtime?.failCode !== null && runtime?.failCode !== undefined) {
|
|
1005
|
-
result = codexWriteProbeFailure(runtime.failCode, evidence);
|
|
1006
|
-
}
|
|
1007
|
-
if (result.outcome === "fail") {
|
|
1008
|
-
result = codexWriteProbeFailure(result.safeErrorCode, evidence);
|
|
1009
|
-
}
|
|
1010
|
-
this.probeRuntime = null;
|
|
1011
|
-
}
|
|
1012
|
-
return result;
|
|
1013
|
-
}
|
|
1014
|
-
selectProbeModel(value) {
|
|
1015
|
-
if (!isRecord(value) || !Array.isArray(value.data) || value.data.length > 100) {
|
|
1016
|
-
return null;
|
|
1017
|
-
}
|
|
1018
|
-
const data = value.data;
|
|
1019
|
-
for (const model of CODEX_PROBE_MODEL_PREFERENCE) {
|
|
1020
|
-
const candidate = data.find((entry) => isRecord(entry) &&
|
|
1021
|
-
entry.model === model &&
|
|
1022
|
-
entry.hidden === false);
|
|
1023
|
-
if (!isRecord(candidate) || !Array.isArray(candidate.supportedReasoningEfforts)) {
|
|
1024
|
-
continue;
|
|
1025
|
-
}
|
|
1026
|
-
const supported = candidate.supportedReasoningEfforts;
|
|
1027
|
-
const effort = CODEX_PROBE_EFFORT.find((preference) => supported.some((option) => isRecord(option) && option.reasoningEffort === preference));
|
|
1028
|
-
if (effort !== undefined)
|
|
1029
|
-
return [model, effort];
|
|
1030
|
-
}
|
|
1031
|
-
return null;
|
|
1032
|
-
}
|
|
1033
|
-
probeCleanupThreadId(value, loadedBefore, forbiddenThreadIds) {
|
|
1034
|
-
if (!isRecord(value) ||
|
|
1035
|
-
!isRecord(value.thread) ||
|
|
1036
|
-
typeof value.thread.id !== "string" ||
|
|
1037
|
-
!validUuidV7(value.thread.id) ||
|
|
1038
|
-
value.thread.id === this.route.threadId ||
|
|
1039
|
-
loadedBefore.includes(value.thread.id) ||
|
|
1040
|
-
forbiddenThreadIds.includes(value.thread.id)) {
|
|
1041
|
-
return null;
|
|
1042
|
-
}
|
|
1043
|
-
return value.thread.id;
|
|
1044
|
-
}
|
|
1045
|
-
validateProbeThread(value, cwd, model, threadId) {
|
|
1046
|
-
if (!isRecord(value) || !isRecord(value.thread))
|
|
1047
|
-
return false;
|
|
1048
|
-
const roots = value.runtimeWorkspaceRoots;
|
|
1049
|
-
const networkAccess = isRecord(value.sandbox)
|
|
1050
|
-
? value.sandbox.networkAccess
|
|
1051
|
-
: undefined;
|
|
1052
|
-
return (value.approvalPolicy === "never" &&
|
|
1053
|
-
value.cwd === cwd &&
|
|
1054
|
-
value.model === model &&
|
|
1055
|
-
isRecord(value.sandbox) &&
|
|
1056
|
-
value.sandbox.type === "readOnly" &&
|
|
1057
|
-
(networkAccess === undefined || networkAccess === false) &&
|
|
1058
|
-
(roots === undefined || (Array.isArray(roots) && roots.length === 0)) &&
|
|
1059
|
-
value.thread.id === threadId &&
|
|
1060
|
-
value.thread.cwd === cwd &&
|
|
1061
|
-
value.thread.ephemeral === false &&
|
|
1062
|
-
isRecord(value.thread.status) &&
|
|
1063
|
-
value.thread.status.type === "idle" &&
|
|
1064
|
-
Array.isArray(value.thread.turns) &&
|
|
1065
|
-
value.thread.turns.length === 0);
|
|
1066
|
-
}
|
|
1067
|
-
async waitForProbeTurn(runtime) {
|
|
1068
|
-
if (runtime.failCode !== null ||
|
|
1069
|
-
(runtime.phase === "terminal" && runtime.tokenCount !== null)) {
|
|
1070
|
-
return;
|
|
1071
|
-
}
|
|
1072
|
-
let timer;
|
|
1073
|
-
await new Promise((resolve) => {
|
|
1074
|
-
runtime.resolveTurn = resolve;
|
|
1075
|
-
timer = setTimeout(resolve, this.turnWatchdogMs);
|
|
1076
|
-
});
|
|
1077
|
-
runtime.resolveTurn = null;
|
|
1078
|
-
if (timer !== undefined)
|
|
1079
|
-
clearTimeout(timer);
|
|
1080
|
-
if (runtime.failCode === null &&
|
|
1081
|
-
!(runtime.phase === "terminal" && runtime.tokenCount !== null)) {
|
|
1082
|
-
runtime.failCode = "CODEX_WRITE_PROBE_TIMEOUT";
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
|
-
settleProbeRuntime() {
|
|
1086
|
-
const runtime = this.probeRuntime;
|
|
1087
|
-
if (runtime === null || runtime.resolveTurn === null)
|
|
1088
|
-
return;
|
|
1089
|
-
if (runtime.failCode !== null ||
|
|
1090
|
-
(runtime.phase === "terminal" && runtime.tokenCount !== null)) {
|
|
1091
|
-
runtime.resolveTurn();
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
settleProbeConnectionLoss() {
|
|
1095
|
-
if (this.probeRuntime === null)
|
|
1096
|
-
return;
|
|
1097
|
-
this.probeRuntime.failCode ??= "CODEX_WRITE_PROBE_THREAD_SETUP_FAILED";
|
|
1098
|
-
this.settleProbeRuntime();
|
|
1099
|
-
}
|
|
1100
677
|
async initialize() {
|
|
1101
678
|
try {
|
|
1102
679
|
const result = await this.request("initialize", {
|
|
1103
680
|
capabilities: {
|
|
1104
681
|
// `thread/resume.excludeTurns` is field-gated behind this capability
|
|
1105
|
-
//
|
|
1106
|
-
// remains closed and exposes no experimental method.
|
|
682
|
+
// and is the only reason Embassy enables the experimental API.
|
|
1107
683
|
experimentalApi: true,
|
|
1108
|
-
optOutNotificationMethods: OUTPUT_NOTIFICATION_OPT_OUTS
|
|
684
|
+
optOutNotificationMethods: OUTPUT_NOTIFICATION_OPT_OUTS,
|
|
1109
685
|
},
|
|
1110
686
|
clientInfo: this.clientInfo,
|
|
1111
687
|
});
|
|
@@ -1171,7 +747,6 @@ export class CodexAppServerConnector {
|
|
|
1171
747
|
}
|
|
1172
748
|
async startMessage(message) {
|
|
1173
749
|
this.assertNoRequest();
|
|
1174
|
-
this.assertWritableReady();
|
|
1175
750
|
if (this.messageExpired(message)) {
|
|
1176
751
|
this.expireMessage(message);
|
|
1177
752
|
return "expired";
|
|
@@ -1270,7 +845,6 @@ export class CodexAppServerConnector {
|
|
|
1270
845
|
*/
|
|
1271
846
|
async steerMessage(message) {
|
|
1272
847
|
this.assertNoRequest();
|
|
1273
|
-
this.assertWritableReady();
|
|
1274
848
|
if (this.messageExpired(message)) {
|
|
1275
849
|
this.expireMessage(message);
|
|
1276
850
|
return "expired";
|
|
@@ -1319,7 +893,6 @@ export class CodexAppServerConnector {
|
|
|
1319
893
|
if (this.drainScheduled ||
|
|
1320
894
|
this.connection !== "ready" ||
|
|
1321
895
|
this.routeStatus !== "idle" ||
|
|
1322
|
-
this.currentWriteBlockCode() !== null ||
|
|
1323
896
|
this.inFlightMethod !== null ||
|
|
1324
897
|
this.queue.length === 0) {
|
|
1325
898
|
return;
|
|
@@ -1333,7 +906,6 @@ export class CodexAppServerConnector {
|
|
|
1333
906
|
this.dropExpiredQueuedMessages();
|
|
1334
907
|
if (this.connection !== "ready" ||
|
|
1335
908
|
this.routeStatus !== "idle" ||
|
|
1336
|
-
this.currentWriteBlockCode() !== null ||
|
|
1337
909
|
this.inFlightMethod !== null ||
|
|
1338
910
|
this.queue.length === 0) {
|
|
1339
911
|
return;
|
|
@@ -1355,8 +927,7 @@ export class CodexAppServerConnector {
|
|
|
1355
927
|
}
|
|
1356
928
|
if (guard.revision !== this.revision ||
|
|
1357
929
|
guard.status !== this.routeStatus ||
|
|
1358
|
-
guard.activeTurnId !== this.activeTurnId
|
|
1359
|
-
guard.writableReady !== this.isWritableReady()) {
|
|
930
|
+
guard.activeTurnId !== this.activeTurnId) {
|
|
1360
931
|
throw new CodexConnectorError("ROUTE_CAS_MISMATCH");
|
|
1361
932
|
}
|
|
1362
933
|
}
|
|
@@ -1372,17 +943,6 @@ export class CodexAppServerConnector {
|
|
|
1372
943
|
throw new CodexConnectorError("ROUTE_BUSY");
|
|
1373
944
|
}
|
|
1374
945
|
}
|
|
1375
|
-
currentWriteBlockCode() {
|
|
1376
|
-
return this.writesEnabled ? null : "WRITES_DISABLED";
|
|
1377
|
-
}
|
|
1378
|
-
isWritableReady() {
|
|
1379
|
-
return this.connection === "ready" && this.currentWriteBlockCode() === null;
|
|
1380
|
-
}
|
|
1381
|
-
assertWritableReady() {
|
|
1382
|
-
const code = this.currentWriteBlockCode();
|
|
1383
|
-
if (code !== null)
|
|
1384
|
-
throw new CodexConnectorError(code);
|
|
1385
|
-
}
|
|
1386
946
|
/**
|
|
1387
947
|
* Refresh the exact thread on this same connection immediately before a
|
|
1388
948
|
* `turn/start`. This confirms the registered task is still the observed idle
|
|
@@ -1390,7 +950,6 @@ export class CodexAppServerConnector {
|
|
|
1390
950
|
* policy.
|
|
1391
951
|
*/
|
|
1392
952
|
async refreshExactRouteBoundary() {
|
|
1393
|
-
this.assertWritableReady();
|
|
1394
953
|
if (this.routeStatus !== "starting" ||
|
|
1395
954
|
this.activeMessageId === null ||
|
|
1396
955
|
this.activeTurnId !== null ||
|
|
@@ -1419,7 +978,6 @@ export class CodexAppServerConnector {
|
|
|
1419
978
|
this.setRouteStatus(refreshedStatus);
|
|
1420
979
|
throw new CodexConnectorError("ROUTE_NOT_READY");
|
|
1421
980
|
}
|
|
1422
|
-
this.assertWritableReady();
|
|
1423
981
|
if (this.statusEpoch !== statusEpoch ||
|
|
1424
982
|
this.routeStatus !== "starting" ||
|
|
1425
983
|
this.activeMessageId === null ||
|
|
@@ -1473,11 +1031,6 @@ export class CodexAppServerConnector {
|
|
|
1473
1031
|
if (method !== "initialize" && !V1_METHOD_SET.has(method)) {
|
|
1474
1032
|
return Promise.reject(new CodexConnectorError("METHOD_NOT_ALLOWED"));
|
|
1475
1033
|
}
|
|
1476
|
-
if (method !== "initialize" &&
|
|
1477
|
-
PROBE_ONLY_METHODS.has(method) &&
|
|
1478
|
-
!this.writeCompatibilityProbe) {
|
|
1479
|
-
return Promise.reject(new CodexConnectorError("METHOD_NOT_ALLOWED"));
|
|
1480
|
-
}
|
|
1481
1034
|
if (method === "initialize") {
|
|
1482
1035
|
if (this.initializeRequested || this.connection !== "connecting") {
|
|
1483
1036
|
return Promise.reject(new CodexConnectorError("METHOD_NOT_ALLOWED"));
|
|
@@ -1594,8 +1147,6 @@ export class CodexAppServerConnector {
|
|
|
1594
1147
|
pending.resolve(parsed.result);
|
|
1595
1148
|
}
|
|
1596
1149
|
handleNotification(method, params) {
|
|
1597
|
-
if (this.handleProbeNotification(method, params))
|
|
1598
|
-
return;
|
|
1599
1150
|
if (method === "item/completed") {
|
|
1600
1151
|
this.handleCompletedItem(params);
|
|
1601
1152
|
return;
|
|
@@ -1825,14 +1376,6 @@ export class CodexAppServerConnector {
|
|
|
1825
1376
|
// item/model/tool payloads are neither normalized nor retained.
|
|
1826
1377
|
}
|
|
1827
1378
|
handleServerRequest(method, params) {
|
|
1828
|
-
if (this.probeRuntime !== null &&
|
|
1829
|
-
isRecord(params) &&
|
|
1830
|
-
params.threadId === this.probeRuntime.threadId) {
|
|
1831
|
-
this.probeRuntime.failCode ??=
|
|
1832
|
-
"CODEX_WRITE_PROBE_TOOL_ACTIVITY_OBSERVED";
|
|
1833
|
-
this.settleProbeRuntime();
|
|
1834
|
-
return;
|
|
1835
|
-
}
|
|
1836
1379
|
if (!APPROVAL_REQUEST_METHODS.has(method)) {
|
|
1837
1380
|
this.emit("server_request_ignored");
|
|
1838
1381
|
return;
|
|
@@ -1857,135 +1400,6 @@ export class CodexAppServerConnector {
|
|
|
1857
1400
|
// Intentionally no JSON-RPC response: the gateway is not an approval UI or
|
|
1858
1401
|
// authority. The owning Desktop client must resolve the request.
|
|
1859
1402
|
}
|
|
1860
|
-
handleProbeNotification(method, params) {
|
|
1861
|
-
const runtime = this.probeRuntime;
|
|
1862
|
-
if (runtime === null)
|
|
1863
|
-
return false;
|
|
1864
|
-
const fail = (code) => {
|
|
1865
|
-
runtime.failCode ??= code;
|
|
1866
|
-
this.settleProbeRuntime();
|
|
1867
|
-
};
|
|
1868
|
-
if (method === "model/rerouted") {
|
|
1869
|
-
fail("CODEX_WRITE_PROBE_MODEL_REROUTED");
|
|
1870
|
-
return true;
|
|
1871
|
-
}
|
|
1872
|
-
if (!isRecord(params))
|
|
1873
|
-
return false;
|
|
1874
|
-
const observeTurnId = (value) => {
|
|
1875
|
-
if (typeof value !== "string" || !validOpaqueId(value))
|
|
1876
|
-
return false;
|
|
1877
|
-
if (runtime.turnId !== null && runtime.turnId !== value)
|
|
1878
|
-
return false;
|
|
1879
|
-
runtime.turnId = value;
|
|
1880
|
-
return true;
|
|
1881
|
-
};
|
|
1882
|
-
if (method === "thread/settings/updated") {
|
|
1883
|
-
runtime.settingsEchoObserved = true;
|
|
1884
|
-
const settingsThreadId = params.threadId;
|
|
1885
|
-
const settings = params.threadSettings;
|
|
1886
|
-
const sandbox = isRecord(settings) ? settings.sandboxPolicy : null;
|
|
1887
|
-
if (typeof settingsThreadId !== "string" ||
|
|
1888
|
-
!validUuidV7(settingsThreadId) ||
|
|
1889
|
-
(runtime.threadId !== null && runtime.threadId !== settingsThreadId) ||
|
|
1890
|
-
(runtime.settingsThreadId !== null &&
|
|
1891
|
-
runtime.settingsThreadId !== settingsThreadId) ||
|
|
1892
|
-
!isRecord(settings) ||
|
|
1893
|
-
settings.cwd !== runtime.expectedCwd ||
|
|
1894
|
-
settings.model !== runtime.expectedModel ||
|
|
1895
|
-
settings.approvalPolicy !== "never" ||
|
|
1896
|
-
(settings.effort !== undefined &&
|
|
1897
|
-
settings.effort !== runtime.expectedEffort) ||
|
|
1898
|
-
!isRecord(sandbox) ||
|
|
1899
|
-
sandbox.type !== "readOnly" ||
|
|
1900
|
-
(sandbox.networkAccess !== undefined &&
|
|
1901
|
-
sandbox.networkAccess !== false)) {
|
|
1902
|
-
fail("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
1903
|
-
}
|
|
1904
|
-
else {
|
|
1905
|
-
runtime.settingsThreadId = settingsThreadId;
|
|
1906
|
-
}
|
|
1907
|
-
return true;
|
|
1908
|
-
}
|
|
1909
|
-
if (runtime.threadId === null || params.threadId !== runtime.threadId) {
|
|
1910
|
-
return false;
|
|
1911
|
-
}
|
|
1912
|
-
if (PROBE_TOOL_NOTIFICATION_METHODS.has(method)) {
|
|
1913
|
-
fail("CODEX_WRITE_PROBE_TOOL_ACTIVITY_OBSERVED");
|
|
1914
|
-
return true;
|
|
1915
|
-
}
|
|
1916
|
-
if (method === "thread/tokenUsage/updated") {
|
|
1917
|
-
const totalTokens = observeTurnId(params.turnId) &&
|
|
1918
|
-
isRecord(params.tokenUsage) &&
|
|
1919
|
-
isRecord(params.tokenUsage.last)
|
|
1920
|
-
? params.tokenUsage.last.totalTokens
|
|
1921
|
-
: null;
|
|
1922
|
-
if (!Number.isSafeInteger(totalTokens) ||
|
|
1923
|
-
totalTokens < 0 ||
|
|
1924
|
-
totalTokens > MAX_PROBE_TOKEN_COUNT) {
|
|
1925
|
-
fail("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
1926
|
-
}
|
|
1927
|
-
else {
|
|
1928
|
-
runtime.tokenCount = totalTokens;
|
|
1929
|
-
this.settleProbeRuntime();
|
|
1930
|
-
}
|
|
1931
|
-
return true;
|
|
1932
|
-
}
|
|
1933
|
-
if (method === "turn/started" || method === "turn/completed") {
|
|
1934
|
-
const turn = parseTurn(params.turn);
|
|
1935
|
-
if (turn === null ||
|
|
1936
|
-
!isRecord(params.turn) ||
|
|
1937
|
-
!Array.isArray(params.turn.items) ||
|
|
1938
|
-
!observeTurnId(turn.id)) {
|
|
1939
|
-
fail("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
1940
|
-
}
|
|
1941
|
-
else if (method === "turn/started") {
|
|
1942
|
-
if (turn.status !== "inProgress" ||
|
|
1943
|
-
runtime.phase !== "awaiting_start") {
|
|
1944
|
-
fail("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
1945
|
-
}
|
|
1946
|
-
else {
|
|
1947
|
-
runtime.phase = "started";
|
|
1948
|
-
}
|
|
1949
|
-
}
|
|
1950
|
-
else if (turn.status === "completed" &&
|
|
1951
|
-
runtime.phase === "item_completed") {
|
|
1952
|
-
runtime.phase = "terminal";
|
|
1953
|
-
this.settleProbeRuntime();
|
|
1954
|
-
}
|
|
1955
|
-
else {
|
|
1956
|
-
fail("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
1957
|
-
}
|
|
1958
|
-
return true;
|
|
1959
|
-
}
|
|
1960
|
-
if (method === "item/started" || method === "item/completed") {
|
|
1961
|
-
const observedAt = method === "item/started" ? params.startedAtMs : params.completedAtMs;
|
|
1962
|
-
const itemType = isRecord(params.item) ? params.item.type : null;
|
|
1963
|
-
if (typeof itemType === "string" &&
|
|
1964
|
-
!PROBE_PASSIVE_ITEM_TYPES.has(itemType)) {
|
|
1965
|
-
fail("CODEX_WRITE_PROBE_TOOL_ACTIVITY_OBSERVED");
|
|
1966
|
-
return true;
|
|
1967
|
-
}
|
|
1968
|
-
if (!observeTurnId(params.turnId) ||
|
|
1969
|
-
!isRecord(params.item) ||
|
|
1970
|
-
typeof itemType !== "string" ||
|
|
1971
|
-
!Number.isSafeInteger(observedAt) ||
|
|
1972
|
-
observedAt < 0 ||
|
|
1973
|
-
(runtime.phase !== "started" && runtime.phase !== "item_completed")) {
|
|
1974
|
-
fail("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
1975
|
-
}
|
|
1976
|
-
else if (method === "item/completed" &&
|
|
1977
|
-
itemType === "agentMessage") {
|
|
1978
|
-
if (runtime.phase !== "started") {
|
|
1979
|
-
fail("CODEX_WRITE_PROBE_THREAD_SETUP_FAILED");
|
|
1980
|
-
}
|
|
1981
|
-
else {
|
|
1982
|
-
runtime.phase = "item_completed";
|
|
1983
|
-
}
|
|
1984
|
-
}
|
|
1985
|
-
return true;
|
|
1986
|
-
}
|
|
1987
|
-
return false;
|
|
1988
|
-
}
|
|
1989
1403
|
handleCompletedItem(params) {
|
|
1990
1404
|
if (!isRecord(params) ||
|
|
1991
1405
|
typeof params.threadId !== "string") {
|
|
@@ -2129,7 +1543,6 @@ export class CodexAppServerConnector {
|
|
|
2129
1543
|
return;
|
|
2130
1544
|
this.faulting = true;
|
|
2131
1545
|
this.connection = "faulted";
|
|
2132
|
-
this.settleProbeConnectionLoss();
|
|
2133
1546
|
this.initialized = false;
|
|
2134
1547
|
this.routeStatus = "stale";
|
|
2135
1548
|
this.selectedThreadObserved = false;
|
|
@@ -2150,7 +1563,6 @@ export class CodexAppServerConnector {
|
|
|
2150
1563
|
if (this.connection === "closed" || this.connection === "faulted")
|
|
2151
1564
|
return;
|
|
2152
1565
|
this.connection = "closed";
|
|
2153
|
-
this.settleProbeConnectionLoss();
|
|
2154
1566
|
this.initialized = false;
|
|
2155
1567
|
this.routeStatus = "stale";
|
|
2156
1568
|
this.selectedThreadObserved = false;
|