@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
package/docs/reference.md CHANGED
@@ -13,7 +13,6 @@ delegate({
13
13
  cwd: "/optional/working/directory",
14
14
  model: "provider/model",
15
15
  context: "fresh",
16
- async: true,
17
16
  });
18
17
  ```
19
18
 
@@ -22,15 +21,35 @@ canned profile. `name` is an optional display name, not a unique identity or rol
22
21
  Omitted cwd/model inherit the invoking session's effective selection. A supplied
23
22
  model must be available; there is no hidden alternate-model retry catalog.
24
23
 
24
+ On hosts exporting Pi's native `resolveModelReference`, `model` also accepts
25
+ `provider/model:thinking` (for example `provider/model:low` or `provider/model:off`).
26
+ The explicit suffix overrides inherited thinking for that child only. Literal
27
+ model IDs take precedence, including IDs containing colons or additional slashes;
28
+ unknown IDs and invalid suffixes fail without fuzzy matching or another provider.
29
+ Native resolution follows Pi's case-insensitive ID matching. Pi still applies the
30
+ selected model's native thinking capabilities.
31
+
32
+ Official Pi 1.3.0 and 1.4.0 do not export that resolver. Older fork runtimes also
33
+ lack the export. Those hosts retain
34
+ case-sensitive literal-ID overrides and inherited thinking; suffix requests fail with an upgrade
35
+ message rather than being parsed by an extension-side replacement. This requires
36
+ a host with the public resolver export, not merely a matching version number.
37
+
25
38
  Fresh context is the default. Fork uses the native conversation, including
26
39
  historical tool calls and results; restricting a child's authority does not erase
27
40
  the parent's evidence. Native Pi system/tool instructions and applicable project
28
41
  resources still apply. Forking does not authorize nested delegation.
29
42
 
30
- The default response acknowledges launch. It does not claim a model response or
31
- completion. `async:false` waits for this new work. Cancelling that owned foreground
32
- invocation requests stop; disposing the parent runtime detaches the observer
33
- without implicitly stopping accepted background work.
43
+ The response acknowledges launch only. It does not claim a model response or
44
+ completion, and no tool waits for child completion: the parent continues other
45
+ work or ends its turn, and the completion notice resumes it. Aborting a parent reply
46
+ with Escape does not stop already-launched children; use `stop` for one run. Exiting
47
+ or replacing the parent session stops its children, including active work, and
48
+ preserves their saved conversations.
49
+
50
+ Completion notifications need a running main session. Ending a turn in a persistent
51
+ TUI, RPC or SDK session is not parent exit. A one-shot print invocation exits after
52
+ its final turn and stops any remaining children; it does not wait for their results.
34
53
 
35
54
  Only a main may delegate. Children cannot bypass the restriction through a hidden
36
55
  tool or programmatic invocation of `delegate` or `agent_runs`.
@@ -43,9 +62,8 @@ project, display name, or a different session forked from its history.
43
62
  | Action | Purpose |
44
63
  | ---------- | ------------------------------------------------------------------------------------ |
45
64
  | `list` | Page through the current native owner's saved-run metadata, without output previews. |
46
- | `inspect` | Read one run's immutable result and process/publication facts. |
65
+ | `inspect` | Read one run's result and process/publication facts without waiting or interrupting. |
47
66
  | `continue` | Supply a complete follow-up prompt using the saved child conversation. |
48
- | `wait` | Wait for an existing run; cancelling this observer does not stop its producer. |
49
67
  | `stop` | Request termination; inspect the actual process outcome afterward. |
50
68
 
51
69
  All actions except `list` require `id`. Use IDs returned by the tools rather than
@@ -63,7 +81,8 @@ missing session state is an error, not permission to start a different chat.
63
81
  If a handoff saved a run but failed before admission, retry `continue` with the
64
82
  exact same prompt after its actor and owned shells have exited. This resumes the
65
83
  saved run and queued command, not another successor. A different prompt is refused
66
- until admission or a canonical result. A missing queued command or ambiguous
84
+ until admission or a canonical result; end the turn or do other work until the
85
+ completion notice rather than polling. A missing queued command or ambiguous
67
86
  process claim requires inspection rather than automatic replay. If the prompt
68
87
  was lost from context, read its exact value from `launch.json` beside the run's
69
88
  reported `outputFile`; do not reconstruct or paraphrase it. For adopted legacy
@@ -72,13 +91,40 @@ not the retired legacy handle.
72
91
  Guidance that arrives after its run settles is reported as stale rather than
73
92
  starting untracked work. Inspection text exposes the guidance command IDs and
74
93
  admission states, native receipt publication and separately known process exit.
75
- Pending admission is not consumption. Inspect the result and send a new continuation
94
+ Continuing active work returns a guidance receipt with the command ID; queue
95
+ acceptance is not admission, and admission is not model consumption. Inspect the result and send a new continuation
76
96
  if guidance became stale; do not infer acceptance from the old completed output.
77
97
 
78
98
  A completed child can remain alive and messageable while its main remains
79
- attached. Once the main detaches or is proven absent, idle actors exit and active
80
- accepted work can finish before exit. Reload/resume uses saved state. There is no
81
- arbitrary idle lease that silently expires a child while the main is still live.
99
+ attached. On a Pi fork with native working-session capture, a child that stays
100
+ idle for five minutes (no run, turn, journal change, live viewer or owned shell)
101
+ parks: it saves its exact working state and its process exits, while its intercom
102
+ identity stays listed with no departure. Passive messages, topic updates and
103
+ `last` reads do not wake it; the main answers `last` from the saved history, and
104
+ without its main the read reports that history unavailable. Receipt reads also do
105
+ not wake it and report `publication_unavailable`. A
106
+ normal message, ask or continuation restores the exact saved state into a new
107
+ process, which then receives retained messages in order. If the final save
108
+ fails, the child records `park-failed.json` and nothing restarts implicitly;
109
+ retained messages stay queued and continuing reports the failure. Official Pi
110
+ keeps idle children resident. Parent exit or session replacement stops its actors and verified owned
111
+ native shells, allowing native abort a short grace period before escalation.
112
+ Actors also watch for parent death, including a hard kill. Ordinary extension
113
+ reload keeps children alive because the parent has not exited.
114
+
115
+ Under a fitchmultz/pi fork launcher that provides `ctx.managedHost`, the launcher
116
+ that survives `pi restart` and `/restart` owns child lifetime instead of the worker
117
+ process. A restart keeps children running; the replacement attaches and delivers
118
+ their completions only after it becomes ready, and startup rollback keeps them too.
119
+ Quitting, including quitting a replacement before it is ready, still stops them.
120
+ After upgrading, relaunch the full CLI once: a worker started by an already-running
121
+ older launcher has no `managedHost`, so restart still stops children with the old
122
+ worker. Official Pi, SDK, print and RPC sessions keep worker-lifetime ownership.
123
+
124
+ Resume the same parent and use `continue` with a new prompt to resume a saved
125
+ child conversation. This does not replay interrupted tool calls or reconstruct
126
+ an execution checkpoint: the child inspects the current work state. Saved
127
+ journals and results are retained whether the child was active, stopped or done.
82
128
 
83
129
  ### Evidence levels
84
130
 
@@ -102,6 +148,27 @@ replacing its cwd/environment behavior. Arbitrary custom or remote bash tools
102
148
  remain owned by their extensions; the package does not claim cold-crash cleanup
103
149
  of those external processes.
104
150
 
151
+ Stop and shutdown skip a saved PID only when it has exited or replacement is
152
+ proven; uncertain ownership still reports a diagnostic without signaling it.
153
+ A PID now owned by another user (permission denied) needs the same proof; it is
154
+ never treated as exited. Linux cannot read another user's namespace identity,
155
+ so such a PID stays uncertain there.
156
+ On macOS, a UTC-observed process birth strictly after its record's write time
157
+ disproves ownership even when the reused PID is no longer a group leader. This
158
+ assumes native record timestamps have not been backdated and the clock did not
159
+ move backward between process birth and publication. Missing timestamps,
160
+ same-second uncertainty and timezone differences alone do not prove replacement.
161
+ Linux uses its native process-clock and namespace identity instead.
162
+ New Android actor and shell records also save a native monotonic-clock bound
163
+ after process birth, qualified by the real boot ID and reader's time namespace.
164
+ Only a conservative birth lower bound from native process elapsed age, in that
165
+ same clock context, can disprove ownership. This survives controller clock and
166
+ timezone changes without comparing `ps` boot-epoch dates with record timestamps.
167
+ Unreadable ages, missing legacy clock bounds, different clock contexts and
168
+ whole-second/tick uncertainty do not prove replacement; identity checks still
169
+ fail closed rather than signal an unverified PID. An existing record without a
170
+ clock bound remains usable when its original process identity is verifiable.
171
+
105
172
  ## `intercom`
106
173
 
107
174
  Use `list`, `send`, and `last` for normal coordination. `ask`, `reply`, `pending`,
@@ -110,17 +177,81 @@ for topology, target resolution, native replay, and delivery distinctions.
110
177
 
111
178
  ### Usage accounting
112
179
 
113
- The parent's token/cost totals are not automatically the entire workflow's spend.
114
- An asynchronous completion notice does not charge child usage to the parent.
115
- Finalized foreground or `wait` collection owns that accounting; repeated collection
116
- does not double-charge the same native contribution. Child journals retain their
117
- own usage. Metadata inventory and the human observer do not repair or collect
118
- accounting; explicit inspection can reconcile pending accounting without itself
119
- charging the parent.
180
+ New calls do not attribute child usage to the parent's token/cost totals. Each
181
+ child result records its own native contributions exactly once, and continuations
182
+ add only newly published entries. Completion notices, inspection, reload and
183
+ metadata inventory do not charge the parent. Explicit inspection can reconcile
184
+ pending child accounting. Per-entry `result.contributions` remain in canonical
185
+ results and native tool details. JSON receipt text (including `stop`) selects
186
+ execution and accounting-status metadata; it does not automatically serialize
187
+ per-entry contributions or additional saved diagnostics. Process/stop evidence
188
+ and output/paging fields remain available in the text. Inventory, receipts,
189
+ inspection and completion notices each have an explicit model-facing renderer.
190
+
191
+ Historical foreground and `wait` charges already recorded in a parent's journal
192
+ remain in its native totals. For workflow totals, count shared continuation
193
+ journals once and do not count child contributions again when an older parent
194
+ tool result already includes them.
195
+
196
+ ## Completion delivery
197
+
198
+ Attachment publishes durable ownership before ordinary session startup returns.
199
+ Retained completion recovery runs about 200 ms later or on an explicit
200
+ reconciliation, not on the awaited startup path. That delay is not a delivery
201
+ deadline. A short-lived main may exit before recovery, and a prompt started
202
+ immediately after startup may receive the recovered backlog as steering input.
203
+ Managed launchers still gate attachment on accepted host readiness.
204
+
205
+ After that, the main reconciles when its owner directory, an unfinished run's
206
+ `result.json`, a pending-accounting child journal or the retired legacy result
207
+ directory changes, and after settlement or `agent_runs` controls. A change only
208
+ starts reconciliation; canonical results, admissions and journal receipts still
209
+ decide delivery. Directories the platform cannot watch, and failed reconciliations,
210
+ fall back to the previous 200 ms polling. An idle main does no recurring scan.
211
+ Instead, once a second it stats the inputs the last reconciliation read (the owner
212
+ and legacy directories and each awaited file) and reconciles if any changed. A
213
+ change notification the OS drops, or a legacy directory created after attachment,
214
+ therefore delays delivery by at most about a second.
215
+
216
+ Actors admit durable commands the same way: a change to their `commands`
217
+ directory, a stop or a deferred run starts a scan. Their one-second parent
218
+ liveness check also rechecks the directory for changes, so a dropped notification
219
+ delays admission by at most that check. Without a watch, actors poll every 100 ms
220
+ as before.
221
+
222
+ Completion notices use steering, not follow-up delivery. A busy parent receives
223
+ results at the next native tool boundary; it need not finish its task first.
224
+ An idle backlog, including retained legacy results, is published before one wakeup.
225
+ This avoids separately deferred settlement wakeups replaying the same result.
226
+ New-runtime backlogs are sorted by completion time, then launch time and ID—not
227
+ directory order. A later scan cannot reorder messages already admitted to Pi.
228
+
229
+ Admission is still not publication or model consumption. Native physical receipts
230
+ suppress redelivery across reload/resume, and dropped queues can recover. Receipt
231
+ polling reuses unchanged verified journal metadata; changed files validate the
232
+ committed prefix before parsing appended records. Replacement, truncation, changed
233
+ prefixes, malformed records, and missing final newlines do not fabricate receipts.
234
+ Completion delivery appends context; it does not replace prior prompts, tools,
235
+ model settings, or the active conversation.
120
236
 
121
237
  ## Read-only human observation
122
238
 
123
- Interactive main sessions expose an agent status line and `/agents` list/watch.
239
+ Interactive main sessions expose an above-editor widget and `/agents` list/watch.
240
+ The widget shows up to five active runs with circle indicators, status and short ID;
241
+ unreadable (unavailable) runs also stay listed. The default picker hides completed
242
+ runs; failed, stopped, interrupted, and unavailable runs remain visible there.
243
+ The header total counts all saved runs; the widget rows and overflow count include
244
+ only active work and unreadable runs.
245
+ `/agents <run-id>` still opens
246
+ saved completed runs, and an already-open watch stays open through completion.
247
+ An empty partial inventory says it is checking saved runs, not that no runs exist.
248
+ In fullscreen mode, click a named row to watch that child; click the header or
249
+ overflow count to open `/agents`. Picker rows and selection/Back/Close hints are
250
+ clickable too. Regular mode retains the keyboard path because Pi leaves mouse
251
+ input to the terminal there. Ctrl+O expands model/cwd details. The widget
252
+ clears once a complete error-free scan finds no active or unreadable runs, or when
253
+ the native parent is replaced; it stays while saved runs are still being checked. It is not
254
+ a footer-only count.
124
255
  Use Up/Down to select, Enter to watch, and Escape to return/close (the displayed
125
256
  hints follow Pi's configured selection keys). `/agents <exact-run-id>` opens that
126
257
  run directly. Children and print/RPC modes do not install a watcher.
@@ -132,6 +263,13 @@ journal records and unpublished provider token deltas are not presented as compl
132
263
  entries. Agent tools remain the orchestration surface; this does not restore the
133
264
  retired workflow UI or remote transcript APIs.
134
265
 
266
+ Long `delegate`, `agent_runs`, and `intercom` result cards start with a four-visual-line
267
+ preview, including output that wraps from one long logical line. Pi's native card
268
+ click and configured expand-tools key (Ctrl+O by default) reveal the full output.
269
+ Child completion and Intercom message cards use the same native expansion state;
270
+ their clickable header hints toggle the global tools-expanded view, just like
271
+ Ctrl+O. Full saved and model-visible content is unchanged.
272
+
135
273
  ## Installation and upgrade
136
274
 
137
275
  ```bash
@@ -146,6 +284,16 @@ committed npm lockfile. The build publishes a complete staged `dist/`; failed
146
284
  compilation does not destroy the previous working tree. Do not rebuild a checkout
147
285
  while active sessions load its files.
148
286
 
287
+ On native Android, source installation and verification require an Android build
288
+ of `tsgo` on PATH. The prebuilt npm installation needs no compiler. See
289
+ [Android development](development.md#native-androidtermux).
290
+
291
+ Where the filesystem denies hard links, exclusive saved-run records are atomic
292
+ relative symlinks to complete, private `.data` files beside them. Keep the entire
293
+ run directory, including those backing files and relative links, when backing up
294
+ or moving saved state; they are not disposable staging files. Existing regular
295
+ records remain readable and are never replaced by a competing exclusive writer.
296
+
149
297
  Finish in-flight work before migrating from the old workflow engine. Preserve the
150
298
  prior package, native sessions, and durable state for rollback. Old profile files
151
299
  are left untouched but no longer configure delegation. Chain/fanout orchestration,
@@ -0,0 +1,252 @@
1
+ # Archive the retired SQLite history index
2
+
3
+ Public tagged releases **v0.44.0 through v0.49.1** can open `history-index`.
4
+ **v0.50.0** first carries removal commit
5
+ [`7b721902`](https://github.com/fitchmultz/pi-subagents/commit/7b721902fb28c31e15757b3dc3dbe1b42b1a06c9).
6
+ Upgrades leave that directory untouched. There is no startup cleanup, deletion
7
+ prompt, or success marker. An older installation can still reopen or recreate it.
8
+
9
+ The packaged `scripts/archive-retired-history.mjs` command is an **opt-in,
10
+ offline-only archive route**, never an extension startup or installation hook.
11
+ It preserves the whole recovery set; it never deletes, opens a database through
12
+ SQLite, checkpoints originals, or copies across filesystems. A same-filesystem
13
+ archive **reclaims zero file allocations**. A later purge needs separate approval.
14
+ This guide is not permission to inspect or change a live installation.
15
+
16
+ ## Establish the maintenance boundary
17
+
18
+ 1. Obtain the machine owner's approval for the exact storage, archive location
19
+ and maintenance window. Inventory every installation from the affected range,
20
+ bridge, launcher, detached runner, service, scheduler and other client that can
21
+ access the volume, including packages that can be launched again.
22
+ 2. Stop **all readers and writers** and disable their launch/relaunch paths for
23
+ the entire window, verification and any rollback. Include old detached timing
24
+ writers and other machines or namespaces that can reach the storage. Keep
25
+ another operator from starting maintenance against the same paths.
26
+ 3. Establish that boundary through the owner's actual service/launcher controls.
27
+ A separately booted OS is not mandatory. If stopped/disabled applications and
28
+ access controls cannot establish quiescence, use an isolated maintenance
29
+ environment, for example a separate boot with the application volume mounted
30
+ locally and no application services or other clients. Otherwise stop untouched.
31
+
32
+ The command's `--quiesced` flag acknowledges these conditions; it cannot enforce
33
+ or verify them. Privileged native `lsof` checks detect observable open handles,
34
+ not a stopped program's next launch, another client, or an inaccessible namespace.
35
+ Root visibility is necessary, not a universal completeness guarantee. A rename,
36
+ one empty `lsof` result, a SQLite lock, a new marker or unchanged hashes do not
37
+ prove exclusion. Rechecks detect some changes but do **not** remove all races.
38
+ Privileged or same-account interference remains outside the enforced boundary.
39
+ Do not use the command if that boundary is uncertain.
40
+
41
+ ## Select and validate the owned set
42
+
43
+ Resolve the retired installation's agent directory. Its default was `~/.pi/agent`;
44
+ `PI_CODING_AGENT_DIR` could select another. In a separate maintenance environment,
45
+ use the mounted application account's paths, not the maintenance user's home.
46
+ Select **only** its `history-index` child, never the agent directory itself.
47
+
48
+ Establish provenance from the retired installation and the owner's private
49
+ inventory. Familiar names and schema matches do not authenticate ownership.
50
+ The command accepts only these historical flat-file names:
51
+
52
+ - SHA-256 owner manifests: `<64 lowercase hex digits>.json`.
53
+ - UUID generation databases: `<uuid>.sqlite`, including unselected generations.
54
+ - Ingest databases: `<uuid>.sqlite.ingest.sqlite`.
55
+ - Timing database: `run-timing.sqlite`.
56
+ - Matching SQLite `-wal` and `-shm` companions.
57
+
58
+ A manifest must select an included generation. An ingest database must have its
59
+ generation; sidecars must have their database and a complete WAL/SHM pair.
60
+ Missing/unexplained companions require investigation, not automatic repair.
61
+ Unknown entries, temporary manifests, rollback journals, nested directories,
62
+ special files, symbolic/hard links, foreign UID or writable-to-others state stop
63
+ the command. It never fixes owners or permissions on the original set. Canonical
64
+ paths must have no symbolic-link components. The filesystem root itself must be
65
+ owned by administrative UID 0. Every other ancestor must be controlled by root
66
+ or the retired account and not writable to unrelated accounts; the sole
67
+ writable-ancestor exception is a root-owned sticky directory such as `/tmp`,
68
+ whose selected child must still have trusted ownership. Private leaf directories
69
+ under an unsafe ancestor are not sufficient. Audit ACLs and mount/user-ID mappings
70
+ as well: POSIX mode checks do not certify those. Extra write grants to other
71
+ principals are unsupported; stop unless you can exclude them before invocation.
72
+
73
+ **Exclude all native Pi JSONL files, `run-history.jsonl`, current `agent-runs`,
74
+ legacy completion inputs and Intercom storage.** Current Intercom still owns
75
+ SQLite. Timing samples may not be reconstructible from another file. Do not open
76
+ original databases to identify schemas: SQLite can recover or create sidecars.
77
+ Inspect and recover a complete disposable copy instead.
78
+
79
+ ## Run the packaged route
80
+
81
+ The command supports Linux and macOS with Node at the package's supported floor.
82
+ It requires administrative native `lsof` visibility. Linux needs `/usr/bin/lsof`
83
+ and effective `CAP_SYS_PTRACE` for cross-account process inspection; root inside
84
+ a restricted container is not sufficient. macOS uses `/usr/sbin/lsof`. Missing
85
+ tools, warnings, incomplete inspection, interruption or unexpected results stop
86
+ the command. There is no unprivileged,
87
+ forced or best-effort bypass. This optional command does not change the extension's
88
+ host support or require `lsof` during normal runtime/install.
89
+
90
+ Use a reviewed, trusted package and Node executable when running with privilege.
91
+ Run from outside the selected source/container so your shell is not an open
92
+ reader of the directory. Create private record and archive **parents** under trusted ancestors, owned by
93
+ the retired account, before invoking the command. Use normalized canonical paths
94
+ without trailing slashes. Select a new, unique container name and external record
95
+ file, both outside the recovery set; the record must also be outside the archive
96
+ container. Keep the record and all command results private. Do not upload caches,
97
+ record contents, names, paths or hashes to issues or PRs.
98
+
99
+ The following placeholders must be replaced locally after owner approval. These
100
+ are commands for the authorized maintenance window, not ordinary upgrade steps:
101
+
102
+ ```sh
103
+ # Set explicit canonical paths and the retired account's numeric UID.
104
+ NODE=/absolute/trusted/path/to/node
105
+ SCRIPT=/absolute/trusted/package/scripts/archive-retired-history.mjs
106
+ SOURCE=/canonical/agent/history-index
107
+ CONTAINER=/private/archive-parent/unique-container
108
+ RECORD=/private/record-parent/unique-inventory.json
109
+ OWNER_UID=1234
110
+
111
+ # Phase 1: inspect, reserve the container exclusively, and write the external plan.
112
+ sudo "$NODE" "$SCRIPT" prepare --source "$SOURCE" --archive "$CONTAINER" \
113
+ --record "$RECORD" --owner-uid "$OWNER_UID" --quiesced
114
+
115
+ # Read-only reconciliation before the move or any retry.
116
+ sudo "$NODE" "$SCRIPT" inspect --record "$RECORD" --owner-uid "$OWNER_UID"
117
+
118
+ # Phase 2: recheck the plan and handles, then rename the complete directory once.
119
+ sudo "$NODE" "$SCRIPT" archive --record "$RECORD" \
120
+ --owner-uid "$OWNER_UID" --quiesced
121
+ ```
122
+
123
+ `prepare` creates a new `0700` container with exclusive `mkdir` and an external
124
+ `0600` record with exclusive no-follow creation. It records source, parent and
125
+ container identities plus every member's device/inode, ownership, mode, link
126
+ count, size, modification/change timestamps and SHA-256. Raw hash reads can update
127
+ access time; they do not recover SQLite or change file contents. All generations
128
+ and available companions stay together. The command does not alter the record afterward; it is not
129
+ OS-enforced immutable storage and is **not** a cleanup-success marker. An
130
+ interruption before record publication can leave an unrecorded empty container;
131
+ stop and investigate rather than adopt or remove it automatically.
132
+
133
+ `archive` checks the real state, identities, entire inventory and open handles
134
+ again. It uses a native same-filesystem directory rename, with **no** copy/delete
135
+ fallback. The exclusively reserved private container must have no unexpected
136
+ contents or existing destination. This is not an atomic no-clobber guarantee
137
+ against a competing root/same-account process; the enforced quiescent boundary
138
+ remains mandatory. After the move, the command checks the archive against the
139
+ record. Any failure preserves the observed locations and record for investigation;
140
+ it does not roll back automatically, merge trees, or conceal ambiguity. Directory
141
+ and record sync calls do not establish a tested power-loss durability guarantee.
142
+
143
+ Public failure output is a controlled refusal ID, not a dump of paths, hashes,
144
+ JSON or `lsof` output. `E_OPEN_HANDLES` means a reader/writer is still observable;
145
+ `E_LSOF_*` means inspection could not be accepted. `E_PROCESS_VISIBILITY` means
146
+ Linux cannot establish the required administrative inspection capability. `E_UNKNOWN_MEMBER`,
147
+ `E_MEMBER_TYPE`, `E_FOREIGN_OWNER`, `E_UNSAFE_MODE` and `E_ANCESTOR_*` require
148
+ provenance/path investigation. `E_CROSS_DEVICE` prohibits the move.
149
+ `E_INVENTORY_CHANGED`, identity-change IDs and `E_AMBIGUOUS_PATHS` mean stop and
150
+ preserve both trees. `E_INSPECTION_FAILED` covers other filesystem/JSON failures;
151
+ inspect the private inputs locally rather than posting their contents.
152
+
153
+ ## Reconcile interruption and rerun
154
+
155
+ Run `inspect` against the **same external record** before retrying. Do not infer
156
+ state from a prior console line or create a replacement record to bypass drift.
157
+
158
+ - Source has the recorded directory/inventory; archive child is absent and its
159
+ reserved container is unchanged: **prepared**, no move yet. Re-establish all
160
+ prerequisites before `archive`. Repeating matching `prepare` also inspects it.
161
+ - Source is absent; the archive child has the recorded identity/inventory:
162
+ **archived**. Repeating `archive` verifies this state without moving anything.
163
+ - Both or neither paths exist, an identity/member changed, the record is unsafe,
164
+ or an unrecorded destination already exists: **stop**. Preserve both trees and
165
+ the record. A recreated source can belong to a relaunched old release; do not
166
+ merge it, overwrite either location, or call it another stale cache.
167
+
168
+ Under the same offline boundary, recover **only a complete disposable copy** of
169
+ the archive with a compatible SQLite implementation. Run `PRAGMA integrity_check`
170
+ and inspect expected committed data on that copy. Do not use `immutable=1` to
171
+ bypass WAL recovery. Recovery can change its copy; retained archive bytes must
172
+ still match the external record. Historical generation schema `user_version=5`
173
+ and `meta`, and timing tables `timing_meta`/`samples`, corroborate format only.
174
+
175
+ For rollback, keep quiescence, verify the complete recorded archive again, and
176
+ restore the whole recovery set **only when the original path is absent**. Never
177
+ overwrite a recreated source or restore individual sidecars. Restoring the data
178
+ does not restore a package version or authorize restarting a live application.
179
+
180
+ ## Later space reclamation: a separate owner decision
181
+
182
+ The archive command never purges and never reports disk cleanup completed.
183
+ A successful same-filesystem move frees no file allocations and can consume
184
+ additional metadata space. Do not purge as part of an upgrade or this procedure.
185
+
186
+ 1. Keep the verified archive for the owner's retention/rollback period. Obtain
187
+ approval for a private, encrypted **off-device** backup destination. Record its
188
+ physical device/storage identity, not just another directory or partition on
189
+ the source disk. Use the storage system's documented durable backup procedure
190
+ to copy the **whole container**, preserving basenames/companions, and retain
191
+ the external inventory separately. Do not recover the retained archive.
192
+ 2. Test a restore into a new, exclusively reserved private **disposable** directory.
193
+ Compare the exact flat member-name set, file sizes and per-file SHA-256 against
194
+ `members` in the external JSON record; reject extra files, links or missing
195
+ members. Native `sha256sum -c` on Linux or `shasum -a 256 -c` on macOS can check
196
+ a private checksum list made from each `sha256` and `name`. A checksum pass
197
+ alone does not detect extra members, so compare the name set separately.
198
+ Recover that complete restored copy and check integrity/expected committed
199
+ contents. Record the verified backup object identity/checksum and successful
200
+ restore checks. Merely copying a file or seeing it on another volume is not
201
+ backup verification.
202
+ 3. Ask the owner for a **new purge authorization** naming only the recorded
203
+ canonical archive container, its filesystem/volume identity, device/inode,
204
+ owner/mode, the external record's checksum and the verified backup identity.
205
+ Explicitly exclude the original `history-index` path, all journals/current
206
+ state, other archives, the external record and the backup. Approval for the
207
+ earlier archive or backup is not purge approval.
208
+ 4. During a separately enforced maintenance window, run the command's `inspect`
209
+ again. Compare the actual container, parents and members with the original
210
+ record, and corroborate the source-volume identity with the OS storage tool.
211
+ Reject symlink/mount substitutions, new source data or any ambiguity. The
212
+ separately approved purge tool/operator must recheck that **exact container**
213
+ immediately before deletion and must not follow links, cross mounts, expand a
214
+ wildcard or fall back to deleting the original path. This archive-only command
215
+ intentionally supplies no purge action or original-path deletion command.
216
+ 5. Record allocation **before** that authorized purge using `du -sk` on the exact
217
+ container and `df -k` on its trusted parent. These report allocated KiB and
218
+ filesystem capacity, not logical database lengths. After the authorized tool
219
+ has removed only that container, verify that identity is gone, the backup and
220
+ excluded paths remain, and repeat `df -k` on the same filesystem. Preserve the
221
+ records. Snapshots, clones, compression, open handles, background writes and
222
+ delayed filesystem reclamation can make free-space changes differ from `du`;
223
+ report actual observations rather than claiming the archive size was freed.
224
+
225
+ No backup, purge, real-data inspection, install or restart is run by repository
226
+ verification. Synthetic command tests exercise these archive/retry guards and
227
+ recover disposable copies; they do not certify operator quiescence, private-data
228
+ provenance, off-device durability or permission to purge.
229
+
230
+ ## Historical source inventory
231
+
232
+ All 16 public tags from v0.44.0 through v0.49.1 retain generation, ingest and timing
233
+ owners. The earliest tagged owner is
234
+ [`v0.44.0` at `4f288484`](https://github.com/fitchmultz/pi-subagents/tree/4f288484d98488286fe137604c45ed49ac6b995d):
235
+ [database](https://github.com/fitchmultz/pi-subagents/blob/4f288484d98488286fe137604c45ed49ac6b995d/src/history/database.ts#L6-L28),
236
+ [manifest/generation/ingest store](https://github.com/fitchmultz/pi-subagents/blob/4f288484d98488286fe137604c45ed49ac6b995d/src/history/store.ts#L43-L78),
237
+ and [timing writer](https://github.com/fitchmultz/pi-subagents/blob/4f288484d98488286fe137604c45ed49ac6b995d/src/runs/shared/run-history.ts#L58-L80).
238
+ The last is
239
+ [`v0.49.1` at `f757ed05`](https://github.com/fitchmultz/pi-subagents/tree/f757ed054e7d819fd590a3da346be8a03aefe2d4):
240
+ [generation](https://github.com/fitchmultz/pi-subagents/blob/f757ed054e7d819fd590a3da346be8a03aefe2d4/src/history/database-generation.ts#L52-L80),
241
+ [ingest](https://github.com/fitchmultz/pi-subagents/blob/f757ed054e7d819fd590a3da346be8a03aefe2d4/src/history/store.ts#L275-L299),
242
+ [detached timing writer](https://github.com/fitchmultz/pi-subagents/blob/f757ed054e7d819fd590a3da346be8a03aefe2d4/src/runs/background/runner-step.ts#L143-L149),
243
+ and [history worker](https://github.com/fitchmultz/pi-subagents/blob/f757ed054e7d819fd590a3da346be8a03aefe2d4/src/history/index.ts#L112-L127).
244
+ These old owners do not honor a new maintenance marker.
245
+
246
+ The earlier execution sample remains specifically **v0.48.0 at
247
+ `d4caa1351c6dbc181276860123b5b00c969de56b`**, not a runtime test of all releases.
248
+ The maintained main baseline for this source candidate is
249
+ `64f7e91d278006622dd2d7f12fc81271a1caffb2`;
250
+ its runtime/startup/install paths have no retired history owner. The original
251
+ measurements remain bound to admitted baseline `4906e67e3a6701a753df3af112304dea2246aeb1`
252
+ and original PR head, not relabeled as measurements after integration.
package/package.json CHANGED
@@ -1,9 +1,19 @@
1
1
  {
2
2
  "name": "@fitchmultz/pi-subagents",
3
- "version": "0.50.0",
3
+ "version": "0.52.0",
4
4
  "description": "Pi extension for subagent orchestration and local session coordination",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
+ "exports": {
8
+ "./intercom-client": {
9
+ "types": {
10
+ "development": "./src/intercom-client.ts",
11
+ "default": "./dist/intercom-client.d.ts"
12
+ },
13
+ "import": "./dist/intercom-client.js"
14
+ },
15
+ "./package.json": "./package.json"
16
+ },
7
17
  "repository": {
8
18
  "type": "git",
9
19
  "url": "git+https://github.com/fitchmultz/pi-subagents.git"
@@ -27,6 +37,7 @@
27
37
  "dist",
28
38
  "src/**/*.ts",
29
39
  "tsconfig.build.json",
40
+ "tsconfig.client.json",
30
41
  "tsconfig.json",
31
42
  "scripts/",
32
43
  "skills/**/*",
@@ -36,6 +47,7 @@
36
47
  "docs/intercom.md",
37
48
  "docs/intercom-bridge.md",
38
49
  "docs/performance.md",
50
+ "docs/retired-history-maintenance.md",
39
51
  "README.md",
40
52
  "CHANGELOG.md"
41
53
  ],
@@ -47,7 +59,7 @@
47
59
  "test:integration": "node scripts/run-tests.mjs integration",
48
60
  "test:all": "node scripts/run-tests.mjs all",
49
61
  "check:compat": "node scripts/check-compat.mjs",
50
- "typecheck": "node node_modules/typescript/bin/tsc --noEmit",
62
+ "typecheck": "node scripts/typescript.mjs --noEmit",
51
63
  "smoke:package": "node scripts/package-smoke.mjs",
52
64
  "smoke:install": "node scripts/local-install-smoke.mjs",
53
65
  "check:size": "node scripts/measure-size.mjs",
@@ -91,18 +103,18 @@
91
103
  }
92
104
  },
93
105
  "devDependencies": {
94
- "@earendil-works/pi-agent-core": "1.3.0",
95
- "@earendil-works/pi-ai": "1.3.0",
96
- "@earendil-works/pi-coding-agent": "1.3.0",
97
- "@earendil-works/pi-tui": "1.3.0",
98
- "@types/node": "24.19.1",
99
- "@types/sinon": "22.0.0",
100
- "oxfmt": "0.72.0",
101
- "oxlint": "1.87.0",
102
- "oxlint-tsgolint": "7.0.2003",
106
+ "@earendil-works/pi-agent-core": "1.4.0",
107
+ "@earendil-works/pi-ai": "1.4.0",
108
+ "@earendil-works/pi-coding-agent": "1.4.0",
109
+ "@earendil-works/pi-tui": "1.4.0",
110
+ "@types/node": "24.19.2",
111
+ "@types/sinon": "22.0.2",
112
+ "oxfmt": "0.74.0",
113
+ "oxlint": "1.89.0",
114
+ "oxlint-tsgolint": "7.0.2005",
103
115
  "sinon": "22.1.0",
104
116
  "type-fest": "5.10.0",
105
- "typebox": "1.3.34",
117
+ "typebox": "1.4.3",
106
118
  "typescript": "7.0.2"
107
119
  },
108
120
  "publishConfig": {
@@ -111,8 +123,9 @@
111
123
  "engines": {
112
124
  "node": ">=24.21.0"
113
125
  },
114
- "dependencies": {
115
- "stream-chain": "4.2.6",
116
- "stream-json": "3.7.0"
126
+ "overrides": {
127
+ "@earendil-works/pi-coding-agent": {
128
+ "undici": "8.12.3"
129
+ }
117
130
  }
118
131
  }