@opengeni/runtime 0.11.0 → 0.13.3

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.
package/dist/index.js CHANGED
@@ -29,6 +29,9 @@ import {
29
29
  REPOSITORY_DISCOVERY_LIMIT,
30
30
  RecordingError,
31
31
  RecordingUnavailableError,
32
+ RoutingBackendRecoveryRequiredError,
33
+ RoutingMutationOutcomeUnknownError,
34
+ RoutingRetainedProcessNotFoundError,
32
35
  RoutingSandboxSession,
33
36
  RoutingUnsupportedError,
34
37
  RunCredentialValidationError,
@@ -48,7 +51,9 @@ import {
48
51
  STREAM_TOKEN_DEFAULT_TTL_SECONDS,
49
52
  SandboxChannelAService,
50
53
  SandboxConfigError,
54
+ SandboxExecReadinessError,
51
55
  SandboxProviderUnavailableError,
56
+ SandboxReplacementProviderStateError,
52
57
  SandboxResumeStateUnavailableError,
53
58
  SelfhostedControlError,
54
59
  SelfhostedSandboxClient,
@@ -60,6 +65,9 @@ import {
60
65
  TerminalServerError,
61
66
  TerminalServerUnsupportedError,
62
67
  ToolspaceTokenPathError,
68
+ TurnSandboxCommandCancelledError,
69
+ WORKSPACE_ARCHIVE_DESCRIPTOR_VERSION,
70
+ WorkspaceArchiveIntegrityError,
63
71
  agentErrorToControlError,
64
72
  assertDescriptorRegistryInvariants,
65
73
  assertProviderRegistryInvariants,
@@ -69,12 +77,15 @@ import {
69
77
  buildSelfhostedBackendSession,
70
78
  buildStreamUrl,
71
79
  buildTerminalServerScript,
80
+ captureVerifiedWorkspaceArchive,
81
+ classifyProviderSandboxFailure,
72
82
  clearRunCredentials,
73
83
  clearRunCredentialsForAttempt,
74
84
  collectSandboxEnvironment,
75
85
  contentTypeForCodec,
76
86
  createSandboxClient,
77
87
  createSandboxClientForBackend,
88
+ createTurnToolCancellationController,
78
89
  decideSelfhostedRetry,
79
90
  decodeModalSnapshotId,
80
91
  defaultSelfhostedRetryClock,
@@ -91,9 +102,13 @@ import {
91
102
  execDeadlineHint,
92
103
  exposeStreamPort,
93
104
  extForCodec,
105
+ fingerprintSandboxWorkspace,
106
+ hasPersistableSandboxProviderIdentity,
94
107
  inspectRecordingArtifact,
95
108
  isExecSessionLostBanner,
109
+ isProviderSandboxGoneDuringRoutedOperation,
96
110
  isProviderSandboxNotFoundError,
111
+ isProviderSandboxTransientError,
97
112
  isSelfhostedProviderNotFoundError,
98
113
  localDirLazySkillSource,
99
114
  makeActiveBackendResolver,
@@ -115,10 +130,13 @@ import {
115
130
  parseNumstatZ,
116
131
  parsePorcelainV2,
117
132
  parseUnifiedPatch,
133
+ parseWorkspaceArchiveDescriptor,
118
134
  payloadTooLargeMessage,
135
+ readVerifiedWorkspaceArchive,
119
136
  readWorkspaceArchiveFromEnvelopeSessionState,
120
137
  recordingStorageKey,
121
138
  renderSelfhostedFault,
139
+ requirePersistableReplacementSandboxEnvelope,
122
140
  restoredSandboxSessionStateFromEntry,
123
141
  runCredentialPointerFile,
124
142
  runCredentialRoot,
@@ -130,6 +148,7 @@ import {
130
148
  selfhostedLiveness,
131
149
  selfhostedRetryBackoffMs,
132
150
  serializeEstablishedSandboxEnvelope,
151
+ serializeReplacementSandboxEnvelope,
133
152
  setSelfhostedApplyDiff,
134
153
  startRecording,
135
154
  stopRecording,
@@ -146,20 +165,39 @@ import {
146
165
  toolspaceTokenFileForSession,
147
166
  toolspaceTokenFileFromEnvironment,
148
167
  uploadRecordingArtifact,
168
+ verifyRestoredWorkspace,
169
+ verifySandboxExecReadiness,
149
170
  verifyStreamToken,
150
171
  withRunCredentialEnvironment,
151
172
  withRunCredentialsClient,
152
173
  withRunCredentialsSession,
153
174
  withToolspaceTokenClient,
154
175
  withToolspaceTokenEnvironment,
155
- withToolspaceTokenSession
156
- } from "./chunk-YUAFQEFY.js";
176
+ withToolspaceTokenSession,
177
+ wrapCapabilityToolsForTurnCancellation
178
+ } from "./chunk-JGUQSAG5.js";
157
179
  import {
158
180
  getSkillLibraryEntry,
159
181
  isSkillLibraryEntryId,
160
182
  listSkillLibraryEntries,
161
183
  loadSkillLibrarySkill
162
184
  } from "./chunk-DPULLAJG.js";
185
+ import {
186
+ MCP_MAX_CONCURRENT_SERVER_OPERATIONS,
187
+ MCP_MAX_TOOL_DEFINITION_BYTES,
188
+ MCP_MAX_TOOL_RESULT_BYTES,
189
+ MCP_MAX_TOOL_SEARCH_DISCLOSURE_BYTES,
190
+ McpAggregateToolListBudget,
191
+ assertMcpPayloadWithinBytes,
192
+ assertMcpServerSelectionWithinBounds,
193
+ assertMcpToolListWithinBounds,
194
+ boundedParallelMap,
195
+ cancelMcpResponseBody,
196
+ guardedMcpFetch,
197
+ mcpOuterConnectTimeoutMs,
198
+ mcpSerializedSizeBytes,
199
+ undiciFetch
200
+ } from "./chunk-SSB7VOLE.js";
163
201
  import {
164
202
  MemorySession,
165
203
  connectMcpServers
@@ -225,6 +263,7 @@ import {
225
263
  import {
226
264
  approvalIdentifier,
227
265
  CAPABILITY_DESCRIPTORS as CAPABILITY_DESCRIPTORS2,
266
+ DEFAULT_FIRST_PARTY_MCP_PERMISSIONS,
228
267
  assertUniqueResourceMountPaths,
229
268
  gitCredentialBindingIdForRepository,
230
269
  gitCredentialProviderForRepository,
@@ -5094,7 +5133,7 @@ var OpenAITracingExporter = class {
5094
5133
  const payloadItems = groupedItems.map((entry) => entry.toJSON()).filter((item) => !!item).map((item) => isRecord5(item) ? sanitizePayloadItemForTracesIngest(item) : item);
5095
5134
  const payload = { data: payloadItems };
5096
5135
  let attempts = 0;
5097
- let delay2 = this.#options.baseDelay;
5136
+ let delay = this.#options.baseDelay;
5098
5137
  while (attempts < this.#options.maxRetries) {
5099
5138
  try {
5100
5139
  const response = await fetch(this.#options.endpoint, {
@@ -5128,13 +5167,13 @@ var OpenAITracingExporter = class {
5128
5167
  if (attempts >= this.#options.maxRetries) {
5129
5168
  break;
5130
5169
  }
5131
- const sleepTime = delay2 + Math.random() * 0.1 * delay2;
5170
+ const sleepTime = delay + Math.random() * 0.1 * delay;
5132
5171
  const shouldContinue = await sleepWithAbort(sleepTime, signal);
5133
5172
  if (!shouldContinue) {
5134
5173
  logger_default.error("Tracing: request aborted");
5135
5174
  break;
5136
5175
  }
5137
- delay2 = Math.min(delay2 * 2, this.#options.maxDelay);
5176
+ delay = Math.min(delay * 2, this.#options.maxDelay);
5138
5177
  }
5139
5178
  if (attempts >= this.#options.maxRetries) {
5140
5179
  logger_default.error(`Tracing: failed to export traces after ${this.#options.maxRetries} attempts`);
@@ -6372,6 +6411,7 @@ import {
6372
6411
  CODEX_APPS_MCP_SERVER_ID,
6373
6412
  CODEX_MODEL_ID_PREFIX,
6374
6413
  CODEX_ORIGINATOR,
6414
+ CODEX_RESPONSE_SDK_OUTER_TIMEOUT_MS,
6375
6415
  boundModelToolOutputItems as boundModelToolOutputItems2,
6376
6416
  codexAppsSanitizingFetch,
6377
6417
  codexRequestStorage,
@@ -6972,7 +7012,21 @@ function codexToolSearchExecutor(args) {
6972
7012
  const deferred = (args.availableTools ?? []).filter(isCodexAppsFunctionTool);
6973
7013
  if (deferred.length === 0) return [];
6974
7014
  const { query, limit } = parseSearchArgs(args.toolCall?.arguments);
6975
- return bm25RankTools(deferred, query, limit);
7015
+ const ranked = bm25RankTools(deferred, query, limit);
7016
+ const bounded = [];
7017
+ let disclosedBytes = 0;
7018
+ for (const tool2 of ranked) {
7019
+ const toolBytes = mcpSerializedSizeBytes(tool2);
7020
+ if (toolBytes > MCP_MAX_TOOL_DEFINITION_BYTES) {
7021
+ continue;
7022
+ }
7023
+ if (disclosedBytes + toolBytes > MCP_MAX_TOOL_SEARCH_DISCLOSURE_BYTES) {
7024
+ break;
7025
+ }
7026
+ disclosedBytes += toolBytes;
7027
+ bounded.push(tool2);
7028
+ }
7029
+ return bounded;
6976
7030
  }
6977
7031
  var NO_NAMESPACES = /* @__PURE__ */ new Set();
6978
7032
  function hasConnectorSources(connectorNamespaces) {
@@ -7570,668 +7624,6 @@ function safeStringify(value) {
7570
7624
  }
7571
7625
  }
7572
7626
 
7573
- // src/sandbox/turn-tool-cancellation.ts
7574
- var TURN_EXEC_YIELD_MS = 250;
7575
- var TURN_WRITE_YIELD_MS = 250;
7576
- var SHELL_HELPER_YIELD_MS = 1e3;
7577
- var SHELL_GRACEFUL_POLLS = 2;
7578
- var SHELL_POLL_MS = 100;
7579
- var SHELL_MARKER_DIR = "/tmp/opengeni-turn-shell";
7580
- var delay = async (milliseconds) => await new Promise((resolve) => setTimeout(resolve, milliseconds));
7581
- var TurnSandboxCommandCancelledError = class extends Error {
7582
- name = "TurnSandboxCommandCancelledError";
7583
- constructor(reason) {
7584
- super(
7585
- reason instanceof Error ? reason.message : String(reason ?? "Turn sandbox tools were cancelled"),
7586
- {
7587
- ...reason instanceof Error ? { cause: reason } : {}
7588
- }
7589
- );
7590
- }
7591
- };
7592
- function cancellationError(reason) {
7593
- return new TurnSandboxCommandCancelledError(reason);
7594
- }
7595
- function parsedObject(input) {
7596
- try {
7597
- const parsed = JSON.parse(input);
7598
- return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
7599
- } catch {
7600
- return null;
7601
- }
7602
- }
7603
- function cappedYield(value, cap) {
7604
- return typeof value === "number" && Number.isInteger(value) && value >= 0 ? Math.min(value, cap) : cap;
7605
- }
7606
- function execOutput(raw) {
7607
- const marker = "\nOutput:\n";
7608
- const index = raw.indexOf(marker);
7609
- return index >= 0 ? raw.slice(index + marker.length) : "";
7610
- }
7611
- function nativeCommandBanner(result) {
7612
- if (typeof result === "string") return result;
7613
- if (!result || typeof result !== "object") {
7614
- throw new Error("Sandbox command returned an invalid result");
7615
- }
7616
- const value = result;
7617
- const sessionId = typeof value.sessionId === "number" ? value.sessionId : typeof value.session_id === "number" ? value.session_id : null;
7618
- const exitCode = typeof value.exitCode === "number" ? value.exitCode : typeof value.exit_code === "number" ? value.exit_code : null;
7619
- const output = [value.output, value.stderr, value.stdout].filter(
7620
- (candidate) => typeof candidate === "string" && candidate.length > 0
7621
- ).join("\n");
7622
- if (sessionId !== null) {
7623
- return `Process running with session ID ${sessionId}
7624
-
7625
- Output:
7626
- ${output}`;
7627
- }
7628
- if (exitCode !== null) {
7629
- return `Process exited with code ${exitCode}
7630
-
7631
- Output:
7632
- ${output}`;
7633
- }
7634
- throw new Error("Sandbox command did not report a session id or exit code");
7635
- }
7636
- function completedCommandBanner(exitCode, output) {
7637
- return `Process exited with code ${exitCode}
7638
-
7639
- Output:
7640
- ${output}`;
7641
- }
7642
- function appendBoundedOutput(current, chunk, maxOutputTokens) {
7643
- const maxChars = Math.max(512, maxOutputTokens * 4);
7644
- const combined = current + chunk;
7645
- return combined.length <= maxChars ? combined : combined.slice(-maxChars);
7646
- }
7647
- function singleQuote(value) {
7648
- return `'${value.replaceAll("'", `'"'"'`)}'`;
7649
- }
7650
- function shellMarkerPath(token) {
7651
- return `${SHELL_MARKER_DIR}/${token}`;
7652
- }
7653
- function cancellableGroupLeaderCommand(command, markerPath) {
7654
- const marker = singleQuote(markerPath);
7655
- const markerDir = singleQuote(SHELL_MARKER_DIR);
7656
- return [
7657
- `__opengeni_marker=${marker}`,
7658
- "umask 077",
7659
- `command mkdir -p ${markerDir} || exit 125`,
7660
- '__opengeni_pid="$$"',
7661
- `__opengeni_pgid="$(command ps -o pgid= -p "$__opengeni_pid" 2>/dev/null | command tr -d '[:space:]')"`,
7662
- 'case "$__opengeni_pid:$__opengeni_pgid" in *[!0-9:]*|*:|:*) exit 125 ;; esac',
7663
- '[ "$__opengeni_pid" -gt 1 ] && [ "$__opengeni_pgid" -gt 1 ] || exit 125',
7664
- // Never signal a provider/container-wide process group. A cancellable PTY
7665
- // command must be its group's leader; fail the command closed otherwise.
7666
- '[ "$__opengeni_pid" = "$__opengeni_pgid" ] || exit 125',
7667
- `command printf '%s %s\\n' "$__opengeni_pid" "$__opengeni_pgid" > "$__opengeni_marker" || exit 125`,
7668
- `trap 'command rm -f "$__opengeni_marker"' EXIT`,
7669
- "(",
7670
- command,
7671
- ")",
7672
- "__opengeni_status=$?",
7673
- 'exit "$__opengeni_status"'
7674
- ].join("\n");
7675
- }
7676
- function cancellableShellCommand(command, markerPath) {
7677
- const groupLeaderCommand = cancellableGroupLeaderCommand(command, markerPath);
7678
- return [
7679
- '__opengeni_outer_pid="$$"',
7680
- `__opengeni_outer_pgid="$(command ps -o pgid= -p "$__opengeni_outer_pid" 2>/dev/null | command tr -d '[:space:]')"`,
7681
- 'case "$__opengeni_outer_pid:$__opengeni_outer_pgid" in *[!0-9:]*|*:|:*) exit 125 ;; esac',
7682
- 'if [ "$__opengeni_outer_pid" != "$__opengeni_outer_pgid" ]; then',
7683
- ' __opengeni_setsid="$(command -v setsid 2>/dev/null)"',
7684
- ' [ -n "$__opengeni_setsid" ] || exit 125',
7685
- ` exec "$__opengeni_setsid" /bin/sh -c ${singleQuote(groupLeaderCommand)}`,
7686
- "fi",
7687
- groupLeaderCommand
7688
- ].join("\n");
7689
- }
7690
- function shellHelperInput(command) {
7691
- return JSON.stringify({
7692
- cmd: command,
7693
- login: false,
7694
- tty: false,
7695
- yield_time_ms: SHELL_HELPER_YIELD_MS,
7696
- max_output_tokens: 128
7697
- });
7698
- }
7699
- function safeProcessIdentity(raw) {
7700
- if (parseExecBannerExitCode(raw) !== 0) return null;
7701
- const match = execOutput(raw).trim().match(/^(\d+)\s+(\d+)$/);
7702
- if (!match) return null;
7703
- const pid = Number.parseInt(match[1], 10);
7704
- const processGroupId = Number.parseInt(match[2], 10);
7705
- return Number.isSafeInteger(pid) && Number.isSafeInteger(processGroupId) && pid > 1 && processGroupId > 1 ? { pid, processGroupId } : null;
7706
- }
7707
- function identityGuardScript(state, body, missingIdentityExitCode) {
7708
- const identity = state.identity;
7709
- if (!identity || !state.token) return `exit ${missingIdentityExitCode}`;
7710
- const token = singleQuote(state.token);
7711
- return [
7712
- `__opengeni_pid=${identity.pid}`,
7713
- `__opengeni_pgid=${identity.processGroupId}`,
7714
- `__opengeni_token=${token}`,
7715
- // The randomized token lands late in the wrapped command line. Procps
7716
- // otherwise truncates `args` to the terminal width, which can make a live
7717
- // group look stale and open the quiescence fence without signalling it.
7718
- '__opengeni_args="$(command ps -ww -o args= -p "$__opengeni_pid" 2>/dev/null)"',
7719
- 'case "$__opengeni_args" in *"$__opengeni_token"*) ;; *) exit 0 ;; esac',
7720
- `__opengeni_live_pgid="$(command ps -o pgid= -p "$__opengeni_pid" 2>/dev/null | command tr -d '[:space:]')"`,
7721
- '[ "$__opengeni_live_pgid" = "$__opengeni_pgid" ] || exit 0',
7722
- body
7723
- ].join("\n");
7724
- }
7725
- function wrapComputer(computer, track) {
7726
- const wrapped = Object.create(Object.getPrototypeOf(computer));
7727
- Object.assign(wrapped, computer);
7728
- for (const method of [
7729
- "initRun",
7730
- "screenshot",
7731
- "click",
7732
- "doubleClick",
7733
- "scroll",
7734
- "type",
7735
- "wait",
7736
- "move",
7737
- "keypress",
7738
- "drag"
7739
- ]) {
7740
- const original = computer[method];
7741
- if (typeof original !== "function") continue;
7742
- wrapped[method] = (...args) => track(async () => await original.apply(computer, args));
7743
- }
7744
- return wrapped;
7745
- }
7746
- var TurnToolCancellationControllerImpl = class {
7747
- cancelled = false;
7748
- reason;
7749
- inFlight = /* @__PURE__ */ new Set();
7750
- shellSessions = /* @__PURE__ */ new Map();
7751
- remoteExecs = /* @__PURE__ */ new Set();
7752
- rawExecInvoke = null;
7753
- rawWriteInvoke = null;
7754
- drainPromise = null;
7755
- signal;
7756
- onAbort = () => this.cancel(this.signal?.reason);
7757
- constructor(signal) {
7758
- this.signal = signal;
7759
- if (signal?.aborted) {
7760
- this.cancel(signal.reason);
7761
- } else {
7762
- signal?.addEventListener("abort", this.onAbort, { once: true });
7763
- }
7764
- }
7765
- cancel(reason) {
7766
- if (!this.cancelled) {
7767
- this.cancelled = true;
7768
- this.reason = reason;
7769
- this.signal?.removeEventListener("abort", this.onAbort);
7770
- }
7771
- void this.ensureDrain().catch(() => void 0);
7772
- }
7773
- async waitForQuiescence() {
7774
- this.cancel(this.reason);
7775
- await this.ensureDrain();
7776
- }
7777
- runSandboxCommand(session, args) {
7778
- return this.track(async () => {
7779
- if (!session.exec && !session.execCommand) {
7780
- throw new Error("Sandbox session does not support command execution");
7781
- }
7782
- const lifecycleRunContext = {};
7783
- const invokeExec = async (_runContext, input) => {
7784
- const parsed = parsedObject(input);
7785
- if (!parsed || typeof parsed.cmd !== "string") {
7786
- throw new Error("Sandbox lifecycle command input was invalid");
7787
- }
7788
- const commandArgs = {
7789
- cmd: parsed.cmd,
7790
- ...typeof parsed.workdir === "string" ? { workdir: parsed.workdir } : {},
7791
- ...typeof parsed.shell === "string" ? { shell: parsed.shell } : {},
7792
- ...typeof parsed.login === "boolean" ? { login: parsed.login } : {},
7793
- ...typeof parsed.tty === "boolean" ? { tty: parsed.tty } : {},
7794
- ...typeof parsed.yield_time_ms === "number" ? { yieldTimeMs: parsed.yield_time_ms } : {},
7795
- ...typeof parsed.max_output_tokens === "number" ? { maxOutputTokens: parsed.max_output_tokens } : {},
7796
- ...typeof parsed.run_as === "string" ? { runAs: parsed.run_as } : {}
7797
- };
7798
- const result = session.supportsPty?.() !== false && session.execCommand ? await session.execCommand(commandArgs) : session.exec ? await session.exec(commandArgs) : await session.execCommand(commandArgs);
7799
- return nativeCommandBanner(result);
7800
- };
7801
- const invokeWrite = session.writeStdin ? async (_runContext, input) => {
7802
- const parsed = parsedObject(input);
7803
- if (!parsed || typeof parsed.session_id !== "number") {
7804
- throw new Error("Sandbox lifecycle stdin input was invalid");
7805
- }
7806
- return await session.writeStdin({
7807
- sessionId: parsed.session_id,
7808
- ...typeof parsed.chars === "string" ? { chars: parsed.chars } : {},
7809
- ...typeof parsed.yield_time_ms === "number" ? { yieldTimeMs: parsed.yield_time_ms } : {},
7810
- ...typeof parsed.max_output_tokens === "number" ? { maxOutputTokens: parsed.max_output_tokens } : {}
7811
- });
7812
- } : null;
7813
- const correlationId = `turn_lifecycle_${crypto.randomUUID()}`;
7814
- const useRemoteOpCancellation = Boolean(session.cancelExecCommand) && session.supportsPty?.() === false;
7815
- const remoteExec = session.cancelExecCommand && useRemoteOpCancellation ? this.registerRemoteExec(
7816
- { cancelExecCommand: session.cancelExecCommand.bind(session) },
7817
- `${sanitizeOpIdToken(correlationId)}:0`
7818
- ) : null;
7819
- const markerPath = shellMarkerPath(crypto.randomUUID());
7820
- const commandInput = JSON.stringify({
7821
- cmd: useRemoteOpCancellation ? args.cmd : cancellableShellCommand(args.cmd, markerPath),
7822
- ...args.workdir ? { workdir: args.workdir } : {},
7823
- ...args.shell ? { shell: args.shell } : {},
7824
- ...args.login !== void 0 ? { login: args.login } : {},
7825
- ...args.runAs ? { run_as: args.runAs } : {},
7826
- tty: useRemoteOpCancellation ? args.tty : true,
7827
- yield_time_ms: useRemoteOpCancellation ? args.yieldTimeMs : cappedYield(args.yieldTimeMs, TURN_EXEC_YIELD_MS),
7828
- ...args.maxOutputTokens !== void 0 ? { max_output_tokens: args.maxOutputTokens } : {}
7829
- });
7830
- let initial;
7831
- try {
7832
- initial = await runWithToolCallCorrelation(
7833
- correlationId,
7834
- async () => await invokeExec(lifecycleRunContext, commandInput, void 0)
7835
- );
7836
- } finally {
7837
- remoteExec?.settle();
7838
- }
7839
- if (typeof initial !== "string" || useRemoteOpCancellation) return initial;
7840
- const sessionId = parseExecBannerSessionId(initial);
7841
- if (sessionId === null) return initial;
7842
- if (!invokeWrite) {
7843
- throw new Error("Sandbox lifecycle command yielded without stdin support");
7844
- }
7845
- const token = markerPath.slice(markerPath.lastIndexOf("/") + 1);
7846
- const state = {
7847
- sessionId,
7848
- markerPath,
7849
- token,
7850
- runContext: lifecycleRunContext,
7851
- execInvoke: invokeExec,
7852
- writeInvoke: invokeWrite,
7853
- identity: null,
7854
- identityValidated: false,
7855
- cancellation: null
7856
- };
7857
- this.shellSessions.set(sessionId, state);
7858
- const maxOutputTokens = args.maxOutputTokens ?? 2e4;
7859
- const initialOutput = execOutput(initial);
7860
- let output = initialOutput ? appendBoundedOutput("", initialOutput, maxOutputTokens) : "";
7861
- while (true) {
7862
- if (this.cancelled) throw cancellationError(this.reason);
7863
- const next = await invokeWrite(
7864
- lifecycleRunContext,
7865
- JSON.stringify({
7866
- session_id: sessionId,
7867
- chars: "",
7868
- yield_time_ms: TURN_WRITE_YIELD_MS,
7869
- max_output_tokens: maxOutputTokens
7870
- }),
7871
- void 0
7872
- );
7873
- if (typeof next !== "string") {
7874
- throw new Error("Sandbox lifecycle stdin returned an invalid result");
7875
- }
7876
- const nextOutput = execOutput(next);
7877
- if (nextOutput) output = appendBoundedOutput(output, nextOutput, maxOutputTokens);
7878
- const exitCode = parseExecBannerExitCode(next);
7879
- if (exitCode !== null) {
7880
- this.shellSessions.delete(sessionId);
7881
- return completedCommandBanner(exitCode, output);
7882
- }
7883
- await delay(SHELL_POLL_MS);
7884
- }
7885
- });
7886
- }
7887
- wrapTools(tools, session) {
7888
- for (const tool2 of tools) {
7889
- if (tool2.type !== "function") continue;
7890
- if (tool2.name === "exec_command") this.rawExecInvoke = tool2.invoke;
7891
- if (tool2.name === "write_stdin") this.rawWriteInvoke = tool2.invoke;
7892
- }
7893
- return tools.map((tool2) => {
7894
- if (tool2.type === "function") return this.wrapFunctionTool(tool2, session);
7895
- if (tool2.type === "apply_patch") return this.wrapApplyPatchTool(tool2);
7896
- if (tool2.type === "computer") return this.wrapComputerTool(tool2);
7897
- return tool2;
7898
- });
7899
- }
7900
- wrapFunctionTool(tool2, cancellationSession) {
7901
- if (tool2.name === "exec_command") {
7902
- return {
7903
- ...tool2,
7904
- invoke: (runContext, input, details) => this.track(async () => {
7905
- const parsed = parsedObject(input);
7906
- if (!parsed || typeof parsed.cmd !== "string") {
7907
- return await this.correlatedInvoke(tool2.invoke, runContext, input, details);
7908
- }
7909
- const token = crypto.randomUUID();
7910
- const markerPath = shellMarkerPath(token);
7911
- const correlationId = details?.toolCall?.callId ?? `turn_exec_${token}`;
7912
- const cancelExecCommand = cancellationSession?.cancelExecCommand;
7913
- const useRemoteOpCancellation = Boolean(cancelExecCommand) && cancellationSession?.supportsPty?.() === false;
7914
- const remoteExec = cancelExecCommand && useRemoteOpCancellation ? this.registerRemoteExec(
7915
- { cancelExecCommand: cancelExecCommand.bind(cancellationSession) },
7916
- `${sanitizeOpIdToken(correlationId)}:0`
7917
- ) : null;
7918
- const cancellableInput = useRemoteOpCancellation ? input : JSON.stringify({
7919
- ...parsed,
7920
- cmd: cancellableShellCommand(parsed.cmd, markerPath),
7921
- // The SDK's only provider-neutral live-process interrupt is a PTY.
7922
- // The process marker provides TERM/KILL escalation when Ctrl-C is
7923
- // ignored; the short yield exposes the provider session promptly.
7924
- tty: true,
7925
- yield_time_ms: cappedYield(parsed.yield_time_ms, TURN_EXEC_YIELD_MS)
7926
- });
7927
- let output;
7928
- try {
7929
- output = await runWithToolCallCorrelation(
7930
- correlationId,
7931
- async () => await tool2.invoke(runContext, cancellableInput, details)
7932
- );
7933
- } finally {
7934
- remoteExec?.settle();
7935
- }
7936
- if (typeof output !== "string") return output;
7937
- const sessionId = useRemoteOpCancellation ? null : parseExecBannerSessionId(output);
7938
- if (sessionId !== null) {
7939
- this.shellSessions.set(sessionId, {
7940
- sessionId,
7941
- markerPath,
7942
- token,
7943
- runContext,
7944
- execInvoke: tool2.invoke,
7945
- writeInvoke: this.rawWriteInvoke,
7946
- identity: null,
7947
- identityValidated: false,
7948
- cancellation: null
7949
- });
7950
- }
7951
- return output;
7952
- })
7953
- };
7954
- }
7955
- if (tool2.name === "write_stdin") {
7956
- return {
7957
- ...tool2,
7958
- invoke: (runContext, input, details) => this.track(async () => {
7959
- const parsed = parsedObject(input);
7960
- const sessionId = parsed && typeof parsed.session_id === "number" && Number.isSafeInteger(parsed.session_id) ? parsed.session_id : null;
7961
- const cappedInput = parsed ? JSON.stringify({
7962
- ...parsed,
7963
- yield_time_ms: cappedYield(parsed.yield_time_ms, TURN_WRITE_YIELD_MS)
7964
- }) : input;
7965
- const output = await tool2.invoke(runContext, cappedInput, details);
7966
- if (sessionId !== null && typeof output === "string") {
7967
- if (isExecSessionLostBanner(output, sessionId)) {
7968
- this.shellSessions.delete(sessionId);
7969
- } else if (parseExecBannerSessionId(output) === sessionId) {
7970
- if (!this.shellSessions.has(sessionId)) {
7971
- this.shellSessions.set(sessionId, {
7972
- sessionId,
7973
- markerPath: null,
7974
- token: null,
7975
- runContext,
7976
- execInvoke: this.rawExecInvoke ?? tool2.invoke,
7977
- writeInvoke: tool2.invoke,
7978
- identity: null,
7979
- identityValidated: false,
7980
- cancellation: null
7981
- });
7982
- }
7983
- } else if (parseExecBannerExitCode(output) !== null) {
7984
- this.shellSessions.delete(sessionId);
7985
- }
7986
- }
7987
- return output;
7988
- })
7989
- };
7990
- }
7991
- return {
7992
- ...tool2,
7993
- invoke: (runContext, input, details) => this.track(async () => await tool2.invoke(runContext, input, details))
7994
- };
7995
- }
7996
- wrapApplyPatchTool(tool2) {
7997
- const editor = tool2.editor;
7998
- const wrappedEditor = {
7999
- createFile: (operation, context) => this.track(async () => await editor.createFile(operation, context)),
8000
- updateFile: (operation, context) => this.track(async () => await editor.updateFile(operation, context)),
8001
- deleteFile: (operation, context) => this.track(async () => await editor.deleteFile(operation, context))
8002
- };
8003
- return { ...tool2, editor: wrappedEditor };
8004
- }
8005
- wrapComputerTool(tool2) {
8006
- const computer = tool2.computer;
8007
- if (typeof computer === "function") {
8008
- return {
8009
- ...tool2,
8010
- computer: async (...args) => wrapComputer(
8011
- await this.track(async () => await computer(...args)),
8012
- (operation) => this.track(operation)
8013
- )
8014
- };
8015
- }
8016
- if ("create" in computer && typeof computer.create === "function") {
8017
- return {
8018
- ...tool2,
8019
- computer: {
8020
- ...computer,
8021
- create: async (...args) => wrapComputer(
8022
- await this.track(async () => await computer.create(...args)),
8023
- (operation) => this.track(operation)
8024
- )
8025
- }
8026
- };
8027
- }
8028
- return {
8029
- ...tool2,
8030
- computer: wrapComputer(computer, (operation) => this.track(operation))
8031
- };
8032
- }
8033
- async correlatedInvoke(invoke, runContext, input, details) {
8034
- const callId = details?.toolCall?.callId;
8035
- return callId ? await runWithToolCallCorrelation(
8036
- callId,
8037
- async () => await invoke(runContext, input, details)
8038
- ) : await invoke(runContext, input, details);
8039
- }
8040
- track(operation) {
8041
- if (this.cancelled) return Promise.reject(cancellationError(this.reason));
8042
- const promise = Promise.resolve().then(async () => {
8043
- if (this.cancelled) throw cancellationError(this.reason);
8044
- return await operation();
8045
- });
8046
- this.inFlight.add(promise);
8047
- void promise.finally(() => {
8048
- this.inFlight.delete(promise);
8049
- }).catch(() => void 0);
8050
- return promise;
8051
- }
8052
- ensureDrain() {
8053
- this.drainPromise ??= this.drain();
8054
- return this.drainPromise;
8055
- }
8056
- async drain() {
8057
- while (true) {
8058
- const remoteCancellations = [...this.remoteExecs].map(async (exec) => await exec.cancel());
8059
- const inFlight = [...this.inFlight];
8060
- if (inFlight.length > 0 || remoteCancellations.length > 0) {
8061
- await Promise.allSettled([...inFlight, ...remoteCancellations]);
8062
- }
8063
- const sessions = [...this.shellSessions.values()];
8064
- if (sessions.length > 0) {
8065
- await Promise.all(sessions.map(async (session) => await this.cancelShellSession(session)));
8066
- }
8067
- if (this.inFlight.size === 0 && this.shellSessions.size === 0 && this.remoteExecs.size === 0)
8068
- return;
8069
- }
8070
- }
8071
- registerRemoteExec(session, opId) {
8072
- let resolveSettled;
8073
- const settledPromise = new Promise((resolve) => {
8074
- resolveSettled = resolve;
8075
- });
8076
- let cancellation = null;
8077
- const entry = {
8078
- settled: false,
8079
- settledPromise,
8080
- settle: () => {
8081
- if (entry.settled) return;
8082
- entry.settled = true;
8083
- resolveSettled();
8084
- this.remoteExecs.delete(entry);
8085
- },
8086
- cancel: () => {
8087
- cancellation ??= (async () => {
8088
- while (!entry.settled) {
8089
- const attempt = session.cancelExecCommand(opId).catch(() => false);
8090
- await Promise.race([attempt, entry.settledPromise]);
8091
- await Promise.race([entry.settledPromise, delay(SHELL_POLL_MS)]);
8092
- }
8093
- })();
8094
- return cancellation;
8095
- }
8096
- };
8097
- this.remoteExecs.add(entry);
8098
- return entry;
8099
- }
8100
- cancelShellSession(state) {
8101
- state.cancellation ??= this.cancelShellSessionOnce(state).finally(() => {
8102
- state.cancellation = null;
8103
- });
8104
- return state.cancellation;
8105
- }
8106
- async cancelShellSessionOnce(state) {
8107
- if (!state.markerPath) {
8108
- while (true) {
8109
- if (state.writeInvoke && await this.rawWrite(state, "", SHELL_POLL_MS)) {
8110
- this.shellSessions.delete(state.sessionId);
8111
- return;
8112
- }
8113
- await delay(SHELL_POLL_MS);
8114
- }
8115
- }
8116
- while (!state.identity) {
8117
- state.identity = await this.readIdentity(state);
8118
- if (state.identity) break;
8119
- if (state.writeInvoke) {
8120
- const completed = await this.rawWrite(state, "", SHELL_POLL_MS);
8121
- if (completed) {
8122
- this.shellSessions.delete(state.sessionId);
8123
- return;
8124
- }
8125
- }
8126
- await delay(SHELL_POLL_MS);
8127
- }
8128
- if (!await this.identityAlive(state)) {
8129
- this.shellSessions.delete(state.sessionId);
8130
- return;
8131
- }
8132
- if (state.writeInvoke) {
8133
- await this.rawWrite(state, "", SHELL_POLL_MS);
8134
- if (!await this.identityAlive(state)) {
8135
- this.shellSessions.delete(state.sessionId);
8136
- return;
8137
- }
8138
- }
8139
- await this.signalIdentity(state, "TERM");
8140
- for (let attempt = 0; attempt < SHELL_GRACEFUL_POLLS; attempt++) {
8141
- if (state.writeInvoke) await this.rawWrite(state, "", SHELL_POLL_MS);
8142
- if (!await this.identityAlive(state)) {
8143
- this.shellSessions.delete(state.sessionId);
8144
- return;
8145
- }
8146
- }
8147
- await this.signalIdentity(state, "KILL");
8148
- while (await this.identityAlive(state)) {
8149
- if (state.writeInvoke) await this.rawWrite(state, "", SHELL_POLL_MS);
8150
- await this.signalIdentity(state, "KILL");
8151
- await delay(SHELL_POLL_MS);
8152
- }
8153
- this.shellSessions.delete(state.sessionId);
8154
- }
8155
- async rawWrite(state, chars, yieldTimeMs) {
8156
- if (!state.writeInvoke) return false;
8157
- try {
8158
- const output = await state.writeInvoke(
8159
- state.runContext,
8160
- JSON.stringify({
8161
- session_id: state.sessionId,
8162
- chars,
8163
- yield_time_ms: yieldTimeMs,
8164
- max_output_tokens: 128
8165
- }),
8166
- void 0
8167
- );
8168
- if (typeof output !== "string") return false;
8169
- if (isExecSessionLostBanner(output, state.sessionId)) return true;
8170
- if (parseExecBannerSessionId(output) === state.sessionId) return false;
8171
- if (parseExecBannerExitCode(output) !== null) return true;
8172
- } catch {
8173
- }
8174
- return false;
8175
- }
8176
- async readIdentity(state) {
8177
- if (!state.markerPath) return null;
8178
- try {
8179
- const output = await state.execInvoke(
8180
- state.runContext,
8181
- shellHelperInput(`command cat ${singleQuote(state.markerPath)} 2>/dev/null`),
8182
- void 0
8183
- );
8184
- return typeof output === "string" ? safeProcessIdentity(output) : null;
8185
- } catch {
8186
- return null;
8187
- }
8188
- }
8189
- async identityAlive(state) {
8190
- const script = state.identityValidated ? [
8191
- `__opengeni_pgid=${state.identity?.processGroupId ?? 0}`,
8192
- 'command kill -0 "-$__opengeni_pgid" 2>/dev/null && exit 75',
8193
- "exit 0"
8194
- ].join("\n") : identityGuardScript(
8195
- state,
8196
- 'command kill -0 "-$__opengeni_pgid" 2>/dev/null && exit 75\nexit 0',
8197
- 76
8198
- );
8199
- try {
8200
- const output = await state.execInvoke(state.runContext, shellHelperInput(script), void 0);
8201
- if (typeof output !== "string") return true;
8202
- const exitCode = parseExecBannerExitCode(output);
8203
- if (exitCode === 75) {
8204
- state.identityValidated = true;
8205
- return true;
8206
- }
8207
- return exitCode !== 0;
8208
- } catch {
8209
- return true;
8210
- }
8211
- }
8212
- async signalIdentity(state, signal) {
8213
- const script = state.identityValidated ? `command kill -${signal} "-${state.identity?.processGroupId ?? 0}" 2>/dev/null || exit 76` : identityGuardScript(
8214
- state,
8215
- `command kill -${signal} "-$__opengeni_pgid" 2>/dev/null || exit 76
8216
- exit 0`,
8217
- 76
8218
- );
8219
- try {
8220
- await state.execInvoke(state.runContext, shellHelperInput(script), void 0);
8221
- } catch {
8222
- }
8223
- }
8224
- };
8225
- function createTurnToolCancellationController(signal) {
8226
- return new TurnToolCancellationControllerImpl(signal);
8227
- }
8228
- function wrapCapabilityToolsForTurnCancellation(capability, controller) {
8229
- const originalTools = capability.tools;
8230
- capability.tools = function() {
8231
- return controller.wrapTools(originalTools.call(this), this._session);
8232
- };
8233
- }
8234
-
8235
7627
  // src/sandbox-computer.ts
8236
7628
  import {
8237
7629
  KeyAction,
@@ -8963,7 +8355,9 @@ var ComputerUseCapability = class extends Capability {
8963
8355
  // any computer-use-only recording. One SandboxComputer instance caches
8964
8356
  // this for the rest of the turn.
8965
8357
  prepare: async (preparedSession) => {
8966
- await ensureDisplayStack(preparedSession);
8358
+ await ensureDisplayStack(preparedSession, {
8359
+ telemetryContext: { callerKind: "computer" }
8360
+ });
8967
8361
  await this.args.onReady?.(preparedSession);
8968
8362
  }
8969
8363
  });
@@ -9008,43 +8402,351 @@ var ComputerUseCapability = class extends Capability {
9008
8402
  };
9009
8403
 
9010
8404
  // src/usage-telemetry.ts
9011
- function modelCallUsageTelemetry(usage) {
8405
+ var MAX_MODEL_USAGE_TOKEN_COUNT = 1e9;
8406
+ function modelUsageTokenCountOrNull(value) {
8407
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 && value <= MAX_MODEL_USAGE_TOKEN_COUNT ? value : null;
8408
+ }
8409
+ function normalizeModelCallUsage(usage) {
8410
+ const rejectedFields = /* @__PURE__ */ new Set();
8411
+ if (!usage) {
8412
+ return {
8413
+ telemetry: {
8414
+ inputTokens: null,
8415
+ outputTokens: null,
8416
+ cachedTokens: null,
8417
+ cacheWriteTokens: null,
8418
+ reasoningTokens: null
8419
+ },
8420
+ totalTokens: null,
8421
+ rejectedFields: []
8422
+ };
8423
+ }
8424
+ const requestEntries = requestUsageEntries(usage.requestUsageEntries, rejectedFields);
8425
+ const topLevelInputTokens = reportedTokenCount(usage.inputTokens, "inputTokens", rejectedFields);
8426
+ const topLevelOutputTokens = reportedTokenCount(
8427
+ usage.outputTokens,
8428
+ "outputTokens",
8429
+ rejectedFields
8430
+ );
8431
+ const topLevelTotalTokens = reportedTokenCount(usage.totalTokens, "totalTokens", rejectedFields);
8432
+ const requestInputTokens = completeRequestTokenAggregate(
8433
+ requestEntries,
8434
+ ["inputTokens", "input_tokens"],
8435
+ "inputTokens.requestUsageEntries",
8436
+ rejectedFields
8437
+ );
8438
+ const requestOutputTokens = completeRequestTokenAggregate(
8439
+ requestEntries,
8440
+ ["outputTokens", "output_tokens"],
8441
+ "outputTokens.requestUsageEntries",
8442
+ rejectedFields
8443
+ );
8444
+ let requestTotalTokens = completeRequestTokenAggregate(
8445
+ requestEntries,
8446
+ ["totalTokens", "total_tokens"],
8447
+ "totalTokens.requestUsageEntries",
8448
+ rejectedFields
8449
+ );
8450
+ if (rejectInconsistentRequestTotals(requestEntries, rejectedFields)) {
8451
+ requestTotalTokens = { ...requestTotalTokens, status: "invalid", value: null };
8452
+ }
8453
+ const inputTokens = canonicalTokenDimension({
8454
+ topLevel: topLevelInputTokens,
8455
+ topLevelReported: isReported(usage.inputTokens),
8456
+ topLevelPath: "inputTokens",
8457
+ requestAggregate: requestInputTokens,
8458
+ rejectedFields
8459
+ });
8460
+ const outputTokens = canonicalTokenDimension({
8461
+ topLevel: topLevelOutputTokens,
8462
+ topLevelReported: isReported(usage.outputTokens),
8463
+ topLevelPath: "outputTokens",
8464
+ requestAggregate: requestOutputTokens,
8465
+ rejectedFields
8466
+ });
8467
+ const totalTokens = canonicalTotalTokens({
8468
+ inputTokens,
8469
+ outputTokens,
8470
+ topLevel: topLevelTotalTokens,
8471
+ topLevelReported: isReported(usage.totalTokens),
8472
+ requestAggregate: requestTotalTokens,
8473
+ rejectedFields
8474
+ });
8475
+ const inputDetailSource = preferredDetailSource(
8476
+ usage.inputTokensDetails,
8477
+ "inputTokensDetails",
8478
+ requestEntries,
8479
+ ["inputTokensDetails", "input_tokens_details"]
8480
+ );
8481
+ const outputDetailSource = preferredDetailSource(
8482
+ usage.outputTokensDetails,
8483
+ "outputTokensDetails",
8484
+ requestEntries,
8485
+ ["outputTokensDetails", "output_tokens_details"]
8486
+ );
9012
8487
  return {
9013
- inputTokens: finiteNumberOrNull(usage?.inputTokens),
9014
- outputTokens: finiteNumberOrNull(usage?.outputTokens),
9015
- cachedTokens: usage ? firstPositiveDetailNumber(usage.inputTokensDetails, [
9016
- "cached_tokens",
9017
- "cachedInputTokens",
9018
- "cached_input_tokens"
9019
- ]) : null,
9020
- reasoningTokens: usage ? firstPositiveDetailNumber(usage.outputTokensDetails, [
9021
- "reasoning_tokens",
9022
- "reasoningTokens",
9023
- "reasoning_output_tokens"
9024
- ]) : null
8488
+ telemetry: {
8489
+ inputTokens,
8490
+ outputTokens,
8491
+ cachedTokens: aggregateDetailTokenCount(
8492
+ inputDetailSource,
8493
+ ["cached_tokens", "cachedInputTokens", "cached_input_tokens"],
8494
+ "cachedTokens",
8495
+ rejectedFields
8496
+ ),
8497
+ cacheWriteTokens: aggregateDetailTokenCount(
8498
+ inputDetailSource,
8499
+ ["cache_write_tokens", "cacheWriteTokens"],
8500
+ "cacheWriteTokens",
8501
+ rejectedFields
8502
+ ),
8503
+ reasoningTokens: aggregateDetailTokenCount(
8504
+ outputDetailSource,
8505
+ ["reasoning_tokens", "reasoningTokens", "reasoning_output_tokens"],
8506
+ "reasoningTokens",
8507
+ rejectedFields
8508
+ )
8509
+ },
8510
+ totalTokens,
8511
+ rejectedFields: [...rejectedFields].slice(0, 32)
9025
8512
  };
9026
8513
  }
9027
- function firstPositiveDetailNumber(details, keys) {
9028
- if (!details) {
8514
+ function modelCallUsageTelemetry(usage) {
8515
+ return normalizeModelCallUsage(usage).telemetry;
8516
+ }
8517
+ function requestUsageEntries(value, rejectedFields) {
8518
+ if (value === void 0) {
8519
+ return [];
8520
+ }
8521
+ if (!Array.isArray(value)) {
8522
+ rejectField(rejectedFields, "requestUsageEntries");
8523
+ return [];
8524
+ }
8525
+ const entries = [];
8526
+ for (const [index, entry] of value.entries()) {
8527
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
8528
+ rejectField(rejectedFields, `requestUsageEntries[${index}]`);
8529
+ entries.push({});
8530
+ continue;
8531
+ }
8532
+ entries.push(entry);
8533
+ }
8534
+ return entries;
8535
+ }
8536
+ function completeRequestTokenAggregate(requestEntries, requestKeys, aggregatePath, rejectedFields) {
8537
+ if (requestEntries.length === 0) {
8538
+ return { status: "absent", value: null };
8539
+ }
8540
+ const values = [];
8541
+ let incomplete = false;
8542
+ let invalid = false;
8543
+ for (const [index, entry] of requestEntries.entries()) {
8544
+ const selected = firstPresentEntry(entry, requestKeys);
8545
+ if (!selected || selected.value === null) {
8546
+ incomplete = true;
8547
+ continue;
8548
+ }
8549
+ const parsed = reportedTokenCount(
8550
+ selected.value,
8551
+ `requestUsageEntries[${index}].${selected.key}`,
8552
+ rejectedFields
8553
+ );
8554
+ if (parsed === null) invalid = true;
8555
+ values.push(parsed);
8556
+ }
8557
+ if (invalid) {
8558
+ return { status: "invalid", value: null };
8559
+ }
8560
+ if (incomplete) {
8561
+ return { status: "incomplete", value: null };
8562
+ }
8563
+ const value = boundedTokenSum(values, aggregatePath, rejectedFields);
8564
+ return value === null ? { status: "invalid", value: null } : { status: "complete", value };
8565
+ }
8566
+ function canonicalTokenDimension(input) {
8567
+ if (input.requestAggregate.status === "complete") {
8568
+ if (input.topLevelReported && input.topLevel !== null && input.topLevel !== input.requestAggregate.value) {
8569
+ rejectField(input.rejectedFields, input.topLevelPath);
8570
+ }
8571
+ return input.requestAggregate.value;
8572
+ }
8573
+ if (input.requestAggregate.status === "invalid") {
8574
+ if (input.topLevelReported) {
8575
+ rejectField(input.rejectedFields, input.topLevelPath);
8576
+ }
9029
8577
  return null;
9030
8578
  }
9031
- const entries = Array.isArray(details) ? details : [details];
9032
- let sawZero = false;
9033
- for (const entry of entries) {
9034
- for (const key of keys) {
9035
- const value = finiteNumberOrNull(entry[key]);
9036
- if (value !== null && value > 0) {
9037
- return value;
8579
+ return input.topLevel;
8580
+ }
8581
+ function canonicalTotalTokens(input) {
8582
+ if (input.inputTokens !== null && input.outputTokens !== null) {
8583
+ const derived = boundedTokenSum(
8584
+ [input.inputTokens, input.outputTokens],
8585
+ "totalTokens.aggregate",
8586
+ input.rejectedFields
8587
+ );
8588
+ if (derived === null) {
8589
+ if (input.topLevelReported) rejectField(input.rejectedFields, "totalTokens");
8590
+ if (input.requestAggregate.status === "complete") {
8591
+ rejectField(input.rejectedFields, "totalTokens.requestUsageEntries");
9038
8592
  }
9039
- if (value === 0) {
9040
- sawZero = true;
8593
+ return null;
8594
+ }
8595
+ if (input.topLevelReported && input.topLevel !== null && input.topLevel !== derived) {
8596
+ rejectField(input.rejectedFields, "totalTokens");
8597
+ }
8598
+ if (input.requestAggregate.status === "complete" && input.requestAggregate.value !== derived) {
8599
+ rejectField(input.rejectedFields, "totalTokens.requestUsageEntries");
8600
+ }
8601
+ return derived;
8602
+ }
8603
+ if (input.requestAggregate.status === "complete") {
8604
+ const requestTotal = input.requestAggregate.value;
8605
+ if (requestTotal === null || !totalCoversKnownComponents(requestTotal, input.inputTokens, input.outputTokens)) {
8606
+ rejectField(input.rejectedFields, "totalTokens.requestUsageEntries");
8607
+ if (input.topLevelReported) rejectField(input.rejectedFields, "totalTokens");
8608
+ return null;
8609
+ }
8610
+ if (input.topLevelReported && input.topLevel !== null && input.topLevel !== requestTotal) {
8611
+ rejectField(input.rejectedFields, "totalTokens");
8612
+ }
8613
+ return requestTotal;
8614
+ }
8615
+ if (input.requestAggregate.status === "invalid") {
8616
+ if (input.topLevelReported) rejectField(input.rejectedFields, "totalTokens");
8617
+ return null;
8618
+ }
8619
+ if (input.topLevel !== null && !totalCoversKnownComponents(input.topLevel, input.inputTokens, input.outputTokens)) {
8620
+ rejectField(input.rejectedFields, "totalTokens");
8621
+ return null;
8622
+ }
8623
+ return input.topLevel;
8624
+ }
8625
+ function totalCoversKnownComponents(total, inputTokens, outputTokens) {
8626
+ return (inputTokens === null || total >= inputTokens) && (outputTokens === null || total >= outputTokens);
8627
+ }
8628
+ function rejectInconsistentRequestTotals(requestEntries, rejectedFields) {
8629
+ let inconsistent = false;
8630
+ for (const [index, entry] of requestEntries.entries()) {
8631
+ const record = entry;
8632
+ const selectedInput = firstPresentEntry(record, ["inputTokens", "input_tokens"]);
8633
+ const selectedOutput = firstPresentEntry(record, ["outputTokens", "output_tokens"]);
8634
+ const selectedTotal = firstPresentEntry(record, ["totalTokens", "total_tokens"]);
8635
+ if (!selectedInput || !selectedOutput || !selectedTotal) continue;
8636
+ const inputTokens = modelUsageTokenCountOrNull(selectedInput.value);
8637
+ const outputTokens = modelUsageTokenCountOrNull(selectedOutput.value);
8638
+ const totalTokens = modelUsageTokenCountOrNull(selectedTotal.value);
8639
+ if (inputTokens === null || outputTokens === null || totalTokens === null) continue;
8640
+ const derived = inputTokens <= MAX_MODEL_USAGE_TOKEN_COUNT - outputTokens ? inputTokens + outputTokens : null;
8641
+ if (derived === null || totalTokens !== derived) {
8642
+ rejectField(rejectedFields, `requestUsageEntries[${index}].${selectedTotal.key}`);
8643
+ inconsistent = true;
8644
+ }
8645
+ }
8646
+ return inconsistent;
8647
+ }
8648
+ function isReported(value) {
8649
+ return value !== void 0 && value !== null;
8650
+ }
8651
+ function preferredDetailSource(aggregateDetails, aggregatePath, requestEntries, requestKeys) {
8652
+ const requestDetails = [];
8653
+ let sawRequestDetails = false;
8654
+ for (const [index, entry] of requestEntries.entries()) {
8655
+ const selected = firstPresentEntry(entry, requestKeys);
8656
+ if (!selected) {
8657
+ requestDetails.push({
8658
+ details: void 0,
8659
+ path: `requestUsageEntries[${index}].${requestKeys[0]}`,
8660
+ missing: true
8661
+ });
8662
+ continue;
8663
+ }
8664
+ sawRequestDetails = true;
8665
+ requestDetails.push({
8666
+ details: selected.value,
8667
+ path: `requestUsageEntries[${index}].${selected.key}`
8668
+ });
8669
+ }
8670
+ return sawRequestDetails ? requestDetails : aggregateDetails === void 0 ? [] : [{ details: aggregateDetails, path: aggregatePath }];
8671
+ }
8672
+ function aggregateDetailTokenCount(sources, keys, field, rejectedFields) {
8673
+ const values = [];
8674
+ let sawReportedValue = false;
8675
+ let sawMissingValue = false;
8676
+ for (const source of sources) {
8677
+ if (source.missing) {
8678
+ sawMissingValue = true;
8679
+ continue;
8680
+ }
8681
+ const details = detailRecords(source.details, source.path, rejectedFields);
8682
+ if (!details.complete) {
8683
+ sawMissingValue = true;
8684
+ }
8685
+ for (const detail of details.records) {
8686
+ const selected = firstPresentEntry(detail.record, keys);
8687
+ if (!selected) {
8688
+ sawMissingValue = true;
8689
+ continue;
9041
8690
  }
8691
+ sawReportedValue = true;
8692
+ values.push(
8693
+ reportedTokenCount(selected.value, `${detail.path}.${selected.key}`, rejectedFields)
8694
+ );
8695
+ }
8696
+ }
8697
+ return sawReportedValue && !sawMissingValue ? boundedTokenSum(values, `${field}.aggregate`, rejectedFields) : null;
8698
+ }
8699
+ function detailRecords(details, path, rejectedFields) {
8700
+ const entries = Array.isArray(details) ? details : [details];
8701
+ const records = [];
8702
+ let complete = true;
8703
+ for (const [index, entry] of entries.entries()) {
8704
+ const entryPath = Array.isArray(details) ? `${path}[${index}]` : path;
8705
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
8706
+ rejectField(rejectedFields, entryPath);
8707
+ complete = false;
8708
+ continue;
8709
+ }
8710
+ records.push({ record: entry, path: entryPath });
8711
+ }
8712
+ return { records, complete };
8713
+ }
8714
+ function firstPresentEntry(record, keys) {
8715
+ for (const key of keys) {
8716
+ if (Object.prototype.hasOwnProperty.call(record, key) && record[key] !== void 0) {
8717
+ return { key, value: record[key] };
8718
+ }
8719
+ }
8720
+ return null;
8721
+ }
8722
+ function reportedTokenCount(value, field, rejectedFields) {
8723
+ if (value === void 0 || value === null) {
8724
+ return null;
8725
+ }
8726
+ const normalized = modelUsageTokenCountOrNull(value);
8727
+ if (normalized === null) {
8728
+ rejectField(rejectedFields, field);
8729
+ }
8730
+ return normalized;
8731
+ }
8732
+ function boundedTokenSum(values, field, rejectedFields) {
8733
+ if (values.length === 0 || values.some((value) => value === null)) {
8734
+ return null;
8735
+ }
8736
+ let total = 0;
8737
+ for (const value of values) {
8738
+ if (value > MAX_MODEL_USAGE_TOKEN_COUNT - total) {
8739
+ rejectField(rejectedFields, field);
8740
+ return null;
9042
8741
  }
8742
+ total += value;
9043
8743
  }
9044
- return sawZero ? 0 : null;
8744
+ return total;
9045
8745
  }
9046
- function finiteNumberOrNull(value) {
9047
- return typeof value === "number" && Number.isFinite(value) ? value : null;
8746
+ function rejectField(rejectedFields, field) {
8747
+ if (rejectedFields.size < 32) {
8748
+ rejectedFields.add(field);
8749
+ }
9048
8750
  }
9049
8751
 
9050
8752
  // src/index.ts
@@ -9154,6 +8856,10 @@ function buildProviderClient(provider, settings) {
9154
8856
  // SDK retries on network/5xx/partial streams can replay provider work
9155
8857
  // or external tool side effects without a durable checkpoint.
9156
8858
  maxRetries: 0,
8859
+ // The Codex transport owns finer headers/idle/whole deadlines and
8860
+ // emits typed durable evidence. Keep the SDK's opaque envelope beyond
8861
+ // that whole-response budget so `Request timed out.` cannot win first.
8862
+ timeout: CODEX_RESPONSE_SDK_OUTER_TIMEOUT_MS,
9157
8863
  fetch: codexSubscriptionFetch(instrumentedModelFetch(provider.id, globalThis.fetch))
9158
8864
  })
9159
8865
  ) : (
@@ -9184,7 +8890,7 @@ function resolveTurnModel(settings, modelId) {
9184
8890
  return {
9185
8891
  provider: resolved.provider,
9186
8892
  client,
9187
- model: buildModelInstance(resolved.provider, client, resolved.model.id),
8893
+ model: buildModelInstance(resolved.provider, client, resolved.model.upstreamModelId),
9188
8894
  configured: resolved.model
9189
8895
  };
9190
8896
  }
@@ -9834,7 +9540,7 @@ function maybeInstallCodexToolSearch(agent, settings, options) {
9834
9540
  }
9835
9541
  }
9836
9542
  function mcpToolRequiresApproval(policy, unprefixedName) {
9837
- return policy === true || Array.isArray(policy) && policy.includes(unprefixedName);
9543
+ return policy === true || policy !== false && policy.has(unprefixedName);
9838
9544
  }
9839
9545
  function installMcpApprovalPolicy(agent, policies) {
9840
9546
  const listMcpTools = agent.getMcpTools.bind(agent);
@@ -9866,7 +9572,7 @@ function applyMcpApprovalPolicy(agent, settings) {
9866
9572
  (server) => server.requireApproval === true || Array.isArray(server.requireApproval) && server.requireApproval.length > 0
9867
9573
  ).map((server) => ({
9868
9574
  prefix: prefixedMcpToolName(server.id, ""),
9869
- requireApproval: server.requireApproval
9575
+ requireApproval: server.requireApproval === true ? true : new Set(server.requireApproval)
9870
9576
  })).sort((a, b) => b.prefix.length - a.prefix.length);
9871
9577
  if (policies.length === 0) {
9872
9578
  return;
@@ -9966,22 +9672,77 @@ function buildAgentCapabilities(settings, packSkills, options = {}) {
9966
9672
  function sandboxRunAs(_settings) {
9967
9673
  return void 0;
9968
9674
  }
9675
+ async function connectMcpServersInBatches(servers, options) {
9676
+ assertMcpServerSelectionWithinBounds(servers);
9677
+ const batches = [];
9678
+ try {
9679
+ for (let offset = 0; offset < servers.length; offset += MCP_MAX_CONCURRENT_SERVER_OPERATIONS) {
9680
+ batches.push(
9681
+ await connectMcpServers(
9682
+ servers.slice(offset, offset + MCP_MAX_CONCURRENT_SERVER_OPERATIONS),
9683
+ {
9684
+ ...options.connectTimeoutMs === void 0 ? {} : { connectTimeoutMs: options.connectTimeoutMs },
9685
+ connectInParallel: true,
9686
+ strict: options.strict
9687
+ }
9688
+ )
9689
+ );
9690
+ }
9691
+ } catch (error) {
9692
+ await closeMcpServerBatches(batches).catch(() => void 0);
9693
+ throw error;
9694
+ }
9695
+ const errors = /* @__PURE__ */ new Map();
9696
+ for (const batch of batches) {
9697
+ for (const [server, error] of batch.errors) errors.set(server, error);
9698
+ }
9699
+ return {
9700
+ active: batches.flatMap((batch) => batch.active),
9701
+ failed: batches.flatMap((batch) => batch.failed),
9702
+ errors,
9703
+ close: async () => {
9704
+ await closeMcpServerBatches(batches);
9705
+ }
9706
+ };
9707
+ }
9708
+ async function closeMcpServerBatches(batches) {
9709
+ let firstError;
9710
+ for (const batch of [...batches].reverse()) {
9711
+ try {
9712
+ await batch.close();
9713
+ } catch (error) {
9714
+ firstError ??= error;
9715
+ }
9716
+ }
9717
+ if (firstError !== void 0) throw firstError;
9718
+ }
9969
9719
  async function prepareAgentTools(settings, tools, options = {}) {
9970
9720
  const codexConnectorNamespaces = /* @__PURE__ */ new Set();
9721
+ assertMcpServerSelectionWithinBounds(tools);
9971
9722
  if (tools.length === 0) {
9972
9723
  return { mcpServers: [], close: async () => {
9973
9724
  }, codexConnectorNamespaces };
9974
9725
  }
9975
9726
  const registry = new Map(settings.mcpServers.map((server) => [server.id, server]));
9976
- const servers = await Promise.all(
9977
- tools.map(async (tool2) => {
9727
+ const aggregateToolBudget = new McpAggregateToolListBudget();
9728
+ const mcpFetchImpl = options.mcpFetchImpl ?? undiciFetch;
9729
+ const servers = await boundedParallelMap(
9730
+ tools,
9731
+ MCP_MAX_CONCURRENT_SERVER_OPERATIONS,
9732
+ async (tool2, index) => {
9978
9733
  const config = registry.get(tool2.id);
9979
9734
  if (!config) {
9980
9735
  throw new Error(`Unknown MCP server id: ${tool2.id}`);
9981
9736
  }
9982
9737
  const url = firstPartyMcpServerUrlForRun(settings, config, options.workspaceId) ?? config.url;
9983
- const baseFetch = isCodexAppsMcpServer(config) ? codexAppsSanitizingFetch(globalThis.fetch, codexConnectorNamespaces) : globalThis.fetch;
9984
- const fetchImpl = config.connectionRef ? connectionBrokerFetch(baseFetch, config, options) : isFirstPartyMcpServer(settings, config) ? firstPartyAuthFetch(baseFetch, settings, options) : baseFetch;
9738
+ const firstParty = isFirstPartyMcpServer(settings, config);
9739
+ const baseFetch = isCodexAppsMcpServer(config) ? codexAppsSanitizingFetch(mcpFetchImpl, codexConnectorNamespaces) : mcpFetchImpl;
9740
+ const guardedFetch = guardedMcpFetch(
9741
+ firstParty ? { ...settings, integrationsAllowPrivateNetworkTargets: true } : settings,
9742
+ baseFetch,
9743
+ firstParty ? { requireHttpsOutsideLocalTest: false } : {}
9744
+ );
9745
+ const fetchImpl = config.connectionRef ? connectionBrokerFetch(guardedFetch, config, options) : firstParty ? firstPartyAuthFetch(guardedFetch, settings, options) : guardedFetch;
9985
9746
  const optional = tool2.optional === true;
9986
9747
  const bestEffort = isCodexAppsMcpServer(config) || optional || !!config.connectionRef;
9987
9748
  const server = new PrefixedMcpServer(
@@ -9993,7 +9754,7 @@ async function prepareAgentTools(settings, tools, options = {}) {
9993
9754
  // MCP SDK's strict Tool schema rejects (fails the turn during tools/list);
9994
9755
  // sanitize the response on the wire before validation. The namespace Set
9995
9756
  // also captures each tool's original connector namespace (P4 Part B.1).
9996
- ...fetchImpl !== globalThis.fetch ? { fetch: fetchImpl } : {},
9757
+ fetch: fetchImpl,
9997
9758
  ...await mcpServerRequestInit(settings, config),
9998
9759
  ...config.timeoutMs ? {
9999
9760
  timeout: config.timeoutMs,
@@ -10002,30 +9763,46 @@ async function prepareAgentTools(settings, tools, options = {}) {
10002
9763
  }),
10003
9764
  config.id,
10004
9765
  config.allowedTools,
10005
- bestEffort
9766
+ bestEffort,
9767
+ aggregateToolBudget,
9768
+ `${config.id}:${index}`
10006
9769
  );
10007
9770
  return {
10008
9771
  server,
10009
9772
  bestEffort,
10010
- optional
9773
+ optional,
9774
+ timeoutMs: config.timeoutMs
10011
9775
  };
10012
- })
9776
+ }
10013
9777
  );
10014
- const requiredServers = servers.filter((entry) => !entry.bestEffort).map((entry) => entry.server);
10015
- const bestEffortServers = servers.filter((entry) => entry.bestEffort).map((entry) => entry.server);
9778
+ const requiredEntries = servers.filter((entry) => !entry.bestEffort);
9779
+ const bestEffortEntries = servers.filter((entry) => entry.bestEffort);
9780
+ const requiredServers = requiredEntries.map((entry) => entry.server);
9781
+ const bestEffortServers = bestEffortEntries.map((entry) => entry.server);
10016
9782
  const optionalServerNames = new Set(
10017
9783
  servers.filter((entry) => entry.optional).map((entry) => entry.server.name)
10018
9784
  );
10019
- const connectedRequired = await connectMcpServers(requiredServers, {
10020
- connectInParallel: true,
10021
- strict: true
9785
+ const connectedRequired = await connectMcpServersInBatches(requiredServers, {
9786
+ strict: true,
9787
+ connectTimeoutMs: mcpOuterConnectTimeoutMs(requiredEntries.map((entry) => entry.timeoutMs))
10022
9788
  });
10023
- const connectedBestEffort = bestEffortServers.length ? await connectMcpServers(bestEffortServers, {
10024
- connectInParallel: true,
10025
- strict: false
10026
- }) : null;
9789
+ let connectedBestEffort = null;
9790
+ try {
9791
+ connectedBestEffort = bestEffortServers.length ? await connectMcpServersInBatches(bestEffortServers, {
9792
+ strict: false,
9793
+ connectTimeoutMs: mcpOuterConnectTimeoutMs(
9794
+ bestEffortEntries.map((entry) => entry.timeoutMs)
9795
+ )
9796
+ }) : null;
9797
+ } catch (error) {
9798
+ await connectedRequired.close().catch(() => void 0);
9799
+ throw error;
9800
+ }
10027
9801
  if (connectedBestEffort) {
10028
9802
  for (const failed of connectedBestEffort.failed) {
9803
+ if (failed instanceof PrefixedMcpServer) {
9804
+ failed.releaseAggregateBudget();
9805
+ }
10029
9806
  if (!optionalServerNames.has(failed.name)) {
10030
9807
  continue;
10031
9808
  }
@@ -10039,10 +9816,20 @@ async function prepareAgentTools(settings, tools, options = {}) {
10039
9816
  return {
10040
9817
  mcpServers: [...connectedRequired.active, ...connectedBestEffort?.active ?? []],
10041
9818
  close: async () => {
10042
- await connectedRequired.close();
9819
+ let firstError;
10043
9820
  if (connectedBestEffort) {
10044
- await connectedBestEffort.close();
9821
+ try {
9822
+ await connectedBestEffort.close();
9823
+ } catch (error) {
9824
+ firstError ??= error;
9825
+ }
9826
+ }
9827
+ try {
9828
+ await connectedRequired.close();
9829
+ } catch (error) {
9830
+ firstError ??= error;
10045
9831
  }
9832
+ if (firstError !== void 0) throw firstError;
10046
9833
  },
10047
9834
  codexConnectorNamespaces
10048
9835
  };
@@ -10054,10 +9841,12 @@ function connectionBrokerFetch(baseFetch, config, options) {
10054
9841
  }
10055
9842
  return async (input, init) => {
10056
9843
  const request = await mcpRequestInfo(input, init);
9844
+ const destinationUrl = mcpRequestDestinationUrl(input);
10057
9845
  const first = await resolveConnectionForRequest(
10058
9846
  options,
10059
9847
  config.id,
10060
9848
  connectionRef,
9849
+ destinationUrl,
10061
9850
  request.toolName,
10062
9851
  false
10063
9852
  );
@@ -10069,10 +9858,12 @@ function connectionBrokerFetch(baseFetch, config, options) {
10069
9858
  withConnectionHeaders(input, init, first.headers)
10070
9859
  );
10071
9860
  if (response.status === 401) {
9861
+ await cancelMcpResponseBody(response);
10072
9862
  const refreshed = await resolveConnectionForRequest(
10073
9863
  options,
10074
9864
  config.id,
10075
9865
  connectionRef,
9866
+ destinationUrl,
10076
9867
  request.toolName,
10077
9868
  true
10078
9869
  );
@@ -10085,9 +9876,14 @@ function connectionBrokerFetch(baseFetch, config, options) {
10085
9876
  );
10086
9877
  if (retry.status === 403) {
10087
9878
  const auth = insufficientScopeAuth(retry.headers, connectionRef, refreshed.connectionId);
10088
- return auth ? await authNeededFetchResponse(options, config.id, request, auth, connectionRef) : retry;
9879
+ if (auth) {
9880
+ await cancelMcpResponseBody(retry);
9881
+ return await authNeededFetchResponse(options, config.id, request, auth, connectionRef);
9882
+ }
9883
+ return retry;
10089
9884
  }
10090
9885
  if (retry.status === 401) {
9886
+ await cancelMcpResponseBody(retry);
10091
9887
  return await authNeededFetchResponse(
10092
9888
  options,
10093
9889
  config.id,
@@ -10109,12 +9905,16 @@ function connectionBrokerFetch(baseFetch, config, options) {
10109
9905
  }
10110
9906
  if (response.status === 403) {
10111
9907
  const auth = insufficientScopeAuth(response.headers, connectionRef, first.connectionId);
10112
- return auth ? await authNeededFetchResponse(options, config.id, request, auth, connectionRef) : response;
9908
+ if (auth) {
9909
+ await cancelMcpResponseBody(response);
9910
+ return await authNeededFetchResponse(options, config.id, request, auth, connectionRef);
9911
+ }
9912
+ return response;
10113
9913
  }
10114
9914
  return response;
10115
9915
  };
10116
9916
  }
10117
- async function resolveConnectionForRequest(options, serverId, connectionRef, toolName, forceRefresh) {
9917
+ async function resolveConnectionForRequest(options, serverId, connectionRef, destinationUrl, toolName, forceRefresh) {
10118
9918
  if (!options.workspaceId || !options.resolveCredential) {
10119
9919
  return {
10120
9920
  status: "auth_needed",
@@ -10131,6 +9931,7 @@ async function resolveConnectionForRequest(options, serverId, connectionRef, too
10131
9931
  workspaceId: options.workspaceId,
10132
9932
  serverId,
10133
9933
  connectionRef,
9934
+ destinationUrl,
10134
9935
  forceRefresh,
10135
9936
  ...toolName ? { toolName } : {},
10136
9937
  ...options.subjectId ? { subjectId: options.subjectId } : {}
@@ -10194,6 +9995,9 @@ async function publishAuthNeeded(options, payload) {
10194
9995
  } catch {
10195
9996
  }
10196
9997
  }
9998
+ function mcpRequestDestinationUrl(input) {
9999
+ return new URL(input instanceof Request ? input.url : input.toString()).toString();
10000
+ }
10197
10001
  async function mcpRequestInfo(input, init) {
10198
10002
  const body = typeof init?.body === "string" ? init.body : input instanceof Request && (init?.method ?? input.method).toUpperCase() === "POST" ? await input.clone().text().catch(() => "") : "";
10199
10003
  if (!body) {
@@ -10326,7 +10130,7 @@ async function signFirstPartyDelegatedBearer(settings, options) {
10326
10130
  workspaceId: options.workspaceId,
10327
10131
  subjectId: options.subjectId ?? "worker:first-party-mcp",
10328
10132
  ...options.subjectLabel ? { subjectLabel: options.subjectLabel } : {},
10329
- permissions: options.firstPartyPermissions ?? firstPartyMcpPermissions,
10133
+ permissions: options.firstPartyPermissions ?? [...DEFAULT_FIRST_PARTY_MCP_PERMISSIONS],
10330
10134
  ...options.sessionId ? { sessionId: options.sessionId } : {},
10331
10135
  ...options.turnId ? { turnId: options.turnId } : {},
10332
10136
  ...options.attemptId ? { attemptId: options.attemptId } : {},
@@ -10375,21 +10179,6 @@ async function codexAppsMcpRequestInit(settings) {
10375
10179
  }
10376
10180
  return { requestInit: { headers } };
10377
10181
  }
10378
- var firstPartyMcpPermissions = [
10379
- "workspace:read",
10380
- "files:read",
10381
- "documents:search",
10382
- "scheduled_tasks:manage",
10383
- "scheduled_tasks:run",
10384
- "goals:manage",
10385
- "sessions:read",
10386
- "sessions:create",
10387
- "sessions:control",
10388
- "variable-sets:use",
10389
- "variable-sets:manage",
10390
- "rigs:use",
10391
- "github:use"
10392
- ];
10393
10182
  function isCodexAppsMcpServer(config) {
10394
10183
  return config.id === CODEX_APPS_MCP_SERVER_ID;
10395
10184
  }
@@ -10457,8 +10246,10 @@ function prefixedMcpToolName(registryId, toolName) {
10457
10246
  return sharedPrefixedMcpToolName(registryId, toolName);
10458
10247
  }
10459
10248
  var PrefixedMcpServer = class {
10460
- constructor(inner, registryId, allowedTools, bestEffort = false) {
10249
+ constructor(inner, registryId, allowedTools, bestEffort = false, aggregateToolBudget, aggregateSourceId = registryId) {
10461
10250
  this.inner = inner;
10251
+ this.aggregateToolBudget = aggregateToolBudget;
10252
+ this.aggregateSourceId = aggregateSourceId;
10462
10253
  this.name = registryId;
10463
10254
  this.prefix = prefixedMcpToolName(registryId, "");
10464
10255
  this.cacheToolsList = inner.cacheToolsList;
@@ -10480,12 +10271,22 @@ var PrefixedMcpServer = class {
10480
10271
  return this.inner.connect();
10481
10272
  }
10482
10273
  close() {
10274
+ this.releaseAggregateBudget();
10483
10275
  return this.inner.close();
10484
10276
  }
10277
+ releaseAggregateBudget() {
10278
+ this.aggregateToolBudget?.remove(this.aggregateSourceId);
10279
+ }
10485
10280
  async listTools() {
10486
- let tools;
10487
10281
  try {
10488
- tools = await this.inner.listTools();
10282
+ const tools = assertMcpToolListWithinBounds(await this.inner.listTools());
10283
+ const exposed = tools.filter((tool2) => this.isAllowed(tool2.name)).map((tool2) => ({
10284
+ ...tool2,
10285
+ name: prefixedMcpToolName(this.name, tool2.name)
10286
+ }));
10287
+ const bounded = this.aggregateToolBudget?.replace(this.aggregateSourceId, exposed) ?? assertMcpToolListWithinBounds(exposed);
10288
+ this.listedToolSchemaTokens = estimateSerializedValueTokens(bounded);
10289
+ return bounded;
10489
10290
  } catch (error) {
10490
10291
  if (!this.bestEffort) {
10491
10292
  throw error;
@@ -10499,14 +10300,9 @@ var PrefixedMcpServer = class {
10499
10300
  { serverId: this.name, ...safeMcpErrorFields(error) }
10500
10301
  );
10501
10302
  }
10303
+ this.releaseAggregateBudget();
10502
10304
  return [];
10503
10305
  }
10504
- const exposed = tools.filter((tool2) => this.isAllowed(tool2.name)).map((tool2) => ({
10505
- ...tool2,
10506
- name: prefixedMcpToolName(this.name, tool2.name)
10507
- }));
10508
- this.listedToolSchemaTokens = estimateSerializedValueTokens(exposed);
10509
- return exposed;
10510
10306
  }
10511
10307
  /** Latest exact tools/list projection used to build the model request. */
10512
10308
  modelToolSchemaTokens() {
@@ -10518,7 +10314,9 @@ var PrefixedMcpServer = class {
10518
10314
  throw new Error(`MCP tool ${unprefixed} is not allowed for server ${this.name}`);
10519
10315
  }
10520
10316
  try {
10521
- return await this.inner.callTool(unprefixed, args, meta);
10317
+ const output = await this.inner.callTool(unprefixed, args, meta);
10318
+ assertMcpPayloadWithinBytes(output, MCP_MAX_TOOL_RESULT_BYTES, "MCP tool result");
10319
+ return output;
10522
10320
  } catch (error) {
10523
10321
  if (isAuthNeededMcpError(error)) {
10524
10322
  return {
@@ -11590,17 +11388,18 @@ function usageFromResponse(response) {
11590
11388
  ),
11591
11389
  ...numberProp(record, "totalTokens", "totalTokens", "total_tokens"),
11592
11390
  ...inputTokenDetailsProp(record),
11593
- ...outputTokenDetailsProp(record)
11391
+ ...outputTokenDetailsProp(record),
11392
+ ...requestUsageEntriesProp(record)
11594
11393
  };
11595
11394
  return Object.keys(usage).length > 0 ? usage : null;
11596
11395
  }
11597
11396
  function numberProp(raw, outputKey, ...keys) {
11598
11397
  const value = keys.map((key) => raw[key]).find((candidate) => candidate !== void 0);
11599
- return typeof value === "number" && Number.isFinite(value) ? { [outputKey]: value } : {};
11398
+ return typeof value === "number" ? { [outputKey]: value } : {};
11600
11399
  }
11601
11400
  function inputTokenDetailsProp(raw) {
11602
11401
  const details = raw.inputTokensDetails ?? raw.input_tokens_details ?? raw.promptTokensDetails ?? raw.prompt_tokens_details;
11603
- if (!details || typeof details !== "object") {
11402
+ if (details === void 0 || details === null) {
11604
11403
  return {};
11605
11404
  }
11606
11405
  return {
@@ -11610,13 +11409,25 @@ function inputTokenDetailsProp(raw) {
11610
11409
  function outputTokenDetailsProp(raw) {
11611
11410
  const details = raw.outputTokensDetails ?? raw.output_tokens_details;
11612
11411
  const normalized = details ?? raw.completionTokensDetails ?? raw.completion_tokens_details;
11613
- if (!normalized || typeof normalized !== "object") {
11412
+ if (normalized === void 0 || normalized === null) {
11614
11413
  return {};
11615
11414
  }
11616
11415
  return {
11617
11416
  outputTokensDetails: normalized
11618
11417
  };
11619
11418
  }
11419
+ function requestUsageEntriesProp(raw) {
11420
+ const entries = raw.requestUsageEntries ?? raw.request_usage_entries;
11421
+ if (entries === void 0 || entries === null) {
11422
+ return {};
11423
+ }
11424
+ return {
11425
+ // The normalizer validates every entry and all supported field aliases.
11426
+ // Preserve the SDK objects rather than rebuilding them and accidentally
11427
+ // dropping provider detail fields such as cache_write_tokens.
11428
+ requestUsageEntries: entries
11429
+ };
11430
+ }
11620
11431
  function serializeApprovals(interruptions) {
11621
11432
  return interruptions.filter((item) => interruptionToolName(item) !== HUMAN_INPUT_TOOL_NAME).map((item) => {
11622
11433
  if (typeof item?.toJSON === "function") {
@@ -12085,13 +11896,95 @@ function runtimeGitBindingDescriptors(resources) {
12085
11896
  };
12086
11897
  });
12087
11898
  }
11899
+ function gitCredentialBindingKey(provider, credentialBindingId) {
11900
+ return `${provider}\0${credentialBindingId}`;
11901
+ }
11902
+ function brokeredGitCredentialBindingKeys(bindings) {
11903
+ return new Set(
11904
+ bindings.filter((binding) => binding.transport?.kind === "http_broker").map((binding) => gitCredentialBindingKey(binding.provider, binding.credentialBindingId))
11905
+ );
11906
+ }
11907
+ function runtimeGitHttpBrokerRouteDescriptors(resources, bindings) {
11908
+ const resourceDescriptors = runtimeGitBindingDescriptors(resources);
11909
+ const byBindingAndUri = /* @__PURE__ */ new Map();
11910
+ for (const descriptor of resourceDescriptors) {
11911
+ byBindingAndUri.set(
11912
+ `${gitCredentialBindingKey(descriptor.provider, descriptor.credentialBindingId)}\0${descriptor.uri}`,
11913
+ descriptor
11914
+ );
11915
+ }
11916
+ const routes = [];
11917
+ const claimedRepositoryUris = /* @__PURE__ */ new Set();
11918
+ const claimedBrokerUris = /* @__PURE__ */ new Set();
11919
+ for (const binding of bindings) {
11920
+ if (!binding.transport) continue;
11921
+ if (typeof binding.transport !== "object" || binding.transport.kind !== "http_broker" || !Array.isArray(binding.transport.repositories)) {
11922
+ throw new Error(
11923
+ `Git credential binding ${binding.credentialBindingId} uses an unsupported transport`
11924
+ );
11925
+ }
11926
+ const bindingKey = gitCredentialBindingKey(binding.provider, binding.credentialBindingId);
11927
+ const expected = resourceDescriptors.filter(
11928
+ (descriptor) => gitCredentialBindingKey(descriptor.provider, descriptor.credentialBindingId) === bindingKey
11929
+ );
11930
+ if (expected.length === 0 || binding.transport.repositories.length !== expected.length) {
11931
+ throw new Error(
11932
+ `Git HTTP broker binding ${binding.credentialBindingId} does not cover its exact repository set`
11933
+ );
11934
+ }
11935
+ for (const route of binding.transport.repositories) {
11936
+ if (!route || typeof route !== "object" || typeof route.repositoryUri !== "string" || typeof route.brokerUri !== "string") {
11937
+ throw new Error(
11938
+ `Git HTTP broker binding ${binding.credentialBindingId} contains an invalid repository route`
11939
+ );
11940
+ }
11941
+ const descriptor = byBindingAndUri.get(`${bindingKey}\0${route.repositoryUri}`);
11942
+ if (!descriptor || claimedRepositoryUris.has(route.repositoryUri)) {
11943
+ throw new Error(
11944
+ `Git HTTP broker binding ${binding.credentialBindingId} contains an unexpected repository route`
11945
+ );
11946
+ }
11947
+ let brokerUrl;
11948
+ try {
11949
+ brokerUrl = new URL(route.brokerUri);
11950
+ } catch {
11951
+ throw new Error(
11952
+ `Git HTTP broker binding ${binding.credentialBindingId} contains an invalid broker URI`
11953
+ );
11954
+ }
11955
+ if (brokerUrl.protocol !== "https:" || brokerUrl.username || brokerUrl.password || brokerUrl.search || brokerUrl.hash || brokerUrl.href !== route.brokerUri || claimedBrokerUris.has(brokerUrl.href)) {
11956
+ throw new Error(
11957
+ `Git HTTP broker binding ${binding.credentialBindingId} contains an unsafe broker URI`
11958
+ );
11959
+ }
11960
+ claimedRepositoryUris.add(route.repositoryUri);
11961
+ claimedBrokerUris.add(brokerUrl.href);
11962
+ routes.push({
11963
+ provider: binding.provider,
11964
+ credentialBindingId: binding.credentialBindingId,
11965
+ bindingHash: gitCredentialBindingHash(binding.credentialBindingId),
11966
+ repositoryUri: route.repositoryUri,
11967
+ brokerUri: route.brokerUri,
11968
+ protocol: "https",
11969
+ host: brokerUrl.host.toLowerCase(),
11970
+ path: brokerUrl.pathname.replace(/^\/+|\/+$/g, "")
11971
+ });
11972
+ }
11973
+ }
11974
+ return routes;
11975
+ }
12088
11976
  function gitUsernameForProvider(provider) {
12089
11977
  if (provider === "github") return "x-access-token";
12090
11978
  if (provider === "gitlab") return "oauth2";
12091
11979
  return "opengeni";
12092
11980
  }
12093
- function gitCredentialHelperBindingCaseLines(resources) {
12094
- return runtimeGitBindingDescriptors(resources).flatMap((descriptor) => {
11981
+ function gitCredentialHelperBindingCaseLines(resources, bindings) {
11982
+ const brokeredBindings = brokeredGitCredentialBindingKeys(bindings);
11983
+ return runtimeGitBindingDescriptors(resources).filter(
11984
+ (descriptor) => !brokeredBindings.has(
11985
+ gitCredentialBindingKey(descriptor.provider, descriptor.credentialBindingId)
11986
+ )
11987
+ ).flatMap((descriptor) => {
12095
11988
  const paths = /* @__PURE__ */ new Set([
12096
11989
  descriptor.path,
12097
11990
  descriptor.path.replace(/\.git$/, ""),
@@ -12102,7 +11995,19 @@ function gitCredentialHelperBindingCaseLines(resources) {
12102
11995
  );
12103
11996
  });
12104
11997
  }
12105
- function gitAskpassHostProviderCaseLines(resources) {
11998
+ function gitCredentialHelperBrokerCaseLines(routes) {
11999
+ return routes.flatMap((route) => {
12000
+ const paths = /* @__PURE__ */ new Set([
12001
+ route.path,
12002
+ route.path.replace(/\.git$/, ""),
12003
+ `${route.path.replace(/\.git$/, "")}.git`
12004
+ ]);
12005
+ return [...paths].map(
12006
+ (path) => ` ${shellQuote(`${route.protocol}|${route.host}|${path}`)}) username=opengeni; token_file="$credential_dir/${route.bindingHash}-token" ;;`
12007
+ );
12008
+ });
12009
+ }
12010
+ function gitAskpassHostProviderCaseLines(resources, brokerRoutes) {
12106
12011
  const hosts = /* @__PURE__ */ new Map();
12107
12012
  for (const descriptor of runtimeGitBindingDescriptors(resources)) {
12108
12013
  const entry = hosts.get(descriptor.host) ?? {
@@ -12112,9 +12017,13 @@ function gitAskpassHostProviderCaseLines(resources) {
12112
12017
  entry.bindings.add(`${descriptor.provider}\0${descriptor.credentialBindingId}`);
12113
12018
  hosts.set(descriptor.host, entry);
12114
12019
  }
12115
- return [...hosts.entries()].filter(([, entry]) => entry.bindings.size === 1).sort(([a], [b]) => a.localeCompare(b)).map(
12116
- ([hostname, entry]) => ` ${shellQuote(hostname)}) printf '%s\\n' ${entry.provider}; return 0 ;;`
12117
- );
12020
+ const brokerHosts = [...new Set(brokerRoutes.map((route) => route.host))].sort((a, b) => a.localeCompare(b)).map((hostname) => ` ${shellQuote(hostname)}) printf '\\n'; return 0 ;;`);
12021
+ return [
12022
+ ...brokerHosts,
12023
+ ...[...hosts.entries()].filter(([, entry]) => entry.bindings.size === 1).sort(([a], [b]) => a.localeCompare(b)).map(
12024
+ ([hostname, entry]) => ` ${shellQuote(hostname)}) printf '%s\\n' ${entry.provider}; return 0 ;;`
12025
+ )
12026
+ ];
12118
12027
  }
12119
12028
  function gitCredentialTokenWriterCommandLines(bindings = []) {
12120
12029
  const bindingWrites = bindings.flatMap((binding) => {
@@ -12123,7 +12032,7 @@ function gitCredentialTokenWriterCommandLines(bindings = []) {
12123
12032
  const count = Math.max(1, binding.providerBindingCount ?? 1);
12124
12033
  const provider = binding.provider;
12125
12034
  const lines = [`write_git_binding_token ${shellQuote(hash)} "\${${seedEnv}:-}"`];
12126
- if (count === 1) {
12035
+ if (count === 1 && binding.transport?.kind !== "http_broker") {
12127
12036
  lines.push(`write_git_provider_token ${shellQuote(provider)} "\${${seedEnv}:-}"`);
12128
12037
  } else {
12129
12038
  lines.push(`remove_git_provider_token ${shellQuote(provider)}`);
@@ -12184,11 +12093,36 @@ function gitCredentialTokenWriterCommandLines(bindings = []) {
12184
12093
  'umask "$seed_umask"'
12185
12094
  ];
12186
12095
  }
12096
+ function gitHttpBrokerConfigCommandLines(routes) {
12097
+ return [
12098
+ 'git_http_broker_config="${OPENGENI_GIT_CREDENTIALS_DIR:-$HOME/.opengeni/git-credentials}/http-broker.gitconfig"',
12099
+ 'mkdir -p "$(dirname "$git_http_broker_config")"',
12100
+ 'broker_umask="$(umask)"',
12101
+ "umask 077",
12102
+ ': > "$git_http_broker_config.tmp.$$"',
12103
+ ...routes.map(
12104
+ (route) => `git config --file "$git_http_broker_config.tmp.$$" --add ${shellQuote(`url.${route.brokerUri}.insteadOf`)} ${shellQuote(route.repositoryUri)}`
12105
+ ),
12106
+ 'mv -f "$git_http_broker_config.tmp.$$" "$git_http_broker_config"',
12107
+ 'umask "$broker_umask"',
12108
+ 'git config --global --unset-all include.path "$git_http_broker_config" >/dev/null 2>&1 || true',
12109
+ 'git config --global --add include.path "$git_http_broker_config"'
12110
+ ];
12111
+ }
12187
12112
  function gitCredentialHelperCommandLines(resources = [], bindings = []) {
12188
- const hostProviderCases = gitAskpassHostProviderCaseLines(resources);
12189
- const bindingCases = gitCredentialHelperBindingCaseLines(resources);
12113
+ const brokerRoutes = runtimeGitHttpBrokerRouteDescriptors(resources, bindings);
12114
+ const hostProviderCases = gitAskpassHostProviderCaseLines(resources, brokerRoutes);
12115
+ const bindingCases = [
12116
+ ...gitCredentialHelperBindingCaseLines(resources, bindings),
12117
+ ...gitCredentialHelperBrokerCaseLines(brokerRoutes)
12118
+ ];
12190
12119
  const descriptors = runtimeGitBindingDescriptors(resources);
12191
- const wrapperDescriptors = bindings.length > 0 ? descriptors : [];
12120
+ const brokeredBindings = brokeredGitCredentialBindingKeys(bindings);
12121
+ const wrapperDescriptors = bindings.length > 0 ? descriptors.filter(
12122
+ (descriptor) => !brokeredBindings.has(
12123
+ gitCredentialBindingKey(descriptor.provider, descriptor.credentialBindingId)
12124
+ )
12125
+ ) : [];
12192
12126
  const bindingProviders = /* @__PURE__ */ new Map();
12193
12127
  for (const descriptor of runtimeGitBindingDescriptors(resources)) {
12194
12128
  const ids = bindingProviders.get(descriptor.provider) ?? /* @__PURE__ */ new Set();
@@ -12213,6 +12147,23 @@ function gitCredentialHelperCommandLines(resources = [], bindings = []) {
12213
12147
  ] : [];
12214
12148
  });
12215
12149
  const multiWrapperProviders = bindings.length > 0 ? [...bindingProviders.entries()].filter(([, ids]) => ids.size > 1).map(([provider]) => provider) : [];
12150
+ const brokeredOriginCases = brokerRoutes.flatMap((route) => {
12151
+ const base = route.repositoryUri.replace(/\.git$/, "");
12152
+ return [.../* @__PURE__ */ new Set([route.repositoryUri, base, `${base}.git`])].map(
12153
+ (uri) => ` ${shellQuote(`${route.provider}|${uri}`)}) return 0 ;;`
12154
+ );
12155
+ });
12156
+ const brokeredBindingHashCases = bindings.filter((binding) => binding.transport?.kind === "http_broker").map(
12157
+ (binding) => ` ${shellQuote(`${binding.provider}|${gitCredentialBindingHash(binding.credentialBindingId)}`)}) return 0 ;;`
12158
+ );
12159
+ const providerBindingKinds = /* @__PURE__ */ new Map();
12160
+ for (const binding of bindings) {
12161
+ const counts = providerBindingKinds.get(binding.provider) ?? { direct: 0, brokered: 0 };
12162
+ if (binding.transport?.kind === "http_broker") counts.brokered += 1;
12163
+ else counts.direct += 1;
12164
+ providerBindingKinds.set(binding.provider, counts);
12165
+ }
12166
+ const brokerOnlyProviders = [...providerBindingKinds.entries()].filter(([, counts]) => counts.brokered > 0 && counts.direct === 0).map(([provider]) => provider);
12216
12167
  return [
12217
12168
  ...gitCredentialTokenWriterCommandLines(bindings),
12218
12169
  // Provision git/provider-CLI helpers at SETUP (runtime) before any clone
@@ -12304,6 +12255,7 @@ function gitCredentialHelperCommandLines(resources = [], bindings = []) {
12304
12255
  "git config --global --add credential.helper ''",
12305
12256
  'git config --global --add credential.helper "$git_credential_helper"',
12306
12257
  "git config --global credential.useHttpPath true",
12258
+ ...gitHttpBrokerConfigCommandLines(brokerRoutes),
12307
12259
  'wrapper_dir="${OPENGENI_GIT_CLI_WRAPPER_DIR:-$HOME/.opengeni/bin}"',
12308
12260
  'mkdir -p "$wrapper_dir"',
12309
12261
  "for opengeni_git_cli_tool in gh glab az; do",
@@ -12331,12 +12283,24 @@ function gitCredentialHelperCommandLines(resources = [], bindings = []) {
12331
12283
  " *) return 1 ;;",
12332
12284
  " esac",
12333
12285
  "}",
12286
+ "binding_hash_is_brokered() {",
12287
+ ' case "$provider|$1" in',
12288
+ ...brokeredBindingHashCases,
12289
+ " *) return 1 ;;",
12290
+ " esac",
12291
+ "}",
12334
12292
  "binding_hash_for_origin() {",
12335
12293
  ' case "$provider|$1" in',
12336
12294
  ...originWrapperHashes,
12337
12295
  " *) return 1 ;;",
12338
12296
  " esac",
12339
12297
  "}",
12298
+ "origin_is_brokered() {",
12299
+ ' case "$provider|$1" in',
12300
+ ...brokeredOriginCases,
12301
+ " *) return 1 ;;",
12302
+ " esac",
12303
+ "}",
12340
12304
  "sole_binding_hash() {",
12341
12305
  ' case "$provider" in',
12342
12306
  ...soleWrapperHashes,
@@ -12344,19 +12308,25 @@ function gitCredentialHelperCommandLines(resources = [], bindings = []) {
12344
12308
  " esac",
12345
12309
  "}",
12346
12310
  `multi_binding_providers=${shellQuote(multiWrapperProviders.join(" "))}`,
12311
+ `broker_only_providers=${shellQuote(brokerOnlyProviders.join(" "))}`,
12347
12312
  'if [ -n "$provider" ]; then',
12348
12313
  " binding_hash=",
12349
12314
  ' if [ -n "${OPENGENI_GIT_BINDING:-}" ]; then',
12350
12315
  ' binding_hash="$(hash_binding_id "$OPENGENI_GIT_BINDING")"',
12316
+ ` binding_hash_is_brokered "$binding_hash" && { printf '%s\\n' "$tool provider API authentication is host-brokered for OPENGENI_GIT_BINDING; use the configured provider MCP tools" >&2; exit 2; }`,
12351
12317
  ` binding_hash_allowed "$binding_hash" || { printf '%s\\n' "OPENGENI_GIT_BINDING does not select a $provider credential attached to this session" >&2; exit 2; }`,
12352
12318
  " elif command -v git >/dev/null 2>&1; then",
12353
12319
  ' origin="$(git config --get remote.origin.url 2>/dev/null || true)"',
12320
+ ` [ -z "$origin" ] || ! origin_is_brokered "$origin" || { printf '%s\\n' "$tool provider API authentication is host-brokered for this repository; use the configured provider MCP tools" >&2; exit 2; }`,
12354
12321
  ' [ -z "$origin" ] || binding_hash="$(binding_hash_for_origin "$origin" 2>/dev/null || true)"',
12355
12322
  " fi",
12356
12323
  ' [ -n "$binding_hash" ] || binding_hash="$(sole_binding_hash 2>/dev/null || true)"',
12357
12324
  ' if [ -n "$binding_hash" ]; then',
12358
12325
  ' token_file="${OPENGENI_GIT_CREDENTIALS_DIR:-$HOME/.opengeni/git-credentials}/$binding_hash-token"',
12359
12326
  " else",
12327
+ ' case " $broker_only_providers " in',
12328
+ ` *" $provider "*) printf '%s\\n' "$tool provider API authentication is host-brokered for this session; use the configured provider MCP tools" >&2; exit 2 ;;`,
12329
+ " esac",
12360
12330
  ' case " $multi_binding_providers " in',
12361
12331
  ` *" $provider "*) printf '%s\\n' "Unable to select one of multiple $provider credentials; run inside an attached repository or set OPENGENI_GIT_BINDING" >&2; exit 2 ;;`,
12362
12332
  " esac",
@@ -13194,6 +13164,7 @@ export {
13194
13164
  GENESIS_TITLE_DIRECTIVE,
13195
13165
  HUMAN_INPUT_TOOL_NAME,
13196
13166
  MAX_COMPACTION_THRESHOLD_RATIO,
13167
+ MAX_MODEL_USAGE_TOKEN_COUNT,
13197
13168
  MIN_COMPACTION_THRESHOLD_RATIO,
13198
13169
  MaxTurnsExceededError,
13199
13170
  MockAgentResponder,
@@ -13212,6 +13183,9 @@ export {
13212
13183
  REPOSITORY_DISCOVERY_LIMIT,
13213
13184
  RecordingError,
13214
13185
  RecordingUnavailableError,
13186
+ RoutingBackendRecoveryRequiredError,
13187
+ RoutingMutationOutcomeUnknownError,
13188
+ RoutingRetainedProcessNotFoundError,
13215
13189
  RoutingSandboxSession,
13216
13190
  RoutingUnsupportedError,
13217
13191
  RunCredentialValidationError,
@@ -13234,7 +13208,9 @@ export {
13234
13208
  SandboxChannelAService,
13235
13209
  SandboxComputer,
13236
13210
  SandboxConfigError,
13211
+ SandboxExecReadinessError,
13237
13212
  SandboxProviderUnavailableError,
13213
+ SandboxReplacementProviderStateError,
13238
13214
  SandboxResumeStateUnavailableError,
13239
13215
  SelfhostedControlError,
13240
13216
  SelfhostedSandboxClient,
@@ -13247,7 +13223,10 @@ export {
13247
13223
  TerminalServerError,
13248
13224
  TerminalServerUnsupportedError,
13249
13225
  ToolspaceTokenPathError,
13226
+ TurnSandboxCommandCancelledError,
13250
13227
  USER_MESSAGE_TRUNCATION_MARKER,
13228
+ WORKSPACE_ARCHIVE_DESCRIPTOR_VERSION,
13229
+ WorkspaceArchiveIntegrityError,
13251
13230
  WorkspaceModelPolicyBlockedError,
13252
13231
  agentErrorToControlError,
13253
13232
  agentsErrorRunState,
@@ -13279,7 +13258,9 @@ export {
13279
13258
  buildSummaryItem,
13280
13259
  buildTerminalServerScript,
13281
13260
  callModelInputFilterForSettings,
13261
+ captureVerifiedWorkspaceArchive,
13282
13262
  clampCompactionThresholdRatio,
13263
+ classifyProviderSandboxFailure,
13283
13264
  clearRunCredentials,
13284
13265
  clearRunCredentialsForAttempt,
13285
13266
  collectSandboxEnvironment,
@@ -13291,12 +13272,14 @@ export {
13291
13272
  computerUse,
13292
13273
  configureOpenAI,
13293
13274
  configureRuntimeMetricsHooks,
13275
+ connectMcpServersInBatches,
13294
13276
  contentTypeForCodec,
13295
13277
  contextRobustnessFilterForSettings,
13296
13278
  coreInstructions,
13297
13279
  createProductionAgentRuntime,
13298
13280
  createSandboxClient,
13299
13281
  createSandboxClientForBackend,
13282
+ createTurnToolCancellationController,
13300
13283
  decideCompaction,
13301
13284
  decideSelfhostedRetry,
13302
13285
  decodeModalSnapshotId,
@@ -13324,16 +13307,20 @@ export {
13324
13307
  extForCodec,
13325
13308
  extractResponseOutputText,
13326
13309
  findCompactionNeededError,
13310
+ fingerprintSandboxWorkspace,
13327
13311
  getSkillLibraryEntry,
13328
13312
  gitCredentialBindingHash,
13329
13313
  gitCredentialBindingTokenRefreshCommand,
13330
13314
  gitProviderTokenRefreshCommand,
13331
13315
  hasModelGeneratedItem,
13316
+ hasPersistableSandboxProviderIdentity,
13332
13317
  inspectRecordingArtifact,
13333
13318
  isCompactionSummary,
13334
13319
  isEphemeralInternalContext,
13335
13320
  isExecSessionLostBanner,
13321
+ isProviderSandboxGoneDuringRoutedOperation,
13336
13322
  isProviderSandboxNotFoundError,
13323
+ isProviderSandboxTransientError,
13337
13324
  isSelfhostedProviderNotFoundError,
13338
13325
  isSkillLibraryEntryId,
13339
13326
  isUserMessage,
@@ -13353,11 +13340,13 @@ export {
13353
13340
  modelCallUsageTelemetry,
13354
13341
  modelResponseUsageFromResponse,
13355
13342
  modelResponseUsageFromSdkEvent,
13343
+ modelUsageTokenCountOrNull,
13356
13344
  negotiateCapabilities,
13357
13345
  negotiateSelfhostedCapabilities,
13358
13346
  neutralizeToolSearchItemsInSerializedRunState,
13359
13347
  nextDurableOpId,
13360
13348
  normalizeComputerCallsFilter,
13349
+ normalizeModelCallUsage,
13361
13350
  normalizeRunCredentialsResolution,
13362
13351
  normalizeSdkEvent,
13363
13352
  normalizeToolOutputForEvent,
@@ -13372,12 +13361,14 @@ export {
13372
13361
  parseNumstatZ,
13373
13362
  parsePorcelainV2,
13374
13363
  parseUnifiedPatch,
13364
+ parseWorkspaceArchiveDescriptor,
13375
13365
  payloadTooLargeMessage,
13376
13366
  pinProvidedSessionManifestEnvironment,
13377
13367
  prefixedMcpToolName,
13378
13368
  prepareAgentTools,
13379
13369
  prepareCompactionPromptInput,
13380
13370
  prepareRunInput,
13371
+ readVerifiedWorkspaceArchive,
13381
13372
  readWorkspaceArchiveFromEnvelopeSessionState,
13382
13373
  recordingStorageKey,
13383
13374
  refreshGitCredentialBindingTokenFiles,
@@ -13387,6 +13378,7 @@ export {
13387
13378
  renderSelfhostedFault,
13388
13379
  repositoryCloneCommand,
13389
13380
  repositoryUsesSandboxClone,
13381
+ requirePersistableReplacementSandboxEnvelope,
13390
13382
  resolveTurnModel,
13391
13383
  restoredSandboxSessionStateFromEntry,
13392
13384
  rigInstructions,
@@ -13418,6 +13410,7 @@ export {
13418
13410
  serializeApprovals,
13419
13411
  serializeEstablishedSandboxEnvelope,
13420
13412
  serializeHumanInputRequests,
13413
+ serializeReplacementSandboxEnvelope,
13421
13414
  setSelfhostedApplyDiff,
13422
13415
  startRecording,
13423
13416
  stopRecording,
@@ -13439,6 +13432,8 @@ export {
13439
13432
  toolspaceTokenFileFromEnvironment,
13440
13433
  toolspaceTokenSeedCommand,
13441
13434
  uploadRecordingArtifact,
13435
+ verifyRestoredWorkspace,
13436
+ verifySandboxExecReadiness,
13442
13437
  verifyStreamToken,
13443
13438
  withManifestRefreshOnResume,
13444
13439
  withRunCredentialEnvironment,