@opengeni/runtime 0.11.0 → 0.13.0

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
@@ -153,13 +153,28 @@ import {
153
153
  withToolspaceTokenClient,
154
154
  withToolspaceTokenEnvironment,
155
155
  withToolspaceTokenSession
156
- } from "./chunk-YUAFQEFY.js";
156
+ } from "./chunk-65UDUFUO.js";
157
157
  import {
158
158
  getSkillLibraryEntry,
159
159
  isSkillLibraryEntryId,
160
160
  listSkillLibraryEntries,
161
161
  loadSkillLibrarySkill
162
162
  } from "./chunk-DPULLAJG.js";
163
+ import {
164
+ MCP_MAX_CONCURRENT_SERVER_OPERATIONS,
165
+ MCP_MAX_TOOL_DEFINITION_BYTES,
166
+ MCP_MAX_TOOL_RESULT_BYTES,
167
+ MCP_MAX_TOOL_SEARCH_DISCLOSURE_BYTES,
168
+ McpAggregateToolListBudget,
169
+ assertMcpPayloadWithinBytes,
170
+ assertMcpServerSelectionWithinBounds,
171
+ assertMcpToolListWithinBounds,
172
+ boundedParallelMap,
173
+ cancelMcpResponseBody,
174
+ guardedMcpFetch,
175
+ mcpSerializedSizeBytes,
176
+ undiciFetch
177
+ } from "./chunk-HAN5D27J.js";
163
178
  import {
164
179
  MemorySession,
165
180
  connectMcpServers
@@ -225,6 +240,7 @@ import {
225
240
  import {
226
241
  approvalIdentifier,
227
242
  CAPABILITY_DESCRIPTORS as CAPABILITY_DESCRIPTORS2,
243
+ DEFAULT_FIRST_PARTY_MCP_PERMISSIONS,
228
244
  assertUniqueResourceMountPaths,
229
245
  gitCredentialBindingIdForRepository,
230
246
  gitCredentialProviderForRepository,
@@ -6372,6 +6388,7 @@ import {
6372
6388
  CODEX_APPS_MCP_SERVER_ID,
6373
6389
  CODEX_MODEL_ID_PREFIX,
6374
6390
  CODEX_ORIGINATOR,
6391
+ CODEX_RESPONSE_SDK_OUTER_TIMEOUT_MS,
6375
6392
  boundModelToolOutputItems as boundModelToolOutputItems2,
6376
6393
  codexAppsSanitizingFetch,
6377
6394
  codexRequestStorage,
@@ -6972,7 +6989,21 @@ function codexToolSearchExecutor(args) {
6972
6989
  const deferred = (args.availableTools ?? []).filter(isCodexAppsFunctionTool);
6973
6990
  if (deferred.length === 0) return [];
6974
6991
  const { query, limit } = parseSearchArgs(args.toolCall?.arguments);
6975
- return bm25RankTools(deferred, query, limit);
6992
+ const ranked = bm25RankTools(deferred, query, limit);
6993
+ const bounded = [];
6994
+ let disclosedBytes = 0;
6995
+ for (const tool2 of ranked) {
6996
+ const toolBytes = mcpSerializedSizeBytes(tool2);
6997
+ if (toolBytes > MCP_MAX_TOOL_DEFINITION_BYTES) {
6998
+ continue;
6999
+ }
7000
+ if (disclosedBytes + toolBytes > MCP_MAX_TOOL_SEARCH_DISCLOSURE_BYTES) {
7001
+ break;
7002
+ }
7003
+ disclosedBytes += toolBytes;
7004
+ bounded.push(tool2);
7005
+ }
7006
+ return bounded;
6976
7007
  }
6977
7008
  var NO_NAMESPACES = /* @__PURE__ */ new Set();
6978
7009
  function hasConnectorSources(connectorNamespaces) {
@@ -8963,7 +8994,9 @@ var ComputerUseCapability = class extends Capability {
8963
8994
  // any computer-use-only recording. One SandboxComputer instance caches
8964
8995
  // this for the rest of the turn.
8965
8996
  prepare: async (preparedSession) => {
8966
- await ensureDisplayStack(preparedSession);
8997
+ await ensureDisplayStack(preparedSession, {
8998
+ telemetryContext: { callerKind: "computer" }
8999
+ });
8967
9000
  await this.args.onReady?.(preparedSession);
8968
9001
  }
8969
9002
  });
@@ -9008,43 +9041,351 @@ var ComputerUseCapability = class extends Capability {
9008
9041
  };
9009
9042
 
9010
9043
  // src/usage-telemetry.ts
9011
- function modelCallUsageTelemetry(usage) {
9044
+ var MAX_MODEL_USAGE_TOKEN_COUNT = 1e9;
9045
+ function modelUsageTokenCountOrNull(value) {
9046
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 && value <= MAX_MODEL_USAGE_TOKEN_COUNT ? value : null;
9047
+ }
9048
+ function normalizeModelCallUsage(usage) {
9049
+ const rejectedFields = /* @__PURE__ */ new Set();
9050
+ if (!usage) {
9051
+ return {
9052
+ telemetry: {
9053
+ inputTokens: null,
9054
+ outputTokens: null,
9055
+ cachedTokens: null,
9056
+ cacheWriteTokens: null,
9057
+ reasoningTokens: null
9058
+ },
9059
+ totalTokens: null,
9060
+ rejectedFields: []
9061
+ };
9062
+ }
9063
+ const requestEntries = requestUsageEntries(usage.requestUsageEntries, rejectedFields);
9064
+ const topLevelInputTokens = reportedTokenCount(usage.inputTokens, "inputTokens", rejectedFields);
9065
+ const topLevelOutputTokens = reportedTokenCount(
9066
+ usage.outputTokens,
9067
+ "outputTokens",
9068
+ rejectedFields
9069
+ );
9070
+ const topLevelTotalTokens = reportedTokenCount(usage.totalTokens, "totalTokens", rejectedFields);
9071
+ const requestInputTokens = completeRequestTokenAggregate(
9072
+ requestEntries,
9073
+ ["inputTokens", "input_tokens"],
9074
+ "inputTokens.requestUsageEntries",
9075
+ rejectedFields
9076
+ );
9077
+ const requestOutputTokens = completeRequestTokenAggregate(
9078
+ requestEntries,
9079
+ ["outputTokens", "output_tokens"],
9080
+ "outputTokens.requestUsageEntries",
9081
+ rejectedFields
9082
+ );
9083
+ let requestTotalTokens = completeRequestTokenAggregate(
9084
+ requestEntries,
9085
+ ["totalTokens", "total_tokens"],
9086
+ "totalTokens.requestUsageEntries",
9087
+ rejectedFields
9088
+ );
9089
+ if (rejectInconsistentRequestTotals(requestEntries, rejectedFields)) {
9090
+ requestTotalTokens = { ...requestTotalTokens, status: "invalid", value: null };
9091
+ }
9092
+ const inputTokens = canonicalTokenDimension({
9093
+ topLevel: topLevelInputTokens,
9094
+ topLevelReported: isReported(usage.inputTokens),
9095
+ topLevelPath: "inputTokens",
9096
+ requestAggregate: requestInputTokens,
9097
+ rejectedFields
9098
+ });
9099
+ const outputTokens = canonicalTokenDimension({
9100
+ topLevel: topLevelOutputTokens,
9101
+ topLevelReported: isReported(usage.outputTokens),
9102
+ topLevelPath: "outputTokens",
9103
+ requestAggregate: requestOutputTokens,
9104
+ rejectedFields
9105
+ });
9106
+ const totalTokens = canonicalTotalTokens({
9107
+ inputTokens,
9108
+ outputTokens,
9109
+ topLevel: topLevelTotalTokens,
9110
+ topLevelReported: isReported(usage.totalTokens),
9111
+ requestAggregate: requestTotalTokens,
9112
+ rejectedFields
9113
+ });
9114
+ const inputDetailSource = preferredDetailSource(
9115
+ usage.inputTokensDetails,
9116
+ "inputTokensDetails",
9117
+ requestEntries,
9118
+ ["inputTokensDetails", "input_tokens_details"]
9119
+ );
9120
+ const outputDetailSource = preferredDetailSource(
9121
+ usage.outputTokensDetails,
9122
+ "outputTokensDetails",
9123
+ requestEntries,
9124
+ ["outputTokensDetails", "output_tokens_details"]
9125
+ );
9012
9126
  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
9127
+ telemetry: {
9128
+ inputTokens,
9129
+ outputTokens,
9130
+ cachedTokens: aggregateDetailTokenCount(
9131
+ inputDetailSource,
9132
+ ["cached_tokens", "cachedInputTokens", "cached_input_tokens"],
9133
+ "cachedTokens",
9134
+ rejectedFields
9135
+ ),
9136
+ cacheWriteTokens: aggregateDetailTokenCount(
9137
+ inputDetailSource,
9138
+ ["cache_write_tokens", "cacheWriteTokens"],
9139
+ "cacheWriteTokens",
9140
+ rejectedFields
9141
+ ),
9142
+ reasoningTokens: aggregateDetailTokenCount(
9143
+ outputDetailSource,
9144
+ ["reasoning_tokens", "reasoningTokens", "reasoning_output_tokens"],
9145
+ "reasoningTokens",
9146
+ rejectedFields
9147
+ )
9148
+ },
9149
+ totalTokens,
9150
+ rejectedFields: [...rejectedFields].slice(0, 32)
9025
9151
  };
9026
9152
  }
9027
- function firstPositiveDetailNumber(details, keys) {
9028
- if (!details) {
9153
+ function modelCallUsageTelemetry(usage) {
9154
+ return normalizeModelCallUsage(usage).telemetry;
9155
+ }
9156
+ function requestUsageEntries(value, rejectedFields) {
9157
+ if (value === void 0) {
9158
+ return [];
9159
+ }
9160
+ if (!Array.isArray(value)) {
9161
+ rejectField(rejectedFields, "requestUsageEntries");
9162
+ return [];
9163
+ }
9164
+ const entries = [];
9165
+ for (const [index, entry] of value.entries()) {
9166
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
9167
+ rejectField(rejectedFields, `requestUsageEntries[${index}]`);
9168
+ entries.push({});
9169
+ continue;
9170
+ }
9171
+ entries.push(entry);
9172
+ }
9173
+ return entries;
9174
+ }
9175
+ function completeRequestTokenAggregate(requestEntries, requestKeys, aggregatePath, rejectedFields) {
9176
+ if (requestEntries.length === 0) {
9177
+ return { status: "absent", value: null };
9178
+ }
9179
+ const values = [];
9180
+ let incomplete = false;
9181
+ let invalid = false;
9182
+ for (const [index, entry] of requestEntries.entries()) {
9183
+ const selected = firstPresentEntry(entry, requestKeys);
9184
+ if (!selected || selected.value === null) {
9185
+ incomplete = true;
9186
+ continue;
9187
+ }
9188
+ const parsed = reportedTokenCount(
9189
+ selected.value,
9190
+ `requestUsageEntries[${index}].${selected.key}`,
9191
+ rejectedFields
9192
+ );
9193
+ if (parsed === null) invalid = true;
9194
+ values.push(parsed);
9195
+ }
9196
+ if (invalid) {
9197
+ return { status: "invalid", value: null };
9198
+ }
9199
+ if (incomplete) {
9200
+ return { status: "incomplete", value: null };
9201
+ }
9202
+ const value = boundedTokenSum(values, aggregatePath, rejectedFields);
9203
+ return value === null ? { status: "invalid", value: null } : { status: "complete", value };
9204
+ }
9205
+ function canonicalTokenDimension(input) {
9206
+ if (input.requestAggregate.status === "complete") {
9207
+ if (input.topLevelReported && input.topLevel !== null && input.topLevel !== input.requestAggregate.value) {
9208
+ rejectField(input.rejectedFields, input.topLevelPath);
9209
+ }
9210
+ return input.requestAggregate.value;
9211
+ }
9212
+ if (input.requestAggregate.status === "invalid") {
9213
+ if (input.topLevelReported) {
9214
+ rejectField(input.rejectedFields, input.topLevelPath);
9215
+ }
9029
9216
  return null;
9030
9217
  }
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;
9218
+ return input.topLevel;
9219
+ }
9220
+ function canonicalTotalTokens(input) {
9221
+ if (input.inputTokens !== null && input.outputTokens !== null) {
9222
+ const derived = boundedTokenSum(
9223
+ [input.inputTokens, input.outputTokens],
9224
+ "totalTokens.aggregate",
9225
+ input.rejectedFields
9226
+ );
9227
+ if (derived === null) {
9228
+ if (input.topLevelReported) rejectField(input.rejectedFields, "totalTokens");
9229
+ if (input.requestAggregate.status === "complete") {
9230
+ rejectField(input.rejectedFields, "totalTokens.requestUsageEntries");
9038
9231
  }
9039
- if (value === 0) {
9040
- sawZero = true;
9232
+ return null;
9233
+ }
9234
+ if (input.topLevelReported && input.topLevel !== null && input.topLevel !== derived) {
9235
+ rejectField(input.rejectedFields, "totalTokens");
9236
+ }
9237
+ if (input.requestAggregate.status === "complete" && input.requestAggregate.value !== derived) {
9238
+ rejectField(input.rejectedFields, "totalTokens.requestUsageEntries");
9239
+ }
9240
+ return derived;
9241
+ }
9242
+ if (input.requestAggregate.status === "complete") {
9243
+ const requestTotal = input.requestAggregate.value;
9244
+ if (requestTotal === null || !totalCoversKnownComponents(requestTotal, input.inputTokens, input.outputTokens)) {
9245
+ rejectField(input.rejectedFields, "totalTokens.requestUsageEntries");
9246
+ if (input.topLevelReported) rejectField(input.rejectedFields, "totalTokens");
9247
+ return null;
9248
+ }
9249
+ if (input.topLevelReported && input.topLevel !== null && input.topLevel !== requestTotal) {
9250
+ rejectField(input.rejectedFields, "totalTokens");
9251
+ }
9252
+ return requestTotal;
9253
+ }
9254
+ if (input.requestAggregate.status === "invalid") {
9255
+ if (input.topLevelReported) rejectField(input.rejectedFields, "totalTokens");
9256
+ return null;
9257
+ }
9258
+ if (input.topLevel !== null && !totalCoversKnownComponents(input.topLevel, input.inputTokens, input.outputTokens)) {
9259
+ rejectField(input.rejectedFields, "totalTokens");
9260
+ return null;
9261
+ }
9262
+ return input.topLevel;
9263
+ }
9264
+ function totalCoversKnownComponents(total, inputTokens, outputTokens) {
9265
+ return (inputTokens === null || total >= inputTokens) && (outputTokens === null || total >= outputTokens);
9266
+ }
9267
+ function rejectInconsistentRequestTotals(requestEntries, rejectedFields) {
9268
+ let inconsistent = false;
9269
+ for (const [index, entry] of requestEntries.entries()) {
9270
+ const record = entry;
9271
+ const selectedInput = firstPresentEntry(record, ["inputTokens", "input_tokens"]);
9272
+ const selectedOutput = firstPresentEntry(record, ["outputTokens", "output_tokens"]);
9273
+ const selectedTotal = firstPresentEntry(record, ["totalTokens", "total_tokens"]);
9274
+ if (!selectedInput || !selectedOutput || !selectedTotal) continue;
9275
+ const inputTokens = modelUsageTokenCountOrNull(selectedInput.value);
9276
+ const outputTokens = modelUsageTokenCountOrNull(selectedOutput.value);
9277
+ const totalTokens = modelUsageTokenCountOrNull(selectedTotal.value);
9278
+ if (inputTokens === null || outputTokens === null || totalTokens === null) continue;
9279
+ const derived = inputTokens <= MAX_MODEL_USAGE_TOKEN_COUNT - outputTokens ? inputTokens + outputTokens : null;
9280
+ if (derived === null || totalTokens !== derived) {
9281
+ rejectField(rejectedFields, `requestUsageEntries[${index}].${selectedTotal.key}`);
9282
+ inconsistent = true;
9283
+ }
9284
+ }
9285
+ return inconsistent;
9286
+ }
9287
+ function isReported(value) {
9288
+ return value !== void 0 && value !== null;
9289
+ }
9290
+ function preferredDetailSource(aggregateDetails, aggregatePath, requestEntries, requestKeys) {
9291
+ const requestDetails = [];
9292
+ let sawRequestDetails = false;
9293
+ for (const [index, entry] of requestEntries.entries()) {
9294
+ const selected = firstPresentEntry(entry, requestKeys);
9295
+ if (!selected) {
9296
+ requestDetails.push({
9297
+ details: void 0,
9298
+ path: `requestUsageEntries[${index}].${requestKeys[0]}`,
9299
+ missing: true
9300
+ });
9301
+ continue;
9302
+ }
9303
+ sawRequestDetails = true;
9304
+ requestDetails.push({
9305
+ details: selected.value,
9306
+ path: `requestUsageEntries[${index}].${selected.key}`
9307
+ });
9308
+ }
9309
+ return sawRequestDetails ? requestDetails : aggregateDetails === void 0 ? [] : [{ details: aggregateDetails, path: aggregatePath }];
9310
+ }
9311
+ function aggregateDetailTokenCount(sources, keys, field, rejectedFields) {
9312
+ const values = [];
9313
+ let sawReportedValue = false;
9314
+ let sawMissingValue = false;
9315
+ for (const source of sources) {
9316
+ if (source.missing) {
9317
+ sawMissingValue = true;
9318
+ continue;
9319
+ }
9320
+ const details = detailRecords(source.details, source.path, rejectedFields);
9321
+ if (!details.complete) {
9322
+ sawMissingValue = true;
9323
+ }
9324
+ for (const detail of details.records) {
9325
+ const selected = firstPresentEntry(detail.record, keys);
9326
+ if (!selected) {
9327
+ sawMissingValue = true;
9328
+ continue;
9041
9329
  }
9330
+ sawReportedValue = true;
9331
+ values.push(
9332
+ reportedTokenCount(selected.value, `${detail.path}.${selected.key}`, rejectedFields)
9333
+ );
9334
+ }
9335
+ }
9336
+ return sawReportedValue && !sawMissingValue ? boundedTokenSum(values, `${field}.aggregate`, rejectedFields) : null;
9337
+ }
9338
+ function detailRecords(details, path, rejectedFields) {
9339
+ const entries = Array.isArray(details) ? details : [details];
9340
+ const records = [];
9341
+ let complete = true;
9342
+ for (const [index, entry] of entries.entries()) {
9343
+ const entryPath = Array.isArray(details) ? `${path}[${index}]` : path;
9344
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
9345
+ rejectField(rejectedFields, entryPath);
9346
+ complete = false;
9347
+ continue;
9348
+ }
9349
+ records.push({ record: entry, path: entryPath });
9350
+ }
9351
+ return { records, complete };
9352
+ }
9353
+ function firstPresentEntry(record, keys) {
9354
+ for (const key of keys) {
9355
+ if (Object.prototype.hasOwnProperty.call(record, key) && record[key] !== void 0) {
9356
+ return { key, value: record[key] };
9357
+ }
9358
+ }
9359
+ return null;
9360
+ }
9361
+ function reportedTokenCount(value, field, rejectedFields) {
9362
+ if (value === void 0 || value === null) {
9363
+ return null;
9364
+ }
9365
+ const normalized = modelUsageTokenCountOrNull(value);
9366
+ if (normalized === null) {
9367
+ rejectField(rejectedFields, field);
9368
+ }
9369
+ return normalized;
9370
+ }
9371
+ function boundedTokenSum(values, field, rejectedFields) {
9372
+ if (values.length === 0 || values.some((value) => value === null)) {
9373
+ return null;
9374
+ }
9375
+ let total = 0;
9376
+ for (const value of values) {
9377
+ if (value > MAX_MODEL_USAGE_TOKEN_COUNT - total) {
9378
+ rejectField(rejectedFields, field);
9379
+ return null;
9042
9380
  }
9381
+ total += value;
9043
9382
  }
9044
- return sawZero ? 0 : null;
9383
+ return total;
9045
9384
  }
9046
- function finiteNumberOrNull(value) {
9047
- return typeof value === "number" && Number.isFinite(value) ? value : null;
9385
+ function rejectField(rejectedFields, field) {
9386
+ if (rejectedFields.size < 32) {
9387
+ rejectedFields.add(field);
9388
+ }
9048
9389
  }
9049
9390
 
9050
9391
  // src/index.ts
@@ -9154,6 +9495,10 @@ function buildProviderClient(provider, settings) {
9154
9495
  // SDK retries on network/5xx/partial streams can replay provider work
9155
9496
  // or external tool side effects without a durable checkpoint.
9156
9497
  maxRetries: 0,
9498
+ // The Codex transport owns finer headers/idle/whole deadlines and
9499
+ // emits typed durable evidence. Keep the SDK's opaque envelope beyond
9500
+ // that whole-response budget so `Request timed out.` cannot win first.
9501
+ timeout: CODEX_RESPONSE_SDK_OUTER_TIMEOUT_MS,
9157
9502
  fetch: codexSubscriptionFetch(instrumentedModelFetch(provider.id, globalThis.fetch))
9158
9503
  })
9159
9504
  ) : (
@@ -9184,7 +9529,7 @@ function resolveTurnModel(settings, modelId) {
9184
9529
  return {
9185
9530
  provider: resolved.provider,
9186
9531
  client,
9187
- model: buildModelInstance(resolved.provider, client, resolved.model.id),
9532
+ model: buildModelInstance(resolved.provider, client, resolved.model.upstreamModelId),
9188
9533
  configured: resolved.model
9189
9534
  };
9190
9535
  }
@@ -9834,7 +10179,7 @@ function maybeInstallCodexToolSearch(agent, settings, options) {
9834
10179
  }
9835
10180
  }
9836
10181
  function mcpToolRequiresApproval(policy, unprefixedName) {
9837
- return policy === true || Array.isArray(policy) && policy.includes(unprefixedName);
10182
+ return policy === true || policy !== false && policy.has(unprefixedName);
9838
10183
  }
9839
10184
  function installMcpApprovalPolicy(agent, policies) {
9840
10185
  const listMcpTools = agent.getMcpTools.bind(agent);
@@ -9866,7 +10211,7 @@ function applyMcpApprovalPolicy(agent, settings) {
9866
10211
  (server) => server.requireApproval === true || Array.isArray(server.requireApproval) && server.requireApproval.length > 0
9867
10212
  ).map((server) => ({
9868
10213
  prefix: prefixedMcpToolName(server.id, ""),
9869
- requireApproval: server.requireApproval
10214
+ requireApproval: server.requireApproval === true ? true : new Set(server.requireApproval)
9870
10215
  })).sort((a, b) => b.prefix.length - a.prefix.length);
9871
10216
  if (policies.length === 0) {
9872
10217
  return;
@@ -9966,22 +10311,76 @@ function buildAgentCapabilities(settings, packSkills, options = {}) {
9966
10311
  function sandboxRunAs(_settings) {
9967
10312
  return void 0;
9968
10313
  }
10314
+ async function connectMcpServersInBatches(servers, options) {
10315
+ assertMcpServerSelectionWithinBounds(servers);
10316
+ const batches = [];
10317
+ try {
10318
+ for (let offset = 0; offset < servers.length; offset += MCP_MAX_CONCURRENT_SERVER_OPERATIONS) {
10319
+ batches.push(
10320
+ await connectMcpServers(
10321
+ servers.slice(offset, offset + MCP_MAX_CONCURRENT_SERVER_OPERATIONS),
10322
+ {
10323
+ connectInParallel: true,
10324
+ strict: options.strict
10325
+ }
10326
+ )
10327
+ );
10328
+ }
10329
+ } catch (error) {
10330
+ await closeMcpServerBatches(batches).catch(() => void 0);
10331
+ throw error;
10332
+ }
10333
+ const errors = /* @__PURE__ */ new Map();
10334
+ for (const batch of batches) {
10335
+ for (const [server, error] of batch.errors) errors.set(server, error);
10336
+ }
10337
+ return {
10338
+ active: batches.flatMap((batch) => batch.active),
10339
+ failed: batches.flatMap((batch) => batch.failed),
10340
+ errors,
10341
+ close: async () => {
10342
+ await closeMcpServerBatches(batches);
10343
+ }
10344
+ };
10345
+ }
10346
+ async function closeMcpServerBatches(batches) {
10347
+ let firstError;
10348
+ for (const batch of [...batches].reverse()) {
10349
+ try {
10350
+ await batch.close();
10351
+ } catch (error) {
10352
+ firstError ??= error;
10353
+ }
10354
+ }
10355
+ if (firstError !== void 0) throw firstError;
10356
+ }
9969
10357
  async function prepareAgentTools(settings, tools, options = {}) {
9970
10358
  const codexConnectorNamespaces = /* @__PURE__ */ new Set();
10359
+ assertMcpServerSelectionWithinBounds(tools);
9971
10360
  if (tools.length === 0) {
9972
10361
  return { mcpServers: [], close: async () => {
9973
10362
  }, codexConnectorNamespaces };
9974
10363
  }
9975
10364
  const registry = new Map(settings.mcpServers.map((server) => [server.id, server]));
9976
- const servers = await Promise.all(
9977
- tools.map(async (tool2) => {
10365
+ const aggregateToolBudget = new McpAggregateToolListBudget();
10366
+ const mcpFetchImpl = options.mcpFetchImpl ?? undiciFetch;
10367
+ const servers = await boundedParallelMap(
10368
+ tools,
10369
+ MCP_MAX_CONCURRENT_SERVER_OPERATIONS,
10370
+ async (tool2, index) => {
9978
10371
  const config = registry.get(tool2.id);
9979
10372
  if (!config) {
9980
10373
  throw new Error(`Unknown MCP server id: ${tool2.id}`);
9981
10374
  }
9982
10375
  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;
10376
+ const firstParty = isFirstPartyMcpServer(settings, config);
10377
+ const baseFetch = isCodexAppsMcpServer(config) ? codexAppsSanitizingFetch(mcpFetchImpl, codexConnectorNamespaces) : mcpFetchImpl;
10378
+ const guardedFetch = guardedMcpFetch(
10379
+ firstParty ? { ...settings, integrationsAllowPrivateNetworkTargets: true } : settings,
10380
+ baseFetch,
10381
+ firstParty ? { requireHttpsOutsideLocalTest: false } : {}
10382
+ );
10383
+ const fetchImpl = config.connectionRef ? connectionBrokerFetch(guardedFetch, config, options) : firstParty ? firstPartyAuthFetch(guardedFetch, settings, options) : guardedFetch;
9985
10384
  const optional = tool2.optional === true;
9986
10385
  const bestEffort = isCodexAppsMcpServer(config) || optional || !!config.connectionRef;
9987
10386
  const server = new PrefixedMcpServer(
@@ -9993,7 +10392,7 @@ async function prepareAgentTools(settings, tools, options = {}) {
9993
10392
  // MCP SDK's strict Tool schema rejects (fails the turn during tools/list);
9994
10393
  // sanitize the response on the wire before validation. The namespace Set
9995
10394
  // also captures each tool's original connector namespace (P4 Part B.1).
9996
- ...fetchImpl !== globalThis.fetch ? { fetch: fetchImpl } : {},
10395
+ fetch: fetchImpl,
9997
10396
  ...await mcpServerRequestInit(settings, config),
9998
10397
  ...config.timeoutMs ? {
9999
10398
  timeout: config.timeoutMs,
@@ -10002,30 +10401,35 @@ async function prepareAgentTools(settings, tools, options = {}) {
10002
10401
  }),
10003
10402
  config.id,
10004
10403
  config.allowedTools,
10005
- bestEffort
10404
+ bestEffort,
10405
+ aggregateToolBudget,
10406
+ `${config.id}:${index}`
10006
10407
  );
10007
10408
  return {
10008
10409
  server,
10009
10410
  bestEffort,
10010
10411
  optional
10011
10412
  };
10012
- })
10413
+ }
10013
10414
  );
10014
10415
  const requiredServers = servers.filter((entry) => !entry.bestEffort).map((entry) => entry.server);
10015
10416
  const bestEffortServers = servers.filter((entry) => entry.bestEffort).map((entry) => entry.server);
10016
10417
  const optionalServerNames = new Set(
10017
10418
  servers.filter((entry) => entry.optional).map((entry) => entry.server.name)
10018
10419
  );
10019
- const connectedRequired = await connectMcpServers(requiredServers, {
10020
- connectInParallel: true,
10021
- strict: true
10022
- });
10023
- const connectedBestEffort = bestEffortServers.length ? await connectMcpServers(bestEffortServers, {
10024
- connectInParallel: true,
10025
- strict: false
10026
- }) : null;
10420
+ const connectedRequired = await connectMcpServersInBatches(requiredServers, { strict: true });
10421
+ let connectedBestEffort = null;
10422
+ try {
10423
+ connectedBestEffort = bestEffortServers.length ? await connectMcpServersInBatches(bestEffortServers, { strict: false }) : null;
10424
+ } catch (error) {
10425
+ await connectedRequired.close().catch(() => void 0);
10426
+ throw error;
10427
+ }
10027
10428
  if (connectedBestEffort) {
10028
10429
  for (const failed of connectedBestEffort.failed) {
10430
+ if (failed instanceof PrefixedMcpServer) {
10431
+ failed.releaseAggregateBudget();
10432
+ }
10029
10433
  if (!optionalServerNames.has(failed.name)) {
10030
10434
  continue;
10031
10435
  }
@@ -10039,10 +10443,20 @@ async function prepareAgentTools(settings, tools, options = {}) {
10039
10443
  return {
10040
10444
  mcpServers: [...connectedRequired.active, ...connectedBestEffort?.active ?? []],
10041
10445
  close: async () => {
10042
- await connectedRequired.close();
10446
+ let firstError;
10043
10447
  if (connectedBestEffort) {
10044
- await connectedBestEffort.close();
10448
+ try {
10449
+ await connectedBestEffort.close();
10450
+ } catch (error) {
10451
+ firstError ??= error;
10452
+ }
10045
10453
  }
10454
+ try {
10455
+ await connectedRequired.close();
10456
+ } catch (error) {
10457
+ firstError ??= error;
10458
+ }
10459
+ if (firstError !== void 0) throw firstError;
10046
10460
  },
10047
10461
  codexConnectorNamespaces
10048
10462
  };
@@ -10054,10 +10468,12 @@ function connectionBrokerFetch(baseFetch, config, options) {
10054
10468
  }
10055
10469
  return async (input, init) => {
10056
10470
  const request = await mcpRequestInfo(input, init);
10471
+ const destinationUrl = mcpRequestDestinationUrl(input);
10057
10472
  const first = await resolveConnectionForRequest(
10058
10473
  options,
10059
10474
  config.id,
10060
10475
  connectionRef,
10476
+ destinationUrl,
10061
10477
  request.toolName,
10062
10478
  false
10063
10479
  );
@@ -10069,10 +10485,12 @@ function connectionBrokerFetch(baseFetch, config, options) {
10069
10485
  withConnectionHeaders(input, init, first.headers)
10070
10486
  );
10071
10487
  if (response.status === 401) {
10488
+ await cancelMcpResponseBody(response);
10072
10489
  const refreshed = await resolveConnectionForRequest(
10073
10490
  options,
10074
10491
  config.id,
10075
10492
  connectionRef,
10493
+ destinationUrl,
10076
10494
  request.toolName,
10077
10495
  true
10078
10496
  );
@@ -10085,9 +10503,14 @@ function connectionBrokerFetch(baseFetch, config, options) {
10085
10503
  );
10086
10504
  if (retry.status === 403) {
10087
10505
  const auth = insufficientScopeAuth(retry.headers, connectionRef, refreshed.connectionId);
10088
- return auth ? await authNeededFetchResponse(options, config.id, request, auth, connectionRef) : retry;
10506
+ if (auth) {
10507
+ await cancelMcpResponseBody(retry);
10508
+ return await authNeededFetchResponse(options, config.id, request, auth, connectionRef);
10509
+ }
10510
+ return retry;
10089
10511
  }
10090
10512
  if (retry.status === 401) {
10513
+ await cancelMcpResponseBody(retry);
10091
10514
  return await authNeededFetchResponse(
10092
10515
  options,
10093
10516
  config.id,
@@ -10109,12 +10532,16 @@ function connectionBrokerFetch(baseFetch, config, options) {
10109
10532
  }
10110
10533
  if (response.status === 403) {
10111
10534
  const auth = insufficientScopeAuth(response.headers, connectionRef, first.connectionId);
10112
- return auth ? await authNeededFetchResponse(options, config.id, request, auth, connectionRef) : response;
10535
+ if (auth) {
10536
+ await cancelMcpResponseBody(response);
10537
+ return await authNeededFetchResponse(options, config.id, request, auth, connectionRef);
10538
+ }
10539
+ return response;
10113
10540
  }
10114
10541
  return response;
10115
10542
  };
10116
10543
  }
10117
- async function resolveConnectionForRequest(options, serverId, connectionRef, toolName, forceRefresh) {
10544
+ async function resolveConnectionForRequest(options, serverId, connectionRef, destinationUrl, toolName, forceRefresh) {
10118
10545
  if (!options.workspaceId || !options.resolveCredential) {
10119
10546
  return {
10120
10547
  status: "auth_needed",
@@ -10131,6 +10558,7 @@ async function resolveConnectionForRequest(options, serverId, connectionRef, too
10131
10558
  workspaceId: options.workspaceId,
10132
10559
  serverId,
10133
10560
  connectionRef,
10561
+ destinationUrl,
10134
10562
  forceRefresh,
10135
10563
  ...toolName ? { toolName } : {},
10136
10564
  ...options.subjectId ? { subjectId: options.subjectId } : {}
@@ -10194,6 +10622,9 @@ async function publishAuthNeeded(options, payload) {
10194
10622
  } catch {
10195
10623
  }
10196
10624
  }
10625
+ function mcpRequestDestinationUrl(input) {
10626
+ return new URL(input instanceof Request ? input.url : input.toString()).toString();
10627
+ }
10197
10628
  async function mcpRequestInfo(input, init) {
10198
10629
  const body = typeof init?.body === "string" ? init.body : input instanceof Request && (init?.method ?? input.method).toUpperCase() === "POST" ? await input.clone().text().catch(() => "") : "";
10199
10630
  if (!body) {
@@ -10326,7 +10757,7 @@ async function signFirstPartyDelegatedBearer(settings, options) {
10326
10757
  workspaceId: options.workspaceId,
10327
10758
  subjectId: options.subjectId ?? "worker:first-party-mcp",
10328
10759
  ...options.subjectLabel ? { subjectLabel: options.subjectLabel } : {},
10329
- permissions: options.firstPartyPermissions ?? firstPartyMcpPermissions,
10760
+ permissions: options.firstPartyPermissions ?? [...DEFAULT_FIRST_PARTY_MCP_PERMISSIONS],
10330
10761
  ...options.sessionId ? { sessionId: options.sessionId } : {},
10331
10762
  ...options.turnId ? { turnId: options.turnId } : {},
10332
10763
  ...options.attemptId ? { attemptId: options.attemptId } : {},
@@ -10375,21 +10806,6 @@ async function codexAppsMcpRequestInit(settings) {
10375
10806
  }
10376
10807
  return { requestInit: { headers } };
10377
10808
  }
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
10809
  function isCodexAppsMcpServer(config) {
10394
10810
  return config.id === CODEX_APPS_MCP_SERVER_ID;
10395
10811
  }
@@ -10457,8 +10873,10 @@ function prefixedMcpToolName(registryId, toolName) {
10457
10873
  return sharedPrefixedMcpToolName(registryId, toolName);
10458
10874
  }
10459
10875
  var PrefixedMcpServer = class {
10460
- constructor(inner, registryId, allowedTools, bestEffort = false) {
10876
+ constructor(inner, registryId, allowedTools, bestEffort = false, aggregateToolBudget, aggregateSourceId = registryId) {
10461
10877
  this.inner = inner;
10878
+ this.aggregateToolBudget = aggregateToolBudget;
10879
+ this.aggregateSourceId = aggregateSourceId;
10462
10880
  this.name = registryId;
10463
10881
  this.prefix = prefixedMcpToolName(registryId, "");
10464
10882
  this.cacheToolsList = inner.cacheToolsList;
@@ -10480,12 +10898,22 @@ var PrefixedMcpServer = class {
10480
10898
  return this.inner.connect();
10481
10899
  }
10482
10900
  close() {
10901
+ this.releaseAggregateBudget();
10483
10902
  return this.inner.close();
10484
10903
  }
10904
+ releaseAggregateBudget() {
10905
+ this.aggregateToolBudget?.remove(this.aggregateSourceId);
10906
+ }
10485
10907
  async listTools() {
10486
- let tools;
10487
10908
  try {
10488
- tools = await this.inner.listTools();
10909
+ const tools = assertMcpToolListWithinBounds(await this.inner.listTools());
10910
+ const exposed = tools.filter((tool2) => this.isAllowed(tool2.name)).map((tool2) => ({
10911
+ ...tool2,
10912
+ name: prefixedMcpToolName(this.name, tool2.name)
10913
+ }));
10914
+ const bounded = this.aggregateToolBudget?.replace(this.aggregateSourceId, exposed) ?? assertMcpToolListWithinBounds(exposed);
10915
+ this.listedToolSchemaTokens = estimateSerializedValueTokens(bounded);
10916
+ return bounded;
10489
10917
  } catch (error) {
10490
10918
  if (!this.bestEffort) {
10491
10919
  throw error;
@@ -10499,14 +10927,9 @@ var PrefixedMcpServer = class {
10499
10927
  { serverId: this.name, ...safeMcpErrorFields(error) }
10500
10928
  );
10501
10929
  }
10930
+ this.releaseAggregateBudget();
10502
10931
  return [];
10503
10932
  }
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
10933
  }
10511
10934
  /** Latest exact tools/list projection used to build the model request. */
10512
10935
  modelToolSchemaTokens() {
@@ -10518,7 +10941,9 @@ var PrefixedMcpServer = class {
10518
10941
  throw new Error(`MCP tool ${unprefixed} is not allowed for server ${this.name}`);
10519
10942
  }
10520
10943
  try {
10521
- return await this.inner.callTool(unprefixed, args, meta);
10944
+ const output = await this.inner.callTool(unprefixed, args, meta);
10945
+ assertMcpPayloadWithinBytes(output, MCP_MAX_TOOL_RESULT_BYTES, "MCP tool result");
10946
+ return output;
10522
10947
  } catch (error) {
10523
10948
  if (isAuthNeededMcpError(error)) {
10524
10949
  return {
@@ -11590,17 +12015,18 @@ function usageFromResponse(response) {
11590
12015
  ),
11591
12016
  ...numberProp(record, "totalTokens", "totalTokens", "total_tokens"),
11592
12017
  ...inputTokenDetailsProp(record),
11593
- ...outputTokenDetailsProp(record)
12018
+ ...outputTokenDetailsProp(record),
12019
+ ...requestUsageEntriesProp(record)
11594
12020
  };
11595
12021
  return Object.keys(usage).length > 0 ? usage : null;
11596
12022
  }
11597
12023
  function numberProp(raw, outputKey, ...keys) {
11598
12024
  const value = keys.map((key) => raw[key]).find((candidate) => candidate !== void 0);
11599
- return typeof value === "number" && Number.isFinite(value) ? { [outputKey]: value } : {};
12025
+ return typeof value === "number" ? { [outputKey]: value } : {};
11600
12026
  }
11601
12027
  function inputTokenDetailsProp(raw) {
11602
12028
  const details = raw.inputTokensDetails ?? raw.input_tokens_details ?? raw.promptTokensDetails ?? raw.prompt_tokens_details;
11603
- if (!details || typeof details !== "object") {
12029
+ if (details === void 0 || details === null) {
11604
12030
  return {};
11605
12031
  }
11606
12032
  return {
@@ -11610,13 +12036,25 @@ function inputTokenDetailsProp(raw) {
11610
12036
  function outputTokenDetailsProp(raw) {
11611
12037
  const details = raw.outputTokensDetails ?? raw.output_tokens_details;
11612
12038
  const normalized = details ?? raw.completionTokensDetails ?? raw.completion_tokens_details;
11613
- if (!normalized || typeof normalized !== "object") {
12039
+ if (normalized === void 0 || normalized === null) {
11614
12040
  return {};
11615
12041
  }
11616
12042
  return {
11617
12043
  outputTokensDetails: normalized
11618
12044
  };
11619
12045
  }
12046
+ function requestUsageEntriesProp(raw) {
12047
+ const entries = raw.requestUsageEntries ?? raw.request_usage_entries;
12048
+ if (entries === void 0 || entries === null) {
12049
+ return {};
12050
+ }
12051
+ return {
12052
+ // The normalizer validates every entry and all supported field aliases.
12053
+ // Preserve the SDK objects rather than rebuilding them and accidentally
12054
+ // dropping provider detail fields such as cache_write_tokens.
12055
+ requestUsageEntries: entries
12056
+ };
12057
+ }
11620
12058
  function serializeApprovals(interruptions) {
11621
12059
  return interruptions.filter((item) => interruptionToolName(item) !== HUMAN_INPUT_TOOL_NAME).map((item) => {
11622
12060
  if (typeof item?.toJSON === "function") {
@@ -12085,13 +12523,95 @@ function runtimeGitBindingDescriptors(resources) {
12085
12523
  };
12086
12524
  });
12087
12525
  }
12526
+ function gitCredentialBindingKey(provider, credentialBindingId) {
12527
+ return `${provider}\0${credentialBindingId}`;
12528
+ }
12529
+ function brokeredGitCredentialBindingKeys(bindings) {
12530
+ return new Set(
12531
+ bindings.filter((binding) => binding.transport?.kind === "http_broker").map((binding) => gitCredentialBindingKey(binding.provider, binding.credentialBindingId))
12532
+ );
12533
+ }
12534
+ function runtimeGitHttpBrokerRouteDescriptors(resources, bindings) {
12535
+ const resourceDescriptors = runtimeGitBindingDescriptors(resources);
12536
+ const byBindingAndUri = /* @__PURE__ */ new Map();
12537
+ for (const descriptor of resourceDescriptors) {
12538
+ byBindingAndUri.set(
12539
+ `${gitCredentialBindingKey(descriptor.provider, descriptor.credentialBindingId)}\0${descriptor.uri}`,
12540
+ descriptor
12541
+ );
12542
+ }
12543
+ const routes = [];
12544
+ const claimedRepositoryUris = /* @__PURE__ */ new Set();
12545
+ const claimedBrokerUris = /* @__PURE__ */ new Set();
12546
+ for (const binding of bindings) {
12547
+ if (!binding.transport) continue;
12548
+ if (typeof binding.transport !== "object" || binding.transport.kind !== "http_broker" || !Array.isArray(binding.transport.repositories)) {
12549
+ throw new Error(
12550
+ `Git credential binding ${binding.credentialBindingId} uses an unsupported transport`
12551
+ );
12552
+ }
12553
+ const bindingKey = gitCredentialBindingKey(binding.provider, binding.credentialBindingId);
12554
+ const expected = resourceDescriptors.filter(
12555
+ (descriptor) => gitCredentialBindingKey(descriptor.provider, descriptor.credentialBindingId) === bindingKey
12556
+ );
12557
+ if (expected.length === 0 || binding.transport.repositories.length !== expected.length) {
12558
+ throw new Error(
12559
+ `Git HTTP broker binding ${binding.credentialBindingId} does not cover its exact repository set`
12560
+ );
12561
+ }
12562
+ for (const route of binding.transport.repositories) {
12563
+ if (!route || typeof route !== "object" || typeof route.repositoryUri !== "string" || typeof route.brokerUri !== "string") {
12564
+ throw new Error(
12565
+ `Git HTTP broker binding ${binding.credentialBindingId} contains an invalid repository route`
12566
+ );
12567
+ }
12568
+ const descriptor = byBindingAndUri.get(`${bindingKey}\0${route.repositoryUri}`);
12569
+ if (!descriptor || claimedRepositoryUris.has(route.repositoryUri)) {
12570
+ throw new Error(
12571
+ `Git HTTP broker binding ${binding.credentialBindingId} contains an unexpected repository route`
12572
+ );
12573
+ }
12574
+ let brokerUrl;
12575
+ try {
12576
+ brokerUrl = new URL(route.brokerUri);
12577
+ } catch {
12578
+ throw new Error(
12579
+ `Git HTTP broker binding ${binding.credentialBindingId} contains an invalid broker URI`
12580
+ );
12581
+ }
12582
+ if (brokerUrl.protocol !== "https:" || brokerUrl.username || brokerUrl.password || brokerUrl.search || brokerUrl.hash || brokerUrl.href !== route.brokerUri || claimedBrokerUris.has(brokerUrl.href)) {
12583
+ throw new Error(
12584
+ `Git HTTP broker binding ${binding.credentialBindingId} contains an unsafe broker URI`
12585
+ );
12586
+ }
12587
+ claimedRepositoryUris.add(route.repositoryUri);
12588
+ claimedBrokerUris.add(brokerUrl.href);
12589
+ routes.push({
12590
+ provider: binding.provider,
12591
+ credentialBindingId: binding.credentialBindingId,
12592
+ bindingHash: gitCredentialBindingHash(binding.credentialBindingId),
12593
+ repositoryUri: route.repositoryUri,
12594
+ brokerUri: route.brokerUri,
12595
+ protocol: "https",
12596
+ host: brokerUrl.host.toLowerCase(),
12597
+ path: brokerUrl.pathname.replace(/^\/+|\/+$/g, "")
12598
+ });
12599
+ }
12600
+ }
12601
+ return routes;
12602
+ }
12088
12603
  function gitUsernameForProvider(provider) {
12089
12604
  if (provider === "github") return "x-access-token";
12090
12605
  if (provider === "gitlab") return "oauth2";
12091
12606
  return "opengeni";
12092
12607
  }
12093
- function gitCredentialHelperBindingCaseLines(resources) {
12094
- return runtimeGitBindingDescriptors(resources).flatMap((descriptor) => {
12608
+ function gitCredentialHelperBindingCaseLines(resources, bindings) {
12609
+ const brokeredBindings = brokeredGitCredentialBindingKeys(bindings);
12610
+ return runtimeGitBindingDescriptors(resources).filter(
12611
+ (descriptor) => !brokeredBindings.has(
12612
+ gitCredentialBindingKey(descriptor.provider, descriptor.credentialBindingId)
12613
+ )
12614
+ ).flatMap((descriptor) => {
12095
12615
  const paths = /* @__PURE__ */ new Set([
12096
12616
  descriptor.path,
12097
12617
  descriptor.path.replace(/\.git$/, ""),
@@ -12102,7 +12622,19 @@ function gitCredentialHelperBindingCaseLines(resources) {
12102
12622
  );
12103
12623
  });
12104
12624
  }
12105
- function gitAskpassHostProviderCaseLines(resources) {
12625
+ function gitCredentialHelperBrokerCaseLines(routes) {
12626
+ return routes.flatMap((route) => {
12627
+ const paths = /* @__PURE__ */ new Set([
12628
+ route.path,
12629
+ route.path.replace(/\.git$/, ""),
12630
+ `${route.path.replace(/\.git$/, "")}.git`
12631
+ ]);
12632
+ return [...paths].map(
12633
+ (path) => ` ${shellQuote(`${route.protocol}|${route.host}|${path}`)}) username=opengeni; token_file="$credential_dir/${route.bindingHash}-token" ;;`
12634
+ );
12635
+ });
12636
+ }
12637
+ function gitAskpassHostProviderCaseLines(resources, brokerRoutes) {
12106
12638
  const hosts = /* @__PURE__ */ new Map();
12107
12639
  for (const descriptor of runtimeGitBindingDescriptors(resources)) {
12108
12640
  const entry = hosts.get(descriptor.host) ?? {
@@ -12112,9 +12644,13 @@ function gitAskpassHostProviderCaseLines(resources) {
12112
12644
  entry.bindings.add(`${descriptor.provider}\0${descriptor.credentialBindingId}`);
12113
12645
  hosts.set(descriptor.host, entry);
12114
12646
  }
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
- );
12647
+ const brokerHosts = [...new Set(brokerRoutes.map((route) => route.host))].sort((a, b) => a.localeCompare(b)).map((hostname) => ` ${shellQuote(hostname)}) printf '\\n'; return 0 ;;`);
12648
+ return [
12649
+ ...brokerHosts,
12650
+ ...[...hosts.entries()].filter(([, entry]) => entry.bindings.size === 1).sort(([a], [b]) => a.localeCompare(b)).map(
12651
+ ([hostname, entry]) => ` ${shellQuote(hostname)}) printf '%s\\n' ${entry.provider}; return 0 ;;`
12652
+ )
12653
+ ];
12118
12654
  }
12119
12655
  function gitCredentialTokenWriterCommandLines(bindings = []) {
12120
12656
  const bindingWrites = bindings.flatMap((binding) => {
@@ -12123,7 +12659,7 @@ function gitCredentialTokenWriterCommandLines(bindings = []) {
12123
12659
  const count = Math.max(1, binding.providerBindingCount ?? 1);
12124
12660
  const provider = binding.provider;
12125
12661
  const lines = [`write_git_binding_token ${shellQuote(hash)} "\${${seedEnv}:-}"`];
12126
- if (count === 1) {
12662
+ if (count === 1 && binding.transport?.kind !== "http_broker") {
12127
12663
  lines.push(`write_git_provider_token ${shellQuote(provider)} "\${${seedEnv}:-}"`);
12128
12664
  } else {
12129
12665
  lines.push(`remove_git_provider_token ${shellQuote(provider)}`);
@@ -12184,11 +12720,36 @@ function gitCredentialTokenWriterCommandLines(bindings = []) {
12184
12720
  'umask "$seed_umask"'
12185
12721
  ];
12186
12722
  }
12723
+ function gitHttpBrokerConfigCommandLines(routes) {
12724
+ return [
12725
+ 'git_http_broker_config="${OPENGENI_GIT_CREDENTIALS_DIR:-$HOME/.opengeni/git-credentials}/http-broker.gitconfig"',
12726
+ 'mkdir -p "$(dirname "$git_http_broker_config")"',
12727
+ 'broker_umask="$(umask)"',
12728
+ "umask 077",
12729
+ ': > "$git_http_broker_config.tmp.$$"',
12730
+ ...routes.map(
12731
+ (route) => `git config --file "$git_http_broker_config.tmp.$$" --add ${shellQuote(`url.${route.brokerUri}.insteadOf`)} ${shellQuote(route.repositoryUri)}`
12732
+ ),
12733
+ 'mv -f "$git_http_broker_config.tmp.$$" "$git_http_broker_config"',
12734
+ 'umask "$broker_umask"',
12735
+ 'git config --global --unset-all include.path "$git_http_broker_config" >/dev/null 2>&1 || true',
12736
+ 'git config --global --add include.path "$git_http_broker_config"'
12737
+ ];
12738
+ }
12187
12739
  function gitCredentialHelperCommandLines(resources = [], bindings = []) {
12188
- const hostProviderCases = gitAskpassHostProviderCaseLines(resources);
12189
- const bindingCases = gitCredentialHelperBindingCaseLines(resources);
12740
+ const brokerRoutes = runtimeGitHttpBrokerRouteDescriptors(resources, bindings);
12741
+ const hostProviderCases = gitAskpassHostProviderCaseLines(resources, brokerRoutes);
12742
+ const bindingCases = [
12743
+ ...gitCredentialHelperBindingCaseLines(resources, bindings),
12744
+ ...gitCredentialHelperBrokerCaseLines(brokerRoutes)
12745
+ ];
12190
12746
  const descriptors = runtimeGitBindingDescriptors(resources);
12191
- const wrapperDescriptors = bindings.length > 0 ? descriptors : [];
12747
+ const brokeredBindings = brokeredGitCredentialBindingKeys(bindings);
12748
+ const wrapperDescriptors = bindings.length > 0 ? descriptors.filter(
12749
+ (descriptor) => !brokeredBindings.has(
12750
+ gitCredentialBindingKey(descriptor.provider, descriptor.credentialBindingId)
12751
+ )
12752
+ ) : [];
12192
12753
  const bindingProviders = /* @__PURE__ */ new Map();
12193
12754
  for (const descriptor of runtimeGitBindingDescriptors(resources)) {
12194
12755
  const ids = bindingProviders.get(descriptor.provider) ?? /* @__PURE__ */ new Set();
@@ -12213,6 +12774,23 @@ function gitCredentialHelperCommandLines(resources = [], bindings = []) {
12213
12774
  ] : [];
12214
12775
  });
12215
12776
  const multiWrapperProviders = bindings.length > 0 ? [...bindingProviders.entries()].filter(([, ids]) => ids.size > 1).map(([provider]) => provider) : [];
12777
+ const brokeredOriginCases = brokerRoutes.flatMap((route) => {
12778
+ const base = route.repositoryUri.replace(/\.git$/, "");
12779
+ return [.../* @__PURE__ */ new Set([route.repositoryUri, base, `${base}.git`])].map(
12780
+ (uri) => ` ${shellQuote(`${route.provider}|${uri}`)}) return 0 ;;`
12781
+ );
12782
+ });
12783
+ const brokeredBindingHashCases = bindings.filter((binding) => binding.transport?.kind === "http_broker").map(
12784
+ (binding) => ` ${shellQuote(`${binding.provider}|${gitCredentialBindingHash(binding.credentialBindingId)}`)}) return 0 ;;`
12785
+ );
12786
+ const providerBindingKinds = /* @__PURE__ */ new Map();
12787
+ for (const binding of bindings) {
12788
+ const counts = providerBindingKinds.get(binding.provider) ?? { direct: 0, brokered: 0 };
12789
+ if (binding.transport?.kind === "http_broker") counts.brokered += 1;
12790
+ else counts.direct += 1;
12791
+ providerBindingKinds.set(binding.provider, counts);
12792
+ }
12793
+ const brokerOnlyProviders = [...providerBindingKinds.entries()].filter(([, counts]) => counts.brokered > 0 && counts.direct === 0).map(([provider]) => provider);
12216
12794
  return [
12217
12795
  ...gitCredentialTokenWriterCommandLines(bindings),
12218
12796
  // Provision git/provider-CLI helpers at SETUP (runtime) before any clone
@@ -12304,6 +12882,7 @@ function gitCredentialHelperCommandLines(resources = [], bindings = []) {
12304
12882
  "git config --global --add credential.helper ''",
12305
12883
  'git config --global --add credential.helper "$git_credential_helper"',
12306
12884
  "git config --global credential.useHttpPath true",
12885
+ ...gitHttpBrokerConfigCommandLines(brokerRoutes),
12307
12886
  'wrapper_dir="${OPENGENI_GIT_CLI_WRAPPER_DIR:-$HOME/.opengeni/bin}"',
12308
12887
  'mkdir -p "$wrapper_dir"',
12309
12888
  "for opengeni_git_cli_tool in gh glab az; do",
@@ -12331,12 +12910,24 @@ function gitCredentialHelperCommandLines(resources = [], bindings = []) {
12331
12910
  " *) return 1 ;;",
12332
12911
  " esac",
12333
12912
  "}",
12913
+ "binding_hash_is_brokered() {",
12914
+ ' case "$provider|$1" in',
12915
+ ...brokeredBindingHashCases,
12916
+ " *) return 1 ;;",
12917
+ " esac",
12918
+ "}",
12334
12919
  "binding_hash_for_origin() {",
12335
12920
  ' case "$provider|$1" in',
12336
12921
  ...originWrapperHashes,
12337
12922
  " *) return 1 ;;",
12338
12923
  " esac",
12339
12924
  "}",
12925
+ "origin_is_brokered() {",
12926
+ ' case "$provider|$1" in',
12927
+ ...brokeredOriginCases,
12928
+ " *) return 1 ;;",
12929
+ " esac",
12930
+ "}",
12340
12931
  "sole_binding_hash() {",
12341
12932
  ' case "$provider" in',
12342
12933
  ...soleWrapperHashes,
@@ -12344,19 +12935,25 @@ function gitCredentialHelperCommandLines(resources = [], bindings = []) {
12344
12935
  " esac",
12345
12936
  "}",
12346
12937
  `multi_binding_providers=${shellQuote(multiWrapperProviders.join(" "))}`,
12938
+ `broker_only_providers=${shellQuote(brokerOnlyProviders.join(" "))}`,
12347
12939
  'if [ -n "$provider" ]; then',
12348
12940
  " binding_hash=",
12349
12941
  ' if [ -n "${OPENGENI_GIT_BINDING:-}" ]; then',
12350
12942
  ' binding_hash="$(hash_binding_id "$OPENGENI_GIT_BINDING")"',
12943
+ ` 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
12944
  ` 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
12945
  " elif command -v git >/dev/null 2>&1; then",
12353
12946
  ' origin="$(git config --get remote.origin.url 2>/dev/null || true)"',
12947
+ ` [ -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
12948
  ' [ -z "$origin" ] || binding_hash="$(binding_hash_for_origin "$origin" 2>/dev/null || true)"',
12355
12949
  " fi",
12356
12950
  ' [ -n "$binding_hash" ] || binding_hash="$(sole_binding_hash 2>/dev/null || true)"',
12357
12951
  ' if [ -n "$binding_hash" ]; then',
12358
12952
  ' token_file="${OPENGENI_GIT_CREDENTIALS_DIR:-$HOME/.opengeni/git-credentials}/$binding_hash-token"',
12359
12953
  " else",
12954
+ ' case " $broker_only_providers " in',
12955
+ ` *" $provider "*) printf '%s\\n' "$tool provider API authentication is host-brokered for this session; use the configured provider MCP tools" >&2; exit 2 ;;`,
12956
+ " esac",
12360
12957
  ' case " $multi_binding_providers " in',
12361
12958
  ` *" $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
12959
  " esac",
@@ -13194,6 +13791,7 @@ export {
13194
13791
  GENESIS_TITLE_DIRECTIVE,
13195
13792
  HUMAN_INPUT_TOOL_NAME,
13196
13793
  MAX_COMPACTION_THRESHOLD_RATIO,
13794
+ MAX_MODEL_USAGE_TOKEN_COUNT,
13197
13795
  MIN_COMPACTION_THRESHOLD_RATIO,
13198
13796
  MaxTurnsExceededError,
13199
13797
  MockAgentResponder,
@@ -13247,6 +13845,7 @@ export {
13247
13845
  TerminalServerError,
13248
13846
  TerminalServerUnsupportedError,
13249
13847
  ToolspaceTokenPathError,
13848
+ TurnSandboxCommandCancelledError,
13250
13849
  USER_MESSAGE_TRUNCATION_MARKER,
13251
13850
  WorkspaceModelPolicyBlockedError,
13252
13851
  agentErrorToControlError,
@@ -13291,12 +13890,14 @@ export {
13291
13890
  computerUse,
13292
13891
  configureOpenAI,
13293
13892
  configureRuntimeMetricsHooks,
13893
+ connectMcpServersInBatches,
13294
13894
  contentTypeForCodec,
13295
13895
  contextRobustnessFilterForSettings,
13296
13896
  coreInstructions,
13297
13897
  createProductionAgentRuntime,
13298
13898
  createSandboxClient,
13299
13899
  createSandboxClientForBackend,
13900
+ createTurnToolCancellationController,
13300
13901
  decideCompaction,
13301
13902
  decideSelfhostedRetry,
13302
13903
  decodeModalSnapshotId,
@@ -13353,11 +13954,13 @@ export {
13353
13954
  modelCallUsageTelemetry,
13354
13955
  modelResponseUsageFromResponse,
13355
13956
  modelResponseUsageFromSdkEvent,
13957
+ modelUsageTokenCountOrNull,
13356
13958
  negotiateCapabilities,
13357
13959
  negotiateSelfhostedCapabilities,
13358
13960
  neutralizeToolSearchItemsInSerializedRunState,
13359
13961
  nextDurableOpId,
13360
13962
  normalizeComputerCallsFilter,
13963
+ normalizeModelCallUsage,
13361
13964
  normalizeRunCredentialsResolution,
13362
13965
  normalizeSdkEvent,
13363
13966
  normalizeToolOutputForEvent,