@agentshouse/mdruntime 0.1.0-ca1eff92bbe73 → 0.1.0-ca4dc698bd0c4

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 (193) hide show
  1. package/AGENTS.md +265 -209
  2. package/README.md +13 -3
  3. package/dist/acquisition.d.ts +14 -0
  4. package/dist/acquisition.d.ts.map +1 -0
  5. package/dist/acquisition.js +24 -0
  6. package/dist/adapter.d.ts +76 -17
  7. package/dist/adapter.d.ts.map +1 -1
  8. package/dist/attempt.d.ts +16 -0
  9. package/dist/attempt.d.ts.map +1 -0
  10. package/dist/attempt.js +70 -0
  11. package/dist/binary.d.ts +23 -0
  12. package/dist/binary.d.ts.map +1 -0
  13. package/dist/binary.js +8 -0
  14. package/dist/bootstrap.d.ts +17 -5
  15. package/dist/bootstrap.d.ts.map +1 -1
  16. package/dist/bootstrap.js +109 -37
  17. package/dist/change-set.d.ts +2 -0
  18. package/dist/change-set.d.ts.map +1 -1
  19. package/dist/change-set.js +48 -16
  20. package/dist/change.d.ts +15 -0
  21. package/dist/change.d.ts.map +1 -1
  22. package/dist/change.js +26 -1
  23. package/dist/chat/acts.d.ts +33 -0
  24. package/dist/chat/acts.d.ts.map +1 -0
  25. package/dist/chat/acts.js +36 -0
  26. package/dist/chat/context.d.ts +3 -0
  27. package/dist/chat/context.d.ts.map +1 -0
  28. package/dist/chat/context.js +10 -0
  29. package/dist/chat/input.d.ts +9 -0
  30. package/dist/chat/input.d.ts.map +1 -0
  31. package/dist/chat/input.js +99 -0
  32. package/dist/chat/kit.d.ts +60 -0
  33. package/dist/chat/kit.d.ts.map +1 -0
  34. package/dist/chat/kit.js +123 -0
  35. package/dist/chat/lasting.d.ts +6 -0
  36. package/dist/chat/lasting.d.ts.map +1 -0
  37. package/dist/chat/lasting.js +19 -0
  38. package/dist/chat/list.d.ts +26 -0
  39. package/dist/chat/list.d.ts.map +1 -0
  40. package/dist/chat/list.js +62 -0
  41. package/dist/chat/markdown.d.ts +20 -0
  42. package/dist/chat/markdown.d.ts.map +1 -0
  43. package/dist/chat/markdown.js +188 -0
  44. package/dist/chat/message.d.ts +6 -0
  45. package/dist/chat/message.d.ts.map +1 -0
  46. package/dist/chat/message.js +192 -0
  47. package/dist/chat/messages.d.ts +21 -0
  48. package/dist/chat/messages.d.ts.map +1 -0
  49. package/dist/chat/messages.js +151 -0
  50. package/dist/chat/name.d.ts +6 -0
  51. package/dist/chat/name.d.ts.map +1 -0
  52. package/dist/chat/name.js +37 -0
  53. package/dist/chat/opened.d.ts +11 -0
  54. package/dist/chat/opened.d.ts.map +1 -0
  55. package/dist/chat/opened.js +17 -0
  56. package/dist/chat/overlay.d.ts +31 -0
  57. package/dist/chat/overlay.d.ts.map +1 -0
  58. package/dist/chat/overlay.js +97 -0
  59. package/dist/chat/parts.d.ts +57 -0
  60. package/dist/chat/parts.d.ts.map +1 -0
  61. package/dist/chat/parts.js +119 -0
  62. package/dist/chat/process.d.ts +20 -0
  63. package/dist/chat/process.d.ts.map +1 -0
  64. package/dist/chat/process.js +32 -0
  65. package/dist/chat/provider.d.ts +63 -0
  66. package/dist/chat/provider.d.ts.map +1 -0
  67. package/dist/chat/provider.js +620 -0
  68. package/dist/chat/question.d.ts +9 -0
  69. package/dist/chat/question.d.ts.map +1 -0
  70. package/dist/chat/question.js +80 -0
  71. package/dist/chat/questions.d.ts +31 -0
  72. package/dist/chat/questions.d.ts.map +1 -0
  73. package/dist/chat/questions.js +111 -0
  74. package/dist/chat/read.d.ts +5 -0
  75. package/dist/chat/read.d.ts.map +1 -0
  76. package/dist/chat/read.js +13 -0
  77. package/dist/chat/ready.d.ts +33 -0
  78. package/dist/chat/ready.d.ts.map +1 -0
  79. package/dist/chat/ready.js +91 -0
  80. package/dist/chat/style.d.ts +4 -0
  81. package/dist/chat/style.d.ts.map +1 -0
  82. package/dist/chat/style.js +302 -0
  83. package/dist/chat.d.ts +14 -0
  84. package/dist/chat.d.ts.map +1 -0
  85. package/dist/chat.js +8 -0
  86. package/dist/client.d.ts +23 -11
  87. package/dist/client.d.ts.map +1 -1
  88. package/dist/client.js +67 -16
  89. package/dist/collection.d.ts +21 -12
  90. package/dist/collection.d.ts.map +1 -1
  91. package/dist/collection.js +271 -23
  92. package/dist/connection.d.ts +41 -15
  93. package/dist/connection.d.ts.map +1 -1
  94. package/dist/connection.js +1631 -378
  95. package/dist/conversation.d.ts +24 -10
  96. package/dist/conversation.d.ts.map +1 -1
  97. package/dist/conversation.js +307 -136
  98. package/dist/durability.d.ts +7 -1
  99. package/dist/durability.d.ts.map +1 -1
  100. package/dist/entry.d.ts +17 -0
  101. package/dist/entry.d.ts.map +1 -0
  102. package/dist/entry.js +156 -0
  103. package/dist/expectation.d.ts +21 -0
  104. package/dist/expectation.d.ts.map +1 -0
  105. package/dist/expectation.js +70 -0
  106. package/dist/failure.d.ts +13 -0
  107. package/dist/failure.d.ts.map +1 -0
  108. package/dist/failure.js +19 -0
  109. package/dist/fence.d.ts +6 -0
  110. package/dist/fence.d.ts.map +1 -0
  111. package/dist/fence.js +10 -0
  112. package/dist/host.d.ts +13 -2
  113. package/dist/host.d.ts.map +1 -1
  114. package/dist/host.js +48 -14
  115. package/dist/identity.d.ts +5 -0
  116. package/dist/identity.d.ts.map +1 -1
  117. package/dist/identity.js +19 -1
  118. package/dist/index.d.ts +14 -7
  119. package/dist/index.d.ts.map +1 -1
  120. package/dist/index.js +4 -0
  121. package/dist/memory.d.ts +6 -3
  122. package/dist/memory.d.ts.map +1 -1
  123. package/dist/memory.js +70 -22
  124. package/dist/model.d.ts +4 -2
  125. package/dist/model.d.ts.map +1 -1
  126. package/dist/model.js +9 -0
  127. package/dist/observation.d.ts +5 -2
  128. package/dist/observation.d.ts.map +1 -1
  129. package/dist/observation.js +22 -11
  130. package/dist/open.d.ts +2 -0
  131. package/dist/open.d.ts.map +1 -0
  132. package/dist/open.js +1 -0
  133. package/dist/outcome.d.ts +32 -3
  134. package/dist/outcome.d.ts.map +1 -1
  135. package/dist/outcome.js +42 -1
  136. package/dist/overlay.d.ts +15 -3
  137. package/dist/overlay.d.ts.map +1 -1
  138. package/dist/overlay.js +94 -77
  139. package/dist/query.d.ts +32 -3
  140. package/dist/query.d.ts.map +1 -1
  141. package/dist/query.js +5 -31
  142. package/dist/reach.d.ts +8 -2
  143. package/dist/reach.d.ts.map +1 -1
  144. package/dist/reach.js +60 -24
  145. package/dist/react.d.ts +28 -17
  146. package/dist/react.d.ts.map +1 -1
  147. package/dist/react.js +8 -6
  148. package/dist/read-side.d.ts +13 -6
  149. package/dist/read-side.d.ts.map +1 -1
  150. package/dist/read-side.js +291 -85
  151. package/dist/readiness.d.ts +53 -0
  152. package/dist/readiness.d.ts.map +1 -0
  153. package/dist/readiness.js +33 -0
  154. package/dist/recovery.d.ts +25 -0
  155. package/dist/recovery.d.ts.map +1 -0
  156. package/dist/recovery.js +197 -0
  157. package/dist/refresh.d.ts +10 -0
  158. package/dist/refresh.d.ts.map +1 -1
  159. package/dist/refresh.js +43 -38
  160. package/dist/replica.d.ts +8 -5
  161. package/dist/replica.d.ts.map +1 -1
  162. package/dist/replica.js +115 -67
  163. package/dist/room-draft.d.ts +26 -9
  164. package/dist/room-draft.d.ts.map +1 -1
  165. package/dist/room-draft.js +132 -23
  166. package/dist/rows.d.ts +8 -13
  167. package/dist/rows.d.ts.map +1 -1
  168. package/dist/rows.js +12 -14
  169. package/dist/save.d.ts +31 -1
  170. package/dist/save.d.ts.map +1 -1
  171. package/dist/save.js +376 -45
  172. package/dist/search.d.ts +31 -8
  173. package/dist/search.d.ts.map +1 -1
  174. package/dist/search.js +108 -45
  175. package/dist/shared.d.ts +11 -0
  176. package/dist/shared.d.ts.map +1 -0
  177. package/dist/shared.js +368 -0
  178. package/dist/status.d.ts +6 -2
  179. package/dist/status.d.ts.map +1 -1
  180. package/dist/status.js +0 -8
  181. package/dist/store.d.ts +73 -17
  182. package/dist/store.d.ts.map +1 -1
  183. package/dist/store.js +531 -198
  184. package/dist/turn.d.ts +7 -0
  185. package/dist/turn.d.ts.map +1 -0
  186. package/dist/turn.js +45 -0
  187. package/dist/wire.d.ts +9 -3
  188. package/dist/wire.d.ts.map +1 -1
  189. package/dist/wire.js +17 -2
  190. package/package.json +26 -14
  191. package/dist/attachment.d.ts +0 -3
  192. package/dist/attachment.d.ts.map +0 -1
  193. package/dist/attachment.js +0 -8
@@ -0,0 +1,63 @@
1
+ import { type ReactElement, type ReactNode } from 'react';
2
+ import type { TurnContext } from '../adapter.js';
3
+ import type { MdRuntimeClient } from '../client.js';
4
+ import type { ChatDraft, ConversationAgent, ConversationEntry, ConversationView } from '../conversation.js';
5
+ import { type ChatOutcome } from './acts.js';
6
+ import { type ChatCommand, type ChatOption } from './process.js';
7
+ import { type ChatLone, type ChatQuestion } from './questions.js';
8
+ export type ChatOpened = {
9
+ readonly conversation: string | null;
10
+ readonly agent: string | null;
11
+ };
12
+ export type ChatActing = {
13
+ readonly busy: boolean;
14
+ readonly outcome: ChatOutcome | null;
15
+ };
16
+ export type ChatQueued = {
17
+ readonly id: string;
18
+ readonly text: string;
19
+ };
20
+ export type ChatState = ChatOpened & {
21
+ readonly conversations: readonly ConversationEntry[];
22
+ readonly agents: readonly ConversationAgent[];
23
+ readonly view: ConversationView | null;
24
+ readonly running: boolean;
25
+ readonly questions: readonly ChatQuestion[];
26
+ readonly lone: ChatLone | null;
27
+ readonly options: readonly ChatOption[];
28
+ readonly commands: readonly ChatCommand[];
29
+ readonly context: TurnContext | null;
30
+ readonly queued: readonly ChatQueued[];
31
+ readonly sending: ChatActing & {
32
+ readonly again: (() => void) | null;
33
+ };
34
+ readonly acting: ChatActing;
35
+ open(conversation: string): void;
36
+ start(agent: string): void;
37
+ send(draft: ChatDraft): void;
38
+ answer(question: ChatQuestion, response: unknown): Promise<boolean>;
39
+ choose(option: string, value: string): Promise<boolean>;
40
+ stop(): Promise<boolean>;
41
+ kill(): Promise<boolean>;
42
+ done(): Promise<boolean>;
43
+ rename(name: string): Promise<boolean>;
44
+ remove(): Promise<boolean>;
45
+ edit(queued: string): void;
46
+ unqueue(queued: string): void;
47
+ };
48
+ export type ChatProviderProps = {
49
+ readonly client: MdRuntimeClient;
50
+ readonly conversation?: string | null;
51
+ readonly agent?: string | null;
52
+ readonly onOpen?: (opened: ChatOpened) => void;
53
+ readonly children?: ReactNode;
54
+ };
55
+ type Scope = {
56
+ readonly client: MdRuntimeClient;
57
+ readonly known: boolean;
58
+ };
59
+ export declare function useChatScope(): Scope;
60
+ export declare function useChat(): ChatState;
61
+ export declare function ChatProvider({ client, conversation, agent, onOpen, children, }: ChatProviderProps): ReactElement;
62
+ export {};
63
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/chat/provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,620 @@
1
+ import { createContext, createElement, useCallback, useContext, useEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore, } from 'react';
2
+ import { AssistantRuntimeProvider, createMessageQueue, generateId, useExternalStoreRuntime, } from '@assistant-ui/react';
3
+ import { attempts } from '../attempt.js';
4
+ import { expectedList } from '../expectation.js';
5
+ import { chatDraftRevision } from '../identity.js';
6
+ import { useObserve } from '../react.js';
7
+ import { accepted, answeredOf, outcomeOf, sender, unanswered, } from './acts.js';
8
+ import { askedBefore, chatMessages, contextOf } from './messages.js';
9
+ import { conversationChanged, conversationGone, conversationListed, optionChosen, overlay, questionAnswered, sentMessage, } from './overlay.js';
10
+ import { commandIn, commandsOf, optionsOf } from './process.js';
11
+ import { loneOf, questionsOf } from './questions.js';
12
+ import { record, text } from './read.js';
13
+ const SHARED = new WeakMap();
14
+ function sharedOf(client) {
15
+ const held = SHARED.get(client);
16
+ if (held !== undefined)
17
+ return held;
18
+ const fresh = { overlay: overlay(), created: new Map(), sends: 0 };
19
+ SHARED.set(client, fresh);
20
+ return fresh;
21
+ }
22
+ function newChat(agent) {
23
+ return `new:${agent}`;
24
+ }
25
+ function ready(observed) {
26
+ return observed.state === 'ready' ? observed.value : null;
27
+ }
28
+ function turnOf(result) {
29
+ return accepted(result) ? text(record(result.result), 'turn') : null;
30
+ }
31
+ async function deliver(client, send, conversation, said, key, idle) {
32
+ const result = await send.numbered('conversation.send', { conversation, key }, (operation) => client.conversation.send(conversation, operation, said));
33
+ if (accepted(result))
34
+ send.expect(sentMessage(key, [conversation], said, turnOf(result)));
35
+ else if (!unanswered(result))
36
+ idle();
37
+ return result;
38
+ }
39
+ const IDLE = { busy: false, outcome: null };
40
+ const SENDING = { state: 'sending' };
41
+ function useActs(overlay, online) {
42
+ const [running, setRunning] = useState(IDLE);
43
+ const [tried] = useState(() => attempts());
44
+ const latest = useRef(0);
45
+ const mounted = useRef(true);
46
+ useEffect(() => {
47
+ mounted.current = true;
48
+ return () => {
49
+ mounted.current = false;
50
+ };
51
+ }, []);
52
+ const clear = useCallback(() => {
53
+ latest.current += 1;
54
+ setRunning(IDLE);
55
+ }, []);
56
+ const call = useCallback((steps, expected = null, then) => {
57
+ latest.current += 1;
58
+ const mine = latest.current;
59
+ const act = overlay.begin();
60
+ const drawn = typeof expected === 'function' ? null : expected;
61
+ overlay.expect(act, drawn);
62
+ setRunning({ busy: true, outcome: expectedList(drawn).length > 0 ? null : SENDING });
63
+ const attempt = tried.begin(act);
64
+ const send = sender({
65
+ online,
66
+ identity: attempt.identity,
67
+ expect: (expecting) => overlay.expect(overlay.begin(), expecting),
68
+ draw: (expecting) => overlay.expect(act, expecting),
69
+ });
70
+ return steps(send)
71
+ .catch(() => ({
72
+ kind: 'transport-uncertainty',
73
+ code: 'transport-uncertainty',
74
+ fields: {},
75
+ retryable: true,
76
+ }))
77
+ .then((result) => {
78
+ if (accepted(result) && typeof expected === 'function') {
79
+ overlay.expect(act, expected(result.result));
80
+ }
81
+ attempt.settle(answeredOf(result), (one) => overlay.drop(one));
82
+ if (!mounted.current || latest.current !== mine)
83
+ return accepted(result);
84
+ if (accepted(result))
85
+ then?.(result.result);
86
+ if (latest.current === mine)
87
+ setRunning({ busy: false, outcome: outcomeOf(result) });
88
+ return accepted(result);
89
+ });
90
+ }, [overlay, online, tried]);
91
+ return { ...running, call, clear };
92
+ }
93
+ const ATTACHMENTS = {
94
+ accept: '*',
95
+ add: ({ file }) => Promise.resolve({
96
+ id: generateId(),
97
+ type: 'file',
98
+ name: file.name,
99
+ contentType: file.type,
100
+ file,
101
+ status: { type: 'requires-action', reason: 'composer-send' },
102
+ }),
103
+ send: (attachment) => Promise.resolve({ ...attachment, status: { type: 'complete' }, content: [] }),
104
+ remove: () => Promise.resolve(),
105
+ };
106
+ function filesOf(attachments) {
107
+ return attachments.flatMap((one) => one.file === undefined ? [] : [{ name: one.name, blob: one.file }]);
108
+ }
109
+ function fileOf(file) {
110
+ return file.blob instanceof File && file.blob.name === file.name
111
+ ? file.blob
112
+ : new File([file.blob], file.name, { type: file.blob.type });
113
+ }
114
+ function textOf(parts) {
115
+ return parts.map((part) => (part.type === 'text' ? (part.text ?? '') : '')).join('');
116
+ }
117
+ function draftOf(message) {
118
+ return { text: textOf(message.content), files: filesOf(message.attachments ?? []) };
119
+ }
120
+ function appended(draft) {
121
+ return {
122
+ role: 'user',
123
+ content: [{ type: 'text', text: draft.text }],
124
+ attachments: draft.files.map((file) => ({
125
+ id: generateId(),
126
+ type: 'file',
127
+ name: file.name,
128
+ contentType: file.blob.type,
129
+ file: fileOf(file),
130
+ status: { type: 'complete' },
131
+ content: [],
132
+ })),
133
+ metadata: { custom: {} },
134
+ createdAt: new Date(),
135
+ parentId: null,
136
+ sourceId: null,
137
+ runConfig: undefined,
138
+ };
139
+ }
140
+ function composed(state) {
141
+ return JSON.stringify([state.text, state.attachments.map((one) => one.id)]);
142
+ }
143
+ const ChatContext = createContext(null);
144
+ const ScopeContext = createContext(null);
145
+ export function useChatScope() {
146
+ const scope = useContext(ScopeContext);
147
+ if (scope === null)
148
+ throw new Error('useChat is called outside a ChatProvider');
149
+ return scope;
150
+ }
151
+ export function useChat() {
152
+ const chat = useContext(ChatContext);
153
+ if (chat === null)
154
+ throw new Error('useChat is called outside a ChatProvider');
155
+ return chat;
156
+ }
157
+ export function ChatProvider({ client, conversation = null, agent = null, onOpen, children, }) {
158
+ const shared = sharedOf(client);
159
+ const asked = JSON.stringify([conversation, agent]);
160
+ const [followed, setFollowed] = useState(asked);
161
+ const [open, setOpen] = useState({ conversation, agent });
162
+ if (followed !== asked) {
163
+ setFollowed(asked);
164
+ setOpen({ conversation, agent });
165
+ }
166
+ const opened = useRef(open);
167
+ opened.current = open;
168
+ const told = useRef(onOpen);
169
+ told.current = onOpen;
170
+ const go = useCallback((next) => {
171
+ opened.current = next;
172
+ setOpen(next);
173
+ told.current?.(next);
174
+ }, []);
175
+ const listedHeld = ready(useObserve(client, 'conversations'));
176
+ const agentsHeld = ready(useObserve(client, 'agents'));
177
+ const sync = ready(useObserve(client, 'sync-status'));
178
+ const refusal = ready(useObserve(client, 'connection-refusal'));
179
+ const online = sync !== null && !sync.providerUnreachable && refusal === null;
180
+ const thread = open.conversation ?? (open.agent === null ? null : newChat(open.agent));
181
+ const held = ready(useObserve(thread === null ? null : client, 'conversation', thread ?? '', {}));
182
+ const version = useSyncExternalStore(shared.overlay.subscribe, shared.overlay.version);
183
+ useEffect(() => {
184
+ if (listedHeld !== null)
185
+ shared.overlay.heardList(listedHeld);
186
+ }, [shared, listedHeld, version]);
187
+ useEffect(() => {
188
+ if (held !== null)
189
+ shared.overlay.heard(held);
190
+ }, [shared, held, version]);
191
+ const conversations = useMemo(() => shared.overlay.conversations(listedHeld ?? []), [shared, listedHeld, version]);
192
+ const view = useMemo(() => held === null || held.conversation !== thread ? null : shared.overlay.conversation(held), [shared, held, thread, version]);
193
+ const agents = agentsHeld ?? [];
194
+ const known = useRef(agents);
195
+ known.current = agents;
196
+ const entry = conversations.find((one) => one.conversation === open.conversation);
197
+ const running = entry?.state === 'working' || view?.turn != null;
198
+ const questions = useMemo(() => questionsOf(view), [view]);
199
+ const lone = loneOf(questions);
200
+ const commands = useMemo(() => commandsOf(view), [view]);
201
+ const messages = useMemo(() => chatMessages(view), [view]);
202
+ const sending = useActs(shared.overlay, online);
203
+ const behind = useActs(shared.overlay, online);
204
+ const acting = useActs(shared.overlay, online);
205
+ const [again, setAgain] = useState(null);
206
+ const current = useRef(thread);
207
+ current.current = thread;
208
+ const seeding = useRef(false);
209
+ const heard = useRef('');
210
+ const shown = useRef('');
211
+ const own = useRef([]);
212
+ const noted = useCallback((to, revision) => {
213
+ own.current = [...own.current, `${to}\u0000${revision ?? ''}`];
214
+ }, []);
215
+ const keep = useCallback((to, draft) => {
216
+ const revision = draft.text === '' && draft.files.length === 0 ? null : chatDraftRevision();
217
+ noted(to, revision);
218
+ void (revision === null
219
+ ? client.chatDraft.discard(to)
220
+ : client.chatDraft.write(to, draft, revision));
221
+ }, [client, noted]);
222
+ useEffect(() => {
223
+ heard.current = '';
224
+ shown.current = '';
225
+ own.current = own.current.filter((one) => one.startsWith(`${thread}\u0000`));
226
+ sending.clear();
227
+ acting.clear();
228
+ setAgain(null);
229
+ }, [thread, sending.clear, acting.clear]);
230
+ const sendTo = (to, said, key, idle, acts = sending) => acts.call((send) => deliver(client, send, to, said, key, idle), sentMessage(key, [to], said, null));
231
+ const first = (to, said, key, returning, idle) => {
232
+ const drafted = [newChat(to)];
233
+ let made = null;
234
+ return sending.call(async (send) => {
235
+ let conversation = shared.created.get(to);
236
+ if (conversation === undefined) {
237
+ const answer = await send.carried('conversation.create', { agent: to }, (operation) => client.conversation.create({ agent_id: to, operation_id: operation }));
238
+ if (!accepted(answer)) {
239
+ if (!unanswered(answer)) {
240
+ returning();
241
+ idle();
242
+ }
243
+ return answer;
244
+ }
245
+ const named = text(record(answer.result), 'conversation');
246
+ if (named === null)
247
+ return answer;
248
+ conversation = named;
249
+ shared.created.set(to, conversation);
250
+ const name = known.current.find((one) => one.identity === to)?.name ?? null;
251
+ send.expect(conversationListed({
252
+ conversation,
253
+ agent: { identity: to, name },
254
+ name: name ?? '',
255
+ created: new Date().toISOString(),
256
+ finished: null,
257
+ outcome: null,
258
+ state: 'idle',
259
+ started: null,
260
+ }));
261
+ }
262
+ made = conversation;
263
+ send.draw(sentMessage(key, [...drafted, conversation], said, null));
264
+ const result = await deliver(client, send, conversation, said, key, idle);
265
+ if (accepted(result))
266
+ shared.created.delete(to);
267
+ return result;
268
+ }, sentMessage(key, drafted, said, null), () => {
269
+ if (made === null)
270
+ return;
271
+ void client.chatDraft.discard(newChat(to));
272
+ const now = opened.current;
273
+ if (now.conversation === null && now.agent === to)
274
+ go({ conversation: made, agent: to });
275
+ });
276
+ };
277
+ const returned = async (to, one) => {
278
+ const now = (await client.conversation.read(to)).chatDraft;
279
+ await client.chatDraft.write(to, {
280
+ text: now === null || now.text === '' ? one.text : `${one.text}\n${now.text}`,
281
+ files: [...one.files, ...(now?.files ?? [])],
282
+ });
283
+ };
284
+ const sent = () => {
285
+ shared.sends += 1;
286
+ return `sent ${shared.sends}`;
287
+ };
288
+ const deliverDraft = (said, returning, idle) => {
289
+ const key = sent();
290
+ const { conversation: to, agent: by } = opened.current;
291
+ const attempt = to !== null
292
+ ? () => void sendTo(to, said, key, idle)
293
+ : by !== null
294
+ ? () => void first(by, said, key, returning, idle)
295
+ : null;
296
+ if (attempt === null)
297
+ return;
298
+ setAgain(() => attempt);
299
+ attempt();
300
+ };
301
+ const control = (name, input, expected) => acting.call((send) => send.carried(name, input, (operation) => client.call(name, { ...input, operation_id: operation })), expected);
302
+ const carried = (name, act) => {
303
+ const to = opened.current.conversation;
304
+ if (to === null)
305
+ return Promise.resolve(false);
306
+ return acting.call((send) => send.carried(name, { conversation: to }, () => act(to)));
307
+ };
308
+ const stop = () => carried('conversation.interrupt', (to) => client.conversation.interrupt(to));
309
+ const kill = () => carried('conversation.kill', (to) => client.conversation.kill(to));
310
+ const answer = (question, response) => {
311
+ const to = opened.current.conversation;
312
+ if (to === null || question.secret)
313
+ return Promise.resolve(false);
314
+ return acting.call((send) => send.carried('conversation.answer', { conversation: to, question: question.interaction }, () => client.conversation.answer(to, { interaction_id: question.interaction, response })), () => questionAnswered(to, question.interaction));
315
+ };
316
+ const choose = (option, value) => {
317
+ const to = opened.current.conversation;
318
+ if (to === null)
319
+ return Promise.resolve(false);
320
+ return control('conversation.setOption', { conversation_id: to, option, value }, optionChosen(to, option, value));
321
+ };
322
+ const done = (to = opened.current.conversation) => to === null
323
+ ? Promise.resolve(false)
324
+ : control('conversation.markDone', { conversation_id: to }, conversationChanged(to, { finished: new Date().toISOString() }, (one) => one.finished !== null));
325
+ const rename = (name, to = opened.current.conversation) => to === null
326
+ ? Promise.resolve(false)
327
+ : control('conversation.rename', { conversation_id: to, name }, conversationChanged(to, { name }, (one) => one.name === name));
328
+ const remove = (to = opened.current.conversation) => to === null
329
+ ? Promise.resolve(false)
330
+ : acting.call((send) => send.carried('conversation.delete', { conversation: to }, () => client.conversation.delete(to)), conversationGone(to), () => {
331
+ const now = opened.current;
332
+ if (now.conversation === to)
333
+ go({ conversation: null, agent: now.agent });
334
+ });
335
+ const openConversation = (id) => go({
336
+ conversation: id,
337
+ agent: conversations.find((one) => one.conversation === id)?.agent.identity ?? null,
338
+ });
339
+ const start = (by) => go({ conversation: null, agent: by });
340
+ const dispatch = useRef(() => undefined);
341
+ const queues = useRef(new Map());
342
+ const into = thread ?? '';
343
+ const laneOf = (key) => {
344
+ const held = queues.current.get(key);
345
+ if (held !== undefined)
346
+ return held;
347
+ const fresh = {
348
+ queue: createMessageQueue({ run: (message) => dispatch.current(message, key) }),
349
+ running: false,
350
+ };
351
+ queues.current.set(key, fresh);
352
+ return fresh;
353
+ };
354
+ const lane = laneOf(into);
355
+ const queue = lane.queue;
356
+ const [, moved] = useReducer((count) => count + 1, 0);
357
+ useEffect(() => queue.subscribe(moved), [queue]);
358
+ useEffect(() => {
359
+ for (const [key, one] of queues.current) {
360
+ const now = key === into
361
+ ? running
362
+ : conversations.some((entry) => entry.conversation === key && entry.state !== 'idle');
363
+ const empty = one.queue.adapter.items.length + one.queue.adapter.steerItems.length === 0;
364
+ if (key !== into && !now && empty) {
365
+ queues.current.delete(key);
366
+ continue;
367
+ }
368
+ if (now === one.running)
369
+ continue;
370
+ one.running = now;
371
+ if (now)
372
+ one.queue.notifyBusy();
373
+ else
374
+ one.queue.notifyIdle();
375
+ }
376
+ }, [into, running, conversations]);
377
+ const waiting = useRef(new Map());
378
+ const lanes = () => [...queue.adapter.steerItems, ...queue.adapter.items];
379
+ const threads = useMemo(() => {
380
+ const item = (one) => ({
381
+ id: one.conversation,
382
+ title: one.name,
383
+ custom: { agent: one.agent.identity, state: one.state, finished: one.finished },
384
+ });
385
+ return {
386
+ regular: conversations
387
+ .filter((one) => one.finished === null)
388
+ .map((one) => ({ ...item(one), status: 'regular' })),
389
+ archived: conversations
390
+ .filter((one) => one.finished !== null)
391
+ .map((one) => ({ ...item(one), status: 'archived' })),
392
+ };
393
+ }, [conversations]);
394
+ const aui = useExternalStoreRuntime({
395
+ messages,
396
+ isRunning: running,
397
+ convertMessage: (message) => message,
398
+ queue: lined(),
399
+ onNew: async (message) => lined().enqueue(message),
400
+ onCancel: async () => {
401
+ void stop();
402
+ },
403
+ onReload: thread === null
404
+ ? undefined
405
+ : async (parent) => {
406
+ const said = askedBefore(view, parent);
407
+ if (said === null)
408
+ return;
409
+ const draft = { text: said, files: [] };
410
+ const into = thread;
411
+ deliverDraft(draft, () => back(draft, into), () => undefined);
412
+ },
413
+ adapters: {
414
+ attachments: ATTACHMENTS,
415
+ threadList: {
416
+ ...(thread === null ? {} : { threadId: thread }),
417
+ threads: threads.regular,
418
+ archivedThreads: threads.archived,
419
+ onSwitchToThread: (id) => openConversation(id),
420
+ onSwitchToNewThread: () => {
421
+ const by = open.agent ?? known.current[0]?.identity;
422
+ if (by !== undefined)
423
+ start(by);
424
+ },
425
+ onRename: (id, name) => {
426
+ void rename(name, id);
427
+ },
428
+ onArchive: (id) => {
429
+ void done(id);
430
+ },
431
+ onDelete: (id) => {
432
+ void remove(id);
433
+ },
434
+ },
435
+ },
436
+ });
437
+ function back(one, into) {
438
+ if (current.current !== into) {
439
+ void returned(into, one);
440
+ return;
441
+ }
442
+ const composer = aui.thread.composer;
443
+ const now = composer.getState().text;
444
+ composer.setText(now === '' ? one.text : `${one.text}\n${now}`);
445
+ for (const file of one.files)
446
+ void composer.addAttachment(fileOf(file));
447
+ }
448
+ function taken(message) {
449
+ const said = textOf(message.content);
450
+ if (lone === null || commandIn(said, commands))
451
+ return false;
452
+ void answer(lone.question, lone.question.accept({ [lone.field.name]: said }));
453
+ return true;
454
+ }
455
+ function lined() {
456
+ const holding = (put) => (message) => {
457
+ if (taken(message))
458
+ return;
459
+ if (!online) {
460
+ const draft = draftOf(message);
461
+ const at = current.current;
462
+ deliverDraft(draft, () => {
463
+ if (at !== null)
464
+ back(draft, at);
465
+ }, () => undefined);
466
+ return;
467
+ }
468
+ const known = new Set(lanes().map((one) => one.id));
469
+ if (!lane.running)
470
+ queue.notifyIdle();
471
+ put(message);
472
+ const added = lanes().find((one) => !known.has(one.id));
473
+ if (added !== undefined)
474
+ waiting.current.set(added.id, message);
475
+ };
476
+ return {
477
+ get items() {
478
+ return queue.adapter.items;
479
+ },
480
+ get steerItems() {
481
+ return queue.adapter.steerItems;
482
+ },
483
+ enqueue: holding(queue.adapter.enqueue),
484
+ steer: holding(queue.adapter.steer),
485
+ move: queue.adapter.move,
486
+ edit: queue.adapter.edit,
487
+ remove: queue.adapter.remove,
488
+ __internal_setDispatchTransform: queue.adapter.__internal_setDispatchTransform,
489
+ };
490
+ }
491
+ dispatch.current = (message, key) => {
492
+ const draft = draftOf(message);
493
+ const idle = () => queues.current.get(key)?.queue.notifyIdle();
494
+ if (key !== (current.current ?? '')) {
495
+ void sendTo(key, draft, sent(), idle, behind);
496
+ return;
497
+ }
498
+ const into = current.current;
499
+ deliverDraft(draft, () => {
500
+ if (into !== null)
501
+ back(draft, into);
502
+ }, idle);
503
+ };
504
+ const unqueue = (id) => {
505
+ const message = waiting.current.get(id);
506
+ waiting.current.delete(id);
507
+ queue.adapter.remove(id);
508
+ return message;
509
+ };
510
+ const edit = (id) => {
511
+ const item = lanes().find((one) => one.id === id);
512
+ const message = unqueue(id);
513
+ if (thread === null || item === undefined)
514
+ return;
515
+ back(message === undefined ? { text: textOf(item.parts), files: [] } : draftOf(message), thread);
516
+ };
517
+ const reconcile = useRef(() => undefined);
518
+ reconcile.current = () => {
519
+ if (thread === null || view === null || seeding.current)
520
+ return;
521
+ const at = `${thread}\u0000`;
522
+ const now = `${at}${view.chatDraft?.revision ?? ''}`;
523
+ if (now === heard.current)
524
+ return;
525
+ const echo = own.current.indexOf(now);
526
+ const known = heard.current.startsWith(at);
527
+ heard.current = now;
528
+ if (known && echo >= 0) {
529
+ own.current = own.current.slice(echo);
530
+ return;
531
+ }
532
+ own.current = own.current.filter((one) => !one.startsWith(at));
533
+ const composer = aui.thread.composer;
534
+ const state = composer.getState();
535
+ const stored = view.chatDraft;
536
+ const typed = { text: state.text, files: filesOf(state.attachments) };
537
+ const kept = !known && (typed.text !== '' || typed.files.length > 0);
538
+ if (kept && stored === null) {
539
+ shown.current = `${at}${composed(state)}`;
540
+ keep(thread, typed);
541
+ return;
542
+ }
543
+ const draft = kept
544
+ ? {
545
+ text: [stored?.text ?? '', typed.text].filter((one) => one !== '').join('\n'),
546
+ files: [...(stored?.files ?? []), ...typed.files],
547
+ }
548
+ : stored;
549
+ if (state.text === (draft?.text ?? '') &&
550
+ state.attachments.length === 0 &&
551
+ (draft?.files.length ?? 0) === 0) {
552
+ shown.current = `${at}${composed(state)}`;
553
+ return;
554
+ }
555
+ seeding.current = true;
556
+ composer.setText(draft?.text ?? '');
557
+ void composer
558
+ .clearAttachments()
559
+ .then(() => Promise.all((draft?.files ?? []).map((file) => composer.addAttachment(fileOf(file)))))
560
+ .finally(() => {
561
+ seeding.current = false;
562
+ if (current.current !== thread)
563
+ return;
564
+ const seeded = composer.getState();
565
+ shown.current = `${at}${composed(seeded)}`;
566
+ if (kept)
567
+ keep(thread, { text: seeded.text, files: filesOf(seeded.attachments) });
568
+ reconcile.current();
569
+ });
570
+ };
571
+ useEffect(() => reconcile.current(), [aui, thread, view]);
572
+ useEffect(() => {
573
+ if (thread === null)
574
+ return;
575
+ const at = `${thread}\u0000`;
576
+ const composer = aui.thread.composer;
577
+ return composer.subscribe(() => {
578
+ if (seeding.current || !heard.current.startsWith(at))
579
+ return;
580
+ const state = composer.getState();
581
+ const next = `${at}${composed(state)}`;
582
+ if (next === shown.current)
583
+ return;
584
+ shown.current = next;
585
+ keep(thread, { text: state.text, files: filesOf(state.attachments) });
586
+ });
587
+ }, [aui, thread, keep]);
588
+ const queued = lanes().map((item) => ({ id: item.id, text: textOf(item.parts) }));
589
+ const chat = {
590
+ ...open,
591
+ conversations,
592
+ agents,
593
+ view,
594
+ running,
595
+ questions,
596
+ lone,
597
+ options: optionsOf(view),
598
+ commands,
599
+ context: contextOf(view),
600
+ queued,
601
+ sending: { busy: sending.busy, outcome: sending.outcome, again },
602
+ acting: { busy: acting.busy, outcome: acting.outcome },
603
+ open: openConversation,
604
+ start,
605
+ send: (draft) => lined().enqueue(appended(draft)),
606
+ answer,
607
+ choose,
608
+ stop,
609
+ kill,
610
+ done: () => done(),
611
+ rename: (name) => rename(name),
612
+ remove: () => remove(),
613
+ edit,
614
+ unqueue: (id) => {
615
+ unqueue(id);
616
+ },
617
+ };
618
+ const scope = useMemo(() => ({ client, known: agentsHeld !== null }), [client, agentsHeld]);
619
+ return createElement(AssistantRuntimeProvider, { runtime: aui }, createElement(ScopeContext.Provider, { value: scope }, createElement(ChatContext.Provider, { value: chat }, children)));
620
+ }