79 lines
4.5 KiB
Markdown
79 lines
4.5 KiB
Markdown
# 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 conversation `metadata` document token.
|
|
- `settings()`, from `/src/sausage/settings.json`. The VFS watcher detects changes to this file.
|
|
- `fs.read`, `write`, `append`, `list`, `stat`, `lstat`, `mkdir`, `remove`, and `rename` on world paths.
|
|
- `exec(command, options)` for shell strings or argument arrays. Its result contains `code`, `stdout`, and `stderr`.
|
|
- Exec options: `cwd`, `env` pairs, `timeout` in seconds, `conversations`, `signal`, and `onOutput(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 or `undefined` when no provider exists.
|
|
- `onOpen(callback)` and `onClose(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-durable` and `/tools`
|
|
- `@earendil-works/pi-ai` and `/models`
|
|
- `@earendil-works/pi-schema`
|
|
- `@earendil-works/chord` and `/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.
|