@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,185 +0,0 @@
1
- import type {
2
- AIProvider,
3
- AIProviderRegistry,
4
- ChatMessage,
5
- ChatRequestOptions,
6
- ChatThinkingPolicy,
7
- } from '@genesislcap/foundation-ai';
8
- import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
9
- import type { AgentConfig } from '../../config/config';
10
- // Side-effect import — MUST come before `./chat-driver` so the driver subclasses
11
- // jsdom's EventTarget rather than Node's native one. Mirrors chat-driver.test.ts.
12
- import './align-event-globals';
13
- import { ChatDriver } from './chat-driver';
14
-
15
- // ---------------------------------------------------------------------------
16
- // Per-turn `thinkingPolicy` resolution.
17
- //
18
- // The cost case for this option is the SHAPE of a tool loop, not a single call: the
19
- // opening turn is a real decision and worth reasoning over, while the iterations that
20
- // follow mostly pick the next tool from a narrow set — and on a thinking model each of
21
- // those bills reasoning at the full output rate. That saving only exists if the
22
- // resolver runs per iteration, so these tests assert the sequence across a loop, not
23
- // just that one value arrives.
24
- //
25
- // The other half is the undefined case. Every agent written before this option leaves
26
- // it unset, and a resolver may answer `undefined` on any given turn; both must reach
27
- // the transport as `undefined` so the model keeps its own default. `undefined` reaching
28
- // the wire as `'off'` would be a silent capability regression, and as `'auto'` a silent
29
- // bill increase — so it is asserted explicitly rather than assumed.
30
- // ---------------------------------------------------------------------------
31
-
32
- /** Captures the policy seen on each model call; calls one tool, then finishes. */
33
- const capturingProvider = (): AIProvider & { seen: Array<ChatThinkingPolicy | undefined> } => {
34
- const seen: Array<ChatThinkingPolicy | undefined> = [];
35
- let turns = 0;
36
- return {
37
- seen,
38
- chat: async (
39
- _history: ChatMessage[],
40
- _userMessage: string,
41
- options?: ChatRequestOptions,
42
- ): Promise<ChatMessage> => {
43
- seen.push(options?.thinkingPolicy);
44
- turns += 1;
45
- // Two tool calls, so the loop runs three model calls in total — enough for a
46
- // per-turn resolver to say something different on the later ones.
47
- if (turns <= 2) {
48
- return {
49
- role: 'assistant',
50
- content: '',
51
- toolCalls: [{ id: `t${turns}`, name: 'step', args: {} }],
52
- };
53
- }
54
- return { role: 'assistant', content: 'done' };
55
- },
56
- };
57
- };
58
-
59
- const makeRegistry = (provider: AIProvider): AIProviderRegistry => ({
60
- get: () => provider,
61
- default: () => provider,
62
- defaultName: () => 'test',
63
- names: () => ['test'],
64
- getStatus: async () => null,
65
- listStatuses: async () => [],
66
- });
67
-
68
- const agent = (overrides: Partial<AgentConfig>): AgentConfig =>
69
- ({
70
- name: 'worker',
71
- description: 'test agent',
72
- toolDefinitions: [
73
- { name: 'step', description: 'step', parameters: { type: 'object', properties: {} } },
74
- ],
75
- toolHandlers: { step: async () => 'stepped' },
76
- ...overrides,
77
- }) as AgentConfig;
78
-
79
- /** Run one user turn through a driver carrying `config`, and return the policies seen. */
80
- const policiesFor = async (
81
- config: Partial<AgentConfig>,
82
- ): Promise<Array<ChatThinkingPolicy | undefined>> => {
83
- const provider = capturingProvider();
84
- const driver = new ChatDriver(makeRegistry(provider), {
85
- maxToolIterations: 10,
86
- maxFoldOperations: 5,
87
- sessionKey: '',
88
- });
89
- driver.applyAgent(agent(config));
90
- await driver.sendMessage('go');
91
- return provider.seen;
92
- };
93
-
94
- const suite = createLogicSuite('ChatDriver thinkingPolicy');
95
-
96
- suite('leaves the policy undefined when the agent does not set one', async () => {
97
- // The compatibility case: every existing agent. Undefined must reach the transport
98
- // as undefined so each model keeps its own default posture — not silently coerced
99
- // to 'off' (a capability regression) or 'auto' (a bill increase).
100
- const seen = await policiesFor({});
101
- assert.ok(seen.length >= 3, `expected a multi-call loop, got ${seen.length}`);
102
- assert.equal(
103
- seen.filter((p) => p !== undefined),
104
- [],
105
- 'no turn invents a policy',
106
- );
107
- });
108
-
109
- suite('applies a static policy to every turn of the loop', async () => {
110
- const seen = await policiesFor({ thinkingPolicy: 'off' });
111
- assert.ok(seen.length >= 3);
112
- assert.equal([...new Set(seen)], ['off'], 'a static value is not just a first-turn setting');
113
- });
114
-
115
- suite('pins the resolved policy for the whole tool loop', async () => {
116
- // A tool-use loop is ONE assistant turn and Anthropic requires a single thinking mode
117
- // for its duration. Toggling part-way does not error — the API silently disables
118
- // thinking for that request and strips blocks that would leave the turn structure
119
- // invalid, so an 'auto' -> 'off' switch loses the continuity the opening call
120
- // established and 'off' -> 'auto' never delivers the reasoning asked for. It also
121
- // invalidates the prompt cache, costing more than the reasoning it meant to save.
122
- //
123
- // So a resolver that changes its mind mid-loop must NOT be honoured mid-loop. This
124
- // asserts the opposite of what it looks like it should: the later values are ignored.
125
- let call = 0;
126
- const seen = await policiesFor({
127
- thinkingPolicy: () => {
128
- call += 1;
129
- return call === 1 ? 'auto' : 'off';
130
- },
131
- });
132
- assert.ok(seen.length >= 3, `expected a multi-call loop, got ${seen.length}`);
133
- assert.equal(
134
- [...new Set(seen)],
135
- ['auto'],
136
- 'the first call decides; later resolutions do not take effect until the next user turn',
137
- );
138
- });
139
-
140
- suite('re-resolves on the next user turn', async () => {
141
- // The flip side: pinning is per turn, not for the driver's lifetime, so a state change
142
- // between turns still lands.
143
- const provider = capturingProvider();
144
- const driver = new ChatDriver(makeRegistry(provider), {
145
- maxToolIterations: 10,
146
- maxFoldOperations: 5,
147
- sessionKey: '',
148
- });
149
- let turn = 0;
150
- driver.applyAgent(
151
- agent({
152
- thinkingPolicy: () => (turn === 0 ? 'auto' : 'off'),
153
- }),
154
- );
155
- await driver.sendMessage('go');
156
- const firstTurn = [...provider.seen];
157
- turn = 1;
158
- await driver.sendMessage('again');
159
- const secondTurn = provider.seen.slice(firstTurn.length);
160
-
161
- assert.equal([...new Set(firstTurn)], ['auto'], 'turn one holds its posture');
162
- assert.equal([...new Set(secondTurn)], ['off'], 'turn two picks up the new one');
163
- });
164
-
165
- suite('passes undefined through when the resolver declines to choose', async () => {
166
- // A resolver may answer on some turns and not others. `undefined` is its third answer —
167
- // "leave this model alone" — and must not be normalised into a value. Asserted across
168
- // the whole loop because the first call's answer is the one that gets pinned.
169
- const seen = await policiesFor({ thinkingPolicy: () => undefined });
170
- assert.ok(seen.length >= 3);
171
- assert.equal(
172
- seen.filter((p) => p !== undefined),
173
- [],
174
- 'declining is not the same as choosing',
175
- );
176
- });
177
-
178
- suite('awaits an async resolver', async () => {
179
- const seen = await policiesFor({
180
- thinkingPolicy: async () => 'off' as ChatThinkingPolicy,
181
- });
182
- assert.equal([...new Set(seen)], ['off'], 'a promise is resolved, not passed through');
183
- });
184
-
185
- suite.run();
@@ -1,251 +0,0 @@
1
- import type {
2
- AIProvider,
3
- AIProviderRegistry,
4
- ChatMessage,
5
- ChatRequestOptions,
6
- } from '@genesislcap/foundation-ai';
7
- import { assert, createLogicSuite } from '@genesislcap/foundation-testing';
8
- import type { AgentConfig } from '../../config/config';
9
- import { sumUsage } from '../../utils/sum-usage';
10
- import { usageRows } from '../../utils/usage-rows';
11
- // Side-effect import — MUST come before `./chat-driver` so the driver subclasses
12
- // jsdom's EventTarget rather than Node's native one. Mirrors chat-driver.test.ts.
13
- import './align-event-globals';
14
- import { ChatDriver } from './chat-driver';
15
-
16
- // ---------------------------------------------------------------------------
17
- // Sub-agent trace capture when ONE tool call invokes SEVERAL sub-agents.
18
- //
19
- // The capture slot used to be a single `{ trace?: ChatMessage[] }` per tool call,
20
- // assigned by each `requestSubAgent`. That covers N tool calls × 1 sub-agent — the
21
- // case its docblock named — but not 1 tool call × N sub-agents, which is what a
22
- // code-driven scheduler or a retry produces. All but the last trace was dropped, so
23
- // those children ran, were billed, and then had no record in history.
24
- //
25
- // The failure was invisible to every existing check: `sumUsage` and `usageRows` both
26
- // recurse into `subAgentTrace`, so they summed a truncated input and still reconciled
27
- // with each other perfectly. Measured at 1 of 7 traces kept on a seven-way fan-out,
28
- // ~2.3x under the true cost.
29
- //
30
- // These tests assert the traces SURVIVE, which is upstream of any cost assertion.
31
- // ---------------------------------------------------------------------------
32
-
33
- /** Answers by rule: concurrent children under one turn make a FIFO queue nondeterministic. */
34
- const ruleProvider = (): AIProvider => {
35
- let parentTurns = 0;
36
- return {
37
- chat: async (
38
- _history: ChatMessage[],
39
- _userMessage: string,
40
- options?: ChatRequestOptions,
41
- ): Promise<ChatMessage> => {
42
- const tools = (options?.tools ?? []).map((t) => t.name);
43
- // Child turn: finishes via its completion tool, and reports usage so the
44
- // reconciliation assertions have something to add up.
45
- if (tools.includes('work')) {
46
- return {
47
- role: 'assistant',
48
- content: '',
49
- model: 'claude-sonnet-5',
50
- cost: 0.01,
51
- inputTokens: 100,
52
- outputTokens: 20,
53
- toolCalls: [{ id: 'w1', name: 'work', args: {} }],
54
- };
55
- }
56
- if (tools.includes('delegate') && parentTurns === 0) {
57
- parentTurns += 1;
58
- return {
59
- role: 'assistant',
60
- content: '',
61
- model: 'claude-sonnet-5',
62
- cost: 0.02,
63
- inputTokens: 200,
64
- outputTokens: 30,
65
- toolCalls: [{ id: 'd0', name: 'delegate', args: {} }],
66
- };
67
- }
68
- return { role: 'assistant', content: 'done', model: 'claude-sonnet-5' };
69
- },
70
- };
71
- };
72
-
73
- const makeRegistry = (provider: AIProvider): AIProviderRegistry => ({
74
- get: () => provider,
75
- default: () => provider,
76
- defaultName: () => 'test',
77
- names: () => ['test'],
78
- getStatus: async () => null,
79
- listStatuses: async () => [],
80
- });
81
-
82
- const agent = (overrides: Partial<AgentConfig> & { name: string }): AgentConfig =>
83
- ({ description: 'test agent', ...overrides }) as AgentConfig;
84
-
85
- /** A child that reports one unit of usage and completes. */
86
- const worker = (name: string): AgentConfig =>
87
- agent({
88
- name,
89
- toolDefinitions: [
90
- { name: 'work', description: 'work', parameters: { type: 'object', properties: {} } },
91
- ],
92
- toolHandlers: {
93
- work: async (_args, ctx) => {
94
- ctx.completeSubAgent?.({ ok: true });
95
- return 'worked';
96
- },
97
- },
98
- });
99
-
100
- /**
101
- * A parent whose single `delegate` tool call invokes `names` in turn — the shape a
102
- * code-driven scheduler produces, and the one the old single slot truncated.
103
- */
104
- const boss = (children: AgentConfig[], invoke: string[]): AgentConfig =>
105
- agent({
106
- name: 'boss',
107
- subAgents: children,
108
- toolDefinitions: [
109
- { name: 'delegate', description: 'delegate', parameters: { type: 'object', properties: {} } },
110
- ],
111
- toolHandlers: {
112
- delegate: async (_args, ctx) => {
113
- await Promise.all(invoke.map((n) => ctx.requestSubAgent!(n, { task: 'go' })));
114
- return 'delegated';
115
- },
116
- },
117
- });
118
-
119
- const run = async (config: AgentConfig): Promise<readonly ChatMessage[]> => {
120
- const driver = new ChatDriver(makeRegistry(ruleProvider()), {
121
- maxToolIterations: 20,
122
- maxFoldOperations: 5,
123
- sessionKey: '',
124
- });
125
- driver.applyAgent(config);
126
- await driver.sendMessage('go');
127
- return driver.getHistory();
128
- };
129
-
130
- const tracesIn = (history: readonly ChatMessage[]): readonly ChatMessage[][] =>
131
- history.flatMap((m) =>
132
- (m.toolCalls ?? []).flatMap((tc) => (tc.subAgentTrace ? [tc.subAgentTrace] : [])),
133
- );
134
-
135
- const suite = createLogicSuite('ChatDriver sub-agent trace capture');
136
-
137
- // ---------------------------------------------------------------------------
138
-
139
- suite('keeps every trace when one tool call invokes three sub-agents', async () => {
140
- const names = ['gen_a', 'gen_b', 'gen_c'];
141
- const history = await run(boss(names.map(worker), names));
142
-
143
- const traces = tracesIn(history);
144
- assert.is(traces.length, 1, 'one tool call, so one concatenated trace');
145
-
146
- // Each child contributes at least its own assistant turn. Before the fix this was
147
- // one child's worth regardless of how many ran.
148
- const seen = new Set(traces[0].map((m) => m.agentName).filter(Boolean));
149
- assert.equal(
150
- [...seen].sort(),
151
- names,
152
- `every invoked child must appear — got ${JSON.stringify([...seen])}`,
153
- );
154
- });
155
-
156
- suite('prices every child — usageRows emits a row per child and reconciles', async () => {
157
- // The reconciliation test extended to a fan-out. The single-child version passed
158
- // before the fix AND after it, which is exactly why it did not catch this.
159
- const names = ['gen_a', 'gen_b', 'gen_c'];
160
- const history = await run(boss(names.map(worker), names));
161
-
162
- const rows = usageRows(history);
163
- const childRows = rows.filter((r) => r.subAgentDepth === 1);
164
- assert.is(childRows.length, names.length, 'one row per child that ran');
165
-
166
- const rowTotal = rows.reduce((n, r) => n + (r.costUsd ?? 0) + (r.externalCostUsd ?? 0), 0);
167
- assert.ok(
168
- Math.abs(rowTotal - sumUsage(history).costUsd) < 1e-12,
169
- `rows ${rowTotal} vs sumUsage ${sumUsage(history).costUsd}`,
170
- );
171
- // Parent turn ($0.02) + three children ($0.01 each). Asserted as a number so a
172
- // regression that silently drops a child fails here rather than only in the
173
- // reconciliation above, which would still agree with a truncated input.
174
- assert.ok(Math.abs(rowTotal - 0.05) < 1e-12, `expected 0.05, got ${rowTotal}`);
175
- });
176
-
177
- suite('keeps both traces when the same sub-agent is invoked twice (a retry)', async () => {
178
- // The retry shape: one handler, one child name, two invocations. Under the old slot
179
- // the first attempt's trace was overwritten by the second.
180
- const history = await run(boss([worker('gen_a')], ['gen_a', 'gen_a']));
181
-
182
- const traces = tracesIn(history);
183
- assert.is(traces.length, 1);
184
- const childTurns = traces[0].filter((m) => m.agentName === 'gen_a' && m.cost != null);
185
- assert.is(childTurns.length, 2, 'both attempts survive, not just the last');
186
-
187
- const rows = usageRows(history).filter((r) => r.subAgentDepth === 1);
188
- assert.is(rows.length, 2, 'and both are priced');
189
- });
190
-
191
- suite('keeps child traces when the parent handler throws after they ran', async () => {
192
- // Children that completed were billed by the provider. If the error path drops
193
- // their traces, the run under-reports exactly as the single-slot bug did — and
194
- // just as silently, since `sumUsage` and `usageRows` would still agree with each
195
- // other over the truncated input. PR review.
196
- const names = ['gen_a', 'gen_b'];
197
- const parent = agent({
198
- name: 'boss',
199
- subAgents: names.map(worker),
200
- toolDefinitions: [
201
- { name: 'delegate', description: 'delegate', parameters: { type: 'object', properties: {} } },
202
- ],
203
- toolHandlers: {
204
- delegate: async (_args, ctx) => {
205
- await Promise.all(names.map((n) => ctx.requestSubAgent!(n, { task: 'go' })));
206
- throw new Error('post-processing the children failed');
207
- },
208
- },
209
- });
210
-
211
- const history = await run(parent);
212
- const traces = tracesIn(history);
213
- assert.is(traces.length, 1, 'the failed tool call still carries its trace');
214
- const seen = [...new Set(traces[0].map((m) => m.agentName).filter(Boolean))].sort();
215
- assert.equal(seen, names, 'both children survive the throw');
216
-
217
- const childRows = usageRows(history).filter((r) => r.subAgentDepth === 1);
218
- assert.is(childRows.length, 2, 'and both are still priced');
219
- });
220
-
221
- suite('leaves subAgentTrace undefined when no sub-agent ran', async () => {
222
- // Presence is the signal readers key off (`usage-rows.ts`, the UI's `when(...)`),
223
- // so an empty array would be a different and wrong claim.
224
- const history = await run(
225
- agent({
226
- name: 'boss',
227
- subAgents: [worker('gen_a')],
228
- toolDefinitions: [
229
- {
230
- name: 'delegate',
231
- description: 'delegate',
232
- parameters: { type: 'object', properties: {} },
233
- },
234
- ],
235
- toolHandlers: { delegate: async () => 'did it myself' },
236
- }),
237
- );
238
-
239
- for (const m of history) {
240
- for (const tc of m.toolCalls ?? []) {
241
- assert.is(tc.subAgentTrace, undefined, 'not an empty array');
242
- }
243
- }
244
- assert.equal(
245
- usageRows(history).filter((r) => r.subAgentDepth === 1),
246
- [],
247
- 'no child rows',
248
- );
249
- });
250
-
251
- suite.run();