mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
A sandbox is a new env type (`type: "gondolin"`): a long-lived Gondolin micro-VM on an SSH environment, with listed host folders and an HTTP(S) host allowlist. The worker reaches it over SSH to the host, where p2's launcher (plugins/envs/sandbox: native, or Docker when the host's Node/QEMU are too old) runs pi-env's daemon inside the VM, so tools, workspace, terminals, uploads and MCP work unchanged. Names outside the allowlist are refused before the host resolves them. Conversations working in a sandbox cannot leave it: location tools, subagent spawn/configure, AgentLocations.move, the terminal tool, and one-shot agents (which now inherit their caller's own location) all keep them there; only the owner's Relocate moves a session out, after a confirmation. conversationConfinement() exposes it to plugins. Owner-only management in the Envs sheet: sandboxes listed under their host, host test reports sandbox support, create/edit/start/stop/restart/test, lock icons in the top bar and sidebar. Tests in test/p2/sandboxes.test.ts (real VM cases with P2_TEST_GONDOLIN=1) and ui-sandboxes.test.ts; docs in docs/remote-envs.md.
58 lines
2.1 KiB
TypeScript
58 lines
2.1 KiB
TypeScript
// The location tools' server side (worker principals only): the environments an agent may move to, their directories,
|
|
// and moving the main agent (its session) once its turn has ended.
|
|
|
|
import { arr, nul, num, obj, opt, type Static, str, union } from "@earendil-works/pi-schema";
|
|
import { DirectoryListingSchema, literal, method, remote, serverHost } from "@p2/app/v1";
|
|
|
|
/** One environment as an agent sees it: never keys or identity files. */
|
|
export const EnvInfoSchema = obj({
|
|
name: str(),
|
|
/** `gondolin`: a sandbox, `host` naming the SSH environment it runs on; conversations working there cannot leave it. */
|
|
type: union([literal("ssh"), literal("gondolin")]),
|
|
host: str(),
|
|
user: opt(str()),
|
|
port: opt(num()),
|
|
/** The owner's last test of this configuration (Envs), if any. */
|
|
tested: opt(
|
|
union([obj({ ok: literal(true), platform: str(), arch: str() }), obj({ ok: literal(false), error: str() })]),
|
|
),
|
|
});
|
|
export type EnvInfo = Static<typeof EnvInfoSchema>;
|
|
|
|
/** The worker attests that the owner started the calling turn; only such turns use the location tools. */
|
|
const Owner = literal("owner");
|
|
|
|
export const AgentLocations = remote("p2.agent-locations", {
|
|
version: 1,
|
|
params: {},
|
|
hostAddress: serverHost,
|
|
variants: {
|
|
worker: {
|
|
methods: {
|
|
/** The configured SSH environments and sandboxes (this device is always available besides them). */
|
|
envs: method(obj({ origin: Owner }), obj({ envs: arr(EnvInfoSchema) })),
|
|
/** Folders of `path` (absolute or `~`-relative) on `env` (null: this device), as the directory picker lists them. */
|
|
directories: method(
|
|
obj({
|
|
origin: Owner,
|
|
env: union([str(), nul()]),
|
|
path: str({ minLength: 1 }),
|
|
}),
|
|
DirectoryListingSchema,
|
|
),
|
|
/**
|
|
* Moves the worker's session (`SessionMoves.relocate`); `refused` is why its busy worker could not be fenced
|
|
* (nothing moved). A session working in a sandbox only moves within it (only the owner's Relocate leaves it).
|
|
*/
|
|
move: method(
|
|
obj({
|
|
origin: Owner,
|
|
env: union([str(), nul()]),
|
|
cwd: str({ minLength: 1 }),
|
|
}),
|
|
obj({ refused: union([str(), nul()]) }),
|
|
),
|
|
},
|
|
},
|
|
},
|
|
});
|