91 lines
6.1 KiB
Markdown
91 lines
6.1 KiB
Markdown
# 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 `<dir>/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-<uuid>.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 <world> --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 Ultimate'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 harness reads configured providers from `/etc/sausage/config.yaml` through Rust.
|
|
`models.ts` registers pi-ai adapters for Anthropic Messages, OpenAI Responses, and OpenAI-compatible Chat Completions.
|
|
Provider API keys in the config are intentionally visible to agents. Rust redacts them from module errors.
|
|
The host refreshes models.dev metadata at startup, with a private cache and a packaged snapshot as offline fallbacks.
|
|
Metadata supplies context limits, reasoning support, and prices. Pi-ai stores estimated costs in assistant usage.
|
|
Provider and Markdown roster edits apply at the next run boundary.
|
|
The console targets the configured main agent conversation. A rename retains its conversation.
|
|
Tests use the faux provider or local fake servers without real 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 Main 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.
|