@desplega.ai/agent-swarm 1.20.0 → 1.51.2

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 (561) hide show
  1. package/README.md +271 -169
  2. package/openapi.json +5015 -0
  3. package/package.json +40 -7
  4. package/plugin/commands/close-issue.md +7 -3
  5. package/plugin/commands/create-pr.md +18 -12
  6. package/plugin/commands/implement-issue.md +7 -3
  7. package/plugin/commands/respond-github.md +8 -4
  8. package/plugin/commands/review-pr.md +44 -10
  9. package/plugin/commands/start-leader.md +1 -3
  10. package/plugin/commands/start-worker.md +1 -3
  11. package/plugin/commands/work-on-task.md +22 -3
  12. package/plugin/pi-skills/close-issue/SKILL.md +90 -0
  13. package/plugin/pi-skills/create-pr/SKILL.md +99 -0
  14. package/plugin/pi-skills/implement-issue/SKILL.md +135 -0
  15. package/plugin/pi-skills/investigate-sentry-issue/SKILL.md +138 -0
  16. package/plugin/pi-skills/respond-github/SKILL.md +98 -0
  17. package/plugin/pi-skills/review-offered-task/SKILL.md +45 -0
  18. package/plugin/pi-skills/review-pr/SKILL.md +261 -0
  19. package/plugin/pi-skills/start-leader/SKILL.md +121 -0
  20. package/plugin/pi-skills/start-worker/SKILL.md +60 -0
  21. package/plugin/pi-skills/swarm-chat/SKILL.md +82 -0
  22. package/plugin/pi-skills/todos/SKILL.md +66 -0
  23. package/plugin/pi-skills/work-on-task/SKILL.md +65 -0
  24. package/plugin/skills/artifacts/examples/approval-flow.ts +34 -0
  25. package/plugin/skills/artifacts/examples/hono-dashboard.ts +31 -0
  26. package/plugin/skills/artifacts/examples/multi-artifact.ts +20 -0
  27. package/plugin/skills/artifacts/examples/static-report.sh +17 -0
  28. package/plugin/skills/artifacts/skill.md +71 -0
  29. package/src/agentmail/app.ts +65 -0
  30. package/src/agentmail/handlers.ts +262 -0
  31. package/src/agentmail/index.ts +9 -0
  32. package/src/agentmail/templates.ts +111 -0
  33. package/src/agentmail/types.ts +51 -0
  34. package/src/artifact-sdk/browser-sdk.ts +30 -0
  35. package/src/artifact-sdk/index.ts +2 -0
  36. package/src/artifact-sdk/localtunnel.d.ts +20 -0
  37. package/src/artifact-sdk/port.ts +12 -0
  38. package/src/artifact-sdk/server.ts +156 -0
  39. package/src/artifact-sdk/tunnel.ts +19 -0
  40. package/src/be/chunking.ts +193 -0
  41. package/src/be/db-queries/oauth.ts +90 -0
  42. package/src/be/db-queries/tracker.ts +182 -0
  43. package/src/be/db.ts +3327 -784
  44. package/src/be/embedding.ts +80 -0
  45. package/src/be/migrations/001_initial.sql +409 -0
  46. package/src/be/migrations/002_one_time_schedules.sql +59 -0
  47. package/src/be/migrations/003_workflows.sql +51 -0
  48. package/src/be/migrations/004_workflow_source.sql +81 -0
  49. package/src/be/migrations/005_epic_next_steps.sql +2 -0
  50. package/src/be/migrations/006_vcs_provider.sql +94 -0
  51. package/src/be/migrations/007_task_dir.sql +2 -0
  52. package/src/be/migrations/008_workflow_redesign.sql +85 -0
  53. package/src/be/migrations/009_tracker_integration.sql +144 -0
  54. package/src/be/migrations/010_step_diagnostics.sql +1 -0
  55. package/src/be/migrations/011_step_next_port.sql +1 -0
  56. package/src/be/migrations/012_trigger_schema.sql +1 -0
  57. package/src/be/migrations/013_task_output_schema.sql +2 -0
  58. package/src/be/migrations/014_prompt_templates.sql +33 -0
  59. package/src/be/migrations/015_workflow_workspace.sql +3 -0
  60. package/src/be/migrations/016_active_session_runner_session.sql +4 -0
  61. package/src/be/migrations/017_channel_activity_cursors.sql +6 -0
  62. package/src/be/migrations/018_fix_seed_double_version.sql +30 -0
  63. package/src/be/migrations/runner.ts +188 -0
  64. package/src/be/seed.ts +62 -0
  65. package/src/cli.tsx +231 -299
  66. package/src/commands/artifact.ts +241 -0
  67. package/src/commands/onboard/compose-generator.ts +169 -0
  68. package/src/commands/onboard/env-generator.ts +79 -0
  69. package/src/commands/onboard/manifest.ts +37 -0
  70. package/src/commands/onboard/presets.ts +85 -0
  71. package/src/commands/onboard/service-names.ts +47 -0
  72. package/src/commands/onboard/steps/core-credentials.tsx +111 -0
  73. package/src/commands/onboard/steps/custom-templates.tsx +168 -0
  74. package/src/commands/onboard/steps/generate.tsx +154 -0
  75. package/src/commands/onboard/steps/harness-credentials.tsx +195 -0
  76. package/src/commands/onboard/steps/harness.tsx +21 -0
  77. package/src/commands/onboard/steps/health-check.tsx +171 -0
  78. package/src/commands/onboard/steps/integration-github.tsx +105 -0
  79. package/src/commands/onboard/steps/integration-gitlab.tsx +79 -0
  80. package/src/commands/onboard/steps/integration-menu.tsx +58 -0
  81. package/src/commands/onboard/steps/integration-sentry.tsx +79 -0
  82. package/src/commands/onboard/steps/integration-slack.tsx +165 -0
  83. package/src/commands/onboard/steps/post-connect.tsx +145 -0
  84. package/src/commands/onboard/steps/post-dashboard.tsx +34 -0
  85. package/src/commands/onboard/steps/post-task.tsx +103 -0
  86. package/src/commands/onboard/steps/prereq-check.tsx +178 -0
  87. package/src/commands/onboard/steps/review.tsx +82 -0
  88. package/src/commands/onboard/steps/start.tsx +97 -0
  89. package/src/commands/onboard/templates.ts +34 -0
  90. package/src/commands/onboard/types.ts +259 -0
  91. package/src/commands/onboard.tsx +425 -0
  92. package/src/commands/runner.ts +1540 -630
  93. package/src/commands/setup.tsx +23 -38
  94. package/src/commands/shared/client-config.ts +41 -0
  95. package/src/commands/templates.ts +172 -0
  96. package/src/github/app.ts +8 -0
  97. package/src/github/handlers.ts +384 -151
  98. package/src/github/index.ts +1 -0
  99. package/src/github/mentions-aliases.test.ts +73 -0
  100. package/src/github/mentions.test.ts +3 -3
  101. package/src/github/mentions.ts +32 -6
  102. package/src/github/templates.ts +398 -0
  103. package/src/github/types.ts +1 -0
  104. package/src/gitlab/auth.ts +63 -0
  105. package/src/gitlab/handlers.ts +368 -0
  106. package/src/gitlab/index.ts +19 -0
  107. package/src/gitlab/reactions.ts +104 -0
  108. package/src/gitlab/templates.ts +140 -0
  109. package/src/gitlab/types.ts +130 -0
  110. package/src/heartbeat/heartbeat.ts +434 -0
  111. package/src/heartbeat/index.ts +1 -0
  112. package/src/heartbeat/templates.ts +30 -0
  113. package/src/hooks/hook.ts +555 -4
  114. package/src/hooks/tool-loop-detection.test.ts +158 -0
  115. package/src/hooks/tool-loop-detection.ts +167 -0
  116. package/src/http/active-sessions.ts +199 -0
  117. package/src/http/agents.ts +328 -0
  118. package/src/http/config.ts +191 -0
  119. package/src/http/core.ts +309 -0
  120. package/src/http/db-query.ts +91 -0
  121. package/src/http/ecosystem.ts +63 -0
  122. package/src/http/epics.ts +460 -0
  123. package/src/http/index.ts +216 -0
  124. package/src/http/mcp.ts +77 -0
  125. package/src/http/memory.ts +168 -0
  126. package/src/http/openapi.ts +109 -0
  127. package/src/http/poll.ts +299 -0
  128. package/src/http/prompt-templates.ts +412 -0
  129. package/src/http/repos.ts +195 -0
  130. package/src/http/route-def.ts +123 -0
  131. package/src/http/schedules.ts +426 -0
  132. package/src/http/session-data.ts +241 -0
  133. package/src/http/stats.ts +174 -0
  134. package/src/http/tasks.ts +468 -0
  135. package/src/http/trackers/index.ts +10 -0
  136. package/src/http/trackers/linear.ts +187 -0
  137. package/src/http/types.ts +12 -0
  138. package/src/http/utils.ts +87 -0
  139. package/src/http/webhooks.ts +432 -0
  140. package/src/http/workflows.ts +530 -0
  141. package/src/http.ts +1 -1890
  142. package/src/linear/README.md +65 -0
  143. package/src/linear/app.ts +48 -0
  144. package/src/linear/client.ts +18 -0
  145. package/src/linear/index.ts +1 -0
  146. package/src/linear/oauth.ts +35 -0
  147. package/src/linear/outbound.ts +212 -0
  148. package/src/linear/sync.ts +567 -0
  149. package/src/linear/templates.ts +47 -0
  150. package/src/linear/types.ts +7 -0
  151. package/src/linear/webhook.ts +104 -0
  152. package/src/oauth/README.md +66 -0
  153. package/src/oauth/index.ts +6 -0
  154. package/src/oauth/wrapper.ts +204 -0
  155. package/src/prompts/base-prompt.ts +150 -265
  156. package/src/prompts/defaults.ts +196 -0
  157. package/src/prompts/registry.ts +57 -0
  158. package/src/prompts/resolver.ts +296 -0
  159. package/src/prompts/session-templates.ts +604 -0
  160. package/src/providers/claude-adapter.ts +442 -0
  161. package/src/providers/index.ts +24 -0
  162. package/src/providers/pi-mono-adapter.ts +442 -0
  163. package/src/providers/pi-mono-extension.ts +624 -0
  164. package/src/providers/pi-mono-mcp-client.ts +124 -0
  165. package/src/providers/types.ts +75 -0
  166. package/src/scheduler/scheduler.test.ts +2 -0
  167. package/src/scheduler/scheduler.ts +231 -40
  168. package/src/server.ts +97 -6
  169. package/src/slack/HEURISTICS.md +105 -0
  170. package/src/slack/actions.ts +133 -0
  171. package/src/slack/app.ts +7 -0
  172. package/src/slack/assistant.ts +118 -0
  173. package/src/slack/blocks.ts +233 -0
  174. package/src/slack/channel-activity.ts +177 -0
  175. package/src/slack/commands.ts +31 -17
  176. package/src/slack/files.ts +1 -1
  177. package/src/slack/handlers.test.ts +114 -1
  178. package/src/slack/handlers.ts +230 -55
  179. package/src/slack/responses.ts +120 -67
  180. package/src/slack/router.ts +17 -99
  181. package/src/slack/templates.ts +55 -0
  182. package/src/slack/thread-buffer.ts +213 -0
  183. package/src/slack/watcher.ts +119 -4
  184. package/src/tests/agent-activity.test.ts +247 -0
  185. package/src/tests/agentmail-filters.test.ts +97 -0
  186. package/src/tests/artifact-sdk.test.ts +800 -0
  187. package/src/tests/base-prompt.test.ts +264 -0
  188. package/src/tests/build-pi-skills.test.ts +127 -0
  189. package/src/tests/channel-activity.test.ts +363 -0
  190. package/src/tests/claude-adapter.test.ts +126 -0
  191. package/src/tests/context-versioning.test.ts +425 -0
  192. package/src/tests/db-queries-oauth.test.ts +197 -0
  193. package/src/tests/db-queries-tracker.test.ts +230 -0
  194. package/src/tests/epics.test.ts +3 -3
  195. package/src/tests/error-tracker.test.ts +368 -0
  196. package/src/tests/fetch-resolved-env.test.ts +167 -0
  197. package/src/tests/generate-default-claude-md.test.ts +9 -1
  198. package/src/tests/generate-identity-templates.test.ts +124 -0
  199. package/src/tests/gitlab-auth.test.ts +109 -0
  200. package/src/tests/gitlab-handlers.test.ts +691 -0
  201. package/src/tests/gitlab-vcs-db.test.ts +177 -0
  202. package/src/tests/heartbeat.test.ts +364 -0
  203. package/src/tests/http-api-integration.test.ts +1698 -0
  204. package/src/tests/linear-outbound-sync.test.ts +200 -0
  205. package/src/tests/linear-webhook.test.ts +406 -0
  206. package/src/tests/match-route.test.ts +187 -0
  207. package/src/tests/memory.test.ts +737 -0
  208. package/src/tests/migration-runner-regressions.test.ts +86 -0
  209. package/src/tests/model-control.test.ts +338 -0
  210. package/src/tests/oauth-wrapper.test.ts +147 -0
  211. package/src/tests/onboard-compose.test.ts +138 -0
  212. package/src/tests/onboard-env.test.ts +174 -0
  213. package/src/tests/onboard-manifest.test.ts +137 -0
  214. package/src/tests/pi-mono-adapter.test.ts +234 -0
  215. package/src/tests/pool-session-logs.test.ts +199 -0
  216. package/src/tests/progress-dedup.test.ts +98 -0
  217. package/src/tests/prompt-template-github.test.ts +682 -0
  218. package/src/tests/prompt-template-remaining.test.ts +504 -0
  219. package/src/tests/prompt-template-resolver.test.ts +621 -0
  220. package/src/tests/prompt-template-session.test.ts +363 -0
  221. package/src/tests/prompt-templates-db.test.ts +616 -0
  222. package/src/tests/provider-adapter.test.ts +122 -0
  223. package/src/tests/provider-command-format.test.ts +98 -0
  224. package/src/tests/reload-config.test.ts +170 -0
  225. package/src/tests/runner-polling-api.test.ts +25 -20
  226. package/src/tests/scheduled-tasks.test.ts +104 -0
  227. package/src/tests/scheduler-backoff.test.ts +166 -0
  228. package/src/tests/self-improvement.test.ts +541 -0
  229. package/src/tests/session-attach.test.ts +536 -0
  230. package/src/tests/session-costs.test.ts +267 -1
  231. package/src/tests/slack-actions.test.ts +133 -0
  232. package/src/tests/slack-assistant.test.ts +136 -0
  233. package/src/tests/slack-blocks.test.ts +246 -0
  234. package/src/tests/slack-metadata-inheritance.test.ts +243 -0
  235. package/src/tests/slack-queue-offline.test.ts +174 -0
  236. package/src/tests/slack-router.test.ts +181 -0
  237. package/src/tests/slack-thread-buffer.test.ts +305 -0
  238. package/src/tests/slack-thread-followups.test.ts +298 -0
  239. package/src/tests/slack-watcher.test.ts +101 -0
  240. package/src/tests/structured-output.test.ts +307 -0
  241. package/src/tests/swarm-repos.test.ts +198 -0
  242. package/src/tests/task-cancellation.test.ts +6 -4
  243. package/src/tests/task-working-dir.test.ts +176 -0
  244. package/src/tests/template-fetch.test.ts +490 -0
  245. package/src/tests/tool-annotations.test.ts +371 -0
  246. package/src/tests/tracker-tools.test.ts +184 -0
  247. package/src/tests/update-profile-agentid.test.ts +248 -0
  248. package/src/tests/update-profile-api.test.ts +143 -3
  249. package/src/tests/update-profile-auth.test.ts +195 -0
  250. package/src/tests/validation-adapters.test.ts +86 -0
  251. package/src/tests/vcs-provider.test.ts +27 -0
  252. package/src/tests/workflow-agent-task.test.ts +196 -0
  253. package/src/tests/workflow-async-v2.test.ts +508 -0
  254. package/src/tests/workflow-convergence.test.ts +541 -0
  255. package/src/tests/workflow-definition-validation.test.ts +366 -0
  256. package/src/tests/workflow-engine-v2.test.ts +691 -0
  257. package/src/tests/workflow-executors.test.ts +736 -0
  258. package/src/tests/workflow-http-v2.test.ts +599 -0
  259. package/src/tests/workflow-integration-io.test.ts +902 -0
  260. package/src/tests/workflow-io-schemas.test.ts +624 -0
  261. package/src/tests/workflow-registry.test.ts +592 -0
  262. package/src/tests/workflow-retry-v2.test.ts +401 -0
  263. package/src/tests/workflow-retry-validation.test.ts +282 -0
  264. package/src/tests/workflow-schedule-trigger.test.ts +104 -0
  265. package/src/tests/workflow-template.test.ts +288 -0
  266. package/src/tests/workflow-trigger-schema.test.ts +359 -0
  267. package/src/tests/workflow-triggers-v2.test.ts +264 -0
  268. package/src/tests/workflow-versions.test.ts +208 -0
  269. package/src/tests/workflow-workspace.test.ts +272 -0
  270. package/src/tests/x402-client.test.ts +117 -0
  271. package/src/tests/x402-config.test.ts +182 -0
  272. package/src/tests/x402-spending-tracker.test.ts +185 -0
  273. package/src/tools/cancel-task.ts +2 -0
  274. package/src/tools/context-diff.ts +171 -0
  275. package/src/tools/context-history.ts +138 -0
  276. package/src/tools/create-channel.ts +1 -0
  277. package/src/tools/db-query.ts +78 -0
  278. package/src/tools/delete-channel.ts +132 -0
  279. package/src/tools/epics/assign-task-to-epic.ts +1 -0
  280. package/src/tools/epics/create-epic.ts +3 -2
  281. package/src/tools/epics/delete-epic.ts +2 -0
  282. package/src/tools/epics/get-epic-details.ts +2 -0
  283. package/src/tools/epics/list-epics.ts +2 -0
  284. package/src/tools/epics/unassign-task-from-epic.ts +1 -0
  285. package/src/tools/epics/update-epic.ts +7 -4
  286. package/src/tools/get-swarm.ts +2 -0
  287. package/src/tools/get-task-details.ts +2 -0
  288. package/src/tools/get-tasks.ts +27 -1
  289. package/src/tools/inject-learning.ts +106 -0
  290. package/src/tools/join-swarm.ts +17 -7
  291. package/src/tools/list-channels.ts +2 -0
  292. package/src/tools/list-services.ts +2 -0
  293. package/src/tools/memory-get.ts +56 -0
  294. package/src/tools/memory-search.ts +131 -0
  295. package/src/tools/my-agent-info.ts +2 -0
  296. package/src/tools/poll-task.ts +2 -20
  297. package/src/tools/post-message.ts +1 -0
  298. package/src/tools/prompt-templates/delete.ts +86 -0
  299. package/src/tools/prompt-templates/get.ts +89 -0
  300. package/src/tools/prompt-templates/index.ts +5 -0
  301. package/src/tools/prompt-templates/list.ts +95 -0
  302. package/src/tools/prompt-templates/preview.ts +84 -0
  303. package/src/tools/prompt-templates/set.ts +117 -0
  304. package/src/tools/read-messages.ts +2 -0
  305. package/src/tools/register-agentmail-inbox.ts +166 -0
  306. package/src/tools/register-service.ts +2 -0
  307. package/src/tools/schedules/create-schedule.ts +134 -24
  308. package/src/tools/schedules/delete-schedule.ts +2 -0
  309. package/src/tools/schedules/list-schedules.ts +20 -4
  310. package/src/tools/schedules/run-schedule-now.ts +1 -0
  311. package/src/tools/schedules/update-schedule.ts +49 -17
  312. package/src/tools/send-task.ts +132 -10
  313. package/src/tools/slack-download-file.ts +4 -2
  314. package/src/tools/slack-list-channels.ts +2 -0
  315. package/src/tools/slack-post.ts +2 -0
  316. package/src/tools/slack-read.ts +2 -0
  317. package/src/tools/slack-reply.ts +2 -0
  318. package/src/tools/slack-upload-file.ts +2 -0
  319. package/src/tools/store-progress.ts +205 -4
  320. package/src/tools/swarm-config/delete-config.ts +87 -0
  321. package/src/tools/swarm-config/get-config.ts +108 -0
  322. package/src/tools/swarm-config/index.ts +4 -0
  323. package/src/tools/swarm-config/list-config.ts +99 -0
  324. package/src/tools/swarm-config/set-config.ts +118 -0
  325. package/src/tools/task-action.ts +50 -5
  326. package/src/tools/task-dedup.ts +97 -0
  327. package/src/tools/templates.ts +53 -0
  328. package/src/tools/tool-config.ts +124 -0
  329. package/src/tools/tracker/index.ts +6 -0
  330. package/src/tools/tracker/tracker-link-epic.ts +64 -0
  331. package/src/tools/tracker/tracker-link-task.ts +64 -0
  332. package/src/tools/tracker/tracker-map-agent.ts +57 -0
  333. package/src/tools/tracker/tracker-status.ts +56 -0
  334. package/src/tools/tracker/tracker-sync-status.ts +42 -0
  335. package/src/tools/tracker/tracker-unlink.ts +41 -0
  336. package/src/tools/unregister-service.ts +2 -0
  337. package/src/tools/update-profile.ts +172 -17
  338. package/src/tools/update-service-status.ts +2 -0
  339. package/src/tools/utils.ts +10 -1
  340. package/src/tools/workflows/create-workflow.ts +129 -0
  341. package/src/tools/workflows/delete-workflow.ts +42 -0
  342. package/src/tools/workflows/get-workflow-run.ts +59 -0
  343. package/src/tools/workflows/get-workflow.ts +53 -0
  344. package/src/tools/workflows/index.ts +9 -0
  345. package/src/tools/workflows/list-workflow-runs.ts +48 -0
  346. package/src/tools/workflows/list-workflows.ts +42 -0
  347. package/src/tools/workflows/retry-workflow-run.ts +40 -0
  348. package/src/tools/workflows/trigger-workflow.ts +96 -0
  349. package/src/tools/workflows/update-workflow.ts +133 -0
  350. package/src/tracker/types.ts +51 -0
  351. package/src/types.ts +530 -14
  352. package/src/utils/credentials.test.ts +156 -0
  353. package/src/utils/credentials.ts +50 -0
  354. package/src/utils/error-tracker.ts +190 -0
  355. package/src/vcs/index.ts +15 -0
  356. package/src/vcs/types.ts +5 -0
  357. package/src/workflows/checkpoint.ts +121 -0
  358. package/src/workflows/cooldown.ts +28 -0
  359. package/src/workflows/definition.ts +235 -0
  360. package/src/workflows/engine.ts +580 -0
  361. package/src/workflows/event-bus.ts +29 -0
  362. package/src/workflows/executors/agent-task.ts +103 -0
  363. package/src/workflows/executors/base.ts +86 -0
  364. package/src/workflows/executors/code-match.ts +88 -0
  365. package/src/workflows/executors/index.ts +16 -0
  366. package/src/workflows/executors/notify.ts +93 -0
  367. package/src/workflows/executors/property-match.ts +104 -0
  368. package/src/workflows/executors/raw-llm.ts +83 -0
  369. package/src/workflows/executors/registry.ts +76 -0
  370. package/src/workflows/executors/script.ts +103 -0
  371. package/src/workflows/executors/validate.ts +215 -0
  372. package/src/workflows/executors/vcs.ts +58 -0
  373. package/src/workflows/index.ts +61 -0
  374. package/src/workflows/input.ts +46 -0
  375. package/src/workflows/json-schema-validator.ts +118 -0
  376. package/src/workflows/recovery.ts +139 -0
  377. package/src/workflows/resume.ts +229 -0
  378. package/src/workflows/retry-poller.ts +216 -0
  379. package/src/workflows/template.ts +74 -0
  380. package/src/workflows/templates.ts +86 -0
  381. package/src/workflows/triggers.ts +124 -0
  382. package/src/workflows/validation.ts +104 -0
  383. package/src/workflows/version.ts +44 -0
  384. package/src/x402/cli.ts +140 -0
  385. package/src/x402/client.ts +192 -0
  386. package/src/x402/config.ts +131 -0
  387. package/src/x402/index.ts +37 -0
  388. package/src/x402/openfort-signer.ts +83 -0
  389. package/src/x402/spending-tracker.ts +109 -0
  390. package/templates/official/coder/CLAUDE.md +49 -0
  391. package/templates/official/coder/IDENTITY.md +28 -0
  392. package/templates/official/coder/SOUL.md +43 -0
  393. package/templates/official/coder/TOOLS.md +40 -0
  394. package/templates/official/coder/config.json +23 -0
  395. package/templates/official/coder/start-up.sh +23 -0
  396. package/templates/official/content-reviewer/CLAUDE.md +68 -0
  397. package/templates/official/content-reviewer/IDENTITY.md +28 -0
  398. package/templates/official/content-reviewer/SOUL.md +44 -0
  399. package/templates/official/content-reviewer/TOOLS.md +37 -0
  400. package/templates/official/content-reviewer/config.json +23 -0
  401. package/templates/official/content-reviewer/start-up.sh +23 -0
  402. package/templates/official/content-strategist/CLAUDE.md +63 -0
  403. package/templates/official/content-strategist/IDENTITY.md +33 -0
  404. package/templates/official/content-strategist/SOUL.md +48 -0
  405. package/templates/official/content-strategist/TOOLS.md +47 -0
  406. package/templates/official/content-strategist/config.json +23 -0
  407. package/templates/official/content-strategist/start-up.sh +23 -0
  408. package/templates/official/content-writer/CLAUDE.md +72 -0
  409. package/templates/official/content-writer/IDENTITY.md +30 -0
  410. package/templates/official/content-writer/SOUL.md +46 -0
  411. package/templates/official/content-writer/TOOLS.md +44 -0
  412. package/templates/official/content-writer/config.json +23 -0
  413. package/templates/official/content-writer/start-up.sh +23 -0
  414. package/templates/official/forward-deployed-engineer/CLAUDE.md +54 -0
  415. package/templates/official/forward-deployed-engineer/IDENTITY.md +37 -0
  416. package/templates/official/forward-deployed-engineer/SOUL.md +55 -0
  417. package/templates/official/forward-deployed-engineer/config.json +21 -0
  418. package/templates/official/lead/CLAUDE.md +33 -0
  419. package/templates/official/lead/IDENTITY.md +36 -0
  420. package/templates/official/lead/SOUL.md +51 -0
  421. package/templates/official/lead/config.json +22 -0
  422. package/templates/official/researcher/CLAUDE.md +46 -0
  423. package/templates/official/researcher/IDENTITY.md +28 -0
  424. package/templates/official/researcher/SOUL.md +43 -0
  425. package/templates/official/researcher/config.json +21 -0
  426. package/templates/official/reviewer/CLAUDE.md +63 -0
  427. package/templates/official/reviewer/IDENTITY.md +28 -0
  428. package/templates/official/reviewer/SOUL.md +45 -0
  429. package/templates/official/reviewer/config.json +21 -0
  430. package/templates/official/tester/CLAUDE.md +53 -0
  431. package/templates/official/tester/IDENTITY.md +28 -0
  432. package/templates/official/tester/SOUL.md +55 -0
  433. package/templates/official/tester/config.json +21 -0
  434. package/templates/schema.ts +35 -0
  435. package/.claude/settings.local.json +0 -115
  436. package/.dockerignore +0 -61
  437. package/.editorconfig +0 -15
  438. package/.env.docker.example +0 -39
  439. package/.env.example +0 -40
  440. package/.github/workflows/ci.yml +0 -76
  441. package/.github/workflows/docker-and-deploy.yml +0 -117
  442. package/.wts-config.json +0 -4
  443. package/.wts-setup.ts +0 -102
  444. package/CLAUDE.md +0 -104
  445. package/CONTRIBUTING.md +0 -270
  446. package/DEPLOYMENT.md +0 -605
  447. package/Dockerfile +0 -57
  448. package/Dockerfile.worker +0 -157
  449. package/FAQ.md +0 -19
  450. package/MCP.md +0 -406
  451. package/UI.md +0 -40
  452. package/assets/agent-swarm-logo-orange.png +0 -0
  453. package/assets/agent-swarm-logo.png +0 -0
  454. package/assets/agent-swarm.mp4 +0 -0
  455. package/assets/agent-swarm.png +0 -0
  456. package/biome.json +0 -39
  457. package/deploy/DEPLOY.md +0 -60
  458. package/deploy/agent-swarm.service +0 -17
  459. package/deploy/docker-push.ts +0 -30
  460. package/deploy/install.ts +0 -85
  461. package/deploy/prod-db.ts +0 -42
  462. package/deploy/uninstall.ts +0 -12
  463. package/deploy/update.ts +0 -21
  464. package/docker-compose.example.yml +0 -159
  465. package/docker-entrypoint.sh +0 -352
  466. package/ecosystem.config.cjs +0 -66
  467. package/plugin/README.md +0 -1
  468. package/plugin/hooks/hooks.json +0 -71
  469. package/pyproject.toml +0 -9
  470. package/scripts/generate-mcp-docs.ts +0 -415
  471. package/slack-manifest.json +0 -71
  472. package/src/tests/get-inbox-message.test.ts +0 -145
  473. package/src/tools/get-inbox-message.ts +0 -89
  474. package/src/tools/inbox-delegate.ts +0 -113
  475. package/thoughts/shared/plans/2025-12-18-slack-integration.md +0 -1195
  476. package/thoughts/shared/plans/2025-12-19-agent-log-streaming.md +0 -732
  477. package/thoughts/shared/plans/2025-12-19-role-based-swarm-plugin.md +0 -361
  478. package/thoughts/shared/plans/2025-12-20-mobile-responsive-ui.md +0 -501
  479. package/thoughts/shared/plans/2025-12-20-startup-team-swarm.md +0 -560
  480. package/thoughts/shared/plans/2025-12-23-runner-level-polling.md +0 -934
  481. package/thoughts/shared/plans/2025-12-23-runner-session-logs.md +0 -1000
  482. package/thoughts/shared/plans/2025-12-23-worker-lead-spawn-triggers.md +0 -568
  483. package/thoughts/shared/plans/2026-01-09-inverse-teleport.md +0 -1516
  484. package/thoughts/shared/plans/2026-01-12-agent-rename-pm2-control.md +0 -1133
  485. package/thoughts/shared/plans/2026-01-12-github-app-integration.md +0 -380
  486. package/thoughts/shared/plans/2026-01-12-lead-inbox-model.md +0 -876
  487. package/thoughts/shared/plans/2026-01-12-ralph-wiggum-integration.md +0 -463
  488. package/thoughts/shared/plans/2026-01-13-agent-concurrency.md +0 -691
  489. package/thoughts/shared/plans/2026-01-13-github-assignment-handling.md +0 -690
  490. package/thoughts/shared/plans/2026-01-13-prevent-duplicate-trigger-processing.md +0 -1071
  491. package/thoughts/shared/plans/2026-01-14-fix-slack-thread-context.md +0 -507
  492. package/thoughts/shared/plans/2026-01-15-scheduled-tasks-implementation.md +0 -565
  493. package/thoughts/shared/plans/2026-01-15-usage-cost-tracking-ui.md +0 -1479
  494. package/thoughts/shared/plans/2026-01-16-epics-feature-implementation.md +0 -1230
  495. package/thoughts/shared/research/.gitkeep +0 -0
  496. package/thoughts/shared/research/2025-01-09-inverse-teleport-plan-review.md +0 -420
  497. package/thoughts/shared/research/2025-12-18-slack-integration.md +0 -442
  498. package/thoughts/shared/research/2025-12-19-agent-log-streaming.md +0 -339
  499. package/thoughts/shared/research/2025-12-19-agent-secrets-cli-research.md +0 -390
  500. package/thoughts/shared/research/2025-12-21-gemini-cli-integration.md +0 -376
  501. package/thoughts/shared/research/2025-12-22-runner-loop-architecture.md +0 -582
  502. package/thoughts/shared/research/2025-12-22-setup-experience-improvements.md +0 -264
  503. package/thoughts/shared/research/2026-01-13-lead-duplicate-trigger-processing.md +0 -223
  504. package/thoughts/shared/research/2026-01-14-lead-slack-thread-context.md +0 -277
  505. package/thoughts/shared/research/2026-01-15-ai-tracker-agent-swarm-integration.md +0 -376
  506. package/thoughts/shared/research/2026-01-15-auto-starting-processes-in-worker-containers.md +0 -787
  507. package/thoughts/shared/research/2026-01-15-scheduled-tasks.md +0 -390
  508. package/thoughts/shared/research/2026-01-16-epics-feature-research.md +0 -437
  509. package/thoughts/taras/plans/2026-01-22-agent-swarm-schemas.md +0 -98
  510. package/thoughts/taras/plans/2026-01-28-per-worker-claude-md.md +0 -617
  511. package/thoughts/taras/plans/2026-01-28-sentry-cli-integration.md +0 -214
  512. package/thoughts/taras/research/2026-01-22-vercel-cli-integration.md +0 -287
  513. package/thoughts/taras/research/2026-01-27-excessive-polling-issue.md +0 -311
  514. package/thoughts/taras/research/2026-01-28-per-worker-claude-md.md +0 -383
  515. package/thoughts/taras/research/2026-01-28-sentry-cli-integration.md +0 -240
  516. package/tsconfig.json +0 -37
  517. package/ui/CLAUDE.md +0 -49
  518. package/ui/bun.lock +0 -771
  519. package/ui/index.html +0 -22
  520. package/ui/package-lock.json +0 -5290
  521. package/ui/package.json +0 -33
  522. package/ui/pnpm-lock.yaml +0 -3341
  523. package/ui/postcss.config.js +0 -6
  524. package/ui/public/logo.png +0 -0
  525. package/ui/src/App.tsx +0 -63
  526. package/ui/src/components/ActivityFeed.tsx +0 -440
  527. package/ui/src/components/AgentDetailPanel.tsx +0 -733
  528. package/ui/src/components/AgentsPanel.tsx +0 -815
  529. package/ui/src/components/ChatPanel.tsx +0 -1920
  530. package/ui/src/components/ConfigModal.tsx +0 -253
  531. package/ui/src/components/Dashboard.tsx +0 -832
  532. package/ui/src/components/EditAgentProfileModal.tsx +0 -433
  533. package/ui/src/components/EpicDetailPage.tsx +0 -741
  534. package/ui/src/components/EpicsPanel.tsx +0 -566
  535. package/ui/src/components/Header.tsx +0 -160
  536. package/ui/src/components/JsonViewer.tsx +0 -171
  537. package/ui/src/components/ScheduledTaskDetailPanel.tsx +0 -517
  538. package/ui/src/components/ScheduledTasksPanel.tsx +0 -639
  539. package/ui/src/components/ServicesPanel.tsx +0 -622
  540. package/ui/src/components/SessionLogPanel.tsx +0 -1219
  541. package/ui/src/components/StatsBar.tsx +0 -321
  542. package/ui/src/components/StatusBadge.tsx +0 -168
  543. package/ui/src/components/TaskDetailPanel.tsx +0 -903
  544. package/ui/src/components/TasksPanel.tsx +0 -614
  545. package/ui/src/components/UsageCharts.tsx +0 -216
  546. package/ui/src/components/UsageTab.tsx +0 -394
  547. package/ui/src/hooks/queries.ts +0 -353
  548. package/ui/src/hooks/useAutoScroll.ts +0 -83
  549. package/ui/src/index.css +0 -257
  550. package/ui/src/lib/api.ts +0 -268
  551. package/ui/src/lib/config.ts +0 -35
  552. package/ui/src/lib/contentPreview.ts +0 -208
  553. package/ui/src/lib/theme.ts +0 -214
  554. package/ui/src/lib/utils.ts +0 -88
  555. package/ui/src/main.tsx +0 -28
  556. package/ui/src/types/api.ts +0 -323
  557. package/ui/src/vite-env.d.ts +0 -1
  558. package/ui/tailwind.config.js +0 -37
  559. package/ui/tsconfig.json +0 -31
  560. package/ui/vite.config.ts +0 -35
  561. /package/{thoughts/shared/plans → templates/community}/.gitkeep +0 -0
@@ -1,1479 +0,0 @@
1
- ---
2
- date: 2026-01-15T23:50:00Z
3
- topic: "Usage/Cost Tracking UI - Frontend Implementation Plan"
4
- author: "Coder (a09d19a4)"
5
- status: "draft"
6
- ---
7
-
8
- # Usage/Cost Tracking UI - Frontend Implementation Plan
9
-
10
- ## Overview
11
-
12
- Implement a comprehensive usage and cost tracking UI for the Agent Swarm dashboard. This feature will visualize session cost data captured by the backend (PR #28) across multiple views, providing insights into token usage, costs, and trends per agent, task, and hive.
13
-
14
- ## Current State Analysis
15
-
16
- ### Backend API Available
17
- - **Endpoint**: `GET /api/session-costs`
18
- - **Filters**: `agentId`, `taskId`, `limit` (date range filtering not yet implemented in backend)
19
- - **Data Fields**: `id`, `sessionId`, `taskId`, `agentId`, `totalCostUsd`, `inputTokens`, `outputTokens`, `cacheReadTokens`, `cacheWriteTokens`, `durationMs`, `numTurns`, `model`, `isError`, `createdAt`
20
-
21
- ### Frontend Patterns (Reference Files)
22
- - **API Client**: `ui/src/lib/api.ts` - Class-based fetch wrapper
23
- - **Query Hooks**: `ui/src/hooks/queries.ts` - TanStack React Query hooks
24
- - **Types**: `ui/src/types/api.ts` - TypeScript interfaces
25
- - **Stats Display**: `ui/src/components/StatsBar.tsx` - Hexagon stat widgets
26
- - **Detail Panels**: `ui/src/components/AgentDetailPanel.tsx`, `TaskDetailPanel.tsx`
27
- - **Dashboard Layout**: `ui/src/components/Dashboard.tsx` - Tab-based navigation
28
-
29
- ### UI Library
30
- - **MUI Joy** (`@mui/joy`) - Primary component library
31
- - **No charting library currently installed** - Will need to add one
32
-
33
- ### Key Discoveries
34
- - Stats use custom hexagon CSS shapes with `clipPath`
35
- - Detail panels support expandable layouts with horizontal/vertical switching
36
- - Tables use MUI Joy `Table` with sticky headers and responsive cards for mobile
37
- - React Query with 5-second auto-refresh is the standard data fetching pattern
38
- - Color scheme supports dark/light mode via `useColorScheme()`
39
-
40
- ## Desired End State
41
-
42
- A fully integrated cost tracking UI that:
43
- 1. Shows monthly totals in the home page stats bar
44
- 2. Displays per-agent monthly costs in the agents table
45
- 3. Provides detailed usage breakdowns in agent/task detail panels
46
- 4. Offers a dedicated "Usage" tab with comprehensive analytics and charts
47
-
48
- ## Charting Library Recommendation
49
-
50
- **Recommended: Recharts**
51
-
52
- Rationale:
53
- - Built for React with declarative components
54
- - Lightweight (~200KB gzipped)
55
- - Good TypeScript support
56
- - Simple API that matches MUI Joy's declarative style
57
- - Supports all required chart types (line, bar, pie, area)
58
- - Active maintenance and community
59
-
60
- Alternative considered: Victory (heavier), Chart.js (imperative API), Nivo (complex)
61
-
62
- ## What We're NOT Doing
63
-
64
- - Backend date range filtering (separate PR needed)
65
- - Real-time cost streaming (polling is sufficient)
66
- - Cost predictions/forecasting
67
- - Export functionality
68
- - Budget alerts/thresholds
69
- - Multi-currency support
70
-
71
- ---
72
-
73
- ## Implementation Approach
74
-
75
- ### Data Flow Architecture
76
-
77
- ```
78
- GET /api/session-costs → api.fetchSessionCosts() → useSessionCosts() hook → Components
79
- useAgentUsage() hook
80
- useTaskUsage() hook
81
- useUsageStats() hook
82
- ```
83
-
84
- ### Aggregation Strategy
85
-
86
- Client-side aggregation from raw session cost data:
87
- - Monthly totals: Filter by `createdAt` month, sum values
88
- - Daily/weekly breakdowns: Group by date, aggregate
89
- - Per-agent/task: Filter by ID, aggregate
90
-
91
- Note: For large datasets, consider adding backend aggregation endpoints in a future phase.
92
-
93
- ---
94
-
95
- ## Phase 1: Foundation - Types, API, and Hooks
96
-
97
- ### Overview
98
- Add TypeScript types, API client methods, and React Query hooks for session costs.
99
-
100
- ### Changes Required
101
-
102
- #### 1. Type Definitions
103
- **File**: `ui/src/types/api.ts`
104
- **Changes**: Add SessionCost interface and response types
105
-
106
- ```typescript
107
- // Add after SessionLogsResponse (line ~93)
108
-
109
- export interface SessionCost {
110
- id: string;
111
- sessionId: string;
112
- taskId?: string;
113
- agentId: string;
114
- totalCostUsd: number;
115
- inputTokens: number;
116
- outputTokens: number;
117
- cacheReadTokens: number;
118
- cacheWriteTokens: number;
119
- durationMs: number;
120
- numTurns: number;
121
- model: string;
122
- isError: boolean;
123
- createdAt: string;
124
- }
125
-
126
- export interface SessionCostsResponse {
127
- costs: SessionCost[];
128
- }
129
-
130
- // Aggregated usage types for UI
131
- export interface UsageStats {
132
- totalCostUsd: number;
133
- totalTokens: number;
134
- inputTokens: number;
135
- outputTokens: number;
136
- cacheReadTokens: number;
137
- cacheWriteTokens: number;
138
- sessionCount: number;
139
- totalDurationMs: number;
140
- avgCostPerSession: number;
141
- }
142
-
143
- export interface DailyUsage {
144
- date: string;
145
- costUsd: number;
146
- tokens: number;
147
- sessions: number;
148
- }
149
-
150
- export interface AgentUsageSummary {
151
- agentId: string;
152
- agentName?: string;
153
- monthlyCostUsd: number;
154
- monthlyTokens: number;
155
- sessionCount: number;
156
- }
157
- ```
158
-
159
- #### 2. API Client Methods
160
- **File**: `ui/src/lib/api.ts`
161
- **Changes**: Add fetchSessionCosts method
162
-
163
- ```typescript
164
- // Add to ApiClient class (after fetchServices method, ~line 182)
165
-
166
- async fetchSessionCosts(filters?: {
167
- agentId?: string;
168
- taskId?: string;
169
- limit?: number
170
- }): Promise<SessionCostsResponse> {
171
- const params = new URLSearchParams();
172
- if (filters?.agentId) params.set("agentId", filters.agentId);
173
- if (filters?.taskId) params.set("taskId", filters.taskId);
174
- if (filters?.limit) params.set("limit", String(filters.limit));
175
- const queryString = params.toString();
176
- const url = `${this.getBaseUrl()}/api/session-costs${queryString ? `?${queryString}` : ""}`;
177
- const res = await fetch(url, { headers: this.getHeaders() });
178
- if (!res.ok) throw new Error(`Failed to fetch session costs: ${res.status}`);
179
- return res.json();
180
- }
181
- ```
182
-
183
- #### 3. React Query Hooks
184
- **File**: `ui/src/hooks/queries.ts`
185
- **Changes**: Add usage-related hooks
186
-
187
- ```typescript
188
- // Add at end of file
189
-
190
- export interface SessionCostFilters {
191
- agentId?: string;
192
- taskId?: string;
193
- limit?: number;
194
- }
195
-
196
- export function useSessionCosts(filters?: SessionCostFilters) {
197
- return useQuery({
198
- queryKey: ["session-costs", filters],
199
- queryFn: () => api.fetchSessionCosts(filters),
200
- select: (data) => data.costs,
201
- });
202
- }
203
-
204
- // Hook for aggregated usage stats (monthly)
205
- export function useMonthlyUsageStats() {
206
- const { data: costs, ...rest } = useSessionCosts({ limit: 1000 });
207
-
208
- const stats = useMemo(() => {
209
- if (!costs) return null;
210
-
211
- const now = new Date();
212
- const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
213
-
214
- const monthlyCosts = costs.filter(
215
- (c) => new Date(c.createdAt) >= startOfMonth
216
- );
217
-
218
- return {
219
- totalCostUsd: monthlyCosts.reduce((sum, c) => sum + c.totalCostUsd, 0),
220
- totalTokens: monthlyCosts.reduce(
221
- (sum, c) => sum + c.inputTokens + c.outputTokens, 0
222
- ),
223
- inputTokens: monthlyCosts.reduce((sum, c) => sum + c.inputTokens, 0),
224
- outputTokens: monthlyCosts.reduce((sum, c) => sum + c.outputTokens, 0),
225
- cacheReadTokens: monthlyCosts.reduce((sum, c) => sum + c.cacheReadTokens, 0),
226
- cacheWriteTokens: monthlyCosts.reduce((sum, c) => sum + c.cacheWriteTokens, 0),
227
- sessionCount: monthlyCosts.length,
228
- totalDurationMs: monthlyCosts.reduce((sum, c) => sum + c.durationMs, 0),
229
- avgCostPerSession: monthlyCosts.length > 0
230
- ? monthlyCosts.reduce((sum, c) => sum + c.totalCostUsd, 0) / monthlyCosts.length
231
- : 0,
232
- };
233
- }, [costs]);
234
-
235
- return { data: stats, ...rest };
236
- }
237
-
238
- // Hook for agent usage summary
239
- export function useAgentUsageSummary(agentId: string) {
240
- return useQuery({
241
- queryKey: ["agent-usage", agentId],
242
- queryFn: () => api.fetchSessionCosts({ agentId, limit: 500 }),
243
- select: (data) => {
244
- const costs = data.costs;
245
- const now = new Date();
246
- const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
247
- const startOfWeek = new Date(now);
248
- startOfWeek.setDate(now.getDate() - now.getDay());
249
- const startOfDay = new Date(now.getFullYear(), now.getMonth(), now.getDate());
250
-
251
- const filterByDate = (start: Date) =>
252
- costs.filter((c) => new Date(c.createdAt) >= start);
253
-
254
- return {
255
- daily: aggregateUsage(filterByDate(startOfDay)),
256
- weekly: aggregateUsage(filterByDate(startOfWeek)),
257
- monthly: aggregateUsage(filterByDate(startOfMonth)),
258
- all: aggregateUsage(costs),
259
- };
260
- },
261
- enabled: !!agentId,
262
- });
263
- }
264
-
265
- // Hook for task usage
266
- export function useTaskUsage(taskId: string) {
267
- return useQuery({
268
- queryKey: ["task-usage", taskId],
269
- queryFn: () => api.fetchSessionCosts({ taskId }),
270
- select: (data) => aggregateUsage(data.costs),
271
- enabled: !!taskId,
272
- });
273
- }
274
-
275
- // Helper function for aggregation
276
- function aggregateUsage(costs: SessionCost[]): UsageStats {
277
- return {
278
- totalCostUsd: costs.reduce((sum, c) => sum + c.totalCostUsd, 0),
279
- totalTokens: costs.reduce((sum, c) => sum + c.inputTokens + c.outputTokens, 0),
280
- inputTokens: costs.reduce((sum, c) => sum + c.inputTokens, 0),
281
- outputTokens: costs.reduce((sum, c) => sum + c.outputTokens, 0),
282
- cacheReadTokens: costs.reduce((sum, c) => sum + c.cacheReadTokens, 0),
283
- cacheWriteTokens: costs.reduce((sum, c) => sum + c.cacheWriteTokens, 0),
284
- sessionCount: costs.length,
285
- totalDurationMs: costs.reduce((sum, c) => sum + c.durationMs, 0),
286
- avgCostPerSession: costs.length > 0
287
- ? costs.reduce((sum, c) => sum + c.totalCostUsd, 0) / costs.length
288
- : 0,
289
- };
290
- }
291
- ```
292
-
293
- ### Success Criteria
294
-
295
- #### Automated Verification
296
- - [ ] TypeScript compiles: `cd ui && npm run typecheck`
297
- - [ ] Build succeeds: `cd ui && npm run build`
298
-
299
- #### Manual Verification
300
- - [ ] API calls return data when tested in browser dev tools
301
-
302
- **Implementation Note**: After completing this phase, verify types compile before proceeding.
303
-
304
- ---
305
-
306
- ## Phase 2: StatsBar Enhancement - Monthly Usage Hives
307
-
308
- ### Overview
309
- Add two new hexagon stats to the home page showing monthly token count and monthly cost.
310
-
311
- ### Changes Required
312
-
313
- #### 1. StatsBar Component
314
- **File**: `ui/src/components/StatsBar.tsx`
315
- **Changes**: Add usage stats hexagons
316
-
317
- ```typescript
318
- // Add import (line ~4)
319
- import { useStats, useMonthlyUsageStats } from "../hooks/queries";
320
-
321
- // Inside StatsBar component, add usage stats hook (after line 117)
322
- const { data: usageStats } = useMonthlyUsageStats();
323
-
324
- // Add to colors object (around line 123)
325
- const colors = {
326
- // ... existing colors
327
- green: "#22C55E",
328
- greenGlow: isDark ? "rgba(34, 197, 94, 0.5)" : "rgba(34, 197, 94, 0.25)",
329
- };
330
-
331
- // Add new stats to topRow array (after existing items, around line 159)
332
- // Option A: Add to existing rows
333
- // Option B: Create a third row for usage stats
334
-
335
- // Recommended: Add to bottom row or create usage section
336
- const usageRow = [
337
- {
338
- label: "MTD TOKENS",
339
- value: usageStats ? formatCompactNumber(usageStats.totalTokens) : "—",
340
- color: colors.green,
341
- glowColor: colors.greenGlow,
342
- },
343
- {
344
- label: "MTD COST",
345
- value: usageStats ? `$${usageStats.totalCostUsd.toFixed(2)}` : "—",
346
- color: colors.amber,
347
- glowColor: colors.amberGlow,
348
- },
349
- ];
350
- ```
351
-
352
- #### 2. Utility Function for Number Formatting
353
- **File**: `ui/src/lib/utils.ts`
354
- **Changes**: Add compact number formatter
355
-
356
- ```typescript
357
- // Add at end of file
358
-
359
- export function formatCompactNumber(num: number): string {
360
- if (num >= 1_000_000_000) return `${(num / 1_000_000_000).toFixed(1)}B`;
361
- if (num >= 1_000_000) return `${(num / 1_000_000).toFixed(1)}M`;
362
- if (num >= 1_000) return `${(num / 1_000).toFixed(1)}K`;
363
- return num.toString();
364
- }
365
-
366
- export function formatCurrency(amount: number): string {
367
- if (amount >= 1000) return `$${(amount / 1000).toFixed(1)}K`;
368
- if (amount >= 1) return `$${amount.toFixed(2)}`;
369
- return `$${amount.toFixed(4)}`;
370
- }
371
-
372
- export function formatDuration(ms: number): string {
373
- const seconds = Math.floor(ms / 1000);
374
- const minutes = Math.floor(seconds / 60);
375
- const hours = Math.floor(minutes / 60);
376
-
377
- if (hours > 0) return `${hours}h ${minutes % 60}m`;
378
- if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
379
- return `${seconds}s`;
380
- }
381
- ```
382
-
383
- ### UI Mockup Description
384
-
385
- The StatsBar will display two additional hexagons:
386
- - **MTD TOKENS**: Green hexagon showing formatted total tokens (e.g., "1.2M")
387
- - **MTD COST**: Amber hexagon showing formatted cost (e.g., "$45.23")
388
-
389
- Layout options:
390
- 1. Add to existing honeycomb grid (extend bottom row to 6 hexagons)
391
- 2. Create separate "Usage" section below existing stats
392
- 3. Add hover tooltip with detailed breakdown
393
-
394
- Recommended: Option 1 for consistency, with tooltip showing breakdown.
395
-
396
- ### Success Criteria
397
-
398
- #### Automated Verification
399
- - [ ] Build succeeds: `cd ui && npm run build`
400
- - [ ] TypeScript compiles: `cd ui && npm run typecheck`
401
-
402
- #### Manual Verification
403
- - [ ] Stats bar shows MTD tokens and cost
404
- - [ ] Values update when session costs are created
405
- - [ ] Responsive layout works on mobile
406
-
407
- ---
408
-
409
- ## Phase 3: Agents Table - Monthly Usage Column
410
-
411
- ### Overview
412
- Add a new column to the agents table showing each agent's monthly usage.
413
-
414
- ### Changes Required
415
-
416
- #### 1. AgentsPanel Component
417
- **File**: `ui/src/components/AgentsPanel.tsx`
418
- **Changes**: Add monthly usage column
419
-
420
- ```typescript
421
- // Add import for useSessionCosts
422
- import { useAgents, useSessionCosts } from "../hooks/queries";
423
-
424
- // Inside AgentsPanel, fetch all session costs
425
- const { data: allCosts } = useSessionCosts({ limit: 2000 });
426
-
427
- // Create agent usage map (memoized)
428
- const agentUsageMap = useMemo(() => {
429
- if (!allCosts) return new Map();
430
-
431
- const now = new Date();
432
- const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
433
-
434
- const map = new Map<string, { cost: number; tokens: number }>();
435
-
436
- allCosts
437
- .filter((c) => new Date(c.createdAt) >= startOfMonth)
438
- .forEach((cost) => {
439
- const existing = map.get(cost.agentId) || { cost: 0, tokens: 0 };
440
- map.set(cost.agentId, {
441
- cost: existing.cost + cost.totalCostUsd,
442
- tokens: existing.tokens + cost.inputTokens + cost.outputTokens,
443
- });
444
- });
445
-
446
- return map;
447
- }, [allCosts]);
448
-
449
- // Add column header in Table thead (after UPDATED column)
450
- <th style={{ width: "100px" }}>MTD USAGE</th>
451
-
452
- // Add column data in table row
453
- <td>
454
- <Box sx={{ display: "flex", flexDirection: "column", gap: 0.25 }}>
455
- <Typography sx={{ fontFamily: "code", fontSize: "0.7rem", color: colors.amber }}>
456
- {formatCurrency(agentUsageMap.get(agent.id)?.cost || 0)}
457
- </Typography>
458
- <Typography sx={{ fontFamily: "code", fontSize: "0.6rem", color: "text.tertiary" }}>
459
- {formatCompactNumber(agentUsageMap.get(agent.id)?.tokens || 0)} tokens
460
- </Typography>
461
- </Box>
462
- </td>
463
- ```
464
-
465
- ### UI Mockup Description
466
-
467
- | NAME | ROLE | STATUS | CAPACITY | MTD USAGE | UPDATED |
468
- |------|------|--------|----------|-----------|---------|
469
- | Worker-1 | Coder | busy | 2/5 | **$12.34** <br/> 245K tokens | 2m ago |
470
- | Worker-2 | Reviewer | idle | 0/3 | **$5.67** <br/> 89K tokens | 5m ago |
471
-
472
- The MTD USAGE column shows:
473
- - Primary: Cost in amber color
474
- - Secondary: Token count in tertiary gray
475
-
476
- ### Success Criteria
477
-
478
- #### Automated Verification
479
- - [ ] Build succeeds: `cd ui && npm run build`
480
-
481
- #### Manual Verification
482
- - [ ] Agents table shows MTD usage column
483
- - [ ] Values are correct per agent
484
- - [ ] Column is sortable (optional enhancement)
485
-
486
- ---
487
-
488
- ## Phase 4: Agent Detail Sidepanel - Usage Breakdown
489
-
490
- ### Overview
491
- Add daily/weekly/monthly usage breakdown to the agent detail panel.
492
-
493
- ### Changes Required
494
-
495
- #### 1. AgentDetailPanel Component
496
- **File**: `ui/src/components/AgentDetailPanel.tsx`
497
- **Changes**: Add usage section
498
-
499
- ```typescript
500
- // Add import
501
- import { useAgent, useLogs, useAgentUsageSummary } from "../hooks/queries";
502
- import { formatCurrency, formatCompactNumber, formatDuration } from "../lib/utils";
503
-
504
- // Inside component, add usage hook (after existing hooks)
505
- const { data: usage } = useAgentUsageSummary(agentId);
506
-
507
- // Add UsageSection component (inside AgentDetailPanel, before return)
508
- const UsageSection = () => (
509
- <Box sx={{ p: { xs: 1.5, md: 2 } }}>
510
- <Typography
511
- sx={{
512
- fontFamily: "code",
513
- fontSize: "0.7rem",
514
- color: "text.tertiary",
515
- letterSpacing: "0.05em",
516
- mb: 1.5,
517
- }}
518
- >
519
- USAGE BREAKDOWN
520
- </Typography>
521
-
522
- {!usage ? (
523
- <Typography sx={{ fontFamily: "code", fontSize: "0.75rem", color: "text.tertiary" }}>
524
- Loading usage data...
525
- </Typography>
526
- ) : (
527
- <Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
528
- {/* Daily */}
529
- <UsageCard
530
- title="TODAY"
531
- cost={usage.daily.totalCostUsd}
532
- tokens={usage.daily.totalTokens}
533
- sessions={usage.daily.sessionCount}
534
- color={colors.amber}
535
- />
536
-
537
- {/* Weekly */}
538
- <UsageCard
539
- title="THIS WEEK"
540
- cost={usage.weekly.totalCostUsd}
541
- tokens={usage.weekly.totalTokens}
542
- sessions={usage.weekly.sessionCount}
543
- color={colors.gold}
544
- />
545
-
546
- {/* Monthly */}
547
- <UsageCard
548
- title="THIS MONTH"
549
- cost={usage.monthly.totalCostUsd}
550
- tokens={usage.monthly.totalTokens}
551
- sessions={usage.monthly.sessionCount}
552
- color={colors.blue}
553
- />
554
- </Box>
555
- )}
556
- </Box>
557
- );
558
-
559
- // UsageCard helper component
560
- const UsageCard = ({ title, cost, tokens, sessions, color }: {
561
- title: string;
562
- cost: number;
563
- tokens: number;
564
- sessions: number;
565
- color: string;
566
- }) => (
567
- <Box
568
- sx={{
569
- bgcolor: "background.level1",
570
- border: "1px solid",
571
- borderColor: "neutral.outlinedBorder",
572
- borderRadius: 1,
573
- p: 1.5,
574
- }}
575
- >
576
- <Typography
577
- sx={{
578
- fontFamily: "code",
579
- fontSize: "0.6rem",
580
- color: "text.tertiary",
581
- letterSpacing: "0.05em",
582
- mb: 0.5,
583
- }}
584
- >
585
- {title}
586
- </Typography>
587
- <Box sx={{ display: "flex", alignItems: "baseline", gap: 1 }}>
588
- <Typography sx={{ fontFamily: "code", fontSize: "1rem", fontWeight: 600, color }}>
589
- {formatCurrency(cost)}
590
- </Typography>
591
- <Typography sx={{ fontFamily: "code", fontSize: "0.7rem", color: "text.secondary" }}>
592
- {formatCompactNumber(tokens)} tokens
593
- </Typography>
594
- </Box>
595
- <Typography sx={{ fontFamily: "code", fontSize: "0.6rem", color: "text.tertiary", mt: 0.5 }}>
596
- {sessions} session{sessions !== 1 ? "s" : ""}
597
- </Typography>
598
- </Box>
599
- );
600
-
601
- // Add UsageSection to the panel layout (after InfoSection, before ActivitySection)
602
- // In collapsed mode:
603
- <InfoSection />
604
- <Divider sx={{ bgcolor: "neutral.outlinedBorder" }} />
605
- <UsageSection />
606
- <Divider sx={{ bgcolor: "neutral.outlinedBorder" }} />
607
- <ActivitySection />
608
-
609
- // In expanded mode: Add as middle column or section
610
- ```
611
-
612
- ### UI Mockup Description
613
-
614
- ```
615
- ┌─────────────────────────────────────┐
616
- │ USAGE BREAKDOWN │
617
- ├─────────────────────────────────────┤
618
- │ ┌─────────────────────────────────┐ │
619
- │ │ TODAY │ │
620
- │ │ $2.45 125K tokens │ │
621
- │ │ 3 sessions │ │
622
- │ └─────────────────────────────────┘ │
623
- │ ┌─────────────────────────────────┐ │
624
- │ │ THIS WEEK │ │
625
- │ │ $12.34 456K tokens │ │
626
- │ │ 15 sessions │ │
627
- │ └─────────────────────────────────┘ │
628
- │ ┌─────────────────────────────────┐ │
629
- │ │ THIS MONTH │ │
630
- │ │ $45.67 1.2M tokens │ │
631
- │ │ 52 sessions │ │
632
- │ └─────────────────────────────────┘ │
633
- └─────────────────────────────────────┘
634
- ```
635
-
636
- ### Success Criteria
637
-
638
- #### Automated Verification
639
- - [ ] Build succeeds: `cd ui && npm run build`
640
-
641
- #### Manual Verification
642
- - [ ] Agent detail shows usage breakdown
643
- - [ ] Daily/weekly/monthly values are accurate
644
- - [ ] Updates when new sessions are created
645
-
646
- ---
647
-
648
- ## Phase 5: Agent Detail Page - Charts (Expanded View)
649
-
650
- ### Overview
651
- Add charts to the expanded agent detail view showing usage trends and breakdowns.
652
-
653
- ### Changes Required
654
-
655
- #### 1. Install Recharts
656
- **File**: `ui/package.json`
657
- **Changes**: Add recharts dependency
658
-
659
- ```bash
660
- cd ui && npm install recharts
661
- ```
662
-
663
- #### 2. Create UsageCharts Component
664
- **File**: `ui/src/components/UsageCharts.tsx` (new file)
665
- **Changes**: Create chart components
666
-
667
- ```typescript
668
- import { useMemo } from "react";
669
- import Box from "@mui/joy/Box";
670
- import Typography from "@mui/joy/Typography";
671
- import { useColorScheme } from "@mui/joy/styles";
672
- import {
673
- LineChart,
674
- Line,
675
- AreaChart,
676
- Area,
677
- BarChart,
678
- Bar,
679
- PieChart,
680
- Pie,
681
- Cell,
682
- XAxis,
683
- YAxis,
684
- CartesianGrid,
685
- Tooltip,
686
- ResponsiveContainer,
687
- Legend,
688
- } from "recharts";
689
- import type { SessionCost } from "../types/api";
690
-
691
- interface UsageChartsProps {
692
- costs: SessionCost[];
693
- timeRange?: "7d" | "30d" | "90d";
694
- }
695
-
696
- export function CostTrendChart({ costs, timeRange = "30d" }: UsageChartsProps) {
697
- const { mode } = useColorScheme();
698
- const isDark = mode === "dark";
699
-
700
- const chartData = useMemo(() => {
701
- const days = timeRange === "7d" ? 7 : timeRange === "30d" ? 30 : 90;
702
- const data: { date: string; cost: number; tokens: number }[] = [];
703
-
704
- for (let i = days - 1; i >= 0; i--) {
705
- const date = new Date();
706
- date.setDate(date.getDate() - i);
707
- const dateStr = date.toISOString().split("T")[0];
708
-
709
- const dayCosts = costs.filter(
710
- (c) => c.createdAt.startsWith(dateStr)
711
- );
712
-
713
- data.push({
714
- date: date.toLocaleDateString("en-US", { month: "short", day: "numeric" }),
715
- cost: dayCosts.reduce((sum, c) => sum + c.totalCostUsd, 0),
716
- tokens: dayCosts.reduce((sum, c) => sum + c.inputTokens + c.outputTokens, 0),
717
- });
718
- }
719
-
720
- return data;
721
- }, [costs, timeRange]);
722
-
723
- const colors = {
724
- line: isDark ? "#F5A623" : "#D48806",
725
- grid: isDark ? "#3D3020" : "#E5DDD0",
726
- text: isDark ? "#8B7355" : "#6B5344",
727
- };
728
-
729
- return (
730
- <Box sx={{ width: "100%", height: 250 }}>
731
- <Typography sx={{ fontFamily: "code", fontSize: "0.7rem", color: "text.tertiary", mb: 1 }}>
732
- COST TREND
733
- </Typography>
734
- <ResponsiveContainer>
735
- <AreaChart data={chartData}>
736
- <CartesianGrid strokeDasharray="3 3" stroke={colors.grid} />
737
- <XAxis
738
- dataKey="date"
739
- tick={{ fontSize: 10, fill: colors.text }}
740
- tickLine={false}
741
- />
742
- <YAxis
743
- tick={{ fontSize: 10, fill: colors.text }}
744
- tickFormatter={(v) => `$${v.toFixed(2)}`}
745
- tickLine={false}
746
- />
747
- <Tooltip
748
- contentStyle={{
749
- backgroundColor: isDark ? "#1A130E" : "#FFFFFF",
750
- border: `1px solid ${colors.grid}`,
751
- borderRadius: 4,
752
- fontFamily: "monospace",
753
- fontSize: 12,
754
- }}
755
- formatter={(value: number) => [`$${value.toFixed(4)}`, "Cost"]}
756
- />
757
- <Area
758
- type="monotone"
759
- dataKey="cost"
760
- stroke={colors.line}
761
- fill={`${colors.line}40`}
762
- strokeWidth={2}
763
- />
764
- </AreaChart>
765
- </ResponsiveContainer>
766
- </Box>
767
- );
768
- }
769
-
770
- export function TokenDistributionChart({ costs }: { costs: SessionCost[] }) {
771
- const { mode } = useColorScheme();
772
- const isDark = mode === "dark";
773
-
774
- const data = useMemo(() => {
775
- const totals = costs.reduce(
776
- (acc, c) => ({
777
- input: acc.input + c.inputTokens,
778
- output: acc.output + c.outputTokens,
779
- cacheRead: acc.cacheRead + c.cacheReadTokens,
780
- cacheWrite: acc.cacheWrite + c.cacheWriteTokens,
781
- }),
782
- { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }
783
- );
784
-
785
- return [
786
- { name: "Input", value: totals.input, color: "#3B82F6" },
787
- { name: "Output", value: totals.output, color: "#F5A623" },
788
- { name: "Cache Read", value: totals.cacheRead, color: "#22C55E" },
789
- { name: "Cache Write", value: totals.cacheWrite, color: "#D4A574" },
790
- ].filter((d) => d.value > 0);
791
- }, [costs]);
792
-
793
- return (
794
- <Box sx={{ width: "100%", height: 250 }}>
795
- <Typography sx={{ fontFamily: "code", fontSize: "0.7rem", color: "text.tertiary", mb: 1 }}>
796
- TOKEN DISTRIBUTION
797
- </Typography>
798
- <ResponsiveContainer>
799
- <PieChart>
800
- <Pie
801
- data={data}
802
- dataKey="value"
803
- nameKey="name"
804
- cx="50%"
805
- cy="50%"
806
- outerRadius={80}
807
- label={({ name, percent }) => `${name} ${(percent * 100).toFixed(0)}%`}
808
- labelLine={false}
809
- >
810
- {data.map((entry, index) => (
811
- <Cell key={`cell-${index}`} fill={entry.color} />
812
- ))}
813
- </Pie>
814
- <Tooltip
815
- formatter={(value: number) => [value.toLocaleString(), "Tokens"]}
816
- contentStyle={{
817
- backgroundColor: isDark ? "#1A130E" : "#FFFFFF",
818
- borderRadius: 4,
819
- fontFamily: "monospace",
820
- fontSize: 12,
821
- }}
822
- />
823
- <Legend />
824
- </PieChart>
825
- </ResponsiveContainer>
826
- </Box>
827
- );
828
- }
829
-
830
- export function ModelUsageChart({ costs }: { costs: SessionCost[] }) {
831
- const { mode } = useColorScheme();
832
- const isDark = mode === "dark";
833
-
834
- const data = useMemo(() => {
835
- const byModel = new Map<string, { cost: number; sessions: number }>();
836
-
837
- costs.forEach((c) => {
838
- const existing = byModel.get(c.model) || { cost: 0, sessions: 0 };
839
- byModel.set(c.model, {
840
- cost: existing.cost + c.totalCostUsd,
841
- sessions: existing.sessions + 1,
842
- });
843
- });
844
-
845
- return Array.from(byModel.entries()).map(([model, data]) => ({
846
- model,
847
- cost: data.cost,
848
- sessions: data.sessions,
849
- }));
850
- }, [costs]);
851
-
852
- const colors = {
853
- bar: isDark ? "#F5A623" : "#D48806",
854
- grid: isDark ? "#3D3020" : "#E5DDD0",
855
- text: isDark ? "#8B7355" : "#6B5344",
856
- };
857
-
858
- return (
859
- <Box sx={{ width: "100%", height: 200 }}>
860
- <Typography sx={{ fontFamily: "code", fontSize: "0.7rem", color: "text.tertiary", mb: 1 }}>
861
- COST BY MODEL
862
- </Typography>
863
- <ResponsiveContainer>
864
- <BarChart data={data} layout="vertical">
865
- <CartesianGrid strokeDasharray="3 3" stroke={colors.grid} />
866
- <XAxis
867
- type="number"
868
- tick={{ fontSize: 10, fill: colors.text }}
869
- tickFormatter={(v) => `$${v.toFixed(2)}`}
870
- />
871
- <YAxis
872
- type="category"
873
- dataKey="model"
874
- tick={{ fontSize: 10, fill: colors.text }}
875
- width={60}
876
- />
877
- <Tooltip
878
- formatter={(value: number) => [`$${value.toFixed(4)}`, "Cost"]}
879
- contentStyle={{
880
- backgroundColor: isDark ? "#1A130E" : "#FFFFFF",
881
- borderRadius: 4,
882
- fontFamily: "monospace",
883
- fontSize: 12,
884
- }}
885
- />
886
- <Bar dataKey="cost" fill={colors.bar} radius={[0, 4, 4, 0]} />
887
- </BarChart>
888
- </ResponsiveContainer>
889
- </Box>
890
- );
891
- }
892
- ```
893
-
894
- #### 3. Update AgentDetailPanel (Expanded View)
895
- **File**: `ui/src/components/AgentDetailPanel.tsx`
896
- **Changes**: Add charts section in expanded mode
897
-
898
- ```typescript
899
- // Add import
900
- import { CostTrendChart, TokenDistributionChart, ModelUsageChart } from "./UsageCharts";
901
- import { useSessionCosts } from "../hooks/queries";
902
-
903
- // Inside component, add costs hook
904
- const { data: agentCosts } = useSessionCosts({ agentId, limit: 500 });
905
-
906
- // Add ChartsSection component
907
- const ChartsSection = () => (
908
- <Box sx={{ p: 2, display: "flex", flexDirection: "column", gap: 3 }}>
909
- <Typography
910
- sx={{
911
- fontFamily: "code",
912
- fontSize: "0.7rem",
913
- color: "text.tertiary",
914
- letterSpacing: "0.05em",
915
- }}
916
- >
917
- USAGE ANALYTICS
918
- </Typography>
919
-
920
- {agentCosts && agentCosts.length > 0 ? (
921
- <>
922
- <CostTrendChart costs={agentCosts} timeRange="30d" />
923
- <Box sx={{ display: "flex", gap: 2, flexWrap: "wrap" }}>
924
- <Box sx={{ flex: 1, minWidth: 250 }}>
925
- <TokenDistributionChart costs={agentCosts} />
926
- </Box>
927
- <Box sx={{ flex: 1, minWidth: 250 }}>
928
- <ModelUsageChart costs={agentCosts} />
929
- </Box>
930
- </Box>
931
- </>
932
- ) : (
933
- <Typography sx={{ fontFamily: "code", fontSize: "0.75rem", color: "text.tertiary" }}>
934
- No usage data available
935
- </Typography>
936
- )}
937
- </Box>
938
- );
939
-
940
- // In expanded layout, add ChartsSection as a new column/section
941
- {expanded && (
942
- <>
943
- {/* Existing columns */}
944
- <Box sx={{ flex: 1, borderLeft: "1px solid", borderColor: "neutral.outlinedBorder", overflow: "auto" }}>
945
- <ChartsSection />
946
- </Box>
947
- </>
948
- )}
949
- ```
950
-
951
- ### Success Criteria
952
-
953
- #### Automated Verification
954
- - [ ] Build succeeds: `cd ui && npm run build`
955
- - [ ] No TypeScript errors
956
-
957
- #### Manual Verification
958
- - [ ] Charts render correctly in expanded agent detail
959
- - [ ] Charts are responsive
960
- - [ ] Dark/light mode styling works
961
-
962
- ---
963
-
964
- ## Phase 6: Task Detail - Cost Totals
965
-
966
- ### Overview
967
- Show cost totals for the task in the task detail panel.
968
-
969
- ### Changes Required
970
-
971
- #### 1. TaskDetailPanel Component
972
- **File**: `ui/src/components/TaskDetailPanel.tsx`
973
- **Changes**: Add cost display
974
-
975
- ```typescript
976
- // Add import
977
- import { useTask, useAgents, useTaskSessionLogs, useTaskUsage } from "../hooks/queries";
978
- import { formatCurrency, formatCompactNumber, formatDuration } from "../lib/utils";
979
-
980
- // Inside component, add usage hook
981
- const { data: taskUsage } = useTaskUsage(taskId);
982
-
983
- // Add to DetailsSection, after Elapsed Time field (around line 243)
984
- {taskUsage && taskUsage.sessionCount > 0 && (
985
- <>
986
- <Divider sx={{ my: 1.5 }} />
987
- <Typography
988
- sx={{
989
- fontFamily: "code",
990
- fontSize: "0.65rem",
991
- color: "text.tertiary",
992
- letterSpacing: "0.05em",
993
- mb: 1,
994
- }}
995
- >
996
- TASK COSTS
997
- </Typography>
998
-
999
- <Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
1000
- <Typography sx={{ fontFamily: "code", fontSize: "0.75rem", color: "text.tertiary" }}>
1001
- Total Cost
1002
- </Typography>
1003
- <Typography sx={{ fontFamily: "code", fontSize: "0.9rem", fontWeight: 600, color: colors.amber }}>
1004
- {formatCurrency(taskUsage.totalCostUsd)}
1005
- </Typography>
1006
- </Box>
1007
-
1008
- <Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
1009
- <Typography sx={{ fontFamily: "code", fontSize: "0.75rem", color: "text.tertiary" }}>
1010
- Total Tokens
1011
- </Typography>
1012
- <Typography sx={{ fontFamily: "code", fontSize: "0.8rem", color: "text.secondary" }}>
1013
- {formatCompactNumber(taskUsage.totalTokens)}
1014
- </Typography>
1015
- </Box>
1016
-
1017
- <Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
1018
- <Typography sx={{ fontFamily: "code", fontSize: "0.75rem", color: "text.tertiary" }}>
1019
- Sessions
1020
- </Typography>
1021
- <Typography sx={{ fontFamily: "code", fontSize: "0.8rem", color: "text.secondary" }}>
1022
- {taskUsage.sessionCount}
1023
- </Typography>
1024
- </Box>
1025
-
1026
- <Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
1027
- <Typography sx={{ fontFamily: "code", fontSize: "0.75rem", color: "text.tertiary" }}>
1028
- Compute Time
1029
- </Typography>
1030
- <Typography sx={{ fontFamily: "code", fontSize: "0.8rem", color: "text.secondary" }}>
1031
- {formatDuration(taskUsage.totalDurationMs)}
1032
- </Typography>
1033
- </Box>
1034
-
1035
- {/* Token breakdown */}
1036
- <Box sx={{ mt: 1, p: 1, bgcolor: "background.level1", borderRadius: 1, border: "1px solid", borderColor: "neutral.outlinedBorder" }}>
1037
- <Typography sx={{ fontFamily: "code", fontSize: "0.6rem", color: "text.tertiary", mb: 0.5 }}>
1038
- TOKEN BREAKDOWN
1039
- </Typography>
1040
- <Box sx={{ display: "flex", flexWrap: "wrap", gap: 1 }}>
1041
- <Chip size="sm" sx={{ fontFamily: "code", fontSize: "0.6rem" }}>
1042
- In: {formatCompactNumber(taskUsage.inputTokens)}
1043
- </Chip>
1044
- <Chip size="sm" sx={{ fontFamily: "code", fontSize: "0.6rem" }}>
1045
- Out: {formatCompactNumber(taskUsage.outputTokens)}
1046
- </Chip>
1047
- {taskUsage.cacheReadTokens > 0 && (
1048
- <Chip size="sm" sx={{ fontFamily: "code", fontSize: "0.6rem" }}>
1049
- Cache R: {formatCompactNumber(taskUsage.cacheReadTokens)}
1050
- </Chip>
1051
- )}
1052
- {taskUsage.cacheWriteTokens > 0 && (
1053
- <Chip size="sm" sx={{ fontFamily: "code", fontSize: "0.6rem" }}>
1054
- Cache W: {formatCompactNumber(taskUsage.cacheWriteTokens)}
1055
- </Chip>
1056
- )}
1057
- </Box>
1058
- </Box>
1059
- </>
1060
- )}
1061
- ```
1062
-
1063
- ### UI Mockup Description
1064
-
1065
- ```
1066
- ┌─────────────────────────────────────┐
1067
- │ Status ● completed │
1068
- │ Agent Worker-1 │
1069
- │ Elapsed Time 12m 34s │
1070
- ├─────────────────────────────────────┤
1071
- │ TASK COSTS │
1072
- │ Total Cost $0.4523 │
1073
- │ Total Tokens 125,432 │
1074
- │ Sessions 3 │
1075
- │ Compute Time 8m 12s │
1076
- │ ┌─────────────────────────────────┐ │
1077
- │ │ TOKEN BREAKDOWN │ │
1078
- │ │ [In: 45K] [Out: 80K] [Cache: 5K]│ │
1079
- │ └─────────────────────────────────┘ │
1080
- └─────────────────────────────────────┘
1081
- ```
1082
-
1083
- ### Success Criteria
1084
-
1085
- #### Automated Verification
1086
- - [ ] Build succeeds: `cd ui && npm run build`
1087
-
1088
- #### Manual Verification
1089
- - [ ] Task detail shows cost totals
1090
- - [ ] Values match actual session costs for the task
1091
- - [ ] Displays gracefully when no costs exist
1092
-
1093
- ---
1094
-
1095
- ## Phase 7: Usage Tab - Dedicated Dashboard
1096
-
1097
- ### Overview
1098
- Create a new "Usage" tab in the dashboard with comprehensive analytics.
1099
-
1100
- ### Changes Required
1101
-
1102
- #### 1. Create UsagePanel Component
1103
- **File**: `ui/src/components/UsagePanel.tsx` (new file)
1104
-
1105
- ```typescript
1106
- import { useState, useMemo } from "react";
1107
- import Box from "@mui/joy/Box";
1108
- import Typography from "@mui/joy/Typography";
1109
- import Select from "@mui/joy/Select";
1110
- import Option from "@mui/joy/Option";
1111
- import Card from "@mui/joy/Card";
1112
- import Table from "@mui/joy/Table";
1113
- import { useColorScheme } from "@mui/joy/styles";
1114
- import { useSessionCosts, useAgents } from "../hooks/queries";
1115
- import { formatCurrency, formatCompactNumber, formatDuration } from "../lib/utils";
1116
- import { CostTrendChart, TokenDistributionChart, ModelUsageChart } from "./UsageCharts";
1117
-
1118
- type TimeRange = "7d" | "30d" | "90d" | "all";
1119
-
1120
- export default function UsagePanel() {
1121
- const [timeRange, setTimeRange] = useState<TimeRange>("30d");
1122
- const { data: allCosts, isLoading } = useSessionCosts({ limit: 5000 });
1123
- const { data: agents } = useAgents();
1124
- const { mode } = useColorScheme();
1125
- const isDark = mode === "dark";
1126
-
1127
- const colors = {
1128
- amber: isDark ? "#F5A623" : "#D48806",
1129
- gold: isDark ? "#D4A574" : "#8B6914",
1130
- blue: "#3B82F6",
1131
- green: "#22C55E",
1132
- hoverBg: isDark ? "rgba(245, 166, 35, 0.08)" : "rgba(212, 136, 6, 0.08)",
1133
- };
1134
-
1135
- // Filter costs by time range
1136
- const filteredCosts = useMemo(() => {
1137
- if (!allCosts) return [];
1138
- if (timeRange === "all") return allCosts;
1139
-
1140
- const days = timeRange === "7d" ? 7 : timeRange === "30d" ? 30 : 90;
1141
- const cutoff = new Date();
1142
- cutoff.setDate(cutoff.getDate() - days);
1143
-
1144
- return allCosts.filter((c) => new Date(c.createdAt) >= cutoff);
1145
- }, [allCosts, timeRange]);
1146
-
1147
- // Aggregate stats
1148
- const stats = useMemo(() => {
1149
- if (!filteredCosts.length) return null;
1150
-
1151
- return {
1152
- totalCost: filteredCosts.reduce((sum, c) => sum + c.totalCostUsd, 0),
1153
- totalTokens: filteredCosts.reduce((sum, c) => sum + c.inputTokens + c.outputTokens, 0),
1154
- totalSessions: filteredCosts.length,
1155
- totalDuration: filteredCosts.reduce((sum, c) => sum + c.durationMs, 0),
1156
- avgCostPerSession: filteredCosts.reduce((sum, c) => sum + c.totalCostUsd, 0) / filteredCosts.length,
1157
- inputTokens: filteredCosts.reduce((sum, c) => sum + c.inputTokens, 0),
1158
- outputTokens: filteredCosts.reduce((sum, c) => sum + c.outputTokens, 0),
1159
- };
1160
- }, [filteredCosts]);
1161
-
1162
- // Per-agent breakdown
1163
- const agentBreakdown = useMemo(() => {
1164
- if (!filteredCosts.length || !agents) return [];
1165
-
1166
- const byAgent = new Map<string, { cost: number; tokens: number; sessions: number }>();
1167
-
1168
- filteredCosts.forEach((c) => {
1169
- const existing = byAgent.get(c.agentId) || { cost: 0, tokens: 0, sessions: 0 };
1170
- byAgent.set(c.agentId, {
1171
- cost: existing.cost + c.totalCostUsd,
1172
- tokens: existing.tokens + c.inputTokens + c.outputTokens,
1173
- sessions: existing.sessions + 1,
1174
- });
1175
- });
1176
-
1177
- return Array.from(byAgent.entries())
1178
- .map(([agentId, data]) => ({
1179
- agentId,
1180
- agentName: agents.find((a) => a.id === agentId)?.name || agentId.slice(0, 8),
1181
- ...data,
1182
- }))
1183
- .sort((a, b) => b.cost - a.cost);
1184
- }, [filteredCosts, agents]);
1185
-
1186
- if (isLoading) {
1187
- return (
1188
- <Box sx={{ p: 3, textAlign: "center" }}>
1189
- <Typography sx={{ fontFamily: "code", color: "text.tertiary" }}>
1190
- Loading usage data...
1191
- </Typography>
1192
- </Box>
1193
- );
1194
- }
1195
-
1196
- return (
1197
- <Box sx={{ height: "100%", overflow: "auto", p: { xs: 1.5, md: 2 } }}>
1198
- {/* Header with time range selector */}
1199
- <Box sx={{ display: "flex", justifyContent: "space-between", alignItems: "center", mb: 3 }}>
1200
- <Typography sx={{ fontFamily: "display", fontSize: "1.25rem", fontWeight: 600, color: colors.amber }}>
1201
- USAGE ANALYTICS
1202
- </Typography>
1203
- <Select
1204
- value={timeRange}
1205
- onChange={(_, value) => value && setTimeRange(value)}
1206
- size="sm"
1207
- sx={{ fontFamily: "code", minWidth: 120 }}
1208
- >
1209
- <Option value="7d">Last 7 days</Option>
1210
- <Option value="30d">Last 30 days</Option>
1211
- <Option value="90d">Last 90 days</Option>
1212
- <Option value="all">All time</Option>
1213
- </Select>
1214
- </Box>
1215
-
1216
- {/* Summary Cards */}
1217
- {stats && (
1218
- <Box sx={{ display: "grid", gridTemplateColumns: { xs: "1fr 1fr", md: "repeat(4, 1fr)" }, gap: 2, mb: 3 }}>
1219
- <SummaryCard
1220
- title="TOTAL COST"
1221
- value={formatCurrency(stats.totalCost)}
1222
- color={colors.amber}
1223
- />
1224
- <SummaryCard
1225
- title="TOTAL TOKENS"
1226
- value={formatCompactNumber(stats.totalTokens)}
1227
- color={colors.blue}
1228
- />
1229
- <SummaryCard
1230
- title="SESSIONS"
1231
- value={stats.totalSessions.toString()}
1232
- color={colors.gold}
1233
- />
1234
- <SummaryCard
1235
- title="COMPUTE TIME"
1236
- value={formatDuration(stats.totalDuration)}
1237
- color={colors.green}
1238
- />
1239
- </Box>
1240
- )}
1241
-
1242
- {/* Charts Row */}
1243
- {filteredCosts.length > 0 && (
1244
- <Box sx={{ display: "grid", gridTemplateColumns: { xs: "1fr", lg: "2fr 1fr" }, gap: 3, mb: 3 }}>
1245
- <Card sx={{ p: 2 }}>
1246
- <CostTrendChart costs={filteredCosts} timeRange={timeRange === "all" ? "90d" : timeRange} />
1247
- </Card>
1248
- <Card sx={{ p: 2 }}>
1249
- <TokenDistributionChart costs={filteredCosts} />
1250
- </Card>
1251
- </Box>
1252
- )}
1253
-
1254
- {/* Model Usage */}
1255
- {filteredCosts.length > 0 && (
1256
- <Card sx={{ p: 2, mb: 3 }}>
1257
- <ModelUsageChart costs={filteredCosts} />
1258
- </Card>
1259
- )}
1260
-
1261
- {/* Agent Breakdown Table */}
1262
- <Card sx={{ p: 2 }}>
1263
- <Typography sx={{ fontFamily: "code", fontSize: "0.7rem", color: "text.tertiary", letterSpacing: "0.05em", mb: 2 }}>
1264
- USAGE BY AGENT
1265
- </Typography>
1266
- <Table size="sm">
1267
- <thead>
1268
- <tr>
1269
- <th>AGENT</th>
1270
- <th style={{ textAlign: "right" }}>COST</th>
1271
- <th style={{ textAlign: "right" }}>TOKENS</th>
1272
- <th style={{ textAlign: "right" }}>SESSIONS</th>
1273
- </tr>
1274
- </thead>
1275
- <tbody>
1276
- {agentBreakdown.map((agent) => (
1277
- <tr key={agent.agentId}>
1278
- <td>
1279
- <Typography sx={{ fontFamily: "code", fontSize: "0.8rem" }}>
1280
- {agent.agentName}
1281
- </Typography>
1282
- </td>
1283
- <td style={{ textAlign: "right" }}>
1284
- <Typography sx={{ fontFamily: "code", fontSize: "0.8rem", color: colors.amber }}>
1285
- {formatCurrency(agent.cost)}
1286
- </Typography>
1287
- </td>
1288
- <td style={{ textAlign: "right" }}>
1289
- <Typography sx={{ fontFamily: "code", fontSize: "0.8rem" }}>
1290
- {formatCompactNumber(agent.tokens)}
1291
- </Typography>
1292
- </td>
1293
- <td style={{ textAlign: "right" }}>
1294
- <Typography sx={{ fontFamily: "code", fontSize: "0.8rem" }}>
1295
- {agent.sessions}
1296
- </Typography>
1297
- </td>
1298
- </tr>
1299
- ))}
1300
- </tbody>
1301
- </Table>
1302
- </Card>
1303
- </Box>
1304
- );
1305
- }
1306
-
1307
- function SummaryCard({ title, value, color }: { title: string; value: string; color: string }) {
1308
- return (
1309
- <Card sx={{ p: 2, textAlign: "center" }}>
1310
- <Typography sx={{ fontFamily: "code", fontSize: "0.6rem", color: "text.tertiary", letterSpacing: "0.05em", mb: 0.5 }}>
1311
- {title}
1312
- </Typography>
1313
- <Typography sx={{ fontFamily: "code", fontSize: "1.5rem", fontWeight: 700, color }}>
1314
- {value}
1315
- </Typography>
1316
- </Card>
1317
- );
1318
- }
1319
- ```
1320
-
1321
- #### 2. Update Dashboard Component
1322
- **File**: `ui/src/components/Dashboard.tsx`
1323
- **Changes**: Add Usage tab
1324
-
1325
- ```typescript
1326
- // Add import (after other component imports)
1327
- import UsagePanel from "./UsagePanel";
1328
-
1329
- // Update activeTab type (line ~104)
1330
- const [activeTab, setActiveTab] = useState<"agents" | "tasks" | "chat" | "services" | "usage">("agents");
1331
-
1332
- // Add Usage tab to TabList (after Services tab, around line 361)
1333
- <Tab value="usage">USAGE</Tab>
1334
-
1335
- // Add Usage TabPanel (after Services TabPanel, around line 529)
1336
- <TabPanel
1337
- value="usage"
1338
- sx={{
1339
- p: 0,
1340
- pt: 2,
1341
- flex: 1,
1342
- minHeight: 0,
1343
- "&[hidden]": {
1344
- display: "none",
1345
- },
1346
- }}
1347
- >
1348
- <UsagePanel />
1349
- </TabPanel>
1350
-
1351
- // Update handleTabChange to handle "usage" (around line 192)
1352
- } else if (tab === "usage") {
1353
- setSelectedAgentId(null);
1354
- setSelectedTaskId(null);
1355
- setSelectedChannelId(null);
1356
- setSelectedThreadId(null);
1357
- setPreFilterAgentId(undefined);
1358
- setAgentStatusFilter("all");
1359
- setTaskStatusFilter("all");
1360
- updateUrl({ tab: "usage", agent: null, task: null, channel: null, agentStatus: null, taskStatus: null, expand: false });
1361
- }
1362
-
1363
- // Update getUrlParams to handle "usage" tab (around line 26)
1364
- tab: params.get("tab") as "agents" | "tasks" | "chat" | "services" | "usage" | null,
1365
- ```
1366
-
1367
- ### UI Mockup Description
1368
-
1369
- ```
1370
- ┌─────────────────────────────────────────────────────────────────────┐
1371
- │ USAGE ANALYTICS [Last 30 days ▼] │
1372
- ├─────────────────────────────────────────────────────────────────────┤
1373
- │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
1374
- │ │ TOTAL │ │ TOTAL │ │ SESSIONS │ │ COMPUTE │ │
1375
- │ │ COST │ │ TOKENS │ │ │ │ TIME │ │
1376
- │ │ $156.78 │ │ 4.2M │ │ 342 │ │ 12h 34m │ │
1377
- │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
1378
- ├─────────────────────────────────────────────────────────────────────┤
1379
- │ ┌─────────────────────────────────────┐ ┌───────────────────────┐ │
1380
- │ │ COST TREND │ │ TOKEN DISTRIBUTION │ │
1381
- │ │ [Area Chart] │ │ [Pie Chart] │ │
1382
- │ │ │ │ │ │
1383
- │ └─────────────────────────────────────┘ └───────────────────────┘ │
1384
- ├─────────────────────────────────────────────────────────────────────┤
1385
- │ ┌─────────────────────────────────────────────────────────────────┐ │
1386
- │ │ COST BY MODEL │ │
1387
- │ │ [Horizontal Bar Chart] │ │
1388
- │ └─────────────────────────────────────────────────────────────────┘ │
1389
- ├─────────────────────────────────────────────────────────────────────┤
1390
- │ USAGE BY AGENT │
1391
- │ ┌─────────────────────────────────────────────────────────────────┐ │
1392
- │ │ AGENT │ COST │ TOKENS │ SESSIONS │ │
1393
- │ │ Worker-1 │ $45.23 │ 1.2M │ 89 │ │
1394
- │ │ Worker-2 │ $34.56 │ 892K │ 67 │ │
1395
- │ │ Lead │ $12.34 │ 345K │ 34 │ │
1396
- │ └─────────────────────────────────────────────────────────────────┘ │
1397
- └─────────────────────────────────────────────────────────────────────┘
1398
- ```
1399
-
1400
- ### Success Criteria
1401
-
1402
- #### Automated Verification
1403
- - [ ] Build succeeds: `cd ui && npm run build`
1404
- - [ ] TypeScript compiles: `cd ui && npm run typecheck`
1405
-
1406
- #### Manual Verification
1407
- - [ ] Usage tab appears in navigation
1408
- - [ ] All charts render correctly
1409
- - [ ] Time range filter works
1410
- - [ ] Agent breakdown table is accurate
1411
- - [ ] Responsive layout works on mobile
1412
-
1413
- ---
1414
-
1415
- ## Quick Verification Reference
1416
-
1417
- Common commands to verify the implementation:
1418
-
1419
- ```bash
1420
- # TypeScript check
1421
- cd ui && npm run typecheck
1422
-
1423
- # Build
1424
- cd ui && npm run build
1425
-
1426
- # Development server
1427
- cd ui && npm run dev
1428
- ```
1429
-
1430
- Key files to check:
1431
- - `ui/src/types/api.ts` - Type definitions
1432
- - `ui/src/lib/api.ts` - API client
1433
- - `ui/src/hooks/queries.ts` - React Query hooks
1434
- - `ui/src/components/StatsBar.tsx` - Home page stats
1435
- - `ui/src/components/AgentsPanel.tsx` - Agents table
1436
- - `ui/src/components/AgentDetailPanel.tsx` - Agent detail
1437
- - `ui/src/components/TaskDetailPanel.tsx` - Task detail
1438
- - `ui/src/components/UsagePanel.tsx` - Usage tab (new)
1439
- - `ui/src/components/UsageCharts.tsx` - Chart components (new)
1440
-
1441
- ---
1442
-
1443
- ## Testing Strategy
1444
-
1445
- ### Unit Tests
1446
- - Test aggregation functions (`aggregateUsage`)
1447
- - Test formatting utilities
1448
- - Test time range filtering logic
1449
-
1450
- ### Integration Tests
1451
- - Mock API responses and verify hook behavior
1452
- - Test chart data transformation
1453
-
1454
- ### Manual Testing
1455
- 1. Create test session costs via API
1456
- 2. Verify all UI components display correct values
1457
- 3. Test with 0 data, small data, large data scenarios
1458
- 4. Test dark/light mode
1459
- 5. Test responsive layouts (mobile, tablet, desktop)
1460
-
1461
- ---
1462
-
1463
- ## Future Enhancements (Out of Scope)
1464
-
1465
- 1. **Backend date range API** - Add `from`/`to` parameters to GET /api/session-costs
1466
- 2. **Export functionality** - CSV/PDF export of usage data
1467
- 3. **Budget alerts** - Set cost thresholds with notifications
1468
- 4. **Cost predictions** - ML-based forecasting
1469
- 5. **Comparison views** - Compare agents or time periods
1470
- 6. **Real-time updates** - WebSocket for live cost streaming
1471
-
1472
- ---
1473
-
1474
- ## References
1475
-
1476
- - Backend PR: [#28 Session Cost Tracking](https://github.com/desplega-ai/agent-swarm/pull/28)
1477
- - API Endpoint: `GET /api/session-costs`
1478
- - UI Library: [MUI Joy](https://mui.com/joy-ui/getting-started/)
1479
- - Charting: [Recharts](https://recharts.org/)