@genesislcap/ai-assistant 15.19.0 → 15.19.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 (288) hide show
  1. package/package.json +17 -18
  2. package/api-extractor.json +0 -11
  3. package/dist/dts/components/chat-driver/chat-driver.compact.test.d.ts +0 -2
  4. package/dist/dts/components/chat-driver/chat-driver.compact.test.d.ts.map +0 -1
  5. package/dist/dts/components/chat-driver/chat-driver.invocation-scope.test.d.ts +0 -2
  6. package/dist/dts/components/chat-driver/chat-driver.invocation-scope.test.d.ts.map +0 -1
  7. package/dist/dts/components/chat-driver/chat-driver.test.d.ts +0 -2
  8. package/dist/dts/components/chat-driver/chat-driver.test.d.ts.map +0 -1
  9. package/dist/dts/components/chat-driver/chat-driver.thinking-policy.test.d.ts +0 -2
  10. package/dist/dts/components/chat-driver/chat-driver.thinking-policy.test.d.ts.map +0 -1
  11. package/dist/dts/components/chat-driver/chat-driver.trace-capture.test.d.ts +0 -2
  12. package/dist/dts/components/chat-driver/chat-driver.trace-capture.test.d.ts.map +0 -1
  13. package/dist/dts/components/chat-driver/chat-driver.turn-usage.test.d.ts +0 -2
  14. package/dist/dts/components/chat-driver/chat-driver.turn-usage.test.d.ts.map +0 -1
  15. package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.test.d.ts +0 -2
  16. package/dist/dts/components/chat-interaction-wrapper/chat-interaction-wrapper.test.d.ts.map +0 -1
  17. package/dist/dts/components/orchestrating-driver/orchestrating-driver.budget.test.d.ts +0 -2
  18. package/dist/dts/components/orchestrating-driver/orchestrating-driver.budget.test.d.ts.map +0 -1
  19. package/dist/dts/components/orchestrating-driver/orchestrating-driver.cost.test.d.ts +0 -2
  20. package/dist/dts/components/orchestrating-driver/orchestrating-driver.cost.test.d.ts.map +0 -1
  21. package/dist/dts/components/orchestrating-driver/orchestrating-driver.pin.test.d.ts +0 -2
  22. package/dist/dts/components/orchestrating-driver/orchestrating-driver.pin.test.d.ts.map +0 -1
  23. package/dist/dts/components/settings-modal/settings-modal.styles.test.d.ts +0 -2
  24. package/dist/dts/components/settings-modal/settings-modal.styles.test.d.ts.map +0 -1
  25. package/dist/dts/components/settings-modal/settings-modal.template.test.d.ts +0 -2
  26. package/dist/dts/components/settings-modal/settings-modal.template.test.d.ts.map +0 -1
  27. package/dist/dts/config/define-stateful-agent.test.d.ts +0 -2
  28. package/dist/dts/config/define-stateful-agent.test.d.ts.map +0 -1
  29. package/dist/dts/config/validate-providers.test.d.ts +0 -2
  30. package/dist/dts/config/validate-providers.test.d.ts.map +0 -1
  31. package/dist/dts/main/blocked-state.test.d.ts +0 -2
  32. package/dist/dts/main/blocked-state.test.d.ts.map +0 -1
  33. package/dist/dts/main/budget-meter.test.d.ts +0 -2
  34. package/dist/dts/main/budget-meter.test.d.ts.map +0 -1
  35. package/dist/dts/main/cost-session-banking.test.d.ts +0 -2
  36. package/dist/dts/main/cost-session-banking.test.d.ts.map +0 -1
  37. package/dist/dts/main/file-attachments.test.d.ts +0 -2
  38. package/dist/dts/main/file-attachments.test.d.ts.map +0 -1
  39. package/dist/dts/main/interaction-cost.test.d.ts +0 -2
  40. package/dist/dts/main/interaction-cost.test.d.ts.map +0 -1
  41. package/dist/dts/main/main.styles.test.d.ts +0 -2
  42. package/dist/dts/main/main.styles.test.d.ts.map +0 -1
  43. package/dist/dts/main/persistence-broken-sources.test.d.ts +0 -2
  44. package/dist/dts/main/persistence-broken-sources.test.d.ts.map +0 -1
  45. package/dist/dts/main/popout-interaction-gate.test.d.ts +0 -2
  46. package/dist/dts/main/popout-interaction-gate.test.d.ts.map +0 -1
  47. package/dist/dts/state/ai-assistant-slice.test.d.ts +0 -2
  48. package/dist/dts/state/ai-assistant-slice.test.d.ts.map +0 -1
  49. package/dist/dts/state/debug-event-log.test.d.ts +0 -2
  50. package/dist/dts/state/debug-event-log.test.d.ts.map +0 -1
  51. package/dist/dts/state/driver-registry.test.d.ts +0 -2
  52. package/dist/dts/state/driver-registry.test.d.ts.map +0 -1
  53. package/dist/dts/state/interaction-context.test.d.ts +0 -2
  54. package/dist/dts/state/interaction-context.test.d.ts.map +0 -1
  55. package/dist/dts/state/persistence/diagnostics-cursors.test.d.ts +0 -2
  56. package/dist/dts/state/persistence/diagnostics-cursors.test.d.ts.map +0 -1
  57. package/dist/dts/state/persistence/diagnostics.test.d.ts +0 -2
  58. package/dist/dts/state/persistence/diagnostics.test.d.ts.map +0 -1
  59. package/dist/dts/state/persistence/session-persistence-provider.test.d.ts +0 -2
  60. package/dist/dts/state/persistence/session-persistence-provider.test.d.ts.map +0 -1
  61. package/dist/dts/state/persistence/session-persistence.integration.test.d.ts +0 -2
  62. package/dist/dts/state/persistence/session-persistence.integration.test.d.ts.map +0 -1
  63. package/dist/dts/state/persistence/session-persister.test.d.ts +0 -2
  64. package/dist/dts/state/persistence/session-persister.test.d.ts.map +0 -1
  65. package/dist/dts/state/persistence/session-snapshot.test.d.ts +0 -2
  66. package/dist/dts/state/persistence/session-snapshot.test.d.ts.map +0 -1
  67. package/dist/dts/state/persistence/stateful-restore.e2e.test.d.ts +0 -2
  68. package/dist/dts/state/persistence/stateful-restore.e2e.test.d.ts.map +0 -1
  69. package/dist/dts/utils/animation-exclusivity.test.d.ts +0 -2
  70. package/dist/dts/utils/animation-exclusivity.test.d.ts.map +0 -1
  71. package/dist/dts/utils/collect-session-models.test.d.ts +0 -2
  72. package/dist/dts/utils/collect-session-models.test.d.ts.map +0 -1
  73. package/dist/dts/utils/condense-history.test.d.ts +0 -2
  74. package/dist/dts/utils/condense-history.test.d.ts.map +0 -1
  75. package/dist/dts/utils/cost-session-history.test.d.ts +0 -2
  76. package/dist/dts/utils/cost-session-history.test.d.ts.map +0 -1
  77. package/dist/dts/utils/derive-cost-session-title.test.d.ts +0 -2
  78. package/dist/dts/utils/derive-cost-session-title.test.d.ts.map +0 -1
  79. package/dist/dts/utils/flatten-sub-agent-messages.test.d.ts +0 -2
  80. package/dist/dts/utils/flatten-sub-agent-messages.test.d.ts.map +0 -1
  81. package/dist/dts/utils/history-transform.test.d.ts +0 -2
  82. package/dist/dts/utils/history-transform.test.d.ts.map +0 -1
  83. package/dist/dts/utils/message-partition.test.d.ts +0 -2
  84. package/dist/dts/utils/message-partition.test.d.ts.map +0 -1
  85. package/dist/dts/utils/resolve-cost-history-config.test.d.ts +0 -2
  86. package/dist/dts/utils/resolve-cost-history-config.test.d.ts.map +0 -1
  87. package/dist/dts/utils/resolve-preference-baseline.test.d.ts +0 -2
  88. package/dist/dts/utils/resolve-preference-baseline.test.d.ts.map +0 -1
  89. package/dist/dts/utils/strip-agent-handlers.test.d.ts +0 -2
  90. package/dist/dts/utils/strip-agent-handlers.test.d.ts.map +0 -1
  91. package/dist/dts/utils/sum-costs.test.d.ts +0 -2
  92. package/dist/dts/utils/sum-costs.test.d.ts.map +0 -1
  93. package/dist/dts/utils/sum-tokens.test.d.ts +0 -2
  94. package/dist/dts/utils/sum-tokens.test.d.ts.map +0 -1
  95. package/dist/dts/utils/sum-usage.test.d.ts +0 -2
  96. package/dist/dts/utils/sum-usage.test.d.ts.map +0 -1
  97. package/dist/dts/utils/usage-rows.test.d.ts +0 -2
  98. package/dist/dts/utils/usage-rows.test.d.ts.map +0 -1
  99. package/dist/esm/components/chat-driver/chat-driver.compact.test.js +0 -93
  100. package/dist/esm/components/chat-driver/chat-driver.invocation-scope.test.js +0 -238
  101. package/dist/esm/components/chat-driver/chat-driver.test.js +0 -2770
  102. package/dist/esm/components/chat-driver/chat-driver.thinking-policy.test.js +0 -137
  103. package/dist/esm/components/chat-driver/chat-driver.trace-capture.test.js +0 -200
  104. package/dist/esm/components/chat-driver/chat-driver.turn-usage.test.js +0 -268
  105. package/dist/esm/components/chat-interaction-wrapper/chat-interaction-wrapper.test.js +0 -104
  106. package/dist/esm/components/orchestrating-driver/orchestrating-driver.budget.test.js +0 -312
  107. package/dist/esm/components/orchestrating-driver/orchestrating-driver.cost.test.js +0 -76
  108. package/dist/esm/components/orchestrating-driver/orchestrating-driver.pin.test.js +0 -61
  109. package/dist/esm/components/settings-modal/settings-modal.styles.test.js +0 -80
  110. package/dist/esm/components/settings-modal/settings-modal.template.test.js +0 -91
  111. package/dist/esm/config/define-stateful-agent.test.js +0 -53
  112. package/dist/esm/config/validate-providers.test.js +0 -100
  113. package/dist/esm/main/blocked-state.test.js +0 -1065
  114. package/dist/esm/main/budget-meter.test.js +0 -359
  115. package/dist/esm/main/cost-session-banking.test.js +0 -538
  116. package/dist/esm/main/file-attachments.test.js +0 -164
  117. package/dist/esm/main/interaction-cost.test.js +0 -107
  118. package/dist/esm/main/main.styles.test.js +0 -161
  119. package/dist/esm/main/persistence-broken-sources.test.js +0 -180
  120. package/dist/esm/main/popout-interaction-gate.test.js +0 -54
  121. package/dist/esm/state/ai-assistant-slice.test.js +0 -229
  122. package/dist/esm/state/debug-event-log.test.js +0 -117
  123. package/dist/esm/state/driver-registry.test.js +0 -55
  124. package/dist/esm/state/interaction-context.test.js +0 -78
  125. package/dist/esm/state/persistence/diagnostics-cursors.test.js +0 -30
  126. package/dist/esm/state/persistence/diagnostics.test.js +0 -280
  127. package/dist/esm/state/persistence/session-persistence-provider.test.js +0 -110
  128. package/dist/esm/state/persistence/session-persistence.integration.test.js +0 -113
  129. package/dist/esm/state/persistence/session-persister.test.js +0 -613
  130. package/dist/esm/state/persistence/session-snapshot.test.js +0 -256
  131. package/dist/esm/state/persistence/stateful-restore.e2e.test.js +0 -158
  132. package/dist/esm/utils/animation-exclusivity.test.js +0 -52
  133. package/dist/esm/utils/collect-session-models.test.js +0 -53
  134. package/dist/esm/utils/condense-history.test.js +0 -547
  135. package/dist/esm/utils/cost-session-history.test.js +0 -174
  136. package/dist/esm/utils/derive-cost-session-title.test.js +0 -37
  137. package/dist/esm/utils/flatten-sub-agent-messages.test.js +0 -139
  138. package/dist/esm/utils/history-transform.test.js +0 -158
  139. package/dist/esm/utils/message-partition.test.js +0 -90
  140. package/dist/esm/utils/resolve-cost-history-config.test.js +0 -32
  141. package/dist/esm/utils/resolve-preference-baseline.test.js +0 -69
  142. package/dist/esm/utils/strip-agent-handlers.test.js +0 -81
  143. package/dist/esm/utils/sum-costs.test.js +0 -114
  144. package/dist/esm/utils/sum-tokens.test.js +0 -51
  145. package/dist/esm/utils/sum-usage.test.js +0 -164
  146. package/dist/esm/utils/usage-rows.test.js +0 -189
  147. package/dist/tsconfig.tsbuildinfo +0 -1
  148. package/license.txt +0 -46
  149. package/sandbox/tsconfig.json +0 -8
  150. package/src/channel/ai-activity-bus.ts +0 -209
  151. package/src/channel/ai-activity-channel.ts +0 -146
  152. package/src/chat-driver-node.ts +0 -159
  153. package/src/components/activity-halo/activity-halo.ts +0 -106
  154. package/src/components/agent-picker/agent-picker.constants.ts +0 -2
  155. package/src/components/agent-picker/agent-picker.styles.ts +0 -24
  156. package/src/components/agent-picker/agent-picker.template.ts +0 -54
  157. package/src/components/agent-picker/agent-picker.ts +0 -99
  158. package/src/components/agent-picker/index.ts +0 -1
  159. package/src/components/ai-driver/ai-driver.ts +0 -166
  160. package/src/components/ai-driver/index.ts +0 -1
  161. package/src/components/chat-bubble/chat-bubble.styles.ts +0 -194
  162. package/src/components/chat-bubble/chat-bubble.template.ts +0 -67
  163. package/src/components/chat-bubble/chat-bubble.ts +0 -436
  164. package/src/components/chat-bubble/index.ts +0 -1
  165. package/src/components/chat-driver/align-event-globals.ts +0 -23
  166. package/src/components/chat-driver/chat-driver.compact.test.ts +0 -131
  167. package/src/components/chat-driver/chat-driver.invocation-scope.test.ts +0 -285
  168. package/src/components/chat-driver/chat-driver.test.ts +0 -3679
  169. package/src/components/chat-driver/chat-driver.thinking-policy.test.ts +0 -185
  170. package/src/components/chat-driver/chat-driver.trace-capture.test.ts +0 -251
  171. package/src/components/chat-driver/chat-driver.ts +0 -3925
  172. package/src/components/chat-driver/chat-driver.turn-usage.test.ts +0 -362
  173. package/src/components/chat-driver/index.ts +0 -1
  174. package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.styles.ts +0 -7
  175. package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.template.ts +0 -6
  176. package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.test.ts +0 -144
  177. package/src/components/chat-interaction-wrapper/chat-interaction-wrapper.ts +0 -243
  178. package/src/components/chat-interaction-wrapper/index.ts +0 -3
  179. package/src/components/chat-markdown/chat-markdown.ts +0 -89
  180. package/src/components/chat-markdown/index.ts +0 -1
  181. package/src/components/flowing-waves-indicator.ts +0 -286
  182. package/src/components/halo-overlay.ts +0 -184
  183. package/src/components/orchestrating-driver/index.ts +0 -1
  184. package/src/components/orchestrating-driver/orchestrating-driver.budget.test.ts +0 -438
  185. package/src/components/orchestrating-driver/orchestrating-driver.cost.test.ts +0 -107
  186. package/src/components/orchestrating-driver/orchestrating-driver.pin.test.ts +0 -96
  187. package/src/components/orchestrating-driver/orchestrating-driver.ts +0 -813
  188. package/src/components/plasma-orb-indicator.ts +0 -281
  189. package/src/components/popout-manager/index.ts +0 -1
  190. package/src/components/popout-manager/popout-manager.ts +0 -212
  191. package/src/components/settings-modal/index.ts +0 -2
  192. package/src/components/settings-modal/settings-modal.styles.test.ts +0 -94
  193. package/src/components/settings-modal/settings-modal.styles.ts +0 -853
  194. package/src/components/settings-modal/settings-modal.template.test.ts +0 -124
  195. package/src/components/settings-modal/settings-modal.template.ts +0 -943
  196. package/src/components/waves-indicator.ts +0 -241
  197. package/src/config/config.ts +0 -608
  198. package/src/config/define-stateful-agent.test.ts +0 -64
  199. package/src/config/define-stateful-agent.ts +0 -642
  200. package/src/config/fallback-agents.ts +0 -29
  201. package/src/config/index.ts +0 -2
  202. package/src/config/validate-providers.test.ts +0 -148
  203. package/src/config/validate-providers.ts +0 -58
  204. package/src/index.ts +0 -29
  205. package/src/main/blocked-state.test.ts +0 -1444
  206. package/src/main/budget-meter.test.ts +0 -477
  207. package/src/main/cost-session-banking.test.ts +0 -679
  208. package/src/main/file-attachments.test.ts +0 -215
  209. package/src/main/index.ts +0 -3
  210. package/src/main/interaction-cost.test.ts +0 -140
  211. package/src/main/main.styles.test.ts +0 -216
  212. package/src/main/main.styles.ts +0 -995
  213. package/src/main/main.template.ts +0 -1119
  214. package/src/main/main.ts +0 -5432
  215. package/src/main/main.types.ts +0 -225
  216. package/src/main/persistence-broken-sources.test.ts +0 -219
  217. package/src/main/popout-interaction-gate.test.ts +0 -87
  218. package/src/provider/ai-provider-switcher.ts +0 -40
  219. package/src/provider/assistant-app-settings.ts +0 -84
  220. package/src/state/ai-assistant-slice.test.ts +0 -336
  221. package/src/state/ai-assistant-slice.ts +0 -604
  222. package/src/state/debug-event-log.test.ts +0 -154
  223. package/src/state/debug-event-log.ts +0 -358
  224. package/src/state/driver-registry.test.ts +0 -70
  225. package/src/state/driver-registry.ts +0 -79
  226. package/src/state/interaction-context.test.ts +0 -90
  227. package/src/state/interaction-context.ts +0 -101
  228. package/src/state/persistence/build-timeline-entries.ts +0 -66
  229. package/src/state/persistence/diagnostics-cursors.test.ts +0 -53
  230. package/src/state/persistence/diagnostics-cursors.ts +0 -80
  231. package/src/state/persistence/diagnostics.test.ts +0 -346
  232. package/src/state/persistence/diagnostics.ts +0 -220
  233. package/src/state/persistence/index.ts +0 -10
  234. package/src/state/persistence/persister-registry.ts +0 -55
  235. package/src/state/persistence/session-persistence-provider.test.ts +0 -136
  236. package/src/state/persistence/session-persistence-provider.ts +0 -226
  237. package/src/state/persistence/session-persistence.integration.test.ts +0 -141
  238. package/src/state/persistence/session-persister.test.ts +0 -787
  239. package/src/state/persistence/session-persister.ts +0 -917
  240. package/src/state/persistence/session-snapshot.test.ts +0 -352
  241. package/src/state/persistence/session-snapshot.ts +0 -344
  242. package/src/state/persistence/stateful-restore.e2e.test.ts +0 -229
  243. package/src/state/session-store.ts +0 -56
  244. package/src/styles/ai-colours.ts +0 -30
  245. package/src/styles/menu.ts +0 -187
  246. package/src/styles/settings-section.ts +0 -40
  247. package/src/suggestions/chat-suggestions.ts +0 -186
  248. package/src/tags/index.ts +0 -1
  249. package/src/types/ai-chat-widget.ts +0 -67
  250. package/src/types/interaction-context.ts +0 -82
  251. package/src/utils/animated-panel-toggle.ts +0 -62
  252. package/src/utils/animation-exclusivity.test.ts +0 -72
  253. package/src/utils/animation-exclusivity.ts +0 -40
  254. package/src/utils/banked-usage-baselines.ts +0 -90
  255. package/src/utils/collect-session-models.test.ts +0 -82
  256. package/src/utils/collect-session-models.ts +0 -65
  257. package/src/utils/condense-history.test.ts +0 -746
  258. package/src/utils/condense-history.ts +0 -448
  259. package/src/utils/cost-session-history.test.ts +0 -226
  260. package/src/utils/cost-session-history.ts +0 -218
  261. package/src/utils/derive-cost-session-title.test.ts +0 -63
  262. package/src/utils/derive-cost-session-title.ts +0 -76
  263. package/src/utils/flatten-sub-agent-messages.test.ts +0 -163
  264. package/src/utils/flatten-sub-agent-messages.ts +0 -88
  265. package/src/utils/format-usd.ts +0 -26
  266. package/src/utils/history-transform.test.ts +0 -228
  267. package/src/utils/history-transform.ts +0 -163
  268. package/src/utils/index.ts +0 -3
  269. package/src/utils/logger.ts +0 -3
  270. package/src/utils/message-partition.test.ts +0 -129
  271. package/src/utils/message-partition.ts +0 -115
  272. package/src/utils/resolve-cost-history-config.test.ts +0 -38
  273. package/src/utils/resolve-cost-history-config.ts +0 -38
  274. package/src/utils/resolve-preference-baseline.test.ts +0 -84
  275. package/src/utils/resolve-preference-baseline.ts +0 -80
  276. package/src/utils/strip-agent-handlers.test.ts +0 -99
  277. package/src/utils/strip-agent-handlers.ts +0 -53
  278. package/src/utils/sum-costs.test.ts +0 -136
  279. package/src/utils/sum-costs.ts +0 -18
  280. package/src/utils/sum-tokens.test.ts +0 -93
  281. package/src/utils/sum-tokens.ts +0 -68
  282. package/src/utils/sum-usage.test.ts +0 -191
  283. package/src/utils/sum-usage.ts +0 -208
  284. package/src/utils/tool-fold.ts +0 -181
  285. package/src/utils/usage-rows.test.ts +0 -237
  286. package/src/utils/usage-rows.ts +0 -187
  287. package/src/utils/with-timeout.ts +0 -24
  288. package/tsconfig.json +0 -9
@@ -1,184 +0,0 @@
1
- import { attr, css, customElement, GenesisElement, html } from '@genesislcap/web-core';
2
- import {
3
- AI_COLOUR_AMBER,
4
- AI_COLOUR_CYAN,
5
- AI_COLOUR_PINK,
6
- AI_COLOUR_VIOLET,
7
- } from '../styles/ai-colours';
8
-
9
- const HALO_DEFAULT_SPEED = 1.5;
10
- const HALO_DEFAULT_BORDER_SIZE = 3;
11
- const HALO_DEFAULT_GLOW_OPACITY = 0.35;
12
- const HALO_DEFAULT_GLOW_SPREAD = 70;
13
- /** Milliseconds in one second. */
14
- const MS_PER_SECOND = 1000;
15
- /** Baseline frame rate (fps) the `speed` (degrees/frame) is calibrated against. */
16
- const BASELINE_FPS = 60;
17
- /**
18
- * Frame duration (ms) at the baseline rate. Rotation advances by the elapsed
19
- * wall-clock time divided by this — not a fixed step per rAF callback — so it
20
- * holds a constant real-world speed regardless of the display's refresh rate or
21
- * dropped frames.
22
- */
23
- const FRAME_MS = MS_PER_SECOND / BASELINE_FPS;
24
-
25
- /**
26
- * Animated halo overlay — rotating conic-gradient border with an inward glow.
27
- *
28
- * Drop inside any `position: relative; overflow: hidden` container and toggle
29
- * the `active` attribute to show/hide the effect.
30
- *
31
- * @example
32
- * ```html
33
- * <ai-halo-overlay ?active="${(x) => x.isLoading}"></ai-halo-overlay>
34
- * ```
35
- *
36
- * @beta
37
- */
38
- @customElement({
39
- name: 'ai-halo-overlay',
40
- template: html`
41
- <slot></slot>
42
- `,
43
- styles: css`
44
- :host {
45
- display: block;
46
- position: absolute;
47
- inset: 0;
48
- border-radius: inherit;
49
- pointer-events: none;
50
- z-index: 1000;
51
- opacity: 0;
52
- transition: opacity 0.4s ease;
53
- }
54
-
55
- :host([active]) {
56
- opacity: 1;
57
- }
58
-
59
- /* Rotating solid border */
60
- :host::before {
61
- content: '';
62
- position: absolute;
63
- inset: 0;
64
- border-radius: inherit;
65
- padding: var(--halo-border-size, 3px);
66
- background: repeating-conic-gradient(
67
- from var(--halo-angle, 0deg),
68
- ${AI_COLOUR_AMBER},
69
- ${AI_COLOUR_PINK},
70
- ${AI_COLOUR_CYAN},
71
- ${AI_COLOUR_VIOLET},
72
- ${AI_COLOUR_AMBER}
73
- );
74
- -webkit-mask:
75
- linear-gradient(#000 0 0) content-box,
76
- linear-gradient(#000 0 0);
77
- mask:
78
- linear-gradient(#000 0 0) content-box,
79
- linear-gradient(#000 0 0);
80
- -webkit-mask-composite: destination-out;
81
- mask-composite: exclude;
82
- }
83
-
84
- /* Inward glow — radial mask makes centre transparent, edges coloured */
85
- :host::after {
86
- content: '';
87
- position: absolute;
88
- inset: 0;
89
- border-radius: inherit;
90
- background: repeating-conic-gradient(
91
- from var(--halo-angle, 0deg),
92
- ${AI_COLOUR_AMBER},
93
- ${AI_COLOUR_PINK},
94
- ${AI_COLOUR_CYAN},
95
- ${AI_COLOUR_VIOLET},
96
- ${AI_COLOUR_AMBER}
97
- );
98
- opacity: var(--halo-glow-opacity, 0.35);
99
- -webkit-mask-image: radial-gradient(
100
- ellipse at center,
101
- transparent var(--halo-glow-spread, 70%),
102
- black 100%
103
- );
104
- mask-image: radial-gradient(
105
- ellipse at center,
106
- transparent var(--halo-glow-spread, 70%),
107
- black 100%
108
- );
109
- }
110
- `,
111
- })
112
- export class AiHaloOverlay extends GenesisElement {
113
- @attr({ mode: 'boolean' }) active: boolean = false;
114
- /** Rotation speed in degrees per frame. Default: 1.5 (≈ 4 s per full revolution at 60 fps). */
115
- @attr({ converter: { fromView: Number, toView: String } }) speed: number = HALO_DEFAULT_SPEED;
116
- /** Rotation direction. Default: 'cw' (clockwise). */
117
- @attr direction: 'cw' | 'ccw' = 'cw';
118
- /** Border thickness in px. Default: 3. */
119
- @attr({ attribute: 'border-size', converter: { fromView: Number, toView: String } })
120
- borderSize: number = HALO_DEFAULT_BORDER_SIZE;
121
- /** Glow layer opacity (0–1). Default: 0.35. */
122
- @attr({ attribute: 'glow-opacity', converter: { fromView: Number, toView: String } })
123
- glowOpacity: number = HALO_DEFAULT_GLOW_OPACITY;
124
- /** Transparent stop of the radial glow mask as a percentage (0–100). Higher = less spread. Default: 70. */
125
- @attr({ attribute: 'glow-spread', converter: { fromView: Number, toView: String } })
126
- glowSpread: number = HALO_DEFAULT_GLOW_SPREAD;
127
-
128
- borderSizeChanged() {
129
- this.style.setProperty('--halo-border-size', `${this.borderSize}px`);
130
- }
131
-
132
- glowOpacityChanged() {
133
- this.style.setProperty('--halo-glow-opacity', String(this.glowOpacity));
134
- }
135
-
136
- glowSpreadChanged() {
137
- this.style.setProperty('--halo-glow-spread', `${this.glowSpread}%`);
138
- }
139
-
140
- // TODO: The rAF loop is fine for demos but has two drawbacks vs a pure CSS @property animation:
141
- // 1. It runs on the main thread, so heavy JS work can cause the animation to stutter.
142
- // 2. The loop ticks even when `active` is false (idle cost is negligible but non-zero).
143
- // Once @property inside Shadow DOM has solid cross-browser support, consider switching back
144
- // to a CSS @keyframes approach and removing connectedCallback/disconnectedCallback/tick().
145
-
146
- private static readonly FULL_ROTATION_DEG = 360;
147
-
148
- private angle = 0;
149
- private lastTime?: number;
150
- private animFrame?: number;
151
-
152
- connectedCallback() {
153
- super.connectedCallback();
154
- this.tick();
155
- }
156
-
157
- disconnectedCallback() {
158
- super.disconnectedCallback();
159
- if (this.animFrame !== undefined) {
160
- cancelAnimationFrame(this.animFrame);
161
- this.animFrame = undefined;
162
- }
163
- // Reset the clock so a later reconnect doesn't apply one huge time delta.
164
- this.lastTime = undefined;
165
- }
166
-
167
- private tick(now?: number) {
168
- // Advance by elapsed wall-clock time (expressed in 60fps-equivalent frames)
169
- // rather than a fixed step per callback, so the rotation runs at a constant
170
- // real-world speed regardless of the display's refresh rate or dropped
171
- // frames. The first tick just seeds the clock (no delta yet).
172
- if (this.lastTime !== undefined && now !== undefined) {
173
- const frames = (now - this.lastTime) / FRAME_MS;
174
- const step = this.direction === 'ccw' ? -this.speed : this.speed;
175
- const raw = this.angle + step * frames;
176
- this.angle =
177
- ((raw % AiHaloOverlay.FULL_ROTATION_DEG) + AiHaloOverlay.FULL_ROTATION_DEG) %
178
- AiHaloOverlay.FULL_ROTATION_DEG;
179
- this.style.setProperty('--halo-angle', `${this.angle}deg`);
180
- }
181
- this.lastTime = now;
182
- this.animFrame = requestAnimationFrame((t) => this.tick(t));
183
- }
184
- }
@@ -1 +0,0 @@
1
- export { OrchestratingDriver } from './orchestrating-driver';
@@ -1,438 +0,0 @@
1
- import type {
2
- AIProvider,
3
- AIProviderRegistry,
4
- ChatDriverResult,
5
- ChatMessage,
6
- ChatRequestOptions,
7
- } from '@genesislcap/foundation-ai';
8
- import { BudgetExhaustedError } from '@genesislcap/foundation-ai';
9
- import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
10
- import { AgenticActivityBus } from '../../channel/ai-activity-bus';
11
- import type { AgentConfig } from '../../config/config';
12
- // Side-effect import — MUST precede `./orchestrating-driver` so the driver
13
- // subclasses jsdom's EventTarget (CustomEvent dispatch then works in node).
14
- import '../chat-driver/align-event-globals';
15
- import { OrchestratingDriver } from './orchestrating-driver';
16
-
17
- // GENC-1464 — the orchestrated turn's OUTCOME.
18
- //
19
- // `createDriver` returns an `OrchestratingDriver` for every agents-configured
20
- // host, and its `sendMessage` used to end with a hardcoded `return { reason:
21
- // 'done' }`. That made every typed failure unreachable for those hosts —
22
- // including `'budget-exhausted'`, which the assistant element latches its blocked
23
- // state off. A 402 therefore produced an apology over a still-enabled composer
24
- // for exactly the configurations this workstream exists to fix.
25
- //
26
- // The classifier compounds it: `classify()` is a SECOND retry ladder stacked on
27
- // the transport's, so an untyped catch re-issued the 402 `classifierRetries + 1`
28
- // times and then ran a fallback turn into the same wall.
29
-
30
- const makeRegistry = (provider: AIProvider): AIProviderRegistry => ({
31
- get: () => provider,
32
- default: () => provider,
33
- defaultName: () => 'test',
34
- names: () => ['test'],
35
- getStatus: async () => null,
36
- listStatuses: async () => [],
37
- });
38
-
39
- const agent = (overrides: Partial<AgentConfig> & { name: string }): AgentConfig =>
40
- ({ description: 'test agent', ...overrides }) as AgentConfig;
41
-
42
- /** A provider that ends every turn immediately with a plain-text reply (no tools). */
43
- const okProvider = (): AIProvider =>
44
- ({
45
- chat: async (): Promise<ChatMessage> => ({ role: 'assistant', content: 'ok' }),
46
- }) as AIProvider;
47
-
48
- /** A provider that counts calls and always refuses for budget. */
49
- const walledProvider = (
50
- vendorLabel = 'Anthropic',
51
- ): { calls: () => number; provider: AIProvider } => {
52
- let n = 0;
53
- return {
54
- calls: () => n,
55
- provider: {
56
- chat: async (): Promise<ChatMessage> => {
57
- n += 1;
58
- throw new BudgetExhaustedError(vendorLabel, 25, 25.4);
59
- },
60
- } as AIProvider,
61
- };
62
- };
63
-
64
- const failureReasonOf = (r: ChatDriverResult) =>
65
- r.reason === 'done' ? r.failureReason : undefined;
66
-
67
- const Suite = createLogicSuite('orchestrating-driver budget wall');
68
-
69
- // ── 1. The showstopper: a walled AGENT turn must reach the caller ───────────
70
-
71
- Suite('a budget wall on the agent turn surfaces as the orchestrated result', async () => {
72
- // ONE specialist and no fallback, so `classify` short-circuits without calling
73
- // the provider — the wall is hit by the AGENT turn, inside a real tool loop.
74
- // Against the old hardcoded return this deep-equal fails.
75
- const { provider } = walledProvider();
76
- const driver = new OrchestratingDriver(makeRegistry(provider), [agent({ name: 'Solo' })], {
77
- sessionKey: 'budget-1',
78
- });
79
-
80
- const result = await driver.sendMessage('go');
81
-
82
- assert.equal(result, {
83
- reason: 'done',
84
- failureReason: 'budget-exhausted',
85
- budget: { budgetUsd: 25, spentUsd: 25.4, vendorLabel: 'Anthropic', vendor: 'anthropic' },
86
- });
87
- });
88
-
89
- Suite("the vendor is the refusing transport's, not a hardcoded default", async () => {
90
- // The label rides the contract; `vendor` is its normalised key. Without a
91
- // second vendor exercised here, a constant would pass every other assertion in
92
- // this file — and per-vendor blocking would wall the wrong vendor forever.
93
- const { provider } = walledProvider('Gemini');
94
- const driver = new OrchestratingDriver(makeRegistry(provider), [agent({ name: 'Solo' })], {
95
- sessionKey: 'budget-gemini',
96
- });
97
-
98
- const result = await driver.sendMessage('go');
99
-
100
- assert.equal(result.reason === 'done' ? result.budget : undefined, {
101
- budgetUsd: 25,
102
- spentUsd: 25.4,
103
- vendorLabel: 'Gemini',
104
- vendor: 'gemini',
105
- });
106
- });
107
-
108
- Suite('an unrecognised vendor label carries no typed vendor at all', async () => {
109
- // Attribution degrades to "unattributable" rather than to a wrong vendor —
110
- // which makes the element fall back to its vendor-agnostic block. Safe.
111
- const { provider } = walledProvider('Acme AI');
112
- const driver = new OrchestratingDriver(makeRegistry(provider), [agent({ name: 'Solo' })], {
113
- sessionKey: 'budget-unknown-vendor',
114
- });
115
-
116
- const result = await driver.sendMessage('go');
117
-
118
- assert.equal(result.reason === 'done' ? result.budget : undefined, {
119
- budgetUsd: 25,
120
- spentUsd: 25.4,
121
- vendorLabel: 'Acme AI',
122
- });
123
- });
124
-
125
- Suite('the wall also reaches the tool-loop-end bus detail through the orchestrator', async () => {
126
- const bus = new AgenticActivityBus();
127
- const seen: Array<{ failureReason?: string } | undefined> = [];
128
- const stop = bus.subscribe('tool-loop-end', (d) => seen.push(d));
129
- const { provider } = walledProvider();
130
- const driver = new OrchestratingDriver(makeRegistry(provider), [agent({ name: 'Solo' })], {
131
- sessionKey: 'budget-2',
132
- activityBus: bus,
133
- });
134
-
135
- await driver.sendMessage('go');
136
-
137
- assert.is(seen.length, 1, 'exactly one tool-loop-end for the one agent turn');
138
- assert.is(seen[0]?.failureReason, 'budget-exhausted');
139
- stop();
140
- bus.close();
141
- });
142
-
143
- Suite('the bus detail carries the proxy figures, not just the failure reason', async () => {
144
- // The element latches off THIS event for an in-loop wall — the publish sits in
145
- // sendMessage's `finally`, so it lands before the return-value seam and the
146
- // idempotent latch makes the later one a no-op. If the figures rode only the
147
- // return value they would be unreachable in the common case and the banner would
148
- // show the generic copy in exactly the path they were added for.
149
- const bus = new AgenticActivityBus();
150
- const seen: Array<
151
- | {
152
- failureReason?: string;
153
- vendor?: string;
154
- budget?: {
155
- budgetUsd?: number;
156
- spentUsd?: number;
157
- vendorLabel: string;
158
- vendor?: string;
159
- };
160
- }
161
- | undefined
162
- > = [];
163
- const stop = bus.subscribe('tool-loop-end', (d) => seen.push(d));
164
- const { provider } = walledProvider();
165
- const driver = new OrchestratingDriver(makeRegistry(provider), [agent({ name: 'Solo' })], {
166
- sessionKey: 'budget-figures',
167
- activityBus: bus,
168
- });
169
-
170
- const result = await driver.sendMessage('go');
171
-
172
- assert.equal(
173
- seen[0]?.budget,
174
- { budgetUsd: 25, spentUsd: 25.4, vendorLabel: 'Anthropic', vendor: 'anthropic' },
175
- 'the event carries the same figures as the return value',
176
- );
177
- assert.is(
178
- seen[0]?.vendor,
179
- 'anthropic',
180
- 'and the top-level vendor agrees with the refusing transport, not with a stale last-resolved provider',
181
- );
182
- assert.equal(
183
- result.reason === 'done' ? result.budget : undefined,
184
- seen[0]?.budget,
185
- 'event and return value agree — neither is the sole carrier',
186
- );
187
- stop();
188
- bus.close();
189
- });
190
-
191
- Suite('a clean turn publishes no budget key at all', async () => {
192
- const bus = new AgenticActivityBus();
193
- const seen: Array<{ budget?: unknown } | undefined> = [];
194
- const stop = bus.subscribe('tool-loop-end', (d) => seen.push(d));
195
- const provider: AIProvider = {
196
- chat: async () => ({ role: 'assistant', content: 'ok' }) as ChatMessage,
197
- } as unknown as AIProvider;
198
- const driver = new OrchestratingDriver(makeRegistry(provider), [agent({ name: 'Solo' })], {
199
- sessionKey: 'budget-clean',
200
- activityBus: bus,
201
- });
202
-
203
- await driver.sendMessage('go');
204
-
205
- assert.ok(
206
- seen.every((d) => d === undefined || !('budget' in d)),
207
- 'no budget key on a non-budget turn — the historical detail shape is unchanged',
208
- );
209
- stop();
210
- bus.close();
211
- });
212
-
213
- // ── 2. No regression on the happy / handoff paths ───────────────────────────
214
-
215
- Suite('a clean turn still returns the bare legacy shape', async () => {
216
- const driver = new OrchestratingDriver(makeRegistry(okProvider()), [agent({ name: 'Solo' })], {
217
- sessionKey: 'budget-3',
218
- });
219
-
220
- const result = await driver.sendMessage('go');
221
-
222
- assert.equal(result, { reason: 'done' }, 'no failureReason on a clean turn');
223
- });
224
-
225
- Suite('a handoff-capped turn never leaks agent-handoff to the caller', async () => {
226
- // `'agent-handoff'` is an internal routing protocol. The two breaks that hold a
227
- // non-'done' inner result both carry it, so the collapse rule in `sendMessage`
228
- // is what keeps "report the turn that ended the loop" from changing the
229
- // discriminant a host matches on.
230
- let n = 0;
231
- const nextId = () => {
232
- n += 1;
233
- return n;
234
- };
235
- const handoffProvider: AIProvider = {
236
- chat: async (
237
- _h: ChatMessage[],
238
- _u: string,
239
- options?: ChatRequestOptions,
240
- ): Promise<ChatMessage> => {
241
- // The classifier call carries the routing tool; answer it with index 0.
242
- if (options?.tools?.some((t) => t.name === 'select_agent')) {
243
- return {
244
- role: 'assistant',
245
- content: '',
246
- toolCalls: [{ id: `c-${nextId()}`, name: 'select_agent', args: { agent_index: 0 } }],
247
- };
248
- }
249
- // Every agent turn asks to hand off, so the handoff cap is what ends it.
250
- return {
251
- role: 'assistant',
252
- content: '',
253
- toolCalls: [
254
- {
255
- id: `h-${nextId()}`,
256
- name: 'request_continuation',
257
- args: { summary: 's', remaining_task: 'more' },
258
- },
259
- ],
260
- };
261
- },
262
- };
263
- const driver = new OrchestratingDriver(
264
- makeRegistry(handoffProvider),
265
- [agent({ name: 'A' }), agent({ name: 'B' })],
266
- { sessionKey: 'budget-4', maxHandoffs: 1 },
267
- );
268
-
269
- const result = await driver.sendMessage('go');
270
-
271
- assert.is(result.reason, 'done', 'the internal handoff protocol never escapes');
272
- });
273
-
274
- Suite('a wall on a post-handoff classify does not duplicate the user message', async () => {
275
- // `classify()` runs at TWO seams. Before the first agent turn the user's
276
- // message is unappended (only the optimistic `history-updated` echo exists),
277
- // so the wall-catch must append it. At the END of a handoff iteration
278
- // `chatDriver.sendMessage` has already appended it — passing it again
279
- // produced ['user','assistant','tool','user','assistant'] with 'go' twice,
280
- // persisting into the snapshot and into the history sent to the model once
281
- // the budget was raised. The single-specialist narrowness guard below cannot
282
- // catch this: with one specialist, classify short-circuits and the handoff
283
- // seam never runs.
284
- let n = 0;
285
- const provider: AIProvider = {
286
- chat: async (
287
- _h: ChatMessage[],
288
- _u: string,
289
- options?: ChatRequestOptions,
290
- ): Promise<ChatMessage> => {
291
- n += 1;
292
- if (options?.tools?.some((t) => t.name === 'select_agent')) {
293
- // First classify routes; the post-handoff classify hits the wall.
294
- if (n > 2) throw new BudgetExhaustedError('Anthropic', 25, 25.4);
295
- return {
296
- role: 'assistant',
297
- content: '',
298
- toolCalls: [{ id: `c-${n}`, name: 'select_agent', args: { agent_index: 0 } }],
299
- };
300
- }
301
- // The agent turn asks to hand off, forcing the second classify.
302
- return {
303
- role: 'assistant',
304
- content: '',
305
- toolCalls: [
306
- {
307
- id: `h-${n}`,
308
- name: 'request_continuation',
309
- args: { summary: 's', remaining_task: 'more' },
310
- },
311
- ],
312
- };
313
- },
314
- };
315
- const driver = new OrchestratingDriver(
316
- makeRegistry(provider),
317
- [agent({ name: 'A' }), agent({ name: 'B' })],
318
- { sessionKey: 'budget-4b' },
319
- );
320
-
321
- const result = await driver.sendMessage('go');
322
-
323
- assert.is(failureReasonOf(result), 'budget-exhausted');
324
- const history = driver.getRawHistory() as ChatMessage[];
325
- const userMessages = history.filter((m) => m.role === 'user' && m.content === 'go');
326
- assert.is(userMessages.length, 1, "the user's message appears exactly once");
327
- });
328
-
329
- // ── 3. The classifier's own retry ladder ───────────────────────────────────
330
-
331
- Suite('a wall during classification costs exactly one provider call', async () => {
332
- // TWO specialists and no fallback, so `classify` really runs. Before the narrow
333
- // rethrow this was FOUR calls: `classifierRetries + 1` classifier attempts, then
334
- // a doomed agent turn.
335
- const { calls, provider } = walledProvider();
336
- const driver = new OrchestratingDriver(
337
- makeRegistry(provider),
338
- [agent({ name: 'A' }), agent({ name: 'B' })],
339
- { sessionKey: 'budget-5' },
340
- );
341
-
342
- const result = await driver.sendMessage('go');
343
-
344
- assert.is(calls(), 1, 'no ladder clears a budget wall — do not climb it');
345
- assert.is(failureReasonOf(result), 'budget-exhausted');
346
- });
347
-
348
- Suite('a classify-time wall lands on the transcript, without a routing apology', async () => {
349
- // The old ordering emitted "I'm not sure how to help with that…" from the
350
- // no-fallback exit and THEN routed anyway, so the user read a routing excuse
351
- // the classifier never earned, immediately above the real error.
352
- const { provider } = walledProvider();
353
- const driver = new OrchestratingDriver(
354
- makeRegistry(provider),
355
- [agent({ name: 'A' }), agent({ name: 'B' })],
356
- { sessionKey: 'budget-6' },
357
- );
358
-
359
- await driver.sendMessage('go');
360
-
361
- const history = driver.getRawHistory() as ChatMessage[];
362
- const text = history.map((m) => m.content).join('\n');
363
- assert.ok(text.includes('AI usage limit'), 'the real reason is what the user reads');
364
- assert.not.ok(text.includes("I'm not sure how to help with that"));
365
- // The user's own message must survive. `runOrchestratedTurn` dispatches it
366
- // optimistically and leaves the real append to `chatDriver.sendMessage`, which
367
- // a classify-time wall never reaches — so the driver's history held only the
368
- // assistant bubble and the next render dropped what the user had typed.
369
- assert.equal(
370
- history.map((m) => m.role),
371
- ['user', 'assistant'],
372
- 'the question stays above the answer',
373
- );
374
- assert.is(history[0].content, 'go');
375
- });
376
-
377
- Suite('an in-loop wall does not double-append the user message', async () => {
378
- // Narrowness guard for the append above: `reportBudgetExhausted` also serves
379
- // the in-loop path, where `sendMessage` has ALREADY appended the user message.
380
- // Only the classify seam passes one, so this path must be unchanged. ONE
381
- // specialist and no fallback, so `classify` short-circuits and the wall is hit
382
- // inside a real tool loop.
383
- const { provider } = walledProvider();
384
- const driver = new OrchestratingDriver(makeRegistry(provider), [agent({ name: 'Solo' })], {
385
- sessionKey: 'budget-6b',
386
- });
387
-
388
- await driver.sendMessage('go');
389
-
390
- const history = driver.getRawHistory() as ChatMessage[];
391
- assert.is(history.filter((m) => m.role === 'user').length, 1, 'one user message, not two');
392
- });
393
-
394
- Suite('a wall during classification never starts the fallback turn', async () => {
395
- const { calls, provider } = walledProvider();
396
- const driver = new OrchestratingDriver(
397
- makeRegistry(provider),
398
- [agent({ name: 'A' }), agent({ name: 'B' }), agent({ name: 'Catch-all', fallback: true })],
399
- { sessionKey: 'budget-7' },
400
- );
401
-
402
- const result = await driver.sendMessage('go');
403
-
404
- assert.is(calls(), 1, 'the fallback agent is not run into the same wall');
405
- assert.is(failureReasonOf(result), 'budget-exhausted');
406
- });
407
-
408
- Suite('a plain classifier failure still retries and falls back (narrowness guard)', async () => {
409
- // Proves the rethrow did not make every classify failure terminal: only the
410
- // budget wall short-circuits, because only the budget wall is unclearable.
411
- let classifierCalls = 0;
412
- const provider: AIProvider = {
413
- chat: async (
414
- _h: ChatMessage[],
415
- _u: string,
416
- options?: ChatRequestOptions,
417
- ): Promise<ChatMessage> => {
418
- if (options?.tools?.some((t) => t.name === 'select_agent')) {
419
- classifierCalls += 1;
420
- throw new Error('classifier is having a moment');
421
- }
422
- return { role: 'assistant', content: 'fallback handled it' };
423
- },
424
- };
425
- const driver = new OrchestratingDriver(
426
- makeRegistry(provider),
427
- [agent({ name: 'A' }), agent({ name: 'B' }), agent({ name: 'Catch-all', fallback: true })],
428
- { sessionKey: 'budget-8' },
429
- );
430
-
431
- const result = await driver.sendMessage('go');
432
-
433
- assert.is(classifierCalls, 3, 'classifierRetries (2) + the first attempt');
434
- assert.is(result.reason, 'done');
435
- assert.is(failureReasonOf(result), undefined, 'the fallback turn succeeded');
436
- });
437
-
438
- Suite.run();