@lunora/angular 1.0.0-alpha.9 → 1.0.0-alpha.90

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 (50) hide show
  1. package/README.md +42 -0
  2. package/dist/index.d.mts +879 -689
  3. package/dist/index.d.ts +879 -689
  4. package/dist/index.mjs +1 -19
  5. package/dist/packem_shared/LUNORA_CLIENT-B0toApHY.mjs +1 -0
  6. package/dist/packem_shared/agent-BCt1D_wQ.mjs +1 -0
  7. package/dist/packem_shared/agentChat-DMMjTLnF.mjs +1 -0
  8. package/dist/packem_shared/agentState-Cdyss0S4.mjs +1 -0
  9. package/dist/packem_shared/agentToolEvents-BOp6-TK8.mjs +1 -0
  10. package/dist/packem_shared/auth-C3dB8sYS.mjs +1 -0
  11. package/dist/packem_shared/connectionStatus-UhmuwzMa.mjs +1 -0
  12. package/dist/packem_shared/flag-BJxkgJR2.mjs +1 -0
  13. package/dist/packem_shared/hydratePreloaded-gUUl_6tS.mjs +1 -0
  14. package/dist/packem_shared/infiniteQuery-yZS4F7PI.mjs +1 -0
  15. package/dist/packem_shared/liveQuery-ZIgKq4et.mjs +1 -0
  16. package/dist/packem_shared/mutate-BZvLQLyu.mjs +1 -0
  17. package/dist/packem_shared/mutator-DjG1yGk8.mjs +1 -0
  18. package/dist/packem_shared/platform-DNlq-CRU.mjs +1 -0
  19. package/dist/packem_shared/presence-DUkZtSSL.mjs +1 -0
  20. package/dist/packem_shared/rateLimit-B3h9qzh-.mjs +1 -0
  21. package/dist/packem_shared/runAction-BfiPq4Xz.mjs +1 -0
  22. package/dist/packem_shared/stream-CVSnLbC2.mjs +1 -0
  23. package/dist/packem_shared/subscription-B_Xj8Ezd.mjs +1 -0
  24. package/dist/packem_shared/voiceAgent-CyPFWGUt.mjs +1 -0
  25. package/dist/server.d.mts +1 -0
  26. package/dist/server.d.ts +1 -0
  27. package/dist/server.mjs +1 -0
  28. package/dist/upload.d.mts +57 -0
  29. package/dist/upload.d.ts +57 -0
  30. package/dist/upload.mjs +1 -0
  31. package/package.json +12 -3
  32. package/dist/packem_shared/LUNORA_CLIENT-DHUfNu9x.mjs +0 -23
  33. package/dist/packem_shared/agent-DDKvrG4u.mjs +0 -31
  34. package/dist/packem_shared/agentChat-C7kUEwO9.mjs +0 -96
  35. package/dist/packem_shared/agentState-C8GWf3t3.mjs +0 -10
  36. package/dist/packem_shared/agentToolEvents-sXgYggvi.mjs +0 -59
  37. package/dist/packem_shared/auth-Df9N87Z4.mjs +0 -27
  38. package/dist/packem_shared/connectionStatus-BlLodleK.mjs +0 -15
  39. package/dist/packem_shared/flag-CGBo90HJ.mjs +0 -70
  40. package/dist/packem_shared/hydratePreloaded-DIpD1cAM.mjs +0 -33
  41. package/dist/packem_shared/infiniteQuery-nboKfr5E.mjs +0 -221
  42. package/dist/packem_shared/liveQuery-DVxKidjM.mjs +0 -32
  43. package/dist/packem_shared/mutate-D3rEHwbb.mjs +0 -8
  44. package/dist/packem_shared/mutator-BHL8bakL.mjs +0 -19
  45. package/dist/packem_shared/platform-Dg8Bppgq.mjs +0 -14
  46. package/dist/packem_shared/presence-BTuq19dS.mjs +0 -77
  47. package/dist/packem_shared/rateLimit-I4kRT9qV.mjs +0 -58
  48. package/dist/packem_shared/stream-PL64AghO.mjs +0 -47
  49. package/dist/packem_shared/subscription-oZ-WTmpp.mjs +0 -39
  50. package/dist/packem_shared/voiceAgent-DwbrDnB9.mjs +0 -401
package/dist/index.d.ts CHANGED
@@ -1,25 +1,26 @@
1
- import { DestroyRef, Signal, InjectionToken, EnvironmentProviders } from '@angular/core';
2
- import { FunctionReference, LunoraClient, SubscriptionError, User, LunoraClientOptions, ConnectionStatus, Preloaded, ArgsOf, ReturnOf, MutationCallOptions, MutatorHandle } from '@lunora/client';
1
+ import { DestroyRef, Signal, InjectionToken, EnvironmentProviders, Injector } from '@angular/core';
2
+ import { FunctionReference, LunoraClient, SubscriptionErrorCallback, SubscriptionError, User, LunoraClientOptions, ConnectionStatus, Preloaded, ArgsOf, ReturnOf, MutationCallOptions, MutatorHandle, ActionCallOptions } from '@lunora/client';
3
3
  export type { ArgsOf, ConnectionStatus, FunctionReference, LunoraClient, LunoraClientOptions, MutationCallOptions, Preloaded, ReturnOf, SubscriptionError, Unsubscribe } from '@lunora/client';
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
  export { SKIP } from '@lunora/client/query';
7
8
  /**
8
- * The lifecycle status stored on an agent thread. Client-safe mirror of
9
- * `@lunora/agent`'s `AgentThreadStatus` — re-declared here (rather than imported)
10
- * so this Angular entry never pulls in the server-only `@lunora/agent` module graph
11
- * (the adapter stays Angular + `@lunora/client` only). Keep in sync with
12
- * `packages/agent/src/types.ts`.
13
- * @experimental
14
- */
9
+ * The lifecycle status stored on an agent thread. Client-safe mirror of
10
+ * `@lunora/agent`'s `AgentThreadStatus` — re-declared here (rather than imported)
11
+ * so this Angular entry never pulls in the server-only `@lunora/agent` module graph
12
+ * (the adapter stays Angular + `@lunora/client` only). Keep in sync with
13
+ * `packages/agent/src/types.ts`.
14
+ * @experimental
15
+ */
15
16
  type AgentThreadStatus = "awaiting_input" | "cancelled" | "error" | "idle" | "running";
16
17
  /**
17
- * The live thread record surfaced by the `agents:agentThread` query. A structural
18
- * subset of the persisted thread row — every field beyond `status` is optional so
19
- * the shape stays forgiving as the server schema grows. Keep in sync with the
20
- * `agent_threads` table in `packages/agent/src/component.ts`.
21
- * @experimental
22
- */
18
+ * The live thread record surfaced by the `agents:agentThread` query. A structural
19
+ * subset of the persisted thread row — every field beyond `status` is optional so
20
+ * the shape stays forgiving as the server schema grows. Keep in sync with the
21
+ * `agent_threads` table in `packages/agent/src/component.ts`.
22
+ * @experimental
23
+ */
23
24
  interface AgentThreadRecord {
24
25
  createdAt?: number;
25
26
  /** The failure message when `status === "error"`. */
@@ -34,20 +35,20 @@ interface AgentThreadRecord {
34
35
  updatedAt?: number;
35
36
  }
36
37
  /**
37
- * One persisted (or optimistic) thread message, as `agents:agentMessages`
38
- * surfaces it. Client-safe mirror of `@lunora/agent`'s `AgentMessageRow` —
39
- * re-declared here (rather than imported) so this Angular entry never pulls in the
40
- * server-only `@lunora/agent` module graph. Keep in sync with the
41
- * `agent_messages` table in `packages/agent/src/component.ts`.
42
- * @experimental
43
- */
38
+ * One persisted (or optimistic) thread message, as `agents:agentMessages`
39
+ * surfaces it. Client-safe mirror of `@lunora/agent`'s `AgentMessageRow` —
40
+ * re-declared here (rather than imported) so this Angular entry never pulls in the
41
+ * server-only `@lunora/agent` module graph. Keep in sync with the
42
+ * `agent_messages` table in `packages/agent/src/component.ts`.
43
+ * @experimental
44
+ */
44
45
  interface AgentChatMessage {
45
46
  content: string;
46
47
  createdAt?: number;
47
48
  /**
48
- * `true` for a client-side optimistic user message not yet acknowledged by
49
- * the server. Cleared once the durable history carries the matching user turn.
50
- */
49
+ * `true` for a client-side optimistic user message not yet acknowledged by
50
+ * the server. Cleared once the durable history carries the matching user turn.
51
+ */
51
52
  optimistic?: boolean;
52
53
  role: "assistant" | "system" | "tool" | "user";
53
54
  seq: number;
@@ -62,12 +63,12 @@ interface AgentChatMessage {
62
63
  toolName?: string;
63
64
  }
64
65
  /**
65
- * A live token delta streamed while a turn is generating. Client-safe mirror of
66
- * `@lunora/agent`'s `AgentTokenDelta`. Ephemeral — deltas feed the chat surface's
67
- * streaming text live and are never replayed; the persisted assistant message
68
- * stays the single source of truth.
69
- * @experimental
70
- */
66
+ * A live token delta streamed while a turn is generating. Client-safe mirror of
67
+ * `@lunora/agent`'s `AgentTokenDelta`. Ephemeral — deltas feed the chat surface's
68
+ * streaming text live and are never replayed; the persisted assistant message
69
+ * stays the single source of truth.
70
+ * @experimental
71
+ */
71
72
  interface AgentTokenDelta {
72
73
  /** Discriminates the token arm of {@link AgentLiveEvent}; unset on the wire (token is the default). */
73
74
  kind?: "token";
@@ -79,11 +80,11 @@ interface AgentTokenDelta {
79
80
  turn: number;
80
81
  }
81
82
  /**
82
- * A live tool-progress event streamed via `ctx.reportProgress(...)`. Client-safe
83
- * mirror of `@lunora/agent`'s `AgentProgressEvent`. Ephemeral and `toolCallId`-keyed;
84
- * surfaced by `agentToolEvents`, ignored by the chat surface's streaming text.
85
- * @experimental
86
- */
83
+ * A live tool-progress event streamed via `ctx.reportProgress(...)`. Client-safe
84
+ * mirror of `@lunora/agent`'s `AgentProgressEvent`. Ephemeral and `toolCallId`-keyed;
85
+ * surfaced by `agentToolEvents`, ignored by the chat surface's streaming text.
86
+ * @experimental
87
+ */
87
88
  interface AgentProgressEvent {
88
89
  /** The arbitrary, JSON-serializable payload the tool reported. */
89
90
  data: unknown;
@@ -95,20 +96,20 @@ interface AgentProgressEvent {
95
96
  toolCallId: string;
96
97
  }
97
98
  /**
98
- * A single event on the agent's live-only channel — a streamed token delta or a
99
- * tool progress event. Client-safe mirror of `@lunora/agent`'s `AgentLiveEvent`.
100
- * Discriminate on `kind` (`"progress"` for the progress arm; token deltas leave
101
- * it unset).
102
- * @experimental
103
- */
99
+ * A single event on the agent's live-only channel — a streamed token delta or a
100
+ * tool progress event. Client-safe mirror of `@lunora/agent`'s `AgentLiveEvent`.
101
+ * Discriminate on `kind` (`"progress"` for the progress arm; token deltas leave
102
+ * it unset).
103
+ * @experimental
104
+ */
104
105
  type AgentLiveEvent = AgentProgressEvent | AgentTokenDelta;
105
106
  /**
106
- * The `agents.agentThread` reference the primitive subscribes to for live thread
107
- * state (status + the in-flight `instanceId`). A structural subset of the
108
- * generated `api.agents` surface, so the whole generated `api` object is
109
- * assignable.
110
- * @experimental
111
- */
107
+ * The `agents.agentThread` reference the primitive subscribes to for live thread
108
+ * state (status + the in-flight `instanceId`). A structural subset of the
109
+ * generated `api.agents` surface, so the whole generated `api` object is
110
+ * assignable.
111
+ * @experimental
112
+ */
112
113
  interface AgentApi {
113
114
  agents: {
114
115
  agentThread: FunctionReference<"query", {
@@ -117,30 +118,36 @@ interface AgentApi {
117
118
  };
118
119
  }
119
120
  /**
120
- * `AgentOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
121
- * @experimental
122
- */
121
+ * `AgentOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
122
+ * @experimental
123
+ */
123
124
  interface AgentOptions {
124
125
  /** The generated `api` — its `agents.agentThread` query drives live thread state. */
125
126
  api: AgentApi;
126
127
  /**
127
- * Optional app mutation over the agent's cancel path
128
- * (`ctx.agents.&lt;name>.cancel(id)`). Called with `{ instanceId, threadKey }`.
129
- * When omitted (or no run is in flight) {@link AgentResult.cancel} is a no-op.
130
- */
128
+ * Optional app mutation over the agent's cancel path
129
+ * (`ctx.agents.<name>.cancel(id)`). Called with `{ instanceId, threadKey }`.
130
+ * When omitted (or no run is in flight) {@link AgentResult.cancel} is a no-op.
131
+ */
131
132
  cancel?: FunctionReference<"mutation">;
132
133
  /** Client to bind to. Defaults to the injected `LUNORA_CLIENT`. */
133
134
  client?: LunoraClient;
134
135
  /**
135
- * `DestroyRef` whose `onDestroy` tears the live subscription down. Defaults to
136
- * `inject(DestroyRef)` — the calling component/service.
137
- */
136
+ * `DestroyRef` whose `onDestroy` tears the live subscription down. Defaults to
137
+ * `inject(DestroyRef)` — the calling component/service.
138
+ */
138
139
  destroyRef?: DestroyRef;
139
140
  /**
140
- * The app mutation that starts (or continues) a run — a thin wrapper over
141
- * `ctx.agents.&lt;name>.run(...)`. Called with `{ threadKey, input }` merged with
142
- * {@link AgentOptions.runArgs} and the per-call args.
143
- */
141
+ * Called when the live thread subscription reports an error (a session
142
+ * expiry, an RLS denial). Without it — and without reading `error` — such a
143
+ * failure is invisible and `thread` / `status` are cleared until a later frame arrives.
144
+ */
145
+ onError?: SubscriptionErrorCallback;
146
+ /**
147
+ * The app mutation that starts (or continues) a run — a thin wrapper over
148
+ * `ctx.agents.<name>.run(...)`. Called with `{ threadKey, input }` merged with
149
+ * {@link AgentOptions.runArgs} and the per-call args.
150
+ */
144
151
  run: FunctionReference<"mutation">;
145
152
  /** Extra args merged into every `run` call (e.g. an `owner` or `title`). */
146
153
  runArgs?: Record<string, unknown>;
@@ -148,15 +155,17 @@ interface AgentOptions {
148
155
  threadKey: string;
149
156
  }
150
157
  /**
151
- * `AgentResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
152
- * @experimental
153
- */
158
+ * `AgentResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
159
+ * @experimental
160
+ */
154
161
  interface AgentResult {
155
162
  /**
156
- * Terminate the in-flight run and mark its thread `"cancelled"`. Resolves as a
157
- * no-op when no `cancel` mutation was supplied or no run is in flight.
158
- */
163
+ * Terminate the in-flight run and mark its thread `"cancelled"`. Resolves as a
164
+ * no-op when no `cancel` mutation was supplied or no run is in flight.
165
+ */
159
166
  cancel: () => Promise<void>;
167
+ /** The live thread subscription's last error, or `undefined`. */
168
+ error: Signal<SubscriptionError | undefined>;
160
169
  /** `true` while a `run` invocation is in flight. */
161
170
  pending: Signal<boolean>;
162
171
  /** Start (or continue) a run with a user message; extra args merge over `runArgs`. */
@@ -167,20 +176,20 @@ interface AgentResult {
167
176
  thread: Signal<AgentThreadRecord | undefined>;
168
177
  }
169
178
  /**
170
- * A thin agent handle: live thread `status` plus `run` / `cancel`, without the
171
- * chat message surface. Composes `subscription(api.agents.agentThread)` for live
172
- * state and drives the run/cancel writes straight on the client — the Angular
173
- * counterpart to React's `useAgent`, re-expressed with signals. For the full
174
- * conversation surface (durable history + streaming + approvals) use `agentChat`.
175
- *
176
- * `run` and `cancel` stay generic over the app-defined mutations that wrap
177
- * `ctx.agents.&lt;name>.run` / `.cancel`, so the primitive hard-codes no function
178
- * names beyond the `agents:*` surface.
179
- *
180
- * Call from an injection context (component/service field or constructor); pass an
181
- * explicit `client` / `destroyRef` to drive it outside one (e.g. in a test).
182
- * @experimental
183
- */
179
+ * A thin agent handle: live thread `status` plus `run` / `cancel`, without the
180
+ * chat message surface. Composes `subscription(api.agents.agentThread)` for live
181
+ * state and drives the run/cancel writes straight on the client — the Angular
182
+ * counterpart to React's `useAgent`, re-expressed with signals. For the full
183
+ * conversation surface (durable history + streaming + approvals) use `agentChat`.
184
+ *
185
+ * `run` and `cancel` stay generic over the app-defined mutations that wrap
186
+ * `ctx.agents.<name>.run` / `.cancel`, so the primitive hard-codes no function
187
+ * names beyond the `agents:*` surface.
188
+ *
189
+ * Call from an injection context (component/service field or constructor); pass an
190
+ * explicit `client` / `destroyRef` to drive it outside one (e.g. in a test).
191
+ * @experimental
192
+ */
184
193
  declare const agent: (options: AgentOptions) => AgentResult;
185
194
  /** The `agents:agentMessages` reference — live durable thread history. */
186
195
  type AgentMessagesReference$1 = FunctionReference<"query", {
@@ -202,20 +211,20 @@ type AgentThreadReference = FunctionReference<"query", {
202
211
  key: string;
203
212
  }, Record<string, unknown> | undefined>;
204
213
  /**
205
- * An app stream reference that tees the agent's in-flight live events, keyed by
206
- * thread. Carries token deltas and — since `ctx.reportProgress` rides the same
207
- * sink — tool progress events; this primitive consumes only the token arm.
208
- * @experimental
209
- */
214
+ * An app stream reference that tees the agent's in-flight live events, keyed by
215
+ * thread. Carries token deltas and — since `ctx.reportProgress` rides the same
216
+ * sink — tool progress events; this primitive consumes only the token arm.
217
+ * @experimental
218
+ */
210
219
  type AgentTokenStreamReference = FunctionReference<"stream", {
211
220
  key: string;
212
221
  }, AgentLiveEvent>;
213
222
  /**
214
- * The `agents.*` reference surface the chat primitive reads. A structural subset
215
- * of the generated `api.agents`, so the whole generated `api` object is
216
- * assignable.
217
- * @experimental
218
- */
223
+ * The `agents.*` reference surface the chat primitive reads. A structural subset
224
+ * of the generated `api.agents`, so the whole generated `api` object is
225
+ * assignable.
226
+ * @experimental
227
+ */
219
228
  interface AgentChatApi {
220
229
  agents: {
221
230
  agentMessages: AgentMessagesReference$1;
@@ -224,57 +233,65 @@ interface AgentChatApi {
224
233
  };
225
234
  }
226
235
  /**
227
- * `AgentChatOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
228
- * @experimental
229
- */
236
+ * `AgentChatOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
237
+ * @experimental
238
+ */
230
239
  interface AgentChatOptions {
231
240
  /** The generated `api` — its `agents.*` surface provides history, thread state, and approval resolution. */
232
241
  api: AgentChatApi;
233
242
  /**
234
- * Optional app mutation over the agent's cancel path
235
- * (`ctx.agents.&lt;name>.cancel(id)`). Called with `{ instanceId, threadKey }`.
236
- * When omitted (or no run is in flight) {@link AgentChatResult.cancel} is a
237
- * no-op.
238
- */
243
+ * Optional app mutation over the agent's cancel path
244
+ * (`ctx.agents.<name>.cancel(id)`). Called with `{ instanceId, threadKey }`.
245
+ * When omitted (or no run is in flight) {@link AgentChatResult.cancel} is a
246
+ * no-op.
247
+ */
239
248
  cancel?: FunctionReference<"mutation">;
240
249
  /** Client to bind to. Defaults to the injected `LUNORA_CLIENT`. */
241
250
  client?: LunoraClient;
242
251
  /**
243
- * `DestroyRef` whose `onDestroy` tears the subscriptions + stream down. Defaults
244
- * to `inject(DestroyRef)` — the calling component/service.
245
- */
252
+ * `DestroyRef` whose `onDestroy` tears the subscriptions + stream down. Defaults
253
+ * to `inject(DestroyRef)` — the calling component/service.
254
+ */
246
255
  destroyRef?: DestroyRef;
247
256
  /** History depth forwarded to `agents:agentMessages`. */
248
257
  limit?: number;
249
258
  /**
250
- * The app mutation that starts (or continues) a run — a thin wrapper over
251
- * `ctx.agents.&lt;name>.run(...)`. Called with `{ threadKey, input }` merged with
252
- * {@link AgentChatOptions.sendArgs} and the per-call args.
253
- */
259
+ * Called when the live history or thread subscription reports an error (a
260
+ * session expiry, an RLS denial). Without it — and without reading `error` —
261
+ * such a failure is invisible and `messages` / `status` are cleared until a later frame arrives.
262
+ */
263
+ onError?: SubscriptionErrorCallback;
264
+ /**
265
+ * The app mutation that starts (or continues) a run — a thin wrapper over
266
+ * `ctx.agents.<name>.run(...)`. Called with `{ threadKey, input }` merged with
267
+ * {@link AgentChatOptions.sendArgs} and the per-call args.
268
+ */
254
269
  send: FunctionReference<"mutation">;
255
270
  /** Extra args merged into every `send` call (e.g. an `owner` or `title`). */
256
271
  sendArgs?: Record<string, unknown>;
257
272
  /**
258
- * Optional live token-delta stream — an app stream function that tees the
259
- * agent's in-flight deltas. When omitted {@link AgentChatResult.streamingText}
260
- * stays empty and the UI updates message-by-message from durable history.
261
- */
273
+ * Optional live token-delta stream — an app stream function that tees the
274
+ * agent's in-flight deltas. When omitted {@link AgentChatResult.streamingText}
275
+ * stays empty and the UI updates message-by-message from durable history.
276
+ */
262
277
  stream?: AgentTokenStreamReference;
263
278
  /** The thread to observe and continue. */
264
279
  threadKey: string;
265
280
  }
266
281
  /**
267
- * `AgentChatResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
268
- * @experimental
269
- */
282
+ * `AgentChatResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
283
+ * @experimental
284
+ */
270
285
  interface AgentChatResult {
271
286
  /** Approve a paused human-in-the-loop tool call (optionally with a note). */
272
287
  approve: (toolCallId: string, note?: string) => Promise<void>;
273
288
  /**
274
- * Terminate the in-flight run and mark its thread `"cancelled"`. Resolves as a
275
- * no-op when no `cancel` mutation was supplied or no run is in flight.
276
- */
289
+ * Terminate the in-flight run and mark its thread `"cancelled"`. Resolves as a
290
+ * no-op when no `cancel` mutation was supplied or no run is in flight.
291
+ */
277
292
  cancel: () => Promise<void>;
293
+ /** The history or thread subscription's last error, or `undefined`. */
294
+ error: Signal<SubscriptionError | undefined>;
278
295
  /** Durable thread history (oldest first) plus any un-acknowledged optimistic user turns. */
279
296
  messages: Signal<ReadonlyArray<AgentChatMessage>>;
280
297
  /** Reject a paused human-in-the-loop tool call (optionally with a reason). */
@@ -287,39 +304,39 @@ interface AgentChatResult {
287
304
  streamingText: Signal<string>;
288
305
  }
289
306
  /**
290
- * A first-class agent chat surface: live durable history + in-flight token
291
- * streaming + the send / approve / reject / cancel writes, keyed by `threadKey` —
292
- * the Angular counterpart to React's `useAgentChat`, re-expressed with signals.
293
- *
294
- * It composes the existing primitives rather than adding transport:
295
- * `subscription(api.agents.agentMessages)` for durable history,
296
- * `subscription(api.agents.agentThread)` for live status + the in-flight
297
- * `instanceId`, {@link stream} over an app token stream for in-flight deltas, and
298
- * the client's own `mutation` for the writes (`api.agents.agentResolveApproval` for
299
- * approvals; app-defined wrappers for `send`/`cancel`). Only the `agents:*` surface
300
- * is hard-coded — `send`/`cancel`/`stream` stay generic references.
301
- *
302
- * A `send` optimistically appends the user turn so it renders immediately; the
303
- * optimistic row clears once the durable history carries the acknowledged turn.
304
- * `streamingText` is live-only: it holds the current turn's streamed text and
305
- * empties as soon as that turn's assistant message lands in `messages` (the
306
- * persisted message is the source of truth), consistent with the loop's
307
- * replay-safe, live-only delta design.
308
- *
309
- * Call from an injection context (component/service field or constructor); pass an
310
- * explicit `client` / `destroyRef` to drive it outside one (e.g. in a test).
311
- * @experimental
312
- */
307
+ * A first-class agent chat surface: live durable history + in-flight token
308
+ * streaming + the send / approve / reject / cancel writes, keyed by `threadKey` —
309
+ * the Angular counterpart to React's `useAgentChat`, re-expressed with signals.
310
+ *
311
+ * It composes the existing primitives rather than adding transport:
312
+ * `subscription(api.agents.agentMessages)` for durable history,
313
+ * `subscription(api.agents.agentThread)` for live status + the in-flight
314
+ * `instanceId`, {@link stream} over an app token stream for in-flight deltas, and
315
+ * the client's own `mutation` for the writes (`api.agents.agentResolveApproval` for
316
+ * approvals; app-defined wrappers for `send`/`cancel`). Only the `agents:*` surface
317
+ * is hard-coded — `send`/`cancel`/`stream` stay generic references.
318
+ *
319
+ * A `send` optimistically appends the user turn so it renders immediately; the
320
+ * optimistic row clears once the durable history carries the acknowledged turn.
321
+ * `streamingText` is live-only: it holds the current turn's streamed text and
322
+ * empties as soon as that turn's assistant message lands in `messages` (the
323
+ * persisted message is the source of truth), consistent with the loop's
324
+ * replay-safe, live-only delta design.
325
+ *
326
+ * Call from an injection context (component/service field or constructor); pass an
327
+ * explicit `client` / `destroyRef` to drive it outside one (e.g. in a test).
328
+ * @experimental
329
+ */
313
330
  declare const agentChat: (options: AgentChatOptions) => AgentChatResult;
314
331
  /**
315
- * The `agents.agentState` reference the primitive subscribes to for the thread's
316
- * live synced state. A structural subset of the generated `api.agents` surface
317
- * (like `AgentApi` for `agentThread`), so the whole generated `api` object is
318
- * assignable. Client-safe: no `@lunora/agent` import — the per-agent state type is
319
- * mirrored by the primitive's generic `T`, since codegen pins the reference return
320
- * as an optional record (it never evaluates agent config).
321
- * @experimental
322
- */
332
+ * The `agents.agentState` reference the primitive subscribes to for the thread's
333
+ * live synced state. A structural subset of the generated `api.agents` surface
334
+ * (like `AgentApi` for `agentThread`), so the whole generated `api` object is
335
+ * assignable. Client-safe: no `@lunora/agent` import — the per-agent state type is
336
+ * mirrored by the primitive's generic `T`, since codegen pins the reference return
337
+ * as an optional record (it never evaluates agent config).
338
+ * @experimental
339
+ */
323
340
  interface AgentStateApi {
324
341
  agents: {
325
342
  agentState: FunctionReference<"query", {
@@ -328,26 +345,26 @@ interface AgentStateApi {
328
345
  };
329
346
  }
330
347
  /**
331
- * `AgentStateOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
332
- * @experimental
333
- */
348
+ * `AgentStateOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
349
+ * @experimental
350
+ */
334
351
  interface AgentStateOptions {
335
352
  /** The generated `api` — its `agents.agentState` query drives live thread state. */
336
353
  api: AgentStateApi;
337
354
  /** Client to bind to. Defaults to the injected `LUNORA_CLIENT`. */
338
355
  client?: LunoraClient;
339
356
  /**
340
- * `DestroyRef` whose `onDestroy` tears the subscription down. Defaults to
341
- * `inject(DestroyRef)` — the calling component/service.
342
- */
357
+ * `DestroyRef` whose `onDestroy` tears the subscription down. Defaults to
358
+ * `inject(DestroyRef)` — the calling component/service.
359
+ */
343
360
  destroyRef?: DestroyRef;
344
361
  /** The thread whose synced state to observe. */
345
362
  threadKey: string;
346
363
  }
347
364
  /**
348
- * `AgentStateResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
349
- * @experimental
350
- */
365
+ * `AgentStateResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
366
+ * @experimental
367
+ */
351
368
  interface AgentStateResult<T> {
352
369
  /** The subscription error, if the live channel reported one. */
353
370
  error: Signal<SubscriptionError | undefined>;
@@ -355,24 +372,24 @@ interface AgentStateResult<T> {
355
372
  state: Signal<T | undefined>;
356
373
  }
357
374
  /**
358
- * Subscribe to an agent thread's synced state — the `setState`-style value a tool
359
- * writes with `ctx.setState(...)`, seeded by `defineAgent({ initialState })`. A
360
- * thin wrapper over `subscription(api.agents.agentState, { key })`: the server
361
- * pushes a fresh frame whenever the state changes (the dedicated query's per-socket
362
- * JSON memo suppresses no-op pushes on unrelated thread writes), so `state` updates
363
- * only on a real `setState`. The Angular counterpart to React's `useAgentState`,
364
- * re-expressed with signals.
365
- *
366
- * Generic over the app's state shape (`agentState&lt;SupportState>(...)`, itself a
367
- * record) — the reference is typed as an optional record because codegen cannot see
368
- * the per-agent state type; the generic casts to `T`. The `extends` bound (not a
369
- * bare unbounded type parameter) is required: this `.ts` file is parsed JSX-aware by
370
- * the bundler, where an unbounded type-param arrow is ambiguous with a JSX element.
371
- *
372
- * Call from an injection context (component/service field or constructor); pass an
373
- * explicit `client` / `destroyRef` to drive it outside one (e.g. in a test).
374
- * @experimental
375
- */
375
+ * Subscribe to an agent thread's synced state — the `setState`-style value a tool
376
+ * writes with `ctx.setState(...)`, seeded by `defineAgent({ initialState })`. A
377
+ * thin wrapper over `subscription(api.agents.agentState, { key })`: the server
378
+ * pushes a fresh frame whenever the state changes (the dedicated query's per-socket
379
+ * JSON memo suppresses no-op pushes on unrelated thread writes), so `state` updates
380
+ * only on a real `setState`. The Angular counterpart to React's `useAgentState`,
381
+ * re-expressed with signals.
382
+ *
383
+ * Generic over the app's state shape (`agentState<SupportState>(...)`, itself a
384
+ * record) — the reference is typed as an optional record because codegen cannot see
385
+ * the per-agent state type; the generic casts to `T`. The `extends` bound (not a
386
+ * bare unbounded type parameter) is required: this `.ts` file is parsed JSX-aware by
387
+ * the bundler, where an unbounded type-param arrow is ambiguous with a JSX element.
388
+ *
389
+ * Call from an injection context (component/service field or constructor); pass an
390
+ * explicit `client` / `destroyRef` to drive it outside one (e.g. in a test).
391
+ * @experimental
392
+ */
376
393
  declare const agentState: <T extends Record<string, unknown> = Record<string, unknown>>(options: AgentStateOptions) => AgentStateResult<T>;
377
394
  /** The `agents:agentMessages` reference — live durable thread history. */
378
395
  type AgentMessagesReference = FunctionReference<"query", {
@@ -380,57 +397,57 @@ type AgentMessagesReference = FunctionReference<"query", {
380
397
  limit?: number;
381
398
  }, ReadonlyArray<Record<string, unknown>>>;
382
399
  /**
383
- * An app stream reference that tees the agent's in-flight live events, keyed by
384
- * thread. Carries token deltas and tool progress events; this primitive consumes
385
- * only the progress arm (`kind === "progress"`).
386
- */
400
+ * An app stream reference that tees the agent's in-flight live events, keyed by
401
+ * thread. Carries token deltas and tool progress events; this primitive consumes
402
+ * only the progress arm (`kind === "progress"`).
403
+ */
387
404
  type AgentLiveStreamReference = FunctionReference<"stream", {
388
405
  key: string;
389
406
  }, AgentLiveEvent>;
390
407
  /**
391
- * The `agents.*` reference surface the tool-events primitive reads. A structural
392
- * subset of the generated `api.agents`, so the whole generated `api` object is
393
- * assignable.
394
- * @experimental
395
- */
408
+ * The `agents.*` reference surface the tool-events primitive reads. A structural
409
+ * subset of the generated `api.agents`, so the whole generated `api` object is
410
+ * assignable.
411
+ * @experimental
412
+ */
396
413
  interface AgentToolEventsApi {
397
414
  agents: {
398
415
  agentMessages: AgentMessagesReference;
399
416
  };
400
417
  }
401
418
  /**
402
- * `AgentToolEventsOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
403
- * @experimental
404
- */
419
+ * `AgentToolEventsOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
420
+ * @experimental
421
+ */
405
422
  interface AgentToolEventsOptions {
406
423
  /** The generated `api` — its `agents.agentMessages` query provides the durable tool lifecycle. */
407
424
  api: AgentToolEventsApi;
408
425
  /** Client to bind to. Defaults to the injected `LUNORA_CLIENT`. */
409
426
  client?: LunoraClient;
410
427
  /**
411
- * `DestroyRef` whose `onDestroy` tears the subscription + stream down. Defaults
412
- * to `inject(DestroyRef)` — the calling component/service.
413
- */
428
+ * `DestroyRef` whose `onDestroy` tears the subscription + stream down. Defaults
429
+ * to `inject(DestroyRef)` — the calling component/service.
430
+ */
414
431
  destroyRef?: DestroyRef;
415
432
  /** History depth forwarded to `agents:agentMessages`. */
416
433
  limit?: number;
417
434
  /**
418
- * Optional live event stream — the same app stream function `agentChat` uses.
419
- * When supplied, ephemeral `ctx.reportProgress(...)` events for the thread are
420
- * surfaced as `{ type: "progress" }` entries; when omitted only the durable
421
- * lifecycle (call / result / awaiting-approval) is returned.
422
- */
435
+ * Optional live event stream — the same app stream function `agentChat` uses.
436
+ * When supplied, ephemeral `ctx.reportProgress(...)` events for the thread are
437
+ * surfaced as `{ type: "progress" }` entries; when omitted only the durable
438
+ * lifecycle (call / result / awaiting-approval) is returned.
439
+ */
423
440
  stream?: AgentLiveStreamReference;
424
441
  /** The thread whose tool activity to observe. */
425
442
  threadKey: string;
426
443
  }
427
444
  /**
428
- * A single tool-lifecycle event for a thread. The durable arms
429
- * (`call`/`result`/`awaiting-approval`) are derived from `agents:agentMessages`
430
- * and carry the persisted `seq`; the ephemeral `progress` arm comes live off the
431
- * stream and has no `seq`. Discriminate on `type`.
432
- * @experimental
433
- */
445
+ * A single tool-lifecycle event for a thread. The durable arms
446
+ * (`call`/`result`/`awaiting-approval`) are derived from `agents:agentMessages`
447
+ * and carry the persisted `seq`; the ephemeral `progress` arm comes live off the
448
+ * stream and has no `seq`. Discriminate on `type`.
449
+ * @experimental
450
+ */
434
451
  type AgentToolEvent = {
435
452
  data: unknown;
436
453
  toolCallId: string;
@@ -455,40 +472,40 @@ type AgentToolEvent = {
455
472
  type: "awaiting-approval";
456
473
  };
457
474
  /**
458
- * `AgentToolEventsResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
459
- * @experimental
460
- */
475
+ * `AgentToolEventsResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
476
+ * @experimental
477
+ */
461
478
  interface AgentToolEventsResult {
462
479
  /**
463
- * The thread's tool events: the durable lifecycle (oldest first, by `seq`)
464
- * followed by any in-flight ephemeral progress events, recomputed from the live
465
- * subscription + stream. Treat as derived, not identity-stable.
466
- */
480
+ * The thread's tool events: the durable lifecycle (oldest first, by `seq`)
481
+ * followed by any in-flight ephemeral progress events, recomputed from the live
482
+ * subscription + stream. Treat as derived, not identity-stable.
483
+ */
467
484
  events: Signal<ReadonlyArray<AgentToolEvent>>;
468
485
  }
469
486
  /**
470
- * A focused view of a thread's tool activity: tool calls, their results,
471
- * human-in-the-loop approval pauses, and live `ctx.reportProgress(...)` events —
472
- * without the full chat message surface. The Angular counterpart to React's
473
- * `useAgentToolEvents`, re-expressed as a `computed` signal.
474
- *
475
- * It composes the existing primitives rather than adding transport:
476
- * `subscription(api.agents.agentMessages)` for the durable lifecycle and
477
- * {@link stream} over the optional app event stream for ephemeral progress.
478
- * Progress events are live-only (the durable path never emits them): they ride the
479
- * same sink as token deltas and are surfaced here, correlated to their tool call by
480
- * `toolCallId`. For the conversational surface (messages + streaming text +
481
- * approvals) use `agentChat`; this primitive is the tool-observability slice.
482
- *
483
- * Call from an injection context (component/service field or constructor); pass an
484
- * explicit `client` / `destroyRef` to drive it outside one (e.g. in a test).
485
- * @experimental
486
- */
487
+ * A focused view of a thread's tool activity: tool calls, their results,
488
+ * human-in-the-loop approval pauses, and live `ctx.reportProgress(...)` events —
489
+ * without the full chat message surface. The Angular counterpart to React's
490
+ * `useAgentToolEvents`, re-expressed as a `computed` signal.
491
+ *
492
+ * It composes the existing primitives rather than adding transport:
493
+ * `subscription(api.agents.agentMessages)` for the durable lifecycle and
494
+ * {@link stream} over the optional app event stream for ephemeral progress.
495
+ * Progress events are live-only (the durable path never emits them): they ride the
496
+ * same sink as token deltas and are surfaced here, correlated to their tool call by
497
+ * `toolCallId`. For the conversational surface (messages + streaming text +
498
+ * approvals) use `agentChat`; this primitive is the tool-observability slice.
499
+ *
500
+ * Call from an injection context (component/service field or constructor); pass an
501
+ * explicit `client` / `destroyRef` to drive it outside one (e.g. in a test).
502
+ * @experimental
503
+ */
487
504
  declare const agentToolEvents: (options: AgentToolEventsOptions) => AgentToolEventsResult;
488
505
  /**
489
- * `AuthOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
490
- * @experimental
491
- */
506
+ * `AuthOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
507
+ * @experimental
508
+ */
492
509
  interface AuthOptions {
493
510
  /** Client to bind to. Defaults to the injected `LUNORA_CLIENT`. */
494
511
  client?: LunoraClient;
@@ -496,92 +513,126 @@ interface AuthOptions {
496
513
  destroyRef?: DestroyRef;
497
514
  }
498
515
  /**
499
- * `AuthResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
500
- * @experimental
501
- */
516
+ * `AuthResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
517
+ * @experimental
518
+ */
502
519
  interface AuthResult {
503
520
  /** Set the auth token (sign-in / sign-out). */
504
521
  setToken: (token: string | null) => void;
522
+ /**
523
+ * The resolved auth state. Branch on this, not on `user() === null` — see the
524
+ * contract in `@lunora/client/auth`; `user` is `null` both when signed out
525
+ * and when a held credential's identity could not be resolved.
526
+ */
527
+ status: Signal<AuthStatus>;
505
528
  /** The current auth token, or `null`. */
506
529
  token: Signal<string | null>;
507
530
  /** The resolved user from `store.getUser()`, or `null`. */
508
531
  user: Signal<User | null>;
509
532
  }
510
533
  /**
511
- * Token + identity plumbing for Angular. `token` is a signal tracking the
512
- * client's auth token; `user` is a signal resolved from `getCurrentUser()`
513
- * whenever the token changes. `setToken(jwt)` after sign-in makes subsequent
514
- * RPC calls carry the `Authorization` header.
515
- *
516
- * Multiple `auth` instances on the same client share a single per-client
517
- * identity store (from `@lunora/client/auth`) — a `setToken` from one component
518
- * re-renders every watcher with the freshly-resolved user.
519
- *
520
- * Call from an injection context (component/service field or constructor):
521
- * ```ts
522
- * const { token, user, setToken } = auth();
523
- * ```
524
- * @experimental
525
- */
534
+ * Token + identity plumbing for Angular. `token` is a signal tracking the
535
+ * client's auth token; `user` is a signal resolved from `getCurrentUser()`
536
+ * whenever the token changes. `setToken(jwt)` after sign-in makes subsequent
537
+ * RPC calls carry the `Authorization` header.
538
+ *
539
+ * Multiple `auth` instances on the same client share a single per-client
540
+ * identity store (from `@lunora/client/auth`) — a `setToken` from one component
541
+ * re-renders every watcher with the freshly-resolved user.
542
+ *
543
+ * Call from an injection context (component/service field or constructor):
544
+ * ```ts
545
+ * const { token, user, setToken } = auth();
546
+ * ```
547
+ * @experimental
548
+ */
526
549
  declare const auth: (options?: AuthOptions) => AuthResult;
527
550
  /**
528
- * DI token carrying the framework-neutral {@link LunoraClient}. Every reactive
529
- * primitive in this adapter (`liveQuery`, `mutate`, `connectionStatus`) reads the
530
- * client from here, so a single {@link provideLunora} in the application config
531
- * wires the whole app.
532
- *
533
- * The token has a root-scoped default factory, so it resolves even without
534
- * {@link provideLunora}: it builds one same-origin browser client (which opens
535
- * its WebSocket lazily on the first subscription). Call {@link provideLunora} to
536
- * point it at a remote URL or hand it a pre-built client.
537
- * @experimental
538
- */
551
+ * `AuthGateResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
552
+ * @experimental
553
+ */
554
+ interface AuthGateResult {
555
+ /** `true` once a credential is held and nothing has contradicted it. */
556
+ isAuthenticated: Signal<boolean>;
557
+ /** `true` while a credential's first identity resolve is in flight. */
558
+ isLoading: Signal<boolean>;
559
+ }
560
+ /**
561
+ * Derived auth-gate signals for template gating (Angular's `\@if` control
562
+ * flow), built on {@link auth}. Angular has no JSX-style `Authenticated` slot
563
+ * component the way React/Vue/Solid do, so this exposes the same three-state
564
+ * logic as two booleans instead, mapped from the shared `AuthStatus` contract in
565
+ * `@lunora/client/auth`: a credential whose first identity resolve is in flight
566
+ * is `isLoading`; a credential nothing has contradicted — including one whose
567
+ * identity endpoint is unreachable — is `isAuthenticated`; no session is neither
568
+ * (the signed-out state a template checks for with a plain `\@else`).
569
+ *
570
+ * Call from an injection context (component/service field or constructor):
571
+ * ```ts
572
+ * protected readonly authState = authGate();
573
+ * // template: \@if (authState.isAuthenticated()) { ... } \@else if (authState.isLoading()) { ... }
574
+ * ```
575
+ * @experimental
576
+ */
577
+ declare const authGate: (options?: AuthOptions) => AuthGateResult;
578
+ /**
579
+ * DI token carrying the framework-neutral {@link LunoraClient}. Every reactive
580
+ * primitive in this adapter (`liveQuery`, `mutate`, `connectionStatus`) reads the
581
+ * client from here, so a single {@link provideLunora} in the application config
582
+ * wires the whole app.
583
+ *
584
+ * The token has a root-scoped default factory, so it resolves even without
585
+ * {@link provideLunora}: it builds one same-origin browser client (which opens
586
+ * its WebSocket lazily on the first subscription). Call {@link provideLunora} to
587
+ * point it at a remote URL or hand it a pre-built client.
588
+ * @experimental
589
+ */
539
590
  declare const LUNORA_CLIENT: InjectionToken<LunoraClient>;
540
591
  /**
541
- * Options accepted by {@link provideLunora}. Identical to {@link LunoraClientOptions}
542
- * except `url` is optional — it defaults to the page origin in the browser (and to
543
- * `""` on the server; pass an explicit `url` for SSR data-loading — see
544
- * {@link sameOriginUrl}).
545
- * @experimental
546
- */
592
+ * Options accepted by {@link provideLunora}. Identical to {@link LunoraClientOptions}
593
+ * except `url` is optional — it defaults to the page origin in the browser (and to
594
+ * `""` on the server; pass an explicit `url` for SSR data-loading — see
595
+ * {@link sameOriginUrl}).
596
+ * @experimental
597
+ */
547
598
  type ProvideLunoraOptions = Omit<LunoraClientOptions, "url"> & {
548
599
  url?: string;
549
600
  };
550
601
  /**
551
- * Wire a {@link LunoraClient} into the application injector. Add the result to the
552
- * `providers` array of an Angular application config (or any `EnvironmentProviders`
553
- * consumer):
554
- *
555
- * ```ts
556
- * export const appConfig: ApplicationConfig = {
557
- * providers: [provideLunora({ url: "https://api.example.com" })],
558
- * };
559
- * ```
560
- *
561
- * Pass {@link LunoraClientOptions} to configure a fresh client (URL defaults to
562
- * the page origin), or hand in an already-constructed {@link LunoraClient} to
563
- * share one instance (e.g. a client you also preload against during SSR).
564
- * @experimental
565
- */
602
+ * Wire a {@link LunoraClient} into the application injector. Add the result to the
603
+ * `providers` array of an Angular application config (or any `EnvironmentProviders`
604
+ * consumer):
605
+ *
606
+ * ```ts
607
+ * export const appConfig: ApplicationConfig = {
608
+ * providers: [provideLunora({ url: "https://api.example.com" })],
609
+ * };
610
+ * ```
611
+ *
612
+ * Pass {@link LunoraClientOptions} to configure a fresh client (URL defaults to
613
+ * the page origin), or hand in an already-constructed {@link LunoraClient} to
614
+ * share one instance (e.g. a client you also preload against during SSR).
615
+ * @experimental
616
+ */
566
617
  declare const provideLunora: (optionsOrClient?: LunoraClient | ProvideLunoraOptions) => EnvironmentProviders;
567
618
  /**
568
- * Read the {@link LunoraClient} from the current injector. Call inside an
569
- * injection context (a component/service field initializer or constructor, or a
570
- * `runInInjectionContext` callback). Use it to hold the client for imperative
571
- * calls — e.g. `mutation`/`action` from event handlers, which run outside an
572
- * injection context:
573
- *
574
- * ```ts
575
- * private readonly client = injectLunoraClient();
576
- * send = (text: string) => this.client.mutation(api.messages.send, { text });
577
- * ```
578
- * @experimental
579
- */
619
+ * Read the {@link LunoraClient} from the current injector. Call inside an
620
+ * injection context (a component/service field initializer or constructor, or a
621
+ * `runInInjectionContext` callback). Use it to hold the client for imperative
622
+ * calls — e.g. `mutation`/`action` from event handlers, which run outside an
623
+ * injection context:
624
+ *
625
+ * ```ts
626
+ * private readonly client = injectLunoraClient();
627
+ * send = (text: string) => this.client.mutation(api.messages.send, { text });
628
+ * ```
629
+ * @experimental
630
+ */
580
631
  declare const injectLunoraClient: () => LunoraClient;
581
632
  /**
582
- * `ConnectionStatusOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
583
- * @experimental
584
- */
633
+ * `ConnectionStatusOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
634
+ * @experimental
635
+ */
585
636
  interface ConnectionStatusOptions {
586
637
  /** Client to observe. Defaults to the injected `LUNORA_CLIENT`. */
587
638
  client?: LunoraClient;
@@ -589,93 +640,90 @@ interface ConnectionStatusOptions {
589
640
  destroyRef?: DestroyRef;
590
641
  }
591
642
  /**
592
- * A `signal` of the client's aggregate live-socket status across all shard
593
- * connections. Reads the current status synchronously and updates on every
594
- * transition (`idle` → `connecting` → `connected` → `offline`). The Angular
595
- * equivalent of `@lunora/react`'s `useConnectionStatus`.
596
- *
597
- * The listener is removed when the owning `DestroyRef` fires. Call from an
598
- * injection context (component/service field or constructor).
599
- * @experimental
600
- */
643
+ * A `signal` of the client's aggregate live-socket status across all shard
644
+ * connections. Reads the current status synchronously and updates on every
645
+ * transition (`idle` → `connecting` → `connected` → `offline`). The Angular
646
+ * equivalent of `@lunora/react`'s `useConnectionStatus`.
647
+ *
648
+ * The listener is removed when the owning `DestroyRef` fires. Call from an
649
+ * injection context (component/service field or constructor).
650
+ * @experimental
651
+ */
601
652
  declare const connectionStatus: (options?: ConnectionStatusOptions) => Signal<ConnectionStatus>;
602
- /**
603
- * The value kinds a flag resolves to — OpenFeature's boolean / number / string / structured (JSON) flags.
604
- * @experimental
605
- */
606
- type FlagValue = boolean | number | string | Record<string, unknown> | unknown[] | null;
607
- /**
608
- * Targeting context bag forwarded to the OpenFeature provider.
609
- * @experimental
610
- */
611
- type FlagContext = Record<string, unknown>;
612
- /**
613
- * `FlagOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
614
- * @experimental
615
- */
653
+ /** The value kinds a flag resolves to — OpenFeature's boolean / number / string / structured (JSON) flags. */
654
+ type FlagValue$1 = boolean | number | string | {
655
+ [key: string]: unknown;
656
+ } | unknown[] | null;
657
+ /**
658
+ * The value kinds a flag resolves to — OpenFeature's boolean / number / string / structured (JSON) flags.
659
+ * @experimental
660
+ */
661
+ type FlagValue = FlagValue$1;
662
+ /**
663
+ * `FlagOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
664
+ * @experimental
665
+ */
616
666
  interface FlagOptions {
617
667
  /** Client to bind to. Defaults to the injected `LUNORA_CLIENT`. */
618
668
  client?: LunoraClient;
619
- /**
620
- * Per-call targeting context merged on top of the app's default `identify`
621
- * targeting key.
622
- */
623
- context?: FlagContext;
624
669
  /** `DestroyRef` whose `onDestroy` tears down the subscription. Defaults to `inject(DestroyRef)`. */
625
670
  destroyRef?: DestroyRef;
626
671
  }
627
672
  /**
628
- * Subscribe to a single feature flag, live over Lunora's WebSocket.
629
- *
630
- * The returned signal holds `defaultValue` until the first evaluation lands, then
631
- * the server's resolved value — re-pushed whenever the provider re-evaluates.
632
- * The flag's kind is inferred from `defaultValue`'s runtime type, so
633
- * `flag("dark", false)` reads a boolean and `flag("hero", "control")` a string.
634
- *
635
- * Evaluation runs through whatever OpenFeature provider the app wired in
636
- * `lunora/flags.ts`; the read never throws — a provider error resolves the
637
- * default (the same fail-open contract as server-side `ctx.flags`).
638
- *
639
- * Call from an injection context:
640
- * ```ts
641
- * readonly darkMode = flag("dark-mode", false);
642
- * ```
643
- * @experimental
644
- */
673
+ * Subscribe to a single feature flag, live over Lunora's WebSocket.
674
+ *
675
+ * The returned signal holds `defaultValue` until the first evaluation lands, then
676
+ * the server's resolved value — re-pushed whenever the provider re-evaluates.
677
+ * The flag's kind is inferred from `defaultValue`'s runtime type, so
678
+ * `flag("dark", false)` reads a boolean and `flag("hero", "control")` a string.
679
+ *
680
+ * The reactive channel is public, so the server evaluates every flag under the
681
+ * socket's own verified identity — the targeting key your `defineFlags({
682
+ * identify })` derives — and accepts no client-supplied targeting context. For
683
+ * evaluation under a context you compute, call `ctx.flags.*` inside a query,
684
+ * mutation, or action and return the resolved value.
685
+ *
686
+ * Evaluation runs through whatever OpenFeature provider the app wired in
687
+ * `lunora/flags.ts`; the read never throws — a provider error resolves the
688
+ * default (the same fail-open contract as server-side `ctx.flags`). No
689
+ * subscription opens during SSR; the signal stays at `defaultValue`.
690
+ *
691
+ * Call from an injection context:
692
+ * ```ts
693
+ * readonly darkMode = flag("dark-mode", false);
694
+ * ```
695
+ * @experimental
696
+ */
645
697
  declare const flag: <T extends FlagValue>(key: string, defaultValue: T, options?: FlagOptions) => Signal<T>;
646
698
  /**
647
- * `FlagsOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
648
- * @experimental
649
- */
699
+ * `FlagsOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
700
+ * @experimental
701
+ */
650
702
  interface FlagsOptions {
651
703
  /** Client to bind to. Defaults to the injected `LUNORA_CLIENT`. */
652
704
  client?: LunoraClient;
653
- /**
654
- * Targeting context shared by every flag in the set, merged on top of the
655
- * app's default `identify` targeting key.
656
- */
657
- context?: FlagContext;
658
705
  /** `DestroyRef` whose `onDestroy` tears down the subscriptions. Defaults to `inject(DestroyRef)`. */
659
706
  destroyRef?: DestroyRef;
660
707
  }
661
708
  /**
662
- * Subscribe to several feature flags at once, live over Lunora's WebSocket.
663
- *
664
- * Pass a record of `key → defaultValue`; each flag's kind is inferred from its
665
- * default, and the returned signal holds the same-shaped record with resolved
666
- * values (the defaults until each evaluation lands).
667
- *
668
- * Call from an injection context:
669
- * ```ts
670
- * readonly features = flags({ "dark-mode": false, "new-editor": false });
671
- * ```
672
- * @experimental
673
- */
709
+ * Subscribe to several feature flags at once, live over Lunora's WebSocket.
710
+ *
711
+ * Pass a record of `key → defaultValue`; each flag's kind is inferred from its
712
+ * default, and the returned signal holds the same-shaped record with resolved
713
+ * values (the defaults until each evaluation lands). Like {@link flag} it
714
+ * evaluates under the socket's server-verified identity only.
715
+ *
716
+ * Call from an injection context:
717
+ * ```ts
718
+ * readonly features = flags({ "dark-mode": false, "new-editor": false });
719
+ * ```
720
+ * @experimental
721
+ */
674
722
  declare const flags: <T extends Record<string, FlagValue>>(flagDefaults: T, options?: FlagsOptions) => Signal<T>;
675
723
  /**
676
- * `HydratePreloadedOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
677
- * @experimental
678
- */
724
+ * `HydratePreloadedOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
725
+ * @experimental
726
+ */
679
727
  interface HydratePreloadedOptions {
680
728
  /** Client to bind to. Defaults to the injected `LUNORA_CLIENT`. */
681
729
  client?: LunoraClient;
@@ -683,9 +731,9 @@ interface HydratePreloadedOptions {
683
731
  destroyRef?: DestroyRef;
684
732
  }
685
733
  /**
686
- * `HydratePreloadedResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
687
- * @experimental
688
- */
734
+ * `HydratePreloadedResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
735
+ * @experimental
736
+ */
689
737
  interface HydratePreloadedResult<T> {
690
738
  /** The latest value pushed by the server. Seeded synchronously from the preloaded value. */
691
739
  data: Signal<T | undefined>;
@@ -693,114 +741,132 @@ interface HydratePreloadedResult<T> {
693
741
  error: Signal<SubscriptionError | undefined>;
694
742
  }
695
743
  /**
696
- * Hydrate a query from a {@link Preloaded} token produced by `preloadQuery`
697
- * during SSR, then keep it live — the Angular half of the reactive-loader
698
- * handoff.
699
- *
700
- * The returned signal is seeded **synchronously** from `preloaded.value`, so the
701
- * very first read (during hydration) shows the server value: no loading flash,
702
- * no hydration mismatch. After seeding it opens a WebSocket subscription on the
703
- * same `(functionPath, args, shardKey)` the SSR loader used, so every later
704
- * server delta updates the signal exactly like `liveQuery`.
705
- *
706
- * The subscription tears down when the owning `DestroyRef` fires.
707
- *
708
- * Call from an injection context:
709
- * ```ts
710
- * readonly { data, error } = hydratePreloaded(preloadedMessages);
711
- * ```
712
- * @experimental
713
- */
744
+ * Hydrate a query from a {@link Preloaded} token produced by `preloadQuery`
745
+ * during SSR, then keep it live — the Angular half of the reactive-loader
746
+ * handoff.
747
+ *
748
+ * The returned signal is seeded **synchronously** from `preloaded.value`, so the
749
+ * very first read (during hydration) shows the server value: no loading flash,
750
+ * no hydration mismatch. After seeding it opens a WebSocket subscription on the
751
+ * same `(functionPath, args, shardKey)` the SSR loader used, so every later
752
+ * server delta updates the signal exactly like `liveQuery`.
753
+ *
754
+ * The subscription tears down when the owning `DestroyRef` fires.
755
+ *
756
+ * Call from an injection context:
757
+ * ```ts
758
+ * readonly { data, error } = hydratePreloaded(preloadedMessages);
759
+ * ```
760
+ * @experimental
761
+ */
714
762
  declare const hydratePreloaded: <T>(preloaded: Preloaded<T>, options?: HydratePreloadedOptions) => HydratePreloadedResult<T>;
715
763
  /**
716
- * `LiveQueryOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
717
- * @experimental
718
- */
764
+ * `LiveQueryOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
765
+ * @experimental
766
+ */
719
767
  interface LiveQueryOptions {
720
768
  /**
721
- * Client to bind to. Defaults to the injected `LUNORA_CLIENT`; pass one
722
- * explicitly to use `liveQuery` outside an injection context (or in a test).
723
- */
769
+ * Client to bind to. Defaults to the injected `LUNORA_CLIENT`; pass one
770
+ * explicitly to use `liveQuery` outside an injection context (or in a test).
771
+ */
724
772
  client?: LunoraClient;
725
773
  /**
726
- * `DestroyRef` whose `onDestroy` tears the subscription down. Defaults to
727
- * `inject(DestroyRef)` — the calling component/service — so it closes when that
728
- * component is destroyed. Pass one explicitly to control the lifetime yourself.
729
- */
774
+ * `DestroyRef` whose `onDestroy` tears the subscription down. Defaults to
775
+ * `inject(DestroyRef)` — the calling component/service — so it closes when that
776
+ * component is destroyed. Pass one explicitly to control the lifetime yourself.
777
+ */
730
778
  destroyRef?: DestroyRef;
731
779
  /**
732
- * Called when the subscription errors after the initial attach — the async
733
- * error channel `createQuerySubscription` only wires when a sink is present.
734
- * Without it, a post-attach failure is dropped silently: the signal simply
735
- * stops updating with no error state exposed. Pass a handler to surface it
736
- * (log, toast, set an error signal of your own).
737
- */
780
+ * `Injector` to create the reactive-args `effect()` from. Only needed when
781
+ * `args` is a function/`Signal` AND `liveQuery` is called outside an injection
782
+ * context (an explicit `destroyRef` is also being passed — e.g. from
783
+ * `ngOnInit`, or from a test with no `TestBed`) — `effect()` cannot resolve an
784
+ * injector on its own there. Defaults to the ambient injection context, the
785
+ * same source `inject(DestroyRef)` already relies on. Unused for the static
786
+ * `args` form, which never creates an `effect()`.
787
+ */
788
+ injector?: Injector;
789
+ /**
790
+ * Called when the subscription errors after the initial attach — the async
791
+ * error channel `createQuerySubscription` only wires when a sink is present.
792
+ * Without it, a post-attach failure is dropped silently: the signal simply
793
+ * stops updating with no error state exposed. Pass a handler to surface it
794
+ * (log, toast, set an error signal of your own).
795
+ */
738
796
  onError?: (error: SubscriptionError) => void;
739
797
  /** Route to a specific shard when the target function is `.shardBy(...)`-partitioned. */
740
798
  shardKey?: string;
741
799
  }
742
800
  /**
743
- * Subscribe to a server query and mirror its value into an Angular `signal`.
744
- *
745
- * Reads `undefined` until the first server frame lands, then updates on every
746
- * delta the WebSocket pushes. The underlying subscription is torn down
747
- * automatically when the owning `DestroyRef` fires (the component/service is
748
- * destroyed), so there is no leaked socket subscription.
749
- *
750
- * Call it from an injection context (a component/service field initializer or
751
- * constructor) so the default `inject(DestroyRef)` resolves the caller's
752
- * lifetime:
753
- *
754
- * ```ts
755
- * export class MessagesComponent {
756
- * readonly messages = liveQuery(api.messages.list, { channelId: "general" });
757
- * }
758
- * ```
759
- *
760
- * Pass `"skip"` (the `SKIP` sentinel from `@lunora/client/query`) as `args` to
761
- * short-circuit — no network call, no socket; the signal stays `undefined`. To
762
- * call outside an injection context (e.g. lazily in `ngOnInit`), supply `client`
763
- * and `destroyRef` via {@link LiveQueryOptions}.
764
- * @experimental
765
- */
766
- declare const liveQuery: <F extends FunctionReference>(reference: F, args: ArgsOf<F> | "skip", options?: LiveQueryOptions) => Signal<ReturnOf<F> | undefined>;
767
- /**
768
- * `MutateOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
769
- * @experimental
770
- */
801
+ * Subscribe to a server query and mirror its value into an Angular `signal`.
802
+ *
803
+ * Reads `undefined` until the first server frame lands, then updates on every
804
+ * delta the WebSocket pushes. The underlying subscription is torn down
805
+ * automatically when the owning `DestroyRef` fires (the component/service is
806
+ * destroyed), so there is no leaked socket subscription.
807
+ *
808
+ * Call it from an injection context (a component/service field initializer or
809
+ * constructor) so the default `inject(DestroyRef)` resolves the caller's
810
+ * lifetime:
811
+ *
812
+ * ```ts
813
+ * export class MessagesComponent {
814
+ * readonly messages = liveQuery(api.messages.list, { channelId: "general" });
815
+ * }
816
+ * ```
817
+ *
818
+ * Pass `"skip"` (the `SKIP` sentinel from `@lunora/client/query`) as `args` to
819
+ * short-circuit — no network call, no socket; the signal stays `undefined`. To
820
+ * call outside an injection context (e.g. lazily in `ngOnInit`), supply `client`
821
+ * and `destroyRef` via {@link LiveQueryOptions}.
822
+ *
823
+ * `args` also accepts a function/`Signal` — `() => ({ channelId: channelId() })`
824
+ * — to make the subscription reactive: an args change tears the old
825
+ * subscription down, resets the signal to `undefined`, and opens a fresh one
826
+ * for the new args, mirroring
827
+ * `@lunora/solid`'s `createQuery`/`@lunora/vue`'s `useQuery`. A static (plain
828
+ * object) `args` resolves once and never re-runs — no `effect()` is created for
829
+ * it, so it carries none of the reactive form's DI requirement.
830
+ * @experimental
831
+ */
832
+ declare const liveQuery: <F extends FunctionReference>(reference: F, args: ArgsOf<F> | "skip" | (() => ArgsOf<F> | "skip"), options?: LiveQueryOptions) => Signal<ReturnOf<F> | undefined>;
833
+ /**
834
+ * `MutateOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
835
+ * @experimental
836
+ */
771
837
  interface MutateOptions<F extends FunctionReference> extends MutationCallOptions<unknown, unknown, ArgsOf<F>> {
772
838
  /**
773
- * Client to run the mutation on. Defaults to the injected `LUNORA_CLIENT`.
774
- * Because mutations usually fire from event handlers — which run *outside* an
775
- * injection context — capture the client once (`injectLunoraClient()` in a
776
- * field) and pass it here, or call `client.mutation(...)` directly.
777
- */
839
+ * Client to run the mutation on. Defaults to the injected `LUNORA_CLIENT`.
840
+ * Because mutations usually fire from event handlers — which run *outside* an
841
+ * injection context — capture the client once (`injectLunoraClient()` in a
842
+ * field) and pass it here, or call `client.mutation(...)` directly.
843
+ */
778
844
  client?: LunoraClient;
779
845
  }
780
846
  /**
781
- * Run a Lunora mutation and resolve with the server result (rejects on failure).
782
- *
783
- * Optimistic updates stay client-owned: the `optimistic` / `optimisticUpdate`
784
- * call options pass straight through to `client.mutation`, which applies and
785
- * rolls them back against the live subscription cache — the same cache
786
- * `liveQuery` reads, so an optimistic write reflects immediately and
787
- * reverts on failure. The client's offline queue also engages when the socket is
788
- * down, so the write stays durable across reconnects.
789
- *
790
- * ```ts
791
- * private readonly client = injectLunoraClient();
792
- * send = (text: string) => mutate(api.messages.send, { text }, { client: this.client });
793
- * ```
794
- *
795
- * When called from within an injection context you may omit `client` and let it
796
- * resolve from the injector.
797
- * @experimental
798
- */
847
+ * Run a Lunora mutation and resolve with the server result (rejects on failure).
848
+ *
849
+ * Optimistic updates stay client-owned: the `optimistic` / `optimisticUpdate`
850
+ * call options pass straight through to `client.mutation`, which applies and
851
+ * rolls them back against the live subscription cache — the same cache
852
+ * `liveQuery` reads, so an optimistic write reflects immediately and
853
+ * reverts on failure. The client's offline queue also engages when the socket is
854
+ * down, so the write stays durable across reconnects.
855
+ *
856
+ * ```ts
857
+ * private readonly client = injectLunoraClient();
858
+ * send = (text: string) => mutate(api.messages.send, { text }, { client: this.client });
859
+ * ```
860
+ *
861
+ * When called from within an injection context you may omit `client` and let it
862
+ * resolve from the injector.
863
+ * @experimental
864
+ */
799
865
  declare const mutate: <F extends FunctionReference>(reference: F, args: ArgsOf<F>, options?: MutateOptions<F>) => Promise<ReturnOf<F>>;
800
866
  /**
801
- * `MutatorResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
802
- * @experimental
803
- */
867
+ * `MutatorResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
868
+ * @experimental
869
+ */
804
870
  interface MutatorResult<TArgs> {
805
871
  /** The latest invocation's error, or `undefined`. */
806
872
  error: Signal<Error | undefined>;
@@ -814,23 +880,23 @@ interface MutatorResult<TArgs> {
814
880
  reset: () => void;
815
881
  }
816
882
  /**
817
- * Ergonomic `{ mutate, pending, error, isError, reset }` wrapper over a bound
818
- * custom-mutator handle from `` `@lunora/db` ``'s `bindMutators` — the Angular
819
- * equivalent of `` `@lunora/react` ``'s `useMutator`. The optimistic overlay and
820
- * server-authoritative push are owned by the bound handle; this function only
821
- * surfaces reactive state for the in-flight/error lifecycle.
822
- *
823
- * `pending` is ref-counted across overlapping invocations of THIS handle, so it
824
- * clears only once every concurrent call has settled.
825
- *
826
- * Does NOT require an injection context — it works with plain signals.
827
- *
828
- * ```ts
829
- * private readonly collection = bindMutators(collections);
830
- * readonly mutator = mutator(this.collection.insert);
831
- * ```
832
- * @experimental
833
- */
883
+ * Ergonomic `{ mutate, pending, error, isError, reset }` wrapper over a bound
884
+ * custom-mutator handle from `` `@lunora/db` ``'s `bindMutators` — the Angular
885
+ * equivalent of `` `@lunora/react` ``'s `useMutator`. The optimistic overlay and
886
+ * server-authoritative push are owned by the bound handle; this function only
887
+ * surfaces reactive state for the in-flight/error lifecycle.
888
+ *
889
+ * `pending` is ref-counted across overlapping invocations of THIS handle, so it
890
+ * clears only once every concurrent call has settled.
891
+ *
892
+ * Does NOT require an injection context — it works with plain signals.
893
+ *
894
+ * ```ts
895
+ * private readonly collection = bindMutators(collections);
896
+ * readonly mutator = mutator(this.collection.insert);
897
+ * ```
898
+ * @experimental
899
+ */
834
900
  declare const mutator: <TArgs = Record<string, unknown>>(handle: MutatorHandle<TArgs>) => MutatorResult<TArgs>;
835
901
  /** The args a paginated query exposes minus the framework-supplied page cursor. */
836
902
  type PaginatedArgs<F extends FunctionReference> = Omit<ArgsOfRaw<F>, "paginationOpts">;
@@ -841,9 +907,9 @@ type PageItemOf<F extends FunctionReference> = ReturnTypeOf<F> extends {
841
907
  type ArgsOfRaw<F extends FunctionReference> = F extends FunctionReference<"query", infer A> ? A : never;
842
908
  type ReturnTypeOf<F extends FunctionReference> = F extends FunctionReference<"query", unknown, infer R> ? R : never;
843
909
  /**
844
- * Options for the paginated query — part of the experimental `@lunora/angular` API and may change without a major version bump.
845
- * @experimental
846
- */
910
+ * Options for the paginated query — part of the experimental `@lunora/angular` API and may change without a major version bump.
911
+ * @experimental
912
+ */
847
913
  interface PaginatedQueryOptions {
848
914
  /** Client to bind to. Defaults to the injected `LUNORA_CLIENT`. */
849
915
  client?: LunoraClient;
@@ -851,14 +917,30 @@ interface PaginatedQueryOptions {
851
917
  destroyRef?: DestroyRef;
852
918
  /** Page size for the first page (and the default for `loadMore`). */
853
919
  initialNumItems: number;
920
+ /**
921
+ * `Injector` to create the reactive-args `effect()` from. Only needed when
922
+ * `args` is a function/`Signal` AND the call is outside an injection context
923
+ * (an explicit `destroyRef` is also being passed). Defaults to the ambient
924
+ * injection context. Unused for the static `args` form.
925
+ */
926
+ injector?: Injector;
927
+ /** Called when a page subscription reports an error (also surfaced on the `error` signal). */
928
+ onError?: SubscriptionErrorCallback;
854
929
  /** Route to a specific shard when the target function is `.shardBy(...)`-partitioned. */
855
930
  shardKey?: string;
856
931
  }
857
932
  /**
858
- * `PaginatedQueryResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
859
- * @experimental
860
- */
933
+ * `PaginatedQueryResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
934
+ * @experimental
935
+ */
861
936
  interface PaginatedQueryResult<T> {
937
+ /**
938
+ * The last page subscription error, or `undefined`. A tail page that fails
939
+ * before its first frame is dropped so `status` returns to `"CanLoadMore"`
940
+ * and `loadMore` can retry it; cleared by the next successful frame,
941
+ * `loadMore`, or an args change.
942
+ */
943
+ error: Signal<SubscriptionError | undefined>;
862
944
  /** `true` while the first page or a `loadMore` page is in flight. */
863
945
  isLoading: Signal<boolean>;
864
946
  /** Request the next page. A no-op unless `status === "CanLoadMore"`. */
@@ -869,10 +951,12 @@ interface PaginatedQueryResult<T> {
869
951
  status: Signal<PaginationStatus>;
870
952
  }
871
953
  /**
872
- * `InfiniteQueryResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
873
- * @experimental
874
- */
954
+ * `InfiniteQueryResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
955
+ * @experimental
956
+ */
875
957
  interface InfiniteQueryResult<T> {
958
+ /** The last page subscription error, or `undefined` — see `PaginatedQueryResult.error`. */
959
+ error: Signal<SubscriptionError | undefined>;
876
960
  /** Request the next page. A no-op unless `status === "CanLoadMore"`. */
877
961
  fetchNextPage: (numberItems?: number) => void;
878
962
  /** `true` when the loaded tail reports it can load another page. */
@@ -887,57 +971,64 @@ interface InfiniteQueryResult<T> {
887
971
  status: Signal<PaginationStatus>;
888
972
  }
889
973
  /**
890
- * Subscribe to a reactively-paginated query and grow the feed page by page.
891
- *
892
- * The query function must accept a `paginationOpts: { numItems, cursor,
893
- * endCursor }` arg and return a `PaginationResult`. Pages are tracked as an
894
- * ordered list of stable boundary cursors; each loaded page is a live
895
- * subscription over a FIXED `(lower, upper]` range.
896
- *
897
- * `loadMore` appends the next page off the open-ended tail's `continueCursor`;
898
- * it is a no-op unless `status === "CanLoadMore"`.
899
- *
900
- * Call from an injection context:
901
- * ```ts
902
- * readonly messages = paginatedQuery(api.messages.list, {}, { initialNumItems: 20 });
903
- * ```
904
- * @experimental
905
- */
906
- declare const paginatedQuery: <F extends FunctionReference>(reference: F, args: PaginatedArgs<F> | "skip", options: PaginatedQueryOptions) => PaginatedQueryResult<PageItemOf<F>>;
907
- /**
908
- * Subscribe to a reactively-paginated query and expose its pages discretely.
909
- *
910
- * Shares `paginatedQuery`'s reactive-pagination engine but keeps each page as
911
- * its own inner array rather than flattening them, and adds the TanStack-Query-
912
- * style `fetchNextPage` / `hasNextPage` / `isFetchingNextPage` shape.
913
- *
914
- * Call from an injection context:
915
- * ```ts
916
- * readonly feed = infiniteQuery(api.messages.list, {}, { initialNumItems: 20 });
917
- * ```
918
- * @experimental
919
- */
920
- declare const infiniteQuery: <F extends FunctionReference>(reference: F, args: PaginatedArgs<F> | "skip", options: PaginatedQueryOptions) => InfiniteQueryResult<PageItemOf<F>>;
921
- /**
922
- * `HeartbeatReference` is part of the experimental `@lunora/angular` API and may change without a major version bump.
923
- * @experimental
924
- */
974
+ * Subscribe to a reactively-paginated query and grow the feed page by page.
975
+ *
976
+ * The query function must accept a `paginationOpts: { numItems, cursor,
977
+ * endCursor }` arg and return a `PaginationResult`. Pages are tracked as an
978
+ * ordered list of stable boundary cursors; each loaded page is a live
979
+ * subscription over a FIXED `(lower, upper]` range.
980
+ *
981
+ * `loadMore` appends the next page off the open-ended tail's `continueCursor`;
982
+ * it is a no-op unless `status === "CanLoadMore"`.
983
+ *
984
+ * Call from an injection context:
985
+ * ```ts
986
+ * readonly messages = paginatedQuery(api.messages.list, {}, { initialNumItems: 20 });
987
+ * ```
988
+ *
989
+ * `args` also accepts a function/`Signal` to make the query reactive — an args
990
+ * change disposes the current pagination engine and builds a fresh one for the
991
+ * new args. A static (plain object) `args` resolves once and never re-runs.
992
+ * @experimental
993
+ */
994
+ declare const paginatedQuery: <F extends FunctionReference>(reference: F, args: PaginatedArgs<F> | "skip" | (() => PaginatedArgs<F> | "skip"), options: PaginatedQueryOptions) => PaginatedQueryResult<PageItemOf<F>>;
995
+ /**
996
+ * Subscribe to a reactively-paginated query and expose its pages discretely.
997
+ *
998
+ * Shares `paginatedQuery`'s reactive-pagination engine but keeps each page as
999
+ * its own inner array rather than flattening them, and adds the TanStack-Query-
1000
+ * style `fetchNextPage` / `hasNextPage` / `isFetchingNextPage` shape.
1001
+ *
1002
+ * Call from an injection context:
1003
+ * ```ts
1004
+ * readonly feed = infiniteQuery(api.messages.list, {}, { initialNumItems: 20 });
1005
+ * ```
1006
+ *
1007
+ * `args` also accepts a function/`Signal` to make the query reactive — see
1008
+ * `paginatedQuery`'s equivalent note.
1009
+ * @experimental
1010
+ */
1011
+ declare const infiniteQuery: <F extends FunctionReference>(reference: F, args: PaginatedArgs<F> | "skip" | (() => PaginatedArgs<F> | "skip"), options: PaginatedQueryOptions) => InfiniteQueryResult<PageItemOf<F>>;
1012
+ /**
1013
+ * `HeartbeatReference` is part of the experimental `@lunora/angular` API and may change without a major version bump.
1014
+ * @experimental
1015
+ */
925
1016
  type HeartbeatReference = FunctionReference<"mutation", {
926
1017
  data?: Record<string, unknown>;
927
1018
  roomId: string;
928
1019
  sessionId: string;
929
1020
  }>;
930
1021
  /**
931
- * `ListPresentReference` is part of the experimental `@lunora/angular` API and may change without a major version bump.
932
- * @experimental
933
- */
1022
+ * `ListPresentReference` is part of the experimental `@lunora/angular` API and may change without a major version bump.
1023
+ * @experimental
1024
+ */
934
1025
  type ListPresentReference = FunctionReference<"query", {
935
1026
  roomId: string;
936
1027
  }>;
937
1028
  /**
938
- * `PresenceOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
939
- * @experimental
940
- */
1029
+ * `PresenceOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
1030
+ * @experimental
1031
+ */
941
1032
  interface PresenceOptions<H extends HeartbeatReference, L extends ListPresentReference> {
942
1033
  /** Client to bind to. Defaults to the injected `LUNORA_CLIENT`. */
943
1034
  client?: LunoraClient;
@@ -952,18 +1043,26 @@ interface PresenceOptions<H extends HeartbeatReference, L extends ListPresentRef
952
1043
  /** The `api.*` reference for the presence listPresent query. */
953
1044
  listPresent: L;
954
1045
  /**
955
- * Stable id for this presence row. Defaults to a fresh per-call id.
956
- * Pass a user/connection id to control deduping across tabs.
957
- */
1046
+ * Called when the `listPresent` subscription reports an error (a session
1047
+ * expiry, an RLS denial). Without it — and without reading `error` — such a
1048
+ * failure is invisible and `present` is cleared until a later frame arrives.
1049
+ */
1050
+ onError?: SubscriptionErrorCallback;
1051
+ /**
1052
+ * Stable id for this presence row. Defaults to a fresh per-call id.
1053
+ * Pass a user/connection id to control deduping across tabs.
1054
+ */
958
1055
  sessionId?: string;
959
1056
  /** Forwarded to the heartbeat mutation / listPresent subscription when sharding by room. */
960
1057
  shardKey?: string;
961
1058
  }
962
1059
  /**
963
- * `PresenceResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
964
- * @experimental
965
- */
1060
+ * `PresenceResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
1061
+ * @experimental
1062
+ */
966
1063
  interface PresenceResult<L extends ListPresentReference> {
1064
+ /** The `listPresent` subscription's last error, or `undefined`. */
1065
+ error: Signal<SubscriptionError | undefined>;
967
1066
  /** The present members for the room. `undefined` until the first push. */
968
1067
  present: Signal<ReturnOf<L> | undefined>;
969
1068
  /** This mount's session id (generated when not supplied). */
@@ -972,44 +1071,44 @@ interface PresenceResult<L extends ListPresentReference> {
972
1071
  setData: (data: Record<string, unknown> | undefined) => void;
973
1072
  }
974
1073
  /**
975
- * `presence` — collaborative-awareness primitive, the client half of the
976
- * `@lunora/server` `definePresence` preset.
977
- *
978
- * Drives the heartbeat mutation (on mount, interval, and tab re-focus) and
979
- * subscribes to the live `listPresent` query for the given room.
980
- *
981
- * Call from an injection context (component/service field or constructor):
982
- * ```ts
983
- * readonly roomPresence = presence("room:general", {
984
- * heartbeat: api.presence.heartbeat,
985
- * listPresent: api.presence.listPresent,
986
- * });
987
- * ```
988
- * @experimental
989
- */
1074
+ * `presence` — collaborative-awareness primitive, the client half of the
1075
+ * `@lunora/server` `definePresence` preset.
1076
+ *
1077
+ * Drives the heartbeat mutation (on mount, interval, and tab re-focus) and
1078
+ * subscribes to the live `listPresent` query for the given room.
1079
+ *
1080
+ * Call from an injection context (component/service field or constructor):
1081
+ * ```ts
1082
+ * readonly roomPresence = presence("room:general", {
1083
+ * heartbeat: api.presence.heartbeat,
1084
+ * listPresent: api.presence.listPresent,
1085
+ * });
1086
+ * ```
1087
+ * @experimental
1088
+ */
990
1089
  declare const presence: <H extends HeartbeatReference, L extends ListPresentReference>(roomId: string, options: PresenceOptions<H, L>) => PresenceResult<L>;
991
1090
  /**
992
- * `RateLimitOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
993
- * @experimental
994
- */
1091
+ * `RateLimitOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
1092
+ * @experimental
1093
+ */
995
1094
  interface RateLimitOptions {
996
1095
  /**
997
- * `DestroyRef` whose `onDestroy` clears the interval. Defaults to
998
- * `inject(DestroyRef)` — the calling component/service.
999
- */
1096
+ * `DestroyRef` whose `onDestroy` clears the interval. Defaults to
1097
+ * `inject(DestroyRef)` — the calling component/service.
1098
+ */
1000
1099
  destroyRef?: DestroyRef;
1001
1100
  /** Clock injection for tests. Defaults to `Date.now`. */
1002
1101
  now?: () => number;
1003
1102
  /**
1004
- * Re-render cadence in milliseconds while throttled, so `retryAfter` ticks
1005
- * down and `disabled` flips back automatically. Defaults to `1000`.
1006
- */
1103
+ * Re-render cadence in milliseconds while throttled, so `retryAfter` ticks
1104
+ * down and `disabled` flips back automatically. Defaults to `1000`.
1105
+ */
1007
1106
  tickMs?: number;
1008
1107
  }
1009
1108
  /**
1010
- * `RateLimitResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
1011
- * @experimental
1012
- */
1109
+ * `RateLimitResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
1110
+ * @experimental
1111
+ */
1013
1112
  interface RateLimitResult {
1014
1113
  /** Would consuming `count` (default 1) succeed right now? Does not consume. */
1015
1114
  check: (count?: number) => boolean;
@@ -1025,46 +1124,90 @@ interface RateLimitResult {
1025
1124
  retryAfter: Signal<number>;
1026
1125
  }
1027
1126
  /**
1028
- * Client-side mirror of a rate limit for instant UX — disable a button or show
1029
- * a countdown without a round-trip. It runs the same token-bucket / fixed-window
1030
- * math as `@lunora/ratelimit` on the server, so the prediction agrees with the
1031
- * authoritative check; the server remains the source of truth.
1032
- *
1033
- * Requires an Angular injection context unless a `DestroyRef` is passed
1034
- * explicitly via `options.destroyRef`.
1035
- *
1036
- * ```ts
1037
- * readonly sendLimit = rateLimit({ kind: "token bucket", period: 1000, rate: 10 });
1038
- * ```
1039
- * @experimental
1040
- */
1127
+ * Client-side mirror of a rate limit for instant UX — disable a button or show
1128
+ * a countdown without a round-trip. It runs the same token-bucket / fixed-window
1129
+ * math as `@lunora/ratelimit` on the server, so the prediction agrees with the
1130
+ * authoritative check; the server remains the source of truth.
1131
+ *
1132
+ * Requires an Angular injection context unless a `DestroyRef` is passed
1133
+ * explicitly via `options.destroyRef`.
1134
+ *
1135
+ * ```ts
1136
+ * readonly sendLimit = rateLimit({ kind: "token bucket", period: 1000, rate: 10 });
1137
+ * ```
1138
+ * @experimental
1139
+ */
1041
1140
  declare const rateLimit: (config: RateLimitConfig, options?: RateLimitOptions) => RateLimitResult;
1042
1141
  /**
1043
- * The lifecycle of a stream the primitive is observing.
1044
- * @experimental
1045
- */
1142
+ * `RunActionOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
1143
+ * @experimental
1144
+ */
1145
+ interface RunActionOptions extends ActionCallOptions {
1146
+ /**
1147
+ * Client to run the action on. Defaults to the injected `LUNORA_CLIENT`.
1148
+ * Because actions usually fire from event handlers — which run *outside* an
1149
+ * injection context — capture the client once (`injectLunoraClient()` in a
1150
+ * field) and pass it here, or call `client.action(...)` directly.
1151
+ */
1152
+ client?: LunoraClient;
1153
+ }
1154
+ /**
1155
+ * Run a Lunora action and resolve with the server result (rejects on failure).
1156
+ *
1157
+ * The sibling of `mutate`, and a plain function for the same reason: Angular's
1158
+ * adapter models writes as calls rather than reactive handles, because they fire
1159
+ * from event handlers where a signal-returning primitive has nothing to bind to.
1160
+ * The other adapters return a reactive `{ call, pending, … }` handle because
1161
+ * their idioms make that natural; this one does not.
1162
+ *
1163
+ * Unlike `mutate` there are no `optimistic` / `optimisticUpdate` options. An
1164
+ * optimistic update patches the subscription cache on the assumption a write
1165
+ * will land; an action is not a write — it runs in the Worker, may call a third
1166
+ * party, and has no declared effect on any query.
1167
+ *
1168
+ * ```ts
1169
+ * private readonly client = injectLunoraClient();
1170
+ * verify = () => runAction(api.commands.run, { command: "lunora", args: ["verify"] }, { client: this.client });
1171
+ * ```
1172
+ *
1173
+ * When called from within an injection context you may omit `client` and let it
1174
+ * resolve from the injector.
1175
+ * @experimental
1176
+ */
1177
+ declare const runAction: <F extends FunctionReference>(reference: F, args: ArgsOf<F>, options?: RunActionOptions) => Promise<ReturnOf<F>>;
1178
+ /**
1179
+ * The lifecycle of a stream the primitive is observing.
1180
+ * @experimental
1181
+ */
1046
1182
  type StreamStatus = "complete" | "error" | "idle" | "streaming";
1047
1183
  /**
1048
- * `StreamOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
1049
- * @experimental
1050
- */
1184
+ * `StreamOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
1185
+ * @experimental
1186
+ */
1051
1187
  interface StreamOptions {
1052
1188
  /** Client to bind to. Defaults to the injected `LUNORA_CLIENT`. */
1053
1189
  client?: LunoraClient;
1054
1190
  /**
1055
- * `DestroyRef` whose `onDestroy` cancels the stream. Defaults to
1056
- * `inject(DestroyRef)` — the calling component/service.
1057
- */
1191
+ * `DestroyRef` whose `onDestroy` cancels the stream. Defaults to
1192
+ * `inject(DestroyRef)` — the calling component/service.
1193
+ */
1058
1194
  destroyRef?: DestroyRef;
1195
+ /**
1196
+ * Opt into resume-on-reconnect for a stream the server declared `durable`.
1197
+ * The chunks already received are kept and the socket re-attaches to the same
1198
+ * run, so a dropped connection mid-generation continues instead of surfacing
1199
+ * `STREAM_DISCONNECTED`. Has no effect on an ephemeral stream.
1200
+ */
1201
+ durable?: boolean;
1059
1202
  /** Forwarded to `client.stream()` — caps the in-flight chunk buffer. */
1060
1203
  maxBuffer?: number;
1061
1204
  /** Route to a specific shard when the target function is `.shardBy(...)`-partitioned. */
1062
1205
  shardKey?: string;
1063
1206
  }
1064
1207
  /**
1065
- * `StreamResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
1066
- * @experimental
1067
- */
1208
+ * `StreamResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
1209
+ * @experimental
1210
+ */
1068
1211
  interface StreamResult<T> {
1069
1212
  /** Force-cancel the stream and resolve the iterator. Safe to call multiple times. */
1070
1213
  cancel: () => void;
@@ -1076,47 +1219,55 @@ interface StreamResult<T> {
1076
1219
  status: Signal<StreamStatus>;
1077
1220
  }
1078
1221
  /**
1079
- * Subscribe to a streaming query. Returns the chunks pushed so far plus a
1080
- * lifecycle status and a `cancel` function, all as signals.
1081
- *
1082
- * Unlike `subscription`, which tracks the latest value, `stream` accumulates every
1083
- * chunk the server pushes — use it for token-by-token deltas and other append-only
1084
- * feeds. Pass `"skip"` as `args` to keep the primitive mounted without opening a
1085
- * stream (mirrors `subscription`); the stream tears down when the owning
1086
- * `DestroyRef` fires. The Angular counterpart to React's `useStream`, re-expressed
1087
- * with signals.
1088
- *
1089
- * Call from an injection context (component/service field or constructor):
1090
- * ```ts
1091
- * readonly tokens = stream(api.chat.liveEvents, { key: "thread-1" });
1092
- * ```
1093
- * @experimental
1094
- */
1222
+ * Subscribe to a streaming query. Returns the chunks pushed so far plus a
1223
+ * lifecycle status and a `cancel` function, all as signals.
1224
+ *
1225
+ * Unlike `subscription`, which tracks the latest value, `stream` accumulates every
1226
+ * chunk the server pushes — use it for token-by-token deltas and other append-only
1227
+ * feeds. Pass `"skip"` as `args` to keep the primitive mounted without opening a
1228
+ * stream (mirrors `subscription`); the stream tears down when the owning
1229
+ * `DestroyRef` fires. Nothing opens on the Angular server platform (SSR). The
1230
+ * Angular counterpart to React's `useStream`, re-expressed with signals.
1231
+ *
1232
+ * Call from an injection context (component/service field or constructor):
1233
+ * ```ts
1234
+ * readonly tokens = stream(api.chat.liveEvents, { key: "thread-1" });
1235
+ * ```
1236
+ * @experimental
1237
+ */
1095
1238
  declare const stream: <F extends FunctionReference<"stream">>(reference: F, args: ArgsOf<F> | "skip", options?: StreamOptions) => StreamResult<ReturnOf<F>>;
1096
1239
  /**
1097
- * `SubscriptionOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
1098
- * @experimental
1099
- */
1240
+ * `SubscriptionOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
1241
+ * @experimental
1242
+ */
1100
1243
  interface SubscriptionOptions {
1101
1244
  /** Client to bind to. Defaults to the injected `LUNORA_CLIENT`. */
1102
1245
  client?: LunoraClient;
1103
1246
  /**
1104
- * `DestroyRef` whose `onDestroy` tears the subscription down. Defaults to
1105
- * `inject(DestroyRef)` — the calling component/service.
1106
- */
1247
+ * `DestroyRef` whose `onDestroy` tears the subscription down. Defaults to
1248
+ * `inject(DestroyRef)` — the calling component/service.
1249
+ */
1107
1250
  destroyRef?: DestroyRef;
1108
1251
  /**
1109
- * Called when the subscription errors after the initial attach. Without it,
1110
- * a post-attach failure is dropped silently.
1111
- */
1252
+ * `Injector` to create the reactive-args `effect()` from. Only needed when
1253
+ * `args` is a function/`Signal` AND `subscription` is called outside an
1254
+ * injection context (an explicit `destroyRef` is also being passed).
1255
+ * Defaults to the ambient injection context. Unused for the static `args`
1256
+ * form, which never creates an `effect()`.
1257
+ */
1258
+ injector?: Injector;
1259
+ /**
1260
+ * Called when the subscription errors after the initial attach. Without it,
1261
+ * a post-attach failure is dropped silently.
1262
+ */
1112
1263
  onError?: (error: SubscriptionError) => void;
1113
1264
  /** Route to a specific shard when the target function is `.shardBy(...)`-partitioned. */
1114
1265
  shardKey?: string;
1115
1266
  }
1116
1267
  /**
1117
- * `SubscriptionResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
1118
- * @experimental
1119
- */
1268
+ * `SubscriptionResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
1269
+ * @experimental
1270
+ */
1120
1271
  interface SubscriptionResult<T> {
1121
1272
  /** The latest value pushed by the server. `undefined` before the first frame. */
1122
1273
  data: Signal<T | undefined>;
@@ -1124,38 +1275,42 @@ interface SubscriptionResult<T> {
1124
1275
  error: Signal<SubscriptionError | undefined>;
1125
1276
  }
1126
1277
  /**
1127
- * Subscribe to a reactive server push stream. Returns `{ data, error }` signals
1128
- * that update whenever the server emits a new value.
1129
- *
1130
- * Unlike `liveQuery`, which tracks a single value, `subscription` also
1131
- * exposes an `error` signal for the async error channel. Use it for ephemeral,
1132
- * high-frequency streams where you need error visibility.
1133
- *
1134
- * Pass `"skip"` as `args` to short-circuit — no network call, no socket.
1135
- * The subscription tears down when the owning `DestroyRef` fires.
1136
- *
1137
- * Call from an injection context (component/service field or constructor):
1138
- * ```ts
1139
- * readonly stream = subscription(api.events.stream, { roomId: "general" });
1140
- * ```
1141
- * @experimental
1142
- */
1143
- declare const subscription: <F extends FunctionReference>(reference: F, args: ArgsOf<F> | "skip", options?: SubscriptionOptions) => SubscriptionResult<ReturnOf<F>>;
1144
- /**
1145
- * Browser Web Audio subsystems for `voiceAgent` — the default microphone capture
1146
- * and speaker playback implementations injected into the primitive via its
1147
- * `createMicrophone` / `createSpeaker` seams. Kept in a sibling module so the
1148
- * heavy Web Audio graph (and its structural DOM typings) stays isolated from the
1149
- * primitive's transport + signal-state logic and remains mockable in a
1150
- * non-browser test env.
1151
- */
1152
- /**
1153
- * The negotiated audio format the voice DO streams back. Mirrors
1154
- * `@lunora/agent`'s `VoiceServerFrame` `ready.audioFormat` — re-declared (not
1155
- * imported) so this Angular package never pulls in the server-only `@lunora/agent`
1156
- * module graph.
1157
- * @experimental
1158
- */
1278
+ * Subscribe to a reactive server push stream. Returns `{ data, error }` signals
1279
+ * that update whenever the server emits a new value.
1280
+ *
1281
+ * Unlike `liveQuery`, which tracks a single value, `subscription` also
1282
+ * exposes an `error` signal for the async error channel. Use it for ephemeral,
1283
+ * high-frequency streams where you need error visibility.
1284
+ *
1285
+ * Pass `"skip"` as `args` to short-circuit — no network call, no socket.
1286
+ * The subscription tears down when the owning `DestroyRef` fires.
1287
+ *
1288
+ * Call from an injection context (component/service field or constructor):
1289
+ * ```ts
1290
+ * readonly stream = subscription(api.events.stream, { roomId: "general" });
1291
+ * ```
1292
+ *
1293
+ * `args` also accepts a function/`Signal` to make the subscription reactive —
1294
+ * an args change tears the old subscription down and opens a fresh one for the
1295
+ * new args. A static (plain object) `args` resolves once and never re-runs.
1296
+ * @experimental
1297
+ */
1298
+ declare const subscription: <F extends FunctionReference>(reference: F, args: ArgsOf<F> | "skip" | (() => ArgsOf<F> | "skip"), options?: SubscriptionOptions) => SubscriptionResult<ReturnOf<F>>;
1299
+ /**
1300
+ * Browser Web Audio subsystems for `voiceAgent` — the default microphone capture
1301
+ * and speaker playback implementations injected into the primitive via its
1302
+ * `createMicrophone` / `createSpeaker` seams. Kept in a sibling module so the
1303
+ * heavy Web Audio graph (and its structural DOM typings) stays isolated from the
1304
+ * primitive's transport + signal-state logic and remains mockable in a
1305
+ * non-browser test env.
1306
+ */
1307
+ /**
1308
+ * The negotiated audio format the voice DO streams back. Mirrors
1309
+ * `@lunora/agent`'s `VoiceServerFrame` `ready.audioFormat` — re-declared (not
1310
+ * imported) so this Angular package never pulls in the server-only `@lunora/agent`
1311
+ * module graph.
1312
+ * @experimental
1313
+ */
1159
1314
  type VoiceAudioFormat = "mp3" | "wav";
1160
1315
  /** Captures microphone audio and reports level / turn boundaries back to the primitive. */
1161
1316
  interface VoiceMicrophone {
@@ -1179,8 +1334,24 @@ interface MicrophoneConfig {
1179
1334
  interruptChunks: number;
1180
1335
  /** RMS above which the user is considered to be barging in while the agent speaks. */
1181
1336
  interruptThreshold: number;
1182
- /** `true` while `status === "speaking"` — gates barge-in detection. */
1183
- isSpeaking: () => boolean;
1337
+ /**
1338
+ * `true` from the moment a turn is committed until it completes — the whole
1339
+ * `thinking` + `speaking` window, not just the audible half.
1340
+ *
1341
+ * It gates BOTH branches below, and the wider span is the point. Gated only
1342
+ * on "audibly speaking", turn detection kept running through the entire
1343
+ * STT+LLM window after a `commit`: room noise at the (deliberately low)
1344
+ * `silenceThreshold` re-armed `sawSpeech`, another quiet gap fired a SECOND
1345
+ * `commit`, and the DO refused it with "a turn is already in progress" —
1346
+ * a refusal that returns before draining the audio buffer, so the PCM
1347
+ * captured since the first commit leaked into the next utterance.
1348
+ *
1349
+ * A genuine barge-in still works in that window: it routes through the
1350
+ * `onInterrupt` branch, which needs `interruptChunks` consecutive chunks at
1351
+ * `interruptThreshold` — an order of magnitude above `silenceThreshold` —
1352
+ * and `interrupt` is exactly what the DO tells the client to send.
1353
+ */
1354
+ isTurnActive: () => boolean;
1184
1355
  /** One 16 kHz mono 16-bit little-endian PCM frame captured from the mic. */
1185
1356
  onAudio: (pcm: Uint8Array) => void;
1186
1357
  /** A barge-in was detected (RMS spike while the agent is speaking). */
@@ -1199,23 +1370,18 @@ type CreateSpeaker = (config: {
1199
1370
  audioFormat: VoiceAudioFormat;
1200
1371
  }) => VoiceSpeaker;
1201
1372
  /**
1202
- * The default browser microphone: `getUserMedia` → a Web Audio `ScriptProcessor`
1203
- * that tees 16 kHz PCM frames, tracks input RMS, auto-commits an utterance after
1204
- * a silence gap, and flags a barge-in while the agent is speaking.
1205
- */
1206
- /**
1207
- * The `agents.&lt;name>Voice` reference codegen emits for a voice-enabled agent — a
1208
- * live, WS-backed session keyed by `threadKey`. A structural subset of the
1209
- * generated member, so passing `api.agents.&lt;name>Voice` type-checks.
1210
- * @experimental
1211
- */
1373
+ * The `agents.<name>Voice` reference codegen emits for a voice-enabled agent — a
1374
+ * live, WS-backed session keyed by `threadKey`. A structural subset of the
1375
+ * generated member, so passing `api.agents.<name>Voice` type-checks.
1376
+ * @experimental
1377
+ */
1212
1378
  type VoiceReference = FunctionReference<"stream", {
1213
1379
  threadKey: string;
1214
1380
  }, Record<string, unknown>>;
1215
1381
  /**
1216
- * The lifecycle of a voice call, mirrored to the UI.
1217
- * @experimental
1218
- */
1382
+ * The lifecycle of a voice call, mirrored to the UI.
1383
+ * @experimental
1384
+ */
1219
1385
  type VoiceStatus = "idle" | "listening" | "speaking" | "thinking";
1220
1386
  /** A minimal structural subset of the DOM `WebSocket` the primitive drives. */
1221
1387
  interface VoiceSocket {
@@ -1232,26 +1398,30 @@ interface VoiceSocket {
1232
1398
  }
1233
1399
  type CreateSocket = (url: string) => VoiceSocket;
1234
1400
  /**
1235
- * `VoiceAgentOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
1236
- * @experimental
1237
- */
1401
+ * `VoiceAgentOptions` is part of the experimental `@lunora/angular` API and may change without a major version bump.
1402
+ * @experimental
1403
+ */
1238
1404
  interface VoiceAgentOptions {
1239
1405
  /** Client to bind to. Defaults to the injected `LUNORA_CLIENT`. */
1240
1406
  client?: LunoraClient;
1241
1407
  /**
1242
- * Advanced/test seam: build the microphone capture subsystem. Defaults to a
1243
- * `getUserMedia` + Web Audio implementation. Injected wholesale so the Web
1244
- * Audio graph stays isolated (and mockable in a non-browser test env).
1245
- */
1408
+ * Advanced/test seam: build the microphone capture subsystem. Defaults to a
1409
+ * `getUserMedia` + Web Audio implementation. Injected wholesale so the Web
1410
+ * Audio graph stays isolated (and mockable in a non-browser test env).
1411
+ */
1246
1412
  createMicrophone?: CreateMicrophone;
1247
- /** Advanced/test seam: open the transport. Defaults to `new WebSocket(url)`. */
1413
+ /**
1414
+ * Advanced/test seam: open the transport. Defaults to the WebSocket
1415
+ * implementation the client was built with (`client.getWebSocketImpl()`),
1416
+ * NOT a raw `globalThis.WebSocket`.
1417
+ */
1248
1418
  createSocket?: CreateSocket;
1249
1419
  /** Advanced/test seam: build the audio playback subsystem. Defaults to a Web Audio implementation. */
1250
1420
  createSpeaker?: CreateSpeaker;
1251
1421
  /**
1252
- * `DestroyRef` whose `onDestroy` tears the call down. Defaults to
1253
- * `inject(DestroyRef)` — the calling component/service.
1254
- */
1422
+ * `DestroyRef` whose `onDestroy` tears the call down. Defaults to
1423
+ * `inject(DestroyRef)` — the calling component/service.
1424
+ */
1255
1425
  destroyRef?: DestroyRef;
1256
1426
  /** Consecutive above-`interruptThreshold` chunks that trigger a barge-in. Default `3`. */
1257
1427
  interruptChunks?: number;
@@ -1261,15 +1431,19 @@ interface VoiceAgentOptions {
1261
1431
  silenceDurationMs?: number;
1262
1432
  /** Input RMS below which audio counts as silence. Default `0.01`. */
1263
1433
  silenceThreshold?: number;
1264
- /** The thread to converse on — shared with the agent's text turns. Resolved when the call opens. */
1265
- threadKey: string;
1266
- /** The generated `api.agents.&lt;name>Voice` reference — identifies the voice DO endpoint. */
1434
+ /**
1435
+ * The thread to converse on — shared with the agent's text turns. A plain
1436
+ * value, or a `Signal`/getter resolved afresh every time a call opens — the
1437
+ * reactive-args form the package's other primitives take.
1438
+ */
1439
+ threadKey: (() => string) | string;
1440
+ /** The generated `api.agents.<name>Voice` reference — identifies the voice DO endpoint. */
1267
1441
  voice: VoiceReference;
1268
1442
  }
1269
1443
  /**
1270
- * `VoiceAgentResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
1271
- * @experimental
1272
- */
1444
+ * `VoiceAgentResult` is part of the experimental `@lunora/angular` API and may change without a major version bump.
1445
+ * @experimental
1446
+ */
1273
1447
  interface VoiceAgentResult {
1274
1448
  /** The current input RMS (0–1) — drive a mic level meter. */
1275
1449
  audioLevel: Signal<number>;
@@ -1295,24 +1469,40 @@ interface VoiceAgentResult {
1295
1469
  transcript: Signal<string>;
1296
1470
  }
1297
1471
  /**
1298
- * A first-class voice-call surface for a voice-enabled agent: it opens a
1299
- * WebSocket to the agent's `VoiceSessionDO`, captures mic audio as 16 kHz PCM,
1300
- * streams the agent's synthesized speech back through the browser's audio output,
1301
- * and mirrors the call lifecycle (`status`, `transcript`, `interimTranscript`,
1302
- * `audioLevel`) to Angular signals. Pass the generated `api.agents.&lt;name>Voice`
1303
- * reference (never a string), matching `agentChat`'s reference-passing style. The
1304
- * Angular counterpart to React's `useVoiceAgent`, re-expressed with signals; the
1305
- * per-call connection lives in a closure variable (the primitive runs once per
1306
- * component, so no signal-of-connection indirection is needed).
1307
- *
1308
- * v1 transport is plain binary WebSocket frames with push-to-talk / silence-timer
1309
- * turn detection and client-side RMS barge-in. The heavy Web Audio capture and
1310
- * playback subsystems are injectable (`createMicrophone` / `createSpeaker` /
1311
- * `createSocket`) so the primitive is drivable outside a browser.
1312
- *
1313
- * Call from an injection context (component/service field or constructor); pass an
1314
- * explicit `client` / `destroyRef` to drive it outside one (e.g. in a test).
1315
- * @experimental
1316
- */
1472
+ * A first-class voice-call surface for a voice-enabled agent: it opens a
1473
+ * WebSocket to the agent's `VoiceSessionDO`, captures mic audio as 16 kHz PCM,
1474
+ * streams the agent's synthesized speech back through the browser's audio output,
1475
+ * and mirrors the call lifecycle (`status`, `transcript`, `interimTranscript`,
1476
+ * `audioLevel`) to Angular signals. Pass the generated `api.agents.<name>Voice`
1477
+ * reference (never a string), matching `agentChat`'s reference-passing style. The
1478
+ * Angular counterpart to React's `useVoiceAgent`, re-expressed with signals; the
1479
+ * per-call connection lives in a closure variable (the primitive runs once per
1480
+ * component, so no signal-of-connection indirection is needed).
1481
+ *
1482
+ * v1 transport is plain binary WebSocket frames with push-to-talk / silence-timer
1483
+ * turn detection and client-side RMS barge-in. The heavy Web Audio capture and
1484
+ * playback subsystems are injectable (`createMicrophone` / `createSpeaker` /
1485
+ * `createSocket`) so the primitive is drivable outside a browser.
1486
+ *
1487
+ * Call from an injection context (component/service field or constructor); pass an
1488
+ * explicit `client` / `destroyRef` to drive it outside one (e.g. in a test).
1489
+ * @experimental
1490
+ */
1317
1491
  declare const voiceAgent: (options: VoiceAgentOptions) => VoiceAgentResult;
1318
- export { type AgentApi, type AgentChatApi, type AgentChatMessage, type AgentChatOptions, type AgentChatResult, type AgentLiveEvent, type AgentOptions, type AgentProgressEvent, type AgentResult, type AgentStateApi, type AgentStateOptions, type AgentStateResult, type AgentThreadRecord, type AgentThreadStatus, type AgentTokenDelta, type AgentTokenStreamReference, type AgentToolEvent, type AgentToolEventsApi, type AgentToolEventsOptions, type AgentToolEventsResult, type AuthOptions, type AuthResult, type ConnectionStatusOptions, type FlagContext, type FlagOptions, type FlagValue, type FlagsOptions, type HeartbeatReference, type HydratePreloadedOptions, type HydratePreloadedResult, type InfiniteQueryResult, LUNORA_CLIENT, type ListPresentReference, type LiveQueryOptions, type MutateOptions, type MutatorResult, type PaginatedQueryOptions, type PaginatedQueryResult, type PresenceOptions, type PresenceResult, type ProvideLunoraOptions, type RateLimitOptions, type RateLimitResult, type StreamOptions, type StreamResult, type StreamStatus, type SubscriptionOptions, type SubscriptionResult, type VoiceAgentOptions, type VoiceAgentResult, type VoiceAudioFormat, type VoiceReference, type VoiceStatus, agent, agentChat, agentState, agentToolEvents, auth, connectionStatus, flag, flags, hydratePreloaded, infiniteQuery, injectLunoraClient, liveQuery, mutate, mutator, paginatedQuery, presence, provideLunora, rateLimit, stream, subscription, voiceAgent };
1492
+ export { type AgentApi, type AgentChatApi, type AgentChatMessage, type AgentChatOptions, type AgentChatResult, type AgentLiveEvent, type AgentOptions, type AgentProgressEvent, type AgentResult, type AgentStateApi, type AgentStateOptions, type AgentStateResult, type AgentThreadRecord, type AgentThreadStatus, type AgentTokenDelta, type AgentTokenStreamReference, type AgentToolEvent, type AgentToolEventsApi, type AgentToolEventsOptions, type AgentToolEventsResult, type AuthGateResult, type AuthOptions, type AuthResult, type ConnectionStatusOptions, type FlagOptions, type FlagValue, type FlagsOptions, type HeartbeatReference, type HydratePreloadedOptions, type HydratePreloadedResult, type InfiniteQueryResult, LUNORA_CLIENT, type ListPresentReference, type LiveQueryOptions, type MutateOptions, type MutatorResult, type PaginatedQueryOptions, type PaginatedQueryResult, type PresenceOptions, type PresenceResult,
1493
+ /**
1494
+ * The Angular adapter for Lunora.
1495
+ *
1496
+ * Thin, idiomatic glue over the framework-neutral `@lunora/client`. Angular
1497
+ * signals map directly onto Lunora's per-subscription deltas, so a live query is
1498
+ * just a `signal` the WebSocket writes to.
1499
+ *
1500
+ * `provideLunora` / `LUNORA_CLIENT` / `injectLunoraClient` are the injectable
1501
+ * provider carrying one `LunoraClient` (opens its socket lazily), wired once in the
1502
+ * application config. `liveQuery` is a live-query `signal` that opens a
1503
+ * subscription and updates on every delta, torn down automatically on
1504
+ * `DestroyRef.onDestroy`. `mutate` runs a mutation (optimistic updates + offline
1505
+ * queue pass through to the client). `connectionStatus` is a `signal` of the
1506
+ * aggregate live-socket status.
1507
+ */
1508
+ type ProvideLunoraOptions, type RateLimitOptions, type RateLimitResult, type RunActionOptions, type StreamOptions, type StreamResult, type StreamStatus, type SubscriptionOptions, type SubscriptionResult, type VoiceAgentOptions, type VoiceAgentResult, type VoiceAudioFormat, type VoiceReference, type VoiceStatus, agent, agentChat, agentState, agentToolEvents, auth, authGate, connectionStatus, flag, flags, hydratePreloaded, infiniteQuery, injectLunoraClient, liveQuery, mutate, mutator, paginatedQuery, presence, provideLunora, rateLimit, runAction, stream, subscription, voiceAgent };