@lunora/solid 1.0.0-alpha.28 → 1.0.0-alpha.280

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.
Files changed (52) hide show
  1. package/README.md +26 -17
  2. package/dist/index.d.mts +392 -76
  3. package/dist/index.d.ts +392 -76
  4. package/dist/index.mjs +1 -19
  5. package/dist/packem_shared/AuthLoading-DjUnIaP5.mjs +1 -0
  6. package/dist/packem_shared/LunoraContext-BWsj_3CK.mjs +1 -0
  7. package/dist/packem_shared/LunoraProvider-DMhpuxvx.mjs +1 -0
  8. package/dist/packem_shared/createAction-BZkayM2R.mjs +1 -0
  9. package/dist/packem_shared/createAgent-IcDj7Z5e.mjs +1 -0
  10. package/dist/packem_shared/createAgentChat-wwb8xK67.mjs +1 -0
  11. package/dist/packem_shared/createAgentState-ujXaR_s2.mjs +1 -0
  12. package/dist/packem_shared/createAgentToolEvents-BoLMpmre.mjs +1 -0
  13. package/dist/packem_shared/createConnectionStatus-D8bC05Jn.mjs +1 -0
  14. package/dist/packem_shared/createFlag-h7xYEHMy.mjs +1 -0
  15. package/dist/packem_shared/createInfiniteQuery-CrhJkyOB.mjs +1 -0
  16. package/dist/packem_shared/createLiveTopic-BBC8EZ82.mjs +1 -0
  17. package/dist/packem_shared/createMutation-DM2mXpq3.mjs +1 -0
  18. package/dist/packem_shared/createMutator-q9cMjGKv.mjs +1 -0
  19. package/dist/packem_shared/createPresence-Cs5k67wt.mjs +1 -0
  20. package/dist/packem_shared/createQuery-Ctd4NG1E.mjs +1 -0
  21. package/dist/packem_shared/createRateLimit-CIVWrD5H.mjs +1 -0
  22. package/dist/packem_shared/createStream-CVUOJMD1.mjs +1 -0
  23. package/dist/packem_shared/createSubscription-DCePkEHR.mjs +1 -0
  24. package/dist/packem_shared/createVoiceAgent-f07WSY5z.mjs +1 -0
  25. package/dist/packem_shared/hydratePreloaded-DCi296oW.mjs +1 -0
  26. package/dist/packem_shared/reactive-args-BwjjJ57s.mjs +1 -0
  27. package/dist/packem_shared/solid-compat-ByG5HD_F.mjs +1 -0
  28. package/dist/server.mjs +1 -1
  29. package/dist/upload.d.mts +2 -0
  30. package/dist/upload.d.ts +2 -0
  31. package/dist/upload.mjs +1 -0
  32. package/package.json +11 -5
  33. package/dist/packem_shared/AuthLoading-u5QJoV-J.mjs +0 -73
  34. package/dist/packem_shared/LunoraContext-C59PzHhN.mjs +0 -13
  35. package/dist/packem_shared/LunoraProvider-CzB3zGhy.mjs +0 -17
  36. package/dist/packem_shared/createAgent-D7EZBeql.mjs +0 -55
  37. package/dist/packem_shared/createAgentChat-Bq-wzSxv.mjs +0 -166
  38. package/dist/packem_shared/createAgentState-DckYu3G8.mjs +0 -21
  39. package/dist/packem_shared/createAgentToolEvents-UavTO16u.mjs +0 -99
  40. package/dist/packem_shared/createConnectionStatus-1poqwqR9.mjs +0 -14
  41. package/dist/packem_shared/createFlag-DQoGdUMB.mjs +0 -78
  42. package/dist/packem_shared/createInfiniteQuery-CD2GT1_O.mjs +0 -339
  43. package/dist/packem_shared/createMutation-LkrbhItI.mjs +0 -36
  44. package/dist/packem_shared/createMutator-foSnPJPt.mjs +0 -24
  45. package/dist/packem_shared/createPresence-DM2cVkiG.mjs +0 -85
  46. package/dist/packem_shared/createQuery-BUldvZXj.mjs +0 -28
  47. package/dist/packem_shared/createRateLimit-BA2f8XyF.mjs +0 -76
  48. package/dist/packem_shared/createStream-D9ONbGAw.mjs +0 -70
  49. package/dist/packem_shared/createSubscription-D3HbChGe.mjs +0 -41
  50. package/dist/packem_shared/createVoiceAgent-hu59SaDl.mjs +0 -418
  51. package/dist/packem_shared/hydratePreloaded-CWny5-2J.mjs +0 -25
  52. package/dist/packem_shared/stable-key-DePnevIy.mjs +0 -38
package/dist/index.d.ts CHANGED
@@ -1,26 +1,74 @@
1
- import { LunoraClient, FunctionReference, User, ConnectionStatus, ArgsOf, MutationCallOptions, ReturnOf, MutatorHandle, Preloaded } from '@lunora/client';
2
- export type { ArgsOf, FunctionReference, MutatorHandle, MutatorTransaction, OptimisticUpdate, Preloaded, ReturnOf, Unsubscribe } from '@lunora/client';
3
- import { Context, Accessor, JSX } from 'solid-js';
1
+ import { LunoraClient, FunctionReference, ArgsOf, ActionCallOptions, ReturnOf, SubscriptionErrorCallback, User, ConnectionStatus, LiveStreamSubscribeError, LiveTopicRef, LiveTopicOptions, MutationCallOptions, OnlineOnlyOption, MutatorHandle, SubscriptionError, Preloaded } from '@lunora/client';
2
+ export type { ArgsOf, FunctionReference, MutatorHandle, MutatorTransaction, OptimisticUpdate, Preloaded, ReturnOf, SubscriptionError, SubscriptionErrorCallback, Unsubscribe } from '@lunora/client';
3
+ import { Context, Accessor } from 'solid-js';
4
+ import { AuthStatus } from '@lunora/client/auth';
4
5
  import { PaginationStatus } from '@lunora/client/pagination';
5
6
  import { RateLimitStatus, RateLimitConfig } from '@lunora/ratelimit';
6
7
  /**
7
8
  * Solid context carrying the framework-neutral {@link LunoraClient}. Every
8
9
  * reactive primitive in this adapter (`createQuery`, `createMutation`,
9
10
  * `hydratePreloaded`) reads the client from here, so a single
10
- * `<LunoraProvider client={…}>` at the root of the tree wires the whole app.
11
+ * `<LunoraProvider client={…}>` at the root of the tree wires the whole app.
11
12
  *
12
13
  * Defaults to `undefined` so {@link useLunora} can throw a helpful error when a
13
- * primitive is used outside a provider rather than dereferencing it.
14
+ * primitive is used outside a provider rather than dereferencing it. Solid 2
15
+ * refuses to hand back an `undefined` default at all — see {@link useLunora}.
14
16
  */
15
17
  declare const LunoraContext: Context<LunoraClient | undefined>;
16
18
  /**
17
- * Read the {@link LunoraClient} from the nearest `&lt;LunoraProvider>`.
19
+ * Read the {@link LunoraClient} from the nearest `<LunoraProvider>`.
18
20
  *
19
21
  * Throws when called outside a provider — the client is required to open the
20
22
  * HTTP/WS transport, so there is no sensible fallback. The React adapter's
21
23
  * `useLunora` has the same contract.
22
24
  */
23
25
  declare const useLunora: () => LunoraClient;
26
+ interface ActionHandle<F extends FunctionReference> {
27
+ /** Invoke the action. Resolves with the server result; rejects on failure. */
28
+ call: (args: ArgsOf<F>, options?: ActionCallOptions) => Promise<ReturnOf<F>>;
29
+ /** The latest invocation's resolved value, or `undefined` before the first success. */
30
+ data: Accessor<ReturnOf<F> | undefined>;
31
+ /** The latest invocation's error, or `undefined`. */
32
+ error: Accessor<Error | undefined>;
33
+ /** `true` while any invocation from this handle is in flight (ref-counted, so overlapping calls compose). */
34
+ pending: Accessor<boolean>;
35
+ /** Clear `data`/`error` back to idle. */
36
+ reset: () => void;
37
+ }
38
+ /**
39
+ * The transport surface {@link createAction} actually needs — just
40
+ * `client.action`. Narrowed so the primitive can be exercised against a stub in
41
+ * tests without constructing a full `LunoraClient`.
42
+ */
43
+ interface ActionClient<F extends FunctionReference> {
44
+ action: (function_: F, args: ArgsOf<F>, options?: ActionCallOptions) => Promise<ReturnOf<F>>;
45
+ }
46
+ /**
47
+ * Build an action handle bound to an explicit client. Internal seam used by the
48
+ * provider-bound {@link createAction}; exported for tests that inject a stub.
49
+ * The ref-counted pending + error-normalize orchestration is the shared
50
+ * `createCallRunner` from `@lunora/client`; only the reactive sinks (Solid
51
+ * signals) are adapter-specific.
52
+ *
53
+ * `data`/`error` follow the adapter-wide contract: both track the LATEST
54
+ * invocation (an earlier call settling later cannot clobber a newer one), a
55
+ * success clears `error`, and a failure leaves the previous `data` in place.
56
+ * `reset()` clears both; it does not cancel an in-flight call.
57
+ */
58
+ declare const createActionForClient: <F extends FunctionReference>(client: ActionClient<F>, function_: F) => ActionHandle<F>;
59
+ /**
60
+ * Returns a reactive handle `{ call, pending, data, error, reset }` for the
61
+ * given action reference, bound to the `LunoraClient` from the nearest
62
+ * `<LunoraProvider>`.
63
+ *
64
+ * **Narrower than `createMutation` on purpose:** no `optimistic` /
65
+ * `optimisticUpdate` call options. An optimistic update patches the subscription
66
+ * cache on the assumption a write will land; an action is not a write — it runs
67
+ * in the Worker, may call a third party, and has no declared effect on any
68
+ * query. Offering the option would imply a rollback guarantee nothing can
69
+ * honour.
70
+ */
71
+ declare const createAction: <F extends FunctionReference>(function_: F) => ActionHandle<F>;
24
72
  /**
25
73
  * The lifecycle status stored on an agent thread. Client-safe mirror of
26
74
  * `@lunora/agent`'s `AgentThreadStatus` — re-declared here (rather than imported)
@@ -37,8 +85,12 @@ type AgentThreadStatus = "awaiting_input" | "cancelled" | "error" | "idle" | "ru
37
85
  */
38
86
  interface AgentThreadRecord {
39
87
  createdAt?: number;
40
- /** The failure message when `status === "error"`. */
41
- error?: string;
88
+ /**
89
+ * The failure message when `status === "error"`, `null` once a later run
90
+ * cleared it, absent on a thread that has never failed. Read it for
91
+ * truthiness — `null` and absent both mean "no error".
92
+ */
93
+ error?: null | string;
42
94
  /** The workflow instance id of the in-flight run — the handle `cancel` targets. */
43
95
  instanceId?: string;
44
96
  messageCount?: number;
@@ -49,7 +101,7 @@ interface AgentThreadRecord {
49
101
  updatedAt?: number;
50
102
  }
51
103
  /** A plain value or a Solid accessor of it — matching `createQuery`'s reactive-args contract. */
52
- type MaybeAccessor$1<T> = Accessor<T> | T;
104
+ type MaybeAccessor<T> = Accessor<T> | T;
53
105
  /**
54
106
  * The `agents.agentThread` reference the primitive subscribes to for live thread
55
107
  * state (status + the in-flight `instanceId`). A structural subset of the
@@ -68,21 +120,27 @@ interface CreateAgentOptions {
68
120
  api: CreateAgentApi;
69
121
  /**
70
122
  * Optional app mutation over the agent's cancel path
71
- * (`ctx.agents.&lt;name>.cancel(id)`). Called with `{ instanceId, threadKey }`.
123
+ * (`ctx.agents.<name>.cancel(id)`). Called with `{ instanceId, threadKey }`.
72
124
  * When omitted (or no run is in flight) {@link CreateAgentResult.cancel} is a
73
125
  * no-op.
74
126
  */
75
127
  cancel?: FunctionReference<"mutation">;
128
+ /**
129
+ * Called when the live thread subscription reports an error (a session
130
+ * expiry, an RLS denial). Without it — and without reading `error` — such a
131
+ * failure is invisible and `thread` / `status` are cleared until a later frame arrives.
132
+ */
133
+ onError?: SubscriptionErrorCallback;
76
134
  /**
77
135
  * The app mutation that starts (or continues) a run — a thin wrapper over
78
- * `ctx.agents.&lt;name>.run(...)`. Called with `{ threadKey, input }` merged with
136
+ * `ctx.agents.<name>.run(...)`. Called with `{ threadKey, input }` merged with
79
137
  * {@link CreateAgentOptions.runArgs} and the per-call args.
80
138
  */
81
139
  run: FunctionReference<"mutation">;
82
140
  /** Extra args merged into every `run` call (e.g. an `owner` or `title`). */
83
141
  runArgs?: Record<string, unknown>;
84
142
  /** The thread to observe and drive — a plain value or accessor (an accessor re-subscribes on change). */
85
- threadKey: MaybeAccessor$1<string>;
143
+ threadKey: MaybeAccessor<string>;
86
144
  }
87
145
  interface CreateAgentResult {
88
146
  /**
@@ -90,6 +148,8 @@ interface CreateAgentResult {
90
148
  * no-op when no `cancel` mutation was supplied or no run is in flight.
91
149
  */
92
150
  cancel: () => Promise<void>;
151
+ /** The live thread subscription's last error, or `undefined`. */
152
+ error: Accessor<Error | undefined>;
93
153
  /** `true` while a `run` invocation is in flight. */
94
154
  pending: Accessor<boolean>;
95
155
  /** Start (or continue) a run with a user message; extra args merge over `runArgs`. */
@@ -108,7 +168,7 @@ interface CreateAgentResult {
108
168
  * `createAgentChat`.
109
169
  *
110
170
  * `run` and `cancel` stay generic over the app-defined mutations that wrap
111
- * `ctx.agents.&lt;name>.run` / `.cancel`, so the primitive hard-codes no function
171
+ * `ctx.agents.<name>.run` / `.cancel`, so the primitive hard-codes no function
112
172
  * names beyond the `agents:*` surface. `threadKey` may be an accessor — a changing
113
173
  * key re-subscribes to the new thread.
114
174
  */
@@ -130,8 +190,8 @@ interface AgentChatMessage {
130
190
  optimistic?: boolean;
131
191
  role: "assistant" | "system" | "tool" | "user";
132
192
  seq: number;
133
- /** Approval lifecycle marker on a human-in-the-loop tool message. */
134
- status?: "approved" | "awaiting_approval" | "rejected";
193
+ /** Approval or secret-request lifecycle marker on a human-in-the-loop tool message. */
194
+ status?: "approved" | "awaiting_approval" | "awaiting_secret" | "rejected";
135
195
  toolCallId?: string;
136
196
  toolCalls?: ReadonlyArray<{
137
197
  id: string;
@@ -185,7 +245,7 @@ type AgentMessagesReference$1 = FunctionReference<"query", {
185
245
  }, ReadonlyArray<Record<string, unknown>>>;
186
246
  /** The `agents:agentResolveApproval` reference — resolves a human-in-the-loop tool approval. */
187
247
  type AgentApprovalReference = FunctionReference<"mutation", {
188
- decision: "approve" | "reject";
248
+ decision: "approve" | "approve_for_thread" | "reject";
189
249
  instanceId: string;
190
250
  note?: string;
191
251
  threadKey: string;
@@ -193,10 +253,41 @@ type AgentApprovalReference = FunctionReference<"mutation", {
193
253
  }, {
194
254
  resolved: boolean;
195
255
  }>;
256
+ /** The `agents:agentSupplySecret` reference — stores a person's value for a paused `requestSecretTool` call. */
257
+ type AgentSupplySecretReference = FunctionReference<"mutation", {
258
+ secret: string;
259
+ threadKey: string;
260
+ toolCallId: string;
261
+ }, {
262
+ resolved: boolean;
263
+ }>;
264
+ /** The `agents:agentDeclineSecret` reference — refuses a paused `requestSecretTool` call. */
265
+ type AgentDeclineSecretReference = FunctionReference<"mutation", {
266
+ threadKey: string;
267
+ toolCallId: string;
268
+ }, {
269
+ resolved: boolean;
270
+ }>;
196
271
  /** The `agents:agentThread` reference — live thread status + in-flight `instanceId`. */
197
272
  type AgentThreadReference = FunctionReference<"query", {
198
273
  key: string;
199
274
  }, Record<string, unknown> | undefined>;
275
+ /** A tool approved for the rest of a thread, as `agents:agentApprovalGrants` lists it. */
276
+ interface AgentApprovalGrant {
277
+ createdAt: number;
278
+ toolName: string;
279
+ }
280
+ /** The `agents:agentApprovalGrants` reference — live thread-scoped approval grants. */
281
+ type AgentApprovalGrantsReference = FunctionReference<"query", {
282
+ key: string;
283
+ }, ReadonlyArray<AgentApprovalGrant>>;
284
+ /** The `agents:agentRevokeApprovalGrant` reference — removes a thread-scoped approval grant. */
285
+ type AgentRevokeApprovalGrantReference = FunctionReference<"mutation", {
286
+ threadKey: string;
287
+ toolName: string;
288
+ }, {
289
+ revoked: boolean;
290
+ }>;
200
291
  /**
201
292
  * An app stream reference that tees the agent's in-flight live events, keyed by
202
293
  * thread. Carries token deltas and — since `ctx.reportProgress` rides the same
@@ -212,8 +303,12 @@ type AgentTokenStreamReference = FunctionReference<"stream", {
212
303
  */
213
304
  interface CreateAgentChatApi {
214
305
  agents: {
306
+ agentApprovalGrants: AgentApprovalGrantsReference;
307
+ agentDeclineSecret: AgentDeclineSecretReference;
215
308
  agentMessages: AgentMessagesReference$1;
216
309
  agentResolveApproval: AgentApprovalReference;
310
+ agentRevokeApprovalGrant: AgentRevokeApprovalGrantReference;
311
+ agentSupplySecret: AgentSupplySecretReference;
217
312
  agentThread: AgentThreadReference;
218
313
  };
219
314
  }
@@ -222,16 +317,22 @@ interface CreateAgentChatOptions {
222
317
  api: CreateAgentChatApi;
223
318
  /**
224
319
  * Optional app mutation over the agent's cancel path
225
- * (`ctx.agents.&lt;name>.cancel(id)`). Called with `{ instanceId, threadKey }`.
320
+ * (`ctx.agents.<name>.cancel(id)`). Called with `{ instanceId, threadKey }`.
226
321
  * When omitted (or no run is in flight) {@link CreateAgentChatResult.cancel} is
227
322
  * a no-op.
228
323
  */
229
324
  cancel?: FunctionReference<"mutation">;
230
325
  /** History depth forwarded to `agents:agentMessages`. */
231
326
  limit?: number;
327
+ /**
328
+ * Called when the live history, thread, or grants subscription reports an error (a
329
+ * session expiry, an RLS denial). Without it — and without reading `error` —
330
+ * such a failure is invisible and `messages` / `status` are cleared until a later frame arrives.
331
+ */
332
+ onError?: SubscriptionErrorCallback;
232
333
  /**
233
334
  * The app mutation that starts (or continues) a run — a thin wrapper over
234
- * `ctx.agents.&lt;name>.run(...)`. Called with `{ threadKey, input }` merged with
335
+ * `ctx.agents.<name>.run(...)`. Called with `{ threadKey, input }` merged with
235
336
  * {@link CreateAgentChatOptions.sendArgs} and the per-call args.
236
337
  */
237
338
  send: FunctionReference<"mutation">;
@@ -244,30 +345,50 @@ interface CreateAgentChatOptions {
244
345
  */
245
346
  stream?: AgentTokenStreamReference;
246
347
  /** The thread to observe and continue — a plain value or accessor (an accessor re-subscribes on change). */
247
- threadKey: MaybeAccessor$1<string>;
348
+ threadKey: MaybeAccessor<string>;
248
349
  }
249
350
  interface CreateAgentChatResult {
351
+ /** The tools approved for the rest of this thread, live. Empty until the grants subscription answers. */
352
+ approvalGrants: Accessor<ReadonlyArray<AgentApprovalGrant>>;
250
353
  /** Approve a paused human-in-the-loop tool call (optionally with a note). */
251
354
  approve: (toolCallId: string, note?: string) => Promise<void>;
355
+ /**
356
+ * Approve a paused tool call and grant that tool for the rest of the thread, so
357
+ * later calls to it run without pausing. Undo with {@link CreateAgentChatResult.revokeGrant}.
358
+ */
359
+ approveForThread: (toolCallId: string, note?: string) => Promise<void>;
252
360
  /**
253
361
  * Terminate the in-flight run and mark its thread `"cancelled"`. Resolves as a
254
362
  * no-op when no `cancel` mutation was supplied or no run is in flight.
255
363
  */
256
364
  cancel: () => Promise<void>;
365
+ /** Refuse a paused `requestSecretTool` call: the run resumes with a "declined" result. */
366
+ declineSecret: (toolCallId: string) => Promise<void>;
367
+ /** The history, thread, or grants subscription's last error, or `undefined`. */
368
+ error: Accessor<Error | undefined>;
257
369
  /** Durable thread history (oldest first) plus any un-acknowledged optimistic user turns. */
258
370
  messages: Accessor<ReadonlyArray<AgentChatMessage>>;
259
371
  /** Reject a paused human-in-the-loop tool call (optionally with a reason). */
260
372
  reject: (toolCallId: string, note?: string) => Promise<void>;
373
+ /** Remove a thread-scoped approval grant so the tool's gate applies again to later calls. */
374
+ revokeGrant: (toolName: string) => Promise<void>;
261
375
  /** Start (or continue) a run with a user message; extra args merge over `sendArgs`. Appends an optimistic user turn. */
262
376
  send: (input: string, args?: Record<string, unknown>) => Promise<void>;
263
377
  /** The live thread status, or `undefined` before the thread exists. */
264
378
  status: Accessor<AgentThreadStatus | undefined>;
265
379
  /** The in-flight turn's streamed text — live-only, empty once the turn persists to `messages`. */
266
380
  streamingText: Accessor<string>;
381
+ /**
382
+ * Give a paused `requestSecretTool` call its value. Always sent online-only: the
383
+ * value is never queued, persisted or replayed, and the call rejects with
384
+ * `OFFLINE_NOT_QUEUED` while the connection is down. The primitive keeps no copy;
385
+ * a failure rethrows the original error.
386
+ */
387
+ supplySecret: (toolCallId: string, secret: string) => Promise<void>;
267
388
  }
268
389
  /**
269
390
  * A first-class agent chat surface: live durable history + in-flight token
270
- * streaming + the send / approve / reject / cancel writes, keyed by `threadKey` —
391
+ * streaming + the send / approve / reject / secret / cancel writes, keyed by `threadKey` —
271
392
  * the Solid counterpart to React's `useAgentChat`, re-expressed with signals.
272
393
  *
273
394
  * It composes the existing primitives rather than adding transport:
@@ -305,7 +426,7 @@ interface CreateAgentStateOptions {
305
426
  /** The generated `api` — its `agents.agentState` query drives live thread state. */
306
427
  api: CreateAgentStateApi;
307
428
  /** The thread whose synced state to observe — a plain value or accessor (an accessor re-subscribes on change). */
308
- threadKey: MaybeAccessor$1<string>;
429
+ threadKey: MaybeAccessor<string>;
309
430
  }
310
431
  interface CreateAgentStateResult<T> {
311
432
  /** The subscription error, if the live channel reported one. */
@@ -361,15 +482,15 @@ interface CreateAgentToolEventsOptions {
361
482
  * Optional live event stream — the same app stream function `createAgentChat`
362
483
  * uses. When supplied, ephemeral `ctx.reportProgress(...)` events for the
363
484
  * thread are surfaced as `{ type: "progress" }` entries; when omitted only the
364
- * durable lifecycle (call / result / awaiting-approval) is returned.
485
+ * durable lifecycle (call / result / awaiting-approval / awaiting-secret) is returned.
365
486
  */
366
487
  stream?: AgentLiveStreamReference;
367
488
  /** The thread whose tool activity to observe — a plain value or accessor (an accessor re-subscribes on change). */
368
- threadKey: MaybeAccessor$1<string>;
489
+ threadKey: MaybeAccessor<string>;
369
490
  }
370
491
  /**
371
492
  * A single tool-lifecycle event for a thread. The durable arms
372
- * (`call`/`result`/`awaiting-approval`) are derived from `agents:agentMessages`
493
+ * (`call`/`result`/`awaiting-approval`/`awaiting-secret`) are derived from `agents:agentMessages`
373
494
  * and carry the persisted `seq`; the ephemeral `progress` arm comes live off the
374
495
  * stream and has no `seq`. Discriminate on `type`.
375
496
  */
@@ -395,6 +516,11 @@ type AgentToolEvent = {
395
516
  toolCallId?: string;
396
517
  toolName?: string;
397
518
  type: "awaiting-approval";
519
+ } | {
520
+ seq: number;
521
+ toolCallId?: string;
522
+ toolName?: string;
523
+ type: "awaiting-secret";
398
524
  };
399
525
  interface CreateAgentToolEventsResult {
400
526
  /**
@@ -419,8 +545,41 @@ interface CreateAgentToolEventsResult {
419
545
  * + approvals) use `createAgentChat`; this primitive is the tool-observability slice.
420
546
  */
421
547
  declare const createAgentToolEvents: (options: CreateAgentToolEventsOptions) => CreateAgentToolEventsResult;
548
+ /**
549
+ * Children accepted by this adapter's components.
550
+ *
551
+ * Structurally identical to Solid 2.0's `Element`, which is in turn a widening
552
+ * of 1.x's `JSX.Element` (a DOM `Node` satisfies `object` with no
553
+ * `call`/`apply`/`bind`). Children are contravariant, so one union that accepts
554
+ * both majors' element types is enough — no `any` required here.
555
+ */
556
+ type SolidChildren = SolidChildrenArray | boolean | null | number | (object & {
557
+ readonly apply?: never;
558
+ readonly bind?: never;
559
+ readonly call?: never;
560
+ }) | (string & {}) | undefined;
561
+ interface SolidChildrenArray extends Array<SolidChildren> {}
562
+ /**
563
+ * The return type of this adapter's components.
564
+ *
565
+ * Deliberately `any`, and the one place in the package where that is the right
566
+ * answer. TypeScript checks a component's return type against the *renderer's*
567
+ * `JSX.Element`, and the two majors define that incompatibly — 1.x's is rooted
568
+ * in the DOM `Node` interface, 2.0's in an opaque `RenderedElement`. Neither is
569
+ * assignable to the other, so no concrete type satisfies both, and a structural
570
+ * union (as used for {@link SolidChildren}) fails in the return position. `any`
571
+ * is what lets one set of `.d.ts` files type-check inside a 1.x app and a 2.x
572
+ * app alike.
573
+ */
574
+ type SolidElement = any;
422
575
  interface UseAuthResult {
423
576
  setToken: (token: string | null) => void;
577
+ /**
578
+ * The resolved auth state. Branch on this, not on `user() === null` — see the
579
+ * contract in `@lunora/client/auth`; `user` is `null` both when signed out
580
+ * and when a held credential's identity could not be resolved.
581
+ */
582
+ status: Accessor<AuthStatus>;
424
583
  token: Accessor<string | null>;
425
584
  user: Accessor<User | null>;
426
585
  }
@@ -432,23 +591,29 @@ interface UseAuthResult {
432
591
  */
433
592
  declare const createAuth: () => UseAuthResult;
434
593
  interface AuthGateProps {
435
- children: JSX.Element;
594
+ children: SolidChildren;
436
595
  }
596
+ /** An auth-gate component: renders its children only while the gate's condition holds. */
597
+ type AuthGate = (props: AuthGateProps) => SolidElement;
437
598
  /**
438
- * Render `children` only after authentication has settled and a token + user
439
- * are both present.
599
+ * Render `children` once authentication has settled in the caller's favour — a
600
+ * credential is held and nothing has contradicted it.
601
+ *
602
+ * Gated on the shared `AuthStatus` contract in `@lunora/client/auth`, not on
603
+ * `user() !== null`: an unreachable identity endpoint leaves `user` at `null`
604
+ * while the credential is still perfectly valid.
440
605
  */
441
- declare const Authenticated: (props: AuthGateProps) => JSX.Element;
606
+ declare const Authenticated: AuthGate;
442
607
  /**
443
- * Render `children` while authentication is still in progress — token is set
444
- * but the user has not yet resolved.
608
+ * Render `children` while authentication is still in progress — a credential is
609
+ * held and the first identity resolve has not come back yet.
445
610
  */
446
- declare const AuthLoading: (props: AuthGateProps) => JSX.Element;
611
+ declare const AuthLoading: AuthGate;
447
612
  /**
448
- * Render `children` only when auth has settled and no token is present (the
449
- * signed-out state).
613
+ * Render `children` only when auth has settled and there is no session — no
614
+ * credential, or the server answered that the credential has none.
450
615
  */
451
- declare const Unauthenticated: (props: AuthGateProps) => JSX.Element;
616
+ declare const Unauthenticated: AuthGate;
452
617
  /**
453
618
  * Reactive accessor of the client's aggregate live-socket status across all
454
619
  * shard connections. Reads the current status synchronously and updates on
@@ -456,18 +621,20 @@ declare const Unauthenticated: (props: AuthGateProps) => JSX.Element;
456
621
  * fine-grained signals mean only the components that read the accessor
457
622
  * re-render. The Solid equivalent of `@lunora/react`'s `useConnectionStatus`.
458
623
  *
459
- * The status listener is torn down via `onCleanup` when the owning reactive
460
- * scope disposes (component unmount). Call inside a component / reactive root.
624
+ * The listener is registered at **mount**, not in the component body, because
625
+ * `onConnectionStatus` invokes it synchronously on subscribe: Solid 2 rejects a
626
+ * signal write inside an owned scope (`REACTIVE_WRITE_IN_OWNED_SCOPE`) and — since
627
+ * the throw escapes render — halts the reactive system for the whole page, not
628
+ * just this component. Nothing is missed by waiting: the initial value comes
629
+ * from `client.connectionStatus()`, and the immediate callback re-syncs at
630
+ * mount. Torn down when the owning scope disposes. Call inside a component /
631
+ * reactive root.
461
632
  */
462
633
  declare const createConnectionStatus: () => Accessor<ConnectionStatus>;
463
- /** A targeting context merged on top of the app's default (`defineFlags({ identify })`). */
464
- type FlagContext = Record<string, unknown>;
465
634
  /** The value kinds a flag resolves to — OpenFeature's boolean / number / string / structured (JSON) flags. */
466
635
  type FlagValue = boolean | number | string | {
467
636
  [key: string]: unknown;
468
637
  } | unknown[] | null;
469
- /** A plain value or a Solid accessor of it — matching `createQuery`'s reactive-args contract. */
470
- type MaybeAccessor<T> = Accessor<T> | T;
471
638
  /**
472
639
  * Subscribe to a single feature flag and return a reactive accessor of its value.
473
640
  *
@@ -477,33 +644,64 @@ type MaybeAccessor<T> = Accessor<T> | T;
477
644
  * `defaultValue`'s runtime type, so `createFlag("dark", false)` reads a boolean
478
645
  * and `createFlag("hero", "control")` a string.
479
646
  *
480
- * `key` and `context` may be plain values or accessors; passing an accessor makes
481
- * the subscription reactive — when it changes the old subscription is torn down
482
- * (via `onCleanup`) and a fresh one opens. `context` supplies a per-call targeting
483
- * context merged on top of the app's default `identify` targeting key.
647
+ * `key` may be a plain value or an accessor; passing an accessor makes the
648
+ * subscription reactive — when it changes the old subscription is torn down (via
649
+ * `onCleanup`) and a fresh one opens.
650
+ *
651
+ * The reactive channel is public, so the server evaluates every flag under the
652
+ * socket's own verified identity — the targeting key your `defineFlags({
653
+ * identify })` derives — and accepts no client-supplied targeting context. For
654
+ * evaluation under a context you compute, call `ctx.flags.*` inside a query,
655
+ * mutation, or action and return the resolved value.
484
656
  *
485
657
  * Evaluation runs through whatever OpenFeature provider the app wired in
486
658
  * `lunora/flags.ts`; the read never throws — a provider error resolves the
487
659
  * default (the same fail-open contract as server-side `ctx.flags`).
488
660
  */
489
- declare const createFlag: <T extends FlagValue>(key: MaybeAccessor<string>, defaultValue: T, context?: MaybeAccessor<FlagContext | undefined>) => Accessor<T>;
661
+ declare const createFlag: <T extends FlagValue>(key: MaybeAccessor<string>, defaultValue: T) => Accessor<T>;
490
662
  /**
491
663
  * Subscribe to several feature flags at once and return a reactive accessor of
492
664
  * the resolved record.
493
665
  *
494
666
  * Pass a record of `key → defaultValue`; each flag's kind is inferred from its
495
667
  * default, and the accessor reads the same-shaped record with resolved values
496
- * (the defaults until each evaluation lands). A single `context` applies to every
497
- * flag and may be an accessor. This is the batched form of {@link createFlag}.
668
+ * (the defaults until each evaluation lands). This is the batched form of
669
+ * {@link createFlag}, and evaluates under the socket's server-verified identity
670
+ * only. The flag set is fixed for the primitive's lifetime, so nothing here is
671
+ * reactive — the subscriptions open on mount and tear down on cleanup.
672
+ */
673
+ declare const createFlags: <T extends Record<string, FlagValue>>(flags: T) => Accessor<T>;
674
+ /**
675
+ * `createLiveTopic` — follow one live topic on the connection shared by its URL.
676
+ *
677
+ * A thin lifecycle wrapper over `subscribeLiveTopic` from `@lunora/client`: it
678
+ * subscribes when the owner mounts, unsubscribes on cleanup, and resubscribes when
679
+ * the topic or the connection options change. Callbacks are not part of that
680
+ * identity, so a new `onEvent` / `onError` never reopens the subscription.
681
+ */
682
+ interface CreateLiveTopicResult<Payload = unknown> {
683
+ /** The shard's refusal of this subscription (403, 409, 429 and similar), or `undefined`. */
684
+ error: Accessor<LiveStreamSubscribeError | undefined>;
685
+ /** The last payload delivered on the topic, or `undefined` until the first event. */
686
+ lastEvent: Accessor<Payload | undefined>;
687
+ }
688
+ /**
689
+ * Follow the topic `topic` names on the live connection shared by its stream's route. Pass the
690
+ * reference `_generated/live.ts` hands out (`live.posts.post({ postId })`), plain or as an
691
+ * accessor; the route is resolved against the `LunoraClient` from the nearest `LunoraContext`,
692
+ * and `lastEvent` and `onEvent` are typed by the payload the topic declares.
693
+ *
694
+ * `options` may be a plain object or an accessor. The `onEvent` and `onError` callbacks are
695
+ * read at call time, so the latest identity is always the one invoked.
498
696
  */
499
- declare const createFlags: <T extends Record<string, FlagValue>>(flags: T, context?: MaybeAccessor<FlagContext | undefined>) => Accessor<T>;
697
+ declare const createLiveTopic: <Payload>(topic: LiveTopicRef<Payload> | Accessor<LiveTopicRef<Payload>>, options?: LiveTopicOptions<Payload> | Accessor<LiveTopicOptions<Payload>>) => CreateLiveTopicResult<Payload>;
500
698
  interface MutationHandle<F extends FunctionReference> {
501
699
  /** The latest invocation's resolved value, or `undefined` before the first success. */
502
700
  data: Accessor<ReturnOf<F> | undefined>;
503
701
  /** The latest invocation's error, or `undefined`. */
504
702
  error: Accessor<Error | undefined>;
505
703
  /** Invoke the mutation. Resolves with the server result; rejects on failure. */
506
- mutate: (args: ArgsOf<F>, options?: MutationCallOptions<unknown, unknown, ArgsOf<F>>) => Promise<ReturnOf<F>>;
704
+ mutate: (args: ArgsOf<F>, options?: MutationCallOptions<unknown, unknown, ArgsOf<F>> & OnlineOnlyOption<F>) => Promise<ReturnOf<F>>;
507
705
  /** `true` while any invocation from this handle is in flight (ref-counted, so overlapping calls compose). */
508
706
  pending: Accessor<boolean>;
509
707
  /** Clear `data`/`error` back to idle. */
@@ -515,20 +713,25 @@ interface MutationHandle<F extends FunctionReference> {
515
713
  * in tests without constructing a full `LunoraClient`.
516
714
  */
517
715
  interface MutationClient<F extends FunctionReference> {
518
- mutation: (function_: F, args: ArgsOf<F>, options?: MutationCallOptions<unknown, unknown, ArgsOf<F>>) => Promise<ReturnOf<F>>;
716
+ mutation: (function_: F, args: ArgsOf<F>, options?: MutationCallOptions<unknown, unknown, ArgsOf<F>> & OnlineOnlyOption<F>) => Promise<ReturnOf<F>>;
519
717
  }
520
718
  /**
521
719
  * Build a mutation handle bound to an explicit client. Internal seam used by the
522
720
  * provider-bound {@link createMutation}; exported for tests that inject a stub.
523
721
  * The ref-counted pending + error-normalize orchestration is the shared
524
- * `createMutationRunner` from `@lunora/client`; only the reactive sinks (Solid
722
+ * `createCallRunner` from `@lunora/client`; only the reactive sinks (Solid
525
723
  * signals) are adapter-specific.
724
+ *
725
+ * `data`/`error` follow the adapter-wide contract: both track the LATEST
726
+ * invocation (an earlier call settling later cannot clobber a newer one), a
727
+ * success clears `error`, and a failure leaves the previous `data` in place.
728
+ * `reset()` clears both; it does not cancel an in-flight call.
526
729
  */
527
730
  declare const createMutationForClient: <F extends FunctionReference>(client: MutationClient<F>, function_: F) => MutationHandle<F>;
528
731
  /**
529
732
  * Returns a reactive handle `{ mutate, pending, data, error, reset }` for the
530
733
  * given mutation reference, bound to the `LunoraClient` from the nearest
531
- * `&lt;LunoraProvider>`.
734
+ * `<LunoraProvider>`.
532
735
  *
533
736
  * Optimistic updates stay client-owned: the `optimistic` / `optimisticUpdate`
534
737
  * call options pass straight through to `client.mutation`, which applies and
@@ -583,9 +786,18 @@ type PageItemOf<F extends FunctionReference> = ReturnOf<F> extends {
583
786
  interface CreatePaginatedQueryOptions {
584
787
  /** Page size for the first page (and the default for `loadMore`). */
585
788
  initialNumItems: number;
789
+ /** Called when a page subscription reports an error (also surfaced on the `error` accessor). */
790
+ onError?: SubscriptionErrorCallback;
586
791
  shardKey?: string;
587
792
  }
588
793
  interface CreatePaginatedQueryResult<T> {
794
+ /**
795
+ * The last page subscription error, or `undefined`. A tail page that fails
796
+ * before its first frame is dropped so `status` returns to `"CanLoadMore"`
797
+ * and `loadMore` can retry it; cleared by the next successful frame,
798
+ * `loadMore`, or an args change.
799
+ */
800
+ error: Accessor<SubscriptionError | undefined>;
589
801
  /** `true` while the first page or a `loadMore` page is in flight. */
590
802
  isLoading: Accessor<boolean>;
591
803
  /** Request the next page. A no-op unless `status === "CanLoadMore"`. */
@@ -597,9 +809,13 @@ interface CreatePaginatedQueryResult<T> {
597
809
  interface CreateInfiniteQueryOptions {
598
810
  /** Page size for the first page (and the default for `fetchNextPage`). */
599
811
  initialNumItems: number;
812
+ /** Called when a page subscription reports an error (also surfaced on the `error` accessor). */
813
+ onError?: SubscriptionErrorCallback;
600
814
  shardKey?: string;
601
815
  }
602
816
  interface CreateInfiniteQueryResult<T> {
817
+ /** The last page subscription error, or `undefined` — see `CreatePaginatedQueryResult.error`. */
818
+ error: Accessor<SubscriptionError | undefined>;
603
819
  /** Request the next page. A no-op unless `status === "CanLoadMore"`. */
604
820
  fetchNextPage: (numberItems?: number) => void;
605
821
  /** `true` when the loaded tail reports it can load another page. */
@@ -665,6 +881,12 @@ interface CreatePresenceOptions<H extends HeartbeatReference, L extends ListPres
665
881
  intervalMs?: number;
666
882
  /** The `api.*` reference for the presence listPresent query. */
667
883
  listPresent: L;
884
+ /**
885
+ * Called when the `listPresent` subscription reports an error (a session
886
+ * expiry, an RLS denial). Without it — and without reading `error` — such a
887
+ * failure is invisible and `present` is cleared until a later frame arrives.
888
+ */
889
+ onError?: SubscriptionErrorCallback;
668
890
  /**
669
891
  * Stable id for this presence row. Defaults to a fresh per-mount id.
670
892
  * Pass a user/connection id to control deduping across tabs.
@@ -674,6 +896,8 @@ interface CreatePresenceOptions<H extends HeartbeatReference, L extends ListPres
674
896
  shardKey?: string;
675
897
  }
676
898
  interface CreatePresenceResult<L extends ListPresentReference> {
899
+ /** The `listPresent` subscription's last error, or `undefined`. */
900
+ error: () => SubscriptionError | undefined;
677
901
  /** The present members for the room. `undefined` until the first push. */
678
902
  present: () => ReturnOf<L> | undefined;
679
903
  /** This mount's session id (generated when not supplied). */
@@ -683,6 +907,12 @@ interface CreatePresenceResult<L extends ListPresentReference> {
683
907
  }
684
908
  declare const createPresence: <H extends HeartbeatReference, L extends ListPresentReference>(roomId: string, options: CreatePresenceOptions<H, L>) => CreatePresenceResult<L>;
685
909
  interface CreateQueryOptions {
910
+ /**
911
+ * Called when the server pushes a subscription-scoped error (an RLS denial, a
912
+ * query that starts failing server-side). Without a handler such an error has
913
+ * nowhere to go and the accessor simply freezes at its last good value.
914
+ */
915
+ onError?: SubscriptionErrorCallback;
686
916
  /** Route to a specific shard when the target function is `.shardBy(...)`-partitioned. */
687
917
  shardKey?: string;
688
918
  }
@@ -696,13 +926,18 @@ interface CreateQueryOptions {
696
926
  *
697
927
  * `args` may be a plain value or an accessor; passing an accessor makes the
698
928
  * subscription reactive — when the args change the old subscription is torn down
699
- * (via `onCleanup`) and a fresh one opens for the new args. Pass `"skip"` (or an
929
+ * (via `onCleanup`), the accessor resets to `undefined`, and a fresh one opens for
930
+ * the new args. Pass `"skip"` (or an
700
931
  * accessor returning `"skip"`) to short-circuit: no network call, no socket.
701
932
  *
702
933
  * ```tsx
703
934
  * const messages = createQuery(api.messages.list, () => ({ channelId: channelId() }));
704
- * return &lt;For each={messages()?.messages}>{(m) => &lt;li>{m.text}&lt;/li>}&lt;/For>;
935
+ * return <For each={messages()?.messages}>{(m) => <li>{m.text}</li>}</For>;
705
936
  * ```
937
+ *
938
+ * Pass `onError` to surface a subscription-scoped error the server pushes (an RLS
939
+ * denial, a query that starts failing server-side). Without it such an error is
940
+ * dropped and the accessor just freezes at its last good value.
706
941
  */
707
942
  declare const createQuery: <F extends FunctionReference>(function_: F, args: (ArgsOf<F> | "skip") | Accessor<ArgsOf<F> | "skip">, options?: CreateQueryOptions) => Accessor<ReturnOf<F> | undefined>;
708
943
  interface CreateRateLimitOptions {
@@ -749,6 +984,13 @@ interface CreateStreamResult<T> {
749
984
  status: Accessor<CreateStreamStatus>;
750
985
  }
751
986
  interface CreateStreamOptions {
987
+ /**
988
+ * Opt into resume-on-reconnect for a stream the server declared `durable`.
989
+ * The chunks already received are kept and the socket re-attaches to the same
990
+ * run, so a dropped connection mid-generation continues instead of surfacing
991
+ * `STREAM_DISCONNECTED`. Has no effect on an ephemeral stream.
992
+ */
993
+ durable?: boolean;
752
994
  /** Forwarded to `client.stream()` — caps the in-flight chunk buffer. */
753
995
  maxBuffer?: number;
754
996
  shardKey?: string;
@@ -772,8 +1014,13 @@ interface CreateSubscriptionResult<T> {
772
1014
  * Subscribe to a reactive server push stream. Returns `{ data, error }`
773
1015
  * accessors that update whenever the server emits. Passing `"skip"` as `args`
774
1016
  * (or an accessor that resolves to `"skip"`) tears down the subscription.
1017
+ *
1018
+ * `onError` mirrors the other adapters' subscription primitives: it receives the
1019
+ * raw wire-level subscription error (code included) alongside the normalised
1020
+ * `error` accessor.
775
1021
  */
776
1022
  declare const createSubscription: <F extends FunctionReference>(function_: F, args: ArgsOf<F> | "skip" | Accessor<ArgsOf<F> | "skip">, options?: {
1023
+ onError?: SubscriptionErrorCallback;
777
1024
  shardKey?: string;
778
1025
  }) => CreateSubscriptionResult<ReturnOf<F>>;
779
1026
  /**
@@ -813,8 +1060,24 @@ interface MicrophoneConfig {
813
1060
  interruptChunks: number;
814
1061
  /** RMS above which the user is considered to be barging in while the agent speaks. */
815
1062
  interruptThreshold: number;
816
- /** `true` while `status === "speaking"` — gates barge-in detection. */
817
- isSpeaking: () => boolean;
1063
+ /**
1064
+ * `true` from the moment a turn is committed until it completes — the whole
1065
+ * `thinking` + `speaking` window, not just the audible half.
1066
+ *
1067
+ * It gates BOTH branches below, and the wider span is the point. Gated only
1068
+ * on "audibly speaking", turn detection kept running through the entire
1069
+ * STT+LLM window after a `commit`: room noise at the (deliberately low)
1070
+ * `silenceThreshold` re-armed `sawSpeech`, another quiet gap fired a SECOND
1071
+ * `commit`, and the DO refused it with "a turn is already in progress" —
1072
+ * a refusal that returns before draining the audio buffer, so the PCM
1073
+ * captured since the first commit leaked into the next utterance.
1074
+ *
1075
+ * A genuine barge-in still works in that window: it routes through the
1076
+ * `onInterrupt` branch, which needs `interruptChunks` consecutive chunks at
1077
+ * `interruptThreshold` — an order of magnitude above `silenceThreshold` —
1078
+ * and `interrupt` is exactly what the DO tells the client to send.
1079
+ */
1080
+ isTurnActive: () => boolean;
818
1081
  /** One 16 kHz mono 16-bit little-endian PCM frame captured from the mic. */
819
1082
  onAudio: (pcm: Uint8Array) => void;
820
1083
  /** A barge-in was detected (RMS spike while the agent is speaking). */
@@ -833,9 +1096,9 @@ type CreateSpeaker = (config: {
833
1096
  audioFormat: VoiceAudioFormat;
834
1097
  }) => VoiceSpeaker;
835
1098
  /**
836
- * The `agents.&lt;name>Voice` reference codegen emits for a voice-enabled agent — a
1099
+ * The `agents.<name>Voice` reference codegen emits for a voice-enabled agent — a
837
1100
  * live, WS-backed session keyed by `threadKey`. A structural subset of the
838
- * generated member, so passing `api.agents.&lt;name>Voice` type-checks.
1101
+ * generated member, so passing `api.agents.<name>Voice` type-checks.
839
1102
  */
840
1103
  type VoiceReference = FunctionReference<"stream", {
841
1104
  threadKey: string;
@@ -863,7 +1126,11 @@ interface CreateVoiceAgentOptions {
863
1126
  * Audio graph stays isolated (and mockable in a non-browser test env).
864
1127
  */
865
1128
  createMicrophone?: CreateMicrophone;
866
- /** Advanced/test seam: open the transport. Defaults to `new WebSocket(url)`. */
1129
+ /**
1130
+ * Advanced/test seam: open the transport. Defaults to the WebSocket
1131
+ * implementation the client was built with (`client.getWebSocketImpl()`),
1132
+ * NOT a raw `globalThis.WebSocket`.
1133
+ */
867
1134
  createSocket?: CreateSocket;
868
1135
  /** Advanced/test seam: build the audio playback subsystem. Defaults to a Web Audio implementation. */
869
1136
  createSpeaker?: CreateSpeaker;
@@ -876,8 +1143,8 @@ interface CreateVoiceAgentOptions {
876
1143
  /** Input RMS below which audio counts as silence. Default `0.01`. */
877
1144
  silenceThreshold?: number;
878
1145
  /** The thread to converse on — shared with the agent's text turns. May be a plain value or accessor (resolved when the call opens). */
879
- threadKey: MaybeAccessor$1<string>;
880
- /** The generated `api.agents.&lt;name>Voice` reference — identifies the voice DO endpoint. */
1146
+ threadKey: MaybeAccessor<string>;
1147
+ /** The generated `api.agents.<name>Voice` reference — identifies the voice DO endpoint. */
881
1148
  voice: VoiceReference;
882
1149
  }
883
1150
  interface CreateVoiceAgentResult {
@@ -909,7 +1176,7 @@ interface CreateVoiceAgentResult {
909
1176
  * WebSocket to the agent's `VoiceSessionDO`, captures mic audio as 16 kHz PCM,
910
1177
  * streams the agent's synthesized speech back through the browser's audio output,
911
1178
  * and mirrors the call lifecycle (`status`, `transcript`, `interimTranscript`,
912
- * `audioLevel`) to Solid signals. Pass the generated `api.agents.&lt;name>Voice`
1179
+ * `audioLevel`) to Solid signals. Pass the generated `api.agents.<name>Voice`
913
1180
  * reference (never a string), matching `createAgentChat`'s reference-passing style.
914
1181
  * The Solid counterpart to React's `useVoiceAgent`, re-expressed with signals; the
915
1182
  * per-call connection lives in a closure variable (a primitive runs once per
@@ -929,23 +1196,37 @@ declare const createVoiceAgent: (options: CreateVoiceAgentOptions) => CreateVoic
929
1196
  * returned accessor is seeded **synchronously** from `preloaded.value`, so the
930
1197
  * very first read — during hydration — returns the server-rendered value with
931
1198
  * no loading flash and no `Suspense` fallback (unlike `createResource`, which
932
- * always starts pending). After the component mounts, a WebSocket subscription
933
- * attaches in an effect and every subsequent server delta flows into the same
934
- * signal, so the UI goes live with zero refetch.
1199
+ * always starts pending). Once the component mounts, a WebSocket subscription
1200
+ * attaches and every subsequent server delta flows into the same signal, so the
1201
+ * UI goes live with zero refetch.
935
1202
  *
936
1203
  * ```tsx
937
1204
  * // route loader (server): const preloaded = await preloadQuery(client, api.messages.list, args);
938
1205
  * const messages = hydratePreloaded(preloaded); // seeded from SSR, then live
939
- * return &lt;pre>{JSON.stringify(messages())}&lt;/pre>;
1206
+ * return <pre>{JSON.stringify(messages())}</pre>;
940
1207
  * ```
941
1208
  *
942
- * Effects do not run on the server during SSR (Solid only runs them after
943
- * hydration), so the subscription is strictly client-side — the seed is the
944
- * only value the server render ever sees.
1209
+ * Mount callbacks do not run on the server during SSR (both Solid majors defer
1210
+ * them until after hydration), so the subscription is strictly client-side —
1211
+ * the seed is the only value the server render ever sees.
1212
+ *
1213
+ * Pass `onError` to surface a subscription-scoped error the server pushes (a
1214
+ * session expiry, an RLS denial). Without it such an error is dropped and the
1215
+ * accessor keeps rendering the SSR snapshot as if it were live.
1216
+ *
1217
+ * The preloaded value was read for whoever was signed in when the page loaded.
1218
+ * After a sign-out or user switch retires that identity
1219
+ * (`client.identityEpoch() > 0`), every `hydratePreloaded` on the client (mounted
1220
+ * then or later) stops using it and returns `undefined` until the live value
1221
+ * arrives — matching `@lunora/react`'s `usePreloadedQuery`, hence the
1222
+ * `Accessor<T | undefined>` type. The same holds under a `LunoraProvider` whose
1223
+ * `client` was swapped: the value was preloaded for the first client.
945
1224
  */
946
- declare const hydratePreloaded: <T>(preloaded: Preloaded<T>) => Accessor<T>;
1225
+ declare const hydratePreloaded: <T>(preloaded: Preloaded<T>, options?: {
1226
+ onError?: SubscriptionErrorCallback;
1227
+ }) => Accessor<T | undefined>;
947
1228
  interface LunoraProviderProps {
948
- children: JSX.Element;
1229
+ children: SolidChildren;
949
1230
  /**
950
1231
  * The framework-neutral transport. Build it once at the app root with
951
1232
  * `new LunoraClient({ url })` (or `createServerClient` during SSR) and pass
@@ -967,11 +1248,46 @@ interface LunoraProviderProps {
967
1248
  * const client = new LunoraClient({ url: window.location.origin });
968
1249
  *
969
1250
  * render(() => (
970
- * &lt;LunoraProvider client={client}>
971
- * &lt;App />
972
- * &lt;/LunoraProvider>
1251
+ * <LunoraProvider client={client}>
1252
+ * <App />
1253
+ * </LunoraProvider>
973
1254
  * ), root);
974
1255
  * ```
1256
+ *
1257
+ * Swapping the `client` prop for a different instance **remounts the subtree**
1258
+ * under the provider. Solid's providers read their value once, so the swap
1259
+ * cannot be pushed into primitives that already resolved the client; instead
1260
+ * every primitive below is disposed (its subscriptions on the old client torn
1261
+ * down) and recreated against the new client, starting from `undefined` — no
1262
+ * rows from the old client survive, and `hydratePreloaded` does not seed a value
1263
+ * preloaded for the client the provider first rendered with (not even after
1264
+ * swapping back to it). Local component state under the provider resets with
1265
+ * it. Re-reading the same instance is a no-op.
1266
+ *
1267
+ * Written with `createComponent` rather than JSX on purpose. Solid 1.x and 2.0
1268
+ * compile JSX against different runtimes (`solid-js/web` vs `@solidjs/web`), so
1269
+ * a JSX source file would force this package to ship two builds; `createComponent`
1270
+ * is exported from the `solid-js` root in both majors, and the provider component
1271
+ * itself is resolved per-major by {@link providerOf}.
1272
+ */
1273
+ declare const LunoraProvider: (props: LunoraProviderProps) => SolidElement;
1274
+ export { type ActionClient, type ActionHandle, type AgentApprovalGrant, type AgentChatMessage, type AgentLiveEvent, type AgentProgressEvent, type AgentThreadRecord, type AgentThreadStatus, type AgentTokenDelta, type AgentToolEvent, AuthLoading, Authenticated, type CreateAgentApi, type CreateAgentChatApi, type CreateAgentChatOptions, type CreateAgentChatResult, type CreateAgentOptions, type CreateAgentResult, type CreateAgentStateApi, type CreateAgentStateOptions, type CreateAgentStateResult, type CreateAgentToolEventsApi, type CreateAgentToolEventsOptions, type CreateAgentToolEventsResult, type CreateInfiniteQueryOptions, type CreateInfiniteQueryResult, type CreateLiveTopicResult, type CreatePaginatedQueryOptions, type CreatePaginatedQueryResult, type CreatePresenceOptions, type CreatePresenceResult, type CreateQueryOptions, type CreateRateLimitOptions, type CreateRateLimitResult, type CreateStreamOptions, type CreateStreamResult, type CreateStreamStatus, type CreateSubscriptionResult, type CreateVoiceAgentOptions, type CreateVoiceAgentResult, type FlagValue, type HeartbeatReference, type ListPresentReference, LunoraContext, LunoraProvider,
1275
+ /**
1276
+ * SolidJS adapter for Lunora.
1277
+ *
1278
+ * Thin, idiomatic glue over the framework-neutral `@lunora/client`. Solid's
1279
+ * fine-grained signals map directly onto Lunora's per-subscription deltas, so a
1280
+ * live query is just a signal the WebSocket writes to. The adapter exposes
1281
+ * `LunoraProvider` / `useLunora` (context carrying the `LunoraClient`),
1282
+ * `createQuery` (a live query accessor that opens a subscription and updates on
1283
+ * every delta), `createMutation` (an optimistic mutation handle), and
1284
+ * `hydratePreloaded` (seed a query from an SSR `Preloaded` token synchronously —
1285
+ * no loading flash — then attach the live subscription; the client half of
1286
+ * PLAN4's "your loaders are live" reactive-loader handoff).
1287
+ *
1288
+ * Server-side preloading (`createServerClient`, `preloadQuery`) lives in the
1289
+ * socket-free `@lunora/solid/server` entry (a re-export of `@lunora/client/ssr`, the
1290
+ * framework-neutral server contract) — call it from your SolidStart route loader
1291
+ * and hand the resulting `Preloaded` token to `hydratePreloaded`.
975
1292
  */
976
- declare const LunoraProvider: (props: LunoraProviderProps) => JSX.Element;
977
- export { type AgentChatMessage, type AgentLiveEvent, type AgentProgressEvent, type AgentThreadRecord, type AgentThreadStatus, type AgentTokenDelta, type AgentToolEvent, AuthLoading, Authenticated, type CreateAgentApi, type CreateAgentChatApi, type CreateAgentChatOptions, type CreateAgentChatResult, type CreateAgentOptions, type CreateAgentResult, type CreateAgentStateApi, type CreateAgentStateOptions, type CreateAgentStateResult, type CreateAgentToolEventsApi, type CreateAgentToolEventsOptions, type CreateAgentToolEventsResult, type CreateInfiniteQueryOptions, type CreateInfiniteQueryResult, type CreatePaginatedQueryOptions, type CreatePaginatedQueryResult, type CreatePresenceOptions, type CreatePresenceResult, type CreateQueryOptions, type CreateRateLimitOptions, type CreateRateLimitResult, type CreateStreamOptions, type CreateStreamResult, type CreateStreamStatus, type CreateSubscriptionResult, type CreateVoiceAgentOptions, type CreateVoiceAgentResult, type FlagContext, type FlagValue, type HeartbeatReference, type ListPresentReference, LunoraContext, LunoraProvider, type LunoraProviderProps, type MutationClient, type MutationHandle, type MutatorHook, type PageItemOf, type PaginatedArgs, Unauthenticated, type UseAuthResult, type VoiceAudioFormat, type VoiceReference, type VoiceStatus, createAgent, createAgentChat, createAgentState, createAgentToolEvents, createAuth, createConnectionStatus, createFlag, createFlags, createInfiniteQuery, createMutation, createMutationForClient, createMutator, createPaginatedQuery, createPresence, createQuery, createRateLimit, createStream, createSubscription, createVoiceAgent, hydratePreloaded, useLunora };
1293
+ type LunoraProviderProps, type MutationClient, type MutationHandle, type MutatorHook, type PageItemOf, type PaginatedArgs, type SolidChildren, Unauthenticated, type UseAuthResult, type VoiceAudioFormat, type VoiceReference, type VoiceStatus, createAction, createActionForClient, createAgent, createAgentChat, createAgentState, createAgentToolEvents, createAuth, createConnectionStatus, createFlag, createFlags, createInfiniteQuery, createLiveTopic, createMutation, createMutationForClient, createMutator, createPaginatedQuery, createPresence, createQuery, createRateLimit, createStream, createSubscription, createVoiceAgent, hydratePreloaded, useLunora };