orkestrator 2.29.0-beta → 2.30.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.
@@ -4161,7 +4161,8 @@ var NATIVE_AGENT_COMMAND_CATALOGUE_ERROR_CODES = [
4161
4161
  "rejected",
4162
4162
  "malformed",
4163
4163
  "too-large",
4164
- "provider-error"
4164
+ "provider-error",
4165
+ "outdated-bridge"
4165
4166
  ];
4166
4167
  var DEFAULT_NATIVE_AGENT_LIVE_WINDOW = {
4167
4168
  messages: 100,
@@ -10805,18 +10806,11 @@ async function deriveTranscriptSubagentPartsForTurn({
10805
10806
  }
10806
10807
 
10807
10808
  // ../../bridges/codex-bridge/src/history/rollout.ts
10809
+ import { createHash as createHash7 } from "node:crypto";
10808
10810
  import { readFile as readFile4, readdir, stat as stat4 } from "node:fs/promises";
10809
10811
  import { homedir } from "node:os";
10810
10812
  import { basename, join as join8 } from "node:path";
10811
10813
 
10812
- // ../../bridges/codex-bridge/src/messages/types.ts
10813
- function createMessageId() {
10814
- return `msg-${crypto.randomUUID()}`;
10815
- }
10816
- function createSessionId() {
10817
- return `session-${crypto.randomUUID()}`;
10818
- }
10819
-
10820
10814
  // ../../bridges/codex-bridge/src/sessions/refused-cell-tail.ts
10821
10815
  import { open as open2, stat as stat3 } from "node:fs/promises";
10822
10816
  var STRONG_REFUSAL_MARKERS = [
@@ -11415,6 +11409,7 @@ class TurnAccumulator {
11415
11409
  turnId;
11416
11410
  phase = "starting";
11417
11411
  cancelRequested = false;
11412
+ stoppedByShutdown = false;
11418
11413
  error;
11419
11414
  finalDiff;
11420
11415
  completedAt;
@@ -11427,9 +11422,15 @@ class TurnAccumulator {
11427
11422
  changedFrozenRows = new Set;
11428
11423
  steerFloor = 0;
11429
11424
  modelToolCalled = false;
11425
+ pendingResponseBoundaries = 0;
11430
11426
  noteModelToolCall() {
11431
11427
  this.modelToolCalled = true;
11432
11428
  }
11429
+ noteModelResponseCompleted() {
11430
+ if (this.isTerminal())
11431
+ return;
11432
+ this.pendingResponseBoundaries += 1;
11433
+ }
11433
11434
  clientToolItemStartedBetween(fromMs, toMs) {
11434
11435
  for (const accumulator of this.items.values()) {
11435
11436
  const item = accumulator.item;
@@ -11767,6 +11768,7 @@ class TurnAccumulator {
11767
11768
  startAssistantSegment(assistantMessageId, boundary, startedAt = new Date().toISOString()) {
11768
11769
  this.assistantMessageId = assistantMessageId;
11769
11770
  this.modelToolCalled = false;
11771
+ this.pendingResponseBoundaries = Math.max(0, this.pendingResponseBoundaries - 1);
11770
11772
  const segment = {
11771
11773
  assistantMessageId,
11772
11774
  startIndex: boundary,
@@ -11779,11 +11781,13 @@ class TurnAccumulator {
11779
11781
  opensModelStep(event) {
11780
11782
  let itemId;
11781
11783
  let type;
11784
+ let toolCall = false;
11782
11785
  switch (event.kind) {
11783
11786
  case "item.started":
11784
11787
  case "item.completed":
11785
11788
  itemId = event.item.id;
11786
11789
  type = event.item.type;
11790
+ toolCall = isClientToolCall(event.item) && !isCodeModeCell(event.item);
11787
11791
  break;
11788
11792
  case "item.text.delta":
11789
11793
  itemId = event.itemId;
@@ -11796,7 +11800,8 @@ class TurnAccumulator {
11796
11800
  default:
11797
11801
  return false;
11798
11802
  }
11799
- if (type !== "reasoning" && type !== "agent_message")
11803
+ const opensResponse = type === "reasoning" || type === "agent_message";
11804
+ if (!opensResponse && !(toolCall && this.pendingResponseBoundaries > 0))
11800
11805
  return false;
11801
11806
  if (this.items.has(itemId) || this.isTerminal())
11802
11807
  return false;
@@ -12975,6 +12980,10 @@ var NON_TOOL_TURN_ITEM_TYPES = new Set([
12975
12980
  "Plan"
12976
12981
  ]);
12977
12982
  var MAX_OPEN_CODE_MODE_CELLS = 256;
12983
+ function replayedRowId(threadId, scope, kind, ordinal) {
12984
+ const digest = createHash7("sha256").update(`${threadId}\x00${scope}\x00${kind}\x00${ordinal}`).digest("hex");
12985
+ return `msg-r-${digest.slice(0, 32)}`;
12986
+ }
12978
12987
  async function replayPersistedRollout(threadId, snapshot, meta, structuredTurns) {
12979
12988
  const finalAssistantRecordByTurn = new Map;
12980
12989
  if (structuredTurns.size > 0) {
@@ -13002,19 +13011,29 @@ async function replayPersistedRollout(threadId, snapshot, meta, structuredTurns)
13002
13011
  let currentTurnModel;
13003
13012
  let currentAssistantMessage;
13004
13013
  let currentRowCalledTool = false;
13014
+ let responseCompletedInRow = false;
13005
13015
  let nextRowStartedAt;
13006
13016
  let lastTimestamp;
13007
13017
  let unreadableRun;
13018
+ const rowOrdinals = new Map;
13019
+ const nextRowId = (kind) => {
13020
+ const scope = currentTurnId ?? "";
13021
+ const key = `${kind}\x00${scope}`;
13022
+ const ordinal = rowOrdinals.get(key) ?? 0;
13023
+ rowOrdinals.set(key, ordinal + 1);
13024
+ return replayedRowId(threadId, scope, kind, ordinal);
13025
+ };
13008
13026
  const closeAssistantRow = () => {
13009
13027
  currentAssistantMessage = undefined;
13010
13028
  currentRowCalledTool = false;
13029
+ responseCompletedInRow = false;
13011
13030
  nextRowStartedAt = undefined;
13012
13031
  };
13013
13032
  const ensureAssistantMessage = (timestamp) => {
13014
13033
  if (currentAssistantMessage)
13015
13034
  return currentAssistantMessage;
13016
13035
  currentAssistantMessage = {
13017
- id: createMessageId(),
13036
+ id: nextRowId("assistant"),
13018
13037
  role: "assistant",
13019
13038
  content: "",
13020
13039
  parts: [],
@@ -13161,7 +13180,7 @@ async function replayPersistedRollout(threadId, snapshot, meta, structuredTurns)
13161
13180
  } else {
13162
13181
  unreadableRun = {
13163
13182
  message: {
13164
- id: createMessageId(),
13183
+ id: replayedRowId(threadId, currentTurnId ?? "", "unreadable", unreadable.offset),
13165
13184
  role: "assistant",
13166
13185
  content: "",
13167
13186
  parts: [],
@@ -13200,11 +13219,16 @@ async function replayPersistedRollout(threadId, snapshot, meta, structuredTurns)
13200
13219
  visitCompletedItem(record.payload.item, timestamp);
13201
13220
  return;
13202
13221
  }
13222
+ if (record.type === "token_usage_record" || record.type === "event_msg" && record.payload?.type === "token_count") {
13223
+ if (currentRowCalledTool)
13224
+ responseCompletedInRow = true;
13225
+ return;
13226
+ }
13203
13227
  if (record.type !== "response_item" || !record.payload) {
13204
13228
  return;
13205
13229
  }
13206
13230
  const payload = record.payload;
13207
- if (currentRowCalledTool && (payload.type === "reasoning" || payload.type === "message" && payload.role === "assistant")) {
13231
+ if (currentRowCalledTool && (payload.type === "reasoning" || payload.type === "message" && payload.role === "assistant" || responseCompletedInRow && ROLLOUT_TOOL_CALL_TYPES.has(String(payload.type)))) {
13208
13232
  closeAssistantRow();
13209
13233
  nextRowStartedAt = timestamp;
13210
13234
  }
@@ -13273,7 +13297,7 @@ async function replayPersistedRollout(threadId, snapshot, meta, structuredTurns)
13273
13297
  }
13274
13298
  closeAssistantRow();
13275
13299
  messages.push({
13276
- id: createMessageId(),
13300
+ id: nextRowId("user"),
13277
13301
  role,
13278
13302
  content: text,
13279
13303
  parts: [...text ? [{ type: "text", content: text }] : [], ...attachments],
@@ -13798,6 +13822,14 @@ async function renderTurn(turn, options) {
13798
13822
  };
13799
13823
  }
13800
13824
 
13825
+ // ../../bridges/codex-bridge/src/messages/types.ts
13826
+ function createMessageId() {
13827
+ return `msg-${crypto.randomUUID()}`;
13828
+ }
13829
+ function createSessionId() {
13830
+ return `session-${crypto.randomUUID()}`;
13831
+ }
13832
+
13801
13833
  // ../../bridges/codex-bridge/src/app-server-runtime-rows.ts
13802
13834
  function patchCommandChangePart(part, itemId, change) {
13803
13835
  if (part.type === "tool-invocation" && part.toolUseId === itemId) {
@@ -14076,7 +14108,7 @@ async function publishAssistantRows(context, state, turn, options) {
14076
14108
  }
14077
14109
 
14078
14110
  // ../../bridges/codex-bridge/src/app-server-runtime-sessions.ts
14079
- import { createHash as createHash9, randomUUID as randomUUID6 } from "node:crypto";
14111
+ import { createHash as createHash10, randomUUID as randomUUID6 } from "node:crypto";
14080
14112
 
14081
14113
  // ../protocol/src/agent-mail.ts
14082
14114
  var AGENT_MAIL_MAX_BODY_BYTES = 32 * 1024;
@@ -14140,7 +14172,7 @@ var ORKESTRATOR_ARTIFACT_MCP_PATH = "/artifact-mcp";
14140
14172
 
14141
14173
  // ../../bridges/codex-bridge/src/engine/app-server-engine.ts
14142
14174
  import { open as open6 } from "node:fs/promises";
14143
- import { createHash as createHash8 } from "node:crypto";
14175
+ import { createHash as createHash9 } from "node:crypto";
14144
14176
  import { isAbsolute as isAbsolute7, join as join13 } from "node:path";
14145
14177
 
14146
14178
  // ../../bridges/codex-bridge/src/app-server/process-supervisor.ts
@@ -16780,8 +16812,6 @@ var IGNORED_METHODS = new Set([
16780
16812
  "autoApprovalReview/strictReviewRequired",
16781
16813
  "mcpServer/event/stream/notification",
16782
16814
  "thread/reverted",
16783
- "modelProvider/authRecoveryStarted",
16784
- "modelProvider/authRecoveryCompleted",
16785
16815
  "thread/status/changed",
16786
16816
  "thread/archived",
16787
16817
  "thread/unarchived",
@@ -16853,6 +16883,25 @@ function reduceNotification(notification, generation, handle) {
16853
16883
  ]
16854
16884
  };
16855
16885
  }
16886
+ case "modelProvider/authRecoveryStarted":
16887
+ case "modelProvider/authRecoveryCompleted": {
16888
+ if (!threadId || !isRecord9(params))
16889
+ return { events: [] };
16890
+ const message = str3(params.message)?.trim().slice(0, 300);
16891
+ const provider = str3(params.provider)?.trim().slice(0, 64);
16892
+ return {
16893
+ events: [
16894
+ {
16895
+ kind: "thread.authRecovery",
16896
+ threadId,
16897
+ phase: notification.method === "modelProvider/authRecoveryStarted" ? "started" : "completed",
16898
+ ...message ? { message } : {},
16899
+ ...provider ? { provider } : {},
16900
+ ...base
16901
+ }
16902
+ ]
16903
+ };
16904
+ }
16856
16905
  case "thread/tokenUsage/updated": {
16857
16906
  if (!isRecord9(params) || !threadId || !turnId || !isRecord9(params.tokenUsage)) {
16858
16907
  return { events: [] };
@@ -16895,7 +16944,8 @@ function reduceNotification(notification, generation, handle) {
16895
16944
  updatedAt: new Date().toISOString()
16896
16945
  },
16897
16946
  ...base
16898
- }
16947
+ },
16948
+ { kind: "model.response.completed", threadId, turnId, ...base }
16899
16949
  ]
16900
16950
  };
16901
16951
  }
@@ -17289,7 +17339,7 @@ import { mkdir as mkdir7, open as open4, rename as rename5, rm as rm7, writeFile
17289
17339
  import { join as join12 } from "node:path";
17290
17340
 
17291
17341
  // ../../bridges/codex-bridge/src/sessions/persistence.ts
17292
- import { createHash as createHash7, randomUUID as randomUUID5 } from "node:crypto";
17342
+ import { createHash as createHash8, randomUUID as randomUUID5 } from "node:crypto";
17293
17343
  import {
17294
17344
  chmod,
17295
17345
  link as link2,
@@ -17383,7 +17433,7 @@ function isPersistedSessionTombstone(value) {
17383
17433
  return tombstone.deleted === true && typeof tombstone.bridgeSessionId === "string" && tombstone.bridgeSessionId.length > 0 && typeof tombstone.deletedAt === "string" && Number.isFinite(Date.parse(tombstone.deletedAt));
17384
17434
  }
17385
17435
  function hashCwd(cwd) {
17386
- return createHash7("sha256").update(cwd).digest("hex").slice(0, 16);
17436
+ return createHash8("sha256").update(cwd).digest("hex").slice(0, 16);
17387
17437
  }
17388
17438
  var DEFAULT_RETENTION_MS = 7 * 24 * 60 * 60 * 1000;
17389
17439
  var TEMP_FILE_GRACE_MS = 60 * 60 * 1000;
@@ -17412,7 +17462,7 @@ class BridgeSessionStore {
17412
17462
  return join11(this.dir(), `bridge-sessions-${this.cwdHash}`);
17413
17463
  }
17414
17464
  recordKey(bridgeSessionId) {
17415
- return createHash7("sha256").update(bridgeSessionId).digest("hex");
17465
+ return createHash8("sha256").update(bridgeSessionId).digest("hex");
17416
17466
  }
17417
17467
  recordPath(bridgeSessionId) {
17418
17468
  return join11(this.recordsDir(), `${this.recordKey(bridgeSessionId)}.json`);
@@ -18808,7 +18858,7 @@ async function readStoredLoginFingerprint(codexHome) {
18808
18858
  const apiKey = typeof root.OPENAI_API_KEY === "string" ? root.OPENAI_API_KEY : "";
18809
18859
  if (!refreshToken && !apiKey)
18810
18860
  return;
18811
- return createHash8("sha256").update(JSON.stringify([refreshToken, apiKey])).digest("hex");
18861
+ return createHash9("sha256").update(JSON.stringify([refreshToken, apiKey])).digest("hex");
18812
18862
  } catch {
18813
18863
  return;
18814
18864
  }
@@ -18863,7 +18913,9 @@ class AppServerEngine {
18863
18913
  pendingSignIn = null;
18864
18914
  pendingSignInTimer;
18865
18915
  completedLoginId = null;
18916
+ activeLoginId = null;
18866
18917
  loginSequence = 0;
18918
+ loginStart;
18867
18919
  accountServingGeneration;
18868
18920
  notificationSequence = 0;
18869
18921
  accountProofInvalidatedSequence = 0;
@@ -18907,7 +18959,7 @@ class AppServerEngine {
18907
18959
  }),
18908
18960
  onGenerationReady: (generation, previous) => this.handleGenerationChange(generation, previous),
18909
18961
  onGenerationExit: (generation) => {
18910
- this.clearPendingSignIn();
18962
+ this.forgetSignIn();
18911
18963
  this.loginSequence++;
18912
18964
  this.hostedNetworkLockVerifiedGeneration = null;
18913
18965
  this.toolHistoryLockVerifiedGeneration = null;
@@ -18975,7 +19027,7 @@ class AppServerEngine {
18975
19027
  return this.info();
18976
19028
  }
18977
19029
  async stop() {
18978
- this.clearPendingSignIn();
19030
+ this.forgetSignIn();
18979
19031
  await this.supervisor.stop();
18980
19032
  this.bindings.clear();
18981
19033
  this.rejectAllWaiters();
@@ -19059,6 +19111,8 @@ class AppServerEngine {
19059
19111
  this.completedLoginId = loginId;
19060
19112
  if (this.pendingSignIn?.loginId === loginId)
19061
19113
  this.clearPendingSignIn();
19114
+ if (this.activeLoginId === loginId)
19115
+ this.activeLoginId = null;
19062
19116
  }
19063
19117
  }
19064
19118
  if (notification.method === "account/login/completed" && objectRecord(notification.params).success === true || notification.method === "account/updated") {
@@ -19603,6 +19657,10 @@ class AppServerEngine {
19603
19657
  this.pendingSignInTimer = undefined;
19604
19658
  this.pendingSignIn = null;
19605
19659
  }
19660
+ forgetSignIn() {
19661
+ this.clearPendingSignIn();
19662
+ this.activeLoginId = null;
19663
+ }
19606
19664
  async readAccount() {
19607
19665
  const sequence = ++this.accountCheckSequence;
19608
19666
  const generation = this.supervisor.getGeneration();
@@ -19612,7 +19670,7 @@ class AppServerEngine {
19612
19670
  console.warn("[codex-bridge] Account connector recovery failed:", redactSecrets(error instanceof Error ? error.message : String(error)));
19613
19671
  });
19614
19672
  if (loginSequence === this.loginSequence && objectRecord(response).account)
19615
- this.clearPendingSignIn();
19673
+ this.forgetSignIn();
19616
19674
  const pendingSignIn = normalizePendingSignIn(this.pendingSignIn?.display);
19617
19675
  if (!pendingSignIn)
19618
19676
  this.clearPendingSignIn();
@@ -19628,15 +19686,26 @@ class AppServerEngine {
19628
19686
  }
19629
19687
  async beginAccountLogin(method = "browser") {
19630
19688
  const sequence = ++this.loginSequence;
19631
- this.clearPendingSignIn();
19632
- const response = await this.supervisor.request("account/login/start", method === "device-code" ? {
19689
+ this.forgetSignIn();
19690
+ const start = this.supervisor.requestWithGeneration("account/login/start", method === "device-code" ? {
19633
19691
  type: "chatgptDeviceCode"
19634
19692
  } : {
19635
19693
  type: "chatgpt",
19636
19694
  codexStreamlinedLogin: true,
19637
19695
  useHostedLoginSuccessPage: true
19638
19696
  });
19697
+ this.loginStart = start;
19698
+ let response;
19699
+ try {
19700
+ response = (await start).result;
19701
+ } finally {
19702
+ if (this.loginStart === start)
19703
+ this.loginStart = undefined;
19704
+ }
19639
19705
  const raw = objectRecord(response);
19706
+ if (sequence === this.loginSequence && typeof raw.loginId === "string" && raw.loginId.length > 0 && raw.loginId.length <= 512 && raw.loginId !== this.completedLoginId) {
19707
+ this.activeLoginId = raw.loginId;
19708
+ }
19640
19709
  const display = normalizePendingSignIn({
19641
19710
  url: raw.verificationUrl,
19642
19711
  code: raw.userCode,
@@ -19644,13 +19713,46 @@ class AppServerEngine {
19644
19713
  });
19645
19714
  if (sequence === this.loginSequence && typeof raw.loginId === "string" && raw.loginId.length > 0 && raw.loginId.length <= 512 && raw.loginId !== this.completedLoginId && display) {
19646
19715
  this.pendingSignIn = { loginId: raw.loginId, display };
19647
- this.pendingSignInTimer = setTimeout(() => this.clearPendingSignIn(), Math.max(0, display.expiresAt - Date.now()));
19716
+ this.pendingSignInTimer = setTimeout(() => void this.cancelAccountLogin().catch(() => {
19717
+ return;
19718
+ }), Math.max(0, display.expiresAt - Date.now()));
19648
19719
  this.pendingSignInTimer.unref?.();
19649
19720
  }
19650
19721
  return response;
19651
19722
  }
19723
+ async cancelAccountLogin() {
19724
+ let loginId = this.activeLoginId;
19725
+ const start = this.loginStart;
19726
+ let generation = this.supervisor.getGeneration();
19727
+ const sequence = ++this.loginSequence;
19728
+ if (!loginId && start) {
19729
+ const started = await start;
19730
+ generation = started.generation;
19731
+ if (generation !== this.supervisor.getGeneration())
19732
+ return "none";
19733
+ const raw = objectRecord(started.result);
19734
+ if (typeof raw.loginId === "string" && raw.loginId.length > 0 && raw.loginId.length <= 512 && raw.loginId !== this.completedLoginId) {
19735
+ loginId = raw.loginId;
19736
+ if (sequence === this.loginSequence) {
19737
+ this.activeLoginId = loginId;
19738
+ this.pendingSignInTimer = setTimeout(() => void this.cancelAccountLogin().catch(() => {
19739
+ return;
19740
+ }), NATIVE_AGENT_PENDING_SIGN_IN_TTL_MS);
19741
+ this.pendingSignInTimer.unref?.();
19742
+ }
19743
+ }
19744
+ }
19745
+ if (!loginId)
19746
+ return "none";
19747
+ const response = await this.supervisor.requestOnGeneration(generation, "account/login/cancel", {
19748
+ loginId
19749
+ });
19750
+ if (sequence === this.loginSequence && this.activeLoginId === loginId && generation === this.supervisor.getGeneration())
19751
+ this.forgetSignIn();
19752
+ return objectRecord(response).status === "canceled" ? "canceled" : "none";
19753
+ }
19652
19754
  async logoutAccount() {
19653
- this.clearPendingSignIn();
19755
+ this.forgetSignIn();
19654
19756
  this.loginSequence++;
19655
19757
  await this.supervisor.request("account/logout", undefined);
19656
19758
  }
@@ -20319,7 +20421,7 @@ class CodexRuntimeSessions {
20319
20421
  }
20320
20422
  createSession(body) {
20321
20423
  const clientSessionKey = typeof body.clientSessionKey === "string" && body.clientSessionKey.trim().length > 0 && body.clientSessionKey.length <= 512 ? body.clientSessionKey : undefined;
20322
- const sessionId = clientSessionKey ? `session-client-${createHash9("sha256").update(this.ctx.options.cwd).update("\x00").update(clientSessionKey).digest("hex").slice(0, 32)}` : createSessionId();
20424
+ const sessionId = clientSessionKey ? `session-client-${createHash10("sha256").update(this.ctx.options.cwd).update("\x00").update(clientSessionKey).digest("hex").slice(0, 32)}` : createSessionId();
20323
20425
  const existing = this.ctx.registry.getSession(sessionId);
20324
20426
  if (existing) {
20325
20427
  this.ctx.registry.touch(sessionId);
@@ -20605,7 +20707,7 @@ class CodexRuntimeSessions {
20605
20707
  return "not-found";
20606
20708
  if (this.sessionAdmissionClosed(session))
20607
20709
  return "closing";
20608
- const inputDigest = createHash9("sha256").update(input).digest("hex");
20710
+ const inputDigest = createHash10("sha256").update(input).digest("hex");
20609
20711
  const previous = this.ctx.steerRequests.get(requestId);
20610
20712
  if (previous) {
20611
20713
  if (previous.threadId !== session.threadId || previous.turnId !== expectedTurnId || previous.inputDigest !== inputDigest) {
@@ -20922,6 +21024,9 @@ class CodexRuntimeSessions {
20922
21024
  logoutAuth() {
20923
21025
  return this.ctx.options.engine.logoutAccount();
20924
21026
  }
21027
+ cancelAuthLogin() {
21028
+ return this.ctx.options.engine.cancelAccountLogin();
21029
+ }
20925
21030
  async rewindMessages(sessionId, messageId) {
20926
21031
  const session = this.ctx.registry.getSession(sessionId);
20927
21032
  if (!session)
@@ -21133,6 +21238,7 @@ class CodexRuntimeSessions {
21133
21238
  messageRevision: session.messageRevision,
21134
21239
  contentEpoch: session.contentEpoch,
21135
21240
  ...this.ctx.commandCatalogue.revision > 0 ? { commandRevision: this.ctx.commandCatalogue.revision } : {},
21241
+ ...context?.authNotice ? { authNotice: context.authNotice } : {},
21136
21242
  ...this.ctx.options.engine.isAccountSignInRequired() ? {
21137
21243
  readiness: {
21138
21244
  state: "authentication-required",
@@ -21653,7 +21759,7 @@ function parseCommandToken(text, sigils = ["/"]) {
21653
21759
  }
21654
21760
 
21655
21761
  // ../../bridges/codex-bridge/src/prompts/template-discovery.ts
21656
- import { createHash as createHash10 } from "node:crypto";
21762
+ import { createHash as createHash11 } from "node:crypto";
21657
21763
  import { open as open7, readdir as readdir3, stat as stat7 } from "node:fs/promises";
21658
21764
  import { join as join14 } from "node:path";
21659
21765
 
@@ -21794,7 +21900,7 @@ function templateBindingRevision(origin, relativePath) {
21794
21900
  return commandBindingRevision(["template", origin, relativePath]);
21795
21901
  }
21796
21902
  function fingerprintOf(bytes) {
21797
- return createHash10("sha256").update(bytes).digest("hex").slice(0, 32);
21903
+ return createHash11("sha256").update(bytes).digest("hex").slice(0, 32);
21798
21904
  }
21799
21905
  function nameFromRelativePath(relativePath) {
21800
21906
  return `/${relativePath.replace(/\.md$/i, "")}`;
@@ -22061,7 +22167,9 @@ var SKILL_INVENTORY_LIMITS = Object.freeze({
22061
22167
  maxSkills: 512,
22062
22168
  maxDescriptionBytes: 1000,
22063
22169
  ttlMs: 30000,
22064
- refreshDebounceMs: 250
22170
+ refreshDebounceMs: 250,
22171
+ coldSettleMs: 1000,
22172
+ maxSettleReads: 5
22065
22173
  });
22066
22174
  var SKILL_ID_PREFIX = "codex-skill:";
22067
22175
  var SKILL_ALIAS_PREFIX = "/skill:";
@@ -22149,6 +22257,10 @@ function normalizeSkillsList(raw, context) {
22149
22257
  fetchedAt: context.now
22150
22258
  };
22151
22259
  }
22260
+ function snapshotSignature(snapshot) {
22261
+ return snapshot.bindings.map((binding) => `${binding.bindingRevision}:${binding.enabled}:${binding.ambiguous}`).join(`
22262
+ `);
22263
+ }
22152
22264
  function skillUnavailability(binding, projectResources = true) {
22153
22265
  if (!projectResources && !["user", "system", "admin"].includes(binding.scope)) {
22154
22266
  return {
@@ -22195,8 +22307,12 @@ class SkillInventory {
22195
22307
  invalidationSerial = 0;
22196
22308
  inflight = null;
22197
22309
  refreshTimer = null;
22310
+ settledGeneration = null;
22311
+ settling = null;
22312
+ answeredGeneration = null;
22313
+ settleTimer = null;
22198
22314
  disposed = false;
22199
- stats = { invalidations: 0, reads: 0, scheduledRefreshes: 0 };
22315
+ stats = { invalidations: 0, reads: 0, scheduledRefreshes: 0, settlingReads: 0 };
22200
22316
  constructor(deps) {
22201
22317
  this.deps = deps;
22202
22318
  }
@@ -22228,7 +22344,7 @@ class SkillInventory {
22228
22344
  const snapshot = this.current();
22229
22345
  const ttl = this.deps.ttlMs ?? SKILL_INVENTORY_LIMITS.ttlMs;
22230
22346
  if (!options.forceReload && snapshot && !this.dirty && this.deps.now() - snapshot.fetchedAt < ttl) {
22231
- return { snapshot, fresh: !snapshot.partial };
22347
+ return { snapshot, fresh: !snapshot.partial, settled: this.isSettled(snapshot.generation) };
22232
22348
  }
22233
22349
  if (this.inflight && (this.inflight.forced || !options.forceReload)) {
22234
22350
  return this.inflight.promise;
@@ -22243,7 +22359,8 @@ class SkillInventory {
22243
22359
  }
22244
22360
  async refresh(forceReload) {
22245
22361
  const read = await this.fetch(forceReload);
22246
- this.deps.onRefreshed?.(read);
22362
+ if (!this.disposed)
22363
+ this.deps.onRefreshed?.(read);
22247
22364
  return read;
22248
22365
  }
22249
22366
  async fetch(forceReload) {
@@ -22263,18 +22380,93 @@ class SkillInventory {
22263
22380
  this.dirty = this.invalidationSerial !== serial;
22264
22381
  const live = this.current();
22265
22382
  if (!live)
22266
- return { snapshot: null, fresh: false, error: "Codex restarted while listing skills" };
22267
- return { snapshot: live, fresh: !live.partial };
22383
+ return {
22384
+ snapshot: null,
22385
+ fresh: false,
22386
+ settled: false,
22387
+ error: "Codex restarted while listing skills"
22388
+ };
22389
+ this.noteSettlingAnswer(live, serial);
22390
+ this.noteAnswer(live);
22391
+ return { snapshot: live, fresh: !live.partial, settled: this.isSettled(live.generation) };
22268
22392
  } catch {
22269
22393
  this.dirty = true;
22270
- return { snapshot: this.current(), fresh: false, error: "Codex skills could not be listed" };
22394
+ const retained = this.current();
22395
+ return {
22396
+ snapshot: retained,
22397
+ fresh: false,
22398
+ settled: retained ? this.isSettled(retained.generation) : false,
22399
+ error: "Codex skills could not be listed"
22400
+ };
22401
+ }
22402
+ }
22403
+ isSettled(generation) {
22404
+ return this.coldSettleMs() <= 0 || this.settledGeneration === generation;
22405
+ }
22406
+ coldSettleMs() {
22407
+ return this.deps.coldSettleMs ?? SKILL_INVENTORY_LIMITS.coldSettleMs;
22408
+ }
22409
+ noteAnswer(snapshot) {
22410
+ const { generation } = snapshot;
22411
+ if (this.isSettled(generation) || this.answeredGeneration === generation || this.disposed) {
22412
+ return;
22271
22413
  }
22414
+ this.answeredGeneration = generation;
22415
+ this.armSettle(snapshot, 1);
22416
+ }
22417
+ noteSettlingAnswer(live, serial) {
22418
+ const settling = this.settling;
22419
+ if (!settling || settling.generation !== live.generation || serial < settling.serial)
22420
+ return;
22421
+ this.settling = null;
22422
+ if (settling.round >= 2 && snapshotSignature(live) === settling.baseline || settling.round >= SKILL_INVENTORY_LIMITS.maxSettleReads) {
22423
+ this.settledGeneration = live.generation;
22424
+ return;
22425
+ }
22426
+ this.armSettle(live, settling.round + 1);
22427
+ }
22428
+ armSettle(snapshot, round) {
22429
+ const { generation } = snapshot;
22430
+ const baseline = snapshotSignature(snapshot);
22431
+ if (this.settleTimer)
22432
+ clearTimeout(this.settleTimer);
22433
+ this.settleTimer = setTimeout(() => {
22434
+ this.settleTimer = null;
22435
+ if (this.disposed || this.deps.generation() !== generation)
22436
+ return;
22437
+ this.invalidationSerial += 1;
22438
+ this.dirty = true;
22439
+ const settling = {
22440
+ generation,
22441
+ serial: this.invalidationSerial,
22442
+ round,
22443
+ baseline
22444
+ };
22445
+ this.settling = settling;
22446
+ this.stats.settlingReads += 1;
22447
+ const pending = this.inflight?.promise.catch(() => {
22448
+ return;
22449
+ });
22450
+ const read = () => {
22451
+ if (this.disposed || this.deps.generation() !== generation || this.settling !== settling)
22452
+ return;
22453
+ return this.read();
22454
+ };
22455
+ (pending ? pending.then(read) : read())?.catch(() => {
22456
+ return;
22457
+ });
22458
+ }, this.coldSettleMs());
22459
+ this.settleTimer.unref?.();
22272
22460
  }
22273
22461
  dispose() {
22274
22462
  this.disposed = true;
22275
22463
  if (this.refreshTimer)
22276
22464
  clearTimeout(this.refreshTimer);
22277
22465
  this.refreshTimer = null;
22466
+ if (this.settleTimer)
22467
+ clearTimeout(this.settleTimer);
22468
+ this.settleTimer = null;
22469
+ this.settling = null;
22278
22470
  }
22279
22471
  }
22280
22472
 
@@ -22362,9 +22554,11 @@ class CodexCommandCatalogue {
22362
22554
  now: options.now,
22363
22555
  ...options.skillTtlMs !== undefined ? { ttlMs: options.skillTtlMs } : {},
22364
22556
  ...options.skillRefreshDebounceMs !== undefined ? { refreshDebounceMs: options.skillRefreshDebounceMs } : {},
22557
+ ...options.skillColdSettleMs !== undefined ? { coldSettleMs: options.skillColdSettleMs } : {},
22365
22558
  onRefreshed: (read) => {
22366
22559
  this.skillsDigest = commandBindingRevision([
22367
22560
  String(read.fresh),
22561
+ String(read.settled),
22368
22562
  read.snapshot ? JSON.stringify([
22369
22563
  read.snapshot.truncated,
22370
22564
  read.snapshot.bindings.map((binding) => skillCommandRow(binding))
@@ -22411,10 +22605,6 @@ class CodexCommandCatalogue {
22411
22605
  this.bumpIfChanged();
22412
22606
  return scan;
22413
22607
  }
22414
- async listTemplateAndBuiltinRows() {
22415
- const templates = await this.scanTemplates();
22416
- return [...BUILTINS.map(builtinRow), ...templates.effective.map(templateCommandRow)];
22417
- }
22418
22608
  async collect(projectResources = true) {
22419
22609
  this.deferredBumps += 1;
22420
22610
  let templates;
@@ -22444,7 +22634,7 @@ class CodexCommandCatalogue {
22444
22634
  }
22445
22635
  return {
22446
22636
  commands,
22447
- status: skillRead.fresh ? "ready" : "stale",
22637
+ status: skillRead.fresh && skillRead.settled ? "ready" : "stale",
22448
22638
  truncated,
22449
22639
  templates,
22450
22640
  skills: skillRead.snapshot
@@ -22752,7 +22942,8 @@ class CodexRuntimeContext {
22752
22942
  cwd: options.cwd,
22753
22943
  now: this.now,
22754
22944
  ...options.skillInventoryTtlMs !== undefined ? { skillTtlMs: options.skillInventoryTtlMs } : {},
22755
- ...options.skillRefreshDebounceMs !== undefined ? { skillRefreshDebounceMs: options.skillRefreshDebounceMs } : {}
22945
+ ...options.skillRefreshDebounceMs !== undefined ? { skillRefreshDebounceMs: options.skillRefreshDebounceMs } : {},
22946
+ ...options.skillColdSettleMs !== undefined ? { skillColdSettleMs: options.skillColdSettleMs } : {}
22756
22947
  });
22757
22948
  }
22758
22949
  getRevisedRowsOverflowVersion(sessionId) {
@@ -22792,6 +22983,30 @@ function promptThreadConfig(config) {
22792
22983
  ]);
22793
22984
  }
22794
22985
 
22986
+ // ../../bridges/codex-bridge/src/sessions/auth-recovery-notice.ts
22987
+ var RENEWAL_FAILED = /\b(fail(?:ed|ure)?|could ?n[o']t|unable|unrecoverable|log ?(?:in|out) again|sign ?(?:in|out) again)\b/i;
22988
+ var CODEX_LOGIN_RENEWING_MESSAGE = "Codex's login stopped working during this turn, and Codex is renewing it.";
22989
+ var CODEX_LOGIN_RENEWAL_FAILED_SUFFIX = "Sign in to Codex again to keep working.";
22990
+ function nextThreadAuthNotice(current, event, newOccurrenceId) {
22991
+ if (event.phase === "started") {
22992
+ if (current?.state === "expired")
22993
+ return current;
22994
+ return {
22995
+ state: "expired",
22996
+ message: event.message ?? CODEX_LOGIN_RENEWING_MESSAGE,
22997
+ occurrenceId: newOccurrenceId()
22998
+ };
22999
+ }
23000
+ if (event.message && RENEWAL_FAILED.test(event.message)) {
23001
+ return {
23002
+ state: "needs-auth",
23003
+ message: `${event.message.replace(/[.\s]*$/, "")}. ${CODEX_LOGIN_RENEWAL_FAILED_SUFFIX}`,
23004
+ occurrenceId: current?.state === "needs-auth" ? current.occurrenceId : newOccurrenceId()
23005
+ };
23006
+ }
23007
+ return;
23008
+ }
23009
+
22795
23010
  // ../../bridges/codex-bridge/src/messages/coalescer.ts
22796
23011
  var DEFAULT_COALESCE_INTERVAL_MS = 100;
22797
23012
 
@@ -23480,6 +23695,9 @@ class CodexRuntimeLifecycle {
23480
23695
  case "turn.model.updated":
23481
23696
  this.applyConfirmedModel(context, event.model, event.turnId);
23482
23697
  return;
23698
+ case "thread.authRecovery":
23699
+ this.setThreadAuthNotice(context, nextThreadAuthNotice(context.authNotice, event, () => crypto.randomUUID()));
23700
+ return;
23483
23701
  case "turn.configuration.updated": {
23484
23702
  const turn = context.activeTurn;
23485
23703
  if (!turn || !turn.accepts(event))
@@ -23531,6 +23749,12 @@ class CodexRuntimeLifecycle {
23531
23749
  turn.noteModelToolCall();
23532
23750
  return;
23533
23751
  }
23752
+ case "model.response.completed": {
23753
+ const turn = context.activeTurn;
23754
+ if (turn?.accepts(event))
23755
+ turn.noteModelResponseCompleted();
23756
+ return;
23757
+ }
23534
23758
  case "item.started":
23535
23759
  case "item.updated":
23536
23760
  case "item.completed": {
@@ -23649,8 +23873,13 @@ class CodexRuntimeLifecycle {
23649
23873
  const turn = context.activeTurn;
23650
23874
  if (!turn || !turn.accepts(event))
23651
23875
  return;
23876
+ if (event.status === "completed")
23877
+ this.setThreadAuthNotice(context, undefined);
23652
23878
  this.discardOutlivingCommandWindows(turn);
23653
- turn.complete(event.status, event.error);
23879
+ if (turn.stoppedByShutdown && event.status === "interrupted")
23880
+ this.completeRecoveredTurn(turn, event.status);
23881
+ else
23882
+ turn.complete(event.status, event.error);
23654
23883
  this.ctx.options.changeEvidence?.turn(this.evidenceSource(context, turn), event.status);
23655
23884
  const finalize = () => this.runFinalization(context, turn).catch((error) => {
23656
23885
  console.error(`[codex-bridge] Failed to finalize turn ${turn.turnId}:`, error instanceof Error ? error.message : error);
@@ -23944,6 +24173,18 @@ class CodexRuntimeLifecycle {
23944
24173
  bytes: patchable ? estimateOrderedEventBytes(changedParts) : normalizedMessageSnapshotChars(target) * 2
23945
24174
  });
23946
24175
  }
24176
+ setThreadAuthNotice(context, notice) {
24177
+ if (context.authNotice === notice)
24178
+ return;
24179
+ context.authNotice = notice;
24180
+ for (const session of this.ctx.registry.boundSessionsForThread(context.threadId)) {
24181
+ this.ctx.options.emit({
24182
+ type: "session.updated",
24183
+ sessionId: session.id,
24184
+ data: { authNotice: notice ?? null }
24185
+ });
24186
+ }
24187
+ }
23947
24188
  applyConfirmedModel(context, model, turnId) {
23948
24189
  if (!turnId)
23949
24190
  context.modelId = model;
@@ -25313,9 +25554,6 @@ class CodexRuntimePrompt {
25313
25554
  }
25314
25555
  return this.ctx.commandCatalogue.refresh();
25315
25556
  }
25316
- async listGlobalCommandRows() {
25317
- return this.ctx.commandCatalogue.listTemplateAndBuiltinRows();
25318
- }
25319
25557
  async modelsListText(session) {
25320
25558
  const { models } = await this.ctx.callbacks.listModels();
25321
25559
  const current = session.config.model || models[0]?.id || "UNCONFIRMED";
@@ -25466,6 +25704,32 @@ class CodexRuntimeTail {
25466
25704
  this.lifecycle = lifecycle;
25467
25705
  this.sessions = sessions;
25468
25706
  }
25707
+ async interruptForParentExit(budgetMs) {
25708
+ const stopping = [];
25709
+ for (const context of this.ctx.registry.listThreads()) {
25710
+ const turn = context.activeTurn;
25711
+ if (!turn || turn.isTerminal() || turn.isUnconfirmed())
25712
+ continue;
25713
+ turn.stoppedByShutdown = true;
25714
+ const { engineHandle } = context;
25715
+ stopping.push(this.ctx.options.engine.interruptTurn(engineHandle, turn.turnId).then(() => this.ctx.options.engine.waitForTurnTerminal(engineHandle, turn.turnId, {
25716
+ timeoutMs: budgetMs
25717
+ })).catch(() => {
25718
+ return;
25719
+ }));
25720
+ }
25721
+ if (stopping.length === 0)
25722
+ return 0;
25723
+ let timer;
25724
+ await Promise.race([
25725
+ Promise.allSettled(stopping),
25726
+ new Promise((resolve) => {
25727
+ timer = setTimeout(resolve, budgetMs);
25728
+ })
25729
+ ]);
25730
+ clearTimeout(timer);
25731
+ return stopping.length;
25732
+ }
25469
25733
  async abort(sessionId) {
25470
25734
  const session = this.ctx.registry.getSession(sessionId);
25471
25735
  if (!session)
@@ -25776,6 +26040,9 @@ class AppServerRuntime {
25776
26040
  logoutAuth(...args) {
25777
26041
  return this.sessions.logoutAuth(...args);
25778
26042
  }
26043
+ cancelAuthLogin(...args) {
26044
+ return this.sessions.cancelAuthLogin(...args);
26045
+ }
25779
26046
  rewindMessages(...args) {
25780
26047
  return this.sessions.rewindMessages(...args);
25781
26048
  }
@@ -25824,15 +26091,15 @@ class AppServerRuntime {
25824
26091
  refreshCommandCatalogue(...args) {
25825
26092
  return this.prompts.refreshCommandCatalogue(...args);
25826
26093
  }
25827
- listGlobalCommandRows(...args) {
25828
- return this.prompts.listGlobalCommandRows(...args);
25829
- }
25830
26094
  get closeStopBudgetMs() {
25831
26095
  return this.tail.closeStopBudgetMs;
25832
26096
  }
25833
26097
  set closeStopBudgetMs(value) {
25834
26098
  this.tail.closeStopBudgetMs = value;
25835
26099
  }
26100
+ interruptForParentExit(...args) {
26101
+ return this.tail.interruptForParentExit(...args);
26102
+ }
25836
26103
  abort(...args) {
25837
26104
  return this.tail.abort(...args);
25838
26105
  }
@@ -26028,7 +26295,8 @@ function parseEventCursor(raw) {
26028
26295
 
26029
26296
  // ../protocol/src/parent-watchdog.ts
26030
26297
  var PARENT_PID_ENV = "ORKESTRATOR_PARENT_PID";
26031
- var DEFAULT_POLL_INTERVAL_MS = 15000;
26298
+ var PARENT_WATCH_INTERVAL_MS = 1000;
26299
+ var DEFAULT_POLL_INTERVAL_MS = PARENT_WATCH_INTERVAL_MS;
26032
26300
  function parseParentPid(value) {
26033
26301
  if (!value)
26034
26302
  return null;
@@ -26460,6 +26728,19 @@ function createShutdownHandler(timer, clearTimer = clearInterval, exit = (code)
26460
26728
  ]).finally(() => exit(0));
26461
26729
  };
26462
26730
  }
26731
+ function createParentExitHandler(interrupt, shutdown, report = (turns, ms) => {
26732
+ console.error(`[codex-bridge] Stopped ${turns} running turn(s) in ${ms} ms`);
26733
+ }) {
26734
+ return async () => {
26735
+ const detectedAt = performance.now();
26736
+ try {
26737
+ const turns = await interrupt(PARENT_EXIT_INTERRUPT_BUDGET_MS).catch(() => 0);
26738
+ report(turns, Math.round(performance.now() - detectedAt));
26739
+ } finally {
26740
+ shutdown();
26741
+ }
26742
+ };
26743
+ }
26463
26744
  var FALLBACK_MODELS = [
26464
26745
  {
26465
26746
  id: "gpt-5.4",
@@ -26756,6 +27037,7 @@ var __testing = {
26756
27037
  createCodexProcessDiagnosticsForTesting: createCodexProcessDiagnostics,
26757
27038
  createSharedTranscriptMetaLoaderForTesting: createSharedTranscriptMetaLoader,
26758
27039
  createShutdownHandlerForTesting: createShutdownHandler,
27040
+ createParentExitHandlerForTesting: createParentExitHandler,
26759
27041
  emitForTesting: emit,
26760
27042
  eventRingForTesting: () => eventRing,
26761
27043
  withIsolatedEventRingForTesting: () => {
@@ -26971,6 +27253,10 @@ app.post("/global/auth/login", async (c) => {
26971
27253
  ...raw.userCode ? { code: raw.userCode } : {}
26972
27254
  });
26973
27255
  });
27256
+ app.post("/global/auth/login/cancel", async (c) => {
27257
+ const status = await appServerRuntime.cancelAuthLogin();
27258
+ return c.json({ cancelled: status === "canceled" });
27259
+ });
26974
27260
  app.post("/global/auth/logout", async (c) => {
26975
27261
  await appServerRuntime.logoutAuth();
26976
27262
  return c.json({ ok: true });
@@ -26982,10 +27268,6 @@ app.get("/global/usage", async (c) => {
26982
27268
  const account = await appServerRuntime.readPlanUsage();
26983
27269
  return c.json({ account: account ?? null });
26984
27270
  });
26985
- app.get("/global/slash-commands", async (c) => {
26986
- const cwd = getWorkingDirectory();
26987
- return c.json({ commands: await appServerRuntime.listGlobalCommandRows(), cwd });
26988
- });
26989
27271
  app.get("/session/:id/commands", async (c) => {
26990
27272
  return c.json(await appServerRuntime.getCommandCatalogue(c.req.param("id")));
26991
27273
  });
@@ -27602,13 +27884,14 @@ for (const signal of ["SIGTERM", "SIGINT"]) {
27602
27884
  process.on(signal, shutdownHandler);
27603
27885
  }
27604
27886
  installFatalRejectionGuard({ label: "[codex-bridge]" });
27887
+ var PARENT_EXIT_INTERRUPT_BUDGET_MS = 2000;
27605
27888
  var parentPid = parseParentPid(process.env[PARENT_PID_ENV]);
27606
27889
  if (parentPid !== null) {
27607
27890
  startParentWatchdog({
27608
27891
  parentPid,
27609
27892
  onParentExit: () => {
27610
27893
  console.error(`[codex-bridge] Backend process ${parentPid} is gone; shutting down`);
27611
- shutdownHandler();
27894
+ createParentExitHandler((budgetMs) => appServerRuntime.interruptForParentExit(budgetMs), shutdownHandler)();
27612
27895
  }
27613
27896
  });
27614
27897
  }