pi-crew 0.9.53 → 0.9.55

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 (493) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/package.json +6 -3
  3. package/scripts/clean-strip-types.mjs +60 -0
  4. package/src/adapters/claude-adapter.ts +23 -0
  5. package/src/adapters/codex-adapter.ts +21 -0
  6. package/src/adapters/cursor-adapter.ts +17 -0
  7. package/src/adapters/export-util.ts +143 -0
  8. package/src/adapters/index.ts +15 -0
  9. package/src/adapters/registry.ts +18 -0
  10. package/src/adapters/types.ts +23 -0
  11. package/src/agents/agent-config.ts +194 -0
  12. package/src/agents/agent-search.ts +98 -0
  13. package/src/agents/agent-serializer.ts +38 -0
  14. package/src/agents/discover-agents.ts +623 -0
  15. package/src/benchmark/benchmark-runner.ts +313 -0
  16. package/src/benchmark/feedback-loop.ts +73 -0
  17. package/src/config/config.ts +1270 -0
  18. package/src/config/defaults.ts +193 -0
  19. package/src/config/drift-detector.ts +290 -0
  20. package/src/config/markers.ts +330 -0
  21. package/src/config/resilient-parser.ts +117 -0
  22. package/src/config/role-tools.ts +118 -0
  23. package/src/config/suggestions.ts +75 -0
  24. package/src/config/types.ts +280 -0
  25. package/src/errors.ts +194 -0
  26. package/src/extension/action-suggestions.ts +117 -0
  27. package/src/extension/async-notifier.ts +179 -0
  28. package/src/extension/autonomous-policy.ts +209 -0
  29. package/src/extension/command-completions.ts +120 -0
  30. package/src/extension/context-status-injection.ts +185 -0
  31. package/src/extension/crew-autocomplete.ts +131 -0
  32. package/src/extension/crew-cleanup.ts +168 -0
  33. package/src/extension/crew-input-router.ts +93 -0
  34. package/src/extension/crew-shortcuts.ts +87 -0
  35. package/src/extension/crew-vibes/cat-frames.ts +18 -0
  36. package/src/extension/crew-vibes/config.ts +195 -0
  37. package/src/extension/crew-vibes/figures.ts +101 -0
  38. package/src/extension/crew-vibes/font-detect.ts +71 -0
  39. package/src/extension/crew-vibes/footer.ts +292 -0
  40. package/src/extension/crew-vibes/index.ts +432 -0
  41. package/src/extension/crew-vibes/provider-usage.ts +396 -0
  42. package/src/extension/crew-vibes/render.ts +206 -0
  43. package/src/extension/crew-vibes/speed.ts +286 -0
  44. package/src/extension/cross-extension-rpc.ts +350 -0
  45. package/src/extension/help.ts +62 -0
  46. package/src/extension/import-index.ts +74 -0
  47. package/src/extension/knowledge-injection.ts +402 -0
  48. package/src/extension/management.ts +571 -0
  49. package/src/extension/message-renderers.ts +108 -0
  50. package/src/extension/notification-router.ts +152 -0
  51. package/src/extension/notification-sink.ts +54 -0
  52. package/src/extension/pi-api.ts +56 -0
  53. package/src/extension/plan-orchestrate.ts +302 -0
  54. package/src/extension/project-init.ts +170 -0
  55. package/src/extension/register.ts +113 -0
  56. package/src/extension/registration/artifact-cleanup.ts +20 -0
  57. package/src/extension/registration/command-registration.ts +58 -0
  58. package/src/extension/registration/command-utils.ts +58 -0
  59. package/src/extension/registration/commands.ts +1228 -0
  60. package/src/extension/registration/compaction-guard.ts +333 -0
  61. package/src/extension/registration/context-builder.ts +135 -0
  62. package/src/extension/registration/crash-recovery-cache.ts +54 -0
  63. package/src/extension/registration/foreground-run-controller.ts +238 -0
  64. package/src/extension/registration/hook-registration.ts +119 -0
  65. package/src/extension/registration/lazy-configurers.ts +124 -0
  66. package/src/extension/registration/lifecycle-handlers.ts +923 -0
  67. package/src/extension/registration/lifecycle.ts +259 -0
  68. package/src/extension/registration/observability.ts +322 -0
  69. package/src/extension/registration/registration-types.ts +158 -0
  70. package/src/extension/registration/runtime-cleanup.ts +230 -0
  71. package/src/extension/registration/subagent-helpers.ts +125 -0
  72. package/src/extension/registration/subagent-manager-setup.ts +330 -0
  73. package/src/extension/registration/subagent-tools.ts +456 -0
  74. package/src/extension/registration/team-tool.ts +225 -0
  75. package/src/extension/registration/tool-registration.ts +50 -0
  76. package/src/extension/registration/ui.ts +172 -0
  77. package/src/extension/registration/viewers.ts +121 -0
  78. package/src/extension/registration/wire-cross-extension.ts +37 -0
  79. package/src/extension/result-watcher.ts +139 -0
  80. package/src/extension/rpc-hmac.ts +256 -0
  81. package/src/extension/run-bundle-schema.ts +104 -0
  82. package/src/extension/run-export.ts +97 -0
  83. package/src/extension/run-import.ts +168 -0
  84. package/src/extension/run-index.ts +131 -0
  85. package/src/extension/run-maintenance.ts +190 -0
  86. package/src/extension/session-summary.ts +18 -0
  87. package/src/extension/team-manager-command.ts +153 -0
  88. package/src/extension/team-onboard.ts +180 -0
  89. package/src/extension/team-recommendation.ts +288 -0
  90. package/src/extension/team-tool/anchor.ts +172 -0
  91. package/src/extension/team-tool/api.ts +1244 -0
  92. package/src/extension/team-tool/auto-summarize.ts +142 -0
  93. package/src/extension/team-tool/cache-control.ts +19 -0
  94. package/src/extension/team-tool/cancel.ts +337 -0
  95. package/src/extension/team-tool/chain-dispatch.ts +95 -0
  96. package/src/extension/team-tool/chain-executor.ts +379 -0
  97. package/src/extension/team-tool/config-patch.ts +50 -0
  98. package/src/extension/team-tool/context.ts +149 -0
  99. package/src/extension/team-tool/destructive-gate.ts +52 -0
  100. package/src/extension/team-tool/dispatch/automate.ts +80 -0
  101. package/src/extension/team-tool/dispatch/control.ts +42 -0
  102. package/src/extension/team-tool/dispatch/index.ts +95 -0
  103. package/src/extension/team-tool/dispatch/manage.ts +161 -0
  104. package/src/extension/team-tool/dispatch/run.ts +53 -0
  105. package/src/extension/team-tool/dispatch/status.ts +187 -0
  106. package/src/extension/team-tool/doctor.ts +431 -0
  107. package/src/extension/team-tool/explain.ts +282 -0
  108. package/src/extension/team-tool/failure-patterns.ts +118 -0
  109. package/src/extension/team-tool/goal-wrap.ts +330 -0
  110. package/src/extension/team-tool/goal.ts +556 -0
  111. package/src/extension/team-tool/handle-schedule.ts +332 -0
  112. package/src/extension/team-tool/handle-settings.ts +520 -0
  113. package/src/extension/team-tool/health-monitor.ts +481 -0
  114. package/src/extension/team-tool/inspect.ts +88 -0
  115. package/src/extension/team-tool/intent-policy.ts +45 -0
  116. package/src/extension/team-tool/lifecycle-actions.ts +615 -0
  117. package/src/extension/team-tool/orchestrate.ts +98 -0
  118. package/src/extension/team-tool/parallel-dispatch.ts +183 -0
  119. package/src/extension/team-tool/plan.ts +50 -0
  120. package/src/extension/team-tool/respond.ts +150 -0
  121. package/src/extension/team-tool/run-deadline.ts +66 -0
  122. package/src/extension/team-tool/run-not-found.ts +49 -0
  123. package/src/extension/team-tool/run.ts +1051 -0
  124. package/src/extension/team-tool/status.ts +260 -0
  125. package/src/extension/team-tool/workflow-manage.ts +261 -0
  126. package/src/extension/team-tool-types.ts +26 -0
  127. package/src/extension/team-tool.ts +750 -0
  128. package/src/extension/tool-result.ts +16 -0
  129. package/src/extension/validate-resources.ts +108 -0
  130. package/src/hooks/registry.ts +200 -0
  131. package/src/hooks/types.ts +69 -0
  132. package/src/i18n.ts +212 -0
  133. package/src/observability/correlation.ts +50 -0
  134. package/src/observability/event-bus.ts +86 -0
  135. package/src/observability/event-to-metric.ts +170 -0
  136. package/src/observability/exporters/adapter.ts +27 -0
  137. package/src/observability/exporters/otlp-exporter.ts +356 -0
  138. package/src/observability/exporters/prometheus-exporter.ts +54 -0
  139. package/src/observability/metric-registry.ts +100 -0
  140. package/src/observability/metric-retention.ts +64 -0
  141. package/src/observability/metric-sink.ts +99 -0
  142. package/src/observability/metrics-primitives.ts +219 -0
  143. package/src/plugins/plugin-define.ts +6 -0
  144. package/src/plugins/plugin-registry.ts +32 -0
  145. package/src/plugins/plugins/index.ts +3 -0
  146. package/src/plugins/plugins/nextjs.ts +19 -0
  147. package/src/plugins/plugins/vite.ts +10 -0
  148. package/src/plugins/plugins/vitest.ts +9 -0
  149. package/src/prompt/prompt-runtime.ts +380 -0
  150. package/src/runtime/adaptive-plan.ts +563 -0
  151. package/src/runtime/agent-control.ts +215 -0
  152. package/src/runtime/agent-memory.ts +81 -0
  153. package/src/runtime/agent-observability.ts +122 -0
  154. package/src/runtime/anchor-manager.ts +475 -0
  155. package/src/runtime/async-marker.ts +32 -0
  156. package/src/runtime/async-runner.ts +381 -0
  157. package/src/runtime/attention-events.ts +28 -0
  158. package/src/runtime/auto-summarize.ts +344 -0
  159. package/src/runtime/background-runner.ts +840 -0
  160. package/src/runtime/batch-barrier.ts +147 -0
  161. package/src/runtime/broker-issuer.ts +39 -0
  162. package/src/runtime/cancellation-token.ts +99 -0
  163. package/src/runtime/cancellation.ts +99 -0
  164. package/src/runtime/capability-inventory.ts +137 -0
  165. package/src/runtime/chain-parser.ts +237 -0
  166. package/src/runtime/chain-runner.ts +606 -0
  167. package/src/runtime/checkpoint.ts +291 -0
  168. package/src/runtime/child-pi-constants.ts +42 -0
  169. package/src/runtime/child-pi-kill.ts +180 -0
  170. package/src/runtime/child-pi-pool.ts +68 -0
  171. package/src/runtime/child-pi-spawn.ts +287 -0
  172. package/src/runtime/child-pi-steering.ts +128 -0
  173. package/src/runtime/child-pi-streams.ts +296 -0
  174. package/src/runtime/child-pi-transcript.ts +169 -0
  175. package/src/runtime/child-pi.ts +1105 -0
  176. package/src/runtime/coalesce-tasks.ts +268 -0
  177. package/src/runtime/code-summary.ts +292 -0
  178. package/src/runtime/command-trace.ts +105 -0
  179. package/src/runtime/compact-pipeline.ts +56 -0
  180. package/src/runtime/compact-stages/ansi-strip-stage.ts +25 -0
  181. package/src/runtime/compact-stages/blank-collapse-stage.ts +31 -0
  182. package/src/runtime/compact-stages/deduplicate-stage.ts +34 -0
  183. package/src/runtime/compact-stages/head-snap-stage.ts +57 -0
  184. package/src/runtime/compact-stages/index.ts +23 -0
  185. package/src/runtime/compact-stages/tail-capture-stage.ts +77 -0
  186. package/src/runtime/compact-stages/truncation-stage.ts +74 -0
  187. package/src/runtime/compaction-summary.ts +278 -0
  188. package/src/runtime/completion-guard.ts +207 -0
  189. package/src/runtime/concurrency.ts +58 -0
  190. package/src/runtime/crash-classification.ts +229 -0
  191. package/src/runtime/crash-recovery.ts +594 -0
  192. package/src/runtime/crew-agent-records.ts +595 -0
  193. package/src/runtime/crew-agent-runtime.ts +62 -0
  194. package/src/runtime/crew-broker-child.ts +88 -0
  195. package/src/runtime/crew-broker-client.ts +673 -0
  196. package/src/runtime/crew-broker-tokens.ts +159 -0
  197. package/src/runtime/crew-broker.ts +1304 -0
  198. package/src/runtime/crew-hooks.ts +227 -0
  199. package/src/runtime/cross-extension-rpc.ts +143 -0
  200. package/src/runtime/custom-tools/irc-tool.ts +282 -0
  201. package/src/runtime/custom-tools/submit-result-tool.ts +102 -0
  202. package/src/runtime/deadletter.ts +47 -0
  203. package/src/runtime/delivery-coordinator.ts +211 -0
  204. package/src/runtime/delta-conflict.ts +348 -0
  205. package/src/runtime/deterministic-ast.ts +161 -0
  206. package/src/runtime/diagnostic-export.ts +171 -0
  207. package/src/runtime/direct-run.ts +45 -0
  208. package/src/runtime/dwf-state-store.ts +102 -0
  209. package/src/runtime/dynamic-workflow-context.ts +1013 -0
  210. package/src/runtime/dynamic-workflow-runner.ts +325 -0
  211. package/src/runtime/effectiveness.ts +90 -0
  212. package/src/runtime/errors/crew-errors.ts +162 -0
  213. package/src/runtime/event-stream-bridge.ts +98 -0
  214. package/src/runtime/foreground-control.ts +193 -0
  215. package/src/runtime/foreground-watchdog.ts +132 -0
  216. package/src/runtime/global-worker-cap.ts +96 -0
  217. package/src/runtime/goal-achievement.ts +148 -0
  218. package/src/runtime/goal-evaluator.ts +365 -0
  219. package/src/runtime/goal-loop-runner.ts +825 -0
  220. package/src/runtime/goal-state-store.ts +219 -0
  221. package/src/runtime/green-contract.ts +55 -0
  222. package/src/runtime/group-join.ts +267 -0
  223. package/src/runtime/handoff-manager.ts +598 -0
  224. package/src/runtime/heartbeat-gradient.ts +36 -0
  225. package/src/runtime/heartbeat-watcher.ts +239 -0
  226. package/src/runtime/hidden-handoff.ts +407 -0
  227. package/src/runtime/important-line-classifier.ts +140 -0
  228. package/src/runtime/intercom-bridge.ts +187 -0
  229. package/src/runtime/iteration-hooks.ts +305 -0
  230. package/src/runtime/live-agent-control.ts +136 -0
  231. package/src/runtime/live-agent-manager.ts +704 -0
  232. package/src/runtime/live-control-realtime.ts +56 -0
  233. package/src/runtime/live-extension-bridge.ts +148 -0
  234. package/src/runtime/live-irc.ts +97 -0
  235. package/src/runtime/live-session-health.ts +108 -0
  236. package/src/runtime/live-session-runtime.ts +1146 -0
  237. package/src/runtime/loop-gates.ts +128 -0
  238. package/src/runtime/manifest-cache.ts +375 -0
  239. package/src/runtime/mcp-proxy.ts +105 -0
  240. package/src/runtime/metric-parser.ts +36 -0
  241. package/src/runtime/model-fallback.ts +409 -0
  242. package/src/runtime/model-resolver.ts +126 -0
  243. package/src/runtime/model-scope.ts +158 -0
  244. package/src/runtime/orphan-worker-registry.ts +465 -0
  245. package/src/runtime/output-validator.ts +202 -0
  246. package/src/runtime/overflow-recovery.ts +206 -0
  247. package/src/runtime/parallel-research.ts +68 -0
  248. package/src/runtime/parallel-utils.ts +160 -0
  249. package/src/runtime/parent-guard.ts +134 -0
  250. package/src/runtime/path-overlap.ts +150 -0
  251. package/src/runtime/peer-dep.ts +292 -0
  252. package/src/runtime/per-write-validator.ts +181 -0
  253. package/src/runtime/phase-progress.ts +217 -0
  254. package/src/runtime/phase-tracker.ts +385 -0
  255. package/src/runtime/pi-args.ts +655 -0
  256. package/src/runtime/pi-json-output.ts +170 -0
  257. package/src/runtime/pi-spawn.ts +273 -0
  258. package/src/runtime/pipeline-runner.ts +523 -0
  259. package/src/runtime/plan-templates.ts +201 -0
  260. package/src/runtime/policy-engine.ts +115 -0
  261. package/src/runtime/post-checks.ts +142 -0
  262. package/src/runtime/post-exit-stdio-guard.ts +109 -0
  263. package/src/runtime/process-lifecycle.ts +491 -0
  264. package/src/runtime/process-status.ts +154 -0
  265. package/src/runtime/progress-event-coalescer.ts +44 -0
  266. package/src/runtime/progress-tracker.ts +124 -0
  267. package/src/runtime/prose-compressor.ts +162 -0
  268. package/src/runtime/recovery-recipes.ts +203 -0
  269. package/src/runtime/replace.ts +570 -0
  270. package/src/runtime/resilient-edit.ts +153 -0
  271. package/src/runtime/result-extractor.ts +217 -0
  272. package/src/runtime/retry-executor.ts +109 -0
  273. package/src/runtime/retry-runner.ts +336 -0
  274. package/src/runtime/role-permission.ts +59 -0
  275. package/src/runtime/run-coalesced-task-group.ts +308 -0
  276. package/src/runtime/run-drift.ts +219 -0
  277. package/src/runtime/run-tracker.ts +116 -0
  278. package/src/runtime/run-worker.ts +78 -0
  279. package/src/runtime/runtime-policy.ts +29 -0
  280. package/src/runtime/runtime-resolver.ts +172 -0
  281. package/src/runtime/runtime-warmup.ts +160 -0
  282. package/src/runtime/scheduler.ts +353 -0
  283. package/src/runtime/semaphore.ts +141 -0
  284. package/src/runtime/sensitive-paths.ts +93 -0
  285. package/src/runtime/session-resources.ts +25 -0
  286. package/src/runtime/session-snapshot.ts +59 -0
  287. package/src/runtime/session-usage.ts +79 -0
  288. package/src/runtime/settings-store.ts +155 -0
  289. package/src/runtime/sidechain-output.ts +35 -0
  290. package/src/runtime/single-agent-compose.ts +84 -0
  291. package/src/runtime/skill-effectiveness.ts +524 -0
  292. package/src/runtime/skill-instructions.ts +394 -0
  293. package/src/runtime/stale-reconciler.ts +680 -0
  294. package/src/runtime/stream-preview.ts +184 -0
  295. package/src/runtime/streaming-output.ts +50 -0
  296. package/src/runtime/subagent-manager.ts +561 -0
  297. package/src/runtime/subprocess-tool-registry.ts +70 -0
  298. package/src/runtime/supervisor-contact.ts +64 -0
  299. package/src/runtime/task-display.ts +52 -0
  300. package/src/runtime/task-graph-scheduler.ts +227 -0
  301. package/src/runtime/task-graph.ts +290 -0
  302. package/src/runtime/task-health.ts +83 -0
  303. package/src/runtime/task-id.ts +161 -0
  304. package/src/runtime/task-output-context.ts +602 -0
  305. package/src/runtime/task-packet.ts +268 -0
  306. package/src/runtime/task-quality.ts +199 -0
  307. package/src/runtime/task-runner/capabilities.ts +78 -0
  308. package/src/runtime/task-runner/child-executor.ts +790 -0
  309. package/src/runtime/task-runner/context-retrieval.ts +163 -0
  310. package/src/runtime/task-runner/live-executor.ts +227 -0
  311. package/src/runtime/task-runner/output-splitter.ts +152 -0
  312. package/src/runtime/task-runner/post-execution.ts +480 -0
  313. package/src/runtime/task-runner/pre-execution.ts +365 -0
  314. package/src/runtime/task-runner/progress.ts +157 -0
  315. package/src/runtime/task-runner/prompt-builder.ts +278 -0
  316. package/src/runtime/task-runner/prompt-pipeline.ts +88 -0
  317. package/src/runtime/task-runner/result-utils.ts +16 -0
  318. package/src/runtime/task-runner/retrieval-orchestrator.ts +310 -0
  319. package/src/runtime/task-runner/run-projection.ts +125 -0
  320. package/src/runtime/task-runner/scaffold-executor.ts +25 -0
  321. package/src/runtime/task-runner/state-helpers.ts +176 -0
  322. package/src/runtime/task-runner/tail-read.ts +34 -0
  323. package/src/runtime/task-runner.ts +216 -0
  324. package/src/runtime/team-runner-artifacts.ts +13 -0
  325. package/src/runtime/team-runner.ts +2396 -0
  326. package/src/runtime/tool-output-pruner.ts +333 -0
  327. package/src/runtime/tool-progress.ts +278 -0
  328. package/src/runtime/usage-tracker.ts +73 -0
  329. package/src/runtime/verification-gates.ts +579 -0
  330. package/src/runtime/verification-integrity.ts +108 -0
  331. package/src/runtime/verification-worktree.ts +186 -0
  332. package/src/runtime/worker-heartbeat.ts +25 -0
  333. package/src/runtime/worker-startup.ts +83 -0
  334. package/src/runtime/workflow-state.ts +195 -0
  335. package/src/runtime/workspace-lock.ts +443 -0
  336. package/src/runtime/workspace-tree.ts +312 -0
  337. package/src/runtime/yield-handler.ts +224 -0
  338. package/src/runtime/zombie-scanner.ts +298 -0
  339. package/src/schema/config-schema.ts +327 -0
  340. package/src/schema/team-tool-schema.ts +492 -0
  341. package/src/schema/validation-types.ts +159 -0
  342. package/src/skills/discover-skills.ts +203 -0
  343. package/src/skills/skill-templates.ts +456 -0
  344. package/src/skills/validate.ts +285 -0
  345. package/src/state/active-run-registry.ts +439 -0
  346. package/src/state/artifact-store.ts +176 -0
  347. package/src/state/atomic-write.ts +970 -0
  348. package/src/state/blob-store.ts +308 -0
  349. package/src/state/contracts.ts +160 -0
  350. package/src/state/crew-init.ts +269 -0
  351. package/src/state/decision-ledger.ts +372 -0
  352. package/src/state/event-log-rotation.ts +399 -0
  353. package/src/state/event-log.ts +1465 -0
  354. package/src/state/event-reconstructor.ts +229 -0
  355. package/src/state/gitignore-manager.ts +46 -0
  356. package/src/state/health-store.ts +79 -0
  357. package/src/state/hook-instinct-bridge.ts +94 -0
  358. package/src/state/hook-integrations.ts +51 -0
  359. package/src/state/instinct-store.ts +275 -0
  360. package/src/state/jsonl-writer.ts +107 -0
  361. package/src/state/locks.ts +562 -0
  362. package/src/state/mailbox.ts +916 -0
  363. package/src/state/observation-store.ts +176 -0
  364. package/src/state/run-cache.ts +193 -0
  365. package/src/state/run-graph.ts +183 -0
  366. package/src/state/run-metrics.ts +165 -0
  367. package/src/state/schedule.ts +166 -0
  368. package/src/state/session-state-map.ts +51 -0
  369. package/src/state/state-store.ts +973 -0
  370. package/src/state/task-claims.ts +61 -0
  371. package/src/state/tiered-eval.ts +480 -0
  372. package/src/state/types-eval.ts +58 -0
  373. package/src/state/types.ts +447 -0
  374. package/src/state/usage.ts +138 -0
  375. package/src/state/worker-atomic-writer.ts +198 -0
  376. package/src/subagents/async-entry.ts +1 -0
  377. package/src/subagents/index.ts +3 -0
  378. package/src/subagents/live/control.ts +1 -0
  379. package/src/subagents/live/manager.ts +1 -0
  380. package/src/subagents/live/realtime.ts +1 -0
  381. package/src/subagents/live/session-runtime.ts +1 -0
  382. package/src/subagents/manager.ts +1 -0
  383. package/src/subagents/spawn.ts +1 -0
  384. package/src/teams/discover-teams.ts +187 -0
  385. package/src/teams/team-config.ts +27 -0
  386. package/src/teams/team-serializer.ts +38 -0
  387. package/src/tools/safe-bash-extension.ts +54 -0
  388. package/src/tools/safe-bash.ts +505 -0
  389. package/src/types/diff.d.ts +18 -0
  390. package/src/types/new-api-types.ts +35 -0
  391. package/src/ui/agent-management-overlay.ts +160 -0
  392. package/src/ui/card-colors.ts +139 -0
  393. package/src/ui/crew-footer.ts +102 -0
  394. package/src/ui/crew-select-list.ts +112 -0
  395. package/src/ui/dashboard-panes/agents-pane.ts +164 -0
  396. package/src/ui/dashboard-panes/cancellation-pane.ts +66 -0
  397. package/src/ui/dashboard-panes/capability-pane.ts +77 -0
  398. package/src/ui/dashboard-panes/health-pane.ts +31 -0
  399. package/src/ui/dashboard-panes/mailbox-pane.ts +35 -0
  400. package/src/ui/dashboard-panes/metrics-pane.ts +37 -0
  401. package/src/ui/dashboard-panes/progress-pane.ts +38 -0
  402. package/src/ui/dashboard-panes/transcript-pane.ts +10 -0
  403. package/src/ui/deploy-bundled-themes.ts +71 -0
  404. package/src/ui/dwf-phase-display.ts +151 -0
  405. package/src/ui/dynamic-border.ts +35 -0
  406. package/src/ui/format-helpers.ts +31 -0
  407. package/src/ui/heartbeat-aggregator.ts +74 -0
  408. package/src/ui/key-utils.ts +42 -0
  409. package/src/ui/keybinding-map.ts +248 -0
  410. package/src/ui/layout-primitives.ts +106 -0
  411. package/src/ui/live-conversation-overlay.ts +183 -0
  412. package/src/ui/live-duration.ts +55 -0
  413. package/src/ui/live-run-sidebar.ts +293 -0
  414. package/src/ui/loaders.ts +6 -0
  415. package/src/ui/mascot.ts +449 -0
  416. package/src/ui/overlays/agent-picker-overlay.ts +66 -0
  417. package/src/ui/overlays/confirm-overlay.ts +60 -0
  418. package/src/ui/overlays/help-overlay.ts +177 -0
  419. package/src/ui/overlays/mailbox-compose-overlay.ts +181 -0
  420. package/src/ui/overlays/mailbox-compose-preview.ts +78 -0
  421. package/src/ui/overlays/mailbox-detail-overlay.ts +155 -0
  422. package/src/ui/pi-ui-compat.ts +68 -0
  423. package/src/ui/powerbar-publisher.ts +459 -0
  424. package/src/ui/render-coalescer.ts +101 -0
  425. package/src/ui/render-diff.ts +160 -0
  426. package/src/ui/render-scheduler.ts +278 -0
  427. package/src/ui/run-action-dispatcher.ts +182 -0
  428. package/src/ui/run-dashboard.ts +910 -0
  429. package/src/ui/run-event-bus.ts +381 -0
  430. package/src/ui/run-snapshot-cache.ts +1057 -0
  431. package/src/ui/settings-overlay.ts +1067 -0
  432. package/src/ui/shared-overlay-scheduler.ts +96 -0
  433. package/src/ui/snapshot-types.ts +90 -0
  434. package/src/ui/spinner.ts +17 -0
  435. package/src/ui/status-colors.ts +143 -0
  436. package/src/ui/syntax-highlight.ts +136 -0
  437. package/src/ui/terminal-status.ts +269 -0
  438. package/src/ui/theme-adapter.ts +271 -0
  439. package/src/ui/theme-discovery.ts +199 -0
  440. package/src/ui/tool-progress-formatter.ts +93 -0
  441. package/src/ui/tool-renderers/brief-mode.ts +225 -0
  442. package/src/ui/tool-renderers/index.ts +726 -0
  443. package/src/ui/transcript-cache.ts +128 -0
  444. package/src/ui/transcript-entries.ts +256 -0
  445. package/src/ui/transcript-viewer.ts +424 -0
  446. package/src/ui/widget/index.ts +418 -0
  447. package/src/ui/widget/widget-formatters.ts +182 -0
  448. package/src/ui/widget/widget-model.ts +110 -0
  449. package/src/ui/widget/widget-renderer.ts +182 -0
  450. package/src/ui/widget/widget-types.ts +36 -0
  451. package/src/utils/bm25-search.ts +235 -0
  452. package/src/utils/completion-dedupe.ts +63 -0
  453. package/src/utils/conflict-detect.ts +668 -0
  454. package/src/utils/env-allowlist.ts +30 -0
  455. package/src/utils/env-filter.ts +155 -0
  456. package/src/utils/file-coalescer.ts +98 -0
  457. package/src/utils/fingerprint.ts +180 -0
  458. package/src/utils/frontmatter.ts +70 -0
  459. package/src/utils/fs-watch.ts +37 -0
  460. package/src/utils/gh-protocol.ts +556 -0
  461. package/src/utils/git.ts +260 -0
  462. package/src/utils/guards.ts +107 -0
  463. package/src/utils/ids.ts +24 -0
  464. package/src/utils/incremental-reader.ts +220 -0
  465. package/src/utils/internal-error.ts +10 -0
  466. package/src/utils/names.ts +36 -0
  467. package/src/utils/ndjson.ts +115 -0
  468. package/src/utils/paths.ts +227 -0
  469. package/src/utils/project-detector.ts +160 -0
  470. package/src/utils/redaction.ts +370 -0
  471. package/src/utils/resolve-shell.ts +36 -0
  472. package/src/utils/run-watcher-registry.ts +152 -0
  473. package/src/utils/safe-paths.ts +393 -0
  474. package/src/utils/scan-cache.ts +144 -0
  475. package/src/utils/session-utils.ts +110 -0
  476. package/src/utils/sleep.ts +54 -0
  477. package/src/utils/socket-path.ts +164 -0
  478. package/src/utils/sse-parser.ts +131 -0
  479. package/src/utils/task-name-generator.ts +337 -0
  480. package/src/utils/timings.ts +33 -0
  481. package/src/utils/token-counter.ts +200 -0
  482. package/src/utils/visual.ts +207 -0
  483. package/src/workflows/cost-estimator.ts +34 -0
  484. package/src/workflows/discover-workflows.ts +308 -0
  485. package/src/workflows/intermediate-store.ts +166 -0
  486. package/src/workflows/preflight-validator.ts +168 -0
  487. package/src/workflows/topology-analyzer.ts +203 -0
  488. package/src/workflows/validate-workflow.ts +50 -0
  489. package/src/workflows/workflow-config.ts +75 -0
  490. package/src/workflows/workflow-serializer.ts +32 -0
  491. package/src/worktree/branch-freshness.ts +112 -0
  492. package/src/worktree/cleanup.ts +341 -0
  493. package/src/worktree/worktree-manager.ts +1204 -0
@@ -0,0 +1,459 @@
1
+ import * as fs from "node:fs";
2
+ import type { CrewUiConfig } from "../config/config.ts";
3
+ import { listRecentRuns } from "../extension/run-index.ts";
4
+ import { readCrewAgents } from "../runtime/crew-agent-records.ts";
5
+ import { listLiveAgents } from "../runtime/live-agent-manager.ts";
6
+ import type { ManifestCache } from "../runtime/manifest-cache.ts";
7
+ import { isDisplayActiveRun } from "../runtime/process-status.ts";
8
+ import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
9
+ import { aggregateUsage } from "../state/usage.ts";
10
+ import { readJsonFileCoalesced } from "../utils/file-coalescer.ts";
11
+ import { logInternalError } from "../utils/internal-error.ts";
12
+ import { allWorkflows, discoverWorkflows } from "../workflows/discover-workflows.ts";
13
+ import { RenderCoalescer } from "./render-coalescer.ts";
14
+ import type { RunSnapshotCache, RunUiSnapshot } from "./snapshot-types.ts";
15
+ import { notificationBadge } from "./widget/widget-formatters.ts";
16
+
17
+ type EventBus =
18
+ | {
19
+ emit?: (event: string, data: unknown) => void;
20
+ listenerCount?: (event: string) => number;
21
+ }
22
+ | undefined;
23
+ type StatusContext =
24
+ | {
25
+ hasUI?: boolean;
26
+ ui?: {
27
+ setStatus?: (key: string, text: string | undefined) => void;
28
+ };
29
+ }
30
+ | undefined;
31
+
32
+ const TASK_READ_TTL_MS = 200;
33
+
34
+ function hasPowerbarConsumer(events: EventBus): boolean {
35
+ try {
36
+ return (events?.listenerCount?.("powerbar:register-segment") ?? 0) > 0 || (events?.listenerCount?.("powerbar:update") ?? 0) > 0;
37
+ } catch {
38
+ return false;
39
+ }
40
+ }
41
+
42
+ function setStatusFallback(ctx: StatusContext, text: string | undefined): void {
43
+ try {
44
+ if (ctx?.hasUI) ctx.ui?.setStatus?.("pi-crew", text);
45
+ } catch (error) {
46
+ logInternalError("powerbar.statusFallback", error);
47
+ }
48
+ }
49
+
50
+ function safeEmit(events: EventBus, event: string, data: unknown): void {
51
+ try {
52
+ events?.emit?.(event, data);
53
+ } catch (error) {
54
+ logInternalError("powerbar.safeEmit", error, `event=${event}`);
55
+ }
56
+ }
57
+
58
+ function readTasks(tasksPath: string): TeamTaskState[] {
59
+ try {
60
+ const parse = () => {
61
+ const parsed = JSON.parse(fs.readFileSync(tasksPath, "utf-8"));
62
+ return Array.isArray(parsed) ? (parsed as TeamTaskState[]) : [];
63
+ };
64
+ return readJsonFileCoalesced(tasksPath, TASK_READ_TTL_MS, parse);
65
+ } catch (error) {
66
+ logInternalError("powerbar.readTasks", error, tasksPath);
67
+ return [];
68
+ }
69
+ }
70
+
71
+ export function compactTokens(total: number): string {
72
+ return total >= 1000 ? `${Math.round(total / 1000)}k` : `${total}`;
73
+ }
74
+
75
+ export function registerPiCrewPowerbarSegments(events: EventBus, config?: CrewUiConfig): void {
76
+ if (config?.powerbar === false) return;
77
+ safeEmit(events, "powerbar:register-segment", {
78
+ id: "pi-crew-active",
79
+ label: "pi-crew active agents",
80
+ });
81
+ safeEmit(events, "powerbar:register-segment", {
82
+ id: "pi-crew-progress",
83
+ label: "pi-crew run progress",
84
+ });
85
+ safeEmit(events, "powerbar:register-segment", {
86
+ id: "pi-crew-steps",
87
+ label: "pi-crew workflow steps",
88
+ });
89
+ }
90
+
91
+ export function updatePiCrewPowerbar(
92
+ events: EventBus,
93
+ cwd: string,
94
+ config?: CrewUiConfig,
95
+ manifestCache?: ManifestCache,
96
+ snapshotCache?: RunSnapshotCache,
97
+ ctx?: StatusContext,
98
+ notificationCount = 0,
99
+ preloadedManifests?: TeamRunManifest[],
100
+ ): void {
101
+ powerbarPublisher.update(events, cwd, config, manifestCache, snapshotCache, ctx, notificationCount, preloadedManifests);
102
+ }
103
+
104
+ // --- Dedup state + coalescer live on a class instance so they survive hot-reload
105
+ // cleanly: previously these were module-level globals (`lastActiveKey`,
106
+ // `latestArgs`, `powerbarCoalescer`) that survived across reloads and could
107
+ // leak between independent sessions. Encapsulating the state on an instance
108
+ // lets `dispose()` tear it down and a fresh `new PowerbarPublisher()` start
109
+ // from clean slate. The module-level functions remain for back-compat and
110
+ // delegate to a process-wide singleton.
111
+
112
+ interface PowerbarPayloadShape {
113
+ id?: string;
114
+ text?: string;
115
+ suffix?: string;
116
+ bar?: number;
117
+ color?: string;
118
+ icon?: string;
119
+ barSegments?: number;
120
+ }
121
+
122
+ function powerbarKey(payload: PowerbarPayloadShape): string {
123
+ return `${payload.text ?? ""}|${payload.suffix ?? ""}|${payload.bar ?? ""}|${payload.color ?? ""}|${payload.icon ?? ""}|${payload.barSegments ?? ""}`;
124
+ }
125
+
126
+ interface ActiveItem {
127
+ run: TeamRunManifest;
128
+ agents: ReturnType<typeof readCrewAgents>;
129
+ tasks: TeamTaskState[];
130
+ snapshot?: RunUiSnapshot;
131
+ }
132
+
133
+ /**
134
+ * Build the workflow steps segment showing: ✓explore › →plan › ○execute › ○verify
135
+ * with the current/active step highlighted using → arrow.
136
+ */
137
+ function buildStepsPayload(active: ActiveItem[], allTasks: TeamTaskState[]): PowerbarPayloadShape {
138
+ if (!active.length) {
139
+ return { id: "pi-crew-steps" };
140
+ }
141
+ const run = active[0]!.run;
142
+ const workflowName = run.workflow ?? "default";
143
+ // Load workflow steps
144
+ const workflows = allWorkflows(discoverWorkflows(run.cwd));
145
+ const workflow = workflows.find((w) => w.name === workflowName);
146
+ if (!workflow || workflow.steps.length === 0) {
147
+ return { id: "pi-crew-steps", text: workflowName };
148
+ }
149
+ // Build step status map from tasks
150
+ const stepStatus = new Map<string, "completed" | "running" | "pending">();
151
+ for (const task of allTasks) {
152
+ if (!task.stepId) continue;
153
+ if (!stepStatus.has(task.stepId)) {
154
+ if (task.status === "completed") {
155
+ stepStatus.set(task.stepId, "completed");
156
+ } else if (task.status === "running" || task.status === "queued" || task.status === "waiting") {
157
+ stepStatus.set(task.stepId, "running");
158
+ }
159
+ }
160
+ }
161
+ // Format: "✓explore › →plan › ○execute › ○verify"
162
+ // ✓ = completed, → = running (current), ○ = pending
163
+ const stepParts: string[] = [];
164
+ for (const step of workflow.steps) {
165
+ const status = stepStatus.get(step.id) ?? "pending";
166
+ const icon = status === "completed" ? "✓" : status === "running" ? "→" : "○";
167
+ // Shorten long step names
168
+ const stepName = step.id.length > 10 ? step.id.slice(0, 9) + "…" : step.id;
169
+ stepParts.push(`${icon}${stepName}`);
170
+ }
171
+ const stepsText = stepParts.join(" › ");
172
+ // Color: accent if running step exists, success if all complete, dim otherwise
173
+ const hasRunningStep = [...stepStatus.values()].includes("running");
174
+ const allComplete = stepStatus.size === workflow.steps.length && ![...stepStatus.values()].includes("running");
175
+ const color = allComplete ? "success" : hasRunningStep ? "accent" : "dim";
176
+ return {
177
+ id: "pi-crew-steps",
178
+ text: stepsText,
179
+ color,
180
+ };
181
+ }
182
+
183
+ // --- Coalesced powerbar update ---
184
+
185
+ interface PowerbarUpdateArgs {
186
+ events: EventBus;
187
+ cwd: string;
188
+ config?: CrewUiConfig;
189
+ manifestCache?: ManifestCache;
190
+ snapshotCache?: RunSnapshotCache;
191
+ ctx?: StatusContext;
192
+ notificationCount: number;
193
+ preloadedManifests?: TeamRunManifest[];
194
+ }
195
+
196
+ /**
197
+ * Owns the per-segment dedup keys and the coalescer. Module-level functions
198
+ * (`updatePiCrewPowerbar`, `requestPowerbarUpdate`, etc.) are thin facades
199
+ * over a process-wide singleton so existing call sites and tests keep
200
+ * working without code changes.
201
+ */
202
+ class PowerbarPublisher {
203
+ #lastActiveKey: string | undefined;
204
+ #lastProgressKey: string | undefined;
205
+ #lastStepsKey: string | undefined;
206
+ #latestArgs: PowerbarUpdateArgs | null = null;
207
+ readonly #coalescer: RenderCoalescer;
208
+
209
+ constructor() {
210
+ this.#coalescer = new RenderCoalescer(() => {
211
+ const a = this.#latestArgs;
212
+ this.#latestArgs = null;
213
+ if (!a) return;
214
+ this.update(a.events, a.cwd, a.config, a.manifestCache, a.snapshotCache, a.ctx, a.notificationCount, a.preloadedManifests);
215
+ }, 200);
216
+ }
217
+
218
+ update(
219
+ events: EventBus,
220
+ cwd: string,
221
+ config?: CrewUiConfig,
222
+ manifestCache?: ManifestCache,
223
+ snapshotCache?: RunSnapshotCache,
224
+ ctx?: StatusContext,
225
+ notificationCount = 0,
226
+ preloadedManifests?: TeamRunManifest[],
227
+ ): void {
228
+ if (config?.powerbar === false) return;
229
+ const useStatusFallback = !hasPowerbarConsumer(events);
230
+ const runs = preloadedManifests ?? (manifestCache ? manifestCache.list(20) : listRecentRuns(cwd, 20));
231
+ const active = runs
232
+ .map((run) => {
233
+ let snapshot: RunUiSnapshot | undefined;
234
+ try {
235
+ // 1.2: render path is read-only. Use cache.get() only; the background
236
+ // preload loop in register.ts populates entries on its own cadence.
237
+ snapshot = snapshotCache?.get(run.runId);
238
+ } catch (error) {
239
+ logInternalError("powerbar.snapshot", error, run.runId);
240
+ }
241
+ if (snapshot)
242
+ return {
243
+ run: snapshot.manifest,
244
+ agents: snapshot.agents,
245
+ tasks: snapshot.tasks,
246
+ snapshot,
247
+ };
248
+ let agents: ReturnType<typeof readCrewAgents> = [];
249
+ try {
250
+ agents = readCrewAgents(run);
251
+ } catch (error) {
252
+ logInternalError("powerbar.readCrewAgents", error, run.runId);
253
+ }
254
+ return { run, agents, tasks: readTasks(run.tasksPath), snapshot };
255
+ })
256
+ .filter((item) => isDisplayActiveRun(item.run, item.agents));
257
+ if (!active.length) {
258
+ this.#lastActiveKey = undefined;
259
+ this.#lastProgressKey = undefined;
260
+ this.#lastStepsKey = undefined;
261
+ safeEmit(events, "powerbar:update", { id: "pi-crew-active" });
262
+ safeEmit(events, "powerbar:update", { id: "pi-crew-progress" });
263
+ safeEmit(events, "powerbar:update", { id: "pi-crew-steps" });
264
+ return;
265
+ }
266
+ const agents = active.flatMap((item) => item.agents);
267
+ const tasks = active.flatMap((item) => item.tasks);
268
+ const running = agents.filter((agent) => agent.status === "running").length;
269
+ const waiting = active.reduce(
270
+ (sum, item) =>
271
+ sum +
272
+ (item.snapshot
273
+ ? item.snapshot.progress.queued + (item.snapshot.progress.waiting ?? 0)
274
+ : item.tasks.reduce((s, t) => s + (t.status === "queued" || t.status === "waiting" ? 1 : 0), 0)),
275
+ 0,
276
+ );
277
+ const completed = active.reduce(
278
+ (sum, item) =>
279
+ sum + (item.snapshot?.progress.completed ?? item.tasks.reduce((s, t) => s + (t.status === "completed" ? 1 : 0), 0)),
280
+ 0,
281
+ );
282
+ const total = Math.max(
283
+ 1,
284
+ active.reduce((sum, item) => sum + (item.snapshot?.progress.total ?? item.tasks.length), 0) || agents.length,
285
+ );
286
+ const usage = aggregateUsage(tasks);
287
+ const snapshotTokens = active.reduce(
288
+ (sum, item) => sum + (item.snapshot ? item.snapshot.usage.tokensIn + item.snapshot.usage.tokensOut : 0),
289
+ 0,
290
+ );
291
+ const hasUsage = usage && (usage.input ?? 0) + (usage.output ?? 0) + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0) > 0;
292
+ const tokenTotal = hasUsage
293
+ ? (usage.input ?? 0) + (usage.output ?? 0) + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0)
294
+ : snapshotTokens;
295
+ const model =
296
+ config?.showModel === false
297
+ ? undefined
298
+ : agents
299
+ .find((agent) => agent.model)
300
+ ?.model?.split("/")
301
+ .at(-1);
302
+ const tokenText = config?.showTokens === false || !tokenTotal ? undefined : compactTokens(tokenTotal);
303
+ const liveRunning = listLiveAgents().filter((a) => a.status === "running").length;
304
+ // Always show consistent status: running count + queued count from live tasks only
305
+ // Avoid snapshot cache for counts to prevent UI jumping
306
+ const runningCount = agents.filter((a) => a.status === "running").length;
307
+ // Count queued/waiting tasks directly from tasks array (not snapshot) for consistency
308
+ const queuedCount = active.reduce(
309
+ (sum, item) => sum + item.tasks.reduce((s, t) => s + (t.status === "queued" || t.status === "waiting" ? 1 : 0), 0),
310
+ 0,
311
+ );
312
+ // Format: "1 running", "2 running · 1 queued", "3 queued", "idle"
313
+ const runningLabel = runningCount === 1 ? "1 running" : `${runningCount} running`;
314
+ const queuedLabel = queuedCount === 1 ? "1 queued" : `${queuedCount} queued`;
315
+ const crewStatus =
316
+ runningCount > 0 && queuedCount > 0
317
+ ? `${runningLabel} · ${queuedLabel}`
318
+ : runningCount > 0
319
+ ? runningLabel
320
+ : queuedCount > 0
321
+ ? queuedLabel
322
+ : "idle";
323
+ const liveSuffix = liveRunning > 0 ? ` (${liveRunning} live)` : "";
324
+ const notificationText = notificationBadge(notificationCount);
325
+ // Always show model + tokens as suffix when available (for activePayload consistency)
326
+ const suffixParts = [model, tokenText].filter(Boolean);
327
+ const activeSuffix = suffixParts.length > 0 ? suffixParts.join(" · ") : undefined;
328
+ // Progress always includes token count for consistency
329
+ const progressSuffix = `${completed}/${total}${tokenText ? ` · ${tokenText}` : ""}`;
330
+ // Build complete, always-consistent fallback text AND event payload to prevent UI flickering
331
+ // Both fallback and events must use the SAME format - no conditional display
332
+ // Format: "⚙ 1 running · 1 queued · model · 30k · 0/1" (never changes based on availability)
333
+ const progressPart = `${completed}/${total}`;
334
+ const allParts = [`⚙ ${crewStatus}`, model ?? "", tokenText ?? "", progressPart].filter(Boolean);
335
+ const unifiedText = allParts.join(" · ");
336
+ // activePayload.text includes notification badge for event payload
337
+ const activePayload = {
338
+ id: "pi-crew-active",
339
+ icon: "⚙",
340
+ text: `⚙ ${crewStatus}${liveSuffix}${notificationText}${activeSuffix ? ` · ${activeSuffix}` : ""}`,
341
+ suffix: activeSuffix,
342
+ color: running ? "accent" : "warning",
343
+ } as const;
344
+ const progressPayload = {
345
+ id: "pi-crew-progress",
346
+ text: (active[0]?.run as TeamRunManifest)?.team ?? "crew",
347
+ bar: Math.round((completed / total) * 100),
348
+ suffix: progressSuffix,
349
+ color: completed === total ? "success" : "accent",
350
+ barSegments: 8,
351
+ } as const;
352
+ // Build step progress: "explorer > planner > executor > verifier" with current step highlighted
353
+ const stepsPayload = buildStepsPayload(active, tasks);
354
+ // 1.8: dedup per segment using a key over every visible field. Previously
355
+ // the dedup string only carried text/suffix/running, so changes to `bar`
356
+ // (progress %) or `color` could be swallowed and stale UI emitted again
357
+ // later as a single noisy burst.
358
+ const activeKey = powerbarKey(activePayload);
359
+ const progressKey = powerbarKey(progressPayload);
360
+ const stepsKey = powerbarKey(stepsPayload);
361
+ if (activeKey !== this.#lastActiveKey) {
362
+ this.#lastActiveKey = activeKey;
363
+ safeEmit(events, "powerbar:update", activePayload);
364
+ }
365
+ if (progressKey !== this.#lastProgressKey) {
366
+ this.#lastProgressKey = progressKey;
367
+ safeEmit(events, "powerbar:update", progressPayload);
368
+ }
369
+ if (stepsKey !== this.#lastStepsKey) {
370
+ this.#lastStepsKey = stepsKey;
371
+ safeEmit(events, "powerbar:update", stepsPayload);
372
+ }
373
+ // Never call setStatusFallback - crew-widget manages "pi-crew" status with its own widget format
374
+ // Powerbar only emits events; it does not set status directly
375
+ }
376
+
377
+ /**
378
+ * Request a coalesced powerbar update. Multiple rapid calls are batched
379
+ * into a single render pass within 200ms, preventing UI flicker from
380
+ * event bursts.
381
+ */
382
+ request(
383
+ events: EventBus,
384
+ cwd: string,
385
+ config?: CrewUiConfig,
386
+ manifestCache?: ManifestCache,
387
+ snapshotCache?: RunSnapshotCache,
388
+ ctx?: StatusContext,
389
+ notificationCount = 0,
390
+ preloadedManifests?: TeamRunManifest[],
391
+ ): void {
392
+ if (config?.powerbar === false) return;
393
+ this.#latestArgs = {
394
+ events,
395
+ cwd,
396
+ config,
397
+ manifestCache,
398
+ snapshotCache,
399
+ ctx,
400
+ notificationCount,
401
+ preloadedManifests,
402
+ };
403
+ this.#coalescer.request();
404
+ }
405
+
406
+ clear(events: EventBus): void {
407
+ this.#lastActiveKey = undefined;
408
+ this.#lastProgressKey = undefined;
409
+ this.#lastStepsKey = undefined;
410
+ safeEmit(events, "powerbar:update", { id: "pi-crew-active" });
411
+ safeEmit(events, "powerbar:update", { id: "pi-crew-progress" });
412
+ safeEmit(events, "powerbar:update", { id: "pi-crew-steps" });
413
+ }
414
+
415
+ /** Reset dedup state on session lifecycle events. */
416
+ resetDedupState(): void {
417
+ this.#lastActiveKey = undefined;
418
+ this.#lastProgressKey = undefined;
419
+ this.#lastStepsKey = undefined;
420
+ }
421
+
422
+ dispose(): void {
423
+ this.#coalescer.flush();
424
+ this.#coalescer.dispose();
425
+ }
426
+ }
427
+
428
+ const powerbarPublisher = new PowerbarPublisher();
429
+
430
+ /**
431
+ * Request a coalesced powerbar update. Multiple rapid calls are batched into a single
432
+ * render pass within 200ms, preventing UI flicker from event bursts.
433
+ */
434
+ export function requestPowerbarUpdate(
435
+ events: EventBus,
436
+ cwd: string,
437
+ config?: CrewUiConfig,
438
+ manifestCache?: ManifestCache,
439
+ snapshotCache?: RunSnapshotCache,
440
+ ctx?: StatusContext,
441
+ notificationCount = 0,
442
+ preloadedManifests?: TeamRunManifest[],
443
+ ): void {
444
+ powerbarPublisher.request(events, cwd, config, manifestCache, snapshotCache, ctx, notificationCount, preloadedManifests);
445
+ }
446
+
447
+ /** Dispose the powerbar coalescer. Call during extension cleanup. */
448
+ export function disposePowerbarCoalescer(): void {
449
+ powerbarPublisher.dispose();
450
+ }
451
+
452
+ export function clearPiCrewPowerbar(events: EventBus): void {
453
+ powerbarPublisher.clear(events);
454
+ }
455
+
456
+ /** Reset dedup state on session lifecycle events. */
457
+ export function resetPowerbarDedupState(): void {
458
+ powerbarPublisher.resetDedupState();
459
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * RenderCoalescer — batches multiple render requests into single render passes.
3
+ * Prevents UI flicker when many events arrive in quick succession.
4
+ * Inspired by oh-my-pi's PROGRESS_COALESCE_MS (150ms) pattern.
5
+ */
6
+ import { logInternalError } from "../utils/internal-error.ts";
7
+
8
+ export interface RenderCoalescerOptions {
9
+ /**
10
+ * Invoked once per coalesce window with the number of `request()` calls
11
+ * that were collapsed into the most recent scheduled render. Defaults to
12
+ * a debug-scoped `logInternalError` so dropped bursts surface in logs
13
+ * (gated behind PI_TEAMS_DEBUG) without callers having to wire one up.
14
+ */
15
+ onDrop?: (droppedCount: number) => void;
16
+ }
17
+
18
+ export class RenderCoalescer {
19
+ #pending = false;
20
+ #timerId: ReturnType<typeof setTimeout> | null = null;
21
+ #callback: () => void;
22
+ #intervalMs: number;
23
+ #onDrop: (droppedCount: number) => void;
24
+ #dropped = 0;
25
+
26
+ constructor(callback: () => void, intervalMs = 32, options: RenderCoalescerOptions = {}) {
27
+ this.#callback = callback;
28
+ this.#intervalMs = intervalMs;
29
+ this.#onDrop =
30
+ options.onDrop ??
31
+ ((count) => {
32
+ logInternalError(
33
+ "render-coalescer",
34
+ new Error(`coalesced ${count} dropped request(s) into a single render`),
35
+ undefined,
36
+ "debug",
37
+ );
38
+ });
39
+ }
40
+
41
+ /** Request a render. Will be coalesced with other requests within the interval. */
42
+ request(): void {
43
+ if (this.#pending) {
44
+ // A render is already scheduled — coalesce this request and bump
45
+ // the drop counter so the scheduled flush can report how many
46
+ // bursts were collapsed (useful for diagnosing UI lag).
47
+ this.#dropped += 1;
48
+ return;
49
+ }
50
+ this.#pending = true;
51
+ const timer = setTimeout(() => {
52
+ this.#pending = false;
53
+ this.#timerId = null;
54
+ const dropped = this.#dropped;
55
+ this.#dropped = 0;
56
+ if (dropped > 0) {
57
+ try {
58
+ this.#onDrop(dropped);
59
+ } catch {
60
+ /* drop callback errors are non-fatal */
61
+ }
62
+ }
63
+ this.#callback();
64
+ }, this.#intervalMs);
65
+ timer.unref();
66
+ this.#timerId = timer;
67
+ }
68
+
69
+ /** Force an immediate render, bypassing coalescing. */
70
+ flush(): void {
71
+ if (this.#timerId !== null) {
72
+ clearTimeout(this.#timerId);
73
+ this.#timerId = null;
74
+ }
75
+ this.#pending = false;
76
+ const dropped = this.#dropped;
77
+ this.#dropped = 0;
78
+ if (dropped > 0) {
79
+ try {
80
+ this.#onDrop(dropped);
81
+ } catch {
82
+ /* drop callback errors are non-fatal */
83
+ }
84
+ }
85
+ this.#callback();
86
+ }
87
+
88
+ /** Check if a render is pending. */
89
+ get pending(): boolean {
90
+ return this.#pending;
91
+ }
92
+
93
+ /** Clean up timers. Call when the coalescer is no longer needed. */
94
+ dispose(): void {
95
+ if (this.#timerId !== null) {
96
+ clearTimeout(this.#timerId);
97
+ this.#timerId = null;
98
+ }
99
+ this.#pending = false;
100
+ }
101
+ }