pi-landstrip 0.18.3 → 0.18.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +99 -293
  2. package/dist/index.ts +843 -294
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -1,32 +1,19 @@
1
1
  # pi-landstrip
2
2
 
3
- <img alt="Screenshot of Pi running pi-landstrip" src="screenshot.png" width="800">
4
-
5
- `pi-landstrip` adds sandboxed Bash, primary agents, and sandboxed subagents to
6
- [Pi](https://pi.dev/). OS isolation is provided by
3
+ `pi-landstrip` adds sandboxed Bash, primary agents, and process-backed subagents
4
+ to [Pi](https://pi.dev/). OS isolation is provided by
7
5
  [`landstrip`](https://github.com/landstrip/landstrip).
8
6
 
9
- The package includes a default [sandbox policy](./sandbox.json). Configure agents,
10
- subagent limits, permissions, and OpenCode imports under `landstrip` in Pi
11
- `settings.json`. Trusted project settings override global settings.
12
-
13
- Process-backed subagents require Pi >= 0.82.0, and Node.js >= 22.19.0.
7
+ Requires Pi 0.82.0 or later and Node.js 22.19.0 or later.
14
8
 
15
9
  ## Installation
16
10
 
17
- ### Automatic install
18
-
19
11
  ```sh
20
12
  pi install npm:pi-landstrip
21
13
  ```
22
14
 
23
- This installs `pi-landstrip` and its `@landstrip/landstrip` dependency. Native
24
- binaries are published for Linux, macOS, and Windows on x64 and Arm64.
25
-
26
- ### Manual install
27
-
28
- Add the extension to `~/.pi/agent/settings.json` (global) or
29
- `.pi/settings.json` (project):
15
+ For a manual install, add the package to global or trusted project
16
+ `settings.json`:
30
17
 
31
18
  ```json
32
19
  {
@@ -34,177 +21,93 @@ Add the extension to `~/.pi/agent/settings.json` (global) or
34
21
  }
35
22
  ```
36
23
 
37
- Alternatively, place the extension under `~/.pi/agent/extensions/` (global) or
38
- `.pi/extensions/` (project). See Pi's
39
- [extension documentation](https://pi.dev/docs/latest/extensions) for details.
40
-
41
- On unsupported platforms the extension loads but leaves sandboxing disabled.
42
-
43
- Pi's Git Bash/MSYS shell cannot start in LPAC, so the default Windows policy uses
44
- standard AppContainer. `/sandbox` and the status line show the active Windows
45
- isolation. LPAC launch failures return an error.
46
-
47
- Restricted-user isolation supports Git Bash with restricted local accounts. See
48
- [Windows restricted-user installation](#windows-restricted-user-installation).
49
-
50
- ## Disabling
51
-
52
- Use the `--no-sandbox` flag, or set `enabled` to `false` in `sandbox.json`:
53
-
54
- ```json
55
- {
56
- "enabled": false
57
- }
58
- ```
59
-
60
- When sandboxing is explicitly disabled, subagents still run as separate Pi RPC
61
- processes, but without the outer Landstrip OS sandbox. The extension warns once
62
- per session. Agent tool permissions still apply, but they are not an OS isolation
63
- boundary.
64
-
65
- Trusted project config overrides global config. `/sandbox` updates a trusted
66
- project sandbox file when present, otherwise the global file. Pi versions
67
- without a project-trust API use only global configuration.
24
+ The package includes native Linux, macOS, and Windows binaries for x64 and
25
+ Arm64. On unsupported platforms, the extension loads with sandboxing disabled.
68
26
 
69
27
  ## Behavior
70
28
 
71
- When a command requests additional access, the extension opens an approval dialog.
72
- The user can allow once, allow for the session, save the approval globally or for the
73
- project, or reject it. Project approvals go to `.pi/sandbox.json`; global approvals
74
- go to `~/.pi/agent/sandbox.json`.
29
+ AI Bash calls and shell commands entered with `!` or `!!` run through
30
+ Landstrip. Subagents run as separate Pi RPC processes and are sandboxed unless
31
+ the sandbox is explicitly disabled.
75
32
 
76
- The main agent remains a normal Pi process. `pi-landstrip` replaces Bash execution,
77
- including AI `bash` calls and manually typed shell commands (`!` and `!!`), with a
78
- Landstrip-wrapped implementation. Network traffic uses an allowlist proxy when
79
- direct access is disabled and the platform policy permits loopback; otherwise it is
80
- blocked. Pi's filesystem tools and plugin callbacks remain trusted code outside the
81
- Landstrip sandbox.
33
+ The root Pi process is trusted. Pi filesystem tools and plugin callbacks run
34
+ outside the OS sandbox. Agent permissions still control tool dispatch, but they
35
+ are not an OS isolation boundary.
82
36
 
83
- By default, Bash and subagent processes have no direct network access. Reads are
84
- limited to the project, Git configuration, and `/dev/null`; writes are limited
85
- to the project and `/dev/null`.
37
+ The default [sandbox policy](./sandbox.json) limits reads and writes to the
38
+ project and a few bootstrap files. Direct network access is blocked. When the
39
+ platform permits loopback, the extension's proxy filters allowed HTTP and HTTPS
40
+ domains.
86
41
 
87
- Subagent startup adds read-only bootstrap access to the selected Pi runtime,
88
- global settings, model/auth configuration, installed plugins, skills, and the
89
- task's dedicated session directory. These paths are required to construct a
90
- normal Pi worker and are not persisted into `sandbox.json`. The worker receives
91
- write access only to its own session and temporary directories.
42
+ When a command needs more access, Pi can allow it once, for the session, or in
43
+ the project or global policy. Project approvals go to `.pi/sandbox.json` and
44
+ global approvals to `~/.pi/agent/sandbox.json`.
92
45
 
93
- Use `/sandbox` to inspect or disable the sandbox. Use `/agents` to select a primary
94
- agent, inspect primary agents, subagents, and tasks, and set subagent limits.
46
+ - `/sandbox` inspects and toggles the sandbox.
47
+ - `/agents` selects agents, edits agent settings, and shows task sessions.
48
+ - `Ctrl+Shift+A` cycles visible primary agents while Pi is idle.
95
49
 
96
- ## Permission model
50
+ ### Disabling the sandbox
97
51
 
98
- Agent and sandbox permissions apply at different stages:
52
+ Use `--no-sandbox` or set `enabled` to `false` in `sandbox.json`:
99
53
 
100
- | Layer | Checked before or during | Controls |
101
- | ------------------ | ------------------------ | ------------------------------------------ |
102
- | Agent permission | Before tool dispatch | Which tools an agent may call |
103
- | Sandbox permission | During process execution | Filesystem and network access for commands |
104
-
105
- ```text
106
- agent → agent permission → tool → sandbox → OS resource
54
+ ```json
55
+ {
56
+ "enabled": false
57
+ }
107
58
  ```
108
59
 
109
- Each layer prompts independently. An agent approval allows tool dispatch; sandbox
110
- restrictions still apply to the resulting process.
111
-
112
- ## Primary agents
60
+ Subagents remain separate processes but lose Landstrip OS isolation. Pi warns
61
+ once per session. Trusted project config overrides global config; `/sandbox`
62
+ updates a project sandbox file when present, otherwise the global file.
113
63
 
114
- `/agents` lists all configured agents and their modes. Press Enter to activate a
115
- visible primary agent. The built-in primary agents are `build` and `plan`. Build has
116
- normal development access; plan asks before shell commands and file edits.
64
+ ### Permission layers
117
65
 
118
- The selected prompt and permissions are stored in the session. Press `Ctrl+Shift+A`
119
- while Pi is idle to cycle through visible primary agents.
66
+ | Layer | Checked | Controls |
67
+ | ---------------- | ------------------ | ------------------------------ |
68
+ | Agent permission | Before a tool runs | Which tools the agent may call |
69
+ | Sandbox policy | During a command | Filesystem and network access |
120
70
 
121
- ## Subagents
71
+ An agent approval allows tool dispatch; the sandbox still applies to the
72
+ resulting process.
122
73
 
123
- The `task` tool runs each subagent in a separate `pi --mode rpc` process inside a
124
- Landstrip sandbox. The root Pi process manages RPC, permissions, nesting, sessions,
125
- and results.
74
+ ## Agents
126
75
 
127
- Workers use normal Pi resource discovery and plugin loading, plus the
128
- `pi-landstrip` worker extension. Requested tools are composed inside the worker,
129
- so plugins can add or replace implementations. Each task starts a fresh Pi
130
- process and plugin instances; continuing a `task_id` restores its persisted Pi
131
- session in a new process.
76
+ The built-in primary agents are `build` and `plan`. The built-in subagents are
77
+ `explore`, `general`, and `scout`. `/agents` can activate, edit, or disable
78
+ configured agents.
132
79
 
133
- The tool accepts these fields:
80
+ The `task` tool starts a subagent. It accepts:
134
81
 
135
82
  ```json
136
83
  {
137
84
  "description": "Review sandbox boundary",
138
85
  "prompt": "Review the sandbox implementation and report concrete issues.",
139
- "subagent_type": "review",
86
+ "subagent_type": "explore",
140
87
  "task_id": "optional-session-id",
141
88
  "command": "optional-originating-command",
142
89
  "background": false
143
90
  }
144
91
  ```
145
92
 
146
- Foreground tasks return their result directly. Background tasks return immediately
147
- and report completion later. The **Tasks** tab in `/agents` shows task status and
148
- output. Open a task with `/agents <id>`. Continue a saved session with `task_id`.
149
-
150
- Session switching or shutdown stops live workers. After an unclean restart,
151
- unfinished work is marked interrupted; completed but undelivered background
152
- results are delivered when the root session resumes.
153
-
154
- Workers check agent permissions before tool dispatch: `deny` blocks, `ask` prompts in
155
- the root UI, and `allow` runs the tool. Required sandbox startup failures stop the
156
- task. With `--no-sandbox` or `enabled: false`, workers run without OS isolation and
157
- Pi shows a warning.
158
-
159
- ### Platform behavior
160
-
161
- - **Linux**: seccomp query traps can grant a blocked filesystem or network
162
- operation dynamically, so an approved worker can continue without restart.
163
- - **macOS**: Seatbelt policy is fixed when the worker starts. Update
164
- `sandbox.json`, then restart or continue the task in a fresh worker to apply
165
- additional filesystem access. Main-agent Bash can retry a command, but there
166
- is no live worker-policy update.
167
- - **Windows AppContainer**: the packaged policy selects standard AppContainer for
168
- Pi's Git Bash/MSYS runtime. With `windows.allowLoopback: false` (the default), the
169
- extension does not start an unreachable proxy and the worker has no network.
170
- Setting `windows.allowLoopback` to `true` enables the domain proxy, but also gives
171
- the AppContainer access to every local loopback service, not only the proxy port.
172
- Setting `network.allowNetwork` to `true` instead gives the entire worker
173
- unrestricted network access; domain lists are then not a boundary. Both opt-ins
174
- are explicit security downgrades and produce warnings.
175
- - **Windows restricted user**: WFP blocks restricted accounts except for the
176
- installed proxy port range, so domain-filtered proxying works without a broad
177
- loopback exemption. `network.allowNetwork: true` leases a separately provisioned
178
- unrestricted account. Filesystem grants are journaled and revoked after every
179
- process tree exits.
180
-
181
- ### Credentials
182
-
183
- Model requests originate inside each worker. Pi's `auth.json` is readable but
184
- not writable by workers. Inherited credential environment variables also cross
185
- the sandbox boundary, and plugins loaded in that worker share access to these
186
- credentials. Landstrip does not provide a credential broker: load only trusted
187
- plugins and use credentials appropriate for the sandboxed process. An OAuth
188
- refresh that needs to rewrite `auth.json` must be completed in the root Pi
189
- first.
93
+ `description`, `prompt`, and `subagent_type` are required. Foreground tasks
94
+ return their result directly. Background tasks return immediately and report
95
+ completion later. Continue a saved task with `task_id`; inspect it with
96
+ `/agents <id>`.
190
97
 
191
- ## Configuration
192
-
193
- Agent permissions are configured under `landstrip` in Pi `settings.json`. Sandbox
194
- permissions are configured in `sandbox.json`. The files use different schemas and
195
- control different stages.
98
+ Workers use normal Pi resource discovery and plugin loading. Each task starts a
99
+ fresh process; continuing a task restores its saved session in a new process.
100
+ Agent `deny`, `ask`, and `allow` rules are checked before tool dispatch.
196
101
 
197
- ### Agent policy (`settings.json`)
102
+ Model requests originate inside each worker. Workers can read Pi authentication
103
+ and inherited credential environment variables. Loaded worker plugins share
104
+ that access, so install only trusted plugins and use suitable credentials.
198
105
 
199
- Agent settings are read from `~/.pi/agent/settings.json` and, for trusted projects,
200
- `.pi/settings.json`. Built-in subagents are `explore`, `general`, and `scout`.
201
-
202
- Pi Markdown agents are loaded from `~/.pi/agent/agents/*.md` and, for trusted
203
- projects, `.pi/agents/*.md`. Nested directories are supported. Project Markdown
204
- agents override global Markdown agents. Settings under `landstrip.agent` override
205
- Markdown and OpenCode agents with the same name.
106
+ ## Configuration
206
107
 
207
- Add agent configuration under `landstrip` in Pi `settings.json`:
108
+ Agent settings are read from `~/.pi/agent/settings.json` and, for trusted
109
+ projects, `.pi/settings.json`. Sandbox settings are read from
110
+ `~/.pi/agent/sandbox.json` and `.pi/sandbox.json`.
208
111
 
209
112
  ```json
210
113
  {
@@ -214,7 +117,7 @@ Add agent configuration under `landstrip` in Pi `settings.json`:
214
117
  "review": {
215
118
  "description": "Review code without modifying it",
216
119
  "mode": "subagent",
217
- "prompt": "Review the requested code and report concrete findings.",
120
+ "prompt": "Report concrete findings.",
218
121
  "permission": {
219
122
  "edit": "deny",
220
123
  "bash": "ask"
@@ -231,163 +134,66 @@ Add agent configuration under `landstrip` in Pi `settings.json`:
231
134
  }
232
135
  ```
233
136
 
234
- `landstrip.maxSubagents` sets the maximum number of concurrent subagents from 0 to 16. The default is 1; zero disables `task`. The `/agents` **Settings** tab provides
235
- **Maximum subagents (global)** and **Maximum subagents (local)**.
236
-
237
- `landstrip.permission` applies to every agent. Each
238
- `landstrip.agent.<name>.permission` map adds agent-specific rules; later matching
239
- rules win. Agent definitions support `mode`, `prompt`, `model`, `variant`, `steps`,
240
- `color`, `disable`, and `allow`/`ask`/`deny` permissions. `{file:path}` prompt
241
- references are resolved from the defining `settings.json`. Provider-specific fields
242
- belong under `options`.
243
-
244
- `hidden` removes an agent from `/agents`. A hidden, subagent-capable agent remains
245
- available to `task`. Agent `mode` controls activation: only `primary` and `all` agents
246
- can be selected as primary, while only `subagent` and `all` agents can be passed to
247
- `task`. Missing, ambiguous, or unauthenticated primary-agent models produce an error
248
- and leave the current agent active.
249
-
250
- OpenCode integration flags also live in Pi `settings.json` under
251
- `landstrip.opencode`; both default to `true`:
252
-
253
- ```json
254
- {
255
- "packages": ["npm:pi-landstrip"],
256
- "landstrip": {
257
- "opencode": {
258
- "showGlobalAgents": true,
259
- "showLocalAgents": true
260
- }
261
- }
262
- }
263
- ```
264
-
265
- - `showGlobalAgents` imports `agent` definitions from `opencode.json` and
266
- `opencode.jsonc`, plus Markdown agents under `agent/` and `agents/`, in the
267
- OpenCode global config directory (`$OPENCODE_CONFIG_DIR`,
268
- `$XDG_CONFIG_HOME/opencode`, or `~/.config/opencode`).
269
- - `showLocalAgents` imports `agent` definitions from project `opencode.json` and
270
- `opencode.jsonc`, `.opencode/opencode.json` and `.opencode/opencode.jsonc`, and
271
- Markdown agents under `.opencode/agent/` and `.opencode/agents/`.
137
+ `landstrip.maxSubagents` is the concurrency limit, from 0 to 16. Zero disables
138
+ the `task` tool. Agent definitions support `mode`, `prompt`, `model`, `variant`,
139
+ `steps`, `color`, `hidden`, `disable`, `options`, and permission rules. Modes
140
+ are `primary`, `subagent`, or `all`.
272
141
 
273
- Set either flag to `false` to disable that OpenCode source. Project OpenCode agents
274
- override global OpenCode agents. Pi Markdown agents and `landstrip.agent` settings
275
- take precedence over OpenCode imports. Project sources require a trusted project.
276
- OpenCode config files support JSONC comments, trailing commas, and `{file:path}`
277
- prompt references.
142
+ Pi Markdown agents are loaded from `~/.pi/agent/agents/` and `.pi/agents/`.
143
+ `landstrip.agent` settings override Markdown agents with the same name.
144
+ Project definitions override global definitions.
278
145
 
279
- ### Sandbox policy (`sandbox.json`)
146
+ OpenCode agent imports are enabled by default. Set
147
+ `landstrip.opencode.showGlobalAgents` or `showLocalAgents` to `false` to disable
148
+ a source. OpenCode agents fill missing names; Pi agents take precedence.
149
+ Project imports require a trusted project.
280
150
 
281
- Sandbox settings are read from `~/.pi/agent/sandbox.json` and, for trusted projects,
282
- `.pi/sandbox.json`. They control runtime filesystem and network access.
151
+ See the main [Landstrip documentation](https://github.com/landstrip/landstrip#readme)
152
+ for `sandbox.json` fields and platform policy semantics.
283
153
 
284
- Configure Windows sandbox fields in `sandbox.json`:
154
+ ### Platform notes
285
155
 
286
- ```json
287
- {
288
- "windows": {
289
- "appContainerMode": "standard",
290
- "allowLoopback": false
291
- }
292
- }
293
- ```
294
-
295
- `appContainerMode` accepts `"lpac"` or `"standard"`. The Pi package defaults to
296
- standard mode for Git Bash. `allowLoopback` applies only to AppContainer. Landstrip
297
- selects Windows isolation automatically.
156
+ - **Linux:** blocked operations can be approved while the command is running.
157
+ - **macOS:** a worker's Seatbelt policy is fixed at startup; restart the task to
158
+ apply new filesystem access.
159
+ - **Windows:** the bundled policy uses standard AppContainer because Pi's Git
160
+ Bash cannot start in LPAC. Standard mode is weaker than LPAC.
161
+ `windows.allowLoopback` enables the domain proxy but exposes every local
162
+ loopback service. Restricted-user mode avoids that broad exemption.
298
163
 
299
- #### Windows restricted-user installation
300
-
301
- Provision restricted-user execution once to activate it:
164
+ Install restricted-user mode from the intended Windows host account, then
165
+ restart Pi:
302
166
 
303
167
  ```powershell
304
168
  npx @landstrip/landstrip windows install
305
169
  npx @landstrip/landstrip windows status
306
170
  ```
307
171
 
308
- Installation requests elevation and creates eight restricted-network accounts, two
309
- unrestricted-network accounts, and proxy ports 60080–60111 by default. Use
310
- `windows install --help` to change these values. Restart Pi after installation.
311
-
312
- Before each launch, the extension checks the installation. A failed health check
313
- stops the task. Restricted-user isolation does not support `windows.allowLoopback`
314
- or `network.allowLocalBinding`. Remove the accounts, WFP rules, runner, and ACL
315
- grants with:
316
-
317
- ```powershell
318
- npx @landstrip/landstrip windows uninstall
319
- ```
172
+ Remove it with `npx @landstrip/landstrip windows uninstall`.
320
173
 
321
174
  ## Plugin API
322
175
 
323
- Pi extensions can discover the active Landstrip runtime through the versioned
324
- API exported by `pi-landstrip/api`:
325
-
326
- ```ts
327
- import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
328
- import { useLandstrip } from 'pi-landstrip/api';
329
-
330
- export default function (pi: ExtensionAPI) {
331
- const dispose = useLandstrip(pi, (landstrip) => {
332
- const context = landstrip.getContext();
333
- // Register Landstrip-aware behavior here.
334
- });
335
- pi.on('session_shutdown', dispose);
336
- }
337
- ```
338
-
339
- Discovery is callback-based so it works regardless of extension load order.
340
- Do not wait for discovery from an extension factory: Pi initializes extension
341
- factories sequentially. The returned function stops future notifications.
176
+ Other Pi extensions can discover the runtime with `useLandstrip` from
177
+ `pi-landstrip/api`. The versioned runtime provides:
342
178
 
343
- The runtime exposes:
179
+ - `getContext()` for sandbox and task context.
180
+ - `createBashTool()` for Landstrip-backed command tools.
181
+ - `prepareProcess()` for one policy-aware process launch.
182
+ - `registerWorkerExtension()` for trusted worker extensions.
183
+ - `on()` for sandbox and subagent lifecycle events.
344
184
 
345
- - `getContext()` for the host, role, sandbox state, task identity, agent and
346
- nesting depth.
347
- - `createBashTool()` for extensions that need Landstrip execution with their
348
- own tool presentation.
349
- - `prepareProcess()` for single-use process launches under the effective
350
- sandbox policy.
351
- - `registerWorkerExtension()` to load an absolute extension entry in Landstrip
352
- subagents. The returned function removes the registration.
353
- - `on()` for typed `sandbox.changed`, `subagent.start` and `subagent.end`
354
- lifecycle events.
355
-
356
- Registered worker extensions are trusted code. Landstrip adds their canonical
357
- directories and dependency roots to worker read access, loads them with Pi's
358
- `--extension` option, and propagates them to nested tasks.
359
-
360
- Workers receive a base64url JSON `LANDSTRIP_CONTEXT` environment variable.
361
- Extensions can read it without runtime discovery:
362
-
363
- ```ts
364
- import { contextFromEnvironment } from 'pi-landstrip/api';
365
-
366
- const context = contextFromEnvironment();
367
- ```
368
-
369
- This public context is informational and contains no permission rules, policy,
370
- credentials or proxy secrets. Environment values can be spoofed outside a
371
- Landstrip worker and must not be treated as authorization.
372
-
373
- Generic process preparation fails closed on Windows when sandboxing is disabled.
374
- Windows cannot reliably reclaim an unsandboxed descendant tree after its leader
375
- has exited; enabled Landstrip launches use an AppContainer job object instead.
185
+ Discovery is callback-based and independent of extension load order. Worker
186
+ extensions are trusted code. Workers also receive an informational
187
+ `LANDSTRIP_CONTEXT` environment value; it is not proof of authorization.
376
188
 
377
189
  ## Limits
378
190
 
379
- `pi-landstrip` grants at most `maxSubagents` scheduler permits to active
380
- subagent work and allows nesting to three levels. A foreground parent returns
381
- its permit while waiting for a child and reacquires it before resuming,
382
- preventing nested-task deadlocks. A worker receives a nested `task` tool only
383
- when its agent has an explicit `task` permission. Nested tasks are separate Pi
384
- processes supervised by the root and Landstrip-wrapped unless sandboxing is
385
- explicitly disabled, so an inactive parent process can remain alive during the
386
- handoff. Persisted sessions remain resumable by `task_id`.
191
+ At most `maxSubagents` tasks run concurrently. Nested task depth is capped at
192
+ 3, and a nested `task` tool is available only when the parent agent has explicit
193
+ permission. Session switching and shutdown stop live workers.
387
194
 
388
195
  ## License
389
196
 
390
- `pi-landstrip` is licensed under `Apache-2.0`. See [LICENSE](LICENSE).
391
-
392
- The bundled `@landstrip/landstrip` package is licensed separately as
197
+ `pi-landstrip` is licensed under `Apache-2.0`. See [LICENSE](LICENSE). The
198
+ bundled `@landstrip/landstrip` package is licensed separately as
393
199
  `Apache-2.0 AND LGPL-2.1-or-later`.