@rivet-dev/agentos-core 0.2.13 → 0.2.15
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 +10 -199
- package/dist/agent-os.d.ts +308 -54
- package/dist/agent-os.js +1430 -83
- package/dist/agentos-package.d.ts +1 -1
- package/dist/agentos-package.js +1 -1
- package/dist/base-filesystem.js +2 -2
- package/dist/code-execution.d.ts +70 -0
- package/dist/code-execution.js +115 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -1
- package/dist/language-execution.d.ts +170 -0
- package/dist/language-execution.js +1 -0
- package/dist/options-schema.d.ts +12 -2
- package/dist/options-schema.js +9 -1
- package/dist/packages.d.ts +1 -1
- package/dist/runtime-compat.js +1 -1
- package/dist/sidecar/agentos-protocol.d.ts +5 -3
- package/dist/sidecar/agentos-protocol.js +2 -0
- package/dist/sidecar/rpc-client.js +10 -8
- package/dist/types.d.ts +4 -3
- package/package.json +11 -6
package/dist/agent-os.js
CHANGED
|
@@ -3,6 +3,8 @@ import { randomUUID } from "node:crypto";
|
|
|
3
3
|
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
4
4
|
import { join, posix as posixPath, resolve as resolveHostPath, } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
+
import * as executionProtocol from "@rivet-dev/agentos-runtime-core/protocol";
|
|
7
|
+
import { SidecarRejectedError } from "@rivet-dev/agentos-runtime-core/sidecar-errors";
|
|
6
8
|
import { validateBindings } from "./bindings.js";
|
|
7
9
|
import { zodToJsonSchema } from "./bindings-zod.js";
|
|
8
10
|
import { parseAgentOsOptions } from "./options-schema.js";
|
|
@@ -12,6 +14,7 @@ import { findCargoBinary, resolveCargoBinary } from "./sidecar/cargo.js";
|
|
|
12
14
|
const ACP_PROTOCOL_VERSION = 1;
|
|
13
15
|
const ACP_EXTENSION_NAMESPACE = "dev.rivet.agent-os.acp";
|
|
14
16
|
const SHELL_DISPOSE_TIMEOUT_MS = 5_000;
|
|
17
|
+
const PROCESS_OUTPUT_EVENT_LIMIT = 1_024;
|
|
15
18
|
function safeWireU64(value) {
|
|
16
19
|
const result = Number(value);
|
|
17
20
|
if (!Number.isSafeInteger(result) || result < 0) {
|
|
@@ -1428,12 +1431,212 @@ async function registerBindingsOnSidecar(client, session, vm, bindings) {
|
|
|
1428
1431
|
}
|
|
1429
1432
|
return buildBindingReference(bindings);
|
|
1430
1433
|
}
|
|
1434
|
+
function executionIdentity(options) {
|
|
1435
|
+
return {
|
|
1436
|
+
contextId: options.contextId ?? null,
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
1439
|
+
function executionOutput(options, background = false) {
|
|
1440
|
+
if (options.output?.retainEvents && !options.contextId && !background) {
|
|
1441
|
+
throw new Error("retainEvents is available only for spawned processes; use output.capture for attached runs");
|
|
1442
|
+
}
|
|
1443
|
+
const capture = options.output?.capture;
|
|
1444
|
+
return {
|
|
1445
|
+
capture: capture === "all"
|
|
1446
|
+
? executionProtocol.ExecutionOutputCapture.All
|
|
1447
|
+
: capture === "stderr"
|
|
1448
|
+
? executionProtocol.ExecutionOutputCapture.Stderr
|
|
1449
|
+
: capture === "none"
|
|
1450
|
+
? executionProtocol.ExecutionOutputCapture.None
|
|
1451
|
+
: null,
|
|
1452
|
+
retainEvents: options.output?.retainEvents ?? null,
|
|
1453
|
+
};
|
|
1454
|
+
}
|
|
1455
|
+
function executionBytes(data) {
|
|
1456
|
+
if (data === undefined)
|
|
1457
|
+
return null;
|
|
1458
|
+
const bytes = typeof data === "string" ? new TextEncoder().encode(data) : data;
|
|
1459
|
+
return bytes.slice().buffer;
|
|
1460
|
+
}
|
|
1461
|
+
function processExecutionOptions(options = {}, internal) {
|
|
1462
|
+
const background = internal?.background ?? false;
|
|
1463
|
+
return {
|
|
1464
|
+
identity: executionIdentity(options),
|
|
1465
|
+
output: executionOutput(options, background),
|
|
1466
|
+
operationId: internal?.executionId ?? null,
|
|
1467
|
+
background,
|
|
1468
|
+
cwd: options.cwd ?? null,
|
|
1469
|
+
env: options.env ? new Map(Object.entries(options.env)) : null,
|
|
1470
|
+
args: options.args ?? [],
|
|
1471
|
+
stdin: executionBytes(options.stdin),
|
|
1472
|
+
timeoutMs: protocolTimeout(options.timeoutMs),
|
|
1473
|
+
pty: options.pty
|
|
1474
|
+
? {
|
|
1475
|
+
cols: options.pty.cols === undefined
|
|
1476
|
+
? null
|
|
1477
|
+
: protocolUnsigned(options.pty.cols, 0xffff, "pty.cols"),
|
|
1478
|
+
rows: options.pty.rows === undefined
|
|
1479
|
+
? null
|
|
1480
|
+
: protocolUnsigned(options.pty.rows, 0xffff, "pty.rows"),
|
|
1481
|
+
}
|
|
1482
|
+
: null,
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
function protocolUnsigned(value, maximum, name) {
|
|
1486
|
+
if (!Number.isSafeInteger(value) || value < 0 || value > maximum) {
|
|
1487
|
+
throw new RangeError(`${name} must be an integer between 0 and ${maximum}`);
|
|
1488
|
+
}
|
|
1489
|
+
return value;
|
|
1490
|
+
}
|
|
1491
|
+
function protocolTimeout(value) {
|
|
1492
|
+
if (value === undefined)
|
|
1493
|
+
return null;
|
|
1494
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
1495
|
+
throw new RangeError("timeoutMs must be a non-negative safe integer");
|
|
1496
|
+
}
|
|
1497
|
+
return BigInt(value);
|
|
1498
|
+
}
|
|
1499
|
+
function safeProtocolNumber(value, name) {
|
|
1500
|
+
const result = Number(value);
|
|
1501
|
+
if (!Number.isSafeInteger(result)) {
|
|
1502
|
+
throw new RangeError(`${name} exceeds JavaScript's safe integer range`);
|
|
1503
|
+
}
|
|
1504
|
+
return result;
|
|
1505
|
+
}
|
|
1506
|
+
function mapExecutionState(state) {
|
|
1507
|
+
const mapped = state.toLowerCase();
|
|
1508
|
+
return (mapped === "creating" ? "idle" : mapped);
|
|
1509
|
+
}
|
|
1510
|
+
function mapExecutionOutcome(outcome) {
|
|
1511
|
+
return outcome === executionProtocol.ExecutionOutcome.TimedOut
|
|
1512
|
+
? "timed_out"
|
|
1513
|
+
: outcome.toLowerCase();
|
|
1514
|
+
}
|
|
1515
|
+
function mapExecutionDescriptor(descriptor) {
|
|
1516
|
+
return {
|
|
1517
|
+
contextId: descriptor.executionId,
|
|
1518
|
+
state: mapExecutionState(descriptor.state),
|
|
1519
|
+
...(descriptor.retainedLanguage
|
|
1520
|
+
? {
|
|
1521
|
+
language: descriptor.retainedLanguage ===
|
|
1522
|
+
executionProtocol.RetainedExecutionLanguage.JavaScript
|
|
1523
|
+
? "javascript"
|
|
1524
|
+
: "python",
|
|
1525
|
+
}
|
|
1526
|
+
: {}),
|
|
1527
|
+
createdAtMs: safeProtocolNumber(descriptor.createdAtMs, "createdAtMs"),
|
|
1528
|
+
...(descriptor.lastStartedAtMs !== null
|
|
1529
|
+
? {
|
|
1530
|
+
lastStartedAtMs: safeProtocolNumber(descriptor.lastStartedAtMs, "lastStartedAtMs"),
|
|
1531
|
+
}
|
|
1532
|
+
: {}),
|
|
1533
|
+
...(descriptor.lastCompletedAtMs !== null
|
|
1534
|
+
? {
|
|
1535
|
+
lastCompletedAtMs: safeProtocolNumber(descriptor.lastCompletedAtMs, "lastCompletedAtMs"),
|
|
1536
|
+
}
|
|
1537
|
+
: {}),
|
|
1538
|
+
};
|
|
1539
|
+
}
|
|
1540
|
+
function mapExecutionError(error) {
|
|
1541
|
+
if (!error)
|
|
1542
|
+
return undefined;
|
|
1543
|
+
return {
|
|
1544
|
+
code: error.code,
|
|
1545
|
+
name: error.name,
|
|
1546
|
+
message: error.message,
|
|
1547
|
+
...(error.stack ? { stack: error.stack } : {}),
|
|
1548
|
+
...(error.details
|
|
1549
|
+
? { details: JSON.parse(error.details) }
|
|
1550
|
+
: {}),
|
|
1551
|
+
};
|
|
1552
|
+
}
|
|
1553
|
+
function mapExecutionResult(result) {
|
|
1554
|
+
const base = {
|
|
1555
|
+
...(result.exitCode !== null ? { exitCode: result.exitCode } : {}),
|
|
1556
|
+
...(result.stdout !== null
|
|
1557
|
+
? { stdout: new TextDecoder().decode(result.stdout) }
|
|
1558
|
+
: {}),
|
|
1559
|
+
...(result.stderr !== null
|
|
1560
|
+
? { stderr: new TextDecoder().decode(result.stderr) }
|
|
1561
|
+
: {}),
|
|
1562
|
+
...(result.stdoutTruncated !== null
|
|
1563
|
+
? { stdoutTruncated: result.stdoutTruncated }
|
|
1564
|
+
: {}),
|
|
1565
|
+
...(result.stderrTruncated !== null
|
|
1566
|
+
? { stderrTruncated: result.stderrTruncated }
|
|
1567
|
+
: {}),
|
|
1568
|
+
...(result.evaluationValue
|
|
1569
|
+
? { evaluationValue: JSON.parse(result.evaluationValue) }
|
|
1570
|
+
: {}),
|
|
1571
|
+
...(result.typeScriptCheckResult
|
|
1572
|
+
? {
|
|
1573
|
+
typeScriptCheckResult: JSON.parse(result.typeScriptCheckResult),
|
|
1574
|
+
}
|
|
1575
|
+
: {}),
|
|
1576
|
+
};
|
|
1577
|
+
const outcome = mapExecutionOutcome(result.outcome);
|
|
1578
|
+
if (outcome === "succeeded") {
|
|
1579
|
+
return { ...base, outcome };
|
|
1580
|
+
}
|
|
1581
|
+
return {
|
|
1582
|
+
...base,
|
|
1583
|
+
outcome,
|
|
1584
|
+
error: mapExecutionError(result.error) ?? {
|
|
1585
|
+
code: "execution_failed",
|
|
1586
|
+
name: "ExecutionError",
|
|
1587
|
+
message: `execution completed with ${outcome}`,
|
|
1588
|
+
},
|
|
1589
|
+
};
|
|
1590
|
+
}
|
|
1591
|
+
function mapTypeScriptCheckResult(result) {
|
|
1592
|
+
if (result.outcome !== "succeeded") {
|
|
1593
|
+
return { ...result, diagnostics: [] };
|
|
1594
|
+
}
|
|
1595
|
+
const data = result.typeScriptCheckResult;
|
|
1596
|
+
if (!data || Array.isArray(data) || typeof data !== "object") {
|
|
1597
|
+
throw new Error("TypeScript checker returned no diagnostic result");
|
|
1598
|
+
}
|
|
1599
|
+
const hasErrors = data.hasErrors;
|
|
1600
|
+
const diagnostics = data.diagnostics;
|
|
1601
|
+
if (typeof hasErrors !== "boolean" || !Array.isArray(diagnostics)) {
|
|
1602
|
+
throw new Error("TypeScript checker returned an invalid diagnostic result");
|
|
1603
|
+
}
|
|
1604
|
+
return {
|
|
1605
|
+
...result,
|
|
1606
|
+
hasErrors,
|
|
1607
|
+
diagnostics: diagnostics,
|
|
1608
|
+
};
|
|
1609
|
+
}
|
|
1610
|
+
function mapExecutionOutputEvent(event) {
|
|
1611
|
+
return {
|
|
1612
|
+
executionId: event.executionId,
|
|
1613
|
+
generation: safeProtocolNumber(event.generation, "execution generation"),
|
|
1614
|
+
...(event.processId ? { processId: event.processId } : {}),
|
|
1615
|
+
sequence: safeProtocolNumber(event.sequence, "execution output sequence"),
|
|
1616
|
+
channel: event.channel.toLowerCase(),
|
|
1617
|
+
chunk: new Uint8Array(event.chunk),
|
|
1618
|
+
timestampMs: safeProtocolNumber(event.timestampMs, "execution timestamp"),
|
|
1619
|
+
};
|
|
1620
|
+
}
|
|
1621
|
+
function mapExecutionCompletedEvent(event) {
|
|
1622
|
+
return {
|
|
1623
|
+
executionId: event.executionId,
|
|
1624
|
+
generation: safeProtocolNumber(event.generation, "execution generation"),
|
|
1625
|
+
outcome: mapExecutionOutcome(event.outcome),
|
|
1626
|
+
...(event.exitCode !== null ? { exitCode: event.exitCode } : {}),
|
|
1627
|
+
...(event.error ? { error: mapExecutionError(event.error) } : {}),
|
|
1628
|
+
};
|
|
1629
|
+
}
|
|
1431
1630
|
export class AgentOs {
|
|
1432
1631
|
#kernel;
|
|
1433
1632
|
sidecar;
|
|
1434
1633
|
_durableSessionEventHandlers = new Map();
|
|
1435
1634
|
_agentExitHandlers = new Map();
|
|
1436
1635
|
_processes = new Map();
|
|
1636
|
+
_languageProcesses = new Map();
|
|
1637
|
+
_languageProcessIds = new Map();
|
|
1638
|
+
_executionOutputHandlers = new Map();
|
|
1639
|
+
_executionCompletedHandlers = new Map();
|
|
1437
1640
|
_shells = new Map();
|
|
1438
1641
|
// Value is the recorded exit code (undefined until/unless the exit
|
|
1439
1642
|
// resolves) so waitShell can still report it after the entry is dropped.
|
|
@@ -1459,6 +1662,119 @@ export class AgentOs {
|
|
|
1459
1662
|
_agentExitHandler;
|
|
1460
1663
|
_limitWarningHandler;
|
|
1461
1664
|
_disposeHooks = [];
|
|
1665
|
+
/**
|
|
1666
|
+
* Execute commands and manage child processes in the VM.
|
|
1667
|
+
*
|
|
1668
|
+
* `exec` evaluates a command with the configured shell, while `execFile`
|
|
1669
|
+
* invokes an executable directly with an argv array.
|
|
1670
|
+
*/
|
|
1671
|
+
process = {
|
|
1672
|
+
exec: this._exec.bind(this),
|
|
1673
|
+
execFile: this._execFile.bind(this),
|
|
1674
|
+
spawn: this._spawnProcess.bind(this),
|
|
1675
|
+
get: this._getProcess.bind(this),
|
|
1676
|
+
list: this._listProcesses.bind(this),
|
|
1677
|
+
tree: this._processTree.bind(this),
|
|
1678
|
+
wait: this._waitProcess.bind(this),
|
|
1679
|
+
signal: this._signalProcess.bind(this),
|
|
1680
|
+
kill: this._killProcess.bind(this),
|
|
1681
|
+
writeStdin: this._writeProcessStdin.bind(this),
|
|
1682
|
+
closeStdin: this._closeProcessStdin.bind(this),
|
|
1683
|
+
resizePty: this._resizeProcessPty.bind(this),
|
|
1684
|
+
readOutput: this._readProcessOutput.bind(this),
|
|
1685
|
+
};
|
|
1686
|
+
javascript = {
|
|
1687
|
+
execute: this._executeJavaScript.bind(this),
|
|
1688
|
+
evaluate: this._evaluateJavaScript.bind(this),
|
|
1689
|
+
executeFile: this._executeJavaScriptFile.bind(this),
|
|
1690
|
+
spawn: this._spawnJavaScript.bind(this),
|
|
1691
|
+
spawnFile: this._spawnJavaScriptFile.bind(this),
|
|
1692
|
+
npm: {
|
|
1693
|
+
install: this._installNpmPackages.bind(this),
|
|
1694
|
+
runScript: this._executeNpmScript.bind(this),
|
|
1695
|
+
runPackage: this._executeNpmPackage.bind(this),
|
|
1696
|
+
},
|
|
1697
|
+
};
|
|
1698
|
+
typescript = {
|
|
1699
|
+
execute: this._executeTypeScript.bind(this),
|
|
1700
|
+
evaluate: this._evaluateTypeScript.bind(this),
|
|
1701
|
+
executeFile: this._executeTypeScriptFile.bind(this),
|
|
1702
|
+
spawn: this._spawnTypeScript.bind(this),
|
|
1703
|
+
spawnFile: this._spawnTypeScriptFile.bind(this),
|
|
1704
|
+
check: this._checkTypeScript.bind(this),
|
|
1705
|
+
checkProject: this._checkTypeScriptProject.bind(this),
|
|
1706
|
+
};
|
|
1707
|
+
python = {
|
|
1708
|
+
execute: this._executePython.bind(this),
|
|
1709
|
+
evaluate: this._evaluatePython.bind(this),
|
|
1710
|
+
executeFile: this._executePythonFile.bind(this),
|
|
1711
|
+
executeModule: this._executePythonModule.bind(this),
|
|
1712
|
+
spawn: this._spawnPython.bind(this),
|
|
1713
|
+
spawnFile: this._spawnPythonFile.bind(this),
|
|
1714
|
+
spawnModule: this._spawnPythonModule.bind(this),
|
|
1715
|
+
install: this._installPythonPackages.bind(this),
|
|
1716
|
+
};
|
|
1717
|
+
contexts = {
|
|
1718
|
+
get: this._getContext.bind(this),
|
|
1719
|
+
list: this._listContexts.bind(this),
|
|
1720
|
+
reset: this._resetContext.bind(this),
|
|
1721
|
+
delete: this._deleteContext.bind(this),
|
|
1722
|
+
};
|
|
1723
|
+
terminal = {
|
|
1724
|
+
open: this._openTerminal.bind(this),
|
|
1725
|
+
write: this._writeTerminal.bind(this),
|
|
1726
|
+
resize: this._resizeTerminal.bind(this),
|
|
1727
|
+
wait: this._waitTerminal.bind(this),
|
|
1728
|
+
close: this._closeTerminal.bind(this),
|
|
1729
|
+
};
|
|
1730
|
+
filesystem = {
|
|
1731
|
+
readFile: this._readFile.bind(this),
|
|
1732
|
+
writeFile: this._writeFile.bind(this),
|
|
1733
|
+
readFiles: this._readFiles.bind(this),
|
|
1734
|
+
writeFiles: this._writeFiles.bind(this),
|
|
1735
|
+
stat: this._stat.bind(this),
|
|
1736
|
+
mkdir: this._mkdir.bind(this),
|
|
1737
|
+
readdir: this._readdir.bind(this),
|
|
1738
|
+
readdirEntries: this._readdirEntries.bind(this),
|
|
1739
|
+
readdirRecursive: this._readdirRecursive.bind(this),
|
|
1740
|
+
exists: this._exists.bind(this),
|
|
1741
|
+
move: this._move.bind(this),
|
|
1742
|
+
remove: this._remove.bind(this),
|
|
1743
|
+
export: this._exportRootFilesystem.bind(this),
|
|
1744
|
+
mount: this._mountFs.bind(this),
|
|
1745
|
+
unmount: this._unmountFs.bind(this),
|
|
1746
|
+
listMounts: this._listMounts.bind(this),
|
|
1747
|
+
};
|
|
1748
|
+
network = {
|
|
1749
|
+
httpRequest: this._httpRequest.bind(this),
|
|
1750
|
+
};
|
|
1751
|
+
software = {
|
|
1752
|
+
list: this._listSoftware.bind(this),
|
|
1753
|
+
link: this._linkSoftware.bind(this),
|
|
1754
|
+
};
|
|
1755
|
+
agents = {
|
|
1756
|
+
list: this._listAgents.bind(this),
|
|
1757
|
+
};
|
|
1758
|
+
sessions = {
|
|
1759
|
+
open: this._openSession.bind(this),
|
|
1760
|
+
get: this._getSession.bind(this),
|
|
1761
|
+
list: this._listSessions.bind(this),
|
|
1762
|
+
delete: this._deleteSession.bind(this),
|
|
1763
|
+
unload: this._unloadSession.bind(this),
|
|
1764
|
+
prompt: this._prompt.bind(this),
|
|
1765
|
+
cancelPrompt: this._cancelPrompt.bind(this),
|
|
1766
|
+
respondPermission: this._respondPermission.bind(this),
|
|
1767
|
+
readHistory: this._readHistory.bind(this),
|
|
1768
|
+
getConfig: this._getSessionConfig.bind(this),
|
|
1769
|
+
setConfigOption: this._setSessionConfigOption.bind(this),
|
|
1770
|
+
getCapabilities: this._getSessionCapabilities.bind(this),
|
|
1771
|
+
getAgentInfo: this._getSessionAgentInfo.bind(this),
|
|
1772
|
+
};
|
|
1773
|
+
cron = {
|
|
1774
|
+
schedule: this._scheduleCron.bind(this),
|
|
1775
|
+
list: this._listCronJobs.bind(this),
|
|
1776
|
+
cancel: this._cancelCronJob.bind(this),
|
|
1777
|
+
};
|
|
1462
1778
|
constructor(kernel, sidecar, softwareRoots, hostMounts, env, rootFilesystem, sidecarClient, sidecarSession, sidecarVm, agentStderrHandler, agentExitHandler, limitWarningHandler) {
|
|
1463
1779
|
this.#kernel = kernel;
|
|
1464
1780
|
this.sidecar = sidecar;
|
|
@@ -1761,18 +2077,673 @@ export class AgentOs {
|
|
|
1761
2077
|
throw error;
|
|
1762
2078
|
}
|
|
1763
2079
|
}
|
|
2080
|
+
async createContext(contextId) {
|
|
2081
|
+
const response = await this._sidecarClient.sendVmRequest(this._sidecarSession, this._sidecarVm, { type: "create_context", request: { contextId } });
|
|
2082
|
+
if (response.type !== "execution_descriptor") {
|
|
2083
|
+
throw new Error(`unexpected createContext response: ${response.type}`);
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
async _executionOperation(payload, options, background = false) {
|
|
2087
|
+
if (options.signal?.aborted) {
|
|
2088
|
+
throw options.signal.reason ?? new DOMException("Aborted", "AbortError");
|
|
2089
|
+
}
|
|
2090
|
+
let executionId;
|
|
2091
|
+
const earlyOutput = [];
|
|
2092
|
+
const completedBeforeAdmission = new Set();
|
|
2093
|
+
let resolveCompletion;
|
|
2094
|
+
const completion = new Promise((resolve) => {
|
|
2095
|
+
resolveCompletion = resolve;
|
|
2096
|
+
});
|
|
2097
|
+
let warnedAboutEarlyEvents = false;
|
|
2098
|
+
const warnAboutEarlyEvents = () => {
|
|
2099
|
+
if (warnedAboutEarlyEvents)
|
|
2100
|
+
return;
|
|
2101
|
+
warnedAboutEarlyEvents = true;
|
|
2102
|
+
console.warn("[agentos] execution admission received more than 1024 early events; use process.readOutput() to replay any omitted output");
|
|
2103
|
+
};
|
|
2104
|
+
const deliverOutput = (event) => {
|
|
2105
|
+
if (event.channel === "stderr")
|
|
2106
|
+
options.onStderr?.(event.chunk);
|
|
2107
|
+
else
|
|
2108
|
+
options.onStdout?.(event.chunk);
|
|
2109
|
+
};
|
|
2110
|
+
const unsubscribeOutput = this._onExecutionOutput("*", (event) => {
|
|
2111
|
+
if (executionId === undefined) {
|
|
2112
|
+
if (earlyOutput.length < 1_024)
|
|
2113
|
+
earlyOutput.push(event);
|
|
2114
|
+
else
|
|
2115
|
+
warnAboutEarlyEvents();
|
|
2116
|
+
return;
|
|
2117
|
+
}
|
|
2118
|
+
if (event.executionId === executionId)
|
|
2119
|
+
deliverOutput(event);
|
|
2120
|
+
});
|
|
2121
|
+
let completed = false;
|
|
2122
|
+
const unsubscribeCompleted = this._onExecutionCompleted("*", (event) => {
|
|
2123
|
+
if (executionId === undefined) {
|
|
2124
|
+
if (completedBeforeAdmission.size < 1_024) {
|
|
2125
|
+
completedBeforeAdmission.add(event.executionId);
|
|
2126
|
+
}
|
|
2127
|
+
else
|
|
2128
|
+
warnAboutEarlyEvents();
|
|
2129
|
+
return;
|
|
2130
|
+
}
|
|
2131
|
+
if (event.executionId === executionId) {
|
|
2132
|
+
completed = true;
|
|
2133
|
+
resolveCompletion?.();
|
|
2134
|
+
}
|
|
2135
|
+
});
|
|
2136
|
+
let response;
|
|
2137
|
+
try {
|
|
2138
|
+
response = await this._sidecarClient.sendVmRequest(this._sidecarSession, this._sidecarVm, payload);
|
|
2139
|
+
}
|
|
2140
|
+
catch (error) {
|
|
2141
|
+
unsubscribeOutput();
|
|
2142
|
+
unsubscribeCompleted();
|
|
2143
|
+
throw error;
|
|
2144
|
+
}
|
|
2145
|
+
if (response.type !== "execution_accepted") {
|
|
2146
|
+
unsubscribeOutput();
|
|
2147
|
+
unsubscribeCompleted();
|
|
2148
|
+
throw new Error(`unexpected execution response: ${response.type}`);
|
|
2149
|
+
}
|
|
2150
|
+
const descriptor = response.response.execution;
|
|
2151
|
+
executionId = response.response.operationId;
|
|
2152
|
+
for (const event of earlyOutput) {
|
|
2153
|
+
if (event.executionId === executionId)
|
|
2154
|
+
deliverOutput(event);
|
|
2155
|
+
}
|
|
2156
|
+
completed = completedBeforeAdmission.has(executionId);
|
|
2157
|
+
if (completed)
|
|
2158
|
+
resolveCompletion?.();
|
|
2159
|
+
let rejectAbort;
|
|
2160
|
+
const aborted = !background && options.signal
|
|
2161
|
+
? new Promise((_resolve, reject) => {
|
|
2162
|
+
rejectAbort = reject;
|
|
2163
|
+
})
|
|
2164
|
+
: undefined;
|
|
2165
|
+
const abort = () => {
|
|
2166
|
+
void this._cancelExecution(response.response.operationId).catch((error) => {
|
|
2167
|
+
console.error("[agentos] failed to cancel aborted execution", error);
|
|
2168
|
+
});
|
|
2169
|
+
rejectAbort?.(options.signal?.reason ?? new DOMException("Aborted", "AbortError"));
|
|
2170
|
+
};
|
|
2171
|
+
options.signal?.addEventListener("abort", abort, { once: true });
|
|
2172
|
+
if (options.signal?.aborted)
|
|
2173
|
+
abort();
|
|
2174
|
+
const cleanup = () => {
|
|
2175
|
+
unsubscribeOutput();
|
|
2176
|
+
unsubscribeCompleted();
|
|
2177
|
+
options.signal?.removeEventListener("abort", abort);
|
|
2178
|
+
};
|
|
2179
|
+
if (background) {
|
|
2180
|
+
if (!descriptor || descriptor.pid === null) {
|
|
2181
|
+
cleanup();
|
|
2182
|
+
throw new Error("spawned execution admission returned no process id");
|
|
2183
|
+
}
|
|
2184
|
+
if (completed)
|
|
2185
|
+
cleanup();
|
|
2186
|
+
else {
|
|
2187
|
+
const unsubscribeBackgroundCompletion = this._onExecutionCompleted(descriptor.executionId, () => {
|
|
2188
|
+
unsubscribeBackgroundCompletion();
|
|
2189
|
+
cleanup();
|
|
2190
|
+
});
|
|
2191
|
+
}
|
|
2192
|
+
return {
|
|
2193
|
+
executionId: descriptor.executionId,
|
|
2194
|
+
pid: descriptor.pid,
|
|
2195
|
+
completed,
|
|
2196
|
+
completion,
|
|
2197
|
+
createdAtMs: safeProtocolNumber(descriptor.createdAtMs, "process startedAtMs"),
|
|
2198
|
+
};
|
|
2199
|
+
}
|
|
2200
|
+
try {
|
|
2201
|
+
await (aborted ? Promise.race([completion, aborted]) : completion);
|
|
2202
|
+
return await this._waitExecutionResult(response.response.operationId);
|
|
2203
|
+
}
|
|
2204
|
+
finally {
|
|
2205
|
+
cleanup();
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
async _spawnLanguageOperation(buildPayload, options, language) {
|
|
2209
|
+
if ("contextId" in options) {
|
|
2210
|
+
throw new Error("contextId is not supported for spawned processes; contexts are for attached operations");
|
|
2211
|
+
}
|
|
2212
|
+
const executionId = `process-${randomUUID()}`;
|
|
2213
|
+
const internalOptions = {
|
|
2214
|
+
...options,
|
|
2215
|
+
output: options.output,
|
|
2216
|
+
};
|
|
2217
|
+
const admitted = (await this._executionOperation(buildPayload(internalOptions, executionId), internalOptions, true));
|
|
2218
|
+
const descriptor = {
|
|
2219
|
+
pid: admitted.pid,
|
|
2220
|
+
state: "running",
|
|
2221
|
+
language,
|
|
2222
|
+
startedAtMs: admitted.createdAtMs,
|
|
2223
|
+
};
|
|
2224
|
+
this._languageProcesses.set(admitted.pid, {
|
|
2225
|
+
executionId: admitted.executionId,
|
|
2226
|
+
descriptor,
|
|
2227
|
+
outputHandlers: new Set(),
|
|
2228
|
+
exitHandlers: new Set(),
|
|
2229
|
+
});
|
|
2230
|
+
this._languageProcessIds.set(admitted.executionId, admitted.pid);
|
|
2231
|
+
const reconcileCompletion = async () => {
|
|
2232
|
+
if (!this._languageProcesses.get(admitted.pid)?.exit) {
|
|
2233
|
+
await this._waitProcess(admitted.pid);
|
|
2234
|
+
}
|
|
2235
|
+
};
|
|
2236
|
+
if (admitted.completed)
|
|
2237
|
+
await reconcileCompletion();
|
|
2238
|
+
else {
|
|
2239
|
+
void admitted.completion
|
|
2240
|
+
.then(reconcileCompletion)
|
|
2241
|
+
.catch((error) => console.error("[agentos] failed to reconcile spawned process completion", error));
|
|
2242
|
+
}
|
|
2243
|
+
if (options.signal) {
|
|
2244
|
+
const abort = () => {
|
|
2245
|
+
void this._killProcess(admitted.pid);
|
|
2246
|
+
};
|
|
2247
|
+
options.signal.addEventListener("abort", abort, { once: true });
|
|
2248
|
+
}
|
|
2249
|
+
return this._languageProcesses.get(admitted.pid)?.descriptor ?? descriptor;
|
|
2250
|
+
}
|
|
2251
|
+
async _exec(command, options = {}) {
|
|
2252
|
+
return (await this._executionOperation({
|
|
2253
|
+
type: "shell_execution",
|
|
2254
|
+
request: { process: processExecutionOptions(options), command },
|
|
2255
|
+
}, options));
|
|
2256
|
+
}
|
|
2257
|
+
async _execFile(command, args = [], options = {}) {
|
|
2258
|
+
const operationOptions = { ...options, args: [...args] };
|
|
2259
|
+
return (await this._executionOperation({
|
|
2260
|
+
type: "argv_execution",
|
|
2261
|
+
request: {
|
|
2262
|
+
process: processExecutionOptions(operationOptions),
|
|
2263
|
+
command,
|
|
2264
|
+
},
|
|
2265
|
+
}, operationOptions));
|
|
2266
|
+
}
|
|
2267
|
+
/** @deprecated Use `process.exec()` for lifecycle-aware execution. */
|
|
1764
2268
|
async exec(command, options) {
|
|
1765
2269
|
return this.#kernel.exec(command, options);
|
|
1766
2270
|
}
|
|
2271
|
+
/** @deprecated Use `process.execFile()` for lifecycle-aware execution. */
|
|
1767
2272
|
async execArgv(command, args = [], options) {
|
|
1768
2273
|
const kernel = this.#kernel;
|
|
1769
2274
|
return kernel.execArgv(command, args, options);
|
|
1770
2275
|
}
|
|
1771
|
-
|
|
2276
|
+
async _executeJavaScript(source, options = {}) {
|
|
2277
|
+
return (await this._executionOperation({
|
|
2278
|
+
type: "javascript_execution",
|
|
2279
|
+
request: {
|
|
2280
|
+
process: processExecutionOptions(options),
|
|
2281
|
+
source,
|
|
2282
|
+
format: options.format === "commonjs"
|
|
2283
|
+
? executionProtocol.JavaScriptModuleFormat.CommonJs
|
|
2284
|
+
: executionProtocol.JavaScriptModuleFormat.Module,
|
|
2285
|
+
filePath: options.filePath ?? null,
|
|
2286
|
+
inputs: options.inputs ? JSON.stringify(options.inputs) : null,
|
|
2287
|
+
},
|
|
2288
|
+
}, options));
|
|
2289
|
+
}
|
|
2290
|
+
async _evaluationOperation(payload, options) {
|
|
2291
|
+
const result = (await this._executionOperation(payload, options));
|
|
2292
|
+
if (result.outcome !== "succeeded")
|
|
2293
|
+
return result;
|
|
2294
|
+
const value = result.evaluationValue;
|
|
2295
|
+
if (value === undefined) {
|
|
2296
|
+
throw new Error("successful evaluation did not return a JSON-serializable value");
|
|
2297
|
+
}
|
|
2298
|
+
return { ...result, value: value };
|
|
2299
|
+
}
|
|
2300
|
+
async _evaluateJavaScript(expression, options = {}) {
|
|
2301
|
+
return this._evaluationOperation({
|
|
2302
|
+
type: "javascript_evaluation",
|
|
2303
|
+
request: {
|
|
2304
|
+
process: processExecutionOptions(options),
|
|
2305
|
+
expression,
|
|
2306
|
+
format: options.format === "commonjs"
|
|
2307
|
+
? executionProtocol.JavaScriptModuleFormat.CommonJs
|
|
2308
|
+
: executionProtocol.JavaScriptModuleFormat.Module,
|
|
2309
|
+
filePath: options.filePath ?? null,
|
|
2310
|
+
inputs: options.inputs ? JSON.stringify(options.inputs) : null,
|
|
2311
|
+
},
|
|
2312
|
+
}, options);
|
|
2313
|
+
}
|
|
2314
|
+
async _executeJavaScriptFile(path, options = {}) {
|
|
2315
|
+
return (await this._executionOperation({
|
|
2316
|
+
type: "javascript_file_execution",
|
|
2317
|
+
request: { process: processExecutionOptions(options), path },
|
|
2318
|
+
}, options));
|
|
2319
|
+
}
|
|
2320
|
+
_spawnJavaScript(source, options = {}) {
|
|
2321
|
+
return this._spawnLanguageOperation((operationOptions, executionId) => ({
|
|
2322
|
+
type: "javascript_execution",
|
|
2323
|
+
request: {
|
|
2324
|
+
process: processExecutionOptions(operationOptions, {
|
|
2325
|
+
background: true,
|
|
2326
|
+
executionId,
|
|
2327
|
+
}),
|
|
2328
|
+
source,
|
|
2329
|
+
format: executionProtocol.JavaScriptModuleFormat.Module,
|
|
2330
|
+
filePath: null,
|
|
2331
|
+
inputs: null,
|
|
2332
|
+
},
|
|
2333
|
+
}), options, "javascript");
|
|
2334
|
+
}
|
|
2335
|
+
_spawnJavaScriptFile(path, options = {}) {
|
|
2336
|
+
return this._spawnLanguageOperation((operationOptions, executionId) => ({
|
|
2337
|
+
type: "javascript_file_execution",
|
|
2338
|
+
request: {
|
|
2339
|
+
process: processExecutionOptions(operationOptions, {
|
|
2340
|
+
background: true,
|
|
2341
|
+
executionId,
|
|
2342
|
+
}),
|
|
2343
|
+
path,
|
|
2344
|
+
},
|
|
2345
|
+
}), options, "javascript");
|
|
2346
|
+
}
|
|
2347
|
+
async _executeTypeScript(source, options = {}) {
|
|
2348
|
+
return (await this._executionOperation({
|
|
2349
|
+
type: "typescript_execution",
|
|
2350
|
+
request: {
|
|
2351
|
+
process: processExecutionOptions(options),
|
|
2352
|
+
source,
|
|
2353
|
+
filePath: options.filePath ?? null,
|
|
2354
|
+
tsconfigPath: options.tsconfigPath ?? null,
|
|
2355
|
+
compilerOptions: options.compilerOptions
|
|
2356
|
+
? JSON.stringify(options.compilerOptions)
|
|
2357
|
+
: null,
|
|
2358
|
+
inputs: options.inputs ? JSON.stringify(options.inputs) : null,
|
|
2359
|
+
},
|
|
2360
|
+
}, options));
|
|
2361
|
+
}
|
|
2362
|
+
async _evaluateTypeScript(expression, options = {}) {
|
|
2363
|
+
return this._evaluationOperation({
|
|
2364
|
+
type: "typescript_evaluation",
|
|
2365
|
+
request: {
|
|
2366
|
+
process: processExecutionOptions(options),
|
|
2367
|
+
expression,
|
|
2368
|
+
filePath: options.filePath ?? null,
|
|
2369
|
+
tsconfigPath: options.tsconfigPath ?? null,
|
|
2370
|
+
compilerOptions: options.compilerOptions
|
|
2371
|
+
? JSON.stringify(options.compilerOptions)
|
|
2372
|
+
: null,
|
|
2373
|
+
inputs: options.inputs ? JSON.stringify(options.inputs) : null,
|
|
2374
|
+
},
|
|
2375
|
+
}, options);
|
|
2376
|
+
}
|
|
2377
|
+
async _executeTypeScriptFile(path, options = {}) {
|
|
2378
|
+
return (await this._executionOperation({
|
|
2379
|
+
type: "typescript_file_execution",
|
|
2380
|
+
request: {
|
|
2381
|
+
process: processExecutionOptions(options),
|
|
2382
|
+
path,
|
|
2383
|
+
tsconfigPath: options.tsconfigPath ?? null,
|
|
2384
|
+
compilerOptions: options.compilerOptions
|
|
2385
|
+
? JSON.stringify(options.compilerOptions)
|
|
2386
|
+
: null,
|
|
2387
|
+
},
|
|
2388
|
+
}, options));
|
|
2389
|
+
}
|
|
2390
|
+
_spawnTypeScript(source, options = {}) {
|
|
2391
|
+
return this._spawnLanguageOperation((operationOptions, executionId) => ({
|
|
2392
|
+
type: "typescript_execution",
|
|
2393
|
+
request: {
|
|
2394
|
+
process: processExecutionOptions(operationOptions, {
|
|
2395
|
+
background: true,
|
|
2396
|
+
executionId,
|
|
2397
|
+
}),
|
|
2398
|
+
source,
|
|
2399
|
+
filePath: null,
|
|
2400
|
+
tsconfigPath: null,
|
|
2401
|
+
compilerOptions: null,
|
|
2402
|
+
inputs: null,
|
|
2403
|
+
},
|
|
2404
|
+
}), options, "javascript");
|
|
2405
|
+
}
|
|
2406
|
+
_spawnTypeScriptFile(path, options = {}) {
|
|
2407
|
+
return this._spawnLanguageOperation((operationOptions, executionId) => ({
|
|
2408
|
+
type: "typescript_file_execution",
|
|
2409
|
+
request: {
|
|
2410
|
+
process: processExecutionOptions(operationOptions, {
|
|
2411
|
+
background: true,
|
|
2412
|
+
executionId,
|
|
2413
|
+
}),
|
|
2414
|
+
path,
|
|
2415
|
+
tsconfigPath: null,
|
|
2416
|
+
compilerOptions: null,
|
|
2417
|
+
},
|
|
2418
|
+
}), options, "javascript");
|
|
2419
|
+
}
|
|
2420
|
+
async _checkTypeScript(source, options = {}) {
|
|
2421
|
+
const result = (await this._executionOperation({
|
|
2422
|
+
type: "typescript_check",
|
|
2423
|
+
request: {
|
|
2424
|
+
identity: executionIdentity(options),
|
|
2425
|
+
output: executionOutput(options),
|
|
2426
|
+
source,
|
|
2427
|
+
cwd: options.cwd ?? null,
|
|
2428
|
+
filePath: options.filePath ?? null,
|
|
2429
|
+
tsconfigPath: options.tsconfigPath ?? null,
|
|
2430
|
+
compilerOptions: options.compilerOptions
|
|
2431
|
+
? JSON.stringify(options.compilerOptions)
|
|
2432
|
+
: null,
|
|
2433
|
+
timeoutMs: protocolTimeout(options.timeoutMs),
|
|
2434
|
+
},
|
|
2435
|
+
}, options));
|
|
2436
|
+
return mapTypeScriptCheckResult(result);
|
|
2437
|
+
}
|
|
2438
|
+
async _checkTypeScriptProject(options = {}) {
|
|
2439
|
+
const result = (await this._executionOperation({
|
|
2440
|
+
type: "typescript_project_check",
|
|
2441
|
+
request: {
|
|
2442
|
+
identity: executionIdentity(options),
|
|
2443
|
+
output: executionOutput(options),
|
|
2444
|
+
cwd: options.cwd ?? null,
|
|
2445
|
+
tsconfigPath: options.tsconfigPath ?? null,
|
|
2446
|
+
timeoutMs: protocolTimeout(options.timeoutMs),
|
|
2447
|
+
},
|
|
2448
|
+
}, options));
|
|
2449
|
+
return mapTypeScriptCheckResult(result);
|
|
2450
|
+
}
|
|
2451
|
+
async _installNpmPackages(packagesOrOptions = {}, maybeOptions = {}) {
|
|
2452
|
+
if (typeof packagesOrOptions === "string" ||
|
|
2453
|
+
Array.isArray(packagesOrOptions)) {
|
|
2454
|
+
const packages = Array.isArray(packagesOrOptions)
|
|
2455
|
+
? packagesOrOptions
|
|
2456
|
+
: [packagesOrOptions];
|
|
2457
|
+
return (await this._executionOperation({
|
|
2458
|
+
type: "npm_package_install",
|
|
2459
|
+
request: {
|
|
2460
|
+
identity: executionIdentity(maybeOptions),
|
|
2461
|
+
output: executionOutput(maybeOptions),
|
|
2462
|
+
cwd: maybeOptions.cwd ?? null,
|
|
2463
|
+
env: maybeOptions.env
|
|
2464
|
+
? new Map(Object.entries(maybeOptions.env))
|
|
2465
|
+
: null,
|
|
2466
|
+
timeoutMs: protocolTimeout(maybeOptions.timeoutMs),
|
|
2467
|
+
packages,
|
|
2468
|
+
dev: maybeOptions.dev ?? null,
|
|
2469
|
+
global: maybeOptions.global ?? null,
|
|
2470
|
+
},
|
|
2471
|
+
}, maybeOptions));
|
|
2472
|
+
}
|
|
2473
|
+
const options = packagesOrOptions;
|
|
2474
|
+
return (await this._executionOperation({
|
|
2475
|
+
type: "npm_project_install",
|
|
2476
|
+
request: {
|
|
2477
|
+
identity: executionIdentity(options),
|
|
2478
|
+
output: executionOutput(options),
|
|
2479
|
+
cwd: options.cwd ?? null,
|
|
2480
|
+
env: options.env ? new Map(Object.entries(options.env)) : null,
|
|
2481
|
+
timeoutMs: protocolTimeout(options.timeoutMs),
|
|
2482
|
+
frozen: options.frozen ?? null,
|
|
2483
|
+
},
|
|
2484
|
+
}, options));
|
|
2485
|
+
}
|
|
2486
|
+
async _executeNpmScript(script, options = {}) {
|
|
2487
|
+
return (await this._executionOperation({
|
|
2488
|
+
type: "npm_script_execution",
|
|
2489
|
+
request: { process: processExecutionOptions(options), script },
|
|
2490
|
+
}, options));
|
|
2491
|
+
}
|
|
2492
|
+
async _executeNpmPackage(packageSpec, options = {}) {
|
|
2493
|
+
return (await this._executionOperation({
|
|
2494
|
+
type: "npm_package_execution",
|
|
2495
|
+
request: {
|
|
2496
|
+
process: processExecutionOptions(options),
|
|
2497
|
+
packageSpec,
|
|
2498
|
+
binary: options.binary ?? null,
|
|
2499
|
+
},
|
|
2500
|
+
}, options));
|
|
2501
|
+
}
|
|
2502
|
+
async _executePython(source, options = {}) {
|
|
2503
|
+
return (await this._executionOperation({
|
|
2504
|
+
type: "python_execution",
|
|
2505
|
+
request: {
|
|
2506
|
+
process: processExecutionOptions(options),
|
|
2507
|
+
source,
|
|
2508
|
+
inputs: options.inputs ? JSON.stringify(options.inputs) : null,
|
|
2509
|
+
},
|
|
2510
|
+
}, options));
|
|
2511
|
+
}
|
|
2512
|
+
async _evaluatePython(expression, options = {}) {
|
|
2513
|
+
return this._evaluationOperation({
|
|
2514
|
+
type: "python_evaluation",
|
|
2515
|
+
request: {
|
|
2516
|
+
process: processExecutionOptions(options),
|
|
2517
|
+
expression,
|
|
2518
|
+
inputs: options.inputs ? JSON.stringify(options.inputs) : null,
|
|
2519
|
+
},
|
|
2520
|
+
}, options);
|
|
2521
|
+
}
|
|
2522
|
+
async _executePythonFile(path, options = {}) {
|
|
2523
|
+
return (await this._executionOperation({
|
|
2524
|
+
type: "python_file_execution",
|
|
2525
|
+
request: { process: processExecutionOptions(options), path },
|
|
2526
|
+
}, options));
|
|
2527
|
+
}
|
|
2528
|
+
async _executePythonModule(module, options = {}) {
|
|
2529
|
+
return (await this._executionOperation({
|
|
2530
|
+
type: "python_module_execution",
|
|
2531
|
+
request: { process: processExecutionOptions(options), module },
|
|
2532
|
+
}, options));
|
|
2533
|
+
}
|
|
2534
|
+
_spawnPython(source, options = {}) {
|
|
2535
|
+
return this._spawnLanguageOperation((operationOptions, executionId) => ({
|
|
2536
|
+
type: "python_execution",
|
|
2537
|
+
request: {
|
|
2538
|
+
process: processExecutionOptions(operationOptions, {
|
|
2539
|
+
background: true,
|
|
2540
|
+
executionId,
|
|
2541
|
+
}),
|
|
2542
|
+
source,
|
|
2543
|
+
inputs: null,
|
|
2544
|
+
},
|
|
2545
|
+
}), options, "python");
|
|
2546
|
+
}
|
|
2547
|
+
_spawnPythonFile(path, options = {}) {
|
|
2548
|
+
return this._spawnLanguageOperation((operationOptions, executionId) => ({
|
|
2549
|
+
type: "python_file_execution",
|
|
2550
|
+
request: {
|
|
2551
|
+
process: processExecutionOptions(operationOptions, {
|
|
2552
|
+
background: true,
|
|
2553
|
+
executionId,
|
|
2554
|
+
}),
|
|
2555
|
+
path,
|
|
2556
|
+
},
|
|
2557
|
+
}), options, "python");
|
|
2558
|
+
}
|
|
2559
|
+
_spawnPythonModule(module, options = {}) {
|
|
2560
|
+
return this._spawnLanguageOperation((operationOptions, executionId) => ({
|
|
2561
|
+
type: "python_module_execution",
|
|
2562
|
+
request: {
|
|
2563
|
+
process: processExecutionOptions(operationOptions, {
|
|
2564
|
+
background: true,
|
|
2565
|
+
executionId,
|
|
2566
|
+
}),
|
|
2567
|
+
module,
|
|
2568
|
+
},
|
|
2569
|
+
}), options, "python");
|
|
2570
|
+
}
|
|
2571
|
+
async _installPythonPackages(packagesOrOptions = {}, maybeOptions = {}) {
|
|
2572
|
+
const packages = typeof packagesOrOptions === "string"
|
|
2573
|
+
? [packagesOrOptions]
|
|
2574
|
+
: Array.isArray(packagesOrOptions)
|
|
2575
|
+
? packagesOrOptions
|
|
2576
|
+
: [];
|
|
2577
|
+
const options = typeof packagesOrOptions === "string" || Array.isArray(packagesOrOptions)
|
|
2578
|
+
? maybeOptions
|
|
2579
|
+
: packagesOrOptions;
|
|
2580
|
+
return (await this._executionOperation({
|
|
2581
|
+
type: "python_install",
|
|
2582
|
+
request: {
|
|
2583
|
+
identity: executionIdentity(options),
|
|
2584
|
+
output: executionOutput(options),
|
|
2585
|
+
cwd: options.cwd ?? null,
|
|
2586
|
+
env: options.env ? new Map(Object.entries(options.env)) : null,
|
|
2587
|
+
timeoutMs: protocolTimeout(options.timeoutMs),
|
|
2588
|
+
packages,
|
|
2589
|
+
upgrade: options.upgrade ?? null,
|
|
2590
|
+
requirementsFile: options.requirementsFile ?? null,
|
|
2591
|
+
indexUrl: options.indexUrl ?? null,
|
|
2592
|
+
extraIndexUrls: options.extraIndexUrls ?? [],
|
|
2593
|
+
},
|
|
2594
|
+
}, options));
|
|
2595
|
+
}
|
|
2596
|
+
async _getContext(contextId) {
|
|
2597
|
+
const response = await this._sidecarClient.sendVmRequest(this._sidecarSession, this._sidecarVm, { type: "get_execution", request: { executionId: contextId } });
|
|
2598
|
+
if (response.type !== "execution_descriptor") {
|
|
2599
|
+
throw new Error(`unexpected getContext response: ${response.type}`);
|
|
2600
|
+
}
|
|
2601
|
+
return mapExecutionDescriptor(response.response.execution);
|
|
2602
|
+
}
|
|
2603
|
+
async _listContexts() {
|
|
2604
|
+
const response = await this._sidecarClient.sendVmRequest(this._sidecarSession, this._sidecarVm, { type: "list_executions" });
|
|
2605
|
+
if (response.type !== "execution_list") {
|
|
2606
|
+
throw new Error(`unexpected listContexts response: ${response.type}`);
|
|
2607
|
+
}
|
|
2608
|
+
return response.response.executions.map(mapExecutionDescriptor);
|
|
2609
|
+
}
|
|
2610
|
+
async _waitExecutionResult(executionId) {
|
|
2611
|
+
let resolveCompletion;
|
|
2612
|
+
const completion = new Promise((resolve) => {
|
|
2613
|
+
resolveCompletion = resolve;
|
|
2614
|
+
});
|
|
2615
|
+
const unsubscribe = this._onExecutionCompleted(executionId, () => {
|
|
2616
|
+
resolveCompletion?.();
|
|
2617
|
+
});
|
|
2618
|
+
let response;
|
|
2619
|
+
try {
|
|
2620
|
+
try {
|
|
2621
|
+
response = await this._sidecarClient.sendVmRequest(this._sidecarSession, this._sidecarVm, { type: "wait_execution", request: { executionId } });
|
|
2622
|
+
}
|
|
2623
|
+
catch (error) {
|
|
2624
|
+
if (!(error instanceof SidecarRejectedError) ||
|
|
2625
|
+
error.detail.code !== "execution_busy") {
|
|
2626
|
+
throw error;
|
|
2627
|
+
}
|
|
2628
|
+
await completion;
|
|
2629
|
+
response = await this._sidecarClient.sendVmRequest(this._sidecarSession, this._sidecarVm, { type: "wait_execution", request: { executionId } });
|
|
2630
|
+
}
|
|
2631
|
+
}
|
|
2632
|
+
finally {
|
|
2633
|
+
unsubscribe();
|
|
2634
|
+
}
|
|
2635
|
+
if (response.type !== "execution_completed") {
|
|
2636
|
+
throw new Error(`unexpected waitExecution response: ${response.type}`);
|
|
2637
|
+
}
|
|
2638
|
+
return mapExecutionResult(response.response);
|
|
2639
|
+
}
|
|
2640
|
+
async _cancelExecution(executionId) {
|
|
2641
|
+
await this._descriptorLifecycleRequest("cancel_execution", executionId);
|
|
2642
|
+
}
|
|
2643
|
+
async _signalExecution(executionId, signal) {
|
|
2644
|
+
const response = await this._sidecarClient.sendVmRequest(this._sidecarSession, this._sidecarVm, { type: "signal_execution", request: { executionId, signal } });
|
|
2645
|
+
if (response.type !== "execution_descriptor") {
|
|
2646
|
+
throw new Error(`unexpected signalExecution response: ${response.type}`);
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
async _resetContext(contextId) {
|
|
2650
|
+
await this._descriptorLifecycleRequest("reset_execution", contextId);
|
|
2651
|
+
}
|
|
2652
|
+
async _descriptorLifecycleRequest(type, executionId) {
|
|
2653
|
+
const payload = type === "cancel_execution"
|
|
2654
|
+
? { type, request: { executionId } }
|
|
2655
|
+
: { type, request: { executionId } };
|
|
2656
|
+
const response = await this._sidecarClient.sendVmRequest(this._sidecarSession, this._sidecarVm, payload);
|
|
2657
|
+
if (response.type !== "execution_descriptor") {
|
|
2658
|
+
throw new Error(`unexpected ${type} response: ${response.type}`);
|
|
2659
|
+
}
|
|
2660
|
+
return mapExecutionDescriptor(response.response.execution);
|
|
2661
|
+
}
|
|
2662
|
+
async _deleteContext(contextId) {
|
|
2663
|
+
const response = await this._sidecarClient.sendVmRequest(this._sidecarSession, this._sidecarVm, { type: "delete_execution", request: { executionId: contextId } });
|
|
2664
|
+
if (response.type !== "execution_deleted") {
|
|
2665
|
+
throw new Error(`unexpected deleteContext response: ${response.type}`);
|
|
2666
|
+
}
|
|
2667
|
+
}
|
|
2668
|
+
async _writeExecutionStdin(executionId, data) {
|
|
2669
|
+
const response = await this._sidecarClient.sendVmRequest(this._sidecarSession, this._sidecarVm, {
|
|
2670
|
+
type: "write_execution_stdin",
|
|
2671
|
+
request: { executionId, chunk: executionBytes(data) },
|
|
2672
|
+
});
|
|
2673
|
+
if (response.type !== "execution_io") {
|
|
2674
|
+
throw new Error(`unexpected writeExecutionStdin response: ${response.type}`);
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
async _closeExecutionStdin(executionId) {
|
|
2678
|
+
const response = await this._sidecarClient.sendVmRequest(this._sidecarSession, this._sidecarVm, { type: "close_execution_stdin", request: { executionId } });
|
|
2679
|
+
if (response.type !== "execution_io") {
|
|
2680
|
+
throw new Error(`unexpected closeExecutionStdin response: ${response.type}`);
|
|
2681
|
+
}
|
|
2682
|
+
}
|
|
2683
|
+
async _resizeExecutionPty(executionId, size) {
|
|
2684
|
+
const cols = protocolUnsigned(size.cols, 0xffff, "size.cols");
|
|
2685
|
+
const rows = protocolUnsigned(size.rows, 0xffff, "size.rows");
|
|
2686
|
+
const response = await this._sidecarClient.sendVmRequest(this._sidecarSession, this._sidecarVm, {
|
|
2687
|
+
type: "resize_execution_pty",
|
|
2688
|
+
request: { executionId, cols, rows },
|
|
2689
|
+
});
|
|
2690
|
+
if (response.type !== "execution_io") {
|
|
2691
|
+
throw new Error(`unexpected resizeExecutionPty response: ${response.type}`);
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
async _readExecutionOutput(executionId, options = {}) {
|
|
2695
|
+
const limit = options.limit === undefined
|
|
2696
|
+
? null
|
|
2697
|
+
: protocolUnsigned(options.limit, 0xffff_ffff, "limit");
|
|
2698
|
+
const response = await this._sidecarClient.sendVmRequest(this._sidecarSession, this._sidecarVm, {
|
|
2699
|
+
type: "read_execution_output",
|
|
2700
|
+
request: {
|
|
2701
|
+
executionId,
|
|
2702
|
+
cursor: options.cursor ?? null,
|
|
2703
|
+
limit,
|
|
2704
|
+
},
|
|
2705
|
+
});
|
|
2706
|
+
if (response.type !== "execution_output_page") {
|
|
2707
|
+
throw new Error(`unexpected readExecutionOutput response: ${response.type}`);
|
|
2708
|
+
}
|
|
2709
|
+
return {
|
|
2710
|
+
events: response.response.events.map(mapExecutionOutputEvent),
|
|
2711
|
+
nextCursor: response.response.nextCursor,
|
|
2712
|
+
hasMore: response.response.hasMore,
|
|
2713
|
+
truncated: response.response.truncated,
|
|
2714
|
+
};
|
|
2715
|
+
}
|
|
2716
|
+
_onExecutionOutput(executionId, handler) {
|
|
2717
|
+
const handlers = this._executionOutputHandlers.get(executionId) ?? new Set();
|
|
2718
|
+
handlers.add(handler);
|
|
2719
|
+
this._executionOutputHandlers.set(executionId, handlers);
|
|
2720
|
+
return () => {
|
|
2721
|
+
handlers.delete(handler);
|
|
2722
|
+
if (handlers.size === 0)
|
|
2723
|
+
this._executionOutputHandlers.delete(executionId);
|
|
2724
|
+
};
|
|
2725
|
+
}
|
|
2726
|
+
_onExecutionCompleted(executionId, handler) {
|
|
2727
|
+
const handlers = this._executionCompletedHandlers.get(executionId) ?? new Set();
|
|
2728
|
+
handlers.add(handler);
|
|
2729
|
+
this._executionCompletedHandlers.set(executionId, handlers);
|
|
2730
|
+
return () => {
|
|
2731
|
+
handlers.delete(handler);
|
|
2732
|
+
if (handlers.size === 0)
|
|
2733
|
+
this._executionCompletedHandlers.delete(executionId);
|
|
2734
|
+
};
|
|
2735
|
+
}
|
|
2736
|
+
_trackProcess(proc, command, args, retainEvents, outputHandlers, exitHandlers) {
|
|
1772
2737
|
const entry = {
|
|
1773
2738
|
proc,
|
|
1774
2739
|
command,
|
|
1775
2740
|
args,
|
|
2741
|
+
startedAtMs: Date.now(),
|
|
2742
|
+
retainEvents,
|
|
2743
|
+
events: [],
|
|
2744
|
+
nextSequence: 0,
|
|
2745
|
+
signal: undefined,
|
|
2746
|
+
exit: undefined,
|
|
1776
2747
|
outputHandlers,
|
|
1777
2748
|
exitHandlers,
|
|
1778
2749
|
};
|
|
@@ -1783,40 +2754,83 @@ export class AgentOs {
|
|
|
1783
2754
|
// `_processes` is a process table for this VM's lifetime; it is freed wholesale
|
|
1784
2755
|
// in dispose(). (H5: the leak was that dispose() never cleared it.)
|
|
1785
2756
|
void proc.wait().then((code) => {
|
|
2757
|
+
const exit = {
|
|
2758
|
+
pid: proc.pid,
|
|
2759
|
+
outcome: entry.signal ? "signalled" : "exited",
|
|
2760
|
+
exitCode: code,
|
|
2761
|
+
...(entry.signal ? { signal: entry.signal } : {}),
|
|
2762
|
+
};
|
|
2763
|
+
entry.exit = exit;
|
|
1786
2764
|
for (const h of exitHandlers)
|
|
1787
|
-
h(
|
|
2765
|
+
h(exit);
|
|
1788
2766
|
});
|
|
1789
|
-
return {
|
|
2767
|
+
return {
|
|
2768
|
+
pid: proc.pid,
|
|
2769
|
+
state: "running",
|
|
2770
|
+
command,
|
|
2771
|
+
startedAtMs: entry.startedAtMs,
|
|
2772
|
+
};
|
|
1790
2773
|
}
|
|
1791
|
-
|
|
2774
|
+
async _spawnProcess(command, args = [], options = {}) {
|
|
1792
2775
|
const outputHandlers = new Set();
|
|
1793
2776
|
const exitHandlers = new Set();
|
|
2777
|
+
const recordOutput = (channel, data) => {
|
|
2778
|
+
const entry = this._processes.get(proc.pid);
|
|
2779
|
+
if (!entry?.retainEvents)
|
|
2780
|
+
return;
|
|
2781
|
+
if (entry.events.length >= PROCESS_OUTPUT_EVENT_LIMIT) {
|
|
2782
|
+
entry.events.shift();
|
|
2783
|
+
}
|
|
2784
|
+
entry.events.push({
|
|
2785
|
+
pid: proc.pid,
|
|
2786
|
+
sequence: entry.nextSequence++,
|
|
2787
|
+
channel,
|
|
2788
|
+
chunk: data,
|
|
2789
|
+
timestampMs: Date.now(),
|
|
2790
|
+
});
|
|
2791
|
+
};
|
|
1794
2792
|
const proc = this.#kernel.spawn(command, args, {
|
|
1795
|
-
|
|
2793
|
+
cwd: options.cwd,
|
|
2794
|
+
env: options.env,
|
|
2795
|
+
stdin: options.stdin,
|
|
2796
|
+
timeout: options.timeoutMs,
|
|
1796
2797
|
onStdout: (data) => {
|
|
2798
|
+
recordOutput("stdout", data);
|
|
1797
2799
|
options?.onStdout?.(data);
|
|
1798
2800
|
for (const h of outputHandlers) {
|
|
1799
2801
|
h({ pid: proc.pid, stream: "stdout", data });
|
|
1800
2802
|
}
|
|
1801
2803
|
},
|
|
1802
2804
|
onStderr: (data) => {
|
|
2805
|
+
recordOutput("stderr", data);
|
|
1803
2806
|
options?.onStderr?.(data);
|
|
1804
2807
|
for (const h of outputHandlers) {
|
|
1805
2808
|
h({ pid: proc.pid, stream: "stderr", data });
|
|
1806
2809
|
}
|
|
1807
2810
|
},
|
|
1808
2811
|
});
|
|
1809
|
-
return this._trackProcess(proc, command, args, outputHandlers, exitHandlers);
|
|
2812
|
+
return this._trackProcess(proc, command, args, options.output?.retainEvents ?? false, outputHandlers, exitHandlers);
|
|
2813
|
+
}
|
|
2814
|
+
spawn(command, args = [], options) {
|
|
2815
|
+
return this.process.spawn(command, args, options);
|
|
1810
2816
|
}
|
|
1811
2817
|
/** Write data to a process's stdin. */
|
|
1812
|
-
|
|
2818
|
+
_writeProcessStdin(pid, data) {
|
|
2819
|
+
const language = this._languageProcesses.get(pid);
|
|
2820
|
+
if (language) {
|
|
2821
|
+
return this._writeExecutionStdin(language.executionId, data);
|
|
2822
|
+
}
|
|
1813
2823
|
const entry = this._processes.get(pid);
|
|
1814
2824
|
if (!entry)
|
|
1815
2825
|
throw new Error(`Process not found: ${pid}`);
|
|
1816
2826
|
return entry.proc.writeStdin(data);
|
|
1817
2827
|
}
|
|
1818
2828
|
/** Close a process's stdin stream. */
|
|
1819
|
-
|
|
2829
|
+
_closeProcessStdin(pid) {
|
|
2830
|
+
const language = this._languageProcesses.get(pid);
|
|
2831
|
+
if (language) {
|
|
2832
|
+
return this._closeExecutionStdin(language.executionId);
|
|
2833
|
+
}
|
|
1820
2834
|
const entry = this._processes.get(pid);
|
|
1821
2835
|
if (!entry)
|
|
1822
2836
|
throw new Error(`Process not found: ${pid}`);
|
|
@@ -1824,7 +2838,7 @@ export class AgentOs {
|
|
|
1824
2838
|
}
|
|
1825
2839
|
/** Subscribe to stdout and stderr from a process. */
|
|
1826
2840
|
onProcessOutput(pid, handler) {
|
|
1827
|
-
const entry = this._processes.get(pid);
|
|
2841
|
+
const entry = this._processes.get(pid) ?? this._languageProcesses.get(pid);
|
|
1828
2842
|
if (!entry)
|
|
1829
2843
|
throw new Error(`Process not found: ${pid}`);
|
|
1830
2844
|
entry.outputHandlers.add(handler);
|
|
@@ -1834,12 +2848,12 @@ export class AgentOs {
|
|
|
1834
2848
|
}
|
|
1835
2849
|
/** Subscribe to process exit. Returns an unsubscribe function. */
|
|
1836
2850
|
onProcessExit(pid, handler) {
|
|
1837
|
-
const entry = this._processes.get(pid);
|
|
2851
|
+
const entry = this._processes.get(pid) ?? this._languageProcesses.get(pid);
|
|
1838
2852
|
if (!entry)
|
|
1839
2853
|
throw new Error(`Process not found: ${pid}`);
|
|
1840
2854
|
// If already exited, call immediately.
|
|
1841
|
-
if (entry
|
|
1842
|
-
handler(
|
|
2855
|
+
if ("exit" in entry && entry.exit) {
|
|
2856
|
+
handler(entry.exit);
|
|
1843
2857
|
return () => { };
|
|
1844
2858
|
}
|
|
1845
2859
|
entry.exitHandlers.add(handler);
|
|
@@ -1848,11 +2862,39 @@ export class AgentOs {
|
|
|
1848
2862
|
};
|
|
1849
2863
|
}
|
|
1850
2864
|
/** Wait for a process to exit. Returns the exit code. */
|
|
1851
|
-
|
|
2865
|
+
async _waitProcess(pid) {
|
|
2866
|
+
const language = this._languageProcesses.get(pid);
|
|
2867
|
+
if (language) {
|
|
2868
|
+
if (language.exit)
|
|
2869
|
+
return language.exit;
|
|
2870
|
+
const result = await this._waitExecutionResult(language.executionId);
|
|
2871
|
+
const exit = {
|
|
2872
|
+
pid,
|
|
2873
|
+
outcome: result.outcome === "timed_out"
|
|
2874
|
+
? "timed_out"
|
|
2875
|
+
: result.outcome === "cancelled"
|
|
2876
|
+
? "signalled"
|
|
2877
|
+
: "exited",
|
|
2878
|
+
...(result.exitCode !== undefined ? { exitCode: result.exitCode } : {}),
|
|
2879
|
+
...(language.signal ? { signal: language.signal } : {}),
|
|
2880
|
+
};
|
|
2881
|
+
language.exit = exit;
|
|
2882
|
+
language.descriptor = { ...language.descriptor, state: "exited" };
|
|
2883
|
+
for (const handler of language.exitHandlers)
|
|
2884
|
+
handler(exit);
|
|
2885
|
+
return exit;
|
|
2886
|
+
}
|
|
1852
2887
|
const entry = this._processes.get(pid);
|
|
1853
2888
|
if (!entry)
|
|
1854
2889
|
throw new Error(`Process not found: ${pid}`);
|
|
1855
|
-
|
|
2890
|
+
if (entry.exit)
|
|
2891
|
+
return entry.exit;
|
|
2892
|
+
const exitCode = await entry.proc.wait();
|
|
2893
|
+
return (entry.exit ?? {
|
|
2894
|
+
pid,
|
|
2895
|
+
outcome: "exited",
|
|
2896
|
+
exitCode,
|
|
2897
|
+
});
|
|
1856
2898
|
}
|
|
1857
2899
|
_assertSafeAbsolutePath(path) {
|
|
1858
2900
|
if (!path.startsWith("/")) {
|
|
@@ -1871,15 +2913,15 @@ export class AgentOs {
|
|
|
1871
2913
|
_vfs() {
|
|
1872
2914
|
return this.#kernel.vfs;
|
|
1873
2915
|
}
|
|
1874
|
-
async
|
|
2916
|
+
async _readFile(path) {
|
|
1875
2917
|
this._assertSafeAbsolutePath(path);
|
|
1876
2918
|
return this.#kernel.readFile(path);
|
|
1877
2919
|
}
|
|
1878
|
-
async
|
|
2920
|
+
async _writeFile(path, content) {
|
|
1879
2921
|
this._assertWritableAbsolutePath(path);
|
|
1880
2922
|
return this.#kernel.writeFile(path, content);
|
|
1881
2923
|
}
|
|
1882
|
-
async
|
|
2924
|
+
async _writeFiles(entries) {
|
|
1883
2925
|
const results = [];
|
|
1884
2926
|
for (const entry of entries) {
|
|
1885
2927
|
try {
|
|
@@ -1902,7 +2944,7 @@ export class AgentOs {
|
|
|
1902
2944
|
}
|
|
1903
2945
|
return results;
|
|
1904
2946
|
}
|
|
1905
|
-
async
|
|
2947
|
+
async _readFiles(paths) {
|
|
1906
2948
|
const results = [];
|
|
1907
2949
|
for (const path of paths) {
|
|
1908
2950
|
try {
|
|
@@ -1931,18 +2973,18 @@ export class AgentOs {
|
|
|
1931
2973
|
// exists() loop makes `mkdir -p` cost O(components * tree).
|
|
1932
2974
|
await this.#kernel.mkdir(path);
|
|
1933
2975
|
}
|
|
1934
|
-
async
|
|
2976
|
+
async _mkdir(path, options) {
|
|
1935
2977
|
if (options?.recursive) {
|
|
1936
2978
|
return this._mkdirp(path);
|
|
1937
2979
|
}
|
|
1938
2980
|
this._assertSafeAbsolutePath(path);
|
|
1939
2981
|
return this.#kernel.mkdir(path);
|
|
1940
2982
|
}
|
|
1941
|
-
async
|
|
2983
|
+
async _readdir(path) {
|
|
1942
2984
|
this._assertSafeAbsolutePath(path);
|
|
1943
2985
|
return this.#kernel.readdir(path);
|
|
1944
2986
|
}
|
|
1945
|
-
async
|
|
2987
|
+
async _readdirEntries(path) {
|
|
1946
2988
|
this._assertSafeAbsolutePath(path);
|
|
1947
2989
|
const entries = await this.#kernel.readdirRecursive(path, { maxDepth: 0 });
|
|
1948
2990
|
return entries.map((entry) => ({
|
|
@@ -1951,7 +2993,7 @@ export class AgentOs {
|
|
|
1951
2993
|
isSymbolicLink: entry.isSymbolicLink,
|
|
1952
2994
|
}));
|
|
1953
2995
|
}
|
|
1954
|
-
async
|
|
2996
|
+
async _readdirRecursive(path, options) {
|
|
1955
2997
|
this._assertSafeAbsolutePath(path);
|
|
1956
2998
|
const exclude = options?.exclude ? new Set(options.exclude) : undefined;
|
|
1957
2999
|
const entries = await this.#kernel.readdirRecursive(path, {
|
|
@@ -1981,15 +3023,15 @@ export class AgentOs {
|
|
|
1981
3023
|
}
|
|
1982
3024
|
return results;
|
|
1983
3025
|
}
|
|
1984
|
-
async
|
|
3026
|
+
async _stat(path) {
|
|
1985
3027
|
this._assertSafeAbsolutePath(path);
|
|
1986
3028
|
return this.#kernel.stat(path);
|
|
1987
3029
|
}
|
|
1988
|
-
async
|
|
3030
|
+
async _exists(path) {
|
|
1989
3031
|
this._assertSafeAbsolutePath(path);
|
|
1990
3032
|
return this.#kernel.exists(path);
|
|
1991
3033
|
}
|
|
1992
|
-
async
|
|
3034
|
+
async _exportRootFilesystem(options) {
|
|
1993
3035
|
if (!Number.isSafeInteger(options.maxBytes) || options.maxBytes <= 0) {
|
|
1994
3036
|
throw new RangeError("maxBytes must be a positive safe integer");
|
|
1995
3037
|
}
|
|
@@ -2009,7 +3051,7 @@ export class AgentOs {
|
|
|
2009
3051
|
* after the returned promise settles; a delivery failure rejects instead of
|
|
2010
3052
|
* leaving the mount silently host-only.
|
|
2011
3053
|
*/
|
|
2012
|
-
async
|
|
3054
|
+
async _mountFs(descriptor) {
|
|
2013
3055
|
this._assertSafeAbsolutePath(descriptor.path);
|
|
2014
3056
|
if (!(this.#kernel instanceof NativeSidecarKernelProxy)) {
|
|
2015
3057
|
throw new Error("portable dynamic mounts require the native sidecar");
|
|
@@ -2023,30 +3065,94 @@ export class AgentOs {
|
|
|
2023
3065
|
},
|
|
2024
3066
|
});
|
|
2025
3067
|
}
|
|
2026
|
-
async
|
|
3068
|
+
async _unmountFs(path) {
|
|
2027
3069
|
this._assertSafeAbsolutePath(path);
|
|
2028
3070
|
if (!(this.#kernel instanceof NativeSidecarKernelProxy)) {
|
|
2029
3071
|
throw new Error("portable dynamic mounts require the native sidecar");
|
|
2030
3072
|
}
|
|
2031
3073
|
await this.#kernel.unmountDescriptor(path);
|
|
2032
3074
|
}
|
|
2033
|
-
async
|
|
3075
|
+
async _listMounts() {
|
|
2034
3076
|
if (!(this.#kernel instanceof NativeSidecarKernelProxy))
|
|
2035
3077
|
return [];
|
|
2036
3078
|
return this.#kernel.listMounts();
|
|
2037
3079
|
}
|
|
2038
|
-
async
|
|
3080
|
+
async _move(from, to) {
|
|
2039
3081
|
this._assertWritableAbsolutePath(from);
|
|
2040
3082
|
this._assertWritableAbsolutePath(to);
|
|
2041
3083
|
await this.#kernel.movePath(from, to);
|
|
2042
3084
|
}
|
|
2043
|
-
async
|
|
3085
|
+
async _remove(path, options) {
|
|
2044
3086
|
this._assertWritableAbsolutePath(path);
|
|
2045
3087
|
await this.#kernel.removePath(path, {
|
|
2046
3088
|
recursive: options?.recursive ?? false,
|
|
2047
3089
|
});
|
|
2048
3090
|
}
|
|
2049
|
-
|
|
3091
|
+
/** @deprecated Use `filesystem.readFile()`. */
|
|
3092
|
+
readFile(path) {
|
|
3093
|
+
return this.filesystem.readFile(path);
|
|
3094
|
+
}
|
|
3095
|
+
/** @deprecated Use `filesystem.writeFile()`. */
|
|
3096
|
+
writeFile(path, content) {
|
|
3097
|
+
return this.filesystem.writeFile(path, content);
|
|
3098
|
+
}
|
|
3099
|
+
/** @deprecated Use `filesystem.writeFiles()`. */
|
|
3100
|
+
writeFiles(entries) {
|
|
3101
|
+
return this.filesystem.writeFiles(entries);
|
|
3102
|
+
}
|
|
3103
|
+
/** @deprecated Use `filesystem.readFiles()`. */
|
|
3104
|
+
readFiles(paths) {
|
|
3105
|
+
return this.filesystem.readFiles(paths);
|
|
3106
|
+
}
|
|
3107
|
+
/** @deprecated Use `filesystem.mkdir()`. */
|
|
3108
|
+
mkdir(path, options) {
|
|
3109
|
+
return this.filesystem.mkdir(path, options);
|
|
3110
|
+
}
|
|
3111
|
+
/** @deprecated Use `filesystem.readdir()`. */
|
|
3112
|
+
readdir(path) {
|
|
3113
|
+
return this.filesystem.readdir(path);
|
|
3114
|
+
}
|
|
3115
|
+
/** @deprecated Use `filesystem.readdirEntries()`. */
|
|
3116
|
+
readdirEntries(path) {
|
|
3117
|
+
return this.filesystem.readdirEntries(path);
|
|
3118
|
+
}
|
|
3119
|
+
/** @deprecated Use `filesystem.readdirRecursive()`. */
|
|
3120
|
+
readdirRecursive(path, options) {
|
|
3121
|
+
return this.filesystem.readdirRecursive(path, options);
|
|
3122
|
+
}
|
|
3123
|
+
/** @deprecated Use `filesystem.stat()`. */
|
|
3124
|
+
stat(path) {
|
|
3125
|
+
return this.filesystem.stat(path);
|
|
3126
|
+
}
|
|
3127
|
+
/** @deprecated Use `filesystem.exists()`. */
|
|
3128
|
+
exists(path) {
|
|
3129
|
+
return this.filesystem.exists(path);
|
|
3130
|
+
}
|
|
3131
|
+
/** @deprecated Use `filesystem.export()`. */
|
|
3132
|
+
exportRootFilesystem(options) {
|
|
3133
|
+
return this.filesystem.export(options);
|
|
3134
|
+
}
|
|
3135
|
+
/** @deprecated Use `filesystem.mount()`. */
|
|
3136
|
+
mountFs(descriptor) {
|
|
3137
|
+
return this.filesystem.mount(descriptor);
|
|
3138
|
+
}
|
|
3139
|
+
/** @deprecated Use `filesystem.unmount()`. */
|
|
3140
|
+
unmountFs(path) {
|
|
3141
|
+
return this.filesystem.unmount(path);
|
|
3142
|
+
}
|
|
3143
|
+
/** @deprecated Use `filesystem.listMounts()`. */
|
|
3144
|
+
listMounts() {
|
|
3145
|
+
return this.filesystem.listMounts();
|
|
3146
|
+
}
|
|
3147
|
+
/** @deprecated Use `filesystem.move()`. */
|
|
3148
|
+
move(from, to) {
|
|
3149
|
+
return this.filesystem.move(from, to);
|
|
3150
|
+
}
|
|
3151
|
+
/** @deprecated Use `filesystem.remove()`. */
|
|
3152
|
+
remove(path, options) {
|
|
3153
|
+
return this.filesystem.remove(path, options);
|
|
3154
|
+
}
|
|
3155
|
+
async _httpRequest(request) {
|
|
2050
3156
|
this._assertSafeAbsolutePath(request.path.split("?")[0] || "/");
|
|
2051
3157
|
const method = request.method?.toUpperCase() ?? "GET";
|
|
2052
3158
|
const body = typeof request.body === "string"
|
|
@@ -2074,9 +3180,13 @@ export class AgentOs {
|
|
|
2074
3180
|
body: new Uint8Array(Buffer.from(responsePayload.body ?? "", "base64")),
|
|
2075
3181
|
};
|
|
2076
3182
|
}
|
|
3183
|
+
/** @deprecated Use `network.httpRequest()`. */
|
|
3184
|
+
httpRequest(request) {
|
|
3185
|
+
return this.network.httpRequest(request);
|
|
3186
|
+
}
|
|
2077
3187
|
/**
|
|
2078
3188
|
* Fetch an HTTP endpoint inside the VM while preserving duplicate response
|
|
2079
|
-
* headers and arbitrary binary bodies.
|
|
3189
|
+
* headers and arbitrary binary bodies. agentOS Apps uses this compatibility
|
|
2080
3190
|
* surface for its direct actor API.
|
|
2081
3191
|
*/
|
|
2082
3192
|
async fetch(port, request) {
|
|
@@ -2142,7 +3252,7 @@ export class AgentOs {
|
|
|
2142
3252
|
streamId,
|
|
2143
3253
|
});
|
|
2144
3254
|
}
|
|
2145
|
-
|
|
3255
|
+
_openTerminal(options) {
|
|
2146
3256
|
const shellId = `shell-${++this._shellCounter}`;
|
|
2147
3257
|
this._closedShellIds.delete(shellId);
|
|
2148
3258
|
const dataHandlers = new Set();
|
|
@@ -2193,7 +3303,7 @@ export class AgentOs {
|
|
|
2193
3303
|
return this.#kernel.connectTerminal(options);
|
|
2194
3304
|
}
|
|
2195
3305
|
/** Write data to a shell's PTY input. */
|
|
2196
|
-
|
|
3306
|
+
_writeTerminal(shellId, data) {
|
|
2197
3307
|
const entry = this._shells.get(shellId);
|
|
2198
3308
|
if (!entry)
|
|
2199
3309
|
throw new Error(`Shell not found: ${shellId}`);
|
|
@@ -2236,7 +3346,7 @@ export class AgentOs {
|
|
|
2236
3346
|
return () => entry.exitHandlers.delete(handler);
|
|
2237
3347
|
}
|
|
2238
3348
|
/** Notify a shell of terminal resize. */
|
|
2239
|
-
|
|
3349
|
+
_resizeTerminal(shellId, cols, rows) {
|
|
2240
3350
|
const entry = this._shells.get(shellId);
|
|
2241
3351
|
if (!entry)
|
|
2242
3352
|
throw new Error(`Shell not found: ${shellId}`);
|
|
@@ -2247,7 +3357,7 @@ export class AgentOs {
|
|
|
2247
3357
|
* immediately for a shell that has already exited (within the closed-shell
|
|
2248
3358
|
* retention window).
|
|
2249
3359
|
*/
|
|
2250
|
-
|
|
3360
|
+
_waitTerminal(shellId) {
|
|
2251
3361
|
const entry = this._shells.get(shellId);
|
|
2252
3362
|
if (!entry) {
|
|
2253
3363
|
const exitCode = this._closedShellIds.get(shellId);
|
|
@@ -2258,7 +3368,7 @@ export class AgentOs {
|
|
|
2258
3368
|
return entry.exitPromise;
|
|
2259
3369
|
}
|
|
2260
3370
|
/** Kill a shell process and remove it from tracking. */
|
|
2261
|
-
|
|
3371
|
+
_closeTerminal(shellId) {
|
|
2262
3372
|
const entry = this._shells.get(shellId);
|
|
2263
3373
|
if (!entry) {
|
|
2264
3374
|
if (this._closedShellIds.has(shellId)) {
|
|
@@ -2270,6 +3380,26 @@ export class AgentOs {
|
|
|
2270
3380
|
this._shells.delete(shellId);
|
|
2271
3381
|
this._closedShellIds.add(shellId);
|
|
2272
3382
|
}
|
|
3383
|
+
/** @deprecated Use `terminal.open()`. */
|
|
3384
|
+
openShell(options) {
|
|
3385
|
+
return this.terminal.open(options);
|
|
3386
|
+
}
|
|
3387
|
+
/** @deprecated Use `terminal.write()`. */
|
|
3388
|
+
writeShell(shellId, data) {
|
|
3389
|
+
return this.terminal.write(shellId, data);
|
|
3390
|
+
}
|
|
3391
|
+
/** @deprecated Use `terminal.resize()`. */
|
|
3392
|
+
resizeShell(shellId, cols, rows) {
|
|
3393
|
+
this.terminal.resize(shellId, cols, rows);
|
|
3394
|
+
}
|
|
3395
|
+
/** @deprecated Use `terminal.wait()`. */
|
|
3396
|
+
waitShell(shellId) {
|
|
3397
|
+
return this.terminal.wait(shellId);
|
|
3398
|
+
}
|
|
3399
|
+
/** @deprecated Use `terminal.close()`. */
|
|
3400
|
+
closeShell(shellId) {
|
|
3401
|
+
this.terminal.close(shellId);
|
|
3402
|
+
}
|
|
2273
3403
|
_resolveVmPathToHostPath(vmPath) {
|
|
2274
3404
|
const normalizedVmPath = posixPath.normalize(vmPath);
|
|
2275
3405
|
for (const mount of this._hostMounts) {
|
|
@@ -2285,34 +3415,51 @@ export class AgentOs {
|
|
|
2285
3415
|
return null;
|
|
2286
3416
|
}
|
|
2287
3417
|
/** Returns info about all processes spawned via spawn(). */
|
|
2288
|
-
|
|
2289
|
-
return [
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
3418
|
+
async _listProcesses() {
|
|
3419
|
+
return [
|
|
3420
|
+
...[...this._processes.values()].map(({ proc, command, startedAtMs }) => ({
|
|
3421
|
+
pid: proc.pid,
|
|
3422
|
+
command,
|
|
3423
|
+
state: proc.exitCode === null ? "running" : "exited",
|
|
3424
|
+
startedAtMs,
|
|
3425
|
+
})),
|
|
3426
|
+
...[...this._languageProcesses.values()].map((entry) => entry.descriptor),
|
|
3427
|
+
];
|
|
2296
3428
|
}
|
|
2297
3429
|
/** Returns all kernel processes across all active runtimes (WASM and Node). */
|
|
2298
|
-
|
|
3430
|
+
_listAllProcesses() {
|
|
2299
3431
|
if (this.#kernel instanceof NativeSidecarKernelProxy) {
|
|
2300
3432
|
return this.#kernel.snapshotProcesses();
|
|
2301
3433
|
}
|
|
2302
3434
|
return [...this.#kernel.processes.values()];
|
|
2303
3435
|
}
|
|
2304
3436
|
/** Returns processes organized as a tree using ppid relationships. */
|
|
2305
|
-
|
|
2306
|
-
const all = this.
|
|
3437
|
+
async _processTree() {
|
|
3438
|
+
const all = this._listAllProcesses();
|
|
2307
3439
|
const nodeMap = new Map();
|
|
2308
3440
|
// Index: create a tree node for each process
|
|
2309
3441
|
for (const proc of all) {
|
|
2310
|
-
nodeMap.set(proc.pid, {
|
|
3442
|
+
nodeMap.set(proc.pid, {
|
|
3443
|
+
pid: proc.pid,
|
|
3444
|
+
ppid: proc.ppid,
|
|
3445
|
+
command: proc.command,
|
|
3446
|
+
state: proc.status,
|
|
3447
|
+
startedAtMs: proc.startTime,
|
|
3448
|
+
children: [],
|
|
3449
|
+
});
|
|
3450
|
+
}
|
|
3451
|
+
for (const process of this._languageProcesses.values()) {
|
|
3452
|
+
if (!nodeMap.has(process.descriptor.pid)) {
|
|
3453
|
+
nodeMap.set(process.descriptor.pid, {
|
|
3454
|
+
...process.descriptor,
|
|
3455
|
+
children: [],
|
|
3456
|
+
});
|
|
3457
|
+
}
|
|
2311
3458
|
}
|
|
2312
3459
|
// Wire: attach each node to its parent
|
|
2313
3460
|
const roots = [];
|
|
2314
3461
|
for (const node of nodeMap.values()) {
|
|
2315
|
-
const parent = nodeMap.get(node.ppid);
|
|
3462
|
+
const parent = node.ppid === undefined ? undefined : nodeMap.get(node.ppid);
|
|
2316
3463
|
if (parent) {
|
|
2317
3464
|
parent.children.push(node);
|
|
2318
3465
|
}
|
|
@@ -2323,7 +3470,10 @@ export class AgentOs {
|
|
|
2323
3470
|
return roots;
|
|
2324
3471
|
}
|
|
2325
3472
|
/** Returns info about a specific process by PID. Throws if not found. */
|
|
2326
|
-
|
|
3473
|
+
async _getProcess(pid) {
|
|
3474
|
+
const language = this._languageProcesses.get(pid);
|
|
3475
|
+
if (language)
|
|
3476
|
+
return language.descriptor;
|
|
2327
3477
|
const entry = this._processes.get(pid);
|
|
2328
3478
|
if (!entry) {
|
|
2329
3479
|
throw new Error(`Process not found: ${pid}`);
|
|
@@ -2331,32 +3481,76 @@ export class AgentOs {
|
|
|
2331
3481
|
return {
|
|
2332
3482
|
pid: entry.proc.pid,
|
|
2333
3483
|
command: entry.command,
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
exitCode: entry.proc.exitCode,
|
|
3484
|
+
state: entry.proc.exitCode === null ? "running" : "exited",
|
|
3485
|
+
startedAtMs: entry.startedAtMs,
|
|
2337
3486
|
};
|
|
2338
3487
|
}
|
|
2339
|
-
|
|
2340
|
-
|
|
3488
|
+
async _signalProcess(pid, signal) {
|
|
3489
|
+
const language = this._languageProcesses.get(pid);
|
|
3490
|
+
if (language) {
|
|
3491
|
+
language.signal = signal;
|
|
3492
|
+
await this._signalExecution(language.executionId, signal);
|
|
3493
|
+
return;
|
|
3494
|
+
}
|
|
2341
3495
|
const entry = this._processes.get(pid);
|
|
2342
3496
|
if (!entry) {
|
|
2343
3497
|
throw new Error(`Process not found: ${pid}`);
|
|
2344
3498
|
}
|
|
2345
3499
|
if (entry.proc.exitCode !== null)
|
|
2346
3500
|
return;
|
|
2347
|
-
entry.
|
|
3501
|
+
entry.signal = signal;
|
|
3502
|
+
const number = signal === "SIGKILL" ? 9 : signal === "SIGINT" ? 2 : 15;
|
|
3503
|
+
entry.proc.kill(number);
|
|
2348
3504
|
}
|
|
2349
3505
|
/** Send SIGKILL to force-kill a process. No-op if already exited. */
|
|
2350
|
-
|
|
3506
|
+
async _killProcess(pid) {
|
|
3507
|
+
await this._signalProcess(pid, "SIGKILL");
|
|
3508
|
+
}
|
|
3509
|
+
async _resizeProcessPty(pid, size) {
|
|
3510
|
+
const language = this._languageProcesses.get(pid);
|
|
3511
|
+
if (!language) {
|
|
3512
|
+
throw new Error(`Process ${pid} does not have a managed PTY`);
|
|
3513
|
+
}
|
|
3514
|
+
await this._resizeExecutionPty(language.executionId, size);
|
|
3515
|
+
}
|
|
3516
|
+
async _readProcessOutput(pid, options = {}) {
|
|
3517
|
+
const language = this._languageProcesses.get(pid);
|
|
3518
|
+
if (language) {
|
|
3519
|
+
const page = await this._readExecutionOutput(language.executionId, {
|
|
3520
|
+
cursor: options.after === undefined ? undefined : `1:${options.after + 1}`,
|
|
3521
|
+
});
|
|
3522
|
+
return {
|
|
3523
|
+
pid,
|
|
3524
|
+
events: page.events.map((event) => ({
|
|
3525
|
+
pid,
|
|
3526
|
+
sequence: event.sequence,
|
|
3527
|
+
channel: event.channel,
|
|
3528
|
+
chunk: event.chunk,
|
|
3529
|
+
timestampMs: event.timestampMs,
|
|
3530
|
+
})),
|
|
3531
|
+
nextCursor: page.nextCursor,
|
|
3532
|
+
hasMore: page.hasMore,
|
|
3533
|
+
truncated: page.truncated,
|
|
3534
|
+
};
|
|
3535
|
+
}
|
|
2351
3536
|
const entry = this._processes.get(pid);
|
|
2352
|
-
if (!entry)
|
|
3537
|
+
if (!entry)
|
|
2353
3538
|
throw new Error(`Process not found: ${pid}`);
|
|
3539
|
+
if (!entry.retainEvents) {
|
|
3540
|
+
throw new Error(`Process ${pid} was not spawned with output.retainEvents enabled`);
|
|
2354
3541
|
}
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
3542
|
+
const after = options.after ?? -1;
|
|
3543
|
+
const events = entry.events.filter((event) => event.sequence > after);
|
|
3544
|
+
return {
|
|
3545
|
+
pid,
|
|
3546
|
+
events,
|
|
3547
|
+
nextCursor: String(events.at(-1)?.sequence ?? after),
|
|
3548
|
+
hasMore: false,
|
|
3549
|
+
truncated: entry.events.length === PROCESS_OUTPUT_EVENT_LIMIT &&
|
|
3550
|
+
after < (entry.events[0]?.sequence ?? 0) - 1,
|
|
3551
|
+
};
|
|
2358
3552
|
}
|
|
2359
|
-
async
|
|
3553
|
+
async _openSession(input) {
|
|
2360
3554
|
const response = await this._sendAcpRequest({
|
|
2361
3555
|
tag: "AcpOpenSessionRequest",
|
|
2362
3556
|
val: {
|
|
@@ -2379,7 +3573,7 @@ export class AgentOs {
|
|
|
2379
3573
|
throw new Error(`unexpected openSession response: ${response.tag}`);
|
|
2380
3574
|
}
|
|
2381
3575
|
}
|
|
2382
|
-
async
|
|
3576
|
+
async _getSession(input) {
|
|
2383
3577
|
const response = await this._sendAcpRequest({
|
|
2384
3578
|
tag: "AcpGetDurableSessionRequest",
|
|
2385
3579
|
val: { sessionId: input?.sessionId ?? null },
|
|
@@ -2389,7 +3583,7 @@ export class AgentOs {
|
|
|
2389
3583
|
}
|
|
2390
3584
|
return decodeDurableSessionInfo(response.val.session);
|
|
2391
3585
|
}
|
|
2392
|
-
async
|
|
3586
|
+
async _listSessions(input) {
|
|
2393
3587
|
const response = await this._sendAcpRequest({
|
|
2394
3588
|
tag: "AcpListDurableSessionsRequest",
|
|
2395
3589
|
val: { cursor: input?.cursor ?? null, limit: input?.limit ?? null },
|
|
@@ -2402,7 +3596,7 @@ export class AgentOs {
|
|
|
2402
3596
|
nextCursor: response.val.nextCursor,
|
|
2403
3597
|
};
|
|
2404
3598
|
}
|
|
2405
|
-
async
|
|
3599
|
+
async _deleteSession(input = {}) {
|
|
2406
3600
|
const response = await this._sendAcpRequest({
|
|
2407
3601
|
tag: "AcpDeleteSessionRequest",
|
|
2408
3602
|
val: { sessionId: input.sessionId ?? null },
|
|
@@ -2411,7 +3605,7 @@ export class AgentOs {
|
|
|
2411
3605
|
throw new Error(`unexpected deleteSession response: ${response.tag}`);
|
|
2412
3606
|
}
|
|
2413
3607
|
}
|
|
2414
|
-
async
|
|
3608
|
+
async _unloadSession(input) {
|
|
2415
3609
|
const response = await this._sendAcpRequest({
|
|
2416
3610
|
tag: "AcpUnloadSessionRequest",
|
|
2417
3611
|
val: { sessionId: input?.sessionId ?? null },
|
|
@@ -2420,7 +3614,7 @@ export class AgentOs {
|
|
|
2420
3614
|
throw new Error(`unexpected unloadSession response: ${response.tag}`);
|
|
2421
3615
|
}
|
|
2422
3616
|
}
|
|
2423
|
-
async
|
|
3617
|
+
async _prompt(input) {
|
|
2424
3618
|
const response = await this._sendAcpRequest({
|
|
2425
3619
|
tag: "AcpPromptRequest",
|
|
2426
3620
|
val: {
|
|
@@ -2438,7 +3632,7 @@ export class AgentOs {
|
|
|
2438
3632
|
stopReason: response.val.stopReason,
|
|
2439
3633
|
};
|
|
2440
3634
|
}
|
|
2441
|
-
async
|
|
3635
|
+
async _cancelPrompt(input) {
|
|
2442
3636
|
const response = await this._sendAcpRequest({
|
|
2443
3637
|
tag: "AcpCancelPromptRequest",
|
|
2444
3638
|
val: { sessionId: input?.sessionId ?? null },
|
|
@@ -2452,7 +3646,7 @@ export class AgentOs {
|
|
|
2452
3646
|
}
|
|
2453
3647
|
return { status: response.val.status };
|
|
2454
3648
|
}
|
|
2455
|
-
async
|
|
3649
|
+
async _respondPermission(input) {
|
|
2456
3650
|
const response = await this._sendAcpRequest({
|
|
2457
3651
|
tag: "AcpRespondPermissionRequest",
|
|
2458
3652
|
val: {
|
|
@@ -2479,7 +3673,7 @@ export class AgentOs {
|
|
|
2479
3673
|
reason: permissionTerminalReason(response.val.reason),
|
|
2480
3674
|
};
|
|
2481
3675
|
}
|
|
2482
|
-
async
|
|
3676
|
+
async _readHistory(input) {
|
|
2483
3677
|
const response = await this._sendAcpRequest({
|
|
2484
3678
|
tag: "AcpReadHistoryRequest",
|
|
2485
3679
|
val: {
|
|
@@ -2498,7 +3692,7 @@ export class AgentOs {
|
|
|
2498
3692
|
hasMoreAfter: response.val.hasMoreAfter,
|
|
2499
3693
|
};
|
|
2500
3694
|
}
|
|
2501
|
-
async
|
|
3695
|
+
async _getSessionConfig(input) {
|
|
2502
3696
|
const response = await this._sendAcpRequest({
|
|
2503
3697
|
tag: "AcpGetSessionConfigRequest",
|
|
2504
3698
|
val: { sessionId: input?.sessionId ?? null },
|
|
@@ -2511,7 +3705,7 @@ export class AgentOs {
|
|
|
2511
3705
|
options: JSON.parse(response.val.options),
|
|
2512
3706
|
};
|
|
2513
3707
|
}
|
|
2514
|
-
async
|
|
3708
|
+
async _setSessionConfigOption(input) {
|
|
2515
3709
|
const response = await this._sendAcpRequest({
|
|
2516
3710
|
tag: "AcpSetSessionConfigOptionRequest",
|
|
2517
3711
|
val: {
|
|
@@ -2528,7 +3722,7 @@ export class AgentOs {
|
|
|
2528
3722
|
options: JSON.parse(response.val.options),
|
|
2529
3723
|
};
|
|
2530
3724
|
}
|
|
2531
|
-
async
|
|
3725
|
+
async _getSessionCapabilities(input) {
|
|
2532
3726
|
const response = await this._sendAcpRequest({
|
|
2533
3727
|
tag: "AcpGetSessionCapabilitiesRequest",
|
|
2534
3728
|
val: { sessionId: input?.sessionId ?? null },
|
|
@@ -2540,7 +3734,7 @@ export class AgentOs {
|
|
|
2540
3734
|
? null
|
|
2541
3735
|
: normalizeSessionCapabilities(JSON.parse(response.val.capabilities));
|
|
2542
3736
|
}
|
|
2543
|
-
async
|
|
3737
|
+
async _getSessionAgentInfo(input) {
|
|
2544
3738
|
const response = await this._sendAcpRequest({
|
|
2545
3739
|
tag: "AcpGetSessionAgentInfoRequest",
|
|
2546
3740
|
val: { sessionId: input?.sessionId ?? null },
|
|
@@ -2552,6 +3746,58 @@ export class AgentOs {
|
|
|
2552
3746
|
? null
|
|
2553
3747
|
: JSON.parse(response.val.agentInfo);
|
|
2554
3748
|
}
|
|
3749
|
+
/** @deprecated Use `sessions.open()`. */
|
|
3750
|
+
openSession(input) {
|
|
3751
|
+
return this.sessions.open(input);
|
|
3752
|
+
}
|
|
3753
|
+
/** @deprecated Use `sessions.get()`. */
|
|
3754
|
+
getSession(input) {
|
|
3755
|
+
return this.sessions.get(input);
|
|
3756
|
+
}
|
|
3757
|
+
/** @deprecated Use `sessions.list()`. */
|
|
3758
|
+
listSessions(input) {
|
|
3759
|
+
return this.sessions.list(input);
|
|
3760
|
+
}
|
|
3761
|
+
/** @deprecated Use `sessions.delete()`. */
|
|
3762
|
+
deleteSession(input = {}) {
|
|
3763
|
+
return this.sessions.delete(input);
|
|
3764
|
+
}
|
|
3765
|
+
/** @deprecated Use `sessions.unload()`. */
|
|
3766
|
+
unloadSession(input) {
|
|
3767
|
+
return this.sessions.unload(input);
|
|
3768
|
+
}
|
|
3769
|
+
/** @deprecated Use `sessions.prompt()`. */
|
|
3770
|
+
prompt(input) {
|
|
3771
|
+
return this.sessions.prompt(input);
|
|
3772
|
+
}
|
|
3773
|
+
/** @deprecated Use `sessions.cancelPrompt()`. */
|
|
3774
|
+
cancelPrompt(input) {
|
|
3775
|
+
return this.sessions.cancelPrompt(input);
|
|
3776
|
+
}
|
|
3777
|
+
/** @deprecated Use `sessions.respondPermission()`. */
|
|
3778
|
+
respondPermission(input) {
|
|
3779
|
+
return this.sessions.respondPermission(input);
|
|
3780
|
+
}
|
|
3781
|
+
/** @deprecated Use `sessions.readHistory()`. */
|
|
3782
|
+
readHistory(input) {
|
|
3783
|
+
return this.sessions.readHistory(input);
|
|
3784
|
+
}
|
|
3785
|
+
/** @deprecated Use `sessions.getConfig()`. */
|
|
3786
|
+
getSessionConfig(input) {
|
|
3787
|
+
return this.sessions.getConfig(input);
|
|
3788
|
+
}
|
|
3789
|
+
/** @deprecated Use `sessions.setConfigOption()`. */
|
|
3790
|
+
setSessionConfigOption(input) {
|
|
3791
|
+
return this.sessions.setConfigOption(input);
|
|
3792
|
+
}
|
|
3793
|
+
/** @deprecated Use `sessions.getCapabilities()`. */
|
|
3794
|
+
getSessionCapabilities(input) {
|
|
3795
|
+
return this.sessions.getCapabilities(input);
|
|
3796
|
+
}
|
|
3797
|
+
/** @deprecated Use `sessions.getAgentInfo()`. */
|
|
3798
|
+
getSessionAgentInfo(input) {
|
|
3799
|
+
return this.sessions.getAgentInfo(input);
|
|
3800
|
+
}
|
|
2555
3801
|
/**
|
|
2556
3802
|
* Dynamically link a software package into the RUNNING VM. The package's
|
|
2557
3803
|
* `bin/` commands appear under `/opt/agentos/bin` (on `$PATH`) and its `share/man`
|
|
@@ -2560,7 +3806,7 @@ export class AgentOs {
|
|
|
2560
3806
|
* block registers the package for `openSession({ agent: name })`. Persists for the VM's
|
|
2561
3807
|
* lifetime (and across a snapshot iff the volume persists).
|
|
2562
3808
|
*/
|
|
2563
|
-
async
|
|
3809
|
+
async _linkSoftware(descriptor) {
|
|
2564
3810
|
// Forward to the sidecar, which owns the `/opt/agentos` projection and
|
|
2565
3811
|
// appends the package to its live host-backed staging dir; the commands
|
|
2566
3812
|
// appear under `/opt/agentos/bin` immediately. The sidecar rejects a
|
|
@@ -2580,7 +3826,7 @@ export class AgentOs {
|
|
|
2580
3826
|
// picked up by the sidecar (it owns the projected `/opt/agentos` and answers
|
|
2581
3827
|
// openSession/listAgents from it). Nothing to record client-side.
|
|
2582
3828
|
}
|
|
2583
|
-
async
|
|
3829
|
+
async _listSoftware() {
|
|
2584
3830
|
return this._sidecarClient.providedCommands(this._sidecarSession, this._sidecarVm);
|
|
2585
3831
|
}
|
|
2586
3832
|
/**
|
|
@@ -2589,7 +3835,7 @@ export class AgentOs {
|
|
|
2589
3835
|
* projected `/opt/agentos` packages (the client parses no manifests). Every such
|
|
2590
3836
|
* agent is a package materialized into the VM, so `installed` is always `true`.
|
|
2591
3837
|
*/
|
|
2592
|
-
async
|
|
3838
|
+
async _listAgents() {
|
|
2593
3839
|
const response = await this._sendAcpRequest({
|
|
2594
3840
|
tag: "AcpListAgentsRequest",
|
|
2595
3841
|
val: { reserved: false },
|
|
@@ -2602,6 +3848,18 @@ export class AgentOs {
|
|
|
2602
3848
|
installed: agent.installed,
|
|
2603
3849
|
}));
|
|
2604
3850
|
}
|
|
3851
|
+
/** @deprecated Use `software.link()`. */
|
|
3852
|
+
linkSoftware(descriptor) {
|
|
3853
|
+
return this.software.link(descriptor);
|
|
3854
|
+
}
|
|
3855
|
+
/** @deprecated Use `software.list()`. */
|
|
3856
|
+
listSoftware() {
|
|
3857
|
+
return this.software.list();
|
|
3858
|
+
}
|
|
3859
|
+
/** @deprecated Use `agents.list()`. */
|
|
3860
|
+
listAgents() {
|
|
3861
|
+
return this.agents.list();
|
|
3862
|
+
}
|
|
2605
3863
|
_recordAgentStderr(event) {
|
|
2606
3864
|
if (!event.sessionId) {
|
|
2607
3865
|
return;
|
|
@@ -2655,6 +3913,81 @@ export class AgentOs {
|
|
|
2655
3913
|
}
|
|
2656
3914
|
}
|
|
2657
3915
|
_handleSidecarEvent(event) {
|
|
3916
|
+
if (event.payload.type === "execution_output") {
|
|
3917
|
+
const output = mapExecutionOutputEvent(event.payload.event);
|
|
3918
|
+
const pid = this._languageProcessIds.get(output.executionId);
|
|
3919
|
+
if (pid !== undefined) {
|
|
3920
|
+
const process = this._languageProcesses.get(pid);
|
|
3921
|
+
if (process) {
|
|
3922
|
+
const publicOutput = {
|
|
3923
|
+
pid,
|
|
3924
|
+
stream: output.channel === "stderr" ? "stderr" : "stdout",
|
|
3925
|
+
data: output.chunk,
|
|
3926
|
+
};
|
|
3927
|
+
for (const handler of process.outputHandlers) {
|
|
3928
|
+
try {
|
|
3929
|
+
handler(publicOutput);
|
|
3930
|
+
}
|
|
3931
|
+
catch (error) {
|
|
3932
|
+
console.error("AgentOS process output handler failed", error);
|
|
3933
|
+
}
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3936
|
+
}
|
|
3937
|
+
for (const key of ["*", output.executionId]) {
|
|
3938
|
+
for (const handler of this._executionOutputHandlers.get(key) ?? []) {
|
|
3939
|
+
try {
|
|
3940
|
+
handler(output);
|
|
3941
|
+
}
|
|
3942
|
+
catch (error) {
|
|
3943
|
+
console.error("AgentOS execution output handler failed", error);
|
|
3944
|
+
}
|
|
3945
|
+
}
|
|
3946
|
+
}
|
|
3947
|
+
return;
|
|
3948
|
+
}
|
|
3949
|
+
if (event.payload.type === "execution_completed") {
|
|
3950
|
+
const completed = mapExecutionCompletedEvent(event.payload.event);
|
|
3951
|
+
const pid = this._languageProcessIds.get(completed.executionId);
|
|
3952
|
+
if (pid !== undefined) {
|
|
3953
|
+
const process = this._languageProcesses.get(pid);
|
|
3954
|
+
if (process && !process.exit) {
|
|
3955
|
+
const exit = {
|
|
3956
|
+
pid,
|
|
3957
|
+
outcome: completed.outcome === "timed_out"
|
|
3958
|
+
? "timed_out"
|
|
3959
|
+
: completed.outcome === "cancelled"
|
|
3960
|
+
? "signalled"
|
|
3961
|
+
: "exited",
|
|
3962
|
+
...(completed.exitCode !== undefined
|
|
3963
|
+
? { exitCode: completed.exitCode }
|
|
3964
|
+
: {}),
|
|
3965
|
+
...(process.signal ? { signal: process.signal } : {}),
|
|
3966
|
+
};
|
|
3967
|
+
process.exit = exit;
|
|
3968
|
+
process.descriptor = { ...process.descriptor, state: "exited" };
|
|
3969
|
+
for (const handler of process.exitHandlers) {
|
|
3970
|
+
try {
|
|
3971
|
+
handler(exit);
|
|
3972
|
+
}
|
|
3973
|
+
catch (error) {
|
|
3974
|
+
console.error("AgentOS process exit handler failed", error);
|
|
3975
|
+
}
|
|
3976
|
+
}
|
|
3977
|
+
}
|
|
3978
|
+
}
|
|
3979
|
+
for (const key of ["*", completed.executionId]) {
|
|
3980
|
+
for (const handler of this._executionCompletedHandlers.get(key) ?? []) {
|
|
3981
|
+
try {
|
|
3982
|
+
handler(completed);
|
|
3983
|
+
}
|
|
3984
|
+
catch (error) {
|
|
3985
|
+
console.error("AgentOS execution completion handler failed", error);
|
|
3986
|
+
}
|
|
3987
|
+
}
|
|
3988
|
+
}
|
|
3989
|
+
return;
|
|
3990
|
+
}
|
|
2658
3991
|
if (event.payload.type === "ext") {
|
|
2659
3992
|
this._handleAcpExtEvent(event.payload.envelope);
|
|
2660
3993
|
return;
|
|
@@ -3056,12 +4389,12 @@ export class AgentOs {
|
|
|
3056
4389
|
this._acpTerminals.set(terminalId, terminal);
|
|
3057
4390
|
return { terminalId };
|
|
3058
4391
|
}
|
|
3059
|
-
_handleAcpWriteTerminal(params) {
|
|
4392
|
+
async _handleAcpWriteTerminal(params) {
|
|
3060
4393
|
const method = "terminal/write";
|
|
3061
4394
|
const terminal = this._requireAcpTerminal(params, method);
|
|
3062
4395
|
const data = this._requireAcpStringParam(params, "data", method);
|
|
3063
4396
|
const encoding = this._optionalAcpStringParam(params, "encoding", method);
|
|
3064
|
-
terminal.handle.write(encoding === "base64" ? Buffer.from(data, "base64") : data);
|
|
4397
|
+
await terminal.handle.write(encoding === "base64" ? Buffer.from(data, "base64") : data);
|
|
3065
4398
|
return null;
|
|
3066
4399
|
}
|
|
3067
4400
|
_handleAcpReadTerminal(params) {
|
|
@@ -3151,17 +4484,29 @@ export class AgentOs {
|
|
|
3151
4484
|
}
|
|
3152
4485
|
// ── Cron ────────────────────────────────────────────────────
|
|
3153
4486
|
/** Schedule a cron job. Returns a handle with the job ID and a cancel method. */
|
|
3154
|
-
|
|
4487
|
+
_scheduleCron(options) {
|
|
3155
4488
|
return this._cronManager.schedule(options);
|
|
3156
4489
|
}
|
|
3157
4490
|
/** List all registered cron jobs. */
|
|
3158
|
-
|
|
4491
|
+
_listCronJobs() {
|
|
3159
4492
|
return this._cronManager.list();
|
|
3160
4493
|
}
|
|
3161
4494
|
/** Cancel a cron job by ID. */
|
|
3162
|
-
|
|
4495
|
+
_cancelCronJob(id) {
|
|
3163
4496
|
this._cronManager.cancel(id);
|
|
3164
4497
|
}
|
|
4498
|
+
/** @deprecated Use `cron.schedule()`. */
|
|
4499
|
+
scheduleCron(options) {
|
|
4500
|
+
return this.cron.schedule(options);
|
|
4501
|
+
}
|
|
4502
|
+
/** @deprecated Use `cron.list()`. */
|
|
4503
|
+
listCronJobs() {
|
|
4504
|
+
return this.cron.list();
|
|
4505
|
+
}
|
|
4506
|
+
/** @deprecated Use `cron.cancel()`. */
|
|
4507
|
+
cancelCronJob(id) {
|
|
4508
|
+
this.cron.cancel(id);
|
|
4509
|
+
}
|
|
3165
4510
|
/** Subscribe to cron lifecycle events (fire, complete, error). */
|
|
3166
4511
|
onCronEvent(handler) {
|
|
3167
4512
|
this._cronManager.onEvent(handler);
|
|
@@ -3180,6 +4525,8 @@ export class AgentOs {
|
|
|
3180
4525
|
}
|
|
3181
4526
|
this._acpTerminals.clear();
|
|
3182
4527
|
this._processes.clear();
|
|
4528
|
+
this._executionOutputHandlers.clear();
|
|
4529
|
+
this._executionCompletedHandlers.clear();
|
|
3183
4530
|
await waitForTrackedExitPromises([...shellExitPromises, ...terminalExitPromises], SHELL_DISPOSE_TIMEOUT_MS);
|
|
3184
4531
|
this._disposeSidecarEventListener();
|
|
3185
4532
|
const sidecarLease = this._sidecarLease;
|