@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,136 +0,0 @@
1
- import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
2
- import { WebStorageSessionProvider } from './session-persistence-provider';
3
- import { createPersistedSession } from './session-snapshot';
4
-
5
- // GENC-1351 §5.2: the WebStorageSessionProvider against a fake Storage (no real
6
- // sessionStorage in Node). Covers round-trip, absent/corrupt entries, clear,
7
- // and keyPrefix isolation.
8
-
9
- /** Minimal in-memory Storage matching the DOM `Storage` shape the provider uses. */
10
- function fakeStorage(): Storage {
11
- const m = new Map<string, string>();
12
- return {
13
- get length() {
14
- return m.size;
15
- },
16
- clear: () => m.clear(),
17
- getItem: (k: string) => (m.has(k) ? m.get(k)! : null),
18
- key: (i: number) => [...m.keys()][i] ?? null,
19
- removeItem: (k: string) => {
20
- m.delete(k);
21
- },
22
- setItem: (k: string, v: string) => {
23
- m.set(k, v);
24
- },
25
- } as Storage;
26
- }
27
-
28
- const snap = (sessionKey: string) =>
29
- createPersistedSession({
30
- sessionKey,
31
- host: 'app.example',
32
- messages: [],
33
- pinnedAgentName: null,
34
- flowOwnerAgentName: null,
35
- sessionCostUsd: 0,
36
- });
37
-
38
- const Suite = createLogicSuite('web-storage-session-provider');
39
-
40
- Suite('save → load round-trips a snapshot', async () => {
41
- const provider = new WebStorageSessionProvider(fakeStorage());
42
- const s = snap('s1');
43
- await provider.save('s1', s);
44
- const loaded = await provider.load('s1');
45
- // Compare through JSON: createPersistedSession leaves optional keys undefined,
46
- // which JSON drops — so normalise both sides the same way.
47
- assert.equal(loaded, JSON.parse(JSON.stringify(s)));
48
- });
49
-
50
- Suite('load returns null for an unknown key', async () => {
51
- const provider = new WebStorageSessionProvider(fakeStorage());
52
- assert.is(await provider.load('missing'), null);
53
- });
54
-
55
- Suite('load returns null (not throw) on a corrupt entry', async () => {
56
- const storage = fakeStorage();
57
- storage.setItem('genesis-ai-session:s1', '{ not valid json');
58
- const provider = new WebStorageSessionProvider(storage);
59
- assert.is(await provider.load('s1'), null);
60
- });
61
-
62
- Suite('clear removes the snapshot', async () => {
63
- const provider = new WebStorageSessionProvider(fakeStorage());
64
- await provider.save('s1', snap('s1'));
65
- await provider.clear('s1');
66
- assert.is(await provider.load('s1'), null);
67
- });
68
-
69
- Suite('keyPrefix isolates snapshots stored in the same Storage', async () => {
70
- const storage = fakeStorage();
71
- const a = new WebStorageSessionProvider(storage, { keyPrefix: 'appA' });
72
- const b = new WebStorageSessionProvider(storage, { keyPrefix: 'appB' });
73
- await a.save('k', snap('k'));
74
- assert.is(await b.load('k'), null);
75
- assert.ok(await a.load('k'));
76
- });
77
-
78
- Suite('saveDebounceMs defaults to 0 (web storage is sync) and is overridable', () => {
79
- assert.is(new WebStorageSessionProvider(fakeStorage()).saveDebounceMs, 0);
80
- assert.is(
81
- new WebStorageSessionProvider(fakeStorage(), { saveDebounceMs: 1500 }).saveDebounceMs,
82
- 1500,
83
- );
84
- });
85
-
86
- // GENC-1351: user preferences (remember-chat + display toggles) — persisted
87
- // independently of the snapshot and of the enabled toggle.
88
- Suite('loadPreferences is undefined until set, then round-trips a blob', async () => {
89
- const provider = new WebStorageSessionProvider(fakeStorage());
90
- assert.is(
91
- await provider.loadPreferences('s1'),
92
- undefined,
93
- 'never saved → undefined (use defaults)',
94
- );
95
- const prefs = {
96
- enabled: false,
97
- showToolCalls: true,
98
- showThinkingSteps: true,
99
- showNarration: false,
100
- showAgentSwitchIndicator: true,
101
- animations: ['dots'],
102
- };
103
- await provider.savePreferences('s1', prefs);
104
- assert.equal(await provider.loadPreferences('s1'), prefs);
105
- });
106
-
107
- Suite('loadPreferences returns undefined (not throw) on a corrupt entry', async () => {
108
- const storage = fakeStorage();
109
- storage.setItem('genesis-ai-session:prefs:s1', '{ not valid json');
110
- const provider = new WebStorageSessionProvider(storage);
111
- assert.is(await provider.loadPreferences('s1'), undefined);
112
- });
113
-
114
- Suite('clear wipes the snapshot but keeps the preferences', async () => {
115
- const provider = new WebStorageSessionProvider(fakeStorage());
116
- await provider.save('s1', snap('s1'));
117
- await provider.savePreferences('s1', { enabled: false, showToolCalls: true });
118
- await provider.clear('s1');
119
- assert.is(await provider.load('s1'), null, 'snapshot cleared');
120
- assert.equal(
121
- await provider.loadPreferences('s1'),
122
- { enabled: false, showToolCalls: true },
123
- 'preferences survive clear',
124
- );
125
- });
126
-
127
- Suite('preferences are keyPrefix-isolated too', async () => {
128
- const storage = fakeStorage();
129
- const a = new WebStorageSessionProvider(storage, { keyPrefix: 'appA' });
130
- const b = new WebStorageSessionProvider(storage, { keyPrefix: 'appB' });
131
- await a.savePreferences('k', { enabled: true });
132
- assert.is(await b.loadPreferences('k'), undefined);
133
- assert.equal(await a.loadPreferences('k'), { enabled: true });
134
- });
135
-
136
- Suite.run();
@@ -1,226 +0,0 @@
1
- import type { CostSessionRecord } from '../../utils/cost-session-history';
2
- import type { DiagnosticEntry } from './diagnostics';
3
- import type { PersistedSession } from './session-snapshot';
4
-
5
- /**
6
- * Storage backend for chat-session persistence. The library ships this
7
- * interface only and wires nothing by default; the host app provides an
8
- * implementation and opts in via `chatConfig` (GENC-1351 §5.2/D2). Every method
9
- * is async so a server-backed provider satisfies the same contract as a
10
- * browser-storage one.
11
- *
12
- * @public
13
- */
14
- export interface SessionPersistenceProvider {
15
- /**
16
- * Minimum milliseconds between autosaves for this backend — a throttle the
17
- * assistant applies to its "save on every transcript change" signal. `0`
18
- * means save as soon as the current tick settles (coalescing only writes
19
- * within the same tick), which is right for synchronous, cheap storage like
20
- * `sessionStorage`/`localStorage`. A network/server backend should set a
21
- * higher value (e.g. 1000–2000) so it doesn't hammer the wire mid tool-loop.
22
- *
23
- * **Required** — not optional — so every implementation makes a deliberate
24
- * choice for its own write-cost profile rather than inheriting a guess.
25
- */
26
- saveDebounceMs: number;
27
- /** Load the snapshot for `sessionKey`, or `null` if none / unreadable. */
28
- load(sessionKey: string): Promise<PersistedSession | null>;
29
- /** Persist `snapshot` for `sessionKey`, overwriting any existing one. */
30
- save(sessionKey: string, snapshot: PersistedSession): Promise<void>;
31
- /** Remove any stored snapshot for `sessionKey`. */
32
- clear(sessionKey: string): Promise<void>;
33
- /**
34
- * Load the user's remembered UI preferences for `sessionKey` (GENC-1351) — the
35
- * "remember chat" toggle plus the cog-menu display switches. Stored
36
- * **independently of the session snapshot** (survives `clear`) and **independently
37
- * of the `enabled` toggle** — a user's display choices persist whether or not
38
- * they remember the chat. `undefined` means none were ever saved → the host's
39
- * configured `chatConfig.ui` defaults apply. Only the fields the user has changed
40
- * are present; absent fields keep their config default. Optional: a provider that
41
- * omits it simply doesn't remember preferences across reloads.
42
- */
43
- loadPreferences?(sessionKey: string): Promise<SessionPreferences | undefined>;
44
- /** Persist the user's UI preferences for `sessionKey` (kept across `clear`). */
45
- savePreferences?(sessionKey: string, preferences: SessionPreferences): Promise<void>;
46
-
47
- /**
48
- * Scoped cost/build history — the per-project usage rows behind the Usage tab.
49
- *
50
- * ‼️ **Keyed by SCOPE, not `sessionKey`**, unlike every other method on this interface.
51
- * A row holds one project's lifetime usage and the list spans projects, so keying it
52
- * per session would defeat the model. The scope comes from
53
- * `chatConfig.costHistory.scope`, falling back to the element `id`, then `'default'`.
54
- *
55
- * Optional: when a provider omits these, the assistant falls back to `localStorage`, so
56
- * hosts that don't implement them are unaffected. Implementing them moves the ledger to
57
- * the host's own backend, which is what stops it being stranded in one browser.
58
- *
59
- * Read/written **independently of the `enabled` toggle** (as with preferences and
60
- * diagnostics): usage accounting is not conversation content, so a user turning off
61
- * chat retention should not lose their cost ledger. Best-effort — a failed write must
62
- * never affect the chat.
63
- *
64
- * `undefined` from `loadCostHistory` means "nothing stored", the same as `[]`.
65
- */
66
- loadCostHistory?(scope: string): Promise<CostSessionRecord[] | undefined>;
67
- /** Replace the stored row list for `scope`. See {@link SessionPersistenceProvider.loadCostHistory}. */
68
- saveCostHistory?(scope: string, records: CostSessionRecord[]): Promise<void>;
69
-
70
- /**
71
- * Server-saved diagnostics (GENC-1351 §5.8) — a **forward-only append** stream
72
- * that lets the downloadable debug log span the whole session lifetime, not just
73
- * the current page load. Optional and **capacious-backends-only**: `WebStorage`
74
- * omits it (the log dwarfs the restore core), so the feature is simply off there.
75
- * The element emits *what happened* (new entries since the last flush); the
76
- * provider owns *how it's filed* (one object per session / per run / per day —
77
- * its call). Best-effort: a failed write must never affect the chat.
78
- *
79
- * Captured **independently of the `enabled` toggle** (like preferences): the
80
- * forensic log is orthogonal to whether the *chat* is remembered, so it runs
81
- * whenever a provider implements this — even with "remember chat" off.
82
- */
83
- appendDiagnostics?(sessionKey: string, entries: DiagnosticEntry[]): Promise<void>;
84
- /** Load the full diagnostics stream for `sessionKey` (all appends, any run), for download-reassembly. */
85
- loadDiagnostics?(sessionKey: string): Promise<DiagnosticEntry[]>;
86
- /** Hard-delete the diagnostics stream for `sessionKey`. Host/GDPR use — NOT wired to the UI Clear. */
87
- clearDiagnostics?(sessionKey: string): Promise<void>;
88
- /**
89
- * Min ms between diagnostics appends — coarser than `saveDebounceMs` because
90
- * the log is heavy. Defaults to a sensible coarse value if omitted.
91
- */
92
- diagnosticsDebounceMs?: number;
93
- }
94
-
95
- /**
96
- * User-changeable settings that persist across reloads, independently of the chat
97
- * snapshot and of the "remember chat" toggle (GENC-1351). All optional — an absent
98
- * field keeps its `chatConfig.ui` default rather than being forced off.
99
- *
100
- * @public
101
- */
102
- export interface SessionPreferences {
103
- /** "Remember chat" — whether the session snapshot itself is persisted. */
104
- enabled?: boolean;
105
- showToolCalls?: boolean;
106
- showThinkingSteps?: boolean;
107
- showNarration?: boolean;
108
- showAgentSwitchIndicator?: boolean;
109
- /** Enabled loading-animation ids (`AiAssistantAnimation` values, as plain strings). */
110
- animations?: string[];
111
- }
112
-
113
- /**
114
- * Host-supplied persistence configuration, passed to the assistant via its
115
- * `persistence` property. Persistence stays inert unless `enabled` is true and
116
- * a `provider` is set — so existing consumers are unaffected (GENC-1351 D2/D3).
117
- *
118
- * @public
119
- */
120
- export interface SessionPersistenceConfig {
121
- /** Master switch — persistence does nothing unless this is true. */
122
- enabled?: boolean;
123
- /** Storage backend. Required for persistence to take effect. */
124
- provider?: SessionPersistenceProvider;
125
- }
126
-
127
- const DEFAULT_KEY_PREFIX = 'genesis-ai-session';
128
-
129
- /**
130
- * A {@link SessionPersistenceProvider} backed by a Web Storage area. Both
131
- * `window.sessionStorage` and `window.localStorage` implement the `Storage`
132
- * API, so one class serves both: point it at `sessionStorage` for
133
- * refresh-survival that clears on tab close (the showcase default, O6), or at
134
- * `localStorage` for durable local persistence.
135
- *
136
- * Opt-in: a host constructs it and passes it through `chatConfig`; the library
137
- * never instantiates it on its own.
138
- *
139
- * @public
140
- */
141
- export class WebStorageSessionProvider implements SessionPersistenceProvider {
142
- private readonly storage: Storage;
143
- private readonly keyPrefix: string;
144
- /**
145
- * Web storage is synchronous and cheap, so the default is `0` — save as
146
- * soon as the tick settles, no throttle. Override via `options.saveDebounceMs`
147
- * (e.g. if pointing at `localStorage` and you'd rather coalesce large writes).
148
- */
149
- readonly saveDebounceMs: number;
150
-
151
- constructor(storage: Storage, options: { keyPrefix?: string; saveDebounceMs?: number } = {}) {
152
- this.storage = storage;
153
- this.keyPrefix = options.keyPrefix ?? DEFAULT_KEY_PREFIX;
154
- this.saveDebounceMs = options.saveDebounceMs ?? 0;
155
- }
156
-
157
- private storageKey(sessionKey: string): string {
158
- return `${this.keyPrefix}:${sessionKey}`;
159
- }
160
-
161
- /** Preferences key, namespaced apart from the session key so `clear` leaves it. */
162
- private preferencesKey(sessionKey: string): string {
163
- return `${this.keyPrefix}:prefs:${sessionKey}`;
164
- }
165
-
166
- async load(sessionKey: string): Promise<PersistedSession | null> {
167
- const raw = this.storage.getItem(this.storageKey(sessionKey));
168
- if (!raw) return null;
169
- try {
170
- return JSON.parse(raw) as PersistedSession;
171
- } catch {
172
- // A corrupt or partially-written entry is treated as absent rather than
173
- // throwing — a bad snapshot must never block the assistant from starting.
174
- return null;
175
- }
176
- }
177
-
178
- async save(sessionKey: string, snapshot: PersistedSession): Promise<void> {
179
- const serialized = JSON.stringify(snapshot);
180
- try {
181
- this.storage.setItem(this.storageKey(sessionKey), serialized);
182
- } catch (e) {
183
- if (isQuotaExceeded(e)) {
184
- throw new Error(
185
- `WebStorageSessionProvider: storage quota exceeded saving session "${sessionKey}" ` +
186
- `(${serialized.length} chars). Compact the conversation or use a server-backed provider.`,
187
- );
188
- }
189
- throw e;
190
- }
191
- }
192
-
193
- async clear(sessionKey: string): Promise<void> {
194
- this.storage.removeItem(this.storageKey(sessionKey));
195
- }
196
-
197
- async loadPreferences(sessionKey: string): Promise<SessionPreferences | undefined> {
198
- const raw = this.storage.getItem(this.preferencesKey(sessionKey));
199
- if (raw === null) return undefined;
200
- try {
201
- return JSON.parse(raw) as SessionPreferences;
202
- } catch {
203
- // A corrupt entry is treated as "no preferences" — never block startup.
204
- return undefined;
205
- }
206
- }
207
-
208
- async savePreferences(sessionKey: string, preferences: SessionPreferences): Promise<void> {
209
- // Small blob — quota is a non-issue, so no QuotaExceeded guard.
210
- this.storage.setItem(this.preferencesKey(sessionKey), JSON.stringify(preferences));
211
- }
212
- }
213
-
214
- /**
215
- * Web Storage signals an exceeded quota with different `name`s across browsers
216
- * (`QuotaExceededError` everywhere modern; `NS_ERROR_DOM_QUOTA_REACHED` on
217
- * older Firefox). The legacy numeric `code`s (22 / 1014) are intentionally not
218
- * checked — they add nothing for the browsers this app targets.
219
- */
220
- function isQuotaExceeded(e: unknown): boolean {
221
- return (
222
- typeof DOMException !== 'undefined' &&
223
- e instanceof DOMException &&
224
- (e.name === 'QuotaExceededError' || e.name === 'NS_ERROR_DOM_QUOTA_REACHED')
225
- );
226
- }
@@ -1,141 +0,0 @@
1
- import type { ChatMessage } from '@genesislcap/foundation-ai';
2
- import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
3
- import { aiAssistantSlice, createDefaultSessionState } from '../ai-assistant-slice';
4
- import { WebStorageSessionProvider } from './session-persistence-provider';
5
- import {
6
- createPersistedSession,
7
- isRestorableSession,
8
- toLoadSessionPayload,
9
- trimToRestorableBoundary,
10
- } from './session-snapshot';
11
-
12
- // GENC-1351 Phase 1 integration: the page-reload cycle the element wires —
13
- // save on idle → reload drops the in-memory store + provider → restore hydrates
14
- // a fresh store. Exercises the REAL provider + schema + reducer composed; the
15
- // custom-element glue (connectedCallback → maybeRestoreSession) is covered by a
16
- // manual app test.
17
-
18
- /** In-memory Storage that survives the simulated reload (like sessionStorage does). */
19
- function fakeStorage(): Storage {
20
- const m = new Map<string, string>();
21
- return {
22
- get length() {
23
- return m.size;
24
- },
25
- clear: () => m.clear(),
26
- getItem: (k: string) => (m.has(k) ? m.get(k)! : null),
27
- key: (i: number) => [...m.keys()][i] ?? null,
28
- removeItem: (k: string) => {
29
- m.delete(k);
30
- },
31
- setItem: (k: string, v: string) => {
32
- m.set(k, v);
33
- },
34
- } as Storage;
35
- }
36
-
37
- const asMsg = (o: Record<string, unknown>): ChatMessage => o as unknown as ChatMessage;
38
- const reduce = aiAssistantSlice.reducer;
39
- const { loadSession } = aiAssistantSlice.actions;
40
-
41
- const Suite = createLogicSuite('session-persistence-integration');
42
-
43
- Suite('save → reload → restore brings back messages, selection, counters', async () => {
44
- const storage = fakeStorage(); // persists across the simulated reload
45
- const host = 'app.example';
46
- const key = 'assistant::Title';
47
-
48
- // 1. A live session reaches idle with history + a pinned agent.
49
- const messages = [
50
- asMsg({ role: 'user', content: 'book a trade' }),
51
- asMsg({ role: 'assistant', content: 'sure' }),
52
- ];
53
- await new WebStorageSessionProvider(storage).save(
54
- key,
55
- createPersistedSession({
56
- sessionKey: key,
57
- host,
58
- messages,
59
- activeAgentName: 'Guided Booking',
60
- pinnedAgentName: 'Guided Booking',
61
- flowOwnerAgentName: null,
62
- sessionCostUsd: 0.5,
63
- contextTokens: 800,
64
- contextLimit: 200000,
65
- activeModel: 'claude-sonnet-4-6',
66
- activeProviderName: 'anthropic',
67
- }),
68
- );
69
-
70
- // 2. "Reload": the in-memory store + provider instance are gone; storage remains.
71
- const freshStore = createDefaultSessionState();
72
- const snapshot = await new WebStorageSessionProvider(storage).load(key);
73
- assert.ok(snapshot, 'snapshot survives the reload');
74
- assert.ok(isRestorableSession(snapshot, { host }), 'snapshot is restorable for this host');
75
-
76
- // 3. Restore into the fresh store (what maybeRestoreSession does).
77
- const restored = reduce(freshStore, loadSession(toLoadSessionPayload(snapshot!)));
78
- assert.is(restored.messages.length, 2);
79
- assert.is(restored.pinnedAgentName, 'Guided Booking');
80
- // Cost comes from the restored transcript, NOT the snapshot's stored counter — so it
81
- // is whatever the messages actually carry. Computed independently here rather than
82
- // by calling the summing helper under test.
83
- const expectedCost = restored.messages.reduce(
84
- (sum, m) => sum + (m.cost ?? 0) + (m.externalCostUsd ?? 0),
85
- 0,
86
- );
87
- assert.is(restored.sessionUsage.costUsd, expectedCost);
88
- assert.is(restored.contextTokens, 800);
89
- assert.is(restored.contextLimit, 200000);
90
- assert.is(restored.activeModel, 'claude-sonnet-4-6');
91
- assert.is(restored.activeProviderName, 'anthropic');
92
- assert.is(restored.state, 'idle');
93
- });
94
-
95
- Suite('a cross-host snapshot is rejected on restore', async () => {
96
- const storage = fakeStorage();
97
- const provider = new WebStorageSessionProvider(storage);
98
- await provider.save(
99
- 'k',
100
- createPersistedSession({
101
- sessionKey: 'k',
102
- host: 'app.example',
103
- messages: [asMsg({ role: 'user', content: 'x' })],
104
- pinnedAgentName: null,
105
- flowOwnerAgentName: null,
106
- sessionCostUsd: 0,
107
- }),
108
- );
109
- const snapshot = await provider.load('k');
110
- assert.ok(snapshot);
111
- assert.ok(!isRestorableSession(snapshot, { host: 'evil.example' }));
112
- });
113
-
114
- Suite('a dangling interaction tail is scrubbed before restore', async () => {
115
- const storage = fakeStorage();
116
- const provider = new WebStorageSessionProvider(storage);
117
- const messages = [
118
- asMsg({ role: 'user', content: 'hi' }),
119
- asMsg({
120
- role: 'assistant',
121
- content: '',
122
- interaction: { interactionId: '1', componentName: 'x', data: {} },
123
- }),
124
- ];
125
- await provider.save(
126
- 'k',
127
- createPersistedSession({
128
- sessionKey: 'k',
129
- host: 'h',
130
- messages,
131
- pinnedAgentName: null,
132
- flowOwnerAgentName: null,
133
- sessionCostUsd: 0,
134
- }),
135
- );
136
- const snapshot = await provider.load('k');
137
- const scrubbed = trimToRestorableBoundary(snapshot!.messages);
138
- assert.is(scrubbed.length, 1, 'the unresolved interaction message is dropped');
139
- });
140
-
141
- Suite.run();