@copilotkit/shared 1.73.0 → 1.73.2-canary.1790068297

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 (74) hide show
  1. package/README.md +22 -0
  2. package/dist/attachments/content.cjs +17 -0
  3. package/dist/attachments/content.cjs.map +1 -0
  4. package/dist/attachments/content.d.cts +9 -0
  5. package/dist/attachments/content.d.cts.map +1 -0
  6. package/dist/attachments/content.d.mts +9 -0
  7. package/dist/attachments/content.d.mts.map +1 -0
  8. package/dist/attachments/content.mjs +16 -0
  9. package/dist/attachments/content.mjs.map +1 -0
  10. package/dist/event-transforms/index.cjs +15 -0
  11. package/dist/event-transforms/index.d.cts +4 -0
  12. package/dist/event-transforms/index.d.mts +4 -0
  13. package/dist/event-transforms/index.mjs +5 -0
  14. package/dist/event-transforms/open-generative-ui-middleware.cjs +286 -0
  15. package/dist/event-transforms/open-generative-ui-middleware.cjs.map +1 -0
  16. package/dist/event-transforms/open-generative-ui-middleware.d.cts +60 -0
  17. package/dist/event-transforms/open-generative-ui-middleware.d.cts.map +1 -0
  18. package/dist/event-transforms/open-generative-ui-middleware.d.mts +60 -0
  19. package/dist/event-transforms/open-generative-ui-middleware.d.mts.map +1 -0
  20. package/dist/event-transforms/open-generative-ui-middleware.mjs +283 -0
  21. package/dist/event-transforms/open-generative-ui-middleware.mjs.map +1 -0
  22. package/dist/event-transforms/recorded-events.cjs +59 -0
  23. package/dist/event-transforms/recorded-events.cjs.map +1 -0
  24. package/dist/event-transforms/recorded-events.d.cts +23 -0
  25. package/dist/event-transforms/recorded-events.d.cts.map +1 -0
  26. package/dist/event-transforms/recorded-events.d.mts +23 -0
  27. package/dist/event-transforms/recorded-events.d.mts.map +1 -0
  28. package/dist/event-transforms/recorded-events.mjs +58 -0
  29. package/dist/event-transforms/recorded-events.mjs.map +1 -0
  30. package/dist/index.cjs +8 -0
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +3 -1
  33. package/dist/index.d.cts.map +1 -1
  34. package/dist/index.d.mts +3 -1
  35. package/dist/index.d.mts.map +1 -1
  36. package/dist/index.mjs +3 -1
  37. package/dist/index.mjs.map +1 -1
  38. package/dist/index.umd.js +224 -1
  39. package/dist/index.umd.js.map +1 -1
  40. package/dist/package.cjs +1 -1
  41. package/dist/package.mjs +1 -1
  42. package/dist/types/message.d.cts +3 -3
  43. package/dist/types/message.d.cts.map +1 -1
  44. package/dist/types/message.d.mts +3 -3
  45. package/dist/types/message.d.mts.map +1 -1
  46. package/dist/utils/index.cjs +1 -0
  47. package/dist/utils/index.cjs.map +1 -1
  48. package/dist/utils/index.d.cts +1 -0
  49. package/dist/utils/index.d.cts.map +1 -1
  50. package/dist/utils/index.d.mts +1 -0
  51. package/dist/utils/index.d.mts.map +1 -1
  52. package/dist/utils/index.mjs +1 -0
  53. package/dist/utils/index.mjs.map +1 -1
  54. package/dist/utils/row-render-keys.cjs +206 -0
  55. package/dist/utils/row-render-keys.cjs.map +1 -0
  56. package/dist/utils/row-render-keys.d.cts +65 -0
  57. package/dist/utils/row-render-keys.d.cts.map +1 -0
  58. package/dist/utils/row-render-keys.d.mts +65 -0
  59. package/dist/utils/row-render-keys.d.mts.map +1 -0
  60. package/dist/utils/row-render-keys.mjs +201 -0
  61. package/dist/utils/row-render-keys.mjs.map +1 -0
  62. package/package.json +12 -1
  63. package/src/__tests__/root-entry-browser-safety.test.ts +7 -1
  64. package/src/attachments/content.test.ts +48 -0
  65. package/src/attachments/content.ts +16 -0
  66. package/src/attachments/index.ts +2 -0
  67. package/src/event-transforms/index.ts +5 -0
  68. package/src/event-transforms/open-generative-ui-middleware.ts +392 -0
  69. package/src/event-transforms/recorded-events.test.ts +166 -0
  70. package/src/event-transforms/recorded-events.ts +74 -0
  71. package/src/utils/__tests__/row-render-keys.test.ts +388 -0
  72. package/src/utils/index.ts +1 -0
  73. package/src/utils/row-render-keys.ts +263 -0
  74. package/tsdown.config.ts +7 -4
@@ -0,0 +1,201 @@
1
+ //#region src/utils/row-render-keys.ts
2
+ /**
3
+ * Stable per-row view keys for chat transcripts, across backends that re-key a
4
+ * message mid-stream. Shared by every frontend package, because the defect and
5
+ * the correlation signal are in the message stream, not in any one framework.
6
+ *
7
+ * ## The problem
8
+ *
9
+ * A message's canonical `id` is not stable within a turn. LangChain stamps a
10
+ * placeholder id on the first streamed chunk when the provider didn't supply
11
+ * one (`chat_models.py`: `if chunk.message.id is None: chunk.message.id =
12
+ * "lc_run-" + "-" + run_id`), then prefers any provider-assigned id it sees
13
+ * while merging chunks (`messages/ai.py`, "Ranks are defined by the order of
14
+ * preference"). The `MESSAGES_SNAPSHOT` therefore carries the provider's final
15
+ * id (e.g. `resp_…`) for a message the client already knows as `lc_run--…`.
16
+ *
17
+ * Every framework destroys and recreates a row whose key changes, so keying
18
+ * rows by `id` turned that swap into the visible HITL chat flash, where a
19
+ * rendered approval card appears to reset during a tool's
20
+ * `executing → complete` transition.
21
+ *
22
+ * This is provider-conditional: providers that stamp an id on every chunk (so
23
+ * `chunk.message.id is None` never holds) never trigger a rename.
24
+ *
25
+ * ## The approach
26
+ *
27
+ * Tool-call ids survive the rename, so they are used as *anchors*: the first
28
+ * message seen carrying a given tool call records the key it was assigned, and
29
+ * any later message carrying that same tool call reuses it. The store is an
30
+ * override table consulted before falling back to `message.id`. Resolving only
31
+ * reads it; each render that reaches the DOM then records what it rendered:
32
+ *
33
+ * ```text
34
+ * render 1 id=lc_run--1 no tools key = message.id = lc_run--1
35
+ * commit 1 store {}
36
+ * render 2 id=lc_run--1 tc call_A key = message.id = lc_run--1
37
+ * commit 2 store { tc:call_A -> lc_run--1 }
38
+ * render 3 id=resp_1 tc call_A key = store[tc:call_A]
39
+ * = lc_run--1 (row survives)
40
+ * commit 3 store unchanged
41
+ * ```
42
+ *
43
+ * Recording the anchor at commit 2 — while the id is still stable — is what
44
+ * makes render 3 resolvable. Note the fix does not depend on render 2
45
+ * existing: a message born already carrying a tool call anchors on the first
46
+ * render that commits it, under whatever id it holds then.
47
+ *
48
+ * ## Why an override table rather than keying rows by tool-call id
49
+ *
50
+ * Deriving the key from the tool call directly (`tc:<id>` whenever a tool call
51
+ * is present) needs no state, but changes the key the moment a tool call
52
+ * *appears* — so an assistant message that streams text and then calls a tool
53
+ * is torn down on that transition, for every provider, whether or not it
54
+ * renames. That trades a conditional flash for an unconditional one. Recording
55
+ * an override keeps the key the row already had.
56
+ *
57
+ * ## Cost
58
+ *
59
+ * The store holds only `tc:<toolCallId>` entries, so a conversation with no
60
+ * tool calls keeps an empty store and behaviour byte-identical to keying by
61
+ * `id`. Size is bounded to the tool calls of the currently-rendered messages
62
+ * (see `pruneRowKeyStore`), not the conversation length. Deleting the store
63
+ * reverts to plain `id` keying.
64
+ *
65
+ * ## Known gaps
66
+ *
67
+ * - A text-only assistant message has no anchor, so it still re-keys. No
68
+ * client-side correlation signal exists for it; the fix is stable ids
69
+ * upstream.
70
+ * - If the tool call's arrival and the id swap land in the same update, the
71
+ * intermediate state never renders, the anchor is never recorded, and the
72
+ * row is recreated as before. Correlating in the event-apply layer (i.e. in
73
+ * the AG-UI client, which observes every intermediate state) would be immune.
74
+ */
75
+ const TOOL_ANCHOR_PREFIX = "tc:";
76
+ function createRowKeyStore() {
77
+ return {
78
+ overrides: /* @__PURE__ */ new Map(),
79
+ ambiguous: /* @__PURE__ */ new Set()
80
+ };
81
+ }
82
+ /**
83
+ * Anchors a message contributes. Only assistant tool calls qualify: LangChain's
84
+ * id preference applies when merging `AIMessageChunk`s, so user message ids are
85
+ * not renamed, and `role: "tool"` messages are not rendered as rows. Every tool
86
+ * call is used (not just the first) so the anchor survives tool-call reordering
87
+ * between snapshots.
88
+ */
89
+ function toolAnchorsOf(message) {
90
+ if (message?.role !== "assistant") return [];
91
+ const toolCalls = message.toolCalls;
92
+ if (!toolCalls?.length) return [];
93
+ const anchors = [];
94
+ for (const toolCall of toolCalls) if (toolCall?.id) anchors.push(`${TOOL_ANCHOR_PREFIX}${toolCall.id}`);
95
+ return anchors;
96
+ }
97
+ /**
98
+ * Resolves the row key for every message, returned by position.
99
+ *
100
+ * Pure: it reads `store` and never writes to it. Every framework here may
101
+ * evaluate a render pass whose result never reaches the DOM — an abandoned
102
+ * concurrent render in React, a discarded `computed` evaluation in Vue or
103
+ * Angular. An anchor recorded by such a pass would vend its key to the pass
104
+ * that does render, re-keying the row the user is looking at, which is the
105
+ * teardown this module exists to prevent. Anchors are recorded by
106
+ * `commitRowKeyStore`, from whichever phase each framework runs after the DOM
107
+ * is updated.
108
+ *
109
+ * Uniqueness is structural rather than assumed: a caller that does not
110
+ * deduplicate can pass two rows with the same id, and an override can vend a
111
+ * key equal to a later message's own id.
112
+ */
113
+ function resolveRowRenderKeys(store, messages) {
114
+ const keys = [];
115
+ const claimed = /* @__PURE__ */ new Set();
116
+ messages.forEach((message, index) => {
117
+ const anchors = toolAnchorsOf(message);
118
+ let key;
119
+ for (const anchor of anchors) {
120
+ if (store.ambiguous.has(anchor)) continue;
121
+ const recorded = store.overrides.get(anchor);
122
+ if (recorded !== void 0 && !claimed.has(recorded)) {
123
+ key = recorded;
124
+ break;
125
+ }
126
+ }
127
+ key ??= message?.id || `index-${index}`;
128
+ if (claimed.has(key)) {
129
+ let suffix = 2;
130
+ while (claimed.has(`${key}:${suffix}`)) suffix += 1;
131
+ key = `${key}:${suffix}`;
132
+ }
133
+ keys.push(key);
134
+ claimed.add(key);
135
+ });
136
+ return keys;
137
+ }
138
+ /**
139
+ * `message.id` → row key, for callers that render by message rather than by
140
+ * position. `messages` must be deduplicated: duplicate ids would overwrite
141
+ * each other in the returned map.
142
+ */
143
+ function resolveRowRenderKeysById(store, messages) {
144
+ const keys = resolveRowRenderKeys(store, messages);
145
+ const byId = /* @__PURE__ */ new Map();
146
+ messages.forEach((message, index) => {
147
+ const key = keys[index];
148
+ if (key !== void 0) byId.set(message.id, key);
149
+ });
150
+ return byId;
151
+ }
152
+ /**
153
+ * Records the anchors of a rendered list, then bounds the store to it. Call
154
+ * from the phase that runs after the DOM is updated, never while resolving —
155
+ * see `resolveRowRenderKeys`.
156
+ *
157
+ * Re-resolving here reproduces the keys the rows were rendered with, because
158
+ * the store cannot change between a render and its own post-render phase.
159
+ *
160
+ * An anchor carried by two rows in the same list is marked ambiguous rather
161
+ * than recorded. Recording it would give the first row's key to whichever row
162
+ * outlived the other, and with it that row's DOM and component state.
163
+ */
164
+ function commitRowKeyStore(store, messages) {
165
+ const keys = resolveRowRenderKeys(store, messages);
166
+ const anchorCounts = /* @__PURE__ */ new Map();
167
+ for (const message of messages) for (const anchor of toolAnchorsOf(message)) anchorCounts.set(anchor, (anchorCounts.get(anchor) ?? 0) + 1);
168
+ store.ambiguous.clear();
169
+ for (const [anchor, count] of anchorCounts) if (count > 1) {
170
+ store.ambiguous.add(anchor);
171
+ store.overrides.delete(anchor);
172
+ }
173
+ messages.forEach((message, index) => {
174
+ const key = keys[index];
175
+ if (key === void 0) return;
176
+ for (const anchor of toolAnchorsOf(message)) {
177
+ if (store.ambiguous.has(anchor)) continue;
178
+ if (!store.overrides.has(anchor)) store.overrides.set(anchor, key);
179
+ }
180
+ });
181
+ pruneRowKeyStore(store, messages);
182
+ }
183
+ /**
184
+ * Drops anchors no longer present in `messages`, bounding the store to the
185
+ * tool calls of the currently-rendered messages. Called by
186
+ * `commitRowKeyStore`; like it, this writes to the store and so belongs after
187
+ * the DOM is updated, never while resolving.
188
+ *
189
+ * Pruned entries are unreachable by construction: `resolveRowRenderKeys` only
190
+ * looks up anchors belonging to messages in the list it is given.
191
+ */
192
+ function pruneRowKeyStore(store, messages) {
193
+ const live = /* @__PURE__ */ new Set();
194
+ for (const message of messages) for (const anchor of toolAnchorsOf(message)) live.add(anchor);
195
+ for (const anchor of store.overrides.keys()) if (!live.has(anchor)) store.overrides.delete(anchor);
196
+ for (const anchor of store.ambiguous) if (!live.has(anchor)) store.ambiguous.delete(anchor);
197
+ }
198
+
199
+ //#endregion
200
+ export { commitRowKeyStore, createRowKeyStore, pruneRowKeyStore, resolveRowRenderKeys, resolveRowRenderKeysById };
201
+ //# sourceMappingURL=row-render-keys.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"row-render-keys.mjs","names":[],"sources":["../../src/utils/row-render-keys.ts"],"sourcesContent":["import type { AssistantMessage, Message } from \"@ag-ui/core\";\n\n/**\n * Stable per-row view keys for chat transcripts, across backends that re-key a\n * message mid-stream. Shared by every frontend package, because the defect and\n * the correlation signal are in the message stream, not in any one framework.\n *\n * ## The problem\n *\n * A message's canonical `id` is not stable within a turn. LangChain stamps a\n * placeholder id on the first streamed chunk when the provider didn't supply\n * one (`chat_models.py`: `if chunk.message.id is None: chunk.message.id =\n * \"lc_run-\" + \"-\" + run_id`), then prefers any provider-assigned id it sees\n * while merging chunks (`messages/ai.py`, \"Ranks are defined by the order of\n * preference\"). The `MESSAGES_SNAPSHOT` therefore carries the provider's final\n * id (e.g. `resp_…`) for a message the client already knows as `lc_run--…`.\n *\n * Every framework destroys and recreates a row whose key changes, so keying\n * rows by `id` turned that swap into the visible HITL chat flash, where a\n * rendered approval card appears to reset during a tool's\n * `executing → complete` transition.\n *\n * This is provider-conditional: providers that stamp an id on every chunk (so\n * `chunk.message.id is None` never holds) never trigger a rename.\n *\n * ## The approach\n *\n * Tool-call ids survive the rename, so they are used as *anchors*: the first\n * message seen carrying a given tool call records the key it was assigned, and\n * any later message carrying that same tool call reuses it. The store is an\n * override table consulted before falling back to `message.id`. Resolving only\n * reads it; each render that reaches the DOM then records what it rendered:\n *\n * ```text\n * render 1 id=lc_run--1 no tools key = message.id = lc_run--1\n * commit 1 store {}\n * render 2 id=lc_run--1 tc call_A key = message.id = lc_run--1\n * commit 2 store { tc:call_A -> lc_run--1 }\n * render 3 id=resp_1 tc call_A key = store[tc:call_A]\n * = lc_run--1 (row survives)\n * commit 3 store unchanged\n * ```\n *\n * Recording the anchor at commit 2 — while the id is still stable — is what\n * makes render 3 resolvable. Note the fix does not depend on render 2\n * existing: a message born already carrying a tool call anchors on the first\n * render that commits it, under whatever id it holds then.\n *\n * ## Why an override table rather than keying rows by tool-call id\n *\n * Deriving the key from the tool call directly (`tc:<id>` whenever a tool call\n * is present) needs no state, but changes the key the moment a tool call\n * *appears* — so an assistant message that streams text and then calls a tool\n * is torn down on that transition, for every provider, whether or not it\n * renames. That trades a conditional flash for an unconditional one. Recording\n * an override keeps the key the row already had.\n *\n * ## Cost\n *\n * The store holds only `tc:<toolCallId>` entries, so a conversation with no\n * tool calls keeps an empty store and behaviour byte-identical to keying by\n * `id`. Size is bounded to the tool calls of the currently-rendered messages\n * (see `pruneRowKeyStore`), not the conversation length. Deleting the store\n * reverts to plain `id` keying.\n *\n * ## Known gaps\n *\n * - A text-only assistant message has no anchor, so it still re-keys. No\n * client-side correlation signal exists for it; the fix is stable ids\n * upstream.\n * - If the tool call's arrival and the id swap land in the same update, the\n * intermediate state never renders, the anchor is never recorded, and the\n * row is recreated as before. Correlating in the event-apply layer (i.e. in\n * the AG-UI client, which observes every intermediate state) would be immune.\n */\n\nconst TOOL_ANCHOR_PREFIX = \"tc:\";\n\nexport interface RowKeyStore {\n /**\n * `tc:<toolCallId>` → the row key first assigned to a message carrying that\n * tool call. Populated only for assistant messages that carry tool calls.\n */\n overrides: Map<string, string>;\n /**\n * Anchors carried by more than one message in the last rendered list. Such\n * an anchor identifies no single row, so it is never used to vend a key.\n */\n ambiguous: Set<string>;\n}\n\nexport function createRowKeyStore(): RowKeyStore {\n return { overrides: new Map(), ambiguous: new Set() };\n}\n\n/**\n * Anchors a message contributes. Only assistant tool calls qualify: LangChain's\n * id preference applies when merging `AIMessageChunk`s, so user message ids are\n * not renamed, and `role: \"tool\"` messages are not rendered as rows. Every tool\n * call is used (not just the first) so the anchor survives tool-call reordering\n * between snapshots.\n */\nfunction toolAnchorsOf(message: Message | undefined): string[] {\n if (message?.role !== \"assistant\") return [];\n const toolCalls = (message as AssistantMessage).toolCalls;\n if (!toolCalls?.length) return [];\n\n const anchors: string[] = [];\n for (const toolCall of toolCalls) {\n if (toolCall?.id) anchors.push(`${TOOL_ANCHOR_PREFIX}${toolCall.id}`);\n }\n return anchors;\n}\n\n/**\n * Resolves the row key for every message, returned by position.\n *\n * Pure: it reads `store` and never writes to it. Every framework here may\n * evaluate a render pass whose result never reaches the DOM — an abandoned\n * concurrent render in React, a discarded `computed` evaluation in Vue or\n * Angular. An anchor recorded by such a pass would vend its key to the pass\n * that does render, re-keying the row the user is looking at, which is the\n * teardown this module exists to prevent. Anchors are recorded by\n * `commitRowKeyStore`, from whichever phase each framework runs after the DOM\n * is updated.\n *\n * Uniqueness is structural rather than assumed: a caller that does not\n * deduplicate can pass two rows with the same id, and an override can vend a\n * key equal to a later message's own id.\n */\nexport function resolveRowRenderKeys(\n store: RowKeyStore,\n messages: readonly (Message | undefined)[],\n): string[] {\n const keys: string[] = [];\n const claimed = new Set<string>();\n\n messages.forEach((message, index) => {\n const anchors = toolAnchorsOf(message);\n\n // Reuse the key recorded for any of this message's anchors. An override\n // pointing at a key another row already claimed this pass is skipped: two\n // messages can share a tool-call id (upstream bug, or replayed state), and\n // the later one falls back to its own id instead.\n let key: string | undefined;\n for (const anchor of anchors) {\n if (store.ambiguous.has(anchor)) continue;\n const recorded = store.overrides.get(anchor);\n if (recorded !== undefined && !claimed.has(recorded)) {\n key = recorded;\n break;\n }\n }\n\n // The index fallback covers a message with no usable id.\n key ??= message?.id || `index-${index}`;\n\n if (claimed.has(key)) {\n let suffix = 2;\n while (claimed.has(`${key}:${suffix}`)) suffix += 1;\n key = `${key}:${suffix}`;\n }\n\n keys.push(key);\n claimed.add(key);\n });\n\n return keys;\n}\n\n/**\n * `message.id` → row key, for callers that render by message rather than by\n * position. `messages` must be deduplicated: duplicate ids would overwrite\n * each other in the returned map.\n */\nexport function resolveRowRenderKeysById(\n store: RowKeyStore,\n messages: readonly Message[],\n): Map<string, string> {\n const keys = resolveRowRenderKeys(store, messages);\n const byId = new Map<string, string>();\n messages.forEach((message, index) => {\n const key = keys[index];\n if (key !== undefined) byId.set(message.id, key);\n });\n return byId;\n}\n\n/**\n * Records the anchors of a rendered list, then bounds the store to it. Call\n * from the phase that runs after the DOM is updated, never while resolving —\n * see `resolveRowRenderKeys`.\n *\n * Re-resolving here reproduces the keys the rows were rendered with, because\n * the store cannot change between a render and its own post-render phase.\n *\n * An anchor carried by two rows in the same list is marked ambiguous rather\n * than recorded. Recording it would give the first row's key to whichever row\n * outlived the other, and with it that row's DOM and component state.\n */\nexport function commitRowKeyStore(\n store: RowKeyStore,\n messages: readonly (Message | undefined)[],\n): void {\n const keys = resolveRowRenderKeys(store, messages);\n\n const anchorCounts = new Map<string, number>();\n for (const message of messages) {\n for (const anchor of toolAnchorsOf(message)) {\n anchorCounts.set(anchor, (anchorCounts.get(anchor) ?? 0) + 1);\n }\n }\n\n // Ambiguity is a property of the rendered list, so it is recomputed rather\n // than accumulated: an anchor left alone by the row that shadowed it becomes\n // usable again.\n store.ambiguous.clear();\n for (const [anchor, count] of anchorCounts) {\n if (count > 1) {\n store.ambiguous.add(anchor);\n store.overrides.delete(anchor);\n }\n }\n\n // First claimant of an anchor owns it, so a re-keyed message resolves to the\n // key the row already had rather than overwriting it.\n messages.forEach((message, index) => {\n const key = keys[index];\n if (key === undefined) return;\n for (const anchor of toolAnchorsOf(message)) {\n if (store.ambiguous.has(anchor)) continue;\n if (!store.overrides.has(anchor)) store.overrides.set(anchor, key);\n }\n });\n\n pruneRowKeyStore(store, messages);\n}\n\n/**\n * Drops anchors no longer present in `messages`, bounding the store to the\n * tool calls of the currently-rendered messages. Called by\n * `commitRowKeyStore`; like it, this writes to the store and so belongs after\n * the DOM is updated, never while resolving.\n *\n * Pruned entries are unreachable by construction: `resolveRowRenderKeys` only\n * looks up anchors belonging to messages in the list it is given.\n */\nexport function pruneRowKeyStore(\n store: RowKeyStore,\n messages: readonly (Message | undefined)[],\n): void {\n const live = new Set<string>();\n for (const message of messages) {\n for (const anchor of toolAnchorsOf(message)) live.add(anchor);\n }\n\n for (const anchor of store.overrides.keys()) {\n if (!live.has(anchor)) store.overrides.delete(anchor);\n }\n for (const anchor of store.ambiguous) {\n if (!live.has(anchor)) store.ambiguous.delete(anchor);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4EA,MAAM,qBAAqB;AAe3B,SAAgB,oBAAiC;AAC/C,QAAO;EAAE,2BAAW,IAAI,KAAK;EAAE,2BAAW,IAAI,KAAK;EAAE;;;;;;;;;AAUvD,SAAS,cAAc,SAAwC;AAC7D,KAAI,SAAS,SAAS,YAAa,QAAO,EAAE;CAC5C,MAAM,YAAa,QAA6B;AAChD,KAAI,CAAC,WAAW,OAAQ,QAAO,EAAE;CAEjC,MAAM,UAAoB,EAAE;AAC5B,MAAK,MAAM,YAAY,UACrB,KAAI,UAAU,GAAI,SAAQ,KAAK,GAAG,qBAAqB,SAAS,KAAK;AAEvE,QAAO;;;;;;;;;;;;;;;;;;AAmBT,SAAgB,qBACd,OACA,UACU;CACV,MAAM,OAAiB,EAAE;CACzB,MAAM,0BAAU,IAAI,KAAa;AAEjC,UAAS,SAAS,SAAS,UAAU;EACnC,MAAM,UAAU,cAAc,QAAQ;EAMtC,IAAI;AACJ,OAAK,MAAM,UAAU,SAAS;AAC5B,OAAI,MAAM,UAAU,IAAI,OAAO,CAAE;GACjC,MAAM,WAAW,MAAM,UAAU,IAAI,OAAO;AAC5C,OAAI,aAAa,UAAa,CAAC,QAAQ,IAAI,SAAS,EAAE;AACpD,UAAM;AACN;;;AAKJ,UAAQ,SAAS,MAAM,SAAS;AAEhC,MAAI,QAAQ,IAAI,IAAI,EAAE;GACpB,IAAI,SAAS;AACb,UAAO,QAAQ,IAAI,GAAG,IAAI,GAAG,SAAS,CAAE,WAAU;AAClD,SAAM,GAAG,IAAI,GAAG;;AAGlB,OAAK,KAAK,IAAI;AACd,UAAQ,IAAI,IAAI;GAChB;AAEF,QAAO;;;;;;;AAQT,SAAgB,yBACd,OACA,UACqB;CACrB,MAAM,OAAO,qBAAqB,OAAO,SAAS;CAClD,MAAM,uBAAO,IAAI,KAAqB;AACtC,UAAS,SAAS,SAAS,UAAU;EACnC,MAAM,MAAM,KAAK;AACjB,MAAI,QAAQ,OAAW,MAAK,IAAI,QAAQ,IAAI,IAAI;GAChD;AACF,QAAO;;;;;;;;;;;;;;AAeT,SAAgB,kBACd,OACA,UACM;CACN,MAAM,OAAO,qBAAqB,OAAO,SAAS;CAElD,MAAM,+BAAe,IAAI,KAAqB;AAC9C,MAAK,MAAM,WAAW,SACpB,MAAK,MAAM,UAAU,cAAc,QAAQ,CACzC,cAAa,IAAI,SAAS,aAAa,IAAI,OAAO,IAAI,KAAK,EAAE;AAOjE,OAAM,UAAU,OAAO;AACvB,MAAK,MAAM,CAAC,QAAQ,UAAU,aAC5B,KAAI,QAAQ,GAAG;AACb,QAAM,UAAU,IAAI,OAAO;AAC3B,QAAM,UAAU,OAAO,OAAO;;AAMlC,UAAS,SAAS,SAAS,UAAU;EACnC,MAAM,MAAM,KAAK;AACjB,MAAI,QAAQ,OAAW;AACvB,OAAK,MAAM,UAAU,cAAc,QAAQ,EAAE;AAC3C,OAAI,MAAM,UAAU,IAAI,OAAO,CAAE;AACjC,OAAI,CAAC,MAAM,UAAU,IAAI,OAAO,CAAE,OAAM,UAAU,IAAI,QAAQ,IAAI;;GAEpE;AAEF,kBAAiB,OAAO,SAAS;;;;;;;;;;;AAYnC,SAAgB,iBACd,OACA,UACM;CACN,MAAM,uBAAO,IAAI,KAAa;AAC9B,MAAK,MAAM,WAAW,SACpB,MAAK,MAAM,UAAU,cAAc,QAAQ,CAAE,MAAK,IAAI,OAAO;AAG/D,MAAK,MAAM,UAAU,MAAM,UAAU,MAAM,CACzC,KAAI,CAAC,KAAK,IAAI,OAAO,CAAE,OAAM,UAAU,OAAO,OAAO;AAEvD,MAAK,MAAM,UAAU,MAAM,UACzB,KAAI,CAAC,KAAK,IAAI,OAAO,CAAE,OAAM,UAAU,OAAO,OAAO"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@copilotkit/shared",
3
- "version": "1.73.0",
3
+ "version": "1.73.2-canary.1790068297",
4
4
  "private": false,
5
5
  "keywords": [
6
6
  "ai",
@@ -32,6 +32,10 @@
32
32
  "import": "./dist/index.mjs",
33
33
  "require": "./dist/index.cjs"
34
34
  },
35
+ "./event-transforms": {
36
+ "import": "./dist/event-transforms/index.mjs",
37
+ "require": "./dist/event-transforms/index.cjs"
38
+ },
35
39
  "./telemetry": {
36
40
  "import": "./dist/telemetry/index.mjs",
37
41
  "require": "./dist/telemetry/index.cjs"
@@ -42,6 +46,9 @@
42
46
  "*": {
43
47
  "telemetry": [
44
48
  "./dist/telemetry/index.d.cts"
49
+ ],
50
+ "event-transforms": [
51
+ "./dist/event-transforms/index.d.cts"
45
52
  ]
46
53
  }
47
54
  },
@@ -49,18 +56,22 @@
49
56
  "access": "public"
50
57
  },
51
58
  "dependencies": {
59
+ "@ag-ui/a2ui-middleware": "0.0.10",
52
60
  "@ag-ui/client": "0.0.59",
53
61
  "@copilotkit/license-verifier": "~0.5.0",
54
62
  "@segment/analytics-node": "^2.1.2",
55
63
  "@standard-schema/spec": "^1.0.0",
56
64
  "chalk": "4.1.2",
65
+ "clarinet": "^0.12.4",
57
66
  "graphql": "^16.8.1",
58
67
  "partial-json": "^0.1.7",
68
+ "rxjs": "7.8.1",
59
69
  "uuid": "^11.1.0",
60
70
  "zod": "^3.23.3",
61
71
  "zod-to-json-schema": "^3.23.5"
62
72
  },
63
73
  "devDependencies": {
74
+ "@types/clarinet": "^0.12.3",
64
75
  "@types/uuid": "^10.0.0",
65
76
  "@valibot/to-json-schema": "^1.5.0",
66
77
  "arktype": "^2.1.29",
@@ -16,7 +16,13 @@ import { describe, expect, it } from "vitest";
16
16
  * value-level graph from `src/index.ts` and fails if it reaches a Node-only
17
17
  * package. Type-only edges are erased at build time and are therefore fine.
18
18
  */
19
- const NODE_ONLY_PACKAGES = ["@segment/analytics-node", "node-fetch"];
19
+ const NODE_ONLY_PACKAGES = [
20
+ "@segment/analytics-node",
21
+ "node-fetch",
22
+ "@ag-ui/a2ui-middleware",
23
+ "clarinet",
24
+ "rxjs",
25
+ ];
20
26
 
21
27
  const SRC = resolve(__dirname, "..");
22
28
 
@@ -0,0 +1,48 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { createAttachmentContent } from "./content";
3
+
4
+ describe("createAttachmentContent", () => {
5
+ it.each(["image", "audio", "video", "document"] as const)(
6
+ "preserves %s bytes, source, and metadata precedence",
7
+ (type) => {
8
+ const source = {
9
+ type: "data",
10
+ value: "AAEC",
11
+ mimeType: "application/octet-stream",
12
+ } as const;
13
+ const part = createAttachmentContent({
14
+ type,
15
+ source,
16
+ filename: "upload.bin",
17
+ metadata: { filename: "custom.bin", custom: { value: 1 } },
18
+ });
19
+ expect(part).toEqual({
20
+ type,
21
+ source,
22
+ metadata: { filename: "custom.bin", custom: { value: 1 } },
23
+ });
24
+ expect("source" in part && part.source).toBe(source);
25
+ },
26
+ );
27
+
28
+ it("preserves URL sources and always includes metadata", () => {
29
+ const source = {
30
+ type: "url",
31
+ value: "https://example.test/image.png",
32
+ } as const;
33
+ expect(
34
+ createAttachmentContent({ type: "image", source, filename: "" }),
35
+ ).toEqual({
36
+ type: "image",
37
+ source,
38
+ metadata: {},
39
+ });
40
+ expect(
41
+ createAttachmentContent({ type: "image", source, filename: "image.png" }),
42
+ ).toEqual({
43
+ type: "image",
44
+ source,
45
+ metadata: { filename: "image.png" },
46
+ });
47
+ });
48
+ });
@@ -0,0 +1,16 @@
1
+ import type { InputContent } from "@ag-ui/core";
2
+ import type { Attachment } from "./types";
3
+
4
+ /** Build the AG-UI content part used when sending a ready attachment. */
5
+ export function createAttachmentContent(
6
+ attachment: Pick<Attachment, "type" | "source" | "filename" | "metadata">,
7
+ ): InputContent {
8
+ return {
9
+ type: attachment.type,
10
+ source: attachment.source,
11
+ metadata: {
12
+ ...(attachment.filename ? { filename: attachment.filename } : {}),
13
+ ...attachment.metadata,
14
+ },
15
+ };
16
+ }
@@ -7,6 +7,8 @@ export type {
7
7
  AttachmentModality,
8
8
  } from "./types";
9
9
 
10
+ export { createAttachmentContent } from "./content";
11
+
10
12
  export {
11
13
  getModalityFromMimeType,
12
14
  formatFileSize,
@@ -0,0 +1,5 @@
1
+ // Server-only entry: A2UI depends on node:crypto. Do not re-export from the root.
2
+ export { A2UIMiddleware } from "@ag-ui/a2ui-middleware";
3
+ export type { A2UIMiddlewareConfig } from "@ag-ui/a2ui-middleware";
4
+ export * from "./open-generative-ui-middleware";
5
+ export * from "./recorded-events";