stitchkit 0.76.0 → 0.76.2

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 (48) hide show
  1. package/dist/agent-runtime-coding-tools.js +1 -2
  2. package/dist/agent-runtime-harness.js +4 -5
  3. package/dist/agent-runtime.js +7 -8
  4. package/dist/application/keyspace.d.ts +37 -0
  5. package/dist/application/keyspace.d.ts.map +1 -1
  6. package/dist/application/watch-hub.d.ts +1 -1
  7. package/dist/application/watch-hub.d.ts.map +1 -1
  8. package/dist/application.d.ts +1 -1
  9. package/dist/application.d.ts.map +1 -1
  10. package/dist/application.js +31 -8
  11. package/dist/cli.js +6 -7
  12. package/dist/contract/index.js +1 -2
  13. package/dist/{index-v5dm4bwq.js → index-58jzmnn4.js} +1 -1
  14. package/dist/{index-885chjxq.js → index-7qy2ex0m.js} +1 -1
  15. package/dist/index-a916km3r.js +45 -0
  16. package/dist/{index-28jsdwt1.js → index-aczggrty.js} +1 -1
  17. package/dist/{index-65kathdm.js → index-da1aqnhb.js} +4 -6
  18. package/dist/{index-xgahrwdf.js → index-ezmn6ac6.js} +3 -3
  19. package/dist/{index-4kp4fxvp.js → index-f6n5n7nz.js} +18 -4
  20. package/dist/{index-6wr9f6qm.js → index-jqtsc9mj.js} +2 -2
  21. package/dist/{index-9c5dbr5q.js → index-k2zczx1g.js} +1 -1
  22. package/dist/{index-v7xjfy10.js → index-m668wzyc.js} +1 -1
  23. package/dist/{index-e0a57ymn.js → index-nemjkxjp.js} +1 -1
  24. package/dist/{index-9my2n66s.js → index-s2rchahr.js} +4 -4
  25. package/dist/{index-k68e5jz0.js → index-s4c8wy8m.js} +2 -2
  26. package/dist/{index-52400rdd.js → index-t8qyvrvg.js} +3 -3
  27. package/dist/{index-endzd8sj.js → index-vbf2p6me.js} +1 -1
  28. package/dist/{index-hsz2mmk0.js → index-vj3vvpaa.js} +17 -1
  29. package/dist/index.js +1 -2
  30. package/dist/internal/stable-digest.d.ts +40 -6
  31. package/dist/internal/stable-digest.d.ts.map +1 -1
  32. package/dist/live/watch-client.d.ts +52 -5
  33. package/dist/live/watch-client.d.ts.map +1 -1
  34. package/dist/live.d.ts +1 -1
  35. package/dist/live.d.ts.map +1 -1
  36. package/dist/live.js +44 -57
  37. package/dist/node.js +4 -5
  38. package/dist/observability/index.js +3 -4
  39. package/dist/primitives.js +1 -2
  40. package/dist/remote.js +2 -3
  41. package/dist/server/index.js +5 -6
  42. package/dist/testing.js +2 -3
  43. package/dist/tool-invoker.js +5 -6
  44. package/dist/tools.js +13 -14
  45. package/llms-full.txt +37 -5
  46. package/package.json +1 -1
  47. package/dist/index-rxfy4cq7.js +0 -17
  48. package/dist/index-vdvmcy7r.js +0 -22
@@ -1,3 +1,38 @@
1
+ /**
2
+ * A stable identity for a call's arguments — one implementation, two callers.
3
+ *
4
+ * Two callers need the same thing for different reasons. The MCP round keys a
5
+ * tool invocation by `(operation, arguments)` so a retry is recognised as the
6
+ * same round; a watched read keys its shared source by `(operation, arguments)`
7
+ * so two browsers asking the same question are one read on the server. If those
8
+ * two computed the key differently, the same arguments would be the same round
9
+ * and two different watches — and nothing would say so.
10
+ *
11
+ * The stability that matters is key order: `{a:1,b:2}` and `{b:2,a:1}` are the
12
+ * same arguments and `JSON.stringify` disagrees, so a naive key silently splits
13
+ * one shared source into two whenever a caller builds its object in a different
14
+ * order. That is not a hypothetical: object literal order follows the source
15
+ * that wrote it, and two components asking the same question rarely share one.
16
+ *
17
+ * ## Why this is not SHA-256
18
+ *
19
+ * It was, and that made every watched read fail on an ordinary intranet. Web
20
+ * Crypto — `crypto.subtle` — exists only in a **secure context**, so a page
21
+ * served over plain HTTP from a LAN name has no `crypto.subtle` at all and the
22
+ * first digest throws `Cannot read properties of undefined`. `localhost` is
23
+ * secure by definition, which is exactly why nothing caught it until a browser
24
+ * opened the app by its name.
25
+ *
26
+ * The fix is not a fallback, it is admitting what this is for: **identity, not
27
+ * secrecy**. Nothing here resists an adversary — an attacker who can choose your
28
+ * watch arguments can already ask the question directly. So the requirement is
29
+ * distribution and determinism, both ends agreeing, and no ambient capability;
30
+ * a cryptographic hash bought none of that and cost the entire non-secure web.
31
+ *
32
+ * Being synchronous is the second thing it buys. A promise for a key made the
33
+ * first subscription of a question asynchronous, which meant a component could
34
+ * not be handed a retained value in the same turn it subscribed.
35
+ */
1
36
  /**
2
37
  * The value with every object's keys sorted, recursively — arrays keep their
3
38
  * order, because in an array order *is* the value.
@@ -7,12 +42,11 @@
7
42
  */
8
43
  export declare function stableValue(value: unknown): unknown;
9
44
  /**
10
- * A bounded, order-independent digest of a call's arguments.
45
+ * A bounded, order-independent identity for a call's arguments.
11
46
  *
12
- * SHA-256 over the key-sorted JSON, base64url without padding. Bounded because
13
- * the key travels on the wire and arguments do not have a bounded size; a hash
14
- * rather than the JSON itself so a large argument object does not become a
15
- * large subscription key repeated in every frame.
47
+ * 128 bits as 32 lowercase hex characters. Deterministic across runtimes and
48
+ * across both ends of a socket, synchronous, and dependent on no ambient
49
+ * capability see the module header for why that last one is the whole point.
16
50
  *
17
51
  * What it cannot do, stated because the limit is easy to walk into: values
18
52
  * `JSON.stringify` drops or transforms — `undefined` members, a `Date`, a `Map`,
@@ -20,5 +54,5 @@ export declare function stableValue(value: unknown): unknown;
20
54
  * Contract arguments are parsed JSON, so this is the argument shape by
21
55
  * construction; a caller digesting something else has to say what it means.
22
56
  */
23
- export declare function argumentsDigest(args: Record<string, unknown>): Promise<string>;
57
+ export declare function argumentsDigest(args: Record<string, unknown>): string;
24
58
  //# sourceMappingURL=stable-digest.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stable-digest.d.ts","sourceRoot":"","sources":["../../src/internal/stable-digest.ts"],"names":[],"mappings":"AAkBA;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAQnD;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAGpF"}
1
+ {"version":3,"file":"stable-digest.d.ts","sourceRoot":"","sources":["../../src/internal/stable-digest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAQnD;AAyCD;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAGrE"}
@@ -44,7 +44,7 @@
44
44
  * sense a rendering component can use.
45
45
  */
46
46
  import type { ContractDef, EndpointDef } from '../contract/define.js';
47
- import { type WatchKey, type WatchStateFrame } from './watch-contract.js';
47
+ import { WATCH_CLOSE, WATCH_OPEN, WATCH_STATE, WATCH_VALUE, type WatchKey, type WatchStateFrame, type WatchValueFrame } from './watch-contract.js';
48
48
  export interface WatchListeners<TValue> {
49
49
  value(value: TValue): void;
50
50
  /** Phase and, when unhealthy, the read's own code and message. */
@@ -56,11 +56,58 @@ export interface WatchHandle<TValue> {
56
56
  /** Drops every listener this handle registered and releases the key. */
57
57
  close(): void;
58
58
  }
59
- /** The transport half a watch client needs — a bound realtime client for `watchContract`. */
59
+ /** What the server sends a watcher, by event name. */
60
+ export interface WatchInboundEvents {
61
+ [WATCH_VALUE]: WatchValueFrame;
62
+ [WATCH_STATE]: WatchStateFrame;
63
+ }
64
+ /**
65
+ * A realtime client, as much of one as this adapter needs to see.
66
+ *
67
+ * Four members and no types on them, because the point of this shape is that a
68
+ * *typed* client satisfies it: a bound realtime client's `on` is generic over
69
+ * the events of the contract it was bound to, and TypeScript will not relate
70
+ * that signature to any concrete one written here — measured, both with a payload
71
+ * parameter and with the tuple form the realtime handler actually has.
72
+ */
73
+ export interface RealtimeClientLike {
74
+ on: (...args: never[]) => unknown;
75
+ emit: (...args: never[]) => unknown;
76
+ request: (...args: never[]) => unknown;
77
+ onConnectionChange: (...args: never[]) => unknown;
78
+ }
79
+ /**
80
+ * Hand a bound realtime client to {@link createWatchClient}.
81
+ *
82
+ * The guide used to say "pass a bound realtime client" and the types refused it,
83
+ * which is the worst combination: an instruction that reads as supported and
84
+ * fails at the call site. This is the conversion, once, in the framework —
85
+ * rather than the same cast copied into every application that follows the
86
+ * guide.
87
+ */
88
+ export declare function watchTransport(client: RealtimeClientLike): WatchTransport;
89
+ /**
90
+ * The transport half a watch client needs — a realtime client bound to a
91
+ * contract that carries `watchContract`.
92
+ *
93
+ * Written in the protocol's own four event names rather than in `string`, so an
94
+ * application bringing its own transport knows exactly what to implement.
95
+ *
96
+ * A **bound realtime client does not satisfy this shape**, and no phrasing of it
97
+ * would fix that: its `on` is generic over the events of the contract it was
98
+ * bound to, and TypeScript will not relate two generic signatures like these.
99
+ * Pass it through {@link watchTransport}, which is that conversion done once
100
+ * here instead of once per application.
101
+ */
60
102
  export interface WatchTransport {
61
- on(event: string, handler: (payload: never) => void): () => void;
62
- emit(event: string, payload: unknown): void;
63
- request(event: string, payload: unknown, options: {
103
+ on<TEvent extends keyof WatchInboundEvents>(event: TEvent, handler: (payload: WatchInboundEvents[TEvent]) => void): () => void;
104
+ emit(event: typeof WATCH_CLOSE, payload: {
105
+ key: WatchKey;
106
+ }): unknown;
107
+ request(event: typeof WATCH_OPEN, payload: {
108
+ key: WatchKey;
109
+ args: unknown;
110
+ }, options: {
64
111
  timeoutMs: number;
65
112
  }): Promise<{
66
113
  accepted: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"watch-client.d.ts","sourceRoot":"","sources":["../../src/live/watch-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEnE,OAAO,EAKL,KAAK,QAAQ,EACb,KAAK,eAAe,EAGrB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,cAAc,CAAC,MAAM;IACpC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,kEAAkE;IAClE,KAAK,CAAC,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,WAAW,CAAC,MAAM;IACjC,sFAAsF;IACtF,SAAS,CAAC,SAAS,EAAE,cAAc,CAAC,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC;IACzD,wEAAwE;IACxE,KAAK,IAAI,IAAI,CAAC;CACf;AAED,6FAA6F;AAC7F,MAAM,WAAW,cAAc;IAC7B,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,OAAO,EAAE,KAAK,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IACjE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAC5C,OAAO,CACL,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,GAC7B,OAAO,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD;;;;;;;OAOG;IACH,kBAAkB,CAAC,QAAQ,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CACzF;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,8DAA8D;IAC9D,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,wEAAwE;IACxE,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9D;AAuBD,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,IAAI;KACnE,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,WAAW,CAAC,OAAO,CAAC;CACzE,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACrE,QAAQ,EAAE,WAAW,CAAC,CAAC,EAAE,MAAM,CAAC,EAChC,MAAM,EAAE,iBAAiB,GACxB,gBAAgB,CAAC,CAAC,CAAC,CAwNrB"}
1
+ {"version":3,"file":"watch-client.d.ts","sourceRoot":"","sources":["../../src/live/watch-client.ts"],"names":[],"mappings":"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"}
package/dist/live.d.ts CHANGED
@@ -13,6 +13,6 @@
13
13
  * a watched read live in `stitchkit/application`. → ADR 0150.
14
14
  */
15
15
  export { defineEvents, type EventDecision, type EventDeliveryMode, type EventPayloads, type EventsConfig, type EventsDeclaration, type EventTopicDeclaration, type EventTopicRegistry, type EventTopicsOfMode, type EventUndecided, toRealtimeContract, type WireTopic, } from './live/events.js';
16
- export { createWatchClient, type TypedWatchClient, type WatchClientConfig, type WatchHandle, type WatchListeners, type WatchTransport, } from './live/watch-client.js';
16
+ export { createWatchClient, type RealtimeClientLike, type TypedWatchClient, type WatchClientConfig, type WatchHandle, type WatchInboundEvents, type WatchListeners, type WatchTransport, watchTransport, } from './live/watch-client.js';
17
17
  export { WATCH_CLOSE, WATCH_OPEN, WATCH_STATE, WATCH_VALUE, type WatchKey, WatchKeySchema, type WatchStateFrame, WatchStateSchema, type WatchValueFrame, WatchValueSchema, watchContract, watchKeyString, } from './live/watch-contract.js';
18
18
  //# sourceMappingURL=live.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"live.d.ts","sourceRoot":"","sources":["../src/live.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EACL,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,kBAAkB,EAClB,KAAK,SAAS,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,iBAAiB,EACjB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,cAAc,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,WAAW,EACX,UAAU,EACV,WAAW,EACX,WAAW,EACX,KAAK,QAAQ,EACb,cAAc,EACd,KAAK,eAAe,EACpB,gBAAgB,EAChB,KAAK,eAAe,EACpB,gBAAgB,EAChB,aAAa,EACb,cAAc,GACf,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"live.d.ts","sourceRoot":"","sources":["../src/live.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EACL,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACtB,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,kBAAkB,EAClB,KAAK,SAAS,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,iBAAiB,EACjB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,cAAc,GACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,WAAW,EACX,UAAU,EACV,WAAW,EACX,WAAW,EACX,KAAK,QAAQ,EACb,cAAc,EACd,KAAK,eAAe,EACpB,gBAAgB,EAChB,KAAK,eAAe,EACpB,gBAAgB,EAChB,aAAa,EACb,cAAc,GACf,MAAM,uBAAuB,CAAC"}
package/dist/live.js CHANGED
@@ -2,9 +2,6 @@ import {
2
2
  LiveStatePhaseSchema,
3
3
  LiveStateStopReasonSchema
4
4
  } from "./index-35z5h2ty.js";
5
- import {
6
- bytesToBase64Url
7
- } from "./index-rxfy4cq7.js";
8
5
 
9
6
  // src/live/events.ts
10
7
  import { z } from "zod";
@@ -68,9 +65,36 @@ function stableValue(value) {
68
65
  }
69
66
  return result;
70
67
  }
71
- async function argumentsDigest(args) {
72
- const bytes = new TextEncoder().encode(JSON.stringify(stableValue(args)));
73
- return bytesToBase64Url(new Uint8Array(await crypto.subtle.digest("SHA-256", bytes)));
68
+ function mix128(input) {
69
+ let a = 2654435769;
70
+ let b = 2246822507;
71
+ let c = 3266489909;
72
+ let d = 668265263;
73
+ for (let index = 0;index < input.length; index += 1) {
74
+ const code = input.charCodeAt(index);
75
+ a = Math.imul(a ^ code, 2246822519);
76
+ b = Math.imul(b ^ code, 3266489917);
77
+ c = Math.imul(c ^ code, 668265263);
78
+ d = Math.imul(d ^ code, 374761393);
79
+ a = a << 13 | a >>> 19;
80
+ b = b << 17 | b >>> 15;
81
+ c = c << 7 | c >>> 25;
82
+ d = d << 11 | d >>> 21;
83
+ }
84
+ a ^= b >>> 15;
85
+ b ^= c >>> 13;
86
+ c ^= d >>> 11;
87
+ d ^= a >>> 9;
88
+ return [
89
+ Math.imul(a ^ a >>> 16, 575075507) >>> 0,
90
+ Math.imul(b ^ b >>> 13, 2654435761) >>> 0,
91
+ Math.imul(c ^ c >>> 16, 2246822507) >>> 0,
92
+ Math.imul(d ^ d >>> 15, 3266489909) >>> 0
93
+ ];
94
+ }
95
+ function argumentsDigest(args) {
96
+ const lanes = mix128(JSON.stringify(stableValue(args)) ?? "undefined");
97
+ return lanes.map((lane) => lane.toString(16).padStart(8, "0")).join("");
74
98
  }
75
99
 
76
100
  // src/live/watch-contract.ts
@@ -116,9 +140,11 @@ function watchKeyString(key) {
116
140
  }
117
141
 
118
142
  // src/live/watch-client.ts
143
+ function watchTransport(client) {
144
+ return client;
145
+ }
119
146
  function createWatchClient(contract, config) {
120
147
  const entries = new Map;
121
- const digests = new Map;
122
148
  const holdMs = config.holdMs ?? 0;
123
149
  const openTimeoutMs = config.openTimeoutMs ?? 1e4;
124
150
  const service = contract.meta.prefix;
@@ -165,18 +191,6 @@ function createWatchClient(contract, config) {
165
191
  open(entry);
166
192
  }
167
193
  });
168
- function cachedDigest(action, args) {
169
- return digests.get(`${service}/${action}/${JSON.stringify(stableValue(args))}`);
170
- }
171
- async function resolveDigest(action, args) {
172
- const cacheKey = `${service}/${action}/${JSON.stringify(stableValue(args))}`;
173
- const cached = digests.get(cacheKey);
174
- if (cached !== undefined)
175
- return cached;
176
- const digest = await argumentsDigest(args);
177
- digests.set(cacheKey, digest);
178
- return digest;
179
- }
180
194
  function entryFor(key, args) {
181
195
  const id = watchKeyString(key);
182
196
  const existing = entries.get(id);
@@ -243,55 +257,27 @@ function createWatchClient(contract, config) {
243
257
  }
244
258
  function handleFor(action, args) {
245
259
  const mine = new Set;
246
- let entry;
247
- const known = cachedDigest(action, args);
248
- if (known !== undefined)
249
- entry = entryFor({ service, action, digest: known }, args);
250
- const ready = (async () => {
251
- if (entry)
252
- return entry;
253
- const digest = await resolveDigest(action, args);
254
- entry = entryFor({ service, action, digest }, args);
255
- return entry;
256
- })();
260
+ const entry = entryFor({ service, action, digest: argumentsDigest(args) }, args);
257
261
  return {
258
262
  subscribe(listeners) {
259
263
  const registered = listeners;
260
264
  mine.add(registered);
261
- if (entry) {
262
- entry.listeners.add(registered);
263
- if (entry.hasValue)
264
- registered.value(entry.value);
265
- registered.state?.(entry.state);
266
- }
267
- ready.then((target) => {
268
- if (!mine.has(registered))
269
- return;
270
- if (!target.listeners.has(registered)) {
271
- target.listeners.add(registered);
272
- if (target.hasValue)
273
- registered.value(target.value);
274
- registered.state?.(target.state);
275
- }
276
- open(target);
277
- });
265
+ entry.listeners.add(registered);
266
+ if (entry.hasValue)
267
+ registered.value(entry.value);
268
+ registered.state?.(entry.state);
269
+ open(entry);
278
270
  return () => {
279
271
  mine.delete(registered);
280
- const target = entry;
281
- if (!target)
282
- return;
283
- target.listeners.delete(registered);
284
- releaseEntry(target);
272
+ entry.listeners.delete(registered);
273
+ releaseEntry(entry);
285
274
  };
286
275
  },
287
276
  close() {
288
- const target = entry;
289
- if (!target)
290
- return;
291
277
  for (const listener of mine)
292
- target.listeners.delete(listener);
278
+ entry.listeners.delete(listener);
293
279
  mine.clear();
294
- releaseEntry(target);
280
+ releaseEntry(entry);
295
281
  }
296
282
  };
297
283
  }
@@ -302,6 +288,7 @@ function createWatchClient(contract, config) {
302
288
  return client;
303
289
  }
304
290
  export {
291
+ watchTransport,
305
292
  watchKeyString,
306
293
  watchContract,
307
294
  toRealtimeContract,
package/dist/node.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  createHandler,
6
6
  createSocketIOServer,
7
7
  createUnixClientTransport
8
- } from "./index-52400rdd.js";
8
+ } from "./index-t8qyvrvg.js";
9
9
  import {
10
10
  createImplement,
11
11
  createImplementRegistry,
@@ -14,8 +14,8 @@ import {
14
14
  createScopedImplementRegistry,
15
15
  implement,
16
16
  implementRegistry
17
- } from "./index-endzd8sj.js";
18
- import"./index-e0a57ymn.js";
17
+ } from "./index-vbf2p6me.js";
18
+ import"./index-nemjkxjp.js";
19
19
  import"./index-7b188kmz.js";
20
20
  import {
21
21
  ShutdownOptionsSchema,
@@ -25,8 +25,7 @@ import {
25
25
  createServerLifecycle
26
26
  } from "./index-3z73fh2c.js";
27
27
  import"./index-nt1mp8km.js";
28
- import"./index-hsz2mmk0.js";
29
- import"./index-rxfy4cq7.js";
28
+ import"./index-vj3vvpaa.js";
30
29
  import {
31
30
  AppError,
32
31
  appError,
@@ -10,7 +10,7 @@ import {
10
10
  redact,
11
11
  sanitizePayload,
12
12
  truncatePreview
13
- } from "../index-28jsdwt1.js";
13
+ } from "../index-aczggrty.js";
14
14
  import {
15
15
  getRequestContext,
16
16
  getTraceId,
@@ -21,7 +21,7 @@ import {
21
21
  setRequestError,
22
22
  setRequestUser,
23
23
  wrapInRequestContext
24
- } from "../index-e0a57ymn.js";
24
+ } from "../index-nemjkxjp.js";
25
25
  import {
26
26
  childSpan,
27
27
  createTraceContext,
@@ -31,8 +31,7 @@ import {
31
31
  resolvePropagationContext,
32
32
  resolveTraceContext
33
33
  } from "../index-nt1mp8km.js";
34
- import"../index-hsz2mmk0.js";
35
- import"../index-rxfy4cq7.js";
34
+ import"../index-vj3vvpaa.js";
36
35
  import"../index-0w9abg87.js";
37
36
  import {
38
37
  isRecord
@@ -1,7 +1,6 @@
1
1
  import {
2
2
  ManagedFileRefSchema
3
- } from "./index-4kp4fxvp.js";
4
- import"./index-rxfy4cq7.js";
3
+ } from "./index-f6n5n7nz.js";
5
4
  import"./index-ksp6e2ye.js";
6
5
 
7
6
  // src/primitives/audit.ts
package/dist/remote.js CHANGED
@@ -1,13 +1,12 @@
1
1
  import {
2
2
  ApiError,
3
3
  createClient
4
- } from "./index-v5dm4bwq.js";
4
+ } from "./index-58jzmnn4.js";
5
5
  import"./index-1rxswfbv.js";
6
6
  import"./index-nt1mp8km.js";
7
7
  import {
8
8
  mergeMeta
9
- } from "./index-hsz2mmk0.js";
10
- import"./index-rxfy4cq7.js";
9
+ } from "./index-vj3vvpaa.js";
11
10
  import {
12
11
  AppError
13
12
  } from "./index-0w9abg87.js";
@@ -13,7 +13,7 @@ import {
13
13
  sseRoute,
14
14
  streamingRoute,
15
15
  webSocketLane
16
- } from "../index-52400rdd.js";
16
+ } from "../index-t8qyvrvg.js";
17
17
  import {
18
18
  composeAuthHooks,
19
19
  createAuthHook,
@@ -26,7 +26,7 @@ import {
26
26
  signJwt,
27
27
  verifyJwt,
28
28
  verifyPkce
29
- } from "../index-65kathdm.js";
29
+ } from "../index-da1aqnhb.js";
30
30
  import {
31
31
  DEFAULT_CORS_ALLOW_HEADERS,
32
32
  DEFAULT_CORS_EXPOSE_HEADERS,
@@ -40,7 +40,7 @@ import {
40
40
  defineMultipartStream,
41
41
  implement,
42
42
  implementRegistry
43
- } from "../index-endzd8sj.js";
43
+ } from "../index-vbf2p6me.js";
44
44
  import {
45
45
  extractIp,
46
46
  generateTraceId,
@@ -49,7 +49,7 @@ import {
49
49
  isPublicIp,
50
50
  resolveSocketIp,
51
51
  resolveTraceId
52
- } from "../index-e0a57ymn.js";
52
+ } from "../index-nemjkxjp.js";
53
53
  import {
54
54
  isWithinDir,
55
55
  realPathWithinDir
@@ -81,8 +81,7 @@ import {
81
81
  DEFAULT_CONTRACT_STREAM_FRAME_BYTES,
82
82
  joinRoutePath,
83
83
  parseTrailingWildcard
84
- } from "../index-hsz2mmk0.js";
85
- import"../index-rxfy4cq7.js";
84
+ } from "../index-vj3vvpaa.js";
86
85
  import {
87
86
  AppError,
88
87
  STITCH_ERROR_STATUS,
package/dist/testing.js CHANGED
@@ -24,13 +24,12 @@ import {
24
24
  import {
25
25
  createClient,
26
26
  createClients
27
- } from "./index-v5dm4bwq.js";
27
+ } from "./index-58jzmnn4.js";
28
28
  import"./index-1rxswfbv.js";
29
29
  import"./index-nt1mp8km.js";
30
30
  import {
31
31
  joinRoutePath
32
- } from "./index-hsz2mmk0.js";
33
- import"./index-rxfy4cq7.js";
32
+ } from "./index-vj3vvpaa.js";
34
33
  import"./index-0w9abg87.js";
35
34
  import {
36
35
  isRecord
@@ -1,14 +1,13 @@
1
1
  import {
2
2
  createToolInvoker
3
- } from "./index-6wr9f6qm.js";
4
- import"./index-885chjxq.js";
5
- import"./index-k68e5jz0.js";
6
- import"./index-e0a57ymn.js";
3
+ } from "./index-jqtsc9mj.js";
4
+ import"./index-7qy2ex0m.js";
5
+ import"./index-s4c8wy8m.js";
6
+ import"./index-nemjkxjp.js";
7
7
  import"./index-22by16v6.js";
8
8
  import"./index-cby4ar3v.js";
9
9
  import"./index-nt1mp8km.js";
10
- import"./index-hsz2mmk0.js";
11
- import"./index-rxfy4cq7.js";
10
+ import"./index-vj3vvpaa.js";
12
11
  import"./index-0w9abg87.js";
13
12
  import"./index-qyrqwr4c.js";
14
13
  import"./index-6k1937bx.js";
package/dist/tools.js CHANGED
@@ -1,18 +1,18 @@
1
1
  import {
2
2
  redact
3
- } from "./index-28jsdwt1.js";
3
+ } from "./index-aczggrty.js";
4
4
  import {
5
5
  buildToolManifest,
6
6
  mountAgent
7
- } from "./index-xgahrwdf.js";
7
+ } from "./index-ezmn6ac6.js";
8
8
  import"./index-3xnq72rz.js";
9
9
  import {
10
10
  argumentsDigest
11
- } from "./index-vdvmcy7r.js";
11
+ } from "./index-a916km3r.js";
12
12
  import {
13
13
  signJwt,
14
14
  verifyPkce
15
- } from "./index-65kathdm.js";
15
+ } from "./index-da1aqnhb.js";
16
16
  import {
17
17
  DEFAULT_CORS_ALLOW_HEADERS,
18
18
  DEFAULT_PROCESS_SIGNALS,
@@ -20,10 +20,10 @@ import {
20
20
  defaultSignalSource,
21
21
  guardSignalCallback,
22
22
  reportSignalError
23
- } from "./index-endzd8sj.js";
23
+ } from "./index-vbf2p6me.js";
24
24
  import {
25
25
  createToolInvoker
26
- } from "./index-6wr9f6qm.js";
26
+ } from "./index-jqtsc9mj.js";
27
27
  import {
28
28
  WaitTimeoutError,
29
29
  createCli,
@@ -32,10 +32,10 @@ import {
32
32
  fetchPinnedDocument,
33
33
  readCapped,
34
34
  runWaitOperation
35
- } from "./index-9my2n66s.js";
35
+ } from "./index-s2rchahr.js";
36
36
  import {
37
37
  collectToolSurface
38
- } from "./index-9c5dbr5q.js";
38
+ } from "./index-k2zczx1g.js";
39
39
  import {
40
40
  createRuntimeToolFactory,
41
41
  defineRuntimeTool
@@ -44,19 +44,19 @@ import {
44
44
  collectTools,
45
45
  createToolRunner,
46
46
  formatToolError
47
- } from "./index-885chjxq.js";
47
+ } from "./index-7qy2ex0m.js";
48
48
  import {
49
49
  ToolExecutionControlError,
50
50
  coerceJsonArgs,
51
51
  executeToolMethod,
52
52
  isToolExecutionControlError,
53
53
  toolResultFromError
54
- } from "./index-k68e5jz0.js";
54
+ } from "./index-s4c8wy8m.js";
55
55
  import {
56
56
  getRequestContext,
57
57
  getTraceId,
58
58
  runWithRequestContext
59
- } from "./index-e0a57ymn.js";
59
+ } from "./index-nemjkxjp.js";
60
60
  import {
61
61
  ManagedFileError
62
62
  } from "./index-bfcpjw20.js";
@@ -79,8 +79,7 @@ import {
79
79
  } from "./index-nt1mp8km.js";
80
80
  import {
81
81
  defineContract
82
- } from "./index-hsz2mmk0.js";
83
- import"./index-rxfy4cq7.js";
82
+ } from "./index-vj3vvpaa.js";
84
83
  import {
85
84
  AppError,
86
85
  STITCH_ERROR_STATUS
@@ -1146,7 +1145,7 @@ async function resolveMcpRound(options) {
1146
1145
  });
1147
1146
  const requests = policy.inputRequired;
1148
1147
  const state = options.context.mcpReq.requestState();
1149
- const digest = await argumentsDigest(options.rawArgs);
1148
+ const digest = argumentsDigest(options.rawArgs);
1150
1149
  if (!state) {
1151
1150
  if (isRecord(options.context.mcpReq.inputResponses)) {
1152
1151
  return failedResolution({
package/llms-full.txt CHANGED
@@ -7617,9 +7617,17 @@ const hub = createWatchHub({
7617
7617
 
7618
7618
  ```ts
7619
7619
  // browser
7620
- import { createWatchClient } from 'stitchkit/live';
7621
-
7622
- const watch = createWatchClient(notesContract, { transport: live, holdMs: 30_000 });
7620
+ import { createWatchClient, watchTransport } from 'stitchkit/live';
7621
+
7622
+ // `live` is the bound realtime client. It goes through `watchTransport`, and
7623
+ // that is not ceremony: its `on` is generic over the contract it was bound to,
7624
+ // and TypeScript will not relate that signature to the one a watch client
7625
+ // declares. The conversion lives in the framework so it is written once rather
7626
+ // than in every application.
7627
+ const watch = createWatchClient(notesContract, {
7628
+ transport: watchTransport(live),
7629
+ holdMs: 30_000,
7630
+ });
7623
7631
 
7624
7632
  const handle = watch.list({ folder: 'inbox' });
7625
7633
  const stop = handle.subscribe({
@@ -7649,6 +7657,13 @@ Without that, one address changing wakes every watcher of the operation. Twenty
7649
7657
  conversations open means twenty reads for one change — nineteen of them publish
7650
7658
  nothing, because nothing changed, and the read is paid anyway.
7651
7659
 
7660
+ The digest is a plain 128-bit hash computed in JavaScript, on purpose. It used
7661
+ to be SHA-256 through Web Crypto, which exists **only in a secure context** — so
7662
+ every watched read threw on a page served over plain HTTP from a LAN name, while
7663
+ `localhost` (secure by definition) and every test kept saying the code was fine.
7664
+ The key identifies a question; it does not protect one, and an attacker who can
7665
+ choose your watch arguments can already ask the question directly.
7666
+
7652
7667
  ### Who shares with whom
7653
7668
 
7654
7669
  A key is `(service, action, digest of arguments)`, and everyone on that key gets
@@ -7737,8 +7752,23 @@ point:
7737
7752
  immediately. An event emitted before the memory update is a wake-up to the old
7738
7753
  value.
7739
7754
 
7740
- It is a **resource**, read with `context.use(...)`, not a function you call
7741
- wherever you need it. The kernel resolves its resource graph in the constructor
7755
+ Where there is no kernel — a server that binds its own signals and closes what
7756
+ it holds in an order it wrote open it directly instead:
7757
+
7758
+ ```ts
7759
+ const opened = await openKeyspace(sessions, { backend });
7760
+ opened.keyspace.get(id); // synchronous, from memory
7761
+ // on shutdown, in the order you chose:
7762
+ opened.stopAdmission();
7763
+ await opened.drain();
7764
+ await opened.close();
7765
+ ```
7766
+
7767
+ The resource is a thin wrapper over exactly that, so there is one implementation
7768
+ and two lifecycles, not two keyspaces.
7769
+
7770
+ Where there **is** a kernel it is a **resource**, read with `context.use(...)`,
7771
+ rather than a function you call wherever you need it. The kernel resolves its resource graph in the constructor
7742
7772
  and cannot register one afterwards, so a keyspace opened by a bare call inside
7743
7773
  another resource's `start` is never drained, never closed, and never ordered
7744
7774
  against the things that write to it.
@@ -13125,6 +13155,7 @@ realtime contract from `stitchkit`, and the server halves live in `stitchkit/app
13125
13155
  | `EventPayloads` / `EventTopicsOfMode` / `WireTopic` | _types_ | payload map keyed by wire topic, the topics of one mode, and the `prefix.name` a topic is addressed by |
13126
13156
  | `createWatchClient` | function | contract-shaped watch client: `watch.action(args)` returns a ref-counted handle sharing one subscription |
13127
13157
  | `WatchHandle` / `WatchListeners` / `WatchClientConfig` / `WatchTransport` / `TypedWatchClient` | _types_ | subscribe/close, the value and state listeners, hold window and open deadline, and the bound realtime client it rides |
13158
+ | `watchTransport` / `RealtimeClientLike` / `WatchInboundEvents` | function / _types_ | hands a bound realtime client to a watch client — two generic signatures TypeScript will not relate, converted once here |
13128
13159
  | `watchContract` | const | the four-event realtime contract a watched read travels on |
13129
13160
  | `WATCH_OPEN` / `WATCH_CLOSE` / `WATCH_VALUE` / `WATCH_STATE` | const | the event names of that contract |
13130
13161
  | `WatchKey` / `WatchKeySchema` / `watchKeyString` | type / schema / function | `(service, action, arguments digest)` — the identity both ends compute the same way |
@@ -13555,6 +13586,7 @@ the function that derives it from a snapshot,
13555
13586
  | Export | Kind | Summary |
13556
13587
  |--------|------|---------|
13557
13588
  | `defineKeyspace` / `keyspaceResource` | function | a named record set read synchronously from memory and written through one serialised chain; backend first, then memory, then the change event |
13589
+ | `openKeyspace` / `OpenedKeyspace` | function / type | the same keyspace opened directly, for an application that owns its own lifecycle rather than declaring resources to a kernel |
13558
13590
  | `KeyspaceBackend` / `KeyspaceDeclaration` / `KeyspaceChange` / `KeyspaceResourceConfig` / `OpenKeyspace` | _types_ | the four-method durability port, the declaration, the announced change, the resource's options and the published handle |
13559
13591
  | `memoryKeyspaceBackend` / `sqliteKeyspaceBackend` / `SqliteKeyspaceBackendConfig` | function / type | a disposable in-process backend, and one table with a key and a JSON payload over a caller-owned database |
13560
13592
  | `SqliteDatabase` / `SqliteStatement` / `SqliteValue` | _types_ | the minimal synchronous SQLite boundary the framework types against |