pi-landstrip 0.18.8 → 0.18.9
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 +67 -59
- package/package.json +2 -2
package/dist/index.ts
CHANGED
|
@@ -1006,19 +1006,33 @@ function loadAgentCatalog(cwd, piAgentDir = getAgentDir2(), includeProject = tru
|
|
|
1006
1006
|
}
|
|
1007
1007
|
return { agents: normalized, permissions, diagnostics, maxSubagents };
|
|
1008
1008
|
}
|
|
1009
|
+
function permissionMatches(pattern, permission) {
|
|
1010
|
+
return pattern === "*" || minimatch(permission, pattern, { dot: true });
|
|
1011
|
+
}
|
|
1009
1012
|
function permissionDecision(rules, permission, resource = "*") {
|
|
1010
1013
|
let decision = "ask";
|
|
1011
1014
|
for (const rule of rules) {
|
|
1012
|
-
const matchesPermission = rule.permission === "*" || minimatch(permission, rule.permission, { dot: true });
|
|
1013
1015
|
const matchesResource = rule.pattern === "*" || minimatch(normalizeAbsolutePath(resource), normalizeAbsolutePath(rule.pattern), {
|
|
1014
1016
|
dot: true,
|
|
1015
1017
|
matchBase: false
|
|
1016
1018
|
});
|
|
1017
|
-
if (
|
|
1019
|
+
if (permissionMatches(rule.permission, permission) && matchesResource)
|
|
1018
1020
|
decision = rule.action;
|
|
1019
1021
|
}
|
|
1020
1022
|
return decision;
|
|
1021
1023
|
}
|
|
1024
|
+
function permissionAlwaysDenied(rules, permission) {
|
|
1025
|
+
let denied = false;
|
|
1026
|
+
for (const rule of rules) {
|
|
1027
|
+
if (!permissionMatches(rule.permission, permission))
|
|
1028
|
+
continue;
|
|
1029
|
+
if (rule.pattern === "*")
|
|
1030
|
+
denied = rule.action === "deny";
|
|
1031
|
+
else if (rule.action !== "deny")
|
|
1032
|
+
denied = false;
|
|
1033
|
+
}
|
|
1034
|
+
return denied;
|
|
1035
|
+
}
|
|
1022
1036
|
function mergePermissionRules(...values) {
|
|
1023
1037
|
return values.flatMap((value) => value);
|
|
1024
1038
|
}
|
|
@@ -1733,8 +1747,6 @@ var MAX_DEPTH = 3;
|
|
|
1733
1747
|
var packageDir = dirname4(fileURLToPath(import.meta.url));
|
|
1734
1748
|
var MAX_TASK_OUTPUT_BYTES = 64 * 1024;
|
|
1735
1749
|
var INSPECTOR_BODY_LINES = 16;
|
|
1736
|
-
var TASK_PREVIEW_MIN_WIDTH = 92;
|
|
1737
|
-
var TASK_PREVIEW_LINES = 7;
|
|
1738
1750
|
var PI_THINKING_LEVELS = new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
|
|
1739
1751
|
var AGENTS_HELP_ROWS = [
|
|
1740
1752
|
["Tab", "Next tab"],
|
|
@@ -1743,7 +1755,7 @@ var AGENTS_HELP_ROWS = [
|
|
|
1743
1755
|
["F", "Toggle task log follow"],
|
|
1744
1756
|
["Page Up / Down", "Scroll task output by page"],
|
|
1745
1757
|
["Home / End", "Jump to task output boundary"],
|
|
1746
|
-
["Enter", "
|
|
1758
|
+
["Enter", "Open logs, set primary, or save"],
|
|
1747
1759
|
["Esc", "Back or close"],
|
|
1748
1760
|
["Ctrl+C", "Close"],
|
|
1749
1761
|
["Space", "Enable agent or toggle sandbox"],
|
|
@@ -2154,6 +2166,15 @@ function permissionName(tool) {
|
|
|
2154
2166
|
return "list";
|
|
2155
2167
|
return tool;
|
|
2156
2168
|
}
|
|
2169
|
+
function permittedToolNames(tools, rules) {
|
|
2170
|
+
return tools.filter((tool) => !permissionAlwaysDenied(rules, permissionName(tool)));
|
|
2171
|
+
}
|
|
2172
|
+
function enforcePermittedTools(pi, rules) {
|
|
2173
|
+
const active = pi.getActiveTools();
|
|
2174
|
+
const permitted = permittedToolNames(active, rules);
|
|
2175
|
+
if (permitted.join("\x00") !== active.join("\x00"))
|
|
2176
|
+
pi.setActiveTools(permitted);
|
|
2177
|
+
}
|
|
2157
2178
|
function canonicalPermissionPath(path, seen = new Set) {
|
|
2158
2179
|
const missing = [];
|
|
2159
2180
|
let existing = path;
|
|
@@ -2243,6 +2264,10 @@ function parseControlResponse(value) {
|
|
|
2243
2264
|
function registerSubagentWorker(pi, config) {
|
|
2244
2265
|
pi.on("session_start", () => {
|
|
2245
2266
|
delete process.env[WORKER_ENV];
|
|
2267
|
+
enforcePermittedTools(pi, config.rules);
|
|
2268
|
+
});
|
|
2269
|
+
pi.on("before_agent_start", () => {
|
|
2270
|
+
enforcePermittedTools(pi, config.rules);
|
|
2246
2271
|
});
|
|
2247
2272
|
pi.on("tool_call", async (event, ctx) => {
|
|
2248
2273
|
if (event.toolName === "task")
|
|
@@ -2434,7 +2459,7 @@ ${this.primaryAgent.prompt}` };
|
|
|
2434
2459
|
let maxSubagentsSettings = loadMaxSubagentsSettings(ctx.cwd, projectTrusted);
|
|
2435
2460
|
const sandboxCallbacks = this.integration.sandboxCallbacks;
|
|
2436
2461
|
let sandboxSettings = sandboxCallbacks?.load(ctx.cwd, projectTrusted) ?? { global: true };
|
|
2437
|
-
let tab = requested ? "
|
|
2462
|
+
let tab = requested ? "logs" : "agents";
|
|
2438
2463
|
let selectedAgent = Math.max(0, agents.findIndex((agent) => agent.name === this.primaryAgent?.name));
|
|
2439
2464
|
let selectedTask = requested ? tasks.findIndex((task) => task.id === requested || task.id.startsWith(requested)) : 0;
|
|
2440
2465
|
if (selectedTask < 0) {
|
|
@@ -2446,8 +2471,7 @@ ${this.primaryAgent.prompt}` };
|
|
|
2446
2471
|
let saving = false;
|
|
2447
2472
|
let editing = false;
|
|
2448
2473
|
let confirmingDeleteAgent;
|
|
2449
|
-
let
|
|
2450
|
-
let follow = detail;
|
|
2474
|
+
let follow = requested.length > 0;
|
|
2451
2475
|
let scroll = 0;
|
|
2452
2476
|
const reloadAgents = () => {
|
|
2453
2477
|
const selectedName = agents[selectedAgent]?.name;
|
|
@@ -2500,7 +2524,7 @@ ${this.primaryAgent.prompt}` };
|
|
|
2500
2524
|
return `${clipped}${" ".repeat(Math.max(0, cellWidth - visibleWidth2(clipped)))}`;
|
|
2501
2525
|
};
|
|
2502
2526
|
refreshTasks();
|
|
2503
|
-
const tabs = `${dialogTabs(theme, ["Agents", "Tasks", "Settings", "Help"], `${tab[0]?.toUpperCase()}${tab.slice(1)}`)}${tab === "tasks" ? "" : theme.fg("dim", ` · Scope: ${scope === "project" ? "Project" : "Global"}`)}`;
|
|
2527
|
+
const tabs = `${dialogTabs(theme, ["Agents", "Tasks", "Logs", "Settings", "Help"], `${tab[0]?.toUpperCase()}${tab.slice(1)}`)}${tab === "tasks" || tab === "logs" ? "" : theme.fg("dim", ` · Scope: ${scope === "project" ? "Project" : "Global"}`)}`;
|
|
2504
2528
|
if (tab === "agents") {
|
|
2505
2529
|
const start = Math.max(0, Math.min(selectedAgent - 3, agents.length - 7));
|
|
2506
2530
|
const shown2 = agents.slice(start, start + 7);
|
|
@@ -2580,13 +2604,19 @@ ${this.primaryAgent.prompt}` };
|
|
|
2580
2604
|
return box(lines2);
|
|
2581
2605
|
}
|
|
2582
2606
|
if (tab === "settings") {
|
|
2583
|
-
const
|
|
2584
|
-
const
|
|
2607
|
+
const maxSubagentsInherited = scope === "project" && maxSubagentsSettings.project === undefined;
|
|
2608
|
+
const sandboxInherited = scope === "project" && sandboxSettings.project === undefined;
|
|
2585
2609
|
const rows = [
|
|
2586
|
-
{
|
|
2610
|
+
{
|
|
2611
|
+
label: "Maximum subagents",
|
|
2612
|
+
value: String(selectedMaxSubagents()),
|
|
2613
|
+
inherited: maxSubagentsInherited,
|
|
2614
|
+
unavailable: false
|
|
2615
|
+
},
|
|
2587
2616
|
{
|
|
2588
2617
|
label: "Sandbox enabled",
|
|
2589
|
-
value:
|
|
2618
|
+
value: selectedSandboxEnabled() ? "on" : "off",
|
|
2619
|
+
inherited: sandboxInherited,
|
|
2590
2620
|
unavailable: sandboxCallbacks === undefined
|
|
2591
2621
|
}
|
|
2592
2622
|
];
|
|
@@ -2595,12 +2625,12 @@ ${this.primaryAgent.prompt}` };
|
|
|
2595
2625
|
const selected = index === selectedSetting;
|
|
2596
2626
|
const cursor = selected ? theme.fg("accent", "›") : " ";
|
|
2597
2627
|
const text = `[ ${row.value} ]`;
|
|
2598
|
-
const value = selected ? theme.fg("accent", text) : theme.fg(row.unavailable ? "muted" : "text", text);
|
|
2628
|
+
const value = row.inherited ? theme.fg("dim", text) : selected ? theme.fg("accent", text) : theme.fg(row.unavailable ? "muted" : "text", text);
|
|
2599
2629
|
const label = theme.fg(row.unavailable ? "muted" : "text", row.label);
|
|
2600
2630
|
const dirty = dirtySettings.has(index) ? theme.fg("warning", " *") : "";
|
|
2601
2631
|
lines2.push(`${cursor} ${value} ${label}${dirty}`);
|
|
2602
2632
|
}
|
|
2603
|
-
lines2.push("", theme.fg("dim", selectedSetting === 0 ? "Sets concurrent subagents. Zero disables the task tool." : "Controls OS sandboxing. Use /sandbox to inspect its policy."));
|
|
2633
|
+
lines2.push("", theme.fg("dim", selectedSetting === 0 ? rows[0]?.inherited ? "Sets concurrent subagents. Zero disables the task tool. Using the Global value." : "Sets concurrent subagents. Zero disables the task tool." : rows[1]?.inherited ? "Controls OS sandboxing. Use /sandbox to inspect its policy. Using the Global value." : "Controls OS sandboxing. Use /sandbox to inspect its policy."));
|
|
2604
2634
|
if (saving)
|
|
2605
2635
|
lines2.push("", theme.fg("dim", "Saving…"));
|
|
2606
2636
|
return box(lines2);
|
|
@@ -2616,51 +2646,21 @@ ${this.primaryAgent.prompt}` };
|
|
|
2616
2646
|
];
|
|
2617
2647
|
return box(lines2);
|
|
2618
2648
|
}
|
|
2619
|
-
if (
|
|
2649
|
+
if (tab === "tasks") {
|
|
2620
2650
|
const listLines = [];
|
|
2621
2651
|
if (tasks.length === 0) {
|
|
2622
2652
|
listLines.push("No task sessions in this session.");
|
|
2623
2653
|
} else {
|
|
2624
2654
|
const start = Math.max(0, Math.min(selectedTask - 5, tasks.length - 11));
|
|
2625
2655
|
const shown2 = tasks.slice(start, start + 11);
|
|
2626
|
-
for (const [offset,
|
|
2656
|
+
for (const [offset, task2] of shown2.entries()) {
|
|
2627
2657
|
const index = start + offset;
|
|
2628
2658
|
const cursor = index === selectedTask ? theme.fg("accent", "›") : " ";
|
|
2629
|
-
const indent = " ".repeat(Math.max(0,
|
|
2630
|
-
listLines.push(`${cursor} ${indent}${taskState(theme,
|
|
2659
|
+
const indent = " ".repeat(Math.max(0, task2.depth - 1));
|
|
2660
|
+
listLines.push(`${cursor} ${indent}${taskState(theme, task2)} ${theme.fg("accent", `@${task2.agent}`)} ${theme.fg("text", task2.description)} ${theme.fg("dim", task2.id.slice(0, 8))}`);
|
|
2631
2661
|
}
|
|
2632
2662
|
}
|
|
2633
|
-
|
|
2634
|
-
return box([tabs, "", ...listLines]);
|
|
2635
|
-
}
|
|
2636
|
-
const task2 = tasks[selectedTask];
|
|
2637
|
-
if (!task2)
|
|
2638
|
-
return box([tabs, "", ...listLines]);
|
|
2639
|
-
const leftWidth = Math.max(44, Math.min(64, Math.floor(contentWidth * 0.52)));
|
|
2640
|
-
const rightWidth = Math.max(1, contentWidth - leftWidth - 3);
|
|
2641
|
-
const transcript2 = taskTranscript(task2).flatMap((line) => wrapTextWithAnsi(line, rightWidth));
|
|
2642
|
-
const duration2 = taskDuration(taskDetails(task2));
|
|
2643
|
-
const metrics2 = [
|
|
2644
|
-
task2.currentTool ? `→ ${task2.currentTool}` : undefined,
|
|
2645
|
-
task2.toolCalls === undefined ? undefined : `${task2.toolCalls} tool call${task2.toolCalls === 1 ? "" : "s"}`,
|
|
2646
|
-
duration2,
|
|
2647
|
-
task2.retryAttempt ? `retry ${task2.retryAttempt}` : undefined
|
|
2648
|
-
].filter(Boolean);
|
|
2649
|
-
const previewLines = [
|
|
2650
|
-
`${theme.fg("accent", theme.bold(`@${task2.agent}`))} ${theme.fg("text", task2.description)}`,
|
|
2651
|
-
`${taskState(theme, task2)} ${theme.fg("dim", task2.id)}`,
|
|
2652
|
-
metrics2.length > 0 ? theme.fg("dim", metrics2.join(" · ")) : "",
|
|
2653
|
-
theme.fg("dim", "Latest activity"),
|
|
2654
|
-
...transcript2.slice(-TASK_PREVIEW_LINES).map((line) => theme.fg("toolOutput", line))
|
|
2655
|
-
];
|
|
2656
|
-
const divider = theme.fg("border", "│");
|
|
2657
|
-
const rowCount = Math.max(listLines.length, previewLines.length);
|
|
2658
|
-
const lines2 = Array.from({ length: rowCount }, (_, index) => {
|
|
2659
|
-
const left = pad(listLines[index] ?? "", leftWidth);
|
|
2660
|
-
const right = truncateToWidth2(previewLines[index] ?? "", rightWidth);
|
|
2661
|
-
return `${left} ${divider} ${right}`;
|
|
2662
|
-
});
|
|
2663
|
-
return box([tabs, "", ...lines2]);
|
|
2663
|
+
return box([tabs, "", ...listLines]);
|
|
2664
2664
|
}
|
|
2665
2665
|
const task = tasks[selectedTask];
|
|
2666
2666
|
if (!task)
|
|
@@ -2742,10 +2742,12 @@ ${this.primaryAgent.prompt}` };
|
|
|
2742
2742
|
ctx.ui.notify("Save changes with Enter or discard them with R before leaving", "warning");
|
|
2743
2743
|
return;
|
|
2744
2744
|
}
|
|
2745
|
-
const tabs = ["agents", "tasks", "settings", "help"];
|
|
2745
|
+
const tabs = ["agents", "tasks", "logs", "settings", "help"];
|
|
2746
2746
|
tab = tabs[(tabs.indexOf(tab) + 1) % tabs.length] ?? "agents";
|
|
2747
|
-
|
|
2748
|
-
|
|
2747
|
+
if (tab === "logs" && tasks.length > 0)
|
|
2748
|
+
follow = true;
|
|
2749
|
+
else
|
|
2750
|
+
follow = false;
|
|
2749
2751
|
editing = false;
|
|
2750
2752
|
scroll = 0;
|
|
2751
2753
|
tui.requestRender();
|
|
@@ -2916,7 +2918,7 @@ ${this.primaryAgent.prompt}` };
|
|
|
2916
2918
|
done();
|
|
2917
2919
|
return;
|
|
2918
2920
|
}
|
|
2919
|
-
if (
|
|
2921
|
+
if (tab === "tasks") {
|
|
2920
2922
|
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c"))
|
|
2921
2923
|
done();
|
|
2922
2924
|
else if (matchesKey(data, "up"))
|
|
@@ -2924,7 +2926,7 @@ ${this.primaryAgent.prompt}` };
|
|
|
2924
2926
|
else if (matchesKey(data, "down") && tasks.length > 0) {
|
|
2925
2927
|
selectedTask = Math.min(tasks.length - 1, selectedTask + 1);
|
|
2926
2928
|
} else if (matchesKey(data, "return") && tasks.length > 0) {
|
|
2927
|
-
|
|
2929
|
+
tab = "logs";
|
|
2928
2930
|
follow = true;
|
|
2929
2931
|
scroll = 0;
|
|
2930
2932
|
} else
|
|
@@ -2932,11 +2934,16 @@ ${this.primaryAgent.prompt}` };
|
|
|
2932
2934
|
tui.requestRender();
|
|
2933
2935
|
return;
|
|
2934
2936
|
}
|
|
2937
|
+
if (tab !== "logs")
|
|
2938
|
+
return;
|
|
2935
2939
|
const task = tasks[selectedTask];
|
|
2936
|
-
if (!task)
|
|
2940
|
+
if (!task) {
|
|
2941
|
+
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c"))
|
|
2942
|
+
done();
|
|
2937
2943
|
return;
|
|
2944
|
+
}
|
|
2938
2945
|
if (matchesKey(data, "escape")) {
|
|
2939
|
-
|
|
2946
|
+
tab = "tasks";
|
|
2940
2947
|
follow = false;
|
|
2941
2948
|
scroll = 0;
|
|
2942
2949
|
} else if (matchesKey(data, "ctrl+c")) {
|
|
@@ -2961,7 +2968,7 @@ ${this.primaryAgent.prompt}` };
|
|
|
2961
2968
|
selectedTask = parentIndex;
|
|
2962
2969
|
follow = true;
|
|
2963
2970
|
} else {
|
|
2964
|
-
|
|
2971
|
+
tab = "tasks";
|
|
2965
2972
|
follow = false;
|
|
2966
2973
|
}
|
|
2967
2974
|
scroll = 0;
|
|
@@ -3434,7 +3441,8 @@ ${catalog.diagnostics.join(`
|
|
|
3434
3441
|
ctx.ui.notify(`Agent ${agent.name} options are not supported by Pi RPC mode: ${providerOptionNames.join(", ")}`, "warning");
|
|
3435
3442
|
}
|
|
3436
3443
|
const taskEnabled = agent.permissions.some((rule) => rule.permission === "task" && rule.action !== "deny");
|
|
3437
|
-
const
|
|
3444
|
+
const activeTools = taskEnabled ? [...new Set([...this.pi.getActiveTools(), "task"])] : this.pi.getActiveTools().filter((tool) => tool !== "task");
|
|
3445
|
+
const tools = permittedToolNames(activeTools, rules);
|
|
3438
3446
|
const workerExtensions = this.integration.getWorkerExtensions?.() ?? [];
|
|
3439
3447
|
const args = [
|
|
3440
3448
|
...invocation.args,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-landstrip",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.9",
|
|
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": "^0.18.
|
|
42
|
+
"@landstrip/landstrip": "^0.18.9",
|
|
43
43
|
"jsonc-parser": "^3.3.1",
|
|
44
44
|
"minimatch": "^10.2.5",
|
|
45
45
|
"yaml": "^2.9.0"
|