@assistant-ui/core 0.3.6 → 0.3.7

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 (68) hide show
  1. package/dist/index.d.ts +1 -1
  2. package/dist/react/RuntimeAdapter.js +1 -1
  3. package/dist/react/RuntimeAdapter.js.map +1 -1
  4. package/dist/react/client/Interactables.js +1 -1
  5. package/dist/react/client/Interactables.js.map +1 -1
  6. package/dist/react/client/Tools.d.ts +1 -1
  7. package/dist/react/client/Tools.js +1 -1
  8. package/dist/react/client/Tools.js.map +1 -1
  9. package/dist/react/index.d.ts +9 -9
  10. package/dist/react/index.js +4 -4
  11. package/dist/react/model-context/interactableTool.d.ts +1 -1
  12. package/dist/react/model-context/useAssistantTool.d.ts +1 -1
  13. package/dist/react/runtimes/createMessageConverter.d.ts +1 -1
  14. package/dist/react/runtimes/createRuntimeExtras.d.ts +4 -1
  15. package/dist/react/runtimes/createRuntimeExtras.d.ts.map +1 -1
  16. package/dist/react/runtimes/createRuntimeExtras.js +6 -17
  17. package/dist/react/runtimes/createRuntimeExtras.js.map +1 -1
  18. package/dist/react/types/scopes/dataRenderers.d.ts +1 -1
  19. package/dist/react/types/scopes/interactables.d.ts +1 -1
  20. package/dist/react/types/scopes/tools.d.ts +1 -1
  21. package/dist/runtime/utils/runtime-extras-brand.d.ts +21 -0
  22. package/dist/runtime/utils/runtime-extras-brand.d.ts.map +1 -0
  23. package/dist/runtime/utils/runtime-extras-brand.js +37 -0
  24. package/dist/runtime/utils/runtime-extras-brand.js.map +1 -0
  25. package/dist/store/clients/runtime-adapter.d.ts.map +1 -1
  26. package/dist/store/clients/runtime-adapter.js +1 -1
  27. package/dist/store/clients/runtime-adapter.js.map +1 -1
  28. package/dist/store/clients/suggestions.js +1 -1
  29. package/dist/store/clients/suggestions.js.map +1 -1
  30. package/dist/store/clients/thread-message-client.js +1 -1
  31. package/dist/store/clients/thread-message-client.js.map +1 -1
  32. package/dist/store/index.d.ts +10 -1
  33. package/dist/store/index.js +7 -1
  34. package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
  35. package/dist/store/runtime-clients/composer-runtime-client.js +1 -1
  36. package/dist/store/runtime-clients/composer-runtime-client.js.map +1 -1
  37. package/dist/store/runtime-clients/message-runtime-client.d.ts.map +1 -1
  38. package/dist/store/runtime-clients/message-runtime-client.js +1 -1
  39. package/dist/store/runtime-clients/message-runtime-client.js.map +1 -1
  40. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
  41. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +1 -1
  42. package/dist/store/runtime-clients/thread-list-item-runtime-client.js.map +1 -1
  43. package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
  44. package/dist/store/runtime-clients/thread-list-runtime-client.js +1 -1
  45. package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
  46. package/dist/store/runtime-clients/thread-runtime-client.js +1 -1
  47. package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
  48. package/package.json +14 -14
  49. package/src/react/RuntimeAdapter.test.ts +85 -0
  50. package/src/react/RuntimeAdapter.ts +1 -1
  51. package/src/react/client/Interactables.test.ts +3 -2
  52. package/src/react/client/Interactables.ts +4 -4
  53. package/src/react/client/Tools.ts +4 -4
  54. package/src/react/index.ts +1 -0
  55. package/src/react/runtimes/createRuntimeExtras.test.ts +14 -1
  56. package/src/react/runtimes/createRuntimeExtras.ts +15 -26
  57. package/src/runtime/utils/runtime-extras-brand.ts +47 -0
  58. package/src/store/clients/runtime-adapter.ts +2 -5
  59. package/src/store/clients/suggestions.ts +1 -1
  60. package/src/store/clients/thread-message-client.ts +1 -1
  61. package/src/store/index.ts +31 -0
  62. package/src/store/runtime-clients/composer-runtime-client.ts +2 -5
  63. package/src/store/runtime-clients/message-runtime-client.ts +2 -5
  64. package/src/store/runtime-clients/thread-list-item-runtime-client.ts +2 -1
  65. package/src/store/runtime-clients/thread-list-runtime-client.ts +2 -5
  66. package/src/store/runtime-clients/thread-runtime-client.ts +2 -2
  67. package/src/tests/framework-neutral-boundary.test.ts +60 -24
  68. package/LICENSE +0 -21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@assistant-ui/core",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "description": "Framework-agnostic core runtime for assistant-ui",
5
5
  "keywords": [
6
6
  "assistant",
@@ -58,6 +58,12 @@
58
58
  "README.md"
59
59
  ],
60
60
  "sideEffects": false,
61
+ "scripts": {
62
+ "build": "aui-build",
63
+ "test": "vitest run",
64
+ "test:react-compiler": "vitest run --config vitest.react-compiler.config.ts",
65
+ "test:watch": "vitest"
66
+ },
61
67
  "dependencies": {
62
68
  "assistant-stream": "^0.3.34",
63
69
  "nanoid": "^6.0.0"
@@ -88,18 +94,18 @@
88
94
  }
89
95
  },
90
96
  "devDependencies": {
97
+ "@assistant-ui/store": "workspace:*",
98
+ "@assistant-ui/tap": "workspace:*",
99
+ "@assistant-ui/vite": "workspace:*",
100
+ "@assistant-ui/x-buildutils": "workspace:*",
91
101
  "@testing-library/react": "^16.3.2",
92
102
  "@types/react": "^19.2.17",
103
+ "assistant-cloud": "workspace:*",
93
104
  "jsdom": "^29.1.1",
94
105
  "react": "^19.2.8",
95
106
  "react-dom": "^19.2.8",
96
107
  "vitest": "^4.1.10",
97
- "zustand": "^5.0.14",
98
- "@assistant-ui/store": "0.3.4",
99
- "@assistant-ui/tap": "0.9.10",
100
- "@assistant-ui/vite": "0.0.11",
101
- "@assistant-ui/x-buildutils": "0.0.21",
102
- "assistant-cloud": "0.1.38"
108
+ "zustand": "^5.0.14"
103
109
  },
104
110
  "publishConfig": {
105
111
  "access": "public",
@@ -113,11 +119,5 @@
113
119
  },
114
120
  "bugs": {
115
121
  "url": "https://github.com/assistant-ui/assistant-ui/issues"
116
- },
117
- "scripts": {
118
- "build": "aui-build",
119
- "test": "vitest run",
120
- "test:react-compiler": "vitest run --config vitest.react-compiler.config.ts",
121
- "test:watch": "vitest"
122
122
  }
123
- }
123
+ }
@@ -0,0 +1,85 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { flushTapSync } from "@assistant-ui/tap";
3
+ import { AuiConfig, createAssistantClient } from "@assistant-ui/store/client";
4
+ import { RuntimeAdapter } from "../store";
5
+ import { ExternalStoreRuntimeCore } from "../runtimes/internal";
6
+ import { AssistantRuntimeImpl } from "../runtime/internal";
7
+ import type { ExternalStoreAdapter } from "../runtimes/external-store/external-store-adapter";
8
+
9
+ type DemoMessage = { role: "user" | "assistant"; text: string };
10
+
11
+ const createRuntime = (
12
+ overrides?: Partial<ExternalStoreAdapter<DemoMessage>>,
13
+ ) => {
14
+ const onNew = vi.fn(async () => {});
15
+ const adapter: ExternalStoreAdapter<DemoMessage> = {
16
+ messages: [{ role: "user", text: "hi" }],
17
+ convertMessage: (message) => ({
18
+ role: message.role,
19
+ content: [{ type: "text", text: message.text }],
20
+ }),
21
+ onNew,
22
+ ...overrides,
23
+ };
24
+ const runtime = new AssistantRuntimeImpl(
25
+ new ExternalStoreRuntimeCore(adapter),
26
+ );
27
+ return { runtime, onNew };
28
+ };
29
+
30
+ describe("RuntimeAdapter via the neutral store entry", () => {
31
+ it("mounts a runtime into a standalone client without React", () => {
32
+ const { runtime } = createRuntime();
33
+ const handle = createAssistantClient(
34
+ AuiConfig({ threads: RuntimeAdapter(runtime) }),
35
+ );
36
+ const aui = handle.getClient();
37
+
38
+ const messages = aui.thread.getState().messages;
39
+ expect(messages).toHaveLength(1);
40
+ expect(messages[0]!.content[0]).toMatchObject({
41
+ type: "text",
42
+ text: "hi",
43
+ });
44
+
45
+ expect(aui.threads.getState().mainThreadId).toBeDefined();
46
+ expect(aui.tools.getState()).toBeDefined();
47
+ expect(aui.dataRenderers.getState()).toBeDefined();
48
+
49
+ handle.destroy();
50
+ });
51
+
52
+ it("routes composer sends to the external store adapter", async () => {
53
+ const { runtime, onNew } = createRuntime();
54
+ const handle = createAssistantClient(
55
+ AuiConfig({ threads: RuntimeAdapter(runtime) }),
56
+ );
57
+ const aui = handle.getClient();
58
+
59
+ flushTapSync(() => aui.composer.setText("hello"));
60
+ flushTapSync(() => aui.composer.send());
61
+ await new Promise((resolve) => setTimeout(resolve));
62
+
63
+ expect(onNew).toHaveBeenCalledTimes(1);
64
+ expect(onNew.mock.calls[0]![0]).toMatchObject({
65
+ content: [{ type: "text", text: "hello" }],
66
+ });
67
+
68
+ handle.destroy();
69
+ });
70
+
71
+ it("registers the client's model context on the runtime", () => {
72
+ const { runtime } = createRuntime();
73
+ const register = vi.spyOn(runtime, "registerModelContextProvider");
74
+
75
+ const handle = createAssistantClient(
76
+ AuiConfig({ threads: RuntimeAdapter(runtime) }),
77
+ );
78
+
79
+ // Dev mode double-mounts the root (StrictMode emulation), so assert
80
+ // presence rather than an exact count
81
+ expect(register).toHaveBeenCalled();
82
+
83
+ handle.destroy();
84
+ });
85
+ });
@@ -8,7 +8,7 @@ import {
8
8
  import {
9
9
  attachTransformScopes,
10
10
  useAssistantClientRef,
11
- } from "@assistant-ui/store";
11
+ } from "@assistant-ui/store/client";
12
12
  import { DataRenderers } from "./client/DataRenderers";
13
13
  import { Tools } from "./client/Tools";
14
14
 
@@ -9,8 +9,9 @@ import type { ThreadMessage } from "../../types/message";
9
9
 
10
10
  const clientHolder: { client: unknown } = { client: null };
11
11
 
12
- vi.mock("@assistant-ui/store", async (importOriginal) => {
13
- const actual = await importOriginal<typeof import("@assistant-ui/store")>();
12
+ vi.mock("@assistant-ui/store/client", async (importOriginal) => {
13
+ const actual =
14
+ await importOriginal<typeof import("@assistant-ui/store/client")>();
14
15
  return {
15
16
  ...actual,
16
17
  useAssistantClientRef: () => ({
@@ -1,10 +1,10 @@
1
1
  import { useState, useEffect, useCallback, useRef, useMemo } from "react";
2
2
  import { resource } from "@assistant-ui/tap";
3
+ import type { ClientOutput } from "@assistant-ui/store";
3
4
  import {
4
- useAssistantClientRef,
5
- type ClientOutput,
6
5
  attachTransformScopes,
7
- } from "@assistant-ui/store";
6
+ useAssistantClientRef,
7
+ } from "@assistant-ui/store/client";
8
8
  import type {
9
9
  Unstable_InteractablesState,
10
10
  Unstable_InteractableRegistration,
@@ -13,7 +13,7 @@ import type {
13
13
  Unstable_InteractablesConfig,
14
14
  } from "../types/scopes/interactables";
15
15
  import { toJSONSchema, toPartialJSONSchema } from "assistant-stream";
16
- import { ModelContext } from "../../store";
16
+ import { ModelContext } from "../../store/clients/model-context-client";
17
17
  import {
18
18
  buildInteractableModelContext,
19
19
  type PartialJSONSchema,
@@ -5,11 +5,11 @@ import {
5
5
  withKey,
6
6
  type ResourceElement,
7
7
  } from "@assistant-ui/tap";
8
+ import type { ClientOutput } from "@assistant-ui/store";
8
9
  import {
9
- useAssistantClientRef,
10
- type ClientOutput,
11
10
  attachTransformScopes,
12
- } from "@assistant-ui/store";
11
+ useAssistantClientRef,
12
+ } from "@assistant-ui/store/client";
13
13
  import type { McpAppResourceOutput, ToolsState } from "../types/scopes/tools";
14
14
  import type { Tool } from "assistant-stream";
15
15
  import {
@@ -18,7 +18,7 @@ import {
18
18
  type Toolkit,
19
19
  } from "../model-context/toolbox";
20
20
  import type { ToolCallMessagePartComponent } from "../types/MessagePartComponentTypes";
21
- import { ModelContext } from "../../store";
21
+ import { ModelContext } from "../../store/clients/model-context-client";
22
22
 
23
23
  export type { McpAppResourceOutput };
24
24
 
@@ -228,6 +228,7 @@ export {
228
228
  } from "./runtimes/useStreamingTiming";
229
229
  export {
230
230
  createRuntimeExtras,
231
+ unstable_createRuntimeExtrasFromBrand,
231
232
  type RuntimeExtras,
232
233
  } from "./runtimes/createRuntimeExtras";
233
234
  export { RemoteThreadListHookInstanceManager } from "./runtimes/RemoteThreadListHookInstanceManager";
@@ -1,6 +1,10 @@
1
1
  import { describe, it, expect } from "vitest";
2
2
  import type { AssistantClient } from "@assistant-ui/store";
3
- import { createRuntimeExtras } from "./createRuntimeExtras";
3
+ import {
4
+ createRuntimeExtras,
5
+ unstable_createRuntimeExtrasFromBrand,
6
+ } from "./createRuntimeExtras";
7
+ import { createRuntimeExtrasBrand } from "../../runtime/utils/runtime-extras-brand";
4
8
 
5
9
  type Extras = { value: number; greet: () => string };
6
10
 
@@ -55,4 +59,13 @@ describe("createRuntimeExtras", () => {
55
59
 
56
60
  expect(() => channel.get(clientWith(undefined))).toThrow("useTestRuntime");
57
61
  });
62
+
63
+ it("reads extras provided through a standalone brand instance", () => {
64
+ const brand = createRuntimeExtrasBrand<Extras>("useTestRuntime");
65
+ const channel = unstable_createRuntimeExtrasFromBrand(brand);
66
+ const branded = brand.provide({ value: 7, greet: () => "hi" });
67
+
68
+ expect(channel.is(branded)).toBe(true);
69
+ expect(channel.get(clientWith(branded)).value).toBe(7);
70
+ });
58
71
  });
@@ -2,6 +2,10 @@
2
2
 
3
3
  import { useAuiState } from "@assistant-ui/store";
4
4
  import type { AssistantClient } from "@assistant-ui/store";
5
+ import {
6
+ createRuntimeExtrasBrand,
7
+ type RuntimeExtrasBrand,
8
+ } from "../../runtime/utils/runtime-extras-brand";
5
9
 
6
10
  /** @deprecated Internal API for external-store adapter authors. Not part of the public API; may change or be removed without notice. */
7
11
  export type RuntimeExtras<T extends object> = {
@@ -17,33 +21,10 @@ export type RuntimeExtras<T extends object> = {
17
21
  };
18
22
 
19
23
  /** @deprecated Internal API for external-store adapter authors. Not part of the public API; may change or be removed without notice. */
20
- export const createRuntimeExtras = <T extends object>(
21
- runtimeName: string,
24
+ export const unstable_createRuntimeExtrasFromBrand = <T extends object>(
25
+ brand: RuntimeExtrasBrand<T>,
22
26
  ): RuntimeExtras<T> => {
23
- const brand = Symbol(`${runtimeName} extras`);
24
-
25
- const is = (extras: unknown): extras is T =>
26
- typeof extras === "object" && extras !== null && brand in extras;
27
-
28
- const assert = (extras: unknown): T => {
29
- if (!is(extras))
30
- throw new Error(
31
- `The current thread is not backed by the ${runtimeName} runtime.`,
32
- );
33
- return extras;
34
- };
35
-
36
- const provide = (value: T): T => {
37
- Object.defineProperty(value, brand, {
38
- value: true,
39
- enumerable: false,
40
- configurable: true,
41
- });
42
- return value;
43
- };
44
-
45
- const tryGet = (extras: unknown): T | undefined =>
46
- is(extras) ? extras : undefined;
27
+ const { provide, is, tryGet, assert } = brand;
47
28
 
48
29
  const get = (client: AssistantClient): T =>
49
30
  assert(client.thread.getState().extras);
@@ -66,3 +47,11 @@ export const createRuntimeExtras = <T extends object>(
66
47
 
67
48
  return { provide, is, tryGet, get, use: use as RuntimeExtras<T>["use"] };
68
49
  };
50
+
51
+ /** @deprecated Internal API for external-store adapter authors. Not part of the public API; may change or be removed without notice. */
52
+ export const createRuntimeExtras = <T extends object>(
53
+ runtimeName: string,
54
+ ): RuntimeExtras<T> =>
55
+ unstable_createRuntimeExtrasFromBrand(
56
+ createRuntimeExtrasBrand<T>(runtimeName),
57
+ );
@@ -0,0 +1,47 @@
1
+ /** @deprecated Internal API for external-store adapter authors. Not part of the public API; may change or be removed without notice. */
2
+ export type RuntimeExtrasBrand<T extends object> = {
3
+ provide: (value: T) => T;
4
+ is: (extras: unknown) => extras is T;
5
+ tryGet: (extras: unknown) => T | undefined;
6
+ assert: (extras: unknown) => T;
7
+ };
8
+
9
+ /**
10
+ * Creates the framework-neutral half of a runtime extras channel: the brand
11
+ * symbol with its provide/read primitives. A runtime factory provides extras
12
+ * through the brand on any framework; the React entry wraps the same brand
13
+ * with a `use()` hook. The symbol is per-instance, so the adapter package
14
+ * creates the brand once and every binding imports that instance.
15
+ *
16
+ * @deprecated Internal API for external-store adapter authors. Not part of the public API; may change or be removed without notice.
17
+ */
18
+ export const createRuntimeExtrasBrand = <T extends object>(
19
+ runtimeName: string,
20
+ ): RuntimeExtrasBrand<T> => {
21
+ const brand = Symbol(`${runtimeName} extras`);
22
+
23
+ const is = (extras: unknown): extras is T =>
24
+ typeof extras === "object" && extras !== null && brand in extras;
25
+
26
+ const assert = (extras: unknown): T => {
27
+ if (!is(extras))
28
+ throw new Error(
29
+ `The current thread is not backed by the ${runtimeName} runtime.`,
30
+ );
31
+ return extras;
32
+ };
33
+
34
+ const provide = (value: T): T => {
35
+ Object.defineProperty(value, brand, {
36
+ value: true,
37
+ enumerable: false,
38
+ configurable: true,
39
+ });
40
+ return value;
41
+ };
42
+
43
+ const tryGet = (extras: unknown): T | undefined =>
44
+ is(extras) ? extras : undefined;
45
+
46
+ return { provide, is, tryGet, assert };
47
+ };
@@ -1,8 +1,5 @@
1
- import {
2
- Derived,
3
- type ScopesConfig,
4
- type AssistantClient,
5
- } from "@assistant-ui/store";
1
+ import type { AssistantClient, ScopesConfig } from "@assistant-ui/store";
2
+ import { Derived } from "@assistant-ui/store/client";
6
3
  import { ModelContext } from "./model-context-client";
7
4
  import { Suggestions } from "./suggestions";
8
5
 
@@ -1,7 +1,7 @@
1
1
  import { useState } from "react";
2
2
  import { resource, withKey } from "@assistant-ui/tap";
3
3
  import type { ClientOutput } from "@assistant-ui/store";
4
- import { useClientLookup } from "@assistant-ui/store";
4
+ import { useClientLookup } from "@assistant-ui/store/client";
5
5
  import type { SuggestionsState } from "../scopes/suggestions";
6
6
  import type { SuggestionState } from "../scopes/suggestion";
7
7
 
@@ -7,7 +7,7 @@ import type { Attachment } from "../../types/attachment";
7
7
  import { useMemo, useState } from "react";
8
8
  import { useResource, resource, withKey } from "@assistant-ui/tap";
9
9
  import type { ClientOutput } from "@assistant-ui/store";
10
- import { useClientLookup } from "@assistant-ui/store";
10
+ import { useClientLookup } from "@assistant-ui/store/client";
11
11
  import type { MessageState } from "../scopes/message";
12
12
  import type { PartState } from "../scopes/part";
13
13
  import { NoOpComposerClient } from "./no-op-composer-client";
@@ -1,4 +1,5 @@
1
1
  /// <reference path="./scope-registration.ts" />
2
+ /// <reference path="../react/types/store-augmentation.ts" />
2
3
 
3
4
  // scopes
4
5
  export type {
@@ -77,6 +78,36 @@ export type {
77
78
  ChainOfThoughtPart,
78
79
  } from "./scopes/chain-of-thought";
79
80
 
81
+ // runtime wiring
82
+ export { RuntimeAdapter } from "../react/RuntimeAdapter";
83
+ export {
84
+ useExternalMessageConverter,
85
+ convertExternalMessages,
86
+ type JoinStrategy,
87
+ } from "../react/runtimes/external-message-converter";
88
+ export {
89
+ useStreamingTiming,
90
+ type StreamingTimingAccessors,
91
+ type StreamingTimingOptions,
92
+ type StreamingTimingState,
93
+ } from "../react/runtimes/useStreamingTiming";
94
+ export {
95
+ createRuntimeExtrasBrand,
96
+ type RuntimeExtrasBrand,
97
+ } from "../runtime/utils/runtime-extras-brand";
98
+ export { defineToolkit } from "../react/model-context/define-toolkit";
99
+ export {
100
+ defineMcpToolkit,
101
+ type McpToolkitDefinition,
102
+ type McpToolkitEntry,
103
+ type McpToolkitToolConfig,
104
+ } from "../react/model-context/define-mcp-toolkit";
105
+ export type {
106
+ Toolkit,
107
+ ToolkitDefinition,
108
+ ToolkitDefinitionEntry,
109
+ } from "../react/model-context/toolbox";
110
+
80
111
  // clients
81
112
  export { NoOpComposerClient } from "./clients/no-op-composer-client";
82
113
  export { Suggestions, type SuggestionConfig } from "./clients/suggestions";
@@ -1,11 +1,8 @@
1
1
  import type { Unsubscribe } from "../../types/unsubscribe";
2
2
  import { useMemo, useEffect } from "react";
3
3
  import { useResource, resource, withKey } from "@assistant-ui/tap";
4
- import {
5
- type ClientOutput,
6
- useAssistantEmit,
7
- useClientLookup,
8
- } from "@assistant-ui/store";
4
+ import type { ClientOutput } from "@assistant-ui/store";
5
+ import { useAssistantEmit, useClientLookup } from "@assistant-ui/store/client";
9
6
  import type {
10
7
  ComposerRuntime,
11
8
  EditComposerRuntime,
@@ -1,10 +1,7 @@
1
1
  import { useMemo, useState } from "react";
2
2
  import { useResource, withKey, resource } from "@assistant-ui/tap";
3
- import {
4
- type ClientOutput,
5
- useClientLookup,
6
- useClientResource,
7
- } from "@assistant-ui/store";
3
+ import type { ClientOutput } from "@assistant-ui/store";
4
+ import { useClientLookup, useClientResource } from "@assistant-ui/store/client";
8
5
  import type { MessageRuntime } from "../../runtime/api/message-runtime";
9
6
  import { useSubscribable } from "./useSubscribable";
10
7
  import { liveRef } from "./liveRef";
@@ -1,7 +1,8 @@
1
1
  import type { Unsubscribe } from "../../types/unsubscribe";
2
2
  import { useEffect, useMemo } from "react";
3
3
  import { resource } from "@assistant-ui/tap";
4
- import { type ClientOutput, useAssistantEmit } from "@assistant-ui/store";
4
+ import type { ClientOutput } from "@assistant-ui/store";
5
+ import { useAssistantEmit } from "@assistant-ui/store/client";
5
6
  import type {
6
7
  ThreadListItemEventType,
7
8
  ThreadListItemRuntime,
@@ -1,10 +1,7 @@
1
1
  import { useMemo } from "react";
2
2
  import { useResource, withKey, resource } from "@assistant-ui/tap";
3
- import {
4
- type ClientOutput,
5
- useClientLookup,
6
- useClientResource,
7
- } from "@assistant-ui/store";
3
+ import type { ClientOutput } from "@assistant-ui/store";
4
+ import { useClientLookup, useClientResource } from "@assistant-ui/store/client";
8
5
  import type { ThreadListRuntime } from "../../runtime/api/thread-list-runtime";
9
6
  import type { AssistantRuntime } from "../../runtime/api/assistant-runtime";
10
7
  import { useSubscribable } from "./useSubscribable";
@@ -4,12 +4,12 @@ import type { ThreadRuntime } from "../../runtime/api/thread-runtime";
4
4
  import { useMemo, useEffect, type RefObject } from "react";
5
5
  import { useResource, resource, withKey } from "@assistant-ui/tap";
6
6
  import { liveRef } from "./liveRef";
7
+ import type { ClientOutput } from "@assistant-ui/store";
7
8
  import {
8
- type ClientOutput,
9
9
  useAssistantEmit,
10
10
  useClientLookup,
11
11
  useClientResource,
12
- } from "@assistant-ui/store";
12
+ } from "@assistant-ui/store/client";
13
13
  import { ComposerClient } from "./composer-runtime-client";
14
14
  import { MessageClient } from "./message-runtime-client";
15
15
  import { useSubscribable } from "./useSubscribable";
@@ -1,6 +1,6 @@
1
1
  /// <reference types="node" />
2
2
  import { describe, expect, it } from "vitest";
3
- import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
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("src/store may only pull react names the tap shim dispatches", () => {
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
- `src/store may only use react names the tap shim dispatches ` +
214
- `(useState, useReducer, useRef, useMemo, useCallback, useEffect, useLayoutEffect, ` +
215
- `useInsertionEffect, useEffectEvent, useSyncExternalStore, useDebugValue, use, ` +
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
  });
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 AgentbaseAI Inc.
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.