pi-landstrip 0.18.8 → 0.18.10
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 +169 -64
- 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"],
|
|
@@ -1853,6 +1865,7 @@ function taskDetails(task, state = task.state) {
|
|
|
1853
1865
|
currentTool: task.currentTool,
|
|
1854
1866
|
toolCalls: task.toolCalls,
|
|
1855
1867
|
retryAttempt: task.retryAttempt,
|
|
1868
|
+
usage: task.usage ? { ...task.usage } : undefined,
|
|
1856
1869
|
startedAt: task.startedAt,
|
|
1857
1870
|
finishedAt: task.finishedAt,
|
|
1858
1871
|
output: task.output,
|
|
@@ -1866,6 +1879,78 @@ function taskDuration(details) {
|
|
|
1866
1879
|
const seconds = Math.max(0, end - details.startedAt) / 1000;
|
|
1867
1880
|
return seconds < 10 ? `${seconds.toFixed(1)}s` : `${Math.round(seconds)}s`;
|
|
1868
1881
|
}
|
|
1882
|
+
function usageNumber(value) {
|
|
1883
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
1884
|
+
}
|
|
1885
|
+
function isTaskUsage(value) {
|
|
1886
|
+
return isRecord2(value) && usageNumber(value.input) && usageNumber(value.output) && usageNumber(value.cacheRead) && usageNumber(value.cacheWrite) && usageNumber(value.cost) && usageNumber(value.turns) && Number.isInteger(value.turns);
|
|
1887
|
+
}
|
|
1888
|
+
function taskUsageFromMessage(message) {
|
|
1889
|
+
if (!isRecord2(message) || message.role !== "assistant" || !isRecord2(message.usage))
|
|
1890
|
+
return;
|
|
1891
|
+
const usage = message.usage;
|
|
1892
|
+
if (!usageNumber(usage.input) || !usageNumber(usage.output) || !usageNumber(usage.cacheRead) || !usageNumber(usage.cacheWrite) || !isRecord2(usage.cost) || !usageNumber(usage.cost.total)) {
|
|
1893
|
+
return;
|
|
1894
|
+
}
|
|
1895
|
+
return {
|
|
1896
|
+
input: usage.input,
|
|
1897
|
+
output: usage.output,
|
|
1898
|
+
cacheRead: usage.cacheRead,
|
|
1899
|
+
cacheWrite: usage.cacheWrite,
|
|
1900
|
+
cost: usage.cost.total,
|
|
1901
|
+
turns: 1
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1904
|
+
function addTaskUsage(current, added) {
|
|
1905
|
+
return {
|
|
1906
|
+
input: (current?.input ?? 0) + added.input,
|
|
1907
|
+
output: (current?.output ?? 0) + added.output,
|
|
1908
|
+
cacheRead: (current?.cacheRead ?? 0) + added.cacheRead,
|
|
1909
|
+
cacheWrite: (current?.cacheWrite ?? 0) + added.cacheWrite,
|
|
1910
|
+
cost: (current?.cost ?? 0) + added.cost,
|
|
1911
|
+
turns: (current?.turns ?? 0) + added.turns
|
|
1912
|
+
};
|
|
1913
|
+
}
|
|
1914
|
+
function formatTokens(count) {
|
|
1915
|
+
if (count < 1000)
|
|
1916
|
+
return String(count);
|
|
1917
|
+
if (count < 1e4)
|
|
1918
|
+
return `${(count / 1000).toFixed(1)}k`;
|
|
1919
|
+
if (count < 1e6)
|
|
1920
|
+
return `${Math.round(count / 1000)}k`;
|
|
1921
|
+
if (count < 1e7)
|
|
1922
|
+
return `${(count / 1e6).toFixed(1)}M`;
|
|
1923
|
+
return `${Math.round(count / 1e6)}M`;
|
|
1924
|
+
}
|
|
1925
|
+
function formatTaskUsage(usage) {
|
|
1926
|
+
if (!usage)
|
|
1927
|
+
return;
|
|
1928
|
+
const parts = [];
|
|
1929
|
+
if (usage.turns)
|
|
1930
|
+
parts.push(`${usage.turns} turn${usage.turns === 1 ? "" : "s"}`);
|
|
1931
|
+
if (usage.input)
|
|
1932
|
+
parts.push(`in:${formatTokens(usage.input)}`);
|
|
1933
|
+
if (usage.output)
|
|
1934
|
+
parts.push(`out:${formatTokens(usage.output)}`);
|
|
1935
|
+
if (usage.cacheRead)
|
|
1936
|
+
parts.push(`R${formatTokens(usage.cacheRead)}`);
|
|
1937
|
+
if (usage.cacheWrite)
|
|
1938
|
+
parts.push(`W${formatTokens(usage.cacheWrite)}`);
|
|
1939
|
+
if (usage.cost)
|
|
1940
|
+
parts.push(`$${usage.cost.toFixed(4)}`);
|
|
1941
|
+
return parts.length > 0 ? parts.join(" ") : undefined;
|
|
1942
|
+
}
|
|
1943
|
+
function formatTaskUsageSummary(usage) {
|
|
1944
|
+
if (!usage)
|
|
1945
|
+
return;
|
|
1946
|
+
const parts = [];
|
|
1947
|
+
const tokens = usage.input + usage.output;
|
|
1948
|
+
if (tokens)
|
|
1949
|
+
parts.push(`${formatTokens(tokens)} tok`);
|
|
1950
|
+
if (usage.cost)
|
|
1951
|
+
parts.push(`$${usage.cost.toFixed(4)}`);
|
|
1952
|
+
return parts.length > 0 ? parts.join(" ") : undefined;
|
|
1953
|
+
}
|
|
1869
1954
|
function activeTaskRecords(tasks) {
|
|
1870
1955
|
const byId = new Map(tasks.map((task) => [task.id, task]));
|
|
1871
1956
|
const visibleIds = new Set;
|
|
@@ -1893,6 +1978,9 @@ function taskProgress(task) {
|
|
|
1893
1978
|
if (task.toolCalls !== undefined) {
|
|
1894
1979
|
progress.push(`${task.toolCalls} call${task.toolCalls === 1 ? "" : "s"}`);
|
|
1895
1980
|
}
|
|
1981
|
+
const usage = formatTaskUsageSummary(task.usage);
|
|
1982
|
+
if (usage)
|
|
1983
|
+
progress.push(usage);
|
|
1896
1984
|
const duration = taskDuration(taskDetails(task));
|
|
1897
1985
|
if (duration)
|
|
1898
1986
|
progress.push(duration);
|
|
@@ -2154,6 +2242,15 @@ function permissionName(tool) {
|
|
|
2154
2242
|
return "list";
|
|
2155
2243
|
return tool;
|
|
2156
2244
|
}
|
|
2245
|
+
function permittedToolNames(tools, rules) {
|
|
2246
|
+
return tools.filter((tool) => !permissionAlwaysDenied(rules, permissionName(tool)));
|
|
2247
|
+
}
|
|
2248
|
+
function enforcePermittedTools(pi, rules) {
|
|
2249
|
+
const active = pi.getActiveTools();
|
|
2250
|
+
const permitted = permittedToolNames(active, rules);
|
|
2251
|
+
if (permitted.join("\x00") !== active.join("\x00"))
|
|
2252
|
+
pi.setActiveTools(permitted);
|
|
2253
|
+
}
|
|
2157
2254
|
function canonicalPermissionPath(path, seen = new Set) {
|
|
2158
2255
|
const missing = [];
|
|
2159
2256
|
let existing = path;
|
|
@@ -2235,14 +2332,20 @@ function parseControlResponse(value) {
|
|
|
2235
2332
|
if (response.value !== undefined && typeof response.value !== "string") {
|
|
2236
2333
|
throw new Error("Invalid supervisor response value");
|
|
2237
2334
|
}
|
|
2238
|
-
if (response.task !== undefined
|
|
2239
|
-
|
|
2335
|
+
if (response.task !== undefined) {
|
|
2336
|
+
if (!isRecord2(response.task) || typeof response.task.taskId !== "string" || typeof response.task.state !== "string" || !TASK_STATES.has(response.task.state) || typeof response.task.agent !== "string" || response.task.usage !== undefined && !isTaskUsage(response.task.usage)) {
|
|
2337
|
+
throw new Error("Invalid supervisor task response");
|
|
2338
|
+
}
|
|
2240
2339
|
}
|
|
2241
2340
|
return response;
|
|
2242
2341
|
}
|
|
2243
2342
|
function registerSubagentWorker(pi, config) {
|
|
2244
2343
|
pi.on("session_start", () => {
|
|
2245
2344
|
delete process.env[WORKER_ENV];
|
|
2345
|
+
enforcePermittedTools(pi, config.rules);
|
|
2346
|
+
});
|
|
2347
|
+
pi.on("before_agent_start", () => {
|
|
2348
|
+
enforcePermittedTools(pi, config.rules);
|
|
2246
2349
|
});
|
|
2247
2350
|
pi.on("tool_call", async (event, ctx) => {
|
|
2248
2351
|
if (event.toolName === "task")
|
|
@@ -2434,7 +2537,7 @@ ${this.primaryAgent.prompt}` };
|
|
|
2434
2537
|
let maxSubagentsSettings = loadMaxSubagentsSettings(ctx.cwd, projectTrusted);
|
|
2435
2538
|
const sandboxCallbacks = this.integration.sandboxCallbacks;
|
|
2436
2539
|
let sandboxSettings = sandboxCallbacks?.load(ctx.cwd, projectTrusted) ?? { global: true };
|
|
2437
|
-
let tab = requested ? "
|
|
2540
|
+
let tab = requested ? "logs" : "agents";
|
|
2438
2541
|
let selectedAgent = Math.max(0, agents.findIndex((agent) => agent.name === this.primaryAgent?.name));
|
|
2439
2542
|
let selectedTask = requested ? tasks.findIndex((task) => task.id === requested || task.id.startsWith(requested)) : 0;
|
|
2440
2543
|
if (selectedTask < 0) {
|
|
@@ -2446,8 +2549,7 @@ ${this.primaryAgent.prompt}` };
|
|
|
2446
2549
|
let saving = false;
|
|
2447
2550
|
let editing = false;
|
|
2448
2551
|
let confirmingDeleteAgent;
|
|
2449
|
-
let
|
|
2450
|
-
let follow = detail;
|
|
2552
|
+
let follow = requested.length > 0;
|
|
2451
2553
|
let scroll = 0;
|
|
2452
2554
|
const reloadAgents = () => {
|
|
2453
2555
|
const selectedName = agents[selectedAgent]?.name;
|
|
@@ -2500,7 +2602,7 @@ ${this.primaryAgent.prompt}` };
|
|
|
2500
2602
|
return `${clipped}${" ".repeat(Math.max(0, cellWidth - visibleWidth2(clipped)))}`;
|
|
2501
2603
|
};
|
|
2502
2604
|
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"}`)}`;
|
|
2605
|
+
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
2606
|
if (tab === "agents") {
|
|
2505
2607
|
const start = Math.max(0, Math.min(selectedAgent - 3, agents.length - 7));
|
|
2506
2608
|
const shown2 = agents.slice(start, start + 7);
|
|
@@ -2580,13 +2682,19 @@ ${this.primaryAgent.prompt}` };
|
|
|
2580
2682
|
return box(lines2);
|
|
2581
2683
|
}
|
|
2582
2684
|
if (tab === "settings") {
|
|
2583
|
-
const
|
|
2584
|
-
const
|
|
2685
|
+
const maxSubagentsInherited = scope === "project" && maxSubagentsSettings.project === undefined;
|
|
2686
|
+
const sandboxInherited = scope === "project" && sandboxSettings.project === undefined;
|
|
2585
2687
|
const rows = [
|
|
2586
|
-
{
|
|
2688
|
+
{
|
|
2689
|
+
label: "Maximum subagents",
|
|
2690
|
+
value: String(selectedMaxSubagents()),
|
|
2691
|
+
inherited: maxSubagentsInherited,
|
|
2692
|
+
unavailable: false
|
|
2693
|
+
},
|
|
2587
2694
|
{
|
|
2588
2695
|
label: "Sandbox enabled",
|
|
2589
|
-
value:
|
|
2696
|
+
value: selectedSandboxEnabled() ? "on" : "off",
|
|
2697
|
+
inherited: sandboxInherited,
|
|
2590
2698
|
unavailable: sandboxCallbacks === undefined
|
|
2591
2699
|
}
|
|
2592
2700
|
];
|
|
@@ -2595,12 +2703,12 @@ ${this.primaryAgent.prompt}` };
|
|
|
2595
2703
|
const selected = index === selectedSetting;
|
|
2596
2704
|
const cursor = selected ? theme.fg("accent", "›") : " ";
|
|
2597
2705
|
const text = `[ ${row.value} ]`;
|
|
2598
|
-
const value = selected ? theme.fg("accent", text) : theme.fg(row.unavailable ? "muted" : "text", text);
|
|
2706
|
+
const value = row.inherited ? theme.fg("dim", text) : selected ? theme.fg("accent", text) : theme.fg(row.unavailable ? "muted" : "text", text);
|
|
2599
2707
|
const label = theme.fg(row.unavailable ? "muted" : "text", row.label);
|
|
2600
2708
|
const dirty = dirtySettings.has(index) ? theme.fg("warning", " *") : "";
|
|
2601
2709
|
lines2.push(`${cursor} ${value} ${label}${dirty}`);
|
|
2602
2710
|
}
|
|
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."));
|
|
2711
|
+
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
2712
|
if (saving)
|
|
2605
2713
|
lines2.push("", theme.fg("dim", "Saving…"));
|
|
2606
2714
|
return box(lines2);
|
|
@@ -2616,51 +2724,21 @@ ${this.primaryAgent.prompt}` };
|
|
|
2616
2724
|
];
|
|
2617
2725
|
return box(lines2);
|
|
2618
2726
|
}
|
|
2619
|
-
if (
|
|
2727
|
+
if (tab === "tasks") {
|
|
2620
2728
|
const listLines = [];
|
|
2621
2729
|
if (tasks.length === 0) {
|
|
2622
2730
|
listLines.push("No task sessions in this session.");
|
|
2623
2731
|
} else {
|
|
2624
2732
|
const start = Math.max(0, Math.min(selectedTask - 5, tasks.length - 11));
|
|
2625
2733
|
const shown2 = tasks.slice(start, start + 11);
|
|
2626
|
-
for (const [offset,
|
|
2734
|
+
for (const [offset, task2] of shown2.entries()) {
|
|
2627
2735
|
const index = start + offset;
|
|
2628
2736
|
const cursor = index === selectedTask ? theme.fg("accent", "›") : " ";
|
|
2629
|
-
const indent = " ".repeat(Math.max(0,
|
|
2630
|
-
listLines.push(`${cursor} ${indent}${taskState(theme,
|
|
2737
|
+
const indent = " ".repeat(Math.max(0, task2.depth - 1));
|
|
2738
|
+
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
2739
|
}
|
|
2632
2740
|
}
|
|
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]);
|
|
2741
|
+
return box([tabs, "", ...listLines]);
|
|
2664
2742
|
}
|
|
2665
2743
|
const task = tasks[selectedTask];
|
|
2666
2744
|
if (!task)
|
|
@@ -2675,11 +2753,13 @@ ${this.primaryAgent.prompt}` };
|
|
|
2675
2753
|
task.toolCalls === undefined ? undefined : `${task.toolCalls} tool call${task.toolCalls === 1 ? "" : "s"}`,
|
|
2676
2754
|
duration
|
|
2677
2755
|
].filter(Boolean);
|
|
2756
|
+
const usage = formatTaskUsage(task.usage);
|
|
2678
2757
|
const lines = [
|
|
2679
2758
|
tabs,
|
|
2680
2759
|
"",
|
|
2681
2760
|
`${theme.fg("accent", theme.bold(`@${task.agent}`))} ${theme.fg("text", task.description)}`,
|
|
2682
2761
|
`${taskState(theme, task)} ${theme.fg("dim", metrics.join(" · "))} ${theme.fg("dim", "·")} ${theme.fg(follow ? "accent" : "muted", `Follow: ${follow ? "on" : "off"}`)}`,
|
|
2762
|
+
...usage ? [theme.fg("dim", usage)] : [],
|
|
2683
2763
|
"",
|
|
2684
2764
|
...shown.map((line) => theme.fg("toolOutput", line))
|
|
2685
2765
|
];
|
|
@@ -2742,10 +2822,12 @@ ${this.primaryAgent.prompt}` };
|
|
|
2742
2822
|
ctx.ui.notify("Save changes with Enter or discard them with R before leaving", "warning");
|
|
2743
2823
|
return;
|
|
2744
2824
|
}
|
|
2745
|
-
const tabs = ["agents", "tasks", "settings", "help"];
|
|
2825
|
+
const tabs = ["agents", "tasks", "logs", "settings", "help"];
|
|
2746
2826
|
tab = tabs[(tabs.indexOf(tab) + 1) % tabs.length] ?? "agents";
|
|
2747
|
-
|
|
2748
|
-
|
|
2827
|
+
if (tab === "logs" && tasks.length > 0)
|
|
2828
|
+
follow = true;
|
|
2829
|
+
else
|
|
2830
|
+
follow = false;
|
|
2749
2831
|
editing = false;
|
|
2750
2832
|
scroll = 0;
|
|
2751
2833
|
tui.requestRender();
|
|
@@ -2916,7 +2998,7 @@ ${this.primaryAgent.prompt}` };
|
|
|
2916
2998
|
done();
|
|
2917
2999
|
return;
|
|
2918
3000
|
}
|
|
2919
|
-
if (
|
|
3001
|
+
if (tab === "tasks") {
|
|
2920
3002
|
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c"))
|
|
2921
3003
|
done();
|
|
2922
3004
|
else if (matchesKey(data, "up"))
|
|
@@ -2924,7 +3006,7 @@ ${this.primaryAgent.prompt}` };
|
|
|
2924
3006
|
else if (matchesKey(data, "down") && tasks.length > 0) {
|
|
2925
3007
|
selectedTask = Math.min(tasks.length - 1, selectedTask + 1);
|
|
2926
3008
|
} else if (matchesKey(data, "return") && tasks.length > 0) {
|
|
2927
|
-
|
|
3009
|
+
tab = "logs";
|
|
2928
3010
|
follow = true;
|
|
2929
3011
|
scroll = 0;
|
|
2930
3012
|
} else
|
|
@@ -2932,11 +3014,16 @@ ${this.primaryAgent.prompt}` };
|
|
|
2932
3014
|
tui.requestRender();
|
|
2933
3015
|
return;
|
|
2934
3016
|
}
|
|
3017
|
+
if (tab !== "logs")
|
|
3018
|
+
return;
|
|
2935
3019
|
const task = tasks[selectedTask];
|
|
2936
|
-
if (!task)
|
|
3020
|
+
if (!task) {
|
|
3021
|
+
if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c"))
|
|
3022
|
+
done();
|
|
2937
3023
|
return;
|
|
3024
|
+
}
|
|
2938
3025
|
if (matchesKey(data, "escape")) {
|
|
2939
|
-
|
|
3026
|
+
tab = "tasks";
|
|
2940
3027
|
follow = false;
|
|
2941
3028
|
scroll = 0;
|
|
2942
3029
|
} else if (matchesKey(data, "ctrl+c")) {
|
|
@@ -2961,7 +3048,7 @@ ${this.primaryAgent.prompt}` };
|
|
|
2961
3048
|
selectedTask = parentIndex;
|
|
2962
3049
|
follow = true;
|
|
2963
3050
|
} else {
|
|
2964
|
-
|
|
3051
|
+
tab = "tasks";
|
|
2965
3052
|
follow = false;
|
|
2966
3053
|
}
|
|
2967
3054
|
scroll = 0;
|
|
@@ -3079,6 +3166,9 @@ ${descriptions}` : ""),
|
|
|
3079
3166
|
const duration = taskDuration(details);
|
|
3080
3167
|
if (duration)
|
|
3081
3168
|
metrics.push(duration);
|
|
3169
|
+
const usage = formatTaskUsage(details.usage);
|
|
3170
|
+
if (usage)
|
|
3171
|
+
metrics.push(usage);
|
|
3082
3172
|
if (metrics.length > 0)
|
|
3083
3173
|
text += `
|
|
3084
3174
|
${theme.fg("dim", metrics.join(" · "))}`;
|
|
@@ -3327,6 +3417,15 @@ ${catalog.diagnostics.join(`
|
|
|
3327
3417
|
update(streamedText);
|
|
3328
3418
|
}
|
|
3329
3419
|
}
|
|
3420
|
+
if (event.type === "message_end") {
|
|
3421
|
+
const usage = taskUsageFromMessage(event.message);
|
|
3422
|
+
if (usage) {
|
|
3423
|
+
task.usage = addTaskUsage(task.usage, usage);
|
|
3424
|
+
this.persist(task);
|
|
3425
|
+
this.updateTaskWidget(ctx);
|
|
3426
|
+
update(streamedText || "Subagent running");
|
|
3427
|
+
}
|
|
3428
|
+
}
|
|
3330
3429
|
if (event.type === "tool_execution_start" && typeof event.toolName === "string") {
|
|
3331
3430
|
task.currentTool = event.toolName;
|
|
3332
3431
|
task.toolCalls = (task.toolCalls ?? 0) + 1;
|
|
@@ -3434,7 +3533,8 @@ ${catalog.diagnostics.join(`
|
|
|
3434
3533
|
ctx.ui.notify(`Agent ${agent.name} options are not supported by Pi RPC mode: ${providerOptionNames.join(", ")}`, "warning");
|
|
3435
3534
|
}
|
|
3436
3535
|
const taskEnabled = agent.permissions.some((rule) => rule.permission === "task" && rule.action !== "deny");
|
|
3437
|
-
const
|
|
3536
|
+
const activeTools = taskEnabled ? [...new Set([...this.pi.getActiveTools(), "task"])] : this.pi.getActiveTools().filter((tool) => tool !== "task");
|
|
3537
|
+
const tools = permittedToolNames(activeTools, rules);
|
|
3438
3538
|
const workerExtensions = this.integration.getWorkerExtensions?.() ?? [];
|
|
3439
3539
|
const args = [
|
|
3440
3540
|
...invocation.args,
|
|
@@ -3742,11 +3842,15 @@ ${renderTaskResult(task.id, "error", message)}`);
|
|
|
3742
3842
|
render: (width) => {
|
|
3743
3843
|
const header = theme.fg("accent", theme.bold("Subagents")) + theme.fg("dim", ` ${summary}`);
|
|
3744
3844
|
const tree = taskTreeLines(visible, (task) => {
|
|
3845
|
+
const state = taskState(theme, task);
|
|
3745
3846
|
const agent = theme.fg("accent", `@${task.agent}`);
|
|
3746
|
-
const
|
|
3747
|
-
const progress = taskProgress(
|
|
3847
|
+
const record = byId.get(task.id);
|
|
3848
|
+
const progress = taskProgress(record);
|
|
3748
3849
|
const suffix = progress.length > 0 ? theme.fg("dim", ` · ${progress.join(" · ")}`) : "";
|
|
3749
|
-
|
|
3850
|
+
const indentWidth = 3 + Math.max(0, record.depth) * 3;
|
|
3851
|
+
const descriptionWidth = Math.max(1, width - indentWidth - visibleWidth2(`${state} ${agent} ${suffix}`));
|
|
3852
|
+
const description = theme.fg("text", truncateToWidth2(task.description, descriptionWidth));
|
|
3853
|
+
return `${state} ${agent} ${description}${suffix}`;
|
|
3750
3854
|
});
|
|
3751
3855
|
const shown = tree.slice(0, 8);
|
|
3752
3856
|
if (tree.length > shown.length) {
|
|
@@ -3767,6 +3871,7 @@ ${renderTaskResult(task.id, "error", message)}`);
|
|
|
3767
3871
|
continue;
|
|
3768
3872
|
this.tasks.set(task.id, {
|
|
3769
3873
|
...task,
|
|
3874
|
+
usage: isTaskUsage(task.usage) ? { ...task.usage } : undefined,
|
|
3770
3875
|
state: task.state === "running" || task.state === "queued" ? "interrupted" : task.state
|
|
3771
3876
|
});
|
|
3772
3877
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-landstrip",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.10",
|
|
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.10",
|
|
43
43
|
"jsonc-parser": "^3.3.1",
|
|
44
44
|
"minimatch": "^10.2.5",
|
|
45
45
|
"yaml": "^2.9.0"
|