@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,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useClientSession — read a ClientSession from the nearest ClientSessionProvider.
|
|
3
|
+
*
|
|
4
|
+
* The session is created by {@link ClientSessionProvider}, which reads the Ably
|
|
5
|
+
* Realtime client from the surrounding `<AblyProvider>`. This hook is a thin
|
|
6
|
+
* context reader — it does not create or manage session state.
|
|
7
|
+
*
|
|
8
|
+
* **Provider lookup**
|
|
9
|
+
* - Omit `channelName` to use the innermost `ClientSessionProvider` in the tree.
|
|
10
|
+
* - Pass `channelName` to look up a specific provider by name.
|
|
11
|
+
* - Pass `skip: true` to receive a stub session that throws on any access —
|
|
12
|
+
* safe to hold in state before auth or other conditions are ready.
|
|
13
|
+
*
|
|
14
|
+
* **Error handling**
|
|
15
|
+
* - When no matching provider is found, or when the provider's `createClientSession`
|
|
16
|
+
* call threw, `sessionError` is set on the returned object instead of throwing.
|
|
17
|
+
* The component can render an error state without an error boundary.
|
|
18
|
+
* - Pass `onError` to receive post-construction session errors (e.g. send failures,
|
|
19
|
+
* channel continuity loss) without wiring `session.on('error', ...)` manually.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import * as Ably from 'ably';
|
|
23
|
+
import { useContext, useEffect, useRef } from 'react';
|
|
24
|
+
|
|
25
|
+
import type { CodecInputEvent, CodecOutputEvent } from '../core/codec/types.js';
|
|
26
|
+
import type { ClientSession } from '../core/transport/types.js';
|
|
27
|
+
import { ErrorCode } from '../errors.js';
|
|
28
|
+
import { ClientSessionContext } from './contexts/client-session-context.js';
|
|
29
|
+
import { makeSkippedClientSession } from './internal/skipped-session.js';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Return value of {@link useClientSession}.
|
|
33
|
+
*
|
|
34
|
+
* `session` is always a valid object. When `skip` is `true`, when no provider was
|
|
35
|
+
* found, or when the provider's session construction failed, `session` is a stub
|
|
36
|
+
* that throws {@link Ably.ErrorInfo} on every access.
|
|
37
|
+
* Check `sessionError` before using `session` to avoid those throws.
|
|
38
|
+
*/
|
|
39
|
+
export interface ClientSessionHandle<
|
|
40
|
+
TInput extends CodecInputEvent,
|
|
41
|
+
TOutput extends CodecOutputEvent,
|
|
42
|
+
TProjection,
|
|
43
|
+
TMessage,
|
|
44
|
+
> {
|
|
45
|
+
/**
|
|
46
|
+
* The resolved session.
|
|
47
|
+
*
|
|
48
|
+
* A throwing stub when `skip` is `true`, when no matching {@link ClientSessionProvider}
|
|
49
|
+
* was found in the tree, or when session construction failed.
|
|
50
|
+
*/
|
|
51
|
+
session: ClientSession<TInput, TOutput, TProjection, TMessage>;
|
|
52
|
+
/**
|
|
53
|
+
* Set when no matching {@link ClientSessionProvider} was found, when session
|
|
54
|
+
* construction failed, and `skip` is `false`.
|
|
55
|
+
* `undefined` when the session resolved successfully or when `skip` is `true`.
|
|
56
|
+
*/
|
|
57
|
+
sessionError?: Ably.ErrorInfo | undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Read a {@link ClientSession} from the nearest {@link ClientSessionProvider}.
|
|
62
|
+
*
|
|
63
|
+
* Returns `{ session, sessionError }`. When no provider is found or session
|
|
64
|
+
* construction failed, `sessionError` is set and `session` is a stub that throws
|
|
65
|
+
* on access — the hook never throws during render.
|
|
66
|
+
*
|
|
67
|
+
* Pass `onError` to subscribe to post-construction session errors
|
|
68
|
+
* (e.g. {@link ErrorCode.SessionSendFailed}, {@link ErrorCode.ChannelContinuityLost})
|
|
69
|
+
* without calling `session.on('error', …)` manually. The subscription is
|
|
70
|
+
* created when the session resolves and removed on unmount.
|
|
71
|
+
* @param props - Hook options.
|
|
72
|
+
* @param props.channelName - Look up a specific provider by channel name; omit for the nearest.
|
|
73
|
+
* @param props.skip - When `true`, return the stub session immediately without resolving a provider slot.
|
|
74
|
+
* @param props.onError - Called whenever the resolved session emits an error event.
|
|
75
|
+
* @returns `{ session, sessionError }`.
|
|
76
|
+
*/
|
|
77
|
+
export const useClientSession = <
|
|
78
|
+
TInput extends CodecInputEvent,
|
|
79
|
+
TOutput extends CodecOutputEvent,
|
|
80
|
+
TProjection,
|
|
81
|
+
TMessage,
|
|
82
|
+
>({
|
|
83
|
+
channelName,
|
|
84
|
+
skip,
|
|
85
|
+
onError,
|
|
86
|
+
}: {
|
|
87
|
+
/**
|
|
88
|
+
* Channel name passed to the enclosing {@link ClientSessionProvider}.
|
|
89
|
+
* Omit to use the nearest provider in the tree.
|
|
90
|
+
*/
|
|
91
|
+
channelName?: string;
|
|
92
|
+
/**
|
|
93
|
+
* When `true`, skip context lookup and return a stub session that throws on
|
|
94
|
+
* any access. Use when a condition (auth, feature flag) is not yet resolved.
|
|
95
|
+
*/
|
|
96
|
+
skip?: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Called whenever the resolved session emits an error event.
|
|
99
|
+
* The subscription is established once the session resolves and
|
|
100
|
+
* automatically removed on unmount or when the session changes.
|
|
101
|
+
*/
|
|
102
|
+
onError?: (error: Ably.ErrorInfo) => void;
|
|
103
|
+
} = {}): ClientSessionHandle<TInput, TOutput, TProjection, TMessage> => {
|
|
104
|
+
const { nearest: nearestSlot, providers } = useContext(ClientSessionContext);
|
|
105
|
+
const errorCallbackRef = useRef(onError);
|
|
106
|
+
errorCallbackRef.current = onError;
|
|
107
|
+
|
|
108
|
+
// Compute the session for the onError subscription *before* any conditional
|
|
109
|
+
// returns to satisfy React's rules of hooks (no hooks in branches).
|
|
110
|
+
// Erased generics — this local is only used in the useEffect below.
|
|
111
|
+
const resolvedForEffect: ClientSession<CodecInputEvent, CodecOutputEvent, unknown, unknown> | undefined = skip
|
|
112
|
+
? undefined
|
|
113
|
+
: channelName === undefined
|
|
114
|
+
? nearestSlot?.session
|
|
115
|
+
: providers[channelName]?.session;
|
|
116
|
+
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
if (!resolvedForEffect) return;
|
|
119
|
+
return resolvedForEffect.on('error', (errorInfo: Ably.ErrorInfo) => {
|
|
120
|
+
errorCallbackRef.current?.(errorInfo);
|
|
121
|
+
});
|
|
122
|
+
}, [resolvedForEffect]);
|
|
123
|
+
|
|
124
|
+
if (skip) {
|
|
125
|
+
return {
|
|
126
|
+
session: makeSkippedClientSession<TInput, TOutput, TProjection, TMessage>(),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (channelName !== undefined) {
|
|
131
|
+
const slot = providers[channelName];
|
|
132
|
+
if (slot) {
|
|
133
|
+
if (slot.session) {
|
|
134
|
+
// CAST: ClientSessionContext stores sessions with erased generics.
|
|
135
|
+
// The caller is responsible for using type parameters matching those of the ClientSessionProvider.
|
|
136
|
+
return {
|
|
137
|
+
session: slot.session as unknown as ClientSession<TInput, TOutput, TProjection, TMessage>,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
// Provider exists but construction failed.
|
|
141
|
+
return {
|
|
142
|
+
session: makeSkippedClientSession<TInput, TOutput, TProjection, TMessage>(),
|
|
143
|
+
sessionError: slot.sessionError,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
session: makeSkippedClientSession<TInput, TOutput, TProjection, TMessage>(),
|
|
148
|
+
sessionError: new Ably.ErrorInfo(
|
|
149
|
+
`unable to use session; no ClientSessionProvider found for channelName "${channelName}"`,
|
|
150
|
+
ErrorCode.BadRequest,
|
|
151
|
+
400,
|
|
152
|
+
),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (nearestSlot) {
|
|
157
|
+
if (nearestSlot.session) {
|
|
158
|
+
// CAST: ClientSessionContext stores session with erased generics; types fixed at call site.
|
|
159
|
+
return {
|
|
160
|
+
session: nearestSlot.session as unknown as ClientSession<TInput, TOutput, TProjection, TMessage>,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
// Nearest provider exists but construction failed.
|
|
164
|
+
return {
|
|
165
|
+
session: makeSkippedClientSession<TInput, TOutput, TProjection, TMessage>(),
|
|
166
|
+
sessionError: nearestSlot.sessionError,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
session: makeSkippedClientSession<TInput, TOutput, TProjection, TMessage>(),
|
|
172
|
+
sessionError: new Ably.ErrorInfo(
|
|
173
|
+
'unable to use session; no ClientSessionProvider found in the tree',
|
|
174
|
+
ErrorCode.BadRequest,
|
|
175
|
+
400,
|
|
176
|
+
),
|
|
177
|
+
};
|
|
178
|
+
};
|
|
@@ -1,56 +1,60 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* useCreateView — create an independent view with the same API as useView.
|
|
3
3
|
*
|
|
4
|
-
* Calls {@link
|
|
4
|
+
* Calls {@link ClientSession.createView} to create an independent view over
|
|
5
5
|
* the same conversation tree, then subscribes to it exactly like
|
|
6
6
|
* {@link useView}. The view is closed automatically on unmount or when the
|
|
7
|
-
*
|
|
7
|
+
* session reference changes.
|
|
8
8
|
*
|
|
9
|
-
* Pass `null` or omit `
|
|
10
|
-
* collapsed). The returned handle has empty state until a
|
|
11
|
-
* When `
|
|
12
|
-
* {@link
|
|
9
|
+
* Pass `null` or omit `session` to defer creation (e.g. when a split pane is
|
|
10
|
+
* collapsed). The returned handle has empty state until a session is provided.
|
|
11
|
+
* When `session` is omitted entirely, defaults to the nearest
|
|
12
|
+
* {@link ClientSessionProvider}'s session via context.
|
|
13
13
|
* Pass `skip: true` to bypass all context reads and view creation entirely.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import { useEffect, useState } from 'react';
|
|
17
17
|
|
|
18
|
-
import type {
|
|
19
|
-
import {
|
|
18
|
+
import type { CodecInputEvent, CodecOutputEvent } from '../core/codec/types.js';
|
|
19
|
+
import type { View } from '../core/transport/types.js';
|
|
20
|
+
import type { BaseSessionOption } from './internal/use-resolved-session.js';
|
|
21
|
+
import { useResolvedSession } from './internal/use-resolved-session.js';
|
|
20
22
|
import type { ViewHandle } from './use-view.js';
|
|
21
23
|
import { useView } from './use-view.js';
|
|
22
24
|
|
|
25
|
+
/** Options for {@link useCreateView}. */
|
|
26
|
+
export interface UseCreateViewOptions<
|
|
27
|
+
TInput extends CodecInputEvent,
|
|
28
|
+
TOutput extends CodecOutputEvent,
|
|
29
|
+
TProjection,
|
|
30
|
+
TMessage,
|
|
31
|
+
> extends BaseSessionOption<TInput, TOutput, TProjection, TMessage> {
|
|
32
|
+
/** When provided, auto-loads the first page on mount. Omit for manual load. */
|
|
33
|
+
limit?: number;
|
|
34
|
+
/** When `true`, skip view creation and return an empty handle immediately. */
|
|
35
|
+
skip?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
23
38
|
/**
|
|
24
39
|
* Create an independent {@link View} and subscribe to it.
|
|
25
40
|
* Returns the same {@link ViewHandle} as {@link useView}, but backed by a
|
|
26
41
|
* newly created view with its own branch selections and pagination state.
|
|
27
|
-
* The view is closed on unmount or when the
|
|
28
|
-
* When `
|
|
29
|
-
* @param props - Options including optional `
|
|
30
|
-
* @param props.
|
|
42
|
+
* The view is closed on unmount or when the session changes.
|
|
43
|
+
* When `session` is omitted, uses the nearest {@link ClientSessionProvider}'s session via context.
|
|
44
|
+
* @param props - Options including optional `session`, `limit` for auto-load, and `skip`.
|
|
45
|
+
* @param props.session - Session to create a view from; defaults to the nearest provider.
|
|
31
46
|
* @param props.limit - Max older messages per page; when provided, auto-loads on mount.
|
|
32
47
|
* @param props.skip - When `true`, skip view creation and return an empty handle.
|
|
33
48
|
* @returns A {@link ViewHandle} with nodes, pagination, navigation, and write operations.
|
|
34
49
|
*/
|
|
35
|
-
export const useCreateView = <
|
|
36
|
-
|
|
50
|
+
export const useCreateView = <TInput extends CodecInputEvent, TOutput extends CodecOutputEvent, TProjection, TMessage>({
|
|
51
|
+
session,
|
|
37
52
|
limit,
|
|
38
53
|
skip,
|
|
39
|
-
}: {
|
|
40
|
-
|
|
41
|
-
transport?: ClientTransport<TEvent, TMessage> | null;
|
|
42
|
-
/** When provided, auto-loads the first page on mount. Omit for manual load. */
|
|
43
|
-
limit?: number;
|
|
44
|
-
/** When `true`, skip view creation and return an empty handle immediately. */
|
|
45
|
-
skip?: boolean;
|
|
46
|
-
} = {}): ViewHandle<TEvent, TMessage> => {
|
|
47
|
-
const nearestSlot = useContext(NearestTransportContext);
|
|
48
|
-
// CAST: NearestTransportContext stores transport with erased generics; types fixed at call site.
|
|
49
|
-
const resolved = skip
|
|
50
|
-
? undefined
|
|
51
|
-
: ((transport ?? nearestSlot?.transport) as ClientTransport<TEvent, TMessage> | null | undefined);
|
|
54
|
+
}: UseCreateViewOptions<TInput, TOutput, TProjection, TMessage> = {}): ViewHandle<TInput, TMessage> => {
|
|
55
|
+
const resolved = useResolvedSession({ session, skip });
|
|
52
56
|
|
|
53
|
-
const [view, setView] = useState<View<
|
|
57
|
+
const [view, setView] = useState<View<TInput, TMessage> | undefined>();
|
|
54
58
|
|
|
55
59
|
useEffect(() => {
|
|
56
60
|
if (!resolved) {
|
package/src/react/use-tree.ts
CHANGED
|
@@ -1,53 +1,84 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* useTree — stable structural query callbacks for a
|
|
2
|
+
* useTree — stable structural query callbacks for a ClientSession's tree.
|
|
3
3
|
*
|
|
4
4
|
* Returns a {@link TreeHandle} with methods to inspect the tree structure.
|
|
5
|
-
* These are thin `useCallback` wrappers around `
|
|
5
|
+
* These are thin `useCallback` wrappers around `session.tree` — no local
|
|
6
6
|
* state or subscriptions. Branch navigation (select, getSelectedIndex) is
|
|
7
7
|
* on {@link ViewHandle} from {@link useView}.
|
|
8
8
|
*
|
|
9
|
-
* When `
|
|
10
|
-
* {@link
|
|
9
|
+
* When `session` is omitted, defaults to the nearest
|
|
10
|
+
* {@link ClientSessionProvider}'s session via context.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { useCallback
|
|
13
|
+
import { useCallback } from 'react';
|
|
14
14
|
|
|
15
|
-
import type {
|
|
16
|
-
import {
|
|
15
|
+
import type { CodecInputEvent, CodecOutputEvent } from '../core/codec/types.js';
|
|
16
|
+
import type { ConversationNode, RunNode } from '../core/transport/types.js';
|
|
17
|
+
import type { BaseSessionOption } from './internal/use-resolved-session.js';
|
|
18
|
+
import { useResolvedSession } from './internal/use-resolved-session.js';
|
|
17
19
|
|
|
18
20
|
/** Handle for querying the conversation tree structure. */
|
|
19
|
-
export interface TreeHandle<
|
|
20
|
-
/**
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
export interface TreeHandle<TProjection> {
|
|
22
|
+
/**
|
|
23
|
+
* Get the RunNode for `runId`, or undefined if no node is keyed by `runId`
|
|
24
|
+
* (or the keyed node is an input node, not a reply run).
|
|
25
|
+
*/
|
|
26
|
+
getRunNode: (runId: string) => RunNode<TProjection> | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Get the node that owns a given codec-message-id, or undefined if not
|
|
29
|
+
* observed. Returns a {@link ConversationNode} union — narrow on `kind`
|
|
30
|
+
* (`'input'` vs `'run'`) before reading kind-specific fields.
|
|
31
|
+
*/
|
|
32
|
+
getNodeByCodecMessageId: (codecMessageId: string) => ConversationNode<TProjection> | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Get the sibling group (both kinds) the node keyed by `key` belongs to —
|
|
35
|
+
* edit versions for an input node, regenerate runs for a reply run — ordered
|
|
36
|
+
* oldest-first. A single-element array when the node has no siblings; empty
|
|
37
|
+
* when `key` is unknown. `key` is a {@link RunNode.runId} or an
|
|
38
|
+
* {@link InputNode.codecMessageId}.
|
|
39
|
+
*/
|
|
40
|
+
getSiblingNodes: (key: string) => ConversationNode<TProjection>[];
|
|
26
41
|
}
|
|
27
42
|
|
|
43
|
+
/** Options for {@link useTree}. */
|
|
44
|
+
export type UseTreeOptions<
|
|
45
|
+
TInput extends CodecInputEvent,
|
|
46
|
+
TOutput extends CodecOutputEvent,
|
|
47
|
+
TProjection,
|
|
48
|
+
TMessage,
|
|
49
|
+
> = BaseSessionOption<TInput, TOutput, TProjection, TMessage>;
|
|
50
|
+
|
|
28
51
|
/**
|
|
29
|
-
* Provide stable structural query callbacks backed by the
|
|
30
|
-
* When `
|
|
31
|
-
* @param props - Options including optional `
|
|
32
|
-
* @param props.
|
|
52
|
+
* Provide stable structural query callbacks backed by the session's tree.
|
|
53
|
+
* When `session` is omitted, uses the nearest {@link ClientSessionProvider}'s session via context.
|
|
54
|
+
* @param props - Options including optional `session`.
|
|
55
|
+
* @param props.session - Session to read tree structure from; defaults to the nearest provider.
|
|
33
56
|
* @returns A {@link TreeHandle} with structural query methods.
|
|
34
57
|
*/
|
|
35
|
-
export const useTree = <
|
|
36
|
-
|
|
37
|
-
}:
|
|
38
|
-
const
|
|
39
|
-
// CAST: NearestTransportContext stores transport with erased generics; types fixed at call site.
|
|
40
|
-
const resolved = (transport ?? nearestSlot?.transport) as ClientTransport<TEvent, TMessage> | undefined;
|
|
58
|
+
export const useTree = <TInput extends CodecInputEvent, TOutput extends CodecOutputEvent, TProjection, TMessage>({
|
|
59
|
+
session,
|
|
60
|
+
}: UseTreeOptions<TInput, TOutput, TProjection, TMessage> = {}): TreeHandle<TProjection> => {
|
|
61
|
+
const resolved = useResolvedSession({ session });
|
|
41
62
|
|
|
42
|
-
const
|
|
63
|
+
const getRunNode = useCallback(
|
|
64
|
+
(runId: string): RunNode<TProjection> | undefined => resolved?.tree.getRunNode(runId),
|
|
65
|
+
[resolved],
|
|
66
|
+
);
|
|
43
67
|
|
|
44
|
-
const
|
|
68
|
+
const getNodeByCodecMessageId = useCallback(
|
|
69
|
+
(codecMessageId: string): ConversationNode<TProjection> | undefined =>
|
|
70
|
+
resolved?.tree.getNodeByCodecMessageId(codecMessageId),
|
|
71
|
+
[resolved],
|
|
72
|
+
);
|
|
45
73
|
|
|
46
|
-
const
|
|
74
|
+
const getSiblingNodes = useCallback(
|
|
75
|
+
(key: string): ConversationNode<TProjection>[] => resolved?.tree.getSiblingNodes(key) ?? [],
|
|
76
|
+
[resolved],
|
|
77
|
+
);
|
|
47
78
|
|
|
48
79
|
return {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
80
|
+
getRunNode,
|
|
81
|
+
getNodeByCodecMessageId,
|
|
82
|
+
getSiblingNodes,
|
|
52
83
|
};
|
|
53
84
|
};
|