@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
@@ -28,13 +28,23 @@
28
28
  // `@opengeni/db`.
29
29
 
30
30
  import type { ExposedPortEndpoint } from "../stream-port";
31
+ import { isDeepStrictEqual } from "node:util";
31
32
  import {
32
33
  withProviderCommandHandle,
34
+ withCommandSupervisionReady,
35
+ withSupervisedLaunchReservation,
36
+ ProviderCommandStartRejectedError,
33
37
  type ProviderCommandPersistence,
34
38
  type ProviderCommandSession,
35
39
  } from "../provider-command-session";
36
40
  import type { SandboxProviderCommand } from "@opengeni/contracts";
37
- import { hasTypedExecHandleLoss } from "../exec-banner";
41
+ import { hasTypedExecHandleLoss, parseExecResponseBanner } from "../exec-banner";
42
+ import {
43
+ SandboxMaterializationVerificationError,
44
+ materializationVerificationDiagnostic,
45
+ retainMaterializationVerificationDiagnostic,
46
+ type MaterializationFailureReason,
47
+ } from "../materialization-verification-error";
38
48
  import { CAPABILITY_DESCRIPTORS, type SandboxBackend } from "@opengeni/contracts";
39
49
  import { SelfhostedControlError } from "../selfhosted/control-rpc";
40
50
  import {
@@ -102,6 +112,8 @@ export interface RoutableBackendSession extends ProviderCommandSession {
102
112
  pathExists?(path: string, runAs?: string): Promise<boolean>;
103
113
  viewImage?(args: unknown): Promise<unknown>;
104
114
  materializeEntry?(args: unknown): Promise<void>;
115
+ /** Provider-owned fixed read-only probe; never an agent command surface. */
116
+ verifyMaterializedPath?(path: string, workdir: string): Promise<void>;
105
117
  supportsPty?(): boolean;
106
118
  resolveExposedPort?(port: number): Promise<ExposedPortEndpoint>;
107
119
  serializeSessionState?(): Promise<unknown>;
@@ -169,6 +181,7 @@ export type RoutingRetainedProcessTerminalProof =
169
181
 
170
182
  export interface RoutingSandboxSessionDeps {
171
183
  providerCommandHandle?: (admission: unknown) => number | undefined;
184
+ providerSupervisionReady?: () => Promise<boolean>;
172
185
  providerCommandPersistence?: (process: RoutingRetainedProcess) => ProviderCommandPersistence;
173
186
  /**
174
187
  * The DEFAULT backend resolved at construction time (the same shape `resolve()`
@@ -208,6 +221,8 @@ export interface RoutingSandboxSessionDeps {
208
221
  * diagnostic only: callback failures are isolated and can never change the
209
222
  * provider result or durable mutation-settlement ordering. */
210
223
  onOperation?: RoutingSandboxOperationObserver;
224
+ /** Capture gates are not physical provider calls; observe every wait separately. */
225
+ onCaptureWait?: RoutingSandboxCaptureWaitObserver;
211
226
  /** Observe the first complete routed operation and only the subphases that
212
227
  * actually ran. Child durations are exclusive; capture waits are removed
213
228
  * from their enclosing admission/provider spans. */
@@ -294,17 +309,35 @@ export interface RoutingSandboxSessionDeps {
294
309
  }) => Promise<DefaultBackendLossResult | null>;
295
310
  }
296
311
 
312
+ function eligibleForSupervision(args: unknown): boolean {
313
+ return Boolean(
314
+ args &&
315
+ typeof args === "object" &&
316
+ !(args as { tty?: boolean }).tty &&
317
+ !(args as { runAs?: string }).runAs,
318
+ );
319
+ }
320
+
297
321
  export type RoutingSandboxOperationObservation = {
298
322
  backend: string;
299
323
  op: string;
300
324
  outcome: "ok" | "not_found" | "failed";
301
325
  durationMs: number;
326
+ materializationFailureReason?: MaterializationFailureReason;
302
327
  };
303
328
 
304
329
  export type RoutingSandboxOperationObserver = (
305
330
  observation: RoutingSandboxOperationObservation,
306
331
  ) => void;
307
332
 
333
+ export type RoutingSandboxCaptureWaitObserver = (observation: {
334
+ backend: string;
335
+ op: string;
336
+ outcome: "ok" | "failed";
337
+ durationMs: number;
338
+ captureWaitStage: "admission" | "provider";
339
+ }) => void;
340
+
308
341
  export type RoutingSandboxPhaseOutcome = "completed" | "failed";
309
342
 
310
343
  export type RoutingSandboxWaitObservation = {
@@ -707,6 +740,7 @@ export class RoutingSandboxSession implements RoutableBackendSession {
707
740
  private cachedEpoch: number | undefined;
708
741
  private cachedSandboxId: string | null | undefined;
709
742
  private cached: ResolvedActiveBackend | undefined;
743
+ private readonly homeResolutions = new Map<number, Promise<ResolvedActiveBackend>>();
710
744
  // The last-resolved backend, exposed via the `state` getter (a method-free read
711
745
  // of the active backend's `state`). Updated on every resolve.
712
746
  private lastResolved: ResolvedActiveBackend | undefined;
@@ -804,6 +838,23 @@ export class RoutingSandboxSession implements RoutableBackendSession {
804
838
  ) {
805
839
  return this.cached;
806
840
  }
841
+ // Home repair may prepare the release client before publishing a handle.
842
+ // Concurrent first operations must join that preparation, including its
843
+ // failure, instead of each resuming/preparing the replacement independently.
844
+ // Explicit machine routes retain their existing resolution behavior.
845
+ if (pointer.activeSandboxId !== null) return await this.resolvePointer(pointer);
846
+ const pending = this.homeResolutions.get(pointer.activeEpoch);
847
+ if (pending) return await pending;
848
+ const resolution = this.resolvePointer(pointer);
849
+ this.homeResolutions.set(pointer.activeEpoch, resolution);
850
+ try {
851
+ return await resolution;
852
+ } finally {
853
+ this.homeResolutions.delete(pointer.activeEpoch);
854
+ }
855
+ }
856
+
857
+ private async resolvePointer(pointer: ActivePointer): Promise<ResolvedActiveBackend> {
807
858
  const fromEpoch = this.cachedEpoch ?? pointer.activeEpoch;
808
859
  const resolved = await this.deps.resolveActiveBackend(pointer);
809
860
  // Re-entrancy guard: a resolver that returns THIS proxy as the active backend
@@ -1243,6 +1294,24 @@ export class RoutingSandboxSession implements RoutableBackendSession {
1243
1294
  const providerSessionId = providerSessionIdFromArgs(args);
1244
1295
  if (providerSessionId === null) throw new RoutingRetainedProcessNotFoundError(-1);
1245
1296
  const record = this.retainedProcess(providerSessionId);
1297
+ if (
1298
+ !modelVisible &&
1299
+ args &&
1300
+ typeof args === "object" &&
1301
+ (args as { chars?: unknown }).chars === "\u0003" &&
1302
+ record.process.providerCommand?.kind === "modal-router-v1" &&
1303
+ record.process.providerCommand.supervision
1304
+ ) {
1305
+ await this.ensureParentPromotion(record);
1306
+ if (
1307
+ !(await record.backend.session.cancelSupervisedCommand?.(
1308
+ providerSessionId,
1309
+ "explicit_stop",
1310
+ ))
1311
+ )
1312
+ throw new Error("Supervised command cancellation is unavailable");
1313
+ args = { ...args, chars: "" };
1314
+ }
1246
1315
  await this.captureRetainedOutput(record);
1247
1316
  const priorTerminal = await this.flushPendingProcessMutation(record);
1248
1317
  if (priorTerminal !== null) {
@@ -1276,6 +1345,31 @@ export class RoutingSandboxSession implements RoutableBackendSession {
1276
1345
  result?: unknown,
1277
1346
  ): Promise<void> {
1278
1347
  if (!this.deps.captureProcessOutput) return;
1348
+ // Router byte-offset pages atomically append output and advance the exact
1349
+ // expected cursor. Never send these through the legacy append-then-ack path.
1350
+ const atomicReceipt =
1351
+ record.pendingProviderReceipt ?? (typeof result === "string" ? result : undefined);
1352
+ if (
1353
+ atomicReceipt &&
1354
+ record.backend.session.captureCommandOutput &&
1355
+ record.backend.session.getProviderCommandOutput?.(atomicReceipt)?.command.kind ===
1356
+ "modal-router-v1"
1357
+ ) {
1358
+ record.pendingProviderReceipt = atomicReceipt;
1359
+ try {
1360
+ if (await record.backend.session.captureCommandOutput(atomicReceipt)) {
1361
+ delete record.pendingProviderReceipt;
1362
+ return;
1363
+ }
1364
+ } catch (error) {
1365
+ throw new RoutingMutationOutcomeUnknownError(
1366
+ "captureProcessOutput",
1367
+ "Provider output atomic capture remains pending; the provider operation was not replayed",
1368
+ { cause: error, retainedProcess: record.process },
1369
+ );
1370
+ }
1371
+ delete record.pendingProviderReceipt;
1372
+ }
1279
1373
  const providerPage = record.backend.session.getProviderCommandOutput?.(result);
1280
1374
  const structured =
1281
1375
  result && typeof result === "object"
@@ -1363,17 +1457,30 @@ export class RoutingSandboxSession implements RoutableBackendSession {
1363
1457
  op: string,
1364
1458
  mutatesWorkspace: boolean,
1365
1459
  fn: (session: RoutableBackendSession, backend: ResolvedActiveBackend) => Promise<T>,
1460
+ supervisionEligible = false,
1366
1461
  ): Promise<T> {
1367
1462
  const firstOperationObserver = this.deps.onFirstOperation;
1368
1463
  if (this.firstOperationClaimed || !firstOperationObserver) {
1369
- return await this.dispatchWithRetries(op, mutatesWorkspace, fn);
1464
+ return await this.dispatchWithRetries(
1465
+ op,
1466
+ mutatesWorkspace,
1467
+ fn,
1468
+ undefined,
1469
+ supervisionEligible,
1470
+ );
1370
1471
  }
1371
1472
  this.firstOperationClaimed = true;
1372
1473
  const startedAt = performance.now();
1373
1474
  const timing: RoutingSandboxFirstOperationTiming = { phases: {} };
1374
1475
  let outcome: RoutingSandboxPhaseOutcome = "failed";
1375
1476
  try {
1376
- const result = await this.dispatchWithRetries(op, mutatesWorkspace, fn, timing);
1477
+ const result = await this.dispatchWithRetries(
1478
+ op,
1479
+ mutatesWorkspace,
1480
+ fn,
1481
+ timing,
1482
+ supervisionEligible,
1483
+ );
1377
1484
  outcome = "completed";
1378
1485
  return result;
1379
1486
  } finally {
@@ -1390,11 +1497,34 @@ export class RoutingSandboxSession implements RoutableBackendSession {
1390
1497
  }
1391
1498
  }
1392
1499
 
1500
+ /** Classify loss on the copied backend, including read-only preflight. This
1501
+ * path never admits work or retries the failed operation on a new route. */
1502
+ private async throwProviderError(
1503
+ op: string,
1504
+ backend: ResolvedActiveBackend,
1505
+ error: unknown,
1506
+ ): Promise<never> {
1507
+ if (!isFenceError(error) && backend.sandboxId === null && this.deps.onDefaultBackendError) {
1508
+ const loss = await this.deps.onDefaultBackendError({
1509
+ op,
1510
+ error,
1511
+ kind: backend.kind,
1512
+ backend,
1513
+ });
1514
+ if (loss) {
1515
+ this.invalidate(backend);
1516
+ throw new RoutingBackendRecoveryRequiredError(op, loss.leaseEpoch, loss.recovery);
1517
+ }
1518
+ }
1519
+ throw error;
1520
+ }
1521
+
1393
1522
  private async dispatchWithRetries<T>(
1394
1523
  op: string,
1395
1524
  mutatesWorkspace: boolean,
1396
1525
  fn: (session: RoutableBackendSession, backend: ResolvedActiveBackend) => Promise<T>,
1397
1526
  firstOperationTiming?: RoutingSandboxFirstOperationTiming,
1527
+ supervisionEligible = false,
1398
1528
  ): Promise<T> {
1399
1529
  let attempt = 0;
1400
1530
  let lastError: unknown;
@@ -1416,7 +1546,35 @@ export class RoutingSandboxSession implements RoutableBackendSession {
1416
1546
  // Admission failures are NOT provider fence errors and must never enter
1417
1547
  // the retry/rebind loop. If this exact route cannot advance its durable
1418
1548
  // mutation generation, fail before the provider sees the operation.
1549
+ const supervisionReady =
1550
+ supervisionEligible &&
1551
+ backend.kind === "modal" &&
1552
+ ((await this.deps.providerSupervisionReady?.()) ?? false);
1553
+ let verifiedSupervision: { sandboxId: string; taskId: string } | undefined;
1554
+ if (supervisionReady) {
1555
+ if (
1556
+ !backend.session.verifyCommandSupervisionCapability ||
1557
+ !this.deps.afterMutation ||
1558
+ !this.deps.providerCommandPersistence
1559
+ )
1560
+ throw new Error(
1561
+ "Supervised command requires exact-instance verification and durable reservation wiring",
1562
+ );
1563
+ try {
1564
+ verifiedSupervision = await backend.session.verifyCommandSupervisionCapability();
1565
+ } catch (error) {
1566
+ // A missing helper is merely an incompatible instance. Only the
1567
+ // existing sandbox-scoped classifier may retire a missing provider.
1568
+ return await this.throwProviderError(op, backend, error);
1569
+ }
1570
+ if (
1571
+ backend.providerInstanceId &&
1572
+ verifiedSupervision.sandboxId !== backend.providerInstanceId
1573
+ )
1574
+ throw new Error("Supervision capability does not match the admitted provider instance");
1575
+ }
1419
1576
  let admission: unknown;
1577
+ let reservedProcess: RoutingRetainedProcess | undefined;
1420
1578
  if (mutatesWorkspace && this.deps.beforeMutation) {
1421
1579
  const admissionStartedAt = performance.now();
1422
1580
  let admissionWaitMs = 0;
@@ -1425,19 +1583,16 @@ export class RoutingSandboxSession implements RoutableBackendSession {
1425
1583
  admission = await this.deps.beforeMutation({
1426
1584
  op,
1427
1585
  backend,
1428
- ...(firstOperationTiming
1429
- ? {
1430
- onCaptureWait: (observation: RoutingSandboxWaitObservation) => {
1431
- admissionWaitMs += Math.max(0, observation.durationMs);
1432
- recordFirstOperationPhase(
1433
- firstOperationTiming,
1434
- "snapshotWait",
1435
- observation.durationMs,
1436
- observation.outcome,
1437
- );
1438
- },
1439
- }
1440
- : {}),
1586
+ onCaptureWait: (observation: RoutingSandboxWaitObservation) => {
1587
+ admissionWaitMs += Math.max(0, observation.durationMs);
1588
+ recordFirstOperationPhase(
1589
+ firstOperationTiming,
1590
+ "snapshotWait",
1591
+ observation.durationMs,
1592
+ observation.outcome,
1593
+ );
1594
+ this.observeCaptureWait(op, backend, "admission", observation);
1595
+ },
1441
1596
  });
1442
1597
  admissionOutcome = "completed";
1443
1598
  } finally {
@@ -1460,19 +1615,64 @@ export class RoutingSandboxSession implements RoutableBackendSession {
1460
1615
  backend,
1461
1616
  () =>
1462
1617
  withProviderCommandHandle(this.deps.providerCommandHandle?.(admission), () =>
1463
- fn(backend.session, backend),
1618
+ withCommandSupervisionReady(supervisionReady, () =>
1619
+ withSupervisedLaunchReservation(
1620
+ {
1621
+ reserve: async (command) => {
1622
+ const handle = this.deps.providerCommandHandle?.(admission);
1623
+ if (!supervisionReady || !handle || reservedProcess || !command.supervision)
1624
+ throw new Error("Invalid supervised launch reservation");
1625
+ if (
1626
+ command.sandboxId !== verifiedSupervision?.sandboxId ||
1627
+ command.taskId !== verifiedSupervision.taskId
1628
+ )
1629
+ throw new Error(
1630
+ "Modal task changed after supervision capability verification",
1631
+ );
1632
+ const process = {
1633
+ id: crypto.randomUUID(),
1634
+ providerSessionId: handle,
1635
+ providerCommand: command,
1636
+ };
1637
+ reservedProcess = process;
1638
+ const reservationResult = await this.deps.afterMutation!({
1639
+ op,
1640
+ backend,
1641
+ admission,
1642
+ outcome: "resolved",
1643
+ retainedProcess: process,
1644
+ });
1645
+ // Even a durable-but-authority-rejected reservation must
1646
+ // never dispatch user work. Its exact row remains recoverable.
1647
+ if (reservationResult)
1648
+ throw new RoutingMutationOutcomeUnknownError(
1649
+ op,
1650
+ "Supervised launch reservation lost authority before dispatch",
1651
+ { retainedProcess: process },
1652
+ );
1653
+ const persisted =
1654
+ await this.deps.providerCommandPersistence!(process).load();
1655
+ if (!persisted || !isDeepStrictEqual(persisted, command))
1656
+ throw new Error(
1657
+ "Supervised launch reservation did not retain the exact invocation",
1658
+ );
1659
+ this.registerRetainedProcess(process, backend).durable = true;
1660
+ },
1661
+ },
1662
+ () => fn(backend.session, backend),
1663
+ ),
1664
+ ),
1464
1665
  ),
1465
- firstOperationTiming
1466
- ? (observation) => {
1467
- providerWaitMs += Math.max(0, observation.durationMs);
1468
- recordFirstOperationPhase(
1469
- firstOperationTiming,
1470
- "snapshotWait",
1471
- observation.durationMs,
1472
- observation.outcome,
1473
- );
1474
- }
1475
- : undefined,
1666
+ (observation) => {
1667
+ providerWaitMs += Math.max(0, observation.durationMs);
1668
+ recordFirstOperationPhase(
1669
+ firstOperationTiming,
1670
+ "snapshotWait",
1671
+ observation.durationMs,
1672
+ observation.outcome,
1673
+ );
1674
+ this.observeCaptureWait(op, backend, "provider", observation);
1675
+ },
1476
1676
  );
1477
1677
  providerOutcome = "completed";
1478
1678
  } finally {
@@ -1484,6 +1684,34 @@ export class RoutingSandboxSession implements RoutableBackendSession {
1484
1684
  );
1485
1685
  }
1486
1686
  } catch (error) {
1687
+ if (reservedProcess) {
1688
+ if (
1689
+ error instanceof ProviderCommandStartRejectedError &&
1690
+ reservedProcess.providerCommand?.kind === "modal-router-v1"
1691
+ ) {
1692
+ const persistence = this.deps.providerCommandPersistence?.(reservedProcess);
1693
+ if (persistence?.rejectSupervisedLaunch) {
1694
+ try {
1695
+ await persistence.rejectSupervisedLaunch(reservedProcess.providerCommand);
1696
+ } catch (cause) {
1697
+ throw new RoutingMutationOutcomeUnknownError(
1698
+ op,
1699
+ "Never-started provider rejection could not settle its exact reservation",
1700
+ { cause, retainedProcess: reservedProcess },
1701
+ );
1702
+ }
1703
+ this.retainedProcesses.delete(reservedProcess.providerSessionId);
1704
+ throw error;
1705
+ }
1706
+ }
1707
+ // A retained pre-dispatch identity is never erased or converted into
1708
+ // ordinary rejected-admission settlement. Reconciliation owns it.
1709
+ throw new RoutingMutationOutcomeUnknownError(
1710
+ op,
1711
+ "Reserved supervised launch did not return; exact invocation retained and not replayed",
1712
+ { cause: error, retainedProcess: reservedProcess },
1713
+ );
1714
+ }
1487
1715
  const partialMutation = error instanceof ChannelAPartialMutationError;
1488
1716
  if (mutatesWorkspace && this.deps.afterMutation) {
1489
1717
  const settlementStartedAt = performance.now();
@@ -1522,23 +1750,7 @@ export class RoutingSandboxSession implements RoutableBackendSession {
1522
1750
  );
1523
1751
  }
1524
1752
  if (!isFenceError(error)) {
1525
- if (backend.sandboxId === null && this.deps.onDefaultBackendError) {
1526
- const loss = await this.deps.onDefaultBackendError({
1527
- op,
1528
- error,
1529
- kind: backend.kind,
1530
- backend,
1531
- });
1532
- if (loss) {
1533
- // Never replay an operation after provider disappearance. Even a
1534
- // read may race a route change, and a mutation's provider outcome
1535
- // is ambiguous. The next independently-admitted call observes the
1536
- // advanced epoch and elected recovery state.
1537
- this.invalidate(backend);
1538
- throw new RoutingBackendRecoveryRequiredError(op, loss.leaseEpoch, loss.recovery);
1539
- }
1540
- }
1541
- throw error;
1753
+ return await this.throwProviderError(op, backend, error);
1542
1754
  }
1543
1755
  this.invalidate(backend);
1544
1756
  if (mutatesWorkspace) {
@@ -1563,18 +1775,21 @@ export class RoutingSandboxSession implements RoutableBackendSession {
1563
1775
  const providerCommand =
1564
1776
  yieldedSessionId === null ? null : backend.session.getProviderCommand?.(yieldedSessionId);
1565
1777
  const retainedProcess =
1566
- yieldedSessionId === null
1778
+ reservedProcess ??
1779
+ (yieldedSessionId === null
1567
1780
  ? undefined
1568
1781
  : {
1569
1782
  id: crypto.randomUUID(),
1570
1783
  providerSessionId: yieldedSessionId,
1571
1784
  ...(providerCommand ? { providerCommand } : {}),
1572
- };
1785
+ });
1573
1786
  const retainedRecord = retainedProcess
1574
- ? this.registerRetainedProcess(retainedProcess, backend)
1787
+ ? reservedProcess
1788
+ ? this.retainedProcess(reservedProcess.providerSessionId)
1789
+ : this.registerRetainedProcess(retainedProcess, backend)
1575
1790
  : null;
1576
1791
 
1577
- if (mutatesWorkspace && this.deps.afterMutation) {
1792
+ if (mutatesWorkspace && this.deps.afterMutation && !reservedProcess) {
1578
1793
  const settlement: PendingParentPromotion = {
1579
1794
  op,
1580
1795
  backend,
@@ -1639,6 +1854,17 @@ export class RoutingSandboxSession implements RoutableBackendSession {
1639
1854
  // let process-aware methods use the copied backend identity.
1640
1855
  if (retainedRecord) {
1641
1856
  await this.captureRetainedOutput(retainedRecord, result);
1857
+ try {
1858
+ await backend.session.releaseSupervisedCommand?.(
1859
+ retainedRecord.process.providerSessionId,
1860
+ );
1861
+ } catch (cause) {
1862
+ throw new RoutingMutationOutcomeUnknownError(
1863
+ op,
1864
+ "Supervisor release is unresolved after durable retention; the exact command remains tracked and was not replayed",
1865
+ { cause, retainedProcess: retainedRecord.process },
1866
+ );
1867
+ }
1642
1868
  return result;
1643
1869
  }
1644
1870
 
@@ -1681,6 +1907,25 @@ export class RoutingSandboxSession implements RoutableBackendSession {
1681
1907
  throw lastError ?? new Error(`routing op "${op}" exhausted fence retries`);
1682
1908
  }
1683
1909
 
1910
+ private observeCaptureWait(
1911
+ op: string,
1912
+ backend: ResolvedActiveBackend,
1913
+ captureWaitStage: "admission" | "provider",
1914
+ observation: RoutingSandboxWaitObservation,
1915
+ ): void {
1916
+ try {
1917
+ this.deps.onCaptureWait?.({
1918
+ backend: backend.kind,
1919
+ op,
1920
+ outcome: observation.outcome === "completed" ? "ok" : "failed",
1921
+ durationMs: observation.durationMs,
1922
+ captureWaitStage,
1923
+ });
1924
+ } catch {
1925
+ // Capture admission and provider execution never depend on telemetry.
1926
+ }
1927
+ }
1928
+
1684
1929
  private async invokeProviderOperation<T>(
1685
1930
  op: string,
1686
1931
  backend: ResolvedActiveBackend,
@@ -1689,11 +1934,13 @@ export class RoutingSandboxSession implements RoutableBackendSession {
1689
1934
  ): Promise<T> {
1690
1935
  const startedAt = performance.now();
1691
1936
  let outcome: RoutingSandboxOperationObservation["outcome"] = "failed";
1937
+ let materializationFailureReason: MaterializationFailureReason | undefined;
1692
1938
  try {
1693
1939
  const result = await withSandboxProviderOperation(backend.session, fn, onCaptureWait);
1694
1940
  outcome = "ok";
1695
1941
  return result;
1696
1942
  } catch (error) {
1943
+ materializationFailureReason = materializationVerificationDiagnostic(error)?.reason;
1697
1944
  if (
1698
1945
  (op === "readFile" || op === "listDir" || op === "pathExists" || op === "viewImage") &&
1699
1946
  isDefinitePathNotFoundError(error)
@@ -1708,6 +1955,7 @@ export class RoutingSandboxSession implements RoutableBackendSession {
1708
1955
  op,
1709
1956
  outcome,
1710
1957
  durationMs: Math.max(0, performance.now() - startedAt),
1958
+ ...(materializationFailureReason ? { materializationFailureReason } : {}),
1711
1959
  });
1712
1960
  } catch {
1713
1961
  // Telemetry is never part of provider or durable-settlement authority.
@@ -1746,30 +1994,40 @@ export class RoutingSandboxSession implements RoutableBackendSession {
1746
1994
  // degrades via the natural fallback or RoutingUnsupportedError.
1747
1995
 
1748
1996
  async exec(args: unknown): Promise<unknown> {
1749
- return this.dispatch("exec", true, async (s) => {
1750
- if (s.exec) {
1751
- return s.exec(args);
1752
- }
1753
- // Some backends (selfhosted) only expose exec; others only execCommand.
1754
- if (s.execCommand) {
1755
- return structuredExecResultFromBanner(await s.execCommand(args));
1756
- }
1757
- throw new RoutingUnsupportedError("exec", this.cached?.kind ?? "unknown");
1758
- });
1997
+ return this.dispatch(
1998
+ "exec",
1999
+ true,
2000
+ async (s) => {
2001
+ if (s.exec) {
2002
+ return s.exec(args);
2003
+ }
2004
+ // Some backends (selfhosted) only expose exec; others only execCommand.
2005
+ if (s.execCommand) {
2006
+ return structuredExecResultFromBanner(await s.execCommand(args));
2007
+ }
2008
+ throw new RoutingUnsupportedError("exec", this.cached?.kind ?? "unknown");
2009
+ },
2010
+ eligibleForSupervision(args),
2011
+ );
1759
2012
  }
1760
2013
 
1761
2014
  async execCommand(args: unknown): Promise<string> {
1762
2015
  try {
1763
- return await this.dispatch("execCommand", true, async (s) => {
1764
- if (s.execCommand) {
1765
- return s.execCommand(args);
1766
- }
1767
- if (s.exec) {
1768
- const r = (await s.exec(args)) as { stdout?: string; output?: string };
1769
- return r.stdout ?? r.output ?? "";
1770
- }
1771
- throw new RoutingUnsupportedError("execCommand", this.cached?.kind ?? "unknown");
1772
- });
2016
+ return await this.dispatch(
2017
+ "execCommand",
2018
+ true,
2019
+ async (s) => {
2020
+ if (s.execCommand) {
2021
+ return s.execCommand(args);
2022
+ }
2023
+ if (s.exec) {
2024
+ const r = (await s.exec(args)) as { stdout?: string; output?: string };
2025
+ return r.stdout ?? r.output ?? "";
2026
+ }
2027
+ throw new RoutingUnsupportedError("execCommand", this.cached?.kind ?? "unknown");
2028
+ },
2029
+ eligibleForSupervision(args),
2030
+ );
1773
2031
  } catch (error) {
1774
2032
  // Render a terminal selfhosted fault as the tool's result (four fields, correct
1775
2033
  // verdict) instead of letting the SDK mislabel it "Please try again".
@@ -1849,7 +2107,10 @@ export class RoutingSandboxSession implements RoutableBackendSession {
1849
2107
  await this.writeStdinForProcessControl({
1850
2108
  sessionId: record.process.providerSessionId,
1851
2109
  chars: "",
1852
- yieldTimeMs: 1,
2110
+ // A near-zero provider long-poll can expire before even an already
2111
+ // completed command's terminal batch is delivered. Use the ordinary
2112
+ // bounded command-read window; never infer completion from silence.
2113
+ yieldTimeMs: 250,
1853
2114
  });
1854
2115
  return true;
1855
2116
  }
@@ -1917,6 +2178,23 @@ export class RoutingSandboxSession implements RoutableBackendSession {
1917
2178
  return await this.dispatchProcessControl(args);
1918
2179
  }
1919
2180
 
2181
+ async cancelSupervisedCommand(
2182
+ providerSessionId: number,
2183
+ reason: "provider_deadline" | "explicit_stop",
2184
+ ): Promise<boolean> {
2185
+ const record = this.retainedProcesses.get(providerSessionId);
2186
+ if (
2187
+ !record ||
2188
+ record.process.providerCommand?.kind !== "modal-router-v1" ||
2189
+ !record.process.providerCommand.supervision
2190
+ )
2191
+ return false;
2192
+ await this.ensureParentPromotion(record);
2193
+ if (!record.backend.session.cancelSupervisedCommand)
2194
+ throw new Error("Supervised command control is unavailable");
2195
+ return await record.backend.session.cancelSupervisedCommand(providerSessionId, reason);
2196
+ }
2197
+
1920
2198
  /** Empty-input eager reads belong to the model's foreground wait, while
1921
2199
  * cancellation/drain reads use the non-observing control method above. */
1922
2200
  async writeStdinForProcessRead(args: unknown): Promise<string> {
@@ -1935,6 +2213,13 @@ export class RoutingSandboxSession implements RoutableBackendSession {
1935
2213
  const exactProviderSessionId = positiveProviderSessionId(providerSessionId);
1936
2214
  if (exactProviderSessionId === null) throw new RoutingRetainedProcessNotFoundError(-1);
1937
2215
  const record = this.retainedProcess(exactProviderSessionId);
2216
+ if (
2217
+ record.process.providerCommand?.kind === "modal-router-v1" &&
2218
+ record.process.providerCommand.supervision
2219
+ )
2220
+ throw new Error(
2221
+ "Legacy process helpers are unavailable for a supervised command; use its native control protocol",
2222
+ );
1938
2223
  const priorTerminal = await this.flushPendingProcessMutation(record);
1939
2224
  if (priorTerminal !== null) return priorTerminal;
1940
2225
  if (record.pendingTerminal) {
@@ -2289,6 +2574,9 @@ async function assertProviderCanSeeMaterializedPath(
2289
2574
  session: RoutableBackendSession,
2290
2575
  path: string,
2291
2576
  ): Promise<void> {
2577
+ if (session.verifyMaterializedPath) {
2578
+ return session.verifyMaterializedPath(path, providerManifestRoot(session) ?? "/workspace");
2579
+ }
2292
2580
  const command = `test -e ${shellSingleQuote(path)} && printf %s ${shellSingleQuote(
2293
2581
  MATERIALIZED_PATH_MARKER,
2294
2582
  )}`;
@@ -2300,18 +2588,68 @@ async function assertProviderCanSeeMaterializedPath(
2300
2588
  tty: false,
2301
2589
  };
2302
2590
  let output: string;
2303
- if (session.execCommand) {
2304
- output = await session.execCommand(args);
2305
- } else if (session.exec) {
2306
- output = formatExecResult(await session.exec(args));
2307
- } else {
2591
+ let result: unknown;
2592
+ if (!session.execCommand && !session.exec) {
2308
2593
  throw new RoutingUnsupportedError("materializeEntry.verify", "unknown");
2309
2594
  }
2310
- if (!output.includes(MATERIALIZED_PATH_MARKER)) {
2311
- throw new Error(
2312
- `Sandbox materialization completed but the provider cannot read the destination path: ${path}`,
2313
- );
2595
+ try {
2596
+ result = session.execCommand ? await session.execCommand(args) : await session.exec!(args);
2597
+ } catch (cause) {
2598
+ retainMaterializationVerificationDiagnostic(cause, {
2599
+ reason: "command_error",
2600
+ path,
2601
+ workdir: args.workdir,
2602
+ command,
2603
+ output: null,
2604
+ exitCode: null,
2605
+ providerSessionId: null,
2606
+ causeMessage: cause instanceof Error ? cause.message : String(cause),
2607
+ });
2608
+ throw cause;
2314
2609
  }
2610
+ try {
2611
+ output = formatExecResult(result);
2612
+ } catch (cause) {
2613
+ const record = result && typeof result === "object" ? (result as Record<string, unknown>) : {};
2614
+ const returnedOutput = [record.output, record.stderr, record.stdout]
2615
+ .filter((value): value is string => typeof value === "string")
2616
+ .join("\n");
2617
+ throw new SandboxMaterializationVerificationError({
2618
+ reason: "invalid_response",
2619
+ path,
2620
+ workdir: args.workdir,
2621
+ command,
2622
+ output: returnedOutput || null,
2623
+ exitCode: null,
2624
+ providerSessionId: null,
2625
+ causeMessage: cause instanceof Error ? cause.message : String(cause),
2626
+ });
2627
+ }
2628
+ const status = parseExecResponseBanner(output);
2629
+ const markerPresent = stripExecBanner(output).includes(MATERIALIZED_PATH_MARKER);
2630
+ if (
2631
+ markerPresent &&
2632
+ (status.kind === "absent" || (status.kind === "exited" && status.exitCode === 0))
2633
+ ) {
2634
+ return;
2635
+ }
2636
+ const reason: MaterializationFailureReason =
2637
+ status.kind === "running"
2638
+ ? "command_pending"
2639
+ : status.kind === "exited" && status.exitCode === 1
2640
+ ? "path_not_visible"
2641
+ : status.kind === "exited" && status.exitCode !== 0
2642
+ ? "command_failed"
2643
+ : "invalid_response";
2644
+ throw new SandboxMaterializationVerificationError({
2645
+ reason,
2646
+ path,
2647
+ workdir: args.workdir,
2648
+ command,
2649
+ output,
2650
+ exitCode: status.kind === "exited" ? status.exitCode : null,
2651
+ providerSessionId: status.kind === "running" ? status.sessionId : null,
2652
+ });
2315
2653
  }
2316
2654
 
2317
2655
  function providerManifestRoot(session: RoutableBackendSession): string | null {