@agentshouse/mdruntime 0.1.0-cbb8d8acc9d19 → 0.1.0-cc56db96f86ae

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 (223) hide show
  1. package/AGENTS.md +285 -225
  2. package/README.md +21 -10
  3. package/dist/adapter.d.ts +114 -26
  4. package/dist/adapter.d.ts.map +1 -1
  5. package/dist/attempt.d.ts +16 -0
  6. package/dist/attempt.d.ts.map +1 -0
  7. package/dist/attempt.js +70 -0
  8. package/dist/binary.d.ts +23 -0
  9. package/dist/binary.d.ts.map +1 -0
  10. package/dist/binary.js +8 -0
  11. package/dist/bootstrap.d.ts +18 -5
  12. package/dist/bootstrap.d.ts.map +1 -1
  13. package/dist/bootstrap.js +147 -40
  14. package/dist/change-set.d.ts +7 -2
  15. package/dist/change-set.d.ts.map +1 -1
  16. package/dist/change-set.js +50 -16
  17. package/dist/change.d.ts +45 -0
  18. package/dist/change.d.ts.map +1 -1
  19. package/dist/change.js +34 -0
  20. package/dist/chat/acts.d.ts +33 -0
  21. package/dist/chat/acts.d.ts.map +1 -0
  22. package/dist/chat/acts.js +36 -0
  23. package/dist/chat/context.d.ts +3 -0
  24. package/dist/chat/context.d.ts.map +1 -0
  25. package/dist/chat/context.js +10 -0
  26. package/dist/chat/earlier.d.ts +10 -0
  27. package/dist/chat/earlier.d.ts.map +1 -0
  28. package/dist/chat/earlier.js +54 -0
  29. package/dist/chat/house.d.ts +25 -0
  30. package/dist/chat/house.d.ts.map +1 -0
  31. package/dist/chat/house.js +38 -0
  32. package/dist/chat/input.d.ts +11 -0
  33. package/dist/chat/input.d.ts.map +1 -0
  34. package/dist/chat/input.js +92 -0
  35. package/dist/chat/kit.d.ts +61 -0
  36. package/dist/chat/kit.d.ts.map +1 -0
  37. package/dist/chat/kit.js +123 -0
  38. package/dist/chat/lasting.d.ts +6 -0
  39. package/dist/chat/lasting.d.ts.map +1 -0
  40. package/dist/chat/lasting.js +19 -0
  41. package/dist/chat/list.d.ts +28 -0
  42. package/dist/chat/list.d.ts.map +1 -0
  43. package/dist/chat/list.js +103 -0
  44. package/dist/chat/markdown.d.ts +21 -0
  45. package/dist/chat/markdown.d.ts.map +1 -0
  46. package/dist/chat/markdown.js +246 -0
  47. package/dist/chat/message.d.ts +7 -0
  48. package/dist/chat/message.d.ts.map +1 -0
  49. package/dist/chat/message.js +237 -0
  50. package/dist/chat/messages.d.ts +29 -0
  51. package/dist/chat/messages.d.ts.map +1 -0
  52. package/dist/chat/messages.js +190 -0
  53. package/dist/chat/name.d.ts +7 -0
  54. package/dist/chat/name.d.ts.map +1 -0
  55. package/dist/chat/name.js +37 -0
  56. package/dist/chat/opened.d.ts +12 -0
  57. package/dist/chat/opened.d.ts.map +1 -0
  58. package/dist/chat/opened.js +19 -0
  59. package/dist/chat/overlay.d.ts +32 -0
  60. package/dist/chat/overlay.d.ts.map +1 -0
  61. package/dist/chat/overlay.js +103 -0
  62. package/dist/chat/parts.d.ts +58 -0
  63. package/dist/chat/parts.d.ts.map +1 -0
  64. package/dist/chat/parts.js +120 -0
  65. package/dist/chat/pieces.d.ts +62 -0
  66. package/dist/chat/pieces.d.ts.map +1 -0
  67. package/dist/chat/pieces.js +1110 -0
  68. package/dist/chat/process.d.ts +20 -0
  69. package/dist/chat/process.d.ts.map +1 -0
  70. package/dist/chat/process.js +32 -0
  71. package/dist/chat/provider.d.ts +68 -0
  72. package/dist/chat/provider.d.ts.map +1 -0
  73. package/dist/chat/provider.js +619 -0
  74. package/dist/chat/question.d.ts +9 -0
  75. package/dist/chat/question.d.ts.map +1 -0
  76. package/dist/chat/question.js +69 -0
  77. package/dist/chat/questions.d.ts +31 -0
  78. package/dist/chat/questions.d.ts.map +1 -0
  79. package/dist/chat/questions.js +111 -0
  80. package/dist/chat/read.d.ts +5 -0
  81. package/dist/chat/read.d.ts.map +1 -0
  82. package/dist/chat/read.js +13 -0
  83. package/dist/chat/ready.d.ts +37 -0
  84. package/dist/chat/ready.d.ts.map +1 -0
  85. package/dist/chat/ready.js +93 -0
  86. package/dist/chat/room-file.d.ts +8 -0
  87. package/dist/chat/room-file.d.ts.map +1 -0
  88. package/dist/chat/room-file.js +67 -0
  89. package/dist/chat/steady.d.ts +3 -0
  90. package/dist/chat/steady.d.ts.map +1 -0
  91. package/dist/chat/steady.js +26 -0
  92. package/dist/chat/style.d.ts +4 -0
  93. package/dist/chat/style.d.ts.map +1 -0
  94. package/dist/chat/style.js +306 -0
  95. package/dist/chat.d.ts +15 -0
  96. package/dist/chat.d.ts.map +1 -0
  97. package/dist/chat.js +8 -0
  98. package/dist/client.d.ts +33 -13
  99. package/dist/client.d.ts.map +1 -1
  100. package/dist/client.js +81 -16
  101. package/dist/collection.d.ts +23 -13
  102. package/dist/collection.d.ts.map +1 -1
  103. package/dist/collection.js +367 -38
  104. package/dist/connection.d.ts +54 -16
  105. package/dist/connection.d.ts.map +1 -1
  106. package/dist/connection.js +2582 -486
  107. package/dist/conversation.d.ts +39 -12
  108. package/dist/conversation.d.ts.map +1 -1
  109. package/dist/conversation.js +546 -171
  110. package/dist/digest.d.ts +2 -0
  111. package/dist/digest.d.ts.map +1 -0
  112. package/dist/digest.js +100 -0
  113. package/dist/durability.d.ts +7 -1
  114. package/dist/durability.d.ts.map +1 -1
  115. package/dist/entry.d.ts +17 -0
  116. package/dist/entry.d.ts.map +1 -0
  117. package/dist/entry.js +157 -0
  118. package/dist/expectation.d.ts +21 -0
  119. package/dist/expectation.d.ts.map +1 -0
  120. package/dist/expectation.js +70 -0
  121. package/dist/failure.d.ts +13 -0
  122. package/dist/failure.d.ts.map +1 -0
  123. package/dist/failure.js +19 -0
  124. package/dist/fence.d.ts +6 -0
  125. package/dist/fence.d.ts.map +1 -0
  126. package/dist/fence.js +10 -0
  127. package/dist/file.d.ts +2 -10
  128. package/dist/file.d.ts.map +1 -1
  129. package/dist/file.js +3 -120
  130. package/dist/host.d.ts +15 -3
  131. package/dist/host.d.ts.map +1 -1
  132. package/dist/host.js +107 -21
  133. package/dist/identity.d.ts +5 -0
  134. package/dist/identity.d.ts.map +1 -1
  135. package/dist/identity.js +19 -1
  136. package/dist/index.d.ts +20 -10
  137. package/dist/index.d.ts.map +1 -1
  138. package/dist/index.js +7 -0
  139. package/dist/memory.d.ts +11 -4
  140. package/dist/memory.d.ts.map +1 -1
  141. package/dist/memory.js +92 -27
  142. package/dist/model.d.ts +7 -4
  143. package/dist/model.d.ts.map +1 -1
  144. package/dist/model.js +10 -1
  145. package/dist/observation.d.ts +7 -2
  146. package/dist/observation.d.ts.map +1 -1
  147. package/dist/observation.js +29 -12
  148. package/dist/open.d.ts +2 -0
  149. package/dist/open.d.ts.map +1 -0
  150. package/dist/open.js +1 -0
  151. package/dist/outcome.d.ts +57 -3
  152. package/dist/outcome.d.ts.map +1 -1
  153. package/dist/outcome.js +67 -1
  154. package/dist/overlay.d.ts +18 -5
  155. package/dist/overlay.d.ts.map +1 -1
  156. package/dist/overlay.js +271 -165
  157. package/dist/query.d.ts +61 -4
  158. package/dist/query.d.ts.map +1 -1
  159. package/dist/query.js +40 -7
  160. package/dist/reach.d.ts +13 -4
  161. package/dist/reach.d.ts.map +1 -1
  162. package/dist/reach.js +201 -47
  163. package/dist/react.d.ts +31 -20
  164. package/dist/react.d.ts.map +1 -1
  165. package/dist/react.js +13 -21
  166. package/dist/read-side.d.ts +18 -9
  167. package/dist/read-side.d.ts.map +1 -1
  168. package/dist/read-side.js +903 -157
  169. package/dist/readiness.d.ts +53 -0
  170. package/dist/readiness.d.ts.map +1 -0
  171. package/dist/readiness.js +33 -0
  172. package/dist/reads.d.ts +11 -0
  173. package/dist/reads.d.ts.map +1 -0
  174. package/dist/reads.js +98 -0
  175. package/dist/recovery.d.ts +25 -0
  176. package/dist/recovery.d.ts.map +1 -0
  177. package/dist/recovery.js +198 -0
  178. package/dist/refresh.d.ts +13 -3
  179. package/dist/refresh.d.ts.map +1 -1
  180. package/dist/refresh.js +73 -43
  181. package/dist/replica.d.ts +16 -8
  182. package/dist/replica.d.ts.map +1 -1
  183. package/dist/replica.js +198 -91
  184. package/dist/room-draft.d.ts +40 -9
  185. package/dist/room-draft.d.ts.map +1 -1
  186. package/dist/room-draft.js +153 -37
  187. package/dist/rows.d.ts +10 -13
  188. package/dist/rows.d.ts.map +1 -1
  189. package/dist/rows.js +19 -14
  190. package/dist/save.d.ts +72 -4
  191. package/dist/save.d.ts.map +1 -1
  192. package/dist/save.js +812 -145
  193. package/dist/search.d.ts +52 -8
  194. package/dist/search.d.ts.map +1 -1
  195. package/dist/search.js +242 -46
  196. package/dist/seed.d.ts +16 -0
  197. package/dist/seed.d.ts.map +1 -0
  198. package/dist/seed.js +75 -0
  199. package/dist/shared.d.ts +19 -0
  200. package/dist/shared.d.ts.map +1 -0
  201. package/dist/shared.js +461 -0
  202. package/dist/status.d.ts +6 -2
  203. package/dist/status.d.ts.map +1 -1
  204. package/dist/status.js +0 -8
  205. package/dist/store.d.ts +90 -23
  206. package/dist/store.d.ts.map +1 -1
  207. package/dist/store.js +675 -295
  208. package/dist/tables.d.ts +181 -0
  209. package/dist/tables.d.ts.map +1 -0
  210. package/dist/tables.js +309 -0
  211. package/dist/turn.d.ts +9 -0
  212. package/dist/turn.d.ts.map +1 -0
  213. package/dist/turn.js +67 -0
  214. package/dist/windows.d.ts +21 -0
  215. package/dist/windows.d.ts.map +1 -0
  216. package/dist/windows.js +84 -0
  217. package/dist/wire.d.ts +165 -33
  218. package/dist/wire.d.ts.map +1 -1
  219. package/dist/wire.js +67 -31
  220. package/package.json +30 -14
  221. package/dist/attachment.d.ts +0 -3
  222. package/dist/attachment.d.ts.map +0 -1
  223. package/dist/attachment.js +0 -8
package/dist/reach.js CHANGED
@@ -1,28 +1,33 @@
1
1
  import { unheldView } from './conversation.js';
2
2
  import { modelTypes, NO_MODEL } from './model.js';
3
3
  import { applied, observing } from './observation.js';
4
+ import { runtimeError } from './failure.js';
5
+ import { failure, lockedRoom, ROOM_LOCKED, undeclared, UNREADABLE, } from './outcome.js';
4
6
  import { beneath } from './paths.js';
5
7
  import { reconciled } from './read-side.js';
6
- import { moveTarget, readParts } from './room-draft.js';
8
+ import { moveTarget, readParts, staleWrite, } from './room-draft.js';
7
9
  import { documentKey } from './rows.js';
10
+ import { undeclaredSearch } from './search.js';
11
+ import { after, stepped } from './turn.js';
8
12
  const UNHELD_TYPES = { collection: modelTypes(NO_MODEL), typedSelection: null };
9
- const UNDECLARED = { kind: 'undeclared' };
10
- function undeclared() {
11
- return Promise.resolve(UNDECLARED);
13
+ function unconversed(name) {
14
+ return () => Promise.resolve(undeclared(name));
12
15
  }
13
16
  const NO_CONVERSATION = {
14
17
  list: () => Promise.resolve([]),
15
18
  read: (conversation) => Promise.resolve(unheldView(conversation)),
16
- create: undeclared,
17
- send: undeclared,
18
- answer: undeclared,
19
- interrupt: undeclared,
20
- kill: undeclared,
21
- cancel: undeclared,
22
- delete: undeclared,
19
+ create: unconversed('conversation.create'),
20
+ send: unconversed('conversation.send'),
21
+ attach: unconversed('conversation.attach'),
22
+ answer: unconversed('conversation.answer'),
23
+ interrupt: unconversed('conversation.interrupt'),
24
+ kill: unconversed('conversation.kill'),
25
+ cancel: unconversed('conversation.cancel'),
26
+ delete: unconversed('conversation.delete'),
23
27
  };
24
28
  const NO_CHAT_DRAFT = {
25
29
  write: () => Promise.resolve(),
30
+ prepend: () => Promise.resolve(),
26
31
  discard: () => Promise.resolve(),
27
32
  };
28
33
  export function holds(reach, room) {
@@ -51,6 +56,18 @@ function covers(reach, room, path) {
51
56
  function conversing(reach) {
52
57
  return reach === null || reach.conversations;
53
58
  }
59
+ function refers(reach, scope) {
60
+ if (reach === null)
61
+ return true;
62
+ if (scope === null)
63
+ return false;
64
+ return scope.path === undefined
65
+ ? wholly(reach, scope.locator)
66
+ : covers(reach, scope.locator, scope.path);
67
+ }
68
+ function unreachedReference(fileRef) {
69
+ return { kind: 'absent', code: 'binary-file-absent', fields: { fileRef }, retryable: false };
70
+ }
54
71
  export function planReach(reach) {
55
72
  return reach === null
56
73
  ? null
@@ -74,7 +91,7 @@ function typesWithin(reach, room, types) {
74
91
  };
75
92
  }
76
93
  function moves(reach, room, move) {
77
- return move === undefined || covers(reach, room, moveTarget(move));
94
+ return move === undefined || covers(reach, move.room ?? room, moveTarget(move));
78
95
  }
79
96
  function draftWithin(reach, draft) {
80
97
  return covers(reach, draft.room, draft.path) && moves(reach, draft.room, draft.parts.move);
@@ -88,7 +105,7 @@ function shownDraft(reach, draft) {
88
105
  function writeWithin(reach, write) {
89
106
  return write.outcome === 'written'
90
107
  ? { outcome: 'written', roomDraft: shownDraft(reach, write.roomDraft) }
91
- : { outcome: 'stale', current: shownDraft(reach, write.current) };
108
+ : staleWrite(shownDraft(reach, write.current));
92
109
  }
93
110
  function savedWithin(reach, room, result) {
94
111
  if (reach === null || result.outcome === 'saved')
@@ -107,58 +124,100 @@ function savedWithin(reach, room, result) {
107
124
  function statusWithin(reach, status) {
108
125
  return {
109
126
  ...status,
110
- attentionRequired: status.attentionRequired.filter((entry) => covers(reach, entry.room, entry.path)),
127
+ attentionRequired: status.attentionRequired.filter((entry) => 'refusal' in entry ? wholly(reach, entry.room) : covers(reach, entry.room, entry.path)),
111
128
  };
112
129
  }
113
130
  function queueWithin(reach, room, entries) {
114
- return entries.filter(({ batch }) => batch.changes.every((change) => covers(reach, room, change.path) &&
115
- (change.kind !== 'move' || covers(reach, room, change.to))));
131
+ return entries
132
+ .filter(({ batch }) => batch.changes.every((change) => covers(reach, room, change.path) &&
133
+ (change.kind !== 'move' || covers(reach, room, change.to))))
134
+ .map((entry) => {
135
+ const { between, ...batch } = entry.batch;
136
+ if (between === undefined)
137
+ return entry;
138
+ return wholly(reach, between.source.room) && wholly(reach, between.destination.room)
139
+ ? entry
140
+ : { ...entry, batch };
141
+ });
116
142
  }
117
143
  function unreached(room) {
118
- return new Error(`No Local Replica for ${room}`);
144
+ return runtimeError('room-not-held', `No Local Replica for ${room}`);
119
145
  }
120
- export function narrowed(runtime, given) {
146
+ function lockedError(room) {
147
+ return runtimeError(ROOM_LOCKED, `${room} is locked until House confirms its access`, true);
148
+ }
149
+ export function narrowed(runtime, given, told) {
121
150
  const { md, watch } = runtime;
122
151
  let reach = given;
152
+ let port = told;
123
153
  const open = new Set();
124
- function following(begin) {
154
+ function following(begin, locking = false) {
125
155
  let stop = begin();
126
156
  const restart = () => {
127
157
  stop();
128
158
  stop = begin();
129
159
  };
130
160
  open.add(restart);
161
+ const unwatch = locking ? runtime.watchLocks(restart) : () => { };
131
162
  return () => {
132
163
  open.delete(restart);
164
+ unwatch();
133
165
  stop();
134
166
  };
135
167
  }
168
+ const unlocking = (rooms) => {
169
+ const locked = (rooms ?? []).filter((room) => runtime.locked(room));
170
+ return locked.length === 0
171
+ ? null
172
+ : { pending: locked.map((room) => ({ room, awaits: 'unlock' })) };
173
+ };
174
+ const unlocked = (entries) => entries.filter((entry) => !runtime.locked(entry.room));
175
+ const intoLocked = (held) => held.some((move) => move?.room !== undefined && runtime.locked(move.room));
136
176
  const within = (topic, input, deliver) => {
137
177
  if (topic === 'rooms') {
138
178
  return following(() => watch(topic, input, (rooms) => deliver(roomsWithin(reach, rooms))));
139
179
  }
180
+ if (topic === 'readiness') {
181
+ return following(() => runtime.watchReadiness(port, reach, deliver));
182
+ }
140
183
  if (topic === 'sync-status') {
141
- return following(() => watch(topic, input, (status) => deliver(statusWithin(reach, status))));
184
+ return following(() => watch(topic, input, (status) => {
185
+ const within = statusWithin(reach, status);
186
+ deliver({ ...within, attentionRequired: unlocked(within.attentionRequired) });
187
+ }), true);
142
188
  }
143
189
  if (topic === 'room-drafts') {
144
- return following(() => watch(topic, input, (drafts) => deliver(draftsWithin(reach, drafts))));
190
+ return following(() => watch(topic, input, (drafts) => deliver(unlocked(draftsWithin(reach, drafts)))), true);
145
191
  }
146
192
  if (topic === 'queue') {
147
193
  const room = input[0];
148
194
  return following(() => {
149
- if (!holds(reach, room)) {
195
+ if (!holds(reach, room) || runtime.locked(room)) {
150
196
  deliver([]);
151
197
  return () => { };
152
198
  }
153
199
  return watch(topic, input, (entries) => deliver(queueWithin(reach, room, entries)));
154
- });
200
+ }, true);
155
201
  }
156
202
  if (topic === 'query') {
157
203
  const plan = runtime.plan(input[0], reach);
158
204
  let held = null;
159
205
  return following(() => {
206
+ const locked = unlocking(plan.rooms);
207
+ if (locked !== null) {
208
+ const answer = { result: locked, keys: [] };
209
+ const same = held !== null &&
210
+ 'pending' in held.result &&
211
+ JSON.stringify(held.result) === JSON.stringify(locked);
212
+ if (!same)
213
+ deliver(answer);
214
+ held = answer;
215
+ return () => { };
216
+ }
160
217
  let first = true;
161
- return runtime.watchPlan({ ...plan, reach: planReach(reach) }, (message) => {
218
+ let open = true;
219
+ let order = undefined;
220
+ const heard = (message) => {
162
221
  if (!first) {
163
222
  held = applied(held, message);
164
223
  deliver(message);
@@ -170,13 +229,30 @@ export function narrowed(runtime, given) {
170
229
  deliver(message);
171
230
  return;
172
231
  }
173
- const next = reconciled(held, message);
174
- if (next === null)
175
- return;
176
- held = next.keyed;
177
- deliver(next.change);
232
+ return after(stepped(reconciled(held, message)), (next) => {
233
+ if (!open || next === null)
234
+ return;
235
+ held = next.keyed;
236
+ deliver(next.message);
237
+ });
238
+ };
239
+ const current = { ...plan, reach: planReach(reach), locked: runtime.lockedRooms() };
240
+ const stop = runtime.watchPlan(current, (message) => {
241
+ const queued = after(order, () => (open ? heard(message) : undefined));
242
+ if (queued instanceof Promise) {
243
+ const ended = queued.catch(() => { });
244
+ order = ended;
245
+ void ended.then(() => {
246
+ if (order === ended)
247
+ order = undefined;
248
+ });
249
+ }
178
250
  });
179
- });
251
+ return () => {
252
+ open = false;
253
+ stop();
254
+ };
255
+ }, true);
180
256
  }
181
257
  if (topic === 'conversation') {
182
258
  return following(() => {
@@ -186,7 +262,7 @@ export function narrowed(runtime, given) {
186
262
  return () => { };
187
263
  });
188
264
  }
189
- if (topic === 'conversations') {
265
+ if (topic === 'conversations' || topic === 'agents') {
190
266
  return following(() => {
191
267
  if (conversing(reach))
192
268
  return watch(topic, input, deliver);
@@ -201,9 +277,16 @@ export function narrowed(runtime, given) {
201
277
  const view = {
202
278
  ...md,
203
279
  rooms: () => roomsWithin(reach, md.rooms()),
204
- query: ((input) => runtime.query(runtime.plan(input, reach))),
205
- search: (input) => runtime.search(input, reach),
280
+ query: ((input) => {
281
+ const plan = runtime.plan(input, reach);
282
+ return Promise.resolve(unlocking(plan.rooms) ?? runtime.query({ ...plan, locked: runtime.lockedRooms() }));
283
+ }),
284
+ search: (input) => port.search === 'required'
285
+ ? runtime.search(input, reach, runtime.lockedRooms())
286
+ : Promise.resolve(undeclaredSearch()),
206
287
  types(room) {
288
+ if (holds(reach, room) && runtime.locked(room))
289
+ throw lockedError(room);
207
290
  if (wholly(reach, room))
208
291
  return md.types(room);
209
292
  if (!holds(reach, room))
@@ -214,30 +297,90 @@ export function narrowed(runtime, given) {
214
297
  async save(room, roomDrafts, writtenBy) {
215
298
  if (!holds(reach, room))
216
299
  throw unreached(room);
217
- const outside = roomDrafts.find(({ path }) => !covers(reach, room, path) ||
218
- !moves(reach, room, md.roomDraft.read(room, path)?.parts.move));
219
- if (outside !== undefined)
220
- return { outcome: 'refused', rule: 'stale', file: outside.path };
300
+ if (runtime.locked(room))
301
+ return { outcome: 'refused', code: ROOM_LOCKED, retryable: true };
302
+ const held = roomDrafts.map(({ path }) => md.roomDraft.read(room, path)?.parts.move);
303
+ const outside = roomDrafts.find(({ path }, at) => !covers(reach, room, path) || !moves(reach, room, held[at]));
304
+ if (outside !== undefined) {
305
+ return { outcome: 'refused', code: 'stale', retryable: false, file: outside.path };
306
+ }
307
+ if (intoLocked(held))
308
+ return { outcome: 'refused', code: ROOM_LOCKED, retryable: true };
221
309
  return savedWithin(reach, room, await md.save(room, roomDrafts, writtenBy));
222
310
  },
223
- history: (room, path, query) => covers(reach, room, path)
224
- ? md.history(room, path, query)
225
- : Promise.resolve({ kind: 'room-not-readable', locator: room }),
311
+ async saveParts(room, parts, writtenBy) {
312
+ if (!holds(reach, room))
313
+ throw unreached(room);
314
+ if (runtime.locked(room))
315
+ return { outcome: 'refused', code: ROOM_LOCKED, retryable: true };
316
+ const held = parts.map(({ parts: one }) => readParts(one).move);
317
+ if (parts.some(({ path }, at) => !covers(reach, room, path) || !moves(reach, room, held[at]))) {
318
+ throw unreached(room);
319
+ }
320
+ if (intoLocked(held))
321
+ return { outcome: 'refused', code: ROOM_LOCKED, retryable: true };
322
+ return savedWithin(reach, room, await md.saveParts(room, parts, writtenBy));
323
+ },
324
+ resolve(room, path, destination) {
325
+ const at = runtime.located(room, path, destination);
326
+ return at !== null && !covers(reach, at.room, at.path)
327
+ ? Promise.resolve(UNREADABLE)
328
+ : md.resolve(room, path, destination);
329
+ },
330
+ history: (room, path, query) => !covers(reach, room, path)
331
+ ? Promise.resolve(failure({ kind: 'room-not-readable', locator: room }))
332
+ : runtime.locked(room)
333
+ ? Promise.resolve(lockedRoom(room))
334
+ : md.history(room, path, query),
335
+ binaryFile: {
336
+ read: (room, path, revision) => !covers(reach, room, path)
337
+ ? Promise.resolve(failure({ kind: 'room-not-readable', locator: room }))
338
+ : runtime.locked(room)
339
+ ? Promise.resolve(lockedRoom(room))
340
+ : md.binaryFile.read(room, path, revision),
341
+ link: (room, path, revision) => !covers(reach, room, path)
342
+ ? Promise.resolve(failure({ kind: 'room-not-readable', locator: room }))
343
+ : runtime.locked(room)
344
+ ? Promise.resolve(lockedRoom(room))
345
+ : md.binaryFile.link(room, path, revision),
346
+ readReference(reference) {
347
+ const scope = runtime.referrerScope(reference.referrer);
348
+ if (!refers(reach, scope))
349
+ return Promise.resolve(unreachedReference(reference.fileRef));
350
+ if (scope !== null && runtime.locked(scope.locator)) {
351
+ return Promise.resolve(lockedRoom(scope.locator));
352
+ }
353
+ return md.binaryFile.readReference(reference);
354
+ },
355
+ },
226
356
  reached: (room, roomVersion, path) => (path === undefined ? wholly(reach, room) : covers(reach, room, path)) &&
357
+ !runtime.locked(room) &&
227
358
  md.reached(room, roomVersion, path),
228
359
  log: () => (reach === null ? md.log() : ''),
229
360
  roomDraft: {
230
- list: () => md.roomDraft.list().then((drafts) => draftsWithin(reach, drafts)),
231
- read: (room, path) => covers(reach, room, path) ? shownDraft(reach, md.roomDraft.read(room, path)) : null,
361
+ list: () => md.roomDraft.list().then((drafts) => unlocked(draftsWithin(reach, drafts))),
362
+ read(room, path) {
363
+ if (!covers(reach, room, path))
364
+ return null;
365
+ if (runtime.locked(room))
366
+ throw lockedError(room);
367
+ return shownDraft(reach, md.roomDraft.read(room, path));
368
+ },
232
369
  async write(room, path, revision, parts) {
233
- if (!covers(reach, room, path) || !moves(reach, room, readParts(parts).move)) {
370
+ const move = readParts(parts).move;
371
+ if (!covers(reach, room, path) || !moves(reach, room, move))
234
372
  throw unreached(room);
235
- }
373
+ if (runtime.locked(room))
374
+ throw lockedError(room);
375
+ if (intoLocked([move]))
376
+ throw lockedError(move.room);
236
377
  return writeWithin(reach, await md.roomDraft.write(room, path, revision, parts));
237
378
  },
238
379
  async discard(room, path, revision) {
239
380
  if (!covers(reach, room, path))
240
- return { outcome: 'stale', current: null };
381
+ return staleWrite(null);
382
+ if (runtime.locked(room))
383
+ throw lockedError(room);
241
384
  return writeWithin(reach, await md.roomDraft.discard(room, path, revision));
242
385
  },
243
386
  },
@@ -245,7 +388,12 @@ export function narrowed(runtime, given) {
245
388
  list: () => conversation().list(),
246
389
  read: (id, options) => conversation().read(id, options),
247
390
  create: (input) => conversation().create(input),
248
- send: (id) => conversation().send(id),
391
+ send: (id, operation, draft) => conversation().send(id, operation, draft),
392
+ attach: (room, file) => !wholly(reach, room)
393
+ ? Promise.resolve(failure({ kind: 'room-not-readable', locator: room }))
394
+ : runtime.locked(room)
395
+ ? Promise.resolve(lockedRoom(room))
396
+ : conversation().attach(room, file),
249
397
  answer: (id, answer) => conversation().answer(id, answer),
250
398
  interrupt: (id) => conversation().interrupt(id),
251
399
  kill: (id) => conversation().kill(id),
@@ -253,7 +401,8 @@ export function narrowed(runtime, given) {
253
401
  delete: (id) => conversation().delete(id),
254
402
  },
255
403
  chatDraft: {
256
- write: (id, draft) => chatDraft().write(id, draft),
404
+ write: (id, draft, revision) => chatDraft().write(id, draft, revision),
405
+ prepend: (id, draft) => chatDraft().prepend(id, draft),
257
406
  discard: (id) => chatDraft().discard(id),
258
407
  },
259
408
  observe: observing(within),
@@ -266,5 +415,10 @@ export function narrowed(runtime, given) {
266
415
  for (const restart of [...open])
267
416
  restart();
268
417
  },
418
+ facts(next) {
419
+ port = next;
420
+ for (const restart of [...open])
421
+ restart();
422
+ },
269
423
  };
270
424
  }
package/dist/react.d.ts CHANGED
@@ -1,32 +1,43 @@
1
1
  import type { MdRuntimeClient } from './client.js';
2
- import type { AppRoom, ConnectionRefusal, SaveResult } from './connection.js';
3
- import type { ServiceMode } from './adapter.js';
4
- import type { ConversationEntry, ConversationOptions, ConversationView } from './conversation.js';
2
+ import type { AppRoom, ConnectionRefusal, SavedRoomDraft, SaveResult } from './connection.js';
3
+ import type { Member, ServiceMode } from './adapter.js';
4
+ import type { ConversationAgent, ConversationEntry, ConversationOptions, ConversationView } from './conversation.js';
5
+ import { type RuntimeError } from './failure.js';
5
6
  import type { QueueEntry } from './outcome.js';
6
- import type { DocumentQuery, DocumentResult, GroupQuery, GroupResult } from './query.js';
7
+ import type { DocumentQuery, DocumentResult, GroupQuery, GroupResult, PendingResult } from './query.js';
8
+ import type { Readiness } from './readiness.js';
7
9
  import type { RoomDraft, RoomDraftParts } from './room-draft.js';
8
10
  import type { SyncStatus } from './status.js';
9
11
  type Client = MdRuntimeClient | null;
10
- export declare function useObserve(client: Client, topic: 'deployment-facts'): unknown;
11
- export declare function useObserve(client: Client, topic: 'rooms'): readonly AppRoom[] | undefined;
12
- export declare function useObserve(client: Client, topic: 'connection-refusal'): ConnectionRefusal | null | undefined;
13
- export declare function useObserve(client: Client, topic: 'storage-loss'): string | null | undefined;
14
- export declare function useObserve(client: Client, topic: 'sync-status'): SyncStatus | undefined;
15
- export declare function useObserve(client: Client, topic: 'service-mode'): ServiceMode | undefined;
16
- export declare function useObserve(client: Client, topic: 'query', query: DocumentQuery): DocumentResult | undefined;
17
- export declare function useObserve(client: Client, topic: 'query', query: GroupQuery): GroupResult | undefined;
18
- export declare function useObserve(client: Client, topic: 'queue', locator: string): readonly QueueEntry[] | undefined;
19
- export declare function useObserve(client: Client, topic: 'room-drafts'): readonly RoomDraft[] | undefined;
20
- export declare function useObserve(client: Client, topic: 'presentation'): unknown;
21
- export declare function useObserve(client: Client, topic: 'conversation', conversation: string, options: ConversationOptions): ConversationView | undefined;
22
- export declare function useObserve(client: Client, topic: 'conversations'): readonly ConversationEntry[] | undefined;
12
+ export type Observed<T> = {
13
+ readonly state: 'loading';
14
+ } | {
15
+ readonly state: 'ready';
16
+ readonly value: T;
17
+ };
18
+ export declare function useObserve(client: Client, topic: 'deployment-facts'): Observed<unknown>;
19
+ export declare function useObserve(client: Client, topic: 'rooms'): Observed<readonly AppRoom[]>;
20
+ export declare function useObserve(client: Client, topic: 'readiness'): Observed<Readiness>;
21
+ export declare function useObserve(client: Client, topic: 'connection-refusal'): Observed<ConnectionRefusal | null>;
22
+ export declare function useObserve(client: Client, topic: 'storage-loss'): Observed<string | null>;
23
+ export declare function useObserve(client: Client, topic: 'sync-status'): Observed<SyncStatus>;
24
+ export declare function useObserve(client: Client, topic: 'service-mode'): Observed<ServiceMode>;
25
+ export declare function useObserve(client: Client, topic: 'member'): Observed<Member | null>;
26
+ export declare function useObserve(client: Client, topic: 'query', query: DocumentQuery): Observed<DocumentResult | PendingResult>;
27
+ export declare function useObserve(client: Client, topic: 'query', query: GroupQuery): Observed<GroupResult | PendingResult>;
28
+ export declare function useObserve(client: Client, topic: 'queue', locator: string): Observed<readonly QueueEntry[]>;
29
+ export declare function useObserve(client: Client, topic: 'room-drafts'): Observed<readonly RoomDraft[]>;
30
+ export declare function useObserve(client: Client, topic: 'presentation'): Observed<unknown>;
31
+ export declare function useObserve(client: Client, topic: 'conversation', conversation: string, options: ConversationOptions): Observed<ConversationView>;
32
+ export declare function useObserve(client: Client, topic: 'conversations'): Observed<readonly ConversationEntry[]>;
33
+ export declare function useObserve(client: Client, topic: 'agents'): Observed<readonly ConversationAgent[]>;
23
34
  export type Saving = {
24
35
  readonly busy: boolean;
25
36
  readonly outcome: SaveResult | null;
26
- readonly failure: Error | null;
27
- save(room: string, roomDrafts: ReadonlyMap<string, RoomDraftParts>): Promise<SaveResult>;
37
+ readonly failure: RuntimeError | null;
38
+ save(room: string, parts: ReadonlyMap<string, RoomDraftParts>): Promise<SaveResult>;
39
+ saveWritten(room: string, roomDrafts: readonly SavedRoomDraft[]): Promise<SaveResult>;
28
40
  };
29
- export declare function saveRoomDrafts(client: MdRuntimeClient, room: string, roomDrafts: ReadonlyMap<string, RoomDraftParts>): Promise<SaveResult>;
30
41
  export declare function useSave(client: MdRuntimeClient): Saving;
31
42
  export {};
32
43
  //# sourceMappingURL=react.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAkB,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC9F,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAElG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzF,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAG9C,KAAK,MAAM,GAAG,eAAe,GAAG,IAAI,CAAC;AAIrC,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC;AAC/E,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,SAAS,OAAO,EAAE,GAAG,SAAS,CAAC;AAC3F,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,oBAAoB,GAC1B,iBAAiB,GAAG,IAAI,GAAG,SAAS,CAAC;AACxC,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;AAC7F,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,UAAU,GAAG,SAAS,CAAC;AACzF,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,WAAW,GAAG,SAAS,CAAC;AAC3F,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,aAAa,GACnB,cAAc,GAAG,SAAS,CAAC;AAC9B,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,UAAU,GAChB,WAAW,GAAG,SAAS,CAAC;AAC3B,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,MAAM,GACd,SAAS,UAAU,EAAE,GAAG,SAAS,CAAC;AACrC,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,SAAS,SAAS,EAAE,GAAG,SAAS,CAAC;AACnG,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC;AAC3E,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,cAAc,EACrB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,mBAAmB,GAC3B,gBAAgB,GAAG,SAAS,CAAC;AAChC,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,eAAe,GACrB,SAAS,iBAAiB,EAAE,GAAG,SAAS,CAAC;AAkC5C,MAAM,MAAM,MAAM,GAAG;IACnB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,KAAK,GAAG,IAAI,CAAC;IAC/B,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CAC1F,CAAC;AAMF,wBAAsB,cAAc,CAClC,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,GAC9C,OAAO,CAAC,UAAU,CAAC,CAWrB;AAED,wBAAgB,OAAO,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAkBvD"}
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"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"}
package/dist/react.js CHANGED
@@ -1,10 +1,12 @@
1
- import { useCallback, useEffect, useRef, useState } from 'react';
1
+ import { useCallback, useLayoutEffect, useRef, useState } from 'react';
2
+ import { failed } from './failure.js';
3
+ const LOADING = { state: 'loading' };
2
4
  export function useObserve(client, topic, ...input) {
3
5
  const key = JSON.stringify([topic, ...input]);
4
6
  const [held, setHeld] = useState(null);
5
7
  const opened = useRef(input);
6
8
  opened.current = input;
7
- useEffect(() => {
9
+ useLayoutEffect(() => {
8
10
  if (client === null)
9
11
  return;
10
12
  let live = true;
@@ -18,7 +20,7 @@ export function useObserve(client, topic, ...input) {
18
20
  observation = open;
19
21
  const take = (value) => {
20
22
  if (live)
21
- setHeld({ client, key, value });
23
+ setHeld({ client, key, observed: { state: 'ready', value } });
22
24
  };
23
25
  take(open.current());
24
26
  open.subscribe(take);
@@ -28,35 +30,25 @@ export function useObserve(client, topic, ...input) {
28
30
  observation?.close();
29
31
  };
30
32
  }, [client, key]);
31
- return held !== null && held.client === client && held.key === key ? held.value : undefined;
33
+ return held !== null && held.client === client && held.key === key ? held.observed : LOADING;
32
34
  }
33
35
  const IDLE = { busy: false, outcome: null, failure: null };
34
- export async function saveRoomDrafts(client, room, roomDrafts) {
35
- const kept = [];
36
- for (const [path, parts] of roomDrafts) {
37
- const prior = await client.roomDraft.read(room, path);
38
- const write = await client.roomDraft.write(room, path, prior?.revision ?? null, parts);
39
- if (write.outcome !== 'written' || write.roomDraft === null) {
40
- throw new Error(`the Room Draft at ${path} was not written`);
41
- }
42
- kept.push({ path, revision: write.roomDraft.revision });
43
- }
44
- return client.save(room, kept);
45
- }
46
36
  export function useSave(client) {
47
37
  const [state, setState] = useState(IDLE);
48
- const save = useCallback(async (room, roomDrafts) => {
38
+ const settled = useCallback(async (saving) => {
49
39
  setState({ busy: true, outcome: null, failure: null });
50
40
  try {
51
- const outcome = await saveRoomDrafts(client, room, roomDrafts);
41
+ const outcome = await saving();
52
42
  setState({ busy: false, outcome, failure: null });
53
43
  return outcome;
54
44
  }
55
45
  catch (error) {
56
- const failure = error instanceof Error ? error : new Error(String(error));
46
+ const failure = failed(error);
57
47
  setState({ busy: false, outcome: null, failure });
58
48
  throw failure;
59
49
  }
60
- }, [client]);
61
- return { ...state, save };
50
+ }, []);
51
+ const save = useCallback((room, parts) => settled(() => client.saveParts(room, [...parts].map(([path, one]) => ({ path, parts: one })))), [client, settled]);
52
+ const saveWritten = useCallback((room, roomDrafts) => settled(() => client.save(room, roomDrafts)), [client, settled]);
53
+ return { ...state, save, saveWritten };
62
54
  }
@@ -1,18 +1,27 @@
1
1
  import type { ChangeSet } from './change-set.js';
2
- import { type KeyedResult, type QueryChange } from './observation.js';
3
- import type { Plan, QueryResult } from './query.js';
2
+ import { type KeyedResult, type QueryMessage } from './observation.js';
3
+ import type { PendingResult, Plan, QueryCoverage, QueryRows } from './query.js';
4
+ import { type Later } from './turn.js';
4
5
  import type { SearchedDocument } from './search.js';
6
+ export type Cover = {
7
+ coverage: QueryCoverage;
8
+ } | PendingResult;
5
9
  export type ReadSide = {
6
- apply(set: ChangeSet): void;
7
- query(plan: Plan): Promise<QueryResult>;
8
- watch(plan: Plan, identity: string, deliver: (message: KeyedResult | QueryChange) => void): () => void;
9
- documents(): readonly SearchedDocument[];
10
+ apply(set: Later<ChangeSet>, next?: (set: ChangeSet) => void): Later<void>;
11
+ recover(): void;
12
+ settled(): Promise<void>;
13
+ query<T>(plan: Plan, at: () => T): Promise<{
14
+ rows: QueryRows;
15
+ at: T;
16
+ }>;
17
+ watch(plan: Plan, identity: string, cover: () => Cover, deliver: (message: QueryMessage) => void): () => void;
18
+ room(room: string): Iterable<string>;
10
19
  document(room: string, path: string): SearchedDocument | null;
11
20
  close(): void;
12
21
  };
13
- export declare function reconciled(previous: KeyedResult, next: KeyedResult): {
22
+ export declare function reconciled(previous: KeyedResult, next: KeyedResult): Generator<number, {
14
23
  keyed: KeyedResult;
15
- change: QueryChange;
16
- } | null;
24
+ message: QueryMessage;
25
+ } | null, void>;
17
26
  export declare function createReadSide(): ReadSide;
18
27
  //# sourceMappingURL=read-side.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"read-side.d.ts","sourceRoot":"","sources":["../src/read-side.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAU,KAAK,WAAW,EAAE,KAAK,WAAW,EAAe,MAAM,kBAAkB,CAAC;AAC3F,OAAO,KAAK,EAOV,IAAI,EAIJ,WAAW,EAIZ,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAuDpD,MAAM,MAAM,QAAQ,GAAG;IACrB,KAAK,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACxC,KAAK,CACH,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,CAAC,OAAO,EAAE,WAAW,GAAG,WAAW,KAAK,IAAI,GACpD,MAAM,IAAI,CAAC;IACd,SAAS,IAAI,SAAS,gBAAgB,EAAE,CAAC;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAAC;IAC9D,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AA+UF,wBAAgB,UAAU,CACxB,QAAQ,EAAE,WAAW,EACrB,IAAI,EAAE,WAAW,GAChB;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GAAG,IAAI,CA4BpD;AAED,wBAAgB,cAAc,IAAI,QAAQ,CA4HzC"}
1
+ {"version":3,"file":"read-side.d.ts","sourceRoot":"","sources":["../src/read-side.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAEL,KAAK,WAAW,EAEhB,KAAK,YAAY,EAElB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAOV,aAAa,EACb,IAAI,EAEJ,aAAa,EAIb,SAAS,EAIV,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAuC,KAAK,KAAK,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAwDpD,MAAM,MAAM,KAAK,GAAG;IAAE,QAAQ,EAAE,aAAa,CAAA;CAAE,GAAG,aAAa,CAAC;AA6ChE,MAAM,MAAM,QAAQ,GAAG;IACrB,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,SAAS,KAAK,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3E,OAAO,IAAI,IAAI,CAAC;IAChB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACzB,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,SAAS,CAAC;QAAC,EAAE,EAAE,CAAC,CAAA;KAAE,CAAC,CAAC;IACvE,KAAK,CACH,IAAI,EAAE,IAAI,EACV,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,KAAK,EAClB,OAAO,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,GACvC,MAAM,IAAI,CAAC;IACd,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IACrC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAAC;IAC9D,KAAK,IAAI,IAAI,CAAC;CACf,CAAC;AAomBF,wBAAiB,UAAU,CACzB,QAAQ,EAAE,WAAW,EACrB,IAAI,EAAE,WAAW,GAChB,SAAS,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GAAG,IAAI,EAAE,IAAI,CAAC,CAqC/E;AAmDD,wBAAgB,cAAc,IAAI,QAAQ,CA+mBzC"}