pi-landstrip 0.17.36 → 0.17.38

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 +20 -10
  2. package/dist/index.ts +121 -157
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -208,17 +208,25 @@ merged over the packaged [defaults](./subagents.json). The packaged subagent typ
208
208
  are `explore`, `general`, and the OpenCode-compatible `scout` reconnaissance agent.
209
209
 
210
210
  Sandbox policy remains in `~/.pi/agent/sandbox.json` and `.pi/sandbox.json`. Pi's
211
- `settings.json` contains normal Pi settings, such as the `pi-landstrip` package
212
- entry, plus optional `landstrip.opencode` import flags:
211
+ `settings.json` only needs the normal package entry for the extension:
213
212
 
214
213
  ```json
215
214
  {
216
- "packages": ["npm:pi-landstrip"],
217
- "landstrip": {
218
- "opencode": {
219
- "showGlobalAgents": false,
220
- "showLocalAgents": false
221
- }
215
+ "packages": ["npm:pi-landstrip"]
216
+ }
217
+ ```
218
+
219
+ Optional OpenCode import flags live in `subagents.json` as top-level `opencode`:
220
+
221
+ ```json
222
+ {
223
+ "maxSubagents": 0,
224
+ "opencode": {
225
+ "showGlobalAgents": false,
226
+ "showLocalAgents": false
227
+ },
228
+ "subagents": {
229
+ "agent": {}
222
230
  }
223
231
  }
224
232
  ```
@@ -236,8 +244,8 @@ from `subagents.json` (built-in, global, and local) take precedence over OpenCod
236
244
  imports with the same name; conflicts are silent. Local OpenCode agents are
237
245
  skipped when the project is untrusted, regardless of the flag.
238
246
 
239
- `subagents.json` accepts only top-level `maxSubagents` and `subagents`; sandbox
240
- fields belong in `sandbox.json`.
247
+ `subagents.json` accepts top-level `maxSubagents`, `opencode`, and `subagents`;
248
+ sandbox fields belong in `sandbox.json`.
241
249
 
242
250
  The Windows sandbox fields are:
243
251
 
@@ -331,6 +339,8 @@ follows, then remove the old fields:
331
339
  - Convert legacy `tools` booleans to explicit `permission` rules.
332
340
  - Move Markdown agent prompts into each `subagents.agent.<name>.prompt` string.
333
341
  - Put the worker limit in top-level `maxSubagents`; zero disables delegation.
342
+ - Move `landstrip.opencode` flags from Pi `settings.json` to top-level
343
+ `opencode` in `subagents.json`.
334
344
  - Use `~/.pi/agent/subagents.json` for global configuration and
335
345
  `.pi/subagents.json` for trusted project configuration.
336
346
  - Leave sandbox policy in `~/.pi/agent/sandbox.json` and `.pi/sandbox.json`;
package/dist/index.ts CHANGED
@@ -3,10 +3,10 @@ import { spawn as spawn2, spawnSync } from "node:child_process";
3
3
  import { randomBytes } from "node:crypto";
4
4
  import { lookup } from "node:dns/promises";
5
5
  import {
6
- existsSync as existsSync6,
6
+ existsSync as existsSync5,
7
7
  mkdirSync as mkdirSync3,
8
8
  mkdtempSync as mkdtempSync2,
9
- readFileSync as readFileSync6,
9
+ readFileSync as readFileSync5,
10
10
  realpathSync as realpathSync2,
11
11
  rmSync as rmSync2,
12
12
  writeFileSync as writeFileSync3
@@ -19,12 +19,12 @@ import {
19
19
  Socket as NetSocket
20
20
  } from "node:net";
21
21
  import { homedir as homedir4, tmpdir as tmpdir2 } from "node:os";
22
- import { basename as basename3, dirname as dirname3, isAbsolute as isAbsolute2, join as join7, parse, resolve as resolve2 } from "node:path";
22
+ import { basename as basename3, dirname as dirname3, isAbsolute as isAbsolute2, join as join6, parse, resolve as resolve2 } from "node:path";
23
23
  import { StringDecoder as StringDecoder2 } from "node:string_decoder";
24
24
  import { fileURLToPath as fileURLToPath3, URL } from "node:url";
25
25
  import {
26
26
  createBashToolDefinition,
27
- getAgentDir as getAgentDir5,
27
+ getAgentDir as getAgentDir4,
28
28
  getShellConfig,
29
29
  SettingsManager,
30
30
  withFileMutationQueue as withFileMutationQueue2
@@ -70,21 +70,21 @@ function encodeLandstripContext(context) {
70
70
  // subagents.ts
71
71
  import { randomUUID } from "node:crypto";
72
72
  import {
73
- existsSync as existsSync5,
73
+ existsSync as existsSync4,
74
74
  lstatSync,
75
75
  mkdirSync as mkdirSync2,
76
76
  mkdtempSync,
77
- readFileSync as readFileSync5,
77
+ readFileSync as readFileSync4,
78
78
  readlinkSync,
79
79
  realpathSync,
80
80
  rmSync,
81
81
  writeFileSync as writeFileSync2
82
82
  } from "node:fs";
83
83
  import { homedir as homedir3, tmpdir } from "node:os";
84
- import { basename as basename2, dirname as dirname2, isAbsolute, join as join6, relative as relative2, resolve, sep as sep2 } from "node:path";
84
+ import { basename as basename2, dirname as dirname2, isAbsolute, join as join5, relative as relative2, resolve, sep as sep2 } from "node:path";
85
85
  import { fileURLToPath as fileURLToPath2 } from "node:url";
86
86
  import {
87
- getAgentDir as getAgentDir4,
87
+ getAgentDir as getAgentDir3,
88
88
  keyHint,
89
89
  SessionManager
90
90
  } from "@earendil-works/pi-coding-agent";
@@ -92,10 +92,10 @@ import { matchesKey, Text, truncateToWidth as truncateToWidth2 } from "@earendil
92
92
  import { Type } from "typebox";
93
93
 
94
94
  // agents.ts
95
- import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
96
- import { join as join5 } from "node:path";
95
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
96
+ import { join as join4 } from "node:path";
97
97
  import { minimatch } from "minimatch";
98
- import { getAgentDir as getAgentDir3 } from "@earendil-works/pi-coding-agent";
98
+ import { getAgentDir as getAgentDir2 } from "@earendil-works/pi-coding-agent";
99
99
 
100
100
  // config.ts
101
101
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
@@ -160,6 +160,38 @@ class AsyncQueue {
160
160
  // config.ts
161
161
  var MAX_SUBAGENTS = 16;
162
162
  var packageDir = dirname(fileURLToPath(import.meta.url));
163
+ var DEFAULT_OPENCODE = {
164
+ showGlobalAgents: false,
165
+ showLocalAgents: false
166
+ };
167
+ var OPENCODE_KEYS = new Set(["showGlobalAgents", "showLocalAgents"]);
168
+ function readBooleanField(section, key, path) {
169
+ if (!(key in section))
170
+ return;
171
+ const value = section[key];
172
+ if (typeof value === "boolean")
173
+ return value;
174
+ throw new Error(`${path}.${key} must be a boolean`);
175
+ }
176
+ function normalizeOpenCode(value, path) {
177
+ if (value === undefined)
178
+ return {};
179
+ if (!isRecord2(value))
180
+ throw new Error(`${path} must be a JSON object`);
181
+ for (const key of Object.keys(value)) {
182
+ if (!OPENCODE_KEYS.has(key)) {
183
+ throw new Error(`${path}: unknown field ${key}`);
184
+ }
185
+ }
186
+ const result = {};
187
+ const showGlobalAgents = readBooleanField(value, "showGlobalAgents", path);
188
+ if (showGlobalAgents !== undefined)
189
+ result.showGlobalAgents = showGlobalAgents;
190
+ const showLocalAgents = readBooleanField(value, "showLocalAgents", path);
191
+ if (showLocalAgents !== undefined)
192
+ result.showLocalAgents = showLocalAgents;
193
+ return result;
194
+ }
163
195
  function readConfig(path) {
164
196
  if (!existsSync(path))
165
197
  return {};
@@ -173,11 +205,27 @@ function readConfig(path) {
173
205
  if (!isRecord2(value))
174
206
  throw new Error(`${path} must contain a JSON object`);
175
207
  for (const key of Object.keys(value)) {
176
- if (key !== "maxSubagents" && key !== "subagents") {
208
+ if (key !== "maxSubagents" && key !== "subagents" && key !== "opencode") {
177
209
  throw new Error(`${path}: unknown top-level field ${key}`);
178
210
  }
179
211
  }
180
- return value;
212
+ const config = {};
213
+ if ("maxSubagents" in value) {
214
+ if (typeof value.maxSubagents !== "number") {
215
+ throw new Error(`${path}: maxSubagents must be a number`);
216
+ }
217
+ config.maxSubagents = value.maxSubagents;
218
+ }
219
+ if ("subagents" in value) {
220
+ if (!isRecord2(value.subagents)) {
221
+ throw new Error(`${path}: subagents must be an object`);
222
+ }
223
+ config.subagents = value.subagents;
224
+ }
225
+ if ("opencode" in value) {
226
+ config.opencode = normalizeOpenCode(value.opencode, `${path}: opencode`);
227
+ }
228
+ return config;
181
229
  }
182
230
  function mergeValue(base, override) {
183
231
  if (override === undefined)
@@ -239,7 +287,11 @@ function loadLandstripConfig(cwd, includeProject = true, agentDir = getAgentDir(
239
287
  }
240
288
  if (!isRecord2(config.subagents))
241
289
  throw new Error("subagents must be an object");
242
- return { ...config, agentSources };
290
+ const opencode = {
291
+ ...DEFAULT_OPENCODE,
292
+ ...config.opencode
293
+ };
294
+ return { ...config, opencode, agentSources };
243
295
  }
244
296
 
245
297
  // opencode-agents.ts
@@ -413,96 +465,6 @@ function loadOpenCodeAgents(options) {
413
465
  return { agents, diagnostics };
414
466
  }
415
467
 
416
- // settings.ts
417
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
418
- import { join as join4 } from "node:path";
419
- import { getAgentDir as getAgentDir2 } from "@earendil-works/pi-coding-agent";
420
- var DEFAULT_OPENCODE_SETTINGS = {
421
- showGlobalAgents: false,
422
- showLocalAgents: false
423
- };
424
- var LANDSTRIP_SETTING_KEYS = new Set(["opencode"]);
425
- var OPENCODE_SETTING_KEYS = new Set(["showGlobalAgents", "showLocalAgents"]);
426
- function readJsonObject(path) {
427
- if (!existsSync3(path))
428
- return {};
429
- try {
430
- const parsed = JSON.parse(readFileSync3(path, "utf8"));
431
- if (!isRecord2(parsed)) {
432
- return { warning: `${path}: settings.json must contain a JSON object` };
433
- }
434
- return { value: parsed };
435
- } catch (error) {
436
- const message = error instanceof Error ? error.message : String(error);
437
- return { warning: `${path}: ${message}` };
438
- }
439
- }
440
- function readBoolean(section, key, path, source, warnings) {
441
- if (!(key in section))
442
- return;
443
- const value = section[key];
444
- if (typeof value === "boolean")
445
- return value;
446
- warnings.push(`${source}: ${path} must be a boolean`);
447
- return;
448
- }
449
- function readOpenCodeSection(section, source, warnings) {
450
- if (!("opencode" in section))
451
- return {};
452
- if (!isRecord2(section.opencode)) {
453
- warnings.push(`${source}: landstrip.opencode must be a JSON object`);
454
- return {};
455
- }
456
- const opencode = section.opencode;
457
- for (const key of Object.keys(opencode)) {
458
- if (!OPENCODE_SETTING_KEYS.has(key)) {
459
- warnings.push(`${source}: unknown landstrip.opencode setting ${key}`);
460
- }
461
- }
462
- const settings = {};
463
- const showGlobalAgents = readBoolean(opencode, "showGlobalAgents", "landstrip.opencode.showGlobalAgents", source, warnings);
464
- if (showGlobalAgents !== undefined)
465
- settings.showGlobalAgents = showGlobalAgents;
466
- const showLocalAgents = readBoolean(opencode, "showLocalAgents", "landstrip.opencode.showLocalAgents", source, warnings);
467
- if (showLocalAgents !== undefined)
468
- settings.showLocalAgents = showLocalAgents;
469
- return settings;
470
- }
471
- function readLandstripSection(root, source, warnings) {
472
- if (!root || !("landstrip" in root))
473
- return {};
474
- if (!isRecord2(root.landstrip)) {
475
- warnings.push(`${source}: landstrip must be a JSON object`);
476
- return {};
477
- }
478
- const section = root.landstrip;
479
- for (const key of Object.keys(section)) {
480
- if (!LANDSTRIP_SETTING_KEYS.has(key)) {
481
- warnings.push(`${source}: unknown landstrip setting ${key}`);
482
- }
483
- }
484
- return readOpenCodeSection(section, source, warnings);
485
- }
486
- function resolveLandstripSettings(cwd, includeProject = true, piAgentDir = getAgentDir2()) {
487
- const warnings = [];
488
- const globalPath = join4(piAgentDir, "settings.json");
489
- const projectPath = join4(cwd, ".pi", "settings.json");
490
- const globalFile = readJsonObject(globalPath);
491
- if (globalFile.warning)
492
- warnings.push(globalFile.warning);
493
- const projectFile = includeProject ? readJsonObject(projectPath) : {};
494
- if (projectFile.warning)
495
- warnings.push(projectFile.warning);
496
- const settings = {
497
- opencode: {
498
- ...DEFAULT_OPENCODE_SETTINGS,
499
- ...readLandstripSection(globalFile.value, globalPath, warnings),
500
- ...includeProject ? readLandstripSection(projectFile.value, projectPath, warnings) : {}
501
- }
502
- };
503
- return { settings, warnings };
504
- }
505
-
506
468
  // agents.ts
507
469
  var AGENT_FIELDS = new Set([
508
470
  "name",
@@ -551,14 +513,14 @@ function normalizePermissions(value) {
551
513
  return Object.entries(value).flatMap(([permission, rules]) => permissionEntries(permission, rules));
552
514
  }
553
515
  function legacyConfigWarnings(piAgentDir) {
554
- const path = join5(piAgentDir, "settings.json");
555
- if (!existsSync4(path))
516
+ const path = join4(piAgentDir, "settings.json");
517
+ if (!existsSync3(path))
556
518
  return [];
557
519
  try {
558
- const value = JSON.parse(readFileSync4(path, "utf8"));
520
+ const value = JSON.parse(readFileSync3(path, "utf8"));
559
521
  if (!isRecord2(value))
560
522
  return [];
561
- const fields = ["agent", "permission", "subagents", "maxSubagents"].filter((field) => value[field] !== undefined);
523
+ const fields = ["agent", "permission", "subagents", "maxSubagents", "landstrip"].filter((field) => value[field] !== undefined);
562
524
  if (fields.length === 0)
563
525
  return [];
564
526
  return [
@@ -618,29 +580,31 @@ function normalizeAgent(name, raw, source) {
618
580
  providerOptions
619
581
  };
620
582
  }
621
- function loadAgentCatalog(cwd, piAgentDir = getAgentDir3(), includeProject = true) {
583
+ function loadAgentCatalog(cwd, piAgentDir = getAgentDir2(), includeProject = true) {
622
584
  const warnings = [
623
585
  ...legacyConfigWarnings(piAgentDir),
624
- ...includeProject ? legacyConfigWarnings(join5(cwd, ".pi")) : []
586
+ ...includeProject ? legacyConfigWarnings(join4(cwd, ".pi")) : []
625
587
  ];
626
588
  const diagnostics = [];
627
589
  let maxSubagents = 0;
628
590
  let subagents = {};
629
591
  let agentSources = new Map;
592
+ let showGlobalAgents = false;
593
+ let showLocalAgents = false;
630
594
  try {
631
595
  const config = loadLandstripConfig(cwd, includeProject, piAgentDir);
632
596
  maxSubagents = config.maxSubagents;
633
597
  subagents = config.subagents;
634
598
  agentSources = new Map(config.agentSources);
599
+ showGlobalAgents = config.opencode.showGlobalAgents;
600
+ showLocalAgents = config.opencode.showLocalAgents;
635
601
  } catch (error) {
636
602
  diagnostics.push(formatError(error));
637
603
  }
638
- const { settings, warnings: settingsWarnings } = resolveLandstripSettings(cwd, includeProject, piAgentDir);
639
- warnings.push(...settingsWarnings);
640
604
  const openCode = loadOpenCodeAgents({
641
605
  cwd,
642
- includeGlobal: settings.opencode.showGlobalAgents,
643
- includeProject: settings.opencode.showLocalAgents && includeProject
606
+ includeGlobal: showGlobalAgents,
607
+ includeProject: showLocalAgents && includeProject
644
608
  });
645
609
  diagnostics.push(...openCode.diagnostics);
646
610
  const normalized = new Map;
@@ -1183,8 +1147,8 @@ function resolvePiPackage() {
1183
1147
  const entry = fileURLToPath2(import.meta.resolve("@earendil-works/pi-coding-agent"));
1184
1148
  let dir2 = dirname2(entry);
1185
1149
  for (;; ) {
1186
- const pkgPath = join6(dir2, "package.json");
1187
- if (existsSync5(pkgPath)) {
1150
+ const pkgPath = join5(dir2, "package.json");
1151
+ if (existsSync4(pkgPath)) {
1188
1152
  const pkg = readPiPackage(pkgPath);
1189
1153
  if (pkg)
1190
1154
  return pkg;
@@ -1197,8 +1161,8 @@ function resolvePiPackage() {
1197
1161
  } catch {}
1198
1162
  let dir = packageDir2;
1199
1163
  for (;; ) {
1200
- const pkgPath = join6(dir, "node_modules", "@earendil-works", "pi-coding-agent", "package.json");
1201
- if (existsSync5(pkgPath)) {
1164
+ const pkgPath = join5(dir, "node_modules", "@earendil-works", "pi-coding-agent", "package.json");
1165
+ if (existsSync4(pkgPath)) {
1202
1166
  const pkg = readPiPackage(pkgPath);
1203
1167
  if (pkg)
1204
1168
  return pkg;
@@ -1211,7 +1175,7 @@ function resolvePiPackage() {
1211
1175
  }
1212
1176
  function readPiPackage(pkgPath) {
1213
1177
  try {
1214
- const value = JSON.parse(readFileSync5(pkgPath, "utf8"));
1178
+ const value = JSON.parse(readFileSync4(pkgPath, "utf8"));
1215
1179
  if (!isRecord2(value) || value.name !== "@earendil-works/pi-coding-agent" || typeof value.version !== "string") {
1216
1180
  return;
1217
1181
  }
@@ -1221,8 +1185,8 @@ function readPiPackage(pkgPath) {
1221
1185
  const binFile = typeof value.bin === "string" ? value.bin : isRecord2(value.bin) && typeof value.bin.pi === "string" ? value.bin.pi : undefined;
1222
1186
  if (!binFile)
1223
1187
  return;
1224
- const cliEntry = join6(dirname2(pkgPath), binFile);
1225
- if (!existsSync5(cliEntry))
1188
+ const cliEntry = join5(dirname2(pkgPath), binFile);
1189
+ if (!existsSync4(cliEntry))
1226
1190
  return;
1227
1191
  cachedPiPackage = { cliEntry, version: parts.slice(0, 3) };
1228
1192
  return cachedPiPackage;
@@ -1296,7 +1260,7 @@ function messageContentText(message) {
1296
1260
  `);
1297
1261
  }
1298
1262
  function taskTranscript(task) {
1299
- if (!task.sessionFile || !existsSync5(task.sessionFile)) {
1263
+ if (!task.sessionFile || !existsSync4(task.sessionFile)) {
1300
1264
  const fallback = task.error ?? task.output ?? "Child session is not available yet.";
1301
1265
  return fallback.split(`
1302
1266
  `);
@@ -1347,7 +1311,7 @@ function agentBootstrapPaths(agentDir) {
1347
1311
  "bin",
1348
1312
  "npm",
1349
1313
  "git"
1350
- ].map((path) => join6(agentDir, path));
1314
+ ].map((path) => join5(agentDir, path));
1351
1315
  }
1352
1316
 
1353
1317
  class Semaphore {
@@ -1760,7 +1724,7 @@ ${this.primaryAgent.prompt}` };
1760
1724
  });
1761
1725
  }
1762
1726
  getAgentCatalog(ctx) {
1763
- return this.loadCatalog(ctx.cwd, getAgentDir4(), isProjectTrusted(ctx));
1727
+ return this.loadCatalog(ctx.cwd, getAgentDir3(), isProjectTrusted(ctx));
1764
1728
  }
1765
1729
  getPrimaryAgent() {
1766
1730
  return this.primaryAgent;
@@ -1974,7 +1938,7 @@ ${this.primaryAgent.prompt}` };
1974
1938
  }
1975
1939
  createTaskTool(parentTask, callerRules, boundCtx) {
1976
1940
  const cwd = boundCtx?.cwd ?? process.cwd();
1977
- const catalog = this.loadCatalog(cwd, getAgentDir4(), boundCtx ? isProjectTrusted(boundCtx) : false);
1941
+ const catalog = this.loadCatalog(cwd, getAgentDir3(), boundCtx ? isProjectTrusted(boundCtx) : false);
1978
1942
  const agents = availableSubagents(catalog).filter((agent) => !agent.hidden && permissionDecision(callerRules ?? catalog.permissions, "task", agent.name) !== "deny");
1979
1943
  const descriptions = agents.map((agent) => `${agent.name}: ${agent.description ?? "No description"}`).join(`
1980
1944
  `);
@@ -2059,7 +2023,7 @@ ${theme.fg("dim", `↳ /subagents ${details.taskId.slice(0, 8)} to inspect`)}`;
2059
2023
  async execute(input, ctx, signal, parentTask, callerRules, update) {
2060
2024
  if (!input.prompt.trim())
2061
2025
  throw new Error("Task prompt cannot be empty");
2062
- const catalog = this.loadCatalog(ctx.cwd, getAgentDir4(), isProjectTrusted(ctx));
2026
+ const catalog = this.loadCatalog(ctx.cwd, getAgentDir3(), isProjectTrusted(ctx));
2063
2027
  for (const warning of catalog.warnings)
2064
2028
  ctx.ui.notify(warning, "warning");
2065
2029
  for (const diagnostic of catalog.diagnostics)
@@ -2099,7 +2063,7 @@ ${catalog.diagnostics.join(`
2099
2063
  if (task.agent !== agent.name) {
2100
2064
  throw new Error(`Task ${task.id} belongs to agent ${task.agent}, not ${agent.name}`);
2101
2065
  }
2102
- if (!this.running.has(task.id) && (!task.sessionDir || !existsSync5(task.sessionDir)) && (!task.sessionFile || !existsSync5(task.sessionFile))) {
2066
+ if (!this.running.has(task.id) && (!task.sessionDir || !existsSync4(task.sessionDir)) && (!task.sessionFile || !existsSync4(task.sessionFile))) {
2103
2067
  throw new Error(`Task ${task.id} session is unavailable`);
2104
2068
  }
2105
2069
  } else {
@@ -2209,7 +2173,7 @@ ${catalog.diagnostics.join(`
2209
2173
  createRecord(input, ctx, parentTask, agent, depth) {
2210
2174
  const parentSession = parentTask?.sessionFile ?? ctx.sessionManager.getSessionFile();
2211
2175
  const id = randomUUID();
2212
- const sessionDir = join6(getAgentDir4(), "sessions", "pi-landstrip", ctx.sessionManager.getSessionId(), id);
2176
+ const sessionDir = join5(getAgentDir3(), "sessions", "pi-landstrip", ctx.sessionManager.getSessionId(), id);
2213
2177
  mkdirSync2(sessionDir, { recursive: true });
2214
2178
  const task = {
2215
2179
  version: 1,
@@ -2399,7 +2363,7 @@ ${catalog.diagnostics.join(`
2399
2363
  "--mode",
2400
2364
  "rpc",
2401
2365
  "--extension",
2402
- join6(packageDir2, "index.ts"),
2366
+ join5(packageDir2, "index.ts"),
2403
2367
  ...workerExtensions.flatMap((extension) => ["--extension", extension.entry]),
2404
2368
  ...task.sessionFile ? ["--session", task.sessionFile] : task.sessionDir ? ["--session-dir", task.sessionDir] : [],
2405
2369
  "--model",
@@ -2414,8 +2378,8 @@ ${catalog.diagnostics.join(`
2414
2378
  ];
2415
2379
  const config = { rules, task, taskEnabled, steps: agent.steps };
2416
2380
  const publicContext = this.taskContext(task, ctx);
2417
- const temp = mkdtempSync(join6(tmpdir(), `pi-landstrip-task-${task.id}-`));
2418
- const agentDir = getAgentDir4();
2381
+ const temp = mkdtempSync(join5(tmpdir(), `pi-landstrip-task-${task.id}-`));
2382
+ const agentDir = getAgentDir3();
2419
2383
  let launch;
2420
2384
  let rpc;
2421
2385
  const abortWorker = () => void rpc?.stop().catch(() => {
@@ -2446,9 +2410,9 @@ ${catalog.diagnostics.join(`
2446
2410
  ...new Set([
2447
2411
  ctx.cwd,
2448
2412
  ...agentBootstrapPaths(agentDir),
2449
- join6(homedir3(), ".agents", "skills"),
2413
+ join5(homedir3(), ".agents", "skills"),
2450
2414
  packageDir2,
2451
- join6(packageDir2, "node_modules"),
2415
+ join5(packageDir2, "node_modules"),
2452
2416
  invocation.command,
2453
2417
  cliRoot,
2454
2418
  extensionRoot,
@@ -2670,7 +2634,7 @@ ${renderTaskResult(task.id, "error", message)}`);
2670
2634
  if (!directory)
2671
2635
  throw new Error(`Task ${task.id} has no artifact directory`);
2672
2636
  mkdirSync2(directory, { recursive: true });
2673
- const artifactPath = join6(directory, `${kind}.txt`);
2637
+ const artifactPath = join5(directory, `${kind}.txt`);
2674
2638
  const bounded = boundTaskOutput(value, artifactPath);
2675
2639
  if (kind === "output")
2676
2640
  task.outputFile = artifactPath;
@@ -2760,7 +2724,7 @@ ${renderTaskResult(task.id, failed ? "error" : "completed", value)}`;
2760
2724
  this.primaryAgent = undefined;
2761
2725
  this.primaryRules = undefined;
2762
2726
  this.primaryConfigurationError = false;
2763
- const catalog = this.loadCatalog(ctx.cwd, getAgentDir4(), isProjectTrusted(ctx));
2727
+ const catalog = this.loadCatalog(ctx.cwd, getAgentDir3(), isProjectTrusted(ctx));
2764
2728
  for (const warning of catalog.warnings)
2765
2729
  ctx.ui.notify(warning, "warning");
2766
2730
  this.maxSubagents = catalog.maxSubagents;
@@ -2916,23 +2880,23 @@ var NETWORK_PERMISSION_OPTIONS = [
2916
2880
  { label: "Abort (keep blocked)", key: "esc", action: "abort" }
2917
2881
  ];
2918
2882
  function loadSandboxConfig(cwd, includeProject) {
2919
- const projectConfigPath = join7(cwd, ".pi", "sandbox.json");
2920
- const globalConfigPath = join7(getAgentDir5(), "sandbox.json");
2921
- if (!existsSync6(globalConfigPath)) {
2922
- const templatePath = join7(packageDir3, "sandbox.json");
2883
+ const projectConfigPath = join6(cwd, ".pi", "sandbox.json");
2884
+ const globalConfigPath = join6(getAgentDir4(), "sandbox.json");
2885
+ if (!existsSync5(globalConfigPath)) {
2886
+ const templatePath = join6(packageDir3, "sandbox.json");
2923
2887
  mkdirSync3(dirname3(globalConfigPath), { recursive: true });
2924
- writeFileSync3(globalConfigPath, readFileSync6(templatePath, "utf-8"), "utf-8");
2888
+ writeFileSync3(globalConfigPath, readFileSync5(templatePath, "utf-8"), "utf-8");
2925
2889
  }
2926
- let globalConfig = JSON.parse(readFileSync6(join7(packageDir3, "sandbox.json"), "utf-8"));
2890
+ let globalConfig = JSON.parse(readFileSync5(join6(packageDir3, "sandbox.json"), "utf-8"));
2927
2891
  try {
2928
- const override = JSON.parse(readFileSync6(globalConfigPath, "utf-8"));
2892
+ const override = JSON.parse(readFileSync5(globalConfigPath, "utf-8"));
2929
2893
  globalConfig = deepMerge(globalConfig, override);
2930
2894
  } catch (error) {
2931
2895
  console.error(`Warning: Could not parse ${globalConfigPath}: ${error}`);
2932
2896
  }
2933
- if (includeProject && existsSync6(projectConfigPath)) {
2897
+ if (includeProject && existsSync5(projectConfigPath)) {
2934
2898
  try {
2935
- const projectConfig = JSON.parse(readFileSync6(projectConfigPath, "utf-8"));
2899
+ const projectConfig = JSON.parse(readFileSync5(projectConfigPath, "utf-8"));
2936
2900
  const merged = deepMerge(globalConfig, projectConfig);
2937
2901
  merged.windows.backend = globalConfig.windows.backend;
2938
2902
  return merged;
@@ -3007,10 +2971,10 @@ function getConfigPaths(cwd) {
3007
2971
  return getPiConfigPaths(cwd, "sandbox.json");
3008
2972
  }
3009
2973
  function readOrEmptyConfig(configPath) {
3010
- if (!existsSync6(configPath))
2974
+ if (!existsSync5(configPath))
3011
2975
  return {};
3012
2976
  try {
3013
- return JSON.parse(readFileSync6(configPath, "utf-8"));
2977
+ return JSON.parse(readFileSync5(configPath, "utf-8"));
3014
2978
  } catch {
3015
2979
  return {};
3016
2980
  }
@@ -3022,7 +2986,7 @@ function writeConfigFile(configPath, config) {
3022
2986
  }
3023
2987
  function getSandboxConfigWriteTarget(cwd, includeProject = true) {
3024
2988
  const { globalPath, projectPath } = getConfigPaths(cwd);
3025
- if (includeProject && existsSync6(projectPath)) {
2989
+ if (includeProject && existsSync5(projectPath)) {
3026
2990
  return { scope: "project", path: projectPath };
3027
2991
  }
3028
2992
  return { scope: "global", path: globalPath };
@@ -3098,7 +3062,7 @@ function canonicalizePath(filePath, cwd) {
3098
3062
  } catch {
3099
3063
  const tail = [];
3100
3064
  let probe = abs;
3101
- while (!existsSync6(probe)) {
3065
+ while (!existsSync5(probe)) {
3102
3066
  const parent = dirname3(probe);
3103
3067
  if (parent === probe)
3104
3068
  return abs;
@@ -3129,7 +3093,7 @@ function matchesPattern(filePath, patterns, cwd) {
3129
3093
  }
3130
3094
  function normalizeBlockedPath(path, cwd) {
3131
3095
  const nativePath = process.platform === "win32" && /^\/[a-zA-Z](?:\/|$)/.test(path) ? `${path[1]}:${path.slice(2)}` : path;
3132
- return canonicalizePath(isAbsolute2(nativePath) ? nativePath : join7(cwd, nativePath), cwd);
3096
+ return canonicalizePath(isAbsolute2(nativePath) ? nativePath : join6(cwd, nativePath), cwd);
3133
3097
  }
3134
3098
  function pathUnderDirectory(filePath, dir) {
3135
3099
  const normalizedFilePath = normalizePathSeparators(filePath);
@@ -3158,7 +3122,7 @@ function sessionScopeFor(filePath, baseDirectory) {
3158
3122
  const first = rest.split("/")[0];
3159
3123
  if (!first)
3160
3124
  return filePath;
3161
- return join7(boundary, first);
3125
+ return join6(boundary, first);
3162
3126
  }
3163
3127
  }
3164
3128
  if (pathUnderDirectory(dir, baseDirectory))
@@ -3431,8 +3395,8 @@ function writeEnvFile(env, proxyPort, proxyToken) {
3431
3395
  lines.push("export NO_PROXY=''");
3432
3396
  lines.push("export no_proxy=''");
3433
3397
  }
3434
- const dir = mkdtempSync2(join7(tmpdir2(), "pi-landstrip-env-"));
3435
- const path = join7(dir, "env.sh");
3398
+ const dir = mkdtempSync2(join6(tmpdir2(), "pi-landstrip-env-"));
3399
+ const path = join6(dir, "env.sh");
3436
3400
  writeFileSync3(path, lines.join(`
3437
3401
  `), "utf-8");
3438
3402
  return { dir, path };
@@ -3573,7 +3537,7 @@ function createLandstripIntegration(options = {}) {
3573
3537
  if (!isAbsolute2(requestedEntry)) {
3574
3538
  throw new Error("Worker extension entry must be an absolute path or file URL");
3575
3539
  }
3576
- if (!existsSync6(requestedEntry)) {
3540
+ if (!existsSync5(requestedEntry)) {
3577
3541
  throw new Error(`Worker extension entry does not exist: ${requestedEntry}`);
3578
3542
  }
3579
3543
  const entry = realpathSync2(requestedEntry);
@@ -3615,7 +3579,7 @@ function createLandstripIntegration(options = {}) {
3615
3579
  const paths = mergeAllowances(config.filesystem.allowRead, sessionAllowedReadPaths, allowances?.readPaths);
3616
3580
  if (process.platform !== "win32")
3617
3581
  return paths;
3618
- return paths.filter((path) => !path.startsWith("/") && (path.includes("*") || existsSync6(expandPath(path, cwd))));
3582
+ return paths.filter((path) => !path.startsWith("/") && (path.includes("*") || existsSync5(expandPath(path, cwd))));
3619
3583
  }
3620
3584
  function getEffectiveAllowWrite(config, allowances) {
3621
3585
  const paths = mergeAllowances(config.filesystem.allowWrite, sessionAllowedWritePaths, allowances?.writePaths);
@@ -3721,8 +3685,8 @@ function createLandstripIntegration(options = {}) {
3721
3685
  };
3722
3686
  }
3723
3687
  function writePolicyFile(cwd, proxyPort, allowances) {
3724
- const dir = mkdtempSync2(join7(tmpdir2(), "pi-landstrip-"));
3725
- const path = join7(dir, "policy.json");
3688
+ const dir = mkdtempSync2(join6(tmpdir2(), "pi-landstrip-"));
3689
+ const path = join6(dir, "policy.json");
3726
3690
  writeFileSync3(path, JSON.stringify(buildLandstripPolicy(cwd, proxyPort, allowances), null, 2) + `
3727
3691
  `, "utf-8");
3728
3692
  return { dir, path };
@@ -4149,7 +4113,7 @@ function createLandstripIntegration(options = {}) {
4149
4113
  function createLandstripBashOps(ctx, callbacks = {}, allowances = { domains: [], readPaths: [], writePaths: [], targets: [] }) {
4150
4114
  return {
4151
4115
  async exec(command, cwd, { onData, signal, timeout, env }) {
4152
- if (!existsSync6(cwd))
4116
+ if (!existsSync5(cwd))
4153
4117
  throw new Error(`Working directory does not exist: ${cwd}`);
4154
4118
  const { shell, args } = getShellConfig(SettingsManager.create(cwd).getShellPath());
4155
4119
  const config = loadConfig(cwd);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-landstrip",
3
- "version": "0.17.36",
3
+ "version": "0.17.38",
4
4
  "description": "Sandbox-aware Pi subagents and interactive command permissions",
5
5
  "keywords": [
6
6
  "landstrip",
@@ -40,7 +40,7 @@
40
40
  "ci:test": "npm run test"
41
41
  },
42
42
  "dependencies": {
43
- "@landstrip/landstrip": "^0.17.36",
43
+ "@landstrip/landstrip": "^0.17.38",
44
44
  "minimatch": "^10.2.5",
45
45
  "yaml": "^2.9.0"
46
46
  },