@trim21/personal-pi-extensions 0.0.161 → 0.0.165

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.
Files changed (2) hide show
  1. package/package.json +2 -1
  2. package/src/spawn-agent.ts +31 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trim21/personal-pi-extensions",
3
- "version": "0.0.161",
3
+ "version": "0.0.165",
4
4
  "type": "module",
5
5
  "description": "Custom pi coding-agent extensions: bwrap sandbox, workspace guard, opencode edit, and more",
6
6
  "keywords": [
@@ -62,6 +62,7 @@
62
62
  "src/opencode-write.ts",
63
63
  "src/bash-default-timeout.ts",
64
64
  "src/gh-readonly.ts",
65
+ "src/spawn-agent.ts",
65
66
  "src/todo-pendant.ts"
66
67
  ]
67
68
  },
@@ -10,6 +10,9 @@
10
10
  * is blocking: the tool awaits the subagent process until it exits and
11
11
  * returns its final output to the parent model. Progress is streamed through
12
12
  * `onUpdate`, the same channel the built-in bash tool uses for live output.
13
+ * Progress is a rolling log: `tool: <name>` lines for tool calls and
14
+ * `text: <content>` lines for completed text blocks, keeping the last
15
+ * `MAX_PROGRESS_LINES` lines.
13
16
  *
14
17
  * Security default: without an explicit `tools:` in the frontmatter, the
15
18
  * subagent only gets read-only tools (read/grep/find/ls) — no bash/write/edit.
@@ -40,6 +43,8 @@ import { type AgentConfig, discoverAgents, formatAgentList } from "./spawn-agent
40
43
  const MAX_OUTPUT_BYTES = 50 * 1024;
41
44
  /** Read-only toolset used when an agent does not declare `tools`. */
42
45
  const DEFAULT_TOOLS = ["read", "grep", "find", "ls"];
46
+ /** Progress log keeps only the most recent lines (rolling window). */
47
+ const MAX_PROGRESS_LINES = 5;
43
48
 
44
49
  // ── schema ───────────────────────────────────────────────────────────────────
45
50
 
@@ -205,9 +210,22 @@ export async function runAgent(
205
210
  env: { ...process.env, PI_SUBAGENT_CHILD: "1" },
206
211
  });
207
212
 
213
+ let logLines: string[] = [];
214
+
215
+ const pushLogLine = (line: string) => {
216
+ logLines.push(line);
217
+ if (logLines.length > MAX_PROGRESS_LINES) {
218
+ logLines = logLines.slice(-MAX_PROGRESS_LINES);
219
+ }
220
+ };
221
+
208
222
  const emitUpdate = () => {
223
+ // Usage line rides on the last row so the TUI always shows live token
224
+ // cost; it lives outside the rolling window so it is never trimmed.
225
+ const usageLine = formatUsageStats(result.usage, result.model);
226
+ const lines = usageLine ? [...logLines, usageLine] : logLines;
209
227
  onUpdate?.({
210
- content: [{ type: "text", text: getFinalOutput(result.messages) || "(running...)" }],
228
+ content: [{ type: "text", text: lines.join("\n") || "(running...)" }],
211
229
  details: { ...result },
212
230
  });
213
231
  };
@@ -224,7 +242,18 @@ export async function runAgent(
224
242
  }
225
243
  if (!isRecord(event)) return;
226
244
 
227
- if (event.type === "message_end" && isRecord(event.message)) {
245
+ if (event.type === "message_update" && isRecord(event.assistantMessageEvent)) {
246
+ // A completed text block (text_end carries the full content) becomes a
247
+ // `text:` log line. Deltas/thinking are intentionally not logged.
248
+ const delta = event.assistantMessageEvent;
249
+ if (delta.type === "text_end" && typeof delta.content === "string") {
250
+ pushLogLine(`text: ${delta.content}`);
251
+ emitUpdate();
252
+ }
253
+ } else if (event.type === "tool_execution_start" && typeof event.toolName === "string") {
254
+ pushLogLine(`tool: ${event.toolName}`);
255
+ emitUpdate();
256
+ } else if (event.type === "message_end" && isRecord(event.message)) {
228
257
  const msg = event.message as unknown as Message;
229
258
  result.messages.push(msg);
230
259
  if (msg.role === "assistant") {
@@ -241,9 +270,6 @@ export async function runAgent(
241
270
  if (typeof msg.errorMessage === "string") result.errorMessage = msg.errorMessage;
242
271
  }
243
272
  emitUpdate();
244
- } else if (event.type === "tool_result_end" && isRecord(event.message)) {
245
- result.messages.push(event.message as unknown as Message);
246
- emitUpdate();
247
273
  }
248
274
  };
249
275