@cello-protocol/daemon 0.0.138 → 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.
- package/dist/column-birth.d.ts +40 -0
- package/dist/column-birth.d.ts.map +1 -0
- package/dist/column-birth.js +60 -0
- package/dist/column-birth.js.map +1 -0
- package/dist/daemon.js +35 -2
- package/dist/daemon.js.map +1 -1
- package/dist/document-ack-inbound.d.ts +1 -1
- package/dist/document-ack-inbound.d.ts.map +1 -1
- package/dist/document-ack-inbound.js +1 -1
- package/dist/document-ack-inbound.js.map +1 -1
- package/dist/document-delivery-transport.d.ts +16 -1
- package/dist/document-delivery-transport.d.ts.map +1 -1
- package/dist/document-delivery-transport.js +45 -5
- package/dist/document-delivery-transport.js.map +1 -1
- package/dist/document-delivery.d.ts +32 -0
- package/dist/document-delivery.d.ts.map +1 -1
- package/dist/document-delivery.js +52 -6
- package/dist/document-delivery.js.map +1 -1
- package/dist/document-handlers.d.ts.map +1 -1
- package/dist/document-handlers.js +171 -15
- package/dist/document-handlers.js.map +1 -1
- package/dist/document-handshake.d.ts +4 -0
- package/dist/document-handshake.d.ts.map +1 -1
- package/dist/document-handshake.js +40 -13
- package/dist/document-handshake.js.map +1 -1
- package/dist/document-layer.d.ts +3 -1
- package/dist/document-layer.d.ts.map +1 -1
- package/dist/document-layer.js +11 -7
- package/dist/document-layer.js.map +1 -1
- package/dist/document-lifecycle.d.ts +10 -0
- package/dist/document-lifecycle.d.ts.map +1 -1
- package/dist/document-lifecycle.js +23 -2
- package/dist/document-lifecycle.js.map +1 -1
- package/dist/document-screen.d.ts +16 -0
- package/dist/document-screen.d.ts.map +1 -1
- package/dist/document-screen.js +40 -20
- package/dist/document-screen.js.map +1 -1
- package/dist/document-store.d.ts +20 -0
- package/dist/document-store.d.ts.map +1 -1
- package/dist/document-store.js +36 -9
- package/dist/document-store.js.map +1 -1
- package/dist/session-node-manager.d.ts.map +1 -1
- package/dist/session-node-manager.js +11 -1
- package/dist/session-node-manager.js.map +1 -1
- package/package.json +3 -3
|
@@ -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
|
@@ -3338,10 +3338,43 @@ async function startDaemonHoldingLock(config, singletonLock) {
|
|
|
3338
3338
|
// returns no rows and the wait times out on every delivery.
|
|
3339
3339
|
awaitAck: async (envelopeHash, timeoutMs) => {
|
|
3340
3340
|
const ownerKey = documentOwnerKeyFor(agentName);
|
|
3341
|
-
if (ownerKey === null)
|
|
3342
|
-
|
|
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
|
+
}
|
|
3343
3351
|
return documentLayer.awaitAck(ownerKey, envelopeHash, timeoutMs);
|
|
3344
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
|
+
},
|
|
3345
3378
|
});
|
|
3346
3379
|
documentTransports.set(agentName, transport);
|
|
3347
3380
|
return transport;
|