@ably/ai-transport 0.1.0 → 0.3.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/README.md +93 -111
- package/dist/ably-ai-transport.js +2401 -1387
- package/dist/ably-ai-transport.js.map +1 -1
- package/dist/ably-ai-transport.umd.cjs +1 -1
- package/dist/ably-ai-transport.umd.cjs.map +1 -1
- package/dist/constants.d.ts +116 -42
- package/dist/core/agent.d.ts +44 -0
- package/dist/core/channel-options.d.ts +57 -0
- package/dist/core/codec/codec-event.d.ts +9 -0
- package/dist/core/codec/decoder.d.ts +24 -24
- package/dist/core/codec/define-codec.d.ts +100 -0
- package/dist/core/codec/encoder.d.ts +10 -12
- package/dist/core/codec/field-bag.d.ts +85 -0
- package/dist/core/codec/fields.d.ts +141 -0
- package/dist/core/codec/index.d.ts +8 -2
- package/dist/core/codec/input-descriptor-decoder.d.ts +19 -0
- package/dist/core/codec/input-descriptor-encoder.d.ts +22 -0
- package/dist/core/codec/input-descriptors.d.ts +281 -0
- package/dist/core/codec/lifecycle-tracker.d.ts +10 -9
- package/dist/core/codec/output-descriptor-decoder.d.ts +29 -0
- package/dist/core/codec/output-descriptor-encoder.d.ts +31 -0
- package/dist/core/codec/output-descriptors.d.ts +237 -0
- package/dist/core/codec/types.d.ts +470 -119
- package/dist/core/codec/well-known-inputs.d.ts +52 -0
- package/dist/core/transport/agent-session.d.ts +10 -0
- package/dist/core/transport/agent-view.d.ts +296 -0
- package/dist/core/transport/client-session.d.ts +13 -0
- package/dist/core/transport/decode-fold.d.ts +55 -0
- package/dist/core/transport/headers.d.ts +121 -14
- package/dist/core/transport/index.d.ts +5 -6
- package/dist/core/transport/internal/bounded-map.d.ts +20 -0
- package/dist/core/transport/invocation.d.ts +74 -0
- package/dist/core/transport/load-history-pages.d.ts +71 -0
- package/dist/core/transport/load-history.d.ts +44 -0
- package/dist/core/transport/pipe-stream.d.ts +9 -9
- package/dist/core/transport/run-manager.d.ts +76 -0
- package/dist/core/transport/session-support.d.ts +55 -0
- package/dist/core/transport/tree.d.ts +523 -109
- package/dist/core/transport/types/agent.d.ts +375 -0
- package/dist/core/transport/types/client.d.ts +201 -0
- package/dist/core/transport/types/shared.d.ts +24 -0
- package/dist/core/transport/types/tree.d.ts +357 -0
- package/dist/core/transport/types/view.d.ts +249 -0
- package/dist/core/transport/types.d.ts +13 -553
- package/dist/core/transport/view.d.ts +390 -84
- package/dist/core/transport/wire-log.d.ts +102 -0
- package/dist/errors.d.ts +27 -10
- package/dist/index.d.ts +8 -9
- package/dist/logger.d.ts +12 -0
- package/dist/react/ably-ai-transport-react.js +1365 -1010
- package/dist/react/ably-ai-transport-react.js.map +1 -1
- package/dist/react/ably-ai-transport-react.umd.cjs +1 -1
- package/dist/react/ably-ai-transport-react.umd.cjs.map +1 -1
- package/dist/react/contexts/client-session-context.d.ts +37 -0
- package/dist/react/contexts/client-session-provider.d.ts +56 -0
- package/dist/react/create-session-hooks.d.ts +116 -0
- package/dist/react/index.d.ts +13 -12
- package/dist/react/internal/skipped-session.d.ts +8 -0
- package/dist/react/internal/use-resolved-session.d.ts +36 -0
- package/dist/react/use-ably-messages.d.ts +17 -14
- package/dist/react/use-client-session.d.ts +81 -0
- package/dist/react/use-create-view.d.ts +14 -13
- package/dist/react/use-tree.d.ts +30 -15
- package/dist/react/use-view.d.ts +81 -50
- package/dist/utils.d.ts +48 -71
- package/dist/vercel/ably-ai-transport-vercel.js +3257 -2499
- package/dist/vercel/ably-ai-transport-vercel.js.map +1 -1
- package/dist/vercel/ably-ai-transport-vercel.umd.cjs +1 -1
- package/dist/vercel/ably-ai-transport-vercel.umd.cjs.map +1 -1
- package/dist/vercel/codec/decode-lifecycle.d.ts +9 -0
- package/dist/vercel/codec/events.d.ts +50 -0
- package/dist/vercel/codec/fields.d.ts +44 -0
- package/dist/vercel/codec/fold-content.d.ts +16 -0
- package/dist/vercel/codec/fold-data.d.ts +16 -0
- package/dist/vercel/codec/fold-input.d.ts +67 -0
- package/dist/vercel/codec/fold-lifecycle.d.ts +16 -0
- package/dist/vercel/codec/fold-text.d.ts +16 -0
- package/dist/vercel/codec/fold-tool-input.d.ts +17 -0
- package/dist/vercel/codec/fold-tool-output.d.ts +16 -0
- package/dist/vercel/codec/index.d.ts +7 -20
- package/dist/vercel/codec/inputs.d.ts +11 -0
- package/dist/vercel/codec/outputs.d.ts +11 -0
- package/dist/vercel/codec/reducer-state.d.ts +121 -0
- package/dist/vercel/codec/reducer.d.ts +62 -0
- package/dist/vercel/codec/tool-transitions.d.ts +2 -8
- package/dist/vercel/codec/wire-data.d.ts +34 -0
- package/dist/vercel/index.d.ts +5 -5
- package/dist/vercel/react/ably-ai-transport-vercel-react.js +2859 -9705
- package/dist/vercel/react/ably-ai-transport-vercel-react.js.map +1 -1
- package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs +1 -45
- package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs.map +1 -1
- package/dist/vercel/react/contexts/chat-transport-context.d.ts +9 -7
- package/dist/vercel/react/contexts/chat-transport-provider.d.ts +53 -41
- package/dist/vercel/react/index.d.ts +1 -2
- package/dist/vercel/react/use-chat-transport.d.ts +30 -26
- package/dist/vercel/react/use-message-sync.d.ts +17 -30
- package/dist/vercel/run-end-reason.d.ts +84 -0
- package/dist/vercel/tool-part.d.ts +21 -0
- package/dist/vercel/transport/chat-transport.d.ts +41 -24
- package/dist/vercel/transport/index.d.ts +24 -20
- package/dist/vercel/transport/run-output-stream.d.ts +54 -0
- package/dist/version.d.ts +2 -0
- package/package.json +31 -24
- package/src/constants.ts +124 -51
- package/src/core/agent.ts +92 -0
- package/src/core/channel-options.ts +89 -0
- package/src/core/codec/codec-event.ts +27 -0
- package/src/core/codec/decoder.ts +202 -105
- package/src/core/codec/define-codec.ts +432 -0
- package/src/core/codec/encoder.ts +114 -107
- package/src/core/codec/field-bag.ts +142 -0
- package/src/core/codec/fields.ts +193 -0
- package/src/core/codec/index.ts +56 -6
- package/src/core/codec/input-descriptor-decoder.ts +97 -0
- package/src/core/codec/input-descriptor-encoder.ts +150 -0
- package/src/core/codec/input-descriptors.ts +373 -0
- package/src/core/codec/lifecycle-tracker.ts +10 -9
- package/src/core/codec/output-descriptor-decoder.ts +139 -0
- package/src/core/codec/output-descriptor-encoder.ts +101 -0
- package/src/core/codec/output-descriptors.ts +307 -0
- package/src/core/codec/types.ts +505 -126
- package/src/core/codec/well-known-inputs.ts +96 -0
- package/src/core/transport/agent-session.ts +1085 -0
- package/src/core/transport/agent-view.ts +738 -0
- package/src/core/transport/client-session.ts +780 -0
- package/src/core/transport/decode-fold.ts +101 -0
- package/src/core/transport/headers.ts +234 -22
- package/src/core/transport/index.ts +27 -27
- package/src/core/transport/internal/bounded-map.ts +27 -0
- package/src/core/transport/invocation.ts +98 -0
- package/src/core/transport/load-history-pages.ts +220 -0
- package/src/core/transport/load-history.ts +271 -0
- package/src/core/transport/pipe-stream.ts +63 -39
- package/src/core/transport/run-manager.ts +243 -0
- package/src/core/transport/session-support.ts +96 -0
- package/src/core/transport/tree.ts +1293 -308
- package/src/core/transport/types/agent.ts +434 -0
- package/src/core/transport/types/client.ts +247 -0
- package/src/core/transport/types/shared.ts +27 -0
- package/src/core/transport/types/tree.ts +393 -0
- package/src/core/transport/types/view.ts +288 -0
- package/src/core/transport/types.ts +13 -706
- package/src/core/transport/view.ts +1229 -450
- package/src/core/transport/wire-log.ts +189 -0
- package/src/errors.ts +29 -9
- package/src/event-emitter.ts +3 -2
- package/src/index.ts +86 -42
- package/src/logger.ts +14 -1
- package/src/react/contexts/client-session-context.ts +41 -0
- package/src/react/contexts/client-session-provider.tsx +222 -0
- package/src/react/create-session-hooks.ts +141 -0
- package/src/react/index.ts +24 -13
- package/src/react/internal/skipped-session.ts +62 -0
- package/src/react/internal/use-resolved-session.ts +63 -0
- package/src/react/use-ably-messages.ts +32 -22
- package/src/react/use-client-session.ts +178 -0
- package/src/react/use-create-view.ts +33 -29
- package/src/react/use-tree.ts +61 -30
- package/src/react/use-view.ts +138 -96
- package/src/utils.ts +83 -131
- package/src/vercel/codec/decode-lifecycle.ts +70 -0
- package/src/vercel/codec/events.ts +85 -0
- package/src/vercel/codec/fields.ts +58 -0
- package/src/vercel/codec/fold-content.ts +54 -0
- package/src/vercel/codec/fold-data.ts +46 -0
- package/src/vercel/codec/fold-input.ts +255 -0
- package/src/vercel/codec/fold-lifecycle.ts +85 -0
- package/src/vercel/codec/fold-text.ts +55 -0
- package/src/vercel/codec/fold-tool-input.ts +86 -0
- package/src/vercel/codec/fold-tool-output.ts +79 -0
- package/src/vercel/codec/index.ts +28 -21
- package/src/vercel/codec/inputs.ts +116 -0
- package/src/vercel/codec/outputs.ts +207 -0
- package/src/vercel/codec/reducer-state.ts +169 -0
- package/src/vercel/codec/reducer.ts +191 -0
- package/src/vercel/codec/tool-transitions.ts +3 -14
- package/src/vercel/codec/wire-data.ts +64 -0
- package/src/vercel/index.ts +7 -19
- package/src/vercel/react/contexts/chat-transport-context.ts +8 -7
- package/src/vercel/react/contexts/chat-transport-provider.tsx +87 -59
- package/src/vercel/react/index.ts +3 -5
- package/src/vercel/react/use-chat-transport.ts +44 -66
- package/src/vercel/react/use-message-sync.ts +75 -39
- package/src/vercel/run-end-reason.ts +157 -0
- package/src/vercel/tool-part.ts +25 -0
- package/src/vercel/transport/chat-transport.ts +380 -98
- package/src/vercel/transport/index.ts +38 -37
- package/src/vercel/transport/run-output-stream.ts +169 -0
- package/src/version.ts +2 -0
- package/dist/core/transport/client-transport.d.ts +0 -10
- package/dist/core/transport/decode-history.d.ts +0 -43
- package/dist/core/transport/server-transport.d.ts +0 -7
- package/dist/core/transport/stream-router.d.ts +0 -29
- package/dist/core/transport/turn-manager.d.ts +0 -37
- package/dist/react/contexts/transport-context.d.ts +0 -31
- package/dist/react/contexts/transport-provider.d.ts +0 -49
- package/dist/react/create-transport-hooks.d.ts +0 -124
- package/dist/react/use-active-turns.d.ts +0 -12
- package/dist/react/use-client-transport.d.ts +0 -80
- package/dist/vercel/codec/accumulator.d.ts +0 -21
- package/dist/vercel/codec/decoder.d.ts +0 -22
- package/dist/vercel/codec/encoder.d.ts +0 -41
- package/dist/vercel/react/use-staged-add-tool-approval-response.d.ts +0 -30
- package/dist/vercel/tool-approvals.d.ts +0 -124
- package/dist/vercel/tool-events.d.ts +0 -26
- package/src/core/transport/client-transport.ts +0 -977
- package/src/core/transport/decode-history.ts +0 -485
- package/src/core/transport/server-transport.ts +0 -612
- package/src/core/transport/stream-router.ts +0 -136
- package/src/core/transport/turn-manager.ts +0 -165
- package/src/react/contexts/transport-context.ts +0 -37
- package/src/react/contexts/transport-provider.tsx +0 -164
- package/src/react/create-transport-hooks.ts +0 -144
- package/src/react/use-active-turns.ts +0 -72
- package/src/react/use-client-transport.ts +0 -197
- package/src/vercel/codec/accumulator.ts +0 -588
- package/src/vercel/codec/decoder.ts +0 -618
- package/src/vercel/codec/encoder.ts +0 -410
- package/src/vercel/react/use-staged-add-tool-approval-response.ts +0 -87
- package/src/vercel/tool-approvals.ts +0 -380
- package/src/vercel/tool-events.ts +0 -53
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import * as Ably from 'ably';
|
|
2
|
+
import type * as AI from 'ai';
|
|
3
|
+
|
|
4
|
+
import type { RunEndReason, StreamResult } from '../core/transport/types.js';
|
|
5
|
+
import { ErrorCode } from '../errors.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The outcome of a Vercel `streamText` response piped through `Run.pipe`.
|
|
9
|
+
* Discriminated on `reason`: `'suspend'` means the run should pause; the
|
|
10
|
+
* non-`'suspend'` arms describe how it terminated, and an `'error'` outcome
|
|
11
|
+
* always carries `error`.
|
|
12
|
+
*
|
|
13
|
+
* This is a *description of what the Vercel run resulted in*, not a command to
|
|
14
|
+
* the SDK. The common case maps cleanly onto one transport action — `'suspend'`
|
|
15
|
+
* → `Run.suspend()`, everything else → `Run.end()` — and to make that case a
|
|
16
|
+
* one-liner the non-`'suspend'` arms are deliberately assignable to
|
|
17
|
+
* {@link RunEndParams}, so after a `suspend` guard the whole object passes
|
|
18
|
+
* straight to `Run.end(outcome)`. That assignability is a convenience for this
|
|
19
|
+
* adapter, not a constraint on what an outcome can mean: responding to an
|
|
20
|
+
* outcome may also involve work outside this SDK (persisting a result,
|
|
21
|
+
* notifying a human, triggering a downstream workflow), and the developer is
|
|
22
|
+
* free to do that around the terminal call.
|
|
23
|
+
*
|
|
24
|
+
* The type is Vercel-specific by design. Outcomes are the layer where agent
|
|
25
|
+
* SDKs diverge most — both in what they report (the `'suspend'` arm exists only
|
|
26
|
+
* because Vercel surfaces unexecuted tool calls as a non-terminal finish) and
|
|
27
|
+
* in what a developer must do in response. A different SDK's outcome type would
|
|
28
|
+
* have different arms; hence each adapter names its own rather than sharing a
|
|
29
|
+
* single core `RunOutcome`. The vocabulary it bottoms out in
|
|
30
|
+
* ({@link RunEndParams}, `Run.suspend`/`Run.end`) is the shared, codec-agnostic
|
|
31
|
+
* part that does live in core.
|
|
32
|
+
*/
|
|
33
|
+
export type VercelRunOutcome =
|
|
34
|
+
| {
|
|
35
|
+
/**
|
|
36
|
+
* The LLM requested tools the SDK did not auto-execute, so the run
|
|
37
|
+
* pauses rather than ending — call `Run.suspend()`.
|
|
38
|
+
*/
|
|
39
|
+
reason: 'suspend';
|
|
40
|
+
/** Never present for a suspend outcome. */
|
|
41
|
+
error?: never;
|
|
42
|
+
}
|
|
43
|
+
| {
|
|
44
|
+
/** A non-error terminal reason; pass the outcome to `Run.end()`. */
|
|
45
|
+
reason: Exclude<RunEndReason, 'error'>;
|
|
46
|
+
/** Never present for a non-error outcome. */
|
|
47
|
+
error?: never;
|
|
48
|
+
}
|
|
49
|
+
| {
|
|
50
|
+
/** The run ended in error; pass the outcome to `Run.end()`. */
|
|
51
|
+
reason: Extract<RunEndReason, 'error'>;
|
|
52
|
+
/**
|
|
53
|
+
* The terminal error: the underlying stream / `finishReason` failure
|
|
54
|
+
* wrapped as an `Ably.ErrorInfo` (code `StreamError`).
|
|
55
|
+
*/
|
|
56
|
+
error: Ably.ErrorInfo;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Derive the {@link VercelRunOutcome} for a Vercel `streamText` response that
|
|
61
|
+
* was piped through `Run.pipe`. Preserves transport-level outcomes
|
|
62
|
+
* (`'cancelled'`, `'error'`) from the pipe result; when the pipe completed
|
|
63
|
+
* naturally, awaits Vercel's `finishReason` and returns `'suspend'` for
|
|
64
|
+
* `'tool-calls'` (the LLM requested tools the SDK did not auto-execute, so the
|
|
65
|
+
* run should suspend rather than end), or `'complete'` otherwise.
|
|
66
|
+
*
|
|
67
|
+
* Surfaces the failure for both error shapes so the caller can forward it to
|
|
68
|
+
* `Run.end(reason, error)`: a stream that threw (`pipeResult.error`) and a
|
|
69
|
+
* `finishReason` that rejected with a non-abort error (e.g.
|
|
70
|
+
* `NoOutputGeneratedError`, network blow-ups). The error is wrapped as an
|
|
71
|
+
* `Ably.ErrorInfo` (code `StreamError`). A stream that already produced a
|
|
72
|
+
* codec-level error chunk is unaffected — stamping run-end is the
|
|
73
|
+
* codec-agnostic baseline that any consumer can read.
|
|
74
|
+
*
|
|
75
|
+
* Tolerates `finishReason` rejection. Vercel AI SDK v6 rejects
|
|
76
|
+
* `streamText().finishReason` with the abort signal's reason when the stream
|
|
77
|
+
* is aborted before any step completes, and rejects with
|
|
78
|
+
* `NoOutputGeneratedError` when the model produced nothing at all. Without
|
|
79
|
+
* this guard the rejection would bubble out of the route handler's `after()`
|
|
80
|
+
* block, skip the developer's `Run.end(...)` call, and leave the run with no
|
|
81
|
+
* `ai-run-end` event on the channel — so observers' UIs stay stuck on
|
|
82
|
+
* `streaming` indefinitely.
|
|
83
|
+
*
|
|
84
|
+
* Saves callers from interpreting Vercel domain semantics inline at the end
|
|
85
|
+
* of every route handler.
|
|
86
|
+
* @param pipeResult - The result returned by `Run.pipe`.
|
|
87
|
+
* @param finishReason - The `finishReason` promise from a `streamText` result.
|
|
88
|
+
* @returns The {@link VercelRunOutcome}: the terminal `reason` (or `'suspend'`)
|
|
89
|
+
* and, when `reason` is `'error'`, the wrapped `error` to pass to `Run.end`.
|
|
90
|
+
*/
|
|
91
|
+
export const vercelRunOutcome = async (
|
|
92
|
+
pipeResult: StreamResult,
|
|
93
|
+
finishReason: PromiseLike<AI.FinishReason>,
|
|
94
|
+
): Promise<VercelRunOutcome> => {
|
|
95
|
+
if (pipeResult.reason !== 'complete') {
|
|
96
|
+
// Vercel's `result.finishReason` getter creates the underlying Promise
|
|
97
|
+
// eagerly, before the caller hands it to us. When `streamText` is
|
|
98
|
+
// aborted before any step completes, Vercel rejects that Promise with
|
|
99
|
+
// the abort signal's reason — typically a DOMException whose
|
|
100
|
+
// `.message` is a read-only getter. Returning early without ever
|
|
101
|
+
// attaching a handler lets Node report it as an unhandled rejection;
|
|
102
|
+
// Next.js' dev bundler then tries to mutate `.message` for logging
|
|
103
|
+
// and crashes with a confusing TypeError. Attach a silent handler so
|
|
104
|
+
// the rejection is observed and discarded — the transport-level
|
|
105
|
+
// `pipeResult.reason` is already what we return.
|
|
106
|
+
Promise.resolve(finishReason).catch(() => {
|
|
107
|
+
/* intentionally discarded; reason already known from pipeResult */
|
|
108
|
+
});
|
|
109
|
+
if (pipeResult.reason === 'error') {
|
|
110
|
+
return { reason: 'error', error: _toErrorInfo(pipeResult.error) };
|
|
111
|
+
}
|
|
112
|
+
return { reason: pipeResult.reason };
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
const finish = await finishReason;
|
|
116
|
+
if (finish === 'tool-calls') return { reason: 'suspend' };
|
|
117
|
+
return { reason: 'complete' };
|
|
118
|
+
} catch (error) {
|
|
119
|
+
// Abort-shaped rejections are surfaced from streamText when the run was
|
|
120
|
+
// cancelled before any step finished — treat the run as cancelled so the
|
|
121
|
+
// observable lifecycle matches the cancel that triggered it. Everything
|
|
122
|
+
// else is a real error (e.g. NoOutputGeneratedError, network blow-ups);
|
|
123
|
+
// surface it as such — wrapped so the caller can stamp it on run-end — so
|
|
124
|
+
// the developer sees the failure rather than a silent cancel.
|
|
125
|
+
if (_isAbortLikeError(error)) return { reason: 'cancelled' };
|
|
126
|
+
return { reason: 'error', error: _toErrorInfo(error) };
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Wrap a caught stream / `finishReason` failure as an `Ably.ErrorInfo` so it
|
|
132
|
+
* can be passed to `Run.end(reason, error)`. An error that is already an
|
|
133
|
+
* `Ably.ErrorInfo` is returned unchanged; anything else is wrapped with code
|
|
134
|
+
* `StreamError`, mirroring how `Run.pipe` wraps stream errors for `onError`.
|
|
135
|
+
* @param error - The caught error (or `undefined` when the stream reported none).
|
|
136
|
+
* @returns The error as an `Ably.ErrorInfo`.
|
|
137
|
+
*/
|
|
138
|
+
const _toErrorInfo = (error: unknown): Ably.ErrorInfo => {
|
|
139
|
+
if (error instanceof Ably.ErrorInfo) return error;
|
|
140
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
141
|
+
return new Ably.ErrorInfo(`unable to complete run; ${message}`, ErrorCode.StreamError, 500);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Heuristic for "this error came from an AbortSignal aborting".
|
|
146
|
+
* Covers `DOMException` aborts (browser / Node 20+ `streamText`),
|
|
147
|
+
* plain `Error` objects whose `name` is `'AbortError'`, and anything
|
|
148
|
+
* else carrying that conventional name. Avoids importing
|
|
149
|
+
* `@ai-sdk/provider-utils` just for `isAbortError`.
|
|
150
|
+
* @param error - The error to test.
|
|
151
|
+
* @returns `true` if the error looks like an abort.
|
|
152
|
+
*/
|
|
153
|
+
const _isAbortLikeError = (error: unknown): boolean => {
|
|
154
|
+
if (typeof error !== 'object' || error === null) return false;
|
|
155
|
+
const name = (error as { name?: unknown }).name;
|
|
156
|
+
return name === 'AbortError';
|
|
157
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared tool-part type guard for the Vercel layer.
|
|
3
|
+
*
|
|
4
|
+
* The codec normalises every tool part to the `dynamic-tool` shape, but the AI
|
|
5
|
+
* SDK emits `tool-${name}` parts for statically-declared tools. Both shapes
|
|
6
|
+
* carry `toolCallId` and `state`. The guard accepts either representation so
|
|
7
|
+
* the transport's unresolved-tool detection and the React overlay merge can
|
|
8
|
+
* match tool parts uniformly — and so the cross-representation rule lives in
|
|
9
|
+
* one place rather than being re-spelled per call site.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type * as AI from 'ai';
|
|
13
|
+
|
|
14
|
+
/** A UIMessage tool part in either the `dynamic-tool` or `tool-${name}` representation. */
|
|
15
|
+
export type ToolPart = AI.DynamicToolUIPart | AI.ToolUIPart;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Whether a UIMessage part is a tool part of either representation. The
|
|
19
|
+
* `toolCallId`/`state` shape check is defensive against a future AI SDK release
|
|
20
|
+
* introducing a non-tool variant under the `tool-` prefix (none exists today).
|
|
21
|
+
* @param part - The UIMessage part to inspect.
|
|
22
|
+
* @returns True when the part is a tool part.
|
|
23
|
+
*/
|
|
24
|
+
export const isToolPart = (part: AI.UIMessage['parts'][number]): part is ToolPart =>
|
|
25
|
+
(part.type === 'dynamic-tool' || part.type.startsWith('tool-')) && 'toolCallId' in part && 'state' in part;
|