@buoy-gg/agent-core 7.0.41 → 7.0.43

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 (201) hide show
  1. package/lib/commonjs/blocks/receipts.js +1 -761
  2. package/lib/commonjs/blocks/runId.js +1 -31
  3. package/lib/commonjs/blocks/types.js +1 -140
  4. package/lib/commonjs/blocks/uiTool.js +6 -1405
  5. package/lib/commonjs/catalog/catalog.g.js +5 -4556
  6. package/lib/commonjs/catalog/catalog.source.json +249 -0
  7. package/lib/commonjs/catalog/catalog.types.g.js +1 -46
  8. package/lib/commonjs/catalog/normalizeParams.js +2 -332
  9. package/lib/commonjs/catalog/signature.js +1 -68
  10. package/lib/commonjs/catalog/snapshotReads.js +1 -263
  11. package/lib/commonjs/catalog/toProviderTools.js +2 -145
  12. package/lib/commonjs/catalog/validateParams.js +1 -129
  13. package/lib/commonjs/context/buildContextPack.js +1 -489
  14. package/lib/commonjs/effects/digest.js +1 -34
  15. package/lib/commonjs/effects/ledger.js +1 -345
  16. package/lib/commonjs/engine/askGate.js +1 -287
  17. package/lib/commonjs/engine/effectFor.js +1 -223
  18. package/lib/commonjs/engine/evidence.js +3 -111
  19. package/lib/commonjs/engine/historyBudget.js +1 -363
  20. package/lib/commonjs/engine/retrieve.js +1 -214
  21. package/lib/commonjs/engine/runAgentTurn.js +8 -1836
  22. package/lib/commonjs/engine/systemPrompt.js +54 -163
  23. package/lib/commonjs/engine/textToolCalls.js +1 -276
  24. package/lib/commonjs/engine/tokenCalibration.js +1 -81
  25. package/lib/commonjs/engine/verify.js +4 -297
  26. package/lib/commonjs/index.js +1 -384
  27. package/lib/commonjs/policy/labels.js +1 -319
  28. package/lib/commonjs/policy/policy.js +1 -148
  29. package/lib/commonjs/policy/redact.js +1 -172
  30. package/lib/commonjs/providers/anthropic.js +3 -445
  31. package/lib/commonjs/providers/openai.js +4 -324
  32. package/lib/commonjs/providers/problem.js +1 -98
  33. package/lib/commonjs/providers/sse.js +1 -240
  34. package/lib/commonjs/providers/streamTimer.js +1 -123
  35. package/lib/commonjs/providers/transport.js +1 -123
  36. package/lib/commonjs/providers/types.js +1 -6
  37. package/lib/commonjs/providers/xhrStream.js +1 -212
  38. package/lib/commonjs/realNow.js +1 -0
  39. package/lib/commonjs/session.js +1 -393
  40. package/lib/commonjs/types.js +0 -1
  41. package/lib/module/blocks/receipts.js +1 -755
  42. package/lib/module/blocks/runId.js +1 -26
  43. package/lib/module/blocks/types.js +1 -134
  44. package/lib/module/blocks/uiTool.js +6 -1396
  45. package/lib/module/catalog/catalog.g.js +5 -4552
  46. package/lib/module/catalog/catalog.source.json +249 -0
  47. package/lib/module/catalog/catalog.types.g.js +1 -42
  48. package/lib/module/catalog/normalizeParams.js +2 -326
  49. package/lib/module/catalog/signature.js +1 -63
  50. package/lib/module/catalog/snapshotReads.js +1 -259
  51. package/lib/module/catalog/toProviderTools.js +2 -139
  52. package/lib/module/catalog/validateParams.js +1 -124
  53. package/lib/module/context/buildContextPack.js +1 -485
  54. package/lib/module/effects/digest.js +1 -29
  55. package/lib/module/effects/ledger.js +1 -339
  56. package/lib/module/engine/askGate.js +1 -280
  57. package/lib/module/engine/effectFor.js +1 -220
  58. package/lib/module/engine/evidence.js +3 -105
  59. package/lib/module/engine/historyBudget.js +1 -355
  60. package/lib/module/engine/retrieve.js +1 -210
  61. package/lib/module/engine/runAgentTurn.js +8 -1827
  62. package/lib/module/engine/systemPrompt.js +54 -157
  63. package/lib/module/engine/textToolCalls.js +1 -270
  64. package/lib/module/engine/tokenCalibration.js +1 -75
  65. package/lib/module/engine/verify.js +4 -289
  66. package/lib/module/index.js +1 -35
  67. package/lib/module/policy/labels.js +1 -312
  68. package/lib/module/policy/policy.js +1 -142
  69. package/lib/module/policy/redact.js +1 -165
  70. package/lib/module/providers/anthropic.js +3 -441
  71. package/lib/module/providers/openai.js +4 -320
  72. package/lib/module/providers/problem.js +1 -92
  73. package/lib/module/providers/sse.js +1 -231
  74. package/lib/module/providers/streamTimer.js +1 -118
  75. package/lib/module/providers/transport.js +1 -119
  76. package/lib/module/providers/types.js +1 -4
  77. package/lib/module/providers/xhrStream.js +1 -207
  78. package/lib/module/realNow.js +1 -0
  79. package/lib/module/session.js +1 -369
  80. package/lib/module/types.js +0 -1
  81. package/lib/typescript/catalog/catalog.g.d.ts +3 -3
  82. package/lib/typescript/catalog/catalog.types.g.d.ts +3 -2
  83. package/lib/typescript/providers/problem.d.ts +0 -20
  84. package/lib/typescript/providers/streamTimer.d.ts +0 -24
  85. package/lib/typescript/realNow.d.ts +18 -0
  86. package/lib/web/index.mjs +52 -53
  87. package/package.json +1 -1
  88. package/lib/commonjs/blocks/receipts.js.map +0 -1
  89. package/lib/commonjs/blocks/runId.js.map +0 -1
  90. package/lib/commonjs/blocks/types.js.map +0 -1
  91. package/lib/commonjs/blocks/uiTool.js.map +0 -1
  92. package/lib/commonjs/catalog/catalog.g.js.map +0 -1
  93. package/lib/commonjs/catalog/catalog.types.g.js.map +0 -1
  94. package/lib/commonjs/catalog/normalizeParams.js.map +0 -1
  95. package/lib/commonjs/catalog/signature.js.map +0 -1
  96. package/lib/commonjs/catalog/snapshotReads.js.map +0 -1
  97. package/lib/commonjs/catalog/toProviderTools.js.map +0 -1
  98. package/lib/commonjs/catalog/validateParams.js.map +0 -1
  99. package/lib/commonjs/context/buildContextPack.js.map +0 -1
  100. package/lib/commonjs/effects/digest.js.map +0 -1
  101. package/lib/commonjs/effects/ledger.js.map +0 -1
  102. package/lib/commonjs/engine/askGate.js.map +0 -1
  103. package/lib/commonjs/engine/effectFor.js.map +0 -1
  104. package/lib/commonjs/engine/evidence.js.map +0 -1
  105. package/lib/commonjs/engine/historyBudget.js.map +0 -1
  106. package/lib/commonjs/engine/retrieve.js.map +0 -1
  107. package/lib/commonjs/engine/runAgentTurn.js.map +0 -1
  108. package/lib/commonjs/engine/systemPrompt.js.map +0 -1
  109. package/lib/commonjs/engine/textToolCalls.js.map +0 -1
  110. package/lib/commonjs/engine/tokenCalibration.js.map +0 -1
  111. package/lib/commonjs/engine/verify.js.map +0 -1
  112. package/lib/commonjs/index.js.map +0 -1
  113. package/lib/commonjs/policy/labels.js.map +0 -1
  114. package/lib/commonjs/policy/policy.js.map +0 -1
  115. package/lib/commonjs/policy/redact.js.map +0 -1
  116. package/lib/commonjs/providers/anthropic.js.map +0 -1
  117. package/lib/commonjs/providers/openai.js.map +0 -1
  118. package/lib/commonjs/providers/problem.js.map +0 -1
  119. package/lib/commonjs/providers/sse.js.map +0 -1
  120. package/lib/commonjs/providers/streamTimer.js.map +0 -1
  121. package/lib/commonjs/providers/transport.js.map +0 -1
  122. package/lib/commonjs/providers/types.js.map +0 -1
  123. package/lib/commonjs/providers/xhrStream.js.map +0 -1
  124. package/lib/commonjs/session.js.map +0 -1
  125. package/lib/commonjs/types.js.map +0 -1
  126. package/lib/module/blocks/receipts.js.map +0 -1
  127. package/lib/module/blocks/runId.js.map +0 -1
  128. package/lib/module/blocks/types.js.map +0 -1
  129. package/lib/module/blocks/uiTool.js.map +0 -1
  130. package/lib/module/catalog/catalog.g.js.map +0 -1
  131. package/lib/module/catalog/catalog.types.g.js.map +0 -1
  132. package/lib/module/catalog/normalizeParams.js.map +0 -1
  133. package/lib/module/catalog/signature.js.map +0 -1
  134. package/lib/module/catalog/snapshotReads.js.map +0 -1
  135. package/lib/module/catalog/toProviderTools.js.map +0 -1
  136. package/lib/module/catalog/validateParams.js.map +0 -1
  137. package/lib/module/context/buildContextPack.js.map +0 -1
  138. package/lib/module/effects/digest.js.map +0 -1
  139. package/lib/module/effects/ledger.js.map +0 -1
  140. package/lib/module/engine/askGate.js.map +0 -1
  141. package/lib/module/engine/effectFor.js.map +0 -1
  142. package/lib/module/engine/evidence.js.map +0 -1
  143. package/lib/module/engine/historyBudget.js.map +0 -1
  144. package/lib/module/engine/retrieve.js.map +0 -1
  145. package/lib/module/engine/runAgentTurn.js.map +0 -1
  146. package/lib/module/engine/systemPrompt.js.map +0 -1
  147. package/lib/module/engine/textToolCalls.js.map +0 -1
  148. package/lib/module/engine/tokenCalibration.js.map +0 -1
  149. package/lib/module/engine/verify.js.map +0 -1
  150. package/lib/module/index.js.map +0 -1
  151. package/lib/module/policy/labels.js.map +0 -1
  152. package/lib/module/policy/policy.js.map +0 -1
  153. package/lib/module/policy/redact.js.map +0 -1
  154. package/lib/module/providers/anthropic.js.map +0 -1
  155. package/lib/module/providers/openai.js.map +0 -1
  156. package/lib/module/providers/problem.js.map +0 -1
  157. package/lib/module/providers/sse.js.map +0 -1
  158. package/lib/module/providers/streamTimer.js.map +0 -1
  159. package/lib/module/providers/transport.js.map +0 -1
  160. package/lib/module/providers/types.js.map +0 -1
  161. package/lib/module/providers/xhrStream.js.map +0 -1
  162. package/lib/module/session.js.map +0 -1
  163. package/lib/module/types.js.map +0 -1
  164. package/lib/typescript/blocks/receipts.d.ts.map +0 -1
  165. package/lib/typescript/blocks/runId.d.ts.map +0 -1
  166. package/lib/typescript/blocks/types.d.ts.map +0 -1
  167. package/lib/typescript/blocks/uiTool.d.ts.map +0 -1
  168. package/lib/typescript/catalog/catalog.g.d.ts.map +0 -1
  169. package/lib/typescript/catalog/catalog.types.g.d.ts.map +0 -1
  170. package/lib/typescript/catalog/normalizeParams.d.ts.map +0 -1
  171. package/lib/typescript/catalog/signature.d.ts.map +0 -1
  172. package/lib/typescript/catalog/snapshotReads.d.ts.map +0 -1
  173. package/lib/typescript/catalog/toProviderTools.d.ts.map +0 -1
  174. package/lib/typescript/catalog/validateParams.d.ts.map +0 -1
  175. package/lib/typescript/context/buildContextPack.d.ts.map +0 -1
  176. package/lib/typescript/effects/digest.d.ts.map +0 -1
  177. package/lib/typescript/effects/ledger.d.ts.map +0 -1
  178. package/lib/typescript/engine/askGate.d.ts.map +0 -1
  179. package/lib/typescript/engine/effectFor.d.ts.map +0 -1
  180. package/lib/typescript/engine/evidence.d.ts.map +0 -1
  181. package/lib/typescript/engine/historyBudget.d.ts.map +0 -1
  182. package/lib/typescript/engine/retrieve.d.ts.map +0 -1
  183. package/lib/typescript/engine/runAgentTurn.d.ts.map +0 -1
  184. package/lib/typescript/engine/systemPrompt.d.ts.map +0 -1
  185. package/lib/typescript/engine/textToolCalls.d.ts.map +0 -1
  186. package/lib/typescript/engine/tokenCalibration.d.ts.map +0 -1
  187. package/lib/typescript/engine/verify.d.ts.map +0 -1
  188. package/lib/typescript/index.d.ts.map +0 -1
  189. package/lib/typescript/policy/labels.d.ts.map +0 -1
  190. package/lib/typescript/policy/policy.d.ts.map +0 -1
  191. package/lib/typescript/policy/redact.d.ts.map +0 -1
  192. package/lib/typescript/providers/anthropic.d.ts.map +0 -1
  193. package/lib/typescript/providers/openai.d.ts.map +0 -1
  194. package/lib/typescript/providers/problem.d.ts.map +0 -1
  195. package/lib/typescript/providers/sse.d.ts.map +0 -1
  196. package/lib/typescript/providers/streamTimer.d.ts.map +0 -1
  197. package/lib/typescript/providers/transport.d.ts.map +0 -1
  198. package/lib/typescript/providers/types.d.ts.map +0 -1
  199. package/lib/typescript/providers/xhrStream.d.ts.map +0 -1
  200. package/lib/typescript/session.d.ts.map +0 -1
  201. package/lib/typescript/types.d.ts.map +0 -1
@@ -1,489 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.buildContextDigest = buildContextDigest;
7
- exports.buildLiveContext = buildLiveContext;
8
- var _snapshotReads = require("../catalog/snapshotReads");
9
- /**
10
- * The grounding digest — "here is what this app looks like".
11
- *
12
- * Composed entirely from readers that already exist. Most Buoy adapters ship a
13
- * compact action written explicitly for a remote driver (`listQueries`,
14
- * `listStores`, `listAtoms`, `getState`, `async.getAllKeys`), and those are
15
- * exactly the right size: measured on a live idle device the whole digest is
16
- * ~5.7KB (~1.5K tokens) covering routes, env, query keys, redux slices,
17
- * zustand stores, jotai atoms, storage keys and API hosts.
18
- *
19
- * TWO HALVES, READ ON TWO SCHEDULES.
20
- *
21
- * The STABLE half (routes, slices, stores, atoms, storage keys, env names) is
22
- * read once when the chat opens and rendered inside the cached system block.
23
- * It changes rarely, and when it does the cache re-warms once.
24
- *
25
- * The LIVE half — the current route, which queries are MOUNTED right now, the
26
- * last few requests — is read again before EVERY turn and rendered as a
27
- * separate, uncached block. This exists because of one failure: asked to
28
- * "rename the Pokémon I'm looking at", the model went to an empty zustand cart
29
- * because the once-at-open digest had been built before any query was cached,
30
- * so React Query never appeared in its prompt at all. A query with
31
- * `observers > 0` IS what the screen is rendering; that fact has to be in
32
- * front of the model at the moment it decides which tool to reach for.
33
- *
34
- * What this does NOT do, and why:
35
- * - No `getSnapshot('query')`. It returns the full dehydrated cache — 8.3KB for
36
- * ten queries in that same measurement, growing with every cached payload.
37
- * `listQueries` covers the same ground in 1.8KB.
38
- * - No console. It is the fattest payload in the system at ~1.1KB per entry
39
- * with up to 1000 retained; a naive "include recent logs" is a 1MB prompt.
40
- * - No values, only shapes and keys. `includeValues`/`includeData` are
41
- * unbounded on the action path — no adapter applies a size guard — and the
42
- * whole point of the digest is to tell the model where to look, not to
43
- * pre-fetch everything it might want.
44
- */
45
-
46
- /** Per-probe budget. A slow tool must not hold up the first message. */
47
- const PROBE_TIMEOUT_MS = 2500;
48
- const list = value => Array.isArray(value) ? value : Array.isArray(value?.items) ? value.items : [];
49
- const rec = value => value && typeof value === "object" ? value : {};
50
- const names = (value, keys, cap = 40) => list(value).slice(0, cap).map(item => {
51
- if (!item || typeof item !== "object") return item;
52
- const rec = item;
53
- for (const k of keys) {
54
- if (rec[k] !== undefined) return rec[k];
55
- }
56
- return rec.id ?? rec.name ?? rec.key;
57
- });
58
-
59
- /**
60
- * Budgets for the store sketches above. These ride in EVERY prompt, so they are
61
- * caps first and features second: a wide store must cost a bounded number of
62
- * characters, not however many lists it happens to hold.
63
- */
64
- const DIGEST_STORE_CAP = 30;
65
- const DIGEST_STORE_KEYS = 24;
66
- const DIGEST_LISTS_PER_STORE = 8;
67
- const DIGEST_ITEM_CHARS = 240;
68
- /**
69
- * TOTAL characters of item sketches across ALL stores — the cap that actually
70
- * bounds the prompt. The per-store limits above bound one store; thirty stores
71
- * each spending their full allowance is 60,000 characters of digest, in a
72
- * block that rides in every turn. Per-item and per-store budgets are not an
73
- * output budget, so this is the one that has to hold.
74
- *
75
- * Measured: the example app's single bag store costs ~490 tokens (+2% of the
76
- * rendered prompt). This allows roughly six of those before it starts cutting.
77
- */
78
- const DIGEST_SHAPES_TOTAL_CHARS = 12_000;
79
-
80
- /** Truncate visibly — a silently cut type sketch reads as a complete one. */
81
- const clampText = (s, max) => s.length <= max ? s : `${s.slice(0, max - 1)}…`;
82
-
83
- /**
84
- * The top-level `name: type` pairs of a `shapeOf` sketch, split on commas that
85
- * are not inside a nested `{...}` or `[...]`.
86
- *
87
- * Only used to drop the store's ACTION FUNCTIONS from `keys`. A zustand store
88
- * keeps its actions alongside its state, so `Object.keys()` on the Poké Mart
89
- * bag returns ten of `addLine`, `setQty`, `removeLine`, `clearLines`… for six
90
- * real fields. Those are not state, cannot be written, and cannot be sent over
91
- * the wire — putting them in front of the model as things the store "has" is
92
- * an invitation to try. The catalog documents `keys` as plain `Object.keys()`,
93
- * so the adapter's contract stays as it is and the filtering happens here.
94
- */
95
- function topLevelFields(sketch) {
96
- const out = new Map();
97
- if (typeof sketch !== "string" || !sketch.startsWith("{")) return out;
98
- let depth = 0;
99
- let part = "";
100
- const take = () => {
101
- const i = part.indexOf(":");
102
- if (i > 0) out.set(part.slice(0, i).trim(), part.slice(i + 1).trim());
103
- part = "";
104
- };
105
- for (const ch of sketch.slice(1, -1)) {
106
- if (ch === "{" || ch === "[" || ch === "(") depth++;else if (ch === "}" || ch === "]" || ch === ")") depth--;
107
- if (ch === "," && depth === 0) take();else part += ch;
108
- }
109
- take();
110
- return out;
111
- }
112
- const STABLE_PROBES = [{
113
- // `getSitemap` was never an action — the sitemap lives in the adapter's
114
- // snapshot, so this probe could only ever have been filtered out or
115
- // thrown. Same for env below.
116
- key: "routes",
117
- toolId: "route-events",
118
- action: _snapshotReads.SNAPSHOT_ACTION,
119
- // projectSnapshot already flattened the nested route tree to paths.
120
- shape: v => list(v?.routes).slice(0, 60)
121
- }, {
122
- key: "reduxSlices",
123
- toolId: "redux",
124
- action: "getState",
125
- params: {
126
- includeValues: false
127
- },
128
- shape: v => v && typeof v === "object" ? Object.keys(v.state ?? v).slice(0, 40) : v
129
- }, {
130
- // Carries `persists to <key>` for persisted stores. That mapping is the
131
- // only way the model can tell that an innocent-looking storage key is
132
- // actually a hydrated store's saved copy — write the key directly and the
133
- // screen does not change, then the store overwrites you. `listStores`
134
- // already reports `persistName`; nothing else surfaces it.
135
- //
136
- // IT ALSO CARRIES THE ITEM SHAPE OF EVERY LIST IN THE STORE, and for a
137
- // long time it did not. `listStores` returns `keys` and `shape`
138
- // (zustandSyncAdapter.ts) — computed by shared/sync/valueShape.ts, which
139
- // exists FOR this — and this probe threw both away, so the only bag-line
140
- // shape anywhere in the prompt was a six-field one appearing incidentally
141
- // inside a React Query cache key, while the store's line has nine. Models
142
- // copied the shape they were shown and invented names for the rest
143
- // (`price` for `unitPrice`, `imageUrl` for `image`). Both a weak and a
144
- // strong model did it identically, which is what makes it ours and not
145
- // theirs.
146
- //
147
- // `unknownLists` is the honest half. An EMPTY list has no observable item
148
- // shape — not unread, unknowable — and saying so is what stops a guess
149
- // going in unremarked, because the typed-edit guard builds its template
150
- // from `list[0]` and has nothing to check against either.
151
- key: "zustandStores",
152
- toolId: "zustand",
153
- action: "listStores",
154
- // Structured, not a display string: `session` reads `persistsTo` back out
155
- // to build the owner map the engine uses at dispatch time, and the digest
156
- // is JSON-stringified into the prompt anyway, so the model loses nothing.
157
- shape: v => {
158
- // Spent across every store, not per store — see DIGEST_SHAPES_TOTAL_CHARS.
159
- let spent = 0;
160
- return list(v?.stores ?? v).slice(0, DIGEST_STORE_CAP).map(raw => {
161
- const s = raw ?? {};
162
- const name = typeof s.name === "string" ? s.name : s.id;
163
- const lists = list(s.shape?.lists).map(rec);
164
- const known = {};
165
- const unknown = [];
166
- for (const l of lists.slice(0, DIGEST_LISTS_PER_STORE)) {
167
- const path = typeof l.path === "string" ? l.path : undefined;
168
- if (!path) continue;
169
- // One level of item fields is what a write needs; `lines[].addonIds`
170
- // is already covered by its parent's item sketch.
171
- if (path.includes("[].")) continue;
172
- if (typeof l.item !== "string") {
173
- // An empty list is cheap to report and the most important thing
174
- // here, so it is never what gets cut.
175
- unknown.push(path);
176
- continue;
177
- }
178
- const item = clampText(l.item, DIGEST_ITEM_CHARS);
179
- if (spent + item.length > DIGEST_SHAPES_TOTAL_CHARS) continue;
180
- spent += item.length;
181
- known[path] = item;
182
- }
183
- return {
184
- name,
185
- ...(typeof s.persistName === "string" ? {
186
- persistsTo: s.persistName
187
- } : {}),
188
- ...(() => {
189
- if (!Array.isArray(s.keys) || !s.keys.length) return {};
190
- const fields = topLevelFields(s.shape?.shape);
191
- const stateKeys = s.keys.filter(k => fields.get(String(k)) !== "function");
192
- return stateKeys.length ? {
193
- keys: stateKeys.slice(0, DIGEST_STORE_KEYS)
194
- } : {};
195
- })(),
196
- ...(Object.keys(known).length ? {
197
- listItemShapes: known
198
- } : {}),
199
- ...(unknown.length ? {
200
- unknownLists: unknown
201
- } : {})
202
- };
203
- });
204
- }
205
- }, {
206
- key: "jotaiAtoms",
207
- toolId: "jotai",
208
- action: "listAtoms",
209
- shape: v => names(v, ["debugLabel", "label", "id"], 40)
210
- }, {
211
- key: "storageKeys",
212
- toolId: "storage",
213
- action: "async.getAllKeys",
214
- shape: v => list(v).slice(0, 60)
215
- }, {
216
- key: "env",
217
- toolId: "env",
218
- action: _snapshotReads.SNAPSHOT_ACTION,
219
- // Names only. Env values are baked into the bundle and can hold anything
220
- // the app author put in an EXPO_PUBLIC_ var, so the digest lists the keys
221
- // and leaves reading a value to an explicit ask.
222
- shape: v => {
223
- const env = v?.env;
224
- return env && typeof env === "object" ? Object.keys(env).slice(0, 40) : undefined;
225
- }
226
- }];
227
-
228
- /** Most-recent queries carried per turn. Mounted ones are sorted first. */
229
- const LIVE_QUERY_CAP = 25;
230
- /**
231
- * How many queries to ASK the adapter for, before we cap to LIVE_QUERY_CAP.
232
- *
233
- * The adapter sorts by `dataUpdatedAt` and slices to `limit`
234
- * (reactQuerySyncAdapter.ts) — so asking it for 25 and sorting mounted-first
235
- * afterwards sorts a list the screen's own query may already have fallen out
236
- * of. A detail screen whose query settled a minute ago sits behind every
237
- * background refetch since; on an app with more than 25 live queries it was
238
- * simply absent from the prompt, and `likelyScreen` then had nothing to mark.
239
- * Our demo app never has enough queries to reach the cap, which is why this
240
- * survived: it is a bug that only appears on apps bigger than the example.
241
- *
242
- * The real fix is in the adapter, which now sorts mounted-first before it
243
- * applies `limit`. This modest over-fetch is defence in depth for the readers
244
- * that do NOT — an older device on a published version, or a desktop mirror
245
- * replaying its own payload — where prioritising here is the only chance left.
246
- * 2x, not more: this rides in every turn and an unbounded scan is how a cheap
247
- * call stops being cheap.
248
- */
249
- const LIVE_QUERY_SCAN = LIVE_QUERY_CAP * 2;
250
- /** Recent requests carried per turn — enough to find the call that fed the screen. */
251
- const LIVE_REQUEST_CAP = 8;
252
- const LIVE_PROBES = [{
253
- // `{path, params}` — the params matter as much as the path: "mewtwo" in
254
- // `params.id` is what ties `/pokemon/[id]` to the `["pokemon-detail",
255
- // "mewtwo", ...]` query below.
256
- key: "currentRoute",
257
- toolId: "route-events",
258
- action: "getCurrentRoute",
259
- shape: v => {
260
- const r = rec(v);
261
- if (typeof r.path !== "string") return undefined;
262
- return r.params && Object.keys(rec(r.params)).length ? {
263
- path: r.path,
264
- params: r.params
265
- } : {
266
- path: r.path
267
- };
268
- }
269
- }, {
270
- // `observers > 0` is the one field that says "a component on screen is
271
- // rendering this right now". Everything else about a query is secondary
272
- // to that when the question is "what am I looking at".
273
- key: "queries",
274
- toolId: "query",
275
- action: "listQueries",
276
- params: {
277
- limit: LIVE_QUERY_SCAN
278
- },
279
- shape: v => {
280
- const rows = list(v?.queries ?? v).map(rec);
281
- const shaped = rows.map(q => {
282
- const observers = typeof q.observers === "number" ? q.observers : 0;
283
- return {
284
- key: q.queryKey ?? q.queryHash,
285
- status: q.status,
286
- ...(observers > 0 ? {
287
- onScreen: true,
288
- observers
289
- } : {})
290
- };
291
- });
292
- // PRIORITISE, THEN CAP — in that order. The adapter has already dropped
293
- // everything past LIVE_QUERY_SCAN by recency; this keeps the mounted
294
- // ones out of the next cut, which is the whole point of the sort.
295
- shaped.sort((a, b) => Number(Boolean(b.onScreen)) - Number(Boolean(a.onScreen)));
296
- return shaped.slice(0, LIVE_QUERY_CAP);
297
- }
298
- }, {
299
- // Whether anything is being recorded AT ALL. Network only captures while
300
- // something holds a subscription, so the request list below can be empty
301
- // or stale for a reason that has nothing to do with the app. Put in front
302
- // of the model on the first turn, it was costing several turns to
303
- // discover ("The network recorder wasn't running when you asked…").
304
- key: "networkCapture",
305
- toolId: "network",
306
- action: "getCaptureStatus",
307
- shape: v => {
308
- const s = rec(v);
309
- if (typeof s.capturing !== "boolean") return undefined;
310
- return {
311
- recording: s.capturing,
312
- ...(s.interceptorLive === false ? {
313
- interceptorLive: false
314
- } : {}),
315
- captured: typeof s.eventCount === "number" ? s.eventCount : undefined
316
- };
317
- }
318
- }, {
319
- // Newest FIRST, as the adapter stores them (the projection keeps that
320
- // order; it once took the oldest N by mistake). Carries the request `id` so a
321
- // network override seeded from the real response is one call away
322
- // (`upsertOverrideRule({ fromRequestId })`), and `overridden` so the model
323
- // never presents mocked data as live.
324
- key: "recentRequests",
325
- toolId: "network",
326
- action: _snapshotReads.SNAPSHOT_ACTION,
327
- params: {
328
- limit: LIVE_REQUEST_CAP
329
- },
330
- shape: v => list(v?.requests).map(raw => {
331
- const e = rec(raw);
332
- return {
333
- id: e.id,
334
- method: e.method,
335
- url: e.url,
336
- status: e.status,
337
- ...(e.overridden ? {
338
- overridden: true
339
- } : {})
340
- };
341
- })
342
- }, {
343
- // An armed override rule explains a response that disagrees with the
344
- // server. Without this the model would tell the user the API is broken.
345
- key: "networkOverrides",
346
- toolId: "network",
347
- action: "listOverrideRules",
348
- shape: v => {
349
- const state = rec(v);
350
- const rules = list(state.rules).map(rec).filter(r => r.enabled !== false).slice(0, 10).map(r => ({
351
- id: r.id,
352
- urlPattern: r.urlPattern,
353
- kind: r.kind,
354
- status: r.status
355
- }));
356
- if (!rules.length) return undefined;
357
- return {
358
- enabled: state.enabled === true,
359
- rules
360
- };
361
- }
362
- }];
363
- async function withTimeout(p, ms) {
364
- return await Promise.race([p.catch(() => undefined), new Promise(resolve => setTimeout(() => resolve(undefined), ms))]);
365
- }
366
- async function runProbes(probes, {
367
- dispatch,
368
- readSnapshot,
369
- availableToolIds,
370
- availableActions
371
- }) {
372
- const runnable = probes.filter(p => {
373
- if (!availableToolIds.includes(p.toolId)) return false;
374
- const actions = availableActions?.[p.toolId];
375
- return !actions || actions.includes(p.action);
376
- });
377
-
378
- // All at once — these are independent reads and the first message should not
379
- // wait on the slowest one twice over.
380
- const settled = await Promise.all(runnable.map(async probe => {
381
- const read = probe.action === _snapshotReads.SNAPSHOT_ACTION ? readSnapshot ? Promise.resolve(readSnapshot(probe.toolId)).then(payload => (0, _snapshotReads.projectSnapshot)(probe.toolId, payload, probe.params ?? {})) : Promise.resolve(undefined) : Promise.resolve(dispatch(probe.toolId, probe.action, probe.params));
382
- const value = await withTimeout(read, PROBE_TIMEOUT_MS);
383
- if (value === undefined) return [probe.key, undefined];
384
- try {
385
- return [probe.key, probe.shape(value)];
386
- } catch {
387
- return [probe.key, undefined];
388
- }
389
- }));
390
- const digest = {};
391
- for (const [key, value] of settled) {
392
- const empty = value === undefined || value === null || Array.isArray(value) && value.length === 0;
393
- if (!empty) digest[key] = value;
394
- }
395
- return digest;
396
- }
397
-
398
- /** The stable half: read once at open, rendered inside the cached prompt. */
399
- function buildContextDigest(input) {
400
- return runProbes(STABLE_PROBES, input);
401
- }
402
-
403
- /** Lowercased string tokens from a query key array (or a hash string). */
404
- function keyTokens(key) {
405
- const parts = Array.isArray(key) ? key : typeof key === "string" ? (() => {
406
- try {
407
- const p = JSON.parse(key);
408
- return Array.isArray(p) ? p : [key];
409
- } catch {
410
- return [key];
411
- }
412
- })() : [key];
413
- return parts.flatMap(p => p === null || p === undefined ? [] : splitTokens(String(typeof p === "object" ? JSON.stringify(p) : p)));
414
- }
415
-
416
- /**
417
- * The one tokeniser. Keys and route params MUST be split identically — that
418
- * they were not is exactly how the scoring came to work only on bare words.
419
- */
420
- function splitTokens(value) {
421
- return value.toLowerCase().split(/[^a-z0-9]+/i).filter(Boolean);
422
- }
423
-
424
- /**
425
- * Tie the mounted queries to the FOCUSED route.
426
- *
427
- * `observers > 0` means "mounted somewhere in the nav stack", NOT "on the
428
- * visible screen" — a stack navigator keeps the screen beneath the current one
429
- * mounted, so its query still reports an observer. That is the exact bug behind
430
- * "it edited a query that isn't the screen I'm on": on /pokemon/charmander both
431
- * the pokemon-detail query and a still-mounted /shop/item/charmander query look
432
- * on-screen. So score each on-screen query against the focused route's param
433
- * VALUES (weighted) and path segments, and mark the single clear winner
434
- * `likelyScreen: true`. A tie marks none — the prompt then tells the model to
435
- * reason from currentRoute rather than guess.
436
- */
437
- function annotateLikelyScreen(live) {
438
- const route = live.currentRoute;
439
- const queries = live.queries;
440
- if (!route?.path || !Array.isArray(queries)) return;
441
-
442
- // Tokenised the SAME way the keys are. They were not, and `tokens.has(value)`
443
- // could therefore never match a param containing anything but letters and
444
- // digits — every UUID, slug, date, email and composite id silently scored
445
- // zero, and only the demo app's bare `mewtwo` / `25` ever matched. A param
446
- // counts as present when every one of its tokens is in the key, so
447
- // "double-cheese-burger" matches ["menu-item","double-cheese-burger"] and
448
- // does not match ["menu","burger"].
449
- const paramTokens = Object.values(route.params ?? {}).map(v => splitTokens(String(v))).filter(t => t.length > 0);
450
- const pathSegs = splitTokens(route.path);
451
- let best = -1;
452
- let bestIdx = -1;
453
- let tie = false;
454
- queries.forEach((q, i) => {
455
- if (!q.onScreen) return;
456
- const tokens = new Set(keyTokens(q.key));
457
- // A param value present in the key is the strong signal (this is THIS id);
458
- // a shared path segment breaks ties between two screens showing that id.
459
- const paramHits = paramTokens.filter(parts => parts.every(t => tokens.has(t))).length;
460
- const pathHits = pathSegs.filter(s => tokens.has(s) || [...tokens].some(t => t.includes(s) || s.includes(t))).length;
461
- const score = paramHits * 3 + pathHits;
462
- if (score <= 0) return;
463
- if (score > best) {
464
- best = score;
465
- bestIdx = i;
466
- tie = false;
467
- } else if (score === best) {
468
- tie = true;
469
- }
470
- });
471
- if (bestIdx >= 0 && !tie) {
472
- queries[bestIdx].likelyScreen = true;
473
- // float it to the very top so a capped list always shows it
474
- const [winner] = queries.splice(bestIdx, 1);
475
- queries.unshift(winner);
476
- }
477
- }
478
-
479
- /**
480
- * The live half: read before every turn. Current route, mounted queries,
481
- * recent requests, armed overrides. Rendered as the uncached "RIGHT NOW"
482
- * block — see `buildLiveBlock` in systemPrompt.ts.
483
- */
484
- async function buildLiveContext(input) {
485
- const live = await runProbes(LIVE_PROBES, input);
486
- annotateLikelyScreen(live);
487
- return live;
488
- }
489
- //# sourceMappingURL=buildContextPack.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.buildContextDigest=buildContextDigest;exports.buildLiveContext=buildLiveContext;var _snapshotReads=require("../catalog/snapshotReads");var _realNow=require("../realNow");const PROBE_TIMEOUT_MS=2500;const list=e=>Array.isArray(e)?e:Array.isArray(e?.items)?e.items:[];const rec=e=>e&&typeof e==="object"?e:{};const names=(e,t,s=40)=>list(e).slice(0,s).map(n=>{if(!n||typeof n!=="object")return n;const a=n;for(const o of t){if(a[o]!==void 0)return a[o]}return a.id??a.name??a.key});const DIGEST_STORE_CAP=30;const DIGEST_STORE_KEYS=24;const DIGEST_LISTS_PER_STORE=8;const DIGEST_ITEM_CHARS=240;const DIGEST_SHAPES_TOTAL_CHARS=12e3;const clampText=(e,t)=>e.length<=t?e:`${e.slice(0,t-1)}\u2026`;function topLevelFields(e){const t=new Map;if(typeof e!=="string"||!e.startsWith("{"))return t;let s=0;let n="";const a=()=>{const o=n.indexOf(":");if(o>0)t.set(n.slice(0,o).trim(),n.slice(o+1).trim());n=""};for(const o of e.slice(1,-1)){if(o==="{"||o==="["||o==="(")s++;else if(o==="}"||o==="]"||o===")")s--;if(o===","&&s===0)a();else n+=o}a();return t}const STABLE_PROBES=[{key:"routes",toolId:"route-events",action:_snapshotReads.SNAPSHOT_ACTION,shape:e=>list(e?.routes).slice(0,60)},{key:"reduxSlices",toolId:"redux",action:"getState",params:{includeValues:false},shape:e=>e&&typeof e==="object"?Object.keys(e.state??e).slice(0,40):e},{key:"zustandStores",toolId:"zustand",action:"listStores",shape:e=>{let t=0;return list(e?.stores??e).slice(0,DIGEST_STORE_CAP).map(s=>{const n=s??{};const a=typeof n.name==="string"?n.name:n.id;const o=list(n.shape?.lists).map(rec);const u={};const l=[];for(const r of o.slice(0,DIGEST_LISTS_PER_STORE)){const i=typeof r.path==="string"?r.path:void 0;if(!i)continue;if(i.includes("[]."))continue;if(typeof r.item!=="string"){l.push(i);continue}const c=clampText(r.item,DIGEST_ITEM_CHARS);if(t+c.length>DIGEST_SHAPES_TOTAL_CHARS)continue;t+=c.length;u[i]=c}return{name:a,...typeof n.persistName==="string"?{persistsTo:n.persistName}:{},...(()=>{if(!Array.isArray(n.keys)||!n.keys.length)return{};const r=topLevelFields(n.shape?.shape);const i=n.keys.filter(c=>r.get(String(c))!=="function");return i.length?{keys:i.slice(0,DIGEST_STORE_KEYS)}:{}})(),...Object.keys(u).length?{listItemShapes:u}:{},...l.length?{unknownLists:l}:{}}})}},{key:"jotaiAtoms",toolId:"jotai",action:"listAtoms",shape:e=>names(e,["debugLabel","label","id"],40)},{key:"storageKeys",toolId:"storage",action:"async.getAllKeys",shape:e=>list(e).slice(0,60)},{key:"env",toolId:"env",action:_snapshotReads.SNAPSHOT_ACTION,shape:e=>{const t=e?.env;return t&&typeof t==="object"?Object.keys(t).slice(0,40):void 0}}];const LIVE_QUERY_CAP=25;const LIVE_QUERY_SCAN=LIVE_QUERY_CAP*2;const LIVE_REQUEST_CAP=8;const LIVE_PROBES=[{key:"currentRoute",toolId:"route-events",action:"getCurrentRoute",shape:e=>{const t=rec(e);if(typeof t.path!=="string")return void 0;return t.params&&Object.keys(rec(t.params)).length?{path:t.path,params:t.params}:{path:t.path}}},{key:"queries",toolId:"query",action:"listQueries",params:{limit:LIVE_QUERY_SCAN},shape:e=>{const t=list(e?.queries??e).map(rec);const s=t.map(n=>{const a=typeof n.observers==="number"?n.observers:0;return{key:n.queryKey??n.queryHash,status:n.status,...a>0?{onScreen:true,observers:a}:{}}});s.sort((n,a)=>Number(Boolean(a.onScreen))-Number(Boolean(n.onScreen)));return s.slice(0,LIVE_QUERY_CAP)}},{key:"networkCapture",toolId:"network",action:"getCaptureStatus",shape:e=>{const t=rec(e);if(typeof t.capturing!=="boolean")return void 0;return{recording:t.capturing,...t.interceptorLive===false?{interceptorLive:false}:{},captured:typeof t.eventCount==="number"?t.eventCount:void 0}}},{key:"recentRequests",toolId:"network",action:_snapshotReads.SNAPSHOT_ACTION,params:{limit:LIVE_REQUEST_CAP},shape:e=>list(e?.requests).map(t=>{const s=rec(t);return{id:s.id,method:s.method,url:s.url,status:s.status,...s.overridden?{overridden:true}:{}}})},{key:"appClock",toolId:"clock",action:"getState",shape:e=>{const t=rec(e);if(t.active!==true||typeof t.virtualIso!=="string")return void 0;return{appTime:t.virtualIso,mode:t.mode,...typeof t.rate==="number"&&t.rate!==1?{rate:t.rate}:{},offsetMs:t.offsetMs}}},{key:"networkOverrides",toolId:"network",action:"listOverrideRules",shape:e=>{const t=rec(e);const s=list(t.rules).map(rec).filter(n=>n.enabled!==false).slice(0,10).map(n=>({id:n.id,urlPattern:n.urlPattern,kind:n.kind,status:n.status}));if(!s.length)return void 0;return{enabled:t.enabled===true,rules:s}}}];async function withTimeout(e,t){return await Promise.race([e.catch(()=>void 0),new Promise(s=>(0,_realNow.realSetTimeout)(()=>s(void 0),t))])}async function runProbes(e,{dispatch:t,readSnapshot:s,availableToolIds:n,availableActions:a}){const o=e.filter(r=>{if(!n.includes(r.toolId))return false;const i=a?.[r.toolId];return!i||i.includes(r.action)});const u=await Promise.all(o.map(async r=>{const i=r.action===_snapshotReads.SNAPSHOT_ACTION?s?Promise.resolve(s(r.toolId)).then(y=>(0,_snapshotReads.projectSnapshot)(r.toolId,y,r.params??{})):Promise.resolve(void 0):Promise.resolve(t(r.toolId,r.action,r.params));const c=await withTimeout(i,PROBE_TIMEOUT_MS);if(c===void 0)return[r.key,void 0];try{return[r.key,r.shape(c)]}catch{return[r.key,void 0]}}));const l={};for(const[r,i]of u){const c=i===void 0||i===null||Array.isArray(i)&&i.length===0;if(!c)l[r]=i}return l}function buildContextDigest(e){return runProbes(STABLE_PROBES,e)}function keyTokens(e){const t=Array.isArray(e)?e:typeof e==="string"?(()=>{try{const s=JSON.parse(e);return Array.isArray(s)?s:[e]}catch{return[e]}})():[e];return t.flatMap(s=>s===null||s===void 0?[]:splitTokens(String(typeof s==="object"?JSON.stringify(s):s)))}function splitTokens(e){return e.toLowerCase().split(/[^a-z0-9]+/i).filter(Boolean)}function annotateLikelyScreen(e){const t=e.currentRoute;const s=e.queries;if(!t?.path||!Array.isArray(s))return;const n=Object.values(t.params??{}).map(r=>splitTokens(String(r))).filter(r=>r.length>0);const a=splitTokens(t.path);let o=-1;let u=-1;let l=false;s.forEach((r,i)=>{if(!r.onScreen)return;const c=new Set(keyTokens(r.key));const y=n.filter(d=>d.every(p=>c.has(p))).length;const h=a.filter(d=>c.has(d)||[...c].some(p=>p.includes(d)||d.includes(p))).length;const f=y*3+h;if(f<=0)return;if(f>o){o=f;u=i;l=false}else if(f===o){l=true}});if(u>=0&&!l){s[u].likelyScreen=true;const[r]=s.splice(u,1);s.unshift(r)}}async function buildLiveContext(e){const t=await runProbes(LIVE_PROBES,e);annotateLikelyScreen(t);return t}
@@ -1,34 +1 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.digestOf = digestOf;
7
- exports.jsonLength = jsonLength;
8
- /**
9
- * A short, stable fingerprint of any JSON-able value. Not cryptographic —
10
- * it only has to answer "is this the same as what I saw?" — and deliberately
11
- * lossy so the thing persisted beside an unanswered approval or a ledger
12
- * entry is a hash of a store's state, never the state itself.
13
- */
14
- function digestOf(value) {
15
- let json;
16
- try {
17
- json = JSON.stringify(value) ?? "";
18
- } catch {
19
- return undefined;
20
- }
21
- let h = 5381;
22
- for (let i = 0; i < json.length; i++) h = (h << 5) + h + json.charCodeAt(i) | 0;
23
- return `${(h >>> 0).toString(16)}:${json.length}`;
24
- }
25
-
26
- /** Byte-ish size of a value once serialised; Infinity when it cannot be. */
27
- function jsonLength(value) {
28
- try {
29
- return JSON.stringify(value)?.length ?? 0;
30
- } catch {
31
- return Number.POSITIVE_INFINITY;
32
- }
33
- }
34
- //# sourceMappingURL=digest.js.map
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.digestOf=digestOf;exports.jsonLength=jsonLength;function digestOf(r){let t;try{t=JSON.stringify(r)??""}catch{return void 0}let e=5381;for(let n=0;n<t.length;n++)e=(e<<5)+e+t.charCodeAt(n)|0;return`${(e>>>0).toString(16)}:${t.length}`}function jsonLength(r){try{return JSON.stringify(r)?.length??0}catch{return Number.POSITIVE_INFINITY}}