crewly 1.20.302 → 1.20.304

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 (276) hide show
  1. package/config/roles/developer/prompt.md +7 -0
  2. package/config/roles/fullstack-dev/prompt.md +7 -0
  3. package/config/roles/product-manager/prompt.md +7 -0
  4. package/config/skills/orchestrator/move-team/SKILL.md +53 -0
  5. package/config/skills/orchestrator/move-team/execute.sh +42 -0
  6. package/config/skills/orchestrator/usage-categories/SKILL.md +59 -0
  7. package/config/skills/orchestrator/usage-categories/execute.sh +81 -0
  8. package/config/skills/orchestrator/usage-categories/execute.test.sh +45 -0
  9. package/config/skills/orchestrator/usage-report/SKILL.md +71 -0
  10. package/config/skills/orchestrator/usage-report/execute.sh +84 -0
  11. package/config/skills/orchestrator/usage-report/execute.test.sh +39 -0
  12. package/config/skills/team-leader/usage-report/SKILL.md +71 -0
  13. package/config/skills/team-leader/usage-report/execute.sh +84 -0
  14. package/config/skills/team-leader/usage-report/execute.test.sh +39 -0
  15. package/dist/backend/backend/src/constants.d.ts +80 -0
  16. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  17. package/dist/backend/backend/src/constants.js +80 -0
  18. package/dist/backend/backend/src/constants.js.map +1 -1
  19. package/dist/backend/backend/src/controllers/team-move/team-move.controller.d.ts +45 -0
  20. package/dist/backend/backend/src/controllers/team-move/team-move.controller.d.ts.map +1 -0
  21. package/dist/backend/backend/src/controllers/team-move/team-move.controller.js +214 -0
  22. package/dist/backend/backend/src/controllers/team-move/team-move.controller.js.map +1 -0
  23. package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.controller.d.ts +36 -0
  24. package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.controller.d.ts.map +1 -0
  25. package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.controller.js +158 -0
  26. package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.controller.js.map +1 -0
  27. package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.routes.d.ts +15 -0
  28. package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.routes.d.ts.map +1 -0
  29. package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.routes.js +26 -0
  30. package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.routes.js.map +1 -0
  31. package/dist/backend/backend/src/index.d.ts +10 -0
  32. package/dist/backend/backend/src/index.d.ts.map +1 -1
  33. package/dist/backend/backend/src/index.js +97 -20
  34. package/dist/backend/backend/src/index.js.map +1 -1
  35. package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
  36. package/dist/backend/backend/src/routes/api.routes.js +6 -0
  37. package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
  38. package/dist/backend/backend/src/services/agent/follow-through.wiring.d.ts +32 -0
  39. package/dist/backend/backend/src/services/agent/follow-through.wiring.d.ts.map +1 -1
  40. package/dist/backend/backend/src/services/agent/follow-through.wiring.js +42 -1
  41. package/dist/backend/backend/src/services/agent/follow-through.wiring.js.map +1 -1
  42. package/dist/backend/backend/src/services/agent/resource-mode.service.d.ts +26 -6
  43. package/dist/backend/backend/src/services/agent/resource-mode.service.d.ts.map +1 -1
  44. package/dist/backend/backend/src/services/agent/resource-mode.service.js +59 -5
  45. package/dist/backend/backend/src/services/agent/resource-mode.service.js.map +1 -1
  46. package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts +1 -1
  47. package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
  48. package/dist/backend/backend/src/services/cloud/cloud-sync.types.js +1 -0
  49. package/dist/backend/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
  50. package/dist/backend/backend/src/services/core/storage.service.d.ts +18 -0
  51. package/dist/backend/backend/src/services/core/storage.service.d.ts.map +1 -1
  52. package/dist/backend/backend/src/services/core/storage.service.js +16 -0
  53. package/dist/backend/backend/src/services/core/storage.service.js.map +1 -1
  54. package/dist/backend/backend/src/services/decisions/decision.service.d.ts +19 -0
  55. package/dist/backend/backend/src/services/decisions/decision.service.d.ts.map +1 -1
  56. package/dist/backend/backend/src/services/decisions/decision.service.js +46 -0
  57. package/dist/backend/backend/src/services/decisions/decision.service.js.map +1 -1
  58. package/dist/backend/backend/src/services/monitoring/agent-turn-state.d.ts +35 -0
  59. package/dist/backend/backend/src/services/monitoring/agent-turn-state.d.ts.map +1 -1
  60. package/dist/backend/backend/src/services/monitoring/agent-turn-state.js +101 -0
  61. package/dist/backend/backend/src/services/monitoring/agent-turn-state.js.map +1 -1
  62. package/dist/backend/backend/src/services/monitoring/background-work-watchdog.d.ts +58 -0
  63. package/dist/backend/backend/src/services/monitoring/background-work-watchdog.d.ts.map +1 -0
  64. package/dist/backend/backend/src/services/monitoring/background-work-watchdog.js +100 -0
  65. package/dist/backend/backend/src/services/monitoring/background-work-watchdog.js.map +1 -0
  66. package/dist/backend/backend/src/services/monitoring/token-usage.service.d.ts.map +1 -1
  67. package/dist/backend/backend/src/services/monitoring/token-usage.service.js +3 -0
  68. package/dist/backend/backend/src/services/monitoring/token-usage.service.js.map +1 -1
  69. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts +7 -0
  70. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts.map +1 -1
  71. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js +15 -0
  72. package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js.map +1 -1
  73. package/dist/backend/backend/src/services/slack/slack-initializer.d.ts.map +1 -1
  74. package/dist/backend/backend/src/services/slack/slack-initializer.js +2 -1
  75. package/dist/backend/backend/src/services/slack/slack-initializer.js.map +1 -1
  76. package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts +9 -0
  77. package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
  78. package/dist/backend/backend/src/services/slack/slack-team-channel.service.js +25 -0
  79. package/dist/backend/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
  80. package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.d.ts +24 -0
  81. package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.d.ts.map +1 -1
  82. package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.js +63 -1
  83. package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.js.map +1 -1
  84. package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts +19 -0
  85. package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
  86. package/dist/backend/backend/src/services/task-pool/task-pool.service.js +41 -0
  87. package/dist/backend/backend/src/services/task-pool/task-pool.service.js.map +1 -1
  88. package/dist/backend/backend/src/services/team-move/team-move-archive.d.ts +122 -0
  89. package/dist/backend/backend/src/services/team-move/team-move-archive.d.ts.map +1 -0
  90. package/dist/backend/backend/src/services/team-move/team-move-archive.js +202 -0
  91. package/dist/backend/backend/src/services/team-move/team-move-archive.js.map +1 -0
  92. package/dist/backend/backend/src/services/team-move/team-move-chat.d.ts +59 -0
  93. package/dist/backend/backend/src/services/team-move/team-move-chat.d.ts.map +1 -0
  94. package/dist/backend/backend/src/services/team-move/team-move-chat.js +133 -0
  95. package/dist/backend/backend/src/services/team-move/team-move-chat.js.map +1 -0
  96. package/dist/backend/backend/src/services/team-move/team-move-cloud.client.d.ts +102 -0
  97. package/dist/backend/backend/src/services/team-move/team-move-cloud.client.d.ts.map +1 -0
  98. package/dist/backend/backend/src/services/team-move/team-move-cloud.client.js +217 -0
  99. package/dist/backend/backend/src/services/team-move/team-move-cloud.client.js.map +1 -0
  100. package/dist/backend/backend/src/services/team-move/team-move-crypto.d.ts +102 -0
  101. package/dist/backend/backend/src/services/team-move/team-move-crypto.d.ts.map +1 -0
  102. package/dist/backend/backend/src/services/team-move/team-move-crypto.js +348 -0
  103. package/dist/backend/backend/src/services/team-move/team-move-crypto.js.map +1 -0
  104. package/dist/backend/backend/src/services/team-move/team-move-driver.service.d.ts +209 -0
  105. package/dist/backend/backend/src/services/team-move/team-move-driver.service.d.ts.map +1 -0
  106. package/dist/backend/backend/src/services/team-move/team-move-driver.service.js +504 -0
  107. package/dist/backend/backend/src/services/team-move/team-move-driver.service.js.map +1 -0
  108. package/dist/backend/backend/src/services/team-move/team-move-exporter.d.ts +156 -0
  109. package/dist/backend/backend/src/services/team-move/team-move-exporter.d.ts.map +1 -0
  110. package/dist/backend/backend/src/services/team-move/team-move-exporter.js +462 -0
  111. package/dist/backend/backend/src/services/team-move/team-move-exporter.js.map +1 -0
  112. package/dist/backend/backend/src/services/team-move/team-move-fs.d.ts +157 -0
  113. package/dist/backend/backend/src/services/team-move/team-move-fs.d.ts.map +1 -0
  114. package/dist/backend/backend/src/services/team-move/team-move-fs.js +298 -0
  115. package/dist/backend/backend/src/services/team-move/team-move-fs.js.map +1 -0
  116. package/dist/backend/backend/src/services/team-move/team-move-git.d.ts +69 -0
  117. package/dist/backend/backend/src/services/team-move/team-move-git.d.ts.map +1 -0
  118. package/dist/backend/backend/src/services/team-move/team-move-git.js +198 -0
  119. package/dist/backend/backend/src/services/team-move/team-move-git.js.map +1 -0
  120. package/dist/backend/backend/src/services/team-move/team-move-importer.d.ts +121 -0
  121. package/dist/backend/backend/src/services/team-move/team-move-importer.d.ts.map +1 -0
  122. package/dist/backend/backend/src/services/team-move/team-move-importer.js +462 -0
  123. package/dist/backend/backend/src/services/team-move/team-move-importer.js.map +1 -0
  124. package/dist/backend/backend/src/services/team-move/team-move-journal.d.ts +110 -0
  125. package/dist/backend/backend/src/services/team-move/team-move-journal.d.ts.map +1 -0
  126. package/dist/backend/backend/src/services/team-move/team-move-journal.js +198 -0
  127. package/dist/backend/backend/src/services/team-move/team-move-journal.js.map +1 -0
  128. package/dist/backend/backend/src/services/team-move/team-move-preflight.d.ts +71 -0
  129. package/dist/backend/backend/src/services/team-move/team-move-preflight.d.ts.map +1 -0
  130. package/dist/backend/backend/src/services/team-move/team-move-preflight.js +102 -0
  131. package/dist/backend/backend/src/services/team-move/team-move-preflight.js.map +1 -0
  132. package/dist/backend/backend/src/services/team-move/team-move-runtime.d.ts +29 -0
  133. package/dist/backend/backend/src/services/team-move/team-move-runtime.d.ts.map +1 -0
  134. package/dist/backend/backend/src/services/team-move/team-move-runtime.js +173 -0
  135. package/dist/backend/backend/src/services/team-move/team-move-runtime.js.map +1 -0
  136. package/dist/backend/backend/src/services/team-move/team-move-secret-scan.d.ts +50 -0
  137. package/dist/backend/backend/src/services/team-move/team-move-secret-scan.d.ts.map +1 -0
  138. package/dist/backend/backend/src/services/team-move/team-move-secret-scan.js +96 -0
  139. package/dist/backend/backend/src/services/team-move/team-move-secret-scan.js.map +1 -0
  140. package/dist/backend/backend/src/services/team-move/team-move-select.d.ts +94 -0
  141. package/dist/backend/backend/src/services/team-move/team-move-select.d.ts.map +1 -0
  142. package/dist/backend/backend/src/services/team-move/team-move-select.js +124 -0
  143. package/dist/backend/backend/src/services/team-move/team-move-select.js.map +1 -0
  144. package/dist/backend/backend/src/services/team-move/team-move.fixture.d.ts +46 -0
  145. package/dist/backend/backend/src/services/team-move/team-move.fixture.d.ts.map +1 -0
  146. package/dist/backend/backend/src/services/team-move/team-move.fixture.js +202 -0
  147. package/dist/backend/backend/src/services/team-move/team-move.fixture.js.map +1 -0
  148. package/dist/backend/backend/src/services/team-move/team-move.types.d.ts +269 -0
  149. package/dist/backend/backend/src/services/team-move/team-move.types.d.ts.map +1 -0
  150. package/dist/backend/backend/src/services/team-move/team-move.types.js +44 -0
  151. package/dist/backend/backend/src/services/team-move/team-move.types.js.map +1 -0
  152. package/dist/backend/backend/src/services/trace/trace-context.service.d.ts +42 -2
  153. package/dist/backend/backend/src/services/trace/trace-context.service.d.ts.map +1 -1
  154. package/dist/backend/backend/src/services/trace/trace-context.service.js +66 -5
  155. package/dist/backend/backend/src/services/trace/trace-context.service.js.map +1 -1
  156. package/dist/backend/backend/src/services/trace/trace-recorder.d.ts.map +1 -1
  157. package/dist/backend/backend/src/services/trace/trace-recorder.js +11 -0
  158. package/dist/backend/backend/src/services/trace/trace-recorder.js.map +1 -1
  159. package/dist/backend/backend/src/services/usage-ledger/categories-param.d.ts +28 -0
  160. package/dist/backend/backend/src/services/usage-ledger/categories-param.d.ts.map +1 -0
  161. package/dist/backend/backend/src/services/usage-ledger/categories-param.js +67 -0
  162. package/dist/backend/backend/src/services/usage-ledger/categories-param.js.map +1 -0
  163. package/dist/backend/backend/src/services/usage-ledger/excerpt.d.ts +37 -0
  164. package/dist/backend/backend/src/services/usage-ledger/excerpt.d.ts.map +1 -0
  165. package/dist/backend/backend/src/services/usage-ledger/excerpt.js +49 -0
  166. package/dist/backend/backend/src/services/usage-ledger/excerpt.js.map +1 -0
  167. package/dist/backend/backend/src/services/usage-ledger/index.d.ts +14 -0
  168. package/dist/backend/backend/src/services/usage-ledger/index.d.ts.map +1 -0
  169. package/dist/backend/backend/src/services/usage-ledger/index.js +14 -0
  170. package/dist/backend/backend/src/services/usage-ledger/index.js.map +1 -0
  171. package/dist/backend/backend/src/services/usage-ledger/ledger-hooks.d.ts +44 -0
  172. package/dist/backend/backend/src/services/usage-ledger/ledger-hooks.d.ts.map +1 -0
  173. package/dist/backend/backend/src/services/usage-ledger/ledger-hooks.js +52 -0
  174. package/dist/backend/backend/src/services/usage-ledger/ledger-hooks.js.map +1 -0
  175. package/dist/backend/backend/src/services/usage-ledger/ledger-rollup.d.ts +100 -0
  176. package/dist/backend/backend/src/services/usage-ledger/ledger-rollup.d.ts.map +1 -0
  177. package/dist/backend/backend/src/services/usage-ledger/ledger-rollup.js +189 -0
  178. package/dist/backend/backend/src/services/usage-ledger/ledger-rollup.js.map +1 -0
  179. package/dist/backend/backend/src/services/usage-ledger/owner-spans.d.ts +41 -0
  180. package/dist/backend/backend/src/services/usage-ledger/owner-spans.d.ts.map +1 -0
  181. package/dist/backend/backend/src/services/usage-ledger/owner-spans.js +71 -0
  182. package/dist/backend/backend/src/services/usage-ledger/owner-spans.js.map +1 -0
  183. package/dist/backend/backend/src/services/usage-ledger/team-directory.d.ts +47 -0
  184. package/dist/backend/backend/src/services/usage-ledger/team-directory.d.ts.map +1 -0
  185. package/dist/backend/backend/src/services/usage-ledger/team-directory.js +106 -0
  186. package/dist/backend/backend/src/services/usage-ledger/team-directory.js.map +1 -0
  187. package/dist/backend/backend/src/services/usage-ledger/unit-classifier.d.ts +101 -0
  188. package/dist/backend/backend/src/services/usage-ledger/unit-classifier.d.ts.map +1 -0
  189. package/dist/backend/backend/src/services/usage-ledger/unit-classifier.js +263 -0
  190. package/dist/backend/backend/src/services/usage-ledger/unit-classifier.js.map +1 -0
  191. package/dist/backend/backend/src/services/usage-ledger/usage-categories.store.d.ts +96 -0
  192. package/dist/backend/backend/src/services/usage-ledger/usage-categories.store.d.ts.map +1 -0
  193. package/dist/backend/backend/src/services/usage-ledger/usage-categories.store.js +166 -0
  194. package/dist/backend/backend/src/services/usage-ledger/usage-categories.store.js.map +1 -0
  195. package/dist/backend/backend/src/services/usage-ledger/usage-ledger.backfill.d.ts +90 -0
  196. package/dist/backend/backend/src/services/usage-ledger/usage-ledger.backfill.d.ts.map +1 -0
  197. package/dist/backend/backend/src/services/usage-ledger/usage-ledger.backfill.js +294 -0
  198. package/dist/backend/backend/src/services/usage-ledger/usage-ledger.backfill.js.map +1 -0
  199. package/dist/backend/backend/src/services/usage-ledger/usage-ledger.recorder.d.ts +102 -0
  200. package/dist/backend/backend/src/services/usage-ledger/usage-ledger.recorder.d.ts.map +1 -0
  201. package/dist/backend/backend/src/services/usage-ledger/usage-ledger.recorder.js +314 -0
  202. package/dist/backend/backend/src/services/usage-ledger/usage-ledger.recorder.js.map +1 -0
  203. package/dist/backend/backend/src/services/usage-ledger/usage-ledger.runtime.d.ts +62 -0
  204. package/dist/backend/backend/src/services/usage-ledger/usage-ledger.runtime.d.ts.map +1 -0
  205. package/dist/backend/backend/src/services/usage-ledger/usage-ledger.runtime.js +106 -0
  206. package/dist/backend/backend/src/services/usage-ledger/usage-ledger.runtime.js.map +1 -0
  207. package/dist/backend/backend/src/services/usage-ledger/usage-ledger.store.d.ts +52 -0
  208. package/dist/backend/backend/src/services/usage-ledger/usage-ledger.store.d.ts.map +1 -0
  209. package/dist/backend/backend/src/services/usage-ledger/usage-ledger.store.js +95 -0
  210. package/dist/backend/backend/src/services/usage-ledger/usage-ledger.store.js.map +1 -0
  211. package/dist/backend/backend/src/services/usage-ledger/usage-ledger.types.d.ts +127 -0
  212. package/dist/backend/backend/src/services/usage-ledger/usage-ledger.types.d.ts.map +1 -0
  213. package/dist/backend/backend/src/services/usage-ledger/usage-ledger.types.js +19 -0
  214. package/dist/backend/backend/src/services/usage-ledger/usage-ledger.types.js.map +1 -0
  215. package/dist/backend/backend/src/services/usage-ledger/usage-report.service.d.ts +124 -0
  216. package/dist/backend/backend/src/services/usage-ledger/usage-report.service.d.ts.map +1 -0
  217. package/dist/backend/backend/src/services/usage-ledger/usage-report.service.js +372 -0
  218. package/dist/backend/backend/src/services/usage-ledger/usage-report.service.js.map +1 -0
  219. package/dist/backend/build-info.json +2 -2
  220. package/dist/cli/backend/src/constants.d.ts +80 -0
  221. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  222. package/dist/cli/backend/src/constants.js +80 -0
  223. package/dist/cli/backend/src/constants.js.map +1 -1
  224. package/dist/cli/backend/src/services/agent/resource-mode.service.d.ts +26 -6
  225. package/dist/cli/backend/src/services/agent/resource-mode.service.d.ts.map +1 -1
  226. package/dist/cli/backend/src/services/agent/resource-mode.service.js +59 -5
  227. package/dist/cli/backend/src/services/agent/resource-mode.service.js.map +1 -1
  228. package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts +1 -1
  229. package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
  230. package/dist/cli/backend/src/services/cloud/cloud-sync.types.js +1 -0
  231. package/dist/cli/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
  232. package/dist/cli/backend/src/services/core/storage.service.d.ts +18 -0
  233. package/dist/cli/backend/src/services/core/storage.service.d.ts.map +1 -1
  234. package/dist/cli/backend/src/services/core/storage.service.js +16 -0
  235. package/dist/cli/backend/src/services/core/storage.service.js.map +1 -1
  236. package/dist/cli/backend/src/services/decisions/decision.service.d.ts +19 -0
  237. package/dist/cli/backend/src/services/decisions/decision.service.d.ts.map +1 -1
  238. package/dist/cli/backend/src/services/decisions/decision.service.js +46 -0
  239. package/dist/cli/backend/src/services/decisions/decision.service.js.map +1 -1
  240. package/dist/cli/backend/src/services/monitoring/agent-turn-state.d.ts +35 -0
  241. package/dist/cli/backend/src/services/monitoring/agent-turn-state.d.ts.map +1 -1
  242. package/dist/cli/backend/src/services/monitoring/agent-turn-state.js +101 -0
  243. package/dist/cli/backend/src/services/monitoring/agent-turn-state.js.map +1 -1
  244. package/dist/cli/backend/src/services/monitoring/token-usage.service.d.ts.map +1 -1
  245. package/dist/cli/backend/src/services/monitoring/token-usage.service.js +3 -0
  246. package/dist/cli/backend/src/services/monitoring/token-usage.service.js.map +1 -1
  247. package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts +9 -0
  248. package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
  249. package/dist/cli/backend/src/services/slack/slack-team-channel.service.js +25 -0
  250. package/dist/cli/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
  251. package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.d.ts +24 -0
  252. package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.d.ts.map +1 -1
  253. package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.js +63 -1
  254. package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.js.map +1 -1
  255. package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts +19 -0
  256. package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
  257. package/dist/cli/backend/src/services/task-pool/task-pool.service.js +41 -0
  258. package/dist/cli/backend/src/services/task-pool/task-pool.service.js.map +1 -1
  259. package/dist/cli/backend/src/services/trace/trace-context.service.d.ts +42 -2
  260. package/dist/cli/backend/src/services/trace/trace-context.service.d.ts.map +1 -1
  261. package/dist/cli/backend/src/services/trace/trace-context.service.js +66 -5
  262. package/dist/cli/backend/src/services/trace/trace-context.service.js.map +1 -1
  263. package/dist/cli/backend/src/services/trace/trace-recorder.d.ts.map +1 -1
  264. package/dist/cli/backend/src/services/trace/trace-recorder.js +11 -0
  265. package/dist/cli/backend/src/services/trace/trace-recorder.js.map +1 -1
  266. package/dist/cli/backend/src/services/usage-ledger/ledger-hooks.d.ts +44 -0
  267. package/dist/cli/backend/src/services/usage-ledger/ledger-hooks.d.ts.map +1 -0
  268. package/dist/cli/backend/src/services/usage-ledger/ledger-hooks.js +52 -0
  269. package/dist/cli/backend/src/services/usage-ledger/ledger-hooks.js.map +1 -0
  270. package/dist/cli/cli/src/commands/team-move.d.ts +68 -0
  271. package/dist/cli/cli/src/commands/team-move.d.ts.map +1 -0
  272. package/dist/cli/cli/src/commands/team-move.js +155 -0
  273. package/dist/cli/cli/src/commands/team-move.js.map +1 -0
  274. package/dist/cli/cli/src/index.js +33 -0
  275. package/dist/cli/cli/src/index.js.map +1 -1
  276. package/package.json +1 -1
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Puts tickets into categories at report time.
3
+ *
4
+ * Order of attempts, cheapest first:
5
+ * 1. Deterministic: keywords (or the comma-separated phrases of the
6
+ * description), team and project hints.
7
+ * 2. Only for tickets nothing matched, and only when a model is available: one
8
+ * small batched question to the default cheap model.
9
+ * 3. Otherwise `other`.
10
+ *
11
+ * With no definitions the category is the ticket's team (else its project).
12
+ *
13
+ * Results are cached per (category-set id, ticket); a fallback `other` that
14
+ * came from having no model is not cached, so it is retried later. The cache
15
+ * lives beside the ledger and is never part of the ticket facts.
16
+ *
17
+ * @module services/usage-ledger/unit-classifier
18
+ */
19
+ import { promises as fs } from 'fs';
20
+ import * as path from 'path';
21
+ import { atomicWriteJson } from '../../utils/file-io.utils.js';
22
+ import { categorySetId } from './usage-categories.store.js';
23
+ /** Category of tickets that match nothing. */
24
+ export const OTHER_CATEGORY = 'other';
25
+ /** Category of usage that belongs to no ticket. */
26
+ export const NO_TICKET_CATEGORY = '(no ticket)';
27
+ /** File with the classification cache. */
28
+ export const CLASSIFICATION_CACHE_FILE = 'classification-cache.json';
29
+ /** Sets kept in the cache (oldest dropped). */
30
+ const MAX_CACHED_SETS = 6;
31
+ /** Tickets sent to the model in one call, and calls per report. */
32
+ const LLM_BATCH = 25;
33
+ const LLM_MAX_CALLS = 2;
34
+ /** Default classification (no user categories): the ticket's team, else its project. */
35
+ export function defaultCategory(unit) {
36
+ return unit.team?.trim() || unit.project?.trim() || OTHER_CATEGORY;
37
+ }
38
+ /**
39
+ * Keywords of a definition: its explicit keywords, else the comma / semicolon
40
+ * separated phrases of its description (so "Xiaohongshu, Substack, LinkedIn"
41
+ * works as typed).
42
+ *
43
+ * @param def - Category definition
44
+ * @returns Lower-case keywords
45
+ */
46
+ export function keywordsOf(def) {
47
+ const explicit = def.keywords ?? [];
48
+ const phrases = explicit.length > 0
49
+ ? []
50
+ : (def.description ?? '')
51
+ .split(/[,;,;、\n]+/)
52
+ .map((p) => p.trim())
53
+ .filter((p) => p.length >= 2 && p.length <= 40);
54
+ return [...new Set([...explicit, ...phrases].map((k) => k.toLowerCase()))];
55
+ }
56
+ /**
57
+ * Whether a lower-case text contains a keyword. Short ASCII keywords must
58
+ * match whole words ("ai" must not match "email").
59
+ */
60
+ function hasKeyword(text, keyword) {
61
+ if (/^[a-z0-9 ]+$/.test(keyword) && keyword.length <= 3) {
62
+ return new RegExp(`(^|[^a-z0-9])${keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}([^a-z0-9]|$)`).test(text);
63
+ }
64
+ return text.includes(keyword);
65
+ }
66
+ /**
67
+ * Best category by keyword / team / project hints.
68
+ *
69
+ * @param unit - The ticket
70
+ * @param defs - Definitions
71
+ * @returns Category name, or null when nothing matched
72
+ */
73
+ export function matchCategory(unit, defs) {
74
+ const text = [unit.title, unit.excerpt, unit.ticketId, unit.team, unit.project].filter(Boolean).join(' \n ').toLowerCase();
75
+ const team = (unit.team ?? '').toLowerCase();
76
+ const project = (unit.project ?? '').toLowerCase();
77
+ let best = null;
78
+ for (const def of defs) {
79
+ let score = 0;
80
+ for (const k of keywordsOf(def))
81
+ if (hasKeyword(text, k))
82
+ score += 1;
83
+ if (team && def.teams?.some((t) => t.toLowerCase() === team))
84
+ score += 2;
85
+ if (project && def.projects?.some((p) => p.toLowerCase() === project))
86
+ score += 2;
87
+ if (score > 0 && (!best || score > best.score))
88
+ best = { name: def.name, score };
89
+ }
90
+ return best?.name ?? null;
91
+ }
92
+ /**
93
+ * Classifies tickets for reports.
94
+ */
95
+ export class UnitClassifier {
96
+ llm;
97
+ cacheFile;
98
+ cache = null;
99
+ /**
100
+ * @param dir - Ledger directory
101
+ * @param llm - Optional model (null: deterministic only)
102
+ */
103
+ constructor(dir, llm = null) {
104
+ this.llm = llm;
105
+ this.cacheFile = path.join(dir, CLASSIFICATION_CACHE_FILE);
106
+ }
107
+ async loadCache() {
108
+ if (this.cache)
109
+ return this.cache;
110
+ try {
111
+ const parsed = JSON.parse(await fs.readFile(this.cacheFile, 'utf-8'));
112
+ this.cache = parsed && typeof parsed.sets === 'object' && parsed.sets ? parsed : { sets: {} };
113
+ }
114
+ catch {
115
+ this.cache = { sets: {} };
116
+ }
117
+ return this.cache;
118
+ }
119
+ /**
120
+ * Classify tickets.
121
+ *
122
+ * @param units - Tickets (never the "(no ticket)" pseudo-units)
123
+ * @param defs - The user's categories; empty means the default (team / project)
124
+ * @returns Category per ticket
125
+ */
126
+ async classify(units, defs) {
127
+ const categories = new Map();
128
+ if (defs.length === 0) {
129
+ for (const u of units)
130
+ categories.set(u.unitId, defaultCategory(u));
131
+ return { categories, llmClassified: 0, unclassified: 0 };
132
+ }
133
+ const setId = categorySetId(defs);
134
+ const cache = await this.loadCache();
135
+ const cached = cache.sets[setId] ?? {};
136
+ const fresh = {};
137
+ const unmatched = [];
138
+ for (const u of units) {
139
+ const known = cached[u.unitId];
140
+ if (known) {
141
+ categories.set(u.unitId, known);
142
+ continue;
143
+ }
144
+ const hit = matchCategory(u, defs);
145
+ if (hit) {
146
+ categories.set(u.unitId, hit);
147
+ fresh[u.unitId] = hit;
148
+ }
149
+ else {
150
+ unmatched.push(u);
151
+ }
152
+ }
153
+ let llmClassified = 0;
154
+ let unclassified = 0;
155
+ const answers = this.llm ? await this.askModel(unmatched, defs) : new Map();
156
+ for (const u of unmatched) {
157
+ const answer = answers.get(u.unitId);
158
+ if (answer) {
159
+ categories.set(u.unitId, answer);
160
+ fresh[u.unitId] = answer;
161
+ if (answer !== OTHER_CATEGORY)
162
+ llmClassified += 1;
163
+ }
164
+ else {
165
+ categories.set(u.unitId, OTHER_CATEGORY);
166
+ unclassified += 1;
167
+ }
168
+ }
169
+ if (Object.keys(fresh).length > 0)
170
+ await this.saveCache(setId, fresh);
171
+ return { categories, llmClassified, unclassified };
172
+ }
173
+ async saveCache(setId, fresh) {
174
+ const cache = await this.loadCache();
175
+ const merged = { ...(cache.sets[setId] ?? {}), ...fresh };
176
+ delete cache.sets[setId];
177
+ cache.sets[setId] = merged;
178
+ for (const key of Object.keys(cache.sets).slice(0, Math.max(0, Object.keys(cache.sets).length - MAX_CACHED_SETS)))
179
+ delete cache.sets[key];
180
+ try {
181
+ await fs.mkdir(path.dirname(this.cacheFile), { recursive: true });
182
+ await atomicWriteJson(this.cacheFile, cache);
183
+ }
184
+ catch {
185
+ // the cache is an optimisation
186
+ }
187
+ }
188
+ /**
189
+ * Ask the model about tickets nothing matched.
190
+ *
191
+ * @returns Ticket → category (only tickets the model answered validly)
192
+ */
193
+ async askModel(units, defs) {
194
+ const out = new Map();
195
+ if (!this.llm || units.length === 0)
196
+ return out;
197
+ const names = new Map(defs.map((d) => [d.name.toLowerCase(), d.name]));
198
+ const catalogue = defs.map((d) => `- ${d.name}${d.description ? `: ${d.description}` : ''}`).join('\n');
199
+ for (let call = 0; call < LLM_MAX_CALLS; call++) {
200
+ const batch = units.slice(call * LLM_BATCH, (call + 1) * LLM_BATCH);
201
+ if (batch.length === 0)
202
+ break;
203
+ const list = batch
204
+ .map((u, i) => `${i + 1}. ${[u.title, u.excerpt && u.excerpt !== u.title ? u.excerpt.slice(0, 160) : '', u.team ? `(team ${u.team})` : ''].filter(Boolean).join(' | ')}`)
205
+ .join('\n');
206
+ const prompt = `Assign each work item to exactly one category, or "${OTHER_CATEGORY}" if none fits.\n\nCategories:\n${catalogue}\n\nWork items:\n${list}\n\n` +
207
+ `Reply with only a JSON object mapping the item number to the category name, for example {"1":"${defs[0].name}","2":"${OTHER_CATEGORY}"}.`;
208
+ let reply = null;
209
+ try {
210
+ reply = await this.llm(prompt);
211
+ }
212
+ catch {
213
+ reply = null;
214
+ }
215
+ if (!reply)
216
+ break;
217
+ const json = /\{[\s\S]*\}/.exec(reply)?.[0];
218
+ if (!json)
219
+ continue;
220
+ try {
221
+ const parsed = JSON.parse(json);
222
+ batch.forEach((u, i) => {
223
+ const value = parsed[String(i + 1)];
224
+ if (typeof value !== 'string')
225
+ return;
226
+ const name = value.trim().toLowerCase();
227
+ if (name === OTHER_CATEGORY)
228
+ out.set(u.unitId, OTHER_CATEGORY);
229
+ else if (names.has(name))
230
+ out.set(u.unitId, names.get(name));
231
+ });
232
+ }
233
+ catch {
234
+ // unparseable answer: those tickets stay unclassified
235
+ }
236
+ }
237
+ return out;
238
+ }
239
+ }
240
+ /**
241
+ * The default cheap model: a small Anthropic model when `ANTHROPIC_API_KEY`
242
+ * is set; none otherwise.
243
+ *
244
+ * @returns A question function, or null
245
+ */
246
+ export function defaultCategoryLlm() {
247
+ if (!process.env.ANTHROPIC_API_KEY)
248
+ return null;
249
+ return async (prompt) => {
250
+ const { generateText } = await import('ai');
251
+ const { anthropic } = await import('@ai-sdk/anthropic');
252
+ const controller = new AbortController();
253
+ const timer = setTimeout(() => controller.abort(), 30_000);
254
+ try {
255
+ const result = await generateText({ model: anthropic('claude-haiku-4-5'), prompt, abortSignal: controller.signal, maxOutputTokens: 600 });
256
+ return result.text;
257
+ }
258
+ finally {
259
+ clearTimeout(timer);
260
+ }
261
+ };
262
+ }
263
+ //# sourceMappingURL=unit-classifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unit-classifier.js","sourceRoot":"","sources":["../../../../../../backend/src/services/usage-ledger/unit-classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAoB,MAAM,6BAA6B,CAAC;AAE9E,8CAA8C;AAC9C,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC;AACtC,mDAAmD;AACnD,MAAM,CAAC,MAAM,kBAAkB,GAAG,aAAa,CAAC;AAEhD,0CAA0C;AAC1C,MAAM,CAAC,MAAM,yBAAyB,GAAG,2BAA2B,CAAC;AAErE,+CAA+C;AAC/C,MAAM,eAAe,GAAG,CAAC,CAAC;AAC1B,mEAAmE;AACnE,MAAM,SAAS,GAAG,EAAE,CAAC;AACrB,MAAM,aAAa,GAAG,CAAC,CAAC;AA6BxB,wFAAwF;AACxF,MAAM,UAAU,eAAe,CAAC,IAAsB;IACrD,OAAO,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,cAAc,CAAC;AACpE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,GAAgB;IAC1C,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC;IACpC,MAAM,OAAO,GACZ,QAAQ,CAAC,MAAM,GAAG,CAAC;QAClB,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC;aACtB,KAAK,CAAC,YAAY,CAAC;aACnB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;aACpB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IACpD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC;AAC5E,CAAC;AAED;;;GAGG;AACH,SAAS,UAAU,CAAC,IAAY,EAAE,OAAe;IAChD,IAAI,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QACzD,OAAO,IAAI,MAAM,CAAC,gBAAgB,OAAO,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7G,CAAC;IACD,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AAC/B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,IAAsB,EAAE,IAAmB;IACxE,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;IAC3H,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAC7C,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IACnD,IAAI,IAAI,GAA2C,IAAI,CAAC;IACxD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACxB,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC;YAAE,IAAI,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;gBAAE,KAAK,IAAI,CAAC,CAAC;QACrE,IAAI,IAAI,IAAI,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC;YAAE,KAAK,IAAI,CAAC,CAAC;QACzE,IAAI,OAAO,IAAI,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,OAAO,CAAC;YAAE,KAAK,IAAI,CAAC,CAAC;QAClF,IAAI,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;YAAE,IAAI,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;IAClF,CAAC;IACD,OAAO,IAAI,EAAE,IAAI,IAAI,IAAI,CAAC;AAC3B,CAAC;AAED;;GAEG;AACH,MAAM,OAAO,cAAc;IAUR;IATD,SAAS,CAAS;IAC3B,KAAK,GAAqB,IAAI,CAAC;IAEvC;;;OAGG;IACH,YACC,GAAW,EACM,MAA0B,IAAI;QAA9B,QAAG,GAAH,GAAG,CAA2B;QAE/C,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,yBAAyB,CAAC,CAAC;IAC5D,CAAC;IAEO,KAAK,CAAC,SAAS;QACtB,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC;QAClC,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,CAAc,CAAC;YACnF,IAAI,CAAC,KAAK,GAAG,MAAM,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QAC/F,CAAC;QAAC,MAAM,CAAC;YACR,IAAI,CAAC,KAAK,GAAG,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QAC3B,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC;IACnB,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,QAAQ,CAAC,KAAyB,EAAE,IAAmB;QAC5D,MAAM,UAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC7C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,KAAK,MAAM,CAAC,IAAI,KAAK;gBAAE,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;YACpE,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;QAC1D,CAAC;QACD,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACvC,MAAM,KAAK,GAA2B,EAAE,CAAC;QACzC,MAAM,SAAS,GAAuB,EAAE,CAAC;QACzC,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;YACvB,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAC/B,IAAI,KAAK,EAAE,CAAC;gBACX,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;gBAChC,SAAS;YACV,CAAC;YACD,MAAM,GAAG,GAAG,aAAa,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;YACnC,IAAI,GAAG,EAAE,CAAC;gBACT,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;gBAC9B,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC;YACvB,CAAC;iBAAM,CAAC;gBACP,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACnB,CAAC;QACF,CAAC;QACD,IAAI,aAAa,GAAG,CAAC,CAAC;QACtB,IAAI,YAAY,GAAG,CAAC,CAAC;QACrB,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,EAAkB,CAAC;QAC5F,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;YAC3B,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YACrC,IAAI,MAAM,EAAE,CAAC;gBACZ,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBACjC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;gBACzB,IAAI,MAAM,KAAK,cAAc;oBAAE,aAAa,IAAI,CAAC,CAAC;YACnD,CAAC;iBAAM,CAAC;gBACP,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;gBACzC,YAAY,IAAI,CAAC,CAAC;YACnB,CAAC;QACF,CAAC;QACD,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACtE,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,CAAC;IACpD,CAAC;IAEO,KAAK,CAAC,SAAS,CAAC,KAAa,EAAE,KAA6B;QACnE,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC;QAC1D,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC;QAC3B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,eAAe,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC1I,IAAI,CAAC;YACJ,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAClE,MAAM,eAAe,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QAC9C,CAAC;QAAC,MAAM,CAAC;YACR,+BAA+B;QAChC,CAAC;IACF,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,QAAQ,CAAC,KAAyB,EAAE,IAAmB;QACpE,MAAM,GAAG,GAAG,IAAI,GAAG,EAAkB,CAAC;QACtC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,GAAG,CAAC;QAChD,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACvE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxG,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,aAAa,EAAE,IAAI,EAAE,EAAE,CAAC;YACjD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,SAAS,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;YACpE,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAM;YAC9B,MAAM,IAAI,GAAG,KAAK;iBAChB,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;iBACxK,IAAI,CAAC,IAAI,CAAC,CAAC;YACb,MAAM,MAAM,GACX,sDAAsD,cAAc,mCAAmC,SAAS,oBAAoB,IAAI,MAAM;gBAC9I,iGAAiG,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,UAAU,cAAc,KAAK,CAAC;YAC5I,IAAI,KAAK,GAAkB,IAAI,CAAC;YAChC,IAAI,CAAC;gBACJ,KAAK,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAChC,CAAC;YAAC,MAAM,CAAC;gBACR,KAAK,GAAG,IAAI,CAAC;YACd,CAAC;YACD,IAAI,CAAC,KAAK;gBAAE,MAAM;YAClB,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAC5C,IAAI,CAAC,IAAI;gBAAE,SAAS;YACpB,IAAI,CAAC;gBACJ,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAA4B,CAAC;gBAC3D,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;oBACtB,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;oBACpC,IAAI,OAAO,KAAK,KAAK,QAAQ;wBAAE,OAAO;oBACtC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;oBACxC,IAAI,IAAI,KAAK,cAAc;wBAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;yBAC1D,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;wBAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAW,CAAC,CAAC;gBACxE,CAAC,CAAC,CAAC;YACJ,CAAC;YAAC,MAAM,CAAC;gBACR,sDAAsD;YACvD,CAAC;QACF,CAAC;QACD,OAAO,GAAG,CAAC;IACZ,CAAC;CACD;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB;IACjC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB;QAAE,OAAO,IAAI,CAAC;IAChD,OAAO,KAAK,EAAE,MAAc,EAAE,EAAE;QAC/B,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QAC5C,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;QACxD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,CAAC;QAC3D,IAAI,CAAC;YACJ,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,EAAE,CAAC,CAAC;YAC1I,OAAO,MAAM,CAAC,IAAI,CAAC;QACpB,CAAC;gBAAS,CAAC;YACV,YAAY,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACF,CAAC,CAAC;AACH,CAAC"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * The owner's own categories for usage reports ("self-media", "book", …).
3
+ *
4
+ * Categories are optional and never written into ticket facts: they only
5
+ * decide how a report groups tickets. Each definition has a natural-language
6
+ * description and optional hints (keywords, team names, project names). The
7
+ * set carries a version that changes whenever a definition changes; the
8
+ * classification cache is keyed on it.
9
+ *
10
+ * @module services/usage-ledger/usage-categories.store
11
+ */
12
+ /** File holding the definitions. */
13
+ export declare const CATEGORIES_FILE = "categories.json";
14
+ /** One user-defined category. */
15
+ export interface CategoryDef {
16
+ /** Short label shown in reports */
17
+ name: string;
18
+ /** What belongs here, in plain words (also read by the language model) */
19
+ description?: string;
20
+ /** Words or phrases that put a ticket here (case-insensitive) */
21
+ keywords?: string[];
22
+ /** Team names that belong here */
23
+ teams?: string[];
24
+ /** Project names that belong here */
25
+ projects?: string[];
26
+ }
27
+ /**
28
+ * Validate and normalise a definition.
29
+ *
30
+ * @param input - Candidate
31
+ * @returns The definition, or an error text
32
+ */
33
+ export declare function normalizeCategory(input: unknown): CategoryDef | string;
34
+ /**
35
+ * Identity of a set of definitions: the same definitions always give the same
36
+ * id, so a classification cache survives a set being re-entered unchanged.
37
+ *
38
+ * @param defs - Definitions
39
+ * @returns `default` for none, else a short hash
40
+ */
41
+ export declare function categorySetId(defs: CategoryDef[]): string;
42
+ /**
43
+ * File-backed category definitions.
44
+ */
45
+ export declare class UsageCategoriesStore {
46
+ readonly dir: string;
47
+ private readonly file;
48
+ /**
49
+ * @param dir - Ledger directory (default `~/.crewly/usage-ledger`)
50
+ */
51
+ constructor(dir?: string);
52
+ private read;
53
+ private write;
54
+ /**
55
+ * The current definitions.
56
+ *
57
+ * @returns Version, set id and definitions
58
+ */
59
+ list(): Promise<{
60
+ version: number;
61
+ setId: string;
62
+ updatedAt: string;
63
+ categories: CategoryDef[];
64
+ }>;
65
+ /**
66
+ * Add or replace one category (matched by name, case-insensitive).
67
+ *
68
+ * @param input - The definition
69
+ * @returns The new state, or an error text
70
+ */
71
+ set(input: unknown): Promise<{
72
+ version: number;
73
+ categories: CategoryDef[];
74
+ } | string>;
75
+ /**
76
+ * Replace every definition.
77
+ *
78
+ * @param inputs - The new set (empty clears it)
79
+ * @returns The new state, or an error text
80
+ */
81
+ replaceAll(inputs: unknown[]): Promise<{
82
+ version: number;
83
+ categories: CategoryDef[];
84
+ } | string>;
85
+ /**
86
+ * Remove one category.
87
+ *
88
+ * @param name - Its name (case-insensitive)
89
+ * @returns The new state, or null when there was no such category
90
+ */
91
+ remove(name: string): Promise<{
92
+ version: number;
93
+ categories: CategoryDef[];
94
+ } | null>;
95
+ }
96
+ //# sourceMappingURL=usage-categories.store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage-categories.store.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/usage-ledger/usage-categories.store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AASH,oCAAoC;AACpC,eAAO,MAAM,eAAe,oBAAoB,CAAC;AAEjD,iCAAiC;AACjC,MAAM,WAAW,WAAW;IAC3B,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,kCAAkC;IAClC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AA0BD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,GAAG,MAAM,CAiBtE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,MAAM,CAIzD;AAED;;GAEG;AACH,qBAAa,oBAAoB;IAMpB,QAAQ,CAAC,GAAG,EAAE,MAAM;IALhC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAE9B;;OAEG;gBACkB,GAAG,GAAE,MAAwD;YAIpE,IAAI;YAYJ,KAAK;IAOnB;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,WAAW,EAAE,CAAA;KAAE,CAAC;IAKvG;;;;;OAKG;IACG,GAAG,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,WAAW,EAAE,CAAA;KAAE,GAAG,MAAM,CAAC;IAU3F;;;;;OAKG;IACG,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,WAAW,EAAE,CAAA;KAAE,GAAG,MAAM,CAAC;IAYrG;;;;;OAKG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,WAAW,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC;CAO1F"}
@@ -0,0 +1,166 @@
1
+ /**
2
+ * The owner's own categories for usage reports ("self-media", "book", …).
3
+ *
4
+ * Categories are optional and never written into ticket facts: they only
5
+ * decide how a report groups tickets. Each definition has a natural-language
6
+ * description and optional hints (keywords, team names, project names). The
7
+ * set carries a version that changes whenever a definition changes; the
8
+ * classification cache is keyed on it.
9
+ *
10
+ * @module services/usage-ledger/usage-categories.store
11
+ */
12
+ import { createHash } from 'crypto';
13
+ import { promises as fs } from 'fs';
14
+ import * as path from 'path';
15
+ import { atomicWriteJson } from '../../utils/file-io.utils.js';
16
+ import { getCrewlyHomePath } from '../core/crewly-home.utils.js';
17
+ import { LEDGER_DIR_NAME } from './usage-ledger.store.js';
18
+ /** File holding the definitions. */
19
+ export const CATEGORIES_FILE = 'categories.json';
20
+ /** Longest name / description / hint accepted. */
21
+ const MAX_TEXT = 400;
22
+ const MAX_HINTS = 60;
23
+ const MAX_CATEGORIES = 40;
24
+ /**
25
+ * Clean a list of hints.
26
+ *
27
+ * @param value - Candidate list
28
+ * @returns Trimmed, de-duplicated strings (empty list becomes undefined)
29
+ */
30
+ function cleanList(value) {
31
+ if (!Array.isArray(value))
32
+ return undefined;
33
+ const out = [...new Set(value.filter((v) => typeof v === 'string').map((v) => v.trim().slice(0, 120)).filter(Boolean))];
34
+ return out.length > 0 ? out.slice(0, MAX_HINTS) : undefined;
35
+ }
36
+ /**
37
+ * Validate and normalise a definition.
38
+ *
39
+ * @param input - Candidate
40
+ * @returns The definition, or an error text
41
+ */
42
+ export function normalizeCategory(input) {
43
+ if (!input || typeof input !== 'object')
44
+ return 'category must be an object';
45
+ const raw = input;
46
+ const name = typeof raw.name === 'string' ? raw.name.trim().slice(0, 60) : '';
47
+ if (!name)
48
+ return 'category needs a name';
49
+ if (name.toLowerCase() === 'other')
50
+ return '"other" is reserved for tickets that match nothing';
51
+ const description = typeof raw.description === 'string' && raw.description.trim() ? raw.description.trim().slice(0, MAX_TEXT) : undefined;
52
+ const keywords = cleanList(raw.keywords);
53
+ const teams = cleanList(raw.teams);
54
+ const projects = cleanList(raw.projects);
55
+ return {
56
+ name,
57
+ ...(description ? { description } : {}),
58
+ ...(keywords ? { keywords } : {}),
59
+ ...(teams ? { teams } : {}),
60
+ ...(projects ? { projects } : {}),
61
+ };
62
+ }
63
+ /**
64
+ * Identity of a set of definitions: the same definitions always give the same
65
+ * id, so a classification cache survives a set being re-entered unchanged.
66
+ *
67
+ * @param defs - Definitions
68
+ * @returns `default` for none, else a short hash
69
+ */
70
+ export function categorySetId(defs) {
71
+ if (defs.length === 0)
72
+ return 'default';
73
+ const canonical = defs.map((d) => [d.name.toLowerCase(), d.description ?? '', d.keywords ?? [], d.teams ?? [], d.projects ?? []]);
74
+ return createHash('sha1').update(JSON.stringify(canonical)).digest('hex').slice(0, 10);
75
+ }
76
+ /**
77
+ * File-backed category definitions.
78
+ */
79
+ export class UsageCategoriesStore {
80
+ dir;
81
+ file;
82
+ /**
83
+ * @param dir - Ledger directory (default `~/.crewly/usage-ledger`)
84
+ */
85
+ constructor(dir = path.join(getCrewlyHomePath(), LEDGER_DIR_NAME)) {
86
+ this.dir = dir;
87
+ this.file = path.join(dir, CATEGORIES_FILE);
88
+ }
89
+ async read() {
90
+ try {
91
+ const parsed = JSON.parse(await fs.readFile(this.file, 'utf-8'));
92
+ const categories = (Array.isArray(parsed.categories) ? parsed.categories : [])
93
+ .map(normalizeCategory)
94
+ .filter((c) => typeof c !== 'string');
95
+ return { version: Number(parsed.version) || 0, updatedAt: parsed.updatedAt ?? '', categories };
96
+ }
97
+ catch {
98
+ return { version: 0, updatedAt: '', categories: [] };
99
+ }
100
+ }
101
+ async write(categories, previousVersion) {
102
+ const next = { version: previousVersion + 1, updatedAt: new Date().toISOString(), categories };
103
+ await fs.mkdir(this.dir, { recursive: true });
104
+ await atomicWriteJson(this.file, next);
105
+ return next;
106
+ }
107
+ /**
108
+ * The current definitions.
109
+ *
110
+ * @returns Version, set id and definitions
111
+ */
112
+ async list() {
113
+ const file = await this.read();
114
+ return { version: file.version, setId: categorySetId(file.categories), updatedAt: file.updatedAt, categories: file.categories };
115
+ }
116
+ /**
117
+ * Add or replace one category (matched by name, case-insensitive).
118
+ *
119
+ * @param input - The definition
120
+ * @returns The new state, or an error text
121
+ */
122
+ async set(input) {
123
+ const def = normalizeCategory(input);
124
+ if (typeof def === 'string')
125
+ return def;
126
+ const file = await this.read();
127
+ const rest = file.categories.filter((c) => c.name.toLowerCase() !== def.name.toLowerCase());
128
+ if (rest.length >= MAX_CATEGORIES)
129
+ return `at most ${MAX_CATEGORIES} categories`;
130
+ const written = await this.write([...rest, def], file.version);
131
+ return { version: written.version, categories: written.categories };
132
+ }
133
+ /**
134
+ * Replace every definition.
135
+ *
136
+ * @param inputs - The new set (empty clears it)
137
+ * @returns The new state, or an error text
138
+ */
139
+ async replaceAll(inputs) {
140
+ const defs = [];
141
+ for (const input of inputs.slice(0, MAX_CATEGORIES)) {
142
+ const def = normalizeCategory(input);
143
+ if (typeof def === 'string')
144
+ return def;
145
+ defs.push(def);
146
+ }
147
+ const file = await this.read();
148
+ const written = await this.write(defs, file.version);
149
+ return { version: written.version, categories: written.categories };
150
+ }
151
+ /**
152
+ * Remove one category.
153
+ *
154
+ * @param name - Its name (case-insensitive)
155
+ * @returns The new state, or null when there was no such category
156
+ */
157
+ async remove(name) {
158
+ const file = await this.read();
159
+ const rest = file.categories.filter((c) => c.name.toLowerCase() !== name.trim().toLowerCase());
160
+ if (rest.length === file.categories.length)
161
+ return null;
162
+ const written = await this.write(rest, file.version);
163
+ return { version: written.version, categories: written.categories };
164
+ }
165
+ }
166
+ //# sourceMappingURL=usage-categories.store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage-categories.store.js","sourceRoot":"","sources":["../../../../../../backend/src/services/usage-ledger/usage-categories.store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * One-time rebuild of the ledger for the time before it existed.
3
+ *
4
+ * Sources: the token ledger (usage events) and the run traces (ticket roots,
5
+ * deliveries, busy turns, status changes). The traces expire after 90 days,
6
+ * the ledger does not, so this keeps what they know.
7
+ *
8
+ * Attribution of a usage event:
9
+ * - it already carries a trace id (the harness stamped it): `recorded`;
10
+ * - otherwise it goes to the trace the session's latest delivery named,
11
+ * when that delivery was at most {@link INFER_MAX_GAP_MS} before the event:
12
+ * `inferred`;
13
+ * - otherwise it lands in the agent's "(no ticket)" unit.
14
+ *
15
+ * Only facts older than the cutoff (the moment live recording began) are
16
+ * written, so history and live facts never overlap. The result goes to
17
+ * `backfill.jsonl`; the run is recorded in `state.json` and never repeats.
18
+ *
19
+ * @module services/usage-ledger/usage-ledger.backfill
20
+ */
21
+ import { type TokenUsageEvent } from '../monitoring/token-usage.service.js';
22
+ import type { TeamLookup } from './team-directory.js';
23
+ import type { UsageLedgerStore } from './usage-ledger.store.js';
24
+ /** An unticketed event this soon after a delivery is credited to the delivery's trace. */
25
+ export declare const INFER_MAX_GAP_MS: number;
26
+ /** File with the ledger's own bookkeeping. */
27
+ export declare const STATE_FILE = "state.json";
28
+ /** Bookkeeping persisted beside the facts. */
29
+ export interface LedgerState {
30
+ /** When live recording began (the backfill cutoff) */
31
+ liveSince?: string;
32
+ backfill?: {
33
+ doneAt: string;
34
+ cutoff: string;
35
+ facts: number;
36
+ usageEvents: number;
37
+ inferredEvents: number;
38
+ };
39
+ }
40
+ /** Where history comes from. */
41
+ export interface BackfillSources {
42
+ /** Usage events of every session */
43
+ forEachUsage: (visit: (session: string, event: TokenUsageEvent) => void) => void;
44
+ /** The traces directory (index.json + `<id>.jsonl`) */
45
+ traceDir: string;
46
+ directory: TeamLookup;
47
+ }
48
+ /** What a backfill did. */
49
+ export interface BackfillResult {
50
+ ran: boolean;
51
+ reason?: string;
52
+ facts: number;
53
+ usageEvents: number;
54
+ inferredEvents: number;
55
+ traces: number;
56
+ }
57
+ /**
58
+ * Read the ledger bookkeeping.
59
+ *
60
+ * @param dir - Ledger directory
61
+ * @returns State (empty when none)
62
+ */
63
+ export declare function readLedgerState(dir: string): Promise<LedgerState>;
64
+ /**
65
+ * Write the ledger bookkeeping.
66
+ *
67
+ * @param dir - Ledger directory
68
+ * @param state - State
69
+ */
70
+ export declare function writeLedgerState(dir: string, state: LedgerState): Promise<void>;
71
+ /**
72
+ * Note when live recording began (once).
73
+ *
74
+ * @param dir - Ledger directory
75
+ * @param now - Now (ms)
76
+ * @returns The cutoff
77
+ */
78
+ export declare function ensureLiveSince(dir: string, now?: number): Promise<string>;
79
+ /**
80
+ * Rebuild history into `backfill.jsonl`.
81
+ *
82
+ * @param store - Ledger store
83
+ * @param sources - Where history comes from
84
+ * @param opts.cutoffMs - Only facts before this time are written
85
+ * @returns What happened
86
+ */
87
+ export declare function runBackfill(store: UsageLedgerStore, sources: BackfillSources, opts: {
88
+ cutoffMs: number;
89
+ }): Promise<BackfillResult>;
90
+ //# sourceMappingURL=usage-ledger.backfill.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage-ledger.backfill.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/usage-ledger/usage-ledger.backfill.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAOH,OAAO,EAA6B,KAAK,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAGvG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,0FAA0F;AAC1F,eAAO,MAAM,gBAAgB,QAAgC,CAAC;AAE9D,8CAA8C;AAC9C,eAAO,MAAM,UAAU,eAAe,CAAC;AAEvC,8CAA8C;AAC9C,MAAM,WAAW,WAAW;IAC3B,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,CAAC;CAC1G;AAED,gCAAgC;AAChC,MAAM,WAAW,eAAe;IAC/B,oCAAoC;IACpC,YAAY,EAAE,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,KAAK,IAAI,KAAK,IAAI,CAAC;IACjF,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,UAAU,CAAC;CACtB;AAED,2BAA2B;AAC3B,MAAM,WAAW,cAAc;IAC9B,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAMvE;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrF;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAM5F;AAUD;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAsMxI"}