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 +143 -143
- package/dist/worker-auth-entry.js +4 -4
- package/package.json +5 -5
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((
|
|
356
|
-
release =
|
|
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((
|
|
363
|
-
wakeReset =
|
|
364
|
-
this.resetWaiters.push(
|
|
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
|
|
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(
|
|
1406
|
-
mkdirSync2(dirname2(
|
|
1407
|
-
writeFileSync2(
|
|
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((
|
|
1633
|
-
value:
|
|
1634
|
-
label:
|
|
1635
|
-
description:
|
|
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((
|
|
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((
|
|
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
|
-
|
|
1753
|
+
resolve();
|
|
1754
1754
|
});
|
|
1755
1755
|
});
|
|
1756
|
-
await new Promise((
|
|
1756
|
+
await new Promise((resolve, reject) => {
|
|
1757
1757
|
const onSpawn = () => {
|
|
1758
1758
|
cleanup();
|
|
1759
|
-
|
|
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((
|
|
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
|
|
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((
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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
|
|
2206
|
-
if (!
|
|
2205
|
+
const result = await registry.getApiKeyAndHeaders(model);
|
|
2206
|
+
if (!result.ok)
|
|
2207
2207
|
throw new Error;
|
|
2208
2208
|
return {
|
|
2209
|
-
auth: { apiKey:
|
|
2210
|
-
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([
|
|
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((
|
|
2878
|
-
const byId = models.filter((
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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((
|
|
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 = (
|
|
3779
|
+
const runtimeFor = (agent) => {
|
|
3780
3780
|
const parts = [];
|
|
3781
|
-
if (
|
|
3781
|
+
if (agent.name === this.primaryAgent?.name)
|
|
3782
3782
|
parts.push("★ primary");
|
|
3783
|
-
const byState = (state) => [...this.tasks.values()].filter((task) => task.agent ===
|
|
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, ...
|
|
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,
|
|
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 =
|
|
3804
|
-
const deleting = confirmingDeleteAgent ===
|
|
3805
|
-
const inherited = !disabledOverrides.project.has(
|
|
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(`@${
|
|
3809
|
-
const name = deleting ? theme.fg("error", plainName) : disabled ? theme.fg("muted", plainName) : colorizeAgentText(
|
|
3810
|
-
const runtime = runtimeFor(
|
|
3811
|
-
const line = `${cursor} ${name} ${theme.fg(color, pad(
|
|
3812
|
-
lines.push(
|
|
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
|
|
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
|
|
4595
|
-
|
|
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
|
-
|
|
4600
|
+
controller?.abort();
|
|
4601
4601
|
throw new Error("Task cancelled");
|
|
4602
4602
|
}
|
|
4603
4603
|
if (!input.background)
|
|
4604
|
-
signal?.addEventListener("abort",
|
|
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",
|
|
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((
|
|
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
|
|
5265
|
-
const progress = taskProgress(
|
|
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,
|
|
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((
|
|
5423
|
-
const matches = qualified.length > 0 ? qualified : models.filter((
|
|
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
|
|
5823
|
-
return abs === absPattern || abs.startsWith(absPattern +
|
|
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 = (
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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,
|
|
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 =
|
|
6349
|
+
const note = options.authorizationNote ? `
|
|
6350
6350
|
|
|
6351
|
-
${
|
|
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
|
-
},
|
|
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((
|
|
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
|
-
|
|
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
|
|
6454
|
-
const
|
|
6455
|
-
if (
|
|
6456
|
-
return
|
|
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(
|
|
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(
|
|
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:
|
|
6565
|
-
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:
|
|
6574
|
-
args:
|
|
6575
|
-
cwd:
|
|
6576
|
-
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(
|
|
6588
|
-
if (
|
|
6587
|
+
async function prepareRpcWorker(options) {
|
|
6588
|
+
if (options.signal?.aborted)
|
|
6589
6589
|
throw new Error("Task cancelled");
|
|
6590
|
-
if (!ensureSandboxState(
|
|
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(
|
|
6595
|
+
warnUnsandboxed(options.ctx, "Subagent processes are running without Landstrip sandboxing", "warning");
|
|
6596
6596
|
unsandboxedWorkerWarningShown = true;
|
|
6597
6597
|
}
|
|
6598
|
-
return prepareUnsandboxedRpcWorker(
|
|
6598
|
+
return prepareUnsandboxedRpcWorker(options);
|
|
6599
6599
|
}
|
|
6600
6600
|
const allowances = {
|
|
6601
|
-
permissionPromptLabel:
|
|
6602
|
-
domains: [...
|
|
6603
|
-
readPaths: [...
|
|
6604
|
-
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((
|
|
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,
|
|
6611
|
+
return [expandPath(path, options.cwd), canonicalizePath2(path, options.cwd)];
|
|
6612
6612
|
}))
|
|
6613
6613
|
]
|
|
6614
6614
|
};
|
|
6615
|
-
const processAllowances = withWindowsProcessReadAccess(allowances,
|
|
6616
|
-
const config = loadConfig(
|
|
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 [
|
|
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],
|
|
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(
|
|
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 (
|
|
6639
|
+
if (options.signal?.aborted)
|
|
6640
6640
|
throw new Error("Task cancelled");
|
|
6641
|
-
policy = writePolicyFile(
|
|
6641
|
+
policy = writePolicyFile(options.cwd, proxy?.port ?? null, "worker", processAllowances);
|
|
6642
6642
|
if (process.platform !== "win32") {
|
|
6643
6643
|
[trapSocket, childEnd] = await createSocketPair();
|
|
6644
|
-
if (
|
|
6644
|
+
if (options.signal?.aborted)
|
|
6645
6645
|
throw new Error("Task cancelled");
|
|
6646
|
-
attachWorkerTrap(trapSocket,
|
|
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 = { ...
|
|
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
|
-
|
|
6683
|
-
...
|
|
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:
|
|
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:
|
|
6712
|
-
args:
|
|
6713
|
-
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(
|
|
6730
|
-
if (process.platform === "win32" && (getNoSandboxFlag() || !loadConfig(
|
|
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
|
-
...
|
|
6735
|
-
env:
|
|
6736
|
-
readPaths:
|
|
6737
|
-
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
|
|
7130
|
-
if (matchesPattern(blockedPath, operation === "read" ?
|
|
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(
|
|
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
|
|
7138
|
-
const choice = operation === "read" ? await promptReadBlock(ctx, blockedPath, matchesPattern(blockedPath,
|
|
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
|
|
7150
|
-
if (operation === "write" && matchesPattern(blockedPath,
|
|
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
|
|
7187
|
-
if (
|
|
7188
|
-
const
|
|
7189
|
-
if (
|
|
7190
|
-
return
|
|
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
|
|
7193
|
-
if (
|
|
7194
|
-
const
|
|
7195
|
-
if (
|
|
7196
|
-
return
|
|
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
|
|
7199
|
-
if (
|
|
7200
|
-
throw new Error(formatLandstripTraps(
|
|
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
|
|
7214
|
-
if (
|
|
7215
|
-
return
|
|
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
|
|
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(` ${
|
|
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
|
|
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
|
|
238
|
+
cwd,
|
|
239
239
|
agentDir,
|
|
240
240
|
modelRuntime: catalog,
|
|
241
|
-
settingsManager: SettingsManager.create(
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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": "^
|
|
57
|
+
"vitest": "^5.0.0"
|
|
58
58
|
},
|
|
59
59
|
"peerDependencies": {
|
|
60
60
|
"@earendil-works/pi-ai": ">=0.82.0",
|