@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
@@ -2,7 +2,8 @@
2
2
  * @pi-unipi/kanboard — settings (hub section "Kanboard").
3
3
  */
4
4
 
5
- import { getSettings, registerCommandRunner, registerSettings, setSettings } from "@pi-unipi/core";
5
+ import { existsSync, readFileSync } from "node:fs";
6
+ import { getSettings, globalSettingsPath, projectSettingsPath, registerCommandRunner, registerSettings, setSettings } from "@pi-unipi/core";
6
7
 
7
8
  export type ChainGate = "in_review" | "done";
8
9
 
@@ -25,12 +26,20 @@ export interface KanboardSettings {
25
26
  requireAuth: boolean;
26
27
  /** Reuse <home>/token so board links survive daemon restarts. */
27
28
  keepToken: boolean;
28
- /** Tasks one session may queue (0 = unlimited). */
29
- queueMax: number;
30
29
  /** Distinct sessions running tasks per project (min 1). */
31
30
  maxSessions: number;
32
- /** `add` calls allowed per -do/runner window (0 = unlimited). */
31
+ /** `add` calls allowed per turn (0 = unlimited). */
33
32
  turnAddLimit: number;
33
+ /** Task slots a /unipi:kanboard-do grants (each `start` uses one). */
34
+ doTasks: number;
35
+ /** Board writes a /unipi:kanboard-do grants. */
36
+ doWrites: number;
37
+ /** Whether a blocked-by-confusion task may ask the user (ask) or must
38
+ * assume-and-note (avoid, the default). */
39
+ blocking: "avoid" | "ask";
40
+ /** Progress reminders when the agent works board tasks by hand: `start`
41
+ * before the first edit, `finish` (or block) before the turn ends. */
42
+ reminders: boolean;
34
43
  }
35
44
 
36
45
  export const DEFAULT_SETTINGS: KanboardSettings = {
@@ -43,9 +52,12 @@ export const DEFAULT_SETTINGS: KanboardSettings = {
43
52
  openBrowser: false,
44
53
  requireAuth: false,
45
54
  keepToken: false,
46
- queueMax: 10,
47
55
  maxSessions: 2,
48
56
  turnAddLimit: 20,
57
+ doTasks: 5,
58
+ doWrites: 10,
59
+ blocking: "avoid",
60
+ reminders: true,
49
61
  };
50
62
 
51
63
  export const KANBOARD_NAMESPACE = "kanboard";
@@ -59,11 +71,11 @@ export function registerKanboardSettings(): void {
59
71
  registerSettings({
60
72
  namespace: KANBOARD_NAMESPACE,
61
73
  label: "Kanboard",
62
- defaults: DEFAULT_SETTINGS as unknown as Record<string, unknown>,
74
+ defaults: { ...DEFAULT_SETTINGS } as unknown as Record<string, unknown>,
63
75
  schema: [
64
76
  {
65
77
  title: "Kanboard",
66
- description: "Deferred-work board: task runner, daemon and skill",
78
+ description: "The session's work board: daemon, -do budgets and skill",
67
79
  fields: [
68
80
  {
69
81
  key: "chainGate",
@@ -100,12 +112,16 @@ export function registerKanboardSettings(): void {
100
112
  label: "Keep the access token across restarts",
101
113
  description: "Reuse one token so board links stay valid; rotate it below.",
102
114
  },
103
- { key: "queueMax", type: "number", label: "-do queue limit", min: 0, zeroLabel: "unlimited", description: "Tasks a session may queue for the runner" },
104
115
  { key: "maxSessions", type: "number", label: "Sessions working at once (per project)", min: 1, description: "Distinct sessions holding in_progress tasks" },
105
- { key: "turnAddLimit", type: "number", label: "New tasks per turn", min: 0, zeroLabel: "unlimited", description: "`add` calls allowed per -do turn or runner task" },
106
- { key: "open", type: "action", label: "Open board…", description: "Start the daemon and print the URL", command: ACTION_OPEN },
107
- { key: "stopDaemon", type: "action", label: "Stop daemon", description: "Terminate the running kanboard daemon", command: ACTION_STOP_DAEMON },
108
- { key: "rotateToken", type: "action", label: "Rotate access token", description: "Drop the persistent token; a fresh one mints on the next daemon start", command: ACTION_ROTATE_TOKEN },
116
+ { key: "turnAddLimit", type: "number", label: "New tasks per turn", min: 0, zeroLabel: "unlimited", description: "`add` calls allowed per turn" },
117
+ {
118
+ key: "reminders",
119
+ type: "boolean",
120
+ label: "Progress reminders",
121
+ description: "Remind the agent to `start` a mentioned Todo task before editing (text only, never blocks; off in child sessions)",
122
+ },
123
+ { key: "doTasks", type: "number", label: "-do task slots", min: 0, zeroLabel: "off", description: "Task slots a /unipi:kanboard-do grants; each `start` uses one" },
124
+ { key: "doWrites", type: "number", label: "-do write budget", min: 0, zeroLabel: "off", description: "Board writes a /unipi:kanboard-do grants (add, edit, link, order, move, note on tasks you don't hold)" },
109
125
  ],
110
126
  },
111
127
  ],
@@ -127,9 +143,18 @@ export function readKanboardSettings(cwd: string = process.cwd()): KanboardSetti
127
143
  openBrowser: raw.openBrowser === true,
128
144
  requireAuth: raw.requireAuth === true,
129
145
  keepToken: raw.keepToken === true,
130
- queueMax: typeof raw.queueMax === "number" && raw.queueMax >= 0 ? raw.queueMax : DEFAULT_SETTINGS.queueMax,
131
146
  maxSessions: typeof raw.maxSessions === "number" && raw.maxSessions >= 1 ? raw.maxSessions : DEFAULT_SETTINGS.maxSessions,
132
147
  turnAddLimit: typeof raw.turnAddLimit === "number" && raw.turnAddLimit >= 0 ? raw.turnAddLimit : DEFAULT_SETTINGS.turnAddLimit,
148
+ // Migration: getSettings merges defaults, so a stored doCredits hides
149
+ // behind the doWrites default. Look at the stored layer for the legacy key
150
+ // (project first, then global) — the next settings write persists doWrites.
151
+ doTasks: typeof raw.doTasks === "number" && raw.doTasks >= 0 ? raw.doTasks : DEFAULT_SETTINGS.doTasks,
152
+ doWrites:
153
+ typeof raw.doWrites === "number" && raw.doWrites >= 0 && storedDoWritesSet(cwd)
154
+ ? raw.doWrites
155
+ : (storedLegacyDoCredits(cwd) ?? DEFAULT_SETTINGS.doWrites),
156
+ blocking: raw.blocking === "ask" ? "ask" : "avoid",
157
+ reminders: raw.reminders !== false,
133
158
  };
134
159
  }
135
160
 
@@ -137,6 +162,35 @@ export function writeKanboardSettings(patch: Partial<KanboardSettings>, cwd: str
137
162
  setSettings(KANBOARD_NAMESPACE, patch as unknown as Record<string, unknown>, "global", cwd);
138
163
  }
139
164
 
165
+ /** Whether any stored layer carries an explicit doWrites (defaults don't count). */
166
+ function storedDoWritesSet(cwd: string): boolean {
167
+ for (const file of [projectSettingsPath(cwd, KANBOARD_NAMESPACE), globalSettingsPath(KANBOARD_NAMESPACE)]) {
168
+ try {
169
+ if (!existsSync(file)) continue;
170
+ const parsed = JSON.parse(readFileSync(file, "utf-8")) as Record<string, unknown>;
171
+ if (typeof parsed.doWrites === "number") return true;
172
+ } catch {
173
+ // A corrupt file has no legacy value either.
174
+ }
175
+ }
176
+ return false;
177
+ }
178
+
179
+ /** The pre-slot doCredits value from the stored layers, if any. */
180
+ function storedLegacyDoCredits(cwd: string): number | undefined {
181
+ for (const file of [projectSettingsPath(cwd, KANBOARD_NAMESPACE), globalSettingsPath(KANBOARD_NAMESPACE)]) {
182
+ try {
183
+ if (!existsSync(file)) continue;
184
+ const parsed = JSON.parse(readFileSync(file, "utf-8")) as Record<string, unknown>;
185
+ const value = parsed.doCredits;
186
+ if (typeof value === "number" && value >= 0) return value;
187
+ } catch {
188
+ // A corrupt file has no legacy value either.
189
+ }
190
+ }
191
+ return undefined;
192
+ }
193
+
140
194
  /** Re-exported so index.ts and tests share one registration path. */
141
195
  export function registerKanboardRunners(runners: {
142
196
  open: (ctx: unknown) => void | Promise<void>;
@@ -150,9 +204,9 @@ export function registerKanboardRunners(runners: {
150
204
 
151
205
  /**
152
206
  * Limits reach the Rust CLI through the environment — refresh the process-wide
153
- * values so the agent's bash calls and the runner's CLI calls both see them.
207
+ * values so the agent's bash calls and spawned CLI calls both see them.
208
+ * (queueMax died with the runner; the binary's queue limit is untouched.)
154
209
  */
155
210
  export function applyLimitEnv(settings: KanboardSettings): void {
156
- process.env.UNIPI_KANBOARD_QUEUE_MAX = String(settings.queueMax);
157
211
  process.env.UNIPI_KANBOARD_MAX_SESSIONS = String(settings.maxSessions);
158
212
  }
@@ -32,6 +32,8 @@ export interface KanboardRun {
32
32
  mode?: string;
33
33
  goal?: string | null;
34
34
  started?: string;
35
+ /** "system" (runner claim) or "agent" (`start` self-claim). */
36
+ owner?: string;
35
37
  }
36
38
 
37
39
  export interface KanboardActivity {
@@ -43,6 +45,8 @@ export interface KanboardActivity {
43
45
  export interface KanboardTask {
44
46
  id: string;
45
47
  title: string;
48
+ /** Title derived from the first body line when `title` is empty (Rust `Task::display_title`). */
49
+ displayTitle?: string;
46
50
  body?: string;
47
51
  status: string;
48
52
  priority?: string;
@@ -113,6 +117,7 @@ export function asTask(command: string, value: unknown): KanboardTask {
113
117
  ...record,
114
118
  id: requireString(command, record, "id"),
115
119
  title: requireString(command, record, "title"),
120
+ displayTitle: typeof record.displayTitle === "string" ? record.displayTitle : undefined,
116
121
  status: requireString(command, record, "status"),
117
122
  deps: optionalArray<string>(record.deps) ?? [],
118
123
  activity: optionalArray<KanboardActivity>(record.activity) ?? [],
@@ -10,20 +10,23 @@ import { existsSync, renameSync } from "node:fs";
10
10
  import { join, resolve } from "node:path";
11
11
  import { Text } from "@earendil-works/pi-tui";
12
12
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
13
- import { emitEvent, getPackageVersion, registerCommandRunner, stateDir, UNIPI_EVENTS, setSharedLongHorizonMode } from "@pi-unipi/core";
13
+ import { appendProgress, emitEvent, getPackageVersion, harnessMetadata, installArbiter, registerCommandRunner, registerCompactionContext, registerNudgeProvider, sendHarnessUserMessage, stateDir, UNIPI_EVENTS, setSharedLongHorizonMode } from "@pi-unipi/core";
14
+ import { longHorizonCompactionBrief } from "./src/compaction-brief.js";
14
15
  import { OwnerCoordinator, type OwnerEvent } from "./src/owner.js";
15
16
  import { Gate } from "./src/gate.js";
16
- import { registerLongHorizonCommands } from "./src/commands.js";
17
+ import { registerLongHorizonCommands, stopActiveOwner } from "./src/commands.js";
17
18
  import { loadSettings } from "./src/settings.js";
18
19
  import { GoalMachine } from "./src/engine/goal-state.js";
19
20
  import { GoalToolset } from "./src/tools/goal.js";
20
21
  import { GoalContinuation } from "./src/engine/continuation.js";
22
+ import { NudgeStash, nextStashMetaState, ownerEventClearsStash, type StashMetaState } from "./src/engine/nudge-stash.js";
21
23
  import { RalphLoop } from "./src/engine/ralph.js";
22
24
  import { registerRalphTools } from "./src/tools/ralph.js";
23
25
  import { SwarmLedger, registerSwarmTools } from "./src/tools/swarm.js";
24
26
  import { GraphLedger, registerGraphTools } from "./src/tools/graph.js";
25
27
  import { TodoStore, registerTodoTool } from "./src/tools/todo.js";
26
28
  import { wireRuntime } from "./src/runtime.js";
29
+ import { ralphProgressData } from "./src/progress.js";
27
30
 
28
31
  export * from "./src/modes.js";
29
32
  export * from "./src/owner.js";
@@ -56,10 +59,29 @@ export default function longHorizon(pi: ExtensionAPI): void {
56
59
  const version = getPackageVersion("long-horizon");
57
60
  const statePath = () => lhStatePath("state.json");
58
61
 
62
+ // Standalone loads (`-e long-horizon/index.ts`) must install the arbiter
63
+ // themselves; the umbrella installs it before modules mount, and this call
64
+ // is a no-op there (holder flag).
65
+ try {
66
+ installArbiter(pi);
67
+ } catch {
68
+ // Never block module load on the arbiter.
69
+ }
70
+
59
71
  // Owner lifecycle → unipi event bus (footer/info-screen consume these).
72
+ // A finished or parked owner invalidates an undelivered nudge (declared
73
+ // before `owner` so the onChange closure can clear it).
74
+ const stash = new NudgeStash();
75
+ // Provenance mirror state lives with the stash (declared BEFORE the owner
76
+ // constructor — its onChange may fire immediately).
77
+ let stashMetaState: StashMetaState<ReturnType<typeof harnessMetadata>> = { meta: undefined, kickoff: false };
60
78
  const owner = new OwnerCoordinator({
61
79
  statePath,
62
80
  onChange: (_snapshot, event: OwnerEvent) => {
81
+ if (ownerEventClearsStash(event)) {
82
+ stash.take();
83
+ stashMetaState = { meta: undefined, kickoff: false };
84
+ }
63
85
  emitEvent(pi, UNIPI_EVENTS.LONG_HORIZON_OWNER_CHANGED, {
64
86
  event: event.type,
65
87
  ...("owner" in event && event.owner
@@ -108,7 +130,9 @@ export default function longHorizon(pi: ExtensionAPI): void {
108
130
  ? "active owner"
109
131
  : data.source === "judge_abstained_low_confidence"
110
132
  ? "judge abstained → default"
111
- : "default";
133
+ : data.source === "child"
134
+ ? "child agent"
135
+ : "default";
112
136
  const line = `${t.fg?.("customMessageText", badge) ?? badge} ${t.fg?.("dim", via) ?? via}`;
113
137
  return new Text(line, 0, 0);
114
138
  },
@@ -117,6 +141,66 @@ export default function longHorizon(pi: ExtensionAPI): void {
117
141
  // Renderer registration is UI-dependent; skip where unavailable.
118
142
  }
119
143
 
144
+ // ── messaging: nudge stash, not the message queue ──────────────────
145
+ // Continuation/ralph sends land in a one-slot stash; the arbiter delivers
146
+ // it as the single before_settle nudge (priority 100) and takes it.
147
+ // Timer wakes while idle (waiting-backoff) stay direct sends: they are
148
+ // events, and nothing would settle to drain the stash for them.
149
+ // (`stash` is declared above the owner so owner stops can clear it.)
150
+ let runActive = false;
151
+ try {
152
+ pi.on("agent_start", () => {
153
+ runActive = true;
154
+ });
155
+ pi.on("agent_settled", () => {
156
+ runActive = false;
157
+ });
158
+ } catch {
159
+ // Idle tracking must never block load.
160
+ }
161
+ const send: (message: string, kind?: "kickoff", meta?: { source: string; title: string; synopsis?: string; severity?: "warning" }) => void = (
162
+ message,
163
+ kind,
164
+ meta,
165
+ ) => {
166
+ const next = nextStashMetaState(stashMetaState, stash.peek() !== null, kind, harnessMetadata(meta ?? { source: "Goal", title: kind === "kickoff" ? "Kickoff" : "Continuation" }, "boundary"));
167
+ stashMetaState = next;
168
+ stash.put(message, kind === "kickoff" ? { kickoff: true } : {});
169
+ };
170
+ const sendNow = (message: string, meta?: { source: string; title: string; synopsis?: string; severity?: "warning" }): void => {
171
+ try {
172
+ const options = runActive ? { deliverAs: "followUp" as const } : undefined;
173
+ sendHarnessUserMessage(
174
+ pi,
175
+ message,
176
+ meta ?? { source: "Goal", title: "Continuation" },
177
+ options,
178
+ );
179
+ } catch {
180
+ // A failed wake must never crash the session.
181
+ }
182
+ };
183
+ try {
184
+ registerNudgeProvider("long-horizon", 100, () => {
185
+ const text = stash.peek();
186
+ if (text === null) return null;
187
+ return {
188
+ source: "long-horizon",
189
+ priority: 100,
190
+ customType: "unipi:lh-continue",
191
+ content: text,
192
+ display: true,
193
+ ...(stashMetaState.meta !== undefined ? { details: { unipiHarness: stashMetaState.meta } } : {}),
194
+ onDelivered: () => {
195
+ stash.take();
196
+ stashMetaState = { meta: undefined, kickoff: false };
197
+ },
198
+ };
199
+ });
200
+ } catch {
201
+ // Registration must never block load.
202
+ }
203
+
120
204
  // Goal engine: machine + tools + continuation + runtime wiring.
121
205
  const machine = new GoalMachine({
122
206
  statePath: () => lhStatePath("goal.json"),
@@ -130,18 +214,18 @@ export default function longHorizon(pi: ExtensionAPI): void {
130
214
  toolset,
131
215
  owner,
132
216
  verifier: { evaluate: async () => { throw new Error("verifier unbound"); } },
133
- send: (message) => {
134
- void pi.sendUserMessage(message);
135
- },
217
+ // Continuation messages ride the arbiter's nudge stash (delivered as the
218
+ // single before_settle nudge); timer wakes use sendNow directly.
219
+ send,
220
+ sendNow,
136
221
  });
137
222
  // Ralph loop rides the same goal machine + verifier; footer events preserved.
138
223
  const ralph = new RalphLoop({
139
224
  machine,
140
225
  owner,
141
226
  ralphDir: () => join(stateDir("long-horizon", "state"), "ralph"),
142
- send: (message) => {
143
- void pi.sendUserMessage(message);
144
- },
227
+ // Iteration prompts ride the arbiter's nudge stash like the goal's.
228
+ send: (message, kind) => send(message, kind, { source: "Ralph", title: "Iteration", synopsis: "Loop iteration prompt" }),
145
229
  onEvent: (event) => {
146
230
  if (event.type === "loop_start") {
147
231
  emitEvent(pi, UNIPI_EVENTS.RALPH_LOOP_START, { name: event.name, iteration: event.iteration, total: event.total });
@@ -150,6 +234,9 @@ export default function longHorizon(pi: ExtensionAPI): void {
150
234
  } else if (event.type === "loop_end") {
151
235
  emitEvent(pi, UNIPI_EVENTS.RALPH_LOOP_END, { name: event.name, reason: event.reason, iterations: event.iterations });
152
236
  }
237
+ // User-only progress bar on every loop update.
238
+ const bar = ralphProgressData(ralph);
239
+ if (bar) appendProgress(pi, bar);
153
240
  },
154
241
  });
155
242
  registerRalphTools(pi, ralph);
@@ -157,7 +244,11 @@ export default function longHorizon(pi: ExtensionAPI): void {
157
244
  registerSwarmTools(pi, { ledger: swarm, owner });
158
245
  const graph = new GraphLedger(owner);
159
246
  registerGraphTools(pi, { ledger: graph });
160
- wireRuntime(pi, { machine, toolset, continuation, gate, loadSettings, ralph });
247
+ const runtime = wireRuntime(pi, { machine, toolset, continuation, gate, loadSettings, ralph });
248
+ // Compaction summaries lead with the live goal / ralph state.
249
+ registerCompactionContext("long-horizon", () =>
250
+ longHorizonCompactionBrief(machine.getActive(), ralph.get(), join(stateDir("long-horizon", "state"), "ralph")),
251
+ );
161
252
 
162
253
  // Cross-module entry points: another module (kanboard's runner) can start a
163
254
  // goal and read its status without importing this package. Mirrors what the
@@ -196,9 +287,91 @@ export default function longHorizon(pi: ExtensionAPI): void {
196
287
  if (typeof goalId === "string" && goalId.length > 0 && goal.goalId !== goalId) {
197
288
  return { found: false, reason: `the active goal is ${goal.goalId}` };
198
289
  }
199
- return { found: true, goalId: goal.goalId, status: goal.status, objective: goal.objective };
290
+ return { found: true, goalId: goal.goalId, status: goal.status, reason: goal.reason, objective: goal.objective };
291
+ });
292
+
293
+ // kanboard runner bridge: pause/resume/stop the goal around task releases
294
+ // and re-claims, so an interrupted task keeps its goal for the next claim.
295
+ registerCommandRunner("unipi:goal-pause", async (_ctx, args) => {
296
+ const goal = machine.get();
297
+ const active = owner.getActive();
298
+ if (!goal || goal.status !== "active" || active?.kind !== "goal") {
299
+ return { ok: false, reason: goal ? `goal is ${goal.status}` : "no goal in this session" };
300
+ }
301
+ const requested = (args as { goalId?: unknown } | undefined)?.goalId;
302
+ if (typeof requested === "string" && requested.length > 0 && requested !== goal.goalId) {
303
+ return { ok: false, reason: `the active goal is ${goal.goalId}` };
304
+ }
305
+ const paused = machine.pause("paused(user_requested)");
306
+ if (!paused) return { ok: false, reason: `goal is ${goal.status}` };
307
+ const parked = owner.suspend("paused(user_requested)");
308
+ if (!parked) {
309
+ // Slot held: the goal stays paused but unowned — resume reactivates it.
310
+ emitEvent(pi, UNIPI_EVENTS.LONG_HORIZON_OWNER_CHANGED, { event: "suspended", kind: "goal" });
311
+ }
312
+ return { ok: true, goalId: paused.goalId, status: paused.status };
200
313
  });
201
- registerLongHorizonCommands(pi, gate, owner, ralph);
314
+
315
+ registerCommandRunner("unipi:goal-resume", async (_ctx, args) => {
316
+ const goal = machine.get();
317
+ const requested = (args as { goalId?: unknown } | undefined)?.goalId;
318
+ if (!goal) return { ok: false, reason: "no goal in this session" };
319
+ if (goal.status !== "paused") return { ok: false, reason: `goal is ${goal.status}, not paused` };
320
+ if (typeof requested === "string" && requested.length > 0 && requested !== goal.goalId) {
321
+ return { ok: false, reason: `the parked goal is ${goal.goalId}` };
322
+ }
323
+ const resumed = machine.resume();
324
+ if (!resumed) return { ok: false, reason: "goal could not be resumed" };
325
+ if (owner.getParked()) owner.resume();
326
+ gate.setExplicit("goal");
327
+ return { ok: true, goalId: resumed.goalId, status: resumed.status };
328
+ });
329
+
330
+ registerCommandRunner("unipi:goal-stop", async (_ctx, args) => {
331
+ const goal = machine.get();
332
+ const requested = (args as { goalId?: unknown } | undefined)?.goalId;
333
+ if (typeof requested === "string" && requested.length > 0 && goal && requested !== goal.goalId) {
334
+ return { ok: false, reason: `the goal in this session is ${goal.goalId}` };
335
+ }
336
+ const stopped = stopActiveOwner(pi, gate, owner, machine, toolset, "goal");
337
+ return stopped !== undefined
338
+ ? { ok: true, goalId: requested || goal?.goalId }
339
+ : { ok: false, reason: goal && goal.status === "complete" ? "goal already stopped" : "no active goal" };
340
+ });
341
+
342
+ // kanboard strategy runners: swarm/graph set the explicit mode for the next
343
+ // turn; ralph starts the checklist loop (its own kickoff prompt ships via
344
+ // ralph.start's send — the runner's task prompt lands behind it).
345
+ registerCommandRunner("unipi:lh-explicit", async (_ctx, args) => {
346
+ const mode = String((args as { mode?: unknown } | undefined)?.mode ?? "");
347
+ if (mode !== "swarm" && mode !== "graph" && mode !== "goal" && mode !== "none") {
348
+ return { ok: false, reason: `unknown mode "${mode}"` };
349
+ }
350
+ if (mode !== "none" && owner.getActive() && owner.getActive()?.kind !== mode) {
351
+ return {
352
+ ok: false,
353
+ reason: `the session is owned by a ${owner.getActive()?.kind} owner — finish or suspend it first`,
354
+ };
355
+ }
356
+ gate.setExplicit(mode);
357
+ return { ok: true, mode };
358
+ });
359
+
360
+ registerCommandRunner("unipi:ralph-start", async (_ctx, args) => {
361
+ const a = (args ?? {}) as { name?: unknown; content?: unknown };
362
+ const name = String(a.name ?? "").trim();
363
+ const content = String(a.content ?? "");
364
+ if (!name || !content.trim()) {
365
+ return { ok: false, reason: "name and content are required" };
366
+ }
367
+ return ralph.start(name, content);
368
+ });
369
+
370
+ registerLongHorizonCommands(pi, gate, owner, ralph, {
371
+ ralphBar: () => ralphProgressData(ralph),
372
+ estimateGoal: (ctx) => runtime.estimateGoal(ctx),
373
+ goalEstimateOn: () => loadSettings().goalProgress !== "off",
374
+ }, machine, toolset);
202
375
 
203
376
  // Crash recovery: repair, don't resume — reload durable state so the gate
204
377
  // reattaches the owner's tool surface; the continuation arms a recovery
@@ -206,7 +379,10 @@ export default function longHorizon(pi: ExtensionAPI): void {
206
379
  pi.on("session_start", () => {
207
380
  const restored = owner.restore();
208
381
  machine.restore();
209
- if (machine.getActive()) continuation.armRecovery();
382
+ // Only a truly drivable goal arms the recovery fragment; a paused goal
383
+ // must come back idle until the user (or the kanboard runner) resumes it.
384
+ const goalStatus = machine.get()?.status;
385
+ if (goalStatus === "active" || goalStatus === "waiting") continuation.armRecovery();
210
386
  // Resume (`pi -r`) starts no turn, so before_agent_start never fires. Publish
211
387
  // the mode to the shared holder the footer PULLS each render — the active
212
388
  // owner's mode if one survived, else the default — so the header restores
@@ -227,6 +403,7 @@ export default function longHorizon(pi: ExtensionAPI): void {
227
403
  "unipi:ralph",
228
404
  "unipi:swarm",
229
405
  "unipi:graph",
406
+ "unipi:regular",
230
407
  ],
231
408
  tools: ["create_goal", "get_goal", "update_goal", "todowrite", "ralph_done", "loop_status", "swarm_report", "swarm_status", "swarm_yield", "update_agent_graph", "graph_output", "view_agent_graph"],
232
409
  });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-unipi/long-horizon",
3
- "version": "3.0.0-alpha.2",
3
+ "version": "3.0.0-alpha.21",
4
4
  "description": "Long-horizon execution for Pi — mode-gated /goal /ralph /swarm /graph orchestration with a TypeSafe prompt judge",
5
5
  "type": "module",
6
6
  "main": "index.ts",
@@ -23,7 +23,7 @@
23
23
  "graph"
24
24
  ],
25
25
  "scripts": {
26
- "test": "tsc --noEmit && npx tsx --test src/__tests__/*.test.ts",
26
+ "test": "tsc --noEmit && UNIPI_FUSION_CHILD= UNIPI_SUBAGENT_CHILD= UNIPI_KANBOARD_CHILD= npx tsx --test src/__tests__/*.test.ts",
27
27
  "typecheck": "tsc --noEmit"
28
28
  },
29
29
  "files": [
@@ -37,7 +37,7 @@
37
37
  "access": "public"
38
38
  },
39
39
  "dependencies": {
40
- "@pi-unipi/core": "3.0.0-alpha.2"
40
+ "@pi-unipi/core": "3.0.0-alpha.21"
41
41
  },
42
42
  "peerDependencies": {
43
43
  "@earendil-works/pi-coding-agent": "^0.87.1",