@kognitivedev/ui 0.2.11

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 (258) hide show
  1. package/.turbo/turbo-build.log +2 -0
  2. package/CHANGELOG.md +19 -0
  3. package/README.md +264 -0
  4. package/dist/__tests__/context-provider.test.d.ts +1 -0
  5. package/dist/__tests__/context-provider.test.js +38 -0
  6. package/dist/__tests__/event-emitter.test.d.ts +1 -0
  7. package/dist/__tests__/event-emitter.test.js +62 -0
  8. package/dist/__tests__/keyboard-shortcuts.test.d.ts +1 -0
  9. package/dist/__tests__/keyboard-shortcuts.test.js +36 -0
  10. package/dist/__tests__/kognitive-runtime.test.d.ts +1 -0
  11. package/dist/__tests__/kognitive-runtime.test.js +58 -0
  12. package/dist/__tests__/kognitive-transport.test.d.ts +1 -0
  13. package/dist/__tests__/kognitive-transport.test.js +96 -0
  14. package/dist/__tests__/make-tool-ui.test.d.ts +1 -0
  15. package/dist/__tests__/make-tool-ui.test.js +50 -0
  16. package/dist/__tests__/message-helpers.test.d.ts +1 -0
  17. package/dist/__tests__/message-helpers.test.js +80 -0
  18. package/dist/__tests__/thread-manager.test.d.ts +1 -0
  19. package/dist/__tests__/thread-manager.test.js +84 -0
  20. package/dist/__tests__/tool-ui-registry.test.d.ts +1 -0
  21. package/dist/__tests__/tool-ui-registry.test.js +68 -0
  22. package/dist/__tests__/toolkit.test.d.ts +1 -0
  23. package/dist/__tests__/toolkit.test.js +33 -0
  24. package/dist/components/attachment-preview.d.ts +8 -0
  25. package/dist/components/attachment-preview.js +10 -0
  26. package/dist/components/composer.d.ts +6 -0
  27. package/dist/components/composer.js +10 -0
  28. package/dist/components/error-banner.d.ts +6 -0
  29. package/dist/components/error-banner.js +8 -0
  30. package/dist/components/markdown-content.d.ts +13 -0
  31. package/dist/components/markdown-content.js +31 -0
  32. package/dist/components/message.d.ts +7 -0
  33. package/dist/components/message.js +28 -0
  34. package/dist/components/status-indicator.d.ts +6 -0
  35. package/dist/components/status-indicator.js +16 -0
  36. package/dist/components/suggestions.d.ts +4 -0
  37. package/dist/components/suggestions.js +12 -0
  38. package/dist/components/thread-list.d.ts +4 -0
  39. package/dist/components/thread-list.js +20 -0
  40. package/dist/components/thread.d.ts +15 -0
  41. package/dist/components/thread.js +19 -0
  42. package/dist/components/tool-approval.d.ts +7 -0
  43. package/dist/components/tool-approval.js +12 -0
  44. package/dist/components/tool-invocation.d.ts +5 -0
  45. package/dist/components/tool-invocation.js +33 -0
  46. package/dist/context/kognitive-context.d.ts +2 -0
  47. package/dist/context/kognitive-context.js +6 -0
  48. package/dist/context/types.d.ts +44 -0
  49. package/dist/context/types.js +2 -0
  50. package/dist/context/use-kognitive.d.ts +1 -0
  51. package/dist/context/use-kognitive.js +7 -0
  52. package/dist/hooks/use-auto-scroll.d.ts +2 -0
  53. package/dist/hooks/use-auto-scroll.js +18 -0
  54. package/dist/hooks/use-copy-to-clipboard.d.ts +4 -0
  55. package/dist/hooks/use-copy-to-clipboard.js +13 -0
  56. package/dist/hooks/use-keyboard-shortcuts.d.ts +8 -0
  57. package/dist/hooks/use-keyboard-shortcuts.js +24 -0
  58. package/dist/hooks/use-kognitive-chat.d.ts +38 -0
  59. package/dist/hooks/use-kognitive-chat.js +35 -0
  60. package/dist/hooks/use-kognitive-event.d.ts +12 -0
  61. package/dist/hooks/use-kognitive-event.js +47 -0
  62. package/dist/hooks/use-streaming-status.d.ts +2 -0
  63. package/dist/hooks/use-streaming-status.js +8 -0
  64. package/dist/hooks/use-thread-manager.d.ts +20 -0
  65. package/dist/hooks/use-thread-manager.js +83 -0
  66. package/dist/index.d.ts +41 -0
  67. package/dist/index.js +92 -0
  68. package/dist/kognitive-ui.d.ts +48 -0
  69. package/dist/kognitive-ui.js +130 -0
  70. package/dist/primitives/action-bar/action-bar-copy.d.ts +6 -0
  71. package/dist/primitives/action-bar/action-bar-copy.js +16 -0
  72. package/dist/primitives/action-bar/action-bar-edit.d.ts +6 -0
  73. package/dist/primitives/action-bar/action-bar-edit.js +11 -0
  74. package/dist/primitives/action-bar/action-bar-feedback.d.ts +10 -0
  75. package/dist/primitives/action-bar/action-bar-feedback.js +30 -0
  76. package/dist/primitives/action-bar/action-bar-retry.d.ts +6 -0
  77. package/dist/primitives/action-bar/action-bar-retry.js +25 -0
  78. package/dist/primitives/action-bar/action-bar-root.d.ts +6 -0
  79. package/dist/primitives/action-bar/action-bar-root.js +7 -0
  80. package/dist/primitives/action-bar/action-bar-stop.d.ts +6 -0
  81. package/dist/primitives/action-bar/action-bar-stop.js +11 -0
  82. package/dist/primitives/action-bar/index.d.ts +14 -0
  83. package/dist/primitives/action-bar/index.js +17 -0
  84. package/dist/primitives/composer/composer-attachment-trigger.d.ts +7 -0
  85. package/dist/primitives/composer/composer-attachment-trigger.js +33 -0
  86. package/dist/primitives/composer/composer-attachments.d.ts +7 -0
  87. package/dist/primitives/composer/composer-attachments.js +16 -0
  88. package/dist/primitives/composer/composer-input.d.ts +6 -0
  89. package/dist/primitives/composer/composer-input.js +19 -0
  90. package/dist/primitives/composer/composer-root.d.ts +6 -0
  91. package/dist/primitives/composer/composer-root.js +91 -0
  92. package/dist/primitives/composer/composer-send.d.ts +6 -0
  93. package/dist/primitives/composer/composer-send.js +10 -0
  94. package/dist/primitives/composer/edit-composer-root.d.ts +11 -0
  95. package/dist/primitives/composer/edit-composer-root.js +24 -0
  96. package/dist/primitives/composer/index.d.ts +15 -0
  97. package/dist/primitives/composer/index.js +19 -0
  98. package/dist/primitives/composer/use-composer.d.ts +12 -0
  99. package/dist/primitives/composer/use-composer.js +6 -0
  100. package/dist/primitives/message/index.d.ts +9 -0
  101. package/dist/primitives/message/index.js +13 -0
  102. package/dist/primitives/message/message-content.d.ts +25 -0
  103. package/dist/primitives/message/message-content.js +70 -0
  104. package/dist/primitives/message/message-role.d.ts +6 -0
  105. package/dist/primitives/message/message-role.js +11 -0
  106. package/dist/primitives/message/message-root.d.ts +9 -0
  107. package/dist/primitives/message/message-root.js +38 -0
  108. package/dist/primitives/message/use-message.d.ts +10 -0
  109. package/dist/primitives/message/use-message.js +6 -0
  110. package/dist/primitives/thread/index.d.ts +17 -0
  111. package/dist/primitives/thread/index.js +21 -0
  112. package/dist/primitives/thread/thread-empty.d.ts +5 -0
  113. package/dist/primitives/thread/thread-empty.js +11 -0
  114. package/dist/primitives/thread/thread-error.d.ts +6 -0
  115. package/dist/primitives/thread/thread-error.js +11 -0
  116. package/dist/primitives/thread/thread-loading.d.ts +5 -0
  117. package/dist/primitives/thread/thread-loading.js +11 -0
  118. package/dist/primitives/thread/thread-messages.d.ts +6 -0
  119. package/dist/primitives/thread/thread-messages.js +9 -0
  120. package/dist/primitives/thread/thread-root.d.ts +6 -0
  121. package/dist/primitives/thread/thread-root.js +12 -0
  122. package/dist/primitives/thread/thread-scroll-to-bottom.d.ts +6 -0
  123. package/dist/primitives/thread/thread-scroll-to-bottom.js +14 -0
  124. package/dist/primitives/thread/thread-suggestions.d.ts +6 -0
  125. package/dist/primitives/thread/thread-suggestions.js +14 -0
  126. package/dist/primitives/thread/use-thread.d.ts +9 -0
  127. package/dist/primitives/thread/use-thread.js +6 -0
  128. package/dist/primitives/thread-list/index.d.ts +11 -0
  129. package/dist/primitives/thread-list/index.js +15 -0
  130. package/dist/primitives/thread-list/thread-list-item.d.ts +9 -0
  131. package/dist/primitives/thread-list/thread-list-item.js +13 -0
  132. package/dist/primitives/thread-list/thread-list-items.d.ts +6 -0
  133. package/dist/primitives/thread-list/thread-list-items.js +9 -0
  134. package/dist/primitives/thread-list/thread-list-new.d.ts +6 -0
  135. package/dist/primitives/thread-list/thread-list-new.js +13 -0
  136. package/dist/primitives/thread-list/thread-list-root.d.ts +6 -0
  137. package/dist/primitives/thread-list/thread-list-root.js +16 -0
  138. package/dist/primitives/thread-list/use-thread-list.d.ts +9 -0
  139. package/dist/primitives/thread-list/use-thread-list.js +6 -0
  140. package/dist/primitives/tool-ui/index.d.ts +7 -0
  141. package/dist/primitives/tool-ui/index.js +11 -0
  142. package/dist/primitives/tool-ui/tool-ui-fallback.d.ts +5 -0
  143. package/dist/primitives/tool-ui/tool-ui-fallback.js +20 -0
  144. package/dist/primitives/tool-ui/tool-ui-root.d.ts +5 -0
  145. package/dist/primitives/tool-ui/tool-ui-root.js +20 -0
  146. package/dist/primitives/tool-ui/use-tool-ui.d.ts +2 -0
  147. package/dist/primitives/tool-ui/use-tool-ui.js +8 -0
  148. package/dist/runtime/kognitive-runtime.d.ts +30 -0
  149. package/dist/runtime/kognitive-runtime.js +32 -0
  150. package/dist/runtime/kognitive-transport.d.ts +26 -0
  151. package/dist/runtime/kognitive-transport.js +42 -0
  152. package/dist/runtime/thread-manager.d.ts +17 -0
  153. package/dist/runtime/thread-manager.js +62 -0
  154. package/dist/runtime/types.d.ts +58 -0
  155. package/dist/runtime/types.js +2 -0
  156. package/dist/tool-ui/make-tool-ui.d.ts +59 -0
  157. package/dist/tool-ui/make-tool-ui.js +10 -0
  158. package/dist/tool-ui/registry.d.ts +9 -0
  159. package/dist/tool-ui/registry.js +26 -0
  160. package/dist/tool-ui/tool-ui-context.d.ts +2 -0
  161. package/dist/tool-ui/tool-ui-context.js +6 -0
  162. package/dist/tool-ui/toolkit.d.ts +31 -0
  163. package/dist/tool-ui/toolkit.js +33 -0
  164. package/dist/tool-ui/types.d.ts +19 -0
  165. package/dist/tool-ui/types.js +2 -0
  166. package/dist/utils/cn.d.ts +2 -0
  167. package/dist/utils/cn.js +8 -0
  168. package/dist/utils/create-context.d.ts +1 -0
  169. package/dist/utils/create-context.js +16 -0
  170. package/dist/utils/message-helpers.d.ts +6 -0
  171. package/dist/utils/message-helpers.js +46 -0
  172. package/package.json +56 -0
  173. package/src/__tests__/context-provider.test.ts +43 -0
  174. package/src/__tests__/event-emitter.test.ts +69 -0
  175. package/src/__tests__/keyboard-shortcuts.test.ts +55 -0
  176. package/src/__tests__/kognitive-runtime.test.ts +62 -0
  177. package/src/__tests__/kognitive-transport.test.ts +113 -0
  178. package/src/__tests__/make-tool-ui.test.ts +60 -0
  179. package/src/__tests__/message-helpers.test.ts +101 -0
  180. package/src/__tests__/thread-manager.test.ts +118 -0
  181. package/src/__tests__/tool-ui-registry.test.ts +80 -0
  182. package/src/__tests__/toolkit.test.ts +37 -0
  183. package/src/components/attachment-preview.tsx +46 -0
  184. package/src/components/composer.tsx +59 -0
  185. package/src/components/error-banner.tsx +33 -0
  186. package/src/components/markdown-content.tsx +64 -0
  187. package/src/components/message.tsx +145 -0
  188. package/src/components/status-indicator.tsx +26 -0
  189. package/src/components/suggestions.tsx +27 -0
  190. package/src/components/thread-list.tsx +69 -0
  191. package/src/components/thread.tsx +89 -0
  192. package/src/components/tool-approval.tsx +54 -0
  193. package/src/components/tool-invocation.tsx +94 -0
  194. package/src/context/kognitive-context.tsx +8 -0
  195. package/src/context/types.ts +43 -0
  196. package/src/context/use-kognitive.ts +5 -0
  197. package/src/hooks/use-auto-scroll.ts +19 -0
  198. package/src/hooks/use-copy-to-clipboard.ts +16 -0
  199. package/src/hooks/use-keyboard-shortcuts.ts +34 -0
  200. package/src/hooks/use-kognitive-chat.ts +73 -0
  201. package/src/hooks/use-kognitive-event.ts +56 -0
  202. package/src/hooks/use-streaming-status.ts +7 -0
  203. package/src/hooks/use-thread-manager.ts +114 -0
  204. package/src/index.ts +56 -0
  205. package/src/kognitive-ui.tsx +216 -0
  206. package/src/primitives/action-bar/action-bar-copy.tsx +30 -0
  207. package/src/primitives/action-bar/action-bar-edit.tsx +24 -0
  208. package/src/primitives/action-bar/action-bar-feedback.tsx +59 -0
  209. package/src/primitives/action-bar/action-bar-retry.tsx +38 -0
  210. package/src/primitives/action-bar/action-bar-root.tsx +14 -0
  211. package/src/primitives/action-bar/action-bar-stop.tsx +24 -0
  212. package/src/primitives/action-bar/index.ts +15 -0
  213. package/src/primitives/composer/composer-attachment-trigger.tsx +70 -0
  214. package/src/primitives/composer/composer-attachments.tsx +36 -0
  215. package/src/primitives/composer/composer-input.tsx +46 -0
  216. package/src/primitives/composer/composer-root.tsx +130 -0
  217. package/src/primitives/composer/composer-send.tsx +23 -0
  218. package/src/primitives/composer/edit-composer-root.tsx +52 -0
  219. package/src/primitives/composer/index.ts +17 -0
  220. package/src/primitives/composer/use-composer.ts +19 -0
  221. package/src/primitives/message/index.ts +11 -0
  222. package/src/primitives/message/message-content.tsx +117 -0
  223. package/src/primitives/message/message-role.tsx +13 -0
  224. package/src/primitives/message/message-root.tsx +64 -0
  225. package/src/primitives/message/use-message.ts +17 -0
  226. package/src/primitives/thread/index.ts +19 -0
  227. package/src/primitives/thread/thread-empty.tsx +12 -0
  228. package/src/primitives/thread/thread-error.tsx +18 -0
  229. package/src/primitives/thread/thread-loading.tsx +12 -0
  230. package/src/primitives/thread/thread-messages.tsx +12 -0
  231. package/src/primitives/thread/thread-root.tsx +28 -0
  232. package/src/primitives/thread/thread-scroll-to-bottom.tsx +26 -0
  233. package/src/primitives/thread/thread-suggestions.tsx +31 -0
  234. package/src/primitives/thread/use-thread.ts +16 -0
  235. package/src/primitives/thread-list/index.ts +13 -0
  236. package/src/primitives/thread-list/thread-list-item.tsx +37 -0
  237. package/src/primitives/thread-list/thread-list-items.tsx +19 -0
  238. package/src/primitives/thread-list/thread-list-new.tsx +26 -0
  239. package/src/primitives/thread-list/thread-list-root.tsx +29 -0
  240. package/src/primitives/thread-list/use-thread-list.ts +16 -0
  241. package/src/primitives/tool-ui/index.ts +9 -0
  242. package/src/primitives/tool-ui/tool-ui-fallback.tsx +63 -0
  243. package/src/primitives/tool-ui/tool-ui-root.tsx +26 -0
  244. package/src/primitives/tool-ui/use-tool-ui.ts +7 -0
  245. package/src/runtime/kognitive-runtime.ts +56 -0
  246. package/src/runtime/kognitive-transport.ts +56 -0
  247. package/src/runtime/thread-manager.ts +92 -0
  248. package/src/runtime/types.ts +63 -0
  249. package/src/tool-ui/make-tool-ui.ts +71 -0
  250. package/src/tool-ui/registry.ts +27 -0
  251. package/src/tool-ui/tool-ui-context.tsx +8 -0
  252. package/src/tool-ui/toolkit.ts +40 -0
  253. package/src/tool-ui/types.ts +29 -0
  254. package/src/utils/cn.ts +6 -0
  255. package/src/utils/create-context.ts +18 -0
  256. package/src/utils/message-helpers.ts +42 -0
  257. package/tsconfig.json +15 -0
  258. package/vitest.config.ts +8 -0
@@ -0,0 +1,37 @@
1
+ import React, { useCallback, type ReactNode } from "react";
2
+ import type { ThreadSummary } from "../../runtime/types";
3
+ import { useThreadList } from "./use-thread-list";
4
+
5
+ export interface ThreadListItemProps {
6
+ thread: ThreadSummary;
7
+ isActive: boolean;
8
+ className?: string;
9
+ children?: ReactNode;
10
+ }
11
+
12
+ export function ThreadListItem({ thread, isActive, className, children }: ThreadListItemProps) {
13
+ const { setActiveSessionId } = useThreadList();
14
+
15
+ const handleClick = useCallback(() => {
16
+ setActiveSessionId(thread.sessionId);
17
+ }, [thread.sessionId, setActiveSessionId]);
18
+
19
+ return (
20
+ <button
21
+ type="button"
22
+ className={className}
23
+ onClick={handleClick}
24
+ data-active={isActive || undefined}
25
+ data-status={thread.status}
26
+ data-session-id={thread.sessionId}
27
+ aria-current={isActive ? "true" : undefined}
28
+ >
29
+ {children ?? (
30
+ <>
31
+ <span>{thread.title || thread.lastUserPreview || "New conversation"}</span>
32
+ <span>{thread.lastAssistantPreview}</span>
33
+ </>
34
+ )}
35
+ </button>
36
+ );
37
+ }
@@ -0,0 +1,19 @@
1
+ import React, { type ReactNode } from "react";
2
+ import type { ThreadSummary } from "../../runtime/types";
3
+ import { useThreadList } from "./use-thread-list";
4
+
5
+ export interface ThreadListItemsProps {
6
+ children: (thread: ThreadSummary, isActive: boolean) => ReactNode;
7
+ }
8
+
9
+ export function ThreadListItems({ children }: ThreadListItemsProps) {
10
+ const { threads, activeSessionId } = useThreadList();
11
+
12
+ return (
13
+ <>
14
+ {threads.map((thread) =>
15
+ children(thread, thread.sessionId === activeSessionId),
16
+ )}
17
+ </>
18
+ );
19
+ }
@@ -0,0 +1,26 @@
1
+ import React, { useCallback, type ReactNode } from "react";
2
+ import { useThreadList } from "./use-thread-list";
3
+
4
+ export interface ThreadListNewProps {
5
+ className?: string;
6
+ children?: ReactNode;
7
+ }
8
+
9
+ export function ThreadListNew({ className, children }: ThreadListNewProps) {
10
+ const { createThread } = useThreadList();
11
+
12
+ const handleClick = useCallback(() => {
13
+ void createThread();
14
+ }, [createThread]);
15
+
16
+ return (
17
+ <button
18
+ type="button"
19
+ className={className}
20
+ onClick={handleClick}
21
+ aria-label="New conversation"
22
+ >
23
+ {children ?? "New conversation"}
24
+ </button>
25
+ );
26
+ }
@@ -0,0 +1,29 @@
1
+ import React, { type ReactNode } from "react";
2
+ import { useKognitiveContext } from "../../context/kognitive-context";
3
+ import { ThreadListContextProvider } from "./use-thread-list";
4
+
5
+ export interface ThreadListRootProps {
6
+ className?: string;
7
+ children: ReactNode;
8
+ }
9
+
10
+ export function ThreadListRoot({ className, children }: ThreadListRootProps) {
11
+ const { threads, activeSessionId, setActiveSessionId, createThread, refreshThreads } =
12
+ useKognitiveContext();
13
+
14
+ return (
15
+ <ThreadListContextProvider
16
+ value={{
17
+ threads,
18
+ activeSessionId,
19
+ setActiveSessionId,
20
+ createThread,
21
+ refreshThreads,
22
+ }}
23
+ >
24
+ <div className={className} role="navigation" aria-label="Threads">
25
+ {children}
26
+ </div>
27
+ </ThreadListContextProvider>
28
+ );
29
+ }
@@ -0,0 +1,16 @@
1
+ import { createContext } from "../../utils/create-context";
2
+ import type { ThreadSummary } from "../../runtime/types";
3
+
4
+ export interface ThreadListContextValue {
5
+ threads: ThreadSummary[];
6
+ activeSessionId: string | null;
7
+ setActiveSessionId: (sessionId: string | null) => void;
8
+ createThread: () => Promise<void>;
9
+ refreshThreads: () => Promise<void>;
10
+ }
11
+
12
+ export const [
13
+ ThreadListContextProvider,
14
+ useThreadList,
15
+ ThreadListContext,
16
+ ] = createContext<ThreadListContextValue>("ThreadList");
@@ -0,0 +1,9 @@
1
+ import { ToolUIRoot } from "./tool-ui-root";
2
+ import { ToolUIFallback } from "./tool-ui-fallback";
3
+
4
+ export const ToolUIPrimitive = {
5
+ Root: ToolUIRoot,
6
+ Fallback: ToolUIFallback,
7
+ };
8
+
9
+ export { useToolUI } from "./use-tool-ui";
@@ -0,0 +1,63 @@
1
+ import React, { useState } from "react";
2
+ import type { ToolUIRenderProps } from "../../tool-ui/types";
3
+
4
+ export interface ToolUIFallbackProps extends ToolUIRenderProps {
5
+ className?: string;
6
+ }
7
+
8
+ function stateLabel(state: string): string {
9
+ switch (state) {
10
+ case "input-streaming": return "streaming input...";
11
+ case "input-available": return "calling...";
12
+ case "approval-requested": return "awaiting approval";
13
+ case "approval-responded": return "approved";
14
+ case "output-available": return "done";
15
+ case "error": return "error";
16
+ default: return state;
17
+ }
18
+ }
19
+
20
+ export function ToolUIFallback({ toolName, toolCallId, input, output, state, className }: ToolUIFallbackProps) {
21
+ const [expanded, setExpanded] = useState(false);
22
+
23
+ return (
24
+ <div
25
+ className={className}
26
+ data-tool={toolName}
27
+ data-tool-state={state}
28
+ data-tool-call-id={toolCallId}
29
+ >
30
+ <button
31
+ type="button"
32
+ onClick={() => setExpanded(!expanded)}
33
+ aria-expanded={expanded}
34
+ style={{ display: "flex", alignItems: "center", gap: "0.5rem", cursor: "pointer", background: "none", border: "none", padding: "0.5rem", width: "100%", textAlign: "left" }}
35
+ >
36
+ <span>&#x1F527;</span>
37
+ <strong>{toolName}</strong>
38
+ <span data-tool-state-badge={state}>{stateLabel(state)}</span>
39
+ <span style={{ marginLeft: "auto" }}>{expanded ? "\u25BC" : "\u25B6"}</span>
40
+ </button>
41
+ {expanded && (
42
+ <div style={{ padding: "0.5rem" }}>
43
+ {input !== undefined && (
44
+ <div>
45
+ <strong>Arguments:</strong>
46
+ <pre style={{ overflow: "auto", fontSize: "0.85em" }}>
47
+ {JSON.stringify(input, null, 2)}
48
+ </pre>
49
+ </div>
50
+ )}
51
+ {output !== undefined && (
52
+ <div>
53
+ <strong>Result:</strong>
54
+ <pre style={{ overflow: "auto", fontSize: "0.85em" }}>
55
+ {JSON.stringify(output, null, 2)}
56
+ </pre>
57
+ </div>
58
+ )}
59
+ </div>
60
+ )}
61
+ </div>
62
+ );
63
+ }
@@ -0,0 +1,26 @@
1
+ import React from "react";
2
+ import { useToolUIRegistry } from "../../tool-ui/tool-ui-context";
3
+ import type { ToolUIRenderProps } from "../../tool-ui/types";
4
+ import { ToolUIFallback } from "./tool-ui-fallback";
5
+
6
+ export interface ToolUIRootProps extends ToolUIRenderProps {
7
+ className?: string;
8
+ }
9
+
10
+ export function ToolUIRoot(props: ToolUIRootProps) {
11
+ let registry: ReturnType<typeof useToolUIRegistry> | undefined;
12
+
13
+ try {
14
+ registry = useToolUIRegistry();
15
+ } catch {
16
+ // Registry not available
17
+ }
18
+
19
+ const RegisteredUI = registry?.get(props.toolName);
20
+
21
+ if (RegisteredUI) {
22
+ return <RegisteredUI {...props} />;
23
+ }
24
+
25
+ return <ToolUIFallback {...props} />;
26
+ }
@@ -0,0 +1,7 @@
1
+ import { useToolUIRegistry } from "../../tool-ui/tool-ui-context";
2
+ import type { ToolUIComponent } from "../../tool-ui/types";
3
+
4
+ export function useToolUI(toolName: string): ToolUIComponent | undefined {
5
+ const registry = useToolUIRegistry();
6
+ return registry.get(toolName);
7
+ }
@@ -0,0 +1,56 @@
1
+ import type { DefaultChatTransport, UIMessage } from "ai";
2
+ import { createKognitiveTransport, type KognitiveTransportConfig } from "./kognitive-transport";
3
+ import { ThreadManager, type ThreadManagerConfig } from "./thread-manager";
4
+ import type { ToolUIRegistry } from "../tool-ui/registry";
5
+
6
+ export interface KognitiveRuntimeConfig {
7
+ /** Base URL of the Kognitive runtime */
8
+ baseUrl: string;
9
+ /** Agent name to connect to */
10
+ agentName: string;
11
+ /** Additional headers (e.g., auth) */
12
+ headers?: Record<string, string>;
13
+ /** User identity */
14
+ resourceId?: { userId?: string };
15
+ /** Initial session/thread ID */
16
+ sessionId?: string;
17
+ /** Enable thread management. When true, uses threadApiBase for CRUD. */
18
+ threads?: boolean;
19
+ /** Thread API base URL. Defaults to {baseUrl}/api/agents/{agentName}/threads */
20
+ threadApiBase?: string;
21
+ /** Tool UI registry */
22
+ toolUIRegistry?: ToolUIRegistry;
23
+ }
24
+
25
+ export class KognitiveRuntime {
26
+ readonly config: KognitiveRuntimeConfig;
27
+ readonly threadManager: ThreadManager | null;
28
+ readonly toolUIRegistry: ToolUIRegistry | null;
29
+
30
+ constructor(config: KognitiveRuntimeConfig) {
31
+ this.config = config;
32
+ this.toolUIRegistry = config.toolUIRegistry ?? null;
33
+
34
+ if (config.threads) {
35
+ const threadApiBase =
36
+ config.threadApiBase ??
37
+ `${config.baseUrl.replace(/\/$/, "")}/api/agents/${encodeURIComponent(config.agentName)}/threads`;
38
+ this.threadManager = new ThreadManager({
39
+ baseUrl: threadApiBase,
40
+ headers: config.headers,
41
+ });
42
+ } else {
43
+ this.threadManager = null;
44
+ }
45
+ }
46
+
47
+ createTransport(sessionId?: string): DefaultChatTransport<UIMessage> {
48
+ return createKognitiveTransport({
49
+ baseUrl: this.config.baseUrl,
50
+ agentName: this.config.agentName,
51
+ sessionId: sessionId ?? this.config.sessionId,
52
+ resourceId: this.config.resourceId,
53
+ headers: this.config.headers,
54
+ });
55
+ }
56
+ }
@@ -0,0 +1,56 @@
1
+ import { DefaultChatTransport, convertToModelMessages, type UIMessage } from "ai";
2
+
3
+ export interface KognitiveTransportConfig {
4
+ /**
5
+ * Explicit API endpoint URL for streaming.
6
+ * When set, takes priority over baseUrl + agentName URL construction.
7
+ * Use this for custom proxy routes (e.g., "/api/chat").
8
+ */
9
+ api?: string;
10
+ /** Base URL of the Kognitive runtime. Combined with agentName to construct the stream URL. */
11
+ baseUrl?: string;
12
+ /** Agent name. Used with baseUrl to build the URL. Also sent in body when using `api` mode. */
13
+ agentName?: string;
14
+ /** Current session/thread ID — sent in every request body */
15
+ sessionId?: string;
16
+ /** User identity — sent in every request body as resourceId */
17
+ resourceId?: { userId?: string; sessionId?: string; organizationId?: string };
18
+ /** Additional headers (e.g., auth) */
19
+ headers?: Record<string, string>;
20
+ /** Extra fields merged into every request body (e.g., { agentName: "assistant" }) */
21
+ body?: Record<string, unknown>;
22
+ }
23
+
24
+ export function createKognitiveTransport(
25
+ config: KognitiveTransportConfig,
26
+ ): DefaultChatTransport<UIMessage> {
27
+ let apiUrl: string;
28
+ if (config.api) {
29
+ apiUrl = config.api;
30
+ } else if (config.baseUrl && config.agentName) {
31
+ apiUrl = `${config.baseUrl.replace(/\/$/, "")}/api/agents/${encodeURIComponent(config.agentName)}/stream`;
32
+ } else {
33
+ throw new Error(
34
+ "createKognitiveTransport requires either `api` or both `baseUrl` and `agentName`.",
35
+ );
36
+ }
37
+
38
+ return new DefaultChatTransport<UIMessage>({
39
+ api: apiUrl,
40
+ headers: config.headers ?? {},
41
+ body: () => ({
42
+ ...(config.body ?? {}),
43
+ ...(config.agentName && config.api ? { agentName: config.agentName } : {}),
44
+ ...(config.sessionId ? { sessionId: config.sessionId } : {}),
45
+ ...(config.resourceId ? { resourceId: config.resourceId } : {}),
46
+ }),
47
+ prepareSendMessagesRequest: async ({ messages, body }) => ({
48
+ body: {
49
+ ...(body ?? {}),
50
+ messages: await convertToModelMessages(
51
+ messages.map(({ id: _id, ...message }) => message),
52
+ ),
53
+ },
54
+ }),
55
+ });
56
+ }
@@ -0,0 +1,92 @@
1
+ import type { ThreadSummary, ThreadDetail } from "./types";
2
+
3
+ export interface ThreadManagerConfig {
4
+ /** Base URL for thread API endpoints */
5
+ baseUrl: string;
6
+ /** Additional headers (e.g., auth) */
7
+ headers?: Record<string, string>;
8
+ }
9
+
10
+ export class ThreadManager {
11
+ private baseUrl: string;
12
+ private headers: Record<string, string>;
13
+
14
+ constructor(config: ThreadManagerConfig) {
15
+ this.baseUrl = config.baseUrl.replace(/\/$/, "");
16
+ this.headers = {
17
+ "Content-Type": "application/json",
18
+ ...(config.headers ?? {}),
19
+ };
20
+ }
21
+
22
+ async list(): Promise<ThreadSummary[]> {
23
+ const res = await fetch(this.baseUrl, { headers: this.headers });
24
+ if (!res.ok) {
25
+ throw new Error(
26
+ (await res.json().catch(() => ({ error: "Failed to list threads" }))).error,
27
+ );
28
+ }
29
+ const data = await res.json();
30
+ return (data.threads ?? []) as ThreadSummary[];
31
+ }
32
+
33
+ async get(sessionId: string): Promise<ThreadDetail> {
34
+ const res = await fetch(`${this.baseUrl}/${encodeURIComponent(sessionId)}`, {
35
+ headers: this.headers,
36
+ });
37
+ if (!res.ok) {
38
+ throw new Error(
39
+ (await res.json().catch(() => ({ error: "Failed to get thread" }))).error,
40
+ );
41
+ }
42
+ return (await res.json()) as ThreadDetail;
43
+ }
44
+
45
+ async create(): Promise<ThreadSummary> {
46
+ const res = await fetch(this.baseUrl, {
47
+ method: "POST",
48
+ headers: this.headers,
49
+ body: JSON.stringify({}),
50
+ });
51
+ if (!res.ok) {
52
+ throw new Error(
53
+ (await res.json().catch(() => ({ error: "Failed to create thread" }))).error,
54
+ );
55
+ }
56
+ const data = await res.json();
57
+ return data.thread as ThreadSummary;
58
+ }
59
+
60
+ async fork(sessionId: string): Promise<ThreadSummary> {
61
+ const res = await fetch(
62
+ `${this.baseUrl}/${encodeURIComponent(sessionId)}/fork`,
63
+ {
64
+ method: "POST",
65
+ headers: this.headers,
66
+ body: JSON.stringify({}),
67
+ },
68
+ );
69
+ if (!res.ok) {
70
+ throw new Error(
71
+ (await res.json().catch(() => ({ error: "Failed to fork thread" }))).error,
72
+ );
73
+ }
74
+ const data = await res.json();
75
+ return data.thread as ThreadSummary;
76
+ }
77
+
78
+ async interrupt(sessionId: string): Promise<void> {
79
+ const res = await fetch(
80
+ `${this.baseUrl}/${encodeURIComponent(sessionId)}/interrupt`,
81
+ {
82
+ method: "POST",
83
+ headers: this.headers,
84
+ },
85
+ );
86
+ if (!res.ok) {
87
+ throw new Error(
88
+ (await res.json().catch(() => ({ error: "Failed to interrupt thread" }))).error,
89
+ );
90
+ }
91
+ }
92
+ }
@@ -0,0 +1,63 @@
1
+ export type ThreadSummary = {
2
+ sessionDbId: string;
3
+ sessionId: string;
4
+ title: string;
5
+ status: string;
6
+ updatedAt: string;
7
+ messageCount: number;
8
+ lastUserPreview: string;
9
+ lastAssistantPreview: string;
10
+ lastError?: string | null;
11
+ lastTraceDbId?: string | null;
12
+ };
13
+
14
+ export type ThreadEvent = {
15
+ id: string;
16
+ sequence: number;
17
+ eventType: "user_message" | "assistant_message" | "tool_call" | "tool_result" | "status";
18
+ payload: Record<string, unknown>;
19
+ createdAt: string;
20
+ };
21
+
22
+ export type ThreadTrace = {
23
+ id: string;
24
+ traceId: string;
25
+ state: string;
26
+ durationMs: number | null;
27
+ totalCostCents: number;
28
+ inputTokens: number;
29
+ outputTokens: number;
30
+ modelId: string | null;
31
+ createdAt: string;
32
+ };
33
+
34
+ export type ThreadRun = {
35
+ id: string;
36
+ runId: string;
37
+ agentName: string;
38
+ status: string;
39
+ triggerType: string;
40
+ inputPreview: string | null;
41
+ outputPreview: string | null;
42
+ inputTokens: number;
43
+ outputTokens: number;
44
+ totalCostCents: number;
45
+ durationMs: number | null;
46
+ startedAt: string;
47
+ completedAt: string | null;
48
+ parentRunId: string | null;
49
+ };
50
+
51
+ export type ThreadDetail = {
52
+ session: {
53
+ id: string;
54
+ sessionId: string;
55
+ messageCount: number;
56
+ metadata?: Record<string, unknown> | null;
57
+ };
58
+ events: ThreadEvent[];
59
+ traces: ThreadTrace[];
60
+ runs: ThreadRun[];
61
+ };
62
+
63
+ export type RuntimeStatus = "ready" | "submitted" | "streaming" | "error";
@@ -0,0 +1,71 @@
1
+ import type { ReactNode } from "react";
2
+ import type { ToolUIRenderProps, ToolUIRegistration } from "./types";
3
+
4
+ /**
5
+ * Options for makeToolUI when using a @kognitivedev/tools Tool definition.
6
+ * The tool's inputSchema and outputSchema provide type inference.
7
+ */
8
+ export interface MakeToolUIOptions<TInput = any, TOutput = any> {
9
+ /** The tool definition from createTool(). Provides name and type info. */
10
+ tool: { id: string; inputSchema?: unknown; outputSchema?: unknown };
11
+ /** Render function with fully typed props from the tool definition. */
12
+ render: (props: ToolUIRenderProps<TInput, TOutput>) => ReactNode;
13
+ }
14
+
15
+ /**
16
+ * Options for makeToolUI when registering by tool name string.
17
+ */
18
+ export interface MakeToolUIByNameOptions {
19
+ /** The tool name (must match the tool ID used on the server) */
20
+ toolName: string;
21
+ /** Render function for the tool UI */
22
+ render: (props: ToolUIRenderProps) => ReactNode;
23
+ }
24
+
25
+ /**
26
+ * Create a type-safe tool UI registration from a @kognitivedev/tools Tool definition.
27
+ *
28
+ * @example
29
+ * ```tsx
30
+ * const weatherTool = createTool({
31
+ * id: "get_weather",
32
+ * inputSchema: z.object({ city: z.string() }),
33
+ * execute: async ({ city }) => ({ temp: 22 }),
34
+ * });
35
+ *
36
+ * const WeatherUI = makeToolUI({
37
+ * tool: weatherTool,
38
+ * render: ({ input, output, state }) => (
39
+ * <div>{input.city}: {output?.temp}°</div>
40
+ * ),
41
+ * });
42
+ * ```
43
+ */
44
+ export function makeToolUI<TInput, TOutput>(
45
+ options: MakeToolUIOptions<TInput, TOutput>,
46
+ ): ToolUIRegistration;
47
+
48
+ /**
49
+ * Create a tool UI registration by tool name string.
50
+ *
51
+ * @example
52
+ * ```tsx
53
+ * const SearchUI = makeToolUI({
54
+ * toolName: "search",
55
+ * render: ({ input, output, state }) => (
56
+ * <div>Searching for {input.query}...</div>
57
+ * ),
58
+ * });
59
+ * ```
60
+ */
61
+ export function makeToolUI(options: MakeToolUIByNameOptions): ToolUIRegistration;
62
+
63
+ export function makeToolUI(
64
+ options: MakeToolUIOptions | MakeToolUIByNameOptions,
65
+ ): ToolUIRegistration {
66
+ const toolName = "tool" in options ? options.tool.id : options.toolName;
67
+ return {
68
+ toolName,
69
+ component: options.render as any,
70
+ };
71
+ }
@@ -0,0 +1,27 @@
1
+ import type { ToolUIComponent, ToolUIRegistration } from "./types";
2
+
3
+ export class ToolUIRegistry {
4
+ private map = new Map<string, ToolUIComponent>();
5
+
6
+ register(registration: ToolUIRegistration): void {
7
+ this.map.set(registration.toolName, registration.component);
8
+ }
9
+
10
+ registerAll(registrations: ToolUIRegistration[]): void {
11
+ for (const reg of registrations) {
12
+ this.register(reg);
13
+ }
14
+ }
15
+
16
+ get(toolName: string): ToolUIComponent | undefined {
17
+ return this.map.get(toolName);
18
+ }
19
+
20
+ has(toolName: string): boolean {
21
+ return this.map.has(toolName);
22
+ }
23
+
24
+ names(): string[] {
25
+ return Array.from(this.map.keys());
26
+ }
27
+ }
@@ -0,0 +1,8 @@
1
+ import { createContext } from "../utils/create-context";
2
+ import type { ToolUIRegistry } from "./registry";
3
+
4
+ export const [
5
+ ToolUIRegistryProvider,
6
+ useToolUIRegistry,
7
+ ToolUIRegistryContext,
8
+ ] = createContext<ToolUIRegistry>("ToolUIRegistry");
@@ -0,0 +1,40 @@
1
+ import type { ReactNode } from "react";
2
+ import type { ToolUIRenderProps, ToolUIRegistration } from "./types";
3
+
4
+ export interface ToolkitEntry {
5
+ /** Render function for this tool's UI */
6
+ render: (props: ToolUIRenderProps) => ReactNode;
7
+ }
8
+
9
+ /**
10
+ * Register multiple tool UIs at once by tool name.
11
+ *
12
+ * Useful when you don't have the Tool objects on the client
13
+ * (e.g., backend-only tools) but still want custom renderers.
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * const myToolkit = toolkit({
18
+ * get_weather: {
19
+ * render: ({ input, output, state }) => (
20
+ * <WeatherCard city={input.city} data={output} />
21
+ * ),
22
+ * },
23
+ * search_database: {
24
+ * render: ({ input, output }) => (
25
+ * <QueryResults query={input.query} results={output?.rows} />
26
+ * ),
27
+ * },
28
+ * });
29
+ *
30
+ * <KognitiveUI toolkit={myToolkit} ... />
31
+ * ```
32
+ */
33
+ export function toolkit(
34
+ map: Record<string, ToolkitEntry>,
35
+ ): ToolUIRegistration[] {
36
+ return Object.entries(map).map(([toolName, entry]) => ({
37
+ toolName,
38
+ component: entry.render as any,
39
+ }));
40
+ }