pi-landstrip 0.18.7 → 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 +171 -104
- 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
|
}
|
|
@@ -1734,6 +1748,25 @@ var packageDir = dirname4(fileURLToPath(import.meta.url));
|
|
|
1734
1748
|
var MAX_TASK_OUTPUT_BYTES = 64 * 1024;
|
|
1735
1749
|
var INSPECTOR_BODY_LINES = 16;
|
|
1736
1750
|
var PI_THINKING_LEVELS = new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
|
|
1751
|
+
var AGENTS_HELP_ROWS = [
|
|
1752
|
+
["Tab", "Next tab"],
|
|
1753
|
+
["Shift+Tab / S", "Switch scope in Agents or Settings"],
|
|
1754
|
+
["↑ / ↓", "Select item or scroll when follow is off"],
|
|
1755
|
+
["F", "Toggle task log follow"],
|
|
1756
|
+
["Page Up / Down", "Scroll task output by page"],
|
|
1757
|
+
["Home / End", "Jump to task output boundary"],
|
|
1758
|
+
["Enter", "Open logs, set primary, or save"],
|
|
1759
|
+
["Esc", "Back or close"],
|
|
1760
|
+
["Ctrl+C", "Close"],
|
|
1761
|
+
["Space", "Enable agent or toggle sandbox"],
|
|
1762
|
+
["E", "Edit project agent"],
|
|
1763
|
+
["X", "Delete project agent"],
|
|
1764
|
+
["Y / N", "Confirm or cancel deletion"],
|
|
1765
|
+
["I", "Use global value"],
|
|
1766
|
+
["R", "Discard setting changes"],
|
|
1767
|
+
["0-9 / + / -", "Set subagent limit"],
|
|
1768
|
+
["Backspace", "Open parent task"]
|
|
1769
|
+
];
|
|
1737
1770
|
var MIN_SUPPORTED_PI_VERSION = [0, 82, 0];
|
|
1738
1771
|
var cachedPiPackage;
|
|
1739
1772
|
var piPackageResolved = false;
|
|
@@ -1845,6 +1878,40 @@ function taskDuration(details) {
|
|
|
1845
1878
|
const seconds = Math.max(0, end - details.startedAt) / 1000;
|
|
1846
1879
|
return seconds < 10 ? `${seconds.toFixed(1)}s` : `${Math.round(seconds)}s`;
|
|
1847
1880
|
}
|
|
1881
|
+
function activeTaskRecords(tasks) {
|
|
1882
|
+
const byId = new Map(tasks.map((task) => [task.id, task]));
|
|
1883
|
+
const visibleIds = new Set;
|
|
1884
|
+
for (const task of tasks) {
|
|
1885
|
+
if (task.state !== "queued" && task.state !== "running")
|
|
1886
|
+
continue;
|
|
1887
|
+
const seen = new Set;
|
|
1888
|
+
let current = task;
|
|
1889
|
+
while (current && !seen.has(current.id)) {
|
|
1890
|
+
seen.add(current.id);
|
|
1891
|
+
visibleIds.add(current.id);
|
|
1892
|
+
current = current.parentTaskId ? byId.get(current.parentTaskId) : undefined;
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
return tasks.filter((task) => visibleIds.has(task.id));
|
|
1896
|
+
}
|
|
1897
|
+
function taskProgress(task) {
|
|
1898
|
+
if (task.state === "queued")
|
|
1899
|
+
return ["waiting for slot"];
|
|
1900
|
+
if (task.state !== "running")
|
|
1901
|
+
return [];
|
|
1902
|
+
const progress = [];
|
|
1903
|
+
if (task.currentTool)
|
|
1904
|
+
progress.push(`→ ${task.currentTool}`);
|
|
1905
|
+
if (task.toolCalls !== undefined) {
|
|
1906
|
+
progress.push(`${task.toolCalls} call${task.toolCalls === 1 ? "" : "s"}`);
|
|
1907
|
+
}
|
|
1908
|
+
const duration = taskDuration(taskDetails(task));
|
|
1909
|
+
if (duration)
|
|
1910
|
+
progress.push(duration);
|
|
1911
|
+
if (task.retryAttempt)
|
|
1912
|
+
progress.push(`retry ${task.retryAttempt}`);
|
|
1913
|
+
return progress;
|
|
1914
|
+
}
|
|
1848
1915
|
function taskOutput(details, fallback) {
|
|
1849
1916
|
return details.error ?? details.output ?? fallback;
|
|
1850
1917
|
}
|
|
@@ -2099,6 +2166,15 @@ function permissionName(tool) {
|
|
|
2099
2166
|
return "list";
|
|
2100
2167
|
return tool;
|
|
2101
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
|
+
}
|
|
2102
2178
|
function canonicalPermissionPath(path, seen = new Set) {
|
|
2103
2179
|
const missing = [];
|
|
2104
2180
|
let existing = path;
|
|
@@ -2188,6 +2264,10 @@ function parseControlResponse(value) {
|
|
|
2188
2264
|
function registerSubagentWorker(pi, config) {
|
|
2189
2265
|
pi.on("session_start", () => {
|
|
2190
2266
|
delete process.env[WORKER_ENV];
|
|
2267
|
+
enforcePermittedTools(pi, config.rules);
|
|
2268
|
+
});
|
|
2269
|
+
pi.on("before_agent_start", () => {
|
|
2270
|
+
enforcePermittedTools(pi, config.rules);
|
|
2191
2271
|
});
|
|
2192
2272
|
pi.on("tool_call", async (event, ctx) => {
|
|
2193
2273
|
if (event.toolName === "task")
|
|
@@ -2379,7 +2459,7 @@ ${this.primaryAgent.prompt}` };
|
|
|
2379
2459
|
let maxSubagentsSettings = loadMaxSubagentsSettings(ctx.cwd, projectTrusted);
|
|
2380
2460
|
const sandboxCallbacks = this.integration.sandboxCallbacks;
|
|
2381
2461
|
let sandboxSettings = sandboxCallbacks?.load(ctx.cwd, projectTrusted) ?? { global: true };
|
|
2382
|
-
let tab = requested ? "
|
|
2462
|
+
let tab = requested ? "logs" : "agents";
|
|
2383
2463
|
let selectedAgent = Math.max(0, agents.findIndex((agent) => agent.name === this.primaryAgent?.name));
|
|
2384
2464
|
let selectedTask = requested ? tasks.findIndex((task) => task.id === requested || task.id.startsWith(requested)) : 0;
|
|
2385
2465
|
if (selectedTask < 0) {
|
|
@@ -2391,7 +2471,7 @@ ${this.primaryAgent.prompt}` };
|
|
|
2391
2471
|
let saving = false;
|
|
2392
2472
|
let editing = false;
|
|
2393
2473
|
let confirmingDeleteAgent;
|
|
2394
|
-
let
|
|
2474
|
+
let follow = requested.length > 0;
|
|
2395
2475
|
let scroll = 0;
|
|
2396
2476
|
const reloadAgents = () => {
|
|
2397
2477
|
const selectedName = agents[selectedAgent]?.name;
|
|
@@ -2444,7 +2524,7 @@ ${this.primaryAgent.prompt}` };
|
|
|
2444
2524
|
return `${clipped}${" ".repeat(Math.max(0, cellWidth - visibleWidth2(clipped)))}`;
|
|
2445
2525
|
};
|
|
2446
2526
|
refreshTasks();
|
|
2447
|
-
const tabs = `${dialogTabs(theme, ["Agents", "Tasks", "Settings"], `${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"}`)}`;
|
|
2448
2528
|
if (tab === "agents") {
|
|
2449
2529
|
const start = Math.max(0, Math.min(selectedAgent - 3, agents.length - 7));
|
|
2450
2530
|
const shown2 = agents.slice(start, start + 7);
|
|
@@ -2513,45 +2593,30 @@ ${this.primaryAgent.prompt}` };
|
|
|
2513
2593
|
lines2.push(` ${theme.fg("error", diagnostic)}`);
|
|
2514
2594
|
}
|
|
2515
2595
|
}
|
|
2516
|
-
lines2.push("");
|
|
2517
2596
|
if (confirmingDeleteAgent) {
|
|
2518
|
-
lines2.push(theme.fg("error", `Delete @${confirmingDeleteAgent}? This removes its project file.`), dialogKeys(theme, [
|
|
2597
|
+
lines2.push("", theme.fg("error", `Delete @${confirmingDeleteAgent}? This removes its project file.`), dialogKeys(theme, [
|
|
2519
2598
|
["Y", "delete"],
|
|
2520
2599
|
["N / Esc", "cancel"]
|
|
2521
2600
|
]));
|
|
2522
2601
|
} else if (saving) {
|
|
2523
|
-
lines2.push(theme.fg("dim", "Saving…"));
|
|
2524
|
-
} else {
|
|
2525
|
-
const mainHints = [["↑↓", "select"]];
|
|
2526
|
-
if (agent && !agent.disabled && agentSupportsMode(agent, "primary")) {
|
|
2527
|
-
mainHints.push(["Enter", "set primary"]);
|
|
2528
|
-
}
|
|
2529
|
-
if (agent && scopedAgent(agent))
|
|
2530
|
-
mainHints.push(["Space", "enable / disable"]);
|
|
2531
|
-
const actionHints = [];
|
|
2532
|
-
if (agent && projectTrusted)
|
|
2533
|
-
actionHints.push(["E", "edit"]);
|
|
2534
|
-
if (agent && projectTrusted && canDeleteProjectAgent(ctx.cwd, agent)) {
|
|
2535
|
-
actionHints.push(["X", "delete"]);
|
|
2536
|
-
}
|
|
2537
|
-
if (agent && scope === "project" && disabledOverrides.project.has(agent.name)) {
|
|
2538
|
-
actionHints.push(["I", "inherit"]);
|
|
2539
|
-
}
|
|
2540
|
-
if (projectTrusted)
|
|
2541
|
-
actionHints.push(["S", "scope"]);
|
|
2542
|
-
actionHints.push(["Tab", "next tab"], ["Esc", "close"]);
|
|
2543
|
-
lines2.push(dialogKeys(theme, mainHints), dialogKeys(theme, actionHints));
|
|
2602
|
+
lines2.push("", theme.fg("dim", "Saving…"));
|
|
2544
2603
|
}
|
|
2545
2604
|
return box(lines2);
|
|
2546
2605
|
}
|
|
2547
2606
|
if (tab === "settings") {
|
|
2548
|
-
const
|
|
2549
|
-
const
|
|
2607
|
+
const maxSubagentsInherited = scope === "project" && maxSubagentsSettings.project === undefined;
|
|
2608
|
+
const sandboxInherited = scope === "project" && sandboxSettings.project === undefined;
|
|
2550
2609
|
const rows = [
|
|
2551
|
-
{
|
|
2610
|
+
{
|
|
2611
|
+
label: "Maximum subagents",
|
|
2612
|
+
value: String(selectedMaxSubagents()),
|
|
2613
|
+
inherited: maxSubagentsInherited,
|
|
2614
|
+
unavailable: false
|
|
2615
|
+
},
|
|
2552
2616
|
{
|
|
2553
2617
|
label: "Sandbox enabled",
|
|
2554
|
-
value:
|
|
2618
|
+
value: selectedSandboxEnabled() ? "on" : "off",
|
|
2619
|
+
inherited: sandboxInherited,
|
|
2555
2620
|
unavailable: sandboxCallbacks === undefined
|
|
2556
2621
|
}
|
|
2557
2622
|
];
|
|
@@ -2560,39 +2625,31 @@ ${this.primaryAgent.prompt}` };
|
|
|
2560
2625
|
const selected = index === selectedSetting;
|
|
2561
2626
|
const cursor = selected ? theme.fg("accent", "›") : " ";
|
|
2562
2627
|
const text = `[ ${row.value} ]`;
|
|
2563
|
-
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);
|
|
2564
2629
|
const label = theme.fg(row.unavailable ? "muted" : "text", row.label);
|
|
2565
2630
|
const dirty = dirtySettings.has(index) ? theme.fg("warning", " *") : "";
|
|
2566
2631
|
lines2.push(`${cursor} ${value} ${label}${dirty}`);
|
|
2567
2632
|
}
|
|
2568
|
-
lines2.push("", theme.fg("dim", selectedSetting === 0 ? "Sets concurrent subagents. Zero disables the task tool." : "Controls OS sandboxing. Use /sandbox to inspect its policy."));
|
|
2569
|
-
if (saving)
|
|
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."));
|
|
2634
|
+
if (saving)
|
|
2570
2635
|
lines2.push("", theme.fg("dim", "Saving…"));
|
|
2571
|
-
} else {
|
|
2572
|
-
const editHints = [["↑↓", "select"]];
|
|
2573
|
-
if (selectedSetting === 0)
|
|
2574
|
-
editHints.push(["0–9 / +−", "change limit"]);
|
|
2575
|
-
else if (sandboxCallbacks)
|
|
2576
|
-
editHints.push(["Space", "toggle"]);
|
|
2577
|
-
const canInherit = scope === "project" && (selectedSetting === 0 ? maxSubagentsSettings.project !== undefined : sandboxSettings.project !== undefined);
|
|
2578
|
-
if (canInherit)
|
|
2579
|
-
editHints.push(["I", "inherit"]);
|
|
2580
|
-
const actionHints = [];
|
|
2581
|
-
if (dirtySettings.size > 0) {
|
|
2582
|
-
if (dirtySettings.has(selectedSetting))
|
|
2583
|
-
actionHints.push(["Enter", "save"]);
|
|
2584
|
-
actionHints.push(["R", "discard"]);
|
|
2585
|
-
} else {
|
|
2586
|
-
actionHints.push(["S", "scope"], ["Tab", "next tab"], ["Esc", "close"]);
|
|
2587
|
-
}
|
|
2588
|
-
lines2.push("", dialogKeys(theme, editHints), dialogKeys(theme, actionHints));
|
|
2589
|
-
}
|
|
2590
2636
|
return box(lines2);
|
|
2591
2637
|
}
|
|
2592
|
-
if (
|
|
2593
|
-
const
|
|
2638
|
+
if (tab === "help") {
|
|
2639
|
+
const shortcutWidth = Math.max(visibleWidth2("Shortcut"), ...AGENTS_HELP_ROWS.map(([shortcut]) => visibleWidth2(shortcut)));
|
|
2640
|
+
const descriptionWidth = Math.max(1, contentWidth - shortcutWidth - 4);
|
|
2641
|
+
const lines2 = [
|
|
2642
|
+
tabs,
|
|
2643
|
+
"",
|
|
2644
|
+
theme.fg("dim", ` ${pad("Shortcut", shortcutWidth)} Description`),
|
|
2645
|
+
...AGENTS_HELP_ROWS.map(([shortcut, description]) => ` ${theme.fg("accent", pad(shortcut, shortcutWidth))} ${theme.fg("text", truncateToWidth2(description, descriptionWidth))}`)
|
|
2646
|
+
];
|
|
2647
|
+
return box(lines2);
|
|
2648
|
+
}
|
|
2649
|
+
if (tab === "tasks") {
|
|
2650
|
+
const listLines = [];
|
|
2594
2651
|
if (tasks.length === 0) {
|
|
2595
|
-
|
|
2652
|
+
listLines.push("No task sessions in this session.");
|
|
2596
2653
|
} else {
|
|
2597
2654
|
const start = Math.max(0, Math.min(selectedTask - 5, tasks.length - 11));
|
|
2598
2655
|
const shown2 = tasks.slice(start, start + 11);
|
|
@@ -2600,23 +2657,17 @@ ${this.primaryAgent.prompt}` };
|
|
|
2600
2657
|
const index = start + offset;
|
|
2601
2658
|
const cursor = index === selectedTask ? theme.fg("accent", "›") : " ";
|
|
2602
2659
|
const indent = " ".repeat(Math.max(0, task2.depth - 1));
|
|
2603
|
-
|
|
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))}`);
|
|
2604
2661
|
}
|
|
2605
2662
|
}
|
|
2606
|
-
|
|
2607
|
-
["↑↓", "select"],
|
|
2608
|
-
["Enter", "inspect"],
|
|
2609
|
-
["Tab", "next tab"],
|
|
2610
|
-
["Esc", "close"]
|
|
2611
|
-
]));
|
|
2612
|
-
return box(lines2);
|
|
2663
|
+
return box([tabs, "", ...listLines]);
|
|
2613
2664
|
}
|
|
2614
2665
|
const task = tasks[selectedTask];
|
|
2615
2666
|
if (!task)
|
|
2616
2667
|
return box([tabs, "", "No task sessions in this session."]);
|
|
2617
2668
|
const transcript = taskTranscript(task).flatMap((line) => wrapTextWithAnsi(line, contentWidth));
|
|
2618
2669
|
const maxScroll = Math.max(0, transcript.length - INSPECTOR_BODY_LINES);
|
|
2619
|
-
scroll = Math.min(scroll, maxScroll);
|
|
2670
|
+
scroll = follow ? maxScroll : Math.min(scroll, maxScroll);
|
|
2620
2671
|
const shown = transcript.slice(scroll, scroll + INSPECTOR_BODY_LINES);
|
|
2621
2672
|
const duration = taskDuration(taskDetails(task));
|
|
2622
2673
|
const metrics = [
|
|
@@ -2628,7 +2679,7 @@ ${this.primaryAgent.prompt}` };
|
|
|
2628
2679
|
tabs,
|
|
2629
2680
|
"",
|
|
2630
2681
|
`${theme.fg("accent", theme.bold(`@${task.agent}`))} ${theme.fg("text", task.description)}`,
|
|
2631
|
-
`${taskState(theme, task)} ${theme.fg("dim", metrics.join(" · "))}`,
|
|
2682
|
+
`${taskState(theme, task)} ${theme.fg("dim", metrics.join(" · "))} ${theme.fg("dim", "·")} ${theme.fg(follow ? "accent" : "muted", `Follow: ${follow ? "on" : "off"}`)}`,
|
|
2632
2683
|
"",
|
|
2633
2684
|
...shown.map((line) => theme.fg("toolOutput", line))
|
|
2634
2685
|
];
|
|
@@ -2637,12 +2688,6 @@ ${this.primaryAgent.prompt}` };
|
|
|
2637
2688
|
if (transcript.length > INSPECTOR_BODY_LINES) {
|
|
2638
2689
|
lines.push(theme.fg("dim", `${scroll + 1}–${scroll + shown.length} of ${transcript.length}`));
|
|
2639
2690
|
}
|
|
2640
|
-
lines.push("", dialogKeys(theme, [
|
|
2641
|
-
["↑↓", "scroll"],
|
|
2642
|
-
...task.parentTaskId ? [["Backspace", "parent"]] : [],
|
|
2643
|
-
["Esc", "task list"],
|
|
2644
|
-
["Tab", "next tab"]
|
|
2645
|
-
]));
|
|
2646
2691
|
return box(lines.map((line) => truncateToWidth2(line, contentWidth)));
|
|
2647
2692
|
},
|
|
2648
2693
|
handleInput: (data) => {
|
|
@@ -2674,7 +2719,7 @@ ${this.primaryAgent.prompt}` };
|
|
|
2674
2719
|
}
|
|
2675
2720
|
return;
|
|
2676
2721
|
}
|
|
2677
|
-
if (data.toLowerCase() === "s" && tab
|
|
2722
|
+
if ((matchesKey(data, "shift+tab") || data.toLowerCase() === "s") && (tab === "agents" || tab === "settings")) {
|
|
2678
2723
|
if (saving)
|
|
2679
2724
|
return;
|
|
2680
2725
|
if (dirtySettings.size > 0) {
|
|
@@ -2697,9 +2742,12 @@ ${this.primaryAgent.prompt}` };
|
|
|
2697
2742
|
ctx.ui.notify("Save changes with Enter or discard them with R before leaving", "warning");
|
|
2698
2743
|
return;
|
|
2699
2744
|
}
|
|
2700
|
-
const tabs = ["agents", "tasks", "settings"];
|
|
2745
|
+
const tabs = ["agents", "tasks", "logs", "settings", "help"];
|
|
2701
2746
|
tab = tabs[(tabs.indexOf(tab) + 1) % tabs.length] ?? "agents";
|
|
2702
|
-
|
|
2747
|
+
if (tab === "logs" && tasks.length > 0)
|
|
2748
|
+
follow = true;
|
|
2749
|
+
else
|
|
2750
|
+
follow = false;
|
|
2703
2751
|
editing = false;
|
|
2704
2752
|
scroll = 0;
|
|
2705
2753
|
tui.requestRender();
|
|
@@ -2865,7 +2913,12 @@ ${this.primaryAgent.prompt}` };
|
|
|
2865
2913
|
tui.requestRender();
|
|
2866
2914
|
return;
|
|
2867
2915
|
}
|
|
2868
|
-
if (
|
|
2916
|
+
if (tab === "help") {
|
|
2917
|
+
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c"))
|
|
2918
|
+
done();
|
|
2919
|
+
return;
|
|
2920
|
+
}
|
|
2921
|
+
if (tab === "tasks") {
|
|
2869
2922
|
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c"))
|
|
2870
2923
|
done();
|
|
2871
2924
|
else if (matchesKey(data, "up"))
|
|
@@ -2873,31 +2926,51 @@ ${this.primaryAgent.prompt}` };
|
|
|
2873
2926
|
else if (matchesKey(data, "down") && tasks.length > 0) {
|
|
2874
2927
|
selectedTask = Math.min(tasks.length - 1, selectedTask + 1);
|
|
2875
2928
|
} else if (matchesKey(data, "return") && tasks.length > 0) {
|
|
2876
|
-
|
|
2929
|
+
tab = "logs";
|
|
2930
|
+
follow = true;
|
|
2877
2931
|
scroll = 0;
|
|
2878
2932
|
} else
|
|
2879
2933
|
return;
|
|
2880
2934
|
tui.requestRender();
|
|
2881
2935
|
return;
|
|
2882
2936
|
}
|
|
2937
|
+
if (tab !== "logs")
|
|
2938
|
+
return;
|
|
2883
2939
|
const task = tasks[selectedTask];
|
|
2884
|
-
if (!task)
|
|
2940
|
+
if (!task) {
|
|
2941
|
+
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c"))
|
|
2942
|
+
done();
|
|
2885
2943
|
return;
|
|
2944
|
+
}
|
|
2886
2945
|
if (matchesKey(data, "escape")) {
|
|
2887
|
-
|
|
2946
|
+
tab = "tasks";
|
|
2947
|
+
follow = false;
|
|
2888
2948
|
scroll = 0;
|
|
2889
2949
|
} else if (matchesKey(data, "ctrl+c")) {
|
|
2890
2950
|
done();
|
|
2891
|
-
} else if (
|
|
2951
|
+
} else if (data.toLowerCase() === "f") {
|
|
2952
|
+
follow = !follow;
|
|
2953
|
+
} else if (!follow && matchesKey(data, "up")) {
|
|
2892
2954
|
scroll = Math.max(0, scroll - 1);
|
|
2893
|
-
} else if (matchesKey(data, "down")) {
|
|
2955
|
+
} else if (!follow && matchesKey(data, "down")) {
|
|
2894
2956
|
scroll += 1;
|
|
2957
|
+
} else if (!follow && matchesKey(data, "pageUp")) {
|
|
2958
|
+
scroll = Math.max(0, scroll - INSPECTOR_BODY_LINES);
|
|
2959
|
+
} else if (!follow && matchesKey(data, "pageDown")) {
|
|
2960
|
+
scroll += INSPECTOR_BODY_LINES;
|
|
2961
|
+
} else if (!follow && matchesKey(data, "home")) {
|
|
2962
|
+
scroll = 0;
|
|
2963
|
+
} else if (!follow && matchesKey(data, "end")) {
|
|
2964
|
+
scroll = Number.MAX_SAFE_INTEGER;
|
|
2895
2965
|
} else if (matchesKey(data, "backspace")) {
|
|
2896
2966
|
const parentIndex = task.parentTaskId ? tasks.findIndex((candidate) => candidate.id === task.parentTaskId) : -1;
|
|
2897
|
-
if (parentIndex >= 0)
|
|
2967
|
+
if (parentIndex >= 0) {
|
|
2898
2968
|
selectedTask = parentIndex;
|
|
2899
|
-
|
|
2900
|
-
|
|
2969
|
+
follow = true;
|
|
2970
|
+
} else {
|
|
2971
|
+
tab = "tasks";
|
|
2972
|
+
follow = false;
|
|
2973
|
+
}
|
|
2901
2974
|
scroll = 0;
|
|
2902
2975
|
} else
|
|
2903
2976
|
return;
|
|
@@ -3368,7 +3441,8 @@ ${catalog.diagnostics.join(`
|
|
|
3368
3441
|
ctx.ui.notify(`Agent ${agent.name} options are not supported by Pi RPC mode: ${providerOptionNames.join(", ")}`, "warning");
|
|
3369
3442
|
}
|
|
3370
3443
|
const taskEnabled = agent.permissions.some((rule) => rule.permission === "task" && rule.action !== "deny");
|
|
3371
|
-
const
|
|
3444
|
+
const activeTools = taskEnabled ? [...new Set([...this.pi.getActiveTools(), "task"])] : this.pi.getActiveTools().filter((tool) => tool !== "task");
|
|
3445
|
+
const tools = permittedToolNames(activeTools, rules);
|
|
3372
3446
|
const workerExtensions = this.integration.getWorkerExtensions?.() ?? [];
|
|
3373
3447
|
const args = [
|
|
3374
3448
|
...invocation.args,
|
|
@@ -3661,33 +3735,26 @@ ${renderTaskResult(task.id, "error", message)}`);
|
|
|
3661
3735
|
if (!ctx.hasUI || ctx.mode !== undefined && ctx.mode !== "tui")
|
|
3662
3736
|
return;
|
|
3663
3737
|
const tasks = [...this.tasks.values()];
|
|
3664
|
-
const byId = new Map(tasks.map((task) => [task.id, task]));
|
|
3665
|
-
const rootId = (task) => {
|
|
3666
|
-
const seen = new Set;
|
|
3667
|
-
let current = task;
|
|
3668
|
-
while (current.parentTaskId && !seen.has(current.id)) {
|
|
3669
|
-
seen.add(current.id);
|
|
3670
|
-
const parent = byId.get(current.parentTaskId);
|
|
3671
|
-
if (!parent)
|
|
3672
|
-
break;
|
|
3673
|
-
current = parent;
|
|
3674
|
-
}
|
|
3675
|
-
return current.id;
|
|
3676
|
-
};
|
|
3677
3738
|
const active = tasks.filter((task) => task.state === "queued" || task.state === "running");
|
|
3678
|
-
|
|
3679
|
-
if (activeRoots.size === 0) {
|
|
3739
|
+
if (active.length === 0) {
|
|
3680
3740
|
ctx.ui.setWidget(TASK_WIDGET, undefined);
|
|
3681
3741
|
return;
|
|
3682
3742
|
}
|
|
3683
|
-
const visible = tasks
|
|
3743
|
+
const visible = activeTaskRecords(tasks);
|
|
3744
|
+
const byId = new Map(tasks.map((task) => [task.id, task]));
|
|
3745
|
+
const running = active.filter((task) => task.state === "running").length;
|
|
3746
|
+
const queued = active.length - running;
|
|
3747
|
+
const runningSummary = this.maxSubagents > 0 ? `${running}/${this.maxSubagents} running` : `${running} running`;
|
|
3748
|
+
const summary = queued > 0 ? `${runningSummary} · ${queued} queued` : runningSummary;
|
|
3684
3749
|
ctx.ui.setWidget(TASK_WIDGET, (_tui, theme) => ({
|
|
3685
3750
|
render: (width) => {
|
|
3686
|
-
const header = theme.fg("accent", theme.bold("Subagents")) + theme.fg("dim", ` ${
|
|
3751
|
+
const header = theme.fg("accent", theme.bold("Subagents")) + theme.fg("dim", ` ${summary}`);
|
|
3687
3752
|
const tree = taskTreeLines(visible, (task) => {
|
|
3688
3753
|
const agent = theme.fg("accent", `@${task.agent}`);
|
|
3689
3754
|
const description = theme.fg("text", task.description);
|
|
3690
|
-
|
|
3755
|
+
const progress = taskProgress(byId.get(task.id));
|
|
3756
|
+
const suffix = progress.length > 0 ? theme.fg("dim", ` · ${progress.join(" · ")}`) : "";
|
|
3757
|
+
return `${taskState(theme, task)} ${agent} ${description}${suffix}`;
|
|
3691
3758
|
});
|
|
3692
3759
|
const shown = tree.slice(0, 8);
|
|
3693
3760
|
if (tree.length > shown.length) {
|
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"
|