basalt-ui 1.9.0 → 1.11.0
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/AGENTS.md +2 -1
- package/CHANGELOG.md +18 -0
- package/README.md +64 -34
- package/agent/rules/basalt-agent.md +49 -6
- package/configs/oxlint-plugin.js +279 -0
- package/configs/oxlint-plugin.test.ts +216 -1
- package/configs/oxlint.json +5 -2
- package/dist/agent/ai-sdk-transport.d.ts +3 -52
- package/dist/agent/ai-sdk-transport.d.ts.map +1 -1
- package/dist/agent/ai-sdk-transport.js +108 -24
- package/dist/agent/coalesce.d.ts +26 -0
- package/dist/agent/coalesce.d.ts.map +1 -0
- package/dist/agent/coalesce.js +69 -0
- package/dist/agent/foreign.d.ts +97 -0
- package/dist/agent/foreign.d.ts.map +1 -0
- package/dist/agent/foreign.js +18 -0
- package/dist/agent/history.d.ts +2 -0
- package/dist/agent/history.d.ts.map +1 -1
- package/dist/agent/id.d.ts +35 -0
- package/dist/agent/id.d.ts.map +1 -0
- package/dist/agent/id.js +14 -0
- package/dist/agent/index.d.ts +9 -4
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +15 -3
- package/dist/agent/merge.d.ts +8 -0
- package/dist/agent/merge.d.ts.map +1 -0
- package/dist/agent/merge.js +23 -0
- package/dist/agent/part-list.d.ts.map +1 -1
- package/dist/agent/part-list.js +16 -6
- package/dist/agent/parts.d.ts +100 -21
- package/dist/agent/parts.d.ts.map +1 -1
- package/dist/agent/parts.js +128 -11
- package/dist/agent/transport.d.ts +39 -3
- package/dist/agent/transport.d.ts.map +1 -1
- package/dist/agent/transport.js +20 -17
- package/dist/agent/use-agent-stream.d.ts +2 -1
- package/dist/agent/use-agent-stream.d.ts.map +1 -1
- package/dist/agent/use-agent-stream.js +5 -2
- package/dist/agent/use-agent-thread-runs.d.ts +9 -3
- package/dist/agent/use-agent-thread-runs.d.ts.map +1 -1
- package/dist/agent/use-agent-thread-runs.js +76 -23
- package/dist/agent-chat/index.d.ts +6 -3
- package/dist/agent-chat/index.d.ts.map +1 -1
- package/dist/agent-chat/index.js +2 -0
- package/dist/agent-chat/thread-message.d.ts +24 -7
- package/dist/agent-chat/thread-message.d.ts.map +1 -1
- package/dist/agent-chat/thread-message.js +102 -43
- package/dist/agent-chat/tool-chip.d.ts +21 -0
- package/dist/agent-chat/tool-chip.d.ts.map +1 -0
- package/dist/agent-chat/tool-chip.js +120 -0
- package/dist/cli/index.d.ts +33 -0
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +131 -0
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +1 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/surfaces.d.ts +11 -1
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +22 -1
- package/llms.txt +10 -3
- package/package.json +6 -1
- package/src/agent/ai-sdk-transport.test.ts +567 -0
- package/src/agent/ai-sdk-transport.ts +206 -53
- package/src/agent/coalesce.test.ts +194 -0
- package/src/agent/coalesce.ts +118 -0
- package/src/agent/foreign.test.ts +47 -0
- package/src/agent/foreign.ts +140 -0
- package/src/agent/history.ts +2 -0
- package/src/agent/id.test.ts +71 -0
- package/src/agent/id.ts +43 -0
- package/src/agent/index.ts +26 -3
- package/src/agent/merge.test.ts +119 -0
- package/src/agent/merge.ts +67 -0
- package/src/agent/part-list.test.tsx +82 -0
- package/src/agent/part-list.tsx +31 -6
- package/src/agent/parts.test.ts +603 -0
- package/src/agent/parts.ts +262 -32
- package/src/agent/thread.test.ts +147 -0
- package/src/agent/transport.test.ts +70 -0
- package/src/agent/transport.ts +72 -20
- package/src/agent/use-agent-stream.test.tsx +261 -0
- package/src/agent/use-agent-stream.ts +13 -3
- package/src/agent/use-agent-thread-runs.foreign.test.tsx +133 -0
- package/src/agent/use-agent-thread-runs.resume.test.tsx +289 -0
- package/src/agent/use-agent-thread-runs.test.tsx +781 -0
- package/src/agent/use-agent-thread-runs.ts +177 -27
- package/src/agent/use-agent-thread-runs.wedge.test.tsx +301 -0
- package/src/agent-chat/index.ts +8 -3
- package/src/agent-chat/thread-message.test.tsx +211 -0
- package/src/agent-chat/thread-message.tsx +235 -62
- package/src/agent-chat/tool-chip.test.tsx +139 -0
- package/src/agent-chat/tool-chip.tsx +219 -0
- package/src/charts/primitives/ChartFrame.test.tsx +8 -5
- package/src/cli/index.ts +285 -0
- package/src/dashboard/stat-card.test.tsx +5 -3
- package/src/guard/check-source.test.ts +8 -4
- package/src/guard/index.ts +1 -3
- package/src/index.ts +2 -0
- package/src/provider/index.test.tsx +6 -5
- package/src/surfaces.ts +23 -1
- package/src/theme/use-basalt-spacing.test.tsx +7 -4
- package/src/tokens/build-fonts-css.test.ts +3 -2
package/dist/agent/parts.js
CHANGED
|
@@ -1,49 +1,166 @@
|
|
|
1
|
+
const TERMINAL_TOOL_STATES = ["output-available", "output-error", "output-denied"];
|
|
2
|
+
function isToolCallSettled(part) {
|
|
3
|
+
return TERMINAL_TOOL_STATES.includes(part.state);
|
|
4
|
+
}
|
|
1
5
|
function parseAgentPart(raw) {
|
|
2
6
|
if (raw === null || typeof raw !== "object") return null;
|
|
3
7
|
const obj = raw;
|
|
4
8
|
if (typeof obj["type"] !== "string") return null;
|
|
9
|
+
if (typeof obj["id"] !== "string") return null;
|
|
10
|
+
const id = obj["id"];
|
|
5
11
|
switch (obj["type"]) {
|
|
6
12
|
case "start":
|
|
7
13
|
if (typeof obj["runId"] !== "string") return null;
|
|
8
14
|
return {
|
|
15
|
+
id,
|
|
9
16
|
type: "start",
|
|
10
17
|
runId: obj["runId"],
|
|
11
18
|
...typeof obj["resumeToken"] === "string" ? { resumeToken: obj["resumeToken"] } : {}
|
|
12
19
|
};
|
|
13
20
|
case "text":
|
|
14
21
|
if (typeof obj["text"] !== "string") return null;
|
|
15
|
-
return {
|
|
22
|
+
return {
|
|
23
|
+
id,
|
|
24
|
+
type: "text",
|
|
25
|
+
text: obj["text"],
|
|
26
|
+
...typeof obj["offset"] === "number" ? { offset: obj["offset"] } : {}
|
|
27
|
+
};
|
|
16
28
|
case "reasoning":
|
|
17
29
|
if (typeof obj["text"] !== "string") return null;
|
|
18
|
-
return { type: "reasoning", text: obj["text"] };
|
|
19
|
-
case "tool":
|
|
20
|
-
if (typeof obj["toolName"] !== "string") return null;
|
|
21
|
-
if (!("input" in obj)) return null;
|
|
22
30
|
return {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
...obj["
|
|
27
|
-
...typeof obj["toolCallId"] === "string" ? { toolCallId: obj["toolCallId"] } : {}
|
|
31
|
+
id,
|
|
32
|
+
type: "reasoning",
|
|
33
|
+
text: obj["text"],
|
|
34
|
+
...typeof obj["offset"] === "number" ? { offset: obj["offset"] } : {}
|
|
28
35
|
};
|
|
36
|
+
case "tool":
|
|
37
|
+
return parseToolCallPart(id, obj);
|
|
29
38
|
case "source":
|
|
30
39
|
if (typeof obj["url"] !== "string") return null;
|
|
31
40
|
return {
|
|
41
|
+
id,
|
|
32
42
|
type: "source",
|
|
33
43
|
url: obj["url"],
|
|
34
44
|
...typeof obj["title"] === "string" ? { title: obj["title"] } : {}
|
|
35
45
|
};
|
|
36
46
|
case "error":
|
|
37
47
|
if (typeof obj["message"] !== "string") return null;
|
|
38
|
-
return { type: "error", message: obj["message"] };
|
|
48
|
+
return { id, type: "error", message: obj["message"] };
|
|
39
49
|
default:
|
|
40
50
|
return null;
|
|
41
51
|
}
|
|
42
52
|
}
|
|
53
|
+
function parseToolCallPart(id, obj) {
|
|
54
|
+
if (typeof obj["toolCallId"] !== "string") return null;
|
|
55
|
+
if (typeof obj["toolName"] !== "string") return null;
|
|
56
|
+
if (typeof obj["state"] !== "string") return null;
|
|
57
|
+
const { toolCallId, toolName } = obj;
|
|
58
|
+
const common = {
|
|
59
|
+
id,
|
|
60
|
+
type: "tool",
|
|
61
|
+
toolCallId,
|
|
62
|
+
toolName,
|
|
63
|
+
...typeof obj["durationMs"] === "number" ? { durationMs: obj["durationMs"] } : {},
|
|
64
|
+
...typeof obj["providerExecuted"] === "boolean" ? { providerExecuted: obj["providerExecuted"] } : {}
|
|
65
|
+
};
|
|
66
|
+
switch (obj["state"]) {
|
|
67
|
+
case "input-streaming":
|
|
68
|
+
return {
|
|
69
|
+
...common,
|
|
70
|
+
state: "input-streaming",
|
|
71
|
+
..."input" in obj ? { input: obj["input"] } : {}
|
|
72
|
+
};
|
|
73
|
+
case "input-available":
|
|
74
|
+
if (!("input" in obj)) return null;
|
|
75
|
+
return { ...common, state: "input-available", input: obj["input"] };
|
|
76
|
+
case "approval-requested": {
|
|
77
|
+
if (!("input" in obj)) return null;
|
|
78
|
+
const approval = parseApproval(obj["approval"]);
|
|
79
|
+
if (approval === null || approval.approved !== void 0 || approval.reason !== void 0) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
...common,
|
|
84
|
+
state: "approval-requested",
|
|
85
|
+
input: obj["input"],
|
|
86
|
+
approval
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
case "approval-responded": {
|
|
90
|
+
if (!("input" in obj)) return null;
|
|
91
|
+
const approval = parseApproval(obj["approval"]);
|
|
92
|
+
if (approval === null || typeof approval.approved !== "boolean") return null;
|
|
93
|
+
return {
|
|
94
|
+
...common,
|
|
95
|
+
state: "approval-responded",
|
|
96
|
+
input: obj["input"],
|
|
97
|
+
approval
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
case "output-denied": {
|
|
101
|
+
if (!("input" in obj)) return null;
|
|
102
|
+
const approval = parseApproval(obj["approval"]);
|
|
103
|
+
if (approval === null || approval.approved !== false) return null;
|
|
104
|
+
return {
|
|
105
|
+
...common,
|
|
106
|
+
state: "output-denied",
|
|
107
|
+
input: obj["input"],
|
|
108
|
+
approval
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
case "output-available": {
|
|
112
|
+
if (!("input" in obj)) return null;
|
|
113
|
+
if (!("output" in obj)) return null;
|
|
114
|
+
const approval = obj["approval"] === void 0 ? null : parseApproval(obj["approval"]);
|
|
115
|
+
if (obj["approval"] !== void 0 && (approval === null || approval.approved === false)) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
...common,
|
|
120
|
+
state: "output-available",
|
|
121
|
+
input: obj["input"],
|
|
122
|
+
output: obj["output"],
|
|
123
|
+
...typeof obj["preliminary"] === "boolean" ? { preliminary: obj["preliminary"] } : {},
|
|
124
|
+
...approval !== null ? { approval } : {}
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
case "output-error": {
|
|
128
|
+
if (typeof obj["errorText"] !== "string") return null;
|
|
129
|
+
const approval = obj["approval"] === void 0 ? null : parseApproval(obj["approval"]);
|
|
130
|
+
if (obj["approval"] !== void 0 && (approval === null || approval.approved === false)) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
...common,
|
|
135
|
+
state: "output-error",
|
|
136
|
+
errorText: obj["errorText"],
|
|
137
|
+
..."input" in obj ? { input: obj["input"] } : {},
|
|
138
|
+
..."rawInput" in obj ? { rawInput: obj["rawInput"] } : {},
|
|
139
|
+
...approval !== null ? { approval } : {}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
default:
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
function parseApproval(raw) {
|
|
147
|
+
if (raw === null || typeof raw !== "object") return null;
|
|
148
|
+
const obj = raw;
|
|
149
|
+
if (typeof obj["id"] !== "string") return null;
|
|
150
|
+
return {
|
|
151
|
+
id: obj["id"],
|
|
152
|
+
...typeof obj["approved"] === "boolean" ? { approved: obj["approved"] } : {},
|
|
153
|
+
...typeof obj["reason"] === "string" ? { reason: obj["reason"] } : {},
|
|
154
|
+
...typeof obj["isAutomatic"] === "boolean" ? { isAutomatic: obj["isAutomatic"] } : {},
|
|
155
|
+
...typeof obj["signature"] === "string" ? { signature: obj["signature"] } : {}
|
|
156
|
+
};
|
|
157
|
+
}
|
|
43
158
|
function isStartPart(part) {
|
|
44
159
|
return typeof part === "object" && part !== null && part.type === "start";
|
|
45
160
|
}
|
|
46
161
|
export {
|
|
162
|
+
TERMINAL_TOOL_STATES,
|
|
47
163
|
isStartPart,
|
|
164
|
+
isToolCallSettled,
|
|
48
165
|
parseAgentPart
|
|
49
166
|
};
|
|
@@ -29,11 +29,16 @@
|
|
|
29
29
|
* api.chat.post({ body: { message: input }, fetch: { signal } }),
|
|
30
30
|
* )
|
|
31
31
|
*/
|
|
32
|
-
import type { AgentPart } from './parts.js';
|
|
32
|
+
import type { AgentPart, AgentPartDraft } from './parts.js';
|
|
33
33
|
/**
|
|
34
34
|
* The injected transport seam. `stream(input, signal)` returns an async generator of parts.
|
|
35
35
|
* Implement this interface for any backend — Eden, fetch-based SSE, AI SDK, or a mock.
|
|
36
36
|
*
|
|
37
|
+
* The default `TPart` is `AgentPartDraft` (not `AgentPart`): transports yield DRAFTS — `id`
|
|
38
|
+
* optional — and the consuming hooks (or `withPartIds`) normalize a draft into a fully-identified
|
|
39
|
+
* part before it is ever accumulated. Pass an explicit `AgentPart` generic when a transport mints
|
|
40
|
+
* its own ids directly (as `aiSdkTransport` does).
|
|
41
|
+
*
|
|
37
42
|
* @example
|
|
38
43
|
* // Minimal mock transport for tests / playground:
|
|
39
44
|
* const mockTransport: AgentTransport = {
|
|
@@ -42,11 +47,33 @@ import type { AgentPart } from './parts.js';
|
|
|
42
47
|
* },
|
|
43
48
|
* }
|
|
44
49
|
*/
|
|
45
|
-
export type AgentTransport<TPart =
|
|
50
|
+
export type AgentTransport<TPart = AgentPartDraft, TInput = string> = {
|
|
46
51
|
stream: (input: TInput, signal?: AbortSignal) => AsyncGenerator<TPart>;
|
|
47
52
|
/** Optional: resume a previously-started run from its resumeToken (see StartPart). Transports that don't support resumption simply omit this. */
|
|
48
53
|
resume?: (resumeToken: string, signal?: AbortSignal) => AsyncGenerator<TPart>;
|
|
49
54
|
};
|
|
55
|
+
/**
|
|
56
|
+
* A transport whose replay is safe to run over an already-rendered turn. Implementing `resume`
|
|
57
|
+
* alone is no longer enough: `idempotentReplay: true` is a literal-typed assertion that every
|
|
58
|
+
* text/reasoning part this transport emits carries an authoritative `offset`, so mergePart
|
|
59
|
+
* rewrites instead of appending.
|
|
60
|
+
*/
|
|
61
|
+
export type ResumableAgentTransport<TPart = AgentPartDraft, TInput = string> = AgentTransport<TPart, TInput> & {
|
|
62
|
+
readonly resume: (resumeToken: string, signal?: AbortSignal) => AsyncGenerator<TPart>;
|
|
63
|
+
readonly idempotentReplay: true;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Type guard: true when `t` both implements `resume` AND has explicitly asserted
|
|
67
|
+
* `idempotentReplay: true`. A transport with `resume` but no `idempotentReplay` is NOT resumable
|
|
68
|
+
* — the mount-time reconcile in `useAgentThreadRuns` falls back to `'interrupted'` for it rather
|
|
69
|
+
* than risk duplicating already-rendered content.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* if (isResumable(transport)) {
|
|
73
|
+
* for await (const part of transport.resume(resumeToken)) { ... }
|
|
74
|
+
* }
|
|
75
|
+
*/
|
|
76
|
+
export declare function isResumable<TPart, TInput>(t: AgentTransport<TPart, TInput>): t is ResumableAgentTransport<TPart, TInput>;
|
|
50
77
|
/**
|
|
51
78
|
* Wraps a consumer's Eden async-generator call into an AgentTransport.
|
|
52
79
|
*
|
|
@@ -64,6 +91,12 @@ export type AgentTransport<TPart = AgentPart, TInput = string> = {
|
|
|
64
91
|
* object has NO `resume` key at all (not `resume: undefined`) — consumers that don't wire a
|
|
65
92
|
* resumable backend get a transport that behaves exactly as before.
|
|
66
93
|
*
|
|
94
|
+
* `resumeCall` alone does NOT make the transport resumable per `isResumable` — pass
|
|
95
|
+
* `{ idempotentReplay: true }` as the third argument to opt in EXPLICITLY once the server-side
|
|
96
|
+
* `resumeCall` route is verified to re-emit every text/reasoning delta with an authoritative
|
|
97
|
+
* `offset` (never a bare append). This is an opt-in, not an inference: a `resumeCall` whose deltas
|
|
98
|
+
* aren't offset-accurate would double-render on replay if treated as resumable.
|
|
99
|
+
*
|
|
67
100
|
* @example
|
|
68
101
|
* import { edenTransport, type AgentPart } from 'basalt-ui/agent'
|
|
69
102
|
*
|
|
@@ -72,6 +105,7 @@ export type AgentTransport<TPart = AgentPart, TInput = string> = {
|
|
|
72
105
|
* const transport = edenTransport<AgentPart>(
|
|
73
106
|
* (input, signal) => api.chat.post({ body: { message: input }, fetch: { signal } }),
|
|
74
107
|
* (resumeToken, signal) => api.chat.resume.post({ body: { resumeToken }, fetch: { signal } }),
|
|
108
|
+
* { idempotentReplay: true },
|
|
75
109
|
* )
|
|
76
110
|
*
|
|
77
111
|
* // Wire into useAgentStream:
|
|
@@ -83,5 +117,7 @@ export declare function edenTransport<TPart = AgentPart, TInput = string>(call:
|
|
|
83
117
|
}>, resumeCall?: (resumeToken: string, signal?: AbortSignal) => Promise<{
|
|
84
118
|
data: AsyncGenerator<TPart> | null;
|
|
85
119
|
error: unknown;
|
|
86
|
-
}
|
|
120
|
+
}>, options?: {
|
|
121
|
+
readonly idempotentReplay?: true;
|
|
122
|
+
}): AgentTransport<TPart, TInput> | ResumableAgentTransport<TPart, TInput>;
|
|
87
123
|
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/agent/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/agent/transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAIxD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,cAAc,CAAC,KAAK,GAAG,cAAc,EAAE,MAAM,GAAG,MAAM,IAAI;IACpE,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,cAAc,CAAC,KAAK,CAAC,CAAA;IACtE,iJAAiJ;IACjJ,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,cAAc,CAAC,KAAK,CAAC,CAAA;CAC9E,CAAA;AAID;;;;;GAKG;AACH,MAAM,MAAM,uBAAuB,CAAC,KAAK,GAAG,cAAc,EAAE,MAAM,GAAG,MAAM,IAAI,cAAc,CAC3F,KAAK,EACL,MAAM,CACP,GAAG;IACF,QAAQ,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,cAAc,CAAC,KAAK,CAAC,CAAA;IACrF,QAAQ,CAAC,gBAAgB,EAAE,IAAI,CAAA;CAChC,CAAA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EACvC,CAAC,EAAE,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,GAC/B,CAAC,IAAI,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,CAK7C;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,aAAa,CAAC,KAAK,GAAG,SAAS,EAAE,MAAM,GAAG,MAAM,EAC9D,IAAI,EAAE,CACJ,KAAK,EAAE,MAAM,EACb,MAAM,CAAC,EAAE,WAAW,KACjB,OAAO,CAAC;IAAE,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,EACpE,UAAU,CAAC,EAAE,CACX,WAAW,EAAE,MAAM,EACnB,MAAM,CAAC,EAAE,WAAW,KACjB,OAAO,CAAC;IAAE,IAAI,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAC,EACpE,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,IAAI,CAAA;CAAE,GAC7C,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,CAsBxE"}
|
package/dist/agent/transport.js
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
|
-
function
|
|
2
|
-
return
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
function isResumable(t) {
|
|
2
|
+
return typeof t.resume === "function" && t.idempotentReplay === true;
|
|
3
|
+
}
|
|
4
|
+
function edenTransport(call, resumeCall, options) {
|
|
5
|
+
const stream = async function* (input, signal) {
|
|
6
|
+
const { data, error } = await call(input, signal);
|
|
7
|
+
if (error) throw error;
|
|
8
|
+
if (data === null) return;
|
|
9
|
+
yield* data;
|
|
10
|
+
};
|
|
11
|
+
if (resumeCall === void 0) return { stream };
|
|
12
|
+
const resume = async function* (resumeToken, signal) {
|
|
13
|
+
const { data, error } = await resumeCall(resumeToken, signal);
|
|
14
|
+
if (error) throw error;
|
|
15
|
+
if (data === null) return;
|
|
16
|
+
yield* data;
|
|
17
17
|
};
|
|
18
|
+
if (options?.idempotentReplay !== true) return { stream, resume };
|
|
19
|
+
return { stream, resume, idempotentReplay: true };
|
|
18
20
|
}
|
|
19
21
|
export {
|
|
20
|
-
edenTransport
|
|
22
|
+
edenTransport,
|
|
23
|
+
isResumable
|
|
21
24
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { PartLike } from './merge.js';
|
|
1
2
|
import type { AgentPart } from './parts.js';
|
|
2
3
|
import type { AgentTransport } from './transport.js';
|
|
3
4
|
/** The lifecycle state of the current stream. */
|
|
@@ -28,7 +29,7 @@ export type UseAgentStreamReturn<TPart, TInput> = {
|
|
|
28
29
|
* // parts accumulates as the stream arrives
|
|
29
30
|
* // status: 'idle' → 'streaming' → 'done' (or 'error')
|
|
30
31
|
*/
|
|
31
|
-
export declare function useAgentStream<TPart = AgentPart, TInput = string>({ transport, }: {
|
|
32
|
+
export declare function useAgentStream<TPart extends PartLike = AgentPart, TInput = string>({ transport, }: {
|
|
32
33
|
transport: AgentTransport<TPart, TInput>;
|
|
33
34
|
}): UseAgentStreamReturn<TPart, TInput>;
|
|
34
35
|
//# sourceMappingURL=use-agent-stream.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-agent-stream.d.ts","sourceRoot":"","sources":["../../src/agent/use-agent-stream.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-agent-stream.d.ts","sourceRoot":"","sources":["../../src/agent/use-agent-stream.ts"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACxC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAIjD,iDAAiD;AACjD,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,WAAW,GAAG,MAAM,GAAG,OAAO,CAAA;AAIlE,MAAM,MAAM,oBAAoB,CAAC,KAAK,EAAE,MAAM,IAAI;IAChD,qEAAqE;IACrE,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,CAAA;IACvB,gCAAgC;IAChC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAA;IAC7B,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/C,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,IAAI,CAAA;IACzB,kEAAkE;IAClE,QAAQ,CAAC,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CACzC,CAAA;AAID;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,KAAK,SAAS,QAAQ,GAAG,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,EAClF,SAAS,GACV,EAAE;IACD,SAAS,EAAE,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;CACzC,GAAG,oBAAoB,CAAC,KAAK,EAAE,MAAM,CAAC,CA0EtC"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { withPartIds } from "./id.js";
|
|
2
3
|
import { isStartPart } from "./parts.js";
|
|
4
|
+
import { mergePart } from "./merge.js";
|
|
3
5
|
function useAgentStream({
|
|
4
6
|
transport
|
|
5
7
|
}) {
|
|
@@ -23,12 +25,13 @@ function useAgentStream({
|
|
|
23
25
|
setParts([]);
|
|
24
26
|
setError(void 0);
|
|
25
27
|
setStatus("streaming");
|
|
28
|
+
const runId = crypto.randomUUID();
|
|
26
29
|
try {
|
|
27
|
-
for await (const part of transport.stream(input, controller.signal)) {
|
|
30
|
+
for await (const part of withPartIds(runId, transport.stream(input, controller.signal))) {
|
|
28
31
|
if (controllerRef.current !== controller) return;
|
|
29
32
|
if (controller.signal.aborted) break;
|
|
30
33
|
if (isStartPart(part)) continue;
|
|
31
|
-
setParts((prev) =>
|
|
34
|
+
setParts((prev) => mergePart(prev, part));
|
|
32
35
|
}
|
|
33
36
|
if (controllerRef.current === controller && !controller.signal.aborted) setStatus("done");
|
|
34
37
|
} catch (err) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { PartLike } from './merge.js';
|
|
1
2
|
import type { OutcomeResolver } from './outcome.js';
|
|
2
3
|
import type { AgentPart } from './parts.js';
|
|
3
4
|
import type { ThreadsStore } from './thread.js';
|
|
@@ -52,9 +53,14 @@ export type UseAgentThreadRunsReturn<TPart = AgentPart> = {
|
|
|
52
53
|
* `threadId` has never had a turn started, or already has one in flight.
|
|
53
54
|
*/
|
|
54
55
|
readonly retry: (threadId: string) => void;
|
|
55
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* Abort the in-flight stream for `threadId` (no-op if idle). Preserves whatever content had
|
|
58
|
+
* already arrived: if the run had accumulated any parts, they're persisted as an assistant
|
|
59
|
+
* ChatMessage with `finish: 'stopped'` (unless that message already landed — see finalizeStop),
|
|
60
|
+
* then distilled into an outcome and the thread is settled to 'done'.
|
|
61
|
+
*/
|
|
56
62
|
readonly stop: (threadId: string) => void;
|
|
57
|
-
/** Abort every in-flight stream across all threads. */
|
|
63
|
+
/** Abort every in-flight stream across all threads, settling each the same way stop() would. */
|
|
58
64
|
readonly stopAll: () => void;
|
|
59
65
|
};
|
|
60
66
|
/**
|
|
@@ -68,5 +74,5 @@ export type UseAgentThreadRunsReturn<TPart = AgentPart> = {
|
|
|
68
74
|
* start(threadB, 'What changed in the last release?')
|
|
69
75
|
* // both stream concurrently; runs.get(threadA) / runs.get(threadB) update independently
|
|
70
76
|
*/
|
|
71
|
-
export declare function useAgentThreadRuns<TPart = AgentPart>({ transport, store, resolveOutcome, toUserParts, }: UseAgentThreadRunsArgs<TPart>): UseAgentThreadRunsReturn<TPart>;
|
|
77
|
+
export declare function useAgentThreadRuns<TPart extends PartLike = AgentPart>({ transport, store, resolveOutcome, toUserParts, }: UseAgentThreadRunsArgs<TPart>): UseAgentThreadRunsReturn<TPart>;
|
|
72
78
|
//# sourceMappingURL=use-agent-thread-runs.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-agent-thread-runs.d.ts","sourceRoot":"","sources":["../../src/agent/use-agent-thread-runs.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"use-agent-thread-runs.d.ts","sourceRoot":"","sources":["../../src/agent/use-agent-thread-runs.ts"],"names":[],"mappings":"AA4CA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AACvC,OAAO,KAAK,EAAgB,eAAe,EAAE,MAAM,WAAW,CAAA;AAE9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACxC,OAAO,KAAK,EAA6B,YAAY,EAAE,MAAM,UAAU,CAAA;AAEvE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAIjD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,cAAc,CAAC,KAAK,GAAG,SAAS,IAAI;IAC9C,yFAAyF;IACzF,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;IAC5B,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,CAAA;CACxB,CAAA;AAID,MAAM,MAAM,sBAAsB,CAAC,KAAK,GAAG,SAAS,IAAI;IACtD;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,SAAS,EACd,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,GAC7B,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,cAAc,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAA;IACzD,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,CAAA;IACnC,uFAAuF;IACvF,QAAQ,CAAC,cAAc,EAAE,eAAe,CAAC,KAAK,CAAC,CAAA;IAC/C;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,KAAK,EAAE,CAAA;CAClD,CAAA;AAID,MAAM,MAAM,wBAAwB,CAAC,KAAK,GAAG,SAAS,IAAI;IACxD,6FAA6F;IAC7F,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,CAAA;IACzD,2FAA2F;IAC3F,QAAQ,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACzD;;;OAGG;IACH,QAAQ,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C;;;;;OAKG;IACH,QAAQ,CAAC,IAAI,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IACzC,gGAAgG;IAChG,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAA;CAC7B,CAAA;AAkND;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,SAAS,QAAQ,GAAG,SAAS,EAAE,EACrE,SAAS,EACT,KAAK,EACL,cAAc,EACd,WAAW,GACZ,EAAE,sBAAsB,CAAC,KAAK,CAAC,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAkPjE"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { withPartIds } from "./id.js";
|
|
3
|
+
import { mergePart } from "./merge.js";
|
|
2
4
|
import { isStartPart } from "./parts.js";
|
|
5
|
+
import { isResumable } from "./transport.js";
|
|
3
6
|
function defaultToUserParts(input) {
|
|
4
|
-
return [{ type: "text", text: input }];
|
|
7
|
+
return [{ id: crypto.randomUUID(), type: "text", text: input }];
|
|
5
8
|
}
|
|
6
9
|
function buildDoneSnapshot(priorThread, threadId, userMessage, assistantMessage) {
|
|
7
10
|
const updatedAt = Date.now();
|
|
@@ -30,12 +33,13 @@ async function consumeAndFinalize(args) {
|
|
|
30
33
|
generator,
|
|
31
34
|
userMessage,
|
|
32
35
|
controllersRef,
|
|
36
|
+
appendedRef,
|
|
33
37
|
storeRef,
|
|
34
38
|
resolveOutcome,
|
|
35
39
|
setRuns,
|
|
36
40
|
onFailureStatus
|
|
37
41
|
} = args;
|
|
38
|
-
|
|
42
|
+
let parts = [];
|
|
39
43
|
try {
|
|
40
44
|
for await (const part of generator) {
|
|
41
45
|
if (controllersRef.current.get(threadId) !== controller) return;
|
|
@@ -44,10 +48,10 @@ async function consumeAndFinalize(args) {
|
|
|
44
48
|
storeRef.current.setResumeToken(threadId, part.resumeToken);
|
|
45
49
|
continue;
|
|
46
50
|
}
|
|
47
|
-
parts
|
|
51
|
+
parts = mergePart(parts, part);
|
|
48
52
|
setRuns((prev) => {
|
|
49
53
|
const next = new Map(prev);
|
|
50
|
-
next.set(threadId, { status: "streaming", parts
|
|
54
|
+
next.set(threadId, { status: "streaming", parts });
|
|
51
55
|
return next;
|
|
52
56
|
});
|
|
53
57
|
}
|
|
@@ -58,16 +62,22 @@ async function consumeAndFinalize(args) {
|
|
|
58
62
|
id: crypto.randomUUID(),
|
|
59
63
|
role: "assistant",
|
|
60
64
|
parts,
|
|
61
|
-
createdAt: Date.now()
|
|
65
|
+
createdAt: Date.now(),
|
|
66
|
+
finish: "complete"
|
|
62
67
|
};
|
|
63
68
|
storeRef.current.appendMessage(threadId, assistantMessage);
|
|
69
|
+
appendedRef.current.set(threadId, controller);
|
|
64
70
|
const priorThread = storeRef.current.threads.find((thread) => thread.id === threadId);
|
|
65
71
|
const snapshot = buildDoneSnapshot(priorThread, threadId, userMessage, assistantMessage);
|
|
66
72
|
const outcome = await resolveOutcome(snapshot);
|
|
73
|
+
if (controllersRef.current.get(threadId) !== controller || controller.signal.aborted) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
67
76
|
storeRef.current.setOutcome(threadId, outcome);
|
|
68
77
|
storeRef.current.setStatus(threadId, outcome.status);
|
|
69
78
|
storeRef.current.setResumeToken(threadId, void 0);
|
|
70
79
|
controllersRef.current.delete(threadId);
|
|
80
|
+
appendedRef.current.delete(threadId);
|
|
71
81
|
setRuns((prev) => {
|
|
72
82
|
if (!prev.has(threadId)) return prev;
|
|
73
83
|
const next = new Map(prev);
|
|
@@ -80,6 +90,7 @@ async function consumeAndFinalize(args) {
|
|
|
80
90
|
if (controller.signal.aborted) return;
|
|
81
91
|
storeRef.current.setStatus(threadId, onFailureStatus);
|
|
82
92
|
controllersRef.current.delete(threadId);
|
|
93
|
+
appendedRef.current.delete(threadId);
|
|
83
94
|
setRuns((prev) => {
|
|
84
95
|
if (!prev.has(threadId)) return prev;
|
|
85
96
|
const next = new Map(prev);
|
|
@@ -88,6 +99,31 @@ async function consumeAndFinalize(args) {
|
|
|
88
99
|
});
|
|
89
100
|
}
|
|
90
101
|
}
|
|
102
|
+
async function finalizeStop(args) {
|
|
103
|
+
const { threadId, parts, alreadyAppended, storeRef, resolveOutcome, setRuns } = args;
|
|
104
|
+
if (!alreadyAppended && parts.length > 0) {
|
|
105
|
+
const stoppedMessage = {
|
|
106
|
+
id: crypto.randomUUID(),
|
|
107
|
+
role: "assistant",
|
|
108
|
+
parts,
|
|
109
|
+
createdAt: Date.now(),
|
|
110
|
+
finish: "stopped"
|
|
111
|
+
};
|
|
112
|
+
storeRef.current.appendMessage(threadId, stoppedMessage);
|
|
113
|
+
}
|
|
114
|
+
storeRef.current.setStatus(threadId, "done");
|
|
115
|
+
storeRef.current.setResumeToken(threadId, void 0);
|
|
116
|
+
setRuns((prev) => {
|
|
117
|
+
if (!prev.has(threadId)) return prev;
|
|
118
|
+
const next = new Map(prev);
|
|
119
|
+
next.delete(threadId);
|
|
120
|
+
return next;
|
|
121
|
+
});
|
|
122
|
+
const snapshot = storeRef.current.threads.find((thread) => thread.id === threadId);
|
|
123
|
+
if (snapshot === void 0) return;
|
|
124
|
+
const outcome = await resolveOutcome(snapshot);
|
|
125
|
+
storeRef.current.setOutcome(threadId, outcome);
|
|
126
|
+
}
|
|
91
127
|
function useAgentThreadRuns({
|
|
92
128
|
transport,
|
|
93
129
|
store,
|
|
@@ -95,7 +131,10 @@ function useAgentThreadRuns({
|
|
|
95
131
|
toUserParts
|
|
96
132
|
}) {
|
|
97
133
|
const [runs, setRuns] = useState(/* @__PURE__ */ new Map());
|
|
134
|
+
const runsRef = useRef(runs);
|
|
135
|
+
runsRef.current = runs;
|
|
98
136
|
const controllersRef = useRef(/* @__PURE__ */ new Map());
|
|
137
|
+
const appendedRef = useRef(/* @__PURE__ */ new Map());
|
|
99
138
|
const lastInputRef = useRef(/* @__PURE__ */ new Map());
|
|
100
139
|
const storeRef = useRef(store);
|
|
101
140
|
storeRef.current = store;
|
|
@@ -117,6 +156,8 @@ function useAgentThreadRuns({
|
|
|
117
156
|
useEffect(
|
|
118
157
|
() => () => {
|
|
119
158
|
controllersRef.current.forEach((controller) => controller.abort());
|
|
159
|
+
controllersRef.current.clear();
|
|
160
|
+
appendedRef.current.clear();
|
|
120
161
|
},
|
|
121
162
|
[]
|
|
122
163
|
);
|
|
@@ -127,7 +168,7 @@ function useAgentThreadRuns({
|
|
|
127
168
|
const resumeToken = thread.resumeToken;
|
|
128
169
|
const lastUserMessage = thread.messages.filter((message) => message.role === "user").at(-1);
|
|
129
170
|
const resolvedTransport = resolveTransport(thread.id);
|
|
130
|
-
if (resolvedTransport
|
|
171
|
+
if (!isResumable(resolvedTransport) || resumeToken === void 0 || lastUserMessage === void 0) {
|
|
131
172
|
storeRef.current.setStatus(thread.id, "interrupted");
|
|
132
173
|
continue;
|
|
133
174
|
}
|
|
@@ -142,9 +183,17 @@ function useAgentThreadRuns({
|
|
|
142
183
|
void consumeAndFinalize({
|
|
143
184
|
threadId: thread.id,
|
|
144
185
|
controller,
|
|
145
|
-
|
|
186
|
+
// Namespaced by thread.id (stable across a thread's whole lifetime, including this
|
|
187
|
+
// resume) rather than per-call — see withPartIds' own doc for why this is NOT a replay-
|
|
188
|
+
// convergence guarantee on its own: it only protects against two id-less drafts within
|
|
189
|
+
// ONE generator colliding (the F1 bug). A transport whose resume() needs genuine replay
|
|
190
|
+
// safety must assert `idempotentReplay: true` AND mint its own content-stable ids (as
|
|
191
|
+
// aiSdkTransport does) — withPartIds is then a no-op passthrough for it, not the mechanism
|
|
192
|
+
// providing that safety.
|
|
193
|
+
generator: withPartIds(thread.id, resume(resumeToken, controller.signal)),
|
|
146
194
|
userMessage: lastUserMessage,
|
|
147
195
|
controllersRef,
|
|
196
|
+
appendedRef,
|
|
148
197
|
storeRef,
|
|
149
198
|
resolveOutcome,
|
|
150
199
|
setRuns,
|
|
@@ -175,9 +224,11 @@ function useAgentThreadRuns({
|
|
|
175
224
|
void consumeAndFinalize({
|
|
176
225
|
threadId,
|
|
177
226
|
controller,
|
|
178
|
-
|
|
227
|
+
// Namespaced by threadId — same choice, same caveat, as the mount-time resume call above.
|
|
228
|
+
generator: withPartIds(threadId, resolvedTransport.stream(input, controller.signal)),
|
|
179
229
|
userMessage,
|
|
180
230
|
controllersRef,
|
|
231
|
+
appendedRef,
|
|
181
232
|
storeRef,
|
|
182
233
|
resolveOutcome,
|
|
183
234
|
setRuns,
|
|
@@ -194,22 +245,24 @@ function useAgentThreadRuns({
|
|
|
194
245
|
},
|
|
195
246
|
[start]
|
|
196
247
|
);
|
|
197
|
-
const stop = useCallback(
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
248
|
+
const stop = useCallback(
|
|
249
|
+
(threadId) => {
|
|
250
|
+
const controller = controllersRef.current.get(threadId);
|
|
251
|
+
if (controller === void 0) return;
|
|
252
|
+
controllersRef.current.delete(threadId);
|
|
253
|
+
const parts = runsRef.current.get(threadId)?.parts ?? [];
|
|
254
|
+
controller.abort();
|
|
255
|
+
const alreadyAppended = appendedRef.current.get(threadId) === controller;
|
|
256
|
+
appendedRef.current.delete(threadId);
|
|
257
|
+
void finalizeStop({ threadId, parts, alreadyAppended, storeRef, resolveOutcome, setRuns });
|
|
258
|
+
},
|
|
259
|
+
[resolveOutcome]
|
|
260
|
+
);
|
|
208
261
|
const stopAll = useCallback(() => {
|
|
209
|
-
controllersRef.current.
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}, []);
|
|
262
|
+
for (const threadId of Array.from(controllersRef.current.keys())) {
|
|
263
|
+
stop(threadId);
|
|
264
|
+
}
|
|
265
|
+
}, [stop]);
|
|
213
266
|
return { runs, start, retry, stop, stopAll };
|
|
214
267
|
}
|
|
215
268
|
export {
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
* Mantine-styled thread-chat components over the headless `basalt-ui/agent` layer, capped by the
|
|
3
3
|
* flagship `ThreadWorkspace` composite.
|
|
4
4
|
*
|
|
5
|
-
* This module is Mantine-coupled by design and
|
|
6
|
-
*
|
|
5
|
+
* This module is Mantine-coupled by design and ships its own `basalt-ui/agent-chat` subpath
|
|
6
|
+
* (also re-exported from the root entry, unchanged for existing consumers) — unlike
|
|
7
|
+
* `basalt-ui/agent`, which stays headless so it can be styled by any consumer.
|
|
7
8
|
*
|
|
8
9
|
* @example
|
|
9
|
-
* import { ThreadWorkspace } from 'basalt-ui'
|
|
10
|
+
* import { ThreadWorkspace } from 'basalt-ui/agent-chat'
|
|
10
11
|
* import { createThreadsStore, edenTransport, heuristicOutcome } from 'basalt-ui/agent'
|
|
11
12
|
*/
|
|
12
13
|
export { ThreadWorkspace } from './thread-workspace.js';
|
|
@@ -21,4 +22,6 @@ export { Composer } from './composer.js';
|
|
|
21
22
|
export type { ComposerProps } from './composer.js';
|
|
22
23
|
export { threadPartRenderers, ThreadTranscript } from './thread-message.js';
|
|
23
24
|
export type { ThreadTranscriptProps } from './thread-message.js';
|
|
25
|
+
export { ToolChip } from './tool-chip.js';
|
|
26
|
+
export type { ToolChipProps } from './tool-chip.js';
|
|
24
27
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent-chat/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent-chat/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAG9D,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC1C,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAGpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AACzD,YAAY,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAGnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AACzD,YAAY,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAGnE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AACrC,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAG/C,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AACxE,YAAY,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AAG7D,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACtC,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA"}
|
package/dist/agent-chat/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { ThreadOutcomeCard } from "./thread-outcome-card.js";
|
|
|
4
4
|
import { ThreadDetailPanel } from "./thread-detail-panel.js";
|
|
5
5
|
import { Composer } from "./composer.js";
|
|
6
6
|
import { threadPartRenderers, ThreadTranscript } from "./thread-message.js";
|
|
7
|
+
import { ToolChip } from "./tool-chip.js";
|
|
7
8
|
export {
|
|
8
9
|
Composer,
|
|
9
10
|
ThreadDetailPanel,
|
|
@@ -11,5 +12,6 @@ export {
|
|
|
11
12
|
ThreadOutcomeCard,
|
|
12
13
|
ThreadTranscript,
|
|
13
14
|
ThreadWorkspace,
|
|
15
|
+
ToolChip,
|
|
14
16
|
threadPartRenderers
|
|
15
17
|
};
|