host: sausage run with the userland bundle, real shell and services (T13)

- userland/bundle.nu builds the userland and assembles target/userland/bin
  (bin names, tools.txt aliases, brush as sh and bash).
- run and sh take --userland (default SAUSAGE_USERLAND); /bin is read-only.
- The console's bash tool runs through the process table with the
  conversation id; large output spills to the world's /tmp.
- sausage run starts the services supervisor.

P1 exit session (openai/gpt-6.1-sol, fresh world): write, read, edit, git
init/add/commit/diff/log, grep -rn, rg, find and a sed|sort|uniq|wc pipeline.
This commit is contained in:
2026-11-06 17:34:11 +00:00
parent b9e3e3677d
commit ec6e20a9d7
12 changed files with 305 additions and 57 deletions
+27 -8
View File
@@ -44318,7 +44318,7 @@ var path2 = (cwd, input) => {
}
return "/" + parts.join("/");
};
function environment(cwd = "/") {
function environment(cwd = "/", conversation) {
const call = (kind, p, bytes2 = []) => ops.op_file(kind, path2(cwd, p), bytes2);
const env = {
id: "world",
@@ -44355,7 +44355,7 @@ function environment(cwd = "/") {
const exists = await call("stat", current);
if (!exists.ok) {
const r = await call("mkdir", current);
if (!r.ok) return r;
if (!r.ok && (await call("stat", current)).value?.kind !== "directory") return r;
}
}
return ok(void 0);
@@ -44377,8 +44377,27 @@ function environment(cwd = "/") {
});
},
exec: async (command, options, context) => {
let spillPath;
let pending = "", bytes2 = 0, lines2 = 0;
const output = async (text2, stream2) => {
options?.onOutput?.(text2, context, { stream: stream2 });
if (!options?.spill) return;
bytes2 += new TextEncoder().encode(text2).length;
lines2 += text2.split("\n").length - 1;
if (!spillPath) {
pending += text2;
if (bytes2 <= options.spill.afterBytes && lines2 + (pending.endsWith("\n") ? 0 : 1) <= options.spill.afterLines) return;
spillPath = `/tmp/bash-${crypto.randomUUID()}.log`;
const created = await env.writeFile(spillPath, pending);
if (!created.ok) throw new Error(created.error.message);
pending = "";
} else {
const appended = await call("append", spillPath, Array.from(new TextEncoder().encode(text2)));
if (!appended.ok) throw new Error(appended.error.message);
}
};
try {
const rid = ops.op_exec(command, options?.cwd ?? cwd, options?.timeout);
const rid = ops.op_exec(command, options?.cwd ?? cwd, options?.timeout, conversation);
const decoders = { stdout: new TextDecoder(), stderr: new TextDecoder() };
let result;
while (true) {
@@ -44387,16 +44406,16 @@ function environment(cwd = "/") {
result = chunk;
break;
}
options?.onOutput?.(decoders[chunk.stream].decode(new Uint8Array(chunk.bytes), { stream: true }), context, { stream: chunk.stream });
await output(decoders[chunk.stream].decode(new Uint8Array(chunk.bytes), { stream: true }), chunk.stream);
}
if (result.error) throw new Error(result.error);
for (const stream2 of ["stdout", "stderr"]) {
const text2 = decoders[stream2].decode();
if (text2) options?.onOutput?.(text2, context, { stream: stream2 });
if (text2) await output(text2, stream2);
}
return ok({ exitCode: result.exitCode });
return ok({ exitCode: result.exitCode, ...spillPath ? { spillPath } : {} });
} catch (error) {
return { ok: false, error };
return { ok: false, error: Object.assign(new Error(String(error)), { code: "unknown", ...spillPath ? { spillPath } : {} }) };
}
},
cleanup: async () => {
@@ -44442,7 +44461,7 @@ registry.install(CodingTools);
var harness = await Harness.open(await openStorage(globalThis.storagePath), {
models,
registry,
env: (target) => environment(target.cwd ?? "/")
env: (target) => environment(target.cwd ?? "/", target.conversationId)
}, BACKGROUND_CONTEXT);
var root = await harness.root(BACKGROUND_CONTEXT, { agent: { model: { provider: "openai", modelId: model2.id }, thinkingLevel: "low", cwd: "/", extensions: [CodingTools] } });
var watch = await root.watch(BACKGROUND_CONTEXT);