agent-embassy 1.7.1 → 1.8.1

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.
Files changed (121) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/CONTRIBUTING.md +51 -47
  3. package/README.md +26 -9
  4. package/README.zh-CN.md +22 -9
  5. package/SECURITY.md +48 -35
  6. package/dist/src/gateway/acp-client.d.ts +12 -19
  7. package/dist/src/gateway/acp-client.js +90 -65
  8. package/dist/src/gateway/acp-client.js.map +1 -1
  9. package/dist/src/gateway/acp-provider.d.ts +11 -10
  10. package/dist/src/gateway/acp-provider.js +165 -53
  11. package/dist/src/gateway/acp-provider.js.map +1 -1
  12. package/dist/src/gateway/claude-helper-client.d.ts +2 -43
  13. package/dist/src/gateway/claude-helper-client.js +1 -211
  14. package/dist/src/gateway/claude-helper-client.js.map +1 -1
  15. package/dist/src/gateway/claude-helper-protocol.d.ts +16 -74
  16. package/dist/src/gateway/claude-helper-protocol.js +99 -378
  17. package/dist/src/gateway/claude-helper-protocol.js.map +1 -1
  18. package/dist/src/gateway/claude-helper-supervisor.d.ts +56 -44
  19. package/dist/src/gateway/claude-helper-supervisor.js +265 -456
  20. package/dist/src/gateway/claude-helper-supervisor.js.map +1 -1
  21. package/dist/src/gateway/claude-helper.js +184 -210
  22. package/dist/src/gateway/claude-helper.js.map +1 -1
  23. package/dist/src/gateway/claude-peer.d.ts +20 -87
  24. package/dist/src/gateway/claude-peer.js +310 -1009
  25. package/dist/src/gateway/claude-peer.js.map +1 -1
  26. package/dist/src/gateway/claude-runtime.d.ts +13 -15
  27. package/dist/src/gateway/claude-runtime.js +14 -168
  28. package/dist/src/gateway/claude-runtime.js.map +1 -1
  29. package/dist/src/gateway/cli-copy.d.ts +1 -1
  30. package/dist/src/gateway/cli-copy.en.d.ts +2 -4
  31. package/dist/src/gateway/cli-copy.en.js +3 -4
  32. package/dist/src/gateway/cli-copy.en.js.map +1 -1
  33. package/dist/src/gateway/cli-copy.js +0 -2
  34. package/dist/src/gateway/cli-copy.js.map +1 -1
  35. package/dist/src/gateway/cli-copy.zh-CN.d.ts +2 -4
  36. package/dist/src/gateway/cli-copy.zh-CN.js +3 -4
  37. package/dist/src/gateway/cli-copy.zh-CN.js.map +1 -1
  38. package/dist/src/gateway/cli.d.ts +7 -2
  39. package/dist/src/gateway/cli.js +273 -617
  40. package/dist/src/gateway/cli.js.map +1 -1
  41. package/dist/src/gateway/codex-app-server.d.ts +5 -227
  42. package/dist/src/gateway/codex-app-server.js +18 -1463
  43. package/dist/src/gateway/codex-app-server.js.map +1 -1
  44. package/dist/src/gateway/codex-doctor.d.ts +6 -3
  45. package/dist/src/gateway/codex-doctor.js +77 -98
  46. package/dist/src/gateway/codex-doctor.js.map +1 -1
  47. package/dist/src/gateway/codex-local-transport.d.ts +1 -6
  48. package/dist/src/gateway/codex-local-transport.js +8 -10
  49. package/dist/src/gateway/codex-local-transport.js.map +1 -1
  50. package/dist/src/gateway/codex-stateless-transport.d.ts +114 -0
  51. package/dist/src/gateway/codex-stateless-transport.js +1108 -0
  52. package/dist/src/gateway/codex-stateless-transport.js.map +1 -0
  53. package/dist/src/gateway/config.d.ts +2 -9
  54. package/dist/src/gateway/config.js +62 -100
  55. package/dist/src/gateway/config.js.map +1 -1
  56. package/dist/src/gateway/control.d.ts +36 -90
  57. package/dist/src/gateway/control.js +464 -1159
  58. package/dist/src/gateway/control.js.map +1 -1
  59. package/dist/src/gateway/dashboard-copy.d.ts +1 -1
  60. package/dist/src/gateway/dashboard-copy.en.d.ts +3 -26
  61. package/dist/src/gateway/dashboard-copy.en.js +14 -37
  62. package/dist/src/gateway/dashboard-copy.en.js.map +1 -1
  63. package/dist/src/gateway/dashboard-copy.js +3 -26
  64. package/dist/src/gateway/dashboard-copy.js.map +1 -1
  65. package/dist/src/gateway/dashboard-copy.zh-CN.d.ts +3 -26
  66. package/dist/src/gateway/dashboard-copy.zh-CN.js +14 -37
  67. package/dist/src/gateway/dashboard-copy.zh-CN.js.map +1 -1
  68. package/dist/src/gateway/dashboard-model.d.ts +6 -23
  69. package/dist/src/gateway/dashboard-model.js +37 -131
  70. package/dist/src/gateway/dashboard-model.js.map +1 -1
  71. package/dist/src/gateway/dashboard.d.ts +0 -6
  72. package/dist/src/gateway/dashboard.js +0 -6
  73. package/dist/src/gateway/dashboard.js.map +1 -1
  74. package/dist/src/gateway/live-dashboard-app/app.js +9 -32
  75. package/dist/src/gateway/live-dashboard-command.js +2 -2
  76. package/dist/src/gateway/live-dashboard-command.js.map +1 -1
  77. package/dist/src/gateway/live-dashboard-http.d.ts +1 -1
  78. package/dist/src/gateway/live-dashboard-http.js +2 -2
  79. package/dist/src/gateway/live-dashboard-http.js.map +1 -1
  80. package/dist/src/gateway/progress-watch-machine.d.ts +1 -37
  81. package/dist/src/gateway/progress-watch-machine.js +4 -15
  82. package/dist/src/gateway/progress-watch-machine.js.map +1 -1
  83. package/dist/src/gateway/providers.d.ts +55 -195
  84. package/dist/src/gateway/providers.js +608 -2465
  85. package/dist/src/gateway/providers.js.map +1 -1
  86. package/dist/src/gateway/server.d.ts +16 -25
  87. package/dist/src/gateway/server.js +164 -251
  88. package/dist/src/gateway/server.js.map +1 -1
  89. package/dist/src/gateway/service.d.ts +146 -448
  90. package/dist/src/gateway/service.js +1670 -6142
  91. package/dist/src/gateway/service.js.map +1 -1
  92. package/dist/src/gateway/state-v2-to-v3.d.ts +23 -0
  93. package/dist/src/gateway/state-v2-to-v3.js +994 -0
  94. package/dist/src/gateway/state-v2-to-v3.js.map +1 -0
  95. package/dist/src/gateway/store.d.ts +54 -309
  96. package/dist/src/gateway/store.js +1563 -3519
  97. package/dist/src/gateway/store.js.map +1 -1
  98. package/dist/src/gateway/types.d.ts +175 -228
  99. package/dist/src/gateway/types.js +114 -252
  100. package/dist/src/gateway/types.js.map +1 -1
  101. package/docs/CONFIGURATION.md +22 -9
  102. package/docs/CONFIGURATION.zh-CN.md +21 -9
  103. package/docs/DASHBOARD.md +10 -10
  104. package/docs/DASHBOARD.zh-CN.md +3 -3
  105. package/docs/DELIVERY.md +4 -4
  106. package/docs/DELIVERY.zh-CN.md +4 -4
  107. package/docs/GATEWAY-ARCHITECTURE.md +158 -187
  108. package/package.json +1 -1
  109. package/skills/embassy-peer/SKILL.md +28 -22
  110. package/dist/src/gateway/codex-registration-generation.d.ts +0 -5
  111. package/dist/src/gateway/codex-registration-generation.js +0 -19
  112. package/dist/src/gateway/codex-registration-generation.js.map +0 -1
  113. package/dist/src/gateway/codex-registration-succession.d.ts +0 -209
  114. package/dist/src/gateway/codex-registration-succession.js +0 -588
  115. package/dist/src/gateway/codex-registration-succession.js.map +0 -1
  116. package/dist/src/gateway/compatibility.d.ts +0 -22
  117. package/dist/src/gateway/compatibility.js +0 -23
  118. package/dist/src/gateway/compatibility.js.map +0 -1
  119. package/dist/src/gateway/delivery-machine.d.ts +0 -235
  120. package/dist/src/gateway/delivery-machine.js +0 -540
  121. package/dist/src/gateway/delivery-machine.js.map +0 -1
@@ -0,0 +1,1108 @@
1
+ import { Buffer } from "node:buffer";
2
+ import { createHash } from "node:crypto";
3
+ import { createLocalCodexTransportFactory, LocalCodexTransportError, } from "./codex-local-transport.js";
4
+ const DEFAULT_MAX_DEADLINE_MS = 24 * 60 * 60 * 1_000;
5
+ const DEFAULT_MAX_FRAME_BYTES = 1024 * 1024;
6
+ const DEFAULT_MAX_INPUT_BYTES = 64 * 1024;
7
+ const DEFAULT_MAX_REPLY_BYTES = 64 * 1024;
8
+ const DEFAULT_REQUEST_TIMEOUT_MS = 15_000;
9
+ const DEFAULT_TURN_TIMEOUT_MS = 2 * 60_000;
10
+ const MAX_FAST_TERMINAL_CANDIDATES = 4;
11
+ const SETUP_ABORTED = Symbol("setup-aborted");
12
+ const OUTPUT_NOTIFICATION_OPT_OUTS = [
13
+ "item/started", "item/agentMessage/delta", "item/reasoning/textDelta",
14
+ "item/reasoning/summaryTextDelta", "item/commandExecution/outputDelta",
15
+ "turn/diff/updated", "turn/plan/updated",
16
+ ];
17
+ const APPROVAL_REQUEST_METHODS = new Set([
18
+ "item/commandExecution/requestApproval", "item/fileChange/requestApproval",
19
+ "item/permissions/requestApproval", "item/tool/requestUserInput",
20
+ "mcpServer/elicitation/request",
21
+ ]);
22
+ class OperationError extends Error {
23
+ code;
24
+ constructor(code) {
25
+ super(code);
26
+ this.code = code;
27
+ this.name = "StatelessCodexOperationError";
28
+ }
29
+ }
30
+ class RpcRejectedError extends OperationError {
31
+ constructor() {
32
+ super("RPC_REJECTED");
33
+ this.name = "StatelessCodexRpcRejectedError";
34
+ }
35
+ }
36
+ async function awaitSetupResource(pending, signal) {
37
+ if (signal === undefined)
38
+ return await pending;
39
+ if (signal.aborted) {
40
+ void pending.then((resource) => resource.close()).catch(() => undefined);
41
+ return SETUP_ABORTED;
42
+ }
43
+ let observeAbort;
44
+ const aborted = new Promise((resolve) => {
45
+ observeAbort = () => resolve(SETUP_ABORTED);
46
+ signal.addEventListener("abort", observeAbort, { once: true });
47
+ });
48
+ try {
49
+ const settled = await Promise.race([pending, aborted]);
50
+ if (settled === SETUP_ABORTED) {
51
+ void pending.then((resource) => resource.close()).catch(() => undefined);
52
+ }
53
+ return settled;
54
+ }
55
+ finally {
56
+ signal.removeEventListener("abort", observeAbort);
57
+ }
58
+ }
59
+ function isRecord(value) {
60
+ return typeof value === "object" && value !== null && !Array.isArray(value);
61
+ }
62
+ function positiveInteger(value, fallback) {
63
+ const candidate = value ?? fallback;
64
+ if (!Number.isSafeInteger(candidate) || candidate <= 0) {
65
+ throw new OperationError("INPUT_INVALID");
66
+ }
67
+ return candidate;
68
+ }
69
+ function validOpaqueId(value, maximumLength = 256) {
70
+ return (value.length > 0 &&
71
+ value.length <= maximumLength &&
72
+ /^[A-Za-z0-9][A-Za-z0-9._:-]*$/u.test(value));
73
+ }
74
+ function validHost(value) {
75
+ return /^[a-z0-9](?:[a-z0-9.-]{0,61}[a-z0-9])?$/u.test(value);
76
+ }
77
+ function validAlias(value, hostId) {
78
+ return (value.length <= 256 &&
79
+ value.endsWith(`@${hostId}`) &&
80
+ /^[a-z0-9][a-z0-9._-]*@[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/u.test(value));
81
+ }
82
+ function parseRouteStatus(value) {
83
+ if (!isRecord(value) || typeof value.type !== "string")
84
+ return null;
85
+ if (value.type === "notLoaded")
86
+ return "not_loaded";
87
+ if (value.type === "idle")
88
+ return "idle";
89
+ if (value.type === "systemError")
90
+ return "system_error";
91
+ if (value.type !== "active")
92
+ return null;
93
+ if (value.activeFlags === undefined)
94
+ return "active";
95
+ if (!Array.isArray(value.activeFlags) ||
96
+ value.activeFlags.length > 32 ||
97
+ !value.activeFlags.every((flag) => typeof flag === "string" && flag.length <= 128)) {
98
+ return null;
99
+ }
100
+ return value.activeFlags.includes("waitingOnApproval")
101
+ ? "waiting_approval"
102
+ : "active";
103
+ }
104
+ function parseTurn(value) {
105
+ if (!isRecord(value) ||
106
+ typeof value.id !== "string" ||
107
+ !validOpaqueId(value.id) ||
108
+ typeof value.status !== "string" ||
109
+ value.status.length > 64) {
110
+ return null;
111
+ }
112
+ return { id: value.id, status: value.status };
113
+ }
114
+ function normalizeOptions(options) {
115
+ return {
116
+ local: { ...options.local },
117
+ maxDeadlineMs: positiveInteger(options.maxDeadlineMs, DEFAULT_MAX_DEADLINE_MS),
118
+ maxFrameBytes: positiveInteger(options.maxFrameBytes, DEFAULT_MAX_FRAME_BYTES),
119
+ maxInputBytes: positiveInteger(options.maxInputBytes, DEFAULT_MAX_INPUT_BYTES),
120
+ maxReplyBytes: positiveInteger(options.maxReplyBytes, DEFAULT_MAX_REPLY_BYTES),
121
+ now: options.now ?? (() => new Date()),
122
+ requestTimeoutMs: positiveInteger(options.requestTimeoutMs, DEFAULT_REQUEST_TIMEOUT_MS),
123
+ turnTimeoutMs: positiveInteger(options.turnTimeoutMs, DEFAULT_TURN_TIMEOUT_MS),
124
+ };
125
+ }
126
+ function validateInput(input, options) {
127
+ if (!validHost(input.route.hostId) ||
128
+ !validAlias(input.route.alias, input.route.hostId) ||
129
+ !validOpaqueId(input.route.threadId) ||
130
+ !validOpaqueId(input.route.registrationId, 128) ||
131
+ typeof input.onAccepted !== "function") {
132
+ throw new OperationError("INPUT_INVALID");
133
+ }
134
+ return validateMessageInput(input, options);
135
+ }
136
+ function validateMessageInput(input, options) {
137
+ if (!validOpaqueId(input.attemptId, 128) ||
138
+ typeof input.text !== "string" ||
139
+ input.text.trim().length === 0 ||
140
+ input.text.includes("\0")) {
141
+ throw new OperationError("INPUT_INVALID");
142
+ }
143
+ const bodyBytes = Buffer.byteLength(input.text, "utf8");
144
+ const nowMs = options.now().getTime();
145
+ const deadlineAtMs = Date.parse(input.deadlineAt);
146
+ if (bodyBytes === 0 ||
147
+ bodyBytes > options.maxInputBytes ||
148
+ !Number.isFinite(nowMs) ||
149
+ !Number.isFinite(deadlineAtMs) ||
150
+ new Date(deadlineAtMs).toISOString() !== input.deadlineAt ||
151
+ deadlineAtMs <= nowMs ||
152
+ deadlineAtMs > nowMs + options.maxDeadlineMs) {
153
+ throw new OperationError("INPUT_INVALID");
154
+ }
155
+ return { bodyBytes, deadlineAtMs };
156
+ }
157
+ class OperationSession {
158
+ transport;
159
+ input;
160
+ options;
161
+ deadlineAtMs;
162
+ bodyBytes;
163
+ nextRequestId = 1;
164
+ pending;
165
+ phase = "clean";
166
+ protocolFailure;
167
+ selectedTurnId;
168
+ acceptedKey;
169
+ sideChannelEvidenceValid = false;
170
+ acceptanceDurable = false;
171
+ activeSteerInFlight = false;
172
+ activeSteerCalls = 0;
173
+ activeSteerAttemptIds = new Set();
174
+ awaitingAuthorization = false;
175
+ prewriteProofValid = false;
176
+ candidates = new Map();
177
+ terminalReject;
178
+ terminalResolve;
179
+ terminalTimer;
180
+ transportLost = false;
181
+ aborted = false;
182
+ unlisten = [];
183
+ constructor(transport, input, options, deadlineAtMs, bodyBytes) {
184
+ this.transport = transport;
185
+ this.input = input;
186
+ this.options = options;
187
+ this.deadlineAtMs = deadlineAtMs;
188
+ this.bodyBytes = bodyBytes;
189
+ this.unlisten.push(transport.onMessage((payload) => this.handlePayload(payload)), transport.onClose(() => this.handleTransportLoss()), transport.onError(() => this.handleTransportLoss()));
190
+ }
191
+ dispose() {
192
+ this.sideChannelEvidenceValid = false;
193
+ for (const remove of this.unlisten.splice(0))
194
+ remove();
195
+ this.rejectPending(new OperationError("TRANSPORT_CLOSED"));
196
+ this.clearTerminalWait();
197
+ }
198
+ abort() {
199
+ if (this.aborted)
200
+ return;
201
+ this.aborted = true;
202
+ this.sideChannelEvidenceValid = false;
203
+ if (this.awaitingAuthorization)
204
+ this.prewriteProofValid = false;
205
+ const error = new OperationError("TRANSPORT_CLOSED");
206
+ this.rejectPending(error);
207
+ const reject = this.terminalReject;
208
+ this.clearTerminalWait();
209
+ reject?.(error);
210
+ }
211
+ async execute() {
212
+ try {
213
+ if (this.aborted)
214
+ return this.cleanFailure("TRANSPORT_CLOSED");
215
+ await this.initialize();
216
+ this.awaitingAuthorization = true;
217
+ this.prewriteProofValid = true;
218
+ const status = await this.resume();
219
+ const held = this.prewriteDisposition(status);
220
+ if (held !== undefined) {
221
+ this.awaitingAuthorization = false;
222
+ return held;
223
+ }
224
+ if (!this.prewriteProofValid || this.expired()) {
225
+ this.awaitingAuthorization = false;
226
+ return this.cleanDeferred(this.prewriteProofValid ? "MESSAGE_EXPIRED" : "ROUTE_BUSY");
227
+ }
228
+ const prepared = this.prepareRequest("turn/start", {
229
+ input: [{ text: this.input.text, type: "text" }],
230
+ threadId: this.input.route.threadId,
231
+ });
232
+ const responsePromise = this.reservePreparedRequest(prepared);
233
+ void responsePromise.catch(() => undefined);
234
+ const evidence = {
235
+ attemptId: this.input.attemptId,
236
+ bodyBytes: this.bodyBytes,
237
+ frameBytes: prepared.frameBytes,
238
+ kind: "codex_turn_start",
239
+ sha256: createHash("sha256").update(prepared.frame).digest("hex"),
240
+ };
241
+ let authorized;
242
+ try {
243
+ authorized = await this.input.authorizeWrite(evidence);
244
+ }
245
+ catch {
246
+ this.awaitingAuthorization = false;
247
+ this.clearPending();
248
+ this.phase = "armed";
249
+ return this.armedAmbiguous("WRITE_AUTHORIZATION_UNCERTAIN");
250
+ }
251
+ this.awaitingAuthorization = false;
252
+ if (!authorized) {
253
+ this.clearPending();
254
+ if (this.aborted)
255
+ return this.cleanFailure("TRANSPORT_CLOSED");
256
+ return this.cleanDeferred("WRITE_AUTHORIZATION_DENIED");
257
+ }
258
+ // Authorization is the consent linearization point. From here until the
259
+ // exact body-bearing send call there is deliberately no await or yield.
260
+ this.phase = "armed";
261
+ if (!this.prewriteProofValid || !this.markPendingSent(prepared.id)) {
262
+ return this.armedAmbiguous(this.aborted ? "TRANSPORT_CLOSED" : "PROTOCOL_ERROR");
263
+ }
264
+ // This monotonic latch begins before the semantic write and is never
265
+ // restored from a later response. Synchronous post-response drift is
266
+ // therefore preserved across the Promise continuation.
267
+ this.sideChannelEvidenceValid = true;
268
+ let writePromise;
269
+ try {
270
+ writePromise = this.transport.send(prepared.frame);
271
+ }
272
+ catch {
273
+ writePromise = Promise.reject(new OperationError("TRANSPORT_WRITE_FAILED"));
274
+ }
275
+ void writePromise.catch(() => {
276
+ this.rejectPending(new OperationError("TRANSPORT_WRITE_FAILED"));
277
+ });
278
+ let result;
279
+ try {
280
+ result = await responsePromise;
281
+ }
282
+ catch (error) {
283
+ return this.armedAmbiguous(this.errorCode(error));
284
+ }
285
+ if (!isRecord(result)) {
286
+ return this.armedAmbiguous("RESULT_SCHEMA_MISMATCH");
287
+ }
288
+ const turn = parseTurn(result.turn);
289
+ if (turn === null || turn.status !== "inProgress") {
290
+ return this.armedAmbiguous("RESULT_SCHEMA_MISMATCH");
291
+ }
292
+ this.phase = "accepted";
293
+ this.selectedTurnId = turn.id;
294
+ const acceptedKey = Object.freeze({
295
+ attemptId: this.input.attemptId,
296
+ registrationId: this.input.route.registrationId,
297
+ threadId: this.input.route.threadId,
298
+ turnId: turn.id,
299
+ });
300
+ this.acceptedKey = acceptedKey;
301
+ for (const candidateId of [...this.candidates.keys()]) {
302
+ if (candidateId !== turn.id)
303
+ this.candidates.delete(candidateId);
304
+ }
305
+ try {
306
+ await this.input.onAccepted({
307
+ attemptId: this.input.attemptId,
308
+ turnId: turn.id,
309
+ steer: (input) => this.steerAcceptedOperation(acceptedKey, input),
310
+ });
311
+ }
312
+ catch {
313
+ this.sideChannelEvidenceValid = false;
314
+ return this.acceptedUnconfirmed("ACCEPTANCE_UNCONFIRMED");
315
+ }
316
+ this.acceptanceDurable = true;
317
+ const fast = this.terminalFor(turn.id);
318
+ if (fast !== undefined)
319
+ return this.terminal(fast);
320
+ try {
321
+ return this.terminal(await this.waitForTerminal(turn.id));
322
+ }
323
+ catch (error) {
324
+ return this.acceptedUnconfirmed(this.errorCode(error));
325
+ }
326
+ }
327
+ catch (error) {
328
+ const code = this.errorCode(error);
329
+ if (this.phase === "accepted")
330
+ return this.acceptedUnconfirmed(code);
331
+ if (this.phase === "armed")
332
+ return this.armedAmbiguous(code);
333
+ return this.cleanFailure(code);
334
+ }
335
+ }
336
+ async observe() {
337
+ try {
338
+ await this.initialize();
339
+ const status = await this.resume();
340
+ if (status === "idle")
341
+ return { state: "idle" };
342
+ if (status === "waiting_approval")
343
+ return { state: "awaiting_approval" };
344
+ if (status === "active")
345
+ return { state: "busy" };
346
+ return {
347
+ state: "unobserved",
348
+ safeErrorCode: status === "not_loaded"
349
+ ? "THREAD_NOT_OBSERVED"
350
+ : "CODEX_OBSERVER_PROTOCOL_ERROR",
351
+ };
352
+ }
353
+ catch (error) {
354
+ return {
355
+ state: "unobserved",
356
+ safeErrorCode: error instanceof OperationError && error.code === "THREAD_NOT_OBSERVED"
357
+ ? "THREAD_NOT_OBSERVED"
358
+ : "CODEX_OBSERVER_UNAVAILABLE",
359
+ };
360
+ }
361
+ }
362
+ async steerAcceptedOperation(key, input) {
363
+ let phase = "clean";
364
+ let ownsSteerSlot = false;
365
+ let result;
366
+ try {
367
+ const validated = validateMessageInput(input, this.options);
368
+ if (!this.acceptedOperationMatches(key)) {
369
+ result = { phase: "clean", safeErrorCode: "ROUTE_BUSY", state: "deferred" };
370
+ }
371
+ else if (input.attemptId === key.attemptId ||
372
+ this.activeSteerAttemptIds.has(input.attemptId)) {
373
+ result = { phase: "clean", safeErrorCode: "INPUT_INVALID", state: "failed" };
374
+ }
375
+ else if (this.activeSteerInFlight || this.activeSteerCalls >= 3) {
376
+ result = { phase: "clean", safeErrorCode: "ROUTE_BUSY", state: "deferred" };
377
+ }
378
+ else {
379
+ this.activeSteerCalls += 1;
380
+ this.activeSteerAttemptIds.add(input.attemptId);
381
+ this.activeSteerInFlight = true;
382
+ ownsSteerSlot = true;
383
+ const prepared = this.prepareRequest("turn/steer", {
384
+ expectedTurnId: key.turnId,
385
+ input: [{ text: input.text, type: "text" }],
386
+ threadId: key.threadId,
387
+ });
388
+ const responsePromise = this.reservePreparedRequest(prepared);
389
+ void responsePromise.catch(() => undefined);
390
+ const evidence = {
391
+ attemptId: input.attemptId,
392
+ bodyBytes: validated.bodyBytes,
393
+ frameBytes: prepared.frameBytes,
394
+ kind: "codex_turn_steer",
395
+ sha256: createHash("sha256").update(prepared.frame).digest("hex"),
396
+ };
397
+ let authorized;
398
+ try {
399
+ authorized = await input.authorizeWrite(evidence);
400
+ }
401
+ catch {
402
+ this.clearPending();
403
+ phase = "armed";
404
+ authorized = false;
405
+ result = {
406
+ phase: "armed",
407
+ safeErrorCode: "WRITE_AUTHORIZATION_UNCERTAIN",
408
+ state: "ambiguous",
409
+ };
410
+ }
411
+ if (result === undefined) {
412
+ if (!authorized) {
413
+ this.clearPending();
414
+ result = {
415
+ phase: "clean",
416
+ safeErrorCode: this.aborted
417
+ ? "TRANSPORT_CLOSED"
418
+ : "WRITE_AUTHORIZATION_DENIED",
419
+ state: this.aborted ? "failed" : "deferred",
420
+ };
421
+ }
422
+ else {
423
+ phase = "armed";
424
+ if (!this.acceptedOperationMatches(key) ||
425
+ this.messageExpired(validated.deadlineAtMs) ||
426
+ !this.markPendingSent(prepared.id)) {
427
+ this.clearPending();
428
+ result = {
429
+ phase: "armed",
430
+ safeErrorCode: this.aborted
431
+ ? "TRANSPORT_CLOSED"
432
+ : "PROTOCOL_ERROR",
433
+ state: "ambiguous",
434
+ };
435
+ }
436
+ else {
437
+ let writePromise;
438
+ try {
439
+ writePromise = this.transport.send(prepared.frame);
440
+ }
441
+ catch {
442
+ writePromise = Promise.reject(new OperationError("TRANSPORT_WRITE_FAILED"));
443
+ }
444
+ void writePromise.catch(() => {
445
+ this.rejectPending(new OperationError("TRANSPORT_WRITE_FAILED"));
446
+ });
447
+ try {
448
+ const response = await responsePromise;
449
+ if (!isRecord(response) ||
450
+ Object.keys(response).length !== 1 ||
451
+ response.turnId !== key.turnId) {
452
+ throw new OperationError("RESULT_SCHEMA_MISMATCH");
453
+ }
454
+ result = {
455
+ outcome: "delivered",
456
+ phase: "terminal",
457
+ replyCode: "REPLY_UNAVAILABLE",
458
+ replyText: null,
459
+ state: "terminal",
460
+ };
461
+ }
462
+ catch (error) {
463
+ result = {
464
+ phase: "armed",
465
+ safeErrorCode: this.errorCode(error),
466
+ state: "ambiguous",
467
+ };
468
+ }
469
+ }
470
+ }
471
+ }
472
+ }
473
+ }
474
+ catch (error) {
475
+ result = phase === "armed"
476
+ ? { phase, safeErrorCode: this.errorCode(error), state: "ambiguous" }
477
+ : { phase, safeErrorCode: this.errorCode(error), state: "failed" };
478
+ }
479
+ finally {
480
+ if (ownsSteerSlot)
481
+ this.activeSteerInFlight = false;
482
+ }
483
+ return { ...result, attemptId: input.attemptId };
484
+ }
485
+ acceptedOperationMatches(key) {
486
+ return (this.sideChannelEvidenceValid &&
487
+ this.acceptanceDurable &&
488
+ this.acceptedKey === key &&
489
+ this.phase === "accepted" &&
490
+ this.selectedTurnId === key.turnId &&
491
+ this.input.route.registrationId === key.registrationId &&
492
+ this.input.attemptId === key.attemptId);
493
+ }
494
+ messageExpired(deadlineAtMs) {
495
+ const nowMs = this.options.now().getTime();
496
+ return !Number.isFinite(nowMs) || nowMs >= deadlineAtMs;
497
+ }
498
+ async initialize() {
499
+ const result = await this.request("initialize", {
500
+ capabilities: {
501
+ experimentalApi: true,
502
+ optOutNotificationMethods: OUTPUT_NOTIFICATION_OPT_OUTS,
503
+ },
504
+ clientInfo: {
505
+ name: "agent_embassy_gateway",
506
+ title: "Embassy Gateway",
507
+ version: "1.7.0",
508
+ },
509
+ });
510
+ if (!isRecord(result))
511
+ throw new OperationError("RESULT_SCHEMA_MISMATCH");
512
+ const initialized = JSON.stringify({ method: "initialized", params: {} });
513
+ if (Buffer.byteLength(initialized, "utf8") > this.options.maxFrameBytes) {
514
+ throw new OperationError("INPUT_INVALID");
515
+ }
516
+ try {
517
+ await this.transport.send(initialized);
518
+ }
519
+ catch {
520
+ throw new OperationError("TRANSPORT_WRITE_FAILED");
521
+ }
522
+ }
523
+ async resume() {
524
+ let result;
525
+ try {
526
+ result = await this.request("thread/resume", {
527
+ excludeTurns: true,
528
+ threadId: this.input.route.threadId,
529
+ });
530
+ }
531
+ catch (error) {
532
+ if (error instanceof RpcRejectedError) {
533
+ throw new OperationError("THREAD_NOT_OBSERVED");
534
+ }
535
+ throw error;
536
+ }
537
+ if (!isRecord(result) ||
538
+ !isRecord(result.thread) ||
539
+ result.thread.id !== this.input.route.threadId ||
540
+ !Array.isArray(result.thread.turns) ||
541
+ result.thread.turns.length !== 0) {
542
+ throw new OperationError("RESULT_SCHEMA_MISMATCH");
543
+ }
544
+ const status = parseRouteStatus(result.thread.status);
545
+ if (status === null)
546
+ throw new OperationError("RESULT_SCHEMA_MISMATCH");
547
+ return status;
548
+ }
549
+ prewriteDisposition(status) {
550
+ if (status === "not_loaded")
551
+ return this.cleanDeferred("THREAD_NOT_OBSERVED");
552
+ if (status === "system_error")
553
+ return this.cleanFailure("THREAD_NOT_OBSERVED");
554
+ if (status === "waiting_approval")
555
+ return this.cleanDeferred("APPROVAL_REQUIRED");
556
+ return status === "idle" ? undefined : this.cleanDeferred("ROUTE_BUSY");
557
+ }
558
+ request(method, params) {
559
+ const prepared = this.prepareRequest(method, params);
560
+ const response = this.reservePreparedRequest(prepared);
561
+ if (!this.markPendingSent(prepared.id))
562
+ return response;
563
+ let write;
564
+ try {
565
+ write = this.transport.send(prepared.frame);
566
+ }
567
+ catch {
568
+ write = Promise.reject(new OperationError("TRANSPORT_WRITE_FAILED"));
569
+ }
570
+ void write.catch(() => {
571
+ this.rejectPending(new OperationError("TRANSPORT_WRITE_FAILED"));
572
+ });
573
+ return response;
574
+ }
575
+ prepareRequest(method, params) {
576
+ if (method !== "initialize" &&
577
+ method !== "thread/resume" &&
578
+ method !== "turn/start" &&
579
+ method !== "turn/steer") {
580
+ throw new OperationError("PROTOCOL_ERROR");
581
+ }
582
+ const id = this.nextRequestId;
583
+ this.nextRequestId += 1;
584
+ if (!Number.isSafeInteger(this.nextRequestId)) {
585
+ throw new OperationError("PROTOCOL_ERROR");
586
+ }
587
+ const frame = JSON.stringify({ id, method, params });
588
+ const frameBytes = Buffer.byteLength(frame, "utf8");
589
+ if (frameBytes > this.options.maxFrameBytes) {
590
+ throw new OperationError("INPUT_INVALID");
591
+ }
592
+ return { frame, frameBytes, id };
593
+ }
594
+ reservePreparedRequest(prepared) {
595
+ if (this.protocolFailure !== undefined)
596
+ throw this.protocolFailure;
597
+ if (this.pending !== undefined || this.transportLost || this.aborted) {
598
+ throw new OperationError("TRANSPORT_CLOSED");
599
+ }
600
+ return new Promise((resolve, reject) => {
601
+ this.pending = { id: prepared.id, reject, resolve, sent: false };
602
+ });
603
+ }
604
+ markPendingSent(id) {
605
+ const pending = this.pending;
606
+ if (pending?.id !== id || pending.sent) {
607
+ this.protocolFault();
608
+ return false;
609
+ }
610
+ pending.sent = true;
611
+ pending.timer = setTimeout(() => {
612
+ if (this.pending?.id !== id)
613
+ return;
614
+ this.pending = undefined;
615
+ pending.reject(new OperationError("REQUEST_TIMEOUT"));
616
+ }, this.options.requestTimeoutMs);
617
+ return true;
618
+ }
619
+ handlePayload(payload) {
620
+ if (Buffer.byteLength(payload, "utf8") > this.options.maxFrameBytes) {
621
+ this.protocolFault();
622
+ return;
623
+ }
624
+ let parsed;
625
+ try {
626
+ parsed = JSON.parse(payload);
627
+ }
628
+ catch {
629
+ this.protocolFault();
630
+ return;
631
+ }
632
+ if (!isRecord(parsed)) {
633
+ this.protocolFault();
634
+ return;
635
+ }
636
+ if (typeof parsed.method === "string") {
637
+ if (parsed.id === undefined)
638
+ this.handleNotification(parsed.method, parsed.params);
639
+ else
640
+ this.handleServerRequest(parsed.method, parsed.params);
641
+ return;
642
+ }
643
+ if (typeof parsed.id !== "number" ||
644
+ !Number.isSafeInteger(parsed.id) ||
645
+ this.pending?.id !== parsed.id ||
646
+ !this.pending.sent) {
647
+ this.protocolFault();
648
+ return;
649
+ }
650
+ const pending = this.pending;
651
+ this.pending = undefined;
652
+ if (pending.timer !== undefined)
653
+ clearTimeout(pending.timer);
654
+ const hasResult = Object.hasOwn(parsed, "result");
655
+ const hasError = Object.hasOwn(parsed, "error");
656
+ if (hasResult === hasError) {
657
+ pending.reject(new OperationError("PROTOCOL_ERROR"));
658
+ this.protocolFault();
659
+ return;
660
+ }
661
+ if (hasError) {
662
+ if (!isRecord(parsed.error) ||
663
+ typeof parsed.error.code !== "number" ||
664
+ !Number.isSafeInteger(parsed.error.code)) {
665
+ pending.reject(new OperationError("PROTOCOL_ERROR"));
666
+ this.protocolFault();
667
+ return;
668
+ }
669
+ pending.reject(new RpcRejectedError());
670
+ return;
671
+ }
672
+ pending.resolve(parsed.result);
673
+ }
674
+ handleNotification(method, params) {
675
+ if (this.phase === "clean") {
676
+ this.invalidatePrewriteProof(method, params);
677
+ return;
678
+ }
679
+ if (method === "turn/started") {
680
+ if (!this.targetParams(params))
681
+ return;
682
+ const turn = parseTurn(params.turn);
683
+ if (turn === null || turn.status !== "inProgress") {
684
+ this.protocolFault();
685
+ return;
686
+ }
687
+ this.candidate(turn.id, true);
688
+ return;
689
+ }
690
+ if (method === "item/completed") {
691
+ this.handleCompletedItem(params);
692
+ return;
693
+ }
694
+ if (method === "turn/completed") {
695
+ if (!this.targetParams(params))
696
+ return;
697
+ const turn = parseTurn(params.turn);
698
+ if (turn === null ||
699
+ (turn.status !== "completed" &&
700
+ turn.status !== "failed" &&
701
+ turn.status !== "interrupted")) {
702
+ this.protocolFault();
703
+ return;
704
+ }
705
+ const candidate = this.candidate(turn.id, false);
706
+ if (candidate === undefined || candidate.terminal !== null)
707
+ return;
708
+ candidate.terminal = turn.status;
709
+ this.sideChannelEvidenceValid = false;
710
+ if (turn.id === this.selectedTurnId) {
711
+ this.resolveTerminal(turn.id, candidate);
712
+ }
713
+ return;
714
+ }
715
+ if (method === "thread/closed") {
716
+ if (this.targetParams(params))
717
+ this.protocolFault();
718
+ return;
719
+ }
720
+ if (method === "thread/status/changed" && this.targetParams(params)) {
721
+ if (parseRouteStatus(params.status) !== "active") {
722
+ this.sideChannelEvidenceValid = false;
723
+ }
724
+ return;
725
+ }
726
+ // Settings, status, warning, output, and tool notifications are not
727
+ // dispatch authority. They are ignored after the global frame bound.
728
+ }
729
+ handleServerRequest(method, params) {
730
+ if (!APPROVAL_REQUEST_METHODS.has(method))
731
+ return;
732
+ if (!isRecord(params) ||
733
+ typeof params.threadId !== "string" ||
734
+ typeof params.turnId !== "string" ||
735
+ !validOpaqueId(params.turnId)) {
736
+ this.protocolFault();
737
+ return;
738
+ }
739
+ if (params.threadId !== this.input.route.threadId)
740
+ return;
741
+ if (this.awaitingAuthorization)
742
+ this.prewriteProofValid = false;
743
+ this.sideChannelEvidenceValid = false;
744
+ // Deliberately no JSON-RPC response. Embassy is not an approval authority.
745
+ }
746
+ invalidatePrewriteProof(method, params) {
747
+ if (!this.awaitingAuthorization ||
748
+ !["item/completed", "serverRequest/resolved", "thread/closed",
749
+ "thread/status/changed", "turn/completed", "turn/started"].includes(method))
750
+ return;
751
+ if (!isRecord(params) || typeof params.threadId !== "string") {
752
+ this.protocolFault();
753
+ return;
754
+ }
755
+ if (params.threadId === this.input.route.threadId)
756
+ this.prewriteProofValid = false;
757
+ }
758
+ handleCompletedItem(params) {
759
+ if (!this.targetParams(params))
760
+ return;
761
+ if (typeof params.turnId !== "string" ||
762
+ !validOpaqueId(params.turnId) ||
763
+ !isRecord(params.item) ||
764
+ typeof params.item.type !== "string") {
765
+ this.protocolFault();
766
+ return;
767
+ }
768
+ const candidate = this.candidate(params.turnId, false);
769
+ if (candidate === undefined || candidate.terminal !== null)
770
+ return;
771
+ if (params.item.type !== "agentMessage")
772
+ return;
773
+ if (params.item.phase !== undefined && params.item.phase !== "final_answer") {
774
+ return;
775
+ }
776
+ if (typeof params.item.text !== "string" ||
777
+ params.item.text.length === 0 ||
778
+ params.item.text.includes("\0")) {
779
+ return;
780
+ }
781
+ if (Buffer.byteLength(params.item.text, "utf8") > this.options.maxReplyBytes) {
782
+ candidate.replyCode = "REPLY_TOO_LARGE";
783
+ candidate.replyText = null;
784
+ return;
785
+ }
786
+ candidate.replyCode = null;
787
+ candidate.replyText = params.item.text;
788
+ }
789
+ targetParams(params) {
790
+ if (!isRecord(params) || typeof params.threadId !== "string") {
791
+ this.protocolFault();
792
+ return false;
793
+ }
794
+ return params.threadId === this.input.route.threadId;
795
+ }
796
+ candidate(turnId, allowCreate) {
797
+ const existing = this.candidates.get(turnId);
798
+ if (existing !== undefined)
799
+ return existing;
800
+ if ((!allowCreate && turnId !== this.selectedTurnId) ||
801
+ (this.selectedTurnId !== undefined && turnId !== this.selectedTurnId)) {
802
+ return undefined;
803
+ }
804
+ if (this.candidates.size >= MAX_FAST_TERMINAL_CANDIDATES) {
805
+ this.protocolFault();
806
+ return undefined;
807
+ }
808
+ const candidate = {
809
+ replyCode: "REPLY_UNAVAILABLE",
810
+ replyText: null,
811
+ terminal: null,
812
+ };
813
+ this.candidates.set(turnId, candidate);
814
+ return candidate;
815
+ }
816
+ terminalFor(turnId) {
817
+ const candidate = this.candidates.get(turnId);
818
+ if (candidate?.terminal === null || candidate === undefined)
819
+ return undefined;
820
+ return {
821
+ outcome: candidate.terminal,
822
+ replyCode: candidate.terminal === "completed"
823
+ ? candidate.replyCode
824
+ : "REPLY_UNAVAILABLE",
825
+ replyText: candidate.terminal === "completed" ? candidate.replyText : null,
826
+ };
827
+ }
828
+ waitForTerminal(turnId) {
829
+ if (this.protocolFailure !== undefined) {
830
+ return Promise.reject(this.protocolFailure);
831
+ }
832
+ if (this.transportLost) {
833
+ return Promise.reject(new OperationError("TRANSPORT_CLOSED"));
834
+ }
835
+ if (this.aborted) {
836
+ return Promise.reject(new OperationError("TRANSPORT_CLOSED"));
837
+ }
838
+ const remaining = Math.min(this.options.turnTimeoutMs, this.deadlineAtMs - this.options.now().getTime());
839
+ if (!Number.isFinite(remaining) || remaining <= 0) {
840
+ return Promise.reject(new OperationError("REQUEST_TIMEOUT"));
841
+ }
842
+ return new Promise((resolve, reject) => {
843
+ this.terminalResolve = resolve;
844
+ this.terminalReject = reject;
845
+ this.terminalTimer = setTimeout(() => {
846
+ this.clearTerminalWait();
847
+ reject(new OperationError("REQUEST_TIMEOUT"));
848
+ }, remaining);
849
+ const terminal = this.terminalFor(turnId);
850
+ if (terminal !== undefined) {
851
+ this.clearTerminalWait();
852
+ resolve(terminal);
853
+ }
854
+ });
855
+ }
856
+ resolveTerminal(turnId, candidate) {
857
+ if (turnId !== this.selectedTurnId || candidate.terminal === null)
858
+ return;
859
+ const terminal = this.terminalFor(turnId);
860
+ if (terminal === undefined || this.terminalResolve === undefined)
861
+ return;
862
+ const resolve = this.terminalResolve;
863
+ this.clearTerminalWait();
864
+ resolve(terminal);
865
+ }
866
+ protocolFault() {
867
+ if (this.protocolFailure !== undefined)
868
+ return;
869
+ this.protocolFailure = new OperationError("PROTOCOL_ERROR");
870
+ this.sideChannelEvidenceValid = false;
871
+ this.rejectPending(this.protocolFailure);
872
+ const reject = this.terminalReject;
873
+ this.clearTerminalWait();
874
+ reject?.(this.protocolFailure);
875
+ }
876
+ handleTransportLoss() {
877
+ if (this.transportLost)
878
+ return;
879
+ this.transportLost = true;
880
+ this.sideChannelEvidenceValid = false;
881
+ const error = new OperationError("TRANSPORT_CLOSED");
882
+ this.rejectPending(error);
883
+ const reject = this.terminalReject;
884
+ this.clearTerminalWait();
885
+ reject?.(error);
886
+ }
887
+ rejectPending(error) {
888
+ const pending = this.pending;
889
+ if (pending === undefined)
890
+ return;
891
+ this.pending = undefined;
892
+ clearTimeout(pending.timer);
893
+ pending.reject(error);
894
+ }
895
+ clearPending() {
896
+ if (this.pending === undefined)
897
+ return;
898
+ clearTimeout(this.pending.timer);
899
+ this.pending = undefined;
900
+ }
901
+ clearTerminalWait() {
902
+ if (this.terminalTimer !== undefined)
903
+ clearTimeout(this.terminalTimer);
904
+ this.terminalTimer = undefined;
905
+ this.terminalResolve = undefined;
906
+ this.terminalReject = undefined;
907
+ }
908
+ expired() {
909
+ const nowMs = this.options.now().getTime();
910
+ return !Number.isFinite(nowMs) || nowMs >= this.deadlineAtMs;
911
+ }
912
+ errorCode(error) {
913
+ if (error instanceof OperationError)
914
+ return error.code;
915
+ if (error instanceof LocalCodexTransportError)
916
+ return error.code;
917
+ return "PROTOCOL_ERROR";
918
+ }
919
+ cleanDeferred(code) {
920
+ return { phase: "clean", safeErrorCode: code, state: "deferred" };
921
+ }
922
+ cleanFailure(code) {
923
+ return { phase: "clean", safeErrorCode: code, state: "failed" };
924
+ }
925
+ armedAmbiguous(code) {
926
+ return { phase: "armed", safeErrorCode: code, state: "ambiguous" };
927
+ }
928
+ acceptedUnconfirmed(code) {
929
+ return { phase: "accepted", safeErrorCode: code, state: "unconfirmed" };
930
+ }
931
+ terminal(result) {
932
+ this.phase = "terminal";
933
+ this.sideChannelEvidenceValid = false;
934
+ return { ...result, phase: "terminal", state: "terminal" };
935
+ }
936
+ }
937
+ function normalizeOuterError(error) {
938
+ if (error instanceof OperationError)
939
+ return error.code;
940
+ if (error instanceof LocalCodexTransportError)
941
+ return error.code;
942
+ return "PROTOCOL_ERROR";
943
+ }
944
+ /**
945
+ * Construct an inert stateless Codex operation runner. Construction performs
946
+ * no provider I/O. Every execute independently re-attests the current managed
947
+ * installation, owns one proxy/connection, and closes it before returning.
948
+ */
949
+ export function createStatelessCodexOperationTransport(options = {}, dependencies = {}) {
950
+ const normalized = normalizeOptions(options);
951
+ return {
952
+ async execute(input) {
953
+ let factory;
954
+ let owned;
955
+ let session;
956
+ let result;
957
+ let setupCleanupFailed = false;
958
+ let lateCleanupUnconfirmed = false;
959
+ let abortClose;
960
+ let removeAbortListener;
961
+ try {
962
+ const validated = validateInput(input, normalized);
963
+ const signal = input.signal;
964
+ if (signal?.aborted) {
965
+ throw new OperationError("TRANSPORT_CLOSED");
966
+ }
967
+ const factoryOptions = {
968
+ ...normalized.local,
969
+ hostId: input.route.hostId,
970
+ };
971
+ const factoryPending = dependencies.createFactory?.(factoryOptions) ??
972
+ createLocalCodexTransportFactory(factoryOptions, dependencies.localDependencies ?? {});
973
+ const created = await awaitSetupResource(factoryPending, signal);
974
+ if (created === SETUP_ABORTED) {
975
+ lateCleanupUnconfirmed = true;
976
+ throw new OperationError("TRANSPORT_CLOSED");
977
+ }
978
+ factory = created;
979
+ if (signal?.aborted)
980
+ throw new OperationError("TRANSPORT_CLOSED");
981
+ const connected = await awaitSetupResource(factory.connectTransport(), signal);
982
+ if (connected === SETUP_ABORTED) {
983
+ lateCleanupUnconfirmed = true;
984
+ throw new OperationError("TRANSPORT_CLOSED");
985
+ }
986
+ owned = connected;
987
+ if (signal?.aborted)
988
+ throw new OperationError("TRANSPORT_CLOSED");
989
+ session = new OperationSession(owned, input, normalized, validated.deadlineAtMs, validated.bodyBytes);
990
+ if (signal !== undefined) {
991
+ const abort = () => {
992
+ session?.abort();
993
+ if (abortClose === undefined && owned !== undefined) {
994
+ try {
995
+ abortClose = owned.close();
996
+ }
997
+ catch (error) {
998
+ abortClose = Promise.reject(error);
999
+ }
1000
+ void abortClose.catch(() => undefined);
1001
+ }
1002
+ };
1003
+ if (signal.aborted)
1004
+ abort();
1005
+ else {
1006
+ signal.addEventListener("abort", abort, { once: true });
1007
+ removeAbortListener = () => signal.removeEventListener("abort", abort);
1008
+ }
1009
+ }
1010
+ result = await session.execute();
1011
+ }
1012
+ catch (error) {
1013
+ setupCleanupFailed =
1014
+ error instanceof LocalCodexTransportError &&
1015
+ error.code === "CLEANUP_FAILED";
1016
+ result = {
1017
+ phase: "clean",
1018
+ safeErrorCode: normalizeOuterError(error),
1019
+ state: "failed",
1020
+ };
1021
+ }
1022
+ removeAbortListener?.();
1023
+ session?.dispose();
1024
+ let cleanupConfirmed = owned === undefined && !setupCleanupFailed && !lateCleanupUnconfirmed;
1025
+ if (owned !== undefined) {
1026
+ try {
1027
+ await (abortClose ?? owned.close());
1028
+ }
1029
+ catch {
1030
+ // Cleanup truth is returned separately and never overwrites delivery.
1031
+ }
1032
+ cleanupConfirmed = owned.cleanupConfirmed;
1033
+ }
1034
+ if (factory !== undefined) {
1035
+ try {
1036
+ await factory.close();
1037
+ }
1038
+ catch {
1039
+ cleanupConfirmed = false;
1040
+ }
1041
+ }
1042
+ return { ...result, attemptId: input.attemptId, cleanupConfirmed };
1043
+ },
1044
+ async observe(route, signal) {
1045
+ let factory;
1046
+ let owned;
1047
+ let session;
1048
+ try {
1049
+ if (!validHost(route.hostId) ||
1050
+ !validAlias(route.alias, route.hostId) ||
1051
+ !validOpaqueId(route.threadId) ||
1052
+ !validOpaqueId(route.registrationId, 128) ||
1053
+ signal?.aborted) {
1054
+ return { state: "unobserved", safeErrorCode: "CODEX_OBSERVER_UNAVAILABLE" };
1055
+ }
1056
+ const factoryOptions = { ...normalized.local, hostId: route.hostId };
1057
+ const created = await awaitSetupResource(dependencies.createFactory?.(factoryOptions) ??
1058
+ createLocalCodexTransportFactory(factoryOptions, dependencies.localDependencies ?? {}), signal);
1059
+ if (created === SETUP_ABORTED) {
1060
+ return { state: "unobserved", safeErrorCode: "CODEX_OBSERVER_UNAVAILABLE" };
1061
+ }
1062
+ factory = created;
1063
+ const connected = await awaitSetupResource(factory.connectTransport(), signal);
1064
+ if (connected === SETUP_ABORTED) {
1065
+ return { state: "unobserved", safeErrorCode: "CODEX_OBSERVER_UNAVAILABLE" };
1066
+ }
1067
+ owned = connected;
1068
+ const input = {
1069
+ attemptId: "observer",
1070
+ authorizeWrite: async () => false,
1071
+ deadlineAt: new Date(normalized.now().getTime() + normalized.requestTimeoutMs).toISOString(),
1072
+ kind: "start",
1073
+ onAccepted: async () => undefined,
1074
+ route,
1075
+ text: "observer",
1076
+ ...(signal === undefined ? {} : { signal }),
1077
+ };
1078
+ session = new OperationSession(owned, input, normalized, Infinity, 0);
1079
+ if (signal !== undefined) {
1080
+ const abort = () => session?.abort();
1081
+ signal.addEventListener("abort", abort, { once: true });
1082
+ try {
1083
+ return await session.observe();
1084
+ }
1085
+ finally {
1086
+ signal.removeEventListener("abort", abort);
1087
+ }
1088
+ }
1089
+ return await session.observe();
1090
+ }
1091
+ catch {
1092
+ return { state: "unobserved", safeErrorCode: "CODEX_OBSERVER_UNAVAILABLE" };
1093
+ }
1094
+ finally {
1095
+ session?.dispose();
1096
+ try {
1097
+ await owned?.close();
1098
+ }
1099
+ catch { /* display evidence only */ }
1100
+ try {
1101
+ await factory?.close();
1102
+ }
1103
+ catch { /* display evidence only */ }
1104
+ }
1105
+ },
1106
+ };
1107
+ }
1108
+ //# sourceMappingURL=codex-stateless-transport.js.map