pi-landstrip
Sandbox-aware Pi subagents and interactive command permissions
Package details
Install pi-landstrip from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-landstrip- Package
pi-landstrip- Version
0.17.34- Published
- Jul 22, 2026
- Downloads
- 11.5K/mo · 4,332/wk
- Author
- jarkkojs
- License
- Apache-2.0
- Types
- extension
- Size
- 200.1 KB
- Dependencies
- 2 dependencies · 3 peers
Pi manifest JSON
{
"extensions": [
"./dist/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-landstrip
Subagents 3 active
├─ ● running @explore Design Pi entry parsing
├─ ● running @general Design trigger serialization
└─ ● running @general Design bounded summaries
pi-landstrip is a Pi extension that provides sandboxed Bash
execution, OpenCode-compatible primary agents, and sandbox-aware subagents. It
uses an Anthropic-compatible policy and delegates OS-level enforcement to
landstrip.
The extension includes a shared sandbox policy and separate agent configuration. Global and trusted-project files can override both.
Process-backed subagents require Pi >= 0.80.6 and < 0.82.0, and Node.js >= 22.19.0.
Installation
Automatic install
pi install npm:pi-landstrip
This installs pi-landstrip and its @landstrip/landstrip dependency. Native
binaries are published for Linux, macOS, and Windows on x64 and Arm64.
Manual install
Add the extension to ~/.pi/agent/settings.json (global) or
.pi/settings.json (project):
{
"packages": ["npm:pi-landstrip"]
}
Alternatively, place the extension under ~/.pi/agent/extensions/ (global) or
.pi/extensions/ (project). See Pi's
extension documentation for details.
On unsupported platforms the extension loads but leaves sandboxing disabled.
Disabling
Use the --no-sandbox flag, or set enabled to false in sandbox.json:
{
"enabled": false
}
When sandboxing is explicitly disabled, subagents still run as separate Pi RPC processes, but without the outer Landstrip OS sandbox. The extension warns once per session. Agent tool permissions still apply, but they are not an OS isolation boundary.
Trusted project config overrides global config. /sandbox updates a trusted
project sandbox file when present, otherwise the global file. Pi versions
without a project-trust API use only global configuration.
Behavior
When Pi requests a sandboxed permission, the extension sends a host notification and opens a dialog. The user can allow once, allow for the session, persist for the project or globally, or reject. The dialog shows the exact path or domain being approved.
Project approvals are written to .pi/sandbox.json; global approvals are
written to ~/.pi/agent/sandbox.json.
The main agent remains a normal Pi process. pi-landstrip replaces Bash
execution, including AI bash calls and manually typed shell commands (! and
!!), with a Landstrip-wrapped implementation. Network traffic uses an
allowlist proxy when direct access is disabled. Pi's filesystem tools and plugin
callbacks remain trusted code outside the Landstrip sandbox.
By default, Bash and subagent processes have no direct network access. Reads are
limited to the project, Git configuration, and /dev/null; writes are limited
to the project and /dev/null.
Subagent startup adds read-only bootstrap access to the selected Pi runtime,
global settings, model/auth configuration, installed plugins, skills, and the
task's dedicated session directory. These paths are required to construct a
normal Pi worker and are not persisted into sandbox.json. The worker receives
write access only to its own session and temporary directories.
Use /sandbox to inspect the active policy and toggle sandboxing. Use /agents
to select the primary role, inspect worker configuration and status, and set the
global or trusted-project concurrency limit.
Primary agents
The /agents selector provides OpenCode-compatible build and plan roles by
default. Build has normal development access; plan asks before shell commands
and file changes. The selection controls the root system prompt and permissions
and is restored with the session.
Subagents
Landstrip provides an OpenCode-compatible task tool. Each active task runs as a
full pi --mode rpc process inside an outer Landstrip sandbox. The sandbox
covers Pi, its plugins and tools, model requests, and descendant processes. The
root Pi process supervises RPC, permissions, nesting, persistence, and result
delivery.
Workers use normal Pi resource discovery and plugin loading, plus the
pi-landstrip worker extension. Requested tools are composed inside the worker,
so plugins can add or replace implementations. Each task starts a fresh Pi
process and plugin instances; continuing a task_id restores its persisted Pi
session in a new process.
The tool accepts the OpenCode task fields:
{
"description": "Review sandbox boundary",
"prompt": "Review the sandbox implementation and report concrete issues.",
"subagent_type": "review",
"task_id": "optional-session-id",
"command": "optional-originating-command",
"background": false
}
Foreground tasks return the child result directly. Background tasks return a
queued result and deliver completion automatically. Task rows show lifecycle
state, current activity, tool-call count, elapsed time, and expandable output.
Use /subagents or the task row's /subagents <id> command to inspect a child
transcript. Completed and failed task metadata remains available after reload,
and persisted sessions can be continued with task_id.
Session switching or shutdown stops live workers. After an unclean restart, unfinished work is marked interrupted; completed but undelivered background results are delivered when the root session resumes.
OpenCode-style permissions wrap each worker's composed tools: deny blocks,
ask prompts in the root UI, and allow runs the tool. Other workers may
continue while a prompt is open. Forwarded worker dialogs identify the agent,
task summary, and task ID. An "Allow for this session" decision applies to the
root session and its descendants.
Tool permissions cannot widen the outer Landstrip policy. Sandbox approvals
continue to use .pi/sandbox.json and ~/.pi/agent/sandbox.json.
Subagents fail closed when sandboxing is expected but unavailable, unsupported,
or missing. An explicit --no-sandbox flag or enabled: false configuration is
treated as an intentional opt-out and uses the warned unsandboxed process path.
Unsupported Pi versions still fail task startup.
Platform behavior
- Linux: seccomp query traps can grant a blocked filesystem or network operation dynamically, so an approved worker can continue without restart.
- macOS: Seatbelt policy is fixed when the worker starts. Update
sandbox.json, then restart or continue the task in a fresh worker to apply additional filesystem access. Main-agent Bash can retry a command, but there is no live worker-policy update. - Windows: AppContainer cannot permit only the loopback proxy port, so
domain-proxied worker networking is unavailable and the worker has no network.
Setting
network.allowNetworktotrueis a degraded mode that permits model access but gives the entire worker unrestricted network access; domain lists are then not a boundary.
Credentials
Model requests originate inside each worker. Pi's auth.json is readable but
not writable by workers. Inherited credential environment variables also cross
the sandbox boundary, and plugins loaded in that worker share access to these
credentials. Landstrip does not provide a credential broker: load only trusted
plugins and use credentials appropriate for the sandboxed process. An OAuth
refresh that needs to rewrite auth.json must be completed in the root Pi
first.
Configuration
Subagent configuration is read from ~/.pi/agent/subagents.json and, for trusted
projects, .pi/subagents.json. Project values override global values; both are
merged over the packaged defaults. Sandbox policy remains in
~/.pi/agent/sandbox.json and .pi/sandbox.json. Pi's settings.json contains
only normal Pi settings, such as the pi-landstrip package entry.
subagents.json accepts only top-level maxSubagents and subagents; sandbox
fields belong in sandbox.json.
{
"maxSubagents": 2,
"subagents": {
"agent": {
"review": {
"description": "Review code without modifying it",
"mode": "subagent",
"prompt": "Review the requested code and report concrete findings.",
"permission": {
"edit": "deny",
"bash": "ask"
}
}
},
"permission": {
"task": {
"*": "deny",
"review": "allow"
}
}
}
}
maxSubagents is an integer from 0 through 16 controlling concurrent workers.
The packaged default is 0, which removes the task tool while retaining primary
roles. There is no separate subagent enable switch. The Settings tab in
/agents edits this limit for global and trusted-project configuration.
Agent modes, hidden/disabled agents, prompts, and ordered allow/ask/deny
permissions apply to primary agents and subagents. Subagent workers also honor
model selection, supported Pi thinking-level variants, and step limits. Primary
agent activation currently changes the prompt and permissions only. Later
matching permission rules win. Put provider-specific values under options;
unknown agent fields are rejected. Agent permissions cannot weaken an enabled
OS sandbox, grant filesystem access, or grant network access.
Configuration migration
There is no compatibility loader for previous subagent configuration sources.
Legacy keys in settings.json produce a migration warning. Move configuration as
follows, then remove the old fields:
- Move
agentandpermissionfrom Pisettings.jsonor OpenCode JSON undersubagents.agentandsubagents.permission. - Convert legacy
toolsbooleans to explicitpermissionrules. - Move Markdown agent prompts into each
subagents.agent.<name>.promptstring. - Put the worker limit in top-level
maxSubagents; zero disables delegation. - Use
~/.pi/agent/subagents.jsonfor global configuration and.pi/subagents.jsonfor trusted project configuration. - Leave sandbox policy in
~/.pi/agent/sandbox.jsonand.pi/sandbox.json; those files are unchanged.
Do not put these fields in Pi's settings.json; pi-landstrip does not read
them there.
Plugin API
Pi extensions can discover the active Landstrip runtime through the versioned
API exported by pi-landstrip/api:
import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
import { useLandstrip } from 'pi-landstrip/api';
export default function (pi: ExtensionAPI) {
const dispose = useLandstrip(pi, (landstrip) => {
const context = landstrip.getContext();
// Register Landstrip-aware behavior here.
});
pi.on('session_shutdown', dispose);
}
Discovery is callback-based so it works regardless of extension load order. Do not wait for discovery from an extension factory: Pi initializes extension factories sequentially. The returned function stops future notifications.
The runtime exposes:
getContext()for the host, role, sandbox state, task identity, agent and nesting depth.createBashTool()for extensions that need Landstrip execution with their own tool presentation.prepareProcess()for single-use process launches under the effective sandbox policy.registerWorkerExtension()to load an absolute extension entry in Landstrip subagents. The returned function removes the registration.on()for typedsandbox.changed,subagent.startandsubagent.endlifecycle events.
Registered worker extensions are trusted code. Landstrip adds their canonical
directories and dependency roots to worker read access, loads them with Pi's
--extension option, and propagates them to nested tasks.
Workers receive a base64url JSON LANDSTRIP_CONTEXT environment variable.
Extensions can read it without runtime discovery:
import { contextFromEnvironment } from 'pi-landstrip/api';
const context = contextFromEnvironment();
This public context is informational and contains no permission rules, policy, credentials or proxy secrets. Environment values can be spoofed outside a Landstrip worker and must not be treated as authorization.
Generic process preparation fails closed on Windows when sandboxing is disabled. Windows cannot reliably reclaim an unsandboxed descendant tree after its leader has exited; enabled Landstrip launches use an AppContainer job object instead.
Limits
pi-landstrip grants at most maxSubagents scheduler permits to active
subagent work and allows nesting to three levels. A foreground parent returns
its permit while waiting for a child and reacquires it before resuming,
preventing nested-task deadlocks. A worker receives a nested task tool only
when its agent has an explicit task permission. Nested tasks are separate Pi
processes supervised by the root and Landstrip-wrapped unless sandboxing is
explicitly disabled, so an inactive parent process can remain alive during the
handoff. Persisted sessions remain resumable by task_id.
License
pi-landstrip is licensed under Apache-2.0. See LICENSE.
The bundled @landstrip/landstrip package is licensed separately as
Apache-2.0 AND LGPL-2.1-or-later.