@loomwork/ui-hydration 0.0.0-stage → 0.1.1

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 (179) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +8 -0
  3. package/README.md +398 -2
  4. package/dist/cache.d.ts +71 -0
  5. package/dist/cache.d.ts.map +1 -0
  6. package/dist/cache.js +157 -0
  7. package/dist/cache.js.map +1 -0
  8. package/dist/chatSource.d.ts +105 -0
  9. package/dist/chatSource.d.ts.map +1 -0
  10. package/dist/chatSource.js +264 -0
  11. package/dist/chatSource.js.map +1 -0
  12. package/dist/client.d.ts +17 -0
  13. package/dist/client.d.ts.map +1 -0
  14. package/dist/client.js +36 -0
  15. package/dist/client.js.map +1 -0
  16. package/dist/contentRefs.d.ts +78 -0
  17. package/dist/contentRefs.d.ts.map +1 -0
  18. package/dist/contentRefs.js +129 -0
  19. package/dist/contentRefs.js.map +1 -0
  20. package/dist/controller.d.ts +228 -0
  21. package/dist/controller.d.ts.map +1 -0
  22. package/dist/controller.js +454 -0
  23. package/dist/controller.js.map +1 -0
  24. package/dist/dataProvider.d.ts +53 -0
  25. package/dist/dataProvider.d.ts.map +1 -0
  26. package/dist/dataProvider.js +77 -0
  27. package/dist/dataProvider.js.map +1 -0
  28. package/dist/esm/cache.js +151 -0
  29. package/dist/esm/cache.js.map +1 -0
  30. package/dist/esm/chatSource.js +259 -0
  31. package/dist/esm/chatSource.js.map +1 -0
  32. package/dist/esm/client.js +15 -0
  33. package/dist/esm/client.js.map +1 -0
  34. package/dist/esm/contentRefs.js +119 -0
  35. package/dist/esm/contentRefs.js.map +1 -0
  36. package/dist/esm/controller.js +449 -0
  37. package/dist/esm/controller.js.map +1 -0
  38. package/dist/esm/dataProvider.js +72 -0
  39. package/dist/esm/dataProvider.js.map +1 -0
  40. package/dist/esm/index.js +15 -0
  41. package/dist/esm/index.js.map +1 -0
  42. package/dist/esm/invoker.js +2 -0
  43. package/dist/esm/invoker.js.map +1 -0
  44. package/dist/esm/invokers.js +310 -0
  45. package/dist/esm/invokers.js.map +1 -0
  46. package/dist/esm/loomEffectProvider.js +96 -0
  47. package/dist/esm/loomEffectProvider.js.map +1 -0
  48. package/dist/esm/params.js +207 -0
  49. package/dist/esm/params.js.map +1 -0
  50. package/dist/esm/path.js +24 -0
  51. package/dist/esm/path.js.map +1 -0
  52. package/dist/esm/plan.js +190 -0
  53. package/dist/esm/plan.js.map +1 -0
  54. package/dist/esm/projection.js +230 -0
  55. package/dist/esm/projection.js.map +1 -0
  56. package/dist/esm/provider.js +416 -0
  57. package/dist/esm/provider.js.map +1 -0
  58. package/dist/esm/repair.js +53 -0
  59. package/dist/esm/repair.js.map +1 -0
  60. package/dist/esm/reshape.js +156 -0
  61. package/dist/esm/reshape.js.map +1 -0
  62. package/dist/esm/restricted.js +54 -0
  63. package/dist/esm/restricted.js.map +1 -0
  64. package/dist/esm/scheduler.js +247 -0
  65. package/dist/esm/scheduler.js.map +1 -0
  66. package/dist/esm/select.js +18 -0
  67. package/dist/esm/select.js.map +1 -0
  68. package/dist/esm/shape.js +34 -0
  69. package/dist/esm/shape.js.map +1 -0
  70. package/dist/esm/split/client.js +312 -0
  71. package/dist/esm/split/client.js.map +1 -0
  72. package/dist/esm/split/index.js +3 -0
  73. package/dist/esm/split/index.js.map +1 -0
  74. package/dist/esm/split/server.js +488 -0
  75. package/dist/esm/split/server.js.map +1 -0
  76. package/dist/esm/split/shared.js +13 -0
  77. package/dist/esm/split/shared.js.map +1 -0
  78. package/dist/index.d.ts +29 -0
  79. package/dist/index.d.ts.map +1 -0
  80. package/dist/index.js +63 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/invoker.d.ts +10 -0
  83. package/dist/invoker.d.ts.map +1 -0
  84. package/dist/invoker.js +3 -0
  85. package/dist/invoker.js.map +1 -0
  86. package/dist/invokers.d.ts +36 -0
  87. package/dist/invokers.d.ts.map +1 -0
  88. package/dist/invokers.js +315 -0
  89. package/dist/invokers.js.map +1 -0
  90. package/dist/loomEffectProvider.d.ts +67 -0
  91. package/dist/loomEffectProvider.d.ts.map +1 -0
  92. package/dist/loomEffectProvider.js +101 -0
  93. package/dist/loomEffectProvider.js.map +1 -0
  94. package/dist/params.d.ts +103 -0
  95. package/dist/params.d.ts.map +1 -0
  96. package/dist/params.js +221 -0
  97. package/dist/params.js.map +1 -0
  98. package/dist/path.d.ts +12 -0
  99. package/dist/path.d.ts.map +1 -0
  100. package/dist/path.js +28 -0
  101. package/dist/path.js.map +1 -0
  102. package/dist/plan.d.ts +56 -0
  103. package/dist/plan.d.ts.map +1 -0
  104. package/dist/plan.js +199 -0
  105. package/dist/plan.js.map +1 -0
  106. package/dist/projection.d.ts +122 -0
  107. package/dist/projection.d.ts.map +1 -0
  108. package/dist/projection.js +238 -0
  109. package/dist/projection.js.map +1 -0
  110. package/dist/provider.d.ts +152 -0
  111. package/dist/provider.d.ts.map +1 -0
  112. package/dist/provider.js +421 -0
  113. package/dist/provider.js.map +1 -0
  114. package/dist/repair.d.ts +18 -0
  115. package/dist/repair.d.ts.map +1 -0
  116. package/dist/repair.js +57 -0
  117. package/dist/repair.js.map +1 -0
  118. package/dist/reshape.d.ts +32 -0
  119. package/dist/reshape.d.ts.map +1 -0
  120. package/dist/reshape.js +164 -0
  121. package/dist/reshape.js.map +1 -0
  122. package/dist/restricted.d.ts +21 -0
  123. package/dist/restricted.d.ts.map +1 -0
  124. package/dist/restricted.js +61 -0
  125. package/dist/restricted.js.map +1 -0
  126. package/dist/scheduler.d.ts +82 -0
  127. package/dist/scheduler.d.ts.map +1 -0
  128. package/dist/scheduler.js +254 -0
  129. package/dist/scheduler.js.map +1 -0
  130. package/dist/select.d.ts +34 -0
  131. package/dist/select.d.ts.map +1 -0
  132. package/dist/select.js +21 -0
  133. package/dist/select.js.map +1 -0
  134. package/dist/shape.d.ts +3 -0
  135. package/dist/shape.d.ts.map +1 -0
  136. package/dist/shape.js +37 -0
  137. package/dist/shape.js.map +1 -0
  138. package/dist/split/client.d.ts +135 -0
  139. package/dist/split/client.d.ts.map +1 -0
  140. package/dist/split/client.js +316 -0
  141. package/dist/split/client.js.map +1 -0
  142. package/dist/split/index.d.ts +5 -0
  143. package/dist/split/index.d.ts.map +1 -0
  144. package/dist/split/index.js +14 -0
  145. package/dist/split/index.js.map +1 -0
  146. package/dist/split/server.d.ts +160 -0
  147. package/dist/split/server.d.ts.map +1 -0
  148. package/dist/split/server.js +496 -0
  149. package/dist/split/server.js.map +1 -0
  150. package/dist/split/shared.d.ts +6 -0
  151. package/dist/split/shared.d.ts.map +1 -0
  152. package/dist/split/shared.js +19 -0
  153. package/dist/split/shared.js.map +1 -0
  154. package/package.json +77 -3
  155. package/src/cache.ts +211 -0
  156. package/src/chatSource.ts +378 -0
  157. package/src/client.ts +46 -0
  158. package/src/contentRefs.ts +195 -0
  159. package/src/controller.ts +708 -0
  160. package/src/dataProvider.ts +141 -0
  161. package/src/index.ts +86 -0
  162. package/src/invoker.ts +14 -0
  163. package/src/invokers.ts +348 -0
  164. package/src/loomEffectProvider.ts +136 -0
  165. package/src/params.ts +314 -0
  166. package/src/path.ts +26 -0
  167. package/src/plan.ts +197 -0
  168. package/src/projection.ts +297 -0
  169. package/src/provider.ts +657 -0
  170. package/src/repair.ts +70 -0
  171. package/src/reshape.ts +193 -0
  172. package/src/restricted.ts +69 -0
  173. package/src/scheduler.ts +284 -0
  174. package/src/select.ts +59 -0
  175. package/src/shape.ts +39 -0
  176. package/src/split/client.ts +452 -0
  177. package/src/split/index.ts +18 -0
  178. package/src/split/server.ts +746 -0
  179. package/src/split/shared.ts +18 -0
@@ -0,0 +1,151 @@
1
+ import { canonicalJson, fnv1a64 } from "@loomwork/core";
2
+ // ── Result cache ──────────────────────────────────────────────────────────────
3
+ // `scope: "global"` shares one entry per handle; `scope: "principal"` keys on (principal, handle)
4
+ // and refuses to cache when no principal is known — a per-viewer result must never leak to the
5
+ // next viewer. TTL is against an injected clock so tests never sleep.
6
+ //
7
+ // The key also carries the SPEC's fingerprint: a handle whose provider was repaired (a remapped
8
+ // schema pin, a different tool, a new reshaping) is a different question, and must not be
9
+ // answered from the entry the old spec left behind. `invalidate(handle)` still drops every
10
+ // spec-generation of that handle, so the fingerprint never makes a stale entry unreachable.
11
+ // NUL: cannot appear in a handle, so `invalidate(handle)`'s suffix match can never mis-hit
12
+ // another handle whose name happens to contain the delimiter (e.g. a handle literally named "h").
13
+ const SEP = String.fromCharCode(0);
14
+ // Memoized per spec OBJECT: the controller fingerprints the same spec on every `schedule`, and
15
+ // `canonicalJson` walks the whole graph. A WeakMap keeps that O(1) without pinning specs alive.
16
+ const fingerprints = new WeakMap();
17
+ let unhashable = 0;
18
+ /**
19
+ * The content fingerprint of a provider spec — the identity both the cache key and the
20
+ * controller's "have I already loaded THIS spec?" check are taken over, so the two can never
21
+ * disagree about what "the same provider" means.
22
+ *
23
+ * Total: `canonicalJson` throws on a spec carrying a non-finite number, a bigint or a nest past
24
+ * its depth cap. Such a spec gets a fingerprint unique to the OBJECT, which is conservative in
25
+ * both directions — the same object is still recognised as itself (no reload storm), and two
26
+ * structurally identical unhashable specs are treated as different (no cache hit).
27
+ */
28
+ export function specFingerprint(spec) {
29
+ const memo = fingerprints.get(spec);
30
+ if (memo !== undefined)
31
+ return memo;
32
+ let fp;
33
+ try {
34
+ fp = fnv1a64(canonicalJson(spec));
35
+ }
36
+ catch {
37
+ unhashable += 1;
38
+ fp = `unhashable-${unhashable}`;
39
+ }
40
+ fingerprints.set(spec, fp);
41
+ return fp;
42
+ }
43
+ /** `{kind: version}` for the bound kinds whose contract declares a version; `undefined` if none. */
44
+ function contractVersions(kinds, contracts) {
45
+ if (contracts === undefined)
46
+ return undefined;
47
+ let out;
48
+ for (const k of kinds) {
49
+ if (!Object.hasOwn(contracts, k))
50
+ continue;
51
+ const v = contracts[k]?.version;
52
+ if (typeof v !== "string")
53
+ continue;
54
+ // Null-prototype-safe: a kind named `__proto__` must not set a prototype.
55
+ out ??= Object.create(null);
56
+ out[k] = v;
57
+ }
58
+ return out;
59
+ }
60
+ /**
61
+ * The one normal form of a kinds list — sorted, de-duplicated. The cache key and the controller's
62
+ * load fingerprint both use it, so they can never disagree about whether two kinds lists are the
63
+ * same question.
64
+ */
65
+ export function normaliseKinds(kinds) {
66
+ return [...new Set(kinds)].sort();
67
+ }
68
+ /** `""` for the plain variant; `undefined` for one that cannot be keyed (never cached). */
69
+ function variantKey(params, kinds, versions) {
70
+ if (Object.keys(params).length === 0 && kinds.length === 0)
71
+ return "";
72
+ try {
73
+ // `versions` is added ONLY when present, so every key without one is what it always was.
74
+ return fnv1a64(canonicalJson(versions === undefined ? { params, kinds } : { params, kinds, versions }));
75
+ }
76
+ catch {
77
+ return undefined;
78
+ }
79
+ }
80
+ /** The default cap on cached results (U17). Generous: a surface with fewer live questions never notices it. */
81
+ export const DEFAULT_CACHE_MAX_ENTRIES = 10_000;
82
+ export class HydrationCache {
83
+ clock;
84
+ // A Map iterates in insertion order, so re-inserting on use keeps the least recently used entry
85
+ // FIRST: eviction and promotion are both O(1).
86
+ entries = new Map();
87
+ maxEntries;
88
+ constructor(clock = Date.now, opts = {}) {
89
+ this.clock = clock;
90
+ const max = opts.maxEntries ?? DEFAULT_CACHE_MAX_ENTRIES;
91
+ if (!Number.isInteger(max) || max < 1)
92
+ throw new RangeError(`HydrationCache maxEntries must be a positive integer, got ${max}`);
93
+ this.maxEntries = max;
94
+ }
95
+ /** How many results are held now (expired ones count until they are read or evicted). */
96
+ get size() {
97
+ return this.entries.size;
98
+ }
99
+ key(spec, principalId, variant) {
100
+ const kinds = spec.outputCheck === "contract" ? normaliseKinds(variant.kinds ?? []) : [];
101
+ const v = variantKey(variant.params ?? {}, kinds, contractVersions(kinds, variant.contracts));
102
+ if (v === undefined)
103
+ return undefined;
104
+ const fp = specFingerprint(spec);
105
+ // The handle stays LAST: `invalidate(handle)` suffix-matches it across every variant.
106
+ if (spec.cachePolicy.scope === "global")
107
+ return `g${SEP}${fp}${SEP}${v}${SEP}${spec.handle}`;
108
+ return principalId === undefined
109
+ ? undefined
110
+ : `p${SEP}${principalId}${SEP}${fp}${SEP}${v}${SEP}${spec.handle}`;
111
+ }
112
+ get(spec, principalId, variant = {}) {
113
+ const k = this.key(spec, principalId, variant);
114
+ if (k === undefined)
115
+ return undefined;
116
+ const e = this.entries.get(k);
117
+ if (!e)
118
+ return undefined;
119
+ if (e.expiresAt <= this.clock()) {
120
+ this.entries.delete(k);
121
+ return undefined;
122
+ }
123
+ // Promote: most recently used goes to the back of the iteration order.
124
+ this.entries.delete(k);
125
+ this.entries.set(k, e);
126
+ return { value: e.value };
127
+ }
128
+ set(spec, principalId, value, variant = {}) {
129
+ const k = this.key(spec, principalId, variant);
130
+ if (k === undefined || spec.cachePolicy.ttlSeconds <= 0)
131
+ return;
132
+ this.entries.delete(k);
133
+ this.entries.set(k, { value, expiresAt: this.clock() + spec.cachePolicy.ttlSeconds * 1000 });
134
+ while (this.entries.size > this.maxEntries) {
135
+ const oldest = this.entries.keys().next();
136
+ if (oldest.done === true)
137
+ break;
138
+ this.entries.delete(oldest.value);
139
+ }
140
+ }
141
+ invalidate(handle) {
142
+ if (handle === undefined) {
143
+ this.entries.clear();
144
+ return;
145
+ }
146
+ for (const k of [...this.entries.keys()])
147
+ if (k.endsWith(SEP + handle))
148
+ this.entries.delete(k);
149
+ }
150
+ }
151
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,259 @@
1
+ import { useChatContracts } from "@loomwork/ui-core/browser";
2
+ /**
3
+ * The most a durable reasoning response may weigh (bytes, or characters for the `text()` check). loom's
4
+ * route caps the readable text at 1 MiB, so 2 MiB leaves room for the JSON envelope and escaping.
5
+ * Residual: a host `fetch` that only offers `json()` and sends no `Content-Length` is not bounded here.
6
+ */
7
+ export const MAX_REASONING_RESPONSE_BYTES = 2 * 1024 * 1024;
8
+ /** A stream open at least this long (ms) was healthy: its end is not a failure streak. */
9
+ const HEALTHY_MS = 5000;
10
+ const EVENTS = [
11
+ "status",
12
+ "partial",
13
+ "delta",
14
+ "reasoning",
15
+ "reasoning-partial",
16
+ "reset",
17
+ "message",
18
+ "closed",
19
+ "reconnect",
20
+ "error",
21
+ ];
22
+ const changedBy = (r) => r?.ok === true && r.changed === true;
23
+ const finding = (code, message) => ({ code, severity: "error", message });
24
+ const defaultTimer = (fn, ms) => {
25
+ const id = setTimeout(fn, ms);
26
+ return () => clearTimeout(id);
27
+ };
28
+ export function connectConversation(options) {
29
+ // The transcript this feeds is published as a Chat value; its contract is deferred in core.
30
+ useChatContracts();
31
+ const { feed, baseUrl } = options;
32
+ const pageLimit = Math.max(1, Math.min(options.pageLimit ?? 50, 500));
33
+ const maxFrame = options.maxFrameBytes ?? 256 * 1024;
34
+ const timer = options.setTimer ?? defaultTimer;
35
+ const ac = new AbortController();
36
+ let source;
37
+ let cancelReopen;
38
+ let closed = false;
39
+ let loadingOlder = false;
40
+ let failures = 0;
41
+ let openedAt = 0;
42
+ const now = options.now ?? Date.now;
43
+ const base = Math.max(1, options.reconnectMs ?? 2000);
44
+ const ceiling = Math.max(base, options.maxReconnectMs ?? 30_000);
45
+ const floor = Math.max(1, options.reconnectFloorMs ?? 250);
46
+ /** Consecutive failures double the wait, capped; the first is the base. */
47
+ const backoff = () => Math.min(ceiling, base * 2 ** Math.min(failures, 16));
48
+ const report = (code, message) => options.onFinding?.(finding(code, message));
49
+ const publish = () => options.publish(feed.value(), { streaming: feed.streaming });
50
+ async function page(cursor) {
51
+ const q = `direction=backward&limit=${pageLimit}${cursor === undefined ? "" : `&cursor=${encodeURIComponent(cursor)}`}`;
52
+ let body;
53
+ try {
54
+ const res = await options.fetch(`${baseUrl}/history?${q}`, { signal: ac.signal });
55
+ if (!res.ok)
56
+ throw new Error("not ok");
57
+ body = await res.json();
58
+ }
59
+ catch {
60
+ if (!ac.signal.aborted)
61
+ report("history-unavailable", "the conversation history could not be loaded");
62
+ return false;
63
+ }
64
+ if (closed)
65
+ return false;
66
+ const r = feed.applyHistory(body, "backward");
67
+ if (!r.ok) {
68
+ report(r.finding.code, r.finding.message);
69
+ return false;
70
+ }
71
+ if (r.changed)
72
+ publish();
73
+ return true;
74
+ }
75
+ function open() {
76
+ if (closed)
77
+ return;
78
+ const cursor = feed.resumeCursor();
79
+ const url = `${baseUrl}/events${cursor === undefined ? "" : `?cursor=${encodeURIComponent(cursor)}`}`;
80
+ const es = options.openStream(url);
81
+ source = es;
82
+ openedAt = now();
83
+ for (const name of EVENTS)
84
+ es.addEventListener(name, (ev) => {
85
+ if (closed || source !== es)
86
+ return;
87
+ const raw = ev.data;
88
+ let data;
89
+ if (typeof raw === "string") {
90
+ if (raw.length > maxFrame)
91
+ return report("frame-too-large", "a conversation event was too large and was ignored");
92
+ try {
93
+ data = JSON.parse(raw);
94
+ }
95
+ catch {
96
+ return report("frame-invalid", "a conversation event was not understood and was ignored");
97
+ }
98
+ }
99
+ const r = feed.applyEvent(name, data);
100
+ if (!r.ok) {
101
+ // The feed's finding text is fixed; an `error` event's body was never read.
102
+ report(r.finding.code, r.finding.message);
103
+ return;
104
+ }
105
+ if (r.reconnect !== true)
106
+ failures = 0; // a frame the feed understood: the connection works
107
+ if (r.changed)
108
+ publish();
109
+ if (r.reconnect === true) {
110
+ // Asked to recycle. A server that does so right after opening is looping: back off like a failure.
111
+ const healthy = now() - openedAt >= HEALTHY_MS;
112
+ if (healthy)
113
+ failures = 0;
114
+ reopen(failures === 0 ? floor : Math.max(floor, backoff()));
115
+ if (!healthy)
116
+ failures++;
117
+ }
118
+ });
119
+ // A stream the platform has given up on (it keeps trying by itself while CONNECTING).
120
+ es.onerror = () => {
121
+ if (es.readyState === 2) {
122
+ // A stream that stayed up long enough was healthy: its end starts the backoff afresh, and the
123
+ // first wait is the documented base.
124
+ if (now() - openedAt >= HEALTHY_MS)
125
+ failures = 0;
126
+ const delay = backoff();
127
+ failures++;
128
+ reopen(delay);
129
+ }
130
+ };
131
+ }
132
+ function reopen(delay) {
133
+ if (closed)
134
+ return;
135
+ source?.close();
136
+ source = undefined;
137
+ cancelReopen?.();
138
+ cancelReopen = timer(() => {
139
+ cancelReopen = undefined;
140
+ open();
141
+ }, delay);
142
+ }
143
+ /** The first page; on failure (and nothing to show) try again after a backoff instead of giving up. */
144
+ async function begin() {
145
+ const ok = await page();
146
+ if (closed)
147
+ return;
148
+ if (ok || feed.value().entries.length > 0) {
149
+ if (ok)
150
+ failures = 0;
151
+ open();
152
+ return;
153
+ }
154
+ const delay = backoff();
155
+ failures++;
156
+ cancelReopen?.();
157
+ cancelReopen = timer(() => {
158
+ cancelReopen = undefined;
159
+ void begin();
160
+ }, delay);
161
+ }
162
+ const loadingReasoning = new Set();
163
+ async function reasoningFor(cursor) {
164
+ const { feed: f } = options;
165
+ if (options.reasoning !== true || closed || loadingReasoning.has(cursor))
166
+ return;
167
+ if (f.beginReasoning === undefined || !changedBy(f.beginReasoning(cursor)))
168
+ return;
169
+ loadingReasoning.add(cursor);
170
+ publish(); // the entry now reads "loading"
171
+ try {
172
+ let body;
173
+ let gone = false;
174
+ try {
175
+ const res = await options.fetch(`${baseUrl}/reasoning?cursor=${encodeURIComponent(cursor)}`, {
176
+ signal: ac.signal,
177
+ });
178
+ if (!res.ok) {
179
+ gone = res.status === 404 || res.status === 403;
180
+ throw new Error("not ok");
181
+ }
182
+ const declared = Number(res.headers?.get("content-length"));
183
+ if (Number.isFinite(declared) && declared > MAX_REASONING_RESPONSE_BYTES)
184
+ throw new Error("too large");
185
+ if (res.text !== undefined) {
186
+ const raw = await res.text();
187
+ if (raw.length > MAX_REASONING_RESPONSE_BYTES)
188
+ throw new Error("too large");
189
+ body = JSON.parse(raw);
190
+ }
191
+ else
192
+ body = await res.json();
193
+ }
194
+ catch {
195
+ if (closed)
196
+ return;
197
+ // A refusal is a settled "none for you" (no retry); anything else is a failure the reader can retry.
198
+ if (gone)
199
+ f.reasoningUnavailable?.(cursor);
200
+ else {
201
+ f.reasoningFailed?.(cursor);
202
+ report("reasoning-unavailable", "the reasoning could not be loaded");
203
+ }
204
+ publish();
205
+ return;
206
+ }
207
+ if (closed)
208
+ return;
209
+ if (changedBy(f.applyReasoning?.(cursor, body)))
210
+ publish();
211
+ }
212
+ finally {
213
+ loadingReasoning.delete(cursor);
214
+ }
215
+ }
216
+ return {
217
+ start: begin,
218
+ loadReasoning: reasoningFor,
219
+ async loadOlder() {
220
+ const cursor = feed.olderCursor();
221
+ if (cursor === undefined || loadingOlder || closed)
222
+ return;
223
+ loadingOlder = true;
224
+ try {
225
+ await page(cursor);
226
+ }
227
+ finally {
228
+ loadingOlder = false;
229
+ }
230
+ },
231
+ close() {
232
+ closed = true;
233
+ ac.abort();
234
+ cancelReopen?.();
235
+ source?.close();
236
+ source = undefined;
237
+ },
238
+ };
239
+ }
240
+ /**
241
+ * A `publish` that writes the transcript to a handle THROUGH a controller's gate (`publishValue`):
242
+ * refused (a finding, nothing written) for a handle the experience does not bind, one that is
243
+ * restricted for the viewer, one that is not a `Chat`, or a value that fails the `ChatValue`
244
+ * contract. `pending` while a reply streams (a screen reader holds its announcements), `ready` once
245
+ * it is durable. Writing the store directly would bypass all of that.
246
+ */
247
+ export function publishChat(controller, handle, onFinding) {
248
+ // It publishes Chat values; their contract is deferred in core.
249
+ useChatContracts();
250
+ return (value, meta) => {
251
+ const r = controller.publishValue(handle, value, {
252
+ state: meta.streaming ? "pending" : "ready",
253
+ kind: "Chat",
254
+ });
255
+ if (!r.ok)
256
+ onFinding?.(r.finding);
257
+ };
258
+ }
259
+ //# sourceMappingURL=chatSource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chatSource.js","sourceRoot":"","sources":["../../src/chatSource.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ // ── @loomwork/ui-hydration/client: the browser entry ─────────────────────────────────
2
+ // Everything a browser needs for SPLIT hydration and nothing that executes a provider: the split
3
+ // controller, the loader contract it calls, the store it publishes to, and param checks. The
4
+ // server half (`handleInventory`, `serveHandles`, `executeProvider`, the invokers and reshapers)
5
+ // stays on the root entry, `@loomwork/ui-hydration`, which imports the sandbox and the MCP plugin.
6
+ // `clientEntry.test.ts` fails the build if this entry's module graph ever reaches them.
7
+ export { HYDRATION_PROJECTION_ID, HYDRATION_PROJECTION_VERSION, HYDRATION_SET_EVENT, HydrationStore, hydrationProjection, isReservedHandle, } from "./projection";
8
+ export { HYDRATION_VALUES_PREFIX, handleOfPath } from "./path";
9
+ export { DEFAULT_TIME_RANGE, checkParams } from "./params";
10
+ export { stepReferences, terminalSteps, topoWaves } from "./plan";
11
+ export { jsonPathSelect } from "./select";
12
+ export { outputShapeHashOf } from "./shape";
13
+ export { SplitHydrationController } from "./split/client";
14
+ export { connectConversation, publishChat } from "./chatSource";
15
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,iGAAiG;AACjG,6FAA6F;AAC7F,iGAAiG;AACjG,mGAAmG;AACnG,wFAAwF;AACxF,OAAO,EACL,uBAAuB,EACvB,4BAA4B,EAC5B,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAQtB,OAAO,EAAE,uBAAuB,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG3D,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE1C,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAG5C,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAQ1D,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,119 @@
1
+ import { contentRefArgFindings, contentRefEntry, markedTopLevelKeys, parseContentRefTable, provenIn, stripNullContentRefs, toolKey, } from "@loomwork/ui-core";
2
+ import { applyParamsDetailed } from "./params";
3
+ // U9b, the SERVER half: a module of its own so that the browser entry (which imports params.ts) never
4
+ // reaches `@loomwork/ui-core`'s root, which pulls in node:crypto.
5
+ const bad = (code, message) => ({ code, severity: "error", message });
6
+ export const contentRefEnv = (table, spec, gate, who) => ({
7
+ table,
8
+ handle: spec.handle,
9
+ steps: new Map(spec.executionGraph.map((s) => [s.stepId, s])),
10
+ ...(gate !== undefined && { acceptContentRefParam: gate }),
11
+ ...(who.principalId !== undefined && { principalId: who.principalId }),
12
+ ...(who.principal !== undefined && { principal: who.principal }),
13
+ });
14
+ /**
15
+ * U9b, defence in depth behind composition: after params are applied and BEFORE anything resolves,
16
+ * a marked content-reference argument must be absent / null, a step reference to an output its tool
17
+ * declares to be a content reference, or a param the host's gate accepted. A model-authored literal,
18
+ * an echo / concat reference, or an ungated param refuses the step, so the call is never made.
19
+ * `paramFilled` says a param filled a guarded argument (the caller then caches per principal only).
20
+ */
21
+ export function checkContentRefs(node, applied, env) {
22
+ const entry = contentRefEntry(env.table, toolKey(node));
23
+ if (entry === undefined)
24
+ return { paramFilled: false };
25
+ const skip = new Set();
26
+ // A tool whose schema mentions the marker but cannot be read: every replaced param is gated.
27
+ const guarded = entry === "unknown"
28
+ ? new Set(applied.replaced)
29
+ : typeof entry === "string"
30
+ ? new Set()
31
+ : markedTopLevelKeys(entry);
32
+ let paramFilled = false;
33
+ for (const name of applied.replaced) {
34
+ if (!guarded.has(name))
35
+ continue;
36
+ paramFilled = true;
37
+ let ok = false;
38
+ try {
39
+ ok =
40
+ env.acceptContentRefParam?.({
41
+ handle: env.handle,
42
+ stepId: node.stepId,
43
+ toolName: node.toolName,
44
+ ...(node.kind !== undefined && { kind: node.kind }),
45
+ param: name,
46
+ value: applied.args[name],
47
+ ...(env.principalId !== undefined && { principalId: env.principalId }),
48
+ ...(env.principal !== undefined && { principal: env.principal }),
49
+ }) === true;
50
+ }
51
+ catch {
52
+ ok = false;
53
+ }
54
+ if (!ok)
55
+ return {
56
+ paramFilled,
57
+ finding: bad("media-arg-param-refused", `step "${node.stepId}": the param for "${name}" fills a content-reference argument and the host did not accept it`),
58
+ };
59
+ skip.add(name);
60
+ }
61
+ const found = contentRefArgFindings(applied.args, entry, `step "${node.stepId}"`, {
62
+ skipKeys: skip,
63
+ proven: provenIn(env.table, env.steps, node.stepId, toolKey),
64
+ }).find((f) => f.severity === "error");
65
+ return { paramFilled, ...(found !== undefined && { finding: found }) };
66
+ }
67
+ /** `args` without JSON null at the step's marked paths (a tool never sees `null` where a reference belongs). */
68
+ export function finishContentRefArgs(node, args, table) {
69
+ const entry = contentRefEntry(table, toolKey(node));
70
+ return entry === undefined || typeof entry === "string"
71
+ ? args
72
+ : stripNullContentRefs(args, entry);
73
+ }
74
+ /**
75
+ * Judge a whole provider BEFORE a cache lookup: the same guard `executeProvider` runs per step, over
76
+ * every step, with the gate asked now. A cached value was produced for an argument the gate accepted
77
+ * once; serving it to another principal, or after the host revoked it, would skip the gate. A
78
+ * refusal means: do not serve from the cache (the load itself will refuse). `paramFilled` means the
79
+ * answer depends on who is asking: cache it per principal, never globally.
80
+ */
81
+ export function inspectContentRefs(spec, params, env) {
82
+ let paramFilled = false;
83
+ for (const step of spec.executionGraph) {
84
+ const applied = applyParamsDetailed(step, params);
85
+ const r = checkContentRefs(step, applied, env);
86
+ paramFilled ||= r.paramFilled;
87
+ if (r.finding !== undefined)
88
+ return { refusal: r.finding, paramFilled };
89
+ }
90
+ return { paramFilled };
91
+ }
92
+ const PRINCIPAL_SCOPED = new WeakMap();
93
+ /** `spec` with `cachePolicy.scope: "principal"` (memoised per spec object, so its fingerprint is computed once). */
94
+ export function principalScoped(spec) {
95
+ if (spec.cachePolicy.scope === "principal")
96
+ return spec;
97
+ let s = PRINCIPAL_SCOPED.get(spec);
98
+ if (s === undefined) {
99
+ s = { ...spec, cachePolicy: { ...spec.cachePolicy, scope: "principal" } };
100
+ PRINCIPAL_SCOPED.set(spec, s);
101
+ }
102
+ return s;
103
+ }
104
+ /** `contentRefArgs` is missing or malformed: a configuration error, never read as "nothing is guarded". */
105
+ export class ContentRefConfigError extends Error {
106
+ code = "content-ref-config";
107
+ constructor(where) {
108
+ super(`${where}: contentRefArgs is required: pass the table contentRefArgsFrom(capabilities) builds (a malformed or missing one is refused, never read as "none"), or the explicit opt-out "none"`);
109
+ this.name = "ContentRefConfigError";
110
+ }
111
+ }
112
+ /** Throws {@link ContentRefConfigError} unless `value` is `"none"` or exactly a well-formed table. */
113
+ export function assertContentRefConfig(value, where) {
114
+ if (value === "none")
115
+ return;
116
+ if (parseContentRefTable(value) === undefined)
117
+ throw new ContentRefConfigError(where);
118
+ }
119
+ //# sourceMappingURL=contentRefs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contentRefs.js","sourceRoot":"","sources":["../../src/contentRefs.ts"],"names":[],"mappings":"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"}