devchain-cli 0.23.1 → 0.23.3

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 (261) hide show
  1. package/dist/drizzle/0083_retire_documents.sql +2 -0
  2. package/dist/drizzle/0084_retire_codebase_overview.sql +1 -0
  3. package/dist/drizzle/0085_clumsy_zodiak.sql +166 -0
  4. package/dist/drizzle/meta/0083_snapshot.json +7422 -0
  5. package/dist/drizzle/meta/0084_snapshot.json +7422 -0
  6. package/dist/drizzle/meta/0085_snapshot.json +7446 -0
  7. package/dist/drizzle/meta/_journal.json +21 -0
  8. package/dist/server/app.main.module.js +0 -4
  9. package/dist/server/app.main.module.js.map +1 -1
  10. package/dist/server/app.normal.module.js +0 -4
  11. package/dist/server/app.normal.module.js.map +1 -1
  12. package/dist/server/modules/epic-time/controllers/external-estimate-log.controller.d.ts +1 -0
  13. package/dist/server/modules/epic-time/controllers/external-estimate-log.controller.js +48 -0
  14. package/dist/server/modules/epic-time/controllers/external-estimate-log.controller.js.map +1 -1
  15. package/dist/server/modules/epic-time/models/epic-time.models.d.ts +10 -0
  16. package/dist/server/modules/epic-time/services/epic-estimate-logging.service.d.ts +4 -1
  17. package/dist/server/modules/epic-time/services/epic-estimate-logging.service.js +43 -1
  18. package/dist/server/modules/epic-time/services/epic-estimate-logging.service.js.map +1 -1
  19. package/dist/server/modules/epics/subscribers/epic-assignment-notifier.subscriber.d.ts +1 -0
  20. package/dist/server/modules/epics/subscribers/epic-assignment-notifier.subscriber.js +31 -1
  21. package/dist/server/modules/epics/subscribers/epic-assignment-notifier.subscriber.js.map +1 -1
  22. package/dist/server/modules/external-integrations/connections/stable-integration-connection.d.ts +14 -0
  23. package/dist/server/modules/external-integrations/connections/stable-integration-connection.js +56 -0
  24. package/dist/server/modules/external-integrations/connections/stable-integration-connection.js.map +1 -0
  25. package/dist/server/modules/external-integrations/my-work/external-edit-session.service.d.ts +2 -1
  26. package/dist/server/modules/external-integrations/my-work/external-edit-session.service.js +51 -33
  27. package/dist/server/modules/external-integrations/my-work/external-edit-session.service.js.map +1 -1
  28. package/dist/server/modules/external-integrations/my-work/external-my-work.controller.js +2 -2
  29. package/dist/server/modules/external-integrations/my-work/external-my-work.controller.js.map +1 -1
  30. package/dist/server/modules/external-integrations/my-work/external-my-work.openapi.d.ts +1 -0
  31. package/dist/server/modules/external-integrations/my-work/external-my-work.openapi.js +33 -1
  32. package/dist/server/modules/external-integrations/my-work/external-my-work.openapi.js.map +1 -1
  33. package/dist/server/modules/external-integrations/my-work/external-my-work.service.d.ts +1 -1
  34. package/dist/server/modules/external-integrations/my-work/external-my-work.service.js +41 -48
  35. package/dist/server/modules/external-integrations/my-work/external-my-work.service.js.map +1 -1
  36. package/dist/server/modules/external-integrations/my-work/external-time-mutation.service.js +24 -11
  37. package/dist/server/modules/external-integrations/my-work/external-time-mutation.service.js.map +1 -1
  38. package/dist/server/modules/external-integrations/sessions/external-edit-session.store.js +3 -1
  39. package/dist/server/modules/external-integrations/sessions/external-edit-session.store.js.map +1 -1
  40. package/dist/server/modules/mcp/constants.d.ts +0 -2
  41. package/dist/server/modules/mcp/constants.js +1 -11
  42. package/dist/server/modules/mcp/constants.js.map +1 -1
  43. package/dist/server/modules/mcp/dtos/mcp.dto.d.ts +0 -165
  44. package/dist/server/modules/mcp/dtos/mcp.dto.js +1 -47
  45. package/dist/server/modules/mcp/dtos/mcp.dto.js.map +1 -1
  46. package/dist/server/modules/mcp/services/instructions-resolver.d.ts +4 -13
  47. package/dist/server/modules/mcp/services/instructions-resolver.js +26 -166
  48. package/dist/server/modules/mcp/services/instructions-resolver.js.map +1 -1
  49. package/dist/server/modules/mcp/services/mappers/dto-mappers.d.ts +2 -4
  50. package/dist/server/modules/mcp/services/mappers/dto-mappers.js +0 -22
  51. package/dist/server/modules/mcp/services/mappers/dto-mappers.js.map +1 -1
  52. package/dist/server/modules/mcp/services/mcp-tool-binding.registry.js +1 -2
  53. package/dist/server/modules/mcp/services/mcp-tool-binding.registry.js.map +1 -1
  54. package/dist/server/modules/mcp/services/utils/resource-resolver.d.ts +2 -4
  55. package/dist/server/modules/mcp/services/utils/resource-resolver.js +0 -59
  56. package/dist/server/modules/mcp/services/utils/resource-resolver.js.map +1 -1
  57. package/dist/server/modules/mcp/tool-descriptors/index.js +0 -2
  58. package/dist/server/modules/mcp/tool-descriptors/index.js.map +1 -1
  59. package/dist/server/modules/mcp/tool-descriptors/runtime-bindings.js +0 -2
  60. package/dist/server/modules/mcp/tool-descriptors/runtime-bindings.js.map +1 -1
  61. package/dist/server/modules/projects/helpers/project-import.d.ts +0 -33
  62. package/dist/server/modules/projects/helpers/project-import.js +0 -224
  63. package/dist/server/modules/projects/helpers/project-import.js.map +1 -1
  64. package/dist/server/modules/projects/services/project-registry-import.service.d.ts +3 -6
  65. package/dist/server/modules/projects/services/project-registry-import.service.js +7 -28
  66. package/dist/server/modules/projects/services/project-registry-import.service.js.map +1 -1
  67. package/dist/server/modules/projects/template-codec/codecs/teams.codec.js.map +1 -1
  68. package/dist/server/modules/registry/controllers/registry.controller.js +3 -10
  69. package/dist/server/modules/registry/controllers/registry.controller.js.map +1 -1
  70. package/dist/server/modules/registry/services/registry-orchestration.service.d.ts +11 -3
  71. package/dist/server/modules/registry/services/registry-orchestration.service.js +6 -1
  72. package/dist/server/modules/registry/services/registry-orchestration.service.js.map +1 -1
  73. package/dist/server/modules/session-reader/adapters/claude-session-reader.adapter.d.ts +8 -0
  74. package/dist/server/modules/session-reader/adapters/claude-session-reader.adapter.js +9 -0
  75. package/dist/server/modules/session-reader/adapters/claude-session-reader.adapter.js.map +1 -1
  76. package/dist/server/modules/session-reader/adapters/codex-session-reader.adapter.d.ts +10 -0
  77. package/dist/server/modules/session-reader/adapters/codex-session-reader.adapter.js +13 -0
  78. package/dist/server/modules/session-reader/adapters/codex-session-reader.adapter.js.map +1 -1
  79. package/dist/server/modules/session-reader/adapters/session-reader-adapter.interface.d.ts +20 -1
  80. package/dist/server/modules/session-reader/adapters/session-reader-adapter.interface.js.map +1 -1
  81. package/dist/server/modules/session-reader/controllers/session-reader.controller.d.ts +21 -1
  82. package/dist/server/modules/session-reader/controllers/session-reader.controller.js +49 -3
  83. package/dist/server/modules/session-reader/controllers/session-reader.controller.js.map +1 -1
  84. package/dist/server/modules/session-reader/data/pricing.json +2052 -178
  85. package/dist/server/modules/session-reader/parsers/bounded-line-read.d.ts +2 -0
  86. package/dist/server/modules/session-reader/parsers/bounded-line-read.js +66 -0
  87. package/dist/server/modules/session-reader/parsers/bounded-line-read.js.map +1 -0
  88. package/dist/server/modules/session-reader/parsers/claude-jsonl.parser.d.ts +6 -0
  89. package/dist/server/modules/session-reader/parsers/claude-jsonl.parser.js +44 -13
  90. package/dist/server/modules/session-reader/parsers/claude-jsonl.parser.js.map +1 -1
  91. package/dist/server/modules/session-reader/parsers/codex-jsonl.parser.d.ts +22 -0
  92. package/dist/server/modules/session-reader/parsers/codex-jsonl.parser.js +61 -16
  93. package/dist/server/modules/session-reader/parsers/codex-jsonl.parser.js.map +1 -1
  94. package/dist/server/modules/session-reader/services/bounded-anchor-proof.d.ts +12 -0
  95. package/dist/server/modules/session-reader/services/bounded-anchor-proof.js +103 -0
  96. package/dist/server/modules/session-reader/services/bounded-anchor-proof.js.map +1 -0
  97. package/dist/server/modules/session-reader/services/metrics-merge.d.ts +17 -0
  98. package/dist/server/modules/session-reader/services/metrics-merge.js +114 -0
  99. package/dist/server/modules/session-reader/services/metrics-merge.js.map +1 -0
  100. package/dist/server/modules/session-reader/services/session-cache.service.d.ts +40 -4
  101. package/dist/server/modules/session-reader/services/session-cache.service.js +232 -148
  102. package/dist/server/modules/session-reader/services/session-cache.service.js.map +1 -1
  103. package/dist/server/modules/session-reader/services/session-reader.service.d.ts +15 -1
  104. package/dist/server/modules/session-reader/services/session-reader.service.js +70 -2
  105. package/dist/server/modules/session-reader/services/session-reader.service.js.map +1 -1
  106. package/dist/server/modules/session-reader/services/transcript-watcher.service.d.ts +20 -3
  107. package/dist/server/modules/session-reader/services/transcript-watcher.service.js +538 -197
  108. package/dist/server/modules/session-reader/services/transcript-watcher.service.js.map +1 -1
  109. package/dist/server/modules/sessions/controllers/sessions.controller.d.ts +4 -1
  110. package/dist/server/modules/sessions/controllers/sessions.controller.js +32 -0
  111. package/dist/server/modules/sessions/controllers/sessions.controller.js.map +1 -1
  112. package/dist/server/modules/sessions/services/message-pool.types.d.ts +23 -0
  113. package/dist/server/modules/sessions/services/sessions-message-pool.service.d.ts +5 -2
  114. package/dist/server/modules/sessions/services/sessions-message-pool.service.js +82 -4
  115. package/dist/server/modules/sessions/services/sessions-message-pool.service.js.map +1 -1
  116. package/dist/server/modules/storage/db/schema.d.ts +57 -219
  117. package/dist/server/modules/storage/db/schema.js +15 -27
  118. package/dist/server/modules/storage/db/schema.js.map +1 -1
  119. package/dist/server/modules/storage/interfaces/storage.interface.d.ts +6 -23
  120. package/dist/server/modules/storage/interfaces/storage.interface.js.map +1 -1
  121. package/dist/server/modules/storage/local/delegates/external-estimate-log.delegate.d.ts +6 -1
  122. package/dist/server/modules/storage/local/delegates/external-estimate-log.delegate.js +163 -32
  123. package/dist/server/modules/storage/local/delegates/external-estimate-log.delegate.js.map +1 -1
  124. package/dist/server/modules/storage/local/delegates/external-managed-subtask.delegate.js +11 -1
  125. package/dist/server/modules/storage/local/delegates/external-managed-subtask.delegate.js.map +1 -1
  126. package/dist/server/modules/storage/local/delegates/integration.delegate.d.ts +1 -1
  127. package/dist/server/modules/storage/local/delegates/integration.delegate.js +17 -13
  128. package/dist/server/modules/storage/local/delegates/integration.delegate.js.map +1 -1
  129. package/dist/server/modules/storage/local/delegates/project.delegate.js +1 -10
  130. package/dist/server/modules/storage/local/delegates/project.delegate.js.map +1 -1
  131. package/dist/server/modules/storage/local/local-storage.service.d.ts +6 -9
  132. package/dist/server/modules/storage/local/local-storage.service.js +8 -22
  133. package/dist/server/modules/storage/local/local-storage.service.js.map +1 -1
  134. package/dist/server/modules/storage/models/domain.models.d.ts +14 -20
  135. package/dist/server/modules/storage/models/domain.models.js +2 -1
  136. package/dist/server/modules/storage/models/domain.models.js.map +1 -1
  137. package/dist/server/modules/terminal/services/human-prompt-state.service.d.ts +7 -0
  138. package/dist/server/modules/terminal/services/human-prompt-state.service.js +26 -0
  139. package/dist/server/modules/terminal/services/human-prompt-state.service.js.map +1 -1
  140. package/dist/server/modules/terminal/services/terminal-session/terminal-session.js +3 -2
  141. package/dist/server/modules/terminal/services/terminal-session/terminal-session.js.map +1 -1
  142. package/dist/server/modules/terminal/utils/terminal-activity.js +8 -1
  143. package/dist/server/modules/terminal/utils/terminal-activity.js.map +1 -1
  144. package/dist/server/templates/teams-dev.json +2 -2
  145. package/dist/server/tsconfig.tsbuildinfo +1 -1
  146. package/dist/server/ui/assets/{ExternalRichEditor-FwCAalXO.js → ExternalRichEditor-akgwTdms.js} +24 -24
  147. package/dist/server/ui/assets/ReviewDetailPage-10JU-Gdr.js +1 -0
  148. package/dist/server/ui/assets/ReviewsPage-BIVn-pS5.js +6 -0
  149. package/dist/server/ui/assets/index-Bg8fZbSZ.css +32 -0
  150. package/dist/server/ui/assets/index-DHmiffN1.js +1000 -0
  151. package/dist/server/ui/assets/useReviewSubscription-Xua63feT.js +69 -0
  152. package/dist/server/ui/index.html +2 -2
  153. package/dist/templates/teams-dev.json +2 -2
  154. package/package.json +13 -1
  155. package/dist/server/modules/codebase-overview-analyzer/codebase-overview-analyzer.module.d.ts +0 -2
  156. package/dist/server/modules/codebase-overview-analyzer/codebase-overview-analyzer.module.js +0 -52
  157. package/dist/server/modules/codebase-overview-analyzer/codebase-overview-analyzer.module.js.map +0 -1
  158. package/dist/server/modules/codebase-overview-analyzer/controllers/codebase-overview.controller.d.ts +0 -30
  159. package/dist/server/modules/codebase-overview-analyzer/controllers/codebase-overview.controller.js +0 -230
  160. package/dist/server/modules/codebase-overview-analyzer/controllers/codebase-overview.controller.js.map +0 -1
  161. package/dist/server/modules/codebase-overview-analyzer/repositories/overview-scope.repository.d.ts +0 -24
  162. package/dist/server/modules/codebase-overview-analyzer/repositories/overview-scope.repository.js +0 -191
  163. package/dist/server/modules/codebase-overview-analyzer/repositories/overview-scope.repository.js.map +0 -1
  164. package/dist/server/modules/codebase-overview-analyzer/services/codebase-overview-analyzer.service.d.ts +0 -73
  165. package/dist/server/modules/codebase-overview-analyzer/services/codebase-overview-analyzer.service.js +0 -1133
  166. package/dist/server/modules/codebase-overview-analyzer/services/codebase-overview-analyzer.service.js.map +0 -1
  167. package/dist/server/modules/codebase-overview-analyzer/services/dependency-aggregation.service.d.ts +0 -14
  168. package/dist/server/modules/codebase-overview-analyzer/services/dependency-aggregation.service.js +0 -114
  169. package/dist/server/modules/codebase-overview-analyzer/services/dependency-aggregation.service.js.map +0 -1
  170. package/dist/server/modules/codebase-overview-analyzer/services/district-splitting.service.d.ts +0 -8
  171. package/dist/server/modules/codebase-overview-analyzer/services/district-splitting.service.js +0 -200
  172. package/dist/server/modules/codebase-overview-analyzer/services/district-splitting.service.js.map +0 -1
  173. package/dist/server/modules/codebase-overview-analyzer/services/evidence-query.service.d.ts +0 -11
  174. package/dist/server/modules/codebase-overview-analyzer/services/evidence-query.service.js +0 -164
  175. package/dist/server/modules/codebase-overview-analyzer/services/evidence-query.service.js.map +0 -1
  176. package/dist/server/modules/codebase-overview-analyzer/services/go-adapter.d.ts +0 -2
  177. package/dist/server/modules/codebase-overview-analyzer/services/go-adapter.js +0 -166
  178. package/dist/server/modules/codebase-overview-analyzer/services/go-adapter.js.map +0 -1
  179. package/dist/server/modules/codebase-overview-analyzer/services/hotspot-scoring.service.d.ts +0 -21
  180. package/dist/server/modules/codebase-overview-analyzer/services/hotspot-scoring.service.js +0 -253
  181. package/dist/server/modules/codebase-overview-analyzer/services/hotspot-scoring.service.js.map +0 -1
  182. package/dist/server/modules/codebase-overview-analyzer/services/identity-resolver.service.d.ts +0 -24
  183. package/dist/server/modules/codebase-overview-analyzer/services/identity-resolver.service.js +0 -98
  184. package/dist/server/modules/codebase-overview-analyzer/services/identity-resolver.service.js.map +0 -1
  185. package/dist/server/modules/codebase-overview-analyzer/services/java-adapter.d.ts +0 -2
  186. package/dist/server/modules/codebase-overview-analyzer/services/java-adapter.js +0 -155
  187. package/dist/server/modules/codebase-overview-analyzer/services/java-adapter.js.map +0 -1
  188. package/dist/server/modules/codebase-overview-analyzer/services/language-adapter-registry.service.d.ts +0 -19
  189. package/dist/server/modules/codebase-overview-analyzer/services/language-adapter-registry.service.js +0 -88
  190. package/dist/server/modules/codebase-overview-analyzer/services/language-adapter-registry.service.js.map +0 -1
  191. package/dist/server/modules/codebase-overview-analyzer/services/php-adapter.d.ts +0 -2
  192. package/dist/server/modules/codebase-overview-analyzer/services/php-adapter.js +0 -173
  193. package/dist/server/modules/codebase-overview-analyzer/services/php-adapter.js.map +0 -1
  194. package/dist/server/modules/codebase-overview-analyzer/services/python-adapter.d.ts +0 -2
  195. package/dist/server/modules/codebase-overview-analyzer/services/python-adapter.js +0 -171
  196. package/dist/server/modules/codebase-overview-analyzer/services/python-adapter.js.map +0 -1
  197. package/dist/server/modules/codebase-overview-analyzer/services/ruby-adapter.d.ts +0 -2
  198. package/dist/server/modules/codebase-overview-analyzer/services/ruby-adapter.js +0 -158
  199. package/dist/server/modules/codebase-overview-analyzer/services/ruby-adapter.js.map +0 -1
  200. package/dist/server/modules/codebase-overview-analyzer/services/rust-adapter.d.ts +0 -2
  201. package/dist/server/modules/codebase-overview-analyzer/services/rust-adapter.js +0 -222
  202. package/dist/server/modules/codebase-overview-analyzer/services/rust-adapter.js.map +0 -1
  203. package/dist/server/modules/codebase-overview-analyzer/services/scope-auto-detector.service.d.ts +0 -4
  204. package/dist/server/modules/codebase-overview-analyzer/services/scope-auto-detector.service.js +0 -72
  205. package/dist/server/modules/codebase-overview-analyzer/services/scope-auto-detector.service.js.map +0 -1
  206. package/dist/server/modules/codebase-overview-analyzer/services/scope-resolver.service.d.ts +0 -6
  207. package/dist/server/modules/codebase-overview-analyzer/services/scope-resolver.service.js +0 -37
  208. package/dist/server/modules/codebase-overview-analyzer/services/scope-resolver.service.js.map +0 -1
  209. package/dist/server/modules/codebase-overview-analyzer/services/typescript-adapter.d.ts +0 -2
  210. package/dist/server/modules/codebase-overview-analyzer/services/typescript-adapter.js +0 -108
  211. package/dist/server/modules/codebase-overview-analyzer/services/typescript-adapter.js.map +0 -1
  212. package/dist/server/modules/codebase-overview-analyzer/types/index.d.ts +0 -2
  213. package/dist/server/modules/codebase-overview-analyzer/types/index.js +0 -6
  214. package/dist/server/modules/codebase-overview-analyzer/types/index.js.map +0 -1
  215. package/dist/server/modules/codebase-overview-analyzer/types/overview.types.d.ts +0 -231
  216. package/dist/server/modules/codebase-overview-analyzer/types/overview.types.js +0 -3
  217. package/dist/server/modules/codebase-overview-analyzer/types/overview.types.js.map +0 -1
  218. package/dist/server/modules/codebase-overview-analyzer/types/scope-defaults.d.ts +0 -2
  219. package/dist/server/modules/codebase-overview-analyzer/types/scope-defaults.js +0 -13
  220. package/dist/server/modules/codebase-overview-analyzer/types/scope-defaults.js.map +0 -1
  221. package/dist/server/modules/codebase-overview-analyzer/types/scope.schema.d.ts +0 -54
  222. package/dist/server/modules/codebase-overview-analyzer/types/scope.schema.js +0 -27
  223. package/dist/server/modules/codebase-overview-analyzer/types/scope.schema.js.map +0 -1
  224. package/dist/server/modules/codebase-overview-analyzer/types/scope.types.d.ts +0 -12
  225. package/dist/server/modules/codebase-overview-analyzer/types/scope.types.js +0 -3
  226. package/dist/server/modules/codebase-overview-analyzer/types/scope.types.js.map +0 -1
  227. package/dist/server/modules/codebase-overview-analyzer/utils/constants.d.ts +0 -1
  228. package/dist/server/modules/codebase-overview-analyzer/utils/constants.js +0 -5
  229. package/dist/server/modules/codebase-overview-analyzer/utils/constants.js.map +0 -1
  230. package/dist/server/modules/codebase-overview-analyzer/utils/path-matcher.d.ts +0 -2
  231. package/dist/server/modules/codebase-overview-analyzer/utils/path-matcher.js +0 -17
  232. package/dist/server/modules/codebase-overview-analyzer/utils/path-matcher.js.map +0 -1
  233. package/dist/server/modules/documents/controllers/documents.controller.d.ts +0 -12
  234. package/dist/server/modules/documents/controllers/documents.controller.js +0 -174
  235. package/dist/server/modules/documents/controllers/documents.controller.js.map +0 -1
  236. package/dist/server/modules/documents/documents.module.d.ts +0 -2
  237. package/dist/server/modules/documents/documents.module.js +0 -22
  238. package/dist/server/modules/documents/documents.module.js.map +0 -1
  239. package/dist/server/modules/mcp/services/handlers/document-context.d.ts +0 -8
  240. package/dist/server/modules/mcp/services/handlers/document-context.js +0 -3
  241. package/dist/server/modules/mcp/services/handlers/document-context.js.map +0 -1
  242. package/dist/server/modules/mcp/services/handlers/document-tools.d.ts +0 -6
  243. package/dist/server/modules/mcp/services/handlers/document-tools.js +0 -109
  244. package/dist/server/modules/mcp/services/handlers/document-tools.js.map +0 -1
  245. package/dist/server/modules/mcp/services/utils/document-link-resolver.d.ts +0 -10
  246. package/dist/server/modules/mcp/services/utils/document-link-resolver.js +0 -124
  247. package/dist/server/modules/mcp/services/utils/document-link-resolver.js.map +0 -1
  248. package/dist/server/modules/mcp/tool-descriptors/document.bindings.d.ts +0 -1
  249. package/dist/server/modules/mcp/tool-descriptors/document.bindings.js +0 -19
  250. package/dist/server/modules/mcp/tool-descriptors/document.bindings.js.map +0 -1
  251. package/dist/server/modules/mcp/tool-descriptors/document.metadata.d.ts +0 -2
  252. package/dist/server/modules/mcp/tool-descriptors/document.metadata.js +0 -96
  253. package/dist/server/modules/mcp/tool-descriptors/document.metadata.js.map +0 -1
  254. package/dist/server/modules/storage/local/delegates/document.delegate.d.ts +0 -18
  255. package/dist/server/modules/storage/local/delegates/document.delegate.js +0 -275
  256. package/dist/server/modules/storage/local/delegates/document.delegate.js.map +0 -1
  257. package/dist/server/ui/assets/ReviewDetailPage-D8DVpsJm.js +0 -1
  258. package/dist/server/ui/assets/ReviewsPage-DcWpRFbm.js +0 -6
  259. package/dist/server/ui/assets/index-CV8ydm7w.css +0 -32
  260. package/dist/server/ui/assets/index-Cwe7LxcW.js +0 -1197
  261. package/dist/server/ui/assets/useReviewSubscription-CYoSg-88.js +0 -69
@@ -52,10 +52,15 @@ const session_cache_service_1 = require("./session-cache.service");
52
52
  const session_reader_adapter_factory_1 = require("../adapters/session-reader-adapter.factory");
53
53
  const events_service_1 = require("../../events/services/events.service");
54
54
  const chunk_builder_1 = require("../builders/chunk-builder");
55
+ const bounded_anchor_proof_1 = require("./bounded-anchor-proof");
56
+ const metrics_merge_1 = require("./metrics-merge");
57
+ const estimate_content_tokens_1 = require("../adapters/utils/estimate-content-tokens");
55
58
  const transcript_cursor_1 = require("./transcript-cursor");
56
59
  const transcript_truncation_1 = require("./transcript-truncation");
57
60
  const transcript_serialization_1 = require("./transcript-serialization");
58
61
  const DEBOUNCE_MS = 100;
62
+ const COSTLY_REFRESH_MS = 50;
63
+ const FILE_REFRESH_COOLDOWN_MS = 2_000;
59
64
  const STAT_POLL_INTERVAL_MS = 3_000;
60
65
  const MAX_INCREMENTAL_BYTES = 10 * 1024 * 1024;
61
66
  function requiresCanonicalRefetch(sourceChangeKind) {
@@ -76,18 +81,27 @@ function requiresCanonicalRefetch(sourceChangeKind) {
76
81
  function assertNever(value) {
77
82
  throw new Error(`Unhandled source change kind: ${String(value)}`);
78
83
  }
84
+ function toFreshnessSnapshot(stat) {
85
+ return {
86
+ size: stat.size,
87
+ mtimeMs: stat.mtime.getTime(),
88
+ fileIdentity: `${stat.dev}:${stat.ino}`,
89
+ };
90
+ }
79
91
  let TranscriptWatcherService = TranscriptWatcherService_1 = class TranscriptWatcherService {
80
92
  cacheService;
81
93
  adapterFactory;
82
94
  events;
83
95
  logger = new common_1.Logger(TranscriptWatcherService_1.name);
84
96
  watchers = new Map();
97
+ endingWatchers = new Map();
85
98
  constructor(cacheService, adapterFactory, events) {
86
99
  this.cacheService = cacheService;
87
100
  this.adapterFactory = adapterFactory;
88
101
  this.events = events;
89
102
  }
90
103
  onModuleDestroy() {
104
+ this.endingWatchers.clear();
91
105
  const sessionIds = [...this.watchers.keys()];
92
106
  for (const sessionId of sessionIds) {
93
107
  this.cleanupResources(sessionId);
@@ -114,106 +128,107 @@ let TranscriptWatcherService = TranscriptWatcherService_1 = class TranscriptWatc
114
128
  }
115
129
  const adapter = this.adapterFactory.getAdapter(providerName);
116
130
  const sourceKind = adapter?.sourceKind ?? 'file';
131
+ if (sourceKind === 'db' && !providerSessionId) {
132
+ this.logger.warn({ sessionId, filePath, providerName }, 'DB-backed watcher requires providerSessionId — skipping watcher');
133
+ return;
134
+ }
135
+ const sourceRef = sourceKind === 'db' ? { filePath, providerName, providerSessionId, kind: 'db' } : undefined;
136
+ const state = {
137
+ sessionId,
138
+ filePath,
139
+ providerName,
140
+ sourceKind,
141
+ sourceRef,
142
+ watchPath: sourceKind === 'db' ? `${filePath}-wal` : filePath,
143
+ fsWatcher: null,
144
+ pollTimer: null,
145
+ debounceTimer: null,
146
+ active: true,
147
+ pending: false,
148
+ nextEligibleAt: 0,
149
+ lastDev: 0,
150
+ lastIno: 0,
151
+ lastSize: 0,
152
+ replacementPending: false,
153
+ pendingCreation: false,
154
+ lastMessageCount: 0,
155
+ lastChunkCount: 0,
156
+ lastSourceVersion: 0,
157
+ lastMetrics: { totalTokens: 0, inputTokens: 0, outputTokens: 0, costUsd: 0, messageCount: 0 },
158
+ lastSummaryMetrics: null,
159
+ inLane: false,
160
+ costlyLanePass: false,
161
+ };
162
+ this.endingWatchers.delete(sessionId);
163
+ this.watchers.set(sessionId, state);
117
164
  let stat = null;
118
165
  try {
119
166
  stat = await fsPromises.stat(filePath);
120
167
  }
121
168
  catch (error) {
169
+ if (!this.isCurrent(state))
170
+ return;
122
171
  const fsCode = error.code;
123
172
  if (sourceKind !== 'file' || fsCode !== 'ENOENT') {
124
173
  this.logger.warn({ sessionId, filePath, fsCode }, 'Cannot stat transcript file — skipping watcher');
174
+ this.cleanupResources(sessionId);
125
175
  return;
126
176
  }
127
- this.logger.debug({ sessionId, filePath }, 'Transcript file not created yet — waiting with stat poll');
177
+ state.pendingCreation = true;
128
178
  }
129
- let sourceRef;
130
- if (sourceKind === 'db') {
131
- if (!providerSessionId) {
132
- this.logger.warn({ sessionId, filePath, providerName }, 'DB-backed watcher requires providerSessionId — skipping watcher');
133
- return;
134
- }
135
- sourceRef = { filePath, providerName, providerSessionId, kind: 'db' };
136
- }
137
- const watchPath = sourceKind === 'db' ? `${filePath}-wal` : filePath;
138
- let fsWatcher = null;
179
+ if (!this.isCurrent(state))
180
+ return;
181
+ state.lastDev = stat?.dev ?? 0;
182
+ state.lastIno = stat?.ino ?? 0;
183
+ state.lastSize = stat?.size ?? 0;
184
+ state.lastSourceVersion = stat?.size ?? 0;
139
185
  if (stat) {
140
186
  try {
141
- fsWatcher = this.createFsWatcher(sessionId, watchPath);
187
+ state.fsWatcher = this.createFsWatcher(state);
142
188
  }
143
189
  catch {
144
- this.logger.warn({ sessionId, watchPath }, 'fs.watch failed to start — using poll only (expected for not-yet-created -wal)');
190
+ this.logger.warn({ sessionId, watchPath: state.watchPath }, 'fs.watch failed to start — using poll only (expected for not-yet-created -wal)');
145
191
  }
146
192
  }
147
- let lastMessageCount = 0;
148
- let lastChunkCount = 0;
149
- let lastSourceVersion = stat?.size ?? 0;
150
- let lastFreshnessToken;
151
- let lastMetrics = {
152
- totalTokens: 0,
153
- inputTokens: 0,
154
- outputTokens: 0,
155
- costUsd: 0,
156
- messageCount: 0,
157
- };
158
- let lastSummaryMetrics = null;
159
193
  try {
160
- if (adapter && sourceKind === 'db' && sourceRef) {
161
- const { session, sourceVersion } = await this.cacheService.getOrParseWithMeta(sessionId, sourceRef, adapter);
162
- lastMessageCount = session.metrics.messageCount;
163
- lastChunkCount = (0, chunk_builder_1.buildChunks)(session.messages).length;
164
- lastSourceVersion = sourceVersion;
165
- lastMetrics = this.toMetricsSnapshot(session.metrics);
166
- lastSummaryMetrics = session.metrics;
167
- if (adapter.getFreshnessToken) {
168
- lastFreshnessToken = await adapter.getFreshnessToken(sourceRef);
194
+ if (adapter && (sourceKind === 'db' || (stat && stat.size > 0))) {
195
+ const seeded = this.isLaneEligible(adapter) && !this.cacheService.getEntry(sessionId)
196
+ ? await this.buildLaneFromSummary(state, adapter)
197
+ : false;
198
+ if (!this.isCurrent(state))
199
+ return;
200
+ if (!seeded) {
201
+ const result = await this.cacheService.getOrParseWithMeta(sessionId, sourceRef ?? filePath, adapter);
202
+ if (!this.isCurrent(state))
203
+ return;
204
+ this.applyBodyResultToState(state, adapter, result, stat);
205
+ }
206
+ if (sourceRef && adapter.getFreshnessToken) {
207
+ const token = await adapter.getFreshnessToken(sourceRef);
208
+ if (!this.isCurrent(state))
209
+ return;
210
+ state.lastFreshnessToken = token;
169
211
  }
170
- }
171
- else if (adapter && stat && stat.size > 0) {
172
- const { session, sourceVersion } = await this.cacheService.getOrParseWithMeta(sessionId, filePath, adapter);
173
- lastMessageCount = session.metrics.messageCount;
174
- lastChunkCount = (0, chunk_builder_1.buildChunks)(session.messages).length;
175
- lastSourceVersion = sourceVersion;
176
- lastMetrics = this.toMetricsSnapshot(session.metrics);
177
- lastSummaryMetrics = session.metrics;
178
212
  }
179
213
  }
180
214
  catch (error) {
215
+ if (!this.isCurrent(state))
216
+ return;
181
217
  this.logger.warn({ error, sessionId }, 'Failed to seed watcher state — starting from zero');
182
218
  }
183
- const pollTimer = setInterval(() => {
184
- this.pollSource(sessionId).catch((error) => {
185
- this.logger.error({ error, sessionId }, 'Source poll error');
186
- });
187
- }, STAT_POLL_INTERVAL_MS);
188
- this.watchers.set(sessionId, {
189
- sessionId,
190
- filePath,
191
- watchPath,
192
- providerName,
193
- sourceKind,
194
- sourceRef,
195
- fsWatcher,
196
- pollTimer,
197
- debounceTimer: null,
198
- lastDev: stat?.dev ?? 0,
199
- lastIno: stat?.ino ?? 0,
200
- lastSize: stat?.size ?? 0,
201
- replacementPending: false,
202
- pendingCreation: stat === null,
203
- lastMessageCount,
204
- lastChunkCount,
205
- lastMetrics,
206
- lastSummaryMetrics,
207
- lastFreshnessToken,
208
- lastSourceVersion,
209
- });
219
+ if (!this.isCurrent(state))
220
+ return;
221
+ state.pollTimer = setInterval(() => this.requestRefresh(state, true), STAT_POLL_INTERVAL_MS);
222
+ state.active = false;
223
+ if (state.pending)
224
+ this.requestRefresh(state);
210
225
  this.logger.log({
211
226
  sessionId,
212
227
  filePath,
213
- watchPath,
228
+ watchPath: state.watchPath,
214
229
  sourceKind,
215
- pendingCreation: stat === null,
216
- hasFsWatch: !!fsWatcher,
230
+ pendingCreation: state.pendingCreation,
231
+ hasFsWatch: !!state.fsWatcher,
217
232
  }, 'Started transcript watcher');
218
233
  }
219
234
  toMetricsSnapshot(metrics) {
@@ -231,24 +246,26 @@ let TranscriptWatcherService = TranscriptWatcherService_1 = class TranscriptWatc
231
246
  return;
232
247
  const { filePath, providerName, lastMetrics } = state;
233
248
  this.cleanupResources(sessionId);
249
+ this.endingWatchers.set(sessionId, state);
234
250
  let finalMetrics = lastMetrics;
235
251
  try {
236
252
  const adapter = this.adapterFactory.getAdapter(providerName);
237
253
  if (adapter) {
238
- const source = state.sourceKind === 'db' && state.sourceRef ? state.sourceRef : filePath;
239
- const session = await this.cacheService.getOrParse(sessionId, source, adapter);
240
- finalMetrics = {
241
- totalTokens: session.metrics.totalTokens,
242
- inputTokens: session.metrics.inputTokens,
243
- outputTokens: session.metrics.outputTokens,
244
- costUsd: session.metrics.costUsd,
245
- messageCount: session.metrics.messageCount,
246
- };
254
+ if (this.isLaneEligible(adapter) && !this.cacheService.getEntry(sessionId)) {
255
+ finalMetrics = await this.finalizeLaneMetrics(state, adapter, lastMetrics);
256
+ }
257
+ else {
258
+ const source = state.sourceKind === 'db' && state.sourceRef ? state.sourceRef : filePath;
259
+ const session = await this.cacheService.getOrParse(sessionId, source, adapter);
260
+ finalMetrics = this.toMetricsSnapshot(session.metrics);
261
+ }
247
262
  }
248
263
  }
249
264
  catch (error) {
250
265
  this.logger.warn({ error, sessionId }, 'Final parse failed — using last known metrics');
251
266
  }
267
+ if (this.endingWatchers.get(sessionId) !== state)
268
+ return;
252
269
  try {
253
270
  await this.events.publish('session.transcript.ended', {
254
271
  sessionId,
@@ -260,6 +277,10 @@ let TranscriptWatcherService = TranscriptWatcherService_1 = class TranscriptWatc
260
277
  catch (error) {
261
278
  this.logger.error({ error, sessionId }, 'Failed to emit transcript ended event');
262
279
  }
280
+ finally {
281
+ if (this.endingWatchers.get(sessionId) === state)
282
+ this.endingWatchers.delete(sessionId);
283
+ }
263
284
  }
264
285
  get activeWatcherCount() {
265
286
  return this.watchers.size;
@@ -275,40 +296,73 @@ let TranscriptWatcherService = TranscriptWatcherService_1 = class TranscriptWatc
275
296
  if (watcher)
276
297
  watcher.lastSummaryMetrics = null;
277
298
  }
278
- scheduleDebounce(sessionId) {
279
- const state = this.watchers.get(sessionId);
280
- if (!state)
299
+ isCurrent(state) {
300
+ return this.watchers.get(state.sessionId) === state;
301
+ }
302
+ laneOwned(state, ending) {
303
+ const map = ending ? this.endingWatchers : this.watchers;
304
+ return map.get(state.sessionId) === state;
305
+ }
306
+ requestRefresh(state, poll = false) {
307
+ if (!this.isCurrent(state))
308
+ return;
309
+ state.pending = true;
310
+ if (state.active || state.debounceTimer)
311
+ return;
312
+ const remaining = state.nextEligibleAt - performance.now();
313
+ if (poll && remaining <= 0) {
314
+ this.launchRefresh(state, true);
281
315
  return;
282
- if (state.debounceTimer) {
283
- clearTimeout(state.debounceTimer);
284
316
  }
317
+ const delay = remaining > 0 ? remaining : DEBOUNCE_MS;
285
318
  state.debounceTimer = setTimeout(() => {
286
319
  state.debounceTimer = null;
287
- this.handleSourceChanged(sessionId).catch((error) => {
288
- this.logger.error({ error, sessionId }, 'Error in debounced change handler');
289
- });
290
- }, DEBOUNCE_MS);
320
+ this.launchRefresh(state, false);
321
+ }, delay);
291
322
  }
292
- async pollSource(sessionId) {
293
- const state = this.watchers.get(sessionId);
294
- if (!state)
295
- return;
296
- if (state.sourceKind === 'db') {
297
- await this.checkDbPoll(state);
298
- }
299
- else {
300
- await this.checkStatPoll(sessionId);
301
- }
323
+ launchRefresh(state, poll) {
324
+ const pass = this.runRefresh(state, poll);
325
+ state.activeRefresh = pass;
326
+ void pass.finally(() => {
327
+ if (state.activeRefresh === pass)
328
+ state.activeRefresh = undefined;
329
+ });
302
330
  }
303
- async handleSourceChanged(sessionId) {
304
- const state = this.watchers.get(sessionId);
305
- if (!state)
331
+ async runRefresh(state, poll) {
332
+ if (!this.isCurrent(state))
306
333
  return;
307
- if (state.sourceKind === 'db') {
308
- await this.handleDbChanged(sessionId);
334
+ state.active = true;
335
+ state.pending = false;
336
+ state.costlyLanePass = false;
337
+ const startedAt = performance.now();
338
+ try {
339
+ if (poll) {
340
+ if (state.sourceKind === 'db')
341
+ await this.checkDbPoll(state);
342
+ else
343
+ await this.checkStatPoll(state);
344
+ }
345
+ else if (state.sourceKind === 'db') {
346
+ await this.handleDbChanged(state);
347
+ }
348
+ else {
349
+ await this.handleFileChanged(state);
350
+ }
309
351
  }
310
- else {
311
- await this.handleFileChanged(sessionId);
352
+ catch (error) {
353
+ this.logger.error({ error, sessionId: state.sessionId }, 'Source refresh failed');
354
+ }
355
+ finally {
356
+ if (this.isCurrent(state)) {
357
+ state.active = false;
358
+ if (!poll && state.sourceKind === 'file') {
359
+ const completedAt = performance.now();
360
+ const costly = completedAt - startedAt >= COSTLY_REFRESH_MS || state.costlyLanePass;
361
+ state.nextEligibleAt = costly ? completedAt + FILE_REFRESH_COOLDOWN_MS : 0;
362
+ }
363
+ if (state.pending)
364
+ this.requestRefresh(state);
365
+ }
312
366
  }
313
367
  }
314
368
  async checkDbPoll(state) {
@@ -323,19 +377,21 @@ let TranscriptWatcherService = TranscriptWatcherService_1 = class TranscriptWatc
323
377
  this.logger.debug({ error, sessionId: state.sessionId }, 'DB freshness poll failed — retry');
324
378
  return;
325
379
  }
380
+ if (!this.isCurrent(state))
381
+ return;
326
382
  if (JSON.stringify(token) !== JSON.stringify(state.lastFreshnessToken)) {
327
- this.scheduleDebounce(state.sessionId);
383
+ this.requestRefresh(state);
328
384
  }
329
385
  }
330
- async checkStatPoll(sessionId) {
331
- const state = this.watchers.get(sessionId);
332
- if (!state)
333
- return;
386
+ async checkStatPoll(state) {
387
+ const { sessionId } = state;
334
388
  let stat;
335
389
  try {
336
390
  stat = await fsPromises.stat(state.filePath);
337
391
  }
338
392
  catch (error) {
393
+ if (!this.isCurrent(state))
394
+ return;
339
395
  if (error.code === 'ENOENT') {
340
396
  if (state.pendingCreation)
341
397
  return;
@@ -345,6 +401,8 @@ let TranscriptWatcherService = TranscriptWatcherService_1 = class TranscriptWatc
345
401
  }
346
402
  throw error;
347
403
  }
404
+ if (!this.isCurrent(state))
405
+ return;
348
406
  if (state.pendingCreation) {
349
407
  state.pendingCreation = false;
350
408
  state.lastDev = stat.dev;
@@ -352,7 +410,7 @@ let TranscriptWatcherService = TranscriptWatcherService_1 = class TranscriptWatc
352
410
  state.lastSize = 0;
353
411
  state.replacementPending = true;
354
412
  this.reopenFsWatcher(state);
355
- this.scheduleDebounce(sessionId);
413
+ this.requestRefresh(state);
356
414
  this.logger.log({ sessionId, filePath: state.filePath }, 'Transcript file materialized — activating watcher');
357
415
  return;
358
416
  }
@@ -365,36 +423,39 @@ let TranscriptWatcherService = TranscriptWatcherService_1 = class TranscriptWatc
365
423
  state.replacementPending = true;
366
424
  }
367
425
  if (identityChanged || state.replacementPending || stat.size !== state.lastSize) {
368
- this.scheduleDebounce(sessionId);
426
+ this.requestRefresh(state);
369
427
  }
370
428
  }
371
- async handleFileChanged(sessionId) {
372
- const state = this.watchers.get(sessionId);
373
- if (!state)
374
- return;
429
+ async handleFileChanged(state) {
430
+ const { sessionId } = state;
375
431
  try {
376
432
  let stat;
377
433
  try {
378
434
  stat = await fsPromises.stat(state.filePath);
379
435
  }
380
436
  catch (error) {
437
+ if (!this.isCurrent(state))
438
+ return;
381
439
  if (error.code === 'ENOENT') {
440
+ if (state.pendingCreation)
441
+ return;
382
442
  this.logger.warn({ sessionId }, 'Transcript file deleted during change handling');
383
443
  await this.stopWatching(sessionId, 'file.deleted');
384
444
  return;
385
445
  }
386
446
  throw error;
387
447
  }
448
+ if (!this.isCurrent(state))
449
+ return;
388
450
  const identityChanged = stat.dev !== state.lastDev || stat.ino !== state.lastIno;
389
- const isReplacement = state.replacementPending || identityChanged;
390
- if (identityChanged) {
451
+ const isReplacement = state.pendingCreation || state.replacementPending || identityChanged;
452
+ if (identityChanged || state.pendingCreation) {
391
453
  this.logger.debug({ sessionId }, 'Inode rotation detected');
392
454
  this.reopenFsWatcher(state);
393
455
  state.lastDev = stat.dev;
394
456
  state.lastIno = stat.ino;
457
+ state.pendingCreation = false;
395
458
  }
396
- if (stat.size === state.lastSize && !isReplacement)
397
- return;
398
459
  const delta = stat.size - state.lastSize;
399
460
  if (delta > MAX_INCREMENTAL_BYTES) {
400
461
  this.logger.warn({ sessionId, delta, maxBytes: MAX_INCREMENTAL_BYTES }, 'Incremental delta exceeds 10MB bound — proceeding with parse');
@@ -404,77 +465,343 @@ let TranscriptWatcherService = TranscriptWatcherService_1 = class TranscriptWatc
404
465
  this.logger.error({ sessionId, providerName: state.providerName }, 'No adapter found');
405
466
  return;
406
467
  }
407
- const { session, sourceChangeKind, sourceVersion } = await this.cacheService.getOrParseWithMeta(sessionId, state.filePath, adapter);
408
- const boundaryFold = this.cacheService.getEntry(sessionId)?.boundaryFold ?? false;
409
- const newMessageCount = session.metrics.messageCount - state.lastMessageCount;
410
- const isFullRefresh = isReplacement || session.metrics.messageCount < state.lastMessageCount;
411
- if (isFullRefresh) {
412
- this.logger.debug({
413
- sessionId,
414
- isReplacement,
415
- previousMessageCount: state.lastMessageCount,
416
- messageCount: session.metrics.messageCount,
417
- }, 'Transcript source replaced or messageCount deflated — requiring canonical refetch');
468
+ if (this.isLaneEligible(adapter)) {
469
+ const outcome = await this.cacheService.refreshIfPresent(sessionId, state.filePath, adapter);
470
+ if (!this.isCurrent(state))
471
+ return;
472
+ if (!outcome.present) {
473
+ const last = outcome.lastFullParse;
474
+ const readerPaidCostlyFullParse = last !== undefined &&
475
+ last.seq !== state.lastFullParseSeqSeen &&
476
+ last.durationMs >= COSTLY_REFRESH_MS;
477
+ state.lastFullParseSeqSeen = last?.seq;
478
+ await this.runFileLane(state, stat, adapter, isReplacement, {
479
+ readerPaidCostlyFullParse,
480
+ });
481
+ return;
482
+ }
483
+ if (state.inLane) {
484
+ state.lastSourceVersion = outcome.preParse.sourceVersion;
485
+ state.lastMessageCount = outcome.preParse.messageCount;
486
+ state.lastChunkCount = outcome.preParse.chunkCount;
487
+ state.inLane = false;
488
+ }
489
+ await this.publishBodyResult(state, stat, adapter, outcome.result, isReplacement);
490
+ return;
418
491
  }
419
- const chunks = (0, chunk_builder_1.buildChunks)(session.messages);
420
- const prevCursor = (0, transcript_cursor_1.encodeCursor)(state.lastSourceVersion, state.lastMessageCount, state.lastChunkCount);
421
- const cursor = (0, transcript_cursor_1.encodeCursor)(sourceVersion, session.metrics.messageCount, chunks.length);
422
- const replaceFromChunkIndex = isFullRefresh ? 0 : Math.max(0, state.lastChunkCount - 1);
423
- const sliceFromMessage = isFullRefresh ? 0 : state.lastMessageCount;
424
- const newChunkIds = chunks.slice(replaceFromChunkIndex).map((c) => c.id);
425
- const truncatedDeltaMessages = (0, transcript_truncation_1.truncateMessages)(session.messages.slice(sliceFromMessage));
426
- const truncatedDeltaChunks = (0, transcript_truncation_1.truncateChunks)(chunks.slice(replaceFromChunkIndex));
427
- const deltaChunks = truncatedDeltaChunks.map(transcript_serialization_1.serializeChunk);
428
- const deltaMessages = truncatedDeltaMessages.map(transcript_serialization_1.serializeMessage);
429
- state.lastSize = stat.size;
430
- state.lastSourceVersion = sourceVersion;
431
- state.lastDev = stat.dev;
432
- state.lastIno = stat.ino;
433
- state.replacementPending = false;
434
- state.lastMessageCount = session.metrics.messageCount;
435
- state.lastChunkCount = chunks.length;
436
- state.lastMetrics = {
437
- totalTokens: session.metrics.totalTokens,
438
- inputTokens: session.metrics.inputTokens,
439
- outputTokens: session.metrics.outputTokens,
440
- costUsd: session.metrics.costUsd,
441
- messageCount: session.metrics.messageCount,
442
- };
443
- state.lastSummaryMetrics = session.metrics;
444
- if (isReplacement || requiresCanonicalRefetch(sourceChangeKind)) {
445
- await this.events.publish('session.transcript.updated', {
446
- kind: 'full-refetch-required',
447
- sessionId,
448
- transcriptPath: state.filePath,
449
- sourceChangeKind,
450
- });
492
+ const result = await this.cacheService.getOrParseWithMeta(sessionId, state.filePath, adapter);
493
+ if (!this.isCurrent(state))
451
494
  return;
495
+ await this.publishBodyResult(state, stat, adapter, result, isReplacement);
496
+ }
497
+ catch (error) {
498
+ this.logger.error({ error, sessionId }, 'File change handler failed — watcher continues');
499
+ }
500
+ }
501
+ isLaneEligible(adapter) {
502
+ return (adapter.sourceKind ?? 'file') === 'file' && adapter.incrementalMode === 'delta';
503
+ }
504
+ laneRef(state) {
505
+ return (state.sourceRef ?? {
506
+ filePath: state.filePath,
507
+ providerName: state.providerName,
508
+ kind: 'file',
509
+ });
510
+ }
511
+ async laneFreshness(filePath) {
512
+ try {
513
+ return toFreshnessSnapshot(await fsPromises.stat(filePath));
514
+ }
515
+ catch {
516
+ return undefined;
517
+ }
518
+ }
519
+ async tryHashAnchors(filePath, expected, offset, allowGrowth = false) {
520
+ try {
521
+ return await (0, bounded_anchor_proof_1.hashFileAnchors)(filePath, expected, offset, allowGrowth);
522
+ }
523
+ catch (error) {
524
+ this.logger.debug({ error, filePath }, 'Lane anchor proof unavailable — rescanning');
525
+ return undefined;
526
+ }
527
+ }
528
+ async buildLaneFromSummary(state, adapter, ending = false) {
529
+ if (!adapter.getSummary)
530
+ return false;
531
+ const summary = await adapter.getSummary(this.laneRef(state));
532
+ if (!this.laneOwned(state, ending))
533
+ return true;
534
+ if (!summary?.laneSeed)
535
+ return false;
536
+ const seed = summary.laneSeed;
537
+ const fresh = await this.laneFreshness(state.filePath);
538
+ if (!this.laneOwned(state, ending))
539
+ return true;
540
+ const anchors = fresh
541
+ ? await this.tryHashAnchors(state.filePath, fresh, seed.endOffset)
542
+ : undefined;
543
+ if (!this.laneOwned(state, ending))
544
+ return true;
545
+ state.lane = {
546
+ offset: seed.endOffset,
547
+ anchors,
548
+ fileIdentity: fresh?.fileIdentity,
549
+ revision: fresh?.mtimeMs,
550
+ metrics: { ...summary.metrics },
551
+ tail: seed.tail,
552
+ visibleContextTokens: seed.visibleContextTokens,
553
+ firstMessageTimestamp: seed.firstMessageTimestamp,
554
+ lastMessageTimestamp: seed.lastMessageTimestamp,
555
+ continuationState: seed.continuationState,
556
+ messageCount: seed.messageCount,
557
+ };
558
+ state.inLane = true;
559
+ this.applyLaneToState(state);
560
+ return true;
561
+ }
562
+ markStatConsumed(state, stat) {
563
+ state.lastSize = stat.size;
564
+ state.lastDev = stat.dev;
565
+ state.lastIno = stat.ino;
566
+ state.replacementPending = false;
567
+ }
568
+ applyLaneToState(state) {
569
+ const lane = state.lane;
570
+ if (!lane)
571
+ return;
572
+ state.lastMessageCount = lane.messageCount;
573
+ state.lastMetrics = this.toMetricsSnapshot(lane.metrics);
574
+ state.lastSummaryMetrics = lane.metrics;
575
+ }
576
+ refreshLaneFromResult(state, result, stat) {
577
+ const msgs = result.session.messages;
578
+ const last = msgs[msgs.length - 1];
579
+ const visibleContextTokens = result.sourceChangeKind === 'same-file-append'
580
+ ? result.session.metrics.visibleContextTokens
581
+ : (0, estimate_content_tokens_1.estimateVisibleFromMessages)(msgs);
582
+ const fresh = toFreshnessSnapshot(stat);
583
+ state.lane = {
584
+ offset: result.lastOffset,
585
+ anchors: result.fileContentAnchors,
586
+ fileIdentity: fresh.fileIdentity,
587
+ revision: fresh.mtimeMs,
588
+ metrics: result.session.metrics,
589
+ tail: (0, metrics_merge_1.describeTail)(last),
590
+ visibleContextTokens,
591
+ firstMessageTimestamp: msgs.length > 0 ? msgs[0].timestamp.getTime() : undefined,
592
+ lastMessageTimestamp: last ? last.timestamp.getTime() : undefined,
593
+ continuationState: result.continuationState,
594
+ messageCount: msgs.length,
595
+ };
596
+ }
597
+ advanceVisible(base, messages) {
598
+ let visible = base;
599
+ for (const m of messages) {
600
+ if (m.isCompactSummary) {
601
+ visible = 0;
602
+ continue;
452
603
  }
453
- const isInPlaceTailFold = boundaryFold && newMessageCount === 0 && !isFullRefresh;
454
- if (newMessageCount > 0 || isFullRefresh || isInPlaceTailFold) {
455
- await this.events.publish('session.transcript.updated', {
456
- kind: 'delta',
457
- sessionId,
458
- transcriptPath: state.filePath,
459
- newMessageCount: Math.max(0, newMessageCount),
460
- metrics: state.lastMetrics,
461
- cursor,
462
- prevCursor,
463
- replaceFromChunkIndex,
464
- newChunkIds,
465
- totalChunkCount: chunks.length,
466
- deltaChunks,
467
- deltaMessages,
604
+ if (m.isSidechain)
605
+ continue;
606
+ visible += (0, estimate_content_tokens_1.estimateMessageTokens)(m.content);
607
+ }
608
+ return visible;
609
+ }
610
+ advanceLaneWithSlice(lane, slice, proofAnchors, fresh) {
611
+ const newMessages = slice.entries;
612
+ const fold = (0, metrics_merge_1.computeLeadingContinuationFold)(lane.tail, newMessages);
613
+ const netNew = newMessages.length - fold.foldCount;
614
+ const newCount = lane.messageCount + netNew;
615
+ const newVisible = this.advanceVisible(lane.visibleContextTokens, newMessages);
616
+ const firstTs = lane.firstMessageTimestamp ??
617
+ (newMessages.length > 0 ? newMessages[0].timestamp.getTime() : undefined);
618
+ const lastTs = netNew > 0
619
+ ? newMessages[newMessages.length - 1].timestamp.getTime()
620
+ : lane.lastMessageTimestamp;
621
+ let durationMs = lane.metrics.durationMs;
622
+ if (newCount >= 2 && firstTs !== undefined && lastTs !== undefined) {
623
+ durationMs = lastTs - firstTs;
624
+ }
625
+ const derived = { messageCount: newCount, visibleContextTokens: newVisible, durationMs };
626
+ lane.metrics = slice.metrics
627
+ ? (0, metrics_merge_1.mergeMetrics)(lane.metrics, slice.metrics, derived)
628
+ : { ...lane.metrics, ...derived };
629
+ lane.offset = slice.nextByteOffset;
630
+ lane.anchors = proofAnchors;
631
+ lane.fileIdentity = fresh.fileIdentity;
632
+ lane.revision = fresh.mtimeMs;
633
+ lane.tail = fold.newTail;
634
+ lane.visibleContextTokens = newVisible;
635
+ lane.firstMessageTimestamp = firstTs;
636
+ lane.lastMessageTimestamp = lastTs;
637
+ lane.continuationState = slice.continuationState;
638
+ lane.messageCount = newCount;
639
+ return netNew > 0 || fold.tailMutatedWithoutNewMessage;
640
+ }
641
+ async runFileLane(state, stat, adapter, isReplacement, { readerPaidCostlyFullParse = false, ending = false, } = {}) {
642
+ state.inLane = true;
643
+ const fresh = toFreshnessSnapshot(stat);
644
+ const lane = state.lane;
645
+ if (!isReplacement &&
646
+ lane !== undefined &&
647
+ lane.fileIdentity === fresh.fileIdentity &&
648
+ lane.revision === fresh.mtimeMs &&
649
+ fresh.size === lane.offset) {
650
+ this.markStatConsumed(state, stat);
651
+ return;
652
+ }
653
+ const canAppend = !isReplacement &&
654
+ lane !== undefined &&
655
+ lane.anchors !== undefined &&
656
+ lane.fileIdentity === fresh.fileIdentity &&
657
+ fresh.size > lane.offset;
658
+ let advanced = false;
659
+ let changed = false;
660
+ if (canAppend && lane) {
661
+ const preAnchors = await this.tryHashAnchors(state.filePath, fresh, lane.offset);
662
+ if (!this.laneOwned(state, ending))
663
+ return;
664
+ if ((0, bounded_anchor_proof_1.anchorsEqual)(preAnchors, lane.anchors)) {
665
+ const slice = await adapter.parseIncremental(state.filePath, {
666
+ byteOffset: lane.offset,
667
+ endByteOffset: fresh.size,
668
+ includeToolCalls: true,
669
+ continuationState: lane.continuationState,
468
670
  });
671
+ if (!this.laneOwned(state, ending))
672
+ return;
673
+ const newOffset = slice.nextByteOffset;
674
+ const prefixHeld = newOffset >= lane.offset &&
675
+ (0, bounded_anchor_proof_1.anchorsEqual)(await this.tryHashAnchors(state.filePath, fresh, lane.offset, true), lane.anchors);
676
+ if (!this.laneOwned(state, ending))
677
+ return;
678
+ const proofAnchors = prefixHeld
679
+ ? await this.tryHashAnchors(state.filePath, fresh, newOffset, true)
680
+ : undefined;
681
+ if (!this.laneOwned(state, ending))
682
+ return;
683
+ if (proofAnchors) {
684
+ changed = this.advanceLaneWithSlice(lane, slice, proofAnchors, fresh);
685
+ advanced = true;
686
+ }
469
687
  }
470
688
  }
471
- catch (error) {
472
- this.logger.error({ error, sessionId }, 'File change handler failed — watcher continues');
689
+ if (!advanced) {
690
+ const reseeded = await this.buildLaneFromSummary(state, adapter, ending);
691
+ if (!this.laneOwned(state, ending))
692
+ return;
693
+ if (!reseeded)
694
+ return;
695
+ changed = true;
696
+ }
697
+ this.markStatConsumed(state, stat);
698
+ this.applyLaneToState(state);
699
+ if (changed && !ending) {
700
+ if (readerPaidCostlyFullParse)
701
+ state.costlyLanePass = true;
702
+ await this.events.publish('session.transcript.updated', {
703
+ kind: 'full-refetch-required',
704
+ sessionId: state.sessionId,
705
+ transcriptPath: state.filePath,
706
+ sourceChangeKind: 'unknown-full-parse',
707
+ });
473
708
  }
474
709
  }
475
- async handleDbChanged(sessionId) {
476
- const state = this.watchers.get(sessionId);
477
- if (!state || !state.sourceRef)
710
+ async finalizeLaneMetrics(state, adapter, fallback) {
711
+ const statIfOwned = async () => {
712
+ try {
713
+ const stat = await fsPromises.stat(state.filePath);
714
+ return this.laneOwned(state, true) ? stat : undefined;
715
+ }
716
+ catch {
717
+ return undefined;
718
+ }
719
+ };
720
+ let stat = await statIfOwned();
721
+ if (!stat)
722
+ return fallback;
723
+ const inflight = state.activeRefresh;
724
+ if (inflight) {
725
+ await inflight.catch(() => undefined);
726
+ stat = await statIfOwned();
727
+ if (!stat)
728
+ return fallback;
729
+ }
730
+ await this.runFileLane(state, stat, adapter, false, { ending: true });
731
+ return this.laneOwned(state, true) ? state.lastMetrics : fallback;
732
+ }
733
+ applyBodyResultToState(state, adapter, result, stat) {
734
+ state.lastMessageCount = result.session.metrics.messageCount;
735
+ state.lastChunkCount = (0, chunk_builder_1.buildChunks)(result.session.messages).length;
736
+ state.lastSourceVersion = result.sourceVersion;
737
+ state.lastMetrics = this.toMetricsSnapshot(result.session.metrics);
738
+ state.lastSummaryMetrics = result.session.metrics;
739
+ state.inLane = false;
740
+ if (stat && this.isLaneEligible(adapter))
741
+ this.refreshLaneFromResult(state, result, stat);
742
+ }
743
+ async publishBodyResult(state, stat, adapter, result, isReplacement) {
744
+ const { sessionId } = state;
745
+ const { session, sourceChangeKind, sourceVersion, boundaryFold } = result;
746
+ const changedCacheHit = sourceChangeKind === 'cache-hit' && sourceVersion !== state.lastSourceVersion;
747
+ const newMessageCount = session.metrics.messageCount - state.lastMessageCount;
748
+ const isFullRefresh = isReplacement || session.metrics.messageCount < state.lastMessageCount;
749
+ if (isFullRefresh) {
750
+ this.logger.debug({
751
+ sessionId,
752
+ isReplacement,
753
+ previousMessageCount: state.lastMessageCount,
754
+ messageCount: session.metrics.messageCount,
755
+ }, 'Transcript source replaced or messageCount deflated — requiring canonical refetch');
756
+ }
757
+ const chunks = (0, chunk_builder_1.buildChunks)(session.messages);
758
+ const prevCursor = (0, transcript_cursor_1.encodeCursor)(state.lastSourceVersion, state.lastMessageCount, state.lastChunkCount);
759
+ const cursor = (0, transcript_cursor_1.encodeCursor)(sourceVersion, session.metrics.messageCount, chunks.length);
760
+ const replaceFromChunkIndex = isFullRefresh ? 0 : Math.max(0, state.lastChunkCount - 1);
761
+ const sliceFromMessage = isFullRefresh ? 0 : state.lastMessageCount;
762
+ const newChunkIds = chunks.slice(replaceFromChunkIndex).map((c) => c.id);
763
+ const truncatedDeltaMessages = (0, transcript_truncation_1.truncateMessages)(session.messages.slice(sliceFromMessage));
764
+ const truncatedDeltaChunks = (0, transcript_truncation_1.truncateChunks)(chunks.slice(replaceFromChunkIndex));
765
+ const deltaChunks = truncatedDeltaChunks.map(transcript_serialization_1.serializeChunk);
766
+ const deltaMessages = truncatedDeltaMessages.map(transcript_serialization_1.serializeMessage);
767
+ this.markStatConsumed(state, stat);
768
+ state.lastSourceVersion = sourceVersion;
769
+ state.lastMessageCount = session.metrics.messageCount;
770
+ state.lastChunkCount = chunks.length;
771
+ state.lastMetrics = this.toMetricsSnapshot(session.metrics);
772
+ state.lastSummaryMetrics = session.metrics;
773
+ if (this.isLaneEligible(adapter))
774
+ this.refreshLaneFromResult(state, result, stat);
775
+ if (isReplacement || changedCacheHit || requiresCanonicalRefetch(sourceChangeKind)) {
776
+ await this.events.publish('session.transcript.updated', {
777
+ kind: 'full-refetch-required',
778
+ sessionId,
779
+ transcriptPath: state.filePath,
780
+ sourceChangeKind,
781
+ });
782
+ return;
783
+ }
784
+ const isInPlaceTailFold = boundaryFold && newMessageCount === 0 && !isFullRefresh;
785
+ if (newMessageCount > 0 || isFullRefresh || isInPlaceTailFold) {
786
+ await this.events.publish('session.transcript.updated', {
787
+ kind: 'delta',
788
+ sessionId,
789
+ transcriptPath: state.filePath,
790
+ newMessageCount: Math.max(0, newMessageCount),
791
+ metrics: state.lastMetrics,
792
+ cursor,
793
+ prevCursor,
794
+ replaceFromChunkIndex,
795
+ newChunkIds,
796
+ totalChunkCount: chunks.length,
797
+ deltaChunks,
798
+ deltaMessages,
799
+ });
800
+ }
801
+ }
802
+ async handleDbChanged(state) {
803
+ const { sessionId } = state;
804
+ if (!state.sourceRef)
478
805
  return;
479
806
  try {
480
807
  const adapter = this.adapterFactory.getAdapter(state.providerName);
@@ -491,11 +818,15 @@ let TranscriptWatcherService = TranscriptWatcherService_1 = class TranscriptWatc
491
818
  this.logger.debug({ error, sessionId }, 'DB freshness check failed during change — retry');
492
819
  return;
493
820
  }
821
+ if (!this.isCurrent(state))
822
+ return;
494
823
  if (JSON.stringify(token) === JSON.stringify(state.lastFreshnessToken)) {
495
824
  return;
496
825
  }
497
826
  }
498
827
  const { session, sourceVersion } = await this.cacheService.getOrParseWithMeta(sessionId, state.sourceRef, adapter);
828
+ if (!this.isCurrent(state))
829
+ return;
499
830
  const isFullRefresh = session.metrics.messageCount < state.lastMessageCount;
500
831
  if (isFullRefresh) {
501
832
  this.logger.debug({
@@ -540,29 +871,34 @@ let TranscriptWatcherService = TranscriptWatcherService_1 = class TranscriptWatc
540
871
  this.logger.error({ error, sessionId }, 'DB change handler failed — watcher continues');
541
872
  }
542
873
  }
543
- createFsWatcher(sessionId, watchPath) {
544
- const watcher = fs.watch(watchPath, (eventType) => {
874
+ createFsWatcher(state) {
875
+ const { sessionId } = state;
876
+ const watcher = fs.watch(state.watchPath, (eventType) => {
877
+ if (!this.isCurrent(state) || state.fsWatcher !== watcher)
878
+ return;
545
879
  if (eventType === 'change' || eventType === 'rename') {
546
- this.scheduleDebounce(sessionId);
880
+ this.requestRefresh(state);
547
881
  }
548
882
  });
549
883
  watcher.on('error', (err) => {
884
+ if (!this.isCurrent(state) || state.fsWatcher !== watcher)
885
+ return;
550
886
  this.logger.warn({ error: err, sessionId }, 'fs.watch error — relying on stat-poll fallback');
551
- const state = this.watchers.get(sessionId);
552
- if (state?.fsWatcher) {
553
- state.fsWatcher.close();
554
- state.fsWatcher = null;
555
- }
887
+ state.fsWatcher = null;
888
+ watcher.close();
556
889
  });
557
890
  return watcher;
558
891
  }
559
892
  reopenFsWatcher(state) {
893
+ if (!this.isCurrent(state))
894
+ return;
560
895
  if (state.fsWatcher) {
561
- state.fsWatcher.close();
896
+ const previous = state.fsWatcher;
562
897
  state.fsWatcher = null;
898
+ previous.close();
563
899
  }
564
900
  try {
565
- state.fsWatcher = this.createFsWatcher(state.sessionId, state.watchPath);
901
+ state.fsWatcher = this.createFsWatcher(state);
566
902
  }
567
903
  catch {
568
904
  this.logger.warn({ sessionId: state.sessionId }, 'Failed to reopen fs.watch after inode rotation');
@@ -572,12 +908,17 @@ let TranscriptWatcherService = TranscriptWatcherService_1 = class TranscriptWatc
572
908
  const state = this.watchers.get(sessionId);
573
909
  if (!state)
574
910
  return;
911
+ this.watchers.delete(sessionId);
575
912
  if (state.debounceTimer)
576
913
  clearTimeout(state.debounceTimer);
577
- clearInterval(state.pollTimer);
914
+ state.debounceTimer = null;
915
+ state.pending = false;
916
+ if (state.pollTimer)
917
+ clearInterval(state.pollTimer);
918
+ state.pollTimer = null;
578
919
  if (state.fsWatcher)
579
920
  state.fsWatcher.close();
580
- this.watchers.delete(sessionId);
921
+ state.fsWatcher = null;
581
922
  this.logger.debug({ sessionId }, 'Cleaned up transcript watcher');
582
923
  }
583
924
  };