@tangle-network/sandbox 0.10.5 → 0.11.1-develop.20260717054349.4472dd9
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/README.md +109 -0
- package/dist/agent/index.d.ts +3 -3
- package/dist/{client-Dv93EI90.js → client-1LKomw8A.js} +481 -63
- package/dist/{client-CtdFU_nx.d.ts → client-mpqiRmdi.d.ts} +7 -3
- package/dist/collaboration/index.js +1 -1
- package/dist/{collaboration-QZXrXdIb.js → collaboration-DDnuTcZd.js} +1 -1
- package/dist/core.d.ts +4 -4
- package/dist/core.js +3 -3
- package/dist/{errors-DSz87Rkk.js → errors-Cbs78OlF.js} +6 -6
- package/dist/{errors--P_nbLzM.d.ts → errors-ntvaf0_F.d.ts} +2 -2
- package/dist/{index-DF36P3Ew.d.ts → index-DD8we2b9.d.ts} +2 -2
- package/dist/index.d.ts +32 -17
- package/dist/index.js +10 -286
- package/dist/intelligence/index.js +1 -1
- package/dist/runtime-api-fqaQ3CN2.js +678 -0
- package/dist/runtime.d.ts +49 -6
- package/dist/runtime.js +7 -5
- package/dist/{sandbox-tksuLdrT.d.ts → sandbox-BhbAxbub.d.ts} +36 -12
- package/dist/{sandbox-Vh7Aog8b.js → sandbox-Cf25Dgrc.js} +259 -124
- package/dist/tangle/index.d.ts +1 -1
- package/dist/tangle/index.js +1 -1
- package/dist/{tangle-Bqbf3dA8.js → tangle-DcGiYTx3.js} +2 -2
- package/dist/{types-BGGfKALh.d.ts → types-Di5KUpoc.d.ts} +180 -42
- package/package.json +4 -3
- package/dist/runtime-api-CvAUrnHc.js +0 -448
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { n as normalizeSessionInfo, r as
|
|
2
|
-
import { c as SandboxError, d as TimeoutError, f as ValidationError, i as NetworkError, l as ServerError, n as CapabilityError, p as parseErrorResponse, s as QuotaError, t as AuthError, u as StateError } from "./errors-
|
|
1
|
+
import { l as normalizeRuntimeBackendConfig, n as normalizeSessionInfo, o as combineAbortSignals, r as uploadChunked, s as encodePromptForWire, t as SandboxRuntimeApi, u as normalizeRuntimeModelOverride } from "./runtime-api-fqaQ3CN2.js";
|
|
2
|
+
import { c as SandboxError, d as TimeoutError, f as ValidationError, i as NetworkError, l as ServerError, n as CapabilityError, p as parseErrorResponse, s as QuotaError, t as AuthError, u as StateError } from "./errors-Cbs78OlF.js";
|
|
3
3
|
import { addTokenUsage, readTokenCostUsd, readTokenUsage } from "@tangle-network/agent-core";
|
|
4
4
|
//#region src/lib/sse-parser.ts
|
|
5
5
|
/**
|
|
@@ -18,6 +18,7 @@ async function* parseSSEStream(body, options) {
|
|
|
18
18
|
let currentEvent = "";
|
|
19
19
|
let dataLines = [];
|
|
20
20
|
let currentId = "";
|
|
21
|
+
let completed = false;
|
|
21
22
|
const flush = function* () {
|
|
22
23
|
if (dataLines.length === 0) {
|
|
23
24
|
currentEvent = "";
|
|
@@ -61,40 +62,13 @@ async function* parseSSEStream(body, options) {
|
|
|
61
62
|
buffer += decoder.decode();
|
|
62
63
|
if (buffer.length > 0) processLine(buffer);
|
|
63
64
|
yield* flush();
|
|
65
|
+
completed = true;
|
|
64
66
|
} finally {
|
|
67
|
+
if (!completed) await reader.cancel().catch(() => void 0);
|
|
65
68
|
reader.releaseLock();
|
|
66
69
|
}
|
|
67
70
|
}
|
|
68
71
|
//#endregion
|
|
69
|
-
//#region src/lib/wire-encoding.ts
|
|
70
|
-
/**
|
|
71
|
-
* Encode a prompt text part for the wire. Server decodes at the route
|
|
72
|
-
* boundary; the LLM only ever sees the original UTF-8. Done
|
|
73
|
-
* unconditionally because readable bodies false-positive on ingress WAF
|
|
74
|
-
* rules that pattern-match shell-injection-shaped substrings (which
|
|
75
|
-
* legitimate prompts routinely contain — e.g. Step-0 dev-server
|
|
76
|
-
* bootstrap).
|
|
77
|
-
*/
|
|
78
|
-
function encodeTextForWire(text) {
|
|
79
|
-
if (typeof Buffer !== "undefined") return Buffer.from(text, "utf8").toString("base64");
|
|
80
|
-
if (typeof btoa === "function") return btoa(encodeURIComponent(text).replace(/%([0-9A-F]{2})/g, (_, h) => String.fromCharCode(Number.parseInt(h, 16))));
|
|
81
|
-
throw new Error("encodeTextForWire: no base64 encoder available (Buffer and btoa both undefined)");
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Convert a caller-supplied prompt (string or parts array) into the
|
|
85
|
-
* wire-format parts array. Every text part is base64-encoded; non-text
|
|
86
|
-
* parts pass through. A bare string is wrapped in a single text part.
|
|
87
|
-
*/
|
|
88
|
-
function encodePromptForWire(message) {
|
|
89
|
-
return (typeof message === "string" ? [{
|
|
90
|
-
type: "text",
|
|
91
|
-
text: message
|
|
92
|
-
}] : message).map((part) => part.type === "text" ? {
|
|
93
|
-
type: "text",
|
|
94
|
-
text: encodeTextForWire(part.text)
|
|
95
|
-
} : part);
|
|
96
|
-
}
|
|
97
|
-
//#endregion
|
|
98
72
|
//#region src/trace-exporter.ts
|
|
99
73
|
function buildTraceExportPayload(bundle, format = "tangle", serviceName = "tangle-sandbox") {
|
|
100
74
|
if (format === "tangle") return bundle;
|
|
@@ -635,6 +609,29 @@ var SandboxSession = class SandboxSession {
|
|
|
635
609
|
async answer(answers) {
|
|
636
610
|
return this.box._answerQuestion(this.id, answers);
|
|
637
611
|
}
|
|
612
|
+
/** Return the newest plan awaiting a decision for this session. */
|
|
613
|
+
async plan() {
|
|
614
|
+
return this.box._currentPlan(this.id);
|
|
615
|
+
}
|
|
616
|
+
/** Approve a durable plan and enqueue its separate execution turn. */
|
|
617
|
+
async approvePlan(planId) {
|
|
618
|
+
const id = planId ?? (await this.requireCurrentPlan()).id;
|
|
619
|
+
return this.box._decidePlan(this.id, id, { outcome: "approved" });
|
|
620
|
+
}
|
|
621
|
+
/** Reject a durable plan with required feedback and enqueue a revision turn. */
|
|
622
|
+
async rejectPlan(feedback, planId) {
|
|
623
|
+
if (!feedback.trim()) throw new Error("Plan rejection feedback is required");
|
|
624
|
+
const id = planId ?? (await this.requireCurrentPlan()).id;
|
|
625
|
+
return this.box._decidePlan(this.id, id, {
|
|
626
|
+
outcome: "rejected",
|
|
627
|
+
feedback: feedback.trim()
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
async requireCurrentPlan() {
|
|
631
|
+
const plan = await this.plan();
|
|
632
|
+
if (!plan) throw new Error("No durable plan is awaiting a decision");
|
|
633
|
+
return plan;
|
|
634
|
+
}
|
|
638
635
|
};
|
|
639
636
|
//#endregion
|
|
640
637
|
//#region src/task-session.ts
|
|
@@ -738,6 +735,10 @@ const SNAPSHOT_RESTORE_POLL_INTERVAL_MS = 1e3;
|
|
|
738
735
|
const SNAPSHOT_RESTORE_TIMEOUT_MS = 600 * 1e3;
|
|
739
736
|
const SNAPSHOT_VISIBILITY_POLL_INTERVAL_MS = 1e3;
|
|
740
737
|
const SNAPSHOT_VISIBILITY_TIMEOUT_MS = 120 * 1e3;
|
|
738
|
+
const EXEC_DEFAULT_TIMEOUT_MS = 3e4;
|
|
739
|
+
const EXEC_MIN_TIMEOUT_MS = 100;
|
|
740
|
+
const EXEC_MAX_TIMEOUT_MS = 6e5;
|
|
741
|
+
const EXEC_TRANSPORT_MARGIN_MS = 15e3;
|
|
741
742
|
const SNAPSHOT_CREATE_REQUEST_TIMEOUT_MS = 600 * 1e3;
|
|
742
743
|
function toSnapshotResult(data) {
|
|
743
744
|
return {
|
|
@@ -767,6 +768,11 @@ function runtimeFilesystemPath(endpoint, path) {
|
|
|
767
768
|
* requirements; a turn whose tool failed (even if narrated around) fails.
|
|
768
769
|
*/
|
|
769
770
|
function deriveOutcome(inputs) {
|
|
771
|
+
if (inputs.plan) return {
|
|
772
|
+
status: "awaiting_plan_decision",
|
|
773
|
+
success: false,
|
|
774
|
+
error: "Agent is awaiting a durable plan decision"
|
|
775
|
+
};
|
|
770
776
|
if (inputs.question) return {
|
|
771
777
|
status: "awaiting_question",
|
|
772
778
|
success: false,
|
|
@@ -799,6 +805,18 @@ function deriveOutcome(inputs) {
|
|
|
799
805
|
success: true
|
|
800
806
|
};
|
|
801
807
|
}
|
|
808
|
+
function readAwaitingPlan(data) {
|
|
809
|
+
const direct = data.plan;
|
|
810
|
+
const outcome = data.outcome;
|
|
811
|
+
const candidate = direct && typeof direct === "object" ? direct : outcome && typeof outcome === "object" && outcome.type === "awaiting_plan_decision" ? outcome.plan : void 0;
|
|
812
|
+
if (!candidate || typeof candidate !== "object") return void 0;
|
|
813
|
+
const plan = candidate;
|
|
814
|
+
if (typeof plan.id !== "string" || typeof plan.revision !== "number" || typeof plan.body !== "string" || typeof plan.submittedAt !== "string") return;
|
|
815
|
+
return plan;
|
|
816
|
+
}
|
|
817
|
+
function readPlanFromEvent(event) {
|
|
818
|
+
return event.type === "result" || event.type === "done" || event.type === "plan.submitted" ? readAwaitingPlan(event.data) : void 0;
|
|
819
|
+
}
|
|
802
820
|
/** Read tool invocations off a `result` event payload. */
|
|
803
821
|
function readToolInvocations(data) {
|
|
804
822
|
const raw = data.toolInvocations;
|
|
@@ -920,6 +938,22 @@ const WRITE_MANY_DEFAULT_PACE_MS = 150;
|
|
|
920
938
|
const WRITE_MANY_DEFAULT_MAX_RETRIES = 4;
|
|
921
939
|
const WRITE_MANY_RETRY_BASE_MS = 250;
|
|
922
940
|
const WRITE_MANY_RETRY_MAX_MS = 2e3;
|
|
941
|
+
function parseTextFileReadResponse(body) {
|
|
942
|
+
let payload;
|
|
943
|
+
try {
|
|
944
|
+
payload = JSON.parse(body);
|
|
945
|
+
} catch {
|
|
946
|
+
throw new ServerError("Runtime returned invalid JSON for file read", 502, {
|
|
947
|
+
origin: "runtime",
|
|
948
|
+
endpoint: "/files/read"
|
|
949
|
+
});
|
|
950
|
+
}
|
|
951
|
+
if (typeof payload !== "object" || payload === null || !("success" in payload) || payload.success !== true || !("data" in payload) || typeof payload.data !== "object" || payload.data === null || !("content" in payload.data) || typeof payload.data.content !== "string" || !("encoding" in payload.data) || payload.data.encoding !== "utf8") throw new ServerError("Runtime returned a malformed text file response", 502, {
|
|
952
|
+
origin: "runtime",
|
|
953
|
+
endpoint: "/files/read"
|
|
954
|
+
});
|
|
955
|
+
return payload.data.content;
|
|
956
|
+
}
|
|
923
957
|
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
924
958
|
/** Transient file-write failures worth retrying: rate-limit (quota), 5xx,
|
|
925
959
|
* transport (network), and request timeouts. A non-transient error (bad path,
|
|
@@ -1240,28 +1274,31 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1240
1274
|
*/
|
|
1241
1275
|
async exec(command, options) {
|
|
1242
1276
|
await this.ensureRunning();
|
|
1277
|
+
const timeoutMs = options?.timeoutMs ?? EXEC_DEFAULT_TIMEOUT_MS;
|
|
1278
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs < EXEC_MIN_TIMEOUT_MS || timeoutMs > EXEC_MAX_TIMEOUT_MS) throw new ValidationError(`exec timeoutMs must be an integer between ${EXEC_MIN_TIMEOUT_MS} and ${EXEC_MAX_TIMEOUT_MS}`);
|
|
1243
1279
|
const headers = {};
|
|
1244
1280
|
if (options?.sessionId) headers["X-Session-Id"] = options.sessionId;
|
|
1245
|
-
const response = await this.runtimeFetch("/
|
|
1281
|
+
const response = await this.runtimeFetch("/process/spawn", {
|
|
1246
1282
|
method: "POST",
|
|
1247
1283
|
headers,
|
|
1248
1284
|
body: JSON.stringify({
|
|
1249
1285
|
command,
|
|
1250
1286
|
cwd: options?.cwd,
|
|
1251
1287
|
env: options?.env,
|
|
1252
|
-
|
|
1288
|
+
timeoutMs,
|
|
1289
|
+
blocking: true
|
|
1253
1290
|
})
|
|
1254
|
-
});
|
|
1291
|
+
}, { timeoutMs: timeoutMs + EXEC_TRANSPORT_MARGIN_MS });
|
|
1255
1292
|
if (!response.ok) {
|
|
1256
1293
|
const body = await response.text();
|
|
1257
1294
|
throw parseErrorResponse(response.status, body, void 0, response.headers);
|
|
1258
1295
|
}
|
|
1259
|
-
const
|
|
1260
|
-
|
|
1296
|
+
const result = (await response.json()).data;
|
|
1297
|
+
if (!result || typeof result.exitCode !== "number" || typeof result.stdout !== "string" || typeof result.stderr !== "string") throw new ServerError("Sandbox process response was malformed", 502);
|
|
1261
1298
|
return {
|
|
1262
|
-
exitCode: result.exitCode
|
|
1263
|
-
stdout: result.stdout
|
|
1264
|
-
stderr: result.stderr
|
|
1299
|
+
exitCode: result.exitCode,
|
|
1300
|
+
stdout: result.stdout,
|
|
1301
|
+
stderr: result.stderr
|
|
1265
1302
|
};
|
|
1266
1303
|
}
|
|
1267
1304
|
/**
|
|
@@ -1331,18 +1368,20 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1331
1368
|
*/
|
|
1332
1369
|
async read(path, options) {
|
|
1333
1370
|
await this.ensureRunning();
|
|
1334
|
-
const headers = {};
|
|
1335
|
-
if (options?.sessionId) headers["X-Session-Id"] = options.sessionId;
|
|
1371
|
+
const headers = options?.sessionId ? { "X-Session-Id": options.sessionId } : void 0;
|
|
1336
1372
|
const response = await this.runtimeFetch("/files/read", {
|
|
1337
1373
|
method: "POST",
|
|
1338
1374
|
headers,
|
|
1339
|
-
body: JSON.stringify({
|
|
1375
|
+
body: JSON.stringify({
|
|
1376
|
+
path,
|
|
1377
|
+
encoding: "utf8"
|
|
1378
|
+
})
|
|
1340
1379
|
});
|
|
1341
1380
|
if (!response.ok) {
|
|
1342
1381
|
const body = await response.text();
|
|
1343
1382
|
throw parseErrorResponse(response.status, body, void 0, response.headers);
|
|
1344
1383
|
}
|
|
1345
|
-
return (await response.
|
|
1384
|
+
return parseTextFileReadResponse(await response.text());
|
|
1346
1385
|
}
|
|
1347
1386
|
/**
|
|
1348
1387
|
* Write content to a file in the sandbox.
|
|
@@ -1393,7 +1432,10 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1393
1432
|
if (started && paceMs > 0) await delay(paceMs);
|
|
1394
1433
|
started = true;
|
|
1395
1434
|
try {
|
|
1396
|
-
await this.write(file.path, file.content, {
|
|
1435
|
+
await this.write(file.path, file.content, {
|
|
1436
|
+
mode: file.mode,
|
|
1437
|
+
encoding: file.encoding
|
|
1438
|
+
});
|
|
1397
1439
|
break;
|
|
1398
1440
|
} catch (err) {
|
|
1399
1441
|
if (isTransientWriteError(err) && attempt < maxRetries) {
|
|
@@ -1420,10 +1462,11 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1420
1462
|
let costUsd;
|
|
1421
1463
|
let toolInvocations;
|
|
1422
1464
|
let question;
|
|
1465
|
+
let plan;
|
|
1423
1466
|
let terminalReached = false;
|
|
1424
1467
|
const controller = new AbortController();
|
|
1425
1468
|
try {
|
|
1426
|
-
const signal = options?.signal ?
|
|
1469
|
+
const signal = options?.signal ? combineAbortSignals([options.signal, controller.signal]) : controller.signal;
|
|
1427
1470
|
for await (const event of this.streamPrompt(message, {
|
|
1428
1471
|
...options,
|
|
1429
1472
|
signal
|
|
@@ -1433,6 +1476,7 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1433
1476
|
usage = readTokenUsage(event.data) ?? usage;
|
|
1434
1477
|
costUsd = readTokenCostUsd(event.data) ?? costUsd;
|
|
1435
1478
|
}
|
|
1479
|
+
plan = readPlanFromEvent(event) ?? plan;
|
|
1436
1480
|
if (event.type === "result") toolInvocations = readToolInvocations(event.data) ?? toolInvocations;
|
|
1437
1481
|
if (event.type === "result" || event.type === "done") terminalReached = true;
|
|
1438
1482
|
if (event.type === "interaction") question = readQuestionEvent(event.data) ?? question;
|
|
@@ -1449,7 +1493,8 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1449
1493
|
runErrorCode,
|
|
1450
1494
|
toolInvocations,
|
|
1451
1495
|
approval,
|
|
1452
|
-
question
|
|
1496
|
+
question,
|
|
1497
|
+
plan
|
|
1453
1498
|
});
|
|
1454
1499
|
return {
|
|
1455
1500
|
success: outcome.success,
|
|
@@ -1460,6 +1505,7 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1460
1505
|
toolInvocations,
|
|
1461
1506
|
approval,
|
|
1462
1507
|
question,
|
|
1508
|
+
plan,
|
|
1463
1509
|
traceId,
|
|
1464
1510
|
durationMs: Date.now() - startTime,
|
|
1465
1511
|
usage,
|
|
@@ -1544,11 +1590,11 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1544
1590
|
* `error` + `done` so callers never see a thrown generator.
|
|
1545
1591
|
*/
|
|
1546
1592
|
async *streamPromptInner(message, options) {
|
|
1593
|
+
if (options?.lastEventId && !options.executionId) throw new ValidationError("lastEventId requires executionId so the SDK can replay the correct execution", { executionId: "Required when lastEventId is set" });
|
|
1547
1594
|
await this.ensureRunning();
|
|
1548
|
-
const parts = encodePromptForWire(message);
|
|
1549
1595
|
const timeoutMs = typeof options?.timeoutMs === "number" && options.timeoutMs > 0 ? options.timeoutMs : void 0;
|
|
1550
1596
|
const timeoutSignal = timeoutMs ? AbortSignal.timeout(timeoutMs) : void 0;
|
|
1551
|
-
const streamSignal = timeoutSignal ? options?.signal ?
|
|
1597
|
+
const streamSignal = timeoutSignal ? options?.signal ? combineAbortSignals([options.signal, timeoutSignal]) : timeoutSignal : options?.signal;
|
|
1552
1598
|
const throwIfTimedOut = () => {
|
|
1553
1599
|
if (!timeoutSignal?.aborted || !timeoutMs) return;
|
|
1554
1600
|
throw new TimeoutError(timeoutMs, `Prompt stream timed out after ${timeoutMs}ms`, {
|
|
@@ -1563,35 +1609,58 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1563
1609
|
...data
|
|
1564
1610
|
});
|
|
1565
1611
|
};
|
|
1612
|
+
const explicitReplay = Boolean(options?.executionId && options.lastEventId);
|
|
1613
|
+
const requestEndpoint = explicitReplay ? `/agents/run/${encodeURIComponent(options?.executionId ?? "")}/events?lastEventId=${encodeURIComponent(options?.lastEventId ?? "")}&format=sse` : "/agents/run/stream";
|
|
1614
|
+
let lastEventId = options?.lastEventId;
|
|
1615
|
+
let executionId = options?.executionId;
|
|
1616
|
+
let sessionId = options?.sessionId;
|
|
1617
|
+
let receivedTerminal = false;
|
|
1618
|
+
const RECONNECT_BACKOFF_MS = [
|
|
1619
|
+
2e3,
|
|
1620
|
+
4e3,
|
|
1621
|
+
8e3,
|
|
1622
|
+
16e3,
|
|
1623
|
+
3e4,
|
|
1624
|
+
3e4
|
|
1625
|
+
];
|
|
1626
|
+
const MAX_RECONNECT_ATTEMPTS = RECONNECT_BACKOFF_MS.length;
|
|
1627
|
+
let lastReplayStatus;
|
|
1628
|
+
let lastExecutionStatus;
|
|
1629
|
+
let firstSseEventSeen = false;
|
|
1566
1630
|
let response;
|
|
1567
1631
|
try {
|
|
1568
1632
|
logStreamCheckpoint("runtime_post_start", {
|
|
1569
|
-
endpoint:
|
|
1633
|
+
endpoint: requestEndpoint,
|
|
1570
1634
|
sessionId: options?.sessionId,
|
|
1571
1635
|
executionId: options?.executionId,
|
|
1572
1636
|
hasLastEventId: Boolean(options?.lastEventId),
|
|
1573
|
-
detach: options?.detach === true
|
|
1637
|
+
detach: options?.detach === true,
|
|
1638
|
+
replayOnly: explicitReplay
|
|
1574
1639
|
});
|
|
1575
|
-
|
|
1640
|
+
const request = explicitReplay ? {
|
|
1641
|
+
method: "GET",
|
|
1642
|
+
signal: streamSignal
|
|
1643
|
+
} : {
|
|
1576
1644
|
method: "POST",
|
|
1577
1645
|
signal: streamSignal,
|
|
1578
1646
|
headers: {
|
|
1579
1647
|
...options?.executionId ? { "X-Execution-ID": options.executionId } : {},
|
|
1580
|
-
...options?.lastEventId ? { "Last-Event-ID": options.lastEventId } : {},
|
|
1581
1648
|
...options?.detach ? { "x-agent-detach": "true" } : {}
|
|
1582
1649
|
},
|
|
1583
1650
|
body: JSON.stringify({
|
|
1584
1651
|
identifier: "default",
|
|
1585
|
-
parts,
|
|
1652
|
+
parts: encodePromptForWire(message),
|
|
1586
1653
|
sessionId: options?.sessionId,
|
|
1587
1654
|
turnId: options?.turnId,
|
|
1588
1655
|
metadata: options?.context,
|
|
1589
1656
|
requireVisibleAssistantOutput: options?.requireVisibleAssistantOutput,
|
|
1590
1657
|
...timeoutMs ? { timeoutMs } : {},
|
|
1591
1658
|
...options?.detach ? { detach: true } : {},
|
|
1592
|
-
backend: normalizeRuntimeBackendConfig(options?.backend ?? this.defaultRuntimeBackend,
|
|
1659
|
+
backend: normalizeRuntimeBackendConfig(options?.backend ?? this.defaultRuntimeBackend),
|
|
1660
|
+
...options?.model ? { model: normalizeRuntimeModelOverride(options.model) } : {}
|
|
1593
1661
|
})
|
|
1594
|
-
}
|
|
1662
|
+
};
|
|
1663
|
+
response = await this.runtimeFetch(requestEndpoint, request);
|
|
1595
1664
|
logStreamCheckpoint("runtime_response_headers", {
|
|
1596
1665
|
status: response.status,
|
|
1597
1666
|
ok: response.ok
|
|
@@ -1601,27 +1670,20 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1601
1670
|
throwIfTimedOut();
|
|
1602
1671
|
throw err;
|
|
1603
1672
|
}
|
|
1604
|
-
if (!response.ok) {
|
|
1673
|
+
if (!response.ok && !explicitReplay) {
|
|
1605
1674
|
const body = await response.text();
|
|
1606
1675
|
throw parseErrorResponse(response.status, body, void 0, response.headers);
|
|
1607
1676
|
}
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
3e4
|
|
1619
|
-
];
|
|
1620
|
-
const MAX_RECONNECT_ATTEMPTS = RECONNECT_BACKOFF_MS.length;
|
|
1621
|
-
let lastReplayStatus;
|
|
1622
|
-
let lastExecutionStatus;
|
|
1623
|
-
let firstSseEventSeen = false;
|
|
1624
|
-
for await (const event of this.parseSSEStream(response, streamSignal)) {
|
|
1677
|
+
if (!response.ok) {
|
|
1678
|
+
lastReplayStatus = response.status;
|
|
1679
|
+
await response.text();
|
|
1680
|
+
logStreamCheckpoint("replay_response_status", {
|
|
1681
|
+
attempt: 0,
|
|
1682
|
+
status: response.status,
|
|
1683
|
+
ok: false
|
|
1684
|
+
});
|
|
1685
|
+
} else for await (const event of this.parseSSEStream(response, streamSignal)) {
|
|
1686
|
+
if (event.type === "history.replay.start" || event.type === "history.replay.end") continue;
|
|
1625
1687
|
if (!firstSseEventSeen) {
|
|
1626
1688
|
firstSseEventSeen = true;
|
|
1627
1689
|
logStreamCheckpoint("first_sse_event", {
|
|
@@ -1922,6 +1984,7 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1922
1984
|
let costUsd;
|
|
1923
1985
|
let toolInvocations;
|
|
1924
1986
|
let question;
|
|
1987
|
+
let plan;
|
|
1925
1988
|
let terminalReached = false;
|
|
1926
1989
|
try {
|
|
1927
1990
|
for await (const event of this.streamPrompt(prompt, {
|
|
@@ -1938,6 +2001,7 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1938
2001
|
}
|
|
1939
2002
|
if (event.type === "done" && costUsd === void 0) costUsd = readTokenCostUsd(event.data);
|
|
1940
2003
|
if (event.type === "result" || event.type === "done") terminalReached = true;
|
|
2004
|
+
plan = readPlanFromEvent(event) ?? plan;
|
|
1941
2005
|
if (event.type === "interaction") question = readQuestionEvent(event.data) ?? question;
|
|
1942
2006
|
if (event.type === "trace.id") traceId = event.data.traceId;
|
|
1943
2007
|
if (event.type === "error") {
|
|
@@ -1952,7 +2016,8 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1952
2016
|
runErrorCode,
|
|
1953
2017
|
toolInvocations,
|
|
1954
2018
|
approval,
|
|
1955
|
-
question
|
|
2019
|
+
question,
|
|
2020
|
+
plan
|
|
1956
2021
|
});
|
|
1957
2022
|
return {
|
|
1958
2023
|
success: outcome.success,
|
|
@@ -1963,6 +2028,7 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1963
2028
|
toolInvocations,
|
|
1964
2029
|
approval,
|
|
1965
2030
|
question,
|
|
2031
|
+
plan,
|
|
1966
2032
|
traceId,
|
|
1967
2033
|
durationMs: Date.now() - startTime,
|
|
1968
2034
|
usage,
|
|
@@ -2099,11 +2165,19 @@ var SandboxInstance = class SandboxInstance {
|
|
|
2099
2165
|
async resourceUsage() {
|
|
2100
2166
|
await this.ensureRunning();
|
|
2101
2167
|
const response = await this.runtimeFetch("/health/detailed", { method: "GET" });
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2168
|
+
const body = await response.text();
|
|
2169
|
+
let data;
|
|
2170
|
+
try {
|
|
2171
|
+
data = JSON.parse(body);
|
|
2172
|
+
} catch (error) {
|
|
2173
|
+
if (response.ok) throw new NetworkError("Detailed health returned a non-JSON response", error instanceof Error ? error : void 0, {
|
|
2174
|
+
endpoint: "/health/detailed",
|
|
2175
|
+
origin: "runtime"
|
|
2176
|
+
});
|
|
2105
2177
|
}
|
|
2106
|
-
|
|
2178
|
+
if (data?.resources) return data.resources;
|
|
2179
|
+
if (!response.ok) throw parseErrorResponse(response.status, body, void 0, response.headers);
|
|
2180
|
+
return null;
|
|
2107
2181
|
}
|
|
2108
2182
|
/**
|
|
2109
2183
|
* Git capability object for repository operations.
|
|
@@ -2322,6 +2396,7 @@ var SandboxInstance = class SandboxInstance {
|
|
|
2322
2396
|
writeMany: (files, options) => this.writeMany(files, options),
|
|
2323
2397
|
search: (query, options) => this.search(query, options),
|
|
2324
2398
|
upload: (localPath, remotePath, options) => this.fsUpload(localPath, remotePath, options),
|
|
2399
|
+
uploadData: (remotePath, data, options) => this.fsUploadData(remotePath, data, options),
|
|
2325
2400
|
download: (remotePath, localPath, options) => this.fsDownload(remotePath, localPath, options),
|
|
2326
2401
|
uploadDir: (localDir, remoteDir) => this.fsUploadDir(localDir, remoteDir),
|
|
2327
2402
|
downloadDir: (remoteDir, localDir) => this.fsDownloadDir(remoteDir, localDir),
|
|
@@ -2369,6 +2444,14 @@ var SandboxInstance = class SandboxInstance {
|
|
|
2369
2444
|
percentage: 100
|
|
2370
2445
|
});
|
|
2371
2446
|
}
|
|
2447
|
+
/** See {@link FileSystem.uploadData}. Delegates to the shared, browser-safe
|
|
2448
|
+
* chunked-upload flow in `lib/chunked-upload.ts`, wired to this sandbox's
|
|
2449
|
+
* runtime endpoint (works over the gateway proxy or a direct connection —
|
|
2450
|
+
* both are transparent through `runtimeFetch`). */
|
|
2451
|
+
async fsUploadData(remotePath, data, options) {
|
|
2452
|
+
await this.ensureRunning();
|
|
2453
|
+
return uploadChunked({ fetch: (path, init) => this.runtimeFetch(path, init) }, remotePath, data, options);
|
|
2454
|
+
}
|
|
2372
2455
|
async fsDownload(remotePath, localPath, options) {
|
|
2373
2456
|
await this.ensureRunning();
|
|
2374
2457
|
const fs = await import("node:fs/promises");
|
|
@@ -2929,6 +3012,7 @@ var SandboxInstance = class SandboxInstance {
|
|
|
2929
3012
|
*/
|
|
2930
3013
|
get process() {
|
|
2931
3014
|
return {
|
|
3015
|
+
ensureDevServer: (options) => this.processEnsureDevServer(options),
|
|
2932
3016
|
spawn: (command, options) => this.processSpawn(command, options),
|
|
2933
3017
|
spawnExact: (executable, args, options) => this.processSpawnExact(executable, args, options),
|
|
2934
3018
|
runCode: (code, options) => this.processRunCode(code, options),
|
|
@@ -2936,17 +3020,35 @@ var SandboxInstance = class SandboxInstance {
|
|
|
2936
3020
|
get: (pid) => this.processGet(pid)
|
|
2937
3021
|
};
|
|
2938
3022
|
}
|
|
2939
|
-
async
|
|
3023
|
+
async processEnsureDevServer(options) {
|
|
3024
|
+
await this.ensureRunning();
|
|
3025
|
+
if (options?.sessionId !== void 0) assertValidSessionId(options.sessionId);
|
|
3026
|
+
const headers = options?.sessionId ? { "X-Session-Id": options.sessionId } : void 0;
|
|
3027
|
+
const response = await this.runtimeFetch("/process/ensure-dev-server", {
|
|
3028
|
+
method: "POST",
|
|
3029
|
+
headers,
|
|
3030
|
+
body: JSON.stringify({})
|
|
3031
|
+
});
|
|
3032
|
+
if (!response.ok) {
|
|
3033
|
+
const body = await response.text();
|
|
3034
|
+
throw parseErrorResponse(response.status, body, void 0, response.headers);
|
|
3035
|
+
}
|
|
3036
|
+
const payload = await response.json();
|
|
3037
|
+
return {
|
|
3038
|
+
...payload.data,
|
|
3039
|
+
startedAt: new Date(payload.data.startedAt)
|
|
3040
|
+
};
|
|
3041
|
+
}
|
|
3042
|
+
async processSpawn(command, options) {
|
|
2940
3043
|
await this.ensureRunning();
|
|
2941
|
-
const response = await this.runtimeFetch("/process/spawn
|
|
3044
|
+
const response = await this.runtimeFetch("/process/spawn", {
|
|
2942
3045
|
method: "POST",
|
|
2943
3046
|
body: JSON.stringify({
|
|
2944
|
-
|
|
2945
|
-
args,
|
|
3047
|
+
command,
|
|
2946
3048
|
cwd: options?.cwd,
|
|
2947
|
-
env: options?.env
|
|
2948
|
-
|
|
2949
|
-
|
|
3049
|
+
env: options?.env,
|
|
3050
|
+
timeoutMs: options?.timeoutMs,
|
|
3051
|
+
blocking: false
|
|
2950
3052
|
})
|
|
2951
3053
|
});
|
|
2952
3054
|
if (!response.ok) {
|
|
@@ -2954,17 +3056,19 @@ var SandboxInstance = class SandboxInstance {
|
|
|
2954
3056
|
throw parseErrorResponse(response.status, body, void 0, response.headers);
|
|
2955
3057
|
}
|
|
2956
3058
|
const data = await response.json();
|
|
2957
|
-
return this.createProcessHandle(data.data.pid,
|
|
3059
|
+
return this.createProcessHandle(data.data.pid, command);
|
|
2958
3060
|
}
|
|
2959
|
-
async
|
|
3061
|
+
async processSpawnExact(executable, args, options) {
|
|
2960
3062
|
await this.ensureRunning();
|
|
2961
3063
|
const response = await this.runtimeFetch("/process/spawn", {
|
|
2962
3064
|
method: "POST",
|
|
2963
3065
|
body: JSON.stringify({
|
|
2964
|
-
|
|
3066
|
+
executable,
|
|
3067
|
+
args,
|
|
2965
3068
|
cwd: options?.cwd,
|
|
2966
3069
|
env: options?.env,
|
|
2967
3070
|
timeoutMs: options?.timeoutMs,
|
|
3071
|
+
stdin: options?.stdin,
|
|
2968
3072
|
blocking: false
|
|
2969
3073
|
})
|
|
2970
3074
|
});
|
|
@@ -2973,7 +3077,7 @@ var SandboxInstance = class SandboxInstance {
|
|
|
2973
3077
|
throw parseErrorResponse(response.status, body, void 0, response.headers);
|
|
2974
3078
|
}
|
|
2975
3079
|
const data = await response.json();
|
|
2976
|
-
return this.createProcessHandle(data.data.pid,
|
|
3080
|
+
return this.createProcessHandle(data.data.pid, JSON.stringify([executable, ...args]));
|
|
2977
3081
|
}
|
|
2978
3082
|
async processRunCode(code, options) {
|
|
2979
3083
|
await this.ensureRunning();
|
|
@@ -3856,7 +3960,7 @@ var SandboxInstance = class SandboxInstance {
|
|
|
3856
3960
|
async resume(options = {}) {
|
|
3857
3961
|
if (options.timeoutMs !== void 0 && (!Number.isFinite(options.timeoutMs) || options.timeoutMs <= 0)) throw new ValidationError("resume timeoutMs must be a positive number");
|
|
3858
3962
|
const timeoutSignal = options.timeoutMs ? AbortSignal.timeout(options.timeoutMs) : void 0;
|
|
3859
|
-
const signal = timeoutSignal ? options.signal ?
|
|
3963
|
+
const signal = timeoutSignal ? options.signal ? combineAbortSignals([options.signal, timeoutSignal]) : timeoutSignal : options.signal;
|
|
3860
3964
|
const response = await this.client.fetch(`/v1/sandboxes/${this.id}/resume`, {
|
|
3861
3965
|
method: "POST",
|
|
3862
3966
|
...signal ? { signal } : {}
|
|
@@ -4097,9 +4201,10 @@ var SandboxInstance = class SandboxInstance {
|
|
|
4097
4201
|
await this.refresh();
|
|
4098
4202
|
if (this.status !== "running") throw new StateError(`Sandbox is not running (status: ${this.status})`, this.status, "running");
|
|
4099
4203
|
}
|
|
4100
|
-
async runtimeFetch(path, options) {
|
|
4204
|
+
async runtimeFetch(path, options, fetchOptions) {
|
|
4101
4205
|
const url = `/v1/sandboxes/${encodeURIComponent(this.id)}/runtime${path}`;
|
|
4102
4206
|
try {
|
|
4207
|
+
if (fetchOptions) return await this.client.fetch(url, options, fetchOptions);
|
|
4103
4208
|
return options ? await this.client.fetch(url, options) : await this.client.fetch(url);
|
|
4104
4209
|
} catch (err) {
|
|
4105
4210
|
throw new NetworkError(`Failed to connect to sandbox: ${err instanceof Error ? err.message : String(err)}`, err instanceof Error ? err : void 0, {
|
|
@@ -4329,24 +4434,8 @@ var SandboxInstance = class SandboxInstance {
|
|
|
4329
4434
|
* abort stamps `interrupted: true` explicitly.
|
|
4330
4435
|
*/
|
|
4331
4436
|
async messages(opts) {
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
if (opts.limit !== void 0) params.set("limit", String(opts.limit));
|
|
4335
|
-
if (opts.offset !== void 0) params.set("offset", String(opts.offset));
|
|
4336
|
-
if (opts.since !== void 0) params.set("since", String(opts.since));
|
|
4337
|
-
const query = params.toString() ? `?${params.toString()}` : "";
|
|
4338
|
-
const response = await this.client.fetch(`/v1/sessions/${encodeURIComponent(opts.sessionId)}/messages${query}`, { method: "GET" });
|
|
4339
|
-
if (!response.ok) {
|
|
4340
|
-
const body = await response.text();
|
|
4341
|
-
throw parseErrorResponse(response.status, body, void 0, response.headers);
|
|
4342
|
-
}
|
|
4343
|
-
return (await response.json()).map((m) => ({
|
|
4344
|
-
id: m.info.id,
|
|
4345
|
-
role: m.info.role,
|
|
4346
|
-
timestamp: m.info.timestamp,
|
|
4347
|
-
parts: m.parts,
|
|
4348
|
-
metadata: m.info.metadata
|
|
4349
|
-
}));
|
|
4437
|
+
const { sessionId, ...options } = opts;
|
|
4438
|
+
return this.runtime.messages(sessionId, options);
|
|
4350
4439
|
}
|
|
4351
4440
|
/** @internal — invoked by SandboxSession.sendMessage(). */
|
|
4352
4441
|
async _sessionSendMessage(id, request, options = {}) {
|
|
@@ -4455,11 +4544,13 @@ var SandboxInstance = class SandboxInstance {
|
|
|
4455
4544
|
}
|
|
4456
4545
|
if (info) {
|
|
4457
4546
|
const result = await this._sessionResult(opts.sessionId);
|
|
4547
|
+
const settled = settleTurnDrive({ ...result });
|
|
4548
|
+
if (settled.state === "awaiting_plan_decision") return settled;
|
|
4458
4549
|
if (!result.success) return {
|
|
4459
4550
|
state: "failed",
|
|
4460
4551
|
error: `session ${opts.sessionId} ${info.status}: ${result.error ?? "no error detail"}`
|
|
4461
4552
|
};
|
|
4462
|
-
return
|
|
4553
|
+
return settled;
|
|
4463
4554
|
}
|
|
4464
4555
|
await this.dispatchPrompt(message, {
|
|
4465
4556
|
...opts,
|
|
@@ -4468,15 +4559,10 @@ var SandboxInstance = class SandboxInstance {
|
|
|
4468
4559
|
return { state: "running" };
|
|
4469
4560
|
}
|
|
4470
4561
|
/**
|
|
4471
|
-
* Mint a scoped, time-bounded JWT for
|
|
4472
|
-
*
|
|
4473
|
-
*
|
|
4474
|
-
*
|
|
4475
|
-
*
|
|
4476
|
-
* Use this to give a browser direct read access to the sandbox without
|
|
4477
|
-
* leaking the full bearer (`box.connection.authToken`). The returned
|
|
4478
|
-
* token verifies against the same sidecar middleware that already
|
|
4479
|
-
* gates ProductTokenIssuer-issued JWTs — no new sidecar surface.
|
|
4562
|
+
* Mint a scoped, time-bounded JWT for browser access (Issue #913 Gap 1).
|
|
4563
|
+
* Authority is the caller's `TANGLE_API_KEY` (sk-tan-*); signing secrets
|
|
4564
|
+
* stay server-side. Session/project scopes work with SessionGatewayClient,
|
|
4565
|
+
* while session-runtime/read-only scopes work with SandboxRuntimeClient.
|
|
4480
4566
|
*/
|
|
4481
4567
|
async mintScopedToken(opts) {
|
|
4482
4568
|
if ((opts.scope === "session" || opts.scope === "session-runtime") && !opts.sessionId) throw new ValidationError(`${opts.scope} scope requires a sessionId`);
|
|
@@ -4515,17 +4601,22 @@ var SandboxInstance = class SandboxInstance {
|
|
|
4515
4601
|
return normalizeSessionInfo(await response.json());
|
|
4516
4602
|
}
|
|
4517
4603
|
/** @internal — invoked by SandboxSession.events(). */
|
|
4518
|
-
async *_sessionEvents(id, opts) {
|
|
4604
|
+
async *_sessionEvents(id, opts, stopOnTurnTerminal = false) {
|
|
4519
4605
|
await this.ensureRunning();
|
|
4520
4606
|
const search = new URLSearchParams();
|
|
4521
4607
|
search.set("sessionId", id);
|
|
4522
4608
|
if (opts?.since) search.set("since", opts.since);
|
|
4609
|
+
if (opts?.executionId) search.set("executionId", opts.executionId);
|
|
4523
4610
|
const response = await this.runtimeFetch(`/agents/events?${search.toString()}`, { signal: opts?.signal });
|
|
4524
4611
|
if (!response.ok) {
|
|
4525
4612
|
const body = await response.text();
|
|
4526
4613
|
throw parseErrorResponse(response.status, body, void 0, response.headers);
|
|
4527
4614
|
}
|
|
4528
|
-
|
|
4615
|
+
for await (const event of this.parseSSEStream(response, opts?.signal)) {
|
|
4616
|
+
yield event;
|
|
4617
|
+
const turnTerminal = event.type === "result" || event.type === "done" || event.type === "error";
|
|
4618
|
+
if ((stopOnTurnTerminal || opts?.executionId !== void 0) && turnTerminal) return;
|
|
4619
|
+
}
|
|
4529
4620
|
}
|
|
4530
4621
|
/** @internal — invoked by SandboxSession.result(). */
|
|
4531
4622
|
async _sessionResult(id) {
|
|
@@ -4538,14 +4629,16 @@ var SandboxInstance = class SandboxInstance {
|
|
|
4538
4629
|
let costUsd;
|
|
4539
4630
|
let toolInvocations;
|
|
4540
4631
|
let question;
|
|
4632
|
+
let plan;
|
|
4541
4633
|
let terminalReached = false;
|
|
4542
|
-
for await (const event of this._sessionEvents(id)) {
|
|
4634
|
+
for await (const event of this._sessionEvents(id, void 0, true)) {
|
|
4543
4635
|
response = applySandboxEventText(response, event);
|
|
4544
4636
|
if (event.type === "result" || event.type === "done") {
|
|
4545
4637
|
const data = event.data;
|
|
4546
4638
|
usage = readTokenUsage(data) ?? usage;
|
|
4547
4639
|
costUsd = readTokenCostUsd(data) ?? costUsd;
|
|
4548
4640
|
}
|
|
4641
|
+
plan = readPlanFromEvent(event) ?? plan;
|
|
4549
4642
|
if (event.type === "result") {
|
|
4550
4643
|
const data = event.data;
|
|
4551
4644
|
toolInvocations = readToolInvocations(data) ?? toolInvocations;
|
|
@@ -4569,7 +4662,8 @@ var SandboxInstance = class SandboxInstance {
|
|
|
4569
4662
|
runErrorCode,
|
|
4570
4663
|
toolInvocations,
|
|
4571
4664
|
approval,
|
|
4572
|
-
question
|
|
4665
|
+
question,
|
|
4666
|
+
plan
|
|
4573
4667
|
});
|
|
4574
4668
|
return {
|
|
4575
4669
|
success: outcome.success,
|
|
@@ -4580,6 +4674,7 @@ var SandboxInstance = class SandboxInstance {
|
|
|
4580
4674
|
toolInvocations,
|
|
4581
4675
|
approval,
|
|
4582
4676
|
question,
|
|
4677
|
+
plan,
|
|
4583
4678
|
traceId,
|
|
4584
4679
|
durationMs: Date.now() - startTime,
|
|
4585
4680
|
usage,
|
|
@@ -4711,8 +4806,48 @@ var SandboxInstance = class SandboxInstance {
|
|
|
4711
4806
|
throw parseErrorResponse(response.status, body, void 0, response.headers);
|
|
4712
4807
|
}
|
|
4713
4808
|
}
|
|
4809
|
+
/** @internal — invoked by SandboxSession.plan(). */
|
|
4810
|
+
async _currentPlan(sessionId) {
|
|
4811
|
+
const path = `/v1/sandboxes/${encodeURIComponent(this.id)}/agent-sessions/${encodeURIComponent(sessionId)}/plans/current`;
|
|
4812
|
+
const response = await this.client.fetch(path);
|
|
4813
|
+
if (response.status === 404) return null;
|
|
4814
|
+
if (!response.ok) {
|
|
4815
|
+
const body = await response.text();
|
|
4816
|
+
throw parseErrorResponse(response.status, body, void 0, response.headers);
|
|
4817
|
+
}
|
|
4818
|
+
const decoded = await response.json();
|
|
4819
|
+
if (!decoded.plan) throw new ServerError("Durable plan response is missing plan", 502, {
|
|
4820
|
+
endpoint: path,
|
|
4821
|
+
origin: "control-plane"
|
|
4822
|
+
});
|
|
4823
|
+
return decoded.plan;
|
|
4824
|
+
}
|
|
4825
|
+
/** @internal — invoked by SandboxSession.approvePlan()/rejectPlan(). */
|
|
4826
|
+
async _decidePlan(sessionId, planId, decision) {
|
|
4827
|
+
const path = `/v1/sandboxes/${encodeURIComponent(this.id)}/agent-sessions/${encodeURIComponent(sessionId)}/plans/${encodeURIComponent(planId)}/decision`;
|
|
4828
|
+
const response = await this.client.fetch(path, {
|
|
4829
|
+
method: "POST",
|
|
4830
|
+
body: JSON.stringify(decision)
|
|
4831
|
+
});
|
|
4832
|
+
if (!response.ok) {
|
|
4833
|
+
const body = await response.text();
|
|
4834
|
+
throw parseErrorResponse(response.status, body, void 0, response.headers);
|
|
4835
|
+
}
|
|
4836
|
+
const decoded = await response.json();
|
|
4837
|
+
if (!decoded.plan) throw new ServerError("Plan decision response is missing plan", 502, {
|
|
4838
|
+
endpoint: path,
|
|
4839
|
+
origin: "control-plane"
|
|
4840
|
+
});
|
|
4841
|
+
return decoded.plan;
|
|
4842
|
+
}
|
|
4714
4843
|
};
|
|
4715
4844
|
function settleTurnDrive(result) {
|
|
4845
|
+
const plan = readAwaitingPlan(result);
|
|
4846
|
+
if (plan) return {
|
|
4847
|
+
state: "awaiting_plan_decision",
|
|
4848
|
+
plan,
|
|
4849
|
+
result
|
|
4850
|
+
};
|
|
4716
4851
|
const text = typeof result.text === "string" ? result.text : typeof result.response === "string" ? result.response : void 0;
|
|
4717
4852
|
const status = typeof result.status === "string" ? result.status : deriveOutcome({
|
|
4718
4853
|
terminalReached: true,
|
|
@@ -4795,4 +4930,4 @@ function quoteForShell(value) {
|
|
|
4795
4930
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
4796
4931
|
}
|
|
4797
4932
|
//#endregion
|
|
4798
|
-
export { InteractiveSessionHandle as a, getSandboxEventText as c, exportTraceBundle as d, otelTraceIdForTangleTrace as f,
|
|
4933
|
+
export { InteractiveSessionHandle as a, getSandboxEventText as c, exportTraceBundle as d, otelTraceIdForTangleTrace as f, SandboxSession as i, normalizeConnection as l, parseSSEStream as m, normalizeStartupDiagnostics as n, applySandboxEventText as o, toOtelJson as p, SandboxTaskSession as r, collectAgentResponseText as s, SandboxInstance as t, buildTraceExportPayload as u };
|