@fitchmultz/pi-subagents 0.50.0 → 0.52.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 (180) hide show
  1. package/CHANGELOG.md +63 -2
  2. package/README.md +36 -5
  3. package/dist/agent/child-runtime.js +80 -0
  4. package/dist/agent/contracts.js +15 -24
  5. package/dist/agent/host.js +3 -16
  6. package/dist/agent/index.js +212 -192
  7. package/dist/agent/journal-tail.js +115 -156
  8. package/dist/agent/launch.js +20 -8
  9. package/dist/agent/legacy.js +65 -23
  10. package/dist/agent/notifications.js +138 -47
  11. package/dist/agent/observation-state.js +246 -0
  12. package/dist/agent/observation-view.js +258 -0
  13. package/dist/agent/observation.js +350 -0
  14. package/dist/agent/observe-card.js +243 -0
  15. package/dist/agent/observe-chrome.js +85 -0
  16. package/dist/agent/observe-client.js +285 -0
  17. package/dist/agent/observe-layout.js +65 -0
  18. package/dist/agent/observe-reading.js +84 -0
  19. package/dist/agent/observe-screen.js +400 -96
  20. package/dist/agent/observe-transcript.js +382 -0
  21. package/dist/agent/observe.js +250 -43
  22. package/dist/agent/parking.js +253 -0
  23. package/dist/agent/process.js +228 -28
  24. package/dist/agent/receipts.js +92 -20
  25. package/dist/agent/runner.js +229 -126
  26. package/dist/agent/shell.js +2 -1
  27. package/dist/agent/storage.js +47 -12
  28. package/dist/agent/tool-schemas.js +20 -0
  29. package/dist/agent/typebox.js +10 -5
  30. package/dist/agent/usage.js +0 -115
  31. package/dist/agent/view.js +46 -16
  32. package/dist/agent/wake.js +99 -0
  33. package/dist/build-info.js +1 -1
  34. package/dist/intercom-client.d.ts +26 -0
  35. package/dist/intercom-client.js +45 -0
  36. package/dist/pi-intercom/agent-dir.d.ts +1 -0
  37. package/dist/pi-intercom/bridge-session.js +2 -1
  38. package/dist/pi-intercom/broker/broker.js +62 -43
  39. package/dist/pi-intercom/broker/client-last.d.ts +24 -0
  40. package/dist/pi-intercom/broker/client-last.js +42 -5
  41. package/dist/pi-intercom/broker/client-park.d.ts +9 -0
  42. package/dist/pi-intercom/broker/client-park.js +21 -0
  43. package/dist/pi-intercom/broker/client.d.ts +59 -0
  44. package/dist/pi-intercom/broker/client.js +55 -29
  45. package/dist/pi-intercom/broker/connection.d.ts +29 -0
  46. package/dist/pi-intercom/broker/framing.d.ts +17 -0
  47. package/dist/pi-intercom/broker/framing.js +10 -7
  48. package/dist/pi-intercom/broker/guard.js +23 -0
  49. package/dist/pi-intercom/broker/last.d.ts +30 -0
  50. package/dist/pi-intercom/broker/last.js +87 -40
  51. package/dist/pi-intercom/broker/parked.js +107 -0
  52. package/dist/pi-intercom/broker/paths.d.ts +13 -0
  53. package/dist/pi-intercom/broker/paths.js +35 -12
  54. package/dist/pi-intercom/broker/pid.js +5 -1
  55. package/dist/pi-intercom/broker/server-connection.js +20 -1
  56. package/dist/pi-intercom/broker/snapshot-reader.d.ts +9 -0
  57. package/dist/pi-intercom/broker/snapshots.d.ts +29 -0
  58. package/dist/pi-intercom/broker/targets.d.ts +9 -0
  59. package/dist/pi-intercom/connection.js +71 -20
  60. package/dist/pi-intercom/identity.js +6 -5
  61. package/dist/pi-intercom/inbound-delivery.js +16 -60
  62. package/dist/pi-intercom/inbound-journal.js +83 -6
  63. package/dist/pi-intercom/inbound-record.js +18 -1
  64. package/dist/pi-intercom/inbound.js +22 -37
  65. package/dist/pi-intercom/index.js +41 -0
  66. package/dist/pi-intercom/last-message.js +4 -16
  67. package/dist/pi-intercom/lifecycle.js +88 -3
  68. package/dist/pi-intercom/outbound.js +5 -15
  69. package/dist/pi-intercom/reply-tracker.js +7 -6
  70. package/dist/pi-intercom/reply-wait.js +2 -2
  71. package/dist/pi-intercom/session-targets.d.ts +24 -0
  72. package/dist/pi-intercom/tool-registration.js +4 -1
  73. package/dist/pi-intercom/tool-results.js +5 -7
  74. package/dist/pi-intercom/tool-schemas.js +12 -26
  75. package/dist/pi-intercom/types.d.ts +212 -0
  76. package/dist/pi-intercom/types.js +54 -3
  77. package/dist/pi-intercom/validation.d.ts +12 -0
  78. package/dist/pi-intercom/validation.js +3 -0
  79. package/dist/shared/bounded-json.js +151 -0
  80. package/dist/shared/compact-cards.js +66 -0
  81. package/dist/shared/journal-frames.js +3 -6
  82. package/dist/shared/json-projection.js +267 -129
  83. package/dist/shared/native-journal.js +123 -56
  84. package/dist/shared/parking.js +12 -0
  85. package/dist/shared/post-exit-stdio-guard.js +120 -7
  86. package/dist/shared/tui-action.js +29 -0
  87. package/dist/shared/unknown.d.ts +9 -0
  88. package/docs/code-quality.md +5 -3
  89. package/docs/development.md +42 -3
  90. package/docs/intercom-bridge.md +9 -4
  91. package/docs/intercom.md +228 -7
  92. package/docs/performance.md +118 -14
  93. package/docs/reference.md +168 -20
  94. package/docs/retired-history-maintenance.md +252 -0
  95. package/package.json +28 -15
  96. package/scripts/archive-retired-history.mjs +464 -0
  97. package/scripts/build.mjs +48 -28
  98. package/scripts/measure-size.mjs +1 -1
  99. package/scripts/native-package-smoke.mjs +153 -12
  100. package/scripts/package-smoke.mjs +80 -11
  101. package/scripts/prepare.mjs +2 -2
  102. package/scripts/real-pi-live.mjs +377 -0
  103. package/scripts/real-pi-processes.mjs +6 -1
  104. package/scripts/real-pi-smoke.mjs +6 -282
  105. package/scripts/run-tests.mjs +4 -2
  106. package/scripts/typescript.mjs +35 -0
  107. package/skills/pi-intercom/SKILL.md +2 -1
  108. package/skills/pi-subagents/SKILL.md +23 -5
  109. package/src/agent/child-runtime.ts +105 -0
  110. package/src/agent/contracts.ts +18 -34
  111. package/src/agent/host.ts +39 -20
  112. package/src/agent/index.ts +234 -207
  113. package/src/agent/journal-tail.ts +141 -153
  114. package/src/agent/launch.ts +33 -9
  115. package/src/agent/legacy.ts +85 -31
  116. package/src/agent/notifications.ts +192 -67
  117. package/src/agent/observation-state.ts +277 -0
  118. package/src/agent/observation-view.ts +267 -0
  119. package/src/agent/observation.ts +409 -0
  120. package/src/agent/observe-card.ts +304 -0
  121. package/src/agent/observe-chrome.ts +170 -0
  122. package/src/agent/observe-client.ts +318 -0
  123. package/src/agent/observe-layout.ts +79 -0
  124. package/src/agent/observe-reading.ts +97 -0
  125. package/src/agent/observe-screen.ts +443 -129
  126. package/src/agent/observe-transcript.ts +434 -0
  127. package/src/agent/observe.ts +304 -71
  128. package/src/agent/parking.ts +341 -0
  129. package/src/agent/process.ts +280 -37
  130. package/src/agent/receipts.ts +114 -24
  131. package/src/agent/runner.ts +227 -149
  132. package/src/agent/shell.ts +2 -1
  133. package/src/agent/storage.ts +44 -11
  134. package/src/agent/tool-schemas.ts +33 -0
  135. package/src/agent/typebox.ts +12 -7
  136. package/src/agent/usage.ts +0 -130
  137. package/src/agent/view.ts +70 -20
  138. package/src/agent/wake.ts +107 -0
  139. package/src/intercom-client.ts +108 -0
  140. package/src/pi-intercom/bridge-session.ts +2 -2
  141. package/src/pi-intercom/broker/broker.ts +64 -53
  142. package/src/pi-intercom/broker/client-last.ts +65 -12
  143. package/src/pi-intercom/broker/client-park.ts +23 -0
  144. package/src/pi-intercom/broker/client.ts +63 -45
  145. package/src/pi-intercom/broker/framing.ts +11 -8
  146. package/src/pi-intercom/broker/guard.ts +33 -0
  147. package/src/pi-intercom/broker/last.ts +131 -43
  148. package/src/pi-intercom/broker/parked.ts +130 -0
  149. package/src/pi-intercom/broker/paths.ts +39 -12
  150. package/src/pi-intercom/broker/pid.ts +6 -1
  151. package/src/pi-intercom/broker/server-connection.ts +27 -2
  152. package/src/pi-intercom/connection.ts +103 -39
  153. package/src/pi-intercom/identity.ts +8 -5
  154. package/src/pi-intercom/inbound-delivery.ts +20 -66
  155. package/src/pi-intercom/inbound-journal.ts +103 -7
  156. package/src/pi-intercom/inbound-record.ts +21 -4
  157. package/src/pi-intercom/inbound.ts +32 -49
  158. package/src/pi-intercom/index.ts +44 -0
  159. package/src/pi-intercom/last-message.ts +11 -20
  160. package/src/pi-intercom/lifecycle.ts +117 -14
  161. package/src/pi-intercom/outbound.ts +7 -16
  162. package/src/pi-intercom/reply-tracker.ts +8 -6
  163. package/src/pi-intercom/reply-wait.ts +3 -2
  164. package/src/pi-intercom/runtime-types.ts +0 -1
  165. package/src/pi-intercom/tool-registration.ts +4 -1
  166. package/src/pi-intercom/tool-results.ts +10 -9
  167. package/src/pi-intercom/tool-schemas.ts +12 -44
  168. package/src/pi-intercom/types.ts +115 -4
  169. package/src/pi-intercom/validation.ts +4 -0
  170. package/src/shared/bounded-json.ts +162 -0
  171. package/src/shared/compact-cards.ts +118 -0
  172. package/src/shared/journal-frames.ts +3 -19
  173. package/src/shared/json-projection.ts +267 -147
  174. package/src/shared/native-journal.ts +157 -75
  175. package/src/shared/parking.ts +22 -0
  176. package/src/shared/post-exit-stdio-guard.ts +165 -7
  177. package/src/shared/tui-action.ts +39 -0
  178. package/tsconfig.client.json +8 -0
  179. package/tsconfig.json +1 -0
  180. package/src/shared/stream-json.d.ts +0 -10
@@ -0,0 +1,382 @@
1
+ import { recordAt, errorMessage } from "../shared/unknown.js";
2
+ import { actionText, actionHints } from "../shared/tui-action.js";
3
+ import { ConversationJournal, plainText } from "./journal-tail.js";
4
+ import { Card } from "./observe-card.js";
5
+ import { Container, Text } from "./observe-layout.js";
6
+ /** One mounted conversation page. Frozen cards are never mutated while the reader is above live. */
7
+ export class ObserverTranscript extends Container {
8
+ journal;
9
+ positions = [];
10
+ cards = new Map();
11
+ pages = new Map();
12
+ expanded = new Set();
13
+ before;
14
+ start = 0;
15
+ stop = 0;
16
+ entries = [];
17
+ selected;
18
+ height = 0;
19
+ mounted = [];
20
+ waiting = false;
21
+ revision = "";
22
+ display;
23
+ changed;
24
+ loaded;
25
+ paused = false;
26
+ hydration;
27
+ active;
28
+ jobs = new Map();
29
+ pending = new Map();
30
+ version = 0;
31
+ constructor(options) {
32
+ super();
33
+ this.journal = new ConversationJournal(options.file, options.sessionId);
34
+ this.display = options;
35
+ this.changed = options.changed;
36
+ this.loaded = options.loaded;
37
+ }
38
+ observe(client) {
39
+ const native = this.journal.native;
40
+ native.refresh();
41
+ this.waiting = !native.indexed;
42
+ if (client && !client.settle((id) => native.byId.has(id))) {
43
+ this.waiting = true;
44
+ }
45
+ }
46
+ refresh(client) {
47
+ this.observe(client);
48
+ const revision = `${this.journal.native.records.length}:${client?.revision ?? -1}:${this.before ?? ""}`;
49
+ if (revision === this.revision) {
50
+ return false;
51
+ }
52
+ this.revision = revision;
53
+ return true;
54
+ }
55
+ sync(client) {
56
+ if (!this.refresh(client)) {
57
+ return false;
58
+ }
59
+ const entries = this.journal.entries(client?.leaf);
60
+ this.entries = entries;
61
+ const stop = this.boundary(entries);
62
+ const start = Math.max(0, stop - 24);
63
+ this.start = start;
64
+ this.stop = stop;
65
+ const selected = entries.slice(start, stop);
66
+ this.clear();
67
+ if (start > 0) {
68
+ this.historyRow("↑ Load earlier activity", -1);
69
+ }
70
+ this.mounted = [];
71
+ for (const entry of selected.toReversed()) {
72
+ this.mounted.unshift(this.card(entry, this.before === undefined && entry === selected.at(-1)));
73
+ }
74
+ for (const card of this.mounted) {
75
+ this.addChild(card);
76
+ }
77
+ if (this.before === undefined) {
78
+ this.live(client);
79
+ }
80
+ if (this.before !== undefined) {
81
+ this.historyRow("↓ Load later activity", 1);
82
+ }
83
+ this.evict();
84
+ return true;
85
+ }
86
+ pause() {
87
+ this.paused = true;
88
+ this.active?.controller.abort();
89
+ this.jobs.clear();
90
+ this.pending.clear();
91
+ }
92
+ resume() {
93
+ if (!this.paused) {
94
+ return;
95
+ }
96
+ this.paused = false;
97
+ for (const card of this.mounted) {
98
+ const entry = this.journal.native.byId.get(card.id);
99
+ const request = this.pages.get(card.id) ?? { requested: 0 };
100
+ if (entry && (!card.ready || request.requested !== card.page)) {
101
+ this.load(entry, card, request);
102
+ }
103
+ }
104
+ }
105
+ card(entry, latest) {
106
+ const id = entry.value.id;
107
+ let card = this.cards.get(id);
108
+ if (!card) {
109
+ const call = recordAt(entry.value, "message")?.toolCallId;
110
+ const live = typeof call === "string" ? `tool:${call}` : "";
111
+ card = this.cards.get(live) ?? new Card(id);
112
+ this.cards.delete(live);
113
+ card.id = id;
114
+ if (!card.tool && !card.assistant) {
115
+ card.content.addChild(new Text("Loading published content…", 0, 0));
116
+ }
117
+ this.cards.set(id, card);
118
+ }
119
+ if (!card.ready) {
120
+ this.load(entry, card, this.pages.get(id) ?? { requested: latest ? -1 : 0 });
121
+ }
122
+ return card;
123
+ }
124
+ current({ card, request, controller }) {
125
+ return (!controller.signal.aborted &&
126
+ this.cards.get(card.id) === card &&
127
+ this.pending.get(card.id) === request);
128
+ }
129
+ load(entry, card, request) {
130
+ if (this.paused || this.pending.get(card.id) === request) {
131
+ return;
132
+ }
133
+ this.pending.set(card.id, request);
134
+ this.pages.set(card.id, request);
135
+ this.jobs.set(card.id, { entry, card, request, controller: new AbortController() });
136
+ if (this.active?.card === card) {
137
+ this.active.controller.abort();
138
+ }
139
+ this.drain();
140
+ }
141
+ drain() {
142
+ const job = this.jobs.values().next().value;
143
+ if (this.hydration || this.paused || !job) {
144
+ return;
145
+ }
146
+ this.jobs.delete(job.card.id);
147
+ const { card } = job;
148
+ this.active = job;
149
+ this.hydration = this.hydrate(job)
150
+ .catch((error) => {
151
+ if (!this.current(job)) {
152
+ return;
153
+ }
154
+ this.pending.delete(card.id);
155
+ card.footer.clear();
156
+ card.footer.addChild(new Text(`Content unavailable: ${plainText(errorMessage(error))}`, 0, 0));
157
+ card.invalidate();
158
+ this.version++;
159
+ this.loaded();
160
+ })
161
+ .finally(() => {
162
+ this.hydration = undefined;
163
+ this.active = undefined;
164
+ this.drain();
165
+ });
166
+ }
167
+ async hydrate(job) {
168
+ if (!this.current(job)) {
169
+ return;
170
+ }
171
+ const { entry, card, request, controller } = job;
172
+ const options = { signal: controller.signal, hideThinking: this.display.hideThinking };
173
+ let page = await this.journal.page(entry, Math.max(0, request.requested), options);
174
+ if (request.requested < 0 && page.pages > 1) {
175
+ page = await this.journal.page(entry, page.pages - 1, options);
176
+ }
177
+ const call = typeof page.message.toolCallId === "string" && !card.tool
178
+ ? await this.journal.call(page.message.toolCallId, card.id, controller.signal)
179
+ : undefined;
180
+ if (!this.current(job)) {
181
+ return;
182
+ }
183
+ this.durable(card.id, page, call?.arguments);
184
+ this.pages.set(card.id, { requested: page.page });
185
+ this.pending.delete(card.id);
186
+ this.version++;
187
+ if (request.navigation === undefined) {
188
+ this.loaded();
189
+ }
190
+ else {
191
+ this.changed({ kind: "content", id: card.id, direction: request.navigation });
192
+ }
193
+ }
194
+ evict() {
195
+ const mounted = new Set(this.mounted.map((card) => card.id));
196
+ for (const id of this.cards.keys()) {
197
+ if (!mounted.has(id)) {
198
+ this.cards.delete(id);
199
+ this.jobs.delete(id);
200
+ this.pending.delete(id);
201
+ }
202
+ }
203
+ if (this.active && !mounted.has(this.active.card.id)) {
204
+ this.active.controller.abort();
205
+ }
206
+ for (const choices of [this.pages, this.expanded]) {
207
+ for (const id of choices.keys()) {
208
+ if (choices.size > 48 && !mounted.has(id)) {
209
+ choices.delete(id);
210
+ }
211
+ }
212
+ }
213
+ }
214
+ durable(id, page, args) {
215
+ const card = this.cards.get(id);
216
+ if (!card) {
217
+ return;
218
+ }
219
+ card.expanded ||= this.expanded.has(id);
220
+ card.publish(page, args, this.display);
221
+ if (page.pages > 1) {
222
+ card.footer.addChild(new Text(`Content page ${page.page + 1}/${page.pages}`, 0, 0));
223
+ card.footer.addChild(actionHints([-1, 1].map((direction) => ({
224
+ text: direction < 0 ? "Previous page" : "Next page",
225
+ action: () => {
226
+ this.changePage(id, direction);
227
+ },
228
+ }))));
229
+ }
230
+ card.invalidate();
231
+ }
232
+ changePage(id, direction) {
233
+ const card = this.cards.get(id);
234
+ const entry = this.journal.native.byId.get(id);
235
+ if (!card || !entry) {
236
+ return false;
237
+ }
238
+ const page = Math.max(0, Math.min(card.pages - 1, card.page + direction));
239
+ if (page === card.page) {
240
+ return false;
241
+ }
242
+ const request = { requested: page, navigation: direction };
243
+ this.pages.set(id, request);
244
+ this.changed({ kind: "pending" });
245
+ this.load(entry, card, request);
246
+ return true;
247
+ }
248
+ contentPage(line, height, direction) {
249
+ const positions = direction < 0 ? this.positions.toReversed() : this.positions;
250
+ return positions.some((item) => {
251
+ const visible = item.end > line && item.start < line + height;
252
+ const boundary = direction < 0 ? line <= item.start : line + height >= item.end;
253
+ return visible && boundary && this.changePage(item.id, direction);
254
+ });
255
+ }
256
+ live(client) {
257
+ if (!client) {
258
+ return;
259
+ }
260
+ const message = client.message;
261
+ if (message) {
262
+ const id = `live:${message.key}`;
263
+ let card = this.cards.get(id);
264
+ if (!card) {
265
+ card = new Card(id);
266
+ this.cards.set(id, card);
267
+ }
268
+ card.stream(message, this.display.hideThinking);
269
+ this.mounted.push(card);
270
+ this.addChild(card);
271
+ }
272
+ this.pendingCalls(client);
273
+ for (const value of client.tools.values()) {
274
+ this.liveTool(value);
275
+ }
276
+ }
277
+ pendingCalls(client) {
278
+ for (const block of client.message?.blocks.filter(Boolean) ?? []) {
279
+ if (block.type === "toolCall" &&
280
+ block.id !== undefined &&
281
+ block.name !== undefined &&
282
+ !client.tools.has(block.id)) {
283
+ this.liveTool({ id: block.id, name: block.name, args: block.args }, true);
284
+ }
285
+ }
286
+ }
287
+ liveTool(value, preparing = false) {
288
+ const id = `tool:${value.id}`;
289
+ let card = this.cards.get(id);
290
+ if (!card) {
291
+ card = new Card(id);
292
+ card.expanded = this.expanded.has(id);
293
+ this.cards.set(id, card);
294
+ }
295
+ card.updateTool(value, this.display, preparing);
296
+ this.mounted.push(card);
297
+ this.addChild(card);
298
+ }
299
+ boundary(entries) {
300
+ const found = entries.findIndex((entry) => entry.value.id === this.before);
301
+ this.before = found < 0 ? undefined : this.before;
302
+ return this.before === undefined || found < 0 ? entries.length : found;
303
+ }
304
+ historyRow(label, direction) {
305
+ this.addChild(actionText(label, () => {
306
+ this.changed({ kind: "history", direction });
307
+ }));
308
+ }
309
+ history(direction) {
310
+ if ((direction < 0 && this.start === 0) || (direction > 0 && this.before === undefined)) {
311
+ return;
312
+ }
313
+ const anchor = direction < 0
314
+ ? this.mounted.at(0)
315
+ : this.mounted.findLast((card) => this.journal.native.byId.has(card.id));
316
+ const stop = direction < 0 ? this.start + 1 : this.stop + 23;
317
+ this.before = this.entries[stop]?.value.id;
318
+ return anchor?.id;
319
+ }
320
+ latest() {
321
+ this.before = undefined;
322
+ const entry = this.entries.at(-1);
323
+ if (!entry) {
324
+ return;
325
+ }
326
+ const card = this.cards.get(entry.value.id);
327
+ if (card?.ready === true && card.page === card.pages - 1) {
328
+ return;
329
+ }
330
+ const request = { requested: -1 };
331
+ this.pages.set(entry.value.id, request);
332
+ if (card) {
333
+ this.load(entry, card, request);
334
+ }
335
+ }
336
+ select(line, height) {
337
+ const position = this.positions.find((item) => item.end > line &&
338
+ item.start < line + height &&
339
+ this.cards.get(item.id)?.tool !== undefined);
340
+ this.selected = position?.id;
341
+ let changed = false;
342
+ for (const card of this.mounted) {
343
+ const selected = card.id === this.selected;
344
+ if (card.selected !== selected) {
345
+ card.selected = selected;
346
+ card.invalidate();
347
+ changed = true;
348
+ }
349
+ }
350
+ return changed;
351
+ }
352
+ toggle() {
353
+ this.cards.get(this.selected ?? "")?.toggle();
354
+ }
355
+ anchor(line) {
356
+ const position = this.positions.find((item) => item.end > line);
357
+ return position && { id: position.id, offset: Math.max(0, line - position.start) };
358
+ }
359
+ line(anchor) {
360
+ const position = this.positions.find((item) => item.id === anchor.id);
361
+ return (anchor.end === true ? (position?.end ?? 0) : (position?.start ?? 0)) + anchor.offset;
362
+ }
363
+ render(width) {
364
+ this.positions.length = 0;
365
+ let start = 0;
366
+ for (const child of this.children) {
367
+ const height = child.render(width).length;
368
+ if (child instanceof Card) {
369
+ this.positions.push({ id: child.id, start, end: start + height });
370
+ if (child.expanded) {
371
+ this.expanded.add(child.id);
372
+ }
373
+ else {
374
+ this.expanded.delete(child.id);
375
+ }
376
+ }
377
+ start += height;
378
+ }
379
+ this.height = start;
380
+ return super.render(width);
381
+ }
382
+ }