@assistant-ui/react-a2a 0.2.46 → 0.2.48
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/{A2AThreadRuntimeCore.d.ts → A2AThreadController.d.ts} +7 -17
- package/dist/A2AThreadController.d.ts.map +1 -0
- package/dist/{A2AThreadRuntimeCore.js → A2AThreadController.js} +79 -152
- package/dist/A2AThreadController.js.map +1 -0
- package/dist/a2aThreadState.d.ts +56 -0
- package/dist/a2aThreadState.d.ts.map +1 -0
- package/dist/a2aThreadState.js +152 -0
- package/dist/a2aThreadState.js.map +1 -0
- package/dist/useA2ARuntime.js +2 -2
- package/dist/useA2ARuntime.js.map +1 -1
- package/package.json +10 -10
- package/src/{A2AThreadRuntimeCore.test.ts → A2AThreadController.test.ts} +48 -20
- package/src/{A2AThreadRuntimeCore.ts → A2AThreadController.ts} +93 -220
- package/src/a2aThreadState.test.ts +244 -0
- package/src/a2aThreadState.ts +242 -0
- package/src/useA2ARuntime.fast-refresh.test.tsx +6 -6
- package/src/useA2ARuntime.ts +2 -2
- package/dist/A2AThreadRuntimeCore.d.ts.map +0 -1
- package/dist/A2AThreadRuntimeCore.js.map +0 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type A2uiState } from "@assistant-ui/generative-ui/a2ui";
|
|
2
|
+
import type { A2AAgentCard, A2AArtifact, A2AMessage, A2ATask, A2ATaskArtifactUpdateEvent, A2ATaskStatusUpdateEvent } from "./types.js";
|
|
3
|
+
export type A2AThreadState = {
|
|
4
|
+
currentTask: A2ATask | undefined;
|
|
5
|
+
currentArtifacts: A2AArtifact[];
|
|
6
|
+
a2uiState: A2uiState;
|
|
7
|
+
a2uiMessageIds: ReadonlySet<string>;
|
|
8
|
+
agentCard: A2AAgentCard | undefined;
|
|
9
|
+
isRunning: boolean;
|
|
10
|
+
isLoading: boolean;
|
|
11
|
+
loadRequested: boolean;
|
|
12
|
+
contextId: string | undefined;
|
|
13
|
+
};
|
|
14
|
+
export type A2AThreadAction = {
|
|
15
|
+
type: "statusUpdate";
|
|
16
|
+
event: A2ATaskStatusUpdateEvent;
|
|
17
|
+
} | {
|
|
18
|
+
type: "artifactUpdate";
|
|
19
|
+
event: A2ATaskArtifactUpdateEvent;
|
|
20
|
+
} | {
|
|
21
|
+
type: "taskSnapshot";
|
|
22
|
+
task: A2ATask;
|
|
23
|
+
} | {
|
|
24
|
+
type: "a2uiMessage";
|
|
25
|
+
message: A2AMessage;
|
|
26
|
+
replay?: boolean;
|
|
27
|
+
} | {
|
|
28
|
+
type: "prepareRun";
|
|
29
|
+
} | {
|
|
30
|
+
type: "runStarted";
|
|
31
|
+
} | {
|
|
32
|
+
type: "runFinished";
|
|
33
|
+
} | {
|
|
34
|
+
type: "agentCardLoaded";
|
|
35
|
+
agentCard: A2AAgentCard;
|
|
36
|
+
} | {
|
|
37
|
+
type: "loadRequested";
|
|
38
|
+
} | {
|
|
39
|
+
type: "loadStarted";
|
|
40
|
+
} | {
|
|
41
|
+
type: "loadFinished";
|
|
42
|
+
} | {
|
|
43
|
+
type: "contextLearned";
|
|
44
|
+
contextId: string | undefined;
|
|
45
|
+
} | {
|
|
46
|
+
type: "resetContext";
|
|
47
|
+
contextId: string | undefined;
|
|
48
|
+
} | {
|
|
49
|
+
type: "taskReplaced";
|
|
50
|
+
task: A2ATask;
|
|
51
|
+
} | {
|
|
52
|
+
type: "reset";
|
|
53
|
+
};
|
|
54
|
+
export declare const createA2AThreadState: (contextId?: string) => A2AThreadState;
|
|
55
|
+
export declare const reduceA2AThreadState: (state: A2AThreadState, action: A2AThreadAction) => A2AThreadState;
|
|
56
|
+
//# sourceMappingURL=a2aThreadState.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"a2aThreadState.d.ts","sourceRoot":"","sources":["../src/a2aThreadState.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,SAAS,EACf,MAAM,kCAAkC,CAAC;AAE1C,OAAO,KAAK,EACV,YAAY,EACZ,WAAW,EACX,UAAU,EAEV,OAAO,EACP,0BAA0B,EAC1B,wBAAwB,EACzB,mBAAgB;AAEjB,MAAM,MAAM,cAAc,GAAG;IAC3B,WAAW,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,gBAAgB,EAAE,WAAW,EAAE,CAAC;IAChC,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACpC,SAAS,EAAE,YAAY,GAAG,SAAS,CAAC;IACpC,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,KAAK,EAAE,wBAAwB,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,0BAA0B,CAAA;CAAE,GAC7D;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,UAAU,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9D;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,YAAY,CAAA;CAAE,GACtB;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,iBAAiB,CAAC;IAAC,SAAS,EAAE,YAAY,CAAA;CAAE,GACpD;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,GACzB;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GACxB;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GACzD;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtB,eAAO,MAAM,oBAAoB,GAAI,YAAY,MAAM,KAAG,cAUxD,CAAC;AAuCH,eAAO,MAAM,oBAAoB,GAC/B,OAAO,cAAc,EACrB,QAAQ,eAAe,KACtB,cAiJF,CAAC"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { a2uiPartsToOperations, isTerminalTaskState } from "./conversions.js";
|
|
2
|
+
import { applyA2uiOperations } from "@assistant-ui/generative-ui/a2ui";
|
|
3
|
+
//#region src/a2aThreadState.ts
|
|
4
|
+
const createA2AThreadState = (contextId) => ({
|
|
5
|
+
currentTask: void 0,
|
|
6
|
+
currentArtifacts: [],
|
|
7
|
+
a2uiState: /* @__PURE__ */ new Map(),
|
|
8
|
+
a2uiMessageIds: /* @__PURE__ */ new Set(),
|
|
9
|
+
agentCard: void 0,
|
|
10
|
+
isRunning: false,
|
|
11
|
+
isLoading: false,
|
|
12
|
+
loadRequested: false,
|
|
13
|
+
contextId
|
|
14
|
+
});
|
|
15
|
+
const normalizeArtifact = (artifact) => ({
|
|
16
|
+
...artifact,
|
|
17
|
+
parts: Array.isArray(artifact.parts) ? artifact.parts : []
|
|
18
|
+
});
|
|
19
|
+
const applyParts = (state, parts) => {
|
|
20
|
+
const operations = a2uiPartsToOperations(parts);
|
|
21
|
+
if (operations.length === 0) return state;
|
|
22
|
+
return {
|
|
23
|
+
...state,
|
|
24
|
+
a2uiState: applyA2uiOperations(state.a2uiState, operations).state
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
const applyMessage = (state, message, replay) => {
|
|
28
|
+
if (message.messageId && replay && state.a2uiMessageIds.has(message.messageId)) return state;
|
|
29
|
+
const next = message.messageId ? {
|
|
30
|
+
...state,
|
|
31
|
+
a2uiMessageIds: /* @__PURE__ */ new Set([...state.a2uiMessageIds, message.messageId])
|
|
32
|
+
} : state;
|
|
33
|
+
return applyParts(next, message.parts);
|
|
34
|
+
};
|
|
35
|
+
const reduceA2AThreadState = (state, action) => {
|
|
36
|
+
switch (action.type) {
|
|
37
|
+
case "statusUpdate": {
|
|
38
|
+
const { event } = action;
|
|
39
|
+
const currentTask = state.currentTask ? {
|
|
40
|
+
...state.currentTask,
|
|
41
|
+
status: event.status
|
|
42
|
+
} : {
|
|
43
|
+
id: event.taskId,
|
|
44
|
+
contextId: event.contextId,
|
|
45
|
+
status: event.status
|
|
46
|
+
};
|
|
47
|
+
const next = {
|
|
48
|
+
...state,
|
|
49
|
+
currentTask,
|
|
50
|
+
contextId: event.contextId || state.contextId
|
|
51
|
+
};
|
|
52
|
+
return event.status.message ? applyMessage(next, event.status.message, false) : next;
|
|
53
|
+
}
|
|
54
|
+
case "artifactUpdate": {
|
|
55
|
+
const { artifact: input, append } = action.event;
|
|
56
|
+
const artifact = normalizeArtifact(input);
|
|
57
|
+
const index = state.currentArtifacts.findIndex((current) => current.artifactId === artifact.artifactId);
|
|
58
|
+
const updated = index >= 0 && append ? {
|
|
59
|
+
...state.currentArtifacts[index],
|
|
60
|
+
parts: [...state.currentArtifacts[index].parts, ...artifact.parts]
|
|
61
|
+
} : artifact;
|
|
62
|
+
const currentArtifacts = index >= 0 ? [
|
|
63
|
+
...state.currentArtifacts.slice(0, index),
|
|
64
|
+
updated,
|
|
65
|
+
...state.currentArtifacts.slice(index + 1)
|
|
66
|
+
] : [...state.currentArtifacts, updated];
|
|
67
|
+
return applyParts({
|
|
68
|
+
...state,
|
|
69
|
+
currentArtifacts
|
|
70
|
+
}, artifact.parts);
|
|
71
|
+
}
|
|
72
|
+
case "taskSnapshot": {
|
|
73
|
+
const { task } = action;
|
|
74
|
+
const artifacts = task.artifacts === void 0 ? void 0 : Array.isArray(task.artifacts) ? task.artifacts.map(normalizeArtifact) : [];
|
|
75
|
+
const history = task.history === void 0 ? void 0 : Array.isArray(task.history) ? task.history : [];
|
|
76
|
+
const complete = history !== void 0 && artifacts !== void 0;
|
|
77
|
+
const artifactsToApply = complete ? artifacts : artifacts?.filter((artifact) => !state.currentArtifacts.some(({ artifactId }) => artifactId === artifact.artifactId));
|
|
78
|
+
let next = {
|
|
79
|
+
...state,
|
|
80
|
+
currentTask: {
|
|
81
|
+
...task,
|
|
82
|
+
...artifacts === void 0 ? {} : { artifacts },
|
|
83
|
+
...history === void 0 ? {} : { history }
|
|
84
|
+
},
|
|
85
|
+
contextId: task.contextId || state.contextId,
|
|
86
|
+
currentArtifacts: complete ? artifacts : artifactsToApply ? [...state.currentArtifacts, ...artifactsToApply] : state.currentArtifacts,
|
|
87
|
+
a2uiState: complete ? /* @__PURE__ */ new Map() : state.a2uiState,
|
|
88
|
+
a2uiMessageIds: complete ? /* @__PURE__ */ new Set() : state.a2uiMessageIds
|
|
89
|
+
};
|
|
90
|
+
for (const message of history ?? []) if (message.role === "agent") next = applyMessage(next, message, true);
|
|
91
|
+
for (const artifact of artifactsToApply ?? []) next = applyParts(next, artifact.parts);
|
|
92
|
+
if (task.status.message) next = applyMessage(next, task.status.message, true);
|
|
93
|
+
return next;
|
|
94
|
+
}
|
|
95
|
+
case "a2uiMessage": return applyMessage(state, action.message, action.replay ?? false);
|
|
96
|
+
case "prepareRun": return {
|
|
97
|
+
...state,
|
|
98
|
+
currentTask: state.currentTask && isTerminalTaskState(state.currentTask.status.state) ? void 0 : state.currentTask,
|
|
99
|
+
currentArtifacts: [],
|
|
100
|
+
a2uiState: /* @__PURE__ */ new Map(),
|
|
101
|
+
a2uiMessageIds: /* @__PURE__ */ new Set()
|
|
102
|
+
};
|
|
103
|
+
case "runStarted": return state.isRunning ? state : {
|
|
104
|
+
...state,
|
|
105
|
+
isRunning: true
|
|
106
|
+
};
|
|
107
|
+
case "runFinished": return !state.isRunning ? state : {
|
|
108
|
+
...state,
|
|
109
|
+
isRunning: false
|
|
110
|
+
};
|
|
111
|
+
case "agentCardLoaded": return state.agentCard === action.agentCard ? state : {
|
|
112
|
+
...state,
|
|
113
|
+
agentCard: action.agentCard
|
|
114
|
+
};
|
|
115
|
+
case "loadRequested": return state.loadRequested ? state : {
|
|
116
|
+
...state,
|
|
117
|
+
loadRequested: true
|
|
118
|
+
};
|
|
119
|
+
case "loadStarted": return state.isLoading ? state : {
|
|
120
|
+
...state,
|
|
121
|
+
isLoading: true
|
|
122
|
+
};
|
|
123
|
+
case "loadFinished": return !state.isLoading ? state : {
|
|
124
|
+
...state,
|
|
125
|
+
isLoading: false
|
|
126
|
+
};
|
|
127
|
+
case "contextLearned": return state.contextId === action.contextId ? state : {
|
|
128
|
+
...state,
|
|
129
|
+
contextId: action.contextId
|
|
130
|
+
};
|
|
131
|
+
case "resetContext": return !state.isLoading && state.contextId === action.contextId ? state : {
|
|
132
|
+
...state,
|
|
133
|
+
isLoading: false,
|
|
134
|
+
contextId: action.contextId
|
|
135
|
+
};
|
|
136
|
+
case "taskReplaced": return state.currentTask === action.task ? state : {
|
|
137
|
+
...state,
|
|
138
|
+
currentTask: action.task
|
|
139
|
+
};
|
|
140
|
+
case "reset": return {
|
|
141
|
+
...state,
|
|
142
|
+
currentTask: void 0,
|
|
143
|
+
currentArtifacts: [],
|
|
144
|
+
a2uiState: /* @__PURE__ */ new Map(),
|
|
145
|
+
a2uiMessageIds: /* @__PURE__ */ new Set()
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
//#endregion
|
|
150
|
+
export { createA2AThreadState, reduceA2AThreadState };
|
|
151
|
+
|
|
152
|
+
//# sourceMappingURL=a2aThreadState.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"a2aThreadState.js","names":[],"sources":["../src/a2aThreadState.ts"],"sourcesContent":["import {\n applyA2uiOperations,\n type A2uiState,\n} from \"@assistant-ui/generative-ui/a2ui\";\nimport { a2uiPartsToOperations, isTerminalTaskState } from \"./conversions\";\nimport type {\n A2AAgentCard,\n A2AArtifact,\n A2AMessage,\n A2APart,\n A2ATask,\n A2ATaskArtifactUpdateEvent,\n A2ATaskStatusUpdateEvent,\n} from \"./types\";\n\nexport type A2AThreadState = {\n currentTask: A2ATask | undefined;\n currentArtifacts: A2AArtifact[];\n a2uiState: A2uiState;\n a2uiMessageIds: ReadonlySet<string>;\n agentCard: A2AAgentCard | undefined;\n isRunning: boolean;\n isLoading: boolean;\n loadRequested: boolean;\n contextId: string | undefined;\n};\n\nexport type A2AThreadAction =\n | { type: \"statusUpdate\"; event: A2ATaskStatusUpdateEvent }\n | { type: \"artifactUpdate\"; event: A2ATaskArtifactUpdateEvent }\n | { type: \"taskSnapshot\"; task: A2ATask }\n | { type: \"a2uiMessage\"; message: A2AMessage; replay?: boolean }\n | { type: \"prepareRun\" }\n | { type: \"runStarted\" }\n | { type: \"runFinished\" }\n | { type: \"agentCardLoaded\"; agentCard: A2AAgentCard }\n | { type: \"loadRequested\" }\n | { type: \"loadStarted\" }\n | { type: \"loadFinished\" }\n | { type: \"contextLearned\"; contextId: string | undefined }\n | { type: \"resetContext\"; contextId: string | undefined }\n | { type: \"taskReplaced\"; task: A2ATask }\n | { type: \"reset\" };\n\nexport const createA2AThreadState = (contextId?: string): A2AThreadState => ({\n currentTask: undefined,\n currentArtifacts: [],\n a2uiState: new Map(),\n a2uiMessageIds: new Set(),\n agentCard: undefined,\n isRunning: false,\n isLoading: false,\n loadRequested: false,\n contextId,\n});\n\nconst normalizeArtifact = (artifact: A2AArtifact): A2AArtifact => ({\n ...artifact,\n parts: Array.isArray(artifact.parts) ? artifact.parts : [],\n});\n\nconst applyParts = (\n state: A2AThreadState,\n parts: readonly A2APart[],\n): A2AThreadState => {\n const operations = a2uiPartsToOperations(parts);\n if (operations.length === 0) return state;\n return {\n ...state,\n a2uiState: applyA2uiOperations(state.a2uiState, operations).state,\n };\n};\n\nconst applyMessage = (\n state: A2AThreadState,\n message: A2AMessage,\n replay: boolean,\n): A2AThreadState => {\n if (\n message.messageId &&\n replay &&\n state.a2uiMessageIds.has(message.messageId)\n )\n return state;\n const next = message.messageId\n ? {\n ...state,\n a2uiMessageIds: new Set([...state.a2uiMessageIds, message.messageId]),\n }\n : state;\n return applyParts(next, message.parts);\n};\n\nexport const reduceA2AThreadState = (\n state: A2AThreadState,\n action: A2AThreadAction,\n): A2AThreadState => {\n switch (action.type) {\n case \"statusUpdate\": {\n const { event } = action;\n const currentTask = state.currentTask\n ? { ...state.currentTask, status: event.status }\n : {\n id: event.taskId,\n contextId: event.contextId,\n status: event.status,\n };\n const next = {\n ...state,\n currentTask,\n contextId: event.contextId || state.contextId,\n };\n return event.status.message\n ? applyMessage(next, event.status.message, false)\n : next;\n }\n case \"artifactUpdate\": {\n const { artifact: input, append } = action.event;\n const artifact = normalizeArtifact(input);\n const index = state.currentArtifacts.findIndex(\n (current) => current.artifactId === artifact.artifactId,\n );\n const updated =\n index >= 0 && append\n ? {\n ...state.currentArtifacts[index]!,\n parts: [\n ...state.currentArtifacts[index]!.parts,\n ...artifact.parts,\n ],\n }\n : artifact;\n const currentArtifacts =\n index >= 0\n ? [\n ...state.currentArtifacts.slice(0, index),\n updated,\n ...state.currentArtifacts.slice(index + 1),\n ]\n : [...state.currentArtifacts, updated];\n return applyParts({ ...state, currentArtifacts }, artifact.parts);\n }\n case \"taskSnapshot\": {\n const { task } = action;\n const artifacts =\n task.artifacts === undefined\n ? undefined\n : Array.isArray(task.artifacts)\n ? task.artifacts.map(normalizeArtifact)\n : [];\n const history =\n task.history === undefined\n ? undefined\n : Array.isArray(task.history)\n ? task.history\n : [];\n const complete = history !== undefined && artifacts !== undefined;\n const artifactsToApply = complete\n ? artifacts\n : artifacts?.filter(\n (artifact) =>\n !state.currentArtifacts.some(\n ({ artifactId }) => artifactId === artifact.artifactId,\n ),\n );\n let next: A2AThreadState = {\n ...state,\n currentTask: {\n ...task,\n ...(artifacts === undefined ? {} : { artifacts }),\n ...(history === undefined ? {} : { history }),\n },\n contextId: task.contextId || state.contextId,\n currentArtifacts: complete\n ? artifacts!\n : artifactsToApply\n ? [...state.currentArtifacts, ...artifactsToApply]\n : state.currentArtifacts,\n a2uiState: complete ? new Map() : state.a2uiState,\n a2uiMessageIds: complete ? new Set() : state.a2uiMessageIds,\n };\n for (const message of history ?? []) {\n if (message.role === \"agent\") next = applyMessage(next, message, true);\n }\n for (const artifact of artifactsToApply ?? []) {\n next = applyParts(next, artifact.parts);\n }\n if (task.status.message) {\n next = applyMessage(next, task.status.message, true);\n }\n return next;\n }\n case \"a2uiMessage\":\n return applyMessage(state, action.message, action.replay ?? false);\n case \"prepareRun\":\n return {\n ...state,\n currentTask:\n state.currentTask &&\n isTerminalTaskState(state.currentTask.status.state)\n ? undefined\n : state.currentTask,\n currentArtifacts: [],\n a2uiState: new Map(),\n a2uiMessageIds: new Set(),\n };\n case \"runStarted\":\n return state.isRunning ? state : { ...state, isRunning: true };\n case \"runFinished\":\n return !state.isRunning ? state : { ...state, isRunning: false };\n case \"agentCardLoaded\":\n return state.agentCard === action.agentCard\n ? state\n : { ...state, agentCard: action.agentCard };\n case \"loadRequested\":\n return state.loadRequested ? state : { ...state, loadRequested: true };\n case \"loadStarted\":\n return state.isLoading ? state : { ...state, isLoading: true };\n case \"loadFinished\":\n return !state.isLoading ? state : { ...state, isLoading: false };\n case \"contextLearned\":\n return state.contextId === action.contextId\n ? state\n : { ...state, contextId: action.contextId };\n case \"resetContext\":\n return !state.isLoading && state.contextId === action.contextId\n ? state\n : { ...state, isLoading: false, contextId: action.contextId };\n case \"taskReplaced\":\n return state.currentTask === action.task\n ? state\n : { ...state, currentTask: action.task };\n case \"reset\":\n return {\n ...state,\n currentTask: undefined,\n currentArtifacts: [],\n a2uiState: new Map(),\n a2uiMessageIds: new Set(),\n };\n }\n};\n"],"mappings":"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"}
|
package/dist/useA2ARuntime.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { A2AClient } from "./A2AClient.js";
|
|
3
|
-
import {
|
|
3
|
+
import { A2AThreadController } from "./A2AThreadController.js";
|
|
4
4
|
import { a2aExtras } from "./a2aExtras.js";
|
|
5
5
|
import { useExternalStoreRuntime, useExternalStoreSharedOptions, useRuntimeAdapters } from "@assistant-ui/core/react";
|
|
6
6
|
import { useCallback, useEffect, useInsertionEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
@@ -66,7 +66,7 @@ function useA2ARuntime(options) {
|
|
|
66
66
|
};
|
|
67
67
|
const coreOptionsRef = useRef(coreOptions);
|
|
68
68
|
coreOptionsRef.current = coreOptions;
|
|
69
|
-
const createCore = () => new
|
|
69
|
+
const createCore = () => new A2AThreadController({
|
|
70
70
|
...coreOptionsRef.current,
|
|
71
71
|
client
|
|
72
72
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useA2ARuntime.js","names":[],"sources":["../src/useA2ARuntime.ts"],"sourcesContent":["\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useInsertionEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport {\n useExternalStoreRuntime,\n useExternalStoreSharedOptions,\n useRuntimeAdapters,\n} from \"@assistant-ui/core/react\";\nimport { useReplaySafeEffect } from \"@assistant-ui/store/internal\";\nimport type {\n AssistantRuntime,\n AppendMessage,\n ExternalStoreAdapter,\n ThreadMessage,\n} from \"@assistant-ui/core\";\nimport { A2AClient, type A2AClientOptions } from \"./A2AClient\";\nimport { A2AThreadRuntimeCore } from \"./A2AThreadRuntimeCore\";\nimport { a2aExtras } from \"./a2aExtras\";\nimport type { UseA2ARuntimeOptions } from \"./types\";\n\ntype ManagedA2AClientOptions = Omit<A2AClientOptions, \"headers\">;\n\nconst serializeManagedClientOptions = (\n options: ManagedA2AClientOptions,\n): string => {\n const fetchOptions = Object.fromEntries(\n Object.entries(options.fetchOptions ?? {}).sort(([a], [b]) =>\n a.localeCompare(b),\n ),\n );\n\n return JSON.stringify({\n ...options,\n fetchOptions,\n });\n};\n\nexport function useA2ARuntime(options: UseA2ARuntimeOptions): AssistantRuntime {\n const runtimeAdapters = useRuntimeAdapters();\n const historyAdapter = options.adapters?.history ?? runtimeAdapters?.history;\n const threadListAdapter = options.adapters?.threadList;\n\n const headersRef = useRef(options.headers);\n useInsertionEffect(() => {\n headersRef.current = options.headers;\n });\n const resolveHeaders = useCallback(() => {\n const headers = headersRef.current;\n return typeof headers === \"function\" ? headers() : (headers ?? {});\n }, []);\n\n const managedClientOptionsKey = options.client\n ? null\n : options.baseUrl\n ? serializeManagedClientOptions({\n baseUrl: options.baseUrl,\n basePath: options.basePath,\n tenant: options.tenant,\n extensions: options.extensions,\n fetchOptions: options.fetchOptions,\n })\n : null;\n\n const createClient = () => {\n if (options.client) return options.client;\n if (!managedClientOptionsKey) {\n throw new Error(\"useA2ARuntime requires either `client` or `baseUrl`\");\n }\n\n return new A2AClient({\n ...(JSON.parse(managedClientOptionsKey) as ManagedA2AClientOptions),\n headers: resolveHeaders,\n });\n };\n const [pinnedClient, setPinnedClient] = useState(() => ({\n key: managedClientOptionsKey,\n provided: options.client,\n client: createClient(),\n }));\n let currentClient = pinnedClient;\n if (\n pinnedClient.key !== managedClientOptionsKey ||\n pinnedClient.provided !== options.client\n ) {\n currentClient = {\n key: managedClientOptionsKey,\n provided: options.client,\n client: createClient(),\n };\n setPinnedClient(currentClient);\n }\n const client = currentClient.client;\n\n const coreOptions = {\n client,\n contextId: options.contextId,\n configuration: options.configuration,\n ...(options.onError && { onError: options.onError }),\n ...(options.onCancel && { onCancel: options.onCancel }),\n ...(options.onArtifactComplete && {\n onArtifactComplete: options.onArtifactComplete,\n }),\n ...(historyAdapter && { history: historyAdapter }),\n };\n const coreOptionsRef = useRef(coreOptions);\n coreOptionsRef.current = coreOptions;\n\n const createCore = () =>\n new A2AThreadRuntimeCore({\n ...coreOptionsRef.current,\n client,\n });\n const [pinnedCore, setPinnedCore] = useState(() => ({\n client,\n core: createCore(),\n }));\n let currentCore = pinnedCore;\n if (pinnedCore.client !== client) {\n currentCore = { client, core: createCore() };\n setPinnedCore(currentCore);\n }\n const core = currentCore.core;\n const snapshot = useSyncExternalStore(\n core.subscribe,\n core.getSnapshot,\n core.getSnapshot,\n );\n\n useEffect(() => {\n core.updateOptions(coreOptions);\n });\n\n // Thread list\n const threadSwitchGenerationRef = useRef(0);\n const switchingGenerationRef = useRef<number | null>(null);\n const threadList = useMemo(() => {\n if (!threadListAdapter) return undefined;\n\n const { onSwitchToNewThread, onSwitchToThread } = threadListAdapter;\n\n const releaseSwitch = (generation: number) => {\n if (switchingGenerationRef.current === generation) {\n switchingGenerationRef.current = null;\n }\n };\n\n return {\n threadId: threadListAdapter.threadId,\n onSwitchToNewThread: onSwitchToNewThread\n ? async () => {\n const generation = ++threadSwitchGenerationRef.current;\n switchingGenerationRef.current = generation;\n try {\n // Clear before the thread id flips, or the old messages leak\n // into the new thread as a sibling branch.\n core.applyExternalMessages([]);\n core.resetContext();\n await onSwitchToNewThread();\n if (generation !== threadSwitchGenerationRef.current) return;\n // Apply first so the abort inside resetContext finds an already\n // cleared repository and cannot persist the old thread's partial\n // assistant message.\n core.applyExternalMessages([]);\n core.resetContext();\n } finally {\n releaseSwitch(generation);\n }\n }\n : undefined,\n onSwitchToThread: onSwitchToThread\n ? async (threadId: string) => {\n const generation = ++threadSwitchGenerationRef.current;\n switchingGenerationRef.current = generation;\n try {\n // Clear before the thread id flips, or the old messages leak\n // into the new thread as a sibling branch.\n core.applyExternalMessages([]);\n core.resetContext();\n const result = await onSwitchToThread(threadId);\n if (generation !== threadSwitchGenerationRef.current) return;\n core.applyExternalMessages([]);\n core.applyExternalMessages(result.messages);\n core.resetContext();\n } finally {\n releaseSwitch(generation);\n }\n }\n : undefined,\n };\n }, [threadListAdapter, core]);\n\n // Adapters\n const adapters = options.adapters;\n const adapterAdapters = useMemo(\n () => ({\n attachments: adapters?.attachments ?? runtimeAdapters?.attachments,\n speech: adapters?.speech,\n dictation: adapters?.dictation,\n voice: adapters?.voice,\n feedback: adapters?.feedback,\n threadList,\n }),\n [adapters, runtimeAdapters, threadList],\n );\n\n // Build store adapter\n const shared = useExternalStoreSharedOptions(options);\n const store = useMemo(() => {\n return {\n ...shared,\n isLoading: snapshot.isLoading,\n messageRepository: snapshot.messageRepository,\n isRunning: snapshot.isRunning,\n unstable_persistsHistory: true,\n extras: a2aExtras.provide({\n task: snapshot.task,\n artifacts: snapshot.artifacts,\n agentCard: snapshot.agentCard,\n }),\n onNew: (message: AppendMessage) =>\n switchingGenerationRef.current === null\n ? core.append(message)\n : Promise.resolve(),\n onVoiceTranscript: (message: ThreadMessage) =>\n core.appendVoiceTranscript(message),\n onEdit: (message: AppendMessage) =>\n switchingGenerationRef.current === null\n ? core.edit(message)\n : Promise.resolve(),\n onReload: (parentId: string | null) =>\n switchingGenerationRef.current === null\n ? core.reload(parentId)\n : Promise.resolve(),\n onCancel: () => core.cancel(),\n unstable_onRecordToolInteraction: (options) =>\n core.recordToolInteraction(options),\n setMessages: (messages: readonly ThreadMessage[]) =>\n core.applyExternalMessages(messages),\n onImport: (messages: readonly ThreadMessage[]) =>\n core.applyExternalMessages(messages),\n adapters: adapterAdapters,\n } satisfies ExternalStoreAdapter<ThreadMessage>;\n }, [adapterAdapters, core, snapshot, shared]);\n\n const runtime = useExternalStoreRuntime(store);\n\n useReplaySafeEffect(() => {\n core.attachRuntime(runtime);\n return () => {\n core.detachRuntime();\n };\n }, [core, runtime]);\n\n useEffect(() => {\n core.__internal_load();\n }, [core]);\n\n return runtime;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useA2ARuntime.js","names":[],"sources":["../src/useA2ARuntime.ts"],"sourcesContent":["\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useInsertionEffect,\n useMemo,\n useRef,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport {\n useExternalStoreRuntime,\n useExternalStoreSharedOptions,\n useRuntimeAdapters,\n} from \"@assistant-ui/core/react\";\nimport { useReplaySafeEffect } from \"@assistant-ui/store/internal\";\nimport type {\n AssistantRuntime,\n AppendMessage,\n ExternalStoreAdapter,\n ThreadMessage,\n} from \"@assistant-ui/core\";\nimport { A2AClient, type A2AClientOptions } from \"./A2AClient\";\nimport { A2AThreadController } from \"./A2AThreadController\";\nimport { a2aExtras } from \"./a2aExtras\";\nimport type { UseA2ARuntimeOptions } from \"./types\";\n\ntype ManagedA2AClientOptions = Omit<A2AClientOptions, \"headers\">;\n\nconst serializeManagedClientOptions = (\n options: ManagedA2AClientOptions,\n): string => {\n const fetchOptions = Object.fromEntries(\n Object.entries(options.fetchOptions ?? {}).sort(([a], [b]) =>\n a.localeCompare(b),\n ),\n );\n\n return JSON.stringify({\n ...options,\n fetchOptions,\n });\n};\n\nexport function useA2ARuntime(options: UseA2ARuntimeOptions): AssistantRuntime {\n const runtimeAdapters = useRuntimeAdapters();\n const historyAdapter = options.adapters?.history ?? runtimeAdapters?.history;\n const threadListAdapter = options.adapters?.threadList;\n\n const headersRef = useRef(options.headers);\n useInsertionEffect(() => {\n headersRef.current = options.headers;\n });\n const resolveHeaders = useCallback(() => {\n const headers = headersRef.current;\n return typeof headers === \"function\" ? headers() : (headers ?? {});\n }, []);\n\n const managedClientOptionsKey = options.client\n ? null\n : options.baseUrl\n ? serializeManagedClientOptions({\n baseUrl: options.baseUrl,\n basePath: options.basePath,\n tenant: options.tenant,\n extensions: options.extensions,\n fetchOptions: options.fetchOptions,\n })\n : null;\n\n const createClient = () => {\n if (options.client) return options.client;\n if (!managedClientOptionsKey) {\n throw new Error(\"useA2ARuntime requires either `client` or `baseUrl`\");\n }\n\n return new A2AClient({\n ...(JSON.parse(managedClientOptionsKey) as ManagedA2AClientOptions),\n headers: resolveHeaders,\n });\n };\n const [pinnedClient, setPinnedClient] = useState(() => ({\n key: managedClientOptionsKey,\n provided: options.client,\n client: createClient(),\n }));\n let currentClient = pinnedClient;\n if (\n pinnedClient.key !== managedClientOptionsKey ||\n pinnedClient.provided !== options.client\n ) {\n currentClient = {\n key: managedClientOptionsKey,\n provided: options.client,\n client: createClient(),\n };\n setPinnedClient(currentClient);\n }\n const client = currentClient.client;\n\n const coreOptions = {\n client,\n contextId: options.contextId,\n configuration: options.configuration,\n ...(options.onError && { onError: options.onError }),\n ...(options.onCancel && { onCancel: options.onCancel }),\n ...(options.onArtifactComplete && {\n onArtifactComplete: options.onArtifactComplete,\n }),\n ...(historyAdapter && { history: historyAdapter }),\n };\n const coreOptionsRef = useRef(coreOptions);\n coreOptionsRef.current = coreOptions;\n\n const createCore = () =>\n new A2AThreadController({\n ...coreOptionsRef.current,\n client,\n });\n const [pinnedCore, setPinnedCore] = useState(() => ({\n client,\n core: createCore(),\n }));\n let currentCore = pinnedCore;\n if (pinnedCore.client !== client) {\n currentCore = { client, core: createCore() };\n setPinnedCore(currentCore);\n }\n const core = currentCore.core;\n const snapshot = useSyncExternalStore(\n core.subscribe,\n core.getSnapshot,\n core.getSnapshot,\n );\n\n useEffect(() => {\n core.updateOptions(coreOptions);\n });\n\n // Thread list\n const threadSwitchGenerationRef = useRef(0);\n const switchingGenerationRef = useRef<number | null>(null);\n const threadList = useMemo(() => {\n if (!threadListAdapter) return undefined;\n\n const { onSwitchToNewThread, onSwitchToThread } = threadListAdapter;\n\n const releaseSwitch = (generation: number) => {\n if (switchingGenerationRef.current === generation) {\n switchingGenerationRef.current = null;\n }\n };\n\n return {\n threadId: threadListAdapter.threadId,\n onSwitchToNewThread: onSwitchToNewThread\n ? async () => {\n const generation = ++threadSwitchGenerationRef.current;\n switchingGenerationRef.current = generation;\n try {\n // Clear before the thread id flips, or the old messages leak\n // into the new thread as a sibling branch.\n core.applyExternalMessages([]);\n core.resetContext();\n await onSwitchToNewThread();\n if (generation !== threadSwitchGenerationRef.current) return;\n // Apply first so the abort inside resetContext finds an already\n // cleared repository and cannot persist the old thread's partial\n // assistant message.\n core.applyExternalMessages([]);\n core.resetContext();\n } finally {\n releaseSwitch(generation);\n }\n }\n : undefined,\n onSwitchToThread: onSwitchToThread\n ? async (threadId: string) => {\n const generation = ++threadSwitchGenerationRef.current;\n switchingGenerationRef.current = generation;\n try {\n // Clear before the thread id flips, or the old messages leak\n // into the new thread as a sibling branch.\n core.applyExternalMessages([]);\n core.resetContext();\n const result = await onSwitchToThread(threadId);\n if (generation !== threadSwitchGenerationRef.current) return;\n core.applyExternalMessages([]);\n core.applyExternalMessages(result.messages);\n core.resetContext();\n } finally {\n releaseSwitch(generation);\n }\n }\n : undefined,\n };\n }, [threadListAdapter, core]);\n\n // Adapters\n const adapters = options.adapters;\n const adapterAdapters = useMemo(\n () => ({\n attachments: adapters?.attachments ?? runtimeAdapters?.attachments,\n speech: adapters?.speech,\n dictation: adapters?.dictation,\n voice: adapters?.voice,\n feedback: adapters?.feedback,\n threadList,\n }),\n [adapters, runtimeAdapters, threadList],\n );\n\n // Build store adapter\n const shared = useExternalStoreSharedOptions(options);\n const store = useMemo(() => {\n return {\n ...shared,\n isLoading: snapshot.isLoading,\n messageRepository: snapshot.messageRepository,\n isRunning: snapshot.isRunning,\n unstable_persistsHistory: true,\n extras: a2aExtras.provide({\n task: snapshot.task,\n artifacts: snapshot.artifacts,\n agentCard: snapshot.agentCard,\n }),\n onNew: (message: AppendMessage) =>\n switchingGenerationRef.current === null\n ? core.append(message)\n : Promise.resolve(),\n onVoiceTranscript: (message: ThreadMessage) =>\n core.appendVoiceTranscript(message),\n onEdit: (message: AppendMessage) =>\n switchingGenerationRef.current === null\n ? core.edit(message)\n : Promise.resolve(),\n onReload: (parentId: string | null) =>\n switchingGenerationRef.current === null\n ? core.reload(parentId)\n : Promise.resolve(),\n onCancel: () => core.cancel(),\n unstable_onRecordToolInteraction: (options) =>\n core.recordToolInteraction(options),\n setMessages: (messages: readonly ThreadMessage[]) =>\n core.applyExternalMessages(messages),\n onImport: (messages: readonly ThreadMessage[]) =>\n core.applyExternalMessages(messages),\n adapters: adapterAdapters,\n } satisfies ExternalStoreAdapter<ThreadMessage>;\n }, [adapterAdapters, core, snapshot, shared]);\n\n const runtime = useExternalStoreRuntime(store);\n\n useReplaySafeEffect(() => {\n core.attachRuntime(runtime);\n return () => {\n core.detachRuntime();\n };\n }, [core, runtime]);\n\n useEffect(() => {\n core.__internal_load();\n }, [core]);\n\n return runtime;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/react-a2a",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.48",
|
|
4
4
|
"description": "A2A (Agent-to-Agent) v1.0 protocol adapter for assistant-ui",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"a2a",
|
|
@@ -29,10 +29,10 @@
|
|
|
29
29
|
],
|
|
30
30
|
"sideEffects": false,
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@assistant-ui/core": "^0.3.
|
|
33
|
-
"@assistant-ui/generative-ui": "^0.0.
|
|
34
|
-
"@assistant-ui/store": "^0.3.
|
|
35
|
-
"assistant-stream": "^0.3.
|
|
32
|
+
"@assistant-ui/core": "^0.3.31",
|
|
33
|
+
"@assistant-ui/generative-ui": "^0.0.7",
|
|
34
|
+
"@assistant-ui/store": "^0.3.22",
|
|
35
|
+
"assistant-stream": "^0.3.54"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@types/react": "*",
|
|
@@ -44,15 +44,15 @@
|
|
|
44
44
|
}
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@assistant-ui/x-buildutils": "0.0.
|
|
47
|
+
"@assistant-ui/x-buildutils": "0.0.31",
|
|
48
48
|
"@assistant-ui/x-react18": "0.0.0",
|
|
49
49
|
"@testing-library/dom": "^10.4.2",
|
|
50
50
|
"@testing-library/react": "^16.3.3",
|
|
51
51
|
"@types/react": "^19.3.0",
|
|
52
|
-
"jsdom": "^30.1.
|
|
53
|
-
"react": "^19.3.
|
|
54
|
-
"react-dom": "^19.3.
|
|
55
|
-
"vitest": "^5.0.
|
|
52
|
+
"jsdom": "^30.1.3",
|
|
53
|
+
"react": "^19.3.1",
|
|
54
|
+
"react-dom": "^19.3.1",
|
|
55
|
+
"vitest": "^5.0.5"
|
|
56
56
|
},
|
|
57
57
|
"publishConfig": {
|
|
58
58
|
"access": "public",
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
applyA2uiOperations,
|
|
4
4
|
convertSurfaceToUISpec,
|
|
5
5
|
} from "@assistant-ui/generative-ui/a2ui";
|
|
6
|
-
import {
|
|
6
|
+
import { A2AThreadController } from "./A2AThreadController";
|
|
7
7
|
import type { A2AClient } from "./A2AClient";
|
|
8
8
|
import type {
|
|
9
9
|
A2AAgentCard,
|
|
@@ -154,7 +154,7 @@ const toolInteraction = {
|
|
|
154
154
|
payload: { value: "selected" },
|
|
155
155
|
};
|
|
156
156
|
|
|
157
|
-
describe("
|
|
157
|
+
describe("A2AThreadController", () => {
|
|
158
158
|
let onUpdate = vi.fn<() => void>();
|
|
159
159
|
|
|
160
160
|
beforeEach(() => {
|
|
@@ -169,7 +169,7 @@ describe("A2AThreadRuntimeCore", () => {
|
|
|
169
169
|
clientOverrides: Partial<A2AClient> = {},
|
|
170
170
|
coreOverrides: Record<string, unknown> = {},
|
|
171
171
|
) {
|
|
172
|
-
const core = new
|
|
172
|
+
const core = new A2AThreadController({
|
|
173
173
|
client: createMockClient(clientOverrides),
|
|
174
174
|
...coreOverrides,
|
|
175
175
|
});
|
|
@@ -634,7 +634,7 @@ describe("A2AThreadRuntimeCore", () => {
|
|
|
634
634
|
yield statusUpdateEvent("completed", "Answer");
|
|
635
635
|
});
|
|
636
636
|
const client = createMockClient({ streamMessage });
|
|
637
|
-
const core = new
|
|
637
|
+
const core = new A2AThreadController({
|
|
638
638
|
client,
|
|
639
639
|
});
|
|
640
640
|
|
|
@@ -653,7 +653,7 @@ describe("A2AThreadRuntimeCore", () => {
|
|
|
653
653
|
yield statusUpdateEvent("completed", "Answer");
|
|
654
654
|
});
|
|
655
655
|
const client = createMockClient({ streamMessage });
|
|
656
|
-
const core = new
|
|
656
|
+
const core = new A2AThreadController({
|
|
657
657
|
client,
|
|
658
658
|
});
|
|
659
659
|
|
|
@@ -706,7 +706,7 @@ describe("A2AThreadRuntimeCore", () => {
|
|
|
706
706
|
yield statusUpdateEvent("completed", "Answer");
|
|
707
707
|
});
|
|
708
708
|
const client = createMockClient({ streamMessage });
|
|
709
|
-
const core = new
|
|
709
|
+
const core = new A2AThreadController({
|
|
710
710
|
client,
|
|
711
711
|
});
|
|
712
712
|
|
|
@@ -725,7 +725,7 @@ describe("A2AThreadRuntimeCore", () => {
|
|
|
725
725
|
yield statusUpdateEvent("completed", "Answer");
|
|
726
726
|
});
|
|
727
727
|
const client = createMockClient({ streamMessage });
|
|
728
|
-
const core = new
|
|
728
|
+
const core = new A2AThreadController({
|
|
729
729
|
client,
|
|
730
730
|
});
|
|
731
731
|
|
|
@@ -1581,8 +1581,9 @@ describe("A2AThreadRuntimeCore", () => {
|
|
|
1581
1581
|
|
|
1582
1582
|
const core = createCore({ sendMessage, streamMessage });
|
|
1583
1583
|
// Simulate agent card with streaming: false
|
|
1584
|
-
(core as any).
|
|
1585
|
-
|
|
1584
|
+
(core as any).state = {
|
|
1585
|
+
...(core as any).state,
|
|
1586
|
+
agentCard: { capabilities: { streaming: false } },
|
|
1586
1587
|
};
|
|
1587
1588
|
|
|
1588
1589
|
await core.append(createUserAppendMessage("Hello"));
|
|
@@ -1605,8 +1606,9 @@ describe("A2AThreadRuntimeCore", () => {
|
|
|
1605
1606
|
} satisfies A2AMessage);
|
|
1606
1607
|
|
|
1607
1608
|
const core = createCore({ sendMessage });
|
|
1608
|
-
(core as any).
|
|
1609
|
-
|
|
1609
|
+
(core as any).state = {
|
|
1610
|
+
...(core as any).state,
|
|
1611
|
+
agentCard: { capabilities: { streaming: false } },
|
|
1610
1612
|
};
|
|
1611
1613
|
|
|
1612
1614
|
await core.append(createUserAppendMessage("Hello"));
|
|
@@ -1763,7 +1765,7 @@ describe("A2AThreadRuntimeCore", () => {
|
|
|
1763
1765
|
it("calls onArtifactComplete when lastChunk=true", async () => {
|
|
1764
1766
|
const onArtifactComplete = vi.fn();
|
|
1765
1767
|
|
|
1766
|
-
const core = new
|
|
1768
|
+
const core = new A2AThreadController({
|
|
1767
1769
|
client: createMockClient({
|
|
1768
1770
|
streamMessage: vi.fn().mockImplementation(async function* () {
|
|
1769
1771
|
yield artifactUpdateEvent("a1", [{ text: "code" }], {
|
|
@@ -1781,6 +1783,32 @@ describe("A2AThreadRuntimeCore", () => {
|
|
|
1781
1783
|
expect(onArtifactComplete.mock.calls[0]![0].artifactId).toBe("a1");
|
|
1782
1784
|
});
|
|
1783
1785
|
|
|
1786
|
+
it("passes the completed artifact even if a subscriber clears the thread", async () => {
|
|
1787
|
+
const onArtifactComplete = vi.fn();
|
|
1788
|
+
|
|
1789
|
+
const core = new A2AThreadController({
|
|
1790
|
+
client: createMockClient({
|
|
1791
|
+
streamMessage: vi.fn().mockImplementation(async function* () {
|
|
1792
|
+
yield artifactUpdateEvent("a1", [{ text: "code" }], {
|
|
1793
|
+
lastChunk: true,
|
|
1794
|
+
});
|
|
1795
|
+
}),
|
|
1796
|
+
}),
|
|
1797
|
+
onArtifactComplete,
|
|
1798
|
+
});
|
|
1799
|
+
let cleared = false;
|
|
1800
|
+
core.subscribe(() => {
|
|
1801
|
+
if (cleared || core.getArtifacts().length === 0) return;
|
|
1802
|
+
cleared = true;
|
|
1803
|
+
core.applyExternalMessages([]);
|
|
1804
|
+
});
|
|
1805
|
+
|
|
1806
|
+
await core.append(createUserAppendMessage("Go"));
|
|
1807
|
+
|
|
1808
|
+
expect(onArtifactComplete).toHaveBeenCalledTimes(1);
|
|
1809
|
+
expect(onArtifactComplete.mock.calls[0]![0]?.artifactId).toBe("a1");
|
|
1810
|
+
});
|
|
1811
|
+
|
|
1784
1812
|
it.each(["throws", "rejects"] as const)(
|
|
1785
1813
|
"continues streaming when onArtifactComplete %s",
|
|
1786
1814
|
async (failureMode) => {
|
|
@@ -2377,7 +2405,7 @@ describe("A2AThreadRuntimeCore", () => {
|
|
|
2377
2405
|
signal.addEventListener("abort", () => resolve(), { once: true });
|
|
2378
2406
|
});
|
|
2379
2407
|
});
|
|
2380
|
-
let core!:
|
|
2408
|
+
let core!: A2AThreadController;
|
|
2381
2409
|
let replacementRun: Promise<void> | undefined;
|
|
2382
2410
|
let startReplacement = true;
|
|
2383
2411
|
core = createCore(
|
|
@@ -2428,9 +2456,9 @@ describe("A2AThreadRuntimeCore", () => {
|
|
|
2428
2456
|
const core = createCore({ cancelTask });
|
|
2429
2457
|
|
|
2430
2458
|
// Manually set task state to simulate a running task
|
|
2431
|
-
(core as any).
|
|
2432
|
-
|
|
2433
|
-
status: { state: "working" },
|
|
2459
|
+
(core as any).state = {
|
|
2460
|
+
...(core as any).state,
|
|
2461
|
+
currentTask: { id: "t1", status: { state: "working" } },
|
|
2434
2462
|
};
|
|
2435
2463
|
(core as any).abortController = new AbortController();
|
|
2436
2464
|
onUpdate.mockClear();
|
|
@@ -2556,7 +2584,7 @@ describe("A2AThreadRuntimeCore", () => {
|
|
|
2556
2584
|
});
|
|
2557
2585
|
});
|
|
2558
2586
|
let restarted = false;
|
|
2559
|
-
let core!:
|
|
2587
|
+
let core!: A2AThreadController;
|
|
2560
2588
|
core = createCore(
|
|
2561
2589
|
{ streamMessage },
|
|
2562
2590
|
{
|
|
@@ -2621,7 +2649,7 @@ describe("A2AThreadRuntimeCore", () => {
|
|
|
2621
2649
|
signal.addEventListener("abort", () => resolve(), { once: true });
|
|
2622
2650
|
});
|
|
2623
2651
|
});
|
|
2624
|
-
let core!:
|
|
2652
|
+
let core!: A2AThreadController;
|
|
2625
2653
|
core = createCore(
|
|
2626
2654
|
{ cancelTask, streamMessage },
|
|
2627
2655
|
{ onCancel: () => core.applyExternalMessages([]) },
|
|
@@ -2704,7 +2732,7 @@ describe("A2AThreadRuntimeCore", () => {
|
|
|
2704
2732
|
it("sets error status and re-throws on stream failure", async () => {
|
|
2705
2733
|
const onError = vi.fn();
|
|
2706
2734
|
|
|
2707
|
-
const core = new
|
|
2735
|
+
const core = new A2AThreadController({
|
|
2708
2736
|
client: createMockClient({
|
|
2709
2737
|
streamMessage: vi.fn().mockImplementation(() => ({
|
|
2710
2738
|
async next() {
|
|
@@ -2959,7 +2987,7 @@ describe("outbound message conversion", () => {
|
|
|
2959
2987
|
const streamMessage = vi.fn().mockImplementation(async function* () {
|
|
2960
2988
|
yield statusUpdateEvent("completed", "Done");
|
|
2961
2989
|
});
|
|
2962
|
-
const core = new
|
|
2990
|
+
const core = new A2AThreadController({
|
|
2963
2991
|
client: createMockClient({ streamMessage }),
|
|
2964
2992
|
});
|
|
2965
2993
|
return { core, streamMessage };
|