4.5 KiB
World extensions
The harness loads files under /src/sausage/extensions/.
Each extension occupies <name>/server.ts in that directory. No Git repository or commit is required.
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
The VFS watcher observes the extension directory recursively.
After changes, the harness waits for 75 milliseconds of quiet before it reads the files.
It computes a SHA-256 hash over sorted paths and their byte contents, only at startup and after file changes.
Unchanged contents do not reload.
The harness copies the captured bytes to /run/sausage/extensions/<hash>/extensions/.
Imports read that snapshot through the world VFS, not the editable files.
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 file set before it changes the live registry. An import error, factory error, name mismatch, or registry validation error retains the previous set. 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. Version values are content hashes.
A session document persists these values in the host database, <dir>/host/harness.sqlite.
After successful validation, another session document retains the complete last good file set in that database.
Broken edits do not replace that copy.
On startup, the harness hashes and loads the current files, including offline edits.
If they fail, it loads the host-side copy and reports the current error before resume().
Tasks without a registered definition remain blocked.
The snapshot directories disappear with /run on restart. The host-side copy survives.
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 file set 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.
Restore files
Use undo to restore earlier files. The normal file watch reloads the restored set.
The startup fallback does not change editable files.
Tests
host/tests/it/extensions.rs runs host/js/extensions-test-bundle.js with a faux model and a process interface that rejects all commands.
It tests file edits, broken edits, a multi-file burst, an old call across reload, and a retained service handle.
It also tests unchanged hashes, offline edits, and recovery from the host-side copy across restarts.
The init tests verify that /src/sausage contains no .git directory.
The Nix shell preserves the user's Cargo configuration and its kache wrapper.