pi-crew 0.9.16 → 0.9.18

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 (435) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/README.md +19 -0
  3. package/dist/build-meta.json +21784 -0
  4. package/dist/index.mjs +83169 -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/migration/atomic-write-v2-migration.md +297 -0
  14. package/docs/patterns/command-agent-skill.md +1 -1
  15. package/docs/perf/performance-review-2026-07.md +287 -0
  16. package/docs/skills/REFERENCE.md +0 -17
  17. package/docs//303/242mmaAAA/303/242 +357 -0
  18. package/index.bundle.ts +25 -0
  19. package/index.ts +95 -4
  20. package/package.json +15 -4
  21. package/skills/iterative-audit/SKILL.md +0 -1
  22. package/skills/widget-rendering/SKILL.md +17 -0
  23. package/src/adapters/claude-adapter.ts +1 -3
  24. package/src/adapters/export-util.ts +16 -16
  25. package/src/adapters/index.ts +5 -5
  26. package/src/agents/agent-config.ts +20 -14
  27. package/src/agents/agent-serializer.ts +1 -1
  28. package/src/agents/discover-agents.ts +83 -39
  29. package/src/benchmark/benchmark-runner.ts +51 -51
  30. package/src/benchmark/feedback-loop.ts +1 -1
  31. package/src/config/config.ts +303 -591
  32. package/src/config/defaults.ts +28 -2
  33. package/src/config/drift-detector.ts +11 -14
  34. package/src/config/markers.ts +201 -203
  35. package/src/config/resilient-parser.ts +14 -6
  36. package/src/config/role-tools.ts +3 -3
  37. package/src/config/suggestions.ts +5 -12
  38. package/src/config/types.ts +9 -25
  39. package/src/errors.ts +151 -157
  40. package/src/extension/action-suggestions.ts +54 -9
  41. package/src/extension/async-notifier.ts +43 -13
  42. package/src/extension/autonomous-policy.ts +77 -37
  43. package/src/extension/command-completions.ts +9 -8
  44. package/src/extension/context-status-injection.ts +14 -12
  45. package/src/extension/crew-autocomplete.ts +8 -16
  46. package/src/extension/crew-cleanup.ts +2 -1
  47. package/src/extension/crew-shortcuts.ts +9 -6
  48. package/src/extension/cross-extension-rpc.ts +136 -42
  49. package/src/extension/help.ts +1 -1
  50. package/src/extension/import-index.ts +10 -5
  51. package/src/extension/knowledge-injection.ts +88 -15
  52. package/src/extension/management.ts +89 -349
  53. package/src/extension/message-renderers.ts +3 -4
  54. package/src/extension/notification-router.ts +22 -5
  55. package/src/extension/notification-sink.ts +6 -3
  56. package/src/extension/pi-api.ts +17 -8
  57. package/src/extension/plan-orchestrate.ts +8 -27
  58. package/src/extension/project-init.ts +21 -6
  59. package/src/extension/register.ts +329 -975
  60. package/src/extension/registration/artifact-cleanup.ts +9 -4
  61. package/src/extension/registration/command-utils.ts +5 -1
  62. package/src/extension/registration/commands.ts +806 -314
  63. package/src/extension/registration/compaction-guard.ts +15 -15
  64. package/src/extension/registration/lifecycle.ts +259 -0
  65. package/src/extension/registration/observability.ts +293 -0
  66. package/src/extension/registration/subagent-helpers.ts +29 -7
  67. package/src/extension/registration/subagent-tools.ts +253 -44
  68. package/src/extension/registration/team-tool.ts +19 -84
  69. package/src/extension/registration/ui.ts +168 -0
  70. package/src/extension/registration/viewers.ts +64 -24
  71. package/src/extension/result-watcher.ts +18 -7
  72. package/src/extension/run-bundle-schema.ts +21 -6
  73. package/src/extension/run-export.ts +15 -6
  74. package/src/extension/run-import.ts +64 -29
  75. package/src/extension/run-index.ts +28 -9
  76. package/src/extension/run-maintenance.ts +32 -12
  77. package/src/extension/session-summary.ts +1 -1
  78. package/src/extension/team-manager-command.ts +64 -7
  79. package/src/extension/team-onboard.ts +149 -150
  80. package/src/extension/team-recommendation.ts +121 -21
  81. package/src/extension/team-tool/anchor.ts +31 -64
  82. package/src/extension/team-tool/api.ts +929 -141
  83. package/src/extension/team-tool/auto-summarize.ts +14 -26
  84. package/src/extension/team-tool/cache-control.ts +1 -5
  85. package/src/extension/team-tool/cancel.ts +138 -38
  86. package/src/extension/team-tool/chain-dispatch.ts +5 -13
  87. package/src/extension/team-tool/chain-executor.ts +29 -51
  88. package/src/extension/team-tool/config-patch.ts +1 -1
  89. package/src/extension/team-tool/context.ts +40 -20
  90. package/src/extension/team-tool/destructive-gate.ts +10 -5
  91. package/src/extension/team-tool/doctor.ts +170 -51
  92. package/src/extension/team-tool/explain.ts +228 -214
  93. package/src/extension/team-tool/failure-patterns.ts +3 -9
  94. package/src/extension/team-tool/goal-wrap.ts +71 -29
  95. package/src/extension/team-tool/goal.ts +185 -35
  96. package/src/extension/team-tool/handle-schedule.ts +34 -15
  97. package/src/extension/team-tool/handle-settings.ts +94 -56
  98. package/src/extension/team-tool/health-monitor.ts +27 -108
  99. package/src/extension/team-tool/inspect.ts +42 -9
  100. package/src/extension/team-tool/intent-policy.ts +4 -1
  101. package/src/extension/team-tool/lifecycle-actions.ts +244 -48
  102. package/src/extension/team-tool/orchestrate.ts +7 -18
  103. package/src/extension/team-tool/parallel-dispatch.ts +51 -29
  104. package/src/extension/team-tool/plan.ts +29 -5
  105. package/src/extension/team-tool/respond.ts +49 -16
  106. package/src/extension/team-tool/run-not-found.ts +3 -8
  107. package/src/extension/team-tool/run.ts +80 -317
  108. package/src/extension/team-tool/status.ts +135 -43
  109. package/src/extension/team-tool/workflow-manage.ts +92 -25
  110. package/src/extension/team-tool-types.ts +8 -1
  111. package/src/extension/team-tool.ts +142 -551
  112. package/src/extension/validate-resources.ts +39 -8
  113. package/src/hooks/registry.ts +66 -17
  114. package/src/hooks/types.ts +1 -1
  115. package/src/i18n.ts +25 -11
  116. package/src/observability/correlation.ts +17 -3
  117. package/src/observability/event-bus.ts +51 -56
  118. package/src/observability/event-to-metric.ts +117 -15
  119. package/src/observability/exporters/otlp-exporter.ts +56 -17
  120. package/src/observability/exporters/prometheus-exporter.ts +1 -1
  121. package/src/observability/metric-registry.ts +16 -4
  122. package/src/observability/metric-retention.ts +8 -2
  123. package/src/observability/metric-sink.ts +11 -3
  124. package/src/observability/metrics-primitives.ts +42 -9
  125. package/src/plugins/plugin-define.ts +2 -2
  126. package/src/plugins/plugin-registry.ts +25 -25
  127. package/src/plugins/plugins/index.ts +1 -1
  128. package/src/plugins/plugins/nextjs.ts +16 -16
  129. package/src/plugins/plugins/vite.ts +7 -11
  130. package/src/plugins/plugins/vitest.ts +6 -11
  131. package/src/runtime/adaptive-plan.ts +225 -40
  132. package/src/runtime/agent-control.ts +58 -15
  133. package/src/runtime/agent-memory.ts +15 -9
  134. package/src/runtime/agent-observability.ts +10 -2
  135. package/src/runtime/anchor-manager.ts +27 -25
  136. package/src/runtime/async-marker.ts +7 -1
  137. package/src/runtime/async-runner.ts +39 -24
  138. package/src/runtime/auto-summarize.ts +7 -13
  139. package/src/runtime/background-runner.ts +189 -251
  140. package/src/runtime/batch-barrier.ts +18 -16
  141. package/src/runtime/cancellation-token.ts +16 -6
  142. package/src/runtime/cancellation.ts +46 -8
  143. package/src/runtime/capability-inventory.ts +6 -4
  144. package/src/runtime/chain-parser.ts +44 -11
  145. package/src/runtime/chain-runner.ts +63 -69
  146. package/src/runtime/checkpoint.ts +12 -56
  147. package/src/runtime/child-pi.ts +344 -79
  148. package/src/runtime/coalesce-tasks.ts +268 -0
  149. package/src/runtime/code-summary.ts +71 -26
  150. package/src/runtime/compact-stages/index.ts +15 -5
  151. package/src/runtime/compact-stages/tail-capture-stage.ts +7 -2
  152. package/src/runtime/compact-stages/truncation-stage.ts +4 -1
  153. package/src/runtime/compaction-summary.ts +17 -10
  154. package/src/runtime/completion-guard.ts +33 -16
  155. package/src/runtime/crash-classification.ts +28 -7
  156. package/src/runtime/crash-recovery.ts +177 -37
  157. package/src/runtime/crew-agent-records.ts +145 -47
  158. package/src/runtime/crew-hooks.ts +9 -24
  159. package/src/runtime/cross-extension-rpc.ts +25 -23
  160. package/src/runtime/custom-tools/irc-tool.ts +43 -15
  161. package/src/runtime/custom-tools/submit-result-tool.ts +16 -5
  162. package/src/runtime/delivery-coordinator.ts +34 -7
  163. package/src/runtime/delta-conflict.ts +9 -21
  164. package/src/runtime/deterministic-ast.ts +1 -1
  165. package/src/runtime/diagnostic-export.ts +53 -20
  166. package/src/runtime/direct-run.ts +12 -2
  167. package/src/runtime/dwf-state-store.ts +1 -1
  168. package/src/runtime/dynamic-workflow-context.ts +187 -59
  169. package/src/runtime/dynamic-workflow-runner.ts +42 -20
  170. package/src/runtime/effectiveness.ts +16 -8
  171. package/src/runtime/errors/crew-errors.ts +7 -11
  172. package/src/runtime/event-stream-bridge.ts +9 -3
  173. package/src/runtime/foreground-control.ts +54 -14
  174. package/src/runtime/foreground-watchdog.ts +9 -6
  175. package/src/runtime/goal-achievement.ts +27 -10
  176. package/src/runtime/goal-evaluator.ts +54 -19
  177. package/src/runtime/goal-loop-runner.ts +280 -99
  178. package/src/runtime/goal-state-store.ts +27 -17
  179. package/src/runtime/green-contract.ts +11 -2
  180. package/src/runtime/group-join.ts +64 -31
  181. package/src/runtime/handoff-manager.ts +37 -42
  182. package/src/runtime/heartbeat-gradient.ts +10 -2
  183. package/src/runtime/heartbeat-watcher.ts +32 -6
  184. package/src/runtime/hidden-handoff.ts +13 -31
  185. package/src/runtime/important-line-classifier.ts +12 -2
  186. package/src/runtime/iteration-hooks.ts +25 -15
  187. package/src/runtime/live-agent-control.ts +55 -7
  188. package/src/runtime/live-agent-manager.ts +130 -27
  189. package/src/runtime/live-control-realtime.ts +23 -3
  190. package/src/runtime/live-irc.ts +4 -1
  191. package/src/runtime/live-session-health.ts +12 -4
  192. package/src/runtime/live-session-runtime.ts +379 -117
  193. package/src/runtime/manifest-cache.ts +28 -12
  194. package/src/runtime/mcp-proxy.ts +10 -18
  195. package/src/runtime/metric-parser.ts +1 -5
  196. package/src/runtime/model-fallback.ts +55 -18
  197. package/src/runtime/model-resolver.ts +2 -6
  198. package/src/runtime/model-scope.ts +19 -3
  199. package/src/runtime/notebook-helpers.ts +60 -62
  200. package/src/runtime/orphan-worker-registry.ts +37 -26
  201. package/src/runtime/output-validator.ts +18 -3
  202. package/src/runtime/overflow-recovery.ts +5 -4
  203. package/src/runtime/parallel-research.ts +29 -5
  204. package/src/runtime/parallel-utils.ts +9 -11
  205. package/src/runtime/path-overlap.ts +150 -0
  206. package/src/runtime/peer-dep.ts +12 -17
  207. package/src/runtime/per-write-validator.ts +1 -3
  208. package/src/runtime/phase-tracker.ts +342 -330
  209. package/src/runtime/pi-args.ts +26 -8
  210. package/src/runtime/pi-json-output.ts +1 -1
  211. package/src/runtime/pi-spawn.ts +30 -19
  212. package/src/runtime/pipeline-runner.ts +56 -55
  213. package/src/runtime/plan-templates.ts +5 -6
  214. package/src/runtime/policy-engine.ts +43 -7
  215. package/src/runtime/post-checks.ts +12 -4
  216. package/src/runtime/post-exit-stdio-guard.ts +2 -2
  217. package/src/runtime/process-lifecycle.ts +20 -10
  218. package/src/runtime/process-status.ts +16 -5
  219. package/src/runtime/progress-event-coalescer.ts +2 -1
  220. package/src/runtime/progress-tracker.ts +103 -103
  221. package/src/runtime/prose-compressor.ts +9 -11
  222. package/src/runtime/recovery-recipes.ts +118 -24
  223. package/src/runtime/replace.ts +25 -10
  224. package/src/runtime/resilient-edit.ts +10 -25
  225. package/src/runtime/result-extractor.ts +2 -6
  226. package/src/runtime/retry-executor.ts +20 -4
  227. package/src/runtime/retry-runner.ts +27 -51
  228. package/src/runtime/role-permission.ts +6 -1
  229. package/src/runtime/run-coalesced-task-group.ts +256 -0
  230. package/src/runtime/run-drift.ts +14 -15
  231. package/src/runtime/run-tracker.ts +6 -28
  232. package/src/runtime/runtime-policy.ts +1 -6
  233. package/src/runtime/runtime-resolver.ts +80 -15
  234. package/src/runtime/scheduler.ts +47 -18
  235. package/src/runtime/semaphore.ts +5 -7
  236. package/src/runtime/sensitive-paths.ts +2 -1
  237. package/src/runtime/session-usage.ts +1 -1
  238. package/src/runtime/settings-store.ts +16 -12
  239. package/src/runtime/sidechain-output.ts +6 -2
  240. package/src/runtime/single-agent-compose.ts +1 -4
  241. package/src/runtime/skill-effectiveness.ts +29 -97
  242. package/src/runtime/skill-instructions.ts +40 -83
  243. package/src/runtime/stale-reconciler.ts +73 -197
  244. package/src/runtime/stream-preview.ts +1 -1
  245. package/src/runtime/streaming-output.ts +1 -1
  246. package/src/runtime/subagent-manager.ts +44 -169
  247. package/src/runtime/subprocess-tool-registry.ts +4 -1
  248. package/src/runtime/supervisor-contact.ts +10 -5
  249. package/src/runtime/task-display.ts +19 -5
  250. package/src/runtime/task-graph-scheduler.ts +95 -21
  251. package/src/runtime/task-graph.ts +5 -11
  252. package/src/runtime/task-health.ts +54 -47
  253. package/src/runtime/task-id.ts +12 -19
  254. package/src/runtime/task-output-context.ts +265 -81
  255. package/src/runtime/task-packet.ts +12 -20
  256. package/src/runtime/task-quality.ts +6 -14
  257. package/src/runtime/task-runner/context-retrieval.ts +4 -13
  258. package/src/runtime/task-runner/live-executor.ts +114 -36
  259. package/src/runtime/task-runner/output-splitter.ts +152 -0
  260. package/src/runtime/task-runner/progress.ts +50 -12
  261. package/src/runtime/task-runner/prompt-builder.ts +31 -7
  262. package/src/runtime/task-runner/prompt-pipeline.ts +31 -7
  263. package/src/runtime/task-runner/result-utils.ts +3 -1
  264. package/src/runtime/task-runner/retrieval-orchestrator.ts +310 -0
  265. package/src/runtime/task-runner/run-projection.ts +27 -8
  266. package/src/runtime/task-runner/state-helpers.ts +50 -14
  267. package/src/runtime/task-runner/tail-read.ts +2 -6
  268. package/src/runtime/task-runner.ts +180 -326
  269. package/src/runtime/team-runner-artifacts.ts +13 -0
  270. package/src/runtime/team-runner.ts +456 -974
  271. package/src/runtime/tool-output-pruner.ts +6 -9
  272. package/src/runtime/tool-progress.ts +11 -21
  273. package/src/runtime/verification-gates.ts +33 -24
  274. package/src/runtime/verification-integrity.ts +2 -7
  275. package/src/runtime/verification-worktree.ts +62 -12
  276. package/src/runtime/worker-heartbeat.ts +5 -1
  277. package/src/runtime/worker-startup.ts +29 -5
  278. package/src/runtime/workflow-state.ts +17 -9
  279. package/src/runtime/workspace-lock.ts +30 -35
  280. package/src/runtime/workspace-tree.ts +20 -32
  281. package/src/runtime/yield-handler.ts +43 -9
  282. package/src/runtime/zombie-scanner.ts +5 -4
  283. package/src/schema/config-schema.ts +266 -172
  284. package/src/schema/team-tool-schema.ts +29 -74
  285. package/src/schema/validation-types.ts +25 -17
  286. package/src/skills/discover-skills.ts +35 -10
  287. package/src/skills/skill-templates.ts +109 -27
  288. package/src/skills/validate.ts +26 -8
  289. package/src/state/active-run-registry.ts +87 -28
  290. package/src/state/artifact-store.ts +9 -5
  291. package/src/state/atomic-write-v2.ts +85 -63
  292. package/src/state/atomic-write.ts +106 -27
  293. package/src/state/blob-store.ts +47 -20
  294. package/src/state/contracts.ts +20 -5
  295. package/src/state/crew-init.ts +5 -22
  296. package/src/state/decision-ledger.ts +19 -73
  297. package/src/state/event-log-rotation.ts +41 -15
  298. package/src/state/event-log.ts +168 -53
  299. package/src/state/event-reconstructor.ts +11 -1
  300. package/src/state/gitignore-manager.ts +2 -8
  301. package/src/state/health-store.ts +57 -57
  302. package/src/state/hook-instinct-bridge.ts +1 -1
  303. package/src/state/hook-integrations.ts +1 -1
  304. package/src/state/instinct-store.ts +17 -5
  305. package/src/state/jsonl-writer.ts +1 -1
  306. package/src/state/locks.ts +23 -9
  307. package/src/state/mailbox.ts +151 -28
  308. package/src/state/observation-store.ts +20 -14
  309. package/src/state/run-cache.ts +142 -135
  310. package/src/state/run-graph.ts +6 -15
  311. package/src/state/run-metrics.ts +5 -18
  312. package/src/state/schedule.ts +15 -9
  313. package/src/state/state-store.ts +137 -47
  314. package/src/state/task-claims.ts +12 -2
  315. package/src/state/tiered-eval.ts +52 -43
  316. package/src/state/types-eval.ts +2 -2
  317. package/src/state/types.ts +24 -20
  318. package/src/state/usage.ts +20 -4
  319. package/src/state/worker-atomic-writer.ts +6 -3
  320. package/src/subagents/index.ts +2 -2
  321. package/src/teams/discover-teams.ts +21 -6
  322. package/src/tools/safe-bash-extension.ts +5 -6
  323. package/src/tools/safe-bash.ts +10 -11
  324. package/src/types/new-api-types.ts +6 -10
  325. package/src/ui/agent-management-overlay.ts +52 -40
  326. package/src/ui/card-colors.ts +13 -4
  327. package/src/ui/crew-footer.ts +8 -7
  328. package/src/ui/crew-select-list.ts +1 -1
  329. package/src/ui/dashboard-panes/agents-pane.ts +41 -25
  330. package/src/ui/dashboard-panes/cancellation-pane.ts +1 -1
  331. package/src/ui/dashboard-panes/capability-pane.ts +32 -15
  332. package/src/ui/dashboard-panes/health-pane.ts +2 -1
  333. package/src/ui/dashboard-panes/metrics-pane.ts +4 -1
  334. package/src/ui/dashboard-panes/progress-pane.ts +12 -9
  335. package/src/ui/heartbeat-aggregator.ts +15 -4
  336. package/src/ui/keybinding-map.ts +40 -7
  337. package/src/ui/live-conversation-overlay.ts +36 -20
  338. package/src/ui/live-duration.ts +1 -4
  339. package/src/ui/live-run-sidebar.ts +88 -25
  340. package/src/ui/loaders.ts +2 -8
  341. package/src/ui/mascot.ts +20 -36
  342. package/src/ui/overlays/agent-picker-overlay.ts +11 -3
  343. package/src/ui/overlays/confirm-overlay.ts +4 -2
  344. package/src/ui/overlays/help-overlay.ts +25 -14
  345. package/src/ui/overlays/mailbox-compose-overlay.ts +48 -11
  346. package/src/ui/overlays/mailbox-compose-preview.ts +20 -5
  347. package/src/ui/overlays/mailbox-detail-overlay.ts +27 -6
  348. package/src/ui/pi-ui-compat.ts +7 -7
  349. package/src/ui/powerbar-publisher.ts +120 -42
  350. package/src/ui/render-diff.ts +10 -3
  351. package/src/ui/render-scheduler.ts +12 -4
  352. package/src/ui/run-action-dispatcher.ts +81 -18
  353. package/src/ui/run-dashboard.ts +172 -76
  354. package/src/ui/run-event-bus.ts +59 -37
  355. package/src/ui/run-snapshot-cache.ts +276 -86
  356. package/src/ui/settings-overlay.ts +361 -74
  357. package/src/ui/status-colors.ts +12 -1
  358. package/src/ui/syntax-highlight.ts +1 -1
  359. package/src/ui/terminal-status.ts +1 -3
  360. package/src/ui/theme-adapter.ts +16 -14
  361. package/src/ui/theme-discovery.ts +13 -2
  362. package/src/ui/tool-progress-formatter.ts +7 -7
  363. package/src/ui/tool-render.ts +128 -56
  364. package/src/ui/tool-renderers/brief-mode.ts +45 -27
  365. package/src/ui/tool-renderers/index.ts +109 -43
  366. package/src/ui/transcript-cache.ts +32 -6
  367. package/src/ui/transcript-entries.ts +25 -23
  368. package/src/ui/transcript-viewer.ts +78 -29
  369. package/src/ui/widget/index.ts +99 -40
  370. package/src/ui/widget/widget-formatters.ts +13 -6
  371. package/src/ui/widget/widget-model.ts +19 -8
  372. package/src/ui/widget/widget-renderer.ts +23 -13
  373. package/src/ui/widget/widget-types.ts +1 -1
  374. package/src/utils/bm25-search.ts +199 -199
  375. package/src/utils/conflict-detect.ts +22 -21
  376. package/src/utils/env-filter.ts +14 -7
  377. package/src/utils/file-coalescer.ts +5 -1
  378. package/src/utils/fingerprint.ts +3 -6
  379. package/src/utils/frontmatter.ts +3 -1
  380. package/src/utils/fs-watch.ts +2 -6
  381. package/src/utils/gh-protocol.ts +119 -44
  382. package/src/utils/git.ts +13 -15
  383. package/src/utils/guards.ts +2 -5
  384. package/src/utils/ids.ts +9 -2
  385. package/src/utils/incremental-reader.ts +14 -3
  386. package/src/utils/internal-error.ts +2 -1
  387. package/src/utils/names.ts +12 -3
  388. package/src/utils/paths.ts +49 -5
  389. package/src/utils/project-detector.ts +2 -2
  390. package/src/utils/redaction.ts +29 -19
  391. package/src/utils/resolve-shell.ts +9 -7
  392. package/src/utils/run-watcher-registry.ts +19 -31
  393. package/src/utils/safe-paths.ts +46 -29
  394. package/src/utils/scan-cache.ts +9 -2
  395. package/src/utils/session-utils.ts +2 -4
  396. package/src/utils/sleep.ts +12 -5
  397. package/src/utils/sse-parser.ts +5 -17
  398. package/src/utils/visual.ts +52 -32
  399. package/src/workflows/discover-workflows.ts +41 -109
  400. package/src/workflows/intermediate-store.ts +5 -21
  401. package/src/workflows/preflight-validator.ts +6 -33
  402. package/src/workflows/topology-analyzer.ts +5 -21
  403. package/src/workflows/workflow-config.ts +7 -6
  404. package/src/worktree/branch-freshness.ts +66 -8
  405. package/src/worktree/cleanup.ts +130 -20
  406. package/src/worktree/worktree-manager.ts +212 -69
  407. package/skills/artifact-analysis-loop/SKILL.md +0 -303
  408. package/skills/detection-pipeline-design/SKILL.md +0 -286
  409. package/skills/hunting-investigation-loop/SKILL.md +0 -402
  410. package/skills/incident-playbook-construction/SKILL.md +0 -384
  411. package/skills/security-review/SKILL.md +0 -561
  412. package/skills/threat-hypothesis-framework/SKILL.md +0 -176
  413. package/skills/ui-render-performance/SKILL.md +0 -58
  414. /package/docs/{followup-review-round3-2026-05-12.md → archive/followup-review-round3-2026-05-12.md} +0 -0
  415. /package/docs/{followup-review-round4-2026-05-13.md → archive/followup-review-round4-2026-05-13.md} +0 -0
  416. /package/docs/{pi-crew-bugs.md → archive/pi-crew-bugs.md} +0 -0
  417. /package/docs/{pi-crew-test-final.md → archive/pi-crew-test-final.md} +0 -0
  418. /package/docs/{pi-crew-test-results.md → archive/pi-crew-test-results.md} +0 -0
  419. /package/docs/{pi-crew-test-round2.md → archive/pi-crew-test-round2.md} +0 -0
  420. /package/docs/{pi-crew-test-round4.md → archive/pi-crew-test-round4.md} +0 -0
  421. /package/docs/{pi-crew-test-round5.md → archive/pi-crew-test-round5.md} +0 -0
  422. /package/docs/{pi-crew-test-round6.md → archive/pi-crew-test-round6.md} +0 -0
  423. /package/docs/{pi-crew-v0.5.10-audit-fix-plan.md → archive/pi-crew-v0.5.10-audit-fix-plan.md} +0 -0
  424. /package/docs/{pi-crew-v0.5.11-audit-fix-plan.md → archive/pi-crew-v0.5.11-audit-fix-plan.md} +0 -0
  425. /package/docs/{pi-crew-v0.5.12-audit-fix-plan.md → archive/pi-crew-v0.5.12-audit-fix-plan.md} +0 -0
  426. /package/docs/{pi-crew-v0.5.13-audit-fix-plan.md → archive/pi-crew-v0.5.13-audit-fix-plan.md} +0 -0
  427. /package/docs/{pi-crew-v0.5.14-audit-fix-plan.md → archive/pi-crew-v0.5.14-audit-fix-plan.md} +0 -0
  428. /package/docs/{pi-crew-v0.5.16-audit-fix-plan.md → archive/pi-crew-v0.5.16-audit-fix-plan.md} +0 -0
  429. /package/docs/{pi-crew-v0.5.17-audit-fix-plan.md → archive/pi-crew-v0.5.17-audit-fix-plan.md} +0 -0
  430. /package/docs/{pi-crew-v0.5.5-audit-fix-plan.md → archive/pi-crew-v0.5.5-audit-fix-plan.md} +0 -0
  431. /package/docs/{pi-crew-v0.5.9-audit-fix-plan.md → archive/pi-crew-v0.5.9-audit-fix-plan.md} +0 -0
  432. /package/docs/{pi-mono-opportunities.md → archive/pi-mono-opportunities.md} +0 -0
  433. /package/docs/{pi-mono-review.md → archive/pi-mono-review.md} +0 -0
  434. /package/docs/{pi-subagent4-comparison.md → archive/pi-subagent4-comparison.md} +0 -0
  435. /package/docs/{pi-subagents3-deep-analysis.md → archive/pi-subagents3-deep-analysis.md} +0 -0
@@ -1,14 +1,14 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
4
3
  import { atomicWriteJson, atomicWriteJsonCoalesced, flushPendingAtomicWrites, readJsonFile } from "../state/atomic-write.ts";
4
+ import type { TeamRunManifest, TeamTaskState } from "../state/types.ts";
5
5
  import { readJsonFileCoalesced } from "../utils/file-coalescer.ts";
6
- import type { CrewAgentProgress, CrewAgentRecord, CrewRuntimeKind } from "./crew-agent-runtime.ts";
7
- import { taskStatusToAgentStatus } from "./crew-agent-runtime.ts";
8
6
  import { logInternalError } from "../utils/internal-error.ts";
9
- import { assertSafePathId, resolveRealContainedPath } from "../utils/safe-paths.ts";
10
7
  import { redactSecretString, redactSecrets } from "../utils/redaction.ts";
8
+ import { assertSafePathId, resolveRealContainedPath } from "../utils/safe-paths.ts";
11
9
  import { sleepSync } from "../utils/sleep.ts";
10
+ import type { CrewAgentProgress, CrewAgentRecord, CrewRuntimeKind } from "./crew-agent-runtime.ts";
11
+ import { taskStatusToAgentStatus } from "./crew-agent-runtime.ts";
12
12
 
13
13
  export function agentsPath(manifest: TeamRunManifest): string {
14
14
  return path.join(manifest.stateRoot, "agents.json");
@@ -65,7 +65,14 @@ const AGENT_READER_TTL_MS = 200;
65
65
  const ASYNC_AGENT_READER_CACHE_MAX_ENTRIES = 128;
66
66
  const AGENTS_LOCK_STALE_MS = 30_000;
67
67
 
68
- const asyncAgentReaderCache = new Map<string, { expiresAt: number; records: CrewAgentRecord[]; inFlight?: Promise<CrewAgentRecord[]> }>();
68
+ const asyncAgentReaderCache = new Map<
69
+ string,
70
+ {
71
+ expiresAt: number;
72
+ records: CrewAgentRecord[];
73
+ inFlight?: Promise<CrewAgentRecord[]>;
74
+ }
75
+ >();
69
76
 
70
77
  function agentsLockPath(manifest: TeamRunManifest): string {
71
78
  return `${agentsPath(manifest)}.lock`;
@@ -76,12 +83,20 @@ function removeStaleAgentsLock(lockPath: string, staleMs: number): boolean {
76
83
  const stat = fs.statSync(lockPath);
77
84
  if (stat.size > 1024) return false;
78
85
  const raw = fs.readFileSync(lockPath, "utf-8");
79
- const parsed = JSON.parse(raw) as { createdAt?: unknown; pid?: unknown };
86
+ const parsed = JSON.parse(raw) as {
87
+ createdAt?: unknown;
88
+ pid?: unknown;
89
+ };
80
90
  const createdAt = typeof parsed.createdAt === "string" ? Date.parse(parsed.createdAt) : NaN;
81
91
  if (Number.isFinite(createdAt) && Date.now() - createdAt <= staleMs) return false;
82
92
  const pid = typeof parsed.pid === "number" ? parsed.pid : undefined;
83
93
  if (pid && pid !== process.pid) {
84
- try { process.kill(pid, 0); return false; } catch { /* owner dead */ }
94
+ try {
95
+ process.kill(pid, 0);
96
+ return false;
97
+ } catch {
98
+ /* owner dead */
99
+ }
85
100
  }
86
101
  fs.rmSync(lockPath, { force: true });
87
102
  return true;
@@ -99,7 +114,13 @@ function withAgentsLock<T>(manifest: TeamRunManifest, fn: () => T): T {
99
114
  try {
100
115
  const fd = fs.openSync(filePath, fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_EXCL, 0o644);
101
116
  try {
102
- fs.writeSync(fd, JSON.stringify({ pid: process.pid, createdAt: new Date().toISOString() }));
117
+ fs.writeSync(
118
+ fd,
119
+ JSON.stringify({
120
+ pid: process.pid,
121
+ createdAt: new Date().toISOString(),
122
+ }),
123
+ );
103
124
  } finally {
104
125
  fs.closeSync(fd);
105
126
  }
@@ -108,10 +129,15 @@ function withAgentsLock<T>(manifest: TeamRunManifest, fn: () => T): T {
108
129
  const code = (error as NodeJS.ErrnoException).code;
109
130
  if (code !== "EEXIST" && code !== "EISDIR") throw error;
110
131
  if (code === "EISDIR") {
111
- try { fs.rmSync(filePath, { recursive: true, force: true }); } catch { /* ignore */ }
132
+ try {
133
+ fs.rmSync(filePath, { recursive: true, force: true });
134
+ } catch {
135
+ /* ignore */
136
+ }
112
137
  continue;
113
138
  }
114
- if (!removeStaleAgentsLock(filePath, AGENTS_LOCK_STALE_MS) && Date.now() > deadline) throw new Error(`Crew agents file is locked by another operation: ${agentsPath(manifest)}`);
139
+ if (!removeStaleAgentsLock(filePath, AGENTS_LOCK_STALE_MS) && Date.now() > deadline)
140
+ throw new Error(`Crew agents file is locked by another operation: ${agentsPath(manifest)}`);
115
141
  sleepSync(Math.min(250, 25 * 2 ** attempt));
116
142
  attempt += 1;
117
143
  }
@@ -119,11 +145,22 @@ function withAgentsLock<T>(manifest: TeamRunManifest, fn: () => T): T {
119
145
  try {
120
146
  return fn();
121
147
  } finally {
122
- try { fs.rmSync(filePath, { force: true }); } catch { /* best-effort */ }
148
+ try {
149
+ fs.rmSync(filePath, { force: true });
150
+ } catch {
151
+ /* best-effort */
152
+ }
123
153
  }
124
154
  }
125
155
 
126
- function setAsyncAgentReaderCache(filePath: string, entry: { expiresAt: number; records: CrewAgentRecord[]; inFlight?: Promise<CrewAgentRecord[]> }): void {
156
+ function setAsyncAgentReaderCache(
157
+ filePath: string,
158
+ entry: {
159
+ expiresAt: number;
160
+ records: CrewAgentRecord[];
161
+ inFlight?: Promise<CrewAgentRecord[]>;
162
+ },
163
+ ): void {
127
164
  const now = Date.now();
128
165
  for (const [key, cached] of asyncAgentReaderCache) {
129
166
  if (cached.expiresAt <= now && !cached.inFlight) asyncAgentReaderCache.delete(key);
@@ -143,7 +180,11 @@ export function readCrewAgents(manifest: TeamRunManifest): CrewAgentRecord[] {
143
180
  // after at most one coalesce window (250 ms).
144
181
  flushPendingAtomicWrites();
145
182
  try {
146
- const records = readJsonFileCoalesced(agentsPath(manifest), AGENT_READER_TTL_MS, () => readJsonFile<CrewAgentRecord[]>(agentsPath(manifest)) ?? []);
183
+ const records = readJsonFileCoalesced(
184
+ agentsPath(manifest),
185
+ AGENT_READER_TTL_MS,
186
+ () => readJsonFile<CrewAgentRecord[]>(agentsPath(manifest)) ?? [],
187
+ );
147
188
  // Validate schema and deduplicate by id to handle concurrent write conflicts
148
189
  const seen = new Set<string>();
149
190
  const deduped = records.filter((r) => {
@@ -171,7 +212,7 @@ export async function readCrewAgentsAsync(manifest: TeamRunManifest): Promise<Cr
171
212
  const inFlight = (async (): Promise<CrewAgentRecord[]> => {
172
213
  try {
173
214
  const parsed = JSON.parse(await fs.promises.readFile(filePath, "utf-8")) as unknown;
174
- const raw = Array.isArray(parsed) ? redactSecrets(parsed) as CrewAgentRecord[] : [];
215
+ const raw = Array.isArray(parsed) ? (redactSecrets(parsed) as CrewAgentRecord[]) : [];
175
216
  // Deduplicate by id to handle concurrent write conflicts
176
217
  const seen = new Set<string>();
177
218
  const deduped = raw.filter((r) => {
@@ -181,16 +222,30 @@ export async function readCrewAgentsAsync(manifest: TeamRunManifest): Promise<Cr
181
222
  return true;
182
223
  });
183
224
  if (deduped.length !== raw.length) {
184
- try { saveCrewAgents(manifest, deduped); } catch { /* best-effort */ }
225
+ try {
226
+ saveCrewAgents(manifest, deduped);
227
+ } catch {
228
+ /* best-effort */
229
+ }
185
230
  }
186
- setAsyncAgentReaderCache(filePath, { expiresAt: Date.now() + AGENT_READER_TTL_MS, records: deduped });
231
+ setAsyncAgentReaderCache(filePath, {
232
+ expiresAt: Date.now() + AGENT_READER_TTL_MS,
233
+ records: deduped,
234
+ });
187
235
  return deduped;
188
236
  } catch {
189
- setAsyncAgentReaderCache(filePath, { expiresAt: Date.now() + AGENT_READER_TTL_MS, records: [] });
237
+ setAsyncAgentReaderCache(filePath, {
238
+ expiresAt: Date.now() + AGENT_READER_TTL_MS,
239
+ records: [],
240
+ });
190
241
  return [];
191
242
  }
192
243
  })();
193
- setAsyncAgentReaderCache(filePath, { expiresAt: now + AGENT_READER_TTL_MS, records: cached?.records ?? [], inFlight });
244
+ setAsyncAgentReaderCache(filePath, {
245
+ expiresAt: now + AGENT_READER_TTL_MS,
246
+ records: cached?.records ?? [],
247
+ inFlight,
248
+ });
194
249
  return inFlight;
195
250
  }
196
251
 
@@ -210,9 +265,7 @@ const TERMINAL_AGENT_STATUSES = new Set(["completed", "failed", "cancelled", "bl
210
265
  * User policy (v0.9.16): cancelled / stopped crew-agent records leave NO trace.
211
266
  * `failed` records keep their audit trail (different from cancel — agent errored).
212
267
  */
213
- export function shouldDeleteCrewAgentOnTerminalStatus(
214
- record: Pick<CrewAgentRecord, "status">,
215
- ): boolean {
268
+ export function shouldDeleteCrewAgentOnTerminalStatus(record: Pick<CrewAgentRecord, "status">): boolean {
216
269
  const s = record.status;
217
270
  return s === "cancelled" || s === "stopped";
218
271
  }
@@ -222,10 +275,7 @@ export function shouldDeleteCrewAgentOnTerminalStatus(
222
275
  * and the per-task `status.json`. Called on cancellation to wipe the trace.
223
276
  * Safe-fail: missing files are treated as success (already removed).
224
277
  */
225
- export function removeCrewAgent(
226
- manifest: TeamRunManifest,
227
- taskId: string,
228
- ): { removedIndex: boolean; removedStatus: boolean } {
278
+ export function removeCrewAgent(manifest: TeamRunManifest, taskId: string): { removedIndex: boolean; removedStatus: boolean } {
229
279
  let removedIndex = false;
230
280
  let removedStatus = false;
231
281
  // 1. Remove from agents.json index
@@ -254,7 +304,11 @@ export function removeCrewAgent(
254
304
 
255
305
  export function upsertCrewAgent(manifest: TeamRunManifest, record: CrewAgentRecord): void {
256
306
  // Guard: skip if run state has been deleted (prune/forget/cleanup)
257
- try { fs.statSync(manifest.stateRoot); } catch { return; }
307
+ try {
308
+ fs.statSync(manifest.stateRoot);
309
+ } catch {
310
+ return;
311
+ }
258
312
  // User policy (v0.9.16): cancelled / stopped crew agents leave NO trace.
259
313
  // We delete BOTH the index entry (agents.json) and the per-task status.json
260
314
  // immediately so the UI dashboard/widget never see the cancelled agent again.
@@ -268,7 +322,7 @@ export function upsertCrewAgent(manifest: TeamRunManifest, record: CrewAgentReco
268
322
  const existing = readCrewAgents(manifest);
269
323
  // Deduplicate by id: keep newer record when same id appears
270
324
  const idIndex = new Map(existing.map((item, i) => [item.id, i]));
271
- const merged: CrewAgentRecord[] = existing.map((item) => item.id === record.id ? record : item);
325
+ const merged: CrewAgentRecord[] = existing.map((item) => (item.id === record.id ? record : item));
272
326
  if (!idIndex.has(record.id)) merged.push(record);
273
327
  // 2.5 caller migration: coalesce non-terminal progress writes; flush
274
328
  // terminal statuses (completed/failed/cancelled/blocked) durably so
@@ -363,7 +417,9 @@ function writeSeqToSidecar(filePath: string, seq: number): void {
363
417
  function nextAgentEventSeq(filePath: string): number {
364
418
  if (!fs.existsSync(filePath)) {
365
419
  // Clean up stale sidecar when main file is gone.
366
- try { fs.unlinkSync(`${filePath}.${AGENT_EVENT_SEQ_SIDECAR}`); } catch {}
420
+ try {
421
+ fs.unlinkSync(`${filePath}.${AGENT_EVENT_SEQ_SIDECAR}`);
422
+ } catch {}
367
423
  return 1;
368
424
  }
369
425
  const stat = fs.statSync(filePath);
@@ -372,7 +428,11 @@ function nextAgentEventSeq(filePath: string): number {
372
428
  // FIX: Try sidecar file for O(1) lookup before falling back to O(n) scan.
373
429
  const sidecarSeq = readSeqFromSidecar(filePath);
374
430
  if (sidecarSeq !== undefined) {
375
- setAgentEventSeqCache(filePath, { size: stat.size, mtimeMs: stat.mtimeMs, seq: sidecarSeq });
431
+ setAgentEventSeqCache(filePath, {
432
+ size: stat.size,
433
+ mtimeMs: stat.mtimeMs,
434
+ seq: sidecarSeq,
435
+ });
376
436
  return sidecarSeq + 1;
377
437
  }
378
438
  let max = 0;
@@ -386,7 +446,11 @@ function nextAgentEventSeq(filePath: string): number {
386
446
  max += 1;
387
447
  }
388
448
  }
389
- setAgentEventSeqCache(filePath, { size: stat.size, mtimeMs: stat.mtimeMs, seq: max });
449
+ setAgentEventSeqCache(filePath, {
450
+ size: stat.size,
451
+ mtimeMs: stat.mtimeMs,
452
+ seq: max,
453
+ });
390
454
  writeSeqToSidecar(filePath, max);
391
455
  return max + 1;
392
456
  }
@@ -398,7 +462,11 @@ export function appendCrewAgentEvent(manifest: TeamRunManifest, taskId: string,
398
462
  fs.appendFileSync(filePath, `${JSON.stringify(redactSecrets({ seq, time: new Date().toISOString(), event }))}\n`, "utf-8");
399
463
  try {
400
464
  const stat = fs.statSync(filePath);
401
- setAgentEventSeqCache(filePath, { size: stat.size, mtimeMs: stat.mtimeMs, seq });
465
+ setAgentEventSeqCache(filePath, {
466
+ size: stat.size,
467
+ mtimeMs: stat.mtimeMs,
468
+ seq,
469
+ });
402
470
  writeSeqToSidecar(filePath, seq);
403
471
  } catch (error) {
404
472
  logInternalError("crew-agent-records.stat", error, `filePath=${filePath}`);
@@ -415,34 +483,64 @@ function readCrewAgentEvents(manifest: TeamRunManifest, taskId: string): unknown
415
483
  return readCrewAgentEventsCursor(manifest, taskId).events;
416
484
  }
417
485
 
418
- export function readCrewAgentEventsCursor(manifest: TeamRunManifest, taskId: string, options: CrewAgentEventCursorOptions = {}): { path: string; events: unknown[]; nextSeq: number; total: number } {
486
+ export function readCrewAgentEventsCursor(
487
+ manifest: TeamRunManifest,
488
+ taskId: string,
489
+ options: CrewAgentEventCursorOptions = {},
490
+ ): { path: string; events: unknown[]; nextSeq: number; total: number } {
419
491
  let filePath: string;
420
492
  try {
421
493
  filePath = agentEventsPath(manifest, taskId);
422
494
  } catch {
423
- return { path: "", events: [], nextSeq: options.sinceSeq ?? 0, total: 0 };
495
+ return {
496
+ path: "",
497
+ events: [],
498
+ nextSeq: options.sinceSeq ?? 0,
499
+ total: 0,
500
+ };
424
501
  }
425
- if (!fs.existsSync(filePath)) return { path: filePath, events: [], nextSeq: options.sinceSeq ?? 0, total: 0 };
502
+ if (!fs.existsSync(filePath))
503
+ return {
504
+ path: filePath,
505
+ events: [],
506
+ nextSeq: options.sinceSeq ?? 0,
507
+ total: 0,
508
+ };
426
509
  try {
427
510
  filePath = safeExistingAgentFile(manifest, taskId, "events.jsonl");
428
511
  } catch {
429
- return { path: "", events: [], nextSeq: options.sinceSeq ?? 0, total: 0 };
512
+ return {
513
+ path: "",
514
+ events: [],
515
+ nextSeq: options.sinceSeq ?? 0,
516
+ total: 0,
517
+ };
430
518
  }
431
- const sinceSeq = typeof options.sinceSeq === "number" && Number.isInteger(options.sinceSeq) && options.sinceSeq >= 0 ? options.sinceSeq : 0;
519
+ const sinceSeq =
520
+ typeof options.sinceSeq === "number" && Number.isInteger(options.sinceSeq) && options.sinceSeq >= 0 ? options.sinceSeq : 0;
432
521
  const limit = typeof options.limit === "number" && Number.isInteger(options.limit) && options.limit >= 0 ? options.limit : undefined;
433
- const parsed = fs.readFileSync(filePath, "utf-8").split(/\r?\n/).filter(Boolean).map((line, index) => {
434
- try {
435
- const event = JSON.parse(line) as Record<string, unknown>;
436
- if (typeof event.seq !== "number") event.seq = index + 1;
437
- return event;
438
- } catch {
439
- return { seq: index + 1, raw: line };
440
- }
441
- });
522
+ const parsed = fs
523
+ .readFileSync(filePath, "utf-8")
524
+ .split(/\r?\n/)
525
+ .filter(Boolean)
526
+ .map((line, index) => {
527
+ try {
528
+ const event = JSON.parse(line) as Record<string, unknown>;
529
+ if (typeof event.seq !== "number") event.seq = index + 1;
530
+ return event;
531
+ } catch {
532
+ return { seq: index + 1, raw: line };
533
+ }
534
+ });
442
535
  const filtered = parsed.filter((event) => typeof event.seq === "number" && event.seq > sinceSeq);
443
536
  const events = limit !== undefined ? filtered.slice(0, limit) : filtered;
444
- const returnedMaxSeq = events.reduce((max, event) => typeof event.seq === "number" ? Math.max(max, event.seq) : max, sinceSeq);
445
- return { path: filePath, events, nextSeq: returnedMaxSeq, total: filtered.length };
537
+ const returnedMaxSeq = events.reduce((max, event) => (typeof event.seq === "number" ? Math.max(max, event.seq) : max), sinceSeq);
538
+ return {
539
+ path: filePath,
540
+ events,
541
+ nextSeq: returnedMaxSeq,
542
+ total: filtered.length,
543
+ };
446
544
  }
447
545
 
448
546
  export function appendCrewAgentOutput(manifest: TeamRunManifest, taskId: string, text: string): void {
@@ -25,12 +25,7 @@
25
25
  import { logInternalError } from "../utils/internal-error.ts";
26
26
 
27
27
  /** Valid hook event types in the crew lifecycle. */
28
- export type CrewHookEventType =
29
- | 'task_started'
30
- | 'task_completed'
31
- | 'task_failed'
32
- | 'run_completed'
33
- | 'run_failed';
28
+ export type CrewHookEventType = "task_started" | "task_completed" | "task_failed" | "run_completed" | "run_failed";
34
29
 
35
30
  /**
36
31
  * A hook event emitted by the crew runtime.
@@ -59,11 +54,7 @@ export type CrewHook = (event: CrewHookEvent) => void | Promise<void>;
59
54
  */
60
55
  export function isValidEventType(type: string): type is CrewHookEventType {
61
56
  return (
62
- type === 'task_started' ||
63
- type === 'task_completed' ||
64
- type === 'task_failed' ||
65
- type === 'run_completed' ||
66
- type === 'run_failed'
57
+ type === "task_started" || type === "task_completed" || type === "task_failed" || type === "run_completed" || type === "run_failed"
67
58
  );
68
59
  }
69
60
 
@@ -71,15 +62,15 @@ export function isValidEventType(type: string): type is CrewHookEventType {
71
62
  * Type guard to check if an object is a valid CrewHookEvent.
72
63
  */
73
64
  export function isHookEvent(obj: unknown): obj is CrewHookEvent {
74
- if (typeof obj !== 'object' || obj === null) return false;
65
+ if (typeof obj !== "object" || obj === null) return false;
75
66
  const event = obj as Record<string, unknown>;
76
67
  return (
77
- typeof event.type === 'string' &&
68
+ typeof event.type === "string" &&
78
69
  isValidEventType(event.type) &&
79
- typeof event.timestamp === 'string' &&
80
- typeof event.runId === 'string' &&
81
- (event.taskId === undefined || typeof event.taskId === 'string') &&
82
- (event.data === undefined || typeof event.data === 'object')
70
+ typeof event.timestamp === "string" &&
71
+ typeof event.runId === "string" &&
72
+ (event.taskId === undefined || typeof event.taskId === "string") &&
73
+ (event.data === undefined || typeof event.data === "object")
83
74
  );
84
75
  }
85
76
 
@@ -96,13 +87,7 @@ export class HookRegistry {
96
87
  constructor() {
97
88
  this.hooks = new Map();
98
89
  // Initialize with empty Sets for all event types
99
- const eventTypes: CrewHookEventType[] = [
100
- 'task_started',
101
- 'task_completed',
102
- 'task_failed',
103
- 'run_completed',
104
- 'run_failed',
105
- ];
90
+ const eventTypes: CrewHookEventType[] = ["task_started", "task_completed", "task_failed", "run_completed", "run_failed"];
106
91
  for (const type of eventTypes) {
107
92
  this.hooks.set(type, new Set());
108
93
  }
@@ -5,9 +5,7 @@ export interface EventBus {
5
5
  emit(event: string, data: unknown): void;
6
6
  }
7
7
 
8
- export type RpcReply<T = void> =
9
- | { success: true; data?: T }
10
- | { success: false; error: string };
8
+ export type RpcReply<T = void> = { success: true; data?: T } | { success: false; error: string };
11
9
 
12
10
  export const PROTOCOL_VERSION = 1;
13
11
 
@@ -84,39 +82,43 @@ export function registerCrewRpcHandlers(deps: RpcDeps): RpcHandle {
84
82
  if (params.token !== EXPECTED_TOKEN) {
85
83
  console.warn(
86
84
  `[pi-crew SECURITY] RPC invocation rejected: missing/invalid token (source=${params.source ?? "(none)"}). ` +
87
- `Privileged RPC requires the in-process token. Request may be from an untrusted extension.`,
85
+ `Privileged RPC requires the in-process token. Request may be from an untrusted extension.`,
88
86
  );
89
87
  return false;
90
88
  }
91
89
  if (!params.source || params.source !== CREW_RPC_SOURCE) {
92
90
  console.warn(
93
91
  `[pi-crew SECURITY] RPC invocation from unexpected source: ${params.source ?? "(none)"}. ` +
94
- `Expected '${CREW_RPC_SOURCE}'. Request may be from an untrusted extension.`,
92
+ `Expected '${CREW_RPC_SOURCE}'. Request may be from an untrusted extension.`,
95
93
  );
96
94
  return false;
97
95
  }
98
96
  return true;
99
97
  }
100
98
 
101
- const unsubSpawn = handleRpc<{ requestId: string; type: string; prompt: string; options?: Record<string, unknown>; source?: string; token?: string }>(
102
- events,
103
- "crew:rpc:spawn",
104
- (params) => {
105
- if (!validateRpcSource(params)) throw new Error("Unauthorized: RPC spawn requires valid token and source='pi-crew'");
106
- const ctx = getCtx();
107
- if (!ctx) throw new Error("No active session");
108
- return { id: spawn(params.type, params.prompt, params.options ?? {}) };
109
- },
110
- );
99
+ const unsubSpawn = handleRpc<{
100
+ requestId: string;
101
+ type: string;
102
+ prompt: string;
103
+ options?: Record<string, unknown>;
104
+ source?: string;
105
+ token?: string;
106
+ }>(events, "crew:rpc:spawn", (params) => {
107
+ if (!validateRpcSource(params)) throw new Error("Unauthorized: RPC spawn requires valid token and source='pi-crew'");
108
+ const ctx = getCtx();
109
+ if (!ctx) throw new Error("No active session");
110
+ return { id: spawn(params.type, params.prompt, params.options ?? {}) };
111
+ });
111
112
 
112
- const unsubStop = handleRpc<{ requestId: string; agentId: string; source?: string; token?: string }>(
113
- events,
114
- "crew:rpc:stop",
115
- (params) => {
116
- if (!validateRpcSource(params)) throw new Error("Unauthorized: RPC stop requires valid token and source='pi-crew'");
117
- if (!abort(params.agentId)) throw new Error("Agent not found");
118
- },
119
- );
113
+ const unsubStop = handleRpc<{
114
+ requestId: string;
115
+ agentId: string;
116
+ source?: string;
117
+ token?: string;
118
+ }>(events, "crew:rpc:stop", (params) => {
119
+ if (!validateRpcSource(params)) throw new Error("Unauthorized: RPC stop requires valid token and source='pi-crew'");
120
+ if (!abort(params.agentId)) throw new Error("Agent not found");
121
+ });
120
122
 
121
123
  return { unsubPing, unsubSpawn, unsubStop };
122
124
  }
@@ -13,15 +13,19 @@
13
13
  */
14
14
 
15
15
  import { defineTool, type ToolDefinition } from "@earendil-works/pi-coding-agent";
16
- import { Type, type Static } from "@sinclair/typebox";
17
- import { listLiveAgents, sendIrcMessage, broadcastIrcMessage, respondAsBackground } from "../live-agent-manager.ts";
16
+ import { type Static, Type } from "@sinclair/typebox";
17
+ import { broadcastIrcMessage, listLiveAgents, respondAsBackground, sendIrcMessage } from "../live-agent-manager.ts";
18
18
  import type { IrcMessage } from "../live-irc.ts";
19
19
 
20
20
  const IrcParams = Type.Object({
21
21
  op: Type.Union(
22
22
  [
23
- Type.Literal("send", { description: "Send a message to one peer or to all peers." }),
24
- Type.Literal("list", { description: "List currently visible peers." }),
23
+ Type.Literal("send", {
24
+ description: "Send a message to one peer or to all peers.",
25
+ }),
26
+ Type.Literal("list", {
27
+ description: "List currently visible peers.",
28
+ }),
25
29
  ],
26
30
  { description: "IRC operation." },
27
31
  ),
@@ -37,7 +41,8 @@ const IrcParams = Type.Object({
37
41
  ),
38
42
  awaitReply: Type.Optional(
39
43
  Type.Boolean({
40
- description: "Wait for a prose reply (default: true for DM, false for broadcast). For DMs the recipient receives the message as a non-blocking background turn and its reply is returned to the caller. Broadcast always ignores this flag.",
44
+ description:
45
+ "Wait for a prose reply (default: true for DM, false for broadcast). For DMs the recipient receives the message as a non-blocking background turn and its reply is returned to the caller. Broadcast always ignores this flag.",
41
46
  }),
42
47
  ),
43
48
  });
@@ -74,9 +79,7 @@ interface IrcDetails {
74
79
  *
75
80
  * @param selfId — This agent's ID (runId:taskId format)
76
81
  */
77
- export function createIrcTool(
78
- selfId: string,
79
- ): ToolDefinition<typeof IrcParams, IrcDetails> {
82
+ export function createIrcTool(selfId: string): ToolDefinition<typeof IrcParams, IrcDetails> {
80
83
  return defineTool({
81
84
  name: "irc",
82
85
  label: "IRC",
@@ -95,7 +98,10 @@ export function createIrcTool(
95
98
  _signal: AbortSignal | undefined,
96
99
  _onUpdate: unknown,
97
100
  _ctx: unknown,
98
- ): Promise<{ content: Array<{ type: "text"; text: string }>; details: IrcDetails }> {
101
+ ): Promise<{
102
+ content: Array<{ type: "text"; text: string }>;
103
+ details: IrcDetails;
104
+ }> {
99
105
  if (params.op === "list") {
100
106
  return executeList(selfId);
101
107
  }
@@ -104,13 +110,20 @@ export function createIrcTool(
104
110
  }
105
111
  return {
106
112
  content: [{ type: "text", text: "Unknown irc op." }],
107
- details: { op: params.op, from: selfId, error: "Unknown operation." },
113
+ details: {
114
+ op: params.op,
115
+ from: selfId,
116
+ error: "Unknown operation.",
117
+ },
108
118
  };
109
119
  },
110
120
  });
111
121
  }
112
122
 
113
- function executeList(selfId: string): { content: Array<{ type: "text"; text: string }>; details: IrcDetails } {
123
+ function executeList(selfId: string): {
124
+ content: Array<{ type: "text"; text: string }>;
125
+ details: IrcDetails;
126
+ } {
114
127
  const agents = listLiveAgents();
115
128
  const peers = agents
116
129
  .filter((a) => a.agentId !== selfId && (a.status === "running" || a.status === "queued"))
@@ -135,7 +148,10 @@ function executeList(selfId: string): { content: Array<{ type: "text"; text: str
135
148
  async function executeSend(
136
149
  selfId: string,
137
150
  params: IrcParams,
138
- ): Promise<{ content: Array<{ type: "text"; text: string }>; details: IrcDetails }> {
151
+ ): Promise<{
152
+ content: Array<{ type: "text"; text: string }>;
153
+ details: IrcDetails;
154
+ }> {
139
155
  const to = params.to?.trim();
140
156
  const message = params.message?.trim();
141
157
 
@@ -148,13 +164,23 @@ async function executeSend(
148
164
  if (!message) {
149
165
  return {
150
166
  content: [{ type: "text", text: '`message` is required for op="send".' }],
151
- details: { op: "send", from: selfId, to, error: "Missing 'message' field." },
167
+ details: {
168
+ op: "send",
169
+ from: selfId,
170
+ to,
171
+ error: "Missing 'message' field.",
172
+ },
152
173
  };
153
174
  }
154
175
  if (to === selfId) {
155
176
  return {
156
177
  content: [{ type: "text", text: "Cannot send a message to yourself." }],
157
- details: { op: "send", from: selfId, to, error: "Self-message not allowed." },
178
+ details: {
179
+ op: "send",
180
+ from: selfId,
181
+ to,
182
+ error: "Self-message not allowed.",
183
+ },
158
184
  };
159
185
  }
160
186
 
@@ -188,7 +214,9 @@ async function executeSend(
188
214
  if (!target || (target.status !== "running" && target.status !== "queued")) {
189
215
  notFound.push(to);
190
216
  } else {
191
- const result = await respondAsBackground(to, selfId, message, { awaitReply: true });
217
+ const result = await respondAsBackground(to, selfId, message, {
218
+ awaitReply: true,
219
+ });
192
220
  if (result.ok) {
193
221
  delivered.push(to);
194
222
  if (result.replyContent) replies.push({ from: to, text: result.replyContent });
@@ -10,9 +10,9 @@
10
10
  */
11
11
 
12
12
  import { defineTool, type ToolDefinition } from "@earendil-works/pi-coding-agent";
13
- import { Type, type Static } from "@sinclair/typebox";
14
- import type { YieldResult } from "../yield-handler.ts";
13
+ import { type Static, Type } from "@sinclair/typebox";
15
14
  import { logInternalError } from "../../utils/internal-error.ts";
15
+ import type { YieldResult } from "../yield-handler.ts";
16
16
 
17
17
  const SubmitResultParams = Type.Object({
18
18
  summary: Type.String({ description: "Summary of completed work." }),
@@ -64,7 +64,10 @@ export function createSubmitResultTool(
64
64
  _signal: AbortSignal | undefined,
65
65
  _onUpdate: unknown,
66
66
  _ctx: unknown,
67
- ): Promise<{ content: Array<{ type: "text"; text: string }>; details: SubmitResultDetails }> {
67
+ ): Promise<{
68
+ content: Array<{ type: "text"; text: string }>;
69
+ details: SubmitResultDetails;
70
+ }> {
68
71
  const result: YieldResult = {
69
72
  summary: params.summary,
70
73
  toolCallId,
@@ -72,8 +75,16 @@ export function createSubmitResultTool(
72
75
  ...(params.structuredData ? { structuredData: params.structuredData } : {}),
73
76
  };
74
77
  // Build response first so the model always gets confirmation
75
- const response: { content: Array<{ type: "text"; text: string }>; details: SubmitResultDetails } = {
76
- content: [{ type: "text", text: "Result submitted successfully. Thank you." }],
78
+ const response: {
79
+ content: Array<{ type: "text"; text: string }>;
80
+ details: SubmitResultDetails;
81
+ } = {
82
+ content: [
83
+ {
84
+ type: "text",
85
+ text: "Result submitted successfully. Thank you.",
86
+ },
87
+ ],
77
88
  details: {
78
89
  summary: params.summary,
79
90
  artifacts: params.artifacts,