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,160 @@
1
+ import * as Diff from "diff";
2
+ import type { CrewTheme } from "./theme-adapter.ts";
3
+ import { asCrewTheme } from "./theme-adapter.ts";
4
+
5
+ interface ParsedDiffLine {
6
+ prefix: string;
7
+ lineNum: string;
8
+ content: string;
9
+ }
10
+
11
+ interface DiffLineContent {
12
+ lineNum: string;
13
+ content: string;
14
+ }
15
+
16
+ function parseDiffLine(line: string): ParsedDiffLine | null {
17
+ const match = line.match(/^([+-\s])(\s*\d*)\s(.*)$/);
18
+ if (!match) return null;
19
+ return { prefix: match[1], lineNum: match[2], content: match[3] };
20
+ }
21
+
22
+ export function replaceTabs(text: string): string {
23
+ return text.replace(/\t/g, " ");
24
+ }
25
+
26
+ /**
27
+ * Minimum similarity (fraction of unchanged chars) required before applying
28
+ * word-level intra-line diff highlighting. Below this, the two lines are
29
+ * considered unrelated and rendered plainly to avoid noisy full-line
30
+ * inverse-video (e.g. an empty line replaced by a large block of code).
31
+ */
32
+ const WORD_DIFF_MIN_SIM = 0.15;
33
+
34
+ /**
35
+ * Compute the similarity between two strings as the fraction of unchanged
36
+ * characters relative to the longer of the two strings, using word-level
37
+ * diff to identify the common (unchanged) parts. Returns a value in [0, 1].
38
+ */
39
+ function computeSimilarity(oldContent: string, newContent: string): number {
40
+ const wordDiff = Diff.diffWords(oldContent, newContent);
41
+ let commonChars = 0;
42
+ for (const part of wordDiff) {
43
+ if (!part.removed && !part.added) {
44
+ commonChars += part.value.length;
45
+ }
46
+ }
47
+ const maxLen = Math.max(oldContent.length, newContent.length);
48
+ if (maxLen === 0) return 1; // both empty -> identical
49
+ return commonChars / maxLen;
50
+ }
51
+
52
+ function renderIntraLineDiff(theme: CrewTheme, oldContent: string, newContent: string): { removedLine: string; addedLine: string } {
53
+ const wordDiff = Diff.diffWords(oldContent, newContent);
54
+ let removedLine = "";
55
+ let addedLine = "";
56
+ let isFirstRemoved = true;
57
+ let isFirstAdded = true;
58
+
59
+ for (const part of wordDiff) {
60
+ if (part.removed) {
61
+ let value = part.value;
62
+ if (isFirstRemoved) {
63
+ const leadingWs = value.match(/^(\s*)/)?.[1] ?? "";
64
+ value = value.slice(leadingWs.length);
65
+ removedLine += leadingWs;
66
+ isFirstRemoved = false;
67
+ }
68
+ if (value) removedLine += theme.inverse?.(value) ?? value;
69
+ } else if (part.added) {
70
+ let value = part.value;
71
+ if (isFirstAdded) {
72
+ const leadingWs = value.match(/^(\s*)/)?.[1] ?? "";
73
+ value = value.slice(leadingWs.length);
74
+ addedLine += leadingWs;
75
+ isFirstAdded = false;
76
+ }
77
+ if (value) addedLine += theme.inverse?.(value) ?? value;
78
+ } else {
79
+ removedLine += part.value;
80
+ addedLine += part.value;
81
+ }
82
+ }
83
+
84
+ return { removedLine, addedLine };
85
+ }
86
+
87
+ export interface RenderDiffOptions {
88
+ filePath?: string;
89
+ theme?: unknown;
90
+ }
91
+
92
+ export function renderDiff(diffText: string, options: RenderDiffOptions = {}): string {
93
+ const theme = asCrewTheme(options.theme);
94
+ const lines = diffText.split("\n");
95
+ const result: string[] = [];
96
+ let i = 0;
97
+
98
+ while (i < lines.length) {
99
+ const line = lines[i] ?? "";
100
+ const parsed = parseDiffLine(line);
101
+ if (!parsed) {
102
+ result.push(theme.fg("toolDiffContext", line));
103
+ i++;
104
+ continue;
105
+ }
106
+
107
+ if (parsed.prefix === "-") {
108
+ const removedLines: DiffLineContent[] = [];
109
+ while (i < lines.length) {
110
+ const nextParsed = parseDiffLine(lines[i] ?? "");
111
+ if (nextParsed?.prefix !== "-") break;
112
+ removedLines.push({
113
+ lineNum: nextParsed.lineNum,
114
+ content: nextParsed.content,
115
+ });
116
+ i++;
117
+ }
118
+
119
+ const addedLines: DiffLineContent[] = [];
120
+ while (i < lines.length) {
121
+ const nextParsed = parseDiffLine(lines[i] ?? "");
122
+ if (nextParsed?.prefix !== "+") break;
123
+ addedLines.push({
124
+ lineNum: nextParsed.lineNum,
125
+ content: nextParsed.content,
126
+ });
127
+ i++;
128
+ }
129
+
130
+ if (removedLines.length === 1 && addedLines.length === 1) {
131
+ const oldContent = replaceTabs(removedLines[0]!.content);
132
+ const newContent = replaceTabs(addedLines[0]!.content);
133
+ const similarity = computeSimilarity(oldContent, newContent);
134
+ if (similarity >= WORD_DIFF_MIN_SIM) {
135
+ const { removedLine, addedLine } = renderIntraLineDiff(theme, oldContent, newContent);
136
+ result.push(theme.fg("toolDiffRemoved", `-${removedLines[0]!.lineNum} ${removedLine}`));
137
+ result.push(theme.fg("toolDiffAdded", `+${addedLines[0]!.lineNum} ${addedLine}`));
138
+ } else {
139
+ result.push(theme.fg("toolDiffRemoved", `-${removedLines[0]!.lineNum} ${oldContent}`));
140
+ result.push(theme.fg("toolDiffAdded", `+${addedLines[0]!.lineNum} ${newContent}`));
141
+ }
142
+ } else {
143
+ for (const removed of removedLines) {
144
+ result.push(theme.fg("toolDiffRemoved", `-${removed.lineNum} ${replaceTabs(removed.content)}`));
145
+ }
146
+ for (const added of addedLines) {
147
+ result.push(theme.fg("toolDiffAdded", `+${added.lineNum} ${replaceTabs(added.content)}`));
148
+ }
149
+ }
150
+ } else if (parsed.prefix === "+") {
151
+ result.push(theme.fg("toolDiffAdded", `+${parsed.lineNum} ${replaceTabs(parsed.content)}`));
152
+ i++;
153
+ } else {
154
+ result.push(theme.fg("toolDiffContext", ` ${parsed.lineNum} ${replaceTabs(parsed.content)}`));
155
+ i++;
156
+ }
157
+ }
158
+
159
+ return result.join("\n");
160
+ }
@@ -0,0 +1,278 @@
1
+ import { logInternalError } from "../utils/internal-error.ts";
2
+
3
+ export interface RenderSchedulerEventBus {
4
+ on?: (event: string, handler: (payload: unknown) => void) => (() => void) | void;
5
+ }
6
+
7
+ export interface RenderSchedulerOptions {
8
+ debounceMs?: number;
9
+ /**
10
+ * Maximum interval (ms) between auto-refreshes when no event arrives.
11
+ * Can be a function returning a dynamic interval — useful to tick fast
12
+ * while a run is active (e.g., 160ms aligned with spinner) and slow
13
+ * down when idle (e.g., 1000ms) without burning CPU.
14
+ */
15
+ fallbackMs?: number | (() => number);
16
+ events?: string[];
17
+ onInvalidate?: (payload: unknown) => void;
18
+ /**
19
+ * 1.9: coalesce `onInvalidate` calls per-runId within this window so that
20
+ * bursts of `crew.subagent.completed`-style events touching the same run
21
+ * trigger a single cache invalidate instead of N. Set to 0 to disable.
22
+ */
23
+ invalidateCoalesceMs?: number;
24
+ /**
25
+ * R1: maximum number of consecutive catch-up renders the fallback loop
26
+ * emits while idle (no real external event) before it stops re-arming.
27
+ * Prevents a hung run from rendering forever. A real event resets the
28
+ * count and re-arms the loop. Defaults to 8.
29
+ */
30
+ maxIdleFallbackRenders?: number;
31
+ }
32
+
33
+ const DEFAULT_EVENTS = [
34
+ "crew.run.created",
35
+ "crew.run.completed",
36
+ "crew.run.failed",
37
+ "crew.run.cancelled",
38
+ "crew.subagent.completed",
39
+ "crew.subagent.failed",
40
+ "crew.mailbox.updated",
41
+ "crew.mailbox.message",
42
+ ];
43
+
44
+ /** R4: max exponent for flush re-entrancy backoff (delay = debounceMs * 2^exp). */
45
+ const CAP_BACKOFF_MAX_EXP = 5;
46
+
47
+ /**
48
+ * Coordinates UI renders with debounce + fallback polling.
49
+ *
50
+ * Critical: uses recursive setTimeout instead of setInterval + a rendering
51
+ * guard (`rendering` / `pendingRender`) so that when render() takes longer
52
+ * than the fallback interval, callbacks do NOT pile up and storm the event
53
+ * loop. Instead, overlapping schedules are collapsed into a single deferred
54
+ * re-render.
55
+ */
56
+ export class RenderScheduler {
57
+ private readonly render: () => void;
58
+ private readonly onInvalidate?: (payload: unknown) => void;
59
+ private readonly debounceMs: number;
60
+ private readonly fallbackProvider: () => number;
61
+ private readonly fallbackMs: number;
62
+ private readonly invalidateCoalesceMs: number;
63
+ private readonly maxIdleFallbackRenders: number;
64
+ private debounceTimer: ReturnType<typeof setTimeout> | undefined;
65
+ private fallbackTimer: ReturnType<typeof setTimeout> | undefined;
66
+ private invalidateTimer: ReturnType<typeof setTimeout> | undefined;
67
+ /** runId → most recent payload to forward when the coalesce window flushes. */
68
+ private invalidateBuffer = new Map<string, unknown>();
69
+ private disposed = false;
70
+ private lastEventAt = 0;
71
+ private rendering = false;
72
+ private pendingRender = false;
73
+ /** R1: consecutive idle fallback renders since the last real event. */
74
+ private idleFallbackRenders = 0;
75
+ /** R4: consecutive re-entrancy cap-hits — drives exponential backoff. */
76
+ private consecutiveCapHits = 0;
77
+ private readonly unsubs: Array<() => void> = [];
78
+
79
+ constructor(events: RenderSchedulerEventBus | undefined, render: () => void, options: RenderSchedulerOptions = {}) {
80
+ this.render = render;
81
+ this.onInvalidate = options.onInvalidate;
82
+ this.debounceMs = options.debounceMs ?? 75;
83
+ const fallback = options.fallbackMs ?? 750;
84
+ this.fallbackProvider = typeof fallback === "function" ? fallback : () => fallback;
85
+ this.fallbackMs = typeof fallback === "number" ? fallback : 750;
86
+ this.invalidateCoalesceMs = options.invalidateCoalesceMs ?? 50;
87
+ this.maxIdleFallbackRenders = options.maxIdleFallbackRenders ?? 8;
88
+ for (const event of options.events ?? DEFAULT_EVENTS) this.subscribe(events, event);
89
+ this.fallbackTimer = setTimeout(() => this.fallbackLoop(), this.currentFallbackMs());
90
+ this.fallbackTimer.unref();
91
+ }
92
+
93
+ private currentFallbackMs(): number {
94
+ try {
95
+ const value = this.fallbackProvider();
96
+ return Number.isFinite(value) && value > 0 ? value : 750;
97
+ } catch (error) {
98
+ logInternalError("render-scheduler.fallbackProvider", error);
99
+ return 750;
100
+ }
101
+ }
102
+
103
+ private subscribe(events: RenderSchedulerEventBus | undefined, event: string): void {
104
+ if (!events?.on) return;
105
+ const handler = (payload: unknown): void => this.schedule(payload);
106
+ try {
107
+ const unsub = events.on(event, handler);
108
+ if (typeof unsub === "function") this.unsubs.push(unsub);
109
+ } catch (error) {
110
+ logInternalError("render-scheduler.subscribe", error, event);
111
+ }
112
+ }
113
+
114
+ /** Recursive setTimeout — avoids setInterval timer storms. */
115
+ private fallbackLoop(): void {
116
+ if (this.disposed) return;
117
+ const fallbackMs = this.currentFallbackMs();
118
+ if (Date.now() - this.lastEventAt < fallbackMs) {
119
+ // A real external event arrived recently — not idle. Keep polling.
120
+ this.idleFallbackRenders = 0;
121
+ this.fallbackTimer = setTimeout(() => this.fallbackLoop(), fallbackMs);
122
+ this.fallbackTimer.unref();
123
+ return;
124
+ }
125
+ // R1: truly idle (no real event for `fallbackMs`). `lastEventAt` is only
126
+ // advanced by real external events (schedule()), so this idle check stays
127
+ // honest — unlike the old code which called schedule() here and thereby
128
+ // reset lastEventAt, self-perpetuating a ~fallbackMs render loop forever.
129
+ if (this.idleFallbackRenders >= this.maxIdleFallbackRenders) {
130
+ // Sustained idleness — stop re-arming so a hung run does not render
131
+ // forever. A subsequent real event (schedule()) re-arms the loop.
132
+ this.fallbackTimer = undefined;
133
+ return;
134
+ }
135
+ // Emit one catch-up render WITHOUT advancing lastEventAt (no real event).
136
+ this.armDebouncedRender();
137
+ this.idleFallbackRenders += 1;
138
+ this.fallbackTimer = setTimeout(() => this.fallbackLoop(), this.currentFallbackMs());
139
+ this.fallbackTimer.unref();
140
+ }
141
+
142
+ schedule(payload?: unknown): void {
143
+ if (this.disposed) return;
144
+ this.lastEventAt = Date.now();
145
+ this.idleFallbackRenders = 0;
146
+ this.consecutiveCapHits = 0;
147
+ this.invalidate(payload);
148
+ this.armDebouncedRender();
149
+ // R1: if the fallback loop stopped itself after a sustained idle period,
150
+ // a real event means activity resumed — re-arm the safety-net loop.
151
+ if (!this.fallbackTimer) {
152
+ this.fallbackTimer = setTimeout(() => this.fallbackLoop(), this.currentFallbackMs());
153
+ this.fallbackTimer.unref();
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Arm (or re-arm) the debounce timer that triggers a single flush. Shared by
159
+ * real-event schedules, the fallback path, and the re-entrancy cap drain.
160
+ * `delay` lets the cap apply exponential backoff (R4) without touching
161
+ * `lastEventAt` or `onInvalidate` (those belong to real external events).
162
+ */
163
+ private armDebouncedRender(delay: number = this.debounceMs): void {
164
+ if (this.debounceTimer) clearTimeout(this.debounceTimer);
165
+ this.debounceTimer = setTimeout(() => {
166
+ this.debounceTimer = undefined;
167
+ this.flush();
168
+ }, delay);
169
+ this.debounceTimer.unref();
170
+ }
171
+
172
+ /**
173
+ * 1.9: forward `onInvalidate` immediately when the payload has no `runId`
174
+ * (we cannot dedup it). When it carries a runId, buffer the latest payload
175
+ * per runId and flush on a coalesce timer so high-frequency event bursts
176
+ * (`crew.subagent.completed` for parallel tasks) collapse into one
177
+ * invalidate per affected run.
178
+ */
179
+ private invalidate(payload: unknown): void {
180
+ try {
181
+ const runId =
182
+ typeof payload === "object" &&
183
+ payload !== null &&
184
+ "runId" in payload &&
185
+ typeof (payload as { runId: unknown }).runId === "string"
186
+ ? (payload as { runId: string }).runId
187
+ : undefined;
188
+ if (runId === undefined || this.invalidateCoalesceMs <= 0) {
189
+ this.onInvalidate?.(payload);
190
+ return;
191
+ }
192
+ this.invalidateBuffer.set(runId, payload);
193
+ if (!this.invalidateTimer) {
194
+ this.invalidateTimer = setTimeout(() => this.flushInvalidate(), this.invalidateCoalesceMs);
195
+ this.invalidateTimer.unref();
196
+ }
197
+ } catch (error) {
198
+ logInternalError("render-scheduler.invalidate", error);
199
+ }
200
+ }
201
+
202
+ private flushInvalidate(): void {
203
+ this.invalidateTimer = undefined;
204
+ if (this.disposed) {
205
+ this.invalidateBuffer.clear();
206
+ return;
207
+ }
208
+ const buffered = this.invalidateBuffer;
209
+ this.invalidateBuffer = new Map();
210
+ for (const payload of buffered.values()) {
211
+ try {
212
+ this.onInvalidate?.(payload);
213
+ } catch (error) {
214
+ logInternalError("render-scheduler.invalidate.flush", error);
215
+ }
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Flush a render. If a render is already in progress the request is
221
+ * collapsed: `pendingRender` is set and the caller that holds
222
+ * `rendering==true` will loop one more time after finishing.
223
+ */
224
+ flush(): void {
225
+ if (this.disposed) return;
226
+ if (this.rendering) {
227
+ this.pendingRender = true;
228
+ return;
229
+ }
230
+ this.rendering = true;
231
+ this.pendingRender = false;
232
+ let iterations = 0;
233
+ try {
234
+ do {
235
+ this.pendingRender = false;
236
+ this.render();
237
+ iterations += 1;
238
+ // Safety valve: 5 re-renders max per flush to prevent infinite loops
239
+ // if render() itself calls flush() synchronously.
240
+ } while (this.pendingRender && !this.disposed && iterations < 5);
241
+ } catch (error) {
242
+ logInternalError("render-scheduler.render", error);
243
+ } finally {
244
+ this.rendering = false;
245
+ // R4: if we hit the re-entrancy cap with more work pending, drain it —
246
+ // but with exponential backoff on consecutive cap-hits so a pathological
247
+ // render() cannot sustain a tight debounceMs render loop. We re-arm via
248
+ // armDebouncedRender (not schedule()) so lastEventAt / onInvalidate are
249
+ // not perturbed by a pure re-entrancy drain.
250
+ if (iterations >= 5 && this.pendingRender && !this.disposed) {
251
+ this.consecutiveCapHits += 1;
252
+ const exp = Math.min(this.consecutiveCapHits - 1, CAP_BACKOFF_MAX_EXP);
253
+ this.armDebouncedRender(this.debounceMs * 2 ** exp);
254
+ } else {
255
+ this.consecutiveCapHits = 0;
256
+ }
257
+ }
258
+ }
259
+
260
+ dispose(): void {
261
+ if (this.disposed) return;
262
+ this.disposed = true;
263
+ if (this.debounceTimer) clearTimeout(this.debounceTimer);
264
+ if (this.fallbackTimer) clearTimeout(this.fallbackTimer);
265
+ if (this.invalidateTimer) clearTimeout(this.invalidateTimer);
266
+ this.debounceTimer = undefined;
267
+ this.fallbackTimer = undefined;
268
+ this.invalidateTimer = undefined;
269
+ this.invalidateBuffer.clear();
270
+ for (const unsub of this.unsubs.splice(0)) {
271
+ try {
272
+ unsub();
273
+ } catch (error) {
274
+ logInternalError("render-scheduler.unsubscribe", error);
275
+ }
276
+ }
277
+ }
278
+ }
@@ -0,0 +1,182 @@
1
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
+ // Lazy-loaded: team-tool.ts pulls in entire runtime chain.
3
+ import type { handleTeamTool as HandleTeamToolFn } from "../extension/team-tool.ts";
4
+ import type { MetricRegistry } from "../observability/metric-registry.ts";
5
+
6
+ let _cachedHandleTeamTool: typeof HandleTeamToolFn | undefined;
7
+ async function handleTeamTool(
8
+ params: Parameters<typeof HandleTeamToolFn>[0],
9
+ ctx: Parameters<typeof HandleTeamToolFn>[1],
10
+ ): Promise<Awaited<ReturnType<typeof HandleTeamToolFn>>> {
11
+ if (!_cachedHandleTeamTool) {
12
+ // LAZY: avoid pulling team-tool.ts (and its entire runtime chain) into module load.
13
+ const mod = await import("../extension/team-tool.ts");
14
+ _cachedHandleTeamTool = mod.handleTeamTool;
15
+ }
16
+ return _cachedHandleTeamTool(params, ctx);
17
+ }
18
+
19
+ import { isToolError, textFromToolResult } from "../extension/tool-result.ts";
20
+ import { readCrewAgents } from "../runtime/crew-agent-records.ts";
21
+ import { exportDiagnostic } from "../runtime/diagnostic-export.ts";
22
+ import { appendEvent } from "../state/event-log.ts";
23
+ import type { MailboxDirection, MailboxMessage } from "../state/mailbox.ts";
24
+ import { loadRunManifestById, saveRunTasks } from "../state/state-store.ts";
25
+
26
+ export interface RunActionResult {
27
+ ok: boolean;
28
+ message: string;
29
+ data?: unknown;
30
+ }
31
+
32
+ function okFromTool(result: Awaited<ReturnType<typeof handleTeamTool>>): RunActionResult {
33
+ return {
34
+ ok: !isToolError(result),
35
+ message: textFromToolResult(result),
36
+ data: result,
37
+ };
38
+ }
39
+
40
+ function err(error: unknown): RunActionResult {
41
+ return {
42
+ ok: false,
43
+ message: error instanceof Error ? error.message : String(error),
44
+ };
45
+ }
46
+
47
+ async function dispatchApi(ctx: ExtensionContext, runId: string, config: Record<string, unknown>): Promise<RunActionResult> {
48
+ try {
49
+ return okFromTool(await handleTeamTool({ action: "api", runId, config }, ctx));
50
+ } catch (error) {
51
+ return err(error);
52
+ }
53
+ }
54
+
55
+ function parseMailboxMessages(text: string): MailboxMessage[] {
56
+ try {
57
+ const parsed = JSON.parse(text) as unknown;
58
+ if (!Array.isArray(parsed)) return [];
59
+ return parsed.filter(
60
+ (item): item is MailboxMessage =>
61
+ Boolean(item) && typeof item === "object" && !Array.isArray(item) && typeof (item as { id?: unknown }).id === "string",
62
+ );
63
+ } catch {
64
+ return [];
65
+ }
66
+ }
67
+
68
+ export function dispatchMailboxAck(ctx: ExtensionContext, runId: string, messageId: string): Promise<RunActionResult> {
69
+ return dispatchApi(ctx, runId, { operation: "ack-message", messageId });
70
+ }
71
+
72
+ export function dispatchMailboxNudge(ctx: ExtensionContext, runId: string, agentId: string, message: string): Promise<RunActionResult> {
73
+ return dispatchApi(ctx, runId, {
74
+ operation: "nudge-agent",
75
+ agentId,
76
+ message,
77
+ });
78
+ }
79
+
80
+ export function dispatchMailboxCompose(
81
+ ctx: ExtensionContext,
82
+ runId: string,
83
+ payload: {
84
+ from: string;
85
+ to: string;
86
+ body: string;
87
+ taskId?: string;
88
+ direction: MailboxDirection;
89
+ },
90
+ ): Promise<RunActionResult> {
91
+ return dispatchApi(ctx, runId, { operation: "send-message", ...payload });
92
+ }
93
+
94
+ export async function dispatchMailboxAckAll(ctx: ExtensionContext, runId: string): Promise<RunActionResult> {
95
+ const listed = await dispatchApi(ctx, runId, {
96
+ operation: "read-mailbox",
97
+ direction: "inbox",
98
+ });
99
+ if (!listed.ok) return listed;
100
+ const messages = parseMailboxMessages(listed.message).filter((message) => message.status !== "acknowledged");
101
+ let count = 0;
102
+ for (const message of messages) {
103
+ const acked = await dispatchMailboxAck(ctx, runId, message.id);
104
+ if (!acked.ok)
105
+ return {
106
+ ok: false,
107
+ message: `Acknowledged ${count}/${messages.length}; failed ${message.id}: ${acked.message}`,
108
+ };
109
+ count += 1;
110
+ }
111
+ return {
112
+ ok: true,
113
+ message: `Acknowledged ${count} messages.`,
114
+ data: { count },
115
+ };
116
+ }
117
+
118
+ export function dispatchHealthRecovery(ctx: ExtensionContext, runId: string): Promise<RunActionResult> {
119
+ return dispatchApi(ctx, runId, {
120
+ operation: "foreground-interrupt",
121
+ reason: "operator health recovery",
122
+ });
123
+ }
124
+
125
+ export async function dispatchKillStaleWorkers(ctx: ExtensionContext, runId: string): Promise<RunActionResult> {
126
+ try {
127
+ const loaded = loadRunManifestById(ctx.cwd, runId); // NOTE: no withRunLock - best-effort only; concurrent writes may cause inconsistency;
128
+ if (!loaded) return { ok: false, message: `Run '${runId}' not found.` };
129
+ const currentMs = Date.now();
130
+ const staleMs = 60_000;
131
+ const now = new Date(currentMs).toISOString();
132
+ let count = 0;
133
+ const tasks = loaded.tasks.map((task) => {
134
+ if ((task.status !== "running" && task.status !== "queued") || !task.heartbeat || task.heartbeat.alive === false) return task;
135
+ const lastSeenMs = Date.parse(task.heartbeat.lastSeenAt);
136
+ if (!Number.isFinite(lastSeenMs) || currentMs - lastSeenMs <= staleMs) return task;
137
+ count += 1;
138
+ return {
139
+ ...task,
140
+ heartbeat: { ...task.heartbeat, alive: false, lastSeenAt: now },
141
+ };
142
+ });
143
+ saveRunTasks(loaded.manifest, tasks);
144
+ appendEvent(loaded.manifest.eventsPath, {
145
+ type: "worker.kill_stale",
146
+ runId,
147
+ message: `Marked ${count} stale worker heartbeat(s) dead.`,
148
+ data: { count },
149
+ });
150
+ return {
151
+ ok: true,
152
+ message: `Marked ${count} stale worker heartbeat(s) dead.`,
153
+ data: { count },
154
+ };
155
+ } catch (error) {
156
+ return err(error);
157
+ }
158
+ }
159
+
160
+ export async function dispatchDiagnosticExport(
161
+ ctx: ExtensionContext,
162
+ runId: string,
163
+ options: { registry?: MetricRegistry } = {},
164
+ ): Promise<RunActionResult> {
165
+ try {
166
+ const exported = await exportDiagnostic(ctx, runId, options);
167
+ return {
168
+ ok: true,
169
+ message: `Diagnostic exported to ${exported.path}`,
170
+ data: exported.path,
171
+ };
172
+ } catch (error) {
173
+ return err(error);
174
+ }
175
+ }
176
+
177
+ /** @internal */
178
+ function defaultNudgeAgentId(ctx: Pick<ExtensionContext, "cwd">, runId: string): string | undefined {
179
+ const loaded = loadRunManifestById(ctx.cwd, runId); // NOTE: no withRunLock - best-effort only; concurrent writes may cause inconsistency;
180
+ if (!loaded) return undefined;
181
+ return readCrewAgents(loaded.manifest).find((agent) => agent.status === "running" || agent.status === "queued")?.taskId;
182
+ }