orkestrator 2.26.3-beta → 2.28.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.
@@ -4060,6 +4060,7 @@ var NATIVE_AGENT_APPROVAL_MODES = Object.freeze({
4060
4060
  }
4061
4061
  ]
4062
4062
  });
4063
+ var NATIVE_AGENT_RETIRED_DISPATCH_TTL_MS = 24 * 60 * 60 * 1000;
4063
4064
  var SUPPRESSED_COMPOSER_PARAMETERS = Object.freeze({
4064
4065
  claude: Object.freeze(["parameter:thinking", "parameter:context1m"]),
4065
4066
  codex: Object.freeze(["parameter:summary"]),
@@ -4191,6 +4192,7 @@ var PROJECTION_FIELD_SET = new Set([
4191
4192
  "runtimeHealthAuthoritative",
4192
4193
  "notices",
4193
4194
  "recoverableDispatch",
4195
+ "retiredDispatch",
4194
4196
  "backgroundTasks",
4195
4197
  "suggestedPrompt",
4196
4198
  "completionBlockedByBackgroundTasks",
@@ -4214,6 +4216,7 @@ var OPTIONAL_PROJECTION_FIELD_SET = new Set([
4214
4216
  "runtimeHealthAuthoritative",
4215
4217
  "notices",
4216
4218
  "recoverableDispatch",
4219
+ "retiredDispatch",
4217
4220
  "backgroundTasks",
4218
4221
  "suggestedPrompt",
4219
4222
  "completionBlockedByBackgroundTasks",
@@ -5089,6 +5092,7 @@ var SESSION_ACTIVITY_BATCH_LIMITS = {
5089
5092
  maxResponseBytes: 512 * 1024,
5090
5093
  maxAsyncQuestionItemIds: 64,
5091
5094
  maxAsyncQuestionItemIdLength: 2048,
5095
+ maxContentRevisionLength: 256,
5092
5096
  readConcurrency: 4
5093
5097
  };
5094
5098
  var SESSION_ACTIVITY_STATES = new Set(["idle", "working", "waiting", "missing"]);
@@ -5103,6 +5107,7 @@ function parseSessionActivityObservation(value) {
5103
5107
  if (!isRecord(value))
5104
5108
  return;
5105
5109
  const { activity, readyForInput, completionPending, asyncQuestionItemIds, closing } = value;
5110
+ const { contentRevision } = value;
5106
5111
  if (typeof activity !== "string" || !SESSION_ACTIVITY_STATES.has(activity))
5107
5112
  return;
5108
5113
  if (readyForInput !== undefined && typeof readyForInput !== "boolean")
@@ -5111,6 +5116,8 @@ function parseSessionActivityObservation(value) {
5111
5116
  return;
5112
5117
  if (closing !== undefined && typeof closing !== "boolean")
5113
5118
  return;
5119
+ if (contentRevision !== undefined && !isContentRevision(contentRevision))
5120
+ return;
5114
5121
  let itemIds;
5115
5122
  if (asyncQuestionItemIds !== undefined) {
5116
5123
  if (!Array.isArray(asyncQuestionItemIds) || asyncQuestionItemIds.length > SESSION_ACTIVITY_BATCH_LIMITS.maxAsyncQuestionItemIds || !asyncQuestionItemIds.every((itemId) => typeof itemId === "string" && itemId.length > 0 && itemId.length <= SESSION_ACTIVITY_BATCH_LIMITS.maxAsyncQuestionItemIdLength)) {
@@ -5123,9 +5130,13 @@ function parseSessionActivityObservation(value) {
5123
5130
  ...readyForInput !== undefined ? { readyForInput } : {},
5124
5131
  ...completionPending !== undefined ? { completionPending } : {},
5125
5132
  ...itemIds !== undefined ? { asyncQuestionItemIds: itemIds } : {},
5126
- ...closing !== undefined ? { closing } : {}
5133
+ ...closing !== undefined ? { closing } : {},
5134
+ ...contentRevision !== undefined ? { contentRevision } : {}
5127
5135
  };
5128
5136
  }
5137
+ function isContentRevision(value) {
5138
+ return typeof value === "string" && value.length > 0 && value.length <= SESSION_ACTIVITY_BATCH_LIMITS.maxContentRevisionLength;
5139
+ }
5129
5140
  function parseSessionActivityBatchRequest(value) {
5130
5141
  if (!isRecord(value))
5131
5142
  return { ok: false, error: "Expected a JSON object" };
@@ -10815,6 +10826,7 @@ var STRONG_REFUSAL_MARKERS = [
10815
10826
  "read-only file system"
10816
10827
  ];
10817
10828
  var SCRIPT_FAILED = "script failed";
10829
+ var INTERRUPTED_CELL = /^aborted by user\b/i;
10818
10830
  var MAX_PENDING_CELLS = 256;
10819
10831
  var MAX_CELL_TEXT_CHARS = 64 * 1024;
10820
10832
  var ROLLOUT_POLL_INTERVAL_MS = 200;
@@ -10846,7 +10858,12 @@ function reportsRefusal(output) {
10846
10858
  }
10847
10859
  function codeModeCellOutcome(output) {
10848
10860
  const refused = reportsRefusal(output);
10849
- return { refused, failed: refused || output.trimStart().toLowerCase().startsWith(SCRIPT_FAILED) };
10861
+ const trimmed = output.trimStart();
10862
+ return {
10863
+ refused,
10864
+ failed: refused || trimmed.toLowerCase().startsWith(SCRIPT_FAILED),
10865
+ interrupted: !refused && INTERRUPTED_CELL.test(trimmed)
10866
+ };
10850
10867
  }
10851
10868
  function defaultWatch(_path, onChange) {
10852
10869
  const timer = setInterval(onChange, ROLLOUT_POLL_INTERVAL_MS);
@@ -10975,7 +10992,9 @@ class RefusedCellTail {
10975
10992
  this.pending.delete(callId);
10976
10993
  const output = cellOutputText(payload.output);
10977
10994
  const completedAtMs = recordTimeMs(record, payload);
10978
- const { refused, failed } = codeModeCellOutcome(output);
10995
+ const { refused, failed, interrupted } = codeModeCellOutcome(output);
10996
+ if (interrupted)
10997
+ return;
10979
10998
  const unseen = !Number.isFinite(cell.startedAtMs) || !Number.isFinite(completedAtMs) || !this.options.itemStartedBetween(cell.startedAtMs, completedAtMs);
10980
10999
  if (!refused && !unseen)
10981
11000
  return;
@@ -10992,6 +11011,7 @@ class RefusedCellTail {
10992
11011
  }
10993
11012
 
10994
11013
  // ../../bridges/codex-bridge/src/app-server/item-adapter.ts
11014
+ import { isAbsolute as isAbsolute5 } from "node:path";
10995
11015
  function isRecord5(value) {
10996
11016
  return !!value && typeof value === "object" && !Array.isArray(value);
10997
11017
  }
@@ -11004,6 +11024,20 @@ function durationMs(value) {
11004
11024
  function strArray(value) {
11005
11025
  return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
11006
11026
  }
11027
+ var MAX_IMAGE_GENERATION_INPUTS = 8;
11028
+ function imageGenerationInputPaths(value) {
11029
+ if (!Array.isArray(value))
11030
+ return [];
11031
+ const paths = new Set;
11032
+ for (const entry of value) {
11033
+ if (paths.size >= MAX_IMAGE_GENERATION_INPUTS)
11034
+ break;
11035
+ const path = isRecord5(entry) ? str(entry.path) : undefined;
11036
+ if (path && isAbsolute5(path))
11037
+ paths.add(path);
11038
+ }
11039
+ return [...paths];
11040
+ }
11007
11041
  var MAX_ASYNC_QUESTIONS = 16;
11008
11042
  var MAX_ASYNC_OPTIONS = 32;
11009
11043
  var MAX_ASYNC_TEXT_LENGTH = 16384;
@@ -11265,13 +11299,15 @@ function adaptAppServerItem(raw) {
11265
11299
  }
11266
11300
  case "imageGeneration": {
11267
11301
  const path = str(raw.savedPath);
11302
+ const inputPaths = imageGenerationInputPaths(raw.inputImages);
11268
11303
  return {
11269
11304
  item: {
11270
11305
  id,
11271
11306
  type: "image",
11272
11307
  text: str(raw.revisedPrompt) ?? "Generated image",
11273
11308
  ...path ? { path } : {},
11274
- source: "generated"
11309
+ source: "generated",
11310
+ ...inputPaths.length > 0 ? { input_paths: inputPaths } : {}
11275
11311
  }
11276
11312
  };
11277
11313
  }
@@ -12074,6 +12110,13 @@ async function itemToParts(item, cwd, fileChangeContext) {
12074
12110
  return [];
12075
12111
  case "image":
12076
12112
  return [
12113
+ ...(item.input_paths ?? []).map((path) => ({
12114
+ type: "image",
12115
+ content: path.split(/[\\/]/).pop() || "Image",
12116
+ fileUrl: `file://${path}`,
12117
+ filename: path,
12118
+ imageSource: "viewed"
12119
+ })),
12077
12120
  {
12078
12121
  type: "image",
12079
12122
  content: item.text,
@@ -12370,11 +12413,8 @@ async function walkJsonlFiles(dir) {
12370
12413
  }
12371
12414
  return files;
12372
12415
  }
12373
- async function listTranscriptPaths() {
12374
- const searchRoots = [
12375
- join8(getCodexHomeDir(), "sessions"),
12376
- join8(getCodexHomeDir(), "archived_sessions")
12377
- ];
12416
+ async function listTranscriptPaths(codexHome = getCodexHomeDir()) {
12417
+ const searchRoots = [join8(codexHome, "sessions"), join8(codexHome, "archived_sessions")];
12378
12418
  return (await Promise.all(searchRoots.map((root) => walkJsonlFiles(root)))).flat();
12379
12419
  }
12380
12420
  var cachedTranscriptPaths = new Map;
@@ -12999,6 +13039,18 @@ async function replayPersistedRollout(threadId, snapshot, meta, structuredTurns)
12999
13039
  const settleCodeModeCell = (cell, output) => {
13000
13040
  openCells.delete(cell.callId);
13001
13041
  const outcome = output === undefined ? undefined : codeModeCellOutcome(cellOutputText(output));
13042
+ if (outcome?.interrupted) {
13043
+ const { toolState: _claimed, ...stopped } = cell.message.parts[cell.execPartIndex];
13044
+ cell.message.parts[cell.execPartIndex] = stopped;
13045
+ if (!cell.keepExecRow) {
13046
+ dropPart(cell.message, cell.execPartIndex);
13047
+ for (const call of cell.sourceCalls) {
13048
+ if (!call.replaced)
13049
+ dropPart(cell.message, call.partIndex);
13050
+ }
13051
+ return;
13052
+ }
13053
+ }
13002
13054
  if (cell.sawMcpRecord) {
13003
13055
  for (const call of cell.sourceCalls) {
13004
13056
  if (!call.replaced)
@@ -14024,7 +14076,7 @@ async function publishAssistantRows(context, state, turn, options) {
14024
14076
  }
14025
14077
 
14026
14078
  // ../../bridges/codex-bridge/src/app-server-runtime-sessions.ts
14027
- import { createHash as createHash9 } from "node:crypto";
14079
+ import { createHash as createHash9, randomUUID as randomUUID6 } from "node:crypto";
14028
14080
 
14029
14081
  // ../protocol/src/agent-mail.ts
14030
14082
  var AGENT_MAIL_MAX_BODY_BYTES = 32 * 1024;
@@ -14087,9 +14139,9 @@ var ORKESTRATOR_ARTIFACT_MCP_SERVER_NAME = "orkestrator-artifacts";
14087
14139
  var ORKESTRATOR_ARTIFACT_MCP_PATH = "/artifact-mcp";
14088
14140
 
14089
14141
  // ../../bridges/codex-bridge/src/engine/app-server-engine.ts
14090
- import { open as open5 } from "node:fs/promises";
14142
+ import { open as open6 } from "node:fs/promises";
14091
14143
  import { createHash as createHash8 } from "node:crypto";
14092
- import { isAbsolute as isAbsolute6, join as join13 } from "node:path";
14144
+ import { isAbsolute as isAbsolute7, join as join13 } from "node:path";
14093
14145
 
14094
14146
  // ../../bridges/codex-bridge/src/app-server/process-supervisor.ts
14095
14147
  import { execFile as execFile3, spawn as spawn3 } from "node:child_process";
@@ -16724,6 +16776,7 @@ var IGNORED_METHODS = new Set([
16724
16776
  "thread/project/updated",
16725
16777
  "thread/queue/changed",
16726
16778
  "thread/prediction/updated",
16779
+ "thread/readState/changed",
16727
16780
  "autoApprovalReview/strictReviewRequired",
16728
16781
  "mcpServer/event/stream/notification",
16729
16782
  "thread/reverted",
@@ -16828,6 +16881,7 @@ function reduceNotification(notification, generation, handle) {
16828
16881
  turns: [
16829
16882
  {
16830
16883
  turnId,
16884
+ at: new Date().toISOString(),
16831
16885
  inputTokens: num(last.inputTokens),
16832
16886
  outputTokens: num(last.outputTokens),
16833
16887
  cacheReadTokens: num(last.cachedInputTokens),
@@ -18019,7 +18073,7 @@ function coordinatorCapabilityPolicy(mode) {
18019
18073
 
18020
18074
  // ../../bridges/codex-bridge/src/codex-config.ts
18021
18075
  import { realpathSync } from "node:fs";
18022
- import { isAbsolute as isAbsolute5 } from "node:path";
18076
+ import { isAbsolute as isAbsolute6 } from "node:path";
18023
18077
  var CODEX_MAX_CONCURRENT_THREADS_ENV = "CODEX_MAX_CONCURRENT_THREADS_PER_SESSION";
18024
18078
  var DEFAULT_CODEX_MAX_CONCURRENT_THREADS = 5;
18025
18079
  var ORKESTRATOR_AGENT_MCP_URL_ENV = "ORKESTRATOR_AGENT_MCP_URL";
@@ -18039,7 +18093,7 @@ function codexNetworkDeniedConfig(policy) {
18039
18093
  };
18040
18094
  }
18041
18095
  function codexProjectTrustConfig(policy, cwd) {
18042
- if (!policy || policy.projectResources || !cwd || !isAbsolute5(cwd))
18096
+ if (!policy || policy.projectResources || !cwd || !isAbsolute6(cwd))
18043
18097
  return {};
18044
18098
  const roots = new Set([cwd]);
18045
18099
  try {
@@ -18062,7 +18116,7 @@ function codexProjectSkillsConfig(listed) {
18062
18116
  const { path, scope, enabled } = skill ?? {};
18063
18117
  if (enabled === true && typeof scope === "string" && KEPT_SKILL_SCOPES.has(scope))
18064
18118
  continue;
18065
- if (typeof path !== "string" || !isAbsolute5(path)) {
18119
+ if (typeof path !== "string" || !isAbsolute6(path)) {
18066
18120
  throw new Error("Codex listed a project skill without a path it can be disabled by");
18067
18121
  }
18068
18122
  disabled.add(path);
@@ -18089,6 +18143,14 @@ function codexReadOnlyTurnPolicy(policy) {
18089
18143
  const denied = base.capabilityPolicy?.deny ?? [];
18090
18144
  return { ...base, capabilityPolicy: { deny: [...denied, "network"] } };
18091
18145
  }
18146
+ function codexToolHistoryGaps(config) {
18147
+ if (!config || typeof config !== "object" || Array.isArray(config)) {
18148
+ return ["config/read returned no configuration"];
18149
+ }
18150
+ const features = config.features;
18151
+ const value = features && typeof features === "object" && !Array.isArray(features) ? features.incremental_tools : undefined;
18152
+ return value === false ? [] : [`features.incremental_tools is ${JSON.stringify(value ?? null)}, not false`];
18153
+ }
18092
18154
  function codexHostedNetworkLockGaps(config, expected = "disabled") {
18093
18155
  if (!config || typeof config !== "object" || Array.isArray(config)) {
18094
18156
  return ["config/read returned no configuration"];
@@ -18102,6 +18164,7 @@ function codexHostedNetworkLockGaps(config, expected = "disabled") {
18102
18164
  if (features && typeof features === "object" && features.standalone_web_search === true) {
18103
18165
  gaps.push("features.standalone_web_search is on");
18104
18166
  }
18167
+ gaps.push(...codexToolHistoryGaps(config));
18105
18168
  return gaps;
18106
18169
  }
18107
18170
  function isCoordinatorReadOnlyEnvironment(env) {
@@ -18128,7 +18191,8 @@ function codexAppServerConfigOverrides(env = process.env) {
18128
18191
  const childLimit = resolveCodexMaxConcurrentThreads(env[CODEX_MAX_CONCURRENT_THREADS_ENV]);
18129
18192
  const overrides = {
18130
18193
  "agents.max_concurrent_threads_per_session": String(childLimit),
18131
- "features.multi_agent_v2.max_concurrent_threads_per_session": String(childLimit + 1)
18194
+ "features.multi_agent_v2.max_concurrent_threads_per_session": String(childLimit + 1),
18195
+ "features.incremental_tools": "false"
18132
18196
  };
18133
18197
  const rawUrl = env[ORKESTRATOR_AGENT_MCP_URL_ENV]?.trim();
18134
18198
  const token = env[ORKESTRATOR_AGENT_MCP_TOKEN_ENV]?.trim();
@@ -18137,11 +18201,11 @@ function codexAppServerConfigOverrides(env = process.env) {
18137
18201
  const readableRuntimeRoot = env.CODEX_BRIDGE_READABLE_RUNTIME_ROOT?.trim();
18138
18202
  const projectRoot = env.CWD?.trim();
18139
18203
  const attachmentRoot = env[BRIDGE_ATTACHMENT_ROOT_ENV]?.trim();
18140
- if (!permissionProfile || !/^[A-Za-z0-9_-]+$/.test(permissionProfile) || !readableRuntimeRoot || !isAbsolute5(readableRuntimeRoot) || !projectRoot || !isAbsolute5(projectRoot)) {
18204
+ if (!permissionProfile || !/^[A-Za-z0-9_-]+$/.test(permissionProfile) || !readableRuntimeRoot || !isAbsolute6(readableRuntimeRoot) || !projectRoot || !isAbsolute6(projectRoot)) {
18141
18205
  throw new Error("Coordinator Codex permission profile configuration is invalid");
18142
18206
  }
18143
18207
  overrides.default_permissions = JSON.stringify(permissionProfile);
18144
- overrides[`permissions.${permissionProfile}.filesystem`] = `{ ":root" = "deny", ":minimal" = "read", ":tmpdir" = "deny", ` + `":slash_tmp" = "deny", ${JSON.stringify(readableRuntimeRoot)} = "read", ` + (attachmentRoot && isAbsolute5(attachmentRoot) ? `${JSON.stringify(attachmentRoot)} = "read", ` : "") + `":workspace_roots" = { "." = "read" } }`;
18208
+ 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" } }`;
18145
18209
  overrides[`permissions.${permissionProfile}.network.enabled`] = "false";
18146
18210
  overrides.web_search = JSON.stringify(codexNetworkDeniedSearchMode(codexCoordinatorPolicy(env)));
18147
18211
  overrides["features.standalone_web_search"] = "false";
@@ -18207,6 +18271,106 @@ function codexAppServerConfigOverrides(env = process.env) {
18207
18271
  return overrides;
18208
18272
  }
18209
18273
 
18274
+ // ../../bridges/codex-bridge/src/sessions/tool-history-guard.ts
18275
+ import { open as open5 } from "node:fs/promises";
18276
+ var TOOL_HISTORY_MARKER = '"type":"additional_tools"';
18277
+ var MAX_TOOL_HISTORY_LINEAGE_DEPTH = 32;
18278
+ async function rolloutDeclaresToolHistory(path, end) {
18279
+ const handle = await open5(path, "r");
18280
+ try {
18281
+ const size = (await handle.stat()).size;
18282
+ const limit = end === undefined ? size : Math.min(end, size);
18283
+ const marker = Buffer.from(TOOL_HISTORY_MARKER);
18284
+ const buffer = Buffer.allocUnsafe(ROLLOUT_CHUNK_BYTES + marker.length);
18285
+ let carried = 0;
18286
+ for (let position = 0;position < limit; ) {
18287
+ const length = Math.min(ROLLOUT_CHUNK_BYTES, limit - position);
18288
+ const { bytesRead } = await handle.read(buffer, carried, length, position);
18289
+ if (bytesRead <= 0)
18290
+ break;
18291
+ const filled = carried + bytesRead;
18292
+ if (buffer.subarray(0, filled).includes(marker))
18293
+ return true;
18294
+ position += bytesRead;
18295
+ carried = Math.min(marker.length - 1, filled);
18296
+ buffer.copy(buffer, 0, filled - carried, filled);
18297
+ }
18298
+ return false;
18299
+ } finally {
18300
+ await handle.close();
18301
+ }
18302
+ }
18303
+ async function historyBase(path) {
18304
+ const handle = await open5(path, "r");
18305
+ try {
18306
+ const chunks = [];
18307
+ let filled = 0;
18308
+ let complete = false;
18309
+ while (filled < MAX_ROLLOUT_RECORD_BYTES) {
18310
+ const chunk = Buffer.allocUnsafe(ROLLOUT_CHUNK_BYTES);
18311
+ const { bytesRead } = await handle.read(chunk, 0, chunk.length, filled);
18312
+ if (bytesRead <= 0) {
18313
+ complete = true;
18314
+ break;
18315
+ }
18316
+ const newline = chunk.subarray(0, bytesRead).indexOf(10);
18317
+ chunks.push(chunk.subarray(0, newline >= 0 ? newline : bytesRead));
18318
+ filled += bytesRead;
18319
+ if (newline >= 0) {
18320
+ complete = true;
18321
+ break;
18322
+ }
18323
+ }
18324
+ if (!complete)
18325
+ return "unreadable";
18326
+ const line = Buffer.concat(chunks).toString("utf8");
18327
+ if (!line.trim())
18328
+ return null;
18329
+ let record;
18330
+ try {
18331
+ record = JSON.parse(line);
18332
+ } catch {
18333
+ return "unreadable";
18334
+ }
18335
+ if (record.type !== "session_meta" || !record.payload)
18336
+ return null;
18337
+ const base = record.payload.history_base;
18338
+ if (base && typeof base === "object") {
18339
+ if (typeof base.thread_id !== "string")
18340
+ return "unreadable";
18341
+ return {
18342
+ threadId: base.thread_id,
18343
+ ...typeof base.end_byte_offset === "number" ? { endByte: base.end_byte_offset } : {}
18344
+ };
18345
+ }
18346
+ return null;
18347
+ } finally {
18348
+ await handle.close();
18349
+ }
18350
+ }
18351
+ async function threadToolHistoryVerdict(rolloutPath, resolveRollout) {
18352
+ let path = rolloutPath;
18353
+ let end;
18354
+ for (let depth = 0;depth <= MAX_TOOL_HISTORY_LINEAGE_DEPTH; depth += 1) {
18355
+ if (!path)
18356
+ return "unknown";
18357
+ try {
18358
+ if (await rolloutDeclaresToolHistory(path, end))
18359
+ return "declares";
18360
+ const base = await historyBase(path);
18361
+ if (base === "unreadable")
18362
+ return "unknown";
18363
+ if (!base)
18364
+ return "clean";
18365
+ end = base.endByte;
18366
+ path = await resolveRollout(base.threadId);
18367
+ } catch {
18368
+ return "unknown";
18369
+ }
18370
+ }
18371
+ return "unknown";
18372
+ }
18373
+
18210
18374
  // ../../bridges/codex-bridge/src/engine/mcp-tool-availability.ts
18211
18375
  var MCP_TOOL_AVAILABILITY_TIMEOUT_MS = 5000;
18212
18376
  var MCP_TOOL_AVAILABILITY_POLL_MS = 100;
@@ -18627,7 +18791,7 @@ function signedOutAccountRead(error) {
18627
18791
  }
18628
18792
  async function readStoredLoginFingerprint(codexHome) {
18629
18793
  try {
18630
- const file = await open5(join13(codexHome, "auth.json"), "r");
18794
+ const file = await open6(join13(codexHome, "auth.json"), "r");
18631
18795
  let raw;
18632
18796
  try {
18633
18797
  const bytes = Buffer.alloc(65537);
@@ -18676,6 +18840,9 @@ var MAX_UNKNOWN_METHODS = 16;
18676
18840
  var MAX_UNKNOWN_METHOD_LENGTH = 128;
18677
18841
  var MAX_ACCOUNT_PROOF_TURNS = 1024;
18678
18842
  var MAX_ACCOUNT_PROOF_ID_LENGTH = 256;
18843
+ function deniesHostedSearch(config) {
18844
+ return Object.keys(codexNetworkDeniedConfig(config.policy)).length > 0 && codexNetworkDeniedSearchMode(config.policy) === "disabled";
18845
+ }
18679
18846
 
18680
18847
  class AppServerEngine {
18681
18848
  options;
@@ -18702,6 +18869,7 @@ class AppServerEngine {
18702
18869
  accountProofInvalidatedSequence = 0;
18703
18870
  accountProofTurns = new Map;
18704
18871
  hostedNetworkLockVerifiedGeneration = null;
18872
+ toolHistoryLockVerifiedGeneration = null;
18705
18873
  accountReloadFingerprint;
18706
18874
  pendingAccountReloadFingerprint;
18707
18875
  accountCheckSequence = 0;
@@ -18742,6 +18910,7 @@ class AppServerEngine {
18742
18910
  this.clearPendingSignIn();
18743
18911
  this.loginSequence++;
18744
18912
  this.hostedNetworkLockVerifiedGeneration = null;
18913
+ this.toolHistoryLockVerifiedGeneration = null;
18745
18914
  this.rejectAllWaiters();
18746
18915
  this.router.abandonGeneration(generation);
18747
18916
  }
@@ -19179,8 +19348,47 @@ class AppServerEngine {
19179
19348
  this.hostedNetworkLockVerifiedGeneration = generation;
19180
19349
  return generation;
19181
19350
  }
19351
+ async assertToolHistoryLock(generation, config) {
19352
+ if (!deniesHostedSearch(config) || config.permissionProfile)
19353
+ return;
19354
+ if (this.toolHistoryLockVerifiedGeneration === generation)
19355
+ return;
19356
+ const result = await this.supervisor.requestOnGeneration(generation, "config/read", { cwd: config.cwd ?? this.options.cwd });
19357
+ const gaps = codexToolHistoryGaps(result?.config);
19358
+ if (gaps.length > 0) {
19359
+ throw new Error(`Codex did not confirm that tool declarations stay out of thread history (${gaps.join("; ")}), so this session, which has web search off, was refused`);
19360
+ }
19361
+ if (!this.supervisor.isReady() || this.supervisor.getGeneration() !== generation) {
19362
+ throw new Error("Codex exited during tool history verification");
19363
+ }
19364
+ this.toolHistoryLockVerifiedGeneration = generation;
19365
+ }
19366
+ async assertNoToolHistory(threadId, config, release) {
19367
+ if (!deniesHostedSearch(config))
19368
+ return;
19369
+ const verdict = await (this.options.toolHistoryVerdict ?? ((id) => this.readToolHistory(id)))(threadId).catch(() => "unknown");
19370
+ if (verdict === "clean")
19371
+ return;
19372
+ if (release) {
19373
+ for (const [handle, binding] of this.bindings) {
19374
+ if (binding.threadId === threadId)
19375
+ this.bindings.delete(handle);
19376
+ }
19377
+ await this.supervisor.request("thread/unsubscribe", { threadId }).catch(() => {
19378
+ return;
19379
+ });
19380
+ }
19381
+ throw new Error(verdict === "declares" ? "This Codex thread was offered web search in an earlier turn and still declares it in its history, so a turn with web search off was refused. Start a new session for it." : "Could not confirm that this Codex thread's history declares no web search, so a turn with web search off was refused.");
19382
+ }
19383
+ async readToolHistory(threadId) {
19384
+ const codexHome = this.options.codexHome;
19385
+ const resolve = async (id) => this.rolloutPaths.get(id) ?? findTranscriptPath(id, () => listTranscriptPaths(codexHome), { allowNegativeCache: false });
19386
+ const path = await resolve(threadId);
19387
+ return path ? threadToolHistoryVerdict(path, resolve) : "unknown";
19388
+ }
19182
19389
  async startThread(options) {
19183
19390
  const generation = await this.assertHostedNetworkLock(options.config);
19391
+ await this.assertToolHistoryLock(generation, options.config);
19184
19392
  const extra = await this.projectResourceThreadConfig(generation, options.config);
19185
19393
  const response = await this.supervisor.requestOnGeneration(generation, "thread/start", this.toThreadParams(options.config, extra));
19186
19394
  this.assertPermissionProfile(response.activePermissionProfile, options.config);
@@ -19188,10 +19396,12 @@ class AppServerEngine {
19188
19396
  }
19189
19397
  async resumeThread(threadId, options) {
19190
19398
  const generation = await this.assertHostedNetworkLock(options.config);
19399
+ await this.assertToolHistoryLock(generation, options.config);
19191
19400
  const extra = await this.projectResourceThreadConfig(generation, options.config);
19192
19401
  const response = await this.supervisor.requestOnGeneration(generation, "thread/resume", { threadId, ...this.toThreadParams(options.config, extra) });
19193
19402
  this.assertPermissionProfile(response.activePermissionProfile, options.config);
19194
19403
  const thread = this.bindThread(response.thread, options.config, response.model);
19404
+ await this.assertNoToolHistory(thread.id ?? threadId, options.config, true);
19195
19405
  thread.turns = this.extractTurns(response.thread);
19196
19406
  return thread;
19197
19407
  }
@@ -19207,6 +19417,8 @@ class AppServerEngine {
19207
19417
  }
19208
19418
  async forkThread(threadId, config, lastTurnId) {
19209
19419
  const generation = await this.assertHostedNetworkLock(config);
19420
+ await this.assertToolHistoryLock(generation, config);
19421
+ await this.assertNoToolHistory(threadId, config, false);
19210
19422
  const extra = await this.projectResourceThreadConfig(generation, config);
19211
19423
  const response = await this.supervisor.requestOnGeneration(generation, "thread/fork", {
19212
19424
  threadId,
@@ -19484,7 +19696,7 @@ class AppServerEngine {
19484
19696
  thread.model = confirmedModel.trim();
19485
19697
  }
19486
19698
  if (thread.id) {
19487
- if (typeof raw.path === "string" && isAbsolute6(raw.path)) {
19699
+ if (typeof raw.path === "string" && isAbsolute7(raw.path)) {
19488
19700
  this.rolloutPaths.set(thread.id, raw.path);
19489
19701
  }
19490
19702
  this.bindings.set(thread.id, {
@@ -20092,6 +20304,7 @@ class ThreadRegistry {
20092
20304
 
20093
20305
  // ../../bridges/codex-bridge/src/app-server-runtime-sessions.ts
20094
20306
  var SESSION_CLOSING_ERROR = "Session is closing";
20307
+ var ACTIVITY_PROCESS = randomUUID6();
20095
20308
 
20096
20309
  class CodexRuntimeSessions {
20097
20310
  ctx;
@@ -20949,7 +21162,8 @@ class CodexRuntimeSessions {
20949
21162
  return { activity: "missing" };
20950
21163
  return {
20951
21164
  activity,
20952
- ...session.asyncQuestionItemIds.length > 0 ? { asyncQuestionItemIds: [...session.asyncQuestionItemIds] } : {}
21165
+ ...session.asyncQuestionItemIds.length > 0 ? { asyncQuestionItemIds: [...session.asyncQuestionItemIds] } : {},
21166
+ contentRevision: `${ACTIVITY_PROCESS}:${session.contentEpoch}.${session.messageRevision}:${session.asyncQuestionItemIds.length}`
20953
21167
  };
20954
21168
  }
20955
21169
  hasParkedInput(session) {
@@ -21440,7 +21654,7 @@ function parseCommandToken(text, sigils = ["/"]) {
21440
21654
 
21441
21655
  // ../../bridges/codex-bridge/src/prompts/template-discovery.ts
21442
21656
  import { createHash as createHash10 } from "node:crypto";
21443
- import { open as open6, readdir as readdir3, stat as stat7 } from "node:fs/promises";
21657
+ import { open as open7, readdir as readdir3, stat as stat7 } from "node:fs/promises";
21444
21658
  import { join as join14 } from "node:path";
21445
21659
 
21446
21660
  // ../../bridges/codex-bridge/src/prompts/template-format.ts
@@ -21595,7 +21809,7 @@ function isReservedName(name) {
21595
21809
  var metadataCache = new Map;
21596
21810
  var METADATA_CACHE_MAX = 1024;
21597
21811
  async function readBounded(absolutePath, maxBytes) {
21598
- const handle = await open6(absolutePath, "r");
21812
+ const handle = await open7(absolutePath, "r");
21599
21813
  try {
21600
21814
  const info = await handle.stat();
21601
21815
  if (!info.isFile())
@@ -21842,7 +22056,7 @@ async function loadTemplateBody(entry, limits = TEMPLATE_LIMITS) {
21842
22056
  }
21843
22057
 
21844
22058
  // ../../bridges/codex-bridge/src/commands/skill-inventory.ts
21845
- import { isAbsolute as isAbsolute7 } from "node:path";
22059
+ import { isAbsolute as isAbsolute8 } from "node:path";
21846
22060
  var SKILL_INVENTORY_LIMITS = Object.freeze({
21847
22061
  maxSkills: 512,
21848
22062
  maxDescriptionBytes: 1000,
@@ -21892,7 +22106,7 @@ function normalizeSkillsList(raw, context) {
21892
22106
  }
21893
22107
  const name = nonEmpty(candidate.name);
21894
22108
  const path = typeof candidate.path === "string" ? candidate.path : undefined;
21895
- if (!name || !usableSkillName(name) || !path || !isAbsolute7(path)) {
22109
+ if (!name || !usableSkillName(name) || !path || !isAbsolute8(path)) {
21896
22110
  truncated = true;
21897
22111
  continue;
21898
22112
  }
@@ -26082,6 +26296,21 @@ var codexPathOverride = process.env.CODEX_PATH || "codex";
26082
26296
  var BRIDGE_VERSION = "1.0.0";
26083
26297
  var execFile4 = promisify2(execFileCallback2);
26084
26298
  var subscribers = new Set;
26299
+ var observers = new Set;
26300
+ function observationFrameData(sessionId) {
26301
+ return JSON.stringify(sessionId === undefined ? {} : { sessionId });
26302
+ }
26303
+ function notifyObservers(event, revision) {
26304
+ for (const observer of observers) {
26305
+ try {
26306
+ Promise.resolve(observer(event, revision, "{}")).catch((error) => {
26307
+ console.error("[codex-bridge] Failed to notify SSE observer:", error);
26308
+ });
26309
+ } catch (error) {
26310
+ console.error("[codex-bridge] Failed to notify SSE observer:", error);
26311
+ }
26312
+ }
26313
+ }
26085
26314
  var MAX_REPLAY_SSE_BYTES = 4 * 1024 * 1024;
26086
26315
  var REPLAY_IDLE_RETENTION_MS = 60000;
26087
26316
  var SUPERSEDED_FRAME_BYTES = 64;
@@ -26349,7 +26578,7 @@ function retainedMessageId(type, payload) {
26349
26578
  }
26350
26579
  function emit(event) {
26351
26580
  if (!replayRetentionEnabled) {
26352
- eventRing.advance();
26581
+ notifyObservers(event, eventRing.advance());
26353
26582
  return;
26354
26583
  }
26355
26584
  const sessionId = event.sessionId;
@@ -26376,6 +26605,7 @@ function emit(event) {
26376
26605
  serialize: () => eager ?? serializeSseEventData({ sessionId, data: payload }),
26377
26606
  bytes
26378
26607
  });
26608
+ notifyObservers(event, revision);
26379
26609
  for (const subscriber of subscribers) {
26380
26610
  try {
26381
26611
  Promise.resolve(subscriber(event, revision, eager ?? "{}")).catch((error) => {
@@ -26704,7 +26934,7 @@ app.get("/global/health", (c) => {
26704
26934
  notificationDepth: health.notificationQueueDepth,
26705
26935
  notificationHighWaterMark: health.notificationQueueHighWaterMark
26706
26936
  },
26707
- events: { ...eventRing.getStats(), subscribers: subscribers.size }
26937
+ events: { ...eventRing.getStats(), subscribers: subscribers.size, observers: observers.size }
26708
26938
  }, terminal ? 503 : 200);
26709
26939
  });
26710
26940
  app.get("/global/auth-check", (c) => {
@@ -27219,6 +27449,7 @@ app.delete("/session/:id", async (c) => {
27219
27449
  app.get("/event/subscribe", (c) => {
27220
27450
  const cursor = parseEventCursor(c.req.query("since")) ?? parseEventCursor(c.req.header("last-event-id"));
27221
27451
  const sessionFilter = c.req.query("sessionId")?.trim() || null;
27452
+ const observe = c.req.query("observe") === "1";
27222
27453
  return streamSSE(c, async (stream) => {
27223
27454
  let open = true;
27224
27455
  let resolveConnectionClosed;
@@ -27244,10 +27475,10 @@ app.get("/event/subscribe", (c) => {
27244
27475
  return {
27245
27476
  event: event.type,
27246
27477
  id: String(revision),
27247
- data: serializeData()
27478
+ data: observe ? observationFrameData(event.sessionId) : serializeData()
27248
27479
  };
27249
27480
  };
27250
- const releaseReplayRetention = acquireReplayRetention();
27481
+ const releaseReplayRetention = observe ? () => {} : acquireReplayRetention();
27251
27482
  const anchorRevision = cursor ?? eventRing.latestRevision;
27252
27483
  let buffered = [];
27253
27484
  let highestSent = anchorRevision;
@@ -27269,7 +27500,7 @@ app.get("/event/subscribe", (c) => {
27269
27500
  highestSent = revision;
27270
27501
  await writeWhileOpen(frameFor(event, revision, () => serializedData));
27271
27502
  };
27272
- subscribers.add(listener);
27503
+ (observe ? observers : subscribers).add(listener);
27273
27504
  let keepalive;
27274
27505
  try {
27275
27506
  if (sseRouteTestHooks?.afterSubscriberRegistered) {
@@ -27283,7 +27514,8 @@ app.get("/event/subscribe", (c) => {
27283
27514
  status: "connected",
27284
27515
  timestamp: new Date().toISOString(),
27285
27516
  revision: replay?.latestRevision ?? anchorRevision,
27286
- replayed: replay?.complete === true ? replay.events.length : 0
27517
+ replayed: replay?.complete === true ? replay.events.length : 0,
27518
+ ...observe ? { observe: true } : {}
27287
27519
  })
27288
27520
  });
27289
27521
  if (replay && !replay.complete) {
@@ -27329,6 +27561,8 @@ app.get("/event/subscribe", (c) => {
27329
27561
  }
27330
27562
  buffered = null;
27331
27563
  keepalive = startSseKeepalive(writeWhileOpen, 5000, () => {
27564
+ if (observe)
27565
+ return false;
27332
27566
  if (!sessionFilter)
27333
27567
  return true;
27334
27568
  const activity = appServerRuntime.getActivity(sessionFilter);
@@ -27348,6 +27582,7 @@ app.get("/event/subscribe", (c) => {
27348
27582
  if (keepalive)
27349
27583
  clearInterval(keepalive);
27350
27584
  subscribers.delete(listener);
27585
+ observers.delete(listener);
27351
27586
  releaseReplayRetention();
27352
27587
  }
27353
27588
  });