@agentshouse/mdruntime 0.1.0-cd50a4607cb7d → 0.1.0-cd574a610e9c8

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 (401) hide show
  1. package/README.md +24 -19
  2. package/dist/acceptance.d.ts +3 -0
  3. package/dist/acceptance.d.ts.map +1 -0
  4. package/dist/acceptance.js +299 -0
  5. package/dist/act-lanes.d.ts +30 -0
  6. package/dist/act-lanes.d.ts.map +1 -0
  7. package/dist/act-lanes.js +225 -0
  8. package/dist/actions.d.ts +26 -0
  9. package/dist/actions.d.ts.map +1 -0
  10. package/dist/actions.js +302 -0
  11. package/dist/adapter.d.ts +226 -117
  12. package/dist/adapter.d.ts.map +1 -1
  13. package/dist/attached-model.d.ts +6 -10
  14. package/dist/attached-model.d.ts.map +1 -1
  15. package/dist/attached-model.js +35 -41
  16. package/dist/authored.d.ts +88 -0
  17. package/dist/authored.d.ts.map +1 -0
  18. package/dist/authored.js +345 -0
  19. package/dist/binary.d.ts +0 -5
  20. package/dist/binary.d.ts.map +1 -1
  21. package/dist/binary.js +1 -1
  22. package/dist/bootstrap.d.ts +31 -11
  23. package/dist/bootstrap.d.ts.map +1 -1
  24. package/dist/bootstrap.js +309 -109
  25. package/dist/cell.d.ts +6 -0
  26. package/dist/cell.d.ts.map +1 -0
  27. package/dist/cell.js +9 -0
  28. package/dist/chain.d.ts +12 -0
  29. package/dist/chain.d.ts.map +1 -0
  30. package/dist/chain.js +64 -0
  31. package/dist/change-set.d.ts +13 -16
  32. package/dist/change-set.d.ts.map +1 -1
  33. package/dist/change-set.js +17 -88
  34. package/dist/change.d.ts +36 -1
  35. package/dist/change.d.ts.map +1 -1
  36. package/dist/change.js +17 -3
  37. package/dist/chat-types.d.ts +3 -0
  38. package/dist/chat-types.d.ts.map +1 -0
  39. package/dist/chat-types.js +11 -0
  40. package/dist/client.d.ts +45 -25
  41. package/dist/client.d.ts.map +1 -1
  42. package/dist/client.js +132 -27
  43. package/dist/collection.d.ts +32 -21
  44. package/dist/collection.d.ts.map +1 -1
  45. package/dist/collection.js +53 -288
  46. package/dist/column.d.ts +5 -0
  47. package/dist/column.d.ts.map +1 -0
  48. package/dist/column.js +9 -0
  49. package/dist/compile.d.ts +18 -0
  50. package/dist/compile.d.ts.map +1 -0
  51. package/dist/compile.js +723 -0
  52. package/dist/connection.d.ts +7 -118
  53. package/dist/connection.d.ts.map +1 -1
  54. package/dist/connection.js +111 -2975
  55. package/dist/context.d.ts +2 -0
  56. package/dist/context.d.ts.map +1 -0
  57. package/dist/{chat/context.js → context.js} +2 -5
  58. package/dist/conversation-live.d.ts +150 -0
  59. package/dist/conversation-live.d.ts.map +1 -0
  60. package/dist/conversation-live.js +259 -0
  61. package/dist/conversation-row.d.ts +40 -0
  62. package/dist/conversation-row.d.ts.map +1 -0
  63. package/dist/conversation-row.js +66 -0
  64. package/dist/conversation.d.ts +85 -48
  65. package/dist/conversation.d.ts.map +1 -1
  66. package/dist/conversation.js +1095 -688
  67. package/dist/declaration.d.ts +10 -0
  68. package/dist/declaration.d.ts.map +1 -0
  69. package/dist/declaration.js +24 -0
  70. package/dist/digest.d.ts +7 -0
  71. package/dist/digest.d.ts.map +1 -0
  72. package/dist/digest.js +113 -0
  73. package/dist/discovery.d.ts +10 -0
  74. package/dist/discovery.d.ts.map +1 -0
  75. package/dist/discovery.js +150 -0
  76. package/dist/durability.d.ts +23 -1
  77. package/dist/durability.d.ts.map +1 -1
  78. package/dist/durability.js +9 -0
  79. package/dist/entry.d.ts +5 -2
  80. package/dist/entry.d.ts.map +1 -1
  81. package/dist/entry.js +33 -28
  82. package/dist/expectation.d.ts +26 -13
  83. package/dist/expectation.d.ts.map +1 -1
  84. package/dist/expectation.js +72 -19
  85. package/dist/failure.d.ts +3 -0
  86. package/dist/failure.d.ts.map +1 -1
  87. package/dist/failure.js +6 -0
  88. package/dist/figures.d.ts +49 -0
  89. package/dist/figures.d.ts.map +1 -0
  90. package/dist/figures.js +199 -0
  91. package/dist/file.d.ts +2 -19
  92. package/dist/file.d.ts.map +1 -1
  93. package/dist/file.js +8 -122
  94. package/dist/holdings.d.ts +29 -0
  95. package/dist/holdings.d.ts.map +1 -0
  96. package/dist/holdings.js +95 -0
  97. package/dist/host.d.ts +27 -4
  98. package/dist/host.d.ts.map +1 -1
  99. package/dist/host.js +177 -27
  100. package/dist/identity.d.ts +0 -2
  101. package/dist/identity.d.ts.map +1 -1
  102. package/dist/identity.js +0 -6
  103. package/dist/index.d.ts +41 -14
  104. package/dist/index.d.ts.map +1 -1
  105. package/dist/index.js +19 -3
  106. package/dist/install.d.ts +60 -0
  107. package/dist/install.d.ts.map +1 -0
  108. package/dist/install.js +537 -0
  109. package/dist/lifecycle.d.ts +7 -0
  110. package/dist/lifecycle.d.ts.map +1 -0
  111. package/dist/lifecycle.js +282 -0
  112. package/dist/listing.d.ts +52 -0
  113. package/dist/listing.d.ts.map +1 -0
  114. package/dist/listing.js +152 -0
  115. package/dist/live.d.ts +25 -0
  116. package/dist/live.d.ts.map +1 -0
  117. package/dist/live.js +206 -0
  118. package/dist/loaded.d.ts +5 -0
  119. package/dist/loaded.d.ts.map +1 -0
  120. package/dist/loaded.js +40 -0
  121. package/dist/locks.d.ts +7 -0
  122. package/dist/locks.d.ts.map +1 -0
  123. package/dist/locks.js +32 -0
  124. package/dist/lost.d.ts +5 -0
  125. package/dist/lost.d.ts.map +1 -0
  126. package/dist/lost.js +49 -0
  127. package/dist/media.d.ts +13 -0
  128. package/dist/media.d.ts.map +1 -0
  129. package/dist/media.js +269 -0
  130. package/dist/memory.d.ts +25 -7
  131. package/dist/memory.d.ts.map +1 -1
  132. package/dist/memory.js +320 -110
  133. package/dist/messages.d.ts +58 -0
  134. package/dist/messages.d.ts.map +1 -0
  135. package/dist/messages.js +160 -0
  136. package/dist/model.d.ts +11 -13
  137. package/dist/model.d.ts.map +1 -1
  138. package/dist/model.js +7 -24
  139. package/dist/models.d.ts +10 -0
  140. package/dist/models.d.ts.map +1 -0
  141. package/dist/models.js +153 -0
  142. package/dist/name.d.ts +4 -0
  143. package/dist/name.d.ts.map +1 -0
  144. package/dist/name.js +122 -0
  145. package/dist/observation.d.ts +40 -10
  146. package/dist/observation.d.ts.map +1 -1
  147. package/dist/observation.js +233 -39
  148. package/dist/opened.d.ts +22 -0
  149. package/dist/opened.d.ts.map +1 -0
  150. package/dist/opened.js +107 -0
  151. package/dist/opening.d.ts +4 -0
  152. package/dist/opening.d.ts.map +1 -0
  153. package/dist/opening.js +53 -0
  154. package/dist/outcome.d.ts +62 -9
  155. package/dist/outcome.d.ts.map +1 -1
  156. package/dist/outcome.js +72 -0
  157. package/dist/overlay.d.ts +9 -6
  158. package/dist/overlay.d.ts.map +1 -1
  159. package/dist/overlay.js +129 -166
  160. package/dist/page-metadata.d.ts +8 -0
  161. package/dist/page-metadata.d.ts.map +1 -0
  162. package/dist/page-metadata.js +19 -0
  163. package/dist/parts.d.ts +4 -0
  164. package/dist/parts.d.ts.map +1 -0
  165. package/dist/parts.js +13 -0
  166. package/dist/paths.d.ts +2 -0
  167. package/dist/paths.d.ts.map +1 -1
  168. package/dist/paths.js +26 -0
  169. package/dist/preparation.d.ts +41 -0
  170. package/dist/preparation.d.ts.map +1 -0
  171. package/dist/preparation.js +314 -0
  172. package/dist/presence.d.ts +58 -0
  173. package/dist/presence.d.ts.map +1 -0
  174. package/dist/presence.js +231 -0
  175. package/dist/queries.d.ts +16 -0
  176. package/dist/queries.d.ts.map +1 -0
  177. package/dist/queries.js +308 -0
  178. package/dist/query.d.ts +27 -186
  179. package/dist/query.d.ts.map +1 -1
  180. package/dist/query.js +3 -464
  181. package/dist/{chat/questions.d.ts → questions.d.ts} +6 -7
  182. package/dist/questions.d.ts.map +1 -0
  183. package/dist/{chat/questions.js → questions.js} +12 -7
  184. package/dist/queue.d.ts +40 -0
  185. package/dist/queue.d.ts.map +1 -0
  186. package/dist/queue.js +480 -0
  187. package/dist/reach.d.ts +20 -7
  188. package/dist/reach.d.ts.map +1 -1
  189. package/dist/reach.js +360 -98
  190. package/dist/react.d.ts +22 -5
  191. package/dist/react.d.ts.map +1 -1
  192. package/dist/react.js +40 -18
  193. package/dist/{chat/read.d.ts → read.d.ts} +2 -0
  194. package/dist/read.d.ts.map +1 -0
  195. package/dist/{chat/read.js → read.js} +4 -0
  196. package/dist/readiness.d.ts +55 -0
  197. package/dist/readiness.d.ts.map +1 -0
  198. package/dist/readiness.js +25 -0
  199. package/dist/reads.d.ts +39 -0
  200. package/dist/reads.d.ts.map +1 -0
  201. package/dist/reads.js +178 -0
  202. package/dist/recovery.d.ts +5 -2
  203. package/dist/recovery.d.ts.map +1 -1
  204. package/dist/recovery.js +65 -8
  205. package/dist/refresh.d.ts +17 -14
  206. package/dist/refresh.d.ts.map +1 -1
  207. package/dist/refresh.js +145 -54
  208. package/dist/refusal.d.ts +5 -0
  209. package/dist/refusal.d.ts.map +1 -0
  210. package/dist/refusal.js +55 -0
  211. package/dist/replica.d.ts +50 -33
  212. package/dist/replica.d.ts.map +1 -1
  213. package/dist/replica.js +340 -191
  214. package/dist/retried.d.ts +30 -0
  215. package/dist/retried.d.ts.map +1 -0
  216. package/dist/retried.js +72 -0
  217. package/dist/room-draft.d.ts +66 -12
  218. package/dist/room-draft.d.ts.map +1 -1
  219. package/dist/room-draft.js +416 -122
  220. package/dist/room-drafts.d.ts +24 -0
  221. package/dist/room-drafts.d.ts.map +1 -0
  222. package/dist/room-drafts.js +220 -0
  223. package/dist/room-list.d.ts +10 -0
  224. package/dist/room-list.d.ts.map +1 -0
  225. package/dist/room-list.js +45 -0
  226. package/dist/rows.d.ts +32 -29
  227. package/dist/rows.d.ts.map +1 -1
  228. package/dist/rows.js +60 -118
  229. package/dist/runtime.d.ts +34 -0
  230. package/dist/runtime.d.ts.map +1 -0
  231. package/dist/runtime.js +1 -0
  232. package/dist/save.d.ts +43 -48
  233. package/dist/save.d.ts.map +1 -1
  234. package/dist/save.js +751 -303
  235. package/dist/saves.d.ts +34 -0
  236. package/dist/saves.d.ts.map +1 -0
  237. package/dist/saves.js +437 -0
  238. package/dist/search.d.ts +15 -77
  239. package/dist/search.d.ts.map +1 -1
  240. package/dist/search.js +10 -186
  241. package/dist/seed.d.ts +25 -0
  242. package/dist/seed.d.ts.map +1 -0
  243. package/dist/seed.js +145 -0
  244. package/dist/shared.d.ts +6 -7
  245. package/dist/shared.d.ts.map +1 -1
  246. package/dist/shared.js +75 -104
  247. package/dist/shown.d.ts +33 -0
  248. package/dist/shown.d.ts.map +1 -0
  249. package/dist/shown.js +309 -0
  250. package/dist/socket/admission.d.ts +7 -0
  251. package/dist/socket/admission.d.ts.map +1 -0
  252. package/dist/socket/admission.js +39 -0
  253. package/dist/socket/answers.d.ts +6 -0
  254. package/dist/socket/answers.d.ts.map +1 -0
  255. package/dist/socket/answers.js +20 -0
  256. package/dist/socket/lanes.d.ts +28 -0
  257. package/dist/socket/lanes.d.ts.map +1 -0
  258. package/dist/socket/lanes.js +103 -0
  259. package/dist/socket/reads.d.ts +10 -0
  260. package/dist/socket/reads.d.ts.map +1 -0
  261. package/dist/socket/reads.js +31 -0
  262. package/dist/socket/session.d.ts +38 -0
  263. package/dist/socket/session.d.ts.map +1 -0
  264. package/dist/socket/session.js +23 -0
  265. package/dist/socket.d.ts +67 -0
  266. package/dist/socket.d.ts.map +1 -0
  267. package/dist/socket.js +164 -0
  268. package/dist/source.d.ts +59 -0
  269. package/dist/source.d.ts.map +1 -0
  270. package/dist/source.js +93 -0
  271. package/dist/status.d.ts +4 -5
  272. package/dist/status.d.ts.map +1 -1
  273. package/dist/status.js +3 -7
  274. package/dist/store/installs.d.ts +30 -0
  275. package/dist/store/installs.d.ts.map +1 -0
  276. package/dist/store/installs.js +40 -0
  277. package/dist/store/readiness.d.ts +11 -0
  278. package/dist/store/readiness.d.ts.map +1 -0
  279. package/dist/store/readiness.js +68 -0
  280. package/dist/store/records.d.ts +223 -0
  281. package/dist/store/records.d.ts.map +1 -0
  282. package/dist/store/records.js +465 -0
  283. package/dist/store/room-held.d.ts +8 -0
  284. package/dist/store/room-held.d.ts.map +1 -0
  285. package/dist/store/room-held.js +17 -0
  286. package/dist/store/rooms-shown.d.ts +8 -0
  287. package/dist/store/rooms-shown.d.ts.map +1 -0
  288. package/dist/store/rooms-shown.js +13 -0
  289. package/dist/store/space.d.ts +11 -0
  290. package/dist/store/space.d.ts.map +1 -0
  291. package/dist/store/space.js +41 -0
  292. package/dist/store/watchers.d.ts +17 -0
  293. package/dist/store/watchers.d.ts.map +1 -0
  294. package/dist/store/watchers.js +56 -0
  295. package/dist/store/writes.d.ts +24 -0
  296. package/dist/store/writes.d.ts.map +1 -0
  297. package/dist/store/writes.js +125 -0
  298. package/dist/store.d.ts +114 -141
  299. package/dist/store.d.ts.map +1 -1
  300. package/dist/store.js +163 -687
  301. package/dist/stored.d.ts +167 -0
  302. package/dist/stored.d.ts.map +1 -0
  303. package/dist/stored.js +700 -0
  304. package/dist/surface.d.ts +90 -0
  305. package/dist/surface.d.ts.map +1 -0
  306. package/dist/surface.js +225 -0
  307. package/dist/sync.d.ts +19 -0
  308. package/dist/sync.d.ts.map +1 -0
  309. package/dist/sync.js +536 -0
  310. package/dist/tables.d.ts +376 -0
  311. package/dist/tables.d.ts.map +1 -0
  312. package/dist/tables.js +499 -0
  313. package/dist/tallies.d.ts +18 -0
  314. package/dist/tallies.d.ts.map +1 -0
  315. package/dist/tallies.js +128 -0
  316. package/dist/transfer.d.ts +0 -5
  317. package/dist/transfer.d.ts.map +1 -1
  318. package/dist/turn.d.ts +5 -3
  319. package/dist/turn.d.ts.map +1 -1
  320. package/dist/turn.js +51 -21
  321. package/dist/validation.d.ts +29 -0
  322. package/dist/validation.d.ts.map +1 -0
  323. package/dist/validation.js +98 -0
  324. package/dist/windows.d.ts +11 -0
  325. package/dist/windows.d.ts.map +1 -0
  326. package/dist/windows.js +16 -0
  327. package/dist/wire.d.ts +230 -36
  328. package/dist/wire.d.ts.map +1 -1
  329. package/dist/wire.js +77 -33
  330. package/package.json +19 -19
  331. package/AGENTS.md +0 -296
  332. package/dist/attempt.d.ts +0 -16
  333. package/dist/attempt.d.ts.map +0 -1
  334. package/dist/attempt.js +0 -70
  335. package/dist/chat/acts.d.ts +0 -33
  336. package/dist/chat/acts.d.ts.map +0 -1
  337. package/dist/chat/acts.js +0 -36
  338. package/dist/chat/context.d.ts +0 -3
  339. package/dist/chat/context.d.ts.map +0 -1
  340. package/dist/chat/input.d.ts +0 -9
  341. package/dist/chat/input.d.ts.map +0 -1
  342. package/dist/chat/input.js +0 -99
  343. package/dist/chat/kit.d.ts +0 -60
  344. package/dist/chat/kit.d.ts.map +0 -1
  345. package/dist/chat/kit.js +0 -123
  346. package/dist/chat/lasting.d.ts +0 -6
  347. package/dist/chat/lasting.d.ts.map +0 -1
  348. package/dist/chat/lasting.js +0 -19
  349. package/dist/chat/list.d.ts +0 -26
  350. package/dist/chat/list.d.ts.map +0 -1
  351. package/dist/chat/list.js +0 -62
  352. package/dist/chat/markdown.d.ts +0 -20
  353. package/dist/chat/markdown.d.ts.map +0 -1
  354. package/dist/chat/markdown.js +0 -188
  355. package/dist/chat/message.d.ts +0 -6
  356. package/dist/chat/message.d.ts.map +0 -1
  357. package/dist/chat/message.js +0 -192
  358. package/dist/chat/messages.d.ts +0 -21
  359. package/dist/chat/messages.d.ts.map +0 -1
  360. package/dist/chat/messages.js +0 -151
  361. package/dist/chat/name.d.ts +0 -6
  362. package/dist/chat/name.d.ts.map +0 -1
  363. package/dist/chat/name.js +0 -37
  364. package/dist/chat/opened.d.ts +0 -11
  365. package/dist/chat/opened.d.ts.map +0 -1
  366. package/dist/chat/opened.js +0 -17
  367. package/dist/chat/overlay.d.ts +0 -31
  368. package/dist/chat/overlay.d.ts.map +0 -1
  369. package/dist/chat/overlay.js +0 -97
  370. package/dist/chat/parts.d.ts +0 -57
  371. package/dist/chat/parts.d.ts.map +0 -1
  372. package/dist/chat/parts.js +0 -119
  373. package/dist/chat/process.d.ts +0 -20
  374. package/dist/chat/process.d.ts.map +0 -1
  375. package/dist/chat/process.js +0 -32
  376. package/dist/chat/provider.d.ts +0 -63
  377. package/dist/chat/provider.d.ts.map +0 -1
  378. package/dist/chat/provider.js +0 -620
  379. package/dist/chat/question.d.ts +0 -9
  380. package/dist/chat/question.d.ts.map +0 -1
  381. package/dist/chat/question.js +0 -80
  382. package/dist/chat/questions.d.ts.map +0 -1
  383. package/dist/chat/read.d.ts.map +0 -1
  384. package/dist/chat/ready.d.ts +0 -33
  385. package/dist/chat/ready.d.ts.map +0 -1
  386. package/dist/chat/ready.js +0 -91
  387. package/dist/chat/style.d.ts +0 -4
  388. package/dist/chat/style.d.ts.map +0 -1
  389. package/dist/chat/style.js +0 -302
  390. package/dist/chat.d.ts +0 -14
  391. package/dist/chat.d.ts.map +0 -1
  392. package/dist/chat.js +0 -8
  393. package/dist/fence.d.ts +0 -6
  394. package/dist/fence.d.ts.map +0 -1
  395. package/dist/fence.js +0 -10
  396. package/dist/log.d.ts +0 -5
  397. package/dist/log.d.ts.map +0 -1
  398. package/dist/log.js +0 -14
  399. package/dist/read-side.d.ts +0 -25
  400. package/dist/read-side.d.ts.map +0 -1
  401. package/dist/read-side.js +0 -639
package/dist/reach.js CHANGED
@@ -1,10 +1,11 @@
1
+ import { QueryError } from '@agentshouse/mdmodel';
2
+ import { selected } from './collection.js';
1
3
  import { unheldView } from './conversation.js';
2
4
  import { modelTypes, NO_MODEL } from './model.js';
3
- import { applied, observing } from './observation.js';
4
- import { runtimeError } from './failure.js';
5
- import { failure, undeclared, } from './outcome.js';
5
+ import { changeOf, digestApplied, digestOf, observing, unchanged, } from './observation.js';
6
+ import { runtimeError, uncaught } from './failure.js';
7
+ import { absentBinary, failure, lockedRoom, ROOM_LOCKED, undeclared, UNREADABLE, } from './outcome.js';
6
8
  import { beneath } from './paths.js';
7
- import { reconciled } from './read-side.js';
8
9
  import { moveTarget, readParts, staleWrite, } from './room-draft.js';
9
10
  import { documentKey } from './rows.js';
10
11
  const UNHELD_TYPES = { collection: modelTypes(NO_MODEL), typedSelection: null };
@@ -14,18 +15,37 @@ function unconversed(name) {
14
15
  const NO_CONVERSATION = {
15
16
  list: () => Promise.resolve([]),
16
17
  read: (conversation) => Promise.resolve(unheldView(conversation)),
17
- create: unconversed('conversation.create'),
18
- send: unconversed('conversation.send'),
19
- answer: unconversed('conversation.answer'),
20
- interrupt: unconversed('conversation.interrupt'),
21
- kill: unconversed('conversation.kill'),
22
- cancel: unconversed('conversation.cancel'),
23
- delete: unconversed('conversation.delete'),
18
+ create: unconversed('conversations/create'),
19
+ send: unconversed('conversations/send'),
20
+ answer: unconversed('conversations/interaction/answer'),
21
+ interrupt: unconversed('conversations/interrupt'),
22
+ kill: unconversed('conversations/kill'),
23
+ delete: unconversed('conversations/delete'),
24
+ takeOut: unconversed('conversations/take-out'),
25
+ decide: unconversed('agent-functions/decide'),
26
+ rename: unconversed('conversations/rename'),
27
+ option: unconversed('conversations/option/set'),
28
+ retry: unconversed('conversations/retry'),
29
+ pin: () => Promise.resolve(),
30
+ openTurn: () => Promise.resolve(),
31
+ dismissSuggestion: () => Promise.resolve(),
32
+ attach: () => Promise.resolve(undeclared('conversation.attach')),
33
+ attachment: (link) => Promise.resolve(absentBinary(link)),
24
34
  };
25
35
  const NO_CHAT_DRAFT = {
26
36
  write: () => Promise.resolve(),
37
+ prepend: () => Promise.resolve(),
38
+ append: () => Promise.resolve(),
27
39
  discard: () => Promise.resolve(),
28
40
  };
41
+ function areasOf(reach) {
42
+ return reach.areas ?? [];
43
+ }
44
+ function fenced(reach, room, path) {
45
+ return areasOf(reach).some((area) => area.room === room &&
46
+ beneath(area.path, path) &&
47
+ !area.open.some((open) => beneath(open, path)));
48
+ }
29
49
  export function holds(reach, room) {
30
50
  return (reach === null ||
31
51
  reach.rooms.includes(room) ||
@@ -38,33 +58,33 @@ export function governs(reach, room) {
38
58
  if (wholly(reach, room.locator)) {
39
59
  return { collection: true, selection: room.typedSelection !== null };
40
60
  }
41
- const selected = (path) => room.typedSelection?.roots.some((root) => beneath(root, path)) ?? false;
61
+ const within = (path) => selected({ selection: room.typedSelection }, path);
42
62
  const paths = reach.documents
43
63
  .filter((document) => document.room === room.locator)
44
64
  .map(({ path }) => path);
45
- return { collection: paths.some((path) => !selected(path)), selection: paths.some(selected) };
65
+ return { collection: paths.some((path) => !within(path)), selection: paths.some(within) };
46
66
  }
47
67
  function covers(reach, room, path) {
48
68
  return (reach === null ||
49
- reach.rooms.includes(room) ||
50
- reach.documents.some((document) => document.room === room && document.path === path));
69
+ (!fenced(reach, room, path) &&
70
+ (reach.rooms.includes(room) ||
71
+ reach.documents.some((document) => document.room === room && document.path === path))));
51
72
  }
52
73
  function conversing(reach) {
53
74
  return reach === null || reach.conversations;
54
75
  }
55
- function refers(reach, scope) {
56
- if (reach === null)
57
- return true;
58
- if (scope === null)
59
- return false;
60
- if (scope.kind === 'conversations')
61
- return reach.conversations;
62
- return scope.path === undefined
63
- ? wholly(reach, scope.locator)
64
- : covers(reach, scope.locator, scope.path);
76
+ function roomsAwaited(error, reach) {
77
+ return error.code === 'unknown-type' && reach !== null && reach.rooms.length === 0;
65
78
  }
66
- function unreachedReference(fileRef) {
67
- return { kind: 'absent', code: 'binary-file-absent', fields: { fileRef }, retryable: false };
79
+ async function planInReach(runtime, input, reach, answered) {
80
+ try {
81
+ return await runtime.plan(input, reach);
82
+ }
83
+ catch (error) {
84
+ if (error instanceof QueryError && (answered || roomsAwaited(error, reach)))
85
+ return null;
86
+ throw error;
87
+ }
68
88
  }
69
89
  export function planReach(reach) {
70
90
  return reach === null
@@ -74,11 +94,22 @@ export function planReach(reach) {
74
94
  documents: [
75
95
  ...new Set(reach.documents.map(({ room, path }) => documentKey(room, path))),
76
96
  ].sort(),
97
+ areas: areasOf(reach)
98
+ .map(({ room, path, open }) => ({ room, path, open: [...new Set(open)].sort() }))
99
+ .sort((left, right) => documentKey(left.room, left.path) < documentKey(right.room, right.path) ? -1 : 1),
77
100
  };
78
101
  }
79
102
  function roomsWithin(reach, rooms) {
80
103
  return rooms.filter((room) => wholly(reach, room.locator));
81
104
  }
105
+ function roomsChangeWithin(reach, { changed, removed, order }) {
106
+ const within = {
107
+ changed: changed.filter(([locator]) => wholly(reach, locator)),
108
+ removed,
109
+ order: order === null ? null : order.filter((locator) => wholly(reach, locator)),
110
+ };
111
+ return unchanged(within) ? null : within;
112
+ }
82
113
  function typesWithin(reach, room, types) {
83
114
  if (room === undefined)
84
115
  return structuredClone(UNHELD_TYPES);
@@ -89,7 +120,7 @@ function typesWithin(reach, room, types) {
89
120
  };
90
121
  }
91
122
  function moves(reach, room, move) {
92
- return move === undefined || covers(reach, room, moveTarget(move));
123
+ return move === undefined || covers(reach, move.room ?? room, moveTarget(move));
93
124
  }
94
125
  function draftWithin(reach, draft) {
95
126
  return covers(reach, draft.room, draft.path) && moves(reach, draft.room, draft.parts.move);
@@ -108,12 +139,13 @@ function writeWithin(reach, write) {
108
139
  function savedWithin(reach, room, result) {
109
140
  if (reach === null || result.outcome === 'saved')
110
141
  return result;
111
- const { file, location, referrers, ...refusal } = result;
142
+ const { file, location, line, referrers, ...refusal } = result;
112
143
  const hidden = file !== undefined && !covers(reach, room, file);
113
144
  return {
114
145
  ...refusal,
115
146
  ...(file === undefined || hidden ? {} : { file }),
116
147
  ...(location === undefined || hidden ? {} : { location }),
148
+ ...(line === undefined || hidden ? {} : { line }),
117
149
  ...(referrers === undefined
118
150
  ? {}
119
151
  : { referrers: referrers.filter((path) => covers(reach, room, path)) }),
@@ -122,81 +154,201 @@ function savedWithin(reach, room, result) {
122
154
  function statusWithin(reach, status) {
123
155
  return {
124
156
  ...status,
125
- attentionRequired: status.attentionRequired.filter((entry) => 'refusal' in entry ? wholly(reach, entry.room) : covers(reach, entry.room, entry.path)),
157
+ attentionRequired: status.attentionRequired.filter((entry) => covers(reach, entry.room, entry.path)),
158
+ unsent: status.unsent.filter((change) => covers(reach, change.room, change.path)),
126
159
  };
127
160
  }
128
161
  function queueWithin(reach, room, entries) {
129
- return entries.filter(({ batch }) => batch.changes.every((change) => covers(reach, room, change.path) &&
130
- (change.kind !== 'move' || covers(reach, room, change.to))));
162
+ return entries
163
+ .filter(({ batch }) => batch.changes.every((change) => covers(reach, room, change.path) &&
164
+ (change.kind !== 'move' || covers(reach, room, change.to))))
165
+ .map((entry) => {
166
+ const { between, ...batch } = entry.batch;
167
+ if (between === undefined)
168
+ return entry;
169
+ return wholly(reach, between.source.room) && wholly(reach, between.destination.room)
170
+ ? entry
171
+ : { ...entry, batch };
172
+ });
131
173
  }
132
174
  function unreached(room) {
133
- return runtimeError('room-not-held', `No Local Replica for ${room}`);
175
+ return runtimeError('unknown-room', `Unknown Room ${room}`);
176
+ }
177
+ function lockedError(room) {
178
+ return runtimeError(ROOM_LOCKED, `${room} is locked until House confirms its access`, true);
179
+ }
180
+ function deviceUnreached() {
181
+ return runtimeError('device-unreached', 'Only the panel reaches this device');
134
182
  }
135
- export function narrowed(runtime, given) {
183
+ const ATTACH_REQUIRED = 'attach-required';
184
+ function attachRequired() {
185
+ return runtimeError(ATTACH_REQUIRED, 'App code takes a file in with binaryFile.attach');
186
+ }
187
+ function carriesFile(parts) {
188
+ return parts !== null && typeof parts === 'object' && Object.hasOwn(parts, 'file');
189
+ }
190
+ export function narrowed(runtime, given, port) {
136
191
  const { md, watch } = runtime;
137
192
  let reach = given;
138
193
  const open = new Set();
139
- function following(begin) {
194
+ function following(begin, locking = false) {
140
195
  let stop = begin();
141
196
  const restart = () => {
142
197
  stop();
143
198
  stop = begin();
144
199
  };
145
200
  open.add(restart);
201
+ const unwatch = locking ? runtime.watchLocks(restart) : () => { };
146
202
  return () => {
147
203
  open.delete(restart);
204
+ unwatch();
205
+ stop();
206
+ };
207
+ }
208
+ const unlocking = (rooms, locked) => {
209
+ const held = (rooms ?? []).filter((room) => locked.includes(room));
210
+ return held.length === 0 ? null : { pending: held.map((room) => ({ room, awaits: 'unlock' })) };
211
+ };
212
+ const unlocked = (entries, locked) => entries.filter((entry) => !locked.includes(entry.room));
213
+ const intoLocked = async (held) => {
214
+ for (const move of held) {
215
+ if (move?.room !== undefined && (await runtime.locked(move.room)))
216
+ return true;
217
+ }
218
+ return false;
219
+ };
220
+ function ordered(deliver) {
221
+ let open = true;
222
+ let shown = Promise.resolve();
223
+ return {
224
+ push(making) {
225
+ shown = shown.then(() => making.then((value) => {
226
+ if (open)
227
+ deliver(value);
228
+ }, uncaught), uncaught);
229
+ },
230
+ stop() {
231
+ open = false;
232
+ },
233
+ };
234
+ }
235
+ function unlockedWatch(topic, input, deliver, fail, within) {
236
+ const shown = ordered(deliver);
237
+ const stop = watch(topic, input, (message) => {
238
+ shown.push(runtime.lockedRooms().then((locked) => within(message, locked)));
239
+ }, fail);
240
+ return () => {
241
+ shown.stop();
242
+ stop();
243
+ };
244
+ }
245
+ function lockedFirst(room, start, shut) {
246
+ let open = true;
247
+ let stop = () => { };
248
+ void (async () => {
249
+ const closed = !holds(reach, room) || (await runtime.locked(room));
250
+ if (!open)
251
+ return;
252
+ if (closed)
253
+ shut();
254
+ else
255
+ stop = start();
256
+ })();
257
+ return () => {
258
+ open = false;
148
259
  stop();
149
260
  };
150
261
  }
151
- const within = (topic, input, deliver) => {
262
+ const within = (topic, input, deliver, fail) => {
263
+ if (topic === 'read' && reach !== null) {
264
+ fail(deviceUnreached());
265
+ return () => { };
266
+ }
152
267
  if (topic === 'rooms') {
153
- return following(() => watch(topic, input, (rooms) => deliver(roomsWithin(reach, rooms))));
268
+ return following(() => watch(topic, input, (rooms) => {
269
+ const shown = Array.isArray(rooms)
270
+ ? roomsWithin(reach, rooms)
271
+ : roomsChangeWithin(reach, rooms);
272
+ if (shown !== null)
273
+ deliver(shown);
274
+ }, fail));
275
+ }
276
+ if (topic === 'readiness') {
277
+ return following(() => runtime.watchReadiness(port, reach, deliver));
154
278
  }
155
279
  if (topic === 'sync-status') {
156
- return following(() => watch(topic, input, (status) => deliver(statusWithin(reach, status))));
280
+ return following(() => unlockedWatch(topic, input, deliver, fail, (status, locked) => {
281
+ const shown = statusWithin(reach, status);
282
+ return { ...shown, attentionRequired: unlocked(shown.attentionRequired, locked) };
283
+ }), true);
157
284
  }
158
285
  if (topic === 'room-drafts') {
159
- return following(() => watch(topic, input, (drafts) => deliver(draftsWithin(reach, drafts))));
286
+ return following(() => unlockedWatch(topic, input, deliver, fail, (drafts, locked) => unlocked(draftsWithin(reach, drafts), locked)), true);
160
287
  }
161
288
  if (topic === 'queue') {
162
289
  const room = input[0];
163
- return following(() => {
164
- if (!holds(reach, room)) {
165
- deliver([]);
166
- return () => { };
167
- }
168
- return watch(topic, input, (entries) => deliver(queueWithin(reach, room, entries)));
169
- });
290
+ return following(() => lockedFirst(room, () => watch(topic, input, (entries) => deliver(queueWithin(reach, room, entries)), fail), () => deliver([])), true);
170
291
  }
171
292
  if (topic === 'query') {
172
- const plan = runtime.plan(input[0], reach);
173
293
  let held = null;
294
+ let planning = null;
295
+ const waiting = (result) => {
296
+ const answer = { result, keys: [] };
297
+ if (held === null || changeOf(held, answer) !== null)
298
+ deliver(answer);
299
+ held = digestOf(answer);
300
+ };
174
301
  return following(() => {
175
- let first = true;
176
- return runtime.watchPlan({ ...plan, reach: planReach(reach) }, (message) => {
177
- if (!first) {
178
- held = applied(held, message);
179
- deliver(message);
180
- return;
181
- }
182
- first = false;
183
- if (held === null) {
184
- held = message;
185
- deliver(message);
186
- return;
187
- }
188
- const next = reconciled(held, message);
189
- if (next === null)
302
+ let open = true;
303
+ let stop = () => { };
304
+ if (planning?.reach !== reach) {
305
+ planning = { reach, plan: planInReach(runtime, input[0], reach, held !== null) };
306
+ }
307
+ const planned = planning.plan;
308
+ void (async () => {
309
+ const plan = await planned;
310
+ const locked = await runtime.lockedRooms();
311
+ if (!open)
190
312
  return;
191
- held = next.keyed;
192
- deliver(next.message);
313
+ if (plan === null)
314
+ return waiting({ pending: [] });
315
+ const pending = unlocking(plan.rooms, locked);
316
+ if (pending !== null)
317
+ return waiting(pending);
318
+ let first = true;
319
+ const heard = (message) => {
320
+ if (!first) {
321
+ held = digestApplied(held, message);
322
+ deliver(message);
323
+ return;
324
+ }
325
+ first = false;
326
+ const change = held === null ? message : changeOf(held, message);
327
+ held = digestOf(message);
328
+ if (change !== null)
329
+ deliver(change);
330
+ };
331
+ stop = runtime.watchPlan({ ...plan, locked }, (message) => {
332
+ if (open)
333
+ heard(message);
334
+ }, (error) => {
335
+ if (open)
336
+ fail(error);
337
+ });
338
+ })().catch((error) => {
339
+ if (open)
340
+ fail(error);
193
341
  });
194
- });
342
+ return () => {
343
+ open = false;
344
+ stop();
345
+ };
346
+ }, true);
195
347
  }
196
348
  if (topic === 'conversation') {
197
349
  return following(() => {
198
350
  if (conversing(reach))
199
- return watch(topic, input, deliver);
351
+ return watch(topic, input, deliver, fail);
200
352
  deliver(unheldView(input[0]));
201
353
  return () => { };
202
354
  });
@@ -204,64 +356,163 @@ export function narrowed(runtime, given) {
204
356
  if (topic === 'conversations' || topic === 'agents') {
205
357
  return following(() => {
206
358
  if (conversing(reach))
207
- return watch(topic, input, deliver);
359
+ return watch(topic, input, deliver, fail);
208
360
  deliver([]);
209
361
  return () => { };
210
362
  });
211
363
  }
212
- return watch(topic, input, deliver);
364
+ if (topic === 'figures' ||
365
+ topic === 'holdings' ||
366
+ topic === 'listed-agents' ||
367
+ topic === 'room-list') {
368
+ if (reach === null)
369
+ return watch(topic, input, deliver, fail);
370
+ fail(deviceUnreached());
371
+ return () => { };
372
+ }
373
+ return watch(topic, input, deliver, fail);
213
374
  };
375
+ const files = new Map();
376
+ let handed = 0;
377
+ async function hand(reading) {
378
+ const answer = await reading;
379
+ if (answer.kind !== 'binary')
380
+ return answer;
381
+ handed += 1;
382
+ const file = String(handed);
383
+ files.set(file, answer.file);
384
+ return { ...answer, file };
385
+ }
214
386
  const conversation = () => (conversing(reach) ? md.conversation : NO_CONVERSATION);
215
387
  const chatDraft = () => (conversing(reach) ? md.chatDraft : NO_CHAT_DRAFT);
216
388
  const view = {
217
389
  ...md,
218
- rooms: () => roomsWithin(reach, md.rooms()),
219
- query: ((input) => runtime.query(runtime.plan(input, reach))),
390
+ rooms: () => md.rooms().then((rooms) => roomsWithin(reach, rooms)),
391
+ query: (async (input) => {
392
+ const plan = await planInReach(runtime, input, reach, false);
393
+ if (plan === null)
394
+ return { pending: [] };
395
+ const locked = await runtime.lockedRooms();
396
+ return unlocking(plan.rooms, locked) ?? runtime.query({ ...plan, locked });
397
+ }),
220
398
  search: (input) => runtime.search(input, reach),
221
- types(room) {
399
+ async types(room) {
400
+ if (holds(reach, room) && (await runtime.locked(room)))
401
+ throw lockedError(room);
222
402
  if (wholly(reach, room))
223
403
  return md.types(room);
224
404
  if (!holds(reach, room))
225
405
  return structuredClone(UNHELD_TYPES);
226
- const listed = md.rooms().find(({ locator }) => locator === room);
227
- return typesWithin(reach, listed, md.types(room));
406
+ return typesWithin(reach, await runtime.room(room), await md.types(room));
228
407
  },
229
408
  async save(room, roomDrafts, writtenBy) {
230
409
  if (!holds(reach, room))
231
410
  throw unreached(room);
232
- const outside = roomDrafts.find(({ path }) => !covers(reach, room, path) ||
233
- !moves(reach, room, md.roomDraft.read(room, path)?.parts.move));
411
+ if (await runtime.locked(room)) {
412
+ return { outcome: 'refused', code: ROOM_LOCKED, retryable: true };
413
+ }
414
+ const held = (await Promise.all(roomDrafts.map(({ path }) => md.roomDraft.read(room, path)))).map((draft) => draft?.parts.move);
415
+ const outside = roomDrafts.find(({ path }, at) => !covers(reach, room, path) || !moves(reach, room, held[at]));
234
416
  if (outside !== undefined) {
235
417
  return { outcome: 'refused', code: 'stale', retryable: false, file: outside.path };
236
418
  }
419
+ if (await intoLocked(held))
420
+ return { outcome: 'refused', code: ROOM_LOCKED, retryable: true };
237
421
  return savedWithin(reach, room, await md.save(room, roomDrafts, writtenBy));
238
422
  },
239
- history: (room, path, query) => covers(reach, room, path)
240
- ? md.history(room, path, query)
241
- : Promise.resolve(failure({ kind: 'room-not-readable', locator: room })),
423
+ async saveParts(room, parts, writtenBy) {
424
+ if (!holds(reach, room))
425
+ throw unreached(room);
426
+ const filed = reach === null ? undefined : parts.find(({ parts: one }) => carriesFile(one));
427
+ if (filed !== undefined) {
428
+ return { outcome: 'refused', code: ATTACH_REQUIRED, retryable: false, file: filed.path };
429
+ }
430
+ if (await runtime.locked(room)) {
431
+ return { outcome: 'refused', code: ROOM_LOCKED, retryable: true };
432
+ }
433
+ const held = parts.map(({ path, parts: one }) => readParts(one, path).move);
434
+ if (parts.some(({ path }, at) => !covers(reach, room, path) || !moves(reach, room, held[at]))) {
435
+ throw unreached(room);
436
+ }
437
+ if (await intoLocked(held))
438
+ return { outcome: 'refused', code: ROOM_LOCKED, retryable: true };
439
+ return savedWithin(reach, room, await md.saveParts(room, parts, writtenBy));
440
+ },
441
+ async resolve(room, path, destination) {
442
+ const at = await runtime.located(room, path, destination);
443
+ return at !== null && !covers(reach, at.room, at.path)
444
+ ? UNREADABLE
445
+ : md.resolve(room, path, destination);
446
+ },
447
+ history: async (room, path, query) => !covers(reach, room, path)
448
+ ? failure({ kind: 'room-not-readable', locator: room })
449
+ : (await runtime.locked(room))
450
+ ? lockedRoom(room)
451
+ : md.history(room, path, query),
242
452
  binaryFile: {
243
- read: (room, path, revision) => covers(reach, room, path)
244
- ? md.binaryFile.read(room, path, revision)
245
- : Promise.resolve(failure({ kind: 'room-not-readable', locator: room })),
246
- readReference: (reference) => refers(reach, runtime.referrerScope(reference.referrer))
247
- ? md.binaryFile.readReference(reference)
248
- : Promise.resolve(unreachedReference(reference.fileRef)),
453
+ read: async (room, path, revision) => !covers(reach, room, path)
454
+ ? failure({ kind: 'room-not-readable', locator: room })
455
+ : (await runtime.locked(room))
456
+ ? lockedRoom(room)
457
+ : hand(md.binaryFile.read(room, path, revision)),
458
+ link: async (room, path, revision) => !covers(reach, room, path)
459
+ ? failure({ kind: 'room-not-readable', locator: room })
460
+ : (await runtime.locked(room))
461
+ ? lockedRoom(room)
462
+ : md.binaryFile.link(room, path, revision),
463
+ show: async (room, path, revision) => !covers(reach, room, path)
464
+ ? failure({ kind: 'room-not-readable', locator: room })
465
+ : (await runtime.locked(room))
466
+ ? lockedRoom(room)
467
+ : hand(md.binaryFile.show(room, path, revision)),
468
+ attach: async (room, file) => !wholly(reach, room)
469
+ ? failure({ kind: 'room-not-readable', locator: room })
470
+ : (await runtime.locked(room))
471
+ ? lockedRoom(room)
472
+ : md.binaryFile.attach(room, file),
473
+ async part(file, part) {
474
+ const key = files.get(file);
475
+ if (key === undefined)
476
+ return null;
477
+ const bytes = await md.binaryFile.part(key, part);
478
+ if (bytes === null)
479
+ files.delete(file);
480
+ return bytes;
481
+ },
249
482
  },
250
- reached: (room, roomVersion, path) => (path === undefined ? wholly(reach, room) : covers(reach, room, path)) &&
251
- md.reached(room, roomVersion, path),
252
- log: () => (reach === null ? md.log() : ''),
483
+ reached: async (room, roomVersion, path) => (path === undefined ? wholly(reach, room) : covers(reach, room, path)) &&
484
+ !(await runtime.locked(room)) &&
485
+ (await md.reached(room, roomVersion, path)),
486
+ cleanUp: () => (reach === null ? md.cleanUp() : Promise.reject(deviceUnreached())),
487
+ unsent: () => (reach === null ? md.unsent() : Promise.reject(deviceUnreached())),
488
+ reset: () => (reach === null ? md.reset() : Promise.reject(deviceUnreached())),
489
+ readAgain: () => (reach === null ? md.readAgain() : Promise.reject(deviceUnreached())),
253
490
  roomDraft: {
254
- list: () => md.roomDraft.list().then((drafts) => draftsWithin(reach, drafts)),
255
- read: (room, path) => covers(reach, room, path) ? shownDraft(reach, md.roomDraft.read(room, path)) : null,
256
- async write(room, path, revision, parts) {
257
- if (!covers(reach, room, path) || !moves(reach, room, readParts(parts).move)) {
491
+ list: async () => unlocked(draftsWithin(reach, await md.roomDraft.list()), await runtime.lockedRooms()),
492
+ async read(room, path) {
493
+ if (!covers(reach, room, path))
494
+ return null;
495
+ if (await runtime.locked(room))
496
+ throw lockedError(room);
497
+ return shownDraft(reach, await md.roomDraft.read(room, path));
498
+ },
499
+ async write(room, path, revision, parts, next) {
500
+ if (reach !== null && carriesFile(parts))
501
+ throw attachRequired();
502
+ const move = readParts(parts, path).move;
503
+ if (!covers(reach, room, path) || !moves(reach, room, move))
258
504
  throw unreached(room);
259
- }
260
- return writeWithin(reach, await md.roomDraft.write(room, path, revision, parts));
505
+ if (await runtime.locked(room))
506
+ throw lockedError(room);
507
+ if (await intoLocked([move]))
508
+ throw lockedError(move.room);
509
+ return writeWithin(reach, await md.roomDraft.write(room, path, revision, parts, next));
261
510
  },
262
511
  async discard(room, path, revision) {
263
512
  if (!covers(reach, room, path))
264
513
  return staleWrite(null);
514
+ if (await runtime.locked(room))
515
+ throw lockedError(room);
265
516
  return writeWithin(reach, await md.roomDraft.discard(room, path, revision));
266
517
  },
267
518
  },
@@ -270,14 +521,25 @@ export function narrowed(runtime, given) {
270
521
  read: (id, options) => conversation().read(id, options),
271
522
  create: (input) => conversation().create(input),
272
523
  send: (id, operation, draft) => conversation().send(id, operation, draft),
273
- answer: (id, answer) => conversation().answer(id, answer),
274
- interrupt: (id) => conversation().interrupt(id),
275
- kill: (id) => conversation().kill(id),
276
- cancel: (id, turn) => conversation().cancel(id, turn),
277
- delete: (id) => conversation().delete(id),
524
+ answer: (id, answer, operation) => conversation().answer(id, answer, operation),
525
+ interrupt: (id, operation) => conversation().interrupt(id, operation),
526
+ kill: (id, operation) => conversation().kill(id, operation),
527
+ delete: (id, operation) => conversation().delete(id, operation),
528
+ takeOut: (id, message, operation) => conversation().takeOut(id, message, operation),
529
+ decide: (request, decision, operation) => conversation().decide(request, decision, operation),
530
+ rename: (id, name, operation) => conversation().rename(id, name, operation),
531
+ option: (id, option, value, operation) => conversation().option(id, option, value, operation),
532
+ retry: (id, message, operation) => conversation().retry(id, message, operation),
533
+ pin: (figures) => conversation().pin(figures),
534
+ openTurn: (id, turn) => conversation().openTurn(id, turn),
535
+ dismissSuggestion: (id, suggestion) => conversation().dismissSuggestion(id, suggestion),
536
+ attach: (file) => conversation().attach(file),
537
+ attachment: (link) => conversation().attachment(link),
278
538
  },
279
539
  chatDraft: {
280
540
  write: (id, draft, revision) => chatDraft().write(id, draft, revision),
541
+ prepend: (id, draft) => chatDraft().prepend(id, draft),
542
+ append: (id, draft) => chatDraft().append(id, draft),
281
543
  discard: (id) => chatDraft().discard(id),
282
544
  },
283
545
  observe: observing(within),