@thruwire/foreman-pi0.1.0
Pi and Pi Durable bridges to Foreman's attached-worker supervisor
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
Foreman for Pi and Pi Durable
These bridges forward Pi lifecycle events and Pi Durable task hooks to the same
foreman hook supervisor used by Codex. Foreman owns routing, evidence, decisions,
request budgets, and attached-session storage. The bridge never makes a model call.
Tested against @earendil-works/pi-coding-agent and
@earendil-works/pi-durable 1.0.4, with Node 22.19+. Pi Durable's API is
experimental; the peer range is limited to 1.0.x. The former @mariozechner
packages are not part of this tested contract.
Installation
Pi supervision requires both the Python foreman-core package and this
TypeScript bridge. Installing foreman-core alone does not load an extension
into Pi. The bridge invokes the foreman executable; it does not bundle Python
or install the core automatically.
The bridge requires foreman-core 0.4.4 or newer. Version 0.4.3 predates
the Pi adapters.
Install the core with:
pipx install 'foreman-core>=0.4.4'
npm installation
Once @thruwire/foreman-pi has been published to npm, install it into Pi with:
pi install npm:@thruwire/foreman-pi
For a Pi Durable application, install the bridge and its supported host packages:
npm install @thruwire/foreman-pi @earendil-works/pi-durable@1.0.4 @earendil-works/chord@1.0.4
Import createForemanDurable from @thruwire/foreman-pi/durable and install it
in the host's registry as shown below. The host must support Node subprocesses
and have foreman on PATH; this bridge does not run inside a Cloudflare Worker.
Build from source
Source installation is available without an npm release. From a cloned Foreman checkout, install the core and build the bridge:
python3 -m pip install -e /absolute/path/to/foreman
cd /absolute/path/to/foreman/integrations/pi
npm ci
npm run build
Put TYPESAFE_API_KEY in the environment or ~/.foreman/.env. Existing Foreman
responsibilities, repository scope, extensions, and request budgets apply to both
clients; see the hook guide.
The default bridge command is
foreman hook --client pi or foreman hook --client pi-durable, found on PATH.
Pi coding agent
pi install registers the npm extension persistently. For a source build,
load the built extension:
pi -e /absolute/path/to/foreman/integrations/pi/dist/pi.js
For persistent loading, add that absolute path to extensions in Pi's
~/.pi/agent/settings.json. Keep the adjacent transport.js in place.
For a custom executable, create a Pi extension that exports the factory result:
import { createForemanPi } from '/absolute/path/to/foreman/integrations/pi/dist/pi.js';
export default createForemanPi({
command: ['/absolute/path/to/foreman'],
// dataDir: '/absolute/path/to/isolated-foreman-data',
// timeoutMs: 120000,
});
The extension handles session start/shutdown, prompt input, tool calls/results,
and agent_before_settle. Routing happens at input, which can consume a refused
prompt before the agent starts; before_agent_start supplies the returned
responsibility context. Pi's ordinary extension exceptions are notification-only,
so a routing failure consumes the input, and a tool failure returns a denial.
A completion block appends model-visible Foreman feedback and requests one
continuation. Foreman persists that allowance; another incomplete finish halts.
Post-tool blocking or halt preserves the result, adds feedback, and aborts the
agent. Aborted/error runs are not automatically continued. Manual user commands
such as !bash are outside agent tool supervision. Extensions that transform
input after this handler may change the eventual prompt; register Foreman after
input transformers when using them.
Pi Durable
Install the extension in the host's registry before opening the harness:
import { createRegistry, Harness } from '@earendil-works/pi-durable';
import { createForemanDurable } from '@thruwire/foreman-pi/durable';
let harness;
const registry = createRegistry();
// Install your tools and other extensions here.
registry.install(createForemanDurable({
sessionId: '/absolute/path/to/my-pi-storage',
cwd: '/absolute/path/to/my-repository',
harness: () => harness,
}));
harness = await Harness.open(storage, { models, registry }, context);
For a source build, replace the bridge import with the absolute path to
integrations/pi/dist/durable.js in your checkout.
storage, models, and context are the host's normal durable setup. The lazy
harness accessor resolves after opening, before any work starts. sessionId
must uniquely identify the storage and stay the same when reopening it. A
conversation ID is only unique within one storage, so the bridge combines both.
A conversation's configured cwd takes precedence over the fallback above.
The extension is selected by default; when explicitly selecting extensions,
include foreman in the conversation's selection.
The native hooks are GenerationTask.beforeRequest, GenerationTask.onYield,
GenerationTask.afterTools, ToolTask.beforeTool, and ToolTask.afterTool. beforeRequest reads the run's
actual submitted entries through the harness, rather than routing the transcript
or a synthetic continuation prompt. Stable submission IDs keep routing and the
one-continuation allowance intact through retries and generation handovers. New
user submissions route new work. Text prompts are required; image-only submissions
are refused because Foreman has no text job to assess.
Pi Durable's beforeTool supports blocking and argument replacement, but no
context injection. Foreman steering at this boundary therefore blocks the
proposed tool with feedback for the model to act on. After a tool, steering is
appended to its result; blocking or halt also marks an error and requests
termination through the native result control. A saved conversation control
record also halts the generation at afterTools, including mixed parallel
batches where one result's terminate flag would otherwise be ignored. As with Pi, a tool already run
cannot be undone, and other calls in an existing parallel batch may already be
running. For strict sequential boundaries set toolExecution: 'sequential' in
harness settings.
Pi Durable reports and swallows ordinary generation-hook exceptions. The bridge
uses the public abortTask API and waits for invocation cancellation, rather
than joining its own invocation, before propagating a refused request or halt.
Completion decisions use both a task memo and Foreman's replay record, covering
the gap between the external decision and Pi's memo commit. This does not make
external model calls or tool executions exactly once. Foreman records expire
under its normal session TTL; reopening after expiry starts fresh supervision.
There is no shutdown task hook, so durable Foreman sessions expire naturally.
Process contract and tests
Bridge input is one JSON envelope on stdin:
{"session_id":"native identity","cwd":"/workspace","work_id":"stable work identity","event":{"type":"input","text":"Fix the tests"}}
The Pi Durable adapter instead accepts beforeRequest with prompt, tool hooks
with a native call (id, name, arguments), afterTool with result, and
onYield with last_assistant_message. work_id deduplicates routing;
event_id identifies replayable completion calls. Reusing either identity with
changed input fails. Successful output is {} or a semantic action object:
{"action":"inject_context|block|halt","reason":"..."}. The bridge applies it
through the client's native API; this JSON is not a native Pi hook response.
Commands are executed without a shell. Full arguments and results go over stdin. Missing executables, nonzero status, invalid/oversized JSON, cancellation, and timeouts prevent supervised operations. Diagnostics are drained rather than retained in the bridge; use Foreman's saved state for assessment failure details.
npm test
# From the Foreman repository root:
PYTHONPATH=src .venv/bin/python -m pytest
.venv/bin/python -m ruff check .
Tests use published Pi packages, their offline faux provider, isolated temporary storage, and a fixture hook process. They do not need provider credentials or alter the user's installed integrations.
Upstream contracts: Pi extensions,
Pi Durable hooks.
The source inspected for implementation is
ae92585;
type checks and runtime tests use the published 1.0.4 packages in the lockfile.