basalt-ui 1.10.0 → 1.12.0

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 (273) hide show
  1. package/AGENTS.md +21 -21
  2. package/CHANGELOG.md +25 -0
  3. package/README.md +36 -27
  4. package/agent/rules/basalt-content.md +16 -3
  5. package/dist/agent/adapter.d.ts +146 -0
  6. package/dist/agent/adapter.d.ts.map +1 -0
  7. package/dist/agent/adapter.js +473 -0
  8. package/dist/agent/adapter.test.d.ts +2 -0
  9. package/dist/agent/adapter.test.d.ts.map +1 -0
  10. package/dist/agent/ai-sdk-transport.d.ts +3 -52
  11. package/dist/agent/ai-sdk-transport.d.ts.map +1 -1
  12. package/dist/agent/ai-sdk-transport.js +108 -24
  13. package/dist/agent/ai-sdk-transport.test.d.ts +2 -0
  14. package/dist/agent/ai-sdk-transport.test.d.ts.map +1 -0
  15. package/dist/agent/coalesce.d.ts +42 -0
  16. package/dist/agent/coalesce.d.ts.map +1 -0
  17. package/dist/agent/coalesce.js +137 -0
  18. package/dist/agent/coalesce.test.d.ts +2 -0
  19. package/dist/agent/coalesce.test.d.ts.map +1 -0
  20. package/dist/agent/foreign.d.ts +97 -0
  21. package/dist/agent/foreign.d.ts.map +1 -0
  22. package/dist/agent/foreign.js +18 -0
  23. package/dist/agent/foreign.test.d.ts +2 -0
  24. package/dist/agent/foreign.test.d.ts.map +1 -0
  25. package/dist/agent/history.d.ts +2 -0
  26. package/dist/agent/history.d.ts.map +1 -1
  27. package/dist/agent/id.d.ts +65 -0
  28. package/dist/agent/id.d.ts.map +1 -0
  29. package/dist/agent/id.js +30 -0
  30. package/dist/agent/id.test.d.ts +2 -0
  31. package/dist/agent/id.test.d.ts.map +1 -0
  32. package/dist/agent/index.d.ts +11 -4
  33. package/dist/agent/index.d.ts.map +1 -1
  34. package/dist/agent/index.js +18 -3
  35. package/dist/agent/merge.d.ts +8 -0
  36. package/dist/agent/merge.d.ts.map +1 -0
  37. package/dist/agent/merge.js +35 -0
  38. package/dist/agent/merge.test.d.ts +2 -0
  39. package/dist/agent/merge.test.d.ts.map +1 -0
  40. package/dist/agent/part-list.d.ts +49 -34
  41. package/dist/agent/part-list.d.ts.map +1 -1
  42. package/dist/agent/part-list.js +53 -7
  43. package/dist/agent/part-list.test.d.ts +2 -0
  44. package/dist/agent/part-list.test.d.ts.map +1 -0
  45. package/dist/agent/parts.d.ts +135 -20
  46. package/dist/agent/parts.d.ts.map +1 -1
  47. package/dist/agent/parts.js +138 -11
  48. package/dist/agent/parts.test.d.ts +2 -0
  49. package/dist/agent/parts.test.d.ts.map +1 -0
  50. package/dist/agent/thread.d.ts +14 -0
  51. package/dist/agent/thread.d.ts.map +1 -1
  52. package/dist/agent/thread.js +8 -2
  53. package/dist/agent/thread.test.d.ts +2 -0
  54. package/dist/agent/thread.test.d.ts.map +1 -0
  55. package/dist/agent/transport.d.ts +39 -3
  56. package/dist/agent/transport.d.ts.map +1 -1
  57. package/dist/agent/transport.js +20 -17
  58. package/dist/agent/transport.test.d.ts +2 -0
  59. package/dist/agent/transport.test.d.ts.map +1 -0
  60. package/dist/agent/use-agent-stream.d.ts +2 -1
  61. package/dist/agent/use-agent-stream.d.ts.map +1 -1
  62. package/dist/agent/use-agent-stream.js +5 -2
  63. package/dist/agent/use-agent-stream.test.d.ts +2 -0
  64. package/dist/agent/use-agent-stream.test.d.ts.map +1 -0
  65. package/dist/agent/use-agent-thread-runs.d.ts +9 -3
  66. package/dist/agent/use-agent-thread-runs.d.ts.map +1 -1
  67. package/dist/agent/use-agent-thread-runs.foreign.test.d.ts +2 -0
  68. package/dist/agent/use-agent-thread-runs.foreign.test.d.ts.map +1 -0
  69. package/dist/agent/use-agent-thread-runs.js +75 -23
  70. package/dist/agent/use-agent-thread-runs.resume.test.d.ts +2 -0
  71. package/dist/agent/use-agent-thread-runs.resume.test.d.ts.map +1 -0
  72. package/dist/agent/use-agent-thread-runs.test.d.ts +2 -0
  73. package/dist/agent/use-agent-thread-runs.test.d.ts.map +1 -0
  74. package/dist/agent/use-agent-thread-runs.wedge.test.d.ts +2 -0
  75. package/dist/agent/use-agent-thread-runs.wedge.test.d.ts.map +1 -0
  76. package/dist/agent-chat/composer.d.ts +130 -8
  77. package/dist/agent-chat/composer.d.ts.map +1 -1
  78. package/dist/agent-chat/composer.js +189 -23
  79. package/dist/agent-chat/composer.test.d.ts +2 -0
  80. package/dist/agent-chat/composer.test.d.ts.map +1 -0
  81. package/dist/agent-chat/index.d.ts +3 -1
  82. package/dist/agent-chat/index.d.ts.map +1 -1
  83. package/dist/agent-chat/index.js +2 -0
  84. package/dist/agent-chat/thread-detail-panel.d.ts.map +1 -1
  85. package/dist/agent-chat/thread-detail-panel.js +1 -1
  86. package/dist/agent-chat/thread-message.d.ts +24 -7
  87. package/dist/agent-chat/thread-message.d.ts.map +1 -1
  88. package/dist/agent-chat/thread-message.js +115 -45
  89. package/dist/agent-chat/thread-message.test.d.ts +2 -0
  90. package/dist/agent-chat/thread-message.test.d.ts.map +1 -0
  91. package/dist/agent-chat/thread-workspace.js +1 -1
  92. package/dist/agent-chat/tool-chip.d.ts +21 -0
  93. package/dist/agent-chat/tool-chip.d.ts.map +1 -0
  94. package/dist/agent-chat/tool-chip.js +120 -0
  95. package/dist/agent-chat/tool-chip.test.d.ts +2 -0
  96. package/dist/agent-chat/tool-chip.test.d.ts.map +1 -0
  97. package/dist/charts/primitives/ChartFrame.test.d.ts +2 -0
  98. package/dist/charts/primitives/ChartFrame.test.d.ts.map +1 -0
  99. package/dist/charts/utils/domain.test.d.ts +2 -0
  100. package/dist/charts/utils/domain.test.d.ts.map +1 -0
  101. package/dist/cli/check-theme.test.d.ts +2 -0
  102. package/dist/cli/check-theme.test.d.ts.map +1 -0
  103. package/dist/cli/help.test.d.ts +2 -0
  104. package/dist/cli/help.test.d.ts.map +1 -0
  105. package/dist/cli/index.d.ts.map +1 -1
  106. package/dist/cli/init-sync.test.d.ts +2 -0
  107. package/dist/cli/init-sync.test.d.ts.map +1 -0
  108. package/dist/cli/placement-engine.test.d.ts +2 -0
  109. package/dist/cli/placement-engine.test.d.ts.map +1 -0
  110. package/dist/cli/tarball-parity.test.d.ts +2 -0
  111. package/dist/cli/tarball-parity.test.d.ts.map +1 -0
  112. package/dist/cli/template-format.test.d.ts +2 -0
  113. package/dist/cli/template-format.test.d.ts.map +1 -0
  114. package/dist/cli/template-ledger.test.d.ts +2 -0
  115. package/dist/cli/template-ledger.test.d.ts.map +1 -0
  116. package/dist/cli/tokens-css.test.d.ts +2 -0
  117. package/dist/cli/tokens-css.test.d.ts.map +1 -0
  118. package/dist/content/article-actions.test.d.ts +2 -0
  119. package/dist/content/article-actions.test.d.ts.map +1 -0
  120. package/dist/content/article-model.test.d.ts +2 -0
  121. package/dist/content/article-model.test.d.ts.map +1 -0
  122. package/dist/content/block-split.test.d.ts +2 -0
  123. package/dist/content/block-split.test.d.ts.map +1 -0
  124. package/dist/content/fence-block.d.ts +37 -10
  125. package/dist/content/fence-block.d.ts.map +1 -1
  126. package/dist/content/fence-block.js +80 -14
  127. package/dist/content/heading-components.d.ts.map +1 -1
  128. package/dist/content/heading-components.js +7 -4
  129. package/dist/content/heading-components.test.d.ts +2 -0
  130. package/dist/content/heading-components.test.d.ts.map +1 -0
  131. package/dist/content/index.d.ts +6 -3
  132. package/dist/content/index.d.ts.map +1 -1
  133. package/dist/content/index.js +5 -1
  134. package/dist/content/markdown.d.ts +101 -14
  135. package/dist/content/markdown.d.ts.map +1 -1
  136. package/dist/content/markdown.js +141 -28
  137. package/dist/content/markdown.test.d.ts +2 -0
  138. package/dist/content/markdown.test.d.ts.map +1 -0
  139. package/dist/content/prose.module.css.test.d.ts +2 -0
  140. package/dist/content/prose.module.css.test.d.ts.map +1 -0
  141. package/dist/content/sanitize.d.ts +170 -0
  142. package/dist/content/sanitize.d.ts.map +1 -0
  143. package/dist/content/sanitize.js +178 -0
  144. package/dist/content/sanitize.test.d.ts +2 -0
  145. package/dist/content/sanitize.test.d.ts.map +1 -0
  146. package/dist/content/slug.test.d.ts +2 -0
  147. package/dist/content/slug.test.d.ts.map +1 -0
  148. package/dist/dashboard/stat-card.test.d.ts +2 -0
  149. package/dist/dashboard/stat-card.test.d.ts.map +1 -0
  150. package/dist/guard/check-source.test.d.ts +2 -0
  151. package/dist/guard/check-source.test.d.ts.map +1 -0
  152. package/dist/guard/guard-hook.test.d.ts +2 -0
  153. package/dist/guard/guard-hook.test.d.ts.map +1 -0
  154. package/dist/guard/index.d.ts.map +1 -1
  155. package/dist/guard/index.js +1 -3
  156. package/dist/index.d.ts +4 -4
  157. package/dist/index.d.ts.map +1 -1
  158. package/dist/index.js +12 -2
  159. package/dist/provider/index.test.d.ts +2 -0
  160. package/dist/provider/index.test.d.ts.map +1 -0
  161. package/dist/provider/lab-theme.test.d.ts +2 -0
  162. package/dist/provider/lab-theme.test.d.ts.map +1 -0
  163. package/dist/router-tanstack/multi-search-param-store.test.d.ts +2 -0
  164. package/dist/router-tanstack/multi-search-param-store.test.d.ts.map +1 -0
  165. package/dist/styles.floor.test.d.ts +2 -0
  166. package/dist/styles.floor.test.d.ts.map +1 -0
  167. package/dist/surfaces.d.ts +5 -5
  168. package/dist/surfaces.d.ts.map +1 -1
  169. package/dist/surfaces.js +13 -4
  170. package/dist/theme/border-coverage.test.d.ts +18 -0
  171. package/dist/theme/border-coverage.test.d.ts.map +1 -0
  172. package/dist/theme/contrast.test.d.ts +2 -0
  173. package/dist/theme/contrast.test.d.ts.map +1 -0
  174. package/dist/theme/density-option.test.d.ts +2 -0
  175. package/dist/theme/density-option.test.d.ts.map +1 -0
  176. package/dist/theme/density-relations.test.d.ts +2 -0
  177. package/dist/theme/density-relations.test.d.ts.map +1 -0
  178. package/dist/theme/derive-theme.test.d.ts +2 -0
  179. package/dist/theme/derive-theme.test.d.ts.map +1 -0
  180. package/dist/theme/fonts-option.test.d.ts +2 -0
  181. package/dist/theme/fonts-option.test.d.ts.map +1 -0
  182. package/dist/theme/radius-option.test.d.ts +2 -0
  183. package/dist/theme/radius-option.test.d.ts.map +1 -0
  184. package/dist/theme/radius.test.d.ts +2 -0
  185. package/dist/theme/radius.test.d.ts.map +1 -0
  186. package/dist/theme/shadow-surfaces.test.d.ts +26 -0
  187. package/dist/theme/shadow-surfaces.test.d.ts.map +1 -0
  188. package/dist/theme/spacing.test.d.ts +2 -0
  189. package/dist/theme/spacing.test.d.ts.map +1 -0
  190. package/dist/theme/use-basalt-spacing.test.d.ts +2 -0
  191. package/dist/theme/use-basalt-spacing.test.d.ts.map +1 -0
  192. package/dist/theme-lab/derive-controls.test.d.ts +2 -0
  193. package/dist/theme-lab/derive-controls.test.d.ts.map +1 -0
  194. package/dist/theme-lab/derive-state.test.d.ts +2 -0
  195. package/dist/theme-lab/derive-state.test.d.ts.map +1 -0
  196. package/dist/theme-lab/theme-lab.test.d.ts +2 -0
  197. package/dist/theme-lab/theme-lab.test.d.ts.map +1 -0
  198. package/dist/tokens/build-fonts-css.test.d.ts +2 -0
  199. package/dist/tokens/build-fonts-css.test.d.ts.map +1 -0
  200. package/dist/tokens/build-radius-css.test.d.ts +2 -0
  201. package/dist/tokens/build-radius-css.test.d.ts.map +1 -0
  202. package/dist/tokens/density.test.d.ts +2 -0
  203. package/dist/tokens/density.test.d.ts.map +1 -0
  204. package/dist/tokens/derive-radius.test.d.ts +2 -0
  205. package/dist/tokens/derive-radius.test.d.ts.map +1 -0
  206. package/dist/tokens/derive.test.d.ts +2 -0
  207. package/dist/tokens/derive.test.d.ts.map +1 -0
  208. package/dist/tokens/palette.test.d.ts +2 -0
  209. package/dist/tokens/palette.test.d.ts.map +1 -0
  210. package/dist/vite-color-mix.test.d.ts +2 -0
  211. package/dist/vite-color-mix.test.d.ts.map +1 -0
  212. package/dist/vite.test.d.ts +2 -0
  213. package/dist/vite.test.d.ts.map +1 -0
  214. package/llms.txt +7 -7
  215. package/package.json +6 -1
  216. package/src/agent/adapter.test.ts +1403 -0
  217. package/src/agent/adapter.ts +887 -0
  218. package/src/agent/ai-sdk-transport.test.ts +380 -55
  219. package/src/agent/ai-sdk-transport.ts +206 -53
  220. package/src/agent/coalesce.test.ts +387 -0
  221. package/src/agent/coalesce.ts +229 -0
  222. package/src/agent/foreign.test.ts +51 -0
  223. package/src/agent/foreign.ts +140 -0
  224. package/src/agent/history.ts +2 -0
  225. package/src/agent/id.test.ts +75 -0
  226. package/src/agent/id.ts +91 -0
  227. package/src/agent/index.ts +34 -3
  228. package/src/agent/merge.test.ts +206 -0
  229. package/src/agent/merge.ts +114 -0
  230. package/src/agent/part-list.test.tsx +112 -0
  231. package/src/agent/part-list.tsx +108 -35
  232. package/src/agent/parts.test.ts +516 -57
  233. package/src/agent/parts.ts +323 -32
  234. package/src/agent/thread.test.ts +75 -2
  235. package/src/agent/thread.ts +21 -1
  236. package/src/agent/transport.test.ts +70 -0
  237. package/src/agent/transport.ts +72 -20
  238. package/src/agent/use-agent-stream.test.tsx +53 -19
  239. package/src/agent/use-agent-stream.ts +13 -3
  240. package/src/agent/use-agent-thread-runs.foreign.test.tsx +138 -0
  241. package/src/agent/use-agent-thread-runs.resume.test.tsx +44 -11
  242. package/src/agent/use-agent-thread-runs.test.tsx +418 -22
  243. package/src/agent/use-agent-thread-runs.ts +170 -26
  244. package/src/agent/use-agent-thread-runs.wedge.test.tsx +15 -7
  245. package/src/agent-chat/composer.test.tsx +924 -0
  246. package/src/agent-chat/composer.tsx +521 -52
  247. package/src/agent-chat/index.ts +5 -1
  248. package/src/agent-chat/thread-detail-panel.tsx +3 -1
  249. package/src/agent-chat/thread-message.test.tsx +351 -0
  250. package/src/agent-chat/thread-message.tsx +262 -64
  251. package/src/agent-chat/thread-workspace.tsx +1 -1
  252. package/src/agent-chat/tool-chip.test.tsx +139 -0
  253. package/src/agent-chat/tool-chip.tsx +219 -0
  254. package/src/cli/index.ts +0 -1
  255. package/src/cli/template-format.test.ts +2 -2
  256. package/src/content/fence-block.tsx +242 -15
  257. package/src/content/heading-components.test.tsx +65 -0
  258. package/src/content/heading-components.tsx +26 -5
  259. package/src/content/index.ts +19 -3
  260. package/src/content/markdown.test.tsx +603 -0
  261. package/src/content/markdown.tsx +355 -35
  262. package/src/content/sanitize.test.ts +659 -0
  263. package/src/content/sanitize.ts +510 -0
  264. package/src/dashboard/stat-card.test.tsx +6 -1
  265. package/src/guard/check-source.test.ts +8 -4
  266. package/src/guard/index.ts +1 -3
  267. package/src/index.ts +15 -1
  268. package/src/surfaces.ts +13 -4
  269. package/src/theme/contrast.test.ts +1 -4
  270. package/src/theme/density-relations.test.ts +4 -2
  271. package/src/theme/fonts-option.test.ts +7 -1
  272. package/src/theme/shadow-surfaces.test.ts +12 -6
  273. package/src/theme/use-basalt-spacing.test.tsx +2 -1
@@ -1,49 +1,176 @@
1
+ const TERMINAL_TOOL_STATES = ["output-available", "output-error", "output-denied"];
2
+ function isToolCallSettled(part) {
3
+ return TERMINAL_TOOL_STATES.includes(part.state);
4
+ }
1
5
  function parseAgentPart(raw) {
2
6
  if (raw === null || typeof raw !== "object") return null;
3
7
  const obj = raw;
4
8
  if (typeof obj["type"] !== "string") return null;
9
+ if (typeof obj["id"] !== "string") return null;
10
+ const id = obj["id"];
5
11
  switch (obj["type"]) {
6
12
  case "start":
7
13
  if (typeof obj["runId"] !== "string") return null;
8
14
  return {
15
+ id,
9
16
  type: "start",
10
17
  runId: obj["runId"],
11
18
  ...typeof obj["resumeToken"] === "string" ? { resumeToken: obj["resumeToken"] } : {}
12
19
  };
13
20
  case "text":
14
21
  if (typeof obj["text"] !== "string") return null;
15
- return { type: "text", text: obj["text"] };
22
+ return {
23
+ id,
24
+ type: "text",
25
+ text: obj["text"],
26
+ ...typeof obj["offset"] === "number" ? { offset: obj["offset"] } : {}
27
+ };
16
28
  case "reasoning":
17
29
  if (typeof obj["text"] !== "string") return null;
18
- return { type: "reasoning", text: obj["text"] };
19
- case "tool":
20
- if (typeof obj["toolName"] !== "string") return null;
21
- if (!("input" in obj)) return null;
22
30
  return {
23
- type: "tool",
24
- toolName: obj["toolName"],
25
- input: obj["input"],
26
- ...obj["output"] !== void 0 ? { output: obj["output"] } : {},
27
- ...typeof obj["toolCallId"] === "string" ? { toolCallId: obj["toolCallId"] } : {}
31
+ id,
32
+ type: "reasoning",
33
+ text: obj["text"],
34
+ ...typeof obj["offset"] === "number" ? { offset: obj["offset"] } : {}
28
35
  };
36
+ case "tool":
37
+ return parseToolCallPart(id, obj);
29
38
  case "source":
30
39
  if (typeof obj["url"] !== "string") return null;
31
40
  return {
41
+ id,
32
42
  type: "source",
33
43
  url: obj["url"],
34
44
  ...typeof obj["title"] === "string" ? { title: obj["title"] } : {}
35
45
  };
36
46
  case "error":
37
47
  if (typeof obj["message"] !== "string") return null;
38
- return { type: "error", message: obj["message"] };
48
+ return { id, type: "error", message: obj["message"] };
39
49
  default:
40
50
  return null;
41
51
  }
42
52
  }
53
+ function parseToolCallPart(id, obj) {
54
+ if (typeof obj["toolName"] !== "string") return null;
55
+ if (typeof obj["state"] !== "string") return null;
56
+ const rawToolCallId = obj["toolCallId"];
57
+ if (rawToolCallId !== void 0 && typeof rawToolCallId !== "string") return null;
58
+ const toolCallId = typeof rawToolCallId === "string" && rawToolCallId.length > 0 ? rawToolCallId : void 0;
59
+ const toolName = obj["toolName"];
60
+ const common = {
61
+ id,
62
+ type: "tool",
63
+ toolName,
64
+ ...typeof obj["durationMs"] === "number" ? { durationMs: obj["durationMs"] } : {},
65
+ ...typeof obj["providerExecuted"] === "boolean" ? { providerExecuted: obj["providerExecuted"] } : {}
66
+ };
67
+ const identified = toolCallId !== void 0 ? { ...common, toolCallId } : null;
68
+ const identity = toolCallId !== void 0 ? { toolCallId } : {};
69
+ switch (obj["state"]) {
70
+ case "input-streaming":
71
+ if (identified === null) return null;
72
+ return {
73
+ ...identified,
74
+ state: "input-streaming",
75
+ ..."input" in obj ? { input: obj["input"] } : {}
76
+ };
77
+ case "input-available":
78
+ if (identified === null) return null;
79
+ if (!("input" in obj)) return null;
80
+ return { ...identified, state: "input-available", input: obj["input"] };
81
+ case "approval-requested": {
82
+ if (identified === null) return null;
83
+ if (!("input" in obj)) return null;
84
+ const approval = parseApproval(obj["approval"]);
85
+ if (approval === null || approval.approved !== void 0 || approval.reason !== void 0) {
86
+ return null;
87
+ }
88
+ return {
89
+ ...identified,
90
+ state: "approval-requested",
91
+ input: obj["input"],
92
+ approval
93
+ };
94
+ }
95
+ case "approval-responded": {
96
+ if (!("input" in obj)) return null;
97
+ const approval = parseApproval(obj["approval"]);
98
+ if (approval === null || typeof approval.approved !== "boolean") return null;
99
+ return {
100
+ ...common,
101
+ ...identity,
102
+ state: "approval-responded",
103
+ input: obj["input"],
104
+ approval
105
+ };
106
+ }
107
+ case "output-denied": {
108
+ if (!("input" in obj)) return null;
109
+ const approval = parseApproval(obj["approval"]);
110
+ if (approval === null || approval.approved !== false) return null;
111
+ return {
112
+ ...common,
113
+ ...identity,
114
+ state: "output-denied",
115
+ input: obj["input"],
116
+ approval
117
+ };
118
+ }
119
+ case "output-available": {
120
+ if (!("input" in obj)) return null;
121
+ if (!("output" in obj)) return null;
122
+ const approval = obj["approval"] === void 0 ? null : parseApproval(obj["approval"]);
123
+ if (obj["approval"] !== void 0 && (approval === null || approval.approved === false)) {
124
+ return null;
125
+ }
126
+ return {
127
+ ...common,
128
+ ...identity,
129
+ state: "output-available",
130
+ input: obj["input"],
131
+ output: obj["output"],
132
+ ...typeof obj["preliminary"] === "boolean" ? { preliminary: obj["preliminary"] } : {},
133
+ ...approval !== null ? { approval } : {}
134
+ };
135
+ }
136
+ case "output-error": {
137
+ if (typeof obj["errorText"] !== "string") return null;
138
+ const approval = obj["approval"] === void 0 ? null : parseApproval(obj["approval"]);
139
+ if (obj["approval"] !== void 0 && (approval === null || approval.approved === false)) {
140
+ return null;
141
+ }
142
+ return {
143
+ ...common,
144
+ ...identity,
145
+ state: "output-error",
146
+ errorText: obj["errorText"],
147
+ ..."input" in obj ? { input: obj["input"] } : {},
148
+ ..."rawInput" in obj ? { rawInput: obj["rawInput"] } : {},
149
+ ...approval !== null ? { approval } : {}
150
+ };
151
+ }
152
+ default:
153
+ return null;
154
+ }
155
+ }
156
+ function parseApproval(raw) {
157
+ if (raw === null || typeof raw !== "object") return null;
158
+ const obj = raw;
159
+ if (typeof obj["id"] !== "string") return null;
160
+ return {
161
+ id: obj["id"],
162
+ ...typeof obj["approved"] === "boolean" ? { approved: obj["approved"] } : {},
163
+ ...typeof obj["reason"] === "string" ? { reason: obj["reason"] } : {},
164
+ ...typeof obj["isAutomatic"] === "boolean" ? { isAutomatic: obj["isAutomatic"] } : {},
165
+ ...typeof obj["signature"] === "string" ? { signature: obj["signature"] } : {}
166
+ };
167
+ }
43
168
  function isStartPart(part) {
44
169
  return typeof part === "object" && part !== null && part.type === "start";
45
170
  }
46
171
  export {
172
+ TERMINAL_TOOL_STATES,
47
173
  isStartPart,
174
+ isToolCallSettled,
48
175
  parseAgentPart
49
176
  };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=parts.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parts.test.d.ts","sourceRoot":"","sources":["../../src/agent/parts.test.ts"],"names":[],"mappings":""}
@@ -103,6 +103,20 @@ export type ThreadsStore<TPart = AgentPart> = {
103
103
  readonly remove: (id: string) => void;
104
104
  /** Remove all threads and clear the active selection. */
105
105
  readonly clear: () => void;
106
+ /**
107
+ * Whether `threads` reflects the backing store yet. Always `true` for the localStorage store
108
+ * (createPersistedState resolves synchronously); meaningful only for an async, server-backed
109
+ * store built with `createAdapterThreadsStore` (./adapter), where it stays `false` until the
110
+ * first `listThreads` SUCCEEDS. Pair it with `error` — `!hydrated && error !== undefined` is a
111
+ * failed load, `!hydrated && error === undefined` is still loading.
112
+ */
113
+ readonly hydrated: boolean;
114
+ /**
115
+ * The most recent persistence failure, or `undefined`. Always `undefined` for the localStorage
116
+ * store, which has no failure the caller can act on. An async store surfaces the rejected
117
+ * load/write here after rolling its optimistic edit back.
118
+ */
119
+ readonly error: unknown;
106
120
  };
107
121
  /**
108
122
  * Creates a stable persisted multi-thread hook for the given key + version.
@@ -1 +1 @@
1
- {"version":3,"file":"thread.d.ts","sourceRoot":"","sources":["../../src/agent/thread.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"thread.d.ts","sourceRoot":"","sources":["../../src/agent/thread.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import { useCallback, useRef } from "react";
2
2
  import { createPersistedState } from "../state.js";
3
+ import { mintThreadId } from "./id.js";
3
4
  function createThreadsStore(opts) {
4
5
  const maxThreads = opts.maxThreads ?? 50;
5
6
  const maxMessages = opts.maxMessagesPerThread ?? 100;
@@ -27,7 +28,7 @@ function createThreadsStore(opts) {
27
28
  );
28
29
  const create = useCallback(
29
30
  (createOpts) => {
30
- const id = crypto.randomUUID();
31
+ const id = mintThreadId();
31
32
  const now = Date.now();
32
33
  const thread = {
33
34
  id,
@@ -122,7 +123,12 @@ function createThreadsStore(opts) {
122
123
  setResumeToken,
123
124
  markRead,
124
125
  remove,
125
- clear
126
+ clear,
127
+ // localStorage resolves synchronously and swallows its own quota errors, so there is never
128
+ // a pending load to await nor a failure a caller could act on. See ./adapter for the async
129
+ // store where these two carry real information.
130
+ hydrated: true,
131
+ error: void 0
126
132
  };
127
133
  };
128
134
  }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=thread.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"thread.test.d.ts","sourceRoot":"","sources":["../../src/agent/thread.test.ts"],"names":[],"mappings":""}
@@ -29,11 +29,16 @@
29
29
  * api.chat.post({ body: { message: input }, fetch: { signal } }),
30
30
  * )
31
31
  */
32
- import type { AgentPart } from './parts.js';
32
+ import type { AgentPart, AgentPartDraft } from './parts.js';
33
33
  /**
34
34
  * The injected transport seam. `stream(input, signal)` returns an async generator of parts.
35
35
  * Implement this interface for any backend — Eden, fetch-based SSE, AI SDK, or a mock.
36
36
  *
37
+ * The default `TPart` is `AgentPartDraft` (not `AgentPart`): transports yield DRAFTS — `id`
38
+ * optional — and the consuming hooks (or `withPartIds`) normalize a draft into a fully-identified
39
+ * part before it is ever accumulated. Pass an explicit `AgentPart` generic when a transport mints
40
+ * its own ids directly (as `aiSdkTransport` does).
41
+ *
37
42
  * @example
38
43
  * // Minimal mock transport for tests / playground:
39
44
  * const mockTransport: AgentTransport = {
@@ -42,11 +47,33 @@ import type { AgentPart } from './parts.js';
42
47
  * },
43
48
  * }
44
49
  */
45
- export type AgentTransport<TPart = AgentPart, TInput = string> = {
50
+ export type AgentTransport<TPart = AgentPartDraft, TInput = string> = {
46
51
  stream: (input: TInput, signal?: AbortSignal) => AsyncGenerator<TPart>;
47
52
  /** Optional: resume a previously-started run from its resumeToken (see StartPart). Transports that don't support resumption simply omit this. */
48
53
  resume?: (resumeToken: string, signal?: AbortSignal) => AsyncGenerator<TPart>;
49
54
  };
55
+ /**
56
+ * A transport whose replay is safe to run over an already-rendered turn. Implementing `resume`
57
+ * alone is no longer enough: `idempotentReplay: true` is a literal-typed assertion that every
58
+ * text/reasoning part this transport emits carries an authoritative `offset`, so mergePart
59
+ * rewrites instead of appending.
60
+ */
61
+ export type ResumableAgentTransport<TPart = AgentPartDraft, TInput = string> = AgentTransport<TPart, TInput> & {
62
+ readonly resume: (resumeToken: string, signal?: AbortSignal) => AsyncGenerator<TPart>;
63
+ readonly idempotentReplay: true;
64
+ };
65
+ /**
66
+ * Type guard: true when `t` both implements `resume` AND has explicitly asserted
67
+ * `idempotentReplay: true`. A transport with `resume` but no `idempotentReplay` is NOT resumable
68
+ * — the mount-time reconcile in `useAgentThreadRuns` falls back to `'interrupted'` for it rather
69
+ * than risk duplicating already-rendered content.
70
+ *
71
+ * @example
72
+ * if (isResumable(transport)) {
73
+ * for await (const part of transport.resume(resumeToken)) { ... }
74
+ * }
75
+ */
76
+ export declare function isResumable<TPart, TInput>(t: AgentTransport<TPart, TInput>): t is ResumableAgentTransport<TPart, TInput>;
50
77
  /**
51
78
  * Wraps a consumer's Eden async-generator call into an AgentTransport.
52
79
  *
@@ -64,6 +91,12 @@ export type AgentTransport<TPart = AgentPart, TInput = string> = {
64
91
  * object has NO `resume` key at all (not `resume: undefined`) — consumers that don't wire a
65
92
  * resumable backend get a transport that behaves exactly as before.
66
93
  *
94
+ * `resumeCall` alone does NOT make the transport resumable per `isResumable` — pass
95
+ * `{ idempotentReplay: true }` as the third argument to opt in EXPLICITLY once the server-side
96
+ * `resumeCall` route is verified to re-emit every text/reasoning delta with an authoritative
97
+ * `offset` (never a bare append). This is an opt-in, not an inference: a `resumeCall` whose deltas
98
+ * aren't offset-accurate would double-render on replay if treated as resumable.
99
+ *
67
100
  * @example
68
101
  * import { edenTransport, type AgentPart } from 'basalt-ui/agent'
69
102
  *
@@ -72,6 +105,7 @@ export type AgentTransport<TPart = AgentPart, TInput = string> = {
72
105
  * const transport = edenTransport<AgentPart>(
73
106
  * (input, signal) => api.chat.post({ body: { message: input }, fetch: { signal } }),
74
107
  * (resumeToken, signal) => api.chat.resume.post({ body: { resumeToken }, fetch: { signal } }),
108
+ * { idempotentReplay: true },
75
109
  * )
76
110
  *
77
111
  * // Wire into useAgentStream:
@@ -83,5 +117,7 @@ export declare function edenTransport<TPart = AgentPart, TInput = string>(call:
83
117
  }>, resumeCall?: (resumeToken: string, signal?: AbortSignal) => Promise<{
84
118
  data: AsyncGenerator<TPart> | null;
85
119
  error: unknown;
86
- }>): AgentTransport<TPart, TInput>;
120
+ }>, options?: {
121
+ readonly idempotentReplay?: true;
122
+ }): AgentTransport<TPart, TInput> | ResumableAgentTransport<TPart, TInput>;
87
123
  //# sourceMappingURL=transport.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/agent/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAIxC;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,cAAc,CAAC,KAAK,GAAG,SAAS,EAAE,MAAM,GAAG,MAAM,IAAI;IAC/D,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,cAAc,CAAC,KAAK,CAAC,CAAA;IACtE,iJAAiJ;IACjJ,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,cAAc,CAAC,KAAK,CAAC,CAAA;CAC9E,CAAA;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,aAAa,CAAC,KAAK,GAAG,SAAS,EAAE,MAAM,GAAG,MAAM,EAC9D,IAAI,EAAE,CACJ,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,WAAW,KACjB,OAAO,CAAC;IAAE,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,EACpE,UAAU,CAAC,EAAE,CACX,WAAW,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,WAAW,KACjB,OAAO,CAAC;IAAE,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,GACnE,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,CAmB/B"}
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/agent/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAIxD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,cAAc,CAAC,KAAK,GAAG,cAAc,EAAE,MAAM,GAAG,MAAM,IAAI;IACpE,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,cAAc,CAAC,KAAK,CAAC,CAAA;IACtE,iJAAiJ;IACjJ,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,cAAc,CAAC,KAAK,CAAC,CAAA;CAC9E,CAAA;AAID;;;;;GAKG;AACH,MAAM,MAAM,uBAAuB,CAAC,KAAK,GAAG,cAAc,EAAE,MAAM,GAAG,MAAM,IAAI,cAAc,CAC3F,KAAK,EACL,MAAM,CACP,GAAG;IACF,QAAQ,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,cAAc,CAAC,KAAK,CAAC,CAAA;IACrF,QAAQ,CAAC,gBAAgB,EAAE,IAAI,CAAA;CAChC,CAAA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EACvC,CAAC,EAAE,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,GAC/B,CAAC,IAAI,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,CAK7C;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,aAAa,CAAC,KAAK,GAAG,SAAS,EAAE,MAAM,GAAG,MAAM,EAC9D,IAAI,EAAE,CACJ,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,WAAW,KACjB,OAAO,CAAC;IAAE,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,EACpE,UAAU,CAAC,EAAE,CACX,WAAW,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,WAAW,KACjB,OAAO,CAAC;IAAE,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,EACpE,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,IAAI,CAAA;CAAE,GAC7C,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,CAsBxE"}
@@ -1,21 +1,24 @@
1
- function edenTransport(call, resumeCall) {
2
- return {
3
- async *stream(input, signal) {
4
- const { data, error } = await call(input, signal);
5
- if (error) throw error;
6
- if (data === null) return;
7
- yield* data;
8
- },
9
- ...resumeCall !== void 0 ? {
10
- async *resume(resumeToken, signal) {
11
- const { data, error } = await resumeCall(resumeToken, signal);
12
- if (error) throw error;
13
- if (data === null) return;
14
- yield* data;
15
- }
16
- } : {}
1
+ function isResumable(t) {
2
+ return typeof t.resume === "function" && t.idempotentReplay === true;
3
+ }
4
+ function edenTransport(call, resumeCall, options) {
5
+ const stream = async function* (input, signal) {
6
+ const { data, error } = await call(input, signal);
7
+ if (error) throw error;
8
+ if (data === null) return;
9
+ yield* data;
10
+ };
11
+ if (resumeCall === void 0) return { stream };
12
+ const resume = async function* (resumeToken, signal) {
13
+ const { data, error } = await resumeCall(resumeToken, signal);
14
+ if (error) throw error;
15
+ if (data === null) return;
16
+ yield* data;
17
17
  };
18
+ if (options?.idempotentReplay !== true) return { stream, resume };
19
+ return { stream, resume, idempotentReplay: true };
18
20
  }
19
21
  export {
20
- edenTransport
22
+ edenTransport,
23
+ isResumable
21
24
  };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=transport.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.test.d.ts","sourceRoot":"","sources":["../../src/agent/transport.test.ts"],"names":[],"mappings":""}
@@ -1,3 +1,4 @@
1
+ import type { PartLike } from './merge.js';
1
2
  import type { AgentPart } from './parts.js';
2
3
  import type { AgentTransport } from './transport.js';
3
4
  /** The lifecycle state of the current stream. */
@@ -28,7 +29,7 @@ export type UseAgentStreamReturn<TPart, TInput> = {
28
29
  * // parts accumulates as the stream arrives
29
30
  * // status: 'idle' → 'streaming' → 'done' (or 'error')
30
31
  */
31
- export declare function useAgentStream<TPart = AgentPart, TInput = string>({ transport, }: {
32
+ export declare function useAgentStream<TPart extends PartLike = AgentPart, TInput = string>({ transport, }: {
32
33
  transport: AgentTransport<TPart, TInput>;
33
34
  }): UseAgentStreamReturn<TPart, TInput>;
34
35
  //# sourceMappingURL=use-agent-stream.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-agent-stream.d.ts","sourceRoot":"","sources":["../../src/agent/use-agent-stream.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACxC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAIjD,iDAAiD;AACjD,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,CAAA;AAIlE,MAAM,MAAM,oBAAoB,CAAC,KAAK,EAAE,MAAM,IAAI;IAChD,qEAAqE;IACrE,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,CAAA;IACvB,gCAAgC;IAChC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;IAC7B,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/C,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,IAAI,CAAA;IACzB,kEAAkE;IAClE,QAAQ,CAAC,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CACzC,CAAA;AAID;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,KAAK,GAAG,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,EACjE,SAAS,GACV,EAAE;IACD,SAAS,EAAE,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;CACzC,GAAG,oBAAoB,CAAC,KAAK,EAAE,MAAM,CAAC,CAmEtC"}
1
+ {"version":3,"file":"use-agent-stream.d.ts","sourceRoot":"","sources":["../../src/agent/use-agent-stream.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACxC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAIjD,iDAAiD;AACjD,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,CAAA;AAIlE,MAAM,MAAM,oBAAoB,CAAC,KAAK,EAAE,MAAM,IAAI;IAChD,qEAAqE;IACrE,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,CAAA;IACvB,gCAAgC;IAChC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;IAC7B,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/C,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,IAAI,CAAA;IACzB,kEAAkE;IAClE,QAAQ,CAAC,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CACzC,CAAA;AAID;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,KAAK,SAAS,QAAQ,GAAG,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,EAClF,SAAS,GACV,EAAE;IACD,SAAS,EAAE,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;CACzC,GAAG,oBAAoB,CAAC,KAAK,EAAE,MAAM,CAAC,CA0EtC"}
@@ -1,5 +1,7 @@
1
1
  import { useCallback, useEffect, useRef, useState } from "react";
2
+ import { withPartIds } from "./id.js";
2
3
  import { isStartPart } from "./parts.js";
4
+ import { mergePart } from "./merge.js";
3
5
  function useAgentStream({
4
6
  transport
5
7
  }) {
@@ -23,12 +25,13 @@ function useAgentStream({
23
25
  setParts([]);
24
26
  setError(void 0);
25
27
  setStatus("streaming");
28
+ const runId = crypto.randomUUID();
26
29
  try {
27
- for await (const part of transport.stream(input, controller.signal)) {
30
+ for await (const part of withPartIds(runId, transport.stream(input, controller.signal))) {
28
31
  if (controllerRef.current !== controller) return;
29
32
  if (controller.signal.aborted) break;
30
33
  if (isStartPart(part)) continue;
31
- setParts((prev) => [...prev, part]);
34
+ setParts((prev) => mergePart(prev, part));
32
35
  }
33
36
  if (controllerRef.current === controller && !controller.signal.aborted) setStatus("done");
34
37
  } catch (err) {
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=use-agent-stream.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-agent-stream.test.d.ts","sourceRoot":"","sources":["../../src/agent/use-agent-stream.test.tsx"],"names":[],"mappings":""}
@@ -1,3 +1,4 @@
1
+ import type { PartLike } from './merge.js';
1
2
  import type { OutcomeResolver } from './outcome.js';
2
3
  import type { AgentPart } from './parts.js';
3
4
  import type { ThreadsStore } from './thread.js';
@@ -52,9 +53,14 @@ export type UseAgentThreadRunsReturn<TPart = AgentPart> = {
52
53
  * `threadId` has never had a turn started, or already has one in flight.
53
54
  */
54
55
  readonly retry: (threadId: string) => void;
55
- /** Abort the in-flight stream for `threadId` (no-op if idle). */
56
+ /**
57
+ * Abort the in-flight stream for `threadId` (no-op if idle). Preserves whatever content had
58
+ * already arrived: if the run had accumulated any parts, they're persisted as an assistant
59
+ * ChatMessage with `finish: 'stopped'` (unless that message already landed — see finalizeStop),
60
+ * then distilled into an outcome and the thread is settled to 'done'.
61
+ */
56
62
  readonly stop: (threadId: string) => void;
57
- /** Abort every in-flight stream across all threads. */
63
+ /** Abort every in-flight stream across all threads, settling each the same way stop() would. */
58
64
  readonly stopAll: () => void;
59
65
  };
60
66
  /**
@@ -68,5 +74,5 @@ export type UseAgentThreadRunsReturn<TPart = AgentPart> = {
68
74
  * start(threadB, 'What changed in the last release?')
69
75
  * // both stream concurrently; runs.get(threadA) / runs.get(threadB) update independently
70
76
  */
71
- export declare function useAgentThreadRuns<TPart = AgentPart>({ transport, store, resolveOutcome, toUserParts, }: UseAgentThreadRunsArgs<TPart>): UseAgentThreadRunsReturn<TPart>;
77
+ export declare function useAgentThreadRuns<TPart extends PartLike = AgentPart>({ transport, store, resolveOutcome, toUserParts, }: UseAgentThreadRunsArgs<TPart>): UseAgentThreadRunsReturn<TPart>;
72
78
  //# sourceMappingURL=use-agent-thread-runs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-agent-thread-runs.d.ts","sourceRoot":"","sources":["../../src/agent/use-agent-thread-runs.ts"],"names":[],"mappings":"AA0CA,OAAO,KAAK,EAAgB,eAAe,EAAE,MAAM,WAAW,CAAA;AAE9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACxC,OAAO,KAAK,EAA6B,YAAY,EAAE,MAAM,UAAU,CAAA;AACvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAIjD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,cAAc,CAAC,KAAK,GAAG,SAAS,IAAI;IAC9C,yFAAyF;IACzF,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,CAAA;CACxB,CAAA;AAID,MAAM,MAAM,sBAAsB,CAAC,KAAK,GAAG,SAAS,IAAI;IACtD;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,SAAS,EACd,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,GAC7B,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAA;IACzD,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,CAAA;IACnC,uFAAuF;IACvF,QAAQ,CAAC,cAAc,EAAE,eAAe,CAAC,KAAK,CAAC,CAAA;IAC/C;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,KAAK,EAAE,CAAA;CAClD,CAAA;AAID,MAAM,MAAM,wBAAwB,CAAC,KAAK,GAAG,SAAS,IAAI;IACxD,6FAA6F;IAC7F,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,CAAA;IACzD,2FAA2F;IAC3F,QAAQ,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACzD;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C,iEAAiE;IACjE,QAAQ,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IACzC,uDAAuD;IACvD,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAC7B,CAAA;AA0ID;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,GAAG,SAAS,EAAE,EACpD,SAAS,EACT,KAAK,EACL,cAAc,EACd,WAAW,GACZ,EAAE,sBAAsB,CAAC,KAAK,CAAC,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAmLjE"}
1
+ {"version":3,"file":"use-agent-thread-runs.d.ts","sourceRoot":"","sources":["../../src/agent/use-agent-thread-runs.ts"],"names":[],"mappings":"AA4CA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AACvC,OAAO,KAAK,EAAgB,eAAe,EAAE,MAAM,WAAW,CAAA;AAE9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACxC,OAAO,KAAK,EAA6B,YAAY,EAAE,MAAM,UAAU,CAAA;AAEvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAIjD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,cAAc,CAAC,KAAK,GAAG,SAAS,IAAI;IAC9C,yFAAyF;IACzF,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,CAAA;CACxB,CAAA;AAID,MAAM,MAAM,sBAAsB,CAAC,KAAK,GAAG,SAAS,IAAI;IACtD;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,SAAS,EACd,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,GAC7B,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAA;IACzD,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,CAAA;IACnC,uFAAuF;IACvF,QAAQ,CAAC,cAAc,EAAE,eAAe,CAAC,KAAK,CAAC,CAAA;IAC/C;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,KAAK,EAAE,CAAA;CAClD,CAAA;AAID,MAAM,MAAM,wBAAwB,CAAC,KAAK,GAAG,SAAS,IAAI;IACxD,6FAA6F;IAC7F,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,CAAA;IACzD,2FAA2F;IAC3F,QAAQ,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACzD;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IACzC,gGAAgG;IAChG,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAC7B,CAAA;AAkND;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,SAAS,QAAQ,GAAG,SAAS,EAAE,EACrE,SAAS,EACT,KAAK,EACL,cAAc,EACd,WAAW,GACZ,EAAE,sBAAsB,CAAC,KAAK,CAAC,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAkPjE"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=use-agent-thread-runs.foreign.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-agent-thread-runs.foreign.test.d.ts","sourceRoot":"","sources":["../../src/agent/use-agent-thread-runs.foreign.test.tsx"],"names":[],"mappings":""}