@cello-protocol/daemon 0.0.52 → 0.0.54
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-handlers.d.ts +57 -0
- package/dist/agent-handlers.d.ts.map +1 -0
- package/dist/agent-handlers.js +360 -0
- package/dist/agent-handlers.js.map +1 -0
- package/dist/agent-id-migration.d.ts.map +1 -1
- package/dist/agent-id-migration.js +1 -0
- package/dist/agent-id-migration.js.map +1 -1
- package/dist/agent-loader.d.ts +3 -4
- package/dist/agent-loader.d.ts.map +1 -1
- package/dist/agent-loader.js +3 -4
- package/dist/agent-loader.js.map +1 -1
- package/dist/cbor-blob-migration.d.ts +8 -0
- package/dist/cbor-blob-migration.d.ts.map +1 -0
- package/dist/cbor-blob-migration.js +115 -0
- package/dist/cbor-blob-migration.js.map +1 -0
- package/dist/close-session-handler.d.ts +41 -0
- package/dist/close-session-handler.d.ts.map +1 -0
- package/dist/close-session-handler.js +399 -0
- package/dist/close-session-handler.js.map +1 -0
- package/dist/consortium-bootstrap.d.ts +83 -0
- package/dist/consortium-bootstrap.d.ts.map +1 -0
- package/dist/consortium-bootstrap.js +120 -0
- package/dist/consortium-bootstrap.js.map +1 -0
- package/dist/contact-handlers.d.ts +56 -0
- package/dist/contact-handlers.d.ts.map +1 -0
- package/dist/contact-handlers.js +303 -0
- package/dist/contact-handlers.js.map +1 -0
- package/dist/content-park-client.d.ts.map +1 -1
- package/dist/content-park-client.js +7 -7
- package/dist/content-park-client.js.map +1 -1
- package/dist/content-park.d.ts +15 -0
- package/dist/content-park.d.ts.map +1 -0
- package/dist/content-park.js +227 -0
- package/dist/content-park.js.map +1 -0
- package/dist/daemon.d.ts +0 -11
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +407 -4552
- package/dist/daemon.js.map +1 -1
- package/dist/db-identity-store.d.ts +31 -35
- package/dist/db-identity-store.d.ts.map +1 -1
- package/dist/db-identity-store.js +62 -66
- package/dist/db-identity-store.js.map +1 -1
- package/dist/directory-bootstrap.d.ts +27 -31
- package/dist/directory-bootstrap.d.ts.map +1 -1
- package/dist/directory-bootstrap.js +18 -21
- package/dist/directory-bootstrap.js.map +1 -1
- package/dist/frame-values.d.ts +3 -0
- package/dist/frame-values.d.ts.map +1 -0
- package/dist/frame-values.js +73 -0
- package/dist/frame-values.js.map +1 -0
- package/dist/identity-migration.d.ts +8 -8
- package/dist/identity-migration.d.ts.map +1 -1
- package/dist/identity-migration.js +30 -31
- package/dist/identity-migration.js.map +1 -1
- package/dist/inbound-seal-request.d.ts +17 -0
- package/dist/inbound-seal-request.d.ts.map +1 -0
- package/dist/inbound-seal-request.js +155 -0
- package/dist/inbound-seal-request.js.map +1 -0
- package/dist/inbound-sessions.d.ts +54 -0
- package/dist/inbound-sessions.d.ts.map +1 -0
- package/dist/inbound-sessions.js +603 -0
- package/dist/inbound-sessions.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -10
- package/dist/index.js.map +1 -1
- package/dist/initiate-session-handler.d.ts +22 -0
- package/dist/initiate-session-handler.d.ts.map +1 -0
- package/dist/initiate-session-handler.js +150 -0
- package/dist/initiate-session-handler.js.map +1 -0
- package/dist/network-directory-node.d.ts +6 -6
- package/dist/network-directory-node.d.ts.map +1 -1
- package/dist/network-directory-node.js +29 -28
- package/dist/network-directory-node.js.map +1 -1
- package/dist/notification-handlers.d.ts +32 -0
- package/dist/notification-handlers.d.ts.map +1 -0
- package/dist/notification-handlers.js +73 -0
- package/dist/notification-handlers.js.map +1 -0
- package/dist/outbound-sessions.d.ts +54 -0
- package/dist/outbound-sessions.d.ts.map +1 -0
- package/dist/outbound-sessions.js +429 -0
- package/dist/outbound-sessions.js.map +1 -0
- package/dist/park-envelope.d.ts.map +1 -1
- package/dist/park-envelope.js +3 -2
- package/dist/park-envelope.js.map +1 -1
- package/dist/register-handler.d.ts +35 -0
- package/dist/register-handler.d.ts.map +1 -0
- package/dist/register-handler.js +179 -0
- package/dist/register-handler.js.map +1 -0
- package/dist/registration-manager.d.ts.map +1 -1
- package/dist/registration-manager.js +3 -6
- package/dist/registration-manager.js.map +1 -1
- package/dist/relay-receipt-store.d.ts.map +1 -1
- package/dist/relay-receipt-store.js +1 -2
- package/dist/relay-receipt-store.js.map +1 -1
- package/dist/retry-queue.d.ts +18 -60
- package/dist/retry-queue.d.ts.map +1 -1
- package/dist/retry-queue.js +46 -97
- package/dist/retry-queue.js.map +1 -1
- package/dist/seal-coordinator.d.ts +47 -0
- package/dist/seal-coordinator.d.ts.map +1 -0
- package/dist/seal-coordinator.js +551 -0
- package/dist/seal-coordinator.js.map +1 -0
- package/dist/seal-flows.d.ts +50 -0
- package/dist/seal-flows.d.ts.map +1 -0
- package/dist/seal-flows.js +454 -0
- package/dist/seal-flows.js.map +1 -0
- package/dist/seal-frontier-verify.d.ts +9 -9
- package/dist/seal-frontier-verify.d.ts.map +1 -1
- package/dist/seal-frontier-verify.js +11 -12
- package/dist/seal-frontier-verify.js.map +1 -1
- package/dist/seal-leaf.d.ts +58 -0
- package/dist/seal-leaf.d.ts.map +1 -0
- package/dist/seal-leaf.js +111 -0
- package/dist/seal-leaf.js.map +1 -0
- package/dist/seal-receipt-upgrade.d.ts +1 -1
- package/dist/seal-receipt-upgrade.js +1 -1
- package/dist/seal-upgrade.d.ts +10 -10
- package/dist/seal-upgrade.d.ts.map +1 -1
- package/dist/seal-upgrade.js +23 -24
- package/dist/seal-upgrade.js.map +1 -1
- package/dist/session-assignment-parser.d.ts +16 -19
- package/dist/session-assignment-parser.d.ts.map +1 -1
- package/dist/session-assignment-parser.js +30 -29
- package/dist/session-assignment-parser.js.map +1 -1
- package/dist/session-ceremony.d.ts +3 -6
- package/dist/session-ceremony.d.ts.map +1 -1
- package/dist/session-ceremony.js +7 -9
- package/dist/session-ceremony.js.map +1 -1
- package/dist/session-content-handlers.d.ts +25 -0
- package/dist/session-content-handlers.d.ts.map +1 -0
- package/dist/session-content-handlers.js +436 -0
- package/dist/session-content-handlers.js.map +1 -0
- package/dist/session-name.d.ts +35 -0
- package/dist/session-name.d.ts.map +1 -0
- package/dist/session-name.js +60 -0
- package/dist/session-name.js.map +1 -0
- package/dist/session-node-manager.d.ts +26 -62
- package/dist/session-node-manager.d.ts.map +1 -1
- package/dist/session-node-manager.js +115 -92
- package/dist/session-node-manager.js.map +1 -1
- package/dist/session-read-handlers.d.ts +38 -0
- package/dist/session-read-handlers.d.ts.map +1 -0
- package/dist/session-read-handlers.js +233 -0
- package/dist/session-read-handlers.js.map +1 -0
- package/dist/session-relay-client.d.ts +8 -8
- package/dist/session-relay-client.d.ts.map +1 -1
- package/dist/session-relay-client.js +44 -46
- package/dist/session-relay-client.js.map +1 -1
- package/dist/session-tree.d.ts +11 -23
- package/dist/session-tree.d.ts.map +1 -1
- package/dist/session-tree.js +14 -26
- package/dist/session-tree.js.map +1 -1
- package/dist/signaling-connect.d.ts +2 -5
- package/dist/signaling-connect.d.ts.map +1 -1
- package/dist/signaling-connect.js +7 -12
- package/dist/signaling-connect.js.map +1 -1
- package/dist/sqlcipher-db.d.ts +18 -19
- package/dist/sqlcipher-db.d.ts.map +1 -1
- package/dist/sqlcipher-db.js +30 -30
- package/dist/sqlcipher-db.js.map +1 -1
- package/dist/telegram-doorbell.d.ts +38 -0
- package/dist/telegram-doorbell.d.ts.map +1 -0
- package/dist/telegram-doorbell.js +139 -0
- package/dist/telegram-doorbell.js.map +1 -0
- package/dist/types.d.ts +86 -79
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -1
- package/dist/types.js.map +1 -1
- package/dist/vocabulary.d.ts +30 -30
- package/dist/vocabulary.d.ts.map +1 -1
- package/dist/vocabulary.js +41 -49
- package/dist/vocabulary.js.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,603 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Seam 2: inbound session establishment — the counterparty side.
|
|
3
|
+
*
|
|
4
|
+
* When agent A initiates a session, the directory FROST-signs a SessionAssignment and pushes it to
|
|
5
|
+
* B. This is everything B does with it: verify it, wait for B's standing receiver, accept the
|
|
6
|
+
* session, and park the event on a per-agent queue that a waiting cello_await_session drains.
|
|
7
|
+
*
|
|
8
|
+
* The accept path is SERIALIZED through one chain (inboundAcceptChain) on purpose — two assignments
|
|
9
|
+
* racing into acceptSession would both try to consume the single standing receiver.
|
|
10
|
+
*
|
|
11
|
+
* Everything here is keyed by (agentName, sessionId), NEVER sessionId alone: two of the operator's
|
|
12
|
+
* agents can hold both ends of the same session on one daemon (DOD-LOOP-1), and keying by session
|
|
13
|
+
* alone would let one agent's inbound event resolve the other's waiter.
|
|
14
|
+
*/
|
|
15
|
+
import { randomUUID } from "node:crypto";
|
|
16
|
+
import { frameValueToHex } from "./frame-values.js";
|
|
17
|
+
import { computeGenesisPrevRoot } from "@cello-protocol/protocol-types";
|
|
18
|
+
import { hash as cryptoHash } from "@cello-protocol/crypto";
|
|
19
|
+
import { DbIdentityStore } from "./db-identity-store.js";
|
|
20
|
+
import { extractOfferedMoniker } from "./session-assignment-parser.js";
|
|
21
|
+
/** How long an un-accepted inbound session request stays claimable. */
|
|
22
|
+
export const INBOUND_SESSION_TTL_MS = 24 * 60 * 60 * 1000;
|
|
23
|
+
export function createInboundSessions(deps) {
|
|
24
|
+
const { logger, sessionNodeManager, agents, getConnState, resolveCurrentAgent, NO_CURRENT_AGENT_RESPONSE, getKeyProvider, sharedSignaling, handleInboundSealInterruptedRequest, reapDeadHalfOpenSessions, sendAwayResponse, dispatchSessionStateChangedWithTelegram, sendTelegramDoorbell, } = deps;
|
|
25
|
+
// ─── Seam 2: inbound session establishment (counterparty side) ─────────────
|
|
26
|
+
//
|
|
27
|
+
// When agent A initiates a session, the directory FROST-signs a SessionAssignment
|
|
28
|
+
// and PUSHES it to the counterparty B over B's directory signaling stream (an
|
|
29
|
+
// unsolicited `session_assignment` frame). The daemon turns that frame into a live
|
|
30
|
+
// inbound session: it resolves which local agent is participant_b, calls
|
|
31
|
+
// SessionNodeManager.acceptSession (which hands off the standing receiver node bound
|
|
32
|
+
// to A's session peer id), and enqueues an inbound session event that a blocked
|
|
33
|
+
// cello_await_session call returns. This is the inbound mirror of cello_initiate_session.
|
|
34
|
+
// MONIKER-2 AC2b: the initiator's validated offered name. Session-scoped display material ONLY
|
|
35
|
+
// (spec: promotion to a stored pet name needs an explicit operator action). In-memory by design —
|
|
36
|
+
// a restart degrades the label to fingerprint, never worse.
|
|
37
|
+
//
|
|
38
|
+
// DOD-MONIKER-6 (spec §10): keyed by (agentName, sessionIdHex), NEVER sessionIdHex alone. A box
|
|
39
|
+
// is written by the RECEIVING side and holds the CALLER's name, so it is meaningful only to the
|
|
40
|
+
// agent it was written for. Keyed by session id alone, a daemon hosting both participants hands
|
|
41
|
+
// the initiator the box filled in for her counterparty — she is told she messaged herself — and
|
|
42
|
+
// either agent's state change or request expiry silently drops the other's name.
|
|
43
|
+
const offeredMonikers = new Map();
|
|
44
|
+
const offerKey = (agentName, sessionIdHex) => `${agentName}:${sessionIdHex}`;
|
|
45
|
+
// Expired requests move HERE (from inboundSessionQueues) rather than vanishing — visible via
|
|
46
|
+
// cello_check_notifications so the operator can see what they missed, not just silence.
|
|
47
|
+
const expiredSessionRequests = new Map();
|
|
48
|
+
// A blocked cello_await_session waiter. connectionId is carried so the disconnect
|
|
49
|
+
// hook can evict waiters belonging to a dead connection (otherwise enqueue would hand
|
|
50
|
+
// an inbound event to a closed connection and the event would be lost — review H2).
|
|
51
|
+
// Per-agent FIFO queue (events accepted while no cello_await_session is blocked) and
|
|
52
|
+
// the per-agent list of blocked waiters. Inbound sessions are addressed to a specific
|
|
53
|
+
// local agent (participant_b), so both are keyed by agent name.
|
|
54
|
+
const inboundSessionQueues = new Map();
|
|
55
|
+
const inboundSessionWaiters = new Map();
|
|
56
|
+
// Session ids whose acceptInboundAssignment is in flight (the accept step is async
|
|
57
|
+
// because it may wait for the standing receiver to rebuild). Guards against two
|
|
58
|
+
// simultaneous frames for the SAME session both passing the getSessionRecord check
|
|
59
|
+
// before either has inserted the row (review M1, race half).
|
|
60
|
+
// DOD-LOOP-1: keyed by (agentName, sessionId), like every other structure here — NOT by session
|
|
61
|
+
// id alone. Two of the operator's agents can hold both ends of the same session on one daemon, and
|
|
62
|
+
// a bare session key would let one agent's in-flight accept suppress the other's.
|
|
63
|
+
const inboundInFlight = new Set();
|
|
64
|
+
// Inbound accepts are SERIALIZED through this chain. acceptSession synchronously
|
|
65
|
+
// consumes the single standing receiver and rebuilds a replacement asynchronously;
|
|
66
|
+
// running two accepts concurrently would let the second pass its readiness check on
|
|
67
|
+
// the receiver the first is about to consume, then fail on the consumed receiver
|
|
68
|
+
// (review M2, race). Serializing makes the second accept wait for the first's rebuild.
|
|
69
|
+
let inboundAcceptChain = Promise.resolve();
|
|
70
|
+
function enqueueInboundSession(agentName, event) {
|
|
71
|
+
const waiters = inboundSessionWaiters.get(agentName);
|
|
72
|
+
if (waiters && waiters.length > 0) {
|
|
73
|
+
// Hand straight to the oldest blocked waiter (deliver clears its own timeout).
|
|
74
|
+
const w = waiters.shift();
|
|
75
|
+
w.deliver(event);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const q = inboundSessionQueues.get(agentName) ?? [];
|
|
79
|
+
q.push({ ...event, enqueuedAt: Date.now() }); // M8C-TTL-1: stamp queue entry time
|
|
80
|
+
inboundSessionQueues.set(agentName, q);
|
|
81
|
+
}
|
|
82
|
+
// M8C-TTL-1 (reviewer HIGH fix, aed2d71f, D19): expiredSessionRequests is a lasting log, not a
|
|
83
|
+
// consumed queue (unlike inboundSessionQueues, nothing ever drains it) — a whitelisted CONTACT-1
|
|
84
|
+
// contact is EXEMPT from ABUSE-1's acceptance bounds ("bounded only by disk"), so they can push
|
|
85
|
+
// unlimited accepted sessions the operator never claims, each becoming a permanent, unremovable
|
|
86
|
+
// entry every 24h for the life of the daemon process. Capped the same way STATUS_RESUMABLE_CAP
|
|
87
|
+
// bounds the interrupted-sessions list — keep only the MOST RECENT N per agent.
|
|
88
|
+
const EXPIRED_SESSION_REQUESTS_CAP = 20;
|
|
89
|
+
// M8C-TTL-1: move any queue entries past the TTL into expiredSessionRequests (visible via
|
|
90
|
+
// INBOX), instead of leaving them to sit forever or vanish silently. Called lazily on every
|
|
91
|
+
// read of the queue (cello_await_session's immediate-check, cello_check_notifications) rather
|
|
92
|
+
// than on a timer — no background sweep needed, and it's always correct-as-of-the-read.
|
|
93
|
+
function reapExpiredInboundSessions(agentName) {
|
|
94
|
+
const q = inboundSessionQueues.get(agentName);
|
|
95
|
+
if (!q || q.length === 0)
|
|
96
|
+
return;
|
|
97
|
+
const now = Date.now();
|
|
98
|
+
const live = [];
|
|
99
|
+
let expiredList = expiredSessionRequests.get(agentName);
|
|
100
|
+
for (const e of q) {
|
|
101
|
+
if (e.enqueuedAt !== undefined && now - e.enqueuedAt > INBOUND_SESSION_TTL_MS) {
|
|
102
|
+
if (!expiredList) {
|
|
103
|
+
expiredList = [];
|
|
104
|
+
expiredSessionRequests.set(agentName, expiredList);
|
|
105
|
+
}
|
|
106
|
+
expiredList.push({ sessionIdHex: e.sessionIdHex, counterpartyPubkeyHex: e.counterpartyPubkeyHex, expiredAt: now });
|
|
107
|
+
if (expiredList.length > EXPIRED_SESSION_REQUESTS_CAP) {
|
|
108
|
+
expiredList.splice(0, expiredList.length - EXPIRED_SESSION_REQUESTS_CAP); // keep newest N
|
|
109
|
+
}
|
|
110
|
+
logger.info("session.request.expired", { agentName, sessionId: e.sessionIdHex, enqueuedAt: e.enqueuedAt });
|
|
111
|
+
// MONIKER-2 AC2b (review F1): the offer's display name expires with the offer.
|
|
112
|
+
if (offeredMonikers.delete(offerKey(agentName, e.sessionIdHex))) {
|
|
113
|
+
logger.debug("moniker.offer.dropped", { agentName, sessionId: e.sessionIdHex, state: "expired" });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
live.push(e);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
inboundSessionQueues.set(agentName, live);
|
|
121
|
+
}
|
|
122
|
+
// Pull the fields out of a pushed session_assignment frame. Returns null when the frame
|
|
123
|
+
// carries no usable assignment object / is missing the essential ids (the handler then
|
|
124
|
+
// ignores it rather than throwing). Field-level validity (peer id, signature type) is
|
|
125
|
+
// checked in the handler so it can emit a distinct, diagnosable event per failure.
|
|
126
|
+
function extractInboundSessionAssignment(frame) {
|
|
127
|
+
const raw = frame["assignment"];
|
|
128
|
+
if (!raw || typeof raw !== "object")
|
|
129
|
+
return null;
|
|
130
|
+
const a = raw;
|
|
131
|
+
const pa = a["participant_a"];
|
|
132
|
+
const pb = a["participant_b"];
|
|
133
|
+
const sessionIdHex = frameValueToHex(a["session_id"]);
|
|
134
|
+
const participantAPubkeyHex = pa ? frameValueToHex(pa["pubkey"]) : null;
|
|
135
|
+
const participantBPubkeyHex = pb ? frameValueToHex(pb["pubkey"]) : null;
|
|
136
|
+
if (!sessionIdHex || !participantAPubkeyHex || !participantBPubkeyHex)
|
|
137
|
+
return null;
|
|
138
|
+
// M7 DOD-SPINE-6 / MSG-001-3b: relay endpoint so the receiver also connects to the
|
|
139
|
+
// relay witness (so the relay can deliver the initiator's witnessed leaves to it).
|
|
140
|
+
const relayEndpoint = a["relay_endpoint"];
|
|
141
|
+
const relayPeerId = relayEndpoint && typeof relayEndpoint["peer_id"] === "string" ? relayEndpoint["peer_id"] : "";
|
|
142
|
+
const relayAddrs = relayEndpoint && Array.isArray(relayEndpoint["multiaddrs"])
|
|
143
|
+
? relayEndpoint["multiaddrs"].filter((m) => typeof m === "string")
|
|
144
|
+
: [];
|
|
145
|
+
// MONIKER-2 AC2: validate the offered name ONCE, at this wire boundary.
|
|
146
|
+
const monikerResult = extractOfferedMoniker(a);
|
|
147
|
+
return {
|
|
148
|
+
sessionIdHex,
|
|
149
|
+
participantAPubkeyHex,
|
|
150
|
+
participantBPubkeyHex,
|
|
151
|
+
initiatorPeerId: typeof a["initiator_session_peer_id"] === "string" ? a["initiator_session_peer_id"] : "",
|
|
152
|
+
counterpartySessionPeerId: typeof a["counterparty_session_peer_id"] === "string" ? a["counterparty_session_peer_id"] : null,
|
|
153
|
+
sessionTimestamp: typeof a["session_timestamp"] === "number" ? a["session_timestamp"] : 0,
|
|
154
|
+
signatureType: typeof a["signature_type"] === "string" ? a["signature_type"] : null,
|
|
155
|
+
// M7 legibility-TBS-binding (responder verify): the FROST-signed assignment embeds the
|
|
156
|
+
// initiator's primary (group) pubkey as `signer_pubkey` — the key that signs the seal.
|
|
157
|
+
// The responder stores it so it can verify the bilateral seal signature locally, not just
|
|
158
|
+
// accept it (session.ts: "embedded so the counterparty can verify").
|
|
159
|
+
signerPubkeyHex: a["signer_pubkey"] !== undefined ? frameValueToHex(a["signer_pubkey"]) : null,
|
|
160
|
+
relayPeerId,
|
|
161
|
+
relayAddrs,
|
|
162
|
+
offeredMoniker: monikerResult.offeredMoniker,
|
|
163
|
+
monikerRejected: monikerResult.rejected,
|
|
164
|
+
monikerRejectReason: monikerResult.reason ?? null,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
// Wait (bounded) for THIS AGENT's standing receiver to be ready. acceptSession consumes the
|
|
168
|
+
// agent's standing receiver and rebuilds a replacement asynchronously, so a burst of inbound
|
|
169
|
+
// assignments for that agent would otherwise drop all but the first (review M2). Polling the
|
|
170
|
+
// per-agent readiness lets each accept proceed once the prior rebuild completes. Must check the
|
|
171
|
+
// OWNING agent — `getStandingReceiverReady()` with no arg returns true if ANY agent has one,
|
|
172
|
+
// which in the loopback case (alice + bob on one daemon) would falsely pass while bob's own SR
|
|
173
|
+
// is still mid-rebuild and drop bob's session (DOD-LOOP-1).
|
|
174
|
+
async function waitForStandingReceiver(agentName, maxWaitMs = 3_000, stepMs = 25) {
|
|
175
|
+
if (sessionNodeManager.getStandingReceiverReady(agentName))
|
|
176
|
+
return true;
|
|
177
|
+
const deadline = Date.now() + maxWaitMs;
|
|
178
|
+
while (Date.now() < deadline) {
|
|
179
|
+
await new Promise((r) => setTimeout(r, stepMs));
|
|
180
|
+
if (sessionNodeManager.getStandingReceiverReady(agentName))
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
return sessionNodeManager.getStandingReceiverReady(agentName);
|
|
184
|
+
}
|
|
185
|
+
async function acceptInboundAssignment(parsed, agentName, correlationId) {
|
|
186
|
+
try {
|
|
187
|
+
// M8C-ABUSE-1 + DOD-TIER-2/3: bound acceptance by the sender's TIER — a per-sender cap that is
|
|
188
|
+
// the sender's tier cap (BLOCKED 0 → refused here, indistinguishably from an over-cap stranger),
|
|
189
|
+
// plus a global anti-swarm cap that applies only to the UNKNOWN-tier stranger pool. No tier is
|
|
190
|
+
// unbounded (INV-TIER-BOUND); KNOWN+ are exempt from the GLOBAL pool only, not from their own
|
|
191
|
+
// cap. Checked FIRST, before any standing-receiver work, so a refusal is cheap.
|
|
192
|
+
// CC-10: reap provably-dead ghosts for THIS agent before counting — otherwise invisible
|
|
193
|
+
// interrupted 0-received sessions (post-restart shape) consume the sender's budget forever
|
|
194
|
+
// and lock the stranger out with no list read ever clearing them. Safe here: abandonSession
|
|
195
|
+
// flips the DB status synchronously, so the bound query below sees the reaped state.
|
|
196
|
+
reapDeadHalfOpenSessions(agentName);
|
|
197
|
+
const bound = sessionNodeManager.checkUnknownSenderAcceptanceBound(agentName, parsed.participantAPubkeyHex);
|
|
198
|
+
if (!bound.ok) {
|
|
199
|
+
logger.warn("session.inbound.accept.failed", {
|
|
200
|
+
sessionId: parsed.sessionIdHex,
|
|
201
|
+
agentName,
|
|
202
|
+
reason: bound.reason,
|
|
203
|
+
correlationId,
|
|
204
|
+
});
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
// DOD-RENAME-1 AC1 (review F1 / DEC-AB-4): record the offered name as the rename baseline the
|
|
208
|
+
// moment the offer passes the acceptance gate — an ALLOWED peer (the bound check let them
|
|
209
|
+
// through) is tracked even if session formation transiently fails below. Deliberately AFTER the
|
|
210
|
+
// bound check, not at the raw parse point: a BLOCKED or over-cap peer must NOT be able to drive
|
|
211
|
+
// the operator's rename baseline or push notices into their inbox. Guarded by offeredMoniker !==
|
|
212
|
+
// null so a silent offer never clears the baseline (AC5).
|
|
213
|
+
if (parsed.offeredMoniker !== null) {
|
|
214
|
+
sessionNodeManager.recordOfferedMoniker(agentName, parsed.participantAPubkeyHex, parsed.offeredMoniker);
|
|
215
|
+
}
|
|
216
|
+
// M8B F14 (fix 2): KICK creation before polling — an inbound offer arriving while no
|
|
217
|
+
// receiver exists and none is being created must trigger the ensure itself (the doc
|
|
218
|
+
// comment's "retries on demand" made true), instead of polling a creation nobody
|
|
219
|
+
// started and dropping the offer. Fire-and-forget: the poll below observes readiness.
|
|
220
|
+
void sessionNodeManager.ensureStandingReceiverForAgent(agentName).catch((err) => {
|
|
221
|
+
logger.warn("session.standing_receiver.ensure.failed", {
|
|
222
|
+
agentName,
|
|
223
|
+
reason: err instanceof Error ? err.message : String(err),
|
|
224
|
+
correlationId,
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
// M2: do not drop the session if this agent's standing receiver is mid-rebuild.
|
|
228
|
+
const ready = await waitForStandingReceiver(agentName);
|
|
229
|
+
if (!ready) {
|
|
230
|
+
logger.warn("session.inbound.accept.failed", {
|
|
231
|
+
sessionId: parsed.sessionIdHex,
|
|
232
|
+
agentName,
|
|
233
|
+
reason: "standing_receiver_unavailable",
|
|
234
|
+
correlationId,
|
|
235
|
+
});
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
// M7 DOD-SPINE-6 / MSG-001-3b: relay witness for the receiver. Build from the
|
|
239
|
+
// inbound assignment's relay endpoint + this agent's K_local + the 16-byte session id.
|
|
240
|
+
const kp = getKeyProvider(agentName);
|
|
241
|
+
let relayParams;
|
|
242
|
+
if (kp && parsed.relayPeerId && parsed.relayAddrs.length > 0) {
|
|
243
|
+
relayParams = {
|
|
244
|
+
relayPeerId: parsed.relayPeerId,
|
|
245
|
+
relayAddrs: parsed.relayAddrs,
|
|
246
|
+
keyProvider: kp,
|
|
247
|
+
senderPubkey: await kp.getPublicKey(),
|
|
248
|
+
sessionIdBytes: new Uint8Array(Buffer.from(parsed.sessionIdHex, "hex")),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
const result = await sessionNodeManager.acceptSession(parsed.sessionIdHex, agentName, parsed.participantAPubkeyHex, // the initiator is OUR counterparty
|
|
252
|
+
parsed.initiatorPeerId, correlationId, relayParams);
|
|
253
|
+
if (!result.ok) {
|
|
254
|
+
logger.warn("session.inbound.accept.failed", {
|
|
255
|
+
sessionId: parsed.sessionIdHex,
|
|
256
|
+
agentName,
|
|
257
|
+
reason: result.reason,
|
|
258
|
+
correlationId,
|
|
259
|
+
});
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
// M7 legibility-TBS-binding (responder verify): store the initiator's primary (the seal
|
|
263
|
+
// signer, carried as signer_pubkey on the FROST-signed assignment) so the bilateral seal
|
|
264
|
+
// signature can be verified locally rather than accepted on faith.
|
|
265
|
+
if (parsed.signerPubkeyHex) {
|
|
266
|
+
sessionNodeManager.recordCounterpartyPrimary(agentName, parsed.sessionIdHex, parsed.signerPubkeyHex);
|
|
267
|
+
}
|
|
268
|
+
// H1: genesis_prev_root is the canonical two-party genesis value — the SAME value
|
|
269
|
+
// baked into the FROST-signed session-establishment TBS and derived by the initiator
|
|
270
|
+
// and directory — NOT the daemon's (empty) tree root. computeGenesisPrevRoot sorts
|
|
271
|
+
// the pubkeys internally, so natural (A, B) order is correct.
|
|
272
|
+
const genesisPrevRootHex = Buffer.from(computeGenesisPrevRoot(Buffer.from(parsed.participantAPubkeyHex, "hex"), Buffer.from(parsed.participantBPubkeyHex, "hex"), Buffer.from(parsed.sessionIdHex, "hex"), parsed.sessionTimestamp)).toString("hex");
|
|
273
|
+
logger.info("session.inbound.accepted", {
|
|
274
|
+
sessionId: parsed.sessionIdHex,
|
|
275
|
+
agentName,
|
|
276
|
+
sessionPeerId: result.peerId,
|
|
277
|
+
correlationId,
|
|
278
|
+
});
|
|
279
|
+
// MONIKER-2 AC2: a present-but-invalid moniker is a red flag (the sender runs modified
|
|
280
|
+
// code) — logged with the spec'd fields and NEVER the raw value. Not grounds to refuse
|
|
281
|
+
// the session (version skew is real; refusing hands strangers a DoS lever).
|
|
282
|
+
if (parsed.monikerRejected) {
|
|
283
|
+
logger.info("moniker.rejected", {
|
|
284
|
+
agentName,
|
|
285
|
+
pubkey: parsed.participantAPubkeyHex,
|
|
286
|
+
reason: parsed.monikerRejectReason ?? "invalid",
|
|
287
|
+
correlationId,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
// MONIKER-2 AC2b: display material for THIS offer/session only (never persisted, never
|
|
291
|
+
// auto-written to contacts — AC3). Session-scoped: MONIKER-4's dispatcher resolves
|
|
292
|
+
// whoLabel from here; entries are dropped when the session's terminal state dispatches.
|
|
293
|
+
// DOD-MONIKER-6: scoped to `agentName` — the agent this offer was received FOR.
|
|
294
|
+
if (parsed.offeredMoniker !== null) {
|
|
295
|
+
offeredMonikers.set(offerKey(agentName, parsed.sessionIdHex), parsed.offeredMoniker);
|
|
296
|
+
// (DOD-RENAME-1's rename-baseline update runs earlier, right after the acceptance bound — see
|
|
297
|
+
// the recordOfferedMoniker call there. This map is only the session-scoped display material.)
|
|
298
|
+
}
|
|
299
|
+
enqueueInboundSession(agentName, {
|
|
300
|
+
sessionIdHex: parsed.sessionIdHex,
|
|
301
|
+
counterpartyPubkeyHex: parsed.participantAPubkeyHex,
|
|
302
|
+
genesisPrevRootHex,
|
|
303
|
+
offeredMoniker: parsed.offeredMoniker,
|
|
304
|
+
});
|
|
305
|
+
dispatchSessionStateChangedWithTelegram(agentName, parsed.sessionIdHex, "created", parsed.participantAPubkeyHex);
|
|
306
|
+
// M8C-TGDOOR-1: session requests ALWAYS ring (DoD) — no coalescing, unlike message-waiting.
|
|
307
|
+
void sendTelegramDoorbell(agentName, parsed.sessionIdHex, "session_request", "New session request");
|
|
308
|
+
// M8C-AWAY-1: an unattended agent auto-acks a fresh inbound session request. Fire-and-forget
|
|
309
|
+
// (sendAwayResponse never throws) — best-effort, must not delay/block acceptance completion.
|
|
310
|
+
void sendAwayResponse(agentName, parsed.sessionIdHex, "request");
|
|
311
|
+
// CC-1 (2026-07-07): do NOT auto-add the requester here. Accepting the *connection* must not
|
|
312
|
+
// grant *trust*. The old auto-add promoted any stranger who knocked once to "known" (Level-4
|
|
313
|
+
// fast-track), which defeated BOTH the screening layer AND the ABUSE-1 acceptance caps
|
|
314
|
+
// (a higher tier raises the per-sender cap, so auto-promoting a stranger would have let their
|
|
315
|
+
// sessions 2+ ride a larger bound — DOD-TIER-2). Promotion to "known" now requires operator engagement only:
|
|
316
|
+
// an outbound cello_initiate_session (below, ~3137), the operator replying INTO the session via
|
|
317
|
+
// cello_send, or an explicit cello_contact_add. An unattended stranger is never auto-whitelisted.
|
|
318
|
+
// See docs/planning/.../2026-07-07_1700_four-level-screening-policy.md (D21).
|
|
319
|
+
}
|
|
320
|
+
finally {
|
|
321
|
+
inboundInFlight.delete(offerKey(agentName, parsed.sessionIdHex));
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function handleInboundSessionAssignment(frame) {
|
|
325
|
+
// M4: one correlationId minted per inbound flow, threaded through EVERY event below.
|
|
326
|
+
const correlationId = randomUUID();
|
|
327
|
+
const parsed = extractInboundSessionAssignment(frame);
|
|
328
|
+
if (!parsed) {
|
|
329
|
+
logger.warn("session.inbound.assignment.malformed", {
|
|
330
|
+
reason: "missing_assignment_or_ids",
|
|
331
|
+
correlationId,
|
|
332
|
+
});
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
// L1: refuse M1 single-key assignments outright (downgrade guard). Distinct from the
|
|
336
|
+
// deferred FROST verification below — track it so SESSION-004's re-home keeps it.
|
|
337
|
+
if (parsed.signatureType === "single") {
|
|
338
|
+
logger.warn("session.inbound.assignment.refused", {
|
|
339
|
+
sessionId: parsed.sessionIdHex,
|
|
340
|
+
reason: "unsupported_signature_type",
|
|
341
|
+
correlationId,
|
|
342
|
+
});
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
// M3: the initiator session peer id is the AC-015 hand-off gate (acceptSession passes
|
|
346
|
+
// it to gater.setAllowedPeer). An empty value would gate the handed-off receiver to "",
|
|
347
|
+
// defeating "only the initiator may connect". The dead stack treated this as malformed.
|
|
348
|
+
if (!parsed.initiatorPeerId) {
|
|
349
|
+
logger.warn("session.inbound.assignment.malformed", {
|
|
350
|
+
sessionId: parsed.sessionIdHex,
|
|
351
|
+
reason: "missing_initiator_peer_id",
|
|
352
|
+
correlationId,
|
|
353
|
+
});
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
// Resolve which local agent is participant_b. participant pubkeys are the agents'
|
|
357
|
+
// K_local identity pubkeys (same convention as the seal-interrupted responder's
|
|
358
|
+
// counterparty match above). If none of our agents is the counterparty, this
|
|
359
|
+
// assignment is not for this daemon — drop it.
|
|
360
|
+
const localAgent = agents.find((ag) => ag.pubkey === parsed.participantBPubkeyHex);
|
|
361
|
+
if (!localAgent) {
|
|
362
|
+
logger.debug("session.inbound.not_local", {
|
|
363
|
+
sessionId: parsed.sessionIdHex,
|
|
364
|
+
counterpartyPubkey: parsed.participantBPubkeyHex,
|
|
365
|
+
correlationId,
|
|
366
|
+
});
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
// DOD-INBOUND-GUARD-1 (D3): the receive-side mirror of the initiator's F13 guard (~3189).
|
|
370
|
+
// An empty counterparty endpoint means the offer-accept never happened (this agent's standing
|
|
371
|
+
// receiver was not up when the directory's session_offer arrived) and the directory signed the
|
|
372
|
+
// assignment anyway, with "empty defaults". The initiator refuses that assignment and creates
|
|
373
|
+
// NO session — accepting it here builds a phantom session whose away-reply lands in the
|
|
374
|
+
// initiator's transcript with no session row: permanently unread AND unreadable. Refuse it
|
|
375
|
+
// loudly, before any session state exists. Nothing legitimate is lost — a session whose
|
|
376
|
+
// initiator has no address to dial is unusable by construction.
|
|
377
|
+
if (!parsed.counterpartySessionPeerId) {
|
|
378
|
+
logger.warn("session.inbound.assignment.incomplete", {
|
|
379
|
+
agentName: localAgent.name,
|
|
380
|
+
sessionId: parsed.sessionIdHex,
|
|
381
|
+
correlationId,
|
|
382
|
+
});
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
// M1: idempotency — a retransmitted assignment for an already-known session (persisted
|
|
386
|
+
// row OR currently in flight) must not double-accept (orphaned node) or double-enqueue.
|
|
387
|
+
if (inboundInFlight.has(offerKey(localAgent.name, parsed.sessionIdHex)) || sessionNodeManager.getSessionRecord(localAgent.name, parsed.sessionIdHex)) {
|
|
388
|
+
logger.info("session.inbound.duplicate.ignored", {
|
|
389
|
+
sessionId: parsed.sessionIdHex,
|
|
390
|
+
agentName: localAgent.name,
|
|
391
|
+
correlationId,
|
|
392
|
+
});
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
// SECURITY — DEFERRED (SESSION-004 re-home): the directory's FROST threshold signature
|
|
396
|
+
// on the assignment (directory_signature over the TBS, verified against signer_pubkey)
|
|
397
|
+
// is NOT yet verified here. The old client's receiveSessionAssignment performed that
|
|
398
|
+
// check; it must be re-homed natively before this path faces a real (untrusted)
|
|
399
|
+
// directory. Until then we accept directory-pushed assignments on trust — the in-process
|
|
400
|
+
// seam tests inject trusted frames.
|
|
401
|
+
// M8B F15: logged at DEBUG with an "expected" note — it fires on EVERY healthy inbound
|
|
402
|
+
// session, and at WARN it read as a failure and actively misled live diagnosis. The
|
|
403
|
+
// deferred-verification state is a known, tracked gap (SESSION-004), not a per-session
|
|
404
|
+
// anomaly.
|
|
405
|
+
logger.debug("session.inbound.assignment.unverified", {
|
|
406
|
+
sessionId: parsed.sessionIdHex,
|
|
407
|
+
agentName: localAgent.name,
|
|
408
|
+
note: "FROST assignment signature verification deferred to SESSION-004 re-home (expected on every inbound session until SESSION-004)",
|
|
409
|
+
correlationId,
|
|
410
|
+
});
|
|
411
|
+
inboundInFlight.add(offerKey(localAgent.name, parsed.sessionIdHex));
|
|
412
|
+
// Serialize: the next accept does not begin until this one (and any standing-receiver
|
|
413
|
+
// rebuild it triggers) settles. A throw inside one accept must not break the chain.
|
|
414
|
+
const agentName = localAgent.name;
|
|
415
|
+
inboundAcceptChain = inboundAcceptChain
|
|
416
|
+
.then(() => acceptInboundAssignment(parsed, agentName, correlationId))
|
|
417
|
+
.catch((err) => {
|
|
418
|
+
inboundInFlight.delete(offerKey(localAgent.name, parsed.sessionIdHex));
|
|
419
|
+
logger.error("session.inbound.accept.error", {
|
|
420
|
+
sessionId: parsed.sessionIdHex,
|
|
421
|
+
agentName,
|
|
422
|
+
error: err instanceof Error ? err.message : String(err),
|
|
423
|
+
correlationId,
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
// CELLO-M7-CONN-001 (DOD-CONN-2): wire the inbound session/seal responders onto a GIVEN
|
|
428
|
+
// signaling manager. Called for EVERY agent's own per-agent manager (via getAgentSignaling)
|
|
429
|
+
// so each agent receives inbound session_assignment + seal_interrupted_request on its OWN
|
|
430
|
+
// authenticated stream — closing the SPINE-5 gap where only the primary (whose stream WAS
|
|
431
|
+
// the keystone) received them. A frame arrives on exactly one agent's stream, so there is no
|
|
432
|
+
// double-dispatch; each handler resolves the local agent internally and that resolution
|
|
433
|
+
// matches the stream the directory routed the frame to.
|
|
434
|
+
// CELLO-M8-TRUST-001: open + verify + store + ACK a sealed trust signal pushed from the directory
|
|
435
|
+
// pickup queue. The daemon is the ONLY party that can open the seal (k_local — SI-001); it verifies
|
|
436
|
+
// the recomputed hash against the directory's anchor before storing, and ACKs only on success so the
|
|
437
|
+
// directory deletes the ciphertext (AC-001/AC-002). open-fail / hash-mismatch / store-fail → NO ACK
|
|
438
|
+
// (the directory keeps the pickup for a later retry; the signal is re-mintable).
|
|
439
|
+
async function handleTrustSignalPickup(frame, keyProvider, mgr, agentName) {
|
|
440
|
+
const id = typeof frame["id"] === "string" ? frame["id"] : null;
|
|
441
|
+
const signalKind = typeof frame["signal_kind"] === "string" ? frame["signal_kind"] : null;
|
|
442
|
+
const signalHash = typeof frame["signal_hash"] === "string" ? frame["signal_hash"] : null;
|
|
443
|
+
const ciphertext = frame["ciphertext"];
|
|
444
|
+
if (!id || !signalKind || !signalHash || !(ciphertext instanceof Uint8Array)) {
|
|
445
|
+
// Neither stores nor ACKs → the directory retains the row and re-delivers. Log it: a PERMANENTLY
|
|
446
|
+
// malformed frame would otherwise be re-delivered forever with zero daemon-side signal (fallback-finder).
|
|
447
|
+
logger.warn("daemon.trust_signal.malformed", {
|
|
448
|
+
agentName,
|
|
449
|
+
hasId: !!id,
|
|
450
|
+
hasSignalKind: !!signalKind,
|
|
451
|
+
hasSignalHash: !!signalHash,
|
|
452
|
+
ciphertextOk: ciphertext instanceof Uint8Array,
|
|
453
|
+
});
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
if (!keyProvider.openContentSeal) {
|
|
457
|
+
// A session-node stub key cannot open content seals. No ACK → the directory re-delivers; log so a
|
|
458
|
+
// pickup persistently routed to a stub-key agent is visible rather than a silent forever-retry.
|
|
459
|
+
logger.warn("daemon.trust_signal.no_content_key", { agentName, signalKind, correlationId: id });
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
// The pickup id correlates the directory's deliver/ack with the daemon's receive (TRUST-001 obs).
|
|
463
|
+
const correlationId = id;
|
|
464
|
+
let recovered;
|
|
465
|
+
try {
|
|
466
|
+
recovered = await keyProvider.openContentSeal(ciphertext);
|
|
467
|
+
}
|
|
468
|
+
catch {
|
|
469
|
+
recovered = null;
|
|
470
|
+
}
|
|
471
|
+
if (!recovered) {
|
|
472
|
+
logger.warn("daemon.trust_signal.open_failed", { agentName, signalKind, correlationId });
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
const recomputed = Buffer.from(cryptoHash(recovered)).toString("hex");
|
|
476
|
+
if (recomputed !== signalHash) {
|
|
477
|
+
logger.error("daemon.trust_signal.hash_mismatch", { agentName, signalKind, correlationId });
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
try {
|
|
481
|
+
const store = new DbIdentityStore(sessionNodeManager.getDb(), logger);
|
|
482
|
+
store.storeTrustSignal({ signalHash, agentId: null, signalKind, payload: recovered });
|
|
483
|
+
}
|
|
484
|
+
catch (err) {
|
|
485
|
+
logger.error("daemon.trust_signal.store_failed", {
|
|
486
|
+
agentName,
|
|
487
|
+
signalKind,
|
|
488
|
+
correlationId,
|
|
489
|
+
error: err instanceof Error ? err.message : String(err),
|
|
490
|
+
});
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
logger.info("daemon.trust_signal.received", { agentName, signalKind, verified: true, correlationId });
|
|
494
|
+
// DOD-SENDRAW-1: sendRaw never throws — a discarded result hid every failed ack, leaving the
|
|
495
|
+
// sender to retransmit against a daemon that believed it had answered.
|
|
496
|
+
const ackRes = await mgr.sendRaw({ type: "trust_signal_ack", id });
|
|
497
|
+
if (!ackRes.ok) {
|
|
498
|
+
logger.warn("daemon.trust_signal.ack_send_failed", {
|
|
499
|
+
agentName,
|
|
500
|
+
signalKind,
|
|
501
|
+
correlationId,
|
|
502
|
+
detail: ackRes.reason ?? "send_not_ok",
|
|
503
|
+
...(ackRes.guidance ? { guidance: ackRes.guidance } : {}),
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
function wirePerAgentSessionInbound(mgr) {
|
|
508
|
+
mgr.registerInboundHandler((frame) => {
|
|
509
|
+
if (frame["type"] !== "seal_interrupted_request")
|
|
510
|
+
return;
|
|
511
|
+
void handleInboundSealInterruptedRequest(frame);
|
|
512
|
+
});
|
|
513
|
+
mgr.registerInboundHandler((frame) => {
|
|
514
|
+
if (frame["type"] !== "session_assignment")
|
|
515
|
+
return;
|
|
516
|
+
handleInboundSessionAssignment(frame);
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
// CONN-001: test path only — the shared manager's inbound session/seal responders. Production
|
|
520
|
+
// wires them per-agent in getAgentSignaling (each agent receives on its own stream).
|
|
521
|
+
if (sharedSignaling)
|
|
522
|
+
wirePerAgentSessionInbound(sharedSignaling);
|
|
523
|
+
// M7 DOD-SPINE-7 / CELLO-M7-CONN-001: the seal-completion listeners (session_sealed /
|
|
524
|
+
// seal_unilateral_confirmed / seal_unilateral_notification) are registered PER-AGENT inside
|
|
525
|
+
// getAgentSignaling (the directory routes each over the session-owning agent's authenticated
|
|
526
|
+
// stream), and on the shared manager for the test path via wireSharedHandlers. No keystone, no
|
|
527
|
+
// runtime election — a fresh-install agent gets them when it comes online (create/start).
|
|
528
|
+
/**
|
|
529
|
+
* Phase 2: register the IPC handler, once the handler map exists.
|
|
530
|
+
*
|
|
531
|
+
* Split from construction because this module is constructed EARLY — the per-agent signaling
|
|
532
|
+
* wiring calls into it at boot, before the handler map is built. Fusing the two would force
|
|
533
|
+
* construction after the map, which pushes the eager per-agent connect below
|
|
534
|
+
* `await flushAwaitingContent()` and serializes every agent's directory handshake behind a
|
|
535
|
+
* relay drain. Same pattern as content-park.ts.
|
|
536
|
+
*/
|
|
537
|
+
function registerHandlers(handlers) {
|
|
538
|
+
// cello_await_session — the counterparty's blocking pull for the next inbound session.
|
|
539
|
+
// Returns immediately if one is already queued for the current agent (FIFO), otherwise
|
|
540
|
+
// blocks until one arrives or timeout_ms elapses.
|
|
541
|
+
handlers.set("cello_await_session", async (params, connectionId) => {
|
|
542
|
+
const connState = getConnState(connectionId);
|
|
543
|
+
// CC-3 / M8C-AUTOSTART-1 F18: resolve the target agent — explicit { agent } wins, else this
|
|
544
|
+
// connection's current agent, else the sole online agent (removes the no_current_agent papercut
|
|
545
|
+
// after a /mcp reconnect when exactly one agent is online). 2+ online with none selected → null.
|
|
546
|
+
const agentName = resolveCurrentAgent(connState, params?.agent);
|
|
547
|
+
if (!agentName) {
|
|
548
|
+
return NO_CURRENT_AGENT_RESPONSE;
|
|
549
|
+
}
|
|
550
|
+
const timeoutMs = typeof params?.["timeout_ms"] === "number" ? params["timeout_ms"] : 30_000;
|
|
551
|
+
const toResponse = (e) => ({
|
|
552
|
+
type: "new_session",
|
|
553
|
+
session_id: e.sessionIdHex,
|
|
554
|
+
counterparty_pubkey: e.counterpartyPubkeyHex,
|
|
555
|
+
genesis_prev_root: e.genesisPrevRootHex,
|
|
556
|
+
// MONIKER-2: validated-at-boundary display hint; null = absent or rejected.
|
|
557
|
+
offered_moniker: e.offeredMoniker ?? null,
|
|
558
|
+
});
|
|
559
|
+
reapExpiredInboundSessions(agentName); // M8C-TTL-1: don't hand back a stale expired entry
|
|
560
|
+
const queued = inboundSessionQueues.get(agentName);
|
|
561
|
+
if (queued && queued.length > 0) {
|
|
562
|
+
return toResponse(queued.shift());
|
|
563
|
+
}
|
|
564
|
+
const event = await new Promise((resolve) => {
|
|
565
|
+
const waiters = inboundSessionWaiters.get(agentName) ?? [];
|
|
566
|
+
const waiter = {
|
|
567
|
+
connectionId,
|
|
568
|
+
deliver: (e) => {
|
|
569
|
+
clearTimeout(timer);
|
|
570
|
+
resolve(e);
|
|
571
|
+
},
|
|
572
|
+
};
|
|
573
|
+
waiters.push(waiter);
|
|
574
|
+
inboundSessionWaiters.set(agentName, waiters);
|
|
575
|
+
const timer = setTimeout(() => {
|
|
576
|
+
const list = inboundSessionWaiters.get(agentName);
|
|
577
|
+
if (list) {
|
|
578
|
+
const idx = list.indexOf(waiter);
|
|
579
|
+
if (idx !== -1)
|
|
580
|
+
list.splice(idx, 1);
|
|
581
|
+
}
|
|
582
|
+
resolve(null);
|
|
583
|
+
}, timeoutMs);
|
|
584
|
+
});
|
|
585
|
+
if (event === null)
|
|
586
|
+
return { type: "timeout" };
|
|
587
|
+
return toResponse(event);
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
return {
|
|
591
|
+
registerHandlers,
|
|
592
|
+
wirePerAgentSessionInbound,
|
|
593
|
+
handleTrustSignalPickup,
|
|
594
|
+
enqueueInboundSession,
|
|
595
|
+
reapExpiredInboundSessions,
|
|
596
|
+
inboundSessionQueues,
|
|
597
|
+
inboundSessionWaiters,
|
|
598
|
+
expiredSessionRequests,
|
|
599
|
+
offeredMonikers,
|
|
600
|
+
offerKey,
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
//# sourceMappingURL=inbound-sessions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inbound-sessions.js","sourceRoot":"","sources":["../src/inbound-sessions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAOzC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,IAAI,IAAI,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAGvE,uEAAuE;AACvE,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AA6C1D,MAAM,UAAU,qBAAqB,CAAC,IAAwB;IAC5D,MAAM,EACJ,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,YAAY,EAAE,mBAAmB,EACrE,yBAAyB,EAAE,cAAc,EAAE,eAAe,EAC1D,mCAAmC,EAAE,wBAAwB,EAC7D,gBAAgB,EAAE,uCAAuC,EAAE,oBAAoB,GAChF,GAAG,IAAI,CAAC;IAET,8EAA8E;IAC9E,EAAE;IACF,kFAAkF;IAClF,8EAA8E;IAC9E,mFAAmF;IACnF,yEAAyE;IACzE,qFAAqF;IACrF,gFAAgF;IAChF,0FAA0F;IAC1F,+FAA+F;IAC/F,kGAAkG;IAClG,4DAA4D;IAC5D,EAAE;IACF,gGAAgG;IAChG,gGAAgG;IAChG,gGAAgG;IAChG,gGAAgG;IAChG,iFAAiF;IACjF,MAAM,eAAe,GAAG,IAAI,GAAG,EAAkB,CAAC;IAClD,MAAM,QAAQ,GAAG,CAAC,SAAiB,EAAE,YAAoB,EAAU,EAAE,CAAC,GAAG,SAAS,IAAI,YAAY,EAAE,CAAC;IACrG,6FAA6F;IAC7F,wFAAwF;IACxF,MAAM,sBAAsB,GAAG,IAAI,GAAG,EAAmC,CAAC;IAC1E,kFAAkF;IAClF,sFAAsF;IACtF,oFAAoF;IACpF,qFAAqF;IACrF,sFAAsF;IACtF,gEAAgE;IAChE,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAiC,CAAC;IACtE,MAAM,qBAAqB,GAAG,IAAI,GAAG,EAAkC,CAAC;IACxE,mFAAmF;IACnF,gFAAgF;IAChF,mFAAmF;IACnF,6DAA6D;IAC7D,gGAAgG;IAChG,mGAAmG;IACnG,kFAAkF;IAClF,MAAM,eAAe,GAAG,IAAI,GAAG,EAAU,CAAC;IAC1C,iFAAiF;IACjF,mFAAmF;IACnF,oFAAoF;IACpF,iFAAiF;IACjF,uFAAuF;IACvF,IAAI,kBAAkB,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;IAE1D,SAAS,qBAAqB,CAAC,SAAiB,EAAE,KAA0B;QAC1E,MAAM,OAAO,GAAG,qBAAqB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACrD,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClC,+EAA+E;YAC/E,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,EAAG,CAAC;YAC3B,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YACjB,OAAO;QACT,CAAC;QACD,MAAM,CAAC,GAAG,oBAAoB,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;QACpD,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,oCAAoC;QAClF,oBAAoB,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IACzC,CAAC;IAED,+FAA+F;IAC/F,iGAAiG;IACjG,gGAAgG;IAChG,gGAAgG;IAChG,+FAA+F;IAC/F,gFAAgF;IAChF,MAAM,4BAA4B,GAAG,EAAE,CAAC;IACxC,0FAA0F;IAC1F,4FAA4F;IAC5F,8FAA8F;IAC9F,wFAAwF;IACxF,SAAS,0BAA0B,CAAC,SAAiB;QACnD,MAAM,CAAC,GAAG,oBAAoB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC9C,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACjC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,IAAI,GAA0B,EAAE,CAAC;QACvC,IAAI,WAAW,GAAG,sBAAsB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACxD,KAAK,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YAClB,IAAI,CAAC,CAAC,UAAU,KAAK,SAAS,IAAI,GAAG,GAAG,CAAC,CAAC,UAAU,GAAG,sBAAsB,EAAE,CAAC;gBAC9E,IAAI,CAAC,WAAW,EAAE,CAAC;oBAAC,WAAW,GAAG,EAAE,CAAC;oBAAC,sBAAsB,CAAC,GAAG,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;gBAAC,CAAC;gBAC3F,WAAW,CAAC,IAAI,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,YAAY,EAAE,qBAAqB,EAAE,CAAC,CAAC,qBAAqB,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;gBACnH,IAAI,WAAW,CAAC,MAAM,GAAG,4BAA4B,EAAE,CAAC;oBACtD,WAAW,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,CAAC,MAAM,GAAG,4BAA4B,CAAC,CAAC,CAAC,gBAAgB;gBAC5F,CAAC;gBACD,MAAM,CAAC,IAAI,CAAC,yBAAyB,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC,YAAY,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;gBAC3G,+EAA+E;gBAC/E,IAAI,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC;oBAChE,MAAM,CAAC,KAAK,CAAC,uBAAuB,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC,YAAY,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;gBACpG,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACf,CAAC;QACH,CAAC;QACD,oBAAoB,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED,wFAAwF;IACxF,uFAAuF;IACvF,sFAAsF;IACtF,mFAAmF;IACnF,SAAS,+BAA+B,CAAC,KAA8B;QAyBrE,MAAM,GAAG,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC;QAChC,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QACjD,MAAM,CAAC,GAAG,GAA8B,CAAC;QACzC,MAAM,EAAE,GAAG,CAAC,CAAC,eAAe,CAAwC,CAAC;QACrE,MAAM,EAAE,GAAG,CAAC,CAAC,eAAe,CAAwC,CAAC;QACrE,MAAM,YAAY,GAAG,eAAe,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC;QACtD,MAAM,qBAAqB,GAAG,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACxE,MAAM,qBAAqB,GAAG,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACxE,IAAI,CAAC,YAAY,IAAI,CAAC,qBAAqB,IAAI,CAAC,qBAAqB;YAAE,OAAO,IAAI,CAAC;QACnF,mFAAmF;QACnF,mFAAmF;QACnF,MAAM,aAAa,GAAG,CAAC,CAAC,gBAAgB,CAAwC,CAAC;QACjF,MAAM,WAAW,GACf,aAAa,IAAI,OAAO,aAAa,CAAC,SAAS,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAChG,MAAM,UAAU,GACd,aAAa,IAAI,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;YACzD,CAAC,CAAE,aAAa,CAAC,YAAY,CAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC;YAC9F,CAAC,CAAC,EAAE,CAAC;QACT,wEAAwE;QACxE,MAAM,aAAa,GAAG,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAC/C,OAAO;YACL,YAAY;YACZ,qBAAqB;YACrB,qBAAqB;YACrB,eAAe,EACb,OAAO,CAAC,CAAC,2BAA2B,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,2BAA2B,CAAC,CAAC,CAAC,CAAC,EAAE;YAC1F,yBAAyB,EACvB,OAAO,CAAC,CAAC,8BAA8B,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,8BAA8B,CAAC,CAAC,CAAC,CAAC,IAAI;YAClG,gBAAgB,EAAE,OAAO,CAAC,CAAC,mBAAmB,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;YACzF,aAAa,EAAE,OAAO,CAAC,CAAC,gBAAgB,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,IAAI;YACnF,uFAAuF;YACvF,uFAAuF;YACvF,0FAA0F;YAC1F,qEAAqE;YACrE,eAAe,EAAE,CAAC,CAAC,eAAe,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI;YAC9F,WAAW;YACX,UAAU;YACV,cAAc,EAAE,aAAa,CAAC,cAAc;YAC5C,eAAe,EAAE,aAAa,CAAC,QAAQ;YACvC,mBAAmB,EAAE,aAAa,CAAC,MAAM,IAAI,IAAI;SAClD,CAAC;IACJ,CAAC;IAED,4FAA4F;IAC5F,6FAA6F;IAC7F,6FAA6F;IAC7F,gGAAgG;IAChG,6FAA6F;IAC7F,+FAA+F;IAC/F,4DAA4D;IAC5D,KAAK,UAAU,uBAAuB,CAAC,SAAiB,EAAE,SAAS,GAAG,KAAK,EAAE,MAAM,GAAG,EAAE;QACtF,IAAI,kBAAkB,CAAC,wBAAwB,CAAC,SAAS,CAAC;YAAE,OAAO,IAAI,CAAC;QACxE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QACxC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC;YAC7B,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;YAChD,IAAI,kBAAkB,CAAC,wBAAwB,CAAC,SAAS,CAAC;gBAAE,OAAO,IAAI,CAAC;QAC1E,CAAC;QACD,OAAO,kBAAkB,CAAC,wBAAwB,CAAC,SAAS,CAAC,CAAC;IAChE,CAAC;IAED,KAAK,UAAU,uBAAuB,CACpC,MAAuE,EACvE,SAAiB,EACjB,aAAqB;QAErB,IAAI,CAAC;YACH,+FAA+F;YAC/F,iGAAiG;YACjG,+FAA+F;YAC/F,8FAA8F;YAC9F,gFAAgF;YAChF,wFAAwF;YACxF,2FAA2F;YAC3F,4FAA4F;YAC5F,qFAAqF;YACrF,wBAAwB,CAAC,SAAS,CAAC,CAAC;YACpC,MAAM,KAAK,GAAG,kBAAkB,CAAC,iCAAiC,CAAC,SAAS,EAAE,MAAM,CAAC,qBAAqB,CAAC,CAAC;YAC5G,IAAI,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;gBACd,MAAM,CAAC,IAAI,CAAC,+BAA+B,EAAE;oBAC3C,SAAS,EAAE,MAAM,CAAC,YAAY;oBAC9B,SAAS;oBACT,MAAM,EAAE,KAAK,CAAC,MAAM;oBACpB,aAAa;iBACd,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YACD,8FAA8F;YAC9F,0FAA0F;YAC1F,gGAAgG;YAChG,gGAAgG;YAChG,iGAAiG;YACjG,0DAA0D;YAC1D,IAAI,MAAM,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;gBACnC,kBAAkB,CAAC,oBAAoB,CAAC,SAAS,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;YAC1G,CAAC;YACD,qFAAqF;YACrF,oFAAoF;YACpF,iFAAiF;YACjF,sFAAsF;YACtF,KAAK,kBAAkB,CAAC,8BAA8B,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;gBACvF,MAAM,CAAC,IAAI,CAAC,yCAAyC,EAAE;oBACrD,SAAS;oBACT,MAAM,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;oBACxD,aAAa;iBACd,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;YACH,gFAAgF;YAChF,MAAM,KAAK,GAAG,MAAM,uBAAuB,CAAC,SAAS,CAAC,CAAC;YACvD,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,CAAC,IAAI,CAAC,+BAA+B,EAAE;oBAC3C,SAAS,EAAE,MAAM,CAAC,YAAY;oBAC9B,SAAS;oBACT,MAAM,EAAE,+BAA+B;oBACvC,aAAa;iBACd,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YACD,8EAA8E;YAC9E,uFAAuF;YACvF,MAAM,EAAE,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;YACrC,IAAI,WAA2C,CAAC;YAChD,IAAI,EAAE,IAAI,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC7D,WAAW,GAAG;oBACZ,WAAW,EAAE,MAAM,CAAC,WAAW;oBAC/B,UAAU,EAAE,MAAM,CAAC,UAAU;oBAC7B,WAAW,EAAE,EAAE;oBACf,YAAY,EAAE,MAAM,EAAE,CAAC,YAAY,EAAE;oBACrC,cAAc,EAAE,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;iBACxE,CAAC;YACJ,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,aAAa,CACnD,MAAM,CAAC,YAAY,EACnB,SAAS,EACT,MAAM,CAAC,qBAAqB,EAAE,oCAAoC;YAClE,MAAM,CAAC,eAAe,EACtB,aAAa,EACb,WAAW,CACZ,CAAC;YACF,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,CAAC,+BAA+B,EAAE;oBAC3C,SAAS,EAAE,MAAM,CAAC,YAAY;oBAC9B,SAAS;oBACT,MAAM,EAAE,MAAM,CAAC,MAAM;oBACrB,aAAa;iBACd,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YACD,wFAAwF;YACxF,yFAAyF;YACzF,mEAAmE;YACnE,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC;gBAC3B,kBAAkB,CAAC,yBAAyB,CAAC,SAAS,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;YACvG,CAAC;YAED,kFAAkF;YAClF,qFAAqF;YACrF,mFAAmF;YACnF,8DAA8D;YAC9D,MAAM,kBAAkB,GAAG,MAAM,CAAC,IAAI,CACpC,sBAAsB,CACpB,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,qBAAqB,EAAE,KAAK,CAAC,EAChD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,qBAAqB,EAAE,KAAK,CAAC,EAChD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,KAAK,CAAC,EACvC,MAAM,CAAC,gBAAgB,CACxB,CACF,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YAElB,MAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE;gBACtC,SAAS,EAAE,MAAM,CAAC,YAAY;gBAC9B,SAAS;gBACT,aAAa,EAAE,MAAM,CAAC,MAAM;gBAC5B,aAAa;aACd,CAAC,CAAC;YACH,uFAAuF;YACvF,uFAAuF;YACvF,4EAA4E;YAC5E,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC;gBAC3B,MAAM,CAAC,IAAI,CAAC,kBAAkB,EAAE;oBAC9B,SAAS;oBACT,MAAM,EAAE,MAAM,CAAC,qBAAqB;oBACpC,MAAM,EAAE,MAAM,CAAC,mBAAmB,IAAI,SAAS;oBAC/C,aAAa;iBACd,CAAC,CAAC;YACL,CAAC;YACD,uFAAuF;YACvF,mFAAmF;YACnF,wFAAwF;YACxF,gFAAgF;YAChF,IAAI,MAAM,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;gBACnC,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;gBACrF,8FAA8F;gBAC9F,8FAA8F;YAChG,CAAC;YACD,qBAAqB,CAAC,SAAS,EAAE;gBAC/B,YAAY,EAAE,MAAM,CAAC,YAAY;gBACjC,qBAAqB,EAAE,MAAM,CAAC,qBAAqB;gBACnD,kBAAkB;gBAClB,cAAc,EAAE,MAAM,CAAC,cAAc;aACtC,CAAC,CAAC;YACH,uCAAuC,CACrC,SAAS,EACT,MAAM,CAAC,YAAY,EACnB,SAAS,EACT,MAAM,CAAC,qBAAqB,CAC7B,CAAC;YACF,4FAA4F;YAC5F,KAAK,oBAAoB,CAAC,SAAS,EAAE,MAAM,CAAC,YAAY,EAAE,iBAAiB,EAAE,qBAAqB,CAAC,CAAC;YACpG,6FAA6F;YAC7F,6FAA6F;YAC7F,KAAK,gBAAgB,CAAC,SAAS,EAAE,MAAM,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;YACjE,6FAA6F;YAC7F,6FAA6F;YAC7F,uFAAuF;YACvF,8FAA8F;YAC9F,6GAA6G;YAC7G,gGAAgG;YAChG,kGAAkG;YAClG,8EAA8E;QAChF,CAAC;gBAAS,CAAC;YACT,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;QACnE,CAAC;IACH,CAAC;IAED,SAAS,8BAA8B,CAAC,KAA8B;QACpE,qFAAqF;QACrF,MAAM,aAAa,GAAG,UAAU,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,+BAA+B,CAAC,KAAK,CAAC,CAAC;QACtD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,CAAC,IAAI,CAAC,sCAAsC,EAAE;gBAClD,MAAM,EAAE,2BAA2B;gBACnC,aAAa;aACd,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,qFAAqF;QACrF,kFAAkF;QAClF,IAAI,MAAM,CAAC,aAAa,KAAK,QAAQ,EAAE,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,oCAAoC,EAAE;gBAChD,SAAS,EAAE,MAAM,CAAC,YAAY;gBAC9B,MAAM,EAAE,4BAA4B;gBACpC,aAAa;aACd,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,sFAAsF;QACtF,wFAAwF;QACxF,wFAAwF;QACxF,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC;YAC5B,MAAM,CAAC,IAAI,CAAC,sCAAsC,EAAE;gBAClD,SAAS,EAAE,MAAM,CAAC,YAAY;gBAC9B,MAAM,EAAE,2BAA2B;gBACnC,aAAa;aACd,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,kFAAkF;QAClF,gFAAgF;QAChF,6EAA6E;QAC7E,+CAA+C;QAC/C,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,KAAK,MAAM,CAAC,qBAAqB,CAAC,CAAC;QACnF,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,CAAC,KAAK,CAAC,2BAA2B,EAAE;gBACxC,SAAS,EAAE,MAAM,CAAC,YAAY;gBAC9B,kBAAkB,EAAE,MAAM,CAAC,qBAAqB;gBAChD,aAAa;aACd,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,0FAA0F;QAC1F,8FAA8F;QAC9F,+FAA+F;QAC/F,8FAA8F;QAC9F,wFAAwF;QACxF,2FAA2F;QAC3F,wFAAwF;QACxF,gEAAgE;QAChE,IAAI,CAAC,MAAM,CAAC,yBAAyB,EAAE,CAAC;YACtC,MAAM,CAAC,IAAI,CAAC,uCAAuC,EAAE;gBACnD,SAAS,EAAE,UAAU,CAAC,IAAI;gBAC1B,SAAS,EAAE,MAAM,CAAC,YAAY;gBAC9B,aAAa;aACd,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,uFAAuF;QACvF,wFAAwF;QACxF,IAAI,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,kBAAkB,CAAC,gBAAgB,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC;YACrJ,MAAM,CAAC,IAAI,CAAC,mCAAmC,EAAE;gBAC/C,SAAS,EAAE,MAAM,CAAC,YAAY;gBAC9B,SAAS,EAAE,UAAU,CAAC,IAAI;gBAC1B,aAAa;aACd,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QAED,uFAAuF;QACvF,uFAAuF;QACvF,qFAAqF;QACrF,gFAAgF;QAChF,yFAAyF;QACzF,oCAAoC;QACpC,uFAAuF;QACvF,oFAAoF;QACpF,uFAAuF;QACvF,WAAW;QACX,MAAM,CAAC,KAAK,CAAC,uCAAuC,EAAE;YACpD,SAAS,EAAE,MAAM,CAAC,YAAY;YAC9B,SAAS,EAAE,UAAU,CAAC,IAAI;YAC1B,IAAI,EAAE,+HAA+H;YACrI,aAAa;SACd,CAAC,CAAC;QAEH,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;QACpE,sFAAsF;QACtF,oFAAoF;QACpF,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC;QAClC,kBAAkB,GAAG,kBAAkB;aACpC,IAAI,CAAC,GAAG,EAAE,CAAC,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;aACrE,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;YACtB,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;YACvE,MAAM,CAAC,KAAK,CAAC,8BAA8B,EAAE;gBAC3C,SAAS,EAAE,MAAM,CAAC,YAAY;gBAC9B,SAAS;gBACT,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;gBACvD,aAAa;aACd,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;IAED,wFAAwF;IACxF,4FAA4F;IAC5F,0FAA0F;IAC1F,0FAA0F;IAC1F,6FAA6F;IAC7F,wFAAwF;IACxF,wDAAwD;IACxD,kGAAkG;IAClG,oGAAoG;IACpG,qGAAqG;IACrG,oGAAoG;IACpG,iFAAiF;IACjF,KAAK,UAAU,uBAAuB,CACpC,KAA8B,EAC9B,WAAyD,EACzD,GAAqB,EACrB,SAAiB;QAEjB,MAAM,EAAE,GAAG,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAChE,MAAM,UAAU,GAAG,OAAO,KAAK,CAAC,aAAa,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC1F,MAAM,UAAU,GAAG,OAAO,KAAK,CAAC,aAAa,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC1F,MAAM,UAAU,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC;QACvC,IAAI,CAAC,EAAE,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC,UAAU,YAAY,UAAU,CAAC,EAAE,CAAC;YAC7E,iGAAiG;YACjG,0GAA0G;YAC1G,MAAM,CAAC,IAAI,CAAC,+BAA+B,EAAE;gBAC3C,SAAS;gBACT,KAAK,EAAE,CAAC,CAAC,EAAE;gBACX,aAAa,EAAE,CAAC,CAAC,UAAU;gBAC3B,aAAa,EAAE,CAAC,CAAC,UAAU;gBAC3B,YAAY,EAAE,UAAU,YAAY,UAAU;aAC/C,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QACD,IAAI,CAAC,WAAW,CAAC,eAAe,EAAE,CAAC;YACjC,kGAAkG;YAClG,gGAAgG;YAChG,MAAM,CAAC,IAAI,CAAC,oCAAoC,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC,CAAC;YAChG,OAAO;QACT,CAAC;QACD,kGAAkG;QAClG,MAAM,aAAa,GAAG,EAAE,CAAC;QAEzB,IAAI,SAA4B,CAAC;QACjC,IAAI,CAAC;YACH,SAAS,GAAG,MAAM,WAAW,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;QAC5D,CAAC;QAAC,MAAM,CAAC;YACP,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC;QACD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CAAC,iCAAiC,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,CAAC,CAAC;YACzF,OAAO;QACT,CAAC;QACD,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACtE,IAAI,UAAU,KAAK,UAAU,EAAE,CAAC;YAC9B,MAAM,CAAC,KAAK,CAAC,mCAAmC,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,CAAC,CAAC;YAC5F,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,eAAe,CAAC,kBAAkB,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,CAAC;YACtE,KAAK,CAAC,gBAAgB,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;QACxF,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,KAAK,CAAC,kCAAkC,EAAE;gBAC/C,SAAS;gBACT,UAAU;gBACV,aAAa;gBACb,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;aACxD,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,8BAA8B,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;QACtG,6FAA6F;QAC7F,uEAAuE;QACvE,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,CAAC,CAAC;QACnE,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,MAAM,CAAC,IAAI,CAAC,qCAAqC,EAAE;gBACjD,SAAS;gBACT,UAAU;gBACV,aAAa;gBACb,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,aAAa;gBACtC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC1D,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,SAAS,0BAA0B,CAAC,GAAqB;QACvD,GAAG,CAAC,sBAAsB,CAAC,CAAC,KAAK,EAAE,EAAE;YACnC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,0BAA0B;gBAAE,OAAO;YACzD,KAAK,mCAAmC,CAAC,KAAgC,CAAC,CAAC;QAC7E,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,sBAAsB,CAAC,CAAC,KAAK,EAAE,EAAE;YACnC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,oBAAoB;gBAAE,OAAO;YACnD,8BAA8B,CAAC,KAAgC,CAAC,CAAC;QACnE,CAAC,CAAC,CAAC;IACL,CAAC;IACD,8FAA8F;IAC9F,qFAAqF;IACrF,IAAI,eAAe;QAAE,0BAA0B,CAAC,eAAe,CAAC,CAAC;IAEjE,sFAAsF;IACtF,4FAA4F;IAC5F,6FAA6F;IAC7F,+FAA+F;IAC/F,0FAA0F;IAE1F;;;;;;;;OAQG;IACH,SAAS,gBAAgB,CAAC,QAAiC;QACzD,uFAAuF;QACvF,uFAAuF;QACvF,kDAAkD;QAClD,QAAQ,CAAC,GAAG,CAAC,qBAAqB,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,EAAE;YACjE,MAAM,SAAS,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;YAC7C,4FAA4F;YAC5F,gGAAgG;YAChG,iGAAiG;YACjG,MAAM,SAAS,GAAG,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,KAA2B,CAAC,CAAC;YACtF,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,OAAO,yBAAyB,CAAC;YACnC,CAAC;YACD,MAAM,SAAS,GAAG,OAAO,MAAM,EAAE,CAAC,YAAY,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAE,MAAM,CAAC,YAAY,CAAY,CAAC,CAAC,CAAC,MAAM,CAAC;YAEzG,MAAM,UAAU,GAAG,CAAC,CAAsB,EAAE,EAAE,CAAC,CAAC;gBAC9C,IAAI,EAAE,aAAa;gBACnB,UAAU,EAAE,CAAC,CAAC,YAAY;gBAC1B,mBAAmB,EAAE,CAAC,CAAC,qBAAqB;gBAC5C,iBAAiB,EAAE,CAAC,CAAC,kBAAkB;gBACvC,4EAA4E;gBAC5E,eAAe,EAAE,CAAC,CAAC,cAAc,IAAI,IAAI;aAC1C,CAAC,CAAC;YAEH,0BAA0B,CAAC,SAAS,CAAC,CAAC,CAAC,mDAAmD;YAC1F,MAAM,MAAM,GAAG,oBAAoB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACnD,IAAI,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAChC,OAAO,UAAU,CAAC,MAAM,CAAC,KAAK,EAAG,CAAC,CAAC;YACrC,CAAC;YAED,MAAM,KAAK,GAAG,MAAM,IAAI,OAAO,CAA6B,CAAC,OAAO,EAAE,EAAE;gBACtE,MAAM,OAAO,GAAG,qBAAqB,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;gBAC3D,MAAM,MAAM,GAAyB;oBACnC,YAAY;oBACZ,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;wBACb,YAAY,CAAC,KAAK,CAAC,CAAC;wBACpB,OAAO,CAAC,CAAC,CAAC,CAAC;oBACb,CAAC;iBACF,CAAC;gBACF,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBACrB,qBAAqB,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;gBAC9C,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;oBAC5B,MAAM,IAAI,GAAG,qBAAqB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;oBAClD,IAAI,IAAI,EAAE,CAAC;wBACT,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;wBACjC,IAAI,GAAG,KAAK,CAAC,CAAC;4BAAE,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;oBACtC,CAAC;oBACD,OAAO,CAAC,IAAI,CAAC,CAAC;gBAChB,CAAC,EAAE,SAAS,CAAC,CAAC;YAChB,CAAC,CAAC,CAAC;YAEH,IAAI,KAAK,KAAK,IAAI;gBAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;YAC/C,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,OAAO;QACL,gBAAgB;QAChB,0BAA0B;QAC1B,uBAAuB;QACvB,qBAAqB;QACrB,0BAA0B;QAC1B,oBAAoB;QACpB,qBAAqB;QACrB,sBAAsB;QACtB,eAAe;QACf,QAAQ;KACT,CAAC;AACJ,CAAC"}
|