@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
@@ -0,0 +1,315 @@
1
+ /**
2
+ * The audience stamp (#291): who a stored row is for, written on the row
3
+ * when it is stored, in its metadata.
4
+ *
5
+ * A row's target used to live only in the write that stored it: the
6
+ * `forAgent` option, or which agent's context manager a writer called. In
7
+ * the residents' shared slot, rows meant for different agents then look
8
+ * alike in the store, and nothing read later can tell whose a row is. #291's
9
+ * presentation index decides from the row which agents it is presented to,
10
+ * and at boot which rows an agent has to be presented that it wasn't, so the
11
+ * framework now writes that on every row it stores:
12
+ *
13
+ * - `metadata.audience` lists the agents the row is meant for, computed when
14
+ * it is written: the agent a write names (`forAgent`); for an admitted
15
+ * inbound message, the recipients admission delivered it to; for a
16
+ * module's process response whose inference request names agents, those
17
+ * of them registered that read the slot it lands in. A write that names
18
+ * no agent (a module's write, a process response that asks every agent
19
+ * or none to read, or only agents that don't read that slot) is for the
20
+ * agents that take untargeted inbound and own the slot it lands in: on a
21
+ * residents' host every resident, as each reads the shared slot; with one
22
+ * resident that resident alone, as before. It falls back to the agent the
23
+ * write is delivered to when there are none.
24
+ * This is the rule #291's presentation index presents an untargeted row
25
+ * in the shared slot by, applied when the row is written, so the record
26
+ * tells an untargeted row from one written for the primary. A message a
27
+ * tune-out holds is meant for none of them, so its list is empty, unless
28
+ * the write names an agent whose own view keeps held traffic (the
29
+ * persistent subconscious), which it is for.
30
+ * Observers of a slot (the subconscious watching the residents' slot) are
31
+ * a relationship in the registry, not part of the list. An inbound
32
+ * message's target is stamped as named even where it doesn't read the
33
+ * slot the row lands in (a worker or a conversation fork, stored in the
34
+ * residents' slot): how such a row is presented is left to the
35
+ * presentation index. A process response's messages and the gate's
36
+ * batched line are stamped for the agents they name that read that slot,
37
+ * and as a write that names no agent when none of them does, so they
38
+ * reach the windows that read the slot, as on main. The wake-onboarding
39
+ * notice is stamped for its recipients that read that slot; one that
40
+ * doesn't (a conversation fork) gets its own copy where it reads, stamped
41
+ * for it, and the notice for an event with no recipients is for the
42
+ * agents that take untargeted inbound.
43
+ * - `metadata.ownRowOf` names the agent whose own row it is: one stored by
44
+ * that agent's turn or act (its rounds, its tool results, a puppeted
45
+ * call), or by the framework for that agent's own turn (a clock row, a
46
+ * `[continue]` row, a batched wake's line, a notice about its own speech
47
+ * or failure, a derived agent's inheritance and framing). Its audience is
48
+ * that agent alone. "Own" goes by who stored the row, never by its
49
+ * participant: a shared fork presents its parent's name as its own voice,
50
+ * but mail its parent sends it is the parent's, meant for the fork, and
51
+ * isn't the fork's own row.
52
+ *
53
+ * Both keys are the framework's. A write through the framework's
54
+ * `addMessage` is stamped there and a stamp its metadata carried (a copy of
55
+ * a stored row's, say) is replaced, as `deferredWriteId` is, but on an
56
+ * imported write, which keeps the stamp it was queued with. The compile
57
+ * (context-manager's) doesn't read the stamp, so a window compiles as the
58
+ * same rows unstamped do; the presentation index does, so another
59
+ * resident's own rows no longer enter a resident's window unless it
60
+ * observes that resident (AgentConfig.observes). Rows stored before the
61
+ * stamp existed carry none; `audienceOf` says so by returning undefined. A
62
+ * write an earlier version left in its deferred queue is stamped when the
63
+ * importer stores it at boot, as any write through `addMessage` is, unless
64
+ * it was queued stamped, so it isn't stored unstamped after the upgrade.
65
+ */
66
+ import type { ContentBlock } from '@animalabs/membrane';
67
+ import type { AddMessageOptions, ContextManager, MessageId, MessageMetadata } from '@animalabs/context-manager';
68
+ import { WriteLandedError, WriteOutcomeUnknownError } from '../context-manager-errors.js';
69
+ import { suppressionRecordOf } from '../gate/suppression-stats.js';
70
+ import { isAnotherAgentsInjection } from '../stored-injections.js';
71
+ import type { AgentName, ObserveEdge } from './types.js';
72
+
73
+ /** The metadata key listing who a row is for. */
74
+ export const AUDIENCE_KEY = 'audience';
75
+ /** The metadata key naming whose own row a row is. */
76
+ export const OWN_ROW_KEY = 'ownRowOf';
77
+
78
+ /** A row's audience stamp, as `audienceOf` reads it. */
79
+ export interface AudienceStamp {
80
+ /** The agents the row is meant for, as computed when it was written. */
81
+ audience: AgentName[];
82
+ /** Set when the row is that agent's own (see the module comment). */
83
+ ownRowOf?: AgentName;
84
+ }
85
+
86
+ /**
87
+ * `metadata` stamped as a row meant for `audience`, and as nobody's own
88
+ * row: a stamp it carried is replaced. A new object; `metadata` is unchanged.
89
+ */
90
+ export function withAudience(metadata: MessageMetadata | undefined, audience: readonly AgentName[]): MessageMetadata {
91
+ const { [OWN_ROW_KEY]: _replaced, ...rest } = (metadata ?? {}) as MessageMetadata;
92
+ return { ...rest, [AUDIENCE_KEY]: [...audience] } as MessageMetadata;
93
+ }
94
+
95
+ /**
96
+ * `metadata` stamped as `owner`'s own row, meant for `owner` alone: a stamp
97
+ * it carried is replaced. A new object; `metadata` is unchanged.
98
+ */
99
+ export function asOwnRow(metadata: MessageMetadata | undefined, owner: AgentName): MessageMetadata {
100
+ return { ...(metadata ?? {}), [AUDIENCE_KEY]: [owner], [OWN_ROW_KEY]: owner } as MessageMetadata;
101
+ }
102
+
103
+ /**
104
+ * The audience stamp a row's metadata carries, or undefined when it carries
105
+ * none (a row stored before the stamp existed) or carries one that isn't a
106
+ * list of names.
107
+ */
108
+ export function audienceOf(metadata: unknown): AudienceStamp | undefined {
109
+ if (!metadata || typeof metadata !== 'object') return undefined;
110
+ const audience = (metadata as Record<string, unknown>)[AUDIENCE_KEY];
111
+ if (!Array.isArray(audience) || !audience.every((name) => typeof name === 'string')) return undefined;
112
+ const own = (metadata as Record<string, unknown>)[OWN_ROW_KEY];
113
+ return { audience: [...audience], ...(typeof own === 'string' ? { ownRowOf: own } : {}) };
114
+ }
115
+
116
+ /** Whether a row's metadata stamps it as `agent`'s own row. */
117
+ export function isOwnRowOf(metadata: unknown, agent: AgentName): boolean {
118
+ return audienceOf(metadata)?.ownRowOf === agent;
119
+ }
120
+
121
+ /**
122
+ * The admission record on a stored message: it was held from the residents
123
+ * by a policy epoch (`metadata.tuneOut = { epochId }`). ONE predicate for
124
+ * "held from the resident", read by the compiled-view filter, the mid-turn
125
+ * injection and the presentation index (#291) alike (#232 H1), so the window
126
+ * and the wire agree. Such a row is stamped for nobody (an empty audience),
127
+ * whatever store it lands in, unless its write names an agent whose own
128
+ * view keeps held traffic, such as the persistent subconscious, which it is
129
+ * for.
130
+ */
131
+ export const heldFromResidents = (metadata: Record<string, unknown> | undefined): boolean =>
132
+ !!(metadata as { tuneOut?: unknown } | undefined)?.tuneOut;
133
+
134
+ /**
135
+ * The suppression record on a stored message: a suppress rule took it as it
136
+ * arrived (`metadata.suppressed = { rule, eventType }`, stamped by addMessage
137
+ * from the gate's suppressionFor). ONE predicate for "never rendered", read
138
+ * by every view filter the framework sets (the residents', the
139
+ * subconscious's, and through them every derived view's, context-options.ts)
140
+ * and so by agentSees too: the compile, a tool boundary's injection and the
141
+ * arrival and unread checks agree. The presentation index (#291) presents
142
+ * such a row to nobody (bearingOf, observedRowFor), the reader of held
143
+ * traffic included, and addMessage stamps it for nobody (an empty
144
+ * audience). The stamp never changes, so the view stays deterministic per
145
+ * row, as context-manager's viewFilter requires.
146
+ */
147
+ export const suppressedRow = (metadata: Record<string, unknown> | undefined): boolean =>
148
+ suppressionRecordOf(metadata) !== undefined;
149
+
150
+ /**
151
+ * Whether a row of a slot an agent observes is for that agent (#291,
152
+ * decision 1): what an observe edge shows of the slot it reads. `observer`
153
+ * is the agent the edge is on, and `owners` are the agents whose own slot it
154
+ * reads (the registry's `slotOwners`).
155
+ *
156
+ * - Another agent's stored context injection: no. It is that agent's
157
+ * context, not conversation, as every view of the framework's leaves out.
158
+ * - A row stored under the observer's own name: no. It is the observer's
159
+ * delivery into the slot (the subconscious's summary or note into the
160
+ * residents' slot), which its own round already holds.
161
+ * - A row a suppress rule hid (suppressedRow): no. It renders for no
162
+ * reader, the reader of held traffic included.
163
+ * - A row a tune-out holds: only through an edge that shows held traffic
164
+ * (`includeHeld`, the subconscious's).
165
+ * - A row with no audience stamp (stored before the stamp, or by a host's
166
+ * writer that doesn't stamp): yes, as the merged view showed it.
167
+ * - An owner's own row (its rounds, results, clock rows, notices about its
168
+ * own turn), and a row whose audience names an owner (channel traffic,
169
+ * mail, module rows): yes.
170
+ * - A row whose audience names the observer itself, and that is nobody's
171
+ * own row (an operator's message or a module's write addressed to the
172
+ * subconscious alone, which lands in the residents' slot it watches):
173
+ * yes. The edge is how the observer reads that slot, and the merged view
174
+ * showed the row before the presentation index, so no window would hold
175
+ * it otherwise (#291).
176
+ * - Anything else, such as the own row of an agent that doesn't own the
177
+ * slot (the observer's own included), a row stamped only for an agent
178
+ * that neither owns nor observes the slot, or one for a derived agent on
179
+ * another branch: no.
180
+ */
181
+ export function observedRowFor(
182
+ edge: Pick<ObserveEdge, 'includeHeld'>,
183
+ row: { participant: string; metadata?: unknown },
184
+ context: { observer: AgentName; owners: readonly AgentName[] },
185
+ ): boolean {
186
+ if (isAnotherAgentsInjection(row.metadata, context.observer)) return false;
187
+ if (row.participant === context.observer) return false;
188
+ if (suppressedRow(row.metadata as Record<string, unknown> | undefined)) return false;
189
+ if (heldFromResidents(row.metadata as Record<string, unknown> | undefined)) return edge.includeHeld === true;
190
+ const stamp = audienceOf(row.metadata);
191
+ if (!stamp) return true;
192
+ if (stamp.ownRowOf !== undefined) return context.owners.includes(stamp.ownRowOf);
193
+ return stamp.audience.some((name) => name === context.observer || context.owners.includes(name));
194
+ }
195
+
196
+ /** What `storeOwnRow` writes through: an agent, or anything that names one and holds its context. */
197
+ export interface OwnRowWriter {
198
+ readonly name: AgentName;
199
+ /** With `getMessage`, a write whose outcome the store can't tell presents
200
+ * the rows it may have stored that the store reads as held
201
+ * (presentLandedThenRethrow); without it, only those it knows landed. */
202
+ getContextManager(): Pick<ContextManager, 'addMessage'> & Partial<Pick<ContextManager, 'getMessage'>>;
203
+ /**
204
+ * Told the ids of the agent's own rows once they are stored (the
205
+ * presentation index's own-row sink, #291), each once its write has
206
+ * returned. A writer without it is told nothing.
207
+ */
208
+ ownRowsStored?(ids: readonly MessageId[]): void;
209
+ }
210
+
211
+ /**
212
+ * Store a row in `agent`'s own context, stamped as its own row (asOwnRow).
213
+ * Every writer that stores into an agent's context directly, past the
214
+ * framework's `addMessage`, stores the agent's own rows through this, but
215
+ * the tool-result guard, which stamps the results it writes itself
216
+ * (asOwnRow, ToolResultGuard.storeResults) and tells its own-row sink of
217
+ * them, so none of them goes unstamped.
218
+ *
219
+ * With `present` (the default), the writer is told of the row once the add
220
+ * has returned (`ownRowsStored`), never from inside it, where a sharded body
221
+ * may still be storing its shards: under the presentation index (#291) that
222
+ * presents the row in the agent's window at once. That is right for a row
223
+ * the agent's stream carries (its round, its results, a notice injected at
224
+ * its boundary), one written before its next compile or after its stream's
225
+ * last request, and one written while the agent has no turn alive. A row
226
+ * written at another moment, where a turn the row isn't part of may be
227
+ * alive, passes `present: false`: it stays stored and out of the window,
228
+ * and the index's own-row sweep at the agent's next presentation point
229
+ * presents it there.
230
+ */
231
+ export function storeOwnRow(
232
+ agent: OwnRowWriter,
233
+ participant: string,
234
+ content: ContentBlock[],
235
+ metadata?: MessageMetadata,
236
+ options?: AddMessageOptions,
237
+ present = true,
238
+ ): MessageId {
239
+ const cm = agent.getContextManager();
240
+ let id: MessageId;
241
+ try {
242
+ id = cm.addMessage(participant, content, asOwnRow(metadata, agent.name), undefined, options);
243
+ } catch (error) {
244
+ presentLandedThenRethrow(
245
+ error,
246
+ agent.name,
247
+ present && agent.ownRowsStored ? (ids) => agent.ownRowsStored!(ids) : undefined,
248
+ cm.getMessage ? (row) => cm.getMessage!(row) !== null : undefined,
249
+ );
250
+ }
251
+ if (present) agent.ownRowsStored?.([id]);
252
+ return id;
253
+ }
254
+
255
+ /**
256
+ * The catch of a write of an agent's own row whose add threw (storeOwnRow,
257
+ * and the tool-result guard's two writes of the results row): the rows of
258
+ * the write the store holds are told to `told`, the own-row sink, as the
259
+ * write's id would have been, then the add's error is thrown on. The write
260
+ * is answered by that error, as before the presentation index: what its
261
+ * caller does on it is the caller's. A row of it that is stored is out of
262
+ * the window until presented, like any stored row, and presented here so
263
+ * that an own row written after it can't enter the window ahead of it.
264
+ *
265
+ * - WriteLandedError: its `landed` rows, in store order.
266
+ * - WriteOutcomeUnknownError: its `landed` rows, then those its `unknown`
267
+ * names (the store holds all of these or none) that `holds` reads as
268
+ * stored, as addMessage presents such a write's rows (presentedThoughUnknown
269
+ * in framework.ts, whose afterStore reads the rows too). Where that read
270
+ * throws, every row `unknown` names, as presentedThoughUnknown gives them:
271
+ * the sink keeps a row it can't present and presents it ahead of the next
272
+ * own row, dropping one the store doesn't hold (presentOwn), so a landed
273
+ * row isn't put behind an own row written after it. Without `holds`, its
274
+ * `landed` rows only.
275
+ * - Any other error: none of the write is stored, so nothing is told.
276
+ *
277
+ * Without `told` (a writer that holds its row back, storeOwnRow's
278
+ * `present: false`, or one with no sink) nothing is told. A sink that
279
+ * throws is logged; the add's error is the one thrown.
280
+ */
281
+ export function presentLandedThenRethrow(
282
+ error: unknown,
283
+ agentName: string,
284
+ told: ((ids: readonly MessageId[]) => void) | undefined,
285
+ holds?: (id: MessageId) => boolean,
286
+ ): never {
287
+ const stored = told ? storedOfThrownWrite(error, holds) : [];
288
+ if (stored.length > 0) {
289
+ try {
290
+ told!(stored);
291
+ } catch (presentError) {
292
+ console.error(
293
+ `[store] agent=${agentName}: a write's add threw with ${stored.length === 1 ? `its row ${stored[0]}` : `rows ${stored.join(', ')}`} ` +
294
+ 'stored, and telling its own-row sink of them threw too:',
295
+ presentError,
296
+ );
297
+ }
298
+ }
299
+ throw error;
300
+ }
301
+
302
+ /** The rows of a write whose add threw that the store holds, by its error (presentLandedThenRethrow). */
303
+ function storedOfThrownWrite(error: unknown, holds: ((id: MessageId) => boolean) | undefined): MessageId[] {
304
+ if (error instanceof WriteLandedError) return [...error.landed];
305
+ if (!(error instanceof WriteOutcomeUnknownError)) return [];
306
+ let unknown: MessageId[] = [];
307
+ if (holds && error.unknown.length > 0) {
308
+ try {
309
+ unknown = error.unknown.filter((id) => holds(id));
310
+ } catch {
311
+ unknown = [...error.unknown];
312
+ }
313
+ }
314
+ return [...error.landed, ...unknown];
315
+ }
@@ -1,5 +1,5 @@
1
- export { AgentRegistry, AgentSpecError } from './registry.js';
2
- export type { AgentRegistryOptions, RestoreOutcome, ReconcileOutcome } from './registry.js';
1
+ export { AgentRegistry, AgentSpecError, RESIDENTS_SLOT, sameSlot, observedSlot } from './registry.js';
2
+ export type { AgentRegistryOptions, RegisterOptions, RestoreOutcome, ReconcileOutcome } from './registry.js';
3
3
  export {
4
4
  residentSpec,
5
5
  subconsciousSpec,
@@ -17,6 +17,7 @@ export type {
17
17
  ActivationBounds,
18
18
  ParentEndPolicy,
19
19
  ResultRoute,
20
+ MessageSlotRef,
20
21
  ObserveEdge,
21
22
  AgentRelationships,
22
23
  ContextInheritance,
@@ -30,3 +31,14 @@ export type {
30
31
  EndOutcome,
31
32
  PolicyEpoch,
32
33
  } from './types.js';
34
+ export {
35
+ AUDIENCE_KEY,
36
+ OWN_ROW_KEY,
37
+ withAudience,
38
+ asOwnRow,
39
+ audienceOf,
40
+ isOwnRowOf,
41
+ observedRowFor,
42
+ storeOwnRow,
43
+ } from './audience.js';
44
+ export type { AudienceStamp, OwnRowWriter } from './audience.js';
@@ -11,10 +11,14 @@ import type { AgentName, AgentSpec, ContextInheritance, ResultRoute } from './ty
11
11
  export const DEFAULT_TASK_IDLE_TIMEOUT_MS = 15 * 60_000;
12
12
 
13
13
  /**
14
- * A resident: persistent, declared in configuration, reads the residents'
15
- * shared message slot. The first one declared owns default delivery.
14
+ * A resident: persistent, declared in configuration, its own rows stored in
15
+ * the residents' shared message slot. The first one declared owns default
16
+ * delivery. `observes` names the other agents whose own rows its window
17
+ * takes (AgentConfig.observes): an observe edge on each, `{ agent }`, which
18
+ * reads that agent's own slot, the residents' slot it shares, and shows it
19
+ * that agent's own rows there (bearingOf). None by default.
16
20
  */
17
- export function residentSpec(name: AgentName, options: { primary: boolean }): AgentSpec {
21
+ export function residentSpec(name: AgentName, options: { primary: boolean; observes?: readonly AgentName[] }): AgentSpec {
18
22
  return {
19
23
  name,
20
24
  kind: 'resident',
@@ -25,6 +29,11 @@ export function residentSpec(name: AgentName, options: { primary: boolean }): Ag
25
29
  ownsProviderScheduling: true,
26
30
  },
27
31
  lifetime: { kind: 'persistent' },
32
+ ...(options.observes?.length ? { observes: options.observes.map((agent) => ({ agent })) } : {}),
33
+ ownSlot: { namespace: null },
34
+ // Deprecated: kept for one release beside ownSlot, which the registry
35
+ // reads first, for a host that reads the flag off a preset's spec or
36
+ // builds its specs from one.
28
37
  readsSharedSlot: true,
29
38
  namespace: `agents/${name}`,
30
39
  };
@@ -33,9 +42,9 @@ export function residentSpec(name: AgentName, options: { primary: boolean }): Ag
33
42
  /**
34
43
  * The subconscious: a persistent attention tenant of the resident it
35
44
  * serves. It persists across policy epochs (cancelling a tune-out ends a
36
- * channel's cadence, not this agent), observes the residents' slot with
37
- * held traffic included, is never a broadcast recipient, and ends with its
38
- * resident.
45
+ * channel's cadence, not this agent), stores its own rows in a slot of its
46
+ * own and observes the residents' slot with held traffic included, is never
47
+ * a broadcast recipient, and ends with its resident.
39
48
  */
40
49
  export function subconsciousSpec(name: AgentName, serves: AgentName): AgentSpec {
41
50
  return {
@@ -52,6 +61,9 @@ export function subconsciousSpec(name: AgentName, serves: AgentName): AgentSpec
52
61
  onParentEnd: 'end',
53
62
  observes: [{ includeHeld: true }],
54
63
  resultTo: { to: serves, as: 'message' },
64
+ ownSlot: { namespace: `subconscious/${serves}` },
65
+ // Deprecated, as on residentSpec: it reads the residents' slot through
66
+ // its observe edge; its ownSlot places its rows.
55
67
  readsSharedSlot: true,
56
68
  namespace: `subconscious/${serves}`,
57
69
  };