@cello-protocol/daemon 0.0.194 → 0.0.195
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/assignment-verify.d.ts +39 -11
- package/dist/assignment-verify.d.ts.map +1 -1
- package/dist/assignment-verify.js +133 -17
- package/dist/assignment-verify.js.map +1 -1
- package/dist/authorship-verification.d.ts +70 -0
- package/dist/authorship-verification.d.ts.map +1 -0
- package/dist/authorship-verification.js +441 -0
- package/dist/authorship-verification.js.map +1 -0
- package/dist/db-identity-store.d.ts +1 -0
- package/dist/db-identity-store.d.ts.map +1 -1
- package/dist/db-identity-store.js +19 -1
- package/dist/db-identity-store.js.map +1 -1
- package/dist/held-content.d.ts +141 -0
- package/dist/held-content.d.ts.map +1 -0
- package/dist/held-content.js +388 -0
- package/dist/held-content.js.map +1 -0
- package/dist/inbound-refusals.d.ts +283 -0
- package/dist/inbound-refusals.d.ts.map +1 -0
- package/dist/inbound-refusals.js +919 -0
- package/dist/inbound-refusals.js.map +1 -0
- package/dist/inbound-sessions.d.ts.map +1 -1
- package/dist/inbound-sessions.js +65 -27
- package/dist/inbound-sessions.js.map +1 -1
- package/dist/initiate-session-handler.d.ts.map +1 -1
- package/dist/initiate-session-handler.js +63 -0
- package/dist/initiate-session-handler.js.map +1 -1
- package/dist/outbound-sessions.d.ts.map +1 -1
- package/dist/outbound-sessions.js +108 -8
- package/dist/outbound-sessions.js.map +1 -1
- package/dist/park-recovery.d.ts +205 -0
- package/dist/park-recovery.d.ts.map +1 -0
- package/dist/park-recovery.js +600 -0
- package/dist/park-recovery.js.map +1 -0
- package/dist/refusal-notices.d.ts +196 -0
- package/dist/refusal-notices.d.ts.map +1 -0
- package/dist/refusal-notices.js +496 -0
- package/dist/refusal-notices.js.map +1 -0
- package/dist/refusal-reasons.d.ts +11 -0
- package/dist/refusal-reasons.d.ts.map +1 -1
- package/dist/refusal-reasons.js +18 -0
- package/dist/refusal-reasons.js.map +1 -1
- package/dist/registration-manager.d.ts.map +1 -1
- package/dist/registration-manager.js +126 -5
- package/dist/registration-manager.js.map +1 -1
- package/dist/registration-persistence.d.ts +18 -0
- package/dist/registration-persistence.d.ts.map +1 -1
- package/dist/registration-persistence.js +6 -0
- package/dist/registration-persistence.js.map +1 -1
- package/dist/session-assignment-parser.d.ts.map +1 -1
- package/dist/session-assignment-parser.js +23 -1
- package/dist/session-assignment-parser.js.map +1 -1
- package/dist/session-ceremony.d.ts +12 -7
- package/dist/session-ceremony.d.ts.map +1 -1
- package/dist/session-ceremony.js +12 -7
- package/dist/session-ceremony.js.map +1 -1
- package/dist/session-ephemerals.d.ts +271 -0
- package/dist/session-ephemerals.d.ts.map +1 -0
- package/dist/session-ephemerals.js +546 -0
- package/dist/session-ephemerals.js.map +1 -0
- package/dist/session-leaf-records.d.ts +132 -0
- package/dist/session-leaf-records.d.ts.map +1 -0
- package/dist/session-leaf-records.js +320 -0
- package/dist/session-leaf-records.js.map +1 -0
- package/dist/session-liveness.d.ts +135 -0
- package/dist/session-liveness.d.ts.map +1 -0
- package/dist/session-liveness.js +347 -0
- package/dist/session-liveness.js.map +1 -0
- package/dist/session-node-manager.d.ts +300 -1670
- package/dist/session-node-manager.d.ts.map +1 -1
- package/dist/session-node-manager.js +871 -9951
- package/dist/session-node-manager.js.map +1 -1
- package/dist/session-node-types.d.ts +933 -0
- package/dist/session-node-types.d.ts.map +1 -0
- package/dist/session-node-types.js +576 -0
- package/dist/session-node-types.js.map +1 -0
- package/dist/session-queries.d.ts +476 -0
- package/dist/session-queries.d.ts.map +1 -0
- package/dist/session-queries.js +1006 -0
- package/dist/session-queries.js.map +1 -0
- package/dist/session-records.d.ts +320 -0
- package/dist/session-records.d.ts.map +1 -0
- package/dist/session-records.js +792 -0
- package/dist/session-records.js.map +1 -0
- package/dist/session-salts.d.ts +390 -0
- package/dist/session-salts.d.ts.map +1 -0
- package/dist/session-salts.js +1457 -0
- package/dist/session-salts.js.map +1 -0
- package/dist/session-schema.d.ts +30 -0
- package/dist/session-schema.d.ts.map +1 -0
- package/dist/session-schema.js +786 -0
- package/dist/session-schema.js.map +1 -0
- package/dist/standing-receivers.d.ts +280 -0
- package/dist/standing-receivers.d.ts.map +1 -0
- package/dist/standing-receivers.js +1100 -0
- package/dist/standing-receivers.js.map +1 -0
- package/dist/transport-selector.d.ts +13 -0
- package/dist/transport-selector.d.ts.map +1 -1
- package/dist/transport-selector.js.map +1 -1
- package/dist/witness-alerts.d.ts +40 -0
- package/dist/witness-alerts.d.ts.map +1 -0
- package/dist/witness-alerts.js +102 -0
- package/dist/witness-alerts.js.map +1 -0
- package/package.json +5 -5
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CELLO Daemon — REFUSALS THE OPERATOR CAN ACTUALLY SEE
|
|
3
|
+
*
|
|
4
|
+
* Split out of `session-node-manager.ts` by 037-SESSIONCORE.
|
|
5
|
+
*
|
|
6
|
+
* ⚠️ THE POINT OF THIS MODULE IS THAT A REFUSAL HAS A READER. Every inbound refusal already logged a
|
|
7
|
+
* reason, an impact and a guidance, and they were good — and they had no consumer. From the
|
|
8
|
+
* receiving operator's chair a refused message simply never arrives: the conversation goes quiet
|
|
9
|
+
* with a full explanation sitting in a file they have no reason to open, and they conclude the other
|
|
10
|
+
* person stopped replying.
|
|
11
|
+
*
|
|
12
|
+
* DURABLE, and that is the half that makes it useful — a restart must not lose the notice, because
|
|
13
|
+
* the question it answers ("why did they go quiet?") outlives the process that refused.
|
|
14
|
+
*
|
|
15
|
+
* Moved verbatim, comments included.
|
|
16
|
+
*/
|
|
17
|
+
import type { Logger } from "./types.js";
|
|
18
|
+
import type { DaemonDatabase } from "./sqlcipher-db.js";
|
|
19
|
+
import type { SessionQueries } from "./session-queries.js";
|
|
20
|
+
import { type RefusalKind } from "./refusal-reasons.js";
|
|
21
|
+
import { type RefusalNotice } from "./session-node-types.js";
|
|
22
|
+
/** What the refusal surface needs from the manager. */
|
|
23
|
+
export interface RefusalNoticeContext {
|
|
24
|
+
readonly logger: Logger;
|
|
25
|
+
readonly queries: SessionQueries;
|
|
26
|
+
/** A function: the manager opens its database after construction. Re-exposed below as `#db`. */
|
|
27
|
+
db(): DaemonDatabase | null;
|
|
28
|
+
requireAgentId(agentName: string): string;
|
|
29
|
+
sessionKey(agentName: string, sessionId: string): string;
|
|
30
|
+
unkey(key: string, agentName: string): string | null;
|
|
31
|
+
}
|
|
32
|
+
export declare class RefusalNotices {
|
|
33
|
+
#private;
|
|
34
|
+
constructor(ctx: RefusalNoticeContext);
|
|
35
|
+
/**
|
|
36
|
+
* The one read path behind both doors. `sessionId` narrows it; omitted, it spans the agent.
|
|
37
|
+
*
|
|
38
|
+
* A single implementation on purpose: the per-consumer rule and the order-of-magnitude rule are
|
|
39
|
+
* the two properties this unit must not lose, and two copies of them is two things to keep true.
|
|
40
|
+
*/
|
|
41
|
+
drainRefusals(agentName: string, consumerId: string, sessionId?: string): {
|
|
42
|
+
notices: RefusalNotice[];
|
|
43
|
+
truncated: boolean;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* ─── DOD-M15-NO-SILENT-REFUSAL-1: refusals the RECEIVING operator can actually see ────────────
|
|
47
|
+
*
|
|
48
|
+
* Every inbound refusal already logs a `reason`, an `impact` and a `guidance` — and they are
|
|
49
|
+
* good. They had no reader. From the receiving operator's chair a refused message simply never
|
|
50
|
+
* arrives: the conversation goes quiet with a full explanation sitting in a file they have no
|
|
51
|
+
* reason to open, and they conclude the other person stopped replying.
|
|
52
|
+
*
|
|
53
|
+
* **DURABLE, and that is the half that makes this useful.** The predecessor kept notices in a
|
|
54
|
+
* `Map` on this instance and drained them on the receive path for one session. So a restart lost
|
|
55
|
+
* them, and an agent NOBODY IS ATTENDING lost them too — the connection is live, the daemon is
|
|
56
|
+
* up, and the notice only ever reaches whoever happens to call `cello_receive` on that exact
|
|
57
|
+
* session. `cello_check_notifications` now reads them as its own inbox category.
|
|
58
|
+
*
|
|
59
|
+
* **DEDUPLICATED PER SESSION PER REASON, and that is the design, not an optimisation.** A skewed
|
|
60
|
+
* peer turns one problem into a flood: the first refusal of a kind is the signal, the ninetieth is
|
|
61
|
+
* noise that trains the operator to ignore the surface. `count` keeps the scale visible without
|
|
62
|
+
* repeating the alert.
|
|
63
|
+
*
|
|
64
|
+
* **NEVER carries the content.** It failed verification; surfacing it is the injection path the
|
|
65
|
+
* cross-check exists to close. The operator learns that a message was refused and why — never
|
|
66
|
+
* what it said.
|
|
67
|
+
*/
|
|
68
|
+
/**
|
|
69
|
+
* Record an inbound refusal for the operator. First of its kind per session is the signal.
|
|
70
|
+
*
|
|
71
|
+
* ⚠️ **DOES NOT THROW, and that is a decision with a cost — stated so it is not mistaken for an
|
|
72
|
+
* oversight.** Every call site here has already decided to refuse and is about to return a reason
|
|
73
|
+
* to its caller; a throw would replace that clean refusal with an exception on the ingest path,
|
|
74
|
+
* changing what the SENDER observes because this daemon could not file a note. So a persistence
|
|
75
|
+
* failure is logged at ERROR under `session.refusal.persist.failed`, carrying the reason, the
|
|
76
|
+
* impact and the guidance verbatim — the forensic record survives even when the operator-facing
|
|
77
|
+
* one does not. It is not silent; it is one surface short, and the log says which notice was lost.
|
|
78
|
+
*/
|
|
79
|
+
noteContentRefusal(agentName: string, sessionId: string, reason: string,
|
|
80
|
+
/**
|
|
81
|
+
* ALL THREE REQUIRED, and that is the enforcement rather than the convention.
|
|
82
|
+
*
|
|
83
|
+
* The DoD clause is "every reason calls this with an impact and a guidance", and an optional
|
|
84
|
+
* field makes that a thing a reviewer checks by reading thirteen call sites. `kind` is required
|
|
85
|
+
* for the same reason one level up: the header over a list of refusals is composed from it, and
|
|
86
|
+
* a notice that could omit it would silently inherit whichever header happened to be first.
|
|
87
|
+
*/
|
|
88
|
+
detail: {
|
|
89
|
+
kind: RefusalKind;
|
|
90
|
+
impact: string;
|
|
91
|
+
guidance: string;
|
|
92
|
+
}): void;
|
|
93
|
+
/**
|
|
94
|
+
* DOD-M15-NO-SILENT-REFUSAL-1 — the per-session byte cap, from the operator's chair.
|
|
95
|
+
*
|
|
96
|
+
* This is the harshest refusal on the inbound path and the one that reads least like a fault:
|
|
97
|
+
* once the cap is crossed, EVERY later message from that sender on that session is refused, for
|
|
98
|
+
* the life of the session. The counterparty is told nothing either, so from both chairs the other
|
|
99
|
+
* person simply stopped replying.
|
|
100
|
+
*
|
|
101
|
+
* One method rather than two copies because the cap is checked twice — once before the screening
|
|
102
|
+
* await and once after, against freshly-read totals — and a notice that differs between the two
|
|
103
|
+
* would describe a different refusal depending on timing.
|
|
104
|
+
*/
|
|
105
|
+
/**
|
|
106
|
+
* DOD-M15-REFUSEDEVIDENCE-1 — **THE BYTE CAP RETAINS NOTHING, and that is a ruling, not an
|
|
107
|
+
* oversight.**
|
|
108
|
+
*
|
|
109
|
+
* Retention is universal everywhere else in this method. Here it is not, because retaining would
|
|
110
|
+
* defeat the very bound it enforces: a session already over its storage budget cannot be given
|
|
111
|
+
* more storage as a reward for exceeding it, and `#getReceivedBytesTotal` counts quarantined bytes
|
|
112
|
+
* precisely so that budget is honest.
|
|
113
|
+
*
|
|
114
|
+
* Andre, 2026-09-03: *"The message limit is the message limit, already handled by the cap. If
|
|
115
|
+
* you're unknown and you have 25 MB and you just tried to send me one gig, well that's it."*
|
|
116
|
+
*
|
|
117
|
+
* The ABUSE is still evidenced — this notice records the reason, the cap and the tier, and every
|
|
118
|
+
* message the session did retain is still there. What is not kept is the oversized payload.
|
|
119
|
+
*/
|
|
120
|
+
noteSizeCapRefusal(agentName: string, sessionId: string, cap: number, tier: number): void;
|
|
121
|
+
/**
|
|
122
|
+
* Drain the refusals a GIVEN CONSUMER has not been shown yet, and remember what it was shown.
|
|
123
|
+
*
|
|
124
|
+
* ─── Why this is keyed by consumer, and not by a single flag ──────────────────────────────────
|
|
125
|
+
*
|
|
126
|
+
* It used to set one `surfaced: boolean` on the notice. Two MCP windows attending the same agent
|
|
127
|
+
* is the ordinary case, and under that flag whoever read FIRST consumed the notice — the second
|
|
128
|
+
* window was told nothing, permanently. **That is the same defect `takeReceivedContent` had**, and
|
|
129
|
+
* the comment above the delivery loop in `session-content-handlers.ts` spells out why it was
|
|
130
|
+
* removed: *"reading is non-destructive by construction. Nothing one consumer does mutates state
|
|
131
|
+
* another consumer reads."*
|
|
132
|
+
*
|
|
133
|
+
* ─── Why the count has a reader ───────────────────────────────────────────────────────────────
|
|
134
|
+
*
|
|
135
|
+
* A reason RE-ANNOUNCES to a consumer when its count has grown by an order of magnitude since that
|
|
136
|
+
* consumer last saw it (1 → 10 → 100 → …), marked `repeat: true`. That keeps the first refusal the
|
|
137
|
+
* signal and the ninetieth silent, which is the dedup's point, while still making a skew that has
|
|
138
|
+
* swallowed hundreds of messages visible — at a handful of announcements per session, not one per
|
|
139
|
+
* message.
|
|
140
|
+
*
|
|
141
|
+
* ─── What a restart does, deliberately ────────────────────────────────────────────────────────
|
|
142
|
+
*
|
|
143
|
+
* The notices survive; the read state is keyed by IPC connection id, which does not. So after a
|
|
144
|
+
* restart every notice is unseen again and the next reader is told. That is the correct direction:
|
|
145
|
+
* a fresh window has not been told anything, and re-announcing costs one line where staying silent
|
|
146
|
+
* costs the whole point of storing it.
|
|
147
|
+
*/
|
|
148
|
+
takeContentRefusals(agentName: string, sessionId: string,
|
|
149
|
+
/**
|
|
150
|
+
* REQUIRED, deliberately — no default.
|
|
151
|
+
*
|
|
152
|
+
* It had one (`"default"`), and a default is the defect this method was rewritten to remove,
|
|
153
|
+
* lying in wait: any future call site that omits the argument silently shares ONE bucket across
|
|
154
|
+
* every window, the first reader consumes the notice for all the others, and nothing fails to
|
|
155
|
+
* compile and no test goes red. The parameter existing is not the protection; being unable to
|
|
156
|
+
* forget it is.
|
|
157
|
+
*/
|
|
158
|
+
consumerId: string): Array<Omit<RefusalNotice, "sessionId">>;
|
|
159
|
+
/**
|
|
160
|
+
* DOD-M15-NO-SILENT-REFUSAL-1: the operator has seen these and does not want to see them again.
|
|
161
|
+
*
|
|
162
|
+
* ⚠️ **WITHOUT THIS THE NOTICES ARE PERMANENT, and that is what makes people stop reading the
|
|
163
|
+
* inbox.** "Already shown you" is tracked per WINDOW — a new MCP connection has been told nothing,
|
|
164
|
+
* so it is told everything. Someone on an older build messages you, you sort it out with them,
|
|
165
|
+
* they upgrade, and every new session you ever open still opens with that refusal.
|
|
166
|
+
*
|
|
167
|
+
* Dismissing does NOT turn anything off. If the cause fires again the notice comes back, because
|
|
168
|
+
* a fresh refusal writes a fresh row. The operator is saying "I know", not "stop telling me".
|
|
169
|
+
*
|
|
170
|
+
* Returns how many were cleared, so the caller can say so rather than claiming a silent success.
|
|
171
|
+
*/
|
|
172
|
+
dismissContentRefusals(agentName: string, sessionId: string): number;
|
|
173
|
+
/**
|
|
174
|
+
* DOD-M15-NO-SILENT-REFUSAL-1: every unshown refusal for an agent, ACROSS its sessions.
|
|
175
|
+
*
|
|
176
|
+
* The inbox's door. `takeContentRefusals` answers for one session because its caller already holds
|
|
177
|
+
* one; `cello_check_notifications` holds an agent and nothing else, and the case this whole line
|
|
178
|
+
* exists for is that nobody is attending any of that agent's sessions — so a per-session read
|
|
179
|
+
* cannot reach it. Same store, same per-consumer rule, same re-announce.
|
|
180
|
+
*/
|
|
181
|
+
takeAgentContentRefusals(agentName: string, consumerId: string): {
|
|
182
|
+
notices: RefusalNotice[];
|
|
183
|
+
truncated: boolean;
|
|
184
|
+
};
|
|
185
|
+
/**
|
|
186
|
+
* Drop the IN-MEMORY fallback notices for one session.
|
|
187
|
+
*
|
|
188
|
+
* ⚠️ THE DURABLE ROWS ARE NOT TOUCHED, and the omission is deliberate rather than forgotten. They
|
|
189
|
+
* live in `content_refusal_notices` keyed on agent_id + session_id, and the question they answer —
|
|
190
|
+
* "why did my counterparty go quiet?" — outlives the session that produced it. Only the in-memory
|
|
191
|
+
* fallback, which exists for the case where the write to disk failed, is dropped here; leaving it
|
|
192
|
+
* would let a store that is already in trouble grow without bound in memory as well.
|
|
193
|
+
*/
|
|
194
|
+
evictSession(agentName: string, sessionId: string): void;
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=refusal-notices.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refusal-notices.d.ts","sourceRoot":"","sources":["../src/refusal-notices.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGvE,OAAO,EACL,KAAK,aAAa,EAGnB,MAAM,yBAAyB,CAAC;AAEjC,uDAAuD;AACvD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,gGAAgG;IAChG,EAAE,IAAI,cAAc,GAAG,IAAI,CAAC;IAC5B,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1C,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;IACzD,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;CACtD;AAED,qBAAa,cAAc;;gBAGb,GAAG,EAAE,oBAAoB;IAkBrC;;;;;OAKG;IACH,aAAa,CACX,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,SAAS,CAAC,EAAE,MAAM,GACjB;QAAE,OAAO,EAAE,aAAa,EAAE,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE;IAgLnD;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH;;;;;;;;;;OAUG;IACH,kBAAkB,CAChB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM;IACd;;;;;;;OAOG;IACH,MAAM,EAAE;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAC9D,IAAI;IA0FP;;;;;;;;;;;OAWG;IACH;;;;;;;;;;;;;;OAcG;IACH,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAuBzF;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,mBAAmB,CACjB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM;IACjB;;;;;;;;OAQG;IACH,UAAU,EAAE,MAAM,GACjB,KAAK,CAAC,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC;IAS1C;;;;;;;;;;;;OAYG;IACH,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAapE;;;;;;;OAOG;IACH,wBAAwB,CACtB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB;QAAE,OAAO,EAAE,aAAa,EAAE,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE;IAInD;;;;;;;;OAQG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;CAGzD"}
|