@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
@@ -0,0 +1,248 @@
1
+ import { Client, Metadata, credentials, status, type ServiceError } from "@grpc/grpc-js";
2
+ import protobuf from "protobufjs";
3
+ import { ProviderCommandStartRejectedError } from "../provider-command-session";
4
+
5
+ // Narrow wire projection of Modal 0.9.0's task_command_router.proto. The public
6
+ // SDK does not expose resumable byte offsets or cancellable command RPCs. Keep
7
+ // this boundary pinned and exercise it against an actual gRPC server in tests.
8
+ // Neither the JWT nor the router URL belongs in a durable command locator.
9
+ export const modalRouterWire = protobuf.parse(`syntax = "proto3";
10
+ message Identity { string task_id = 1; string exec_id = 2; }
11
+ message Pty {
12
+ bool enabled = 1; uint32 winsz_rows = 2; uint32 winsz_cols = 3;
13
+ string env_term = 4; string env_colorterm = 5; string env_term_program = 6;
14
+ int32 pty_type = 7; bool no_terminate_on_idle_stdin = 8;
15
+ }
16
+ message Start {
17
+ string task_id = 1; string exec_id = 2; repeated string command_args = 3;
18
+ int32 stdout_config = 4; int32 stderr_config = 5;
19
+ optional uint32 timeout_secs = 6; optional string workdir = 7;
20
+ repeated string secret_ids = 8; optional Pty pty_info = 9;
21
+ bool runtime_debug = 10; string container_id = 11; map<string,string> env = 12;
22
+ }
23
+ message Read {
24
+ string task_id = 1; string exec_id = 2; uint64 offset = 3; int32 file_descriptor = 4;
25
+ }
26
+ message Data { bytes data = 1; }
27
+ message Poll { optional int32 code = 1; optional int32 signal = 2; }
28
+ message Write {
29
+ string task_id = 1; string exec_id = 2; uint64 offset = 3; bytes data = 4; bool eof = 5;
30
+ }
31
+ message Empty {}
32
+ `).root;
33
+
34
+ const prefix = "/modal.task_command_router.TaskCommandRouter/";
35
+ const maxPageBytes = 64 * 1024;
36
+ const maxWireBytes = 4 * 1024 * 1024;
37
+
38
+ /** Only constructed at the authenticated Start RPC boundary. Transport loss,
39
+ * deadline, cancellation and UNKNOWN are deliberately not rejection proof. */
40
+ export class ModalCommandStartRejectedError extends ProviderCommandStartRejectedError {
41
+ constructor(
42
+ readonly code: number,
43
+ cause: unknown,
44
+ ) {
45
+ super(cause);
46
+ this.name = "ModalCommandStartRejectedError";
47
+ }
48
+ }
49
+
50
+ export type ModalRouterIdentity = { taskId: string; execId: string };
51
+ export type ModalRouterAccess = { url: string; jwt: string };
52
+ export type ModalRouterStart = ModalRouterIdentity & {
53
+ commandArgs: string[];
54
+ workdir: string;
55
+ env: Record<string, string>;
56
+ ptyInfo?: {
57
+ enabled: boolean;
58
+ winszRows: number;
59
+ winszCols: number;
60
+ envTerm: string;
61
+ ptyType: number;
62
+ noTerminateOnIdleStdin: boolean;
63
+ };
64
+ };
65
+
66
+ function encode(type: string, value: object): Buffer {
67
+ const codec = modalRouterWire.lookupType(type);
68
+ return Buffer.from(codec.encode(codec.fromObject(value)).finish());
69
+ }
70
+
71
+ /** A single authenticated transport, owned and closed by its command adapter.
72
+ * Reads may stop at a byte boundary because the next read resumes at that exact
73
+ * offset. Mutations are never retried by this layer, including ambiguous starts.
74
+ */
75
+ export class ModalCommandRouterWire {
76
+ private readonly client: Client;
77
+ private readonly metadata: Metadata;
78
+ private closed = false;
79
+
80
+ constructor(access: ModalRouterAccess, trustedRoots?: Buffer) {
81
+ const url = new URL(access.url);
82
+ if (url.protocol !== "https:" || url.username || url.password || !access.jwt)
83
+ throw new Error("Modal command router requires authenticated TLS");
84
+ this.client = new Client(url.host, credentials.createSsl(trustedRoots), {
85
+ "grpc.max_receive_message_length": maxWireBytes,
86
+ "grpc.max_send_message_length": maxWireBytes,
87
+ "grpc.enable_retries": 0,
88
+ });
89
+ this.metadata = new Metadata();
90
+ this.metadata.set("authorization", `Bearer ${access.jwt}`);
91
+ }
92
+
93
+ close(): void {
94
+ this.closed = true;
95
+ this.client.close();
96
+ }
97
+
98
+ private async unary(
99
+ method: "TaskExecStart" | "TaskExecStdinWrite" | "TaskExecPoll",
100
+ requestType: string,
101
+ responseType: string,
102
+ request: object,
103
+ signal?: AbortSignal,
104
+ ): Promise<protobuf.Message> {
105
+ signal?.throwIfAborted();
106
+ if (this.closed) throw new Error("Modal command router is closed");
107
+ return await new Promise((resolve, reject) => {
108
+ const call = this.client.makeUnaryRequest(
109
+ prefix + method,
110
+ (value: object) => encode(requestType, value),
111
+ (bytes) => modalRouterWire.lookupType(responseType).decode(bytes),
112
+ request,
113
+ this.metadata,
114
+ { deadline: Date.now() + 30_000 },
115
+ (error, result) => {
116
+ signal?.removeEventListener("abort", abort);
117
+ if (signal?.aborted) reject(signal.reason);
118
+ else if (error) reject(error);
119
+ else if (result) resolve(result);
120
+ else reject(new Error("Modal command router returned no response"));
121
+ },
122
+ );
123
+ const abort = () => call.cancel();
124
+ signal?.addEventListener("abort", abort, { once: true });
125
+ if (signal?.aborted) abort();
126
+ });
127
+ }
128
+
129
+ async start(request: ModalRouterStart, signal?: AbortSignal): Promise<void> {
130
+ try {
131
+ await this.unary(
132
+ "TaskExecStart",
133
+ "Start",
134
+ "Empty",
135
+ {
136
+ ...request,
137
+ stdoutConfig: 1,
138
+ stderrConfig: 1,
139
+ },
140
+ signal,
141
+ );
142
+ } catch (error) {
143
+ const code = (error as Partial<ServiceError> | null)?.code;
144
+ if (
145
+ typeof code === "number" &&
146
+ [
147
+ status.INVALID_ARGUMENT,
148
+ status.NOT_FOUND,
149
+ status.PERMISSION_DENIED,
150
+ status.UNAUTHENTICATED,
151
+ status.UNIMPLEMENTED,
152
+ ].includes(code)
153
+ )
154
+ throw new ModalCommandStartRejectedError(code, error);
155
+ throw error;
156
+ }
157
+ }
158
+
159
+ async write(
160
+ identity: ModalRouterIdentity,
161
+ offset: number,
162
+ data: Uint8Array,
163
+ signal?: AbortSignal,
164
+ ): Promise<void> {
165
+ if (!Number.isSafeInteger(offset) || offset < 0)
166
+ throw new Error("Invalid Modal stdin byte offset");
167
+ await this.unary(
168
+ "TaskExecStdinWrite",
169
+ "Write",
170
+ "Empty",
171
+ { ...identity, offset, data, eof: false },
172
+ signal,
173
+ );
174
+ }
175
+
176
+ async poll(identity: ModalRouterIdentity, signal?: AbortSignal): Promise<number | null> {
177
+ const value = await this.unary("TaskExecPoll", "Identity", "Poll", identity, signal);
178
+ const decoded = modalRouterWire.lookupType("Poll").toObject(value) as {
179
+ code?: number;
180
+ signal?: number;
181
+ };
182
+ if (decoded.code !== undefined) return decoded.code;
183
+ if (decoded.signal !== undefined) return 128 + decoded.signal;
184
+ return null;
185
+ }
186
+
187
+ async read(
188
+ identity: ModalRouterIdentity,
189
+ stream: "stdout" | "stderr",
190
+ offset: number,
191
+ waitMs: number,
192
+ signal?: AbortSignal,
193
+ ): Promise<{ bytes: Buffer; eof: boolean }> {
194
+ signal?.throwIfAborted();
195
+ if (this.closed) throw new Error("Modal command router is closed");
196
+ if (
197
+ !Number.isSafeInteger(offset) ||
198
+ offset < 0 ||
199
+ !Number.isFinite(waitMs) ||
200
+ waitMs < 0 ||
201
+ waitMs > 50_000
202
+ )
203
+ throw new Error("Invalid Modal output read bounds");
204
+ const chunks: Buffer[] = [];
205
+ let length = 0;
206
+ let limited = false;
207
+ return await new Promise((resolve, reject) => {
208
+ const call = this.client.makeServerStreamRequest(
209
+ prefix + "TaskExecStdioRead",
210
+ (value: object) => encode("Read", value),
211
+ (bytes) =>
212
+ modalRouterWire.lookupType("Data").decode(bytes) as protobuf.Message & {
213
+ data: Uint8Array;
214
+ },
215
+ { ...identity, offset, fileDescriptor: stream === "stdout" ? 0 : 1 },
216
+ this.metadata,
217
+ { deadline: Date.now() + Math.max(1, waitMs) },
218
+ );
219
+ const abort = () => call.cancel();
220
+ const finish = (eof: boolean, error?: unknown) => {
221
+ signal?.removeEventListener("abort", abort);
222
+ if (signal?.aborted) reject(signal.reason);
223
+ else if (error) reject(error);
224
+ else resolve({ bytes: Buffer.concat(chunks, length), eof });
225
+ };
226
+ call.on("data", (value: { data: Uint8Array }) => {
227
+ if (limited) return;
228
+ const part = Buffer.from(value.data).subarray(0, maxPageBytes - length);
229
+ if (part.length) {
230
+ chunks.push(part);
231
+ length += part.length;
232
+ }
233
+ if (length === maxPageBytes) {
234
+ limited = true;
235
+ call.cancel();
236
+ }
237
+ });
238
+ call.once("error", (error: ServiceError) => {
239
+ if ((limited && error.code === status.CANCELLED) || error.code === status.DEADLINE_EXCEEDED)
240
+ finish(false);
241
+ else finish(false, error);
242
+ });
243
+ call.once("end", () => finish(!limited));
244
+ signal?.addEventListener("abort", abort, { once: true });
245
+ if (signal?.aborted) abort();
246
+ });
247
+ }
248
+ }
@@ -1,4 +1,5 @@
1
1
  import { randomUUID } from "node:crypto";
2
+ import { isDeepStrictEqual } from "node:util";
2
3
  import { truncateOutput } from "@openai/agents-core/sandbox/internal";
3
4
  import type { ChannelASession } from "../channel-a";
4
5
  import { ModalProcessObservationUnavailableError } from "../errors";
@@ -11,6 +12,7 @@ import {
11
12
  admittedProviderCommandHandle,
12
13
  } from "../provider-command-session";
13
14
  import type { ModalCommandControl, ModalProviderCommand } from "./modal-command-control";
15
+ import { verifyModalMaterializedPath } from "./modal-materialization-verification";
14
16
 
15
17
  type Entry = {
16
18
  command: ModalProviderCommand;
@@ -19,9 +21,14 @@ type Entry = {
19
21
 
20
22
  function sameExecution(a: ModalProviderCommand, b: ModalProviderCommand): boolean {
21
23
  return (
24
+ a.kind === b.kind &&
22
25
  a.sandboxId === b.sandboxId &&
23
26
  a.taskId === b.taskId &&
24
27
  a.execId === b.execId &&
28
+ isDeepStrictEqual(
29
+ "supervision" in a ? a.supervision : undefined,
30
+ "supervision" in b ? b.supervision : undefined,
31
+ ) &&
25
32
  Boolean(a.pty) === Boolean(b.pty)
26
33
  );
27
34
  }
@@ -33,14 +40,23 @@ export function installModalCommandSession(
33
40
  session: ProviderCommandSession & {
34
41
  execCommand?: ChannelASession["execCommand"];
35
42
  writeStdin?: ChannelASession["writeStdin"];
43
+ verifyMaterializedPath?: (path: string, workdir: string) => Promise<void>;
36
44
  },
37
- control: ModalCommandControl,
45
+ control: Pick<ModalCommandControl, "start" | "read" | "readProbe" | "write"> &
46
+ Partial<Pick<ModalCommandControl, "supervisionControl" | "verifySupervisionCapability">>,
38
47
  ): void {
39
48
  markTypedExecHandleLoss(session);
40
49
  const originalExec = session.execCommand?.bind(session);
41
50
  const originalWrite = session.writeStdin?.bind(session);
42
51
  const cancelLegacyStart = session.cancelPendingExecCommand?.bind(session);
43
52
  const pendingStarts = new Set<AbortController>();
53
+ session.verifyCommandSupervisionCapability = async () => {
54
+ if (!control.verifySupervisionCapability)
55
+ throw new Error("Modal instance lacks supervised command capability verification");
56
+ return await control.verifySupervisionCapability();
57
+ };
58
+ session.verifyMaterializedPath = (path, workdir) =>
59
+ verifyModalMaterializedPath(control, path, workdir, pendingStarts);
44
60
  const entries = new Map<number, Entry>();
45
61
  // Current SDK setup commands are not retained/admitted commands. Give their
46
62
  // live observer handles a disjoint, adapter-local range; never use a missing
@@ -48,6 +64,64 @@ export function installModalCommandSession(
48
64
  const setupHandles = new Map<number, number>();
49
65
  let nextSetupHandle = MAX_PROVIDER_COMMAND_HANDLE + 1;
50
66
  const receipts = new Map<string, { handle: number; page: ProviderCommandOutput }>();
67
+ const atomicallyCaptured = new WeakSet<ProviderCommandOutput>();
68
+
69
+ const supervise = async (entry: Entry, release = false): Promise<void> => {
70
+ if (entry.command.kind !== "modal-router-v1" || !entry.command.supervision) return;
71
+ const persistence = entry.persistence;
72
+ if (
73
+ !persistence?.recordSupervisionReceipt ||
74
+ !persistence.loadSupervisionReceipt ||
75
+ !persistence.cancellationRequested ||
76
+ !control.supervisionControl
77
+ )
78
+ throw new Error("Supervised command requires durable control persistence");
79
+ let receipt = await persistence.loadSupervisionReceipt();
80
+ if (!receipt) {
81
+ const action = (await persistence.cancellationRequested())
82
+ ? "cancel"
83
+ : release
84
+ ? "release"
85
+ : "status";
86
+ const observation = await control.supervisionControl(entry.command, action);
87
+ receipt = observation.receipt ?? null;
88
+ if (receipt) await persistence.recordSupervisionReceipt(receipt);
89
+ }
90
+ if (receipt) {
91
+ // Lost ACK responses are safe to retry. A dead control socket is not
92
+ // terminal proof: the subsequent authenticated provider read still must
93
+ // prove exit and commit every output byte.
94
+ await control
95
+ .supervisionControl(entry.command, "ack", receipt.receiptId)
96
+ .catch(() => undefined);
97
+ }
98
+ };
99
+
100
+ const captureRouterPage = async (entry: Entry, page: ProviderCommandOutput): Promise<boolean> => {
101
+ if (
102
+ page.command.kind !== "modal-router-v1" ||
103
+ !page.expected ||
104
+ !entry.persistence?.captureRouterPage
105
+ )
106
+ throw new Error("Modal byte-offset output requires atomic retained capture");
107
+ const result = await entry.persistence.captureRouterPage({
108
+ expected: page.expected,
109
+ command: page.command,
110
+ stdout: page.chunks
111
+ .filter((chunk) => chunk.stream === "stdout")
112
+ .map((chunk) => chunk.text)
113
+ .join(""),
114
+ stderr: page.chunks
115
+ .filter((chunk) => chunk.stream === "stderr")
116
+ .map((chunk) => chunk.text)
117
+ .join(""),
118
+ });
119
+ if (!sameExecution(entry.command, result.command))
120
+ throw new Error("Modal output capture changed execution identity");
121
+ entry.command = result.command;
122
+ if (result.captured) atomicallyCaptured.add(page);
123
+ return result.captured;
124
+ };
51
125
 
52
126
  const setupPage = (raw: string, handle: number, sdkHandle: number): string => {
53
127
  const banner = parseExecResponseBanner(raw);
@@ -97,8 +171,30 @@ export function installModalCommandSession(
97
171
  if (!retained || !sameExecution(entry.command, retained))
98
172
  throw new Error("Original Modal command identity is unavailable");
99
173
  entry.command = retained;
174
+ // Output observation stays available when control/proof persistence is
175
+ // unavailable. Terminal exposure below remains fenced by durable proof.
176
+ await supervise(entry).catch(() => undefined);
177
+ }
178
+ let page = await control.read(entry.command, yieldTimeMs, signal);
179
+ if (entry.persistence && page.command.kind === "modal-router-v1") {
180
+ // A stale concurrent observer must reread from the committed cursor before
181
+ // exposing a terminal receipt; its uncommitted tail is not settlement proof.
182
+ let captured = false;
183
+ for (let attempt = 0; attempt < 3; attempt++) {
184
+ if (await captureRouterPage(entry, page)) {
185
+ captured = true;
186
+ break;
187
+ }
188
+ if (attempt < 2) page = await control.read(entry.command, yieldTimeMs, signal);
189
+ }
190
+ if (!captured) throw new Error("Modal command output cursor changed during bounded capture");
191
+ }
192
+ if (page.command.kind === "modal-router-v1" && page.command.supervision) {
193
+ const receipt = await entry.persistence?.loadSupervisionReceipt?.();
194
+ // The supervisor exits 0 after ACK; that is provider terminal evidence,
195
+ // not the original shell result. Retain both without conflating them.
196
+ page = { ...page, exitCode: receipt && page.exitCode === 0 ? receipt.leaderExitCode : null };
100
197
  }
101
- const page = await control.read(entry.command, yieldTimeMs, signal);
102
198
  // The receipt is generated here, not parsed from command output. Its only
103
199
  // purpose is correlating this return value with a trusted in-memory page.
104
200
  return formatPage(handle, page, maxOutputTokens);
@@ -122,8 +218,17 @@ export function installModalCommandSession(
122
218
  const cancellation = new AbortController();
123
219
  pendingStarts.add(cancellation);
124
220
  try {
125
- const entry = { command: await control.start(args, cancellation.signal) };
221
+ const command = await control.start(args, cancellation.signal);
222
+ // Pre-dispatch reservation can already bind protected persistence.
223
+ const entry = entries.get(handle) ?? { command };
224
+ if (!sameExecution(entry.command, command))
225
+ throw new Error("Reserved Modal command changed during dispatch");
126
226
  entries.set(handle, entry);
227
+ if (entry.command.kind === "modal-router-v1" && entry.command.supervision) {
228
+ // The launch cannot run user code until the routing layer commits
229
+ // initial retention and calls releaseSupervisedCommand.
230
+ return formatPage(handle, { command: entry.command, chunks: [], exitCode: null });
231
+ }
127
232
  try {
128
233
  return await read(
129
234
  handle,
@@ -160,6 +265,25 @@ export function installModalCommandSession(
160
265
  const entry = entries.get(handle);
161
266
  return entry ? structuredClone(entry.command) : null;
162
267
  };
268
+ session.releaseSupervisedCommand = async (handle) => {
269
+ const entry = entries.get(handle);
270
+ if (!entry || entry.command.kind !== "modal-router-v1" || !entry.command.supervision) return;
271
+ const retained = await entry.persistence?.load();
272
+ if (!retained || !sameExecution(entry.command, retained))
273
+ throw new Error("Supervisor release requires committed initial retention");
274
+ entry.command = retained;
275
+ await supervise(entry, true);
276
+ };
277
+ session.cancelSupervisedCommand = async (handle, reason) => {
278
+ const entry = entries.get(handle);
279
+ if (!entry || entry.command.kind !== "modal-router-v1" || !entry.command.supervision)
280
+ return false;
281
+ if (!entry.persistence?.requestCancellation)
282
+ throw new Error("Supervisor cancellation requires durable intent");
283
+ await entry.persistence.requestCancellation(reason);
284
+ await supervise(entry);
285
+ return true;
286
+ };
163
287
  session.bindProviderCommand = (handle, command, persistence) => {
164
288
  if (!Number.isSafeInteger(handle) || handle <= 0 || handle > MAX_PROVIDER_COMMAND_HANDLE)
165
289
  throw new Error("Invalid retained Modal command handle");
@@ -170,6 +294,55 @@ export function installModalCommandSession(
170
294
  };
171
295
  session.getProviderCommandOutput = (result) =>
172
296
  typeof result === "string" ? (receipts.get(result)?.page ?? null) : null;
297
+ session.captureCommandOutput = async (result) => {
298
+ const receipt = receipts.get(result);
299
+ if (!receipt || receipt.page.command.kind !== "modal-router-v1") return false;
300
+ if (
301
+ receipt.page.command.supervision &&
302
+ !receipt.page.expected &&
303
+ receipt.page.exitCode === null &&
304
+ receipt.page.chunks.length === 0
305
+ ) {
306
+ // Idle-launch receipt carries identity only; there are no bytes to ACK.
307
+ receipts.delete(result);
308
+ return true;
309
+ }
310
+ if (atomicallyCaptured.has(receipt.page)) {
311
+ receipts.delete(result);
312
+ return true;
313
+ }
314
+ const entry = entries.get(receipt.handle);
315
+ if (!entry?.persistence?.captureRouterPage || !receipt.page.expected)
316
+ throw new Error("Modal byte-offset output requires atomic retained capture");
317
+ if (!(await captureRouterPage(entry, receipt.page))) {
318
+ const target = receipt.page.command;
319
+ const covered = () =>
320
+ entry.command.kind === "modal-router-v1" &&
321
+ (["stdout", "stderr"] as const).every((stream) => {
322
+ const current = entry.command.streams[stream];
323
+ // This narrowing stays inside the discriminator's synchronous scope.
324
+ if (!("byteOffset" in current)) return false;
325
+ const wanted = target.streams[stream];
326
+ return (
327
+ current.byteOffset >= wanted.byteOffset &&
328
+ (!wanted.eof || current.eof) &&
329
+ (wanted.exitCode === null || current.exitCode === wanted.exitCode)
330
+ );
331
+ });
332
+ for (let attempt = 0; !covered() && attempt < 3; attempt++) {
333
+ // Initial promotion can race reconciliation before background adoption.
334
+ // Re-read, never re-execute; only a committed cursor covering the original
335
+ // page makes that page's terminal proof eligible for settlement.
336
+ const refreshedReceipt = await read(receipt.handle, entry, 250);
337
+ const refreshed = receipts.get(refreshedReceipt);
338
+ if (refreshed && atomicallyCaptured.has(refreshed.page)) receipts.delete(refreshedReceipt);
339
+ }
340
+ if (!covered())
341
+ throw new Error("Initial Modal output tail remains uncommitted after bounded capture");
342
+ }
343
+ receipts.delete(result);
344
+ return true;
345
+ };
173
346
  session.acknowledgeCommandOutput = async (result) => {
174
347
  const receipt = receipts.get(result);
175
348
  if (!receipt) return;
@@ -197,7 +370,7 @@ export function installModalCommandSession(
197
370
  const retained = await entry.persistence.load();
198
371
  if (!retained || !sameExecution(entry.command, retained))
199
372
  throw new Error("Original Modal command identity is unavailable");
200
- const index = await entry.persistence.reserveInput();
373
+ const index = await entry.persistence.reserveInput(Buffer.byteLength(args.chars));
201
374
  await control.write(retained, args.chars, index);
202
375
  }
203
376
  return read(args.sessionId, entry, args.yieldTimeMs ?? 250, args.maxOutputTokens);