@tangle-network/sandbox 0.10.5 → 0.11.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.
- package/README.md +109 -0
- package/dist/agent/index.d.ts +3 -3
- package/dist/{client-CtdFU_nx.d.ts → client-B2YYIsdw.d.ts} +7 -3
- package/dist/{client-Dv93EI90.js → client-C2yRj_dB.js} +481 -63
- 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-V2vjBfHU.d.ts} +2 -2
- package/dist/index.d.ts +32 -17
- package/dist/index.js +9 -285
- package/dist/intelligence/index.js +1 -1
- package/dist/runtime-api-kukaWPtF.js +678 -0
- package/dist/runtime.d.ts +49 -6
- package/dist/runtime.js +7 -5
- package/dist/{sandbox-tksuLdrT.d.ts → sandbox-BSsCz6nP.d.ts} +34 -12
- package/dist/{sandbox-Vh7Aog8b.js → sandbox-r2Lxbw8z.js} +208 -124
- package/dist/tangle/index.d.ts +1 -1
- package/dist/tangle/index.js +1 -1
- package/dist/{tangle-Bqbf3dA8.js → tangle-051CtV2b.js} +2 -2
- package/dist/{types-BGGfKALh.d.ts → types-C4dFf5eL.d.ts} +140 -40
- 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 } from "./runtime-api-kukaWPtF.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
|
|
@@ -767,6 +764,11 @@ function runtimeFilesystemPath(endpoint, path) {
|
|
|
767
764
|
* requirements; a turn whose tool failed (even if narrated around) fails.
|
|
768
765
|
*/
|
|
769
766
|
function deriveOutcome(inputs) {
|
|
767
|
+
if (inputs.plan) return {
|
|
768
|
+
status: "awaiting_plan_decision",
|
|
769
|
+
success: false,
|
|
770
|
+
error: "Agent is awaiting a durable plan decision"
|
|
771
|
+
};
|
|
770
772
|
if (inputs.question) return {
|
|
771
773
|
status: "awaiting_question",
|
|
772
774
|
success: false,
|
|
@@ -799,6 +801,18 @@ function deriveOutcome(inputs) {
|
|
|
799
801
|
success: true
|
|
800
802
|
};
|
|
801
803
|
}
|
|
804
|
+
function readAwaitingPlan(data) {
|
|
805
|
+
const direct = data.plan;
|
|
806
|
+
const outcome = data.outcome;
|
|
807
|
+
const candidate = direct && typeof direct === "object" ? direct : outcome && typeof outcome === "object" && outcome.type === "awaiting_plan_decision" ? outcome.plan : void 0;
|
|
808
|
+
if (!candidate || typeof candidate !== "object") return void 0;
|
|
809
|
+
const plan = candidate;
|
|
810
|
+
if (typeof plan.id !== "string" || typeof plan.revision !== "number" || typeof plan.body !== "string" || typeof plan.submittedAt !== "string") return;
|
|
811
|
+
return plan;
|
|
812
|
+
}
|
|
813
|
+
function readPlanFromEvent(event) {
|
|
814
|
+
return event.type === "result" || event.type === "done" || event.type === "plan.submitted" ? readAwaitingPlan(event.data) : void 0;
|
|
815
|
+
}
|
|
802
816
|
/** Read tool invocations off a `result` event payload. */
|
|
803
817
|
function readToolInvocations(data) {
|
|
804
818
|
const raw = data.toolInvocations;
|
|
@@ -920,6 +934,22 @@ const WRITE_MANY_DEFAULT_PACE_MS = 150;
|
|
|
920
934
|
const WRITE_MANY_DEFAULT_MAX_RETRIES = 4;
|
|
921
935
|
const WRITE_MANY_RETRY_BASE_MS = 250;
|
|
922
936
|
const WRITE_MANY_RETRY_MAX_MS = 2e3;
|
|
937
|
+
function parseTextFileReadResponse(body) {
|
|
938
|
+
let payload;
|
|
939
|
+
try {
|
|
940
|
+
payload = JSON.parse(body);
|
|
941
|
+
} catch {
|
|
942
|
+
throw new ServerError("Runtime returned invalid JSON for file read", 502, {
|
|
943
|
+
origin: "runtime",
|
|
944
|
+
endpoint: "/files/read"
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
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, {
|
|
948
|
+
origin: "runtime",
|
|
949
|
+
endpoint: "/files/read"
|
|
950
|
+
});
|
|
951
|
+
return payload.data.content;
|
|
952
|
+
}
|
|
923
953
|
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
924
954
|
/** Transient file-write failures worth retrying: rate-limit (quota), 5xx,
|
|
925
955
|
* transport (network), and request timeouts. A non-transient error (bad path,
|
|
@@ -1331,18 +1361,20 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1331
1361
|
*/
|
|
1332
1362
|
async read(path, options) {
|
|
1333
1363
|
await this.ensureRunning();
|
|
1334
|
-
const headers = {};
|
|
1335
|
-
if (options?.sessionId) headers["X-Session-Id"] = options.sessionId;
|
|
1364
|
+
const headers = options?.sessionId ? { "X-Session-Id": options.sessionId } : void 0;
|
|
1336
1365
|
const response = await this.runtimeFetch("/files/read", {
|
|
1337
1366
|
method: "POST",
|
|
1338
1367
|
headers,
|
|
1339
|
-
body: JSON.stringify({
|
|
1368
|
+
body: JSON.stringify({
|
|
1369
|
+
path,
|
|
1370
|
+
encoding: "utf8"
|
|
1371
|
+
})
|
|
1340
1372
|
});
|
|
1341
1373
|
if (!response.ok) {
|
|
1342
1374
|
const body = await response.text();
|
|
1343
1375
|
throw parseErrorResponse(response.status, body, void 0, response.headers);
|
|
1344
1376
|
}
|
|
1345
|
-
return (await response.
|
|
1377
|
+
return parseTextFileReadResponse(await response.text());
|
|
1346
1378
|
}
|
|
1347
1379
|
/**
|
|
1348
1380
|
* Write content to a file in the sandbox.
|
|
@@ -1393,7 +1425,10 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1393
1425
|
if (started && paceMs > 0) await delay(paceMs);
|
|
1394
1426
|
started = true;
|
|
1395
1427
|
try {
|
|
1396
|
-
await this.write(file.path, file.content, {
|
|
1428
|
+
await this.write(file.path, file.content, {
|
|
1429
|
+
mode: file.mode,
|
|
1430
|
+
encoding: file.encoding
|
|
1431
|
+
});
|
|
1397
1432
|
break;
|
|
1398
1433
|
} catch (err) {
|
|
1399
1434
|
if (isTransientWriteError(err) && attempt < maxRetries) {
|
|
@@ -1420,10 +1455,11 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1420
1455
|
let costUsd;
|
|
1421
1456
|
let toolInvocations;
|
|
1422
1457
|
let question;
|
|
1458
|
+
let plan;
|
|
1423
1459
|
let terminalReached = false;
|
|
1424
1460
|
const controller = new AbortController();
|
|
1425
1461
|
try {
|
|
1426
|
-
const signal = options?.signal ?
|
|
1462
|
+
const signal = options?.signal ? combineAbortSignals([options.signal, controller.signal]) : controller.signal;
|
|
1427
1463
|
for await (const event of this.streamPrompt(message, {
|
|
1428
1464
|
...options,
|
|
1429
1465
|
signal
|
|
@@ -1433,6 +1469,7 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1433
1469
|
usage = readTokenUsage(event.data) ?? usage;
|
|
1434
1470
|
costUsd = readTokenCostUsd(event.data) ?? costUsd;
|
|
1435
1471
|
}
|
|
1472
|
+
plan = readPlanFromEvent(event) ?? plan;
|
|
1436
1473
|
if (event.type === "result") toolInvocations = readToolInvocations(event.data) ?? toolInvocations;
|
|
1437
1474
|
if (event.type === "result" || event.type === "done") terminalReached = true;
|
|
1438
1475
|
if (event.type === "interaction") question = readQuestionEvent(event.data) ?? question;
|
|
@@ -1449,7 +1486,8 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1449
1486
|
runErrorCode,
|
|
1450
1487
|
toolInvocations,
|
|
1451
1488
|
approval,
|
|
1452
|
-
question
|
|
1489
|
+
question,
|
|
1490
|
+
plan
|
|
1453
1491
|
});
|
|
1454
1492
|
return {
|
|
1455
1493
|
success: outcome.success,
|
|
@@ -1460,6 +1498,7 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1460
1498
|
toolInvocations,
|
|
1461
1499
|
approval,
|
|
1462
1500
|
question,
|
|
1501
|
+
plan,
|
|
1463
1502
|
traceId,
|
|
1464
1503
|
durationMs: Date.now() - startTime,
|
|
1465
1504
|
usage,
|
|
@@ -1544,11 +1583,11 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1544
1583
|
* `error` + `done` so callers never see a thrown generator.
|
|
1545
1584
|
*/
|
|
1546
1585
|
async *streamPromptInner(message, options) {
|
|
1586
|
+
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
1587
|
await this.ensureRunning();
|
|
1548
|
-
const parts = encodePromptForWire(message);
|
|
1549
1588
|
const timeoutMs = typeof options?.timeoutMs === "number" && options.timeoutMs > 0 ? options.timeoutMs : void 0;
|
|
1550
1589
|
const timeoutSignal = timeoutMs ? AbortSignal.timeout(timeoutMs) : void 0;
|
|
1551
|
-
const streamSignal = timeoutSignal ? options?.signal ?
|
|
1590
|
+
const streamSignal = timeoutSignal ? options?.signal ? combineAbortSignals([options.signal, timeoutSignal]) : timeoutSignal : options?.signal;
|
|
1552
1591
|
const throwIfTimedOut = () => {
|
|
1553
1592
|
if (!timeoutSignal?.aborted || !timeoutMs) return;
|
|
1554
1593
|
throw new TimeoutError(timeoutMs, `Prompt stream timed out after ${timeoutMs}ms`, {
|
|
@@ -1563,26 +1602,47 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1563
1602
|
...data
|
|
1564
1603
|
});
|
|
1565
1604
|
};
|
|
1605
|
+
const explicitReplay = Boolean(options?.executionId && options.lastEventId);
|
|
1606
|
+
const requestEndpoint = explicitReplay ? `/agents/run/${encodeURIComponent(options?.executionId ?? "")}/events?lastEventId=${encodeURIComponent(options?.lastEventId ?? "")}&format=sse` : "/agents/run/stream";
|
|
1607
|
+
let lastEventId = options?.lastEventId;
|
|
1608
|
+
let executionId = options?.executionId;
|
|
1609
|
+
let sessionId = options?.sessionId;
|
|
1610
|
+
let receivedTerminal = false;
|
|
1611
|
+
const RECONNECT_BACKOFF_MS = [
|
|
1612
|
+
2e3,
|
|
1613
|
+
4e3,
|
|
1614
|
+
8e3,
|
|
1615
|
+
16e3,
|
|
1616
|
+
3e4,
|
|
1617
|
+
3e4
|
|
1618
|
+
];
|
|
1619
|
+
const MAX_RECONNECT_ATTEMPTS = RECONNECT_BACKOFF_MS.length;
|
|
1620
|
+
let lastReplayStatus;
|
|
1621
|
+
let lastExecutionStatus;
|
|
1622
|
+
let firstSseEventSeen = false;
|
|
1566
1623
|
let response;
|
|
1567
1624
|
try {
|
|
1568
1625
|
logStreamCheckpoint("runtime_post_start", {
|
|
1569
|
-
endpoint:
|
|
1626
|
+
endpoint: requestEndpoint,
|
|
1570
1627
|
sessionId: options?.sessionId,
|
|
1571
1628
|
executionId: options?.executionId,
|
|
1572
1629
|
hasLastEventId: Boolean(options?.lastEventId),
|
|
1573
|
-
detach: options?.detach === true
|
|
1630
|
+
detach: options?.detach === true,
|
|
1631
|
+
replayOnly: explicitReplay
|
|
1574
1632
|
});
|
|
1575
|
-
|
|
1633
|
+
const request = explicitReplay ? {
|
|
1634
|
+
method: "GET",
|
|
1635
|
+
signal: streamSignal
|
|
1636
|
+
} : {
|
|
1576
1637
|
method: "POST",
|
|
1577
1638
|
signal: streamSignal,
|
|
1578
1639
|
headers: {
|
|
1579
1640
|
...options?.executionId ? { "X-Execution-ID": options.executionId } : {},
|
|
1580
|
-
...options?.lastEventId ? { "Last-Event-ID": options.lastEventId } : {},
|
|
1581
1641
|
...options?.detach ? { "x-agent-detach": "true" } : {}
|
|
1582
1642
|
},
|
|
1583
1643
|
body: JSON.stringify({
|
|
1584
1644
|
identifier: "default",
|
|
1585
|
-
parts,
|
|
1645
|
+
parts: encodePromptForWire(message),
|
|
1586
1646
|
sessionId: options?.sessionId,
|
|
1587
1647
|
turnId: options?.turnId,
|
|
1588
1648
|
metadata: options?.context,
|
|
@@ -1591,7 +1651,8 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1591
1651
|
...options?.detach ? { detach: true } : {},
|
|
1592
1652
|
backend: normalizeRuntimeBackendConfig(options?.backend ?? this.defaultRuntimeBackend, { model: options?.model })
|
|
1593
1653
|
})
|
|
1594
|
-
}
|
|
1654
|
+
};
|
|
1655
|
+
response = await this.runtimeFetch(requestEndpoint, request);
|
|
1595
1656
|
logStreamCheckpoint("runtime_response_headers", {
|
|
1596
1657
|
status: response.status,
|
|
1597
1658
|
ok: response.ok
|
|
@@ -1601,27 +1662,20 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1601
1662
|
throwIfTimedOut();
|
|
1602
1663
|
throw err;
|
|
1603
1664
|
}
|
|
1604
|
-
if (!response.ok) {
|
|
1665
|
+
if (!response.ok && !explicitReplay) {
|
|
1605
1666
|
const body = await response.text();
|
|
1606
1667
|
throw parseErrorResponse(response.status, body, void 0, response.headers);
|
|
1607
1668
|
}
|
|
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)) {
|
|
1669
|
+
if (!response.ok) {
|
|
1670
|
+
lastReplayStatus = response.status;
|
|
1671
|
+
await response.text();
|
|
1672
|
+
logStreamCheckpoint("replay_response_status", {
|
|
1673
|
+
attempt: 0,
|
|
1674
|
+
status: response.status,
|
|
1675
|
+
ok: false
|
|
1676
|
+
});
|
|
1677
|
+
} else for await (const event of this.parseSSEStream(response, streamSignal)) {
|
|
1678
|
+
if (event.type === "history.replay.start" || event.type === "history.replay.end") continue;
|
|
1625
1679
|
if (!firstSseEventSeen) {
|
|
1626
1680
|
firstSseEventSeen = true;
|
|
1627
1681
|
logStreamCheckpoint("first_sse_event", {
|
|
@@ -1922,6 +1976,7 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1922
1976
|
let costUsd;
|
|
1923
1977
|
let toolInvocations;
|
|
1924
1978
|
let question;
|
|
1979
|
+
let plan;
|
|
1925
1980
|
let terminalReached = false;
|
|
1926
1981
|
try {
|
|
1927
1982
|
for await (const event of this.streamPrompt(prompt, {
|
|
@@ -1938,6 +1993,7 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1938
1993
|
}
|
|
1939
1994
|
if (event.type === "done" && costUsd === void 0) costUsd = readTokenCostUsd(event.data);
|
|
1940
1995
|
if (event.type === "result" || event.type === "done") terminalReached = true;
|
|
1996
|
+
plan = readPlanFromEvent(event) ?? plan;
|
|
1941
1997
|
if (event.type === "interaction") question = readQuestionEvent(event.data) ?? question;
|
|
1942
1998
|
if (event.type === "trace.id") traceId = event.data.traceId;
|
|
1943
1999
|
if (event.type === "error") {
|
|
@@ -1952,7 +2008,8 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1952
2008
|
runErrorCode,
|
|
1953
2009
|
toolInvocations,
|
|
1954
2010
|
approval,
|
|
1955
|
-
question
|
|
2011
|
+
question,
|
|
2012
|
+
plan
|
|
1956
2013
|
});
|
|
1957
2014
|
return {
|
|
1958
2015
|
success: outcome.success,
|
|
@@ -1963,6 +2020,7 @@ var SandboxInstance = class SandboxInstance {
|
|
|
1963
2020
|
toolInvocations,
|
|
1964
2021
|
approval,
|
|
1965
2022
|
question,
|
|
2023
|
+
plan,
|
|
1966
2024
|
traceId,
|
|
1967
2025
|
durationMs: Date.now() - startTime,
|
|
1968
2026
|
usage,
|
|
@@ -2099,11 +2157,19 @@ var SandboxInstance = class SandboxInstance {
|
|
|
2099
2157
|
async resourceUsage() {
|
|
2100
2158
|
await this.ensureRunning();
|
|
2101
2159
|
const response = await this.runtimeFetch("/health/detailed", { method: "GET" });
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2160
|
+
const body = await response.text();
|
|
2161
|
+
let data;
|
|
2162
|
+
try {
|
|
2163
|
+
data = JSON.parse(body);
|
|
2164
|
+
} catch (error) {
|
|
2165
|
+
if (response.ok) throw new NetworkError("Detailed health returned a non-JSON response", error instanceof Error ? error : void 0, {
|
|
2166
|
+
endpoint: "/health/detailed",
|
|
2167
|
+
origin: "runtime"
|
|
2168
|
+
});
|
|
2105
2169
|
}
|
|
2106
|
-
|
|
2170
|
+
if (data?.resources) return data.resources;
|
|
2171
|
+
if (!response.ok) throw parseErrorResponse(response.status, body, void 0, response.headers);
|
|
2172
|
+
return null;
|
|
2107
2173
|
}
|
|
2108
2174
|
/**
|
|
2109
2175
|
* Git capability object for repository operations.
|
|
@@ -2322,6 +2388,7 @@ var SandboxInstance = class SandboxInstance {
|
|
|
2322
2388
|
writeMany: (files, options) => this.writeMany(files, options),
|
|
2323
2389
|
search: (query, options) => this.search(query, options),
|
|
2324
2390
|
upload: (localPath, remotePath, options) => this.fsUpload(localPath, remotePath, options),
|
|
2391
|
+
uploadData: (remotePath, data, options) => this.fsUploadData(remotePath, data, options),
|
|
2325
2392
|
download: (remotePath, localPath, options) => this.fsDownload(remotePath, localPath, options),
|
|
2326
2393
|
uploadDir: (localDir, remoteDir) => this.fsUploadDir(localDir, remoteDir),
|
|
2327
2394
|
downloadDir: (remoteDir, localDir) => this.fsDownloadDir(remoteDir, localDir),
|
|
@@ -2369,6 +2436,14 @@ var SandboxInstance = class SandboxInstance {
|
|
|
2369
2436
|
percentage: 100
|
|
2370
2437
|
});
|
|
2371
2438
|
}
|
|
2439
|
+
/** See {@link FileSystem.uploadData}. Delegates to the shared, browser-safe
|
|
2440
|
+
* chunked-upload flow in `lib/chunked-upload.ts`, wired to this sandbox's
|
|
2441
|
+
* runtime endpoint (works over the gateway proxy or a direct connection —
|
|
2442
|
+
* both are transparent through `runtimeFetch`). */
|
|
2443
|
+
async fsUploadData(remotePath, data, options) {
|
|
2444
|
+
await this.ensureRunning();
|
|
2445
|
+
return uploadChunked({ fetch: (path, init) => this.runtimeFetch(path, init) }, remotePath, data, options);
|
|
2446
|
+
}
|
|
2372
2447
|
async fsDownload(remotePath, localPath, options) {
|
|
2373
2448
|
await this.ensureRunning();
|
|
2374
2449
|
const fs = await import("node:fs/promises");
|
|
@@ -2930,32 +3005,11 @@ var SandboxInstance = class SandboxInstance {
|
|
|
2930
3005
|
get process() {
|
|
2931
3006
|
return {
|
|
2932
3007
|
spawn: (command, options) => this.processSpawn(command, options),
|
|
2933
|
-
spawnExact: (executable, args, options) => this.processSpawnExact(executable, args, options),
|
|
2934
3008
|
runCode: (code, options) => this.processRunCode(code, options),
|
|
2935
3009
|
list: () => this.processList(),
|
|
2936
3010
|
get: (pid) => this.processGet(pid)
|
|
2937
3011
|
};
|
|
2938
3012
|
}
|
|
2939
|
-
async processSpawnExact(executable, args, options) {
|
|
2940
|
-
await this.ensureRunning();
|
|
2941
|
-
const response = await this.runtimeFetch("/process/spawn-exact", {
|
|
2942
|
-
method: "POST",
|
|
2943
|
-
body: JSON.stringify({
|
|
2944
|
-
executable,
|
|
2945
|
-
args,
|
|
2946
|
-
cwd: options?.cwd,
|
|
2947
|
-
env: options?.env ?? {},
|
|
2948
|
-
stdin: options?.stdin,
|
|
2949
|
-
timeoutMs: options?.timeoutMs
|
|
2950
|
-
})
|
|
2951
|
-
});
|
|
2952
|
-
if (!response.ok) {
|
|
2953
|
-
const body = await response.text();
|
|
2954
|
-
throw parseErrorResponse(response.status, body, void 0, response.headers);
|
|
2955
|
-
}
|
|
2956
|
-
const data = await response.json();
|
|
2957
|
-
return this.createProcessHandle(data.data.pid, executable);
|
|
2958
|
-
}
|
|
2959
3013
|
async processSpawn(command, options) {
|
|
2960
3014
|
await this.ensureRunning();
|
|
2961
3015
|
const response = await this.runtimeFetch("/process/spawn", {
|
|
@@ -3856,7 +3910,7 @@ var SandboxInstance = class SandboxInstance {
|
|
|
3856
3910
|
async resume(options = {}) {
|
|
3857
3911
|
if (options.timeoutMs !== void 0 && (!Number.isFinite(options.timeoutMs) || options.timeoutMs <= 0)) throw new ValidationError("resume timeoutMs must be a positive number");
|
|
3858
3912
|
const timeoutSignal = options.timeoutMs ? AbortSignal.timeout(options.timeoutMs) : void 0;
|
|
3859
|
-
const signal = timeoutSignal ? options.signal ?
|
|
3913
|
+
const signal = timeoutSignal ? options.signal ? combineAbortSignals([options.signal, timeoutSignal]) : timeoutSignal : options.signal;
|
|
3860
3914
|
const response = await this.client.fetch(`/v1/sandboxes/${this.id}/resume`, {
|
|
3861
3915
|
method: "POST",
|
|
3862
3916
|
...signal ? { signal } : {}
|
|
@@ -4329,24 +4383,8 @@ var SandboxInstance = class SandboxInstance {
|
|
|
4329
4383
|
* abort stamps `interrupted: true` explicitly.
|
|
4330
4384
|
*/
|
|
4331
4385
|
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
|
-
}));
|
|
4386
|
+
const { sessionId, ...options } = opts;
|
|
4387
|
+
return this.runtime.messages(sessionId, options);
|
|
4350
4388
|
}
|
|
4351
4389
|
/** @internal — invoked by SandboxSession.sendMessage(). */
|
|
4352
4390
|
async _sessionSendMessage(id, request, options = {}) {
|
|
@@ -4455,11 +4493,13 @@ var SandboxInstance = class SandboxInstance {
|
|
|
4455
4493
|
}
|
|
4456
4494
|
if (info) {
|
|
4457
4495
|
const result = await this._sessionResult(opts.sessionId);
|
|
4496
|
+
const settled = settleTurnDrive({ ...result });
|
|
4497
|
+
if (settled.state === "awaiting_plan_decision") return settled;
|
|
4458
4498
|
if (!result.success) return {
|
|
4459
4499
|
state: "failed",
|
|
4460
4500
|
error: `session ${opts.sessionId} ${info.status}: ${result.error ?? "no error detail"}`
|
|
4461
4501
|
};
|
|
4462
|
-
return
|
|
4502
|
+
return settled;
|
|
4463
4503
|
}
|
|
4464
4504
|
await this.dispatchPrompt(message, {
|
|
4465
4505
|
...opts,
|
|
@@ -4468,15 +4508,10 @@ var SandboxInstance = class SandboxInstance {
|
|
|
4468
4508
|
return { state: "running" };
|
|
4469
4509
|
}
|
|
4470
4510
|
/**
|
|
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.
|
|
4511
|
+
* Mint a scoped, time-bounded JWT for browser access (Issue #913 Gap 1).
|
|
4512
|
+
* Authority is the caller's `TANGLE_API_KEY` (sk-tan-*); signing secrets
|
|
4513
|
+
* stay server-side. Session/project scopes work with SessionGatewayClient,
|
|
4514
|
+
* while session-runtime/read-only scopes work with SandboxRuntimeClient.
|
|
4480
4515
|
*/
|
|
4481
4516
|
async mintScopedToken(opts) {
|
|
4482
4517
|
if ((opts.scope === "session" || opts.scope === "session-runtime") && !opts.sessionId) throw new ValidationError(`${opts.scope} scope requires a sessionId`);
|
|
@@ -4515,17 +4550,22 @@ var SandboxInstance = class SandboxInstance {
|
|
|
4515
4550
|
return normalizeSessionInfo(await response.json());
|
|
4516
4551
|
}
|
|
4517
4552
|
/** @internal — invoked by SandboxSession.events(). */
|
|
4518
|
-
async *_sessionEvents(id, opts) {
|
|
4553
|
+
async *_sessionEvents(id, opts, stopOnTurnTerminal = false) {
|
|
4519
4554
|
await this.ensureRunning();
|
|
4520
4555
|
const search = new URLSearchParams();
|
|
4521
4556
|
search.set("sessionId", id);
|
|
4522
4557
|
if (opts?.since) search.set("since", opts.since);
|
|
4558
|
+
if (opts?.executionId) search.set("executionId", opts.executionId);
|
|
4523
4559
|
const response = await this.runtimeFetch(`/agents/events?${search.toString()}`, { signal: opts?.signal });
|
|
4524
4560
|
if (!response.ok) {
|
|
4525
4561
|
const body = await response.text();
|
|
4526
4562
|
throw parseErrorResponse(response.status, body, void 0, response.headers);
|
|
4527
4563
|
}
|
|
4528
|
-
|
|
4564
|
+
for await (const event of this.parseSSEStream(response, opts?.signal)) {
|
|
4565
|
+
yield event;
|
|
4566
|
+
const turnTerminal = event.type === "result" || event.type === "done" || event.type === "error";
|
|
4567
|
+
if ((stopOnTurnTerminal || opts?.executionId !== void 0) && turnTerminal) return;
|
|
4568
|
+
}
|
|
4529
4569
|
}
|
|
4530
4570
|
/** @internal — invoked by SandboxSession.result(). */
|
|
4531
4571
|
async _sessionResult(id) {
|
|
@@ -4538,14 +4578,16 @@ var SandboxInstance = class SandboxInstance {
|
|
|
4538
4578
|
let costUsd;
|
|
4539
4579
|
let toolInvocations;
|
|
4540
4580
|
let question;
|
|
4581
|
+
let plan;
|
|
4541
4582
|
let terminalReached = false;
|
|
4542
|
-
for await (const event of this._sessionEvents(id)) {
|
|
4583
|
+
for await (const event of this._sessionEvents(id, void 0, true)) {
|
|
4543
4584
|
response = applySandboxEventText(response, event);
|
|
4544
4585
|
if (event.type === "result" || event.type === "done") {
|
|
4545
4586
|
const data = event.data;
|
|
4546
4587
|
usage = readTokenUsage(data) ?? usage;
|
|
4547
4588
|
costUsd = readTokenCostUsd(data) ?? costUsd;
|
|
4548
4589
|
}
|
|
4590
|
+
plan = readPlanFromEvent(event) ?? plan;
|
|
4549
4591
|
if (event.type === "result") {
|
|
4550
4592
|
const data = event.data;
|
|
4551
4593
|
toolInvocations = readToolInvocations(data) ?? toolInvocations;
|
|
@@ -4569,7 +4611,8 @@ var SandboxInstance = class SandboxInstance {
|
|
|
4569
4611
|
runErrorCode,
|
|
4570
4612
|
toolInvocations,
|
|
4571
4613
|
approval,
|
|
4572
|
-
question
|
|
4614
|
+
question,
|
|
4615
|
+
plan
|
|
4573
4616
|
});
|
|
4574
4617
|
return {
|
|
4575
4618
|
success: outcome.success,
|
|
@@ -4580,6 +4623,7 @@ var SandboxInstance = class SandboxInstance {
|
|
|
4580
4623
|
toolInvocations,
|
|
4581
4624
|
approval,
|
|
4582
4625
|
question,
|
|
4626
|
+
plan,
|
|
4583
4627
|
traceId,
|
|
4584
4628
|
durationMs: Date.now() - startTime,
|
|
4585
4629
|
usage,
|
|
@@ -4711,8 +4755,48 @@ var SandboxInstance = class SandboxInstance {
|
|
|
4711
4755
|
throw parseErrorResponse(response.status, body, void 0, response.headers);
|
|
4712
4756
|
}
|
|
4713
4757
|
}
|
|
4758
|
+
/** @internal — invoked by SandboxSession.plan(). */
|
|
4759
|
+
async _currentPlan(sessionId) {
|
|
4760
|
+
const path = `/v1/sandboxes/${encodeURIComponent(this.id)}/agent-sessions/${encodeURIComponent(sessionId)}/plans/current`;
|
|
4761
|
+
const response = await this.client.fetch(path);
|
|
4762
|
+
if (response.status === 404) return null;
|
|
4763
|
+
if (!response.ok) {
|
|
4764
|
+
const body = await response.text();
|
|
4765
|
+
throw parseErrorResponse(response.status, body, void 0, response.headers);
|
|
4766
|
+
}
|
|
4767
|
+
const decoded = await response.json();
|
|
4768
|
+
if (!decoded.plan) throw new ServerError("Durable plan response is missing plan", 502, {
|
|
4769
|
+
endpoint: path,
|
|
4770
|
+
origin: "control-plane"
|
|
4771
|
+
});
|
|
4772
|
+
return decoded.plan;
|
|
4773
|
+
}
|
|
4774
|
+
/** @internal — invoked by SandboxSession.approvePlan()/rejectPlan(). */
|
|
4775
|
+
async _decidePlan(sessionId, planId, decision) {
|
|
4776
|
+
const path = `/v1/sandboxes/${encodeURIComponent(this.id)}/agent-sessions/${encodeURIComponent(sessionId)}/plans/${encodeURIComponent(planId)}/decision`;
|
|
4777
|
+
const response = await this.client.fetch(path, {
|
|
4778
|
+
method: "POST",
|
|
4779
|
+
body: JSON.stringify(decision)
|
|
4780
|
+
});
|
|
4781
|
+
if (!response.ok) {
|
|
4782
|
+
const body = await response.text();
|
|
4783
|
+
throw parseErrorResponse(response.status, body, void 0, response.headers);
|
|
4784
|
+
}
|
|
4785
|
+
const decoded = await response.json();
|
|
4786
|
+
if (!decoded.plan) throw new ServerError("Plan decision response is missing plan", 502, {
|
|
4787
|
+
endpoint: path,
|
|
4788
|
+
origin: "control-plane"
|
|
4789
|
+
});
|
|
4790
|
+
return decoded.plan;
|
|
4791
|
+
}
|
|
4714
4792
|
};
|
|
4715
4793
|
function settleTurnDrive(result) {
|
|
4794
|
+
const plan = readAwaitingPlan(result);
|
|
4795
|
+
if (plan) return {
|
|
4796
|
+
state: "awaiting_plan_decision",
|
|
4797
|
+
plan,
|
|
4798
|
+
result
|
|
4799
|
+
};
|
|
4716
4800
|
const text = typeof result.text === "string" ? result.text : typeof result.response === "string" ? result.response : void 0;
|
|
4717
4801
|
const status = typeof result.status === "string" ? result.status : deriveOutcome({
|
|
4718
4802
|
terminalReached: true,
|
|
@@ -4795,4 +4879,4 @@ function quoteForShell(value) {
|
|
|
4795
4879
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
4796
4880
|
}
|
|
4797
4881
|
//#endregion
|
|
4798
|
-
export { InteractiveSessionHandle as a, getSandboxEventText as c, exportTraceBundle as d, otelTraceIdForTangleTrace as f,
|
|
4882
|
+
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 };
|
package/dist/tangle/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as TANGLE_JOBS_CONTRACT, c as AgentSandboxBlueprintAbi, d as SandboxCreateParamTypes, f as SandboxCreateResponseParamTypes, i as TANGLE_CHAIN_ID, l as ITangleJobsAbi, n as JOB_SANDBOX_CREATE, o as TANGLE_MAINNET_RPC, p as SandboxIdParamTypes, r as JOB_SANDBOX_DELETE, s as TangleSandboxClientConfig, t as TangleSandboxClient, u as JsonResponseParamTypes } from "../index-
|
|
1
|
+
import { a as TANGLE_JOBS_CONTRACT, c as AgentSandboxBlueprintAbi, d as SandboxCreateParamTypes, f as SandboxCreateResponseParamTypes, i as TANGLE_CHAIN_ID, l as ITangleJobsAbi, n as JOB_SANDBOX_CREATE, o as TANGLE_MAINNET_RPC, p as SandboxIdParamTypes, r as JOB_SANDBOX_DELETE, s as TangleSandboxClientConfig, t as TangleSandboxClient, u as JsonResponseParamTypes } from "../index-V2vjBfHU.js";
|
|
2
2
|
export { AgentSandboxBlueprintAbi, ITangleJobsAbi, JOB_SANDBOX_CREATE, JOB_SANDBOX_DELETE, JsonResponseParamTypes, SandboxCreateParamTypes, SandboxCreateResponseParamTypes, SandboxIdParamTypes, TANGLE_CHAIN_ID, TANGLE_JOBS_CONTRACT, TANGLE_MAINNET_RPC, TangleSandboxClient, TangleSandboxClientConfig };
|
package/dist/tangle/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as TANGLE_JOBS_CONTRACT, c as ITangleJobsAbi, d as SandboxCreateResponseParamTypes, f as SandboxIdParamTypes, i as TANGLE_CHAIN_ID, l as JsonResponseParamTypes, n as JOB_SANDBOX_CREATE, o as TANGLE_MAINNET_RPC, r as JOB_SANDBOX_DELETE, s as AgentSandboxBlueprintAbi, t as TangleSandboxClient, u as SandboxCreateParamTypes } from "../tangle-
|
|
1
|
+
import { a as TANGLE_JOBS_CONTRACT, c as ITangleJobsAbi, d as SandboxCreateResponseParamTypes, f as SandboxIdParamTypes, i as TANGLE_CHAIN_ID, l as JsonResponseParamTypes, n as JOB_SANDBOX_CREATE, o as TANGLE_MAINNET_RPC, r as JOB_SANDBOX_DELETE, s as AgentSandboxBlueprintAbi, t as TangleSandboxClient, u as SandboxCreateParamTypes } from "../tangle-051CtV2b.js";
|
|
2
2
|
export { AgentSandboxBlueprintAbi, ITangleJobsAbi, JOB_SANDBOX_CREATE, JOB_SANDBOX_DELETE, JsonResponseParamTypes, SandboxCreateParamTypes, SandboxCreateResponseParamTypes, SandboxIdParamTypes, TANGLE_CHAIN_ID, TANGLE_JOBS_CONTRACT, TANGLE_MAINNET_RPC, TangleSandboxClient };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { p as parseErrorResponse } from "./errors-
|
|
2
|
-
import { t as SandboxInstance } from "./sandbox-
|
|
1
|
+
import { p as parseErrorResponse } from "./errors-Cbs78OlF.js";
|
|
2
|
+
import { t as SandboxInstance } from "./sandbox-r2Lxbw8z.js";
|
|
3
3
|
//#region src/tangle/abi.ts
|
|
4
4
|
/**
|
|
5
5
|
* Tangle Contract ABI Definitions
|