orkestrator 2.26.0-alpha → 2.26.1-alpha

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.
@@ -3984,11 +3984,6 @@ var LEGACY_ENABLED_AGENT_PLATFORMS = Object.freeze([
3984
3984
  // ../protocol/src/native-agent-transcript-patch.ts
3985
3985
  var FORBIDDEN_KEYS = new Set(["__proto__", "constructor", "prototype"]);
3986
3986
 
3987
- // ../protocol/src/native-agent-policy-effects.ts
3988
- function appendNativeAgentPolicyNote(policy, note) {
3989
- return { ...policy, note: policy.note ? `${policy.note} ${note}` : note };
3990
- }
3991
-
3992
3987
  // ../protocol/src/native-agent-steer-rejection.ts
3993
3988
  var MAX_STEER_JOURNAL_BYTES = 1024 * 1024 * 1024;
3994
3989
  // ../protocol/src/native-agent.ts
@@ -17200,7 +17195,7 @@ function reduceHistoricalTurns(turns, generation, threadId, handle) {
17200
17195
  return { events, unsupportedItemTypes };
17201
17196
  }
17202
17197
 
17203
- // ../../bridges/codex-bridge/src/app-server/redaction.ts
17198
+ // ../protocol/src/secret-redaction.ts
17204
17199
  var TEXT_RULES = [
17205
17200
  {
17206
17201
  pattern: /([a-z][a-z0-9+.-]*:\/\/)[^/\s:@]+(?::[^/\s@]*)?@/gi,
@@ -18023,6 +18018,7 @@ function coordinatorCapabilityPolicy(mode) {
18023
18018
  }
18024
18019
 
18025
18020
  // ../../bridges/codex-bridge/src/codex-config.ts
18021
+ import { realpathSync } from "node:fs";
18026
18022
  import { isAbsolute as isAbsolute5 } from "node:path";
18027
18023
  var CODEX_MAX_CONCURRENT_THREADS_ENV = "CODEX_MAX_CONCURRENT_THREADS_PER_SESSION";
18028
18024
  var DEFAULT_CODEX_MAX_CONCURRENT_THREADS = 5;
@@ -18042,6 +18038,43 @@ function codexNetworkDeniedConfig(policy) {
18042
18038
  "features.standalone_web_search": false
18043
18039
  };
18044
18040
  }
18041
+ function codexProjectTrustConfig(policy, cwd) {
18042
+ if (!policy || policy.projectResources || !cwd || !isAbsolute5(cwd))
18043
+ return {};
18044
+ const roots = new Set([cwd]);
18045
+ try {
18046
+ roots.add(realpathSync(cwd));
18047
+ } catch {}
18048
+ return {
18049
+ projects: Object.fromEntries([...roots].map((root) => [root, { trust_level: "untrusted" }]))
18050
+ };
18051
+ }
18052
+ var MAX_CODEX_DISABLED_SKILLS = 1024;
18053
+ var KEPT_SKILL_SCOPES = new Set(["user", "system", "admin"]);
18054
+ function codexProjectSkillsConfig(listed) {
18055
+ const data = listed && typeof listed === "object" && Array.isArray(listed.data) ? listed.data : undefined;
18056
+ if (!data)
18057
+ throw new Error("Codex skills/list returned no skill inventory");
18058
+ const disabled = new Set;
18059
+ for (const entry of data) {
18060
+ const skills = entry?.skills;
18061
+ for (const skill of Array.isArray(skills) ? skills : []) {
18062
+ const { path, scope, enabled } = skill ?? {};
18063
+ if (enabled === true && typeof scope === "string" && KEPT_SKILL_SCOPES.has(scope))
18064
+ continue;
18065
+ if (typeof path !== "string" || !isAbsolute5(path)) {
18066
+ throw new Error("Codex listed a project skill without a path it can be disabled by");
18067
+ }
18068
+ disabled.add(path);
18069
+ }
18070
+ }
18071
+ if (disabled.size > MAX_CODEX_DISABLED_SKILLS) {
18072
+ throw new Error(`Codex listed more than ${MAX_CODEX_DISABLED_SKILLS} skills to disable`);
18073
+ }
18074
+ if (disabled.size === 0)
18075
+ return {};
18076
+ return { skills: { config: [...disabled].map((path) => ({ path, enabled: false })) } };
18077
+ }
18045
18078
  var READ_ONLY_TURN_FALLBACK_POLICY = Object.freeze({
18046
18079
  id: "interactive-host",
18047
18080
  sandbox: "provider",
@@ -18537,6 +18570,8 @@ function mcpFailureReason(notice) {
18537
18570
  return reason || undefined;
18538
18571
  }
18539
18572
  var MAX_SKILL_LOAD_NOTICES = 16;
18573
+ var MCP_STATUS_PAGE_SIZE = 100;
18574
+ var MCP_STATUS_MAX_PAGES = 10;
18540
18575
  function skillPathFingerprint(path) {
18541
18576
  let hash = 2166136261;
18542
18577
  for (let index = 0;index < path.length; index += 1) {
@@ -19063,10 +19098,30 @@ class AppServerEngine {
19063
19098
  }
19064
19099
  throw new Error("Codex model catalogue exceeded its page limit");
19065
19100
  }
19066
- toThreadParams(config) {
19101
+ async projectResourceThreadConfig(generation, config) {
19102
+ if (!config.policy || config.policy.projectResources)
19103
+ return {};
19104
+ let listed;
19105
+ try {
19106
+ listed = await this.supervisor.requestOnGeneration(generation, "skills/list", {
19107
+ cwds: [config.cwd ?? this.options.cwd],
19108
+ forceReload: true
19109
+ });
19110
+ } catch (error) {
19111
+ if (error instanceof AppServerRpcError && error.code === JSON_RPC_METHOD_NOT_FOUND) {
19112
+ throw new Error("Codex cannot enforce disabled project resources without skills/list");
19113
+ }
19114
+ throw error;
19115
+ }
19116
+ return codexProjectSkillsConfig(listed);
19117
+ }
19118
+ toThreadParams(config, extraConfig = {}) {
19067
19119
  const approveAgentMcpTools = config.policy?.approvals === "auto-approve" && (config.approvalPolicy ?? "never") === "never" && config.sandbox !== "read-only" || config.policy?.id === "coordinator-read-only" && Boolean(config.permissionProfile) || Boolean(config.workflowResultTool && config.agentMcp);
19120
+ const cwd = config.cwd ?? this.options.cwd;
19068
19121
  const threadConfig = {
19069
19122
  ...codexNetworkDeniedConfig(config.policy),
19123
+ ...codexProjectTrustConfig(config.policy, cwd),
19124
+ ...extraConfig,
19070
19125
  ...config.agentMcp ? {
19071
19126
  ...config.agentMcp.design ? {
19072
19127
  "mcp_servers.orkestrator-design": {
@@ -19095,7 +19150,7 @@ class AppServerEngine {
19095
19150
  } : {}
19096
19151
  };
19097
19152
  return {
19098
- cwd: config.cwd ?? this.options.cwd,
19153
+ cwd,
19099
19154
  approvalPolicy: config.approvalPolicy ?? "never",
19100
19155
  approvalsReviewer: config.approvalsReviewer ?? "user",
19101
19156
  ...config.permissionProfile ? {} : {
@@ -19126,13 +19181,15 @@ class AppServerEngine {
19126
19181
  }
19127
19182
  async startThread(options) {
19128
19183
  const generation = await this.assertHostedNetworkLock(options.config);
19129
- const response = await this.supervisor.requestOnGeneration(generation, "thread/start", this.toThreadParams(options.config));
19184
+ const extra = await this.projectResourceThreadConfig(generation, options.config);
19185
+ const response = await this.supervisor.requestOnGeneration(generation, "thread/start", this.toThreadParams(options.config, extra));
19130
19186
  this.assertPermissionProfile(response.activePermissionProfile, options.config);
19131
19187
  return this.bindThread(response.thread, options.config, response.model);
19132
19188
  }
19133
19189
  async resumeThread(threadId, options) {
19134
19190
  const generation = await this.assertHostedNetworkLock(options.config);
19135
- const response = await this.supervisor.requestOnGeneration(generation, "thread/resume", { threadId, ...this.toThreadParams(options.config) });
19191
+ const extra = await this.projectResourceThreadConfig(generation, options.config);
19192
+ const response = await this.supervisor.requestOnGeneration(generation, "thread/resume", { threadId, ...this.toThreadParams(options.config, extra) });
19136
19193
  this.assertPermissionProfile(response.activePermissionProfile, options.config);
19137
19194
  const thread = this.bindThread(response.thread, options.config, response.model);
19138
19195
  thread.turns = this.extractTurns(response.thread);
@@ -19150,10 +19207,11 @@ class AppServerEngine {
19150
19207
  }
19151
19208
  async forkThread(threadId, config, lastTurnId) {
19152
19209
  const generation = await this.assertHostedNetworkLock(config);
19210
+ const extra = await this.projectResourceThreadConfig(generation, config);
19153
19211
  const response = await this.supervisor.requestOnGeneration(generation, "thread/fork", {
19154
19212
  threadId,
19155
19213
  ...lastTurnId ? { lastTurnId } : {},
19156
- ...this.toThreadParams(config)
19214
+ ...this.toThreadParams(config, extra)
19157
19215
  });
19158
19216
  this.assertPermissionProfile(response.activePermissionProfile, config);
19159
19217
  return this.bindThread(response.thread, config, response.model);
@@ -19193,11 +19251,7 @@ class AppServerEngine {
19193
19251
  }
19194
19252
  async getRuntimeHealth(threadId) {
19195
19253
  const [mcp, skills, hooks, rateLimits] = await Promise.allSettled([
19196
- this.supervisor.request("mcpServerStatus/list", {
19197
- limit: 100,
19198
- detail: "full",
19199
- ...threadId ? { threadId } : {}
19200
- }),
19254
+ this.listMcpServerStatus(threadId),
19201
19255
  this.supervisor.request("skills/list", {
19202
19256
  cwds: [this.options.cwd]
19203
19257
  }),
@@ -19260,12 +19314,29 @@ class AppServerEngine {
19260
19314
  rateLimits: allowlistRateLimits(rateLimitValue)
19261
19315
  };
19262
19316
  }
19317
+ async listMcpServerStatus(threadId) {
19318
+ const data = [];
19319
+ let cursor;
19320
+ let last = {};
19321
+ for (let page = 0;page < MCP_STATUS_MAX_PAGES; page += 1) {
19322
+ const response = await this.supervisor.request("mcpServerStatus/list", {
19323
+ ...cursor ? { cursor } : {},
19324
+ limit: MCP_STATUS_PAGE_SIZE,
19325
+ detail: "full",
19326
+ ...threadId ? { threadId } : {}
19327
+ });
19328
+ last = objectRecord(response);
19329
+ if (!Array.isArray(last.data))
19330
+ return page === 0 ? response : { ...last, data };
19331
+ data.push(...last.data);
19332
+ cursor = typeof last.nextCursor === "string" && last.nextCursor ? last.nextCursor : undefined;
19333
+ if (!cursor)
19334
+ break;
19335
+ }
19336
+ return { ...last, data };
19337
+ }
19263
19338
  async listMcpServers(threadId) {
19264
- const response = await this.supervisor.request("mcpServerStatus/list", {
19265
- limit: 100,
19266
- detail: "full",
19267
- ...threadId ? { threadId } : {}
19268
- });
19339
+ const response = await this.listMcpServerStatus(threadId);
19269
19340
  const root = objectRecord(response);
19270
19341
  if (!Array.isArray(root.data))
19271
19342
  return response;
@@ -19489,18 +19560,24 @@ class AppServerEngine {
19489
19560
  rolloutPath(threadId) {
19490
19561
  return this.rolloutPaths.get(threadId);
19491
19562
  }
19492
- async unsubscribeThread(handle) {
19563
+ async unsubscribeThread(handle, requireConfirmation = false) {
19493
19564
  const binding = this.bindings.get(handle);
19494
- this.accountProofTurns.delete(binding?.threadId ?? handle);
19495
- this.rolloutPaths.delete(binding?.threadId ?? handle);
19496
- this.bindings.delete(handle);
19497
- if (!binding)
19565
+ const threadId = binding?.threadId ?? handle;
19566
+ if (!binding && !requireConfirmation) {
19567
+ this.accountProofTurns.delete(threadId);
19568
+ this.rolloutPaths.delete(threadId);
19498
19569
  return;
19570
+ }
19499
19571
  try {
19500
- await this.supervisor.request("thread/unsubscribe", { threadId: binding.threadId });
19572
+ await this.supervisor.request("thread/unsubscribe", { threadId });
19501
19573
  } catch (error) {
19574
+ if (requireConfirmation)
19575
+ throw error;
19502
19576
  console.warn("[codex-bridge] thread/unsubscribe failed:", error instanceof Error ? error.message : error);
19503
19577
  }
19578
+ this.accountProofTurns.delete(threadId);
19579
+ this.rolloutPaths.delete(threadId);
19580
+ this.bindings.delete(handle);
19504
19581
  }
19505
19582
  async configureThread(handle, config) {
19506
19583
  const binding = this.bindings.get(handle);
@@ -20013,14 +20090,6 @@ class ThreadRegistry {
20013
20090
  }
20014
20091
  }
20015
20092
 
20016
- // ../../bridges/codex-bridge/src/codex-reported-policy.ts
20017
- var CODEX_PROJECT_TRUST_NOTE = "Codex loads a trusted project's own configuration, including its MCP servers and hooks, whatever this session's policy says.";
20018
- function codexReportedPolicy(policy) {
20019
- if (policy.projectResources || policy.id === "coordinator-read-only")
20020
- return policy;
20021
- return appendNativeAgentPolicyNote({ ...policy, projectResources: true }, CODEX_PROJECT_TRUST_NOTE);
20022
- }
20023
-
20024
20093
  // ../../bridges/codex-bridge/src/app-server-runtime-sessions.ts
20025
20094
  var SESSION_CLOSING_ERROR = "Session is closing";
20026
20095
 
@@ -20720,14 +20789,25 @@ class CodexRuntimeSessions {
20720
20789
  if (requestedModelChanged)
20721
20790
  attached.modelId = undefined;
20722
20791
  await this.ctx.options.engine.configureThread(attached.engineHandle, nextConfig);
20792
+ if (session.config.policy?.projectResources !== nextConfig.policy?.projectResources || (session.config.cwd ?? this.ctx.options.cwd) !== (nextConfig.cwd ?? this.ctx.options.cwd)) {
20793
+ await this.lifecycle.detachThread(attached, true);
20794
+ }
20723
20795
  }
20724
20796
  } catch (error) {
20797
+ if (attached) {
20798
+ await this.ctx.options.engine.configureThread(attached.engineHandle, session.config).catch(() => {
20799
+ return;
20800
+ });
20801
+ }
20725
20802
  if (attached && requestedModelChanged) {
20726
20803
  attached.modelId = previousConfirmedModel;
20727
20804
  }
20728
20805
  console.warn(`[codex-bridge] Could not apply configuration for session ${sessionId}:`, error instanceof Error ? error.message : error);
20729
20806
  return "unavailable";
20730
20807
  }
20808
+ if (session.config.policy?.projectResources !== nextConfig.policy?.projectResources) {
20809
+ this.ctx.commandCatalogue.markPolicyChanged();
20810
+ }
20731
20811
  session.config = nextConfig;
20732
20812
  session.configurationRevision += 1;
20733
20813
  this.ctx.registry.touch(sessionId);
@@ -20818,7 +20898,7 @@ class CodexRuntimeSessions {
20818
20898
  const error = context?.error ?? (phase === "failed" && session.restartFailure ? CODEX_RESTARTED_MID_TURN_MESSAGE : undefined);
20819
20899
  return {
20820
20900
  status: phaseToExternalStatus(phase),
20821
- ...session.config.policy ? { policy: codexReportedPolicy(session.config.policy) } : {},
20901
+ ...session.config.policy ? { policy: session.config.policy } : {},
20822
20902
  phase,
20823
20903
  title: session.title,
20824
20904
  error,
@@ -21003,7 +21083,7 @@ class CodexRuntimeSessions {
21003
21083
  ...session.config.reasoningEffort ? { modelReasoningEffort: session.config.reasoningEffort } : {},
21004
21084
  mode: session.config.mode,
21005
21085
  fastMode: session.config.serviceTier === "fast",
21006
- ...session.config.policy ? { policy: codexReportedPolicy(session.config.policy) } : {},
21086
+ ...session.config.policy ? { policy: session.config.policy } : {},
21007
21087
  durable: await this.isSessionConfigPersisted(session)
21008
21088
  };
21009
21089
  }
@@ -21855,7 +21935,13 @@ function normalizeSkillsList(raw, context) {
21855
21935
  fetchedAt: context.now
21856
21936
  };
21857
21937
  }
21858
- function skillUnavailability(binding) {
21938
+ function skillUnavailability(binding, projectResources = true) {
21939
+ if (!projectResources && !["user", "system", "admin"].includes(binding.scope)) {
21940
+ return {
21941
+ reason: "disabled",
21942
+ message: "This skill is disabled because project resources are off for this session."
21943
+ };
21944
+ }
21859
21945
  if (!binding.enabled) {
21860
21946
  return {
21861
21947
  reason: "disabled",
@@ -21870,8 +21956,8 @@ function skillUnavailability(binding) {
21870
21956
  }
21871
21957
  return;
21872
21958
  }
21873
- function skillCommandRow(binding) {
21874
- const unavailable = skillUnavailability(binding);
21959
+ function skillCommandRow(binding, projectResources = true) {
21960
+ const unavailable = skillUnavailability(binding, projectResources);
21875
21961
  return {
21876
21962
  name: `$${binding.name}`,
21877
21963
  insertText: `$${binding.name}`,
@@ -22065,7 +22151,10 @@ class CodexCommandCatalogue {
22065
22151
  onRefreshed: (read) => {
22066
22152
  this.skillsDigest = commandBindingRevision([
22067
22153
  String(read.fresh),
22068
- read.snapshot ? JSON.stringify([read.snapshot.truncated, read.snapshot.bindings.map(skillCommandRow)]) : "none"
22154
+ read.snapshot ? JSON.stringify([
22155
+ read.snapshot.truncated,
22156
+ read.snapshot.bindings.map((binding) => skillCommandRow(binding))
22157
+ ]) : "none"
22069
22158
  ]);
22070
22159
  this.bumpIfChanged();
22071
22160
  }
@@ -22083,6 +22172,9 @@ class CodexCommandCatalogue {
22083
22172
  this.compositeDigest = composite;
22084
22173
  this.revisionValue += 1;
22085
22174
  }
22175
+ markPolicyChanged() {
22176
+ this.revisionValue += 1;
22177
+ }
22086
22178
  markSkillsChanged() {
22087
22179
  this.skills.markChanged();
22088
22180
  }
@@ -22109,7 +22201,7 @@ class CodexCommandCatalogue {
22109
22201
  const templates = await this.scanTemplates();
22110
22202
  return [...BUILTINS.map(builtinRow), ...templates.effective.map(templateCommandRow)];
22111
22203
  }
22112
- async collect() {
22204
+ async collect(projectResources = true) {
22113
22205
  this.deferredBumps += 1;
22114
22206
  let templates;
22115
22207
  let skillRead;
@@ -22122,7 +22214,7 @@ class CodexCommandCatalogue {
22122
22214
  const candidates = [
22123
22215
  ...BUILTINS.map(builtinRow),
22124
22216
  ...templates.effective.map(templateCommandRow),
22125
- ...skillRead.snapshot?.bindings.map(skillCommandRow) ?? []
22217
+ ...skillRead.snapshot?.bindings.map((binding) => skillCommandRow(binding, projectResources)) ?? []
22126
22218
  ];
22127
22219
  const commands = [];
22128
22220
  let bytes = 0;
@@ -22144,8 +22236,8 @@ class CodexCommandCatalogue {
22144
22236
  skills: skillRead.snapshot
22145
22237
  };
22146
22238
  }
22147
- async list() {
22148
- const collected = await this.collect();
22239
+ async list(projectResources = true) {
22240
+ const collected = await this.collect(projectResources);
22149
22241
  this.listedTemplates = new Map(collected.templates.effective.map((entry) => [entry.id, entry.fingerprint]));
22150
22242
  return {
22151
22243
  catalogueVersion: NATIVE_AGENT_COMMAND_CATALOGUE_VERSION,
@@ -22167,7 +22259,7 @@ class CodexCommandCatalogue {
22167
22259
  }
22168
22260
  return { outcome: "reloaded" };
22169
22261
  }
22170
- async resolveSelected(command) {
22262
+ async resolveSelected(command, projectResources = true) {
22171
22263
  const stale = (name) => ({
22172
22264
  kind: "refused",
22173
22265
  message: `${name} changed since it was selected. Choose it again from the menu.`
@@ -22228,7 +22320,11 @@ class CodexCommandCatalogue {
22228
22320
  const binding = read.snapshot.byId.get(command.id);
22229
22321
  if (!binding)
22230
22322
  return this.unknownSelection();
22231
- return checkRow(skillCommandRow(binding)) ?? { kind: "skill", binding, args: command.arguments };
22323
+ return checkRow(skillCommandRow(binding, projectResources)) ?? {
22324
+ kind: "skill",
22325
+ binding,
22326
+ args: command.arguments
22327
+ };
22232
22328
  }
22233
22329
  return this.unknownSelection();
22234
22330
  }
@@ -22238,7 +22334,21 @@ class CodexCommandCatalogue {
22238
22334
  message: "The selected command is no longer available. Choose it again from the menu."
22239
22335
  };
22240
22336
  }
22241
- async resolveTyped(prompt) {
22337
+ async resolveTyped(prompt, projectResources = true) {
22338
+ if (!projectResources) {
22339
+ const mention = parseCommandToken(prompt, ["$"]);
22340
+ if (mention) {
22341
+ const read = await this.skills.read();
22342
+ if (!read.fresh)
22343
+ return { kind: "refused", message: "Codex skills could not be verified." };
22344
+ const binding = read.snapshot?.bindings.find((candidate) => `$${candidate.name}` === mention.token);
22345
+ if (binding) {
22346
+ const unavailable = skillUnavailability(binding, projectResources);
22347
+ if (unavailable)
22348
+ return { kind: "refused", message: unavailable.message };
22349
+ }
22350
+ }
22351
+ }
22242
22352
  const token = parseCommandToken(prompt, ["/"]);
22243
22353
  if (!token)
22244
22354
  return { kind: "text" };
@@ -22247,7 +22357,7 @@ class CodexCommandCatalogue {
22247
22357
  if (builtin)
22248
22358
  return { kind: "builtin", builtin: builtin.kind };
22249
22359
  if (typed.toLowerCase().startsWith(SKILL_ALIAS_PREFIX)) {
22250
- return this.resolveTypedSkill(typed, token.arguments);
22360
+ return this.resolveTypedSkill(typed, token.arguments, projectResources);
22251
22361
  }
22252
22362
  const scan = await this.scanTemplates();
22253
22363
  const matches = matchByPrecedence(scan.effective, (entry) => [entry.displayName], typed);
@@ -22264,7 +22374,7 @@ class CodexCommandCatalogue {
22264
22374
  return { kind: "refused", message: entry.problem.message };
22265
22375
  return { kind: "template", entry, args: token.arguments };
22266
22376
  }
22267
- async resolveTypedSkill(typed, args) {
22377
+ async resolveTypedSkill(typed, args, projectResources) {
22268
22378
  const read = await this.skills.read();
22269
22379
  if (!read.snapshot) {
22270
22380
  return {
@@ -22286,7 +22396,7 @@ class CodexCommandCatalogue {
22286
22396
  };
22287
22397
  }
22288
22398
  const binding = matches[0];
22289
- const unavailable = skillUnavailability(binding);
22399
+ const unavailable = skillUnavailability(binding, projectResources);
22290
22400
  if (unavailable)
22291
22401
  return { kind: "refused", message: unavailable.message };
22292
22402
  return { kind: "skill", binding, args };
@@ -22297,8 +22407,8 @@ class CodexCommandCatalogue {
22297
22407
  return loaded;
22298
22408
  return expandTemplateArguments(loaded.body, args, TEMPLATE_LIMITS.maxExpandedBytes);
22299
22409
  }
22300
- async helpText() {
22301
- const collected = await this.collect();
22410
+ async helpText(projectResources = true) {
22411
+ const collected = await this.collect(projectResources);
22302
22412
  const lines = ["Available Codex slash commands:"];
22303
22413
  const describe = (row, label) => {
22304
22414
  const hint = row.argumentHint ? ` ${row.argumentHint}` : "";
@@ -22402,6 +22512,7 @@ class CodexRuntimeContext {
22402
22512
  transcriptHydrateInFlight = new Map;
22403
22513
  steerRequests = new Map;
22404
22514
  commandCatalogue;
22515
+ projectSkillsRevision = 0;
22405
22516
  accountRateLimits = [];
22406
22517
  accountCredits;
22407
22518
  closingSessionIds = new Set;
@@ -22461,6 +22572,8 @@ function promptThreadConfig(config) {
22461
22572
  config.permissionProfile,
22462
22573
  config.workflowResultTool
22463
22574
  ] : [],
22575
+ config.policy?.projectResources,
22576
+ config.cwd,
22464
22577
  codexNetworkDeniedConfig(config.policy)
22465
22578
  ]);
22466
22579
  }
@@ -22767,7 +22880,7 @@ class CodexRuntimeLifecycle {
22767
22880
  return;
22768
22881
  }
22769
22882
  const lastMessage = context.messages.at(-1);
22770
- const assistantMessage = lastMessage?.role === "assistant" ? lastMessage : {
22883
+ const assistantMessage = lastMessage?.role === "assistant" && !lastMessage.content && (lastMessage.parts?.length ?? 0) === 0 ? lastMessage : {
22771
22884
  id: createMessageId(),
22772
22885
  role: "assistant",
22773
22886
  content: "",
@@ -22817,7 +22930,11 @@ class CodexRuntimeLifecycle {
22817
22930
  }
22818
22931
  return { detached, forgotten };
22819
22932
  }
22820
- async detachThread(context) {
22933
+ async detachThread(context, requireUnsubscribe = false) {
22934
+ if (requireUnsubscribe) {
22935
+ context.promptThreadConfig = undefined;
22936
+ await this.ctx.options.engine.unsubscribeThread(context.engineHandle, true);
22937
+ }
22821
22938
  const sessionIds = [...context.bridgeSessionIds];
22822
22939
  this.releaseThreadRuntimeState(context.threadId);
22823
22940
  this.ctx.registry.detachThread(context.threadId);
@@ -22831,9 +22948,11 @@ class CodexRuntimeLifecycle {
22831
22948
  }));
22832
22949
  }
22833
22950
  this.ctx.detachedThreads += 1;
22834
- await this.ctx.options.engine.unsubscribeThread(context.engineHandle).catch(() => {
22835
- return;
22836
- });
22951
+ if (!requireUnsubscribe) {
22952
+ await this.ctx.options.engine.unsubscribeThread(context.engineHandle).catch(() => {
22953
+ return;
22954
+ });
22955
+ }
22837
22956
  }
22838
22957
  releaseThreadRuntimeState(threadId) {
22839
22958
  const timer = this.ctx.recoveryBackstops.get(threadId);
@@ -23031,6 +23150,7 @@ class CodexRuntimeLifecycle {
23031
23150
  return;
23032
23151
  }
23033
23152
  if (event.kind === "skills.changed") {
23153
+ this.ctx.projectSkillsRevision += 1;
23034
23154
  this.ctx.commandCatalogue.markSkillsChanged();
23035
23155
  return;
23036
23156
  }
@@ -24253,7 +24373,7 @@ class CodexRuntimePrompt {
24253
24373
  const interpretCommands = input.allowProviderCommands !== false;
24254
24374
  let plan;
24255
24375
  if (input.command) {
24256
- plan = await this.ctx.commandCatalogue.resolveSelected(input.command);
24376
+ plan = await this.ctx.commandCatalogue.resolveSelected(input.command, session.config.policy?.projectResources !== false);
24257
24377
  if (plan.kind === "refused")
24258
24378
  return commandRefusal(plan.message);
24259
24379
  if (plan.kind === "builtin" && input.outputSchema) {
@@ -24289,22 +24409,19 @@ class CodexRuntimePrompt {
24289
24409
  context = await this.ctx.callbacks.ensureAttached(session.id);
24290
24410
  this.ctx.registry.assertNoActiveTurn(context);
24291
24411
  if (!plan) {
24292
- plan = interpretCommands && !input.outputSchema ? await this.ctx.commandCatalogue.resolveTyped(input.prompt) : { kind: "text" };
24412
+ plan = interpretCommands && !input.outputSchema ? await this.ctx.commandCatalogue.resolveTyped(input.prompt, session.config.policy?.projectResources !== false) : { kind: "text" };
24293
24413
  } else if (plan.kind === "skill") {
24294
- const live = this.ctx.commandCatalogue.skills.current()?.byId.get(plan.binding.id);
24295
- if (!live || live.path !== plan.binding.path || !live.enabled || live.ambiguous) {
24296
- const revalidated = await this.ctx.commandCatalogue.resolveSelected(input.command);
24297
- if (revalidated.kind === "refused")
24298
- return commandRefusal(revalidated.message);
24299
- plan = revalidated;
24300
- }
24414
+ const revalidated = await this.ctx.commandCatalogue.resolveSelected(input.command, session.config.policy?.projectResources !== false);
24415
+ if (revalidated.kind === "refused")
24416
+ return commandRefusal(revalidated.message);
24417
+ plan = revalidated;
24301
24418
  }
24302
24419
  if (plan.kind === "refused") {
24303
24420
  this.emitLocalResponse(session, input.prompt, plan.message);
24304
24421
  return { ok: true, result: { status: "processing", requestId } };
24305
24422
  }
24306
24423
  if (plan.kind === "builtin") {
24307
- const response = plan.builtin === "help" ? await this.ctx.commandCatalogue.helpText() : await this.modelsListText(session);
24424
+ const response = plan.builtin === "help" ? await this.ctx.commandCatalogue.helpText(session.config.policy?.projectResources !== false) : await this.modelsListText(session);
24308
24425
  this.emitLocalResponse(session, input.prompt, response);
24309
24426
  return { ok: true, result: { status: "processing", requestId } };
24310
24427
  }
@@ -24323,6 +24440,11 @@ class CodexRuntimePrompt {
24323
24440
  executionPrompt = `$${plan.binding.name}${plan.args ? ` ${plan.args}` : ""}`;
24324
24441
  skillInput = { name: plan.binding.name, path: plan.binding.path };
24325
24442
  }
24443
+ if (plan.kind === "skill") {
24444
+ const unavailable = skillUnavailability(plan.binding, session.config.policy?.projectResources !== false);
24445
+ if (unavailable)
24446
+ return commandRefusal(unavailable.message);
24447
+ }
24326
24448
  const isPlanReview = session.config.mode === "plan";
24327
24449
  const promptConfig = () => ({
24328
24450
  ...session.config,
@@ -24336,7 +24458,8 @@ class CodexRuntimePrompt {
24336
24458
  } : {},
24337
24459
  ...input.workflowResultTool ? { workflowResultTool: input.workflowResultTool } : {}
24338
24460
  });
24339
- const needsThreadReload = (config) => Boolean(input.workflowResultTool) || context?.promptThreadConfig !== promptThreadConfig(config);
24461
+ let projectSkillsRevision = this.ctx.projectSkillsRevision;
24462
+ const needsThreadReload = (config) => Boolean(input.workflowResultTool) || context?.promptThreadConfig !== promptThreadConfig(config) || config.policy?.projectResources === false && context?.projectSkillsRevision !== projectSkillsRevision;
24340
24463
  if (context && !context.materialized && needsThreadReload(promptConfig())) {
24341
24464
  await this.lifecycle.detachThread(context);
24342
24465
  context = undefined;
@@ -24370,7 +24493,28 @@ class CodexRuntimePrompt {
24370
24493
  if (hadAttachedContext && needsThreadReload(turnConfig)) {
24371
24494
  context = await this.resumeThreadForPrompt(session.id, context, turnConfig);
24372
24495
  }
24496
+ for (let attempt = 0;turnConfig.policy?.projectResources === false && projectSkillsRevision !== this.ctx.projectSkillsRevision; attempt += 1) {
24497
+ if (attempt >= 3) {
24498
+ context.promptThreadConfig = undefined;
24499
+ return {
24500
+ ok: false,
24501
+ status: 503,
24502
+ error: "Codex project skills kept changing; retry when the inventory settles"
24503
+ };
24504
+ }
24505
+ if (!context.materialized) {
24506
+ await this.lifecycle.detachThread(context, true);
24507
+ return {
24508
+ ok: false,
24509
+ status: 503,
24510
+ error: "Codex project skills changed during attachment; retry the prompt"
24511
+ };
24512
+ }
24513
+ projectSkillsRevision = this.ctx.projectSkillsRevision;
24514
+ context = await this.resumeThreadForPrompt(session.id, context, turnConfig);
24515
+ }
24373
24516
  context.promptThreadConfig = promptThreadConfig(turnConfig);
24517
+ context.projectSkillsRevision = projectSkillsRevision;
24374
24518
  if (input.workflowResultTool && turnConfig.agentMcp) {
24375
24519
  const availability = await this.ctx.options.engine.workflowResultToolAvailability(context.threadId, input.workflowResultTool);
24376
24520
  if (availability.state === "missing") {
@@ -24553,6 +24697,7 @@ class CodexRuntimePrompt {
24553
24697
  context = await this.resumeThreadForPrompt(session.id, context, turnConfig);
24554
24698
  }
24555
24699
  context.promptThreadConfig = promptThreadConfig(turnConfig);
24700
+ context.projectSkillsRevision = projectSkillsRevision;
24556
24701
  context.dispatchInFlight = true;
24557
24702
  this.ctx.registry.setPhase(context, "starting");
24558
24703
  if (context !== staleContext) {
@@ -24946,7 +25091,7 @@ class CodexRuntimePrompt {
24946
25091
  commands: []
24947
25092
  };
24948
25093
  }
24949
- return this.ctx.commandCatalogue.list();
25094
+ return this.ctx.commandCatalogue.list(this.ctx.registry.getSession(sessionId)?.config.policy?.projectResources !== false);
24950
25095
  }
24951
25096
  async refreshCommandCatalogue(sessionId) {
24952
25097
  if (!this.ctx.registry.getSession(sessionId)) {
@@ -27240,7 +27385,5 @@ export {
27240
27385
  REPLAY_IDLE_RETENTION_MS,
27241
27386
  __testing,
27242
27387
  app,
27243
- boundCodexTranscriptResponse,
27244
- itemToParts,
27245
- stringifyUnknown
27388
+ boundCodexTranscriptResponse
27246
27389
  };