@pi-unipi/unipi 3.0.0-alpha.2 → 3.0.0-alpha.21

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 (359) hide show
  1. package/CHANGELOG.md +292 -0
  2. package/README.md +8 -11
  3. package/docs/prefix-cache-architecture.md +14 -0
  4. package/package.json +22 -24
  5. package/packages/ask-user/README.md +55 -107
  6. package/packages/ask-user/ask-ui.ts +306 -732
  7. package/packages/ask-user/clipboard.ts +50 -0
  8. package/packages/ask-user/config.ts +74 -78
  9. package/packages/ask-user/package.json +2 -2
  10. package/packages/ask-user/questions.ts +151 -0
  11. package/packages/ask-user/skills/ask-user/SKILL.md +59 -133
  12. package/packages/ask-user/tools.ts +198 -432
  13. package/packages/autocomplete/package.json +1 -1
  14. package/packages/autocomplete/src/constants.ts +38 -43
  15. package/packages/autocomplete/src/sorting.ts +8 -0
  16. package/packages/background-tasks/package.json +2 -2
  17. package/packages/background-tasks/src/config.ts +0 -73
  18. package/packages/background-tasks/src/index.ts +20 -16
  19. package/packages/background-tasks/src/registry.ts +1 -171
  20. package/packages/background-tasks/src/types.ts +0 -32
  21. package/packages/btw/README.md +13 -56
  22. package/packages/btw/extensions/btw.ts +403 -1796
  23. package/packages/btw/package.json +5 -2
  24. package/packages/btw/skills/btw/SKILL.md +7 -143
  25. package/packages/compactor/README.md +56 -287
  26. package/packages/compactor/package.json +5 -9
  27. package/packages/compactor/skills/compactor/SKILL.md +13 -20
  28. package/packages/compactor/src/card.ts +90 -0
  29. package/packages/compactor/src/commands/index.ts +73 -130
  30. package/packages/compactor/src/compaction/compact-args.ts +5 -3
  31. package/packages/compactor/src/compaction/filter-noise.ts +1 -1
  32. package/packages/compactor/src/compaction/hooks.ts +487 -311
  33. package/packages/compactor/src/compaction/source.ts +178 -0
  34. package/packages/compactor/src/compaction/summarize.ts +586 -52
  35. package/packages/compactor/src/config/manager.ts +130 -127
  36. package/packages/compactor/src/config/schema.ts +32 -48
  37. package/packages/compactor/src/index.ts +40 -431
  38. package/packages/compactor/src/info-screen.ts +44 -190
  39. package/packages/compactor/src/session/recall-blocks.ts +1 -1
  40. package/packages/compactor/src/stats.ts +76 -0
  41. package/packages/compactor/src/tools/context-budget.ts +17 -34
  42. package/packages/compactor/src/tools/ctx-doctor.ts +45 -36
  43. package/packages/compactor/src/tools/register.ts +29 -269
  44. package/packages/compactor/src/types.ts +47 -186
  45. package/packages/core/README.md +25 -0
  46. package/packages/core/child-agent.ts +1 -0
  47. package/packages/core/command-echo.ts +63 -0
  48. package/packages/core/compaction-context.ts +41 -0
  49. package/packages/core/constants.ts +8 -38
  50. package/packages/core/events.ts +0 -53
  51. package/packages/core/fusion-status.ts +4 -0
  52. package/packages/core/global.d.ts +10 -0
  53. package/packages/core/harness-messages.ts +392 -0
  54. package/packages/core/index.ts +14 -0
  55. package/packages/core/kanboard-status.ts +49 -0
  56. package/packages/core/long-horizon-owner-status.ts +46 -0
  57. package/packages/core/model-cache.ts +81 -7
  58. package/packages/core/package.json +6 -3
  59. package/packages/core/src/attach/detect.ts +163 -0
  60. package/packages/core/src/child-agent/child-args.ts +31 -0
  61. package/packages/core/src/child-agent/delivery.ts +41 -0
  62. package/packages/core/src/child-agent/index.ts +5 -0
  63. package/packages/core/src/child-agent/readers.ts +203 -0
  64. package/packages/core/src/child-agent/runtime.ts +584 -0
  65. package/packages/core/src/evidence.ts +78 -0
  66. package/packages/core/src/hints/browser.ts +278 -0
  67. package/packages/core/src/hints/crab-data.ts +19 -0
  68. package/packages/core/src/hints/index.ts +865 -0
  69. package/packages/core/src/hints/lines.ts +607 -0
  70. package/packages/core/src/hints/store.ts +120 -0
  71. package/packages/core/src/jev/chatter.ts +24 -0
  72. package/packages/core/src/jev/settings.ts +177 -14
  73. package/packages/core/src/package-colors.ts +2 -0
  74. package/packages/core/src/settings/catalog.ts +39 -15
  75. package/packages/core/src/settings/engine.ts +48 -2
  76. package/packages/core/src/settings/hub.ts +83 -20
  77. package/packages/core/src/settings/open.ts +49 -0
  78. package/packages/core/src/settings/schema.ts +27 -7
  79. package/packages/core/src/tui/hub-kit.ts +18 -5
  80. package/packages/core/src/tui/kit.ts +198 -0
  81. package/packages/core/src/tui/progress.ts +73 -0
  82. package/packages/core/src/turn/arbiter.ts +290 -0
  83. package/packages/footer/README.md +4 -6
  84. package/packages/footer/package.json +3 -3
  85. package/packages/footer/src/glance-editor.ts +19 -11
  86. package/packages/footer/src/help.ts +1 -1
  87. package/packages/footer/src/index.ts +36 -3
  88. package/packages/footer/src/presets.ts +3 -4
  89. package/packages/footer/src/rendering/icons.ts +0 -20
  90. package/packages/footer/src/segments/compactor.ts +52 -142
  91. package/packages/footer/src/segments/status-ext.ts +0 -1
  92. package/packages/fusion/README.md +14 -4
  93. package/packages/fusion/package.json +4 -3
  94. package/packages/fusion/src/child-args.ts +1 -0
  95. package/packages/fusion/src/index.ts +161 -8
  96. package/packages/fusion/src/pi-settings.ts +57 -0
  97. package/packages/fusion/src/picker.ts +186 -51
  98. package/packages/fusion/src/preset.ts +56 -2
  99. package/packages/fusion/src/prompts.ts +1 -1
  100. package/packages/fusion/src/sidekick-runtime.ts +1 -420
  101. package/packages/fusion/src/tools.ts +107 -128
  102. package/packages/fusion/src/transcript.ts +27 -85
  103. package/packages/info-screen/package.json +2 -2
  104. package/packages/input-shortcuts/README.md +18 -30
  105. package/packages/input-shortcuts/package.json +2 -2
  106. package/packages/input-shortcuts/src/burst.ts +88 -0
  107. package/packages/input-shortcuts/src/chord-overlay.ts +20 -70
  108. package/packages/input-shortcuts/src/cleared-input.ts +97 -0
  109. package/packages/input-shortcuts/src/index.ts +99 -193
  110. package/packages/input-shortcuts/src/last-response.ts +40 -0
  111. package/packages/input-shortcuts/src/registers.ts +8 -23
  112. package/packages/input-shortcuts/src/types.ts +4 -16
  113. package/packages/input-shortcuts/src/undo-redo.ts +59 -47
  114. package/packages/kanboard/README.md +51 -47
  115. package/packages/kanboard/index.ts +293 -55
  116. package/packages/kanboard/package.json +8 -8
  117. package/packages/kanboard/skills/kanboard/SKILL.md +61 -27
  118. package/packages/kanboard/src/badges.ts +93 -0
  119. package/packages/kanboard/src/bin.ts +4 -2
  120. package/packages/kanboard/src/commands.ts +218 -84
  121. package/packages/kanboard/src/debug.ts +20 -0
  122. package/packages/kanboard/src/guard.ts +267 -109
  123. package/packages/kanboard/src/monitor.ts +330 -0
  124. package/packages/kanboard/src/notice-buffer.ts +74 -0
  125. package/packages/kanboard/src/progress.ts +27 -0
  126. package/packages/kanboard/src/reminders.ts +234 -0
  127. package/packages/kanboard/src/settings.ts +69 -15
  128. package/packages/kanboard/src/shapes.ts +5 -0
  129. package/packages/long-horizon/index.ts +190 -13
  130. package/packages/long-horizon/package.json +3 -3
  131. package/packages/long-horizon/src/commands.ts +103 -7
  132. package/packages/long-horizon/src/compaction-brief.ts +35 -0
  133. package/packages/long-horizon/src/engine/continuation.ts +84 -15
  134. package/packages/long-horizon/src/engine/goal-state.ts +64 -13
  135. package/packages/long-horizon/src/engine/nudge-stash.ts +71 -0
  136. package/packages/long-horizon/src/engine/ralph.ts +3 -2
  137. package/packages/long-horizon/src/engine/runaway.ts +68 -11
  138. package/packages/long-horizon/src/engine/verifier.ts +19 -0
  139. package/packages/long-horizon/src/gate.ts +37 -12
  140. package/packages/long-horizon/src/judge/resolve.ts +1 -0
  141. package/packages/long-horizon/src/owner.ts +35 -2
  142. package/packages/long-horizon/src/progress.ts +87 -0
  143. package/packages/long-horizon/src/prompts/goal.ts +1 -1
  144. package/packages/long-horizon/src/runtime.ts +172 -18
  145. package/packages/long-horizon/src/settings.ts +54 -39
  146. package/packages/long-horizon/src/tools/goal.ts +6 -0
  147. package/packages/long-horizon/src/tools/graph.ts +1 -1
  148. package/packages/long-horizon/src/tools/swarm.ts +3 -3
  149. package/packages/mcp/package.json +2 -2
  150. package/packages/memory/commands.ts +7 -2
  151. package/packages/memory/index.ts +210 -32
  152. package/packages/memory/package.json +3 -3
  153. package/packages/memory/save-session.ts +264 -0
  154. package/packages/memory/settings.ts +17 -0
  155. package/packages/memory/tools.ts +253 -203
  156. package/packages/notify/package.json +2 -2
  157. package/packages/skill-registry/README.md +53 -16
  158. package/packages/skill-registry/index.ts +230 -0
  159. package/packages/skill-registry/package.json +19 -8
  160. package/packages/skill-registry/src/editor.ts +206 -0
  161. package/packages/skill-registry/src/judge.ts +289 -0
  162. package/packages/skill-registry/src/registry.ts +87 -0
  163. package/packages/skill-registry/src/settings.ts +259 -0
  164. package/packages/skill-registry/src/vault.ts +80 -0
  165. package/packages/subagents/README.md +39 -119
  166. package/packages/subagents/package.json +6 -13
  167. package/packages/subagents/skills/subagents/SKILL.md +18 -42
  168. package/packages/subagents/src/agents.ts +213 -0
  169. package/packages/subagents/src/cards.ts +182 -0
  170. package/packages/subagents/src/index.ts +609 -1084
  171. package/packages/subagents/src/manager.ts +429 -0
  172. package/packages/subagents/src/profiles.ts +130 -0
  173. package/packages/subagents/src/transcript.ts +154 -0
  174. package/packages/subagents/src/ui.ts +389 -0
  175. package/packages/unipi/bundled.js +39756 -45569
  176. package/packages/unipi/index.ts +55 -10
  177. package/packages/updater/package.json +2 -2
  178. package/packages/utility/README.md +76 -69
  179. package/packages/utility/package.json +2 -2
  180. package/packages/utility/skills/image/SKILL.md +37 -0
  181. package/packages/utility/skills/utility/SKILL.md +17 -60
  182. package/packages/utility/src/answer/extract.ts +128 -0
  183. package/packages/utility/src/answer/index.ts +213 -0
  184. package/packages/utility/src/answer/reply.ts +133 -0
  185. package/packages/utility/src/answer/web.ts +167 -0
  186. package/packages/utility/src/attach/index.ts +205 -0
  187. package/packages/utility/src/commands.ts +38 -142
  188. package/packages/utility/src/diagnostics/engine.ts +50 -5
  189. package/packages/utility/src/image/index.ts +33 -0
  190. package/packages/utility/src/image/models.ts +221 -0
  191. package/packages/{image/src → utility/src/image}/recognize.ts +2 -2
  192. package/packages/utility/src/image/settings.ts +178 -0
  193. package/packages/{image/src/image-source.ts → utility/src/image/source.ts} +1 -1
  194. package/packages/utility/src/image/tools.ts +168 -0
  195. package/packages/utility/src/image/transports.ts +186 -0
  196. package/packages/utility/src/index.ts +68 -389
  197. package/packages/utility/src/lifecycle/cleanup.ts +116 -311
  198. package/packages/utility/src/rename/gate.ts +102 -0
  199. package/packages/utility/src/rename/index.ts +191 -0
  200. package/packages/utility/src/rename/session.ts +170 -0
  201. package/packages/utility/src/render/delegated.ts +285 -0
  202. package/packages/utility/src/render/harness.ts +683 -0
  203. package/packages/utility/src/render/parse.ts +156 -0
  204. package/packages/utility/src/render/pi-settings.ts +38 -0
  205. package/packages/utility/src/render/reply-bg.ts +307 -0
  206. package/packages/utility/src/render/simple.ts +724 -0
  207. package/packages/utility/src/render/spacing.ts +235 -0
  208. package/packages/utility/src/render/styled.ts +251 -0
  209. package/packages/utility/src/render/tools.ts +353 -0
  210. package/packages/utility/src/settings.ts +166 -233
  211. package/packages/utility/src/types.ts +1 -222
  212. package/packages/watchdog/index.ts +17 -6
  213. package/packages/watchdog/package.json +3 -3
  214. package/packages/watchdog/src/config.ts +3 -2
  215. package/packages/web-api/package.json +2 -2
  216. package/packages/workflow/README.md +3 -0
  217. package/packages/workflow/index.ts +0 -2
  218. package/packages/workflow/package.json +2 -2
  219. package/packages/workflow/src/permission/decide.ts +6 -4
  220. package/packages/workflow/src/permission/index.ts +17 -7
  221. package/packages/workflow/src/permission/settings.ts +39 -4
  222. package/packages/workflow/src/plan/index.ts +19 -4
  223. package/packages/background-tasks/extensions/delegate-child.ts +0 -1
  224. package/packages/background-tasks/src/context-parent-snapshot.ts +0 -142
  225. package/packages/background-tasks/src/context-token-budget.ts +0 -903
  226. package/packages/background-tasks/src/context-visible-conversation-v2.ts +0 -551
  227. package/packages/background-tasks/src/delegate/artifacts.ts +0 -487
  228. package/packages/background-tasks/src/delegate/budget.ts +0 -415
  229. package/packages/background-tasks/src/delegate/hook-contract-evidence.json +0 -18
  230. package/packages/background-tasks/src/delegate/hook-contract.ts +0 -154
  231. package/packages/background-tasks/src/delegate/launch.ts +0 -484
  232. package/packages/background-tasks/src/delegate/result-package.ts +0 -459
  233. package/packages/background-tasks/src/delegate/runner.ts +0 -430
  234. package/packages/background-tasks/src/delegate/seed.ts +0 -423
  235. package/packages/background-tasks/src/delegate/types.ts +0 -323
  236. package/packages/background-tasks/src/delegate-child-extension.ts +0 -978
  237. package/packages/background-tasks/src/delegate-extension.ts +0 -654
  238. package/packages/background-tasks/src/fixtures/delegate-context-incident.json +0 -17
  239. package/packages/compactor/skills/compactor-detail/SKILL.md +0 -116
  240. package/packages/compactor/skills/compactor-doctor/SKILL.md +0 -71
  241. package/packages/compactor/skills/compactor-stats/SKILL.md +0 -51
  242. package/packages/compactor/src/compaction/build-sections.ts +0 -76
  243. package/packages/compactor/src/compaction/extract/goals.ts +0 -36
  244. package/packages/compactor/src/compaction/extract/preferences.ts +0 -40
  245. package/packages/compactor/src/compaction/format.ts +0 -53
  246. package/packages/compactor/src/compaction/merge.ts +0 -143
  247. package/packages/compactor/src/config/presets.ts +0 -160
  248. package/packages/compactor/src/executor/executor.ts +0 -233
  249. package/packages/compactor/src/executor/runtime.ts +0 -115
  250. package/packages/compactor/src/security/evaluator.ts +0 -89
  251. package/packages/compactor/src/security/policy.ts +0 -96
  252. package/packages/compactor/src/security/scanner.ts +0 -65
  253. package/packages/compactor/src/session/auto-inject.ts +0 -60
  254. package/packages/compactor/src/session/db.ts +0 -325
  255. package/packages/compactor/src/session/extract.ts +0 -144
  256. package/packages/compactor/src/session/resume-inject.ts +0 -64
  257. package/packages/compactor/src/session/snapshot.ts +0 -327
  258. package/packages/compactor/src/tools/ctx-batch-execute.ts +0 -41
  259. package/packages/compactor/src/tools/ctx-execute-file.ts +0 -23
  260. package/packages/compactor/src/tools/ctx-execute.ts +0 -23
  261. package/packages/compactor/src/tools/ctx-stats.ts +0 -96
  262. package/packages/image/README.md +0 -99
  263. package/packages/image/package.json +0 -61
  264. package/packages/image/skills/image/SKILL.md +0 -76
  265. package/packages/image/src/generate.ts +0 -256
  266. package/packages/image/src/index.ts +0 -135
  267. package/packages/image/src/models.ts +0 -519
  268. package/packages/image/src/openai-images-api.ts +0 -282
  269. package/packages/image/src/register-providers.ts +0 -209
  270. package/packages/image/src/settings.ts +0 -173
  271. package/packages/image/src/tools.ts +0 -326
  272. package/packages/input-shortcuts/src/clipboard.ts +0 -119
  273. package/packages/kanboard/src/runner.ts +0 -817
  274. package/packages/skill-registry/skills/auto/SKILL.md +0 -465
  275. package/packages/skill-registry/skills/brainstorm/SKILL.md +0 -219
  276. package/packages/skill-registry/skills/chore-create/SKILL.md +0 -315
  277. package/packages/skill-registry/skills/chore-execute/SKILL.md +0 -314
  278. package/packages/skill-registry/skills/consolidate/SKILL.md +0 -142
  279. package/packages/skill-registry/skills/consultant/SKILL.md +0 -97
  280. package/packages/skill-registry/skills/debug/SKILL.md +0 -228
  281. package/packages/skill-registry/skills/document/SKILL.md +0 -120
  282. package/packages/skill-registry/skills/fix/SKILL.md +0 -212
  283. package/packages/skill-registry/skills/gather-context/SKILL.md +0 -145
  284. package/packages/skill-registry/skills/plan/SKILL.md +0 -231
  285. package/packages/skill-registry/skills/quick-fix/SKILL.md +0 -135
  286. package/packages/skill-registry/skills/quick-work/SKILL.md +0 -110
  287. package/packages/skill-registry/skills/research/SKILL.md +0 -251
  288. package/packages/skill-registry/skills/review-work/SKILL.md +0 -187
  289. package/packages/skill-registry/skills/scan-issues/SKILL.md +0 -195
  290. package/packages/skill-registry/skills/work/SKILL.md +0 -218
  291. package/packages/skill-registry/skills/worktree-create/SKILL.md +0 -69
  292. package/packages/skill-registry/skills/worktree-list/SKILL.md +0 -78
  293. package/packages/skill-registry/skills/worktree-merge/SKILL.md +0 -89
  294. package/packages/subagents/agents/delegate.md +0 -14
  295. package/packages/subagents/agents/oracle.md +0 -78
  296. package/packages/subagents/agents/researcher.md +0 -52
  297. package/packages/subagents/agents/reviewer.md +0 -80
  298. package/packages/subagents/agents/scout.md +0 -50
  299. package/packages/subagents/agents/worker.md +0 -59
  300. package/packages/subagents/prompts/council.md +0 -48
  301. package/packages/subagents/prompts/gather-context-and-clarify.md +0 -13
  302. package/packages/subagents/prompts/parallel-cleanup.md +0 -59
  303. package/packages/subagents/prompts/parallel-research.md +0 -50
  304. package/packages/subagents/prompts/parallel-review.md +0 -54
  305. package/packages/subagents/prompts/review-loop.md +0 -43
  306. package/packages/subagents/skills/council-mode/SKILL.md +0 -230
  307. package/packages/subagents/skills/subagents/references/constraints-and-recipes.md +0 -259
  308. package/packages/subagents/skills/subagents/references/execution-controls.md +0 -454
  309. package/packages/subagents/skills/subagents/references/management-authoring-rpc.md +0 -161
  310. package/packages/subagents/skills/subagents/references/multi-lane-orchestration.md +0 -39
  311. package/packages/subagents/skills/subagents/references/prompting-and-roles.md +0 -267
  312. package/packages/subagents/src/acceptance.ts +0 -331
  313. package/packages/subagents/src/agent-manager.ts +0 -405
  314. package/packages/subagents/src/agent-memory.ts +0 -300
  315. package/packages/subagents/src/agent-overrides.ts +0 -183
  316. package/packages/subagents/src/agent-runner.ts +0 -271
  317. package/packages/subagents/src/async-runner.ts +0 -422
  318. package/packages/subagents/src/authority-policy.ts +0 -47
  319. package/packages/subagents/src/budgets.ts +0 -285
  320. package/packages/subagents/src/child-safety.ts +0 -140
  321. package/packages/subagents/src/config.ts +0 -249
  322. package/packages/subagents/src/conversation-viewer.ts +0 -281
  323. package/packages/subagents/src/core-compat.ts +0 -94
  324. package/packages/subagents/src/custom-agents.ts +0 -343
  325. package/packages/subagents/src/file-system-retry.ts +0 -50
  326. package/packages/subagents/src/fleet-data.ts +0 -105
  327. package/packages/subagents/src/fleet-view.ts +0 -322
  328. package/packages/subagents/src/foreground-detach.ts +0 -59
  329. package/packages/subagents/src/fork-context.ts +0 -285
  330. package/packages/subagents/src/global.d.ts +0 -7
  331. package/packages/subagents/src/guide.ts +0 -129
  332. package/packages/subagents/src/mission-state.ts +0 -133
  333. package/packages/subagents/src/mission-store.ts +0 -436
  334. package/packages/subagents/src/output-limits.ts +0 -142
  335. package/packages/subagents/src/parity-types.ts +0 -315
  336. package/packages/subagents/src/pi-args.ts +0 -167
  337. package/packages/subagents/src/result-files.ts +0 -232
  338. package/packages/subagents/src/result-watcher.ts +0 -221
  339. package/packages/subagents/src/retained-children.ts +0 -176
  340. package/packages/subagents/src/run-fanout-budget.ts +0 -356
  341. package/packages/subagents/src/scheduled-runs.ts +0 -349
  342. package/packages/subagents/src/schemas.ts +0 -263
  343. package/packages/subagents/src/slash-commands.ts +0 -80
  344. package/packages/subagents/src/supervisor-channel.ts +0 -249
  345. package/packages/subagents/src/tool-handler.ts +0 -1144
  346. package/packages/subagents/src/types.ts +0 -238
  347. package/packages/subagents/src/widget.ts +0 -457
  348. package/packages/subagents/src/workflow-script.ts +0 -737
  349. package/packages/subagents/src/workflow-worker.ts +0 -384
  350. package/packages/subagents/src/worktree.ts +0 -614
  351. package/packages/utility/src/analytics/collector.ts +0 -293
  352. package/packages/utility/src/info-screen.ts +0 -80
  353. package/packages/utility/src/lifecycle/process.ts +0 -162
  354. package/packages/utility/src/prefix-cache.ts +0 -263
  355. package/packages/utility/src/skill-discovery.ts +0 -542
  356. package/packages/utility/src/tools/env.ts +0 -113
  357. package/packages/utility/src/tui/name-badge-state.ts +0 -315
  358. package/packages/utility/src/tui/name-badge.ts +0 -109
  359. /package/packages/{subagents/src → core/src/child-agent}/pi-spawn.ts +0 -0
@@ -1,201 +1,55 @@
1
- import { formatTokens } from "@pi-unipi/core";
2
1
  /**
3
- * Info-screen integration for @pi-unipi/compactor
4
- *
5
- * Stats driven by COMPACTION SAVINGS (the compactor's actual value),
6
- * not sandbox/index diversion bytes.
7
- *
8
- * Data sources (in priority order):
9
- * 1. Runtime counters (in-memory, current session only)
10
- * 2. DB compaction stats (total_chars_before/kept in session_meta)
11
- * 3. Session event counts (session_events table, always reliable)
2
+ * Info-screen Compactor tab: the settings in force and this session's
3
+ * compactions, read from the session branch.
12
4
  */
13
5
 
14
- import type { SessionDB } from "./session/db.js";
15
- import { getLastCompactionStats, formatCompactionStats } from "./compaction/hooks.js";
16
- import { parseUsageStatsAsync } from "@pi-unipi/info-screen/usage-parser.js";
17
- import type { RuntimeCounters } from "./types.js";
6
+ import { formatTokens } from "@pi-unipi/core";
7
+ import { loadConfig } from "./config/manager.js";
8
+ import { methodBreakdown, sessionCompactionStats } from "./stats.js";
9
+
10
+ type Stat = { value: string; detail: string };
18
11
 
19
12
  export interface CompactorInfoData {
20
- tokensSaved: { value: string; detail: string };
21
- costSaved: { value: string; detail: string };
22
- pctReduction: { value: string; detail: string };
23
- topTools: { value: string; detail: string };
24
- compactions: { value: string; detail: string };
25
- toolCalls: { value: string; detail: string };
13
+ settings: Stat;
14
+ compactions: Stat;
15
+ tokens: Stat;
16
+ last: Stat;
26
17
  }
27
18
 
28
- /** Format token count for display (e.g., "12.4k", "1.2M"). */
29
- /** Format cost for display (e.g., "$0.34", "<$0.01"). */
30
- function formatCost(n: number): string {
31
- if (n === 0) return "$0.00";
32
- if (n < 0.01) return "<$0.01";
33
- if (n < 1) return `$${n.toFixed(2)}`;
34
- return `$${n.toFixed(2)}`;
35
- }
19
+ const METHOD_NAME = { vcc: "lossless", llm: "model summary" } as const;
36
20
 
37
- /** Estimate cost per token for the most-used model in the current session. */
38
- async function estimateCostPerToken(): Promise<number | null> {
39
- try {
40
- const usage = await parseUsageStatsAsync();
41
- // Use today's most-used model if available, otherwise all-time
42
- const models = usage.byModelToday;
43
- const todayKeys = Object.keys(models);
44
- if (todayKeys.length > 0) {
45
- const topModel = todayKeys.reduce((a, b) => models[a].tokens > models[b].tokens ? a : b);
46
- const entry = models[topModel];
47
- if (entry.tokens > 0 && entry.cost > 0) {
48
- return entry.cost / entry.tokens;
49
- }
50
- }
51
- // Fall back to all-time model data
52
- const allKeys = Object.keys(usage.byModel);
53
- if (allKeys.length > 0) {
54
- const topModel = allKeys.reduce((a, b) => usage.byModel[a].tokens > usage.byModel[b].tokens ? a : b);
55
- const entry = usage.byModel[topModel];
56
- if (entry.tokens > 0 && entry.cost > 0) {
57
- return entry.cost / entry.tokens;
58
- }
59
- }
60
- return null;
61
- } catch {
62
- return null;
63
- }
21
+ function ago(ms: number): string {
22
+ const s = Math.max(0, Math.round(ms / 1000));
23
+ if (s < 60) return `${s}s ago`;
24
+ if (s < 3600) return `${Math.round(s / 60)}m ago`;
25
+ if (s < 86400) return `${Math.round(s / 3600)}h ago`;
26
+ return `${Math.round(s / 86400)}d ago`;
64
27
  }
65
28
 
66
- export async function getInfoScreenData(
67
- sessionDB: SessionDB,
68
- sessionId: string,
69
- counters?: RuntimeCounters,
70
- ): Promise<CompactorInfoData> {
71
- try {
72
- // ── Compaction savings (the compactor's actual value) ──
73
- // Priority: in-memory counter → DB per-session stats → DB all-time stats
74
- let tokensSaved = counters?.totalTokensCompacted ?? 0;
75
- let charsBefore = 0;
76
- let charsKept = 0;
77
-
78
- if (tokensSaved === 0) {
79
- // Try DB per-session stats
80
- const sessionStats = sessionDB.getSessionStats(sessionId);
81
- if (sessionStats) {
82
- charsBefore = (sessionStats as any).total_chars_before ?? 0;
83
- charsKept = (sessionStats as any).total_chars_kept ?? 0;
84
- tokensSaved = Math.round((charsBefore - charsKept) / 4);
85
- }
86
- }
87
-
88
- if (tokensSaved === 0) {
89
- // Try DB all-time stats
90
- const allTime = sessionDB.getAllTimeStats();
91
- charsBefore = allTime.allCharsBefore;
92
- charsKept = allTime.allCharsKept;
93
- tokensSaved = Math.round((charsBefore - charsKept) / 4);
94
- }
95
-
96
- // ── Compaction count ──
97
- let compactionCount = counters?.compactions ?? 0;
98
- if (compactionCount === 0) {
99
- const allTime = sessionDB.getAllTimeStats();
100
- compactionCount = allTime.allCompactions;
101
- }
102
-
103
- // ── Compression ratio / pct reduction ──
104
- let pctReduction = 0;
105
- if (charsBefore > 0) {
106
- pctReduction = Math.round((1 - charsKept / charsBefore) * 100);
107
- }
108
-
109
- // ── Tool call counts from session_events (always reliable) ──
110
- // The session_events table captures every tool_result event, so this
111
- // is an accurate count regardless of runtimeStats state.
112
- interface ToolCountRow { category: string; cnt: number }
113
- let toolCountRows: ToolCountRow[] = [];
114
- let totalToolCalls = 0;
115
- try {
116
- const db = sessionDB.getDb();
117
- if (db) {
118
- toolCountRows = db.prepare(
119
- "SELECT category, COUNT(*) as cnt FROM session_events WHERE session_id = ? GROUP BY category",
120
- ).all(sessionId) as ToolCountRow[];
121
- for (const row of toolCountRows) {
122
- totalToolCalls += row.cnt;
123
- }
124
- }
125
- } catch {
126
- // Non-fatal: DB query failed, show zero
127
- }
128
-
129
- // Build per-tool breakdown for display
130
- const toolBreakdown = toolCountRows.length > 0
131
- ? toolCountRows
132
- .sort((a, b) => b.cnt - a.cnt)
133
- .map(r => ` ${r.category.padEnd(20)} ${String(r.cnt).padStart(5)} events`)
134
- .join("\n")
135
- : "No tool calls yet";
136
-
137
- const topCategory = toolCountRows.length > 0
138
- ? toolCountRows.reduce((a, b) => a.cnt > b.cnt ? a : b)
139
- : null;
140
-
141
- const top5Detail = toolCountRows.length > 0
142
- ? toolCountRows
143
- .sort((a, b) => b.cnt - a.cnt)
144
- .slice(0, 5)
145
- .map(r => `${r.category}: ${r.cnt} events`)
146
- .join("\n")
147
- : "No tool calls yet";
148
-
149
- // ── Cost saved estimate ──
150
- const costPerToken = await estimateCostPerToken();
151
- const costSaved = costPerToken !== null ? tokensSaved * costPerToken : null;
152
-
153
- // ── Last compaction details ──
154
- const compactStats = getLastCompactionStats();
155
-
156
- return {
157
- tokensSaved: {
158
- value: formatTokens(tokensSaved),
159
- detail: toolBreakdown,
160
- },
161
- costSaved: {
162
- value: costSaved !== null ? formatCost(costSaved) : "N/A",
163
- detail: costSaved !== null
164
- ? `~${formatTokens(tokensSaved)} tokens × $${(costPerToken! * 1_000_000).toFixed(2)}/M tokens`
165
- : "Cost data unavailable for current model",
166
- },
167
- pctReduction: {
168
- value: `${pctReduction}%`,
169
- detail: charsBefore > 0
170
- ? `${formatTokens(Math.round(charsBefore / 4))} before → ${formatTokens(Math.round(charsKept / 4))} after compaction`
171
- : "No compaction data yet",
172
- },
173
- topTools: {
174
- value: topCategory ? `${topCategory.category}: ${topCategory.cnt}` : "N/A",
175
- detail: top5Detail,
176
- },
177
- compactions: {
178
- value: String(compactionCount),
179
- detail: compactStats
180
- ? `Last: ${formatCompactionStats(compactStats)}`
181
- : compactionCount > 0
182
- ? `${compactionCount} compaction(s) across all sessions`
183
- : "No compactions yet",
184
- },
185
- toolCalls: {
186
- value: String(totalToolCalls),
187
- detail: `${totalToolCalls} events across ${toolCountRows.length} categor${toolCountRows.length !== 1 ? "ies" : "y"}`,
188
- },
189
- };
190
- } catch {
191
- // Never throw from dataProvider — return zeroed stats
192
- return {
193
- tokensSaved: { value: "0", detail: "No data" },
194
- costSaved: { value: "N/A", detail: "No data" },
195
- pctReduction: { value: "0%", detail: "No data" },
196
- topTools: { value: "N/A", detail: "No data" },
197
- compactions: { value: "0", detail: "No data" },
198
- toolCalls: { value: "0", detail: "No data" },
199
- };
200
- }
29
+ export function getInfoScreenData(branch: readonly any[], cwd: string = process.cwd(), now = Date.now()): CompactorInfoData {
30
+ const config = loadConfig(cwd);
31
+ const stats = sessionCompactionStats(branch);
32
+ const count = stats.compactions.length;
33
+ const last = stats.compactions[count - 1];
34
+ const pct = stats.tokensBefore > 0 ? Math.round((stats.tokensSaved / stats.tokensBefore) * 100) : 0;
35
+ return {
36
+ settings: {
37
+ value: METHOD_NAME[config.method],
38
+ detail: `when: ${config.trigger === "pi" ? "Pi's context limit" : `${config.thresholdPercent}% of context`} · Pi's /compact: ${config.piCompact === "follow" ? "same as method" : METHOD_NAME[config.piCompact]}`,
39
+ },
40
+ compactions: {
41
+ value: String(count),
42
+ detail: count > 0 ? methodBreakdown(stats) : "none this session",
43
+ },
44
+ tokens: {
45
+ value: count > 0 ? `${formatTokens(stats.tokensBefore)} → ${formatTokens(stats.tokensAfter)}` : "—",
46
+ detail: count > 0 ? `−${formatTokens(stats.tokensSaved)} (${pct}%) across all compactions` : "nothing compacted yet",
47
+ },
48
+ last: {
49
+ value: last ? `${formatTokens(last.tokensBefore)} → ${formatTokens(last.tokensAfter)}` : "—",
50
+ detail: last
51
+ ? [METHOD_NAME[last.method], last.jevDropped ? `${last.jevDropped} stale dropped` : "", last.at ? ago(now - last.at) : ""].filter(Boolean).join(" · ")
52
+ : "no compaction yet",
53
+ },
54
+ };
201
55
  }
@@ -19,7 +19,7 @@ function block(kind: "user" | "assistant", text: string, sourceIndex?: number):
19
19
  return clean ? [{ kind, text: clean, sourceIndex }] : [];
20
20
  }
21
21
 
22
- function normalizeAgentMessage(message: AgentMessage, sourceIndex: number): NormalizedBlock[] {
22
+ export function normalizeAgentMessage(message: AgentMessage, sourceIndex: number): NormalizedBlock[] {
23
23
  const role = (message as { role?: string }).role;
24
24
 
25
25
  // Standard LLM message roles are handled by the existing normalizer.
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Compaction stats read straight from the session branch — no database.
3
+ *
4
+ * tokensAfter comes from our own compaction details when present; otherwise
5
+ * (Pi's summaries, older entries) it is estimated as summary + kept tail.
6
+ */
7
+
8
+ import { estimateMessageContentChars } from "./compaction/token-estimate.js";
9
+ import type { CompactionMethod } from "./types.js";
10
+
11
+ export interface CompactionRecord {
12
+ tokensBefore: number;
13
+ tokensAfter: number;
14
+ method: CompactionMethod;
15
+ /** Epoch ms, when the entry records it. */
16
+ at?: number;
17
+ /** Items jev dropped (jev method only). */
18
+ jevDropped?: number;
19
+ }
20
+
21
+ export interface SessionCompactionStats {
22
+ compactions: CompactionRecord[];
23
+ tokensSaved: number;
24
+ tokensBefore: number;
25
+ tokensAfter: number;
26
+ }
27
+
28
+ const CHARS_PER_TOKEN = 4;
29
+
30
+ function methodOf(details: any): CompactionMethod {
31
+ if (details?.compactor !== "@pi-unipi/compactor") return "llm";
32
+ return details.method === "llm" ? "llm" : "vcc";
33
+ }
34
+
35
+ export function sessionCompactionStats(branch: readonly any[]): SessionCompactionStats {
36
+ const compactions: CompactionRecord[] = [];
37
+ for (let i = 0; i < branch.length; i++) {
38
+ const entry = branch[i];
39
+ if (entry?.type !== "compaction") continue;
40
+ const before = Number(entry.tokensBefore ?? 0);
41
+ const details = entry.details ?? {};
42
+ let after = Number(details.tokensAfter ?? 0);
43
+ if (!after) {
44
+ const keptIdx = entry.firstKeptEntryId ? branch.findIndex((e) => e?.id === entry.firstKeptEntryId) : -1;
45
+ let chars = typeof entry.summary === "string" ? entry.summary.length : 0;
46
+ if (keptIdx >= 0 && keptIdx < i) {
47
+ for (let k = keptIdx; k < i; k++) {
48
+ if (branch[k]?.type === "message") chars += estimateMessageContentChars(branch[k].message?.content);
49
+ }
50
+ }
51
+ after = Math.ceil(chars / CHARS_PER_TOKEN);
52
+ }
53
+ const at = Date.parse(entry.timestamp ?? "");
54
+ const method = methodOf(details);
55
+ compactions.push({
56
+ tokensBefore: before,
57
+ tokensAfter: after,
58
+ method,
59
+ ...(Number.isFinite(at) ? { at } : {}),
60
+ ...(typeof details.jev?.dropped === "number" ? { jevDropped: details.jev.dropped } : {}),
61
+ });
62
+ }
63
+
64
+ const tokensBefore = compactions.reduce((s, c) => s + c.tokensBefore, 0);
65
+ const tokensAfter = compactions.reduce((s, c) => s + Math.min(c.tokensAfter, c.tokensBefore || c.tokensAfter), 0);
66
+ return { compactions, tokensBefore, tokensAfter, tokensSaved: Math.max(0, tokensBefore - tokensAfter) };
67
+ }
68
+
69
+ const METHOD_WORD: Record<CompactionMethod, string> = { vcc: "lossless", llm: "model" };
70
+
71
+ /** "3 lossless, 1 jev" — only the methods that occurred. */
72
+ export function methodBreakdown(stats: SessionCompactionStats): string {
73
+ const counts = new Map<CompactionMethod, number>();
74
+ for (const c of stats.compactions) counts.set(c.method, (counts.get(c.method) ?? 0) + 1);
75
+ return [...counts.entries()].map(([m, n]) => `${n} ${METHOD_WORD[m]}`).join(", ");
76
+ }
@@ -2,7 +2,7 @@
2
2
  * context_budget tool — estimate remaining context window
3
3
  */
4
4
 
5
- import type { AutoCompactionConfig } from "../types.js";
5
+ import type { CompactorConfig } from "../types.js";
6
6
 
7
7
  export interface ContextBudgetResult {
8
8
  percentFull: number;
@@ -13,54 +13,37 @@ export interface ContextBudgetResult {
13
13
  }
14
14
 
15
15
  export function estimateContextBudget(
16
- tokensBefore?: number,
16
+ tokensUsed?: number,
17
17
  contextWindowSize?: number,
18
- autoCompaction?: Pick<AutoCompactionConfig, "enabled" | "thresholdPercent">,
18
+ config?: Pick<CompactorConfig, "trigger" | "thresholdPercent">,
19
19
  ): ContextBudgetResult | null {
20
- const windowSize = contextWindowSize ?? 200000; // Default 200K context
21
- const used = tokensBefore ?? 0;
22
-
23
- if (used <= 0 && tokensBefore === undefined) return null;
24
-
20
+ if (tokensUsed === undefined) return null;
21
+ const windowSize = contextWindowSize ?? 200000;
22
+ const used = Math.max(0, tokensUsed);
25
23
  const remaining = Math.max(0, windowSize - used);
26
24
  const percentFull = windowSize > 0 ? Math.round((used / windowSize) * 100) : 0;
27
25
 
28
26
  let advice: string;
29
- if (percentFull >= 90) {
30
- advice = "CRITICAL: Compact immediately. Very little room for complex tasks.";
31
- } else if (percentFull >= 75) {
32
- advice = "Context is filling up. Compact before starting complex work.";
33
- } else if (percentFull >= 50) {
34
- advice = "Moderate context usage. Compact before large multi-step tasks.";
27
+ if (config?.trigger === "percent") {
28
+ advice = percentFull >= config.thresholdPercent
29
+ ? `Context will be compacted automatically at the next turn boundary (threshold ${config.thresholdPercent}%). Keep working.`
30
+ : `Context compacts automatically at ${config.thresholdPercent}%. Keep working.`;
35
31
  } else {
36
- advice = "Context has plenty of room. No compaction needed yet.";
37
- }
38
-
39
- if (autoCompaction?.enabled) {
40
- const threshold = autoCompaction.thresholdPercent;
41
- if (percentFull >= threshold) {
42
- advice += ` UniPi percentage auto-compaction is enabled at ${threshold}% and usage is above that threshold, subject to cooldown/repeat safeguards.`;
43
- } else {
44
- advice += ` UniPi percentage auto-compaction is enabled at ${threshold}%.`;
45
- }
32
+ advice = percentFull >= 85
33
+ ? "Context is nearly full; Pi compacts automatically near the limit and the work continues. Keep working."
34
+ : "Plenty of room. Pi compacts automatically near the limit.";
46
35
  }
47
36
 
48
37
  const message = `Context: ~${percentFull}% full (estimated ${remaining.toLocaleString()} tokens remaining)`;
49
-
50
38
  return { percentFull, remainingTokens: remaining, totalTokens: windowSize, message, advice };
51
39
  }
52
40
 
53
- /**
54
- * The context_budget tool handler.
55
- * Called from the tool registration — receives tokensBefore from Pi context.
56
- */
57
41
  export function contextBudgetTool(
58
- tokensBefore?: number,
42
+ tokensUsed?: number,
59
43
  contextWindowSize?: number,
60
- autoCompaction?: Pick<AutoCompactionConfig, "enabled" | "thresholdPercent">,
44
+ config?: Pick<CompactorConfig, "trigger" | "thresholdPercent">,
61
45
  ): string {
62
- const budget = estimateContextBudget(tokensBefore, contextWindowSize, autoCompaction);
63
- if (!budget) return "Context budget: Unknown (no token data available from session).";
64
-
46
+ const budget = estimateContextBudget(tokensUsed, contextWindowSize, config);
47
+ if (!budget) return "Context budget: unknown (no token data available yet).";
65
48
  return `${budget.message}\nAdvice: ${budget.advice}`;
66
49
  }
@@ -1,10 +1,11 @@
1
1
  /**
2
- * ctx_doctor tool — diagnostics checklist
2
+ * Compactor diagnostics — settings, Pi's compaction switch, leftovers.
3
3
  */
4
4
 
5
- import { existsSync } from "node:fs";
6
- import { COMPACTOR_CONFIG_PATH } from "../config/manager.js";
7
- import type { SessionDB } from "../session/db.js";
5
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
6
+ import { homedir } from "node:os";
7
+ import { join } from "node:path";
8
+ import type { CompactorConfig } from "../types.js";
8
9
 
9
10
  export interface DoctorResult {
10
11
  healthy: boolean;
@@ -15,46 +16,54 @@ export interface DoctorResult {
15
16
  }>;
16
17
  }
17
18
 
18
- export async function ctxDoctor(
19
- sessionDB: SessionDB,
20
- ): Promise<DoctorResult> {
21
- const checks: DoctorResult["checks"] = [];
19
+ export const LEGACY_DB_DIR = join(homedir(), ".unipi", "db", "compactor");
22
20
 
23
- // Config check
24
- checks.push({
25
- name: "Config file",
26
- status: existsSync(COMPACTOR_CONFIG_PATH) ? "pass" : "warn",
27
- message: existsSync(COMPACTOR_CONFIG_PATH) ? "Config found" : "Using defaults (no config file)",
28
- });
21
+ function piCompactionEnabled(): boolean | null {
22
+ try {
23
+ const raw = JSON.parse(readFileSync(join(homedir(), ".pi", "agent", "settings.json"), "utf-8"));
24
+ const enabled = raw?.compaction?.enabled;
25
+ return typeof enabled === "boolean" ? enabled : true;
26
+ } catch {
27
+ return null;
28
+ }
29
+ }
29
30
 
30
- // Session DB check
31
+ function dirSizeMb(dir: string): number {
31
32
  try {
32
- const count = sessionDB.getEventCount("test");
33
- checks.push({
34
- name: "Session DB",
35
- status: "pass",
36
- message: "SQLite connection OK",
37
- });
38
- } catch (err) {
33
+ return readdirSync(dir).reduce((sum, name) => sum + statSync(join(dir, name)).size, 0) / (1024 * 1024);
34
+ } catch {
35
+ return 0;
36
+ }
37
+ }
38
+
39
+ export function ctxDoctor(config: CompactorConfig, opts: { hasModel: boolean }): DoctorResult {
40
+ const checks: DoctorResult["checks"] = [];
41
+ const method = config.method === "vcc" ? "lossless" : "model summary";
42
+ const when = config.trigger === "pi" ? "Pi's context limit" : `${config.thresholdPercent}% of context`;
43
+ checks.push({ name: "Settings", status: "pass", message: `method: ${method} · when: ${when} · Pi's /compact: ${config.piCompact}` });
44
+
45
+ const piAuto = piCompactionEnabled();
46
+ if (config.trigger === "pi" && piAuto === false) {
39
47
  checks.push({
40
- name: "Session DB",
48
+ name: "Pi auto-compaction",
41
49
  status: "fail",
42
- message: `Connection failed: ${err}`,
50
+ message: "compaction.enabled is false in ~/.pi/agent/settings.json, so nothing compacts automatically. Enable it or set When = at a percentage.",
43
51
  });
52
+ } else {
53
+ checks.push({ name: "Pi auto-compaction", status: "pass", message: piAuto === null ? "Pi settings unreadable (defaults apply)" : "enabled" });
44
54
  }
45
55
 
46
- // Runtime checks
47
- const runtimes = ["node", "python3", "bash"];
48
- for (const rt of runtimes) {
49
- try {
50
- const { execSync } = await import("node:child_process");
51
- execSync(`command -v ${rt}`, { stdio: "ignore" });
52
- checks.push({ name: `Runtime: ${rt}`, status: "pass", message: "Available" });
53
- } catch {
54
- checks.push({ name: `Runtime: ${rt}`, status: "warn", message: "Not found" });
55
- }
56
+ if (config.method === "llm" && !opts.hasModel) {
57
+ checks.push({ name: "Model", status: "warn", message: "model summaries need a selected model" });
58
+ }
59
+
60
+ if (existsSync(LEGACY_DB_DIR)) {
61
+ checks.push({
62
+ name: "Old continuity database",
63
+ status: "warn",
64
+ message: `${LEGACY_DB_DIR} (${dirSizeMb(LEGACY_DB_DIR).toFixed(1)} MB) is no longer used and can be deleted.`,
65
+ });
56
66
  }
57
67
 
58
- const healthy = checks.every((c) => c.status !== "fail");
59
- return { healthy, checks };
68
+ return { healthy: checks.every((c) => c.status !== "fail"), checks };
60
69
  }