@opengeni/runtime 0.13.4 → 0.13.10

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
@@ -175,7 +175,7 @@ import {
175
175
  withToolspaceTokenEnvironment,
176
176
  withToolspaceTokenSession,
177
177
  wrapCapabilityToolsForTurnCancellation
178
- } from "./chunk-JGUQSAG5.js";
178
+ } from "./chunk-RHB7ETON.js";
179
179
  import {
180
180
  getSkillLibraryEntry,
181
181
  isSkillLibraryEntryId,
@@ -186,7 +186,10 @@ import {
186
186
  MCP_MAX_CONCURRENT_SERVER_OPERATIONS,
187
187
  MCP_MAX_TOOL_DEFINITION_BYTES,
188
188
  MCP_MAX_TOOL_RESULT_BYTES,
189
+ MCP_MAX_TOOL_SEARCH_DESCRIPTION_BYTES,
189
190
  MCP_MAX_TOOL_SEARCH_DISCLOSURE_BYTES,
191
+ MCP_MAX_TOOL_SEARCH_SOURCES,
192
+ MCP_MAX_TOOL_SEARCH_SOURCE_LABEL_LENGTH,
190
193
  McpAggregateToolListBudget,
191
194
  assertMcpPayloadWithinBytes,
192
195
  assertMcpServerSelectionWithinBounds,
@@ -272,6 +275,7 @@ import {
272
275
  normalizeResourceMountPath,
273
276
  prefixedMcpToolName as sharedPrefixedMcpToolName,
274
277
  resourceMountPath,
278
+ redactSensitiveText as redactSensitiveText2,
275
279
  sessionEventMediaPreview,
276
280
  sessionEventMediaPreviewFromDataUrl,
277
281
  signDelegatedAccessToken,
@@ -6883,6 +6887,58 @@ var MAX_SEARCH_LIMIT = 20;
6883
6887
  function isCodexAppsFunctionTool(tool2) {
6884
6888
  return !!tool2 && typeof tool2 === "object" && tool2.type === "function" && typeof tool2.name === "string" && tool2.name.startsWith(CODEX_APPS_TOOL_PREFIX);
6885
6889
  }
6890
+ var MCP_TOOL_NAME_SEPARATOR = "__";
6891
+ var MANDATORY_FIRST_PARTY_MCP_SERVER_ID = "opengeni";
6892
+ var NO_MCP_SERVER_IDS = /* @__PURE__ */ new Set();
6893
+ function mcpServerIdForTool(tool2, mcpServerIds) {
6894
+ if (!tool2 || typeof tool2 !== "object") return null;
6895
+ if (tool2.type !== "function") return null;
6896
+ const name = tool2.name;
6897
+ if (typeof name !== "string") return null;
6898
+ let match = null;
6899
+ for (const serverId of mcpServerIds) {
6900
+ if (typeof serverId !== "string" || serverId.length === 0 || !name.startsWith(`${serverId}${MCP_TOOL_NAME_SEPARATOR}`)) {
6901
+ continue;
6902
+ }
6903
+ if (match === null || serverId.length > match.length) {
6904
+ match = serverId;
6905
+ }
6906
+ }
6907
+ return match;
6908
+ }
6909
+ function isSearchableMcpFunctionTool(tool2, mcpServerIds = NO_MCP_SERVER_IDS) {
6910
+ const serverId = mcpServerIdForTool(tool2, mcpServerIds);
6911
+ return serverId !== null && serverId !== MANDATORY_FIRST_PARTY_MCP_SERVER_ID;
6912
+ }
6913
+ function truncateUtf8(value, maxBytes) {
6914
+ if (Buffer.byteLength(value) <= maxBytes) return value;
6915
+ let output = "";
6916
+ for (const character of value) {
6917
+ const candidate = output + character;
6918
+ if (Buffer.byteLength(candidate) > maxBytes) break;
6919
+ output = candidate;
6920
+ }
6921
+ return output;
6922
+ }
6923
+ function searchSourceForTool(tool2, mcpServerIds) {
6924
+ const serverId = mcpServerIdForTool(tool2, mcpServerIds);
6925
+ return serverId && serverId !== MANDATORY_FIRST_PARTY_MCP_SERVER_ID ? truncateUtf8(serverId, MCP_MAX_TOOL_SEARCH_SOURCE_LABEL_LENGTH) : null;
6926
+ }
6927
+ function searchSourcesForTools(connectorNamespaces, tools, mcpServerIds) {
6928
+ const sources = /* @__PURE__ */ new Set();
6929
+ for (const source of connectorNamespaces) {
6930
+ if (typeof source !== "string" || source.length === 0) continue;
6931
+ if (sources.size >= MCP_MAX_TOOL_SEARCH_SOURCES) break;
6932
+ sources.add(truncateUtf8(source, MCP_MAX_TOOL_SEARCH_SOURCE_LABEL_LENGTH));
6933
+ }
6934
+ for (const tool2 of tools) {
6935
+ const source = isSearchableMcpFunctionTool(tool2, mcpServerIds) && !isCodexAppsFunctionTool(tool2) ? searchSourceForTool(tool2, mcpServerIds) : null;
6936
+ if (!source || sources.has(source)) continue;
6937
+ if (sources.size >= MCP_MAX_TOOL_SEARCH_SOURCES) break;
6938
+ sources.add(source);
6939
+ }
6940
+ return sources;
6941
+ }
6886
6942
  var STOPWORDS = /* @__PURE__ */ new Set([
6887
6943
  "a",
6888
6944
  "an",
@@ -6987,14 +7043,23 @@ function parseSearchArgs(raw) {
6987
7043
  }
6988
7044
  function renderSearchToolDescription(connectorNamespaces) {
6989
7045
  const base = `Search the user's connected app tools by capability. Describe in plain language WHAT you need to do (for example: "send an email", "create a calendar event") rather than guessing exact tool names. Returns the matching connector tools, which then become callable.`;
6990
- const sources = Array.from(connectorNamespaces).filter((n) => typeof n === "string" && n.length > 0).sort();
7046
+ const sources = Array.from(connectorNamespaces).filter((n) => typeof n === "string" && n.length > 0).slice(0, MCP_MAX_TOOL_SEARCH_SOURCES).map((source) => truncateUtf8(source, MCP_MAX_TOOL_SEARCH_SOURCE_LABEL_LENGTH)).filter(Boolean).sort();
6991
7047
  if (sources.length === 0) {
6992
7048
  return `${base}
6993
7049
  Connected sources: none currently available.`;
6994
7050
  }
6995
- return `${base}
6996
- You have access to tools from the following sources:
6997
- ${sources.map((s) => `- ${s}`).join("\n")}`;
7051
+ const header = `${base}
7052
+ You have access to tools from the following sources:`;
7053
+ const lines = [];
7054
+ for (const source of sources) {
7055
+ const candidate = `${header}${[...lines, `- ${source}`].map((line) => `
7056
+ ${line}`).join("")}`;
7057
+ if (Buffer.byteLength(candidate) > MCP_MAX_TOOL_SEARCH_DESCRIPTION_BYTES) break;
7058
+ lines.push(`- ${source}`);
7059
+ }
7060
+ return lines.length > 0 ? `${header}
7061
+ ${lines.join("\n")}` : `${base}
7062
+ Connected sources: none currently available.`;
6998
7063
  }
6999
7064
  var SEARCH_TOOL_PARAMETERS = {
7000
7065
  type: "object",
@@ -7008,8 +7073,10 @@ var SEARCH_TOOL_PARAMETERS = {
7008
7073
  required: ["query"],
7009
7074
  additionalProperties: false
7010
7075
  };
7011
- function codexToolSearchExecutor(args) {
7012
- const deferred = (args.availableTools ?? []).filter(isCodexAppsFunctionTool);
7076
+ function codexToolSearchExecutor(args, mcpServerIds) {
7077
+ const deferred = (args.availableTools ?? []).filter(
7078
+ (tool2) => isSearchableMcpFunctionTool(tool2, mcpServerIds)
7079
+ );
7013
7080
  if (deferred.length === 0) return [];
7014
7081
  const { query, limit } = parseSearchArgs(args.toolCall?.arguments);
7015
7082
  const ranked = bm25RankTools(deferred, query, limit);
@@ -7047,45 +7114,54 @@ function resolveSearchToolDescription(connectorNamespaces, freeze) {
7047
7114
  }
7048
7115
  return rendered;
7049
7116
  }
7050
- function buildCodexToolSearchToolFromDescription(description) {
7117
+ function buildCodexToolSearchToolFromDescription(description, mcpServerIds) {
7051
7118
  return toolSearchTool({
7052
7119
  execution: "client",
7053
7120
  description,
7054
7121
  parameters: SEARCH_TOOL_PARAMETERS,
7055
- execute: codexToolSearchExecutor
7122
+ execute: (args) => codexToolSearchExecutor(args, mcpServerIds)
7056
7123
  });
7057
7124
  }
7058
7125
  function isToolSearchTool(tool2) {
7059
7126
  const t = tool2;
7060
7127
  return !!t && (t.name === "tool_search" || t.providerData?.type === "tool_search");
7061
7128
  }
7062
- function applyCodexToolSearch(tools, connectorNamespaces = NO_NAMESPACES, descriptionFreeze) {
7129
+ function applyCodexToolSearch(tools, connectorNamespaces = NO_NAMESPACES, descriptionFreeze, mcpServerIds = NO_MCP_SERVER_IDS) {
7063
7130
  for (const tool2 of tools) {
7064
- if (isCodexAppsFunctionTool(tool2) && tool2.deferLoading !== true) {
7131
+ if (isSearchableMcpFunctionTool(tool2, mcpServerIds) && tool2.deferLoading !== true) {
7065
7132
  tool2.deferLoading = true;
7066
7133
  }
7067
7134
  }
7068
7135
  if (tools.some(isToolSearchTool)) {
7069
7136
  return tools;
7070
7137
  }
7071
- const description = resolveSearchToolDescription(connectorNamespaces, descriptionFreeze);
7072
- return [...tools, buildCodexToolSearchToolFromDescription(description)];
7138
+ const description = resolveSearchToolDescription(
7139
+ searchSourcesForTools(connectorNamespaces, tools, mcpServerIds),
7140
+ descriptionFreeze
7141
+ );
7142
+ return [...tools, buildCodexToolSearchToolFromDescription(description, mcpServerIds)];
7073
7143
  }
7074
- function installCodexToolSearch(agent, connectorNamespaces = NO_NAMESPACES) {
7075
- installCodexToolSearchWithFreeze(agent, connectorNamespaces, { value: null });
7144
+ function installCodexToolSearch(agent, connectorNamespaces = NO_NAMESPACES, mcpServerIds = NO_MCP_SERVER_IDS) {
7145
+ installCodexToolSearchWithFreeze(agent, connectorNamespaces, mcpServerIds, { value: null });
7076
7146
  }
7077
- function installCodexToolSearchWithFreeze(agent, connectorNamespaces, descriptionFreeze) {
7147
+ function installCodexToolSearchWithFreeze(agent, connectorNamespaces, mcpServerIds, descriptionFreeze) {
7078
7148
  const originalGetAllTools = agent.getAllTools.bind(agent);
7079
7149
  agent.getAllTools = async (runContext) => applyCodexToolSearch(
7080
7150
  await originalGetAllTools(runContext),
7081
7151
  connectorNamespaces,
7082
- descriptionFreeze
7152
+ descriptionFreeze,
7153
+ mcpServerIds
7083
7154
  );
7084
7155
  const originalClone = agent.clone?.bind(agent);
7085
7156
  if (originalClone) {
7086
7157
  const cloneWithToolSearch = (config) => {
7087
7158
  const cloned = originalClone(config);
7088
- installCodexToolSearchWithFreeze(cloned, connectorNamespaces, descriptionFreeze);
7159
+ installCodexToolSearchWithFreeze(
7160
+ cloned,
7161
+ connectorNamespaces,
7162
+ mcpServerIds,
7163
+ descriptionFreeze
7164
+ );
7089
7165
  return cloned;
7090
7166
  };
7091
7167
  agent.clone = cloneWithToolSearch;
@@ -7630,6 +7706,7 @@ import {
7630
7706
  PointerAction,
7631
7707
  PointerButton
7632
7708
  } from "@opengeni/agent-proto";
7709
+ import { redactSensitiveText } from "@opengeni/contracts";
7633
7710
  function requireBoundSession(capabilityType, session) {
7634
7711
  if (!session) {
7635
7712
  throw new ComputerUnavailableError(`capability "${capabilityType}" used before bind(session)`);
@@ -7783,7 +7860,7 @@ var SandboxComputer = class {
7783
7860
  const output = sandboxCommandOutput(result);
7784
7861
  if (sandboxCommandStillRunning(result)) {
7785
7862
  console.warn(
7786
- `[SandboxComputer] action command did not finish before the ${ACTION_YIELD_MS}ms yield window \u2014 proceeding to screenshot: ${cmd}`
7863
+ `[SandboxComputer] action command did not finish before the ${ACTION_YIELD_MS}ms yield window \u2014 proceeding to screenshot: ${redactSensitiveText(cmd)}`
7787
7864
  );
7788
7865
  return output;
7789
7866
  }
@@ -8040,7 +8117,9 @@ var NativeDesktopComputer = class {
8040
8117
  break;
8041
8118
  }
8042
8119
  }
8043
- const reason = lastError instanceof Error ? lastError.message : String(lastError);
8120
+ const reason = redactSensitiveText(
8121
+ lastError instanceof Error ? lastError.message : String(lastError)
8122
+ );
8044
8123
  console.warn(
8045
8124
  `[NativeDesktopComputer] screenshot failed after ${attempt} attempt(s): ${reason}`
8046
8125
  );
@@ -8750,6 +8829,8 @@ function rejectField(rejectedFields, field) {
8750
8829
  }
8751
8830
 
8752
8831
  // src/index.ts
8832
+ process.env.OPENAI_AGENTS_DONT_LOG_MODEL_DATA = "1";
8833
+ process.env.OPENAI_AGENTS_DONT_LOG_TOOL_DATA = "1";
8753
8834
  setSelfhostedApplyDiff(
8754
8835
  applyDiff
8755
8836
  );
@@ -9319,6 +9400,19 @@ function appendWorkspaceMemory(composed, workspaceMemory) {
9319
9400
  function appendToolspaceInstructions(composed, toolspaceAvailable) {
9320
9401
  return toolspaceAvailable ? `${composed} ${TOOLSPACE_PROGRAMMATIC_DIRECTIVE}` : composed;
9321
9402
  }
9403
+ var GIT_BINDING_DISCOVERY_DIRECTIVE = "This managed sandbox has multiple credential bindings for at least one Git provider. Native Git selects credentials from the repository remote. For gh, glab, or az, run inside the intended attached repository so its origin selects the binding; when running elsewhere, inspect $HOME/.opengeni/git-bindings.json and set OPENGENI_GIT_BINDING to the listed credentialBindingId. The inventory contains identifiers and repository routes, never credential values. A binding marked git_http_broker is Git-only, so use the configured provider MCP tools for provider API operations on it.";
9404
+ function appendGitCredentialBindingInstructions(composed, bindings, activeSandboxBackend) {
9405
+ if (activeSandboxBackend === "selfhosted" || !bindings?.length) {
9406
+ return composed;
9407
+ }
9408
+ const bindingsByProvider = /* @__PURE__ */ new Map();
9409
+ for (const binding of bindings) {
9410
+ const ids = bindingsByProvider.get(binding.provider) ?? /* @__PURE__ */ new Set();
9411
+ ids.add(binding.credentialBindingId);
9412
+ bindingsByProvider.set(binding.provider, ids);
9413
+ }
9414
+ return [...bindingsByProvider.values()].some((ids) => ids.size > 1) ? `${composed} ${GIT_BINDING_DISCOVERY_DIRECTIVE}` : composed;
9415
+ }
9322
9416
  function appendGenesisTitleDirective(instructions, genesisTitleHint) {
9323
9417
  return genesisTitleHint ? `${instructions} ${GENESIS_TITLE_DIRECTIVE}` : instructions;
9324
9418
  }
@@ -9333,7 +9427,7 @@ var agentActiveSandboxBackend = /* @__PURE__ */ new WeakMap();
9333
9427
  var agentRigSetup = /* @__PURE__ */ new WeakMap();
9334
9428
  var agentRigCredentialHooks = /* @__PURE__ */ new WeakMap();
9335
9429
  function mcpToolErrorOutput(error) {
9336
- const details = error instanceof Error ? error.message : String(error);
9430
+ const details = redactSensitiveText2(error instanceof Error ? error.message : String(error));
9337
9431
  return {
9338
9432
  isError: true,
9339
9433
  content: [
@@ -9395,12 +9489,14 @@ function buildOpenGeniAgent(settings, resources, options = {}) {
9395
9489
  // when a toolspace token was minted for this turn (feature enabled + a
9396
9490
  // per-turn seed — the mint gate, which includes selfhosted turns since
9397
9491
  // they now receive the token too) — appendToolspaceInstructions,
9398
- // 3. + workspace memory working set, ONLY when the workspace setting is on
9492
+ // 3. + managed-sandbox Git binding discovery, ONLY when one provider has
9493
+ // multiple credential bindings,
9494
+ // 4. + workspace memory working set, ONLY when the workspace setting is on
9399
9495
  // and the worker resolved a nonblank block — appendWorkspaceMemory,
9400
- // 4. + the per-session persona instructions (session-specific, so it
9496
+ // 5. + the per-session persona instructions (session-specific, so it
9401
9497
  // refines both the workspace persona and the substrate note),
9402
- // 5. + host context for this exact turn, when supplied,
9403
- // 6. + durable session-setting state (title present + child notification
9498
+ // 6. + host context for this exact turn, when supplied,
9499
+ // 7. + durable session-setting state (title present + child notification
9404
9500
  // mode), when supplied by the worker,
9405
9501
  // The genesis title directive is deliberately NOT part of this persistent
9406
9502
  // string. runAgentStream injects it into the first model call only.
@@ -9408,13 +9504,17 @@ function buildOpenGeniAgent(settings, resources, options = {}) {
9408
9504
  appendTurnInstructions(
9409
9505
  appendSessionInstructions(
9410
9506
  appendWorkspaceMemory(
9411
- appendToolspaceInstructions(
9412
- composeAgentInstructions(
9413
- options.instructionsTemplate ?? settings.agentInstructionsTemplate,
9414
- options.workspaceEnvironment,
9415
- options.rig
9507
+ appendGitCredentialBindingInstructions(
9508
+ appendToolspaceInstructions(
9509
+ composeAgentInstructions(
9510
+ options.instructionsTemplate ?? settings.agentInstructionsTemplate,
9511
+ options.workspaceEnvironment,
9512
+ options.rig
9513
+ ),
9514
+ settings.toolspaceEnabled && Boolean(options.toolspaceTokenSeed)
9416
9515
  ),
9417
- settings.toolspaceEnabled && Boolean(options.toolspaceTokenSeed)
9516
+ options.gitCredentialBindings,
9517
+ options.activeSandboxBackend
9418
9518
  ),
9419
9519
  options.workspaceMemory
9420
9520
  ),
@@ -9533,9 +9633,15 @@ function buildOpenGeniAgent(settings, resources, options = {}) {
9533
9633
  }
9534
9634
  function maybeInstallCodexToolSearch(agent, settings, options) {
9535
9635
  if (settings.codexToolSearchEnabled && options.structuredToolTransport === false) {
9636
+ const mcpServers = options.mcpServers ?? [];
9637
+ for (const server of mcpServers) {
9638
+ if (server.name === "opengeni") continue;
9639
+ server.deferModelToolSchemaAccounting?.();
9640
+ }
9536
9641
  installCodexToolSearch(
9537
9642
  agent,
9538
- options.codexConnectorNamespaces ?? /* @__PURE__ */ new Set()
9643
+ options.codexConnectorNamespaces ?? /* @__PURE__ */ new Set(),
9644
+ new Set(mcpServers.map((server) => server.name))
9539
9645
  );
9540
9646
  }
9541
9647
  }
@@ -9754,6 +9860,10 @@ async function prepareAgentTools(settings, tools, options = {}) {
9754
9860
  url,
9755
9861
  name: config.name ?? config.id,
9756
9862
  cacheToolsList: config.cacheToolsList,
9863
+ // The upstream transport logger receives raw thrown errors, whose
9864
+ // messages may contain response bodies, URLs, headers, or echoed
9865
+ // credentials. Keep its diagnostic surface structural only.
9866
+ logger: safeMcpTransportLogger(config.id),
9757
9867
  // codex_apps returns connector tools with empty `outputSchema: {}` that the
9758
9868
  // MCP SDK's strict Tool schema rejects (fails the turn during tools/list);
9759
9869
  // sanitize the response on the wire before validation. The namespace Set
@@ -9813,7 +9923,7 @@ async function prepareAgentTools(settings, tools, options = {}) {
9813
9923
  const error = connectedBestEffort.errors.get(failed);
9814
9924
  console.warn(
9815
9925
  `[mcp] optional server "${failed.name}" failed to connect/list tools; skipping it for this turn`,
9816
- error instanceof Error ? error.message : error
9926
+ safeMcpErrorFields(error)
9817
9927
  );
9818
9928
  }
9819
9929
  }
@@ -10093,6 +10203,41 @@ function safeMcpErrorFields(error) {
10093
10203
  }
10094
10204
  return { errorClass, status };
10095
10205
  }
10206
+ function safeMcpTransportError(error) {
10207
+ const fields = safeMcpErrorFields(error);
10208
+ const safeError = new Error(
10209
+ `MCP transport operation failed (${mcpErrorReason(fields)})`
10210
+ );
10211
+ safeError.name = "McpTransportError";
10212
+ if (fields.status !== void 0) {
10213
+ safeError.status = fields.status;
10214
+ safeError.code = fields.status;
10215
+ }
10216
+ return safeError;
10217
+ }
10218
+ function safeMcpTransportLogger(serverId) {
10219
+ const logFailure = (_message, ...args) => {
10220
+ let error;
10221
+ for (let index = args.length - 1; index >= 0; index -= 1) {
10222
+ if (args[index] instanceof Error) {
10223
+ error = args[index];
10224
+ break;
10225
+ }
10226
+ }
10227
+ console.warn("[mcp] transport operation failed", {
10228
+ serverId,
10229
+ ...safeMcpErrorFields(error)
10230
+ });
10231
+ };
10232
+ return {
10233
+ namespace: "opengeni:mcp-transport",
10234
+ debug: () => void 0,
10235
+ error: logFailure,
10236
+ warn: logFailure,
10237
+ dontLogModelData: true,
10238
+ dontLogToolData: true
10239
+ };
10240
+ }
10096
10241
  function mcpErrorReason(fields) {
10097
10242
  return fields.status === void 0 ? fields.errorClass : `${fields.errorClass} ${fields.status}`;
10098
10243
  }
@@ -10271,8 +10416,11 @@ var PrefixedMcpServer = class {
10271
10416
  bestEffort;
10272
10417
  loggedListToolsFailure = false;
10273
10418
  listedToolSchemaTokens = 0;
10419
+ modelToolSchemaAccountingDeferred = false;
10274
10420
  connect() {
10275
- return this.inner.connect();
10421
+ return this.inner.connect().catch((error) => {
10422
+ throw safeMcpTransportError(error);
10423
+ });
10276
10424
  }
10277
10425
  close() {
10278
10426
  this.releaseAggregateBudget();
@@ -10310,7 +10458,15 @@ var PrefixedMcpServer = class {
10310
10458
  }
10311
10459
  /** Latest exact tools/list projection used to build the model request. */
10312
10460
  modelToolSchemaTokens() {
10313
- return this.listedToolSchemaTokens;
10461
+ return this.modelToolSchemaAccountingDeferred ? 0 : this.listedToolSchemaTokens;
10462
+ }
10463
+ /**
10464
+ * Keep model-input accounting aligned with Codex `defer_loading`: the full
10465
+ * schema is not provider context until a tool_search_output discloses it.
10466
+ * Instances are turn-local, so this cannot leak into a non-Codex turn.
10467
+ */
10468
+ deferModelToolSchemaAccounting() {
10469
+ this.modelToolSchemaAccountingDeferred = true;
10314
10470
  }
10315
10471
  async callTool(toolName, args, meta) {
10316
10472
  const unprefixed = this.unprefixToolName(toolName);
@@ -11641,6 +11797,7 @@ function sandboxFileDownloadCommand(download, targetPath) {
11641
11797
  const targetDir = posixPath.dirname(targetPath);
11642
11798
  const tmpPath = `${targetPath}.opengeni-download-$$`;
11643
11799
  return [
11800
+ "set +x",
11644
11801
  "set -eu",
11645
11802
  `mkdir -p -- ${shellQuote(targetDir)}`,
11646
11803
  `if [ ! -f ${shellQuote(targetPath)} ]; then`,
@@ -11896,10 +12053,51 @@ function runtimeGitBindingDescriptors(resources) {
11896
12053
  protocol: url.protocol.replace(/:$/, "").toLowerCase(),
11897
12054
  host: url.host.toLowerCase(),
11898
12055
  path,
11899
- uri: resource.uri
12056
+ uri: resource.uri,
12057
+ mountPath: `/workspace/${resourceMountPath(resource)}`
11900
12058
  };
11901
12059
  });
11902
12060
  }
12061
+ function gitCredentialBindingInventoryCommandLines(resources, bindings) {
12062
+ const bindingTransports = new Map(
12063
+ bindings.map((binding) => [
12064
+ gitCredentialBindingKey(binding.provider, binding.credentialBindingId),
12065
+ binding.transport?.kind === "http_broker" ? "git_http_broker" : "direct_token"
12066
+ ])
12067
+ );
12068
+ const entries = /* @__PURE__ */ new Map();
12069
+ for (const descriptor of runtimeGitBindingDescriptors(resources)) {
12070
+ const key = gitCredentialBindingKey(descriptor.provider, descriptor.credentialBindingId);
12071
+ const transport = bindingTransports.get(key);
12072
+ if (!transport) continue;
12073
+ const entry = entries.get(key) ?? {
12074
+ credentialBindingId: descriptor.credentialBindingId,
12075
+ provider: descriptor.provider,
12076
+ transport,
12077
+ repositories: []
12078
+ };
12079
+ entry.repositories.push({ uri: descriptor.uri, mountPath: descriptor.mountPath });
12080
+ entries.set(key, entry);
12081
+ }
12082
+ const inventory = `${JSON.stringify(
12083
+ {
12084
+ version: 1,
12085
+ bindings: [...entries.values()]
12086
+ },
12087
+ null,
12088
+ 2
12089
+ )}
12090
+ `;
12091
+ return [
12092
+ 'git_binding_inventory="${OPENGENI_GIT_BINDINGS_FILE:-$HOME/.opengeni/git-bindings.json}"',
12093
+ 'mkdir -p "$(dirname "$git_binding_inventory")"',
12094
+ 'inventory_umask="$(umask)"',
12095
+ "umask 077",
12096
+ `printf '%s' ${shellQuote(inventory)} > "$git_binding_inventory.tmp.$$"`,
12097
+ 'mv -f "$git_binding_inventory.tmp.$$" "$git_binding_inventory"',
12098
+ 'umask "$inventory_umask"'
12099
+ ];
12100
+ }
11903
12101
  function gitCredentialBindingKey(provider, credentialBindingId) {
11904
12102
  return `${provider}\0${credentialBindingId}`;
11905
12103
  }
@@ -12170,6 +12368,7 @@ function gitCredentialHelperCommandLines(resources = [], bindings = []) {
12170
12368
  const brokerOnlyProviders = [...providerBindingKinds.entries()].filter(([, counts]) => counts.brokered > 0 && counts.direct === 0).map(([provider]) => provider);
12171
12369
  return [
12172
12370
  ...gitCredentialTokenWriterCommandLines(bindings),
12371
+ ...gitCredentialBindingInventoryCommandLines(resources, bindings),
12173
12372
  // Provision git/provider-CLI helpers at SETUP (runtime) before any clone
12174
12373
  // runs. Renewal updates only token files and deliberately leaves these
12175
12374
  // repository-specific host mappings intact.
@@ -12332,7 +12531,7 @@ function gitCredentialHelperCommandLines(resources = [], bindings = []) {
12332
12531
  ` *" $provider "*) printf '%s\\n' "$tool provider API authentication is host-brokered for this session; use the configured provider MCP tools" >&2; exit 2 ;;`,
12333
12532
  " esac",
12334
12533
  ' case " $multi_binding_providers " in',
12335
- ` *" $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 ;;`,
12534
+ ` *" $provider "*) printf '%s\\n' "Unable to select one of multiple $provider credentials; run inside an attached repository, or inspect \${OPENGENI_GIT_BINDINGS_FILE:-$HOME/.opengeni/git-bindings.json} and set OPENGENI_GIT_BINDING" >&2; exit 2 ;;`,
12336
12535
  " esac",
12337
12536
  ' case "$provider" in',
12338
12537
  ' github) token_file="${OPENGENI_GIT_TOKEN_FILE:-$HOME/.opengeni/git-token}" ;;',
@@ -12377,6 +12576,7 @@ function gitProviderTokenRefreshCommand(seeds) {
12377
12576
  return "";
12378
12577
  }
12379
12578
  return [
12579
+ "set +x",
12380
12580
  seedPrefix,
12381
12581
  "set -eu",
12382
12582
  'export HOME="${HOME:-/workspace}"',
@@ -12387,6 +12587,7 @@ function gitCredentialBindingTokenRefreshCommand(bindings) {
12387
12587
  const seedPrefix = gitCredentialBindingSeedExportPrefix(bindings);
12388
12588
  if (!seedPrefix) return "";
12389
12589
  return [
12590
+ "set +x",
12390
12591
  seedPrefix,
12391
12592
  "set -eu",
12392
12593
  'export HOME="${HOME:-/workspace}"',
@@ -12428,6 +12629,7 @@ async function refreshGitCredentialBindingTokenFiles(session, bindings, options
12428
12629
  function repositoryCloneCommand(resources, bindings = []) {
12429
12630
  assertUniqueResourceMountPaths(resources);
12430
12631
  const commands = [
12632
+ "set +x",
12431
12633
  "set -eu",
12432
12634
  'export HOME="${HOME:-/workspace}"',
12433
12635
  'export GIT_TERMINAL_PROMPT="${GIT_TERMINAL_PROMPT:-0}"',
@@ -12534,6 +12736,7 @@ function repositoryCloneCommand(resources, bindings = []) {
12534
12736
  }
12535
12737
  function toolspaceTokenSeedCommand(options = {}) {
12536
12738
  return [
12739
+ "set +x",
12537
12740
  "set -eu",
12538
12741
  'export HOME="${HOME:-/workspace}"',
12539
12742
  'if [ -n "${OPENGENI_TOOLSPACE_TOKEN_SEED:-}" ]; then',
@@ -12555,7 +12758,8 @@ async function runToolspaceTokenSeedHook(session, context) {
12555
12758
  if (!context.toolspaceTokenSeed) {
12556
12759
  return;
12557
12760
  }
12558
- const command = `export OPENGENI_TOOLSPACE_TOKEN_SEED=${shellQuote(context.toolspaceTokenSeed)}
12761
+ const command = `set +x
12762
+ export OPENGENI_TOOLSPACE_TOKEN_SEED=${shellQuote(context.toolspaceTokenSeed)}
12559
12763
  ${toolspaceTokenSeedCommand(
12560
12764
  {
12561
12765
  ...context.toolspaceTokenFile ? { tokenFile: context.toolspaceTokenFile } : {},
@@ -12576,7 +12780,8 @@ ${toolspaceTokenSeedCommand(
12576
12780
  assertSandboxCommandSucceeded(result, "Toolspace token seed hook");
12577
12781
  }
12578
12782
  async function refreshToolspaceTokenFile(session, token, options = {}) {
12579
- const command = `export OPENGENI_TOOLSPACE_TOKEN_SEED=${shellQuote(token)}
12783
+ const command = `set +x
12784
+ export OPENGENI_TOOLSPACE_TOKEN_SEED=${shellQuote(token)}
12580
12785
  ${toolspaceTokenSeedCommand(
12581
12786
  {
12582
12787
  ...options.tokenFile ? { tokenFile: options.tokenFile } : {},
@@ -12724,7 +12929,8 @@ async function runRepositoryCloneHook(session, resources, context = { environmen
12724
12929
  gitCredentialBindingSeedExportPrefix(gitCredentialBindings)
12725
12930
  ].filter(Boolean).join("\n");
12726
12931
  const cloneCommand = repositoryCloneCommand(resources, gitCredentialBindings);
12727
- const command = seedPrefix ? `${seedPrefix}
12932
+ const command = seedPrefix ? `set +x
12933
+ ${seedPrefix}
12728
12934
  ${cloneCommand}` : cloneCommand;
12729
12935
  const result = await runSandboxLifecycleCommand(
12730
12936
  session,
@@ -12755,6 +12961,7 @@ ${cloneCommand}` : cloneCommand;
12755
12961
  }
12756
12962
  function azureCliLoginCommand() {
12757
12963
  return [
12964
+ "set +x",
12758
12965
  'export HOME="${HOME:-/workspace}"',
12759
12966
  'mkdir -p "$HOME/.azure"',
12760
12967
  'CLIENT_ID="${AZURE_CLIENT_ID:-${ARM_CLIENT_ID:-}}"',
@@ -13235,6 +13442,7 @@ export {
13235
13442
  agentErrorToControlError,
13236
13443
  agentsErrorRunState,
13237
13444
  appendGenesisTitleDirective,
13445
+ appendGitCredentialBindingInstructions,
13238
13446
  appendPersistentSessionSettings,
13239
13447
  appendSessionInstructions,
13240
13448
  appendToolspaceInstructions,