@tinycloud/courier 0.6.0-beta.33 → 0.6.0-beta.34

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/watch.d.ts CHANGED
@@ -12,6 +12,8 @@ export interface PendingWake {
12
12
  approvals?: WakeApproval[];
13
13
  firstSeenAt: number;
14
14
  lastSeenAt: number;
15
+ /** When an authority check first gave no verdict for this wake; cleared by a verdict. */
16
+ deferredSince?: number;
15
17
  }
16
18
  interface NoticeRetry {
17
19
  /** Six digits while dispatching, otherwise six dashes; independent of the display count. */
@@ -34,6 +36,15 @@ interface PauseEpisode extends NoticeRetry {
34
36
  /** Fixed-width saturating count of additional transitions represented by this slot. */
35
37
  repeats: string;
36
38
  }
39
+ /**
40
+ * A peer whose authority checks give no verdict: since when, and once that lasts `DEFERRAL_LIMIT_MS`, the one notice
41
+ * (`eventId`) its human gets for this outage. A verdict ends the outage; a later one notifies again.
42
+ */
43
+ interface UnverifiedPeer extends NoticeRetry {
44
+ since: number;
45
+ eventId: string;
46
+ notified: boolean;
47
+ }
37
48
  /** Allocated only from our own thread/wake/pause records, never from incoming notice ids. */
38
49
  interface ThreadNoticeState {
39
50
  ourPaused: boolean;
@@ -75,7 +86,33 @@ export interface WatchState {
75
86
  noticeGlobalLog: number[];
76
87
  /** Every dispatch attempt, success or failure, per peer for the shared rolling-hour cap. */
77
88
  noticeLog: Record<string, number[]>;
89
+ /** Paired peers whose authority checks currently give no verdict. */
90
+ unverified: Record<string, UnverifiedPeer>;
91
+ }
92
+ /**
93
+ * What a watcher already knows and need not ask the node again, kept on this machine across runs: a `--once` loop
94
+ * starts fresh each time. Losing it only costs the calls it saves.
95
+ */
96
+ export interface LocalWatchCache {
97
+ v: 1;
98
+ /** When wake claims were last pruned (hourly). */
99
+ claimsPrunedAt?: number;
100
+ /** When each peer's renewal marker was last read. */
101
+ markerReadAt: Record<string, number>;
102
+ /** Per peer, its `meta.json` keys already mirrored (create-only). Two peers may use the same name for us, so keys alone are ambiguous. */
103
+ metaMirrored: Record<string, string[]>;
104
+ /** Per peer, `thread/message` ids its receipts already list (receipts only grow). */
105
+ receipted: Record<string, string[]>;
106
+ /** Per peer, the identity (DID, space, session) its facts above were learned under; facts without one are dropped. */
107
+ identities: Record<string, PeerIdentity>;
108
+ }
109
+ export interface PeerIdentity {
110
+ did: string;
111
+ space: string;
112
+ session: string | null;
78
113
  }
114
+ export declare const peerIdentity: (peer: StoredPeer) => PeerIdentity;
115
+ export declare const sameIdentity: (a: PeerIdentity | undefined, b: PeerIdentity) => boolean;
79
116
  /** `tries`: notification dispatch attempts (max 3), or failed inform attempts (max 8). */
80
117
  export type OutboxItem = {
81
118
  id: string;
@@ -86,6 +123,7 @@ export type OutboxItem = {
86
123
  pauseId?: string;
87
124
  tries?: number;
88
125
  nextAttemptAt?: number;
126
+ deferredSince?: number;
89
127
  } | {
90
128
  id: string;
91
129
  kind: 'notify';
@@ -155,6 +193,11 @@ export interface WatchDeps {
155
193
  notificationHome?: string;
156
194
  /** D mode mirrors and receipts, then notifies without invoking a wake adapter. */
157
195
  notifyOnly?: boolean;
196
+ /** This machine's copy of what the watcher already knows (see `LocalWatchCache`). Default: none, kept in memory. */
197
+ localCache?: {
198
+ load(): LocalWatchCache | undefined;
199
+ save(cache: LocalWatchCache): void;
200
+ };
158
201
  /**
159
202
  * Who this watcher is, for wake claims. `profileId` is stable for this profile on this machine (a restart keeps it,
160
203
  * a second profile or machine differs); `pid` and `isAlive` tell a restarted watcher its predecessor is dead.
@@ -189,7 +232,6 @@ export declare class Watcher {
189
232
  private readonly profileId;
190
233
  private readonly pid;
191
234
  private readonly isAlive;
192
- private lastClaimPrune;
193
235
  /** Set when another watcher is writing this profile's state: this one stops and `run` throws it. */
194
236
  private fatalError;
195
237
  /** Set once `run` is told to stop: nothing new is woken after that. */
@@ -197,8 +239,17 @@ export declare class Watcher {
197
239
  private onFatal;
198
240
  /** Etag of this agent's watch.json as this watcher last saw it: null = absent, undefined = unknown (fetch before writing). */
199
241
  private stateEtag;
242
+ /** Per `<peer handle>\n<peer task key>`: two peers may use the same name for us, so a key alone is ambiguous. */
200
243
  private readonly taskCache;
244
+ /** Peer keys whose value is over the batch cap: reported once and not read again by this process. */
245
+ private readonly tooLarge;
201
246
  private readonly metaMirrored;
247
+ private readonly markerReadAt;
248
+ private claimsPrunedAt;
249
+ /** Per peer, the identity the facts above were learned under: a peer that changed identity starts with none. */
250
+ private readonly factIdentity;
251
+ /** The local cache changed since it was last saved. */
252
+ private localDirty;
202
253
  private readonly syncing;
203
254
  /** Peers whose last sync did not finish: a cycle between full catch-ups lists only these again. */
204
255
  private readonly dirtyPeers;
@@ -215,6 +266,12 @@ export declare class Watcher {
215
266
  private readonly now;
216
267
  private readonly keys;
217
268
  constructor(deps: WatchDeps, opts?: WatchOptions);
269
+ /**
270
+ * Before using or learning facts about a peer: if the peer is now a different agent, space or session than the one
271
+ * the facts were learned under, they are dropped, so a new peer's delivery is never skipped on an old peer's facts.
272
+ */
273
+ private bindFacts;
274
+ private saveLocal;
218
275
  /**
219
276
  * Before TC-2917 every agent of the owner shared `me/state/`. On an agent's first start its own keys get a copy of
220
277
  * what concerns its own configured peers (eligible now or not): pause markers first, then watch.json, whose existence
@@ -222,7 +279,11 @@ export declare class Watcher {
222
279
  * keys are left in place.
223
280
  */
224
281
  private migrateLegacyState;
225
- loadState(): Promise<void>;
282
+ /** `preloaded`: this agent's watch.json as just read (saves reading it twice at startup). */
283
+ loadState(preloaded?: {
284
+ value: WatchState;
285
+ etag: string | null;
286
+ }): Promise<void>;
226
287
  snapshot(): WatchState;
227
288
  /**
228
289
  * Serialised: callers mutate `this.state` inside `fn`, then it is written with if-match against the etag this
@@ -246,8 +307,8 @@ export declare class Watcher {
246
307
  private claimWake;
247
308
  /** Drops this run's own claim (best effort): a failed wake must not keep the thread from being woken. */
248
309
  private releaseClaim;
249
- /** Claim markers are only needed while a double wake is possible; drop those older than two days. */
250
- private pruneClaims;
310
+ /** Claim markers are only needed while a double wake is possible; once an hour, drop those older than two days. */
311
+ private pruneClaimsIfDue;
251
312
  /**
252
313
  * Serialises syncs per peer; a request that arrives mid-sync reruns once after it. `pausedListed`: the caller has
253
314
  * just listed the pause markers for this cycle, so the sync shares that listing instead of making its own.
@@ -263,6 +324,14 @@ export declare class Watcher {
263
324
  * a cached one: the 300 s schedule only bounds how soon an idle revocation is noticed. False once the peer is gone.
264
325
  */
265
326
  private authorized;
327
+ /**
328
+ * A check gave no verdict for this peer: the outage starts now if it isn't already running. Once it has lasted
329
+ * `DEFERRAL_LIMIT_MS`, the peer's human is told once (Naomi's TC-2951 ruling: imports keep retrying; wakes and informs
330
+ * deferred that long are dropped). Only configured paired peers allocate a record.
331
+ */
332
+ private noteUnverified;
333
+ /** A verdict ends the outage; a notice not yet sent for it is withdrawn. Called inside mutate. */
334
+ private endUnverified;
266
335
  /** The scheduled renewal and authority check for every peer; each peer's next sync also reads its renewal marker. */
267
336
  private checkAuthority;
268
337
  private syncPeer;