@assistant-ui/react-a2a 0.2.21 → 0.2.23
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/A2AClient.d.ts +8 -5
- package/dist/A2AClient.d.ts.map +1 -1
- package/dist/A2AClient.js +85 -24
- package/dist/A2AClient.js.map +1 -1
- package/dist/A2AThreadRuntimeCore.d.ts +15 -4
- package/dist/A2AThreadRuntimeCore.d.ts.map +1 -1
- package/dist/A2AThreadRuntimeCore.js +162 -45
- package/dist/A2AThreadRuntimeCore.js.map +1 -1
- package/dist/a2aExtras.d.ts +0 -1
- package/dist/a2aExtras.d.ts.map +1 -1
- package/dist/conversions.d.ts +6 -4
- package/dist/conversions.d.ts.map +1 -1
- package/dist/conversions.js +32 -3
- package/dist/conversions.js.map +1 -1
- package/dist/hooks.d.ts +0 -1
- package/dist/hooks.d.ts.map +1 -1
- package/dist/types.d.ts +27 -14
- package/dist/types.d.ts.map +1 -1
- package/dist/useA2ARuntime.d.ts +0 -1
- package/dist/useA2ARuntime.d.ts.map +1 -1
- package/dist/useA2ARuntime.js +30 -18
- package/dist/useA2ARuntime.js.map +1 -1
- package/package.json +11 -6
- package/src/A2AClient.test.ts +321 -4
- package/src/A2AClient.ts +158 -49
- package/src/A2AThreadRuntimeCore.test.ts +458 -0
- package/src/A2AThreadRuntimeCore.ts +215 -57
- package/src/conversions.test.ts +109 -2
- package/src/conversions.ts +46 -4
- package/src/useA2ARuntime.test.tsx +142 -0
- package/src/useA2ARuntime.ts +55 -36
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { a2aMessageToContent, isTerminalTaskState, taskStateToMessageStatus } from "./conversions.js";
|
|
2
|
+
import { a2aMessageToContent, contentPartsToA2AParts, isTerminalTaskState, taskStateToMessageStatus } from "./conversions.js";
|
|
3
|
+
import { MessageRepository } from "@assistant-ui/core/internal";
|
|
3
4
|
import { fromThreadMessageLike, generateId } from "@assistant-ui/core";
|
|
4
5
|
//#region src/A2AThreadRuntimeCore.ts
|
|
5
6
|
const FALLBACK_USER_STATUS = {
|
|
@@ -16,7 +17,8 @@ var A2AThreadRuntimeCore = class {
|
|
|
16
17
|
history;
|
|
17
18
|
notifyUpdate;
|
|
18
19
|
runtime;
|
|
19
|
-
|
|
20
|
+
repository = new MessageRepository();
|
|
21
|
+
exportedRepository;
|
|
20
22
|
isRunningFlag = false;
|
|
21
23
|
abortController = null;
|
|
22
24
|
pendingError = null;
|
|
@@ -60,7 +62,52 @@ var A2AThreadRuntimeCore = class {
|
|
|
60
62
|
return this.runtime;
|
|
61
63
|
}
|
|
62
64
|
getMessages() {
|
|
63
|
-
return this.
|
|
65
|
+
return this.repository.getMessages();
|
|
66
|
+
}
|
|
67
|
+
getMessageRepository() {
|
|
68
|
+
this.exportedRepository ??= this.repository.export();
|
|
69
|
+
return this.exportedRepository;
|
|
70
|
+
}
|
|
71
|
+
tryGetMessage(messageId) {
|
|
72
|
+
try {
|
|
73
|
+
return this.repository.getMessage(messageId);
|
|
74
|
+
} catch {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
tryGetMessages(messageId) {
|
|
79
|
+
try {
|
|
80
|
+
return this.repository.getMessages(messageId);
|
|
81
|
+
} catch {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
hasMessage(messageId) {
|
|
86
|
+
return this.tryGetMessage(messageId) !== void 0;
|
|
87
|
+
}
|
|
88
|
+
addOrUpdateMessage(parentId, message) {
|
|
89
|
+
this.repository.addOrUpdateMessage(parentId, message);
|
|
90
|
+
this.exportedRepository = void 0;
|
|
91
|
+
}
|
|
92
|
+
switchToBranch(messageId) {
|
|
93
|
+
this.repository.switchToBranch(messageId);
|
|
94
|
+
this.exportedRepository = void 0;
|
|
95
|
+
}
|
|
96
|
+
resetRepositoryHead(messageId) {
|
|
97
|
+
this.repository.resetHead(messageId);
|
|
98
|
+
this.exportedRepository = void 0;
|
|
99
|
+
}
|
|
100
|
+
clearRepository() {
|
|
101
|
+
this.repository.clear();
|
|
102
|
+
this.exportedRepository = void 0;
|
|
103
|
+
}
|
|
104
|
+
updateMessage(messageId, updater) {
|
|
105
|
+
const item = this.tryGetMessage(messageId);
|
|
106
|
+
if (!item) return false;
|
|
107
|
+
const message = updater(item.message);
|
|
108
|
+
if (message === item.message) return false;
|
|
109
|
+
this.addOrUpdateMessage(item.parentId, message);
|
|
110
|
+
return true;
|
|
64
111
|
}
|
|
65
112
|
getTask() {
|
|
66
113
|
return this.currentTask;
|
|
@@ -84,10 +131,7 @@ var A2AThreadRuntimeCore = class {
|
|
|
84
131
|
const agentCardPromise = this.client.getAgentCard().catch(() => void 0);
|
|
85
132
|
this._loadPromise = Promise.all([historyPromise, agentCardPromise]).then(([repo, agentCard]) => {
|
|
86
133
|
if (agentCard) this.agentCardValue = agentCard;
|
|
87
|
-
if (repo)
|
|
88
|
-
const messages = repo.messages.map((item) => item.message);
|
|
89
|
-
this.applyExternalMessages(messages);
|
|
90
|
-
}
|
|
134
|
+
if (repo) this.applyExternalMessageRepository(repo);
|
|
91
135
|
}).catch((error) => {
|
|
92
136
|
this.onError?.(error instanceof Error ? error : new Error(String(error)));
|
|
93
137
|
}).finally(() => {
|
|
@@ -99,12 +143,12 @@ var A2AThreadRuntimeCore = class {
|
|
|
99
143
|
}
|
|
100
144
|
async append(message) {
|
|
101
145
|
const startRun = message.startRun ?? message.role === "user";
|
|
102
|
-
if (message.sourceId) this.messages = this.messages.filter((entry) => entry.id !== message.sourceId);
|
|
103
|
-
this.resetHead(message.parentId);
|
|
104
146
|
const threadMessage = fromThreadMessageLike(message, generateId(), FALLBACK_USER_STATUS);
|
|
105
|
-
this.
|
|
147
|
+
const parentId = message.parentId === null ? null : message.parentId && this.hasMessage(message.parentId) ? message.parentId : this.repository.headId;
|
|
148
|
+
this.addOrUpdateMessage(parentId, threadMessage);
|
|
149
|
+
this.switchToBranch(threadMessage.id);
|
|
106
150
|
this.notifyUpdate();
|
|
107
|
-
this.recordHistoryEntry(
|
|
151
|
+
this.recordHistoryEntry(parentId, threadMessage);
|
|
108
152
|
if (!startRun) return;
|
|
109
153
|
await this.startRun(threadMessage);
|
|
110
154
|
}
|
|
@@ -112,10 +156,9 @@ var A2AThreadRuntimeCore = class {
|
|
|
112
156
|
await this.append(message);
|
|
113
157
|
}
|
|
114
158
|
async reload(parentId, _config = {}) {
|
|
115
|
-
this.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
await this.startRun(this.messages[i]);
|
|
159
|
+
const messages = parentId === null ? [] : this.tryGetMessages(parentId) ?? this.getMessages();
|
|
160
|
+
for (let i = messages.length - 1; i >= 0; i--) if (messages[i].role === "user") {
|
|
161
|
+
await this.startRun(messages[i]);
|
|
119
162
|
return;
|
|
120
163
|
}
|
|
121
164
|
}
|
|
@@ -127,15 +170,101 @@ var A2AThreadRuntimeCore = class {
|
|
|
127
170
|
this.currentTask = updated;
|
|
128
171
|
} catch {}
|
|
129
172
|
}
|
|
130
|
-
|
|
173
|
+
appendLinearChain(messages) {
|
|
174
|
+
let parentId = null;
|
|
175
|
+
let lastId = null;
|
|
176
|
+
const seen = /* @__PURE__ */ new Set();
|
|
177
|
+
for (const message of messages) {
|
|
178
|
+
if (seen.has(message.id)) continue;
|
|
179
|
+
seen.add(message.id);
|
|
180
|
+
this.addOrUpdateMessage(parentId, message);
|
|
181
|
+
parentId = message.id;
|
|
182
|
+
lastId = message.id;
|
|
183
|
+
}
|
|
184
|
+
return lastId;
|
|
185
|
+
}
|
|
186
|
+
finalizeExternalApply() {
|
|
131
187
|
this.assistantHistoryParents.clear();
|
|
132
|
-
this.messages = [...messages];
|
|
133
188
|
this.recordedHistoryIds.clear();
|
|
134
|
-
for (const message of this.messages) this.recordedHistoryIds.add(message.id);
|
|
189
|
+
for (const { message } of this.getMessageRepository().messages) this.recordedHistoryIds.add(message.id);
|
|
135
190
|
this.currentTask = void 0;
|
|
136
191
|
this.currentArtifacts = [];
|
|
137
192
|
this.notifyUpdate();
|
|
138
193
|
}
|
|
194
|
+
applyExternalMessages(messages) {
|
|
195
|
+
if (messages.length === 0) this.clearRepository();
|
|
196
|
+
else {
|
|
197
|
+
let expectedParentId = null;
|
|
198
|
+
let lastAppliedId = null;
|
|
199
|
+
let hardReplace = false;
|
|
200
|
+
const seen = /* @__PURE__ */ new Set();
|
|
201
|
+
for (const message of messages) {
|
|
202
|
+
if (seen.has(message.id)) continue;
|
|
203
|
+
seen.add(message.id);
|
|
204
|
+
const existing = this.tryGetMessage(message.id);
|
|
205
|
+
if (existing && existing.parentId !== expectedParentId) {
|
|
206
|
+
hardReplace = true;
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
this.addOrUpdateMessage(expectedParentId, message);
|
|
210
|
+
expectedParentId = message.id;
|
|
211
|
+
lastAppliedId = message.id;
|
|
212
|
+
}
|
|
213
|
+
if (hardReplace) {
|
|
214
|
+
this.clearRepository();
|
|
215
|
+
lastAppliedId = this.appendLinearChain(messages);
|
|
216
|
+
}
|
|
217
|
+
this.resetRepositoryHead(lastAppliedId);
|
|
218
|
+
}
|
|
219
|
+
this.finalizeExternalApply();
|
|
220
|
+
}
|
|
221
|
+
applyExternalMessageRepository(loaded) {
|
|
222
|
+
const headId = loaded.headId ?? loaded.messages.at(-1)?.message.id ?? null;
|
|
223
|
+
const ids = /* @__PURE__ */ new Set();
|
|
224
|
+
let degenerate = false;
|
|
225
|
+
for (const { message } of loaded.messages) {
|
|
226
|
+
if (ids.has(message.id)) {
|
|
227
|
+
degenerate = true;
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
ids.add(message.id);
|
|
231
|
+
}
|
|
232
|
+
if (headId !== null && !ids.has(headId)) degenerate = true;
|
|
233
|
+
if (!degenerate) {
|
|
234
|
+
this.clearRepository();
|
|
235
|
+
let pending = [...loaded.messages];
|
|
236
|
+
const importedIds = /* @__PURE__ */ new Set();
|
|
237
|
+
while (pending.length > 0) {
|
|
238
|
+
const unresolved = [];
|
|
239
|
+
let progressed = false;
|
|
240
|
+
for (const item of pending) {
|
|
241
|
+
if (item.parentId !== null && !importedIds.has(item.parentId)) {
|
|
242
|
+
unresolved.push(item);
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
this.addOrUpdateMessage(item.parentId, item.message);
|
|
246
|
+
importedIds.add(item.message.id);
|
|
247
|
+
progressed = true;
|
|
248
|
+
}
|
|
249
|
+
if (!progressed) {
|
|
250
|
+
degenerate = true;
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
pending = unresolved;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (degenerate) {
|
|
257
|
+
this.clearRepository();
|
|
258
|
+
let previousId = null;
|
|
259
|
+
for (const { message } of loaded.messages) {
|
|
260
|
+
const existing = this.tryGetMessage(message.id);
|
|
261
|
+
this.addOrUpdateMessage(existing ? existing.parentId : previousId, message);
|
|
262
|
+
previousId = message.id;
|
|
263
|
+
}
|
|
264
|
+
this.resetRepositoryHead(previousId);
|
|
265
|
+
} else this.resetRepositoryHead(headId);
|
|
266
|
+
this.finalizeExternalApply();
|
|
267
|
+
}
|
|
139
268
|
async startRun(userThreadMessage) {
|
|
140
269
|
if (this.abortController) {
|
|
141
270
|
this.abortController.abort();
|
|
@@ -145,7 +274,7 @@ var A2AThreadRuntimeCore = class {
|
|
|
145
274
|
if (this.currentTask && isTerminalTaskState(this.currentTask.status.state)) this.currentTask = void 0;
|
|
146
275
|
this.currentArtifacts = [];
|
|
147
276
|
const assistantParentId = userThreadMessage.id;
|
|
148
|
-
const assistantId = this.insertAssistantPlaceholder();
|
|
277
|
+
const assistantId = this.insertAssistantPlaceholder(assistantParentId);
|
|
149
278
|
this.markPendingAssistantHistory(assistantId, assistantParentId);
|
|
150
279
|
const abortController = new AbortController();
|
|
151
280
|
this.abortController = abortController;
|
|
@@ -291,11 +420,8 @@ var A2AThreadRuntimeCore = class {
|
|
|
291
420
|
threadMessageToA2AMessage(message) {
|
|
292
421
|
const parts = [];
|
|
293
422
|
if (message.role === "user") {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
url: part.image,
|
|
297
|
-
mediaType: "image/*"
|
|
298
|
-
});
|
|
423
|
+
parts.push(...contentPartsToA2AParts(message.content));
|
|
424
|
+
for (const attachment of message.attachments ?? []) parts.push(...contentPartsToA2AParts(attachment.content ?? [], attachment.contentType));
|
|
299
425
|
}
|
|
300
426
|
const a2aMsg = {
|
|
301
427
|
messageId: message.id,
|
|
@@ -306,7 +432,7 @@ var A2AThreadRuntimeCore = class {
|
|
|
306
432
|
if (this.currentTask?.id && !isTerminalTaskState(this.currentTask.status.state)) a2aMsg.taskId = this.currentTask.id;
|
|
307
433
|
return a2aMsg;
|
|
308
434
|
}
|
|
309
|
-
insertAssistantPlaceholder() {
|
|
435
|
+
insertAssistantPlaceholder(parentId) {
|
|
310
436
|
const id = generateId();
|
|
311
437
|
const assistant = {
|
|
312
438
|
id,
|
|
@@ -322,13 +448,14 @@ var A2AThreadRuntimeCore = class {
|
|
|
322
448
|
custom: {}
|
|
323
449
|
}
|
|
324
450
|
};
|
|
325
|
-
this.
|
|
451
|
+
this.addOrUpdateMessage(parentId, assistant);
|
|
452
|
+
this.switchToBranch(id);
|
|
326
453
|
this.notifyUpdate();
|
|
327
454
|
return id;
|
|
328
455
|
}
|
|
329
456
|
updateAssistantContent(messageId, content) {
|
|
330
|
-
this.
|
|
331
|
-
if (message.
|
|
457
|
+
this.updateMessage(messageId, (message) => {
|
|
458
|
+
if (message.role !== "assistant") return message;
|
|
332
459
|
return {
|
|
333
460
|
...message,
|
|
334
461
|
content
|
|
@@ -336,22 +463,21 @@ var A2AThreadRuntimeCore = class {
|
|
|
336
463
|
});
|
|
337
464
|
}
|
|
338
465
|
updateAssistantStatus(messageId, status) {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
if (message.id !== messageId || message.role !== "assistant") return message;
|
|
342
|
-
touched = true;
|
|
466
|
+
if (this.updateMessage(messageId, (message) => {
|
|
467
|
+
if (message.role !== "assistant") return message;
|
|
343
468
|
return {
|
|
344
469
|
...message,
|
|
345
470
|
status
|
|
346
471
|
};
|
|
347
|
-
})
|
|
348
|
-
if (touched) {
|
|
472
|
+
})) {
|
|
349
473
|
this.notifyUpdate();
|
|
350
474
|
if (status.type === "complete" || status.type === "incomplete") this.persistAssistantHistory(messageId);
|
|
351
475
|
}
|
|
352
476
|
}
|
|
353
477
|
getAssistantStatus(messageId) {
|
|
354
|
-
|
|
478
|
+
const msg = this.tryGetMessage(messageId)?.message;
|
|
479
|
+
if (msg?.role !== "assistant") return void 0;
|
|
480
|
+
return msg.status;
|
|
355
481
|
}
|
|
356
482
|
setRunning(running) {
|
|
357
483
|
this.isRunningFlag = running;
|
|
@@ -361,15 +487,6 @@ var A2AThreadRuntimeCore = class {
|
|
|
361
487
|
if (this.abortController === controller) this.abortController = null;
|
|
362
488
|
this.setRunning(false);
|
|
363
489
|
}
|
|
364
|
-
resetHead(parentId) {
|
|
365
|
-
if (!parentId) {
|
|
366
|
-
if (this.messages.length) this.messages = [];
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
369
|
-
const idx = this.messages.findIndex((message) => message.id === parentId);
|
|
370
|
-
if (idx === -1) return;
|
|
371
|
-
this.messages = this.messages.slice(0, idx + 1);
|
|
372
|
-
}
|
|
373
490
|
recordHistoryEntry(parentId, message) {
|
|
374
491
|
this.appendHistoryItem(parentId, message);
|
|
375
492
|
}
|
|
@@ -381,7 +498,7 @@ var A2AThreadRuntimeCore = class {
|
|
|
381
498
|
if (!this.history) return;
|
|
382
499
|
const parentId = this.assistantHistoryParents.get(messageId);
|
|
383
500
|
if (parentId === void 0) return;
|
|
384
|
-
const message = this.
|
|
501
|
+
const message = this.tryGetMessage(messageId)?.message;
|
|
385
502
|
if (!message || message.role !== "assistant") return;
|
|
386
503
|
if (message.status?.type !== "complete" && message.status?.type !== "incomplete") return;
|
|
387
504
|
this.assistantHistoryParents.delete(messageId);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"A2AThreadRuntimeCore.js","names":[],"sources":["../src/A2AThreadRuntimeCore.ts"],"sourcesContent":["\"use client\";\n\nimport { generateId, fromThreadMessageLike } from \"@assistant-ui/core\";\nimport type {\n AppendMessage,\n AssistantRuntime,\n MessageStatus,\n ThreadAssistantMessage,\n ThreadHistoryAdapter,\n ThreadMessage,\n} from \"@assistant-ui/core\";\nimport type { A2AClient } from \"./A2AClient\";\nimport type {\n A2AArtifact,\n A2AAgentCard,\n A2AMessage,\n A2APart,\n A2ASendMessageConfiguration,\n A2AStreamEvent,\n A2ATask,\n A2ATaskArtifactUpdateEvent,\n A2ATaskStatusUpdateEvent,\n} from \"./types\";\nimport {\n a2aMessageToContent,\n isTerminalTaskState,\n taskStateToMessageStatus,\n} from \"./conversions\";\n\nexport type A2AThreadRuntimeCoreOptions = {\n client: A2AClient;\n contextId?: string | undefined;\n configuration?: A2ASendMessageConfiguration | undefined;\n onError?: ((error: Error) => void) | undefined;\n onCancel?: (() => void) | undefined;\n onArtifactComplete?: ((artifact: A2AArtifact) => void) | undefined;\n history?: ThreadHistoryAdapter | undefined;\n notifyUpdate: () => void;\n};\n\nconst FALLBACK_USER_STATUS = {\n type: \"complete\",\n reason: \"unknown\",\n} as const;\n\nexport class A2AThreadRuntimeCore {\n private client: A2AClient;\n private contextId: string | undefined;\n private configuration: A2ASendMessageConfiguration | undefined;\n private onError: ((error: Error) => void) | undefined;\n private onCancel: (() => void) | undefined;\n private onArtifactComplete: ((artifact: A2AArtifact) => void) | undefined;\n private history: ThreadHistoryAdapter | undefined;\n private readonly notifyUpdate: () => void;\n\n private runtime: AssistantRuntime | undefined;\n private messages: ThreadMessage[] = [];\n private isRunningFlag = false;\n private abortController: AbortController | null = null;\n private pendingError: Error | null = null;\n\n // A2A-specific state\n private currentTask: A2ATask | undefined;\n private currentArtifacts: A2AArtifact[] = [];\n private agentCardValue: A2AAgentCard | undefined;\n\n // History tracking\n private readonly assistantHistoryParents = new Map<string, string | null>();\n private readonly recordedHistoryIds = new Set<string>();\n private _isLoading = false;\n private _loadPromise: Promise<void> | undefined;\n\n constructor(options: A2AThreadRuntimeCoreOptions) {\n this.client = options.client;\n this.contextId = options.contextId;\n this.configuration = options.configuration;\n this.onError = options.onError;\n this.onCancel = options.onCancel;\n this.onArtifactComplete = options.onArtifactComplete;\n this.history = options.history;\n this.notifyUpdate = options.notifyUpdate;\n }\n\n updateOptions(options: Omit<A2AThreadRuntimeCoreOptions, \"notifyUpdate\">) {\n this.client = options.client;\n this.contextId = options.contextId;\n this.configuration = options.configuration;\n this.onError = options.onError;\n this.onCancel = options.onCancel;\n this.onArtifactComplete = options.onArtifactComplete;\n this.history = options.history;\n }\n\n attachRuntime(runtime: AssistantRuntime) {\n this.runtime = runtime;\n }\n\n detachRuntime() {\n this.runtime = undefined;\n // Abort in-flight requests on unmount\n if (this.abortController) {\n this.abortController.abort();\n this.abortController = null;\n }\n }\n\n getRuntime(): AssistantRuntime | undefined {\n return this.runtime;\n }\n\n getMessages(): readonly ThreadMessage[] {\n return this.messages;\n }\n\n getTask(): A2ATask | undefined {\n return this.currentTask;\n }\n\n getArtifacts(): readonly A2AArtifact[] {\n return this.currentArtifacts;\n }\n\n getAgentCard(): A2AAgentCard | undefined {\n return this.agentCardValue;\n }\n\n isRunning(): boolean {\n return this.isRunningFlag;\n }\n\n get isLoading(): boolean {\n return this._isLoading;\n }\n\n __internal_load(): Promise<void> {\n if (this._loadPromise) return this._loadPromise;\n\n this._isLoading = true;\n\n const historyPromise = this.history?.load() ?? Promise.resolve(null);\n const agentCardPromise = this.client.getAgentCard().catch(() => undefined);\n\n this._loadPromise = Promise.all([historyPromise, agentCardPromise])\n .then(([repo, agentCard]) => {\n if (agentCard) {\n this.agentCardValue = agentCard;\n }\n if (repo) {\n const messages = repo.messages.map((item) => item.message);\n this.applyExternalMessages(messages);\n }\n })\n .catch((error) => {\n this.onError?.(\n error instanceof Error ? error : new Error(String(error)),\n );\n })\n .finally(() => {\n this._isLoading = false;\n this.notifyUpdate();\n });\n\n this.notifyUpdate();\n return this._loadPromise;\n }\n\n async append(message: AppendMessage): Promise<void> {\n const startRun = message.startRun ?? message.role === \"user\";\n if (message.sourceId) {\n this.messages = this.messages.filter(\n (entry) => entry.id !== message.sourceId,\n );\n }\n this.resetHead(message.parentId);\n\n const threadMessage = fromThreadMessageLike(\n message as any,\n generateId(),\n FALLBACK_USER_STATUS,\n );\n this.messages = [...this.messages, threadMessage];\n this.notifyUpdate();\n this.recordHistoryEntry(message.parentId ?? null, threadMessage);\n\n if (!startRun) return;\n await this.startRun(threadMessage);\n }\n\n async edit(message: AppendMessage): Promise<void> {\n await this.append(message);\n }\n\n async reload(\n parentId: string | null,\n _config: { runConfig?: Record<string, unknown> } = {},\n ): Promise<void> {\n this.resetHead(parentId);\n this.notifyUpdate();\n\n // Find the last user message to re-run\n for (let i = this.messages.length - 1; i >= 0; i--) {\n if (this.messages[i]!.role === \"user\") {\n await this.startRun(this.messages[i]!);\n return;\n }\n }\n }\n\n async cancel(): Promise<void> {\n if (!this.abortController) return;\n\n // Abort locally first so the stream stops immediately\n this.abortController.abort();\n\n // Then try to cancel the task on the server\n if (this.currentTask?.id) {\n try {\n const updated = await this.client.cancelTask(this.currentTask.id);\n this.currentTask = updated;\n } catch {\n // Server cancel failed; local abort already handled\n }\n }\n }\n\n applyExternalMessages(messages: readonly ThreadMessage[]): void {\n this.assistantHistoryParents.clear();\n this.messages = [...messages];\n this.recordedHistoryIds.clear();\n for (const message of this.messages) {\n this.recordedHistoryIds.add(message.id);\n }\n // Reset task-specific state to prevent leaking into new thread\n this.currentTask = undefined;\n this.currentArtifacts = [];\n this.notifyUpdate();\n }\n\n // --- Run logic ---\n\n private async startRun(userThreadMessage: ThreadMessage): Promise<void> {\n // Cancel any in-progress run before starting a new one\n if (this.abortController) {\n this.abortController.abort();\n this.abortController = null;\n }\n\n const a2aMessage = this.threadMessageToA2AMessage(userThreadMessage);\n\n // Clear task if previous task reached terminal state\n if (\n this.currentTask &&\n isTerminalTaskState(this.currentTask.status.state)\n ) {\n this.currentTask = undefined;\n }\n\n this.currentArtifacts = [];\n\n const assistantParentId = userThreadMessage.id;\n const assistantId = this.insertAssistantPlaceholder();\n this.markPendingAssistantHistory(assistantId, assistantParentId);\n\n const abortController = new AbortController();\n this.abortController = abortController;\n\n abortController.signal.addEventListener(\n \"abort\",\n () => {\n this.updateAssistantStatus(assistantId, {\n type: \"incomplete\",\n reason: \"cancelled\",\n });\n this.finishRun(abortController);\n this.onCancel?.();\n },\n { once: true },\n );\n\n this.setRunning(true);\n\n // Check if agent supports streaming; fall back to sync sendMessage if not\n const supportsStreaming =\n this.agentCardValue?.capabilities?.streaming !== false;\n\n try {\n if (supportsStreaming) {\n await this.runStreaming(a2aMessage, assistantId, abortController);\n } else {\n await this.runSync(a2aMessage, assistantId, abortController);\n }\n } catch (error) {\n if (!abortController.signal.aborted) {\n const err = error instanceof Error ? error : new Error(String(error));\n this.updateAssistantStatus(assistantId, {\n type: \"incomplete\",\n reason: \"error\",\n });\n this.onError?.(err);\n this.pendingError = this.pendingError ?? err;\n }\n } finally {\n this.finishRun(abortController);\n }\n\n if (this.pendingError) {\n const err = this.pendingError;\n this.pendingError = null;\n throw err;\n }\n }\n\n private async runStreaming(\n a2aMessage: A2AMessage,\n assistantId: string,\n abortController: AbortController,\n ): Promise<void> {\n const stream = this.client.streamMessage(\n a2aMessage,\n this.configuration,\n undefined, // metadata\n abortController.signal,\n );\n\n for await (const event of stream) {\n if (abortController.signal.aborted) break;\n this.handleStreamEvent(assistantId, event);\n }\n\n if (!abortController.signal.aborted) {\n const lastStatus = this.getAssistantStatus(assistantId);\n if (lastStatus?.type === \"running\") {\n this.updateAssistantStatus(assistantId, {\n type: \"complete\",\n reason: \"stop\",\n });\n }\n }\n }\n\n private async runSync(\n a2aMessage: A2AMessage,\n assistantId: string,\n abortController: AbortController,\n ): Promise<void> {\n const result = await this.client.sendMessage(\n a2aMessage,\n this.configuration,\n undefined, // metadata\n abortController.signal,\n );\n\n if (abortController.signal.aborted) return;\n\n // Result is either A2ATask or A2AMessage\n if (\"id\" in result && \"status\" in result) {\n // It's a Task\n this.handleTaskSnapshot(assistantId, result as A2ATask);\n } else if (\"messageId\" in result && \"parts\" in result) {\n // It's a Message\n this.handleMessage(assistantId, result as A2AMessage);\n this.updateAssistantStatus(assistantId, {\n type: \"complete\",\n reason: \"stop\",\n });\n }\n }\n\n private handleStreamEvent(assistantId: string, event: A2AStreamEvent) {\n switch (event.type) {\n case \"statusUpdate\":\n this.handleStatusUpdate(assistantId, event.event);\n break;\n case \"artifactUpdate\":\n this.handleArtifactUpdate(event.event);\n break;\n case \"message\":\n this.handleMessage(assistantId, event.message);\n break;\n case \"task\":\n this.handleTaskSnapshot(assistantId, event.task);\n break;\n }\n }\n\n private handleStatusUpdate(\n assistantId: string,\n event: A2ATaskStatusUpdateEvent,\n ) {\n if (!this.currentTask) {\n this.currentTask = {\n id: event.taskId,\n contextId: event.contextId,\n status: event.status,\n };\n } else {\n this.currentTask = { ...this.currentTask, status: event.status };\n }\n\n if (event.contextId) {\n this.contextId = event.contextId;\n }\n\n if (event.status.message) {\n const content = a2aMessageToContent(event.status.message);\n this.updateAssistantContent(assistantId, content);\n }\n\n const status = taskStateToMessageStatus(event.status.state);\n this.updateAssistantStatus(assistantId, status);\n\n this.notifyUpdate();\n }\n\n private handleArtifactUpdate(event: A2ATaskArtifactUpdateEvent) {\n const { artifact, append, lastChunk } = event;\n const existingIdx = this.currentArtifacts.findIndex(\n (a) => a.artifactId === artifact.artifactId,\n );\n\n let updated: A2AArtifact;\n if (existingIdx >= 0 && append) {\n const existing = this.currentArtifacts[existingIdx]!;\n updated = {\n ...existing,\n parts: [...existing.parts, ...artifact.parts],\n };\n this.currentArtifacts = [\n ...this.currentArtifacts.slice(0, existingIdx),\n updated,\n ...this.currentArtifacts.slice(existingIdx + 1),\n ];\n } else if (existingIdx >= 0) {\n updated = artifact;\n this.currentArtifacts = [\n ...this.currentArtifacts.slice(0, existingIdx),\n updated,\n ...this.currentArtifacts.slice(existingIdx + 1),\n ];\n } else {\n updated = artifact;\n this.currentArtifacts = [...this.currentArtifacts, updated];\n }\n\n if (lastChunk) {\n this.onArtifactComplete?.(updated);\n }\n\n this.notifyUpdate();\n }\n\n private handleMessage(assistantId: string, message: A2AMessage) {\n if (message.role !== \"agent\") return;\n\n const content = a2aMessageToContent(message);\n this.updateAssistantContent(assistantId, content);\n this.notifyUpdate();\n }\n\n private handleTaskSnapshot(assistantId: string, task: A2ATask) {\n this.currentTask = task;\n\n if (task.contextId) {\n this.contextId = task.contextId;\n }\n if (task.artifacts) {\n this.currentArtifacts = task.artifacts;\n }\n\n if (task.status.message) {\n const content = a2aMessageToContent(task.status.message);\n this.updateAssistantContent(assistantId, content);\n }\n\n const status = taskStateToMessageStatus(task.status.state);\n this.updateAssistantStatus(assistantId, status);\n\n this.notifyUpdate();\n }\n\n // --- Message helpers ---\n\n private threadMessageToA2AMessage(message: ThreadMessage): A2AMessage {\n const parts: A2APart[] = [];\n\n if (message.role === \"user\") {\n for (const part of message.content) {\n if (part.type === \"text\") {\n parts.push({ text: part.text });\n } else if (part.type === \"image\") {\n parts.push({ url: part.image, mediaType: \"image/*\" });\n }\n }\n }\n\n const a2aMsg: A2AMessage = {\n messageId: message.id,\n role: \"user\",\n parts,\n };\n\n if (this.contextId) {\n a2aMsg.contextId = this.contextId;\n }\n // Only attach taskId if current task is NOT in terminal state\n if (\n this.currentTask?.id &&\n !isTerminalTaskState(this.currentTask.status.state)\n ) {\n a2aMsg.taskId = this.currentTask.id;\n }\n\n return a2aMsg;\n }\n\n private insertAssistantPlaceholder(): string {\n const id = generateId();\n const assistant: ThreadAssistantMessage = {\n id,\n role: \"assistant\",\n createdAt: new Date(),\n status: { type: \"running\" },\n content: [],\n metadata: {\n unstable_state: null,\n unstable_annotations: [],\n unstable_data: [],\n steps: [],\n custom: {},\n },\n };\n this.messages = [...this.messages, assistant];\n this.notifyUpdate();\n return id;\n }\n\n private updateAssistantContent(\n messageId: string,\n content: ThreadAssistantMessage[\"content\"],\n ) {\n this.messages = this.messages.map((message) => {\n if (message.id !== messageId || message.role !== \"assistant\")\n return message;\n return { ...message, content };\n });\n }\n\n private updateAssistantStatus(messageId: string, status: MessageStatus) {\n let touched = false;\n this.messages = this.messages.map((message) => {\n if (message.id !== messageId || message.role !== \"assistant\")\n return message;\n touched = true;\n return { ...message, status };\n });\n if (touched) {\n this.notifyUpdate();\n if (status.type === \"complete\" || status.type === \"incomplete\") {\n this.persistAssistantHistory(messageId);\n }\n }\n }\n\n private getAssistantStatus(messageId: string): MessageStatus | undefined {\n const msg = this.messages.find(\n (m) => m.id === messageId && m.role === \"assistant\",\n );\n return msg?.status;\n }\n\n // --- Lifecycle helpers ---\n\n private setRunning(running: boolean) {\n this.isRunningFlag = running;\n this.notifyUpdate();\n }\n\n private finishRun(controller: AbortController | null) {\n if (this.abortController === controller) {\n this.abortController = null;\n }\n this.setRunning(false);\n }\n\n private resetHead(parentId: string | null | undefined) {\n if (!parentId) {\n if (this.messages.length) {\n this.messages = [];\n }\n return;\n }\n const idx = this.messages.findIndex((message) => message.id === parentId);\n if (idx === -1) return;\n this.messages = this.messages.slice(0, idx + 1);\n }\n\n // --- History persistence ---\n\n private recordHistoryEntry(parentId: string | null, message: ThreadMessage) {\n this.appendHistoryItem(parentId, message);\n }\n\n private markPendingAssistantHistory(\n messageId: string,\n parentId: string | null,\n ) {\n if (!this.history) return;\n this.assistantHistoryParents.set(messageId, parentId);\n }\n\n private persistAssistantHistory(messageId: string) {\n if (!this.history) return;\n const parentId = this.assistantHistoryParents.get(messageId);\n if (parentId === undefined) return;\n const message = this.messages.find((m) => m.id === messageId);\n if (!message || message.role !== \"assistant\") return;\n if (\n message.status?.type !== \"complete\" &&\n message.status?.type !== \"incomplete\"\n )\n return;\n this.assistantHistoryParents.delete(messageId);\n this.appendHistoryItem(parentId, message);\n }\n\n private appendHistoryItem(parentId: string | null, message: ThreadMessage) {\n if (!this.history || this.recordedHistoryIds.has(message.id)) return;\n this.recordedHistoryIds.add(message.id);\n void this.history.append({ parentId, message }).catch(() => {\n this.recordedHistoryIds.delete(message.id);\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"A2AThreadRuntimeCore.js","names":[],"sources":["../src/A2AThreadRuntimeCore.ts"],"sourcesContent":["\"use client\";\n\nimport { generateId, fromThreadMessageLike } from \"@assistant-ui/core\";\nimport type {\n AppendMessage,\n AssistantRuntime,\n ExportedMessageRepository,\n MessageStatus,\n ThreadAssistantMessage,\n ThreadHistoryAdapter,\n ThreadMessage,\n} from \"@assistant-ui/core\";\nimport { MessageRepository } from \"@assistant-ui/core/internal\";\nimport type { A2AClient } from \"./A2AClient\";\nimport type {\n A2AArtifact,\n A2AAgentCard,\n A2AMessage,\n A2APart,\n A2ASendMessageConfiguration,\n A2AStreamEvent,\n A2ATask,\n A2ATaskArtifactUpdateEvent,\n A2ATaskStatusUpdateEvent,\n} from \"./types\";\nimport {\n a2aMessageToContent,\n contentPartsToA2AParts,\n isTerminalTaskState,\n taskStateToMessageStatus,\n} from \"./conversions\";\n\nexport type A2AThreadRuntimeCoreOptions = {\n client: A2AClient;\n contextId?: string | undefined;\n configuration?: A2ASendMessageConfiguration | undefined;\n onError?: ((error: Error) => void) | undefined;\n onCancel?: (() => void) | undefined;\n onArtifactComplete?: ((artifact: A2AArtifact) => void) | undefined;\n history?: ThreadHistoryAdapter | undefined;\n notifyUpdate: () => void;\n};\n\nconst FALLBACK_USER_STATUS = {\n type: \"complete\",\n reason: \"unknown\",\n} as const;\n\nexport class A2AThreadRuntimeCore {\n private client: A2AClient;\n private contextId: string | undefined;\n private configuration: A2ASendMessageConfiguration | undefined;\n private onError: ((error: Error) => void) | undefined;\n private onCancel: (() => void) | undefined;\n private onArtifactComplete: ((artifact: A2AArtifact) => void) | undefined;\n private history: ThreadHistoryAdapter | undefined;\n private readonly notifyUpdate: () => void;\n\n private runtime: AssistantRuntime | undefined;\n private readonly repository = new MessageRepository();\n private exportedRepository: ExportedMessageRepository | undefined;\n private isRunningFlag = false;\n private abortController: AbortController | null = null;\n private pendingError: Error | null = null;\n\n // A2A-specific state\n private currentTask: A2ATask | undefined;\n private currentArtifacts: A2AArtifact[] = [];\n private agentCardValue: A2AAgentCard | undefined;\n\n // History tracking\n private readonly assistantHistoryParents = new Map<string, string | null>();\n private readonly recordedHistoryIds = new Set<string>();\n private _isLoading = false;\n private _loadPromise: Promise<void> | undefined;\n\n constructor(options: A2AThreadRuntimeCoreOptions) {\n this.client = options.client;\n this.contextId = options.contextId;\n this.configuration = options.configuration;\n this.onError = options.onError;\n this.onCancel = options.onCancel;\n this.onArtifactComplete = options.onArtifactComplete;\n this.history = options.history;\n this.notifyUpdate = options.notifyUpdate;\n }\n\n updateOptions(options: Omit<A2AThreadRuntimeCoreOptions, \"notifyUpdate\">) {\n this.client = options.client;\n this.contextId = options.contextId;\n this.configuration = options.configuration;\n this.onError = options.onError;\n this.onCancel = options.onCancel;\n this.onArtifactComplete = options.onArtifactComplete;\n this.history = options.history;\n }\n\n attachRuntime(runtime: AssistantRuntime) {\n this.runtime = runtime;\n }\n\n detachRuntime() {\n this.runtime = undefined;\n // Abort in-flight requests on unmount\n if (this.abortController) {\n this.abortController.abort();\n this.abortController = null;\n }\n }\n\n getRuntime(): AssistantRuntime | undefined {\n return this.runtime;\n }\n\n getMessages(): readonly ThreadMessage[] {\n return this.repository.getMessages();\n }\n\n getMessageRepository(): ExportedMessageRepository {\n this.exportedRepository ??= this.repository.export();\n return this.exportedRepository;\n }\n\n private tryGetMessage(messageId: string) {\n try {\n return this.repository.getMessage(messageId);\n } catch {\n return undefined;\n }\n }\n\n private tryGetMessages(\n messageId: string,\n ): readonly ThreadMessage[] | undefined {\n try {\n return this.repository.getMessages(messageId);\n } catch {\n return undefined;\n }\n }\n\n private hasMessage(messageId: string): boolean {\n return this.tryGetMessage(messageId) !== undefined;\n }\n\n private addOrUpdateMessage(\n parentId: string | null,\n message: ThreadMessage,\n ): void {\n this.repository.addOrUpdateMessage(parentId, message);\n this.exportedRepository = undefined;\n }\n\n private switchToBranch(messageId: string): void {\n this.repository.switchToBranch(messageId);\n this.exportedRepository = undefined;\n }\n\n private resetRepositoryHead(messageId: string | null): void {\n this.repository.resetHead(messageId);\n this.exportedRepository = undefined;\n }\n\n private clearRepository(): void {\n this.repository.clear();\n this.exportedRepository = undefined;\n }\n\n private updateMessage(\n messageId: string,\n updater: (message: ThreadMessage) => ThreadMessage,\n ): boolean {\n const item = this.tryGetMessage(messageId);\n if (!item) return false;\n const message = updater(item.message);\n if (message === item.message) return false;\n this.addOrUpdateMessage(item.parentId, message);\n return true;\n }\n\n getTask(): A2ATask | undefined {\n return this.currentTask;\n }\n\n getArtifacts(): readonly A2AArtifact[] {\n return this.currentArtifacts;\n }\n\n getAgentCard(): A2AAgentCard | undefined {\n return this.agentCardValue;\n }\n\n isRunning(): boolean {\n return this.isRunningFlag;\n }\n\n get isLoading(): boolean {\n return this._isLoading;\n }\n\n __internal_load(): Promise<void> {\n if (this._loadPromise) return this._loadPromise;\n\n this._isLoading = true;\n\n const historyPromise = this.history?.load() ?? Promise.resolve(null);\n const agentCardPromise = this.client.getAgentCard().catch(() => undefined);\n\n this._loadPromise = Promise.all([historyPromise, agentCardPromise])\n .then(([repo, agentCard]) => {\n if (agentCard) {\n this.agentCardValue = agentCard;\n }\n if (repo) {\n this.applyExternalMessageRepository(repo);\n }\n })\n .catch((error) => {\n this.onError?.(\n error instanceof Error ? error : new Error(String(error)),\n );\n })\n .finally(() => {\n this._isLoading = false;\n this.notifyUpdate();\n });\n\n this.notifyUpdate();\n return this._loadPromise;\n }\n\n async append(message: AppendMessage): Promise<void> {\n const startRun = message.startRun ?? message.role === \"user\";\n\n const threadMessage = fromThreadMessageLike(\n message as any,\n generateId(),\n FALLBACK_USER_STATUS,\n );\n const parentId =\n message.parentId === null\n ? null\n : message.parentId && this.hasMessage(message.parentId)\n ? message.parentId\n : this.repository.headId;\n this.addOrUpdateMessage(parentId, threadMessage);\n this.switchToBranch(threadMessage.id);\n this.notifyUpdate();\n this.recordHistoryEntry(parentId, threadMessage);\n\n if (!startRun) return;\n await this.startRun(threadMessage);\n }\n\n async edit(message: AppendMessage): Promise<void> {\n await this.append(message);\n }\n\n async reload(\n parentId: string | null,\n _config: { runConfig?: Record<string, unknown> } = {},\n ): Promise<void> {\n const messages =\n parentId === null\n ? []\n : (this.tryGetMessages(parentId) ?? this.getMessages());\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i]!.role === \"user\") {\n await this.startRun(messages[i]!);\n return;\n }\n }\n }\n\n async cancel(): Promise<void> {\n if (!this.abortController) return;\n\n // Abort locally first so the stream stops immediately\n this.abortController.abort();\n\n // Then try to cancel the task on the server\n if (this.currentTask?.id) {\n try {\n const updated = await this.client.cancelTask(this.currentTask.id);\n this.currentTask = updated;\n } catch {\n // Server cancel failed; local abort already handled\n }\n }\n }\n\n private appendLinearChain(messages: readonly ThreadMessage[]): string | null {\n let parentId: string | null = null;\n let lastId: string | null = null;\n const seen = new Set<string>();\n\n for (const message of messages) {\n if (seen.has(message.id)) continue;\n seen.add(message.id);\n this.addOrUpdateMessage(parentId, message);\n parentId = message.id;\n lastId = message.id;\n }\n\n return lastId;\n }\n\n private finalizeExternalApply(): void {\n this.assistantHistoryParents.clear();\n this.recordedHistoryIds.clear();\n for (const { message } of this.getMessageRepository().messages) {\n this.recordedHistoryIds.add(message.id);\n }\n this.currentTask = undefined;\n this.currentArtifacts = [];\n this.notifyUpdate();\n }\n\n applyExternalMessages(messages: readonly ThreadMessage[]): void {\n if (messages.length === 0) {\n this.clearRepository();\n } else {\n let expectedParentId: string | null = null;\n let lastAppliedId: string | null = null;\n let hardReplace = false;\n const seen = new Set<string>();\n\n for (const message of messages) {\n if (seen.has(message.id)) continue;\n seen.add(message.id);\n const existing = this.tryGetMessage(message.id);\n if (existing && existing.parentId !== expectedParentId) {\n hardReplace = true;\n break;\n }\n this.addOrUpdateMessage(expectedParentId, message);\n expectedParentId = message.id;\n lastAppliedId = message.id;\n }\n\n if (hardReplace) {\n this.clearRepository();\n lastAppliedId = this.appendLinearChain(messages);\n }\n\n this.resetRepositoryHead(lastAppliedId);\n }\n\n this.finalizeExternalApply();\n }\n\n private applyExternalMessageRepository(\n loaded: ExportedMessageRepository,\n ): void {\n const headId = loaded.headId ?? loaded.messages.at(-1)?.message.id ?? null;\n const ids = new Set<string>();\n let degenerate = false;\n for (const { message } of loaded.messages) {\n if (ids.has(message.id)) {\n degenerate = true;\n break;\n }\n ids.add(message.id);\n }\n if (headId !== null && !ids.has(headId)) degenerate = true;\n\n if (!degenerate) {\n this.clearRepository();\n let pending = [...loaded.messages];\n const importedIds = new Set<string>();\n\n while (pending.length > 0) {\n const unresolved: typeof pending = [];\n let progressed = false;\n for (const item of pending) {\n if (item.parentId !== null && !importedIds.has(item.parentId)) {\n unresolved.push(item);\n continue;\n }\n this.addOrUpdateMessage(item.parentId, item.message);\n importedIds.add(item.message.id);\n progressed = true;\n }\n if (!progressed) {\n degenerate = true;\n break;\n }\n pending = unresolved;\n }\n }\n\n if (degenerate) {\n this.clearRepository();\n let previousId: string | null = null;\n for (const { message } of loaded.messages) {\n const existing = this.tryGetMessage(message.id);\n this.addOrUpdateMessage(\n existing ? existing.parentId : previousId,\n message,\n );\n previousId = message.id;\n }\n this.resetRepositoryHead(previousId);\n } else {\n this.resetRepositoryHead(headId);\n }\n\n this.finalizeExternalApply();\n }\n\n // --- Run logic ---\n\n private async startRun(userThreadMessage: ThreadMessage): Promise<void> {\n // Cancel any in-progress run before starting a new one\n if (this.abortController) {\n this.abortController.abort();\n this.abortController = null;\n }\n\n const a2aMessage = this.threadMessageToA2AMessage(userThreadMessage);\n\n // Clear task if previous task reached terminal state\n if (\n this.currentTask &&\n isTerminalTaskState(this.currentTask.status.state)\n ) {\n this.currentTask = undefined;\n }\n\n this.currentArtifacts = [];\n\n const assistantParentId = userThreadMessage.id;\n const assistantId = this.insertAssistantPlaceholder(assistantParentId);\n this.markPendingAssistantHistory(assistantId, assistantParentId);\n\n const abortController = new AbortController();\n this.abortController = abortController;\n\n abortController.signal.addEventListener(\n \"abort\",\n () => {\n this.updateAssistantStatus(assistantId, {\n type: \"incomplete\",\n reason: \"cancelled\",\n });\n this.finishRun(abortController);\n this.onCancel?.();\n },\n { once: true },\n );\n\n this.setRunning(true);\n\n // Check if agent supports streaming; fall back to sync sendMessage if not\n const supportsStreaming =\n this.agentCardValue?.capabilities?.streaming !== false;\n\n try {\n if (supportsStreaming) {\n await this.runStreaming(a2aMessage, assistantId, abortController);\n } else {\n await this.runSync(a2aMessage, assistantId, abortController);\n }\n } catch (error) {\n if (!abortController.signal.aborted) {\n const err = error instanceof Error ? error : new Error(String(error));\n this.updateAssistantStatus(assistantId, {\n type: \"incomplete\",\n reason: \"error\",\n });\n this.onError?.(err);\n this.pendingError = this.pendingError ?? err;\n }\n } finally {\n this.finishRun(abortController);\n }\n\n if (this.pendingError) {\n const err = this.pendingError;\n this.pendingError = null;\n throw err;\n }\n }\n\n private async runStreaming(\n a2aMessage: A2AMessage,\n assistantId: string,\n abortController: AbortController,\n ): Promise<void> {\n const stream = this.client.streamMessage(\n a2aMessage,\n this.configuration,\n undefined, // metadata\n abortController.signal,\n );\n\n for await (const event of stream) {\n if (abortController.signal.aborted) break;\n this.handleStreamEvent(assistantId, event);\n }\n\n if (!abortController.signal.aborted) {\n const lastStatus = this.getAssistantStatus(assistantId);\n if (lastStatus?.type === \"running\") {\n this.updateAssistantStatus(assistantId, {\n type: \"complete\",\n reason: \"stop\",\n });\n }\n }\n }\n\n private async runSync(\n a2aMessage: A2AMessage,\n assistantId: string,\n abortController: AbortController,\n ): Promise<void> {\n const result = await this.client.sendMessage(\n a2aMessage,\n this.configuration,\n undefined, // metadata\n abortController.signal,\n );\n\n if (abortController.signal.aborted) return;\n\n // Result is either A2ATask or A2AMessage\n if (\"id\" in result && \"status\" in result) {\n // It's a Task\n this.handleTaskSnapshot(assistantId, result as A2ATask);\n } else if (\"messageId\" in result && \"parts\" in result) {\n // It's a Message\n this.handleMessage(assistantId, result as A2AMessage);\n this.updateAssistantStatus(assistantId, {\n type: \"complete\",\n reason: \"stop\",\n });\n }\n }\n\n private handleStreamEvent(assistantId: string, event: A2AStreamEvent) {\n switch (event.type) {\n case \"statusUpdate\":\n this.handleStatusUpdate(assistantId, event.event);\n break;\n case \"artifactUpdate\":\n this.handleArtifactUpdate(event.event);\n break;\n case \"message\":\n this.handleMessage(assistantId, event.message);\n break;\n case \"task\":\n this.handleTaskSnapshot(assistantId, event.task);\n break;\n }\n }\n\n private handleStatusUpdate(\n assistantId: string,\n event: A2ATaskStatusUpdateEvent,\n ) {\n if (!this.currentTask) {\n this.currentTask = {\n id: event.taskId,\n contextId: event.contextId,\n status: event.status,\n };\n } else {\n this.currentTask = { ...this.currentTask, status: event.status };\n }\n\n if (event.contextId) {\n this.contextId = event.contextId;\n }\n\n if (event.status.message) {\n const content = a2aMessageToContent(event.status.message);\n this.updateAssistantContent(assistantId, content);\n }\n\n const status = taskStateToMessageStatus(event.status.state);\n this.updateAssistantStatus(assistantId, status);\n\n this.notifyUpdate();\n }\n\n private handleArtifactUpdate(event: A2ATaskArtifactUpdateEvent) {\n const { artifact, append, lastChunk } = event;\n const existingIdx = this.currentArtifacts.findIndex(\n (a) => a.artifactId === artifact.artifactId,\n );\n\n let updated: A2AArtifact;\n if (existingIdx >= 0 && append) {\n const existing = this.currentArtifacts[existingIdx]!;\n updated = {\n ...existing,\n parts: [...existing.parts, ...artifact.parts],\n };\n this.currentArtifacts = [\n ...this.currentArtifacts.slice(0, existingIdx),\n updated,\n ...this.currentArtifacts.slice(existingIdx + 1),\n ];\n } else if (existingIdx >= 0) {\n updated = artifact;\n this.currentArtifacts = [\n ...this.currentArtifacts.slice(0, existingIdx),\n updated,\n ...this.currentArtifacts.slice(existingIdx + 1),\n ];\n } else {\n updated = artifact;\n this.currentArtifacts = [...this.currentArtifacts, updated];\n }\n\n if (lastChunk) {\n this.onArtifactComplete?.(updated);\n }\n\n this.notifyUpdate();\n }\n\n private handleMessage(assistantId: string, message: A2AMessage) {\n if (message.role !== \"agent\") return;\n\n const content = a2aMessageToContent(message);\n this.updateAssistantContent(assistantId, content);\n this.notifyUpdate();\n }\n\n private handleTaskSnapshot(assistantId: string, task: A2ATask) {\n this.currentTask = task;\n\n if (task.contextId) {\n this.contextId = task.contextId;\n }\n if (task.artifacts) {\n this.currentArtifacts = task.artifacts;\n }\n\n if (task.status.message) {\n const content = a2aMessageToContent(task.status.message);\n this.updateAssistantContent(assistantId, content);\n }\n\n const status = taskStateToMessageStatus(task.status.state);\n this.updateAssistantStatus(assistantId, status);\n\n this.notifyUpdate();\n }\n\n // --- Message helpers ---\n\n private threadMessageToA2AMessage(message: ThreadMessage): A2AMessage {\n const parts: A2APart[] = [];\n\n if (message.role === \"user\") {\n parts.push(...contentPartsToA2AParts(message.content));\n for (const attachment of message.attachments ?? []) {\n parts.push(\n ...contentPartsToA2AParts(\n attachment.content ?? [],\n attachment.contentType,\n ),\n );\n }\n }\n\n const a2aMsg: A2AMessage = {\n messageId: message.id,\n role: \"user\",\n parts,\n };\n\n if (this.contextId) {\n a2aMsg.contextId = this.contextId;\n }\n // Only attach taskId if current task is NOT in terminal state\n if (\n this.currentTask?.id &&\n !isTerminalTaskState(this.currentTask.status.state)\n ) {\n a2aMsg.taskId = this.currentTask.id;\n }\n\n return a2aMsg;\n }\n\n private insertAssistantPlaceholder(parentId: string): string {\n const id = generateId();\n const assistant: ThreadAssistantMessage = {\n id,\n role: \"assistant\",\n createdAt: new Date(),\n status: { type: \"running\" },\n content: [],\n metadata: {\n unstable_state: null,\n unstable_annotations: [],\n unstable_data: [],\n steps: [],\n custom: {},\n },\n };\n this.addOrUpdateMessage(parentId, assistant);\n this.switchToBranch(id);\n this.notifyUpdate();\n return id;\n }\n\n private updateAssistantContent(\n messageId: string,\n content: ThreadAssistantMessage[\"content\"],\n ) {\n this.updateMessage(messageId, (message) => {\n if (message.role !== \"assistant\") return message;\n return { ...message, content };\n });\n }\n\n private updateAssistantStatus(messageId: string, status: MessageStatus) {\n const touched = this.updateMessage(messageId, (message) => {\n if (message.role !== \"assistant\") return message;\n return { ...message, status };\n });\n if (touched) {\n this.notifyUpdate();\n if (status.type === \"complete\" || status.type === \"incomplete\") {\n this.persistAssistantHistory(messageId);\n }\n }\n }\n\n private getAssistantStatus(messageId: string): MessageStatus | undefined {\n const msg = this.tryGetMessage(messageId)?.message;\n if (msg?.role !== \"assistant\") return undefined;\n return msg.status;\n }\n\n // --- Lifecycle helpers ---\n\n private setRunning(running: boolean) {\n this.isRunningFlag = running;\n this.notifyUpdate();\n }\n\n private finishRun(controller: AbortController | null) {\n if (this.abortController === controller) {\n this.abortController = null;\n }\n this.setRunning(false);\n }\n\n // --- History persistence ---\n\n private recordHistoryEntry(parentId: string | null, message: ThreadMessage) {\n this.appendHistoryItem(parentId, message);\n }\n\n private markPendingAssistantHistory(\n messageId: string,\n parentId: string | null,\n ) {\n if (!this.history) return;\n this.assistantHistoryParents.set(messageId, parentId);\n }\n\n private persistAssistantHistory(messageId: string) {\n if (!this.history) return;\n const parentId = this.assistantHistoryParents.get(messageId);\n if (parentId === undefined) return;\n const message = this.tryGetMessage(messageId)?.message;\n if (!message || message.role !== \"assistant\") return;\n if (\n message.status?.type !== \"complete\" &&\n message.status?.type !== \"incomplete\"\n )\n return;\n this.assistantHistoryParents.delete(messageId);\n this.appendHistoryItem(parentId, message);\n }\n\n private appendHistoryItem(parentId: string | null, message: ThreadMessage) {\n if (!this.history || this.recordedHistoryIds.has(message.id)) return;\n this.recordedHistoryIds.add(message.id);\n void this.history.append({ parentId, message }).catch(() => {\n this.recordedHistoryIds.delete(message.id);\n });\n }\n}\n"],"mappings":"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"}
|
package/dist/a2aExtras.d.ts
CHANGED
package/dist/a2aExtras.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"a2aExtras.d.ts","names":[],"sources":["../src/a2aExtras.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"a2aExtras.d.ts","names":[],"sources":["../src/a2aExtras.ts"],"mappings":";;cAGa,iDAAS,cAAA"}
|
package/dist/conversions.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { A2AMessage, A2APart, A2ATaskState } from "./types.js";
|
|
2
2
|
import { MessageStatus, ThreadAssistantMessage } from "@assistant-ui/core";
|
|
3
|
-
|
|
4
3
|
//#region src/conversions.d.ts
|
|
5
4
|
declare function a2aPartToContent(part: A2APart): ThreadAssistantMessage["content"][number];
|
|
6
5
|
declare function a2aPartsToContent(parts: A2APart[]): ThreadAssistantMessage["content"];
|
|
@@ -9,9 +8,12 @@ declare function isInterruptedTaskState(state: A2ATaskState): boolean;
|
|
|
9
8
|
declare function taskStateToMessageStatus(state: A2ATaskState): MessageStatus;
|
|
10
9
|
declare function contentPartsToA2AParts(content: ReadonlyArray<{
|
|
11
10
|
type: string;
|
|
12
|
-
text?: string;
|
|
13
|
-
image?: string;
|
|
14
|
-
|
|
11
|
+
text?: string | undefined;
|
|
12
|
+
image?: string | undefined;
|
|
13
|
+
data?: string | undefined;
|
|
14
|
+
mimeType?: string | undefined;
|
|
15
|
+
filename?: string | undefined;
|
|
16
|
+
}>, fallbackMimeType?: string): A2APart[];
|
|
15
17
|
declare function a2aMessageToContent(message: A2AMessage): ThreadAssistantMessage["content"];
|
|
16
18
|
//#endregion
|
|
17
19
|
export { a2aMessageToContent, a2aPartToContent, a2aPartsToContent, contentPartsToA2AParts, isInterruptedTaskState, isTerminalTaskState, taskStateToMessageStatus };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversions.d.ts","names":[],"sources":["../src/conversions.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"conversions.d.ts","names":[],"sources":["../src/conversions.ts"],"mappings":";;;iBAUgB,iBACd,MAAM,UACL;iBA+Ba,kBACd,OAAO,YACN;iBAgBa,oBAAoB,OAAO;iBAI3B,uBAAuB,OAAO;iBAI9B,yBAAyB,OAAO,eAAe;iBAoB/C,uBACd,SAAS;EACP;EACA;EACA;EACA;EACA;EACA;IAEF,4BACC;iBAqDa,oBACd,SAAS,aACR"}
|
package/dist/conversions.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { httpUrlPattern, parseDataUrl } from "@assistant-ui/core/internal";
|
|
2
3
|
//#region src/conversions.ts
|
|
3
4
|
function isImageMediaType(mediaType) {
|
|
4
5
|
return !!mediaType && mediaType.startsWith("image/");
|
|
@@ -78,16 +79,44 @@ function taskStateToMessageStatus(state) {
|
|
|
78
79
|
default: return { type: "running" };
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
|
-
function contentPartsToA2AParts(content) {
|
|
82
|
+
function contentPartsToA2AParts(content, fallbackMimeType) {
|
|
82
83
|
return content.map((part) => {
|
|
83
84
|
switch (part.type) {
|
|
84
85
|
case "text": return { text: part.text ?? "" };
|
|
85
|
-
case "image":
|
|
86
|
+
case "image": {
|
|
86
87
|
if (!part.image) return null;
|
|
88
|
+
const parsed = parseDataUrl(part.image);
|
|
89
|
+
if (parsed) return {
|
|
90
|
+
raw: parsed.data,
|
|
91
|
+
mediaType: parsed.mimeType,
|
|
92
|
+
...part.filename && { filename: part.filename }
|
|
93
|
+
};
|
|
87
94
|
return {
|
|
88
95
|
url: part.image,
|
|
89
|
-
mediaType:
|
|
96
|
+
...fallbackMimeType && { mediaType: fallbackMimeType },
|
|
97
|
+
...part.filename && { filename: part.filename }
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
case "file": {
|
|
101
|
+
if (!part.data) return null;
|
|
102
|
+
const declaredMimeType = part.mimeType || fallbackMimeType;
|
|
103
|
+
if (httpUrlPattern.test(part.data)) return {
|
|
104
|
+
url: part.data,
|
|
105
|
+
...declaredMimeType && { mediaType: declaredMimeType },
|
|
106
|
+
...part.filename && { filename: part.filename }
|
|
107
|
+
};
|
|
108
|
+
const parsed = parseDataUrl(part.data);
|
|
109
|
+
if (parsed) return {
|
|
110
|
+
raw: parsed.data,
|
|
111
|
+
mediaType: parsed.mimeType,
|
|
112
|
+
...part.filename && { filename: part.filename }
|
|
113
|
+
};
|
|
114
|
+
return {
|
|
115
|
+
raw: part.data,
|
|
116
|
+
...declaredMimeType && { mediaType: declaredMimeType },
|
|
117
|
+
...part.filename && { filename: part.filename }
|
|
90
118
|
};
|
|
119
|
+
}
|
|
91
120
|
default: return null;
|
|
92
121
|
}
|
|
93
122
|
}).filter((p) => p !== null);
|
package/dist/conversions.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversions.js","names":[],"sources":["../src/conversions.ts"],"sourcesContent":["\"use client\";\n\nimport type { MessageStatus, ThreadAssistantMessage } from \"@assistant-ui/core\";\nimport type { A2AMessage, A2APart, A2ATaskState } from \"./types\";\n\nfunction isImageMediaType(mediaType?: string): boolean {\n return !!mediaType && mediaType.startsWith(\"image/\");\n}\n\nexport function a2aPartToContent(\n part: A2APart,\n): ThreadAssistantMessage[\"content\"][number] {\n if (part.text !== undefined) {\n return { type: \"text\", text: part.text };\n }\n if (part.url !== undefined) {\n if (isImageMediaType(part.mediaType)) {\n return { type: \"image\", image: part.url };\n }\n return {\n type: \"text\",\n text: part.filename ? `[${part.filename}](${part.url})` : part.url,\n };\n }\n if (part.raw !== undefined) {\n if (isImageMediaType(part.mediaType)) {\n return {\n type: \"image\",\n image: `data:${part.mediaType};base64,${part.raw}`,\n };\n }\n return {\n type: \"text\",\n text: `[File: ${part.filename ?? \"download\"}]`,\n };\n }\n if (part.data !== undefined) {\n return { type: \"text\", text: JSON.stringify(part.data, null, 2) };\n }\n return { type: \"text\", text: \"\" };\n}\n\nexport function a2aPartsToContent(\n parts: A2APart[],\n): ThreadAssistantMessage[\"content\"] {\n return parts.map(a2aPartToContent);\n}\n\nconst TERMINAL_STATES = new Set<A2ATaskState>([\n \"completed\",\n \"failed\",\n \"canceled\",\n \"rejected\",\n]);\n\nconst INTERRUPTED_STATES = new Set<A2ATaskState>([\n \"input_required\",\n \"auth_required\",\n]);\n\nexport function isTerminalTaskState(state: A2ATaskState): boolean {\n return TERMINAL_STATES.has(state);\n}\n\nexport function isInterruptedTaskState(state: A2ATaskState): boolean {\n return INTERRUPTED_STATES.has(state);\n}\n\nexport function taskStateToMessageStatus(state: A2ATaskState): MessageStatus {\n switch (state) {\n case \"submitted\":\n case \"working\":\n return { type: \"running\" };\n case \"completed\":\n return { type: \"complete\", reason: \"stop\" };\n case \"failed\":\n case \"rejected\":\n return { type: \"incomplete\", reason: \"error\" };\n case \"canceled\":\n return { type: \"incomplete\", reason: \"cancelled\" };\n case \"input_required\":\n case \"auth_required\":\n return { type: \"requires-action\", reason: \"interrupt\" };\n default:\n return { type: \"running\" };\n }\n}\n\nexport function contentPartsToA2AParts(\n content: ReadonlyArray<{\n type: string;\n text?: string;\n image?: string;\n }>,\n): A2APart[] {\n return content\n .map((part): A2APart | null => {\n switch (part.type) {\n case \"text\":\n return { text: part.text ?? \"\" };\n case \"image\"
|
|
1
|
+
{"version":3,"file":"conversions.js","names":[],"sources":["../src/conversions.ts"],"sourcesContent":["\"use client\";\n\nimport type { MessageStatus, ThreadAssistantMessage } from \"@assistant-ui/core\";\nimport { httpUrlPattern, parseDataUrl } from \"@assistant-ui/core/internal\";\nimport type { A2AMessage, A2APart, A2ATaskState } from \"./types\";\n\nfunction isImageMediaType(mediaType?: string): boolean {\n return !!mediaType && mediaType.startsWith(\"image/\");\n}\n\nexport function a2aPartToContent(\n part: A2APart,\n): ThreadAssistantMessage[\"content\"][number] {\n if (part.text !== undefined) {\n return { type: \"text\", text: part.text };\n }\n if (part.url !== undefined) {\n if (isImageMediaType(part.mediaType)) {\n return { type: \"image\", image: part.url };\n }\n return {\n type: \"text\",\n text: part.filename ? `[${part.filename}](${part.url})` : part.url,\n };\n }\n if (part.raw !== undefined) {\n if (isImageMediaType(part.mediaType)) {\n return {\n type: \"image\",\n image: `data:${part.mediaType};base64,${part.raw}`,\n };\n }\n return {\n type: \"text\",\n text: `[File: ${part.filename ?? \"download\"}]`,\n };\n }\n if (part.data !== undefined) {\n return { type: \"text\", text: JSON.stringify(part.data, null, 2) };\n }\n return { type: \"text\", text: \"\" };\n}\n\nexport function a2aPartsToContent(\n parts: A2APart[],\n): ThreadAssistantMessage[\"content\"] {\n return parts.map(a2aPartToContent);\n}\n\nconst TERMINAL_STATES = new Set<A2ATaskState>([\n \"completed\",\n \"failed\",\n \"canceled\",\n \"rejected\",\n]);\n\nconst INTERRUPTED_STATES = new Set<A2ATaskState>([\n \"input_required\",\n \"auth_required\",\n]);\n\nexport function isTerminalTaskState(state: A2ATaskState): boolean {\n return TERMINAL_STATES.has(state);\n}\n\nexport function isInterruptedTaskState(state: A2ATaskState): boolean {\n return INTERRUPTED_STATES.has(state);\n}\n\nexport function taskStateToMessageStatus(state: A2ATaskState): MessageStatus {\n switch (state) {\n case \"submitted\":\n case \"working\":\n return { type: \"running\" };\n case \"completed\":\n return { type: \"complete\", reason: \"stop\" };\n case \"failed\":\n case \"rejected\":\n return { type: \"incomplete\", reason: \"error\" };\n case \"canceled\":\n return { type: \"incomplete\", reason: \"cancelled\" };\n case \"input_required\":\n case \"auth_required\":\n return { type: \"requires-action\", reason: \"interrupt\" };\n default:\n return { type: \"running\" };\n }\n}\n\nexport function contentPartsToA2AParts(\n content: ReadonlyArray<{\n type: string;\n text?: string | undefined;\n image?: string | undefined;\n data?: string | undefined;\n mimeType?: string | undefined;\n filename?: string | undefined;\n }>,\n fallbackMimeType?: string,\n): A2APart[] {\n return content\n .map((part): A2APart | null => {\n switch (part.type) {\n case \"text\":\n return { text: part.text ?? \"\" };\n case \"image\": {\n if (!part.image) return null;\n const parsed = parseDataUrl(part.image);\n if (parsed) {\n return {\n raw: parsed.data,\n mediaType: parsed.mimeType,\n ...(part.filename && { filename: part.filename }),\n };\n }\n return {\n url: part.image,\n ...(fallbackMimeType && { mediaType: fallbackMimeType }),\n ...(part.filename && { filename: part.filename }),\n };\n }\n case \"file\": {\n if (!part.data) return null;\n const declaredMimeType = part.mimeType || fallbackMimeType;\n if (httpUrlPattern.test(part.data)) {\n return {\n url: part.data,\n ...(declaredMimeType && { mediaType: declaredMimeType }),\n ...(part.filename && { filename: part.filename }),\n };\n }\n const parsed = parseDataUrl(part.data);\n if (parsed) {\n return {\n raw: parsed.data,\n mediaType: parsed.mimeType,\n ...(part.filename && { filename: part.filename }),\n };\n }\n return {\n raw: part.data,\n ...(declaredMimeType && { mediaType: declaredMimeType }),\n ...(part.filename && { filename: part.filename }),\n };\n }\n default:\n return null;\n }\n })\n .filter((p): p is A2APart => p !== null);\n}\n\nexport function a2aMessageToContent(\n message: A2AMessage,\n): ThreadAssistantMessage[\"content\"] {\n return a2aPartsToContent(message.parts);\n}\n"],"mappings":";;;AAMA,SAAS,iBAAiB,WAA6B;CACrD,OAAO,CAAC,CAAC,aAAa,UAAU,WAAW,QAAQ;AACrD;AAEA,SAAgB,iBACd,MAC2C;CAC3C,IAAI,KAAK,SAAS,KAAA,GAChB,OAAO;EAAE,MAAM;EAAQ,MAAM,KAAK;CAAK;CAEzC,IAAI,KAAK,QAAQ,KAAA,GAAW;EAC1B,IAAI,iBAAiB,KAAK,SAAS,GACjC,OAAO;GAAE,MAAM;GAAS,OAAO,KAAK;EAAI;EAE1C,OAAO;GACL,MAAM;GACN,MAAM,KAAK,WAAW,IAAI,KAAK,SAAS,IAAI,KAAK,IAAI,KAAK,KAAK;EACjE;CACF;CACA,IAAI,KAAK,QAAQ,KAAA,GAAW;EAC1B,IAAI,iBAAiB,KAAK,SAAS,GACjC,OAAO;GACL,MAAM;GACN,OAAO,QAAQ,KAAK,UAAU,UAAU,KAAK;EAC/C;EAEF,OAAO;GACL,MAAM;GACN,MAAM,UAAU,KAAK,YAAY,WAAW;EAC9C;CACF;CACA,IAAI,KAAK,SAAS,KAAA,GAChB,OAAO;EAAE,MAAM;EAAQ,MAAM,KAAK,UAAU,KAAK,MAAM,MAAM,CAAC;CAAE;CAElE,OAAO;EAAE,MAAM;EAAQ,MAAM;CAAG;AAClC;AAEA,SAAgB,kBACd,OACmC;CACnC,OAAO,MAAM,IAAI,gBAAgB;AACnC;AAEA,MAAM,kCAAkB,IAAI,IAAkB;CAC5C;CACA;CACA;CACA;AACF,CAAC;AAED,MAAM,qCAAqB,IAAI,IAAkB,CAC/C,kBACA,eACF,CAAC;AAED,SAAgB,oBAAoB,OAA8B;CAChE,OAAO,gBAAgB,IAAI,KAAK;AAClC;AAEA,SAAgB,uBAAuB,OAA8B;CACnE,OAAO,mBAAmB,IAAI,KAAK;AACrC;AAEA,SAAgB,yBAAyB,OAAoC;CAC3E,QAAQ,OAAR;EACE,KAAK;EACL,KAAK,WACH,OAAO,EAAE,MAAM,UAAU;EAC3B,KAAK,aACH,OAAO;GAAE,MAAM;GAAY,QAAQ;EAAO;EAC5C,KAAK;EACL,KAAK,YACH,OAAO;GAAE,MAAM;GAAc,QAAQ;EAAQ;EAC/C,KAAK,YACH,OAAO;GAAE,MAAM;GAAc,QAAQ;EAAY;EACnD,KAAK;EACL,KAAK,iBACH,OAAO;GAAE,MAAM;GAAmB,QAAQ;EAAY;EACxD,SACE,OAAO,EAAE,MAAM,UAAU;CAC7B;AACF;AAEA,SAAgB,uBACd,SAQA,kBACW;CACX,OAAO,QACJ,KAAK,SAAyB;EAC7B,QAAQ,KAAK,MAAb;GACE,KAAK,QACH,OAAO,EAAE,MAAM,KAAK,QAAQ,GAAG;GACjC,KAAK,SAAS;IACZ,IAAI,CAAC,KAAK,OAAO,OAAO;IACxB,MAAM,SAAS,aAAa,KAAK,KAAK;IACtC,IAAI,QACF,OAAO;KACL,KAAK,OAAO;KACZ,WAAW,OAAO;KAClB,GAAI,KAAK,YAAY,EAAE,UAAU,KAAK,SAAS;IACjD;IAEF,OAAO;KACL,KAAK,KAAK;KACV,GAAI,oBAAoB,EAAE,WAAW,iBAAiB;KACtD,GAAI,KAAK,YAAY,EAAE,UAAU,KAAK,SAAS;IACjD;GACF;GACA,KAAK,QAAQ;IACX,IAAI,CAAC,KAAK,MAAM,OAAO;IACvB,MAAM,mBAAmB,KAAK,YAAY;IAC1C,IAAI,eAAe,KAAK,KAAK,IAAI,GAC/B,OAAO;KACL,KAAK,KAAK;KACV,GAAI,oBAAoB,EAAE,WAAW,iBAAiB;KACtD,GAAI,KAAK,YAAY,EAAE,UAAU,KAAK,SAAS;IACjD;IAEF,MAAM,SAAS,aAAa,KAAK,IAAI;IACrC,IAAI,QACF,OAAO;KACL,KAAK,OAAO;KACZ,WAAW,OAAO;KAClB,GAAI,KAAK,YAAY,EAAE,UAAU,KAAK,SAAS;IACjD;IAEF,OAAO;KACL,KAAK,KAAK;KACV,GAAI,oBAAoB,EAAE,WAAW,iBAAiB;KACtD,GAAI,KAAK,YAAY,EAAE,UAAU,KAAK,SAAS;IACjD;GACF;GACA,SACE,OAAO;EACX;CACF,CAAC,CAAC,CACD,QAAQ,MAAoB,MAAM,IAAI;AAC3C;AAEA,SAAgB,oBACd,SACmC;CACnC,OAAO,kBAAkB,QAAQ,KAAK;AACxC"}
|
package/dist/hooks.d.ts
CHANGED
package/dist/hooks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","names":[],"sources":["../src/hooks.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","names":[],"sources":["../src/hooks.ts"],"mappings":";;;cAKa,kBAAA;;cAGA,gCAAA;;cAGA,uBAAA"}
|