@animalabs/agent-framework 0.21.1 → 0.22.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 (271) hide show
  1. package/README.md +43 -0
  2. package/dist/src/agent.d.ts +17 -4
  3. package/dist/src/agent.d.ts.map +1 -1
  4. package/dist/src/agent.js +102 -42
  5. package/dist/src/agent.js.map +1 -1
  6. package/dist/src/code-execution/py-runner.d.ts +22 -1
  7. package/dist/src/code-execution/py-runner.d.ts.map +1 -1
  8. package/dist/src/code-execution/py-runner.js +64 -9
  9. package/dist/src/code-execution/py-runner.js.map +1 -1
  10. package/dist/src/code-execution/runtime-py.d.ts +4 -0
  11. package/dist/src/code-execution/runtime-py.d.ts.map +1 -1
  12. package/dist/src/code-execution/runtime-py.js +215 -38
  13. package/dist/src/code-execution/runtime-py.js.map +1 -1
  14. package/dist/src/framework.d.ts +250 -39
  15. package/dist/src/framework.d.ts.map +1 -1
  16. package/dist/src/framework.js +1602 -371
  17. package/dist/src/framework.js.map +1 -1
  18. package/dist/src/gate/event-gate.d.ts +21 -0
  19. package/dist/src/gate/event-gate.d.ts.map +1 -1
  20. package/dist/src/gate/event-gate.js +40 -7
  21. package/dist/src/gate/event-gate.js.map +1 -1
  22. package/dist/src/index.d.ts +5 -0
  23. package/dist/src/index.d.ts.map +1 -1
  24. package/dist/src/index.js +4 -0
  25. package/dist/src/index.js.map +1 -1
  26. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  27. package/dist/src/mcpl/channel-registry.js +26 -3
  28. package/dist/src/mcpl/channel-registry.js.map +1 -1
  29. package/dist/src/mcpl/conversation-router.d.ts +28 -1
  30. package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
  31. package/dist/src/mcpl/conversation-router.js +17 -0
  32. package/dist/src/mcpl/conversation-router.js.map +1 -1
  33. package/dist/src/mcpl/feature-set-manager.d.ts +11 -0
  34. package/dist/src/mcpl/feature-set-manager.d.ts.map +1 -1
  35. package/dist/src/mcpl/feature-set-manager.js +31 -7
  36. package/dist/src/mcpl/feature-set-manager.js.map +1 -1
  37. package/dist/src/mcpl/index.d.ts +2 -0
  38. package/dist/src/mcpl/index.d.ts.map +1 -1
  39. package/dist/src/mcpl/index.js +1 -0
  40. package/dist/src/mcpl/index.js.map +1 -1
  41. package/dist/src/mcpl/prose-stream-router.d.ts +27 -6
  42. package/dist/src/mcpl/prose-stream-router.d.ts.map +1 -1
  43. package/dist/src/mcpl/prose-stream-router.js +85 -22
  44. package/dist/src/mcpl/prose-stream-router.js.map +1 -1
  45. package/dist/src/mcpl/push-coalescer.d.ts +32 -4
  46. package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
  47. package/dist/src/mcpl/push-coalescer.js +46 -13
  48. package/dist/src/mcpl/push-coalescer.js.map +1 -1
  49. package/dist/src/mcpl/push-handler.d.ts +5 -3
  50. package/dist/src/mcpl/push-handler.d.ts.map +1 -1
  51. package/dist/src/mcpl/push-handler.js +41 -11
  52. package/dist/src/mcpl/push-handler.js.map +1 -1
  53. package/dist/src/mcpl/server-connection.d.ts +4 -0
  54. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  55. package/dist/src/mcpl/server-connection.js +45 -8
  56. package/dist/src/mcpl/server-connection.js.map +1 -1
  57. package/dist/src/mcpl/server-registry.d.ts +5 -2
  58. package/dist/src/mcpl/server-registry.d.ts.map +1 -1
  59. package/dist/src/mcpl/server-registry.js +25 -7
  60. package/dist/src/mcpl/server-registry.js.map +1 -1
  61. package/dist/src/mcpl/tool-lifecycle.d.ts +52 -2
  62. package/dist/src/mcpl/tool-lifecycle.d.ts.map +1 -1
  63. package/dist/src/mcpl/tool-lifecycle.js +47 -1
  64. package/dist/src/mcpl/tool-lifecycle.js.map +1 -1
  65. package/dist/src/mcpl/tool-pattern-check.d.ts +51 -0
  66. package/dist/src/mcpl/tool-pattern-check.d.ts.map +1 -0
  67. package/dist/src/mcpl/tool-pattern-check.js +79 -0
  68. package/dist/src/mcpl/tool-pattern-check.js.map +1 -0
  69. package/dist/src/mcpl/types.d.ts +15 -5
  70. package/dist/src/mcpl/types.d.ts.map +1 -1
  71. package/dist/src/mcpl/types.js.map +1 -1
  72. package/dist/src/mcpl/visible-content.d.ts +39 -0
  73. package/dist/src/mcpl/visible-content.d.ts.map +1 -0
  74. package/dist/src/mcpl/visible-content.js +54 -0
  75. package/dist/src/mcpl/visible-content.js.map +1 -0
  76. package/dist/src/module-registry.d.ts +81 -3
  77. package/dist/src/module-registry.d.ts.map +1 -1
  78. package/dist/src/module-registry.js +257 -16
  79. package/dist/src/module-registry.js.map +1 -1
  80. package/dist/src/modules/workspace/branch-intent.d.ts +72 -0
  81. package/dist/src/modules/workspace/branch-intent.d.ts.map +1 -0
  82. package/dist/src/modules/workspace/branch-intent.js +83 -0
  83. package/dist/src/modules/workspace/branch-intent.js.map +1 -0
  84. package/dist/src/modules/workspace/disk-agreement.d.ts +167 -0
  85. package/dist/src/modules/workspace/disk-agreement.d.ts.map +1 -0
  86. package/dist/src/modules/workspace/disk-agreement.js +259 -0
  87. package/dist/src/modules/workspace/disk-agreement.js.map +1 -0
  88. package/dist/src/modules/workspace/effects.d.ts +84 -0
  89. package/dist/src/modules/workspace/effects.d.ts.map +1 -0
  90. package/dist/src/modules/workspace/effects.js +368 -0
  91. package/dist/src/modules/workspace/effects.js.map +1 -0
  92. package/dist/src/modules/workspace/index.d.ts +155 -17
  93. package/dist/src/modules/workspace/index.d.ts.map +1 -1
  94. package/dist/src/modules/workspace/index.js +1059 -424
  95. package/dist/src/modules/workspace/index.js.map +1 -1
  96. package/dist/src/modules/workspace/observe.d.ts +278 -0
  97. package/dist/src/modules/workspace/observe.d.ts.map +1 -0
  98. package/dist/src/modules/workspace/observe.js +805 -0
  99. package/dist/src/modules/workspace/observe.js.map +1 -0
  100. package/dist/src/modules/workspace/reconcile.d.ts +224 -0
  101. package/dist/src/modules/workspace/reconcile.d.ts.map +1 -0
  102. package/dist/src/modules/workspace/reconcile.js +901 -0
  103. package/dist/src/modules/workspace/reconcile.js.map +1 -0
  104. package/dist/src/modules/workspace/sync.d.ts +6 -59
  105. package/dist/src/modules/workspace/sync.d.ts.map +1 -1
  106. package/dist/src/modules/workspace/sync.js +6 -257
  107. package/dist/src/modules/workspace/sync.js.map +1 -1
  108. package/dist/src/modules/workspace/types.d.ts +77 -10
  109. package/dist/src/modules/workspace/types.d.ts.map +1 -1
  110. package/dist/src/modules/workspace/types.js.map +1 -1
  111. package/dist/src/record-journal.d.ts +127 -0
  112. package/dist/src/record-journal.d.ts.map +1 -0
  113. package/dist/src/record-journal.js +202 -0
  114. package/dist/src/record-journal.js.map +1 -0
  115. package/dist/src/shutdown.d.ts +13 -0
  116. package/dist/src/shutdown.d.ts.map +1 -0
  117. package/dist/src/shutdown.js +46 -0
  118. package/dist/src/shutdown.js.map +1 -0
  119. package/dist/src/silent-heartbeat.d.ts +74 -0
  120. package/dist/src/silent-heartbeat.d.ts.map +1 -0
  121. package/dist/src/silent-heartbeat.js +77 -0
  122. package/dist/src/silent-heartbeat.js.map +1 -0
  123. package/dist/src/tool-presentation.d.ts +50 -0
  124. package/dist/src/tool-presentation.d.ts.map +1 -0
  125. package/dist/src/tool-presentation.js +235 -0
  126. package/dist/src/tool-presentation.js.map +1 -0
  127. package/dist/src/tool-result-guard.d.ts +125 -6
  128. package/dist/src/tool-result-guard.d.ts.map +1 -1
  129. package/dist/src/tool-result-guard.js +310 -35
  130. package/dist/src/tool-result-guard.js.map +1 -1
  131. package/dist/src/tool-result-history.d.ts +13 -0
  132. package/dist/src/tool-result-history.d.ts.map +1 -1
  133. package/dist/src/tool-result-history.js +21 -1
  134. package/dist/src/tool-result-history.js.map +1 -1
  135. package/dist/src/types/agent.d.ts +18 -2
  136. package/dist/src/types/agent.d.ts.map +1 -1
  137. package/dist/src/types/events.d.ts +4 -0
  138. package/dist/src/types/events.d.ts.map +1 -1
  139. package/dist/src/types/framework.d.ts +12 -0
  140. package/dist/src/types/framework.d.ts.map +1 -1
  141. package/dist/src/types/module.d.ts +14 -0
  142. package/dist/src/types/module.d.ts.map +1 -1
  143. package/dist/src/types/trace.d.ts +73 -0
  144. package/dist/src/types/trace.d.ts.map +1 -1
  145. package/dist/test/batched-wake-locus.test.d.ts +2 -0
  146. package/dist/test/batched-wake-locus.test.d.ts.map +1 -0
  147. package/dist/test/batched-wake-locus.test.js +166 -0
  148. package/dist/test/batched-wake-locus.test.js.map +1 -0
  149. package/dist/test/code-execution.test.js +386 -2
  150. package/dist/test/code-execution.test.js.map +1 -1
  151. package/dist/test/component-defaults.test.d.ts +2 -0
  152. package/dist/test/component-defaults.test.d.ts.map +1 -0
  153. package/dist/test/component-defaults.test.js +54 -0
  154. package/dist/test/component-defaults.test.js.map +1 -0
  155. package/dist/test/conversation-routing.test.js +57 -1
  156. package/dist/test/conversation-routing.test.js.map +1 -1
  157. package/dist/test/ephemeral-settle.test.js +3 -0
  158. package/dist/test/ephemeral-settle.test.js.map +1 -1
  159. package/dist/test/event-gate.test.js +118 -0
  160. package/dist/test/event-gate.test.js.map +1 -1
  161. package/dist/test/explicit-prose-routing.test.js +977 -0
  162. package/dist/test/explicit-prose-routing.test.js.map +1 -1
  163. package/dist/test/helpers/pytc-stop-rows.d.ts +11 -0
  164. package/dist/test/helpers/pytc-stop-rows.d.ts.map +1 -0
  165. package/dist/test/helpers/pytc-stop-rows.js +143 -0
  166. package/dist/test/helpers/pytc-stop-rows.js.map +1 -0
  167. package/dist/test/mcpl-coalescing.test.js +19 -6
  168. package/dist/test/mcpl-coalescing.test.js.map +1 -1
  169. package/dist/test/mcpl-config-diagnostics.test.d.ts +2 -0
  170. package/dist/test/mcpl-config-diagnostics.test.d.ts.map +1 -0
  171. package/dist/test/mcpl-config-diagnostics.test.js +449 -0
  172. package/dist/test/mcpl-config-diagnostics.test.js.map +1 -0
  173. package/dist/test/mcpl-empty-content.test.d.ts +2 -0
  174. package/dist/test/mcpl-empty-content.test.d.ts.map +1 -0
  175. package/dist/test/mcpl-empty-content.test.js +368 -0
  176. package/dist/test/mcpl-empty-content.test.js.map +1 -0
  177. package/dist/test/mcpl-tool-lifecycle-emitter.test.js +106 -1
  178. package/dist/test/mcpl-tool-lifecycle-emitter.test.js.map +1 -1
  179. package/dist/test/mcpl-tool-lifecycle-script.test.d.ts +2 -0
  180. package/dist/test/mcpl-tool-lifecycle-script.test.d.ts.map +1 -0
  181. package/dist/test/mcpl-tool-lifecycle-script.test.js +288 -0
  182. package/dist/test/mcpl-tool-lifecycle-script.test.js.map +1 -0
  183. package/dist/test/prose-stream-router.test.js +41 -0
  184. package/dist/test/prose-stream-router.test.js.map +1 -1
  185. package/dist/test/record-journal.test.d.ts +6 -0
  186. package/dist/test/record-journal.test.d.ts.map +1 -0
  187. package/dist/test/record-journal.test.js +269 -0
  188. package/dist/test/record-journal.test.js.map +1 -0
  189. package/dist/test/save-recent-image.test.js +102 -0
  190. package/dist/test/save-recent-image.test.js.map +1 -1
  191. package/dist/test/shutdown-errors.test.d.ts +2 -0
  192. package/dist/test/shutdown-errors.test.d.ts.map +1 -0
  193. package/dist/test/shutdown-errors.test.js +662 -0
  194. package/dist/test/shutdown-errors.test.js.map +1 -0
  195. package/dist/test/silent-heartbeat-rows.test.d.ts +2 -0
  196. package/dist/test/silent-heartbeat-rows.test.d.ts.map +1 -0
  197. package/dist/test/silent-heartbeat-rows.test.js +451 -0
  198. package/dist/test/silent-heartbeat-rows.test.js.map +1 -0
  199. package/dist/test/silent-heartbeat.test.js +49 -38
  200. package/dist/test/silent-heartbeat.test.js.map +1 -1
  201. package/dist/test/stop-with-work-in-flight.test.d.ts +23 -0
  202. package/dist/test/stop-with-work-in-flight.test.d.ts.map +1 -0
  203. package/dist/test/stop-with-work-in-flight.test.js +672 -0
  204. package/dist/test/stop-with-work-in-flight.test.js.map +1 -0
  205. package/dist/test/tool-batch-hook.test.d.ts +8 -0
  206. package/dist/test/tool-batch-hook.test.d.ts.map +1 -0
  207. package/dist/test/tool-batch-hook.test.js +275 -0
  208. package/dist/test/tool-batch-hook.test.js.map +1 -0
  209. package/dist/test/tool-presentation.test.d.ts +2 -0
  210. package/dist/test/tool-presentation.test.d.ts.map +1 -0
  211. package/dist/test/tool-presentation.test.js +268 -0
  212. package/dist/test/tool-presentation.test.js.map +1 -0
  213. package/dist/test/tool-result-guard.test.js +2330 -18
  214. package/dist/test/tool-result-guard.test.js.map +1 -1
  215. package/dist/test/tool-result-history.test.js +2 -1
  216. package/dist/test/tool-result-history.test.js.map +1 -1
  217. package/dist/test/turn-trigger-provenance.test.js +35 -0
  218. package/dist/test/turn-trigger-provenance.test.js.map +1 -1
  219. package/dist/test/unconvertible-tool-result.test.d.ts +2 -0
  220. package/dist/test/unconvertible-tool-result.test.d.ts.map +1 -0
  221. package/dist/test/unconvertible-tool-result.test.js +358 -0
  222. package/dist/test/unconvertible-tool-result.test.js.map +1 -0
  223. package/dist/test/workspace-materialize-freshness.test.d.ts +16 -0
  224. package/dist/test/workspace-materialize-freshness.test.d.ts.map +1 -0
  225. package/dist/test/workspace-materialize-freshness.test.js +252 -0
  226. package/dist/test/workspace-materialize-freshness.test.js.map +1 -0
  227. package/dist/test/workspace-three-way.test.d.ts +12 -0
  228. package/dist/test/workspace-three-way.test.d.ts.map +1 -0
  229. package/dist/test/workspace-three-way.test.js +3079 -0
  230. package/dist/test/workspace-three-way.test.js.map +1 -0
  231. package/dist/tsconfig.tsbuildinfo +1 -1
  232. package/package.json +2 -2
  233. package/src/agent.ts +120 -44
  234. package/src/code-execution/py-runner.ts +66 -9
  235. package/src/code-execution/runtime-py.ts +215 -38
  236. package/src/framework.ts +1661 -363
  237. package/src/gate/event-gate.ts +59 -7
  238. package/src/index.ts +11 -0
  239. package/src/mcpl/channel-registry.ts +24 -3
  240. package/src/mcpl/conversation-router.ts +34 -1
  241. package/src/mcpl/feature-set-manager.ts +36 -11
  242. package/src/mcpl/index.ts +2 -0
  243. package/src/mcpl/prose-stream-router.ts +79 -23
  244. package/src/mcpl/push-coalescer.ts +62 -13
  245. package/src/mcpl/push-handler.ts +41 -12
  246. package/src/mcpl/server-connection.ts +41 -10
  247. package/src/mcpl/server-registry.ts +24 -7
  248. package/src/mcpl/tool-lifecycle.ts +77 -2
  249. package/src/mcpl/tool-pattern-check.ts +106 -0
  250. package/src/mcpl/types.ts +15 -5
  251. package/src/mcpl/visible-content.ts +57 -0
  252. package/src/module-registry.ts +252 -16
  253. package/src/modules/workspace/branch-intent.ts +123 -0
  254. package/src/modules/workspace/disk-agreement.ts +325 -0
  255. package/src/modules/workspace/effects.ts +362 -0
  256. package/src/modules/workspace/index.ts +1058 -446
  257. package/src/modules/workspace/observe.ts +839 -0
  258. package/src/modules/workspace/reconcile.ts +1136 -0
  259. package/src/modules/workspace/sync.ts +6 -336
  260. package/src/modules/workspace/types.ts +75 -10
  261. package/src/record-journal.ts +246 -0
  262. package/src/shutdown.ts +49 -0
  263. package/src/silent-heartbeat.ts +141 -0
  264. package/src/tool-presentation.ts +188 -0
  265. package/src/tool-result-guard.ts +335 -33
  266. package/src/tool-result-history.ts +25 -1
  267. package/src/types/agent.ts +18 -2
  268. package/src/types/events.ts +4 -0
  269. package/src/types/framework.ts +12 -0
  270. package/src/types/module.ts +15 -0
  271. package/src/types/trace.ts +80 -0
@@ -0,0 +1,127 @@
1
+ /**
2
+ * An append-only journal of typed Chronicle records that does not follow
3
+ * branch switches.
4
+ *
5
+ * Chronicle's branch state — snapshot, tree and log states — rewinds with a
6
+ * historical rollback (createBranchAt + switchBranch). Typed records do not:
7
+ * `getRecordIdsByType` enumerates them from every branch, in global order, and
8
+ * they survive `deleteBranch`, reopen and a hard kill. A journal is for state
9
+ * that must not rewind with the conversation but should live and die with its
10
+ * store: what a workspace's disk last agreed with, what reached a recipient,
11
+ * what became of a suppressed draft.
12
+ *
13
+ * Durability. A record append reaches the OS before `append` returns, so it
14
+ * survives a killed process, while branch-state commits are buffered until
15
+ * `store.sync()`: a record appended after a state commit can outlive that
16
+ * commit. Two barriers, each named where it's needed and neither implied by
17
+ * the `{id, sequence}` an append returns:
18
+ * - `afterCommittedState: true` syncs BEFORE appending — for an entry that
19
+ * asserts state committed just before it, so the entry can never survive
20
+ * without that state;
21
+ * - `durable: true` syncs AFTER appending — for an entry that must be on
22
+ * stable storage before the caller acts on it (an attempt before its
23
+ * outbound request, an intent before its disk effect).
24
+ *
25
+ * Checkpoints bound the replay at open: `checkpoint(snapshot)` records the
26
+ * caller's reduced state together with the last entry it covers, and `load()`
27
+ * returns the latest checkpoint's snapshot plus only the entries appended
28
+ * after it. A journal that never checkpoints replays everything. What entries
29
+ * mean, how they reduce, and when to checkpoint stay with the caller.
30
+ *
31
+ * One writer per record type: a checkpoint covers every entry up to the last
32
+ * one THIS journal loaded or appended, so entries another instance appended
33
+ * to the same type in between would be covered without being reduced.
34
+ *
35
+ * An ambiguous write must be resolved before the journal writes again. Once
36
+ * `append` or `checkpoint` has handed a record to the store, a failure leaves
37
+ * a record the caller didn't reduce, or may leave one, and a later checkpoint
38
+ * would cover it without its ever entering the reducer. There are two kinds:
39
+ * - The store's append itself throws. Its record may still be in the store's
40
+ * log, outside this process's view of the store: the store writes a
41
+ * record to its log, syncs the log every so often, and only then indexes
42
+ * the record, which is what `getRecordIdsByType` reads. So a failed sync
43
+ * leaves a record that reopening the store will find and no `load()` on
44
+ * this store can. Nothing but a reopen rereads the log, so from then on
45
+ * every `append` and `checkpoint` throws, for the life of the journal. A
46
+ * journal on the store reopened, as at the next start, loads that record
47
+ * with its tail for the caller to reduce.
48
+ * - A failure after the append returned: a `durable` barrier, or reading
49
+ * the id it returned. The record is in this process's view, so every
50
+ * further `append` and `checkpoint` throws until `load()` has replayed the
51
+ * tail, that record included, for the caller to reduce.
52
+ * A failure BEFORE the record reaches the store (the `afterCommittedState`
53
+ * sync) wrote nothing and leaves the journal usable.
54
+ */
55
+ import type { JsStore } from '@animalabs/chronicle';
56
+ /** Where an appended record landed. Not a durability receipt — see the barriers. */
57
+ export interface JournalRecordRef {
58
+ /** Chronicle record id: global, increasing in append order. */
59
+ id: string;
60
+ /** The record's sequence on the branch current when it was appended. */
61
+ sequence: number;
62
+ }
63
+ export interface JournalEntry<Entry> extends JournalRecordRef {
64
+ entry: Entry;
65
+ }
66
+ export interface JournalLoad<Entry, Snapshot> {
67
+ /** The latest checkpoint's snapshot, or null when none was written. */
68
+ snapshot: Snapshot | null;
69
+ /** Every entry appended after that checkpoint, in append order. */
70
+ entries: Array<JournalEntry<Entry>>;
71
+ }
72
+ export interface AppendOptions {
73
+ /** `store.sync()` before appending: the entry asserts state committed just before it. */
74
+ afterCommittedState?: boolean;
75
+ /** `store.sync()` after appending: the entry must be on stable storage before the caller acts. */
76
+ durable?: boolean;
77
+ }
78
+ export declare class RecordJournal<Entry, Snapshot = never> {
79
+ private readonly store;
80
+ /** Record type of the entries. */
81
+ readonly type: string;
82
+ /** Record type of the checkpoints. */
83
+ readonly checkpointType: string;
84
+ /** Highest entry id this journal has loaded or appended (0 when none). */
85
+ private lastEntryId;
86
+ private sinceCheckpoint;
87
+ /** Set by a failure after the store's append returned; cleared by load(). */
88
+ private unreconciled;
89
+ /** Set when the store's append itself threw: its record may be in the log
90
+ * but not in this process's view of the store. Never cleared. */
91
+ private unreadable;
92
+ constructor(store: JsStore, opts: {
93
+ type: string;
94
+ checkpointType?: string;
95
+ });
96
+ /** Entries appended since the last checkpoint (or since load, when there was none). */
97
+ get entriesSinceCheckpoint(): number;
98
+ /**
99
+ * The latest checkpoint and every entry after it, whichever branch each was
100
+ * appended on. The caller rebuilds its reduced state from exactly these —
101
+ * at open, and to reconcile after an ambiguous write. Also positions the
102
+ * journal, so later checkpoints cover what was loaded.
103
+ */
104
+ load(): JournalLoad<Entry, Snapshot>;
105
+ /** True after a failure that load() can resolve, until it has replayed the
106
+ * tail. An append that threw inside the store is never resolved here: the
107
+ * journal stays unwritable until a journal on the reopened store loads it. */
108
+ get needsReconcile(): boolean;
109
+ /** True once an append threw inside the store, for the rest of the
110
+ * journal's life. What `load()` returns may then lack that append's record,
111
+ * so a caller that decides from the journal's contents can't trust them
112
+ * until a journal on the reopened store has loaded it. */
113
+ get needsReopen(): boolean;
114
+ /** Append one entry. The returned ref says where it landed, not that it's durable. */
115
+ append(entry: Entry, opts?: AppendOptions): JournalRecordRef;
116
+ /**
117
+ * Record the caller's reduced state as covering every entry this journal has
118
+ * loaded or appended. Like an entry, it's durable only with `durable`.
119
+ */
120
+ checkpoint(snapshot: Snapshot, opts?: {
121
+ durable?: boolean;
122
+ }): JournalRecordRef;
123
+ /** Hand one record to the store; any failure from here on is ambiguous. */
124
+ private write;
125
+ private assertReconciled;
126
+ }
127
+ //# sourceMappingURL=record-journal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record-journal.d.ts","sourceRoot":"","sources":["../../src/record-journal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAEpD,oFAAoF;AACpF,MAAM,WAAW,gBAAgB;IAC/B,+DAA+D;IAC/D,EAAE,EAAE,MAAM,CAAC;IACX,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY,CAAC,KAAK,CAAE,SAAQ,gBAAgB;IAC3D,KAAK,EAAE,KAAK,CAAC;CACd;AAED,MAAM,WAAW,WAAW,CAAC,KAAK,EAAE,QAAQ;IAC1C,uEAAuE;IACvE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC1B,mEAAmE;IACnE,OAAO,EAAE,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,aAAa;IAC5B,yFAAyF;IACzF,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,kGAAkG;IAClG,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAsBD,qBAAa,aAAa,CAAC,KAAK,EAAE,QAAQ,GAAG,KAAK;IAChD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAU;IAChC,kCAAkC;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,0EAA0E;IAC1E,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,eAAe,CAAK;IAC5B,6EAA6E;IAC7E,OAAO,CAAC,YAAY,CAAsB;IAC1C;sEACkE;IAClE,OAAO,CAAC,UAAU,CAAsB;gBAE5B,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE;IAU3E,uFAAuF;IACvF,IAAI,sBAAsB,IAAI,MAAM,CAEnC;IAED;;;;;OAKG;IACH,IAAI,IAAI,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC;IAiCpC;;mFAE+E;IAC/E,IAAI,cAAc,IAAI,OAAO,CAE5B;IAED;;;+DAG2D;IAC3D,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,sFAAsF;IACtF,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,GAAE,aAAkB,GAAG,gBAAgB;IAUhE;;;OAGG;IACH,UAAU,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,GAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,gBAAgB;IAQlF,2EAA2E;IAC3E,OAAO,CAAC,KAAK;IAmBb,OAAO,CAAC,gBAAgB;CAezB"}
@@ -0,0 +1,202 @@
1
+ /**
2
+ * An append-only journal of typed Chronicle records that does not follow
3
+ * branch switches.
4
+ *
5
+ * Chronicle's branch state — snapshot, tree and log states — rewinds with a
6
+ * historical rollback (createBranchAt + switchBranch). Typed records do not:
7
+ * `getRecordIdsByType` enumerates them from every branch, in global order, and
8
+ * they survive `deleteBranch`, reopen and a hard kill. A journal is for state
9
+ * that must not rewind with the conversation but should live and die with its
10
+ * store: what a workspace's disk last agreed with, what reached a recipient,
11
+ * what became of a suppressed draft.
12
+ *
13
+ * Durability. A record append reaches the OS before `append` returns, so it
14
+ * survives a killed process, while branch-state commits are buffered until
15
+ * `store.sync()`: a record appended after a state commit can outlive that
16
+ * commit. Two barriers, each named where it's needed and neither implied by
17
+ * the `{id, sequence}` an append returns:
18
+ * - `afterCommittedState: true` syncs BEFORE appending — for an entry that
19
+ * asserts state committed just before it, so the entry can never survive
20
+ * without that state;
21
+ * - `durable: true` syncs AFTER appending — for an entry that must be on
22
+ * stable storage before the caller acts on it (an attempt before its
23
+ * outbound request, an intent before its disk effect).
24
+ *
25
+ * Checkpoints bound the replay at open: `checkpoint(snapshot)` records the
26
+ * caller's reduced state together with the last entry it covers, and `load()`
27
+ * returns the latest checkpoint's snapshot plus only the entries appended
28
+ * after it. A journal that never checkpoints replays everything. What entries
29
+ * mean, how they reduce, and when to checkpoint stay with the caller.
30
+ *
31
+ * One writer per record type: a checkpoint covers every entry up to the last
32
+ * one THIS journal loaded or appended, so entries another instance appended
33
+ * to the same type in between would be covered without being reduced.
34
+ *
35
+ * An ambiguous write must be resolved before the journal writes again. Once
36
+ * `append` or `checkpoint` has handed a record to the store, a failure leaves
37
+ * a record the caller didn't reduce, or may leave one, and a later checkpoint
38
+ * would cover it without its ever entering the reducer. There are two kinds:
39
+ * - The store's append itself throws. Its record may still be in the store's
40
+ * log, outside this process's view of the store: the store writes a
41
+ * record to its log, syncs the log every so often, and only then indexes
42
+ * the record, which is what `getRecordIdsByType` reads. So a failed sync
43
+ * leaves a record that reopening the store will find and no `load()` on
44
+ * this store can. Nothing but a reopen rereads the log, so from then on
45
+ * every `append` and `checkpoint` throws, for the life of the journal. A
46
+ * journal on the store reopened, as at the next start, loads that record
47
+ * with its tail for the caller to reduce.
48
+ * - A failure after the append returned: a `durable` barrier, or reading
49
+ * the id it returned. The record is in this process's view, so every
50
+ * further `append` and `checkpoint` throws until `load()` has replayed the
51
+ * tail, that record included, for the caller to reduce.
52
+ * A failure BEFORE the record reaches the store (the `afterCommittedState`
53
+ * sync) wrote nothing and leaves the journal usable.
54
+ */
55
+ const asError = (err) => (err instanceof Error ? err : new Error(String(err)));
56
+ /** Record ids are decimal strings from one store-wide counter. */
57
+ function idValue(id) {
58
+ if (!/^\d+$/.test(id))
59
+ throw new Error(`RecordJournal: unexpected chronicle record id "${id}"`);
60
+ return BigInt(id);
61
+ }
62
+ function decode(store, id) {
63
+ const record = store.getRecord(id);
64
+ if (!record)
65
+ throw new Error(`RecordJournal: record ${id} is listed but cannot be read`);
66
+ return { value: JSON.parse(Buffer.from(record.payload).toString('utf-8')), sequence: record.sequence };
67
+ }
68
+ export class RecordJournal {
69
+ store;
70
+ /** Record type of the entries. */
71
+ type;
72
+ /** Record type of the checkpoints. */
73
+ checkpointType;
74
+ /** Highest entry id this journal has loaded or appended (0 when none). */
75
+ lastEntryId = 0n;
76
+ sinceCheckpoint = 0;
77
+ /** Set by a failure after the store's append returned; cleared by load(). */
78
+ unreconciled = null;
79
+ /** Set when the store's append itself threw: its record may be in the log
80
+ * but not in this process's view of the store. Never cleared. */
81
+ unreadable = null;
82
+ constructor(store, opts) {
83
+ if (!opts.type)
84
+ throw new Error('RecordJournal: a record type is required');
85
+ this.store = store;
86
+ this.type = opts.type;
87
+ this.checkpointType = opts.checkpointType ?? `${opts.type}/checkpoint`;
88
+ if (this.checkpointType === this.type) {
89
+ throw new Error('RecordJournal: entries and checkpoints need distinct record types');
90
+ }
91
+ }
92
+ /** Entries appended since the last checkpoint (or since load, when there was none). */
93
+ get entriesSinceCheckpoint() {
94
+ return this.sinceCheckpoint;
95
+ }
96
+ /**
97
+ * The latest checkpoint and every entry after it, whichever branch each was
98
+ * appended on. The caller rebuilds its reduced state from exactly these —
99
+ * at open, and to reconcile after an ambiguous write. Also positions the
100
+ * journal, so later checkpoints cover what was loaded.
101
+ */
102
+ load() {
103
+ let snapshot = null;
104
+ let through = 0n;
105
+ let latestCheckpoint = null;
106
+ let latestCheckpointId = null;
107
+ for (const id of this.store.getRecordIdsByType(this.checkpointType)) {
108
+ const value = idValue(id);
109
+ if (latestCheckpoint === null || value > latestCheckpoint) {
110
+ latestCheckpoint = value;
111
+ latestCheckpointId = id;
112
+ }
113
+ }
114
+ if (latestCheckpointId !== null) {
115
+ const payload = decode(this.store, latestCheckpointId).value;
116
+ snapshot = payload.snapshot;
117
+ through = idValue(payload.through);
118
+ }
119
+ const tail = this.store.getRecordIdsByType(this.type)
120
+ .map((id) => ({ id, value: idValue(id) }))
121
+ .filter(({ value }) => value > through)
122
+ .sort((a, b) => (a.value < b.value ? -1 : a.value > b.value ? 1 : 0));
123
+ const entries = tail.map(({ id }) => {
124
+ const { value, sequence } = decode(this.store, id);
125
+ return { id, sequence, entry: value };
126
+ });
127
+ this.lastEntryId = tail.length > 0 ? tail[tail.length - 1].value : through;
128
+ this.sinceCheckpoint = entries.length;
129
+ this.unreconciled = null;
130
+ return { snapshot, entries };
131
+ }
132
+ /** True after a failure that load() can resolve, until it has replayed the
133
+ * tail. An append that threw inside the store is never resolved here: the
134
+ * journal stays unwritable until a journal on the reopened store loads it. */
135
+ get needsReconcile() {
136
+ return this.unreconciled !== null;
137
+ }
138
+ /** True once an append threw inside the store, for the rest of the
139
+ * journal's life. What `load()` returns may then lack that append's record,
140
+ * so a caller that decides from the journal's contents can't trust them
141
+ * until a journal on the reopened store has loaded it. */
142
+ get needsReopen() {
143
+ return this.unreadable !== null;
144
+ }
145
+ /** Append one entry. The returned ref says where it landed, not that it's durable. */
146
+ append(entry, opts = {}) {
147
+ this.assertReconciled();
148
+ if (opts.afterCommittedState)
149
+ this.store.sync();
150
+ return this.write(this.type, entry, opts.durable === true, (ref) => {
151
+ const value = idValue(ref.id);
152
+ if (value > this.lastEntryId)
153
+ this.lastEntryId = value;
154
+ this.sinceCheckpoint++;
155
+ });
156
+ }
157
+ /**
158
+ * Record the caller's reduced state as covering every entry this journal has
159
+ * loaded or appended. Like an entry, it's durable only with `durable`.
160
+ */
161
+ checkpoint(snapshot, opts = {}) {
162
+ this.assertReconciled();
163
+ const payload = { through: this.lastEntryId.toString(), snapshot };
164
+ return this.write(this.checkpointType, payload, opts.durable === true, () => {
165
+ this.sinceCheckpoint = 0;
166
+ });
167
+ }
168
+ /** Hand one record to the store; any failure from here on is ambiguous. */
169
+ write(type, payload, durable, onWritten) {
170
+ let record;
171
+ try {
172
+ record = this.store.appendJson(type, payload);
173
+ }
174
+ catch (err) {
175
+ this.unreadable = asError(err);
176
+ throw err;
177
+ }
178
+ try {
179
+ const ref = { id: record.id, sequence: record.sequence };
180
+ onWritten(ref);
181
+ if (durable)
182
+ this.store.sync();
183
+ return ref;
184
+ }
185
+ catch (err) {
186
+ this.unreconciled = asError(err);
187
+ throw err;
188
+ }
189
+ }
190
+ assertReconciled() {
191
+ if (this.unreadable) {
192
+ throw new Error(`RecordJournal(${this.type}): an earlier append failed inside the store ` +
193
+ `(${this.unreadable.message}), and its record may be in the store's log where this process can't ` +
194
+ `read it back; nothing more is written until the store is reopened, as at the next start`);
195
+ }
196
+ if (this.unreconciled) {
197
+ throw new Error(`RecordJournal(${this.type}): an earlier write failed after reaching the store ` +
198
+ `(${this.unreconciled.message}); load() and reduce its tail before writing again`);
199
+ }
200
+ }
201
+ }
202
+ //# sourceMappingURL=record-journal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record-journal.js","sourceRoot":"","sources":["../../src/record-journal.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /** No implicit deadline: the caller owns the module/child shutdown contract. */
2
+ export declare const DEFAULT_SHUTDOWN_TIMEOUT_MS: number | undefined;
3
+ export declare function validateShutdownTimeout(timeoutMs: number | undefined): void;
4
+ export declare class ShutdownTimeoutError extends AggregateError {
5
+ readonly pending: string[];
6
+ constructor(pending: string[], timeoutMs: number, failures: unknown[]);
7
+ }
8
+ export declare function shutdownErrorMessage(error: unknown): string;
9
+ export declare function waitForShutdown(attempts: Array<{
10
+ label: string;
11
+ promise: Promise<void>;
12
+ }>, timeoutMs: number | undefined): Promise<void>;
13
+ //# sourceMappingURL=shutdown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shutdown.d.ts","sourceRoot":"","sources":["../../src/shutdown.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,eAAO,MAAM,2BAA2B,EAAE,MAAM,GAAG,SAAqB,CAAC;AAEzE,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAK3E;AAED,qBAAa,oBAAqB,SAAQ,cAAc;IAC1C,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE;gBAAjB,OAAO,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE;CAM/E;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAE3D;AAED,wBAAsB,eAAe,CACnC,QAAQ,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GACxF,OAAO,CAAC,IAAI,CAAC,CAuBf"}
@@ -0,0 +1,46 @@
1
+ /** No implicit deadline: the caller owns the module/child shutdown contract. */
2
+ export const DEFAULT_SHUTDOWN_TIMEOUT_MS = undefined;
3
+ export function validateShutdownTimeout(timeoutMs) {
4
+ if (timeoutMs === undefined)
5
+ return;
6
+ if (!Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647) {
7
+ throw new RangeError('Shutdown timeout must be an integer from 1 to 2147483647 ms');
8
+ }
9
+ }
10
+ export class ShutdownTimeoutError extends AggregateError {
11
+ pending;
12
+ constructor(pending, timeoutMs, failures) {
13
+ super([...failures, new Error(`Shutdown timed out after ${timeoutMs} ms; pending: ${pending.join(', ')}`)], `Shutdown timed out after ${timeoutMs} ms; pending: ${pending.join(', ')}` +
14
+ (failures.length ? `; failures: ${failures.map(shutdownErrorMessage).join('; ')}` : ''));
15
+ this.pending = pending;
16
+ this.name = 'ShutdownTimeoutError';
17
+ }
18
+ }
19
+ export function shutdownErrorMessage(error) {
20
+ return error instanceof Error ? error.message : String(error);
21
+ }
22
+ export async function waitForShutdown(attempts, timeoutMs) {
23
+ validateShutdownTimeout(timeoutMs);
24
+ const pending = new Set(attempts.map((_, index) => index));
25
+ const failures = new Map();
26
+ const settled = Promise.all(attempts.map(({ promise }, index) => promise.then(() => { pending.delete(index); }, error => { failures.set(index, error); pending.delete(index); })));
27
+ let timer;
28
+ try {
29
+ if (timeoutMs === undefined)
30
+ await settled;
31
+ else
32
+ await Promise.race([settled, new Promise((_, reject) => {
33
+ timer = setTimeout(() => reject(new ShutdownTimeoutError([...pending].map(index => attempts[index].label), timeoutMs, [...failures.values()])), timeoutMs);
34
+ })]);
35
+ }
36
+ finally {
37
+ if (timer)
38
+ clearTimeout(timer);
39
+ }
40
+ const errors = [...failures.values()];
41
+ if (errors.length === 1)
42
+ throw errors[0];
43
+ if (errors.length > 1)
44
+ throw new AggregateError(errors, `Shutdown failed: ${errors.map(shutdownErrorMessage).join('; ')}`);
45
+ }
46
+ //# sourceMappingURL=shutdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shutdown.js","sourceRoot":"","sources":["../../src/shutdown.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Stored-row handling for silent heartbeat ticks.
3
+ *
4
+ * A silent tick stores no prompt row (#216): its reply is written straight
5
+ * after whatever the window held before it — often the previous assistant
6
+ * message, since a suppressed tick leaves no delivery receipt behind. The
7
+ * wire formatter merges consecutive same-role messages, so without a turn
8
+ * between them the tick would read as an unprompted continuation of the
9
+ * earlier response and one assistant message would carry signed thinking
10
+ * from two separate responses (a provider 400 when it is the latest
11
+ * assistant message; see `Agent.addAssistantResponse`).
12
+ *
13
+ * Every row a tick writes is therefore stamped with `metadata.silentHeartbeat`
14
+ * and, at request-build time only, a terse user turn is rendered immediately
15
+ * before the first of the tick's rows that survives compilation. The
16
+ * separator is never stored and its position is a function of the stamped
17
+ * rows alone, so every compile renders it in the same place (the tick's own
18
+ * request ends on the same turn), which keeps the prompt-cache prefix
19
+ * stable — unlike a per-compile injection that re-anchors to the newest user
20
+ * message.
21
+ *
22
+ * Known limit: compiled messages carry no store provenance, so a stored row
23
+ * is recognised only by structural identity the provider made unique
24
+ * (tool_use ids, thinking signatures). A tick reply with neither — unsigned
25
+ * and tool-free — gets no separator and still merges as before. Without
26
+ * signed thinking that merge carries no provider-rejection risk; it is only
27
+ * a misreading. Reply text is deliberately never used as identity: identical
28
+ * replies ("all quiet") would collide, and an ordinary reply with the same
29
+ * text could take a tick's separator and rewrite cached history. Exact
30
+ * matching for every row would need the compile result to expose each
31
+ * message's source rows.
32
+ */
33
+ import type { ContentBlock, NormalizedMessage } from '@animalabs/membrane';
34
+ /** Request-only separator text. Constant on purpose: it carries no time or
35
+ * id, so it is byte-identical on every compile. */
36
+ export declare const SILENT_HEARTBEAT_SEPARATOR = "[heartbeat tick]";
37
+ /** Identity of one authenticated silent heartbeat tick. */
38
+ export interface SilentHeartbeatTick {
39
+ /** The MCPL push event that woke the tick. A broadcast tick reaches every
40
+ * resident with the same id. */
41
+ eventId: string;
42
+ /** The MCPL server that sent it. */
43
+ serverId: string;
44
+ }
45
+ /** `metadata.silentHeartbeat` on every row a tick's turn writes. Residents
46
+ * share one message slot, so the stamp names the agent whose turn wrote it
47
+ * (tool_result rows are stored as `user`). */
48
+ export interface SilentHeartbeatStamp extends SilentHeartbeatTick {
49
+ agentName: string;
50
+ }
51
+ /** The tick stamp on a stored row's metadata, if any. */
52
+ export declare function silentHeartbeatOf(metadata: unknown): SilentHeartbeatStamp | undefined;
53
+ export declare function silentHeartbeatSeparatorTurn(): NormalizedMessage;
54
+ export interface StoredRowLike {
55
+ participant: string;
56
+ content: ContentBlock[];
57
+ metadata?: unknown;
58
+ }
59
+ export interface SilentTickIndex {
60
+ /** Identity key of every assistant row this agent stored during a tick → tick event id. */
61
+ rows: Map<string, string>;
62
+ /** Ticks this agent has already stored rows for (its own, not another resident's). */
63
+ started: Set<string>;
64
+ }
65
+ /** Index the rows this agent stored during silent ticks. */
66
+ export declare function indexSilentTickRows(stored: readonly StoredRowLike[], agentName: string): SilentTickIndex;
67
+ /**
68
+ * Insert the separator turn immediately before the first surviving row of
69
+ * each tick in a compiled window — the tick's first row when it is rendered,
70
+ * otherwise whichever later row a strategy kept. Rows folded into summaries
71
+ * simply do not match; ordinary messages pass through untouched.
72
+ */
73
+ export declare function separateSilentHeartbeatTicks(messages: NormalizedMessage[], rows: ReadonlyMap<string, string>, agentName: string): NormalizedMessage[];
74
+ //# sourceMappingURL=silent-heartbeat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"silent-heartbeat.d.ts","sourceRoot":"","sources":["../../src/silent-heartbeat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE3E;mDACmD;AACnD,eAAO,MAAM,0BAA0B,qBAAqB,CAAC;AAE7D,2DAA2D;AAC3D,MAAM,WAAW,mBAAmB;IAClC;oCACgC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;8CAE8C;AAC9C,MAAM,WAAW,oBAAqB,SAAQ,mBAAmB;IAC/D,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,yDAAyD;AACzD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,GAAG,oBAAoB,GAAG,SAAS,CAOrF;AAED,wBAAgB,4BAA4B,IAAI,iBAAiB,CAEhE;AAsBD,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,2FAA2F;IAC3F,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1B,sFAAsF;IACtF,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACtB;AAED,4DAA4D;AAC5D,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,eAAe,CAWxG;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,iBAAiB,EAAE,EAC7B,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EACjC,SAAS,EAAE,MAAM,GAChB,iBAAiB,EAAE,CAerB"}
@@ -0,0 +1,77 @@
1
+ /** Request-only separator text. Constant on purpose: it carries no time or
2
+ * id, so it is byte-identical on every compile. */
3
+ export const SILENT_HEARTBEAT_SEPARATOR = '[heartbeat tick]';
4
+ /** The tick stamp on a stored row's metadata, if any. */
5
+ export function silentHeartbeatOf(metadata) {
6
+ const stamp = metadata?.silentHeartbeat;
7
+ if (!stamp || typeof stamp !== 'object')
8
+ return undefined;
9
+ const { eventId, serverId, agentName } = stamp;
10
+ return typeof eventId === 'string' && typeof serverId === 'string' && typeof agentName === 'string'
11
+ ? { eventId, serverId, agentName }
12
+ : undefined;
13
+ }
14
+ export function silentHeartbeatSeparatorTurn() {
15
+ return { participant: 'user', content: [{ type: 'text', text: SILENT_HEARTBEAT_SEPARATOR }] };
16
+ }
17
+ /**
18
+ * Structural identity of one stored response wherever a compile renders it:
19
+ * tool_use ids and thinking signatures, which the provider made unique and
20
+ * strategies keep verbatim (the same identity the tool-result guard relies
21
+ * on). Text is never read, so request-build sanitizing (blank text blocks
22
+ * stripped) cannot make a stored row and its compiled copy disagree.
23
+ */
24
+ function identityKeys(content) {
25
+ const keys = [];
26
+ for (const block of content) {
27
+ if (block.type === 'tool_use' && typeof block.id === 'string' && block.id)
28
+ keys.push(`tool_use:${block.id}`);
29
+ else if (block.type === 'thinking' && typeof block.signature === 'string' && block.signature) {
30
+ keys.push(`thinking:${block.signature}`);
31
+ }
32
+ else if (block.type === 'redacted_thinking' && typeof block.data === 'string' && block.data) {
33
+ keys.push(`redacted:${block.data}`);
34
+ }
35
+ }
36
+ return keys;
37
+ }
38
+ /** Index the rows this agent stored during silent ticks. */
39
+ export function indexSilentTickRows(stored, agentName) {
40
+ const rows = new Map();
41
+ const started = new Set();
42
+ for (const row of stored) {
43
+ const stamp = silentHeartbeatOf(row.metadata);
44
+ if (!stamp || stamp.agentName !== agentName)
45
+ continue;
46
+ started.add(stamp.eventId);
47
+ if (row.participant !== agentName)
48
+ continue;
49
+ for (const key of identityKeys(row.content))
50
+ rows.set(key, stamp.eventId);
51
+ }
52
+ return { rows, started };
53
+ }
54
+ /**
55
+ * Insert the separator turn immediately before the first surviving row of
56
+ * each tick in a compiled window — the tick's first row when it is rendered,
57
+ * otherwise whichever later row a strategy kept. Rows folded into summaries
58
+ * simply do not match; ordinary messages pass through untouched.
59
+ */
60
+ export function separateSilentHeartbeatTicks(messages, rows, agentName) {
61
+ if (rows.size === 0)
62
+ return messages;
63
+ const separated = new Set();
64
+ const out = [];
65
+ for (const message of messages) {
66
+ if (message.participant === agentName) {
67
+ const eventId = identityKeys(message.content).map((key) => rows.get(key)).find((id) => id !== undefined);
68
+ if (eventId !== undefined && !separated.has(eventId)) {
69
+ separated.add(eventId);
70
+ out.push(silentHeartbeatSeparatorTurn());
71
+ }
72
+ }
73
+ out.push(message);
74
+ }
75
+ return out;
76
+ }
77
+ //# sourceMappingURL=silent-heartbeat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"silent-heartbeat.js","sourceRoot":"","sources":["../../src/silent-heartbeat.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import type { ToolDefinition, ToolResult } from './types/events.js';
2
+ export interface ToolPresentationConfig {
3
+ /** Absolute path to shared editable JSON. Missing file means no overrides. */
4
+ path: string;
5
+ /** Reserved generated workspace path, e.g. board/tool-catalogue.md. */
6
+ cataloguePath: string;
7
+ /** Optional description-only profiles, bound to a registered component source. */
8
+ defaults?: {
9
+ source: string;
10
+ path: string;
11
+ }[];
12
+ }
13
+ export interface ToolOverride {
14
+ description?: string;
15
+ visible?: boolean;
16
+ }
17
+ export interface PresentationDocument {
18
+ version: 1;
19
+ tools: Record<string, ToolOverride>;
20
+ }
21
+ export interface PresentationSnapshot {
22
+ revision: string;
23
+ source: string;
24
+ cataloguePath: string;
25
+ diagnostics: string[];
26
+ available: ToolDefinition[];
27
+ advertised: ToolDefinition[];
28
+ entries: {
29
+ name: string;
30
+ source: string;
31
+ visible: boolean;
32
+ originalDescription: string;
33
+ description: string;
34
+ descriptionSource?: string;
35
+ inputSchema: ToolDefinition['inputSchema'];
36
+ }[];
37
+ }
38
+ export declare const isPresentationTool: (name: string) => boolean;
39
+ export declare function parsePresentation(raw: string): PresentationDocument;
40
+ export declare function presentationTools(path: string): ToolDefinition[];
41
+ /** Per-agent presentation; files are the sole persistent source, independent of editor identity. */
42
+ export declare class ToolPresentation {
43
+ readonly config: ToolPresentationConfig;
44
+ constructor(config: ToolPresentationConfig);
45
+ private read;
46
+ resolve(tools: ToolDefinition[], sources?: ReadonlyMap<string, string>): PresentationSnapshot;
47
+ edit(tool: string, input: unknown, available: ToolDefinition[]): ToolResult;
48
+ }
49
+ export declare function renderCatalogue(snapshot: PresentationSnapshot): string;
50
+ //# sourceMappingURL=tool-presentation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-presentation.d.ts","sourceRoot":"","sources":["../../src/tool-presentation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpE,MAAM,WAAW,sBAAsB;IACrC,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,aAAa,EAAE,MAAM,CAAC;IACtB,kFAAkF;IAClF,QAAQ,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC/C;AACD,MAAM,WAAW,YAAY;IAAG,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE;AACzE,MAAM,WAAW,oBAAoB;IAAG,OAAO,EAAE,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;CAAE;AACzF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,mBAAmB,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,cAAc,CAAC,aAAa,CAAC,CAAA;KAAE,EAAE,CAAC;CACzL;AAGD,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,KAAG,OAA4E,CAAC;AAG/H,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,CAanE;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,EAAE,CAQhE;AAED,oGAAoG;AACpG,qBAAa,gBAAgB;IACf,QAAQ,CAAC,MAAM,EAAE,sBAAsB;gBAA9B,MAAM,EAAE,sBAAsB;IACnD,OAAO,CAAC,IAAI;IAcZ,OAAO,CAAC,KAAK,EAAE,cAAc,EAAE,EAAE,OAAO,GAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAa,GAAG,oBAAoB;IAwCxG,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,GAAG,UAAU;CAqC5E;AACD,wBAAgB,eAAe,CAAC,QAAQ,EAAE,oBAAoB,GAAG,MAAM,CAuCtE"}