pi-landstrip 0.19.1 → 0.19.2
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 +47 -17
- package/package.json +2 -2
package/dist/index.ts
CHANGED
|
@@ -2807,7 +2807,8 @@ function taskProgress(task) {
|
|
|
2807
2807
|
return progress;
|
|
2808
2808
|
}
|
|
2809
2809
|
function workerDialogTitle(task, title) {
|
|
2810
|
-
|
|
2810
|
+
const label = `@${task.agent} · ${task.description} · ${task.id.slice(0, 8)}`;
|
|
2811
|
+
return title === undefined ? label : `${label}
|
|
2811
2812
|
${title}`;
|
|
2812
2813
|
}
|
|
2813
2814
|
function messageContentText(message) {
|
|
@@ -2880,7 +2881,7 @@ function modelEndpoint(ctx, selectedModel) {
|
|
|
2880
2881
|
const active = selectedModel === activeName || byId.length === 0 && selectedModel === ctx.model?.id ? ctx.model : undefined;
|
|
2881
2882
|
const model = registered ?? active;
|
|
2882
2883
|
if (!model)
|
|
2883
|
-
return {
|
|
2884
|
+
return { error: byId.length > 1 ? "Ambiguous model" : "Model not found" };
|
|
2884
2885
|
const domains = [];
|
|
2885
2886
|
const endpoint = {
|
|
2886
2887
|
provider: model.provider,
|
|
@@ -2897,20 +2898,21 @@ function modelEndpoint(ctx, selectedModel) {
|
|
|
2897
2898
|
domains.push(refreshHost);
|
|
2898
2899
|
return endpoint;
|
|
2899
2900
|
}
|
|
2900
|
-
async function workerEndpoint(ctx, selectedModel, signal) {
|
|
2901
|
+
async function workerEndpoint(ctx, selectedModel, agentName, signal) {
|
|
2901
2902
|
if (signal.aborted)
|
|
2902
2903
|
throw new Error("Task cancelled");
|
|
2903
2904
|
const endpoint = modelEndpoint(ctx, selectedModel);
|
|
2905
|
+
if ("error" in endpoint) {
|
|
2906
|
+
throw new Error(`${endpoint.error} for subagent ${agentName}: ${selectedModel}`);
|
|
2907
|
+
}
|
|
2904
2908
|
const { provider, domains, selected } = endpoint;
|
|
2905
|
-
if (provider === undefined)
|
|
2906
|
-
return endpoint;
|
|
2907
2909
|
const auth = await resolveWorkerAuth(ctx.modelRegistry, provider, signal, selected);
|
|
2908
2910
|
const host = workerAuthHost(auth?.auth.baseUrl);
|
|
2909
2911
|
const granted = [...new Set(host ? [...domains, host] : domains)];
|
|
2910
2912
|
return {
|
|
2911
2913
|
...endpoint,
|
|
2912
2914
|
domains: granted,
|
|
2913
|
-
resolveAuth: auth
|
|
2915
|
+
resolveAuth: auth ? workerAuthResolver(ctx.modelRegistry, selected, granted) : undefined
|
|
2914
2916
|
};
|
|
2915
2917
|
}
|
|
2916
2918
|
function agentBootstrapPaths(agentDir, privateAuth) {
|
|
@@ -4880,7 +4882,7 @@ ${catalog.diagnostics.join(`
|
|
|
4880
4882
|
const model = agent.model ?? (ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : undefined);
|
|
4881
4883
|
if (!model)
|
|
4882
4884
|
throw new Error(`No model available for subagent ${agent.name}`);
|
|
4883
|
-
const endpoint = await workerEndpoint(ctx, model, signal);
|
|
4885
|
+
const endpoint = await workerEndpoint(ctx, model, agent.name, signal);
|
|
4884
4886
|
if (signal.aborted)
|
|
4885
4887
|
throw new Error("Task cancelled");
|
|
4886
4888
|
const thinking = agent.variant && PI_THINKING_LEVELS.has(agent.variant) ? agent.variant : this.pi.getThinkingLevel();
|
|
@@ -4905,7 +4907,8 @@ ${catalog.diagnostics.join(`
|
|
|
4905
4907
|
join7(packageDir, "index.ts"),
|
|
4906
4908
|
...workerExtensions.flatMap((extension) => ["--extension", extension.entry]),
|
|
4907
4909
|
...task.sessionFile ? ["--session", task.sessionFile] : task.sessionDir ? ["--session-dir", task.sessionDir] : [],
|
|
4908
|
-
|
|
4910
|
+
"--provider",
|
|
4911
|
+
endpoint.provider,
|
|
4909
4912
|
"--model",
|
|
4910
4913
|
endpoint.model,
|
|
4911
4914
|
"--thinking",
|
|
@@ -4934,6 +4937,7 @@ ${catalog.diagnostics.join(`
|
|
|
4934
4937
|
const modelsStorePath = join7(agentDir, "models-store.json");
|
|
4935
4938
|
const cliEntry = invocation.args[0] ?? invocation.command;
|
|
4936
4939
|
launch = await this.integration.prepareRpcWorker({
|
|
4940
|
+
permissionPromptLabel: workerDialogTitle(task),
|
|
4937
4941
|
command: invocation.command,
|
|
4938
4942
|
args,
|
|
4939
4943
|
cwd: ctx.cwd,
|
|
@@ -5867,9 +5871,32 @@ function extractNativeDeniedPath(output, cwd) {
|
|
|
5867
5871
|
return normalizePathMatch(match[1], cwd);
|
|
5868
5872
|
return null;
|
|
5869
5873
|
}
|
|
5874
|
+
function extractDeniedPathMention(output, cwd, denied) {
|
|
5875
|
+
const start = /(?<=^|['"`\s=:(])(?=\/|[a-zA-Z]:[\\/])/g;
|
|
5876
|
+
for (const line of output.split(/\r?\n/)) {
|
|
5877
|
+
for (const match of line.matchAll(start)) {
|
|
5878
|
+
const raw = line.slice(match.index);
|
|
5879
|
+
const quoted = raw.match(/^([^'"`]+)['"`]/);
|
|
5880
|
+
const words = (quoted ? quoted[1] : raw).replace(/[.,:;)\]]+$/, "").split(" ");
|
|
5881
|
+
for (let count = words.length;count > 0; count -= 1) {
|
|
5882
|
+
const candidate = words.slice(0, count).join(" ");
|
|
5883
|
+
if (!existsSync6(candidate))
|
|
5884
|
+
continue;
|
|
5885
|
+
const path = normalizeBlockedPath(candidate, cwd);
|
|
5886
|
+
if (denied(path))
|
|
5887
|
+
return path;
|
|
5888
|
+
break;
|
|
5889
|
+
}
|
|
5890
|
+
}
|
|
5891
|
+
}
|
|
5892
|
+
return null;
|
|
5893
|
+
}
|
|
5870
5894
|
function extractRetryableNativeReadDeniedPath(output, cwd, allowRead, denyRead) {
|
|
5895
|
+
const denied = (path2) => !readAllowed(path2, allowRead, denyRead, cwd);
|
|
5871
5896
|
const path = extractNativeDeniedPath(output, cwd);
|
|
5872
|
-
|
|
5897
|
+
if (path)
|
|
5898
|
+
return denied(path) ? path : null;
|
|
5899
|
+
return extractDeniedPathMention(output, cwd, denied);
|
|
5873
5900
|
}
|
|
5874
5901
|
function extractNativeWriteDeniedPath(output, cwd) {
|
|
5875
5902
|
const denial = String.raw`(?:Access is denied\.?|Operation not permitted|Permission denied)`;
|
|
@@ -5947,11 +5974,12 @@ async function stopPreparedChild(child) {
|
|
|
5947
5974
|
settleTimer = setTimeout(done, 1000);
|
|
5948
5975
|
});
|
|
5949
5976
|
}
|
|
5950
|
-
async function showPermissionPrompt(ctx, title, options, signal) {
|
|
5977
|
+
async function showPermissionPrompt(ctx, title, options, signal, label) {
|
|
5951
5978
|
if (!ctx.hasUI)
|
|
5952
5979
|
return "abort";
|
|
5953
5980
|
const labels = options.map((option2) => option2.hint ? `${option2.label} — ${option2.hint}` : option2.label);
|
|
5954
|
-
const selected = await ctx.ui.select(
|
|
5981
|
+
const selected = await ctx.ui.select(label ? `${label}
|
|
5982
|
+
${title}` : title, labels, { signal });
|
|
5955
5983
|
if (signal?.aborted)
|
|
5956
5984
|
return "abort";
|
|
5957
5985
|
const index = selected === undefined ? -1 : labels.indexOf(selected);
|
|
@@ -5959,7 +5987,8 @@ async function showPermissionPrompt(ctx, title, options, signal) {
|
|
|
5959
5987
|
if (!option)
|
|
5960
5988
|
return "abort";
|
|
5961
5989
|
if (option.confirm) {
|
|
5962
|
-
const confirmed = await ctx.ui.confirm(
|
|
5990
|
+
const confirmed = await ctx.ui.confirm(label ? `${label}
|
|
5991
|
+
Save this permission?` : "Save this permission?", `${title}
|
|
5963
5992
|
|
|
5964
5993
|
${option.hint ?? "This changes persisted sandbox policy."}`, { signal });
|
|
5965
5994
|
if (!confirmed || signal?.aborted)
|
|
@@ -5967,9 +5996,9 @@ ${option.hint ?? "This changes persisted sandbox policy."}`, { signal });
|
|
|
5967
5996
|
}
|
|
5968
5997
|
return option.action;
|
|
5969
5998
|
}
|
|
5970
|
-
function promptReadBlock(ctx, filePath, reason, signal) {
|
|
5999
|
+
function promptReadBlock(ctx, filePath, reason, signal, label) {
|
|
5971
6000
|
const title = reason ? `Read blocked: "${filePath}" is in denyRead (${reason})` : `Read blocked: "${filePath}" is not in allowRead`;
|
|
5972
|
-
return showPermissionPrompt(ctx, title, PERMISSION_OPTIONS, signal);
|
|
6001
|
+
return showPermissionPrompt(ctx, title, PERMISSION_OPTIONS, signal, label);
|
|
5973
6002
|
}
|
|
5974
6003
|
function writeEnvFile(env, proxyPort, proxyToken) {
|
|
5975
6004
|
const lines = [];
|
|
@@ -6348,7 +6377,7 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
|
|
|
6348
6377
|
if (!ctx.hasUI)
|
|
6349
6378
|
return current() ?? false;
|
|
6350
6379
|
return permissionPrompts.resolve(current, async (promptSignal) => {
|
|
6351
|
-
const choice = await showPermissionPrompt(ctx, `Network blocked: "${domain}" is not in the allowed domains list`, PERMISSION_OPTIONS, promptSignal);
|
|
6380
|
+
const choice = await showPermissionPrompt(ctx, `Network blocked: "${domain}" is not in the allowed domains list`, PERMISSION_OPTIONS, promptSignal, allowances?.permissionPromptLabel);
|
|
6352
6381
|
if (choice === "abort" || promptSignal.aborted)
|
|
6353
6382
|
return false;
|
|
6354
6383
|
await applyDomainChoice(choice, domain, cwd, allowances);
|
|
@@ -6428,7 +6457,7 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
|
|
|
6428
6457
|
if (!ctx.hasUI || !promptOnBlock)
|
|
6429
6458
|
return { action: "deny", reason: "unprompted" };
|
|
6430
6459
|
return permissionPrompts.resolve(current2, async (promptSignal) => {
|
|
6431
|
-
const choice = await showPermissionPrompt(ctx, `Network blocked: ${trap.operation} "${trap.target}"`, NETWORK_PERMISSION_OPTIONS, promptSignal);
|
|
6460
|
+
const choice = await showPermissionPrompt(ctx, `Network blocked: ${trap.operation} "${trap.target}"`, NETWORK_PERMISSION_OPTIONS, promptSignal, allowances.permissionPromptLabel);
|
|
6432
6461
|
if (choice === "abort" || promptSignal.aborted)
|
|
6433
6462
|
return { action: "deny", reason: "rejected" };
|
|
6434
6463
|
const targets = choice === "once" ? allowances.targets : sessionAllowedTargets;
|
|
@@ -6462,7 +6491,7 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
|
|
|
6462
6491
|
return { action: "deny", reason: "unprompted" };
|
|
6463
6492
|
return permissionPrompts.resolve(current, async (promptSignal) => {
|
|
6464
6493
|
const config = loadConfig(cwd);
|
|
6465
|
-
const choice = trap.operation === "read" ? await promptReadBlock(ctx, path, matchesPattern(path, config.filesystem.denyRead, cwd) ? "granting allowRead will override it" : undefined, promptSignal) : await showPermissionPrompt(ctx, `Write blocked: "${path}" is not in allowWrite`, PERMISSION_OPTIONS, promptSignal);
|
|
6494
|
+
const choice = trap.operation === "read" ? await promptReadBlock(ctx, path, matchesPattern(path, config.filesystem.denyRead, cwd) ? "granting allowRead will override it" : undefined, promptSignal, allowances.permissionPromptLabel) : await showPermissionPrompt(ctx, `Write blocked: "${path}" is not in allowWrite`, PERMISSION_OPTIONS, promptSignal, allowances.permissionPromptLabel);
|
|
6466
6495
|
if (choice === "abort" || promptSignal.aborted)
|
|
6467
6496
|
return { action: "deny", reason: "rejected" };
|
|
6468
6497
|
if (trap.operation === "read") {
|
|
@@ -6569,6 +6598,7 @@ ${paths}${note}`, PERMISSION_OPTIONS, promptSignal);
|
|
|
6569
6598
|
return prepareUnsandboxedRpcWorker(options2);
|
|
6570
6599
|
}
|
|
6571
6600
|
const allowances = {
|
|
6601
|
+
permissionPromptLabel: options2.permissionPromptLabel,
|
|
6572
6602
|
domains: [...options2.domains ?? []],
|
|
6573
6603
|
readPaths: [...options2.readPaths],
|
|
6574
6604
|
writePaths: [...options2.writePaths],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-landstrip",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.2",
|
|
4
4
|
"description": "Sandboxed Bash and process-backed agents for Pi",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"landstrip",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"ci:test": "npm run test"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@landstrip/landstrip-api": "^0.19.
|
|
42
|
+
"@landstrip/landstrip-api": "^0.19.2",
|
|
43
43
|
"jsonc-parser": "^3.3.1",
|
|
44
44
|
"minimatch": "^10.2.5",
|
|
45
45
|
"undici": "^8.9.0",
|