effect-assistant 0.0.0-stage → 0.1.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 (185) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +22 -2
  3. package/dist/AppEvent.d.ts +111 -0
  4. package/dist/AppEvent.d.ts.map +1 -0
  5. package/dist/AppEvent.js +71 -0
  6. package/dist/AppEvent.js.map +1 -0
  7. package/dist/Assistant.d.ts +403 -0
  8. package/dist/Assistant.d.ts.map +1 -0
  9. package/dist/Assistant.js +126 -0
  10. package/dist/Assistant.js.map +1 -0
  11. package/dist/AssistantActivity.d.ts +85 -0
  12. package/dist/AssistantActivity.d.ts.map +1 -0
  13. package/dist/AssistantActivity.js +105 -0
  14. package/dist/AssistantActivity.js.map +1 -0
  15. package/dist/AssistantEngine.d.ts +116 -0
  16. package/dist/AssistantEngine.d.ts.map +1 -0
  17. package/dist/AssistantEngine.js +255 -0
  18. package/dist/AssistantEngine.js.map +1 -0
  19. package/dist/AssistantEvent.d.ts +614 -0
  20. package/dist/AssistantEvent.d.ts.map +1 -0
  21. package/dist/AssistantEvent.js +523 -0
  22. package/dist/AssistantEvent.js.map +1 -0
  23. package/dist/AssistantLoop.d.ts +171 -0
  24. package/dist/AssistantLoop.d.ts.map +1 -0
  25. package/dist/AssistantLoop.js +108 -0
  26. package/dist/AssistantLoop.js.map +1 -0
  27. package/dist/AssistantToolCall.d.ts +32 -0
  28. package/dist/AssistantToolCall.d.ts.map +1 -0
  29. package/dist/AssistantToolCall.js +37 -0
  30. package/dist/AssistantToolCall.js.map +1 -0
  31. package/dist/Block.d.ts +69 -0
  32. package/dist/Block.d.ts.map +1 -0
  33. package/dist/Block.js +39 -0
  34. package/dist/Block.js.map +1 -0
  35. package/dist/BlockKit.d.ts +184 -0
  36. package/dist/BlockKit.d.ts.map +1 -0
  37. package/dist/BlockKit.js +151 -0
  38. package/dist/BlockKit.js.map +1 -0
  39. package/dist/ChatId.d.ts +31 -0
  40. package/dist/ChatId.d.ts.map +1 -0
  41. package/dist/ChatId.js +36 -0
  42. package/dist/ChatId.js.map +1 -0
  43. package/dist/ChatLog.d.ts +109 -0
  44. package/dist/ChatLog.d.ts.map +1 -0
  45. package/dist/ChatLog.js +177 -0
  46. package/dist/ChatLog.js.map +1 -0
  47. package/dist/ChatView.d.ts +459 -0
  48. package/dist/ChatView.d.ts.map +1 -0
  49. package/dist/ChatView.js +496 -0
  50. package/dist/ChatView.js.map +1 -0
  51. package/dist/Chats.d.ts +110 -0
  52. package/dist/Chats.d.ts.map +1 -0
  53. package/dist/Chats.js +98 -0
  54. package/dist/Chats.js.map +1 -0
  55. package/dist/Compaction.d.ts +105 -0
  56. package/dist/Compaction.d.ts.map +1 -0
  57. package/dist/Compaction.js +133 -0
  58. package/dist/Compaction.js.map +1 -0
  59. package/dist/CurrentChat.d.ts +40 -0
  60. package/dist/CurrentChat.d.ts.map +1 -0
  61. package/dist/CurrentChat.js +33 -0
  62. package/dist/CurrentChat.js.map +1 -0
  63. package/dist/Decision.d.ts +145 -0
  64. package/dist/Decision.d.ts.map +1 -0
  65. package/dist/Decision.js +178 -0
  66. package/dist/Decision.js.map +1 -0
  67. package/dist/DecisionId.d.ts +37 -0
  68. package/dist/DecisionId.d.ts.map +1 -0
  69. package/dist/DecisionId.js +44 -0
  70. package/dist/DecisionId.js.map +1 -0
  71. package/dist/InputId.d.ts +38 -0
  72. package/dist/InputId.d.ts.map +1 -0
  73. package/dist/InputId.js +43 -0
  74. package/dist/InputId.js.map +1 -0
  75. package/dist/Instructions.d.ts +56 -0
  76. package/dist/Instructions.d.ts.map +1 -0
  77. package/dist/Instructions.js +50 -0
  78. package/dist/Instructions.js.map +1 -0
  79. package/dist/Memory.d.ts +190 -0
  80. package/dist/Memory.d.ts.map +1 -0
  81. package/dist/Memory.js +216 -0
  82. package/dist/Memory.js.map +1 -0
  83. package/dist/RunId.d.ts +41 -0
  84. package/dist/RunId.d.ts.map +1 -0
  85. package/dist/RunId.js +46 -0
  86. package/dist/RunId.js.map +1 -0
  87. package/dist/Skill.d.ts +72 -0
  88. package/dist/Skill.d.ts.map +1 -0
  89. package/dist/Skill.js +58 -0
  90. package/dist/Skill.js.map +1 -0
  91. package/dist/Specialist.d.ts +267 -0
  92. package/dist/Specialist.d.ts.map +1 -0
  93. package/dist/Specialist.js +119 -0
  94. package/dist/Specialist.js.map +1 -0
  95. package/dist/ToolDiscovery.d.ts +108 -0
  96. package/dist/ToolDiscovery.d.ts.map +1 -0
  97. package/dist/ToolDiscovery.js +139 -0
  98. package/dist/ToolDiscovery.js.map +1 -0
  99. package/dist/ToolFailure.d.ts +66 -0
  100. package/dist/ToolFailure.d.ts.map +1 -0
  101. package/dist/ToolFailure.js +79 -0
  102. package/dist/ToolFailure.js.map +1 -0
  103. package/dist/_virtual/_rolldown/runtime.js +13 -0
  104. package/dist/index.d.ts +26 -0
  105. package/dist/index.js +26 -0
  106. package/dist/internal/chat.js +202 -0
  107. package/dist/internal/chat.js.map +1 -0
  108. package/dist/internal/decisions.js +66 -0
  109. package/dist/internal/decisions.js.map +1 -0
  110. package/dist/internal/events.d.ts +46 -0
  111. package/dist/internal/events.d.ts.map +1 -0
  112. package/dist/internal/events.js +65 -0
  113. package/dist/internal/events.js.map +1 -0
  114. package/dist/internal/history.js +185 -0
  115. package/dist/internal/history.js.map +1 -0
  116. package/dist/internal/instructions.js +11 -0
  117. package/dist/internal/instructions.js.map +1 -0
  118. package/dist/internal/loop.d.ts +73 -0
  119. package/dist/internal/loop.d.ts.map +1 -0
  120. package/dist/internal/loop.js +166 -0
  121. package/dist/internal/loop.js.map +1 -0
  122. package/dist/internal/schema.js +14 -0
  123. package/dist/internal/schema.js.map +1 -0
  124. package/dist/internal/skills.js +28 -0
  125. package/dist/internal/skills.js.map +1 -0
  126. package/dist/internal/specialists.js +273 -0
  127. package/dist/internal/specialists.js.map +1 -0
  128. package/dist/internal/telemetry.js +85 -0
  129. package/dist/internal/telemetry.js.map +1 -0
  130. package/dist/internal/toolkit.d.ts +76 -0
  131. package/dist/internal/toolkit.d.ts.map +1 -0
  132. package/dist/internal/toolkit.js +129 -0
  133. package/dist/internal/toolkit.js.map +1 -0
  134. package/dist/internal/tools.js +132 -0
  135. package/dist/internal/tools.js.map +1 -0
  136. package/dist/internal/turns.d.ts +64 -0
  137. package/dist/internal/turns.d.ts.map +1 -0
  138. package/dist/internal/turns.js +221 -0
  139. package/dist/internal/turns.js.map +1 -0
  140. package/dist/internal/uuid.js +45 -0
  141. package/dist/internal/uuid.js.map +1 -0
  142. package/dist/internal/writer.d.ts +12 -0
  143. package/dist/internal/writer.d.ts.map +1 -0
  144. package/package.json +51 -3
  145. package/src/AppEvent.ts +134 -0
  146. package/src/Assistant.ts +589 -0
  147. package/src/AssistantActivity.ts +150 -0
  148. package/src/AssistantEngine.ts +343 -0
  149. package/src/AssistantEvent.ts +635 -0
  150. package/src/AssistantLoop.ts +207 -0
  151. package/src/AssistantToolCall.ts +40 -0
  152. package/src/Block.ts +84 -0
  153. package/src/BlockKit.ts +311 -0
  154. package/src/ChatId.ts +36 -0
  155. package/src/ChatLog.ts +247 -0
  156. package/src/ChatView.ts +658 -0
  157. package/src/Chats.ts +122 -0
  158. package/src/Compaction.ts +224 -0
  159. package/src/CurrentChat.ts +47 -0
  160. package/src/Decision.ts +241 -0
  161. package/src/DecisionId.ts +46 -0
  162. package/src/InputId.ts +43 -0
  163. package/src/Instructions.ts +72 -0
  164. package/src/Memory.ts +276 -0
  165. package/src/RunId.ts +46 -0
  166. package/src/Skill.ts +91 -0
  167. package/src/Specialist.ts +355 -0
  168. package/src/ToolDiscovery.ts +187 -0
  169. package/src/ToolFailure.ts +74 -0
  170. package/src/index.ts +130 -0
  171. package/src/internal/chat.ts +372 -0
  172. package/src/internal/decisions.ts +131 -0
  173. package/src/internal/events.ts +100 -0
  174. package/src/internal/history.ts +366 -0
  175. package/src/internal/instructions.ts +9 -0
  176. package/src/internal/loop.ts +344 -0
  177. package/src/internal/schema.ts +25 -0
  178. package/src/internal/skills.ts +37 -0
  179. package/src/internal/specialists.ts +414 -0
  180. package/src/internal/telemetry.ts +125 -0
  181. package/src/internal/toolkit.ts +231 -0
  182. package/src/internal/tools.ts +210 -0
  183. package/src/internal/turns.ts +429 -0
  184. package/src/internal/uuid.ts +50 -0
  185. package/src/internal/writer.ts +12 -0
@@ -0,0 +1,177 @@
1
+ import { __exportAll } from "./_virtual/_rolldown/runtime.js";
2
+ import { ChatId } from "./ChatId.js";
3
+ import { AssistantEvent, Entry, StoredAppEvent, isTurnEnd } from "./AssistantEvent.js";
4
+ import * as Predicate from "effect/Predicate";
5
+ import * as Context from "effect/Context";
6
+ import * as Effect from "effect/Effect";
7
+ import * as Layer from "effect/Layer";
8
+ import * as Schema from "effect/Schema";
9
+ import * as Clock from "effect/Clock";
10
+ import * as Ref from "effect/Ref";
11
+ import * as SqlClient from "effect/sql/SqlClient";
12
+ //#region src/ChatLog.ts
13
+ /**
14
+ * This module provides the `ChatLog` service, which stores each chat as an append-only list of
15
+ * `AssistantEvent`s.
16
+ *
17
+ * A log is built on a {@link Storage}. {@link layerMemory} keeps every chat in memory, for tests
18
+ * and local runs, and {@link layerSql} stores chats in a SQL table so they survive restarts. To
19
+ * use another store, implement a `Storage` and pass it to {@link make}.
20
+ *
21
+ * @since 0.1.0
22
+ */
23
+ var ChatLog_exports = /* @__PURE__ */ __exportAll({
24
+ ChatLog: () => ChatLog,
25
+ layerMemory: () => layerMemory,
26
+ layerSql: () => layerSql,
27
+ make: () => make
28
+ });
29
+ /**
30
+ * Service that stores each chat's events as an ordered list of entries.
31
+ *
32
+ * @category services
33
+ * @since 0.1.0
34
+ */
35
+ var ChatLog = class extends Context.Service()("effect-assistant/ChatLog") {};
36
+ /**
37
+ * Creates a `ChatLog` from a {@link Storage}. `append` numbers new entries after the chat's last
38
+ * `seq` and stamps them with the current time.
39
+ *
40
+ * @category constructors
41
+ * @since 0.1.0
42
+ */
43
+ const make = (storage) => ChatLog.of({
44
+ read: storage.read,
45
+ append: Effect.fn("ChatLog.append")(function* (chatId, events) {
46
+ if (events.length === 0) return [];
47
+ const seq = yield* storage.lastSeq(chatId);
48
+ const at = yield* Clock.currentTimeMillis;
49
+ const entries = events.map((event, n) => Entry.make({
50
+ chatId,
51
+ seq: seq + n + 1,
52
+ at,
53
+ event
54
+ }));
55
+ yield* storage.write(chatId, entries);
56
+ return entries;
57
+ }),
58
+ unfinished: Effect.gen(function* () {
59
+ return (yield* storage.lastEntries).flatMap((entry) => isTurnEnd(entry.event) || Predicate.isTagged(entry.event, "ChatDeleted") ? [] : [entry.chatId]);
60
+ })
61
+ });
62
+ /**
63
+ * Layer that keeps every chat in memory. The log is lost when the process stops.
64
+ *
65
+ * @category layers
66
+ * @since 0.1.0
67
+ */
68
+ const layerMemory = Layer.effect(ChatLog, Effect.gen(function* () {
69
+ const chats = yield* Ref.make(/* @__PURE__ */ new Map());
70
+ return make({
71
+ read: Effect.fnUntraced(function* (chatId, options) {
72
+ return ((yield* Ref.get(chats)).get(chatId) ?? []).filter((entry) => entry.seq > (options?.after ?? 0));
73
+ }),
74
+ lastSeq: Effect.fnUntraced(function* (chatId) {
75
+ return (yield* Ref.get(chats)).get(chatId)?.at(-1)?.seq ?? 0;
76
+ }),
77
+ write: (chatId, entries) => Ref.update(chats, (all) => new Map(all).set(chatId, [...all.get(chatId) ?? [], ...entries])),
78
+ lastEntries: Effect.gen(function* () {
79
+ return [...(yield* Ref.get(chats)).values()].flatMap((entries) => entries.slice(-1));
80
+ })
81
+ });
82
+ }));
83
+ const EventJson = Schema.fromJsonString(Schema.Union([AssistantEvent, StoredAppEvent]));
84
+ const SqlNumber = Schema.Union([Schema.Finite, Schema.FiniteFromString]);
85
+ const Row = Schema.Struct({
86
+ chat_id: ChatId,
87
+ seq: SqlNumber,
88
+ at: SqlNumber,
89
+ event: EventJson
90
+ });
91
+ const decodeLastSeq = Schema.decodeUnknownEffect(Schema.Array(Schema.Struct({ seq: Schema.NullOr(SqlNumber) })));
92
+ /**
93
+ * Layer that stores chats in a SQL table named `chat_log`.
94
+ *
95
+ * **Details**
96
+ *
97
+ * The table must already exist. It has the columns `chat_id`, `seq`, `at`, and `event`, with the
98
+ * primary key `(chat_id, seq)`. The layer requires a `SqlClient`.
99
+ *
100
+ * **Example** (Storing chats in Postgres)
101
+ *
102
+ * ```ts
103
+ * import * as PgClient from "@effect/sql-pg/PgClient"
104
+ * import { ChatLog } from "effect-assistant"
105
+ * import * as Config from "effect/Config"
106
+ * import * as Layer from "effect/Layer"
107
+ *
108
+ * const ChatLogLive = ChatLog.layerSql.pipe(
109
+ * Layer.provide(PgClient.layerConfig({ url: Config.Redacted("DATABASE_URL") }))
110
+ * )
111
+ * ```
112
+ *
113
+ * The table, in Postgres:
114
+ *
115
+ * ```sql
116
+ * CREATE TABLE chat_log (
117
+ * chat_id TEXT NOT NULL,
118
+ * seq INTEGER NOT NULL,
119
+ * at DOUBLE PRECISION NOT NULL,
120
+ * event TEXT NOT NULL,
121
+ * PRIMARY KEY (chat_id, seq)
122
+ * );
123
+ * ```
124
+ *
125
+ * @category layers
126
+ * @since 0.1.0
127
+ */
128
+ const layerSql = Layer.effect(ChatLog, Effect.gen(function* () {
129
+ const sql = (yield* SqlClient.SqlClient).withoutTransforms();
130
+ const toEntries = Effect.fnUntraced(function* (rows) {
131
+ return (yield* Schema.decodeUnknownEffect(Schema.Array(Row))(rows)).map((row) => Entry.make({
132
+ chatId: row.chat_id,
133
+ seq: row.seq,
134
+ at: row.at,
135
+ event: row.event
136
+ }));
137
+ });
138
+ const encodeEvent = Schema.encodeEffect(EventJson);
139
+ return make({
140
+ read: Effect.fnUntraced(function* (chatId, options) {
141
+ const rows = yield* sql`
142
+ SELECT chat_id, seq, at, event FROM chat_log WHERE chat_id = ${chatId} AND seq > ${options?.after ?? 0} ORDER BY seq
143
+ `;
144
+ return yield* toEntries(rows);
145
+ }, Effect.orDie),
146
+ lastSeq: Effect.fnUntraced(function* (chatId) {
147
+ const result = yield* sql`
148
+ SELECT MAX(seq) AS seq FROM chat_log WHERE chat_id = ${chatId}
149
+ `;
150
+ return (yield* decodeLastSeq(result))[0]?.seq ?? 0;
151
+ }, Effect.orDie),
152
+ write: Effect.fnUntraced(function* (chatId, entries) {
153
+ const rows = yield* Effect.forEach(entries, Effect.fnUntraced(function* (entry) {
154
+ const event = yield* encodeEvent(entry.event);
155
+ return {
156
+ chat_id: chatId,
157
+ seq: entry.seq,
158
+ at: entry.at,
159
+ event
160
+ };
161
+ }));
162
+ yield* sql`INSERT INTO chat_log ${sql.insert(rows)}`;
163
+ }, Effect.orDie),
164
+ lastEntries: Effect.gen(function* () {
165
+ const rows = yield* sql`
166
+ SELECT e.chat_id, e.seq, e.at, e.event FROM chat_log e
167
+ JOIN (SELECT chat_id, MAX(seq) AS seq FROM chat_log GROUP BY chat_id) last
168
+ ON last.chat_id = e.chat_id AND last.seq = e.seq
169
+ `;
170
+ return yield* toEntries(rows);
171
+ }).pipe(Effect.orDie)
172
+ });
173
+ }));
174
+ //#endregion
175
+ export { ChatLog, ChatLog_exports, layerMemory, layerSql, make };
176
+
177
+ //# sourceMappingURL=ChatLog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatLog.js","names":["AssistantEvent.isTurnEnd","AssistantEvent.AssistantEvent","AssistantEvent.StoredAppEvent"],"sources":["../src/ChatLog.ts"],"sourcesContent":["/**\n * This module provides the `ChatLog` service, which stores each chat as an append-only list of\n * `AssistantEvent`s.\n *\n * A log is built on a {@link Storage}. {@link layerMemory} keeps every chat in memory, for tests\n * and local runs, and {@link layerSql} stores chats in a SQL table so they survive restarts. To\n * use another store, implement a `Storage` and pass it to {@link make}.\n *\n * @since 0.1.0\n */\nimport * as Clock from \"effect/Clock\"\nimport * as Context from \"effect/Context\"\nimport * as Effect from \"effect/Effect\"\nimport * as Layer from \"effect/Layer\"\nimport * as Predicate from \"effect/Predicate\"\nimport * as Ref from \"effect/Ref\"\nimport * as Schema from \"effect/Schema\"\nimport * as SqlClient from \"effect/sql/SqlClient\"\nimport * as AssistantEvent from \"./AssistantEvent.ts\"\nimport { ChatId } from \"./ChatId.ts\"\n\n/**\n * Service that stores each chat's events as an ordered list of entries.\n *\n * @category services\n * @since 0.1.0\n */\nexport class ChatLog extends Context.Service<\n ChatLog,\n {\n /** Appends events to a chat and returns them as stored entries. */\n readonly append: (\n chatId: ChatId,\n events: ReadonlyArray<AssistantEvent.AssistantEvent | AssistantEvent.StoredAppEvent>\n ) => Effect.Effect<ReadonlyArray<AssistantEvent.Entry>>\n /** Reads a chat's stored entries in order, either all of them or only those after a `seq`. */\n readonly read: (chatId: ChatId, options?: ReadOptions) => Effect.Effect<ReadonlyArray<AssistantEvent.Entry>>\n /**\n * Returns the chats whose last entry ends neither a turn nor the chat. After a restart, these\n * are the chats that may still owe a turn.\n */\n readonly unfinished: Effect.Effect<ReadonlyArray<ChatId>>\n }\n>()(\"effect-assistant/ChatLog\") {}\n\n/**\n * Represents the options that select which of a chat's entries to read.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface ReadOptions {\n /** Reads only the entries after this `seq`. */\n readonly after?: number | undefined\n}\n\n/**\n * Represents the storage that a `ChatLog` is built on.\n *\n * A storage reads a chat's entries, finds a chat's last `seq`, writes new entries, and finds every\n * chat's last entry. Entries are never removed, so a deleted chat keeps its log, which ends in\n * `ChatDeleted`.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface Storage {\n readonly read: (chatId: ChatId, options?: ReadOptions) => Effect.Effect<ReadonlyArray<AssistantEvent.Entry>>\n readonly lastSeq: (chatId: ChatId) => Effect.Effect<number>\n readonly write: (chatId: ChatId, entries: ReadonlyArray<AssistantEvent.Entry>) => Effect.Effect<void>\n /** Returns the last entry of every chat. */\n readonly lastEntries: Effect.Effect<ReadonlyArray<AssistantEvent.Entry>>\n}\n\n/**\n * Creates a `ChatLog` from a {@link Storage}. `append` numbers new entries after the chat's last\n * `seq` and stamps them with the current time.\n *\n * @category constructors\n * @since 0.1.0\n */\nexport const make = (storage: Storage): ChatLog[\"Service\"] =>\n ChatLog.of({\n read: storage.read,\n append: Effect.fn(\"ChatLog.append\")(function* (\n chatId: ChatId,\n events: ReadonlyArray<AssistantEvent.AssistantEvent | AssistantEvent.StoredAppEvent>\n ) {\n if (events.length === 0) return []\n\n const seq = yield* storage.lastSeq(chatId)\n const at = yield* Clock.currentTimeMillis\n const entries = events.map((event, n) => AssistantEvent.Entry.make({ chatId, seq: seq + n + 1, at, event }))\n\n yield* storage.write(chatId, entries)\n\n return entries\n }),\n unfinished: Effect.gen(function* () {\n const last = yield* storage.lastEntries\n\n // A deleted chat owes nothing.\n return last.flatMap((entry) =>\n AssistantEvent.isTurnEnd(entry.event) || Predicate.isTagged(entry.event, \"ChatDeleted\") ? [] : [entry.chatId]\n )\n })\n })\n\n/**\n * Layer that keeps every chat in memory. The log is lost when the process stops.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layerMemory: Layer.Layer<ChatLog> = Layer.effect(\n ChatLog,\n Effect.gen(function* () {\n const chats = yield* Ref.make(new Map<ChatId, ReadonlyArray<AssistantEvent.Entry>>())\n\n return make({\n read: Effect.fnUntraced(function* (chatId: ChatId, options?: ReadOptions) {\n const all = yield* Ref.get(chats)\n\n return (all.get(chatId) ?? []).filter((entry) => entry.seq > (options?.after ?? 0))\n }),\n lastSeq: Effect.fnUntraced(function* (chatId: ChatId) {\n const all = yield* Ref.get(chats)\n\n return all.get(chatId)?.at(-1)?.seq ?? 0\n }),\n write: (chatId, entries) =>\n Ref.update(chats, (all) => new Map(all).set(chatId, [...(all.get(chatId) ?? []), ...entries])),\n lastEntries: Effect.gen(function* () {\n const all = yield* Ref.get(chats)\n\n return [...all.values()].flatMap((entries) => entries.slice(-1))\n })\n })\n })\n)\n\nconst EventJson = Schema.fromJsonString(Schema.Union([AssistantEvent.AssistantEvent, AssistantEvent.StoredAppEvent]))\n\n// Postgres returns BIGINT and DOUBLE PRECISION columns as strings; other databases as numbers.\nconst SqlNumber = Schema.Union([Schema.Finite, Schema.FiniteFromString])\n\nconst Row = Schema.Struct({ chat_id: ChatId, seq: SqlNumber, at: SqlNumber, event: EventJson })\n\nconst decodeLastSeq = Schema.decodeUnknownEffect(Schema.Array(Schema.Struct({ seq: Schema.NullOr(SqlNumber) })))\n\n/**\n * Layer that stores chats in a SQL table named `chat_log`.\n *\n * **Details**\n *\n * The table must already exist. It has the columns `chat_id`, `seq`, `at`, and `event`, with the\n * primary key `(chat_id, seq)`. The layer requires a `SqlClient`.\n *\n * **Example** (Storing chats in Postgres)\n *\n * ```ts\n * import * as PgClient from \"@effect/sql-pg/PgClient\"\n * import { ChatLog } from \"effect-assistant\"\n * import * as Config from \"effect/Config\"\n * import * as Layer from \"effect/Layer\"\n *\n * const ChatLogLive = ChatLog.layerSql.pipe(\n * Layer.provide(PgClient.layerConfig({ url: Config.Redacted(\"DATABASE_URL\") }))\n * )\n * ```\n *\n * The table, in Postgres:\n *\n * ```sql\n * CREATE TABLE chat_log (\n * chat_id TEXT NOT NULL,\n * seq INTEGER NOT NULL,\n * at DOUBLE PRECISION NOT NULL,\n * event TEXT NOT NULL,\n * PRIMARY KEY (chat_id, seq)\n * );\n * ```\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layerSql: Layer.Layer<ChatLog, never, SqlClient.SqlClient> = Layer.effect(\n ChatLog,\n Effect.gen(function* () {\n const sql = (yield* SqlClient.SqlClient).withoutTransforms()\n\n const toEntries = Effect.fnUntraced(function* (rows: ReadonlyArray<unknown>) {\n const decoded = yield* Schema.decodeUnknownEffect(Schema.Array(Row))(rows)\n\n return decoded.map((row) =>\n AssistantEvent.Entry.make({\n chatId: row.chat_id,\n seq: row.seq,\n at: row.at,\n event: row.event\n })\n )\n })\n\n const encodeEvent = Schema.encodeEffect(EventJson)\n\n return make({\n read: Effect.fnUntraced(function* (chatId: ChatId, options?: ReadOptions) {\n const rows = yield* sql`\n SELECT chat_id, seq, at, event FROM chat_log WHERE chat_id = ${chatId} AND seq > ${options?.after ?? 0} ORDER BY seq\n `\n\n return yield* toEntries(rows)\n }, Effect.orDie),\n lastSeq: Effect.fnUntraced(function* (chatId: ChatId) {\n const result = yield* sql`\n SELECT MAX(seq) AS seq FROM chat_log WHERE chat_id = ${chatId}\n `\n\n const rows = yield* decodeLastSeq(result)\n\n return rows[0]?.seq ?? 0\n }, Effect.orDie),\n write: Effect.fnUntraced(function* (chatId: ChatId, entries: ReadonlyArray<AssistantEvent.Entry>) {\n const rows = yield* Effect.forEach(\n entries,\n Effect.fnUntraced(function* (entry: AssistantEvent.Entry) {\n const event = yield* encodeEvent(entry.event)\n\n return { chat_id: chatId, seq: entry.seq, at: entry.at, event }\n })\n )\n\n yield* sql`INSERT INTO chat_log ${sql.insert(rows)}`\n }, Effect.orDie),\n lastEntries: Effect.gen(function* () {\n const rows = yield* sql`\n SELECT e.chat_id, e.seq, e.at, e.event FROM chat_log e\n JOIN (SELECT chat_id, MAX(seq) AS seq FROM chat_log GROUP BY chat_id) last\n ON last.chat_id = e.chat_id AND last.seq = e.seq\n `\n\n return yield* toEntries(rows)\n }).pipe(Effect.orDie)\n })\n })\n)\n"],"mappings":"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"}
@@ -0,0 +1,459 @@
1
+ import { AnyEntry, AssistantEvent_d_exports } from "./AssistantEvent.js";
2
+ import * as Schema from "effect/Schema";
3
+ import * as Stream from "effect/Stream";
4
+ declare namespace ChatView_d_exports {
5
+ export { Activity, AssistantMessage, Block, BlockStatus, Definition, Message, Options, PendingDecision, SpecialistWorking, Update, UpdatesOptions, UserMessage, View, apply, empty, fromEntries, isIdle, updates };
6
+ }
7
+ /**
8
+ * Schema for a decision as the user sees it on a decision block. It holds the call the decision is
9
+ * for (its tool and params, which the app can display as it likes) and, once the user has
10
+ * decided, the outcome.
11
+ *
12
+ * @category schemas
13
+ * @since 0.1.0
14
+ */
15
+ export declare const PendingDecision: Schema.Struct<{
16
+ readonly id: Schema.brand<Schema.TemplateLiteral<readonly ["decision:", Schema.String]>, "~effect-assistant/DecisionId">;
17
+ readonly tool: Schema.String;
18
+ readonly params: Schema.Codec<Schema.Json, Schema.Json, never, never>;
19
+ readonly outcome: Schema.optional<Schema.Literals<readonly ["approved", "declined"]>>;
20
+ }>;
21
+ /**
22
+ * Represents a decision as the user sees it.
23
+ *
24
+ * @category models
25
+ * @since 0.1.0
26
+ */
27
+ export type PendingDecision = typeof PendingDecision.Type;
28
+ /**
29
+ * Schema for the status of a block. A block is `"open"` while it accepts an answer, `"answered"`
30
+ * once the user has used it, and `"closed"` when it closed without an answer because the user
31
+ * answered in words or moved on.
32
+ *
33
+ * @category schemas
34
+ * @since 0.1.0
35
+ */
36
+ export declare const BlockStatus: Schema.Literals<readonly ["open", "answered", "closed"]>;
37
+ /**
38
+ * The status of a block.
39
+ *
40
+ * @category models
41
+ * @since 0.1.0
42
+ */
43
+ export type BlockStatus = typeof BlockStatus.Type;
44
+ /**
45
+ * Schema for a block on an assistant's message. It holds the block's kind, its props (JSON, as
46
+ * the block's props schema encodes them), its status, the user's answer (JSON, as the block's
47
+ * answer schema encodes it), and, for a decision block, its decisions.
48
+ *
49
+ * @category schemas
50
+ * @since 0.1.0
51
+ */
52
+ export declare const Block: Schema.Struct<{
53
+ readonly kind: Schema.String;
54
+ readonly props: Schema.Codec<Schema.Json, Schema.Json, never, never>;
55
+ readonly status: Schema.Literals<readonly ["open", "answered", "closed"]>;
56
+ readonly answer: Schema.optional<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
57
+ readonly decisions: Schema.optional<Schema.$Array<Schema.Struct<{
58
+ readonly id: Schema.brand<Schema.TemplateLiteral<readonly ["decision:", Schema.String]>, "~effect-assistant/DecisionId">;
59
+ readonly tool: Schema.String;
60
+ readonly params: Schema.Codec<Schema.Json, Schema.Json, never, never>;
61
+ readonly outcome: Schema.optional<Schema.Literals<readonly ["approved", "declined"]>>;
62
+ }>>>;
63
+ }>;
64
+ /**
65
+ * Represents a block on an assistant's message.
66
+ *
67
+ * @category models
68
+ * @since 0.1.0
69
+ */
70
+ export type Block = typeof Block.Type;
71
+ /**
72
+ * Schema for a message the user sent. `at` is when it was stored, in epoch
73
+ * milliseconds.
74
+ *
75
+ * @category schemas
76
+ * @since 0.1.0
77
+ */
78
+ export declare const UserMessage: Schema.TaggedStruct<"UserMessage", {
79
+ readonly id: Schema.brand<Schema.String, "~effect-assistant/InputId">;
80
+ readonly text: Schema.String;
81
+ readonly attachments: Schema.optional<Schema.$Array<Schema.Struct<{
82
+ readonly fileId: Schema.String;
83
+ readonly name: Schema.String;
84
+ readonly mediaType: Schema.String;
85
+ readonly size: Schema.Finite;
86
+ }>>>;
87
+ readonly at: Schema.Finite;
88
+ }>;
89
+ /**
90
+ * Represents a message the user sent.
91
+ *
92
+ * @category models
93
+ * @since 0.1.0
94
+ */
95
+ export type UserMessage = typeof UserMessage.Type;
96
+ /**
97
+ * Schema for a message the assistant sent the user, including its block if it has one.
98
+ *
99
+ * @category schemas
100
+ * @since 0.1.0
101
+ */
102
+ export declare const AssistantMessage: Schema.TaggedStruct<"AssistantMessage", {
103
+ readonly id: Schema.String;
104
+ readonly text: Schema.String;
105
+ readonly at: Schema.Finite;
106
+ readonly block: Schema.optional<Schema.Struct<{
107
+ readonly kind: Schema.String;
108
+ readonly props: Schema.Codec<Schema.Json, Schema.Json, never, never>;
109
+ readonly status: Schema.Literals<readonly ["open", "answered", "closed"]>;
110
+ readonly answer: Schema.optional<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
111
+ readonly decisions: Schema.optional<Schema.$Array<Schema.Struct<{
112
+ readonly id: Schema.brand<Schema.TemplateLiteral<readonly ["decision:", Schema.String]>, "~effect-assistant/DecisionId">;
113
+ readonly tool: Schema.String;
114
+ readonly params: Schema.Codec<Schema.Json, Schema.Json, never, never>;
115
+ readonly outcome: Schema.optional<Schema.Literals<readonly ["approved", "declined"]>>;
116
+ }>>>;
117
+ }>>;
118
+ }>;
119
+ /**
120
+ * Represents a message the assistant sent the user.
121
+ *
122
+ * @category models
123
+ * @since 0.1.0
124
+ */
125
+ export type AssistantMessage = typeof AssistantMessage.Type;
126
+ /**
127
+ * Schema for a message in the chat, sent by either the user or the assistant.
128
+ *
129
+ * @category schemas
130
+ * @since 0.1.0
131
+ */
132
+ export declare const Message: Schema.Union<readonly [Schema.TaggedStruct<"UserMessage", {
133
+ readonly id: Schema.brand<Schema.String, "~effect-assistant/InputId">;
134
+ readonly text: Schema.String;
135
+ readonly attachments: Schema.optional<Schema.$Array<Schema.Struct<{
136
+ readonly fileId: Schema.String;
137
+ readonly name: Schema.String;
138
+ readonly mediaType: Schema.String;
139
+ readonly size: Schema.Finite;
140
+ }>>>;
141
+ readonly at: Schema.Finite;
142
+ }>, Schema.TaggedStruct<"AssistantMessage", {
143
+ readonly id: Schema.String;
144
+ readonly text: Schema.String;
145
+ readonly at: Schema.Finite;
146
+ readonly block: Schema.optional<Schema.Struct<{
147
+ readonly kind: Schema.String;
148
+ readonly props: Schema.Codec<Schema.Json, Schema.Json, never, never>;
149
+ readonly status: Schema.Literals<readonly ["open", "answered", "closed"]>;
150
+ readonly answer: Schema.optional<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
151
+ readonly decisions: Schema.optional<Schema.$Array<Schema.Struct<{
152
+ readonly id: Schema.brand<Schema.TemplateLiteral<readonly ["decision:", Schema.String]>, "~effect-assistant/DecisionId">;
153
+ readonly tool: Schema.String;
154
+ readonly params: Schema.Codec<Schema.Json, Schema.Json, never, never>;
155
+ readonly outcome: Schema.optional<Schema.Literals<readonly ["approved", "declined"]>>;
156
+ }>>>;
157
+ }>>;
158
+ }>]>;
159
+ /**
160
+ * Represents a message in the chat.
161
+ *
162
+ * @category models
163
+ * @since 0.1.0
164
+ */
165
+ export type Message = typeof Message.Type;
166
+ /**
167
+ * Schema for a specialist that is working for the assistant. It holds the specialist's name and
168
+ * when it started, in epoch milliseconds.
169
+ *
170
+ * @category schemas
171
+ * @since 0.1.0
172
+ */
173
+ export declare const SpecialistWorking: Schema.Struct<{
174
+ readonly specialist: Schema.String;
175
+ readonly since: Schema.Finite;
176
+ }>;
177
+ /**
178
+ * Represents a specialist that is working for the assistant.
179
+ *
180
+ * @category models
181
+ * @since 0.1.0
182
+ */
183
+ export type SpecialistWorking = typeof SpecialistWorking.Type;
184
+ /**
185
+ * Schema for what is working in the chat.
186
+ *
187
+ * `working` is true while the assistant runs a turn, or when a turn is about to start for
188
+ * something that arrived. `specialists` lists the specialists that are working. When the chat is
189
+ * idle, nothing is working, even if a workflow is waiting on a timer or a decision.
190
+ *
191
+ * @category schemas
192
+ * @since 0.1.0
193
+ */
194
+ export declare const Activity: Schema.Struct<{
195
+ readonly working: Schema.Boolean;
196
+ readonly specialists: Schema.$Array<Schema.Struct<{
197
+ readonly specialist: Schema.String;
198
+ readonly since: Schema.Finite;
199
+ }>>;
200
+ }>;
201
+ /**
202
+ * Represents what is working in the chat.
203
+ *
204
+ * @category models
205
+ * @since 0.1.0
206
+ */
207
+ export type Activity = typeof Activity.Type;
208
+ /**
209
+ * Schema for a chat as its user sees it. It holds the chat's messages, what is working in it,
210
+ * whether the last turn failed, and whether the chat was deleted. `seq` is the position in the
211
+ * chat's log that the view is up to.
212
+ *
213
+ * @category schemas
214
+ * @since 0.1.0
215
+ */
216
+ export declare const View: Schema.Struct<{
217
+ readonly seq: Schema.Int;
218
+ readonly messages: Schema.$Array<Schema.Union<readonly [Schema.TaggedStruct<"UserMessage", {
219
+ readonly id: Schema.brand<Schema.String, "~effect-assistant/InputId">;
220
+ readonly text: Schema.String;
221
+ readonly attachments: Schema.optional<Schema.$Array<Schema.Struct<{
222
+ readonly fileId: Schema.String;
223
+ readonly name: Schema.String;
224
+ readonly mediaType: Schema.String;
225
+ readonly size: Schema.Finite;
226
+ }>>>;
227
+ readonly at: Schema.Finite;
228
+ }>, Schema.TaggedStruct<"AssistantMessage", {
229
+ readonly id: Schema.String;
230
+ readonly text: Schema.String;
231
+ readonly at: Schema.Finite;
232
+ readonly block: Schema.optional<Schema.Struct<{
233
+ readonly kind: Schema.String;
234
+ readonly props: Schema.Codec<Schema.Json, Schema.Json, never, never>;
235
+ readonly status: Schema.Literals<readonly ["open", "answered", "closed"]>;
236
+ readonly answer: Schema.optional<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
237
+ readonly decisions: Schema.optional<Schema.$Array<Schema.Struct<{
238
+ readonly id: Schema.brand<Schema.TemplateLiteral<readonly ["decision:", Schema.String]>, "~effect-assistant/DecisionId">;
239
+ readonly tool: Schema.String;
240
+ readonly params: Schema.Codec<Schema.Json, Schema.Json, never, never>;
241
+ readonly outcome: Schema.optional<Schema.Literals<readonly ["approved", "declined"]>>;
242
+ }>>>;
243
+ }>>;
244
+ }>]>>;
245
+ readonly activity: Schema.Struct<{
246
+ readonly working: Schema.Boolean;
247
+ readonly specialists: Schema.$Array<Schema.Struct<{
248
+ readonly specialist: Schema.String;
249
+ readonly since: Schema.Finite;
250
+ }>>;
251
+ }>;
252
+ /** Whether the last turn failed with no message after it. */
253
+ readonly failed: Schema.Boolean;
254
+ /** Whether the chat was deleted. A deleted chat accepts nothing more, and its stream ends. */
255
+ readonly deleted: Schema.Boolean;
256
+ }>;
257
+ /**
258
+ * Represents a chat as its user sees it.
259
+ *
260
+ * @category models
261
+ * @since 0.1.0
262
+ */
263
+ export type View = typeof View.Type;
264
+ /**
265
+ * Schema for a change to a chat's view.
266
+ *
267
+ * An update holds each message that is new or changed (for example, a block answered or closed,
268
+ * or a decision made), in full. It also holds the current activity, whether the last turn failed,
269
+ * and whether the chat was deleted. `seq` is the position in the chat's log that the update is up
270
+ * to.
271
+ *
272
+ * @category schemas
273
+ * @since 0.1.0
274
+ */
275
+ export declare const Update: Schema.Struct<{
276
+ readonly seq: Schema.Int;
277
+ readonly messages: Schema.$Array<Schema.Union<readonly [Schema.TaggedStruct<"UserMessage", {
278
+ readonly id: Schema.brand<Schema.String, "~effect-assistant/InputId">;
279
+ readonly text: Schema.String;
280
+ readonly attachments: Schema.optional<Schema.$Array<Schema.Struct<{
281
+ readonly fileId: Schema.String;
282
+ readonly name: Schema.String;
283
+ readonly mediaType: Schema.String;
284
+ readonly size: Schema.Finite;
285
+ }>>>;
286
+ readonly at: Schema.Finite;
287
+ }>, Schema.TaggedStruct<"AssistantMessage", {
288
+ readonly id: Schema.String;
289
+ readonly text: Schema.String;
290
+ readonly at: Schema.Finite;
291
+ readonly block: Schema.optional<Schema.Struct<{
292
+ readonly kind: Schema.String;
293
+ readonly props: Schema.Codec<Schema.Json, Schema.Json, never, never>;
294
+ readonly status: Schema.Literals<readonly ["open", "answered", "closed"]>;
295
+ readonly answer: Schema.optional<Schema.Codec<Schema.Json, Schema.Json, never, never>>;
296
+ readonly decisions: Schema.optional<Schema.$Array<Schema.Struct<{
297
+ readonly id: Schema.brand<Schema.TemplateLiteral<readonly ["decision:", Schema.String]>, "~effect-assistant/DecisionId">;
298
+ readonly tool: Schema.String;
299
+ readonly params: Schema.Codec<Schema.Json, Schema.Json, never, never>;
300
+ readonly outcome: Schema.optional<Schema.Literals<readonly ["approved", "declined"]>>;
301
+ }>>>;
302
+ }>>;
303
+ }>]>>;
304
+ readonly activity: Schema.Struct<{
305
+ readonly working: Schema.Boolean;
306
+ readonly specialists: Schema.$Array<Schema.Struct<{
307
+ readonly specialist: Schema.String;
308
+ readonly since: Schema.Finite;
309
+ }>>;
310
+ }>;
311
+ readonly failed: Schema.Boolean;
312
+ readonly deleted: Schema.Boolean;
313
+ }>;
314
+ /**
315
+ * Represents a change to a chat's view.
316
+ *
317
+ * @category models
318
+ * @since 0.1.0
319
+ */
320
+ export type Update = typeof Update.Type;
321
+ /**
322
+ * The view of a chat before anything has happened in it, with no messages and nothing working.
323
+ * Use it as the starting point when folding updates with {@link apply}.
324
+ *
325
+ * @category constructors
326
+ * @since 0.1.0
327
+ */
328
+ export declare const empty: View;
329
+ /**
330
+ * Returns the view after applying an update.
331
+ *
332
+ * **Details**
333
+ *
334
+ * Each message in the update replaces the message with the same id, in place, and new messages
335
+ * are added after the rest. The update's activity, `failed`, and `deleted` replace the view's.
336
+ * Applying an update twice changes nothing, so a client that resubscribes from the start can fold
337
+ * again and get the same view.
338
+ *
339
+ * @category combinators
340
+ * @since 0.1.0
341
+ */
342
+ export declare const apply: (view: View, update: Update) => View;
343
+ /**
344
+ * Returns whether nothing is working in the chat: no turn is running or about to start, and no
345
+ * specialist is working.
346
+ *
347
+ * @category guards
348
+ * @since 0.1.0
349
+ */
350
+ export declare const isIdle: (view: View) => boolean;
351
+ /**
352
+ * Represents the options for building a view from a chat's log.
353
+ *
354
+ * @category models
355
+ * @since 0.1.0
356
+ */
357
+ export interface Options {
358
+ /**
359
+ * The tags of the app's events that wake a turn. After such an event, the view shows a turn as
360
+ * about to start. Defaults to none.
361
+ */
362
+ readonly wakes?: ReadonlySet<string> | undefined;
363
+ }
364
+ /**
365
+ * Returns a chat's view, built from the entries of its log.
366
+ *
367
+ * **When to use**
368
+ *
369
+ * Use it in server-side code, for example to return a whole chat at once. A client gets the view
370
+ * as a stream from {@link updates} instead.
371
+ *
372
+ * **Example** (Building a chat's view from its log)
373
+ *
374
+ * ```ts
375
+ * import { type ChatId, ChatLog, ChatView } from "effect-assistant"
376
+ * import * as Effect from "effect/Effect"
377
+ *
378
+ * const viewOf = (chatId: ChatId.ChatId) =>
379
+ * ChatLog.ChatLog.use((log) => log.read(chatId)).pipe(
380
+ * Effect.map((entries) => ChatView.fromEntries(entries))
381
+ * )
382
+ * ```
383
+ *
384
+ * @see {@link updates} for a stream of changes instead of a whole view
385
+ *
386
+ * @category constructors
387
+ * @since 0.1.0
388
+ */
389
+ export declare const fromEntries: (entries: ReadonlyArray<AnyEntry>, options?: Options) => View;
390
+ /**
391
+ * Represents an assistant's definition as {@link updates} reads it: the app's events it
392
+ * registered, and whether each one wakes a turn. An assistant made with `Assistant.make` fits it.
393
+ *
394
+ * @category models
395
+ * @since 0.1.0
396
+ */
397
+ export interface Definition {
398
+ readonly events: ReadonlyArray<{
399
+ readonly tag: string;
400
+ readonly wakes: boolean;
401
+ }>;
402
+ }
403
+ /**
404
+ * Represents the options accepted by {@link updates}.
405
+ *
406
+ * @category models
407
+ * @since 0.1.0
408
+ */
409
+ export interface UpdatesOptions {
410
+ /**
411
+ * The `seq` up to which the client already has the view. The result contains only the updates
412
+ * after it. Defaults to `0`, every update.
413
+ */
414
+ readonly after?: number | undefined;
415
+ }
416
+ /**
417
+ * Returns the stream of view updates that a chat's entries produce, for a chat client.
418
+ *
419
+ * **Details**
420
+ *
421
+ * The entries must stream from the start of the chat's log, as `assistant.subscribe(chatId)` does
422
+ * without `after`. The fold reads every entry, because a message's view depends on earlier
423
+ * entries: a block is answered or closed later, and a decision is made later. `after` only skips
424
+ * emitting the updates up to that `seq`, so a client that has the view up to a `seq` receives only
425
+ * what changed since. Pass `after` here, not to `subscribe`: a subscription that starts after a
426
+ * `seq` gives the fold an incomplete log, and the updates it produces are wrong.
427
+ *
428
+ * The definition tells the fold which of the app's events wake a turn, so the view shows a turn as
429
+ * about to start after one. Each chunk of entries becomes one update; the stored entries, for
430
+ * example, arrive as one chunk.
431
+ *
432
+ * **Example** (Streaming a chat's view to a client)
433
+ *
434
+ * ```ts
435
+ * import { Assistant, type ChatId, ChatView } from "effect-assistant"
436
+ * import * as Stream from "effect/Stream"
437
+ *
438
+ * const MyAssistant = Assistant.make("my-assistant")
439
+ *
440
+ * const watch = (chatId: ChatId.ChatId, after: number) =>
441
+ * Stream.unwrap(
442
+ * MyAssistant.useSync((assistant) =>
443
+ * assistant.subscribe(chatId).pipe(ChatView.updates(MyAssistant, { after }))
444
+ * )
445
+ * )
446
+ * ```
447
+ *
448
+ * @see {@link apply} for folding the updates into a view on the client
449
+ *
450
+ * @category utils
451
+ * @since 0.1.0
452
+ */
453
+ export declare const updates: {
454
+ (definition: Definition, options?: UpdatesOptions): <E, R>(entries: Stream.Stream<AnyEntry, E, R>) => Stream.Stream<Update, E, R>;
455
+ <E, R>(entries: Stream.Stream<AnyEntry, E, R>, definition: Definition, options?: UpdatesOptions): Stream.Stream<Update, E, R>;
456
+ };
457
+ //#endregion
458
+ export { ChatView_d_exports };
459
+ //# sourceMappingURL=ChatView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatView.d.ts","names":[],"sources":["../src/ChatView.ts"],"mappings":";;;;;;;;;;;;;;qBAkCa,iBAAe,OAAA;;;;;;;;;;;;YAahB,yBAAyB,gBAAgB;;;;;;;;;qBAUxC,aAAW,OAAA;;;;;;;YAQZ,qBAAqB,YAAY;;;;;;;;;qBAUhC,OAAK,OAAA;;;;;;;;;;;;;;;;;;YAcN,eAAe,MAAM;;;;;;;;qBASpB,aAAW,OAAA;;;;;;;;;;;;;;;;;YAaZ,qBAAqB,YAAY;;;;;;;qBAQhC,kBAAgB,OAAA;;;;;;;;;;;;;;;;;;;;;;;YAajB,0BAA0B,iBAAiB;;;;;;;qBAQ1C,SAAO,OAAA,gBAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAQR,iBAAiB,QAAQ;;;;;;;;qBASxB,mBAAiB,OAAA;;;;;;;;;;YAWlB,2BAA2B,kBAAkB;;;;;;;;;;;qBAY5C,UAAQ,OAAA;;;;;;;;;;;;;YAWT,kBAAkB,SAAS;;;;;;;;;qBAU1B,MAAI,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAgBL,cAAc,KAAK;;;;;;;;;;;;qBAalB,QAAM,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAcP,gBAAgB,OAAO;;;;;;;;qBAStB,OAAO;;;;;;;;;;;;;;qBAqBP,QAAK,MAAU,MAAI,QAAU,WAAS;;;;;;;;qBAuBtC,SAAM,MAAU;;;;;;;iBAQZ;;;;;WAKN,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA4BN,cAAW,SAAa,cAAc,WAAwB,UAAY,YAAU;;;;;;;;iBAgOhF;WACN,QAAQ;aAAyB;aAAsB;;;;;;;;;iBASjD;;;;;WAKN;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA2CE;GAET,YAAY,YACZ,UAAU,kBACR,GAAG,GAAG,SAAS,OAAO,OAAO,UAAyB,GAAG,OAAO,OAAO,OAAO,QAAQ,GAAG;GAC5F,GAAG,GACF,SAAS,OAAO,OAAO,UAAyB,GAAG,IACnD,YAAY,YACZ,UAAU,iBACT,OAAO,OAAO,QAAQ,GAAG"}