@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,256 +0,0 @@
1
- import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
2
- import { PERSISTED_SESSION_VERSION, createPersistedSession, isRestorableSession, reconcileAgentSnapshots, resolveNonResumableNotice, toLoadSessionPayload, keepThroughLastUserMessage, trimToRestorableBoundary, unknownAgentSnapshots, } from './session-snapshot';
3
- const asMsg = (o) => o;
4
- // GENC-1351 §5.1: the PersistedSession schema + its pure (de)serialize helpers.
5
- // These have no DOM/storage deps, so they run straight in Node.
6
- const Suite = createLogicSuite('persisted-session-schema');
7
- const base = () => createPersistedSession({
8
- sessionKey: 'k',
9
- host: 'app.example',
10
- messages: [],
11
- pinnedAgentName: 'A',
12
- flowOwnerAgentName: null,
13
- sessionCostUsd: 2,
14
- contextTokens: 10,
15
- contextLimit: 100,
16
- activeModel: 'm',
17
- activeProviderName: 'p',
18
- activeAgentName: 'A',
19
- agentSnapshots: { A: { machine: { state: 'x', context: {} } } },
20
- });
21
- Suite('createPersistedSession stamps version + savedAt and keeps agentSnapshots', () => {
22
- const s = base();
23
- assert.is(s.version, PERSISTED_SESSION_VERSION);
24
- assert.is(typeof s.savedAt, 'string');
25
- assert.ok(s.savedAt.length > 0);
26
- assert.equal(s.agentSnapshots, { A: { machine: { state: 'x', context: {} } } });
27
- });
28
- // GENC-1464 — the blocked latch is deliberately NOT persisted. The snapshot is
29
- // per-stateKey and long-lived, so a persisted latch would survive an out-of-band
30
- // budget raise with no in-element way to clear it: the user would return to a
31
- // permanently dead composer against a budget that has headroom. Re-deriving the
32
- // state (host pre-flight on mount, or the next 402) is strictly safer than
33
- // remembering it. This test exists so a future "helpful" addition trips here and
34
- // has to read that reasoning first — see `FoundationAiAssistant.blocked`'s
35
- // Lifetime paragraph and docs/migration-GENC-1464.md §Scope.
36
- Suite('the blocked latch is deliberately not persisted', () => {
37
- const s = base();
38
- assert.not.ok('blocked' in s, 'blocked must not enter the persisted snapshot');
39
- assert.not.ok('blockedReason' in s, 'blockedReason must not enter the persisted snapshot');
40
- // The per-vendor latches inherit the same lifetime, so they inherit the same
41
- // rule — a persisted per-vendor wall is exactly as stale as a persisted global
42
- // one, just harder to notice because only one vendor looks broken.
43
- assert.not.ok('blockedVendors' in s, 'blockedVendors must not enter the persisted snapshot');
44
- assert.not.ok('blockedVendorReasons' in s, 'blockedVendorReasons must not enter the persisted snapshot');
45
- });
46
- Suite('createPersistedSession defaults agentSnapshots to {}', () => {
47
- const s = createPersistedSession({
48
- sessionKey: 'k',
49
- host: 'h',
50
- messages: [],
51
- pinnedAgentName: null,
52
- flowOwnerAgentName: null,
53
- sessionCostUsd: 0,
54
- });
55
- assert.equal(s.agentSnapshots, {});
56
- });
57
- Suite('isRestorableSession accepts a matching version + host', () => {
58
- assert.ok(isRestorableSession(base(), { host: 'app.example' }));
59
- });
60
- Suite('isRestorableSession rejects version mismatch, cross-host, and malformed input', () => {
61
- assert.ok(!isRestorableSession(Object.assign(Object.assign({}, base()), { version: 999 }), { host: 'app.example' }));
62
- assert.ok(!isRestorableSession(base(), { host: 'other.example' }));
63
- assert.ok(!isRestorableSession(null, { host: 'h' }));
64
- assert.ok(!isRestorableSession({ version: PERSISTED_SESSION_VERSION, host: 'h' }, { host: 'h' }), 'missing messages array → not restorable');
65
- });
66
- Suite('toLoadSessionPayload projects only the redux-restorable fields', () => {
67
- const p = toLoadSessionPayload(base());
68
- assert.is(p.pinnedAgentName, 'A');
69
- assert.is(p.flowOwnerAgentName, null);
70
- // Cost is deliberately NOT projected — the slice re-derives it (and the token
71
- // buckets) from `messages`, so a stored counter would be a second, divergeable
72
- // source of truth.
73
- assert.is(p.sessionCostUsd, undefined);
74
- assert.is(p.contextTokens, 10);
75
- assert.is(p.contextLimit, 100);
76
- assert.is(p.activeModel, 'm');
77
- assert.is(p.activeProviderName, 'p');
78
- assert.equal(p.messages, []);
79
- // agentSnapshots + activeAgentName are applied by the host, not via the slice.
80
- assert.ok(!('agentSnapshots' in p));
81
- assert.ok(!('activeAgentName' in p));
82
- });
83
- Suite('trimToRestorableBoundary returns a clean transcript unchanged (same ref)', () => {
84
- const clean = [
85
- asMsg({ role: 'user', content: 'hi' }),
86
- asMsg({ role: 'assistant', content: 'hello' }),
87
- ];
88
- assert.is(trimToRestorableBoundary(clean), clean);
89
- assert.is(trimToRestorableBoundary([]).length, 0);
90
- });
91
- Suite('trimToRestorableBoundary drops a trailing unresolved interaction, keeps a resolved one', () => {
92
- const user = asMsg({ role: 'user', content: 'hi' });
93
- const pending = asMsg({
94
- role: 'assistant',
95
- content: '',
96
- interaction: { interactionId: '2', componentName: 'x', data: {} },
97
- });
98
- const resolved = asMsg({
99
- role: 'assistant',
100
- content: '',
101
- interaction: {
102
- interactionId: '1',
103
- componentName: 'x',
104
- data: {},
105
- resolved: { status: 'approved' },
106
- },
107
- });
108
- assert.is(trimToRestorableBoundary([user, pending]).length, 1);
109
- assert.is(trimToRestorableBoundary([user, resolved]).length, 2);
110
- });
111
- Suite('trimToRestorableBoundary drops dangling + partially-answered tool calls', () => {
112
- const user = asMsg({ role: 'user', content: 'do x' });
113
- const callAB = asMsg({
114
- role: 'assistant',
115
- content: '',
116
- toolCalls: [
117
- { id: 'a', name: 'f', args: {} },
118
- { id: 'b', name: 'g', args: {} },
119
- ],
120
- });
121
- const resultA = asMsg({
122
- role: 'tool',
123
- content: '',
124
- toolResult: { toolCallId: 'a', content: 'ra' },
125
- });
126
- const resultB = asMsg({
127
- role: 'tool',
128
- content: '',
129
- toolResult: { toolCallId: 'b', content: 'rb' },
130
- });
131
- // No results → drop the whole round back to [user].
132
- assert.is(trimToRestorableBoundary([user, callAB]).length, 1);
133
- // Only A answered → B orphaned → drop the round back to [user].
134
- assert.is(trimToRestorableBoundary([user, callAB, resultA]).length, 1);
135
- // Both answered → kept (ends on a balanced tool result).
136
- assert.is(trimToRestorableBoundary([user, callAB, resultA, resultB]).length, 4);
137
- });
138
- Suite('trimToRestorableBoundary drops a trailing empty assistant placeholder', () => {
139
- const user = asMsg({ role: 'user', content: 'hi' });
140
- const placeholder = asMsg({ role: 'assistant', content: '' });
141
- assert.is(trimToRestorableBoundary([user, placeholder]).length, 1);
142
- });
143
- // GENC-1351: orphaned agent snapshots (dynamic agent arrays — e.g. genesis-create).
144
- Suite('unknownAgentSnapshots flags saved agents missing from the configured set', () => {
145
- const snap = {
146
- agentSnapshots: { 'Guided Booking': {}, Bootstrap: {} },
147
- };
148
- // Bootstrap gone from the current set → flagged; Guided Booking is known.
149
- assert.equal(unknownAgentSnapshots(snap, ['Guided Booking', 'Trades']), ['Bootstrap']);
150
- // All known → none flagged.
151
- assert.equal(unknownAgentSnapshots(snap, ['Guided Booking', 'Bootstrap']), []);
152
- // Empty known set → can't classify yet → none (avoids false positives mid-init).
153
- assert.equal(unknownAgentSnapshots(snap, []), []);
154
- });
155
- // GENC-1351 §6 — `reconcileAgentSnapshots`: the save-time guard against a
156
- // post-restore autosave wiping a mid-flow machine. The bug (regression #1): a
157
- // restore reads a good snapshot, then the restore's own history change fires an
158
- // autosave BEFORE the agent re-activates, so the live capture is empty and, left
159
- // unguarded, `{}` overwrites the machine → the flow resets on the next reload.
160
- const MACHINE = { machine: { value: 'pickInstrument', context: { counterparty: { id: 'c1' } } } };
161
- Suite('reconcileAgentSnapshots: REGRESSION — carries the flow-owner forward when live capture is empty', () => {
162
- // Post-restore, pre-activation: getActiveAgent() is undefined → live is {}, but
163
- // a flow is owned and the good image sits in the cache (seeded on restore).
164
- const { snapshots, cache } = reconcileAgentSnapshots({}, 'Guided Booking', {
165
- 'Guided Booking': MACHINE,
166
- });
167
- assert.equal(snapshots['Guided Booking'], MACHINE, 'the flow-owner machine is preserved, NOT wiped to {}');
168
- assert.equal(cache['Guided Booking'], MACHINE, 'cache keeps the good image');
169
- });
170
- Suite('reconcileAgentSnapshots: a live capture wins and advances the cache', () => {
171
- const FRESH = { machine: { value: 'enterDetails', context: {} } };
172
- const { snapshots, cache } = reconcileAgentSnapshots({ 'Guided Booking': FRESH }, 'Guided Booking', {
173
- 'Guided Booking': MACHINE, // stale cached value
174
- });
175
- assert.equal(snapshots['Guided Booking'], FRESH, 'live image is used (never the stale cache)');
176
- assert.equal(cache['Guided Booking'], FRESH, 'cache advances to the freshly-captured image');
177
- });
178
- Suite('reconcileAgentSnapshots: no flow owner → empty stays empty (no stale carry-forward)', () => {
179
- // Idle / stateless save: nothing to carry, even if the cache holds an old image.
180
- const { snapshots, cache } = reconcileAgentSnapshots({}, null, { 'Guided Booking': MACHINE });
181
- assert.equal(snapshots, {}, 'nothing carried forward without a flow owner');
182
- assert.equal(cache['Guided Booking'], MACHINE, 'an empty save never erases the cache');
183
- });
184
- Suite('reconcileAgentSnapshots: flow owner set but nothing cached → cannot fabricate state', () => {
185
- const { snapshots } = reconcileAgentSnapshots({}, 'Guided Booking', {});
186
- assert.is(snapshots['Guided Booking'], undefined, 'no image to carry → left absent, not invented');
187
- });
188
- Suite('reconcileAgentSnapshots: carries the flow owner while keeping a co-captured other agent', () => {
189
- const OTHER = { machine: { value: 'x' } };
190
- const { snapshots } = reconcileAgentSnapshots({ Other: OTHER }, 'Guided Booking', {
191
- 'Guided Booking': MACHINE,
192
- });
193
- assert.equal(snapshots.Other, OTHER, 'the live non-owner image is kept');
194
- assert.equal(snapshots['Guided Booking'], MACHINE, 'the missing flow-owner is carried forward');
195
- });
196
- Suite('reconcileAgentSnapshots: does not mutate its inputs', () => {
197
- const live = {};
198
- const cacheIn = { 'Guided Booking': MACHINE };
199
- reconcileAgentSnapshots(live, 'Guided Booking', cacheIn);
200
- assert.equal(live, {}, 'live input untouched');
201
- assert.equal(Object.keys(cacheIn), ['Guided Booking'], 'cache input untouched');
202
- });
203
- // GENC-1351 §5.9: the pure notice-resolution helper (message + pre-fill prompt).
204
- // The freeze/marker mechanics live on the element (integration), not here.
205
- Suite('resolveNonResumableNotice: custom message wins, blank falls back to default', () => {
206
- const msgs = [
207
- asMsg({ role: 'user', content: 'first' }),
208
- asMsg({ role: 'assistant', content: 'x' }),
209
- ];
210
- assert.is(resolveNonResumableNotice({
211
- notResumableMessage: 'custom',
212
- messages: msgs,
213
- defaultMessage: 'D',
214
- }).resumeMessage, 'custom');
215
- assert.is(resolveNonResumableNotice({ notResumableMessage: ' ', messages: msgs, defaultMessage: 'D' })
216
- .resumeMessage, 'D', 'a blank custom message falls back to the default');
217
- assert.is(resolveNonResumableNotice({ messages: msgs, defaultMessage: 'D' }).resumeMessage, 'D', 'no custom message → default');
218
- });
219
- Suite('resolveNonResumableNotice: prompt prefers the captured flow prompt, else last user msg', () => {
220
- const msgs = [
221
- asMsg({ role: 'user', content: 'earlier' }),
222
- asMsg({ role: 'assistant', content: 'x' }),
223
- asMsg({ role: 'user', content: 'latest user' }),
224
- ];
225
- assert.is(resolveNonResumableNotice({
226
- flowActivationPrompt: 'the flow prompt',
227
- messages: msgs,
228
- defaultMessage: 'D',
229
- }).resumePrompt, 'the flow prompt', 'captured flow-activating prompt takes precedence');
230
- assert.is(resolveNonResumableNotice({ flowActivationPrompt: null, messages: msgs, defaultMessage: 'D' })
231
- .resumePrompt, 'latest user', 'no flow prompt → falls back to the last user message');
232
- assert.is(resolveNonResumableNotice({ messages: [], defaultMessage: 'D' }).resumePrompt, undefined, 'no flow prompt and no user message → undefined');
233
- });
234
- // GENC-1351 §5.9: the freeze fallback keeps the transcript through the last user
235
- // message, dropping the agent's in-flight turns (issue #3).
236
- Suite('keepThroughLastUserMessage drops trailing agent turns, keeps through the last user msg', () => {
237
- const u1 = asMsg({ role: 'user', content: 'book a flight' });
238
- const a1 = asMsg({ role: 'assistant', content: 'ok' });
239
- const u2 = asMsg({ role: 'user', content: 'show fills for T1' });
240
- const plan = asMsg({
241
- role: 'assistant',
242
- content: '',
243
- toolCalls: [{ id: 'a', name: 'f', args: {} }],
244
- });
245
- const res = asMsg({ role: 'tool', content: '' });
246
- // Trailing agent turns after the last user message → dropped.
247
- assert.equal(keepThroughLastUserMessage([u1, a1, u2, plan, res]), [u1, a1, u2]);
248
- // Already ends on a user message → unchanged (same ref).
249
- const endsUser = [u1, a1, u2];
250
- assert.is(keepThroughLastUserMessage(endsUser), endsUser);
251
- // No user message at all → unchanged (nothing to trim to).
252
- const noUser = [a1, res];
253
- assert.is(keepThroughLastUserMessage(noUser), noUser);
254
- assert.equal(keepThroughLastUserMessage([]).length, 0);
255
- });
256
- Suite.run();
@@ -1,158 +0,0 @@
1
- import { __awaiter } from "tslib";
2
- import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
3
- // Side-effect import — MUST precede the driver import so it subclasses jsdom's
4
- // EventTarget (CustomEvent dispatch then works in node).
5
- import '../../components/chat-driver/align-event-globals';
6
- import { OrchestratingDriver } from '../../components/orchestrating-driver/orchestrating-driver';
7
- import { clearDiagnosticsCursorRegistry } from './diagnostics-cursors';
8
- import { clearPersisterRegistry } from './persister-registry';
9
- import { SessionPersister } from './session-persister';
10
- // GENC-1351 — end-to-end save→restore→route→re-activate proof. The persister unit
11
- // tests exercise `persistSession`/`restore` against a FAKE driver; the driver pin/
12
- // flow-owner tests exercise routing against a real driver. NOTHING wired the two
13
- // together — the exact integration that broke once (bug #2: the live agent's
14
- // `serialize()` was read via `driver.getActiveAgent()` — a host minting fresh
15
- // configs handed back a never-activated closure whose `serialize()` returned
16
- // undefined, so the machine reset on reload). This test closes that gap with a REAL
17
- // `OrchestratingDriver` + a REAL `SessionPersister` + an in-memory provider:
18
- // persist (capture the LIVE agent's state) → restore (prime + re-lock routing) →
19
- // send (route back to the flow-owner without re-classifying) → re-activate aligned.
20
- // `Planner` is `excludeFromClassifier` AND never pinned, so a turn landing on it after
21
- // restore proves the RESTORED flow-owner lock (not the pin, not the classifier) routed
22
- // it — the `setFlowOwner`-on-restore half of the fix.
23
- const KEY = 'e2e-session';
24
- const makeRegistry = (provider) => ({
25
- get: () => provider,
26
- default: () => provider,
27
- defaultName: () => 'test',
28
- names: () => ['test'],
29
- getStatus: () => __awaiter(void 0, void 0, void 0, function* () { return null; }),
30
- listStatuses: () => __awaiter(void 0, void 0, void 0, function* () { return []; }),
31
- });
32
- /** A provider that ends every turn immediately with a plain-text reply (no tools). */
33
- const noToolProvider = () => ({
34
- chat: (_history, _userMessage, _options) => __awaiter(void 0, void 0, void 0, function* () { return ({ role: 'assistant', content: 'ok' }); }),
35
- });
36
- const agentCfg = (overrides) => (Object.assign({ description: 'test agent' }, overrides));
37
- /** In-memory persistence provider (single session), enough for one save + one load. */
38
- function makeMemoryProvider() {
39
- let stored = null;
40
- return {
41
- saveDebounceMs: 0,
42
- save: (_k, snap) => __awaiter(this, void 0, void 0, function* () {
43
- stored = snap;
44
- }),
45
- load: () => __awaiter(this, void 0, void 0, function* () { return stored; }),
46
- clear: () => __awaiter(this, void 0, void 0, function* () {
47
- stored = null;
48
- }),
49
- get current() {
50
- return stored;
51
- },
52
- };
53
- }
54
- /** The functional store shape the persister reads scalars from / hydrates into. */
55
- function makeStoreFake(over = {}) {
56
- return {
57
- store: {
58
- aiAssistant: Object.assign({ messages: [], flowOwnerAgentName: null, flowActivationPrompt: null, activeAgent: undefined, pinnedAgentName: null,
59
- // The persister reads `sessionUsage.costUsd` when building a snapshot, so this
60
- // has to be the object rather than a bare cost scalar.
61
- sessionUsage: {
62
- costUsd: 0,
63
- uncachedInputTokens: 0,
64
- cacheReadTokens: 0,
65
- cacheWriteTokens: 0,
66
- outputTokens: 0,
67
- }, contextTokens: 0, contextLimit: 0, activeModel: undefined, activeProviderName: undefined, restoring: false }, over),
68
- },
69
- actions: {
70
- aiAssistant: {
71
- loadSession: () => { },
72
- setInputValue: () => { },
73
- },
74
- },
75
- };
76
- }
77
- function makePersister(driver, storeFake, provider) {
78
- const deps = {
79
- stateKey: KEY,
80
- getPersistence: () => ({ enabled: true, provider: provider }),
81
- getAgentNames: () => ['Genesis Assistant', 'Planner'],
82
- getDriver: () => driver,
83
- getStore: () => storeFake,
84
- getDiagnosticEntries: () => [],
85
- };
86
- return new SessionPersister(deps);
87
- }
88
- const tick = () => new Promise((r) => {
89
- setTimeout(r, 0);
90
- });
91
- const Suite = createLogicSuite('stateful-restore-e2e');
92
- Suite.before.each(() => {
93
- clearPersisterRegistry();
94
- clearDiagnosticsCursorRegistry();
95
- });
96
- Suite('a flow-owned stateful agent survives persist → restore → resume, routing back without re-classifying', () => __awaiter(void 0, void 0, void 0, function* () {
97
- var _a, _b;
98
- const provider = makeMemoryProvider();
99
- // ── Persist side ────────────────────────────────────────────────────────────
100
- // `stateA` is the Planner's live state bag; `serialize` closes over it, exactly
101
- // like a real stateful agent's machine snapshot.
102
- let stateA = { cursor: 0 };
103
- const planner1 = agentCfg({
104
- name: 'Planner',
105
- excludeFromClassifier: true,
106
- onActivate: (_a) => __awaiter(void 0, [_a], void 0, function* ({ restored }) {
107
- if (restored !== undefined)
108
- stateA = restored;
109
- }),
110
- serialize: () => stateA,
111
- systemPrompt: () => 'planner',
112
- });
113
- const driver1 = new OrchestratingDriver(makeRegistry(noToolProvider()), [agentCfg({ name: 'Genesis Assistant' }), planner1], { sessionKey: KEY });
114
- // Flow-owned but NOT pinned, then run a turn: the flow-owner lock routes the
115
- // Planner (it's excludeFromClassifier, so the classifier could never pick it).
116
- driver1.setFlowOwner('Planner');
117
- yield driver1.sendMessage('go');
118
- assert.is((_a = driver1.getActiveAgent()) === null || _a === void 0 ? void 0 : _a.name, 'Planner', 'flow-owner routed the Planner on persist side');
119
- // The flow advances — mutate the SAME object `serialize()` returns.
120
- stateA.cursor = 3;
121
- // The store mirrors what main.ts keeps: the flow-owner lock lives in the store
122
- // (that's the scalar `persistSession` reads).
123
- const storeFake1 = makeStoreFake({ flowOwnerAgentName: 'Planner' });
124
- const persister1 = makePersister(driver1, storeFake1, provider);
125
- persister1.scheduleSave();
126
- yield tick();
127
- yield tick();
128
- const saved = provider.current;
129
- assert.ok(saved, 'a snapshot was persisted');
130
- assert.is(saved.flowOwnerAgentName, 'Planner', 'flow-owner lock persisted');
131
- // Bug-#2 assertion: the LIVE (activated) agent's serialize was captured via
132
- // driver.getActiveAgent(), so the advanced state is in the snapshot.
133
- assert.equal(saved.agentSnapshots.Planner, { cursor: 3 }, "live agent's advanced state captured (not a never-activated closure)");
134
- // ── Restore side (fresh instances share the same in-memory provider) ──────────
135
- let stateB = { cursor: -1 }; // sentinel; restore must overwrite
136
- const planner2 = agentCfg({
137
- name: 'Planner',
138
- excludeFromClassifier: true,
139
- onActivate: (_a) => __awaiter(void 0, [_a], void 0, function* ({ restored }) {
140
- if (restored !== undefined)
141
- stateB = restored;
142
- }),
143
- serialize: () => stateB,
144
- systemPrompt: () => 'planner',
145
- });
146
- const driver2 = new OrchestratingDriver(makeRegistry(noToolProvider()), [agentCfg({ name: 'Genesis Assistant' }), planner2], { sessionKey: KEY });
147
- const storeFake2 = makeStoreFake();
148
- const persister2 = makePersister(driver2, storeFake2, provider);
149
- yield persister2.restore();
150
- // The restored flow-owner lock (setFlowOwner-on-restore) routes the next turn
151
- // back to the Planner without re-classifying.
152
- yield driver2.sendMessage('again');
153
- assert.is((_b = driver2.getActiveAgent()) === null || _b === void 0 ? void 0 : _b.name, 'Planner', 'restored flow-owner routed the Planner (excludeFromClassifier + unpinned ⇒ only the restored lock could have)');
154
- // primeRestoredAgentState → onActivate({ restored }) fed the snapshot back, so
155
- // the fresh Planner resumed exactly where the persisted one left off.
156
- assert.equal(stateB, { cursor: 3 }, 'restored agent re-activated aligned to the persisted state');
157
- }));
158
- Suite.run();
@@ -1,52 +0,0 @@
1
- import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
2
- import { resolveExclusiveLoadingStyle } from './animation-exclusivity';
3
- const suite = createLogicSuite('resolveExclusiveLoadingStyle');
4
- suite('leaves a selection with only the dots loading style untouched', () => {
5
- const next = ['loading', 'halo'];
6
- assert.equal(resolveExclusiveLoadingStyle(next, ['halo']), ['loading', 'halo']);
7
- });
8
- suite('leaves a selection with only the waves loading style untouched', () => {
9
- const next = ['waves', 'halo'];
10
- assert.equal(resolveExclusiveLoadingStyle(next, ['halo']), ['waves', 'halo']);
11
- });
12
- suite('leaves a selection with no loading style untouched', () => {
13
- assert.equal(resolveExclusiveLoadingStyle(['halo'], []), ['halo']);
14
- });
15
- suite('drops dots when waves was just added on top of dots', () => {
16
- // Previously dots was on; user ticks waves → waves wins, dots removed.
17
- const result = resolveExclusiveLoadingStyle(['loading', 'waves', 'halo'], ['loading', 'halo']);
18
- assert.equal(result, ['waves', 'halo']);
19
- });
20
- suite('drops waves when dots was just added on top of waves', () => {
21
- // Previously waves was on; user ticks dots → dots wins, waves removed.
22
- const result = resolveExclusiveLoadingStyle(['loading', 'waves', 'halo'], ['waves', 'halo']);
23
- assert.equal(result, ['loading', 'halo']);
24
- });
25
- suite('preserves the order of the surviving selection', () => {
26
- const result = resolveExclusiveLoadingStyle(['halo', 'loading', 'waves'], ['halo', 'loading']);
27
- // waves was just added → loading dropped; halo and waves keep their order.
28
- assert.equal(result, ['halo', 'waves']);
29
- });
30
- suite('falls back to dots when both are present with no prior state (e.g. bad config)', () => {
31
- const result = resolveExclusiveLoadingStyle(['loading', 'waves', 'halo'], []);
32
- assert.equal(result, ['loading', 'halo']);
33
- });
34
- suite('falls back to dots when both were already present (neither newly added)', () => {
35
- const result = resolveExclusiveLoadingStyle(['loading', 'waves'], ['loading', 'waves', 'halo']);
36
- assert.equal(result, ['loading']);
37
- });
38
- suite('handles an empty selection', () => {
39
- assert.equal(resolveExclusiveLoadingStyle([], ['loading']), []);
40
- });
41
- suite('switching between any two loading styles keeps only the newly added one', () => {
42
- // flowingWaves was on; user picks plasma → plasma wins, flowingWaves dropped.
43
- assert.equal(resolveExclusiveLoadingStyle(['flowingWaves', 'plasma', 'halo'], ['flowingWaves', 'halo']), ['plasma', 'halo']);
44
- // plasma was on; user picks the dots → dots win, plasma dropped.
45
- assert.equal(resolveExclusiveLoadingStyle(['plasma', 'loading'], ['plasma']), ['loading']);
46
- });
47
- suite('drops all but the newly added style when several are somehow selected', () => {
48
- const result = resolveExclusiveLoadingStyle(['loading', 'waves', 'flowingWaves', 'plasma'], ['loading', 'waves', 'plasma']);
49
- // flowingWaves is the only one absent from `previous`, so it wins.
50
- assert.equal(result, ['flowingWaves']);
51
- });
52
- suite.run();
@@ -1,53 +0,0 @@
1
- import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
2
- import { collectSessionModels } from './collect-session-models';
3
- const assistantMsg = (overrides = {}) => (Object.assign({ role: 'assistant', content: '' }, overrides));
4
- const statusEntry = (name, model, provider) => ({
5
- name,
6
- isDefault: false,
7
- status: { model, provider },
8
- });
9
- const suite = createLogicSuite('collectSessionModels');
10
- suite('collects unique models from assistant messages', () => {
11
- var _a, _b, _c, _d;
12
- const models = collectSessionModels([
13
- assistantMsg({ model: 'claude-sonnet-4-6' }),
14
- assistantMsg({ model: 'claude-haiku-4-5-20251001' }),
15
- assistantMsg({ model: 'claude-sonnet-4-6', provider: 'anthropic' }),
16
- assistantMsg({ model: 'gemini-2.5-pro', providerName: 'high', provider: 'gemini' }),
17
- ], []);
18
- assert.is(models.length, 3);
19
- assert.is((_a = models[0]) === null || _a === void 0 ? void 0 : _a.model, 'claude-sonnet-4-6');
20
- assert.is((_b = models[0]) === null || _b === void 0 ? void 0 : _b.provider, 'anthropic');
21
- assert.is((_c = models[2]) === null || _c === void 0 ? void 0 : _c.model, 'gemini-2.5-pro');
22
- assert.is((_d = models[2]) === null || _d === void 0 ? void 0 : _d.provider, 'gemini');
23
- });
24
- suite('resolves vendor from providerStatuses when messages lack provider', () => {
25
- var _a;
26
- const models = collectSessionModels([assistantMsg({ model: 'gemini-3.1-flash-lite', providerName: 'low' })], [statusEntry('low', 'gemini-3.1-flash-lite', 'gemini')]);
27
- assert.is(models.length, 1);
28
- assert.is((_a = models[0]) === null || _a === void 0 ? void 0 : _a.provider, 'gemini');
29
- });
30
- suite('does not persist registry tier names as provider', () => {
31
- var _a, _b;
32
- const models = collectSessionModels([assistantMsg({ model: 'gemini-3.1-flash-lite', providerName: 'low' })], []);
33
- assert.is((_a = models[0]) === null || _a === void 0 ? void 0 : _a.provider, undefined);
34
- assert.is('providerName' in ((_b = models[0]) !== null && _b !== void 0 ? _b : {}), false);
35
- });
36
- suite('includes models from sub-agent traces', () => {
37
- var _a;
38
- const models = collectSessionModels([
39
- assistantMsg({
40
- toolCalls: [
41
- {
42
- id: 'tc1',
43
- name: 'spawn',
44
- args: {},
45
- subAgentTrace: [assistantMsg({ model: 'claude-haiku-4-5-20251001' })],
46
- },
47
- ],
48
- }),
49
- ], []);
50
- assert.is(models.length, 1);
51
- assert.is((_a = models[0]) === null || _a === void 0 ? void 0 : _a.model, 'claude-haiku-4-5-20251001');
52
- });
53
- suite.run();