orkestrator 2.31.0-beta → 2.32.0-beta

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.
@@ -331,7 +331,7 @@ function createBridgeDiagnostics(bridge, state, metrics) {
331
331
  import { execFile as execFileCallback2 } from "node:child_process";
332
332
  import { randomBytes, timingSafeEqual } from "node:crypto";
333
333
  import { appendFile as appendFile5, mkdir as mkdir8, readFile as readFile7, writeFile as writeFile6 } from "node:fs/promises";
334
- import { join as join15 } from "node:path";
334
+ import { join as join17 } from "node:path";
335
335
  import { promisify as promisify2 } from "node:util";
336
336
 
337
337
  // ../../node_modules/.bun/@hono+node-server@2.1.5+c539cad7f494f5c9/node_modules/@hono/node-server/dist/constants-BLSFu_RU.mjs
@@ -5941,8 +5941,18 @@ function registerCodexChangeEvidenceRoutes(app, spool) {
5941
5941
 
5942
5942
  // ../../bridges/codex-bridge/src/change-evidence.ts
5943
5943
  import { createHash as createHash5 } from "node:crypto";
5944
- import { existsSync } from "node:fs";
5945
- import { join as join6 } from "node:path";
5944
+ import { existsSync as existsSync2 } from "node:fs";
5945
+ import { basename, join as join8 } from "node:path";
5946
+
5947
+ // ../../bridges/codex-bridge/src/bridge-state-dir.ts
5948
+ import { isAbsolute as isAbsolute2, join as join2 } from "node:path";
5949
+ function codexBridgeStateDir(codexHome, env = process.env) {
5950
+ const override = env.CODEX_BRIDGE_STATE_DIR?.trim();
5951
+ return override && isAbsolute2(override) ? override : codexLoginBridgeDir(codexHome);
5952
+ }
5953
+ function codexLoginBridgeDir(codexHome) {
5954
+ return join2(codexHome, "orkestrator-bridge");
5955
+ }
5946
5956
 
5947
5957
  // ../protocol/src/change-evidence.ts
5948
5958
  var PROVIDER_EVIDENCE_SCHEMA_VERSION = 1;
@@ -6340,7 +6350,7 @@ function workspaceObservationReportIssues(value) {
6340
6350
  import { createHash as createHash4, randomUUID as randomUUID3 } from "node:crypto";
6341
6351
  import { readdirSync, readFileSync } from "node:fs";
6342
6352
  import { appendFile, mkdir, rename, rm as rm2, writeFile } from "node:fs/promises";
6343
- import { join as join2 } from "node:path";
6353
+ import { join as join3 } from "node:path";
6344
6354
  var DEFAULT_EVIDENCE_SPOOL_LIMITS = {
6345
6355
  maxEntries: 2048,
6346
6356
  maxEntryBytes: 256 * 1024,
@@ -6603,7 +6613,7 @@ class ChangeEvidenceSpool {
6603
6613
  const lines = this.pendingLines.splice(0).join("");
6604
6614
  this.enqueueIo(async () => {
6605
6615
  await mkdir(directory, { recursive: true, mode: 448 });
6606
- await appendFile(join2(directory, ENTRIES_FILE), lines, { mode: 384 });
6616
+ await appendFile(join3(directory, ENTRIES_FILE), lines, { mode: 384 });
6607
6617
  });
6608
6618
  }
6609
6619
  persistBody(id, bytes) {
@@ -6611,9 +6621,9 @@ class ChangeEvidenceSpool {
6611
6621
  if (!directory || this.restoring)
6612
6622
  return;
6613
6623
  this.enqueueIo(async () => {
6614
- const bodies = join2(directory, BODY_DIRECTORY);
6624
+ const bodies = join3(directory, BODY_DIRECTORY);
6615
6625
  await mkdir(bodies, { recursive: true, mode: 448 });
6616
- await writeFile(join2(bodies, bodyFileName(id)), bytes, { mode: 384, flag: "wx" }).catch((error) => {
6626
+ await writeFile(join3(bodies, bodyFileName(id)), bytes, { mode: 384, flag: "wx" }).catch((error) => {
6617
6627
  if (error.code !== "EEXIST")
6618
6628
  throw error;
6619
6629
  });
@@ -6623,7 +6633,7 @@ class ChangeEvidenceSpool {
6623
6633
  const directory = this.directory;
6624
6634
  if (!directory)
6625
6635
  return;
6626
- this.enqueueIo(() => rm2(join2(directory, BODY_DIRECTORY, bodyFileName(id)), { force: true }));
6636
+ this.enqueueIo(() => rm2(join3(directory, BODY_DIRECTORY, bodyFileName(id)), { force: true }));
6627
6637
  }
6628
6638
  compactPersisted() {
6629
6639
  const directory = this.directory;
@@ -6634,7 +6644,7 @@ class ChangeEvidenceSpool {
6634
6644
  `);
6635
6645
  this.enqueueIo(async () => {
6636
6646
  await mkdir(directory, { recursive: true, mode: 448 });
6637
- const file = join2(directory, ENTRIES_FILE);
6647
+ const file = join3(directory, ENTRIES_FILE);
6638
6648
  const temp = `${file}.${process.pid}.tmp`;
6639
6649
  await writeFile(temp, body ? `${body}
6640
6650
  ` : "", { mode: 384 });
@@ -6646,10 +6656,10 @@ class ChangeEvidenceSpool {
6646
6656
  await this.io;
6647
6657
  }
6648
6658
  restore(directory) {
6649
- const bodyDirectory = join2(directory, BODY_DIRECTORY);
6659
+ const bodyDirectory = join3(directory, BODY_DIRECTORY);
6650
6660
  try {
6651
6661
  for (const name of readdirSync(bodyDirectory)) {
6652
- const bytes = new Uint8Array(readFileSync(join2(bodyDirectory, name)));
6662
+ const bytes = new Uint8Array(readFileSync(join3(bodyDirectory, name)));
6653
6663
  const id = evidenceBodyId(bytes);
6654
6664
  if (bodyFileName(id) !== name)
6655
6665
  continue;
@@ -6661,7 +6671,7 @@ class ChangeEvidenceSpool {
6661
6671
  } catch {}
6662
6672
  let text = "";
6663
6673
  try {
6664
- text = readFileSync(join2(directory, ENTRIES_FILE), "utf8");
6674
+ text = readFileSync(join3(directory, ENTRIES_FILE), "utf8");
6665
6675
  } catch {
6666
6676
  return;
6667
6677
  }
@@ -6999,7 +7009,7 @@ function canonicalEvidence(evidence) {
6999
7009
  }
7000
7010
 
7001
7011
  // ../../bridges/codex-bridge/src/messages/apply-patch.ts
7002
- import { isAbsolute as isAbsolute2, join as join3 } from "node:path";
7012
+ import { isAbsolute as isAbsolute3, join as join4 } from "node:path";
7003
7013
 
7004
7014
  // ../../bridges/codex-bridge/src/messages/diff-budget.ts
7005
7015
  var MAX_INLINE_FILE_BYTES = 256 * 1024;
@@ -7153,7 +7163,7 @@ function rawPatchTitle(change) {
7153
7163
  }
7154
7164
  function rawApplyPatchParts(value, cwd, state) {
7155
7165
  return parseRawApplyPatchChanges(value).map((change) => {
7156
- const filePath = isAbsolute2(change.targetPath) ? change.targetPath : join3(cwd, change.targetPath);
7166
+ const filePath = isAbsolute3(change.targetPath) ? change.targetPath : join4(cwd, change.targetPath);
7157
7167
  return {
7158
7168
  type: "tool-invocation",
7159
7169
  content: change.targetPath,
@@ -7176,7 +7186,7 @@ function rawApplyPatchParts(value, cwd, state) {
7176
7186
  }
7177
7187
 
7178
7188
  // ../../bridges/codex-bridge/src/messages/file-change-diff.ts
7179
- import { isAbsolute as isAbsolute3, join as join4, relative } from "node:path";
7189
+ import { isAbsolute as isAbsolute4, join as join5, relative } from "node:path";
7180
7190
  var MOVED_TO_SUFFIX = /\n\nMoved to: ([^\n]+)\n?$/;
7181
7191
  var UNIFIED_START = /^(?:@@ -\d+(?:,\d+)? \+\d+(?:,\d+)? @@|--- |diff --git )/;
7182
7192
  function suppliedFileChange(change) {
@@ -7200,7 +7210,7 @@ function suppliedFileChange(change) {
7200
7210
  };
7201
7211
  }
7202
7212
  function headerPath(cwd, path) {
7203
- return isAbsolute3(path) ? relative(cwd, path) || path : path;
7213
+ return isAbsolute4(path) ? relative(cwd, path) || path : path;
7204
7214
  }
7205
7215
  function contentLines(content) {
7206
7216
  if (content.length === 0)
@@ -7239,11 +7249,11 @@ ${supplied.hunks}`;
7239
7249
  `);
7240
7250
  }
7241
7251
  function resolvedChangePath(cwd, path) {
7242
- return isAbsolute3(path) ? path : join4(cwd, path);
7252
+ return isAbsolute4(path) ? path : join5(cwd, path);
7243
7253
  }
7244
7254
 
7245
7255
  // ../../bridges/codex-bridge/src/sessions/command-changes.ts
7246
- import { isAbsolute as isAbsolute4, join as join5 } from "node:path";
7256
+ import { isAbsolute as isAbsolute5, join as join6 } from "node:path";
7247
7257
  import { fileURLToPath } from "node:url";
7248
7258
 
7249
7259
  // ../protocol/src/node/command-change-journal.ts
@@ -7424,7 +7434,7 @@ function journalFileName(threadId) {
7424
7434
  }
7425
7435
  function commandChangeJournalPath(codexHome, threadId) {
7426
7436
  const fileName = journalFileName(threadId);
7427
- return fileName ? join5(codexHome, "orkestrator-bridge", "command-changes", fileName) : undefined;
7437
+ return fileName ? join6(codexBridgeStateDir(codexHome), "command-changes", fileName) : undefined;
7428
7438
  }
7429
7439
  function commandChangeJournalFor(codexHome, threadId) {
7430
7440
  const path = commandChangeJournalPath(codexHome, threadId);
@@ -7449,7 +7459,13 @@ async function readCommandChanges(codexHome, threadId) {
7449
7459
  const journal = commandChangeJournalFor(codexHome, threadId);
7450
7460
  if (!journal)
7451
7461
  return new Map;
7452
- return journal.read().catch(() => new Map);
7462
+ const current = await journal.read().catch(() => new Map);
7463
+ const fileName = journalFileName(threadId);
7464
+ const legacyPath = fileName ? join6(codexLoginBridgeDir(codexHome), "command-changes", fileName) : undefined;
7465
+ if (!legacyPath || legacyPath === commandChangeJournalPath(codexHome, threadId))
7466
+ return current;
7467
+ const legacy = await new CommandChangeJournal(legacyPath).read().catch(() => new Map);
7468
+ return legacy.size === 0 ? current : new Map([...legacy, ...current]);
7453
7469
  }
7454
7470
  function recordCommandChanges(codexHome, threadId, toolUseId, change) {
7455
7471
  if (!hasMeasuredChanges(change))
@@ -7479,7 +7495,7 @@ function commandChangeCwd(item, fallback) {
7479
7495
  return fallback;
7480
7496
  }
7481
7497
  }
7482
- return isAbsolute4(item.cwd) ? item.cwd : fallback;
7498
+ return isAbsolute5(item.cwd) ? item.cwd : fallback;
7483
7499
  }
7484
7500
  function commandDidNotRun(item) {
7485
7501
  return item.type === "command_execution" && item.status === "failed" && item.exit_code === undefined;
@@ -7556,8 +7572,111 @@ async function inheritCommandChanges(codexHome, parentThreadId, forkThreadId, me
7556
7572
  journal?.append(id, change);
7557
7573
  }
7558
7574
 
7575
+ // ../../bridges/codex-bridge/src/change-evidence-migration.ts
7576
+ import { randomUUID as randomUUID4 } from "node:crypto";
7577
+ import {
7578
+ existsSync,
7579
+ mkdirSync,
7580
+ readFileSync as readFileSync2,
7581
+ readdirSync as readdirSync2,
7582
+ renameSync,
7583
+ rmSync as rmSync2,
7584
+ statSync,
7585
+ writeFileSync
7586
+ } from "node:fs";
7587
+ import { join as join7, resolve as resolve2 } from "node:path";
7588
+ import { isDeepStrictEqual } from "node:util";
7589
+ function readBounded(path, limit) {
7590
+ if (statSync(path).size > limit)
7591
+ throw new Error("Evidence migration exceeds storage capacity");
7592
+ const bytes = readFileSync2(path);
7593
+ if (bytes.length > limit)
7594
+ throw new Error("Evidence migration exceeds storage capacity");
7595
+ return bytes;
7596
+ }
7597
+ function publishFile(path, contents) {
7598
+ const temporary = `${path}.${randomUUID4()}.tmp`;
7599
+ try {
7600
+ writeFileSync(temporary, contents, { mode: 384 });
7601
+ renameSync(temporary, path);
7602
+ } finally {
7603
+ rmSync2(temporary, { force: true });
7604
+ }
7605
+ }
7606
+ function migrateCodexChangeEvidence(legacy, shared) {
7607
+ if (resolve2(legacy) === resolve2(shared))
7608
+ return;
7609
+ const legacyFile = join7(legacy, "entries.jsonl");
7610
+ if (!existsSync(legacyFile))
7611
+ return;
7612
+ const entries = new Map;
7613
+ const bodyIds = new Set;
7614
+ for (const directory of [shared, legacy]) {
7615
+ const file = join7(directory, "entries.jsonl");
7616
+ if (!existsSync(file))
7617
+ continue;
7618
+ for (const line of readBounded(file, DEFAULT_EVIDENCE_SPOOL_LIMITS.maxQueuedBytes).toString("utf8").split(`
7619
+ `)) {
7620
+ if (!line)
7621
+ continue;
7622
+ const entry = JSON.parse(line).entry;
7623
+ let identity;
7624
+ if (entry?.type === "frame" && isProviderEvidenceFrame(entry.frame)) {
7625
+ identity = `frame:${entry.frame.frameId}`;
7626
+ for (const id of collectBodyIds(entry.frame))
7627
+ bodyIds.add(id);
7628
+ } else if (entry?.type === "observation" && workspaceObservationReportIssues(entry.report).length === 0) {
7629
+ identity = `observation:${entry.report.reportId}`;
7630
+ } else {
7631
+ throw new Error("Evidence migration encountered an invalid entry");
7632
+ }
7633
+ const normalized = { ...entry, seq: 0 };
7634
+ const existing = entries.get(identity);
7635
+ if (existing && !isDeepStrictEqual(existing, normalized)) {
7636
+ throw new Error("Evidence migration encountered conflicting identities");
7637
+ }
7638
+ entries.set(identity, normalized);
7639
+ }
7640
+ }
7641
+ const lines = [...entries.values()].map((entry, index) => {
7642
+ const serialized = JSON.stringify({ ...entry, seq: index + 1 });
7643
+ if (serialized.length > DEFAULT_EVIDENCE_SPOOL_LIMITS.maxEntryBytes) {
7644
+ throw new Error("Evidence migration exceeds entry capacity");
7645
+ }
7646
+ return JSON.stringify({ entry: { ...entry, seq: index + 1 } });
7647
+ });
7648
+ const contents = lines.length ? `${lines.join(`
7649
+ `)}
7650
+ ` : "";
7651
+ if (entries.size >= DEFAULT_EVIDENCE_SPOOL_LIMITS.maxEntries || Buffer.byteLength(contents) > DEFAULT_EVIDENCE_SPOOL_LIMITS.maxQueuedBytes) {
7652
+ throw new Error("Evidence migration exceeds queue capacity");
7653
+ }
7654
+ mkdirSync(join7(shared, "bodies"), { recursive: true, mode: 448 });
7655
+ let bodyBytes = 0;
7656
+ for (const id of bodyIds) {
7657
+ const name = id.replace(/[^A-Za-z0-9]/g, "_");
7658
+ const target = join7(shared, "bodies", name);
7659
+ const bytes = readBounded(existsSync(target) ? target : join7(legacy, "bodies", name), DEFAULT_EVIDENCE_SPOOL_LIMITS.maxSingleBodyBytes);
7660
+ if (evidenceBodyId(bytes) !== id)
7661
+ throw new Error("Evidence migration body integrity failed");
7662
+ bodyBytes += bytes.length;
7663
+ if (bodyBytes > DEFAULT_EVIDENCE_SPOOL_LIMITS.maxBodyBytes)
7664
+ throw new Error("Evidence migration exceeds body capacity");
7665
+ if (!existsSync(target))
7666
+ publishFile(target, bytes);
7667
+ }
7668
+ const retainedNames = new Set([...bodyIds].map((id) => id.replace(/[^A-Za-z0-9]/g, "_")));
7669
+ for (const name of readdirSync2(join7(shared, "bodies"))) {
7670
+ if (/^sha256_[a-f0-9]{64}$/.test(name) && !retainedNames.has(name)) {
7671
+ rmSync2(join7(shared, "bodies", name));
7672
+ }
7673
+ }
7674
+ publishFile(join7(shared, "entries.jsonl"), contents);
7675
+ rmSync2(legacyFile);
7676
+ }
7677
+
7559
7678
  // ../../bridges/codex-bridge/src/change-evidence.ts
7560
- function detectCaptureExecutor(exists = existsSync) {
7679
+ function detectCaptureExecutor(exists = existsSync2) {
7561
7680
  return exists("/.dockerenv") || exists("/run/.containerenv") ? "container-worker" : "host-worker";
7562
7681
  }
7563
7682
  function codexChangeCapabilities(executor) {
@@ -7793,13 +7912,13 @@ function suppliedMovePath(change) {
7793
7912
  }
7794
7913
  function codexChangeEvidenceDir(codexHome, cwd) {
7795
7914
  const cwdHash = createHash5("sha256").update(cwd).digest("hex").slice(0, 16);
7796
- return join6(codexHome, "orkestrator-bridge", "change-evidence", cwdHash);
7915
+ return join8(codexBridgeStateDir(codexHome), "change-evidence", cwdHash);
7797
7916
  }
7798
7917
  function createCodexChangeEvidence(options) {
7918
+ const directory = codexChangeEvidenceDir(options.codexHome, options.cwd);
7919
+ migrateCodexChangeEvidence(join8(codexLoginBridgeDir(options.codexHome), "change-evidence", basename(directory)), directory);
7799
7920
  return new CodexChangeEvidence({
7800
- spool: new ChangeEvidenceSpool({
7801
- directory: codexChangeEvidenceDir(options.codexHome, options.cwd)
7802
- }),
7921
+ spool: new ChangeEvidenceSpool({ directory }),
7803
7922
  executor: options.executor ?? detectCaptureExecutor()
7804
7923
  });
7805
7924
  }
@@ -7828,7 +7947,7 @@ function codexApprovalConfig(policy, sandbox, mode) {
7828
7947
  import { spawn as spawn2 } from "node:child_process";
7829
7948
  import { appendFile as appendFile3, mkdir as mkdir3, mkdtemp as mkdtemp2, readFile as readFile2, rm as rm4, writeFile as writeFile3 } from "node:fs/promises";
7830
7949
  import { tmpdir as tmpdir2 } from "node:os";
7831
- import { join as join7 } from "node:path";
7950
+ import { join as join9 } from "node:path";
7832
7951
 
7833
7952
  // ../protocol/src/codex-background-task.ts
7834
7953
  var CODEX_BACKGROUND_TASK_MODEL = "gpt-5.6-luna";
@@ -7908,7 +8027,7 @@ async function refreshRuntimeEnvironment(run = execFile2) {
7908
8027
  `if [ -f "$${RUNTIME_ENV_SCRIPT_ENV}" ]; then . "$${RUNTIME_ENV_SCRIPT_ENV}" 2>/dev/null || true; orkestrator_source_runtime_env 2>/dev/null || true; fi; env`
7909
8028
  ], {
7910
8029
  env: {
7911
- ...process.env,
8030
+ ...runtimeEnvironmentSnapshot(),
7912
8031
  [RUNTIME_ENV_SCRIPT_ENV]: runtimeEnvScript
7913
8032
  },
7914
8033
  maxBuffer: 256 * 1024
@@ -7932,7 +8051,9 @@ function fingerprintRuntimeEnvironment(env = process.env) {
7932
8051
  return `sha256:${hash.digest("hex").slice(0, 32)}`;
7933
8052
  }
7934
8053
  function runtimeEnvironmentSnapshot(env = process.env) {
7935
- return { ...env };
8054
+ const snapshot = { ...env };
8055
+ delete snapshot.CODEX_BRIDGE_STATE_DIR;
8056
+ return snapshot;
7936
8057
  }
7937
8058
  function runtimeEnvironmentWithoutCredentials(env = process.env) {
7938
8059
  const snapshot = runtimeEnvironmentSnapshot(env);
@@ -8217,10 +8338,10 @@ async function runCodexTitleCommand(codexPath, args, input, options = {}) {
8217
8338
  });
8218
8339
  }
8219
8340
  async function generateSessionTitleWithCodexExec(codexPath, sourcePrompt, options = {}) {
8220
- const temporaryDirectory = await mkdtemp2(join7(options.temporaryRoot ?? tmpdir2(), "orkestrator-session-title-"));
8221
- const outputPath = join7(temporaryDirectory, "title.txt");
8222
- const modelCatalogPath = join7(temporaryDirectory, "model-catalog.json");
8223
- const outputSchemaPath = join7(temporaryDirectory, "title-schema.json");
8341
+ const temporaryDirectory = await mkdtemp2(join9(options.temporaryRoot ?? tmpdir2(), "orkestrator-session-title-"));
8342
+ const outputPath = join9(temporaryDirectory, "title.txt");
8343
+ const modelCatalogPath = join9(temporaryDirectory, "model-catalog.json");
8344
+ const outputSchemaPath = join9(temporaryDirectory, "title-schema.json");
8224
8345
  const maxOutputBytes = options.maxOutputBytes ?? MAX_COMMAND_OUTPUT_LENGTH;
8225
8346
  try {
8226
8347
  await Promise.all([
@@ -8251,7 +8372,11 @@ async function generateSessionTitleWithCodexExec(codexPath, sourcePrompt, option
8251
8372
  }
8252
8373
  }
8253
8374
  function sessionTitleIndexPath(codexHome) {
8254
- return join7(codexHome, "orkestrator-bridge", SESSION_TITLE_INDEX_FILENAME);
8375
+ return join9(codexBridgeStateDir(codexHome), SESSION_TITLE_INDEX_FILENAME);
8376
+ }
8377
+ function legacySessionTitleIndexPath(codexHome) {
8378
+ const legacy = join9(codexLoginBridgeDir(codexHome), SESSION_TITLE_INDEX_FILENAME);
8379
+ return legacy === sessionTitleIndexPath(codexHome) ? undefined : legacy;
8255
8380
  }
8256
8381
  function persistedSourcePriority(source) {
8257
8382
  if (source === "explicit")
@@ -8261,7 +8386,11 @@ function persistedSourcePriority(source) {
8261
8386
  return 1;
8262
8387
  }
8263
8388
  async function readPersistedSessionTitleEntries(codexHome) {
8264
- const raw = await readFile2(sessionTitleIndexPath(codexHome), "utf8").catch(() => "");
8389
+ const legacyPath = legacySessionTitleIndexPath(codexHome);
8390
+ const legacy = legacyPath ? await readFile2(legacyPath, "utf8").catch(() => "") : "";
8391
+ const current = await readFile2(sessionTitleIndexPath(codexHome), "utf8").catch(() => "");
8392
+ const raw = legacy ? `${legacy}
8393
+ ${current}` : current;
8265
8394
  const selected = new Map;
8266
8395
  for (const [lineIndex, line] of raw.split(`
8267
8396
  `).entries()) {
@@ -8306,7 +8435,7 @@ async function persistSessionTitle(codexHome, threadId, title, options = {}) {
8306
8435
  throw new Error("Session title updatedAt must be a valid date");
8307
8436
  }
8308
8437
  const updatedAt = requestedUpdatedAt.toISOString();
8309
- const directory = join7(codexHome, "orkestrator-bridge");
8438
+ const directory = codexBridgeStateDir(codexHome);
8310
8439
  await mkdir3(directory, { recursive: true });
8311
8440
  await appendFile3(sessionTitleIndexPath(codexHome), `${JSON.stringify({
8312
8441
  threadId: normalizedThreadId,
@@ -10825,7 +10954,7 @@ async function deriveTranscriptSubagentPartsForTurn({
10825
10954
  import { createHash as createHash7 } from "node:crypto";
10826
10955
  import { readFile as readFile4, readdir, stat as stat4 } from "node:fs/promises";
10827
10956
  import { homedir } from "node:os";
10828
- import { basename, join as join8 } from "node:path";
10957
+ import { basename as basename2, join as join10 } from "node:path";
10829
10958
 
10830
10959
  // ../../bridges/codex-bridge/src/sessions/refused-cell-tail.ts
10831
10960
  import { open as open2, stat as stat3 } from "node:fs/promises";
@@ -11021,7 +11150,7 @@ class RefusedCellTail {
11021
11150
  }
11022
11151
 
11023
11152
  // ../../bridges/codex-bridge/src/app-server/item-adapter.ts
11024
- import { isAbsolute as isAbsolute5 } from "node:path";
11153
+ import { isAbsolute as isAbsolute6 } from "node:path";
11025
11154
  function isRecord5(value) {
11026
11155
  return !!value && typeof value === "object" && !Array.isArray(value);
11027
11156
  }
@@ -11043,7 +11172,7 @@ function imageGenerationInputPaths(value) {
11043
11172
  if (paths.size >= MAX_IMAGE_GENERATION_INPUTS)
11044
11173
  break;
11045
11174
  const path = isRecord5(entry) ? str(entry.path) : undefined;
11046
- if (path && isAbsolute5(path))
11175
+ if (path && isAbsolute6(path))
11047
11176
  paths.add(path);
11048
11177
  }
11049
11178
  return [...paths];
@@ -11390,6 +11519,28 @@ function mapTodoArgs(items) {
11390
11519
  };
11391
11520
  }
11392
11521
 
11522
+ // ../../bridges/codex-bridge/src/messages/turn-failure.ts
11523
+ var MAX_TURN_FAILURE_CHARS = 2000;
11524
+ function turnFailureItemId(turnId) {
11525
+ return `turn-error:${turnId}`;
11526
+ }
11527
+ function turnFailureText(message) {
11528
+ const raw = message.trim();
11529
+ let text = raw;
11530
+ if (raw.startsWith("{")) {
11531
+ try {
11532
+ const body = JSON.parse(raw);
11533
+ const nested = body.error && typeof body.error === "object" ? body.error.message : undefined;
11534
+ const candidate = typeof nested === "string" ? nested : body.message;
11535
+ if (typeof candidate === "string" && candidate.trim())
11536
+ text = candidate.trim();
11537
+ } catch {}
11538
+ }
11539
+ if (!text)
11540
+ text = "Codex ended the turn on an error.";
11541
+ return text.length > MAX_TURN_FAILURE_CHARS ? `${text.slice(0, MAX_TURN_FAILURE_CHARS - 1)}…` : text;
11542
+ }
11543
+
11393
11544
  // ../../bridges/codex-bridge/src/sessions/turn-accumulator.ts
11394
11545
  var MAX_ITEM_PROGRESS_CHARS = 200;
11395
11546
  var MODEL_STEP_TOOL_ITEM_TYPES = new Set([
@@ -11711,6 +11862,14 @@ class TurnAccumulator {
11711
11862
  this.touch(accumulator);
11712
11863
  }
11713
11864
  }
11865
+ recordProviderFailure(error) {
11866
+ this.onItemCompleted({
11867
+ id: turnFailureItemId(this.turnId),
11868
+ type: "status",
11869
+ text: turnFailureText(error.message),
11870
+ severity: "error"
11871
+ });
11872
+ }
11714
11873
  interruptOpenAgentMessages(keepId) {
11715
11874
  for (const id of Array.from(this.openAgentMessageIds)) {
11716
11875
  if (id === keepId)
@@ -12412,7 +12571,7 @@ function stripCoordinatorContext(text) {
12412
12571
 
12413
12572
  // ../../bridges/codex-bridge/src/history/rollout.ts
12414
12573
  function getCodexHomeDir() {
12415
- return process.env.CODEX_HOME || join8(homedir(), ".codex");
12574
+ return process.env.CODEX_HOME || join10(homedir(), ".codex");
12416
12575
  }
12417
12576
  function getWorkingDirectory(explicitCwd) {
12418
12577
  return explicitCwd || process.env.CWD || process.cwd();
@@ -12426,7 +12585,7 @@ async function walkJsonlFiles(dir) {
12426
12585
  }
12427
12586
  const files = [];
12428
12587
  for (const entry of entries) {
12429
- const absolutePath = join8(dir, entry.name);
12588
+ const absolutePath = join10(dir, entry.name);
12430
12589
  if (entry.isDirectory()) {
12431
12590
  files.push(...await walkJsonlFiles(absolutePath));
12432
12591
  continue;
@@ -12438,7 +12597,7 @@ async function walkJsonlFiles(dir) {
12438
12597
  return files;
12439
12598
  }
12440
12599
  async function listTranscriptPaths(codexHome = getCodexHomeDir()) {
12441
- const searchRoots = [join8(codexHome, "sessions"), join8(codexHome, "archived_sessions")];
12600
+ const searchRoots = [join10(codexHome, "sessions"), join10(codexHome, "archived_sessions")];
12442
12601
  return (await Promise.all(searchRoots.map((root) => walkJsonlFiles(root)))).flat();
12443
12602
  }
12444
12603
  var cachedTranscriptPaths = new Map;
@@ -12465,7 +12624,7 @@ function rememberTranscriptPath(threadId, path) {
12465
12624
  }
12466
12625
  function selectTranscriptPath(paths, threadId) {
12467
12626
  const anchored = paths.find((file) => {
12468
- const stem = basename(file).replace(/\.jsonl$/, "");
12627
+ const stem = basename2(file).replace(/\.jsonl$/, "");
12469
12628
  return stem === threadId || stem.endsWith(`-${threadId}`);
12470
12629
  });
12471
12630
  return anchored ?? paths.find((file) => file.includes(threadId)) ?? null;
@@ -12581,8 +12740,8 @@ async function buildTranscriptCatalog() {
12581
12740
  const metaByPath = new Map;
12582
12741
  const transcriptPathByThreadId = new Map;
12583
12742
  for (const root of [
12584
- join8(getCodexHomeDir(), "sessions"),
12585
- join8(getCodexHomeDir(), "archived_sessions")
12743
+ join10(getCodexHomeDir(), "sessions"),
12744
+ join10(getCodexHomeDir(), "archived_sessions")
12586
12745
  ]) {
12587
12746
  const files = await walkJsonlFiles(root);
12588
12747
  for (const transcriptPath of files) {
@@ -12592,7 +12751,7 @@ async function buildTranscriptCatalog() {
12592
12751
  }
12593
12752
  metas.push(meta);
12594
12753
  metaByPath.set(transcriptPath, meta);
12595
- const fileThreadId = basename(transcriptPath, ".jsonl");
12754
+ const fileThreadId = basename2(transcriptPath, ".jsonl");
12596
12755
  if (!transcriptPathByThreadId.has(fileThreadId)) {
12597
12756
  transcriptPathByThreadId.set(fileThreadId, transcriptPath);
12598
12757
  }
@@ -12777,7 +12936,7 @@ async function listPersistedSessionsForCwd(cwd, options) {
12777
12936
  return (await listPersistedSessionsWithTitlesForCwd(cwd, options)).sessions;
12778
12937
  }
12779
12938
  async function listPersistedSessionsWithTitlesForCwd(cwd, options) {
12780
- const indexPath = join8(getCodexHomeDir(), "session_index.jsonl");
12939
+ const indexPath = join10(getCodexHomeDir(), "session_index.jsonl");
12781
12940
  const lines = await readTranscriptLines(indexPath);
12782
12941
  const sessions = new Map;
12783
12942
  const transcriptCatalog = await buildTranscriptCatalogCached(options?.mustContainThreadId);
@@ -12826,9 +12985,16 @@ function mergePersistedSessionMeta(sessionsById, meta) {
12826
12985
  indexed.updatedAt = meta.updatedAt;
12827
12986
  }
12828
12987
  }
12988
+ var CODEX_CONTEXT_FRAGMENTS = [
12989
+ ["<environment_context>", "</environment_context>"],
12990
+ ["<skill>", "</skill>"],
12991
+ ["<turn_aborted>", "</turn_aborted>"],
12992
+ ["<subagent_notification>", "</subagent_notification>"],
12993
+ ["<user_shell_command>", "</user_shell_command>"]
12994
+ ];
12829
12995
  function isSyntheticPersistedUserText(text) {
12830
12996
  const trimmed = text.trim();
12831
- return trimmed.startsWith("# AGENTS.md instructions for ") || /^# AGENTS\.md instructions[ \t]*\n\s*<INSTRUCTIONS>/.test(trimmed) || trimmed.startsWith("<environment_context>") && trimmed.endsWith("</environment_context>") || trimmed.startsWith(`<recommended_plugins>
12997
+ return trimmed.startsWith("# AGENTS.md instructions for ") || /^# AGENTS\.md instructions[ \t]*\n\s*<INSTRUCTIONS>/.test(trimmed) || CODEX_CONTEXT_FRAGMENTS.some(([open, close]) => trimmed.startsWith(open) && trimmed.endsWith(close)) || trimmed.startsWith(`<recommended_plugins>
12832
12998
  Here is a list of plugins that are available but not installed.`);
12833
12999
  }
12834
13000
  function extractPersistedMessageContent(content, role) {
@@ -12845,6 +13011,9 @@ function extractPersistedMessageContent(content, role) {
12845
13011
  if (segments.length === 0) {
12846
13012
  return null;
12847
13013
  }
13014
+ if (role === "user" && segments.every((segment) => !segment.trim() || isSyntheticPersistedUserText(segment))) {
13015
+ return null;
13016
+ }
12848
13017
  const joined = segments.join(`
12849
13018
  `).trim();
12850
13019
  if (!joined) {
@@ -12854,7 +13023,7 @@ function extractPersistedMessageContent(content, role) {
12854
13023
  if (!text && parts.length === 0) {
12855
13024
  return null;
12856
13025
  }
12857
- if (role === "user" && isSyntheticPersistedUserText(text)) {
13026
+ if (role === "user" && segments.length === 1 && isSyntheticPersistedUserText(text)) {
12858
13027
  return null;
12859
13028
  }
12860
13029
  return { text, attachments: parts };
@@ -12906,7 +13075,7 @@ function persistedOutputState(payloadType, part, output) {
12906
13075
  return payloadType === "custom_tool_call_output" ? resolveTranscriptToolOutputState(part.toolName, output, part.toolState ?? null) : null;
12907
13076
  }
12908
13077
  async function findSessionIndexEntry(threadId) {
12909
- const lines = await readTranscriptLines(join8(getCodexHomeDir(), "session_index.jsonl"));
13078
+ const lines = await readTranscriptLines(join10(getCodexHomeDir(), "session_index.jsonl"));
12910
13079
  let match = null;
12911
13080
  for (const line of lines) {
12912
13081
  let parsed;
@@ -13238,6 +13407,17 @@ async function replayPersistedRollout(threadId, snapshot, meta, structuredTurns)
13238
13407
  visitCompletedItem(record.payload.item, timestamp);
13239
13408
  return;
13240
13409
  }
13410
+ if (record.type === "event_msg" && record.payload?.type === "task_complete") {
13411
+ const error = record.payload.error;
13412
+ if (error && typeof error === "object" && typeof error.message === "string") {
13413
+ ensureAssistantMessage(timestamp).parts.push({
13414
+ type: "status",
13415
+ severity: "error",
13416
+ content: turnFailureText(error.message)
13417
+ });
13418
+ }
13419
+ return;
13420
+ }
13241
13421
  if (record.type === "token_usage_record" || record.type === "event_msg" && record.payload?.type === "token_count") {
13242
13422
  if (currentRowCalledTool)
13243
13423
  responseCompletedInRow = true;
@@ -14127,7 +14307,7 @@ async function publishAssistantRows(context, state, turn, options) {
14127
14307
  }
14128
14308
 
14129
14309
  // ../../bridges/codex-bridge/src/app-server-runtime-sessions.ts
14130
- import { createHash as createHash10, randomUUID as randomUUID6 } from "node:crypto";
14310
+ import { createHash as createHash10, randomUUID as randomUUID7 } from "node:crypto";
14131
14311
 
14132
14312
  // ../protocol/src/agent-mail.ts
14133
14313
  var AGENT_MAIL_MAX_BODY_BYTES = 32 * 1024;
@@ -14192,11 +14372,11 @@ var ORKESTRATOR_ARTIFACT_MCP_PATH = "/artifact-mcp";
14192
14372
  // ../../bridges/codex-bridge/src/engine/app-server-engine.ts
14193
14373
  import { open as open6 } from "node:fs/promises";
14194
14374
  import { createHash as createHash9 } from "node:crypto";
14195
- import { isAbsolute as isAbsolute7, join as join13 } from "node:path";
14375
+ import { isAbsolute as isAbsolute8, join as join15 } from "node:path";
14196
14376
 
14197
14377
  // ../../bridges/codex-bridge/src/app-server/process-supervisor.ts
14198
14378
  import { execFile as execFile3, spawn as spawn3 } from "node:child_process";
14199
- import { randomUUID as randomUUID4 } from "node:crypto";
14379
+ import { randomUUID as randomUUID5 } from "node:crypto";
14200
14380
  import {
14201
14381
  link,
14202
14382
  mkdir as mkdir5,
@@ -14207,7 +14387,7 @@ import {
14207
14387
  rm as rm5,
14208
14388
  stat as stat5
14209
14389
  } from "node:fs/promises";
14210
- import { join as join10 } from "node:path";
14390
+ import { join as join12 } from "node:path";
14211
14391
 
14212
14392
  // ../../bridges/codex-bridge/src/app-server/errors.ts
14213
14393
  var APP_SERVER_OVERLOAD_CODE = -32001;
@@ -14795,7 +14975,7 @@ class SerialQueue {
14795
14975
 
14796
14976
  // ../../bridges/codex-bridge/src/app-server/notification-recorder.ts
14797
14977
  import { appendFile as appendFile4, mkdir as mkdir4 } from "node:fs/promises";
14798
- import { join as join9 } from "node:path";
14978
+ import { join as join11 } from "node:path";
14799
14979
  var DEFAULT_MAX_RECORDING_BYTES = 64 * 1024 * 1024;
14800
14980
  var DEFAULT_FLUSH_INTERVAL_MS = 250;
14801
14981
  var DEFAULT_WARN_INTERVAL_MS = 60000;
@@ -14821,7 +15001,7 @@ class NotificationRecorder {
14821
15001
  stats;
14822
15002
  constructor(options) {
14823
15003
  this.options = options;
14824
- this.path = join9(options.directory, options.fileName);
15004
+ this.path = join11(options.directory, options.fileName);
14825
15005
  this.maxBytes = options.maxBytes ?? DEFAULT_MAX_RECORDING_BYTES;
14826
15006
  this.flushIntervalMs = options.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;
14827
15007
  this.warnIntervalMs = options.warnIntervalMs ?? DEFAULT_WARN_INTERVAL_MS;
@@ -14992,7 +15172,7 @@ class AppServerSupervisor {
14992
15172
  unknownNotifications = 0;
14993
15173
  unknownServerRequests = 0;
14994
15174
  drainPromise = null;
14995
- pidOwnerToken = randomUUID4();
15175
+ pidOwnerToken = randomUUID5();
14996
15176
  pidOwnershipHeld = false;
14997
15177
  constructor(options) {
14998
15178
  this.options = options;
@@ -15151,7 +15331,7 @@ class AppServerSupervisor {
15151
15331
  if (this.stopping)
15152
15332
  throw new AppServerUnavailableError("stopped");
15153
15333
  const environmentFingerprint = this.fingerprintEnvironment();
15154
- const instanceId = randomUUID4();
15334
+ const instanceId = randomUUID5();
15155
15335
  const instanceStartedAt = this.now();
15156
15336
  if (this.options.pidFileEnabled !== false && !this.pidOwnershipHeld) {
15157
15337
  const available = await this.acquirePidFileOwnership(instanceId);
@@ -15169,7 +15349,7 @@ class AppServerSupervisor {
15169
15349
  const child = spawnFn(this.options.codexPath, args, {
15170
15350
  cwd: this.options.cwd,
15171
15351
  env: {
15172
- ...process.env,
15352
+ ...runtimeEnvironmentSnapshot(),
15173
15353
  CODEX_HOME: this.options.codexHome,
15174
15354
  LOG_FORMAT: "json",
15175
15355
  ORKESTRATOR_APP_SERVER_INSTANCE_ID: instanceId
@@ -15445,10 +15625,10 @@ class AppServerSupervisor {
15445
15625
  } catch {}
15446
15626
  }
15447
15627
  pidFileDir() {
15448
- return join10(this.options.codexHome, "orkestrator-bridge");
15628
+ return join12(this.options.codexHome, "orkestrator-bridge");
15449
15629
  }
15450
15630
  pidFilePath() {
15451
- return join10(this.pidFileDir(), `app-server-${hashPath(this.options.cwd)}.pid`);
15631
+ return join12(this.pidFileDir(), `app-server-${hashPath(this.options.cwd)}.pid`);
15452
15632
  }
15453
15633
  async readOwnedPidRecord(path) {
15454
15634
  let raw;
@@ -15706,7 +15886,7 @@ function runPs(args) {
15706
15886
  return new Promise((resolve, reject) => {
15707
15887
  execFile3("/bin/ps", args, {
15708
15888
  maxBuffer: 1024 * 1024,
15709
- env: { ...process.env, LC_ALL: "C", LANG: "C", TZ: process.env.TZ }
15889
+ env: { ...runtimeEnvironmentSnapshot(), LC_ALL: "C", LANG: "C", TZ: process.env.TZ }
15710
15890
  }, (error, stdout) => {
15711
15891
  if (error)
15712
15892
  reject(error);
@@ -17386,10 +17566,10 @@ function redactSecrets(value) {
17386
17566
 
17387
17567
  // ../../bridges/codex-bridge/src/sessions/dispatch-journal.ts
17388
17568
  import { mkdir as mkdir7, open as open4, rename as rename5, rm as rm7, writeFile as writeFile5 } from "node:fs/promises";
17389
- import { join as join12 } from "node:path";
17569
+ import { join as join14 } from "node:path";
17390
17570
 
17391
17571
  // ../../bridges/codex-bridge/src/sessions/persistence.ts
17392
- import { createHash as createHash8, randomUUID as randomUUID5 } from "node:crypto";
17572
+ import { createHash as createHash8, randomUUID as randomUUID6 } from "node:crypto";
17393
17573
  import {
17394
17574
  chmod,
17395
17575
  link as link2,
@@ -17401,7 +17581,7 @@ import {
17401
17581
  stat as stat6,
17402
17582
  writeFile as writeFile4
17403
17583
  } from "node:fs/promises";
17404
- import { join as join11 } from "node:path";
17584
+ import { join as join13 } from "node:path";
17405
17585
  var BRIDGE_SESSION_REGISTRY_VERSION = 2;
17406
17586
  var SESSION_TITLE_SOURCES = new Set([
17407
17587
  "codex",
@@ -17503,19 +17683,23 @@ class BridgeSessionStore {
17503
17683
  this.now = options.now ?? Date.now;
17504
17684
  }
17505
17685
  dir() {
17506
- return join11(this.codexHome, "orkestrator-bridge");
17686
+ return codexBridgeStateDir(this.codexHome);
17507
17687
  }
17508
17688
  path() {
17509
- return join11(this.dir(), `bridge-sessions-${this.cwdHash}.json`);
17689
+ return join13(this.dir(), `bridge-sessions-${this.cwdHash}.json`);
17510
17690
  }
17511
17691
  recordsDir() {
17512
- return join11(this.dir(), `bridge-sessions-${this.cwdHash}`);
17692
+ return join13(this.dir(), `bridge-sessions-${this.cwdHash}`);
17693
+ }
17694
+ loginRecordsDir() {
17695
+ const directory = join13(codexLoginBridgeDir(this.codexHome), `bridge-sessions-${this.cwdHash}`);
17696
+ return directory === this.recordsDir() ? undefined : directory;
17513
17697
  }
17514
17698
  recordKey(bridgeSessionId) {
17515
17699
  return createHash8("sha256").update(bridgeSessionId).digest("hex");
17516
17700
  }
17517
17701
  recordPath(bridgeSessionId) {
17518
- return join11(this.recordsDir(), `${this.recordKey(bridgeSessionId)}.json`);
17702
+ return join13(this.recordsDir(), `${this.recordKey(bridgeSessionId)}.json`);
17519
17703
  }
17520
17704
  async ensurePrivateStorage() {
17521
17705
  await mkdir6(this.dir(), { recursive: true, mode: PRIVATE_DIRECTORY_MODE });
@@ -17534,6 +17718,7 @@ class BridgeSessionStore {
17534
17718
  migrationFailed = true;
17535
17719
  this.warnPersistenceFailure(error);
17536
17720
  });
17721
+ await this.adoptLoginRecords().catch((error) => this.warnPersistenceFailure(error));
17537
17722
  let names;
17538
17723
  try {
17539
17724
  await this.ensurePrivateStorage();
@@ -17542,7 +17727,7 @@ class BridgeSessionStore {
17542
17727
  return legacy;
17543
17728
  }
17544
17729
  const records = await Promise.all(names.map(async (name) => {
17545
- const path = join11(this.recordsDir(), name);
17730
+ const path = join13(this.recordsDir(), name);
17546
17731
  if (name.endsWith(".tmp")) {
17547
17732
  await chmod(path, PRIVATE_FILE_MODE).catch(() => {
17548
17733
  return;
@@ -17625,7 +17810,7 @@ class BridgeSessionStore {
17625
17810
  await this.ensurePrivateStorage();
17626
17811
  await Promise.all(sessions.map(async (session) => {
17627
17812
  const target = this.recordPath(session.bridgeSessionId);
17628
- const temporary = `${target}.${process.pid}.${randomUUID5()}.tmp`;
17813
+ const temporary = `${target}.${process.pid}.${randomUUID6()}.tmp`;
17629
17814
  await writeFile4(temporary, `${JSON.stringify(session, null, 2)}
17630
17815
  `, {
17631
17816
  encoding: "utf8",
@@ -17645,13 +17830,43 @@ class BridgeSessionStore {
17645
17830
  return;
17646
17831
  });
17647
17832
  }
17833
+ async adoptLoginRecords() {
17834
+ const source = this.loginRecordsDir();
17835
+ if (!source)
17836
+ return;
17837
+ const names = (await readdir2(source).catch(() => [])).filter((name) => name.endsWith(".json"));
17838
+ if (names.length === 0)
17839
+ return;
17840
+ await this.ensurePrivateStorage();
17841
+ await Promise.all(names.map(async (name) => {
17842
+ const from = join13(source, name);
17843
+ const target = join13(this.recordsDir(), name);
17844
+ const temporary = `${target}.${process.pid}.${randomUUID6()}.tmp`;
17845
+ await writeFile4(temporary, await readFile6(from), { mode: PRIVATE_FILE_MODE });
17846
+ await chmod(temporary, PRIVATE_FILE_MODE);
17847
+ try {
17848
+ await link2(temporary, target).catch((error) => {
17849
+ if (error.code !== "EEXIST")
17850
+ throw error;
17851
+ });
17852
+ } finally {
17853
+ await rm6(temporary, { force: true }).catch(() => {
17854
+ return;
17855
+ });
17856
+ }
17857
+ await chmod(target, PRIVATE_FILE_MODE);
17858
+ await rm6(from, { force: true }).catch(() => {
17859
+ return;
17860
+ });
17861
+ }));
17862
+ }
17648
17863
  async writeRecordAtomic(session) {
17649
17864
  await this.ensurePrivateStorage();
17650
17865
  await this.writeAtomicPath(this.recordPath(session.bridgeSessionId), `${JSON.stringify(session, null, 2)}
17651
17866
  `);
17652
17867
  }
17653
17868
  async writeAtomicPath(path, payload) {
17654
- const temporary = `${path}.${process.pid}.${randomUUID5()}.tmp`;
17869
+ const temporary = `${path}.${process.pid}.${randomUUID6()}.tmp`;
17655
17870
  await writeFile4(temporary, payload, {
17656
17871
  encoding: "utf8",
17657
17872
  mode: PRIVATE_FILE_MODE
@@ -17743,6 +17958,9 @@ function isPersistedRecord(value) {
17743
17958
  }
17744
17959
  return (record.threadId === null || record.threadId.length > 0) && record.turnId === undefined && record.terminalStatus === undefined;
17745
17960
  }
17961
+ function sameDispatchIdentity(left, right) {
17962
+ return left.bridgeSessionId === right.bridgeSessionId && left.threadId === right.threadId && left.turnId === right.turnId && left.state === right.state && left.terminalStatus === right.terminalStatus && left.quarantined === right.quarantined;
17963
+ }
17746
17964
 
17747
17965
  class DispatchJournal {
17748
17966
  records = new Map;
@@ -17768,7 +17986,7 @@ class DispatchJournal {
17768
17986
  this.persist = options.persist !== false;
17769
17987
  }
17770
17988
  path() {
17771
- return join12(this.codexHome, "orkestrator-bridge", `dispatch-journal-${this.cwdHash}.json`);
17989
+ return join14(codexBridgeStateDir(this.codexHome), `dispatch-journal-${this.cwdHash}.json`);
17772
17990
  }
17773
17991
  async load() {
17774
17992
  if (this.loaded)
@@ -17776,46 +17994,71 @@ class DispatchJournal {
17776
17994
  this.loaded = true;
17777
17995
  if (!this.persist)
17778
17996
  return;
17997
+ const legacy = join14(codexLoginBridgeDir(this.codexHome), `dispatch-journal-${this.cwdHash}.json`);
17779
17998
  try {
17780
- const parsed = JSON.parse(await this.readBounded());
17781
- if (parsed.version !== DISPATCH_JOURNAL_VERSION || !Array.isArray(parsed.records)) {
17782
- this.integrityError = "Dispatch journal has an unsupported or malformed file shape";
17783
- return;
17784
- }
17785
- for (const persisted of parsed.records) {
17786
- const candidate = persisted && typeof persisted === "object" ? persisted : undefined;
17787
- const requestId = typeof candidate?.requestId === "string" ? candidate.requestId : "";
17788
- if (!requestId)
17789
- continue;
17790
- const sequence = Number.isSafeInteger(candidate?.sequence) && Number(candidate?.sequence) > 0 ? Number(candidate.sequence) : this.nextSequence + 1;
17791
- this.nextSequence = Math.max(this.nextSequence, sequence);
17792
- const timestamp = new Date(this.now()).toISOString();
17793
- const record = isPersistedRecord(persisted) ? { ...persisted, sequence } : {
17794
- requestId,
17795
- bridgeSessionId: typeof candidate?.bridgeSessionId === "string" ? candidate.bridgeSessionId : "",
17796
- threadId: typeof candidate?.threadId === "string" || candidate?.threadId === null ? candidate.threadId : null,
17797
- state: "terminal",
17798
- terminalStatus: "failed",
17799
- quarantined: true,
17800
- createdAt: timestamp,
17801
- updatedAt: timestamp,
17802
- sequence
17803
- };
17804
- const existing = this.records.get(requestId);
17805
- if (!existing || compareDispatchRecordsNewestFirst(record, existing) < 0) {
17806
- this.records.set(requestId, record);
17807
- }
17999
+ await this.loadPath(this.path());
18000
+ if (legacy !== this.path() && await this.loadPath(legacy, true)) {
18001
+ this.collectGarbage();
18002
+ this.refreshCapacityError();
18003
+ if (this.capacityError)
18004
+ throw new DispatchJournalCapacityError(this.capacityError);
18005
+ await this.flush({ failClosed: true });
18006
+ await rm7(legacy);
17808
18007
  }
17809
18008
  this.collectGarbage();
17810
18009
  this.refreshCapacityError();
17811
18010
  } catch (error) {
17812
- if (error?.code === "ENOENT")
17813
- return;
17814
18011
  this.integrityError = error instanceof DispatchJournalIntegrityError ? error.message : "Dispatch journal could not be safely decoded";
17815
18012
  }
17816
18013
  }
17817
- async readBounded() {
17818
- const handle = await open4(this.path(), "r");
18014
+ async loadPath(path, merging = false) {
18015
+ let contents;
18016
+ try {
18017
+ contents = await this.readBounded(path);
18018
+ } catch (error) {
18019
+ if (error?.code === "ENOENT")
18020
+ return false;
18021
+ throw error;
18022
+ }
18023
+ const parsed = JSON.parse(contents);
18024
+ if (parsed.version !== DISPATCH_JOURNAL_VERSION || !Array.isArray(parsed.records)) {
18025
+ throw new DispatchJournalIntegrityError("Dispatch journal has an unsupported or malformed file shape");
18026
+ }
18027
+ for (const persisted of parsed.records) {
18028
+ const candidate = persisted && typeof persisted === "object" ? persisted : undefined;
18029
+ const requestId = typeof candidate?.requestId === "string" ? candidate.requestId : "";
18030
+ if (!requestId)
18031
+ continue;
18032
+ const sequence = Number.isSafeInteger(candidate?.sequence) && Number(candidate?.sequence) > 0 ? Number(candidate.sequence) : this.nextSequence + 1;
18033
+ this.nextSequence = Math.max(this.nextSequence, sequence);
18034
+ const timestamp = new Date(this.now()).toISOString();
18035
+ const record = isPersistedRecord(persisted) ? { ...persisted, sequence } : {
18036
+ requestId,
18037
+ bridgeSessionId: typeof candidate?.bridgeSessionId === "string" ? candidate.bridgeSessionId : "",
18038
+ threadId: typeof candidate?.threadId === "string" || candidate?.threadId === null ? candidate.threadId : null,
18039
+ state: "terminal",
18040
+ terminalStatus: "failed",
18041
+ quarantined: true,
18042
+ createdAt: timestamp,
18043
+ updatedAt: timestamp,
18044
+ sequence
18045
+ };
18046
+ const existing = this.records.get(requestId);
18047
+ if (merging && existing && !sameDispatchIdentity(existing, record)) {
18048
+ this.records.set(requestId, {
18049
+ ...existing,
18050
+ state: "terminal",
18051
+ terminalStatus: "failed",
18052
+ quarantined: true
18053
+ });
18054
+ } else if (!existing || !merging && compareDispatchRecordsNewestFirst(record, existing) < 0) {
18055
+ this.records.set(requestId, record);
18056
+ }
18057
+ }
18058
+ return true;
18059
+ }
18060
+ async readBounded(path) {
18061
+ const handle = await open4(path, "r");
17819
18062
  try {
17820
18063
  const metadata = await handle.stat();
17821
18064
  if (!metadata.isFile() || metadata.size > this.maxBytes) {
@@ -17839,6 +18082,9 @@ class DispatchJournal {
17839
18082
  }
17840
18083
  classify(requestId) {
17841
18084
  const record = this.records.get(requestId);
18085
+ if (this.integrityError) {
18086
+ return { duplicate: Boolean(record), action: "blocked", reason: this.integrityError, record };
18087
+ }
17842
18088
  if (!record) {
17843
18089
  const blockedReason = this.integrityError ?? this.capacityError;
17844
18090
  if (blockedReason) {
@@ -17950,6 +18196,13 @@ class DispatchJournal {
17950
18196
  latestForSession(bridgeSessionId) {
17951
18197
  return [...this.records.values()].filter((record) => record.bridgeSessionId === bridgeSessionId).sort(compareDispatchRecordsNewestFirst)[0];
17952
18198
  }
18199
+ requestIdForTurn(turnId) {
18200
+ for (const record of this.records.values()) {
18201
+ if (record.turnId === turnId && !record.quarantined)
18202
+ return record.requestId;
18203
+ }
18204
+ return;
18205
+ }
17953
18206
  async forget(requestId) {
17954
18207
  if (this.integrityError)
17955
18208
  throw new DispatchJournalIntegrityError(this.integrityError);
@@ -18045,7 +18298,7 @@ class DispatchJournal {
18045
18298
  const attempt = this.writeChain.then(async () => {
18046
18299
  try {
18047
18300
  const path = this.path();
18048
- await mkdir7(join12(this.codexHome, "orkestrator-bridge"), { recursive: true });
18301
+ await mkdir7(codexBridgeStateDir(this.codexHome), { recursive: true });
18049
18302
  const temporary = `${path}.${process.pid}.${Math.random().toString(36).slice(2, 8)}.tmp`;
18050
18303
  await writeFile5(temporary, `${JSON.stringify(snapshot, null, 2)}
18051
18304
  `, "utf8");
@@ -18173,7 +18426,7 @@ function coordinatorCapabilityPolicy(mode) {
18173
18426
 
18174
18427
  // ../../bridges/codex-bridge/src/codex-config.ts
18175
18428
  import { realpathSync } from "node:fs";
18176
- import { isAbsolute as isAbsolute6 } from "node:path";
18429
+ import { isAbsolute as isAbsolute7 } from "node:path";
18177
18430
  var CODEX_MAX_CONCURRENT_THREADS_ENV = "CODEX_MAX_CONCURRENT_THREADS_PER_SESSION";
18178
18431
  var DEFAULT_CODEX_MAX_CONCURRENT_THREADS = 5;
18179
18432
  var ORKESTRATOR_AGENT_MCP_URL_ENV = "ORKESTRATOR_AGENT_MCP_URL";
@@ -18193,7 +18446,7 @@ function codexNetworkDeniedConfig(policy) {
18193
18446
  };
18194
18447
  }
18195
18448
  function codexProjectTrustConfig(policy, cwd) {
18196
- if (!policy || policy.projectResources || !cwd || !isAbsolute6(cwd))
18449
+ if (!policy || policy.projectResources || !cwd || !isAbsolute7(cwd))
18197
18450
  return {};
18198
18451
  const roots = new Set([cwd]);
18199
18452
  try {
@@ -18221,7 +18474,7 @@ function codexProjectSkillsConfig(listed) {
18221
18474
  const { path, scope, enabled } = skill ?? {};
18222
18475
  if (enabled === true && typeof scope === "string" && KEPT_SKILL_SCOPES.has(scope))
18223
18476
  continue;
18224
- if (typeof path !== "string" || !isAbsolute6(path)) {
18477
+ if (typeof path !== "string" || !isAbsolute7(path)) {
18225
18478
  throw new Error("Codex listed a project skill without a path it can be disabled by");
18226
18479
  }
18227
18480
  disabled.add(path);
@@ -18306,11 +18559,11 @@ function codexAppServerConfigOverrides(env = process.env) {
18306
18559
  const readableRuntimeRoot = env.CODEX_BRIDGE_READABLE_RUNTIME_ROOT?.trim();
18307
18560
  const projectRoot = env.CWD?.trim();
18308
18561
  const attachmentRoot = env[BRIDGE_ATTACHMENT_ROOT_ENV]?.trim();
18309
- if (!permissionProfile || !/^[A-Za-z0-9_-]+$/.test(permissionProfile) || !readableRuntimeRoot || !isAbsolute6(readableRuntimeRoot) || !projectRoot || !isAbsolute6(projectRoot)) {
18562
+ if (!permissionProfile || !/^[A-Za-z0-9_-]+$/.test(permissionProfile) || !readableRuntimeRoot || !isAbsolute7(readableRuntimeRoot) || !projectRoot || !isAbsolute7(projectRoot)) {
18310
18563
  throw new Error("Coordinator Codex permission profile configuration is invalid");
18311
18564
  }
18312
18565
  overrides.default_permissions = JSON.stringify(permissionProfile);
18313
- overrides[`permissions.${permissionProfile}.filesystem`] = `{ ":root" = "deny", ":minimal" = "read", ":tmpdir" = "deny", ` + `":slash_tmp" = "deny", ${JSON.stringify(readableRuntimeRoot)} = "read", ` + (attachmentRoot && isAbsolute6(attachmentRoot) ? `${JSON.stringify(attachmentRoot)} = "read", ` : "") + `":workspace_roots" = { "." = "read" } }`;
18566
+ overrides[`permissions.${permissionProfile}.filesystem`] = `{ ":root" = "deny", ":minimal" = "read", ":tmpdir" = "deny", ` + `":slash_tmp" = "deny", ${JSON.stringify(readableRuntimeRoot)} = "read", ` + (attachmentRoot && isAbsolute7(attachmentRoot) ? `${JSON.stringify(attachmentRoot)} = "read", ` : "") + `":workspace_roots" = { "." = "read" } }`;
18314
18567
  overrides[`permissions.${permissionProfile}.network.enabled`] = "false";
18315
18568
  overrides.web_search = JSON.stringify(codexNetworkDeniedSearchMode(codexCoordinatorPolicy(env)));
18316
18569
  overrides["features.standalone_web_search"] = "false";
@@ -18896,7 +19149,7 @@ function signedOutAccountRead(error) {
18896
19149
  }
18897
19150
  async function readStoredLoginFingerprint(codexHome) {
18898
19151
  try {
18899
- const file = await open6(join13(codexHome, "auth.json"), "r");
19152
+ const file = await open6(join15(codexHome, "auth.json"), "r");
18900
19153
  let raw;
18901
19154
  try {
18902
19155
  const bytes = Buffer.alloc(65537);
@@ -19174,6 +19427,9 @@ class AppServerEngine {
19174
19427
  this.invalidateAccountServing(sequence);
19175
19428
  this.checkAccountSignIn(true);
19176
19429
  }
19430
+ if (notification.method === "account/login/completed" && objectRecord(notification.params).success === true) {
19431
+ this.emit({ kind: "account.signedIn", engineGeneration: generation });
19432
+ }
19177
19433
  const result = reduceNotification(notification, generation);
19178
19434
  if (result.unknownMethod) {
19179
19435
  this.unknownNotificationCount += 1;
@@ -19240,7 +19496,11 @@ class AppServerEngine {
19240
19496
  if (sequence < this.accountAppliedSequence || generation !== this.supervisor.getGeneration())
19241
19497
  return;
19242
19498
  this.accountAppliedSequence = sequence;
19499
+ const wasSignInRequired = this.accountSignInRequired;
19243
19500
  const signedIn = this.recordAccount(response);
19501
+ if (signedIn && wasSignInRequired) {
19502
+ this.emit({ kind: "account.signedIn", engineGeneration: generation });
19503
+ }
19244
19504
  if (!signedIn) {
19245
19505
  await this.requestAccountReloadFromStoredLogin(sequence, generation);
19246
19506
  return;
@@ -19853,7 +20113,7 @@ class AppServerEngine {
19853
20113
  thread.model = confirmedModel.trim();
19854
20114
  }
19855
20115
  if (thread.id) {
19856
- if (typeof raw.path === "string" && isAbsolute7(raw.path)) {
20116
+ if (typeof raw.path === "string" && isAbsolute8(raw.path)) {
19857
20117
  this.rolloutPaths.set(thread.id, raw.path);
19858
20118
  }
19859
20119
  this.bindings.set(thread.id, {
@@ -20459,7 +20719,7 @@ class ThreadRegistry {
20459
20719
 
20460
20720
  // ../../bridges/codex-bridge/src/app-server-runtime-sessions.ts
20461
20721
  var SESSION_CLOSING_ERROR = "Session is closing";
20462
- var ACTIVITY_PROCESS = randomUUID6();
20722
+ var ACTIVITY_PROCESS = randomUUID7();
20463
20723
 
20464
20724
  class CodexRuntimeSessions {
20465
20725
  ctx;
@@ -21266,6 +21526,7 @@ class CodexRuntimeSessions {
21266
21526
  const awaitingRecovery = session.threadId !== null && this.ctx.threadsAwaitingDispatchRecovery.has(session.threadId);
21267
21527
  const phase = context?.phase ?? (awaitingRecovery ? "recovering" : session.restartFailure ? "failed" : "idle");
21268
21528
  const latestDispatch = this.ctx.journal.latestForSession(sessionId);
21529
+ const interruptedRequestId = session.restartFailure ? this.ctx.journal.requestIdForTurn(session.restartFailure.turnId) : undefined;
21269
21530
  const error = context?.error ?? (phase === "failed" && session.restartFailure ? CODEX_RESTARTED_MID_TURN_MESSAGE : undefined);
21270
21531
  return {
21271
21532
  status: phaseToExternalStatus(phase),
@@ -21273,7 +21534,10 @@ class CodexRuntimeSessions {
21273
21534
  phase,
21274
21535
  title: session.title,
21275
21536
  error,
21276
- ...phase === "failed" && session.restartFailure && error === CODEX_RESTARTED_MID_TURN_MESSAGE ? { interruption: "restart" } : {},
21537
+ ...phase === "failed" && session.restartFailure && error === CODEX_RESTARTED_MID_TURN_MESSAGE ? {
21538
+ interruption: "restart",
21539
+ ...interruptedRequestId ? { interruptedRequestId } : {}
21540
+ } : {},
21277
21541
  threadId: session.threadId,
21278
21542
  turnId: context?.activeTurn?.turnId,
21279
21543
  turnStartedAt: context?.turnStartedAt,
@@ -21814,7 +22078,7 @@ function parseCommandToken(text, sigils = ["/"]) {
21814
22078
  // ../../bridges/codex-bridge/src/prompts/template-discovery.ts
21815
22079
  import { createHash as createHash11 } from "node:crypto";
21816
22080
  import { open as open7, readdir as readdir3, stat as stat7 } from "node:fs/promises";
21817
- import { join as join14 } from "node:path";
22081
+ import { join as join16 } from "node:path";
21818
22082
 
21819
22083
  // ../../bridges/codex-bridge/src/prompts/template-format.ts
21820
22084
  var TEMPLATE_DESCRIPTION_MAX_BYTES = 1000;
@@ -21942,8 +22206,8 @@ var RESERVED_TEMPLATE_PREFIXES = ["/skill:", "/prompts:"];
21942
22206
  var TEMPLATE_ID_PREFIX = "codex-template:";
21943
22207
  function templateRoots(cwd, codexHome = getCodexHomeDir()) {
21944
22208
  return [
21945
- { origin: "project", dir: join14(cwd, ".codex", "prompts") },
21946
- { origin: "user", dir: join14(codexHome, "prompts") }
22209
+ { origin: "project", dir: join16(cwd, ".codex", "prompts") },
22210
+ { origin: "user", dir: join16(codexHome, "prompts") }
21947
22211
  ];
21948
22212
  }
21949
22213
  function templateCommandId(origin, relativePath) {
@@ -21967,7 +22231,7 @@ function isReservedName(name) {
21967
22231
  }
21968
22232
  var metadataCache = new Map;
21969
22233
  var METADATA_CACHE_MAX = 1024;
21970
- async function readBounded(absolutePath, maxBytes) {
22234
+ async function readBounded2(absolutePath, maxBytes) {
21971
22235
  const handle = await open7(absolutePath, "r");
21972
22236
  try {
21973
22237
  const info = await handle.stat();
@@ -22048,7 +22312,7 @@ async function scanPromptTemplates(roots, limits = TEMPLATE_LIMITS) {
22048
22312
  return;
22049
22313
  }
22050
22314
  visited += 1;
22051
- const absolutePath = join14(dir, entry.name);
22315
+ const absolutePath = join16(dir, entry.name);
22052
22316
  const relativePath = relative ? `${relative}/${entry.name}` : entry.name;
22053
22317
  if (entry.isDirectory()) {
22054
22318
  if (depth + 1 > limits.maxDepth) {
@@ -22110,7 +22374,7 @@ async function scanPromptTemplates(roots, limits = TEMPLATE_LIMITS) {
22110
22374
  if (candidate.size > limits.maxTemplateBytes)
22111
22375
  return null;
22112
22376
  try {
22113
- const { bytes, oversized } = await readBounded(candidate.absolutePath, limits.maxTemplateBytes);
22377
+ const { bytes, oversized } = await readBounded2(candidate.absolutePath, limits.maxTemplateBytes);
22114
22378
  if (oversized)
22115
22379
  return null;
22116
22380
  if (bytes.length === 0)
@@ -22187,7 +22451,7 @@ async function loadTemplateBody(entry, limits = TEMPLATE_LIMITS) {
22187
22451
  }
22188
22452
  let loaded;
22189
22453
  try {
22190
- loaded = await readBounded(entry.absolutePath, limits.maxTemplateBytes);
22454
+ loaded = await readBounded2(entry.absolutePath, limits.maxTemplateBytes);
22191
22455
  } catch {
22192
22456
  return {
22193
22457
  ok: false,
@@ -22215,7 +22479,7 @@ async function loadTemplateBody(entry, limits = TEMPLATE_LIMITS) {
22215
22479
  }
22216
22480
 
22217
22481
  // ../../bridges/codex-bridge/src/commands/skill-inventory.ts
22218
- import { isAbsolute as isAbsolute8 } from "node:path";
22482
+ import { isAbsolute as isAbsolute9 } from "node:path";
22219
22483
  var SKILL_INVENTORY_LIMITS = Object.freeze({
22220
22484
  maxSkills: 512,
22221
22485
  maxDescriptionBytes: 1000,
@@ -22267,7 +22531,7 @@ function normalizeSkillsList(raw, context) {
22267
22531
  }
22268
22532
  const name = nonEmpty(candidate.name);
22269
22533
  const path = typeof candidate.path === "string" ? candidate.path : undefined;
22270
- if (!name || !usableSkillName(name) || !path || !isAbsolute8(path)) {
22534
+ if (!name || !usableSkillName(name) || !path || !isAbsolute9(path)) {
22271
22535
  truncated = true;
22272
22536
  continue;
22273
22537
  }
@@ -23646,6 +23910,12 @@ class CodexRuntimeLifecycle {
23646
23910
  }
23647
23911
  if (event.kind === "unknown.protocol")
23648
23912
  return;
23913
+ if (event.kind === "account.signedIn") {
23914
+ for (const context of this.ctx.registry.listThreads()) {
23915
+ this.setThreadAuthNotice(context, undefined);
23916
+ }
23917
+ return;
23918
+ }
23649
23919
  if (event.kind === "account.reload.requested") {
23650
23920
  this.ctx.callbacks.reloadStoredAccountLogin().catch((error) => {
23651
23921
  console.warn("[codex-bridge] Reloading the stored account login failed:", error instanceof Error ? error.message : error);
@@ -23931,8 +24201,11 @@ class CodexRuntimeLifecycle {
23931
24201
  this.discardOutlivingCommandWindows(turn);
23932
24202
  if (turn.stoppedByShutdown && event.status === "interrupted")
23933
24203
  this.completeRecoveredTurn(turn, event.status);
23934
- else
24204
+ else {
23935
24205
  turn.complete(event.status, event.error);
24206
+ if (event.status === "failed" && event.error)
24207
+ turn.recordProviderFailure(event.error);
24208
+ }
23936
24209
  this.ctx.options.changeEvidence?.turn(this.evidenceSource(context, turn), event.status);
23937
24210
  const finalize = () => this.runFinalization(context, turn).catch((error) => {
23938
24211
  console.error(`[codex-bridge] Failed to finalize turn ${turn.turnId}:`, error instanceof Error ? error.message : error);
@@ -26749,7 +27022,7 @@ async function writeCodexRawLog(sessionId, entry, logDir = codexRawLogDir) {
26749
27022
  try {
26750
27023
  await mkdir8(logDir, { recursive: true });
26751
27024
  const filename = `${sanitizeLogFileComponent(sessionId)}.jsonl`;
26752
- await appendFile5(join15(logDir, filename), `${JSON.stringify({
27025
+ await appendFile5(join17(logDir, filename), `${JSON.stringify({
26753
27026
  timestamp: new Date().toISOString(),
26754
27027
  sessionId,
26755
27028
  ...entry
@@ -26946,10 +27219,10 @@ function emit(event) {
26946
27219
  }
26947
27220
  }
26948
27221
  function bridgeCacheDir() {
26949
- return join15(getCodexHomeDir(), "orkestrator-bridge");
27222
+ return join17(getCodexHomeDir(), "orkestrator-bridge");
26950
27223
  }
26951
27224
  function bridgeCachePath() {
26952
- return join15(bridgeCacheDir(), "models-cache.json");
27225
+ return join17(bridgeCacheDir(), "models-cache.json");
26953
27226
  }
26954
27227
  var BRIDGE_MODEL_CACHE_VERSION = 2;
26955
27228
  async function readPersistedBridgeCache() {
@@ -26973,7 +27246,7 @@ async function writePersistedBridgeCache(models) {
26973
27246
  }
26974
27247
  async function readCodexCliModelCache() {
26975
27248
  try {
26976
- const raw = await readFile7(join15(getCodexHomeDir(), "models_cache.json"), "utf8");
27249
+ const raw = await readFile7(join17(getCodexHomeDir(), "models_cache.json"), "utf8");
26977
27250
  const models = parseModelCatalog(raw);
26978
27251
  return models.length > 0 ? models : null;
26979
27252
  } catch {
@@ -27406,7 +27679,10 @@ app.get("/session/:id/dispatch", (c) => {
27406
27679
  return c.json({ dispatch: "unknown" });
27407
27680
  const record = appServerRuntime.getJournal().get(requestId);
27408
27681
  const dispatched = record?.bridgeSessionId === c.req.param("id") && (record.state === "accepted" || record.state === "terminal");
27409
- return c.json({ dispatch: dispatched ? "dispatched" : "unknown" });
27682
+ return c.json({
27683
+ dispatch: dispatched ? "dispatched" : "unknown",
27684
+ ...dispatched ? { pending: record.state === "accepted" } : {}
27685
+ });
27410
27686
  });
27411
27687
  app.get("/session/:id/structured-output", (c) => {
27412
27688
  const result = appServerRuntime.getStructuredOutput(c.req.param("id"), c.req.query("requestId")?.trim());