@cello-protocol/daemon 0.0.137 → 0.0.139

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 (48) hide show
  1. package/dist/column-birth.d.ts +40 -0
  2. package/dist/column-birth.d.ts.map +1 -0
  3. package/dist/column-birth.js +60 -0
  4. package/dist/column-birth.js.map +1 -0
  5. package/dist/daemon.js +42 -0
  6. package/dist/daemon.js.map +1 -1
  7. package/dist/document-ack-inbound.d.ts +7 -0
  8. package/dist/document-ack-inbound.d.ts.map +1 -1
  9. package/dist/document-ack-inbound.js +3 -0
  10. package/dist/document-ack-inbound.js.map +1 -1
  11. package/dist/document-delivery-transport.d.ts +38 -0
  12. package/dist/document-delivery-transport.d.ts.map +1 -1
  13. package/dist/document-delivery-transport.js +72 -1
  14. package/dist/document-delivery-transport.js.map +1 -1
  15. package/dist/document-delivery.d.ts +32 -0
  16. package/dist/document-delivery.d.ts.map +1 -1
  17. package/dist/document-delivery.js +52 -6
  18. package/dist/document-delivery.js.map +1 -1
  19. package/dist/document-handlers.d.ts.map +1 -1
  20. package/dist/document-handlers.js +171 -15
  21. package/dist/document-handlers.js.map +1 -1
  22. package/dist/document-handshake.d.ts +4 -0
  23. package/dist/document-handshake.d.ts.map +1 -1
  24. package/dist/document-handshake.js +40 -13
  25. package/dist/document-handshake.js.map +1 -1
  26. package/dist/document-layer.d.ts +15 -0
  27. package/dist/document-layer.d.ts.map +1 -1
  28. package/dist/document-layer.js +53 -1
  29. package/dist/document-layer.js.map +1 -1
  30. package/dist/document-lifecycle.d.ts +10 -0
  31. package/dist/document-lifecycle.d.ts.map +1 -1
  32. package/dist/document-lifecycle.js +23 -2
  33. package/dist/document-lifecycle.js.map +1 -1
  34. package/dist/document-screen.d.ts +16 -0
  35. package/dist/document-screen.d.ts.map +1 -1
  36. package/dist/document-screen.js +40 -20
  37. package/dist/document-screen.js.map +1 -1
  38. package/dist/document-store.d.ts +29 -0
  39. package/dist/document-store.d.ts.map +1 -1
  40. package/dist/document-store.js +49 -7
  41. package/dist/document-store.js.map +1 -1
  42. package/dist/session-content-handlers.d.ts.map +1 -1
  43. package/dist/session-content-handlers.js +30 -2
  44. package/dist/session-content-handlers.js.map +1 -1
  45. package/dist/session-node-manager.d.ts.map +1 -1
  46. package/dist/session-node-manager.js +33 -0
  47. package/dist/session-node-manager.js.map +1 -1
  48. package/package.json +4 -4
@@ -0,0 +1,40 @@
1
+ /**
2
+ * COLUMN BIRTH — adding a column to a table an operator already holds data in.
3
+ *
4
+ * `ALTER TABLE … ADD COLUMN` throws when the column is already there, and that throw is the guard:
5
+ * it is how a birth-gated migration stays idempotent without a version table nobody maintains. The
6
+ * pattern is right. The way it was written twice in this codebase was not:
7
+ *
8
+ * try { db.exec(sql); } catch { }
9
+ *
10
+ * A bare catch cannot tell "already present" — the expected, benign case, which happens on every
11
+ * start after the first — from a schema that is actually broken. So a real failure produced no log
12
+ * line, no throw, and no symptom at the point of failure. The only evidence would be every later
13
+ * query on that column failing, in a different subsystem, with a message that names neither the
14
+ * migration nor the table.
15
+ *
16
+ * That matters most exactly where it is least observable: the FIRST run on a new machine. A second
17
+ * operator, or a fresh database, is where a migration executes for real — and it was the one place
18
+ * the code was guaranteed to say nothing.
19
+ *
20
+ * So: match the benign case by its message and return; anything else is logged under its own event
21
+ * and rethrown. Rethrowing is the deliberate half. If the column cannot be added, every query that
22
+ * reads it fails anyway — the choice is not between working and failing, it is between failing at
23
+ * startup with the cause named and failing later somewhere else with the cause lost.
24
+ */
25
+ import type { Logger } from "./types.js";
26
+ /**
27
+ * Run one `ADD COLUMN`, treating "already present" as success and everything else as a fault.
28
+ *
29
+ * `table` and `column` are for the log line only — the SQL is the authority. They are separate
30
+ * arguments rather than parsed out of it because a regex over SQL is a second, worse parser, and
31
+ * the caller already knows both.
32
+ */
33
+ export declare function addColumnIfMissing(db: {
34
+ exec(sql: string): void;
35
+ }, logger: Logger, input: {
36
+ table: string;
37
+ column: string;
38
+ sql: string;
39
+ }): void;
40
+ //# sourceMappingURL=column-birth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"column-birth.d.ts","sourceRoot":"","sources":["../src/column-birth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAYzC;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,EAAE,EAAE;IAAE,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EAC/B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACpD,IAAI,CAgBN"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * COLUMN BIRTH — adding a column to a table an operator already holds data in.
3
+ *
4
+ * `ALTER TABLE … ADD COLUMN` throws when the column is already there, and that throw is the guard:
5
+ * it is how a birth-gated migration stays idempotent without a version table nobody maintains. The
6
+ * pattern is right. The way it was written twice in this codebase was not:
7
+ *
8
+ * try { db.exec(sql); } catch { }
9
+ *
10
+ * A bare catch cannot tell "already present" — the expected, benign case, which happens on every
11
+ * start after the first — from a schema that is actually broken. So a real failure produced no log
12
+ * line, no throw, and no symptom at the point of failure. The only evidence would be every later
13
+ * query on that column failing, in a different subsystem, with a message that names neither the
14
+ * migration nor the table.
15
+ *
16
+ * That matters most exactly where it is least observable: the FIRST run on a new machine. A second
17
+ * operator, or a fresh database, is where a migration executes for real — and it was the one place
18
+ * the code was guaranteed to say nothing.
19
+ *
20
+ * So: match the benign case by its message and return; anything else is logged under its own event
21
+ * and rethrown. Rethrowing is the deliberate half. If the column cannot be added, every query that
22
+ * reads it fails anyway — the choice is not between working and failing, it is between failing at
23
+ * startup with the cause named and failing later somewhere else with the cause lost.
24
+ */
25
+ /**
26
+ * SQLite's wording for "this column is already here", which is the whole benign case.
27
+ *
28
+ * Matched on the MESSAGE rather than a code because the driver surfaces a plain `Error`. Anchored
29
+ * to the phrase rather than the column name so one expression covers every call site — and
30
+ * deliberately NOT a loose `/duplicate/`, which would also swallow a duplicate-table or
31
+ * duplicate-index failure that has nothing to do with this column.
32
+ */
33
+ const ALREADY_PRESENT = /duplicate column name/i;
34
+ /**
35
+ * Run one `ADD COLUMN`, treating "already present" as success and everything else as a fault.
36
+ *
37
+ * `table` and `column` are for the log line only — the SQL is the authority. They are separate
38
+ * arguments rather than parsed out of it because a regex over SQL is a second, worse parser, and
39
+ * the caller already knows both.
40
+ */
41
+ export function addColumnIfMissing(db, logger, input) {
42
+ try {
43
+ db.exec(input.sql);
44
+ }
45
+ catch (err) {
46
+ const message = err instanceof Error ? err.message : String(err);
47
+ if (ALREADY_PRESENT.test(message))
48
+ return;
49
+ // NAMED, at error, before the rethrow — so the cause is in the log even if something upstream
50
+ // catches the throw and reports it as a generic startup failure.
51
+ logger.error("db.column_birth.failed", {
52
+ table: input.table,
53
+ column: input.column,
54
+ sql: input.sql,
55
+ error: message,
56
+ });
57
+ throw err;
58
+ }
59
+ }
60
+ //# sourceMappingURL=column-birth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"column-birth.js","sourceRoot":"","sources":["../src/column-birth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH;;;;;;;GAOG;AACH,MAAM,eAAe,GAAG,wBAAwB,CAAC;AAEjD;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAChC,EAA+B,EAC/B,MAAc,EACd,KAAqD;IAErD,IAAI,CAAC;QACH,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,IAAI,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,OAAO;QAC1C,8FAA8F;QAC9F,iEAAiE;QACjE,MAAM,CAAC,KAAK,CAAC,wBAAwB,EAAE;YACrC,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,KAAK,EAAE,OAAO;SACf,CAAC,CAAC;QACH,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
package/dist/daemon.js CHANGED
@@ -3333,6 +3333,48 @@ async function startDaemonHoldingLock(config, singletonLock) {
3333
3333
  // `0x00` domain prefix, so a bare hash is discarded at the authenticity check.
3334
3334
  return { bytes, hash: wireContentHash(bytes) };
3335
3335
  },
3336
+ // The owner KEY, not the agent name — every document row is scoped by our own pubkey hex
3337
+ // (M14-D5), and the store query behind this reads that column. Passing the name here
3338
+ // returns no rows and the wait times out on every delivery.
3339
+ awaitAck: async (envelopeHash, timeoutMs) => {
3340
+ const ownerKey = documentOwnerKeyFor(agentName);
3341
+ if (ownerKey === null) {
3342
+ // NOT "the peer stayed silent". `false` means the grace expired with no answer, and the
3343
+ // caller logs exactly that — `graceMs: 10000`, a claim that ten seconds elapsed when
3344
+ // none did, about a peer that was never asked. A delivery worker running for an agent
3345
+ // whose own pubkey cannot be resolved is a wiring bug, and it says so here rather than
3346
+ // arriving downstream dressed as a fact about the counterparty. Same distinction
3347
+ // `abandoned_at` exists to keep: giving up is our decision, not evidence about them.
3348
+ logger.error("document.delivery.ack_wait_unwired", { agentName, envelopeHash });
3349
+ return null;
3350
+ }
3351
+ return documentLayer.awaitAck(ownerKey, envelopeHash, timeoutMs);
3352
+ },
3353
+ drainHeld: async (sessionId, correlationId) => {
3354
+ // ONLY WHEN THERE IS A GAP. `sealReadiness` already answers exactly this question, and
3355
+ // gating on it keeps the ordinary delivery — the overwhelming majority — free of a relay
3356
+ // round trip it does not need.
3357
+ const readiness = sessionNodeManager.sealReadiness(agentName, sessionId);
3358
+ if (readiness.heldCount === 0 && readiness.missingLeaves === 0)
3359
+ return;
3360
+ logger.info("document.delivery.drain_held", {
3361
+ agentName,
3362
+ sessionId,
3363
+ heldCount: readiness.heldCount,
3364
+ missingLeaves: readiness.missingLeaves,
3365
+ correlationId,
3366
+ });
3367
+ await autoRecoverForAgent(agentName, "delivery_ack_grace").catch((err) => {
3368
+ // CONTAINED. A recovery that fails leaves the ack held and the grace expires — the same
3369
+ // outcome as before, and never a reason to fail a delivery whose content already left.
3370
+ logger.warn("document.delivery.drain_held_failed", {
3371
+ agentName,
3372
+ sessionId,
3373
+ correlationId,
3374
+ error: err instanceof Error ? err.message : String(err),
3375
+ });
3376
+ });
3377
+ },
3336
3378
  });
3337
3379
  documentTransports.set(agentName, transport);
3338
3380
  return transport;