@subako-ai/assistant-ui 0.1.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.
package/dist/chat.js ADDED
@@ -0,0 +1,227 @@
1
+ "use client";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { AssistantRuntimeProvider, ComposerPrimitive, MessagePartPrimitive, MessagePrimitive, ThreadPrimitive, } from "@assistant-ui/react";
4
+ import { MarkdownTextPrimitive } from "@assistant-ui/react-markdown";
5
+ import { useSessionState } from "@subako-ai/react";
6
+ import { createContext, useContext } from "react";
7
+ import remarkGfm from "remark-gfm";
8
+ import { useSubakoRuntime } from "./runtime.js";
9
+ /**
10
+ * Enough style to read a conversation with no setup at all. Every surface names
11
+ * its own colour and its own background, so the chat is legible on a page of any
12
+ * colour rather than inheriting half of one. The eight custom properties are the
13
+ * theming seam: light by default, dark when the browser asks for it, and an app
14
+ * that sets them on `.subako-chat` overrides both. `className` is where an app
15
+ * takes the rest over.
16
+ *
17
+ * Every rule sits in the `subako` cascade layer. A rule of the app's own that
18
+ * names a class therefore wins at any specificity and from anywhere in the
19
+ * page; an app whose CSS is layered itself names `subako` in its layer order,
20
+ * where it wants the chat's defaults to sit. The guard after the layer keeps a
21
+ * page's reset out of the chat's own elements.
22
+ */
23
+ const STYLE = `
24
+ @layer subako {
25
+ .subako-chat { --subako-bg: #ffffff; --subako-fg: #18181b; --subako-muted: #52525b; --subako-accent: #2563eb;
26
+ --subako-accent-fg: #ffffff; --subako-border: #d4d4d8; --subako-surface: #f4f4f5; --subako-error: #b91c1c;
27
+ color-scheme: light dark; display: flex; flex-direction: column; height: 100%; min-height: 0; gap: 8px;
28
+ font: 14px/1.5 system-ui, sans-serif; background: var(--subako-bg); color: var(--subako-fg); }
29
+ @media (prefers-color-scheme: dark) {
30
+ .subako-chat { --subako-bg: #18181b; --subako-fg: #f4f4f5; --subako-muted: #a1a1aa; --subako-accent: #3b82f6;
31
+ --subako-accent-fg: #ffffff; --subako-border: #3f3f46; --subako-surface: #27272a; --subako-error: #fca5a5; }
32
+ }
33
+ .subako-viewport { flex: 1 1 auto; min-height: 0; overflow-y: auto; display: flex; flex-direction: column;
34
+ gap: 8px; padding: 8px; }
35
+ .subako-message { max-width: 42em; padding: 8px 12px; border-radius: 12px; white-space: normal;
36
+ overflow-wrap: anywhere; }
37
+ .subako-message p { margin: 0 0 4px; }
38
+ .subako-message p:last-child { margin-bottom: 0; }
39
+ .subako-user { align-self: flex-end; background: var(--subako-accent); color: var(--subako-accent-fg); }
40
+ .subako-assistant { align-self: flex-start; background: var(--subako-surface); color: var(--subako-fg); }
41
+ .subako-thinking { margin: 4px 0; padding: 4px 8px; border-left: 2px solid var(--subako-border);
42
+ color: var(--subako-muted); font-style: italic; white-space: pre-wrap; }
43
+ /* The assistant's markdown: enough type to read headings, lists, code and tables inside a bubble. */
44
+ .subako-markdown > :first-child { margin-top: 0; }
45
+ .subako-markdown > :last-child { margin-bottom: 0; }
46
+ .subako-markdown p, .subako-markdown ul, .subako-markdown ol, .subako-markdown pre, .subako-markdown blockquote,
47
+ .subako-markdown table { margin: 0 0 6px; }
48
+ .subako-markdown h1, .subako-markdown h2, .subako-markdown h3, .subako-markdown h4, .subako-markdown h5,
49
+ .subako-markdown h6 { margin: 8px 0 4px; font-size: 1em; font-weight: 600; line-height: 1.3; }
50
+ .subako-markdown h1 { font-size: 1.25em; }
51
+ .subako-markdown h2 { font-size: 1.15em; }
52
+ .subako-markdown h3 { font-size: 1.05em; }
53
+ .subako-markdown ul, .subako-markdown ol { padding-left: 1.5em; }
54
+ .subako-markdown ul { list-style: disc; }
55
+ .subako-markdown ol { list-style: decimal; }
56
+ .subako-markdown li { margin: 2px 0; }
57
+ .subako-markdown a { color: var(--subako-accent); text-decoration: underline; }
58
+ .subako-markdown code { padding: 1px 4px; border-radius: 4px; background: var(--subako-bg);
59
+ font-family: ui-monospace, monospace; font-size: 12px; }
60
+ .subako-markdown pre { padding: 6px 8px; border-radius: 6px; background: var(--subako-bg); white-space: pre-wrap;
61
+ overflow-wrap: anywhere; }
62
+ .subako-markdown pre code { padding: 0; background: none; }
63
+ .subako-markdown blockquote { padding: 0 0 0 8px; border-left: 2px solid var(--subako-border);
64
+ color: var(--subako-muted); }
65
+ .subako-markdown table { border-collapse: collapse; font-size: 13px; }
66
+ .subako-markdown th, .subako-markdown td { padding: 2px 6px; border: 1px solid var(--subako-border);
67
+ text-align: left; }
68
+ .subako-markdown hr { margin: 8px 0; border: 0; border-top: 1px solid var(--subako-border); }
69
+ .subako-markdown img { max-width: 100%; height: auto; border-radius: 6px; }
70
+ .subako-writing { display: block; margin-top: 4px; color: var(--subako-muted); font-size: 10px; }
71
+ .subako-tool { margin: 4px 0; padding: 6px 8px; border: 1px solid var(--subako-border); border-radius: 8px;
72
+ background: var(--subako-bg); color: var(--subako-fg); }
73
+ /* The row is the fold's handle: the name, the status, and a chevron that turns when it is open. */
74
+ .subako-tool-summary { display: flex; align-items: center; gap: 8px; cursor: pointer; list-style: none; }
75
+ .subako-tool-summary::-webkit-details-marker { display: none; }
76
+ .subako-tool-summary::after { content: "›"; margin-left: auto; color: var(--subako-muted); font-size: 16px;
77
+ line-height: 1; transition: transform 0.15s; }
78
+ .subako-tool-fold[open] > .subako-tool-summary::after { transform: rotate(90deg); }
79
+ .subako-tool-name { font-weight: 600; font-family: ui-monospace, monospace; }
80
+ .subako-tool-status { color: var(--subako-muted); font-size: 12px; }
81
+ .subako-tool-label { margin-top: 6px; color: var(--subako-muted); font-size: 11px; text-transform: uppercase;
82
+ letter-spacing: 0.04em; }
83
+ /* The arguments and the result wrap instead of scrolling sideways: a chat panel is narrow. */
84
+ .subako-tool pre { margin: 4px 0 0; padding: 4px 6px; border-radius: 6px; background: var(--subako-surface);
85
+ color: var(--subako-fg); font-family: ui-monospace, monospace; font-size: 12px; white-space: pre-wrap;
86
+ overflow-wrap: anywhere; }
87
+ .subako-tool-result[data-error] { color: var(--subako-error); }
88
+ .subako-approval { display: flex; align-items: center; gap: 8px; margin-top: 6px; padding: 4px 6px;
89
+ border-radius: 6px; background: var(--subako-surface); color: var(--subako-fg); }
90
+ .subako-approval button { padding: 2px 10px; border: 1px solid var(--subako-border); border-radius: 6px;
91
+ background: var(--subako-bg); color: var(--subako-fg); font: inherit; cursor: pointer; }
92
+ .subako-composer { display: flex; gap: 8px; padding: 8px; border-top: 1px solid var(--subako-border);
93
+ background: var(--subako-bg); color: var(--subako-fg); }
94
+ .subako-input { flex: 1 1 auto; resize: none; padding: 6px 8px; border: 1px solid var(--subako-border);
95
+ border-radius: 8px; background: var(--subako-bg); color: var(--subako-fg); font: inherit; }
96
+ .subako-input::placeholder { color: var(--subako-muted); }
97
+ .subako-composer button { padding: 4px 14px; border: 1px solid var(--subako-border); border-radius: 8px;
98
+ background: var(--subako-bg); color: var(--subako-fg); font: inherit; cursor: pointer; }
99
+ .subako-composer button:disabled { opacity: 0.5; cursor: default; }
100
+ }
101
+ /* Element rules from outside — a reset's star and button, an app's own pre — stop at the chat's own
102
+ elements. These guards sit in no layer, with the specificity of two element names, and ask the
103
+ cascade to skip every unlayered declaration and take the layered ones: a reset, which names no
104
+ class, loses to them, and a rule that names a class outranks them. A tool UI the app draws is
105
+ left alone, and so is the root, where the app's own classes go. */
106
+ div:where(.subako-chat) div:where(.subako-viewport, .subako-message, .subako-thinking, .subako-tool,
107
+ .subako-tool-label, .subako-approval, .subako-markdown),
108
+ div:where(.subako-chat) :where(.subako-markdown) :is(h1, h2, h3, h4, h5, h6, p, ul, ol, li, a, code, pre, blockquote,
109
+ table, th, td, hr, img),
110
+ div:where(.subako-chat) details:where(.subako-tool-fold),
111
+ div:where(.subako-chat) summary:where(.subako-tool-summary),
112
+ div:where(.subako-chat) span:where(.subako-tool-name, .subako-tool-status, .subako-writing),
113
+ div:where(.subako-chat) pre:where(.subako-tool-args, .subako-tool-result),
114
+ div:where(.subako-chat) :where(.subako-approval, .subako-composer) > button,
115
+ div:where(.subako-chat) form:where(.subako-composer),
116
+ div:where(.subako-chat) textarea:where(.subako-input),
117
+ div:where(.subako-chat) textarea:where(.subako-input)::placeholder { all: revert-layer; }
118
+ `;
119
+ /** A `thinking` block: shown, quietly, rather than hidden the way assistant-ui hides it by default. */
120
+ function Reasoning({ text }) {
121
+ return _jsx("div", { className: "subako-thinking", children: text });
122
+ }
123
+ /** GFM on top of CommonMark: tables, strikethrough, task lists and bare URLs, which a model writes unasked. */
124
+ const REMARK_PLUGINS = [remarkGfm];
125
+ /** react-markdown hands each element its node as well; a link opens beside the app rather than leaving it. */
126
+ const MARKDOWN_COMPONENTS = {
127
+ a: ({ node: _node, ...rest }) => (_jsx("a", { ...rest, target: "_blank", rel: "noreferrer" })),
128
+ };
129
+ /**
130
+ * The assistant's body as markdown, which is what a model writes. The text
131
+ * arrives whole, so nothing is animated; a fenced block is a plain `pre` and
132
+ * `code`, and `components.Text` is where a highlighter goes. While the run is
133
+ * still writing, a mark follows the text, the way assistant-ui's own text
134
+ * part shows one.
135
+ */
136
+ function Markdown() {
137
+ return (_jsxs(_Fragment, { children: [_jsx(MarkdownTextPrimitive, { className: "subako-markdown", remarkPlugins: REMARK_PLUGINS, components: MARKDOWN_COMPONENTS, smooth: false }), _jsx(MessagePartPrimitive.InProgress, { children: _jsx("span", { className: "subako-writing", children: "\u25CF" }) })] }));
138
+ }
139
+ /** A result is text by the time it reaches here; an app's own tool UI may answer with anything. */
140
+ const resultText = (result) => (typeof result === "string" ? result : JSON.stringify(result));
141
+ /** JSON laid out to read, when the text is JSON; anything else is shown as it came. */
142
+ function laidOut(text) {
143
+ try {
144
+ const parsed = JSON.parse(text);
145
+ return typeof parsed === "object" && parsed !== null ? JSON.stringify(parsed, null, 2) : text;
146
+ }
147
+ catch {
148
+ return text;
149
+ }
150
+ }
151
+ /** Whether the server is still waiting on the person: no decision and no cancellation are recorded. */
152
+ const awaiting = (approval) => approval !== undefined && approval.approved === undefined && approval.resolution === undefined;
153
+ /**
154
+ * The approval prompt of the chat's own tool row, for a tool UI the app draws
155
+ * itself: Allow and Deny while the server waits on the person, and nothing
156
+ * once there is an answer or the run was cancelled. Spread the tool call's
157
+ * props into it; the answer reaches `resolveApproval` the same way.
158
+ */
159
+ export function SubakoToolApproval({ toolName, approval, respondToApproval }) {
160
+ if (!awaiting(approval)) {
161
+ return null;
162
+ }
163
+ return (_jsxs("div", { className: "subako-approval", children: [_jsxs("span", { children: ["Run ", toolName, "?"] }), _jsx("button", { type: "button", onClick: () => void respondToApproval({ approved: true }), children: "Allow" }), _jsx("button", { type: "button", onClick: () => void respondToApproval({ approved: false }), children: "Deny" })] }));
164
+ }
165
+ /** The one word the folded row says about the call. */
166
+ function statusOf(props) {
167
+ if (awaiting(props.approval)) {
168
+ return "needs approval";
169
+ }
170
+ if (props.approval?.approved === false) {
171
+ return "denied";
172
+ }
173
+ if (props.result === undefined) {
174
+ return "running";
175
+ }
176
+ return props.isError === true ? "failed" : "done";
177
+ }
178
+ /**
179
+ * A tool call folded to its name and its status. Opening it shows the
180
+ * arguments and the result, as JSON laid out to read. The approval prompt
181
+ * sits outside the fold, so a closed row still asks.
182
+ */
183
+ function ToolCall(props) {
184
+ const { toolName, args, result, isError } = props;
185
+ return (_jsxs("div", { className: "subako-tool", "data-testid": `tool-${toolName}`, children: [_jsxs("details", { className: "subako-tool-fold", children: [_jsxs("summary", { className: "subako-tool-summary", children: [_jsx("span", { className: "subako-tool-name", children: toolName }), _jsx("span", { className: "subako-tool-status", children: statusOf(props) })] }), _jsx("div", { className: "subako-tool-label", children: "Input" }), _jsx("pre", { className: "subako-tool-args", children: JSON.stringify(args, null, 2) }), result === undefined ? null : (_jsxs(_Fragment, { children: [_jsx("div", { className: "subako-tool-label", children: "Output" }), _jsx("pre", { className: "subako-tool-result", "data-error": isError === true ? "" : undefined, children: laidOut(resultText(result)) })] }))] }), _jsx(SubakoToolApproval, { ...props })] }));
186
+ }
187
+ /** The app's part components, handed down from `<SubakoChat>` to every assistant message. */
188
+ const PartsContext = createContext(undefined);
189
+ /**
190
+ * The chat's own parts under the app's: its markdown body, its reasoning block
191
+ * and its tool row stay wherever the app names nothing, so a `by_name` keeps
192
+ * the row for every other tool, and an `Override` takes all of them. A
193
+ * `ChainOfThought` draws the reasoning and the tool calls itself.
194
+ */
195
+ function partsOf(theirs) {
196
+ if (theirs?.ChainOfThought !== undefined) {
197
+ return { Text: Markdown, ...theirs };
198
+ }
199
+ const tools = theirs?.tools;
200
+ return {
201
+ Text: Markdown,
202
+ Reasoning,
203
+ ...theirs,
204
+ tools: tools !== undefined && "Override" in tools ? tools : { Fallback: ToolCall, ...tools },
205
+ };
206
+ }
207
+ function UserMessage() {
208
+ return (_jsx(MessagePrimitive.Root, { className: "subako-message subako-user", children: _jsx(MessagePrimitive.Parts, {}) }));
209
+ }
210
+ function AssistantMessage() {
211
+ const theirs = useContext(PartsContext);
212
+ return (_jsx(MessagePrimitive.Root, { className: "subako-message subako-assistant", children: _jsx(MessagePrimitive.Parts, { components: partsOf(theirs) }) }));
213
+ }
214
+ /**
215
+ * One session as a working chat: the transcript, the tool calls with their
216
+ * approvals, a composer, and a stop button while a run is under way. The
217
+ * session is the app's own, so the chat opens no second connection; until it
218
+ * has one the thread is empty and the composer is disabled. It exists for the
219
+ * first ten minutes; an app that wants its own thread keeps `useSubakoRuntime`
220
+ * and builds the rest from assistant-ui's primitives.
221
+ */
222
+ export function SubakoChat({ session, className, components, nonce, children }) {
223
+ const runtime = useSubakoRuntime(session);
224
+ const state = useSessionState(session);
225
+ return (_jsxs(AssistantRuntimeProvider, { runtime: runtime, children: [_jsx("style", { nonce: nonce, children: STYLE }), children, _jsx(PartsContext.Provider, { value: components, children: _jsxs(ThreadPrimitive.Root, { className: className === undefined ? "subako-chat" : `subako-chat ${className}`, children: [_jsx(ThreadPrimitive.Viewport, { className: "subako-viewport", children: _jsx(ThreadPrimitive.Messages, { components: { UserMessage, AssistantMessage } }) }), _jsxs(ComposerPrimitive.Root, { className: "subako-composer", children: [_jsx(ComposerPrimitive.Input, { className: "subako-input", rows: 1, placeholder: "Message the agent" }), _jsx(ComposerPrimitive.Send, { children: "Send" }), state?.isRunning === true ? _jsx(ComposerPrimitive.Cancel, { children: "Stop" }) : null] })] }) })] }));
226
+ }
227
+ //# sourceMappingURL=chat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.js","sourceRoot":"","sources":["../src/chat.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { ThreadMessageLike } from "@assistant-ui/react";
2
+ import type { TranscriptMessage } from "@subako-ai/sdk";
3
+ /**
4
+ * One message of the log as assistant-ui reads it: `text` becomes a text part,
5
+ * `thinking` a reasoning part, and every `tool_call` the joined call, so its
6
+ * result and its approval travel with it.
7
+ *
8
+ * The id is the event's `seq`, which the log never reuses. The conversion is
9
+ * pure, and assistant-ui caches it on the identity of the message object rather
10
+ * than on that id — so what keeps the cache warm is the connection's transcript
11
+ * handing back the same message until an event touches it.
12
+ */
13
+ export declare function convertMessage(message: TranscriptMessage): ThreadMessageLike;
14
+ //# sourceMappingURL=convert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convert.d.ts","sourceRoot":"","sources":["../src/convert.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,KAAK,EAAoB,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAgD1E;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,iBAAiB,GAAG,iBAAiB,CA0B5E"}
@@ -0,0 +1,69 @@
1
+ /** The arguments arrive as JSON off the wire; assistant-ui asks for them typed as JSON. */
2
+ const asArguments = (args) => args;
3
+ /** A tool result is content blocks; assistant-ui renders one value, so the blocks are read as text. */
4
+ const resultText = (content) => content.map((block) => (block.type === "text" ? block.data.text : JSON.stringify(block.data))).join("\n");
5
+ /**
6
+ * The approval, in assistant-ui's spelling: an id with no decision is what its
7
+ * renderers read as "still waiting", which is what shows the prompt.
8
+ */
9
+ function approvalOf(call) {
10
+ switch (call.approval) {
11
+ case "pending":
12
+ return { id: call.id };
13
+ case "allowed":
14
+ return { id: call.id, approved: true };
15
+ case "denied":
16
+ return { id: call.id, approved: false };
17
+ default:
18
+ return undefined;
19
+ }
20
+ }
21
+ function toolCallPart(call) {
22
+ const approval = approvalOf(call);
23
+ return {
24
+ type: "tool-call",
25
+ toolCallId: call.id,
26
+ toolName: call.name,
27
+ args: asArguments(call.arguments),
28
+ ...(call.result === undefined ? {} : { result: resultText(call.result.content), isError: call.result.is_error }),
29
+ ...(approval === undefined ? {} : { approval }),
30
+ };
31
+ }
32
+ /**
33
+ * One message of the log as assistant-ui reads it: `text` becomes a text part,
34
+ * `thinking` a reasoning part, and every `tool_call` the joined call, so its
35
+ * result and its approval travel with it.
36
+ *
37
+ * The id is the event's `seq`, which the log never reuses. The conversion is
38
+ * pure, and assistant-ui caches it on the identity of the message object rather
39
+ * than on that id — so what keeps the cache warm is the connection's transcript
40
+ * handing back the same message until an event touches it.
41
+ */
42
+ export function convertMessage(message) {
43
+ // `transcript` joins result and approval onto the calls, so the blocks are read through them.
44
+ const calls = new Map((message.toolCalls ?? []).map((call) => [call.id, call]));
45
+ const content = [];
46
+ for (const block of message.message.content) {
47
+ switch (block.type) {
48
+ case "text":
49
+ content.push({ type: "text", text: block.data.text });
50
+ break;
51
+ case "thinking":
52
+ content.push({ type: "reasoning", text: block.data.thinking });
53
+ break;
54
+ case "tool_call":
55
+ content.push(toolCallPart(calls.get(block.data.id) ?? { ...block.data }));
56
+ break;
57
+ default:
58
+ break;
59
+ }
60
+ }
61
+ return {
62
+ role: message.role,
63
+ id: String(message.seq),
64
+ content,
65
+ // Where a user turn came from: the client, a steer, or something outside the browser.
66
+ ...(message.source === undefined ? {} : { metadata: { custom: { source: message.source } } }),
67
+ };
68
+ }
69
+ //# sourceMappingURL=convert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convert.js","sourceRoot":"","sources":["../src/convert.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export { SubakoChat, type SubakoChatProps, SubakoToolApproval, type SubakoToolApprovalProps } from "./chat.js";
2
+ export { convertMessage } from "./convert.js";
3
+ export { type SubakoSession, useSubakoRuntime } from "./runtime.js";
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,eAAe,EAAE,kBAAkB,EAAE,KAAK,uBAAuB,EAAE,MAAM,WAAW,CAAC;AAC/G,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,KAAK,aAAa,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { SubakoChat, SubakoToolApproval } from "./chat.js";
2
+ export { convertMessage } from "./convert.js";
3
+ export { useSubakoRuntime } from "./runtime.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAwB,kBAAkB,EAAgC,MAAM,WAAW,CAAC;AAC/G,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAsB,gBAAgB,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,41 @@
1
+ import type { AssistantRuntime } from "@assistant-ui/react";
2
+ import type { SessionState } from "@subako-ai/sdk";
3
+ /**
4
+ * What the runtime reads off a session: the SDK's `SessionConnection`, or
5
+ * anything else that carries the same members. It is spelled structurally so a
6
+ * session from `useSession` fits without this package naming the hook's type.
7
+ */
8
+ export interface SubakoSession {
9
+ /** The connection as it stands, transcript and all: the store the runtime subscribes to and reads. */
10
+ readonly state: SessionState;
11
+ /** Calls `listener` with the state after every change; returns an unsubscribe. */
12
+ readonly subscribe: (listener: (state: SessionState) => void) => () => void;
13
+ /** Posts a user turn, which queues a run or steers the active one. */
14
+ send(text: string): Promise<void>;
15
+ /** Cancels the active run. */
16
+ cancel(): Promise<void>;
17
+ resolveApproval(callId: string, decision: "allow" | "deny", message?: string): Promise<void>;
18
+ }
19
+ /**
20
+ * An assistant-ui runtime over one Subako session: the log is the store, and
21
+ * every action the thread offers is a call on the session.
22
+ *
23
+ * | assistant-ui | Subako |
24
+ * | ------------------------- | --------------------------------------------- |
25
+ * | `messages` | `state.transcript`, through `convertMessage` |
26
+ * | `isRunning` | `state.isRunning` |
27
+ * | `onNew` | `send(text)` |
28
+ * | `onCancel` | `cancel()` |
29
+ * | `onRespondToToolApproval` | `resolveApproval(callId, decision, reason)` |
30
+ *
31
+ * The runtime keeps no messages of its own: what the server put on the log is
32
+ * what the thread shows, and a send that fails leaves the log as it was. A
33
+ * `null` session is the one `useSession` has yet to open: an empty thread, and
34
+ * a composer disabled until there is something to send to.
35
+ *
36
+ * The session is a value, not something React re-renders for, so the runtime
37
+ * subscribes to it here: a chat handed the session by a parent that does not
38
+ * re-render still follows the log.
39
+ */
40
+ export declare function useSubakoRuntime(session: SubakoSession | null): AssistantRuntime;
41
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAiB,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAG3E,OAAO,KAAK,EAAE,YAAY,EAAqB,MAAM,gBAAgB,CAAC;AAGtE;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC7B,sGAAsG;IACtG,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,kFAAkF;IAClF,QAAQ,CAAC,SAAS,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;IAC5E,sEAAsE;IACtE,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,8BAA8B;IAC9B,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7F;AAgBD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,GAAG,gBAAgB,CAmBhF"}
@@ -0,0 +1,58 @@
1
+ "use client";
2
+ import { useExternalStoreRuntime } from "@assistant-ui/react";
3
+ import { useSessionState } from "@subako-ai/react";
4
+ import { convertMessage } from "./convert.js";
5
+ /** The thread before the connection is open: no messages, and the same array on every render. */
6
+ const NOTHING = [];
7
+ /** assistant-ui hands the composer's parts; a session takes one string. */
8
+ function appendedText(message) {
9
+ let text = "";
10
+ for (const part of message.content) {
11
+ if (part.type === "text") {
12
+ text += part.text;
13
+ }
14
+ }
15
+ return text;
16
+ }
17
+ /**
18
+ * An assistant-ui runtime over one Subako session: the log is the store, and
19
+ * every action the thread offers is a call on the session.
20
+ *
21
+ * | assistant-ui | Subako |
22
+ * | ------------------------- | --------------------------------------------- |
23
+ * | `messages` | `state.transcript`, through `convertMessage` |
24
+ * | `isRunning` | `state.isRunning` |
25
+ * | `onNew` | `send(text)` |
26
+ * | `onCancel` | `cancel()` |
27
+ * | `onRespondToToolApproval` | `resolveApproval(callId, decision, reason)` |
28
+ *
29
+ * The runtime keeps no messages of its own: what the server put on the log is
30
+ * what the thread shows, and a send that fails leaves the log as it was. A
31
+ * `null` session is the one `useSession` has yet to open: an empty thread, and
32
+ * a composer disabled until there is something to send to.
33
+ *
34
+ * The session is a value, not something React re-renders for, so the runtime
35
+ * subscribes to it here: a chat handed the session by a parent that does not
36
+ * re-render still follows the log.
37
+ */
38
+ export function useSubakoRuntime(session) {
39
+ // The snapshot this render was woken for, which is what the thread is built from.
40
+ const state = useSessionState(session);
41
+ return useExternalStoreRuntime({
42
+ messages: state?.transcript ?? NOTHING,
43
+ isRunning: state?.isRunning ?? false,
44
+ isDisabled: session === null,
45
+ convertMessage,
46
+ onNew: async (message) => {
47
+ await session?.send(appendedText(message));
48
+ },
49
+ onCancel: async () => {
50
+ await session?.cancel();
51
+ },
52
+ // The approval's id is the call it gates, which is what the session answers by.
53
+ onRespondToToolApproval: async ({ approvalId, approved, reason }) => {
54
+ await session?.resolveApproval(approvalId, approved ? "allow" : "deny", reason);
55
+ },
56
+ });
57
+ }
58
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAGb,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAmB9C,iGAAiG;AACjG,MAAM,OAAO,GAAiC,EAAE,CAAC;AAEjD,2EAA2E;AAC3E,SAAS,YAAY,CAAC,OAAsB;IAC3C,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpC,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC1B,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC;QACnB,CAAC;IACF,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAA6B;IAC7D,kFAAkF;IAClF,MAAM,KAAK,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IACvC,OAAO,uBAAuB,CAAoB;QACjD,QAAQ,EAAE,KAAK,EAAE,UAAU,IAAI,OAAO;QACtC,SAAS,EAAE,KAAK,EAAE,SAAS,IAAI,KAAK;QACpC,UAAU,EAAE,OAAO,KAAK,IAAI;QAC5B,cAAc;QACd,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YACxB,MAAM,OAAO,EAAE,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;QAC5C,CAAC;QACD,QAAQ,EAAE,KAAK,IAAI,EAAE;YACpB,MAAM,OAAO,EAAE,MAAM,EAAE,CAAC;QACzB,CAAC;QACD,gFAAgF;QAChF,uBAAuB,EAAE,KAAK,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE;YACnE,MAAM,OAAO,EAAE,eAAe,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACjF,CAAC;KACD,CAAC,CAAC;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@subako-ai/assistant-ui",
3
+ "version": "0.1.0",
4
+ "description": "assistant-ui runtime and chat for Subako sessions",
5
+ "homepage": "https://github.com/Kikuvi-Inc/subako-sdk-typescript/tree/main/packages/assistant-ui#readme",
6
+ "bugs": {
7
+ "url": "https://github.com/Kikuvi-Inc/subako-sdk-typescript/issues"
8
+ },
9
+ "license": "Apache-2.0",
10
+ "author": "Kikuvi, Inc.",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/Kikuvi-Inc/subako-sdk-typescript.git",
14
+ "directory": "packages/assistant-ui"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "src"
19
+ ],
20
+ "type": "module",
21
+ "sideEffects": false,
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/index.d.ts",
25
+ "default": "./dist/index.js"
26
+ },
27
+ "./package.json": "./package.json"
28
+ },
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "dependencies": {
33
+ "remark-gfm": "^4.0.1"
34
+ },
35
+ "devDependencies": {
36
+ "@assistant-ui/react": "^0.15.18",
37
+ "@assistant-ui/react-markdown": "^0.14.15",
38
+ "@testing-library/react": "^16.3.0",
39
+ "@types/react": "^19.2.0",
40
+ "@types/react-dom": "^19.2.0",
41
+ "happy-dom": "^20.14.0",
42
+ "react": "^19.2.0",
43
+ "react-dom": "^19.2.0",
44
+ "typescript": "^5.9.3",
45
+ "@subako-ai/react": "0.1.0",
46
+ "@subako-ai/sdk": "0.1.0"
47
+ },
48
+ "peerDependencies": {
49
+ "@assistant-ui/react": "^0.15.0",
50
+ "@assistant-ui/react-markdown": "^0.14.15",
51
+ "react": "^19.0.0",
52
+ "@subako-ai/react": "^0.1.0",
53
+ "@subako-ai/sdk": "^0.1.0"
54
+ },
55
+ "engines": {
56
+ "node": ">=22"
57
+ },
58
+ "scripts": {}
59
+ }