@assistant-ui/core 0.3.11 → 0.3.13

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 (132) hide show
  1. package/dist/index.d.ts +6 -5
  2. package/dist/index.js +2 -2
  3. package/dist/index.js.map +1 -1
  4. package/dist/internal.d.ts +6 -5
  5. package/dist/internal.js +5 -4
  6. package/dist/react/client/interactable-model-context.d.ts.map +1 -1
  7. package/dist/react/client/interactable-model-context.js +5 -1
  8. package/dist/react/client/interactable-model-context.js.map +1 -1
  9. package/dist/runtime/api/message-runtime.d.ts +2 -3
  10. package/dist/runtime/api/message-runtime.d.ts.map +1 -1
  11. package/dist/runtime/api/message-runtime.js +2 -16
  12. package/dist/runtime/api/message-runtime.js.map +1 -1
  13. package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
  14. package/dist/runtime/api/thread-runtime.js +5 -1
  15. package/dist/runtime/api/thread-runtime.js.map +1 -1
  16. package/dist/runtime/base/base-composer-runtime-core.d.ts +25 -0
  17. package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
  18. package/dist/runtime/base/base-composer-runtime-core.js +64 -11
  19. package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
  20. package/dist/runtime/base/base-thread-runtime-core.d.ts +14 -0
  21. package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
  22. package/dist/runtime/base/base-thread-runtime-core.js +31 -0
  23. package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
  24. package/dist/runtime/base/default-edit-composer-runtime-core.d.ts.map +1 -1
  25. package/dist/runtime/base/default-edit-composer-runtime-core.js +1 -6
  26. package/dist/runtime/base/default-edit-composer-runtime-core.js.map +1 -1
  27. package/dist/runtime/base/default-thread-composer-runtime-core.d.ts.map +1 -1
  28. package/dist/runtime/base/default-thread-composer-runtime-core.js +1 -4
  29. package/dist/runtime/base/default-thread-composer-runtime-core.js.map +1 -1
  30. package/dist/runtime/internal.d.ts +4 -4
  31. package/dist/runtime/internal.js +4 -4
  32. package/dist/runtime/queue/external-thread-queue-adapter.d.ts +15 -0
  33. package/dist/runtime/queue/external-thread-queue-adapter.d.ts.map +1 -1
  34. package/dist/runtime/queue/message-queue.js +23 -10
  35. package/dist/runtime/queue/message-queue.js.map +1 -1
  36. package/dist/runtimes/external-store/external-store-adapter.d.ts +7 -0
  37. package/dist/runtimes/external-store/external-store-adapter.d.ts.map +1 -1
  38. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +3 -1
  39. package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
  40. package/dist/runtimes/external-store/external-store-thread-runtime-core.js +49 -13
  41. package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
  42. package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
  43. package/dist/runtimes/local/local-thread-runtime-core.js +2 -1
  44. package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
  45. package/dist/store/clients/external-thread.d.ts +61 -0
  46. package/dist/store/clients/external-thread.d.ts.map +1 -0
  47. package/dist/store/clients/external-thread.js +1445 -0
  48. package/dist/store/clients/external-thread.js.map +1 -0
  49. package/dist/store/clients/runtime-adapter.d.ts.map +1 -1
  50. package/dist/store/clients/runtime-adapter.js +5 -2
  51. package/dist/store/clients/runtime-adapter.js.map +1 -1
  52. package/dist/store/clients/single-thread-list.d.ts +9 -0
  53. package/dist/store/clients/single-thread-list.d.ts.map +1 -0
  54. package/dist/store/clients/single-thread-list.js +175 -0
  55. package/dist/store/clients/single-thread-list.js.map +1 -0
  56. package/dist/store/clients/suggestions.d.ts +3 -1
  57. package/dist/store/clients/suggestions.d.ts.map +1 -1
  58. package/dist/store/clients/suggestions.js +65 -37
  59. package/dist/store/clients/suggestions.js.map +1 -1
  60. package/dist/store/index.d.ts +5 -3
  61. package/dist/store/index.js +4 -2
  62. package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
  63. package/dist/store/runtime-clients/thread-list-item-runtime-client.js +64 -50
  64. package/dist/store/runtime-clients/thread-list-item-runtime-client.js.map +1 -1
  65. package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
  66. package/dist/store/runtime-clients/thread-runtime-client.js +98 -82
  67. package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
  68. package/dist/store/scope-registration.d.ts +2 -2
  69. package/dist/store/scopes/suggestions.d.ts +6 -1
  70. package/dist/store/scopes/suggestions.d.ts.map +1 -1
  71. package/dist/store/scopes/thread.d.ts +17 -0
  72. package/dist/store/scopes/thread.d.ts.map +1 -1
  73. package/dist/types/augmentations.d.ts +33 -0
  74. package/dist/types/augmentations.d.ts.map +1 -0
  75. package/dist/types/augmentations.js +0 -0
  76. package/dist/types/error.d.ts +15 -1
  77. package/dist/types/error.d.ts.map +1 -1
  78. package/dist/types/error.js +18 -1
  79. package/dist/types/error.js.map +1 -1
  80. package/dist/types/message.d.ts +2 -0
  81. package/dist/types/message.d.ts.map +1 -1
  82. package/dist/types/message.js.map +1 -1
  83. package/dist/utils/normalizePartStatus.d.ts +3 -2
  84. package/dist/utils/normalizePartStatus.d.ts.map +1 -1
  85. package/dist/utils/normalizePartStatus.js +15 -1
  86. package/dist/utils/normalizePartStatus.js.map +1 -1
  87. package/package.json +5 -5
  88. package/src/index.ts +7 -0
  89. package/src/internal.ts +1 -1
  90. package/src/react/client/interactable-model-context.ts +6 -1
  91. package/src/react/runtimes/useExternalStoreRuntime.suggestions.test.tsx +114 -0
  92. package/src/runtime/api/message-runtime.test.ts +1 -1
  93. package/src/runtime/api/message-runtime.ts +2 -35
  94. package/src/runtime/api/thread-runtime.ts +8 -1
  95. package/src/runtime/base/base-composer-runtime-core.ts +100 -8
  96. package/src/runtime/base/base-thread-runtime-core.ts +36 -0
  97. package/src/runtime/base/default-edit-composer-runtime-core.ts +1 -15
  98. package/src/runtime/base/default-thread-composer-runtime-core.ts +1 -11
  99. package/src/runtime/queue/external-thread-queue-adapter.ts +17 -0
  100. package/src/runtime/queue/message-queue.ts +26 -9
  101. package/src/runtimes/external-store/external-store-adapter.ts +7 -0
  102. package/src/runtimes/external-store/external-store-thread-runtime-core.ts +100 -26
  103. package/src/runtimes/local/local-thread-runtime-core.ts +4 -1
  104. package/src/store/clients/external-thread.ts +1255 -0
  105. package/src/store/clients/runtime-adapter.ts +5 -2
  106. package/src/store/clients/single-thread-list.ts +116 -0
  107. package/src/store/clients/suggestions.ts +39 -13
  108. package/src/store/index.ts +6 -0
  109. package/src/store/runtime-clients/thread-list-item-runtime-client.ts +13 -28
  110. package/src/store/runtime-clients/thread-runtime-client.ts +5 -0
  111. package/src/store/scopes/suggestions.ts +6 -0
  112. package/src/store/scopes/thread.ts +17 -0
  113. package/src/tests/append-interactable-snapshots.test.ts +350 -0
  114. package/src/tests/base-composer-runtime-core-send.test.ts +126 -0
  115. package/src/tests/base-composer-runtime-core.test.ts +108 -1
  116. package/src/tests/default-edit-composer-runtime-core.test.ts +0 -68
  117. package/src/tests/external-store-thread-runtime-core-adapter.test.ts +300 -0
  118. package/src/tests/external-thread-attachments.test.tsx +584 -0
  119. package/src/tests/external-thread-branches.test.tsx +157 -0
  120. package/src/tests/external-thread-feedback.test.tsx +204 -0
  121. package/src/tests/external-thread-parity.test.tsx +548 -0
  122. package/src/tests/external-thread-refetch.test.tsx +117 -0
  123. package/src/tests/external-thread-speech.test.tsx +328 -0
  124. package/src/tests/external-thread-suggestions.test.tsx +59 -0
  125. package/src/tests/message-queue.test.ts +26 -0
  126. package/src/tests/thread-message-like.test.ts +39 -0
  127. package/src/tests/thread-switch-events.test.tsx +136 -0
  128. package/src/types/augmentations.ts +33 -0
  129. package/src/types/error.ts +24 -0
  130. package/src/types/message.ts +2 -0
  131. package/src/utils/normalizePartStatus.ts +27 -0
  132. package/src/tests/default-thread-composer-runtime-core.test.ts +0 -82
@@ -1,7 +1,6 @@
1
1
  import type { AssistantClient, ScopesConfig } from "@assistant-ui/store";
2
2
  import { Derived } from "@assistant-ui/store/client";
3
3
  import { ModelContext } from "./model-context-client";
4
- import { Suggestions } from "./suggestions";
5
4
 
6
5
  export const baseRuntimeAdapterTransformScopes = (
7
6
  scopes: ScopesConfig,
@@ -27,6 +26,10 @@ export const baseRuntimeAdapterTransformScopes = (
27
26
  scopes.modelContext = ModelContext();
28
27
  }
29
28
  if (!scopes.suggestions && parent.suggestions.source === null) {
30
- scopes.suggestions = Suggestions();
29
+ scopes.suggestions = Derived({
30
+ source: "thread",
31
+ query: {},
32
+ get: (aui) => aui.thread.suggestions(),
33
+ });
31
34
  }
32
35
  };
@@ -0,0 +1,116 @@
1
+ import { useMemo, useState } from "react";
2
+ import { resource } from "@assistant-ui/tap";
3
+ import type { ClientElement, ClientOutput } from "@assistant-ui/store";
4
+ import { useClientResource } from "@assistant-ui/store/client";
5
+
6
+ const RESOLVED_PROMISE = Promise.resolve();
7
+ const THREAD_ID = "default";
8
+
9
+ const useSingleThreadListItem = ({
10
+ isRunning,
11
+ }: {
12
+ isRunning: boolean;
13
+ }): ClientOutput<"threadListItem"> => {
14
+ const [custom, setCustom] = useState<Record<string, unknown> | undefined>();
15
+
16
+ return {
17
+ getState: () => ({
18
+ id: THREAD_ID,
19
+ remoteId: undefined,
20
+ externalId: undefined,
21
+ title: undefined,
22
+ status: "regular",
23
+ custom,
24
+ isRunning,
25
+ }),
26
+ switchTo: () => {},
27
+ rename: () => {},
28
+ updateCustom: setCustom,
29
+ archive: () => {},
30
+ unarchive: () => {},
31
+ delete: () => {},
32
+ generateTitle: () => {},
33
+ initialize: async () => ({ remoteId: THREAD_ID, externalId: undefined }),
34
+ detach: () => {},
35
+ };
36
+ };
37
+
38
+ const SingleThreadListItem = resource(useSingleThreadListItem);
39
+
40
+ type SingleThreadListProps = {
41
+ thread: ClientElement<"thread">;
42
+ };
43
+
44
+ /**
45
+ * A minimal threads scope that wraps a single thread.
46
+ * Automatically provided by ExternalThread when no threads scope exists.
47
+ * Mounts the provided thread resource element.
48
+ */
49
+ const useSingleThreadList = ({
50
+ thread,
51
+ }: SingleThreadListProps): ClientOutput<"threads"> => {
52
+ const threadClient = useClientResource(thread);
53
+ const itemClient = useClientResource(
54
+ SingleThreadListItem({ isRunning: threadClient.state.isRunning }),
55
+ );
56
+
57
+ const state = useMemo(
58
+ () => ({
59
+ mainThreadId: THREAD_ID,
60
+ newThreadId: null,
61
+ isLoading: false,
62
+ isLoadingMore: false,
63
+ hasMore: false,
64
+ threadIds: [THREAD_ID],
65
+ archivedThreadIds: [],
66
+ threadItems: [itemClient.state],
67
+ main: threadClient.state,
68
+ }),
69
+ [itemClient.state, threadClient.state],
70
+ );
71
+
72
+ return {
73
+ getState: () => state,
74
+ switchToThread: () => {
75
+ throw new Error("SingleThreadList does not support switchToThread");
76
+ },
77
+ switchToNewThread: () => {
78
+ throw new Error("SingleThreadList does not support switchToNewThread");
79
+ },
80
+ getLoadThreadsPromise: () => RESOLVED_PROMISE,
81
+ reload: () => RESOLVED_PROMISE,
82
+ reloadMainThread: () =>
83
+ threadClient.methods.unstable_refetchThread?.() ?? RESOLVED_PROMISE,
84
+ loadMore: () => RESOLVED_PROMISE,
85
+ item: (selector) => {
86
+ if (
87
+ selector !== "main" &&
88
+ !(
89
+ typeof selector === "object" &&
90
+ "id" in selector &&
91
+ selector.id === THREAD_ID
92
+ ) &&
93
+ !(
94
+ typeof selector === "object" &&
95
+ "index" in selector &&
96
+ selector.index === 0
97
+ )
98
+ ) {
99
+ throw new Error(
100
+ `SingleThreadList: unknown item selector ${JSON.stringify(selector)}`,
101
+ );
102
+ }
103
+ return itemClient.methods;
104
+ },
105
+ thread: (selector) => {
106
+ if (selector !== "main" && selector !== THREAD_ID) {
107
+ throw new Error(
108
+ `SingleThreadList: unknown thread selector ${JSON.stringify(selector)}`,
109
+ );
110
+ }
111
+ return threadClient.methods;
112
+ },
113
+ };
114
+ };
115
+
116
+ export const SingleThreadList = resource(useSingleThreadList);
@@ -1,9 +1,10 @@
1
- import { useState } from "react";
1
+ import { useMemo, useState } from "react";
2
2
  import { resource, withKey } from "@assistant-ui/tap";
3
3
  import type { ClientOutput } from "@assistant-ui/store";
4
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
+ import type { ThreadSuggestion } from "../../runtime/interfaces/thread-runtime-core";
7
8
 
8
9
  export type SuggestionConfig =
9
10
  | string
@@ -19,7 +20,24 @@ const useSuggestionClient = (
19
20
 
20
21
  const SuggestionClient = resource(useSuggestionClient);
21
22
 
22
- const useSuggestionsResource = (
23
+ const useSuggestionsClient = (
24
+ state: SuggestionsState,
25
+ ): ClientOutput<"suggestions"> => {
26
+ const suggestionClients = useClientLookup(
27
+ state.suggestions.map((suggestion, index) =>
28
+ withKey(index, SuggestionClient(suggestion), [suggestion]),
29
+ ),
30
+ );
31
+
32
+ return {
33
+ getState: () => state,
34
+ suggestion: ({ index }: { index: number }) => {
35
+ return suggestionClients.get({ index });
36
+ },
37
+ };
38
+ };
39
+
40
+ const useStaticSuggestions = (
23
41
  suggestions?: SuggestionConfig[],
24
42
  ): ClientOutput<"suggestions"> => {
25
43
  const [state] = useState<SuggestionsState>(() => {
@@ -43,18 +61,26 @@ const useSuggestionsResource = (
43
61
  };
44
62
  });
45
63
 
46
- const suggestionClients = useClientLookup(
47
- state.suggestions.map((suggestion, index) =>
48
- withKey(index, SuggestionClient(suggestion), [suggestion]),
49
- ),
64
+ return useSuggestionsClient(state);
65
+ };
66
+
67
+ export const Suggestions = resource(useStaticSuggestions);
68
+
69
+ const useThreadSuggestions = (
70
+ suggestions: readonly ThreadSuggestion[],
71
+ ): ClientOutput<"suggestions"> => {
72
+ const state = useMemo<SuggestionsState>(
73
+ () => ({
74
+ suggestions: suggestions.map((s) => ({
75
+ title: s.prompt,
76
+ label: "",
77
+ prompt: s.prompt,
78
+ })),
79
+ }),
80
+ [suggestions],
50
81
  );
51
82
 
52
- return {
53
- getState: () => state,
54
- suggestion: ({ index }: { index: number }) => {
55
- return suggestionClients.get({ index });
56
- },
57
- };
83
+ return useSuggestionsClient(state);
58
84
  };
59
85
 
60
- export const Suggestions = resource(useSuggestionsResource);
86
+ export const ThreadSuggestions = resource(useThreadSuggestions);
@@ -109,6 +109,12 @@ export type {
109
109
  } from "../react/model-context/toolbox";
110
110
 
111
111
  // clients
112
+ export {
113
+ ExternalThread,
114
+ type ExternalThreadProps,
115
+ type ExternalThreadMessage,
116
+ } from "./clients/external-thread";
117
+ export { SingleThreadList } from "./clients/single-thread-list";
112
118
  export { NoOpComposerClient } from "./clients/no-op-composer-client";
113
119
  export { Suggestions, type SuggestionConfig } from "./clients/suggestions";
114
120
  export { ChainOfThoughtClient } from "./clients/chain-of-thought-client";
@@ -1,12 +1,8 @@
1
- import type { Unsubscribe } from "../../types/unsubscribe";
2
- import { useEffect, useMemo } from "react";
1
+ import { useEffect, useMemo, useRef } from "react";
3
2
  import { resource } from "@assistant-ui/tap";
4
3
  import type { ClientOutput } from "@assistant-ui/store";
5
4
  import { useAssistantEmit } from "@assistant-ui/store/client";
6
- import type {
7
- ThreadListItemEventType,
8
- ThreadListItemRuntime,
9
- } from "../../runtime/api/thread-list-item-runtime";
5
+ import type { ThreadListItemRuntime } from "../../runtime/api/thread-list-item-runtime";
10
6
  import { useSubscribable } from "./useSubscribable";
11
7
 
12
8
  const useThreadListItemClient = ({
@@ -28,29 +24,18 @@ const useThreadListItemClient = ({
28
24
  }, [runtimeState, mainThreadIsRunning]);
29
25
  const emit = useAssistantEmit();
30
26
 
31
- // Bind thread list item events to event manager
27
+ // Emitted after the flush that rebinds the derived scopes; the runtime's own
28
+ // synchronous notification would be delivered against the pre-switch binding.
29
+ const { isMain, id: threadId } = runtimeState;
30
+ const selectionRef = useRef({ isMain, threadId });
32
31
  useEffect(() => {
33
- const unsubscribers: Unsubscribe[] = [];
34
-
35
- // Subscribe to thread list item events
36
- const threadListItemEvents: ThreadListItemEventType[] = [
37
- "switchedTo",
38
- "switchedAway",
39
- ];
40
-
41
- for (const event of threadListItemEvents) {
42
- const unsubscribe = runtime.unstable_on(event, () => {
43
- emit(`threadListItem.${event}`, {
44
- threadId: runtime.getState()!.id,
45
- });
46
- });
47
- unsubscribers.push(unsubscribe);
48
- }
49
-
50
- return () => {
51
- for (const unsub of unsubscribers) unsub();
52
- };
53
- }, [runtime, emit]);
32
+ const previous = selectionRef.current;
33
+ if (previous.isMain === isMain && previous.threadId === threadId) return;
34
+ selectionRef.current = { isMain, threadId };
35
+ emit(isMain ? "threadListItem.switchedTo" : "threadListItem.switchedAway", {
36
+ threadId,
37
+ });
38
+ }, [isMain, threadId, emit]);
54
39
 
55
40
  return {
56
41
  getState: () => state,
@@ -12,6 +12,7 @@ import {
12
12
  } from "@assistant-ui/store/client";
13
13
  import { ComposerClient } from "./composer-runtime-client";
14
14
  import { MessageClient } from "./message-runtime-client";
15
+ import { ThreadSuggestions } from "../clients/suggestions";
15
16
  import { useSubscribable } from "./useSubscribable";
16
17
  import type { ThreadState } from "../scopes/thread";
17
18
 
@@ -78,6 +79,9 @@ const useThreadClient = ({
78
79
  threadIdRef,
79
80
  }),
80
81
  );
82
+ const suggestions = useClientResource(
83
+ ThreadSuggestions(runtimeState.suggestions),
84
+ );
81
85
  const messages = useClientLookup(
82
86
  runtimeState.messages.map((m) =>
83
87
  withKey(m.id, MessageClientById({ runtime, id: m.id, threadIdRef }), [
@@ -109,6 +113,7 @@ const useThreadClient = ({
109
113
  return {
110
114
  getState: () => state,
111
115
  composer: () => composer.methods,
116
+ suggestions: () => suggestions.methods,
112
117
  append: runtime.append,
113
118
  deleteMessage: runtime.deleteMessage,
114
119
  startRun: runtime.startRun,
@@ -15,6 +15,12 @@ export type SuggestionsMethods = {
15
15
  suggestion(query: { index: number }): SuggestionMethods;
16
16
  };
17
17
 
18
+ export type SuggestionsMeta = {
19
+ source: "thread";
20
+ query: Record<string, never>;
21
+ };
22
+
18
23
  export type SuggestionsClientSchema = {
19
24
  methods: SuggestionsMethods;
25
+ meta: SuggestionsMeta;
20
26
  };
@@ -17,6 +17,7 @@ import type {
17
17
  import type { ModelContext } from "../../model-context/types";
18
18
  import type { MessageMethods, MessageState } from "./message";
19
19
  import type { ComposerMethods, ComposerState } from "./composer";
20
+ import type { SuggestionsMethods } from "./suggestions";
20
21
 
21
22
  export type ThreadState = {
22
23
  /**
@@ -71,6 +72,10 @@ export type ThreadMethods = {
71
72
  * The thread composer runtime.
72
73
  */
73
74
  composer(): ComposerMethods;
75
+ /**
76
+ * The suggestions shown for this thread.
77
+ */
78
+ suggestions(): SuggestionsMethods;
74
79
  /**
75
80
  * Append a new message to the thread.
76
81
  *
@@ -100,6 +105,18 @@ export type ThreadMethods = {
100
105
  */
101
106
  resumeRun(config: CreateResumeRunConfig): void;
102
107
  cancelRun(): void;
108
+ /**
109
+ * Re-fetch this thread's state from its backing store, in place: the tap
110
+ * thread's refetch hook, which `threads.reloadMainThread()` prefers and
111
+ * whose rejection it propagates. `capabilities.refetchThread` is the
112
+ * portable feature-detection signal; a legacy-bridged thread reports it
113
+ * there while routing the refetch through its runtime, not this method.
114
+ * The method-shorthand optionality is load-bearing: an explicit
115
+ * `| undefined` stops `ThreadMethods` satisfying `ClientMethods` and
116
+ * collapses the client schema, which only a workspace-level app typecheck
117
+ * surfaces.
118
+ */
119
+ unstable_refetchThread?(): Promise<void>;
103
120
  getModelContext(): ModelContext;
104
121
  export(): ExportedMessageRepository;
105
122
  import(repository: ExportedMessageRepository): void;