stitchkit 0.108.5 → 0.109.0

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.
Files changed (43) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/browser/cancellation.d.ts +4 -5
  3. package/dist/browser/cancellation.d.ts.map +1 -1
  4. package/dist/browser/client-executors.d.ts.map +1 -1
  5. package/dist/browser/http.d.ts.map +1 -1
  6. package/dist/entrypoints/cli.js +1 -1
  7. package/dist/entrypoints/index.js +2 -2
  8. package/dist/entrypoints/react.js +1 -1
  9. package/dist/entrypoints/remote.js +2 -2
  10. package/dist/entrypoints/telegram.d.ts +2 -2
  11. package/dist/entrypoints/telegram.d.ts.map +1 -1
  12. package/dist/entrypoints/telegram.js +868 -137
  13. package/dist/entrypoints/testing.js +2 -2
  14. package/dist/entrypoints/tools.js +1 -1
  15. package/dist/{index-7jvwrjqa.js → index-00v1qz76.js} +20 -17
  16. package/dist/{index-05s01c94.js → index-5cw390s6.js} +96 -16
  17. package/dist/{index-p5kx5vqd.js → index-e32vy8jw.js} +22 -11
  18. package/dist/telegram/update-intake-work.d.ts +39 -0
  19. package/dist/telegram/update-intake-work.d.ts.map +1 -0
  20. package/dist/telegram/update-intake.d.ts +47 -9
  21. package/dist/telegram/update-intake.d.ts.map +1 -1
  22. package/dist/telegram/update-store-check.d.ts.map +1 -1
  23. package/dist/telegram/update-store-postgres.d.ts +2 -2
  24. package/dist/telegram/update-store-postgres.d.ts.map +1 -1
  25. package/dist/telegram/update-store-sqlite.d.ts +2 -2
  26. package/dist/telegram/update-store-sqlite.d.ts.map +1 -1
  27. package/dist/telegram/update-store.d.ts +52 -3
  28. package/dist/telegram/update-store.d.ts.map +1 -1
  29. package/dist/tools/cli/args-fields.d.ts +7 -1
  30. package/dist/tools/cli/args-fields.d.ts.map +1 -1
  31. package/dist/tools/cli/args.d.ts.map +1 -1
  32. package/llms/cli.txt +9 -1
  33. package/llms/stitchkit.txt +10 -3
  34. package/llms/telegram.txt +59 -8
  35. package/llms/{upgrading-0.100.0-0.108.0.txt → upgrading-0.101.0-0.109.0.txt} +70 -72
  36. package/llms/upgrading-0.37.0.txt +176 -0
  37. package/llms/{upgrading-0.37.0-0.58.0.txt → upgrading-0.44.0-0.60.0.txt} +229 -169
  38. package/llms/{upgrading-0.59.0-0.71.0.txt → upgrading-0.61.0-0.73.0.txt} +122 -229
  39. package/llms/{upgrading-0.72.0-0.99.0.txt → upgrading-0.74.0-0.100.0.txt} +72 -122
  40. package/llms/upgrading.txt +5 -4
  41. package/llms-full.txt +145 -12
  42. package/llms.txt +9 -8
  43. package/package.json +1 -1
@@ -12,7 +12,10 @@
12
12
  * whose attempts are spent as `abandoned`;
13
13
  * - `renew` and `settle` act only while the record is still `processing` under
14
14
  * the same attempt, so a process that lost its lease cannot overwrite the
15
- * one that took it over.
15
+ * one that took it over;
16
+ * - a durable abandonment keeps `settledAt` empty until the consumer has
17
+ * handled its final obligation. It stays in this record and is never pruned
18
+ * while it is pending.
16
19
  */
17
20
  export type TelegramUpdateState = 'pending' | 'processing' | 'failed' | 'completed' | 'abandoned';
18
21
  export type TelegramUpdateSettlement = {
@@ -32,6 +35,8 @@ export interface TelegramUpdateClaimOptions {
32
35
  readonly now: number;
33
36
  readonly leaseUntil: number;
34
37
  readonly maxAttempts: number;
38
+ /** Keep an attempts-spent abandonment pending until the consumer acknowledges it. */
39
+ readonly durableExhaustion?: boolean;
35
40
  }
36
41
  export interface TelegramUpdateDueQuery {
37
42
  readonly now: number;
@@ -43,6 +48,23 @@ export interface StoredTelegramUpdate {
43
48
  readonly updateId: number;
44
49
  readonly body: string;
45
50
  }
51
+ /** The immutable identity of one claim, including a token that never repeats after pruning. */
52
+ export interface TelegramUpdateAttemptIdentity {
53
+ readonly updateId: number;
54
+ readonly attempt: number;
55
+ readonly claimId: string;
56
+ }
57
+ /** A final failure waiting for the consumer's durable obligation. */
58
+ export interface StoredTelegramUpdateExhaustion extends StoredTelegramUpdate, TelegramUpdateAttemptIdentity {
59
+ readonly exhaustedAt: number;
60
+ readonly error: string;
61
+ }
62
+ /** One ordered page of final failures still waiting for acknowledgement. */
63
+ export interface TelegramUpdateExhaustionQuery {
64
+ readonly limit: number;
65
+ /** Continue strictly after this `update_id`; omit to begin at the first row. */
66
+ readonly afterUpdateId?: number;
67
+ }
46
68
  export interface TelegramUpdateStore {
47
69
  /** Record a received update as `pending`; `false` when this `update_id` is already recorded. */
48
70
  add(update: StoredTelegramUpdate & {
@@ -51,7 +73,9 @@ export interface TelegramUpdateStore {
51
73
  /** Take a claimable update for one attempt; its number, or `undefined` when not taken. */
52
74
  claim(updateId: number, options: TelegramUpdateClaimOptions): Promise<number | undefined>;
53
75
  /** Extend the lease of this attempt; `false` when the attempt no longer holds the update. */
54
- renew(updateId: number, attempt: number, leaseUntil: number): Promise<boolean>;
76
+ renew(updateId: number, attempt: number, leaseUntil: number,
77
+ /** When supplied, a lease that already expired cannot be resurrected. */
78
+ now?: number): Promise<boolean>;
55
79
  /** End this attempt; ignored when the attempt no longer holds the update. */
56
80
  settle(updateId: number, attempt: number, settlement: TelegramUpdateSettlement): Promise<void>;
57
81
  /** Claimable updates in `update_id` order. */
@@ -59,9 +83,34 @@ export interface TelegramUpdateStore {
59
83
  /** Forget `completed` and `abandoned` updates settled before `before`; how many were forgotten. */
60
84
  prune(before: number): Promise<number>;
61
85
  }
86
+ /** A store able to verify an immutable attempt fence inside the caller's transaction. */
87
+ export interface TelegramUpdateFencedStore extends TelegramUpdateStore {
88
+ /** Atomically take one claim and return its persisted, globally unique identity. */
89
+ claimOwned(updateId: number, options: TelegramUpdateClaimOptions): Promise<TelegramUpdateAttemptIdentity | undefined>;
90
+ /** Extend only this exact claim's live lease. */
91
+ renewOwned(identity: TelegramUpdateAttemptIdentity, leaseUntil: number, now?: number): Promise<boolean>;
92
+ /** `true` only while this exact attempt still owns an unexpired lease. */
93
+ owns(identity: TelegramUpdateAttemptIdentity, at: number): Promise<boolean>;
94
+ /** Atomically settle only while this exact attempt still owns an unexpired lease. */
95
+ settleOwned(identity: TelegramUpdateAttemptIdentity, settlement: TelegramUpdateSettlement): Promise<boolean>;
96
+ }
97
+ /**
98
+ * The same update store with recoverable final-failure delivery.
99
+ *
100
+ * `exhaust` leaves the abandonment pending, `dueExhaustions` reads it after a
101
+ * restart, and `acknowledgeExhaustion` makes it eligible for ordinary pruning.
102
+ */
103
+ export interface TelegramUpdateDurableStore extends TelegramUpdateFencedStore {
104
+ exhaust(identity: TelegramUpdateAttemptIdentity, exhaustion: {
105
+ readonly at: number;
106
+ readonly error: string;
107
+ }): Promise<boolean>;
108
+ dueExhaustions(query: TelegramUpdateExhaustionQuery): Promise<readonly StoredTelegramUpdateExhaustion[]>;
109
+ acknowledgeExhaustion(identity: TelegramUpdateAttemptIdentity, at: number): Promise<boolean>;
110
+ }
62
111
  /**
63
112
  * A store in this process's memory — for tests, and for a bot that accepts
64
113
  * losing what was received but not handled when it restarts.
65
114
  */
66
- export declare function memoryTelegramUpdateStore(): TelegramUpdateStore;
115
+ export declare function memoryTelegramUpdateStore(): TelegramUpdateDurableStore;
67
116
  //# sourceMappingURL=update-store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"update-store.d.ts","sourceRoot":"","sources":["../../src/telegram/update-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,MAAM,mBAAmB,GAC3B,SAAS,GACT,YAAY,GACZ,QAAQ,GACR,WAAW,GACX,WAAW,CAAC;AAEhB,MAAM,MAAM,wBAAwB,GAChC;IAAE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GACpD;IACE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB,GACD;IAAE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjF,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,kGAAkG;IAClG,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,gGAAgG;IAChG,GAAG,CAAC,MAAM,EAAE,oBAAoB,GAAG;QAAE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtF,0FAA0F;IAC1F,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC1F,6FAA6F;IAC7F,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/E,6EAA6E;IAC7E,MAAM,CACJ,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,wBAAwB,GACnC,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,8CAA8C;IAC9C,GAAG,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,SAAS,oBAAoB,EAAE,CAAC,CAAC;IAC7E,mGAAmG;IACnG,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACxC;AAsBD;;;GAGG;AACH,wBAAgB,yBAAyB,IAAI,mBAAmB,CAsE/D"}
1
+ {"version":3,"file":"update-store.d.ts","sourceRoot":"","sources":["../../src/telegram/update-store.ts"],"names":[],"mappings":"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"}
@@ -21,6 +21,12 @@ export declare function appendCliOptionValue(raw: Map<string, string[]>, name: s
21
21
  * scalar schema yields an empty map and every value is left as a string.
22
22
  */
23
23
  export declare function describeSchemaFields(schema: z.ZodType | undefined): Map<string, FieldInfo>;
24
+ /**
25
+ * Resolve the declared leaf of a dotted option. An intersection's concrete
26
+ * constraint can refine a free member; a union with a free member stays raw,
27
+ * because coercion must not choose one accepted branch for the caller.
28
+ */
29
+ export declare function describeSchemaPath(schema: z.ZodType | undefined, path: readonly string[]): FieldInfo | undefined;
24
30
  /** Strict boolean for a RESERVED option — an unrecognised value is a usage error, never a silent `true`. */
25
31
  /**
26
32
  * The token after a bare boolean flag, when it is the flag's value: exactly
@@ -33,7 +39,7 @@ export declare function separateBoolValue(next: string | undefined): boolean | u
33
39
  /** True for a value the reserved-boolean grammar already claims. */
34
40
  export declare function isReservedBoolWord(value: string): boolean;
35
41
  export declare function parseReservedBool(name: string, value: string): boolean;
36
- /** Best-effort coercion for a dotted-path leaf, where the schema type is unknown. */
42
+ /** Preserve the legacy best-effort grammar for a wholly unknown passthrough option. */
37
43
  export declare function looseCoerce(value: string): unknown;
38
44
  /** Coerce the collected raw string value(s) for one field to its typed form. */
39
45
  export declare function coerceField(info: FieldInfo | undefined, values: string[], label: string): unknown;
@@ -1 +1 @@
1
- {"version":3,"file":"args-fields.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/args-fields.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,MAAM,MAAM,SAAS,GACjB,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,MAAM,GACN,OAAO,GACP,QAAQ,GACR,OAAO,CAAC;AAEZ,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,uEAAuE;IACvE,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,iFAAiF;IACjF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,4EAA4E;AAC5E,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAC1B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,SAAS,GAAG,SAAS,GAC1B,IAAI,CAaN;AAwGD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAI1F;AAKD,4GAA4G;AAC5G;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAI/E;AAED,oEAAoE;AACpE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGzD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAKtE;AA8BD,qFAAqF;AACrF,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAKlD;AAuCD,gFAAgF;AAChF,wBAAgB,WAAW,CACzB,IAAI,EAAE,SAAS,GAAG,SAAS,EAC3B,MAAM,EAAE,MAAM,EAAE,EAChB,KAAK,EAAE,MAAM,GACZ,OAAO,CAaT;AAED,wBAAgB,SAAS,CACvB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,EAAE,MAAM,EAAE,EACd,KAAK,EAAE,OAAO,GACb,IAAI,CAsBN"}
1
+ {"version":3,"file":"args-fields.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/args-fields.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,MAAM,MAAM,SAAS,GACjB,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,MAAM,GACN,OAAO,GACP,QAAQ,GACR,OAAO,CAAC;AAEZ,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,SAAS,CAAC;IAChB,uEAAuE;IACvE,WAAW,CAAC,EAAE,SAAS,CAAC;IACxB,iFAAiF;IACjF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,4EAA4E;AAC5E,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAC1B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,SAAS,GAAG,SAAS,GAC1B,IAAI,CAaN;AA6GD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAI1F;AAiFD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,SAAS,EAC7B,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,SAAS,GAAG,SAAS,CAGvB;AAKD,4GAA4G;AAC5G;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAI/E;AAED,oEAAoE;AACpE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGzD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAKtE;AA8BD,uFAAuF;AACvF,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAKlD;AAuCD,gFAAgF;AAChF,wBAAgB,WAAW,CACzB,IAAI,EAAE,SAAS,GAAG,SAAS,EAC3B,MAAM,EAAE,MAAM,EAAE,EAChB,KAAK,EAAE,MAAM,GACZ,OAAO,CAaT;AAED,wBAAgB,SAAS,CACvB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,EAAE,MAAM,EAAE,EACd,KAAK,EAAE,OAAO,GACb,IAAI,CAsBN"}
@@ -1 +1 @@
1
- {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/args.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAkBxB,OAAO,EAAE,KAAK,aAAa,EAAkB,MAAM,aAAa,CAAC;AAGjE,yEAAyE;AACzE,MAAM,WAAW,aAAa;IAC5B,sEAAsE;IACtE,IAAI,EAAE,OAAO,CAAC;IACd,iEAAiE;IACjE,IAAI,EAAE,OAAO,CAAC;IACd,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,KAAK,EAAE,OAAO,CAAC;IACf,qEAAqE;IACrE,MAAM,EAAE,OAAO,CAAC;IAChB,qDAAqD;IACrD,IAAI,EAAE,OAAO,CAAC;IACd,0FAA0F;IAC1F,IAAI,CAAC,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,+BAA+B;IAC/B,OAAO,EAAE,aAAa,CAAC;CACxB;AAsRD,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,SAAS,EAC7B,MAAM,GAAE;IACN,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,aAAa,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B,GACL,aAAa,CAUf;AAED,iFAAiF;AACjF,MAAM,WAAW,qBAAqB;IACpC,0EAA0E;IAC1E,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AASD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,MAAM,EAAE,CAAC,CAAC,SAAS,GAAG,SAAS,GAC9B,qBAAqB,CAkDvB"}
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/args.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAoBxB,OAAO,EAAE,KAAK,aAAa,EAAkB,MAAM,aAAa,CAAC;AAGjE,yEAAyE;AACzE,MAAM,WAAW,aAAa;IAC5B,sEAAsE;IACtE,IAAI,EAAE,OAAO,CAAC;IACd,iEAAiE;IACjE,IAAI,EAAE,OAAO,CAAC;IACd,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yDAAyD;IACzD,KAAK,EAAE,OAAO,CAAC;IACf,qEAAqE;IACrE,MAAM,EAAE,OAAO,CAAC;IAChB,qDAAqD;IACrD,IAAI,EAAE,OAAO,CAAC;IACd,0FAA0F;IAC1F,IAAI,CAAC,EAAE,aAAa,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,+BAA+B;IAC/B,OAAO,EAAE,aAAa,CAAC;CACxB;AAiSD,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EAAE,EACd,MAAM,EAAE,CAAC,CAAC,OAAO,GAAG,SAAS,EAC7B,MAAM,GAAE;IACN,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,aAAa,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC5B,GACL,aAAa,CAsBf;AAED,iFAAiF;AACjF,MAAM,WAAW,qBAAqB;IACpC,0EAA0E;IAC1E,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AASD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,MAAM,EAAE,CAAC,CAAC,SAAS,GAAG,SAAS,GAC9B,qBAAqB,CAkDvB"}
package/llms/cli.txt CHANGED
@@ -244,7 +244,7 @@ string, the schema says what it should be:
244
244
  | `z.enum([...])` | `--size large` |
245
245
  | `z.array(z.string())`| `--tag a --tag b` → `["a","b"]` |
246
246
  | `z.object({...})` | `--opts '{"k":"v"}'` (JSON) or `--opts.k v` |
247
- | `z.record(z.string(), z.unknown())` | `--data '{"code":"007"}'` (JSON) |
247
+ | `z.record(z.string(), z.unknown())` | `--data '{"code":"007"}'` or `--data.code=007` → `"007"` |
248
248
  | `.optional()` / `.default()` | not required |
249
249
 
250
250
  Primitive literals use the same coercion through optional, nullable and default wrappers and as
@@ -255,6 +255,14 @@ Object and record JSON keeps the same behavior when a CLI discovers a tool throu
255
255
  `propertyNames`, `minProperties` and `maxProperties` constraints still run before dispatch; CLI
256
256
  coercion does not execute application transforms.
257
257
 
258
+ A dotted leaf follows the leaf schema, including through optional/nullable wrappers and restored
259
+ MCP object guards. A declared number or boolean is coerced; a string stays byte-for-byte lexical.
260
+ For a free `z.record(z.string(), z.unknown())`, argv provides no JSON type tag, so the dotted
261
+ value also stays a string: `007`, `+007`, `1e3`, `true` and `false` keep those spellings.
262
+ Use the whole-object JSON form when a free record value must be a JSON number or boolean. This
263
+ avoids silently changing identifiers while keeping `z.record(..., z.number())` and
264
+ `z.record(..., z.boolean())` explicitly typed.
265
+
258
266
  After a bare boolean flag, the next token is its value when it is exactly
259
267
  `true` or `false` — `--active false` turns it off, as it reads. Any other word
260
268
  there stays a positional, and `--` makes a literal `false` one too. The same
@@ -83,12 +83,12 @@ own, recorded as an ADR.
83
83
  | `stitchkit/geo` | server (Bun or Node) | evolving | managed GeoIP reader generations, last-known-good reload and the optional MaxMind adapter |
84
84
  | `stitchkit/oauth` | browser **and** server | evolving | provider-neutral Authorization Code + PKCE request and one-shot callback transaction mechanics |
85
85
  | `stitchkit/google` | server (Bun or Node) | evolving | optional Google code exchange and verified OIDC identity adapter |
86
- | `stitchkit/observability` | server | stable<br>_redefined in 2 of the 53 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
86
+ | `stitchkit/observability` | server | stable<br>_redefined in 2 of the 54 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
87
87
  | `stitchkit/testing` | tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, the managed-resource conformance kit and manual time for schedules and retries |
88
88
  | `stitchkit/declaration` | browser + build and deployment tooling (Bun or Node) | evolving | `ProjectDeclarationSchema` — the one machine-readable statement a repository makes about itself |
89
89
  | `stitchkit/react` | browser + server rendering | stable | `createCursorQuery`, `createCacheBridge`, QueryClient and `ApiError` retry policy |
90
90
  | `stitchkit/react/keyboard` | browser | evolving | which part of a screen gets a key — ordered layers, one Escape per level, list zones with real focus; see [keyboard guide](keyboard.md) |
91
- | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 23 of the 53 minors since 0.56.2, most recently 0.105.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
91
+ | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 23 of the 54 minors since 0.56.2, most recently 0.105.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
92
92
  | `stitchkit/agent-runtime/sandbox` | server | evolving | optional Linux Bubblewrap sessions, durable workspaces and a host HTTP credential gateway; see [sandbox guide](sandbox.md) |
93
93
  | `stitchkit/agent-runtime/testing` | tests on Bun or Node | evolving | credential-free replay, scripted provider faults, deterministic race controls and the agent-store conformance kit |
94
94
  | `stitchkit/agent-runtime/harness` | server | evolving | resource-aware process-local facade over the canonical Agent runtime; supervision stays outside |
@@ -100,7 +100,7 @@ own, recorded as an ADR.
100
100
  | `stitchkit/agent-runtime/sqlite/bun` | server (Bun) | evolving | durable built-in SQLite store for the agent runtime |
101
101
  | `stitchkit/agent-runtime/sqlite/node` | server (Node ≥ 22.5) | evolving | durable built-in SQLite store for the agent runtime |
102
102
  | `stitchkit-tui` | terminal (Bun) | evolving | optional official OpenTUI host over a caller-composed headless runtime |
103
- | `stitchkit/application` | browser + server | evolving<br>_redefined in 13 of the 53 minors since 0.56.2, most recently 0.105.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
103
+ | `stitchkit/application` | browser + server | evolving<br>_redefined in 13 of the 54 minors since 0.56.2, most recently 0.105.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
104
104
  | `stitchkit/application/grammy` | server | evolving | a grammY bot as application resources — configuration, then polling with updates admitted by batch — and the webhook adapter |
105
105
  | `stitchkit/application/opentelemetry` | server | evolving | maps application snapshots onto an injected OpenTelemetry `Meter` |
106
106
  | `stitchkit/application/schemas` | browser + server | evolving | the application's snapshot, health and shutdown schemas alone, without the kernel |
@@ -621,6 +621,13 @@ preserve facts the generic client cannot invent — for example a Unix transport
621
621
  an effect from `UNKNOWN_ERROR` alone; inspect the owned adapter's cause and retry
622
622
  only when it proves that dispatch did not happen.
623
623
 
624
+ The same evidence survives when caller abort or the request deadline wins the
625
+ race: the public code remains `REQUEST_ABORTED` or `REQUEST_TIMEOUT`, while
626
+ the exact rejection produced by the transport remains in `ApiError.cause`.
627
+ That cause is internal diagnostic data — it is not included by JSON/string
628
+ rendering — so delivery state and trace metadata remain available without
629
+ putting transport payloads or credentials in a client-facing error.
630
+
624
631
  For an endpoint without contract arguments, pass only the options object:
625
632
 
626
633
  ```ts
package/llms/telegram.txt CHANGED
@@ -337,9 +337,10 @@ import { Database } from 'bun:sqlite'
337
337
  import type { Update } from 'grammy/types'
338
338
 
339
339
  const webhook = { token: env.BOT_TOKEN, url: env.WEBHOOK_URL, secret: env.WEBHOOK_SECRET }
340
+ const updateStore = sqliteTelegramUpdateStore({ database: new Database(env.UPDATES_DB) })
340
341
 
341
342
  const intake = createTelegramUpdateIntake<Update>({
342
- store: sqliteTelegramUpdateStore({ database: new Database(env.UPDATES_DB) }),
343
+ store: updateStore,
343
344
  handle: (update) => bot.handleUpdate(update),
344
345
  onFailure: (failure) => logger.warn('Update failed', { ...failure }),
345
346
  })
@@ -383,13 +384,55 @@ fix (a refused message, a blocked user) is abandoned; `maxAttempts` (5) ends
383
384
  the rest. Delivery is at least once: a handler that finished but could not be
384
385
  recorded as finished runs again.
385
386
 
387
+ Use `handleAttempt` when the handler needs the immutable attempt fence and an
388
+ ownership-loss signal. A signal asks slow work to stop; the fence is what
389
+ authorizes a domain write. Check it through a transaction-bound reference store
390
+ inside the same database transaction as that write:
391
+
392
+ ```ts
393
+ handleAttempt: async (update, { fence, ownerLost }) => {
394
+ ownerLost.throwIfAborted()
395
+ await database.transaction(async (tx) => {
396
+ const transactionStore = postgresTelegramUpdateStore({
397
+ query: (text, parameters) => tx.query(text, parameters),
398
+ createTable: false,
399
+ })
400
+ if (!(await transactionStore.owns(fence, Date.now()))) return
401
+ await applyDomainTransition(tx, update)
402
+ })
403
+ }
404
+ ```
405
+
406
+ The fence contains the claim's own `{ updateId, attempt, claimId }`. The opaque
407
+ `claimId` is persisted and never reused, so an old handler cannot adopt either
408
+ a newer attempt or a pruned and recreated `updateId`. Existing `handle(update)`
409
+ remains unchanged, including direct functions whose own optional second
410
+ argument has a different meaning.
411
+
412
+ For a final obligation, configure `handleExhaustion`. The reference store keeps
413
+ the terminal record unacknowledged until this handler resolves, including
414
+ across restart; a throw leaves it for the next sweep. Delivery is at least once,
415
+ so use the complete `{ updateId, attempt, claimId }` as an idempotency key in
416
+ the consumer database:
417
+
418
+ ```ts
419
+ handleExhaustion: ({ updateId, attempt, claimId, update, error }) =>
420
+ recordFinalFailureOnce({ key: `telegram:${updateId}:${attempt}:${claimId}`, update, error })
421
+ ```
422
+
423
+ This reuses the update row and table. Without `handleExhaustion`, terminal
424
+ abandonment retains its previous behavior.
425
+
386
426
  With grammY the handler is `bot.handleUpdate`, which wraps a middleware's error
387
427
  in a `BotError`; the intake unwraps it, so `retry`, `onFailure` and the error
388
428
  stored with the update are what the middleware threw — a `retry` that returns
389
429
  `false` for the bot's own terminal error sees that error.
390
430
 
391
- The store is an interface of six atomic steps (`TelegramUpdateStore`), so the
392
- bot's own database holds it:
431
+ The base store remains the six atomic steps of `TelegramUpdateStore`. A
432
+ `TelegramUpdateFencedStore` adds `claimOwned`, `renewOwned`, `owns` and
433
+ `settleOwned`; `TelegramUpdateDurableStore` adds the final-obligation methods.
434
+ Reference stores implement both, so the bot's own database holds the fence and
435
+ final obligation without a second queue:
393
436
 
394
437
  | Store | Takes |
395
438
  |---|---|
@@ -416,7 +459,12 @@ postgresTelegramUpdateStore({
416
459
  Each rule is one conditional statement, so a claim is atomic across every
417
460
  process sharing the table. By default the table is created on first use; where
418
461
  migrations own the schema, `postgresTelegramUpdateStoreSchema(table)` is the
419
- statement to put in one.
462
+ statement to put in one. Apply it before using `handleAttempt` or
463
+ `handleExhaustion`; it adds `claim_id` idempotently. Legacy `handle(update)`
464
+ uses base PostgreSQL operations that do not reference that column. SQLite adds
465
+ the column automatically and tolerates two rolling processes racing the
466
+ migration. A direct base-store claim with `durableExhaustion: true` also opts
467
+ into the column-backed durable capability.
420
468
 
421
469
  A store of your own — over an ORM, another database — is put through the same
422
470
  rules the shipped ones are, concurrent claims included:
@@ -506,13 +554,16 @@ injected grammY bot. → ADR 0143, ADR 0201 — [guide](../guide/telegram.md)
506
554
  | `telegramWebhookUrl` | function | the address with an `owner` tag derived from the secret: equal for one secret, different for another, the path untouched |
507
555
  | `checkTelegramWebhook` / `TelegramWebhookState` | function / _type_ | whether the webhook still points here: `owned`, `ownerHost`, `pendingUpdates`, Telegram's `lastError` |
508
556
  | `receiveTelegramWebhook` / `ReceiveTelegramWebhookOptions` / `TelegramUpdateAcceptance` | function / _type_ | one webhook `Request`: 403 without the secret (compared in constant time), 405 for another method, 400 for no update, 200 once `accept` recorded it |
509
- | `createTelegramUpdateIntake` | function | webhook updates recorded before Telegram is answered and handled after: chat order kept, other chats alongside up to `maxConcurrent`, a renewed lease per attempt, a sweep that takes over what a restart or a dead process left, retries, `maxAttempts`, finished updates remembered `retainMs` to refuse repeats; at least once |
510
- | `TelegramUpdateIntakeConfig` / `TelegramUpdateIntake` / `TelegramUpdateEnvelope` / `TelegramUpdateFailure` / `TelegramUpdateStoreStep` | _type_ | `store`, `handle` (a grammY `BotError` is unwrapped: `retry`, `onFailure` and the stored error see what the middleware threw), `retry`, `maxAttempts` (5), `leaseMs` (120 000), `pendingGraceMs` (60 000), `sweepEveryMs` (30 000), `retainMs` (24 h), `maxConcurrent` (32), `onFailure`, `onStoreError`, `now`; `accept(body)`, `start()`, `sweep()`, `idle()`, `close()` |
511
- | `TelegramUpdateStore` / `StoredTelegramUpdate` / `TelegramUpdateState` / `TelegramUpdateSettlement` / `TelegramUpdateClaimOptions` / `TelegramUpdateDueQuery` | _type_ | the store an intake runs on: `add`, `claim`, `renew`, `settle`, `due`, `prune`, each atomic on one record — a claim takes `pending`, a due `failed` or a lapsed `processing`; renew and settle only by the attempt holding it |
557
+ | `createTelegramUpdateIntake` | function | webhook updates recorded before Telegram is answered and handled after: chat order kept, other chats alongside up to `maxConcurrent`, a renewed lease and immutable fence per attempt, a sweep that takes over what a restart or a dead process left, retries, `maxAttempts`, optional recoverable exhaustion delivery, finished updates remembered `retainMs` to refuse repeats; at least once |
558
+ | `TelegramUpdateIntakeConfig` / `TelegramUpdateAttemptIntakeConfig` / `TelegramUpdateIntakeOptions` / `TelegramUpdateIntake` / `TelegramUpdateEnvelope` / `TelegramUpdateFailure` / `TelegramUpdateStoreStep` | _type_ | `store`, unchanged `handle(update)` or attempt-aware `handleAttempt(update, context)` (a grammY `BotError` is unwrapped), shared `retry`, `maxAttempts` (5), `leaseMs` (120 000), `pendingGraceMs` (60 000), `sweepEveryMs` (30 000), `retainMs` (24 h), `maxConcurrent` (32), `onFailure`, durable `handleExhaustion`, `onExhaustionFailure`, `onStoreError`, `now`; `accept(body)`, `start()`, `sweep()`, `idle()`, `close()` |
559
+ | `TelegramUpdateAttemptContext` / `TelegramUpdateAttemptIdentity` / `TelegramUpdateOwnershipLostError` | _type_ / class | immutable `{ updateId, attempt, claimId }` fence plus `ownerLost`; the persisted opaque claim token prevents a stale capability from matching after prune/re-add, while a store check in the consumer's DB transaction fences a domain write |
560
+ | `TelegramUpdateExhaustion` / `TelegramUpdateExhaustionFailure` / `StoredTelegramUpdateExhaustion` / `TelegramUpdateExhaustionQuery` | _type_ | a terminal update/attempt/error retained in the update store until `handleExhaustion` resolves; ordered `{ limit, afterUpdateId? }` pages prevent a failing first batch from starving later rows; delivery failures are observational and leave it due |
561
+ | `TelegramUpdateStore` / `StoredTelegramUpdate` / `TelegramUpdateState` / `TelegramUpdateSettlement` / `TelegramUpdateClaimOptions` / `TelegramUpdateDueQuery` | _type_ | the base store an intake runs on: `add`, `claim`, `renew`, `settle`, `due`, `prune`, each atomic on one record — a claim takes `pending`, a due `failed` or a lapsed `processing`; renew and settle only by the attempt holding it |
562
+ | `TelegramUpdateFencedStore` / `TelegramUpdateDurableStore` | _type_ | reference-store capabilities: `claimOwned` creates and returns the persisted identity, `renewOwned`, `owns(identity, at)` and `settleOwned(identity, settlement)` act only on that exact unexpired claim; `exhaust`, `dueExhaustions`, `acknowledgeExhaustion` keep a final consumer obligation in the same row until exact acknowledgement |
512
563
  | `memoryTelegramUpdateStore` | function | the store in this process's memory — tests, or a bot that accepts losing unhandled updates to a restart |
513
564
  | `sqliteTelegramUpdateStore` / `SqliteTelegramUpdateStoreConfig` / `TelegramSqliteDatabase` | function / _type_ | the store in SQLite over the handle the application opened — `bun:sqlite` `Database` or `node:sqlite` `DatabaseSync`; `table` (default `telegram_updates`) created when missing |
514
565
  | `postgresTelegramUpdateStore` / `PostgresTelegramUpdateStoreConfig` / `TelegramPostgresQuery` | function / _type_ | the store in Postgres over the client the application holds, as one `query(text, parameters)` returning rows — Bun `sql.unsafe`, Prisma `$queryRawUnsafe`, `pg` `pool.query`; each rule one conditional statement; `table`, `createTable` (default `true`) |
515
- | `postgresTelegramUpdateStoreSchema` | function | the table and its index as a statement for a migration, safe to run twice |
566
+ | `postgresTelegramUpdateStoreSchema` | function | the table, idempotent `claim_id` upgrade and index as a statement for a migration, safe to run twice; required before fenced methods when `createTable: false`, while legacy base methods remain compatible with the prior table |
516
567
  | `checkTelegramUpdateStore` | function | put any store through every rule, each on a fresh store from `make`, concurrent claims included; resolves with the violations, empty when it keeps them |
517
568
 
518
569
  ---
@@ -1,14 +1,81 @@
1
- # Upgrading 0.100.0 → 0.108.0 — stitchkit docs slice
1
+ # Upgrading 0.101.0 → 0.109.0 — stitchkit docs slice
2
2
 
3
3
  > Generated from the `docs/` tree by `bun run gen:llms`. The index of all slices is
4
4
  > `llms.txt` in this package.
5
5
 
6
- Contains: Guide: Upgrading, 0.100.0 → 0.108.0 (docs/guide/upgrading.md)
6
+ Contains: Guide: Upgrading, 0.101.0 → 0.109.0 (docs/guide/upgrading.md)
7
7
 
8
8
  ==============================================================================
9
- # Guide: Upgrading, 0.100.0 → 0.108.0 (docs/guide/upgrading.md)
9
+ # Guide: Upgrading, 0.101.0 → 0.109.0 (docs/guide/upgrading.md)
10
10
  ==============================================================================
11
11
 
12
+ ## Released migration: 0.109.0
13
+
14
+ ### free CLI record leaves are lexical
15
+
16
+ **Who must act:** scripts that pass dotted values into a free `record<string, unknown>` and rely on
17
+ stitchkit guessing numbers or booleans from their spelling. Declared object leaves and whole-object
18
+ JSON keep their typed behavior.
19
+
20
+ Free record values have no schema type to justify coercion. They now preserve argv text, so values
21
+ such as `007`, `1e3`, `true` and `false` arrive exactly as written.
22
+ See [ADR 0257](../decisions/0257-cli-dotted-values-follow-their-declared-schema.md).
23
+
24
+ 1. Pass typed free-record values as one JSON object:
25
+
26
+ ```sh
27
+ # before: produced { count: 5 } by spelling-based inference
28
+ app command --data.count=5
29
+ # after: JSON says explicitly that 5 is a number
30
+ app command --data='{"count":5}'
31
+ ```
32
+
33
+ 2. If dotted flags are the intended interface, declare their object shape; a `z.number()` or
34
+ `z.boolean()` leaf still coerces and validates its own CLI value.
35
+
36
+ ### Opting into Telegram attempt fencing
37
+
38
+ Existing `handle(update)` callbacks and the six-method `TelegramUpdateStore` need no change. The
39
+ new capability is opt-in
40
+ ([ADR 0256](../decisions/0256-a-telegram-attempt-is-fenced-by-a-persisted-claim.md)).
41
+
42
+ 1. To fence a domain write, move only that handler to the explicit context-aware option:
43
+
44
+ ```ts
45
+ createTelegramUpdateIntake({
46
+ store,
47
+ handleAttempt: async (update, { fence, ownerLost }) => {
48
+ ownerLost.throwIfAborted()
49
+ await domainTransaction(async (tx) => {
50
+ if (await transactionStore(tx).owns(fence, Date.now())) await apply(tx, update)
51
+ })
52
+ },
53
+ })
54
+ ```
55
+
56
+ 2. A direct grammY handler remains `handle: bot.handleUpdate.bind(bot)`; stitchkit does not pass its
57
+ attempt context into grammY's optional webhook-envelope parameter.
58
+
59
+ 3. A custom store used by `handleAttempt` implements `claimOwned`, `renewOwned`, `owns` and
60
+ `settleOwned` over the full `{ updateId, attempt, claimId }` identity. If it also delivers
61
+ `handleExhaustion`, implement `exhaust`, `dueExhaustions` and `acknowledgeExhaustion`. Run
62
+ `checkTelegramUpdateStore` against a fresh database; it checks base, fenced and durable rules
63
+ according to the methods the store exposes.
64
+
65
+ 4. SQLite stores add `claim_id` automatically. PostgreSQL stores with the default
66
+ `createTable: true` do the same. When migrations own a PostgreSQL table, apply the returned
67
+ schema before enabling `handleAttempt` or `handleExhaustion`:
68
+
69
+ ```ts
70
+ await sql.unsafe(postgresTelegramUpdateStoreSchema('telegram_updates'))
71
+ // Equivalent addition for an existing table:
72
+ // ALTER TABLE telegram_updates ADD COLUMN IF NOT EXISTS claim_id TEXT;
73
+ ```
74
+
75
+ Legacy `handle(update)` continues to use base operations that do not reference `claim_id`, so
76
+ the migration is required only when the new capability or an explicit base
77
+ `claim(..., { durableExhaustion: true })` is enabled.
78
+
12
79
  ## Released migration: 0.108.0
13
80
 
14
81
  ### a request that never left is its own send outcome
@@ -831,72 +898,3 @@ text.replace(new RegExp(TELEGRAM_BOT_TOKEN_PATTERN.source, 'g'), '[redacted]')
831
898
  createJsonLogger({})
832
899
  redactTelegramBotToken(text)
833
900
  ```
834
-
835
- ## Released migration: 0.100.0
836
-
837
- ### watch capacity and session/live/delivery adoption
838
-
839
- **Who must act:** watch consumers exceeding 1024 hub sources, 1024 hub subscribers,
840
- 1024 client keys or 128 listeners per key. These previously unbounded collections
841
- now refuse excess admission. Choose explicit positive finite limits, for example:
842
-
843
- ```ts
844
- // Before: no hub-wide capacity bounds.
845
- createWatchHub({ read, watchable });
846
- // After: explicitly budget a larger workload.
847
- createWatchHub({ read, watchable, maxSources: 4096, maxSubscribers: 4096 });
848
- // Client limits are configured independently.
849
- createWatchClient({ ...clientConfig, maxKeys: 2048, maxListenersPerKey: 256 });
850
- ```
851
-
852
- The remaining steps apply only to applications opting into session lifetimes,
853
- scoped watched reads, managed subscriptions or multi-action notification delivery.
854
- Existing clients within the limits and single-send outboxes need no migration.
855
-
856
- Replace local session generation counters with one `createSessionScope`; share it
857
- with `createSessionCredentials`. Guard transport attempts, complete asynchronous
858
- operations and final synchronous delivery. Route credential persistence through
859
- one coordinator. Keep provider-specific error classification and bounded command
860
- retry in the application. Coordination is per client instance.
861
-
862
- Upgrade both watch endpoints before enabling session-scoped watches: strict older
863
- servers reject the new optional `WatchKey.instance`. Server-verified scope controls
864
- sharing; the client instance is only routing identity. Bind exact watched handles
865
- to session-qualified cache keys. Existing delta/reconnect behavior stays in place.
866
- For applications needing more than 1024 hub sources/subscribers or client keys, set
867
- explicit finite capacity options. Enable a reconciliation interval when lost hints
868
- must recover without a reconnect.
869
-
870
- For multi-action outboxes, enqueue new items with a versioned delivery plan and
871
- return small JSON receipts from `send`. Existing queued single-send items should
872
- be drained before enabling a delivery executor on that queue, or migrated under
873
- the store's atomic transition. When a new executor version ships, list the old
874
- version's projection in `delivery.retiredVersions` until its queued items drain;
875
- a plan version with no executor waits, reported to `onError` as
876
- `NotificationPlanVersionError` (from 0.100.1).
877
- Make projection idempotent. Provider idempotency/reconciliation remains necessary
878
- for a crash between remote success and checkpoint. These APIs are available starting with 0.100.0.
879
-
880
- ### exclusive lock recovery
881
-
882
- New lock and reclaim-guard records include `owner.process`: kernel boot identity,
883
- process birth token and namespace. `readDiagnosticJournalLockDiagnosis(error)`
884
- adds `identity` to distinguish matched lifetime evidence, PID reuse, a different
885
- boot, an absent PID, a legacy record or unavailable identity. Legacy `alive` only
886
- means the PID is present; it does not prove that the recorded writer still exists.
887
-
888
- **Who must act:** deployments recovering a legacy lock with a reused live PID,
889
- or bundling file-lock consumers into a standalone Darwin executable. Legacy
890
- records cannot safely infer their missing boot/start identity from `acquiredAt`.
891
- Stop all writers to that resource, verify exclusive ownership, preserve the old
892
- lock for incident evidence outside the active lock path, and restart with the
893
- new package. Never delete a lock merely because it is old. New acquisitions
894
- write the modern record; no journal data migration is needed.
895
-
896
- Darwin distributions must retain the package's architecture-matched native
897
- `.node` asset for process birth measurement, as for contained filesystem access.
898
- If native/proc identity cannot be read, new locks explicitly record null and
899
- recovery refuses rather than treating unknown ownership as death. From 0.100.1 a
900
- Linux kernel without time namespaces (before 5.6) records the PID namespace alone
901
- instead of null, so its locks recover like any other. Existing
902
- `refuse` policy and the separately configured ownerless-file grace are unchanged.