@assistant-ui/core 0.2.6 → 0.2.8

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 (192) hide show
  1. package/dist/adapters/attachment.d.ts.map +1 -1
  2. package/dist/adapters/speech.d.ts.map +1 -1
  3. package/dist/adapters/speech.js.map +1 -1
  4. package/dist/index.d.ts +4 -1
  5. package/dist/index.js +8 -1
  6. package/dist/index.js.map +1 -0
  7. package/dist/internal/duplicate-detection.d.ts +5 -0
  8. package/dist/internal/duplicate-detection.d.ts.map +1 -0
  9. package/dist/internal/duplicate-detection.js +11 -0
  10. package/dist/internal/duplicate-detection.js.map +1 -0
  11. package/dist/internal.d.ts +2 -2
  12. package/dist/internal.js +2 -2
  13. package/dist/model-context/frame/host.d.ts.map +1 -1
  14. package/dist/model-context/frame/host.js.map +1 -1
  15. package/dist/model-context/frame/provider.d.ts.map +1 -1
  16. package/dist/model-context/frame/provider.js.map +1 -1
  17. package/dist/model-context/registry.d.ts.map +1 -1
  18. package/dist/model-context/tool.d.ts.map +1 -1
  19. package/dist/react/AssistantProvider.d.ts.map +1 -1
  20. package/dist/react/AssistantProvider.js.map +1 -1
  21. package/dist/react/client/Interactables.js.map +1 -1
  22. package/dist/react/client/Tools.d.ts.map +1 -1
  23. package/dist/react/client/Tools.js +26 -15
  24. package/dist/react/client/Tools.js.map +1 -1
  25. package/dist/react/index.d.ts +5 -4
  26. package/dist/react/index.js +2 -2
  27. package/dist/react/model-context/toolbox.d.ts +29 -2
  28. package/dist/react/model-context/toolbox.d.ts.map +1 -1
  29. package/dist/react/model-context/toolbox.js +18 -0
  30. package/dist/react/model-context/toolbox.js.map +1 -0
  31. package/dist/react/model-context/useAssistantTool.d.ts.map +1 -1
  32. package/dist/react/model-context/useAssistantTool.js +6 -3
  33. package/dist/react/model-context/useAssistantTool.js.map +1 -1
  34. package/dist/react/model-context/useAssistantToolUI.d.ts +6 -0
  35. package/dist/react/model-context/useAssistantToolUI.d.ts.map +1 -1
  36. package/dist/react/model-context/useAssistantToolUI.js +4 -2
  37. package/dist/react/model-context/useAssistantToolUI.js.map +1 -1
  38. package/dist/react/model-context/useInlineRender.js.map +1 -1
  39. package/dist/react/primitives/chainOfThought/ChainOfThoughtParts.js.map +1 -1
  40. package/dist/react/primitives/message/MessageGroupedParts.d.ts +49 -7
  41. package/dist/react/primitives/message/MessageGroupedParts.d.ts.map +1 -1
  42. package/dist/react/primitives/message/MessageGroupedParts.js +28 -3
  43. package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -1
  44. package/dist/react/primitives/message/MessageParts.d.ts.map +1 -1
  45. package/dist/react/primitives/message/MessageParts.js +2 -7
  46. package/dist/react/primitives/message/MessageParts.js.map +1 -1
  47. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
  48. package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
  49. package/dist/react/runtimes/RuntimeAdapterProvider.d.ts.map +1 -1
  50. package/dist/react/runtimes/RuntimeAdapterProvider.js +6 -5
  51. package/dist/react/runtimes/RuntimeAdapterProvider.js.map +1 -1
  52. package/dist/react/runtimes/cloud/CloudFileAttachmentAdapter.d.ts.map +1 -1
  53. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.d.ts.map +1 -1
  54. package/dist/react/runtimes/cloud/useCloudThreadListAdapter.js.map +1 -1
  55. package/dist/react/runtimes/external-message-converter.d.ts +1 -1
  56. package/dist/react/runtimes/external-message-converter.d.ts.map +1 -1
  57. package/dist/react/runtimes/external-message-converter.js +1 -0
  58. package/dist/react/runtimes/external-message-converter.js.map +1 -1
  59. package/dist/react/runtimes/useExternalStoreSharedOptions.d.ts +7 -0
  60. package/dist/react/runtimes/useExternalStoreSharedOptions.d.ts.map +1 -0
  61. package/dist/react/runtimes/useExternalStoreSharedOptions.js +21 -0
  62. package/dist/react/runtimes/useExternalStoreSharedOptions.js.map +1 -0
  63. package/dist/react/runtimes/useLocalRuntime.d.ts.map +1 -1
  64. package/dist/react/runtimes/useLocalRuntime.js.map +1 -1
  65. package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
  66. package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
  67. package/dist/react/types/scopes/tools.d.ts +19 -2
  68. package/dist/react/types/scopes/tools.d.ts.map +1 -1
  69. package/dist/react/utils/groupParts.d.ts +32 -11
  70. package/dist/react/utils/groupParts.d.ts.map +1 -1
  71. package/dist/react/utils/groupParts.js +13 -6
  72. package/dist/react/utils/groupParts.js.map +1 -1
  73. package/dist/runtime/api/assistant-runtime.d.ts.map +1 -1
  74. package/dist/runtime/api/attachment-runtime.d.ts.map +1 -1
  75. package/dist/runtime/api/attachment-runtime.js.map +1 -1
  76. package/dist/runtime/api/composer-runtime.d.ts.map +1 -1
  77. package/dist/runtime/api/message-part-runtime.d.ts.map +1 -1
  78. package/dist/runtime/api/message-runtime.d.ts.map +1 -1
  79. package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
  80. package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
  81. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  82. package/dist/runtime/base/base-assistant-runtime-core.d.ts.map +1 -1
  83. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  84. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  85. package/dist/runtime/base/default-edit-composer-runtime-core.d.ts.map +1 -1
  86. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
  87. package/dist/runtime/interfaces/thread-runtime-core.d.ts +8 -0
  88. package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
  89. package/dist/runtime/utils/message-repository.d.ts +9 -1
  90. package/dist/runtime/utils/message-repository.d.ts.map +1 -1
  91. package/dist/runtime/utils/message-repository.js +34 -14
  92. package/dist/runtime/utils/message-repository.js.map +1 -1
  93. package/dist/runtime/utils/thread-message-like.d.ts +1 -0
  94. package/dist/runtime/utils/thread-message-like.d.ts.map +1 -1
  95. package/dist/runtime/utils/thread-message-like.js +2 -1
  96. package/dist/runtime/utils/thread-message-like.js.map +1 -1
  97. package/dist/runtimes/external-store/external-store-adapter.d.ts +31 -0
  98. package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
  99. package/dist/runtimes/external-store/external-store-shared-options.d.ts +8 -0
  100. package/dist/runtimes/external-store/external-store-shared-options.d.ts.map +1 -0
  101. package/dist/runtimes/external-store/external-store-shared-options.js +11 -0
  102. package/dist/runtimes/external-store/external-store-shared-options.js.map +1 -0
  103. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
  104. package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
  105. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +25 -2
  106. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  107. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +106 -26
  108. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  109. package/dist/runtimes/external-store/thread-message-converter.d.ts.map +1 -1
  110. package/dist/runtimes/local/local-thread-list-runtime-core.d.ts.map +1 -1
  111. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  112. package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts.map +1 -1
  113. package/dist/runtimes/remote-thread-list/adapter/in-memory.d.ts.map +1 -1
  114. package/dist/runtimes/remote-thread-list/optimistic-state.d.ts.map +1 -1
  115. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts +168 -0
  116. package/dist/runtimes/tool-invocations/ToolInvocationTracker.d.ts.map +1 -0
  117. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js +449 -0
  118. package/dist/runtimes/tool-invocations/ToolInvocationTracker.js.map +1 -0
  119. package/dist/subscribable/subscribable.d.ts.map +1 -1
  120. package/dist/subscribable/subscribable.js.map +1 -1
  121. package/dist/tests/remote-thread-list-test-helpers.d.ts.map +1 -1
  122. package/dist/types/message.d.ts +6 -0
  123. package/dist/types/message.d.ts.map +1 -1
  124. package/dist/types/message.js.map +1 -1
  125. package/dist/utils/composite-context-provider.d.ts.map +1 -1
  126. package/dist/utils/id.d.ts +1 -3
  127. package/dist/utils/id.d.ts.map +1 -1
  128. package/dist/utils/id.js +1 -4
  129. package/dist/utils/id.js.map +1 -1
  130. package/package.json +10 -10
  131. package/src/adapters/index.ts +1 -4
  132. package/src/adapters/speech.ts +0 -1
  133. package/src/index.ts +12 -0
  134. package/src/internal/duplicate-detection.ts +26 -0
  135. package/src/internal.ts +0 -2
  136. package/src/model-context/frame/host.ts +0 -1
  137. package/src/model-context/frame/provider.ts +0 -1
  138. package/src/react/AssistantProvider.tsx +2 -3
  139. package/src/react/client/Interactables.ts +0 -1
  140. package/src/react/client/Tools.ts +50 -25
  141. package/src/react/index.ts +9 -8
  142. package/src/react/model-context/toolbox.ts +46 -1
  143. package/src/react/model-context/useAssistantTool.ts +8 -3
  144. package/src/react/model-context/useAssistantToolUI.ts +9 -2
  145. package/src/react/model-context/useInlineRender.ts +0 -1
  146. package/src/react/primitives/chainOfThought/ChainOfThoughtParts.tsx +1 -2
  147. package/src/react/primitives/message/MessageAttachments.test.tsx +1 -1
  148. package/src/react/primitives/message/MessageGroupedParts.tsx +102 -13
  149. package/src/react/primitives/message/MessageParts.tsx +4 -7
  150. package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +0 -3
  151. package/src/react/runtimes/RuntimeAdapterProvider.tsx +12 -7
  152. package/src/react/runtimes/cloud/useCloudThreadListAdapter.tsx +0 -3
  153. package/src/react/runtimes/external-message-converter.ts +5 -1
  154. package/src/react/runtimes/useExternalStoreSharedOptions.ts +23 -0
  155. package/src/react/runtimes/useLocalRuntime.ts +0 -10
  156. package/src/react/runtimes/useRemoteThreadListRuntime.ts +0 -6
  157. package/src/react/types/scopes/tools.ts +20 -1
  158. package/src/react/utils/groupParts.ts +49 -18
  159. package/src/runtime/api/attachment-runtime.ts +1 -2
  160. package/src/runtime/interfaces/thread-runtime-core.ts +8 -0
  161. package/src/runtime/internal.ts +1 -4
  162. package/src/runtime/utils/message-repository.ts +57 -16
  163. package/src/runtime/utils/thread-message-like.ts +2 -0
  164. package/src/runtimes/external-store/external-store-adapter.ts +33 -0
  165. package/src/runtimes/external-store/external-store-shared-options.ts +18 -0
  166. package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +1 -3
  167. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +179 -37
  168. package/src/runtimes/tool-invocations/EDGE_CASES.md +194 -0
  169. package/src/runtimes/tool-invocations/ToolInvocationTracker.test.ts +1054 -0
  170. package/src/runtimes/tool-invocations/ToolInvocationTracker.ts +782 -0
  171. package/src/subscribable/subscribable.ts +3 -3
  172. package/src/tests/MessageRepository.test.ts +83 -52
  173. package/src/tests/OptimisticState-delete-crash.test.ts +2 -0
  174. package/src/tests/OptimisticState-list-race.test.ts +2 -4
  175. package/src/tests/RemoteThreadListThreadListRuntimeCore-loadMore.test.ts +5 -5
  176. package/src/tests/auiV0Encode.test.ts +1 -1
  177. package/src/tests/composer-can-send.test.ts +8 -4
  178. package/src/tests/duplicate-detection.test.ts +34 -0
  179. package/src/tests/external-store-thread-list-runtime-core.test.ts +1 -1
  180. package/src/tests/external-store-thread-runtime-core.test.ts +112 -79
  181. package/src/tests/groupParts.test.ts +70 -0
  182. package/src/tests/no-unsafe-process-env.test.ts +1 -0
  183. package/src/tests/remote-thread-list-isLoading.test.ts +2 -5
  184. package/src/tests/thread-message-like.test.ts +4 -1
  185. package/src/types/index.ts +1 -4
  186. package/src/types/message.ts +6 -0
  187. package/src/utils/id.ts +0 -4
  188. package/dist/react/runtimes/useToolInvocations.d.ts +0 -53
  189. package/dist/react/runtimes/useToolInvocations.d.ts.map +0 -1
  190. package/dist/react/runtimes/useToolInvocations.js +0 -380
  191. package/dist/react/runtimes/useToolInvocations.js.map +0 -1
  192. package/src/react/runtimes/useToolInvocations.ts +0 -694
@@ -0,0 +1,194 @@
1
+ # `ToolInvocationTracker` — known state-transition edge cases
2
+
3
+ This document captures the non-trivial state transitions the tracker may
4
+ observe via `setState(snapshot)` and what the current behavior is.
5
+
6
+ ## Hard contract
7
+
8
+ > **`streamCall` (and `execute`) fires exactly once per logical
9
+ > `toolCallId`.** No matter how the host's snapshot mutates after that
10
+ > first observation — args regress, args change after first completion,
11
+ > result is replaced, result is cleared, key order shuffles — the tracker
12
+ > never invokes the host's tool callback a second time.
13
+
14
+ This guarantees host-side side effects (the typical reason `streamCall` /
15
+ `execute` exists at all) can't double-run. The cost: post-completion
16
+ mutations are not surfaced to the host through the tool callback.
17
+ Consumers that need to observe them will opt into the planned
18
+ `reader.events()` API.
19
+
20
+ The tracker also never throws. Every public method that observes runtime
21
+ state (`setState`, `reset`, `abort`, `resume`) wraps its work in
22
+ try/catch and logs to `console.error`. The tracker is built into the hot
23
+ message-processing path; a malformed snapshot must never crash the host
24
+ runtime.
25
+
26
+ ## A. Tool changes shape after first observation
27
+
28
+ ### A.1. Args grow (normal streaming case)
29
+ Each snapshot's `argsText` is a longer prefix of the previous. The
30
+ tracker appends the delta into the active controller's `argsText`
31
+ stream. No re-fire.
32
+
33
+ ### A.2. Args regress mid-stream (snapshot regression)
34
+ A later snapshot's `argsText` is shorter than what we already streamed,
35
+ or otherwise *not* a prefix of it. Under the exactly-once contract, the
36
+ tracker does **not** restart the stream. The controller keeps whatever
37
+ prefix already streamed. The regression is logged in non-prod. The
38
+ host's view diverges from the snapshot until `reader.events()` ships.
39
+
40
+ Subsequent snapshots that *are* prefixes of the new (regressed) snapshot
41
+ also won't be appended, because `entry.argsText` still points at the
42
+ pre-regression value used for delta calculation.
43
+
44
+ ### A.3. Args complete then equivalent-JSON key reorder
45
+ Both old and new `argsText` parse to equivalent JSON values (e.g. keys
46
+ reordered by the backend). The tracker updates its tracked `argsText`
47
+ silently. No re-fire.
48
+
49
+ ### A.4. Args complete then change to non-equivalent value
50
+ The tracker does **not** restart the stream and does **not** invoke
51
+ `streamCall` a second time. Logs the divergence in non-prod. The host's
52
+ existing `streamCall` keeps its original args view.
53
+
54
+ ### A.5. First resolution (`result` becomes defined)
55
+ The tracker calls `setResponse` on the active controller and closes it.
56
+ `reader.response.get()` resolves. If the tool also had a frontend
57
+ `execute`, the executor is short-circuited via `_skipExecuteStreamIds`.
58
+ Single fire.
59
+
60
+ ### A.6. Previously-resolved tool's `result` is replaced
61
+ Silently ignored — `entry.hasResult` short-circuits both the
62
+ re-`setResponse` path and the downstream result-chunk handler. The host
63
+ sees only the first result.
64
+
65
+ ### A.7. Previously-resolved tool loses its `result` (back to undefined)
66
+ Silently ignored. The entry stays in the resolved phase internally.
67
+
68
+ ## B. Tool call disappears from snapshot
69
+
70
+ ### B.1. Tool call removed entirely (rollback, branch switch)
71
+ The tracker does not auto-clean entries that disappear from the
72
+ snapshot. The entry persists in `_entries` until the next `reset()`.
73
+
74
+ Auto-cleanup is intentionally avoided: if the same `toolCallId` ever
75
+ reappears in a later snapshot, treating it as new would re-fire
76
+ `streamCall`, violating the exactly-once contract. The cost is a bounded
77
+ memory accumulation across the tracker's lifetime; `reset()` clears it.
78
+
79
+ ## C. Initial snapshot vs. live snapshot
80
+
81
+ ### C.1. Tool call present in the initial snapshot
82
+ While `_pendingRestore === true` (either by construction, or because
83
+ `snapshot.isLoading === true`), tool calls are recorded as restored
84
+ entries with no controller. `streamCall` / `execute` do not fire.
85
+
86
+ ### C.2. Restored entry observed in a live snapshot, unchanged
87
+ Silently kept as restored. Recursion into `content.messages` still
88
+ happens so any nested live tool calls are processed.
89
+
90
+ ### C.3. Restored entry observed in a live snapshot, signature changed
91
+ The restored entry is deleted and a new active entry starts via
92
+ `_startActiveEntry`. This is PR #4057's promotion path. `streamCall`
93
+ fires once — its first and only fire for this `toolCallId`.
94
+
95
+ ### C.4. `isLoading` transitions `true → false` while messages are stable
96
+ The next `setState` call sees `isLoading === false` and processes
97
+ messages as live. Snapshots observed while `isLoading` was true seeded
98
+ restored entries. The first live snapshot promotes any whose signature
99
+ changed.
100
+
101
+ ### C.5. `isLoading` transitions `false → true` mid-session
102
+ Treated as a return to the historical-loading window. Subsequent
103
+ snapshots are recorded as restored. Tool calls observed live before the
104
+ transition keep their active controllers — the tracker does not unwind
105
+ them.
106
+
107
+ ## D. Nested tool calls (PTC sub-tools via `content.messages`)
108
+
109
+ ### D.1. Parent tool's nested messages are observed
110
+ The tracker recurses via `_processMessages(content.messages)`. Nested
111
+ tool calls go through the same restore / live / promotion logic as
112
+ top-level ones, all under the same exactly-once contract.
113
+
114
+ ### D.2. Nested tool's parent gets a new `result`
115
+ Handled like A.5 for the parent; the recursion into `content.messages`
116
+ still runs in the same pass, so nested tool calls also get processed.
117
+
118
+ ### D.3. Nested tool's `content.messages` itself changes
119
+ Identity is by `toolCallId`, not index. A different `toolCallId` at
120
+ the same nested position is a fresh tool call. Same id with different
121
+ shape goes through A.1–A.4.
122
+
123
+ ## E. Malformed snapshot
124
+
125
+ ### E.1. `message` is null/undefined or `message.content` is not an array
126
+ Skipped silently. The rest of the snapshot still processes.
127
+
128
+ ### E.2. `content` item is null or not a tool-call part
129
+ Skipped silently. Other parts in the same `message.content` still process.
130
+
131
+ ### E.3. Different `messages` reference, identical contents
132
+ The tracker re-walks the array on every non-identity snapshot. The
133
+ reference-equality fast path in `setState` rarely fires for class
134
+ consumers (external-store rebuilds the array on every adapter update).
135
+
136
+ ### E.4. `setState` throws inside `_processMessages`
137
+ The top-level try/catch in `setState` swallows the error and logs.
138
+ `_lastSnapshot` and `_isRunning` mutations are deferred until *after*
139
+ successful processing, so a transient failure does not corrupt the
140
+ tracker's view of "what we last observed". The next snapshot retries.
141
+
142
+ ## F. Concurrency and lifecycle
143
+
144
+ ### F.1. `reset()` called while `execute()` invocations are in flight
145
+ `abort()` is invoked, in-flight executions reject with
146
+ `Tool execution aborted`. Once they settle, the cleanup logic clears
147
+ `_executing`. The settled-resolver promises fire so the abort promise
148
+ resolves.
149
+
150
+ ### F.2. `setState` called during `reset()`'s in-flight abort
151
+ The new snapshot is processed against an empty `_entries`. Tool calls
152
+ in it are seeded as restored (because `reset()` re-armed
153
+ `_pendingRestore`). Eventual cancellation `result` chunks for the
154
+ aborted executions are dropped via `_skipExecuteStreamIds`.
155
+
156
+ ### F.3. `resume(toolCallId, payload)` for an unknown id
157
+ Silently no-ops. (The pre-class hook *threw*; the tracker softens this
158
+ to match the never-throw guarantee.)
159
+
160
+ ### F.4. Assistant-stream pipeline itself errors
161
+ The `.pipeTo(...).catch(...)` handler logs and flips `_pipelineDead`.
162
+ The next `setState` call recreates the pipeline once per tracker
163
+ lifetime: existing active entries are *demoted to restored* (so the
164
+ rebuilt pipeline does not re-fire `streamCall` for them) and the
165
+ snapshot is processed against the fresh pipeline. Repeated failures
166
+ keep the tracker dead with a visible error to avoid restart loops.
167
+
168
+ ## Known limitations
169
+
170
+ ### Result delivery after args regression (A.2 + A.5 in the same snapshot)
171
+ When a snapshot has both a regressed `argsText` *and* a backend result
172
+ on the same tool call, `activeController.setResponse(result)` closes
173
+ `argsText` before enqueueing the result chunk. The args-text-finish
174
+ chunk reaches `ToolExecutionStream` first, attempts to parse the
175
+ (stale) accumulated argsText, fails, and emits a parse-error result
176
+ that beats the backend result to the reader's response promise.
177
+
178
+ The tracker's `entry.hasResult` short-circuit *does* suppress both
179
+ result chunks at the `onResult` callback level (no double-fire), but
180
+ the reader's `response.get()` already resolved with the parse error.
181
+
182
+ Fixable upstream in `ToolCallStreamControllerImpl.setResponse` by
183
+ enqueueing the result chunk before closing argsText. Tracked separately;
184
+ out of scope for the tracker layer.
185
+
186
+ ### Host callback throws
187
+ `onResult` and `onStatusesChange` are invoked through wrappers that
188
+ catch and log. The tracker continues to function; the host's bad
189
+ callback is isolated.
190
+
191
+ ### Args-stream divergence after A.2 / A.4
192
+ Documented in the corresponding sections. The host's `streamCall` may
193
+ operate on stale args. The `reader.events()` follow-up gives consumers
194
+ a way to observe and react to these post-completion transitions.