@cello-protocol/daemon 0.0.171 → 0.0.172
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/agent-id-migration.d.ts.map +1 -1
- package/dist/agent-id-migration.js +2 -0
- package/dist/agent-id-migration.js.map +1 -1
- package/dist/close-session-handler.d.ts +7 -0
- package/dist/close-session-handler.d.ts.map +1 -1
- package/dist/close-session-handler.js +205 -76
- package/dist/close-session-handler.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +59 -0
- package/dist/daemon.js.map +1 -1
- package/dist/restart-seal-resolver.d.ts +92 -0
- package/dist/restart-seal-resolver.d.ts.map +1 -0
- package/dist/restart-seal-resolver.js +276 -0
- package/dist/restart-seal-resolver.js.map +1 -0
- package/dist/seal-certificate-pull.d.ts.map +1 -1
- package/dist/seal-certificate-pull.js +17 -2
- package/dist/seal-certificate-pull.js.map +1 -1
- package/dist/seal-coordinator.d.ts.map +1 -1
- package/dist/seal-coordinator.js +48 -2
- package/dist/seal-coordinator.js.map +1 -1
- package/dist/session-node-manager.d.ts +76 -0
- package/dist/session-node-manager.d.ts.map +1 -1
- package/dist/session-node-manager.js +245 -4
- package/dist/session-node-manager.js.map +1 -1
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +3 -3
package/dist/daemon.js
CHANGED
|
@@ -29,6 +29,7 @@ import { mkdir } from "node:fs/promises";
|
|
|
29
29
|
import { randomUUID } from "node:crypto";
|
|
30
30
|
import { dirname, join } from "node:path";
|
|
31
31
|
import { loadAgents } from "./agent-loader.js";
|
|
32
|
+
import { RestartSealResolver } from "./restart-seal-resolver.js";
|
|
32
33
|
import { acquireLock, removeLockIfOwned } from "./lock-file.js";
|
|
33
34
|
import { acquireSingletonLock } from "./singleton-lock.js";
|
|
34
35
|
import { createIpcServer } from "./ipc-server.js";
|
|
@@ -276,6 +277,8 @@ async function startDaemonHoldingLock(config, singletonLock) {
|
|
|
276
277
|
// classification (INV-TYPE-CARRY). Absent pubkey = polling disabled, all types unclassified.
|
|
277
278
|
const typeRegistry = new TypeRegistry();
|
|
278
279
|
let stopRegistryPoll;
|
|
280
|
+
// DOD-M12B-RESTART-SEAL-1: assigned as the last act of boot, stopped in stop().
|
|
281
|
+
let restartSealResolver;
|
|
279
282
|
if (config.registryPubkey && config.registryPollScheduler) {
|
|
280
283
|
const registryVersionStore = new DbRegistryVersionStore(sessionNodeManager.getDb(), logger);
|
|
281
284
|
stopRegistryPoll = startRegistryPoll({
|
|
@@ -4003,6 +4006,11 @@ async function startDaemonHoldingLock(config, singletonLock) {
|
|
|
4003
4006
|
if (config.registryPollScheduler) {
|
|
4004
4007
|
config.registryPollScheduler.cancel();
|
|
4005
4008
|
}
|
|
4009
|
+
// DOD-M12B-RESTART-SEAL-1: stop opening directory ceremonies. A seal is the most outbound thing
|
|
4010
|
+
// this daemon does, and DOD-M12B-SHUTDOWN-1's rule is that a shutdown which keeps starting new
|
|
4011
|
+
// outbound work is not draining. Above the `daemon.stopped` log with the other cancels, because
|
|
4012
|
+
// this is "stop making new work", not "tear down transports".
|
|
4013
|
+
await restartSealResolver?.stop();
|
|
4006
4014
|
logger.info("daemon.stopped", { pid: process.pid, reason });
|
|
4007
4015
|
// DOD-LOGOUT-EXIT-1: what the teardown actually DID, carried to onStopped so the binary can
|
|
4008
4016
|
// exit non-zero on a dirty stop. Without it a shutdown that threw halfway — sessions never
|
|
@@ -4101,6 +4109,57 @@ async function startDaemonHoldingLock(config, singletonLock) {
|
|
|
4101
4109
|
// M8C-TGDOOR-1: cold-capable — start the poller if a token was already configured from a
|
|
4102
4110
|
// prior run, without waiting for any agent to come online or any client to attach.
|
|
4103
4111
|
startTelegramPollerIfConfigured();
|
|
4112
|
+
// DOD-M12B-RESTART-SEAL-1: seal the sessions the LAST shutdown orphaned.
|
|
4113
|
+
//
|
|
4114
|
+
// 114 of 118 interrupted sessions on one operator's machine were flipped by our own shutdown
|
|
4115
|
+
// sweep and then sat there, because nothing has ever moved a session out of `interrupted`. Their
|
|
4116
|
+
// only exit was a force-abandon, which forfeits the notarized receipt.
|
|
4117
|
+
//
|
|
4118
|
+
// Started last, like the Telegram poller, and resolved LAZILY out of the live `handlers` map: the
|
|
4119
|
+
// close handler is registered far earlier, but reading it inside the callback is what lets a test
|
|
4120
|
+
// swap it after boot and prove this wiring exists rather than assume it.
|
|
4121
|
+
restartSealResolver = new RestartSealResolver({
|
|
4122
|
+
logger,
|
|
4123
|
+
listRestartOrphans: () => sessionNodeManager.listRestartOrphanedSessions(),
|
|
4124
|
+
...(config.restartSealInitialDelayMs !== undefined
|
|
4125
|
+
? { initialDelayMs: config.restartSealInitialDelayMs }
|
|
4126
|
+
: {}),
|
|
4127
|
+
...(config.restartSealStaggerMs !== undefined ? { staggerMs: config.restartSealStaggerMs } : {}),
|
|
4128
|
+
sealSession: async (agentName, sessionId) => {
|
|
4129
|
+
const close = handlers.get("cello_close_session");
|
|
4130
|
+
if (!close)
|
|
4131
|
+
return { ok: false, reason: "close_handler_missing" };
|
|
4132
|
+
const res = (await close({ session_id: sessionId, agent: agentName }, `restart-seal-${sessionId}`));
|
|
4133
|
+
// SUCCESS IS A RECEIPT, NOT AN `ok`. The interrupted close answers `ok: true` for a bilateral
|
|
4134
|
+
// COMMITMENT that nobody has notarized — `seal_interrupted_pending`, the status this resolver
|
|
4135
|
+
// exists to stop producing. Reading `ok` as success logged "resolved" for 137 sessions that
|
|
4136
|
+
// got no receipt at all. `sealed_root` is present only when a notarization actually happened.
|
|
4137
|
+
if (typeof res?.["sealed_root"] === "string" && res["sealed_root"].length > 0) {
|
|
4138
|
+
return { ok: true };
|
|
4139
|
+
}
|
|
4140
|
+
// Everything the close computed about WHY, carried instead of collapsed. `reason` is an exit
|
|
4141
|
+
// point — `seal_interrupted_rejected_by_counterparty` alone stands for six distinct causes,
|
|
4142
|
+
// and the close already put the discriminating detail on the response.
|
|
4143
|
+
const detail = {};
|
|
4144
|
+
for (const k of ["rejection_reason", "your_leaf_count", "their_leaf_count", "diverging_leaf_index",
|
|
4145
|
+
"seal_pending_reason", "seal_receipt", "missing_leaves", "held_messages", "status"]) {
|
|
4146
|
+
if (res?.[k] !== undefined)
|
|
4147
|
+
detail[k] = res[k];
|
|
4148
|
+
}
|
|
4149
|
+
const retry = res?.["retry_after_seconds"];
|
|
4150
|
+
return {
|
|
4151
|
+
ok: false,
|
|
4152
|
+
reason: (typeof res?.["seal_pending_reason"] === "string" ? res["seal_pending_reason"] : undefined)
|
|
4153
|
+
?? (typeof res?.["reason"] === "string" ? res["reason"] : undefined)
|
|
4154
|
+
?? "close_returned_no_receipt",
|
|
4155
|
+
...(typeof retry === "number" ? { retryAfterSeconds: retry } : {}),
|
|
4156
|
+
...(typeof res?.["guidance"] === "string" ? { guidance: res["guidance"] } : {}),
|
|
4157
|
+
...(Object.keys(detail).length > 0 ? { detail } : {}),
|
|
4158
|
+
};
|
|
4159
|
+
},
|
|
4160
|
+
markGaveUp: (agentName, sessionId, reason) => sessionNodeManager.markRestartSealGaveUp(agentName, sessionId, reason),
|
|
4161
|
+
});
|
|
4162
|
+
restartSealResolver.start();
|
|
4104
4163
|
/**
|
|
4105
4164
|
* The live handler map.
|
|
4106
4165
|
*
|