@snoglobe/helios 0.3.6 → 0.4.1

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 (352) hide show
  1. package/dist/__tests__/db-helper.d.ts +8 -0
  2. package/dist/__tests__/db-helper.d.ts.map +1 -0
  3. package/dist/__tests__/db-helper.js +15 -0
  4. package/dist/__tests__/db-helper.js.map +1 -0
  5. package/dist/cli/replay.d.ts.map +1 -1
  6. package/dist/cli/replay.js +2 -3
  7. package/dist/cli/replay.js.map +1 -1
  8. package/dist/cli/sessions.d.ts.map +1 -1
  9. package/dist/cli/sessions.js +2 -1
  10. package/dist/cli/sessions.js.map +1 -1
  11. package/dist/config/project.test.d.ts +2 -0
  12. package/dist/config/project.test.d.ts.map +1 -0
  13. package/dist/config/project.test.js +219 -0
  14. package/dist/config/project.test.js.map +1 -0
  15. package/dist/core/orchestrator-integration.test.d.ts +2 -0
  16. package/dist/core/orchestrator-integration.test.d.ts.map +1 -0
  17. package/dist/core/orchestrator-integration.test.js +674 -0
  18. package/dist/core/orchestrator-integration.test.js.map +1 -0
  19. package/dist/core/orchestrator.d.ts +1 -0
  20. package/dist/core/orchestrator.d.ts.map +1 -1
  21. package/dist/core/orchestrator.js +129 -37
  22. package/dist/core/orchestrator.js.map +1 -1
  23. package/dist/core/orchestrator.test.d.ts +2 -0
  24. package/dist/core/orchestrator.test.d.ts.map +1 -0
  25. package/dist/core/orchestrator.test.js +982 -0
  26. package/dist/core/orchestrator.test.js.map +1 -0
  27. package/dist/core/state-machine.d.ts.map +1 -1
  28. package/dist/core/state-machine.js +3 -0
  29. package/dist/core/state-machine.js.map +1 -1
  30. package/dist/core/state-machine.test.d.ts +2 -0
  31. package/dist/core/state-machine.test.d.ts.map +1 -0
  32. package/dist/core/state-machine.test.js +95 -0
  33. package/dist/core/state-machine.test.js.map +1 -0
  34. package/dist/core/stickies.d.ts.map +1 -1
  35. package/dist/core/stickies.js +3 -0
  36. package/dist/core/stickies.js.map +1 -1
  37. package/dist/core/stickies.test.d.ts +2 -0
  38. package/dist/core/stickies.test.d.ts.map +1 -0
  39. package/dist/core/stickies.test.js +63 -0
  40. package/dist/core/stickies.test.js.map +1 -0
  41. package/dist/core/task-poller.d.ts +2 -1
  42. package/dist/core/task-poller.d.ts.map +1 -1
  43. package/dist/core/task-poller.js +25 -3
  44. package/dist/core/task-poller.js.map +1 -1
  45. package/dist/init.d.ts +2 -0
  46. package/dist/init.d.ts.map +1 -1
  47. package/dist/init.js +42 -7
  48. package/dist/init.js.map +1 -1
  49. package/dist/memory/context-gate.d.ts.map +1 -1
  50. package/dist/memory/context-gate.js +2 -1
  51. package/dist/memory/context-gate.js.map +1 -1
  52. package/dist/memory/context-gate.test.d.ts +2 -0
  53. package/dist/memory/context-gate.test.d.ts.map +1 -0
  54. package/dist/memory/context-gate.test.js +318 -0
  55. package/dist/memory/context-gate.test.js.map +1 -0
  56. package/dist/memory/experiment-tracker.test.d.ts +2 -0
  57. package/dist/memory/experiment-tracker.test.d.ts.map +1 -0
  58. package/dist/memory/experiment-tracker.test.js +325 -0
  59. package/dist/memory/experiment-tracker.test.js.map +1 -0
  60. package/dist/memory/global-memory.d.ts +0 -1
  61. package/dist/memory/global-memory.d.ts.map +1 -1
  62. package/dist/memory/global-memory.js +0 -14
  63. package/dist/memory/global-memory.js.map +1 -1
  64. package/dist/memory/memory-store.d.ts +2 -0
  65. package/dist/memory/memory-store.d.ts.map +1 -1
  66. package/dist/memory/memory-store.js +35 -28
  67. package/dist/memory/memory-store.js.map +1 -1
  68. package/dist/memory/memory-store.test.d.ts +2 -0
  69. package/dist/memory/memory-store.test.d.ts.map +1 -0
  70. package/dist/memory/memory-store.test.js +144 -0
  71. package/dist/memory/memory-store.test.js.map +1 -0
  72. package/dist/memory/token-estimator.test.d.ts +2 -0
  73. package/dist/memory/token-estimator.test.d.ts.map +1 -0
  74. package/dist/memory/token-estimator.test.js +42 -0
  75. package/dist/memory/token-estimator.test.js.map +1 -0
  76. package/dist/metrics/analyzer.test.d.ts +2 -0
  77. package/dist/metrics/analyzer.test.d.ts.map +1 -0
  78. package/dist/metrics/analyzer.test.js +65 -0
  79. package/dist/metrics/analyzer.test.js.map +1 -0
  80. package/dist/metrics/parser.test.d.ts +2 -0
  81. package/dist/metrics/parser.test.d.ts.map +1 -0
  82. package/dist/metrics/parser.test.js +106 -0
  83. package/dist/metrics/parser.test.js.map +1 -0
  84. package/dist/metrics/store.d.ts +2 -0
  85. package/dist/metrics/store.d.ts.map +1 -1
  86. package/dist/metrics/store.js +26 -50
  87. package/dist/metrics/store.js.map +1 -1
  88. package/dist/metrics/store.test.d.ts +2 -0
  89. package/dist/metrics/store.test.d.ts.map +1 -0
  90. package/dist/metrics/store.test.js +148 -0
  91. package/dist/metrics/store.test.js.map +1 -0
  92. package/dist/paths.js +5 -5
  93. package/dist/paths.js.map +1 -1
  94. package/dist/providers/auth/auth-manager.d.ts +1 -0
  95. package/dist/providers/auth/auth-manager.d.ts.map +1 -1
  96. package/dist/providers/auth/auth-manager.js +8 -1
  97. package/dist/providers/auth/auth-manager.js.map +1 -1
  98. package/dist/providers/auth/auth-manager.test.d.ts +2 -0
  99. package/dist/providers/auth/auth-manager.test.d.ts.map +1 -0
  100. package/dist/providers/auth/auth-manager.test.js +364 -0
  101. package/dist/providers/auth/auth-manager.test.js.map +1 -0
  102. package/dist/providers/auth/token-store.js +2 -2
  103. package/dist/providers/auth/token-store.js.map +1 -1
  104. package/dist/providers/claude/history.test.d.ts +2 -0
  105. package/dist/providers/claude/history.test.d.ts.map +1 -0
  106. package/dist/providers/claude/history.test.js +757 -0
  107. package/dist/providers/claude/history.test.js.map +1 -0
  108. package/dist/providers/claude/provider.d.ts +2 -1
  109. package/dist/providers/claude/provider.d.ts.map +1 -1
  110. package/dist/providers/claude/provider.js +87 -25
  111. package/dist/providers/claude/provider.js.map +1 -1
  112. package/dist/providers/claude/provider.test.d.ts +2 -0
  113. package/dist/providers/claude/provider.test.d.ts.map +1 -0
  114. package/dist/providers/claude/provider.test.js +1168 -0
  115. package/dist/providers/claude/provider.test.js.map +1 -0
  116. package/dist/providers/openai/history.test.d.ts +2 -0
  117. package/dist/providers/openai/history.test.d.ts.map +1 -0
  118. package/dist/providers/openai/history.test.js +657 -0
  119. package/dist/providers/openai/history.test.js.map +1 -0
  120. package/dist/providers/openai/provider.d.ts +2 -1
  121. package/dist/providers/openai/provider.d.ts.map +1 -1
  122. package/dist/providers/openai/provider.js +75 -22
  123. package/dist/providers/openai/provider.js.map +1 -1
  124. package/dist/providers/openai/provider.test.d.ts +2 -0
  125. package/dist/providers/openai/provider.test.d.ts.map +1 -0
  126. package/dist/providers/openai/provider.test.js +1093 -0
  127. package/dist/providers/openai/provider.test.js.map +1 -0
  128. package/dist/providers/retry.test.d.ts +2 -0
  129. package/dist/providers/retry.test.d.ts.map +1 -0
  130. package/dist/providers/retry.test.js +194 -0
  131. package/dist/providers/retry.test.js.map +1 -0
  132. package/dist/providers/sse.d.ts +1 -0
  133. package/dist/providers/sse.d.ts.map +1 -1
  134. package/dist/providers/sse.js +29 -20
  135. package/dist/providers/sse.js.map +1 -1
  136. package/dist/providers/sse.test.d.ts +2 -0
  137. package/dist/providers/sse.test.d.ts.map +1 -0
  138. package/dist/providers/sse.test.js +79 -0
  139. package/dist/providers/sse.test.js.map +1 -0
  140. package/dist/providers/types.d.ts +3 -0
  141. package/dist/providers/types.d.ts.map +1 -1
  142. package/dist/providers/types.js.map +1 -1
  143. package/dist/providers/types.test.d.ts +2 -0
  144. package/dist/providers/types.test.d.ts.map +1 -0
  145. package/dist/providers/types.test.js +112 -0
  146. package/dist/providers/types.test.js.map +1 -0
  147. package/dist/remote/connection-pool.d.ts.map +1 -1
  148. package/dist/remote/connection-pool.js +24 -3
  149. package/dist/remote/connection-pool.js.map +1 -1
  150. package/dist/remote/file-sync.d.ts.map +1 -1
  151. package/dist/remote/file-sync.js +4 -3
  152. package/dist/remote/file-sync.js.map +1 -1
  153. package/dist/scheduler/sleep-manager.d.ts.map +1 -1
  154. package/dist/scheduler/sleep-manager.js +9 -2
  155. package/dist/scheduler/sleep-manager.js.map +1 -1
  156. package/dist/scheduler/sleep-manager.test.d.ts +2 -0
  157. package/dist/scheduler/sleep-manager.test.d.ts.map +1 -0
  158. package/dist/scheduler/sleep-manager.test.js +491 -0
  159. package/dist/scheduler/sleep-manager.test.js.map +1 -0
  160. package/dist/scheduler/ssh-batcher.d.ts.map +1 -1
  161. package/dist/scheduler/ssh-batcher.js +6 -4
  162. package/dist/scheduler/ssh-batcher.js.map +1 -1
  163. package/dist/scheduler/ssh-batcher.test.d.ts +2 -0
  164. package/dist/scheduler/ssh-batcher.test.d.ts.map +1 -0
  165. package/dist/scheduler/ssh-batcher.test.js +76 -0
  166. package/dist/scheduler/ssh-batcher.test.js.map +1 -0
  167. package/dist/scheduler/state-store.d.ts.map +1 -1
  168. package/dist/scheduler/state-store.js +1 -2
  169. package/dist/scheduler/state-store.js.map +1 -1
  170. package/dist/scheduler/trigger-scheduler.d.ts.map +1 -1
  171. package/dist/scheduler/trigger-scheduler.js +59 -36
  172. package/dist/scheduler/trigger-scheduler.js.map +1 -1
  173. package/dist/scheduler/trigger-scheduler.test.d.ts +2 -0
  174. package/dist/scheduler/trigger-scheduler.test.d.ts.map +1 -0
  175. package/dist/scheduler/trigger-scheduler.test.js +483 -0
  176. package/dist/scheduler/trigger-scheduler.test.js.map +1 -0
  177. package/dist/scheduler/triggers/file.d.ts.map +1 -1
  178. package/dist/scheduler/triggers/file.js +12 -3
  179. package/dist/scheduler/triggers/file.js.map +1 -1
  180. package/dist/scheduler/triggers/file.test.d.ts +2 -0
  181. package/dist/scheduler/triggers/file.test.d.ts.map +1 -0
  182. package/dist/scheduler/triggers/file.test.js +294 -0
  183. package/dist/scheduler/triggers/file.test.js.map +1 -0
  184. package/dist/scheduler/triggers/metric.d.ts +3 -1
  185. package/dist/scheduler/triggers/metric.d.ts.map +1 -1
  186. package/dist/scheduler/triggers/metric.js +12 -8
  187. package/dist/scheduler/triggers/metric.js.map +1 -1
  188. package/dist/scheduler/triggers/metric.test.d.ts +2 -0
  189. package/dist/scheduler/triggers/metric.test.d.ts.map +1 -0
  190. package/dist/scheduler/triggers/metric.test.js +533 -0
  191. package/dist/scheduler/triggers/metric.test.js.map +1 -0
  192. package/dist/scheduler/triggers/process-exit.d.ts.map +1 -1
  193. package/dist/scheduler/triggers/process-exit.js +2 -1
  194. package/dist/scheduler/triggers/process-exit.js.map +1 -1
  195. package/dist/scheduler/triggers/process-exit.test.d.ts +2 -0
  196. package/dist/scheduler/triggers/process-exit.test.d.ts.map +1 -0
  197. package/dist/scheduler/triggers/process-exit.test.js +118 -0
  198. package/dist/scheduler/triggers/process-exit.test.js.map +1 -0
  199. package/dist/scheduler/triggers/resource.d.ts.map +1 -1
  200. package/dist/scheduler/triggers/resource.js +2 -10
  201. package/dist/scheduler/triggers/resource.js.map +1 -1
  202. package/dist/scheduler/triggers/resource.test.d.ts +2 -0
  203. package/dist/scheduler/triggers/resource.test.d.ts.map +1 -0
  204. package/dist/scheduler/triggers/resource.test.js +225 -0
  205. package/dist/scheduler/triggers/resource.test.js.map +1 -0
  206. package/dist/scheduler/triggers/timer.test.d.ts +2 -0
  207. package/dist/scheduler/triggers/timer.test.d.ts.map +1 -0
  208. package/dist/scheduler/triggers/timer.test.js +56 -0
  209. package/dist/scheduler/triggers/timer.test.js.map +1 -0
  210. package/dist/scheduler/triggers/types.d.ts +4 -2
  211. package/dist/scheduler/triggers/types.d.ts.map +1 -1
  212. package/dist/scheduler/triggers/types.js +0 -1
  213. package/dist/scheduler/triggers/types.js.map +1 -1
  214. package/dist/skills/executor.d.ts.map +1 -1
  215. package/dist/skills/executor.js +13 -15
  216. package/dist/skills/executor.js.map +1 -1
  217. package/dist/store/database.d.ts +5 -0
  218. package/dist/store/database.d.ts.map +1 -1
  219. package/dist/store/database.js +17 -1
  220. package/dist/store/database.js.map +1 -1
  221. package/dist/store/migrations.d.ts.map +1 -1
  222. package/dist/store/migrations.js +7 -0
  223. package/dist/store/migrations.js.map +1 -1
  224. package/dist/store/migrations.test.d.ts +2 -0
  225. package/dist/store/migrations.test.d.ts.map +1 -0
  226. package/dist/store/migrations.test.js +278 -0
  227. package/dist/store/migrations.test.js.map +1 -0
  228. package/dist/store/session-store-edge.test.d.ts +2 -0
  229. package/dist/store/session-store-edge.test.d.ts.map +1 -0
  230. package/dist/store/session-store-edge.test.js +522 -0
  231. package/dist/store/session-store-edge.test.js.map +1 -0
  232. package/dist/store/session-store.d.ts +28 -1
  233. package/dist/store/session-store.d.ts.map +1 -1
  234. package/dist/store/session-store.js +62 -26
  235. package/dist/store/session-store.js.map +1 -1
  236. package/dist/store/session-store.test.d.ts +2 -0
  237. package/dist/store/session-store.test.d.ts.map +1 -0
  238. package/dist/store/session-store.test.js +125 -0
  239. package/dist/store/session-store.test.js.map +1 -0
  240. package/dist/subagent/executor.d.ts +24 -0
  241. package/dist/subagent/executor.d.ts.map +1 -0
  242. package/dist/subagent/executor.js +140 -0
  243. package/dist/subagent/executor.js.map +1 -0
  244. package/dist/subagent/manager.d.ts +20 -0
  245. package/dist/subagent/manager.d.ts.map +1 -0
  246. package/dist/subagent/manager.js +100 -0
  247. package/dist/subagent/manager.js.map +1 -0
  248. package/dist/subagent/scoped-memory.d.ts +28 -0
  249. package/dist/subagent/scoped-memory.d.ts.map +1 -0
  250. package/dist/subagent/scoped-memory.js +122 -0
  251. package/dist/subagent/scoped-memory.js.map +1 -0
  252. package/dist/subagent/types.d.ts +27 -0
  253. package/dist/subagent/types.d.ts.map +1 -0
  254. package/dist/subagent/types.js +2 -0
  255. package/dist/subagent/types.js.map +1 -0
  256. package/dist/tools/file-ops.d.ts.map +1 -1
  257. package/dist/tools/file-ops.js +23 -13
  258. package/dist/tools/file-ops.js.map +1 -1
  259. package/dist/tools/file-ops.test.d.ts +2 -0
  260. package/dist/tools/file-ops.test.d.ts.map +1 -0
  261. package/dist/tools/file-ops.test.js +656 -0
  262. package/dist/tools/file-ops.test.js.map +1 -0
  263. package/dist/tools/memory-tools.test.d.ts +2 -0
  264. package/dist/tools/memory-tools.test.d.ts.map +1 -0
  265. package/dist/tools/memory-tools.test.js +273 -0
  266. package/dist/tools/memory-tools.test.js.map +1 -0
  267. package/dist/tools/sleep.d.ts.map +1 -1
  268. package/dist/tools/sleep.js +19 -7
  269. package/dist/tools/sleep.js.map +1 -1
  270. package/dist/tools/subagent.d.ts +10 -0
  271. package/dist/tools/subagent.d.ts.map +1 -0
  272. package/dist/tools/subagent.js +164 -0
  273. package/dist/tools/subagent.js.map +1 -0
  274. package/dist/tools/task-output.d.ts.map +1 -1
  275. package/dist/tools/task-output.js +13 -2
  276. package/dist/tools/task-output.js.map +1 -1
  277. package/dist/ui/components/input-bar.d.ts +1 -1
  278. package/dist/ui/components/input-bar.d.ts.map +1 -1
  279. package/dist/ui/components/input-bar.js +3 -3
  280. package/dist/ui/components/input-bar.js.map +1 -1
  281. package/dist/ui/components/input-bar.test.d.ts +2 -0
  282. package/dist/ui/components/input-bar.test.d.ts.map +1 -0
  283. package/dist/ui/components/input-bar.test.js +380 -0
  284. package/dist/ui/components/input-bar.test.js.map +1 -0
  285. package/dist/ui/components/key-hint-rule.d.ts +2 -1
  286. package/dist/ui/components/key-hint-rule.d.ts.map +1 -1
  287. package/dist/ui/components/key-hint-rule.js +3 -3
  288. package/dist/ui/components/key-hint-rule.js.map +1 -1
  289. package/dist/ui/components/status-bar.d.ts +1 -1
  290. package/dist/ui/components/status-bar.d.ts.map +1 -1
  291. package/dist/ui/components/status-bar.js +11 -10
  292. package/dist/ui/components/status-bar.js.map +1 -1
  293. package/dist/ui/components/status-bar.test.d.ts +2 -0
  294. package/dist/ui/components/status-bar.test.d.ts.map +1 -0
  295. package/dist/ui/components/status-bar.test.js +206 -0
  296. package/dist/ui/components/status-bar.test.js.map +1 -0
  297. package/dist/ui/format.d.ts +9 -0
  298. package/dist/ui/format.d.ts.map +1 -1
  299. package/dist/ui/format.js +21 -0
  300. package/dist/ui/format.js.map +1 -1
  301. package/dist/ui/format.test.d.ts +2 -0
  302. package/dist/ui/format.test.d.ts.map +1 -0
  303. package/dist/ui/format.test.js +122 -0
  304. package/dist/ui/format.test.js.map +1 -0
  305. package/dist/ui/layout.d.ts.map +1 -1
  306. package/dist/ui/layout.js +123 -16
  307. package/dist/ui/layout.js.map +1 -1
  308. package/dist/ui/markdown.test.d.ts +2 -0
  309. package/dist/ui/markdown.test.d.ts.map +1 -0
  310. package/dist/ui/markdown.test.js +133 -0
  311. package/dist/ui/markdown.test.js.map +1 -0
  312. package/dist/ui/mouse-filter.test.d.ts +2 -0
  313. package/dist/ui/mouse-filter.test.d.ts.map +1 -0
  314. package/dist/ui/mouse-filter.test.js +231 -0
  315. package/dist/ui/mouse-filter.test.js.map +1 -0
  316. package/dist/ui/overlays/metrics-overlay.test.d.ts +2 -0
  317. package/dist/ui/overlays/metrics-overlay.test.d.ts.map +1 -0
  318. package/dist/ui/overlays/metrics-overlay.test.js +248 -0
  319. package/dist/ui/overlays/metrics-overlay.test.js.map +1 -0
  320. package/dist/ui/overlays/task-overlay.test.d.ts +2 -0
  321. package/dist/ui/overlays/task-overlay.test.d.ts.map +1 -0
  322. package/dist/ui/overlays/task-overlay.test.js +238 -0
  323. package/dist/ui/overlays/task-overlay.test.js.map +1 -0
  324. package/dist/ui/panels/conversation.d.ts.map +1 -1
  325. package/dist/ui/panels/conversation.js +60 -5
  326. package/dist/ui/panels/conversation.js.map +1 -1
  327. package/dist/ui/panels/conversation.test.d.ts +2 -0
  328. package/dist/ui/panels/conversation.test.d.ts.map +1 -0
  329. package/dist/ui/panels/conversation.test.js +381 -0
  330. package/dist/ui/panels/conversation.test.js.map +1 -0
  331. package/dist/ui/panels/metrics-dashboard.test.d.ts +2 -0
  332. package/dist/ui/panels/metrics-dashboard.test.d.ts.map +1 -0
  333. package/dist/ui/panels/metrics-dashboard.test.js +191 -0
  334. package/dist/ui/panels/metrics-dashboard.test.js.map +1 -0
  335. package/dist/ui/panels/sleep-panel.test.d.ts +2 -0
  336. package/dist/ui/panels/sleep-panel.test.d.ts.map +1 -0
  337. package/dist/ui/panels/sleep-panel.test.js +376 -0
  338. package/dist/ui/panels/sleep-panel.test.js.map +1 -0
  339. package/dist/ui/panels/task-list.d.ts.map +1 -1
  340. package/dist/ui/panels/task-list.js +5 -6
  341. package/dist/ui/panels/task-list.js.map +1 -1
  342. package/dist/ui/panels/task-list.test.d.ts +2 -0
  343. package/dist/ui/panels/task-list.test.d.ts.map +1 -0
  344. package/dist/ui/panels/task-list.test.js +210 -0
  345. package/dist/ui/panels/task-list.test.js.map +1 -0
  346. package/dist/ui/theme.test.d.ts +2 -0
  347. package/dist/ui/theme.test.d.ts.map +1 -0
  348. package/dist/ui/theme.test.js +159 -0
  349. package/dist/ui/theme.test.js.map +1 -0
  350. package/dist/ui/types.d.ts +3 -0
  351. package/dist/ui/types.d.ts.map +1 -1
  352. package/package.json +6 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.test.d.ts","sourceRoot":"","sources":["../../src/metrics/store.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,148 @@
1
+ import { describe, it, expect, beforeEach, vi } from "vitest";
2
+ import { createTestDb } from "../__tests__/db-helper.js";
3
+ const mockDb = { current: createTestDb() };
4
+ vi.mock("../store/database.js", () => {
5
+ const getDb = () => mockDb.current;
6
+ class StmtCache {
7
+ cache = new Map();
8
+ stmt(sql) {
9
+ let s = this.cache.get(sql);
10
+ if (!s) {
11
+ s = getDb().prepare(sql);
12
+ this.cache.set(sql, s);
13
+ }
14
+ return s;
15
+ }
16
+ }
17
+ return { getDb, StmtCache, getHeliosDir: () => "/tmp/helios-test" };
18
+ });
19
+ const { MetricStore } = await import("./store.js");
20
+ describe("MetricStore", () => {
21
+ beforeEach(() => {
22
+ mockDb.current = createTestDb();
23
+ });
24
+ it("inserts and retrieves a metric", () => {
25
+ const store = new MetricStore("agent1");
26
+ store.insert("task1", "local", {
27
+ metricName: "loss",
28
+ value: 0.5,
29
+ timestamp: 1000,
30
+ });
31
+ const latest = store.getLatest("task1", "loss");
32
+ expect(latest).not.toBeNull();
33
+ expect(latest.value).toBe(0.5);
34
+ });
35
+ it("returns null for missing metric", () => {
36
+ const store = new MetricStore("agent1");
37
+ expect(store.getLatest("task1", "nonexistent")).toBeNull();
38
+ });
39
+ it("getLatest returns most recent value", () => {
40
+ const store = new MetricStore("agent1");
41
+ store.insert("task1", "local", { metricName: "loss", value: 1.0, timestamp: 1000 });
42
+ store.insert("task1", "local", { metricName: "loss", value: 0.5, timestamp: 2000 });
43
+ store.insert("task1", "local", { metricName: "loss", value: 0.3, timestamp: 3000 });
44
+ const latest = store.getLatest("task1", "loss");
45
+ expect(latest.value).toBe(0.3);
46
+ });
47
+ it("getSeries returns points in chronological order", () => {
48
+ const store = new MetricStore("agent1");
49
+ for (let i = 0; i < 5; i++) {
50
+ store.insert("task1", "local", { metricName: "loss", value: 5 - i, timestamp: 1000 + i * 100 });
51
+ }
52
+ const series = store.getSeries("task1", "loss");
53
+ expect(series).toHaveLength(5);
54
+ expect(series[0].value).toBe(5); // earliest
55
+ expect(series[4].value).toBe(1); // latest
56
+ });
57
+ it("getSeries respects limit", () => {
58
+ const store = new MetricStore("agent1");
59
+ for (let i = 0; i < 10; i++) {
60
+ store.insert("task1", "local", { metricName: "loss", value: i, timestamp: 1000 + i });
61
+ }
62
+ const series = store.getSeries("task1", "loss", 3);
63
+ expect(series).toHaveLength(3);
64
+ // Should be the most recent 3, in ASC order
65
+ expect(series[0].value).toBe(7);
66
+ expect(series[2].value).toBe(9);
67
+ });
68
+ it("insertBatch inserts all points atomically", () => {
69
+ const store = new MetricStore("agent1");
70
+ const points = Array.from({ length: 100 }, (_, i) => ({
71
+ metricName: "loss",
72
+ value: Math.random(),
73
+ timestamp: 1000 + i,
74
+ }));
75
+ store.insertBatch("task1", "local", points);
76
+ const series = store.getSeries("task1", "loss", 200);
77
+ expect(series).toHaveLength(100);
78
+ });
79
+ it("getMetricNames lists distinct names", () => {
80
+ const store = new MetricStore("agent1");
81
+ store.insert("task1", "local", { metricName: "loss", value: 1, timestamp: 1000 });
82
+ store.insert("task1", "local", { metricName: "acc", value: 0.9, timestamp: 1000 });
83
+ store.insert("task1", "local", { metricName: "loss", value: 0.5, timestamp: 2000 });
84
+ const names = store.getMetricNames("task1");
85
+ expect(names.sort()).toEqual(["acc", "loss"]);
86
+ });
87
+ it("getLatestAll returns latest value per metric", () => {
88
+ const store = new MetricStore("agent1");
89
+ store.insert("task1", "local", { metricName: "loss", value: 1.0, timestamp: 1000 });
90
+ store.insert("task1", "local", { metricName: "loss", value: 0.5, timestamp: 2000 });
91
+ store.insert("task1", "local", { metricName: "acc", value: 0.95, timestamp: 2000 });
92
+ const all = store.getLatestAll("task1");
93
+ expect(all.loss).toBe(0.5);
94
+ expect(all.acc).toBe(0.95);
95
+ });
96
+ it("isolates by agentId", () => {
97
+ const store1 = new MetricStore("agent1");
98
+ const store2 = new MetricStore("agent2");
99
+ store1.insert("task1", "local", { metricName: "loss", value: 1, timestamp: 1000 });
100
+ expect(store2.getLatest("task1", "loss")).toBeNull();
101
+ });
102
+ it("clearTask removes task metrics", () => {
103
+ const store = new MetricStore("agent1");
104
+ store.insert("task1", "local", { metricName: "loss", value: 1, timestamp: 1000 });
105
+ store.insert("task2", "local", { metricName: "loss", value: 2, timestamp: 1000 });
106
+ store.clearTask("task1");
107
+ expect(store.getLatest("task1", "loss")).toBeNull();
108
+ expect(store.getLatest("task2", "loss")).not.toBeNull();
109
+ });
110
+ it("clear removes all agent metrics", () => {
111
+ const store = new MetricStore("agent1");
112
+ store.insert("task1", "local", { metricName: "loss", value: 1, timestamp: 1000 });
113
+ store.insert("task2", "local", { metricName: "acc", value: 0.9, timestamp: 1000 });
114
+ const removed = store.clear();
115
+ expect(removed).toBe(2);
116
+ expect(store.getMetricNames("task1")).toEqual([]);
117
+ });
118
+ it("getAllSeries groups by metric name", () => {
119
+ const store = new MetricStore("agent1");
120
+ store.insert("task1", "local", { metricName: "loss", value: 1, timestamp: 1000 });
121
+ store.insert("task1", "local", { metricName: "loss", value: 0.5, timestamp: 2000 });
122
+ store.insert("task1", "local", { metricName: "acc", value: 0.9, timestamp: 1000 });
123
+ const all = store.getAllSeries(50);
124
+ expect(all.get("loss")).toEqual([1, 0.5]);
125
+ expect(all.get("acc")).toEqual([0.9]);
126
+ });
127
+ it("getTaskSummary returns min/max/latest/count", () => {
128
+ const store = new MetricStore("agent1");
129
+ store.insert("task1", "local", { metricName: "loss", value: 3, timestamp: 1000 });
130
+ store.insert("task1", "local", { metricName: "loss", value: 1, timestamp: 2000 });
131
+ store.insert("task1", "local", { metricName: "loss", value: 2, timestamp: 3000 });
132
+ const summary = store.getTaskSummary("task1");
133
+ expect(summary.loss).toMatchObject({
134
+ latest: 2,
135
+ min: 1,
136
+ max: 3,
137
+ count: 3,
138
+ });
139
+ });
140
+ it("getLatestPerMetric aggregates across tasks", () => {
141
+ const store = new MetricStore("agent1");
142
+ store.insert("task1", "local", { metricName: "loss", value: 0.5, timestamp: 1000 });
143
+ store.insert("task2", "local", { metricName: "loss", value: 0.3, timestamp: 2000 });
144
+ const latest = store.getLatestPerMetric();
145
+ expect(latest.loss).toBe(0.3);
146
+ });
147
+ });
148
+ //# sourceMappingURL=store.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.test.js","sourceRoot":"","sources":["../../src/metrics/store.test.ts"],"names":[],"mappings":"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"}
package/dist/paths.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { join } from "node:path";
2
2
  import { homedir } from "node:os";
3
- import { mkdirSync, appendFileSync } from "node:fs";
3
+ import { mkdirSync, openSync, writeSync } from "node:fs";
4
4
  /** Root config/data directory. Override with HELIOS_HOME env var. */
5
5
  export const HELIOS_DIR = process.env.HELIOS_HOME ?? join(homedir(), ".helios");
6
6
  /** Get the agent ID from environment. */
@@ -13,19 +13,19 @@ export const WEB_SEARCH_TOOL = "web_search";
13
13
  export function isDebug() {
14
14
  return process.env.HELIOS_DEBUG === "1";
15
15
  }
16
- let debugLogPath = null;
16
+ let debugLogFd = null;
17
17
  /** Log a debug message to ~/.helios/debug.log (only when debug mode is enabled). */
18
18
  export function debugLog(label, ...args) {
19
19
  if (!isDebug())
20
20
  return;
21
21
  try {
22
- if (!debugLogPath) {
22
+ if (debugLogFd === null) {
23
23
  mkdirSync(HELIOS_DIR, { recursive: true });
24
- debugLogPath = join(HELIOS_DIR, "debug.log");
24
+ debugLogFd = openSync(join(HELIOS_DIR, "debug.log"), "a");
25
25
  }
26
26
  const timestamp = new Date().toISOString().slice(11, 23);
27
27
  const line = `[${timestamp}] [${label}] ${args.map(a => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}\n`;
28
- appendFileSync(debugLogPath, line);
28
+ writeSync(debugLogFd, line);
29
29
  }
30
30
  catch {
31
31
  // Don't let debug logging crash the app
package/dist/paths.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEpD,qEAAqE;AACrE,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;AAEhF,yCAAyC;AACzC,MAAM,UAAU,UAAU;IACxB,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC;AAC1C,CAAC;AAED,uFAAuF;AACvF,MAAM,CAAC,MAAM,eAAe,GAAG,YAAY,CAAC;AAE5C,6EAA6E;AAC7E,MAAM,UAAU,OAAO;IACrB,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,KAAK,GAAG,CAAC;AAC1C,CAAC;AAED,IAAI,YAAY,GAAkB,IAAI,CAAC;AAEvC,oFAAoF;AACpF,MAAM,UAAU,QAAQ,CAAC,KAAa,EAAE,GAAG,IAAe;IACxD,IAAI,CAAC,OAAO,EAAE;QAAE,OAAO;IACvB,IAAI,CAAC;QACH,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC3C,YAAY,GAAG,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;QAC/C,CAAC;QACD,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACzD,MAAM,IAAI,GAAG,IAAI,SAAS,MAAM,KAAK,KAAK,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;QACrH,cAAc,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,wCAAwC;IAC1C,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzD,qEAAqE;AACrE,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;AAEhF,yCAAyC;AACzC,MAAM,UAAU,UAAU;IACxB,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE,CAAC;AAC1C,CAAC;AAED,uFAAuF;AACvF,MAAM,CAAC,MAAM,eAAe,GAAG,YAAY,CAAC;AAE5C,6EAA6E;AAC7E,MAAM,UAAU,OAAO;IACrB,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,KAAK,GAAG,CAAC;AAC1C,CAAC;AAED,IAAI,UAAU,GAAkB,IAAI,CAAC;AAErC,oFAAoF;AACpF,MAAM,UAAU,QAAQ,CAAC,KAAa,EAAE,GAAG,IAAe;IACxD,IAAI,CAAC,OAAO,EAAE;QAAE,OAAO;IACvB,IAAI,CAAC;QACH,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;YACxB,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC3C,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,EAAE,GAAG,CAAC,CAAC;QAC5D,CAAC;QACD,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACzD,MAAM,IAAI,GAAG,IAAI,SAAS,MAAM,KAAK,KAAK,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;QACrH,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,wCAAwC;IAC1C,CAAC;AACH,CAAC"}
@@ -2,6 +2,7 @@ import { TokenStore } from "./token-store.js";
2
2
  import type { AuthCredentials } from "../types.js";
3
3
  export declare class AuthManager {
4
4
  readonly tokenStore: TokenStore;
5
+ private refreshPromises;
5
6
  private refreshHandlers;
6
7
  constructor();
7
8
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"auth-manager.d.ts","sourceRoot":"","sources":["../../../src/providers/auth/auth-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAc,MAAM,aAAa,CAAC;AAE/D,qBAAa,WAAW;IACtB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,OAAO,CAAC,eAAe,CAOnB;;IAMJ;;;OAGG;IACH,sBAAsB,CACpB,QAAQ,EAAE,QAAQ,GAAG,QAAQ,EAC7B,OAAO,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC;QACzC,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC,GACD,IAAI;IAID,cAAc,CAClB,QAAQ,EAAE,QAAQ,GAAG,QAAQ,GAC5B,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAgB5B,SAAS,CACb,QAAQ,EAAE,QAAQ,GAAG,QAAQ,EAC7B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC;IAQV,cAAc,CAClB,QAAQ,EAAE,QAAQ,GAAG,QAAQ,EAC7B,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;IAUhB,eAAe,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO;YASzC,OAAO;CA0BtB"}
1
+ {"version":3,"file":"auth-manager.d.ts","sourceRoot":"","sources":["../../../src/providers/auth/auth-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAc,MAAM,aAAa,CAAC;AAE/D,qBAAa,WAAW;IACtB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,OAAO,CAAC,eAAe,CAA+C;IACtE,OAAO,CAAC,eAAe,CAOnB;;IAMJ;;;OAGG;IACH,sBAAsB,CACpB,QAAQ,EAAE,QAAQ,GAAG,QAAQ,EAC7B,OAAO,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC;QACzC,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC,GACD,IAAI;IAID,cAAc,CAClB,QAAQ,EAAE,QAAQ,GAAG,QAAQ,GAC5B,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAyB5B,SAAS,CACb,QAAQ,EAAE,QAAQ,GAAG,QAAQ,EAC7B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC;IAQV,cAAc,CAClB,QAAQ,EAAE,QAAQ,GAAG,QAAQ,EAC7B,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;IAUhB,eAAe,CAAC,QAAQ,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO;YASzC,OAAO;CA0BtB"}
@@ -1,6 +1,7 @@
1
1
  import { TokenStore } from "./token-store.js";
2
2
  export class AuthManager {
3
3
  tokenStore;
4
+ refreshPromises = new Map();
4
5
  refreshHandlers = new Map();
5
6
  constructor() {
6
7
  this.tokenStore = new TokenStore();
@@ -20,7 +21,13 @@ export class AuthManager {
20
21
  if (creds.method === "oauth" &&
21
22
  this.tokenStore.needsRefresh(provider) &&
22
23
  creds.refreshToken) {
23
- return this.refresh(provider, creds);
24
+ // Deduplicate concurrent refresh attempts per provider
25
+ if (!this.refreshPromises.has(provider)) {
26
+ this.refreshPromises.set(provider, this.refresh(provider, creds).finally(() => {
27
+ this.refreshPromises.delete(provider);
28
+ }));
29
+ }
30
+ return this.refreshPromises.get(provider);
24
31
  }
25
32
  return creds;
26
33
  }
@@ -1 +1 @@
1
- {"version":3,"file":"auth-manager.js","sourceRoot":"","sources":["../../../src/providers/auth/auth-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG9C,MAAM,OAAO,WAAW;IACb,UAAU,CAAa;IACxB,eAAe,GAAG,IAAI,GAAG,EAO9B,CAAC;IAEJ;QACE,IAAI,CAAC,UAAU,GAAG,IAAI,UAAU,EAAE,CAAC;IACrC,CAAC;IAED;;;OAGG;IACH,sBAAsB,CACpB,QAA6B,EAC7B,OAIE;QAEF,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,cAAc,CAClB,QAA6B;QAE7B,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QAExB,wCAAwC;QACxC,IACE,KAAK,CAAC,MAAM,KAAK,OAAO;YACxB,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,QAAQ,CAAC;YACtC,KAAK,CAAC,YAAY,EAClB,CAAC;YACD,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QACvC,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,SAAS,CACb,QAA6B,EAC7B,MAAc;QAEd,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE;YAC5B,MAAM,EAAE,SAAS;YACjB,QAAQ;YACR,MAAM;SACP,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,cAAc,CAClB,QAA6B,EAC7B,WAAmB,EACnB,YAAoB,EACpB,SAAiB;QAEjB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE;YAC5B,MAAM,EAAE,OAAO;YACf,QAAQ;YACR,WAAW;YACX,YAAY;YACZ,SAAS;SACV,CAAC,CAAC;IACL,CAAC;IAED,eAAe,CAAC,QAA6B;QAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC5C,IAAI,CAAC,KAAK;YAAE,OAAO,KAAK,CAAC;QACzB,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC;QACtD,sDAAsD;QACtD,2CAA2C;QAC3C,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC;IACrD,CAAC;IAEO,KAAK,CAAC,OAAO,CACnB,QAA6B,EAC7B,KAAsB;QAEtB,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACnD,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC;YACpC,+DAA+D;YAC/D,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YACjD,MAAM,OAAO,GAAoB;gBAC/B,MAAM,EAAE,OAAO;gBACf,QAAQ;gBACR,WAAW,EAAE,MAAM,CAAC,WAAW;gBAC/B,YAAY,EAAE,MAAM,CAAC,YAAY;gBACjC,SAAS,EAAE,MAAM,CAAC,SAAS;aAC5B,CAAC;YACF,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACvC,OAAO,OAAO,CAAC;QACjB,CAAC;QAAC,MAAM,CAAC;YACP,sCAAsC;YACtC,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF"}
1
+ {"version":3,"file":"auth-manager.js","sourceRoot":"","sources":["../../../src/providers/auth/auth-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=auth-manager.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-manager.test.d.ts","sourceRoot":"","sources":["../../../src/providers/auth/auth-manager.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,364 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+ // ---------------------------------------------------------------------------
3
+ // Mock TokenStore — avoid filesystem access
4
+ // ---------------------------------------------------------------------------
5
+ function createMockTokenStore() {
6
+ const store = new Map();
7
+ return {
8
+ get: vi.fn((provider) => store.get(provider) ?? null),
9
+ set: vi.fn((provider, creds) => store.set(provider, creds)),
10
+ clear: vi.fn((provider) => store.delete(provider)),
11
+ isExpired: vi.fn().mockReturnValue(false),
12
+ needsRefresh: vi.fn().mockReturnValue(false),
13
+ _store: store, // escape hatch for assertions
14
+ };
15
+ }
16
+ let latestMockTokenStore;
17
+ vi.mock("./token-store.js", () => ({
18
+ TokenStore: class {
19
+ constructor() {
20
+ latestMockTokenStore = createMockTokenStore();
21
+ Object.assign(this, latestMockTokenStore);
22
+ }
23
+ },
24
+ }));
25
+ const { AuthManager } = await import("./auth-manager.js");
26
+ describe("AuthManager", () => {
27
+ let manager;
28
+ let tokenStore;
29
+ beforeEach(() => {
30
+ manager = new AuthManager();
31
+ tokenStore = latestMockTokenStore;
32
+ });
33
+ // -----------------------------------------------------------------------
34
+ // getCredentials
35
+ // -----------------------------------------------------------------------
36
+ describe("getCredentials", () => {
37
+ it("returns null when no creds stored", async () => {
38
+ const creds = await manager.getCredentials("claude");
39
+ expect(creds).toBeNull();
40
+ });
41
+ it("returns stored API key", async () => {
42
+ const apiCreds = {
43
+ method: "api_key",
44
+ provider: "claude",
45
+ apiKey: "sk-ant-test123",
46
+ };
47
+ tokenStore.set("claude", apiCreds);
48
+ const creds = await manager.getCredentials("claude");
49
+ expect(creds).toEqual(apiCreds);
50
+ });
51
+ it("returns stored OAuth tokens", async () => {
52
+ const oauthCreds = {
53
+ method: "oauth",
54
+ provider: "openai",
55
+ accessToken: "access-123",
56
+ refreshToken: "refresh-456",
57
+ expiresAt: Date.now() + 3600_000,
58
+ };
59
+ tokenStore.set("openai", oauthCreds);
60
+ const creds = await manager.getCredentials("openai");
61
+ expect(creds).toEqual(oauthCreds);
62
+ });
63
+ it("auto-refreshes when needsRefresh returns true", async () => {
64
+ const oauthCreds = {
65
+ method: "oauth",
66
+ provider: "claude",
67
+ accessToken: "old-access",
68
+ refreshToken: "refresh-abc",
69
+ expiresAt: Date.now() - 1000,
70
+ };
71
+ tokenStore.set("claude", oauthCreds);
72
+ tokenStore.needsRefresh.mockReturnValue(true);
73
+ const refreshedTokens = {
74
+ accessToken: "new-access",
75
+ refreshToken: "new-refresh",
76
+ expiresAt: Date.now() + 3600_000,
77
+ };
78
+ manager.registerRefreshHandler("claude", vi.fn().mockResolvedValue(refreshedTokens));
79
+ const creds = await manager.getCredentials("claude");
80
+ expect(creds.accessToken).toBe("new-access");
81
+ expect(creds.refreshToken).toBe("new-refresh");
82
+ });
83
+ it("deduplicates concurrent refresh calls", async () => {
84
+ const oauthCreds = {
85
+ method: "oauth",
86
+ provider: "claude",
87
+ accessToken: "old",
88
+ refreshToken: "refresh-tok",
89
+ expiresAt: Date.now() - 1000,
90
+ };
91
+ tokenStore.set("claude", oauthCreds);
92
+ tokenStore.needsRefresh.mockReturnValue(true);
93
+ let resolveRefresh;
94
+ const handler = vi.fn(() => new Promise((resolve) => {
95
+ resolveRefresh = resolve;
96
+ }));
97
+ manager.registerRefreshHandler("claude", handler);
98
+ // Fire two concurrent getCredentials calls
99
+ const p1 = manager.getCredentials("claude");
100
+ const p2 = manager.getCredentials("claude");
101
+ resolveRefresh({
102
+ accessToken: "refreshed",
103
+ refreshToken: "new-rt",
104
+ expiresAt: Date.now() + 3600_000,
105
+ });
106
+ const [c1, c2] = await Promise.all([p1, p2]);
107
+ // Handler should only be called once
108
+ expect(handler).toHaveBeenCalledTimes(1);
109
+ expect(c1.accessToken).toBe("refreshed");
110
+ expect(c2.accessToken).toBe("refreshed");
111
+ });
112
+ it("deduplication clears after completion", async () => {
113
+ const oauthCreds = {
114
+ method: "oauth",
115
+ provider: "claude",
116
+ accessToken: "old",
117
+ refreshToken: "rt",
118
+ expiresAt: Date.now() - 1000,
119
+ };
120
+ tokenStore.set("claude", oauthCreds);
121
+ tokenStore.needsRefresh.mockReturnValue(true);
122
+ const handler = vi.fn().mockResolvedValue({
123
+ accessToken: "new1",
124
+ refreshToken: "rt1",
125
+ expiresAt: Date.now() + 3600_000,
126
+ });
127
+ manager.registerRefreshHandler("claude", handler);
128
+ await manager.getCredentials("claude");
129
+ expect(handler).toHaveBeenCalledTimes(1);
130
+ // Second call after first completes should trigger refresh again
131
+ handler.mockResolvedValue({
132
+ accessToken: "new2",
133
+ refreshToken: "rt2",
134
+ expiresAt: Date.now() + 3600_000,
135
+ });
136
+ await manager.getCredentials("claude");
137
+ expect(handler).toHaveBeenCalledTimes(2);
138
+ });
139
+ it("returns stale creds if no refresh handler registered", async () => {
140
+ const oauthCreds = {
141
+ method: "oauth",
142
+ provider: "claude",
143
+ accessToken: "stale",
144
+ refreshToken: "rt",
145
+ expiresAt: Date.now() - 1000,
146
+ };
147
+ tokenStore.set("claude", oauthCreds);
148
+ tokenStore.needsRefresh.mockReturnValue(true);
149
+ // No handler registered
150
+ const creds = await manager.getCredentials("claude");
151
+ expect(creds.accessToken).toBe("stale");
152
+ });
153
+ it("returns stale creds if refresh fails", async () => {
154
+ const oauthCreds = {
155
+ method: "oauth",
156
+ provider: "claude",
157
+ accessToken: "stale-token",
158
+ refreshToken: "rt",
159
+ expiresAt: Date.now() - 1000,
160
+ };
161
+ tokenStore.set("claude", oauthCreds);
162
+ tokenStore.needsRefresh.mockReturnValue(true);
163
+ manager.registerRefreshHandler("claude", vi.fn().mockRejectedValue(new Error("refresh failed")));
164
+ const creds = await manager.getCredentials("claude");
165
+ expect(creds.accessToken).toBe("stale-token");
166
+ });
167
+ });
168
+ // -----------------------------------------------------------------------
169
+ // setApiKey
170
+ // -----------------------------------------------------------------------
171
+ describe("setApiKey", () => {
172
+ it("stores API key credentials", async () => {
173
+ await manager.setApiKey("claude", "sk-ant-key-123");
174
+ expect(tokenStore.set).toHaveBeenCalledWith("claude", {
175
+ method: "api_key",
176
+ provider: "claude",
177
+ apiKey: "sk-ant-key-123",
178
+ });
179
+ });
180
+ it("stores for openai provider", async () => {
181
+ await manager.setApiKey("openai", "sk-openai-key");
182
+ expect(tokenStore.set).toHaveBeenCalledWith("openai", {
183
+ method: "api_key",
184
+ provider: "openai",
185
+ apiKey: "sk-openai-key",
186
+ });
187
+ });
188
+ });
189
+ // -----------------------------------------------------------------------
190
+ // setOAuthTokens
191
+ // -----------------------------------------------------------------------
192
+ describe("setOAuthTokens", () => {
193
+ it("stores OAuth credentials", async () => {
194
+ const expiresAt = Date.now() + 3600_000;
195
+ await manager.setOAuthTokens("claude", "access-tok", "refresh-tok", expiresAt);
196
+ expect(tokenStore.set).toHaveBeenCalledWith("claude", {
197
+ method: "oauth",
198
+ provider: "claude",
199
+ accessToken: "access-tok",
200
+ refreshToken: "refresh-tok",
201
+ expiresAt,
202
+ });
203
+ });
204
+ });
205
+ // -----------------------------------------------------------------------
206
+ // isAuthenticated
207
+ // -----------------------------------------------------------------------
208
+ describe("isAuthenticated", () => {
209
+ it("returns false when no creds", () => {
210
+ expect(manager.isAuthenticated("claude")).toBe(false);
211
+ });
212
+ it("returns true with API key", () => {
213
+ tokenStore.set("claude", {
214
+ method: "api_key",
215
+ provider: "claude",
216
+ apiKey: "sk-ant-test",
217
+ });
218
+ expect(manager.isAuthenticated("claude")).toBe(true);
219
+ });
220
+ it("returns true with access token", () => {
221
+ tokenStore.set("openai", {
222
+ method: "oauth",
223
+ provider: "openai",
224
+ accessToken: "access-tok",
225
+ refreshToken: "rt",
226
+ expiresAt: Date.now() + 3600_000,
227
+ });
228
+ expect(manager.isAuthenticated("openai")).toBe(true);
229
+ });
230
+ it("returns true with only refresh token (can try refresh)", () => {
231
+ tokenStore.set("claude", {
232
+ method: "oauth",
233
+ provider: "claude",
234
+ refreshToken: "refresh-only",
235
+ expiresAt: Date.now() - 1000,
236
+ });
237
+ expect(manager.isAuthenticated("claude")).toBe(true);
238
+ });
239
+ it("returns false for API key with empty string", () => {
240
+ tokenStore.set("claude", {
241
+ method: "api_key",
242
+ provider: "claude",
243
+ apiKey: "",
244
+ });
245
+ expect(manager.isAuthenticated("claude")).toBe(false);
246
+ });
247
+ });
248
+ // -----------------------------------------------------------------------
249
+ // registerRefreshHandler + refresh
250
+ // -----------------------------------------------------------------------
251
+ describe("registerRefreshHandler / refresh", () => {
252
+ it("stores handler", () => {
253
+ const handler = vi.fn();
254
+ manager.registerRefreshHandler("claude", handler);
255
+ // No direct getter, but we can trigger it via getCredentials
256
+ // Just verify no error occurs
257
+ expect(() => manager.registerRefreshHandler("openai", vi.fn())).not.toThrow();
258
+ });
259
+ it("calls registered handler on refresh", async () => {
260
+ const oauthCreds = {
261
+ method: "oauth",
262
+ provider: "claude",
263
+ accessToken: "old",
264
+ refreshToken: "rt-123",
265
+ expiresAt: Date.now() - 1000,
266
+ };
267
+ tokenStore.set("claude", oauthCreds);
268
+ tokenStore.needsRefresh.mockReturnValue(true);
269
+ const handler = vi.fn().mockResolvedValue({
270
+ accessToken: "new-at",
271
+ refreshToken: "new-rt",
272
+ expiresAt: Date.now() + 3600_000,
273
+ });
274
+ manager.registerRefreshHandler("claude", handler);
275
+ await manager.getCredentials("claude");
276
+ expect(handler).toHaveBeenCalledWith("rt-123");
277
+ });
278
+ it("updates token store on successful refresh", async () => {
279
+ const oauthCreds = {
280
+ method: "oauth",
281
+ provider: "openai",
282
+ accessToken: "old",
283
+ refreshToken: "rt-abc",
284
+ expiresAt: Date.now() - 1000,
285
+ };
286
+ tokenStore.set("openai", oauthCreds);
287
+ tokenStore.needsRefresh.mockReturnValue(true);
288
+ const newExpires = Date.now() + 7200_000;
289
+ manager.registerRefreshHandler("openai", vi.fn().mockResolvedValue({
290
+ accessToken: "fresh-at",
291
+ refreshToken: "fresh-rt",
292
+ expiresAt: newExpires,
293
+ }));
294
+ await manager.getCredentials("openai");
295
+ // tokenStore.set should have been called with the new creds
296
+ expect(tokenStore.set).toHaveBeenCalledWith("openai", {
297
+ method: "oauth",
298
+ provider: "openai",
299
+ accessToken: "fresh-at",
300
+ refreshToken: "fresh-rt",
301
+ expiresAt: newExpires,
302
+ });
303
+ });
304
+ it("returns stale creds on refresh failure", async () => {
305
+ const oauthCreds = {
306
+ method: "oauth",
307
+ provider: "claude",
308
+ accessToken: "will-be-stale",
309
+ refreshToken: "rt",
310
+ expiresAt: Date.now() - 1000,
311
+ };
312
+ tokenStore.set("claude", oauthCreds);
313
+ tokenStore.needsRefresh.mockReturnValue(true);
314
+ manager.registerRefreshHandler("claude", vi.fn().mockRejectedValue(new Error("network error")));
315
+ const creds = await manager.getCredentials("claude");
316
+ expect(creds.accessToken).toBe("will-be-stale");
317
+ });
318
+ });
319
+ // -----------------------------------------------------------------------
320
+ // Multiple providers isolated
321
+ // -----------------------------------------------------------------------
322
+ describe("multiple providers", () => {
323
+ it("claude and openai credentials are isolated", async () => {
324
+ await manager.setApiKey("claude", "sk-claude");
325
+ await manager.setOAuthTokens("openai", "at-openai", "rt-openai", Date.now() + 3600_000);
326
+ const claudeCreds = await manager.getCredentials("claude");
327
+ const openaiCreds = await manager.getCredentials("openai");
328
+ expect(claudeCreds.apiKey).toBe("sk-claude");
329
+ expect(claudeCreds.method).toBe("api_key");
330
+ expect(openaiCreds.accessToken).toBe("at-openai");
331
+ expect(openaiCreds.method).toBe("oauth");
332
+ });
333
+ it("refreshing one provider does not affect the other", async () => {
334
+ const claudeCreds = {
335
+ method: "oauth",
336
+ provider: "claude",
337
+ accessToken: "claude-old",
338
+ refreshToken: "claude-rt",
339
+ expiresAt: Date.now() - 1000,
340
+ };
341
+ const openaiCreds = {
342
+ method: "oauth",
343
+ provider: "openai",
344
+ accessToken: "openai-stable",
345
+ refreshToken: "openai-rt",
346
+ expiresAt: Date.now() + 3600_000,
347
+ };
348
+ tokenStore.set("claude", claudeCreds);
349
+ tokenStore.set("openai", openaiCreds);
350
+ // Only claude needs refresh
351
+ tokenStore.needsRefresh.mockImplementation((provider) => provider === "claude");
352
+ manager.registerRefreshHandler("claude", vi.fn().mockResolvedValue({
353
+ accessToken: "claude-new",
354
+ refreshToken: "claude-rt-new",
355
+ expiresAt: Date.now() + 3600_000,
356
+ }));
357
+ const c = await manager.getCredentials("claude");
358
+ const o = await manager.getCredentials("openai");
359
+ expect(c.accessToken).toBe("claude-new");
360
+ expect(o.accessToken).toBe("openai-stable");
361
+ });
362
+ });
363
+ });
364
+ //# sourceMappingURL=auth-manager.test.js.map