@agentshouse/mdruntime 0.1.0-c94cfdbdb6f68 → 0.1.0-c95f0f04f2113

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 (361) hide show
  1. package/README.md +19 -16
  2. package/dist/acceptance.d.ts +6 -0
  3. package/dist/acceptance.d.ts.map +1 -0
  4. package/dist/acceptance.js +389 -0
  5. package/dist/actions.d.ts +20 -0
  6. package/dist/actions.d.ts.map +1 -0
  7. package/dist/actions.js +248 -0
  8. package/dist/adapter.d.ts +139 -85
  9. package/dist/adapter.d.ts.map +1 -1
  10. package/dist/attached-model.d.ts +7 -10
  11. package/dist/attached-model.d.ts.map +1 -1
  12. package/dist/attached-model.js +40 -41
  13. package/dist/binary.d.ts +0 -5
  14. package/dist/binary.d.ts.map +1 -1
  15. package/dist/binary.js +1 -1
  16. package/dist/bootstrap.d.ts +30 -11
  17. package/dist/bootstrap.d.ts.map +1 -1
  18. package/dist/bootstrap.js +319 -111
  19. package/dist/cell.d.ts +6 -0
  20. package/dist/cell.d.ts.map +1 -0
  21. package/dist/cell.js +9 -0
  22. package/dist/chain.d.ts +12 -0
  23. package/dist/chain.d.ts.map +1 -0
  24. package/dist/chain.js +64 -0
  25. package/dist/change-set.d.ts +9 -16
  26. package/dist/change-set.d.ts.map +1 -1
  27. package/dist/change-set.js +11 -89
  28. package/dist/change.d.ts +32 -1
  29. package/dist/change.d.ts.map +1 -1
  30. package/dist/change.js +10 -1
  31. package/dist/chat-types.d.ts +3 -0
  32. package/dist/chat-types.d.ts.map +1 -0
  33. package/dist/chat-types.js +2 -0
  34. package/dist/client.d.ts +28 -11
  35. package/dist/client.d.ts.map +1 -1
  36. package/dist/client.js +83 -7
  37. package/dist/collection.d.ts +11 -21
  38. package/dist/collection.d.ts.map +1 -1
  39. package/dist/collection.js +39 -321
  40. package/dist/compile.d.ts +16 -0
  41. package/dist/compile.d.ts.map +1 -0
  42. package/dist/compile.js +717 -0
  43. package/dist/connection.d.ts +7 -137
  44. package/dist/connection.d.ts.map +1 -1
  45. package/dist/connection.js +98 -3600
  46. package/dist/context.d.ts +2 -0
  47. package/dist/context.d.ts.map +1 -0
  48. package/dist/{chat/context.js → context.js} +1 -4
  49. package/dist/conversation-row.d.ts +33 -0
  50. package/dist/conversation-row.d.ts.map +1 -0
  51. package/dist/conversation-row.js +47 -0
  52. package/dist/conversation.d.ts +36 -18
  53. package/dist/conversation.d.ts.map +1 -1
  54. package/dist/conversation.js +570 -287
  55. package/dist/digest.d.ts +7 -0
  56. package/dist/digest.d.ts.map +1 -0
  57. package/dist/digest.js +113 -0
  58. package/dist/discovery.d.ts +10 -0
  59. package/dist/discovery.d.ts.map +1 -0
  60. package/dist/discovery.js +175 -0
  61. package/dist/durability.d.ts +7 -4
  62. package/dist/durability.d.ts.map +1 -1
  63. package/dist/durability.js +5 -0
  64. package/dist/entry.d.ts +5 -2
  65. package/dist/entry.d.ts.map +1 -1
  66. package/dist/entry.js +33 -29
  67. package/dist/figures.d.ts +48 -0
  68. package/dist/figures.d.ts.map +1 -0
  69. package/dist/figures.js +197 -0
  70. package/dist/file.d.ts +2 -19
  71. package/dist/file.d.ts.map +1 -1
  72. package/dist/file.js +7 -122
  73. package/dist/host.d.ts +4 -7
  74. package/dist/host.d.ts.map +1 -1
  75. package/dist/host.js +67 -39
  76. package/dist/identity.d.ts +0 -2
  77. package/dist/identity.d.ts.map +1 -1
  78. package/dist/identity.js +0 -6
  79. package/dist/index.d.ts +30 -13
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +10 -2
  82. package/dist/install.d.ts +56 -0
  83. package/dist/install.d.ts.map +1 -0
  84. package/dist/install.js +560 -0
  85. package/dist/job.d.ts +33 -0
  86. package/dist/job.d.ts.map +1 -0
  87. package/dist/job.js +54 -0
  88. package/dist/lifecycle.d.ts +7 -0
  89. package/dist/lifecycle.d.ts.map +1 -0
  90. package/dist/lifecycle.js +267 -0
  91. package/dist/listing.d.ts +53 -0
  92. package/dist/listing.d.ts.map +1 -0
  93. package/dist/listing.js +175 -0
  94. package/dist/live.d.ts +23 -0
  95. package/dist/live.d.ts.map +1 -0
  96. package/dist/live.js +165 -0
  97. package/dist/locks.d.ts +9 -0
  98. package/dist/locks.d.ts.map +1 -0
  99. package/dist/locks.js +61 -0
  100. package/dist/log.d.ts +1 -0
  101. package/dist/log.d.ts.map +1 -1
  102. package/dist/lost.d.ts +5 -0
  103. package/dist/lost.d.ts.map +1 -0
  104. package/dist/lost.js +61 -0
  105. package/dist/media.d.ts +8 -0
  106. package/dist/media.d.ts.map +1 -0
  107. package/dist/media.js +218 -0
  108. package/dist/memory.d.ts +14 -6
  109. package/dist/memory.d.ts.map +1 -1
  110. package/dist/memory.js +237 -105
  111. package/dist/messages.d.ts +52 -0
  112. package/dist/messages.d.ts.map +1 -0
  113. package/dist/messages.js +168 -0
  114. package/dist/model.d.ts +8 -10
  115. package/dist/model.d.ts.map +1 -1
  116. package/dist/model.js +6 -18
  117. package/dist/models.d.ts +14 -0
  118. package/dist/models.d.ts.map +1 -0
  119. package/dist/models.js +193 -0
  120. package/dist/name.d.ts +4 -0
  121. package/dist/name.d.ts.map +1 -0
  122. package/dist/name.js +122 -0
  123. package/dist/observation.d.ts +15 -2
  124. package/dist/observation.d.ts.map +1 -1
  125. package/dist/observation.js +91 -2
  126. package/dist/opened.d.ts +22 -0
  127. package/dist/opened.d.ts.map +1 -0
  128. package/dist/opened.js +107 -0
  129. package/dist/opening.d.ts +4 -0
  130. package/dist/opening.d.ts.map +1 -0
  131. package/dist/opening.js +56 -0
  132. package/dist/outcome.d.ts +41 -8
  133. package/dist/outcome.d.ts.map +1 -1
  134. package/dist/outcome.js +41 -2
  135. package/dist/overlay.d.ts +3 -5
  136. package/dist/overlay.d.ts.map +1 -1
  137. package/dist/overlay.js +98 -172
  138. package/dist/{chat/parts.d.ts → parts.d.ts} +11 -9
  139. package/dist/parts.d.ts.map +1 -0
  140. package/dist/{chat/parts.js → parts.js} +45 -46
  141. package/dist/paths.d.ts +1 -0
  142. package/dist/paths.d.ts.map +1 -1
  143. package/dist/paths.js +4 -0
  144. package/dist/preparation.d.ts +20 -0
  145. package/dist/preparation.d.ts.map +1 -0
  146. package/dist/preparation.js +226 -0
  147. package/dist/queries.d.ts +16 -0
  148. package/dist/queries.d.ts.map +1 -0
  149. package/dist/queries.js +272 -0
  150. package/dist/query.d.ts +21 -206
  151. package/dist/query.d.ts.map +1 -1
  152. package/dist/query.js +3 -493
  153. package/dist/{chat/questions.d.ts → questions.d.ts} +3 -2
  154. package/dist/questions.d.ts.map +1 -0
  155. package/dist/{chat/questions.js → questions.js} +8 -1
  156. package/dist/reach.d.ts +20 -9
  157. package/dist/reach.d.ts.map +1 -1
  158. package/dist/reach.js +278 -103
  159. package/dist/react.d.ts +6 -2
  160. package/dist/react.d.ts.map +1 -1
  161. package/dist/{chat/read.d.ts → read.d.ts} +2 -0
  162. package/dist/read.d.ts.map +1 -0
  163. package/dist/{chat/read.js → read.js} +4 -0
  164. package/dist/readiness.d.ts +14 -17
  165. package/dist/readiness.d.ts.map +1 -1
  166. package/dist/readiness.js +2 -8
  167. package/dist/reads.d.ts +35 -10
  168. package/dist/reads.d.ts.map +1 -1
  169. package/dist/reads.js +122 -84
  170. package/dist/recovery.d.ts +3 -2
  171. package/dist/recovery.d.ts.map +1 -1
  172. package/dist/recovery.js +43 -8
  173. package/dist/refresh.d.ts +18 -15
  174. package/dist/refresh.d.ts.map +1 -1
  175. package/dist/refresh.js +143 -58
  176. package/dist/refusal.d.ts +5 -0
  177. package/dist/refusal.d.ts.map +1 -0
  178. package/dist/refusal.js +56 -0
  179. package/dist/replica.d.ts +54 -36
  180. package/dist/replica.d.ts.map +1 -1
  181. package/dist/replica.js +313 -188
  182. package/dist/room-draft.d.ts +25 -6
  183. package/dist/room-draft.d.ts.map +1 -1
  184. package/dist/room-draft.js +158 -71
  185. package/dist/room-drafts.d.ts +24 -0
  186. package/dist/room-drafts.d.ts.map +1 -0
  187. package/dist/room-drafts.js +216 -0
  188. package/dist/rows.d.ts +31 -31
  189. package/dist/rows.d.ts.map +1 -1
  190. package/dist/rows.js +59 -123
  191. package/dist/runtime.d.ts +42 -0
  192. package/dist/runtime.d.ts.map +1 -0
  193. package/dist/runtime.js +1 -0
  194. package/dist/save.d.ts +52 -14
  195. package/dist/save.d.ts.map +1 -1
  196. package/dist/save.js +518 -185
  197. package/dist/saves.d.ts +70 -0
  198. package/dist/saves.d.ts.map +1 -0
  199. package/dist/saves.js +1004 -0
  200. package/dist/search.d.ts +12 -95
  201. package/dist/search.d.ts.map +1 -1
  202. package/dist/search.js +10 -266
  203. package/dist/seed.d.ts +11 -2
  204. package/dist/seed.d.ts.map +1 -1
  205. package/dist/seed.js +71 -0
  206. package/dist/shared.d.ts +7 -3
  207. package/dist/shared.d.ts.map +1 -1
  208. package/dist/shared.js +119 -66
  209. package/dist/shown.d.ts +31 -0
  210. package/dist/shown.d.ts.map +1 -0
  211. package/dist/shown.js +294 -0
  212. package/dist/socket/admission.d.ts +7 -0
  213. package/dist/socket/admission.d.ts.map +1 -0
  214. package/dist/socket/admission.js +39 -0
  215. package/dist/socket/answers.d.ts +6 -0
  216. package/dist/socket/answers.d.ts.map +1 -0
  217. package/dist/socket/answers.js +20 -0
  218. package/dist/socket/lanes.d.ts +20 -0
  219. package/dist/socket/lanes.d.ts.map +1 -0
  220. package/dist/socket/lanes.js +77 -0
  221. package/dist/socket/reads.d.ts +10 -0
  222. package/dist/socket/reads.d.ts.map +1 -0
  223. package/dist/socket/reads.js +31 -0
  224. package/dist/socket/rooms.d.ts +43 -0
  225. package/dist/socket/rooms.d.ts.map +1 -0
  226. package/dist/socket/rooms.js +90 -0
  227. package/dist/socket/session.d.ts +39 -0
  228. package/dist/socket/session.d.ts.map +1 -0
  229. package/dist/socket/session.js +23 -0
  230. package/dist/socket.d.ts +70 -0
  231. package/dist/socket.d.ts.map +1 -0
  232. package/dist/socket.js +175 -0
  233. package/dist/source.d.ts +61 -0
  234. package/dist/source.d.ts.map +1 -0
  235. package/dist/source.js +81 -0
  236. package/dist/status.d.ts +4 -5
  237. package/dist/status.d.ts.map +1 -1
  238. package/dist/status.js +3 -7
  239. package/dist/store/fences.d.ts +16 -0
  240. package/dist/store/fences.d.ts.map +1 -0
  241. package/dist/store/fences.js +26 -0
  242. package/dist/store/installs.d.ts +30 -0
  243. package/dist/store/installs.d.ts.map +1 -0
  244. package/dist/store/installs.js +34 -0
  245. package/dist/store/readiness.d.ts +10 -0
  246. package/dist/store/readiness.d.ts.map +1 -0
  247. package/dist/store/readiness.js +52 -0
  248. package/dist/store/records.d.ts +212 -0
  249. package/dist/store/records.d.ts.map +1 -0
  250. package/dist/store/records.js +408 -0
  251. package/dist/store/room-held.d.ts +8 -0
  252. package/dist/store/room-held.d.ts.map +1 -0
  253. package/dist/store/room-held.js +17 -0
  254. package/dist/store/space.d.ts +11 -0
  255. package/dist/store/space.d.ts.map +1 -0
  256. package/dist/store/space.js +43 -0
  257. package/dist/store/watchers.d.ts +17 -0
  258. package/dist/store/watchers.d.ts.map +1 -0
  259. package/dist/store/watchers.js +57 -0
  260. package/dist/store/writes.d.ts +24 -0
  261. package/dist/store/writes.d.ts.map +1 -0
  262. package/dist/store/writes.js +125 -0
  263. package/dist/store.d.ts +106 -164
  264. package/dist/store.d.ts.map +1 -1
  265. package/dist/store.js +136 -752
  266. package/dist/stored.d.ts +156 -0
  267. package/dist/stored.d.ts.map +1 -0
  268. package/dist/stored.js +637 -0
  269. package/dist/surface.d.ts +86 -0
  270. package/dist/surface.d.ts.map +1 -0
  271. package/dist/surface.js +217 -0
  272. package/dist/sync.d.ts +20 -0
  273. package/dist/sync.d.ts.map +1 -0
  274. package/dist/sync.js +531 -0
  275. package/dist/tables.d.ts +407 -0
  276. package/dist/tables.d.ts.map +1 -0
  277. package/dist/tables.js +724 -0
  278. package/dist/tallies.d.ts +18 -0
  279. package/dist/tallies.d.ts.map +1 -0
  280. package/dist/tallies.js +129 -0
  281. package/dist/transfer.d.ts +0 -5
  282. package/dist/transfer.d.ts.map +1 -1
  283. package/dist/turn.d.ts +4 -2
  284. package/dist/turn.d.ts.map +1 -1
  285. package/dist/turn.js +46 -22
  286. package/dist/validation.d.ts +29 -0
  287. package/dist/validation.d.ts.map +1 -0
  288. package/dist/validation.js +104 -0
  289. package/dist/windows.d.ts +1 -16
  290. package/dist/windows.d.ts.map +1 -1
  291. package/dist/windows.js +2 -79
  292. package/dist/wire.d.ts +33 -6
  293. package/dist/wire.d.ts.map +1 -1
  294. package/dist/wire.js +11 -2
  295. package/package.json +18 -19
  296. package/AGENTS.md +0 -299
  297. package/dist/chat/acts.d.ts +0 -33
  298. package/dist/chat/acts.d.ts.map +0 -1
  299. package/dist/chat/acts.js +0 -36
  300. package/dist/chat/context.d.ts +0 -3
  301. package/dist/chat/context.d.ts.map +0 -1
  302. package/dist/chat/earlier.d.ts +0 -10
  303. package/dist/chat/earlier.d.ts.map +0 -1
  304. package/dist/chat/earlier.js +0 -54
  305. package/dist/chat/input.d.ts +0 -9
  306. package/dist/chat/input.d.ts.map +0 -1
  307. package/dist/chat/input.js +0 -99
  308. package/dist/chat/kit.d.ts +0 -61
  309. package/dist/chat/kit.d.ts.map +0 -1
  310. package/dist/chat/kit.js +0 -123
  311. package/dist/chat/lasting.d.ts +0 -6
  312. package/dist/chat/lasting.d.ts.map +0 -1
  313. package/dist/chat/lasting.js +0 -19
  314. package/dist/chat/list.d.ts +0 -27
  315. package/dist/chat/list.d.ts.map +0 -1
  316. package/dist/chat/list.js +0 -99
  317. package/dist/chat/markdown.d.ts +0 -20
  318. package/dist/chat/markdown.d.ts.map +0 -1
  319. package/dist/chat/markdown.js +0 -188
  320. package/dist/chat/message.d.ts +0 -6
  321. package/dist/chat/message.d.ts.map +0 -1
  322. package/dist/chat/message.js +0 -192
  323. package/dist/chat/messages.d.ts +0 -22
  324. package/dist/chat/messages.d.ts.map +0 -1
  325. package/dist/chat/messages.js +0 -155
  326. package/dist/chat/name.d.ts +0 -6
  327. package/dist/chat/name.d.ts.map +0 -1
  328. package/dist/chat/name.js +0 -37
  329. package/dist/chat/opened.d.ts +0 -11
  330. package/dist/chat/opened.d.ts.map +0 -1
  331. package/dist/chat/opened.js +0 -17
  332. package/dist/chat/overlay.d.ts +0 -31
  333. package/dist/chat/overlay.d.ts.map +0 -1
  334. package/dist/chat/overlay.js +0 -97
  335. package/dist/chat/parts.d.ts.map +0 -1
  336. package/dist/chat/process.d.ts +0 -20
  337. package/dist/chat/process.d.ts.map +0 -1
  338. package/dist/chat/process.js +0 -32
  339. package/dist/chat/provider.d.ts +0 -63
  340. package/dist/chat/provider.d.ts.map +0 -1
  341. package/dist/chat/provider.js +0 -628
  342. package/dist/chat/question.d.ts +0 -9
  343. package/dist/chat/question.d.ts.map +0 -1
  344. package/dist/chat/question.js +0 -69
  345. package/dist/chat/questions.d.ts.map +0 -1
  346. package/dist/chat/read.d.ts.map +0 -1
  347. package/dist/chat/ready.d.ts +0 -34
  348. package/dist/chat/ready.d.ts.map +0 -1
  349. package/dist/chat/ready.js +0 -91
  350. package/dist/chat/style.d.ts +0 -4
  351. package/dist/chat/style.d.ts.map +0 -1
  352. package/dist/chat/style.js +0 -302
  353. package/dist/chat.d.ts +0 -14
  354. package/dist/chat.d.ts.map +0 -1
  355. package/dist/chat.js +0 -8
  356. package/dist/fence.d.ts +0 -6
  357. package/dist/fence.d.ts.map +0 -1
  358. package/dist/fence.js +0 -10
  359. package/dist/read-side.d.ts +0 -27
  360. package/dist/read-side.d.ts.map +0 -1
  361. package/dist/read-side.js +0 -824
@@ -1,17 +1,35 @@
1
+ import { cursorOf, decodedCursor } from '@agentshouse/mdmodel';
1
2
  import { conversationChangeSet } from './change-set.js';
3
+ import { heldFacts } from './conversation-row.js';
2
4
  import { runtimeError } from './failure.js';
3
- import { declaredFile } from './file.js';
5
+ import { refusedForSpace } from './durability.js';
4
6
  import { changeBatchIdentity, chatDraftRevision } from './identity.js';
5
7
  import { OFFLINE } from './outcome.js';
6
- import { capped, cursorOf, decodedCursor, FROM_START, messageOrder, precedes } from './windows.js';
8
+ import { jobsOf, runningLine } from './parts.js';
9
+ import { openQuestion } from './questions.js';
10
+ import { untilAnswered } from './recovery.js';
11
+ import { capped, FROM_START, messageOrder, precedes } from './windows.js';
7
12
  const QUIET = {
8
13
  turn: null,
9
14
  questions: [],
10
15
  process: null,
11
16
  commands: [],
12
17
  options: [],
18
+ starting: null,
19
+ subagents: [],
20
+ link: null,
21
+ deliveryUnknown: [],
13
22
  };
14
- const FACTS = ['questions', 'process', 'commands', 'options'];
23
+ const FACTS = [
24
+ 'questions',
25
+ 'process',
26
+ 'commands',
27
+ 'options',
28
+ 'starting',
29
+ 'subagents',
30
+ 'link',
31
+ 'deliveryUnknown',
32
+ ];
15
33
  export function unheldView(conversation) {
16
34
  return {
17
35
  conversation,
@@ -19,13 +37,22 @@ export function unheldView(conversation) {
19
37
  turn: null,
20
38
  questions: [],
21
39
  process: null,
40
+ processEnded: null,
22
41
  commands: [],
23
42
  options: [],
43
+ starting: null,
44
+ subagents: [],
45
+ link: null,
46
+ deliveryUnknown: [],
24
47
  chatDraft: null,
48
+ jobs: [],
25
49
  };
26
50
  }
27
51
  export const SESSION_STARTED = 'Session started';
28
52
  const NEW_CHAT = 'new:';
53
+ export const HISTORY_CONVERSATIONS = 64;
54
+ // A row is read at a glance and each telling clones the list to every observing tab, while a running line changes at each Kit flush.
55
+ const ROW_INTERVAL_MS = 1_000;
29
56
  export function newChat(agent) {
30
57
  return `${NEW_CHAT}${agent}`;
31
58
  }
@@ -192,9 +219,22 @@ function askedOf(options) {
192
219
  }
193
220
  return { limit: capped(limit), after, before: beforeOf(after), total: total === true };
194
221
  }
195
- const EXPIRED = { kind: 'rejected', code: 'expired', fields: {}, retryable: false };
222
+ function phraseOf(text) {
223
+ if (text === undefined)
224
+ return null;
225
+ if (typeof text !== 'string' || text === '') {
226
+ throw runtimeError('invalid-text', 'a conversation is found by a phrase');
227
+ }
228
+ return text;
229
+ }
230
+ const DURABLE_WRITE = {
231
+ kind: 'rejected',
232
+ code: 'durable-write',
233
+ fields: {},
234
+ retryable: true,
235
+ };
196
236
  function unwritten(draft) {
197
- return draft.text === '' && draft.files.length === 0;
237
+ return draft.text === '';
198
238
  }
199
239
  function pending(draft) {
200
240
  return {
@@ -210,10 +250,7 @@ function withSubmitted(draft, submitted) {
210
250
  return submitted.length === 0 ? unsent : { ...unsent, submitted };
211
251
  }
212
252
  function prepended(draft, said) {
213
- return {
214
- text: [said.text, draft.text].filter((one) => one !== '').join('\n'),
215
- files: [...said.files, ...draft.files],
216
- };
253
+ return { text: [said.text, draft.text].filter((one) => one !== '').join('\n') };
217
254
  }
218
255
  function returned(draft, submission, others) {
219
256
  return withSubmitted({
@@ -223,10 +260,6 @@ function returned(draft, submission, others) {
223
260
  }, others);
224
261
  }
225
262
  export function createConversationStore(deps) {
226
- const stored = new Set();
227
- const reads = new Map();
228
- const partials = new Set();
229
- const drafts = new Map();
230
263
  const sending = new Map();
231
264
  const behind = new Set();
232
265
  const catching = new Map();
@@ -234,21 +267,27 @@ export function createConversationStore(deps) {
234
267
  const lives = new Map();
235
268
  const unsent = new Map();
236
269
  const views = new Map();
237
- const listing = new Map();
270
+ const heardNewest = new Map();
271
+ let relisting = null;
238
272
  const creations = new Set();
239
273
  const listWatchers = new Set();
274
+ const unlisted = new Set();
240
275
  let listKnown = false;
276
+ let listOwed = false;
277
+ let listTelling = false;
278
+ let rowTimer = null;
279
+ const closing = new AbortController();
241
280
  const agentWatchers = new Set();
242
- let agentNames = new Map();
243
- let retiredAgents = new Set();
244
- let agentsRead = null;
281
+ const unnamed = new Set();
282
+ let agentsShown = null;
283
+ let listedAgents = null;
284
+ let agentsReading = false;
245
285
  let replicated = new Set();
246
286
  let handedReplicated;
247
287
  function liveOf(conversation) {
248
288
  return lives.get(conversation) ?? QUIET;
249
289
  }
250
- function viewOf(conversation, held, frame) {
251
- const draft = drafts.get(conversation);
290
+ function viewOf(conversation, held, frame, draft, ended, marked) {
252
291
  const facts = liveOf(conversation);
253
292
  return {
254
293
  conversation,
@@ -262,11 +301,17 @@ export function createConversationStore(deps) {
262
301
  turn: structuredClone(facts.turn),
263
302
  questions: structuredClone(facts.questions),
264
303
  process: facts.process === null ? null : { ...facts.process },
304
+ processEnded: ended,
265
305
  commands: structuredClone(facts.commands),
266
306
  options: structuredClone(facts.options),
267
- chatDraft: draft === undefined || unwritten(draft)
268
- ? null
269
- : { text: draft.text, files: [...draft.files], revision: draft.revision },
307
+ starting: facts.starting,
308
+ subagents: structuredClone(facts.subagents),
309
+ link: facts.link === null ? null : { ...facts.link },
310
+ deliveryUnknown: [...facts.deliveryUnknown],
311
+ chatDraft: draft === null || unwritten(draft) ? null : { text: draft.text, revision: draft.revision },
312
+ jobs: facts.process?.running === false
313
+ ? []
314
+ : jobsOf(marked.filter(({ at }) => ended === null || at > ended), facts.turn),
270
315
  };
271
316
  }
272
317
  function emit(conversation) {
@@ -278,7 +323,7 @@ export function createConversationStore(deps) {
278
323
  const ready = [...view.watchers].filter((watcher) => watcher.ready);
279
324
  if (ready.length === 0)
280
325
  continue;
281
- const value = viewOf(conversation, view.window, sharedFrame(view, view.window));
326
+ const value = viewOf(conversation, view.window, sharedFrame(view, view.window), view.draft, view.ended, view.marked);
282
327
  for (const watcher of ready)
283
328
  watcher.notify(value);
284
329
  }
@@ -288,11 +333,13 @@ export function createConversationStore(deps) {
288
333
  }
289
334
  async function slide(local, conversation, arriving) {
290
335
  let count;
336
+ let marked;
291
337
  for (const view of views.values()) {
292
338
  if (view.conversation !== conversation || view.window === null)
293
339
  continue;
294
340
  if (arriving === null) {
295
341
  view.window = [];
342
+ view.marked = [];
296
343
  view.more = false;
297
344
  if (view.total !== null)
298
345
  view.total = 0;
@@ -302,109 +349,250 @@ export function createConversationStore(deps) {
302
349
  const next = windowOf(view.window, joining, view.asked.limit);
303
350
  view.window = next.messages;
304
351
  view.more ||= next.dropped;
352
+ if (arriving.length > 0) {
353
+ marked ??= await local.jobMarkers(conversation);
354
+ view.marked = marked;
355
+ }
305
356
  if (view.total !== null && arriving.length > 0) {
306
357
  count ??= await local.messageCount(conversation);
307
358
  view.total = count;
308
359
  }
309
360
  }
310
361
  }
311
- function changesOf(conversation, arriving) {
312
- return arriving.map(({ identity, text }) => ({ conversation, identity, text }));
313
- }
314
362
  function stateOf(conversation) {
315
363
  const facts = liveOf(conversation);
316
364
  if (facts.questions.length > 0)
317
365
  return 'asking';
318
366
  return facts.turn === null ? 'idle' : 'working';
319
367
  }
320
- function entries() {
321
- return [...listing.values()].map((entry) => ({
368
+ function rowOf(conversation) {
369
+ const turn = lives.get(conversation)?.turn ?? null;
370
+ return {
371
+ state: stateOf(conversation),
372
+ started: turn?.started ?? null,
373
+ line: turn === null ? null : runningLine(turn.parts),
374
+ question: openQuestion(liveOf(conversation).questions),
375
+ };
376
+ }
377
+ async function entries() {
378
+ const list = await heldList();
379
+ if (list === null)
380
+ return null;
381
+ const held = list.length === 0
382
+ ? []
383
+ : await deps.durably((local) => local.conversationRows(list.map(({ conversation }) => conversation)));
384
+ const rows = new Map(held.map((one) => [one.conversation, one]));
385
+ return list.map((entry) => ({
322
386
  conversation: entry.conversation,
323
- agent: { identity: entry.agent, name: agentNames.get(entry.agent) ?? null },
387
+ agent: { identity: entry.agent, name: entry.agentName, retired: entry.agentRetired },
324
388
  name: entry.name,
325
389
  created: entry.created,
326
390
  finished: entry.finished,
327
391
  outcome: entry.outcome,
328
- state: stateOf(entry.conversation),
329
- started: lives.get(entry.conversation)?.turn?.started ?? null,
392
+ waiting: entry.waiting,
393
+ ...rowOf(entry.conversation),
394
+ ...heldFacts(rows.get(entry.conversation), entry),
330
395
  }));
331
396
  }
397
+ async function heldList() {
398
+ const stored = (await (await deps.local()).facts()).conversationList;
399
+ if (stored !== null)
400
+ return stored;
401
+ return !listKnown && deps.stream() !== undefined && deps.online() ? null : [];
402
+ }
403
+ async function endedIn(conversation) {
404
+ const list = await heldList();
405
+ return list?.find((entry) => entry.conversation === conversation)?.processEnded ?? null;
406
+ }
407
+ function listChanged(change) {
408
+ void deps
409
+ .durably(async (local) => {
410
+ const { conversationList, conversationCursor } = await local.facts();
411
+ const kept = change.replacing ? [] : (conversationList ?? []);
412
+ const held = new Map(kept.map((entry) => [entry.conversation, entry]));
413
+ for (const entry of change.set) {
414
+ held.set(entry.conversation, { ...entry });
415
+ creations.delete(entry.conversation);
416
+ }
417
+ if (change.gone !== null)
418
+ held.delete(change.gone);
419
+ const list = [...held.values()];
420
+ if (JSON.stringify(list) !== JSON.stringify(conversationList)) {
421
+ await local.fact('conversationList', list);
422
+ }
423
+ if (change.cursor !== undefined) {
424
+ if (change.cursor !== conversationCursor) {
425
+ await local.fact('conversationCursor', change.cursor);
426
+ }
427
+ deps.stream()?.hold(change.cursor);
428
+ }
429
+ return list;
430
+ })
431
+ .catch(async () => (await (await deps.local()).facts()).conversationList ?? [])
432
+ .then((list) => {
433
+ if (deps.closed() || change.paged === true)
434
+ return;
435
+ endedAnew(list);
436
+ if (change.cursor !== undefined) {
437
+ listKnown = true;
438
+ settleBehind(list);
439
+ openingEnded();
440
+ deps.settled();
441
+ }
442
+ emitList();
443
+ if (!listKnown)
444
+ return;
445
+ retiredDrafts(list);
446
+ if (change.cursor === undefined &&
447
+ (list.length !== replicated.size ||
448
+ list.some(({ conversation }) => !replicated.has(conversation)))) {
449
+ deps.again();
450
+ }
451
+ }, () => { });
452
+ }
453
+ function endedAnew(list) {
454
+ const entries = new Map(list.map((entry) => [entry.conversation, entry]));
455
+ for (const view of views.values()) {
456
+ const ended = entries.get(view.conversation)?.processEnded ?? null;
457
+ if (view.window === null || ended === view.ended)
458
+ continue;
459
+ view.ended = ended;
460
+ emit(view.conversation);
461
+ }
462
+ }
463
+ let listShown = Promise.resolve();
464
+ function shownList(work) {
465
+ const reading = heldList();
466
+ listShown = listShown.then(async () => {
467
+ const list = await reading.catch(() => null);
468
+ if (list === null || deps.closed())
469
+ return;
470
+ work(list);
471
+ });
472
+ }
473
+ async function told() {
474
+ try {
475
+ while (listOwed && !deps.closed()) {
476
+ listOwed = false;
477
+ const value = await untilAnswered(entries, closing.signal);
478
+ if (deps.closed())
479
+ return;
480
+ if (value === null)
481
+ continue;
482
+ for (const watcher of listWatchers)
483
+ watcher(value);
484
+ for (const watcher of [...unlisted]) {
485
+ unlisted.delete(watcher);
486
+ listWatchers.add(watcher);
487
+ watcher(value);
488
+ }
489
+ }
490
+ }
491
+ finally {
492
+ listTelling = false;
493
+ }
494
+ }
495
+ function tellList() {
496
+ if (deps.closed() || (listWatchers.size === 0 && unlisted.size === 0))
497
+ return;
498
+ listOwed = true;
499
+ if (listTelling)
500
+ return;
501
+ listTelling = true;
502
+ void told().catch(() => { });
503
+ }
504
+ function tellSoon() {
505
+ if (rowTimer !== null)
506
+ return;
507
+ rowTimer = setTimeout(() => {
508
+ rowTimer = null;
509
+ tellList();
510
+ }, ROW_INTERVAL_MS);
511
+ }
332
512
  function emitList() {
333
- if (deps.closed() || listWatchers.size === 0)
513
+ if (deps.closed())
514
+ return;
515
+ tellList();
516
+ emitAgents();
517
+ learned();
518
+ }
519
+ function learned() {
520
+ if (deps.closed())
521
+ return;
522
+ if (unlisted.size > 0)
523
+ tellList();
524
+ if (unnamed.size === 0)
334
525
  return;
335
- const value = entries();
336
- for (const watcher of listWatchers)
337
- watcher(value);
526
+ shownList((list) => {
527
+ const value = agents(list);
528
+ for (const watcher of [...unnamed]) {
529
+ unnamed.delete(watcher);
530
+ agentWatchers.add(watcher);
531
+ watcher(value);
532
+ }
533
+ });
338
534
  }
339
- function agents() {
340
- return [...agentNames]
341
- .filter(([identity]) => !retiredAgents.has(identity))
342
- .map(([identity, name]) => ({ identity, name }))
535
+ function agents(list) {
536
+ const named = new Map();
537
+ for (const agent of listedAgents ?? [])
538
+ named.set(agent.agent, agent);
539
+ for (const entry of list) {
540
+ named.set(entry.agent, { name: entry.agentName, retired: entry.agentRetired });
541
+ }
542
+ return [...named]
543
+ .filter(([, agent]) => !agent.retired)
544
+ .map(([identity, agent]) => ({ identity, name: agent.name }))
343
545
  .sort((left, right) => left.name === right.name
344
546
  ? left.identity.localeCompare(right.identity)
345
547
  : left.name.localeCompare(right.name));
346
548
  }
549
+ function readAgents() {
550
+ const stream = deps.stream();
551
+ if (stream?.agents === undefined || !deps.online() || agentsReading)
552
+ return;
553
+ agentsReading = true;
554
+ void stream
555
+ .agents()
556
+ .then((read) => {
557
+ if (read === null)
558
+ return;
559
+ listedAgents = read;
560
+ emitAgents();
561
+ }, () => { })
562
+ .finally(() => {
563
+ agentsReading = false;
564
+ });
565
+ }
347
566
  function emitAgents() {
348
567
  if (deps.closed() || agentWatchers.size === 0)
349
568
  return;
350
- const value = agents();
351
- for (const watcher of agentWatchers)
352
- watcher(value);
569
+ shownList((list) => {
570
+ const value = agents(list);
571
+ const shown = JSON.stringify(value);
572
+ if (shown === agentsShown)
573
+ return;
574
+ agentsShown = shown;
575
+ for (const watcher of agentWatchers)
576
+ watcher(value);
577
+ });
353
578
  }
354
579
  function setLive(conversation, next) {
355
- const before = stateOf(conversation);
356
- const began = lives.get(conversation)?.turn?.started;
580
+ const before = rowOf(conversation);
357
581
  if (next === null)
358
582
  lives.delete(conversation);
359
583
  else
360
584
  lives.set(conversation, next);
361
- if (!listing.has(conversation))
362
- return;
363
- if (stateOf(conversation) !== before || next?.turn?.started !== began)
585
+ const after = rowOf(conversation);
586
+ if (after.state !== before.state || after.started !== before.started)
364
587
  emitList();
588
+ else if (after.line !== before.line || after.question !== before.question)
589
+ tellSoon();
365
590
  }
366
- async function readAgents() {
367
- const declaration = deps.agents();
368
- if (declaration === undefined)
369
- return;
370
- const held = await deps.documents(declaration);
371
- if ('pending' in held)
372
- return;
373
- const { documents } = held;
374
- const names = new Map();
375
- const retired = new Set();
376
- for (const document of documents) {
377
- if (document.type !== declaration.type)
378
- continue;
379
- const identity = document.fields?.[declaration.identity];
380
- const name = document.fields?.[declaration.name];
381
- if (typeof identity !== 'string' || typeof name !== 'string')
382
- continue;
383
- names.set(identity, name);
384
- const ended = declaration.retired === undefined ? null : document.fields?.[declaration.retired];
385
- if (ended !== undefined && ended !== null)
386
- retired.add(identity);
387
- }
388
- const named = JSON.stringify([...names]) !== JSON.stringify([...agentNames]);
389
- if (!named && JSON.stringify([...retired]) === JSON.stringify([...retiredAgents]))
390
- return;
391
- agentNames = names;
392
- retiredAgents = retired;
393
- if (named)
394
- emitList();
395
- emitAgents();
396
- const ended = [...drafts.keys()].filter(retiredChat);
397
- if (ended.length > 0)
398
- await remove(ended);
399
- }
400
- function retiredChat(conversation) {
401
- return (conversation.startsWith(NEW_CHAT) && retiredAgents.has(conversation.slice(NEW_CHAT.length)));
402
- }
403
- function agentsReady() {
404
- agentsRead ??= readAgents().catch(() => {
405
- agentsRead = null;
406
- });
407
- return agentsRead;
591
+ function retiredChat(conversation, list) {
592
+ if (!conversation.startsWith(NEW_CHAT))
593
+ return false;
594
+ const agent = conversation.slice(NEW_CHAT.length);
595
+ return list.some((entry) => entry.agent === agent && entry.agentRetired);
408
596
  }
409
597
  function live(conversation, change) {
410
598
  const owed = unsent.get(conversation);
@@ -426,6 +614,12 @@ export function createConversationStore(deps) {
426
614
  process: facts.has('process') ? QUIET.process : held.process,
427
615
  commands: facts.has('commands') ? QUIET.commands : held.commands,
428
616
  options: facts.has('options') ? QUIET.options : held.options,
617
+ starting: facts.has('starting') ? QUIET.starting : held.starting,
618
+ subagents: facts.has('subagents') ? QUIET.subagents : held.subagents,
619
+ link: facts.has('link') ? QUIET.link : held.link,
620
+ deliveryUnknown: facts.has('deliveryUnknown')
621
+ ? QUIET.deliveryUnknown
622
+ : held.deliveryUnknown,
429
623
  });
430
624
  emit(conversation);
431
625
  }
@@ -456,31 +650,94 @@ export function createConversationStore(deps) {
456
650
  }
457
651
  async function remove(leaving) {
458
652
  await deps.durably(async (local) => {
459
- const removed = [];
460
- for (const conversation of leaving) {
461
- for (const identity of await local.messageIdentities(conversation)) {
462
- removed.push({ conversation, identity, text: null });
463
- }
464
- }
653
+ const kept = new Set([
654
+ ...(await local.conversations(leaving)).map(({ conversation }) => conversation),
655
+ ...(await local.chatDrafts())
656
+ .filter(({ conversation }) => leaving.includes(conversation))
657
+ .map(({ conversation }) => conversation),
658
+ ]);
465
659
  await local.dropConversations(leaving);
466
660
  const changed = [];
467
661
  for (const conversation of leaving) {
468
- if (stored.has(conversation) || drafts.has(conversation) || lives.has(conversation)) {
662
+ if (kept.has(conversation) || lives.has(conversation))
469
663
  changed.push(conversation);
664
+ for (const view of views.values()) {
665
+ if (view.conversation === conversation)
666
+ view.draft = null;
470
667
  }
471
- stored.delete(conversation);
472
- reads.delete(conversation);
473
- partials.delete(conversation);
474
- drafts.delete(conversation);
475
668
  behind.delete(conversation);
669
+ heardNewest.delete(conversation);
476
670
  unsent.delete(conversation);
477
671
  setLive(conversation, null);
478
672
  await slide(local, conversation, null);
479
673
  }
480
674
  if (changed.length > 0)
481
- deps.publish(conversationChangeSet(removed, changed));
675
+ deps.publish(conversationChangeSet(changed));
482
676
  });
483
677
  }
678
+ function newestOfListed(entry) {
679
+ if (entry === undefined)
680
+ return undefined;
681
+ const heard = heardNewest.get(entry.conversation);
682
+ if (heard === undefined)
683
+ return entry.newest;
684
+ return entry.newest === null || heard > entry.newest ? heard : entry.newest;
685
+ }
686
+ function missed(conversation, held, entry) {
687
+ const read = held?.read;
688
+ const newest = newestOfListed(entry);
689
+ return (read === undefined ||
690
+ newest === undefined ||
691
+ held?.partial === true ||
692
+ (newest !== null && (read === null || newest > read)));
693
+ }
694
+ async function readsOf(local, conversations) {
695
+ return new Map((await local.conversations(conversations)).map((held) => [held.conversation, held]));
696
+ }
697
+ function retiredDrafts(list) {
698
+ if (!list.some((entry) => entry.agentRetired))
699
+ return;
700
+ void deps
701
+ .local()
702
+ .then((local) => local.chatDrafts())
703
+ .then((held) => {
704
+ if (!deps.closed() && held.some(({ conversation }) => retiredChat(conversation, list))) {
705
+ deps.again();
706
+ }
707
+ }, () => { });
708
+ }
709
+ function settleBehind(list) {
710
+ void deps
711
+ .durably(async (local) => {
712
+ const entries = new Map(list.map((entry) => [entry.conversation, entry]));
713
+ const named = [...replicated].filter((conversation) => entries.has(conversation));
714
+ const held = await readsOf(local, named);
715
+ let owed = false;
716
+ for (const conversation of named) {
717
+ if (!missed(conversation, held.get(conversation), entries.get(conversation))) {
718
+ behind.delete(conversation);
719
+ }
720
+ else if (!behind.has(conversation)) {
721
+ gap(conversation);
722
+ owed = true;
723
+ }
724
+ }
725
+ return owed;
726
+ })
727
+ .then((owed) => {
728
+ if (deps.closed())
729
+ return;
730
+ deps.settled();
731
+ if (owed)
732
+ deps.again();
733
+ }, () => { });
734
+ }
735
+ function noted(conversation, message) {
736
+ const heard = heardNewest.get(conversation);
737
+ if (heard === undefined || message.position > heard) {
738
+ heardNewest.set(conversation, message.position);
739
+ }
740
+ }
484
741
  function gap(conversation) {
485
742
  gaps.set(conversation, (gaps.get(conversation) ?? 0) + 1);
486
743
  behind.add(conversation);
@@ -489,7 +746,7 @@ export function createConversationStore(deps) {
489
746
  return deps.durably(async (local) => {
490
747
  if (!replicated.has(conversation))
491
748
  return;
492
- const read = reads.get(conversation);
749
+ const read = (await readsOf(local, [conversation])).get(conversation)?.read;
493
750
  const advances = read !== undefined &&
494
751
  !behind.has(conversation) &&
495
752
  (read === null || message.position > read);
@@ -499,22 +756,22 @@ export function createConversationStore(deps) {
499
756
  messages: [message],
500
757
  ...(advances ? { read: { at: message.position, partial: false } } : {}),
501
758
  });
502
- stored.add(conversation);
503
- if (advances)
504
- reads.set(conversation, message.position);
505
759
  sealTurns(conversation, new Set([message.turn]));
506
760
  await slide(local, conversation, [message]);
507
- deps.publish(conversationChangeSet(changesOf(conversation, [message]), [conversation]));
761
+ deps.publish(conversationChangeSet([conversation]));
762
+ tellList();
508
763
  });
509
764
  }
510
765
  async function readHistories(conversations, ended) {
511
766
  const generations = new Map(conversations.map((one) => [one, gaps.get(one) ?? 0]));
512
- let asking = conversations.map((conversation) => ({
767
+ const held = await readsOf(await deps.local(), conversations);
768
+ let waiting = conversations.map((conversation) => ({
513
769
  conversation,
514
- after: reads.get(conversation) ?? undefined,
770
+ after: held.get(conversation)?.read ?? undefined,
515
771
  }));
516
- while (asking.length > 0) {
517
- const result = await deps.call('conversation.history', {
772
+ while (waiting.length > 0) {
773
+ const asking = waiting.slice(0, HISTORY_CONVERSATIONS);
774
+ const result = await deps.history({
518
775
  conversations: asking.map(({ conversation, after }) => ({
519
776
  conversation,
520
777
  ...(after === undefined ? {} : { after }),
@@ -537,7 +794,7 @@ export function createConversationStore(deps) {
537
794
  if (!following.some((next) => next.conversation === conversation))
538
795
  ended(conversation);
539
796
  }
540
- asking = following;
797
+ waiting = [...waiting.slice(HISTORY_CONVERSATIONS), ...following];
541
798
  }
542
799
  return null;
543
800
  }
@@ -566,22 +823,19 @@ export function createConversationStore(deps) {
566
823
  return;
567
824
  const whole = next === undefined;
568
825
  const cursor = next ?? newestOf(arriving, from);
569
- const partial = !whole && (partials.has(conversation) || !reads.has(conversation));
826
+ const held = (await readsOf(local, [conversation])).get(conversation);
827
+ const partial = !whole && (held?.partial === true || held?.read === undefined);
570
828
  const sealing = await sealed(local, conversation, new Set(arriving.map((message) => message.turn)));
571
829
  await local.keepMessages({ conversation, messages: arriving, read: { at: cursor, partial } });
572
- stored.add(conversation);
573
- reads.set(conversation, cursor);
574
- if (partial)
575
- partials.add(conversation);
576
- else
577
- partials.delete(conversation);
578
830
  sealTurns(conversation, new Set(arriving.map((message) => message.turn)));
579
831
  if (whole && (gaps.get(conversation) ?? 0) === generation)
580
832
  behind.delete(conversation);
581
833
  await slide(local, conversation, arriving);
582
834
  if (arriving.length > 0 || sealing || whole) {
583
- deps.publish(conversationChangeSet(changesOf(conversation, arriving), [conversation]));
835
+ deps.publish(conversationChangeSet([conversation]));
584
836
  }
837
+ if (arriving.length > 0)
838
+ tellSoon();
585
839
  });
586
840
  }
587
841
  function heard(event) {
@@ -613,28 +867,59 @@ export function createConversationStore(deps) {
613
867
  live(conversation, { options: structuredClone(event.options) });
614
868
  return;
615
869
  }
870
+ if (event.kind === 'starting') {
871
+ live(conversation, { starting: event.step });
872
+ return;
873
+ }
874
+ if (event.kind === 'subagents') {
875
+ live(conversation, { subagents: structuredClone(event.subagents) });
876
+ return;
877
+ }
878
+ if (event.kind === 'link') {
879
+ live(conversation, { link: { connected: event.connected } });
880
+ return;
881
+ }
882
+ if (event.kind === 'deliveries') {
883
+ live(conversation, { deliveryUnknown: [...event.unknown] });
884
+ return;
885
+ }
616
886
  const arriving = acceptedMessage(event.message);
617
887
  if (arriving === null)
618
888
  return;
889
+ noted(conversation, arriving);
619
890
  if (replicated.has(conversation)) {
620
- void keep(conversation, arriving).catch(() => { });
891
+ if (deps.full()) {
892
+ gap(conversation);
893
+ return;
894
+ }
895
+ void keep(conversation, arriving).catch((error) => {
896
+ if (refusedForSpace(error))
897
+ gap(conversation);
898
+ });
621
899
  return;
622
900
  }
623
901
  if (sealTurns(conversation, new Set([arriving.turn])))
624
902
  emit(conversation);
625
903
  }
626
904
  async function kept(local, conversation, following) {
627
- if (following === null || empty(following)) {
905
+ const shown = following === null || empty(following) ? null : following;
906
+ if (shown === null)
628
907
  await local.dropChatDraft(conversation);
629
- drafts.delete(conversation);
630
- }
631
- else {
632
- await local.keepChatDrafts([following]);
633
- drafts.set(conversation, following);
908
+ else
909
+ await local.keepChatDrafts([shown]);
910
+ shownDraft(conversation, shown);
911
+ }
912
+ function shownDraft(conversation, draft) {
913
+ for (const view of views.values()) {
914
+ if (view.conversation === conversation)
915
+ view.draft = draft;
634
916
  }
635
917
  }
918
+ async function draftAt(local, conversation) {
919
+ return (await local.chatDraft(conversation)) ?? undefined;
920
+ }
636
921
  async function sealed(local, conversation, turns) {
637
- const held = drafts.get(conversation);
922
+ const held = await draftAt(local, conversation);
638
923
  const submitted = held?.submitted ?? [];
639
924
  const left = submitted.filter((one) => one.turn === null || !turns.has(one.turn));
640
925
  if (held === undefined || left.length === submitted.length)
@@ -645,85 +930,70 @@ export function createConversationStore(deps) {
645
930
  function moved(agent, operation, to) {
646
931
  const from = newChat(agent);
647
932
  return deps.durably(async (local) => {
648
- const held = drafts.get(from);
649
- const earlier = (held?.moves ?? []).filter((one) => !deps.expired(one));
933
+ const held = await draftAt(local, from);
934
+ const earlier = held?.moves ?? [];
650
935
  if (earlier.includes(operation))
651
936
  return;
652
937
  const blank = {
653
938
  conversation: from,
654
939
  text: '',
655
- files: [],
656
940
  revision: chatDraftRevision(),
657
941
  };
658
942
  const { moves: _answered, ...unsent } = held ?? blank;
659
- const taken = drafts.has(to) || empty(unsent) ? [] : [{ ...unsent, conversation: to }];
943
+ const taken = (await draftAt(local, to)) !== undefined || empty(unsent)
944
+ ? []
945
+ : [{ ...unsent, conversation: to }];
660
946
  const left = { ...(taken.length === 0 ? unsent : blank), moves: [...earlier, operation] };
661
947
  await local.keepChatDrafts([left, ...taken]);
662
- drafts.set(from, left);
948
+ shownDraft(from, left);
663
949
  for (const one of taken)
664
- drafts.set(to, one);
665
- deps.publish(conversationChangeSet([], [from, to]));
950
+ shownDraft(to, one);
951
+ deps.publish(conversationChangeSet([from, to]));
666
952
  });
667
953
  }
668
954
  function withDraft(conversation, next) {
669
955
  return deps.durably(async (local) => {
670
- const following = await next(drafts.get(conversation), local);
956
+ const following = await next(await draftAt(local, conversation), local);
671
957
  if (following === undefined)
672
958
  return;
673
959
  await kept(local, conversation, following);
674
- deps.publish(conversationChangeSet([], [conversation]));
960
+ deps.publish(conversationChangeSet([conversation]));
675
961
  });
676
962
  }
677
963
  async function submitted(conversation, operation, draft) {
678
964
  let submission;
679
- let earlier = false;
680
965
  await withDraft(conversation, (current) => {
681
966
  submission = current?.submitted?.find((one) => one.operation === operation);
682
- earlier = submission !== undefined;
683
967
  if (submission !== undefined)
684
968
  return undefined;
685
969
  const said = draft ?? current;
686
- submission = {
687
- operation,
688
- text: said?.text ?? '',
689
- files: [...(said?.files ?? [])],
690
- turn: null,
691
- };
970
+ submission = { operation, text: said?.text ?? '', turn: null };
692
971
  return {
693
972
  conversation,
694
973
  ...(draft === undefined || current === undefined
695
- ? { text: '', files: [], revision: chatDraftRevision() }
696
- : { text: current.text, files: current.files, revision: current.revision }),
974
+ ? { text: '', revision: chatDraftRevision() }
975
+ : { text: current.text, revision: current.revision }),
697
976
  submitted: [...(current?.submitted ?? []), submission],
698
977
  };
699
978
  });
700
- return { submission: submission, earlier };
701
- }
702
- function forgotten(conversation, submission) {
703
- return withDraft(conversation, (current) => {
704
- const held = current?.submitted ?? [];
705
- if (current === undefined || !held.includes(submission))
706
- return undefined;
707
- return withSubmitted(current, held.filter((one) => one !== submission));
708
- });
979
+ return submission;
709
980
  }
710
981
  async function dispatched(conversation, submission) {
711
- const dispatch = {};
982
+ const dispatch = { answered: false };
712
983
  sending.set(submission.operation, dispatch);
713
984
  try {
714
- const files = await Promise.all(submission.files.map(async (file) => ({ ...(await declaredFile(file)), blob: file.blob })));
715
- const result = await invoke('conversation.send', conversation, {
716
- text: submission.text,
717
- files,
985
+ const result = await invoke('conversations/send', conversation, {
718
986
  operation_id: submission.operation,
987
+ text: submission.text,
719
988
  });
989
+ dispatch.answered = result.kind !== 'transport-uncertainty';
720
990
  if (result.kind === 'revoked-authorization')
721
991
  return result;
722
992
  if (result.kind !== 'action' && result.retryable) {
723
993
  deps.unanswered();
724
994
  return result;
725
995
  }
726
- const turn = answered(result, 'turn');
996
+ const turn = answered(result, 'message_id');
727
997
  await withDraft(conversation, async (current, local) => {
728
998
  const held = current?.submitted?.find((one) => one.operation === submission.operation);
729
999
  if (current === undefined || held === undefined || held.turn !== null)
@@ -743,72 +1013,80 @@ export function createConversationStore(deps) {
743
1013
  sending.delete(submission.operation);
744
1014
  }
745
1015
  }
746
- function invoke(name, conversation, extra = {}) {
747
- return deps.call(name, { conversation, ...extra });
1016
+ function invoke(name, conversation, extra = { operation_id: changeBatchIdentity() }) {
1017
+ return deps.call(name, { conversation_id: conversation, ...extra });
748
1018
  }
749
1019
  const surface = {
750
- async list() {
751
- await agentsReady();
752
- return entries();
1020
+ list() {
1021
+ return new Promise((settle) => {
1022
+ const answer = (listed) => {
1023
+ unlisted.delete(answer);
1024
+ listWatchers.delete(answer);
1025
+ settle(listed);
1026
+ };
1027
+ unlisted.add(answer);
1028
+ learned();
1029
+ });
753
1030
  },
754
1031
  async read(conversation, options = {}) {
755
- const asked = askedOf(options);
1032
+ const { text, ...windowed } = options;
1033
+ const asked = askedOf(windowed);
1034
+ const phrase = phraseOf(text);
756
1035
  return deps.durably(async (local) => {
757
- const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
758
- const total = asked.total ? await local.messageCount(conversation) : null;
759
- return structuredClone(viewOf(conversation, messages, frameOf(asked, messages, more, total)));
1036
+ const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before, phrase);
1037
+ const total = asked.total ? await local.messageCount(conversation, phrase) : null;
1038
+ const marked = await local.jobMarkers(conversation);
1039
+ return structuredClone(viewOf(conversation, messages, frameOf(asked, messages, more, total), (await local.chatDraft(conversation)) ?? null, await endedIn(conversation), marked));
760
1040
  });
761
1041
  },
762
1042
  async create(input) {
763
- const result = await deps.call('conversation.create', input);
764
- const conversation = answered(result, 'conversation');
1043
+ const result = await deps.call('conversations/create', input);
1044
+ const conversation = answered(result, 'conversation_id');
765
1045
  if (conversation === null)
766
1046
  return result;
767
- if (!listing.has(conversation))
1047
+ const listed = (await heldList()) ?? [];
1048
+ if (!listed.some((entry) => entry.conversation === conversation)) {
768
1049
  creations.add(conversation);
1050
+ }
769
1051
  const creation = creationOf(input);
770
1052
  if (creation !== null)
771
1053
  await moved(creation.agent, creation.operation, conversation);
772
1054
  return result;
773
1055
  },
774
1056
  async send(conversation, operation = changeBatchIdentity(), draft) {
1057
+ if (deps.full())
1058
+ return DURABLE_WRITE;
775
1059
  if (deps.online()) {
776
- const { submission, earlier } = await submitted(conversation, operation, draft);
777
- if (!earlier || !deps.expired(operation))
778
- return dispatched(conversation, submission);
779
- await forgotten(conversation, submission);
780
- return EXPIRED;
1060
+ const held = await submitted(conversation, operation, draft).catch((error) => {
1061
+ if (refusedForSpace(error))
1062
+ return null;
1063
+ throw error;
1064
+ });
1065
+ if (held === null)
1066
+ return DURABLE_WRITE;
1067
+ return dispatched(conversation, held);
781
1068
  }
782
1069
  if (draft !== undefined) {
783
1070
  await withDraft(conversation, (current) => current?.submitted?.some((one) => one.operation === operation) === true
784
1071
  ? undefined
785
- : returned(current ?? { conversation, text: '', files: [], revision: '' }, { operation, ...draft, turn: null }, current?.submitted ?? []));
1072
+ : returned(current ?? { conversation, text: '', revision: '' }, { operation, ...draft, turn: null }, current?.submitted ?? []));
786
1073
  }
787
1074
  return OFFLINE;
788
1075
  },
789
1076
  answer(conversation, answer) {
790
- return invoke('conversation.answer', conversation, { answer });
1077
+ return invoke('conversations/interaction/answer', conversation, {
1078
+ operation_id: changeBatchIdentity(),
1079
+ ...answer,
1080
+ });
791
1081
  },
792
1082
  interrupt(conversation) {
793
- return invoke('conversation.interrupt', conversation);
1083
+ return invoke('conversations/interrupt', conversation);
794
1084
  },
795
1085
  kill(conversation) {
796
- return invoke('conversation.kill', conversation);
797
- },
798
- async cancel(conversation, turn) {
799
- const result = await invoke('conversation.cancel', conversation, { turn });
800
- if (result.kind !== 'action')
801
- return result;
802
- await withDraft(conversation, (current) => {
803
- const held = current?.submitted?.find((one) => one.turn === turn);
804
- if (current === undefined || held === undefined)
805
- return undefined;
806
- return returned(current, held, current.submitted.filter((one) => one !== held));
807
- });
808
- return result;
1086
+ return invoke('conversations/kill', conversation);
809
1087
  },
810
1088
  delete(conversation) {
811
- return invoke('conversation.delete', conversation);
1089
+ return invoke('conversations/delete', conversation);
812
1090
  },
813
1091
  };
814
1092
  const chatDraft = {
@@ -816,7 +1094,6 @@ export function createConversationStore(deps) {
816
1094
  return withDraft(conversation, (current) => ({
817
1095
  conversation,
818
1096
  text: draft.text,
819
- files: [...draft.files],
820
1097
  revision,
821
1098
  ...(current === undefined ? {} : pending(current)),
822
1099
  }));
@@ -824,9 +1101,7 @@ export function createConversationStore(deps) {
824
1101
  prepend(conversation, draft) {
825
1102
  return withDraft(conversation, (current) => ({
826
1103
  conversation,
827
- ...(current === undefined
828
- ? { text: draft.text, files: [...draft.files] }
829
- : prepended(current, draft)),
1104
+ ...(current === undefined ? { text: draft.text } : prepended(current, draft)),
830
1105
  revision: chatDraftRevision(),
831
1106
  ...(current === undefined ? {} : pending(current)),
832
1107
  }));
@@ -834,45 +1109,46 @@ export function createConversationStore(deps) {
834
1109
  discard(conversation) {
835
1110
  return withDraft(conversation, (current) => current === undefined
836
1111
  ? undefined
837
- : {
838
- conversation,
839
- text: '',
840
- files: [],
841
- revision: chatDraftRevision(),
842
- ...pending(current),
843
- });
1112
+ : { conversation, text: '', revision: chatDraftRevision(), ...pending(current) });
844
1113
  },
845
1114
  };
846
1115
  return {
847
1116
  surface,
848
1117
  chatDraft,
849
- hydrate(held, kept) {
850
- stored.clear();
851
- reads.clear();
852
- partials.clear();
853
- for (const entry of held) {
854
- stored.add(entry.conversation);
855
- if (entry.read !== undefined)
856
- reads.set(entry.conversation, entry.read);
857
- if (entry.partial)
858
- partials.add(entry.conversation);
859
- }
860
- drafts.clear();
861
- for (const record of kept)
862
- drafts.set(record.conversation, record);
1118
+ async opened(local) {
1119
+ const { conversationList, conversationCursor } = await local.facts();
1120
+ if (conversationList === null || listKnown)
1121
+ return;
1122
+ deps.stream()?.hold(conversationCursor);
1123
+ learned();
863
1124
  },
864
1125
  async reconcile() {
865
1126
  if (!listKnown)
866
1127
  return;
867
- const named = new Set(listing.keys());
1128
+ const local = await deps.local();
1129
+ const [held, kept, { conversationList }] = await Promise.all([
1130
+ local.conversations(),
1131
+ local.chatDrafts(),
1132
+ local.facts(),
1133
+ ]);
1134
+ const list = conversationList ?? [];
1135
+ const entries = new Map(list.map((entry) => [entry.conversation, entry]));
1136
+ const named = new Set(entries.keys());
1137
+ const reads = new Map(held.map((one) => [one.conversation, one]));
868
1138
  const leaving = [
869
- ...new Set([...replicated, ...stored, ...reads.keys(), ...drafts.keys()]),
1139
+ ...new Set([
1140
+ ...replicated,
1141
+ ...reads.keys(),
1142
+ ...kept.map(({ conversation }) => conversation),
1143
+ ]),
870
1144
  ].filter((conversation) => !named.has(conversation) &&
871
1145
  !creations.has(conversation) &&
872
- (!conversation.startsWith(NEW_CHAT) || retiredChat(conversation)));
1146
+ (!conversation.startsWith(NEW_CHAT) || retiredChat(conversation, list)));
873
1147
  for (const conversation of named) {
874
- if (!replicated.has(conversation))
1148
+ if (!replicated.has(conversation) &&
1149
+ missed(conversation, reads.get(conversation), entries.get(conversation))) {
875
1150
  behind.add(conversation);
1151
+ }
876
1152
  }
877
1153
  replicated = named;
878
1154
  handReplicated();
@@ -880,6 +1156,11 @@ export function createConversationStore(deps) {
880
1156
  await remove(leaving);
881
1157
  },
882
1158
  async catchUp(progress) {
1159
+ if (deps.full()) {
1160
+ if (behind.size > 0)
1161
+ progress.complete = false;
1162
+ return;
1163
+ }
883
1164
  const owed = (conversation) => behind.has(conversation) && replicated.has(conversation);
884
1165
  const read = async (conversations) => {
885
1166
  if (conversations.length === 0 || deps.closed())
@@ -898,16 +1179,16 @@ export function createConversationStore(deps) {
898
1179
  progress.complete = false;
899
1180
  },
900
1181
  async resend(progress) {
901
- for (const [conversation, held] of [...drafts]) {
902
- for (const submission of held.submitted ?? []) {
903
- if (submission.turn !== null || sending.has(submission.operation))
904
- continue;
1182
+ const local = await deps.local();
1183
+ const tried = new Set();
1184
+ for (const { conversation } of await local.chatDrafts()) {
1185
+ for (;;) {
1186
+ const submission = (await local.chatDraft(conversation))?.submitted?.find((one) => one.turn === null && !sending.has(one.operation) && !tried.has(one.operation));
1187
+ if (submission === undefined)
1188
+ break;
905
1189
  if (deps.closed())
906
1190
  return;
907
- if (deps.expired(submission.operation)) {
908
- await forgotten(conversation, submission);
909
- continue;
910
- }
1191
+ tried.add(submission.operation);
911
1192
  const result = await dispatched(conversation, submission);
912
1193
  if (result.kind === 'revoked-authorization')
913
1194
  return;
@@ -928,12 +1209,11 @@ export function createConversationStore(deps) {
928
1209
  known() {
929
1210
  return listKnown;
930
1211
  },
931
- reconnected() {
932
- for (const conversation of replicated)
933
- gap(conversation);
934
- },
935
1212
  reopened() {
936
- sending.clear();
1213
+ relisting = null;
1214
+ for (const [operation, dispatch] of sending)
1215
+ if (!dispatch.answered)
1216
+ sending.delete(operation);
937
1217
  },
938
1218
  lost() {
939
1219
  catching.clear();
@@ -946,30 +1226,37 @@ export function createConversationStore(deps) {
946
1226
  asked,
947
1227
  watchers: new Set(),
948
1228
  window: null,
1229
+ draft: null,
1230
+ ended: null,
1231
+ marked: [],
949
1232
  more: false,
950
1233
  total: null,
951
1234
  };
952
1235
  views.set(key, view);
953
1236
  const watcher = { notify: deliver, ready: false };
954
1237
  view.watchers.add(watcher);
955
- void deps
956
- .durably(async (local) => {
1238
+ const reading = new AbortController();
1239
+ void untilAnswered(() => deps.durably(async (local) => {
957
1240
  if (view.window !== null)
958
1241
  return;
959
1242
  const { messages, more } = await local.messageWindow(conversation, asked.limit, asked.before);
1243
+ view.draft = (await local.chatDraft(conversation)) ?? null;
1244
+ view.ended = await endedIn(conversation);
960
1245
  view.window = messages;
1246
+ view.marked = await local.jobMarkers(conversation);
961
1247
  view.more = more;
962
1248
  view.total = asked.total ? await local.messageCount(conversation) : null;
963
- })
1249
+ }), reading.signal)
964
1250
  .then(() => {
965
1251
  if (deps.closed() || !view.watchers.has(watcher))
966
1252
  return;
967
1253
  watcher.ready = true;
968
1254
  const window = view.window ?? [];
969
- watcher.notify(viewOf(conversation, window, sharedFrame(view, window)));
1255
+ watcher.notify(viewOf(conversation, window, sharedFrame(view, window), view.draft, view.ended, view.marked));
970
1256
  })
971
1257
  .catch(() => { });
972
1258
  return () => {
1259
+ reading.abort();
973
1260
  if (!view.watchers.delete(watcher))
974
1261
  return;
975
1262
  if (view.watchers.size === 0)
@@ -977,77 +1264,73 @@ export function createConversationStore(deps) {
977
1264
  };
978
1265
  },
979
1266
  watchList(deliver) {
980
- listWatchers.add(deliver);
981
- void agentsReady().then(() => {
982
- if (!deps.closed() && listWatchers.has(deliver))
983
- deliver(entries());
984
- });
1267
+ unlisted.add(deliver);
1268
+ learned();
985
1269
  return () => {
1270
+ unlisted.delete(deliver);
986
1271
  listWatchers.delete(deliver);
987
1272
  };
988
1273
  },
989
1274
  watchAgents(deliver) {
990
- agentWatchers.add(deliver);
991
- void agentsReady().then(() => {
992
- if (!deps.closed() && agentWatchers.has(deliver))
993
- deliver(agents());
994
- });
1275
+ unnamed.add(deliver);
1276
+ learned();
1277
+ readAgents();
995
1278
  return () => {
1279
+ unnamed.delete(deliver);
996
1280
  agentWatchers.delete(deliver);
997
1281
  };
998
1282
  },
1283
+ learned,
999
1284
  changed(set) {
1000
1285
  for (const conversation of set.conversations)
1001
1286
  emit(conversation);
1002
- const declaration = deps.agents();
1003
- if (declaration !== undefined &&
1004
- set.documents.some((change) => change.locator === declaration.locator)) {
1005
- agentsRead = readAgents().catch(() => {
1006
- agentsRead = null;
1007
- });
1008
- }
1009
1287
  },
1010
1288
  receive(event) {
1011
1289
  if (deps.closed())
1012
- return false;
1013
- if (event.kind === 'list' || event.kind === 'listed' || event.kind === 'unlisted') {
1014
- if (event.kind === 'list') {
1015
- listing.clear();
1016
- for (const entry of event.entries) {
1017
- listing.set(entry.conversation, { ...entry });
1018
- creations.delete(entry.conversation);
1019
- }
1020
- listKnown = true;
1021
- openingEnded();
1022
- }
1023
- else if (event.kind === 'listed') {
1024
- listing.set(event.entry.conversation, { ...event.entry });
1025
- creations.delete(event.entry.conversation);
1026
- }
1027
- else {
1028
- listing.delete(event.conversation);
1290
+ return;
1291
+ if (event.kind === 'list') {
1292
+ if (event.whole)
1293
+ relisting ??= new Map();
1294
+ if (relisting !== null) {
1295
+ for (const entry of event.entries)
1296
+ relisting.set(entry.conversation, { ...entry });
1029
1297
  }
1030
- emitList();
1031
- return (event.kind !== 'list' &&
1032
- listKnown &&
1033
- (listing.size !== replicated.size ||
1034
- [...listing.keys()].some((conversation) => !replicated.has(conversation))));
1298
+ if (event.more && relisting !== null)
1299
+ return;
1300
+ const whole = relisting;
1301
+ relisting = null;
1302
+ listChanged({
1303
+ replacing: whole !== null,
1304
+ set: whole === null ? event.entries : [...whole.values()],
1305
+ gone: null,
1306
+ ...(event.more ? { paged: true } : { cursor: event.cursor }),
1307
+ });
1308
+ return;
1309
+ }
1310
+ if (event.kind === 'listed') {
1311
+ relisting?.set(event.entry.conversation, { ...event.entry });
1312
+ listChanged({ replacing: false, set: [event.entry], gone: null });
1313
+ return;
1314
+ }
1315
+ if (event.kind === 'unlisted') {
1316
+ relisting?.delete(event.conversation);
1317
+ listChanged({ replacing: false, set: [], gone: event.conversation });
1318
+ return;
1035
1319
  }
1036
1320
  heard(event);
1037
- return false;
1038
- },
1039
- searchable() {
1040
- return [...(listKnown ? listing.keys() : stored)].sort();
1041
- },
1042
- acquired(conversation) {
1043
- return reads.has(conversation) && !partials.has(conversation);
1044
1321
  },
1045
1322
  close() {
1323
+ closing.abort();
1324
+ if (rowTimer !== null)
1325
+ clearTimeout(rowTimer);
1326
+ rowTimer = null;
1046
1327
  views.clear();
1047
1328
  lives.clear();
1048
1329
  unsent.clear();
1049
1330
  listWatchers.clear();
1331
+ unlisted.clear();
1050
1332
  agentWatchers.clear();
1333
+ unnamed.clear();
1051
1334
  },
1052
1335
  };
1053
1336
  }