@agent-compose/sdk 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/agent-context.d.ts +1 -1
- package/dist/agent/agent-loop.d.ts +8 -0
- package/dist/agent/run-agent.d.ts +4 -0
- package/dist/client.d.ts +77 -15
- package/dist/display.d.ts +16 -0
- package/dist/index.d.ts +6 -6
- package/dist/index.js +522 -123
- package/dist/runtimes/_cli-agent.d.ts +34 -7
- package/dist/runtimes/claude-code.d.ts +10 -8
- package/dist/runtimes/codex.buildcommand.test.d.ts +9 -0
- package/dist/runtimes/codex.d.ts +4 -1
- package/dist/runtimes/openai-desktop.js +507 -122
- package/dist/sandbox/sizes.d.ts +120 -30
- package/dist/sandbox.d.ts +1 -1
- package/dist/types/api-conversations.d.ts +198 -0
- package/dist/types/api-factory.d.ts +84 -7
- package/dist/types/api-runs.d.ts +48 -2
- package/dist/types/protocol.d.ts +8 -0
- package/dist/types/workflow-metadata.d.ts +14 -5
- package/dist/utils/bundler.d.ts +56 -0
- package/dist/workflow-steps/workflow.d.ts +7 -0
- package/dist/workflows/invoke-child.d.ts +18 -0
- package/dist/workflows/invoke-child.test.d.ts +9 -0
- package/package.json +2 -2
- package/src/agent/agent-context.ts +28 -17
- package/src/agent/agent-loop.ts +9 -0
- package/src/agent/run-agent.ts +5 -0
- package/src/client.ts +201 -30
- package/src/display.ts +61 -15
- package/src/index.ts +22 -9
- package/src/runtimes/_cli-agent.ts +302 -63
- package/src/runtimes/claude-code.ts +25 -15
- package/src/runtimes/codex.ts +19 -5
- package/src/sandbox/providers/e2b.ts +8 -4
- package/src/sandbox/sizes.ts +127 -44
- package/src/sandbox.ts +8 -0
- package/src/types/api-conversations.ts +180 -0
- package/src/types/api-factory.ts +89 -7
- package/src/types/api-runs.ts +50 -2
- package/src/types/protocol.ts +8 -0
- package/src/types/workflow-metadata.ts +15 -5
- package/src/utils/bundler.ts +213 -3
- package/src/workflow-steps/workflow.ts +7 -0
- package/src/workflows/invoke-child.ts +47 -11
package/dist/index.js
CHANGED
|
@@ -959,6 +959,8 @@ function extractMetadata(source) {
|
|
|
959
959
|
out.invokePolicy = freezeMetadataValue(source.invokePolicy);
|
|
960
960
|
if (source.environmentBuild !== undefined)
|
|
961
961
|
out.environmentBuild = source.environmentBuild;
|
|
962
|
+
if (source.factoryDrive !== undefined)
|
|
963
|
+
out.factoryDrive = source.factoryDrive;
|
|
962
964
|
return Object.freeze(out);
|
|
963
965
|
}
|
|
964
966
|
function freezeMetadataValue(value) {
|
|
@@ -1446,14 +1448,47 @@ class AgentComposeClient {
|
|
|
1446
1448
|
...opts?.placeholders !== undefined ? { placeholders: opts.placeholders } : {},
|
|
1447
1449
|
...opts?.size !== undefined ? { size: opts.size } : {},
|
|
1448
1450
|
...parentRunId ? { parentRunId } : {},
|
|
1449
|
-
...opts?.agentId ? { agentId: opts.agentId } : {}
|
|
1451
|
+
...opts?.agentId ? { agentId: opts.agentId } : {},
|
|
1452
|
+
...opts?.funding !== undefined ? { funding: opts.funding } : {},
|
|
1453
|
+
...opts?.fundingSecret !== undefined ? { fundingSecret: opts.fundingSecret } : {}
|
|
1454
|
+
}
|
|
1455
|
+
});
|
|
1456
|
+
}
|
|
1457
|
+
invokeInline(workflow, input, opts) {
|
|
1458
|
+
const parentRunId = opts?.parentRunId === undefined ? detectAmbientParentRunId() : opts.parentRunId;
|
|
1459
|
+
const factorySlug = opts?.factorySlug ?? DEFAULT_FACTORY;
|
|
1460
|
+
const headers = {};
|
|
1461
|
+
if (opts?.idempotencyKey)
|
|
1462
|
+
headers["Idempotency-Key"] = opts.idempotencyKey;
|
|
1463
|
+
return this.fetch(`/api/v1/factories/${encodeURIComponent(factorySlug)}/invoke`, {
|
|
1464
|
+
method: "POST",
|
|
1465
|
+
...opts?.idempotencyKey ? { headers } : {},
|
|
1466
|
+
body: {
|
|
1467
|
+
workflow,
|
|
1468
|
+
input,
|
|
1469
|
+
...opts?.title !== undefined ? { title: opts.title } : {},
|
|
1470
|
+
...opts?.snapshots !== undefined ? { snapshots: opts.snapshots } : {},
|
|
1471
|
+
...opts?.networkPolicy !== undefined ? { networkPolicy: opts.networkPolicy } : {},
|
|
1472
|
+
...opts?.placeholders !== undefined ? { placeholders: opts.placeholders } : {},
|
|
1473
|
+
...opts?.size !== undefined ? { size: opts.size } : {},
|
|
1474
|
+
...parentRunId ? { parentRunId } : {},
|
|
1475
|
+
...opts?.agentId ? { agentId: opts.agentId } : {},
|
|
1476
|
+
...opts?.funding !== undefined ? { funding: opts.funding } : {},
|
|
1477
|
+
...opts?.fundingSecret !== undefined ? { fundingSecret: opts.fundingSecret } : {}
|
|
1450
1478
|
}
|
|
1451
1479
|
});
|
|
1452
1480
|
}
|
|
1453
1481
|
async invokeAndWait(name, input, opts) {
|
|
1482
|
+
const { id: runId } = await this.invoke(name, input, opts);
|
|
1483
|
+
return this.waitForRun(runId, opts);
|
|
1484
|
+
}
|
|
1485
|
+
async invokeInlineAndWait(workflow, input, opts) {
|
|
1486
|
+
const { id: runId } = await this.invokeInline(workflow, input, opts);
|
|
1487
|
+
return this.waitForRun(runId, opts);
|
|
1488
|
+
}
|
|
1489
|
+
async waitForRun(runId, opts) {
|
|
1454
1490
|
const timeoutMs = opts?.timeoutMs ?? 30 * 60 * 1000;
|
|
1455
1491
|
const pollMs = opts?.pollIntervalMs ?? 1000;
|
|
1456
|
-
const { id: runId } = await this.invoke(name, input, opts);
|
|
1457
1492
|
const deadline = Date.now() + timeoutMs;
|
|
1458
1493
|
while (Date.now() < deadline) {
|
|
1459
1494
|
const status = await this.getStatus(runId);
|
|
@@ -1462,7 +1497,7 @@ class AgentComposeClient {
|
|
|
1462
1497
|
}
|
|
1463
1498
|
await new Promise((r) => setTimeout(r, pollMs));
|
|
1464
1499
|
}
|
|
1465
|
-
throw new AgentComposeError(504, `
|
|
1500
|
+
throw new AgentComposeError(504, `waitForRun: run ${runId} did not settle within ${timeoutMs}ms`);
|
|
1466
1501
|
}
|
|
1467
1502
|
async listSnapshots(opts) {
|
|
1468
1503
|
const body = await this.listSnapshotsPage(opts);
|
|
@@ -1598,6 +1633,11 @@ class AgentComposeClient {
|
|
|
1598
1633
|
preview: a.preview
|
|
1599
1634
|
}));
|
|
1600
1635
|
}
|
|
1636
|
+
async getRunArtifactBytes(runId, path) {
|
|
1637
|
+
const q2 = new URLSearchParams({ path });
|
|
1638
|
+
const buf = await this.fetch(`/api/v1/workflows/${encodeURIComponent(runId)}/artifacts/content?${q2}`, { responseType: "arrayBuffer" });
|
|
1639
|
+
return new Uint8Array(buf);
|
|
1640
|
+
}
|
|
1601
1641
|
async putFactoryFile(path, content, opts) {
|
|
1602
1642
|
const factorySlug = opts?.factorySlug ?? (typeof process !== "undefined" ? process.env?.AGENT_COMPOSE_FACTORY : undefined) ?? DEFAULT_FACTORY;
|
|
1603
1643
|
const runToken = typeof process !== "undefined" ? process.env?.AGENT_COMPOSE_RUN_TOKEN : undefined;
|
|
@@ -1621,12 +1661,28 @@ class AgentComposeClient {
|
|
|
1621
1661
|
const q2 = new URLSearchParams({ path });
|
|
1622
1662
|
if (opts?.revision !== undefined)
|
|
1623
1663
|
q2.set("revision", String(opts.revision));
|
|
1664
|
+
if (opts?.branch !== undefined)
|
|
1665
|
+
q2.set("branch", opts.branch);
|
|
1624
1666
|
const buf = await this.fetch(`/api/v1/factories/${encodeURIComponent(factorySlug)}/files/content?${q2}`, { responseType: "arrayBuffer" });
|
|
1625
1667
|
return new Uint8Array(buf);
|
|
1626
1668
|
}
|
|
1627
1669
|
async getFactoryFile(path, opts) {
|
|
1628
1670
|
return new TextDecoder().decode(await this.getFactoryFileBytes(path, opts));
|
|
1629
1671
|
}
|
|
1672
|
+
async createDriveMountSession(opts) {
|
|
1673
|
+
const factorySlug = opts?.factorySlug ?? (typeof process !== "undefined" ? process.env?.AGENT_COMPOSE_FACTORY : undefined) ?? DEFAULT_FACTORY;
|
|
1674
|
+
return this.fetch(`/api/v1/factories/${encodeURIComponent(factorySlug)}/files/mount-sessions`, {
|
|
1675
|
+
method: "POST",
|
|
1676
|
+
body: {
|
|
1677
|
+
...opts?.conversationId ? { conversationId: opts.conversationId } : {},
|
|
1678
|
+
...opts?.host ? { host: opts.host } : {}
|
|
1679
|
+
}
|
|
1680
|
+
});
|
|
1681
|
+
}
|
|
1682
|
+
async releaseDriveMountSession(conversationId, opts) {
|
|
1683
|
+
const factorySlug = opts?.factorySlug ?? (typeof process !== "undefined" ? process.env?.AGENT_COMPOSE_FACTORY : undefined) ?? DEFAULT_FACTORY;
|
|
1684
|
+
return this.fetch(`/api/v1/factories/${encodeURIComponent(factorySlug)}/files/mount-sessions/${encodeURIComponent(conversationId)}/release`, { method: "POST", body: {} });
|
|
1685
|
+
}
|
|
1630
1686
|
listConversations(opts) {
|
|
1631
1687
|
const q2 = new URLSearchParams;
|
|
1632
1688
|
if (opts?.cursor)
|
|
@@ -1639,8 +1695,9 @@ class AgentComposeClient {
|
|
|
1639
1695
|
q2.set("cursor", opts.cursor);
|
|
1640
1696
|
return this.fetch(`/api/v1/sessions?${q2}`);
|
|
1641
1697
|
}
|
|
1642
|
-
getConversation(id) {
|
|
1643
|
-
|
|
1698
|
+
getConversation(id, opts) {
|
|
1699
|
+
const q2 = opts?.limit !== undefined ? `?limit=${opts.limit}` : "";
|
|
1700
|
+
return this.fetch(`/api/v1/conversations/${encodeURIComponent(id)}${q2}`);
|
|
1644
1701
|
}
|
|
1645
1702
|
createCloudSession(input) {
|
|
1646
1703
|
return this.fetch(`/api/v1/sessions/cloud`, {
|
|
@@ -1658,7 +1715,10 @@ class AgentComposeClient {
|
|
|
1658
1715
|
...input.templateId ? { templateId: input.templateId } : {},
|
|
1659
1716
|
...input.sandboxSize ? { sandboxSize: input.sandboxSize } : {},
|
|
1660
1717
|
...input.networkPolicy !== undefined ? { networkPolicy: input.networkPolicy } : {},
|
|
1661
|
-
...input.connectorProfileId !== undefined ? { connectorProfileId: input.connectorProfileId } : {}
|
|
1718
|
+
...input.connectorProfileId !== undefined ? { connectorProfileId: input.connectorProfileId } : {},
|
|
1719
|
+
...input.setupCommand !== undefined ? { setupCommand: input.setupCommand } : {},
|
|
1720
|
+
...input.handoffOrigin !== undefined ? { handoffOrigin: input.handoffOrigin } : {},
|
|
1721
|
+
...input.seedFiles !== undefined ? { seedFiles: input.seedFiles } : {}
|
|
1662
1722
|
}
|
|
1663
1723
|
});
|
|
1664
1724
|
}
|
|
@@ -1696,6 +1756,13 @@ class AgentComposeClient {
|
|
|
1696
1756
|
const body = await this.fetch(`/api/v1/conversations/${encodeURIComponent(conversationId)}/preview/${port}`, { method: "DELETE" });
|
|
1697
1757
|
return body.closed;
|
|
1698
1758
|
}
|
|
1759
|
+
holdBackgroundWork(conversationId, input) {
|
|
1760
|
+
return this.fetch(`/api/v1/conversations/${encodeURIComponent(conversationId)}/background-work`, { method: "POST", body: { ...input?.minutes !== undefined ? { minutes: input.minutes } : {} } });
|
|
1761
|
+
}
|
|
1762
|
+
async releaseBackgroundWork(conversationId) {
|
|
1763
|
+
const body = await this.fetch(`/api/v1/conversations/${encodeURIComponent(conversationId)}/background-work`, { method: "DELETE" });
|
|
1764
|
+
return body.released;
|
|
1765
|
+
}
|
|
1699
1766
|
forkSession(conversationId, input) {
|
|
1700
1767
|
return this.fetch(`/api/v1/conversations/${encodeURIComponent(conversationId)}/fork`, { method: "POST", body: input?.title ? { title: input.title } : {} });
|
|
1701
1768
|
}
|
|
@@ -1945,7 +2012,7 @@ class AgentComposeClient {
|
|
|
1945
2012
|
async createRepoLink(input, opts) {
|
|
1946
2013
|
const slug = opts?.factorySlug ?? DEFAULT_FACTORY;
|
|
1947
2014
|
const body = await this.fetch(`/api/v1/factories/${encodeURIComponent(slug)}/repo-links`, { method: "POST", body: input });
|
|
1948
|
-
return body.
|
|
2015
|
+
return body.links;
|
|
1949
2016
|
}
|
|
1950
2017
|
deleteRepoLink(linkId, opts) {
|
|
1951
2018
|
const slug = opts?.factorySlug ?? DEFAULT_FACTORY;
|
|
@@ -1969,9 +2036,17 @@ class AgentComposeClient {
|
|
|
1969
2036
|
createApiKey(input) {
|
|
1970
2037
|
return this.fetch("/api-keys", { method: "POST", body: input });
|
|
1971
2038
|
}
|
|
1972
|
-
async listApiKeys() {
|
|
1973
|
-
const
|
|
1974
|
-
|
|
2039
|
+
async listApiKeys(opts) {
|
|
2040
|
+
const qs = new URLSearchParams;
|
|
2041
|
+
if (opts?.limit !== undefined)
|
|
2042
|
+
qs.set("limit", String(opts.limit));
|
|
2043
|
+
if (opts?.status !== undefined)
|
|
2044
|
+
qs.set("status", opts.status);
|
|
2045
|
+
if (opts?.cursor !== undefined)
|
|
2046
|
+
qs.set("cursor", opts.cursor);
|
|
2047
|
+
const suffix = qs.toString() ? `?${qs.toString()}` : "";
|
|
2048
|
+
const body = await this.fetch(`/api-keys${suffix}`);
|
|
2049
|
+
return { data: body.data, hasMore: body.has_more, nextCursor: body.next_cursor ?? null };
|
|
1975
2050
|
}
|
|
1976
2051
|
getUsage(from, to) {
|
|
1977
2052
|
const qs = `?from=${encodeURIComponent(from.toISOString())}&to=${encodeURIComponent(to.toISOString())}`;
|
|
@@ -2466,6 +2541,7 @@ class AcpClientPeer {
|
|
|
2466
2541
|
}
|
|
2467
2542
|
// src/utils/bundler.ts
|
|
2468
2543
|
import { createHash as createHash2 } from "node:crypto";
|
|
2544
|
+
import { dirname as dirname2 } from "node:path";
|
|
2469
2545
|
import { parse as babelParse } from "@babel/parser";
|
|
2470
2546
|
|
|
2471
2547
|
// src/utils/source-loader.ts
|
|
@@ -2495,15 +2571,119 @@ class WorkflowSourceValidationError extends Error {
|
|
|
2495
2571
|
this.name = "WorkflowSourceValidationError";
|
|
2496
2572
|
}
|
|
2497
2573
|
}
|
|
2574
|
+
var SDK_PACKAGE = "@agent-compose/sdk";
|
|
2575
|
+
var SDK_SPECIFIER_ALIASES = [
|
|
2576
|
+
"agentc/sdk",
|
|
2577
|
+
"@agentc/sdk",
|
|
2578
|
+
"agentc-sdk",
|
|
2579
|
+
"agent-compose/sdk",
|
|
2580
|
+
"agentcompose/sdk",
|
|
2581
|
+
"@agentcompose/sdk",
|
|
2582
|
+
"agent-compose-sdk"
|
|
2583
|
+
];
|
|
2584
|
+
var SDK_ALIAS_SET = new Set(SDK_SPECIFIER_ALIASES);
|
|
2585
|
+
function resolveSdkAlias(specifier) {
|
|
2586
|
+
return SDK_ALIAS_SET.has(specifier) ? SDK_PACKAGE : null;
|
|
2587
|
+
}
|
|
2588
|
+
function aliasFilter() {
|
|
2589
|
+
const alternation = SDK_SPECIFIER_ALIASES.map((s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join("|");
|
|
2590
|
+
return new RegExp(`^(?:${alternation})$`);
|
|
2591
|
+
}
|
|
2592
|
+
function sdkAliasPlugin(bun) {
|
|
2593
|
+
return {
|
|
2594
|
+
name: "agent-compose-sdk-alias",
|
|
2595
|
+
setup(build) {
|
|
2596
|
+
build.onResolve({ filter: aliasFilter() }, (args) => {
|
|
2597
|
+
const target = resolveSdkAlias(args.path);
|
|
2598
|
+
if (!target)
|
|
2599
|
+
return;
|
|
2600
|
+
const from = args.importer ? dirname2(args.importer) : args.resolveDir;
|
|
2601
|
+
try {
|
|
2602
|
+
return { path: bun.resolveSync(target, from && from.length > 0 ? from : process.cwd()) };
|
|
2603
|
+
} catch {
|
|
2604
|
+
return;
|
|
2605
|
+
}
|
|
2606
|
+
});
|
|
2607
|
+
}
|
|
2608
|
+
};
|
|
2609
|
+
}
|
|
2610
|
+
function readUnresolvedSpecifier(diag) {
|
|
2611
|
+
if (!diag || typeof diag !== "object")
|
|
2612
|
+
return null;
|
|
2613
|
+
const d = diag;
|
|
2614
|
+
if (typeof d.specifier === "string" && d.specifier.length > 0)
|
|
2615
|
+
return d.specifier;
|
|
2616
|
+
const message = typeof d.message === "string" ? d.message : "";
|
|
2617
|
+
const m = /Could not resolve:?\s*"([^"]+)"/.exec(message);
|
|
2618
|
+
return m ? m[1] : null;
|
|
2619
|
+
}
|
|
2620
|
+
function flattenDiagnostics(failure) {
|
|
2621
|
+
const out = [];
|
|
2622
|
+
const seen = new Set;
|
|
2623
|
+
const walk = (node, depth) => {
|
|
2624
|
+
if (!node || depth > 4 || seen.has(node))
|
|
2625
|
+
return;
|
|
2626
|
+
seen.add(node);
|
|
2627
|
+
if (Array.isArray(node)) {
|
|
2628
|
+
for (const sub of node)
|
|
2629
|
+
walk(sub, depth + 1);
|
|
2630
|
+
return;
|
|
2631
|
+
}
|
|
2632
|
+
out.push(node);
|
|
2633
|
+
if (typeof node === "object") {
|
|
2634
|
+
const n = node;
|
|
2635
|
+
if (Array.isArray(n.errors))
|
|
2636
|
+
for (const sub of n.errors)
|
|
2637
|
+
walk(sub, depth + 1);
|
|
2638
|
+
if (n.cause)
|
|
2639
|
+
walk(n.cause, depth + 1);
|
|
2640
|
+
}
|
|
2641
|
+
};
|
|
2642
|
+
walk(failure, 0);
|
|
2643
|
+
return out;
|
|
2644
|
+
}
|
|
2645
|
+
function diagnosticText(diag) {
|
|
2646
|
+
if (diag instanceof Error)
|
|
2647
|
+
return diag.message || String(diag);
|
|
2648
|
+
if (diag && typeof diag === "object" && typeof diag.message === "string") {
|
|
2649
|
+
return diag.message;
|
|
2650
|
+
}
|
|
2651
|
+
return String(diag);
|
|
2652
|
+
}
|
|
2653
|
+
function explainBundleFailure(err, label) {
|
|
2654
|
+
const diagnostics = flattenDiagnostics(err);
|
|
2655
|
+
const unresolved = [];
|
|
2656
|
+
for (const diag of diagnostics) {
|
|
2657
|
+
const specifier = readUnresolvedSpecifier(diag);
|
|
2658
|
+
if (specifier && !unresolved.includes(specifier))
|
|
2659
|
+
unresolved.push(specifier);
|
|
2660
|
+
}
|
|
2661
|
+
if (unresolved.length > 0) {
|
|
2662
|
+
const quoted = unresolved.map((s) => `"${s}"`).join(", ");
|
|
2663
|
+
return `Could not resolve ${quoted} — workflow source imports the platform SDK as "${SDK_PACKAGE}". ` + `Fix the import specifier in ${label}; anything that is genuinely a third-party ` + `package must be installed where the source is bundled.`;
|
|
2664
|
+
}
|
|
2665
|
+
const detail = diagnostics.map(diagnosticText).filter((t) => t.length > 0 && t !== "Bundle failed").join(`
|
|
2666
|
+
`);
|
|
2667
|
+
return `Failed to bundle ${label}:
|
|
2668
|
+
${detail || "the bundler reported no diagnostics"}`;
|
|
2669
|
+
}
|
|
2498
2670
|
async function bundle(path, label) {
|
|
2499
2671
|
const bun = globalThis;
|
|
2500
2672
|
if (!bun.Bun?.build)
|
|
2501
2673
|
throw new Error("bundleWorkflow requires the Bun runtime (Bun.build)");
|
|
2502
|
-
|
|
2674
|
+
let result;
|
|
2675
|
+
try {
|
|
2676
|
+
result = await bun.Bun.build({
|
|
2677
|
+
entrypoints: [path],
|
|
2678
|
+
format: "esm",
|
|
2679
|
+
target: "node",
|
|
2680
|
+
plugins: [sdkAliasPlugin(bun.Bun)]
|
|
2681
|
+
});
|
|
2682
|
+
} catch (err) {
|
|
2683
|
+
throw new WorkflowSourceValidationError(explainBundleFailure(err, label));
|
|
2684
|
+
}
|
|
2503
2685
|
if (!result.success) {
|
|
2504
|
-
throw new
|
|
2505
|
-
${result.logs.map((l) => l.message).join(`
|
|
2506
|
-
`)}`);
|
|
2686
|
+
throw new WorkflowSourceValidationError(explainBundleFailure(result.logs, label));
|
|
2507
2687
|
}
|
|
2508
2688
|
return result.outputs[0].text();
|
|
2509
2689
|
}
|
|
@@ -2621,7 +2801,8 @@ async function bundleWorkflow(workflowPath, overrides) {
|
|
|
2621
2801
|
...metadata.connectors !== undefined ? { connectors: metadata.connectors } : {},
|
|
2622
2802
|
...metadata.connectorOperation !== undefined ? { connectorOperation: metadata.connectorOperation } : {},
|
|
2623
2803
|
...metadata.invokePolicy !== undefined ? { invokePolicy: metadata.invokePolicy } : {},
|
|
2624
|
-
...metadata.environmentBuild !== undefined ? { environmentBuild: metadata.environmentBuild } : {}
|
|
2804
|
+
...metadata.environmentBuild !== undefined ? { environmentBuild: metadata.environmentBuild } : {},
|
|
2805
|
+
...metadata.factoryDrive !== undefined ? { factoryDrive: metadata.factoryDrive } : {}
|
|
2625
2806
|
};
|
|
2626
2807
|
}
|
|
2627
2808
|
function extractIOSchema(zodSchema) {
|
|
@@ -3019,7 +3200,8 @@ async function agentLoop(opts) {
|
|
|
3019
3200
|
label,
|
|
3020
3201
|
allowedTools: opts.allowedTools ?? DEFAULT_ALLOWED_TOOLS,
|
|
3021
3202
|
...client.model != null ? { model: client.model } : {},
|
|
3022
|
-
...client.kind != null ? { runtimeKind: client.kind } : {}
|
|
3203
|
+
...client.kind != null ? { runtimeKind: client.kind } : {},
|
|
3204
|
+
...opts.phase != null ? { phase: opts.phase } : {}
|
|
3023
3205
|
});
|
|
3024
3206
|
}
|
|
3025
3207
|
const flushState = async (currentIteration) => {
|
|
@@ -3616,6 +3798,29 @@ function wrapJsonlCommand(args) {
|
|
|
3616
3798
|
function now4() {
|
|
3617
3799
|
return new Date().toISOString();
|
|
3618
3800
|
}
|
|
3801
|
+
var TAIL_REATTACH_DELAY_MS = 2000;
|
|
3802
|
+
var LAUNCH_EXEC_TIMEOUT_MS = 30000;
|
|
3803
|
+
var LAUNCH_PID_RECOVERY_ATTEMPTS = 3;
|
|
3804
|
+
var LAUNCH_PID_RECOVERY_DELAY_MS = 1000;
|
|
3805
|
+
var INSTALL_PROBE_TIMEOUT_MS = 30000;
|
|
3806
|
+
function parsePid(raw) {
|
|
3807
|
+
const pid = Number.parseInt(raw.trim().split(`
|
|
3808
|
+
`).pop() ?? "", 10);
|
|
3809
|
+
return Number.isInteger(pid) && pid > 0 ? pid : Number.NaN;
|
|
3810
|
+
}
|
|
3811
|
+
async function recoverLaunchPid(read, pidPath) {
|
|
3812
|
+
for (let attempt = 0;attempt < LAUNCH_PID_RECOVERY_ATTEMPTS; attempt++) {
|
|
3813
|
+
try {
|
|
3814
|
+
const pid = parsePid(await read(pidPath));
|
|
3815
|
+
if (!Number.isNaN(pid))
|
|
3816
|
+
return pid;
|
|
3817
|
+
} catch {}
|
|
3818
|
+
if (attempt < LAUNCH_PID_RECOVERY_ATTEMPTS - 1) {
|
|
3819
|
+
await new Promise((r) => setTimeout(r, LAUNCH_PID_RECOVERY_DELAY_MS));
|
|
3820
|
+
}
|
|
3821
|
+
}
|
|
3822
|
+
return Number.NaN;
|
|
3823
|
+
}
|
|
3619
3824
|
function shellQuote(value) {
|
|
3620
3825
|
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
3621
3826
|
}
|
|
@@ -3701,7 +3906,7 @@ class CliAgentRunner {
|
|
|
3701
3906
|
async ensureInstalled() {
|
|
3702
3907
|
if (this.installed)
|
|
3703
3908
|
return;
|
|
3704
|
-
const probe = await this.sandbox.commands.run(`command -v ${this.spec.bin}
|
|
3909
|
+
const probe = await this.sandbox.commands.run(`command -v ${this.spec.bin}`, { timeoutMs: INSTALL_PROBE_TIMEOUT_MS });
|
|
3705
3910
|
if (probe.exitCode === 0) {
|
|
3706
3911
|
this.installed = true;
|
|
3707
3912
|
return;
|
|
@@ -3866,51 +4071,153 @@ class CliAgentRunner {
|
|
|
3866
4071
|
keepBytes: JSONL_GUARD_KEEP_BYTES
|
|
3867
4072
|
});
|
|
3868
4073
|
const lines = new AsyncQueue;
|
|
3869
|
-
let
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3876
|
-
buf = buf.slice(nl + 1);
|
|
3877
|
-
if (line)
|
|
3878
|
-
lines.push(line);
|
|
3879
|
-
}
|
|
3880
|
-
};
|
|
3881
|
-
const runOpts = {
|
|
3882
|
-
...this.options.cwd ? { cwd: this.options.cwd } : {},
|
|
3883
|
-
onStdout,
|
|
3884
|
-
timeoutMs: 0
|
|
3885
|
-
};
|
|
3886
|
-
let exited = false;
|
|
3887
|
-
const settle = (res) => {
|
|
3888
|
-
exited = true;
|
|
3889
|
-
const tail = buf.trim();
|
|
3890
|
-
if (tail)
|
|
3891
|
-
lines.push(tail);
|
|
3892
|
-
lines.close();
|
|
3893
|
-
return res;
|
|
3894
|
-
};
|
|
3895
|
-
const fail = (err) => {
|
|
3896
|
-
exited = true;
|
|
3897
|
-
lines.close();
|
|
3898
|
-
throw err;
|
|
3899
|
-
};
|
|
3900
|
-
let kill;
|
|
3901
|
-
let runPromise;
|
|
4074
|
+
let exitCode = null;
|
|
4075
|
+
let exitStderr = "";
|
|
4076
|
+
let transportError = null;
|
|
4077
|
+
let consumerStopped = false;
|
|
4078
|
+
let reap = () => {};
|
|
4079
|
+
let transport;
|
|
4080
|
+
const durableRead = this.sandbox.files.read?.bind(this.sandbox.files);
|
|
3902
4081
|
if (this.sandbox.commands.runBackground) {
|
|
3903
|
-
const
|
|
3904
|
-
|
|
3905
|
-
|
|
4082
|
+
const outPath = `${promptPath}.out`;
|
|
4083
|
+
const errPath = `${promptPath}.err`;
|
|
4084
|
+
const detachedScript = `{ ${guardedCmd}; } >> ${shellQuote(outPath)} 2>> ${shellQuote(errPath)} </dev/null; ` + `printf '\\n%s %s\\n' ${shellQuote(sentinel)} "$?" >> ${shellQuote(outPath)}`;
|
|
4085
|
+
const pidPath = `${promptPath}.pid`;
|
|
4086
|
+
const launchCmd = `: > ${shellQuote(outPath)}; : > ${shellQuote(errPath)}; ` + `if command -v setsid >/dev/null 2>&1; then setsid sh -c ${shellQuote(detachedScript)} >/dev/null 2>&1 </dev/null & ` + `else sh -c ${shellQuote(detachedScript)} >/dev/null 2>&1 </dev/null & fi; ` + `echo "$!" > ${shellQuote(pidPath)}; echo "$!"`;
|
|
4087
|
+
let pid = Number.NaN;
|
|
4088
|
+
let launched = null;
|
|
4089
|
+
let launchErr = null;
|
|
4090
|
+
try {
|
|
4091
|
+
launched = await this.sandbox.commands.run(launchCmd, {
|
|
4092
|
+
...this.options.cwd ? { cwd: this.options.cwd } : {},
|
|
4093
|
+
timeoutMs: LAUNCH_EXEC_TIMEOUT_MS
|
|
4094
|
+
});
|
|
4095
|
+
} catch (err) {
|
|
4096
|
+
launchErr = err;
|
|
4097
|
+
}
|
|
4098
|
+
if (launched) {
|
|
4099
|
+
pid = parsePid(launched.stdout ?? "");
|
|
4100
|
+
if (launched.exitCode !== 0 || Number.isNaN(pid)) {
|
|
4101
|
+
throw new Error(`failed to launch ${this.spec.kind}: ` + `exit ${launched.exitCode}${launched.stderr ? `: ${launched.stderr.slice(-500)}` : ""}`);
|
|
4102
|
+
}
|
|
4103
|
+
} else {
|
|
4104
|
+
pid = durableRead ? await recoverLaunchPid(durableRead, pidPath) : Number.NaN;
|
|
4105
|
+
if (Number.isNaN(pid))
|
|
4106
|
+
throw launchErr;
|
|
4107
|
+
console.warn(`[cli-agent] ${this.spec.kind} launch exec stream died but the detached runner is up ` + `(pid ${pid} via ${pidPath}) — continuing on the durable transport: ${formatError(launchErr)}`);
|
|
4108
|
+
}
|
|
4109
|
+
let offset = 0;
|
|
4110
|
+
let currentTail = null;
|
|
4111
|
+
const consumeLine = (raw) => {
|
|
4112
|
+
offset += Buffer.byteLength(raw, "utf8") + 1;
|
|
4113
|
+
const line = raw.trim();
|
|
4114
|
+
if (!line)
|
|
4115
|
+
return;
|
|
4116
|
+
if (line.startsWith(sentinel)) {
|
|
4117
|
+
const code = Number(line.slice(sentinel.length).trim());
|
|
4118
|
+
exitCode = Number.isFinite(code) ? code : 0;
|
|
4119
|
+
return;
|
|
4120
|
+
}
|
|
4121
|
+
lines.push(line);
|
|
4122
|
+
};
|
|
4123
|
+
reap = () => {
|
|
4124
|
+
this.sandbox.commands.run(`kill -TERM -- -${pid} 2>/dev/null; kill -TERM ${pid} 2>/dev/null; true`, { timeoutMs: 1e4 }).catch(() => {});
|
|
4125
|
+
currentTail?.kill().catch(() => {});
|
|
4126
|
+
};
|
|
4127
|
+
transport = (async () => {
|
|
4128
|
+
while (exitCode === null && !consumerStopped && !opts.signal?.aborted) {
|
|
4129
|
+
let buf = "";
|
|
4130
|
+
let handle = null;
|
|
4131
|
+
const onStdout = (data) => {
|
|
4132
|
+
buf += data;
|
|
4133
|
+
let nl;
|
|
4134
|
+
while ((nl = buf.indexOf(`
|
|
4135
|
+
`)) >= 0) {
|
|
4136
|
+
consumeLine(buf.slice(0, nl));
|
|
4137
|
+
buf = buf.slice(nl + 1);
|
|
4138
|
+
if (exitCode !== null) {
|
|
4139
|
+
handle?.kill().catch(() => {});
|
|
4140
|
+
return;
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
};
|
|
4144
|
+
try {
|
|
4145
|
+
handle = await this.sandbox.commands.runBackground(`tail -c +${offset + 1} -f ${shellQuote(outPath)}`, { timeoutMs: 0, onStdout });
|
|
4146
|
+
} catch {}
|
|
4147
|
+
if (handle) {
|
|
4148
|
+
if (exitCode !== null)
|
|
4149
|
+
handle.kill().catch(() => {});
|
|
4150
|
+
currentTail = handle;
|
|
4151
|
+
await handle.wait().catch(() => {
|
|
4152
|
+
return;
|
|
4153
|
+
});
|
|
4154
|
+
currentTail = null;
|
|
4155
|
+
}
|
|
4156
|
+
if (exitCode !== null || consumerStopped || opts.signal?.aborted)
|
|
4157
|
+
break;
|
|
4158
|
+
if (durableRead) {
|
|
4159
|
+
try {
|
|
4160
|
+
const bytes = Buffer.from(await durableRead(outPath), "utf8");
|
|
4161
|
+
let rest = bytes.subarray(offset).toString("utf8");
|
|
4162
|
+
let nl;
|
|
4163
|
+
while (exitCode === null && (nl = rest.indexOf(`
|
|
4164
|
+
`)) >= 0) {
|
|
4165
|
+
consumeLine(rest.slice(0, nl));
|
|
4166
|
+
rest = rest.slice(nl + 1);
|
|
4167
|
+
}
|
|
4168
|
+
} catch {}
|
|
4169
|
+
}
|
|
4170
|
+
if (exitCode !== null)
|
|
4171
|
+
break;
|
|
4172
|
+
let alive = null;
|
|
4173
|
+
try {
|
|
4174
|
+
const probe = await this.sandbox.commands.run(`kill -0 ${pid} 2>/dev/null`, { timeoutMs: 1e4 });
|
|
4175
|
+
alive = probe.exitCode === 0;
|
|
4176
|
+
} catch {
|
|
4177
|
+
alive = null;
|
|
4178
|
+
}
|
|
4179
|
+
if (alive === false) {
|
|
4180
|
+
exitCode = JSONL_GUARD_NO_SENTINEL_EXIT;
|
|
4181
|
+
break;
|
|
4182
|
+
}
|
|
4183
|
+
console.warn(`[cli-agent] ${this.spec.kind} turn stream lost with the runner alive — re-tailing ${outPath} from byte ${offset}`);
|
|
4184
|
+
await new Promise((r) => setTimeout(r, TAIL_REATTACH_DELAY_MS));
|
|
4185
|
+
}
|
|
4186
|
+
if (exitCode !== null && exitCode !== 0 && durableRead) {
|
|
4187
|
+
try {
|
|
4188
|
+
exitStderr = (await durableRead(errPath)).slice(-2000);
|
|
4189
|
+
} catch {}
|
|
4190
|
+
}
|
|
4191
|
+
})().finally(() => lines.close());
|
|
3906
4192
|
} else {
|
|
3907
|
-
|
|
4193
|
+
let buf = "";
|
|
4194
|
+
const onStdout = (data) => {
|
|
4195
|
+
buf += data;
|
|
4196
|
+
let nl;
|
|
4197
|
+
while ((nl = buf.indexOf(`
|
|
4198
|
+
`)) >= 0) {
|
|
4199
|
+
const line = buf.slice(0, nl).trim();
|
|
4200
|
+
buf = buf.slice(nl + 1);
|
|
4201
|
+
if (line)
|
|
4202
|
+
lines.push(line);
|
|
4203
|
+
}
|
|
4204
|
+
};
|
|
4205
|
+
const runOpts = {
|
|
4206
|
+
...this.options.cwd ? { cwd: this.options.cwd } : {},
|
|
4207
|
+
onStdout,
|
|
4208
|
+
timeoutMs: 0
|
|
4209
|
+
};
|
|
4210
|
+
transport = this.sandbox.commands.run(guardedCmd, runOpts).then((res) => {
|
|
4211
|
+
const tail = buf.trim();
|
|
4212
|
+
if (tail)
|
|
4213
|
+
lines.push(tail);
|
|
4214
|
+
exitCode = res.exitCode;
|
|
4215
|
+
exitStderr = (res.stderr ?? "").slice(-2000);
|
|
4216
|
+
}, (err) => {
|
|
4217
|
+
transportError = err;
|
|
4218
|
+
}).finally(() => lines.close());
|
|
3908
4219
|
}
|
|
3909
|
-
|
|
3910
|
-
const reap = () => {
|
|
3911
|
-
if (!exited)
|
|
3912
|
-
kill?.().catch(() => {});
|
|
3913
|
-
};
|
|
4220
|
+
transport.catch(() => {});
|
|
3914
4221
|
if (opts.signal) {
|
|
3915
4222
|
if (opts.signal.aborted)
|
|
3916
4223
|
reap();
|
|
@@ -3934,18 +4241,28 @@ class CliAgentRunner {
|
|
|
3934
4241
|
yield msg;
|
|
3935
4242
|
}
|
|
3936
4243
|
}
|
|
3937
|
-
|
|
3938
|
-
if (
|
|
3939
|
-
|
|
3940
|
-
|
|
4244
|
+
await transport;
|
|
4245
|
+
if (transportError)
|
|
4246
|
+
throw transportError;
|
|
4247
|
+
if (exitCode === null) {
|
|
4248
|
+
if (!sawError) {
|
|
4249
|
+
yield { type: "error", text: `${this.spec.kind} aborted — the in-sandbox runner was terminated`, timestamp: now4() };
|
|
4250
|
+
}
|
|
4251
|
+
return;
|
|
4252
|
+
}
|
|
4253
|
+
if (exitCode !== 0 && !sawError) {
|
|
4254
|
+
const detail = exitCode === JSONL_GUARD_NO_SENTINEL_EXIT ? " (the runner died without reporting an exit code)" : "";
|
|
4255
|
+
yield { type: "error", text: `${this.spec.kind} exited with code ${exitCode}${detail}${exitStderr ? `: ${exitStderr}` : ""}`, timestamp: now4() };
|
|
3941
4256
|
return;
|
|
3942
4257
|
}
|
|
3943
4258
|
if (!sawError)
|
|
3944
4259
|
yield { type: "done", sessionId: sessionId ?? "", timestamp: now4() };
|
|
3945
4260
|
} finally {
|
|
3946
4261
|
opts.signal?.removeEventListener("abort", reap);
|
|
3947
|
-
|
|
3948
|
-
|
|
4262
|
+
consumerStopped = true;
|
|
4263
|
+
if (exitCode === null)
|
|
4264
|
+
reap();
|
|
4265
|
+
this.sandbox.commands.run(`rm -f ${shellQuote(promptPath)} ${shellQuote(guardPath)} ${shellQuote(`${promptPath}.out`)} ${shellQuote(`${promptPath}.err`)} ${shellQuote(`${promptPath}.pid`)}`, { timeoutMs: 1e4 }).catch(() => {});
|
|
3949
4266
|
}
|
|
3950
4267
|
} catch (err) {
|
|
3951
4268
|
yield { type: "error", text: formatError(err), timestamp: now4() };
|
|
@@ -3986,11 +4303,11 @@ var codexSpec = {
|
|
|
3986
4303
|
"--skip-git-repo-check",
|
|
3987
4304
|
"--dangerously-bypass-approvals-and-sandbox",
|
|
3988
4305
|
...model ? ["-m", shellQuote(model)] : [],
|
|
3989
|
-
...effort ? ["-c", `model_reasoning_effort=${effort}`] : []
|
|
3990
|
-
...cwd ? ["-C", shellQuote(cwd)] : []
|
|
4306
|
+
...effort ? ["-c", `model_reasoning_effort=${effort === "max" ? "xhigh" : effort}`] : []
|
|
3991
4307
|
].join(" ");
|
|
4308
|
+
const cd = cwd ? `cd ${shellQuote(cwd)} && ` : "";
|
|
3992
4309
|
const exec = sessionId ? `codex exec resume ${shellQuote(sessionId)} ${flags}` : `codex exec ${flags}`;
|
|
3993
|
-
return `${exec} - < ${shellQuote(promptPath)}`;
|
|
4310
|
+
return `${cd}${exec} - < ${shellQuote(promptPath)}`;
|
|
3994
4311
|
},
|
|
3995
4312
|
extractSessionId: (p) => p.type === "thread.started" && typeof p.thread_id === "string" ? p.thread_id : undefined,
|
|
3996
4313
|
mapEvent: (p) => {
|
|
@@ -4208,7 +4525,7 @@ function toolResultText(content) {
|
|
|
4208
4525
|
}
|
|
4209
4526
|
return content == null ? "" : JSON.stringify(content) ?? "";
|
|
4210
4527
|
}
|
|
4211
|
-
var
|
|
4528
|
+
var CLAUDE_CODE_EFFORT_LEVELS = ["low", "medium", "high", "xhigh", "max"];
|
|
4212
4529
|
var claudeCodeSpec = {
|
|
4213
4530
|
kind: "claude-code",
|
|
4214
4531
|
authEnv: "ANTHROPIC_API_KEY",
|
|
@@ -4224,14 +4541,15 @@ var claudeCodeSpec = {
|
|
|
4224
4541
|
"--include-partial-messages",
|
|
4225
4542
|
"--dangerously-skip-permissions",
|
|
4226
4543
|
...model ? [`--model ${shellQuote(model)}`] : [],
|
|
4544
|
+
...effort ? [`--effort ${effort}`] : [],
|
|
4227
4545
|
...sessionId ? [`--resume ${shellQuote(sessionId)}`] : []
|
|
4228
4546
|
].join(" ");
|
|
4229
|
-
|
|
4230
|
-
return `${cwd ? `cd ${shellQuote(cwd)} && ` : ""}${thinking}IS_SANDBOX=1 claude ${flags} < ${shellQuote(promptPath)}`;
|
|
4547
|
+
return `${cwd ? `cd ${shellQuote(cwd)} && ` : ""}IS_SANDBOX=1 claude ${flags} < ${shellQuote(promptPath)}`;
|
|
4231
4548
|
},
|
|
4232
4549
|
extractSessionId: (p) => typeof p.session_id === "string" ? p.session_id : undefined,
|
|
4233
4550
|
mapEvent: (p) => {
|
|
4234
4551
|
const ts = now10();
|
|
4552
|
+
const parent = typeof p.parent_tool_use_id === "string" && p.parent_tool_use_id.length > 0 ? { parentToolUseId: p.parent_tool_use_id } : {};
|
|
4235
4553
|
switch (p.type) {
|
|
4236
4554
|
case "assistant": {
|
|
4237
4555
|
const message = p.message;
|
|
@@ -4249,6 +4567,7 @@ var claudeCodeSpec = {
|
|
|
4249
4567
|
toolName: String(b.name ?? "tool"),
|
|
4250
4568
|
toolInput: b.input ?? {},
|
|
4251
4569
|
toolUseId: String(b.id ?? ""),
|
|
4570
|
+
...parent,
|
|
4252
4571
|
timestamp: ts
|
|
4253
4572
|
}];
|
|
4254
4573
|
}
|
|
@@ -4263,6 +4582,7 @@ var claudeCodeSpec = {
|
|
|
4263
4582
|
toolUseId: String(b.tool_use_id ?? ""),
|
|
4264
4583
|
output: toolResultText(b.content),
|
|
4265
4584
|
isError: b.is_error === true,
|
|
4585
|
+
...parent,
|
|
4266
4586
|
timestamp: ts
|
|
4267
4587
|
}] : []);
|
|
4268
4588
|
}
|
|
@@ -4382,23 +4702,36 @@ function toE2bNetwork(policy) {
|
|
|
4382
4702
|
}
|
|
4383
4703
|
|
|
4384
4704
|
// src/sandbox/sizes.ts
|
|
4385
|
-
var
|
|
4386
|
-
"
|
|
4387
|
-
"
|
|
4388
|
-
"
|
|
4389
|
-
"
|
|
4705
|
+
var SANDBOX_MACHINES = {
|
|
4706
|
+
"1vcpu-2gb": { vcpus: 1, memoryMB: 2048 },
|
|
4707
|
+
"2vcpu-4gb": { vcpus: 2, memoryMB: 4096 },
|
|
4708
|
+
"4vcpu-8gb": { vcpus: 4, memoryMB: 8192 },
|
|
4709
|
+
"8vcpu-8gb": { vcpus: 8, memoryMB: 8192 },
|
|
4710
|
+
"8vcpu-16gb": { vcpus: 8, memoryMB: 16384 },
|
|
4711
|
+
"32vcpu-64gb": { vcpus: 32, memoryMB: 65536 }
|
|
4390
4712
|
};
|
|
4713
|
+
var SANDBOX_SIZES = Object.keys(SANDBOX_MACHINES);
|
|
4714
|
+
var SANDBOX_VCPUS = Object.fromEntries(SANDBOX_SIZES.map((s) => [s, SANDBOX_MACHINES[s].vcpus]));
|
|
4391
4715
|
var DEFAULT_SANDBOX_SIZE = "2vcpu-4gb";
|
|
4392
|
-
var
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
];
|
|
4716
|
+
var SESSION_DEFAULT_SANDBOX_SIZE = "4vcpu-8gb";
|
|
4717
|
+
var E2B_MAX_VCPUS = 8;
|
|
4718
|
+
var E2B_MAX_MEMORY_MB = 8192;
|
|
4719
|
+
var E2B_TEMPLATE_SIZES = SANDBOX_SIZES.filter((s) => SANDBOX_MACHINES[s].vcpus <= E2B_MAX_VCPUS && SANDBOX_MACHINES[s].memoryMB <= E2B_MAX_MEMORY_MB);
|
|
4396
4720
|
function isE2bSupportedSize(size) {
|
|
4397
4721
|
return E2B_TEMPLATE_SIZES.includes(size);
|
|
4398
4722
|
}
|
|
4723
|
+
var VERCEL_MEMORY_MB_PER_VCPU = 2048;
|
|
4724
|
+
function isVercelSupportedSize(size) {
|
|
4725
|
+
const m = SANDBOX_MACHINES[size];
|
|
4726
|
+
return m.memoryMB === m.vcpus * VERCEL_MEMORY_MB_PER_VCPU;
|
|
4727
|
+
}
|
|
4399
4728
|
function e2bMachineSpec(size) {
|
|
4400
|
-
const
|
|
4401
|
-
return { cpuCount, memoryMB:
|
|
4729
|
+
const m = SANDBOX_MACHINES[size];
|
|
4730
|
+
return { cpuCount: m.vcpus, memoryMB: m.memoryMB };
|
|
4731
|
+
}
|
|
4732
|
+
function sandboxSizeLabel(size) {
|
|
4733
|
+
const m = SANDBOX_MACHINES[size];
|
|
4734
|
+
return `${m.vcpus} vCPU · ${Math.round(m.memoryMB / 1024)} GB`;
|
|
4402
4735
|
}
|
|
4403
4736
|
function e2bBaseTemplate(size) {
|
|
4404
4737
|
return `agent-compose-base-${size}`;
|
|
@@ -4615,7 +4948,7 @@ function makeE2bSandboxProvider(sb) {
|
|
|
4615
4948
|
return { resumeHandle: sb.sandboxId };
|
|
4616
4949
|
},
|
|
4617
4950
|
async extendLifetime(ms) {
|
|
4618
|
-
await sb.setTimeout(ms);
|
|
4951
|
+
await sb.setTimeout(Math.min(ms, e2bMaxSandboxMs()));
|
|
4619
4952
|
},
|
|
4620
4953
|
async updateNetworkPolicy(policy) {
|
|
4621
4954
|
await sb.updateNetwork(toE2bNetwork(policy));
|
|
@@ -4892,7 +5225,7 @@ var vercelProviderDef = {
|
|
|
4892
5225
|
|
|
4893
5226
|
// src/sandbox/providers/local.ts
|
|
4894
5227
|
import { promises as fs2 } from "node:fs";
|
|
4895
|
-
import { dirname as
|
|
5228
|
+
import { dirname as dirname3 } from "node:path";
|
|
4896
5229
|
import { spawn } from "node:child_process";
|
|
4897
5230
|
import { Readable, Writable } from "node:stream";
|
|
4898
5231
|
function makeLocalSandboxProvider() {
|
|
@@ -4958,7 +5291,7 @@ function makeLocalSandboxProvider() {
|
|
|
4958
5291
|
},
|
|
4959
5292
|
files: {
|
|
4960
5293
|
async write(path, content) {
|
|
4961
|
-
await fs2.mkdir(
|
|
5294
|
+
await fs2.mkdir(dirname3(path), { recursive: true });
|
|
4962
5295
|
await fs2.writeFile(path, content);
|
|
4963
5296
|
},
|
|
4964
5297
|
async read(path) {
|
|
@@ -5509,6 +5842,15 @@ async function runWorkflow(wf, ctx, opts) {
|
|
|
5509
5842
|
}
|
|
5510
5843
|
}
|
|
5511
5844
|
// src/workflows/invoke-child.ts
|
|
5845
|
+
function deriveInvokeChildIdempotencyKey(parentRunId, childName) {
|
|
5846
|
+
const step = getActiveStep();
|
|
5847
|
+
if (!step)
|
|
5848
|
+
return null;
|
|
5849
|
+
const ordinal = nextPauseOrdinalInActiveStep(`invoke-child:${childName}`);
|
|
5850
|
+
if (ordinal === null)
|
|
5851
|
+
return null;
|
|
5852
|
+
return `invoke-child:${parentRunId}:step${step.stepIndex}:${childName}:${ordinal}`;
|
|
5853
|
+
}
|
|
5512
5854
|
function buildInvokeChild(runId, opts = {}) {
|
|
5513
5855
|
let childClient = null;
|
|
5514
5856
|
const getChildClient = () => {
|
|
@@ -5522,11 +5864,15 @@ function buildInvokeChild(runId, opts = {}) {
|
|
|
5522
5864
|
childClient = new AgentComposeClient({ apiKey, baseUrl });
|
|
5523
5865
|
return childClient;
|
|
5524
5866
|
};
|
|
5525
|
-
return (name, input, childOpts) =>
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5867
|
+
return (name, input, childOpts) => {
|
|
5868
|
+
const idempotencyKey = childOpts?.idempotencyKey ?? deriveInvokeChildIdempotencyKey(runId, name) ?? undefined;
|
|
5869
|
+
return getChildClient().invokeAndWait(name, input, {
|
|
5870
|
+
...childOpts,
|
|
5871
|
+
...idempotencyKey !== undefined ? { idempotencyKey } : {},
|
|
5872
|
+
parentRunId: runId,
|
|
5873
|
+
factorySlug: childOpts?.factorySlug ?? opts.defaultFactorySlug ?? process.env.AGENT_COMPOSE_FACTORY ?? process.env.AGENT_COMPOSE_FACTORY_SLUG ?? "default"
|
|
5874
|
+
});
|
|
5875
|
+
};
|
|
5530
5876
|
}
|
|
5531
5877
|
// src/workflow-steps/step.ts
|
|
5532
5878
|
var CONTROL_CHARS = /[\u0000-\u001f\u007f]/;
|
|
@@ -6256,38 +6602,49 @@ there is nothing to type, paste, or screenshot a credential from.
|
|
|
6256
6602
|
|
|
6257
6603
|
## Recording a demo — the desktop, captured to a video the human can play
|
|
6258
6604
|
|
|
6259
|
-
"Record a demo of you using X" is a normal ask, and this machine does it
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
to any teammate watching; a recording is the durable, replayable artifact.
|
|
6264
|
-
Both modes exist; say so when it matters.)
|
|
6605
|
+
"Record a demo of you using X" is a normal ask, and this machine does it.
|
|
6606
|
+
(For a LIVE view no recording is needed — the session header's **Desktop**
|
|
6607
|
+
button already streams this display to any teammate watching; a recording is
|
|
6608
|
+
the durable, replayable artifact. Both modes exist; say so when it matters.)
|
|
6265
6609
|
|
|
6266
|
-
**
|
|
6610
|
+
**Use \`ac-record\` — the platform recorder is already on PATH** (cloud
|
|
6611
|
+
sessions; \`command -v ac-record\` to confirm on older machines):
|
|
6267
6612
|
|
|
6268
|
-
|
|
6613
|
+
ac-record start # begins capturing the desktop (display :0)
|
|
6614
|
+
# ... drive the app with xdotool, screenshotting as you go ...
|
|
6615
|
+
ac-record stop # finishes + saves to recordings/ in your workspace
|
|
6616
|
+
ac-record status # one JSON line: {"recording":true,...}
|
|
6269
6617
|
|
|
6270
|
-
|
|
6618
|
+
It records the whole display (with desktop audio when the machine has a
|
|
6619
|
+
PulseAudio monitor), enforces sane caps (5 min / 200 MB — start a fresh
|
|
6620
|
+
recording per scene rather than one long take), keeps the file playable even
|
|
6621
|
+
if the machine dies mid-take, and \`stop\` prints the saved path — the file
|
|
6622
|
+
lands ON THE DRIVE in \`recordings/\`, visible in Files and playable in the
|
|
6623
|
+
dashboard. A human watching the Desktop pane sees the recording indicator
|
|
6624
|
+
while you record.
|
|
6625
|
+
|
|
6626
|
+
If \`ac-record\` is missing (older machine), record by hand.
|
|
6627
|
+
**ffmpeg IS pre-installed** on platform images (\`command -v ffmpeg\`; only
|
|
6628
|
+
if absent: \`sudo apt-get update -q && sudo apt-get install -y -q ffmpeg\`):
|
|
6271
6629
|
|
|
6272
6630
|
DISPLAY=:0 ffmpeg -f x11grab \\
|
|
6273
6631
|
-video_size "$(DISPLAY=:0 xdotool getdisplaygeometry | tr ' ' x)" \\
|
|
6274
6632
|
-framerate 10 -i :0 -c:v libvpx -b:v 1M -deadline realtime -cpu-used 8 \\
|
|
6275
6633
|
demo.webm &
|
|
6276
6634
|
FFMPEG_PID=$!
|
|
6277
|
-
# ... drive the app with xdotool
|
|
6635
|
+
# ... drive the app with xdotool ...
|
|
6278
6636
|
kill -INT "$FFMPEG_PID" && wait "$FFMPEG_PID"
|
|
6279
6637
|
|
|
6280
|
-
The gotchas, each one earned:
|
|
6638
|
+
The hand-rolled gotchas, each one earned:
|
|
6281
6639
|
- **Stop with SIGINT (\`kill -INT\`), never SIGKILL** — ffmpeg finalizes the
|
|
6282
6640
|
file on SIGINT; a hard kill truncates the encode mid-write.
|
|
6283
|
-
- **Record WebM (matroska-family), not MP4** — mp4 writes its moov atom
|
|
6284
|
-
END, so a killed or crashed encode leaves an UNPLAYABLE file; webm
|
|
6285
|
-
playable up to the last written frame and plays natively in the
|
|
6286
|
-
|
|
6287
|
-
afterwards if you truly need it, never record straight to it.
|
|
6641
|
+
- **Record WebM (matroska-family), not plain MP4** — mp4 writes its moov atom
|
|
6642
|
+
at the END, so a killed or crashed encode leaves an UNPLAYABLE file; webm
|
|
6643
|
+
stays playable up to the last written frame and plays natively in the
|
|
6644
|
+
browser. (\`ac-record\` sidesteps this with fragmented mp4.)
|
|
6288
6645
|
- **\`-video_size\` must match the real screen** — x11grab does not default to
|
|
6289
6646
|
it; read the geometry from \`xdotool getdisplaygeometry\` as above.
|
|
6290
|
-
- **10–
|
|
6647
|
+
- **10–15 fps is right for a screen demo** — small files, legible UI motion;
|
|
6291
6648
|
this is not video production.
|
|
6292
6649
|
- **Write to the drive, not /tmp** — the recording must land in your working
|
|
6293
6650
|
directory to persist and show up in Files; a file in /tmp dies with the
|
|
@@ -6599,6 +6956,7 @@ async function agent(opts) {
|
|
|
6599
6956
|
runtime: (runtimeOpts) => opts.runtime.create(opts.sandbox, { ...runtimeOpts, agentManual: buildAgentContextDoc(process.env) }),
|
|
6600
6957
|
agentId,
|
|
6601
6958
|
...opts.label !== undefined ? { label: opts.label } : {},
|
|
6959
|
+
...opts.phase !== undefined ? { phase: opts.phase } : {},
|
|
6602
6960
|
...opts.budget?.turnsPerIteration !== undefined ? { turnsPerIteration: opts.budget.turnsPerIteration } : {},
|
|
6603
6961
|
...opts.budget?.maxIterations !== undefined ? { maxIterations: opts.budget.maxIterations } : {},
|
|
6604
6962
|
...opts.tools !== undefined ? { allowedTools: opts.tools } : {},
|
|
@@ -6669,7 +7027,21 @@ function clampPlanEntries(raw) {
|
|
|
6669
7027
|
var TABLE_MAX_COLUMNS = 12;
|
|
6670
7028
|
var TABLE_MAX_ROWS = 100;
|
|
6671
7029
|
var TABLE_CELL_MAX_CHARS = 200;
|
|
6672
|
-
var
|
|
7030
|
+
var DRIVE_PATH_MAX_CHARS = 512;
|
|
7031
|
+
function isPlausibleDrivePath(raw) {
|
|
7032
|
+
if (raw.trim().length === 0 || raw.length > DRIVE_PATH_MAX_CHARS)
|
|
7033
|
+
return false;
|
|
7034
|
+
if (raw.startsWith("-"))
|
|
7035
|
+
return false;
|
|
7036
|
+
if (/[\u0000-\u001f\u007f\\]/.test(raw))
|
|
7037
|
+
return false;
|
|
7038
|
+
if (/[|<>;`]|\$\(/.test(raw))
|
|
7039
|
+
return false;
|
|
7040
|
+
const trimmed = raw.replace(/^\/+|\/+$/g, "");
|
|
7041
|
+
if (trimmed.length === 0)
|
|
7042
|
+
return false;
|
|
7043
|
+
return trimmed.split("/").every((seg) => seg !== "" && seg !== "." && seg !== "..");
|
|
7044
|
+
}
|
|
6673
7045
|
function clampCellString(s) {
|
|
6674
7046
|
return s.length > TABLE_CELL_MAX_CHARS ? `${s.slice(0, TABLE_CELL_MAX_CHARS - 1)}…` : s;
|
|
6675
7047
|
}
|
|
@@ -6860,7 +7232,7 @@ function parseDisplayMarker(line) {
|
|
|
6860
7232
|
}
|
|
6861
7233
|
case "document": {
|
|
6862
7234
|
const path = str("path");
|
|
6863
|
-
if (!path)
|
|
7235
|
+
if (!path || !isPlausibleDrivePath(path))
|
|
6864
7236
|
return null;
|
|
6865
7237
|
return {
|
|
6866
7238
|
kind: "document",
|
|
@@ -6875,7 +7247,7 @@ function parseDisplayMarker(line) {
|
|
|
6875
7247
|
}
|
|
6876
7248
|
case "image": {
|
|
6877
7249
|
const path = str("path");
|
|
6878
|
-
if (!path || path
|
|
7250
|
+
if (!path || !isPlausibleDrivePath(path))
|
|
6879
7251
|
return null;
|
|
6880
7252
|
return {
|
|
6881
7253
|
kind: "image",
|
|
@@ -7014,6 +7386,8 @@ function detectAgentcInvocation(command) {
|
|
|
7014
7386
|
const w = words[i];
|
|
7015
7387
|
if (SHELL_STOPPERS.has(w))
|
|
7016
7388
|
break;
|
|
7389
|
+
if (/^\d*>&\d+$/.test(w))
|
|
7390
|
+
continue;
|
|
7017
7391
|
if (w.startsWith("--")) {
|
|
7018
7392
|
if (VALUE_FLAGS.has(w) && i + 1 < words.length && !SHELL_STOPPERS.has(words[i + 1])) {
|
|
7019
7393
|
flags.set(w, words[i + 1]);
|
|
@@ -7026,6 +7400,7 @@ function detectAgentcInvocation(command) {
|
|
|
7026
7400
|
const factorySlug = flags.get("--factory");
|
|
7027
7401
|
const withSlug = factorySlug ? { factorySlug } : {};
|
|
7028
7402
|
const uuid = (v) => v && UUID_RE.test(v) ? v : null;
|
|
7403
|
+
const drivePath = (v) => v && isPlausibleDrivePath(v) ? v : null;
|
|
7029
7404
|
if (args[0] === "display") {
|
|
7030
7405
|
switch (args[1]) {
|
|
7031
7406
|
case "run": {
|
|
@@ -7036,19 +7411,27 @@ function detectAgentcInvocation(command) {
|
|
|
7036
7411
|
const runId = uuid(args[2]);
|
|
7037
7412
|
return runId ? { verb: "display-changes", runId, ...withSlug } : null;
|
|
7038
7413
|
}
|
|
7039
|
-
case "document":
|
|
7040
|
-
|
|
7414
|
+
case "document": {
|
|
7415
|
+
const path = drivePath(args[2]);
|
|
7416
|
+
return path ? { verb: "display-document", path, ...withSlug } : null;
|
|
7417
|
+
}
|
|
7041
7418
|
case "plan":
|
|
7042
7419
|
return { verb: "display-plan" };
|
|
7043
|
-
case "image":
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
|
|
7420
|
+
case "image": {
|
|
7421
|
+
const path = drivePath(args[2]);
|
|
7422
|
+
return path ? { verb: "display-image", path, ...withSlug } : null;
|
|
7423
|
+
}
|
|
7424
|
+
case "preview": {
|
|
7425
|
+
const path = drivePath(args[2]);
|
|
7426
|
+
return path ? { verb: "display-preview", path } : null;
|
|
7427
|
+
}
|
|
7047
7428
|
case "table": {
|
|
7048
7429
|
const file = flags.get("--file");
|
|
7049
7430
|
const hasData = flags.has("--data");
|
|
7050
|
-
if (file && !hasData)
|
|
7051
|
-
|
|
7431
|
+
if (file && !hasData) {
|
|
7432
|
+
const path = drivePath(file);
|
|
7433
|
+
return path ? { verb: "display-table", source: "file", path } : null;
|
|
7434
|
+
}
|
|
7052
7435
|
if (hasData && !file)
|
|
7053
7436
|
return { verb: "display-table", source: "inline" };
|
|
7054
7437
|
return null;
|
|
@@ -7060,11 +7443,12 @@ function detectAgentcInvocation(command) {
|
|
|
7060
7443
|
case "diff": {
|
|
7061
7444
|
const from = flags.get("--from");
|
|
7062
7445
|
const to = flags.get("--to");
|
|
7063
|
-
|
|
7446
|
+
const path = drivePath(args[2]);
|
|
7447
|
+
if (!path || !from || !to)
|
|
7064
7448
|
return null;
|
|
7065
7449
|
if (!REVISION_SELECTOR_RE.test(from) || !REVISION_SELECTOR_RE.test(to))
|
|
7066
7450
|
return null;
|
|
7067
|
-
return { verb: "display-diff", path
|
|
7451
|
+
return { verb: "display-diff", path, from, to };
|
|
7068
7452
|
}
|
|
7069
7453
|
case "ask": {
|
|
7070
7454
|
const prompt = flags.get("--prompt");
|
|
@@ -7095,8 +7479,9 @@ function detectAgentcInvocation(command) {
|
|
|
7095
7479
|
const runId = uuid(args[2]);
|
|
7096
7480
|
return runId ? { verb: "run-get", runId } : null;
|
|
7097
7481
|
}
|
|
7098
|
-
if (args[0] === "files" && args[1] === "read"
|
|
7099
|
-
|
|
7482
|
+
if (args[0] === "files" && args[1] === "read") {
|
|
7483
|
+
const path = drivePath(args[2]);
|
|
7484
|
+
return path ? { verb: "files-read", path, ...withSlug } : null;
|
|
7100
7485
|
}
|
|
7101
7486
|
return null;
|
|
7102
7487
|
}
|
|
@@ -7335,10 +7720,12 @@ export {
|
|
|
7335
7720
|
shellWords,
|
|
7336
7721
|
serveStep,
|
|
7337
7722
|
serializeDisplayMarker,
|
|
7723
|
+
sandboxSizeLabel,
|
|
7338
7724
|
runWorkflowSteps,
|
|
7339
7725
|
runWorkflowSingleStep,
|
|
7340
7726
|
runWorkflow,
|
|
7341
7727
|
runProcessorChain,
|
|
7728
|
+
resolveSdkAlias,
|
|
7342
7729
|
requireScope,
|
|
7343
7730
|
requestPauseAndAwait,
|
|
7344
7731
|
requestContextPath,
|
|
@@ -7367,6 +7754,8 @@ export {
|
|
|
7367
7754
|
killSandboxById,
|
|
7368
7755
|
killAllSandboxes,
|
|
7369
7756
|
isWorkflow,
|
|
7757
|
+
isVercelSupportedSize,
|
|
7758
|
+
isPlausibleDrivePath,
|
|
7370
7759
|
isPlatformE2bTemplateAlias,
|
|
7371
7760
|
isE2bSupportedSize,
|
|
7372
7761
|
invokeStep,
|
|
@@ -7375,6 +7764,7 @@ export {
|
|
|
7375
7764
|
getSandboxQuotas,
|
|
7376
7765
|
formatError,
|
|
7377
7766
|
findDisplayMarker,
|
|
7767
|
+
explainBundleFailure,
|
|
7378
7768
|
editTool,
|
|
7379
7769
|
e2bMachineSpec,
|
|
7380
7770
|
e2bDevboxTemplateRef,
|
|
@@ -7431,6 +7821,7 @@ export {
|
|
|
7431
7821
|
WorkflowError,
|
|
7432
7822
|
Verdict,
|
|
7433
7823
|
VercelRunner,
|
|
7824
|
+
VERCEL_MEMORY_MB_PER_VCPU,
|
|
7434
7825
|
TMP_DIR,
|
|
7435
7826
|
TABLE_MAX_ROWS,
|
|
7436
7827
|
TABLE_MAX_COLUMNS,
|
|
@@ -7441,8 +7832,13 @@ export {
|
|
|
7441
7832
|
STEP_RESULT_PREFIX,
|
|
7442
7833
|
STEP_PAUSE_PREFIX,
|
|
7443
7834
|
STEP_ENV,
|
|
7835
|
+
SESSION_DEFAULT_SANDBOX_SIZE,
|
|
7836
|
+
SDK_SPECIFIER_ALIASES,
|
|
7837
|
+
SDK_PACKAGE,
|
|
7444
7838
|
SANDBOX_VCPUS,
|
|
7445
7839
|
SANDBOX_UNAVAILABLE_PREFIX,
|
|
7840
|
+
SANDBOX_SIZES,
|
|
7841
|
+
SANDBOX_MACHINES,
|
|
7446
7842
|
ReservedKeyError,
|
|
7447
7843
|
RequestContext,
|
|
7448
7844
|
RUNNER_COMMAND,
|
|
@@ -7459,9 +7855,12 @@ export {
|
|
|
7459
7855
|
LATEST_VERSION,
|
|
7460
7856
|
EngineError,
|
|
7461
7857
|
E2B_TEMPLATE_SIZES,
|
|
7858
|
+
E2B_MAX_VCPUS,
|
|
7859
|
+
E2B_MAX_MEMORY_MB,
|
|
7462
7860
|
E2B_DEVBOX_TEMPLATE,
|
|
7463
7861
|
E2B_DEVBOX_SPEC,
|
|
7464
7862
|
E2B_DEVBOX_RECIPE_VERSION,
|
|
7863
|
+
DRIVE_PATH_MAX_CHARS,
|
|
7465
7864
|
DISPLAY_MARKER_VERSION,
|
|
7466
7865
|
DISPLAY_MARKER_KEY,
|
|
7467
7866
|
DIRECTIVE_MARKER_SUFFIX,
|
|
@@ -7469,7 +7868,7 @@ export {
|
|
|
7469
7868
|
DEFAULT_SANDBOX_SIZE,
|
|
7470
7869
|
DEFAULT_CLAUDE_MODEL,
|
|
7471
7870
|
ClaudeRunner,
|
|
7472
|
-
|
|
7871
|
+
CLAUDE_CODE_EFFORT_LEVELS,
|
|
7473
7872
|
CLAUDE_CODE_ACP_ADAPTER,
|
|
7474
7873
|
CHART_MAX_SERIES,
|
|
7475
7874
|
CHART_MAX_POINTS_PER_SERIES,
|