# Host JavaScript
The headless harness uses pi-durable in V8. Rust owns the SQLite connection.
The harness runs V8 on a dedicated current-thread Tokio runtime.
This satisfies Deno's runtime requirement without a change to the CLI runtime.
The database lives at `
/host/harness.sqlite`, outside the world.
WAL preserves atomic transactions after SIGKILL. `synchronous=FULL` flushes each commit before SQLite reports success.
The coding tools access the shared VFS through Rust operations.
The shell sends commands to the host's `Exec` implementation.
Each coding invocation binds its call id to a separate environment.
File mutations and shell descendants share that undo tag and the conversation chain.
The journal lists the tool name in `program` and its paths in `changed_paths`, newest first.
Extension `host.exec` uses the extension name and `options.task` as its tag.
Without a task, it creates a unique invocation tag.
An extension can supply `options.toolCall` to share the tag of its triggering tool call.
It supplies `options.conversations` for the conversation filter, as with other extension commands.
The adapter supplies the filesystem operations that the four coding tools use.
It does not implement the other portable filesystem capabilities.
The console uses the real process table and passes the conversation id to each command.
Large shell output spills to `/tmp/bash-.log` inside the world.
The tool result names the file and retains pi's limited output window.
Build the userland with `nix develop -c nu userland/bundle.nu`.
The script discovers binaries through Cargo metadata and reads each package's optional `tools.txt`.
It mounts through `sausage run --userland target/userland/bin`.
The `sh` subcommand accepts the same option. Both commands default to `SAUSAGE_USERLAND`.
The bundle appears read-only at `/bin`. Agents can install tools in `/usr/local/bin`.
## Reproduce the bundles
```sh
npm ci --prefix host/js
npm run --prefix host/js bundle
CARGO_TARGET_DIR=target/harness nix develop -c cargo test -p sausage --test harness
```
The lock file pins npm dependencies. The bundle command requires Nushell, GNU patch, and npm.
It applies `patches/pi-durable-1.5.0.patch` before esbuild runs.
This is Standing agent's patch at `7ec0b4f`, without the Node execution environment hunks (D65).
The patch adds transactional `beforeInput`, input markers, scheduler hold/release, and the failed-run boundary fix.
The step verifies an existing application before another bundle build. A mismatched or partially applied patch fails the command.
`npm ci` restores the unpatched dependency. The next bundle command applies the patch again.
The checked-in bundles let Cargo build without npm or network access for JavaScript.
Cargo still needs its normal Rust dependencies and the V8 archive.
For Nix, use a separate npm derivation with `importNpmLock` and the checked-in lock file.
Run the bundle script there. Pass its output to the Rust source derivation before Crane builds the host.
The Rust build must not execute npm or download JavaScript packages.
The console reads the `llm-proxy` credentials item through Rust.
Its public `base_url` field contains the proxy URL. Its secret `api_key` field contains the token.
If the item does not exist, Rust creates it from `PI_PROXY_BASE_URL` and `PI_PROXY_API_KEY` once.
Rust encrypts the token before it commits the item. Later starts ignore these environment variables.
The token enters JavaScript only through an operation. No startup script, transcript, or world file stores it.
The console selects `openai/gpt-6.1-sol` with low reasoning effort.
This model rejects disabled reasoning. Tests use the faux provider without credentials.
Credential requests own background pi-durable tasks in their requesting conversations.
Filed notifications create tasks. Startup reconciliation also creates missing tasks, including requests decided during a host stop.
A conversation document records each request id in the same commit that creates its task.
Program requests create no task. A pending task sleeps durably and checks the decision each second.
The harness also expires requests during these checks.
A decided task admits one plain-language input and completes in one SQLite commit.
The input starts a new turn when the conversation is idle. Neither the input nor the task contains a secret.
The task and its input survive SIGKILL. The input uses a stable request id to prevent duplicate admission.
Hosts with a shared credentials handle can use `run_module_with_credentials`.
## Channel input and shutdown
`run_module_with_input` accepts a receiver from `harness::input::channel()` and runs without console input or output.
Its caller supplies the world, process interface, shared credentials handle, and bundled module.
`Input::Submit` contains a conversation id, JSON content, and a request id.
The harness admits that input into the specified conversation. The request id preserves submission deduplication.
Headless admission does not wait for a model answer.
The harness groups queued follow-ups into one new run.
`Input::Stop` waits for admitted foreground work, then closes the harness cleanly.
The JavaScript `serve` function implements this channel protocol.
The main module exports `storage`, including its `scanSubmissions` method.
`run_console` connects stdin and stop signals to the same channel.
The console submits to the durable root conversation (#1).
EOF closes only the stdin client. `sausage run` stays active until SIGINT or SIGTERM.
The signal client sends `Input::Stop`. The harness closes its watches, credential coordinator, and SQLite connection.
The host then stops its process services and HTTP listener.
Console output still streams assistant messages, without replay of the historical transcript.
`harness_foundation.rs` runs the three Standing agent input tests and the scheduler phase-boundary test through bundled V8 modules.
It also tests the channel with closed stdin, CLI EOF and SIGTERM, submission scans, and the patched bundle hooks.