@effect-uai/ui 0.0.0-stage → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +64 -2
  3. package/dist/Atoms.d.mts +141 -0
  4. package/dist/Atoms.d.mts.map +1 -0
  5. package/dist/Atoms.mjs +132 -0
  6. package/dist/Atoms.mjs.map +1 -0
  7. package/dist/Client-C9UYN_VK.d.mts +219 -0
  8. package/dist/Client-C9UYN_VK.d.mts.map +1 -0
  9. package/dist/Client.d.mts +2 -0
  10. package/dist/Client.mjs +55 -0
  11. package/dist/Client.mjs.map +1 -0
  12. package/dist/Input-Cz-Kxusm.d.mts +277 -0
  13. package/dist/Input-Cz-Kxusm.d.mts.map +1 -0
  14. package/dist/Input.d.mts +2 -0
  15. package/dist/Input.mjs +127 -0
  16. package/dist/Input.mjs.map +1 -0
  17. package/dist/Projection.d.mts +44 -0
  18. package/dist/Projection.d.mts.map +1 -0
  19. package/dist/Projection.mjs +133 -0
  20. package/dist/Projection.mjs.map +1 -0
  21. package/dist/Run.d.mts +44 -0
  22. package/dist/Run.d.mts.map +1 -0
  23. package/dist/Run.mjs +117 -0
  24. package/dist/Run.mjs.map +1 -0
  25. package/dist/RunEvent.d.mts +1225 -0
  26. package/dist/RunEvent.d.mts.map +1 -0
  27. package/dist/RunEvent.mjs +264 -0
  28. package/dist/RunEvent.mjs.map +1 -0
  29. package/dist/RunView-nHfJwN8t.d.mts +380 -0
  30. package/dist/RunView-nHfJwN8t.d.mts.map +1 -0
  31. package/dist/RunView.d.mts +2 -0
  32. package/dist/RunView.mjs +452 -0
  33. package/dist/RunView.mjs.map +1 -0
  34. package/dist/SseEncoding.d.mts +437 -0
  35. package/dist/SseEncoding.d.mts.map +1 -0
  36. package/dist/SseEncoding.mjs +64 -0
  37. package/dist/SseEncoding.mjs.map +1 -0
  38. package/dist/Thread-CAXsjhpX.d.mts +474 -0
  39. package/dist/Thread-CAXsjhpX.d.mts.map +1 -0
  40. package/dist/Thread.d.mts +2 -0
  41. package/dist/Thread.mjs +225 -0
  42. package/dist/Thread.mjs.map +1 -0
  43. package/dist/Transport.d.mts +96 -0
  44. package/dist/Transport.d.mts.map +1 -0
  45. package/dist/Transport.mjs +82 -0
  46. package/dist/Transport.mjs.map +1 -0
  47. package/dist/UiContract.d.mts +114 -0
  48. package/dist/UiContract.d.mts.map +1 -0
  49. package/dist/UiContract.mjs +229 -0
  50. package/dist/UiContract.mjs.map +1 -0
  51. package/dist/index.d.mts +12 -0
  52. package/dist/index.mjs +12 -0
  53. package/dist/rolldown-runtime-D7D4PA-g.mjs +13 -0
  54. package/package.json +99 -3
  55. package/src/Atoms.test.ts +163 -0
  56. package/src/Atoms.ts +355 -0
  57. package/src/Client.test.ts +53 -0
  58. package/src/Client.ts +92 -0
  59. package/src/Input.test.ts +68 -0
  60. package/src/Input.ts +165 -0
  61. package/src/Projection.test.ts +198 -0
  62. package/src/Projection.ts +142 -0
  63. package/src/Run.test.ts +119 -0
  64. package/src/Run.ts +166 -0
  65. package/src/RunEvent.test.ts +84 -0
  66. package/src/RunEvent.ts +374 -0
  67. package/src/RunView.test.ts +98 -0
  68. package/src/RunView.ts +583 -0
  69. package/src/SseEncoding.test.ts +46 -0
  70. package/src/SseEncoding.ts +77 -0
  71. package/src/Thread.test.ts +304 -0
  72. package/src/Thread.ts +286 -0
  73. package/src/Transport.ts +182 -0
  74. package/src/UiContract.test.ts +342 -0
  75. package/src/UiContract.ts +402 -0
  76. package/src/index.ts +11 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Betalyra Sociedade Unipessoal Lda.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,65 @@
1
- # Temporary Holding Version
1
+ # @effect-uai/ui
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Agentic UI primitives for effect-uai: state and streams, not components.
4
+
5
+ Your agent runs on the server; your user watches it in a browser. This
6
+ package is the part in between: the server projects its loop onto a small,
7
+ resumable wire protocol, and the client folds that protocol into plain data
8
+ any framework can render. Every piece is a small function you can read,
9
+ replace, or skip.
10
+
11
+ ## Modules
12
+
13
+ Server:
14
+
15
+ - `Projection`: turns an effect-uai loop into `RunEvent`s
16
+ (`toRunEvents({ contract })`; the contract's tool schemas encode tool values).
17
+ - `Run`: one operator per job. `start` (`run-started` first), `complete`
18
+ (one terminal last), `stopWhen` (stop on a signal), `failed` (an error as
19
+ `run-failed`), `sequence` (per-stream `seq`), `coalesce` (merge deltas).
20
+ - `SseEncoding`: envelopes as Server-Sent Events on Effect's `Sse`
21
+ (`encode`, `decode`, `responseHeaders`, `lastEventId` for reconnects).
22
+
23
+ Wire:
24
+
25
+ - `RunEvent`: the event schemas, the `Envelope { streamId, seq, runId?, event }`
26
+ every frame is wrapped in, and branded `StreamId` / `RunId` / `InputId`.
27
+ - `Input`: what a client sends. A preset (`Input.submit(text)`,
28
+ `Input.resolve(decisions)`), with typed app `data` via `Input.make(DataSchema)`;
29
+ any schema with an `id` works instead.
30
+ - `UiContract`: optional types shared by server and client, from Schemas
31
+ only. The wire stays JSON; the client decodes each envelope once, so the
32
+ state holds typed values (a `Date` is a `Date`).
33
+
34
+ ```ts
35
+ // shared: Schemas only, safe in the browser
36
+ const ChatUi = UiContract.make({ tools: [GetWeather] })
37
+
38
+ // client: the state is typed by the contract
39
+ const chat = Atoms.thread({ contract: ChatUi, runtime, transport, busy: "block" })
40
+ for (const b of thread.active?.blocks ?? [])
41
+ if (b.type === "tool" && b.name === "get_weather") b.output?.observedAt // Date
42
+ ```
43
+
44
+ Client:
45
+
46
+ - `RunView`, `Thread`: folds (`initial` + `step`) from envelopes to plain
47
+ data: one run, or a chat (messages, the active run, pending inputs).
48
+ - `Client`: `sse`, `ndjson`, and `resumable` (reconnect from the last `seq`).
49
+ - `Transport`: per run (`direct`, `durable`, `local`) or per session
50
+ (`session`: one stream per chat, the server queues messages).
51
+ - `Atoms`: state on `effect/reactivity`. `make` / `thread` for per-run
52
+ transports, `session` / `sessionThread` for sessions. React reads them with
53
+ `@effect/atom-react`, plain JS with `AtomRegistry.subscribe`.
54
+
55
+ The `direct-chat` and `durable-chat` recipes are a server and a page each;
56
+ `examples/tanstack-start-chat` and `examples/foldkit-chat` are framework apps
57
+ with typed tools.
58
+
59
+ ## Install
60
+
61
+ ```sh
62
+ pnpm add @effect-uai/ui
63
+ ```
64
+
65
+ ESM-only. Requires `effect@>=4.0.1` and `@effect-uai/core` as peers.
@@ -0,0 +1,141 @@
1
+ import { Envelope, Payloads, StreamId, Untyped } from "./RunEvent.mjs";
2
+ import { a as Input, r as Draft, u as Sent } from "./Input-Cz-Kxusm.mjs";
3
+ import { c as Thread } from "./Thread-CAXsjhpX.mjs";
4
+ import { Durable, Session, Transport } from "./Transport.mjs";
5
+ import { UiContract } from "./UiContract.mjs";
6
+ import { Effect, Schema } from "effect";
7
+ import { Atom } from "effect/reactivity";
8
+ import { SecureRandom } from "effect/crypto";
9
+ declare namespace Atoms_d_exports {
10
+ export { Busy, Decode, Options, Runtime, SessionOptions, make, session, sessionThread, thread };
11
+ }
12
+ /**
13
+ * What `submit` does while a run is in flight. `"block"` ignores the new
14
+ * input; `"stop-then-submit"` abandons the current run locally and starts
15
+ * the new one (in durable mode the server-side run keeps going).
16
+ */
17
+ type Busy = "block" | "stop-then-submit";
18
+ /**
19
+ * The atoms' runtime: the transport's services plus `SecureRandom` for
20
+ * input ids, e.g. `Atom.runtime(Layer.mergeAll(FetchHttpClient.layer,
21
+ * SecureRandom.layerWebCrypto))`. A test provides a seeded one.
22
+ */
23
+ type Runtime<R, ER> = Atom.AtomRuntime<R | SecureRandom.SecureRandom, ER>;
24
+ /** Turns a wire envelope into what `step` folds (a contract's `decode`). */
25
+ type Decode<D> = (envelope: Envelope) => Effect.Effect<D, Schema.SchemaError>;
26
+ type WithDecode<D> = [Envelope] extends [D] ? {
27
+ readonly decode?: Decode<D>;
28
+ } : {
29
+ readonly decode: Decode<D>;
30
+ };
31
+ /**
32
+ * `I` is the app's input type. `submit` takes a draft of it (`id` optional);
33
+ * the transport receives it with the id assigned (`Input.Sent<I>`, a UUIDv7
34
+ * from `SecureRandom`). `D` is the envelope `step` folds: the wire envelope,
35
+ * or a contract's decoded one (then pass `decode`).
36
+ */
37
+ type BaseOptions<I, S, E, R, ER, D> = {
38
+ readonly runtime: Runtime<R, ER>;
39
+ readonly transport: Transport<Sent<I>, S, E, R>;
40
+ readonly initial: S;
41
+ readonly step: (state: S, envelope: D) => S;
42
+ readonly busy: Busy;
43
+ readonly isBusy?: (state: S) => boolean;
44
+ /** Optimistic update applied before the request goes out. */
45
+ readonly submitted?: (state: S, input: Sent<I>) => S;
46
+ /** Applied when a run's stream ends without its terminal. */
47
+ readonly ended?: (state: S, reason: "stopped" | "disconnected") => S;
48
+ };
49
+ type Options<I, S, E, R, ER, D = Envelope> = BaseOptions<I, S, E, R, ER, D> & WithDecode<D>;
50
+ declare const build: <I, S, E, R, ER, D>(options: BaseOptions<I, S, E, R, ER, D> & {
51
+ readonly decode: Decode<D>;
52
+ }) => {
53
+ state: Atom.Writable<S, S>;
54
+ streamId: Atom.Writable<(string & import("effect/Brand").Brand<"StreamId">) | null, (string & import("effect/Brand").Brand<"StreamId">) | null>;
55
+ submit: Atom.AtomResultFn<Draft<I>, void, ER>;
56
+ /** The current run; `AtomRegistry.getResult` on it waits for the run to finish. */
57
+ run: Atom.AtomResultFn<Sent<I>, void, E | ER | Schema.SchemaError>;
58
+ stop: Atom.AtomResultFn<void, void, E | ER>;
59
+ /** Re-attach to a run (`{ streamId, after }`); set for durable transports. */
60
+ resume: Atom.AtomResultFn<{
61
+ readonly streamId: StreamId;
62
+ readonly after: number;
63
+ }, void, E | ER | Schema.SchemaError> | undefined;
64
+ };
65
+ type Built<I, S, E, R, ER> = ReturnType<typeof build<I, S, E, R, ER, unknown>>;
66
+ /**
67
+ * State over a per-run transport: `state`, `submit`, `stop`, `run`, and
68
+ * `resume` when the transport can re-attach (`Transport.durable`,
69
+ * `durableHttp`), typed as present for those.
70
+ */
71
+ declare function make<I, S, E, R, ER, D = Envelope>(options: Options<I, S, E, R, ER, D> & {
72
+ readonly transport: Durable<Sent<I>, S, E, R>;
73
+ }): Omit<Built<I, S, E, R, ER>, "resume"> & {
74
+ readonly resume: NonNullable<Built<I, S, E, R, ER>["resume"]>;
75
+ };
76
+ declare function make<I, S, E, R, ER, D = Envelope>(options: Options<I, S, E, R, ER, D>): Built<I, S, E, R, ER>;
77
+ type ThreadOptions<I, E, R, ER, P extends Payloads> = {
78
+ readonly runtime: Runtime<R, ER>;
79
+ readonly transport: Transport<Sent<I>, Thread<P>, E, R>;
80
+ readonly busy: Busy;
81
+ readonly initial?: Thread<P>;
82
+ };
83
+ /**
84
+ * `make` with the `Thread` preset: a message list plus the active run. Takes
85
+ * the `Input` preset, with or without typed `data`. With `contract`, the
86
+ * state is typed by it. For other entry types, spread a `Thread.make(config)`
87
+ * fold into `make`.
88
+ */
89
+ declare function thread<I extends Input, E, R, ER, P extends Payloads>(options: ThreadOptions<I, E, R, ER, P> & {
90
+ readonly contract: UiContract<P>;
91
+ }): Built<I, Thread<P>, E, R, ER>;
92
+ declare function thread<I extends Input, E, R, ER>(options: ThreadOptions<I, E, R, ER, Untyped>): Built<I, Thread, E, R, ER>;
93
+ type BaseSessionOptions<I, S, E, R, ER, D> = {
94
+ readonly runtime: Runtime<R, ER>;
95
+ readonly transport: Session<Sent<I>, S, E, R>;
96
+ readonly initial: S;
97
+ readonly step: (state: S, envelope: D) => S;
98
+ /** While true, `submit` ignores new input (the server may also refuse). */
99
+ readonly isBusy?: (state: S) => boolean;
100
+ /** Optimistic update applied before the input is sent. */
101
+ readonly submitted?: (state: S, input: Sent<I>) => S;
102
+ /** Applied when sending fails or the session's stream gives up. */
103
+ readonly ended?: (state: S, reason: "stopped" | "disconnected") => S;
104
+ };
105
+ type SessionOptions<I, S, E, R, ER, D = Envelope> = BaseSessionOptions<I, S, E, R, ER, D> & WithDecode<D>;
106
+ declare const buildSession: <I, S, E, R, ER, D>(options: BaseSessionOptions<I, S, E, R, ER, D> & {
107
+ readonly decode: Decode<D>;
108
+ }) => {
109
+ state: Atom.Writable<S, S>;
110
+ streamId: Atom.Writable<(string & import("effect/Brand").Brand<"StreamId">) | null, (string & import("effect/Brand").Brand<"StreamId">) | null>;
111
+ open: Atom.AtomResultFn<string & import("effect/Brand").Brand<"StreamId">, unknown, unknown>;
112
+ submit: Atom.AtomResultFn<Draft<I>, unknown, unknown>;
113
+ stop: Atom.AtomResultFn<void, void, E | ER>;
114
+ };
115
+ type BuiltSession<I, S, E, R, ER> = ReturnType<typeof buildSession<I, S, E, R, ER, unknown>>;
116
+ /**
117
+ * State over a session transport. `open(streamId)` follows a session's
118
+ * stream from its start (e.g. a chat id kept across reloads). `submit` sends
119
+ * an input; without an open session the server starts one, which is then
120
+ * opened. Runs arrive on the followed stream, not as responses to `submit`.
121
+ */
122
+ declare function session<I, S, E, R, ER, D = Envelope>(options: SessionOptions<I, S, E, R, ER, D>): BuiltSession<I, S, E, R, ER>;
123
+ type SessionThreadOptions<I, E, R, ER, P extends Payloads> = {
124
+ readonly runtime: Runtime<R, ER>;
125
+ readonly transport: Session<Sent<I>, Thread<P>, E, R>;
126
+ readonly isBusy?: (thread: Thread<P>) => boolean;
127
+ };
128
+ /**
129
+ * `session` with the `Thread` preset. Input sent while a run is active, or
130
+ * echoed by the server (`input-accepted`), waits in `thread.pending`: the
131
+ * server queues it. Pass `isBusy` for a server that refuses instead. With
132
+ * `contract`, the state is typed by it. For other entry types, spread a
133
+ * `Thread.make(config)` fold into `session`.
134
+ */
135
+ declare function sessionThread<I extends Input, E, R, ER, P extends Payloads>(options: SessionThreadOptions<I, E, R, ER, P> & {
136
+ readonly contract: UiContract<P>;
137
+ }): BuiltSession<I, Thread<P>, E, R, ER>;
138
+ declare function sessionThread<I extends Input, E, R, ER>(options: SessionThreadOptions<I, E, R, ER, Untyped>): BuiltSession<I, Thread, E, R, ER>;
139
+ //#endregion
140
+ export { Busy, Decode, Options, Runtime, SessionOptions, make, session, sessionThread, Atoms_d_exports as t, thread };
141
+ //# sourceMappingURL=Atoms.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Atoms.d.mts","names":[],"sources":["../src/Atoms.ts"],"mappings":"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"}
package/dist/Atoms.mjs ADDED
@@ -0,0 +1,132 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { withId } from "./Input.mjs";
3
+ import { presetFor } from "./Thread.mjs";
4
+ import { Effect, Exit, Option, Stream } from "effect";
5
+ import { Atom } from "effect/reactivity";
6
+ //#region src/Atoms.ts
7
+ /**
8
+ * Framework-neutral state over a transport, built on `effect/reactivity`.
9
+ * Per-run transports: `make` takes any fold (`initial` + `step`), `thread`
10
+ * is the `Thread` preset. Session transports: `session` and
11
+ * `sessionThread`, likewise. React reads them with `@effect/atom-react` (`useAtomValue`,
12
+ * `useAtomSet`), Vue and Solid with their official bindings, plain JS with
13
+ * `AtomRegistry.subscribe`.
14
+ *
15
+ * With a `UiContract`, every envelope is decoded once on arrival, so the
16
+ * state holds decoded payloads and is typed by the contract.
17
+ */
18
+ var Atoms_exports = /* @__PURE__ */ __exportAll({
19
+ make: () => make,
20
+ session: () => session,
21
+ sessionThread: () => sessionThread,
22
+ thread: () => thread
23
+ });
24
+ const identity = Effect.succeed;
25
+ const build = (options) => {
26
+ const state = Atom.make(options.initial).pipe(Atom.keepAlive);
27
+ const streamId = Atom.make(null).pipe(Atom.keepAlive);
28
+ const follow = (envelopes) => envelopes.pipe(Stream.runForEach((envelope) => options.decode(envelope).pipe(Effect.flatMap((decoded) => Atom.set(streamId, envelope.streamId).pipe(Effect.andThen(Atom.update(state, (s) => options.step(s, decoded))))))), Effect.onExit((exit) => {
29
+ const ended = options.ended;
30
+ if (Exit.isSuccess(exit) || ended === void 0) return Effect.void;
31
+ const reason = Exit.hasInterrupts(exit) ? "stopped" : "disconnected";
32
+ return Atom.update(state, (s) => ended(s, reason));
33
+ }));
34
+ const run = options.runtime.fn()((input) => Effect.gen(function* () {
35
+ const submitted = options.submitted;
36
+ if (submitted !== void 0) yield* Atom.update(state, (s) => submitted(s, input));
37
+ yield* follow(options.transport.start(input, yield* Atom.get(state)));
38
+ })).pipe(Atom.keepAlive);
39
+ const submit = options.runtime.fn()((draft) => Effect.gen(function* () {
40
+ if (options.busy === "block" && options.isBusy?.(yield* Atom.get(state)) === true) return;
41
+ const input = yield* withId(draft);
42
+ yield* Atom.set(run, input);
43
+ }), { concurrent: true }).pipe(Atom.keepAlive);
44
+ const stop = options.runtime.fn()(() => Effect.gen(function* () {
45
+ const id = yield* Atom.get(streamId);
46
+ const serverStop = options.transport.stop;
47
+ if (serverStop !== void 0 && id !== null) return yield* serverStop(id);
48
+ yield* Atom.set(run, Atom.Interrupt);
49
+ })).pipe(Atom.keepAlive);
50
+ const resume = options.transport.resume;
51
+ return {
52
+ state,
53
+ streamId,
54
+ submit,
55
+ /** The current run; `AtomRegistry.getResult` on it waits for the run to finish. */
56
+ run,
57
+ stop,
58
+ /** Re-attach to a run (`{ streamId, after }`); set for durable transports. */
59
+ resume: resume === void 0 ? void 0 : options.runtime.fn()((arg) => follow(resume(arg.streamId, arg.after))).pipe(Atom.keepAlive)
60
+ };
61
+ };
62
+ function make(options) {
63
+ return build({
64
+ ...options,
65
+ decode: options.decode ?? identity
66
+ });
67
+ }
68
+ function thread(options) {
69
+ const fold = presetFor();
70
+ return build({
71
+ runtime: options.runtime,
72
+ transport: options.transport,
73
+ busy: options.busy,
74
+ initial: options.initial ?? fold.initial,
75
+ step: fold.step,
76
+ decode: options.contract?.decode ?? identity,
77
+ isBusy: (t) => t.active !== null,
78
+ submitted: (t, input) => fold.submitted(t, input),
79
+ ended: fold.ended
80
+ });
81
+ }
82
+ const buildSession = (options) => {
83
+ const state = Atom.make(options.initial).pipe(Atom.keepAlive);
84
+ const streamId = Atom.make(null).pipe(Atom.keepAlive);
85
+ const settle = (exit) => {
86
+ const ended = options.ended;
87
+ return ended === void 0 || Exit.isSuccess(exit) || Exit.hasInterrupts(exit) ? Effect.void : Atom.update(state, (s) => ended(s, "disconnected"));
88
+ };
89
+ const open = options.runtime.fn()((id) => Atom.set(streamId, id).pipe(Effect.andThen(options.transport.events(id, 0).pipe(Stream.runForEach((envelope) => options.decode(envelope).pipe(Effect.flatMap((decoded) => Atom.update(state, (s) => options.step(s, decoded))))))), Effect.onExit(settle))).pipe(Atom.keepAlive);
90
+ return {
91
+ state,
92
+ streamId,
93
+ open,
94
+ submit: options.runtime.fn()((draft) => Effect.gen(function* () {
95
+ if (options.isBusy?.(yield* Atom.get(state)) === true) return;
96
+ const input = yield* withId(draft);
97
+ const submitted = options.submitted;
98
+ if (submitted !== void 0) yield* Atom.update(state, (s) => submitted(s, input));
99
+ const opened = Option.fromNullishOr(yield* Atom.get(streamId));
100
+ const sent = yield* options.transport.send(opened, input, yield* Atom.get(state));
101
+ if (Option.isNone(opened)) yield* Atom.set(open, sent);
102
+ }).pipe(Effect.onExit(settle)), { concurrent: true }).pipe(Atom.keepAlive),
103
+ stop: options.runtime.fn()(() => Effect.gen(function* () {
104
+ const id = yield* Atom.get(streamId);
105
+ const serverStop = options.transport.stop;
106
+ if (serverStop !== void 0 && id !== null) yield* serverStop(id);
107
+ })).pipe(Atom.keepAlive)
108
+ };
109
+ };
110
+ function session(options) {
111
+ return buildSession({
112
+ ...options,
113
+ decode: options.decode ?? identity
114
+ });
115
+ }
116
+ function sessionThread(options) {
117
+ const fold = presetFor();
118
+ return buildSession({
119
+ runtime: options.runtime,
120
+ transport: options.transport,
121
+ initial: fold.initial,
122
+ step: fold.step,
123
+ decode: options.contract?.decode ?? identity,
124
+ ...options.isBusy !== void 0 ? { isBusy: options.isBusy } : {},
125
+ submitted: (t, input) => fold.submitted(t, input),
126
+ ended: fold.ended
127
+ });
128
+ }
129
+ //#endregion
130
+ export { make, session, sessionThread, Atoms_exports as t, thread };
131
+
132
+ //# sourceMappingURL=Atoms.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Atoms.mjs","names":["Input.withId","Thread.presetFor"],"sources":["../src/Atoms.ts"],"sourcesContent":["/**\n * Framework-neutral state over a transport, built on `effect/reactivity`.\n * Per-run transports: `make` takes any fold (`initial` + `step`), `thread`\n * is the `Thread` preset. Session transports: `session` and\n * `sessionThread`, likewise. React reads them with `@effect/atom-react` (`useAtomValue`,\n * `useAtomSet`), Vue and Solid with their official bindings, plain JS with\n * `AtomRegistry.subscribe`.\n *\n * With a `UiContract`, every envelope is decoded once on arrival, so the\n * state holds decoded payloads and is typed by the contract.\n */\nimport { Effect, Exit, Option, type Schema, Stream } from \"effect\"\nimport type { SecureRandom } from \"effect/crypto\"\nimport { Atom } from \"effect/reactivity\"\nimport * as Input from \"./Input.js\"\nimport type { Envelope, Payloads, StreamId, Untyped } from \"./RunEvent.js\"\nimport * as Thread from \"./Thread.js\"\nimport type * as Transport from \"./Transport.js\"\nimport type { UiContract } from \"./UiContract.js\"\n\n/**\n * What `submit` does while a run is in flight. `\"block\"` ignores the new\n * input; `\"stop-then-submit\"` abandons the current run locally and starts\n * the new one (in durable mode the server-side run keeps going).\n */\nexport type Busy = \"block\" | \"stop-then-submit\"\n\n/**\n * The atoms' runtime: the transport's services plus `SecureRandom` for\n * input ids, e.g. `Atom.runtime(Layer.mergeAll(FetchHttpClient.layer,\n * SecureRandom.layerWebCrypto))`. A test provides a seeded one.\n */\nexport type Runtime<R, ER> = Atom.AtomRuntime<R | SecureRandom.SecureRandom, ER>\n\n/** Turns a wire envelope into what `step` folds (a contract's `decode`). */\nexport type Decode<D> = (envelope: Envelope) => Effect.Effect<D, Schema.SchemaError>\n\n// `decode` may be left out only when `step` folds wire envelopes.\ntype WithDecode<D> = [Envelope] extends [D]\n ? { readonly decode?: Decode<D> }\n : { readonly decode: Decode<D> }\n\n/**\n * `I` is the app's input type. `submit` takes a draft of it (`id` optional);\n * the transport receives it with the id assigned (`Input.Sent<I>`, a UUIDv7\n * from `SecureRandom`). `D` is the envelope `step` folds: the wire envelope,\n * or a contract's decoded one (then pass `decode`).\n */\ntype BaseOptions<I, S, E, R, ER, D> = {\n readonly runtime: Runtime<R, ER>\n readonly transport: Transport.Transport<Input.Sent<I>, S, E, R>\n readonly initial: S\n readonly step: (state: S, envelope: D) => S\n readonly busy: Busy\n readonly isBusy?: (state: S) => boolean\n /** Optimistic update applied before the request goes out. */\n readonly submitted?: (state: S, input: Input.Sent<I>) => S\n /** Applied when a run's stream ends without its terminal. */\n readonly ended?: (state: S, reason: \"stopped\" | \"disconnected\") => S\n}\n\nexport type Options<I, S, E, R, ER, D = Envelope> = BaseOptions<I, S, E, R, ER, D> & WithDecode<D>\n\nconst identity: Decode<Envelope> = Effect.succeed\n\nconst build = <I, S, E, R, ER, D>(\n options: BaseOptions<I, S, E, R, ER, D> & { readonly decode: Decode<D> },\n) => {\n const state = Atom.make(options.initial).pipe(Atom.keepAlive)\n const streamId = Atom.make<StreamId | null>(null).pipe(Atom.keepAlive)\n\n const follow = (envelopes: Stream.Stream<Envelope, E, R>) =>\n envelopes.pipe(\n Stream.runForEach((envelope) =>\n options\n .decode(envelope)\n .pipe(\n Effect.flatMap((decoded) =>\n Atom.set(streamId, envelope.streamId).pipe(\n Effect.andThen(Atom.update(state, (s) => options.step(s, decoded))),\n ),\n ),\n ),\n ),\n Effect.onExit((exit) => {\n const ended = options.ended\n if (Exit.isSuccess(exit) || ended === undefined) return Effect.void\n const reason = Exit.hasInterrupts(exit) ? \"stopped\" : \"disconnected\"\n return Atom.update(state, (s) => ended(s, reason))\n }),\n )\n\n // Writing to a non-concurrent `Atom.fn` interrupts its previous run, so the\n // streaming lives in `run` and `submit` is a concurrent gate that applies\n // the busy policy before anything is interrupted. Every fn atom is kept\n // alive: an unobserved one is disposed, which interrupts its fiber.\n const run = options.runtime\n .fn<Input.Sent<I>>()((input) =>\n Effect.gen(function* () {\n const submitted = options.submitted\n if (submitted !== undefined) yield* Atom.update(state, (s) => submitted(s, input))\n yield* follow(options.transport.start(input, yield* Atom.get(state)))\n }),\n )\n .pipe(Atom.keepAlive)\n\n const submit = options.runtime\n .fn<Input.Draft<I>>()(\n (draft) =>\n Effect.gen(function* () {\n const blocked =\n options.busy === \"block\" && options.isBusy?.(yield* Atom.get(state)) === true\n if (blocked) return\n const input = yield* Input.withId(draft)\n yield* Atom.set(run, input)\n }),\n { concurrent: true },\n )\n .pipe(Atom.keepAlive)\n\n // Durable: ask the server (its `run-stopped` arrives on the stream).\n // Direct: interrupt the request; `ended` settles the run locally.\n const stop = options.runtime\n .fn<void>()(() =>\n Effect.gen(function* () {\n const id = yield* Atom.get(streamId)\n const serverStop = options.transport.stop\n if (serverStop !== undefined && id !== null) return yield* serverStop(id)\n yield* Atom.set(run, Atom.Interrupt)\n }),\n )\n .pipe(Atom.keepAlive)\n\n const resume = options.transport.resume\n const attach =\n resume === undefined\n ? undefined\n : options.runtime\n .fn<{ readonly streamId: StreamId; readonly after: number }>()((arg) =>\n follow(resume(arg.streamId, arg.after)),\n )\n .pipe(Atom.keepAlive)\n\n return {\n state,\n streamId,\n submit,\n /** The current run; `AtomRegistry.getResult` on it waits for the run to finish. */\n run,\n stop,\n /** Re-attach to a run (`{ streamId, after }`); set for durable transports. */\n resume: attach,\n }\n}\n\ntype Built<I, S, E, R, ER> = ReturnType<typeof build<I, S, E, R, ER, unknown>>\n\n/**\n * State over a per-run transport: `state`, `submit`, `stop`, `run`, and\n * `resume` when the transport can re-attach (`Transport.durable`,\n * `durableHttp`), typed as present for those.\n */\nexport function make<I, S, E, R, ER, D = Envelope>(\n options: Options<I, S, E, R, ER, D> & {\n readonly transport: Transport.Durable<Input.Sent<I>, S, E, R>\n },\n): Omit<Built<I, S, E, R, ER>, \"resume\"> & {\n readonly resume: NonNullable<Built<I, S, E, R, ER>[\"resume\"]>\n}\nexport function make<I, S, E, R, ER, D = Envelope>(\n options: Options<I, S, E, R, ER, D>,\n): Built<I, S, E, R, ER>\nexport function make<I, S, E, R, ER>(\n options: Options<I, S, E, R, ER, Envelope>,\n): Built<I, S, E, R, ER> {\n return build({ ...options, decode: options.decode ?? identity })\n}\n\ntype ThreadOptions<I, E, R, ER, P extends Payloads> = {\n readonly runtime: Runtime<R, ER>\n readonly transport: Transport.Transport<Input.Sent<I>, Thread.Thread<P>, E, R>\n readonly busy: Busy\n readonly initial?: Thread.Thread<P>\n}\n\n/**\n * `make` with the `Thread` preset: a message list plus the active run. Takes\n * the `Input` preset, with or without typed `data`. With `contract`, the\n * state is typed by it. For other entry types, spread a `Thread.make(config)`\n * fold into `make`.\n */\nexport function thread<I extends Input.Input, E, R, ER, P extends Payloads>(\n options: ThreadOptions<I, E, R, ER, P> & { readonly contract: UiContract<P> },\n): Built<I, Thread.Thread<P>, E, R, ER>\nexport function thread<I extends Input.Input, E, R, ER>(\n options: ThreadOptions<I, E, R, ER, Untyped>,\n): Built<I, Thread.Thread, E, R, ER>\n// The implementation sees `P` as `any`: the options both read and receive\n// `Thread<P>`, so no single `P` fits both overloads. Callers get the overloads.\nexport function thread<I extends Input.Input, E, R, ER>(\n options: ThreadOptions<I, E, R, ER, any> & { readonly contract?: UiContract<any> },\n): Built<I, Thread.Thread<any>, E, R, ER> {\n const fold = Thread.presetFor<any>()\n return build({\n runtime: options.runtime,\n transport: options.transport,\n busy: options.busy,\n initial: options.initial ?? fold.initial,\n step: fold.step,\n decode: options.contract?.decode ?? identity,\n isBusy: (t) => t.active !== null,\n submitted: (t, input: Input.Sent<I>) => fold.submitted(t, input),\n ended: fold.ended,\n })\n}\n\n// ---------------------------------------------------------------------------\n// Sessions: one stream carries every run (a durable chat)\n// ---------------------------------------------------------------------------\n\ntype BaseSessionOptions<I, S, E, R, ER, D> = {\n readonly runtime: Runtime<R, ER>\n readonly transport: Transport.Session<Input.Sent<I>, S, E, R>\n readonly initial: S\n readonly step: (state: S, envelope: D) => S\n /** While true, `submit` ignores new input (the server may also refuse). */\n readonly isBusy?: (state: S) => boolean\n /** Optimistic update applied before the input is sent. */\n readonly submitted?: (state: S, input: Input.Sent<I>) => S\n /** Applied when sending fails or the session's stream gives up. */\n readonly ended?: (state: S, reason: \"stopped\" | \"disconnected\") => S\n}\n\nexport type SessionOptions<I, S, E, R, ER, D = Envelope> = BaseSessionOptions<I, S, E, R, ER, D> &\n WithDecode<D>\n\nconst buildSession = <I, S, E, R, ER, D>(\n options: BaseSessionOptions<I, S, E, R, ER, D> & { readonly decode: Decode<D> },\n) => {\n const state = Atom.make(options.initial).pipe(Atom.keepAlive)\n const streamId = Atom.make<StreamId | null>(null).pipe(Atom.keepAlive)\n\n const settle = (exit: Exit.Exit<unknown, unknown>) => {\n const ended = options.ended\n return ended === undefined || Exit.isSuccess(exit) || Exit.hasInterrupts(exit)\n ? Effect.void\n : Atom.update(state, (s) => ended(s, \"disconnected\"))\n }\n\n // Non-concurrent: opening another session stops following the previous one.\n const open = options.runtime\n .fn<StreamId>()((id) =>\n Atom.set(streamId, id).pipe(\n Effect.andThen(\n options.transport\n .events(id, 0)\n .pipe(\n Stream.runForEach((envelope) =>\n options\n .decode(envelope)\n .pipe(\n Effect.flatMap((decoded) =>\n Atom.update(state, (s) => options.step(s, decoded)),\n ),\n ),\n ),\n ),\n ),\n Effect.onExit(settle),\n ),\n )\n .pipe(Atom.keepAlive)\n\n const submit = options.runtime\n .fn<Input.Draft<I>>()(\n (draft) =>\n Effect.gen(function* () {\n if (options.isBusy?.(yield* Atom.get(state)) === true) return\n const input = yield* Input.withId(draft)\n const submitted = options.submitted\n if (submitted !== undefined) yield* Atom.update(state, (s) => submitted(s, input))\n const opened = Option.fromNullishOr(yield* Atom.get(streamId))\n const sent = yield* options.transport.send(opened, input, yield* Atom.get(state))\n if (Option.isNone(opened)) yield* Atom.set(open, sent)\n }).pipe(Effect.onExit(settle)),\n { concurrent: true },\n )\n .pipe(Atom.keepAlive)\n\n // The server stops the active run; its `run-stopped` arrives on the stream.\n const stop = options.runtime\n .fn<void>()(() =>\n Effect.gen(function* () {\n const id = yield* Atom.get(streamId)\n const serverStop = options.transport.stop\n if (serverStop !== undefined && id !== null) yield* serverStop(id)\n }),\n )\n .pipe(Atom.keepAlive)\n\n return { state, streamId, open, submit, stop }\n}\n\ntype BuiltSession<I, S, E, R, ER> = ReturnType<typeof buildSession<I, S, E, R, ER, unknown>>\n\n/**\n * State over a session transport. `open(streamId)` follows a session's\n * stream from its start (e.g. a chat id kept across reloads). `submit` sends\n * an input; without an open session the server starts one, which is then\n * opened. Runs arrive on the followed stream, not as responses to `submit`.\n */\nexport function session<I, S, E, R, ER, D = Envelope>(\n options: SessionOptions<I, S, E, R, ER, D>,\n): BuiltSession<I, S, E, R, ER>\nexport function session<I, S, E, R, ER>(\n options: SessionOptions<I, S, E, R, ER, Envelope>,\n): BuiltSession<I, S, E, R, ER> {\n return buildSession({ ...options, decode: options.decode ?? identity })\n}\n\ntype SessionThreadOptions<I, E, R, ER, P extends Payloads> = {\n readonly runtime: Runtime<R, ER>\n readonly transport: Transport.Session<Input.Sent<I>, Thread.Thread<P>, E, R>\n readonly isBusy?: (thread: Thread.Thread<P>) => boolean\n}\n\n/**\n * `session` with the `Thread` preset. Input sent while a run is active, or\n * echoed by the server (`input-accepted`), waits in `thread.pending`: the\n * server queues it. Pass `isBusy` for a server that refuses instead. With\n * `contract`, the state is typed by it. For other entry types, spread a\n * `Thread.make(config)` fold into `session`.\n */\nexport function sessionThread<I extends Input.Input, E, R, ER, P extends Payloads>(\n options: SessionThreadOptions<I, E, R, ER, P> & { readonly contract: UiContract<P> },\n): BuiltSession<I, Thread.Thread<P>, E, R, ER>\nexport function sessionThread<I extends Input.Input, E, R, ER>(\n options: SessionThreadOptions<I, E, R, ER, Untyped>,\n): BuiltSession<I, Thread.Thread, E, R, ER>\n// `any` for `P` in the implementation only, as in `thread`.\nexport function sessionThread<I extends Input.Input, E, R, ER>(\n options: SessionThreadOptions<I, E, R, ER, any> & { readonly contract?: UiContract<any> },\n): BuiltSession<I, Thread.Thread<any>, E, R, ER> {\n const fold = Thread.presetFor<any>()\n return buildSession({\n runtime: options.runtime,\n transport: options.transport,\n initial: fold.initial,\n step: fold.step,\n decode: options.contract?.decode ?? identity,\n ...(options.isBusy !== undefined ? { isBusy: options.isBusy } : {}),\n submitted: (t, input: Input.Sent<I>) => fold.submitted(t, input),\n ended: fold.ended,\n })\n}\n"],"mappings":"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"}
@@ -0,0 +1,219 @@
1
+ import { Envelope } from "./RunEvent.mjs";
2
+ import { Effect, Schedule, Schema, Stream } from "effect";
3
+ import { Ndjson, Sse } from "effect/encoding";
4
+ import { HttpClient, HttpClientError, HttpClientRequest } from "effect/http";
5
+ declare namespace Client_d_exports {
6
+ export { ClientError, Disconnected, execute, ndjson, resumable, sse };
7
+ }
8
+ declare const Disconnected_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
9
+ readonly _tag: "Disconnected";
10
+ } & Readonly<A>;
11
+ /** The connection ended before the run's terminal event arrived. */
12
+ declare class Disconnected extends Disconnected_base<{
13
+ readonly lastSeq: number;
14
+ }> {}
15
+ /** What an HTTP source can fail with: transport, a malformed SSE frame, or a malformed NDJSON line. */
16
+ type ClientError = HttpClientError.HttpClientError | Schema.SchemaError | Sse.Retry | Sse.SseError | Ndjson.NdjsonError;
17
+ /** Run a request, failing on a non-2xx status. */
18
+ declare const execute: (request: HttpClientRequest.HttpClientRequest) => Effect.Effect<import("effect/http/HttpClientResponse").HttpClientResponse, HttpClientError.HttpClientError, HttpClient.HttpClient>;
19
+ /** An SSE response (`text/event-stream`) as envelopes. */
20
+ declare const sse: (request: HttpClientRequest.HttpClientRequest) => Stream.Stream<Envelope, HttpClientError.HttpClientError | Sse.Retry | Schema.SchemaError | Sse.SseError, HttpClient.HttpClient>;
21
+ /** An NDJSON response (one envelope per line) as envelopes. */
22
+ declare const ndjson: (request: HttpClientRequest.HttpClientRequest) => Stream.Stream<{
23
+ readonly streamId: string & import("effect/Brand").Brand<"StreamId">;
24
+ readonly seq: number;
25
+ readonly runId?: (string & import("effect/Brand").Brand<"RunId">) | undefined;
26
+ readonly event: {
27
+ readonly type: "run-started";
28
+ readonly runId: string & import("effect/Brand").Brand<"RunId">;
29
+ readonly name?: string | undefined;
30
+ readonly inputs: readonly (string & import("effect/Brand").Brand<"InputId">)[];
31
+ readonly protocol: 2;
32
+ } | {
33
+ readonly type: "run-failed";
34
+ readonly reason: string;
35
+ readonly message?: string | undefined;
36
+ } | {
37
+ readonly type: "run-stopped";
38
+ } | {
39
+ readonly type: "step-started";
40
+ } | {
41
+ readonly type: "step-finished";
42
+ readonly usage?: {
43
+ readonly input_tokens?: number | undefined;
44
+ readonly output_tokens?: number | undefined;
45
+ readonly total_tokens?: number | undefined;
46
+ readonly input_tokens_details?: {
47
+ readonly cached_tokens?: number | undefined;
48
+ readonly cache_write_tokens?: number | undefined;
49
+ } | undefined;
50
+ readonly output_tokens_details?: {
51
+ readonly reasoning_tokens?: number | undefined;
52
+ } | undefined;
53
+ } | undefined;
54
+ } | {
55
+ readonly type: "step-reset";
56
+ } | {
57
+ readonly type: "text-delta";
58
+ readonly text: string;
59
+ } | {
60
+ readonly type: "refusal-delta";
61
+ readonly text: string;
62
+ } | {
63
+ readonly type: "reasoning-started";
64
+ } | {
65
+ readonly type: "reasoning-delta";
66
+ readonly text: string;
67
+ readonly kind: "summary" | "trace";
68
+ } | {
69
+ readonly type: "citation";
70
+ readonly annotation: {
71
+ readonly type: "url_citation";
72
+ readonly url: string;
73
+ readonly title: string;
74
+ readonly start_index?: number | undefined;
75
+ readonly end_index?: number | undefined;
76
+ readonly cited_text?: string | undefined;
77
+ readonly marker?: number | undefined;
78
+ } | {
79
+ readonly type: "file_citation";
80
+ readonly file_id: string;
81
+ readonly index: number;
82
+ } | {
83
+ readonly type: "container_file_citation";
84
+ readonly container_id: string;
85
+ readonly file_id: string;
86
+ readonly start_index: number;
87
+ readonly end_index: number;
88
+ } | {
89
+ readonly type: "file_path";
90
+ readonly file_id: string;
91
+ readonly index: number;
92
+ };
93
+ } | {
94
+ readonly type: "image";
95
+ readonly image: import("@effect-uai/core/Image").ImageSource;
96
+ readonly partialIndex?: number | undefined;
97
+ } | {
98
+ readonly type: "object-started";
99
+ readonly id: string;
100
+ readonly name?: string | undefined;
101
+ } | {
102
+ readonly type: "object-delta";
103
+ readonly id: string;
104
+ readonly patch?: readonly ({
105
+ readonly op: "add";
106
+ readonly path: string;
107
+ readonly value: Schema.Json;
108
+ } | {
109
+ readonly op: "remove";
110
+ readonly path: string;
111
+ } | {
112
+ readonly op: "replace";
113
+ readonly path: string;
114
+ readonly value: Schema.Json;
115
+ })[] | undefined;
116
+ readonly value?: Schema.Json | undefined;
117
+ } | {
118
+ readonly type: "object-failed";
119
+ readonly id: string;
120
+ readonly issues: readonly {
121
+ readonly path: readonly (string | number)[];
122
+ readonly message: string;
123
+ }[];
124
+ } | {
125
+ readonly type: "tool-call-started";
126
+ readonly callId: string;
127
+ readonly name: string;
128
+ } | {
129
+ readonly type: "tool-call-args-delta";
130
+ readonly callId: string;
131
+ readonly delta: string;
132
+ } | {
133
+ readonly type: "usage";
134
+ readonly usage: {
135
+ readonly input_tokens?: number | undefined;
136
+ readonly output_tokens?: number | undefined;
137
+ readonly total_tokens?: number | undefined;
138
+ readonly input_tokens_details?: {
139
+ readonly cached_tokens?: number | undefined;
140
+ readonly cache_write_tokens?: number | undefined;
141
+ } | undefined;
142
+ readonly output_tokens_details?: {
143
+ readonly reasoning_tokens?: number | undefined;
144
+ } | undefined;
145
+ };
146
+ } | {
147
+ readonly type: "input-accepted";
148
+ readonly input: Schema.Json;
149
+ } | {
150
+ readonly type: "run-completed";
151
+ readonly metadata?: Schema.Json | undefined;
152
+ } | {
153
+ readonly type: "run-interrupted";
154
+ readonly interrupts: readonly {
155
+ readonly callId: string;
156
+ readonly reason: "approval";
157
+ readonly name: string;
158
+ readonly input: Schema.Json;
159
+ }[];
160
+ } | {
161
+ readonly type: "object-done";
162
+ readonly id: string;
163
+ readonly name?: string | undefined;
164
+ readonly value: Schema.Json;
165
+ } | {
166
+ readonly type: "tool-call-ready";
167
+ readonly callId: string;
168
+ readonly name: string;
169
+ readonly input: Schema.Json;
170
+ } | {
171
+ readonly type: "tool-approval-requested";
172
+ readonly callId: string;
173
+ readonly name: string;
174
+ readonly input: Schema.Json;
175
+ } | {
176
+ readonly type: "tool-partial";
177
+ readonly callId: string;
178
+ readonly name: string;
179
+ readonly value: Schema.Json;
180
+ } | {
181
+ readonly type: "tool-result";
182
+ readonly callId: string;
183
+ readonly name: string;
184
+ readonly result: {
185
+ readonly _tag: "Success";
186
+ readonly value: Schema.Json;
187
+ } | {
188
+ readonly _tag: "Failure";
189
+ readonly error: Schema.Json;
190
+ };
191
+ } | {
192
+ readonly type: "data";
193
+ readonly name: string;
194
+ readonly value: Schema.Json;
195
+ readonly id?: string | undefined;
196
+ } | {
197
+ readonly type: "live";
198
+ readonly name: string;
199
+ readonly value: Schema.Json;
200
+ };
201
+ }, HttpClientError.HttpClientError | Ndjson.NdjsonError | Schema.SchemaError, HttpClient.HttpClient>;
202
+ /**
203
+ * Keep events flowing across dropped connections: `connect(after)` is
204
+ * re-invoked with the last seen `seq` on failure, per `schedule`; duplicates
205
+ * are dropped. A connection that ends cleanly before the end counts as a
206
+ * failure (`Disconnected`), so it is retried too.
207
+ *
208
+ * `until: "terminal"` (default) follows one run and completes after its
209
+ * terminal. `until: "never"` follows a session stream that carries many runs
210
+ * (a durable chat) and never completes on its own.
211
+ */
212
+ declare const resumable: <E, R, X>(connect: (after: number) => Stream.Stream<Envelope, E, R>, options: {
213
+ readonly from?: number;
214
+ readonly until?: "terminal" | "never";
215
+ readonly schedule: Schedule.Schedule<X, E | Disconnected>;
216
+ }) => Stream.Stream<Envelope, E | Disconnected, R>;
217
+ //#endregion
218
+ export { ndjson as a, execute as i, Client_d_exports as n, resumable as o, Disconnected as r, sse as s, ClientError as t };
219
+ //# sourceMappingURL=Client-C9UYN_VK.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Client-C9UYN_VK.d.mts","names":[],"sources":["../src/Client.ts"],"mappings":";;;;;;;;;;;cAaa,qBAAqB;WAA4C;;;KAGlE,cACR,gBAAgB,kBAChB,OAAO,cACP,IAAI,QACJ,IAAI,WACJ,OAAO;;cAGE,UAAO,SAAa,kBAAkB,sBAAiB,OAAA,gDAAA,oBAAA,gBAAA,iBAAA,WAAA;;cASvD,MAAG,SAAa,kBAAkB,sBAAiB,OAAA,OAAA,UAAA,gBAAA,kBAAA,IAAA,QAAA,OAAA,cAAA,IAAA,UAAA,WAAA;;cAInD,SAAM,SAAa,kBAAkB,sBAAiB,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GACuB,gBAAA,kBAAA,OAAA,cAAA,OAAA,aAAA,WAAA;;;;;;;;;;;cAY7E,YAAa,GAAG,GAAG,GAAC,UACrB,kBAAkB,OAAO,OAAO,UAAU,GAAG,IAAE;WAE9C;WACA;WACA,UAAU,SAAS,SAAS,GAAG,IAAI;MAE7C,OAAO,OAAO,UAAU,IAAI,cAAc"}
@@ -0,0 +1,2 @@
1
+ import { a as ndjson, i as execute, o as resumable, r as Disconnected, s as sse, t as ClientError } from "./Client-C9UYN_VK.mjs";
2
+ export { ClientError, Disconnected, execute, ndjson, resumable, sse };