@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
@@ -0,0 +1,133 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { Effect, Match, Option, Schema, Stream } from "effect";
3
+ import * as Turn from "@effect-uai/core/Turn";
4
+ //#region src/Projection.ts
5
+ var Projection_exports = /* @__PURE__ */ __exportAll({
6
+ Options: () => Options,
7
+ State: () => State,
8
+ initial: () => initial,
9
+ step: () => step,
10
+ toRunEvents: () => toRunEvents
11
+ });
12
+ const Options = Schema.Struct({
13
+ /**
14
+ * `"signal"` (default): only `reasoning-started`. `"summary"`: also
15
+ * forward summary deltas. `"full"`: also forward raw reasoning traces.
16
+ */
17
+ reasoning: Schema.optionalKey(Schema.Literals([
18
+ "signal",
19
+ "summary",
20
+ "full"
21
+ ])),
22
+ /** Forward raw tool-argument deltas (`tool-call-args-delta`). Default false. */
23
+ toolArgs: Schema.optionalKey(Schema.Boolean)
24
+ });
25
+ const State = Schema.Struct({
26
+ inStep: Schema.Boolean,
27
+ reasoningShown: Schema.Boolean,
28
+ /** Tool names by call id, for outputs that arrive as plain `function_call_output` items. */
29
+ tools: Schema.Record(Schema.String, Schema.String)
30
+ });
31
+ const initial = {
32
+ inStep: false,
33
+ reasoningShown: false,
34
+ tools: {}
35
+ };
36
+ const parseJson = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Json));
37
+ const decodeJson = (raw) => Option.getOrElse(parseJson(raw), () => raw);
38
+ const inStep = (s, events) => s.inStep ? [s, events] : [{
39
+ ...s,
40
+ inStep: true,
41
+ reasoningShown: false
42
+ }, [{ type: "step-started" }, ...events]];
43
+ const reasoning = (options) => (s, e) => {
44
+ const mode = options?.reasoning ?? "signal";
45
+ const forward = mode === "full" || mode === "summary" && e.kind === "summary";
46
+ const [opened, prefix] = inStep(s, []);
47
+ const started = opened.reasoningShown ? [] : [{ type: "reasoning-started" }];
48
+ const delta = forward ? [{
49
+ type: "reasoning-delta",
50
+ text: e.text,
51
+ kind: e.kind
52
+ }] : [];
53
+ return [{
54
+ ...opened,
55
+ reasoningShown: true
56
+ }, [
57
+ ...prefix,
58
+ ...started,
59
+ ...delta
60
+ ]];
61
+ };
62
+ const turnComplete = (s, turn) => {
63
+ const calls = Turn.getToolCalls(turn);
64
+ const ready = calls.map((call) => ({
65
+ type: "tool-call-ready",
66
+ callId: call.call_id,
67
+ name: call.name,
68
+ input: decodeJson(call.arguments)
69
+ }));
70
+ const [, events] = inStep(s, [...ready, {
71
+ type: "step-finished",
72
+ usage: turn.usage
73
+ }]);
74
+ const tools = {
75
+ ...s.tools,
76
+ ...Object.fromEntries(calls.map((c) => [c.call_id, c.name]))
77
+ };
78
+ return [{
79
+ ...initial,
80
+ tools
81
+ }, events];
82
+ };
83
+ const outputResult = (config, s, o) => Effect.map(config.contract.toolOutput(o, s.tools[o.call_id] ?? ""), (event) => [s, [event]]);
84
+ /** Map one loop event onto zero or more `RunEvent`s; tool values are encoded by the contract. */
85
+ const step = (config) => (s, event) => Match.value(event).pipe(Match.when({ type: "function_call_output" }, (o) => outputResult(config, s, o)), Match.tags({
86
+ Partial: (e) => Effect.map(config.contract.toolEvent(e), (r) => [s, [r]]),
87
+ Output: (e) => Effect.map(config.contract.toolEvent(e), (r) => [s, [r]])
88
+ }), Match.orElse((e) => Effect.succeed(turnStep(config)(s, e))));
89
+ const turnStep = (options) => (s, event) => Match.value(event).pipe(Match.tagsExhaustive({
90
+ TextDelta: (e) => inStep(s, [{
91
+ type: "text-delta",
92
+ text: e.text
93
+ }]),
94
+ RefusalDelta: (e) => inStep(s, [{
95
+ type: "refusal-delta",
96
+ text: e.text
97
+ }]),
98
+ ReasoningDelta: (e) => reasoning(options)(s, e),
99
+ ToolCallStart: (e) => inStep(s, [{
100
+ type: "tool-call-started",
101
+ callId: e.call_id,
102
+ name: e.name
103
+ }]),
104
+ ToolCallArgsDelta: (e) => inStep(s, options?.toolArgs === true ? [{
105
+ type: "tool-call-args-delta",
106
+ callId: e.call_id,
107
+ delta: e.delta
108
+ }] : []),
109
+ CitationAdded: (e) => inStep(s, [{
110
+ type: "citation",
111
+ annotation: e.annotation
112
+ }]),
113
+ ImageOutput: (e) => inStep(s, [e.partialIndex === void 0 ? {
114
+ type: "image",
115
+ image: e.image
116
+ } : {
117
+ type: "image",
118
+ image: e.image,
119
+ partialIndex: e.partialIndex
120
+ }]),
121
+ UsageUpdate: () => [s, []],
122
+ WebSearchCall: () => [s, []],
123
+ TurnComplete: (e) => turnComplete(s, e.turn)
124
+ }));
125
+ /**
126
+ * Stream operator: project a loop's events onto `RunEvent`s. A tool value the
127
+ * contract can't encode fails the stream: a contract violation, not data to show.
128
+ */
129
+ const toRunEvents = (config) => (self) => self.pipe(Stream.mapAccumEffect(() => initial, step(config)));
130
+ //#endregion
131
+ export { Options, State, initial, step, Projection_exports as t, toRunEvents };
132
+
133
+ //# sourceMappingURL=Projection.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Projection.mjs","names":[],"sources":["../src/Projection.ts"],"sourcesContent":["/**\n * Project an effect-uai loop onto `RunEvent`s. The projection is a fold pair\n * (`initial`, `step`) because step boundaries need state; `toRunEvents` runs\n * it over a stream. To add app events (metrics, domain data), map them\n * yourself and delegate the rest to `step`.\n *\n * Never forwards `TurnComplete.turn` itself: it carries `providerData`\n * (encrypted reasoning, item ids) that must not reach a browser.\n */\nimport type * as Items from \"@effect-uai/core/Items\"\nimport type * as ToolEvent from \"@effect-uai/core/ToolEvent\"\nimport * as Turn from \"@effect-uai/core/Turn\"\nimport { Effect, Match, Option, Schema, Stream } from \"effect\"\nimport type { Json, RunEvent } from \"./RunEvent.js\"\nimport * as UiContract from \"./UiContract.js\"\n\n/** What an effect-uai loop emits: turn events, tool events, and plain tool outputs. */\nexport type Source = Turn.TurnEvent | ToolEvent.ToolEvent | Items.ToolCallOutput\n\nexport const Options = Schema.Struct({\n /**\n * `\"signal\"` (default): only `reasoning-started`. `\"summary\"`: also\n * forward summary deltas. `\"full\"`: also forward raw reasoning traces.\n */\n reasoning: Schema.optionalKey(Schema.Literals([\"signal\", \"summary\", \"full\"])),\n /** Forward raw tool-argument deltas (`tool-call-args-delta`). Default false. */\n toolArgs: Schema.optionalKey(Schema.Boolean),\n})\nexport type Options = typeof Options.Type\n\n/** `Options` plus the contract whose tool schemas encode tool values and errors. */\nexport type Config = Options & {\n readonly contract: Pick<UiContract.UiContract<any>, \"toolEvent\" | \"toolOutput\">\n}\n\nexport const State = Schema.Struct({\n inStep: Schema.Boolean,\n reasoningShown: Schema.Boolean,\n /** Tool names by call id, for outputs that arrive as plain `function_call_output` items. */\n tools: Schema.Record(Schema.String, Schema.String),\n})\nexport type State = typeof State.Type\n\nexport const initial: State = { inStep: false, reasoningShown: false, tools: {} }\n\ntype Step = readonly [State, ReadonlyArray<RunEvent>]\n\n// Tool arguments are a JSON string; hand the client the decoded value, falling\n// back to the raw string when it isn't JSON.\nconst parseJson = Schema.decodeUnknownOption(Schema.fromJsonString(Schema.Json))\nconst decodeJson = (raw: string): Json => Option.getOrElse(parseJson(raw), () => raw)\n\n// Content belongs to a step (one model turn); open one lazily on its first event.\nconst inStep = (s: State, events: ReadonlyArray<RunEvent>): Step =>\n s.inStep\n ? [s, events]\n : [{ ...s, inStep: true, reasoningShown: false }, [{ type: \"step-started\" }, ...events]]\n\nconst reasoning =\n (options: Options | undefined) =>\n (s: State, e: Extract<Turn.TurnEvent, { _tag: \"ReasoningDelta\" }>): Step => {\n const mode = options?.reasoning ?? \"signal\"\n const forward = mode === \"full\" || (mode === \"summary\" && e.kind === \"summary\")\n const [opened, prefix] = inStep(s, [])\n const started: ReadonlyArray<RunEvent> = opened.reasoningShown\n ? []\n : [{ type: \"reasoning-started\" }]\n const delta: ReadonlyArray<RunEvent> = forward\n ? [{ type: \"reasoning-delta\", text: e.text, kind: e.kind }]\n : []\n return [{ ...opened, reasoningShown: true }, [...prefix, ...started, ...delta]]\n }\n\nconst turnComplete = (s: State, turn: Turn.Turn): Step => {\n const calls = Turn.getToolCalls(turn)\n const ready = calls.map((call): RunEvent => ({\n type: \"tool-call-ready\",\n callId: call.call_id,\n name: call.name,\n input: decodeJson(call.arguments),\n }))\n const [, events] = inStep(s, [...ready, { type: \"step-finished\", usage: turn.usage }])\n const tools = { ...s.tools, ...Object.fromEntries(calls.map((c) => [c.call_id, c.name])) }\n return [{ ...initial, tools }, events]\n}\n\n// A history item already holds the encoded value; the contract shapes it for the wire.\nconst outputResult = (config: Config, s: State, o: Items.ToolCallOutput) =>\n Effect.map(config.contract.toolOutput(o, s.tools[o.call_id] ?? \"\"), (event): Step => [s, [event]])\n\n/** Map one loop event onto zero or more `RunEvent`s; tool values are encoded by the contract. */\nexport const step =\n (config: Config) =>\n (s: State, event: Source): Effect.Effect<Step, Schema.SchemaError> =>\n Match.value(event).pipe(\n Match.when({ type: \"function_call_output\" }, (o) => outputResult(config, s, o)),\n Match.tags({\n Partial: (e) => Effect.map(config.contract.toolEvent(e), (r): Step => [s, [r]]),\n Output: (e) => Effect.map(config.contract.toolEvent(e), (r): Step => [s, [r]]),\n }),\n Match.orElse((e) => Effect.succeed(turnStep(config)(s, e))),\n )\n\nconst turnStep =\n (options: Options) =>\n (s: State, event: Turn.TurnEvent): Step =>\n Match.value(event).pipe(\n Match.tagsExhaustive({\n TextDelta: (e) => inStep(s, [{ type: \"text-delta\", text: e.text }]),\n RefusalDelta: (e) => inStep(s, [{ type: \"refusal-delta\", text: e.text }]),\n ReasoningDelta: (e) => reasoning(options)(s, e),\n ToolCallStart: (e) =>\n inStep(s, [{ type: \"tool-call-started\", callId: e.call_id, name: e.name }]),\n ToolCallArgsDelta: (e) =>\n inStep(\n s,\n options?.toolArgs === true\n ? [{ type: \"tool-call-args-delta\", callId: e.call_id, delta: e.delta }]\n : [],\n ),\n CitationAdded: (e) => inStep(s, [{ type: \"citation\", annotation: e.annotation }]),\n ImageOutput: (e) =>\n inStep(s, [\n e.partialIndex === undefined\n ? { type: \"image\", image: e.image }\n : { type: \"image\", image: e.image, partialIndex: e.partialIndex },\n ]),\n // Usage arrives on `step-finished`; search progress has no UI event yet.\n UsageUpdate: (): Step => [s, []],\n WebSearchCall: (): Step => [s, []],\n TurnComplete: (e) => turnComplete(s, e.turn),\n }),\n )\n\n/**\n * Stream operator: project a loop's events onto `RunEvent`s. A tool value the\n * contract can't encode fails the stream: a contract violation, not data to show.\n */\nexport const toRunEvents =\n (config: Config) =>\n <E, R>(self: Stream.Stream<Source, E, R>): Stream.Stream<RunEvent, E | Schema.SchemaError, R> =>\n self.pipe(Stream.mapAccumEffect(() => initial, step(config)))\n"],"mappings":"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"}
package/dist/Run.d.mts ADDED
@@ -0,0 +1,44 @@
1
+ import { Envelope, InputId, RunEvent, RunId, StreamId } from "./RunEvent.mjs";
2
+ import { Cause, Effect, Schema, Stream } from "effect";
3
+ import * as Duration from "effect/Duration";
4
+ declare namespace Run_d_exports {
5
+ export { SequenceState, StartOptions, coalesce, complete, failed, sequence, start, stopWhen };
6
+ }
7
+ type StartOptions = {
8
+ readonly runId: RunId;
9
+ /** Client input ids the run answers. */
10
+ readonly inputs?: ReadonlyArray<InputId>;
11
+ /** Role label when a stream carries several kinds of run. */
12
+ readonly name?: string;
13
+ };
14
+ /** Prepend `run-started`. */
15
+ declare const start: (options: StartOptions) => <E, R>(self: Stream.Stream<RunEvent, E, R>) => Stream.Stream<RunEvent, E, R>;
16
+ /** End the run at its first terminal; append `run-completed` if it ends without one. */
17
+ declare const complete: <E, R>(self: Stream.Stream<RunEvent, E, R>) => Stream.Stream<RunEvent, E, R>;
18
+ /** End the run with `run-stopped` when `signal` completes (e.g. a stop `Deferred`). */
19
+ declare const stopWhen: (signal: Effect.Effect<void>) => <E, R>(self: Stream.Stream<RunEvent, E, R>) => Stream.Stream<RunEvent, E, R>;
20
+ /** A `run-failed` for a failed stream: `reason` is the error's `_tag`, else `"internal"`. */
21
+ declare const failed: (cause: Cause.Cause<unknown>) => RunEvent;
22
+ declare const SequenceState: Schema.Struct<{
23
+ readonly seq: Schema.Int;
24
+ /** Id of the run currently open on this stream. */
25
+ readonly runId: Schema.optionalKey<Schema.brand<Schema.String, "RunId">>;
26
+ }>;
27
+ type SequenceState = typeof SequenceState.Type;
28
+ /**
29
+ * Wrap events in envelopes with a per-stream `seq` (starting after `from`,
30
+ * default 0) and the current run's id, taken from the last `run-started`.
31
+ */
32
+ declare const sequence: (streamId: StreamId, options?: {
33
+ readonly from?: number;
34
+ }) => <E, R>(self: Stream.Stream<RunEvent, E, R>) => Stream.Stream<Envelope, E, R>;
35
+ /**
36
+ * Merge consecutive text, reasoning and tool-argument deltas that arrive
37
+ * within `window`; every other event passes through unchanged and in order.
38
+ * Bounds frames on the wire and rows in a durable log; adds up to `window`
39
+ * latency.
40
+ */
41
+ declare const coalesce: (window: Duration.Input) => <E, R>(self: Stream.Stream<RunEvent, E, R>) => Stream.Stream<RunEvent, E, R>;
42
+ //#endregion
43
+ export { SequenceState, StartOptions, coalesce, complete, failed, sequence, start, stopWhen, Run_d_exports as t };
44
+ //# sourceMappingURL=Run.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Run.d.mts","names":[],"sources":["../src/Run.ts"],"mappings":";;;;;;KAmCY;WACD,OAAO;;WAEP,SAAS,cAAc;;WAEvB;;;cAIE,QAAK,SACN,kBACT,GAAG,GAAC,MAAQ,OAAO,OAAO,UAAU,GAAG,OAAK,OAAO,OAAO,UAAU,GAAG;;cAY7D,WAAY,GAAG,GAAC,MACrB,OAAO,OAAO,UAAU,GAAG,OAChC,OAAO,OAAO,UAAU,GAAG;;cAcjB,WAAQ,QACV,OAAO,kBACf,GAAG,GAAC,MAAQ,OAAO,OAAO,UAAU,GAAG,OAAK,OAAO,OAAO,UAAU,GAAG;;cAS7D,SAAM,OAAW,MAAM,mBAAiB;cAWxC,eAAa,OAAA;;;;;KAKd,uBAAuB,cAAc;;;;;cAiBpC,WAAQ,UACR,UAAQ;WAAuB;OACzC,GAAG,GAAC,MAAQ,OAAO,OAAO,UAAU,GAAG,OAAK,OAAO,OAAO,UAAU,GAAG;;;;;;;cAuC7D,WAAQ,QACV,SAAS,WACjB,GAAG,GAAC,MAAQ,OAAO,OAAO,UAAU,GAAG,OAAK,OAAO,OAAO,UAAU,GAAG"}
package/dist/Run.mjs ADDED
@@ -0,0 +1,117 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
+ import { RunId, isTerminal } from "./RunEvent.mjs";
3
+ import { Array, Cause, Effect, Predicate, Schema, Stream } from "effect";
4
+ //#region src/Run.ts
5
+ /**
6
+ * Server-side stream operators for runs. Each does one thing; a run pipes
7
+ * the ones it needs:
8
+ *
9
+ * agent(history).pipe(
10
+ * Projection.toRunEvents({ contract }),
11
+ * Run.coalesce("30 millis"),
12
+ * Stream.catchCause((cause) => Stream.succeed(Run.failed(cause))),
13
+ * Run.stopWhen(Deferred.await(stop)), // durable runs
14
+ * Run.start({ runId, inputs }),
15
+ * Run.complete,
16
+ * Run.sequence(streamId),
17
+ * )
18
+ *
19
+ * In this order they keep the protocol's rule: `run-started` first, exactly
20
+ * one terminal last. Direct mode needs no stop: when the client goes away,
21
+ * the server interrupts the stream (Effect's `HttpServer`, or
22
+ * `HttpRouter.toWebHandler` behind a framework's route).
23
+ */
24
+ var Run_exports = /* @__PURE__ */ __exportAll({
25
+ SequenceState: () => SequenceState,
26
+ coalesce: () => coalesce,
27
+ complete: () => complete,
28
+ failed: () => failed,
29
+ sequence: () => sequence,
30
+ start: () => start,
31
+ stopWhen: () => stopWhen
32
+ });
33
+ /** Prepend `run-started`. */
34
+ const start = (options) => (self) => {
35
+ const started = {
36
+ type: "run-started",
37
+ runId: options.runId,
38
+ inputs: options.inputs ?? [],
39
+ protocol: 2,
40
+ ...options.name !== void 0 ? { name: options.name } : {}
41
+ };
42
+ return Stream.make(started).pipe(Stream.concat(self));
43
+ };
44
+ /** End the run at its first terminal; append `run-completed` if it ends without one. */
45
+ const complete = (self) => self.pipe(Stream.takeUntil(isTerminal), Stream.mapAccum(() => false, (ended, event) => [ended || isTerminal(event), [event]], { onHalt: (ended) => ended ? [] : [{ type: "run-completed" }] }));
46
+ /** End the run with `run-stopped` when `signal` completes (e.g. a stop `Deferred`). */
47
+ const stopWhen = (signal) => (self) => self.pipe(Stream.merge(Stream.fromEffect(Effect.as(signal, { type: "run-stopped" })), { haltStrategy: "left" }), Stream.takeUntil((event) => event.type === "run-stopped"));
48
+ /** A `run-failed` for a failed stream: `reason` is the error's `_tag`, else `"internal"`. */
49
+ const failed = (cause) => {
50
+ const error = Cause.squash(cause);
51
+ return {
52
+ type: "run-failed",
53
+ reason: Predicate.hasProperty(error, "_tag") && Predicate.isString(error._tag) ? error._tag : "internal"
54
+ };
55
+ };
56
+ const SequenceState = Schema.Struct({
57
+ seq: Schema.Int,
58
+ /** Id of the run currently open on this stream. */
59
+ runId: Schema.optionalKey(RunId)
60
+ });
61
+ const sequenceStep = (streamId) => (s, event) => {
62
+ const runId = event.type === "run-started" ? event.runId : s.runId;
63
+ const seq = s.seq + 1;
64
+ const envelope = runId === void 0 ? {
65
+ streamId,
66
+ seq,
67
+ event
68
+ } : {
69
+ streamId,
70
+ seq,
71
+ runId,
72
+ event
73
+ };
74
+ return [runId === void 0 || isTerminal(event) ? { seq } : {
75
+ seq,
76
+ runId
77
+ }, [envelope]];
78
+ };
79
+ /**
80
+ * Wrap events in envelopes with a per-stream `seq` (starting after `from`,
81
+ * default 0) and the current run's id, taken from the last `run-started`.
82
+ */
83
+ const sequence = (streamId, options) => (self) => self.pipe(Stream.mapAccum(() => ({ seq: options?.from ?? 0 }), sequenceStep(streamId)));
84
+ const merge = (a, b) => {
85
+ if (a.type === "text-delta" && b.type === "text-delta") return {
86
+ ...a,
87
+ text: a.text + b.text
88
+ };
89
+ if (a.type === "refusal-delta" && b.type === "refusal-delta") return {
90
+ ...a,
91
+ text: a.text + b.text
92
+ };
93
+ if (a.type === "reasoning-delta" && b.type === "reasoning-delta" && a.kind === b.kind) return {
94
+ ...a,
95
+ text: a.text + b.text
96
+ };
97
+ if (a.type === "tool-call-args-delta" && b.type === "tool-call-args-delta" && a.callId === b.callId) return {
98
+ ...a,
99
+ delta: a.delta + b.delta
100
+ };
101
+ };
102
+ const mergeAdjacent = (group) => Array.reduce(group, Array.empty(), (acc, event) => {
103
+ const last = acc.at(-1);
104
+ const merged = last === void 0 ? void 0 : merge(last, event);
105
+ return merged === void 0 ? [...acc, event] : [...acc.slice(0, -1), merged];
106
+ });
107
+ /**
108
+ * Merge consecutive text, reasoning and tool-argument deltas that arrive
109
+ * within `window`; every other event passes through unchanged and in order.
110
+ * Bounds frames on the wire and rows in a durable log; adds up to `window`
111
+ * latency.
112
+ */
113
+ const coalesce = (window) => (self) => self.pipe(Stream.groupedWithin(1024, window), Stream.flatMap((group) => Stream.fromIterable(mergeAdjacent(group))));
114
+ //#endregion
115
+ export { SequenceState, coalesce, complete, failed, sequence, start, stopWhen, Run_exports as t };
116
+
117
+ //# sourceMappingURL=Run.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Run.mjs","names":["Arr"],"sources":["../src/Run.ts"],"sourcesContent":["/**\n * Server-side stream operators for runs. Each does one thing; a run pipes\n * the ones it needs:\n *\n * agent(history).pipe(\n * Projection.toRunEvents({ contract }),\n * Run.coalesce(\"30 millis\"),\n * Stream.catchCause((cause) => Stream.succeed(Run.failed(cause))),\n * Run.stopWhen(Deferred.await(stop)), // durable runs\n * Run.start({ runId, inputs }),\n * Run.complete,\n * Run.sequence(streamId),\n * )\n *\n * In this order they keep the protocol's rule: `run-started` first, exactly\n * one terminal last. Direct mode needs no stop: when the client goes away,\n * the server interrupts the stream (Effect's `HttpServer`, or\n * `HttpRouter.toWebHandler` behind a framework's route).\n */\nimport { Array as Arr, Cause, Effect, Predicate, Schema, Stream } from \"effect\"\nimport type * as Duration from \"effect/Duration\"\nimport {\n type Envelope,\n type InputId,\n isTerminal,\n PROTOCOL_VERSION,\n RunId,\n type RunEvent,\n type StreamId,\n} from \"./RunEvent.js\"\n\n// ---------------------------------------------------------------------------\n// A run's frame: start, stop, failure, completion\n// ---------------------------------------------------------------------------\n\nexport type StartOptions = {\n readonly runId: RunId\n /** Client input ids the run answers. */\n readonly inputs?: ReadonlyArray<InputId>\n /** Role label when a stream carries several kinds of run. */\n readonly name?: string\n}\n\n/** Prepend `run-started`. */\nexport const start =\n (options: StartOptions) =>\n <E, R>(self: Stream.Stream<RunEvent, E, R>): Stream.Stream<RunEvent, E, R> => {\n const started: RunEvent = {\n type: \"run-started\",\n runId: options.runId,\n inputs: options.inputs ?? [],\n protocol: PROTOCOL_VERSION,\n ...(options.name !== undefined ? { name: options.name } : {}),\n }\n return Stream.make(started).pipe(Stream.concat(self))\n }\n\n/** End the run at its first terminal; append `run-completed` if it ends without one. */\nexport const complete = <E, R>(\n self: Stream.Stream<RunEvent, E, R>,\n): Stream.Stream<RunEvent, E, R> =>\n self.pipe(\n Stream.takeUntil(isTerminal),\n Stream.mapAccum(\n () => false,\n (ended, event): readonly [boolean, ReadonlyArray<RunEvent>] => [\n ended || isTerminal(event),\n [event],\n ],\n { onHalt: (ended): ReadonlyArray<RunEvent> => (ended ? [] : [{ type: \"run-completed\" }]) },\n ),\n )\n\n/** End the run with `run-stopped` when `signal` completes (e.g. a stop `Deferred`). */\nexport const stopWhen =\n (signal: Effect.Effect<void>) =>\n <E, R>(self: Stream.Stream<RunEvent, E, R>): Stream.Stream<RunEvent, E, R> =>\n self.pipe(\n Stream.merge(Stream.fromEffect(Effect.as(signal, { type: \"run-stopped\" } as const)), {\n haltStrategy: \"left\",\n }),\n Stream.takeUntil((event) => event.type === \"run-stopped\"),\n )\n\n/** A `run-failed` for a failed stream: `reason` is the error's `_tag`, else `\"internal\"`. */\nexport const failed = (cause: Cause.Cause<unknown>): RunEvent => {\n const error = Cause.squash(cause)\n const reason =\n Predicate.hasProperty(error, \"_tag\") && Predicate.isString(error._tag) ? error._tag : \"internal\"\n return { type: \"run-failed\", reason }\n}\n\n// ---------------------------------------------------------------------------\n// sequence\n// ---------------------------------------------------------------------------\n\nexport const SequenceState = Schema.Struct({\n seq: Schema.Int,\n /** Id of the run currently open on this stream. */\n runId: Schema.optionalKey(RunId),\n})\nexport type SequenceState = typeof SequenceState.Type\n\nconst sequenceStep =\n (streamId: StreamId) =>\n (s: SequenceState, event: RunEvent): readonly [SequenceState, ReadonlyArray<Envelope>] => {\n const runId = event.type === \"run-started\" ? event.runId : s.runId\n const seq = s.seq + 1\n const envelope: Envelope =\n runId === undefined ? { streamId, seq, event } : { streamId, seq, runId, event }\n const next: SequenceState = runId === undefined || isTerminal(event) ? { seq } : { seq, runId }\n return [next, [envelope]]\n }\n\n/**\n * Wrap events in envelopes with a per-stream `seq` (starting after `from`,\n * default 0) and the current run's id, taken from the last `run-started`.\n */\nexport const sequence =\n (streamId: StreamId, options?: { readonly from?: number }) =>\n <E, R>(self: Stream.Stream<RunEvent, E, R>): Stream.Stream<Envelope, E, R> =>\n self.pipe(\n Stream.mapAccum((): SequenceState => ({ seq: options?.from ?? 0 }), sequenceStep(streamId)),\n )\n\n// ---------------------------------------------------------------------------\n// coalesce\n// ---------------------------------------------------------------------------\n\nconst merge = (a: RunEvent, b: RunEvent): RunEvent | undefined => {\n if (a.type === \"text-delta\" && b.type === \"text-delta\") return { ...a, text: a.text + b.text }\n if (a.type === \"refusal-delta\" && b.type === \"refusal-delta\")\n return { ...a, text: a.text + b.text }\n if (a.type === \"reasoning-delta\" && b.type === \"reasoning-delta\" && a.kind === b.kind) {\n return { ...a, text: a.text + b.text }\n }\n if (\n a.type === \"tool-call-args-delta\" &&\n b.type === \"tool-call-args-delta\" &&\n a.callId === b.callId\n ) {\n return { ...a, delta: a.delta + b.delta }\n }\n return undefined\n}\n\nconst mergeAdjacent = (group: ReadonlyArray<RunEvent>): ReadonlyArray<RunEvent> =>\n Arr.reduce(group, Arr.empty<RunEvent>(), (acc: ReadonlyArray<RunEvent>, event) => {\n const last = acc.at(-1)\n const merged = last === undefined ? undefined : merge(last, event)\n return merged === undefined ? [...acc, event] : [...acc.slice(0, -1), merged]\n })\n\n/**\n * Merge consecutive text, reasoning and tool-argument deltas that arrive\n * within `window`; every other event passes through unchanged and in order.\n * Bounds frames on the wire and rows in a durable log; adds up to `window`\n * latency.\n */\nexport const coalesce =\n (window: Duration.Input) =>\n <E, R>(self: Stream.Stream<RunEvent, E, R>): Stream.Stream<RunEvent, E, R> =>\n self.pipe(\n Stream.groupedWithin(1024, window),\n Stream.flatMap((group) => Stream.fromIterable(mergeAdjacent(group))),\n )\n"],"mappings":"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"}