pi-crew 0.9.53 → 0.9.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (493) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/package.json +6 -3
  3. package/scripts/clean-strip-types.mjs +60 -0
  4. package/src/adapters/claude-adapter.ts +23 -0
  5. package/src/adapters/codex-adapter.ts +21 -0
  6. package/src/adapters/cursor-adapter.ts +17 -0
  7. package/src/adapters/export-util.ts +143 -0
  8. package/src/adapters/index.ts +15 -0
  9. package/src/adapters/registry.ts +18 -0
  10. package/src/adapters/types.ts +23 -0
  11. package/src/agents/agent-config.ts +194 -0
  12. package/src/agents/agent-search.ts +98 -0
  13. package/src/agents/agent-serializer.ts +38 -0
  14. package/src/agents/discover-agents.ts +623 -0
  15. package/src/benchmark/benchmark-runner.ts +313 -0
  16. package/src/benchmark/feedback-loop.ts +73 -0
  17. package/src/config/config.ts +1270 -0
  18. package/src/config/defaults.ts +193 -0
  19. package/src/config/drift-detector.ts +290 -0
  20. package/src/config/markers.ts +330 -0
  21. package/src/config/resilient-parser.ts +117 -0
  22. package/src/config/role-tools.ts +118 -0
  23. package/src/config/suggestions.ts +75 -0
  24. package/src/config/types.ts +280 -0
  25. package/src/errors.ts +194 -0
  26. package/src/extension/action-suggestions.ts +117 -0
  27. package/src/extension/async-notifier.ts +179 -0
  28. package/src/extension/autonomous-policy.ts +209 -0
  29. package/src/extension/command-completions.ts +120 -0
  30. package/src/extension/context-status-injection.ts +185 -0
  31. package/src/extension/crew-autocomplete.ts +131 -0
  32. package/src/extension/crew-cleanup.ts +168 -0
  33. package/src/extension/crew-input-router.ts +93 -0
  34. package/src/extension/crew-shortcuts.ts +87 -0
  35. package/src/extension/crew-vibes/cat-frames.ts +18 -0
  36. package/src/extension/crew-vibes/config.ts +195 -0
  37. package/src/extension/crew-vibes/figures.ts +101 -0
  38. package/src/extension/crew-vibes/font-detect.ts +71 -0
  39. package/src/extension/crew-vibes/footer.ts +292 -0
  40. package/src/extension/crew-vibes/index.ts +432 -0
  41. package/src/extension/crew-vibes/provider-usage.ts +396 -0
  42. package/src/extension/crew-vibes/render.ts +206 -0
  43. package/src/extension/crew-vibes/speed.ts +286 -0
  44. package/src/extension/cross-extension-rpc.ts +350 -0
  45. package/src/extension/help.ts +62 -0
  46. package/src/extension/import-index.ts +74 -0
  47. package/src/extension/knowledge-injection.ts +402 -0
  48. package/src/extension/management.ts +571 -0
  49. package/src/extension/message-renderers.ts +108 -0
  50. package/src/extension/notification-router.ts +152 -0
  51. package/src/extension/notification-sink.ts +54 -0
  52. package/src/extension/pi-api.ts +56 -0
  53. package/src/extension/plan-orchestrate.ts +302 -0
  54. package/src/extension/project-init.ts +170 -0
  55. package/src/extension/register.ts +113 -0
  56. package/src/extension/registration/artifact-cleanup.ts +20 -0
  57. package/src/extension/registration/command-registration.ts +58 -0
  58. package/src/extension/registration/command-utils.ts +58 -0
  59. package/src/extension/registration/commands.ts +1228 -0
  60. package/src/extension/registration/compaction-guard.ts +333 -0
  61. package/src/extension/registration/context-builder.ts +135 -0
  62. package/src/extension/registration/crash-recovery-cache.ts +54 -0
  63. package/src/extension/registration/foreground-run-controller.ts +238 -0
  64. package/src/extension/registration/hook-registration.ts +119 -0
  65. package/src/extension/registration/lazy-configurers.ts +124 -0
  66. package/src/extension/registration/lifecycle-handlers.ts +923 -0
  67. package/src/extension/registration/lifecycle.ts +259 -0
  68. package/src/extension/registration/observability.ts +322 -0
  69. package/src/extension/registration/registration-types.ts +158 -0
  70. package/src/extension/registration/runtime-cleanup.ts +230 -0
  71. package/src/extension/registration/subagent-helpers.ts +125 -0
  72. package/src/extension/registration/subagent-manager-setup.ts +330 -0
  73. package/src/extension/registration/subagent-tools.ts +456 -0
  74. package/src/extension/registration/team-tool.ts +225 -0
  75. package/src/extension/registration/tool-registration.ts +50 -0
  76. package/src/extension/registration/ui.ts +172 -0
  77. package/src/extension/registration/viewers.ts +121 -0
  78. package/src/extension/registration/wire-cross-extension.ts +37 -0
  79. package/src/extension/result-watcher.ts +139 -0
  80. package/src/extension/rpc-hmac.ts +256 -0
  81. package/src/extension/run-bundle-schema.ts +104 -0
  82. package/src/extension/run-export.ts +97 -0
  83. package/src/extension/run-import.ts +168 -0
  84. package/src/extension/run-index.ts +131 -0
  85. package/src/extension/run-maintenance.ts +190 -0
  86. package/src/extension/session-summary.ts +18 -0
  87. package/src/extension/team-manager-command.ts +153 -0
  88. package/src/extension/team-onboard.ts +180 -0
  89. package/src/extension/team-recommendation.ts +288 -0
  90. package/src/extension/team-tool/anchor.ts +172 -0
  91. package/src/extension/team-tool/api.ts +1244 -0
  92. package/src/extension/team-tool/auto-summarize.ts +142 -0
  93. package/src/extension/team-tool/cache-control.ts +19 -0
  94. package/src/extension/team-tool/cancel.ts +337 -0
  95. package/src/extension/team-tool/chain-dispatch.ts +95 -0
  96. package/src/extension/team-tool/chain-executor.ts +379 -0
  97. package/src/extension/team-tool/config-patch.ts +50 -0
  98. package/src/extension/team-tool/context.ts +149 -0
  99. package/src/extension/team-tool/destructive-gate.ts +52 -0
  100. package/src/extension/team-tool/dispatch/automate.ts +80 -0
  101. package/src/extension/team-tool/dispatch/control.ts +42 -0
  102. package/src/extension/team-tool/dispatch/index.ts +95 -0
  103. package/src/extension/team-tool/dispatch/manage.ts +161 -0
  104. package/src/extension/team-tool/dispatch/run.ts +53 -0
  105. package/src/extension/team-tool/dispatch/status.ts +187 -0
  106. package/src/extension/team-tool/doctor.ts +431 -0
  107. package/src/extension/team-tool/explain.ts +282 -0
  108. package/src/extension/team-tool/failure-patterns.ts +118 -0
  109. package/src/extension/team-tool/goal-wrap.ts +330 -0
  110. package/src/extension/team-tool/goal.ts +556 -0
  111. package/src/extension/team-tool/handle-schedule.ts +332 -0
  112. package/src/extension/team-tool/handle-settings.ts +520 -0
  113. package/src/extension/team-tool/health-monitor.ts +481 -0
  114. package/src/extension/team-tool/inspect.ts +88 -0
  115. package/src/extension/team-tool/intent-policy.ts +45 -0
  116. package/src/extension/team-tool/lifecycle-actions.ts +615 -0
  117. package/src/extension/team-tool/orchestrate.ts +98 -0
  118. package/src/extension/team-tool/parallel-dispatch.ts +183 -0
  119. package/src/extension/team-tool/plan.ts +50 -0
  120. package/src/extension/team-tool/respond.ts +150 -0
  121. package/src/extension/team-tool/run-deadline.ts +66 -0
  122. package/src/extension/team-tool/run-not-found.ts +49 -0
  123. package/src/extension/team-tool/run.ts +1051 -0
  124. package/src/extension/team-tool/status.ts +260 -0
  125. package/src/extension/team-tool/workflow-manage.ts +261 -0
  126. package/src/extension/team-tool-types.ts +26 -0
  127. package/src/extension/team-tool.ts +750 -0
  128. package/src/extension/tool-result.ts +16 -0
  129. package/src/extension/validate-resources.ts +108 -0
  130. package/src/hooks/registry.ts +200 -0
  131. package/src/hooks/types.ts +69 -0
  132. package/src/i18n.ts +212 -0
  133. package/src/observability/correlation.ts +50 -0
  134. package/src/observability/event-bus.ts +86 -0
  135. package/src/observability/event-to-metric.ts +170 -0
  136. package/src/observability/exporters/adapter.ts +27 -0
  137. package/src/observability/exporters/otlp-exporter.ts +356 -0
  138. package/src/observability/exporters/prometheus-exporter.ts +54 -0
  139. package/src/observability/metric-registry.ts +100 -0
  140. package/src/observability/metric-retention.ts +64 -0
  141. package/src/observability/metric-sink.ts +99 -0
  142. package/src/observability/metrics-primitives.ts +219 -0
  143. package/src/plugins/plugin-define.ts +6 -0
  144. package/src/plugins/plugin-registry.ts +32 -0
  145. package/src/plugins/plugins/index.ts +3 -0
  146. package/src/plugins/plugins/nextjs.ts +19 -0
  147. package/src/plugins/plugins/vite.ts +10 -0
  148. package/src/plugins/plugins/vitest.ts +9 -0
  149. package/src/prompt/prompt-runtime.ts +380 -0
  150. package/src/runtime/adaptive-plan.ts +563 -0
  151. package/src/runtime/agent-control.ts +215 -0
  152. package/src/runtime/agent-memory.ts +81 -0
  153. package/src/runtime/agent-observability.ts +122 -0
  154. package/src/runtime/anchor-manager.ts +475 -0
  155. package/src/runtime/async-marker.ts +32 -0
  156. package/src/runtime/async-runner.ts +381 -0
  157. package/src/runtime/attention-events.ts +28 -0
  158. package/src/runtime/auto-summarize.ts +344 -0
  159. package/src/runtime/background-runner.ts +840 -0
  160. package/src/runtime/batch-barrier.ts +147 -0
  161. package/src/runtime/broker-issuer.ts +39 -0
  162. package/src/runtime/cancellation-token.ts +99 -0
  163. package/src/runtime/cancellation.ts +99 -0
  164. package/src/runtime/capability-inventory.ts +137 -0
  165. package/src/runtime/chain-parser.ts +237 -0
  166. package/src/runtime/chain-runner.ts +606 -0
  167. package/src/runtime/checkpoint.ts +291 -0
  168. package/src/runtime/child-pi-constants.ts +42 -0
  169. package/src/runtime/child-pi-kill.ts +180 -0
  170. package/src/runtime/child-pi-pool.ts +68 -0
  171. package/src/runtime/child-pi-spawn.ts +287 -0
  172. package/src/runtime/child-pi-steering.ts +128 -0
  173. package/src/runtime/child-pi-streams.ts +296 -0
  174. package/src/runtime/child-pi-transcript.ts +169 -0
  175. package/src/runtime/child-pi.ts +1105 -0
  176. package/src/runtime/coalesce-tasks.ts +268 -0
  177. package/src/runtime/code-summary.ts +292 -0
  178. package/src/runtime/command-trace.ts +105 -0
  179. package/src/runtime/compact-pipeline.ts +56 -0
  180. package/src/runtime/compact-stages/ansi-strip-stage.ts +25 -0
  181. package/src/runtime/compact-stages/blank-collapse-stage.ts +31 -0
  182. package/src/runtime/compact-stages/deduplicate-stage.ts +34 -0
  183. package/src/runtime/compact-stages/head-snap-stage.ts +57 -0
  184. package/src/runtime/compact-stages/index.ts +23 -0
  185. package/src/runtime/compact-stages/tail-capture-stage.ts +77 -0
  186. package/src/runtime/compact-stages/truncation-stage.ts +74 -0
  187. package/src/runtime/compaction-summary.ts +278 -0
  188. package/src/runtime/completion-guard.ts +207 -0
  189. package/src/runtime/concurrency.ts +58 -0
  190. package/src/runtime/crash-classification.ts +229 -0
  191. package/src/runtime/crash-recovery.ts +594 -0
  192. package/src/runtime/crew-agent-records.ts +595 -0
  193. package/src/runtime/crew-agent-runtime.ts +62 -0
  194. package/src/runtime/crew-broker-child.ts +88 -0
  195. package/src/runtime/crew-broker-client.ts +673 -0
  196. package/src/runtime/crew-broker-tokens.ts +159 -0
  197. package/src/runtime/crew-broker.ts +1304 -0
  198. package/src/runtime/crew-hooks.ts +227 -0
  199. package/src/runtime/cross-extension-rpc.ts +143 -0
  200. package/src/runtime/custom-tools/irc-tool.ts +282 -0
  201. package/src/runtime/custom-tools/submit-result-tool.ts +102 -0
  202. package/src/runtime/deadletter.ts +47 -0
  203. package/src/runtime/delivery-coordinator.ts +211 -0
  204. package/src/runtime/delta-conflict.ts +348 -0
  205. package/src/runtime/deterministic-ast.ts +161 -0
  206. package/src/runtime/diagnostic-export.ts +171 -0
  207. package/src/runtime/direct-run.ts +45 -0
  208. package/src/runtime/dwf-state-store.ts +102 -0
  209. package/src/runtime/dynamic-workflow-context.ts +1013 -0
  210. package/src/runtime/dynamic-workflow-runner.ts +325 -0
  211. package/src/runtime/effectiveness.ts +90 -0
  212. package/src/runtime/errors/crew-errors.ts +162 -0
  213. package/src/runtime/event-stream-bridge.ts +98 -0
  214. package/src/runtime/foreground-control.ts +193 -0
  215. package/src/runtime/foreground-watchdog.ts +132 -0
  216. package/src/runtime/global-worker-cap.ts +96 -0
  217. package/src/runtime/goal-achievement.ts +148 -0
  218. package/src/runtime/goal-evaluator.ts +365 -0
  219. package/src/runtime/goal-loop-runner.ts +825 -0
  220. package/src/runtime/goal-state-store.ts +219 -0
  221. package/src/runtime/green-contract.ts +55 -0
  222. package/src/runtime/group-join.ts +267 -0
  223. package/src/runtime/handoff-manager.ts +598 -0
  224. package/src/runtime/heartbeat-gradient.ts +36 -0
  225. package/src/runtime/heartbeat-watcher.ts +239 -0
  226. package/src/runtime/hidden-handoff.ts +407 -0
  227. package/src/runtime/important-line-classifier.ts +140 -0
  228. package/src/runtime/intercom-bridge.ts +187 -0
  229. package/src/runtime/iteration-hooks.ts +305 -0
  230. package/src/runtime/live-agent-control.ts +136 -0
  231. package/src/runtime/live-agent-manager.ts +704 -0
  232. package/src/runtime/live-control-realtime.ts +56 -0
  233. package/src/runtime/live-extension-bridge.ts +148 -0
  234. package/src/runtime/live-irc.ts +97 -0
  235. package/src/runtime/live-session-health.ts +108 -0
  236. package/src/runtime/live-session-runtime.ts +1146 -0
  237. package/src/runtime/loop-gates.ts +128 -0
  238. package/src/runtime/manifest-cache.ts +375 -0
  239. package/src/runtime/mcp-proxy.ts +105 -0
  240. package/src/runtime/metric-parser.ts +36 -0
  241. package/src/runtime/model-fallback.ts +409 -0
  242. package/src/runtime/model-resolver.ts +126 -0
  243. package/src/runtime/model-scope.ts +158 -0
  244. package/src/runtime/orphan-worker-registry.ts +465 -0
  245. package/src/runtime/output-validator.ts +202 -0
  246. package/src/runtime/overflow-recovery.ts +206 -0
  247. package/src/runtime/parallel-research.ts +68 -0
  248. package/src/runtime/parallel-utils.ts +160 -0
  249. package/src/runtime/parent-guard.ts +134 -0
  250. package/src/runtime/path-overlap.ts +150 -0
  251. package/src/runtime/peer-dep.ts +292 -0
  252. package/src/runtime/per-write-validator.ts +181 -0
  253. package/src/runtime/phase-progress.ts +217 -0
  254. package/src/runtime/phase-tracker.ts +385 -0
  255. package/src/runtime/pi-args.ts +655 -0
  256. package/src/runtime/pi-json-output.ts +170 -0
  257. package/src/runtime/pi-spawn.ts +273 -0
  258. package/src/runtime/pipeline-runner.ts +523 -0
  259. package/src/runtime/plan-templates.ts +201 -0
  260. package/src/runtime/policy-engine.ts +115 -0
  261. package/src/runtime/post-checks.ts +142 -0
  262. package/src/runtime/post-exit-stdio-guard.ts +109 -0
  263. package/src/runtime/process-lifecycle.ts +491 -0
  264. package/src/runtime/process-status.ts +154 -0
  265. package/src/runtime/progress-event-coalescer.ts +44 -0
  266. package/src/runtime/progress-tracker.ts +124 -0
  267. package/src/runtime/prose-compressor.ts +162 -0
  268. package/src/runtime/recovery-recipes.ts +203 -0
  269. package/src/runtime/replace.ts +570 -0
  270. package/src/runtime/resilient-edit.ts +153 -0
  271. package/src/runtime/result-extractor.ts +217 -0
  272. package/src/runtime/retry-executor.ts +109 -0
  273. package/src/runtime/retry-runner.ts +336 -0
  274. package/src/runtime/role-permission.ts +59 -0
  275. package/src/runtime/run-coalesced-task-group.ts +308 -0
  276. package/src/runtime/run-drift.ts +219 -0
  277. package/src/runtime/run-tracker.ts +116 -0
  278. package/src/runtime/run-worker.ts +78 -0
  279. package/src/runtime/runtime-policy.ts +29 -0
  280. package/src/runtime/runtime-resolver.ts +172 -0
  281. package/src/runtime/runtime-warmup.ts +160 -0
  282. package/src/runtime/scheduler.ts +353 -0
  283. package/src/runtime/semaphore.ts +141 -0
  284. package/src/runtime/sensitive-paths.ts +93 -0
  285. package/src/runtime/session-resources.ts +25 -0
  286. package/src/runtime/session-snapshot.ts +59 -0
  287. package/src/runtime/session-usage.ts +79 -0
  288. package/src/runtime/settings-store.ts +155 -0
  289. package/src/runtime/sidechain-output.ts +35 -0
  290. package/src/runtime/single-agent-compose.ts +84 -0
  291. package/src/runtime/skill-effectiveness.ts +524 -0
  292. package/src/runtime/skill-instructions.ts +394 -0
  293. package/src/runtime/stale-reconciler.ts +680 -0
  294. package/src/runtime/stream-preview.ts +184 -0
  295. package/src/runtime/streaming-output.ts +50 -0
  296. package/src/runtime/subagent-manager.ts +561 -0
  297. package/src/runtime/subprocess-tool-registry.ts +70 -0
  298. package/src/runtime/supervisor-contact.ts +64 -0
  299. package/src/runtime/task-display.ts +52 -0
  300. package/src/runtime/task-graph-scheduler.ts +227 -0
  301. package/src/runtime/task-graph.ts +290 -0
  302. package/src/runtime/task-health.ts +83 -0
  303. package/src/runtime/task-id.ts +161 -0
  304. package/src/runtime/task-output-context.ts +602 -0
  305. package/src/runtime/task-packet.ts +268 -0
  306. package/src/runtime/task-quality.ts +199 -0
  307. package/src/runtime/task-runner/capabilities.ts +78 -0
  308. package/src/runtime/task-runner/child-executor.ts +790 -0
  309. package/src/runtime/task-runner/context-retrieval.ts +163 -0
  310. package/src/runtime/task-runner/live-executor.ts +227 -0
  311. package/src/runtime/task-runner/output-splitter.ts +152 -0
  312. package/src/runtime/task-runner/post-execution.ts +480 -0
  313. package/src/runtime/task-runner/pre-execution.ts +365 -0
  314. package/src/runtime/task-runner/progress.ts +157 -0
  315. package/src/runtime/task-runner/prompt-builder.ts +278 -0
  316. package/src/runtime/task-runner/prompt-pipeline.ts +88 -0
  317. package/src/runtime/task-runner/result-utils.ts +16 -0
  318. package/src/runtime/task-runner/retrieval-orchestrator.ts +310 -0
  319. package/src/runtime/task-runner/run-projection.ts +125 -0
  320. package/src/runtime/task-runner/scaffold-executor.ts +25 -0
  321. package/src/runtime/task-runner/state-helpers.ts +176 -0
  322. package/src/runtime/task-runner/tail-read.ts +34 -0
  323. package/src/runtime/task-runner.ts +216 -0
  324. package/src/runtime/team-runner-artifacts.ts +13 -0
  325. package/src/runtime/team-runner.ts +2396 -0
  326. package/src/runtime/tool-output-pruner.ts +333 -0
  327. package/src/runtime/tool-progress.ts +278 -0
  328. package/src/runtime/usage-tracker.ts +73 -0
  329. package/src/runtime/verification-gates.ts +579 -0
  330. package/src/runtime/verification-integrity.ts +108 -0
  331. package/src/runtime/verification-worktree.ts +186 -0
  332. package/src/runtime/worker-heartbeat.ts +25 -0
  333. package/src/runtime/worker-startup.ts +83 -0
  334. package/src/runtime/workflow-state.ts +195 -0
  335. package/src/runtime/workspace-lock.ts +443 -0
  336. package/src/runtime/workspace-tree.ts +312 -0
  337. package/src/runtime/yield-handler.ts +224 -0
  338. package/src/runtime/zombie-scanner.ts +298 -0
  339. package/src/schema/config-schema.ts +327 -0
  340. package/src/schema/team-tool-schema.ts +492 -0
  341. package/src/schema/validation-types.ts +159 -0
  342. package/src/skills/discover-skills.ts +203 -0
  343. package/src/skills/skill-templates.ts +456 -0
  344. package/src/skills/validate.ts +285 -0
  345. package/src/state/active-run-registry.ts +439 -0
  346. package/src/state/artifact-store.ts +176 -0
  347. package/src/state/atomic-write.ts +970 -0
  348. package/src/state/blob-store.ts +308 -0
  349. package/src/state/contracts.ts +160 -0
  350. package/src/state/crew-init.ts +269 -0
  351. package/src/state/decision-ledger.ts +372 -0
  352. package/src/state/event-log-rotation.ts +399 -0
  353. package/src/state/event-log.ts +1465 -0
  354. package/src/state/event-reconstructor.ts +229 -0
  355. package/src/state/gitignore-manager.ts +46 -0
  356. package/src/state/health-store.ts +79 -0
  357. package/src/state/hook-instinct-bridge.ts +94 -0
  358. package/src/state/hook-integrations.ts +51 -0
  359. package/src/state/instinct-store.ts +275 -0
  360. package/src/state/jsonl-writer.ts +107 -0
  361. package/src/state/locks.ts +562 -0
  362. package/src/state/mailbox.ts +916 -0
  363. package/src/state/observation-store.ts +176 -0
  364. package/src/state/run-cache.ts +193 -0
  365. package/src/state/run-graph.ts +183 -0
  366. package/src/state/run-metrics.ts +165 -0
  367. package/src/state/schedule.ts +166 -0
  368. package/src/state/session-state-map.ts +51 -0
  369. package/src/state/state-store.ts +973 -0
  370. package/src/state/task-claims.ts +61 -0
  371. package/src/state/tiered-eval.ts +480 -0
  372. package/src/state/types-eval.ts +58 -0
  373. package/src/state/types.ts +447 -0
  374. package/src/state/usage.ts +138 -0
  375. package/src/state/worker-atomic-writer.ts +198 -0
  376. package/src/subagents/async-entry.ts +1 -0
  377. package/src/subagents/index.ts +3 -0
  378. package/src/subagents/live/control.ts +1 -0
  379. package/src/subagents/live/manager.ts +1 -0
  380. package/src/subagents/live/realtime.ts +1 -0
  381. package/src/subagents/live/session-runtime.ts +1 -0
  382. package/src/subagents/manager.ts +1 -0
  383. package/src/subagents/spawn.ts +1 -0
  384. package/src/teams/discover-teams.ts +187 -0
  385. package/src/teams/team-config.ts +27 -0
  386. package/src/teams/team-serializer.ts +38 -0
  387. package/src/tools/safe-bash-extension.ts +54 -0
  388. package/src/tools/safe-bash.ts +505 -0
  389. package/src/types/diff.d.ts +18 -0
  390. package/src/types/new-api-types.ts +35 -0
  391. package/src/ui/agent-management-overlay.ts +160 -0
  392. package/src/ui/card-colors.ts +139 -0
  393. package/src/ui/crew-footer.ts +102 -0
  394. package/src/ui/crew-select-list.ts +112 -0
  395. package/src/ui/dashboard-panes/agents-pane.ts +164 -0
  396. package/src/ui/dashboard-panes/cancellation-pane.ts +66 -0
  397. package/src/ui/dashboard-panes/capability-pane.ts +77 -0
  398. package/src/ui/dashboard-panes/health-pane.ts +31 -0
  399. package/src/ui/dashboard-panes/mailbox-pane.ts +35 -0
  400. package/src/ui/dashboard-panes/metrics-pane.ts +37 -0
  401. package/src/ui/dashboard-panes/progress-pane.ts +38 -0
  402. package/src/ui/dashboard-panes/transcript-pane.ts +10 -0
  403. package/src/ui/deploy-bundled-themes.ts +71 -0
  404. package/src/ui/dwf-phase-display.ts +151 -0
  405. package/src/ui/dynamic-border.ts +35 -0
  406. package/src/ui/format-helpers.ts +31 -0
  407. package/src/ui/heartbeat-aggregator.ts +74 -0
  408. package/src/ui/key-utils.ts +42 -0
  409. package/src/ui/keybinding-map.ts +248 -0
  410. package/src/ui/layout-primitives.ts +106 -0
  411. package/src/ui/live-conversation-overlay.ts +183 -0
  412. package/src/ui/live-duration.ts +55 -0
  413. package/src/ui/live-run-sidebar.ts +293 -0
  414. package/src/ui/loaders.ts +6 -0
  415. package/src/ui/mascot.ts +449 -0
  416. package/src/ui/overlays/agent-picker-overlay.ts +66 -0
  417. package/src/ui/overlays/confirm-overlay.ts +60 -0
  418. package/src/ui/overlays/help-overlay.ts +177 -0
  419. package/src/ui/overlays/mailbox-compose-overlay.ts +181 -0
  420. package/src/ui/overlays/mailbox-compose-preview.ts +78 -0
  421. package/src/ui/overlays/mailbox-detail-overlay.ts +155 -0
  422. package/src/ui/pi-ui-compat.ts +68 -0
  423. package/src/ui/powerbar-publisher.ts +459 -0
  424. package/src/ui/render-coalescer.ts +101 -0
  425. package/src/ui/render-diff.ts +160 -0
  426. package/src/ui/render-scheduler.ts +278 -0
  427. package/src/ui/run-action-dispatcher.ts +182 -0
  428. package/src/ui/run-dashboard.ts +910 -0
  429. package/src/ui/run-event-bus.ts +381 -0
  430. package/src/ui/run-snapshot-cache.ts +1057 -0
  431. package/src/ui/settings-overlay.ts +1067 -0
  432. package/src/ui/shared-overlay-scheduler.ts +96 -0
  433. package/src/ui/snapshot-types.ts +90 -0
  434. package/src/ui/spinner.ts +17 -0
  435. package/src/ui/status-colors.ts +143 -0
  436. package/src/ui/syntax-highlight.ts +136 -0
  437. package/src/ui/terminal-status.ts +269 -0
  438. package/src/ui/theme-adapter.ts +271 -0
  439. package/src/ui/theme-discovery.ts +199 -0
  440. package/src/ui/tool-progress-formatter.ts +93 -0
  441. package/src/ui/tool-renderers/brief-mode.ts +225 -0
  442. package/src/ui/tool-renderers/index.ts +726 -0
  443. package/src/ui/transcript-cache.ts +128 -0
  444. package/src/ui/transcript-entries.ts +256 -0
  445. package/src/ui/transcript-viewer.ts +424 -0
  446. package/src/ui/widget/index.ts +418 -0
  447. package/src/ui/widget/widget-formatters.ts +182 -0
  448. package/src/ui/widget/widget-model.ts +110 -0
  449. package/src/ui/widget/widget-renderer.ts +182 -0
  450. package/src/ui/widget/widget-types.ts +36 -0
  451. package/src/utils/bm25-search.ts +235 -0
  452. package/src/utils/completion-dedupe.ts +63 -0
  453. package/src/utils/conflict-detect.ts +668 -0
  454. package/src/utils/env-allowlist.ts +30 -0
  455. package/src/utils/env-filter.ts +155 -0
  456. package/src/utils/file-coalescer.ts +98 -0
  457. package/src/utils/fingerprint.ts +180 -0
  458. package/src/utils/frontmatter.ts +70 -0
  459. package/src/utils/fs-watch.ts +37 -0
  460. package/src/utils/gh-protocol.ts +556 -0
  461. package/src/utils/git.ts +260 -0
  462. package/src/utils/guards.ts +107 -0
  463. package/src/utils/ids.ts +24 -0
  464. package/src/utils/incremental-reader.ts +220 -0
  465. package/src/utils/internal-error.ts +10 -0
  466. package/src/utils/names.ts +36 -0
  467. package/src/utils/ndjson.ts +115 -0
  468. package/src/utils/paths.ts +227 -0
  469. package/src/utils/project-detector.ts +160 -0
  470. package/src/utils/redaction.ts +370 -0
  471. package/src/utils/resolve-shell.ts +36 -0
  472. package/src/utils/run-watcher-registry.ts +152 -0
  473. package/src/utils/safe-paths.ts +393 -0
  474. package/src/utils/scan-cache.ts +144 -0
  475. package/src/utils/session-utils.ts +110 -0
  476. package/src/utils/sleep.ts +54 -0
  477. package/src/utils/socket-path.ts +164 -0
  478. package/src/utils/sse-parser.ts +131 -0
  479. package/src/utils/task-name-generator.ts +337 -0
  480. package/src/utils/timings.ts +33 -0
  481. package/src/utils/token-counter.ts +200 -0
  482. package/src/utils/visual.ts +207 -0
  483. package/src/workflows/cost-estimator.ts +34 -0
  484. package/src/workflows/discover-workflows.ts +308 -0
  485. package/src/workflows/intermediate-store.ts +166 -0
  486. package/src/workflows/preflight-validator.ts +168 -0
  487. package/src/workflows/topology-analyzer.ts +203 -0
  488. package/src/workflows/validate-workflow.ts +50 -0
  489. package/src/workflows/workflow-config.ts +75 -0
  490. package/src/workflows/workflow-serializer.ts +32 -0
  491. package/src/worktree/branch-freshness.ts +112 -0
  492. package/src/worktree/cleanup.ts +341 -0
  493. package/src/worktree/worktree-manager.ts +1204 -0
@@ -0,0 +1,16 @@
1
+ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
2
+ import type { TeamToolDetails } from "./team-tool-types.ts";
3
+
4
+ export type PiTeamsToolResult<TDetails = TeamToolDetails> = AgentToolResult<TDetails> & { isError?: boolean };
5
+
6
+ export function toolResult<TDetails>(text: string, details: TDetails, isError = false): PiTeamsToolResult<TDetails> {
7
+ return { content: [{ type: "text", text }], details, isError };
8
+ }
9
+
10
+ export function isToolError(result: { isError?: boolean }): boolean {
11
+ return result.isError === true;
12
+ }
13
+
14
+ export function textFromToolResult(result: { content?: Array<{ type: string; text?: string }> }): string {
15
+ return result.content?.map((item) => item.text ?? "").join("\n") ?? "";
16
+ }
@@ -0,0 +1,108 @@
1
+ import { allAgents, discoverAgents } from "../agents/discover-agents.ts";
2
+ import { allTeams, discoverTeams } from "../teams/discover-teams.ts";
3
+ import { allWorkflows, discoverWorkflows } from "../workflows/discover-workflows.ts";
4
+ import { validateWorkflowForTeam } from "../workflows/validate-workflow.ts";
5
+
6
+ export interface ValidationIssue {
7
+ level: "error" | "warning";
8
+ resource: string;
9
+ message: string;
10
+ }
11
+
12
+ export interface ValidationReport {
13
+ issues: ValidationIssue[];
14
+ agents: number;
15
+ teams: number;
16
+ workflows: number;
17
+ }
18
+
19
+ export function validateResources(cwd: string): ValidationReport {
20
+ const agents = allAgents(discoverAgents(cwd));
21
+ const teams = allTeams(discoverTeams(cwd));
22
+ const workflows = allWorkflows(discoverWorkflows(cwd));
23
+ const agentNames = new Set(agents.map((agent) => agent.name));
24
+ const workflowNames = new Set(workflows.map((workflow) => workflow.name));
25
+ const issues: ValidationIssue[] = [];
26
+
27
+ for (const agent of agents) {
28
+ const modelValues = [agent.model, ...(agent.fallbackModels ?? [])].filter(
29
+ (value): value is string => typeof value === "string" && value.length > 0,
30
+ );
31
+ for (const model of modelValues) {
32
+ if (/\s/.test(model)) {
33
+ issues.push({
34
+ level: "warning",
35
+ resource: `agent:${agent.name}`,
36
+ message: `Model reference '${model}' contains whitespace.`,
37
+ });
38
+ }
39
+ if (model.includes("/") && model.split("/").some((part) => part.trim() === "")) {
40
+ issues.push({
41
+ level: "warning",
42
+ resource: `agent:${agent.name}`,
43
+ message: `Model reference '${model}' has an empty provider/model segment.`,
44
+ });
45
+ }
46
+ }
47
+ }
48
+
49
+ for (const team of teams) {
50
+ for (const role of team.roles) {
51
+ if (!agentNames.has(role.agent)) {
52
+ issues.push({
53
+ level: "error",
54
+ resource: `team:${team.name}`,
55
+ message: `Role '${role.name}' references unknown agent '${role.agent}'.`,
56
+ });
57
+ }
58
+ }
59
+ if (team.defaultWorkflow && !workflowNames.has(team.defaultWorkflow)) {
60
+ issues.push({
61
+ level: "error",
62
+ resource: `team:${team.name}`,
63
+ message: `defaultWorkflow references unknown workflow '${team.defaultWorkflow}'.`,
64
+ });
65
+ }
66
+ const workflow = workflows.find((candidate) => candidate.name === team.defaultWorkflow);
67
+ if (workflow) {
68
+ for (const error of validateWorkflowForTeam(workflow, team)) {
69
+ issues.push({
70
+ level: "error",
71
+ resource: `workflow:${workflow.name}`,
72
+ message: `Team '${team.name}': ${error}`,
73
+ });
74
+ }
75
+ }
76
+ }
77
+
78
+ for (const workflow of workflows) {
79
+ if (workflow.steps.length === 0) {
80
+ issues.push({
81
+ level: "warning",
82
+ resource: `workflow:${workflow.name}`,
83
+ message: "Workflow has no steps.",
84
+ });
85
+ }
86
+ }
87
+
88
+ return {
89
+ issues,
90
+ agents: agents.length,
91
+ teams: teams.length,
92
+ workflows: workflows.length,
93
+ };
94
+ }
95
+
96
+ export function formatValidationReport(report: ValidationReport): string {
97
+ const lines = [
98
+ "pi-crew resource validation:",
99
+ `Agents: ${report.agents}`,
100
+ `Teams: ${report.teams}`,
101
+ `Workflows: ${report.workflows}`,
102
+ `Issues: ${report.issues.length}`,
103
+ ];
104
+ if (report.issues.length > 0) {
105
+ lines.push("", ...report.issues.map((issue) => `- ${issue.level.toUpperCase()} ${issue.resource}: ${issue.message}`));
106
+ }
107
+ return lines.join("\n");
108
+ }
@@ -0,0 +1,200 @@
1
+ import { appendEvent } from "../state/event-log.ts";
2
+ import type { TeamRunManifest } from "../state/types.ts";
3
+ import { runEventBus } from "../ui/run-event-bus.ts";
4
+ import type { HookContext, HookDefinition, HookExecutionReport, HookName, HookResult } from "./types.ts";
5
+
6
+ const registry = new Map<HookName, HookDefinition[]>();
7
+
8
+ // Track hook IDs registered by pi-crew for scope-aware cleanup
9
+ const _piCrewHookIds = new Set<number>();
10
+ let _nextHookId = 1;
11
+
12
+ // SECURITY: Hooks are currently global (registered once, applied to all workspaces).
13
+ // For multi-workspace environments, consider filtering hooks by workspace scope:
14
+ // const workspaceHooks = getHooks(name).filter(h => !h.workspaceId || h.workspaceId === ctx.workspaceId);
15
+ // This prevents globally-registered hooks from operating on runs they weren't designed for.
16
+
17
+ export function registerHook(definition: HookDefinition): number {
18
+ const hookId = _nextHookId++;
19
+ _piCrewHookIds.add(hookId);
20
+ const hooks = registry.get(definition.name) ?? [];
21
+ hooks.push({ ...definition, _hookId: hookId });
22
+ registry.set(definition.name, hooks);
23
+ return hookId;
24
+ }
25
+
26
+ export function clearHooks(): void {
27
+ registry.clear();
28
+ _piCrewHookIds.clear();
29
+ _nextHookId = 1;
30
+ }
31
+
32
+ // Scope-aware hook clearing: only removes hooks registered by pi-crew
33
+ export function clearHooksScoped(): void {
34
+ for (const [name, hooks] of registry) {
35
+ const remaining = hooks.filter((h) => !("_hookId" in h && _piCrewHookIds.has((h as { _hookId?: number })._hookId ?? -1)));
36
+ if (remaining.length === 0) {
37
+ registry.delete(name);
38
+ } else {
39
+ registry.set(name, remaining);
40
+ }
41
+ }
42
+ _piCrewHookIds.clear();
43
+ _nextHookId = 1;
44
+ }
45
+
46
+ export function getHooks(name: HookName): HookDefinition[] {
47
+ return registry.get(name) ?? [];
48
+ }
49
+
50
+ export async function executeHook(name: HookName, ctx: HookContext): Promise<HookExecutionReport> {
51
+ const hooks = getHooks(name);
52
+ if (hooks.length === 0) return { hookName: name, outcome: "allow", durationMs: 0 };
53
+ // SECURITY: Filter hooks by workspace scope.
54
+ // - Global hooks (no workspaceId) match all contexts UNLESS ctx explicitly
55
+ // opts out via `includeGlobalHooks: false`. This is the documented
56
+ // behavior: "Hooks without workspaceId match ALL workspaces".
57
+ // - Scoped hooks (workspaceId set) require an exact match with ctx.workspaceId.
58
+ // - If ctx has no workspaceId, scoped hooks are excluded (they can't match).
59
+ // - `includeGlobalHooks: true` on ctx forces inclusion of global hooks even
60
+ // when ctx has a workspaceId.
61
+ const scopedHooks = hooks.filter((h) => {
62
+ // Scoped hook: must match ctx.workspaceId exactly
63
+ if (h.workspaceId !== undefined) {
64
+ return h.workspaceId === ctx.workspaceId;
65
+ }
66
+ // Global hook (no workspaceId): include unless ctx explicitly excludes
67
+ return ctx.includeGlobalHooks !== false;
68
+ });
69
+ if (scopedHooks.length === 0) return { hookName: name, outcome: "allow", durationMs: 0 };
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
+ );
86
+ function sanitizeMergeData(data: Record<string, unknown>): Record<string, unknown> {
87
+ const clean: Record<string, unknown> = {};
88
+ for (const [k, v] of Object.entries(data)) {
89
+ if (!POLLUTED_KEYS.has(k.toLowerCase().normalize("NFKC"))) {
90
+ if (v !== null && typeof v === "object") {
91
+ if (Array.isArray(v)) {
92
+ // Sanitize array elements that are objects
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
+ );
98
+ } else {
99
+ clean[k] = sanitizeMergeData(v as Record<string, unknown>);
100
+ }
101
+ } else {
102
+ clean[k] = v;
103
+ }
104
+ }
105
+ }
106
+ return clean;
107
+ }
108
+ // Sanitize ctx by stripping dangerous property names before passing to handlers.
109
+ // Hook authors must NOT set these keys directly on ctx: [...POLLUTED_KEYS]
110
+ // This sanitization runs at the start of executeHook to prevent prototype pollution attacks.
111
+ function sanitizeContext(ctx: HookContext): HookContext {
112
+ for (const key of Object.keys(ctx)) {
113
+ if (POLLUTED_KEYS.has(key.toLowerCase().normalize("NFKC"))) {
114
+ delete ctx[key];
115
+ }
116
+ }
117
+ return ctx;
118
+ }
119
+ function sanitizeErrorMessage(message: string): string {
120
+ // Remove file paths, environment variable references, and other potentially sensitive data
121
+ return message
122
+ .replace(/\/[^:\s]+/g, "[path]")
123
+ .replace(/\b[A-Z_0-9]+\s*=/g, "[env]")
124
+ .replace(/\b\d+\.\d+\.\d+\.\d+\b/g, "[ip]");
125
+ }
126
+ const start = Date.now();
127
+ const diagnostics: string[] = [];
128
+ let capturedModifications: Record<string, unknown> | undefined;
129
+ for (const hook of scopedHooks) {
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
+ }
143
+ if (result.outcome === "modify" && result.data) {
144
+ // EXT-13 (Round 3): Deep-clone hook result.data so subsequent hooks
145
+ // (or the same hook if reused) cannot mutate shared ctx via shared
146
+ // nested-object references. sanitizeMergeData already produces a
147
+ // fresh top-level object, but nested values would still alias.
148
+ const clonedData = sanitizeMergeData(result.data);
149
+ Object.assign(ctx, clonedData);
150
+ capturedModifications = clonedData;
151
+ }
152
+ } catch (error) {
153
+ const message = sanitizeErrorMessage(error instanceof Error ? error.message : String(error));
154
+ if (hook.mode === "blocking") {
155
+ return {
156
+ hookName: name,
157
+ outcome: "block",
158
+ durationMs: Date.now() - start,
159
+ reason: `Hook error: ${message}`,
160
+ };
161
+ }
162
+ // Non-blocking hook errors are accumulated as diagnostics; continue to next hook
163
+ diagnostics.push(message);
164
+ }
165
+ }
166
+ if (diagnostics.length > 0) {
167
+ return {
168
+ hookName: name,
169
+ outcome: "diagnostic",
170
+ durationMs: Date.now() - start,
171
+ reason: diagnostics.join("; "),
172
+ modifiedData: capturedModifications,
173
+ };
174
+ }
175
+ return {
176
+ hookName: name,
177
+ outcome: "allow",
178
+ durationMs: Date.now() - start,
179
+ modifiedData: capturedModifications,
180
+ };
181
+ }
182
+
183
+ export function appendHookEvent(manifest: TeamRunManifest, report: HookExecutionReport): void {
184
+ appendEvent(manifest.eventsPath, {
185
+ type: "hook.executed",
186
+ runId: manifest.runId,
187
+ message: `Hook ${report.hookName} completed with outcome=${report.outcome}${report.reason ? `: ${report.reason}` : ""}`,
188
+ data: {
189
+ hookName: report.hookName,
190
+ outcome: report.outcome,
191
+ durationMs: report.durationMs,
192
+ reason: report.reason,
193
+ },
194
+ });
195
+ runEventBus.emit({
196
+ type: "effectiveness_changed",
197
+ runId: manifest.runId,
198
+ data: { hookName: report.hookName, outcome: report.outcome },
199
+ });
200
+ }
@@ -0,0 +1,69 @@
1
+ export type HookName =
2
+ | "before_run_start"
3
+ | "after_run_complete"
4
+ | "before_task_start"
5
+ | "after_task_complete"
6
+ | "task_result"
7
+ | "before_cancel"
8
+ | "before_retry"
9
+ | "before_forget"
10
+ | "before_cleanup"
11
+ | "before_publish"
12
+ | "session_before_switch"
13
+ | "session_after_connect"
14
+ | "session_after_disconnect"
15
+ | "run_recovery"
16
+ | "before_goal_step"
17
+ | "before_goal_abort";
18
+
19
+ /**
20
+ * Hook exit codes inspired by claude-mem's lifecycle architecture:
21
+ * - 0 = allow (success)
22
+ * - 1 = warn (non-blocking error, continue)
23
+ * - 2 = block (blocking error, stop)
24
+ */
25
+ /** @internal */ const HOOK_EXIT_SUCCESS = 0 as const;
26
+ /** @internal */ const HOOK_EXIT_WARN = 1 as const;
27
+ /** @internal */ const HOOK_EXIT_BLOCK = 2 as const;
28
+
29
+ export type HookMode = "blocking" | "non_blocking";
30
+ export type HookOutcome = "allow" | "block" | "modify" | "diagnostic";
31
+
32
+ export interface HookContext {
33
+ runId: string;
34
+ taskId?: string;
35
+ cwd: string;
36
+ // NOTE: Hooks receive a shared mutable context object. A hook may directly set
37
+ // properties on ctx, but MUST NOT set dangerous names like "__proto__",
38
+ // "constructor", "prototype", etc. (see registry.ts POLLUTED_KEYS).
39
+ // Hook authors are trusted but must avoid prototype pollution attacks.
40
+ // The sanitizeMergeData function prevents dangerous properties from propagating
41
+ // via result.data merge, and a runtime guard sanitizes ctx before passing to
42
+ // handlers AND after handler execution to catch direct mutations.
43
+ [key: string]: unknown;
44
+ }
45
+
46
+ export interface HookResult {
47
+ outcome: HookOutcome;
48
+ reason?: string;
49
+ data?: Record<string, unknown>;
50
+ }
51
+
52
+ export interface HookDefinition {
53
+ name: HookName;
54
+ mode: HookMode;
55
+ handler: (ctx: HookContext) => HookResult | Promise<HookResult>;
56
+ // SECURITY: Optional workspace scoping. When set, the hook only executes for
57
+ // runs in the specified workspace. When absent, the hook applies to all runs.
58
+ workspaceId?: string;
59
+ /** @internal Internal hook ID for scope-aware cleanup. */
60
+ _hookId?: number;
61
+ }
62
+
63
+ export interface HookExecutionReport {
64
+ hookName: HookName;
65
+ outcome: HookOutcome;
66
+ durationMs: number;
67
+ reason?: string;
68
+ modifiedData?: Record<string, unknown>;
69
+ }
package/src/i18n.ts ADDED
@@ -0,0 +1,212 @@
1
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { logInternalError } from "./utils/internal-error.ts";
3
+
4
+ type Params = Record<string, string | number>;
5
+
6
+ const namespace = "pi-crew";
7
+ const TEMPLATE_RE = /\{(\w+)\}/g;
8
+
9
+ const fallback = {
10
+ "agent.requiresPrompt": "Agent requires prompt.",
11
+ "agent.started": "Agent {state}.",
12
+ "agent.id": "Agent ID: {id}",
13
+ "agent.type": "Type: {type}",
14
+ "agent.description": "Description: {description}",
15
+ "agent.retrieveHint": "Use get_subagent_result to retrieve output. Do not duplicate this agent's work.",
16
+ "agent.foregroundStatus": "Agent {id} {status}.",
17
+ "agent.noOutput": "No output.",
18
+ "result.requiresAgentId": "get_subagent_result requires agent_id.",
19
+ "result.notFound": "Agent not found: {id}",
20
+ "result.unrecoverable": "Subagent was interrupted before its durable run id was recorded; it cannot be recovered after restart.",
21
+ "result.waitAborted": "Waiting for subagent result was aborted.",
22
+ "result.waitTimeout": "Timed out waiting for subagent result.",
23
+ "result.stillRunning": "Agent is still running. Use wait=true or check again later.",
24
+ "steer.noted": "Steering request noted for {id}.",
25
+ "steer.unavailable": "Current default pi-crew backend is child-process, so mid-turn session.steer is not available yet.",
26
+ "steer.cancelHint": "Use team cancel runId={runId} if the agent must be interrupted.",
27
+ } as const;
28
+
29
+ type Key = keyof typeof fallback;
30
+
31
+ /** Map of locale → partial translations. Keys not present fall back to English. */
32
+ const translations: Record<string, Partial<Record<Key, string>>> = {
33
+ es: {
34
+ "agent.requiresPrompt": "Agent requiere prompt.",
35
+ "agent.started": "Agent {state}.",
36
+ "agent.id": "ID del agente: {id}",
37
+ "agent.type": "Tipo: {type}",
38
+ "agent.description": "Descripción: {description}",
39
+ "agent.retrieveHint": "Usa get_subagent_result para recuperar la salida. No dupliques el trabajo de este agente.",
40
+ "agent.foregroundStatus": "Agent {id} {status}.",
41
+ "agent.noOutput": "Sin salida.",
42
+ "result.requiresAgentId": "get_subagent_result requiere agent_id.",
43
+ "result.notFound": "Agente no encontrado: {id}",
44
+ "result.unrecoverable":
45
+ "El subagente fue interrumpido antes de registrar su ID de ejecución duradero; no se puede recuperar tras reiniciar.",
46
+ "result.waitAborted": "Se canceló la espera del resultado del subagente.",
47
+ "result.waitTimeout": "Se agotó el tiempo de espera del resultado del subagente.",
48
+ "result.stillRunning": "El agente sigue ejecutándose. Usa wait=true o vuelve a comprobar más tarde.",
49
+ "steer.noted": "Solicitud de dirección registrada para {id}.",
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.",
52
+ "steer.cancelHint": "Usa team cancel runId={runId} si hay que interrumpir el agente.",
53
+ },
54
+ fr: {
55
+ "agent.requiresPrompt": "Agent nécessite un prompt.",
56
+ "agent.started": "Agent {state}.",
57
+ "agent.id": "ID de l'agent : {id}",
58
+ "agent.type": "Type : {type}",
59
+ "agent.description": "Description : {description}",
60
+ "agent.retrieveHint": "Utilisez get_subagent_result pour récupérer la sortie. Ne dupliquez pas le travail de cet agent.",
61
+ "agent.foregroundStatus": "Agent {id} {status}.",
62
+ "agent.noOutput": "Aucune sortie.",
63
+ "result.requiresAgentId": "get_subagent_result nécessite agent_id.",
64
+ "result.notFound": "Agent introuvable : {id}",
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.",
67
+ "result.waitAborted": "L'attente du résultat du sous-agent a été annulée.",
68
+ "result.waitTimeout": "Délai d'attente du résultat du sous-agent dépassé.",
69
+ "result.stillRunning": "L'agent est toujours en cours d'exécution. Utilisez wait=true ou réessayez plus tard.",
70
+ "steer.noted": "Demande de pilotage enregistrée pour {id}.",
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.",
73
+ "steer.cancelHint": "Utilisez team cancel runId={runId} si l'agent doit être interrompu.",
74
+ },
75
+ "pt-BR": {
76
+ "agent.requiresPrompt": "Agent requer prompt.",
77
+ "agent.started": "Agent {state}.",
78
+ "agent.id": "ID do agente: {id}",
79
+ "agent.type": "Tipo: {type}",
80
+ "agent.description": "Descrição: {description}",
81
+ "agent.retrieveHint": "Use get_subagent_result para recuperar a saída. Não duplique o trabalho deste agente.",
82
+ "agent.foregroundStatus": "Agent {id} {status}.",
83
+ "agent.noOutput": "Sem saída.",
84
+ "result.requiresAgentId": "get_subagent_result requer agent_id.",
85
+ "result.notFound": "Agente não encontrado: {id}",
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.",
88
+ "result.waitAborted": "A espera pelo resultado do subagente foi abortada.",
89
+ "result.waitTimeout": "Tempo limite de espera pelo resultado do subagente esgotado.",
90
+ "result.stillRunning": "O agente ainda está em execução. Use wait=true ou verifique novamente mais tarde.",
91
+ "steer.noted": "Solicitação de orientação registrada para {id}.",
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.",
94
+ "steer.cancelHint": "Use team cancel runId={runId} se o agente precisar ser interrompido.",
95
+ },
96
+ };
97
+
98
+ // --- Runtime state ---
99
+
100
+ let currentLocale: string | undefined;
101
+ const warnedMissing = new Set<string>();
102
+
103
+ // --- Helpers ---
104
+
105
+ function format(template: string, params: Params = {}): string {
106
+ return template.replace(TEMPLATE_RE, (_match, key) => String(params[key] ?? `{${key}}`));
107
+ }
108
+
109
+ function warnOnce(key: string): void {
110
+ const tag = `${currentLocale}:${key}`;
111
+ if (warnedMissing.has(tag)) return;
112
+ warnedMissing.add(tag);
113
+ logInternalError("i18n.missing", new Error(`Missing translation`), `key="${key}" locale="${currentLocale}"`);
114
+ }
115
+
116
+ // --- Public API ---
117
+
118
+ /**
119
+ * Translate a key for the currently active locale.
120
+ * Falls back to English, then to the raw key as a last resort.
121
+ */
122
+ export function t(key: Key, params?: Params): string {
123
+ if (currentLocale && translations[currentLocale]) {
124
+ const template = translations[currentLocale]?.[key];
125
+ if (template) return format(template, params);
126
+ warnOnce(key);
127
+ }
128
+ return format(fallback[key] ?? key, params);
129
+ }
130
+
131
+ /**
132
+ * Register or extend translations for a locale at runtime.
133
+ * Useful for contributors adding new language bundles without modifying i18n.ts.
134
+ *
135
+ * @example
136
+ * addTranslations("vi", { "agent.requiresPrompt": "Agent cần prompt." })
137
+ */
138
+ const DANGEROUS_KEYS = new Set(["__proto__", "constructor", "prototype"]);
139
+
140
+ function stripDangerousKeys<T extends Record<string, unknown>>(obj: T): T {
141
+ const safe: Record<string, unknown> = {};
142
+ for (const key of Object.keys(obj)) {
143
+ if (!DANGEROUS_KEYS.has(key)) {
144
+ safe[key] = obj[key];
145
+ }
146
+ }
147
+ return safe as T;
148
+ }
149
+
150
+ export function addTranslations(locale: string, bundle: Partial<Record<Key, string>>): void {
151
+ if (!locale) return;
152
+ const safeBundle = stripDangerousKeys(bundle as Record<string, unknown>) as Partial<Record<Key, string>>;
153
+ const existing = translations[locale];
154
+ if (existing) {
155
+ Object.assign(existing, safeBundle);
156
+ } else {
157
+ translations[locale] = { ...safeBundle };
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Returns the list of currently registered locales (excluding English, which is always available).
163
+ */
164
+ export function listLocales(): string[] {
165
+ return Object.keys(translations);
166
+ }
167
+
168
+ // --- Initialization ---
169
+
170
+ export function initI18n(pi: ExtensionAPI): () => void {
171
+ try {
172
+ pi.events?.emit?.("pi-core/i18n/registerBundle", {
173
+ namespace,
174
+ defaultLocale: "en",
175
+ fallback,
176
+ translations,
177
+ });
178
+ } catch {
179
+ // Non-critical.
180
+ }
181
+ const unsubscribe = pi.events?.on?.("pi-core/i18n/localeChanged", (event: unknown) => {
182
+ if (!event || typeof event !== "object") return;
183
+ const raw = String((event as { locale?: unknown }).locale ?? "").trim();
184
+ currentLocale = raw && translations[raw] ? raw : undefined;
185
+ });
186
+ try {
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
+ });
194
+ } catch {
195
+ // Non-critical.
196
+ }
197
+ return () => {
198
+ currentLocale = undefined;
199
+ unsubscribe?.();
200
+ };
201
+ }
202
+
203
+ // --- Test helpers ---
204
+
205
+ export function __test__resetI18n(): void {
206
+ currentLocale = undefined;
207
+ warnedMissing.clear();
208
+ // Clear runtime-added translations but keep built-in ones.
209
+ for (const key of Object.keys(translations)) {
210
+ if (!["es", "fr", "pt-BR"].includes(key)) delete translations[key];
211
+ }
212
+ }
@@ -0,0 +1,50 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { randomBytes } from "node:crypto";
3
+
4
+ export interface CorrelationContext {
5
+ traceId: string;
6
+ parentSpanId?: string;
7
+ spanId: string;
8
+ }
9
+
10
+ const storage = new AsyncLocalStorage<CorrelationContext>();
11
+
12
+ export function withCorrelation<T>(ctx: CorrelationContext, fn: () => T): T {
13
+ return storage.run(ctx, fn);
14
+ }
15
+
16
+ export function getCurrentContext(): CorrelationContext | undefined {
17
+ return storage.getStore();
18
+ }
19
+
20
+ export function newSpanId(runId: string, taskId = "main"): string {
21
+ // OBS-4: random 8-hex-char suffix — bounded length, collision-resistant across
22
+ // module reloads (the old monotonic counter collided after HMR and grew unboundedly).
23
+ return `${runId}:${taskId}:${randomBytes(4).toString("hex")}`;
24
+ }
25
+
26
+ export function childCorrelation(runId: string, taskId: string): CorrelationContext {
27
+ const parent = getCurrentContext();
28
+ const spanId = newSpanId(runId, taskId);
29
+ return {
30
+ traceId: parent?.traceId ?? spanId,
31
+ parentSpanId: parent?.spanId,
32
+ spanId,
33
+ };
34
+ }
35
+
36
+ export function correlatedEvent<T extends { runId?: string; data?: Record<string, unknown> }>(
37
+ event: T,
38
+ ): T & { data: Record<string, unknown> } {
39
+ const ctx = getCurrentContext();
40
+ if (!ctx) return event as T & { data: Record<string, unknown> };
41
+ return {
42
+ ...event,
43
+ data: {
44
+ ...(event.data ?? {}),
45
+ traceId: ctx.traceId,
46
+ spanId: ctx.spanId,
47
+ parentSpanId: ctx.parentSpanId,
48
+ },
49
+ };
50
+ }