@animalabs/agent-framework 0.32.0 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. package/README.md +204 -14
  2. package/dist/src/admission/admit.d.ts +18 -0
  3. package/dist/src/admission/admit.d.ts.map +1 -1
  4. package/dist/src/admission/admit.js +16 -3
  5. package/dist/src/admission/admit.js.map +1 -1
  6. package/dist/src/agent.d.ts +185 -78
  7. package/dist/src/agent.d.ts.map +1 -1
  8. package/dist/src/agent.js +279 -102
  9. package/dist/src/agent.js.map +1 -1
  10. package/dist/src/api/mcp-server-core.d.ts.map +1 -1
  11. package/dist/src/api/mcp-server-core.js +50 -12
  12. package/dist/src/api/mcp-server-core.js.map +1 -1
  13. package/dist/src/api/server.d.ts.map +1 -1
  14. package/dist/src/api/server.js +4 -20
  15. package/dist/src/api/server.js.map +1 -1
  16. package/dist/src/code-execution/isolate/scripts.d.ts +12 -8
  17. package/dist/src/code-execution/isolate/scripts.d.ts.map +1 -1
  18. package/dist/src/code-execution/isolate/scripts.js +24 -0
  19. package/dist/src/code-execution/isolate/scripts.js.map +1 -1
  20. package/dist/src/config-checks.d.ts +7 -0
  21. package/dist/src/config-checks.d.ts.map +1 -1
  22. package/dist/src/config-checks.js +14 -0
  23. package/dist/src/config-checks.js.map +1 -1
  24. package/dist/src/context-manager-errors.d.ts +25 -0
  25. package/dist/src/context-manager-errors.d.ts.map +1 -0
  26. package/dist/src/context-manager-errors.js +31 -0
  27. package/dist/src/context-manager-errors.js.map +1 -0
  28. package/dist/src/context-options.d.ts +67 -0
  29. package/dist/src/context-options.d.ts.map +1 -0
  30. package/dist/src/context-options.js +85 -0
  31. package/dist/src/context-options.js.map +1 -0
  32. package/dist/src/deferred-import.d.ts +93 -0
  33. package/dist/src/deferred-import.d.ts.map +1 -0
  34. package/dist/src/deferred-import.js +177 -0
  35. package/dist/src/deferred-import.js.map +1 -0
  36. package/dist/src/dendrite/audience.d.ts +223 -0
  37. package/dist/src/dendrite/audience.d.ts.map +1 -0
  38. package/dist/src/dendrite/audience.js +202 -0
  39. package/dist/src/dendrite/audience.js.map +1 -0
  40. package/dist/src/dendrite/index.d.ts +5 -3
  41. package/dist/src/dendrite/index.d.ts.map +1 -1
  42. package/dist/src/dendrite/index.js +2 -1
  43. package/dist/src/dendrite/index.js.map +1 -1
  44. package/dist/src/dendrite/presets.d.ts +10 -5
  45. package/dist/src/dendrite/presets.d.ts.map +1 -1
  46. package/dist/src/dendrite/presets.js +17 -5
  47. package/dist/src/dendrite/presets.js.map +1 -1
  48. package/dist/src/dendrite/registry.d.ts +62 -7
  49. package/dist/src/dendrite/registry.d.ts.map +1 -1
  50. package/dist/src/dendrite/registry.js +179 -10
  51. package/dist/src/dendrite/registry.js.map +1 -1
  52. package/dist/src/dendrite/types.d.ts +91 -8
  53. package/dist/src/dendrite/types.d.ts.map +1 -1
  54. package/dist/src/foreign-rounds.d.ts +69 -0
  55. package/dist/src/foreign-rounds.d.ts.map +1 -0
  56. package/dist/src/foreign-rounds.js +152 -0
  57. package/dist/src/foreign-rounds.js.map +1 -0
  58. package/dist/src/framework.d.ts +1976 -818
  59. package/dist/src/framework.d.ts.map +1 -1
  60. package/dist/src/framework.js +7566 -3422
  61. package/dist/src/framework.js.map +1 -1
  62. package/dist/src/gate/event-gate.d.ts +85 -15
  63. package/dist/src/gate/event-gate.d.ts.map +1 -1
  64. package/dist/src/gate/event-gate.js +421 -47
  65. package/dist/src/gate/event-gate.js.map +1 -1
  66. package/dist/src/gate/index.d.ts +1 -1
  67. package/dist/src/gate/index.d.ts.map +1 -1
  68. package/dist/src/gate/index.js.map +1 -1
  69. package/dist/src/gate/rule-scope.d.ts +30 -0
  70. package/dist/src/gate/rule-scope.d.ts.map +1 -0
  71. package/dist/src/gate/rule-scope.js +41 -0
  72. package/dist/src/gate/rule-scope.js.map +1 -0
  73. package/dist/src/gate/suppress-script-worker.d.ts +2 -0
  74. package/dist/src/gate/suppress-script-worker.d.ts.map +1 -0
  75. package/dist/src/gate/suppress-script-worker.js +103 -0
  76. package/dist/src/gate/suppress-script-worker.js.map +1 -0
  77. package/dist/src/gate/suppress-scripts.d.ts +105 -0
  78. package/dist/src/gate/suppress-scripts.d.ts.map +1 -0
  79. package/dist/src/gate/suppress-scripts.js +403 -0
  80. package/dist/src/gate/suppress-scripts.js.map +1 -0
  81. package/dist/src/gate/suppression-review.d.ts +143 -0
  82. package/dist/src/gate/suppression-review.d.ts.map +1 -0
  83. package/dist/src/gate/suppression-review.js +463 -0
  84. package/dist/src/gate/suppression-review.js.map +1 -0
  85. package/dist/src/gate/suppression-stats.d.ts +125 -0
  86. package/dist/src/gate/suppression-stats.d.ts.map +1 -0
  87. package/dist/src/gate/suppression-stats.js +214 -0
  88. package/dist/src/gate/suppression-stats.js.map +1 -0
  89. package/dist/src/gate/types.d.ts +135 -7
  90. package/dist/src/gate/types.d.ts.map +1 -1
  91. package/dist/src/gate/wake-onboarding.d.ts +119 -0
  92. package/dist/src/gate/wake-onboarding.d.ts.map +1 -0
  93. package/dist/src/gate/wake-onboarding.js +277 -0
  94. package/dist/src/gate/wake-onboarding.js.map +1 -0
  95. package/dist/src/held-writes.d.ts +31 -23
  96. package/dist/src/held-writes.d.ts.map +1 -1
  97. package/dist/src/held-writes.js +44 -59
  98. package/dist/src/held-writes.js.map +1 -1
  99. package/dist/src/index.d.ts +5 -2
  100. package/dist/src/index.d.ts.map +1 -1
  101. package/dist/src/index.js +3 -0
  102. package/dist/src/index.js.map +1 -1
  103. package/dist/src/jsonrpc-envelope.d.ts +69 -0
  104. package/dist/src/jsonrpc-envelope.d.ts.map +1 -0
  105. package/dist/src/jsonrpc-envelope.js +66 -0
  106. package/dist/src/jsonrpc-envelope.js.map +1 -0
  107. package/dist/src/mcpl/channel-registry.d.ts +131 -15
  108. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  109. package/dist/src/mcpl/channel-registry.js +290 -46
  110. package/dist/src/mcpl/channel-registry.js.map +1 -1
  111. package/dist/src/mcpl/checkpoint-manager.d.ts +2 -4
  112. package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -1
  113. package/dist/src/mcpl/checkpoint-manager.js.map +1 -1
  114. package/dist/src/mcpl/feature-set-manager.d.ts +2 -4
  115. package/dist/src/mcpl/feature-set-manager.d.ts.map +1 -1
  116. package/dist/src/mcpl/feature-set-manager.js.map +1 -1
  117. package/dist/src/mcpl/inbound-source.d.ts +2 -1
  118. package/dist/src/mcpl/inbound-source.d.ts.map +1 -1
  119. package/dist/src/mcpl/inbound-source.js +2 -1
  120. package/dist/src/mcpl/inbound-source.js.map +1 -1
  121. package/dist/src/mcpl/inference-router.d.ts +6 -6
  122. package/dist/src/mcpl/inference-router.d.ts.map +1 -1
  123. package/dist/src/mcpl/inference-router.js.map +1 -1
  124. package/dist/src/mcpl/push-coalescer.d.ts +132 -22
  125. package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
  126. package/dist/src/mcpl/push-coalescer.js +105 -15
  127. package/dist/src/mcpl/push-coalescer.js.map +1 -1
  128. package/dist/src/mcpl/push-handler.d.ts +9 -7
  129. package/dist/src/mcpl/push-handler.d.ts.map +1 -1
  130. package/dist/src/mcpl/push-handler.js.map +1 -1
  131. package/dist/src/mcpl/server-connection.d.ts +13 -10
  132. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  133. package/dist/src/mcpl/server-connection.js +134 -56
  134. package/dist/src/mcpl/server-connection.js.map +1 -1
  135. package/dist/src/mcpl/tool-classes.d.ts.map +1 -1
  136. package/dist/src/mcpl/tool-classes.js +3 -0
  137. package/dist/src/mcpl/tool-classes.js.map +1 -1
  138. package/dist/src/mcpl/types.d.ts +8 -2
  139. package/dist/src/mcpl/types.d.ts.map +1 -1
  140. package/dist/src/mcpl/types.js.map +1 -1
  141. package/dist/src/module-registry.d.ts +24 -1
  142. package/dist/src/module-registry.d.ts.map +1 -1
  143. package/dist/src/module-registry.js +68 -18
  144. package/dist/src/module-registry.js.map +1 -1
  145. package/dist/src/modules/discord/index.d.ts +45 -8
  146. package/dist/src/modules/discord/index.d.ts.map +1 -1
  147. package/dist/src/modules/discord/index.js +237 -86
  148. package/dist/src/modules/discord/index.js.map +1 -1
  149. package/dist/src/modules/history/index.d.ts +59 -0
  150. package/dist/src/modules/history/index.d.ts.map +1 -1
  151. package/dist/src/modules/history/index.js +432 -67
  152. package/dist/src/modules/history/index.js.map +1 -1
  153. package/dist/src/modules/history/semantic.d.ts +6 -5
  154. package/dist/src/modules/history/semantic.d.ts.map +1 -1
  155. package/dist/src/modules/history/semantic.js +15 -22
  156. package/dist/src/modules/history/semantic.js.map +1 -1
  157. package/dist/src/modules/workspace/disk-agreement.d.ts +1 -1
  158. package/dist/src/modules/workspace/disk-agreement.js +1 -1
  159. package/dist/src/modules/workspace/index.d.ts +28 -7
  160. package/dist/src/modules/workspace/index.d.ts.map +1 -1
  161. package/dist/src/modules/workspace/index.js +76 -22
  162. package/dist/src/modules/workspace/index.js.map +1 -1
  163. package/dist/src/notice-tags.d.ts +10 -1
  164. package/dist/src/notice-tags.d.ts.map +1 -1
  165. package/dist/src/notice-tags.js +10 -1
  166. package/dist/src/notice-tags.js.map +1 -1
  167. package/dist/src/operator-log.d.ts +36 -3
  168. package/dist/src/operator-log.d.ts.map +1 -1
  169. package/dist/src/operator-log.js +2 -2
  170. package/dist/src/operator-log.js.map +1 -1
  171. package/dist/src/presentation.d.ts +746 -0
  172. package/dist/src/presentation.d.ts.map +1 -0
  173. package/dist/src/presentation.js +1435 -0
  174. package/dist/src/presentation.js.map +1 -0
  175. package/dist/src/prose-drafts.d.ts +15 -11
  176. package/dist/src/prose-drafts.d.ts.map +1 -1
  177. package/dist/src/prose-drafts.js +16 -12
  178. package/dist/src/prose-drafts.js.map +1 -1
  179. package/dist/src/read-marks.d.ts +192 -0
  180. package/dist/src/read-marks.d.ts.map +1 -0
  181. package/dist/src/read-marks.js +242 -0
  182. package/dist/src/read-marks.js.map +1 -0
  183. package/dist/src/recovery/offline-branch.d.ts +91 -6
  184. package/dist/src/recovery/offline-branch.d.ts.map +1 -1
  185. package/dist/src/recovery/offline-branch.js +273 -29
  186. package/dist/src/recovery/offline-branch.js.map +1 -1
  187. package/dist/src/recovery/recover-cli.js +22 -3
  188. package/dist/src/recovery/recover-cli.js.map +1 -1
  189. package/dist/src/send-tools.d.ts +5 -9
  190. package/dist/src/send-tools.d.ts.map +1 -1
  191. package/dist/src/send-tools.js +40 -7
  192. package/dist/src/send-tools.js.map +1 -1
  193. package/dist/src/stored-injections.d.ts +2 -2
  194. package/dist/src/stored-injections.js +2 -2
  195. package/dist/src/stored-removal.d.ts +17 -4
  196. package/dist/src/stored-removal.d.ts.map +1 -1
  197. package/dist/src/stored-removal.js +29 -5
  198. package/dist/src/stored-removal.js.map +1 -1
  199. package/dist/src/tool-result-guard.d.ts +12 -1
  200. package/dist/src/tool-result-guard.d.ts.map +1 -1
  201. package/dist/src/tool-result-guard.js +42 -8
  202. package/dist/src/tool-result-guard.js.map +1 -1
  203. package/dist/src/tune-out/coordinator.d.ts +1159 -79
  204. package/dist/src/tune-out/coordinator.d.ts.map +1 -1
  205. package/dist/src/tune-out/coordinator.js +3709 -363
  206. package/dist/src/tune-out/coordinator.js.map +1 -1
  207. package/dist/src/tune-out/tools.d.ts +114 -17
  208. package/dist/src/tune-out/tools.d.ts.map +1 -1
  209. package/dist/src/tune-out/tools.js +87 -27
  210. package/dist/src/tune-out/tools.js.map +1 -1
  211. package/dist/src/tune-out/window-anchor.d.ts +143 -0
  212. package/dist/src/tune-out/window-anchor.d.ts.map +1 -0
  213. package/dist/src/tune-out/window-anchor.js +202 -0
  214. package/dist/src/tune-out/window-anchor.js.map +1 -0
  215. package/dist/src/types/agent.d.ts +82 -20
  216. package/dist/src/types/agent.d.ts.map +1 -1
  217. package/dist/src/types/events.d.ts +2 -0
  218. package/dist/src/types/events.d.ts.map +1 -1
  219. package/dist/src/types/framework.d.ts +32 -12
  220. package/dist/src/types/framework.d.ts.map +1 -1
  221. package/dist/src/types/module.d.ts +180 -69
  222. package/dist/src/types/module.d.ts.map +1 -1
  223. package/dist/src/types/trace.d.ts +510 -36
  224. package/dist/src/types/trace.d.ts.map +1 -1
  225. package/package.json +3 -3
  226. package/src/admission/admit.ts +32 -3
  227. package/src/agent.ts +323 -120
  228. package/src/api/mcp-server-core.ts +54 -17
  229. package/src/api/server.ts +4 -23
  230. package/src/code-execution/isolate/scripts.ts +28 -3
  231. package/src/config-checks.ts +14 -0
  232. package/src/context-manager-errors.ts +37 -0
  233. package/src/context-options.ts +96 -0
  234. package/src/deferred-import.ts +239 -0
  235. package/src/dendrite/audience.ts +315 -0
  236. package/src/dendrite/index.ts +14 -2
  237. package/src/dendrite/presets.ts +18 -6
  238. package/src/dendrite/registry.ts +209 -12
  239. package/src/dendrite/types.ts +92 -8
  240. package/src/foreign-rounds.ts +199 -0
  241. package/src/framework.ts +7804 -3546
  242. package/src/gate/event-gate.ts +428 -65
  243. package/src/gate/index.ts +3 -0
  244. package/src/gate/rule-scope.ts +45 -0
  245. package/src/gate/suppress-script-worker.ts +89 -0
  246. package/src/gate/suppress-scripts.ts +414 -0
  247. package/src/gate/suppression-review.ts +517 -0
  248. package/src/gate/suppression-stats.ts +314 -0
  249. package/src/gate/types.ts +141 -7
  250. package/src/gate/wake-onboarding.ts +309 -0
  251. package/src/held-writes.ts +49 -60
  252. package/src/index.ts +9 -2
  253. package/src/jsonrpc-envelope.ts +81 -0
  254. package/src/mcpl/channel-registry.ts +353 -85
  255. package/src/mcpl/checkpoint-manager.ts +3 -2
  256. package/src/mcpl/feature-set-manager.ts +2 -1
  257. package/src/mcpl/inbound-source.ts +2 -1
  258. package/src/mcpl/inference-router.ts +7 -4
  259. package/src/mcpl/push-coalescer.ts +193 -34
  260. package/src/mcpl/push-handler.ts +10 -5
  261. package/src/mcpl/server-connection.ts +141 -60
  262. package/src/mcpl/tool-classes.ts +3 -0
  263. package/src/mcpl/types.ts +8 -2
  264. package/src/module-registry.ts +73 -17
  265. package/src/modules/discord/index.ts +229 -81
  266. package/src/modules/history/index.ts +439 -70
  267. package/src/modules/history/semantic.ts +15 -21
  268. package/src/modules/workspace/disk-agreement.ts +1 -1
  269. package/src/modules/workspace/index.ts +85 -23
  270. package/src/notice-tags.ts +10 -1
  271. package/src/operator-log.ts +36 -3
  272. package/src/presentation.ts +1676 -0
  273. package/src/prose-drafts.ts +16 -12
  274. package/src/read-marks.ts +328 -0
  275. package/src/recovery/offline-branch.ts +346 -32
  276. package/src/recovery/recover-cli.ts +21 -3
  277. package/src/send-tools.ts +40 -7
  278. package/src/stored-injections.ts +2 -2
  279. package/src/stored-removal.ts +31 -7
  280. package/src/tool-result-guard.ts +41 -7
  281. package/src/tune-out/coordinator.ts +4089 -457
  282. package/src/tune-out/tools.ts +160 -43
  283. package/src/tune-out/window-anchor.ts +277 -0
  284. package/src/types/agent.ts +86 -20
  285. package/src/types/events.ts +2 -0
  286. package/src/types/framework.ts +32 -12
  287. package/src/types/module.ts +181 -68
  288. package/src/types/trace.ts +542 -40
@@ -11,12 +11,14 @@
11
11
  * the exact words until the resident resends or dismisses them, or the
12
12
  * retention below lets it go.
13
13
  *
14
- * The boundary is publication and agency, not visibility: a draft is never
14
+ * The boundary is publication and agency: a draft is never
15
15
  * published except by its resident's explicit resend (or `{{unsent}}`), and
16
16
  * only that resident can list or act on it. Its notices go into the
17
- * resident's own history — which residents that share one message slot
18
- * (#197) share, as they already share the assistant turn the words came
19
- * from.
17
+ * resident's own history: residents that share one message slot store
18
+ * them in that slot, but each is stamped as its resident's: the drafts
19
+ * notice as its own row, which enters no other resident's window but an
20
+ * observer's (AgentConfig.observes), as the assistant turn the words came
21
+ * from does, and a bounce notice as for that resident alone.
20
22
  *
21
23
  * Durability is outside the conversation. Drafts live in a RecordJournal of
22
24
  * typed Chronicle records, which do not follow branch switches: undo,
@@ -24,8 +26,10 @@
24
26
  * in a turn that is later undone stays usable (the words were genuinely
25
27
  * written and held back), and a delivered draft stays delivered. The
26
28
  * notices that name a draft are conversation, so they do follow the branch: a draft counts as
27
- * named while a message naming it is on the current branch, and one a
28
- * rollback or branch move has left unnamed is named again.
29
+ * named while a message naming it is on the current branch and, on a
30
+ * presented window, not taken out of it by an undo; one a rollback or
31
+ * branch move has left unnamed, or whose notice an undo took out of the
32
+ * window, is named again.
29
33
  *
30
34
  * Drafts are kept, not hoarded. Each resident's KEEP_NEWEST newest drafts
31
35
  * are always kept; an older one, in any state, is let go at the journal's
@@ -405,13 +409,13 @@ export class ProseDraftStore {
405
409
  }
406
410
 
407
411
  /**
408
- * Open drafts, newest first, that no notice on the current branch names:
409
- * none was recorded (a crash before the notice), or a rollback or branch
410
- * move took away every message that named them. `onBranch` says whether a
411
- * message, by id, is on the current branch.
412
+ * Open drafts, newest first, that no standing notice names: none was
413
+ * recorded (a crash before the notice), or a rollback, a branch move or an
414
+ * undo took away every message that named them. `stands` says whether a
415
+ * message, by id, still names them to the agent.
412
416
  */
413
- unnoticed(agent: string, onBranch: (messageId: string) => boolean): Draft[] {
414
- return this.open(agent).filter((d) => !d.noticedIn?.some(onBranch));
417
+ unnoticed(agent: string, stands: (messageId: string) => boolean): Draft[] {
418
+ return this.open(agent).filter((d) => !d.noticedIn?.some(stands));
415
419
  }
416
420
 
417
421
  /**
@@ -0,0 +1,328 @@
1
+ /**
2
+ * Read marks (#291): how far an agent had read, or been shown, or answered,
3
+ * when something happened, and whether a stored row falls within it.
4
+ *
5
+ * Every such question used to compare a row's own store sequence with the
6
+ * chronicle head taken at the moment (`{ branch, sequence }`). While the
7
+ * window an agent reads is the record itself (context-manager's legacy
8
+ * mode) that is exact: a compile reads every row stored so far that its
9
+ * view keeps (a row its view leaves out it never read, and each reader of a
10
+ * row is weighed by its own view, agentSees). Once the
11
+ * agent's window follows its presentation index (`presentRows`), a row is
12
+ * stored first and presented later, so a row stored before the head was
13
+ * taken can still be out of the window then, and the head says nothing of
14
+ * what the agent was shown; a mark is taken at a request, which carries the
15
+ * window as it then stands. The window's own coordinate is the index ordinal: a
16
+ * row is in the window as of a mark when its ordinal is below the index
17
+ * length taken with the mark (`indexLength()` is exclusive: the ordinal the
18
+ * next row will take).
19
+ *
20
+ * A mark keeps both: the head, as before, and the index length when the
21
+ * manager was in presented mode. A mark taken in legacy mode stands for a
22
+ * window that was the record up to its head. Read against a manager still
23
+ * in legacy mode, it covers by sequence. Read once the branch's window is
24
+ * written down (presented mode), it covers the rows the window has held,
25
+ * where they stand now, since its head or before: the rows a branch cut at
26
+ * the head would keep in its window (ContextManager.branchSequenceOf at or
27
+ * below the head). That sequence is a row's own for a row the write-down put
28
+ * in the window, and the write that presented it for any other, so it only
29
+ * grows along the window: the rows a cut at the head keeps are the window's
30
+ * first ones, and the mark reads as one taken in presented mode whose length
31
+ * is the ordinal of the first row past them (windowLengthAt). For a mark taken
32
+ * before the write-down, which lists the record in record order, those are
33
+ * the rows the write-down put in the window at or below the head that are
34
+ * still where it put them. A row stored after the head isn't covered, and
35
+ * neither is a row taken out of the window since, or one presented again,
36
+ * at a new ordinal past the write-down, which a compare by sequence would
37
+ * cover: a reader walking the window back from its newest row (the turn's
38
+ * inputs, turnInputs) would stop at that older row and leave the rows the
39
+ * agent hadn't read before it out. Nothing is converted in place: every
40
+ * reader reads a mark through coverTest, which makes the same reading for
41
+ * each.
42
+ *
43
+ * The mark is opaque: its fields are keyed by symbols this module keeps, so
44
+ * a `mark.sequence` read or a hand-built `{ branch, sequence }` doesn't
45
+ * compile. Every compare goes through `covers` (or `coverTest` for many
46
+ * rows) and `markAtOrAfter`. The keys are symbols rather than `#private`
47
+ * fields so two marks taken at the same place are deep-equal.
48
+ */
49
+ import type { ContextManager, StoredMessage } from '@animalabs/context-manager';
50
+
51
+ declare const ordinalBrand: unique symbol;
52
+ /** A row's index ordinal in one agent's window on one branch (ContextManager.ordinalOf). Never a store sequence. */
53
+ export type WindowOrdinal = number & { readonly [ordinalBrand]: true };
54
+
55
+ declare const lengthBrand: unique symbol;
56
+ /** An index length (ContextManager.indexLength): exclusive, the ordinal the next presented row will take. */
57
+ export type WindowLength = number & { readonly [lengthBrand]: true };
58
+
59
+ declare const headBrand: unique symbol;
60
+ /** The store's head sequence (currentSequence) when a mark was taken. */
61
+ export type StoreHead = number & { readonly [headBrand]: true };
62
+
63
+ const HEAD: unique symbol = Symbol('read-mark head');
64
+ const LENGTH: unique symbol = Symbol('read-mark length');
65
+
66
+ /**
67
+ * Where an agent had read to on one branch: the store's head then, and the
68
+ * window's index length when the agent's manager was in presented mode (null
69
+ * in legacy mode). Read only through this module (coverTest): a mark taken in
70
+ * legacy mode covers by sequence while its window is the record, and, once
71
+ * the window is written down, as a mark taken in presented mode that covers
72
+ * the rows the window held by its head where they still stand.
73
+ */
74
+ export interface ReadMark {
75
+ readonly branch: string;
76
+ readonly [HEAD]: StoreHead;
77
+ readonly [LENGTH]: WindowLength | null;
78
+ }
79
+
80
+ /**
81
+ * Whether a mark covers a row: `'covered'` and `'not-covered'` are answers;
82
+ * `'doubt'` is a mark that can't say (none, one on another branch, or one
83
+ * taken in presented mode read against a manager that no longer presents),
84
+ * which each reader maps to the choice it makes when it doesn't know.
85
+ */
86
+ export type Cover = 'covered' | 'not-covered' | 'doubt';
87
+
88
+ /** What a mark is taken from and read against: a context manager, or a double of one. */
89
+ export type MarkCm = Partial<Pick<ContextManager,
90
+ 'currentBranch' | 'getStore' | 'getAllMessages' | 'getMessageCount' | 'windowMode' | 'indexLength' | 'ordinalOf'
91
+ | 'getPresentedMessages' | 'branchSequenceOf'>>;
92
+
93
+ /** A row as a cover reads it. */
94
+ export type MarkedRow = Pick<StoredMessage, 'id' | 'sequence'>;
95
+
96
+ function mark(branch: string, head: number, length: number | null): ReadMark {
97
+ return Object.freeze({ branch, [HEAD]: head as StoreHead, [LENGTH]: length as WindowLength | null });
98
+ }
99
+
100
+ /**
101
+ * A mark of where `cm` stands now: its branch, the store's head (the
102
+ * chronicle head, not the slot's last row: in legacy mode a compile reads
103
+ * everything stored on the branch that its view keeps, rows merged in from
104
+ * another slot included; a double without a store gives its last row's
105
+ * sequence), and
106
+ * its index length when it is in presented mode, where a compile reads the
107
+ * window and the mark covers by that length, not by the head. Null for a
108
+ * double that can't say, or when reading throws.
109
+ */
110
+ export function readMarkOf(cm: MarkCm): ReadMark | null {
111
+ try {
112
+ const head = storeHeadOf(cm);
113
+ if (head === null) return null;
114
+ const presented = typeof cm.windowMode === 'function' && cm.windowMode() === 'presented'
115
+ && typeof cm.indexLength === 'function';
116
+ return mark(cm.currentBranch!().name, head, presented ? cm.indexLength!() : null);
117
+ } catch {
118
+ return null;
119
+ }
120
+ }
121
+
122
+ /**
123
+ * The store's head as `cm` reads it now, the head a mark readMarkOf takes
124
+ * keeps: the chronicle head, not the slot's last message, since in legacy
125
+ * mode a compile reads everything stored on the branch so far, rows merged
126
+ * in from another slot included (in presented mode it reads the window,
127
+ * which a mark covers by its index length). A record sequence, on the
128
+ * branch `cm` is on: for a reader
129
+ * that compares stored rows' sequences with it on that branch (Agent's
130
+ * branchHead), not a cover of the window. Null for a context double that
131
+ * can't say.
132
+ */
133
+ export function storeHeadOf(cm: MarkCm): number | null {
134
+ try {
135
+ if (typeof cm.currentBranch !== 'function' || typeof cm.getAllMessages !== 'function') return null;
136
+ const store = typeof cm.getStore === 'function' ? cm.getStore() as { currentSequence?: () => number } : undefined;
137
+ let head = typeof store?.currentSequence === 'function' ? store.currentSequence() : undefined;
138
+ if (head === undefined) {
139
+ const count = typeof cm.getMessageCount === 'function' ? cm.getMessageCount() : cm.getAllMessages().length;
140
+ head = count > 0 ? cm.getAllMessages().at(-1)?.sequence ?? 0 : 0;
141
+ }
142
+ return head;
143
+ } catch {
144
+ return null;
145
+ }
146
+ }
147
+
148
+ /**
149
+ * A mark of where `cm` stands now, as readMarkOf takes one, that covers its
150
+ * window only below `length` (an index length of that window, at most its
151
+ * own): for a redo on the window (AgentFramework.redo), which puts back,
152
+ * at new ordinals and in their order, rows a mark covered before they were
153
+ * undone and rows it didn't, so the mark it gives again covers the first
154
+ * and not the rest. Null where readMarkOf gives null, and in legacy mode,
155
+ * where a mark has no length.
156
+ */
157
+ export function readMarkThrough(cm: MarkCm, length: number): ReadMark | null {
158
+ const now = readMarkOf(cm);
159
+ if (!now || now[LENGTH] === null) return null;
160
+ return mark(now.branch, now[HEAD], Math.min(length, now[LENGTH]));
161
+ }
162
+
163
+ /**
164
+ * Whether a mark was taken in presented mode: it keeps the window's index
165
+ * length, and covers rows by their ordinals there, so a row taken out of the
166
+ * window and presented again, at a new ordinal past its length, leaves it.
167
+ * A mark taken in legacy mode keeps none: it covers by sequence while its
168
+ * window is the record, and, once the window is written down, by ordinal
169
+ * too, below the first row the window didn't hold by its head (coverTest),
170
+ * so a row presented again after it leaves it in the same way.
171
+ */
172
+ export function coversByOrdinal(mark: ReadMark | null): boolean {
173
+ return !!mark && mark[LENGTH] !== null;
174
+ }
175
+
176
+ /**
177
+ * A test of many rows against one mark, read against `reader` (the manager
178
+ * whose window the mark is of), which reads the reader's branch and mode
179
+ * once. The rules:
180
+ *
181
+ * - no mark: doubt;
182
+ * - a mark on another branch than the reader's: doubt, except that a mark
183
+ * taken in legacy mode, read `across` a branch point (`partAt`, the last
184
+ * sequence the two branches share), reads as one on the reader's branch
185
+ * (below) through the lower of its head and the branch point;
186
+ * - a mark taken in legacy mode, read against a reader in legacy mode: a
187
+ * row at or below its head is covered;
188
+ * - a mark taken in legacy mode, read against a reader in presented mode:
189
+ * a row the window has held where it stands since the head or before is
190
+ * covered (windowLengthAt: the window's rows below the ordinal of its first
191
+ * row a branch cut at the head wouldn't keep), and no other: not a row
192
+ * stored after the head, out of the window, or presented after the head,
193
+ * again or for the first time. A reader that can't say where its window
194
+ * held a row (a double without ordinalOf, getPresentedMessages,
195
+ * branchSequenceOf or indexLength) is read by sequence, as in legacy
196
+ * mode; a read of the window that throws is doubt for the rows at or
197
+ * below the head;
198
+ * - a mark taken in presented mode, read against a reader no longer in it:
199
+ * doubt;
200
+ * - else a row is covered when its ordinal is below the mark's length:
201
+ * strictly, since the length is the ordinal the next row would take. A row
202
+ * out of the window (never presented, or taken out) is not covered.
203
+ */
204
+ export function coverTest(mark: ReadMark | null, reader: MarkCm, across?: { partAt: number }): (row: MarkedRow) => Cover {
205
+ if (!mark) return () => 'doubt';
206
+ let branch: string | undefined;
207
+ let presented = false;
208
+ try {
209
+ branch = typeof reader.currentBranch === 'function' ? reader.currentBranch().name : undefined;
210
+ presented = typeof reader.windowMode === 'function' && reader.windowMode() === 'presented';
211
+ } catch {
212
+ return () => 'doubt';
213
+ }
214
+ const head = mark[HEAD];
215
+ const length = mark[LENGTH];
216
+ if (branch !== mark.branch) {
217
+ if (!across || length !== null) return () => 'doubt';
218
+ return legacyCover(reader, presented, Math.min(head, across.partAt));
219
+ }
220
+ if (length === null) return legacyCover(reader, presented, head);
221
+ if (!presented || typeof reader.ordinalOf !== 'function') return () => 'doubt';
222
+ return byOrdinal(reader.ordinalOf.bind(reader), length);
223
+ }
224
+
225
+ function byOrdinal(ordinalOf: (id: MarkedRow['id']) => number | undefined, length: number): (row: MarkedRow) => Cover {
226
+ return (row) => {
227
+ const ordinal = ordinalOf(row.id);
228
+ return ordinal !== undefined && ordinal < length ? 'covered' : 'not-covered';
229
+ };
230
+ }
231
+
232
+ /**
233
+ * How a mark taken in legacy mode covers through `through` (its head, or the
234
+ * lower of its head and a branch point): by sequence against a reader in
235
+ * legacy mode, or one that can't say where its window held a row; against
236
+ * one in presented mode, by ordinal below windowLengthAt(reader, through),
237
+ * read once, at the first row that needs it. A row stored after `through`
238
+ * entered the window after it, wherever it stands, so it is not covered
239
+ * without that read.
240
+ */
241
+ function legacyCover(reader: MarkCm, presented: boolean, through: number): (row: MarkedRow) => Cover {
242
+ const bySequence = (row: MarkedRow): Cover => (row.sequence <= through ? 'covered' : 'not-covered');
243
+ if (!presented || typeof reader.ordinalOf !== 'function' || typeof reader.getPresentedMessages !== 'function'
244
+ || typeof reader.branchSequenceOf !== 'function' || typeof reader.indexLength !== 'function') {
245
+ return bySequence;
246
+ }
247
+ let cover: ((row: MarkedRow) => Cover) | undefined;
248
+ return (row) => {
249
+ if (row.sequence > through) return 'not-covered';
250
+ if (!cover) {
251
+ const length = windowLengthAt(reader, through);
252
+ cover = length === null ? () => 'doubt' : byOrdinal(reader.ordinalOf!.bind(reader), length);
253
+ }
254
+ return cover(row);
255
+ };
256
+ }
257
+
258
+ /**
259
+ * The index length a mark taken in presented mode would hold to cover the
260
+ * rows `reader`'s window (in presented mode) has held, where they stand
261
+ * now, since sequence `through` or before: the ordinal of the window's first
262
+ * row that a branch cut at `through` wouldn't keep in its window, or the
263
+ * index length when such a cut keeps them all. A cut keeps a row when its
264
+ * branchSequenceOf (the sequence a cut has to reach for its window to hold
265
+ * the row) is at or below `through`: a row's own sequence (its body's last
266
+ * shard's) for a row the write-down put in the window, which lists the
267
+ * record in record order, and the write that presented it for any other,
268
+ * which comes after the write-down and after every write that presented a
269
+ * row before it. So that sequence only grows along the window, and the
270
+ * first row past `through` is found by a binary search over it: a few reads
271
+ * per mark, whatever the window's length. Null when a read throws.
272
+ */
273
+ function windowLengthAt(reader: MarkCm, through: number): number | null {
274
+ try {
275
+ const window = reader.getPresentedMessages!();
276
+ let low = 0;
277
+ let high = window.length;
278
+ while (low < high) {
279
+ const middle = (low + high) >>> 1;
280
+ if (reader.branchSequenceOf!(window[middle]!.id) <= through) low = middle + 1;
281
+ else high = middle;
282
+ }
283
+ if (low === window.length) return reader.indexLength!();
284
+ return reader.ordinalOf!(window[low]!.id) ?? null;
285
+ } catch {
286
+ return null;
287
+ }
288
+ }
289
+
290
+ /** Whether `mark` covers `row` in `reader`'s window (see coverTest for the rules). */
291
+ export function covers(mark: ReadMark | null, reader: MarkCm, row: MarkedRow, across?: { partAt: number }): Cover {
292
+ return coverTest(mark, reader, across)(row);
293
+ }
294
+
295
+ /**
296
+ * Whether mark `a` was taken at or after mark `b`: on one branch, by the
297
+ * window's index length when both keep one, which only grows there and
298
+ * grows at every presentation, and by the store's head, which only grows
299
+ * there too, where the lengths are equal or either mark keeps none;
300
+ * `'other-branch'` when they are on two. Two marks at one head can keep two
301
+ * lengths: a mark readMarkThrough gives (a redo's) covers less of the window
302
+ * than one taken at the same head, and a double without a store reads its
303
+ * head from its last row, which no presentation moves. The one with the
304
+ * greater length is the later: a publish noted with it (Agent.notePublished)
305
+ * is credited with the rows presented between.
306
+ */
307
+ export function markAtOrAfter(a: ReadMark, b: ReadMark): boolean | 'other-branch' {
308
+ if (a.branch !== b.branch) return 'other-branch';
309
+ const [aLength, bLength] = [a[LENGTH], b[LENGTH]];
310
+ if (aLength !== null && bLength !== null && aLength !== bLength) return aLength > bLength;
311
+ return a[HEAD] >= b[HEAD];
312
+ }
313
+
314
+ /** A mark for a log line or a test's message: `main@412` in legacy mode, `main@412#57` with its index length. */
315
+ export function describeReadMark(mark: ReadMark | null): string {
316
+ if (!mark) return 'no mark';
317
+ return `${mark.branch}@${mark[HEAD]}${mark[LENGTH] === null ? '' : `#${mark[LENGTH]}`}`;
318
+ }
319
+
320
+ /**
321
+ * A mark built from its parts, for a test that needs one taken at a given
322
+ * place. Production code takes marks with readMarkOf.
323
+ *
324
+ * @internal
325
+ */
326
+ export function readMarkForTest(branch: string, head: number, length: number | null): ReadMark {
327
+ return mark(branch, head, length);
328
+ }