pi-crew 0.9.15 → 0.9.17

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 (432) hide show
  1. package/CHANGELOG.md +145 -0
  2. package/README.md +19 -0
  3. package/dist/build-meta.json +21790 -0
  4. package/dist/index.mjs +83070 -0
  5. package/dist/index.mjs.map +7 -0
  6. package/docs/A +358 -0
  7. package/docs/M-A +357 -0
  8. package/docs/REVIEW-FINDINGS-2026-06 +357 -0
  9. package/docs/Y +357 -0
  10. package/docs/a +357 -0
  11. package/docs/aA +358 -0
  12. package/docs/archive/README.md +91 -0
  13. package/docs/patterns/command-agent-skill.md +1 -1
  14. package/docs/skills/REFERENCE.md +0 -17
  15. package/docs//303/242mmaAAA/303/242 +357 -0
  16. package/index.bundle.ts +25 -0
  17. package/index.ts +95 -4
  18. package/package.json +15 -4
  19. package/skills/iterative-audit/SKILL.md +0 -1
  20. package/skills/widget-rendering/SKILL.md +17 -0
  21. package/src/adapters/claude-adapter.ts +1 -3
  22. package/src/adapters/export-util.ts +16 -16
  23. package/src/adapters/index.ts +5 -5
  24. package/src/agents/agent-config.ts +20 -14
  25. package/src/agents/agent-serializer.ts +1 -1
  26. package/src/agents/discover-agents.ts +83 -39
  27. package/src/benchmark/benchmark-runner.ts +51 -51
  28. package/src/benchmark/feedback-loop.ts +1 -1
  29. package/src/config/config.ts +166 -590
  30. package/src/config/defaults.ts +28 -2
  31. package/src/config/drift-detector.ts +11 -14
  32. package/src/config/markers.ts +201 -203
  33. package/src/config/resilient-parser.ts +14 -6
  34. package/src/config/role-tools.ts +3 -3
  35. package/src/config/suggestions.ts +5 -12
  36. package/src/config/types.ts +9 -25
  37. package/src/errors.ts +151 -157
  38. package/src/extension/action-suggestions.ts +54 -9
  39. package/src/extension/async-notifier.ts +43 -13
  40. package/src/extension/autonomous-policy.ts +77 -37
  41. package/src/extension/command-completions.ts +9 -8
  42. package/src/extension/context-status-injection.ts +14 -12
  43. package/src/extension/crew-autocomplete.ts +8 -16
  44. package/src/extension/crew-cleanup.ts +2 -1
  45. package/src/extension/crew-shortcuts.ts +9 -6
  46. package/src/extension/cross-extension-rpc.ts +136 -42
  47. package/src/extension/help.ts +1 -1
  48. package/src/extension/import-index.ts +10 -5
  49. package/src/extension/knowledge-injection.ts +88 -15
  50. package/src/extension/management.ts +89 -349
  51. package/src/extension/message-renderers.ts +3 -4
  52. package/src/extension/notification-router.ts +22 -5
  53. package/src/extension/notification-sink.ts +6 -3
  54. package/src/extension/pi-api.ts +17 -8
  55. package/src/extension/plan-orchestrate.ts +8 -27
  56. package/src/extension/project-init.ts +21 -6
  57. package/src/extension/register.ts +329 -975
  58. package/src/extension/registration/artifact-cleanup.ts +9 -4
  59. package/src/extension/registration/command-utils.ts +5 -1
  60. package/src/extension/registration/commands.ts +806 -314
  61. package/src/extension/registration/compaction-guard.ts +15 -15
  62. package/src/extension/registration/lifecycle.ts +259 -0
  63. package/src/extension/registration/observability.ts +293 -0
  64. package/src/extension/registration/subagent-helpers.ts +29 -7
  65. package/src/extension/registration/subagent-tools.ts +253 -44
  66. package/src/extension/registration/team-tool.ts +19 -84
  67. package/src/extension/registration/ui.ts +168 -0
  68. package/src/extension/registration/viewers.ts +64 -24
  69. package/src/extension/result-watcher.ts +18 -7
  70. package/src/extension/run-bundle-schema.ts +21 -6
  71. package/src/extension/run-export.ts +15 -6
  72. package/src/extension/run-import.ts +64 -29
  73. package/src/extension/run-index.ts +28 -9
  74. package/src/extension/run-maintenance.ts +32 -12
  75. package/src/extension/session-summary.ts +1 -1
  76. package/src/extension/team-manager-command.ts +64 -7
  77. package/src/extension/team-onboard.ts +151 -150
  78. package/src/extension/team-recommendation.ts +121 -21
  79. package/src/extension/team-tool/anchor.ts +31 -64
  80. package/src/extension/team-tool/api.ts +929 -141
  81. package/src/extension/team-tool/auto-summarize.ts +14 -26
  82. package/src/extension/team-tool/cache-control.ts +1 -5
  83. package/src/extension/team-tool/cancel.ts +138 -38
  84. package/src/extension/team-tool/chain-dispatch.ts +5 -13
  85. package/src/extension/team-tool/chain-executor.ts +29 -51
  86. package/src/extension/team-tool/config-patch.ts +1 -1
  87. package/src/extension/team-tool/context.ts +40 -20
  88. package/src/extension/team-tool/destructive-gate.ts +10 -5
  89. package/src/extension/team-tool/doctor.ts +170 -51
  90. package/src/extension/team-tool/explain.ts +228 -214
  91. package/src/extension/team-tool/failure-patterns.ts +3 -9
  92. package/src/extension/team-tool/goal-wrap.ts +71 -29
  93. package/src/extension/team-tool/goal.ts +185 -35
  94. package/src/extension/team-tool/handle-schedule.ts +34 -15
  95. package/src/extension/team-tool/handle-settings.ts +94 -56
  96. package/src/extension/team-tool/health-monitor.ts +27 -108
  97. package/src/extension/team-tool/inspect.ts +42 -9
  98. package/src/extension/team-tool/intent-policy.ts +4 -1
  99. package/src/extension/team-tool/lifecycle-actions.ts +244 -48
  100. package/src/extension/team-tool/orchestrate.ts +7 -18
  101. package/src/extension/team-tool/parallel-dispatch.ts +51 -29
  102. package/src/extension/team-tool/plan.ts +29 -5
  103. package/src/extension/team-tool/respond.ts +49 -16
  104. package/src/extension/team-tool/run-not-found.ts +3 -8
  105. package/src/extension/team-tool/run.ts +80 -317
  106. package/src/extension/team-tool/status.ts +135 -43
  107. package/src/extension/team-tool/workflow-manage.ts +92 -25
  108. package/src/extension/team-tool-types.ts +8 -1
  109. package/src/extension/team-tool.ts +142 -551
  110. package/src/extension/validate-resources.ts +39 -8
  111. package/src/hooks/registry.ts +66 -17
  112. package/src/hooks/types.ts +1 -1
  113. package/src/i18n.ts +25 -11
  114. package/src/observability/correlation.ts +17 -3
  115. package/src/observability/event-bus.ts +51 -56
  116. package/src/observability/event-to-metric.ts +117 -15
  117. package/src/observability/exporters/otlp-exporter.ts +56 -17
  118. package/src/observability/exporters/prometheus-exporter.ts +1 -1
  119. package/src/observability/metric-registry.ts +16 -4
  120. package/src/observability/metric-retention.ts +8 -2
  121. package/src/observability/metric-sink.ts +11 -3
  122. package/src/observability/metrics-primitives.ts +42 -9
  123. package/src/plugins/plugin-define.ts +2 -2
  124. package/src/plugins/plugin-registry.ts +25 -25
  125. package/src/plugins/plugins/index.ts +1 -1
  126. package/src/plugins/plugins/nextjs.ts +16 -16
  127. package/src/plugins/plugins/vite.ts +7 -11
  128. package/src/plugins/plugins/vitest.ts +6 -11
  129. package/src/runtime/adaptive-plan.ts +225 -40
  130. package/src/runtime/agent-control.ts +58 -15
  131. package/src/runtime/agent-memory.ts +15 -9
  132. package/src/runtime/agent-observability.ts +10 -2
  133. package/src/runtime/anchor-manager.ts +27 -25
  134. package/src/runtime/async-marker.ts +7 -1
  135. package/src/runtime/async-runner.ts +27 -23
  136. package/src/runtime/auto-summarize.ts +7 -13
  137. package/src/runtime/background-runner.ts +189 -251
  138. package/src/runtime/batch-barrier.ts +18 -16
  139. package/src/runtime/cancellation-token.ts +16 -6
  140. package/src/runtime/cancellation.ts +46 -8
  141. package/src/runtime/capability-inventory.ts +6 -4
  142. package/src/runtime/chain-parser.ts +44 -11
  143. package/src/runtime/chain-runner.ts +63 -69
  144. package/src/runtime/checkpoint.ts +12 -56
  145. package/src/runtime/child-pi.ts +343 -78
  146. package/src/runtime/coalesce-tasks.ts +268 -0
  147. package/src/runtime/code-summary.ts +71 -26
  148. package/src/runtime/compact-stages/index.ts +15 -5
  149. package/src/runtime/compact-stages/tail-capture-stage.ts +7 -2
  150. package/src/runtime/compact-stages/truncation-stage.ts +4 -1
  151. package/src/runtime/compaction-summary.ts +17 -10
  152. package/src/runtime/completion-guard.ts +33 -16
  153. package/src/runtime/crash-classification.ts +28 -7
  154. package/src/runtime/crash-recovery.ts +177 -37
  155. package/src/runtime/crew-agent-records.ts +193 -40
  156. package/src/runtime/crew-hooks.ts +9 -24
  157. package/src/runtime/cross-extension-rpc.ts +25 -23
  158. package/src/runtime/custom-tools/irc-tool.ts +43 -15
  159. package/src/runtime/custom-tools/submit-result-tool.ts +16 -5
  160. package/src/runtime/delivery-coordinator.ts +34 -7
  161. package/src/runtime/delta-conflict.ts +9 -21
  162. package/src/runtime/deterministic-ast.ts +1 -1
  163. package/src/runtime/diagnostic-export.ts +53 -20
  164. package/src/runtime/direct-run.ts +12 -2
  165. package/src/runtime/dwf-state-store.ts +1 -1
  166. package/src/runtime/dynamic-workflow-context.ts +186 -58
  167. package/src/runtime/dynamic-workflow-runner.ts +42 -20
  168. package/src/runtime/effectiveness.ts +16 -8
  169. package/src/runtime/errors/crew-errors.ts +7 -11
  170. package/src/runtime/event-stream-bridge.ts +9 -3
  171. package/src/runtime/foreground-control.ts +54 -14
  172. package/src/runtime/foreground-watchdog.ts +9 -6
  173. package/src/runtime/goal-achievement.ts +27 -10
  174. package/src/runtime/goal-evaluator.ts +54 -19
  175. package/src/runtime/goal-loop-runner.ts +280 -99
  176. package/src/runtime/goal-state-store.ts +27 -17
  177. package/src/runtime/green-contract.ts +11 -2
  178. package/src/runtime/group-join.ts +64 -31
  179. package/src/runtime/handoff-manager.ts +37 -42
  180. package/src/runtime/heartbeat-gradient.ts +10 -2
  181. package/src/runtime/heartbeat-watcher.ts +32 -6
  182. package/src/runtime/hidden-handoff.ts +12 -30
  183. package/src/runtime/important-line-classifier.ts +12 -2
  184. package/src/runtime/iteration-hooks.ts +25 -15
  185. package/src/runtime/live-agent-control.ts +55 -7
  186. package/src/runtime/live-agent-manager.ts +130 -27
  187. package/src/runtime/live-control-realtime.ts +23 -3
  188. package/src/runtime/live-irc.ts +4 -1
  189. package/src/runtime/live-session-health.ts +12 -4
  190. package/src/runtime/live-session-runtime.ts +379 -117
  191. package/src/runtime/manifest-cache.ts +28 -12
  192. package/src/runtime/mcp-proxy.ts +8 -16
  193. package/src/runtime/metric-parser.ts +1 -5
  194. package/src/runtime/model-fallback.ts +55 -18
  195. package/src/runtime/model-resolver.ts +2 -6
  196. package/src/runtime/model-scope.ts +19 -3
  197. package/src/runtime/notebook-helpers.ts +60 -62
  198. package/src/runtime/orphan-worker-registry.ts +37 -26
  199. package/src/runtime/output-validator.ts +18 -3
  200. package/src/runtime/overflow-recovery.ts +5 -4
  201. package/src/runtime/parallel-research.ts +29 -5
  202. package/src/runtime/parallel-utils.ts +9 -11
  203. package/src/runtime/path-overlap.ts +150 -0
  204. package/src/runtime/peer-dep.ts +12 -17
  205. package/src/runtime/per-write-validator.ts +1 -3
  206. package/src/runtime/phase-tracker.ts +342 -330
  207. package/src/runtime/pi-args.ts +26 -8
  208. package/src/runtime/pi-json-output.ts +1 -1
  209. package/src/runtime/pi-spawn.ts +30 -19
  210. package/src/runtime/pipeline-runner.ts +56 -55
  211. package/src/runtime/plan-templates.ts +5 -6
  212. package/src/runtime/policy-engine.ts +43 -7
  213. package/src/runtime/post-checks.ts +12 -4
  214. package/src/runtime/post-exit-stdio-guard.ts +2 -2
  215. package/src/runtime/process-lifecycle.ts +20 -10
  216. package/src/runtime/process-status.ts +16 -5
  217. package/src/runtime/progress-event-coalescer.ts +2 -1
  218. package/src/runtime/progress-tracker.ts +103 -103
  219. package/src/runtime/prose-compressor.ts +9 -11
  220. package/src/runtime/recovery-recipes.ts +118 -24
  221. package/src/runtime/replace.ts +25 -10
  222. package/src/runtime/resilient-edit.ts +10 -25
  223. package/src/runtime/result-extractor.ts +1 -2
  224. package/src/runtime/retry-executor.ts +20 -4
  225. package/src/runtime/retry-runner.ts +26 -50
  226. package/src/runtime/role-permission.ts +6 -1
  227. package/src/runtime/run-coalesced-task-group.ts +256 -0
  228. package/src/runtime/run-drift.ts +14 -15
  229. package/src/runtime/run-tracker.ts +6 -28
  230. package/src/runtime/runtime-policy.ts +1 -6
  231. package/src/runtime/runtime-resolver.ts +80 -15
  232. package/src/runtime/scheduler.ts +47 -18
  233. package/src/runtime/semaphore.ts +5 -7
  234. package/src/runtime/sensitive-paths.ts +2 -1
  235. package/src/runtime/session-usage.ts +1 -1
  236. package/src/runtime/settings-store.ts +16 -12
  237. package/src/runtime/sidechain-output.ts +6 -2
  238. package/src/runtime/single-agent-compose.ts +1 -4
  239. package/src/runtime/skill-effectiveness.ts +29 -97
  240. package/src/runtime/skill-instructions.ts +40 -83
  241. package/src/runtime/stale-reconciler.ts +73 -197
  242. package/src/runtime/stream-preview.ts +1 -1
  243. package/src/runtime/streaming-output.ts +1 -1
  244. package/src/runtime/subagent-manager.ts +86 -169
  245. package/src/runtime/subprocess-tool-registry.ts +4 -1
  246. package/src/runtime/supervisor-contact.ts +10 -5
  247. package/src/runtime/task-display.ts +19 -5
  248. package/src/runtime/task-graph-scheduler.ts +95 -21
  249. package/src/runtime/task-graph.ts +5 -11
  250. package/src/runtime/task-health.ts +54 -47
  251. package/src/runtime/task-id.ts +11 -18
  252. package/src/runtime/task-output-context.ts +265 -81
  253. package/src/runtime/task-packet.ts +12 -20
  254. package/src/runtime/task-quality.ts +6 -14
  255. package/src/runtime/task-runner/context-retrieval.ts +4 -13
  256. package/src/runtime/task-runner/live-executor.ts +114 -36
  257. package/src/runtime/task-runner/output-splitter.ts +152 -0
  258. package/src/runtime/task-runner/progress.ts +50 -12
  259. package/src/runtime/task-runner/prompt-builder.ts +31 -7
  260. package/src/runtime/task-runner/prompt-pipeline.ts +31 -7
  261. package/src/runtime/task-runner/result-utils.ts +3 -1
  262. package/src/runtime/task-runner/retrieval-orchestrator.ts +310 -0
  263. package/src/runtime/task-runner/run-projection.ts +27 -8
  264. package/src/runtime/task-runner/state-helpers.ts +29 -8
  265. package/src/runtime/task-runner/tail-read.ts +2 -6
  266. package/src/runtime/task-runner.ts +180 -326
  267. package/src/runtime/team-runner-artifacts.ts +13 -0
  268. package/src/runtime/team-runner.ts +456 -974
  269. package/src/runtime/tool-output-pruner.ts +6 -9
  270. package/src/runtime/tool-progress.ts +11 -21
  271. package/src/runtime/verification-gates.ts +33 -24
  272. package/src/runtime/verification-integrity.ts +1 -4
  273. package/src/runtime/verification-worktree.ts +62 -12
  274. package/src/runtime/worker-heartbeat.ts +5 -1
  275. package/src/runtime/worker-startup.ts +29 -5
  276. package/src/runtime/workflow-state.ts +17 -9
  277. package/src/runtime/workspace-lock.ts +30 -35
  278. package/src/runtime/workspace-tree.ts +20 -32
  279. package/src/runtime/yield-handler.ts +43 -9
  280. package/src/runtime/zombie-scanner.ts +5 -4
  281. package/src/schema/config-schema.ts +266 -172
  282. package/src/schema/team-tool-schema.ts +29 -74
  283. package/src/schema/validation-types.ts +25 -17
  284. package/src/skills/discover-skills.ts +35 -10
  285. package/src/skills/skill-templates.ts +109 -27
  286. package/src/skills/validate.ts +26 -8
  287. package/src/state/active-run-registry.ts +87 -28
  288. package/src/state/artifact-store.ts +9 -5
  289. package/src/state/atomic-write-v2.ts +85 -63
  290. package/src/state/atomic-write.ts +105 -26
  291. package/src/state/blob-store.ts +47 -20
  292. package/src/state/contracts.ts +20 -5
  293. package/src/state/crew-init.ts +5 -22
  294. package/src/state/decision-ledger.ts +19 -73
  295. package/src/state/event-log-rotation.ts +41 -15
  296. package/src/state/event-log.ts +168 -53
  297. package/src/state/event-reconstructor.ts +11 -1
  298. package/src/state/gitignore-manager.ts +2 -8
  299. package/src/state/health-store.ts +57 -57
  300. package/src/state/hook-instinct-bridge.ts +1 -1
  301. package/src/state/hook-integrations.ts +1 -1
  302. package/src/state/instinct-store.ts +17 -5
  303. package/src/state/jsonl-writer.ts +1 -1
  304. package/src/state/locks.ts +23 -9
  305. package/src/state/mailbox.ts +151 -28
  306. package/src/state/observation-store.ts +20 -14
  307. package/src/state/run-cache.ts +142 -135
  308. package/src/state/run-graph.ts +6 -15
  309. package/src/state/run-metrics.ts +5 -18
  310. package/src/state/schedule.ts +15 -9
  311. package/src/state/state-store.ts +129 -45
  312. package/src/state/task-claims.ts +12 -2
  313. package/src/state/tiered-eval.ts +52 -43
  314. package/src/state/types-eval.ts +2 -2
  315. package/src/state/types.ts +24 -20
  316. package/src/state/usage.ts +20 -4
  317. package/src/state/worker-atomic-writer.ts +6 -3
  318. package/src/subagents/index.ts +2 -2
  319. package/src/teams/discover-teams.ts +21 -6
  320. package/src/tools/safe-bash-extension.ts +5 -6
  321. package/src/tools/safe-bash.ts +10 -11
  322. package/src/types/new-api-types.ts +6 -10
  323. package/src/ui/agent-management-overlay.ts +52 -40
  324. package/src/ui/card-colors.ts +13 -4
  325. package/src/ui/crew-footer.ts +8 -7
  326. package/src/ui/crew-select-list.ts +1 -1
  327. package/src/ui/dashboard-panes/agents-pane.ts +41 -25
  328. package/src/ui/dashboard-panes/cancellation-pane.ts +1 -1
  329. package/src/ui/dashboard-panes/capability-pane.ts +32 -15
  330. package/src/ui/dashboard-panes/health-pane.ts +2 -1
  331. package/src/ui/dashboard-panes/metrics-pane.ts +4 -1
  332. package/src/ui/dashboard-panes/progress-pane.ts +12 -9
  333. package/src/ui/heartbeat-aggregator.ts +15 -4
  334. package/src/ui/keybinding-map.ts +40 -7
  335. package/src/ui/live-conversation-overlay.ts +36 -20
  336. package/src/ui/live-duration.ts +1 -4
  337. package/src/ui/live-run-sidebar.ts +88 -25
  338. package/src/ui/loaders.ts +2 -8
  339. package/src/ui/mascot.ts +20 -36
  340. package/src/ui/overlays/agent-picker-overlay.ts +11 -3
  341. package/src/ui/overlays/confirm-overlay.ts +4 -2
  342. package/src/ui/overlays/help-overlay.ts +25 -14
  343. package/src/ui/overlays/mailbox-compose-overlay.ts +48 -11
  344. package/src/ui/overlays/mailbox-compose-preview.ts +20 -5
  345. package/src/ui/overlays/mailbox-detail-overlay.ts +27 -6
  346. package/src/ui/pi-ui-compat.ts +7 -7
  347. package/src/ui/powerbar-publisher.ts +120 -42
  348. package/src/ui/render-diff.ts +10 -3
  349. package/src/ui/render-scheduler.ts +12 -4
  350. package/src/ui/run-action-dispatcher.ts +81 -18
  351. package/src/ui/run-dashboard.ts +172 -76
  352. package/src/ui/run-event-bus.ts +59 -37
  353. package/src/ui/run-snapshot-cache.ts +276 -86
  354. package/src/ui/settings-overlay.ts +361 -74
  355. package/src/ui/status-colors.ts +12 -1
  356. package/src/ui/syntax-highlight.ts +1 -1
  357. package/src/ui/theme-adapter.ts +16 -14
  358. package/src/ui/theme-discovery.ts +13 -2
  359. package/src/ui/tool-progress-formatter.ts +7 -7
  360. package/src/ui/tool-render.ts +128 -56
  361. package/src/ui/tool-renderers/brief-mode.ts +45 -27
  362. package/src/ui/tool-renderers/index.ts +109 -43
  363. package/src/ui/transcript-cache.ts +32 -6
  364. package/src/ui/transcript-entries.ts +25 -23
  365. package/src/ui/transcript-viewer.ts +78 -29
  366. package/src/ui/widget/index.ts +99 -40
  367. package/src/ui/widget/widget-formatters.ts +13 -6
  368. package/src/ui/widget/widget-model.ts +19 -8
  369. package/src/ui/widget/widget-renderer.ts +23 -13
  370. package/src/ui/widget/widget-types.ts +1 -1
  371. package/src/utils/bm25-search.ts +199 -199
  372. package/src/utils/conflict-detect.ts +22 -21
  373. package/src/utils/env-filter.ts +14 -7
  374. package/src/utils/file-coalescer.ts +5 -1
  375. package/src/utils/fingerprint.ts +3 -6
  376. package/src/utils/frontmatter.ts +3 -1
  377. package/src/utils/fs-watch.ts +2 -6
  378. package/src/utils/gh-protocol.ts +119 -44
  379. package/src/utils/git.ts +13 -15
  380. package/src/utils/guards.ts +2 -5
  381. package/src/utils/ids.ts +9 -2
  382. package/src/utils/incremental-reader.ts +14 -3
  383. package/src/utils/internal-error.ts +2 -1
  384. package/src/utils/names.ts +12 -3
  385. package/src/utils/paths.ts +49 -5
  386. package/src/utils/project-detector.ts +2 -2
  387. package/src/utils/redaction.ts +29 -19
  388. package/src/utils/resolve-shell.ts +9 -7
  389. package/src/utils/run-watcher-registry.ts +19 -31
  390. package/src/utils/safe-paths.ts +45 -24
  391. package/src/utils/scan-cache.ts +9 -2
  392. package/src/utils/session-utils.ts +2 -4
  393. package/src/utils/sleep.ts +12 -5
  394. package/src/utils/sse-parser.ts +5 -17
  395. package/src/utils/visual.ts +52 -32
  396. package/src/workflows/discover-workflows.ts +41 -109
  397. package/src/workflows/intermediate-store.ts +5 -21
  398. package/src/workflows/preflight-validator.ts +6 -33
  399. package/src/workflows/topology-analyzer.ts +5 -21
  400. package/src/workflows/workflow-config.ts +7 -6
  401. package/src/worktree/branch-freshness.ts +66 -8
  402. package/src/worktree/cleanup.ts +130 -20
  403. package/src/worktree/worktree-manager.ts +212 -69
  404. package/skills/artifact-analysis-loop/SKILL.md +0 -303
  405. package/skills/detection-pipeline-design/SKILL.md +0 -286
  406. package/skills/hunting-investigation-loop/SKILL.md +0 -402
  407. package/skills/incident-playbook-construction/SKILL.md +0 -384
  408. package/skills/security-review/SKILL.md +0 -561
  409. package/skills/threat-hypothesis-framework/SKILL.md +0 -176
  410. package/skills/ui-render-performance/SKILL.md +0 -58
  411. /package/docs/{followup-review-round3-2026-05-12.md → archive/followup-review-round3-2026-05-12.md} +0 -0
  412. /package/docs/{followup-review-round4-2026-05-13.md → archive/followup-review-round4-2026-05-13.md} +0 -0
  413. /package/docs/{pi-crew-bugs.md → archive/pi-crew-bugs.md} +0 -0
  414. /package/docs/{pi-crew-test-final.md → archive/pi-crew-test-final.md} +0 -0
  415. /package/docs/{pi-crew-test-results.md → archive/pi-crew-test-results.md} +0 -0
  416. /package/docs/{pi-crew-test-round2.md → archive/pi-crew-test-round2.md} +0 -0
  417. /package/docs/{pi-crew-test-round4.md → archive/pi-crew-test-round4.md} +0 -0
  418. /package/docs/{pi-crew-test-round5.md → archive/pi-crew-test-round5.md} +0 -0
  419. /package/docs/{pi-crew-test-round6.md → archive/pi-crew-test-round6.md} +0 -0
  420. /package/docs/{pi-crew-v0.5.10-audit-fix-plan.md → archive/pi-crew-v0.5.10-audit-fix-plan.md} +0 -0
  421. /package/docs/{pi-crew-v0.5.11-audit-fix-plan.md → archive/pi-crew-v0.5.11-audit-fix-plan.md} +0 -0
  422. /package/docs/{pi-crew-v0.5.12-audit-fix-plan.md → archive/pi-crew-v0.5.12-audit-fix-plan.md} +0 -0
  423. /package/docs/{pi-crew-v0.5.13-audit-fix-plan.md → archive/pi-crew-v0.5.13-audit-fix-plan.md} +0 -0
  424. /package/docs/{pi-crew-v0.5.14-audit-fix-plan.md → archive/pi-crew-v0.5.14-audit-fix-plan.md} +0 -0
  425. /package/docs/{pi-crew-v0.5.16-audit-fix-plan.md → archive/pi-crew-v0.5.16-audit-fix-plan.md} +0 -0
  426. /package/docs/{pi-crew-v0.5.17-audit-fix-plan.md → archive/pi-crew-v0.5.17-audit-fix-plan.md} +0 -0
  427. /package/docs/{pi-crew-v0.5.5-audit-fix-plan.md → archive/pi-crew-v0.5.5-audit-fix-plan.md} +0 -0
  428. /package/docs/{pi-crew-v0.5.9-audit-fix-plan.md → archive/pi-crew-v0.5.9-audit-fix-plan.md} +0 -0
  429. /package/docs/{pi-mono-opportunities.md → archive/pi-mono-opportunities.md} +0 -0
  430. /package/docs/{pi-mono-review.md → archive/pi-mono-review.md} +0 -0
  431. /package/docs/{pi-subagent4-comparison.md → archive/pi-subagent4-comparison.md} +0 -0
  432. /package/docs/{pi-subagents3-deep-analysis.md → archive/pi-subagents3-deep-analysis.md} +0 -0
@@ -25,13 +25,23 @@ export function validateResources(cwd: string): ValidationReport {
25
25
  const issues: ValidationIssue[] = [];
26
26
 
27
27
  for (const agent of agents) {
28
- const modelValues = [agent.model, ...(agent.fallbackModels ?? [])].filter((value): value is string => typeof value === "string" && value.length > 0);
28
+ const modelValues = [agent.model, ...(agent.fallbackModels ?? [])].filter(
29
+ (value): value is string => typeof value === "string" && value.length > 0,
30
+ );
29
31
  for (const model of modelValues) {
30
32
  if (/\s/.test(model)) {
31
- issues.push({ level: "warning", resource: `agent:${agent.name}`, message: `Model reference '${model}' contains whitespace.` });
33
+ issues.push({
34
+ level: "warning",
35
+ resource: `agent:${agent.name}`,
36
+ message: `Model reference '${model}' contains whitespace.`,
37
+ });
32
38
  }
33
39
  if (model.includes("/") && model.split("/").some((part) => part.trim() === "")) {
34
- issues.push({ level: "warning", resource: `agent:${agent.name}`, message: `Model reference '${model}' has an empty provider/model segment.` });
40
+ issues.push({
41
+ level: "warning",
42
+ resource: `agent:${agent.name}`,
43
+ message: `Model reference '${model}' has an empty provider/model segment.`,
44
+ });
35
45
  }
36
46
  }
37
47
  }
@@ -39,27 +49,48 @@ export function validateResources(cwd: string): ValidationReport {
39
49
  for (const team of teams) {
40
50
  for (const role of team.roles) {
41
51
  if (!agentNames.has(role.agent)) {
42
- issues.push({ level: "error", resource: `team:${team.name}`, message: `Role '${role.name}' references unknown agent '${role.agent}'.` });
52
+ issues.push({
53
+ level: "error",
54
+ resource: `team:${team.name}`,
55
+ message: `Role '${role.name}' references unknown agent '${role.agent}'.`,
56
+ });
43
57
  }
44
58
  }
45
59
  if (team.defaultWorkflow && !workflowNames.has(team.defaultWorkflow)) {
46
- issues.push({ level: "error", resource: `team:${team.name}`, message: `defaultWorkflow references unknown workflow '${team.defaultWorkflow}'.` });
60
+ issues.push({
61
+ level: "error",
62
+ resource: `team:${team.name}`,
63
+ message: `defaultWorkflow references unknown workflow '${team.defaultWorkflow}'.`,
64
+ });
47
65
  }
48
66
  const workflow = workflows.find((candidate) => candidate.name === team.defaultWorkflow);
49
67
  if (workflow) {
50
68
  for (const error of validateWorkflowForTeam(workflow, team)) {
51
- issues.push({ level: "error", resource: `workflow:${workflow.name}`, message: `Team '${team.name}': ${error}` });
69
+ issues.push({
70
+ level: "error",
71
+ resource: `workflow:${workflow.name}`,
72
+ message: `Team '${team.name}': ${error}`,
73
+ });
52
74
  }
53
75
  }
54
76
  }
55
77
 
56
78
  for (const workflow of workflows) {
57
79
  if (workflow.steps.length === 0) {
58
- issues.push({ level: "warning", resource: `workflow:${workflow.name}`, message: "Workflow has no steps." });
80
+ issues.push({
81
+ level: "warning",
82
+ resource: `workflow:${workflow.name}`,
83
+ message: "Workflow has no steps.",
84
+ });
59
85
  }
60
86
  }
61
87
 
62
- return { issues, agents: agents.length, teams: teams.length, workflows: workflows.length };
88
+ return {
89
+ issues,
90
+ agents: agents.length,
91
+ teams: teams.length,
92
+ workflows: workflows.length,
93
+ };
63
94
  }
64
95
 
65
96
  export function formatValidationReport(report: ValidationReport): string {
@@ -1,7 +1,7 @@
1
- import type { HookDefinition, HookName, HookContext, HookResult, HookExecutionReport } from "./types.ts";
2
1
  import { appendEvent } from "../state/event-log.ts";
3
2
  import type { TeamRunManifest } from "../state/types.ts";
4
3
  import { runEventBus } from "../ui/run-event-bus.ts";
4
+ import type { HookContext, HookDefinition, HookExecutionReport, HookName, HookResult } from "./types.ts";
5
5
 
6
6
  const registry = new Map<HookName, HookDefinition[]>();
7
7
 
@@ -67,7 +67,22 @@ export async function executeHook(name: HookName, ctx: HookContext): Promise<Hoo
67
67
  return ctx.includeGlobalHooks !== false;
68
68
  });
69
69
  if (scopedHooks.length === 0) return { hookName: name, outcome: "allow", durationMs: 0 };
70
- const POLLUTED_KEYS = new Set(["__proto__", "constructor", "prototype", "hasOwnProperty", "toString", "valueOf", "isPrototypeOf", "propertyIsEnumerable", "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__"].map((k) => k.toLowerCase().normalize("NFKC")));
70
+ const POLLUTED_KEYS = new Set(
71
+ [
72
+ "__proto__",
73
+ "constructor",
74
+ "prototype",
75
+ "hasOwnProperty",
76
+ "toString",
77
+ "valueOf",
78
+ "isPrototypeOf",
79
+ "propertyIsEnumerable",
80
+ "__defineGetter__",
81
+ "__defineSetter__",
82
+ "__lookupGetter__",
83
+ "__lookupSetter__",
84
+ ].map((k) => k.toLowerCase().normalize("NFKC")),
85
+ );
71
86
  function sanitizeMergeData(data: Record<string, unknown>): Record<string, unknown> {
72
87
  const clean: Record<string, unknown> = {};
73
88
  for (const [k, v] of Object.entries(data)) {
@@ -75,7 +90,11 @@ export async function executeHook(name: HookName, ctx: HookContext): Promise<Hoo
75
90
  if (v !== null && typeof v === "object") {
76
91
  if (Array.isArray(v)) {
77
92
  // Sanitize array elements that are objects
78
- clean[k] = v.map((item) => (item !== null && typeof item === "object" && !Array.isArray(item) ? sanitizeMergeData(item as Record<string, unknown>) : item));
93
+ clean[k] = v.map((item) =>
94
+ item !== null && typeof item === "object" && !Array.isArray(item)
95
+ ? sanitizeMergeData(item as Record<string, unknown>)
96
+ : item,
97
+ );
79
98
  } else {
80
99
  clean[k] = sanitizeMergeData(v as Record<string, unknown>);
81
100
  }
@@ -108,14 +127,19 @@ export async function executeHook(name: HookName, ctx: HookContext): Promise<Hoo
108
127
  const diagnostics: string[] = [];
109
128
  let capturedModifications: Record<string, unknown> | undefined;
110
129
  for (const hook of scopedHooks) {
111
- try {
112
- const result: HookResult = await hook.handler(sanitizeContext(ctx));
113
- // SECURITY: Sanitize any direct mutations the handler may have made to ctx.
114
- // This prevents hooks from injecting dangerous properties via direct ctx assignment.
115
- sanitizeContext(ctx);
116
- if (hook.mode === "blocking" && result.outcome === "block") {
117
- return { hookName: name, outcome: "block", durationMs: Date.now() - start, reason: result.reason };
118
- }
130
+ try {
131
+ const result: HookResult = await hook.handler(sanitizeContext(ctx));
132
+ // SECURITY: Sanitize any direct mutations the handler may have made to ctx.
133
+ // This prevents hooks from injecting dangerous properties via direct ctx assignment.
134
+ sanitizeContext(ctx);
135
+ if (hook.mode === "blocking" && result.outcome === "block") {
136
+ return {
137
+ hookName: name,
138
+ outcome: "block",
139
+ durationMs: Date.now() - start,
140
+ reason: result.reason,
141
+ };
142
+ }
119
143
  if (result.outcome === "modify" && result.data) {
120
144
  Object.assign(ctx, sanitizeMergeData(result.data));
121
145
  capturedModifications = { ...result.data };
@@ -123,16 +147,32 @@ export async function executeHook(name: HookName, ctx: HookContext): Promise<Hoo
123
147
  } catch (error) {
124
148
  const message = sanitizeErrorMessage(error instanceof Error ? error.message : String(error));
125
149
  if (hook.mode === "blocking") {
126
- return { hookName: name, outcome: "block", durationMs: Date.now() - start, reason: `Hook error: ${message}` };
150
+ return {
151
+ hookName: name,
152
+ outcome: "block",
153
+ durationMs: Date.now() - start,
154
+ reason: `Hook error: ${message}`,
155
+ };
127
156
  }
128
157
  // Non-blocking hook errors are accumulated as diagnostics; continue to next hook
129
158
  diagnostics.push(message);
130
- }
159
+ }
131
160
  }
132
161
  if (diagnostics.length > 0) {
133
- return { hookName: name, outcome: "diagnostic", durationMs: Date.now() - start, reason: diagnostics.join("; "), modifiedData: capturedModifications };
162
+ return {
163
+ hookName: name,
164
+ outcome: "diagnostic",
165
+ durationMs: Date.now() - start,
166
+ reason: diagnostics.join("; "),
167
+ modifiedData: capturedModifications,
168
+ };
134
169
  }
135
- return { hookName: name, outcome: "allow", durationMs: Date.now() - start, modifiedData: capturedModifications };
170
+ return {
171
+ hookName: name,
172
+ outcome: "allow",
173
+ durationMs: Date.now() - start,
174
+ modifiedData: capturedModifications,
175
+ };
136
176
  }
137
177
 
138
178
  export function appendHookEvent(manifest: TeamRunManifest, report: HookExecutionReport): void {
@@ -140,7 +180,16 @@ export function appendHookEvent(manifest: TeamRunManifest, report: HookExecution
140
180
  type: "hook.executed",
141
181
  runId: manifest.runId,
142
182
  message: `Hook ${report.hookName} completed with outcome=${report.outcome}${report.reason ? `: ${report.reason}` : ""}`,
143
- data: { hookName: report.hookName, outcome: report.outcome, durationMs: report.durationMs, reason: report.reason },
183
+ data: {
184
+ hookName: report.hookName,
185
+ outcome: report.outcome,
186
+ durationMs: report.durationMs,
187
+ reason: report.reason,
188
+ },
189
+ });
190
+ runEventBus.emit({
191
+ type: "effectiveness_changed",
192
+ runId: manifest.runId,
193
+ data: { hookName: report.hookName, outcome: report.outcome },
144
194
  });
145
- runEventBus.emit({ type: "effectiveness_changed", runId: manifest.runId, data: { hookName: report.hookName, outcome: report.outcome } });
146
195
  }
@@ -66,4 +66,4 @@ export interface HookExecutionReport {
66
66
  durationMs: number;
67
67
  reason?: string;
68
68
  modifiedData?: Record<string, unknown>;
69
- }
69
+ }
package/src/i18n.ts CHANGED
@@ -41,12 +41,14 @@ const translations: Record<string, Partial<Record<Key, string>>> = {
41
41
  "agent.noOutput": "Sin salida.",
42
42
  "result.requiresAgentId": "get_subagent_result requiere agent_id.",
43
43
  "result.notFound": "Agente no encontrado: {id}",
44
- "result.unrecoverable": "El subagente fue interrumpido antes de registrar su ID de ejecución duradero; no se puede recuperar tras reiniciar.",
44
+ "result.unrecoverable":
45
+ "El subagente fue interrumpido antes de registrar su ID de ejecución duradero; no se puede recuperar tras reiniciar.",
45
46
  "result.waitAborted": "Se canceló la espera del resultado del subagente.",
46
47
  "result.waitTimeout": "Se agotó el tiempo de espera del resultado del subagente.",
47
48
  "result.stillRunning": "El agente sigue ejecutándose. Usa wait=true o vuelve a comprobar más tarde.",
48
49
  "steer.noted": "Solicitud de dirección registrada para {id}.",
49
- "steer.unavailable": "El backend predeterminado actual de pi-crew es child-process, así que session.steer a mitad de turno aún no está disponible.",
50
+ "steer.unavailable":
51
+ "El backend predeterminado actual de pi-crew es child-process, así que session.steer a mitad de turno aún no está disponible.",
50
52
  "steer.cancelHint": "Usa team cancel runId={runId} si hay que interrumpir el agente.",
51
53
  },
52
54
  fr: {
@@ -60,12 +62,14 @@ const translations: Record<string, Partial<Record<Key, string>>> = {
60
62
  "agent.noOutput": "Aucune sortie.",
61
63
  "result.requiresAgentId": "get_subagent_result nécessite agent_id.",
62
64
  "result.notFound": "Agent introuvable : {id}",
63
- "result.unrecoverable": "Le sous-agent a été interrompu avant l'enregistrement de son ID d'exécution durable ; il ne peut pas être récupéré après redémarrage.",
65
+ "result.unrecoverable":
66
+ "Le sous-agent a été interrompu avant l'enregistrement de son ID d'exécution durable ; il ne peut pas être récupéré après redémarrage.",
64
67
  "result.waitAborted": "L'attente du résultat du sous-agent a été annulée.",
65
68
  "result.waitTimeout": "Délai d'attente du résultat du sous-agent dépassé.",
66
69
  "result.stillRunning": "L'agent est toujours en cours d'exécution. Utilisez wait=true ou réessayez plus tard.",
67
70
  "steer.noted": "Demande de pilotage enregistrée pour {id}.",
68
- "steer.unavailable": "Le backend pi-crew par défaut actuel est child-process, donc session.steer en milieu de tour n'est pas encore disponible.",
71
+ "steer.unavailable":
72
+ "Le backend pi-crew par défaut actuel est child-process, donc session.steer en milieu de tour n'est pas encore disponible.",
69
73
  "steer.cancelHint": "Utilisez team cancel runId={runId} si l'agent doit être interrompu.",
70
74
  },
71
75
  "pt-BR": {
@@ -79,12 +83,14 @@ const translations: Record<string, Partial<Record<Key, string>>> = {
79
83
  "agent.noOutput": "Sem saída.",
80
84
  "result.requiresAgentId": "get_subagent_result requer agent_id.",
81
85
  "result.notFound": "Agente não encontrado: {id}",
82
- "result.unrecoverable": "O subagente foi interrompido antes que seu ID de execução durável fosse registrado; ele não pode ser recuperado após reiniciar.",
86
+ "result.unrecoverable":
87
+ "O subagente foi interrompido antes que seu ID de execução durável fosse registrado; ele não pode ser recuperado após reiniciar.",
83
88
  "result.waitAborted": "A espera pelo resultado do subagente foi abortada.",
84
89
  "result.waitTimeout": "Tempo limite de espera pelo resultado do subagente esgotado.",
85
90
  "result.stillRunning": "O agente ainda está em execução. Use wait=true ou verifique novamente mais tarde.",
86
91
  "steer.noted": "Solicitação de orientação registrada para {id}.",
87
- "steer.unavailable": "O backend padrão atual do pi-crew é child-process, então session.steer no meio do turno ainda não está disponível.",
92
+ "steer.unavailable":
93
+ "O backend padrão atual do pi-crew é child-process, então session.steer no meio do turno ainda não está disponível.",
88
94
  "steer.cancelHint": "Use team cancel runId={runId} se o agente precisar ser interrompido.",
89
95
  },
90
96
  };
@@ -163,7 +169,12 @@ export function listLocales(): string[] {
163
169
 
164
170
  export function initI18n(pi: ExtensionAPI): () => void {
165
171
  try {
166
- pi.events?.emit?.("pi-core/i18n/registerBundle", { namespace, defaultLocale: "en", fallback, translations });
172
+ pi.events?.emit?.("pi-core/i18n/registerBundle", {
173
+ namespace,
174
+ defaultLocale: "en",
175
+ fallback,
176
+ translations,
177
+ });
167
178
  } catch {
168
179
  // Non-critical.
169
180
  }
@@ -173,10 +184,13 @@ export function initI18n(pi: ExtensionAPI): () => void {
173
184
  currentLocale = raw && translations[raw] ? raw : undefined;
174
185
  });
175
186
  try {
176
- pi.events?.emit?.("pi-core/i18n/requestApi", { namespace, onApi(api: { getLocale?: () => string | undefined }) {
177
- const raw = api.getLocale?.()?.trim();
178
- if (raw && translations[raw]) currentLocale = raw;
179
- } });
187
+ pi.events?.emit?.("pi-core/i18n/requestApi", {
188
+ namespace,
189
+ onApi(api: { getLocale?: () => string | undefined }) {
190
+ const raw = api.getLocale?.()?.trim();
191
+ if (raw && translations[raw]) currentLocale = raw;
192
+ },
193
+ });
180
194
  } catch {
181
195
  // Non-critical.
182
196
  }
@@ -25,11 +25,25 @@ export function newSpanId(runId: string, taskId = "main"): string {
25
25
  export function childCorrelation(runId: string, taskId: string): CorrelationContext {
26
26
  const parent = getCurrentContext();
27
27
  const spanId = newSpanId(runId, taskId);
28
- return { traceId: parent?.traceId ?? spanId, parentSpanId: parent?.spanId, spanId };
28
+ return {
29
+ traceId: parent?.traceId ?? spanId,
30
+ parentSpanId: parent?.spanId,
31
+ spanId,
32
+ };
29
33
  }
30
34
 
31
- export function correlatedEvent<T extends { runId?: string; data?: Record<string, unknown> }>(event: T): T & { data: Record<string, unknown> } {
35
+ export function correlatedEvent<T extends { runId?: string; data?: Record<string, unknown> }>(
36
+ event: T,
37
+ ): T & { data: Record<string, unknown> } {
32
38
  const ctx = getCurrentContext();
33
39
  if (!ctx) return event as T & { data: Record<string, unknown> };
34
- return { ...event, data: { ...(event.data ?? {}), traceId: ctx.traceId, spanId: ctx.spanId, parentSpanId: ctx.parentSpanId } };
40
+ return {
41
+ ...event,
42
+ data: {
43
+ ...(event.data ?? {}),
44
+ traceId: ctx.traceId,
45
+ spanId: ctx.spanId,
46
+ parentSpanId: ctx.parentSpanId,
47
+ },
48
+ };
35
49
  }
@@ -1,74 +1,69 @@
1
- import { logInternalError } from "../utils/internal-error.ts";
2
1
  import type { AgentProgress } from "../runtime/progress-tracker.ts";
2
+ import { logInternalError } from "../utils/internal-error.ts";
3
3
 
4
- export type CrewEventType =
5
- | "agent:progress"
6
- | "agent:complete"
7
- | "agent:error"
8
- | "run:start"
9
- | "run:complete";
4
+ export type CrewEventType = "agent:progress" | "agent:complete" | "agent:error" | "run:start" | "run:complete";
10
5
 
11
6
  export interface CrewEvent {
12
- type: CrewEventType;
13
- runId: string;
14
- agentId?: string;
15
- payload?: AgentProgress | string;
16
- timestamp: number;
7
+ type: CrewEventType;
8
+ runId: string;
9
+ agentId?: string;
10
+ payload?: AgentProgress | string;
11
+ timestamp: number;
17
12
  }
18
13
 
19
14
  type CrewEventListener = (event: CrewEvent) => void;
20
15
 
21
16
  class EventBus {
22
- private listeners = new Map<CrewEventType, Set<CrewEventListener>>();
23
- private static _instance?: EventBus;
17
+ private listeners = new Map<CrewEventType, Set<CrewEventListener>>();
18
+ private static _instance?: EventBus;
24
19
 
25
- static getInstance(): EventBus {
26
- if (!EventBus._instance) {
27
- EventBus._instance = new EventBus();
28
- }
29
- return EventBus._instance;
30
- }
20
+ static getInstance(): EventBus {
21
+ if (!EventBus._instance) {
22
+ EventBus._instance = new EventBus();
23
+ }
24
+ return EventBus._instance;
25
+ }
31
26
 
32
- /**
33
- * Dispose of the EventBus instance and clear all listeners.
34
- * Resets the singleton so a new instance can be created.
35
- */
36
- dispose(): void {
37
- this.listeners.clear();
38
- EventBus._instance = undefined;
39
- }
27
+ /**
28
+ * Dispose of the EventBus instance and clear all listeners.
29
+ * Resets the singleton so a new instance can be created.
30
+ */
31
+ dispose(): void {
32
+ this.listeners.clear();
33
+ EventBus._instance = undefined;
34
+ }
40
35
 
41
- emit(event: CrewEvent): void {
42
- const listeners = this.listeners.get(event.type);
43
- if (listeners) {
44
- for (const listener of listeners) {
45
- try {
46
- listener(event);
47
- } catch (e) {
48
- // FIX (Round 15, L1): Use logInternalError for consistency with
49
- // the rest of the codebase. Previously console.error may not be
50
- // visible in all environments (e.g. JSON-RPC mode, redirected
51
- // stderr).
52
- logInternalError("event-bus.listener", e, `type=${event.type} runId=${event.runId}`);
53
- }
54
- }
55
- }
56
- }
36
+ emit(event: CrewEvent): void {
37
+ const listeners = this.listeners.get(event.type);
38
+ if (listeners) {
39
+ for (const listener of listeners) {
40
+ try {
41
+ listener(event);
42
+ } catch (e) {
43
+ // FIX (Round 15, L1): Use logInternalError for consistency with
44
+ // the rest of the codebase. Previously console.error may not be
45
+ // visible in all environments (e.g. JSON-RPC mode, redirected
46
+ // stderr).
47
+ logInternalError("event-bus.listener", e, `type=${event.type} runId=${event.runId}`);
48
+ }
49
+ }
50
+ }
51
+ }
57
52
 
58
- on(type: CrewEventType, listener: CrewEventListener): () => void {
59
- if (!this.listeners.has(type)) {
60
- this.listeners.set(type, new Set());
61
- }
62
- this.listeners.get(type)!.add(listener);
53
+ on(type: CrewEventType, listener: CrewEventListener): () => void {
54
+ if (!this.listeners.has(type)) {
55
+ this.listeners.set(type, new Set());
56
+ }
57
+ this.listeners.get(type)!.add(listener);
63
58
 
64
- return () => {
65
- this.listeners.get(type)?.delete(listener);
66
- };
67
- }
59
+ return () => {
60
+ this.listeners.get(type)?.delete(listener);
61
+ };
62
+ }
68
63
 
69
- off(type: CrewEventType, listener: CrewEventListener): void {
70
- this.listeners.get(type)?.delete(listener);
71
- }
64
+ off(type: CrewEventType, listener: CrewEventListener): void {
65
+ this.listeners.get(type)?.delete(listener);
66
+ }
72
67
  }
73
68
 
74
69
  /**
@@ -2,7 +2,7 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
2
  import type { MetricRegistry } from "./metric-registry.ts";
3
3
 
4
4
  function recordValue(value: unknown): Record<string, unknown> {
5
- return value && typeof value === "object" && !Array.isArray(value) ? value as Record<string, unknown> : {};
5
+ return value && typeof value === "object" && !Array.isArray(value) ? (value as Record<string, unknown>) : {};
6
6
  }
7
7
 
8
8
  function stringValue(value: unknown, fallback: string): string {
@@ -13,7 +13,15 @@ function numberValue(value: unknown, fallback = 0): number {
13
13
  return typeof value === "number" && Number.isFinite(value) ? value : fallback;
14
14
  }
15
15
 
16
- const CANCELLATION_REASON_LABELS = new Set(["caller_cancelled", "leader_interrupted", "provider_timeout", "worker_timeout", "tool_timeout", "shutdown", "unknown"]);
16
+ const CANCELLATION_REASON_LABELS = new Set([
17
+ "caller_cancelled",
18
+ "leader_interrupted",
19
+ "provider_timeout",
20
+ "worker_timeout",
21
+ "tool_timeout",
22
+ "shutdown",
23
+ "unknown",
24
+ ]);
17
25
 
18
26
  function cancellationReasonLabel(value: unknown): string {
19
27
  const raw = stringValue(value, "unknown");
@@ -34,34 +42,128 @@ export function wireEventToMetrics(events: ExtensionAPI["events"] | undefined, r
34
42
  const overflowCount = registry.counter("crew.task.overflow_phase_total", "Overflow recovery phase transitions");
35
43
  const supervisorContactCount = registry.counter("crew.task.supervisor_contact_total", "Supervisor contact requests by reason");
36
44
  registry.gauge("crew.heartbeat.staleness_ms", "Heartbeat elapsed since last seen, milliseconds");
37
- const runDuration = registry.histogram("crew.run.duration_ms", "Run end-to-end duration, milliseconds", [1000, 5000, 15000, 30000, 60000, 300000, 600000, 1800000]);
38
- const taskDuration = registry.histogram("crew.task.duration_ms", "Task duration, milliseconds", [50, 200, 500, 1000, 5000, 30000, 120000]);
45
+ const runDuration = registry.histogram(
46
+ "crew.run.duration_ms",
47
+ "Run end-to-end duration, milliseconds",
48
+ [1000, 5000, 15000, 30000, 60000, 300000, 600000, 1800000],
49
+ );
50
+ const taskDuration = registry.histogram(
51
+ "crew.task.duration_ms",
52
+ "Task duration, milliseconds",
53
+ [50, 200, 500, 1000, 5000, 30000, 120000],
54
+ );
39
55
  registry.histogram("crew.task.retry_count", "Retries per task", [0, 1, 2, 3, 5, 10]);
40
56
  const tokenUsage = registry.histogram("crew.task.tokens_total", "Token usage per task", [100, 500, 2000, 10000, 50000, 200000]);
41
57
 
42
58
  const handlers: Array<[string, (data: unknown) => void]> = [
43
- ["crew.run.completed", (data) => { const item = recordValue(data); runCount.inc({ status: "completed" }); runDuration.observe({ team: stringValue(item.team, "unknown") }, numberValue(item.durationMs)); }],
59
+ [
60
+ "crew.run.completed",
61
+ (data) => {
62
+ const item = recordValue(data);
63
+ runCount.inc({ status: "completed" });
64
+ runDuration.observe({ team: stringValue(item.team, "unknown") }, numberValue(item.durationMs));
65
+ },
66
+ ],
44
67
  ["crew.run.failed", () => runCount.inc({ status: "failed" })],
45
- ["crew.run.cancelled", (data) => { const item = recordValue(data); runCount.inc({ status: "cancelled", reason: cancellationReasonLabel(item.reason) }); }],
46
- ["crew.task.completed", (data) => { const item = recordValue(data); taskCount.inc({ status: "completed" }); taskDuration.observe({ role: stringValue(item.role, "unknown") }, numberValue(item.durationMs)); tokenUsage.observe({ role: stringValue(item.role, "unknown") }, numberValue(item.tokens)); }],
68
+ [
69
+ "crew.run.cancelled",
70
+ (data) => {
71
+ const item = recordValue(data);
72
+ runCount.inc({
73
+ status: "cancelled",
74
+ reason: cancellationReasonLabel(item.reason),
75
+ });
76
+ },
77
+ ],
78
+ [
79
+ "crew.task.completed",
80
+ (data) => {
81
+ const item = recordValue(data);
82
+ taskCount.inc({ status: "completed" });
83
+ taskDuration.observe({ role: stringValue(item.role, "unknown") }, numberValue(item.durationMs));
84
+ tokenUsage.observe({ role: stringValue(item.role, "unknown") }, numberValue(item.tokens));
85
+ },
86
+ ],
47
87
  ["crew.task.failed", () => taskCount.inc({ status: "failed" })],
48
88
  ["crew.task.needs_attention", () => taskCount.inc({ status: "needs_attention" })],
49
- ["crew.task.retry_attempt", (data) => { const item = recordValue(data); taskCount.inc({ status: "retry" }); retryAttemptCount.inc({ runId: stringValue(item.runId, "unknown"), taskId: stringValue(item.taskId, "unknown") }); }],
50
- ["crew.task.deadletter", (data) => { const item = recordValue(data); deadletterCount.inc({ reason: stringValue(item.reason, "unknown") }); }],
51
- ["crew.task.overflow", (data) => { const item = recordValue(data); overflowCount.inc({ phase: stringValue(item.phase, "unknown"), previous_phase: stringValue(item.previousPhase, "none") }); }],
52
- ["supervisor.contact", (data) => { const item = recordValue(data); supervisorContactCount.inc({ reason: stringValue(item.reason, "unknown"), taskId: stringValue(item.taskId, "unknown") }); }],
53
- ["crew.subagent.completed", (data) => { const item = recordValue(data); subagentCount.inc({ status: stringValue(item.status, "completed") }); }],
89
+ [
90
+ "crew.task.retry_attempt",
91
+ (data) => {
92
+ const item = recordValue(data);
93
+ taskCount.inc({ status: "retry" });
94
+ retryAttemptCount.inc({
95
+ runId: stringValue(item.runId, "unknown"),
96
+ taskId: stringValue(item.taskId, "unknown"),
97
+ });
98
+ },
99
+ ],
100
+ [
101
+ "crew.task.deadletter",
102
+ (data) => {
103
+ const item = recordValue(data);
104
+ deadletterCount.inc({
105
+ reason: stringValue(item.reason, "unknown"),
106
+ });
107
+ },
108
+ ],
109
+ [
110
+ "crew.task.overflow",
111
+ (data) => {
112
+ const item = recordValue(data);
113
+ overflowCount.inc({
114
+ phase: stringValue(item.phase, "unknown"),
115
+ previous_phase: stringValue(item.previousPhase, "none"),
116
+ });
117
+ },
118
+ ],
119
+ [
120
+ "supervisor.contact",
121
+ (data) => {
122
+ const item = recordValue(data);
123
+ supervisorContactCount.inc({
124
+ reason: stringValue(item.reason, "unknown"),
125
+ taskId: stringValue(item.taskId, "unknown"),
126
+ });
127
+ },
128
+ ],
129
+ [
130
+ "crew.subagent.completed",
131
+ (data) => {
132
+ const item = recordValue(data);
133
+ subagentCount.inc({
134
+ status: stringValue(item.status, "completed"),
135
+ });
136
+ },
137
+ ],
54
138
  ["crew.subagent.failed", () => subagentCount.inc({ status: "failed" })],
55
- ["crew.mailbox.message", (data) => { const item = recordValue(data); mailboxCount.inc({ direction: stringValue(item.direction, "unknown") }); }],
139
+ [
140
+ "crew.mailbox.message",
141
+ (data) => {
142
+ const item = recordValue(data);
143
+ mailboxCount.inc({
144
+ direction: stringValue(item.direction, "unknown"),
145
+ });
146
+ },
147
+ ],
56
148
  ];
57
149
 
58
150
  const unsubscribers: Array<() => void> = [];
59
151
  for (const [event, handler] of handlers) {
60
152
  const unsubscribe = events?.on?.(event, (data: unknown) => {
61
- try { handler(data); } catch { /* metric handlers must never break event delivery */ }
153
+ try {
154
+ handler(data);
155
+ } catch {
156
+ /* metric handlers must never break event delivery */
157
+ }
62
158
  });
63
159
  if (typeof unsubscribe === "function") unsubscribers.push(unsubscribe);
64
160
  }
65
161
  let disposed = false;
66
- return { dispose() { if (disposed) return; disposed = true; for (const unsubscribe of unsubscribers.splice(0)) unsubscribe(); } };
162
+ return {
163
+ dispose() {
164
+ if (disposed) return;
165
+ disposed = true;
166
+ for (const unsubscribe of unsubscribers.splice(0)) unsubscribe();
167
+ },
168
+ };
67
169
  }