@gram-ai/elements 1.37.1 → 1.38.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/components/ActiveChatTitle.d.ts +22 -0
  2. package/dist/components/ActiveChatTitle.test.d.ts +1 -0
  3. package/dist/components/Markdown.d.ts +7 -0
  4. package/dist/components/MessageContent.d.ts +4 -0
  5. package/dist/components/activeChatTitle.helpers.d.ts +12 -0
  6. package/dist/components/ui/tool-ui.d.ts +52 -3
  7. package/dist/elements.cjs +1 -1
  8. package/dist/elements.css +1 -1
  9. package/dist/elements.js +31 -26
  10. package/dist/hooks/useMCPTools.d.ts +1 -1
  11. package/dist/{index-BdBraSNn.js → index-DJwZquGT.js} +35140 -29458
  12. package/dist/index-DJwZquGT.js.map +1 -0
  13. package/dist/index-EHNsQZHU.cjs +222 -0
  14. package/dist/index-EHNsQZHU.cjs.map +1 -0
  15. package/dist/index.d.ts +5 -0
  16. package/dist/lib/messageConverter.d.ts +2 -0
  17. package/dist/{profiler-Cl-9cG3B.js → profiler-De-Fmqbg.js} +2 -2
  18. package/dist/{profiler-Cl-9cG3B.js.map → profiler-De-Fmqbg.js.map} +1 -1
  19. package/dist/{profiler-ttCkbP-N.cjs → profiler-tmT7xKnI.cjs} +2 -2
  20. package/dist/{profiler-ttCkbP-N.cjs.map → profiler-tmT7xKnI.cjs.map} +1 -1
  21. package/dist/{startRecording-C41DbnxY.js → startRecording-BD0yDLcT.js} +2 -2
  22. package/dist/{startRecording-C41DbnxY.js.map → startRecording-BD0yDLcT.js.map} +1 -1
  23. package/dist/{startRecording-DLCeKyz9.cjs → startRecording-BDccFns1.cjs} +2 -2
  24. package/dist/{startRecording-DLCeKyz9.cjs.map → startRecording-BDccFns1.cjs.map} +1 -1
  25. package/package.json +11 -13
  26. package/src/components/ActiveChatTitle.test.ts +39 -0
  27. package/src/components/ActiveChatTitle.tsx +152 -0
  28. package/src/components/Markdown.tsx +210 -0
  29. package/src/components/MessageContent.tsx +9 -0
  30. package/src/components/activeChatTitle.helpers.ts +16 -0
  31. package/src/components/ui/tool-ui.tsx +360 -7
  32. package/src/contexts/ElementsProvider.tsx +2 -1
  33. package/src/hooks/useGramThreadListAdapter.tsx +100 -19
  34. package/src/hooks/useMCPTools.ts +1 -1
  35. package/src/index.ts +19 -0
  36. package/src/lib/messageConverter.ts +5 -0
  37. package/src/lib/tools.test.ts +24 -12
  38. package/dist/index-BdBraSNn.js.map +0 -1
  39. package/dist/index-Bl5cH0sz.cjs +0 -194
  40. package/dist/index-Bl5cH0sz.cjs.map +0 -1
package/src/index.ts CHANGED
@@ -15,11 +15,30 @@ export { useChatId } from "./contexts/ChatIdContext";
15
15
  // Core Components
16
16
  export { Chat } from "@/components/Chat";
17
17
  export { ChatHistory } from "@/components/ChatHistory";
18
+ export { ActiveChatTitle } from "@/components/ActiveChatTitle";
18
19
  export { ShareButton } from "@/components/ShareButton";
19
20
  export type { ShareButtonProps } from "@/components/ShareButton";
20
21
  export { ToolFallback } from "@/components/assistant-ui/tool-fallback";
21
22
  export { MessageContent } from "@/components/MessageContent";
22
23
  export type { MessageContentProps } from "@/components/MessageContent";
24
+ export { Markdown } from "@/components/Markdown";
25
+ export type { MarkdownProps } from "@/components/Markdown";
26
+
27
+ // Static presentation primitives — render with no ElementsProvider/runtime, so
28
+ // the dashboard's chat detail panel can reuse the elements tool UI directly.
29
+ export {
30
+ ToolUI,
31
+ ToolUISection,
32
+ SyntaxHighlightedCode,
33
+ } from "@/components/ui/tool-ui";
34
+ export type {
35
+ ToolUIProps,
36
+ ToolUISectionProps,
37
+ ToolStatus,
38
+ ContentItem,
39
+ SectionHighlight,
40
+ SectionMatch,
41
+ } from "@/components/ui/tool-ui";
23
42
 
24
43
  // Replay
25
44
  export { Replay } from "@/components/Replay";
@@ -71,6 +71,8 @@ export type GramChatContent = string | GramChatContentPart[];
71
71
  */
72
72
  export interface GramChatMessage {
73
73
  id: string;
74
+ // Monotonic sequence number; used as the keyset cursor when paging chat.load.
75
+ seq?: number;
74
76
  model: string;
75
77
  created_at: Date | string;
76
78
  role: "system" | "developer" | "user" | "assistant" | "tool";
@@ -92,6 +94,9 @@ export interface GramChat {
92
94
  messages: GramChatMessage[];
93
95
  createdAt: Date | string;
94
96
  updatedAt: Date | string;
97
+ // chat.load paginates by seq keyset; true when older messages remain before
98
+ // the first message in `messages` (snake_case to match the wire payload).
99
+ has_more_before?: boolean;
95
100
  }
96
101
 
97
102
  /**
@@ -11,12 +11,12 @@ import {
11
11
  type UIMessage,
12
12
  type UIMessagePart,
13
13
  } from "ai";
14
- import { MockLanguageModelV2 } from "ai/test";
14
+ import { MockLanguageModelV3 } from "ai/test";
15
15
 
16
16
  type MockStream = Extract<
17
17
  NonNullable<
18
18
  NonNullable<
19
- ConstructorParameters<typeof MockLanguageModelV2>[0]
19
+ ConstructorParameters<typeof MockLanguageModelV3>[0]
20
20
  >["doStream"]
21
21
  >,
22
22
  (...a: never[]) => PromiseLike<{ stream: ReadableStream<unknown> }>
@@ -70,8 +70,16 @@ function toolCallChunks(opts: {
70
70
  },
71
71
  {
72
72
  type: "finish",
73
- finishReason: "tool-calls",
74
- usage: { inputTokens: 1, outputTokens: 1, totalTokens: 2 },
73
+ finishReason: { unified: "tool-calls", raw: undefined },
74
+ usage: {
75
+ inputTokens: {
76
+ total: 1,
77
+ noCache: 1,
78
+ cacheRead: undefined,
79
+ cacheWrite: undefined,
80
+ },
81
+ outputTokens: { total: 1, text: 1, reasoning: undefined },
82
+ },
75
83
  },
76
84
  ];
77
85
  }
@@ -118,7 +126,7 @@ async function streamToolCallOnly(toolCallId: string): Promise<UIMessage[]> {
118
126
  },
119
127
  } as unknown as ToolSet;
120
128
 
121
- const model = new MockLanguageModelV2({
129
+ const model = new MockLanguageModelV3({
122
130
  doStream: async () => ({
123
131
  stream: makeStream([
124
132
  ...toolCallChunks({
@@ -174,10 +182,13 @@ describe("frontend tool Skip flow (sendAutomaticallyWhen fix)", () => {
174
182
  false,
175
183
  );
176
184
 
177
- // And the resulting model-message sequence contains a bogus `role: "tool"`
178
- // with empty content the provider will reject this as an invalid tool
179
- // message, surfacing to the user as the "needs role: assistant" error.
180
- const modelMsgs = convertToModelMessages(follow);
185
+ // The resulting model-message sequence has an assistant `tool-call` with no
186
+ // matching tool result. (In ai v5 `convertToModelMessages` fabricated a
187
+ // bogus empty `role: "tool"` message here; ai v6 instead drops the
188
+ // unresolved call entirely and the next turn is the follow-up user message.)
189
+ // Either way the sequence is invalid — a tool-call with no result — which is
190
+ // exactly the state the `sendAutomaticallyWhen` fix prevents.
191
+ const modelMsgs = await convertToModelMessages(follow);
181
192
  const assistantIdx = modelMsgs.findIndex(
182
193
  (m) =>
183
194
  m.role === "assistant" &&
@@ -187,8 +198,9 @@ describe("frontend tool Skip flow (sendAutomaticallyWhen fix)", () => {
187
198
  ),
188
199
  );
189
200
  expect(assistantIdx).toBeGreaterThanOrEqual(0);
190
- expect(modelMsgs[assistantIdx + 1]?.role).toBe("tool");
191
- expect(modelMsgs[assistantIdx + 1]?.content).toEqual([]);
201
+ // No valid tool result was produced for the unresolved tool-call.
202
+ expect(modelMsgs.some((m) => m.role === "tool")).toBe(false);
203
+ expect(modelMsgs[assistantIdx + 1]?.role).not.toBe("tool");
192
204
  });
193
205
 
194
206
  it("once the tool-result is patched onto the message, sendAutomaticallyWhen fires and the sequence is valid", async () => {
@@ -237,7 +249,7 @@ describe("frontend tool Skip flow (sendAutomaticallyWhen fix)", () => {
237
249
 
238
250
  // And the sequence handed to the model is well-formed (assistant
239
251
  // tool-call is followed by a real role:"tool" message with a result).
240
- const modelMsgs = convertToModelMessages(patched);
252
+ const modelMsgs = await convertToModelMessages(patched);
241
253
  const assistantIdx = modelMsgs.findIndex(
242
254
  (m) =>
243
255
  m.role === "assistant" &&