@arhen/pi-core-subagent 1.3.53 → 1.3.55
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/package.json +1 -1
- package/src/format.ts +23 -13
- package/src/index.ts +1 -0
- package/src/manager.ts +12 -3
- package/src/types.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arhen/pi-core-subagent",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.55",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "pi extension: fast in-process subagents with a dependency-graph scheduler (needs edges gate tasks and carry upstream output into dependent prompts), plus background runs, intercom and agent-to-agent mailbox. Leader defines agents inline.",
|
|
6
6
|
"license": "MIT",
|
package/src/format.ts
CHANGED
|
@@ -60,8 +60,12 @@ function taskStatsWithUsage(task: TaskSnapshot): string {
|
|
|
60
60
|
const usage = formatUsage(task.usage);
|
|
61
61
|
return `${stats}${usage ? ` · ${usage}` : ""}`;
|
|
62
62
|
}
|
|
63
|
+
export function modelTag(task: TaskSnapshot): string {
|
|
64
|
+
const ref = [task.provider, task.model, task.thinking].filter(Boolean).join("/");
|
|
65
|
+
return ref ? ` [${ref}]` : "";
|
|
66
|
+
}
|
|
63
67
|
export function taskLine(task: TaskSnapshot): string {
|
|
64
|
-
return `${statusIcon(task.status)} ${task.agent} · ${taskStatsWithUsage(task)} · ${taskTimer(task)}`;
|
|
68
|
+
return `${statusIcon(task.status)} ${task.agent}${modelTag(task)} · ${taskStatsWithUsage(task)} · ${taskTimer(task)}`;
|
|
65
69
|
}
|
|
66
70
|
export function colorNums(text: string, theme: Theme): string {
|
|
67
71
|
return text.replace(/((?:\d+(?:\.\d+)?[a-zA-Z]*)+)|([^\d]+)/g, (_m, num?: string, rest?: string) =>
|
|
@@ -71,15 +75,16 @@ export function colorNums(text: string, theme: Theme): string {
|
|
|
71
75
|
function themedTaskLine(task: TaskSnapshot, theme: Theme, activity = ""): string {
|
|
72
76
|
const tail = `${taskStatsWithUsage(task)} · ${taskTimer(task)}`;
|
|
73
77
|
|
|
78
|
+
const tag = theme.fg("dim", modelTag(task));
|
|
74
79
|
const gate =
|
|
75
80
|
task.status === "queued" && task.needs?.length ? `${theme.fg("muted", `↳ waits ${task.needs.join(", ")}`)} · ` : "";
|
|
76
81
|
if (TERMINAL.includes(task.status)) {
|
|
77
|
-
return theme.fg("dim", `${statusIcon(task.status)} ${task.agent} · ${tail}`);
|
|
82
|
+
return theme.fg("dim", `${statusIcon(task.status)} ${task.agent}${tag} · ${tail}`);
|
|
78
83
|
}
|
|
79
84
|
|
|
80
85
|
pulsePhase += 1;
|
|
81
86
|
const name = isTalking(task) ? theme.fg(pulsePhase % 2 === 0 ? "accent" : "dim", `${task.agent} ⇄`) : task.agent;
|
|
82
|
-
return `${statusIcon(task.status)} ${name} · ${gate}${activity}${colorNums(tail, theme)}`;
|
|
87
|
+
return `${statusIcon(task.status)} ${name}${tag} · ${gate}${activity}${colorNums(tail, theme)}`;
|
|
83
88
|
}
|
|
84
89
|
const ARG_KEYS = ["pattern", "query", "command", "path", "file_path", "filePath", "url", "name", "subject", "task"];
|
|
85
90
|
export function describeCall(toolName: string, args: unknown, cwd?: string): string {
|
|
@@ -114,7 +119,8 @@ export function compactLines(run: RunSnapshot): string[] {
|
|
|
114
119
|
if (run.tasks.length > MAX_TASKS) lines.push(`… +${run.tasks.length - MAX_TASKS} more`);
|
|
115
120
|
return lines;
|
|
116
121
|
}
|
|
117
|
-
|
|
122
|
+
// header + 4 task rows; live tasks are ordered first, so the cap only ever hides finished work
|
|
123
|
+
const WIDGET_MAX_LINES = 5;
|
|
118
124
|
|
|
119
125
|
export class SubagentsWidget implements Component {
|
|
120
126
|
constructor(
|
|
@@ -139,17 +145,21 @@ export class SubagentsWidget implements Component {
|
|
|
139
145
|
),
|
|
140
146
|
];
|
|
141
147
|
const budget = WIDGET_MAX_LINES - 1;
|
|
148
|
+
// live tasks first so the budget never hides work in progress
|
|
149
|
+
const all = runs.flatMap((run) => run.tasks);
|
|
150
|
+
const ordered = [
|
|
151
|
+
...all.filter((t) => !TERMINAL.includes(t.status)),
|
|
152
|
+
...all.filter((t) => TERMINAL.includes(t.status)),
|
|
153
|
+
];
|
|
142
154
|
let shown = 0;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const activity = task.lastActivity ? `${this.theme.fg("dim", `→ ${task.lastActivity}`)} · ` : "";
|
|
155
|
+
for (const task of ordered) {
|
|
156
|
+
if (shown >= budget) break;
|
|
157
|
+
shown += 1;
|
|
158
|
+
const activity = task.lastActivity ? `${this.theme.fg("dim", `→ ${task.lastActivity}`)} · ` : "";
|
|
148
159
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
}
|
|
160
|
+
lines.push(
|
|
161
|
+
truncateToWidth(`${this.theme.fg("dim", "├─")} ${themedTaskLine(task, this.theme, activity)}`, width, "…"),
|
|
162
|
+
);
|
|
153
163
|
}
|
|
154
164
|
const hidden = total - shown;
|
|
155
165
|
if (hidden > 0) {
|
package/src/index.ts
CHANGED
|
@@ -140,6 +140,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
140
140
|
"Define each agent inline: invented name, focused system prompt, read-only by default (write:true to edit). A matched agent file takes over (see description); matching is by description, not name — name the agent whatever fits the goal.",
|
|
141
141
|
"Right after spawning, call subagent_status(runId) ONCE before any other work — a child that died on spawn (or never started) is invisible until far later otherwise. If it shows a task failed/never started, fix or respawn immediately.",
|
|
142
142
|
"Never block with nothing to do: if you have no work left after spawning, end your turn — completion notifies you and wakes a fresh turn with the results. await_subagent/autoAwait while idle only burns time and tokens.",
|
|
143
|
+
"A failed task interrupts you immediately as a steering message — handle it in the same turn (resume, swap model, re-dispatch) instead of finishing the plan on a broken intermediate result. Completes and aborts queue as follow-ups.",
|
|
143
144
|
"autoAwait:true only when this SAME turn must consume the result immediately. await_subagent is for syncing with your own parallel work — not the default follow-up to a spawn.",
|
|
144
145
|
"A task that failed mid-work (provider error, rate limit, timeout) keeps its session file and branch: resume_subagent(runId, taskId, model?) revives it with full context — prefer that over respawning. Respawn only when it never started (no session file).",
|
|
145
146
|
],
|
package/src/manager.ts
CHANGED
|
@@ -437,7 +437,9 @@ export class SubagentManager {
|
|
|
437
437
|
}
|
|
438
438
|
|
|
439
439
|
private deliverMode(kind: string, task: TaskSnapshot): "steer" | "followUp" {
|
|
440
|
-
|
|
440
|
+
// failures interrupt the leader's turn: it must decide immediately (resume, swap model, respawn)
|
|
441
|
+
// instead of finding out after its own turn ended. Aborts are user-initiated, so they queue.
|
|
442
|
+
return kind === "failed" || isStartupFailure(task, kind) ? "steer" : "followUp";
|
|
441
443
|
}
|
|
442
444
|
|
|
443
445
|
private notifyTask(run: RunSnapshot, task: TaskSnapshot, kind: "completed" | "failed" | "aborted"): void {
|
|
@@ -470,7 +472,7 @@ export class SubagentManager {
|
|
|
470
472
|
? `A subagent is asking you a question (task ${extra?.taskId}): ${extra?.question ?? ""}\nReply with reply_subagent(runId: "${run.id}", taskId: "${extra?.taskId}", message: ...).`
|
|
471
473
|
: makeNotice(run, kind);
|
|
472
474
|
try {
|
|
473
|
-
this.pi.sendUserMessage(body, { deliverAs: "followUp" });
|
|
475
|
+
this.pi.sendUserMessage(body, { deliverAs: kind === "failed" ? "steer" : "followUp" });
|
|
474
476
|
} catch {}
|
|
475
477
|
this.emit("subagent:notification", { runId: run.id, kind, body });
|
|
476
478
|
}
|
|
@@ -825,6 +827,7 @@ export class SubagentManager {
|
|
|
825
827
|
task: input.task,
|
|
826
828
|
|
|
827
829
|
model: model?.id ?? input.model,
|
|
830
|
+
provider: model?.provider,
|
|
828
831
|
thinking,
|
|
829
832
|
tools,
|
|
830
833
|
},
|
|
@@ -878,7 +881,13 @@ export class SubagentManager {
|
|
|
878
881
|
this.updateTask(
|
|
879
882
|
run,
|
|
880
883
|
task,
|
|
881
|
-
{
|
|
884
|
+
{
|
|
885
|
+
status: "running",
|
|
886
|
+
sessionId: child.sessionId,
|
|
887
|
+
sessionFile: child.sessionFile,
|
|
888
|
+
provider: child.model?.provider ?? task.provider,
|
|
889
|
+
thinking: child.thinkingLevel,
|
|
890
|
+
},
|
|
882
891
|
ctx,
|
|
883
892
|
onUpdate,
|
|
884
893
|
);
|