openmausbot 0.1.79 → 0.1.80

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.
@@ -19049,7 +19049,7 @@ var init_registry = __esm({
19049
19049
  this.npmPresent = options.npmAvailable ?? npmAvailable;
19050
19050
  this.driversByKind = new Map(drivers.map((d) => [d.driverKind, d]));
19051
19051
  }
19052
- async load(configs) {
19052
+ async load(configs, decorate) {
19053
19053
  for (const [instanceId, entry] of Object.entries(configs)) {
19054
19054
  await this.dispose(instanceId);
19055
19055
  const driver = this.driversByKind.get(entry.driver);
@@ -19078,7 +19078,7 @@ var init_registry = __esm({
19078
19078
  enabled: entry.enabled ?? true,
19079
19079
  config: config2
19080
19080
  });
19081
- this.byId.set(instanceId, { instanceId, live });
19081
+ this.byId.set(instanceId, { instanceId, live: decorate ? decorate(live) : live });
19082
19082
  } catch (e) {
19083
19083
  this.byId.set(instanceId, {
19084
19084
  instanceId,
@@ -19600,6 +19600,8 @@ var init_redact = __esm({
19600
19600
  REDACTION_MARKER = /^«redacted \d+ chars»$/;
19601
19601
  mask = (value) => REDACTION_MARKER.test(value) ? value : `\xABredacted ${value.length} chars\xBB`;
19602
19602
  KEY_PREFIXES = [
19603
+ /\bom[dg]_[A-Za-z0-9_-]{43}(?![A-Za-z0-9_-])/g,
19604
+ // desktop device and model-only credentials
19603
19605
  /\bsk-(?:ant-|proj-|live-|test-)?[A-Za-z0-9_-]{16,}/g,
19604
19606
  // anthropic / openai / stripe
19605
19607
  /\bxai-[A-Za-z0-9_-]{20,}/g,
@@ -22637,21 +22639,17 @@ function claudeCliUpdate(version2, cli) {
22637
22639
  if (!parsed || versionAtLeast(parsed, CLAUDE_CONTEXT_CONTROL_MIN_VERSION)) return void 0;
22638
22640
  const floor = CLAUDE_CONTEXT_CONTROL_MIN_VERSION.join(".");
22639
22641
  const missing = Object.keys(CLAUDE_FLAG_FLOORS).filter((flag) => !claudeCliSupports(parsed, flag));
22642
+ const effects = [
22643
+ ...missing.includes("--autocompact") ? ["no compaction window picked by OpenMausBot"] : [],
22644
+ ...missing.includes("--setting-sources") ? ["bots still see this machine's own Claude Code setup"] : [],
22645
+ ...missing.includes("--system-prompt-snapshot") ? ["coordinated resumed turns cannot refresh stale system prompts"] : []
22646
+ ];
22640
22647
  return {
22641
22648
  title: "Update Claude Code for context controls",
22642
- message: `Claude Code ${parsed.join(".")} predates ${floor}, so bots run without ${missing.join(", ")}: no compaction window picked by OpenMausBot` + (missing.includes("--setting-sources") ? ", and bots still see this machine's own Claude Code setup" : "") + ". Update it, then refresh Engines.",
22649
+ message: `Claude Code ${parsed.join(".")} predates ${floor}, so bots run without ${missing.join(", ")}: ${effects.join("; ")}. Update it, then refresh Engines.`,
22643
22650
  command: cli === "claude" ? "claude update" : `${cli} update`
22644
22651
  };
22645
22652
  }
22646
- function claudeCliHelpSupportsFlag(help, flag) {
22647
- if (!help) return false;
22648
- const escaped = flag.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
22649
- return new RegExp(`^\\s*${escaped}\\b`, "m").test(help);
22650
- }
22651
- function claudeAutoCompactSupported(version2, help) {
22652
- if (help !== null) return claudeCliHelpSupportsFlag(help, "--autocompact");
22653
- return claudeCliSupports(version2, "--autocompact");
22654
- }
22655
22653
  async function resolveClaudeTurnModel(model, env) {
22656
22654
  if (!model || decodeInjectId(model) || STATIC_CLAUDE_MODELS.options.some((option) => option.id === model)) {
22657
22655
  return model;
@@ -22914,6 +22912,7 @@ function decodeConfig2(raw) {
22914
22912
  return {
22915
22913
  cli: typeof o.cli === "string" ? o.cli : "claude",
22916
22914
  ...configDir ? { configDir } : {},
22915
+ ...o.managed === true ? { managed: true } : {},
22917
22916
  permissionMode: mode ?? "acceptEdits",
22918
22917
  ...tools !== void 0 ? { tools } : {},
22919
22918
  ...disallowedTools !== void 0 ? { disallowedTools } : {}
@@ -23003,9 +23002,12 @@ var init_claude = __esm({
23003
23002
  CLAUDE_FLAG_FLOORS = {
23004
23003
  "--strict-mcp-config": [1, 0, 60],
23005
23004
  "--setting-sources": [1, 0, 122],
23006
- "--autocompact": [2, 1, 122]
23005
+ "--autocompact": [2, 1, 122],
23006
+ // 2.1.267 is the first CLI that accepts it; below that the recorded prompt
23007
+ // simply is not refreshed, which is the pre-existing behaviour.
23008
+ "--system-prompt-snapshot": [2, 1, 267]
23007
23009
  };
23008
- CLAUDE_CONTEXT_CONTROL_MIN_VERSION = CLAUDE_FLAG_FLOORS["--autocompact"];
23010
+ CLAUDE_CONTEXT_CONTROL_MIN_VERSION = CLAUDE_FLAG_FLOORS["--system-prompt-snapshot"];
23009
23011
  DRIVER_KIND2 = "claudeAgent";
23010
23012
  STATIC_CLAUDE_MODELS = {
23011
23013
  default: "claude-sonnet-5",
@@ -23044,10 +23046,11 @@ var init_claude = __esm({
23044
23046
  defaultConfig: () => decodeConfig2({}),
23045
23047
  async create(input) {
23046
23048
  const { instanceId, config: config2 } = input;
23047
- const environment = (model) => claudeEnvironment(model, { ...process.env, ...input.environment }, config2.configDir, input.environment);
23049
+ const environment = (model) => claudeEnvironment(config2.managed ? void 0 : model, { ...process.env, ...input.environment }, config2.configDir, input.environment);
23048
23050
  const catalogEnv = environment();
23049
23051
  let models2 = STATIC_CLAUDE_MODELS;
23050
23052
  const refreshModels = async () => {
23053
+ if (config2.managed) return;
23051
23054
  try {
23052
23055
  const resolved = await mergeLocalInject(readClaudeModelCatalog(catalogEnv), catalogEnv);
23053
23056
  if (resolved.options.length) models2 = resolved;
@@ -23056,8 +23059,15 @@ var init_claude = __esm({
23056
23059
  };
23057
23060
  await refreshModels();
23058
23061
  let cliVersion = null;
23059
- let cliHelp = null;
23060
- let cliHelpVersion = null;
23062
+ let cliVersionChecked = false;
23063
+ const readCliVersion = (env) => new Promise((resolve12) => {
23064
+ execCli(
23065
+ config2.cli,
23066
+ ["--version"],
23067
+ { timeout: 8e3, env },
23068
+ (err, stdout2) => resolve12(err ? null : stdout2.trim() || null)
23069
+ );
23070
+ });
23061
23071
  const listeners = /* @__PURE__ */ new Set();
23062
23072
  const active = /* @__PURE__ */ new Map();
23063
23073
  const sessions = /* @__PURE__ */ new Map();
@@ -23124,6 +23134,9 @@ var init_claude = __esm({
23124
23134
  });
23125
23135
  const retryState = /* @__PURE__ */ new Map();
23126
23136
  const sendTurn = async (turn, logicalTurnId) => {
23137
+ if (config2.managed && (!turn.model || turn.model.includes("::") || !config2.configDir || !input.environment.ANTHROPIC_API_KEY || !input.environment.ANTHROPIC_BASE_URL)) {
23138
+ throw new Error("Company model access is unavailable. Reconnect your organization; personal billing will not be used.");
23139
+ }
23127
23140
  const { threadId, botId } = turn;
23128
23141
  const relaunch = logicalTurnId !== void 0;
23129
23142
  if (active.has(threadId) && !relaunch) throw new Error("a turn is already running on this thread");
@@ -23160,17 +23173,27 @@ var init_claude = __esm({
23160
23173
  args.push("--disallowedTools", config2.disallowedTools.join(","));
23161
23174
  }
23162
23175
  const turnEnvironment = environment();
23176
+ if (turn.refreshSystemPrompt && !cliVersionChecked) {
23177
+ const version2 = await readCliVersion(turnEnvironment);
23178
+ if (version2) {
23179
+ cliVersion = parseClaudeCliVersion(version2);
23180
+ cliVersionChecked = true;
23181
+ }
23182
+ }
23163
23183
  const isolated = !inheritsUserConfig(turnEnvironment);
23164
23184
  if (isolated) {
23165
23185
  if (claudeCliSupports(cliVersion, "--strict-mcp-config")) args.push("--strict-mcp-config");
23166
23186
  if (claudeCliSupports(cliVersion, "--setting-sources")) args.push("--setting-sources", "project");
23167
23187
  }
23168
23188
  const compactWindow = autoCompactWindow(turnEnvironment);
23169
- if (compactWindow && claudeAutoCompactSupported(cliVersion, cliHelp)) {
23189
+ if (compactWindow && claudeCliSupports(cliVersion, "--autocompact")) {
23170
23190
  args.push("--autocompact", compactWindow);
23171
23191
  }
23172
- const turnModel = await resolveClaudeTurnModel(turn.model, turnEnvironment);
23173
- const injected = applyClaudeInject({ ...turnEnvironment }, turnModel);
23192
+ if (turn.refreshSystemPrompt && cliVersionChecked && claudeCliSupports(cliVersion, "--system-prompt-snapshot")) {
23193
+ args.push("--system-prompt-snapshot", "off");
23194
+ }
23195
+ const turnModel = config2.managed ? turn.model : await resolveClaudeTurnModel(turn.model, turnEnvironment);
23196
+ const injected = config2.managed ? { model: turnModel ?? null, injected: false } : applyClaudeInject({ ...turnEnvironment }, turnModel);
23174
23197
  if (injected.model) args.push("--model", injected.model);
23175
23198
  if (turn.effort) args.push("--effort", turn.effort);
23176
23199
  let systemPromptPath = null;
@@ -23506,7 +23529,10 @@ var init_claude = __esm({
23506
23529
  type: "thread.token-usage.updated",
23507
23530
  input: (msg.usage.input_tokens || 0) + (msg.usage.cache_read_input_tokens || 0),
23508
23531
  output: msg.usage.output_tokens || 0,
23509
- ...typeof msg.usage.cache_read_input_tokens === "number" ? { cachedInput: msg.usage.cache_read_input_tokens } : {}
23532
+ ...typeof msg.usage.cache_read_input_tokens === "number" ? { cachedInput: msg.usage.cache_read_input_tokens } : {},
23533
+ // one assistant message = one model call, and its prompt is
23534
+ // everything in the window: fresh text, cache reads and writes
23535
+ contextTokens: (msg.usage.input_tokens || 0) + (msg.usage.cache_read_input_tokens || 0) + (msg.usage.cache_creation_input_tokens || 0)
23510
23536
  });
23511
23537
  }
23512
23538
  break;
@@ -23739,38 +23765,13 @@ var init_claude = __esm({
23739
23765
  } });
23740
23766
  const snapshot = async () => {
23741
23767
  const env = environment();
23742
- const version2 = await new Promise((resolve12) => {
23743
- execCli(
23744
- config2.cli,
23745
- ["--version"],
23746
- { timeout: 8e3, env },
23747
- (err, stdout2) => resolve12(err ? null : stdout2.trim())
23748
- );
23749
- });
23768
+ const version2 = await readCliVersion(env);
23750
23769
  if (!version2) return { state: "unavailable", reason: `\`${config2.cli}\` CLI not found` };
23751
23770
  cliVersion = parseClaudeCliVersion(version2);
23752
- if (version2 !== cliHelpVersion) {
23753
- cliHelp = await new Promise((resolve12) => {
23754
- execCli(
23755
- config2.cli,
23756
- ["--help"],
23757
- { timeout: 8e3, env },
23758
- (err, stdout2) => resolve12(err ? null : stdout2)
23759
- );
23760
- });
23761
- cliHelpVersion = version2;
23762
- }
23763
- const features = cliHelp !== null ? { autocompact: claudeCliHelpSupportsFlag(cliHelp, "--autocompact") } : void 0;
23771
+ cliVersionChecked = true;
23764
23772
  const auth = await claudeAuthStatus(config2.cli, env);
23765
23773
  const update = claudeCliUpdate(version2, config2.cli);
23766
- return {
23767
- state: "available",
23768
- version: version2,
23769
- ...auth,
23770
- ...update ? { update } : {},
23771
- ...features ? { features } : {},
23772
- billing: "subscription"
23773
- };
23774
+ return { state: "available", version: version2, ...auth, ...update ? { update } : {}, billing: "subscription" };
23774
23775
  };
23775
23776
  const generateReview = (prompt, signal) => new Promise((resolve12, reject) => {
23776
23777
  const child = spawnCli(
@@ -24709,6 +24710,9 @@ var init_codex_identity = __esm({
24709
24710
  // server/drivers/codex.ts
24710
24711
  import { existsSync as existsSync10 } from "node:fs";
24711
24712
  import { homedir as homedir13 } from "node:os";
24713
+ function missingNativeCodexThread(error51, cursor3) {
24714
+ return error51 instanceof CodexRpcError && error51.code === -32600 && error51.message === `no rollout found for thread id ${cursor3}`;
24715
+ }
24712
24716
  function codexPredatesAstra(version2) {
24713
24717
  const value = version2.trim();
24714
24718
  const match = /\bcodex-cli\s+v?(\d+)\.(\d+)\.(\d+)(?:[-+][0-9a-z.-]+)?(?![\d.])\b/i.exec(value) ?? /^v?(\d+)\.(\d+)\.(\d+)(?:[-+][0-9a-z.-]+)?$/i.exec(value);
@@ -24743,9 +24747,39 @@ function decodeConfig3(raw) {
24743
24747
  const o = raw ?? {};
24744
24748
  return {
24745
24749
  cli: typeof o.cli === "string" ? o.cli : "codex",
24746
- fullAuto: o.fullAuto === true
24750
+ fullAuto: o.fullAuto === true,
24751
+ ...o.managed && typeof o.managed === "object" ? { managed: decodeManagedCodex(o.managed) } : {}
24747
24752
  };
24748
24753
  }
24754
+ function decodeManagedCodex(raw) {
24755
+ const value = raw;
24756
+ if (typeof value.url !== "string" || !Array.isArray(value.models) || !value.models.length || value.models.some((model) => typeof model !== "string" || !/^[\w][\w./+-]*$/.test(model))) {
24757
+ throw new Error("Invalid Company Codex configuration.");
24758
+ }
24759
+ const url2 = new URL(value.url);
24760
+ if (url2.username || url2.password || url2.search || url2.hash || !["https:", "http:"].includes(url2.protocol)) throw new Error("Invalid Company Codex endpoint.");
24761
+ return { url: url2.href.replace(/\/$/, ""), models: value.models };
24762
+ }
24763
+ function managedCodexArgs(config2) {
24764
+ return [
24765
+ "-c",
24766
+ 'model_provider="openmaus_company"',
24767
+ "-c",
24768
+ 'model_providers.openmaus_company.name="Company"',
24769
+ "-c",
24770
+ `model_providers.openmaus_company.base_url=${JSON.stringify(config2.url)}`,
24771
+ "-c",
24772
+ 'model_providers.openmaus_company.env_key="OPENMAUSBOT_COMPANY_API_KEY"',
24773
+ "-c",
24774
+ 'model_providers.openmaus_company.wire_api="responses"',
24775
+ "-c",
24776
+ "model_providers.openmaus_company.requires_openai_auth=false",
24777
+ "-c",
24778
+ 'cli_auth_credentials_store="ephemeral"',
24779
+ "-c",
24780
+ "shell_environment_policy.ignore_default_excludes=false"
24781
+ ];
24782
+ }
24749
24783
  function grantedPermissions(raw) {
24750
24784
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return {};
24751
24785
  return Object.fromEntries(
@@ -24961,7 +24995,7 @@ function mountMcpServer(appServerArgs, env, name, server, preApproved = true) {
24961
24995
  appServerArgs.push("-c", `${prefix}.default_tools_approval_mode="auto"`);
24962
24996
  }
24963
24997
  }
24964
- var DRIVER_KIND3, ASTRA_MODEL_ID, ASTRA_MIN_CODEX_VERSION, QUESTION_TIMEOUT_NOTE2, DENY_TIMEOUT_NOTE2, plainRecord, containsControlCharacter, CodexDriver;
24998
+ var DRIVER_KIND3, ASTRA_MODEL_ID, ASTRA_MIN_CODEX_VERSION, CodexRpcError, QUESTION_TIMEOUT_NOTE2, DENY_TIMEOUT_NOTE2, plainRecord, containsControlCharacter, CodexDriver;
24965
24999
  var init_codex = __esm({
24966
25000
  "server/drivers/codex.ts"() {
24967
25001
  "use strict";
@@ -24978,10 +25012,18 @@ var init_codex = __esm({
24978
25012
  init_codex_instructions();
24979
25013
  init_codex_device_auth();
24980
25014
  init_codex_identity();
25015
+ init_resume_recovery();
24981
25016
  init_codex_catalog();
24982
25017
  DRIVER_KIND3 = "codex";
24983
25018
  ASTRA_MODEL_ID = "gpt-6-astra";
24984
25019
  ASTRA_MIN_CODEX_VERSION = [0, 153, 1];
25020
+ CodexRpcError = class extends Error {
25021
+ code;
25022
+ constructor(error51) {
25023
+ super(error51.message ?? JSON.stringify(error51));
25024
+ this.code = error51.code;
25025
+ }
25026
+ };
24985
25027
  QUESTION_TIMEOUT_NOTE2 = "No answer was given \u2014 use your best judgment.";
24986
25028
  DENY_TIMEOUT_NOTE2 = "OpenMausBot: nobody answered this permission request in time. Skip this action and finish what you can without it.";
24987
25029
  plainRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -25022,8 +25064,9 @@ var init_codex = __esm({
25022
25064
  return env;
25023
25065
  };
25024
25066
  const catalogEnv = childEnv();
25025
- let models2 = STATIC_CODEX_MODELS;
25067
+ let models2 = config2.managed ? { default: config2.managed.models[0], options: config2.managed.models.map((id) => ({ id, label: id })) } : STATIC_CODEX_MODELS;
25026
25068
  const refreshModels = async () => {
25069
+ if (config2.managed) return;
25027
25070
  try {
25028
25071
  const resolved = await readCodexModelCatalog(catalogEnv, fetch, config2.cli);
25029
25072
  if (resolved.options.length) models2 = resolved;
@@ -25049,7 +25092,12 @@ var init_codex = __esm({
25049
25092
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
25050
25093
  });
25051
25094
  const sendTurn = async (turn) => {
25095
+ if (config2.managed && (!turn.model || !config2.managed.models.includes(turn.model) || !input.environment.OPENMAUSBOT_COMPANY_API_KEY || !input.environment.CODEX_HOME)) {
25096
+ throw new Error("Company model access is unavailable. Reconnect your organization; personal billing will not be used.");
25097
+ }
25052
25098
  let stopRequested = false;
25099
+ let promptSubmitted = false;
25100
+ let recoveredMissingSession = false;
25053
25101
  const stopSignal = new AbortController();
25054
25102
  const { threadId } = turn;
25055
25103
  const approvalMode = turn.approvalMode ?? (config2.fullAuto ? "full" : "ask");
@@ -25065,7 +25113,7 @@ var init_codex = __esm({
25065
25113
  const retryScale = Number(process.env.FAKE_CODEX_RETRY_SCALE ?? "1");
25066
25114
  const launchAttempt = async (attempt) => {
25067
25115
  const env = childEnv();
25068
- const appServerArgs = ["app-server", ...codexLocalProviderArgs(env, turn.model)];
25116
+ const appServerArgs = ["app-server", ...config2.managed ? managedCodexArgs(config2.managed) : codexLocalProviderArgs(env, turn.model)];
25069
25117
  if (turn.integrations?.composio) {
25070
25118
  mountMcpServer(appServerArgs, env, "openmausbot_connectors", turn.integrations.composio);
25071
25119
  }
@@ -25111,9 +25159,12 @@ var init_codex = __esm({
25111
25159
  lastError: "",
25112
25160
  lastText: "",
25113
25161
  sawStreamDelta: false,
25114
- // codex reports token usage as a running THREAD total; the harness
25115
- // wants this turn's figure, so the last report is banked on settle
25116
- usage: void 0
25162
+ // codex reports token usage as a running total for this app-server
25163
+ // process. The harness wants this turn's figure: the total minus
25164
+ // whatever the process already carried before turn/start (a resumed
25165
+ // thread may restore earlier usage), banked on settle.
25166
+ usage: void 0,
25167
+ usageBaseline: void 0
25117
25168
  };
25118
25169
  const asks = /* @__PURE__ */ new Map();
25119
25170
  let nextId = 1;
@@ -25140,6 +25191,12 @@ var init_codex = __esm({
25140
25191
  rpcPending.set(id, {
25141
25192
  resolve: (v) => {
25142
25193
  clearTimeout(timer);
25194
+ if (method === "thread/start" || method === "thread/resume") {
25195
+ const returnedId = v?.thread?.id;
25196
+ const requestedId = method === "thread/resume" && params && typeof params === "object" && "threadId" in params ? params.threadId : null;
25197
+ if (typeof returnedId === "string" && returnedId) codexThreadId = returnedId;
25198
+ else if (typeof requestedId === "string" && requestedId) codexThreadId = requestedId;
25199
+ }
25143
25200
  if (method === "turn/start") {
25144
25201
  if (typeof v?.turn?.id !== "string" || !v.turn.id) {
25145
25202
  reject(new Error("Codex did not return a native turn id"));
@@ -25268,6 +25325,11 @@ var init_codex = __esm({
25268
25325
  const connectionError = msg.method === "error" && !("threadId" in p) && !("turnId" in p);
25269
25326
  if (!connectionError) {
25270
25327
  if (!codexThreadId || p.threadId !== codexThreadId) return;
25328
+ if (!codexTurnId && msg.method === "thread/tokenUsage/updated" && p.tokenUsage?.total) {
25329
+ const t2 = p.tokenUsage.total;
25330
+ state.usageBaseline = { input: t2.inputTokens ?? 0, output: t2.outputTokens ?? 0, cachedInput: t2.cachedInputTokens ?? 0 };
25331
+ return;
25332
+ }
25271
25333
  if (!codexTurnId) {
25272
25334
  if (startingNativeTurn) {
25273
25335
  if (earlyNotifications.length >= 1024) {
@@ -25351,22 +25413,35 @@ var init_codex = __esm({
25351
25413
  break;
25352
25414
  }
25353
25415
  case "thread/tokenUsage/updated": {
25354
- const turnUsage = p.tokenUsage?.last ?? p.tokenUsage?.total;
25355
- if (turnUsage) {
25416
+ const t2 = p.tokenUsage?.total;
25417
+ const last = p.tokenUsage?.last;
25418
+ const shape = (u4) => ({
25419
+ input: u4.inputTokens ?? 0,
25420
+ output: u4.outputTokens ?? 0,
25421
+ cachedInput: u4.cachedInputTokens ?? 0
25422
+ });
25423
+ if (t2) {
25424
+ const b = state.usageBaseline ?? { input: 0, output: 0, cachedInput: 0 };
25425
+ const now = shape(t2);
25356
25426
  state.usage = {
25357
- input: turnUsage.inputTokens ?? 0,
25358
- output: turnUsage.outputTokens ?? 0,
25359
- ...typeof turnUsage.cachedInputTokens === "number" ? { cachedInput: turnUsage.cachedInputTokens } : {}
25427
+ input: Math.max(0, now.input - b.input),
25428
+ output: Math.max(0, now.output - b.output),
25429
+ ...typeof t2.cachedInputTokens === "number" ? { cachedInput: Math.max(0, now.cachedInput - b.cachedInput) } : {}
25360
25430
  };
25431
+ } else if (last) {
25432
+ state.usage = { input: last.inputTokens ?? 0, output: last.outputTokens ?? 0, ...typeof last.cachedInputTokens === "number" ? { cachedInput: last.cachedInputTokens } : {} };
25361
25433
  }
25362
- const t2 = p.tokenUsage?.total;
25363
25434
  if (t2) {
25435
+ const window = p.tokenUsage?.modelContextWindow;
25364
25436
  emit({
25365
25437
  ...base2(threadId, turnId),
25366
25438
  type: "thread.token-usage.updated",
25367
25439
  input: t2.inputTokens ?? 0,
25368
25440
  output: t2.outputTokens ?? 0,
25369
- ...typeof t2.cachedInputTokens === "number" ? { cachedInput: t2.cachedInputTokens } : {}
25441
+ ...typeof t2.cachedInputTokens === "number" ? { cachedInput: t2.cachedInputTokens } : {},
25442
+ // the last call's prompt is what fills the window
25443
+ ...last && typeof last.inputTokens === "number" ? { contextTokens: last.inputTokens } : {},
25444
+ ...typeof window === "number" && window > 0 ? { contextWindow: window } : {}
25370
25445
  });
25371
25446
  }
25372
25447
  break;
@@ -25420,7 +25495,7 @@ var init_codex = __esm({
25420
25495
  const pend = rpcPending.get(msg.id);
25421
25496
  if (pend) {
25422
25497
  rpcPending.delete(msg.id);
25423
- if (msg.error) pend.reject(new Error(msg.error.message ?? JSON.stringify(msg.error)));
25498
+ if (msg.error) pend.reject(new CodexRpcError(msg.error));
25424
25499
  else pend.resolve(msg.result);
25425
25500
  }
25426
25501
  } else if (msg.id !== void 0 && msg.method) {
@@ -25451,7 +25526,7 @@ var init_codex = __esm({
25451
25526
  }
25452
25527
  const recentStderr = stderrSinceOutput.trim();
25453
25528
  const hadStreamedOutput = codexTurnId !== null || state.sawStreamDelta;
25454
- const verdict = classifyError({ exitCode: code, stderr: recentStderr || stderr });
25529
+ const verdict = signal !== null ? { transient: false, reason: "interrupted" } : classifyError({ exitCode: code, stderr: recentStderr });
25455
25530
  if (!stopRequested && codexTurnId === null && earlyNotifications.length === 0 && verdict.transient && attempt < RETRY_MAX_ATTEMPTS - 1) {
25456
25531
  const delayMs = computeBackoff(attempt);
25457
25532
  attempt++;
@@ -25518,30 +25593,39 @@ var init_codex = __esm({
25518
25593
  autoAcceptPermissions = approvalMode === "full";
25519
25594
  const cursor3 = typeof turn.resumeCursor === "string" ? turn.resumeCursor : null;
25520
25595
  let startedModel = null;
25596
+ let resumedNativeThread = false;
25597
+ let promptText = turn.text;
25521
25598
  if (cursor3) {
25599
+ const resumeThread = () => request("thread/resume", {
25600
+ threadId: cursor3,
25601
+ developerInstructions,
25602
+ ...approvalParams.thread
25603
+ });
25522
25604
  try {
25523
- const resumed = await request("thread/resume", {
25524
- threadId: cursor3,
25525
- developerInstructions,
25526
- ...approvalParams.thread
25527
- });
25528
- codexThreadId = resumed?.thread?.id ?? cursor3;
25529
- } catch (error51) {
25530
- if (approvalParams.fallback && permissionProfileUnsupported(error51)) {
25605
+ let resumed;
25606
+ try {
25607
+ resumed = await resumeThread();
25608
+ } catch (error51) {
25609
+ if (!approvalParams.fallback || !permissionProfileUnsupported(error51)) throw error51;
25531
25610
  approvalParams = approvalParams.fallback;
25532
- const resumed = await request("thread/resume", {
25533
- threadId: cursor3,
25534
- developerInstructions,
25535
- ...approvalParams.thread
25536
- });
25537
- codexThreadId = resumed?.thread?.id ?? cursor3;
25538
- } else {
25539
- throw error51;
25611
+ resumed = await resumeThread();
25540
25612
  }
25613
+ codexThreadId = resumed?.thread?.id ?? cursor3;
25614
+ resumedNativeThread = true;
25615
+ } catch (error51) {
25616
+ const failure = classifyResumeFailure({
25617
+ attempted: true,
25618
+ rejected: error51 instanceof CodexRpcError,
25619
+ promptSubmitted,
25620
+ producedOutput: state.sawStreamDelta
25621
+ });
25622
+ if (!config2.managed || recoveredMissingSession || stopRequested || state.settled || !turn.recoveryText?.trim() || !missingNativeCodexThread(error51, cursor3) || !mayReplay(failure)) throw error51;
25623
+ recoveredMissingSession = true;
25624
+ promptText = recoveryPromptFor({ recoveryText: turn.recoveryText, currentText: turn.text, failure }).text;
25541
25625
  }
25542
25626
  }
25543
25627
  if (!codexThreadId) {
25544
- const selection = decodeCodexSelection(turn.model);
25628
+ const selection = config2.managed ? { model: turn.model, modelProvider: "openmaus_company" } : decodeCodexSelection(turn.model);
25545
25629
  const startThread = () => request("thread/start", {
25546
25630
  developerInstructions,
25547
25631
  cwd: turn.cwd ?? homedir13(),
@@ -25562,14 +25646,14 @@ var init_codex = __esm({
25562
25646
  startedModel = started?.model ?? null;
25563
25647
  }
25564
25648
  if (!codexThreadId) throw new Error("Codex did not return a native thread id");
25565
- await syncCodexInstructions(threadId, codexThreadId, developerInstructions, Boolean(cursor3), request);
25649
+ await syncCodexInstructions(threadId, codexThreadId, developerInstructions, resumedNativeThread, request);
25566
25650
  emit({ ...base2(threadId, turnId), type: "session.started", sessionId: codexThreadId, model: startedModel ?? turn.model ?? null });
25567
- const promptText = turn.text;
25568
25651
  const turnInput = [
25569
25652
  ...promptText ? [{ type: "text", text: promptText }] : [],
25570
25653
  ...(turn.images ?? []).map((image) => ({ type: "localImage", path: image.path }))
25571
25654
  ];
25572
25655
  const startTurn = () => {
25656
+ promptSubmitted = true;
25573
25657
  startingNativeTurn = true;
25574
25658
  return request("turn/start", {
25575
25659
  threadId: codexThreadId,
@@ -25599,7 +25683,7 @@ var init_codex = __esm({
25599
25683
  const message2 = e instanceof Error ? e.message : String(e);
25600
25684
  const needsAuth = /(?:\b401\b|unauthorized|missing bearer|authentication required)/i.test(message2);
25601
25685
  const verdict = classifyError(failure);
25602
- if (!state.settled && !abandoned && !needsAuth && verdict.transient && attempt < RETRY_MAX_ATTEMPTS - 1 && state.sawStreamDelta === false) {
25686
+ if (!state.settled && !abandoned && !recoveredMissingSession && !stopRequested && !needsAuth && verdict.transient && attempt < RETRY_MAX_ATTEMPTS - 1 && state.sawStreamDelta === false) {
25603
25687
  const delayMs = computeBackoff(attempt);
25604
25688
  attempt++;
25605
25689
  emit({
@@ -25649,6 +25733,7 @@ var init_codex = __esm({
25649
25733
  );
25650
25734
  });
25651
25735
  if (!version2) return { state: "unavailable", reason: `\`${config2.cli}\` CLI not found` };
25736
+ if (config2.managed) return { state: "available", version: version2, authenticated: Boolean(input.environment.OPENMAUSBOT_COMPANY_API_KEY && input.environment.CODEX_HOME), billing: "metered" };
25652
25737
  const authenticated = await new Promise((resolve12) => {
25653
25738
  execCli(
25654
25739
  config2.cli,