@tangle-network/agent-app 0.22.0 → 0.23.0

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.
@@ -1,550 +1,70 @@
1
1
  import {
2
- assertHarnessModelCompatible
3
- } from "../chunk-5VXPDXZJ.js";
2
+ DEFAULT_SANDBOX_RESOURCES,
3
+ attachReasoningEffort,
4
+ bearerSubprotocolToken,
5
+ bearerToken,
6
+ buildAppToolMcpServers,
7
+ buildSandboxRuntimeProxyHeaders,
8
+ classifySeveredStream,
9
+ createSandboxTerminalToken,
10
+ createWorkspaceSandboxConnectionHandler,
11
+ createWorkspaceSandboxManager,
12
+ createWorkspaceSandboxRuntimeProxyHandler,
13
+ createWorkspaceSandboxTerminalUpgradeHandler,
14
+ deleteSecret,
15
+ detectInteractiveQuestion,
16
+ driveSandboxTurn,
17
+ encodeSandboxRuntimePath,
18
+ ensureWorkspaceSandbox,
19
+ flattenHistory,
20
+ getClient,
21
+ isSandboxTerminalWsUpgrade,
22
+ isTerminalPromptEvent,
23
+ matchSandboxTerminalWsPath,
24
+ mergeExtraMcp,
25
+ mintSandboxScopedToken,
26
+ mintTerminalProxyToken,
27
+ readSecret,
28
+ resetClientCache,
29
+ resolveModel,
30
+ runSandboxPrompt,
31
+ secretStoreFromClient,
32
+ storeSecret,
33
+ streamSandboxPrompt,
34
+ syncSandboxMemberAdd,
35
+ syncSandboxMemberRemove,
36
+ syncSandboxMemberRole,
37
+ terminalTokenFromRequest,
38
+ verifySandboxTerminalToken,
39
+ verifyTerminalProxyToken
40
+ } from "../chunk-XDCHKFBX.js";
41
+ import "../chunk-5VXPDXZJ.js";
4
42
  import "../chunk-MH6AVXQ7.js";
5
- import {
6
- buildAppToolMcpServer
7
- } from "../chunk-A76ZHWNF.js";
43
+ import "../chunk-A76ZHWNF.js";
8
44
  import "../chunk-JZZ6AWF4.js";
9
-
10
- // src/sandbox/index.ts
11
- import {
12
- Sandbox
13
- } from "@tangle-network/sandbox";
14
- var ok = (value) => ({ succeeded: true, value });
15
- var fail = (error) => ({
16
- succeeded: false,
17
- error: error instanceof Error ? error : new Error(String(error))
18
- });
19
- var DEFAULT_SANDBOX_RESOURCES = {
20
- image: "universal",
21
- cpuCores: 2,
22
- memoryMB: 4096,
23
- diskGB: 10,
24
- maxLifetimeSeconds: 86400,
25
- idleTimeoutSeconds: 3600
26
- };
27
- var _cached = null;
28
- function getClientFromCreds(creds) {
29
- const fingerprint = `${creds.apiKey} ${creds.baseUrl}`;
30
- if (_cached && _cached.fingerprint === fingerprint) return _cached.client;
31
- const client = new Sandbox({ apiKey: creds.apiKey, baseUrl: creds.baseUrl });
32
- _cached = { client, fingerprint };
33
- return client;
34
- }
35
- function getClient(shell) {
36
- const creds = shell.credentials();
37
- if (creds && typeof creds.then === "function") {
38
- throw new Error("getClient: scoped (async) credentials require the async sandbox path");
39
- }
40
- if (!creds) throw new Error("sandbox credentials are required (apiKey/baseUrl)");
41
- return getClientFromCreds(creds);
42
- }
43
- function resetClientCache() {
44
- _cached = null;
45
- }
46
- function buildAppToolMcpServers(options) {
47
- const entries = {};
48
- for (const { tool, key, description } of options.tools) {
49
- entries[key] = buildAppToolMcpServer({
50
- tool,
51
- baseUrl: options.baseUrl,
52
- token: options.token,
53
- ctx: options.ctx,
54
- description,
55
- headerNames: options.headerNames
56
- });
57
- }
58
- return entries;
59
- }
60
- function shellSingleQuote(value) {
61
- return `'${value.replace(/'/g, `'\\''`)}'`;
62
- }
63
- async function listRunning(client, name) {
64
- try {
65
- const running = await client.list({ status: "running" });
66
- return ok(running.find((s) => s.name === name) ?? null);
67
- } catch (err) {
68
- return fail(err);
69
- }
70
- }
71
- async function deleteBox(box) {
72
- try {
73
- await box.delete();
74
- return ok(void 0);
75
- } catch (err) {
76
- return fail(err);
77
- }
78
- }
79
- async function isBoxAlive(box, harness, probe) {
80
- if (!probe) return true;
81
- const execTimeout = probe.execTimeoutMs ?? 5e3;
82
- const psTimeout = probe.psTimeoutMs ?? 3e3;
83
- const race = (p, ms, label) => Promise.race([
84
- p,
85
- new Promise((_, reject) => setTimeout(() => reject(new Error(label)), ms))
86
- ]);
87
- try {
88
- const alive = await race(box.exec("echo alive"), execTimeout, "alive check timeout");
89
- if (!alive.stdout.includes("alive")) return false;
90
- const pattern = probe.sidecarProcessPattern(harness);
91
- try {
92
- const ps = await race(
93
- box.exec(`pgrep -f ${shellSingleQuote(pattern)} || echo no-sidecar`),
94
- psTimeout,
95
- "ps check timeout"
96
- );
97
- if (ps.stdout.includes("no-sidecar")) return false;
98
- } catch {
99
- }
100
- return true;
101
- } catch {
102
- return false;
103
- }
104
- }
105
- async function resumeStoppedBox(client, name, timeoutMs) {
106
- try {
107
- const stopped = await client.list({ status: "stopped" });
108
- const match = stopped.find((s) => s.name === name) ?? null;
109
- if (!match) return ok(null);
110
- await match.resume();
111
- await match.waitFor("running", { timeoutMs });
112
- return ok(match);
113
- } catch (err) {
114
- return fail(err);
115
- }
116
- }
117
- async function ensureWorkspaceSandbox(shell, options) {
118
- const { workspaceId, userId, harness, forceNew } = options;
119
- const scope = { workspaceId, ...userId ? { userId } : {} };
120
- const creds = await shell.credentials(scope);
121
- if (!creds) throw new Error("sandbox credentials are required (apiKey/baseUrl)");
122
- const client = getClientFromCreds(creds);
123
- const name = shell.boxKey ? shell.boxKey(scope) : shell.name(workspaceId);
124
- const resources = shell.resources ?? DEFAULT_SANDBOX_RESOURCES;
125
- const resumeTimeout = 12e4;
126
- const existing = await listRunning(client, name);
127
- if (existing.succeeded && existing.value) {
128
- const found = existing.value;
129
- if (forceNew) {
130
- const dropped = await deleteBox(found);
131
- if (!dropped.succeeded) {
132
- throw new Error(`forceNew: sandbox ${name} could not be deleted`, { cause: dropped.error });
133
- }
134
- } else if (found.metadata?.harness === harness && await isBoxAlive(found, harness, shell.livenessProbe)) {
135
- if (shell.bootstrap) {
136
- const boot = await shell.bootstrap(found, scope);
137
- if (!boot.succeeded) {
138
- throw new Error(`bootstrap failed on reused box ${name}`, { cause: boot.error });
139
- }
140
- }
141
- return found;
142
- } else {
143
- const dropped = await deleteBox(found);
144
- if (!dropped.succeeded) {
145
- throw new Error(
146
- `sandbox ${name} (was ${String(found.metadata?.harness ?? "unknown")}, want ${harness}, or unresponsive) could not be deleted`,
147
- { cause: dropped.error }
148
- );
149
- }
150
- }
151
- }
152
- if (!forceNew && shell.resumeStopped !== false) {
153
- const resumed = await resumeStoppedBox(client, name, resumeTimeout);
154
- if (resumed.succeeded && resumed.value && await isBoxAlive(resumed.value, harness, shell.livenessProbe)) {
155
- const box2 = resumed.value;
156
- if (shell.bootstrap) {
157
- const boot = await shell.bootstrap(box2, scope);
158
- if (!boot.succeeded) {
159
- throw new Error(`bootstrap failed on resumed box ${name}`, { cause: boot.error });
160
- }
161
- }
162
- return box2;
163
- }
164
- }
165
- const connectedIntegrationIds = await shell.connectedIntegrationIds(workspaceId);
166
- const buildCtx = {
167
- workspaceId,
168
- connectedIntegrationIds,
169
- ...userId ? { userId } : {}
170
- };
171
- const [secrets, env, files] = await Promise.all([
172
- shell.secrets(workspaceId),
173
- shell.env(buildCtx),
174
- shell.files(buildCtx)
175
- ]);
176
- const profile = shell.profile({ extraFiles: files });
177
- const role = userId && shell.permissionRole ? shell.permissionRole("developer") : void 0;
178
- let model = shell.backendModelAtCreate ? resolveModel(shell.provider) : void 0;
179
- if (model && shell.childKeyMint && model.provider === "openai-compat") {
180
- const minted = await shell.childKeyMint(scope);
181
- if (minted.succeeded) model = { ...model, apiKey: minted.value };
182
- else {
183
- console.error(
184
- `[sandbox] childKeyMint failed for ${workspaceId}; using parent key:`,
185
- minted.error.message
186
- );
187
- }
188
- }
189
- const storage = shell.storage?.(buildCtx);
190
- const restore = shell.restore?.(buildCtx);
191
- const payload = {
192
- name,
193
- image: resources.image,
194
- metadata: shell.metadata(harness),
195
- ...userId ? { permissions: { initialUsers: [{ userId, role }] } } : {},
196
- env,
197
- secrets,
198
- backend: { type: harness, profile, ...model ? { model } : {} },
199
- ...storage ? { storage } : {},
200
- ...restore ? restore : {},
201
- maxLifetimeSeconds: resources.maxLifetimeSeconds,
202
- idleTimeoutSeconds: resources.idleTimeoutSeconds,
203
- resources: {
204
- cpuCores: resources.cpuCores,
205
- memoryMB: resources.memoryMB,
206
- diskGB: resources.diskGB
207
- }
208
- };
209
- const box = await client.create(payload);
210
- await box.waitFor("running", { timeoutMs: 12e4 });
211
- if (!box.connection?.runtimeUrl) await box.refresh();
212
- if (shell.bootstrap) {
213
- const boot = await shell.bootstrap(box, scope);
214
- if (!boot.succeeded) {
215
- throw new Error(`bootstrap failed on new box ${name}`, { cause: boot.error });
216
- }
217
- }
218
- return box;
219
- }
220
- function resolveModel(config, override) {
221
- const c = config ?? {};
222
- const explicitBaseUrl = c.routerBaseUrl;
223
- const explicitApiKey = override?.modelApiKey ?? c.apiKey;
224
- const provider = c.providerName ?? (explicitApiKey ? "openai-compat" : c.openaiApiKey ? "openai" : void 0);
225
- const modelName = override?.model ?? c.modelName ?? (provider === "openai" || provider === "openai-compat" ? c.defaultModel : void 0);
226
- const apiKey = explicitApiKey ?? (provider === "openai" ? c.openaiApiKey : void 0);
227
- if (!provider || !modelName || !apiKey) return void 0;
228
- return {
229
- model: modelName,
230
- provider,
231
- apiKey,
232
- ...explicitBaseUrl ? { baseUrl: explicitBaseUrl } : {}
233
- };
234
- }
235
- function flattenHistory(message, history) {
236
- if (!history?.length) return message;
237
- const transcript = history.map((entry) => `${entry.role === "assistant" ? "Assistant" : "User"}: ${entry.content}`).join("\n\n");
238
- return `${transcript}
239
-
240
- User: ${message}`;
241
- }
242
- function mergeExtraMcp(appToolMcp, baseProfileMcp, extra) {
243
- for (const key of Object.keys(extra ?? {})) {
244
- if (key in appToolMcp || key in baseProfileMcp) {
245
- throw new Error(`extraMcp key '${key}' collides with an existing profile MCP server`);
246
- }
247
- }
248
- return { ...appToolMcp, ...extra ?? {} };
249
- }
250
- function attachReasoningEffort(profile, harness, effort) {
251
- if (!effort || effort === "auto") return profile;
252
- return {
253
- ...profile,
254
- extensions: {
255
- ...profile.extensions ?? {},
256
- [harness]: {
257
- ...profile.extensions?.[harness] ?? {},
258
- reasoningEffort: effort
259
- }
260
- }
261
- };
262
- }
263
- async function* streamSandboxPrompt(shell, box, message, options) {
264
- const harness = options?.harness ?? "opencode";
265
- const model = resolveModel(shell.provider, {
266
- model: options?.model,
267
- modelApiKey: options?.modelApiKey
268
- });
269
- if (model?.model) assertHarnessModelCompatible(harness, model.model);
270
- const prompt = flattenHistory(message, options?.history);
271
- const appToolMcp = options?.appToolMcp ?? {};
272
- const extraMcp = mergeExtraMcp(appToolMcp, options?.baseProfileMcp ?? {}, options?.extraMcp);
273
- const profile = shell.profile({ systemPrompt: options?.systemPrompt, extraMcp });
274
- const profileWithEffort = attachReasoningEffort(profile, harness, options?.effort);
275
- const stream = box.streamPrompt(prompt, {
276
- sessionId: options?.sessionId,
277
- executionId: options?.executionId,
278
- lastEventId: options?.lastEventId,
279
- ...options?.signal ? { signal: options.signal } : {},
280
- ...options?.timeoutMs !== void 0 ? { timeoutMs: options.timeoutMs } : {},
281
- backend: {
282
- type: harness,
283
- profile: profileWithEffort,
284
- ...model ? { model } : {}
285
- }
286
- });
287
- let severedFinishReason = null;
288
- for await (const event of stream) {
289
- const step = classifySeveredStream(event);
290
- if (step) severedFinishReason = step.kind === "step-finish" && step.severed ? step.reason : null;
291
- if (severedFinishReason && isTerminalPromptEvent(event)) {
292
- throw new Error(`sandbox model stream severed mid-turn (reason="${severedFinishReason}")`);
293
- }
294
- if (options?.disallowQuestions) {
295
- const q = detectInteractiveQuestion(event);
296
- if (q) {
297
- throw new Error(`sandbox agent asked an interactive question during an autonomous run: ${q}`);
298
- }
299
- }
300
- yield event;
301
- }
302
- if (severedFinishReason) {
303
- throw new Error(`sandbox model stream severed mid-turn (reason="${severedFinishReason}")`);
304
- }
305
- }
306
- async function runSandboxPrompt(shell, box, message, options) {
307
- let fullText = "";
308
- let firstTextSeen = false;
309
- for await (const rawEvent of streamSandboxPrompt(shell, box, message, options)) {
310
- const event = rawEvent;
311
- if (!event.type) continue;
312
- if (event.type === "message.part.updated") {
313
- const part = event.data?.part;
314
- const delta = typeof event.data?.delta === "string" ? event.data.delta : null;
315
- if (String(part?.type ?? "") === "text") {
316
- if (!firstTextSeen) {
317
- firstTextSeen = true;
318
- continue;
319
- }
320
- if (delta) fullText += delta;
321
- else if (typeof part?.text === "string") fullText = part.text;
322
- }
323
- } else if (event.type === "result") {
324
- const finalText = typeof event.data?.finalText === "string" ? event.data.finalText : null;
325
- if (finalText) fullText = finalText;
326
- }
327
- }
328
- return fullText;
329
- }
330
- async function syncSandboxMemberAdd(box, seam, userId, role) {
331
- try {
332
- await box.permissions.add({ userId, role: seam.roleToSandboxRole(role) });
333
- return ok(void 0);
334
- } catch (err) {
335
- return fail(err);
336
- }
337
- }
338
- async function syncSandboxMemberRemove(box, userId) {
339
- try {
340
- await box.permissions.remove(userId, { preserveHomeDir: true });
341
- return ok(void 0);
342
- } catch (err) {
343
- return fail(err);
344
- }
345
- }
346
- async function syncSandboxMemberRole(box, seam, userId, role) {
347
- try {
348
- await box.permissions.update(userId, { role: seam.roleToSandboxRole(role) });
349
- return ok(void 0);
350
- } catch (err) {
351
- return fail(err);
352
- }
353
- }
354
- function secretStoreFromClient(shell) {
355
- const client = getClient(shell);
356
- return {
357
- create: async (name, value) => {
358
- await client.secrets.create(name, value);
359
- },
360
- update: async (name, value) => {
361
- await client.secrets.update(name, value);
362
- },
363
- get: (name) => client.secrets.get(name),
364
- delete: async (name) => {
365
- await client.secrets.delete(name);
366
- }
367
- };
368
- }
369
- async function storeSecret(store, name, value) {
370
- try {
371
- await store.create(name, value);
372
- return ok(void 0);
373
- } catch {
374
- try {
375
- await store.update(name, value);
376
- return ok(void 0);
377
- } catch (err) {
378
- return fail(new Error(`Failed to store sandbox secret ${name}`, { cause: err }));
379
- }
380
- }
381
- }
382
- async function readSecret(store, name) {
383
- try {
384
- return ok(await store.get(name));
385
- } catch (err) {
386
- return fail(err);
387
- }
388
- }
389
- async function deleteSecret(store, name) {
390
- try {
391
- await store.delete(name);
392
- return ok(void 0);
393
- } catch (err) {
394
- return fail(err);
395
- }
396
- }
397
- async function mintSandboxScopedToken(box, options) {
398
- try {
399
- const token = await box.mintScopedToken({
400
- scope: options.scope,
401
- ...options.sessionId ? { sessionId: options.sessionId } : {},
402
- ...options.ttlMinutes ? { ttlMinutes: options.ttlMinutes } : {}
403
- });
404
- return ok({ token: token.token, expiresAt: token.expiresAt, scope: token.scope });
405
- } catch (err) {
406
- return fail(err);
407
- }
408
- }
409
- async function driveSandboxTurn(shell, box, message, options) {
410
- const harness = options.harness ?? "opencode";
411
- const model = resolveModel(shell.provider, {
412
- model: options.model,
413
- modelApiKey: options.modelApiKey
414
- });
415
- if (model?.model) assertHarnessModelCompatible(harness, model.model);
416
- const prompt = flattenHistory(message, options.history);
417
- const appToolMcp = options.appToolMcp ?? {};
418
- const extraMcp = mergeExtraMcp(appToolMcp, options.baseProfileMcp ?? {}, options.extraMcp);
419
- const profile = attachReasoningEffort(
420
- shell.profile({ systemPrompt: options.systemPrompt, extraMcp }),
421
- harness,
422
- options.effort
423
- );
424
- try {
425
- const result = await box.prompt(prompt, {
426
- sessionId: options.sessionId,
427
- ...options.executionId ? { executionId: options.executionId } : {},
428
- ...options.timeoutMs !== void 0 ? { timeoutMs: options.timeoutMs } : {},
429
- ...options.signal ? { signal: options.signal } : {},
430
- backend: { type: harness, profile, ...model ? { model } : {} }
431
- });
432
- if (!result.success) return fail(new Error(result.error ?? "sandbox turn failed"));
433
- return ok(result);
434
- } catch (err) {
435
- return fail(err);
436
- }
437
- }
438
- var TERMINAL_PROXY_TOKEN_TTL_MS = 15 * 60 * 1e3;
439
- async function signTerminalProxyToken(secret, encodedPayload) {
440
- const key = await crypto.subtle.importKey(
441
- "raw",
442
- new TextEncoder().encode(secret),
443
- { name: "HMAC", hash: "SHA-256" },
444
- false,
445
- ["sign"]
446
- );
447
- const sig = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(encodedPayload));
448
- return base64UrlEncodeBytes(new Uint8Array(sig));
449
- }
450
- async function mintTerminalProxyToken(secret, identity, ttlMs = TERMINAL_PROXY_TOKEN_TTL_MS) {
451
- if (!secret) return fail(new Error("mintTerminalProxyToken: secret is required"));
452
- if (!identity.userId || !identity.workspaceId || !identity.sandboxId) {
453
- return fail(new Error("mintTerminalProxyToken: userId/workspaceId/sandboxId are required"));
454
- }
455
- const expiresAt = new Date(Date.now() + ttlMs);
456
- const payload = { ...identity, exp: Math.floor(expiresAt.getTime() / 1e3) };
457
- const encoded = base64UrlEncodeUtf8(JSON.stringify(payload));
458
- const sig = await signTerminalProxyToken(secret, encoded);
459
- return ok({ token: `${encoded}.${sig}`, expiresAt });
460
- }
461
- async function verifyTerminalProxyToken(secret, token, expected) {
462
- if (!secret) return false;
463
- const [encoded, sig, extra] = token.split(".");
464
- if (!encoded || !sig || extra !== void 0) return false;
465
- const expectedSig = await signTerminalProxyToken(secret, encoded);
466
- if (!constantTimeEqual(sig, expectedSig)) return false;
467
- let payload;
468
- try {
469
- payload = JSON.parse(base64UrlDecodeUtf8(encoded));
470
- } catch {
471
- return false;
472
- }
473
- return payload.userId === expected.userId && payload.workspaceId === expected.workspaceId && payload.sandboxId === expected.sandboxId && Number.isFinite(payload.exp) && payload.exp > Math.floor(Date.now() / 1e3);
474
- }
475
- function base64UrlEncodeBytes(bytes) {
476
- let bin = "";
477
- for (const b of bytes) bin += String.fromCharCode(b);
478
- return btoa(bin).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
479
- }
480
- function base64UrlEncodeUtf8(v) {
481
- return base64UrlEncodeBytes(new TextEncoder().encode(v));
482
- }
483
- function base64UrlDecodeUtf8(v) {
484
- const padded = v.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(v.length / 4) * 4, "=");
485
- const bin = atob(padded);
486
- const bytes = new Uint8Array(bin.length);
487
- for (let i = 0; i < bin.length; i += 1) bytes[i] = bin.charCodeAt(i);
488
- return new TextDecoder().decode(bytes);
489
- }
490
- function constantTimeEqual(a, b) {
491
- if (a.length !== b.length) return false;
492
- let r = 0;
493
- for (let i = 0; i < a.length; i += 1) r |= a.charCodeAt(i) ^ b.charCodeAt(i);
494
- return r === 0;
495
- }
496
- var SEVERED_FINISH_REASONS = /* @__PURE__ */ new Set(["error", "other", "unknown"]);
497
- function asPlainRecord(v) {
498
- return v && typeof v === "object" && !Array.isArray(v) ? v : null;
499
- }
500
- function classifySeveredStream(event) {
501
- const root = asPlainRecord(event);
502
- if (!root || root.type !== "message.part.updated") return null;
503
- const body = asPlainRecord(root.properties) ?? asPlainRecord(root.data) ?? root;
504
- const part = asPlainRecord(body.part);
505
- if (!part) return null;
506
- if (part.type === "step-start") return { kind: "step-start" };
507
- if (part.type !== "step-finish") return null;
508
- const reason = typeof part.reason === "string" && part.reason ? part.reason : "unknown";
509
- return { kind: "step-finish", reason, severed: SEVERED_FINISH_REASONS.has(reason) };
510
- }
511
- function isTerminalPromptEvent(event) {
512
- const t = asPlainRecord(event)?.type;
513
- return t === "result" || t === "done";
514
- }
515
- function detectInteractiveQuestion(event) {
516
- const root = asPlainRecord(event);
517
- if (!root) return null;
518
- const type = typeof root.type === "string" ? root.type : void 0;
519
- const data = asPlainRecord(root.data);
520
- const props = asPlainRecord(root.properties);
521
- const body = props ?? data ?? root;
522
- if (type === "question.asked" || type === "question") return firstQuestionText(body);
523
- const part = asPlainRecord(data?.part) ?? asPlainRecord(body.part);
524
- const tool = typeof part?.tool === "string" && part.tool || typeof part?.name === "string" && part.name || typeof body.tool === "string" && body.tool || void 0;
525
- const isQ = type === "message.part.updated" && (tool === "question" || asPlainRecord(part)?.type === "question");
526
- if (!isQ) return null;
527
- const state = asPlainRecord(asPlainRecord(part)?.state);
528
- return firstQuestionText(asPlainRecord(state?.input) ?? state ?? part ?? body);
529
- }
530
- function firstQuestionText(value) {
531
- const arr = Array.isArray(value?.questions) ? value.questions : Array.isArray(asPlainRecord(value?.input)?.questions) ? asPlainRecord(value.input).questions : [];
532
- const first = asPlainRecord(arr[0]);
533
- const q = typeof first?.question === "string" && first.question || typeof first?.prompt === "string" && first.prompt || void 0;
534
- return q ?? "interactive question";
535
- }
536
45
  export {
537
46
  DEFAULT_SANDBOX_RESOURCES,
538
47
  attachReasoningEffort,
48
+ bearerSubprotocolToken,
49
+ bearerToken,
539
50
  buildAppToolMcpServers,
51
+ buildSandboxRuntimeProxyHeaders,
540
52
  classifySeveredStream,
53
+ createSandboxTerminalToken,
54
+ createWorkspaceSandboxConnectionHandler,
55
+ createWorkspaceSandboxManager,
56
+ createWorkspaceSandboxRuntimeProxyHandler,
57
+ createWorkspaceSandboxTerminalUpgradeHandler,
541
58
  deleteSecret,
542
59
  detectInteractiveQuestion,
543
60
  driveSandboxTurn,
61
+ encodeSandboxRuntimePath,
544
62
  ensureWorkspaceSandbox,
545
63
  flattenHistory,
546
64
  getClient,
65
+ isSandboxTerminalWsUpgrade,
547
66
  isTerminalPromptEvent,
67
+ matchSandboxTerminalWsPath,
548
68
  mergeExtraMcp,
549
69
  mintSandboxScopedToken,
550
70
  mintTerminalProxyToken,
@@ -558,6 +78,8 @@ export {
558
78
  syncSandboxMemberAdd,
559
79
  syncSandboxMemberRemove,
560
80
  syncSandboxMemberRole,
81
+ terminalTokenFromRequest,
82
+ verifySandboxTerminalToken,
561
83
  verifyTerminalProxyToken
562
84
  };
563
85
  //# sourceMappingURL=index.js.map