@opengeni/runtime 3.0.1 → 4.0.0-canary.35585322809001

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.
Files changed (99) hide show
  1. package/dist/assets/codemode-client.json +1 -0
  2. package/dist/chunk-4A4XTKKX.js +1 -0
  3. package/dist/{chunk-RIJ6DCYS.js → chunk-HCGLD7NH.js} +9817 -9494
  4. package/dist/chunk-HCGLD7NH.js.map +1 -0
  5. package/dist/{chunk-FHGLYLBL.js → chunk-SPT3PLMX.js} +1 -87
  6. package/dist/chunk-SPT3PLMX.js.map +1 -0
  7. package/dist/{chunk-GB763TSV.js → chunk-TE27J65Q.js} +1 -1
  8. package/dist/chunk-TE27J65Q.js.map +1 -0
  9. package/dist/{chunk-46BFUGFL.js → chunk-WIHICNWS.js} +1660 -388
  10. package/dist/chunk-WIHICNWS.js.map +1 -0
  11. package/dist/{chunk-UU6EJR52.js → chunk-YU4VRTR4.js} +2 -2
  12. package/dist/chunk-YU4VRTR4.js.map +1 -0
  13. package/dist/chunk-ZG2YH25H.js +94 -0
  14. package/dist/chunk-ZG2YH25H.js.map +1 -0
  15. package/dist/codex-tool-search.d.ts +5 -4
  16. package/dist/context-compaction.d.ts +19 -0
  17. package/dist/gmail-rest-mcp.js +1 -1
  18. package/dist/index.d.ts +30 -22
  19. package/dist/index.js +25 -5
  20. package/dist/lazy-manifest-OEYEHNVY.js +45 -0
  21. package/dist/lazy-manifest-OEYEHNVY.js.map +1 -0
  22. package/dist/lazy-manifest.d.ts +6 -0
  23. package/dist/lazy-tool-transport.d.ts +3 -2
  24. package/dist/{localSources-PVFCAPN2.js → localSources-TWHTEKGO.js} +3 -2
  25. package/dist/localSources-TWHTEKGO.js.map +1 -0
  26. package/dist/metrics.d.ts +6 -0
  27. package/dist/operational-instructions.d.ts +1 -1
  28. package/dist/prepared-compaction-request.d.ts +8 -0
  29. package/dist/provider-history-adapter.d.ts +2 -2
  30. package/dist/runtime-skills.d.ts +3 -10
  31. package/dist/sandbox/codemode-client.d.ts +17 -0
  32. package/dist/sandbox/codemode-token.d.ts +3 -3
  33. package/dist/sandbox/index.d.ts +2 -1
  34. package/dist/sandbox/index.js +8 -2
  35. package/dist/sandbox/materialization-verification-error.d.ts +27 -0
  36. package/dist/sandbox/provider-command-session.d.ts +44 -2
  37. package/dist/sandbox/providers/modal-command-control.d.ts +32 -29
  38. package/dist/sandbox/providers/modal-command-router-wire.d.ts +49 -0
  39. package/dist/sandbox/providers/modal-command-session.d.ts +2 -1
  40. package/dist/sandbox/providers/modal-legacy-command-control.d.ts +50 -0
  41. package/dist/sandbox/providers/modal-materialization-verification.d.ts +7 -0
  42. package/dist/sandbox/providers/modal.d.ts +0 -8
  43. package/dist/sandbox/routing/routing-session.d.ts +21 -0
  44. package/dist/sandbox/turn-tool-cancellation.d.ts +1 -0
  45. package/dist/skill-library.d.ts +0 -6
  46. package/dist/skill-library.js +1 -1
  47. package/dist/workspace-tool-gateway.js +7 -5
  48. package/package.json +13 -11
  49. package/src/bundled_artifact_skills/opengeni-documents/SKILL.md +19 -0
  50. package/src/bundled_artifact_skills/opengeni-documents/references/api.md +22 -0
  51. package/src/bundled_default_skills/opengeni-client/SKILL.md +349 -0
  52. package/src/bundled_default_skills/opengeni-client/agents/openai.yaml +4 -0
  53. package/src/bundled_default_skills/opengeni-client/references/api-workflows.md +256 -0
  54. package/src/bundled_default_skills/opengeni-client/references/compatibility-and-troubleshooting.md +84 -0
  55. package/src/bundled_default_skills/opengeni-client/references/customer-skill-template.md +96 -0
  56. package/src/bundled_default_skills/opengeni-client/references/data-tools-and-credentials.md +99 -0
  57. package/src/bundled_default_skills/opengeni-client/references/discovery-and-autonomy.md +81 -0
  58. package/src/bundled_default_skills/opengeni-client/references/external-users-and-connect.md +457 -0
  59. package/src/bundled_default_skills/opengeni-client/references/isolation-and-authorization.md +77 -0
  60. package/src/bundled_default_skills/opengeni-client/references/product-integration-shapes.md +232 -0
  61. package/src/bundled_default_skills/opengeni-client/references/product-shapes-and-ui.md +116 -0
  62. package/src/bundled_default_skills/opengeni-client/references/runtime-profile-and-verification.md +157 -0
  63. package/src/bundled_default_skills/opengeni-help/SKILL.md +101 -0
  64. package/src/bundled_management_skills/opengeni-skills/SKILL.md +33 -3
  65. package/src/codex-tool-search.ts +13 -5
  66. package/src/context-compaction.ts +87 -4
  67. package/src/gmail-rest-mcp.ts +1 -2
  68. package/src/index.ts +363 -131
  69. package/src/lazy-manifest.ts +55 -0
  70. package/src/lazy-tool-transport.ts +15 -2
  71. package/src/metrics.ts +6 -0
  72. package/src/model-request-capture.ts +3 -0
  73. package/src/operational-instructions.ts +11 -2
  74. package/src/prepared-compaction-request.ts +51 -0
  75. package/src/provider-history-adapter.ts +12 -3
  76. package/src/runtime-skills.ts +3 -17
  77. package/src/sandbox/codemode-client.ts +204 -0
  78. package/src/sandbox/codemode-token.ts +7 -2
  79. package/src/sandbox/index.ts +7 -0
  80. package/src/sandbox/materialization-verification-error.ts +65 -0
  81. package/src/sandbox/provider-command-session.ts +70 -2
  82. package/src/sandbox/providers/modal-command-control.ts +386 -242
  83. package/src/sandbox/providers/modal-command-router-wire.ts +248 -0
  84. package/src/sandbox/providers/modal-command-session.ts +177 -4
  85. package/src/sandbox/providers/modal-legacy-command-control.ts +351 -0
  86. package/src/sandbox/providers/modal-materialization-verification.ts +122 -0
  87. package/src/sandbox/providers/modal.ts +1 -9
  88. package/src/sandbox/routing/routing-session.ts +417 -79
  89. package/src/sandbox/turn-tool-cancellation.ts +38 -2
  90. package/src/skill-catalog.ts +1 -0
  91. package/src/skill-library.ts +0 -6
  92. package/src/workspace-governance.ts +0 -1
  93. package/src/workspace-skills.ts +114 -74
  94. package/dist/chunk-46BFUGFL.js.map +0 -1
  95. package/dist/chunk-FHGLYLBL.js.map +0 -1
  96. package/dist/chunk-GB763TSV.js.map +0 -1
  97. package/dist/chunk-RIJ6DCYS.js.map +0 -1
  98. package/dist/chunk-UU6EJR52.js.map +0 -1
  99. /package/dist/{localSources-PVFCAPN2.js.map → chunk-4A4XTKKX.js.map} +0 -0
package/src/index.ts CHANGED
@@ -1,3 +1,8 @@
1
+ import {
2
+ withPreparedCompactionRequest,
3
+ deferCompactionToModelBoundary,
4
+ } from "./prepared-compaction-request";
5
+ export { preparedCompactionRequest, queuePreparedCompaction } from "./prepared-compaction-request";
1
6
  import type { ModelProviderApi, ResolvedModelProvider, Settings } from "@opengeni/config";
2
7
  import { executeCommandReadWithRefresh } from "./command-read-refresh";
3
8
  import {
@@ -233,6 +238,7 @@ import {
233
238
  CODEX_APPS_MCP_SERVER_ID,
234
239
  CODEX_APPS_MCP_URL,
235
240
  CODEX_ORIGINATOR,
241
+ classifyCodexEncryptedArtifactRejection,
236
242
  codexAppsSanitizingFetch,
237
243
  } from "@opengeni/codex";
238
244
  import { AsyncLocalStorage } from "node:async_hooks";
@@ -275,6 +281,11 @@ import {
275
281
  sandboxCommandStdout,
276
282
  } from "./sandbox/command-result";
277
283
  import { shellCodemodePath } from "./sandbox/codemode-token";
284
+ import {
285
+ installManagedCodemodeClient,
286
+ loadManagedCodemodeClient,
287
+ managedCodemodeClientDirectory,
288
+ } from "./sandbox/codemode-client";
278
289
  import { InputWaitYield, type InputWaitYieldStream } from "./input-wait-yield";
279
290
  export { InputWaitYield } from "./input-wait-yield";
280
291
  import {
@@ -319,7 +330,6 @@ export {
319
330
  type EffectiveSkillSelection,
320
331
  type InstalledSkillActivation,
321
332
  type NativeToolSkillSet,
322
- type PackSkillActivation,
323
333
  type RuntimeSkillActivation,
324
334
  type RuntimeSkillArtifact,
325
335
  type RuntimeSkillArtifactFile,
@@ -338,6 +348,7 @@ import {
338
348
  import {
339
349
  ModelRequestCaptureModel,
340
350
  ModelRequestCaptureProvider,
351
+ notifyModelRequestCapture,
341
352
  withModelRequestCapture,
342
353
  type ModelRequestCapture,
343
354
  nextModelContextCaptureIndex,
@@ -598,6 +609,10 @@ export {
598
609
  CompactionNeededError,
599
610
  CompactionProviderResponseError,
600
611
  EmptyCompactionSummaryError,
612
+ compactionProviderRejection,
613
+ compactionProviderRejectionFromDiagnostics,
614
+ describeCompactionProviderRejection,
615
+ type CompactionProviderRejection,
601
616
  buildCompactionPromptInput,
602
617
  buildCompactionReplacementHistory,
603
618
  compactionReplacementFingerprint,
@@ -1258,10 +1273,9 @@ function serializeToolForRemoteCompaction(tool: Tool): SerializedTool | null {
1258
1273
  * exactly one `{ type: "compaction", encrypted_content }` output item.
1259
1274
  * Must run inside Codex ALS with `remote_compaction_v2` beta + turn metadata.
1260
1275
  *
1261
- * Prompt-cache critical: `systemInstructions` and `tools` must match the
1262
- * ordinary agent turn prefix (Codex CLI sends `base_instructions` +
1263
- * `model_visible_specs` on the compact call). An empty instructions string
1264
- * busts the shared tools→instructions prefix and is rejected here.
1276
+ * Reuse the complete prepared ordinary request, after sandbox and lazy-tool
1277
+ * preparation. Never reconstruct its prefix from Agent configuration. Empty
1278
+ * instructions are rejected.
1265
1279
  *
1266
1280
  * Tools are schema context only. This is a single `_fetchResponse` (no tool
1267
1281
  * loop), and extract still requires exactly one compaction item, so a
@@ -1274,54 +1288,44 @@ export async function requestRemoteCompactionV2(
1274
1288
  client: OpenAI;
1275
1289
  provider?: ResolvedModelProvider;
1276
1290
  model: string;
1277
- /**
1278
- * Exact agent system instructions for this session/turn. Required and
1279
- * non-blank — must match the prior ordinary model call for cache prefix.
1280
- */
1281
- systemInstructions: string;
1282
- promptCacheKey?: string;
1283
- /** Model-visible tool schemas for the compact request (CLI parity). */
1284
- tools?: readonly SerializedTool[];
1291
+ preparedRequest: Omit<ModelRequest, "input">;
1292
+ captureAgent?: object;
1293
+ signal?: AbortSignal | undefined;
1285
1294
  onUsage?: (usage: ModelResponseUsage) => void | Promise<void>;
1286
1295
  },
1287
1296
  ): Promise<Record<string, unknown>> {
1288
- // Match Agents SDK `normalizeInstructions`: reject blank after trim, but send
1289
- // the original bytes. Trimming here would diverge from ordinary turns that
1290
- // keep leading/trailing whitespace and bust the tools→instructions prefix.
1291
- if (options.systemInstructions.trim() === "") {
1297
+ const { signal: _priorSignal, ...prefix } = options.preparedRequest;
1298
+ if (!prefix.systemInstructions?.trim()) {
1292
1299
  throw new EmptyCompactionSummaryError({
1293
1300
  stage: "remote_v2_instructions",
1294
1301
  reason: "empty_system_instructions",
1295
1302
  });
1296
1303
  }
1297
- const systemInstructions = options.systemInstructions;
1298
- const promptInput = buildRemoteCompactionV2PromptInput(input);
1299
- const tools = options.tools ? [...options.tools] : [];
1304
+ // Reuse the complete prepared request. New SDK/model settings flow through
1305
+ // automatically; only history and the transient compaction marker differ.
1300
1306
  const request: ModelRequest = {
1301
- systemInstructions,
1302
- input: promptInput as AgentInputItem[],
1303
- modelSettings: {
1304
- // Azure rejects store:false; Codex transport enforces store:false itself.
1305
- ...(settings.openaiProvider === "azure" ? {} : { store: false }),
1306
- ...(options.promptCacheKey
1307
- ? { providerData: { prompt_cache_key: options.promptCacheKey } }
1308
- : {}),
1309
- },
1310
- tools,
1311
- toolsExplicitlyProvided: true,
1312
- outputType: "text",
1313
- handoffs: [],
1307
+ ...prefix,
1308
+ input: buildRemoteCompactionV2PromptInput(input) as AgentInputItem[],
1309
+ // This call outlives the SDK Runner trace. Tracing is client-side metadata,
1310
+ // not part of the provider request or cache prefix.
1314
1311
  tracing: false,
1312
+ // The stopped inference stream may have aborted its per-call signal.
1313
+ // Compaction uses the still-active turn cancellation signal instead.
1314
+ ...(options.signal ? { signal: options.signal } : {}),
1315
1315
  };
1316
1316
  let response: unknown;
1317
1317
  try {
1318
1318
  const provider = options.provider ?? configuredProviders(settings)[0];
1319
1319
  if (!provider) throw new Error("Built-in model provider is unavailable");
1320
- response = await new CompactionResponsesModel(
1321
- options.client,
1322
- options.model,
1323
- provider,
1324
- ).fetchResponse(request);
1320
+ response = await withModelRequestCapture(
1321
+ options.captureAgent ? agentModelContextCaptures.get(options.captureAgent) : undefined,
1322
+ async () => {
1323
+ void notifyModelRequestCapture(request);
1324
+ return new CompactionResponsesModel(options.client, options.model, provider).fetchResponse(
1325
+ request,
1326
+ );
1327
+ },
1328
+ );
1325
1329
  } catch (error) {
1326
1330
  throw new CompactionProviderResponseError(compactionProviderFailureDiagnostics(error), error);
1327
1331
  }
@@ -1366,8 +1370,13 @@ export function compactionProviderFailureDiagnostics(error: unknown): Record<str
1366
1370
  let code: string | null = null;
1367
1371
  let type: string | null = null;
1368
1372
  let requestId: string | null = null;
1373
+ let param: string | null = null;
1369
1374
  let eventType: string | null = null;
1370
- let rejectionReason: "missing_required_reasoning_item" | null = null;
1375
+ let rejectionReason: CompactionRejectionReason | null = classifyCodexEncryptedArtifactRejection(
1376
+ error,
1377
+ )
1378
+ ? "encrypted_content_rejected"
1379
+ : null;
1371
1380
  const seen = new Set<object>();
1372
1381
  for (let depth = 0; depth < 6 && current && typeof current === "object"; depth += 1) {
1373
1382
  if (seen.has(current)) break;
@@ -1377,6 +1386,11 @@ export function compactionProviderFailureDiagnostics(error: unknown): Record<str
1377
1386
  if (!errorName && current instanceof Error) {
1378
1387
  errorName = boundCompactionDiagnosticField(current.name);
1379
1388
  }
1389
+ // `error.param` names the rejected request field (an identifier path such
1390
+ // as `input[3].encrypted_content`), never conversation content.
1391
+ if (param === null && typeof record.param === "string" && record.param.length > 0) {
1392
+ param = boundCompactionDiagnosticField(record.param);
1393
+ }
1380
1394
  if (
1381
1395
  httpStatus === null &&
1382
1396
  typeof record.status === "number" &&
@@ -1406,7 +1420,10 @@ export function compactionProviderFailureDiagnostics(error: unknown): Record<str
1406
1420
  eventType = boundCompactionDiagnosticField(directEventType);
1407
1421
  }
1408
1422
  if (requestId === null) {
1409
- const directRequestId = record.request_id ?? record.requestId ?? record._request_id;
1423
+ // `requestID` is the OpenAI SDK's APIError property; the others are
1424
+ // provider-body and legacy spellings.
1425
+ const directRequestId =
1426
+ record.request_id ?? record.requestId ?? record.requestID ?? record._request_id;
1410
1427
  if (typeof directRequestId === "string") {
1411
1428
  requestId = boundCompactionDiagnosticField(directRequestId);
1412
1429
  }
@@ -1431,6 +1448,9 @@ export function compactionProviderFailureDiagnostics(error: unknown): Record<str
1431
1448
  if (type === null && typeof nested.type === "string") {
1432
1449
  type = boundCompactionDiagnosticField(nested.type);
1433
1450
  }
1451
+ if (param === null && typeof nested.param === "string" && nested.param.length > 0) {
1452
+ param = boundCompactionDiagnosticField(nested.param);
1453
+ }
1434
1454
  const nestedResponseStatus = nested.response_status ?? nested.responseStatus;
1435
1455
  if (responseStatus === null && typeof nestedResponseStatus === "string") {
1436
1456
  responseStatus = boundCompactionDiagnosticField(nestedResponseStatus);
@@ -1453,12 +1473,21 @@ export function compactionProviderFailureDiagnostics(error: unknown): Record<str
1453
1473
  responseId,
1454
1474
  code,
1455
1475
  type,
1476
+ param,
1456
1477
  requestId,
1457
1478
  ...(eventType ? { eventType } : {}),
1458
1479
  ...(rejectionReason ? { rejectionReason } : {}),
1459
1480
  };
1460
1481
  }
1461
1482
 
1483
+ /**
1484
+ * Closed provider rejection families the worker reacts to. Anything else is a
1485
+ * bounded status/code/param record with no classification.
1486
+ */
1487
+ export type CompactionRejectionReason =
1488
+ | "missing_required_reasoning_item"
1489
+ | "encrypted_content_rejected";
1490
+
1462
1491
  function compactionRejectionReason(
1463
1492
  record: Record<string, unknown>,
1464
1493
  ): "missing_required_reasoning_item" | null {
@@ -1978,15 +2007,12 @@ export type BuildAgentOptions = {
1978
2007
  // timeline message. Omitted ⇒ the composed instructions are byte-identical to
1979
2008
  // a workspace-only persona.
1980
2009
  sessionInstructions?: string;
1981
- /**
1982
- * Exact Skill activations admitted for this turn. Optional/domain Skills
1983
- * enter only through an explicit installation, Pack owner, or session
1984
- * selection; native tool-bound Skills are derived separately from the exact
1985
- * executable tool catalog.
1986
- */
2010
+
1987
2011
  skillActivations?: readonly RuntimeSkillActivation[];
1988
2012
  /** Host-owned descriptors and reader; mutually exclusive with skillActivations. */
1989
2013
  skillCatalog?: readonly SkillCatalogDescriptor[];
2014
+ /** Worker has persisted the catalog in conversation history before inference. */
2015
+ skillCatalogInHistory?: boolean;
1990
2016
  /**
1991
2017
  * Internal per-attempt cancellation boundary. The worker supplies Temporal's
1992
2018
  * signal so an in-flight shell process is interrupted immediately instead of
@@ -2034,10 +2060,10 @@ export type RigInstructionsContext = {
2034
2060
 
2035
2061
  export function rigInstructions(rig: RigInstructionsContext): string[] {
2036
2062
  return [
2037
- `This session runs on rig "${rig.name}" (active version v${rig.version}) — a shared, versioned sandbox machine definition for your workspace.`,
2038
- "Your sandbox is an EPHEMERAL FORK of that rig: you have root and may install anything freely, but everything you change here is junk that dies with the box and never reaches the rig or other sessions.",
2039
- "For a DURABLE, team-wide change (tooling every future session on this rig should have), propose it with rig_propose_change, passing the EXACT command that already worked in this box — never assume an unverified change propagates.",
2040
- "If tooling you expect is missing, consult rig_get to see the rig's current setup and checks before reinstalling.",
2063
+ `This session uses sandbox environment "${rig.name}" (active version v${rig.version}) — a versioned definition of custom sandbox setup and health checks.`,
2064
+ "Your sandbox is an EPHEMERAL FORK of this environment. You may install tools here, but local changes do not update the environment definition or other sessions.",
2065
+ "To make a verified setup change available to future sessions using this environment, call rig_propose_change with the exact command that already worked here. Never assume an unverified change propagates.",
2066
+ "If tooling you expect is missing, consult rig_get to see the sandbox environment's current setup and checks before reinstalling.",
2041
2067
  ];
2042
2068
  }
2043
2069
 
@@ -2075,7 +2101,9 @@ export function coreInstructions(
2075
2101
  ): string[] {
2076
2102
  return [
2077
2103
  "If the session has a goal, you own it: keep working until you call opengeni__goal_complete with concrete evidence or opengeni__goal_pause with a rationale; resume a paused goal with opengeni__goal_resume regardless of who paused it or why; revise it with opengeni__goal_update; create one with opengeni__goal_set when given a long-running objective.",
2078
- "Use knowledge_search and knowledge_get when retained information is relevant. For questions about what the workspace knows, ground the answer in authorized Knowledge and attached sources. Missing internal facts remain unknown; do not infer a person's role or cite unrelated public search results as evidence. Keep any relevant external research clearly separate from workspace records. Default retrieval returns published information. Before retaining or correcting anything, also search view=needs_review for existing pending entries and collections; read those with knowledge_get view=needs_review. Pending means unapproved: you may inspect and improve it, but do not present it as accepted knowledge or activate behavioral guidance from it. Reuse the existing entryId and current version instead of creating another proposal each run. Save durable facts, decisions, requirements, incidents, fixes and outcomes autonomously with knowledge_save when useful for future work, whether requested explicitly or learned during ordinary work. When a user supplies or confirms a durable fact, use knowledge_retain_message to retain the actual message and cite its returned entryId/revisionId in the finding evidence. Preserve existing source evidence and relationships when correcting an entry; do not replace them with empty arrays merely because the user confirmed a new value. Explicit confirmation can supersede a conflicting pending proposal, but explain that outcome to the user. Preserve uncertainty and exact supporting evidence; the fact label is not a verification claim. Chat attachments retain their original files and automatically prepare source text when authoring is enabled. Use knowledge_retain_file for a newly fetched file or failed preparation. A source entry contains retained original text; a finding states a useful conclusion and cites the exact source revision. Do not create both when they would simply repeat the same text. Reuse collections for customers, products, systems or subjects across sources, and link one entry to multiple collections instead of copying it. Technical incidents belong with the affected system and should include cause, fix and outcome when known. Reorganize references when useful; do not erase source evidence or revision history. Private tasks author personal Knowledge; shared tasks author workspace Knowledge. Automatic publishes immediately, Review first keeps a pending proposal without pausing your task, and Off prevents authoring while permitting retrieval. Never bypass review by making a new entry, switching tools, or asking for another approval. Reuse the same operationId and exact request after an uncertain save, including recovery. Use task_note_save for temporary coordination in this task tree. Conversation history is separate. Workspace instructions are concise standing rules; Skills are reusable procedures with their own Agent learning settings. Do not save the same content in multiple authorities.",
2104
+ 'Choose durable storage by purpose, including requests to "remember this" or keep something "for future sessions". Knowledge is retrieval-only information, not standing behavior. Use instruction_policy_get then instruction_policy_save for short always-on workspace rules, such as "Keep replies concise; expand when asked." Use Skills for reusable procedures and context-specific or personal behavioral preferences; make the Skill description state when it applies so the prompt index can guide skill_read. Do not save behavioral preferences as Knowledge by rephrasing them as facts like "the user prefers concise replies". Preserve the intended scope: do not turn a personal preference into a workspace-wide rule; use an authorized personal Skill when appropriate, or explain the unavailable scope. Split mixed requests into a short rule and detailed Skill steps without duplicating them in Knowledge.',
2105
+ "Before changing workspace instructions, read the current instruction and exact baseline, preserve unrelated rules, append new rules, and use only a localized exact anchored edit for updates or removals. Agents cannot replace the complete instruction; whole-policy rewrites belong in the manual workspace editor. Do not bypass a destination's Agent learning setting, review, scope, limits, or unavailable tools by saving to another destination. Report the actual destination and receipt: active, pending review, or not saved. Do not promise future behavior from a Knowledge save.",
2106
+ "Use knowledge_search and knowledge_get when retained information is relevant. For questions about what the workspace knows, ground the answer in authorized Knowledge and attached sources. Missing internal facts remain unknown; do not infer a person's role or cite unrelated public search results as evidence. Keep any relevant external research clearly separate from workspace records. Default retrieval returns published information. Before retaining or correcting anything, also search view=needs_review for existing pending entries and collections; read those with knowledge_get view=needs_review. Pending means unapproved: you may inspect and improve it, but do not present it as accepted knowledge or activate behavioral guidance from it. Reuse the existing entryId and current version instead of creating another proposal each run. Save durable facts, decisions, requirements, incidents, fixes and outcomes autonomously with knowledge_save only when they help a plausible future task. Do not retain routine approvals, acknowledgments, temporary task instructions, status chatter or raw screenshots merely because they occurred. Before saving, use knowledge_prepare_save when available to fetch the authorized collection map with descriptions and parent IDs plus related published and pending entries; otherwise search both views and browse collections. Skip unchanged duplicates, improve the existing entry when appropriate, and create a new entry only for distinct useful information. Read the current entry before correcting it and preserve uncertainty, conflicting evidence and existing relationships. When a user supplies or confirms a durable fact, use knowledge_retain_message to retain the actual message and cite its returned entryId/revisionId in the finding evidence. Preserve existing source evidence and relationships when correcting an entry; do not replace them with empty arrays merely because the user confirmed a new value. Explicit confirmation can supersede a conflicting pending proposal, but explain that outcome to the user. Preserve uncertainty and exact supporting evidence; the fact label is not a verification claim. Chat attachments retain their original files for the conversation without automatically publishing OCR or source text into Knowledge. Inspect an image visually in the current task; save only a useful supported observation, requirement or incident when warranted. Use knowledge_retain_file purpose=evidence only when a selected finding needs the original as supporting evidence, or purpose=reference when deliberately retaining a reusable source document. Supporting sources remain accessible through evidence links and explicit includeEvidence searches, but do not fill ordinary Knowledge discovery. An upload alone is not a reason to save Knowledge. A source entry contains retained original text; a finding states a useful conclusion and cites the exact source revision. Do not create both when they would simply repeat the same text. Reuse collections for customers, products, systems or subjects across sources, and link one entry to multiple collections instead of copying it. Technical incidents belong with the affected system and should include cause, fix and outcome when known. Reorganize references when useful; do not erase source evidence or revision history. Private tasks author personal Knowledge; shared tasks author workspace Knowledge. Automatic publishes immediately, Review first keeps a pending proposal without pausing your task, and Off prevents authoring while permitting retrieval. Never bypass review by making a new entry, switching tools, or asking for another approval. Reuse the same operationId and exact request after an uncertain save, including recovery. Use task_note_save for temporary coordination in this task tree. Conversation history is separate. Workspace instructions are concise standing rules; Skills are reusable procedures with their own Agent learning settings. Do not save the same content in multiple authorities.",
2079
2107
  ...(workspaceEnvironment ? workspaceEnvironmentInstructions(workspaceEnvironment) : []),
2080
2108
  // Rig doctrine (M3): data-conditional, inside the non-bypassable CORE so a
2081
2109
  // white-label persona template can never drop it. Absent for rig-less sessions.
@@ -2195,12 +2223,12 @@ export function inspectPersistentAgentInstructions(
2195
2223
  });
2196
2224
  }
2197
2225
  push("workspace_memory", "Workspace memory", options.workspaceMemory);
2198
- if (options.skillCatalog)
2226
+ if (options.skillCatalog && !options.skillCatalogInHistory)
2199
2227
  push("skill_catalog", "Skills", formatSkillCatalog(options.skillCatalog));
2200
2228
  push("session_instructions", "Session instructions", options.sessionInstructions);
2201
2229
  } else {
2202
2230
  push("workspace_governance", "Workspace governance", options.workspaceGovernance);
2203
- if (options.skillCatalog)
2231
+ if (options.skillCatalog && !options.skillCatalogInHistory)
2204
2232
  push("skill_catalog", "Skills", formatSkillCatalog(options.skillCatalog));
2205
2233
  push("session_instructions", "Session instructions", options.sessionInstructions);
2206
2234
  if (codemodeIsAvailable(options)) {
@@ -2847,8 +2875,8 @@ function maybeInstallLazyToolTransport(
2847
2875
  if (!enabled) return;
2848
2876
 
2849
2877
  const mcpServers = options.mcpServers ?? [];
2850
- // Prepared servers use a shared SDK lifecycle name; tool prefixes come from
2851
- // their registry identity. Preserve the fallback for embedded/test servers.
2878
+ // Prepared servers use exact model-name mappings, not SDK lifecycle names
2879
+ // or parseable prefixes. Preserve legacy embedded/test-server classification.
2852
2880
  const mcpServerIds = new Set(mcpServers.map((server) => mcpServerRegistryId(server)));
2853
2881
  const deferredMcpServerIds = new Set(
2854
2882
  mcpServers
@@ -2862,6 +2890,18 @@ function maybeInstallLazyToolTransport(
2862
2890
  options.toolPreparationReady,
2863
2891
  deferredMcpServerIds,
2864
2892
  new Set(options.preparationIndependentToolNames ?? []),
2893
+ () => {
2894
+ const identities = new Map<string, string>();
2895
+ for (const server of mcpServers) {
2896
+ if (!(server instanceof PrefixedMcpServer || server instanceof DeferredPreparedMcpServer))
2897
+ continue;
2898
+ for (const name of server.modelToolNames()) {
2899
+ if (identities.has(name)) throw new Error("MCP model tool identity collision");
2900
+ identities.set(name, server.registryId);
2901
+ }
2902
+ }
2903
+ return identities;
2904
+ },
2865
2905
  );
2866
2906
  }
2867
2907
 
@@ -2894,7 +2934,7 @@ function sessionMcpApprovalConnectionId(serverId: string, url: string): string {
2894
2934
  return `session-mcp:${serverId}:${targetHash}`;
2895
2935
  }
2896
2936
 
2897
- /** A per-server approval policy keyed by the server's `<id>__` tool prefix. */
2937
+ /** Exact server policy. Prefix matching is only for legacy unwrapped servers. */
2898
2938
  type McpApprovalPolicy = {
2899
2939
  prefix: string;
2900
2940
  serverId: string;
@@ -2906,6 +2946,7 @@ type McpApprovalPolicy = {
2906
2946
  /** The subset of the agent surface the approval wrap needs — including `clone`. */
2907
2947
  type ApprovalCapableAgent = {
2908
2948
  getMcpTools: (runContext: unknown) => Promise<Tool<any>[]>;
2949
+ mcpServers?: MCPServer[];
2909
2950
  clone?: (config: unknown) => ApprovalCapableAgent;
2910
2951
  };
2911
2952
 
@@ -2948,9 +2989,9 @@ function modelMcpSourceCallId(modelName: string): string | undefined {
2948
2989
  /**
2949
2990
  * Install the approval wrap on a single agent instance: replace `getMcpTools`
2950
2991
  * with one that stamps `needsApproval: () => true` on every MCP tool whose
2951
- * server policy demands it. Tools are matched by the server's `<id>__` prefix
2952
- * (LONGEST prefix first — see {@link applyMcpApprovalPolicy}), then the
2953
- * unprefixed tool name.
2992
+ * server policy demands it. Prepared tools resolve through their frozen model
2993
+ * name -> original server/tool map; legacy unwrapped servers retain longest-
2994
+ * prefix matching. Never infer account authority from a sanitized SDK name.
2954
2995
  *
2955
2996
  * CLONE SURVIVAL (also used by `installLazyToolRuntime`): the sandbox runtime
2956
2997
  * resolves tools not on the agent we build here but on a FRESH clone —
@@ -2973,20 +3014,35 @@ function installMcpApprovalPolicy(
2973
3014
  const listMcpTools = agent.getMcpTools.bind(agent);
2974
3015
  agent.getMcpTools = async (resolutionContext: unknown) => {
2975
3016
  const tools = await listMcpTools(resolutionContext);
3017
+ const identities = new Map<string, { serverId: string; toolName: string }>();
3018
+ for (const server of agent.mcpServers ?? []) {
3019
+ if (!(server instanceof PrefixedMcpServer || server instanceof DeferredPreparedMcpServer))
3020
+ continue;
3021
+ for (const descriptor of await server.listTools()) {
3022
+ if (identities.has(descriptor.name)) throw new Error("MCP model tool identity collision");
3023
+ identities.set(descriptor.name, {
3024
+ serverId: server.registryId,
3025
+ toolName: await server.unprefixedToolName(descriptor.name),
3026
+ });
3027
+ }
3028
+ }
2976
3029
  return tools.map((tool) => {
2977
3030
  if (tool.type !== "function") {
2978
3031
  return tool;
2979
3032
  }
2980
- const policy = policies.find((entry) => tool.name.startsWith(entry.prefix));
3033
+ const identity = identities.get(tool.name);
3034
+ const policy = identity
3035
+ ? policies.find((entry) => entry.serverId === identity.serverId)
3036
+ : policies.find((entry) => tool.name.startsWith(entry.prefix));
2981
3037
  if (!policy) {
2982
3038
  return tool;
2983
3039
  }
2984
- const unprefixed = tool.name.slice(policy.prefix.length);
3040
+ const unprefixed = identity?.toolName ?? tool.name.slice(policy.prefix.length);
2985
3041
  const originalNeedsApproval = tool.needsApproval.bind(tool);
2986
3042
  const originalInvoke = tool.invoke.bind(tool);
2987
3043
  const legacyApproval =
2988
3044
  !policy.connectorBacked && mcpToolRequiresApproval(policy.requireApproval, unprefixed);
2989
- if (!policy.connectorBacked && !legacyApproval) {
3045
+ if (!policy.connectorBacked && !legacyApproval && !connectorActionPolicy) {
2990
3046
  return tool;
2991
3047
  }
2992
3048
  const connectorCall = (approvalId: string, args: unknown): ConnectorActionToolCall => {
@@ -3295,7 +3351,8 @@ function installInteractionInterventionPolicy(
3295
3351
  * tools to function tools with `needsApproval` unset (defaults false) and exposes
3296
3352
  * no per-server/agent approval knob, so we wrap the agent's `getMcpTools` to
3297
3353
  * attach a `needsApproval: () => true` predicate to the matching tools — matched
3298
- * by the server's `<id>__` prefix, then the unprefixed tool name. A tool that
3354
+ * by exact prepared server/tool identity (legacy unwrapped servers use their
3355
+ * `<id>__` prefix). A tool that
3299
3356
  * needs approval raises a run INTERRUPTION, which the worker turns into
3300
3357
  * `session.requiresAction` and resolves via `user.approvalDecision`
3301
3358
  * (resumeApproval) — the same generic path other tool approvals use, so
@@ -3324,6 +3381,7 @@ function applyMcpApprovalPolicy(
3324
3381
  .filter(
3325
3382
  (server) =>
3326
3383
  Boolean(server.connectionRef) ||
3384
+ Boolean(connectorActionPolicy && attemptToolSource(server.id) === "mcp") ||
3327
3385
  server.requireApproval === true ||
3328
3386
  (Array.isArray(server.requireApproval) && server.requireApproval.length > 0),
3329
3387
  )
@@ -3338,7 +3396,9 @@ function applyMcpApprovalPolicy(
3338
3396
  prefix: prefixedMcpToolName(server.id, ""),
3339
3397
  serverId: server.id,
3340
3398
  requireApproval:
3341
- server.requireApproval === true ? true : new Set(server.requireApproval as string[]),
3399
+ server.requireApproval === true
3400
+ ? true
3401
+ : new Set(Array.isArray(server.requireApproval) ? server.requireApproval : []),
3342
3402
  connectorBacked: Boolean(server.connectionRef),
3343
3403
  connectionId,
3344
3404
  };
@@ -3676,11 +3736,15 @@ export type ToolPreparationPhase =
3676
3736
 
3677
3737
  export type ToolPreparationPhaseMeasurement = {
3678
3738
  phase: ToolPreparationPhase;
3739
+ /** Background preparation starts immediately but is not a first-request dependency. */
3740
+ execution: "blocking" | "background";
3679
3741
  outcome: "completed" | "failed";
3680
3742
  durationSeconds: number;
3681
3743
  };
3682
3744
 
3683
3745
  export type PrepareToolsOptions = {
3746
+ /** Frozen, explicitly selected account labels keyed by execution route, not provider. */
3747
+ mcpAccountLabels?: ReadonlyMap<string, string>;
3684
3748
  /** Opt-in exact-attempt persistence; absence preserves ordinary MCP execution. */
3685
3749
  mcpOperationPersistence?: McpOperationPersistence;
3686
3750
  /** Live exact-owner control refresh; API remains read/observation authority. */
@@ -3792,6 +3856,7 @@ async function measureToolPreparationPhase<T>(
3792
3856
  options: PrepareToolsOptions,
3793
3857
  phase: ToolPreparationPhase,
3794
3858
  operation: () => Promise<T>,
3859
+ execution: ToolPreparationPhaseMeasurement["execution"] = "blocking",
3795
3860
  ): Promise<T> {
3796
3861
  const startedAt = performance.now();
3797
3862
  let outcome: ToolPreparationPhaseMeasurement["outcome"] = "completed";
@@ -3804,6 +3869,7 @@ async function measureToolPreparationPhase<T>(
3804
3869
  try {
3805
3870
  options.onPreparationPhase?.({
3806
3871
  phase,
3872
+ execution,
3807
3873
  outcome,
3808
3874
  durationSeconds: (performance.now() - startedAt) / 1_000,
3809
3875
  });
@@ -3929,6 +3995,7 @@ class DeferredPreparedMcpServer implements MCPServer {
3929
3995
  readonly cacheToolsList = false;
3930
3996
  readonly deferredPreparation = true;
3931
3997
  readonly name: string;
3998
+ private readonly listedModelNames = new Set<string>();
3932
3999
 
3933
4000
  constructor(
3934
4001
  readonly registryId: string,
@@ -3959,7 +4026,20 @@ class DeferredPreparedMcpServer implements MCPServer {
3959
4026
  async listTools(): Promise<RuntimeMcpTool[]> {
3960
4027
  if (!this.isPrepared()) return [];
3961
4028
  const target = await this.resolveTarget();
3962
- return target ? ((await target.listTools()) as RuntimeMcpTool[]) : [];
4029
+ const tools = target ? ((await target.listTools()) as RuntimeMcpTool[]) : [];
4030
+ this.listedModelNames.clear();
4031
+ for (const tool of tools) this.listedModelNames.add(tool.name);
4032
+ return tools;
4033
+ }
4034
+
4035
+ modelToolNames(): Iterable<string> {
4036
+ return this.listedModelNames.values();
4037
+ }
4038
+
4039
+ async unprefixedToolName(name: string): Promise<string> {
4040
+ const target = await this.requiredTarget();
4041
+ if (!(target instanceof PrefixedMcpServer)) throw new Error("Unknown prepared MCP identity");
4042
+ return target.unprefixedToolName(name);
3963
4043
  }
3964
4044
 
3965
4045
  async callTool(
@@ -4076,6 +4156,9 @@ export async function prepareAgentTools(
4076
4156
  tool.eager !== true,
4077
4157
  local.preflightCall,
4078
4158
  local.approvalAuthority,
4159
+ undefined,
4160
+ undefined,
4161
+ options.mcpAccountLabels?.get(config.id),
4079
4162
  ),
4080
4163
  config,
4081
4164
  options,
@@ -4129,7 +4212,7 @@ export async function prepareAgentTools(
4129
4212
  // reject the bearer at the initialize/tools-list handshake, so a 401/403
4130
4213
  // (or a missing/failed token) drops the server.
4131
4214
  // - an optional ToolRef: either an auto-attached workspace-default
4132
- // capability MCP or a client/pack-selected portable ref. A
4215
+
4133
4216
  // broken/expired credential or unavailable endpoint skips the server
4134
4217
  // with a warning, never killing the turn before the model runs. Bare
4135
4218
  // refs stay strict (below), preserving the fail-loud default.
@@ -4217,6 +4300,7 @@ export async function prepareAgentTools(
4217
4300
  firstParty && config.id === "opengeni" && !config.connectionRef && !bridge
4218
4301
  ? options.refreshOwnedCommand
4219
4302
  : undefined,
4303
+ options.mcpAccountLabels?.get(config.id),
4220
4304
  ),
4221
4305
  config,
4222
4306
  options,
@@ -4263,6 +4347,7 @@ export async function prepareAgentTools(
4263
4347
  entries: typeof servers,
4264
4348
  strict: boolean,
4265
4349
  phase: "required_connect" | "optional_connect",
4350
+ execution: ToolPreparationPhaseMeasurement["execution"],
4266
4351
  ): Promise<ConnectedMcpServerBatches | null> =>
4267
4352
  entries.length === 0
4268
4353
  ? null
@@ -4277,6 +4362,7 @@ export async function prepareAgentTools(
4277
4362
  connectTimeoutMs: mcpOuterConnectTimeoutMs(entries.map((entry) => entry.timeoutMs)),
4278
4363
  },
4279
4364
  ),
4365
+ execution,
4280
4366
  );
4281
4367
  const warnBestEffortFailures = (connected: ConnectedMcpServerBatches | null): void => {
4282
4368
  if (!connected) return;
@@ -4303,13 +4389,14 @@ export async function prepareAgentTools(
4303
4389
  const connectEntryGroups = async (
4304
4390
  required: typeof servers,
4305
4391
  bestEffort: typeof servers,
4392
+ execution: ToolPreparationPhaseMeasurement["execution"],
4306
4393
  ): Promise<{
4307
4394
  required: ConnectedMcpServerBatches | null;
4308
4395
  bestEffort: ConnectedMcpServerBatches | null;
4309
4396
  }> => {
4310
4397
  const [requiredResult, bestEffortResult] = await Promise.allSettled([
4311
- connectEntries(required, true, "required_connect"),
4312
- connectEntries(bestEffort, false, "optional_connect"),
4398
+ connectEntries(required, true, "required_connect", execution),
4399
+ connectEntries(bestEffort, false, "optional_connect", execution),
4313
4400
  ]);
4314
4401
  const connectedBestEffort =
4315
4402
  bestEffortResult.status === "fulfilled" ? bestEffortResult.value : null;
@@ -4324,7 +4411,11 @@ export async function prepareAgentTools(
4324
4411
  }
4325
4412
  return { required: requiredResult.value, bestEffort: connectedBestEffort };
4326
4413
  };
4327
- const connectedEager = await connectEntryGroups(eagerRequiredEntries, eagerBestEffortEntries);
4414
+ const connectedEager = await connectEntryGroups(
4415
+ eagerRequiredEntries,
4416
+ eagerBestEffortEntries,
4417
+ "blocking",
4418
+ );
4328
4419
  const connectedEagerRequired = connectedEager.required;
4329
4420
  const connectedEagerBestEffort = connectedEager.bestEffort;
4330
4421
  warnBestEffortFailures(connectedEagerBestEffort);
@@ -4344,6 +4435,7 @@ export async function prepareAgentTools(
4344
4435
  await connectedEagerRequired?.close().catch(() => undefined);
4345
4436
  };
4346
4437
  const completePreparation = async (): Promise<PreparedAgentTools> => {
4438
+ const execution = exposesDeferredPreparation ? "background" : "blocking";
4347
4439
  let attemptToolEnvironment: AttemptToolEnvironment | null = null;
4348
4440
  let toolGatewayCatalog: ToolGatewayCatalog | null = null;
4349
4441
  let toolGateway: ToolGateway | null = null;
@@ -4351,6 +4443,7 @@ export async function prepareAgentTools(
4351
4443
  const connectedDeferred = await connectEntryGroups(
4352
4444
  deferredRequiredEntries,
4353
4445
  deferredBestEffortEntries,
4446
+ execution,
4354
4447
  );
4355
4448
  connectedDeferredRequired = connectedDeferred.required;
4356
4449
  connectedDeferredBestEffort = connectedDeferred.bestEffort;
@@ -4371,14 +4464,20 @@ export async function prepareAgentTools(
4371
4464
  resolvedMcpConnectionIds,
4372
4465
  options,
4373
4466
  ),
4467
+ execution,
4374
4468
  );
4375
4469
  if (attemptToolEnvironment && localToolServer) {
4376
4470
  localToolServer.bindAttemptToolEnvironment(attemptToolEnvironment);
4377
4471
  }
4378
4472
  if (attemptToolEnvironment) {
4379
- await measureToolPreparationPhase(options, "attempt_catalog_persist", async () => {
4380
- await options.onAttemptToolCatalog?.(attemptToolEnvironment!.catalog);
4381
- });
4473
+ await measureToolPreparationPhase(
4474
+ options,
4475
+ "attempt_catalog_persist",
4476
+ async () => {
4477
+ await options.onAttemptToolCatalog?.(attemptToolEnvironment!.catalog);
4478
+ },
4479
+ execution,
4480
+ );
4382
4481
  }
4383
4482
  if (options.workspaceToolGateway) {
4384
4483
  const prepared = await measureToolPreparationPhase(
@@ -4386,6 +4485,7 @@ export async function prepareAgentTools(
4386
4485
  "workspace_gateway_catalog_build",
4387
4486
  async () =>
4388
4487
  await prepareWorkspaceToolGatewayEnvironment(activeMcpServers, registry, options),
4488
+ execution,
4389
4489
  );
4390
4490
  toolGatewayCatalog = prepared.catalog;
4391
4491
  toolGateway = prepared.gateway;
@@ -4655,7 +4755,11 @@ function installAttemptConnectorActionGatewayLifecycle(
4655
4755
  const config = registry.get(definition.identity.serverId);
4656
4756
  const legacyMcpApproval =
4657
4757
  Boolean(config) && !config!.connectionRef && definition.approval === "human";
4658
- if (!binding && !config?.connectionRef && !legacyMcpApproval) return definition;
4758
+ const workspaceConnectorPolicy = Boolean(
4759
+ connectorActionPolicy && config && attemptToolSource(config.id) === "mcp",
4760
+ );
4761
+ if (!binding && !config?.connectionRef && !legacyMcpApproval && !workspaceConnectorPolicy)
4762
+ return definition;
4659
4763
  if (definition.lifecycle) {
4660
4764
  throw new Error(`Connector action tool already owns a lifecycle: ${definition.modelName}`);
4661
4765
  }
@@ -4684,7 +4788,7 @@ function installAttemptConnectorActionGatewayLifecycle(
4684
4788
  serverId: definition.identity.serverId,
4685
4789
  toolName: definition.identity.toolName,
4686
4790
  arguments: arguments_,
4687
- approvalMode: "session_mcp",
4791
+ ...(legacyMcpApproval ? { approvalMode: "session_mcp" as const } : {}),
4688
4792
  });
4689
4793
  return {
4690
4794
  ...definition,
@@ -6408,7 +6512,15 @@ function normalizeUrl(raw: string): string | null {
6408
6512
  }
6409
6513
 
6410
6514
  export function prefixedMcpToolName(registryId: string, toolName: string): string {
6411
- return sharedPrefixedMcpToolName(registryId, toolName);
6515
+ const name = sharedPrefixedMcpToolName(registryId, toolName);
6516
+ // The SDK normalizes punctuation (including hyphens) and providers cap names
6517
+ // at 64 characters. Use the complete digest, not lossy truncation. Hex names
6518
+ // cannot collide with ordinary names, which always contain the __ separator.
6519
+ return name.length <= 64 && /^[A-Za-z0-9_]+$/.test(name)
6520
+ ? name
6521
+ : createHash("sha256")
6522
+ .update(JSON.stringify([registryId, toolName]))
6523
+ .digest("hex");
6412
6524
  }
6413
6525
 
6414
6526
  const MCP_SDK_LIFECYCLE_NAME = "opengeni-mcp-lifecycle";
@@ -6605,6 +6717,11 @@ export class PrefixedMcpServer implements MCPServer {
6605
6717
  private loggedListToolsFailure = false;
6606
6718
  private listedToolSchemaTokens = 0;
6607
6719
  private frozenTools: Promise<RuntimeMcpTool[]> | null = null;
6720
+ private readonly originalToolNames = new Map<string, string>();
6721
+ /** Exact attempt-local classification, including names too long for a prefix. */
6722
+ modelToolNames(): Iterable<string> {
6723
+ return this.originalToolNames.keys();
6724
+ }
6608
6725
  private attemptToolEnvironment: AttemptToolEnvironment | null = null;
6609
6726
  private attemptToolSubjectId = "worker:mcp-model";
6610
6727
  private readonly resultCustomDataBridge: McpResultCustomDataBridge;
@@ -6626,6 +6743,7 @@ export class PrefixedMcpServer implements MCPServer {
6626
6743
  private readonly approvalAuthority?: unknown,
6627
6744
  private readonly inputWaitYield?: InputWaitYield,
6628
6745
  private readonly refreshOwnedCommand?: (commandId: string) => Promise<boolean>,
6746
+ private readonly accountLabel?: string,
6629
6747
  ) {
6630
6748
  this.registryId = registryId;
6631
6749
  // The SDK uses `name` for cache keys, traces, and lifecycle diagnostics.
@@ -6802,10 +6920,23 @@ export class PrefixedMcpServer implements MCPServer {
6802
6920
  const tools = assertMcpToolListWithinBounds(await this.inner.listTools()) as RuntimeMcpTool[];
6803
6921
  const exposed = tools
6804
6922
  .filter((tool) => this.isAllowed(tool.name))
6805
- .map((tool) => ({
6806
- ...tool,
6807
- name: prefixedMcpToolName(this.registryId, tool.name),
6808
- }));
6923
+ .map((tool) => {
6924
+ const name = prefixedMcpToolName(this.registryId, tool.name);
6925
+ const prior = this.originalToolNames.get(name);
6926
+ if (prior !== undefined && prior !== tool.name)
6927
+ throw new Error("MCP model tool identity collision");
6928
+ this.originalToolNames.set(name, tool.name);
6929
+ return {
6930
+ ...tool,
6931
+ name,
6932
+ ...(this.accountLabel
6933
+ ? {
6934
+ description:
6935
+ `Account: ${this.accountLabel}. Use only this account; if the intended account for a write is unclear, ask before calling.\n${tool.description ?? ""}`.trimEnd(),
6936
+ }
6937
+ : {}),
6938
+ };
6939
+ });
6809
6940
  const bounded = (this.aggregateToolBudget?.replace(this.aggregateSourceId, exposed) ??
6810
6941
  assertMcpToolListWithinBounds(exposed)) as RuntimeMcpTool[];
6811
6942
  this.listedToolSchemaTokens = estimateSerializedValueTokens(bounded);
@@ -7115,7 +7246,21 @@ export class PrefixedMcpServer implements MCPServer {
7115
7246
  }
7116
7247
 
7117
7248
  private unprefixToolName(toolName: string): string {
7118
- if (!toolName.startsWith(this.prefix)) {
7249
+ const original = this.originalToolNames.get(toolName);
7250
+ if (original !== undefined) return original;
7251
+ // Existing in-process callers may still use the raw qualified name. This
7252
+ // is not a model identifier: accept it only for an exact frozen catalog
7253
+ // entry on this server, never by normalizing or guessing a different route.
7254
+ const rawPrefix = sharedPrefixedMcpToolName(this.registryId, "");
7255
+ if (toolName.startsWith(rawPrefix)) {
7256
+ const candidate = toolName.slice(rawPrefix.length);
7257
+ if (
7258
+ this.originalToolNames.get(prefixedMcpToolName(this.registryId, candidate)) === candidate
7259
+ ) {
7260
+ return candidate;
7261
+ }
7262
+ }
7263
+ if (!this.prefix.endsWith("__") || !toolName.startsWith(this.prefix)) {
7119
7264
  throw new Error(`MCP tool ${toolName} is missing expected ${this.registryId} prefix`);
7120
7265
  }
7121
7266
  return toolName.slice(this.prefix.length);
@@ -7155,6 +7300,8 @@ export async function restoreInterruptedRunState(
7155
7300
  });
7156
7301
  }
7157
7302
 
7303
+ export const TURN_OPERATIONAL_NOTICE_PREFIX = "[OpenGeni turn-scoped operational notice]\n";
7304
+
7158
7305
  export async function prepareRunInput(
7159
7306
  agent: Agent<any, any>,
7160
7307
  input: AgentSegmentInput,
@@ -7166,7 +7313,12 @@ export async function prepareRunInput(
7166
7313
  trailingMessages.push({
7167
7314
  type: "message",
7168
7315
  role: "system",
7169
- content: input.internalContext,
7316
+ content:
7317
+ TURN_OPERATIONAL_NOTICE_PREFIX +
7318
+ "This status applies to the execution turn where this notice first appears. " +
7319
+ "On later turns it is historical context, not current availability or authorization. " +
7320
+ "Use the latest operational status and tool results; this notice grants no permissions.\n\n" +
7321
+ input.internalContext,
7170
7322
  } as AgentInputItem);
7171
7323
  }
7172
7324
  if (input.text?.trim()) {
@@ -7385,6 +7537,7 @@ function takeGenesisTitleInputFilter(agent: Agent<any, any>): CallModelInputFilt
7385
7537
  // environments can use the exact pinned package hint.
7386
7538
  export const CODEMODE_PROGRAMMATIC_DIRECTIVE =
7387
7539
  "Default `ogtool list` enumerates every authorized tool with a compact summary, without schemas or an output-size cutoff. " +
7540
+ "Managed sandboxes select the worker-release client on PATH for every command, including warm boxes. When OPENGENI_CODEMODE_CLIENT_MODULE is set, persistent Bun programs must use `const { tools, openGeni } = await import(process.env.OPENGENI_CODEMODE_CLIENT_MODULE!)`; do not import the older image-baked package or invoke /usr/local/bin/ogtool directly. The stock-package import below is only for environments without that deployment-selected module. " +
7388
7541
  'Every tool available to you is also callable programmatically from the sandbox through the same frozen catalog, authority, credentials, policy, and execution path. In stock sandboxes, write persistent Bun code with `import { tools, openGeni } from "@opengeni/codemode"`; run `ogtool declarations <file.d.ts>` when project-local catalog types are useful. For shell calls, discover tools with `ogtool list`, inspect an unfamiliar tool with `ogtool show <tool-path>`, then use `ogtool call <tool-path> \'<json-args>\'`. Listing is compact by default; `list --json` gives compact structured discovery and `list --full` explicitly includes all schemas. When $OPENGENI_CODEMODE_NATIVE_CLIENT is available, prefer the connection-bound native client even if an older `ogtool` is installed: use `"$OPENGENI_CODEMODE_NATIVE_CLIENT" codemode` with the same list, show, and call commands; this uses the same public Codemode operation journal, not another tool path. If no compatible installed client is available and Bun plus $OPENGENI_OGTOOL_PACKAGE_SPEC are available, run the exact deployment-pinned package with `bun x -p "$OPENGENI_OGTOOL_PACKAGE_SPEC" ogtool ...`; never guess a version or install `latest`. Prefer Codemode for loops, polling, bulk filtering, and intermediate data that should remain in the sandbox instead of consuming your context window. Tools requiring human approval return a typed error in Codemode and must be invoked normally.';
7389
7542
 
7390
7543
  function modelModalityProjectionFilterForAgent(
@@ -7426,11 +7579,16 @@ function measuredModelInputFilter(
7426
7579
  };
7427
7580
  }
7428
7581
 
7582
+ const agentModelContextCaptures = new WeakMap<object, ModelRequestCapture>();
7583
+
7429
7584
  function bindModelVisibleContextCapture(
7430
7585
  agent: Agent<any, any>,
7431
7586
  onCapture: RunAgentStreamOptions["onModelVisibleContext"],
7432
7587
  ): ModelRequestCapture | undefined {
7433
- if (!onCapture) return undefined;
7588
+ if (!onCapture) {
7589
+ agentModelContextCaptures.delete(agent);
7590
+ return undefined;
7591
+ }
7434
7592
  const capture: ModelRequestCapture = async (request) => {
7435
7593
  const requestIndex = nextModelContextCaptureIndex(agent);
7436
7594
  await onCapture(
@@ -7455,6 +7613,7 @@ function bindModelVisibleContextCapture(
7455
7613
  }),
7456
7614
  );
7457
7615
  };
7616
+ agentModelContextCaptures.set(agent, capture);
7458
7617
  return capture;
7459
7618
  }
7460
7619
 
@@ -7483,7 +7642,9 @@ export async function runAgentStream(
7483
7642
  const scope = gate?.beginStream(overrides.signal);
7484
7643
  try {
7485
7644
  if (scope) agent.toolUseBehavior = scope.toolUseBehavior;
7486
- const stream = await runAgentStreamInternal(agent, input, settings, overrides, scope);
7645
+ const stream = await withPreparedCompactionRequest(agent, () =>
7646
+ runAgentStreamInternal(agent, input, settings, overrides, scope),
7647
+ );
7487
7648
  // Observe the SDK's own settlement promise before exposing the stream. Do
7488
7649
  // not wrap/replace SDK history, errors, cancellation, or stream iteration.
7489
7650
  // In particular a fatal sibling tool error must not leave admission open
@@ -7515,12 +7676,16 @@ async function runAgentStreamInternal(
7515
7676
  const environment = overrides.sandboxEnvironment ?? collectSandboxEnvironment(settings);
7516
7677
  const codemodeTokenFile = codemodeTokenFileForAgent(agent, environment);
7517
7678
  const codemodeUrl = environment.OPENGENI_CODEMODE_URL;
7679
+ const codemodeClientDirectory =
7680
+ codemodeTokenFile && agentActiveSandboxBackend.get(agent) !== "selfhosted"
7681
+ ? managedCodemodeClientDirectory(await loadManagedCodemodeClient())
7682
+ : undefined;
7518
7683
  const genesisTitleInputFilter = takeGenesisTitleInputFilter(agent);
7519
7684
  const modelRequestCapture = bindModelVisibleContextCapture(
7520
7685
  agent,
7521
7686
  overrides.onModelVisibleContext,
7522
7687
  );
7523
- if (modelRequestCapture) installNonLazyModelRequestCapture(agent);
7688
+ installNonLazyModelRequestCapture(agent);
7524
7689
  if (overrides.onRunCredentialSessionReady && !overrides.runCredentialSessionId) {
7525
7690
  throw new Error("runCredentialSessionId is required when run credential setup is enabled");
7526
7691
  }
@@ -7546,13 +7711,23 @@ async function runAgentStreamInternal(
7546
7711
  ? withRunCredentialsSession(session as SandboxSessionLike, overrides.runCredentialSessionId)
7547
7712
  : (session as SandboxSessionLike);
7548
7713
  const agentSession = codemodeTokenFile
7549
- ? withCodemodeTokenSession(credentialAgentSession, codemodeTokenFile, codemodeUrl)
7714
+ ? withCodemodeTokenSession(
7715
+ credentialAgentSession,
7716
+ codemodeTokenFile,
7717
+ codemodeUrl,
7718
+ codemodeClientDirectory,
7719
+ )
7550
7720
  : credentialAgentSession;
7551
7721
  const credentialSetupSession = overrides.runCredentialSessionId
7552
7722
  ? withRunCredentialsSession(setupSession, overrides.runCredentialSessionId)
7553
7723
  : setupSession;
7554
7724
  const decoratedSetupSession = codemodeTokenFile
7555
- ? withCodemodeTokenSession(credentialSetupSession, codemodeTokenFile, codemodeUrl)
7725
+ ? withCodemodeTokenSession(
7726
+ credentialSetupSession,
7727
+ codemodeTokenFile,
7728
+ codemodeUrl,
7729
+ codemodeClientDirectory,
7730
+ )
7556
7731
  : credentialSetupSession;
7557
7732
  // Platform setup (manifest-env pin + beforeAgentStart hooks + file downloads)
7558
7733
  // against the UN-proxied established box — the ONE-TRUTH helper shared with the
@@ -7640,7 +7815,12 @@ async function runAgentStreamInternal(
7640
7815
  )
7641
7816
  : resourceClient;
7642
7817
  const codemodeResourceClient = codemodeTokenFile
7643
- ? withCodemodeTokenClient(credentialResourceClient, codemodeTokenFile, codemodeUrl)
7818
+ ? withCodemodeTokenClient(
7819
+ credentialResourceClient,
7820
+ codemodeTokenFile,
7821
+ codemodeUrl,
7822
+ codemodeClientDirectory,
7823
+ )
7644
7824
  : credentialResourceClient;
7645
7825
  const decoratedClient = withSandboxLifecycleHooks(
7646
7826
  codemodeResourceClient,
@@ -7669,19 +7849,21 @@ async function runAgentStreamInternal(
7669
7849
  ),
7670
7850
  measuredModelInputFilter(
7671
7851
  "input_filter_context",
7672
- contextRobustnessFilterForSettings(settings, {
7673
- throwOnCompactionNeeded: Boolean(
7674
- overrides.contextCompactionSignal || overrides.contextCompactionRequested,
7675
- ),
7676
- ...(overrides.contextCompactionSignal
7677
- ? { contextCompactionSignal: overrides.contextCompactionSignal }
7678
- : {}),
7679
- ...(overrides.contextCompactionRequested
7680
- ? {
7681
- contextCompactionRequested: overrides.contextCompactionRequested,
7682
- }
7683
- : {}),
7684
- }),
7852
+ deferCompactionToModelBoundary(
7853
+ contextRobustnessFilterForSettings(settings, {
7854
+ throwOnCompactionNeeded: Boolean(
7855
+ overrides.contextCompactionSignal || overrides.contextCompactionRequested,
7856
+ ),
7857
+ ...(overrides.contextCompactionSignal
7858
+ ? { contextCompactionSignal: overrides.contextCompactionSignal }
7859
+ : {}),
7860
+ ...(overrides.contextCompactionRequested
7861
+ ? {
7862
+ contextCompactionRequested: overrides.contextCompactionRequested,
7863
+ }
7864
+ : {}),
7865
+ }),
7866
+ ),
7685
7867
  ),
7686
7868
  // Seal admission before any provider preparation/transport awaits.
7687
7869
  inputWaitYield?.modelDispatchFilter,
@@ -7753,7 +7935,12 @@ async function runAgentStreamInternal(
7753
7935
  : resourceClient;
7754
7936
  const codemodeClient =
7755
7937
  credentialClient && codemodeTokenFile
7756
- ? withCodemodeTokenClient(credentialClient, codemodeTokenFile, codemodeUrl)
7938
+ ? withCodemodeTokenClient(
7939
+ credentialClient,
7940
+ codemodeTokenFile,
7941
+ codemodeUrl,
7942
+ codemodeClientDirectory,
7943
+ )
7757
7944
  : credentialClient;
7758
7945
  // TOKEN-BROKER (B1): the per-turn git token seed, forwarded OFF-MANIFEST so the
7759
7946
  // repository-clone hook seeds it to the box's token file before the clone.
@@ -7820,19 +8007,21 @@ async function runAgentStreamInternal(
7820
8007
  ),
7821
8008
  measuredModelInputFilter(
7822
8009
  "input_filter_context",
7823
- contextRobustnessFilterForSettings(settings, {
7824
- throwOnCompactionNeeded: Boolean(
7825
- overrides.contextCompactionSignal || overrides.contextCompactionRequested,
7826
- ),
7827
- ...(overrides.contextCompactionSignal
7828
- ? { contextCompactionSignal: overrides.contextCompactionSignal }
7829
- : {}),
7830
- ...(overrides.contextCompactionRequested
7831
- ? {
7832
- contextCompactionRequested: overrides.contextCompactionRequested,
7833
- }
7834
- : {}),
7835
- }),
8010
+ deferCompactionToModelBoundary(
8011
+ contextRobustnessFilterForSettings(settings, {
8012
+ throwOnCompactionNeeded: Boolean(
8013
+ overrides.contextCompactionSignal || overrides.contextCompactionRequested,
8014
+ ),
8015
+ ...(overrides.contextCompactionSignal
8016
+ ? { contextCompactionSignal: overrides.contextCompactionSignal }
8017
+ : {}),
8018
+ ...(overrides.contextCompactionRequested
8019
+ ? {
8020
+ contextCompactionRequested: overrides.contextCompactionRequested,
8021
+ }
8022
+ : {}),
8023
+ }),
8024
+ ),
7836
8025
  ),
7837
8026
  inputWaitYield?.modelDispatchFilter,
7838
8027
  ].filter((f): f is CallModelInputFilter => Boolean(f)),
@@ -8283,6 +8472,8 @@ export async function runOwnedSandboxSetup(
8283
8472
  environment: Record<string, string>;
8284
8473
  preparedInput?: PreparedAgentInput;
8285
8474
  fileDownloadsMaterialized?: boolean;
8475
+ /** Lazy setup runs after SDK preparation has already observed the logical manifest. */
8476
+ recordLazyManifest?: boolean;
8286
8477
  onRuntimeEvent?: SandboxLifecycleHookContext["onRuntimeEvent"];
8287
8478
  gitTokenSeedsOverride?: GitTokenSeeds;
8288
8479
  gitTokenSeedOverride?: string;
@@ -8397,6 +8588,13 @@ export async function runOwnedSandboxSetup(
8397
8588
  appendSandboxFileDownloadFailureNote(opts.preparedInput, materialized.failures);
8398
8589
  }
8399
8590
  }
8591
+ if (opts.recordLazyManifest) {
8592
+ const manifest = (agent as { defaultManifest?: Manifest }).defaultManifest;
8593
+ if (manifest) {
8594
+ const { recordLazyMaterializedDirectories } = await import("./lazy-manifest");
8595
+ await recordLazyMaterializedDirectories(session, manifest);
8596
+ }
8597
+ }
8400
8598
  }
8401
8599
 
8402
8600
  function mergePathGrants(
@@ -8715,7 +8913,7 @@ export function buildManifest(
8715
8913
  }
8716
8914
  // No extraPathGrants here: remote sandbox clients (Modal) reject manifests
8717
8915
  // that carry them at create/apply time, which broke every Modal session.
8718
- // Pack, selected-library, session, and artifact skills are represented by
8916
+
8719
8917
  // sandbox-safe in-memory or staged local-dir sources, so no host path grant
8720
8918
  // is required here.
8721
8919
  return new Manifest({
@@ -9277,6 +9475,15 @@ function codemodeTokenFileForAgent(
9277
9475
  function sandboxCodemodeTokenHooksForAgent(agent: Agent<any, any>): SandboxLifecycleHook[] {
9278
9476
  return codemodeTokenSeedForAgent(agent)
9279
9477
  ? [
9478
+ ...(agentActiveSandboxBackend.get(agent) !== "selfhosted"
9479
+ ? [
9480
+ {
9481
+ id: "codemode-client",
9482
+ phase: "beforeAgentStart" as const,
9483
+ run: runManagedCodemodeClientHook,
9484
+ },
9485
+ ]
9486
+ : []),
9280
9487
  {
9281
9488
  id: "codemode-token",
9282
9489
  phase: "beforeAgentStart",
@@ -10518,6 +10725,29 @@ export async function runCodemodeTokenSeedHook(
10518
10725
  assertSandboxCommandSucceeded(result, "Codemode token seed hook");
10519
10726
  }
10520
10727
 
10728
+ export async function runManagedCodemodeClientHook(
10729
+ session: SandboxSessionLike,
10730
+ context: SandboxLifecycleHookContext,
10731
+ ): Promise<void> {
10732
+ await installManagedCodemodeClient(
10733
+ session,
10734
+ await loadManagedCodemodeClient(),
10735
+ async (cmd) =>
10736
+ await runSandboxLifecycleCommand(
10737
+ session,
10738
+ {
10739
+ cmd,
10740
+ workdir: "/workspace",
10741
+ ...(context.runAs ? { runAs: context.runAs } : {}),
10742
+ yieldTimeMs: SANDBOX_LIFECYCLE_COMMAND_TIMEOUT_MS,
10743
+ maxOutputTokens: 1_000,
10744
+ },
10745
+ context.commandRunner,
10746
+ ),
10747
+ context.runAs,
10748
+ );
10749
+ }
10750
+
10521
10751
  export async function refreshCodemodeTokenFile(
10522
10752
  session: CodemodeTokenWriterSession,
10523
10753
  token: string,
@@ -10589,7 +10819,7 @@ function rigSetupHeredocDelimiter(script: string): string {
10589
10819
 
10590
10820
  function rigSetupExistingMarkerProbe(versionId: string, contentHash?: string): string {
10591
10821
  if (contentHash !== undefined && !/^sha256:[0-9a-f]{64}$/u.test(contentHash)) {
10592
- throw new Error("Rig setup content hash must be a canonical SHA-256 value");
10822
+ throw new Error("Sandbox Environment setup content hash must be a canonical SHA-256 value");
10593
10823
  }
10594
10824
  const markers = [`${RIG_SETUP_RUNTIME_MARKER_ROOT}/rig-setup-${versionId}.done`];
10595
10825
  if (contentHash) {
@@ -10632,14 +10862,14 @@ export function rigSetupScriptCommand(
10632
10862
  ["trusted content marker", trustedContentMarkerRoot],
10633
10863
  ] as const) {
10634
10864
  if (!isAbsolute(root) || root === "/") {
10635
- throw new Error(`Rig setup ${label} root must be a non-root absolute path`);
10865
+ throw new Error(`Sandbox Environment setup ${label} root must be a non-root absolute path`);
10636
10866
  }
10637
10867
  }
10638
10868
  if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
10639
- throw new Error("Rig setup timeout must be a positive finite duration");
10869
+ throw new Error("Sandbox Environment setup timeout must be a positive finite duration");
10640
10870
  }
10641
10871
  if (contentHash !== undefined && !/^sha256:[0-9a-f]{64}$/u.test(contentHash)) {
10642
- throw new Error("Rig setup content hash must be a canonical SHA-256 value");
10872
+ throw new Error("Sandbox Environment setup content hash must be a canonical SHA-256 value");
10643
10873
  }
10644
10874
  const timeoutSecs = Math.max(1, Math.ceil(timeoutMs / 1000));
10645
10875
  const lockWaitSecs = timeoutSecs + 6;
@@ -10658,7 +10888,7 @@ export function rigSetupScriptCommand(
10658
10888
  const heredocDelimiter = rigSetupHeredocDelimiter(script);
10659
10889
  return [
10660
10890
  "set -u",
10661
- `if ! mkdir -p ${shellQuote(markerRoot)}; then printf '%s\\n' 'unable to create rig setup marker root' >&2; exit 73; fi`,
10891
+ `if ! mkdir -p ${shellQuote(markerRoot)}; then printf '%s\\n' 'unable to create sandbox environment setup marker root' >&2; exit 73; fi`,
10662
10892
  `__OG_RIG_VERSION_MARKER=${shellQuote(versionMarker)}`,
10663
10893
  `__OG_RIG_CONTENT_MARKER=${shellQuote(contentMarker ?? "")}`,
10664
10894
  `__OG_RIG_TRUSTED_CONTENT_MARKER=${shellQuote(trustedContentMarker ?? "")}`,
@@ -10671,7 +10901,7 @@ export function rigSetupScriptCommand(
10671
10901
  ' if mkdir "$__OG_RIG_LOCK" 2>/dev/null; then',
10672
10902
  " trap 'rm -rf \"$__OG_RIG_LOCK\"' EXIT",
10673
10903
  ` if ${markerReady}; then printf '%s\\n' ${shellQuote(RIG_SETUP_SKIPPED_SENTINEL)}; exit 0; fi`,
10674
- " if ! __OG_RIG_SCRIPT=\"$(mktemp)\"; then printf '%s\\n' 'unable to create rig setup script file' >&2; exit 73; fi",
10904
+ " if ! __OG_RIG_SCRIPT=\"$(mktemp)\"; then printf '%s\\n' 'unable to create sandbox environment setup script file' >&2; exit 73; fi",
10675
10905
  `cat > "$__OG_RIG_SCRIPT" <<'${heredocDelimiter}'`,
10676
10906
  script,
10677
10907
  heredocDelimiter,
@@ -10679,12 +10909,12 @@ export function rigSetupScriptCommand(
10679
10909
  "__OG_RIG_RC=$?",
10680
10910
  ' rm -f "$__OG_RIG_SCRIPT"',
10681
10911
  ' if [ "$__OG_RIG_RC" -eq 0 ]; then',
10682
- " if ! touch \"$__OG_RIG_VERSION_MARKER\"; then printf '%s\\n' 'unable to write rig setup version marker' >&2; exit 73; fi",
10683
- " if [ -n \"$__OG_RIG_CONTENT_MARKER\" ] && ! touch \"$__OG_RIG_CONTENT_MARKER\"; then printf '%s\\n' 'unable to write rig setup content marker' >&2; exit 73; fi",
10912
+ " if ! touch \"$__OG_RIG_VERSION_MARKER\"; then printf '%s\\n' 'unable to write sandbox environment setup version marker' >&2; exit 73; fi",
10913
+ " if [ -n \"$__OG_RIG_CONTENT_MARKER\" ] && ! touch \"$__OG_RIG_CONTENT_MARKER\"; then printf '%s\\n' 'unable to write sandbox environment setup content marker' >&2; exit 73; fi",
10684
10914
  " fi",
10685
10915
  ' exit "$__OG_RIG_RC"',
10686
10916
  " fi",
10687
- " if [ ! -d \"$__OG_RIG_LOCK\" ]; then printf '%s\\n' 'unable to create rig setup lock' >&2; exit 73; fi",
10917
+ " if [ ! -d \"$__OG_RIG_LOCK\" ]; then printf '%s\\n' 'unable to create sandbox environment setup lock' >&2; exit 73; fi",
10688
10918
  " __OG_RIG_WAITED=0",
10689
10919
  ' while [ "$__OG_RIG_WAITED" -lt "$__OG_RIG_LOCK_WAIT_SECS" ]; do',
10690
10920
  ` if ${markerReady}; then printf '%s\\n' ${shellQuote(RIG_SETUP_SKIPPED_SENTINEL)}; exit 0; fi`,
@@ -10694,7 +10924,7 @@ export function rigSetupScriptCommand(
10694
10924
  " done",
10695
10925
  ` if ${markerReady}; then printf '%s\\n' ${shellQuote(RIG_SETUP_SKIPPED_SENTINEL)}; exit 0; fi`,
10696
10926
  ' if [ ! -d "$__OG_RIG_LOCK" ]; then continue; fi',
10697
- " if ! rmdir \"$__OG_RIG_LOCK\" 2>/dev/null && [ -d \"$__OG_RIG_LOCK\" ]; then printf '%s\\n' 'unable to reclaim stale rig setup lock' >&2; exit 73; fi",
10927
+ " if ! rmdir \"$__OG_RIG_LOCK\" 2>/dev/null && [ -d \"$__OG_RIG_LOCK\" ]; then printf '%s\\n' 'unable to reclaim stale sandbox environment setup lock' >&2; exit 73; fi",
10698
10928
  "done",
10699
10929
  ].join("\n");
10700
10930
  }
@@ -10731,7 +10961,7 @@ async function stageRigSetupScript(
10731
10961
  },
10732
10962
  context.commandRunner,
10733
10963
  );
10734
- assertSandboxCommandSucceeded(result, "Rig setup payload staging");
10964
+ assertSandboxCommandSucceeded(result, "Sandbox Environment setup payload staging");
10735
10965
  }
10736
10966
  return payloadPath;
10737
10967
  } catch (error) {
@@ -10833,8 +11063,10 @@ export async function runRigSetupHook(
10833
11063
  ) {
10834
11064
  result = markerProbe;
10835
11065
  } else if (markerProbeExitCode !== 42) {
10836
- assertSandboxCommandSucceeded(markerProbe, "Rig setup marker probe");
10837
- throw new Error("Rig setup marker probe returned success without its sentinel");
11066
+ assertSandboxCommandSucceeded(markerProbe, "Sandbox Environment setup marker probe");
11067
+ throw new Error(
11068
+ "Sandbox Environment setup marker probe returned success without its sentinel",
11069
+ );
10838
11070
  }
10839
11071
  if (result === undefined) {
10840
11072
  stagedScriptPath = await stageRigSetupScript(session, rigSetup.script, context);
@@ -10859,7 +11091,7 @@ export async function runRigSetupHook(
10859
11091
  },
10860
11092
  });
10861
11093
  throw new Error(
10862
- `Rig setup failed for rig "${rigSetup.rigName}" (version ${rigSetup.versionId}): ${message}`,
11094
+ `Sandbox Environment setup failed for sandbox environment "${rigSetup.rigName}" (version ${rigSetup.versionId}): ${message}`,
10863
11095
  { cause: error },
10864
11096
  );
10865
11097
  } finally {
@@ -10894,12 +11126,12 @@ export async function runRigSetupHook(
10894
11126
  : output;
10895
11127
  const timedOut = stillRunning || exitCode === 124 || exitCode === 137;
10896
11128
  const reason = timedOut
10897
- ? `did not finish within the rig setup timeout (${rigSetup.timeoutMs}ms)`
11129
+ ? `did not finish within the sandbox environment setup timeout (${rigSetup.timeoutMs}ms)`
10898
11130
  : exitCode === null
10899
11131
  ? "did not report an exit code"
10900
11132
  : `exited with code ${exitCode}`;
10901
11133
  const failure = new Error(
10902
- `Rig setup failed for rig "${rigSetup.rigName}" (version ${rigSetup.versionId}): the setup script ${reason}${tail ? `:\n${tail}` : ""}`,
11134
+ `Sandbox Environment setup failed for sandbox environment "${rigSetup.rigName}" (version ${rigSetup.versionId}): the setup script ${reason}${tail ? `:\n${tail}` : ""}`,
10903
11135
  );
10904
11136
  await context.onRuntimeEvent?.({
10905
11137
  type: "rig.setup.failed",