@assistant-ui/core 0.3.6 → 0.3.8
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/index.d.ts +1 -1
- package/dist/react/RuntimeAdapter.js +1 -1
- package/dist/react/RuntimeAdapter.js.map +1 -1
- package/dist/react/client/Interactables.js +1 -1
- package/dist/react/client/Interactables.js.map +1 -1
- package/dist/react/client/Tools.d.ts +1 -1
- package/dist/react/client/Tools.js +1 -1
- package/dist/react/client/Tools.js.map +1 -1
- package/dist/react/index.d.ts +9 -9
- package/dist/react/index.js +4 -4
- package/dist/react/model-context/interactableTool.d.ts +1 -1
- package/dist/react/model-context/useAssistantTool.d.ts +1 -1
- package/dist/react/runtimes/cloud/auiV0.d.ts +1 -0
- package/dist/react/runtimes/cloud/auiV0.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/auiV0.js +2 -1
- package/dist/react/runtimes/cloud/auiV0.js.map +1 -1
- package/dist/react/runtimes/createMessageConverter.d.ts +1 -1
- package/dist/react/runtimes/createRuntimeExtras.d.ts +4 -1
- package/dist/react/runtimes/createRuntimeExtras.d.ts.map +1 -1
- package/dist/react/runtimes/createRuntimeExtras.js +6 -17
- package/dist/react/runtimes/createRuntimeExtras.js.map +1 -1
- package/dist/react/types/scopes/dataRenderers.d.ts +1 -1
- package/dist/react/types/scopes/interactables.d.ts +1 -1
- package/dist/react/types/scopes/tools.d.ts +1 -1
- package/dist/runtime/base/base-composer-runtime-core.d.ts +1 -1
- package/dist/runtime/base/default-edit-composer-runtime-core.d.ts +0 -2
- package/dist/runtime/base/default-edit-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/default-edit-composer-runtime-core.js +18 -25
- package/dist/runtime/base/default-edit-composer-runtime-core.js.map +1 -1
- package/dist/runtime/utils/runtime-extras-brand.d.ts +21 -0
- package/dist/runtime/utils/runtime-extras-brand.d.ts.map +1 -0
- package/dist/runtime/utils/runtime-extras-brand.js +37 -0
- package/dist/runtime/utils/runtime-extras-brand.js.map +1 -0
- package/dist/runtime/utils/thread-message-like.js +3 -1
- package/dist/runtime/utils/thread-message-like.js.map +1 -1
- package/dist/store/clients/runtime-adapter.d.ts.map +1 -1
- package/dist/store/clients/runtime-adapter.js +1 -1
- package/dist/store/clients/runtime-adapter.js.map +1 -1
- package/dist/store/clients/suggestions.js +1 -1
- package/dist/store/clients/suggestions.js.map +1 -1
- package/dist/store/clients/thread-message-client.js +1 -1
- package/dist/store/clients/thread-message-client.js.map +1 -1
- package/dist/store/index.d.ts +10 -1
- package/dist/store/index.js +7 -1
- package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/composer-runtime-client.js +1 -1
- package/dist/store/runtime-clients/composer-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/message-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/message-runtime-client.js +1 -1
- package/dist/store/runtime-clients/message-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/thread-list-item-runtime-client.js +1 -1
- package/dist/store/runtime-clients/thread-list-item-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/thread-list-runtime-client.js +1 -1
- package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/thread-runtime-client.js +1 -1
- package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
- package/dist/types/message.d.ts +1 -0
- package/dist/types/message.d.ts.map +1 -1
- package/dist/types/message.js.map +1 -1
- package/package.json +3 -3
- package/src/react/RuntimeAdapter.test.ts +85 -0
- package/src/react/RuntimeAdapter.ts +1 -1
- package/src/react/client/Interactables.test.ts +3 -2
- package/src/react/client/Interactables.ts +4 -4
- package/src/react/client/Tools.ts +4 -4
- package/src/react/index.ts +1 -0
- package/src/react/runtimes/cloud/auiV0.ts +8 -1
- package/src/react/runtimes/createRuntimeExtras.test.ts +14 -1
- package/src/react/runtimes/createRuntimeExtras.ts +15 -26
- package/src/runtime/base/default-edit-composer-runtime-core.ts +32 -50
- package/src/runtime/utils/runtime-extras-brand.ts +47 -0
- package/src/runtime/utils/thread-message-like.ts +5 -1
- package/src/store/clients/runtime-adapter.ts +2 -5
- package/src/store/clients/suggestions.ts +1 -1
- package/src/store/clients/thread-message-client.ts +1 -1
- package/src/store/index.ts +31 -0
- package/src/store/runtime-clients/composer-runtime-client.ts +2 -5
- package/src/store/runtime-clients/lookup-domains.test.ts +109 -0
- package/src/store/runtime-clients/message-runtime-client.ts +2 -5
- package/src/store/runtime-clients/thread-list-item-runtime-client.ts +2 -1
- package/src/store/runtime-clients/thread-list-runtime-client.ts +2 -5
- package/src/store/runtime-clients/thread-runtime-client.ts +2 -2
- package/src/tests/auiV0Encode.test.ts +97 -0
- package/src/tests/default-edit-composer-runtime-core.test.ts +6 -2
- package/src/tests/framework-neutral-boundary.test.ts +60 -24
- package/src/tests/thread-message-like.test.ts +43 -0
- package/src/types/message.ts +1 -0
|
@@ -236,6 +236,33 @@ describe("auiV0Encode", () => {
|
|
|
236
236
|
]);
|
|
237
237
|
});
|
|
238
238
|
|
|
239
|
+
it("preserves reasoning summaries and omits absent summaries", () => {
|
|
240
|
+
const encoded = auiV0Encode({
|
|
241
|
+
id: "m1",
|
|
242
|
+
createdAt: new Date("2026-03-15T00:00:00.000Z"),
|
|
243
|
+
role: "assistant",
|
|
244
|
+
status: { type: "complete", reason: "stop" },
|
|
245
|
+
metadata: {
|
|
246
|
+
unstable_state: null,
|
|
247
|
+
unstable_annotations: [],
|
|
248
|
+
unstable_data: [],
|
|
249
|
+
steps: [],
|
|
250
|
+
custom: {},
|
|
251
|
+
},
|
|
252
|
+
content: [
|
|
253
|
+
{ type: "reasoning", text: "thinking", unstable_summary: "Planning" },
|
|
254
|
+
{ type: "reasoning", text: "more thinking", unstable_summary: "" },
|
|
255
|
+
{ type: "reasoning", text: "no summary" },
|
|
256
|
+
],
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
expect(encoded.content).toEqual([
|
|
260
|
+
{ type: "reasoning", text: "thinking", unstable_summary: "Planning" },
|
|
261
|
+
{ type: "reasoning", text: "more thinking", unstable_summary: "" },
|
|
262
|
+
{ type: "reasoning", text: "no summary" },
|
|
263
|
+
]);
|
|
264
|
+
});
|
|
265
|
+
|
|
239
266
|
it("drops per-part status from attachment content in the core cloud encoder", () => {
|
|
240
267
|
const encoded = auiV0Encode({
|
|
241
268
|
id: "m1",
|
|
@@ -306,6 +333,76 @@ describe("auiV0Encode", () => {
|
|
|
306
333
|
});
|
|
307
334
|
|
|
308
335
|
describe("auiV0Decode", () => {
|
|
336
|
+
it("round-trips a reasoning summary", () => {
|
|
337
|
+
const encoded = auiV0Encode({
|
|
338
|
+
id: "local",
|
|
339
|
+
createdAt: new Date("2026-03-15T00:00:00.000Z"),
|
|
340
|
+
role: "assistant",
|
|
341
|
+
status: { type: "complete", reason: "stop" },
|
|
342
|
+
metadata: {
|
|
343
|
+
unstable_state: null,
|
|
344
|
+
unstable_annotations: [],
|
|
345
|
+
unstable_data: [],
|
|
346
|
+
steps: [],
|
|
347
|
+
custom: {},
|
|
348
|
+
},
|
|
349
|
+
content: [
|
|
350
|
+
{ type: "reasoning", text: "thinking", unstable_summary: "Planning" },
|
|
351
|
+
],
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
const { message } = auiV0Decode({
|
|
355
|
+
id: "cloud",
|
|
356
|
+
parent_id: null,
|
|
357
|
+
format: "aui/v0",
|
|
358
|
+
content: encoded,
|
|
359
|
+
created_at: new Date("2026-03-15T00:00:00.000Z"),
|
|
360
|
+
} as unknown as Parameters<typeof auiV0Decode>[0]);
|
|
361
|
+
|
|
362
|
+
expect(message.content).toEqual([
|
|
363
|
+
{ type: "reasoning", text: "thinking", unstable_summary: "Planning" },
|
|
364
|
+
]);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it("round-trips a reasoning summary without text", () => {
|
|
368
|
+
const encoded = auiV0Encode({
|
|
369
|
+
id: "local",
|
|
370
|
+
createdAt: new Date("2026-03-15T00:00:00.000Z"),
|
|
371
|
+
role: "assistant",
|
|
372
|
+
status: { type: "complete", reason: "stop" },
|
|
373
|
+
metadata: {
|
|
374
|
+
unstable_state: null,
|
|
375
|
+
unstable_annotations: [],
|
|
376
|
+
unstable_data: [],
|
|
377
|
+
steps: [],
|
|
378
|
+
custom: {},
|
|
379
|
+
},
|
|
380
|
+
content: [
|
|
381
|
+
{
|
|
382
|
+
type: "reasoning",
|
|
383
|
+
text: "",
|
|
384
|
+
unstable_summary: "Searching the codebase",
|
|
385
|
+
},
|
|
386
|
+
],
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
const { message } = auiV0Decode({
|
|
390
|
+
id: "cloud",
|
|
391
|
+
parent_id: null,
|
|
392
|
+
format: "aui/v0",
|
|
393
|
+
content: encoded,
|
|
394
|
+
created_at: new Date("2026-03-15T00:00:00.000Z"),
|
|
395
|
+
} as unknown as Parameters<typeof auiV0Decode>[0]);
|
|
396
|
+
|
|
397
|
+
expect(message.content).toEqual([
|
|
398
|
+
{
|
|
399
|
+
type: "reasoning",
|
|
400
|
+
text: "",
|
|
401
|
+
unstable_summary: "Searching the codebase",
|
|
402
|
+
},
|
|
403
|
+
]);
|
|
404
|
+
});
|
|
405
|
+
|
|
309
406
|
it("round-trips a pending tool-call approval so a paused run stays resumable", () => {
|
|
310
407
|
const content = auiV0Encode({
|
|
311
408
|
id: "local",
|
|
@@ -132,7 +132,7 @@ describe("DefaultEditComposerRuntimeCore", () => {
|
|
|
132
132
|
});
|
|
133
133
|
|
|
134
134
|
describe("send behavior", () => {
|
|
135
|
-
it("
|
|
135
|
+
it("calls append even when nothing changed, branching with the same message", async () => {
|
|
136
136
|
const { runtime, append } = makeRuntime();
|
|
137
137
|
const composer = new DefaultEditComposerRuntimeCore(runtime, () => {}, {
|
|
138
138
|
parentId: "p1",
|
|
@@ -141,7 +141,11 @@ describe("DefaultEditComposerRuntimeCore", () => {
|
|
|
141
141
|
}),
|
|
142
142
|
});
|
|
143
143
|
await composer.send();
|
|
144
|
-
expect(append).
|
|
144
|
+
expect(append).toHaveBeenCalledTimes(1);
|
|
145
|
+
const appended = append.mock.calls[0]![0] as AppendMessage;
|
|
146
|
+
expect(appended.content).toEqual([{ type: "text", text: "same" }]);
|
|
147
|
+
expect(appended.parentId).toBe("p1");
|
|
148
|
+
expect(appended.sourceId).toBe("msg-1");
|
|
145
149
|
});
|
|
146
150
|
|
|
147
151
|
it("calls append when text changes", async () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { describe, expect, it } from "vitest";
|
|
3
|
-
import { existsSync, readFileSync,
|
|
3
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
4
4
|
import { dirname, join, relative, resolve } from "node:path";
|
|
5
5
|
|
|
6
6
|
const SRC_DIR = resolve(__dirname, "..");
|
|
@@ -108,19 +108,6 @@ function walkFrom(entry: string): { forbidden: string[]; jsxFiles: string[] } {
|
|
|
108
108
|
return { forbidden, jsxFiles };
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
function findFiles(dir: string, ext: string[]): string[] {
|
|
112
|
-
const results: string[] = [];
|
|
113
|
-
for (const entry of readdirSync(dir)) {
|
|
114
|
-
const full = join(dir, entry);
|
|
115
|
-
if (statSync(full).isDirectory()) {
|
|
116
|
-
results.push(...findFiles(full, ext));
|
|
117
|
-
} else if (ext.some((e) => full.endsWith(e))) {
|
|
118
|
-
results.push(full);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
return results;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
111
|
function nonTypeImportNames(clause: string): string[] {
|
|
125
112
|
const names: string[] = [];
|
|
126
113
|
const trimmed = clause.trim();
|
|
@@ -183,15 +170,60 @@ describe("framework neutral boundary", () => {
|
|
|
183
170
|
).toEqual([]);
|
|
184
171
|
});
|
|
185
172
|
|
|
186
|
-
it("
|
|
187
|
-
const storeDir = resolve(SRC_DIR, "store");
|
|
188
|
-
const files = findFiles(storeDir, [".ts"]).filter(
|
|
189
|
-
(f) => !f.includes(".test."),
|
|
190
|
-
);
|
|
173
|
+
it("the store entry graphs stay hostable inside a tap root", () => {
|
|
191
174
|
const violations: string[] = [];
|
|
175
|
+
const jsxFiles: string[] = [];
|
|
176
|
+
const visited = new Set<string>();
|
|
177
|
+
const pending = [
|
|
178
|
+
resolve(SRC_DIR, "store/index.ts"),
|
|
179
|
+
resolve(SRC_DIR, "store/internal.ts"),
|
|
180
|
+
];
|
|
181
|
+
|
|
182
|
+
// Walks the runtime graph only: type-only imports are erased at build, so
|
|
183
|
+
// they neither carry runtime dependencies nor get traversed. This is also
|
|
184
|
+
// why type imports may keep using the @assistant-ui/store barrel: the
|
|
185
|
+
// augmentations target that specifier, and splitting types across entries
|
|
186
|
+
// would fork the ScopeRegistry seen by src-mapped consumers.
|
|
187
|
+
const RUNTIME_IMPORT_RE =
|
|
188
|
+
/(?:^|\n)\s*(?:import|export)\s+(?!type\b)(?:(?:\{[^}]*\}|\*\s+as\s+\w+|\*|\w+(?:\s*,\s*(?:\{[^}]*\}|\*\s+as\s+\w+))?)\s+from\s+)?["']([^"']+)["']/g;
|
|
189
|
+
|
|
190
|
+
const collectRuntimeImports = (content: string): string[] => {
|
|
191
|
+
const specs: string[] = [];
|
|
192
|
+
for (const re of [RUNTIME_IMPORT_RE, DYNAMIC_IMPORT_RE]) {
|
|
193
|
+
re.lastIndex = 0;
|
|
194
|
+
let match: RegExpExecArray | null;
|
|
195
|
+
while ((match = re.exec(content)) !== null) {
|
|
196
|
+
specs.push(match[1]!);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return specs;
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
while (pending.length > 0) {
|
|
203
|
+
const file = pending.pop()!;
|
|
204
|
+
if (visited.has(file)) continue;
|
|
205
|
+
visited.add(file);
|
|
206
|
+
if (file.endsWith(".tsx")) jsxFiles.push(relative(SRC_DIR, file));
|
|
192
207
|
|
|
193
|
-
for (const file of files) {
|
|
194
208
|
const content = readFileSync(file, "utf-8");
|
|
209
|
+
|
|
210
|
+
for (const spec of collectRuntimeImports(content)) {
|
|
211
|
+
if (spec === "react/jsx-runtime") {
|
|
212
|
+
violations.push(
|
|
213
|
+
`${relative(SRC_DIR, file)} imports "react/jsx-runtime"`,
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
if (spec === "@assistant-ui/store") {
|
|
217
|
+
violations.push(
|
|
218
|
+
`${relative(SRC_DIR, file)} value-imports the "@assistant-ui/store" barrel; use "@assistant-ui/store/client"`,
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
const resolved = resolveRelative(file, spec);
|
|
222
|
+
if (resolved && !visited.has(resolved)) {
|
|
223
|
+
pending.push(resolved);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
195
227
|
FROM_REACT_RE.lastIndex = 0;
|
|
196
228
|
let match: RegExpExecArray | null;
|
|
197
229
|
while ((match = FROM_REACT_RE.exec(content)) !== null) {
|
|
@@ -210,12 +242,16 @@ describe("framework neutral boundary", () => {
|
|
|
210
242
|
|
|
211
243
|
expect(
|
|
212
244
|
violations,
|
|
213
|
-
`
|
|
214
|
-
`
|
|
215
|
-
`
|
|
216
|
-
`useContext, createContext).\n` +
|
|
245
|
+
`The store entry graphs run inside tap roots on every framework, so they may ` +
|
|
246
|
+
`only use react names the tap shim dispatches, never react/jsx-runtime, and ` +
|
|
247
|
+
`never the @assistant-ui/store barrel (whose module scope requires React).\n` +
|
|
217
248
|
`Violations:\n` +
|
|
218
249
|
violations.map((f) => ` - ${f}`).join("\n"),
|
|
219
250
|
).toEqual([]);
|
|
251
|
+
expect(
|
|
252
|
+
jsxFiles,
|
|
253
|
+
`The JSX transform injects react/jsx-runtime at build time, so the store entry graphs must not contain .tsx files:\n` +
|
|
254
|
+
jsxFiles.map((f) => ` - ${f}`).join("\n"),
|
|
255
|
+
).toEqual([]);
|
|
220
256
|
});
|
|
221
257
|
});
|
|
@@ -147,6 +147,49 @@ describe("fromThreadMessageLike", () => {
|
|
|
147
147
|
expect(result.content).toEqual([{ type: "reasoning", text: "hi" }]);
|
|
148
148
|
});
|
|
149
149
|
|
|
150
|
+
it("keeps a reasoning part with a summary and no text", () => {
|
|
151
|
+
const result = fromThreadMessageLike(
|
|
152
|
+
{
|
|
153
|
+
role: "assistant",
|
|
154
|
+
content: [
|
|
155
|
+
{
|
|
156
|
+
type: "reasoning",
|
|
157
|
+
text: "",
|
|
158
|
+
unstable_summary: "Searching the codebase",
|
|
159
|
+
},
|
|
160
|
+
],
|
|
161
|
+
},
|
|
162
|
+
fallbackId,
|
|
163
|
+
fallbackStatus,
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
expect(result.content).toEqual([
|
|
167
|
+
{
|
|
168
|
+
type: "reasoning",
|
|
169
|
+
text: "",
|
|
170
|
+
unstable_summary: "Searching the codebase",
|
|
171
|
+
},
|
|
172
|
+
]);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("drops a reasoning part with neither text nor a summary to show", () => {
|
|
176
|
+
// the transport layers round-trip an empty summary faithfully; this is
|
|
177
|
+
// the display normalizer, so it drops a part with nothing to render for
|
|
178
|
+
// the same reason it drops whitespace-only text.
|
|
179
|
+
const message = fromThreadMessageLike(
|
|
180
|
+
{
|
|
181
|
+
role: "assistant",
|
|
182
|
+
content: [
|
|
183
|
+
{ type: "reasoning", text: "", unstable_summary: "" },
|
|
184
|
+
{ type: "reasoning", text: " ", unstable_summary: " " },
|
|
185
|
+
],
|
|
186
|
+
},
|
|
187
|
+
"m1",
|
|
188
|
+
{ type: "complete", reason: "unknown" },
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
expect(message.content).toEqual([]);
|
|
192
|
+
});
|
|
150
193
|
it("drops an assistant image part whose image is undefined", () => {
|
|
151
194
|
const result = fromThreadMessageLike(
|
|
152
195
|
{
|
package/src/types/message.ts
CHANGED
|
@@ -23,6 +23,7 @@ export type ReasoningMessagePart = {
|
|
|
23
23
|
readonly type: "reasoning";
|
|
24
24
|
readonly text: string;
|
|
25
25
|
readonly status?: MessagePartStreamStatus;
|
|
26
|
+
readonly unstable_summary?: string;
|
|
26
27
|
readonly providerMetadata?: PartProviderMetadata;
|
|
27
28
|
readonly parentId?: string;
|
|
28
29
|
};
|