pi-landstrip 0.19.2 → 0.19.3

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.
package/dist/index.ts CHANGED
@@ -352,16 +352,16 @@ class AsyncQueue {
352
352
  const generation = this.generation;
353
353
  let release;
354
354
  const previous = this.tail;
355
- this.tail = new Promise((resolve2) => {
356
- release = resolve2;
355
+ this.tail = new Promise((resolve) => {
356
+ release = resolve;
357
357
  });
358
358
  const finish = () => {
359
359
  release?.();
360
360
  };
361
361
  let wakeReset;
362
- const resetSignal = new Promise((resolve2) => {
363
- wakeReset = resolve2;
364
- this.resetWaiters.push(resolve2);
362
+ const resetSignal = new Promise((resolve) => {
363
+ wakeReset = resolve;
364
+ this.resetWaiters.push(resolve);
365
365
  });
366
366
  const dropResetWaiter = () => {
367
367
  if (!wakeReset)
@@ -1396,15 +1396,15 @@ async function deleteProjectAgent(cwd, agent, agentDir = getAgentDir3()) {
1396
1396
  function prepareProjectAgentEditor(cwd, agent, agentDir = getAgentDir3()) {
1397
1397
  const origin = agent.origin;
1398
1398
  if (origin?.path && origin.kind === "pi-markdown") {
1399
- const target2 = projectMarkdownPath(cwd, agentDir, agent);
1399
+ const target = projectMarkdownPath(cwd, agentDir, agent);
1400
1400
  return {
1401
1401
  title: `Edit @${agent.name}`,
1402
1402
  content: readFileSync4(origin.path, "utf8"),
1403
1403
  async save(content) {
1404
1404
  validateAgentRaw(agent.name, parseMarkdownAgentRaw(content));
1405
- await withFileMutationQueue2(target2, async () => {
1406
- mkdirSync2(dirname2(target2), { recursive: true });
1407
- writeFileSync2(target2, ensureFinalNewline2(content), { mode: 384 });
1405
+ await withFileMutationQueue2(target, async () => {
1406
+ mkdirSync2(dirname2(target), { recursive: true });
1407
+ writeFileSync2(target, ensureFinalNewline2(content), { mode: 384 });
1408
1408
  });
1409
1409
  }
1410
1410
  };
@@ -1629,10 +1629,10 @@ function getLandstripArgumentCompletions(argumentPrefix, runtime, contextProvide
1629
1629
  const deps = { runtime, ctx: contextProvider?.() };
1630
1630
  if (argv.words.length === 0 && !argv.trailing) {
1631
1631
  const roots = [
1632
- ...SUBCOMMANDS.map((command2) => ({
1633
- value: command2.name,
1634
- label: command2.name,
1635
- description: command2.summary
1632
+ ...SUBCOMMANDS.map((command) => ({
1633
+ value: command.name,
1634
+ label: command.name,
1635
+ description: command.summary
1636
1636
  })),
1637
1637
  ...taskItems(runtime?.getTasks() ?? [], "")
1638
1638
  ];
@@ -1685,7 +1685,7 @@ function defaultSpawn(command, args, options) {
1685
1685
  return spawn(command, [...args], options);
1686
1686
  }
1687
1687
  function delay(milliseconds) {
1688
- return new Promise((resolve2) => setTimeout(resolve2, milliseconds));
1688
+ return new Promise((resolve) => setTimeout(resolve, milliseconds));
1689
1689
  }
1690
1690
 
1691
1691
  class RpcProcess {
@@ -1742,7 +1742,7 @@ class RpcProcess {
1742
1742
  });
1743
1743
  child.stdin.on("error", (error) => this.handleChildError(child, error, "RPC stdin error"));
1744
1744
  child.on("error", (error) => this.handleChildError(child, error, "RPC process error"));
1745
- this.exitPromise = new Promise((resolve2) => {
1745
+ this.exitPromise = new Promise((resolve) => {
1746
1746
  child.once("exit", (code, signal) => {
1747
1747
  this.disposeAuthPipe?.();
1748
1748
  this.disposeAuthPipe = undefined;
@@ -1750,13 +1750,13 @@ class RpcProcess {
1750
1750
  this.child = null;
1751
1751
  this.failAll(new Error(this.exitMessage(code, signal)));
1752
1752
  }
1753
- resolve2();
1753
+ resolve();
1754
1754
  });
1755
1755
  });
1756
- await new Promise((resolve2, reject) => {
1756
+ await new Promise((resolve, reject) => {
1757
1757
  const onSpawn = () => {
1758
1758
  cleanup();
1759
- resolve2();
1759
+ resolve();
1760
1760
  };
1761
1761
  const onError = (error) => {
1762
1762
  cleanup();
@@ -1815,12 +1815,12 @@ class RpcProcess {
1815
1815
  if (this.fatalError)
1816
1816
  throw this.fatalError;
1817
1817
  const id = `rpc-${++this.nextRequestId}`;
1818
- const response = await new Promise((resolve2, reject) => {
1818
+ const response = await new Promise((resolve, reject) => {
1819
1819
  const timer = setTimeout(() => {
1820
1820
  this.pending.delete(id);
1821
1821
  reject(this.withStderr(`Timed out waiting for ${type} response`));
1822
1822
  }, this.options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS);
1823
- this.pending.set(id, { command: type, resolve: resolve2, reject, timer });
1823
+ this.pending.set(id, { command: type, resolve, reject, timer });
1824
1824
  this.write({ ...fields, id, type }, (error) => {
1825
1825
  if (!error)
1826
1826
  return;
@@ -2036,12 +2036,12 @@ class RpcProcess {
2036
2036
  }
2037
2037
  waitForAgentSettled() {
2038
2038
  let waiter;
2039
- const promise = new Promise((resolve2, reject) => {
2039
+ const promise = new Promise((resolve, reject) => {
2040
2040
  const timer = setTimeout(() => {
2041
2041
  this.settledWaiters.delete(waiter);
2042
2042
  reject(this.withStderr("Timed out waiting for agent_settled"));
2043
2043
  }, this.options.settleTimeoutMs ?? DEFAULT_SETTLE_TIMEOUT_MS);
2044
- waiter = { resolve: resolve2, reject, timer };
2044
+ waiter = { resolve, reject, timer };
2045
2045
  this.settledWaiters.add(waiter);
2046
2046
  });
2047
2047
  return {
@@ -2060,13 +2060,13 @@ class RpcProcess {
2060
2060
  }
2061
2061
  this.settledWaiters.clear();
2062
2062
  }
2063
- write(record2, callback) {
2063
+ write(record, callback) {
2064
2064
  const child = this.child;
2065
2065
  if (!child || child.stdin.destroyed) {
2066
2066
  callback(this.notRunningError());
2067
2067
  return;
2068
2068
  }
2069
- child.stdin.write(`${JSON.stringify(record2)}
2069
+ child.stdin.write(`${JSON.stringify(record)}
2070
2070
  `, callback);
2071
2071
  }
2072
2072
  handleChildError(child, error, prefix) {
@@ -2196,18 +2196,18 @@ async function resolveWorkerAuth(registry, provider, signal, model) {
2196
2196
  timer = setTimeout(() => reject(new Error("Subagent provider authentication lookup timed out after 30 seconds")), 30000);
2197
2197
  });
2198
2198
  try {
2199
- const resolve2 = async () => {
2199
+ const resolve = async () => {
2200
2200
  try {
2201
2201
  const providerAuth = await registry.getProviderAuth(provider);
2202
2202
  if (signal.aborted || !providerAuth)
2203
2203
  return;
2204
2204
  if (model && typeof registry.getApiKeyAndHeaders === "function") {
2205
- const result2 = await registry.getApiKeyAndHeaders(model);
2206
- if (!result2.ok)
2205
+ const result = await registry.getApiKeyAndHeaders(model);
2206
+ if (!result.ok)
2207
2207
  throw new Error;
2208
2208
  return {
2209
- auth: { apiKey: result2.apiKey, headers: result2.headers, baseUrl: result2.baseUrl },
2210
- env: result2.env
2209
+ auth: { apiKey: result.apiKey, headers: result.headers, baseUrl: result.baseUrl },
2210
+ env: result.env
2211
2211
  };
2212
2212
  }
2213
2213
  return { auth: providerAuth.auth, env: providerAuth.env };
@@ -2215,7 +2215,7 @@ async function resolveWorkerAuth(registry, provider, signal, model) {
2215
2215
  throw new Error("Could not resolve subagent provider authentication");
2216
2216
  }
2217
2217
  };
2218
- const result = await Promise.race([resolve2(), interrupted]);
2218
+ const result = await Promise.race([resolve(), interrupted]);
2219
2219
  if (signal.aborted)
2220
2220
  throw new Error("Task cancelled");
2221
2221
  try {
@@ -2874,8 +2874,8 @@ var OAUTH_REFRESH_HOSTS = {
2874
2874
  };
2875
2875
  function modelEndpoint(ctx, selectedModel) {
2876
2876
  const models = ctx.modelRegistry?.getAll() ?? [];
2877
- const qualified = models.find((model2) => `${model2.provider}/${model2.id}` === selectedModel);
2878
- const byId = models.filter((model2) => model2.id === selectedModel);
2877
+ const qualified = models.find((model) => `${model.provider}/${model.id}` === selectedModel);
2878
+ const byId = models.filter((model) => model.id === selectedModel);
2879
2879
  const registered = qualified ?? (byId.length === 1 ? byId[0] : undefined);
2880
2880
  const activeName = ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined;
2881
2881
  const active = selectedModel === activeName || byId.length === 0 && selectedModel === ctx.model?.id ? ctx.model : undefined;
@@ -2966,7 +2966,7 @@ class Semaphore {
2966
2966
  this.active += 1;
2967
2967
  return this.releaseOnce();
2968
2968
  }
2969
- await new Promise((resolve4, reject) => {
2969
+ await new Promise((resolve, reject) => {
2970
2970
  const abort = () => {
2971
2971
  const index = this.waiters.indexOf(start);
2972
2972
  if (index >= 0)
@@ -2976,7 +2976,7 @@ class Semaphore {
2976
2976
  const start = () => {
2977
2977
  signal?.removeEventListener("abort", abort);
2978
2978
  this.active += 1;
2979
- resolve4();
2979
+ resolve();
2980
2980
  };
2981
2981
  this.waiters.push(start);
2982
2982
  signal?.addEventListener("abort", abort, { once: true });
@@ -3640,7 +3640,7 @@ ${this.primaryAgent.prompt}` };
3640
3640
  selectedTask = Math.max(0, Math.min(nextSelected < 0 ? 0 : nextSelected, tasks.length - 1));
3641
3641
  };
3642
3642
  const taskTargets = () => {
3643
- const selected = tasks.filter((task2) => selectedTaskIds.has(task2.id));
3643
+ const selected = tasks.filter((task) => selectedTaskIds.has(task.id));
3644
3644
  if (selected.length > 0)
3645
3645
  return selected;
3646
3646
  const task = tasks[selectedTask];
@@ -3772,22 +3772,22 @@ ${this.primaryAgent.prompt}` };
3772
3772
  const listedAgents = agentsForTab();
3773
3773
  const agentRows = listRowBudget(tui.terminal?.rows, AGENT_LIST_ROWS, AGENT_TAB_CHROME_ROWS);
3774
3774
  const { shown, start, status } = listWindow(listedAgents, selectedAgent, agentRows);
3775
- const nameWidth = Math.min(20, Math.max(5, ...listedAgents.map((agent2) => visibleWidth(`@${agent2.name}`))));
3775
+ const nameWidth = Math.min(20, Math.max(5, ...listedAgents.map((agent) => visibleWidth(`@${agent.name}`))));
3776
3776
  const modelWidth = Math.max(10, Math.min(24, Math.floor((contentWidth - 2) / 3)));
3777
3777
  const enabledWidth = 13;
3778
3778
  const runtimeWidth = Math.max(12, contentWidth - 2 - nameWidth - 1 - modelWidth - 1 - enabledWidth - 1);
3779
- const runtimeFor = (agent2) => {
3779
+ const runtimeFor = (agent) => {
3780
3780
  const parts = [];
3781
- if (agent2.name === this.primaryAgent?.name)
3781
+ if (agent.name === this.primaryAgent?.name)
3782
3782
  parts.push("★ primary");
3783
- const byState = (state) => [...this.tasks.values()].filter((task) => task.agent === agent2.name && task.state === state).length;
3783
+ const byState = (state) => [...this.tasks.values()].filter((task) => task.agent === agent.name && task.state === state).length;
3784
3784
  const running = byState("running");
3785
3785
  const queued = byState("queued");
3786
3786
  if (running > 0)
3787
3787
  parts.push(`${running} running`);
3788
3788
  if (queued > 0)
3789
3789
  parts.push(`${queued} queued`);
3790
- const rules = [...catalog.permissions, ...agent2.permissions].length;
3790
+ const rules = [...catalog.permissions, ...agent.permissions].length;
3791
3791
  parts.push(rules > 0 ? `${rules} rules` : "ask by default");
3792
3792
  return parts.length > 0 ? parts.join(" · ") : "—";
3793
3793
  };
@@ -3796,20 +3796,20 @@ ${this.primaryAgent.prompt}` };
3796
3796
  lines.push(`${theme.fg("accent", "Filter")} ${agentFilter}${filteringAgents ? theme.fg("accent", "█") : ""}`);
3797
3797
  }
3798
3798
  lines.push(theme.fg("dim", ` ${pad("Agent", nameWidth)} ${pad("Model", modelWidth)} ${pad("Enabled", enabledWidth)} ${pad("Runtime", runtimeWidth)}`));
3799
- for (const [offset, agent2] of shown.entries()) {
3799
+ for (const [offset, agent] of shown.entries()) {
3800
3800
  const index = start + offset;
3801
3801
  const selected = index === selectedAgent;
3802
3802
  const cursor = selected ? theme.fg("accent", "›") : " ";
3803
- const disabled = agent2.disabled;
3804
- const deleting = confirmingDeleteAgent === agent2.name;
3805
- const inherited = !disabledOverrides.project.has(agent2.name);
3803
+ const disabled = agent.disabled;
3804
+ const deleting = confirmingDeleteAgent === agent.name;
3805
+ const inherited = !disabledOverrides.project.has(agent.name);
3806
3806
  const enabled = inherited ? `inherited ${disabled ? "off" : "on"}` : disabled ? "disabled" : "enabled";
3807
3807
  const color = deleting ? "error" : disabled ? "muted" : "text";
3808
- const plainName = pad(`@${agent2.name}`, nameWidth);
3809
- const name = deleting ? theme.fg("error", plainName) : disabled ? theme.fg("muted", plainName) : colorizeAgentText(agent2.color, plainName, (agentColor, text) => theme.fg(agentColor, text));
3810
- const runtime = runtimeFor(agent2);
3811
- const line = `${cursor} ${name} ${theme.fg(color, pad(agent2.model ?? "current model", modelWidth))} ${theme.fg(deleting ? "error" : disabled ? "warning" : color, pad(enabled, enabledWidth))} ${theme.fg(disabled ? "muted" : "dim", pad(runtime, runtimeWidth))}`;
3812
- lines.push(agent2.name === this.primaryAgent?.name ? theme.bold(line) : line);
3808
+ const plainName = pad(`@${agent.name}`, nameWidth);
3809
+ const name = deleting ? theme.fg("error", plainName) : disabled ? theme.fg("muted", plainName) : colorizeAgentText(agent.color, plainName, (agentColor, text) => theme.fg(agentColor, text));
3810
+ const runtime = runtimeFor(agent);
3811
+ const line = `${cursor} ${name} ${theme.fg(color, pad(agent.model ?? "current model", modelWidth))} ${theme.fg(deleting ? "error" : disabled ? "warning" : color, pad(enabled, enabledWidth))} ${theme.fg(disabled ? "muted" : "dim", pad(runtime, runtimeWidth))}`;
3812
+ lines.push(agent.name === this.primaryAgent?.name ? theme.bold(line) : line);
3813
3813
  }
3814
3814
  if (listedAgents.length === 0) {
3815
3815
  lines.push(theme.fg("muted", agentFilter ? ` No ${tab} agents match ${agentFilter}` : ` No ${tab} agents configured.`));
@@ -4586,22 +4586,22 @@ ${catalog.diagnostics.join(`
4586
4586
  if (input.task_id)
4587
4587
  task.delivered = false;
4588
4588
  if (existingRun) {
4589
- const controller2 = this.controllers.get(task.id);
4589
+ const controller = this.controllers.get(task.id);
4590
4590
  let rejectCancelled;
4591
4591
  const cancelled = new Promise((_resolve, reject) => {
4592
4592
  rejectCancelled = reject;
4593
4593
  });
4594
- const abort2 = () => {
4595
- controller2?.abort();
4594
+ const abort = () => {
4595
+ controller?.abort();
4596
4596
  rejectCancelled?.(new Error("Task cancelled"));
4597
4597
  };
4598
4598
  const waitFor = (promise) => signal && !input.background ? Promise.race([promise, cancelled]) : promise;
4599
4599
  if (!input.background && signal?.aborted) {
4600
- controller2?.abort();
4600
+ controller?.abort();
4601
4601
  throw new Error("Task cancelled");
4602
4602
  }
4603
4603
  if (!input.background)
4604
- signal?.addEventListener("abort", abort2, { once: true });
4604
+ signal?.addEventListener("abort", abort, { once: true });
4605
4605
  try {
4606
4606
  const running = this.running.get(task.id);
4607
4607
  if (running) {
@@ -4631,7 +4631,7 @@ ${catalog.diagnostics.join(`
4631
4631
  this.releaseForeground(task.id);
4632
4632
  }
4633
4633
  } finally {
4634
- signal?.removeEventListener("abort", abort2);
4634
+ signal?.removeEventListener("abort", abort);
4635
4635
  }
4636
4636
  }
4637
4637
  task.state = "queued";
@@ -5152,7 +5152,7 @@ ${catalog.diagnostics.join(`
5152
5152
  }
5153
5153
  async forwardWorkerUi(ctx, task, request, signal) {
5154
5154
  if (request.method === "select" && typeof request.title === "string" && Array.isArray(request.options)) {
5155
- const options = request.options.filter((value2) => typeof value2 === "string");
5155
+ const options = request.options.filter((value) => typeof value === "string");
5156
5156
  const value = await ctx.ui.select(workerDialogTitle(task, request.title), options, {
5157
5157
  signal
5158
5158
  });
@@ -5261,10 +5261,10 @@ ${renderTaskResult(task.id, "error", message)}`);
5261
5261
  const tree = taskTreeLines(visible, (task) => {
5262
5262
  const state = taskState(theme, task);
5263
5263
  const agent = theme.fg("accent", `@${task.agent}`);
5264
- const record2 = byId.get(task.id);
5265
- const progress = taskProgress(record2);
5264
+ const record = byId.get(task.id);
5265
+ const progress = taskProgress(record);
5266
5266
  const suffix = progress.length > 0 ? theme.fg("dim", ` · ${progress.join(" · ")}`) : "";
5267
- const indentWidth = 3 + Math.max(0, record2.depth) * 3;
5267
+ const indentWidth = 3 + Math.max(0, record.depth) * 3;
5268
5268
  const descriptionWidth = Math.max(1, width - indentWidth - visibleWidth(`${state} ${agent} ${suffix}`));
5269
5269
  const description = theme.fg("text", truncateToWidth(task.description, descriptionWidth));
5270
5270
  return `${state} ${agent} ${description}${suffix}`;
@@ -5419,8 +5419,8 @@ ${renderTaskResult(task.id, failed ? "error" : "completed", value)}`;
5419
5419
  }
5420
5420
  if (agent.model) {
5421
5421
  const models = ctx.modelRegistry.getAll();
5422
- const qualified = models.filter((model2) => `${model2.provider}/${model2.id}` === agent.model);
5423
- const matches = qualified.length > 0 ? qualified : models.filter((model2) => model2.id === agent.model);
5422
+ const qualified = models.filter((model) => `${model.provider}/${model.id}` === agent.model);
5423
+ const matches = qualified.length > 0 ? qualified : models.filter((model) => model.id === agent.model);
5424
5424
  if (matches.length === 0) {
5425
5425
  ctx.ui.notify(`Model not found for primary agent ${agent.name}: ${agent.model}`, "error");
5426
5426
  return false;
@@ -5819,8 +5819,8 @@ function matchesPattern(filePath, patterns, cwd, explicitDenyGlobRoots) {
5819
5819
  }
5820
5820
  return roots.some((target) => abs === target || abs.startsWith(target.endsWith("/") ? target : `${target}/`));
5821
5821
  }
5822
- const sep5 = absPattern.endsWith("/") ? "" : "/";
5823
- return abs === absPattern || abs.startsWith(absPattern + sep5);
5822
+ const sep = absPattern.endsWith("/") ? "" : "/";
5823
+ return abs === absPattern || abs.startsWith(absPattern + sep);
5824
5824
  });
5825
5825
  }
5826
5826
  function normalizeBlockedPath(path, cwd) {
@@ -5892,7 +5892,7 @@ function extractDeniedPathMention(output, cwd, denied) {
5892
5892
  return null;
5893
5893
  }
5894
5894
  function extractRetryableNativeReadDeniedPath(output, cwd, allowRead, denyRead) {
5895
- const denied = (path2) => !readAllowed(path2, allowRead, denyRead, cwd);
5895
+ const denied = (path) => !readAllowed(path, allowRead, denyRead, cwd);
5896
5896
  const path = extractNativeDeniedPath(output, cwd);
5897
5897
  if (path)
5898
5898
  return denied(path) ? path : null;
@@ -5962,13 +5962,13 @@ async function stopPreparedChild(child) {
5962
5962
  child.kill("SIGKILL");
5963
5963
  if (child.exitCode !== null || child.signalCode !== null)
5964
5964
  return;
5965
- await new Promise((resolve5) => {
5965
+ await new Promise((resolve) => {
5966
5966
  let settleTimer;
5967
5967
  const done = () => {
5968
5968
  if (settleTimer)
5969
5969
  clearTimeout(settleTimer);
5970
5970
  child.removeListener("exit", done);
5971
- resolve5();
5971
+ resolve();
5972
5972
  };
5973
5973
  child.once("exit", done);
5974
5974
  settleTimer = setTimeout(done, 1000);
@@ -5977,7 +5977,7 @@ async function stopPreparedChild(child) {
5977
5977
  async function showPermissionPrompt(ctx, title, options, signal, label) {
5978
5978
  if (!ctx.hasUI)
5979
5979
  return "abort";
5980
- const labels = options.map((option2) => option2.hint ? `${option2.label} — ${option2.hint}` : option2.label);
5980
+ const labels = options.map((option) => option.hint ? `${option.label} — ${option.hint}` : option.label);
5981
5981
  const selected = await ctx.ui.select(label ? `${label}
5982
5982
  ${title}` : title, labels, { signal });
5983
5983
  if (signal?.aborted)
@@ -6314,7 +6314,7 @@ function createLandstripIntegrationWithPrompts(options, permissionPrompts) {
6314
6314
  target.push(scope);
6315
6315
  noteScope(ctx, "Write", choice, filePath, scope);
6316
6316
  }
6317
- async function authorizeFilesystemToolAccess(ctx, accesses, options2 = {}) {
6317
+ async function authorizeFilesystemToolAccess(ctx, accesses, options = {}) {
6318
6318
  const normalized = [
6319
6319
  ...new Map(accesses.map((access) => {
6320
6320
  const path = normalizeBlockedPath(access.path, ctx.cwd);
@@ -6346,9 +6346,9 @@ function createLandstripIntegrationWithPrompts(options, permissionPrompts) {
6346
6346
  const blocked = normalized.filter((access) => access.operation === "read" ? !readAllowed(access.path, getEffectiveAllowRead(config, ctx.cwd), getEffectiveDenyRead(config, ctx.cwd), ctx.cwd) : shouldPromptForWrite(access.path, getEffectiveAllowWrite(config), ctx.cwd));
6347
6347
  const paths = blocked.map(({ operation, path }) => `${operation}: ${path}`).join(`
6348
6348
  `);
6349
- const note = options2.authorizationNote ? `
6349
+ const note = options.authorizationNote ? `
6350
6350
 
6351
- ${options2.authorizationNote}` : "";
6351
+ ${options.authorizationNote}` : "";
6352
6352
  const choice = await showPermissionPrompt(ctx, `Filesystem policy blocked this tool call:
6353
6353
  ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
6354
6354
  if (choice === "abort" || promptSignal.aborted) {
@@ -6362,7 +6362,7 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
6362
6362
  }
6363
6363
  }
6364
6364
  return { allowed: true, prompted: true };
6365
- }, options2.signal);
6365
+ }, options.signal);
6366
6366
  }
6367
6367
  async function ensureDomainAllowed(ctx, domain, cwd, allowances, signal) {
6368
6368
  const current = () => {
@@ -6427,7 +6427,7 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
6427
6427
  });
6428
6428
  }
6429
6429
  function createSocketPair() {
6430
- return new Promise((resolve5, reject) => {
6430
+ return new Promise((resolve, reject) => {
6431
6431
  const server = createServer();
6432
6432
  server.on("error", reject);
6433
6433
  let client = null;
@@ -6436,7 +6436,7 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
6436
6436
  server.close();
6437
6437
  if (client) {
6438
6438
  client.removeListener("error", reject);
6439
- resolve5([client, serverEnd]);
6439
+ resolve([client, serverEnd]);
6440
6440
  }
6441
6441
  });
6442
6442
  server.listen(0, "127.0.0.1", () => {
@@ -6450,13 +6450,13 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
6450
6450
  async function resolveTrapQuery(trap, ctx, cwd, allowances, promptOnBlock, signal) {
6451
6451
  if (!isFilesystemTrap(trap)) {
6452
6452
  const key = `${trap.operation}\x00${trap.target}`;
6453
- const current2 = () => sessionAllowedTargets.includes(key) || allowances.targets.includes(key) ? { action: "allow" } : undefined;
6454
- const existing2 = current2();
6455
- if (existing2)
6456
- return existing2;
6453
+ const current = () => sessionAllowedTargets.includes(key) || allowances.targets.includes(key) ? { action: "allow" } : undefined;
6454
+ const existing = current();
6455
+ if (existing)
6456
+ return existing;
6457
6457
  if (!ctx.hasUI || !promptOnBlock)
6458
6458
  return { action: "deny", reason: "unprompted" };
6459
- return permissionPrompts.resolve(current2, async (promptSignal) => {
6459
+ return permissionPrompts.resolve(current, async (promptSignal) => {
6460
6460
  const choice = await showPermissionPrompt(ctx, `Network blocked: ${trap.operation} "${trap.target}"`, NETWORK_PERMISSION_OPTIONS, promptSignal, allowances.permissionPromptLabel);
6461
6461
  if (choice === "abort" || promptSignal.aborted)
6462
6462
  return { action: "deny", reason: "rejected" };
@@ -6548,7 +6548,7 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
6548
6548
  }
6549
6549
  });
6550
6550
  }
6551
- function prepareUnsandboxedRpcWorker(options2) {
6551
+ function prepareUnsandboxedRpcWorker(options) {
6552
6552
  let spawned = false;
6553
6553
  let disposed = false;
6554
6554
  let disposePromise;
@@ -6561,8 +6561,8 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
6561
6561
  spawned = true;
6562
6562
  const child = spawn2(command, [...args], {
6563
6563
  ...spawnOptions,
6564
- cwd: options2.cwd,
6565
- env: options2.env,
6564
+ cwd: options.cwd,
6565
+ env: options.env,
6566
6566
  detached: true
6567
6567
  });
6568
6568
  patchProcessGroupKill(child);
@@ -6570,10 +6570,10 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
6570
6570
  return child;
6571
6571
  };
6572
6572
  return {
6573
- command: options2.command,
6574
- args: options2.args,
6575
- cwd: options2.cwd,
6576
- env: options2.env,
6573
+ command: options.command,
6574
+ args: options.args,
6575
+ cwd: options.cwd,
6576
+ env: options.env,
6577
6577
  spawn: spawnWorker,
6578
6578
  dispose() {
6579
6579
  disposePromise ??= (async () => {
@@ -6584,66 +6584,66 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
6584
6584
  }
6585
6585
  };
6586
6586
  }
6587
- async function prepareRpcWorker(options2) {
6588
- if (options2.signal?.aborted)
6587
+ async function prepareRpcWorker(options) {
6588
+ if (options.signal?.aborted)
6589
6589
  throw new Error("Task cancelled");
6590
- if (!ensureSandboxState(options2.ctx)) {
6590
+ if (!ensureSandboxState(options.ctx)) {
6591
6591
  if (sandboxState === "unavailable") {
6592
6592
  throw new Error("Sandbox is unavailable; refusing subagent process");
6593
6593
  }
6594
6594
  if (!unsandboxedWorkerWarningShown) {
6595
- warnUnsandboxed(options2.ctx, "Subagent processes are running without Landstrip sandboxing", "warning");
6595
+ warnUnsandboxed(options.ctx, "Subagent processes are running without Landstrip sandboxing", "warning");
6596
6596
  unsandboxedWorkerWarningShown = true;
6597
6597
  }
6598
- return prepareUnsandboxedRpcWorker(options2);
6598
+ return prepareUnsandboxedRpcWorker(options);
6599
6599
  }
6600
6600
  const allowances = {
6601
- permissionPromptLabel: options2.permissionPromptLabel,
6602
- domains: [...options2.domains ?? []],
6603
- readPaths: [...options2.readPaths],
6604
- writePaths: [...options2.writePaths],
6601
+ permissionPromptLabel: options.permissionPromptLabel,
6602
+ domains: [...options.domains ?? []],
6603
+ readPaths: [...options.readPaths],
6604
+ writePaths: [...options.writePaths],
6605
6605
  targets: [],
6606
6606
  protectedPaths: [
6607
- ...new Set((options2.protectedPaths ?? []).flatMap((path) => {
6607
+ ...new Set((options.protectedPaths ?? []).flatMap((path) => {
6608
6608
  if (/[*?[\]]/.test(path)) {
6609
6609
  throw new Error(`Worker protected path "${path}" must be a concrete path, not a glob`);
6610
6610
  }
6611
- return [expandPath(path, options2.cwd), canonicalizePath2(path, options2.cwd)];
6611
+ return [expandPath(path, options.cwd), canonicalizePath2(path, options.cwd)];
6612
6612
  }))
6613
6613
  ]
6614
6614
  };
6615
- const processAllowances = withWindowsProcessReadAccess(allowances, options2.command, options2.cwd);
6616
- const config = loadConfig(options2.cwd);
6615
+ const processAllowances = withWindowsProcessReadAccess(allowances, options.command, options.cwd);
6616
+ const config = loadConfig(options.cwd);
6617
6617
  const explicitDenyGlobRoots = new Map;
6618
6618
  processAllowances.explicitDenyRead = {
6619
6619
  patterns: config.explicitDenyRead,
6620
6620
  globRoots: explicitDenyGlobRoots
6621
6621
  };
6622
- for (const path of [options2.cwd, options2.command, ...processAllowances.readPaths]) {
6622
+ for (const path of [options.cwd, options.command, ...processAllowances.readPaths]) {
6623
6623
  if (/[*?[\]]/.test(path)) {
6624
6624
  throw new Error(`Worker startup read path "${path}" must be a concrete path, not a glob`);
6625
6625
  }
6626
- const deny = config.explicitDenyRead.find((pattern) => matchesPattern(path, [pattern], options2.cwd, explicitDenyGlobRoots));
6626
+ const deny = config.explicitDenyRead.find((pattern) => matchesPattern(path, [pattern], options.cwd, explicitDenyGlobRoots));
6627
6627
  if (deny !== undefined) {
6628
6628
  throw new Error(`Worker startup read path "${path}" conflicts with explicit filesystem.denyRead "${deny}"; change the configured deny or the worker startup paths`);
6629
6629
  }
6630
6630
  }
6631
6631
  const proxyToken = randomBytes(32).toString("base64url");
6632
6632
  const proxyAuthorization = `Basic ${Buffer.from(`landstrip:${proxyToken}`).toString("base64")}`;
6633
- const proxy = shouldStartProxy(config) ? await startProxy(options2.ctx, options2.cwd, processAllowances, options2.signal, proxyAuthorization, restrictedUserProxyPortRange()) : null;
6633
+ const proxy = shouldStartProxy(config) ? await startProxy(options.ctx, options.cwd, processAllowances, options.signal, proxyAuthorization, restrictedUserProxyPortRange()) : null;
6634
6634
  let policy;
6635
6635
  let trapSocket;
6636
6636
  let childEnd;
6637
6637
  let disposed = false;
6638
6638
  try {
6639
- if (options2.signal?.aborted)
6639
+ if (options.signal?.aborted)
6640
6640
  throw new Error("Task cancelled");
6641
- policy = writePolicyFile(options2.cwd, proxy?.port ?? null, "worker", processAllowances);
6641
+ policy = writePolicyFile(options.cwd, proxy?.port ?? null, "worker", processAllowances);
6642
6642
  if (process.platform !== "win32") {
6643
6643
  [trapSocket, childEnd] = await createSocketPair();
6644
- if (options2.signal?.aborted)
6644
+ if (options.signal?.aborted)
6645
6645
  throw new Error("Task cancelled");
6646
- attachWorkerTrap(trapSocket, options2.ctx, options2.cwd, processAllowances, options2.signal);
6646
+ attachWorkerTrap(trapSocket, options.ctx, options.cwd, processAllowances, options.signal);
6647
6647
  }
6648
6648
  } catch (error) {
6649
6649
  trapSocket?.destroy();
@@ -6656,7 +6656,7 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
6656
6656
  const workerPolicy = policy;
6657
6657
  const workerTrapSocket = trapSocket;
6658
6658
  const workerChildEnd = childEnd;
6659
- const workerEnv = { ...options2.env };
6659
+ const workerEnv = { ...options.env };
6660
6660
  if (proxy) {
6661
6661
  const url = `http://landstrip:${proxyToken}@127.0.0.1:${proxy.port}`;
6662
6662
  for (const name of PROXY_ENVIRONMENT_VARIABLES) {
@@ -6679,8 +6679,8 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
6679
6679
  "-p",
6680
6680
  workerPolicy.path,
6681
6681
  "--",
6682
- options2.command,
6683
- ...options2.args
6682
+ options.command,
6683
+ ...options.args
6684
6684
  ];
6685
6685
  const authPipe = Array.isArray(spawnOptions.stdio) && spawnOptions.stdio[WORKER_AUTH_FD] === "pipe";
6686
6686
  if (authPipe && process.platform === "win32") {
@@ -6697,7 +6697,7 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
6697
6697
  landstripArgs.splice(1, 0, "--trap", "3");
6698
6698
  const child = spawn2(binaryPath(), landstripArgs, {
6699
6699
  ...spawnOptions,
6700
- cwd: options2.cwd,
6700
+ cwd: options.cwd,
6701
6701
  env: workerEnv,
6702
6702
  detached: true,
6703
6703
  stdio
@@ -6708,9 +6708,9 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
6708
6708
  return child;
6709
6709
  };
6710
6710
  return {
6711
- command: options2.command,
6712
- args: options2.args,
6713
- cwd: options2.cwd,
6711
+ command: options.command,
6712
+ args: options.args,
6713
+ cwd: options.cwd,
6714
6714
  env: workerEnv,
6715
6715
  spawn: spawnWorker,
6716
6716
  dispose() {
@@ -6726,15 +6726,15 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
6726
6726
  }
6727
6727
  };
6728
6728
  }
6729
- async function prepareProcess(options2) {
6730
- if (process.platform === "win32" && (getNoSandboxFlag() || !loadConfig(options2.cwd).enabled)) {
6729
+ async function prepareProcess(options) {
6730
+ if (process.platform === "win32" && (getNoSandboxFlag() || !loadConfig(options.cwd).enabled)) {
6731
6731
  throw new Error("Generic process preparation requires sandboxing on Windows");
6732
6732
  }
6733
6733
  return prepareRpcWorker({
6734
- ...options2,
6735
- env: options2.env ?? process.env,
6736
- readPaths: options2.readPaths ?? [],
6737
- writePaths: options2.writePaths ?? []
6734
+ ...options,
6735
+ env: options.env ?? process.env,
6736
+ readPaths: options.readPaths ?? [],
6737
+ writePaths: options.writePaths ?? []
6738
6738
  });
6739
6739
  }
6740
6740
  function createProviderBashOps() {
@@ -7126,16 +7126,16 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
7126
7126
  return null;
7127
7127
  const { globalPath, projectPath } = getPiConfigPaths(ctx.cwd, "sandbox.json");
7128
7128
  const current = () => {
7129
- const config2 = loadConfig(ctx.cwd);
7130
- if (matchesPattern(blockedPath, operation === "read" ? config2.filesystem.denyReadAlways : config2.filesystem.denyWrite, ctx.cwd)) {
7129
+ const config = loadConfig(ctx.cwd);
7130
+ if (matchesPattern(blockedPath, operation === "read" ? config.filesystem.denyReadAlways : config.filesystem.denyWrite, ctx.cwd)) {
7131
7131
  return false;
7132
7132
  }
7133
- const needsPrompt = operation === "read" ? !readAllowed(blockedPath, getEffectiveAllowRead(config2, ctx.cwd, allowances), getEffectiveDenyRead(config2, ctx.cwd), ctx.cwd) : shouldPromptForWrite(blockedPath, getEffectiveAllowWrite(config2, allowances), ctx.cwd);
7133
+ const needsPrompt = operation === "read" ? !readAllowed(blockedPath, getEffectiveAllowRead(config, ctx.cwd, allowances), getEffectiveDenyRead(config, ctx.cwd), ctx.cwd) : shouldPromptForWrite(blockedPath, getEffectiveAllowWrite(config, allowances), ctx.cwd);
7134
7134
  return needsPrompt ? undefined : true;
7135
7135
  };
7136
7136
  const access = await permissionPrompts.resolve(current, async (promptSignal) => {
7137
- const config2 = loadConfig(ctx.cwd);
7138
- const choice = operation === "read" ? await promptReadBlock(ctx, blockedPath, matchesPattern(blockedPath, config2.filesystem.denyRead, ctx.cwd) ? "granting allowRead will override it" : undefined, promptSignal) : await showPermissionPrompt(ctx, `Write blocked: "${blockedPath}" is not in allowWrite`, PERMISSION_OPTIONS, promptSignal);
7137
+ const config = loadConfig(ctx.cwd);
7138
+ const choice = operation === "read" ? await promptReadBlock(ctx, blockedPath, matchesPattern(blockedPath, config.filesystem.denyRead, ctx.cwd) ? "granting allowRead will override it" : undefined, promptSignal) : await showPermissionPrompt(ctx, `Write blocked: "${blockedPath}" is not in allowWrite`, PERMISSION_OPTIONS, promptSignal);
7139
7139
  if (choice === "abort" || promptSignal.aborted)
7140
7140
  return false;
7141
7141
  if (operation === "read") {
@@ -7146,8 +7146,8 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
7146
7146
  return true;
7147
7147
  }, signal);
7148
7148
  if (!access) {
7149
- const config2 = loadConfig(ctx.cwd);
7150
- if (operation === "write" && matchesPattern(blockedPath, config2.filesystem.denyWrite, ctx.cwd)) {
7149
+ const config = loadConfig(ctx.cwd);
7150
+ if (operation === "write" && matchesPattern(blockedPath, config.filesystem.denyWrite, ctx.cwd)) {
7151
7151
  notify(ctx, `"${blockedPath}" is blocked by denyWrite. Check:
7152
7152
  ${projectPath}
7153
7153
  ${globalPath}`, "warning");
@@ -7183,21 +7183,21 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
7183
7183
  const errorText = formatError(error);
7184
7184
  const fallbackOutput = `${stderrOutput}
7185
7185
  ${errorText}`;
7186
- const blockedWritePath2 = extractTrapBlockedPath(landstripErrorOutput, ctx.cwd, "write") ?? extractNativeWriteDeniedPath(fallbackOutput, ctx.cwd);
7187
- if (blockedWritePath2) {
7188
- const retryResult2 = await retryWithAccess("write", blockedWritePath2);
7189
- if (retryResult2)
7190
- return retryResult2;
7186
+ const blockedWritePath = extractTrapBlockedPath(landstripErrorOutput, ctx.cwd, "write") ?? extractNativeWriteDeniedPath(fallbackOutput, ctx.cwd);
7187
+ if (blockedWritePath) {
7188
+ const retryResult = await retryWithAccess("write", blockedWritePath);
7189
+ if (retryResult)
7190
+ return retryResult;
7191
7191
  }
7192
- const blockedReadPath2 = blockedReadPathForRetry(fallbackOutput);
7193
- if (blockedReadPath2) {
7194
- const retryResult2 = await retryWithAccess("read", blockedReadPath2);
7195
- if (retryResult2)
7196
- return retryResult2;
7192
+ const blockedReadPath = blockedReadPathForRetry(fallbackOutput);
7193
+ if (blockedReadPath) {
7194
+ const retryResult = await retryWithAccess("read", blockedReadPath);
7195
+ if (retryResult)
7196
+ return retryResult;
7197
7197
  }
7198
- const landstripErrors2 = parseLandstripTraps(landstripErrorOutput);
7199
- if (landstripErrors2.length > 0) {
7200
- throw new Error(formatLandstripTraps(landstripErrors2));
7198
+ const landstripErrors = parseLandstripTraps(landstripErrorOutput);
7199
+ if (landstripErrors.length > 0) {
7200
+ throw new Error(formatLandstripTraps(landstripErrors));
7201
7201
  }
7202
7202
  throw error;
7203
7203
  }
@@ -7210,9 +7210,9 @@ ${message}
7210
7210
  }
7211
7211
  const blockedWritePath = extractTrapBlockedPath(landstripErrorOutput, ctx.cwd, "write") ?? extractNativeWriteDeniedPath(stderrOutput, ctx.cwd);
7212
7212
  if (blockedWritePath) {
7213
- const retryResult2 = await retryWithAccess("write", blockedWritePath);
7214
- if (retryResult2)
7215
- return retryResult2;
7213
+ const retryResult = await retryWithAccess("write", blockedWritePath);
7214
+ if (retryResult)
7215
+ return retryResult;
7216
7216
  }
7217
7217
  const blockedReadPath = blockedReadPathForRetry(stderrOutput);
7218
7218
  if (!blockedReadPath)
@@ -7270,7 +7270,7 @@ ${message}
7270
7270
  networkLabel = `${config.network.allowedDomains.length} domains`;
7271
7271
  networkColor = "accent";
7272
7272
  }
7273
- const sep5 = theme.fg("dim", "·");
7273
+ const sep = theme.fg("dim", "·");
7274
7274
  const net = theme.fg(networkColor, networkLabel);
7275
7275
  const write = theme.fg("accent", `${config.filesystem.allowWrite.length} write paths`);
7276
7276
  const fields = [`${dot} ${label}`, net, write];
@@ -7279,7 +7279,7 @@ ${message}
7279
7279
  const implementationLabel = windowsImplementation === "restrictedUser" ? "restricted user" : `${config.windows.appContainerMode} AppContainer`;
7280
7280
  fields.splice(1, 0, theme.fg(standard ? "warning" : "accent", implementationLabel));
7281
7281
  }
7282
- setTuiStatus(ctx, "sandbox", fields.join(` ${sep5} `));
7282
+ setTuiStatus(ctx, "sandbox", fields.join(` ${sep} `));
7283
7283
  }
7284
7284
  const headlessWarnings = new Set;
7285
7285
  function warnUnsandboxed(ctx, message, level) {
@@ -228,17 +228,17 @@ async function main() {
228
228
  const agentDir = getAgentDir();
229
229
  const sessionManager = args.session ? SessionManager.open(args.session, args.sessionDir) : args.noSession ? SessionManager.inMemory(cwd) : SessionManager.create(cwd, args.sessionDir);
230
230
  const absolutePaths = (paths) => paths?.map((path) => resolve(cwd, path));
231
- const runtime = await createAgentSessionRuntime(async ({ cwd: cwd2, sessionManager: sessionManager2, sessionStartEvent }) => {
231
+ const runtime = await createAgentSessionRuntime(async ({ cwd, sessionManager, sessionStartEvent }) => {
232
232
  const catalog = await ModelRuntime.create({
233
233
  credentials: new InMemoryCredentialStore,
234
234
  allowModelNetwork: false,
235
235
  refreshOnCreate: false
236
236
  });
237
237
  const services = await createAgentSessionServices({
238
- cwd: cwd2,
238
+ cwd,
239
239
  agentDir,
240
240
  modelRuntime: catalog,
241
- settingsManager: SettingsManager.create(cwd2, agentDir, {
241
+ settingsManager: SettingsManager.create(cwd, agentDir, {
242
242
  projectTrusted: args.projectTrustOverride === true
243
243
  }),
244
244
  extensionFlagValues: args.unknownFlags,
@@ -274,7 +274,7 @@ async function main() {
274
274
  return {
275
275
  ...await createAgentSessionFromServices({
276
276
  services,
277
- sessionManager: sessionManager2,
277
+ sessionManager,
278
278
  sessionStartEvent,
279
279
  model: initial.model,
280
280
  thinkingLevel: args.thinking,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-landstrip",
3
- "version": "0.19.2",
3
+ "version": "0.19.3",
4
4
  "description": "Sandboxed Bash and process-backed agents for Pi",
5
5
  "keywords": [
6
6
  "landstrip",
@@ -39,22 +39,22 @@
39
39
  "ci:test": "npm run test"
40
40
  },
41
41
  "dependencies": {
42
- "@landstrip/landstrip-api": "^0.19.2",
42
+ "@landstrip/landstrip-api": "^0.19.3",
43
43
  "jsonc-parser": "^3.3.1",
44
44
  "minimatch": "^10.2.5",
45
45
  "undici": "^8.9.0",
46
46
  "yaml": "^2.9.0"
47
47
  },
48
48
  "devDependencies": {
49
- "@earendil-works/pi-ai": "^0.84.1",
49
+ "@earendil-works/pi-ai": "^0.85.1",
50
50
  "@earendil-works/pi-coding-agent": "^0.85.1",
51
51
  "@earendil-works/pi-tui": "^0.85.1",
52
52
  "@types/node": "^26.4.1",
53
- "oxfmt": "^0.64.0",
53
+ "oxfmt": "^0.67.0",
54
54
  "oxlint": "^1.68.0",
55
55
  "typebox": "^1.3.6",
56
56
  "typescript": "^5.0.0",
57
- "vitest": "^4.1.9"
57
+ "vitest": "^5.0.0"
58
58
  },
59
59
  "peerDependencies": {
60
60
  "@earendil-works/pi-ai": ">=0.82.0",