@cello-protocol/daemon 0.0.168 → 0.0.170

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 (111) hide show
  1. package/dist/agent-id-migration.d.ts.map +1 -1
  2. package/dist/agent-id-migration.js +16 -0
  3. package/dist/agent-id-migration.js.map +1 -1
  4. package/dist/away-detection.d.ts +62 -15
  5. package/dist/away-detection.d.ts.map +1 -1
  6. package/dist/away-detection.js +77 -20
  7. package/dist/away-detection.js.map +1 -1
  8. package/dist/close-session-handler.d.ts.map +1 -1
  9. package/dist/close-session-handler.js +69 -3
  10. package/dist/close-session-handler.js.map +1 -1
  11. package/dist/daemon.d.ts.map +1 -1
  12. package/dist/daemon.js +406 -303
  13. package/dist/daemon.js.map +1 -1
  14. package/dist/delivery-open-registry.d.ts +92 -0
  15. package/dist/delivery-open-registry.d.ts.map +1 -0
  16. package/dist/delivery-open-registry.js +121 -0
  17. package/dist/delivery-open-registry.js.map +1 -0
  18. package/dist/document-amendment-store.d.ts +94 -46
  19. package/dist/document-amendment-store.d.ts.map +1 -1
  20. package/dist/document-amendment-store.js +217 -102
  21. package/dist/document-amendment-store.js.map +1 -1
  22. package/dist/document-delivery-transport.d.ts +51 -53
  23. package/dist/document-delivery-transport.d.ts.map +1 -1
  24. package/dist/document-delivery-transport.js +16 -157
  25. package/dist/document-delivery-transport.js.map +1 -1
  26. package/dist/document-frame-router.d.ts +13 -17
  27. package/dist/document-frame-router.d.ts.map +1 -1
  28. package/dist/document-frame-router.js +21 -93
  29. package/dist/document-frame-router.js.map +1 -1
  30. package/dist/document-handlers.d.ts +1 -1
  31. package/dist/document-handlers.d.ts.map +1 -1
  32. package/dist/document-handlers.js +462 -511
  33. package/dist/document-handlers.js.map +1 -1
  34. package/dist/document-handshake.d.ts +4 -1
  35. package/dist/document-handshake.d.ts.map +1 -1
  36. package/dist/document-handshake.js +3 -1
  37. package/dist/document-handshake.js.map +1 -1
  38. package/dist/document-inbound.d.ts +18 -5
  39. package/dist/document-inbound.d.ts.map +1 -1
  40. package/dist/document-inbound.js +94 -110
  41. package/dist/document-inbound.js.map +1 -1
  42. package/dist/document-layer.d.ts +49 -67
  43. package/dist/document-layer.d.ts.map +1 -1
  44. package/dist/document-layer.js +688 -565
  45. package/dist/document-layer.js.map +1 -1
  46. package/dist/document-lifecycle.d.ts +11 -157
  47. package/dist/document-lifecycle.d.ts.map +1 -1
  48. package/dist/document-lifecycle.js +26 -568
  49. package/dist/document-lifecycle.js.map +1 -1
  50. package/dist/document-publish.d.ts +13 -0
  51. package/dist/document-publish.d.ts.map +1 -1
  52. package/dist/document-publish.js +19 -11
  53. package/dist/document-publish.js.map +1 -1
  54. package/dist/document-reconcile-engine.d.ts +66 -0
  55. package/dist/document-reconcile-engine.d.ts.map +1 -0
  56. package/dist/document-reconcile-engine.js +225 -0
  57. package/dist/document-reconcile-engine.js.map +1 -0
  58. package/dist/document-reconcile-scheduler.d.ts +124 -0
  59. package/dist/document-reconcile-scheduler.d.ts.map +1 -0
  60. package/dist/document-reconcile-scheduler.js +226 -0
  61. package/dist/document-reconcile-scheduler.js.map +1 -0
  62. package/dist/document-rejection.d.ts +2 -0
  63. package/dist/document-rejection.d.ts.map +1 -1
  64. package/dist/document-rejection.js +5 -1
  65. package/dist/document-rejection.js.map +1 -1
  66. package/dist/document-store.d.ts +18 -247
  67. package/dist/document-store.d.ts.map +1 -1
  68. package/dist/document-store.js +95 -827
  69. package/dist/document-store.js.map +1 -1
  70. package/dist/inbound-sessions.d.ts +7 -0
  71. package/dist/inbound-sessions.d.ts.map +1 -1
  72. package/dist/inbound-sessions.js +27 -6
  73. package/dist/inbound-sessions.js.map +1 -1
  74. package/dist/initiate-session-handler.d.ts +5 -0
  75. package/dist/initiate-session-handler.d.ts.map +1 -1
  76. package/dist/initiate-session-handler.js +1 -0
  77. package/dist/initiate-session-handler.js.map +1 -1
  78. package/dist/notification-handlers.d.ts.map +1 -1
  79. package/dist/notification-handlers.js +39 -2
  80. package/dist/notification-handlers.js.map +1 -1
  81. package/dist/session-content-handlers.d.ts.map +1 -1
  82. package/dist/session-content-handlers.js +134 -8
  83. package/dist/session-content-handlers.js.map +1 -1
  84. package/dist/session-node-manager.d.ts +153 -8
  85. package/dist/session-node-manager.d.ts.map +1 -1
  86. package/dist/session-node-manager.js +1376 -35
  87. package/dist/session-node-manager.js.map +1 -1
  88. package/dist/types.d.ts +64 -2
  89. package/dist/types.d.ts.map +1 -1
  90. package/dist/types.js.map +1 -1
  91. package/package.json +5 -5
  92. package/dist/delivery-sweep-bound.d.ts +0 -79
  93. package/dist/delivery-sweep-bound.d.ts.map +0 -1
  94. package/dist/delivery-sweep-bound.js +0 -109
  95. package/dist/delivery-sweep-bound.js.map +0 -1
  96. package/dist/document-ack-inbound.d.ts +0 -71
  97. package/dist/document-ack-inbound.d.ts.map +0 -1
  98. package/dist/document-ack-inbound.js +0 -210
  99. package/dist/document-ack-inbound.js.map +0 -1
  100. package/dist/document-control-notifier.d.ts +0 -124
  101. package/dist/document-control-notifier.d.ts.map +0 -1
  102. package/dist/document-control-notifier.js +0 -176
  103. package/dist/document-control-notifier.js.map +0 -1
  104. package/dist/document-delivery.d.ts +0 -250
  105. package/dist/document-delivery.d.ts.map +0 -1
  106. package/dist/document-delivery.js +0 -608
  107. package/dist/document-delivery.js.map +0 -1
  108. package/dist/document-join-store.d.ts +0 -80
  109. package/dist/document-join-store.d.ts.map +0 -1
  110. package/dist/document-join-store.js +0 -197
  111. package/dist/document-join-store.js.map +0 -1
@@ -1,76 +1,90 @@
1
1
  /**
2
- * DOD-MP-AMEND-1 (daemon half) — the append-only amendment store.
2
+ * SYNC-P1 (daemon half) — the fork-tolerant entry store.
3
3
  *
4
- * One row per epoch per document, holding the amendment AS RECEIVED the wire bytes, never a
5
- * re-encode. The bytes are the truth: `chain` decodes them on read, so what replay consumes is
6
- * exactly what was signed, and a codec asymmetry can never silently rewrite an agreed record
7
- * (the class of defect TRACE-1 found in the proposal codec).
4
+ * One row per ENTRY (keyed by entry hash never by an epoch slot), holding the entry AS
5
+ * RECEIVED — the wire bytes, never a re-encode. The bytes are the truth: `chain` decodes them on
6
+ * read, so what the fold consumes is exactly what was signed, and a codec asymmetry can never
7
+ * silently rewrite an agreed record (the class of defect TRACE-1 found in the proposal codec).
8
8
  *
9
- * The store owns persistence and chain SHAPE:
10
- * - **Contiguity** epoch N appends only onto N-1 rows. An out-of-order arrival is refused by
11
- * name, not buffered; amendment-lag buffering is DOD-MP-INBOUND-N-1's design, and until it
12
- * lands the honest behaviour is a loud refusal the sender retries.
13
- * - **Fork refusal** a different amendment at an occupied epoch is `document_amendment_conflict`,
14
- * first record kept. Two rivals for one epoch is a governance fork; absorbing either silently
15
- * would have two holders replay different arrangements with no error anywhere.
16
- * - **Idempotent redelivery** — the same bytes again is `recorded: false`, not an error and not a
17
- * second row.
9
+ * The store owns persistence and ANCESTRY CLOSURE:
10
+ * - **Held until whole (R14)** an entry lands in `document_entries` only when every parent has
11
+ * landed. Otherwise it waits in `document_entries_pending`: recorded, never applied, never in a
12
+ * watermark, promoted the moment its ancestry completes cascade included. The invariant this
13
+ * buys: everything in `document_entries` has its FULL ancestry there too.
14
+ * - **Fork tolerance** two entries claiming the same epoch or the same author seq are BOTH
15
+ * stored. Ruling on conflicts is the causal fold's job (`deriveDocumentState`), not the
16
+ * store's; the old chain-gap and epoch-conflict refusals are gone WITH the epoch spine.
17
+ * - **Idempotent redelivery** — the same bytes again is `recorded: false`, not an error and not
18
+ * a second row.
19
+ * - **Watermarks** — per author, the highest CONTIGUOUS seq with its head hash(es). Two heads at
20
+ * one seq is an equivocation made visible for the exchange to resolve, never silently picked.
18
21
  *
19
- * What the store does NOT judge: signatures, policy, subject semantics. `deriveArrangement`
20
- * (protocol-types) rules on those at every consumption, and the inbound path rules BEFORE
21
- * appending. A row in this table is a claim to be replayed, not an admitted fact.
22
+ * What the store does NOT judge: signatures, policy, subject semantics. The fold rules on those
23
+ * at every consumption, and the inbound path rules BEFORE appending. A row in these tables is a
24
+ * claim to be folded, not an admitted fact.
22
25
  */
23
26
  import { decodeDocumentAmendment, documentAmendmentHash, } from "@cello-protocol/protocol-types";
24
27
  /**
25
- * Shared with `DocumentStore`, which also execs this (the :353 shared-definition precedent there):
26
- * every epoch producer already holds a DocumentStore, and `currentDocumentEpoch` reads this table
27
- * through it so the table must exist whichever module constructs first. Keep the two consumers
28
- * on THIS one string.
28
+ * Drop columns a previous milestone left behind, birth-gated: only what the table actually still
29
+ * has is touched, so this is safe on a database created yesterday and on one created before the
30
+ * column existed. Dead schema is not free it is the design a future author reads and rebuilds.
29
31
  */
30
- export const DOCUMENT_AMENDMENTS_CREATE_SQL = `
31
- CREATE TABLE IF NOT EXISTS document_amendments (
32
+ export function dropLegacyColumns(db, table, columns) {
33
+ const held = new Set(db.prepare(`PRAGMA table_info(${table})`).all().map((c) => c.name));
34
+ for (const column of columns) {
35
+ if (held.has(column))
36
+ db.exec(`ALTER TABLE ${table} DROP COLUMN ${column}`);
37
+ }
38
+ }
39
+ export function dropLegacyEpochColumn(db, table) {
40
+ dropLegacyColumns(db, table, ["epoch_id"]);
41
+ }
42
+ export const DOCUMENT_ENTRIES_CREATE_SQL = `
43
+ CREATE TABLE IF NOT EXISTS document_entries (
44
+ owner_agent_id TEXT NOT NULL,
45
+ document_id TEXT NOT NULL,
46
+ entry_hash TEXT NOT NULL,
47
+ author_agent_id TEXT NOT NULL,
48
+ author_seq INTEGER NOT NULL,
49
+ received_bytes BLOB NOT NULL,
50
+ recorded_at INTEGER NOT NULL,
51
+ PRIMARY KEY (owner_agent_id, document_id, entry_hash)
52
+ );
53
+ CREATE INDEX IF NOT EXISTS idx_document_entries_author
54
+ ON document_entries (owner_agent_id, document_id, author_agent_id, author_seq);
55
+ CREATE TABLE IF NOT EXISTS document_entries_pending (
32
56
  owner_agent_id TEXT NOT NULL,
33
57
  document_id TEXT NOT NULL,
34
- epoch_id INTEGER NOT NULL,
35
- amendment_hash TEXT NOT NULL,
58
+ entry_hash TEXT NOT NULL,
59
+ author_agent_id TEXT NOT NULL,
36
60
  received_bytes BLOB NOT NULL,
37
61
  recorded_at INTEGER NOT NULL,
38
- PRIMARY KEY (owner_agent_id, document_id, epoch_id)
62
+ PRIMARY KEY (owner_agent_id, document_id, entry_hash)
39
63
  );
40
64
  `;
41
65
  /**
42
- * The last membership event naming this agent in an ordered chain ONE implementation, because
43
- * two walks (the inbound refusal's and the publish gate's) disagreeing about whether someone was
44
- * removed is two daemons disagreeing about the arrangement.
66
+ * Ceiling on HELD entries per (document, author). An honest author's pending set is a short gap
67
+ * awaiting one delivery; a hostile known author fabricating parents could otherwise grow the
68
+ * pending table without bound (review F4).
45
69
  */
46
- export function walkMembership(chain, agentId) {
47
- let state = "untouched";
48
- let epochId = null;
49
- for (const env of chain) {
50
- if (env.body.subject_agent_id !== agentId)
51
- continue;
52
- if (env.body.kind === "add_holder") {
53
- state = "holder";
54
- epochId = env.body.epoch_id;
55
- }
56
- else if (env.body.kind === "remove_holder") {
57
- state = "removed";
58
- epochId = env.body.epoch_id;
59
- }
60
- }
61
- return { state, epochId };
62
- }
70
+ export const MAX_PENDING_PER_AUTHOR = 64;
63
71
  export class DocumentAmendmentStore {
64
72
  #db;
65
73
  #logger;
66
74
  constructor(db, logger) {
67
75
  this.#db = db;
68
76
  this.#logger = logger;
69
- this.#db.exec(DOCUMENT_AMENDMENTS_CREATE_SQL);
77
+ // D7/D9 residue sweep (review F5/F6): the epoch-keyed legacy table and the withdrawals
78
+ // table have no reader or writer left — never born on a fresh database, dropped in place
79
+ // on one that predates the pivot.
80
+ this.#db.exec(`DROP TABLE IF EXISTS document_amendments`);
81
+ this.#db.exec(DOCUMENT_ENTRIES_CREATE_SQL);
82
+ dropLegacyEpochColumn(this.#db, "document_entries");
70
83
  }
71
84
  /**
72
- * Append one amendment from its wire bytes. Decodes first — malformed bytes refuse with the
73
- * decoder's named reason and nothing is stored.
85
+ * Append one entry from its wire bytes. Decodes first — malformed bytes refuse with the
86
+ * decoder's named reason and nothing is stored. An entry with missing parents is HELD; an
87
+ * arrival that completes held ancestry promotes the whole cascade.
74
88
  */
75
89
  append(ownerAgentId, documentId, receivedBytes, nowMs) {
76
90
  const env = decodeDocumentAmendment(receivedBytes);
@@ -78,74 +92,175 @@ export class DocumentAmendmentStore {
78
92
  throw new Error(`document_amendment_wrong_document: the bytes name ${env.body.document_id}, appending ` +
79
93
  `under ${documentId}`);
80
94
  }
81
- const epochId = env.body.epoch_id;
82
95
  const hash = Buffer.from(documentAmendmentHash(env.body)).toString("hex");
83
- const existing = this.#db
84
- .prepare(`SELECT amendment_hash FROM document_amendments
85
- WHERE owner_agent_id = ? AND document_id = ? AND epoch_id = ?`)
86
- .get(ownerAgentId, documentId, epochId);
87
- if (existing) {
88
- if (existing.amendment_hash === hash) {
89
- return { recorded: false, epochId, amendmentHash: hash };
96
+ if (this.#hasEntry(ownerAgentId, documentId, hash)) {
97
+ return { recorded: false, held: false, entryHash: hash, promoted: [] };
98
+ }
99
+ if (this.#hasPending(ownerAgentId, documentId, hash)) {
100
+ return { recorded: false, held: true, entryHash: hash, promoted: [] };
101
+ }
102
+ const missing = env.body.parents.filter((p) => !this.#hasEntry(ownerAgentId, documentId, p));
103
+ if (missing.length > 0) {
104
+ const author = env.body.author_agent_id;
105
+ const backlog = this.#db
106
+ .prepare(`SELECT COUNT(*) AS n FROM document_entries_pending
107
+ WHERE owner_agent_id = ? AND document_id = ? AND author_agent_id = ?`)
108
+ .get(ownerAgentId, documentId, author);
109
+ if (backlog.n >= MAX_PENDING_PER_AUTHOR) {
110
+ throw new Error(`document_pending_cap: ${author} already has ${backlog.n} entries held awaiting ` +
111
+ `parents — the ceiling is ${MAX_PENDING_PER_AUTHOR}, and an honest gap is never ` +
112
+ `this deep; deliver the missing ancestry first`);
90
113
  }
91
- this.#logger.error("document.amendment.conflict", {
114
+ this.#db
115
+ .prepare(`INSERT INTO document_entries_pending
116
+ (owner_agent_id, document_id, entry_hash, author_agent_id, received_bytes, recorded_at)
117
+ VALUES (?, ?, ?, ?, ?, ?)`)
118
+ .run(ownerAgentId, documentId, hash, author, Buffer.from(receivedBytes), nowMs);
119
+ this.#logger.info("document.entry.held", {
92
120
  documentId,
93
- epochId,
94
- storedHash: existing.amendment_hash,
95
- rivalHash: hash,
121
+ entryHash: hash,
122
+ author: env.body.author_agent_id,
123
+ authorSeq: env.body.author_seq,
124
+ kind: env.body.kind,
125
+ missingParents: missing,
96
126
  });
97
- throw new Error(`document_amendment_conflict: epoch ${epochId} of ${documentId} already holds ` +
98
- `${existing.amendment_hash} and a rival amendment ${hash} arrived — a governance fork, ` +
99
- `refused with the first record kept`);
100
- }
101
- const prior = this.currentEpoch(ownerAgentId, documentId);
102
- if (epochId !== prior + 1) {
103
- throw new Error(`document_amendment_chain_gap: epoch ${epochId} cannot append onto epoch ${prior} — ` +
104
- `amendments land contiguously, and an out-of-order arrival is retried by its sender, ` +
105
- `never buffered silently`);
127
+ return { recorded: true, held: true, entryHash: hash, promoted: [] };
106
128
  }
107
- this.#db
108
- .prepare(`INSERT INTO document_amendments
109
- (owner_agent_id, document_id, epoch_id, amendment_hash, received_bytes, recorded_at)
110
- VALUES (?, ?, ?, ?, ?, ?)`)
111
- .run(ownerAgentId, documentId, epochId, hash,
112
- // Copied — the caller may reuse or zero its buffer after we return.
113
- Buffer.from(receivedBytes), nowMs);
114
- this.#logger.info("document.amendment.recorded", {
115
- documentId,
116
- epochId,
117
- amendmentHash: hash,
118
- kind: env.body.kind,
119
- });
120
- return { recorded: true, epochId, amendmentHash: hash };
129
+ this.#insertEntry(ownerAgentId, documentId, hash, env, receivedBytes, nowMs);
130
+ const promoted = this.#promoteReady(ownerAgentId, documentId, nowMs);
131
+ return { recorded: true, held: false, entryHash: hash, promoted };
121
132
  }
122
- /** The ordered chain, decoded from the stored received bytes. */
133
+ /** Every applied entry, decoded from the stored received bytes. The set is ancestry-closed. */
123
134
  chain(ownerAgentId, documentId) {
124
135
  const rows = this.#db
125
- .prepare(`SELECT received_bytes FROM document_amendments
136
+ .prepare(`SELECT received_bytes FROM document_entries
126
137
  WHERE owner_agent_id = ? AND document_id = ?
127
- ORDER BY epoch_id ASC`)
138
+ ORDER BY author_seq ASC, entry_hash ASC`)
128
139
  .all(ownerAgentId, documentId);
129
140
  return rows.map((r) => decodeDocumentAmendment(new Uint8Array(r.received_bytes)));
130
141
  }
142
+ /** Held entries and exactly which parents they still wait on (R37's receiver-side record). */
143
+ pending(ownerAgentId, documentId) {
144
+ const rows = this.#db
145
+ .prepare(`SELECT entry_hash, received_bytes, recorded_at FROM document_entries_pending
146
+ WHERE owner_agent_id = ? AND document_id = ?
147
+ ORDER BY recorded_at ASC, entry_hash ASC`)
148
+ .all(ownerAgentId, documentId);
149
+ return rows.map((r) => {
150
+ const env = decodeDocumentAmendment(new Uint8Array(r.received_bytes));
151
+ return {
152
+ entryHash: r.entry_hash,
153
+ missingParents: env.body.parents.filter((p) => !this.#hasEntry(ownerAgentId, documentId, p)),
154
+ recordedAtMs: r.recorded_at,
155
+ };
156
+ });
157
+ }
158
+ /**
159
+ * This author's applied entries with seq strictly beyond `afterSeq`, as WIRE BYTES in seq
160
+ * order — what a reconcile reply carries to a peer whose watermark for this author is behind
161
+ * ours (R10 step 2/3). Forked seqs both ship: the peer's fold rules on them like ours did.
162
+ */
163
+ entriesByAuthorAfter(ownerAgentId, documentId, authorAgentId, afterSeq) {
164
+ const rows = this.#db
165
+ .prepare(`SELECT received_bytes FROM document_entries
166
+ WHERE owner_agent_id = ? AND document_id = ? AND author_agent_id = ? AND author_seq > ?
167
+ ORDER BY author_seq ASC, entry_hash ASC`)
168
+ .all(ownerAgentId, documentId, authorAgentId, afterSeq);
169
+ return rows.map((r) => new Uint8Array(r.received_bytes));
170
+ }
131
171
  /**
132
- * DOD-MP-REMOVE-1 this agent's LAST membership event in the recorded chain.
133
- * "holder" = admitted (or never touched by any membership amendment genesis membership is
134
- * the CALLER's fact, not this table's); "removed" = the last event naming them was
135
- * remove_holder, with the epoch it happened at, so a refusal can name the removal rather than
136
- * a generic condition. Reads the recorded chain only — validity was ruled before append
137
- * (the standing invariant).
172
+ * Per-author position: the highest CONTIGUOUS seq held and the head hash(es) at it. A held
173
+ * (pending) entry is not counted a gap ends the walk (R13: report contiguous, never highest
174
+ * received).
138
175
  */
139
- membershipOf(ownerAgentId, documentId, agentId) {
140
- return walkMembership(this.chain(ownerAgentId, documentId), agentId);
176
+ watermarks(ownerAgentId, documentId) {
177
+ const rows = this.#db
178
+ .prepare(`SELECT author_agent_id, author_seq, entry_hash FROM document_entries
179
+ WHERE owner_agent_id = ? AND document_id = ?
180
+ ORDER BY author_agent_id ASC, author_seq ASC, entry_hash ASC`)
181
+ .all(ownerAgentId, documentId);
182
+ const byAuthor = new Map();
183
+ for (const r of rows) {
184
+ let seqs = byAuthor.get(r.author_agent_id);
185
+ if (!seqs) {
186
+ seqs = new Map();
187
+ byAuthor.set(r.author_agent_id, seqs);
188
+ }
189
+ const at = seqs.get(r.author_seq);
190
+ if (at)
191
+ at.push(r.entry_hash);
192
+ else
193
+ seqs.set(r.author_seq, [r.entry_hash]);
194
+ }
195
+ const out = new Map();
196
+ for (const [author, seqs] of byAuthor) {
197
+ let seq = 0;
198
+ while (seqs.has(seq + 1))
199
+ seq++;
200
+ if (seq === 0)
201
+ continue;
202
+ out.set(author, { seq, headHashes: seqs.get(seq) });
203
+ }
204
+ return out;
205
+ }
206
+ #hasEntry(ownerAgentId, documentId, hash) {
207
+ return (this.#db
208
+ .prepare(`SELECT 1 FROM document_entries
209
+ WHERE owner_agent_id = ? AND document_id = ? AND entry_hash = ?`)
210
+ .get(ownerAgentId, documentId, hash) !== undefined);
211
+ }
212
+ #hasPending(ownerAgentId, documentId, hash) {
213
+ return (this.#db
214
+ .prepare(`SELECT 1 FROM document_entries_pending
215
+ WHERE owner_agent_id = ? AND document_id = ? AND entry_hash = ?`)
216
+ .get(ownerAgentId, documentId, hash) !== undefined);
141
217
  }
142
- /** The highest recorded epoch, or 0 — genesis — when no amendment exists. */
143
- currentEpoch(ownerAgentId, documentId) {
144
- const r = this.#db
145
- .prepare(`SELECT MAX(epoch_id) AS max_epoch FROM document_amendments
146
- WHERE owner_agent_id = ? AND document_id = ?`)
147
- .get(ownerAgentId, documentId);
148
- return r?.max_epoch ?? 0;
218
+ #insertEntry(ownerAgentId, documentId, hash, env, bytes, nowMs) {
219
+ this.#db
220
+ .prepare(`INSERT INTO document_entries
221
+ (owner_agent_id, document_id, entry_hash, author_agent_id, author_seq,
222
+ received_bytes, recorded_at)
223
+ VALUES (?, ?, ?, ?, ?, ?, ?)`)
224
+ .run(ownerAgentId, documentId, hash, env.body.author_agent_id, env.body.author_seq,
225
+ // Copied — the caller may reuse or zero its buffer after we return.
226
+ Buffer.from(bytes), nowMs);
227
+ this.#logger.info("document.entry.recorded", {
228
+ documentId,
229
+ entryHash: hash,
230
+ author: env.body.author_agent_id,
231
+ authorSeq: env.body.author_seq,
232
+ kind: env.body.kind,
233
+ });
234
+ }
235
+ /** Move every pending entry whose ancestry is now complete — repeated until a pass moves none. */
236
+ #promoteReady(ownerAgentId, documentId, nowMs) {
237
+ const promoted = [];
238
+ for (;;) {
239
+ const rows = this.#db
240
+ .prepare(`SELECT entry_hash, received_bytes FROM document_entries_pending
241
+ WHERE owner_agent_id = ? AND document_id = ?`)
242
+ .all(ownerAgentId, documentId);
243
+ let movedThisPass = false;
244
+ for (const row of rows) {
245
+ const env = decodeDocumentAmendment(new Uint8Array(row.received_bytes));
246
+ const ready = env.body.parents.every((p) => this.#hasEntry(ownerAgentId, documentId, p));
247
+ if (!ready)
248
+ continue;
249
+ this.#insertEntry(ownerAgentId, documentId, row.entry_hash, env, new Uint8Array(row.received_bytes), nowMs);
250
+ this.#db
251
+ .prepare(`DELETE FROM document_entries_pending
252
+ WHERE owner_agent_id = ? AND document_id = ? AND entry_hash = ?`)
253
+ .run(ownerAgentId, documentId, row.entry_hash);
254
+ this.#logger.info("document.entry.promoted", {
255
+ documentId,
256
+ entryHash: row.entry_hash,
257
+ });
258
+ promoted.push({ entryHash: row.entry_hash, envelope: env });
259
+ movedThisPass = true;
260
+ }
261
+ if (!movedThisPass)
262
+ return promoted;
263
+ }
149
264
  }
150
265
  }
151
266
  //# sourceMappingURL=document-amendment-store.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"document-amendment-store.js","sourceRoot":"","sources":["../src/document-amendment-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"document-amendment-store.js","sourceRoot":"","sources":["../src/document-amendment-store.ts"],"names":[],"mappings":"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"}
@@ -12,12 +12,8 @@
12
12
  * because opening is the expensive half — a directory negotiation, a dial, and a seal — and a
13
13
  * backlog of pending envelopes for one peer would otherwise pay it per envelope. It is also the
14
14
  * behaviour an operator expects when they are mid-conversation about the document: the change lands
15
- * in the same sealed record as the discussion, without anyone passing a session hint.
16
- *
17
- * An explicit `sessionHint` overrides the choice but NOT the validation — a hint naming a session
18
- * that is not active with this peer is refused rather than silently replaced by the daemon's own
19
- * pick, because the one reason to pass a hint is to control which sealed record the change lands
20
- * in, and quietly choosing a different one defeats exactly that.
15
+ * in the same sealed record as the discussion. (SYNC-D10: the explicit session hint is deleted —
16
+ * the daemon's reuse-most-recent choice is the whole rule.)
21
17
  *
22
18
  * ── WHAT AN ACK IS HERE, AND WHAT IT IS NOT ───────────────────────────────────────────────────
23
19
  *
@@ -33,8 +29,6 @@
33
29
  * permanent-redelivery shape this milestone has already fixed once. The worker records the envelope
34
30
  * as delivered, leaves it unacked, and asks again on the capped backoff.
35
31
  */
36
- import type { DocumentDeliveryTransport } from "./document-delivery.js";
37
- import type { DocumentEnvelopeRow } from "./document-store.js";
38
32
  import { DiscoveryUnavailableError } from "./document-reachability.js";
39
33
  import type { DiscoveryOutcome } from "./cross-node-negotiation.js";
40
34
  import type { Logger } from "./types.js";
@@ -46,7 +40,13 @@ export interface DocumentTransportDeps {
46
40
  * audit record: the tree is the sequence space both sides count in, so a sender that skips it
47
41
  * falls one behind per frame and the peer silently drops what it has already consumed.
48
42
  */
49
- appendLeaf(agentName: string, sessionId: string, contentHash: Uint8Array, correlationId: string): void;
43
+ /** DOD-M12B-INDEX-1: `assignedSeq` is the relay's position for this frame — the leaf goes THERE,
44
+ * not at the tail. A document leaf rides the same session sequence counter a message does, so it
45
+ * is subject to exactly the same divergence. Undefined when no relay answered. */
46
+ appendLeaf(agentName: string, sessionId: string, contentHash: Uint8Array, frameBytes: Uint8Array, correlationId: string, assignedSeq?: number): {
47
+ placed: boolean;
48
+ leafIndex: number | null;
49
+ };
50
50
  /** The agent this worker delivers for. One worker per attended agent. */
51
51
  agentName: string;
52
52
  /** `runDiscoveryLookup`, supplied by the composition root — it lives in a closure there. */
@@ -76,62 +76,21 @@ export interface DocumentTransportDeps {
76
76
  ok: true;
77
77
  delivered: true;
78
78
  relayRefusal?: string;
79
+ sequenceNumber?: number;
79
80
  } | {
80
81
  ok: true;
81
82
  delivered: false;
82
83
  parked: true;
83
84
  relayRefusal?: string;
85
+ sequenceNumber?: number;
84
86
  } | {
85
87
  ok: false;
86
88
  reason: string;
87
89
  error: string;
90
+ sequenceNumber?: number;
88
91
  }>;
89
- /** Encode a document envelope row onto the wire, and its content hash. */
90
- encodeEnvelope(envelope: DocumentEnvelopeRow): {
91
- bytes: Uint8Array;
92
- hash: Uint8Array;
93
- };
94
- /**
95
- * Wait for the peer to ANSWER this envelope, bounded. Resolves true if the ack landed.
96
- *
97
- * The seal below tears the session down, and the teardown drops any content still held for
98
- * ordering — so an ack that arrives correctly and is held for a gap is DELETED rather than
99
- * applied. Measured on a live daemon: the ack was sent, logged `session.content.held`, and the
100
- * session was destroyed three seconds later with it still buffered. The sender then re-sent the
101
- * envelope every tick until the unacked ceiling retired it.
102
- *
103
- * Waiting here is deliberately indifferent to WHY the answer is slow — held for ordering, a slow
104
- * relay, a busy peer. The rule is simply that we do not tear down the channel an answer is due
105
- * on until it has arrived or we have given up on it.
106
- */
107
- /** Waits for THE DIALED HOLDER'S answer — any other holder's ack must not resolve it (H1). */
108
- awaitAck(envelopeHash: string, expectedAckerAgentId: string, timeoutMs: number): Promise<{
109
- admitted: boolean;
110
- } | null>;
111
- /**
112
- * Try to fill this session's ordering gaps, so anything HELD can be released.
113
- *
114
- * Without it the grace above cannot help in the very case it was written for. An ack whose
115
- * canonical sequence lands ahead of our tree is HELD, and held content is not routed to the
116
- * document layer at all — it waits for `#releaseHeld`, which only runs when the missing
117
- * in-between sequence arrives. So the ack sits in a buffer, nothing settles, the grace expires in
118
- * full, and the seal then discards it. The wait alone converted a fast silent failure into a slow
119
- * one.
120
- *
121
- * A no-op when the session has no gap, so the ordinary delivery pays nothing.
122
- */
123
- drainHeld(sessionId: string, correlationId: string): Promise<void>;
124
92
  logger: Logger;
125
93
  }
126
- /**
127
- * How long a session WE opened stays up waiting for the peer's answer.
128
- *
129
- * Sized against the observed round trip, not guessed: on a live cross-region session the ack was
130
- * on the wire ~375ms after the frame landed, and the seal ceremony itself takes ~3s. Ten seconds
131
- * covers a slow relay and a park-and-recover round without keeping an autonomous session alive
132
- * long enough to be mistaken for a conversation.
133
- */
134
- export declare const DELIVERY_ACK_GRACE_MS = 10000;
135
94
  /**
136
95
  * Everything this transport sends is document-domain traffic, so 0x04 is the default rather than
137
96
  * something each call site remembers. A refusal overrides it with 0x05 — the two are distinguished
@@ -145,6 +104,45 @@ export declare const DELIVERY_ACK_GRACE_MS = 10000;
145
104
  * this path submitted every document leaf as a MESSAGE.
146
105
  */
147
106
  export declare const DOCUMENT_LEAF_KIND = 4;
107
+ /**
108
+ * The document frame carrier (SYNC-P4: D1/D2/D4 deleted — this used to live in the delivery
109
+ * worker's module). Two capabilities survive: a reachability probe, and putting already-encoded
110
+ * bytes on a session the transport opens or reuses then seals. There is no `deliver` and no ack
111
+ * wait — content confirmation is the peer's position at the next reconcile exchange.
112
+ */
113
+ export interface DocumentDeliveryTransport {
114
+ /**
115
+ * Is the peer reachable right now? `discovery_lookup` today. Returns false rather than throwing
116
+ * for an ordinary "not online"; a throw means the LOOKUP failed, which is a different fact and
117
+ * must not be recorded as the peer being away.
118
+ */
119
+ isPeerReachable(peerAgentId: string, correlationId: string): Promise<{
120
+ reachable: boolean;
121
+ unknownAgent: boolean;
122
+ }>;
123
+ /**
124
+ * Send already-encoded bytes to a peer over the open-or-reuse-then-seal path. `documentId` is
125
+ * for the log line only. `parked: true` means the relay took it because the peer had no live
126
+ * counterparty — it is NOT with them yet, and only the next exchange proves anything landed.
127
+ */
128
+ sendBytes(input: {
129
+ peerAgentId: string;
130
+ documentId: string;
131
+ bytes: Uint8Array;
132
+ correlationId: string;
133
+ /** The witnessed leaf DOMAIN. Defaults to the document kind (0x04); a refusal passes 0x05. */
134
+ leafKind?: number;
135
+ }): Promise<{
136
+ ok: true;
137
+ sessionId: string;
138
+ sessionOpened: boolean;
139
+ parked?: boolean;
140
+ } | {
141
+ ok: false;
142
+ reason: string;
143
+ detail?: string;
144
+ }>;
145
+ }
148
146
  export declare function createDocumentDeliveryTransport(deps: DocumentTransportDeps): DocumentDeliveryTransport;
149
147
  export { DiscoveryUnavailableError };
150
148
  //# sourceMappingURL=document-delivery-transport.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"document-delivery-transport.d.ts","sourceRoot":"","sources":["../src/document-delivery-transport.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"document-delivery-transport.d.ts","sourceRoot":"","sources":["../src/document-delivery-transport.ts"],"names":[],"mappings":"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"}