@wrongstack/core 0.296.4 → 0.298.0

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 (820) hide show
  1. package/dist/chronicle/index.js +126 -22
  2. package/dist/chronicle/project-server-client.d.ts +12 -0
  3. package/dist/chronicle/project-server-protocol.d.ts +22 -0
  4. package/dist/chronicle/project-server.js +1076 -283
  5. package/dist/coordination/agents/index.js +136 -1
  6. package/dist/coordination/agents/role-skills.d.ts +3 -2
  7. package/dist/coordination/brain-cache.d.ts +14 -0
  8. package/dist/coordination/brain-trace.d.ts +2 -1
  9. package/dist/coordination/director/director-task-registry.d.ts +12 -0
  10. package/dist/coordination/fleet-supervisor.d.ts +21 -0
  11. package/dist/coordination/index.d.ts +23 -21
  12. package/dist/coordination/index.js +16514 -15945
  13. package/dist/coordination/mail-tools.d.ts +3 -3
  14. package/dist/coordination/mailbox-credential-store.d.ts +22 -2
  15. package/dist/coordination/mailbox-credential-throttle.d.ts +63 -0
  16. package/dist/coordination/mailbox-http-router.d.ts +6 -0
  17. package/dist/coordination/mailbox-project-server-client.d.ts +16 -0
  18. package/dist/coordination/mailbox-project-server-protocol.d.ts +43 -5
  19. package/dist/coordination/mailbox-project-server.js +1270 -289
  20. package/dist/coordination/provider-status-tracker.d.ts +2 -0
  21. package/dist/coordination/remote-mailbox-credential-store.d.ts +5 -5
  22. package/dist/coordination/remote-mailbox.d.ts +30 -4
  23. package/dist/coordination/sqlite-mailbox-credentials.d.ts +11 -0
  24. package/dist/core/context.d.ts +5 -5
  25. package/dist/core/fallback-profile-manager.d.ts +14 -0
  26. package/dist/core/index.js +624 -168
  27. package/dist/core/instruction-bundle.d.ts +12 -0
  28. package/dist/core/system-prompt-builder.d.ts +14 -0
  29. package/dist/core/system-prompt-skill-bodies.d.ts +1 -1
  30. package/dist/core/system-prompt-skill-text.d.ts +1 -2
  31. package/dist/defaults/index.js +2393 -1302
  32. package/dist/design/index.js +47 -8
  33. package/dist/execution/design-materialize.d.ts +22 -0
  34. package/dist/execution/index.js +582 -285
  35. package/dist/execution/skill-loader.d.ts +10 -1
  36. package/dist/execution/tool-error-taxonomy.d.ts +31 -0
  37. package/dist/execution/tool-executor.d.ts +1 -0
  38. package/dist/goal/index.js +71 -17
  39. package/dist/goal/phase-store.d.ts +7 -0
  40. package/dist/hooks/index.js +5 -0
  41. package/dist/hq/auth-audit.d.ts +6 -0
  42. package/dist/hq/auth-store.d.ts +59 -3
  43. package/dist/hq/index.js +680 -163
  44. package/dist/hq/kanban-store.d.ts +3 -0
  45. package/dist/hq/persistence/event-log.d.ts +25 -0
  46. package/dist/hq/persistence.d.ts +1 -1
  47. package/dist/hq/protocol/browser.d.ts +2 -1
  48. package/dist/hq/protocol/client.d.ts +18 -1
  49. package/dist/hq/protocol/core.d.ts +5 -16
  50. package/dist/hq/protocol/envelope.d.ts +29 -0
  51. package/dist/hq/protocol/governance.d.ts +34 -0
  52. package/dist/hq/protocol/peer.d.ts +55 -0
  53. package/dist/hq/protocol/project.d.ts +3 -0
  54. package/dist/hq/protocol/resume.d.ts +56 -0
  55. package/dist/hq/protocol.d.ts +11 -8
  56. package/dist/hq/protocol.js +675 -0
  57. package/dist/hq/publisher.d.ts +31 -2
  58. package/dist/hq/session-bridge.d.ts +8 -0
  59. package/dist/index.js +7197 -4006
  60. package/dist/infrastructure/index.js +198 -101
  61. package/dist/kernel/events/session-events.d.ts +3 -0
  62. package/dist/kernel/events/tool-events.d.ts +3 -1
  63. package/dist/models/index.js +1 -1
  64. package/dist/observability/index.d.ts +1 -1
  65. package/dist/observability/index.js +103 -5
  66. package/dist/observability/metrics.d.ts +37 -0
  67. package/dist/observability/prometheus.d.ts +11 -0
  68. package/dist/plugin/index.d.ts +5 -1
  69. package/dist/plugin/index.js +965 -511
  70. package/dist/plugins/auto-review-plugin.d.ts +6 -0
  71. package/dist/plugins/review-store-maintenance.d.ts +26 -0
  72. package/dist/plugins/review-types.d.ts +6 -0
  73. package/dist/security/capabilities.d.ts +45 -1
  74. package/dist/security/error-sanitize.d.ts +49 -0
  75. package/dist/security/file-permissions.d.ts +41 -0
  76. package/dist/security/index.d.ts +9 -6
  77. package/dist/security/index.js +3007 -2032
  78. package/dist/security/permission-policy.d.ts +55 -3
  79. package/dist/security/secret-vault.d.ts +17 -0
  80. package/dist/security/totp.d.ts +86 -0
  81. package/dist/skills/frontmatter.d.ts +12 -1
  82. package/dist/skills/github-fetcher.d.ts +48 -0
  83. package/dist/skills/index.js +137 -26
  84. package/dist/skills/limits.d.ts +19 -0
  85. package/dist/skills/skill-installer.d.ts +13 -0
  86. package/dist/storage/file-session-writer.d.ts +23 -0
  87. package/dist/storage/index.js +1140 -905
  88. package/dist/storage/provider-config-watcher.d.ts +1 -0
  89. package/dist/storage/session-helpers.d.ts +1 -0
  90. package/dist/storage/session-reader.d.ts +0 -7
  91. package/dist/storage/session-store/types.d.ts +12 -1
  92. package/dist/storage/session-store.d.ts +11 -2
  93. package/dist/storage/session-workspace-checkpoints.d.ts +5 -0
  94. package/dist/tools/fallback-manage-tools.d.ts +6 -0
  95. package/dist/tools/index.js +305 -133
  96. package/dist/tools/mcp-use.d.ts +1 -1
  97. package/dist/types/blocks.d.ts +7 -0
  98. package/dist/types/config/autonomy.d.ts +12 -3
  99. package/dist/types/config/mcp-features.d.ts +15 -1
  100. package/dist/types/config/root.d.ts +10 -4
  101. package/dist/types/context-window.d.ts +15 -0
  102. package/dist/types/index.d.ts +1 -1
  103. package/dist/types/observability.d.ts +13 -0
  104. package/dist/types/secret-vault.d.ts +8 -0
  105. package/dist/types/session.d.ts +42 -0
  106. package/dist/types/skill.d.ts +56 -0
  107. package/dist/types/tool-executor.d.ts +2 -0
  108. package/dist/utils/continuous-memory-profiler.d.ts +35 -0
  109. package/dist/utils/env-typed.d.ts +64 -0
  110. package/dist/utils/glob-match.d.ts +21 -1
  111. package/dist/utils/heap-watchdog-types.d.ts +62 -0
  112. package/dist/utils/heap-watchdog.d.ts +15 -34
  113. package/dist/utils/heap-watchdog.js +592 -18
  114. package/dist/utils/incoming-images.d.ts +19 -0
  115. package/dist/utils/index.d.ts +15 -11
  116. package/dist/utils/index.js +1425 -545
  117. package/dist/utils/memory-flight-recorder.d.ts +47 -0
  118. package/dist/utils/path-segment.d.ts +29 -0
  119. package/dist/utils/tool-name.d.ts +34 -0
  120. package/dist/utils/win32-cmd.d.ts +25 -0
  121. package/dist/worktree/index.js +5 -0
  122. package/package.json +11 -3
  123. package/skills/chimera/SKILL.md +16 -4
  124. package/skills/data-governance/SKILL.md +92 -0
  125. package/skills/wrongstack-kanban/SKILL.md +129 -0
  126. package/skills/wrongstack-kanban/agents/openai.yaml +4 -0
  127. package/skills/wrongstack-mailbox-mcp/SKILL.md +72 -0
  128. package/skills/wrongstack-mailbox-mcp/agents/openai.yaml +4 -0
  129. package/dist/agent-status-helpers.d.ts.map +0 -1
  130. package/dist/agent-status-tracker.d.ts.map +0 -1
  131. package/dist/boot.d.ts.map +0 -1
  132. package/dist/chronicle/context.d.ts.map +0 -1
  133. package/dist/chronicle/decision-adapter.d.ts.map +0 -1
  134. package/dist/chronicle/domain-adapter.d.ts.map +0 -1
  135. package/dist/chronicle/event-hash.d.ts.map +0 -1
  136. package/dist/chronicle/file-observer.d.ts.map +0 -1
  137. package/dist/chronicle/health-monitor.d.ts.map +0 -1
  138. package/dist/chronicle/identity.d.ts.map +0 -1
  139. package/dist/chronicle/index.d.ts.map +0 -1
  140. package/dist/chronicle/index.js.map +0 -7
  141. package/dist/chronicle/journal.d.ts.map +0 -1
  142. package/dist/chronicle/legacy-journal-import.d.ts.map +0 -1
  143. package/dist/chronicle/metrics-store.d.ts.map +0 -1
  144. package/dist/chronicle/partition-filename.d.ts.map +0 -1
  145. package/dist/chronicle/partition-range-cache.d.ts.map +0 -1
  146. package/dist/chronicle/process-adapter.d.ts.map +0 -1
  147. package/dist/chronicle/project-access.d.ts.map +0 -1
  148. package/dist/chronicle/project-server-client.d.ts.map +0 -1
  149. package/dist/chronicle/project-server-endpoint.d.ts.map +0 -1
  150. package/dist/chronicle/project-server-protocol.d.ts.map +0 -1
  151. package/dist/chronicle/project-server.d.ts.map +0 -1
  152. package/dist/chronicle/project-server.js.map +0 -7
  153. package/dist/chronicle/prompt-manifest.d.ts.map +0 -1
  154. package/dist/chronicle/provider-adapter.d.ts.map +0 -1
  155. package/dist/chronicle/query.d.ts.map +0 -1
  156. package/dist/chronicle/review-adapter.d.ts.map +0 -1
  157. package/dist/chronicle/rollup-adapter.d.ts.map +0 -1
  158. package/dist/chronicle/sink.d.ts.map +0 -1
  159. package/dist/chronicle/sqlite-journal.d.ts.map +0 -1
  160. package/dist/chronicle/sqlite-query.d.ts.map +0 -1
  161. package/dist/chronicle/stream-adapter.d.ts.map +0 -1
  162. package/dist/chronicle/tool-adapter.d.ts.map +0 -1
  163. package/dist/chronicle/types.d.ts.map +0 -1
  164. package/dist/coordination/adaptive-concurrency.d.ts.map +0 -1
  165. package/dist/coordination/agent-bridge.d.ts.map +0 -1
  166. package/dist/coordination/agent-monitor.d.ts.map +0 -1
  167. package/dist/coordination/agent-subagent-runner.d.ts.map +0 -1
  168. package/dist/coordination/agents/agent-prompts.d.ts.map +0 -1
  169. package/dist/coordination/agents/index.d.ts.map +0 -1
  170. package/dist/coordination/agents/index.js.map +0 -7
  171. package/dist/coordination/agents/phase1-discovery.d.ts.map +0 -1
  172. package/dist/coordination/agents/phase2-planning.d.ts.map +0 -1
  173. package/dist/coordination/agents/phase3-build.d.ts.map +0 -1
  174. package/dist/coordination/agents/phase3-techstack.d.ts.map +0 -1
  175. package/dist/coordination/agents/phase3-wave1-platform.d.ts.map +0 -1
  176. package/dist/coordination/agents/phase3-wave2-meta.d.ts.map +0 -1
  177. package/dist/coordination/agents/phase4-verify.d.ts.map +0 -1
  178. package/dist/coordination/agents/phase5-review.d.ts.map +0 -1
  179. package/dist/coordination/agents/phase6-domain.d.ts.map +0 -1
  180. package/dist/coordination/agents/phase7-knowledge.d.ts.map +0 -1
  181. package/dist/coordination/agents/phase8-delivery.d.ts.map +0 -1
  182. package/dist/coordination/agents/phase8-wave3-products.d.ts.map +0 -1
  183. package/dist/coordination/agents/phase9-meta.d.ts.map +0 -1
  184. package/dist/coordination/agents/phase9-wave4-platform-meta.d.ts.map +0 -1
  185. package/dist/coordination/agents/project-agent-config-validation.d.ts.map +0 -1
  186. package/dist/coordination/agents/project-agent-consolidation.d.ts.map +0 -1
  187. package/dist/coordination/agents/project-agent-files.d.ts.map +0 -1
  188. package/dist/coordination/agents/project-agent-identity-types.d.ts.map +0 -1
  189. package/dist/coordination/agents/project-agent-identity.d.ts.map +0 -1
  190. package/dist/coordination/agents/project-agent-knowledge-manifests.d.ts.map +0 -1
  191. package/dist/coordination/agents/project-agent-learning-entries.d.ts.map +0 -1
  192. package/dist/coordination/agents/project-agent-learning-normalize.d.ts.map +0 -1
  193. package/dist/coordination/agents/project-agent-learning-policy.d.ts.map +0 -1
  194. package/dist/coordination/agents/project-agent-learning-structured.d.ts.map +0 -1
  195. package/dist/coordination/agents/project-agent-paths.d.ts.map +0 -1
  196. package/dist/coordination/agents/project-agent-profile.d.ts.map +0 -1
  197. package/dist/coordination/agents/role-skills.d.ts.map +0 -1
  198. package/dist/coordination/agents/types.d.ts.map +0 -1
  199. package/dist/coordination/agents.d.ts.map +0 -1
  200. package/dist/coordination/auto-extend.d.ts.map +0 -1
  201. package/dist/coordination/autonomous-brain.d.ts.map +0 -1
  202. package/dist/coordination/autonomous-coordinator.d.ts.map +0 -1
  203. package/dist/coordination/brain-cache.d.ts.map +0 -1
  204. package/dist/coordination/brain-heuristics.d.ts.map +0 -1
  205. package/dist/coordination/brain-ledger.d.ts.map +0 -1
  206. package/dist/coordination/brain-monitor.d.ts.map +0 -1
  207. package/dist/coordination/brain-rules.d.ts.map +0 -1
  208. package/dist/coordination/brain-telemetry.d.ts.map +0 -1
  209. package/dist/coordination/brain-trace.d.ts.map +0 -1
  210. package/dist/coordination/brain.d.ts.map +0 -1
  211. package/dist/coordination/change-manager.d.ts.map +0 -1
  212. package/dist/coordination/checkpoint-wiring.d.ts.map +0 -1
  213. package/dist/coordination/collab-bus.d.ts.map +0 -1
  214. package/dist/coordination/collab-debug-types.d.ts.map +0 -1
  215. package/dist/coordination/collab-debug.d.ts.map +0 -1
  216. package/dist/coordination/collab-director-host.d.ts.map +0 -1
  217. package/dist/coordination/commit-safety.d.ts.map +0 -1
  218. package/dist/coordination/consensus-protocol.d.ts.map +0 -1
  219. package/dist/coordination/coordinator/error-classifier.d.ts.map +0 -1
  220. package/dist/coordination/delegate-tool.d.ts.map +0 -1
  221. package/dist/coordination/dep-watcher-bridge.d.ts.map +0 -1
  222. package/dist/coordination/dep-watcher.d.ts.map +0 -1
  223. package/dist/coordination/director/director-btw-notes.d.ts.map +0 -1
  224. package/dist/coordination/director/director-budget-policy.d.ts.map +0 -1
  225. package/dist/coordination/director/director-collab.d.ts.map +0 -1
  226. package/dist/coordination/director/director-errors.d.ts.map +0 -1
  227. package/dist/coordination/director/director-task-registry.d.ts.map +0 -1
  228. package/dist/coordination/director/director-toolset.d.ts.map +0 -1
  229. package/dist/coordination/director-basic-tools.d.ts.map +0 -1
  230. package/dist/coordination/director-collab-tools.d.ts.map +0 -1
  231. package/dist/coordination/director-host-contracts.d.ts.map +0 -1
  232. package/dist/coordination/director-input-helpers.d.ts.map +0 -1
  233. package/dist/coordination/director-kanban-queue-helpers.d.ts.map +0 -1
  234. package/dist/coordination/director-options.d.ts.map +0 -1
  235. package/dist/coordination/director-prompts.d.ts.map +0 -1
  236. package/dist/coordination/director-quality-gate-tool.d.ts.map +0 -1
  237. package/dist/coordination/director-session.d.ts.map +0 -1
  238. package/dist/coordination/director-spawn-model.d.ts.map +0 -1
  239. package/dist/coordination/director-tools.d.ts.map +0 -1
  240. package/dist/coordination/director.d.ts.map +0 -1
  241. package/dist/coordination/dispatcher.d.ts.map +0 -1
  242. package/dist/coordination/file-author-tracker.d.ts.map +0 -1
  243. package/dist/coordination/fleet-bus.d.ts.map +0 -1
  244. package/dist/coordination/fleet-event-validation.d.ts.map +0 -1
  245. package/dist/coordination/fleet-manager.d.ts.map +0 -1
  246. package/dist/coordination/fleet-spawn.d.ts.map +0 -1
  247. package/dist/coordination/fleet-status-tool.d.ts.map +0 -1
  248. package/dist/coordination/fleet-supervisor.d.ts.map +0 -1
  249. package/dist/coordination/fleet.d.ts.map +0 -1
  250. package/dist/coordination/global-mailbox-completion.d.ts.map +0 -1
  251. package/dist/coordination/global-mailbox-paths.d.ts.map +0 -1
  252. package/dist/coordination/icoordinator.d.ts.map +0 -1
  253. package/dist/coordination/ifleet-manager.d.ts.map +0 -1
  254. package/dist/coordination/in-memory-transport.d.ts.map +0 -1
  255. package/dist/coordination/index.d.ts.map +0 -1
  256. package/dist/coordination/index.js.map +0 -7
  257. package/dist/coordination/knowledge-graph.d.ts.map +0 -1
  258. package/dist/coordination/large-answer-store.d.ts.map +0 -1
  259. package/dist/coordination/mail-tools.d.ts.map +0 -1
  260. package/dist/coordination/mailbox-actions.d.ts.map +0 -1
  261. package/dist/coordination/mailbox-auth-types.d.ts.map +0 -1
  262. package/dist/coordination/mailbox-codecs.d.ts.map +0 -1
  263. package/dist/coordination/mailbox-constants.d.ts.map +0 -1
  264. package/dist/coordination/mailbox-credential-store.d.ts.map +0 -1
  265. package/dist/coordination/mailbox-events.d.ts.map +0 -1
  266. package/dist/coordination/mailbox-health.d.ts.map +0 -1
  267. package/dist/coordination/mailbox-hooks.d.ts.map +0 -1
  268. package/dist/coordination/mailbox-http-auth.d.ts.map +0 -1
  269. package/dist/coordination/mailbox-http-rate-limit.d.ts.map +0 -1
  270. package/dist/coordination/mailbox-http-router.d.ts.map +0 -1
  271. package/dist/coordination/mailbox-http-validation.d.ts.map +0 -1
  272. package/dist/coordination/mailbox-message-codec.d.ts.map +0 -1
  273. package/dist/coordination/mailbox-parse-state.d.ts.map +0 -1
  274. package/dist/coordination/mailbox-project-server-client.d.ts.map +0 -1
  275. package/dist/coordination/mailbox-project-server-endpoint.d.ts.map +0 -1
  276. package/dist/coordination/mailbox-project-server-protocol.d.ts.map +0 -1
  277. package/dist/coordination/mailbox-project-server.d.ts.map +0 -1
  278. package/dist/coordination/mailbox-project-server.js.map +0 -7
  279. package/dist/coordination/mailbox-receipt-folding.d.ts.map +0 -1
  280. package/dist/coordination/mailbox-registry-codec.d.ts.map +0 -1
  281. package/dist/coordination/mailbox-retention-state.d.ts.map +0 -1
  282. package/dist/coordination/mailbox-status-mappers.d.ts.map +0 -1
  283. package/dist/coordination/mailbox-tool.d.ts.map +0 -1
  284. package/dist/coordination/mailbox-type-properties.d.ts.map +0 -1
  285. package/dist/coordination/mailbox-types.d.ts.map +0 -1
  286. package/dist/coordination/model-matrix.d.ts.map +0 -1
  287. package/dist/coordination/multi-agent-coordinator.d.ts.map +0 -1
  288. package/dist/coordination/multi-agent-timeout.d.ts.map +0 -1
  289. package/dist/coordination/null-fleet-bus.d.ts.map +0 -1
  290. package/dist/coordination/package-author-tracker.d.ts.map +0 -1
  291. package/dist/coordination/package-outdated-watcher.d.ts.map +0 -1
  292. package/dist/coordination/provider-status-tracker.d.ts.map +0 -1
  293. package/dist/coordination/remote-mailbox-credential-store.d.ts.map +0 -1
  294. package/dist/coordination/remote-mailbox.d.ts.map +0 -1
  295. package/dist/coordination/single-instance-mailbox.d.ts.map +0 -1
  296. package/dist/coordination/spawn-budget.d.ts.map +0 -1
  297. package/dist/coordination/sqlite-mailbox-compaction.d.ts.map +0 -1
  298. package/dist/coordination/sqlite-mailbox-credentials.d.ts.map +0 -1
  299. package/dist/coordination/sqlite-mailbox-rows.d.ts.map +0 -1
  300. package/dist/coordination/sqlite-mailbox-schema.d.ts.map +0 -1
  301. package/dist/coordination/sqlite-mailbox.d.ts.map +0 -1
  302. package/dist/coordination/subagent-budget.d.ts.map +0 -1
  303. package/dist/coordination/subagent-nicknames.d.ts.map +0 -1
  304. package/dist/coordination/subagent-result-tool.d.ts.map +0 -1
  305. package/dist/coordination/task-auctioneer.d.ts.map +0 -1
  306. package/dist/coordination/task-dag.d.ts.map +0 -1
  307. package/dist/coordination/techstack-mailbox-consumer.d.ts.map +0 -1
  308. package/dist/coordination/transport.d.ts.map +0 -1
  309. package/dist/coordination/worktree-task-runner.d.ts.map +0 -1
  310. package/dist/core/agent-internals.d.ts.map +0 -1
  311. package/dist/core/agent-loop.d.ts.map +0 -1
  312. package/dist/core/agent-response.d.ts.map +0 -1
  313. package/dist/core/agent-tools.d.ts.map +0 -1
  314. package/dist/core/agent-types.d.ts.map +0 -1
  315. package/dist/core/agent.d.ts.map +0 -1
  316. package/dist/core/btw.d.ts.map +0 -1
  317. package/dist/core/context.d.ts.map +0 -1
  318. package/dist/core/continue-intent.d.ts.map +0 -1
  319. package/dist/core/continue-to-next-iteration.d.ts.map +0 -1
  320. package/dist/core/conversation-state.d.ts.map +0 -1
  321. package/dist/core/fallback-model.d.ts.map +0 -1
  322. package/dist/core/fallback-profile-manager.d.ts.map +0 -1
  323. package/dist/core/fleet-pulse.d.ts.map +0 -1
  324. package/dist/core/index.d.ts.map +0 -1
  325. package/dist/core/index.js.map +0 -7
  326. package/dist/core/input-builder.d.ts.map +0 -1
  327. package/dist/core/instruction-bundle.d.ts.map +0 -1
  328. package/dist/core/iteration-limit.d.ts.map +0 -1
  329. package/dist/core/mailbox-loop.d.ts.map +0 -1
  330. package/dist/core/model-availability-calendar.d.ts.map +0 -1
  331. package/dist/core/model-ref.d.ts.map +0 -1
  332. package/dist/core/model-ref.js.map +0 -7
  333. package/dist/core/modes/brief.d.ts.map +0 -1
  334. package/dist/core/modes/default.d.ts.map +0 -1
  335. package/dist/core/modes/teach.d.ts.map +0 -1
  336. package/dist/core/provider-runner.d.ts.map +0 -1
  337. package/dist/core/queued-messages.d.ts.map +0 -1
  338. package/dist/core/request-provider-binding.d.ts.map +0 -1
  339. package/dist/core/run-env.d.ts.map +0 -1
  340. package/dist/core/streaming-response-builder.d.ts.map +0 -1
  341. package/dist/core/system-prompt-blocks.d.ts.map +0 -1
  342. package/dist/core/system-prompt-builder.d.ts.map +0 -1
  343. package/dist/core/system-prompt-environment-probes.d.ts.map +0 -1
  344. package/dist/core/system-prompt-environment.d.ts.map +0 -1
  345. package/dist/core/system-prompt-memory-skills.d.ts.map +0 -1
  346. package/dist/core/system-prompt-plan.d.ts.map +0 -1
  347. package/dist/core/system-prompt-shell.d.ts.map +0 -1
  348. package/dist/core/system-prompt-skill-bodies.d.ts.map +0 -1
  349. package/dist/core/system-prompt-skill-text.d.ts.map +0 -1
  350. package/dist/defaults/index.d.ts.map +0 -1
  351. package/dist/defaults/index.js.map +0 -7
  352. package/dist/design/index.d.ts.map +0 -1
  353. package/dist/design/index.js.map +0 -7
  354. package/dist/execution/auto-compaction-middleware.d.ts.map +0 -1
  355. package/dist/execution/autonomous-runner.d.ts.map +0 -1
  356. package/dist/execution/autonomy-brain.d.ts.map +0 -1
  357. package/dist/execution/autonomy-prompt-contributor.d.ts.map +0 -1
  358. package/dist/execution/brain-chain.d.ts.map +0 -1
  359. package/dist/execution/brain-circuit.d.ts.map +0 -1
  360. package/dist/execution/brain-evaluation.d.ts.map +0 -1
  361. package/dist/execution/brain-runtime-constants.d.ts.map +0 -1
  362. package/dist/execution/brain-runtime.d.ts.map +0 -1
  363. package/dist/execution/compaction-core.d.ts.map +0 -1
  364. package/dist/execution/compaction-scoring.d.ts.map +0 -1
  365. package/dist/execution/compaction-summary-cache.d.ts.map +0 -1
  366. package/dist/execution/compactor.d.ts.map +0 -1
  367. package/dist/execution/council-brain.d.ts.map +0 -1
  368. package/dist/execution/council-orchestrator.d.ts.map +0 -1
  369. package/dist/execution/council-personas.d.ts.map +0 -1
  370. package/dist/execution/council-profiles.d.ts.map +0 -1
  371. package/dist/execution/council-prompts.d.ts.map +0 -1
  372. package/dist/execution/council-resolution.d.ts.map +0 -1
  373. package/dist/execution/design-color.d.ts.map +0 -1
  374. package/dist/execution/design-detect.d.ts.map +0 -1
  375. package/dist/execution/design-kit-loader.d.ts.map +0 -1
  376. package/dist/execution/design-materialize.d.ts.map +0 -1
  377. package/dist/execution/design-project-store.d.ts.map +0 -1
  378. package/dist/execution/design-tune.d.ts.map +0 -1
  379. package/dist/execution/design-verify.d.ts.map +0 -1
  380. package/dist/execution/enhance-recovery.d.ts.map +0 -1
  381. package/dist/execution/error-handler.d.ts.map +0 -1
  382. package/dist/execution/eternal-autonomy-types.d.ts.map +0 -1
  383. package/dist/execution/eternal-autonomy.d.ts.map +0 -1
  384. package/dist/execution/goal-preamble.d.ts.map +0 -1
  385. package/dist/execution/index.d.ts.map +0 -1
  386. package/dist/execution/index.js.map +0 -7
  387. package/dist/execution/intelligent-compactor.d.ts.map +0 -1
  388. package/dist/execution/model-runtime.d.ts.map +0 -1
  389. package/dist/execution/one-shot-llm.d.ts.map +0 -1
  390. package/dist/execution/parallel-eternal-engine.d.ts.map +0 -1
  391. package/dist/execution/prompt-enhancer.d.ts.map +0 -1
  392. package/dist/execution/prompt-enhancer.js.map +0 -7
  393. package/dist/execution/prompt-loader.d.ts.map +0 -1
  394. package/dist/execution/provider-runner-impl.d.ts.map +0 -1
  395. package/dist/execution/regex-patterns.d.ts.map +0 -1
  396. package/dist/execution/retry-policy.d.ts.map +0 -1
  397. package/dist/execution/selective-compactor.d.ts.map +0 -1
  398. package/dist/execution/skill-loader.d.ts.map +0 -1
  399. package/dist/execution/strategy-compactor.d.ts.map +0 -1
  400. package/dist/execution/subagent-compaction.d.ts.map +0 -1
  401. package/dist/execution/tool-executor-logging.d.ts.map +0 -1
  402. package/dist/execution/tool-executor-results.d.ts.map +0 -1
  403. package/dist/execution/tool-executor-stream.d.ts.map +0 -1
  404. package/dist/execution/tool-executor-support.d.ts.map +0 -1
  405. package/dist/execution/tool-executor.d.ts.map +0 -1
  406. package/dist/extension/extension-points.d.ts.map +0 -1
  407. package/dist/extension/index.d.ts.map +0 -1
  408. package/dist/extension/index.js.map +0 -7
  409. package/dist/extension/registry.d.ts.map +0 -1
  410. package/dist/fleet-notifier.d.ts.map +0 -1
  411. package/dist/goal/checkpoint.d.ts.map +0 -1
  412. package/dist/goal/goal-assessor.d.ts.map +0 -1
  413. package/dist/goal/goal-planner.d.ts.map +0 -1
  414. package/dist/goal/goal-runner.d.ts.map +0 -1
  415. package/dist/goal/index.d.ts.map +0 -1
  416. package/dist/goal/index.js.map +0 -7
  417. package/dist/goal/phase-board-mutations.d.ts.map +0 -1
  418. package/dist/goal/phase-graph-builder.d.ts.map +0 -1
  419. package/dist/goal/phase-orchestrator-integration.d.ts.map +0 -1
  420. package/dist/goal/phase-orchestrator-queries.d.ts.map +0 -1
  421. package/dist/goal/phase-orchestrator-runtime.d.ts.map +0 -1
  422. package/dist/goal/phase-orchestrator-types.d.ts.map +0 -1
  423. package/dist/goal/phase-orchestrator.d.ts.map +0 -1
  424. package/dist/goal/phase-store.d.ts.map +0 -1
  425. package/dist/goal/types.d.ts.map +0 -1
  426. package/dist/hooks/http-executor.d.ts.map +0 -1
  427. package/dist/hooks/index.d.ts.map +0 -1
  428. package/dist/hooks/index.js.map +0 -7
  429. package/dist/hooks/registry.d.ts.map +0 -1
  430. package/dist/hooks/runner.d.ts.map +0 -1
  431. package/dist/hooks/shell-executor.d.ts.map +0 -1
  432. package/dist/hooks/shell-hooks-equal.d.ts.map +0 -1
  433. package/dist/hq/alerts.d.ts.map +0 -1
  434. package/dist/hq/auth-audit.d.ts.map +0 -1
  435. package/dist/hq/auth-store.d.ts.map +0 -1
  436. package/dist/hq/bootstrap-store.d.ts.map +0 -1
  437. package/dist/hq/brain-bridge.d.ts.map +0 -1
  438. package/dist/hq/bridge-context.d.ts.map +0 -1
  439. package/dist/hq/commands.d.ts.map +0 -1
  440. package/dist/hq/cost-bridge.d.ts.map +0 -1
  441. package/dist/hq/exposure.d.ts.map +0 -1
  442. package/dist/hq/factory.d.ts.map +0 -1
  443. package/dist/hq/fleet-bridge.d.ts.map +0 -1
  444. package/dist/hq/index.d.ts.map +0 -1
  445. package/dist/hq/index.js.map +0 -7
  446. package/dist/hq/kanban-store.d.ts.map +0 -1
  447. package/dist/hq/mailbox-mapper.d.ts.map +0 -1
  448. package/dist/hq/persistence/event-log.d.ts.map +0 -1
  449. package/dist/hq/persistence/jsonl-io.d.ts.map +0 -1
  450. package/dist/hq/persistence/simple-log.d.ts.map +0 -1
  451. package/dist/hq/persistence/snapshot-store.d.ts.map +0 -1
  452. package/dist/hq/persistence/timeseries-store.d.ts.map +0 -1
  453. package/dist/hq/persistence.d.ts.map +0 -1
  454. package/dist/hq/protocol/brain.d.ts.map +0 -1
  455. package/dist/hq/protocol/browser.d.ts.map +0 -1
  456. package/dist/hq/protocol/client.d.ts.map +0 -1
  457. package/dist/hq/protocol/core.d.ts.map +0 -1
  458. package/dist/hq/protocol/fleet.d.ts.map +0 -1
  459. package/dist/hq/protocol/kanban.d.ts.map +0 -1
  460. package/dist/hq/protocol/mailbox.d.ts.map +0 -1
  461. package/dist/hq/protocol/mcp.d.ts.map +0 -1
  462. package/dist/hq/protocol/project.d.ts.map +0 -1
  463. package/dist/hq/protocol/session.d.ts.map +0 -1
  464. package/dist/hq/protocol/tool.d.ts.map +0 -1
  465. package/dist/hq/protocol.d.ts.map +0 -1
  466. package/dist/hq/publisher.d.ts.map +0 -1
  467. package/dist/hq/redaction.d.ts.map +0 -1
  468. package/dist/hq/session-bridge.d.ts.map +0 -1
  469. package/dist/hq/tool-bridge.d.ts.map +0 -1
  470. package/dist/hq/transcript-mapper.d.ts.map +0 -1
  471. package/dist/hq/worktree-bridge.d.ts.map +0 -1
  472. package/dist/hq/write-queues.d.ts.map +0 -1
  473. package/dist/index.d.ts.map +0 -1
  474. package/dist/index.js.map +0 -7
  475. package/dist/infrastructure/context-manager.d.ts.map +0 -1
  476. package/dist/infrastructure/index.d.ts.map +0 -1
  477. package/dist/infrastructure/index.js.map +0 -7
  478. package/dist/infrastructure/logger.d.ts.map +0 -1
  479. package/dist/infrastructure/mcp-servers.d.ts.map +0 -1
  480. package/dist/infrastructure/path-resolver.d.ts.map +0 -1
  481. package/dist/infrastructure/provider-cache-ledger.d.ts.map +0 -1
  482. package/dist/infrastructure/token-counter.d.ts.map +0 -1
  483. package/dist/kernel/container.d.ts.map +0 -1
  484. package/dist/kernel/events/agent-events.d.ts.map +0 -1
  485. package/dist/kernel/events/brain-events.d.ts.map +0 -1
  486. package/dist/kernel/events/file-events.d.ts.map +0 -1
  487. package/dist/kernel/events/fleet-events.d.ts.map +0 -1
  488. package/dist/kernel/events/memory-events.d.ts.map +0 -1
  489. package/dist/kernel/events/network-events.d.ts.map +0 -1
  490. package/dist/kernel/events/process-events.d.ts.map +0 -1
  491. package/dist/kernel/events/provider-events.d.ts.map +0 -1
  492. package/dist/kernel/events/sdd-events.d.ts.map +0 -1
  493. package/dist/kernel/events/session-events.d.ts.map +0 -1
  494. package/dist/kernel/events/tool-events.d.ts.map +0 -1
  495. package/dist/kernel/events/worktree-events.d.ts.map +0 -1
  496. package/dist/kernel/events.d.ts.map +0 -1
  497. package/dist/kernel/index.d.ts.map +0 -1
  498. package/dist/kernel/index.js.map +0 -7
  499. package/dist/kernel/pipeline.d.ts.map +0 -1
  500. package/dist/kernel/run-controller.d.ts.map +0 -1
  501. package/dist/kernel/tokens.d.ts.map +0 -1
  502. package/dist/mailbox-attach.d.ts.map +0 -1
  503. package/dist/middleware/collab-pause.d.ts.map +0 -1
  504. package/dist/models/alibaba-token-plan-catalog.d.ts.map +0 -1
  505. package/dist/models/codex-catalog.d.ts.map +0 -1
  506. package/dist/models/index.d.ts.map +0 -1
  507. package/dist/models/index.js.map +0 -7
  508. package/dist/models/llm-selector.d.ts.map +0 -1
  509. package/dist/models/mode-store.d.ts.map +0 -1
  510. package/dist/models/model-intelligence.d.ts.map +0 -1
  511. package/dist/models/model-router.d.ts.map +0 -1
  512. package/dist/models/models-registry.d.ts.map +0 -1
  513. package/dist/models/provider-model-resolve.d.ts.map +0 -1
  514. package/dist/notifications/index.d.ts.map +0 -1
  515. package/dist/notifications/index.js.map +0 -7
  516. package/dist/notifications/notifier-impl.d.ts.map +0 -1
  517. package/dist/notifications/notifier.d.ts.map +0 -1
  518. package/dist/notifications/types.d.ts.map +0 -1
  519. package/dist/observability/event-bridge.d.ts.map +0 -1
  520. package/dist/observability/health.d.ts.map +0 -1
  521. package/dist/observability/index.d.ts.map +0 -1
  522. package/dist/observability/index.js.map +0 -7
  523. package/dist/observability/metrics.d.ts.map +0 -1
  524. package/dist/observability/network-telemetry.d.ts.map +0 -1
  525. package/dist/observability/otel-tracer.d.ts.map +0 -1
  526. package/dist/observability/otlp-metrics.d.ts.map +0 -1
  527. package/dist/observability/otlp-traces.d.ts.map +0 -1
  528. package/dist/observability/process-telemetry.d.ts.map +0 -1
  529. package/dist/observability/prometheus.d.ts.map +0 -1
  530. package/dist/observability/redact-command.d.ts.map +0 -1
  531. package/dist/observability/tracer.d.ts.map +0 -1
  532. package/dist/plugin/api.d.ts.map +0 -1
  533. package/dist/plugin/config.d.ts.map +0 -1
  534. package/dist/plugin/index.d.ts.map +0 -1
  535. package/dist/plugin/index.js.map +0 -7
  536. package/dist/plugin/loader.d.ts.map +0 -1
  537. package/dist/plugins/auto-review-plugin.d.ts.map +0 -1
  538. package/dist/plugins/chimera-plugin.d.ts.map +0 -1
  539. package/dist/plugins/prompts-plugin.d.ts.map +0 -1
  540. package/dist/plugins/review-claim-registry.d.ts.map +0 -1
  541. package/dist/plugins/review-context-builder.d.ts.map +0 -1
  542. package/dist/plugins/review-finding-commands.d.ts.map +0 -1
  543. package/dist/plugins/review-finding-integration.d.ts.map +0 -1
  544. package/dist/plugins/review-finding-parser.d.ts.map +0 -1
  545. package/dist/plugins/review-finding-store.d.ts.map +0 -1
  546. package/dist/plugins/review-finding-types.d.ts.map +0 -1
  547. package/dist/plugins/review-report-integration.d.ts.map +0 -1
  548. package/dist/plugins/review-report-store.d.ts.map +0 -1
  549. package/dist/plugins/review-report-types.d.ts.map +0 -1
  550. package/dist/plugins/review-types.d.ts.map +0 -1
  551. package/dist/plugins/skills-plugin.d.ts.map +0 -1
  552. package/dist/plugins/sync-plugin.d.ts.map +0 -1
  553. package/dist/prompts/index.d.ts.map +0 -1
  554. package/dist/prompts/prompt-installer.d.ts.map +0 -1
  555. package/dist/prompts/prompt-manifest-store.d.ts.map +0 -1
  556. package/dist/registry/index.d.ts.map +0 -1
  557. package/dist/registry/index.js.map +0 -7
  558. package/dist/registry/provider-registry.d.ts.map +0 -1
  559. package/dist/registry/slash-command-registry.d.ts.map +0 -1
  560. package/dist/registry/tool-registry.d.ts.map +0 -1
  561. package/dist/replay/hash.d.ts.map +0 -1
  562. package/dist/replay/index.d.ts.map +0 -1
  563. package/dist/replay/index.js.map +0 -7
  564. package/dist/replay/replay-provider-runner.d.ts.map +0 -1
  565. package/dist/security/capabilities.d.ts.map +0 -1
  566. package/dist/security/config-secrets.d.ts.map +0 -1
  567. package/dist/security/directory-permission-policy.d.ts.map +0 -1
  568. package/dist/security/directory-policy-schema.d.ts.map +0 -1
  569. package/dist/security/index.d.ts.map +0 -1
  570. package/dist/security/index.js.map +0 -7
  571. package/dist/security/kanban-boundary.d.ts.map +0 -1
  572. package/dist/security/permission-policy-schema.d.ts.map +0 -1
  573. package/dist/security/permission-policy.d.ts.map +0 -1
  574. package/dist/security/readonly-permission-policy.d.ts.map +0 -1
  575. package/dist/security/secret-scrubber.d.ts.map +0 -1
  576. package/dist/security/secret-vault.d.ts.map +0 -1
  577. package/dist/security/trust-boundary.d.ts.map +0 -1
  578. package/dist/security/yolo-risk.d.ts.map +0 -1
  579. package/dist/session-registry-atomic-file.d.ts.map +0 -1
  580. package/dist/session-registry-types.d.ts.map +0 -1
  581. package/dist/session-registry.d.ts.map +0 -1
  582. package/dist/skills/foreign-sources.d.ts.map +0 -1
  583. package/dist/skills/frontmatter.d.ts.map +0 -1
  584. package/dist/skills/github-fetcher.d.ts.map +0 -1
  585. package/dist/skills/index.d.ts.map +0 -1
  586. package/dist/skills/index.js.map +0 -7
  587. package/dist/skills/limits.d.ts.map +0 -1
  588. package/dist/skills/manifest-store.d.ts.map +0 -1
  589. package/dist/skills/registry/github-direct-adapter.d.ts.map +0 -1
  590. package/dist/skills/registry/registry-adapter.d.ts.map +0 -1
  591. package/dist/skills/registry/skills-sh-adapter.d.ts.map +0 -1
  592. package/dist/skills/skill-generator.d.ts.map +0 -1
  593. package/dist/skills/skill-installer.d.ts.map +0 -1
  594. package/dist/storage/annotations-store.d.ts.map +0 -1
  595. package/dist/storage/attachment-store.d.ts.map +0 -1
  596. package/dist/storage/cloud-sync.d.ts.map +0 -1
  597. package/dist/storage/completed-work-checkpoint.d.ts.map +0 -1
  598. package/dist/storage/config-loader/bootstrap.d.ts.map +0 -1
  599. package/dist/storage/config-loader/default-repair.d.ts.map +0 -1
  600. package/dist/storage/config-loader/defaults.d.ts.map +0 -1
  601. package/dist/storage/config-loader/diagnostics.d.ts.map +0 -1
  602. package/dist/storage/config-loader/env-overrides.d.ts.map +0 -1
  603. package/dist/storage/config-loader/in-project-policy.d.ts.map +0 -1
  604. package/dist/storage/config-loader/inline-provider-models.d.ts.map +0 -1
  605. package/dist/storage/config-loader/legacy-sage-migration.d.ts.map +0 -1
  606. package/dist/storage/config-loader/path-identity.d.ts.map +0 -1
  607. package/dist/storage/config-loader/types.d.ts.map +0 -1
  608. package/dist/storage/config-loader/validation.d.ts.map +0 -1
  609. package/dist/storage/config-loader.d.ts.map +0 -1
  610. package/dist/storage/config-migration.d.ts.map +0 -1
  611. package/dist/storage/config-store.d.ts.map +0 -1
  612. package/dist/storage/director-state.d.ts.map +0 -1
  613. package/dist/storage/file-session-writer.d.ts.map +0 -1
  614. package/dist/storage/goal-coordination.d.ts.map +0 -1
  615. package/dist/storage/goal-kanban.d.ts.map +0 -1
  616. package/dist/storage/goal-store.d.ts.map +0 -1
  617. package/dist/storage/index.d.ts.map +0 -1
  618. package/dist/storage/index.js.map +0 -7
  619. package/dist/storage/input-history-store.d.ts.map +0 -1
  620. package/dist/storage/memory-backend.d.ts.map +0 -1
  621. package/dist/storage/memory-consolidator.d.ts.map +0 -1
  622. package/dist/storage/memory-graph-backend.d.ts.map +0 -1
  623. package/dist/storage/plan-store.d.ts.map +0 -1
  624. package/dist/storage/plan-templates.d.ts.map +0 -1
  625. package/dist/storage/prompt-store.d.ts.map +0 -1
  626. package/dist/storage/prompt-usage-store.d.ts.map +0 -1
  627. package/dist/storage/provider-config-watcher.d.ts.map +0 -1
  628. package/dist/storage/queue-store.d.ts.map +0 -1
  629. package/dist/storage/recovery-lock.d.ts.map +0 -1
  630. package/dist/storage/replay-log-store.d.ts.map +0 -1
  631. package/dist/storage/session-analyzer.d.ts.map +0 -1
  632. package/dist/storage/session-checkpoint-cas.d.ts.map +0 -1
  633. package/dist/storage/session-event-bridge.d.ts.map +0 -1
  634. package/dist/storage/session-helpers.d.ts.map +0 -1
  635. package/dist/storage/session-id-resolver.d.ts.map +0 -1
  636. package/dist/storage/session-id.d.ts.map +0 -1
  637. package/dist/storage/session-read-scrubber.d.ts.map +0 -1
  638. package/dist/storage/session-reader.d.ts.map +0 -1
  639. package/dist/storage/session-recovery.d.ts.map +0 -1
  640. package/dist/storage/session-resume-validation.d.ts.map +0 -1
  641. package/dist/storage/session-rewind-apply.d.ts.map +0 -1
  642. package/dist/storage/session-rewinder.d.ts.map +0 -1
  643. package/dist/storage/session-store/delete-session-artifacts.d.ts.map +0 -1
  644. package/dist/storage/session-store/delete-session-guards.d.ts.map +0 -1
  645. package/dist/storage/session-store/directory-scan.d.ts.map +0 -1
  646. package/dist/storage/session-store/directory-session-files.d.ts.map +0 -1
  647. package/dist/storage/session-store/events.d.ts.map +0 -1
  648. package/dist/storage/session-store/fork-session.d.ts.map +0 -1
  649. package/dist/storage/session-store/index-reader.d.ts.map +0 -1
  650. package/dist/storage/session-store/load-cache.d.ts.map +0 -1
  651. package/dist/storage/session-store/load-session-data.d.ts.map +0 -1
  652. package/dist/storage/session-store/paths.d.ts.map +0 -1
  653. package/dist/storage/session-store/prune-helpers.d.ts.map +0 -1
  654. package/dist/storage/session-store/replay.d.ts.map +0 -1
  655. package/dist/storage/session-store/search-events.d.ts.map +0 -1
  656. package/dist/storage/session-store/shard-manifest.d.ts.map +0 -1
  657. package/dist/storage/session-store/summary-builder.d.ts.map +0 -1
  658. package/dist/storage/session-store/summary-header.d.ts.map +0 -1
  659. package/dist/storage/session-store/types.d.ts.map +0 -1
  660. package/dist/storage/session-store.d.ts.map +0 -1
  661. package/dist/storage/session-summary.d.ts.map +0 -1
  662. package/dist/storage/session-tool-call-ends.d.ts.map +0 -1
  663. package/dist/storage/session-writer-scrubber.d.ts.map +0 -1
  664. package/dist/storage/session-writer-truncate.d.ts.map +0 -1
  665. package/dist/storage/storage-concurrency.d.ts.map +0 -1
  666. package/dist/storage/task-store.d.ts.map +0 -1
  667. package/dist/storage/todos-checkpoint.d.ts.map +0 -1
  668. package/dist/storage/tool-audit-log.d.ts.map +0 -1
  669. package/dist/tasking/index.d.ts.map +0 -1
  670. package/dist/tasking/index.js.map +0 -7
  671. package/dist/tasking/task-store.d.ts.map +0 -1
  672. package/dist/tasking/task-tracker.d.ts.map +0 -1
  673. package/dist/tools/council-tool.d.ts.map +0 -1
  674. package/dist/tools/fallback-chain-manage-tool.d.ts.map +0 -1
  675. package/dist/tools/fallback-favorite-manage-tool.d.ts.map +0 -1
  676. package/dist/tools/fallback-manage-helpers.d.ts.map +0 -1
  677. package/dist/tools/fallback-manage-tool-options.d.ts.map +0 -1
  678. package/dist/tools/fallback-manage-tools.d.ts.map +0 -1
  679. package/dist/tools/fallback-model-ref-parse.d.ts.map +0 -1
  680. package/dist/tools/fallback-provider-key-store.d.ts.map +0 -1
  681. package/dist/tools/fallback-system-config-view-tool.d.ts.map +0 -1
  682. package/dist/tools/index.d.ts.map +0 -1
  683. package/dist/tools/index.js.map +0 -7
  684. package/dist/tools/mcp-control.d.ts.map +0 -1
  685. package/dist/tools/mcp-use.d.ts.map +0 -1
  686. package/dist/tools/one-shot-llm-tool.d.ts.map +0 -1
  687. package/dist/tools/plugin-manager.d.ts.map +0 -1
  688. package/dist/types/agent-bridge.d.ts.map +0 -1
  689. package/dist/types/attachment.d.ts.map +0 -1
  690. package/dist/types/autonomy.d.ts.map +0 -1
  691. package/dist/types/blocks.d.ts.map +0 -1
  692. package/dist/types/compactor.d.ts.map +0 -1
  693. package/dist/types/config/autonomy.d.ts.map +0 -1
  694. package/dist/types/config/context.d.ts.map +0 -1
  695. package/dist/types/config/fleet-chat.d.ts.map +0 -1
  696. package/dist/types/config/mcp-features.d.ts.map +0 -1
  697. package/dist/types/config/providers.d.ts.map +0 -1
  698. package/dist/types/config/root.d.ts.map +0 -1
  699. package/dist/types/config/runtime.d.ts.map +0 -1
  700. package/dist/types/config/skills-fleet-brain.d.ts.map +0 -1
  701. package/dist/types/config/tools.d.ts.map +0 -1
  702. package/dist/types/config.d.ts.map +0 -1
  703. package/dist/types/context-evidence.d.ts.map +0 -1
  704. package/dist/types/context-window.d.ts.map +0 -1
  705. package/dist/types/council.d.ts.map +0 -1
  706. package/dist/types/default-config.d.ts.map +0 -1
  707. package/dist/types/design-kit.d.ts.map +0 -1
  708. package/dist/types/error-handler.d.ts.map +0 -1
  709. package/dist/types/errors.d.ts.map +0 -1
  710. package/dist/types/file-event-record.d.ts.map +0 -1
  711. package/dist/types/hooks.d.ts.map +0 -1
  712. package/dist/types/index.d.ts.map +0 -1
  713. package/dist/types/index.js.map +0 -7
  714. package/dist/types/input-reader.d.ts.map +0 -1
  715. package/dist/types/logger.d.ts.map +0 -1
  716. package/dist/types/memory.d.ts.map +0 -1
  717. package/dist/types/messages.d.ts.map +0 -1
  718. package/dist/types/mode-prompts.d.ts.map +0 -1
  719. package/dist/types/mode.d.ts.map +0 -1
  720. package/dist/types/models-registry.d.ts.map +0 -1
  721. package/dist/types/multi-agent.d.ts.map +0 -1
  722. package/dist/types/observability.d.ts.map +0 -1
  723. package/dist/types/one-shot-llm.d.ts.map +0 -1
  724. package/dist/types/path-resolver.d.ts.map +0 -1
  725. package/dist/types/permission.d.ts.map +0 -1
  726. package/dist/types/plugin.d.ts.map +0 -1
  727. package/dist/types/prompt-registry.d.ts.map +0 -1
  728. package/dist/types/prompt.d.ts.map +0 -1
  729. package/dist/types/provider-runner.d.ts.map +0 -1
  730. package/dist/types/provider.d.ts.map +0 -1
  731. package/dist/types/quota-regex.d.ts.map +0 -1
  732. package/dist/types/renderer.d.ts.map +0 -1
  733. package/dist/types/retry-policy.d.ts.map +0 -1
  734. package/dist/types/secret-scrubber.d.ts.map +0 -1
  735. package/dist/types/secret-vault.d.ts.map +0 -1
  736. package/dist/types/selector.d.ts.map +0 -1
  737. package/dist/types/session-markers.d.ts.map +0 -1
  738. package/dist/types/session-markers.js.map +0 -7
  739. package/dist/types/session-reader.d.ts.map +0 -1
  740. package/dist/types/session-rewinder.d.ts.map +0 -1
  741. package/dist/types/session.d.ts.map +0 -1
  742. package/dist/types/side-effect.d.ts.map +0 -1
  743. package/dist/types/skill.d.ts.map +0 -1
  744. package/dist/types/slash-command.d.ts.map +0 -1
  745. package/dist/types/spec.d.ts.map +0 -1
  746. package/dist/types/system-prompt-contributor.d.ts.map +0 -1
  747. package/dist/types/system-prompt.d.ts.map +0 -1
  748. package/dist/types/task-graph.d.ts.map +0 -1
  749. package/dist/types/token-counter.d.ts.map +0 -1
  750. package/dist/types/tool-executor.d.ts.map +0 -1
  751. package/dist/types/tool-markers.d.ts.map +0 -1
  752. package/dist/types/tool.d.ts.map +0 -1
  753. package/dist/types/utility-types.d.ts.map +0 -1
  754. package/dist/utils/assert-never.d.ts.map +0 -1
  755. package/dist/utils/atomic-write.d.ts.map +0 -1
  756. package/dist/utils/cache-key.d.ts.map +0 -1
  757. package/dist/utils/child-env.d.ts.map +0 -1
  758. package/dist/utils/color.d.ts.map +0 -1
  759. package/dist/utils/compaction-preview.d.ts.map +0 -1
  760. package/dist/utils/config-backup.d.ts.map +0 -1
  761. package/dist/utils/config-json.d.ts.map +0 -1
  762. package/dist/utils/connectivity.d.ts.map +0 -1
  763. package/dist/utils/context-breakdown.d.ts.map +0 -1
  764. package/dist/utils/context-evidence.d.ts.map +0 -1
  765. package/dist/utils/crash-shield.d.ts.map +0 -1
  766. package/dist/utils/deep-merge.d.ts.map +0 -1
  767. package/dist/utils/diff.d.ts.map +0 -1
  768. package/dist/utils/error.d.ts.map +0 -1
  769. package/dist/utils/error.js.map +0 -7
  770. package/dist/utils/expect-defined.d.ts.map +0 -1
  771. package/dist/utils/expect-defined.js.map +0 -7
  772. package/dist/utils/glob-expand.d.ts.map +0 -1
  773. package/dist/utils/glob-match.d.ts.map +0 -1
  774. package/dist/utils/heap-watchdog.d.ts.map +0 -1
  775. package/dist/utils/heap-watchdog.js.map +0 -7
  776. package/dist/utils/incoming-images.d.ts.map +0 -1
  777. package/dist/utils/index.d.ts.map +0 -1
  778. package/dist/utils/index.js.map +0 -7
  779. package/dist/utils/instruction-file.d.ts.map +0 -1
  780. package/dist/utils/ip-guard.d.ts.map +0 -1
  781. package/dist/utils/json-repair.d.ts.map +0 -1
  782. package/dist/utils/json-schema-validate.d.ts.map +0 -1
  783. package/dist/utils/lru-cache.d.ts.map +0 -1
  784. package/dist/utils/merge-custom-models.d.ts.map +0 -1
  785. package/dist/utils/merge-models-payload.d.ts.map +0 -1
  786. package/dist/utils/message-invariants.d.ts.map +0 -1
  787. package/dist/utils/newline-normalize.d.ts.map +0 -1
  788. package/dist/utils/perf-profile.d.ts.map +0 -1
  789. package/dist/utils/pid.d.ts.map +0 -1
  790. package/dist/utils/project-identity.d.ts.map +0 -1
  791. package/dist/utils/project-watch.d.ts.map +0 -1
  792. package/dist/utils/regex-guard.d.ts.map +0 -1
  793. package/dist/utils/safe-json.d.ts.map +0 -1
  794. package/dist/utils/sage-output-block.d.ts.map +0 -1
  795. package/dist/utils/session-scoped-path.d.ts.map +0 -1
  796. package/dist/utils/sleep.d.ts.map +0 -1
  797. package/dist/utils/slug.d.ts.map +0 -1
  798. package/dist/utils/socket-path.d.ts.map +0 -1
  799. package/dist/utils/sqlite-warning.d.ts.map +0 -1
  800. package/dist/utils/string.d.ts.map +0 -1
  801. package/dist/utils/task-format.d.ts.map +0 -1
  802. package/dist/utils/term.d.ts.map +0 -1
  803. package/dist/utils/todos-format.d.ts.map +0 -1
  804. package/dist/utils/token-estimate.d.ts.map +0 -1
  805. package/dist/utils/tool-description-mode.d.ts.map +0 -1
  806. package/dist/utils/tool-output-serializer.d.ts.map +0 -1
  807. package/dist/utils/tool-result-render-mode.d.ts.map +0 -1
  808. package/dist/utils/tool-subject.d.ts.map +0 -1
  809. package/dist/utils/tool-wire-compact.d.ts.map +0 -1
  810. package/dist/utils/tree-kill.d.ts.map +0 -1
  811. package/dist/utils/tree-kill.js.map +0 -7
  812. package/dist/utils/ulid.d.ts.map +0 -1
  813. package/dist/utils/walk-ignore.d.ts.map +0 -1
  814. package/dist/utils/wstack-paths.d.ts.map +0 -1
  815. package/dist/worktree/index.d.ts.map +0 -1
  816. package/dist/worktree/index.js.map +0 -7
  817. package/dist/worktree/worktree-git.d.ts.map +0 -1
  818. package/dist/worktree/worktree-managed-ops.d.ts.map +0 -1
  819. package/dist/worktree/worktree-manager.d.ts.map +0 -1
  820. package/dist/worktree/worktree-types.d.ts.map +0 -1
@@ -2,67 +2,80 @@
2
2
  import * as fsp12 from "node:fs/promises";
3
3
  import * as path13 from "node:path";
4
4
 
5
+ // src/security/file-permissions.ts
6
+ var SECRET_FILE_MODE = 384;
7
+
5
8
  // src/security/secret-scrubber.ts
6
9
  var PATTERNS = [
7
10
  // Anchored at the start where possible so partial matches inside larger
8
11
  // strings don't trigger false positives.
9
12
  {
10
13
  type: "anthropic_key",
11
- regex: /(?<![A-Za-z0-9])sk-ant-api\d+-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9])/g
14
+ regex: /(?<![A-Za-z0-9])sk-ant-api\d+-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9])/g,
15
+ anchor: "sk-ant-"
12
16
  },
13
- { type: "openai_key", regex: /(?<![A-Za-z0-9])sk-(?:proj-)?[A-Za-z0-9_-]{20,}(?![A-Za-z0-9])/g },
14
- { type: "github_pat", regex: /(?<![A-Za-z0-9])ghp_[A-Za-z0-9]{36,}(?![A-Za-z0-9])/g },
15
- { type: "github_pat_v2", regex: /(?<![A-Za-z0-9])github_pat_[A-Za-z0-9_]{50,}(?![A-Za-z0-9])/g },
16
- { type: "aws_access_key", regex: /(?<![A-Za-z0-9])AKIA[0-9A-Z]{16}(?![A-Za-z0-9])/g },
17
- { type: "gcp_key", regex: /(?<![A-Za-z0-9])AIza[0-9A-Za-z_-]{35}(?![A-Za-z0-9])/g },
18
- { type: "slack_token", regex: /(?<![A-Za-z0-9-])xox[abpos]-[A-Za-z0-9-]{10,}(?![A-Za-z0-9-])/g },
17
+ { type: "openai_key", regex: /(?<![A-Za-z0-9])sk-(?:proj-)?[A-Za-z0-9_-]{20,}(?![A-Za-z0-9])/g, anchor: "sk-" },
18
+ { type: "github_pat", regex: /(?<![A-Za-z0-9])ghp_[A-Za-z0-9]{36,}(?![A-Za-z0-9])/g, anchor: "ghp_" },
19
+ { type: "github_pat_v2", regex: /(?<![A-Za-z0-9])github_pat_[A-Za-z0-9_]{50,}(?![A-Za-z0-9])/g, anchor: "github_pat_" },
20
+ { type: "aws_access_key", regex: /(?<![A-Za-z0-9])AKIA[0-9A-Z]{16}(?![A-Za-z0-9])/g, anchor: "AKIA" },
21
+ { type: "gcp_key", regex: /(?<![A-Za-z0-9])AIza[0-9A-Za-z_-]{35}(?![A-Za-z0-9])/g, anchor: "AIza" },
22
+ { type: "slack_token", regex: /(?<![A-Za-z0-9-])xox[abpos]-[A-Za-z0-9-]{10,}(?![A-Za-z0-9-])/g, anchor: "xox" },
19
23
  {
20
24
  type: "stripe_key",
21
- regex: /(?<![A-Za-z0-9])sk_(?:live|test)_[A-Za-z0-9]{24,}(?![A-Za-z0-9])/g
25
+ regex: /(?<![A-Za-z0-9])sk_(?:live|test)_[A-Za-z0-9]{24,}(?![A-Za-z0-9])/g,
26
+ anchor: "sk_"
22
27
  },
23
28
  {
24
29
  type: "twilio_sid",
25
- regex: /(?<![A-Za-z0-9])AC[a-f0-9]{32}(?![A-Za-z0-9])/g
30
+ regex: /(?<![A-Za-z0-9])AC[a-f0-9]{32}(?![A-Za-z0-9])/g,
31
+ anchor: "AC"
26
32
  },
27
33
  {
28
34
  type: "telegram_bot_token",
29
35
  // Telegram tokens are of the form bot<digits>:<alphanum> in URL paths
30
- regex: /\/bot\d+:[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g
36
+ regex: /\/bot\d+:[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
37
+ anchor: "/bot"
31
38
  },
32
39
  {
33
40
  type: "jwt",
34
41
  // Anchored: look for literal "eyJ" which is unambiguous for JWT header
35
- regex: /(?<![A-Za-z0-9/+=])eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}(?![A-Za-z0-9/+=])/g
42
+ regex: /(?<![A-Za-z0-9/+=])eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}(?![A-Za-z0-9/+=])/g,
43
+ anchor: "eyJ"
36
44
  },
37
45
  {
38
46
  type: "private_key",
39
47
  // Anchored: start must be BEGIN, end must be END with no extra dashes after END
40
- regex: /(?:^|\n)-----BEGIN (?:RSA|EC|OPENSSH|DSA|PGP)? ?PRIVATE KEY-----[\s\S]*?-----END[^-]*-----(?:\n|$)/g
48
+ regex: /(?:^|\n)-----BEGIN (?:RSA|EC|OPENSSH|DSA|PGP)? ?PRIVATE KEY-----[\s\S]*?-----END[^-]*-----(?:\n|$)/g,
49
+ anchor: "-----BEGIN"
41
50
  },
42
- { type: "mongodb_uri", regex: /mongodb(?:\+srv)?:\/\/[^\s"'`]+/g },
43
- { type: "postgres_uri", regex: /postgres(?:ql)?:\/\/[^\s"'`]+/g },
44
- { type: "mysql_uri", regex: /mysql:\/\/[^\s"'`]+/g },
45
- { type: "redis_uri", regex: /redis:\/\/[^\s"'`]+/g },
51
+ { type: "mongodb_uri", regex: /mongodb(?:\+srv)?:\/\/[^\s"'`]+/g, anchor: "mongodb" },
52
+ { type: "postgres_uri", regex: /postgres(?:ql)?:\/\/[^\s"'`]+/g, anchor: "postgres" },
53
+ { type: "mysql_uri", regex: /mysql:\/\/[^\s"'`]+/g, anchor: "mysql://" },
54
+ { type: "redis_uri", regex: /redis:\/\/[^\s"'`]+/g, anchor: "redis://" },
46
55
  // AI/ML provider keys — modern LLM services with well-known prefixes
47
56
  {
48
57
  type: "huggingface_token",
49
58
  // HuggingFace tokens: hf_ followed by 34 alphanumeric chars
50
- regex: /(?<![A-Za-z0-9])hf_[A-Za-z0-9]{34}(?![A-Za-z0-9])/g
59
+ regex: /(?<![A-Za-z0-9])hf_[A-Za-z0-9]{34}(?![A-Za-z0-9])/g,
60
+ anchor: "hf_"
51
61
  },
52
62
  {
53
63
  type: "replicate_token",
54
64
  // Replicate tokens: r8_ followed by 40+ alphanumeric chars
55
- regex: /(?<![A-Za-z0-9])r8_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g
65
+ regex: /(?<![A-Za-z0-9])r8_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g,
66
+ anchor: "r8_"
56
67
  },
57
68
  {
58
69
  type: "perplexity_key",
59
70
  // Perplexity API keys: pplx- followed by 40+ alphanumeric chars
60
- regex: /(?<![A-Za-z0-9])pplx-[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g
71
+ regex: /(?<![A-Za-z0-9])pplx-[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g,
72
+ anchor: "pplx-"
61
73
  },
62
74
  {
63
75
  type: "groq_key",
64
76
  // Groq API keys: gsk_ followed by 40+ alphanumeric chars
65
- regex: /(?<![A-Za-z0-9])gsk_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g
77
+ regex: /(?<![A-Za-z0-9])gsk_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g,
78
+ anchor: "gsk_"
66
79
  },
67
80
  {
68
81
  type: "bearer_token",
@@ -75,7 +88,8 @@ var PATTERNS = [
75
88
  // tokens sharing a single delimiter (`Bearer a… Bearer b…`) must both be
76
89
  // redacted. A consuming trailing delimiter would eat the separator the
77
90
  // next match needs for its leading anchor, leaking the second token.
78
- regex: /(?:^|[^A-Za-z0-9_.~+/-])Bearer\s+[A-Za-z0-9._~+/-]{12,512}=*(?=$|[^A-Za-z0-9_.~+/-])/g
91
+ regex: /(?:^|[^A-Za-z0-9_.~+/-])Bearer\s+[A-Za-z0-9._~+/-]{12,512}=*(?=$|[^A-Za-z0-9_.~+/-])/g,
92
+ anchor: "Bearer"
79
93
  },
80
94
  {
81
95
  type: "high_entropy_env",
@@ -91,7 +105,89 @@ var PATTERNS = [
91
105
  // replacement so the separator between adjacent secrets is preserved
92
106
  // rather than collapsed. Capture groups are therefore: 1=leading
93
107
  // delimiter, 2=key name, 3=value.
94
- regex: /(^|\s)([A-Z_]{4,}(?:KEY|TOKEN|SECRET|PASSWORD|PWD))\s*[:=]\s*['"]?([A-Za-z0-9_/+=-]{20,512})['"]?(?=\s|$)/g
108
+ regex: /(^|\s)([A-Z_]{4,}(?:KEY|TOKEN|SECRET|PASSWORD|PWD))\s*[:=]\s*['"]?([A-Za-z0-9_/+=-]{20,512})['"]?(?=\s|$)/g,
109
+ anchor: ["KEY", "TOKEN", "SECRET", "PASSWORD", "PWD"]
110
+ },
111
+ // ── Ported from packages/plugins credential-patterns.ts (WS-034) ─────────
112
+ // The plugin runtime carried 37 patterns while this scrubber — the one that
113
+ // guards session JSONL, chronicle, HQ broadcast, WebUI events and the auth
114
+ // audit — carried 22. The plugin side already had a parity test; it just did
115
+ // not cover core. Most consequential: WrongStack mints `gho_` tokens itself
116
+ // in the Copilot OAuth flow, and `gh[ousr]_` was absent here.
117
+ {
118
+ type: "github_oauth_token",
119
+ regex: /(?<![A-Za-z0-9])gh[ousr]_[A-Za-z0-9]{36,}(?![A-Za-z0-9])/g,
120
+ anchor: ["gho_", "ghu_", "ghs_", "ghr_"]
121
+ },
122
+ {
123
+ type: "gitlab_pat",
124
+ regex: /(?<![A-Za-z0-9])glpat-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
125
+ anchor: "glpat-"
126
+ },
127
+ {
128
+ type: "gitlab_runner_token",
129
+ regex: /(?<![A-Za-z0-9])glrt-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
130
+ anchor: "glrt-"
131
+ },
132
+ {
133
+ type: "npm_token",
134
+ regex: /(?<![A-Za-z0-9])npm_[A-Za-z0-9]{36}(?![A-Za-z0-9])/g,
135
+ anchor: "npm_"
136
+ },
137
+ {
138
+ type: "slack_app_token",
139
+ regex: /(?<![A-Za-z0-9-])xapp-\d-[A-Za-z0-9-]{10,}(?![A-Za-z0-9-])/g,
140
+ anchor: "xapp-"
141
+ },
142
+ {
143
+ type: "slack_webhook",
144
+ regex: /https:\/\/hooks\.slack\.com\/services\/T[A-Za-z0-9_-]+\/B[A-Za-z0-9_-]+\/[A-Za-z0-9]{16,}/g,
145
+ anchor: "hooks.slack.com"
146
+ },
147
+ {
148
+ type: "sendgrid_key",
149
+ regex: /(?<![A-Za-z0-9])SG\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
150
+ anchor: "SG."
151
+ },
152
+ {
153
+ type: "digitalocean_token",
154
+ regex: /(?<![A-Za-z0-9])dop_v1_[a-f0-9]{64}(?![A-Za-z0-9])/g,
155
+ anchor: "dop_v1_"
156
+ },
157
+ {
158
+ type: "doppler_token",
159
+ regex: /(?<![A-Za-z0-9])dp\.(?:pt|st|sa|scim|audit)\.[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g,
160
+ anchor: "dp."
161
+ },
162
+ {
163
+ type: "shopify_token",
164
+ regex: /(?<![A-Za-z0-9])shp(?:at|ca|pa|ss)_[a-fA-F0-9]{32}(?![A-Za-z0-9])/g,
165
+ anchor: "shp"
166
+ },
167
+ {
168
+ type: "docker_pat",
169
+ regex: /(?<![A-Za-z0-9])dckr_pat_[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
170
+ anchor: "dckr_pat_"
171
+ },
172
+ {
173
+ type: "linear_key",
174
+ regex: /(?<![A-Za-z0-9])lin_api_[A-Za-z0-9]{40,}(?![A-Za-z0-9])/g,
175
+ anchor: "lin_api_"
176
+ },
177
+ {
178
+ type: "atlassian_token",
179
+ regex: /(?<![A-Za-z0-9])ATATT3[A-Za-z0-9_\-=]{40,}(?![A-Za-z0-9_\-=])/g,
180
+ anchor: "ATATT3"
181
+ },
182
+ {
183
+ type: "square_token",
184
+ regex: /(?<![A-Za-z0-9])(?:sq0(?:atp|csp)-|EAAA)[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
185
+ anchor: ["sq0atp-", "sq0csp-", "EAAA"]
186
+ },
187
+ {
188
+ type: "google_oauth_client_secret",
189
+ regex: /(?<![A-Za-z0-9_-])GOCSPX-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g,
190
+ anchor: "GOCSPX-"
95
191
  }
96
192
  ];
97
193
  var SIMPLE_PATTERNS = PATTERNS.filter((p) => p.type !== "high_entropy_env");
@@ -100,32 +196,32 @@ var HIGH_ENTROPY_REGEX = PATTERNS.find((p) => p.type === "high_entropy_env").reg
100
196
  var COMBINED_REPLACEMENTS = SIMPLE_PATTERNS.map((p) => `[REDACTED:${p.type}]`);
101
197
  var SCRUB_CHUNK_BYTES = 64 * 1024;
102
198
  var SCRUB_OVERLAP_BYTES = 1024;
199
+ var PATTERN_ANCHORS = [
200
+ ...new Set(
201
+ PATTERNS.flatMap(
202
+ (pattern) => typeof pattern.anchor === "string" ? [pattern.anchor] : [...pattern.anchor]
203
+ )
204
+ )
205
+ ];
206
+ var JSON_KEY_ANCHORS = [
207
+ '"apiKey"',
208
+ '"api_key"',
209
+ '"token"',
210
+ '"secret"',
211
+ '"password"',
212
+ '"authorization"',
213
+ '"bearer"',
214
+ '"private_key"',
215
+ '"access_token"',
216
+ '"refresh_token"',
217
+ '"client_secret"'
218
+ ];
219
+ var ALL_ANCHORS = [...PATTERN_ANCHORS, ...JSON_KEY_ANCHORS];
103
220
  function hasCredentialAnchors(text) {
104
- return text.includes("-----BEGIN") || // Private keys (most unique → cheap reject)
105
- text.includes("sk-") || // Anthropic + OpenAI keys
106
- text.includes("sk_") || // Stripe live/test keys
107
- text.includes("ghp_") || // GitHub PAT v1
108
- text.includes("github_pat_") || // GitHub PAT v2
109
- text.includes("eyJ") || // JWT
110
- text.includes("AKIA") || // AWS access key
111
- text.includes("AIza") || // GCP service key
112
- text.includes("xox") || // Slack token (xoxa/xoxb/xoxp/xoxo/xoxs)
113
- text.includes("Bearer ") || // Bearer token (space suffix reduces false positives)
114
- text.includes("/bot") || // Telegram bot token (URL path pattern)
115
- text.includes("hf_") || // HuggingFace token
116
- text.includes("r8_") || // Replicate token
117
- text.includes("pplx-") || // Perplexity API key
118
- text.includes("gsk_") || // Groq API key
119
- text.includes("_KEY=") || // High-entropy env vars: API_KEY=, SECRET_KEY=, ...
120
- text.includes("_TOKEN=") || // ACCESS_TOKEN=, AUTH_TOKEN=, ...
121
- text.includes("_SECRET=") || // API_SECRET=, CLIENT_SECRET=, ...
122
- text.includes("_PASSWORD=") || // DB_PASSWORD=, ROOT_PASSWORD=, ...
123
- text.includes("mongodb://") || text.includes("mongodb+srv://") || text.includes("postgres://") || text.includes("postgresql://") || text.includes("mysql://") || text.includes("redis://") || // JSON-style credential keys: tool outputs often serialise objects as
124
- // raw JSON strings rather than parsed objects. The value may not start
125
- // with a recognisable prefix (sk-, ghp_, etc.), so we anchor on common
126
- // key names. The `"` prefix avoids matching prose that happens to mention
127
- // these words — JSON serialisation always quotes string keys.
128
- text.includes('"apiKey"') || text.includes('"api_key"') || text.includes('"token"') || text.includes('"secret"') || text.includes('"password"') || text.includes('"authorization"') || text.includes('"bearer"') || text.includes('"private_key"') || text.includes('"access_token"') || text.includes('"refresh_token"') || text.includes('"client_secret"');
221
+ for (const anchor of ALL_ANCHORS) {
222
+ if (text.includes(anchor)) return true;
223
+ }
224
+ return false;
129
225
  }
130
226
  var DefaultSecretScrubber = class {
131
227
  scrub(text) {
@@ -388,6 +484,11 @@ var SECRET_NAME_PARTS = [
388
484
  "SECRET",
389
485
  "PASSWORD",
390
486
  "PASSWD",
487
+ // WRONGSTACK_VAULT_PASSPHRASE is the vault KEK. Without this entry it matched
488
+ // no rule here and fell through to the `WRONGSTACK_` prefix allow-rule, so the
489
+ // key that protects the credential vault was forwarded to every child process
490
+ // — including the agent's own exec/bash tools and MCP stdio servers (WS-033).
491
+ "PASSPHRASE",
391
492
  "AUTH",
392
493
  "CRED",
393
494
  "BEARER",
@@ -558,6 +659,65 @@ async function backupConfigFile(filePath, paths) {
558
659
  }
559
660
  }
560
661
 
662
+ // src/utils/deep-merge.ts
663
+ var FORBIDDEN_PROTO_KEYS = /* @__PURE__ */ new Set([
664
+ "__proto__",
665
+ "constructor",
666
+ "prototype",
667
+ "__defineGetter__",
668
+ "__defineSetter__",
669
+ "__lookupGetter__",
670
+ "__lookupSetter__"
671
+ ]);
672
+ function isPrimitiveArray(a) {
673
+ return a.every((v) => v === null || typeof v !== "object" && typeof v !== "function");
674
+ }
675
+ function deepMerge(base, patch, options = {}) {
676
+ const {
677
+ conflictResolution = "prefer-patch",
678
+ arrayMode = "replace",
679
+ protectProto = true,
680
+ onNonPrimitiveArrayReplace
681
+ } = options;
682
+ if (typeof base !== "object" || base === null) {
683
+ return conflictResolution === "prefer-patch" ? patch : base;
684
+ }
685
+ if (typeof patch !== "object" || patch === null) {
686
+ return conflictResolution === "prefer-patch" ? patch : base;
687
+ }
688
+ if (Array.isArray(base) && Array.isArray(patch)) {
689
+ if (arrayMode === "concat-primitives" && isPrimitiveArray(base) && isPrimitiveArray(patch)) {
690
+ return [.../* @__PURE__ */ new Set([...base, ...patch])];
691
+ }
692
+ return conflictResolution === "prefer-patch" ? patch : base;
693
+ }
694
+ if (Array.isArray(base) || Array.isArray(patch)) {
695
+ return conflictResolution === "prefer-patch" ? patch : base;
696
+ }
697
+ const baseObj = base;
698
+ const patchObj = patch;
699
+ const out = { ...baseObj };
700
+ for (const [k, v] of Object.entries(patchObj)) {
701
+ if (protectProto && FORBIDDEN_PROTO_KEYS.has(k)) continue;
702
+ const existing = out[k];
703
+ if (v !== null && typeof v === "object" && !Array.isArray(v) && existing !== null && typeof existing === "object" && !Array.isArray(existing)) {
704
+ out[k] = deepMerge(existing, v, options);
705
+ } else if (Array.isArray(v) && Array.isArray(existing)) {
706
+ if (onNonPrimitiveArrayReplace && !isPrimitiveArray(v)) {
707
+ onNonPrimitiveArrayReplace(k, existing.length, v.length);
708
+ }
709
+ out[k] = deepMerge(existing, v, options);
710
+ } else if (v !== void 0) {
711
+ if (onNonPrimitiveArrayReplace && Array.isArray(v) && !isPrimitiveArray(v)) {
712
+ const existingLen = Array.isArray(existing) ? existing.length : 0;
713
+ onNonPrimitiveArrayReplace(k, existingLen, v.length);
714
+ }
715
+ out[k] = v;
716
+ }
717
+ }
718
+ return out;
719
+ }
720
+
561
721
  // src/utils/expect-defined.ts
562
722
  function expectDefined(value, label) {
563
723
  if (value === null || value === void 0) {
@@ -677,68 +837,137 @@ function isEmptyMessage(msg) {
677
837
  // src/types/config/runtime.ts
678
838
  var DEFAULT_TUI_THINKING_WORD = "thinking";
679
839
 
680
- // src/utils/deep-merge.ts
681
- var FORBIDDEN_PROTO_KEYS = /* @__PURE__ */ new Set([
682
- "__proto__",
683
- "constructor",
684
- "prototype",
685
- "__defineGetter__",
686
- "__defineSetter__",
687
- "__lookupGetter__",
688
- "__lookupSetter__"
689
- ]);
690
- function isPrimitiveArray(a) {
691
- return a.every((v) => v === null || typeof v !== "object" && typeof v !== "function");
692
- }
693
- function deepMerge(base, patch, options = {}) {
694
- const {
695
- conflictResolution = "prefer-patch",
696
- arrayMode = "replace",
697
- protectProto = true,
698
- onNonPrimitiveArrayReplace
699
- } = options;
700
- if (typeof base !== "object" || base === null) {
701
- return conflictResolution === "prefer-patch" ? patch : base;
702
- }
703
- if (typeof patch !== "object" || patch === null) {
704
- return conflictResolution === "prefer-patch" ? patch : base;
705
- }
706
- if (Array.isArray(base) && Array.isArray(patch)) {
707
- if (arrayMode === "concat-primitives" && isPrimitiveArray(base) && isPrimitiveArray(patch)) {
708
- return [.../* @__PURE__ */ new Set([...base, ...patch])];
709
- }
710
- return conflictResolution === "prefer-patch" ? patch : base;
711
- }
712
- if (Array.isArray(base) || Array.isArray(patch)) {
713
- return conflictResolution === "prefer-patch" ? patch : base;
840
+ // src/utils/wstack-paths.ts
841
+ import { createHash } from "node:crypto";
842
+ import * as fs2 from "node:fs";
843
+ import * as os from "node:os";
844
+ import * as path2 from "node:path";
845
+ function canonicalProjectRoot(absRoot) {
846
+ const checkoutRoot = path2.resolve(absRoot);
847
+ const dotGit = path2.join(checkoutRoot, ".git");
848
+ try {
849
+ if (!fs2.statSync(dotGit).isFile()) return checkoutRoot;
850
+ const gitDirLine = fs2.readFileSync(dotGit, "utf8").trim();
851
+ const match = /^gitdir:\s*(.+)$/i.exec(gitDirLine);
852
+ if (!match?.[1]) return checkoutRoot;
853
+ const gitDir = path2.resolve(checkoutRoot, match[1].trim());
854
+ const commonDirFile = path2.join(gitDir, "commondir");
855
+ if (!fs2.statSync(commonDirFile).isFile()) return checkoutRoot;
856
+ const commonDir = path2.resolve(gitDir, fs2.readFileSync(commonDirFile, "utf8").trim());
857
+ if (path2.basename(commonDir).toLowerCase() !== ".git") return checkoutRoot;
858
+ return path2.dirname(commonDir);
859
+ } catch {
860
+ return checkoutRoot;
714
861
  }
715
- const baseObj = base;
716
- const patchObj = patch;
717
- const out = { ...baseObj };
718
- for (const [k, v] of Object.entries(patchObj)) {
719
- if (protectProto && FORBIDDEN_PROTO_KEYS.has(k)) continue;
720
- const existing = out[k];
721
- if (v !== null && typeof v === "object" && !Array.isArray(v) && existing !== null && typeof existing === "object" && !Array.isArray(existing)) {
722
- out[k] = deepMerge(existing, v, options);
723
- } else if (Array.isArray(v) && Array.isArray(existing)) {
724
- if (onNonPrimitiveArrayReplace && !isPrimitiveArray(v)) {
725
- onNonPrimitiveArrayReplace(k, existing.length, v.length);
726
- }
727
- out[k] = deepMerge(existing, v, options);
728
- } else if (v !== void 0) {
729
- if (onNonPrimitiveArrayReplace && Array.isArray(v) && !isPrimitiveArray(v)) {
730
- const existingLen = Array.isArray(existing) ? existing.length : 0;
731
- onNonPrimitiveArrayReplace(k, existingLen, v.length);
732
- }
733
- out[k] = v;
734
- }
862
+ }
863
+ function projectHash(absRoot) {
864
+ return createHash("sha256").update(canonicalProjectRoot(absRoot)).digest("hex").slice(0, 12);
865
+ }
866
+ function projectSlug(absRoot) {
867
+ const identityRoot = canonicalProjectRoot(absRoot);
868
+ const base = slugify(path2.basename(identityRoot));
869
+ const hash = createHash("sha256").update(identityRoot).digest("hex").slice(0, 6);
870
+ return `${base}-${hash}`;
871
+ }
872
+ function slugify(name) {
873
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
874
+ }
875
+ function safeProfileName(name) {
876
+ const safe = (name ?? "").replace(/[/\\:]/g, "_").replace(/\.\./g, "_").trim();
877
+ return safe || "default";
878
+ }
879
+ function activeProfileName(globalRoot) {
880
+ try {
881
+ const parsed = JSON.parse(fs2.readFileSync(path2.join(globalRoot, "config.json"), "utf8"));
882
+ return safeProfileName(typeof parsed.activeProfile === "string" ? parsed.activeProfile : void 0);
883
+ } catch {
884
+ return "default";
735
885
  }
736
- return out;
886
+ }
887
+ function wstackGlobalRoot() {
888
+ const fromEnv = process.env["WRONGSTACK_HOME"];
889
+ if (fromEnv && fromEnv.trim().length > 0) return path2.resolve(fromEnv);
890
+ return path2.join(os.homedir(), ".wrongstack");
891
+ }
892
+ function resolveWstackPaths(opts) {
893
+ const globalRoot = opts.globalRoot ?? (opts.userHome ? path2.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
894
+ const homeDir = opts.userHome ?? os.homedir();
895
+ const profileName = safeProfileName(opts.profileName ?? activeProfileName(globalRoot));
896
+ const profileDir = path2.join(globalRoot, "profiles", profileName);
897
+ const hash = projectHash(opts.projectRoot);
898
+ const slug = projectSlug(opts.projectRoot);
899
+ const projectDir = path2.join(globalRoot, "projects", slug);
900
+ return {
901
+ globalRoot,
902
+ profileName,
903
+ profileDir,
904
+ projectRoot: opts.projectRoot,
905
+ homeDir,
906
+ configDir: profileDir,
907
+ globalConfig: path2.join(globalRoot, "config.json"),
908
+ profilesDir: path2.join(globalRoot, "profiles"),
909
+ profileConfig: (name) => {
910
+ return path2.join(globalRoot, "profiles", safeProfileName(name), "config.json");
911
+ },
912
+ profileStatuslineConfig: (name) => {
913
+ return path2.join(globalRoot, "profiles", safeProfileName(name), "statusline.json");
914
+ },
915
+ profileModeConfig: (name) => {
916
+ return path2.join(globalRoot, "profiles", safeProfileName(name), "mode.json");
917
+ },
918
+ profileProviderStatus: (name) => {
919
+ return path2.join(globalRoot, "profiles", safeProfileName(name), "provider-status.json");
920
+ },
921
+ profileUpdateCache: (name) => {
922
+ return path2.join(globalRoot, "profiles", safeProfileName(name), "update-cache.json");
923
+ },
924
+ secretsKey: path2.join(globalRoot, ".key"),
925
+ globalMemory: path2.join(profileDir, "memory.md"),
926
+ globalSkills: path2.join(profileDir, "skills"),
927
+ globalClaudeSkills: path2.join(homeDir, ".claude", "skills"),
928
+ globalDesignKits: path2.join(profileDir, "design-kits"),
929
+ globalPrompts: path2.join(profileDir, "prompts"),
930
+ globalInstructions: path2.join(profileDir, "instructions"),
931
+ promptUsage: path2.join(profileDir, "prompt-usage.json"),
932
+ cacheDir: path2.join(globalRoot, "cache"),
933
+ modelsCache: path2.join(globalRoot, "cache", "models.dev.json"),
934
+ modelsOverlayCache: path2.join(globalRoot, "cache", "models-overlay.json"),
935
+ historyFile: path2.join(profileDir, "history"),
936
+ logFile: path2.join(globalRoot, "logs", "wrongstack.log"),
937
+ projectDir,
938
+ projectCodebaseIndex: path2.join(projectDir, "codebase-index"),
939
+ projectMemory: path2.join(projectDir, "memory.md"),
940
+ projectSessions: path2.join(projectDir, "sessions"),
941
+ projectTrust: path2.join(projectDir, "trust.json"),
942
+ projectMeta: path2.join(projectDir, "meta.json"),
943
+ projectLocalConfig: path2.join(projectDir, "config.local.json"),
944
+ inProjectConfig: path2.join(opts.projectRoot, ".wrongstack", "config.json"),
945
+ inProjectAgentsFile: path2.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
946
+ inProjectSkills: path2.join(opts.projectRoot, ".wrongstack", "skills"),
947
+ inProjectClaudeSkills: path2.join(opts.projectRoot, ".claude", "skills"),
948
+ inProjectPrompts: path2.join(opts.projectRoot, ".wrongstack", "prompts"),
949
+ inProjectInstructions: path2.join(opts.projectRoot, ".wrongstack", "instructions"),
950
+ inProjectDesignKits: path2.join(opts.projectRoot, ".wrongstack", "design-kits"),
951
+ inProjectWorktrees: path2.join(opts.projectRoot, ".wrongstack", "worktrees"),
952
+ projectHash: hash,
953
+ projectSlug: slug,
954
+ projectGoal: path2.join(projectDir, "goal.json"),
955
+ projectInputHistory: path2.join(projectDir, "input-history.json"),
956
+ projectSpecs: path2.join(projectDir, "specs"),
957
+ projectTaskGraphs: path2.join(projectDir, "task-graphs"),
958
+ projectSddSession: path2.join(projectDir, "sdd-session.json"),
959
+ projectPlan: path2.join(projectDir, "plan.json"),
960
+ projectAutophase: path2.join(projectDir, "autophase"),
961
+ projectSddBoards: path2.join(projectDir, "sdd-boards"),
962
+ syncConfig: path2.join(profileDir, "sync.json"),
963
+ configHistoryDir: path2.join(globalRoot, "config-history"),
964
+ projectStatus: (projectHash2) => path2.join(globalRoot, "projects", projectHash2, "status.json")
965
+ };
737
966
  }
738
967
 
739
968
  // src/utils/instruction-file.ts
740
- import { readFileSync, statSync } from "node:fs";
741
- import * as path2 from "node:path";
969
+ import { readFileSync as readFileSync2, statSync as statSync2 } from "node:fs";
970
+ import * as path3 from "node:path";
742
971
  import { fileURLToPath } from "node:url";
743
972
  var textCache = /* @__PURE__ */ new Map();
744
973
  var rootCandidates;
@@ -748,7 +977,7 @@ function readBundledInstructionText(relativePath) {
748
977
  let resolved = "";
749
978
  for (const root of instructionRootCandidates()) {
750
979
  try {
751
- resolved = readFileSync(path2.join(root, relativePath), "utf8").trimEnd();
980
+ resolved = readFileSync2(path3.join(root, relativePath), "utf8").trimEnd();
752
981
  break;
753
982
  } catch {
754
983
  }
@@ -764,18 +993,18 @@ function renderInstructionTemplate(template, values) {
764
993
  }
765
994
  function instructionRootCandidates() {
766
995
  if (rootCandidates !== void 0) return rootCandidates;
767
- const here = path2.dirname(fileURLToPath(import.meta.url));
996
+ const here = path3.dirname(fileURLToPath(import.meta.url));
768
997
  const candidates = [
769
- path2.resolve(here, "../../instructions"),
770
- path2.resolve(here, "../instructions"),
771
- path2.resolve(here, "instructions")
998
+ path3.resolve(here, "../../instructions"),
999
+ path3.resolve(here, "../instructions"),
1000
+ path3.resolve(here, "instructions")
772
1001
  ];
773
1002
  rootCandidates = candidates.sort((a, b) => Number(!isDirectory(a)) - Number(!isDirectory(b)));
774
1003
  return rootCandidates;
775
1004
  }
776
1005
  function isDirectory(candidate) {
777
1006
  try {
778
- return statSync(candidate).isDirectory();
1007
+ return statSync2(candidate).isDirectory();
779
1008
  } catch {
780
1009
  return false;
781
1010
  }
@@ -875,14 +1104,14 @@ function safeParse(input, maxBytes = 5e6) {
875
1104
  }
876
1105
 
877
1106
  // src/utils/session-scoped-path.ts
878
- import * as path3 from "node:path";
1107
+ import * as path4 from "node:path";
879
1108
  function sessionScopedPath(dir, sessionId, suffix) {
880
1109
  if (!sessionId || sessionId.includes("\\") || sessionId.includes("..")) {
881
1110
  throw invalid(sessionId);
882
1111
  }
883
- const resolved = path3.resolve(dir, `${sessionId}${suffix}`);
884
- const rel = path3.relative(path3.resolve(dir), resolved);
885
- if (rel.startsWith("..") || path3.isAbsolute(rel)) {
1112
+ const resolved = path4.resolve(dir, `${sessionId}${suffix}`);
1113
+ const rel = path4.relative(path4.resolve(dir), resolved);
1114
+ if (rel.startsWith("..") || path4.isAbsolute(rel)) {
886
1115
  throw invalid(sessionId);
887
1116
  }
888
1117
  return resolved;
@@ -897,144 +1126,33 @@ function invalid(sessionId) {
897
1126
  }
898
1127
 
899
1128
  // src/utils/slug.ts
900
- function slugify(name, fallback = "prompt", maxLen = 64) {
1129
+ function slugify2(name, fallback = "prompt", maxLen = 64) {
901
1130
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, maxLen).replace(/-+$/g, "") || fallback;
902
1131
  }
903
1132
 
904
- // src/utils/wstack-paths.ts
905
- import { createHash } from "node:crypto";
906
- import * as fs2 from "node:fs";
907
- import * as os from "node:os";
908
- import * as path4 from "node:path";
909
- function canonicalProjectRoot(absRoot) {
910
- const checkoutRoot = path4.resolve(absRoot);
911
- const dotGit = path4.join(checkoutRoot, ".git");
912
- try {
913
- if (!fs2.statSync(dotGit).isFile()) return checkoutRoot;
914
- const gitDirLine = fs2.readFileSync(dotGit, "utf8").trim();
915
- const match = /^gitdir:\s*(.+)$/i.exec(gitDirLine);
916
- if (!match?.[1]) return checkoutRoot;
917
- const gitDir = path4.resolve(checkoutRoot, match[1].trim());
918
- const commonDirFile = path4.join(gitDir, "commondir");
919
- if (!fs2.statSync(commonDirFile).isFile()) return checkoutRoot;
920
- const commonDir = path4.resolve(gitDir, fs2.readFileSync(commonDirFile, "utf8").trim());
921
- if (path4.basename(commonDir).toLowerCase() !== ".git") return checkoutRoot;
922
- return path4.dirname(commonDir);
923
- } catch {
924
- return checkoutRoot;
1133
+ // src/storage/file-session-writer.ts
1134
+ import { closeSync, createReadStream, fsyncSync, openSync, writeSync } from "node:fs";
1135
+ import * as fsp2 from "node:fs/promises";
1136
+ import * as path5 from "node:path";
1137
+ import { createInterface } from "node:readline";
1138
+
1139
+ // src/storage/session-workspace-checkpoints.ts
1140
+ function requiredCheckpointCas(checkpointCas, operation) {
1141
+ if (!checkpointCas) {
1142
+ throw new Error(`Workspace checkpoint ${operation} requires a projectRoot-aware session store`);
925
1143
  }
1144
+ return checkpointCas;
926
1145
  }
927
- function projectHash(absRoot) {
928
- return createHash("sha256").update(canonicalProjectRoot(absRoot)).digest("hex").slice(0, 12);
929
- }
930
- function projectSlug(absRoot) {
931
- const identityRoot = canonicalProjectRoot(absRoot);
932
- const base = slugify2(path4.basename(identityRoot));
933
- const hash = createHash("sha256").update(identityRoot).digest("hex").slice(0, 6);
934
- return `${base}-${hash}`;
935
- }
936
- function slugify2(name) {
937
- return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "project";
1146
+ function captureCheckpoint(checkpointCas, sessionId, promptIndex) {
1147
+ return requiredCheckpointCas(checkpointCas, "capture").capture(sessionId, promptIndex);
938
1148
  }
939
- function safeProfileName(name) {
940
- const safe = (name ?? "").replace(/[/\\:]/g, "_").replace(/\.\./g, "_").trim();
941
- return safe || "default";
942
- }
943
- function activeProfileName(globalRoot) {
944
- try {
945
- const parsed = JSON.parse(fs2.readFileSync(path4.join(globalRoot, "config.json"), "utf8"));
946
- return safeProfileName(typeof parsed.activeProfile === "string" ? parsed.activeProfile : void 0);
947
- } catch {
948
- return "default";
949
- }
950
- }
951
- function wstackGlobalRoot() {
952
- const fromEnv = process.env["WRONGSTACK_HOME"];
953
- if (fromEnv && fromEnv.trim().length > 0) return path4.resolve(fromEnv);
954
- return path4.join(os.homedir(), ".wrongstack");
955
- }
956
- function resolveWstackPaths(opts) {
957
- const globalRoot = opts.globalRoot ?? (opts.userHome ? path4.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
958
- const homeDir = opts.userHome ?? os.homedir();
959
- const profileName = safeProfileName(opts.profileName ?? activeProfileName(globalRoot));
960
- const profileDir = path4.join(globalRoot, "profiles", profileName);
961
- const hash = projectHash(opts.projectRoot);
962
- const slug = projectSlug(opts.projectRoot);
963
- const projectDir = path4.join(globalRoot, "projects", slug);
964
- return {
965
- globalRoot,
966
- profileName,
967
- profileDir,
968
- projectRoot: opts.projectRoot,
969
- homeDir,
970
- configDir: profileDir,
971
- globalConfig: path4.join(globalRoot, "config.json"),
972
- profilesDir: path4.join(globalRoot, "profiles"),
973
- profileConfig: (name) => {
974
- return path4.join(globalRoot, "profiles", safeProfileName(name), "config.json");
975
- },
976
- profileStatuslineConfig: (name) => {
977
- return path4.join(globalRoot, "profiles", safeProfileName(name), "statusline.json");
978
- },
979
- profileModeConfig: (name) => {
980
- return path4.join(globalRoot, "profiles", safeProfileName(name), "mode.json");
981
- },
982
- profileProviderStatus: (name) => {
983
- return path4.join(globalRoot, "profiles", safeProfileName(name), "provider-status.json");
984
- },
985
- profileUpdateCache: (name) => {
986
- return path4.join(globalRoot, "profiles", safeProfileName(name), "update-cache.json");
987
- },
988
- secretsKey: path4.join(globalRoot, ".key"),
989
- globalMemory: path4.join(profileDir, "memory.md"),
990
- globalSkills: path4.join(profileDir, "skills"),
991
- globalClaudeSkills: path4.join(homeDir, ".claude", "skills"),
992
- globalDesignKits: path4.join(profileDir, "design-kits"),
993
- globalPrompts: path4.join(profileDir, "prompts"),
994
- globalInstructions: path4.join(profileDir, "instructions"),
995
- promptUsage: path4.join(profileDir, "prompt-usage.json"),
996
- cacheDir: path4.join(globalRoot, "cache"),
997
- modelsCache: path4.join(globalRoot, "cache", "models.dev.json"),
998
- modelsOverlayCache: path4.join(globalRoot, "cache", "models-overlay.json"),
999
- historyFile: path4.join(profileDir, "history"),
1000
- logFile: path4.join(globalRoot, "logs", "wrongstack.log"),
1001
- projectDir,
1002
- projectCodebaseIndex: path4.join(projectDir, "codebase-index"),
1003
- projectMemory: path4.join(projectDir, "memory.md"),
1004
- projectSessions: path4.join(projectDir, "sessions"),
1005
- projectTrust: path4.join(projectDir, "trust.json"),
1006
- projectMeta: path4.join(projectDir, "meta.json"),
1007
- projectLocalConfig: path4.join(projectDir, "config.local.json"),
1008
- inProjectConfig: path4.join(opts.projectRoot, ".wrongstack", "config.json"),
1009
- inProjectAgentsFile: path4.join(opts.projectRoot, ".wrongstack", "AGENTS.md"),
1010
- inProjectSkills: path4.join(opts.projectRoot, ".wrongstack", "skills"),
1011
- inProjectClaudeSkills: path4.join(opts.projectRoot, ".claude", "skills"),
1012
- inProjectPrompts: path4.join(opts.projectRoot, ".wrongstack", "prompts"),
1013
- inProjectInstructions: path4.join(opts.projectRoot, ".wrongstack", "instructions"),
1014
- inProjectDesignKits: path4.join(opts.projectRoot, ".wrongstack", "design-kits"),
1015
- inProjectWorktrees: path4.join(opts.projectRoot, ".wrongstack", "worktrees"),
1016
- projectHash: hash,
1017
- projectSlug: slug,
1018
- projectGoal: path4.join(projectDir, "goal.json"),
1019
- projectInputHistory: path4.join(projectDir, "input-history.json"),
1020
- projectSpecs: path4.join(projectDir, "specs"),
1021
- projectTaskGraphs: path4.join(projectDir, "task-graphs"),
1022
- projectSddSession: path4.join(projectDir, "sdd-session.json"),
1023
- projectPlan: path4.join(projectDir, "plan.json"),
1024
- projectAutophase: path4.join(projectDir, "autophase"),
1025
- projectSddBoards: path4.join(projectDir, "sdd-boards"),
1026
- syncConfig: path4.join(profileDir, "sync.json"),
1027
- configHistoryDir: path4.join(globalRoot, "config-history"),
1028
- projectStatus: (projectHash2) => path4.join(globalRoot, "projects", projectHash2, "status.json")
1029
- };
1149
+ function materializeCheckpoint(checkpointCas, checkpoint, targetRoot) {
1150
+ return requiredCheckpointCas(checkpointCas, "materialization").materialize(
1151
+ checkpoint,
1152
+ targetRoot
1153
+ );
1030
1154
  }
1031
1155
 
1032
- // src/storage/file-session-writer.ts
1033
- import { closeSync, createReadStream, fsyncSync, openSync, writeSync } from "node:fs";
1034
- import * as fsp2 from "node:fs/promises";
1035
- import * as path5 from "node:path";
1036
- import { createInterface } from "node:readline";
1037
-
1038
1156
  // src/storage/session-helpers.ts
1039
1157
  function consumeControlString(value, start) {
1040
1158
  let index = start;
@@ -1259,6 +1377,16 @@ function scrubSessionWriterEvent(event, secretScrubber) {
1259
1377
  if (event.type === "llm_response") {
1260
1378
  return { ...event, content: secretScrubber.scrubObject(event.content) };
1261
1379
  }
1380
+ if (event.type === "file_snapshot") {
1381
+ return {
1382
+ ...event,
1383
+ files: event.files.map((f) => ({
1384
+ ...f,
1385
+ before: f.before !== null ? secretScrubber.scrub(f.before) : null,
1386
+ after: f.after !== null ? secretScrubber.scrub(f.after) : null
1387
+ }))
1388
+ };
1389
+ }
1262
1390
  return event;
1263
1391
  }
1264
1392
 
@@ -1275,6 +1403,8 @@ var FileSessionWriter = class _FileSessionWriter {
1275
1403
  this.filePath = opts.filePath ?? "";
1276
1404
  this.secretScrubber = opts.secretScrubber;
1277
1405
  this.checkpointCas = opts.checkpointCas;
1406
+ this._onAppend = opts.onAppend;
1407
+ this._onAppendBatch = opts.onAppendBatch;
1278
1408
  this.onCloseCb = opts.onClose;
1279
1409
  this.resolveNameCb = opts.resolveName;
1280
1410
  this.summary = opts.initialSummary ? { ...opts.initialSummary, id } : {
@@ -1330,12 +1460,44 @@ var FileSessionWriter = class _FileSessionWriter {
1330
1460
  lastAppendWarnAt = 0;
1331
1461
  secretScrubber;
1332
1462
  checkpointCas;
1463
+ /** Mutable slot for onAppend callback — constructor opts seed it, setOnAppend replaces it. */
1464
+ _onAppend;
1465
+ /** Implements SessionWriter.onAppend — public getter/setter for direct property access. */
1466
+ get onAppend() {
1467
+ return this._onAppend;
1468
+ }
1469
+ set onAppend(cb) {
1470
+ this._onAppend = cb;
1471
+ }
1472
+ /** Mutable slot for onAppendBatch callback. */
1473
+ _onAppendBatch;
1474
+ /** Implements SessionWriter.onAppendBatch — public getter/setter for direct property access. */
1475
+ get onAppendBatch() {
1476
+ return this._onAppendBatch;
1477
+ }
1478
+ set onAppendBatch(cb) {
1479
+ this._onAppendBatch = cb;
1480
+ }
1333
1481
  onCloseCb;
1334
1482
  resolveNameCb;
1335
1483
  /** Implements SessionWriter.traceId — propagated from ContextInit.traceId. */
1336
1484
  traceId;
1337
1485
  // ── Write buffer — batches events to reduce per-event disk I/O ──────────────
1338
1486
  //
1487
+ /**
1488
+ * Set or replace the onAppend callback. Used by telemetry bridges that
1489
+ * receive the writer as an already-constructed dependency.
1490
+ */
1491
+ setOnAppend(cb) {
1492
+ this._onAppend = cb;
1493
+ }
1494
+ /**
1495
+ * Set or replace the onAppendBatch callback.
1496
+ */
1497
+ setOnAppendBatch(cb) {
1498
+ this._onAppendBatch = cb;
1499
+ }
1500
+ //
1339
1501
  // Every append() pushes the scrubbed event into an in-memory buffer instead
1340
1502
  // of calling handle.appendFile() synchronously. The buffer flushes to disk
1341
1503
  // when it reaches FLUSH_SIZE events OR after FLUSH_INTERVAL_MS of inactivity.
@@ -1442,7 +1604,12 @@ var FileSessionWriter = class _FileSessionWriter {
1442
1604
  if (this.closed) return;
1443
1605
  void this.ensureInit();
1444
1606
  this.observeForSummary(event);
1445
- this.pushWriteBuffer(event);
1607
+ const appendEvent = event.type === "file_snapshot" ? scrubSessionWriterEvent(event, this.secretScrubber) : event;
1608
+ try {
1609
+ this._onAppend?.(appendEvent);
1610
+ } catch {
1611
+ }
1612
+ this.pushWriteBuffer(appendEvent);
1446
1613
  if (this.writeBuffer.length >= _FileSessionWriter.FLUSH_SIZE) {
1447
1614
  if (this.flushTimer) {
1448
1615
  clearTimeout(this.flushTimer);
@@ -1524,6 +1691,10 @@ var FileSessionWriter = class _FileSessionWriter {
1524
1691
  await this.ensureInit();
1525
1692
  const scrubbed = scrubSessionWriterEvent(event, this.secretScrubber);
1526
1693
  this.observeForSummary(scrubbed);
1694
+ try {
1695
+ this._onAppend?.(scrubbed);
1696
+ } catch {
1697
+ }
1527
1698
  this.pushWriteBuffer(scrubbed);
1528
1699
  if (this.writeBuffer.length >= _FileSessionWriter.FLUSH_SIZE) {
1529
1700
  if (this.flushTimer) {
@@ -1539,11 +1710,22 @@ var FileSessionWriter = class _FileSessionWriter {
1539
1710
  async appendBatch(events) {
1540
1711
  if (this.closed || events.length === 0) return;
1541
1712
  await this.ensureInit();
1713
+ const scrubbedBatch = [];
1542
1714
  for (const event of events) {
1543
1715
  const scrubbed = scrubSessionWriterEvent(event, this.secretScrubber);
1544
1716
  this.observeForSummary(scrubbed);
1717
+ try {
1718
+ this._onAppend?.(scrubbed);
1719
+ } catch {
1720
+ }
1545
1721
  this.pushWriteBuffer(scrubbed);
1722
+ scrubbedBatch.push(scrubbed);
1723
+ }
1724
+ try {
1725
+ this._onAppendBatch?.(scrubbedBatch);
1726
+ } catch {
1546
1727
  }
1728
+ ;
1547
1729
  if (this.writeBuffer.length >= _FileSessionWriter.FLUSH_SIZE) {
1548
1730
  if (this.flushTimer) {
1549
1731
  clearTimeout(this.flushTimer);
@@ -1946,7 +2128,8 @@ var FileSessionWriter = class _FileSessionWriter {
1946
2128
  promptIndex,
1947
2129
  promptPreview,
1948
2130
  ts: (/* @__PURE__ */ new Date()).toISOString(),
1949
- fileCount
2131
+ fileCount,
2132
+ ...workspaceCheckpoint ? { workspaceCheckpoint } : {}
1950
2133
  });
1951
2134
  }
1952
2135
  async writeFileSnapshot(promptIndex, files) {
@@ -2052,6 +2235,10 @@ var FileSessionWriter = class _FileSessionWriter {
2052
2235
  this.activePromptIndex = null;
2053
2236
  this.pendingFileSnapshots = [];
2054
2237
  this.pendingFileSnapshotBytes = 0;
2238
+ this.appendFailCount = 0;
2239
+ this.lastAppendWarnAt = 0;
2240
+ this.bufferOverflowCount = 0;
2241
+ this.lastBufferOverflowWarnAt = 0;
2055
2242
  }
2056
2243
  /**
2057
2244
  * Write an in-flight marker. The agent loop should call
@@ -2188,8 +2375,8 @@ var SessionCheckpointCas = class {
2188
2375
  return null;
2189
2376
  }
2190
2377
  try {
2191
- const stat16 = await fsp3.lstat(absolute);
2192
- if (stat16.isSymbolicLink()) {
2378
+ const stat15 = await fsp3.lstat(absolute);
2379
+ if (stat15.isSymbolicLink()) {
2193
2380
  const linkTarget = await fsp3.readlink(absolute);
2194
2381
  const resolvedLink = path6.resolve(path6.dirname(absolute), linkTarget);
2195
2382
  if (path6.isAbsolute(linkTarget) || !isInside(this.projectRoot, resolvedLink)) {
@@ -2201,29 +2388,29 @@ var SessionCheckpointCas = class {
2201
2388
  }
2202
2389
  return { path: relative8, state: "symlink", linkTarget };
2203
2390
  }
2204
- if (!stat16.isFile()) {
2391
+ if (!stat15.isFile()) {
2205
2392
  unresolved.push({ path: relative8, reason: "changed path is not a regular file" });
2206
2393
  return null;
2207
2394
  }
2208
- if (stat16.size > MAX_BLOB_BYTES) {
2395
+ if (stat15.size > MAX_BLOB_BYTES) {
2209
2396
  unresolved.push({
2210
2397
  path: relative8,
2211
2398
  reason: `file exceeds ${MAX_BLOB_BYTES}-byte checkpoint blob limit`
2212
2399
  });
2213
2400
  return null;
2214
2401
  }
2215
- if (scheduledBytes + stat16.size > MAX_CHECKPOINT_BYTES) {
2402
+ if (scheduledBytes + stat15.size > MAX_CHECKPOINT_BYTES) {
2216
2403
  unresolved.push({
2217
2404
  path: relative8,
2218
2405
  reason: `checkpoint exceeds ${MAX_CHECKPOINT_BYTES}-byte aggregate blob limit`
2219
2406
  });
2220
2407
  return null;
2221
2408
  }
2222
- scheduledBytes += stat16.size;
2409
+ scheduledBytes += stat15.size;
2223
2410
  const content = await fsp3.readFile(absolute);
2224
2411
  const blobHash = sha256(content);
2225
2412
  await this.putBlob(blobHash, content);
2226
- return { path: relative8, state: "file", blobHash, mode: stat16.mode & 511 };
2413
+ return { path: relative8, state: "file", blobHash, mode: stat15.mode & 511 };
2227
2414
  } catch (err) {
2228
2415
  if (err.code === "ENOENT") {
2229
2416
  return { path: relative8, state: "absent" };
@@ -2612,11 +2799,11 @@ async function validateOne(observation, lexicalRoot, realRoot) {
2612
2799
  };
2613
2800
  }
2614
2801
  try {
2615
- const stat16 = await fsp4.stat(realFile);
2616
- if (!stat16.isFile()) {
2802
+ const stat15 = await fsp4.stat(realFile);
2803
+ if (!stat15.isFile()) {
2617
2804
  return { ...base, status: "unreadable", detail: "Path is no longer a regular file." };
2618
2805
  }
2619
- if (stat16.size > MAX_REVALIDATE_BYTES) {
2806
+ if (stat15.size > MAX_REVALIDATE_BYTES) {
2620
2807
  return {
2621
2808
  ...base,
2622
2809
  status: "unreadable",
@@ -2674,57 +2861,82 @@ function formatInterruptedToolNotice(pendingToolUseCount) {
2674
2861
  ].join("\n");
2675
2862
  }
2676
2863
 
2677
- // src/storage/session-store/index-reader.ts
2864
+ // src/storage/session-store/delete-session-artifacts.ts
2678
2865
  import * as fsp5 from "node:fs/promises";
2679
- function applySessionIndexLines(raw, byId, deleted) {
2680
- for (const line of raw.split("\n")) {
2681
- if (!line.trim()) continue;
2682
- try {
2683
- const entry = JSON.parse(line);
2684
- if (entry.action === "delete" && entry.id) {
2685
- deleted.add(entry.id);
2686
- byId.delete(entry.id);
2687
- continue;
2688
- }
2689
- if (entry.id && !deleted.has(entry.id)) {
2690
- byId.set(entry.id, entry);
2691
- }
2692
- } catch {
2866
+ import * as path10 from "node:path";
2867
+
2868
+ // src/storage/session-store/paths.ts
2869
+ import * as path9 from "node:path";
2870
+ function sessionPath(storeDir, id, ext) {
2871
+ return sessionScopedPath(storeDir, id, ext);
2872
+ }
2873
+ function shardManifestPath(storeDir, shardKey) {
2874
+ return shardKey ? path9.join(storeDir, shardKey, "_manifest.json") : path9.join(storeDir, "_manifest.json");
2875
+ }
2876
+ function shardKeyForSessionId(id) {
2877
+ const dirName = path9.dirname(id);
2878
+ return dirName === "." ? "" : dirName;
2879
+ }
2880
+ async function ensureShardDir(storeDir, id) {
2881
+ const dirPath = path9.dirname(sessionScopedPath(storeDir, id, ""));
2882
+ await ensureDir(dirPath);
2883
+ return dirPath;
2884
+ }
2885
+
2886
+ // src/storage/session-store/delete-session-artifacts.ts
2887
+ async function deleteSessionArtifacts({
2888
+ rootDir,
2889
+ id,
2890
+ jsonlPath
2891
+ }) {
2892
+ const shardDir = path10.dirname(jsonlPath);
2893
+ const base = path10.basename(id);
2894
+ const sessDir = path10.join(shardDir, base);
2895
+ const deletions = [
2896
+ fsp5.unlink(jsonlPath),
2897
+ fsp5.unlink(sessionPath(rootDir, id, ".summary.json")),
2898
+ fsp5.unlink(sessionPath(rootDir, id, ".plan.json")),
2899
+ fsp5.unlink(sessionPath(rootDir, id, ".tasks.json")),
2900
+ fsp5.unlink(sessionPath(rootDir, id, ".todos.json"))
2901
+ ];
2902
+ const results = await Promise.allSettled(deletions);
2903
+ for (const result of results) {
2904
+ if (result.status === "rejected") {
2905
+ warnDeleteFailure(id, result.reason);
2693
2906
  }
2694
2907
  }
2908
+ await fsp5.rm(sessDir, { recursive: true, force: true }).catch((err) => {
2909
+ console.warn(
2910
+ JSON.stringify({
2911
+ level: "warn",
2912
+ event: "session_store.rmdir_failed",
2913
+ sessionId: id,
2914
+ message: toErrorMessage(err),
2915
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2916
+ })
2917
+ );
2918
+ });
2695
2919
  }
2696
- async function readFileRange(file, start, end) {
2697
- const length = end - start;
2698
- if (length <= 0) return { raw: "", end: start };
2699
- const buffer = Buffer.allocUnsafe(length);
2700
- let offset = 0;
2701
- let handle;
2702
- try {
2703
- handle = await fsp5.open(file, "r");
2704
- while (offset < length) {
2705
- const { bytesRead } = await handle.read(buffer, offset, length - offset, start + offset);
2706
- if (bytesRead === 0) return null;
2707
- offset += bytesRead;
2708
- }
2709
- let completeLength = buffer.length;
2710
- if (buffer.at(-1) !== 10) {
2711
- const lastNewline = buffer.lastIndexOf(10);
2712
- const trailing = buffer.subarray(lastNewline + 1).toString("utf8").trim();
2713
- try {
2714
- if (trailing) JSON.parse(trailing);
2715
- else completeLength = lastNewline + 1;
2716
- } catch {
2717
- completeLength = lastNewline + 1;
2718
- }
2719
- }
2720
- return {
2721
- raw: buffer.subarray(0, completeLength).toString("utf8"),
2722
- end: start + completeLength
2723
- };
2724
- } catch {
2725
- return null;
2726
- } finally {
2727
- await handle?.close().catch(() => void 0);
2920
+ function warnDeleteFailure(id, reason) {
2921
+ const msg = reason instanceof Error ? reason.message : String(reason);
2922
+ if (reason?.code === "ENOENT") return;
2923
+ console.warn(
2924
+ JSON.stringify({
2925
+ level: "warn",
2926
+ event: "session_store.delete_failed",
2927
+ sessionId: id,
2928
+ message: msg,
2929
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
2930
+ })
2931
+ );
2932
+ }
2933
+
2934
+ // src/storage/session-store/delete-session-guards.ts
2935
+ async function assertSessionCanBeDeleted(sessionId, isSessionInUse) {
2936
+ if (!isSessionInUse) return;
2937
+ const reason = await isSessionInUse(sessionId);
2938
+ if (reason) {
2939
+ throw new Error(`Session ${sessionId} is in use (${reason}) and cannot be deleted.`);
2728
2940
  }
2729
2941
  }
2730
2942
 
@@ -2738,7 +2950,7 @@ function isSessionJsonlFileName(name) {
2738
2950
 
2739
2951
  // src/storage/session-store/directory-session-files.ts
2740
2952
  import * as fsp6 from "node:fs/promises";
2741
- import * as path9 from "node:path";
2953
+ import * as path11 from "node:path";
2742
2954
  function sessionIdForFile(prefix, name) {
2743
2955
  const base = name.replace(/\.jsonl$/, "");
2744
2956
  return prefix ? `${prefix}/${base}` : base;
@@ -2760,12 +2972,12 @@ async function collectSessionFiles(dir, prefix = "", depth = 0) {
2760
2972
  if (entry.isDirectory()) {
2761
2973
  dirEntries.push(entry);
2762
2974
  } else if (entry.isFile() && isSessionJsonlFileName(entry.name)) {
2763
- files.push({ id: sessionIdForFile(prefix, entry.name), filePath: path9.join(dir, entry.name) });
2975
+ files.push({ id: sessionIdForFile(prefix, entry.name), filePath: path11.join(dir, entry.name) });
2764
2976
  }
2765
2977
  }
2766
2978
  const childFileArrays = await Promise.all(
2767
2979
  dirEntries.map(
2768
- (entry) => collectSessionFiles(path9.join(dir, entry.name), childPrefixFor(entry, prefix, depth), depth + 1)
2980
+ (entry) => collectSessionFiles(path11.join(dir, entry.name), childPrefixFor(entry, prefix, depth), depth + 1)
2769
2981
  )
2770
2982
  );
2771
2983
  return [...childFileArrays.flat(), ...files];
@@ -2789,230 +3001,55 @@ async function collectSessionIds(dir, prefix = "", depth = 0) {
2789
3001
  }
2790
3002
  const childIdArrays = await Promise.all(
2791
3003
  dirEntries.map(
2792
- (entry) => collectSessionIds(path9.join(dir, entry.name), childPrefixFor(entry, prefix, depth), depth + 1)
3004
+ (entry) => collectSessionIds(path11.join(dir, entry.name), childPrefixFor(entry, prefix, depth), depth + 1)
2793
3005
  )
2794
3006
  );
2795
3007
  return [...childIdArrays.flat(), ...fileIds];
2796
3008
  }
2797
3009
 
2798
- // src/storage/session-store/delete-session-artifacts.ts
2799
- import * as fsp7 from "node:fs/promises";
2800
- import * as path11 from "node:path";
2801
-
2802
- // src/storage/session-store/paths.ts
2803
- import * as path10 from "node:path";
2804
- function sessionPath(storeDir, id, ext) {
2805
- return sessionScopedPath(storeDir, id, ext);
2806
- }
2807
- function shardManifestPath(storeDir, shardKey) {
2808
- return shardKey ? path10.join(storeDir, shardKey, "_manifest.json") : path10.join(storeDir, "_manifest.json");
3010
+ // src/storage/session-store/events.ts
3011
+ function emitSessionStoreRead(events, sessionId, filePath, operation, outcome, durationMs, error) {
3012
+ events?.emit("storage.read", {
3013
+ sessionId,
3014
+ store: "session",
3015
+ filePath,
3016
+ operation,
3017
+ outcome,
3018
+ durationMs,
3019
+ ...error !== void 0 ? { error } : {}
3020
+ });
2809
3021
  }
2810
- function shardKeyForSessionId(id) {
2811
- const dirName = path10.dirname(id);
2812
- return dirName === "." ? "" : dirName;
3022
+ function emitSessionStoreWrite(events, sessionId, filePath, operation, outcome, durationMs, eventCount, error) {
3023
+ events?.emit("storage.write", {
3024
+ sessionId,
3025
+ store: "session",
3026
+ filePath,
3027
+ operation,
3028
+ outcome,
3029
+ durationMs,
3030
+ ...eventCount !== void 0 ? { eventCount } : {},
3031
+ ...error !== void 0 ? { error } : {}
3032
+ });
2813
3033
  }
2814
- async function ensureShardDir(storeDir, id) {
2815
- const dirPath = path10.dirname(sessionScopedPath(storeDir, id, ""));
2816
- await ensureDir(dirPath);
2817
- return dirPath;
3034
+ function emitSessionStoreError(events, sessionId, filePath, operation, error, recoverable) {
3035
+ events?.emit("storage.error", {
3036
+ sessionId,
3037
+ store: "session",
3038
+ filePath,
3039
+ operation,
3040
+ error,
3041
+ recoverable
3042
+ });
2818
3043
  }
2819
3044
 
2820
- // src/storage/session-store/delete-session-artifacts.ts
2821
- async function deleteSessionArtifacts({
2822
- rootDir,
2823
- id,
2824
- jsonlPath
2825
- }) {
2826
- const shardDir = path11.dirname(jsonlPath);
2827
- const base = path11.basename(id);
2828
- const sessDir = path11.join(shardDir, base);
2829
- const deletions = [
2830
- fsp7.unlink(jsonlPath),
2831
- fsp7.unlink(sessionPath(rootDir, id, ".summary.json")),
2832
- fsp7.unlink(sessionPath(rootDir, id, ".plan.json")),
2833
- fsp7.unlink(sessionPath(rootDir, id, ".tasks.json")),
2834
- fsp7.unlink(sessionPath(rootDir, id, ".todos.json"))
2835
- ];
2836
- const results = await Promise.allSettled(deletions);
2837
- for (const result of results) {
2838
- if (result.status === "rejected") {
2839
- warnDeleteFailure(id, result.reason);
2840
- }
2841
- }
2842
- await fsp7.rm(sessDir, { recursive: true, force: true }).catch((err) => {
2843
- console.warn(
2844
- JSON.stringify({
2845
- level: "warn",
2846
- event: "session_store.rmdir_failed",
2847
- sessionId: id,
2848
- message: toErrorMessage(err),
2849
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
2850
- })
2851
- );
2852
- });
2853
- }
2854
- function warnDeleteFailure(id, reason) {
2855
- const msg = reason instanceof Error ? reason.message : String(reason);
2856
- if (reason?.code === "ENOENT") return;
2857
- console.warn(
2858
- JSON.stringify({
2859
- level: "warn",
2860
- event: "session_store.delete_failed",
2861
- sessionId: id,
2862
- message: msg,
2863
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
2864
- })
2865
- );
2866
- }
2867
-
2868
- // src/storage/session-store/delete-session-guards.ts
2869
- async function assertSessionCanBeDeleted(sessionId, isSessionInUse) {
2870
- if (!isSessionInUse) return;
2871
- const reason = await isSessionInUse(sessionId);
2872
- if (reason) {
2873
- throw new Error(`Session ${sessionId} is in use (${reason}) and cannot be deleted.`);
2874
- }
2875
- }
2876
-
2877
- // src/storage/session-store/load-cache.ts
2878
- var LOAD_CACHE_MAX_ENTRIES = 50;
2879
- var LOAD_CACHE_MAX_BYTES = 64 * 1024 * 1024;
2880
- var SessionLoadCache = class {
2881
- constructor(entries = /* @__PURE__ */ new Map()) {
2882
- this.entries = entries;
2883
- }
2884
- entries;
2885
- bytes = 0;
2886
- clear(sessionId) {
2887
- if (sessionId !== void 0) {
2888
- this.delete(sessionId);
2889
- return;
2890
- }
2891
- this.entries.clear();
2892
- this.bytes = 0;
2893
- }
2894
- getFresh(id, stat16, full) {
2895
- const cached = this.entries.get(id);
2896
- if (!cached || cached.mtimeMs !== stat16.mtimeMs || cached.size !== stat16.size) {
2897
- return null;
2898
- }
2899
- this.entries.delete(id);
2900
- this.entries.set(id, cached);
2901
- if (full) return cached.data;
2902
- return { ...cached.data, messages: [] };
2903
- }
2904
- set(id, stat16, data) {
2905
- this.delete(id);
2906
- while (stat16.size <= LOAD_CACHE_MAX_BYTES && (this.entries.size >= LOAD_CACHE_MAX_ENTRIES || this.bytes + stat16.size > LOAD_CACHE_MAX_BYTES)) {
2907
- const oldest = this.entries.keys().next().value;
2908
- if (oldest === void 0) break;
2909
- this.delete(oldest);
2910
- }
2911
- if (stat16.size <= LOAD_CACHE_MAX_BYTES) {
2912
- this.entries.set(id, { mtimeMs: stat16.mtimeMs, size: stat16.size, data });
2913
- this.bytes += stat16.size;
2914
- }
2915
- }
2916
- delete(sessionId) {
2917
- const cached = this.entries.get(sessionId);
2918
- if (cached) this.bytes = Math.max(0, this.bytes - cached.size);
2919
- this.entries.delete(sessionId);
2920
- }
2921
- };
2922
-
2923
- // src/storage/session-store/prune-helpers.ts
2924
- import * as fsp8 from "node:fs/promises";
2925
- import * as path12 from "node:path";
2926
- function isPrunableSessionJsonl(name) {
2927
- return name.endsWith(".jsonl") && name !== "_index.jsonl" && name !== "_mailbox.jsonl" && !name.endsWith(".replay.jsonl") && !name.endsWith(".audit.jsonl");
2928
- }
2929
- async function pruneSessionFiles(storeDir, maxAgeDays, deleteSession) {
2930
- const cutoff = Date.now() - maxAgeDays * 864e5;
2931
- let deleted = 0;
2932
- const pruneFile = async (dir, name, prefix) => {
2933
- const jsonlPath = path12.join(dir, name);
2934
- try {
2935
- const stat16 = await fsp8.stat(jsonlPath);
2936
- if (stat16.mtimeMs >= cutoff) return;
2937
- } catch {
2938
- return;
2939
- }
2940
- const base = name.replace(/\.jsonl$/, "");
2941
- const id = prefix ? `${prefix}/${base}` : base;
2942
- await deleteSession(id);
2943
- deleted++;
2944
- };
2945
- const entries = await fsp8.readdir(storeDir, { withFileTypes: true }).catch(() => []);
2946
- for (const entry of entries) {
2947
- if (entry.isFile()) {
2948
- if (isPrunableSessionJsonl(entry.name)) await pruneFile(storeDir, entry.name, "");
2949
- continue;
2950
- }
2951
- if (!entry.isDirectory()) continue;
2952
- const dateDir = path12.join(storeDir, entry.name);
2953
- const files = await fsp8.readdir(dateDir, { withFileTypes: true }).catch(() => []);
2954
- for (const file of files) {
2955
- if (!file.isFile() || !isPrunableSessionJsonl(file.name)) continue;
2956
- await pruneFile(dateDir, file.name, entry.name);
2957
- }
2958
- }
2959
- for (const entry of entries) {
2960
- if (!entry.isDirectory()) continue;
2961
- const dateDir = path12.join(storeDir, entry.name);
2962
- try {
2963
- const remaining = await fsp8.readdir(dateDir);
2964
- if (remaining.length === 0) {
2965
- await fsp8.rmdir(dateDir).catch(() => void 0);
2966
- }
2967
- } catch {
2968
- }
2969
- }
2970
- return deleted;
2971
- }
2972
-
2973
- // src/storage/session-store/events.ts
2974
- function emitSessionStoreRead(events, sessionId, filePath, operation, outcome, durationMs, error) {
2975
- events?.emit("storage.read", {
2976
- sessionId,
2977
- store: "session",
2978
- filePath,
2979
- operation,
2980
- outcome,
2981
- durationMs,
2982
- ...error !== void 0 ? { error } : {}
2983
- });
2984
- }
2985
- function emitSessionStoreWrite(events, sessionId, filePath, operation, outcome, durationMs, eventCount, error) {
2986
- events?.emit("storage.write", {
2987
- sessionId,
2988
- store: "session",
2989
- filePath,
2990
- operation,
2991
- outcome,
2992
- durationMs,
2993
- ...eventCount !== void 0 ? { eventCount } : {},
2994
- ...error !== void 0 ? { error } : {}
2995
- });
2996
- }
2997
- function emitSessionStoreError(events, sessionId, filePath, operation, error, recoverable) {
2998
- events?.emit("storage.error", {
2999
- sessionId,
3000
- store: "session",
3001
- filePath,
3002
- operation,
3003
- error,
3004
- recoverable
3005
- });
3006
- }
3007
-
3008
- // src/storage/session-store/fork-session.ts
3009
- import { createHash as createHash4 } from "node:crypto";
3010
-
3011
- // src/storage/session-store/replay.ts
3012
- function isReplayableMessage(value) {
3013
- if (!value || typeof value !== "object") return false;
3014
- const candidate = value;
3015
- return (candidate.role === "user" || candidate.role === "assistant" || candidate.role === "system") && (typeof candidate.content === "string" || Array.isArray(candidate.content));
3045
+ // src/storage/session-store/fork-session.ts
3046
+ import { createHash as createHash4 } from "node:crypto";
3047
+
3048
+ // src/storage/session-store/replay.ts
3049
+ function isReplayableMessage(value) {
3050
+ if (!value || typeof value !== "object") return false;
3051
+ const candidate = value;
3052
+ return (candidate.role === "user" || candidate.role === "assistant" || candidate.role === "system") && (typeof candidate.content === "string" || Array.isArray(candidate.content));
3016
3053
  }
3017
3054
  function trackMessageToolState(message, openToolUses) {
3018
3055
  if (!Array.isArray(message.content)) return;
@@ -3119,6 +3156,106 @@ async function forkSession(host, id, opts = {}) {
3119
3156
  }
3120
3157
  }
3121
3158
 
3159
+ // src/storage/session-store/index-reader.ts
3160
+ import * as fsp7 from "node:fs/promises";
3161
+ function applySessionIndexLines(raw, byId, deleted) {
3162
+ for (const line of raw.split("\n")) {
3163
+ if (!line.trim()) continue;
3164
+ try {
3165
+ const entry = JSON.parse(line);
3166
+ if (entry.action === "delete" && entry.id) {
3167
+ deleted.add(entry.id);
3168
+ byId.delete(entry.id);
3169
+ continue;
3170
+ }
3171
+ if (entry.id && !deleted.has(entry.id)) {
3172
+ byId.set(entry.id, entry);
3173
+ }
3174
+ } catch {
3175
+ }
3176
+ }
3177
+ }
3178
+ async function readFileRange(file, start, end) {
3179
+ const length = end - start;
3180
+ if (length <= 0) return { raw: "", end: start };
3181
+ const buffer = Buffer.allocUnsafe(length);
3182
+ let offset = 0;
3183
+ let handle;
3184
+ try {
3185
+ handle = await fsp7.open(file, "r");
3186
+ while (offset < length) {
3187
+ const { bytesRead } = await handle.read(buffer, offset, length - offset, start + offset);
3188
+ if (bytesRead === 0) return null;
3189
+ offset += bytesRead;
3190
+ }
3191
+ let completeLength = buffer.length;
3192
+ if (buffer.at(-1) !== 10) {
3193
+ const lastNewline = buffer.lastIndexOf(10);
3194
+ const trailing = buffer.subarray(lastNewline + 1).toString("utf8").trim();
3195
+ try {
3196
+ if (trailing) JSON.parse(trailing);
3197
+ else completeLength = lastNewline + 1;
3198
+ } catch {
3199
+ completeLength = lastNewline + 1;
3200
+ }
3201
+ }
3202
+ return {
3203
+ raw: buffer.subarray(0, completeLength).toString("utf8"),
3204
+ end: start + completeLength
3205
+ };
3206
+ } catch {
3207
+ return null;
3208
+ } finally {
3209
+ await handle?.close().catch(() => void 0);
3210
+ }
3211
+ }
3212
+
3213
+ // src/storage/session-store/load-cache.ts
3214
+ var LOAD_CACHE_MAX_ENTRIES = 50;
3215
+ var LOAD_CACHE_MAX_BYTES = 64 * 1024 * 1024;
3216
+ var SessionLoadCache = class {
3217
+ constructor(entries = /* @__PURE__ */ new Map()) {
3218
+ this.entries = entries;
3219
+ }
3220
+ entries;
3221
+ bytes = 0;
3222
+ clear(sessionId) {
3223
+ if (sessionId !== void 0) {
3224
+ this.delete(sessionId);
3225
+ return;
3226
+ }
3227
+ this.entries.clear();
3228
+ this.bytes = 0;
3229
+ }
3230
+ getFresh(id, stat15, full) {
3231
+ const cached = this.entries.get(id);
3232
+ if (!cached || cached.mtimeMs !== stat15.mtimeMs || cached.size !== stat15.size) {
3233
+ return null;
3234
+ }
3235
+ this.entries.delete(id);
3236
+ this.entries.set(id, cached);
3237
+ if (full) return cached.data;
3238
+ return { ...cached.data, messages: [] };
3239
+ }
3240
+ set(id, stat15, data) {
3241
+ this.delete(id);
3242
+ while (stat15.size <= LOAD_CACHE_MAX_BYTES && (this.entries.size >= LOAD_CACHE_MAX_ENTRIES || this.bytes + stat15.size > LOAD_CACHE_MAX_BYTES)) {
3243
+ const oldest = this.entries.keys().next().value;
3244
+ if (oldest === void 0) break;
3245
+ this.delete(oldest);
3246
+ }
3247
+ if (stat15.size <= LOAD_CACHE_MAX_BYTES) {
3248
+ this.entries.set(id, { mtimeMs: stat15.mtimeMs, size: stat15.size, data });
3249
+ this.bytes += stat15.size;
3250
+ }
3251
+ }
3252
+ delete(sessionId) {
3253
+ const cached = this.entries.get(sessionId);
3254
+ if (cached) this.bytes = Math.max(0, this.bytes - cached.size);
3255
+ this.entries.delete(sessionId);
3256
+ }
3257
+ };
3258
+
3122
3259
  // src/storage/session-store/load-session-data.ts
3123
3260
  import { createReadStream as createReadStream2 } from "node:fs";
3124
3261
  import { createInterface as createInterface2 } from "node:readline";
@@ -3373,6 +3510,178 @@ function emitDamaged(params, detail) {
3373
3510
  });
3374
3511
  }
3375
3512
 
3513
+ // src/storage/session-store/prune-helpers.ts
3514
+ import * as fsp8 from "node:fs/promises";
3515
+ import * as path12 from "node:path";
3516
+ function isPrunableSessionJsonl(name) {
3517
+ return name.endsWith(".jsonl") && name !== "_index.jsonl" && name !== "_mailbox.jsonl" && !name.endsWith(".replay.jsonl") && !name.endsWith(".audit.jsonl");
3518
+ }
3519
+ async function pruneSessionFiles(storeDir, maxAgeDays, deleteSession) {
3520
+ const cutoff = Date.now() - maxAgeDays * 864e5;
3521
+ let deleted = 0;
3522
+ const pruneFile = async (dir, name, prefix) => {
3523
+ const jsonlPath = path12.join(dir, name);
3524
+ try {
3525
+ const stat15 = await fsp8.stat(jsonlPath);
3526
+ if (stat15.mtimeMs >= cutoff) return;
3527
+ } catch {
3528
+ return;
3529
+ }
3530
+ const base = name.replace(/\.jsonl$/, "");
3531
+ const id = prefix ? `${prefix}/${base}` : base;
3532
+ await deleteSession(id);
3533
+ deleted++;
3534
+ };
3535
+ const entries = await fsp8.readdir(storeDir, { withFileTypes: true }).catch(() => []);
3536
+ for (const entry of entries) {
3537
+ if (entry.isFile()) {
3538
+ if (isPrunableSessionJsonl(entry.name)) await pruneFile(storeDir, entry.name, "");
3539
+ continue;
3540
+ }
3541
+ if (!entry.isDirectory()) continue;
3542
+ const dateDir = path12.join(storeDir, entry.name);
3543
+ const files = await fsp8.readdir(dateDir, { withFileTypes: true }).catch(() => []);
3544
+ for (const file of files) {
3545
+ if (!file.isFile() || !isPrunableSessionJsonl(file.name)) continue;
3546
+ await pruneFile(dateDir, file.name, entry.name);
3547
+ }
3548
+ }
3549
+ for (const entry of entries) {
3550
+ if (!entry.isDirectory()) continue;
3551
+ const dateDir = path12.join(storeDir, entry.name);
3552
+ try {
3553
+ const remaining = await fsp8.readdir(dateDir);
3554
+ if (remaining.length === 0) {
3555
+ await fsp8.rmdir(dateDir).catch(() => void 0);
3556
+ }
3557
+ } catch {
3558
+ }
3559
+ }
3560
+ return deleted;
3561
+ }
3562
+
3563
+ // src/storage/session-store/search-events.ts
3564
+ import * as fsp9 from "node:fs/promises";
3565
+ async function searchSessionEvents(args) {
3566
+ const { file, secretScrubber, predicate, limit, signal } = args;
3567
+ const out = [];
3568
+ let stat15;
3569
+ try {
3570
+ stat15 = await fsp9.stat(file);
3571
+ } catch (err) {
3572
+ if (err.code === "ENOENT") return [];
3573
+ throw err;
3574
+ }
3575
+ if (stat15.size === 0) return [];
3576
+ let fh;
3577
+ try {
3578
+ fh = await fsp9.open(file, "r");
3579
+ const CHUNK = 64 * 1024;
3580
+ const buf = Buffer.alloc(CHUNK);
3581
+ let leftover = "";
3582
+ let eventIndex = 0;
3583
+ for (let position = 0; ; position += buf.byteLength) {
3584
+ if (signal?.aborted) {
3585
+ const reason = signal.reason ?? new DOMException("Aborted", "AbortError");
3586
+ throw reason;
3587
+ }
3588
+ const { bytesRead } = await fh.read(buf, 0, CHUNK, position);
3589
+ if (bytesRead === 0) break;
3590
+ const text = leftover + buf.subarray(0, bytesRead).toString("utf8");
3591
+ const parts = text.split("\n");
3592
+ leftover = parts.pop() ?? "";
3593
+ for (const line of parts) {
3594
+ if (!line) continue;
3595
+ const ev = parseSessionEventLine(line, secretScrubber);
3596
+ if (!ev) continue;
3597
+ if (predicate(ev, eventIndex, ev.ts)) {
3598
+ out.push({ event: ev, eventIndex, ts: ev.ts });
3599
+ if (limit !== void 0 && out.length >= limit) return out;
3600
+ }
3601
+ eventIndex++;
3602
+ }
3603
+ }
3604
+ const trailing = parseSessionEventLine(leftover.trim() ? leftover : "", secretScrubber);
3605
+ if (trailing && predicate(trailing, eventIndex, trailing.ts)) {
3606
+ out.push({ event: trailing, eventIndex, ts: trailing.ts });
3607
+ }
3608
+ return out;
3609
+ } finally {
3610
+ if (fh) await fh.close().catch(() => void 0);
3611
+ }
3612
+ }
3613
+ function parseSessionEventLine(line, secretScrubber) {
3614
+ if (!line) return null;
3615
+ try {
3616
+ const parsed = JSON.parse(line);
3617
+ if (parsed === null || typeof parsed !== "object" || typeof parsed.type !== "string" || typeof parsed.ts !== "string") {
3618
+ return null;
3619
+ }
3620
+ return scrubPersistedSessionEvent(parsed, secretScrubber);
3621
+ } catch {
3622
+ return null;
3623
+ }
3624
+ }
3625
+
3626
+ // src/storage/session-store/shard-manifest.ts
3627
+ import * as fsp10 from "node:fs/promises";
3628
+
3629
+ // src/storage/session-summary.ts
3630
+ function compareSessionSummaries(a, b) {
3631
+ const aActivity = a.lastActivityAt ?? a.endedAt ?? a.startedAt;
3632
+ const bActivity = b.lastActivityAt ?? b.endedAt ?? b.startedAt;
3633
+ if (aActivity < bActivity) return 1;
3634
+ if (aActivity > bActivity) return -1;
3635
+ if (a.startedAt < b.startedAt) return 1;
3636
+ if (a.startedAt > b.startedAt) return -1;
3637
+ return a.id.localeCompare(b.id);
3638
+ }
3639
+ function matchesSessionFilter(summary, criteria) {
3640
+ if (criteria.since && summary.startedAt < criteria.since) return false;
3641
+ if (criteria.until && summary.startedAt > criteria.until) return false;
3642
+ if (criteria.provider && summary.provider !== criteria.provider) return false;
3643
+ if (criteria.model && summary.model !== criteria.model) return false;
3644
+ if (criteria.minTokens !== void 0 && summary.tokenTotal < criteria.minTokens) return false;
3645
+ if (criteria.titleContains) {
3646
+ const needle = criteria.titleContains.toLocaleLowerCase();
3647
+ if (!summary.title.toLocaleLowerCase().includes(needle)) return false;
3648
+ }
3649
+ return true;
3650
+ }
3651
+
3652
+ // src/storage/session-store/shard-manifest.ts
3653
+ async function readOrBuildShardManifestEntry(opts) {
3654
+ try {
3655
+ const raw = await fsp10.readFile(opts.manifestPath, "utf8");
3656
+ const parsed = JSON.parse(raw);
3657
+ return {
3658
+ summaries: Array.isArray(parsed.summaries) ? parsed.summaries : [],
3659
+ ids: Array.isArray(parsed.ids) ? parsed.ids : []
3660
+ };
3661
+ } catch {
3662
+ }
3663
+ const refs = await opts.collectSessionFilesInShard(opts.shardKey);
3664
+ const candidates = await mapWithConcurrency(
3665
+ refs,
3666
+ opts.concurrency,
3667
+ async (ref) => {
3668
+ const manifest = await opts.readSummaryManifest(ref.id);
3669
+ if (manifest) return { summary: manifest, needsBackfill: false };
3670
+ const summary = await opts.summaryHeaderFor(ref);
3671
+ if (!summary) return null;
3672
+ const hydrated = await opts.summaryFor(summary.id).catch(() => summary);
3673
+ return { summary: hydrated, needsBackfill: false };
3674
+ }
3675
+ );
3676
+ const summaries = candidates.filter((candidate) => candidate !== null).map((candidate) => candidate.summary);
3677
+ summaries.sort(compareSessionSummaries);
3678
+ const entry = { summaries, ids: summaries.map((summary) => summary.id) };
3679
+ await atomicWrite(opts.manifestPath, JSON.stringify(entry), { mode: 384 }).catch(
3680
+ () => void 0
3681
+ );
3682
+ return entry;
3683
+ }
3684
+
3376
3685
  // src/storage/session-store/summary-builder.ts
3377
3686
  import { createReadStream as createReadStream3 } from "node:fs";
3378
3687
  import { createInterface as createInterface3 } from "node:readline";
@@ -3512,13 +3821,13 @@ async function* iterateSessionEvents(file, secretScrubber) {
3512
3821
  }
3513
3822
 
3514
3823
  // src/storage/session-store/summary-header.ts
3515
- import * as fsp9 from "node:fs/promises";
3824
+ import * as fsp11 from "node:fs/promises";
3516
3825
  async function readSessionSummaryHeader(ref, secretScrubber) {
3517
3826
  let mtime = (/* @__PURE__ */ new Date(0)).toISOString();
3518
3827
  try {
3519
- const stat16 = await fsp9.stat(ref.filePath);
3520
- if (!stat16.isFile()) return damagedSummary(ref.id, stat16.mtime.toISOString());
3521
- mtime = stat16.mtime.toISOString();
3828
+ const stat15 = await fsp11.stat(ref.filePath);
3829
+ if (!stat15.isFile()) return damagedSummary(ref.id, stat15.mtime.toISOString());
3830
+ mtime = stat15.mtime.toISOString();
3522
3831
  } catch {
3523
3832
  return null;
3524
3833
  }
@@ -3561,128 +3870,6 @@ function damagedSummary(id, startedAt) {
3561
3870
  };
3562
3871
  }
3563
3872
 
3564
- // src/storage/session-store/shard-manifest.ts
3565
- import * as fsp10 from "node:fs/promises";
3566
-
3567
- // src/storage/session-summary.ts
3568
- function compareSessionSummaries(a, b) {
3569
- const aActivity = a.lastActivityAt ?? a.endedAt ?? a.startedAt;
3570
- const bActivity = b.lastActivityAt ?? b.endedAt ?? b.startedAt;
3571
- if (aActivity < bActivity) return 1;
3572
- if (aActivity > bActivity) return -1;
3573
- if (a.startedAt < b.startedAt) return 1;
3574
- if (a.startedAt > b.startedAt) return -1;
3575
- return a.id.localeCompare(b.id);
3576
- }
3577
- function matchesSessionFilter(summary, criteria) {
3578
- if (criteria.since && summary.startedAt < criteria.since) return false;
3579
- if (criteria.until && summary.startedAt > criteria.until) return false;
3580
- if (criteria.provider && summary.provider !== criteria.provider) return false;
3581
- if (criteria.model && summary.model !== criteria.model) return false;
3582
- if (criteria.minTokens !== void 0 && summary.tokenTotal < criteria.minTokens) return false;
3583
- if (criteria.titleContains) {
3584
- const needle = criteria.titleContains.toLocaleLowerCase();
3585
- if (!summary.title.toLocaleLowerCase().includes(needle)) return false;
3586
- }
3587
- return true;
3588
- }
3589
-
3590
- // src/storage/session-store/shard-manifest.ts
3591
- async function readOrBuildShardManifestEntry(opts) {
3592
- try {
3593
- const raw = await fsp10.readFile(opts.manifestPath, "utf8");
3594
- const parsed = JSON.parse(raw);
3595
- return {
3596
- summaries: Array.isArray(parsed.summaries) ? parsed.summaries : [],
3597
- ids: Array.isArray(parsed.ids) ? parsed.ids : []
3598
- };
3599
- } catch {
3600
- }
3601
- const refs = await opts.collectSessionFilesInShard(opts.shardKey);
3602
- const candidates = await mapWithConcurrency(
3603
- refs,
3604
- opts.concurrency,
3605
- async (ref) => {
3606
- const manifest = await opts.readSummaryManifest(ref.id);
3607
- if (manifest) return { summary: manifest, needsBackfill: false };
3608
- const summary = await opts.summaryHeaderFor(ref);
3609
- if (!summary) return null;
3610
- const hydrated = await opts.summaryFor(summary.id).catch(() => summary);
3611
- return { summary: hydrated, needsBackfill: false };
3612
- }
3613
- );
3614
- const summaries = candidates.filter((candidate) => candidate !== null).map((candidate) => candidate.summary);
3615
- summaries.sort(compareSessionSummaries);
3616
- const entry = { summaries, ids: summaries.map((summary) => summary.id) };
3617
- await atomicWrite(opts.manifestPath, JSON.stringify(entry), { mode: 384 }).catch(
3618
- () => void 0
3619
- );
3620
- return entry;
3621
- }
3622
-
3623
- // src/storage/session-store/search-events.ts
3624
- import * as fsp11 from "node:fs/promises";
3625
- async function searchSessionEvents(args) {
3626
- const { file, secretScrubber, predicate, limit, signal } = args;
3627
- const out = [];
3628
- let stat16;
3629
- try {
3630
- stat16 = await fsp11.stat(file);
3631
- } catch (err) {
3632
- if (err.code === "ENOENT") return [];
3633
- throw err;
3634
- }
3635
- if (stat16.size === 0) return [];
3636
- let fh;
3637
- try {
3638
- fh = await fsp11.open(file, "r");
3639
- const CHUNK = 64 * 1024;
3640
- const buf = Buffer.alloc(CHUNK);
3641
- let leftover = "";
3642
- let eventIndex = 0;
3643
- for (let position = 0; ; position += buf.byteLength) {
3644
- if (signal?.aborted) {
3645
- const reason = signal.reason ?? new DOMException("Aborted", "AbortError");
3646
- throw reason;
3647
- }
3648
- const { bytesRead } = await fh.read(buf, 0, CHUNK, position);
3649
- if (bytesRead === 0) break;
3650
- const text = leftover + buf.subarray(0, bytesRead).toString("utf8");
3651
- const parts = text.split("\n");
3652
- leftover = parts.pop() ?? "";
3653
- for (const line of parts) {
3654
- if (!line) continue;
3655
- const ev = parseSessionEventLine(line, secretScrubber);
3656
- if (!ev) continue;
3657
- if (predicate(ev, eventIndex, ev.ts)) {
3658
- out.push({ event: ev, eventIndex, ts: ev.ts });
3659
- if (limit !== void 0 && out.length >= limit) return out;
3660
- }
3661
- eventIndex++;
3662
- }
3663
- }
3664
- const trailing = parseSessionEventLine(leftover.trim() ? leftover : "", secretScrubber);
3665
- if (trailing && predicate(trailing, eventIndex, trailing.ts)) {
3666
- out.push({ event: trailing, eventIndex, ts: trailing.ts });
3667
- }
3668
- return out;
3669
- } finally {
3670
- if (fh) await fh.close().catch(() => void 0);
3671
- }
3672
- }
3673
- function parseSessionEventLine(line, secretScrubber) {
3674
- if (!line) return null;
3675
- try {
3676
- const parsed = JSON.parse(line);
3677
- if (parsed === null || typeof parsed !== "object" || typeof parsed.type !== "string" || typeof parsed.ts !== "string") {
3678
- return null;
3679
- }
3680
- return scrubPersistedSessionEvent(parsed, secretScrubber);
3681
- } catch {
3682
- return null;
3683
- }
3684
- }
3685
-
3686
3873
  // src/storage/session-store.ts
3687
3874
  var DefaultSessionStore = class _DefaultSessionStore {
3688
3875
  dir;
@@ -3692,6 +3879,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
3692
3879
  checkpointCas;
3693
3880
  isSessionInUse;
3694
3881
  logger;
3882
+ onAppend;
3883
+ onAppendBatch;
3695
3884
  /**
3696
3885
  * In-memory cache for load() results, keyed by session ID. The cache is
3697
3886
  * invalidated when the file's mtimeMs or size changes (indicating the
@@ -3718,6 +3907,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
3718
3907
  this.secretScrubber = opts.secretScrubber ?? new DefaultSecretScrubber();
3719
3908
  this.isSessionInUse = opts.isSessionInUse;
3720
3909
  this.logger = opts.logger;
3910
+ this.onAppend = opts.onAppend;
3911
+ this.onAppendBatch = opts.onAppendBatch;
3721
3912
  }
3722
3913
  /**
3723
3914
  * Emit a structured warning. Uses the configured Logger when available;
@@ -3794,6 +3985,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
3794
3985
  filePath: file,
3795
3986
  secretScrubber: this.secretScrubber,
3796
3987
  checkpointCas: this.checkpointCas,
3988
+ onAppend: this.onAppend,
3989
+ onAppendBatch: this.onAppendBatch,
3797
3990
  resolveName: async () => {
3798
3991
  const current = await this.readSummaryManifest(id);
3799
3992
  if (!current) return null;
@@ -3817,21 +4010,25 @@ var DefaultSessionStore = class _DefaultSessionStore {
3817
4010
  async fork(id, opts = {}) {
3818
4011
  return forkSession(this, id, opts);
3819
4012
  }
4013
+ /**
4014
+ * Capture the deterministic post-tool workspace identity through the store-owned CAS.
4015
+ */
4016
+ async captureWorkspaceCheckpoint(sessionId, promptIndex) {
4017
+ return captureCheckpoint(this.checkpointCas, sessionId, promptIndex);
4018
+ }
4019
+ /**
4020
+ * Materialize a store-owned checkpoint into an already isolated workspace.
4021
+ */
3820
4022
  async materializeWorkspaceCheckpoint(checkpoint, targetRoot) {
3821
- if (!this.checkpointCas) {
3822
- throw new Error(
3823
- "Workspace checkpoint materialization requires a projectRoot-aware session store"
3824
- );
3825
- }
3826
- return this.checkpointCas.materialize(checkpoint, targetRoot);
4023
+ return materializeCheckpoint(this.checkpointCas, checkpoint, targetRoot);
3827
4024
  }
3828
4025
  async resolveId(query) {
3829
4026
  const normalized = query.trim();
3830
4027
  if (!normalized) throw new Error("Session not found: (empty query)");
3831
4028
  if (normalized) {
3832
4029
  try {
3833
- const stat16 = await fsp12.stat(this.sessionPath(normalized, ".jsonl"));
3834
- if (stat16.isFile()) return normalized;
4030
+ const stat15 = await fsp12.stat(this.sessionPath(normalized, ".jsonl"));
4031
+ if (stat15.isFile()) return normalized;
3835
4032
  } catch {
3836
4033
  }
3837
4034
  }
@@ -3908,9 +4105,12 @@ var DefaultSessionStore = class _DefaultSessionStore {
3908
4105
  handle = await fsp12.open(file, "a", 384);
3909
4106
  } catch (err) {
3910
4107
  emitSessionStoreError(this.events, canonicalId, file, "resume", toErrorMessage(err), false);
3911
- throw new Error(`Failed to open session "${canonicalId}" for append: ${toErrorMessage(err)}`, {
3912
- cause: err
3913
- });
4108
+ throw new Error(
4109
+ `Failed to open session "${canonicalId}" for append: ${toErrorMessage(err)}`,
4110
+ {
4111
+ cause: err
4112
+ }
4113
+ );
3914
4114
  }
3915
4115
  try {
3916
4116
  const writer = new FileSessionWriter(
@@ -3926,13 +4126,15 @@ var DefaultSessionStore = class _DefaultSessionStore {
3926
4126
  {
3927
4127
  resumed: true,
3928
4128
  initialSummary,
3929
- // Shard directory (sessions/<date>/) — must match create() so the
4129
+ // Shard directory (sessions/<date>/) must match create() so the
3930
4130
  // .summary.json sidecar lands next to the JSONL instead of the
3931
4131
  // sessions root (where summaryFor() would never find it).
3932
4132
  dir: path13.dirname(file),
3933
4133
  filePath: file,
3934
4134
  secretScrubber: this.secretScrubber,
3935
4135
  checkpointCas: this.checkpointCas,
4136
+ onAppend: this.onAppend,
4137
+ onAppendBatch: this.onAppendBatch,
3936
4138
  resolveName: async () => {
3937
4139
  const current = await this.readSummaryManifest(canonicalId);
3938
4140
  if (!current) return null;
@@ -3982,8 +4184,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
3982
4184
  let cacheHit = false;
3983
4185
  try {
3984
4186
  const s = await fsp12.stat(file);
3985
- const stat16 = { mtimeMs: s.mtimeMs, size: s.size };
3986
- const cached = this.loadCache.getFresh(id, stat16, mode.full);
4187
+ const stat15 = { mtimeMs: s.mtimeMs, size: s.size };
4188
+ const cached = this.loadCache.getFresh(id, stat15, mode.full);
3987
4189
  if (cached) {
3988
4190
  cacheHit = true;
3989
4191
  return cached;
@@ -3996,7 +4198,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
3996
4198
  secretScrubber: this.secretScrubber
3997
4199
  });
3998
4200
  if (mode.full) {
3999
- this.loadCache.set(id, stat16, data);
4201
+ this.loadCache.set(id, stat15, data);
4000
4202
  }
4001
4203
  return data;
4002
4204
  } catch (err) {
@@ -4105,7 +4307,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
4105
4307
  await withFileLock(this.indexFile, async () => {
4106
4308
  await this.invalidateShardManifestBySessionId(summary.id);
4107
4309
  const line = JSON.stringify(summary) + "\n";
4108
- await fsp12.appendFile(this.indexFile, line, "utf8");
4310
+ await fsp12.appendFile(this.indexFile, line, { encoding: "utf8", mode: SECRET_FILE_MODE });
4109
4311
  this._indexCache = null;
4110
4312
  this.indexAppendCount++;
4111
4313
  if (this.indexAppendCount >= _DefaultSessionStore.COMPACT_EVERY) {
@@ -4128,7 +4330,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
4128
4330
  await ensureDir(this.dir);
4129
4331
  await withFileLock(this.indexFile, async () => {
4130
4332
  const line = JSON.stringify({ action: "delete", id }) + "\n";
4131
- await fsp12.appendFile(this.indexFile, line, "utf8");
4333
+ await fsp12.appendFile(this.indexFile, line, { encoding: "utf8", mode: SECRET_FILE_MODE });
4132
4334
  this._indexCache = null;
4133
4335
  await this.invalidateShardManifestBySessionId(id);
4134
4336
  this.indexAppendCount++;
@@ -4183,26 +4385,26 @@ var DefaultSessionStore = class _DefaultSessionStore {
4183
4385
  * Returns empty array when the index doesn't exist or is corrupt.
4184
4386
  */
4185
4387
  async readIndex() {
4186
- let stat16;
4388
+ let stat15;
4187
4389
  try {
4188
4390
  const s = await fsp12.stat(this.indexFile);
4189
- stat16 = { mtimeMs: s.mtimeMs, size: s.size, ino: s.ino, birthtimeMs: s.birthtimeMs };
4391
+ stat15 = { mtimeMs: s.mtimeMs, size: s.size, ino: s.ino, birthtimeMs: s.birthtimeMs };
4190
4392
  } catch {
4191
4393
  this._indexCache = null;
4192
4394
  return [];
4193
4395
  }
4194
- if (this._indexCache !== null && this._indexCache.mtimeMs === stat16.mtimeMs && this._indexCache.size === stat16.size && this._indexCache.ino === stat16.ino && this._indexCache.birthtimeMs === stat16.birthtimeMs) {
4396
+ if (this._indexCache !== null && this._indexCache.mtimeMs === stat15.mtimeMs && this._indexCache.size === stat15.size && this._indexCache.ino === stat15.ino && this._indexCache.birthtimeMs === stat15.birthtimeMs) {
4195
4397
  return this._indexCache.summaries;
4196
4398
  }
4197
4399
  const cached = this._indexCache;
4198
- const sameFile = cached !== null && cached.ino === stat16.ino && cached.birthtimeMs === stat16.birthtimeMs;
4199
- if (cached && sameFile && stat16.size > cached.size) {
4200
- const appended = await readFileRange(this.indexFile, cached.size, stat16.size);
4400
+ const sameFile = cached !== null && cached.ino === stat15.ino && cached.birthtimeMs === stat15.birthtimeMs;
4401
+ if (cached && sameFile && stat15.size > cached.size) {
4402
+ const appended = await readFileRange(this.indexFile, cached.size, stat15.size);
4201
4403
  if (appended !== null) {
4202
4404
  applySessionIndexLines(appended.raw, cached.byId, cached.deleted);
4203
4405
  const summaries2 = Array.from(cached.byId.values()).sort(compareSessionSummaries);
4204
4406
  this._indexCache = {
4205
- ...stat16,
4407
+ ...stat15,
4206
4408
  size: appended.end,
4207
4409
  summaries: summaries2,
4208
4410
  byId: cached.byId,
@@ -4223,7 +4425,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
4223
4425
  applySessionIndexLines(raw, byId, deleted);
4224
4426
  const summaries = Array.from(byId.values());
4225
4427
  summaries.sort(compareSessionSummaries);
4226
- this._indexCache = { ...stat16, summaries, byId, deleted };
4428
+ this._indexCache = { ...stat15, summaries, byId, deleted };
4227
4429
  return summaries;
4228
4430
  }
4229
4431
  /**
@@ -4323,8 +4525,8 @@ var DefaultSessionStore = class _DefaultSessionStore {
4323
4525
  if (fromManifest) return fromManifest;
4324
4526
  try {
4325
4527
  const full = this.sessionPath(id, ".jsonl");
4326
- const stat16 = await fsp12.stat(full);
4327
- const summary = await this.summarize(id, stat16.mtime.toISOString());
4528
+ const stat15 = await fsp12.stat(full);
4529
+ const summary = await this.summarize(id, stat15.mtime.toISOString());
4328
4530
  await atomicWrite(manifest, JSON.stringify(summary), { mode: 384 }).catch((err) => {
4329
4531
  const msg = toErrorMessage(err);
4330
4532
  emitSessionStoreError(this.events, id, manifest, "summary_fallback", msg, true);
@@ -4336,12 +4538,28 @@ var DefaultSessionStore = class _DefaultSessionStore {
4336
4538
  });
4337
4539
  outcome = "failure";
4338
4540
  errorMsg = "summary fallback \xE2\u20AC\u201D manifest rebuilt";
4339
- emitSessionStoreRead(this.events, id, manifest, "summary", outcome, Date.now() - t0, errorMsg);
4541
+ emitSessionStoreRead(
4542
+ this.events,
4543
+ id,
4544
+ manifest,
4545
+ "summary",
4546
+ outcome,
4547
+ Date.now() - t0,
4548
+ errorMsg
4549
+ );
4340
4550
  return summary;
4341
4551
  } catch (err) {
4342
4552
  outcome = "failure";
4343
4553
  errorMsg = toErrorMessage(err);
4344
- emitSessionStoreRead(this.events, id, manifest, "summary", outcome, Date.now() - t0, errorMsg);
4554
+ emitSessionStoreRead(
4555
+ this.events,
4556
+ id,
4557
+ manifest,
4558
+ "summary",
4559
+ outcome,
4560
+ Date.now() - t0,
4561
+ errorMsg
4562
+ );
4345
4563
  return {
4346
4564
  id,
4347
4565
  title: "(damaged)",
@@ -4356,14 +4574,7 @@ var DefaultSessionStore = class _DefaultSessionStore {
4356
4574
  const manifest = this.sessionPath(id, ".summary.json");
4357
4575
  try {
4358
4576
  const raw = await fsp12.readFile(manifest, "utf8");
4359
- emitSessionStoreRead(
4360
- this.events,
4361
- id,
4362
- manifest,
4363
- "summary",
4364
- "success",
4365
- Date.now() - startTime
4366
- );
4577
+ emitSessionStoreRead(this.events, id, manifest, "summary", "success", Date.now() - startTime);
4367
4578
  return JSON.parse(raw);
4368
4579
  } catch {
4369
4580
  return null;
@@ -4578,12 +4789,12 @@ var QueueStore = class {
4578
4789
  const t0 = Date.now();
4579
4790
  let raw;
4580
4791
  try {
4581
- const stat16 = await fsp13.stat(this.file);
4582
- if (stat16.size > QUEUE_MAX_BYTES) {
4792
+ const stat15 = await fsp13.stat(this.file);
4793
+ if (stat15.size > QUEUE_MAX_BYTES) {
4583
4794
  this.logWarn("Queue file exceeds the safe read budget; ignoring it", {
4584
4795
  event: "queue_store.read_budget_exceeded",
4585
4796
  path: this.file,
4586
- bytes: stat16.size,
4797
+ bytes: stat15.size,
4587
4798
  maxBytes: QUEUE_MAX_BYTES
4588
4799
  });
4589
4800
  this.events?.emit("storage.read", {
@@ -5034,8 +5245,8 @@ var FileMemoryBackend = class {
5034
5245
  }
5035
5246
  async getMtime(file) {
5036
5247
  try {
5037
- const stat16 = await fs3.stat(file);
5038
- return stat16.mtimeMs;
5248
+ const stat15 = await fs3.stat(file);
5249
+ return stat15.mtimeMs;
5039
5250
  } catch {
5040
5251
  return 0;
5041
5252
  }
@@ -5086,15 +5297,15 @@ var FileMemoryBackend = class {
5086
5297
  const line = `- [${entry.ts}] ${id}${meta} ${entry.text.replace(/\n/g, " ")}
5087
5298
  `;
5088
5299
  await withFileLock(file, async () => {
5089
- const handle = await fs3.open(file, "a+");
5300
+ const handle = await fs3.open(file, "a+", SECRET_FILE_MODE);
5090
5301
  try {
5091
- const stat16 = await handle.stat();
5302
+ const stat15 = await handle.stat();
5092
5303
  let prefix = "";
5093
- if (stat16.size === 0) {
5304
+ if (stat15.size === 0) {
5094
5305
  prefix = "# Agent Memory\n\n";
5095
5306
  } else {
5096
5307
  const tail = Buffer.allocUnsafe(1);
5097
- const { bytesRead } = await handle.read(tail, 0, 1, stat16.size - 1);
5308
+ const { bytesRead } = await handle.read(tail, 0, 1, stat15.size - 1);
5098
5309
  if (bytesRead === 1 && tail[0] !== 10) prefix = "\n";
5099
5310
  }
5100
5311
  await handle.appendFile(`${prefix}${line}`, "utf8");
@@ -6095,6 +6306,9 @@ async function readProviderSnapshot(configPath, vault, warn) {
6095
6306
  if (typeof decrypted.uiLocale === "string" && decrypted.uiLocale)
6096
6307
  snapshot.uiLocale = decrypted.uiLocale;
6097
6308
  if (Array.isArray(decrypted.fallbackModels)) snapshot.fallbackModels = decrypted.fallbackModels;
6309
+ if (typeof decrypted.fallbackBridge === "string" && decrypted.fallbackBridge.trim()) {
6310
+ snapshot.fallbackBridge = decrypted.fallbackBridge.trim();
6311
+ }
6098
6312
  if (decrypted.fallbackProfiles) snapshot.fallbackProfiles = decrypted.fallbackProfiles;
6099
6313
  if (Array.isArray(decrypted.favoriteModels)) snapshot.favoriteModels = decrypted.favoriteModels;
6100
6314
  if (typeof decrypted.favoriteModelsOnly === "boolean")
@@ -6112,6 +6326,7 @@ function serializeSnapshot(s) {
6112
6326
  baseUrl: s.baseUrl ?? null,
6113
6327
  uiLocale: s.uiLocale ?? null,
6114
6328
  fallbackModels: s.fallbackModels ?? null,
6329
+ fallbackBridge: s.fallbackBridge ?? null,
6115
6330
  fallbackProfiles: s.fallbackProfiles ?? null,
6116
6331
  favoriteModels: s.favoriteModels ?? null,
6117
6332
  favoriteModelsOnly: s.favoriteModelsOnly ?? null,
@@ -6326,7 +6541,13 @@ var CONFIG_BEHAVIOR_DEFAULTS = {
6326
6541
  // models still get cost savings without capability loss. Explicit tiers
6327
6542
  // are respected verbatim.
6328
6543
  tokenSavingMode: "auto",
6329
- allowOutsideProjectRoot: true
6544
+ // Derived, never written by hand: this and `tools.restrictToProjectRoot`
6545
+ // (line 41) are the two halves of one switch, and they used to be declared
6546
+ // independently in this same object — one `true`, one `true` — with
6547
+ // session.ts resolving the contradiction via `??` in favour of the
6548
+ // permissive half. The confinement guard therefore shipped disabled on a
6549
+ // fresh install even though the other half asked for it (WS-075).
6550
+ allowOutsideProjectRoot: !DEFAULT_TOOLS_CONFIG.restrictToProjectRoot
6330
6551
  },
6331
6552
  Sage: {
6332
6553
  enabled: true,
@@ -6405,7 +6626,9 @@ var CONFIG_BEHAVIOR_DEFAULTS = {
6405
6626
  enhance: true,
6406
6627
  enhanceDelayMs: 6e4,
6407
6628
  enhanceLanguage: "original",
6408
- statuslineMode: "detailed",
6629
+ // Product-wide statusline density default. Mirrored by the TUI's
6630
+ // DEFAULT_STATUSLINE_MODE (packages/tui/src/components/settings-picker-model.ts).
6631
+ statuslineMode: "minimum",
6409
6632
  thinkingWord: DEFAULT_TUI_THINKING_WORD,
6410
6633
  showAgentSwarmPanel: true
6411
6634
  },
@@ -6574,6 +6797,7 @@ var IN_PROJECT_ALLOWED_KEYS = /* @__PURE__ */ new Set([
6574
6797
  "maxConcurrent",
6575
6798
  "uiLocale",
6576
6799
  "fallbackModels",
6800
+ "fallbackBridge",
6577
6801
  "fallbackProfiles",
6578
6802
  "favoriteModels",
6579
6803
  "favoriteModelsOnly",
@@ -6636,6 +6860,7 @@ var KNOWN_CONFIG_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
6636
6860
  "tools",
6637
6861
  "mcpServers",
6638
6862
  "fallbackModels",
6863
+ "fallbackBridge",
6639
6864
  "fallbackProfiles",
6640
6865
  "favoriteModels",
6641
6866
  "favoriteModelsOnly",
@@ -6670,6 +6895,75 @@ var KNOWN_CONFIG_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
6670
6895
  "brain",
6671
6896
  "git"
6672
6897
  ]);
6898
+ var IN_PROJECT_DENIED_PATHS = [
6899
+ {
6900
+ path: "tools.exec.allow",
6901
+ reason: "Extends the exec allow-list; a repo could authorise its own binaries."
6902
+ },
6903
+ { path: "tools.exec.danger", reason: "Weakens the destructive-command banner." },
6904
+ { path: "skills.extraDirs", reason: "Loads skill definitions from repo-chosen directories." },
6905
+ { path: "skills.registryUrl", reason: "Redirects skill installs to a repo-chosen host." },
6906
+ // Deliberately NOT denied: skills.mode and skills.eagerMaxChars. The audit
6907
+ // suggested stripping both, but config-loader-extra.test.ts classifies `mode`
6908
+ // as a safe preference with a stated rationale, and a repo can only use these
6909
+ // to move injection volume in either direction — the trust problem was that
6910
+ // repo-supplied skill bodies were indistinguishable from the user's own, which
6911
+ // the provenance tag in system-prompt-skill-bodies.ts now fixes. Re-classifying
6912
+ // them is a product call.
6913
+ { path: "Sage.storage.directory", reason: "Redirects memory storage outside the profile." },
6914
+ {
6915
+ path: "autonomy.yolo",
6916
+ reason: "Alias for the denied top-level `yolo`, and it wins over the user setting \u2014 a repo-committed config could disable every permission prompt."
6917
+ },
6918
+ {
6919
+ path: "autonomy.defaultMode",
6920
+ reason: "Sets the startup autonomy mode; autonomy is user-owned, never repo-owned."
6921
+ },
6922
+ // Deliberately NOT denied: autonomy.autoProceedDelayMs and
6923
+ // autoProceedMaxIterations. They tune a mode the user has already switched on
6924
+ // rather than granting it, and config-loader-extra.test.ts classifies the
6925
+ // delay as a benign project preference. Re-classifying them is a product call.
6926
+ {
6927
+ path: "launch.autonomy",
6928
+ reason: "Launch-time autonomy mode; same user-owned boundary as autonomy.defaultMode."
6929
+ },
6930
+ {
6931
+ path: "features.allowOutsideProjectRoot",
6932
+ reason: "Short-circuits both the project-root containment check and the symlink realpath check in the file tools."
6933
+ },
6934
+ {
6935
+ path: "tools.restrictToProjectRoot",
6936
+ reason: "The other half of the filesystem confinement switch."
6937
+ }
6938
+ ];
6939
+ function deleteNestedPath(target, path30) {
6940
+ const segments = path30.split(".");
6941
+ const last = segments[segments.length - 1];
6942
+ if (last === void 0) return false;
6943
+ let cursor = target;
6944
+ const chain = [];
6945
+ for (const segment of segments.slice(0, -1)) {
6946
+ const next = cursor[segment];
6947
+ if (!next || typeof next !== "object" || Array.isArray(next)) return false;
6948
+ chain.push({ parent: cursor, key: segment });
6949
+ cursor = next;
6950
+ }
6951
+ if (!(last in cursor)) return false;
6952
+ const clonedLeaf = { ...cursor };
6953
+ delete clonedLeaf[last];
6954
+ let replacement = clonedLeaf;
6955
+ for (let i = chain.length - 1; i >= 0; i--) {
6956
+ const link2 = chain[i];
6957
+ if (!link2) continue;
6958
+ replacement = { ...link2.parent, [link2.key]: replacement };
6959
+ if (i === 0) {
6960
+ for (const [k, v] of Object.entries(replacement)) target[k] = v;
6961
+ return true;
6962
+ }
6963
+ }
6964
+ for (const [k, v] of Object.entries(replacement)) target[k] = v;
6965
+ return true;
6966
+ }
6673
6967
  function assertInProjectAllowListComplete() {
6674
6968
  const missingFromBoth = [];
6675
6969
  for (const key of KNOWN_CONFIG_TOP_LEVEL_KEYS) {
@@ -6699,6 +6993,22 @@ function assertInProjectAllowListComplete() {
6699
6993
  `field(s) appear in BOTH IN_PROJECT_ALLOWED_KEYS and KNOWN_DENIED_IN_PROJECT: ` + duplicate.join(", ") + ". The allow-list wins at runtime; remove from one of the two."
6700
6994
  );
6701
6995
  }
6996
+ const orphanedPaths = IN_PROJECT_DENIED_PATHS.filter(
6997
+ ({ path: path30 }) => !IN_PROJECT_ALLOWED_KEYS.has(path30.split(".")[0] ?? "")
6998
+ ).map(({ path: path30 }) => path30);
6999
+ if (orphanedPaths.length > 0) {
7000
+ problems.push(
7001
+ `IN_PROJECT_DENIED_PATHS entr(ies) whose top-level parent is not allowed: ` + orphanedPaths.join(", ") + ". The parent is already stripped wholesale, so the nested denial is dead \u2014 remove it."
7002
+ );
7003
+ }
7004
+ const malformedPaths = IN_PROJECT_DENIED_PATHS.filter(
7005
+ ({ path: path30 }) => path30.split(".").length < 2 || path30.split(".").some((s) => s.length === 0)
7006
+ ).map(({ path: path30 }) => path30);
7007
+ if (malformedPaths.length > 0) {
7008
+ problems.push(
7009
+ `IN_PROJECT_DENIED_PATHS entr(ies) are not dotted nested paths: ` + malformedPaths.join(", ") + ". Top-level keys belong in KNOWN_DENIED_IN_PROJECT instead."
7010
+ );
7011
+ }
6702
7012
  if (problems.length > 0) {
6703
7013
  throw new Error(
6704
7014
  `stripUnsafeInProjectFields drift check failed:
@@ -6721,58 +7031,8 @@ function stripUnsafeInProjectFields(inProject, sourcePath, warn = (msg) => conso
6721
7031
  }
6722
7032
  stripped.push(k);
6723
7033
  }
6724
- const outTools = out["tools"];
6725
- if (outTools && typeof outTools === "object") {
6726
- const execCfg = outTools["exec"];
6727
- if (execCfg && typeof execCfg === "object") {
6728
- const hasAllow = "allow" in execCfg;
6729
- const hasDanger = "danger" in execCfg;
6730
- if (hasAllow || hasDanger) {
6731
- const clonedExec = { ...execCfg };
6732
- if (hasAllow) {
6733
- delete clonedExec["allow"];
6734
- stripped.push("tools.exec.allow");
6735
- }
6736
- if (hasDanger) {
6737
- delete clonedExec["danger"];
6738
- stripped.push("tools.exec.danger");
6739
- }
6740
- out["tools"] = {
6741
- ...outTools,
6742
- exec: clonedExec
6743
- };
6744
- }
6745
- }
6746
- }
6747
- const outSkills = out["skills"];
6748
- if (outSkills && typeof outSkills === "object") {
6749
- const skillsRec = outSkills;
6750
- const needsClone = "extraDirs" in skillsRec || "registryUrl" in skillsRec;
6751
- if (needsClone) {
6752
- const clonedSkills = { ...skillsRec };
6753
- if ("extraDirs" in clonedSkills) {
6754
- delete clonedSkills["extraDirs"];
6755
- stripped.push("skills.extraDirs");
6756
- }
6757
- if ("registryUrl" in clonedSkills) {
6758
- delete clonedSkills["registryUrl"];
6759
- stripped.push("skills.registryUrl");
6760
- }
6761
- out["skills"] = clonedSkills;
6762
- }
6763
- }
6764
- const outSage = out["Sage"];
6765
- if (outSage && typeof outSage === "object") {
6766
- const storage = outSage["storage"];
6767
- if (storage && typeof storage === "object" && "directory" in storage) {
6768
- const clonedStorage = { ...storage };
6769
- delete clonedStorage["directory"];
6770
- out["Sage"] = {
6771
- ...outSage,
6772
- storage: clonedStorage
6773
- };
6774
- stripped.push("Sage.storage.directory");
6775
- }
7034
+ for (const { path: path30 } of IN_PROJECT_DENIED_PATHS) {
7035
+ if (deleteNestedPath(out, path30)) stripped.push(path30);
6776
7036
  }
6777
7037
  if (stripped.length > 0) {
6778
7038
  warn(
@@ -7233,8 +7493,8 @@ var DefaultConfigLoader = class _DefaultConfigLoader {
7233
7493
  const t0 = Date.now();
7234
7494
  let mtimeMs = null;
7235
7495
  try {
7236
- const stat16 = await fs6.stat(file);
7237
- mtimeMs = stat16.mtimeMs;
7496
+ const stat15 = await fs6.stat(file);
7497
+ mtimeMs = stat15.mtimeMs;
7238
7498
  const cached = this.jsonCache.get(file);
7239
7499
  if (cached && cached.mtimeMs === mtimeMs) {
7240
7500
  return structuredClone(cached.value);
@@ -7572,72 +7832,15 @@ function isLockFile(v) {
7572
7832
  var defaultIsPidAlive = isPidAlive;
7573
7833
 
7574
7834
  // src/storage/session-reader.ts
7575
- import * as fs7 from "node:fs/promises";
7576
- var DefaultSessionReader = class _DefaultSessionReader {
7835
+ var DefaultSessionReader = class {
7577
7836
  store;
7578
7837
  secretScrubber;
7579
- eventCache = /* @__PURE__ */ new Map();
7580
- eventCacheMtimes = /* @__PURE__ */ new Map();
7581
- eventCacheSizes = /* @__PURE__ */ new Map();
7582
- eventCacheBytes = 0;
7583
- static EVENT_CACHE_MAX_ENTRIES = 32;
7584
- static EVENT_CACHE_MAX_BYTES = 32 * 1024 * 1024;
7585
7838
  constructor(opts) {
7586
7839
  this.store = opts.store;
7587
7840
  this.secretScrubber = opts.secretScrubber ?? new DefaultSecretScrubber();
7588
7841
  }
7589
7842
  async loadCachedSessionData(sessionId) {
7590
- const storeWithPath = this.store;
7591
- const rootDir = storeWithPath.dir;
7592
- if (!rootDir) {
7593
- return scrubPersistedSessionData(await this.store.load(sessionId), this.secretScrubber);
7594
- }
7595
- const sessionPath2 = sessionScopedPath(rootDir, sessionId, ".jsonl");
7596
- let mtimeMs = null;
7597
- let fileSize = 0;
7598
- try {
7599
- const stat16 = await fs7.stat(sessionPath2);
7600
- mtimeMs = stat16.mtimeMs;
7601
- fileSize = stat16.size;
7602
- } catch {
7603
- this.deleteEventCacheEntry(sessionId);
7604
- return scrubPersistedSessionData(await this.store.load(sessionId), this.secretScrubber);
7605
- }
7606
- const cachedMtime = this.eventCacheMtimes.get(sessionId);
7607
- const cachedData = this.eventCache.get(sessionId);
7608
- if (cachedData && cachedMtime === mtimeMs) {
7609
- this.eventCache.delete(sessionId);
7610
- this.eventCacheMtimes.delete(sessionId);
7611
- this.eventCacheSizes.delete(sessionId);
7612
- this.eventCache.set(sessionId, cachedData);
7613
- this.eventCacheMtimes.set(sessionId, mtimeMs);
7614
- this.eventCacheSizes.set(sessionId, fileSize);
7615
- return cachedData;
7616
- }
7617
- const data = scrubPersistedSessionData(await this.store.load(sessionId), this.secretScrubber);
7618
- this.deleteEventCacheEntry(sessionId);
7619
- if (fileSize <= _DefaultSessionReader.EVENT_CACHE_MAX_BYTES) {
7620
- while (this.eventCache.size >= _DefaultSessionReader.EVENT_CACHE_MAX_ENTRIES || this.eventCacheBytes + fileSize > _DefaultSessionReader.EVENT_CACHE_MAX_BYTES) {
7621
- const oldest = this.eventCache.keys().next().value;
7622
- if (oldest === void 0) break;
7623
- this.deleteEventCacheEntry(oldest);
7624
- }
7625
- this.eventCache.set(sessionId, data);
7626
- this.eventCacheMtimes.set(sessionId, mtimeMs);
7627
- this.eventCacheSizes.set(sessionId, fileSize);
7628
- this.eventCacheBytes += fileSize;
7629
- }
7630
- if (data.metadata.endedAt) {
7631
- storeWithPath.clearLoadCache?.(sessionId);
7632
- }
7633
- return data;
7634
- }
7635
- deleteEventCacheEntry(sessionId) {
7636
- const size = this.eventCacheSizes.get(sessionId) ?? 0;
7637
- this.eventCache.delete(sessionId);
7638
- this.eventCacheMtimes.delete(sessionId);
7639
- this.eventCacheSizes.delete(sessionId);
7640
- this.eventCacheBytes = Math.max(0, this.eventCacheBytes - size);
7843
+ return scrubPersistedSessionData(await this.store.load(sessionId), this.secretScrubber);
7641
7844
  }
7642
7845
  async query(q = {}) {
7643
7846
  const storeWithFilter = this.store;
@@ -7727,11 +7930,12 @@ var DefaultSessionReader = class _DefaultSessionReader {
7727
7930
  id,
7728
7931
  (rawEvent, eventIndex) => {
7729
7932
  const ev = scrubPersistedSessionEvent(rawEvent, this.secretScrubber);
7730
- scrubbedByIndex.set(eventIndex, ev);
7731
7933
  if (allowedTypes && !allowedTypes.has(ev.type)) return false;
7732
7934
  const text = eventText(ev);
7733
7935
  if (text === null) return false;
7734
- return matcher(text) !== null;
7936
+ const matched2 = matcher(text) !== null;
7937
+ if (matched2) scrubbedByIndex.set(eventIndex, ev);
7938
+ return matched2;
7735
7939
  },
7736
7940
  { limit: limit - hits.length }
7737
7941
  );
@@ -7979,7 +8183,7 @@ function renderPlainText(meta, events) {
7979
8183
 
7980
8184
  // src/storage/annotations-store.ts
7981
8185
  import { randomUUID as randomUUID3 } from "node:crypto";
7982
- import * as fs8 from "node:fs/promises";
8186
+ import * as fs7 from "node:fs/promises";
7983
8187
  var FILE_VERSION = 1;
7984
8188
  var MAX_TEXT_LENGTH = 2e3;
7985
8189
  var MAX_ANNOTATIONS = 1e3;
@@ -8206,7 +8410,7 @@ var AnnotationsStore = class {
8206
8410
  const fp = this.filePath(sessionId);
8207
8411
  let raw;
8208
8412
  try {
8209
- raw = await fs8.readFile(fp, "utf8");
8413
+ raw = await fs7.readFile(fp, "utf8");
8210
8414
  } catch (err) {
8211
8415
  if (err.code === "ENOENT") return null;
8212
8416
  throw err;
@@ -8244,7 +8448,7 @@ var AnnotationsStore = class {
8244
8448
  };
8245
8449
 
8246
8450
  // src/storage/replay-log-store.ts
8247
- import * as fs9 from "node:fs/promises";
8451
+ import * as fs8 from "node:fs/promises";
8248
8452
  import * as path21 from "node:path";
8249
8453
 
8250
8454
  // src/replay/hash.ts
@@ -8335,12 +8539,12 @@ var ReplayLogStore = class _ReplayLogStore {
8335
8539
  const line = JSON.stringify(entry) + "\n";
8336
8540
  let offset2 = 0;
8337
8541
  try {
8338
- const stat16 = await fs9.stat(fp);
8339
- offset2 = stat16.size;
8542
+ const stat15 = await fs8.stat(fp);
8543
+ offset2 = stat15.size;
8340
8544
  } catch (err) {
8341
8545
  if (err.code !== "ENOENT") throw err;
8342
8546
  }
8343
- await fs9.appendFile(fp, line, "utf8");
8547
+ await fs8.appendFile(fp, line, { encoding: "utf8", mode: SECRET_FILE_MODE });
8344
8548
  cache.set(hash, { entry, offset: offset2, length: Buffer.byteLength(line, "utf8") });
8345
8549
  this.diskCount.set(input.sessionId, currentCount + 1);
8346
8550
  this.events?.emit("storage.write", {
@@ -8468,7 +8672,7 @@ var ReplayLogStore = class _ReplayLogStore {
8468
8672
  const scan = async (dir, prefix, depth) => {
8469
8673
  let entries;
8470
8674
  try {
8471
- entries = await fs9.readdir(dir, { withFileTypes: true });
8675
+ entries = await fs8.readdir(dir, { withFileTypes: true });
8472
8676
  } catch (err) {
8473
8677
  if (depth === 0 && err.code !== "ENOENT") {
8474
8678
  console.warn(
@@ -8508,7 +8712,7 @@ var ReplayLogStore = class _ReplayLogStore {
8508
8712
  return sessionScopedPath(this.dir, sessionId, ".replay.jsonl");
8509
8713
  }
8510
8714
  async countEntries(filePath) {
8511
- const handle = await fs9.open(filePath, "r");
8715
+ const handle = await fs8.open(filePath, "r");
8512
8716
  const buffer = Buffer.allocUnsafe(64 * 1024);
8513
8717
  let count = 0;
8514
8718
  let hasNonWhitespace2 = false;
@@ -8549,7 +8753,7 @@ var ReplayLogStore = class _ReplayLogStore {
8549
8753
  async readAll(sessionId) {
8550
8754
  const fp = this.filePath(sessionId);
8551
8755
  try {
8552
- const raw = await fs9.readFile(fp, "utf8");
8756
+ const raw = await fs8.readFile(fp, "utf8");
8553
8757
  const out = [];
8554
8758
  for (const line of raw.split("\n")) {
8555
8759
  if (!line.trim()) continue;
@@ -8589,7 +8793,7 @@ var ReplayLogStore = class _ReplayLogStore {
8589
8793
  const fp = this.filePath(sessionId);
8590
8794
  cache = /* @__PURE__ */ new Map();
8591
8795
  try {
8592
- const handle = await fs9.open(fp, "r");
8796
+ const handle = await fs8.open(fp, "r");
8593
8797
  const CHUNK = 64 * 1024;
8594
8798
  const buffer = Buffer.alloc(CHUNK);
8595
8799
  let leftover = "";
@@ -8675,7 +8879,7 @@ var ReplayLogStore = class _ReplayLogStore {
8675
8879
  const fp = this.filePath(sessionId);
8676
8880
  let handle;
8677
8881
  try {
8678
- handle = await fs9.open(fp, "r");
8882
+ handle = await fs8.open(fp, "r");
8679
8883
  } catch {
8680
8884
  return null;
8681
8885
  }
@@ -8703,7 +8907,7 @@ var ReplayLogStore = class _ReplayLogStore {
8703
8907
 
8704
8908
  // src/storage/session-recovery.ts
8705
8909
  import { createReadStream as createReadStream4 } from "node:fs";
8706
- import * as fs10 from "node:fs/promises";
8910
+ import * as fs9 from "node:fs/promises";
8707
8911
  import * as path22 from "node:path";
8708
8912
  import { createInterface as createInterface4 } from "node:readline";
8709
8913
  var SessionRecovery = class _SessionRecovery {
@@ -8741,16 +8945,16 @@ var SessionRecovery = class _SessionRecovery {
8741
8945
  }
8742
8946
  async detectStaleExact(sessionId) {
8743
8947
  const fp = this.filePath(sessionId);
8744
- let stat16;
8948
+ let stat15;
8745
8949
  try {
8746
- stat16 = await fs10.stat(fp);
8950
+ stat15 = await fs9.stat(fp);
8747
8951
  } catch (err) {
8748
8952
  if (err.code === "ENOENT") return null;
8749
8953
  return null;
8750
8954
  }
8751
- if (stat16.size === 0) return null;
8955
+ if (stat15.size === 0) return null;
8752
8956
  try {
8753
- const scan = await scanLatestLifecycleBoundary(fp, stat16.size);
8957
+ const scan = await scanLatestLifecycleBoundary(fp, stat15.size);
8754
8958
  if (scan?.boundary.type !== "in_flight_start") return null;
8755
8959
  return {
8756
8960
  sessionId,
@@ -8843,7 +9047,7 @@ var SessionRecovery = class _SessionRecovery {
8843
9047
  const collect = async (dir, prefix, depth) => {
8844
9048
  let entries;
8845
9049
  try {
8846
- entries = await fs10.readdir(dir, { withFileTypes: true });
9050
+ entries = await fs9.readdir(dir, { withFileTypes: true });
8847
9051
  } catch {
8848
9052
  return;
8849
9053
  }
@@ -8894,7 +9098,7 @@ function hasNonWhitespace(line) {
8894
9098
  }
8895
9099
  async function scanLatestLifecycleBoundary(filePath, size) {
8896
9100
  const CHUNK_SIZE2 = 64 * 1024;
8897
- const handle = await fs10.open(filePath, "r");
9101
+ const handle = await fs9.open(filePath, "r");
8898
9102
  let position = size;
8899
9103
  let laterLineFragment = Buffer.alloc(0);
8900
9104
  let latestBoundary = null;
@@ -8933,7 +9137,7 @@ async function scanLatestLifecycleBoundary(filePath, size) {
8933
9137
 
8934
9138
  // src/storage/tool-audit-log.ts
8935
9139
  import { createHash as createHash6, randomUUID as randomUUID4 } from "node:crypto";
8936
- import * as fs11 from "node:fs/promises";
9140
+ import * as fs10 from "node:fs/promises";
8937
9141
  var GENESIS_PREV = "0".repeat(64);
8938
9142
  var DEFAULT_FSYNC_EVERY = 100;
8939
9143
  var ToolAuditLog = class {
@@ -9003,9 +9207,15 @@ var ToolAuditLog = class {
9003
9207
  isError: input.isError,
9004
9208
  index
9005
9209
  };
9006
- await fs11.appendFile(fp, JSON.stringify(entry) + "\n", "utf8");
9210
+ await fs10.appendFile(fp, JSON.stringify(entry) + "\n", {
9211
+ encoding: "utf8",
9212
+ // WS-035: this file records raw tool INPUT and OUTPUT. Created
9213
+ // without a mode it lands 0644 — world-readable — while its
9214
+ // siblings under the same directory correctly use 0600.
9215
+ mode: SECRET_FILE_MODE
9216
+ });
9007
9217
  try {
9008
- const st = await fs11.stat(fp);
9218
+ const st = await fs10.stat(fp);
9009
9219
  this.tailStat.set(input.sessionId, { mtimeMs: st.mtimeMs, size: st.size });
9010
9220
  } catch {
9011
9221
  }
@@ -9052,7 +9262,7 @@ var ToolAuditLog = class {
9052
9262
  const cachedStat = this.tailStat.get(sessionId);
9053
9263
  if (cachedHash !== void 0 && cachedIndex !== void 0 && cachedStat) {
9054
9264
  try {
9055
- const st = await fs11.stat(fp);
9265
+ const st = await fs10.stat(fp);
9056
9266
  if (st.mtimeMs === cachedStat.mtimeMs && st.size === cachedStat.size) {
9057
9267
  return { prevHash: cachedHash, nextIndex: cachedIndex };
9058
9268
  }
@@ -9073,7 +9283,7 @@ var ToolAuditLog = class {
9073
9283
  this.tailHash.set(sessionId, prevHash);
9074
9284
  this.tailIndex.set(sessionId, nextIndex);
9075
9285
  try {
9076
- const st = await fs11.stat(fp);
9286
+ const st = await fs10.stat(fp);
9077
9287
  this.tailStat.set(sessionId, { mtimeMs: st.mtimeMs, size: st.size });
9078
9288
  } catch {
9079
9289
  }
@@ -9194,7 +9404,7 @@ var ToolAuditLog = class {
9194
9404
  async readAll(sessionId) {
9195
9405
  const fp = this.filePath(sessionId);
9196
9406
  try {
9197
- const raw = await fs11.readFile(fp, "utf8");
9407
+ const raw = await fs10.readFile(fp, "utf8");
9198
9408
  const out = [];
9199
9409
  for (const line of raw.split("\n")) {
9200
9410
  if (!line.trim()) continue;
@@ -9232,7 +9442,7 @@ var ToolAuditLog = class {
9232
9442
  }
9233
9443
  async sync(sessionId, fp) {
9234
9444
  try {
9235
- const fh = await fs11.open(fp, "r+");
9445
+ const fh = await fs10.open(fp, "r+");
9236
9446
  try {
9237
9447
  await fh.sync();
9238
9448
  } finally {
@@ -9240,7 +9450,7 @@ var ToolAuditLog = class {
9240
9450
  }
9241
9451
  } catch {
9242
9452
  } finally {
9243
- this.unSyncedWrites.set(sessionId, 0);
9453
+ this.unSyncedWrites.delete(sessionId);
9244
9454
  }
9245
9455
  }
9246
9456
  enqueue(sessionId, fn) {
@@ -9371,29 +9581,29 @@ var SessionAnalyzer = class {
9371
9581
  };
9372
9582
 
9373
9583
  // src/session-registry.ts
9374
- import * as fs13 from "node:fs/promises";
9584
+ import * as fs12 from "node:fs/promises";
9375
9585
  import * as path24 from "node:path";
9376
9586
 
9377
9587
  // src/session-registry-atomic-file.ts
9378
9588
  import { randomUUID as randomUUID5 } from "node:crypto";
9379
- import * as fs12 from "node:fs/promises";
9589
+ import * as fs11 from "node:fs/promises";
9380
9590
  import * as path23 from "node:path";
9381
9591
  var STALE_LOCK_MS = 1e4;
9382
9592
  var STALE_TMP_MS = 6e4;
9383
9593
  var MAX_STALE_TMP_FILES = 20;
9384
9594
  async function breakStaleLock(lockPath) {
9385
9595
  try {
9386
- const [stat16, content] = await Promise.all([
9387
- fs12.stat(lockPath),
9596
+ const [stat15, content] = await Promise.all([
9597
+ fs11.stat(lockPath),
9388
9598
  /* v8 ignore start -- best-effort lock-content read; .catch only fires if the lock vanished */
9389
- fs12.readFile(lockPath, "utf8").catch(() => "")
9599
+ fs11.readFile(lockPath, "utf8").catch(() => "")
9390
9600
  /* v8 ignore stop */
9391
9601
  ]);
9392
- const ageMs = Date.now() - stat16.mtimeMs;
9602
+ const ageMs = Date.now() - stat15.mtimeMs;
9393
9603
  const ownerPid = Number.parseInt(content.trim(), 10);
9394
9604
  const ownerDead = Number.isInteger(ownerPid) && ownerPid > 0 && ownerPid !== process.pid && !isPidAlive(ownerPid);
9395
9605
  if (ownerDead || ageMs > STALE_LOCK_MS) {
9396
- await fs12.unlink(lockPath).catch(() => void 0);
9606
+ await fs11.unlink(lockPath).catch(() => void 0);
9397
9607
  return true;
9398
9608
  }
9399
9609
  return false;
@@ -9408,7 +9618,7 @@ async function writeAtomicFile(filePath, registry) {
9408
9618
  );
9409
9619
  let tmpPersisted = false;
9410
9620
  try {
9411
- const handle = await fs12.open(tmp, "w");
9621
+ const handle = await fs11.open(tmp, "w");
9412
9622
  try {
9413
9623
  await handle.writeFile(JSON.stringify(registry, null, 2), "utf8");
9414
9624
  await handle.sync().catch(() => void 0);
@@ -9417,14 +9627,14 @@ async function writeAtomicFile(filePath, registry) {
9417
9627
  }
9418
9628
  tmpPersisted = true;
9419
9629
  try {
9420
- await fs12.rename(tmp, filePath);
9630
+ await fs11.rename(tmp, filePath);
9421
9631
  tmpPersisted = false;
9422
9632
  } catch (renameErr) {
9423
9633
  const code = renameErr?.code;
9424
9634
  if (code === "EPERM" || code === "EBUSY" || code === "EACCES") {
9425
- await fs12.copyFile(tmp, filePath);
9635
+ await fs11.copyFile(tmp, filePath);
9426
9636
  try {
9427
- const destHandle = await fs12.open(filePath, "r+");
9637
+ const destHandle = await fs11.open(filePath, "r+");
9428
9638
  try {
9429
9639
  await destHandle.sync().catch(() => void 0);
9430
9640
  } finally {
@@ -9432,14 +9642,14 @@ async function writeAtomicFile(filePath, registry) {
9432
9642
  }
9433
9643
  } catch {
9434
9644
  }
9435
- await fs12.unlink(tmp).catch(() => void 0);
9645
+ await fs11.unlink(tmp).catch(() => void 0);
9436
9646
  tmpPersisted = false;
9437
9647
  } else {
9438
9648
  throw renameErr;
9439
9649
  }
9440
9650
  }
9441
9651
  } catch (err) {
9442
- if (tmpPersisted) await fs12.unlink(tmp).catch(() => void 0);
9652
+ if (tmpPersisted) await fs11.unlink(tmp).catch(() => void 0);
9443
9653
  throw err;
9444
9654
  }
9445
9655
  }
@@ -9449,17 +9659,17 @@ async function pruneStaleTempFiles(filePath) {
9449
9659
  const base = path23.basename(filePath);
9450
9660
  const now = Date.now();
9451
9661
  const stale = [];
9452
- for (const name of await fs12.readdir(dir)) {
9662
+ for (const name of await fs11.readdir(dir)) {
9453
9663
  const isTemp = (name.startsWith(`${base}.`) || name.startsWith(`.${base}.`)) && name.endsWith(".tmp");
9454
9664
  if (!isTemp) continue;
9455
- const stat16 = await fs12.stat(path23.join(dir, name)).catch(() => null);
9456
- if (!stat16) continue;
9457
- if (now - stat16.mtimeMs > STALE_TMP_MS) stale.push({ name, mtimeMs: stat16.mtimeMs });
9665
+ const stat15 = await fs11.stat(path23.join(dir, name)).catch(() => null);
9666
+ if (!stat15) continue;
9667
+ if (now - stat15.mtimeMs > STALE_TMP_MS) stale.push({ name, mtimeMs: stat15.mtimeMs });
9458
9668
  }
9459
9669
  stale.sort((a, b) => b.mtimeMs - a.mtimeMs);
9460
9670
  await Promise.all(
9461
9671
  stale.slice(MAX_STALE_TMP_FILES).map(async ({ name }) => {
9462
- await fs12.unlink(path23.join(dir, name)).catch(() => void 0);
9672
+ await fs11.unlink(path23.join(dir, name)).catch(() => void 0);
9463
9673
  })
9464
9674
  );
9465
9675
  } catch {
@@ -9762,7 +9972,7 @@ var SessionRegistry = class {
9762
9972
  }
9763
9973
  async readAndPrune() {
9764
9974
  try {
9765
- const raw = await fs13.readFile(this.filePath, "utf8");
9975
+ const raw = await fs12.readFile(this.filePath, "utf8");
9766
9976
  const registry = parseRegistry(raw);
9767
9977
  const observed = new Map(
9768
9978
  Object.entries(registry).map(([id, entry]) => [
@@ -9833,7 +10043,7 @@ var SessionRegistry = class {
9833
10043
  const deadline = Date.now() + waitBudgetMs;
9834
10044
  let attempt = 0;
9835
10045
  try {
9836
- await fs13.mkdir(path24.dirname(this.filePath), { recursive: true });
10046
+ await fs12.mkdir(path24.dirname(this.filePath), { recursive: true });
9837
10047
  await this.maybePruneStaleTempFiles();
9838
10048
  } catch (err) {
9839
10049
  if (required) {
@@ -9845,10 +10055,10 @@ var SessionRegistry = class {
9845
10055
  }
9846
10056
  while (true) {
9847
10057
  try {
9848
- let lockHandle = await fs13.open(lockPath, "wx").catch(() => null);
10058
+ let lockHandle = await fs12.open(lockPath, "wx").catch(() => null);
9849
10059
  if (!lockHandle) {
9850
10060
  if (await breakStaleLock(lockPath)) {
9851
- lockHandle = await fs13.open(lockPath, "wx").catch(() => null);
10061
+ lockHandle = await fs12.open(lockPath, "wx").catch(() => null);
9852
10062
  }
9853
10063
  if (!lockHandle) {
9854
10064
  const remainingMs = deadline - Date.now();
@@ -9863,14 +10073,14 @@ var SessionRegistry = class {
9863
10073
  }
9864
10074
  try {
9865
10075
  await lockHandle.writeFile(String(process.pid)).catch(() => void 0);
9866
- const raw = await fs13.readFile(this.filePath, "utf8").catch(() => "{}");
10076
+ const raw = await fs12.readFile(this.filePath, "utf8").catch(() => "{}");
9867
10077
  const registry = parseRegistry(raw);
9868
10078
  fn(registry);
9869
10079
  await this.writeAtomicWithRetry(registry);
9870
10080
  return;
9871
10081
  } finally {
9872
10082
  await lockHandle.close();
9873
- await fs13.unlink(lockPath).catch(() => void 0);
10083
+ await fs12.unlink(lockPath).catch(() => void 0);
9874
10084
  }
9875
10085
  } catch (err) {
9876
10086
  if (err instanceof SessionOwnershipConflictError) throw err;
@@ -10706,7 +10916,7 @@ var AgentStatusTracker = class _AgentStatusTracker {
10706
10916
  };
10707
10917
 
10708
10918
  // src/fleet-notifier.ts
10709
- import * as fs14 from "node:fs/promises";
10919
+ import * as fs13 from "node:fs/promises";
10710
10920
  import * as path25 from "node:path";
10711
10921
  var INSTANCES_FILE = "webui-instances.json";
10712
10922
  var DISCOVERY_TTL_MS = 2500;
@@ -10766,7 +10976,7 @@ var FleetNotifier = class {
10766
10976
  }
10767
10977
  async discover() {
10768
10978
  try {
10769
- const raw = await fs14.readFile(path25.join(this.baseDir, INSTANCES_FILE), "utf8");
10979
+ const raw = await fs13.readFile(path25.join(this.baseDir, INSTANCES_FILE), "utf8");
10770
10980
  const data = JSON.parse(raw);
10771
10981
  const list = Array.isArray(data?.instances) ? data.instances : [];
10772
10982
  return list.filter((i) => i && typeof i.httpPort === "number").filter((i) => i.pid !== this.selfPid).filter((i) => normRoot(i.projectRoot) === this.projectRoot).filter((i) => pidAlive2(i.pid)).map((i) => {
@@ -12322,6 +12532,11 @@ function applyGoalDeliverableCompletions(goal, completed) {
12322
12532
  (item, index) => completedIndices.has(index) && !isGoalDeliverableComplete(item) ? `\u2705 ${item}` : item
12323
12533
  )
12324
12534
  };
12535
+ next = appendGoalDeliverableJournals(next, completed);
12536
+ return recomputeGoalProgress(next);
12537
+ }
12538
+ function appendGoalDeliverableJournals(goal, completed) {
12539
+ let next = goal;
12325
12540
  for (const item of completed) {
12326
12541
  next = appendJournal(next, {
12327
12542
  source: "deliverable",
@@ -12330,7 +12545,7 @@ function applyGoalDeliverableCompletions(goal, completed) {
12330
12545
  note: `[DONE: ${item.index + 1}]`
12331
12546
  });
12332
12547
  }
12333
- return recomputeGoalProgress(next);
12548
+ return next;
12334
12549
  }
12335
12550
  function recomputeGoalProgress(goal) {
12336
12551
  const deliverables = goal.deliverables ?? [];
@@ -12343,10 +12558,19 @@ async function coordinateGoalIteration(options) {
12343
12558
  const current = await loadGoal(options.goalPath, options.events);
12344
12559
  if (!current) return null;
12345
12560
  const completed = parseCompletedGoalDeliverables(options.finalText, current.deliverables ?? []);
12346
- let goal = applyGoalDeliverableCompletions(current, completed);
12347
- await saveGoal(options.goalPath, goal, options.events);
12348
- const kanbanUpdatedTaskIds = await refreshGoalKanban(options.projectRoot, goal, completed);
12349
- goal = await recomputeGoalProgressFromKanban(options.projectRoot, goal);
12561
+ const board = await resolveGoalBoard(options.projectRoot, current);
12562
+ let kanbanUpdatedTaskIds = [];
12563
+ let goal;
12564
+ if (board) {
12565
+ kanbanUpdatedTaskIds = await refreshGoalKanban(options.projectRoot, current, completed, board);
12566
+ goal = await recomputeGoalProgressFromKanban(options.projectRoot, current);
12567
+ const confirmed = completed.filter(
12568
+ (item) => !isGoalDeliverableComplete(current.deliverables?.[item.index] ?? "") && isGoalDeliverableComplete(goal.deliverables?.[item.index] ?? "")
12569
+ );
12570
+ goal = appendGoalDeliverableJournals(goal, confirmed);
12571
+ } else {
12572
+ goal = applyGoalDeliverableCompletions(current, completed);
12573
+ }
12350
12574
  let reached = false;
12351
12575
  const allComplete = (goal.deliverables?.length ?? 0) > 0 && goal.deliverables?.every(isGoalDeliverableComplete) === true;
12352
12576
  if (allComplete && options.brain) {
@@ -12411,9 +12635,9 @@ async function resolveGoalBoard(projectRoot, goal) {
12411
12635
  }
12412
12636
  return findGoalBoardByTag(projectRoot, goal);
12413
12637
  }
12414
- async function refreshGoalKanban(projectRoot, goal, completed) {
12638
+ async function refreshGoalKanban(projectRoot, goal, completed, resolvedBoard) {
12415
12639
  if (completed.length === 0) return [];
12416
- const board = await resolveGoalBoard(projectRoot, goal);
12640
+ const board = resolvedBoard ?? await resolveGoalBoard(projectRoot, goal);
12417
12641
  if (!board) return [];
12418
12642
  const doneColumn = board.columns.find((column) => column.title.toLowerCase() === "done");
12419
12643
  if (!doneColumn) return [];
@@ -12422,7 +12646,7 @@ async function refreshGoalKanban(projectRoot, goal, completed) {
12422
12646
  const updated = [];
12423
12647
  for (const task of board.tasks) {
12424
12648
  const originMatch = task.origin?.system === "goal" && task.origin?.taskId ? originKeys.has(task.origin.taskId) : false;
12425
- const titleMatch = completedTitles.has(normalizeTitle(task.title));
12649
+ const titleMatch = !task.origin?.taskId && completedTitles.has(normalizeTitle(task.title));
12426
12650
  if (!originMatch && !titleMatch) continue;
12427
12651
  const result = await updateTask(
12428
12652
  projectRoot,
@@ -12441,12 +12665,17 @@ async function refreshGoalKanban(projectRoot, goal, completed) {
12441
12665
  async function recomputeGoalProgressFromKanban(projectRoot, goal) {
12442
12666
  const board = await resolveGoalBoard(projectRoot, goal);
12443
12667
  if (!board || !goal.deliverables?.length) return recomputeGoalProgress(goal);
12444
- const completedTitles = new Set(
12445
- board.tasks.filter((task) => task.status === "completed").map((task) => normalizeTitle(task.title))
12668
+ const tasksByOrigin = new Map(
12669
+ board.tasks.filter((task) => task.origin?.system === "goal" && task.origin.taskId).map((task) => [task.origin.taskId, task])
12446
12670
  );
12447
- const deliverables = goal.deliverables.map((item) => {
12448
- if (isGoalDeliverableComplete(item)) return item;
12449
- return completedTitles.has(normalizeTitle(item)) ? `\u2705 ${item}` : item;
12671
+ const completedLegacyTitles = new Set(
12672
+ board.tasks.filter((task) => !task.origin?.taskId && task.status === "completed").map((task) => normalizeTitle(task.title))
12673
+ );
12674
+ const deliverables = goal.deliverables.map((item, index) => {
12675
+ const text = stripGoalDeliverableMarker(item);
12676
+ const originTask = tasksByOrigin.get(`deliverable:${index}`);
12677
+ const complete = originTask ? originTask.status === "completed" : completedLegacyTitles.has(normalizeTitle(text)) || isGoalDeliverableComplete(item);
12678
+ return complete ? `\u2705 ${text}` : text;
12450
12679
  });
12451
12680
  return recomputeGoalProgress({ ...goal, deliverables });
12452
12681
  }
@@ -12456,7 +12685,7 @@ function normalizeTitle(value) {
12456
12685
 
12457
12686
  // src/storage/prompt-store.ts
12458
12687
  import { createHash as createHash7 } from "node:crypto";
12459
- import * as fs15 from "node:fs/promises";
12688
+ import * as fs14 from "node:fs/promises";
12460
12689
  import * as path27 from "node:path";
12461
12690
  var SCHEMA_VERSION = 2;
12462
12691
  function promptChecksum(content) {
@@ -12472,7 +12701,7 @@ function migratePromptEntry(raw) {
12472
12701
  const tags = Array.isArray(r["tags"]) ? r["tags"].filter((t) => typeof t === "string") : [];
12473
12702
  return {
12474
12703
  id: r["id"],
12475
- slug: typeof r["slug"] === "string" && r["slug"].length > 0 ? r["slug"] : slugify(title),
12704
+ slug: typeof r["slug"] === "string" && r["slug"].length > 0 ? r["slug"] : slugify2(title),
12476
12705
  title,
12477
12706
  description: typeof r["description"] === "string" ? r["description"] : "",
12478
12707
  content,
@@ -12521,12 +12750,12 @@ var DefaultPromptStore = class {
12521
12750
  await ensureDir(this.dir);
12522
12751
  const entries = [];
12523
12752
  try {
12524
- const files = await fs15.readdir(this.dir);
12753
+ const files = await fs14.readdir(this.dir);
12525
12754
  for (const file of files) {
12526
12755
  if (!file.endsWith(".json")) continue;
12527
12756
  try {
12528
12757
  const raw = JSON.parse(
12529
- await fs15.readFile(path27.join(this.dir, file), "utf8")
12758
+ await fs14.readFile(path27.join(this.dir, file), "utf8")
12530
12759
  );
12531
12760
  const migrated = migratePromptEntry(raw.entry);
12532
12761
  if (migrated) entries.push(migrated);
@@ -12542,7 +12771,7 @@ var DefaultPromptStore = class {
12542
12771
  async get(id) {
12543
12772
  const file = path27.join(this.dir, `${id}.json`);
12544
12773
  try {
12545
- const raw = JSON.parse(await fs15.readFile(file, "utf8"));
12774
+ const raw = JSON.parse(await fs14.readFile(file, "utf8"));
12546
12775
  return migratePromptEntry(raw.entry);
12547
12776
  } catch {
12548
12777
  return null;
@@ -12557,7 +12786,7 @@ var DefaultPromptStore = class {
12557
12786
  async delete(id) {
12558
12787
  const file = path27.join(this.dir, `${id}.json`);
12559
12788
  try {
12560
- await fs15.unlink(file);
12789
+ await fs14.unlink(file);
12561
12790
  return true;
12562
12791
  } catch {
12563
12792
  return false;
@@ -12575,7 +12804,7 @@ var DefaultPromptStore = class {
12575
12804
  const now = (/* @__PURE__ */ new Date()).toISOString();
12576
12805
  return {
12577
12806
  id: ulid(),
12578
- slug: extra.slug && extra.slug.length > 0 ? extra.slug : slugify(title),
12807
+ slug: extra.slug && extra.slug.length > 0 ? extra.slug : slugify2(title),
12579
12808
  title,
12580
12809
  description: extra.description ?? "",
12581
12810
  content,
@@ -12596,7 +12825,7 @@ var DefaultPromptStore = class {
12596
12825
  };
12597
12826
 
12598
12827
  // src/storage/prompt-usage-store.ts
12599
- import * as fs16 from "node:fs/promises";
12828
+ import * as fs15 from "node:fs/promises";
12600
12829
  var PromptUsageStore = class {
12601
12830
  constructor(file) {
12602
12831
  this.file = file;
@@ -12616,7 +12845,7 @@ var PromptUsageStore = class {
12616
12845
  return this.cachedUsage;
12617
12846
  }
12618
12847
  try {
12619
- const raw = JSON.parse(await fs16.readFile(this.file, "utf8"));
12848
+ const raw = JSON.parse(await fs15.readFile(this.file, "utf8"));
12620
12849
  if (raw && typeof raw === "object" && raw.usage && typeof raw.usage === "object") {
12621
12850
  this.cachedUsage = raw.usage;
12622
12851
  this.cachedSignature = signature;
@@ -12696,8 +12925,8 @@ var PromptUsageStore = class {
12696
12925
  }
12697
12926
  async fileSignature() {
12698
12927
  try {
12699
- const stat16 = await fs16.stat(this.file);
12700
- return { size: stat16.size, mtimeMs: stat16.mtimeMs, ctimeMs: stat16.ctimeMs };
12928
+ const stat15 = await fs15.stat(this.file);
12929
+ return { size: stat15.size, mtimeMs: stat15.mtimeMs, ctimeMs: stat15.ctimeMs };
12701
12930
  } catch {
12702
12931
  return null;
12703
12932
  }
@@ -12712,7 +12941,7 @@ function cloneUsage(usage) {
12712
12941
  }
12713
12942
 
12714
12943
  // src/storage/input-history-store.ts
12715
- import * as fs17 from "node:fs/promises";
12944
+ import * as fs16 from "node:fs/promises";
12716
12945
  import * as path28 from "node:path";
12717
12946
  var INPUT_HISTORY_DEFAULT_MAX = 100;
12718
12947
  var InputHistoryStore = class {
@@ -12735,7 +12964,7 @@ var InputHistoryStore = class {
12735
12964
  */
12736
12965
  async load() {
12737
12966
  try {
12738
- const raw = JSON.parse(await fs17.readFile(this.file, "utf8"));
12967
+ const raw = JSON.parse(await fs16.readFile(this.file, "utf8"));
12739
12968
  if (raw && typeof raw === "object" && Array.isArray(raw.entries) && raw.entries.every((e) => typeof e === "string")) {
12740
12969
  return raw.entries.slice(0, this.maxEntries);
12741
12970
  }
@@ -12792,7 +13021,7 @@ var InputHistoryStore = class {
12792
13021
  };
12793
13022
 
12794
13023
  // src/storage/cloud-sync.ts
12795
- import * as fs18 from "node:fs/promises";
13024
+ import * as fs17 from "node:fs/promises";
12796
13025
  import * as path29 from "node:path";
12797
13026
  import { createHash as createHash8 } from "node:crypto";
12798
13027
  var ALL_SYNC_CATEGORIES = ["settings", "skills", "prompts", "memory", "history"];
@@ -12957,7 +13186,7 @@ var CloudSync = class {
12957
13186
  }
12958
13187
  async loadState() {
12959
13188
  try {
12960
- const raw = await fs18.readFile(this.statePath, "utf8");
13189
+ const raw = await fs17.readFile(this.statePath, "utf8");
12961
13190
  this.state = JSON.parse(raw);
12962
13191
  } catch {
12963
13192
  this.state = null;
@@ -13082,18 +13311,18 @@ var CloudSync = class {
13082
13311
  const localPath = this.categoryToPath(cat);
13083
13312
  if (!localPath) continue;
13084
13313
  try {
13085
- const stat16 = await fs18.lstat(localPath);
13086
- if (stat16.isSymbolicLink()) continue;
13087
- if (stat16.isDirectory()) {
13314
+ const stat15 = await fs17.lstat(localPath);
13315
+ if (stat15.isSymbolicLink()) continue;
13316
+ if (stat15.isDirectory()) {
13088
13317
  const files = await this.walkDir(localPath, localPath);
13089
13318
  for (const file of files) {
13090
- const content = await fs18.readFile(file, "utf8");
13319
+ const content = await fs17.readFile(file, "utf8");
13091
13320
  const rel = path29.relative(localPath, file).replace(/\\/g, "/");
13092
13321
  entries.push({ path: `data/${cat}/${rel}`, content, mode: "100644" });
13093
13322
  hashes.push(`${cat}/${rel}\0${content}`);
13094
13323
  }
13095
13324
  } else {
13096
- const content = await fs18.readFile(localPath, "utf8");
13325
+ const content = await fs17.readFile(localPath, "utf8");
13097
13326
  entries.push({ path: `data/${cat}`, content, mode: "100644" });
13098
13327
  hashes.push(`${cat}\0${content}`);
13099
13328
  }
@@ -13109,17 +13338,17 @@ var CloudSync = class {
13109
13338
  const localPath = this.categoryToPath(cat);
13110
13339
  if (!localPath) continue;
13111
13340
  try {
13112
- const stat16 = await fs18.lstat(localPath);
13113
- if (stat16.isSymbolicLink()) continue;
13114
- if (stat16.isDirectory()) {
13341
+ const stat15 = await fs17.lstat(localPath);
13342
+ if (stat15.isSymbolicLink()) continue;
13343
+ if (stat15.isDirectory()) {
13115
13344
  const files = await this.walkDir(localPath, localPath);
13116
13345
  for (const file of files) {
13117
- const content = await fs18.readFile(file, "utf8");
13346
+ const content = await fs17.readFile(file, "utf8");
13118
13347
  const rel = path29.relative(localPath, file).replace(/\\/g, "/");
13119
13348
  hashes.push(`${cat}/${rel}\0${content}`);
13120
13349
  }
13121
13350
  } else {
13122
- const content = await fs18.readFile(localPath, "utf8");
13351
+ const content = await fs17.readFile(localPath, "utf8");
13123
13352
  hashes.push(`${cat}\0${content}`);
13124
13353
  }
13125
13354
  } catch {
@@ -13146,7 +13375,7 @@ var CloudSync = class {
13146
13375
  }
13147
13376
  async walkDir(dir, base) {
13148
13377
  const results = [];
13149
- const entries = await fs18.readdir(dir, { withFileTypes: true });
13378
+ const entries = await fs17.readdir(dir, { withFileTypes: true });
13150
13379
  entries.sort((a, b) => a.name.localeCompare(b.name));
13151
13380
  for (const entry of entries) {
13152
13381
  const full = path29.join(dir, entry.name);
@@ -13173,18 +13402,18 @@ async function preparePulledDestination(cat, localPath, destPath, remotePath) {
13173
13402
  if (relativeParent) {
13174
13403
  for (const segment of relativeParent.split(path29.sep)) {
13175
13404
  cursor = path29.join(cursor, segment);
13176
- const stat16 = await lstatIfExists(cursor);
13177
- if (stat16?.isSymbolicLink()) throw unsafePulledSymlinkError(remotePath, cursor);
13405
+ const stat15 = await lstatIfExists(cursor);
13406
+ if (stat15?.isSymbolicLink()) throw unsafePulledSymlinkError(remotePath, cursor);
13178
13407
  }
13179
13408
  }
13180
13409
  }
13181
13410
  const destStat = await lstatIfExists(destPath);
13182
13411
  if (destStat?.isSymbolicLink()) throw unsafePulledSymlinkError(remotePath, destPath);
13183
- await fs18.mkdir(path29.dirname(destPath), { recursive: true });
13412
+ await fs17.mkdir(path29.dirname(destPath), { recursive: true });
13184
13413
  }
13185
13414
  async function lstatIfExists(filePath) {
13186
13415
  try {
13187
- return await fs18.lstat(filePath);
13416
+ return await fs17.lstat(filePath);
13188
13417
  } catch (err) {
13189
13418
  if (err.code === "ENOENT") return null;
13190
13419
  throw err;
@@ -13335,6 +13564,9 @@ function createSessionEventBridge(writer, level = "standard", options = {}) {
13335
13564
  async append(event) {
13336
13565
  const target = resolveWriter();
13337
13566
  if (!target) return;
13567
+ if (event.type === "tool_call_end") {
13568
+ progressCounters.delete(event.id);
13569
+ }
13338
13570
  if (!isAllowed(event.type, currentLevel)) return;
13339
13571
  if (!shouldSample(event)) return;
13340
13572
  try {
@@ -13345,6 +13577,9 @@ function createSessionEventBridge(writer, level = "standard", options = {}) {
13345
13577
  async appendBatch(events) {
13346
13578
  const target = resolveWriter();
13347
13579
  if (!target || events.length === 0) return;
13580
+ for (const e of events) {
13581
+ if (e.type === "tool_call_end") progressCounters.delete(e.id);
13582
+ }
13348
13583
  const allowed = events.filter((e) => isAllowed(e.type, currentLevel) && shouldSample(e));
13349
13584
  if (allowed.length === 0) return;
13350
13585
  try {