A Lody-owned ACP adapter for the pinned official Pi CLI (1.4.0).
Lody → ACP Adapter → official Pi CLI --mode rpc
└─ packaged tools
The adapter translates protocols. Pi owns model execution, native tools, automatic compaction, retry and session files. There is no custom SDK worker.
Requires Node.js 22.19 or newer.
pnpm install
pnpm build
node dist/index.js --provider <provider> --model <model>Startup accepts --provider, --model and --thinking plus repeatable -e/--extension flags naming an existing absolute or ~/ file or directory. Authenticate/configure models through Pi on the execution machine. ACP configuration also exposes model and thinking selection. stdout is ACP; stderr contains diagnostics.
LODY_PI_PATH can select another Pi executable. It must support the Pi 1.4
RPC contract and explicit builtin:codemode loading, like the bundled runtime.
Extensions are opt-in only. The adapter disables ambient and project
auto-discovery; nothing outside an explicit -e/--extension path and the
packaged extension and Pi's built-in codemode extension is loaded.
Codemode stays inactive unless selected through Pi's defaultTools setting.
For example, {"defaultTools":["+codemode"],"codemode":{"mode":"only"}}
routes tools through codemode in both parent and child sessions. Pi's built-in
MCP discovery remains disabled; ACP supplies MCP servers through the existing bridge.
node dist/index.js --list-extensions performs
a read-only scan of globally installed extensions and prints candidates as
JSON; it never executes extension code. The same explicitly selected paths
are passed to native subagent children; the packaged adapter extension is
not forwarded. A separate context-only extension supplies their workspace and
subagent instructions without replacing native user prompts. Remote or package-spec sources (npm:, git:, URLs) and relative
paths are refused. Selected extension code runs with Pi's full authority and
can add tools: this boundary is a supported-feature contract, not a security
sandbox for shell commands, and arbitrary community plugins are not a
compatibility promise.
When both peers advertise subagentEvents v1, child JSONL text/thinking deltas,
tool starts/results and lifecycle/progress feed Core _lody/subagents/event.
Legacy task cards are suppressed for these runs; non-negotiated clients keep them.
Children still cannot ask questions or spawn descendants. Existing task-ID
list/output/cancel endpoints remain separate; normalized run controls are disabled.
Core 0.1.9 supplies the contract and helper. A rebuilt, checksummed managed
runtime is still required before this reaches Lody's installed Pi provider.
- Native prompts, text/images, tool progress/results, model errors and cancellation.
- Native file identity for explicit new/resume/load/fork. No session-id map.
- Session list of native files for a working directory; load replays the current branch's user text/images, assistant text/thinking, tool and todo history. Compaction summaries and other branches are not replayed. A missing session file fails resume/load instead of starting an empty session.
- Acknowledged steering using acp-extension-core identity metadata.
- Slash-command discovery on new/resume/load/fork:
/stats,/compact, and Pi's loaded extension, prompt-template and skill commands. The private steering command is hidden. Commands keep Pi's native execution semantics; terminal-only UI and detached extension work are not supported. - Per-response token usage by model (including compaction summaries and subagents), native context snapshots, automatic compaction activity and /compact.
- ACP-selected stdio and streamable HTTP MCP tools with text/image results and cancellation. Legacy SSE and ACP transports are not supported.
- Questionnaire: one or more questions in Lody's existing question card.
- Todo: list/add/toggle/clear through Lody's existing checklist.
- Subagent: native Lody task lifecycle, list, output and individual cancellation.
session/new, session/load, session/resume and session/fork accept
additionalDirectories. Both cwd and every additional directory must be
absolute paths. Each activation replaces the request-owned directory list:
[A] → [B] → [] → omitted means A, then B, then no additional request
directories, then no additional request directories. Native user configuration
is preserved. Load/resume reject a cwd that differs from the native session
header before changing runtime state or configuration (symlink aliases of the
same directory are accepted). Fork can choose a different target cwd.
The packaged extension supplies these directories to the native model's system
context. Relative paths and Bash's initial directory still use cwd; native
Read/Write/Bash can access additional roots by absolute path. This is workspace
context, not a filesystem sandbox or new permission guarantee. Additional roots
do not trigger extension discovery or load their project configuration. Subagent
processes inherit the current workspace context, but not the parent's MCP tools.
Request roots are activation-only, are not saved as native history, and are not
reported as durable roots by session/list; send the full list when reattaching.
Fork uses Pi's native file-fork mechanism for existing persisted history, creates
a new native ID/file and preserves the source file and active runtime. It copies
native history rather than sending history as a prompt. Each session has its own
RPC process and private workspace/MCP configuration; parent, child and sibling
sessions remain independently addressable on the connection. Core forkAtTurn
selects a native branch through the turnId emitted after Pi settles a prompt.
An exact boundary can be forked while the source continues running; an untargeted
fork still requires the source to be idle. Connection close cleans up all owned runtimes;
an unexpected Pi exit still fails the entire connection. Empty, never-persisted
sessions are outside the recovery/fork contract.
Implementation and validation: additional directories note.
MCP audio, resource links and embedded resources are unsupported: a result containing
any of these fails explicitly, including mixed results. Structured JSON output is
appended as text. This validates returned content; it cannot undo an MCP tool's effects.
Codemode calls also receive the structured MCP result (content,
structuredContent, isError), so scripts can pass image blocks to image().
Questionnaire waits inside the parent tool call and returns answers or cancellation. Late answers are ignored. Child agents do not ask questions; they report missing information to the parent. Official TUI Question/Questionnaire code is not loaded.
Todo state is persisted in Pi tool results and reconstructed on resume. Codemode results carry the same todo snapshot because nested calls have no separate native result messages. Replayed nested calls use Pi's recorded ids, arguments and statuses; Pi stores only bounded nested metadata, so full nested results are not recreated. ACP checklist updates are a display projection. There is no second todo database. Todo completion maps to pending/completed; the tool does not invent in-progress status. The /todos terminal window is not provided.
Subagent launches an isolated official Pi child and waits for exit. It uses the
optional model argument (provider/model-id, which must be an available Pi
model) or else the parent's model. It inherits the parent's thinking level
(clamped by Pi to the child model), cwd and the same explicitly selected
extension paths, and uses Pi's built-in tools. It does not inherit MCP clients
or load the packaged questionnaire/subagent adapter extension.
The execution owner generates task ids and supplies Core task metadata; Lody
presents the native task UI and forwards list/output/cancel requests.
Output queries retain the last 64 Ki characters for the current runtime.
Completed child processes are not resumed after adapter restart.
Child tasks are not detached background jobs or separate Lody sessions.
Pi's prompt response reports started, queued, or handled. A handled input
finishes without waiting for a model event; started and queued inputs wait for
native agent_settled, which covers the supported native model loop and
awaited tools, retries and automatic compaction. Manual compact instead waits for
its RPC result. Execution settlement is followed by output delivery and cleanup;
a new request cannot overtake this barrier.
Stop does not release admission immediately. It waits for preflight if necessary, cancels outstanding questions, clears queues, aborts Pi, and waits for request cleanup. A transport failure fails the request. Connection close terminates the owned process group, including non-detached child agents.
On Unix, V1 relies on Pi's cooperative cleanup of its detached shell commands. If Pi is forcibly killed or crashes before that cleanup runs, commands in separate process groups can survive the adapter and continue writing files or running services. They may require manual termination. This is an accepted V1 limitation: there is no additional watchdog or replacement shell executor. Normal Stop and connection-close cleanup remain supported. Unexpected Pi exit fails the connection with a nonzero adapter exit code; it never reports success or retries the prompt. Resume requires an explicit new connection and does not clean up orphaned commands.
On Windows, the adapter joins a Windows Job before starting Pi. Adapter exit releases the Job and terminates remaining descendants, including MCP servers. Unexpected Pi exit ends the connection with a nonzero adapter exit code; native session files remain available for explicit resume through a new connection. This adds no worker process. Missing or unusable native support fails startup.
Ordinary diagnostics do not replace the model's terminal outcome. Session file changes cannot silently retarget ACP identity. No extension may enqueue work after its tool returns or replace the parent session.
V1 does not support arbitrary community or official-example plugin compatibility, remote or package-spec extension sources, ambient or project extension discovery, Plan Mode, presets/tool-selection plugins, extension-triggered compaction, hot reload, handoff/session-navigation plugins, permission/environment plugins, terminal UI adaptation, background child jobs or recursive subagent tools.
Windows source development requires pnpm build:native with the Visual C++
toolchain and Python available. Published packages carry CI-built Node-API
modules for Windows x64 and ARM64; end users do not compile them. The Check
workflow assembles the distributable tarball only after both Windows jobs pass.
pnpm check
pnpm build
pnpm smoke
pnpm packUnit tests protect transport failure, session identity, steering, final outcomes, cancellation and delivery ordering. The native smoke uses the official CLI and a local synthetic model endpoint. It checks question answers/cancellation, todo recovery, subagent lifecycle/output/cancel, ambient extension exclusion, and an explicitly selected provider extension reaching both the parent and a native subagent. The entire smoke runs in ordinary-tool and codemode-only modes, including stats, manual compaction and extension commands handled without a model turn. It verifies real file read/write/edit and shell effects, image input and MCP images in the model request, per-response usage, steering, provider-error recovery and explicit child model selection and tool isolation. The same script accepts an installed dist/index.js path.
Upgrade decision and verification limits: Pi 1.4 runtime note. The previous runtime contract is recorded in the Pi 1.3 runtime note.
The separate Electron acceptance in that note covers one real provider, a locally served checksummed managed runtime, and a host-side image attachment submission limit. It does not establish other providers' quality or public runtime publication. Historical checks of the previous SDK implementation do not validate this architecture.
When Pi exits during startup, adapter failures include only a short extension diagnostics allowlist: load failures, tool-name conflicts and extension-related hints. Other startup failures remain on the process stream and can be added to the allowlist later when their presentation is defined; unrelated output is never forwarded through ACP.
The adapter advertises sessionCapabilities.systemPrompt.mode.override and accepts
standard ACP systemPrompt on session/new, session/load, session/resume, and
session/fork:
{
"systemPrompt": {
"mode": "append",
"content": [{ "type": "text", "text": "Use the project's terminology." }]
}
}append adds instructions to the native prompt; override replaces its base
instructions. Content must contain at least one text block; unsupported modes or
content return invalidParams (-32602). These instructions do not change tool
permissions or remove required runtime context. They apply to the requested
session, not sibling sessions. Pass them again when reconnecting or forking to
ensure the same instructions are used.
The root Pi agent applies these instructions before each turn; subagents retain
their own native instructions. Workspace context is always retained. Load/resume
replaces the activation's instructions rather than accumulating them; omission or
null restores native defaults. Instructions stay outside native session history.