@agentshouse/mdruntime 0.1.0-c94cfdbdb6f68 → 0.1.0-c95dc33df5c05

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