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,189 @@
1
+ /**
2
+ * In-memory fold of the ledger files into per-unit rollups.
3
+ *
4
+ * Reads are incremental: each file's byte offset is remembered, so a refresh
5
+ * only parses what was appended since the last one (the first refresh after a
6
+ * restart reads everything once). The rollup is derived data; the files stay
7
+ * the source of truth.
8
+ *
9
+ * @module services/usage-ledger/ledger-rollup
10
+ */
11
+ import { promises as fs } from 'fs';
12
+ import * as path from 'path';
13
+ import { UNTICKETED_PREFIX } from './usage-ledger.types.js';
14
+ const READ_CHUNK = 1 << 20;
15
+ /**
16
+ * Per-unit rollups of the ledger.
17
+ */
18
+ export class LedgerRollup {
19
+ units = new Map();
20
+ offsets = new Map();
21
+ running = Promise.resolve();
22
+ /** Facts folded in so far */
23
+ factCount = 0;
24
+ /**
25
+ * Fold in whatever was appended since the last refresh. Calls are serialised.
26
+ *
27
+ * @param store - The ledger store
28
+ */
29
+ refresh(store) {
30
+ this.running = this.running.then(() => this.run(store)).catch(() => undefined);
31
+ return this.running;
32
+ }
33
+ async run(store) {
34
+ const files = await store.listFiles();
35
+ for (const name of files) {
36
+ const file = path.join(store.dir, name);
37
+ let size;
38
+ try {
39
+ size = (await fs.stat(file)).size;
40
+ }
41
+ catch {
42
+ continue;
43
+ }
44
+ const known = this.offsets.get(name) ?? 0;
45
+ if (size < known) {
46
+ // A file got shorter: the ledger was edited from outside. Start over.
47
+ this.units.clear();
48
+ this.offsets.clear();
49
+ this.factCount = 0;
50
+ await this.run(store);
51
+ return;
52
+ }
53
+ if (size === known)
54
+ continue;
55
+ this.offsets.set(name, known + (await this.readNew(file, known, size)));
56
+ }
57
+ }
58
+ /**
59
+ * Read complete lines from `from` to `size`.
60
+ *
61
+ * @returns Bytes consumed (up to and including the last newline)
62
+ */
63
+ async readNew(file, from, size) {
64
+ const handle = await fs.open(file, 'r');
65
+ try {
66
+ let consumed = 0;
67
+ let pending = Buffer.alloc(0);
68
+ let position = from;
69
+ while (position < size) {
70
+ const buf = Buffer.alloc(Math.min(READ_CHUNK, size - position));
71
+ const { bytesRead } = await handle.read(buf, 0, buf.length, position);
72
+ if (bytesRead === 0)
73
+ break;
74
+ position += bytesRead;
75
+ pending = Buffer.concat([pending, buf.subarray(0, bytesRead)]);
76
+ const lastNewline = pending.lastIndexOf(0x0a);
77
+ if (lastNewline < 0)
78
+ continue;
79
+ const complete = pending.subarray(0, lastNewline + 1).toString('utf-8');
80
+ pending = pending.subarray(lastNewline + 1);
81
+ consumed += Buffer.byteLength(complete);
82
+ for (const line of complete.split('\n'))
83
+ this.foldLine(line);
84
+ }
85
+ return consumed;
86
+ }
87
+ finally {
88
+ await handle.close();
89
+ }
90
+ }
91
+ foldLine(line) {
92
+ if (!line.trim())
93
+ return;
94
+ try {
95
+ const fact = JSON.parse(line);
96
+ if (fact && typeof fact === 'object' && typeof fact.unitId === 'string')
97
+ this.fold(fact);
98
+ }
99
+ catch {
100
+ // a torn or foreign line
101
+ }
102
+ }
103
+ unit(unitId, ms) {
104
+ let u = this.units.get(unitId);
105
+ if (!u) {
106
+ u = {
107
+ unitId,
108
+ synthetic: unitId.startsWith(UNTICKETED_PREFIX),
109
+ firstMs: ms,
110
+ lastMs: ms,
111
+ agents: new Map(),
112
+ usage: [],
113
+ busy: [],
114
+ ownerMessages: [],
115
+ inferred: false,
116
+ };
117
+ this.units.set(unitId, u);
118
+ }
119
+ if (Number.isFinite(ms)) {
120
+ u.firstMs = Math.min(u.firstMs, ms);
121
+ u.lastMs = Math.max(u.lastMs, ms);
122
+ }
123
+ return u;
124
+ }
125
+ /**
126
+ * Fold one fact in.
127
+ *
128
+ * @param fact - A ledger fact
129
+ */
130
+ fold(fact) {
131
+ const ms = Date.parse(fact.ts);
132
+ const u = this.unit(fact.unitId, ms);
133
+ this.factCount += 1;
134
+ if (fact.attribution === 'inferred')
135
+ u.inferred = true;
136
+ switch (fact.kind) {
137
+ case 'unit.meta': {
138
+ // First value wins; later facts only fill gaps.
139
+ u.ticketId ??= fact.ticketId;
140
+ u.requestId ??= fact.requestId;
141
+ u.rootKind ??= fact.rootKind;
142
+ u.title ||= fact.title;
143
+ u.excerpt ||= fact.excerpt;
144
+ u.project ??= fact.project;
145
+ u.team ??= fact.team;
146
+ u.channel ??= fact.channel;
147
+ u.threadRef ??= fact.threadRef;
148
+ u.openedAt ??= fact.openedAt;
149
+ break;
150
+ }
151
+ case 'unit.agent': {
152
+ const known = u.agents.get(fact.agent);
153
+ u.agents.set(fact.agent, { team: known?.team ?? fact.team, project: known?.project ?? fact.project });
154
+ break;
155
+ }
156
+ case 'unit.closed': {
157
+ if (!u.closedAt || fact.ts > u.closedAt) {
158
+ u.closedAt = fact.ts;
159
+ u.closedStatus = fact.status;
160
+ }
161
+ break;
162
+ }
163
+ case 'usage':
164
+ u.usage.push({
165
+ hourMs: ms,
166
+ agent: fact.agent,
167
+ team: fact.team,
168
+ model: fact.model,
169
+ share: fact.share,
170
+ input: fact.input,
171
+ cachedInput: fact.cachedInput,
172
+ output: fact.output,
173
+ cacheWrite: fact.cacheWrite,
174
+ budget: fact.budget,
175
+ costUsd: fact.costUsd,
176
+ attribution: fact.attribution,
177
+ });
178
+ break;
179
+ case 'busy':
180
+ u.busy.push({ hourMs: ms, agent: fact.agent, team: fact.team, busyMs: fact.busyMs, share: fact.share, attribution: fact.attribution });
181
+ break;
182
+ case 'owner.message':
183
+ if (Number.isFinite(ms))
184
+ u.ownerMessages.push(ms);
185
+ break;
186
+ }
187
+ }
188
+ }
189
+ //# sourceMappingURL=ledger-rollup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger-rollup.js","sourceRoot":"","sources":["../../../../../../backend/src/services/usage-ledger/ledger-rollup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Owner time per ticket, from owner message times.
3
+ *
4
+ * Heuristic (specs/2026-10-11-usage-ledger.md): owner messages on one ticket
5
+ * that follow each other within {@link OWNER_SPAN_GAP_MS} form one active
6
+ * span. A span runs from its first message to its last message plus
7
+ * {@link OWNER_MESSAGE_CREDIT_MS} (the time spent writing that last message),
8
+ * so a single message counts for one minute. When spans of different tickets
9
+ * overlap in time, the overlap is split evenly between them, so the owner's
10
+ * minutes are never counted twice.
11
+ *
12
+ * @module services/usage-ledger/owner-spans
13
+ */
14
+ /** Messages at most this far apart belong to the same span. */
15
+ export declare const OWNER_SPAN_GAP_MS: number;
16
+ /** Time credited to the last (or only) message of a span. */
17
+ export declare const OWNER_MESSAGE_CREDIT_MS = 60000;
18
+ /** One stretch of owner activity on a ticket. */
19
+ export interface OwnerSpan {
20
+ unitId: string;
21
+ startMs: number;
22
+ endMs: number;
23
+ }
24
+ /**
25
+ * Group one ticket's owner message times into spans.
26
+ *
27
+ * @param unitId - Ticket (unit) id
28
+ * @param times - Message times (ms), any order, duplicates allowed
29
+ * @returns Spans, oldest first
30
+ */
31
+ export declare function buildOwnerSpans(unitId: string, times: number[]): OwnerSpan[];
32
+ /**
33
+ * Owner minutes per ticket inside a time window, with overlaps split evenly.
34
+ *
35
+ * @param spans - Spans of any tickets
36
+ * @param fromMs - Window start (ms)
37
+ * @param toMs - Window end (ms)
38
+ * @returns Ticket id → minutes
39
+ */
40
+ export declare function ownerMinutesByUnit(spans: OwnerSpan[], fromMs: number, toMs: number): Map<string, number>;
41
+ //# sourceMappingURL=owner-spans.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"owner-spans.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/usage-ledger/owner-spans.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,+DAA+D;AAC/D,eAAO,MAAM,iBAAiB,QAAc,CAAC;AAC7C,6DAA6D;AAC7D,eAAO,MAAM,uBAAuB,QAAS,CAAC;AAE9C,iDAAiD;AACjD,MAAM,WAAW,SAAS;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACd;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,CAgB5E;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAgBxG"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Owner time per ticket, from owner message times.
3
+ *
4
+ * Heuristic (specs/2026-10-11-usage-ledger.md): owner messages on one ticket
5
+ * that follow each other within {@link OWNER_SPAN_GAP_MS} form one active
6
+ * span. A span runs from its first message to its last message plus
7
+ * {@link OWNER_MESSAGE_CREDIT_MS} (the time spent writing that last message),
8
+ * so a single message counts for one minute. When spans of different tickets
9
+ * overlap in time, the overlap is split evenly between them, so the owner's
10
+ * minutes are never counted twice.
11
+ *
12
+ * @module services/usage-ledger/owner-spans
13
+ */
14
+ /** Messages at most this far apart belong to the same span. */
15
+ export const OWNER_SPAN_GAP_MS = 10 * 60_000;
16
+ /** Time credited to the last (or only) message of a span. */
17
+ export const OWNER_MESSAGE_CREDIT_MS = 60_000;
18
+ /**
19
+ * Group one ticket's owner message times into spans.
20
+ *
21
+ * @param unitId - Ticket (unit) id
22
+ * @param times - Message times (ms), any order, duplicates allowed
23
+ * @returns Spans, oldest first
24
+ */
25
+ export function buildOwnerSpans(unitId, times) {
26
+ const sorted = [...new Set(times.filter((t) => Number.isFinite(t)))].sort((a, b) => a - b);
27
+ const spans = [];
28
+ let start = null;
29
+ let last = 0;
30
+ for (const t of sorted) {
31
+ if (start !== null && t - last <= OWNER_SPAN_GAP_MS) {
32
+ last = t;
33
+ continue;
34
+ }
35
+ if (start !== null)
36
+ spans.push({ unitId, startMs: start, endMs: last + OWNER_MESSAGE_CREDIT_MS });
37
+ start = t;
38
+ last = t;
39
+ }
40
+ if (start !== null)
41
+ spans.push({ unitId, startMs: start, endMs: last + OWNER_MESSAGE_CREDIT_MS });
42
+ return spans;
43
+ }
44
+ /**
45
+ * Owner minutes per ticket inside a time window, with overlaps split evenly.
46
+ *
47
+ * @param spans - Spans of any tickets
48
+ * @param fromMs - Window start (ms)
49
+ * @param toMs - Window end (ms)
50
+ * @returns Ticket id → minutes
51
+ */
52
+ export function ownerMinutesByUnit(spans, fromMs, toMs) {
53
+ const clipped = spans
54
+ .map((s) => ({ unitId: s.unitId, startMs: Math.max(s.startMs, fromMs), endMs: Math.min(s.endMs, toMs) }))
55
+ .filter((s) => s.endMs > s.startMs);
56
+ const points = [...new Set(clipped.flatMap((s) => [s.startMs, s.endMs]))].sort((a, b) => a - b);
57
+ const minutes = new Map();
58
+ for (let i = 0; i < points.length - 1; i++) {
59
+ const a = points[i];
60
+ const b = points[i + 1];
61
+ const active = clipped.filter((s) => s.startMs <= a && s.endMs >= b);
62
+ if (active.length === 0)
63
+ continue;
64
+ const units = [...new Set(active.map((s) => s.unitId))];
65
+ const each = (b - a) / 60_000 / units.length;
66
+ for (const u of units)
67
+ minutes.set(u, (minutes.get(u) ?? 0) + each);
68
+ }
69
+ return minutes;
70
+ }
71
+ //# sourceMappingURL=owner-spans.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"owner-spans.js","sourceRoot":"","sources":["../../../../../../backend/src/services/usage-ledger/owner-spans.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Which team and project an agent session belongs to, read from the team
3
+ * configs under CREWLY_HOME (`teams/<id>/config.json`, `projects.json`).
4
+ * Cached; a stale cache is refreshed in the background so hot paths stay
5
+ * synchronous and cheap.
6
+ *
7
+ * @module services/usage-ledger/team-directory
8
+ */
9
+ /** Team and project of a session. */
10
+ export interface TeamInfo {
11
+ team?: string;
12
+ project?: string;
13
+ }
14
+ /** What the ledger asks of a directory. */
15
+ export interface TeamLookup {
16
+ /** Team / project of an agent session (sync, from cache) */
17
+ lookup(session: string): TeamInfo | null;
18
+ /** Project name for a project id */
19
+ projectName(projectId: string): string | undefined;
20
+ /** Wait until the cache has been loaded at least once */
21
+ ready(): Promise<void>;
22
+ }
23
+ /**
24
+ * Reads team and project names from disk.
25
+ */
26
+ export declare class TeamDirectory implements TeamLookup {
27
+ private readonly home;
28
+ private readonly now;
29
+ private sessions;
30
+ private projects;
31
+ private loadedAt;
32
+ private loading;
33
+ /**
34
+ * @param home - Crewly home (default CREWLY_HOME)
35
+ * @param now - Clock (ms)
36
+ */
37
+ constructor(home?: string, now?: () => number);
38
+ lookup(session: string): TeamInfo | null;
39
+ projectName(projectId: string): string | undefined;
40
+ ready(): Promise<void>;
41
+ /**
42
+ * Reload the cache (one load at a time).
43
+ */
44
+ refresh(): Promise<void>;
45
+ private load;
46
+ }
47
+ //# sourceMappingURL=team-directory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team-directory.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/usage-ledger/team-directory.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,qCAAqC;AACrC,MAAM,WAAW,QAAQ;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,2CAA2C;AAC3C,MAAM,WAAW,UAAU;IAC1B,4DAA4D;IAC5D,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC;IACzC,oCAAoC;IACpC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACnD,yDAAyD;IACzD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAmBD;;GAEG;AACH,qBAAa,aAAc,YAAW,UAAU;IAW9C,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAXrB,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,OAAO,CAA8B;IAE7C;;;OAGG;gBAEe,IAAI,GAAE,MAA4B,EAClC,GAAG,GAAE,MAAM,MAAyB;IAGtD,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI;IAMxC,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI5C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5B;;OAEG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YAWV,IAAI;CAiClB"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Which team and project an agent session belongs to, read from the team
3
+ * configs under CREWLY_HOME (`teams/<id>/config.json`, `projects.json`).
4
+ * Cached; a stale cache is refreshed in the background so hot paths stay
5
+ * synchronous and cheap.
6
+ *
7
+ * @module services/usage-ledger/team-directory
8
+ */
9
+ import { promises as fs } from 'fs';
10
+ import * as path from 'path';
11
+ import { getCrewlyHomePath } from '../core/crewly-home.utils.js';
12
+ /** How long a loaded cache is trusted. */
13
+ const TTL_MS = 60_000;
14
+ /** The orchestrator's session is not in any team. */
15
+ const ORCHESTRATOR_SESSION = /^crewly-orc(?:$|-)/;
16
+ /**
17
+ * Reads team and project names from disk.
18
+ */
19
+ export class TeamDirectory {
20
+ home;
21
+ now;
22
+ sessions = new Map();
23
+ projects = new Map();
24
+ loadedAt = 0;
25
+ loading = null;
26
+ /**
27
+ * @param home - Crewly home (default CREWLY_HOME)
28
+ * @param now - Clock (ms)
29
+ */
30
+ constructor(home = getCrewlyHomePath(), now = () => Date.now()) {
31
+ this.home = home;
32
+ this.now = now;
33
+ }
34
+ lookup(session) {
35
+ if (this.now() - this.loadedAt > TTL_MS)
36
+ void this.refresh();
37
+ if (ORCHESTRATOR_SESSION.test(session))
38
+ return { team: 'Orchestrator' };
39
+ return this.sessions.get(session) ?? null;
40
+ }
41
+ projectName(projectId) {
42
+ return this.projects.get(projectId);
43
+ }
44
+ async ready() {
45
+ if (this.loadedAt === 0)
46
+ await this.refresh();
47
+ else if (this.loading)
48
+ await this.loading;
49
+ }
50
+ /**
51
+ * Reload the cache (one load at a time).
52
+ */
53
+ refresh() {
54
+ if (!this.loading) {
55
+ this.loading = this.load()
56
+ .catch(() => undefined)
57
+ .finally(() => {
58
+ this.loading = null;
59
+ });
60
+ }
61
+ return this.loading;
62
+ }
63
+ async load() {
64
+ const projects = new Map();
65
+ try {
66
+ const list = JSON.parse(await fs.readFile(path.join(this.home, 'projects.json'), 'utf-8'));
67
+ for (const p of Array.isArray(list) ? list : [])
68
+ if (p?.id && p.name)
69
+ projects.set(p.id, p.name);
70
+ }
71
+ catch {
72
+ // no projects file
73
+ }
74
+ const sessions = new Map();
75
+ try {
76
+ const teamsDir = path.join(this.home, 'teams');
77
+ for (const entry of await fs.readdir(teamsDir, { withFileTypes: true })) {
78
+ if (!entry.isDirectory())
79
+ continue;
80
+ try {
81
+ const team = JSON.parse(await fs.readFile(path.join(teamsDir, entry.name, 'config.json'), 'utf-8'));
82
+ if (!team?.name)
83
+ continue;
84
+ const project = team.projectIds?.map((id) => projects.get(id)).find((n) => !!n);
85
+ const info = { team: team.name, ...(project ? { project } : {}) };
86
+ for (const m of team.members ?? []) {
87
+ if (m.sessionName)
88
+ sessions.set(m.sessionName, info);
89
+ if (m.agentId)
90
+ sessions.set(m.agentId, info);
91
+ }
92
+ }
93
+ catch {
94
+ // unreadable team
95
+ }
96
+ }
97
+ }
98
+ catch {
99
+ // no teams dir
100
+ }
101
+ this.sessions = sessions;
102
+ this.projects = projects;
103
+ this.loadedAt = this.now();
104
+ }
105
+ }
106
+ //# sourceMappingURL=team-directory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team-directory.js","sourceRoot":"","sources":["../../../../../../backend/src/services/usage-ledger/team-directory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,101 @@
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 { type CategoryDef } from './usage-categories.store.js';
20
+ /** Category of tickets that match nothing. */
21
+ export declare const OTHER_CATEGORY = "other";
22
+ /** Category of usage that belongs to no ticket. */
23
+ export declare const NO_TICKET_CATEGORY = "(no ticket)";
24
+ /** File with the classification cache. */
25
+ export declare const CLASSIFICATION_CACHE_FILE = "classification-cache.json";
26
+ /** What the classifier reads about a ticket. */
27
+ export interface ClassifiableUnit {
28
+ unitId: string;
29
+ ticketId?: string;
30
+ title?: string;
31
+ excerpt?: string;
32
+ team?: string;
33
+ project?: string;
34
+ }
35
+ /** Asks a small model one question; null when none is available or it failed. */
36
+ export type CategoryLlm = (prompt: string) => Promise<string | null>;
37
+ /** Where tickets ended up. */
38
+ export interface ClassifyResult {
39
+ /** Ticket → category */
40
+ categories: Map<string, string>;
41
+ /** Tickets answered by the model in this call */
42
+ llmClassified: number;
43
+ /** Tickets that matched nothing and had no model to ask */
44
+ unclassified: number;
45
+ }
46
+ /** Default classification (no user categories): the ticket's team, else its project. */
47
+ export declare function defaultCategory(unit: ClassifiableUnit): string;
48
+ /**
49
+ * Keywords of a definition: its explicit keywords, else the comma / semicolon
50
+ * separated phrases of its description (so "Xiaohongshu, Substack, LinkedIn"
51
+ * works as typed).
52
+ *
53
+ * @param def - Category definition
54
+ * @returns Lower-case keywords
55
+ */
56
+ export declare function keywordsOf(def: CategoryDef): string[];
57
+ /**
58
+ * Best category by keyword / team / project hints.
59
+ *
60
+ * @param unit - The ticket
61
+ * @param defs - Definitions
62
+ * @returns Category name, or null when nothing matched
63
+ */
64
+ export declare function matchCategory(unit: ClassifiableUnit, defs: CategoryDef[]): string | null;
65
+ /**
66
+ * Classifies tickets for reports.
67
+ */
68
+ export declare class UnitClassifier {
69
+ private readonly llm;
70
+ private readonly cacheFile;
71
+ private cache;
72
+ /**
73
+ * @param dir - Ledger directory
74
+ * @param llm - Optional model (null: deterministic only)
75
+ */
76
+ constructor(dir: string, llm?: CategoryLlm | null);
77
+ private loadCache;
78
+ /**
79
+ * Classify tickets.
80
+ *
81
+ * @param units - Tickets (never the "(no ticket)" pseudo-units)
82
+ * @param defs - The user's categories; empty means the default (team / project)
83
+ * @returns Category per ticket
84
+ */
85
+ classify(units: ClassifiableUnit[], defs: CategoryDef[]): Promise<ClassifyResult>;
86
+ private saveCache;
87
+ /**
88
+ * Ask the model about tickets nothing matched.
89
+ *
90
+ * @returns Ticket → category (only tickets the model answered validly)
91
+ */
92
+ private askModel;
93
+ }
94
+ /**
95
+ * The default cheap model: a small Anthropic model when `ANTHROPIC_API_KEY`
96
+ * is set; none otherwise.
97
+ *
98
+ * @returns A question function, or null
99
+ */
100
+ export declare function defaultCategoryLlm(): CategoryLlm | null;
101
+ //# sourceMappingURL=unit-classifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unit-classifier.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/usage-ledger/unit-classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAKH,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAE9E,8CAA8C;AAC9C,eAAO,MAAM,cAAc,UAAU,CAAC;AACtC,mDAAmD;AACnD,eAAO,MAAM,kBAAkB,gBAAgB,CAAC;AAEhD,0CAA0C;AAC1C,eAAO,MAAM,yBAAyB,8BAA8B,CAAC;AAQrE,gDAAgD;AAChD,MAAM,WAAW,gBAAgB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,iFAAiF;AACjF,MAAM,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAErE,8BAA8B;AAC9B,MAAM,WAAW,cAAc;IAC9B,wBAAwB;IACxB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,iDAAiD;IACjD,aAAa,EAAE,MAAM,CAAC;IACtB,2DAA2D;IAC3D,YAAY,EAAE,MAAM,CAAC;CACrB;AAMD,wFAAwF;AACxF,wBAAgB,eAAe,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAE9D;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,EAAE,CAUrD;AAaD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,MAAM,GAAG,IAAI,CAaxF;AAED;;GAEG;AACH,qBAAa,cAAc;IAUzB,OAAO,CAAC,QAAQ,CAAC,GAAG;IATrB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,KAAK,CAA0B;IAEvC;;;OAGG;gBAEF,GAAG,EAAE,MAAM,EACM,GAAG,GAAE,WAAW,GAAG,IAAW;YAKlC,SAAS;IAWvB;;;;;;OAMG;IACG,QAAQ,CAAC,KAAK,EAAE,gBAAgB,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC;YA2CzE,SAAS;IAcvB;;;;OAIG;YACW,QAAQ;CAsCtB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,IAAI,WAAW,GAAG,IAAI,CAcvD"}