pi-crew 0.9.55 → 0.9.57

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 (309) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +58 -0
  3. package/README.md +53 -5
  4. package/dist/index.mjs +5666 -4370
  5. package/docs/README.md +54 -0
  6. package/package.json +12 -13
  7. package/skills/real-test-pi-crew/SKILL.md +93 -3
  8. package/src/agents/discover-agents.ts +68 -10
  9. package/src/benchmark/feedback-loop.ts +1 -1
  10. package/src/config/config.ts +2 -1
  11. package/src/config/types.ts +12 -0
  12. package/src/extension/action-suggestions.ts +16 -63
  13. package/src/extension/async-notifier.ts +3 -3
  14. package/src/extension/command-completions.ts +2 -2
  15. package/src/extension/crew-cleanup.ts +3 -3
  16. package/src/extension/crew-vibes/footer.ts +4 -2
  17. package/src/extension/cross-extension-rpc.ts +10 -5
  18. package/src/extension/knowledge-injection.ts +57 -10
  19. package/src/extension/register.ts +2 -2
  20. package/src/extension/registration/artifact-cleanup.ts +41 -3
  21. package/src/extension/registration/commands.ts +4 -4
  22. package/src/extension/registration/context-builder.ts +10 -10
  23. package/src/extension/registration/crash-recovery-cache.ts +3 -3
  24. package/src/extension/registration/foreground-run-controller.ts +1 -1
  25. package/src/extension/registration/lazy-configurers.ts +2 -2
  26. package/src/extension/registration/lifecycle-handlers.ts +12 -12
  27. package/src/extension/registration/lifecycle.ts +2 -2
  28. package/src/extension/registration/observability.ts +2 -2
  29. package/src/extension/registration/registration-types.ts +3 -3
  30. package/src/extension/registration/runtime-cleanup.ts +1 -1
  31. package/src/extension/registration/subagent-helpers.ts +2 -2
  32. package/src/extension/registration/subagent-manager-setup.ts +2 -2
  33. package/src/extension/registration/subagent-tools.ts +21 -16
  34. package/src/extension/registration/team-tool.ts +109 -17
  35. package/src/extension/registration/viewers.ts +3 -4
  36. package/src/extension/registration/wire-cross-extension.ts +7 -7
  37. package/src/extension/rpc-hmac.ts +3 -2
  38. package/src/extension/run-bundle-schema.ts +13 -4
  39. package/src/extension/run-export.ts +2 -2
  40. package/src/extension/run-index.ts +2 -3
  41. package/src/extension/run-maintenance.ts +57 -1
  42. package/src/extension/team-manager-command.ts +3 -1
  43. package/src/extension/team-tool/anchor.ts +8 -2
  44. package/src/extension/team-tool/api.ts +117 -39
  45. package/src/extension/team-tool/cancel.ts +23 -8
  46. package/src/extension/team-tool/chain-executor.ts +1 -1
  47. package/src/extension/team-tool/dispatch/automate.ts +17 -2
  48. package/src/extension/team-tool/dispatch/control.ts +17 -2
  49. package/src/extension/team-tool/dispatch/manage.ts +34 -2
  50. package/src/extension/team-tool/dispatch/run.ts +17 -2
  51. package/src/extension/team-tool/dispatch/status.ts +37 -5
  52. package/src/extension/team-tool/doctor.ts +2 -2
  53. package/src/extension/team-tool/explain.ts +1 -1
  54. package/src/extension/team-tool/goal-wrap.ts +5 -5
  55. package/src/extension/team-tool/goal.ts +30 -9
  56. package/src/extension/team-tool/handle-schedule.ts +37 -19
  57. package/src/extension/team-tool/inspect.ts +21 -5
  58. package/src/extension/team-tool/lifecycle-actions.ts +53 -10
  59. package/src/extension/team-tool/parallel-dispatch.ts +14 -5
  60. package/src/extension/team-tool/param-error.ts +138 -0
  61. package/src/extension/team-tool/respond.ts +20 -6
  62. package/src/extension/team-tool/run.ts +27 -17
  63. package/src/extension/team-tool/status.ts +17 -6
  64. package/src/extension/team-tool.ts +26 -19
  65. package/src/hooks/registry.ts +1 -1
  66. package/src/i18n.ts +22 -0
  67. package/src/observability/event-bus.ts +1 -1
  68. package/src/prompt/prompt-runtime.ts +2 -2
  69. package/src/runtime/README.md +35 -0
  70. package/src/runtime/agent-control.ts +1 -1
  71. package/src/runtime/anchor-manager.ts +0 -2
  72. package/src/runtime/async-runner.ts +1 -1
  73. package/src/runtime/attention-events.ts +3 -2
  74. package/src/runtime/auto-summarize.ts +0 -2
  75. package/src/runtime/background-runner.ts +103 -30
  76. package/src/runtime/{crew-broker-child.ts → broker/crew-broker-child.ts} +2 -2
  77. package/src/runtime/{crew-broker-client.ts → broker/crew-broker-client.ts} +4 -7
  78. package/src/runtime/{crew-broker.ts → broker/crew-broker.ts} +13 -21
  79. package/src/runtime/chain-runner.ts +50 -5
  80. package/src/runtime/{child-pi-constants.ts → child-pi/child-pi-constants.ts} +1 -1
  81. package/src/runtime/{child-pi-kill.ts → child-pi/child-pi-kill.ts} +3 -3
  82. package/src/runtime/{child-pi-spawn.ts → child-pi/child-pi-spawn.ts} +28 -12
  83. package/src/runtime/{child-pi-steering.ts → child-pi/child-pi-steering.ts} +1 -1
  84. package/src/runtime/{child-pi-streams.ts → child-pi/child-pi-streams.ts} +23 -4
  85. package/src/runtime/{child-pi-transcript.ts → child-pi/child-pi-transcript.ts} +6 -6
  86. package/src/runtime/{child-pi.ts → child-pi/child-pi.ts} +47 -78
  87. package/src/runtime/compaction/compact-stages/bounded-tail.ts +84 -0
  88. package/src/runtime/{compact-stages → compaction/compact-stages}/index.ts +2 -2
  89. package/src/runtime/{compaction-summary.ts → compaction/compaction-summary.ts} +3 -3
  90. package/src/runtime/{tool-output-pruner.ts → compaction/tool-output-pruner.ts} +1 -1
  91. package/src/runtime/crew-agent-records.ts +48 -2
  92. package/src/runtime/custom-tools/irc-tool.ts +2 -2
  93. package/src/runtime/delivery-coordinator.ts +0 -3
  94. package/src/runtime/diagnostic-export.ts +5 -4
  95. package/src/runtime/foreground-control.ts +1 -1
  96. package/src/runtime/foreground-watchdog.ts +1 -1
  97. package/src/runtime/{adaptive-plan.ts → goal-workflow/adaptive-plan.ts} +8 -8
  98. package/src/runtime/{dynamic-workflow-context.ts → goal-workflow/dynamic-workflow-context.ts} +16 -16
  99. package/src/runtime/{dynamic-workflow-runner.ts → goal-workflow/dynamic-workflow-runner.ts} +10 -10
  100. package/src/runtime/{goal-achievement.ts → goal-workflow/goal-achievement.ts} +3 -3
  101. package/src/runtime/{goal-evaluator.ts → goal-workflow/goal-evaluator.ts} +8 -8
  102. package/src/runtime/{goal-loop-runner.ts → goal-workflow/goal-loop-runner.ts} +27 -25
  103. package/src/runtime/{goal-state-store.ts → goal-workflow/goal-state-store.ts} +8 -8
  104. package/src/runtime/{plan-templates.ts → goal-workflow/plan-templates.ts} +1 -1
  105. package/src/runtime/group-join.ts +3 -3
  106. package/src/runtime/handoff-manager.ts +0 -2
  107. package/src/runtime/{heartbeat-watcher.ts → heartbeat/heartbeat-watcher.ts} +7 -7
  108. package/src/runtime/iteration-hooks.ts +1 -1
  109. package/src/runtime/{live-agent-control.ts → live-session/live-agent-control.ts} +2 -2
  110. package/src/runtime/{live-agent-manager.ts → live-session/live-agent-manager.ts} +4 -4
  111. package/src/runtime/{live-session-runtime.ts → live-session/live-session-runtime.ts} +39 -41
  112. package/src/runtime/manifest-cache.ts +6 -2
  113. package/src/runtime/{model-fallback.ts → model/model-fallback.ts} +1 -1
  114. package/src/runtime/{pi-args.ts → model/pi-args.ts} +20 -5
  115. package/src/runtime/model/provider-extensions.ts +116 -0
  116. package/src/runtime/{runtime-policy.ts → model/runtime-policy.ts} +2 -2
  117. package/src/runtime/{runtime-resolver.ts → model/runtime-resolver.ts} +3 -3
  118. package/src/runtime/{runtime-warmup.ts → model/runtime-warmup.ts} +8 -8
  119. package/src/runtime/orphan-worker-registry.ts +1 -1
  120. package/src/runtime/{progress-tracker.ts → output/progress-tracker.ts} +1 -1
  121. package/src/runtime/{result-extractor.ts → output/result-extractor.ts} +4 -4
  122. package/src/runtime/{sidechain-output.ts → output/sidechain-output.ts} +2 -2
  123. package/src/runtime/{stream-preview.ts → output/stream-preview.ts} +1 -1
  124. package/src/runtime/{streaming-output.ts → output/streaming-output.ts} +2 -2
  125. package/src/runtime/{tool-progress.ts → output/tool-progress.ts} +1 -1
  126. package/src/runtime/parent-guard.ts +30 -10
  127. package/src/runtime/pipeline-runner.ts +2 -2
  128. package/src/runtime/policy-engine.ts +2 -2
  129. package/src/runtime/{cancellation.ts → process/cancellation.ts} +1 -1
  130. package/src/runtime/{post-exit-stdio-guard.ts → process/post-exit-stdio-guard.ts} +1 -1
  131. package/src/runtime/{process-lifecycle.ts → process/process-lifecycle.ts} +3 -3
  132. package/src/runtime/{checkpoint.ts → recovery/checkpoint.ts} +4 -4
  133. package/src/runtime/{crash-recovery.ts → recovery/crash-recovery.ts} +93 -21
  134. package/src/runtime/{overflow-recovery.ts → recovery/overflow-recovery.ts} +1 -1
  135. package/src/runtime/{recovery-recipes.ts → recovery/recovery-recipes.ts} +1 -1
  136. package/src/runtime/{retry-executor.ts → recovery/retry-executor.ts} +2 -2
  137. package/src/runtime/{retry-runner.ts → recovery/retry-runner.ts} +2 -8
  138. package/src/runtime/run-tracker.ts +1 -1
  139. package/src/runtime/run-worker.ts +3 -3
  140. package/src/runtime/{coalesce-tasks.ts → scheduling/coalesce-tasks.ts} +3 -3
  141. package/src/runtime/{concurrency.ts → scheduling/concurrency.ts} +9 -3
  142. package/src/runtime/{parallel-research.ts → scheduling/parallel-research.ts} +4 -2
  143. package/src/runtime/{run-coalesced-task-group.ts → scheduling/run-coalesced-task-group.ts} +95 -36
  144. package/src/runtime/{semaphore.ts → scheduling/semaphore.ts} +4 -1
  145. package/src/runtime/{task-graph-scheduler.ts → scheduling/task-graph-scheduler.ts} +1 -1
  146. package/src/runtime/session-resources.ts +1 -1
  147. package/src/runtime/settings-store.ts +1 -1
  148. package/src/runtime/single-agent-compose.ts +1 -0
  149. package/src/runtime/stale-reconciler.ts +1 -1
  150. package/src/runtime/subagent-manager.ts +1 -1
  151. package/src/runtime/supervisor-contact.ts +28 -13
  152. package/src/runtime/task-output-context.ts +7 -5
  153. package/src/runtime/task-runner/child-executor.ts +67 -31
  154. package/src/runtime/task-runner/live-executor.ts +8 -8
  155. package/src/runtime/task-runner/post-execution.ts +12 -12
  156. package/src/runtime/task-runner/pre-execution.ts +6 -6
  157. package/src/runtime/task-runner/progress.ts +1 -1
  158. package/src/runtime/task-runner/run-projection.ts +1 -1
  159. package/src/runtime/task-runner/scaffold-executor.ts +1 -1
  160. package/src/runtime/task-runner/state-helpers.ts +2 -2
  161. package/src/runtime/task-runner.ts +4 -4
  162. package/src/runtime/team-runner.ts +441 -175
  163. package/src/runtime/{completion-guard.ts → verification/completion-guard.ts} +1 -1
  164. package/src/runtime/{green-contract.ts → verification/green-contract.ts} +1 -1
  165. package/src/runtime/{post-checks.ts → verification/post-checks.ts} +4 -4
  166. package/src/runtime/{verification-gates.ts → verification/verification-gates.ts} +5 -5
  167. package/src/runtime/{verification-integrity.ts → verification/verification-integrity.ts} +1 -1
  168. package/src/schema/config-schema.ts +1 -0
  169. package/src/schema/team-tool-schema.ts +149 -185
  170. package/src/state/README.md +44 -0
  171. package/src/state/atomic-write.ts +24 -3
  172. package/src/state/{locks.ts → coordination/locks.ts} +118 -38
  173. package/src/state/{mailbox.ts → coordination/mailbox.ts} +14 -8
  174. package/src/state/{schedule.ts → coordination/schedule.ts} +3 -3
  175. package/src/state/{task-claims.ts → coordination/task-claims.ts} +1 -1
  176. package/src/state/decision-ledger.ts +1 -1
  177. package/src/state/{event-log-rotation.ts → event-log/event-log-rotation.ts} +189 -31
  178. package/src/state/{event-log.ts → event-log/event-log.ts} +77 -39
  179. package/src/state/{jsonl-writer.ts → event-log/jsonl-writer.ts} +14 -6
  180. package/src/state/hook-instinct-bridge.ts +2 -2
  181. package/src/state/{active-run-registry.ts → stores/active-run-registry.ts} +7 -7
  182. package/src/state/{artifact-store.ts → stores/artifact-store.ts} +62 -4
  183. package/src/state/{blob-store.ts → stores/blob-store.ts} +3 -3
  184. package/src/state/{health-store.ts → stores/health-store.ts} +3 -3
  185. package/src/state/{instinct-store.ts → stores/instinct-store.ts} +3 -3
  186. package/src/state/{observation-store.ts → stores/observation-store.ts} +2 -2
  187. package/src/state/{run-cache.ts → stores/run-cache.ts} +4 -4
  188. package/src/state/{run-graph.ts → stores/run-graph.ts} +4 -4
  189. package/src/state/{run-metrics.ts → stores/run-metrics.ts} +3 -3
  190. package/src/state/{state-store.ts → stores/state-store.ts} +254 -21
  191. package/src/state/types.ts +27 -3
  192. package/src/ui/component.ts +55 -0
  193. package/src/ui/crew-select-list.ts +3 -1
  194. package/src/ui/dashboard-panes/agents-pane.ts +1 -1
  195. package/src/ui/dwf-phase-display.ts +1 -1
  196. package/src/ui/heartbeat-aggregator.ts +1 -1
  197. package/src/ui/keybinding-map.ts +179 -6
  198. package/src/ui/layout-primitives.ts +9 -5
  199. package/src/ui/live-conversation-overlay.ts +1 -1
  200. package/src/ui/live-run-sidebar.ts +1 -1
  201. package/src/ui/overlay-stack.ts +148 -0
  202. package/src/ui/overlays/agent-picker-overlay.ts +1 -1
  203. package/src/ui/overlays/mailbox-compose-overlay.ts +1 -1
  204. package/src/ui/overlays/mailbox-detail-overlay.ts +2 -2
  205. package/src/ui/powerbar-publisher.ts +1 -1
  206. package/src/ui/render-scheduler.ts +1 -1
  207. package/src/ui/run-action-dispatcher.ts +3 -3
  208. package/src/ui/run-dashboard.ts +5 -33
  209. package/src/ui/run-event-bus.ts +3 -3
  210. package/src/ui/run-snapshot-cache.ts +37 -18
  211. package/src/ui/settings-overlay.ts +13 -21
  212. package/src/ui/snapshot-types.ts +1 -1
  213. package/src/ui/status-colors.ts +0 -2
  214. package/src/ui/terminal-status.ts +1 -1
  215. package/src/ui/theme-adapter.ts +33 -13
  216. package/src/ui/tool-renderers/index.ts +8 -0
  217. package/src/ui/transcript-viewer.ts +37 -13
  218. package/src/ui/widget/index.ts +32 -9
  219. package/src/ui/widget/widget-formatters.ts +62 -4
  220. package/src/ui/widget/widget-model.ts +2 -2
  221. package/src/ui/widget/widget-renderer.ts +1 -1
  222. package/src/utils/conflict-detect.ts +58 -5
  223. package/src/utils/ndjson.ts +2 -2
  224. package/src/utils/redaction.ts +39 -0
  225. package/src/utils/socket-path.ts +1 -1
  226. package/src/workflows/workflow-config.ts +1 -1
  227. package/src/worktree/cleanup.ts +1 -1
  228. package/src/worktree/worktree-manager.ts +111 -47
  229. package/workflows/chain.workflow.md +2 -2
  230. package/dist/build-meta.json +0 -16807
  231. package/index.bundle.ts +0 -25
  232. package/scripts/bench-check.mjs +0 -96
  233. package/scripts/bench-cold-start.mjs +0 -216
  234. package/scripts/build-bundle.mjs +0 -92
  235. package/scripts/check-all-skills.ts +0 -294
  236. package/scripts/check-bundle-size.mjs +0 -38
  237. package/scripts/check-bundle-staleness.mjs +0 -97
  238. package/scripts/check-conflict-markers.mjs +0 -91
  239. package/scripts/check-lazy-imports.mjs +0 -28
  240. package/scripts/dev-runner.mjs +0 -106
  241. package/scripts/profile-startup.mjs +0 -125
  242. package/scripts/release-smoke.mjs +0 -74
  243. package/scripts/run-bench.mjs +0 -47
  244. package/scripts/run-real-chain.ts +0 -41
  245. package/scripts/test-issue-29-crash.ts +0 -111
  246. package/scripts/test-issue-29-e2e.ts +0 -183
  247. package/scripts/test-issue-29-real-runtime.ts +0 -330
  248. package/scripts/test-issue-29-real-tasks.ts +0 -387
  249. package/scripts/test-issue-29-team-tool.ts +0 -105
  250. package/scripts/test-runner.mjs +0 -84
  251. package/scripts/verify-flicker-fix.ts +0 -109
  252. package/scripts/verify-skill.ts +0 -550
  253. package/scripts/watch-bundle.mjs +0 -259
  254. package/scripts/watchdog-harness.ts +0 -119
  255. package/src/adapters/claude-adapter.ts +0 -23
  256. package/src/adapters/codex-adapter.ts +0 -21
  257. package/src/adapters/cursor-adapter.ts +0 -17
  258. package/src/adapters/export-util.ts +0 -143
  259. package/src/adapters/index.ts +0 -15
  260. package/src/adapters/registry.ts +0 -18
  261. package/src/adapters/types.ts +0 -23
  262. package/src/runtime/code-summary.ts +0 -292
  263. package/src/runtime/cross-extension-rpc.ts +0 -143
  264. package/src/runtime/hidden-handoff.ts +0 -407
  265. package/src/runtime/prose-compressor.ts +0 -162
  266. package/src/subagents/async-entry.ts +0 -1
  267. package/src/subagents/index.ts +0 -3
  268. package/src/subagents/live/control.ts +0 -1
  269. package/src/subagents/live/manager.ts +0 -1
  270. package/src/subagents/live/realtime.ts +0 -1
  271. package/src/subagents/live/session-runtime.ts +0 -1
  272. package/src/subagents/manager.ts +0 -1
  273. package/src/subagents/spawn.ts +0 -1
  274. package/test-integration-check.ts +0 -118
  275. /package/src/runtime/{broker-issuer.ts → broker/broker-issuer.ts} +0 -0
  276. /package/src/runtime/{crew-broker-tokens.ts → broker/crew-broker-tokens.ts} +0 -0
  277. /package/src/runtime/{child-pi-pool.ts → child-pi/child-pi-pool.ts} +0 -0
  278. /package/src/runtime/{compact-pipeline.ts → compaction/compact-pipeline.ts} +0 -0
  279. /package/src/runtime/{compact-stages → compaction/compact-stages}/ansi-strip-stage.ts +0 -0
  280. /package/src/runtime/{compact-stages → compaction/compact-stages}/blank-collapse-stage.ts +0 -0
  281. /package/src/runtime/{compact-stages → compaction/compact-stages}/deduplicate-stage.ts +0 -0
  282. /package/src/runtime/{compact-stages → compaction/compact-stages}/head-snap-stage.ts +0 -0
  283. /package/src/runtime/{compact-stages → compaction/compact-stages}/tail-capture-stage.ts +0 -0
  284. /package/src/runtime/{compact-stages → compaction/compact-stages}/truncation-stage.ts +0 -0
  285. /package/src/runtime/{important-line-classifier.ts → compaction/important-line-classifier.ts} +0 -0
  286. /package/src/runtime/{heartbeat-gradient.ts → heartbeat/heartbeat-gradient.ts} +0 -0
  287. /package/src/runtime/{worker-heartbeat.ts → heartbeat/worker-heartbeat.ts} +0 -0
  288. /package/src/runtime/{worker-startup.ts → heartbeat/worker-startup.ts} +0 -0
  289. /package/src/runtime/{intercom-bridge.ts → live-session/intercom-bridge.ts} +0 -0
  290. /package/src/runtime/{live-control-realtime.ts → live-session/live-control-realtime.ts} +0 -0
  291. /package/src/runtime/{live-extension-bridge.ts → live-session/live-extension-bridge.ts} +0 -0
  292. /package/src/runtime/{live-irc.ts → live-session/live-irc.ts} +0 -0
  293. /package/src/runtime/{live-session-health.ts → live-session/live-session-health.ts} +0 -0
  294. /package/src/runtime/{model-resolver.ts → model/model-resolver.ts} +0 -0
  295. /package/src/runtime/{model-scope.ts → model/model-scope.ts} +0 -0
  296. /package/src/runtime/{output-validator.ts → output/output-validator.ts} +0 -0
  297. /package/src/runtime/{pi-json-output.ts → output/pi-json-output.ts} +0 -0
  298. /package/src/runtime/{progress-event-coalescer.ts → output/progress-event-coalescer.ts} +0 -0
  299. /package/src/runtime/{cancellation-token.ts → process/cancellation-token.ts} +0 -0
  300. /package/src/runtime/{zombie-scanner.ts → process/zombie-scanner.ts} +0 -0
  301. /package/src/runtime/{crash-classification.ts → recovery/crash-classification.ts} +0 -0
  302. /package/src/runtime/{batch-barrier.ts → scheduling/batch-barrier.ts} +0 -0
  303. /package/src/runtime/{global-worker-cap.ts → scheduling/global-worker-cap.ts} +0 -0
  304. /package/src/runtime/{parallel-utils.ts → scheduling/parallel-utils.ts} +0 -0
  305. /package/src/runtime/{scheduler.ts → scheduling/scheduler.ts} +0 -0
  306. /package/src/runtime/{task-graph.ts → scheduling/task-graph.ts} +0 -0
  307. /package/src/runtime/{verification-worktree.ts → verification/verification-worktree.ts} +0 -0
  308. /package/src/state/{event-reconstructor.ts → event-log/event-reconstructor.ts} +0 -0
  309. /package/src/state/{worker-atomic-writer.ts → event-log/worker-atomic-writer.ts} +0 -0
@@ -1,14 +1,14 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
- import { DEFAULT_EVENT_LOG } from "../config/defaults.ts";
5
- import { errors } from "../errors.ts";
6
- import { emitFromTeamEvent } from "../ui/run-event-bus.ts";
7
- import { type IncrementalReadState, readJsonlSince, readJsonlTail } from "../utils/incremental-reader.ts";
8
- import { logInternalError } from "../utils/internal-error.ts";
9
- import { redactSecrets } from "../utils/redaction.ts";
10
- import { sleep, sleepSync } from "../utils/sleep.ts";
11
- import { atomicWriteFile } from "./atomic-write.ts";
4
+ import { DEFAULT_EVENT_LOG } from "../../config/defaults.ts";
5
+ import { errors } from "../../errors.ts";
6
+ import { emitFromTeamEvent } from "../../ui/run-event-bus.ts";
7
+ import { type IncrementalReadState, readJsonlSince, readJsonlTail } from "../../utils/incremental-reader.ts";
8
+ import { logInternalError } from "../../utils/internal-error.ts";
9
+ import { redactSecrets } from "../../utils/redaction.ts";
10
+ import { sleep, sleepSync } from "../../utils/sleep.ts";
11
+ import { atomicWriteFile } from "../atomic-write.ts";
12
12
  import {
13
13
  applyCompactionUnlocked,
14
14
  currentGeneration,
@@ -69,7 +69,25 @@ const MAX_EVENTS_BYTES = 50 * 1024 * 1024;
69
69
 
70
70
  const sequenceCache = new Map<string, { size: number; mtimeMs: number; seq: number; lastAccessMs: number }>();
71
71
  const MAX_SEQUENCE_CACHE_ENTRIES = 256;
72
- let appendCounter = 0;
72
+ // P0-2: per-eventsPath append counter. Previously a single module-global shared
73
+ // across all runs AND never incremented on the async path, so the async rotation
74
+ // gate (`0 % 100 === 0`) was always true → needsRotation ran on EVERY async append
75
+ // (a full read+parse+rewrite once the log crossed 4 MB). FIFO-bounded to avoid
76
+ // unbounded growth with run count.
77
+ const appendCounters = new Map<string, number>();
78
+ const APPEND_COUNTER_MAX_ENTRIES = 256;
79
+ export function tickAppendCounter(eventsPath: string, inc = 1): boolean {
80
+ const prev = appendCounters.get(eventsPath) ?? 0;
81
+ const next = prev + inc;
82
+ appendCounters.set(eventsPath, next);
83
+ if (appendCounters.size > APPEND_COUNTER_MAX_ENTRIES) {
84
+ const oldest = appendCounters.keys().next().value;
85
+ if (oldest !== undefined) appendCounters.delete(oldest);
86
+ }
87
+ // True when a 100-boundary is crossed (matches the old `next % 100 === 0`
88
+ // semantics for inc=1, and generalizes to batch increments).
89
+ return Math.floor(next / 100) > Math.floor(prev / 100);
90
+ }
73
91
  let overflowCounter = 0;
74
92
 
75
93
  /** Simple cross-process lock for an eventsPath to prevent JSONL interleave on concurrent append.
@@ -82,7 +100,7 @@ let overflowCounter = 0;
82
100
  * blocks the Node.js event loop for up to 5s. During that time, AbortSignal handlers
83
101
  * cannot fire, SIGTERM handlers are delayed, and the process appears unresponsive to
84
102
  * orchestrator health checks. Known callers include `appendEvent` (sync path),
85
- * `flushOneEventLogBuffer`, and `state/mailbox.ts`. Prefer the async alternative
103
+ * `flushOneEventLogBuffer`, and `state/coordination/mailbox.ts`. Prefer the async alternative
86
104
  * (`appendEventAsync`) for all new code.
87
105
  */
88
106
  export function withEventLogLockSync<T>(eventsPath: string, fn: () => T, options?: { timeoutMs?: number; staleMs?: number }): T {
@@ -288,13 +306,16 @@ function nextSequence(eventsPath: string): number {
288
306
  const stored = readStoredSequence(eventsPath);
289
307
  const fileShrunk = cached && stat.size < cached.size;
290
308
  if (stored !== undefined && !fileShrunk) {
291
- // EL-1: the sidecar can regress via sync/async interleave —
292
- // appendEventAsync reserves a seq, yields during appendFile/fsync, a
293
- // concurrent sync appendEvent reserves+persists a higher seq, then the
294
- // async path persists its lower seq — regressing the sidecar below the
295
- // file's true max. Trusting the regressed sidecar alone would return a
296
- // duplicate seq on the next append. Take max with scanSequence so a
297
- // regressed sidecar cannot cause duplicate sequence numbers.
309
+ // Trust the sidecar, but guard against a REGRESSED sidecar (e.g. the
310
+ // async path persisted a lower seq, rolling the sidecar back below the
311
+ // file's true max). Take max with a full scan so a regressed sidecar
312
+ // cannot produce a duplicate sequence number (EL-1 regression guard).
313
+ // NOTE (ST-12): a full scan here is acceptable because all three append
314
+ // paths now allocate seqs via reserveSequence/reserveSequenceUnderLock
315
+ // (ST-5: re-read sidecar under lock + max with in-process counter);
316
+ // nextSequence is only consulted for seeding/test helpers, NOT on the
317
+ // production append path. ST-12's perf goal (avoid 4MB scan on first
318
+ // append) is therefore met by ST-5 at the reserveSequence layer.
298
319
  const fileMax = scanSequence(eventsPath);
299
320
  const safeSeq = Math.max(stored, fileMax);
300
321
  sequenceCache.set(eventsPath, {
@@ -318,7 +339,9 @@ function nextSequence(eventsPath: string): number {
318
339
 
319
340
  function persistSequence(eventsPath: string, seq: number): void {
320
341
  try {
321
- atomicWriteFile(sequencePath(eventsPath), String(seq));
342
+ // P0-4: the .seq sidecar is disposable (event-reconstructor tolerates an
343
+ // inconsistent tail); best-effort avoids 2 fsyncs per event append.
344
+ atomicWriteFile(sequencePath(eventsPath), String(seq), { durability: "best-effort" });
322
345
  } catch (error) {
323
346
  logInternalError("event-log.persist-sequence-file", error, `eventsPath=${eventsPath}`);
324
347
  }
@@ -335,17 +358,26 @@ function persistSequence(eventsPath: string, seq: number): void {
335
358
  // sidecar durable for crash recovery across restarts.
336
359
  const seqCounters = new Map<string, number>();
337
360
 
338
- /** Atomically reserve the next sequence number for `eventsPath`. */
361
+ /** Atomically reserve the next sequence number for `eventsPath`.
362
+ *
363
+ * ST-5 (v0.9.56): previously this seeded the in-process `seqCounters` counter
364
+ * ONCE per process (reading the `.seq` sidecar a single time via
365
+ * `nextSequence`) and then served every subsequent call purely from that
366
+ * process-local counter. Two processes that both seeded before either had
367
+ * persisted ended up sharing the same counter base -> duplicate sequence
368
+ * numbers -> `sinceSeq` streaming readers silently dropped the second event.
369
+ * The async path was already fixed via `reserveSequenceUnderLock` (which
370
+ * re-reads the sidecar every call); the sync (`appendEvent`) and buffered
371
+ * (`appendEventBatchInsideLock`) paths were NOT.
372
+ *
373
+ * Now ALL three append paths share one body: re-read the authoritative `.seq`
374
+ * sidecar on EVERY call and take `max(sidecar, inProcess)` so a counter that
375
+ * lags behind a sidecar advanced by another process can never assign a
376
+ * regressed (duplicate) seq. Every call site already holds a cross-process
377
+ * file lock (`withEventLogLockSync` for sync/buffered, `withEventLogLockAsync`
378
+ * for async), so the sidecar re-read is race-free within each lock class. */
339
379
  function reserveSequence(eventsPath: string): number {
340
- let last = seqCounters.get(eventsPath);
341
- if (last === undefined) {
342
- // Seed once from the authoritative source (sidecar / cache / file scan).
343
- // nextSequence() returns the NEXT seq to assign, so the last assigned is one less.
344
- last = nextSequence(eventsPath) - 1;
345
- }
346
- const next = last + 1;
347
- seqCounters.set(eventsPath, next);
348
- return next;
380
+ return reserveSequenceUnderLock(eventsPath);
349
381
  }
350
382
 
351
383
  /** Keep the in-process counter monotonic w.r.t. an explicitly-provided seq
@@ -501,7 +533,8 @@ async function withEventLogLockAsync<T>(
501
533
  try {
502
534
  await fs.promises.mkdir(lockDir);
503
535
  try {
504
- atomicWriteFile(pidFile, String(process.pid));
536
+ // P0-4: the lock pid file is disposable stale-lock state; best-effort.
537
+ atomicWriteFile(pidFile, String(process.pid), { durability: "best-effort" });
505
538
  } catch {
506
539
  /* best-effort */
507
540
  }
@@ -724,12 +757,15 @@ export async function appendEventAsync(eventsPath: string, event: AppendTeamEven
724
757
  // Phase 1.5: when worker atomic writer is enabled, append via worker.
725
758
  if (isWorkerAtomicWriterEnabled()) {
726
759
  await appendFileViaWorker(eventsPath, line);
727
- // Worker path: fsync via a separate open (worker manages its own fd).
728
- const fd = await fs.promises.open(eventsPath, "r+");
729
- try {
730
- await fd.sync();
731
- } finally {
732
- await fd.close();
760
+ // P0-4 (F3a mirror): fsync terminal events only; non-terminal events are
761
+ // informational and the event-reconstructor tolerates an inconsistent tail.
762
+ if (isTerminal) {
763
+ const fd = await fs.promises.open(eventsPath, "r+");
764
+ try {
765
+ await fd.sync();
766
+ } finally {
767
+ await fd.close();
768
+ }
733
769
  }
734
770
  } else {
735
771
  // FIND-10: single-fd append+fsync. Opens in append mode, writes,
@@ -740,7 +776,8 @@ export async function appendEventAsync(eventsPath: string, event: AppendTeamEven
740
776
  const fd = await fs.promises.open(eventsPath, "a");
741
777
  try {
742
778
  await fd.appendFile(line, "utf-8");
743
- await fd.sync();
779
+ // P0-4 (F3a mirror): skip the data fsync for non-terminal events.
780
+ if (isTerminal) await fd.sync();
744
781
  // FIND-10 R1 fix: the cache-optimization fd.stat() must NOT sit in the
745
782
  // seq-durability critical path. If it threw (rare — fd invalidated),
746
783
  // it would skip persistSequence below and reopen the seq-reuse
@@ -764,7 +801,7 @@ export async function appendEventAsync(eventsPath: string, event: AppendTeamEven
764
801
  // FIND-10: track whether compaction happened after the append so the
765
802
  // cache-update stat can safely reuse postAppendStat (file unchanged).
766
803
  let compactedAfterAppend = false;
767
- if (appendCounter % 100 === 0 && needsRotation(eventsPath)) {
804
+ if (tickAppendCounter(eventsPath) && needsRotation(eventsPath)) {
768
805
  compactedAfterAppend = true;
769
806
  try {
770
807
  const prepared = prepareCompaction(eventsPath);
@@ -865,6 +902,8 @@ export async function appendEventAsync(eventsPath: string, event: AppendTeamEven
865
902
  */
866
903
  async function appendEventBatchInsideLock(eventsPath: string, queue: BufferedAppend[]): Promise<void> {
867
904
  if (queue.length === 0) return;
905
+ // P0-2: keep the per-path counter honest for cross-path rotation sampling.
906
+ tickAppendCounter(eventsPath, queue.length);
868
907
  fs.mkdirSync(path.dirname(eventsPath), { recursive: true });
869
908
 
870
909
  // Pre-flight size check (mirrors appendEventInsideLock). We do it once for
@@ -1100,8 +1139,7 @@ function appendEventInsideLock(eventsPath: string, event: AppendTeamEvent): Team
1100
1139
  logInternalError("event-log.persist-sequence", error, `eventsPath=${eventsPath}`);
1101
1140
  }
1102
1141
  }
1103
- appendCounter++;
1104
- if (appendCounter % 100 === 0 && needsRotation(eventsPath)) {
1142
+ if (tickAppendCounter(eventsPath) && needsRotation(eventsPath)) {
1105
1143
  // Round 24 (BUG 1): we are INSIDE withEventLogLockSync here (called via
1106
1144
  // appendEventInsideLock). The mkdir lock is NOT re-entrant, so calling the
1107
1145
  // locked compactEventLog would deadlock → 5s timeout → compaction never
@@ -1,6 +1,6 @@
1
1
  import * as fs from "node:fs";
2
- import { logInternalError } from "../utils/internal-error.ts";
3
- import { redactJsonLine } from "../utils/redaction.ts";
2
+ import { logInternalError } from "../../utils/internal-error.ts";
3
+ import { redactJsonLine } from "../../utils/redaction.ts";
4
4
 
5
5
  export interface DrainableSource {
6
6
  pause(): void;
@@ -35,8 +35,12 @@ export interface JsonlWriter {
35
35
  export function createJsonlWriter(filePath: string | undefined, source: DrainableSource, deps: JsonlWriterDeps = {}): JsonlWriter {
36
36
  if (!filePath) {
37
37
  return {
38
- writeLine() {},
39
- async close() {},
38
+ writeLine() {
39
+ /* no-op: null writer */
40
+ },
41
+ async close() {
42
+ /* no-op: null writer */
43
+ },
40
44
  };
41
45
  }
42
46
 
@@ -46,8 +50,12 @@ export function createJsonlWriter(filePath: string | undefined, source: Drainabl
46
50
  stream = createWriteStream(filePath);
47
51
  } catch {
48
52
  return {
49
- writeLine() {},
50
- async close() {},
53
+ writeLine() {
54
+ /* no-op: stream creation failed */
55
+ },
56
+ async close() {
57
+ /* no-op: stream creation failed */
58
+ },
51
59
  };
52
60
  }
53
61
 
@@ -6,13 +6,13 @@
6
6
  import { crewHooks } from "../runtime/crew-hooks.ts";
7
7
 
8
8
  // Lazy-initialized store and paths
9
- let storeInstance: import("./instinct-store.js").InstinctStore | null = null;
9
+ let storeInstance: import("./stores/instinct-store.js").InstinctStore | null = null;
10
10
  let pathsInstance: typeof import("../utils/paths.js") | null = null;
11
11
 
12
12
  async function getStore() {
13
13
  if (!storeInstance) {
14
14
  // LAZY: defer dynamic import of ./instinct-store.js to its call site.
15
- const { InstinctStore } = await import("./instinct-store.js");
15
+ const { InstinctStore } = await import("./stores/instinct-store.js");
16
16
  // LAZY: defer dynamic import of ../utils/paths.js to its call site.
17
17
  const paths = await import("../utils/paths.js");
18
18
  storeInstance = new InstinctStore(paths.projectCrewRoot(process.cwd()));
@@ -1,13 +1,13 @@
1
1
  import * as crypto from "node:crypto";
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
- import { DEFAULT_CACHE, DEFAULT_PATHS } from "../config/defaults.ts";
5
- import { userCrewRoot } from "../utils/paths.ts";
6
- import { isSafePathId } from "../utils/safe-paths.ts";
7
- import { sharedScanCache } from "../utils/scan-cache.ts";
8
- import { sleepSync } from "../utils/sleep.ts";
9
- import { atomicWriteJson, isSymlinkSafePath, renameWithRetry } from "./atomic-write.ts";
10
- import type { TeamRunManifest } from "./types.ts";
4
+ import { DEFAULT_CACHE, DEFAULT_PATHS } from "../../config/defaults.ts";
5
+ import { userCrewRoot } from "../../utils/paths.ts";
6
+ import { isSafePathId } from "../../utils/safe-paths.ts";
7
+ import { sharedScanCache } from "../../utils/scan-cache.ts";
8
+ import { sleepSync } from "../../utils/sleep.ts";
9
+ import { atomicWriteJson, isSymlinkSafePath, renameWithRetry } from "../atomic-write.ts";
10
+ import type { TeamRunManifest } from "../types.ts";
11
11
 
12
12
  /** Magic bytes prefix for binary registry to prevent deserialization of hostile files. */
13
13
  const BINARY_MAGIC = Buffer.from("PICREW2BIN", "utf-8");
@@ -1,10 +1,10 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
- import { redactSecretString, redactSecrets } from "../utils/redaction.ts";
5
- import { resolveRealContainedPath } from "../utils/safe-paths.ts";
6
- import { atomicWriteFile } from "./atomic-write.ts";
7
- import type { ArtifactDescriptor } from "./types.ts";
4
+ import { redactSecretString, redactSecrets } from "../../utils/redaction.ts";
5
+ import { resolveRealContainedPath } from "../../utils/safe-paths.ts";
6
+ import { atomicWriteFile } from "../atomic-write.ts";
7
+ import type { ArtifactDescriptor } from "../types.ts";
8
8
 
9
9
  function hashContent(content: string): string {
10
10
  return createHash("sha256").update(content).digest("hex");
@@ -101,6 +101,64 @@ export function cleanupOldArtifacts(artifactsRoot: string, options: ArtifactClea
101
101
  if (!didCleanup) writeCleanupMarker(artifactsRoot, markerFile);
102
102
  }
103
103
 
104
+ /**
105
+ * ST-10: Default TTL for "temporary" retention artifacts (1 hour).
106
+ * Temporary artifacts (progress, notepad) are short-lived and should not
107
+ * accumulate. One hour balances cleanup aggressiveness with the possibility
108
+ * that a long-running task writes periodic progress updates.
109
+ */
110
+ export const TEMPORARY_RETENTION_TTL_MS = 60 * 60 * 1000;
111
+
112
+ /**
113
+ * ST-10: Check whether an artifact descriptor indicates the file should be
114
+ * pruned (expired).
115
+ *
116
+ * Enforces:
117
+ * - `expiresAt`: if set and now ≥ expiresAt → expired.
118
+ * - `retention: "temporary"`: if createdAt age ≥ temporaryTtlMs → expired.
119
+ * ("run" and "project" retentions are handled by run-level prune and the
120
+ * age-based {@link cleanupOldArtifacts}, not by this per-descriptor check.)
121
+ */
122
+ export function isArtifactExpired(desc: ArtifactDescriptor, nowMs: number, temporaryTtlMs: number = TEMPORARY_RETENTION_TTL_MS): boolean {
123
+ // Explicit expiresAt takes precedence.
124
+ if (desc.expiresAt) {
125
+ const expiryMs = Date.parse(desc.expiresAt);
126
+ if (Number.isFinite(expiryMs) && nowMs >= expiryMs) return true;
127
+ }
128
+ // "temporary" retention has a short TTL.
129
+ if (desc.retention === "temporary" && desc.createdAt) {
130
+ const createdMs = Date.parse(desc.createdAt);
131
+ if (Number.isFinite(createdMs) && nowMs - createdMs >= temporaryTtlMs) return true;
132
+ }
133
+ return false;
134
+ }
135
+
136
+ /**
137
+ * ST-10: Prune expired artifacts by enforcing ArtifactDescriptor.retention/expiresAt.
138
+ *
139
+ * Deletes artifact files whose descriptor indicates expiry (expiresAt passed
140
+ * or temporary-retention TTL exceeded). Returns the count of files deleted.
141
+ *
142
+ * Unlike {@link cleanupOldArtifacts} (which is filesystem age-based on raw
143
+ * files), this operates on artifact *metadata* from the manifest's `artifacts[]`
144
+ * and deletes specific expired files via their descriptor `path`.
145
+ */
146
+ export function pruneExpiredArtifacts(descriptors: ArtifactDescriptor[], options?: { now?: number; temporaryTtlMs?: number }): number {
147
+ const nowMs = options?.now ?? Date.now();
148
+ const temporaryTtlMs = options?.temporaryTtlMs ?? TEMPORARY_RETENTION_TTL_MS;
149
+ let deleted = 0;
150
+ for (const desc of descriptors) {
151
+ if (!isArtifactExpired(desc, nowMs, temporaryTtlMs)) continue;
152
+ try {
153
+ fs.unlinkSync(desc.path);
154
+ deleted++;
155
+ } catch {
156
+ // Best-effort: file may already be removed or path may be stale.
157
+ }
158
+ }
159
+ return deleted;
160
+ }
161
+
104
162
  function resolveInside(baseDir: string, relativePath: string): string {
105
163
  // Check if baseDir is a symlink on every call to prevent symlink attacks
106
164
  try {
@@ -1,9 +1,9 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
- import { resolveRealContainedPath } from "../utils/safe-paths.ts";
5
- import { atomicWriteFile, renameWithRetry } from "./atomic-write.ts";
6
- import { withFileLockSync } from "./locks.ts";
4
+ import { resolveRealContainedPath } from "../../utils/safe-paths.ts";
5
+ import { atomicWriteFile, renameWithRetry } from "../atomic-write.ts";
6
+ import { withFileLockSync } from "../coordination/locks.ts";
7
7
 
8
8
  const SHA256_HEX = /^[a-f0-9]{64}$/i;
9
9
 
@@ -1,8 +1,8 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import type { ManifestSummary } from "../runtime/task-health.ts";
4
- import { computeRunHealth } from "../runtime/task-health.ts";
5
- import { atomicWriteFile } from "./atomic-write.ts";
3
+ import type { ManifestSummary } from "../../runtime/task-health.ts";
4
+ import { computeRunHealth } from "../../runtime/task-health.ts";
5
+ import { atomicWriteFile } from "../atomic-write.ts";
6
6
 
7
7
  // Relative to the crew root (`<cwd>/.crew`). BUG A fix (pts/2 hang
8
8
  // investigation 2026-06-16): this was `.crew/state/health`, which double-joined
@@ -1,8 +1,8 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
- import { assertSafePathId } from "../utils/safe-paths.ts";
5
- import { atomicWriteFile } from "./atomic-write.ts";
4
+ import { assertSafePathId } from "../../utils/safe-paths.ts";
5
+ import { atomicWriteFile } from "../atomic-write.ts";
6
6
 
7
7
  /**
8
8
  * Represents a learned instinct that guides agent behavior.
@@ -272,4 +272,4 @@ export class InstinctStore {
272
272
  export {
273
273
  getGlobalStorageDir,
274
274
  getProjectStorageDir,
275
- } from "../utils/project-detector.ts";
275
+ } from "../../utils/project-detector.ts";
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
- import { logInternalError } from "../utils/internal-error.ts";
3
- import { atomicWriteJson } from "./atomic-write.ts";
2
+ import { logInternalError } from "../../utils/internal-error.ts";
3
+ import { atomicWriteJson } from "../atomic-write.ts";
4
4
 
5
5
  // ── Types ────────────────────────────────────────────────────────────────
6
6
 
@@ -1,10 +1,10 @@
1
1
  import * as crypto from "node:crypto";
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
- import { projectCrewRoot } from "../utils/paths.ts";
5
- import { atomicWriteJson } from "./atomic-write.ts";
6
- import { withFileLockSync } from "./locks.ts";
7
- import type { TeamTaskState } from "./types.ts";
4
+ import { projectCrewRoot } from "../../utils/paths.ts";
5
+ import { atomicWriteJson } from "../atomic-write.ts";
6
+ import { withFileLockSync } from "../coordination/locks.ts";
7
+ import type { TeamTaskState } from "../types.ts";
8
8
 
9
9
  const DEFAULT_CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
10
10
 
@@ -1,9 +1,9 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import { projectCrewRoot } from "../utils/paths.ts";
4
- import { assertSafePathId } from "../utils/safe-paths.ts";
5
- import { atomicWriteFile } from "./atomic-write.ts";
6
- import type { TeamRunManifest, TeamTaskState } from "./types.ts";
3
+ import { projectCrewRoot } from "../../utils/paths.ts";
4
+ import { assertSafePathId } from "../../utils/safe-paths.ts";
5
+ import { atomicWriteFile } from "../atomic-write.ts";
6
+ import type { TeamRunManifest, TeamTaskState } from "../types.ts";
7
7
 
8
8
  export interface RunGraphNode {
9
9
  id: string;
@@ -1,8 +1,8 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import { projectCrewRoot } from "../utils/paths.ts";
4
- import { assertSafePathId } from "../utils/safe-paths.ts";
5
- import { atomicWriteJson, readJsonFile } from "./atomic-write.ts";
3
+ import { projectCrewRoot } from "../../utils/paths.ts";
4
+ import { assertSafePathId } from "../../utils/safe-paths.ts";
5
+ import { atomicWriteJson, readJsonFile } from "../atomic-write.ts";
6
6
  import { loadRunManifestById } from "./state-store.ts";
7
7
 
8
8
  /**