What this adapter actually supports. See the class doc on capability honesty.
Whether this session actually continued HarnessSessionConfig.ResumeSessionId.
Default false: a harness that cannot resume, or one offered no prior session, starts cold and must be sent the full conversation. Adapters that DO resume MUST override this and report truthfully — the caller sends only the newest message when it returns true, so a false positive leaves the harness answering a question it never saw the context for.
Deliberately separate from CapabilitySettings.SessionResume. That flag says the adapter CAN
resume in principle; this says it DID, this time. A stale or pruned session id makes the two
disagree, and only the second one is safe to branch the turn input on.
ProtectedExecutableAbsolute path or bare command name of the harness binary.
The model the harness actually used, if it reports one.
Adapters that can observe this SHOULD override it. Accounting resolves AIPromptRun.ModelID
from this first and only falls back to the harness row's declared model, because a harness left
to choose its own model will — and billing a run against a model it never used is worse than
having no attribution at all, since it looks authoritative.
Vendor session id once known, persisted to AIAgentRun.ExternalSessionID.
Maps MJ's posture onto Pi's --tools / --exclude-tools.
Pi gates at the TOOL level, and unlike Claude Code's prefix-matched Bash patterns these are
exact tool-name matches — so what this configures is what is enforced, with no way for a
cleverly-flagged command to slip past. strict is therefore genuinely enforceable here,
whereas on Claude Code it degrades to "prompts that have nowhere to go".
A policy authored for Claude Code may contain Bash(git:*). Pi has no sub-command vocabulary,
so the only faithful translations are "all of bash" or "none of it" — and those are not
equivalent to what was written. The rule is fail-closed in both directions:
git push should not get all of bash.Silently widening an allow is the failure this whole capability exists to prevent.
ProtectedBuildBuilds the argv for one turn.
Task prompt on the first turn; formatted step results afterwards.
Lets a subclass choose between "start" and "resume" argument forms.
ProtecteddescribeNarrows an unknown catch binding to a readable message without reaching for any.
Tears the session down.
MUST be idempotent and MUST be safe to call on every exit path — success, failure, cancellation and crash — because it is what revokes the per-run MCP credential and releases the workspace. A teardown that only runs on the happy path leaks a live credential.
ProtectedMapInterprets one line of the harness's JSON stream.
Return null for lines that carry no meaning for MJ — heartbeats, progress spinners, internal
bookkeeping. Returning null is normal and expected; it is not an error path.
ProtectedreadFrames stdout into lines, parses each as JSON, and maps it through the subclass.
ProtectedreadReads a numeric field from a raw event, or undefined when absent/not a number.
ProtectedreadReads a nested object from a raw event, or undefined when absent/not an object.
ProtectedreadReads a string field from a raw event, or undefined when absent/not a string.
Answers a permission-request the adapter raised.
Only meaningful when Capabilities.PermissionHooks is true; adapters without hooks should treat this as a no-op rather than throwing, because the posture layer above may still call it defensively.
Optional_note: stringRuns one turn and streams what happens.
The first call receives the task prompt; subsequent calls receive formatted results of steps MJ
executed on the harness's behalf. Implementations MUST emit exactly one terminal event —
turn-complete or session-error — so the caller's accumulation loop always terminates.
Where the harness cannot resume a session natively (SessionResume false), the adapter is
responsible for replaying prior context into a fresh invocation here, and for reporting the
resulting token cost through usage so the run's guardrails see the true spend.
Points the adapter at a specific binary, from AIAgentHarness.ExecutablePath.
Selects Pi's provider (default google), typically from the harness row's configuration.
Supplies MJ's system prompt for the session, where the harness can accept one.
Harnesses ship their own system prompt defining their identity, and it dominates anything sent as user text. MJ's turn-end contract delivered as a user message therefore competes with the harness's own instructions and loses — observed directly: a harness given the contract in the user turn still answered "what can you do?" in prose, costing a retry every run.
Adapters whose harness accepts a system prompt SHOULD override this. Those that cannot are no worse off than before: the contract still rides in the turn input.
Launches the harness session. Called once per run, before the first turn.
Implementations must not begin reasoning here — only establish the process, workspace and credentials. Any failure should throw, so the run fails visibly rather than proceeding with a half-built session.
ProtectedstartStarts one turn's process INSIDE THE SANDBOX.
Note what this does not do: call spawn. Where the process physically runs is the sandbox
provider's decision, delivered through the executor, so this adapter behaves identically on a
laptop and in a per-run container.
Drives Pi (
@earendil-works/pi-coding-agent).Verified against a real install, not inferred
An earlier version of this adapter subclassed the generic stdio-JSON escape hatch, because the obvious npm names for Pi are placeholder reservations and there was no published contract to code against. With Pi actually installed (0.83.0), its CLI turns out to be well specified, so this is now a first-class adapter:
·
-p/--printnon-interactive: process the prompt and exit ·--mode jsonnewline-delimited JSON events ·--session-id <id>exact session, created if missing — so turns resume rather than replay ·--provider <name>defaultgoogle·--model <pattern>model pattern orprovider/idThe session-id form is what earns
SessionResume: true: MJ supplies the id up front rather than discovering it, so continuity does not depend on parsing it back out before turn two.Authentication
Pi resolves credentials from environment variables (or its own
/login). It therefore works with the standard credential path — grant a credential whoseEnvVariableNamematches what the chosen provider expects — and equally with the "true local" mode, where a developer who has already runpi /loginneeds no credential row at all, because the local executor passesHOMEthrough.