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.
- package/config/roles/developer/prompt.md +7 -0
- package/config/roles/fullstack-dev/prompt.md +7 -0
- package/config/roles/product-manager/prompt.md +7 -0
- package/config/skills/orchestrator/move-team/SKILL.md +53 -0
- package/config/skills/orchestrator/move-team/execute.sh +42 -0
- package/config/skills/orchestrator/usage-categories/SKILL.md +59 -0
- package/config/skills/orchestrator/usage-categories/execute.sh +81 -0
- package/config/skills/orchestrator/usage-categories/execute.test.sh +45 -0
- package/config/skills/orchestrator/usage-report/SKILL.md +71 -0
- package/config/skills/orchestrator/usage-report/execute.sh +84 -0
- package/config/skills/orchestrator/usage-report/execute.test.sh +39 -0
- package/config/skills/team-leader/usage-report/SKILL.md +71 -0
- package/config/skills/team-leader/usage-report/execute.sh +84 -0
- package/config/skills/team-leader/usage-report/execute.test.sh +39 -0
- package/dist/backend/backend/src/constants.d.ts +80 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +80 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/team-move/team-move.controller.d.ts +45 -0
- package/dist/backend/backend/src/controllers/team-move/team-move.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/team-move/team-move.controller.js +214 -0
- package/dist/backend/backend/src/controllers/team-move/team-move.controller.js.map +1 -0
- package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.controller.d.ts +36 -0
- package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.controller.js +158 -0
- package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.controller.js.map +1 -0
- package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.routes.d.ts +15 -0
- package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.routes.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.routes.js +26 -0
- package/dist/backend/backend/src/controllers/usage-ledger/usage-ledger.routes.js.map +1 -0
- package/dist/backend/backend/src/index.d.ts +10 -0
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +97 -20
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.js +6 -0
- package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
- package/dist/backend/backend/src/services/agent/follow-through.wiring.d.ts +32 -0
- package/dist/backend/backend/src/services/agent/follow-through.wiring.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/follow-through.wiring.js +42 -1
- package/dist/backend/backend/src/services/agent/follow-through.wiring.js.map +1 -1
- package/dist/backend/backend/src/services/agent/resource-mode.service.d.ts +26 -6
- package/dist/backend/backend/src/services/agent/resource-mode.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/resource-mode.service.js +59 -5
- package/dist/backend/backend/src/services/agent/resource-mode.service.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-sync.types.js +1 -0
- package/dist/backend/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
- package/dist/backend/backend/src/services/core/storage.service.d.ts +18 -0
- package/dist/backend/backend/src/services/core/storage.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/core/storage.service.js +16 -0
- package/dist/backend/backend/src/services/core/storage.service.js.map +1 -1
- package/dist/backend/backend/src/services/decisions/decision.service.d.ts +19 -0
- package/dist/backend/backend/src/services/decisions/decision.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/decisions/decision.service.js +46 -0
- package/dist/backend/backend/src/services/decisions/decision.service.js.map +1 -1
- package/dist/backend/backend/src/services/monitoring/agent-turn-state.d.ts +35 -0
- package/dist/backend/backend/src/services/monitoring/agent-turn-state.d.ts.map +1 -1
- package/dist/backend/backend/src/services/monitoring/agent-turn-state.js +101 -0
- package/dist/backend/backend/src/services/monitoring/agent-turn-state.js.map +1 -1
- package/dist/backend/backend/src/services/monitoring/background-work-watchdog.d.ts +58 -0
- package/dist/backend/backend/src/services/monitoring/background-work-watchdog.d.ts.map +1 -0
- package/dist/backend/backend/src/services/monitoring/background-work-watchdog.js +100 -0
- package/dist/backend/backend/src/services/monitoring/background-work-watchdog.js.map +1 -0
- package/dist/backend/backend/src/services/monitoring/token-usage.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/monitoring/token-usage.service.js +3 -0
- package/dist/backend/backend/src/services/monitoring/token-usage.service.js.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts +7 -0
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js +15 -0
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-initializer.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-initializer.js +2 -1
- package/dist/backend/backend/src/services/slack/slack-initializer.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts +9 -0
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.js +25 -0
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.d.ts +24 -0
- package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.js +63 -1
- package/dist/backend/backend/src/services/slack/slack-typing-placeholder.service.js.map +1 -1
- package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts +19 -0
- package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/task-pool/task-pool.service.js +41 -0
- package/dist/backend/backend/src/services/task-pool/task-pool.service.js.map +1 -1
- package/dist/backend/backend/src/services/team-move/team-move-archive.d.ts +122 -0
- package/dist/backend/backend/src/services/team-move/team-move-archive.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-archive.js +202 -0
- package/dist/backend/backend/src/services/team-move/team-move-archive.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-chat.d.ts +59 -0
- package/dist/backend/backend/src/services/team-move/team-move-chat.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-chat.js +133 -0
- package/dist/backend/backend/src/services/team-move/team-move-chat.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-cloud.client.d.ts +102 -0
- package/dist/backend/backend/src/services/team-move/team-move-cloud.client.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-cloud.client.js +217 -0
- package/dist/backend/backend/src/services/team-move/team-move-cloud.client.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-crypto.d.ts +102 -0
- package/dist/backend/backend/src/services/team-move/team-move-crypto.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-crypto.js +348 -0
- package/dist/backend/backend/src/services/team-move/team-move-crypto.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-driver.service.d.ts +209 -0
- package/dist/backend/backend/src/services/team-move/team-move-driver.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-driver.service.js +504 -0
- package/dist/backend/backend/src/services/team-move/team-move-driver.service.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-exporter.d.ts +156 -0
- package/dist/backend/backend/src/services/team-move/team-move-exporter.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-exporter.js +462 -0
- package/dist/backend/backend/src/services/team-move/team-move-exporter.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-fs.d.ts +157 -0
- package/dist/backend/backend/src/services/team-move/team-move-fs.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-fs.js +298 -0
- package/dist/backend/backend/src/services/team-move/team-move-fs.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-git.d.ts +69 -0
- package/dist/backend/backend/src/services/team-move/team-move-git.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-git.js +198 -0
- package/dist/backend/backend/src/services/team-move/team-move-git.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-importer.d.ts +121 -0
- package/dist/backend/backend/src/services/team-move/team-move-importer.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-importer.js +462 -0
- package/dist/backend/backend/src/services/team-move/team-move-importer.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-journal.d.ts +110 -0
- package/dist/backend/backend/src/services/team-move/team-move-journal.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-journal.js +198 -0
- package/dist/backend/backend/src/services/team-move/team-move-journal.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-preflight.d.ts +71 -0
- package/dist/backend/backend/src/services/team-move/team-move-preflight.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-preflight.js +102 -0
- package/dist/backend/backend/src/services/team-move/team-move-preflight.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-runtime.d.ts +29 -0
- package/dist/backend/backend/src/services/team-move/team-move-runtime.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-runtime.js +173 -0
- package/dist/backend/backend/src/services/team-move/team-move-runtime.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-secret-scan.d.ts +50 -0
- package/dist/backend/backend/src/services/team-move/team-move-secret-scan.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-secret-scan.js +96 -0
- package/dist/backend/backend/src/services/team-move/team-move-secret-scan.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-select.d.ts +94 -0
- package/dist/backend/backend/src/services/team-move/team-move-select.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move-select.js +124 -0
- package/dist/backend/backend/src/services/team-move/team-move-select.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move.fixture.d.ts +46 -0
- package/dist/backend/backend/src/services/team-move/team-move.fixture.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move.fixture.js +202 -0
- package/dist/backend/backend/src/services/team-move/team-move.fixture.js.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move.types.d.ts +269 -0
- package/dist/backend/backend/src/services/team-move/team-move.types.d.ts.map +1 -0
- package/dist/backend/backend/src/services/team-move/team-move.types.js +44 -0
- package/dist/backend/backend/src/services/team-move/team-move.types.js.map +1 -0
- package/dist/backend/backend/src/services/trace/trace-context.service.d.ts +42 -2
- package/dist/backend/backend/src/services/trace/trace-context.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/trace/trace-context.service.js +66 -5
- package/dist/backend/backend/src/services/trace/trace-context.service.js.map +1 -1
- package/dist/backend/backend/src/services/trace/trace-recorder.d.ts.map +1 -1
- package/dist/backend/backend/src/services/trace/trace-recorder.js +11 -0
- package/dist/backend/backend/src/services/trace/trace-recorder.js.map +1 -1
- package/dist/backend/backend/src/services/usage-ledger/categories-param.d.ts +28 -0
- package/dist/backend/backend/src/services/usage-ledger/categories-param.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/categories-param.js +67 -0
- package/dist/backend/backend/src/services/usage-ledger/categories-param.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/excerpt.d.ts +37 -0
- package/dist/backend/backend/src/services/usage-ledger/excerpt.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/excerpt.js +49 -0
- package/dist/backend/backend/src/services/usage-ledger/excerpt.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/index.d.ts +14 -0
- package/dist/backend/backend/src/services/usage-ledger/index.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/index.js +14 -0
- package/dist/backend/backend/src/services/usage-ledger/index.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/ledger-hooks.d.ts +44 -0
- package/dist/backend/backend/src/services/usage-ledger/ledger-hooks.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/ledger-hooks.js +52 -0
- package/dist/backend/backend/src/services/usage-ledger/ledger-hooks.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/ledger-rollup.d.ts +100 -0
- package/dist/backend/backend/src/services/usage-ledger/ledger-rollup.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/ledger-rollup.js +189 -0
- package/dist/backend/backend/src/services/usage-ledger/ledger-rollup.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/owner-spans.d.ts +41 -0
- package/dist/backend/backend/src/services/usage-ledger/owner-spans.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/owner-spans.js +71 -0
- package/dist/backend/backend/src/services/usage-ledger/owner-spans.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/team-directory.d.ts +47 -0
- package/dist/backend/backend/src/services/usage-ledger/team-directory.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/team-directory.js +106 -0
- package/dist/backend/backend/src/services/usage-ledger/team-directory.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/unit-classifier.d.ts +101 -0
- package/dist/backend/backend/src/services/usage-ledger/unit-classifier.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/unit-classifier.js +263 -0
- package/dist/backend/backend/src/services/usage-ledger/unit-classifier.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-categories.store.d.ts +96 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-categories.store.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-categories.store.js +166 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-categories.store.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.backfill.d.ts +90 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.backfill.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.backfill.js +294 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.backfill.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.recorder.d.ts +102 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.recorder.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.recorder.js +314 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.recorder.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.runtime.d.ts +62 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.runtime.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.runtime.js +106 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.runtime.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.store.d.ts +52 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.store.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.store.js +95 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.store.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.types.d.ts +127 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.types.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.types.js +19 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-ledger.types.js.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-report.service.d.ts +124 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-report.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-report.service.js +372 -0
- package/dist/backend/backend/src/services/usage-ledger/usage-report.service.js.map +1 -0
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +80 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +80 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/agent/resource-mode.service.d.ts +26 -6
- package/dist/cli/backend/src/services/agent/resource-mode.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/resource-mode.service.js +59 -5
- package/dist/cli/backend/src/services/agent/resource-mode.service.js.map +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-sync.types.js +1 -0
- package/dist/cli/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
- package/dist/cli/backend/src/services/core/storage.service.d.ts +18 -0
- package/dist/cli/backend/src/services/core/storage.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/core/storage.service.js +16 -0
- package/dist/cli/backend/src/services/core/storage.service.js.map +1 -1
- package/dist/cli/backend/src/services/decisions/decision.service.d.ts +19 -0
- package/dist/cli/backend/src/services/decisions/decision.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/decisions/decision.service.js +46 -0
- package/dist/cli/backend/src/services/decisions/decision.service.js.map +1 -1
- package/dist/cli/backend/src/services/monitoring/agent-turn-state.d.ts +35 -0
- package/dist/cli/backend/src/services/monitoring/agent-turn-state.d.ts.map +1 -1
- package/dist/cli/backend/src/services/monitoring/agent-turn-state.js +101 -0
- package/dist/cli/backend/src/services/monitoring/agent-turn-state.js.map +1 -1
- package/dist/cli/backend/src/services/monitoring/token-usage.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/monitoring/token-usage.service.js +3 -0
- package/dist/cli/backend/src/services/monitoring/token-usage.service.js.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts +9 -0
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.js +25 -0
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.d.ts +24 -0
- package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.js +63 -1
- package/dist/cli/backend/src/services/slack/slack-typing-placeholder.service.js.map +1 -1
- package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts +19 -0
- package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/task-pool/task-pool.service.js +41 -0
- package/dist/cli/backend/src/services/task-pool/task-pool.service.js.map +1 -1
- package/dist/cli/backend/src/services/trace/trace-context.service.d.ts +42 -2
- package/dist/cli/backend/src/services/trace/trace-context.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/trace/trace-context.service.js +66 -5
- package/dist/cli/backend/src/services/trace/trace-context.service.js.map +1 -1
- package/dist/cli/backend/src/services/trace/trace-recorder.d.ts.map +1 -1
- package/dist/cli/backend/src/services/trace/trace-recorder.js +11 -0
- package/dist/cli/backend/src/services/trace/trace-recorder.js.map +1 -1
- package/dist/cli/backend/src/services/usage-ledger/ledger-hooks.d.ts +44 -0
- package/dist/cli/backend/src/services/usage-ledger/ledger-hooks.d.ts.map +1 -0
- package/dist/cli/backend/src/services/usage-ledger/ledger-hooks.js +52 -0
- package/dist/cli/backend/src/services/usage-ledger/ledger-hooks.js.map +1 -0
- package/dist/cli/cli/src/commands/team-move.d.ts +68 -0
- package/dist/cli/cli/src/commands/team-move.d.ts.map +1 -0
- package/dist/cli/cli/src/commands/team-move.js +155 -0
- package/dist/cli/cli/src/commands/team-move.js.map +1 -0
- package/dist/cli/cli/src/index.js +33 -0
- package/dist/cli/cli/src/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-ledger.store.js","sourceRoot":"","sources":["../../../../../../backend/src/services/usage-ledger/usage-ledger.store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usage ledger types: the immutable facts behind per-ticket time and token
|
|
3
|
+
* accounting (specs/2026-10-11-usage-ledger.md).
|
|
4
|
+
*
|
|
5
|
+
* A "unit" is one trace (a ticket, a request, a work item thread, an owner
|
|
6
|
+
* conversation). Facts only ever get appended; every statistic (category,
|
|
7
|
+
* team, owner minutes) is computed from them at report time.
|
|
8
|
+
*
|
|
9
|
+
* @module services/usage-ledger/usage-ledger.types
|
|
10
|
+
*/
|
|
11
|
+
/** Schema version written on every fact. */
|
|
12
|
+
export declare const LEDGER_VERSION = 1;
|
|
13
|
+
/**
|
|
14
|
+
* How a fact was tied to its unit.
|
|
15
|
+
* - `recorded`: the harness knew the unit when it happened.
|
|
16
|
+
* - `inferred`: rebuilt afterwards from timestamps (history backfill).
|
|
17
|
+
*/
|
|
18
|
+
export type Attribution = 'recorded' | 'inferred';
|
|
19
|
+
/** Id of the pseudo-unit that holds an agent's usage while it worked on no ticket. */
|
|
20
|
+
export declare const UNTICKETED_PREFIX = "~none:";
|
|
21
|
+
/** Fields every fact carries. */
|
|
22
|
+
interface FactBase {
|
|
23
|
+
v: typeof LEDGER_VERSION;
|
|
24
|
+
/** ISO time of the thing that happened (the covered hour's start, for usage / busy) */
|
|
25
|
+
ts: string;
|
|
26
|
+
}
|
|
27
|
+
/** What is known about a unit. Several of these merge field by field, first value wins. */
|
|
28
|
+
export interface UnitMetaFact extends FactBase {
|
|
29
|
+
kind: 'unit.meta';
|
|
30
|
+
unitId: string;
|
|
31
|
+
/** `TKT-12` or a project ticket id (`CE-7`) */
|
|
32
|
+
ticketId?: string;
|
|
33
|
+
requestId?: string;
|
|
34
|
+
/** What started the trace (`request`, `owner_message`, `ticket`, …) */
|
|
35
|
+
rootKind?: string;
|
|
36
|
+
title?: string;
|
|
37
|
+
/** The owner's words / the ticket description, cut */
|
|
38
|
+
excerpt?: string;
|
|
39
|
+
team?: string;
|
|
40
|
+
project?: string;
|
|
41
|
+
/** Where the request came from (`slack-dm`, `chat`, …) */
|
|
42
|
+
channel?: string;
|
|
43
|
+
/** Conversation thread the request lives in (Slack thread ref) */
|
|
44
|
+
threadRef?: string;
|
|
45
|
+
/** When the unit began */
|
|
46
|
+
openedAt?: string;
|
|
47
|
+
attribution: Attribution;
|
|
48
|
+
}
|
|
49
|
+
/** An agent worked on a unit (written once per agent and unit). */
|
|
50
|
+
export interface UnitAgentFact extends FactBase {
|
|
51
|
+
kind: 'unit.agent';
|
|
52
|
+
unitId: string;
|
|
53
|
+
agent: string;
|
|
54
|
+
team?: string;
|
|
55
|
+
project?: string;
|
|
56
|
+
attribution: Attribution;
|
|
57
|
+
}
|
|
58
|
+
/** The unit reached a terminal status. */
|
|
59
|
+
export interface UnitClosedFact extends FactBase {
|
|
60
|
+
kind: 'unit.closed';
|
|
61
|
+
unitId: string;
|
|
62
|
+
status: string;
|
|
63
|
+
attribution: Attribution;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Token usage of one agent on one unit within one hour (a delta: the sum of
|
|
67
|
+
* the facts is the total). `share` is the fraction of the underlying events
|
|
68
|
+
* this unit carries (1, or 1/n when the turn was about n tickets); token
|
|
69
|
+
* fields are the raw event sums, before the share.
|
|
70
|
+
*/
|
|
71
|
+
export interface UsageFact extends FactBase {
|
|
72
|
+
kind: 'usage';
|
|
73
|
+
unitId: string;
|
|
74
|
+
agent: string;
|
|
75
|
+
team?: string;
|
|
76
|
+
model: string;
|
|
77
|
+
runtime?: string;
|
|
78
|
+
events: number;
|
|
79
|
+
share: number;
|
|
80
|
+
/** Every input token, cached ones included (see eventTokens) */
|
|
81
|
+
input: number;
|
|
82
|
+
cachedInput: number;
|
|
83
|
+
output: number;
|
|
84
|
+
cacheWrite: number;
|
|
85
|
+
/** Cost-weighted tokens (see eventTokens) */
|
|
86
|
+
budget: number;
|
|
87
|
+
/** Estimated USD at the model's rate (cache-aware) */
|
|
88
|
+
costUsd: number;
|
|
89
|
+
attribution: Attribution;
|
|
90
|
+
}
|
|
91
|
+
/** Agent busy time on one unit within one hour (a delta, like {@link UsageFact}). */
|
|
92
|
+
export interface BusyFact extends FactBase {
|
|
93
|
+
kind: 'busy';
|
|
94
|
+
unitId: string;
|
|
95
|
+
agent: string;
|
|
96
|
+
team?: string;
|
|
97
|
+
turns: number;
|
|
98
|
+
busyMs: number;
|
|
99
|
+
share: number;
|
|
100
|
+
attribution: Attribution;
|
|
101
|
+
}
|
|
102
|
+
/** The owner wrote to a unit (a message delivered to an agent on it). */
|
|
103
|
+
export interface OwnerMessageFact extends FactBase {
|
|
104
|
+
kind: 'owner.message';
|
|
105
|
+
unitId: string;
|
|
106
|
+
attribution: Attribution;
|
|
107
|
+
}
|
|
108
|
+
/** Any ledger fact. */
|
|
109
|
+
export type LedgerFact = UnitMetaFact | UnitAgentFact | UnitClosedFact | UsageFact | BusyFact | OwnerMessageFact;
|
|
110
|
+
/** Where a fact is written. */
|
|
111
|
+
export type LedgerFile = 'live' | 'backfill';
|
|
112
|
+
/** One tokens + cost tally. */
|
|
113
|
+
export interface Tally {
|
|
114
|
+
input: number;
|
|
115
|
+
cachedInput: number;
|
|
116
|
+
output: number;
|
|
117
|
+
cacheWrite: number;
|
|
118
|
+
/** Raw total: input (cached included) + output */
|
|
119
|
+
total: number;
|
|
120
|
+
/** Cost-weighted total */
|
|
121
|
+
budget: number;
|
|
122
|
+
costUsd: number;
|
|
123
|
+
}
|
|
124
|
+
/** A zeroed {@link Tally}. */
|
|
125
|
+
export declare function emptyTally(): Tally;
|
|
126
|
+
export {};
|
|
127
|
+
//# sourceMappingURL=usage-ledger.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-ledger.types.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/usage-ledger/usage-ledger.types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usage ledger types: the immutable facts behind per-ticket time and token
|
|
3
|
+
* accounting (specs/2026-10-11-usage-ledger.md).
|
|
4
|
+
*
|
|
5
|
+
* A "unit" is one trace (a ticket, a request, a work item thread, an owner
|
|
6
|
+
* conversation). Facts only ever get appended; every statistic (category,
|
|
7
|
+
* team, owner minutes) is computed from them at report time.
|
|
8
|
+
*
|
|
9
|
+
* @module services/usage-ledger/usage-ledger.types
|
|
10
|
+
*/
|
|
11
|
+
/** Schema version written on every fact. */
|
|
12
|
+
export const LEDGER_VERSION = 1;
|
|
13
|
+
/** Id of the pseudo-unit that holds an agent's usage while it worked on no ticket. */
|
|
14
|
+
export const UNTICKETED_PREFIX = '~none:';
|
|
15
|
+
/** A zeroed {@link Tally}. */
|
|
16
|
+
export function emptyTally() {
|
|
17
|
+
return { input: 0, cachedInput: 0, output: 0, cacheWrite: 0, total: 0, budget: 0, costUsd: 0 };
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=usage-ledger.types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-ledger.types.js","sourceRoot":"","sources":["../../../../../../backend/src/services/usage-ledger/usage-ledger.types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,4CAA4C;AAC5C,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC;AAShC,sFAAsF;AACtF,MAAM,CAAC,MAAM,iBAAiB,GAAG,QAAQ,CAAC;AAmH1C,8BAA8B;AAC9B,MAAM,UAAU,UAAU;IACzB,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;AAChG,CAAC"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usage report: where the owner's time and the agents' tokens went, per
|
|
3
|
+
* category / team / project / agent / ticket.
|
|
4
|
+
*
|
|
5
|
+
* Everything is computed from the ledger facts at call time. Categories are
|
|
6
|
+
* applied here (see {@link UnitClassifier}) and never stored in the facts.
|
|
7
|
+
* Owner minutes follow the span heuristic of owner-spans.ts. Figures are
|
|
8
|
+
* summed per UTC hour, so a window edge is accurate to the hour.
|
|
9
|
+
*
|
|
10
|
+
* @module services/usage-ledger/usage-report.service
|
|
11
|
+
*/
|
|
12
|
+
import type { LedgerRollup } from './ledger-rollup.js';
|
|
13
|
+
import type { TeamLookup } from './team-directory.js';
|
|
14
|
+
import { type CategoryDef } from './usage-categories.store.js';
|
|
15
|
+
import { UnitClassifier } from './unit-classifier.js';
|
|
16
|
+
import { type Tally } from './usage-ledger.types.js';
|
|
17
|
+
/** Ways to group a report. */
|
|
18
|
+
export declare const REPORT_DIMENSIONS: readonly ["category", "team", "project", "agent", "ticket"];
|
|
19
|
+
/** A grouping. */
|
|
20
|
+
export type ReportDimension = (typeof REPORT_DIMENSIONS)[number];
|
|
21
|
+
/** Request for a report. */
|
|
22
|
+
export interface ReportQuery {
|
|
23
|
+
/** Window start (ms) */
|
|
24
|
+
sinceMs: number;
|
|
25
|
+
/** Window end (ms) */
|
|
26
|
+
untilMs: number;
|
|
27
|
+
by: ReportDimension;
|
|
28
|
+
/** Categories to use instead of the stored ones (ad hoc) */
|
|
29
|
+
categories?: CategoryDef[];
|
|
30
|
+
/** Keep only these groups (by name), e.g. two categories to compare */
|
|
31
|
+
only?: string[];
|
|
32
|
+
/** Groups returned (default 50) */
|
|
33
|
+
limit?: number;
|
|
34
|
+
}
|
|
35
|
+
/** One ticket in a group. */
|
|
36
|
+
export interface TicketLine {
|
|
37
|
+
unitId: string;
|
|
38
|
+
ticketId?: string;
|
|
39
|
+
title: string;
|
|
40
|
+
team?: string;
|
|
41
|
+
ownerMinutes: number;
|
|
42
|
+
agentBusyMinutes: number;
|
|
43
|
+
tokens: number;
|
|
44
|
+
costUsd: number;
|
|
45
|
+
inferred: boolean;
|
|
46
|
+
}
|
|
47
|
+
/** One group of a report. */
|
|
48
|
+
export interface ReportGroup {
|
|
49
|
+
key: string;
|
|
50
|
+
ownerMinutes: number;
|
|
51
|
+
agentBusyMinutes: number;
|
|
52
|
+
/** Token figures: `budget` is the cost-weighted headline, `total` the raw sum */
|
|
53
|
+
tokens: Tally;
|
|
54
|
+
costUsd: number;
|
|
55
|
+
ticketCount: number;
|
|
56
|
+
topTickets: TicketLine[];
|
|
57
|
+
}
|
|
58
|
+
/** A report. */
|
|
59
|
+
export interface UsageReport {
|
|
60
|
+
window: {
|
|
61
|
+
since: string;
|
|
62
|
+
until: string;
|
|
63
|
+
};
|
|
64
|
+
by: ReportDimension;
|
|
65
|
+
categories: {
|
|
66
|
+
setId: string;
|
|
67
|
+
source: 'stored' | 'ad-hoc' | 'default';
|
|
68
|
+
names: string[];
|
|
69
|
+
llmClassified: number;
|
|
70
|
+
unclassified: number;
|
|
71
|
+
} | null;
|
|
72
|
+
totals: Omit<ReportGroup, 'key' | 'topTickets'>;
|
|
73
|
+
groups: ReportGroup[];
|
|
74
|
+
/** Voice / Slack friendly text */
|
|
75
|
+
summary: string;
|
|
76
|
+
notes: string[];
|
|
77
|
+
}
|
|
78
|
+
/** Where the report gets its inputs. */
|
|
79
|
+
export interface ReportDeps {
|
|
80
|
+
rollup: LedgerRollup;
|
|
81
|
+
directory: TeamLookup;
|
|
82
|
+
classifier: UnitClassifier;
|
|
83
|
+
/** Stored category definitions */
|
|
84
|
+
categories: () => Promise<CategoryDef[]>;
|
|
85
|
+
/** Called before reading so buffered facts are on disk and in the rollup */
|
|
86
|
+
sync: () => Promise<void>;
|
|
87
|
+
now?: () => number;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Parse a window start.
|
|
91
|
+
*
|
|
92
|
+
* @param value - `7d`, `24h`, `2w`, `90m`, or an ISO date / time
|
|
93
|
+
* @param nowMs - Now (ms)
|
|
94
|
+
* @returns Epoch ms, or null when unreadable
|
|
95
|
+
*/
|
|
96
|
+
export declare function parseSince(value: string, nowMs: number): number | null;
|
|
97
|
+
/**
|
|
98
|
+
* Format minutes for speech: `45m`, `2h10m`, `1d3h`.
|
|
99
|
+
*
|
|
100
|
+
* @param minutes - Minutes
|
|
101
|
+
* @returns Short text
|
|
102
|
+
*/
|
|
103
|
+
export declare function formatMinutes(minutes: number): string;
|
|
104
|
+
/**
|
|
105
|
+
* Builds reports from the ledger.
|
|
106
|
+
*/
|
|
107
|
+
export declare class UsageReportService {
|
|
108
|
+
private readonly deps;
|
|
109
|
+
constructor(deps: ReportDeps);
|
|
110
|
+
/**
|
|
111
|
+
* Compute a report.
|
|
112
|
+
*
|
|
113
|
+
* @param query - Window, grouping, optional ad-hoc categories
|
|
114
|
+
* @returns The report
|
|
115
|
+
*/
|
|
116
|
+
report(query: ReportQuery): Promise<UsageReport>;
|
|
117
|
+
private teamOf;
|
|
118
|
+
/** The team a ticket is credited to: the recorded one, else the team with the most spend. */
|
|
119
|
+
private primaryTeam;
|
|
120
|
+
private projectOf;
|
|
121
|
+
private labelOf;
|
|
122
|
+
private summarize;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=usage-report.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usage-report.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/usage-ledger/usage-report.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usage report: where the owner's time and the agents' tokens went, per
|
|
3
|
+
* category / team / project / agent / ticket.
|
|
4
|
+
*
|
|
5
|
+
* Everything is computed from the ledger facts at call time. Categories are
|
|
6
|
+
* applied here (see {@link UnitClassifier}) and never stored in the facts.
|
|
7
|
+
* Owner minutes follow the span heuristic of owner-spans.ts. Figures are
|
|
8
|
+
* summed per UTC hour, so a window edge is accurate to the hour.
|
|
9
|
+
*
|
|
10
|
+
* @module services/usage-ledger/usage-report.service
|
|
11
|
+
*/
|
|
12
|
+
import { compactTokens } from '../usage/token-format.js';
|
|
13
|
+
import { buildOwnerSpans, ownerMinutesByUnit } from './owner-spans.js';
|
|
14
|
+
import { categorySetId } from './usage-categories.store.js';
|
|
15
|
+
import { NO_TICKET_CATEGORY, OTHER_CATEGORY } from './unit-classifier.js';
|
|
16
|
+
import { emptyTally } from './usage-ledger.types.js';
|
|
17
|
+
/** Ways to group a report. */
|
|
18
|
+
export const REPORT_DIMENSIONS = ['category', 'team', 'project', 'agent', 'ticket'];
|
|
19
|
+
const HOUR_MS = 3_600_000;
|
|
20
|
+
/**
|
|
21
|
+
* Parse a window start.
|
|
22
|
+
*
|
|
23
|
+
* @param value - `7d`, `24h`, `2w`, `90m`, or an ISO date / time
|
|
24
|
+
* @param nowMs - Now (ms)
|
|
25
|
+
* @returns Epoch ms, or null when unreadable
|
|
26
|
+
*/
|
|
27
|
+
export function parseSince(value, nowMs) {
|
|
28
|
+
const rel = /^(\d+(?:\.\d+)?)\s*([mhdw])$/i.exec(value.trim());
|
|
29
|
+
if (rel) {
|
|
30
|
+
const unit = { m: 60_000, h: HOUR_MS, d: 24 * HOUR_MS, w: 7 * 24 * HOUR_MS }[rel[2].toLowerCase()];
|
|
31
|
+
return nowMs - Number(rel[1]) * unit;
|
|
32
|
+
}
|
|
33
|
+
const abs = Date.parse(value);
|
|
34
|
+
return Number.isFinite(abs) ? abs : null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Format minutes for speech: `45m`, `2h10m`, `1d3h`.
|
|
38
|
+
*
|
|
39
|
+
* @param minutes - Minutes
|
|
40
|
+
* @returns Short text
|
|
41
|
+
*/
|
|
42
|
+
export function formatMinutes(minutes) {
|
|
43
|
+
const m = Math.round(minutes);
|
|
44
|
+
if (m < 60)
|
|
45
|
+
return `${m}m`;
|
|
46
|
+
const h = Math.floor(m / 60);
|
|
47
|
+
if (h < 24)
|
|
48
|
+
return m % 60 ? `${h}h${m % 60}m` : `${h}h`;
|
|
49
|
+
return h % 24 ? `${Math.floor(h / 24)}d${h % 24}h` : `${Math.floor(h / 24)}d`;
|
|
50
|
+
}
|
|
51
|
+
/** Add `weight` times a usage entry to a tally. */
|
|
52
|
+
function addUsage(t, e) {
|
|
53
|
+
const w = e.share;
|
|
54
|
+
t.input += e.input * w;
|
|
55
|
+
t.cachedInput += e.cachedInput * w;
|
|
56
|
+
t.output += e.output * w;
|
|
57
|
+
t.cacheWrite += e.cacheWrite * w;
|
|
58
|
+
t.total += (e.input + e.output) * w;
|
|
59
|
+
t.budget += e.budget * w;
|
|
60
|
+
t.costUsd += e.costUsd * w;
|
|
61
|
+
}
|
|
62
|
+
function addTally(into, from) {
|
|
63
|
+
into.input += from.input;
|
|
64
|
+
into.cachedInput += from.cachedInput;
|
|
65
|
+
into.output += from.output;
|
|
66
|
+
into.cacheWrite += from.cacheWrite;
|
|
67
|
+
into.total += from.total;
|
|
68
|
+
into.budget += from.budget;
|
|
69
|
+
into.costUsd += from.costUsd;
|
|
70
|
+
}
|
|
71
|
+
function roundTally(t) {
|
|
72
|
+
return {
|
|
73
|
+
input: Math.round(t.input),
|
|
74
|
+
cachedInput: Math.round(t.cachedInput),
|
|
75
|
+
output: Math.round(t.output),
|
|
76
|
+
cacheWrite: Math.round(t.cacheWrite),
|
|
77
|
+
total: Math.round(t.total),
|
|
78
|
+
budget: Math.round(t.budget),
|
|
79
|
+
costUsd: Math.round(t.costUsd * 10_000) / 10_000,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const round1 = (n) => Math.round(n * 10) / 10;
|
|
83
|
+
/**
|
|
84
|
+
* Builds reports from the ledger.
|
|
85
|
+
*/
|
|
86
|
+
export class UsageReportService {
|
|
87
|
+
deps;
|
|
88
|
+
constructor(deps) {
|
|
89
|
+
this.deps = deps;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Compute a report.
|
|
93
|
+
*
|
|
94
|
+
* @param query - Window, grouping, optional ad-hoc categories
|
|
95
|
+
* @returns The report
|
|
96
|
+
*/
|
|
97
|
+
async report(query) {
|
|
98
|
+
await this.deps.sync();
|
|
99
|
+
await this.deps.directory.ready();
|
|
100
|
+
const { rollup } = this.deps;
|
|
101
|
+
const from = query.sinceMs;
|
|
102
|
+
const to = query.untilMs;
|
|
103
|
+
// Owner minutes across every ticket (overlaps are shared, so all spans are needed)
|
|
104
|
+
const spans = [];
|
|
105
|
+
for (const u of rollup.units.values()) {
|
|
106
|
+
if (u.ownerMessages.length > 0)
|
|
107
|
+
spans.push(...buildOwnerSpans(u.unitId, u.ownerMessages));
|
|
108
|
+
}
|
|
109
|
+
const ownerMinutes = ownerMinutesByUnit(spans, from, to);
|
|
110
|
+
const windows = [];
|
|
111
|
+
for (const unit of rollup.units.values()) {
|
|
112
|
+
const w = { unit, tally: emptyTally(), busyMs: 0, owner: ownerMinutes.get(unit.unitId) ?? 0, byAgent: new Map(), byTeam: new Map() };
|
|
113
|
+
const slice = (m, key) => {
|
|
114
|
+
let s = m.get(key);
|
|
115
|
+
if (!s) {
|
|
116
|
+
s = { tally: emptyTally(), busyMs: 0, ownerMinutes: 0 };
|
|
117
|
+
m.set(key, s);
|
|
118
|
+
}
|
|
119
|
+
return s;
|
|
120
|
+
};
|
|
121
|
+
for (const e of unit.usage) {
|
|
122
|
+
if (e.hourMs + HOUR_MS <= from || e.hourMs >= to)
|
|
123
|
+
continue;
|
|
124
|
+
addUsage(w.tally, e);
|
|
125
|
+
const one = emptyTally();
|
|
126
|
+
addUsage(one, e);
|
|
127
|
+
addTally(slice(w.byAgent, e.agent).tally, one);
|
|
128
|
+
addTally(slice(w.byTeam, this.teamOf(e.team, e.agent) ?? '(unknown team)').tally, one);
|
|
129
|
+
}
|
|
130
|
+
for (const e of unit.busy) {
|
|
131
|
+
if (e.hourMs + HOUR_MS <= from || e.hourMs >= to)
|
|
132
|
+
continue;
|
|
133
|
+
const ms = e.busyMs * e.share;
|
|
134
|
+
w.busyMs += ms;
|
|
135
|
+
slice(w.byAgent, e.agent).busyMs += ms;
|
|
136
|
+
slice(w.byTeam, this.teamOf(e.team, e.agent) ?? '(unknown team)').busyMs += ms;
|
|
137
|
+
}
|
|
138
|
+
if (w.tally.total === 0 && w.busyMs === 0 && w.owner === 0)
|
|
139
|
+
continue;
|
|
140
|
+
w.team = this.primaryTeam(unit, w.byTeam);
|
|
141
|
+
windows.push(w);
|
|
142
|
+
}
|
|
143
|
+
// Categories
|
|
144
|
+
let categoryInfo = null;
|
|
145
|
+
let categoryOf = new Map();
|
|
146
|
+
if (query.by === 'category') {
|
|
147
|
+
const stored = await this.deps.categories();
|
|
148
|
+
const defs = query.categories ?? stored;
|
|
149
|
+
const source = query.categories ? 'ad-hoc' : stored.length > 0 ? 'stored' : 'default';
|
|
150
|
+
const units = windows
|
|
151
|
+
.filter((w) => !w.unit.synthetic)
|
|
152
|
+
.map((w) => ({
|
|
153
|
+
unitId: w.unit.unitId,
|
|
154
|
+
ticketId: w.unit.ticketId,
|
|
155
|
+
title: w.unit.title,
|
|
156
|
+
excerpt: w.unit.excerpt,
|
|
157
|
+
team: w.team,
|
|
158
|
+
project: this.projectOf(w.unit),
|
|
159
|
+
}));
|
|
160
|
+
const result = await this.deps.classifier.classify(units, defs);
|
|
161
|
+
categoryOf = result.categories;
|
|
162
|
+
categoryInfo = { setId: categorySetId(defs), source, names: defs.map((d) => d.name), llmClassified: result.llmClassified, unclassified: result.unclassified };
|
|
163
|
+
}
|
|
164
|
+
const groups = new Map();
|
|
165
|
+
const add = (key, unitId, tally, busyMs, owner) => {
|
|
166
|
+
let g = groups.get(key);
|
|
167
|
+
if (!g) {
|
|
168
|
+
g = { key, slices: new Map() };
|
|
169
|
+
groups.set(key, g);
|
|
170
|
+
}
|
|
171
|
+
let s = g.slices.get(unitId);
|
|
172
|
+
if (!s) {
|
|
173
|
+
s = { tally: emptyTally(), busyMs: 0, ownerMinutes: 0 };
|
|
174
|
+
g.slices.set(unitId, s);
|
|
175
|
+
}
|
|
176
|
+
addTally(s.tally, tally);
|
|
177
|
+
s.busyMs += busyMs;
|
|
178
|
+
s.ownerMinutes += owner;
|
|
179
|
+
};
|
|
180
|
+
for (const w of windows) {
|
|
181
|
+
const u = w.unit;
|
|
182
|
+
switch (query.by) {
|
|
183
|
+
case 'ticket':
|
|
184
|
+
add(this.labelOf(u), u.unitId, w.tally, w.busyMs, w.owner);
|
|
185
|
+
break;
|
|
186
|
+
case 'category':
|
|
187
|
+
add(u.synthetic ? NO_TICKET_CATEGORY : (categoryOf.get(u.unitId) ?? OTHER_CATEGORY), u.unitId, w.tally, w.busyMs, w.owner);
|
|
188
|
+
break;
|
|
189
|
+
case 'project':
|
|
190
|
+
add(this.projectOf(u) ?? '(no project)', u.unitId, w.tally, w.busyMs, w.owner);
|
|
191
|
+
break;
|
|
192
|
+
case 'team': {
|
|
193
|
+
const primary = w.team ?? '(unknown team)';
|
|
194
|
+
if (w.byTeam.size === 0)
|
|
195
|
+
add(primary, u.unitId, w.tally, w.busyMs, w.owner);
|
|
196
|
+
for (const [team, s] of w.byTeam)
|
|
197
|
+
add(team, u.unitId, s.tally, s.busyMs, team === primary ? w.owner : 0);
|
|
198
|
+
if (w.byTeam.size > 0 && !w.byTeam.has(primary) && w.owner > 0)
|
|
199
|
+
add(primary, u.unitId, emptyTally(), 0, w.owner);
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
case 'agent':
|
|
203
|
+
for (const [agent, s] of w.byAgent)
|
|
204
|
+
add(agent, u.unitId, s.tally, s.busyMs, 0);
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const teamOfUnit = new Map(windows.map((w) => [w.unit.unitId, w.team]));
|
|
209
|
+
const lineOf = (unitId, s) => {
|
|
210
|
+
const u = rollup.units.get(unitId);
|
|
211
|
+
return {
|
|
212
|
+
unitId,
|
|
213
|
+
...(u.ticketId ? { ticketId: u.ticketId } : {}),
|
|
214
|
+
title: u.title || u.ticketId || unitId,
|
|
215
|
+
...(teamOfUnit.get(unitId) ? { team: teamOfUnit.get(unitId) } : {}),
|
|
216
|
+
ownerMinutes: round1(s.ownerMinutes),
|
|
217
|
+
agentBusyMinutes: round1(s.busyMs / 60_000),
|
|
218
|
+
tokens: Math.round(s.tally.budget),
|
|
219
|
+
costUsd: Math.round(s.tally.costUsd * 10_000) / 10_000,
|
|
220
|
+
inferred: u.inferred,
|
|
221
|
+
};
|
|
222
|
+
};
|
|
223
|
+
let out = [...groups.values()].map((g) => {
|
|
224
|
+
const tally = emptyTally();
|
|
225
|
+
let busyMs = 0;
|
|
226
|
+
let owner = 0;
|
|
227
|
+
let tickets = 0;
|
|
228
|
+
for (const [unitId, s] of g.slices) {
|
|
229
|
+
addTally(tally, s.tally);
|
|
230
|
+
busyMs += s.busyMs;
|
|
231
|
+
owner += s.ownerMinutes;
|
|
232
|
+
if (!(rollup.units.get(unitId)?.synthetic ?? false))
|
|
233
|
+
tickets += 1;
|
|
234
|
+
}
|
|
235
|
+
const top = [...g.slices.entries()]
|
|
236
|
+
.filter(([id]) => !(rollup.units.get(id)?.synthetic ?? false))
|
|
237
|
+
.sort((a, b) => b[1].tally.costUsd - a[1].tally.costUsd || b[1].busyMs - a[1].busyMs)
|
|
238
|
+
.slice(0, 5)
|
|
239
|
+
.map(([id, s]) => lineOf(id, s));
|
|
240
|
+
return {
|
|
241
|
+
key: g.key,
|
|
242
|
+
ownerMinutes: round1(owner),
|
|
243
|
+
agentBusyMinutes: round1(busyMs / 60_000),
|
|
244
|
+
tokens: roundTally(tally),
|
|
245
|
+
costUsd: Math.round(tally.costUsd * 10_000) / 10_000,
|
|
246
|
+
ticketCount: tickets,
|
|
247
|
+
topTickets: top,
|
|
248
|
+
};
|
|
249
|
+
});
|
|
250
|
+
if (query.only && query.only.length > 0) {
|
|
251
|
+
const wanted = new Set(query.only.map((n) => n.toLowerCase()));
|
|
252
|
+
out = out.filter((g) => wanted.has(g.key.toLowerCase()));
|
|
253
|
+
}
|
|
254
|
+
out.sort((a, b) => b.costUsd - a.costUsd || b.agentBusyMinutes - a.agentBusyMinutes || b.ownerMinutes - a.ownerMinutes);
|
|
255
|
+
const totalTally = emptyTally();
|
|
256
|
+
let totalBusy = 0;
|
|
257
|
+
let totalOwner = 0;
|
|
258
|
+
const tickets = new Set();
|
|
259
|
+
if (query.only && query.only.length > 0) {
|
|
260
|
+
// A filtered report totals only what it shows.
|
|
261
|
+
for (const g of out) {
|
|
262
|
+
for (const [unitId, s] of groups.get(g.key)?.slices ?? []) {
|
|
263
|
+
addTally(totalTally, s.tally);
|
|
264
|
+
totalBusy += s.busyMs;
|
|
265
|
+
totalOwner += s.ownerMinutes;
|
|
266
|
+
if (!(rollup.units.get(unitId)?.synthetic ?? false))
|
|
267
|
+
tickets.add(unitId);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
for (const w of windows) {
|
|
273
|
+
addTally(totalTally, w.tally);
|
|
274
|
+
totalBusy += w.busyMs;
|
|
275
|
+
totalOwner += w.owner;
|
|
276
|
+
if (!w.unit.synthetic)
|
|
277
|
+
tickets.add(w.unit.unitId);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
const totals = {
|
|
281
|
+
ownerMinutes: round1(totalOwner),
|
|
282
|
+
agentBusyMinutes: round1(totalBusy / 60_000),
|
|
283
|
+
tokens: roundTally(totalTally),
|
|
284
|
+
costUsd: Math.round(totalTally.costUsd * 10_000) / 10_000,
|
|
285
|
+
ticketCount: tickets.size,
|
|
286
|
+
};
|
|
287
|
+
const limit = Math.max(1, query.limit ?? 50);
|
|
288
|
+
const shown = out.slice(0, limit);
|
|
289
|
+
const inferredUnits = windows.filter((w) => w.unit.inferred && !w.unit.synthetic).length;
|
|
290
|
+
const notes = [
|
|
291
|
+
'Tokens are cost-weighted (cache reads count 0.1x, cache writes 1.25x for Claude); tokens.total is the raw sum.',
|
|
292
|
+
'Owner minutes: messages on one ticket within 10 minutes form a span; overlapping spans share the time.',
|
|
293
|
+
...(inferredUnits > 0 ? [`${inferredUnits} ticket(s) are partly rebuilt from history (attribution "inferred").`] : []),
|
|
294
|
+
...(categoryInfo && categoryInfo.unclassified > 0 ? [`${categoryInfo.unclassified} ticket(s) matched no category and no model was available; shown as "${OTHER_CATEGORY}".`] : []),
|
|
295
|
+
];
|
|
296
|
+
const report = {
|
|
297
|
+
window: { since: new Date(from).toISOString(), until: new Date(to).toISOString() },
|
|
298
|
+
by: query.by,
|
|
299
|
+
categories: categoryInfo,
|
|
300
|
+
totals,
|
|
301
|
+
groups: shown,
|
|
302
|
+
summary: '',
|
|
303
|
+
notes,
|
|
304
|
+
};
|
|
305
|
+
report.summary = this.summarize(report, out.length, to - from);
|
|
306
|
+
return report;
|
|
307
|
+
}
|
|
308
|
+
teamOf(recorded, agent) {
|
|
309
|
+
return recorded ?? this.deps.directory.lookup(agent)?.team;
|
|
310
|
+
}
|
|
311
|
+
/** The team a ticket is credited to: the recorded one, else the team with the most spend. */
|
|
312
|
+
primaryTeam(u, byTeam) {
|
|
313
|
+
if (u.team)
|
|
314
|
+
return u.team;
|
|
315
|
+
let best;
|
|
316
|
+
let score = -1;
|
|
317
|
+
for (const [team, s] of byTeam) {
|
|
318
|
+
const sc = s.tally.costUsd * 1000 + s.busyMs / 1000;
|
|
319
|
+
if (team !== '(unknown team)' && sc > score) {
|
|
320
|
+
score = sc;
|
|
321
|
+
best = team;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (best)
|
|
325
|
+
return best;
|
|
326
|
+
for (const [agent, info] of u.agents) {
|
|
327
|
+
const team = this.teamOf(info.team, agent);
|
|
328
|
+
if (team)
|
|
329
|
+
return team;
|
|
330
|
+
}
|
|
331
|
+
for (const e of u.usage)
|
|
332
|
+
if (e.team)
|
|
333
|
+
return e.team;
|
|
334
|
+
return undefined;
|
|
335
|
+
}
|
|
336
|
+
projectOf(u) {
|
|
337
|
+
if (u.project)
|
|
338
|
+
return u.project;
|
|
339
|
+
for (const info of u.agents.values())
|
|
340
|
+
if (info.project)
|
|
341
|
+
return info.project;
|
|
342
|
+
return undefined;
|
|
343
|
+
}
|
|
344
|
+
labelOf(u) {
|
|
345
|
+
const id = u.ticketId ?? u.unitId;
|
|
346
|
+
return u.title ? `${id}: ${u.title}` : id;
|
|
347
|
+
}
|
|
348
|
+
summarize(report, groupCount, spanMs) {
|
|
349
|
+
const days = spanMs / (24 * HOUR_MS);
|
|
350
|
+
const label = days >= 1 ? `last ${Math.round(days * 10) / 10 === Math.round(days) ? Math.round(days) : Math.round(days * 10) / 10} days` : `last ${Math.max(1, Math.round(spanMs / HOUR_MS))} hours`;
|
|
351
|
+
const t = report.totals;
|
|
352
|
+
const lines = [
|
|
353
|
+
`Usage, ${label}: ${t.ticketCount} tickets, ${formatMinutes(t.agentBusyMinutes)} of agent work, ${formatMinutes(t.ownerMinutes)} of your time, ${compactTokens(t.tokens.budget)} tokens (about $${t.costUsd.toFixed(2)}).`,
|
|
354
|
+
];
|
|
355
|
+
if (report.groups.length > 0) {
|
|
356
|
+
lines.push(`By ${report.by}:`);
|
|
357
|
+
for (const g of report.groups.slice(0, 6)) {
|
|
358
|
+
const parts = [
|
|
359
|
+
g.ownerMinutes > 0 ? `${formatMinutes(g.ownerMinutes)} of you` : '',
|
|
360
|
+
g.agentBusyMinutes > 0 ? `${formatMinutes(g.agentBusyMinutes)} agent work` : '',
|
|
361
|
+
`${compactTokens(g.tokens.budget)} tokens ($${g.costUsd.toFixed(2)})`,
|
|
362
|
+
report.by === 'ticket' || report.by === 'agent' || g.ticketCount === 0 ? '' : `${g.ticketCount} tickets`,
|
|
363
|
+
].filter(Boolean);
|
|
364
|
+
lines.push(`- ${g.key}: ${parts.join(', ')}`);
|
|
365
|
+
}
|
|
366
|
+
if (groupCount > 6)
|
|
367
|
+
lines.push(`- and ${groupCount - 6} more`);
|
|
368
|
+
}
|
|
369
|
+
return lines.join('\n');
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
//# sourceMappingURL=usage-report.service.js.map
|