@xynogen/pix-subagent 0.1.1 → 0.1.3

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/README.md CHANGED
@@ -8,7 +8,7 @@ Pi extension — planner-driven sub-agents with 3 tools, live widget (model alwa
8
8
  pi install npm:@xynogen/pix-subagent
9
9
  ```
10
10
 
11
- > Also included in [`@xynogen/pix-core`](https://github.com/xynogen/pix-mono/tree/main/packages/pix-core):
11
+ > Also included in [`@xynogen/pix-core`](https://www.npmjs.com/package/@xynogen/pix-core):
12
12
  >
13
13
  > ```bash
14
14
  > pi install npm:@xynogen/pix-core
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xynogen/pix-subagent",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Pi tool — planner-driven sub-agents: spawn, fetch, steer scoped child agents",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
package/src/index.ts CHANGED
@@ -86,7 +86,6 @@ export default function registerPixSubagent(pi: ExtensionAPI): void {
86
86
  // onComplete — fire subagent-notification nudge for each finished bg agent
87
87
  (record) => {
88
88
  agentActivity.delete(record.id);
89
- widget.markFinished(record.id);
90
89
 
91
90
  if (record.resultConsumed) {
92
91
  widget.update();
@@ -191,9 +190,9 @@ export default function registerPixSubagent(pi: ExtensionAPI): void {
191
190
  }
192
191
  });
193
192
 
194
- pi.on("tool_execution_start", (_event, ctx) => {
195
- widget.setUICtx(ctx.ui as Parameters<typeof widget.setUICtx>[0]);
193
+ pi.on("turn_start", (_event, ctx) => {
196
194
  widget.onTurnStart();
195
+ widget.setUICtx(ctx.ui as Parameters<typeof widget.setUICtx>[0]);
197
196
  widget.ensureTimer();
198
197
  });
199
198
 
package/src/tools.ts CHANGED
@@ -180,10 +180,10 @@ If the target is already known, use a direct tool — \`read\` for a known path,
180
180
 
181
181
  ## Usage notes
182
182
  - Always include a short (3-5 word) description summarizing what the agent will do (shown in UI).
183
- - When you launch multiple agents for independent work, send them in a single message with multiple tool uses, with run_in_background: true on each, so they run concurrently.
183
+ - When you launch multiple agents for independent work, send them in a single message with multiple tool uses, so they run concurrently.
184
184
  - When the agent is done, it returns a single message. The result is not visible to the user — to show the user, send a text message with a concise summary.
185
185
  - Trust but verify: an agent's summary describes what it intended to do, not what it did. When an agent writes or edits code, check the actual changes before reporting work as done.
186
- - Use run_in_background for work you don't need immediately. You will be notified when it completes do NOT poll or sleep waiting for it.
186
+ - Use run_in_background: true only when you explicitly do not need to see the output inline (e.g. fire-and-forget background tasks). Default is foregroundthe agent streams inline and you see its work as it runs.
187
187
  - Use resume with an agent ID to continue a previous agent's work.
188
188
  - Use agent_steer to send mid-run messages to a running background agent.
189
189
  - Use model to specify a model from the available models list above (provider/id or fuzzy e.g. "haiku").
@@ -296,12 +296,10 @@ export function createAgentTool(
296
296
 
297
297
  const stats = buildStats(details, theme);
298
298
 
299
- // Streaming / running
299
+ // Streaming / running — live state shown by the ● Agents widget, so the
300
+ // inline transcript stays empty to avoid stacking one card per agent.
300
301
  if (isPartial || details.status === "running") {
301
- const frame = SPINNER[details.spinnerFrame ?? 0];
302
- let line = theme.fg("accent", frame) + (stats ? ` ${stats}` : "");
303
- line += `\n${theme.fg("dim", ` ⎿ ${details.activity ?? "thinking…"}`)}`;
304
- return new Text(line, 0, 0);
302
+ return new Text("", 0, 0);
305
303
  }
306
304
 
307
305
  // Background launched
package/src/ui/widget.ts CHANGED
@@ -133,8 +133,8 @@ export class AgentWidget {
133
133
  private uiCtx: UICtx | undefined;
134
134
  private widgetFrame = 0;
135
135
  private widgetInterval: ReturnType<typeof setInterval> | undefined;
136
- private finishedTurnAge = new Map<string, number>();
137
- private static readonly ERROR_LINGER_TURNS = 2;
136
+ private static readonly FINISHED_LINGER_MS = 5_000;
137
+ private static readonly ERROR_LINGER_MS = 15_000;
138
138
  private widgetRegistered = false;
139
139
  private tui: unknown = undefined;
140
140
  private lastStatusText: string | undefined;
@@ -154,9 +154,6 @@ export class AgentWidget {
154
154
  }
155
155
 
156
156
  onTurnStart() {
157
- for (const [id, age] of this.finishedTurnAge) {
158
- this.finishedTurnAge.set(id, age + 1);
159
- }
160
157
  this.update();
161
158
  }
162
159
 
@@ -166,18 +163,14 @@ export class AgentWidget {
166
163
  }
167
164
  }
168
165
 
169
- private shouldShowFinished(agentId: string, status: string): boolean {
170
- const age = this.finishedTurnAge.get(agentId) ?? 0;
171
- const maxAge = ERROR_STATUSES.has(status)
172
- ? AgentWidget.ERROR_LINGER_TURNS
173
- : 1;
174
- return age < maxAge;
175
- }
176
-
177
- markFinished(agentId: string) {
178
- if (!this.finishedTurnAge.has(agentId)) {
179
- this.finishedTurnAge.set(agentId, 0);
180
- }
166
+ private shouldShowFinished(status: string, completedAt: number): boolean {
167
+ // Linger a few seconds after finish, then drop. The ✓ … Done line in the
168
+ // transcript is the permanent record; errors stay longer so failures are
169
+ // noticed. The 80ms widget timer re-evaluates this continuously.
170
+ const linger = ERROR_STATUSES.has(status)
171
+ ? AgentWidget.ERROR_LINGER_MS
172
+ : AgentWidget.FINISHED_LINGER_MS;
173
+ return Date.now() - completedAt < linger;
181
174
  }
182
175
 
183
176
  private renderFinishedLine(
@@ -246,8 +239,8 @@ export class AgentWidget {
246
239
  (a) =>
247
240
  a.status !== "running" &&
248
241
  a.status !== "queued" &&
249
- a.completedAt &&
250
- this.shouldShowFinished(a.id, a.status),
242
+ a.completedAt != null &&
243
+ this.shouldShowFinished(a.status, a.completedAt),
251
244
  );
252
245
 
253
246
  if (running.length === 0 && queued.length === 0 && finished.length === 0)
@@ -403,7 +396,10 @@ export class AgentWidget {
403
396
  for (const a of allAgents) {
404
397
  if (a.status === "running") runningCount++;
405
398
  else if (a.status === "queued") queuedCount++;
406
- else if (a.completedAt && this.shouldShowFinished(a.id, a.status))
399
+ else if (
400
+ a.completedAt != null &&
401
+ this.shouldShowFinished(a.status, a.completedAt)
402
+ )
407
403
  hasFinished = true;
408
404
  }
409
405
  const hasActive = runningCount > 0 || queuedCount > 0;
@@ -422,10 +418,6 @@ export class AgentWidget {
422
418
  clearInterval(this.widgetInterval);
423
419
  this.widgetInterval = undefined;
424
420
  }
425
- for (const [id] of this.finishedTurnAge) {
426
- if (!allAgents.some((a) => a.id === id))
427
- this.finishedTurnAge.delete(id);
428
- }
429
421
  return;
430
422
  }
431
423