@fitchmultz/pi-subagents 0.50.0 → 0.51.0

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 (169) hide show
  1. package/CHANGELOG.md +51 -2
  2. package/README.md +36 -5
  3. package/dist/agent/contracts.js +6 -24
  4. package/dist/agent/host.js +3 -16
  5. package/dist/agent/index.js +207 -173
  6. package/dist/agent/journal-tail.js +115 -156
  7. package/dist/agent/launch.js +20 -8
  8. package/dist/agent/legacy.js +65 -23
  9. package/dist/agent/notifications.js +138 -47
  10. package/dist/agent/observation-state.js +246 -0
  11. package/dist/agent/observation-view.js +258 -0
  12. package/dist/agent/observation.js +346 -0
  13. package/dist/agent/observe-card.js +243 -0
  14. package/dist/agent/observe-chrome.js +85 -0
  15. package/dist/agent/observe-client.js +285 -0
  16. package/dist/agent/observe-layout.js +65 -0
  17. package/dist/agent/observe-reading.js +84 -0
  18. package/dist/agent/observe-screen.js +400 -96
  19. package/dist/agent/observe-transcript.js +382 -0
  20. package/dist/agent/observe.js +250 -43
  21. package/dist/agent/process.js +198 -25
  22. package/dist/agent/receipts.js +92 -20
  23. package/dist/agent/runner.js +125 -20
  24. package/dist/agent/shell.js +2 -1
  25. package/dist/agent/storage.js +29 -12
  26. package/dist/agent/tool-schemas.js +20 -0
  27. package/dist/agent/typebox.js +10 -5
  28. package/dist/agent/usage.js +0 -115
  29. package/dist/agent/view.js +46 -16
  30. package/dist/agent/wake.js +99 -0
  31. package/dist/build-info.js +1 -1
  32. package/dist/intercom-client.d.ts +26 -0
  33. package/dist/intercom-client.js +45 -0
  34. package/dist/pi-intercom/agent-dir.d.ts +1 -0
  35. package/dist/pi-intercom/bridge-session.js +2 -1
  36. package/dist/pi-intercom/broker/broker.js +27 -21
  37. package/dist/pi-intercom/broker/client-last.d.ts +13 -0
  38. package/dist/pi-intercom/broker/client-last.js +17 -5
  39. package/dist/pi-intercom/broker/client.d.ts +60 -0
  40. package/dist/pi-intercom/broker/client.js +47 -16
  41. package/dist/pi-intercom/broker/connection.d.ts +29 -0
  42. package/dist/pi-intercom/broker/framing.d.ts +17 -0
  43. package/dist/pi-intercom/broker/framing.js +10 -7
  44. package/dist/pi-intercom/broker/guard.js +23 -0
  45. package/dist/pi-intercom/broker/last.d.ts +26 -0
  46. package/dist/pi-intercom/broker/last.js +57 -32
  47. package/dist/pi-intercom/broker/paths.d.ts +13 -0
  48. package/dist/pi-intercom/broker/paths.js +35 -12
  49. package/dist/pi-intercom/broker/pid.js +5 -1
  50. package/dist/pi-intercom/broker/server-connection.js +19 -0
  51. package/dist/pi-intercom/broker/snapshot-reader.d.ts +9 -0
  52. package/dist/pi-intercom/broker/snapshots.d.ts +29 -0
  53. package/dist/pi-intercom/broker/targets.d.ts +9 -0
  54. package/dist/pi-intercom/connection.js +10 -2
  55. package/dist/pi-intercom/identity.js +6 -5
  56. package/dist/pi-intercom/inbound-delivery.js +16 -60
  57. package/dist/pi-intercom/inbound-journal.js +83 -6
  58. package/dist/pi-intercom/inbound-record.js +18 -1
  59. package/dist/pi-intercom/inbound.js +12 -38
  60. package/dist/pi-intercom/index.js +3 -0
  61. package/dist/pi-intercom/last-message.js +4 -16
  62. package/dist/pi-intercom/lifecycle.js +88 -3
  63. package/dist/pi-intercom/outbound.js +5 -15
  64. package/dist/pi-intercom/reply-tracker.js +7 -6
  65. package/dist/pi-intercom/reply-wait.js +2 -2
  66. package/dist/pi-intercom/session-targets.d.ts +24 -0
  67. package/dist/pi-intercom/tool-registration.js +4 -1
  68. package/dist/pi-intercom/tool-results.js +5 -7
  69. package/dist/pi-intercom/tool-schemas.js +12 -26
  70. package/dist/pi-intercom/types.d.ts +212 -0
  71. package/dist/pi-intercom/types.js +54 -3
  72. package/dist/pi-intercom/validation.d.ts +12 -0
  73. package/dist/pi-intercom/validation.js +3 -0
  74. package/dist/shared/bounded-json.js +151 -0
  75. package/dist/shared/compact-cards.js +66 -0
  76. package/dist/shared/journal-frames.js +3 -6
  77. package/dist/shared/json-projection.js +267 -129
  78. package/dist/shared/native-journal.js +123 -56
  79. package/dist/shared/post-exit-stdio-guard.js +120 -7
  80. package/dist/shared/tui-action.js +29 -0
  81. package/dist/shared/unknown.d.ts +9 -0
  82. package/docs/code-quality.md +2 -0
  83. package/docs/development.md +40 -1
  84. package/docs/intercom-bridge.md +9 -4
  85. package/docs/intercom.md +226 -6
  86. package/docs/performance.md +87 -11
  87. package/docs/reference.md +157 -20
  88. package/docs/retired-history-maintenance.md +252 -0
  89. package/package.json +28 -15
  90. package/scripts/archive-retired-history.mjs +464 -0
  91. package/scripts/build.mjs +48 -28
  92. package/scripts/measure-size.mjs +1 -1
  93. package/scripts/native-package-smoke.mjs +153 -12
  94. package/scripts/package-smoke.mjs +80 -11
  95. package/scripts/prepare.mjs +2 -2
  96. package/scripts/real-pi-live.mjs +377 -0
  97. package/scripts/real-pi-processes.mjs +6 -1
  98. package/scripts/real-pi-smoke.mjs +6 -282
  99. package/scripts/run-tests.mjs +4 -2
  100. package/scripts/typescript.mjs +35 -0
  101. package/skills/pi-intercom/SKILL.md +2 -1
  102. package/skills/pi-subagents/SKILL.md +23 -5
  103. package/src/agent/contracts.ts +8 -34
  104. package/src/agent/host.ts +19 -20
  105. package/src/agent/index.ts +224 -190
  106. package/src/agent/journal-tail.ts +141 -153
  107. package/src/agent/launch.ts +33 -9
  108. package/src/agent/legacy.ts +85 -31
  109. package/src/agent/notifications.ts +192 -67
  110. package/src/agent/observation-state.ts +277 -0
  111. package/src/agent/observation-view.ts +267 -0
  112. package/src/agent/observation.ts +405 -0
  113. package/src/agent/observe-card.ts +304 -0
  114. package/src/agent/observe-chrome.ts +170 -0
  115. package/src/agent/observe-client.ts +318 -0
  116. package/src/agent/observe-layout.ts +79 -0
  117. package/src/agent/observe-reading.ts +97 -0
  118. package/src/agent/observe-screen.ts +443 -129
  119. package/src/agent/observe-transcript.ts +434 -0
  120. package/src/agent/observe.ts +304 -71
  121. package/src/agent/process.ts +240 -34
  122. package/src/agent/receipts.ts +114 -24
  123. package/src/agent/runner.ts +125 -27
  124. package/src/agent/shell.ts +2 -1
  125. package/src/agent/storage.ts +27 -11
  126. package/src/agent/tool-schemas.ts +33 -0
  127. package/src/agent/typebox.ts +12 -7
  128. package/src/agent/usage.ts +0 -130
  129. package/src/agent/view.ts +70 -20
  130. package/src/agent/wake.ts +107 -0
  131. package/src/intercom-client.ts +108 -0
  132. package/src/pi-intercom/bridge-session.ts +2 -2
  133. package/src/pi-intercom/broker/broker.ts +27 -23
  134. package/src/pi-intercom/broker/client-last.ts +25 -12
  135. package/src/pi-intercom/broker/client.ts +55 -33
  136. package/src/pi-intercom/broker/framing.ts +11 -8
  137. package/src/pi-intercom/broker/guard.ts +33 -0
  138. package/src/pi-intercom/broker/last.ts +86 -33
  139. package/src/pi-intercom/broker/paths.ts +39 -12
  140. package/src/pi-intercom/broker/pid.ts +6 -1
  141. package/src/pi-intercom/broker/server-connection.ts +25 -0
  142. package/src/pi-intercom/connection.ts +21 -10
  143. package/src/pi-intercom/identity.ts +8 -5
  144. package/src/pi-intercom/inbound-delivery.ts +20 -66
  145. package/src/pi-intercom/inbound-journal.ts +103 -7
  146. package/src/pi-intercom/inbound-record.ts +21 -4
  147. package/src/pi-intercom/inbound.ts +18 -49
  148. package/src/pi-intercom/index.ts +3 -0
  149. package/src/pi-intercom/last-message.ts +10 -19
  150. package/src/pi-intercom/lifecycle.ts +117 -14
  151. package/src/pi-intercom/outbound.ts +7 -16
  152. package/src/pi-intercom/reply-tracker.ts +8 -6
  153. package/src/pi-intercom/reply-wait.ts +3 -2
  154. package/src/pi-intercom/runtime-types.ts +0 -1
  155. package/src/pi-intercom/tool-registration.ts +4 -1
  156. package/src/pi-intercom/tool-results.ts +10 -9
  157. package/src/pi-intercom/tool-schemas.ts +12 -44
  158. package/src/pi-intercom/types.ts +115 -4
  159. package/src/pi-intercom/validation.ts +4 -0
  160. package/src/shared/bounded-json.ts +162 -0
  161. package/src/shared/compact-cards.ts +118 -0
  162. package/src/shared/journal-frames.ts +3 -19
  163. package/src/shared/json-projection.ts +267 -147
  164. package/src/shared/native-journal.ts +157 -75
  165. package/src/shared/post-exit-stdio-guard.ts +165 -7
  166. package/src/shared/tui-action.ts +39 -0
  167. package/tsconfig.client.json +8 -0
  168. package/tsconfig.json +1 -0
  169. package/src/shared/stream-json.d.ts +0 -10
@@ -1,11 +1,16 @@
1
1
  import * as path from "node:path";
2
+ import * as fs from "node:fs";
3
+ import { SettingsManager, } from "@earendil-works/pi-coding-agent";
4
+ import { Container, TruncatedText } from "@earendil-works/pi-tui";
5
+ import { actionText } from "../shared/tui-action.js";
2
6
  import { getPiAgentDir } from "../pi-intercom/agent-dir.js";
3
7
  import { isChild, errorText } from "./host.js";
4
8
  import { launchAt, list, ownerDirectory, runDirectory, read } from "./storage.js";
5
9
  import { successorSchema } from "./contracts.js";
6
10
  import { readCanonicalResult } from "./usage.js";
7
- import { actor, alive } from "./process.js";
8
- import { JournalTail, plainText } from "./journal-tail.js";
11
+ import { actor, observeAlive } from "./process.js";
12
+ import { plainText } from "./journal-tail.js";
13
+ import { ObservationClient } from "./observe-client.js";
9
14
  import { adoptedJournal } from "./saved.js";
10
15
  const STATUS_KEY = "agent-observer";
11
16
  class Observer {
@@ -13,9 +18,16 @@ class Observer {
13
18
  owner = "";
14
19
  base = "";
15
20
  timer;
21
+ scanning;
22
+ rescan = false;
16
23
  close;
17
24
  refresh;
18
25
  rows = new Map();
26
+ peers = new Map();
27
+ watcher;
28
+ widgetVisible = false;
29
+ widgetSignature = "";
30
+ widgetRender;
19
31
  launches = new Map();
20
32
  finished = new Set();
21
33
  pending = [];
@@ -27,10 +39,22 @@ class Observer {
27
39
  }
28
40
  dispose() {
29
41
  clearInterval(this.timer);
42
+ this.timer = undefined;
43
+ this.scanning = undefined;
44
+ this.rescan = false;
45
+ this.watcher?.close();
46
+ this.watcher = undefined;
47
+ for (const peer of this.peers.values()) {
48
+ peer.client.close();
49
+ }
50
+ this.peers.clear();
51
+ this.widgetVisible = false;
52
+ this.widgetSignature = "";
53
+ this.widgetRender = undefined;
30
54
  this.close?.();
31
55
  this.close = undefined;
32
56
  this.refresh = undefined;
33
- this.ctx?.ui.setStatus(STATUS_KEY, undefined);
57
+ this.ctx?.ui.setWidget(STATUS_KEY, undefined);
34
58
  this.ctx = undefined;
35
59
  this.rows.clear();
36
60
  this.launches.clear();
@@ -49,57 +73,75 @@ class Observer {
49
73
  this.ctx = ctx;
50
74
  this.owner = ctx.sessionManager.getSessionId();
51
75
  this.base = ownerDirectory(path.resolve(getPiAgentDir()), this.owner);
76
+ fs.mkdirSync(this.base, { recursive: true, mode: 0o700 });
77
+ try {
78
+ const watcher = fs.watch(this.base, { recursive: true }, (_event, file) => {
79
+ if (this.watcher === watcher &&
80
+ this.timer === undefined &&
81
+ (file === null || /(?:launch|result|successor|process)\.json$/.test(file))) {
82
+ this.scan();
83
+ }
84
+ });
85
+ this.watcher = watcher;
86
+ watcher.on("error", () => {
87
+ if (this.watcher !== watcher) {
88
+ return;
89
+ }
90
+ watcher.close();
91
+ this.watcher = undefined;
92
+ this.scan();
93
+ });
94
+ }
95
+ catch {
96
+ /* Sparse native execution events still wake inventory without an idle poll. */
97
+ }
52
98
  this.scan();
53
- this.timer = setInterval(() => this.scan(), 1000);
54
- this.timer.unref();
55
99
  }
56
100
  launch(id) {
57
101
  const launch = this.launches.get(id) ?? launchAt(this.base, id, this.owner);
58
102
  this.launches.set(id, launch);
59
103
  return launch;
60
104
  }
61
- live(launch, roots) {
62
- const cached = roots.get(launch.rootId);
63
- if (cached !== undefined) {
64
- return cached;
65
- }
66
- const identity = actor(this.base, launch);
67
- const live = identity !== undefined && alive(identity);
68
- roots.set(launch.rootId, live);
69
- return live;
70
- }
105
+ /** Reads durable metadata now; unfinished rows commit after their root's batched liveness. */
71
106
  load(id, roots) {
72
107
  if (this.finished.has(id)) {
73
- return;
108
+ return undefined;
74
109
  }
75
110
  const launch = this.launch(id);
76
111
  const result = readCanonicalResult(this.base, launch);
77
112
  const finished = result !== undefined || this.hasSuccessor(id);
78
- const live = !finished && this.live(launch, roots);
79
113
  if (finished) {
80
114
  this.finished.add(id);
81
115
  }
82
- this.rows.set(id, {
83
- id,
84
- name: plainText(launch.name ?? id, 120),
85
- status: result?.status ?? (live ? "running" : "interrupted"),
86
- live,
87
- createdAt: launch.createdAt,
88
- });
116
+ else if (!roots.has(launch.rootId)) {
117
+ roots.set(launch.rootId, actor(this.base, launch));
118
+ }
119
+ return (live) => {
120
+ const running = !finished && live.get(launch.rootId) === true;
121
+ this.rows.set(id, {
122
+ id,
123
+ name: plainText(launch.name ?? id, 120),
124
+ status: result?.status ?? (running ? "running" : "interrupted"),
125
+ live: running,
126
+ createdAt: launch.createdAt,
127
+ activity: this.peers.get(id)?.client.activity,
128
+ });
129
+ };
89
130
  }
90
131
  hasSuccessor(id) {
91
132
  return (read(path.join(runDirectory(this.base, id), "successor.json"), successorSchema) !== undefined);
92
133
  }
93
134
  loadVisible(id, roots) {
94
135
  try {
95
- this.load(id, roots);
136
+ return this.load(id, roots);
96
137
  }
97
138
  catch (error) {
98
139
  this.rows.set(id, { id, name: id, status: "unavailable", live: false, createdAt: 0 });
99
140
  this.error = plainText(errorText(error));
141
+ return undefined;
100
142
  }
101
143
  }
102
- scan() {
144
+ loadBatch(roots) {
103
145
  try {
104
146
  const ids = list(this.base);
105
147
  const present = new Set(ids);
@@ -118,14 +160,161 @@ class Observer {
118
160
  this.pending = ids.filter((id) => !this.finished.has(id));
119
161
  }
120
162
  this.error = "";
121
- const roots = new Map();
122
- this.pending.splice(0, 50).forEach((id) => this.loadVisible(id, roots));
163
+ return this.pending.splice(0, 50).flatMap((id) => this.loadVisible(id, roots) ?? []);
123
164
  }
124
165
  catch (error) {
125
166
  this.error = plainText(errorText(error));
167
+ return [];
168
+ }
169
+ }
170
+ /** One scan at a time; triggers during a scan coalesce into one follow-up. */
171
+ scan() {
172
+ if (this.scanning !== undefined) {
173
+ this.rescan = true;
174
+ return;
175
+ }
176
+ const scanning = this.update()
177
+ .catch((error) => {
178
+ this.error = plainText(errorText(error));
179
+ })
180
+ .finally(() => {
181
+ if (this.scanning !== scanning) {
182
+ return;
183
+ }
184
+ this.scanning = undefined;
185
+ if (this.rescan) {
186
+ this.rescan = false;
187
+ this.scan();
188
+ }
189
+ });
190
+ this.scanning = scanning;
191
+ }
192
+ async update() {
193
+ const ctx = this.ctx;
194
+ const roots = new Map();
195
+ const commits = this.loadBatch(roots);
196
+ const claims = [...roots].flatMap(([root, identity]) => (identity ? [{ root, identity }] : []));
197
+ const verdicts = await observeAlive(claims.map(({ identity }) => identity));
198
+ if (this.ctx !== ctx) {
199
+ return;
200
+ }
201
+ const live = new Map(claims.map(({ root }, index) => [root, verdicts[index]]));
202
+ for (const commit of commits) {
203
+ commit(live);
126
204
  }
127
- this.ctx?.ui.setStatus(STATUS_KEY, this.status());
205
+ this.activity();
206
+ this.widget();
128
207
  this.refresh?.();
208
+ const periodic = this.pending.length > 0 || [...this.rows.values()].some((row) => row.live);
209
+ if (periodic && !this.timer) {
210
+ this.timer = setInterval(() => this.scan(), 1000);
211
+ this.timer.unref();
212
+ }
213
+ else if (!periodic) {
214
+ clearInterval(this.timer);
215
+ this.timer = undefined;
216
+ }
217
+ }
218
+ activity() {
219
+ const visible = this.strip()
220
+ .filter((entry) => entry.live)
221
+ .slice(0, 4);
222
+ const ids = new Set(visible.map((entry) => entry.id));
223
+ for (const [id, peer] of this.peers) {
224
+ if (!ids.has(id) || peer.client.closed) {
225
+ peer.client.close();
226
+ this.peers.delete(id);
227
+ }
228
+ }
229
+ for (const row of visible) {
230
+ if (this.peers.has(row.id)) {
231
+ continue;
232
+ }
233
+ try {
234
+ const launch = this.launch(row.id);
235
+ const identity = actor(this.base, launch);
236
+ if (!identity) {
237
+ continue;
238
+ }
239
+ const client = new ObservationClient({ token: identity.token, rootId: launch.rootId, sessionId: launch.sessionId }, "activity", () => {
240
+ if (this.peers.get(row.id)?.client !== client) {
241
+ return;
242
+ }
243
+ const current = this.rows.get(row.id);
244
+ if (current) {
245
+ current.activity = client.connected ? client.activity : "Reconnecting";
246
+ }
247
+ this.widget();
248
+ });
249
+ this.peers.set(row.id, { token: identity.token, client });
250
+ }
251
+ catch {
252
+ // Older or not-yet-ready actors still have a known run status, but no live phase.
253
+ }
254
+ }
255
+ }
256
+ widget() {
257
+ const ctx = this.ctx;
258
+ if (!ctx) {
259
+ return;
260
+ }
261
+ const rows = this.strip();
262
+ if (rows.length === 0 && this.rows.size === this.total && this.error.length === 0) {
263
+ if (this.widgetVisible) {
264
+ ctx.ui.setWidget(STATUS_KEY, undefined);
265
+ }
266
+ this.widgetVisible = false;
267
+ this.widgetSignature = "";
268
+ return;
269
+ }
270
+ const signature = JSON.stringify([this.status(), rows.slice(0, 4), rows.length]);
271
+ if (signature === this.widgetSignature) {
272
+ return;
273
+ }
274
+ this.widgetSignature = signature;
275
+ if (this.widgetVisible) {
276
+ this.widgetRender?.();
277
+ return;
278
+ }
279
+ this.widgetVisible = true;
280
+ ctx.ui.setWidget(STATUS_KEY, (tui, theme) => {
281
+ this.widgetRender = () => {
282
+ tui.requestRender();
283
+ };
284
+ const content = new Container();
285
+ const open = (id) => {
286
+ if (ctx !== this.ctx) {
287
+ return;
288
+ }
289
+ void this.show(id, ctx).catch((error) => {
290
+ ctx.ui.notify(errorText(error), "error");
291
+ });
292
+ };
293
+ return {
294
+ render: (width) => {
295
+ const strip = this.strip();
296
+ const visible = strip.slice(0, 4);
297
+ content.clear();
298
+ content.addChild(actionText(theme.fg("accent", this.status()), () => open("")));
299
+ for (const row of visible) {
300
+ const glyph = row.live ? "●" : "○";
301
+ const color = row.live ? "accent" : "warning";
302
+ content.addChild(actionText(`${theme.fg(color, glyph)} ${theme.bold(row.name)} · ${row.activity ?? row.status}`, () => open(row.id)));
303
+ }
304
+ if (strip.length > visible.length) {
305
+ content.addChild(actionText(theme.fg("dim", `+${strip.length - visible.length} more · /agents`), () => open("")));
306
+ }
307
+ if (this.error.length > 0) {
308
+ content.addChild(new TruncatedText(theme.fg("warning", this.error), 0, 0));
309
+ }
310
+ return content.render(width);
311
+ },
312
+ handleMouse: (event) => content.handleMouse(event),
313
+ invalidate: () => {
314
+ // Render reads current rows, expansion and theme on every frame.
315
+ },
316
+ };
317
+ }, { placement: "aboveEditor" });
129
318
  }
130
319
  status() {
131
320
  const active = [...this.rows.values()].filter((row) => row.live).length;
@@ -134,7 +323,9 @@ class Observer {
134
323
  return `Agents: ${active}${checking ? "+" : ""} active${checking ? " (checking…)" : ""} · ${this.total} runs${error} · /agents`;
135
324
  }
136
325
  inventory() {
137
- return [...this.rows.values()].sort((a, b) => {
326
+ return [...this.rows.values()]
327
+ .filter((row) => row.status !== "completed")
328
+ .sort((a, b) => {
138
329
  const order = Number(b.live) - Number(a.live);
139
330
  if (order !== 0) {
140
331
  return order;
@@ -143,6 +334,10 @@ class Observer {
143
334
  return created !== 0 ? created : a.id.localeCompare(b.id);
144
335
  });
145
336
  }
337
+ /** The always-visible strip shows active work and unreadable runs; /agents keeps history. */
338
+ strip() {
339
+ return this.inventory().filter((row) => row.live || row.status === "unavailable");
340
+ }
146
341
  watch(id) {
147
342
  const matches = list(this.base).filter((candidate) => candidate.startsWith(id));
148
343
  const exact = matches.includes(id);
@@ -171,11 +366,14 @@ class Observer {
171
366
  !adoptedJournal(this.base, launch)) {
172
367
  throw new Error("Child journal path is outside its saved conversation.");
173
368
  }
369
+ const identity = actor(this.base, launch);
174
370
  return {
175
- tail: new JournalTail(launch.sessionFile, launch.sessionId),
176
- file: launch.sessionFile,
177
- id: launch.id,
178
- inherited: launch.context === "fork",
371
+ launch,
372
+ identity: identity && {
373
+ token: identity.token,
374
+ rootId: launch.rootId,
375
+ sessionId: launch.sessionId,
376
+ },
179
377
  };
180
378
  }
181
379
  async show(args, ctx) {
@@ -193,6 +391,7 @@ class Observer {
193
391
  const screen = new ObserverScreen({
194
392
  data: {
195
393
  inventory: () => this.inventory(),
394
+ checking: () => this.rows.size < this.total,
196
395
  row: (id) => this.rows.get(id),
197
396
  status: () => this.status(),
198
397
  error: () => this.error,
@@ -203,24 +402,32 @@ class Observer {
203
402
  keys,
204
403
  done: () => done(true),
205
404
  selected: args.trim(),
405
+ hideThinking: SettingsManager.create(ctx.cwd, getPiAgentDir()).getHideThinkingBlock(),
206
406
  });
207
407
  this.close = () => done(true);
208
408
  this.refresh = () => screen.update();
209
- return {
210
- render: (width) => screen.render(width),
211
- handleInput: (data) => screen.handleInput(data),
212
- invalidate: () => screen.invalidate(),
213
- dispose: () => {
214
- screen.dispose();
215
- this.close = undefined;
216
- this.refresh = undefined;
217
- },
409
+ const dispose = screen.dispose.bind(screen);
410
+ screen.dispose = () => {
411
+ dispose();
412
+ this.close = undefined;
413
+ this.refresh = undefined;
218
414
  };
415
+ return screen;
416
+ }, {
417
+ overlay: true,
418
+ overlayOptions: { width: "100%", maxHeight: "100%", anchor: "top-left", margin: 0 },
219
419
  });
220
420
  }
221
421
  register() {
222
422
  this.pi.on("session_start", (_event, ctx) => this.attach(ctx));
223
423
  this.pi.on("session_shutdown", () => this.dispose());
424
+ this.pi.on("tool_execution_end", (event) => {
425
+ if (this.ctx &&
426
+ this.watcher === undefined &&
427
+ (event.toolName === "delegate" || event.toolName === "agent_runs")) {
428
+ this.scan();
429
+ }
430
+ });
224
431
  this.pi.registerCommand("agents", {
225
432
  description: "List this parent's children; /agents <run-id or unique 8+ prefix> watches native activity read-only",
226
433
  handler: (args, ctx) => this.show(args, ctx),