Run it on Node
Keep a durable Agent in a SQLite file, serve its routes and survive restarts.
@fungi.computer/shiitake/node runs one durable Agent in a SQLite file on disk,
on the Node event loop. It needs Node with node:sqlite. Accepted work is
durable: close the process mid-run and reopening the same file resumes it.
Open, serve and prompt
Section titled “Open, serve and prompt”import { fauxAssistantMessage } from "@earendil-works/pi-ai/providers/faux";import { rejectingSandbox } from "@fungi.computer/mycelium/testing";import { openNodeShiitake } from "@fungi.computer/shiitake/node";import { fauxModelSource } from "@fungi.computer/shiitake/testing";import { createShiitakeClient } from "@fungi.computer/shiitake/client";import { Hono } from "hono";import type { ShiitakeHttpEnv } from "@fungi.computer/shiitake/http";
/** * Open a durable Agent in a SQLite file, serve its Session routes behind * middleware that names the caller, and wait for one reply. */export async function nodeAgent(path: string) { const host = await openNodeShiitake({ path, extensions: { roster: [], grants: [] }, instructions: { load: () => Promise.resolve({ revision: "v1", text: "Reply briefly." }), }, model: fauxModelSource([fauxAssistantMessage("Ready on Node.")]), tools: { sandbox: rejectingSandbox("This Agent has no tools") }, }); // Only your middleware sets `shiitakeMeta`; request bodies cannot. const app = new Hono<ShiitakeHttpEnv>() .use(async (context, next) => { context.set("shiitakeMeta", { author: { id: "ada", name: "Ada" } }); await next(); return undefined; }) .route("/", host.routes); // Any HTTP client reaches the same routes; here, Shiitake's own client. const client = createShiitakeClient({ baseUrl: "http://agent.local", fetch: (input, init) => Promise.resolve(app.request(new Request(input, init))), }); try { const sessionId = await client.createSession("first"); await client .session(sessionId) .send("Say hello.", { requestId: "hello", delivery: "followUp" }); // The Node event loop runs the accepted prompt; poll until it settles. for (let attempt = 0; attempt < 200; attempt += 1) { const snapshot = await host.agent.session(sessionId).read(); if (snapshot.terminals.length > 0) return snapshot; await new Promise((resolve) => setTimeout(resolve, 10)); } throw new Error("The prompt did not settle"); } finally { await client.close(); await host.close(); }}openNodeShiitake({ path, ... }) takes the same model, instructions, extensions
and tools as Shiitake.make; it owns the store and the runtime host. It returns
the in-process agent, the Session routes as a Hono app, and close.
Serve routes with any server, such as @hono/node-server or Bun.serve.
Mount it behind your own middleware: authenticate the caller there and set the
shiitakeMeta variable to the metadata their prompts carry. Request bodies
cannot carry metadata; every command body is closed. Pass meta.schema to make
Shiitake check each value against your own schema.
One owner per file
Section titled “One owner per file”The process holds an exclusive lease on the store until close. A second
openNodeShiitake of the same file fails at once instead of sharing it, under
any name: a store reached through a symbolic link, or with a second hard link,
is refused. The store and its lease file are created private to your user
(0600), whatever the umask, and a missing directory is created 0700. close
stops waking, closes the Agent, then releases the database and lease.
Run the example
Section titled “Run the example”import { nodeAgent } from "./node-agent.js";
const snapshot = await nodeAgent("./agent.sqlite");console.log(JSON.stringify(snapshot.messages.at(-1)?.value, null, 2));npm init -ynpm pkg set type=modulenpm install @fungi.computer/shiitake @fungi.computer/mycelium hono @earendil-works/pi-ai@1.3.0npm install --save-dev tsx@4.23.12node --import tsx run-node-agent.tsThe package’s runtime test calls the same nodeAgent() function against a
temporary file and checks the completed run, the reply and the attributed
prompt.
Swap fauxModelSource for your provider’s model source, and give the Agent
tools and grants as Grant only what it needs shows.