@opengeni/runtime 4.6.0 → 4.7.0-canary.37090756480001
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-instructions/modules/media.d.ts +3 -0
- package/dist/anthropic-messages.d.ts +13 -3
- package/dist/anthropic-request-error.d.ts +10 -0
- package/dist/assets/codemode-client.json +1 -1
- package/dist/{chunk-A22BKN4P.js → chunk-LJNCGUMB.js} +3 -3
- package/dist/chunk-LJNCGUMB.js.map +1 -0
- package/dist/{chunk-GTWKWJ67.js → chunk-P2OVGKDG.js} +505 -100
- package/dist/chunk-P2OVGKDG.js.map +1 -0
- package/dist/{chunk-D5DPVA2C.js → chunk-R25L27T3.js} +2047 -1275
- package/dist/chunk-R25L27T3.js.map +1 -0
- package/dist/claude-subscription-usage.d.ts +14 -4
- package/dist/context-compaction.d.ts +7 -7
- package/dist/index.d.ts +2 -0
- package/dist/index.js +13 -3
- package/dist/interaction-tools.d.ts +1 -1
- package/dist/mcp-network.js +1 -1
- package/dist/runtime-skills.d.ts +1 -0
- package/dist/sandbox/channel-a.d.ts +3 -0
- package/dist/sandbox/index.d.ts +3 -1
- package/dist/sandbox/index.js +7 -1
- package/dist/sandbox/provider-command-session.d.ts +21 -0
- package/dist/sandbox/providers/modal-command-argv.d.ts +4 -0
- package/dist/sandbox/providers/modal-command-control.d.ts +17 -5
- package/dist/sandbox/providers/modal-command-observation-errors.d.ts +3 -0
- package/dist/sandbox/providers/modal-command-raw-page.d.ts +100 -0
- package/dist/sandbox/providers/modal-command-router-wire.d.ts +17 -0
- package/dist/sandbox/routing/routing-session.d.ts +10 -0
- package/dist/sandbox/turn-tool-cancellation.d.ts +4 -0
- package/dist/workspace-tool-gateway.js +3 -3
- package/package.json +12 -12
- package/src/agent-instructions/PROMPT_CHANGELOG.md +23 -1
- package/src/agent-instructions/compose.ts +2 -0
- package/src/agent-instructions/modules/media.ts +20 -0
- package/src/agent-instructions/runtime-mechanics.ts +8 -0
- package/src/anthropic-messages.ts +223 -31
- package/src/anthropic-request-error.ts +45 -0
- package/src/bundled_default_skills/opengeni-client/SKILL.md +9 -7
- package/src/bundled_default_skills/opengeni-client/references/agent-recipes.md +1 -1
- package/src/bundled_default_skills/opengeni-client/references/api-workflows.md +61 -0
- package/src/bundled_default_skills/opengeni-client/references/compatibility-and-troubleshooting.md +86 -0
- package/src/bundled_default_skills/opengeni-client/references/data-tools-and-credentials.md +82 -0
- package/src/bundled_default_skills/opengeni-client/references/discovery-and-autonomy.md +32 -25
- package/src/bundled_default_skills/opengeni-client/references/external-users-and-connect.md +17 -0
- package/src/bundled_default_skills/opengeni-client/references/product-shapes-and-ui.md +156 -0
- package/src/bundled_schedule_skills/opengeni-schedules/SKILL.md +56 -0
- package/src/claude-subscription-usage.ts +55 -9
- package/src/context-compaction.ts +16 -9
- package/src/index.ts +85 -18
- package/src/interaction-tools.ts +113 -3
- package/src/lazy-tool-transport.ts +26 -10
- package/src/mcp-network.ts +4 -2
- package/src/model-provider-client.ts +11 -3
- package/src/model-provider-routing.ts +4 -0
- package/src/runtime-skills.ts +8 -0
- package/src/sandbox/channel-a.ts +3 -0
- package/src/sandbox/index.ts +11 -1
- package/src/sandbox/provider-command-session.ts +58 -0
- package/src/sandbox/provider-errors.ts +43 -9
- package/src/sandbox/providers/modal-command-argv.ts +20 -0
- package/src/sandbox/providers/modal-command-control.ts +422 -116
- package/src/sandbox/providers/modal-command-observation-errors.ts +65 -0
- package/src/sandbox/providers/modal-command-raw-page.ts +147 -0
- package/src/sandbox/providers/modal-command-router-wire.ts +104 -12
- package/src/sandbox/providers/modal-command-session.ts +26 -4
- package/src/sandbox/providers/modal-legacy-command-control.ts +2 -9
- package/src/sandbox/providers/modal-materialization-verification.ts +70 -6
- package/src/sandbox/routing/routing-session.ts +140 -41
- package/src/sandbox/run-credentials.ts +62 -19
- package/src/sandbox/turn-tool-cancellation.ts +159 -42
- package/dist/chunk-A22BKN4P.js.map +0 -1
- package/dist/chunk-D5DPVA2C.js.map +0 -1
- package/dist/chunk-GTWKWJ67.js.map +0 -1
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/** Only used inside a read-only provider RPC boundary. This is deliberately
|
|
2
|
+
* not a Start classifier: remote DNS prose never proves non-dispatch. */
|
|
3
|
+
export function isModalCommandObservationTransportError(error: unknown): boolean {
|
|
4
|
+
const seen = new Map<object, boolean>();
|
|
5
|
+
let nodes = 0;
|
|
6
|
+
const own = (value: object, key: string): unknown => {
|
|
7
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
8
|
+
if (descriptor && !("value" in descriptor)) throw new Error("Unreadable provider error graph");
|
|
9
|
+
return descriptor && "value" in descriptor ? descriptor.value : undefined;
|
|
10
|
+
};
|
|
11
|
+
const visit = (value: unknown, depth: number): boolean => {
|
|
12
|
+
if (!value || typeof value !== "object" || depth > 6 || ++nodes > 32) return false;
|
|
13
|
+
if (seen.has(value)) return seen.get(value)!;
|
|
14
|
+
// A cycle sees false until this bounded branch has been fully evaluated.
|
|
15
|
+
seen.set(value, false);
|
|
16
|
+
try {
|
|
17
|
+
const code = own(value, "code");
|
|
18
|
+
const nested: unknown[] = [own(value, "cause"), own(value, "error")].filter(
|
|
19
|
+
(item) => item !== undefined,
|
|
20
|
+
);
|
|
21
|
+
const errors = own(value, "errors");
|
|
22
|
+
if (errors !== undefined) {
|
|
23
|
+
if (!Array.isArray(errors) || !errors.length || errors.length > 16) return false;
|
|
24
|
+
for (let index = 0; index < errors.length; index++) nested.push(own(errors, String(index)));
|
|
25
|
+
}
|
|
26
|
+
if (code !== undefined) {
|
|
27
|
+
const normalized = typeof code === "string" ? code.trim().toUpperCase() : code;
|
|
28
|
+
if (
|
|
29
|
+
![
|
|
30
|
+
1,
|
|
31
|
+
2,
|
|
32
|
+
4,
|
|
33
|
+
13,
|
|
34
|
+
14,
|
|
35
|
+
"1",
|
|
36
|
+
"2",
|
|
37
|
+
"4",
|
|
38
|
+
"13",
|
|
39
|
+
"14",
|
|
40
|
+
"CANCELLED",
|
|
41
|
+
"UNKNOWN",
|
|
42
|
+
"DEADLINE_EXCEEDED",
|
|
43
|
+
"INTERNAL",
|
|
44
|
+
"UNAVAILABLE",
|
|
45
|
+
"ECONNRESET",
|
|
46
|
+
"ECONNREFUSED",
|
|
47
|
+
"ETIMEDOUT",
|
|
48
|
+
"EAI_AGAIN",
|
|
49
|
+
"ENOTFOUND",
|
|
50
|
+
].includes(normalized as never)
|
|
51
|
+
)
|
|
52
|
+
return false;
|
|
53
|
+
const result = nested.length === 0 || nested.every((item) => visit(item, depth + 1));
|
|
54
|
+
seen.set(value, result);
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
const result = nested.length > 0 && nested.every((item) => visit(item, depth + 1));
|
|
58
|
+
seen.set(value, result);
|
|
59
|
+
return result;
|
|
60
|
+
} catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
return visit(error, 0);
|
|
65
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { ModalRouterProviderCommand } from "@opengeni/contracts";
|
|
4
|
+
import type { ProviderCommandOutput } from "../provider-command-session";
|
|
5
|
+
import { decodePage } from "./modal-legacy-command-control";
|
|
6
|
+
import { MODAL_ROUTER_READ_PAGE_BYTES } from "./modal-command-router-wire";
|
|
7
|
+
|
|
8
|
+
const RawStream = z
|
|
9
|
+
.object({
|
|
10
|
+
bytes: z
|
|
11
|
+
.instanceof(Uint8Array)
|
|
12
|
+
.transform((bytes): Uint8Array<ArrayBufferLike> => bytes)
|
|
13
|
+
.refine(
|
|
14
|
+
(bytes) => bytes.byteLength <= MODAL_ROUTER_READ_PAGE_BYTES,
|
|
15
|
+
"Raw page exceeded its bound",
|
|
16
|
+
),
|
|
17
|
+
eof: z.boolean(),
|
|
18
|
+
})
|
|
19
|
+
.strict();
|
|
20
|
+
const RawExit = z.discriminatedUnion("source", [
|
|
21
|
+
z.object({ source: z.literal("router_poll"), code: z.number().int().nullable() }).strict(),
|
|
22
|
+
z.object({ source: z.literal("retained_terminal"), code: z.number().int() }).strict(),
|
|
23
|
+
]);
|
|
24
|
+
const RawPage = z
|
|
25
|
+
.object({
|
|
26
|
+
protocol: z.literal("modal-router-raw-page-v1"),
|
|
27
|
+
expected: ModalRouterProviderCommand,
|
|
28
|
+
command: ModalRouterProviderCommand,
|
|
29
|
+
streams: z.object({ stdout: RawStream, stderr: RawStream }).strict(),
|
|
30
|
+
exit: RawExit,
|
|
31
|
+
})
|
|
32
|
+
.strict();
|
|
33
|
+
|
|
34
|
+
/** Bounded native RPC bytes and their exact cursor projection. The source tag
|
|
35
|
+
* distinguishes a fresh poll from previously captured terminal evidence; it
|
|
36
|
+
* does not authenticate an object supplied by a caller. No Start, custody,
|
|
37
|
+
* database settlement or parent completion authority is conveyed. */
|
|
38
|
+
export type ModalRawOutputPage = z.infer<typeof RawPage>;
|
|
39
|
+
export type ModalRawOutputStreams = ModalRawOutputPage["streams"];
|
|
40
|
+
export type ModalRawOutputExit = ModalRawOutputPage["exit"];
|
|
41
|
+
type NativeOutput = ProviderCommandOutput & {
|
|
42
|
+
expected: ModalRouterProviderCommand;
|
|
43
|
+
command: ModalRouterProviderCommand;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
function project(
|
|
47
|
+
expected: ModalRouterProviderCommand,
|
|
48
|
+
streams: ModalRawOutputStreams,
|
|
49
|
+
exit: ModalRawOutputExit,
|
|
50
|
+
): NativeOutput {
|
|
51
|
+
const command = structuredClone(expected);
|
|
52
|
+
const chunks: ProviderCommandOutput["chunks"] = [];
|
|
53
|
+
for (const stream of ["stdout", "stderr"] as const) {
|
|
54
|
+
const old = expected.streams[stream];
|
|
55
|
+
const page = streams[stream];
|
|
56
|
+
const decoded = decodePage(old.utf8Remainder, [page.bytes], page.eof);
|
|
57
|
+
const byteOffset = old.byteOffset + page.bytes.byteLength;
|
|
58
|
+
if (!Number.isSafeInteger(byteOffset)) throw new Error("Modal output offset exhausted");
|
|
59
|
+
command.streams[stream] = {
|
|
60
|
+
byteOffset,
|
|
61
|
+
utf8Remainder: decoded.remainder,
|
|
62
|
+
eof: page.eof,
|
|
63
|
+
exitCode: page.eof ? exit.code : null,
|
|
64
|
+
};
|
|
65
|
+
if (decoded.text)
|
|
66
|
+
chunks.push({
|
|
67
|
+
stream,
|
|
68
|
+
chunkId: `modal-router:${expected.execId}:${stream}:${old.byteOffset}:${byteOffset}:${page.eof ? 1 : 0}`,
|
|
69
|
+
text: decoded.text,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
command,
|
|
74
|
+
expected: structuredClone(expected),
|
|
75
|
+
chunks,
|
|
76
|
+
exitCode: command.streams.stdout.eof && command.streams.stderr.eof ? exit.code : null,
|
|
77
|
+
providerExited: exit.code !== null,
|
|
78
|
+
streamFidelity: expected.pty ? "merged" : "separate",
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Provider-private construction from joined read/poll replies. Keep the SDK
|
|
83
|
+
* projection unchanged; this pure helper itself is not provider proof. */
|
|
84
|
+
export function collectModalRawOutputPage(
|
|
85
|
+
command: ModalRouterProviderCommand,
|
|
86
|
+
streams: ModalRawOutputStreams,
|
|
87
|
+
exit: ModalRawOutputExit,
|
|
88
|
+
): { raw: ModalRawOutputPage; output: NativeOutput } {
|
|
89
|
+
const expected = structuredClone(command);
|
|
90
|
+
const captured = {
|
|
91
|
+
stdout: { bytes: Buffer.from(streams.stdout.bytes), eof: streams.stdout.eof },
|
|
92
|
+
stderr: { bytes: Buffer.from(streams.stderr.bytes), eof: streams.stderr.eof },
|
|
93
|
+
};
|
|
94
|
+
const output = project(expected, captured, exit);
|
|
95
|
+
return {
|
|
96
|
+
raw: {
|
|
97
|
+
protocol: "modal-router-raw-page-v1",
|
|
98
|
+
expected,
|
|
99
|
+
command: structuredClone(output.command),
|
|
100
|
+
streams: captured,
|
|
101
|
+
exit: { ...exit },
|
|
102
|
+
},
|
|
103
|
+
output,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Recompute decoded output from exact bytes, never caller-decoded text. The
|
|
108
|
+
* current cursor must come from the caller's authoritative store. Equality
|
|
109
|
+
* checks are not a database CAS; coherent bytes still need trusted acquisition
|
|
110
|
+
* and atomic capture by the owner. */
|
|
111
|
+
export function reduceModalRawOutputPage(
|
|
112
|
+
input: ModalRawOutputPage,
|
|
113
|
+
current: ModalRouterProviderCommand,
|
|
114
|
+
): NativeOutput {
|
|
115
|
+
const page = RawPage.parse(input);
|
|
116
|
+
const expected = ModalRouterProviderCommand.parse(current);
|
|
117
|
+
if (!isDeepStrictEqual(page.expected, expected))
|
|
118
|
+
throw new Error("Raw page does not match the current native cursor");
|
|
119
|
+
for (const stream of ["stdout", "stderr"] as const) {
|
|
120
|
+
const old = expected.streams[stream];
|
|
121
|
+
const raw = page.streams[stream];
|
|
122
|
+
const remainder = Buffer.from(old.utf8Remainder, "base64");
|
|
123
|
+
if (
|
|
124
|
+
remainder.toString("base64") !== old.utf8Remainder ||
|
|
125
|
+
remainder.length > 3 ||
|
|
126
|
+
remainder.length > old.byteOffset ||
|
|
127
|
+
(old.eof && remainder.length !== 0)
|
|
128
|
+
)
|
|
129
|
+
throw new Error("Raw page has an invalid saved UTF-8 remainder");
|
|
130
|
+
if (old.eof && (!raw.eof || raw.bytes.byteLength !== 0))
|
|
131
|
+
throw new Error("Raw page reopened or advanced a completed stream");
|
|
132
|
+
if (old.exitCode !== null && page.exit.code !== old.exitCode)
|
|
133
|
+
throw new Error("Raw page rewrote captured terminal evidence");
|
|
134
|
+
}
|
|
135
|
+
if (
|
|
136
|
+
page.exit.source === "retained_terminal" &&
|
|
137
|
+
(!expected.streams.stdout.eof ||
|
|
138
|
+
!expected.streams.stderr.eof ||
|
|
139
|
+
expected.streams.stdout.exitCode !== page.exit.code ||
|
|
140
|
+
expected.streams.stderr.exitCode !== page.exit.code)
|
|
141
|
+
)
|
|
142
|
+
throw new Error("Raw page has no matching retained terminal evidence");
|
|
143
|
+
const output = project(expected, page.streams, page.exit);
|
|
144
|
+
if (!isDeepStrictEqual(page.command, output.command))
|
|
145
|
+
throw new Error("Raw page cursor delta or remainder does not match its bytes");
|
|
146
|
+
return output;
|
|
147
|
+
}
|
|
@@ -127,6 +127,63 @@ export type ModalRouterStart = ModalRouterIdentity & {
|
|
|
127
127
|
};
|
|
128
128
|
};
|
|
129
129
|
|
|
130
|
+
declare const preparedStartBrand: unique symbol;
|
|
131
|
+
/** An in-memory, single-use request on its issuing transport, NOT command
|
|
132
|
+
* admission or durable dispatch authority. Copies/serialized identities cannot
|
|
133
|
+
* be dispatched. The caller still owns authorization before physical Start. */
|
|
134
|
+
export type ModalRouterPreparedStart = Readonly<ModalRouterIdentity> & {
|
|
135
|
+
readonly [preparedStartBrand]: true;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
type PreparedStartRequest = ModalRouterStart & { stdoutConfig: number; stderrConfig: number };
|
|
139
|
+
|
|
140
|
+
function prepareStartRequest(request: ModalRouterStart): PreparedStartRequest {
|
|
141
|
+
// Validate before protobuf's fromObject coercions and snapshot only the
|
|
142
|
+
// supported request fields. Caller mutations must not substitute parameters
|
|
143
|
+
// between preparation and the eventual dispatch boundary.
|
|
144
|
+
const { taskId, execId, commandArgs, workdir, env, ptyInfo } = request;
|
|
145
|
+
if (
|
|
146
|
+
!taskId ||
|
|
147
|
+
!execId ||
|
|
148
|
+
!Array.isArray(commandArgs) ||
|
|
149
|
+
!commandArgs.length ||
|
|
150
|
+
typeof workdir !== "string" ||
|
|
151
|
+
!env ||
|
|
152
|
+
typeof env !== "object" ||
|
|
153
|
+
Array.isArray(env)
|
|
154
|
+
)
|
|
155
|
+
throw new Error("Invalid Modal command Start request");
|
|
156
|
+
const snapshot: PreparedStartRequest = {
|
|
157
|
+
taskId,
|
|
158
|
+
execId,
|
|
159
|
+
commandArgs: [...commandArgs],
|
|
160
|
+
workdir,
|
|
161
|
+
env: { ...env },
|
|
162
|
+
...(ptyInfo
|
|
163
|
+
? {
|
|
164
|
+
ptyInfo: {
|
|
165
|
+
enabled: ptyInfo.enabled,
|
|
166
|
+
winszRows: ptyInfo.winszRows,
|
|
167
|
+
winszCols: ptyInfo.winszCols,
|
|
168
|
+
envTerm: ptyInfo.envTerm,
|
|
169
|
+
ptyType: ptyInfo.ptyType,
|
|
170
|
+
noTerminateOnIdleStdin: ptyInfo.noTerminateOnIdleStdin,
|
|
171
|
+
},
|
|
172
|
+
}
|
|
173
|
+
: {}),
|
|
174
|
+
stdoutConfig: 1,
|
|
175
|
+
stderrConfig: 1,
|
|
176
|
+
};
|
|
177
|
+
if (modalRouterWire.lookupType("Start").verify(snapshot))
|
|
178
|
+
throw new Error("Invalid Modal command Start request");
|
|
179
|
+
if (encode("Start", snapshot).length > maxWireBytes)
|
|
180
|
+
throw new Error("Modal command Start request exceeds the wire limit");
|
|
181
|
+
Object.freeze(snapshot.commandArgs);
|
|
182
|
+
Object.freeze(snapshot.env);
|
|
183
|
+
if (snapshot.ptyInfo) Object.freeze(snapshot.ptyInfo);
|
|
184
|
+
return Object.freeze(snapshot);
|
|
185
|
+
}
|
|
186
|
+
|
|
130
187
|
function encode(type: string, value: object): Buffer {
|
|
131
188
|
const codec = modalRouterWire.lookupType(type);
|
|
132
189
|
return Buffer.from(codec.encode(codec.fromObject(value)).finish());
|
|
@@ -139,6 +196,10 @@ function encode(type: string, value: object): Buffer {
|
|
|
139
196
|
export class ModalCommandRouterWire {
|
|
140
197
|
private readonly client: Client;
|
|
141
198
|
private readonly metadata: Metadata;
|
|
199
|
+
readonly #preparedStarts = new WeakMap<
|
|
200
|
+
ModalRouterPreparedStart,
|
|
201
|
+
{ request: PreparedStartRequest; spent: boolean }
|
|
202
|
+
>();
|
|
142
203
|
private closed = false;
|
|
143
204
|
constructor(access: ModalRouterAccess, trustedRoots?: Buffer) {
|
|
144
205
|
const url = new URL(access.url);
|
|
@@ -199,12 +260,26 @@ export class ModalCommandRouterWire {
|
|
|
199
260
|
});
|
|
200
261
|
}
|
|
201
262
|
|
|
202
|
-
|
|
263
|
+
/** Read-only transport preparation. It sends neither Start nor stdin and
|
|
264
|
+
* does not invoke a caller's dispatch/admission callback. Prevalidation is not
|
|
265
|
+
* wire proof: gRPC still serializes at RPC invocation after dispatch begins. */
|
|
266
|
+
async prepareStart(
|
|
267
|
+
request: ModalRouterStart,
|
|
268
|
+
signal?: AbortSignal,
|
|
269
|
+
): Promise<ModalRouterPreparedStart> {
|
|
203
270
|
try {
|
|
271
|
+
signal?.throwIfAborted();
|
|
204
272
|
if (this.closed) throw new Error("Modal command router is closed");
|
|
273
|
+
const snapshot = prepareStartRequest(request);
|
|
205
274
|
await ModalCommandStartPreDispatchUnavailableError.ensureReady(this.client, signal);
|
|
206
275
|
signal?.throwIfAborted();
|
|
207
276
|
if (this.closed) throw new Error("Modal command router is closed");
|
|
277
|
+
const prepared = Object.freeze({
|
|
278
|
+
taskId: snapshot.taskId,
|
|
279
|
+
execId: snapshot.execId,
|
|
280
|
+
}) as ModalRouterPreparedStart;
|
|
281
|
+
this.#preparedStarts.set(prepared, { request: snapshot, spent: false });
|
|
282
|
+
return prepared;
|
|
208
283
|
} catch (error) {
|
|
209
284
|
if (this.closed)
|
|
210
285
|
throw new ModalCommandStartNotDispatchedError(
|
|
@@ -213,18 +288,30 @@ export class ModalCommandRouterWire {
|
|
|
213
288
|
if (error instanceof ModalCommandStartPreDispatchUnavailableError) throw error;
|
|
214
289
|
throw new ModalCommandStartNotDispatchedError(error);
|
|
215
290
|
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** No readiness, route lookup or retry here. Consume the authentic local
|
|
294
|
+
* handle synchronously before RPC, including when cancellation/closure wins.
|
|
295
|
+
* Once spent it never resets, even if no acknowledgement was received. */
|
|
296
|
+
async dispatchPreparedStart(
|
|
297
|
+
prepared: ModalRouterPreparedStart,
|
|
298
|
+
signal?: AbortSignal,
|
|
299
|
+
): Promise<void> {
|
|
300
|
+
const entry = this.#preparedStarts.get(prepared);
|
|
301
|
+
if (!entry || entry.spent)
|
|
302
|
+
// An invalid/spent reference is not proof that an earlier Start was never
|
|
303
|
+
// dispatched, and must not acquire a retry/settlement error brand.
|
|
304
|
+
throw new Error("Invalid or consumed Modal prepared Start handle");
|
|
305
|
+
entry.spent = true;
|
|
306
|
+
try {
|
|
307
|
+
signal?.throwIfAborted();
|
|
308
|
+
if (this.closed) throw new Error("Modal command router is closed");
|
|
309
|
+
} catch (error) {
|
|
310
|
+
throw new ModalCommandStartNotDispatchedError(error);
|
|
311
|
+
}
|
|
312
|
+
const request = entry.request;
|
|
216
313
|
try {
|
|
217
|
-
await this.unary(
|
|
218
|
-
"TaskExecStart",
|
|
219
|
-
"Start",
|
|
220
|
-
"Empty",
|
|
221
|
-
{
|
|
222
|
-
...request,
|
|
223
|
-
stdoutConfig: 1,
|
|
224
|
-
stderrConfig: 1,
|
|
225
|
-
},
|
|
226
|
-
signal,
|
|
227
|
-
);
|
|
314
|
+
await this.unary("TaskExecStart", "Start", "Empty", request, signal);
|
|
228
315
|
} catch (error) {
|
|
229
316
|
const code = (error as Partial<ServiceError> | null)?.code;
|
|
230
317
|
if (
|
|
@@ -242,6 +329,11 @@ export class ModalCommandRouterWire {
|
|
|
242
329
|
}
|
|
243
330
|
}
|
|
244
331
|
|
|
332
|
+
async start(request: ModalRouterStart, signal?: AbortSignal): Promise<void> {
|
|
333
|
+
const prepared = await this.prepareStart(request, signal);
|
|
334
|
+
await this.dispatchPreparedStart(prepared, signal);
|
|
335
|
+
}
|
|
336
|
+
|
|
245
337
|
async write(
|
|
246
338
|
identity: ModalRouterIdentity,
|
|
247
339
|
offset: number,
|
|
@@ -3,6 +3,7 @@ import { isDeepStrictEqual } from "node:util";
|
|
|
3
3
|
import { truncateOutput } from "@openai/agents-core/sandbox/internal";
|
|
4
4
|
import type { ChannelASession } from "../channel-a";
|
|
5
5
|
import { ModalProcessObservationUnavailableError } from "../errors";
|
|
6
|
+
import { classifyProviderSandboxFailure } from "../provider-errors";
|
|
6
7
|
import { markTypedExecHandleLoss, parseExecResponseBanner } from "../exec-banner";
|
|
7
8
|
import {
|
|
8
9
|
MAX_PROVIDER_COMMAND_HANDLE,
|
|
@@ -11,6 +12,7 @@ import {
|
|
|
11
12
|
type ProviderCommandSession,
|
|
12
13
|
admittedProviderCommandHandle,
|
|
13
14
|
ProviderCommandStartOutcomeUnknownError,
|
|
15
|
+
ProviderCommandInputOutcomeUnknownError,
|
|
14
16
|
} from "../provider-command-session";
|
|
15
17
|
import type { ModalCommandControl, ModalProviderCommand } from "./modal-command-control";
|
|
16
18
|
import { verifyModalMaterializedPath } from "./modal-materialization-verification";
|
|
@@ -333,12 +335,13 @@ export function installModalCommandSession(
|
|
|
333
335
|
const receipt = receipts.get(result);
|
|
334
336
|
if (!receipt || receipt.page.command.kind !== "modal-router-v1") return false;
|
|
335
337
|
if (
|
|
336
|
-
receipt.page.command.supervision &&
|
|
337
338
|
!receipt.page.expected &&
|
|
338
339
|
receipt.page.exitCode === null &&
|
|
339
340
|
receipt.page.chunks.length === 0
|
|
340
341
|
) {
|
|
341
|
-
//
|
|
342
|
+
// Both a supervised idle launch and an unavailable first observation
|
|
343
|
+
// carry identity only. Retention already owns this command; there are
|
|
344
|
+
// no observed bytes or terminal evidence to ACK.
|
|
342
345
|
receipts.delete(result);
|
|
343
346
|
return true;
|
|
344
347
|
}
|
|
@@ -406,8 +409,27 @@ export function installModalCommandSession(
|
|
|
406
409
|
if (!retained || !sameExecution(entry.command, retained))
|
|
407
410
|
throw new Error("Original Modal command identity is unavailable");
|
|
408
411
|
const index = await entry.persistence.reserveInput(Buffer.byteLength(args.chars));
|
|
409
|
-
|
|
412
|
+
try {
|
|
413
|
+
await control.write(retained, args.chars, index);
|
|
414
|
+
} catch (error) {
|
|
415
|
+
// Native gRPC transport failure does not establish whether this reserved
|
|
416
|
+
// range reached stdin. Never resend it or recommend another input write.
|
|
417
|
+
if (
|
|
418
|
+
retained.kind === "modal-router-v1" &&
|
|
419
|
+
// Contain supported transport graphs, including mixed failures. This
|
|
420
|
+
// grants no read/write retry permission; the locator and byte range
|
|
421
|
+
// come only from the retained command and its one input reservation.
|
|
422
|
+
classifyProviderSandboxFailure("modal", error).kind === "transient_transport"
|
|
423
|
+
)
|
|
424
|
+
throw new ProviderCommandInputOutcomeUnknownError(
|
|
425
|
+
retained,
|
|
426
|
+
index,
|
|
427
|
+
Buffer.byteLength(args.chars),
|
|
428
|
+
error,
|
|
429
|
+
);
|
|
430
|
+
throw error;
|
|
431
|
+
}
|
|
410
432
|
}
|
|
411
|
-
return read(args.sessionId, entry, args.yieldTimeMs ?? 250, args.maxOutputTokens);
|
|
433
|
+
return read(args.sessionId, entry, args.yieldTimeMs ?? 250, args.maxOutputTokens, args.signal);
|
|
412
434
|
};
|
|
413
435
|
}
|
|
@@ -6,7 +6,7 @@ import { ModalCommandStartPreDispatchUnavailableError } from "./modal-command-ro
|
|
|
6
6
|
import { ProviderCommandStartRejectedError } from "../provider-command-session";
|
|
7
7
|
import { ModalLegacyProviderCommand } from "@opengeni/contracts";
|
|
8
8
|
import type { ChannelAExecArgs } from "../channel-a";
|
|
9
|
-
import {
|
|
9
|
+
import { modalCommandArgv } from "./modal-command-argv";
|
|
10
10
|
|
|
11
11
|
type ControlPlane = Pick<
|
|
12
12
|
ModalClient["cpClient"],
|
|
@@ -162,14 +162,7 @@ export class ModalCommandControl {
|
|
|
162
162
|
if (!task.taskId || task.taskResult) throw new Error("Modal command task is unavailable");
|
|
163
163
|
if (sandboxId !== this.sandboxId)
|
|
164
164
|
throw new Error("Modal sandbox changed during command preparation");
|
|
165
|
-
|
|
166
|
-
let command = [args.shell ?? "/bin/sh", login ? "-lc" : "-c", args.cmd];
|
|
167
|
-
if (args.runAs) {
|
|
168
|
-
const user = shellQuote(args.runAs);
|
|
169
|
-
const invocation = command.map(shellQuote).join(" ");
|
|
170
|
-
const script = `if [ "$(id -u)" = ${user} ] || [ "$(id -un 2>/dev/null)" = ${user} ]; then exec ${invocation}; elif [ "$(id -u)" = 0 ]; then exec su -s /bin/sh ${user} -c ${shellQuote(`exec ${invocation}`)}; else exec sudo -n -u ${user} -- ${invocation}; fi`;
|
|
171
|
-
command = ["/bin/sh", "-c", script];
|
|
172
|
-
}
|
|
165
|
+
let command = modalCommandArgv(args);
|
|
173
166
|
const environment =
|
|
174
167
|
typeof this.environment === "function" ? this.environment() : this.environment;
|
|
175
168
|
if (Object.keys(environment).length) {
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from "node:util";
|
|
2
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
1
3
|
import {
|
|
2
4
|
SandboxMaterializationVerificationError,
|
|
3
5
|
retainMaterializationVerificationDiagnostic,
|
|
4
6
|
type MaterializationVerificationDiagnostic,
|
|
5
7
|
} from "../materialization-verification-error";
|
|
6
8
|
import type { ModalCommandControl, ModalProviderCommand } from "./modal-command-control";
|
|
9
|
+
import {
|
|
10
|
+
ProviderCommandObservationUnavailableError,
|
|
11
|
+
ProviderCommandStartOutcomeUnknownError,
|
|
12
|
+
type ProviderCommandOutput,
|
|
13
|
+
} from "../provider-command-session";
|
|
7
14
|
|
|
8
15
|
const MARKER = "__OPENGENI_MATERIALIZED_PATH_VISIBLE__";
|
|
9
16
|
const OUTPUT_LIMIT = 16 * 1024;
|
|
@@ -25,6 +32,7 @@ export async function verifyModalMaterializedPath(
|
|
|
25
32
|
const cancellation = new AbortController();
|
|
26
33
|
const deadlineReason = new Error("Materialization visibility observation deadline exceeded");
|
|
27
34
|
let providerCommand: ModalProviderCommand | undefined;
|
|
35
|
+
let startUnknown: ProviderCommandStartOutcomeUnknownError | undefined;
|
|
28
36
|
let stdout = "";
|
|
29
37
|
let stderr = "";
|
|
30
38
|
const diagnostic = (
|
|
@@ -54,13 +62,57 @@ export async function verifyModalMaterializedPath(
|
|
|
54
62
|
timer.unref();
|
|
55
63
|
pending.add(cancellation);
|
|
56
64
|
try {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
65
|
+
try {
|
|
66
|
+
providerCommand = await control.start(
|
|
67
|
+
{ cmd: command, workdir, shell: "sh", login: false, tty: false },
|
|
68
|
+
cancellation.signal,
|
|
69
|
+
);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
// A possibly dispatched fixed visibility probe has one original locator.
|
|
72
|
+
// Observe that invocation; never replay it or the surrounding mutation.
|
|
73
|
+
if (!(error instanceof ProviderCommandStartOutcomeUnknownError)) throw error;
|
|
74
|
+
providerCommand = structuredClone(error.command);
|
|
75
|
+
startUnknown = error;
|
|
76
|
+
}
|
|
61
77
|
for (;;) {
|
|
62
78
|
cancellation.signal.throwIfAborted();
|
|
63
|
-
|
|
79
|
+
let page: ProviderCommandOutput;
|
|
80
|
+
try {
|
|
81
|
+
page = await control.readProbe(providerCommand, 1_000, cancellation);
|
|
82
|
+
} catch (error) {
|
|
83
|
+
cancellation.signal.throwIfAborted();
|
|
84
|
+
if (
|
|
85
|
+
!(error instanceof ProviderCommandObservationUnavailableError) ||
|
|
86
|
+
!error.readRetryAllowed
|
|
87
|
+
) {
|
|
88
|
+
if (!startUnknown) throw error;
|
|
89
|
+
// A missing/denied read cannot disprove the original Start. Preserve
|
|
90
|
+
// its exact invocation without granting any further read retry.
|
|
91
|
+
throw new ProviderCommandObservationUnavailableError(
|
|
92
|
+
structuredClone(providerCommand),
|
|
93
|
+
new AggregateError(
|
|
94
|
+
[startUnknown, error],
|
|
95
|
+
"Original visibility Start and observation remain uncertain",
|
|
96
|
+
),
|
|
97
|
+
false,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
// Unavailable reads do not advance any cursor. A contradictory locator
|
|
101
|
+
// is an invariant failure, never authority to inspect another process.
|
|
102
|
+
if (!isDeepStrictEqual(error.command, providerCommand))
|
|
103
|
+
throw new SandboxMaterializationVerificationError(diagnostic("invalid_response"), {
|
|
104
|
+
cause: error,
|
|
105
|
+
});
|
|
106
|
+
// Read retries remain inside this probe's original thirty-second
|
|
107
|
+
// deadline, including outages longer than a single bounded read.
|
|
108
|
+
try {
|
|
109
|
+
await delay(100, undefined, { signal: cancellation.signal });
|
|
110
|
+
} catch (interrupted) {
|
|
111
|
+
cancellation.signal.throwIfAborted();
|
|
112
|
+
throw interrupted;
|
|
113
|
+
}
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
64
116
|
// Cancellation ends observation, not the process. Never accept a late
|
|
65
117
|
// success after the attempt requested cancellation.
|
|
66
118
|
cancellation.signal.throwIfAborted();
|
|
@@ -104,7 +156,19 @@ export async function verifyModalMaterializedPath(
|
|
|
104
156
|
...diagnostic("command_pending"),
|
|
105
157
|
causeMessage: "Observation deadline exceeded; provider process completion is unconfirmed",
|
|
106
158
|
},
|
|
107
|
-
{
|
|
159
|
+
{
|
|
160
|
+
cause: providerCommand
|
|
161
|
+
? new ProviderCommandObservationUnavailableError(
|
|
162
|
+
providerCommand,
|
|
163
|
+
startUnknown
|
|
164
|
+
? new AggregateError(
|
|
165
|
+
[startUnknown, error],
|
|
166
|
+
"Original visibility Start and observation deadline remain uncertain",
|
|
167
|
+
)
|
|
168
|
+
: error,
|
|
169
|
+
)
|
|
170
|
+
: error,
|
|
171
|
+
},
|
|
108
172
|
);
|
|
109
173
|
}
|
|
110
174
|
if (!(error instanceof SandboxMaterializationVerificationError)) {
|