@fabric-harness/sdk 1.13.0 → 2.0.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 (169) hide show
  1. package/dist/action.d.ts +69 -0
  2. package/dist/action.d.ts.map +1 -0
  3. package/dist/action.js +119 -0
  4. package/dist/action.js.map +1 -0
  5. package/dist/agent-definition.d.ts +6 -0
  6. package/dist/agent-definition.d.ts.map +1 -1
  7. package/dist/agent-definition.js.map +1 -1
  8. package/dist/agent.d.ts +2 -0
  9. package/dist/agent.d.ts.map +1 -1
  10. package/dist/agent.js +35 -1
  11. package/dist/agent.js.map +1 -1
  12. package/dist/attachment-store-contract.d.ts +34 -0
  13. package/dist/attachment-store-contract.d.ts.map +1 -0
  14. package/dist/attachment-store-contract.js +0 -0
  15. package/dist/attachment-store-contract.js.map +1 -0
  16. package/dist/attachment-store.d.ts +107 -0
  17. package/dist/attachment-store.d.ts.map +1 -0
  18. package/dist/attachment-store.js +164 -0
  19. package/dist/attachment-store.js.map +1 -0
  20. package/dist/channel.d.ts +84 -0
  21. package/dist/channel.d.ts.map +1 -0
  22. package/dist/channel.js +115 -0
  23. package/dist/channel.js.map +1 -0
  24. package/dist/context-budget.d.ts +16 -0
  25. package/dist/context-budget.d.ts.map +1 -1
  26. package/dist/context-budget.js +38 -3
  27. package/dist/context-budget.js.map +1 -1
  28. package/dist/conversation-projection.d.ts +52 -0
  29. package/dist/conversation-projection.d.ts.map +1 -0
  30. package/dist/conversation-projection.js +207 -0
  31. package/dist/conversation-projection.js.map +1 -0
  32. package/dist/conversation-stream-contract.d.ts +37 -0
  33. package/dist/conversation-stream-contract.d.ts.map +1 -0
  34. package/dist/conversation-stream-contract.js +453 -0
  35. package/dist/conversation-stream-contract.js.map +1 -0
  36. package/dist/conversation-stream.d.ts +141 -0
  37. package/dist/conversation-stream.d.ts.map +1 -0
  38. package/dist/conversation-stream.js +159 -0
  39. package/dist/conversation-stream.js.map +1 -0
  40. package/dist/cost-budget.d.ts +106 -1
  41. package/dist/cost-budget.d.ts.map +1 -1
  42. package/dist/cost-budget.js +48 -2
  43. package/dist/cost-budget.js.map +1 -1
  44. package/dist/delivered-message.d.ts +90 -0
  45. package/dist/delivered-message.d.ts.map +1 -0
  46. package/dist/delivered-message.js +154 -0
  47. package/dist/delivered-message.js.map +1 -0
  48. package/dist/dispatch.d.ts +108 -0
  49. package/dist/dispatch.d.ts.map +1 -0
  50. package/dist/dispatch.js +179 -0
  51. package/dist/dispatch.js.map +1 -0
  52. package/dist/error.d.ts +1 -1
  53. package/dist/error.d.ts.map +1 -1
  54. package/dist/error.js.map +1 -1
  55. package/dist/history.d.ts +49 -2
  56. package/dist/history.d.ts.map +1 -1
  57. package/dist/history.js +163 -0
  58. package/dist/history.js.map +1 -1
  59. package/dist/in-memory-submission-store.d.ts +53 -0
  60. package/dist/in-memory-submission-store.d.ts.map +1 -0
  61. package/dist/in-memory-submission-store.js +340 -0
  62. package/dist/in-memory-submission-store.js.map +1 -0
  63. package/dist/index.d.ts +51 -13
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +28 -7
  66. package/dist/index.js.map +1 -1
  67. package/dist/loop.d.ts +29 -3
  68. package/dist/loop.d.ts.map +1 -1
  69. package/dist/loop.js +167 -24
  70. package/dist/loop.js.map +1 -1
  71. package/dist/model.d.ts +37 -0
  72. package/dist/model.d.ts.map +1 -1
  73. package/dist/model.js +250 -79
  74. package/dist/model.js.map +1 -1
  75. package/dist/otel-observer.d.ts +44 -0
  76. package/dist/otel-observer.d.ts.map +1 -0
  77. package/dist/otel-observer.js +165 -0
  78. package/dist/otel-observer.js.map +1 -0
  79. package/dist/persistence.d.ts +70 -0
  80. package/dist/persistence.d.ts.map +1 -0
  81. package/dist/persistence.js +17 -0
  82. package/dist/persistence.js.map +1 -0
  83. package/dist/persistent-agent.d.ts +86 -0
  84. package/dist/persistent-agent.d.ts.map +1 -0
  85. package/dist/persistent-agent.js +75 -0
  86. package/dist/persistent-agent.js.map +1 -0
  87. package/dist/pi-loop-runtime.d.ts.map +1 -1
  88. package/dist/pi-loop-runtime.js +7 -1
  89. package/dist/pi-loop-runtime.js.map +1 -1
  90. package/dist/profile.d.ts +33 -0
  91. package/dist/profile.d.ts.map +1 -0
  92. package/dist/profile.js +10 -0
  93. package/dist/profile.js.map +1 -0
  94. package/dist/providers.d.ts +14 -0
  95. package/dist/providers.d.ts.map +1 -1
  96. package/dist/providers.js +18 -1
  97. package/dist/providers.js.map +1 -1
  98. package/dist/result.d.ts +12 -0
  99. package/dist/result.d.ts.map +1 -1
  100. package/dist/result.js +26 -0
  101. package/dist/result.js.map +1 -1
  102. package/dist/retrieval.d.ts +61 -0
  103. package/dist/retrieval.d.ts.map +1 -0
  104. package/dist/retrieval.js +26 -0
  105. package/dist/retrieval.js.map +1 -0
  106. package/dist/runtime-mode.d.ts +1 -1
  107. package/dist/runtime-mode.d.ts.map +1 -1
  108. package/dist/runtime-mode.js +2 -1
  109. package/dist/runtime-mode.js.map +1 -1
  110. package/dist/scope-key.d.ts +25 -0
  111. package/dist/scope-key.d.ts.map +1 -0
  112. package/dist/scope-key.js +34 -0
  113. package/dist/scope-key.js.map +1 -0
  114. package/dist/session.d.ts +47 -1
  115. package/dist/session.d.ts.map +1 -1
  116. package/dist/session.js +364 -35
  117. package/dist/session.js.map +1 -1
  118. package/dist/skills.d.ts +33 -0
  119. package/dist/skills.d.ts.map +1 -0
  120. package/dist/skills.js +65 -0
  121. package/dist/skills.js.map +1 -0
  122. package/dist/store.d.ts +36 -1
  123. package/dist/store.d.ts.map +1 -1
  124. package/dist/store.js +83 -0
  125. package/dist/store.js.map +1 -1
  126. package/dist/stream-chunks.d.ts +42 -0
  127. package/dist/stream-chunks.d.ts.map +1 -0
  128. package/dist/stream-chunks.js +138 -0
  129. package/dist/stream-chunks.js.map +1 -0
  130. package/dist/strict.d.ts +40 -11
  131. package/dist/strict.d.ts.map +1 -1
  132. package/dist/strict.js +22 -7
  133. package/dist/strict.js.map +1 -1
  134. package/dist/submission-context.d.ts +26 -0
  135. package/dist/submission-context.d.ts.map +1 -0
  136. package/dist/submission-context.js +20 -0
  137. package/dist/submission-context.js.map +1 -0
  138. package/dist/submission-runner.d.ts +150 -0
  139. package/dist/submission-runner.d.ts.map +1 -0
  140. package/dist/submission-runner.js +767 -0
  141. package/dist/submission-runner.js.map +1 -0
  142. package/dist/submission-state.d.ts +50 -0
  143. package/dist/submission-state.d.ts.map +1 -0
  144. package/dist/submission-state.js +82 -0
  145. package/dist/submission-state.js.map +1 -0
  146. package/dist/submission-store-contract.d.ts +31 -0
  147. package/dist/submission-store-contract.d.ts.map +1 -0
  148. package/dist/submission-store-contract.js +730 -0
  149. package/dist/submission-store-contract.js.map +1 -0
  150. package/dist/submission-store.d.ts +365 -0
  151. package/dist/submission-store.d.ts.map +1 -0
  152. package/dist/submission-store.js +191 -0
  153. package/dist/submission-store.js.map +1 -0
  154. package/dist/submission-telemetry.d.ts +56 -0
  155. package/dist/submission-telemetry.d.ts.map +1 -0
  156. package/dist/submission-telemetry.js +36 -0
  157. package/dist/submission-telemetry.js.map +1 -0
  158. package/dist/testing-contracts.d.ts +17 -0
  159. package/dist/testing-contracts.d.ts.map +1 -0
  160. package/dist/testing-contracts.js +14 -0
  161. package/dist/testing-contracts.js.map +1 -0
  162. package/dist/tools.d.ts +32 -4
  163. package/dist/tools.d.ts.map +1 -1
  164. package/dist/tools.js +132 -6
  165. package/dist/tools.js.map +1 -1
  166. package/dist/types.d.ts +132 -3
  167. package/dist/types.d.ts.map +1 -1
  168. package/dist/types.js.map +1 -1
  169. package/package.json +16 -8
@@ -0,0 +1,453 @@
1
+ /**
2
+ * Shared contract tests for {@link ConversationStreamStore} implementations.
3
+ *
4
+ * Store packages call {@link defineConversationStreamStoreContractTests} with
5
+ * a factory that creates their backend. The tests exercise every method on
6
+ * `ConversationStreamStore` with identical behavioral assertions regardless of
7
+ * the underlying storage engine — batch atomicity, producer fencing via
8
+ * epochs, idempotent redelivery, and offset catch-up reads.
9
+ *
10
+ * `subscribe` is a process-local notification seam: the suite only asserts
11
+ * that appends made through the same store instance notify local listeners
12
+ * and that unsubscribing stops further notifications. Cross-process fan-out
13
+ * is deliberately out of contract — backends may no-op it.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * import { defineConversationStreamStoreContractTests } from '@fabric-harness/sdk/testing/contracts';
18
+ *
19
+ * defineConversationStreamStoreContractTests('My backend', async () => ({
20
+ * store: await createMyStreamStore(),
21
+ * close: async () => myBackend.close(),
22
+ * }));
23
+ * ```
24
+ */
25
+ import { afterEach, describe, expect, it } from 'vitest';
26
+ // ─── Fixtures ───────────────────────────────────────────────────────────────
27
+ const STREAM_PATH = 'conversations/agent:assistant:agent-1:default';
28
+ const IDENTITY = { storeSessionId: 'agent:assistant:agent-1:default' };
29
+ function entry(id, type, parentId) {
30
+ return {
31
+ id,
32
+ type,
33
+ timestamp: '2026-06-03T00:00:00.000Z',
34
+ ...(parentId ? { parentId } : {}),
35
+ data: { text: `payload for ${id}` },
36
+ };
37
+ }
38
+ function entryRecord(id, type, parentId) {
39
+ return { kind: 'entry', entry: entry(id, type, parentId) };
40
+ }
41
+ async function expectStreamError(operation, code) {
42
+ const outcome = await operation.then(() => undefined, (error) => error);
43
+ expect(outcome, `expected rejection with ${code}`).toBeDefined();
44
+ expect(outcome).toMatchObject({ name: 'ConversationStreamStoreError', code });
45
+ }
46
+ /**
47
+ * Register the standard {@link ConversationStreamStore} contract tests under
48
+ * the given describe label. Each test gets a fresh store from `factory()`.
49
+ */
50
+ export function defineConversationStreamStoreContractTests(name, factory) {
51
+ describe(name, () => {
52
+ let active;
53
+ async function create() {
54
+ active = await factory();
55
+ return active.store;
56
+ }
57
+ async function createWithProducer(producerId = 'writer-1') {
58
+ const store = await create();
59
+ await store.createStream(STREAM_PATH, IDENTITY);
60
+ const claim = await store.acquireProducer(STREAM_PATH, producerId);
61
+ return { store, claim };
62
+ }
63
+ afterEach(async () => {
64
+ await active?.close?.();
65
+ active = undefined;
66
+ });
67
+ // ── Stream lifecycle ──────────────────────────────────────────────
68
+ describe('stream lifecycle', () => {
69
+ it('creates a stream idempotently without resetting existing state', async () => {
70
+ const store = await create();
71
+ await store.createStream(STREAM_PATH, IDENTITY);
72
+ await store.createStream(STREAM_PATH, IDENTITY);
73
+ expect(await store.getMeta(STREAM_PATH)).toEqual({
74
+ identity: IDENTITY,
75
+ nextOffset: '0',
76
+ producerId: null,
77
+ producerEpoch: 0,
78
+ nextProducerSequence: 0,
79
+ });
80
+ const claim = await store.acquireProducer(STREAM_PATH, 'writer-1');
81
+ await store.append({
82
+ path: STREAM_PATH,
83
+ producerId: claim.producerId,
84
+ producerEpoch: claim.producerEpoch,
85
+ producerSequence: 0,
86
+ records: [entryRecord('a', 'user_prompt')],
87
+ });
88
+ // Re-creating an existing stream keeps offsets and producer state.
89
+ await store.createStream(STREAM_PATH, IDENTITY);
90
+ expect(await store.getMeta(STREAM_PATH)).toMatchObject({
91
+ nextOffset: '1',
92
+ producerId: 'writer-1',
93
+ producerEpoch: claim.producerEpoch,
94
+ nextProducerSequence: 1,
95
+ });
96
+ });
97
+ it('deletes a stream, tolerates re-deletion, and allows a fresh restart', async () => {
98
+ const { store, claim } = await createWithProducer();
99
+ await store.append({
100
+ path: STREAM_PATH,
101
+ producerId: claim.producerId,
102
+ producerEpoch: claim.producerEpoch,
103
+ producerSequence: 0,
104
+ records: [entryRecord('a', 'user_prompt')],
105
+ });
106
+ await store.delete(STREAM_PATH);
107
+ expect(await store.getMeta(STREAM_PATH)).toBeNull();
108
+ await expectStreamError(store.read(STREAM_PATH), 'STREAM_NOT_FOUND');
109
+ // Deleting a missing stream is a silent no-op.
110
+ await expect(store.delete(STREAM_PATH)).resolves.toBeUndefined();
111
+ // Recreating starts a fresh, empty stream.
112
+ await store.createStream(STREAM_PATH, IDENTITY);
113
+ expect(await store.getMeta(STREAM_PATH)).toMatchObject({ nextOffset: '0' });
114
+ expect((await store.read(STREAM_PATH)).batches).toEqual([]);
115
+ });
116
+ it('throws STREAM_NOT_FOUND for operations against a missing stream', async () => {
117
+ const store = await create();
118
+ await expectStreamError(store.acquireProducer('missing', 'writer-1'), 'STREAM_NOT_FOUND');
119
+ await expectStreamError(store.read('missing'), 'STREAM_NOT_FOUND');
120
+ await expectStreamError(store.append({
121
+ path: 'missing',
122
+ producerId: 'writer-1',
123
+ producerEpoch: 1,
124
+ producerSequence: 0,
125
+ records: [entryRecord('a', 'user_prompt')],
126
+ }), 'STREAM_NOT_FOUND');
127
+ });
128
+ it('returns null meta for a missing stream', async () => {
129
+ const store = await create();
130
+ expect(await store.getMeta('missing')).toBeNull();
131
+ });
132
+ });
133
+ // ── Producer fencing ──────────────────────────────────────────────
134
+ describe('producer fencing', () => {
135
+ it('bumps the epoch on every acquisition and starts sequences at 0', async () => {
136
+ const store = await create();
137
+ await store.createStream(STREAM_PATH, IDENTITY);
138
+ const first = await store.acquireProducer(STREAM_PATH, 'writer-1');
139
+ const second = await store.acquireProducer(STREAM_PATH, 'writer-2');
140
+ expect(first).toEqual({
141
+ producerId: 'writer-1',
142
+ producerEpoch: 1,
143
+ nextProducerSequence: 0,
144
+ offset: '0',
145
+ });
146
+ expect(second.producerEpoch).toBe(first.producerEpoch + 1);
147
+ expect(second.nextProducerSequence).toBe(0);
148
+ expect(second.offset).toBe('0');
149
+ });
150
+ it('fences stale producers after re-acquisition', async () => {
151
+ const { store, claim: stale } = await createWithProducer();
152
+ await store.acquireProducer(STREAM_PATH, 'writer-2');
153
+ // A different producer id with a stale epoch is fenced…
154
+ await expectStreamError(store.append({
155
+ path: STREAM_PATH,
156
+ producerId: stale.producerId,
157
+ producerEpoch: stale.producerEpoch,
158
+ producerSequence: 0,
159
+ records: [entryRecord('x', 'user_prompt')],
160
+ }), 'PRODUCER_FENCED');
161
+ // …and so is the same producer id carrying a stale epoch.
162
+ const current = await store.acquireProducer(STREAM_PATH, 'writer-2');
163
+ await expectStreamError(store.append({
164
+ path: STREAM_PATH,
165
+ producerId: 'writer-2',
166
+ producerEpoch: current.producerEpoch - 1,
167
+ producerSequence: 0,
168
+ records: [entryRecord('y', 'user_prompt')],
169
+ }), 'PRODUCER_FENCED');
170
+ });
171
+ it('resets nextProducerSequence on re-acquisition while offsets keep growing', async () => {
172
+ // This is how fork/replay epoch bumps work: the projector re-acquires,
173
+ // starts sequencing from 0 again, and the stream keeps appending at
174
+ // its previous tail (see conversation-projection.ts).
175
+ const { store, claim } = await createWithProducer();
176
+ for (let index = 0; index < 2; index += 1) {
177
+ await store.append({
178
+ path: STREAM_PATH,
179
+ producerId: claim.producerId,
180
+ producerEpoch: claim.producerEpoch,
181
+ producerSequence: index,
182
+ records: [entryRecord(`e${index}`, 'user_prompt')],
183
+ });
184
+ }
185
+ const renewed = await store.acquireProducer(STREAM_PATH, claim.producerId);
186
+ expect(renewed.producerEpoch).toBeGreaterThan(claim.producerEpoch);
187
+ expect(renewed.nextProducerSequence).toBe(0);
188
+ expect(renewed.offset).toBe('2');
189
+ const appended = await store.append({
190
+ path: STREAM_PATH,
191
+ producerId: renewed.producerId,
192
+ producerEpoch: renewed.producerEpoch,
193
+ producerSequence: 0,
194
+ records: [entryRecord('after-fork', 'assistant_message')],
195
+ });
196
+ expect(appended.offset).toBe('2');
197
+ expect(await store.getMeta(STREAM_PATH)).toMatchObject({
198
+ nextOffset: '3',
199
+ producerEpoch: renewed.producerEpoch,
200
+ nextProducerSequence: 1,
201
+ });
202
+ });
203
+ });
204
+ // ── Append semantics ──────────────────────────────────────────────
205
+ describe('append semantics', () => {
206
+ it('persists a multi-record batch atomically under one offset', async () => {
207
+ const { store, claim } = await createWithProducer();
208
+ const records = [
209
+ entryRecord('a', 'user_prompt'),
210
+ entryRecord('b', 'assistant_message', 'a'),
211
+ entryRecord('c', 'tool_call', 'b'),
212
+ ];
213
+ const appended = await store.append({
214
+ path: STREAM_PATH,
215
+ producerId: claim.producerId,
216
+ producerEpoch: claim.producerEpoch,
217
+ producerSequence: 0,
218
+ records,
219
+ });
220
+ expect(appended.offset).toBe('0');
221
+ const result = await store.read(STREAM_PATH);
222
+ expect(result.batches).toHaveLength(1);
223
+ expect(result.batches[0]?.offset).toBe('0');
224
+ expect(result.batches[0]?.records).toEqual(records);
225
+ });
226
+ it('treats redelivered appends as idempotent (same coordinates → same offset)', async () => {
227
+ const { store, claim } = await createWithProducer();
228
+ const input = {
229
+ path: STREAM_PATH,
230
+ producerId: claim.producerId,
231
+ producerEpoch: claim.producerEpoch,
232
+ producerSequence: 0,
233
+ records: [entryRecord('a', 'user_prompt')],
234
+ };
235
+ const first = await store.append(input);
236
+ const replay = await store.append(input);
237
+ expect(replay.offset).toBe(first.offset);
238
+ // No duplicate batch was written and the counters advanced once.
239
+ const result = await store.read(STREAM_PATH);
240
+ expect(result.batches).toHaveLength(1);
241
+ expect(await store.getMeta(STREAM_PATH)).toMatchObject({
242
+ nextOffset: '1',
243
+ nextProducerSequence: 1,
244
+ });
245
+ });
246
+ it('redelivery of an earlier sequence returns its offset after later appends', async () => {
247
+ const { store, claim } = await createWithProducer();
248
+ const offsets = [];
249
+ for (let index = 0; index < 3; index += 1) {
250
+ const appended = await store.append({
251
+ path: STREAM_PATH,
252
+ producerId: claim.producerId,
253
+ producerEpoch: claim.producerEpoch,
254
+ producerSequence: index,
255
+ records: [entryRecord(`e${index}`, 'user_prompt')],
256
+ });
257
+ offsets.push(appended.offset);
258
+ }
259
+ const replay = await store.append({
260
+ path: STREAM_PATH,
261
+ producerId: claim.producerId,
262
+ producerEpoch: claim.producerEpoch,
263
+ producerSequence: 1,
264
+ records: [entryRecord('e1', 'user_prompt')],
265
+ });
266
+ expect(replay.offset).toBe(offsets[1]);
267
+ expect((await store.read(STREAM_PATH)).batches).toHaveLength(3);
268
+ });
269
+ it('rejects out-of-order producer sequences with SEQUENCE_CONFLICT', async () => {
270
+ const { store, claim } = await createWithProducer();
271
+ await expectStreamError(store.append({
272
+ path: STREAM_PATH,
273
+ producerId: claim.producerId,
274
+ producerEpoch: claim.producerEpoch,
275
+ producerSequence: 5,
276
+ records: [entryRecord('a', 'user_prompt')],
277
+ }), 'SEQUENCE_CONFLICT');
278
+ await store.append({
279
+ path: STREAM_PATH,
280
+ producerId: claim.producerId,
281
+ producerEpoch: claim.producerEpoch,
282
+ producerSequence: 0,
283
+ records: [entryRecord('a', 'user_prompt')],
284
+ });
285
+ // A gap after a committed sequence is also a conflict.
286
+ await expectStreamError(store.append({
287
+ path: STREAM_PATH,
288
+ producerId: claim.producerId,
289
+ producerEpoch: claim.producerEpoch,
290
+ producerSequence: 2,
291
+ records: [entryRecord('b', 'user_prompt')],
292
+ }), 'SEQUENCE_CONFLICT');
293
+ expect((await store.read(STREAM_PATH)).batches).toHaveLength(1);
294
+ });
295
+ it('accepts an optional submission correlation on append', async () => {
296
+ const { store, claim } = await createWithProducer();
297
+ const appended = await store.append({
298
+ path: STREAM_PATH,
299
+ producerId: claim.producerId,
300
+ producerEpoch: claim.producerEpoch,
301
+ producerSequence: 0,
302
+ submission: { submissionId: 'submission-1', attemptId: 'attempt-1' },
303
+ records: [entryRecord('a', 'user_prompt')],
304
+ });
305
+ expect(appended.offset).toBe('0');
306
+ const result = await store.read(STREAM_PATH);
307
+ expect(result.batches[0]?.records).toEqual([entryRecord('a', 'user_prompt')]);
308
+ });
309
+ });
310
+ // ── Record round-trips ────────────────────────────────────────────
311
+ describe('record round-trips', () => {
312
+ it('round-trips entry and truncated records structurally', async () => {
313
+ const { store, claim } = await createWithProducer();
314
+ const realisticEntry = {
315
+ id: 'entry-1',
316
+ type: 'assistant_message',
317
+ timestamp: '2026-06-03T12:34:56.789Z',
318
+ parentId: 'entry-0',
319
+ tenantId: 'tenant-1',
320
+ data: {
321
+ text: 'Here is the answer.',
322
+ usage: { inputTokens: 12, outputTokens: 34 },
323
+ annotations: ['grounded', 'final'],
324
+ },
325
+ };
326
+ const records = [
327
+ { kind: 'entry', entry: realisticEntry },
328
+ {
329
+ kind: 'truncated',
330
+ rewoundTo: 'entry-0',
331
+ newLeafId: 'entry-1b',
332
+ reason: 'replay',
333
+ },
334
+ ];
335
+ await store.append({
336
+ path: STREAM_PATH,
337
+ producerId: claim.producerId,
338
+ producerEpoch: claim.producerEpoch,
339
+ producerSequence: 0,
340
+ records,
341
+ });
342
+ const result = await store.read(STREAM_PATH);
343
+ expect(result.batches[0]?.records).toEqual(records);
344
+ });
345
+ });
346
+ // ── Reads ─────────────────────────────────────────────────────────
347
+ describe('offset reads', () => {
348
+ async function appendMany(store, claim, count) {
349
+ for (let index = 0; index < count; index += 1) {
350
+ await store.append({
351
+ path: STREAM_PATH,
352
+ producerId: claim.producerId,
353
+ producerEpoch: claim.producerEpoch,
354
+ producerSequence: index,
355
+ records: [entryRecord(`e${index}`, 'user_prompt')],
356
+ });
357
+ }
358
+ }
359
+ it('supports limited catch-up reads with nextOffset and upToDate', async () => {
360
+ const { store, claim } = await createWithProducer();
361
+ await appendMany(store, claim, 5);
362
+ const first = await store.read(STREAM_PATH, { limit: 2 });
363
+ expect(first.batches.map((batch) => batch.offset)).toEqual(['0', '1']);
364
+ expect(first.nextOffset).toBe('2');
365
+ expect(first.upToDate).toBe(false);
366
+ const second = await store.read(STREAM_PATH, { offset: first.nextOffset, limit: 2 });
367
+ expect(second.batches.map((batch) => batch.offset)).toEqual(['2', '3']);
368
+ expect(second.upToDate).toBe(false);
369
+ const rest = await store.read(STREAM_PATH, { offset: second.nextOffset });
370
+ expect(rest.batches.map((batch) => batch.offset)).toEqual(['4']);
371
+ expect(rest.nextOffset).toBe('5');
372
+ expect(rest.upToDate).toBe(true);
373
+ });
374
+ it('reads from a mid-stream offset', async () => {
375
+ const { store, claim } = await createWithProducer();
376
+ await appendMany(store, claim, 4);
377
+ const result = await store.read(STREAM_PATH, { offset: '2' });
378
+ expect(result.batches.map((batch) => batch.offset)).toEqual(['2', '3']);
379
+ expect(result.batches.flatMap((batch) => batch.records.flatMap((record) => (record.kind === 'entry' ? [record.entry.id] : [])))).toEqual(['e2', 'e3']);
380
+ expect(result.upToDate).toBe(true);
381
+ });
382
+ it('returns an empty up-to-date page when reading at the tail', async () => {
383
+ const { store, claim } = await createWithProducer();
384
+ await appendMany(store, claim, 2);
385
+ const tail = await store.read(STREAM_PATH, { offset: '2' });
386
+ expect(tail.batches).toEqual([]);
387
+ expect(tail.nextOffset).toBe('2');
388
+ expect(tail.upToDate).toBe(true);
389
+ });
390
+ });
391
+ // ── Meta ──────────────────────────────────────────────────────────
392
+ describe('getMeta()', () => {
393
+ it('reflects producer state and offsets before and after appends', async () => {
394
+ const store = await create();
395
+ await store.createStream(STREAM_PATH, IDENTITY);
396
+ expect(await store.getMeta(STREAM_PATH)).toEqual({
397
+ identity: IDENTITY,
398
+ nextOffset: '0',
399
+ producerId: null,
400
+ producerEpoch: 0,
401
+ nextProducerSequence: 0,
402
+ });
403
+ const claim = await store.acquireProducer(STREAM_PATH, 'writer-1');
404
+ for (let index = 0; index < 2; index += 1) {
405
+ await store.append({
406
+ path: STREAM_PATH,
407
+ producerId: claim.producerId,
408
+ producerEpoch: claim.producerEpoch,
409
+ producerSequence: index,
410
+ records: [entryRecord(`e${index}`, 'user_prompt')],
411
+ });
412
+ }
413
+ expect(await store.getMeta(STREAM_PATH)).toEqual({
414
+ identity: IDENTITY,
415
+ nextOffset: '2',
416
+ producerId: 'writer-1',
417
+ producerEpoch: claim.producerEpoch,
418
+ nextProducerSequence: 2,
419
+ });
420
+ });
421
+ });
422
+ // ── Subscriptions (process-local) ─────────────────────────────────
423
+ describe('subscribe()', () => {
424
+ it('notifies local listeners on append and stops after unsubscribe', async () => {
425
+ const { store, claim } = await createWithProducer();
426
+ let notified = 0;
427
+ const unsubscribe = store.subscribe(STREAM_PATH, () => {
428
+ notified += 1;
429
+ });
430
+ expect(typeof unsubscribe).toBe('function');
431
+ await store.append({
432
+ path: STREAM_PATH,
433
+ producerId: claim.producerId,
434
+ producerEpoch: claim.producerEpoch,
435
+ producerSequence: 0,
436
+ records: [entryRecord('a', 'user_prompt')],
437
+ });
438
+ expect(notified).toBeGreaterThanOrEqual(1);
439
+ const seen = notified;
440
+ unsubscribe();
441
+ await store.append({
442
+ path: STREAM_PATH,
443
+ producerId: claim.producerId,
444
+ producerEpoch: claim.producerEpoch,
445
+ producerSequence: 1,
446
+ records: [entryRecord('b', 'user_prompt')],
447
+ });
448
+ expect(notified).toBe(seen);
449
+ });
450
+ });
451
+ });
452
+ }
453
+ //# sourceMappingURL=conversation-stream-contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-stream-contract.js","sourceRoot":"","sources":["../src/conversation-stream-contract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,141 @@
1
+ import type { SessionEntry } from './types.js';
2
+ /**
3
+ * Append-only conversation stream — the durable, offset-addressable
4
+ * projection of a session's active path (v2 migration, phase A4).
5
+ *
6
+ * The SessionEntry DAG remains the single source of truth for model context;
7
+ * this stream exists so clients can read a conversation with offsets
8
+ * (catch-up + live tail), across processes, without count-based replay. The
9
+ * projection appends one record per active-path entry, and — because the DAG
10
+ * can branch (fork/replay/checkpoint-restore) while a stream cannot — an
11
+ * explicit `truncated` record whenever the active path rewinds, paired with
12
+ * a producer-epoch bump so stale writers are fenced out.
13
+ */
14
+ export type ConversationStreamRecord = {
15
+ kind: 'entry';
16
+ entry: SessionEntry;
17
+ } | {
18
+ kind: 'truncated';
19
+ /**
20
+ * Last entry id shared with the previously projected path (empty when
21
+ * the paths diverge from the very first entry). Readers drop every
22
+ * projected record after this id and continue from the next records.
23
+ */
24
+ rewoundTo: string;
25
+ /** The new active leaf after the rewind. */
26
+ newLeafId: string;
27
+ /** Why the path branched: replay, checkpoint restore, manual fork… */
28
+ reason: string;
29
+ };
30
+ export interface ConversationStreamIdentity {
31
+ /** Store session id the stream projects (e.g. `agent:name:id:session`). */
32
+ storeSessionId: string;
33
+ }
34
+ export interface ConversationProducerClaim {
35
+ producerId: string;
36
+ producerEpoch: number;
37
+ nextProducerSequence: number;
38
+ offset: string;
39
+ }
40
+ export interface ConversationStreamBatch {
41
+ offset: string;
42
+ records: ConversationStreamRecord[];
43
+ }
44
+ export interface ConversationStreamReadResult {
45
+ batches: ConversationStreamBatch[];
46
+ /** Pass back as `offset` on the next read to continue. */
47
+ nextOffset: string;
48
+ /** True when the read reached the stream tail. */
49
+ upToDate: boolean;
50
+ }
51
+ export interface ConversationStreamMeta {
52
+ identity: ConversationStreamIdentity;
53
+ nextOffset: string;
54
+ producerId: string | null;
55
+ producerEpoch: number;
56
+ nextProducerSequence: number;
57
+ }
58
+ export interface ConversationStreamAppendInput {
59
+ path: string;
60
+ producerId: string;
61
+ producerEpoch: number;
62
+ producerSequence: number;
63
+ /** Correlates the batch to a submission attempt when one owns the turn. */
64
+ submission?: {
65
+ submissionId: string;
66
+ attemptId: string;
67
+ };
68
+ records: readonly ConversationStreamRecord[];
69
+ }
70
+ /**
71
+ * Durable append-only conversation stream storage.
72
+ *
73
+ * **Batch atomicity is a hard contract requirement**: every record in an
74
+ * `append` must be persisted together under one offset, all-or-nothing.
75
+ * First-party adapters satisfy this by serializing the batch into a single
76
+ * row/document write; an adapter that splits records across non-atomic
77
+ * writes violates the contract.
78
+ *
79
+ * **Producer fencing**: `acquireProducer` bumps the producer epoch; appends
80
+ * carrying a stale epoch are rejected. The (path, producerId, epoch,
81
+ * sequence) uniqueness makes redelivered appends idempotent — a retried
82
+ * append with the same coordinates returns the already-committed offset.
83
+ */
84
+ export interface ConversationStreamStore {
85
+ /** Idempotently ensure the stream exists. */
86
+ createStream(path: string, identity: ConversationStreamIdentity): Promise<void>;
87
+ /** Claim single-writer ownership, fencing out previous producers. */
88
+ acquireProducer(path: string, producerId: string): Promise<ConversationProducerClaim>;
89
+ /** Append one atomic batch; returns its offset. */
90
+ append(input: ConversationStreamAppendInput): Promise<{
91
+ offset: string;
92
+ }>;
93
+ /** Read batches from `offset` (default: the start), up to `limit` batches. */
94
+ read(path: string, options?: {
95
+ offset?: string;
96
+ limit?: number;
97
+ }): Promise<ConversationStreamReadResult>;
98
+ getMeta(path: string): Promise<ConversationStreamMeta | null>;
99
+ delete(path: string): Promise<void>;
100
+ /** Process-local change notification; returns an unsubscribe function. */
101
+ subscribe(path: string, listener: () => void): () => void;
102
+ }
103
+ export declare class ConversationStreamStoreError extends Error {
104
+ readonly code: 'STREAM_NOT_FOUND' | 'PRODUCER_FENCED' | 'SEQUENCE_CONFLICT';
105
+ constructor(code: ConversationStreamStoreError['code'], message: string);
106
+ }
107
+ export declare const CONVERSATION_STREAM_DEFAULT_READ_LIMIT = 100;
108
+ export declare const CONVERSATION_STREAM_MAX_READ_LIMIT = 1000;
109
+ export declare function clampReadLimit(limit: number | undefined): number;
110
+ export declare function formatStreamOffset(offset: number): string;
111
+ export declare function parseStreamOffset(offset: string | undefined): number;
112
+ /** Stream path for a session's conversation projection. */
113
+ export declare function conversationStreamPath(storeSessionId: string): string;
114
+ /**
115
+ * Process-local listener registry shared by store implementations —
116
+ * registration, unsubscribe-and-prune, and error-swallowing notify.
117
+ */
118
+ export declare class StreamListenerRegistry {
119
+ private readonly listeners;
120
+ subscribe(path: string, listener: () => void): () => void;
121
+ notify(path: string): void;
122
+ }
123
+ /** In-memory conversation stream store (dev / `runtime: 'stateless'` / tests). */
124
+ export declare class InMemoryConversationStreamStore implements ConversationStreamStore {
125
+ private readonly streams;
126
+ private readonly registry;
127
+ createStream(path: string, identity: ConversationStreamIdentity): Promise<void>;
128
+ acquireProducer(path: string, producerId: string): Promise<ConversationProducerClaim>;
129
+ append(input: ConversationStreamAppendInput): Promise<{
130
+ offset: string;
131
+ }>;
132
+ read(path: string, options?: {
133
+ offset?: string;
134
+ limit?: number;
135
+ }): Promise<ConversationStreamReadResult>;
136
+ getMeta(path: string): Promise<ConversationStreamMeta | null>;
137
+ delete(path: string): Promise<void>;
138
+ subscribe(path: string, listener: () => void): () => void;
139
+ private requireStream;
140
+ }
141
+ //# sourceMappingURL=conversation-stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-stream.d.ts","sourceRoot":"","sources":["../src/conversation-stream.ts"],"names":[],"mappings":"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"}