4.3 KiB
World extensions
The harness loads /src/sausage from its main branch.
Each extension occupies extensions/<name>/server.ts in that repository.
The default export is an extension or a factory that receives a Host.
The extension name must match its folder.
An optional numeric order export controls order. Names break ties.
Reload contract
A change to the branch ref or packed-refs triggers a reload.
The harness creates a detached world worktree at /run/sausage/extensions/<sha>.
Imports read that snapshot through the world VFS, not the editable checkout.
Relative imports must include their file extensions.
TypeScript modules use the same deno_ast transpiler as the harness.
The harness imports and validates the whole commit before it changes the live registry. An import error, factory error, name mismatch, or registry validation error retains the previous commit. A running tool call keeps its old implementation. The next call uses the new implementation. Removed extensions leave the registry after a successful reload.
/proc/extensions contains JSON with good, current, and error.
A session document persists these values in the harness database.
On startup, the harness tries the tip, then the last good commit, before resume().
Tasks without a registered definition remain blocked.
The snapshot worktrees disappear with /run on restart. The loader prunes stale worktree registrations before it recreates them.
Host
The host supplies these members:
harness(),storage.scanSubmissions,models(), and the conversationmetadatadocument token.settings(), from/src/sausage/settings.json. The VFS watcher detects changes to this file.fs.read,write,append,list,stat,lstat,mkdir,remove, andrenameon world paths.exec(command, options)for shell strings or argument arrays. Its result containscode,stdout, andstderr.- Exec options:
cwd,envpairs,timeoutin seconds,conversations,signal, andonOutput(text, stream). git(cwd, ...args), which returns trimmed stdout and throws with stderr on failure.implement(api, value), which stages a service until the commit installs.need(api), which returns a proxy. Every method call resolves the current provider, including a retained method reference.want(api), which returns a proxy orundefinedwhen no provider exists.onOpen(callback)andonClose(callback)for facet lifecycle.resource(key, create), which retains a value across reloads. Keys belong to their extension name.notice(message), which writes a console line and the latest notice to/proc/extensions.
P3-d will add emit, react, and eventLog at the marked seam in host/js/extensions.ts.
Those APIs will supply transactional events and persistent reactor cursors.
The synthetic package modules reuse the bundle's instances:
@earendil-works/pi-durableand/tools@earendil-works/pi-aiand/models@earendil-works/pi-schema@earendil-works/chordand/context
New package surfaces require a namespace in registerModules().
They must not bundle a second pi-durable instance.
The registry supplies the default extension selection for existing conversations.
New root conversations do not pin the built-ins.
Explicit per-conversation extension selections remain explicit.
Revert
Run this command inside the world:
extensions-revert
The Rust program lives in userland/exec-tools/src/revert.rs.
It reads /src/sausage/.git/sausage-last-good and calls world git to update refs/heads/main.
The loader writes this helper file after it persists the session status.
An empty helper file means no commit succeeded yet.
The command preserves the editable checkout. The branch change triggers the normal reload.
Tests
host/tests/extensions.rs runs host/js/extensions-test-bundle.js with world git and a faux model.
It tests A, broken B, changed C, an old call across reload, a retained service handle, and restart recovery.
It also tests the revert command, file operations, and exec environment options.
The world git and helper components use the executor's shared CARGO_TARGET_DIR.
The API and hook fixtures use that directory too.
The Nix shell overrides CARGO_HOME. Unset that variable before Cargo commands to preserve the configured kache wrapper.