@aleph-alpha/chat-kit 1.3.1 → 1.5.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 (73) hide show
  1. package/dist/{CkInput.vue_vue_type_script_setup_true_lang-CyGLItQZ.js → CkInput.vue_vue_type_script_setup_true_lang-C5C09u0z.js} +9 -8
  2. package/dist/adapters/index.d.ts +18 -0
  3. package/dist/adapters/index.d.ts.map +1 -0
  4. package/dist/adapters/index.js +4 -0
  5. package/dist/adapters/responses-api.d.ts +180 -0
  6. package/dist/adapters/responses-api.d.ts.map +1 -0
  7. package/dist/adapters/responses-api.js +153 -0
  8. package/dist/components/base/CkPromptActionButton/CkPromptActionButton.vue.d.ts +1 -1
  9. package/dist/components/composed/CkConversation/CkConversation.stories.d.ts +11 -3
  10. package/dist/components/composed/CkConversation/CkConversation.stories.d.ts.map +1 -1
  11. package/dist/components/composed/CkConversation/CkConversation.vue.d.ts +5 -1
  12. package/dist/components/composed/CkConversation/CkConversation.vue.d.ts.map +1 -1
  13. package/dist/components/index.js +1 -1
  14. package/dist/composables/fileUpload/index.d.ts +7 -0
  15. package/dist/composables/fileUpload/index.d.ts.map +1 -0
  16. package/dist/composables/fileUpload/types.d.ts +295 -0
  17. package/dist/composables/fileUpload/types.d.ts.map +1 -0
  18. package/dist/composables/fileUpload/useFileUpload.d.ts +4 -0
  19. package/dist/composables/fileUpload/useFileUpload.d.ts.map +1 -0
  20. package/dist/composables/fileUpload/useFileUploadWrapper.d.ts +3 -0
  21. package/dist/composables/fileUpload/useFileUploadWrapper.d.ts.map +1 -0
  22. package/dist/composables/index.d.ts +2 -1
  23. package/dist/composables/index.d.ts.map +1 -1
  24. package/dist/composables/index.js +5 -2
  25. package/dist/composables/useConversations.d.ts +12 -0
  26. package/dist/composables/useConversations.d.ts.map +1 -1
  27. package/dist/generateId-BHf0W4Rq.js +6 -0
  28. package/dist/handlers/index.js +1 -1
  29. package/dist/handlers/useMessageHandler.d.ts +2 -2
  30. package/dist/handlers/useMessageHandler.d.ts.map +1 -1
  31. package/dist/index-BbTgIoYa.js +1557 -0
  32. package/dist/index.d.ts +2 -2
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +6 -8
  35. package/dist/services/index.d.ts +0 -2
  36. package/dist/services/index.d.ts.map +1 -1
  37. package/dist/services/index.js +1 -37
  38. package/dist/services/types.d.ts +2 -2
  39. package/dist/services/types.d.ts.map +1 -1
  40. package/dist/types.d.ts +47 -2
  41. package/dist/types.d.ts.map +1 -1
  42. package/dist/{useMessageHandler-BmBdu2EW.js → useMessageHandler-ZRC-gIOJ.js} +5 -5
  43. package/package.json +10 -2
  44. package/src/adapters/index.ts +17 -0
  45. package/src/adapters/responses-api.spec.ts +365 -0
  46. package/src/adapters/responses-api.ts +444 -0
  47. package/src/components/composed/CkConversation/CkConversation.spec.ts +171 -25
  48. package/src/components/composed/CkConversation/CkConversation.stories.ts +266 -46
  49. package/src/components/composed/CkConversation/CkConversation.vue +19 -7
  50. package/src/composables/fileUpload/index.spec.ts +213 -0
  51. package/src/composables/fileUpload/index.ts +83 -0
  52. package/src/composables/fileUpload/types.ts +409 -0
  53. package/src/composables/fileUpload/useFileUpload.ts +1242 -0
  54. package/src/composables/fileUpload/useFileUploadWrapper.ts +382 -0
  55. package/src/composables/index.ts +2 -1
  56. package/src/composables/useConversations.spec.ts +118 -0
  57. package/src/composables/useConversations.ts +32 -0
  58. package/src/handlers/useChatKitLabels.spec.ts +16 -2
  59. package/src/handlers/useMessageHandler.spec.ts +112 -44
  60. package/src/handlers/useMessageHandler.ts +16 -2
  61. package/src/helpers/conversationTree.spec.ts +46 -19
  62. package/src/index.ts +11 -2
  63. package/src/services/index.ts +0 -2
  64. package/src/services/types.ts +2 -2
  65. package/src/types.ts +55 -2
  66. package/dist/ChatService-LogwJNxh.js +0 -9
  67. package/dist/services/ChatService.d.ts +0 -42
  68. package/dist/services/ChatService.d.ts.map +0 -1
  69. package/dist/services/MockChatService.d.ts +0 -10
  70. package/dist/services/MockChatService.d.ts.map +0 -1
  71. package/dist/useConversationSessions-ulFhtpQr.js +0 -243
  72. package/src/services/ChatService.ts +0 -60
  73. package/src/services/MockChatService.ts +0 -47
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- export { ChatService, type IChatService, type ChatConfig, type ChatMessage, type ChatResponse, type ConversationSummary, type HttpClient, type RawConversation, type RawConversationList, type StreamEvent, } from './services';
1
+ export { type ChatConfig, type ChatMessage, type ChatResponse, type ConversationSummary, type HttpClient, type RawConversation, type RawConversationList, type StreamEvent, } from './services';
2
2
  export { useMessageHandler, useMarkdownParser, provideChatKitLabels, useChatKitLabels, defaultChatKitLabels, CHAT_KIT_LABELS, } from './handlers';
3
3
  export type { ChatKitLabels } from './handlers';
4
4
  export { useChat, useConversationSessions, useConversations, } from './composables';
5
5
  export type { ChatActions, ChatState, ConversationSession, CreateConversationInput, MessageHandler, MutationOptions, UseChatReturn, UseConversationSessionsReturn, UseConversationsOptions, UseConversationsReturn, } from './composables';
6
6
  export { CkAgentMessage, CkConversation, CkConversationLayout, CkCopyMessageActionButton, CkFeedbackButton, CkHistory, CkHistoryItem, CkHistoryWrapper, CkInput, CkMarkdownRenderer, CkMessageActionButton, CkPromptActionButton, CkPromptStopButton, CkPromptInput, CkPromptInputDisclaimer, CkSendIcon, CkStopIcon, CkUserMessage, } from './components';
7
7
  export type { CkConversationLayoutProps, CkConversationProps, CkFeedbackButtonProps, CkFeedbackValue, CkHistoryEmits, CkHistoryItemEmits, CkHistoryItemProps, CkHistoryProps, CkHistoryWrapperProps, CkMarkdownElementClasses, CkMarkdownRendererProps, CkUserMessageProps, } from './components';
8
- export type { BaseMessage, Conversation, ConversationTree, MessageRole, MessageStatus, TreeMessage, } from './types';
8
+ export type { BaseMessage, Conversation, ConversationTree, MessageContent, MessageContentStatus, MessageReasoningContent, MessageRole, MessageStatus, MessageTextContent, MessageToolContent, TreeMessage, } from './types';
9
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,WAAW,EACX,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,WAAW,GACjB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGhD,OAAO,EACL,OAAO,EACP,uBAAuB,EACvB,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,WAAW,EACX,SAAS,EACT,mBAAmB,EACnB,uBAAuB,EACvB,cAAc,EACd,eAAe,EACf,aAAa,EACb,6BAA6B,EAC7B,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,cAAc,EACd,cAAc,EACd,oBAAoB,EACpB,yBAAyB,EACzB,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,OAAO,EACP,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,EACb,uBAAuB,EACvB,UAAU,EACV,UAAU,EACV,aAAa,GACd,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,yBAAyB,EACzB,mBAAmB,EACnB,qBAAqB,EACrB,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,qBAAqB,EACrB,wBAAwB,EACxB,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AAGtB,YAAY,EACV,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACX,aAAa,EACb,WAAW,GACZ,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,WAAW,GACjB,MAAM,YAAY,CAAC;AASpB,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,GAChB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGhD,OAAO,EACL,OAAO,EACP,uBAAuB,EACvB,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,WAAW,EACX,SAAS,EACT,mBAAmB,EACnB,uBAAuB,EACvB,cAAc,EACd,eAAe,EACf,aAAa,EACb,6BAA6B,EAC7B,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,cAAc,EACd,cAAc,EACd,oBAAoB,EACpB,yBAAyB,EACzB,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,OAAO,EACP,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,EACb,uBAAuB,EACvB,UAAU,EACV,UAAU,EACV,aAAa,GACd,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,yBAAyB,EACzB,mBAAmB,EACnB,qBAAqB,EACrB,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,qBAAqB,EACrB,wBAAwB,EACxB,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,cAAc,CAAC;AAGtB,YAAY,EACV,WAAW,EACX,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,uBAAuB,EACvB,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,GACZ,MAAM,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -1,15 +1,13 @@
1
- import { C } from "./ChatService-LogwJNxh.js";
2
- import { u } from "./useMessageHandler-BmBdu2EW.js";
3
- import { C as C2, d, p, u as u2, a } from "./useChatKitLabels-ftC52m7f.js";
4
- import { u as u3, a as a2, b } from "./useConversationSessions-ulFhtpQr.js";
5
- import { _, a as a3, b as b2, C as C3, c, d as d2, e, f, g, h, i, j, k, l, m, n, o, p as p2 } from "./CkInput.vue_vue_type_script_setup_true_lang-CyGLItQZ.js";
1
+ import { u } from "./useMessageHandler-ZRC-gIOJ.js";
2
+ import { C, d, p, u as u2, a } from "./useChatKitLabels-ftC52m7f.js";
3
+ import { u as u3, a as a2, b } from "./index-BbTgIoYa.js";
4
+ import { _, a as a3, b as b2, C as C2, c, d as d2, e, f, g, h, i, j, k, l, m, n, o, p as p2 } from "./CkInput.vue_vue_type_script_setup_true_lang-C5C09u0z.js";
6
5
  export {
7
- C2 as CHAT_KIT_LABELS,
8
- C as ChatService,
6
+ C as CHAT_KIT_LABELS,
9
7
  _ as CkAgentMessage,
10
8
  a3 as CkConversation,
11
9
  b2 as CkConversationLayout,
12
- C3 as CkCopyMessageActionButton,
10
+ C2 as CkCopyMessageActionButton,
13
11
  c as CkFeedbackButton,
14
12
  d2 as CkHistory,
15
13
  e as CkHistoryItem,
@@ -1,4 +1,2 @@
1
- export { ChatService, type IChatService } from './ChatService';
2
- export { MockChatService } from './MockChatService';
3
1
  export type { ChatConfig, ChatMessage, ChatResponse, ConversationSummary, HttpClient, RawConversation, RawConversationList, StreamEvent, } from './types';
4
2
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,YAAY,EACV,UAAU,EACV,WAAW,EACX,YAAY,EACZ,mBAAmB,EACnB,UAAU,EACV,eAAe,EACf,mBAAmB,EACnB,WAAW,GACZ,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,UAAU,EACV,WAAW,EACX,YAAY,EACZ,mBAAmB,EACnB,UAAU,EACV,eAAe,EACf,mBAAmB,EACnB,WAAW,GACZ,MAAM,SAAS,CAAC"}
@@ -1,37 +1 @@
1
- import { C as ChatService } from "../ChatService-LogwJNxh.js";
2
- class MockChatService extends ChatService {
3
- constructor() {
4
- super({});
5
- }
6
- async sendMessage(message) {
7
- const replyText = `Mock reply: ${message.content}`;
8
- return {
9
- message: this.createBaseMessage({
10
- role: "assistant",
11
- content: replyText
12
- }),
13
- metadata: { mock: true }
14
- };
15
- }
16
- async getHistory() {
17
- return [];
18
- }
19
- async streamMessage(message, onEvent) {
20
- const response = await this.sendMessage(message);
21
- onEvent({ type: "chunk", data: response.message.content });
22
- onEvent({ type: "done" });
23
- }
24
- createBaseMessage(input) {
25
- return {
26
- id: crypto.randomUUID(),
27
- role: input.role,
28
- content: input.content,
29
- createdAt: /* @__PURE__ */ new Date(),
30
- status: "completed"
31
- };
32
- }
33
- }
34
- export {
35
- ChatService,
36
- MockChatService
37
- };
1
+
@@ -1,4 +1,4 @@
1
- import type { BaseMessage, MessageRole } from '../types';
1
+ import type { BaseMessage, MessageContent, MessageRole } from '../types';
2
2
  /**
3
3
  * Minimal HTTP transport contract that chat-kit composables depend on.
4
4
  *
@@ -58,7 +58,7 @@ export interface RawConversationList {
58
58
  export interface ChatMessage {
59
59
  id: string;
60
60
  role: MessageRole;
61
- content: string;
61
+ content: MessageContent[];
62
62
  }
63
63
  /**
64
64
  * Response from the chat service
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/services/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEzD;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,CAAC,EACH,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAC7C,OAAO,CAAC,CAAC,CAAC,CAAC;IACd,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAClD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/services/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEzE;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,CAAC,EACH,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,GAC7C,OAAO,CAAC,CAAC,CAAC,CAAC;IACd,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAClD,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAC7D,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC"}
package/dist/types.d.ts CHANGED
@@ -3,10 +3,55 @@
3
3
  */
4
4
  export type MessageRole = 'user' | 'assistant' | 'system';
5
5
  export type MessageStatus = 'started' | 'sending' | 'completed' | 'error';
6
+ /**
7
+ * Lifecycle of a single content item inside a message — used to indicate
8
+ * whether a streamed text part is still being produced, has finished, or was
9
+ * cut short. Tool content does not carry a status (it represents a single
10
+ * fired tool call) so it always omits this field.
11
+ */
12
+ export type MessageContentStatus = 'in_progress' | 'completed' | 'incomplete';
13
+ /**
14
+ * Plain text part of a message. Multiple text parts can appear in a single
15
+ * assistant turn, interleaved with reasoning and tool calls.
16
+ */
17
+ export interface MessageTextContent {
18
+ type: 'text';
19
+ id: string;
20
+ text: string;
21
+ status: MessageContentStatus;
22
+ }
23
+ /**
24
+ * Tool invocation triggered by the assistant during its turn. Only the tool
25
+ * name is rendered; arguments and results are intentionally outside the
26
+ * UI-facing model and should be tracked by the host application.
27
+ */
28
+ export interface MessageToolContent {
29
+ type: 'tool';
30
+ id: string;
31
+ name: string;
32
+ }
33
+ /**
34
+ * Chain-of-thought / reasoning segment produced by the assistant. `text` is
35
+ * the (optional) human-readable trail — populated when the underlying
36
+ * transport exposes reasoning content (`REASONING_MESSAGE_CONTENT` deltas
37
+ * during a live run, `reasoning_text` parts when re-hydrating from the
38
+ * Responses API). Surfaces that only need a "Thinking…" / "Thought"
39
+ * indicator can ignore it and rely on `status` alone.
40
+ */
41
+ export interface MessageReasoningContent {
42
+ type: 'reasoning';
43
+ id: string;
44
+ text?: string;
45
+ status: MessageContentStatus;
46
+ }
47
+ /**
48
+ * Discriminated union of all content parts that can appear inside a message.
49
+ */
50
+ export type MessageContent = MessageTextContent | MessageToolContent | MessageReasoningContent;
6
51
  export interface BaseMessage {
7
52
  id: string;
8
53
  role: MessageRole;
9
- content: string;
54
+ content: MessageContent[];
10
55
  createdAt: Date;
11
56
  status: MessageStatus;
12
57
  siblingIds?: string[];
@@ -18,7 +63,7 @@ export interface TreeMessage {
18
63
  childrenIds: string[];
19
64
  activeChildIndex: number;
20
65
  role: MessageRole;
21
- content: string;
66
+ content: MessageContent[];
22
67
  status: MessageStatus;
23
68
  createdAt: Date;
24
69
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE1D,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;AAE1E,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;IAChB,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACnC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;AAE1D,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG,aAAa,GAAG,WAAW,GAAG,YAAY,CAAC;AAE9E;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,oBAAoB,CAAC;CAC9B;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,WAAW,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,oBAAoB,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GACtB,kBAAkB,GAClB,kBAAkB,GAClB,uBAAuB,CAAC;AAE5B,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,SAAS,EAAE,IAAI,CAAC;IAChB,MAAM,EAAE,aAAa,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,WAAW,CAAC;IAClB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,MAAM,EAAE,aAAa,CAAC;IACtB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACnC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB"}
@@ -1,3 +1,4 @@
1
+ import { g as generateId } from "./generateId-BHf0W4Rq.js";
1
2
  import { reactive, ref, computed, readonly } from "vue";
2
3
  function createEmptyTree() {
3
4
  return { nodes: {}, rootId: null };
@@ -77,9 +78,6 @@ function navigate(tree, targetId) {
77
78
  if (idx === -1) return;
78
79
  parent.activeChildIndex = idx;
79
80
  }
80
- function generateId() {
81
- return typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : String(Date.now());
82
- }
83
81
  const useMessageHandler = () => {
84
82
  const tree = reactive(createEmptyTree());
85
83
  const currentUserMessage = ref("");
@@ -98,13 +96,16 @@ const useMessageHandler = () => {
98
96
  const id = generateId();
99
97
  const text = currentUserMessage.value.trim();
100
98
  if (!text) throw new Error("No message");
99
+ const content = [
100
+ { type: "text", id: generateId(), text, status: "completed" }
101
+ ];
101
102
  const node = {
102
103
  id,
103
104
  parentId: currentLeafId.value,
104
105
  childrenIds: [],
105
106
  activeChildIndex: 0,
106
107
  role: "user",
107
- content: text,
108
+ content,
108
109
  status: "completed",
109
110
  createdAt: /* @__PURE__ */ new Date()
110
111
  };
@@ -180,6 +181,5 @@ const useMessageHandler = () => {
180
181
  };
181
182
  };
182
183
  export {
183
- generateId as g,
184
184
  useMessageHandler as u
185
185
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aleph-alpha/chat-kit",
3
- "version": "1.3.1",
3
+ "version": "1.5.0",
4
4
  "description": "Chat kit package for building chat interfaces with Vue",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -26,6 +26,14 @@
26
26
  "./handlers": {
27
27
  "import": "./dist/handlers/index.js",
28
28
  "types": "./dist/handlers/index.d.ts"
29
+ },
30
+ "./adapters": {
31
+ "import": "./dist/adapters/index.js",
32
+ "types": "./dist/adapters/index.d.ts"
33
+ },
34
+ "./adapters/responses-api": {
35
+ "import": "./dist/adapters/responses-api.js",
36
+ "types": "./dist/adapters/responses-api.d.ts"
29
37
  }
30
38
  },
31
39
  "files": [
@@ -64,7 +72,7 @@
64
72
  "remark-parse": "^11.0.0",
65
73
  "remark-rehype": "^11.1.2",
66
74
  "unified": "^11.0.5",
67
- "@aleph-alpha/ui-library": "2.4.2"
75
+ "@aleph-alpha/ui-library": "2.4.3"
68
76
  },
69
77
  "devDependencies": {
70
78
  "@storybook/addon-a11y": "10.2.17",
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Format adapters for chat-kit.
3
+ *
4
+ * Adapters convert a transport-specific transcript shape into chat-kit's
5
+ * domain model (`ConversationTree`). They are intentionally optional — the
6
+ * message handler only knows about `setTree(tree)`, so apps that use a
7
+ * different format can write their own adapter (or skip the concept
8
+ * entirely and build trees by hand).
9
+ *
10
+ * Currently shipped:
11
+ * - `responses-api` — OpenAI-style `GET /conversations/{id}/responses`.
12
+ *
13
+ * Re-exported via `@aleph-alpha/chat-kit/adapters`. Use the more granular
14
+ * `@aleph-alpha/chat-kit/adapters/responses-api` subpath when you only
15
+ * need a specific adapter.
16
+ */
17
+ export * from './responses-api';
@@ -0,0 +1,365 @@
1
+ import { useMessageHandler } from '../handlers/useMessageHandler';
2
+ import type { MessageContent, MessageTextContent } from '../types';
3
+ import {
4
+ responsesToTree,
5
+ type ResponseOutputMessage,
6
+ type ResponseOutputReasoning,
7
+ type ResponseOutputTool,
8
+ type StoredResponse,
9
+ } from './responses-api';
10
+
11
+ function getText(content: MessageContent[]): string {
12
+ return content
13
+ .filter((c): c is MessageTextContent => c.type === 'text')
14
+ .map((c) => c.text)
15
+ .join('');
16
+ }
17
+
18
+ function makeResponse(
19
+ overrides: Partial<StoredResponse> & { id: string },
20
+ ): StoredResponse {
21
+ return {
22
+ object: 'response',
23
+ created_at: 1_777_456_700,
24
+ completed_at: 1_777_456_701,
25
+ status: 'completed',
26
+ model: 'pharia-1',
27
+ output: [],
28
+ request_input: [],
29
+ ...overrides,
30
+ };
31
+ }
32
+
33
+ function userInput(text: string): Record<string, unknown> {
34
+ return { role: 'user', content: text };
35
+ }
36
+
37
+ function assistantOutput(id: string, text: string): ResponseOutputMessage {
38
+ return {
39
+ type: 'message',
40
+ id,
41
+ status: 'completed',
42
+ role: 'assistant',
43
+ content: [{ type: 'output_text', text }],
44
+ };
45
+ }
46
+
47
+ function reasoningOutput(id: string, text: string): ResponseOutputReasoning {
48
+ return {
49
+ type: 'reasoning',
50
+ id,
51
+ status: 'completed',
52
+ role: null,
53
+ content: [{ type: 'reasoning_text', text }],
54
+ };
55
+ }
56
+
57
+ function toolOutput(
58
+ overrides: Partial<ResponseOutputTool> & { id: string },
59
+ ): ResponseOutputTool {
60
+ return {
61
+ type: 'mcp_list_tools',
62
+ server_label: 'firecrawl',
63
+ tools: [],
64
+ ...overrides,
65
+ };
66
+ }
67
+
68
+ describe('responsesToTree', () => {
69
+ it('returns an empty tree for an empty response list', () => {
70
+ expect(responsesToTree([])).toEqual({ nodes: {}, rootId: null });
71
+ });
72
+
73
+ it('builds a linear tree from a single response with one user input and one assistant output', () => {
74
+ const tree = responsesToTree([
75
+ makeResponse({
76
+ id: 'r1',
77
+ request_input: [userInput('Hello')],
78
+ output: [assistantOutput('msg-a1', 'Hi there')],
79
+ }),
80
+ ]);
81
+
82
+ const ids = Object.keys(tree.nodes);
83
+ expect(ids).toHaveLength(2);
84
+
85
+ const userNode = tree.nodes['user-r1-0']!;
86
+ expect(userNode.role).toBe('user');
87
+ expect(userNode.parentId).toBeNull();
88
+ expect(getText(userNode.content)).toBe('Hello');
89
+
90
+ const assistantNode = tree.nodes['msg-a1']!;
91
+ expect(assistantNode.role).toBe('assistant');
92
+ expect(assistantNode.parentId).toBe('user-r1-0');
93
+ expect(getText(assistantNode.content)).toBe('Hi there');
94
+
95
+ expect(tree.rootId).toBe('user-r1-0');
96
+ });
97
+
98
+ it('chains a follow-up response under the previous assistant message via previous_response_id', () => {
99
+ const tree = responsesToTree([
100
+ makeResponse({
101
+ id: 'r1',
102
+ request_input: [userInput('First')],
103
+ output: [assistantOutput('msg-a1', 'A1')],
104
+ }),
105
+ makeResponse({
106
+ id: 'r2',
107
+ previous_response_id: 'r1',
108
+ request_input: [userInput('Second')],
109
+ output: [assistantOutput('msg-a2', 'A2')],
110
+ }),
111
+ ]);
112
+
113
+ expect(tree.nodes['user-r2-0']!.parentId).toBe('msg-a1');
114
+ expect(tree.nodes['msg-a2']!.parentId).toBe('user-r2-0');
115
+ expect(tree.rootId).toBe('user-r1-0');
116
+ });
117
+
118
+ it('treats sibling responses (same previous_response_id) as branches and surfaces the latest as active', () => {
119
+ const tree = responsesToTree([
120
+ makeResponse({
121
+ id: 'r1',
122
+ request_input: [userInput('Hello')],
123
+ output: [assistantOutput('msg-a1', 'Hi')],
124
+ }),
125
+ makeResponse({
126
+ id: 'r2',
127
+ previous_response_id: 'r1',
128
+ request_input: [userInput('Branch A')],
129
+ output: [assistantOutput('msg-a2', 'A')],
130
+ }),
131
+ makeResponse({
132
+ id: 'r3',
133
+ previous_response_id: 'r1',
134
+ request_input: [userInput('Branch B')],
135
+ output: [assistantOutput('msg-a3', 'B')],
136
+ }),
137
+ ]);
138
+
139
+ const a1 = tree.nodes['msg-a1']!;
140
+ expect(a1.childrenIds).toEqual(['user-r2-0', 'user-r3-0']);
141
+ expect(a1.activeChildIndex).toBe(1);
142
+ });
143
+
144
+ it('skips responses with no visible content but keeps descendants attached to the parent tail', () => {
145
+ const tree = responsesToTree([
146
+ makeResponse({
147
+ id: 'r1',
148
+ request_input: [userInput('Hello')],
149
+ output: [assistantOutput('msg-a1', 'Hi')],
150
+ }),
151
+ makeResponse({
152
+ id: 'r2',
153
+ previous_response_id: 'r1',
154
+ request_input: [],
155
+ output: [],
156
+ }),
157
+ makeResponse({
158
+ id: 'r3',
159
+ previous_response_id: 'r2',
160
+ request_input: [userInput('Follow-up')],
161
+ output: [assistantOutput('msg-a3', 'Reply')],
162
+ }),
163
+ ]);
164
+
165
+ expect(tree.nodes['user-r3-0']!.parentId).toBe('msg-a1');
166
+ });
167
+
168
+ it('extracts text from string content as well as output_text / text parts', () => {
169
+ const tree = responsesToTree([
170
+ makeResponse({
171
+ id: 'r1',
172
+ request_input: [userInput('plain string')],
173
+ output: [
174
+ {
175
+ type: 'message',
176
+ id: 'msg-mixed',
177
+ status: 'completed',
178
+ role: 'assistant',
179
+ content: [
180
+ { type: 'output_text', text: 'first ' },
181
+ { type: 'reasoning_summary', text: 'should-be-ignored' },
182
+ { type: 'text', text: 'second' },
183
+ ],
184
+ },
185
+ ],
186
+ }),
187
+ ]);
188
+
189
+ expect(getText(tree.nodes['user-r1-0']!.content)).toBe('plain string');
190
+ expect(getText(tree.nodes['msg-mixed']!.content)).toBe('first second');
191
+ });
192
+
193
+ it('merges reasoning, tool calls, and message text into a single assistant node, preserving wire order', () => {
194
+ const tree = responsesToTree([
195
+ makeResponse({
196
+ id: 'r1',
197
+ request_input: [userInput('Hello')],
198
+ output: [
199
+ toolOutput({
200
+ id: 'tool-list',
201
+ type: 'mcp_list_tools',
202
+ server_label: 'firecrawl',
203
+ tools: [{ name: 'firecrawl_scrape' }],
204
+ }),
205
+ reasoningOutput('rs-1', 'thinking out loud'),
206
+ toolOutput({
207
+ id: 'tool-call-1',
208
+ type: 'mcp_call',
209
+ name: 'firecrawl_scrape',
210
+ }),
211
+ assistantOutput('msg-a1', 'Hi'),
212
+ ],
213
+ }),
214
+ ]);
215
+
216
+ // Reasoning and tool items don't get their own tree nodes; they are
217
+ // rolled up onto the assistant message.
218
+ expect(tree.nodes['tool-list']).toBeUndefined();
219
+ expect(tree.nodes['rs-1']).toBeUndefined();
220
+ expect(tree.nodes['tool-call-1']).toBeUndefined();
221
+
222
+ const assistant = tree.nodes['msg-a1']!;
223
+ expect(assistant).toBeDefined();
224
+ // mcp_list_tools is informational and skipped; reasoning + mcp_call +
225
+ // text remain in their original order.
226
+ expect(assistant.content).toHaveLength(3);
227
+ expect(assistant.content[0]).toMatchObject({
228
+ type: 'reasoning',
229
+ id: 'rs-1',
230
+ text: 'thinking out loud',
231
+ status: 'completed',
232
+ });
233
+ expect(assistant.content[1]).toMatchObject({
234
+ type: 'tool',
235
+ id: 'tool-call-1',
236
+ name: 'firecrawl_scrape',
237
+ });
238
+ expect(assistant.content[2]).toMatchObject({
239
+ type: 'text',
240
+ id: 'msg-a1',
241
+ text: 'Hi',
242
+ status: 'completed',
243
+ });
244
+ });
245
+
246
+ it('also picks up function_call / tool_call outputs as tool parts', () => {
247
+ const tree = responsesToTree([
248
+ makeResponse({
249
+ id: 'r1',
250
+ request_input: [userInput('Hello')],
251
+ output: [
252
+ toolOutput({ id: 'fn-1', type: 'function_call', name: 'lookup' }),
253
+ toolOutput({ id: 'tc-1', type: 'tool_call', name: 'shell' }),
254
+ assistantOutput('msg-a1', 'Done'),
255
+ ],
256
+ }),
257
+ ]);
258
+
259
+ const assistant = tree.nodes['msg-a1']!;
260
+ expect(assistant.content).toHaveLength(3);
261
+ expect(assistant.content[0]).toMatchObject({
262
+ type: 'tool',
263
+ id: 'fn-1',
264
+ name: 'lookup',
265
+ });
266
+ expect(assistant.content[1]).toMatchObject({
267
+ type: 'tool',
268
+ id: 'tc-1',
269
+ name: 'shell',
270
+ });
271
+ expect(assistant.content[2].type).toBe('text');
272
+ });
273
+
274
+ it('creates an assistant node from reasoning / tool outputs even when no message output is present', () => {
275
+ const tree = responsesToTree([
276
+ makeResponse({
277
+ id: 'r1',
278
+ request_input: [userInput('Hello')],
279
+ output: [
280
+ reasoningOutput('rs-1', 'planning'),
281
+ toolOutput({ id: 'tool-1', type: 'mcp_call', name: 'search' }),
282
+ ],
283
+ }),
284
+ ]);
285
+
286
+ const assistant = tree.nodes['assistant-r1']!;
287
+ expect(assistant).toBeDefined();
288
+ expect(assistant.role).toBe('assistant');
289
+ expect(assistant.content).toHaveLength(2);
290
+ expect(assistant.content[0]).toMatchObject({
291
+ type: 'reasoning',
292
+ text: 'planning',
293
+ });
294
+ expect(assistant.content[1].type).toBe('tool');
295
+ });
296
+
297
+ it('omits reasoning text when no reasoning_text parts are present', () => {
298
+ const tree = responsesToTree([
299
+ makeResponse({
300
+ id: 'r1',
301
+ request_input: [userInput('Hello')],
302
+ output: [
303
+ {
304
+ type: 'reasoning',
305
+ id: 'rs-empty',
306
+ status: 'completed',
307
+ role: null,
308
+ content: [],
309
+ },
310
+ assistantOutput('msg-a1', 'Hi'),
311
+ ],
312
+ }),
313
+ ]);
314
+
315
+ const reasoning = tree.nodes['msg-a1']!.content[0];
316
+ expect(reasoning.type).toBe('reasoning');
317
+ if (reasoning.type === 'reasoning') {
318
+ expect(reasoning.text).toBeUndefined();
319
+ }
320
+ });
321
+
322
+ it('chains the next response under a reasoning-only assistant turn', () => {
323
+ const tree = responsesToTree([
324
+ makeResponse({
325
+ id: 'r1',
326
+ request_input: [userInput('Hello')],
327
+ output: [reasoningOutput('rs-1', 'planning')],
328
+ }),
329
+ makeResponse({
330
+ id: 'r2',
331
+ previous_response_id: 'r1',
332
+ request_input: [userInput('Follow-up')],
333
+ output: [assistantOutput('msg-a2', 'Reply')],
334
+ }),
335
+ ]);
336
+
337
+ expect(tree.nodes['rs-1']).toBeUndefined();
338
+ expect(tree.nodes['assistant-r1']).toBeDefined();
339
+ expect(tree.nodes['user-r2-0']!.parentId).toBe('assistant-r1');
340
+ expect(tree.nodes['msg-a2']!.parentId).toBe('user-r2-0');
341
+ });
342
+ });
343
+
344
+ describe('responsesToTree + useMessageHandler.setTree', () => {
345
+ it('hydrates a handler from a Responses-API list via the documented adapter pattern', () => {
346
+ const handler = useMessageHandler();
347
+ handler.setTree(
348
+ responsesToTree([
349
+ makeResponse({
350
+ id: 'r1',
351
+ request_input: [userInput('Hello')],
352
+ output: [assistantOutput('msg-a1', 'Hi')],
353
+ }),
354
+ ]),
355
+ );
356
+
357
+ expect(handler.messages.value).toHaveLength(2);
358
+ expect(handler.messages.value.map((m) => m.role)).toEqual([
359
+ 'user',
360
+ 'assistant',
361
+ ]);
362
+ expect(getText(handler.messages.value[0].content)).toBe('Hello');
363
+ expect(getText(handler.messages.value[1].content)).toBe('Hi');
364
+ });
365
+ });