@buoy-gg/agent-core 7.0.33

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 (138) hide show
  1. package/LICENSE +58 -0
  2. package/README.md +42 -0
  3. package/lib/commonjs/blocks/receipts.js +702 -0
  4. package/lib/commonjs/blocks/receipts.js.map +1 -0
  5. package/lib/commonjs/blocks/types.js +119 -0
  6. package/lib/commonjs/blocks/types.js.map +1 -0
  7. package/lib/commonjs/blocks/uiTool.js +994 -0
  8. package/lib/commonjs/blocks/uiTool.js.map +1 -0
  9. package/lib/commonjs/catalog/catalog.g.js +4044 -0
  10. package/lib/commonjs/catalog/catalog.g.js.map +1 -0
  11. package/lib/commonjs/catalog/catalog.source.json +4956 -0
  12. package/lib/commonjs/catalog/snapshotReads.js +256 -0
  13. package/lib/commonjs/catalog/snapshotReads.js.map +1 -0
  14. package/lib/commonjs/catalog/toProviderTools.js +107 -0
  15. package/lib/commonjs/catalog/toProviderTools.js.map +1 -0
  16. package/lib/commonjs/catalog/validateParams.js +129 -0
  17. package/lib/commonjs/catalog/validateParams.js.map +1 -0
  18. package/lib/commonjs/context/buildContextPack.js +141 -0
  19. package/lib/commonjs/context/buildContextPack.js.map +1 -0
  20. package/lib/commonjs/effects/ledger.js +229 -0
  21. package/lib/commonjs/effects/ledger.js.map +1 -0
  22. package/lib/commonjs/engine/effectFor.js +95 -0
  23. package/lib/commonjs/engine/effectFor.js.map +1 -0
  24. package/lib/commonjs/engine/runAgentTurn.js +667 -0
  25. package/lib/commonjs/engine/runAgentTurn.js.map +1 -0
  26. package/lib/commonjs/engine/systemPrompt.js +86 -0
  27. package/lib/commonjs/engine/systemPrompt.js.map +1 -0
  28. package/lib/commonjs/index.js +288 -0
  29. package/lib/commonjs/index.js.map +1 -0
  30. package/lib/commonjs/package.json +1 -0
  31. package/lib/commonjs/policy/labels.js +303 -0
  32. package/lib/commonjs/policy/labels.js.map +1 -0
  33. package/lib/commonjs/policy/policy.js +143 -0
  34. package/lib/commonjs/policy/policy.js.map +1 -0
  35. package/lib/commonjs/policy/redact.js +135 -0
  36. package/lib/commonjs/policy/redact.js.map +1 -0
  37. package/lib/commonjs/providers/anthropic.js +272 -0
  38. package/lib/commonjs/providers/anthropic.js.map +1 -0
  39. package/lib/commonjs/providers/openai.js +193 -0
  40. package/lib/commonjs/providers/openai.js.map +1 -0
  41. package/lib/commonjs/providers/sse.js +104 -0
  42. package/lib/commonjs/providers/sse.js.map +1 -0
  43. package/lib/commonjs/providers/types.js +6 -0
  44. package/lib/commonjs/providers/types.js.map +1 -0
  45. package/lib/commonjs/session.js +173 -0
  46. package/lib/commonjs/session.js.map +1 -0
  47. package/lib/commonjs/types.js +2 -0
  48. package/lib/commonjs/types.js.map +1 -0
  49. package/lib/module/blocks/receipts.js +696 -0
  50. package/lib/module/blocks/receipts.js.map +1 -0
  51. package/lib/module/blocks/types.js +113 -0
  52. package/lib/module/blocks/types.js.map +1 -0
  53. package/lib/module/blocks/uiTool.js +987 -0
  54. package/lib/module/blocks/uiTool.js.map +1 -0
  55. package/lib/module/catalog/catalog.g.js +4040 -0
  56. package/lib/module/catalog/catalog.g.js.map +1 -0
  57. package/lib/module/catalog/catalog.source.json +4956 -0
  58. package/lib/module/catalog/snapshotReads.js +252 -0
  59. package/lib/module/catalog/snapshotReads.js.map +1 -0
  60. package/lib/module/catalog/toProviderTools.js +101 -0
  61. package/lib/module/catalog/toProviderTools.js.map +1 -0
  62. package/lib/module/catalog/validateParams.js +124 -0
  63. package/lib/module/catalog/validateParams.js.map +1 -0
  64. package/lib/module/context/buildContextPack.js +138 -0
  65. package/lib/module/context/buildContextPack.js.map +1 -0
  66. package/lib/module/effects/ledger.js +223 -0
  67. package/lib/module/effects/ledger.js.map +1 -0
  68. package/lib/module/engine/effectFor.js +91 -0
  69. package/lib/module/engine/effectFor.js.map +1 -0
  70. package/lib/module/engine/runAgentTurn.js +663 -0
  71. package/lib/module/engine/runAgentTurn.js.map +1 -0
  72. package/lib/module/engine/systemPrompt.js +82 -0
  73. package/lib/module/engine/systemPrompt.js.map +1 -0
  74. package/lib/module/index.js +30 -0
  75. package/lib/module/index.js.map +1 -0
  76. package/lib/module/policy/labels.js +296 -0
  77. package/lib/module/policy/labels.js.map +1 -0
  78. package/lib/module/policy/policy.js +137 -0
  79. package/lib/module/policy/policy.js.map +1 -0
  80. package/lib/module/policy/redact.js +128 -0
  81. package/lib/module/policy/redact.js.map +1 -0
  82. package/lib/module/providers/anthropic.js +268 -0
  83. package/lib/module/providers/anthropic.js.map +1 -0
  84. package/lib/module/providers/openai.js +189 -0
  85. package/lib/module/providers/openai.js.map +1 -0
  86. package/lib/module/providers/sse.js +99 -0
  87. package/lib/module/providers/sse.js.map +1 -0
  88. package/lib/module/providers/types.js +4 -0
  89. package/lib/module/providers/types.js.map +1 -0
  90. package/lib/module/session.js +168 -0
  91. package/lib/module/session.js.map +1 -0
  92. package/lib/module/types.js +2 -0
  93. package/lib/module/types.js.map +1 -0
  94. package/lib/typescript/blocks/receipts.d.ts +32 -0
  95. package/lib/typescript/blocks/receipts.d.ts.map +1 -0
  96. package/lib/typescript/blocks/types.d.ts +576 -0
  97. package/lib/typescript/blocks/types.d.ts.map +1 -0
  98. package/lib/typescript/blocks/uiTool.d.ts +15 -0
  99. package/lib/typescript/blocks/uiTool.d.ts.map +1 -0
  100. package/lib/typescript/catalog/catalog.g.d.ts +11 -0
  101. package/lib/typescript/catalog/catalog.g.d.ts.map +1 -0
  102. package/lib/typescript/catalog/snapshotReads.d.ts +37 -0
  103. package/lib/typescript/catalog/snapshotReads.d.ts.map +1 -0
  104. package/lib/typescript/catalog/toProviderTools.d.ts +52 -0
  105. package/lib/typescript/catalog/toProviderTools.d.ts.map +1 -0
  106. package/lib/typescript/catalog/validateParams.d.ts +33 -0
  107. package/lib/typescript/catalog/validateParams.d.ts.map +1 -0
  108. package/lib/typescript/context/buildContextPack.d.ts +32 -0
  109. package/lib/typescript/context/buildContextPack.d.ts.map +1 -0
  110. package/lib/typescript/effects/ledger.d.ts +119 -0
  111. package/lib/typescript/effects/ledger.d.ts.map +1 -0
  112. package/lib/typescript/engine/effectFor.d.ts +21 -0
  113. package/lib/typescript/engine/effectFor.d.ts.map +1 -0
  114. package/lib/typescript/engine/runAgentTurn.d.ts +140 -0
  115. package/lib/typescript/engine/runAgentTurn.d.ts.map +1 -0
  116. package/lib/typescript/engine/systemPrompt.d.ts +49 -0
  117. package/lib/typescript/engine/systemPrompt.d.ts.map +1 -0
  118. package/lib/typescript/index.d.ts +29 -0
  119. package/lib/typescript/index.d.ts.map +1 -0
  120. package/lib/typescript/policy/labels.d.ts +10 -0
  121. package/lib/typescript/policy/labels.d.ts.map +1 -0
  122. package/lib/typescript/policy/policy.d.ts +106 -0
  123. package/lib/typescript/policy/policy.d.ts.map +1 -0
  124. package/lib/typescript/policy/redact.d.ts +37 -0
  125. package/lib/typescript/policy/redact.d.ts.map +1 -0
  126. package/lib/typescript/providers/anthropic.d.ts +22 -0
  127. package/lib/typescript/providers/anthropic.d.ts.map +1 -0
  128. package/lib/typescript/providers/openai.d.ts +19 -0
  129. package/lib/typescript/providers/openai.d.ts.map +1 -0
  130. package/lib/typescript/providers/sse.d.ts +18 -0
  131. package/lib/typescript/providers/sse.d.ts.map +1 -0
  132. package/lib/typescript/providers/types.d.ts +129 -0
  133. package/lib/typescript/providers/types.d.ts.map +1 -0
  134. package/lib/typescript/session.d.ts +58 -0
  135. package/lib/typescript/session.d.ts.map +1 -0
  136. package/lib/typescript/types.d.ts +119 -0
  137. package/lib/typescript/types.d.ts.map +1 -0
  138. package/package.json +58 -0
@@ -0,0 +1,696 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Receipts — blocks the engine builds from tool results, deterministically.
5
+ *
6
+ * The model never authors these, so they cannot hallucinate, and a weak model
7
+ * cannot fail to produce them. They render the SAME redacted result the model
8
+ * gets, so what the user sees and what the model reasons about are one thing.
9
+ *
10
+ * Every projector is small and honest about caps: it truncates and records
11
+ * `total` so the renderer can say "+38 more" truthfully.
12
+ */
13
+ import { CAPS } from "./types";
14
+ let seq = 0;
15
+ const id = () => `rc${++seq}`;
16
+ const rec = v => v && typeof v === "object" && !Array.isArray(v) ? v : undefined;
17
+ const arr = v => Array.isArray(v) ? v : [];
18
+ const str = v => typeof v === "string" ? v : undefined;
19
+ const num = v => typeof v === "number" ? v : undefined;
20
+ function pathOf(url) {
21
+ try {
22
+ const u = new URL(url);
23
+ return u.pathname + (u.search.length > 1 ? "?…" : "");
24
+ } catch {
25
+ return url;
26
+ }
27
+ }
28
+ function requestSummary(r) {
29
+ const rid = str(r.id);
30
+ const url = str(r.url);
31
+ if (!rid || !url) return undefined;
32
+ return {
33
+ id: rid,
34
+ method: str(r.method) ?? "GET",
35
+ url,
36
+ status: num(r.status),
37
+ durationMs: num(r.durationMs),
38
+ // Both spellings: the snapshot projection says `overridden`, a raw event
39
+ // carries the `override` mark object.
40
+ mocked: r.overridden === true || r.override != null && typeof r.override === "object" || undefined
41
+ };
42
+ }
43
+
44
+ /** Shallow diff of two plain objects: top-level keys that differ. */
45
+ export function shallowDiff(before, after) {
46
+ const a = rec(before) ?? {};
47
+ const b = rec(after) ?? {};
48
+ const keys = new Set([...Object.keys(a), ...Object.keys(b)]);
49
+ const out = [];
50
+ for (const k of keys) {
51
+ if (typeof a[k] === "function" || typeof b[k] === "function") continue;
52
+ if (JSON.stringify(a[k]) !== JSON.stringify(b[k])) {
53
+ out.push({
54
+ path: k,
55
+ before: a[k],
56
+ after: b[k]
57
+ });
58
+ }
59
+ }
60
+ return out;
61
+ }
62
+
63
+ /** Group console entries by message, newest first, counting repeats. */
64
+ function groupLogs(entries) {
65
+ const map = new Map();
66
+ for (const e of entries) {
67
+ const r = rec(e);
68
+ if (!r) continue;
69
+ const message = str(r.message) ?? "";
70
+ const level = str(r.level) ?? "log";
71
+ const key = `${level}:${message}`;
72
+ const cur = map.get(key);
73
+ if (cur) cur.count += 1;else map.set(key, {
74
+ level,
75
+ message,
76
+ count: 1
77
+ });
78
+ }
79
+ return [...map.values()];
80
+ }
81
+
82
+ /** A bag-line / product heuristic: an array of objects with a name and a price/qty/image. */
83
+ function looksLikeItems(v) {
84
+ const items = arr(v).map(rec).filter(Boolean);
85
+ if (items.length < 2) return undefined;
86
+ const ok = items.every(i => str(i.name) && (i.image !== undefined || i.qty !== undefined || i.price !== undefined || i.unitPrice !== undefined));
87
+ return ok ? items : undefined;
88
+ }
89
+ function money(cents) {
90
+ const n = num(cents);
91
+ return n === undefined ? undefined : `$${(n / 100).toFixed(2)}`;
92
+ }
93
+ /**
94
+ * The receipt for one successful call, or nothing. Adding a projector is the
95
+ * whole job of making a tool's result visible — no UI change needed.
96
+ */
97
+ export function projectReceipt(input) {
98
+ const {
99
+ toolId,
100
+ action,
101
+ params,
102
+ result
103
+ } = input;
104
+ const r = rec(result);
105
+ const source = input.effect === "read" ? "live" : "changed";
106
+
107
+ // ---- network --------------------------------------------------------
108
+ if (toolId === "network" && action === "getSnapshot" && r) {
109
+ const requests = arr(r.requests).map(rec).filter(Boolean).map(x => requestSummary(x)).filter(Boolean);
110
+ if (requests.length === 0) return undefined;
111
+ return {
112
+ id: id(),
113
+ kind: "requestList",
114
+ requests: requests.slice(0, CAPS.listItems * 2),
115
+ total: num(r.totalCaptured) ?? requests.length,
116
+ source
117
+ };
118
+ }
119
+ if (toolId === "network" && action === "getEventBody" && r) {
120
+ const reqId = str(params.id) ?? "";
121
+ const wasMocked = r.override != null && typeof r.override === "object";
122
+ return {
123
+ id: id(),
124
+ kind: "requestCard",
125
+ request: {
126
+ id: reqId,
127
+ method: str(r.method) ?? "",
128
+ url: str(r.url) ?? reqId,
129
+ status: num(r.status),
130
+ mocked: wasMocked || undefined
131
+ },
132
+ requestBody: r.requestData ?? undefined,
133
+ responseBody: r.responseData ?? undefined,
134
+ // A body an override rule synthesized must never render as "live".
135
+ source: wasMocked ? "mocked" : source
136
+ };
137
+ }
138
+ if (toolId === "network" && (action === "upsertOverrideRule" || action === "getOverrideRuleBody") && r) {
139
+ const rule = rec(r.rule) ?? r;
140
+ const ruleId = str(rule.id);
141
+ const pattern = str(rule.urlPattern);
142
+ if (!ruleId || !pattern) return undefined;
143
+ const kind = str(rule.kind) ?? "respond";
144
+ const asked = rec(params.rule) ?? {};
145
+ return {
146
+ id: id(),
147
+ kind: "ruleCard",
148
+ rule: {
149
+ id: ruleId,
150
+ name: str(rule.name) ?? str(asked.name),
151
+ urlPattern: pattern,
152
+ methods: arr(rule.methods ?? asked.methods).map(String),
153
+ kind,
154
+ status: num(rule.status) ?? num(asked.status),
155
+ delayMs: num(rule.delayMs) ?? num(asked.delayMs),
156
+ times: num(rule.times) ?? num(asked.times),
157
+ enabled: rule.enabled !== false,
158
+ hits: num(rule.hits)
159
+ },
160
+ source: action === "upsertOverrideRule" ? "changed" : "live"
161
+ };
162
+ }
163
+ if (toolId === "network" && action === "listOverrideRules" && r) {
164
+ const rules = arr(r.rules).map(rec).filter(Boolean);
165
+ if (rules.length === 0) return undefined;
166
+ return {
167
+ id: id(),
168
+ kind: "list",
169
+ title: `${rules.length} network override${rules.length === 1 ? "" : "s"}`,
170
+ items: rules.slice(0, CAPS.listItems).map(x => ({
171
+ id: str(x.id),
172
+ title: str(x.name) ?? str(x.urlPattern) ?? "rule",
173
+ subtitle: `${arr(x.methods).join("/") || "ANY"} ${str(x.urlPattern) ?? ""} → ${str(x.kind) ?? ""}${x.status ? ` ${String(x.status)}` : ""}`,
174
+ trailing: x.enabled === false ? "off" : `on · ${num(x.hits) ?? 0} hit${num(x.hits) === 1 ? "" : "s"}`,
175
+ tone: x.enabled === false ? "muted" : "warning"
176
+ })),
177
+ total: rules.length,
178
+ source: "live"
179
+ };
180
+ }
181
+
182
+ // ---- routes ---------------------------------------------------------
183
+ if (toolId === "route-events" && action === "getSnapshot" && r) {
184
+ const routes = [...new Set(arr(r.routes).map(String))];
185
+ if (routes.length === 0) return undefined;
186
+ const current = rec(r.currentRoute);
187
+ return {
188
+ id: id(),
189
+ kind: "routeChips",
190
+ routes: routes.map(p => ({
191
+ path: p,
192
+ dynamic: /\[.+\]/.test(p)
193
+ })),
194
+ current: str(current?.path),
195
+ total: routes.length,
196
+ source: "live"
197
+ };
198
+ }
199
+ if (toolId === "route-events" && action === "navigate" && r) {
200
+ const path = str(r.navigated) ?? str(params.path);
201
+ return path ? {
202
+ id: id(),
203
+ kind: "peek",
204
+ label: `Opened ${path}`,
205
+ path
206
+ } : undefined;
207
+ }
208
+ if (toolId === "route-events" && (action === "stackGoBack" || action === "stackPopToTop")) {
209
+ return {
210
+ id: id(),
211
+ kind: "peek",
212
+ label: action === "stackPopToTop" ? "Went back to the first screen" : "Went back"
213
+ };
214
+ }
215
+
216
+ // ---- screen ---------------------------------------------------------
217
+ if (toolId === "highlight-updates" && action === "describeScreen" && r) {
218
+ const els = arr(r.elements).map(rec).filter(Boolean);
219
+ const interactive = els.filter(e => e.interactive === true && !String(str(e.testID) ?? "").startsWith("buoy-"));
220
+ if (interactive.length === 0) return undefined;
221
+ return {
222
+ id: id(),
223
+ kind: "screenElements",
224
+ elements: interactive.slice(0, CAPS.chips).map(e => ({
225
+ nativeTag: num(e.nativeTag) ?? 0,
226
+ name: str(e.name) ?? "",
227
+ label: str(e.label) ?? str(e.text),
228
+ testID: str(e.testID),
229
+ interactive: true
230
+ })),
231
+ total: interactive.length,
232
+ source: "live"
233
+ };
234
+ }
235
+ if (toolId === "highlight-updates" && action === "tapElement" && r?.tapped === true) {
236
+ const m = rec(r.matched);
237
+ const what = str(m?.label) ?? str(m?.text) ?? str(m?.testID) ?? "element";
238
+ return {
239
+ id: id(),
240
+ kind: "peek",
241
+ label: `Tapped ${what}`
242
+ };
243
+ }
244
+
245
+ // ---- console --------------------------------------------------------
246
+ if (toolId === "console" && action === "getSnapshot" && r) {
247
+ const entries = arr(r.entries);
248
+ if (entries.length === 0) return undefined;
249
+ const grouped = groupLogs(entries);
250
+ return {
251
+ id: id(),
252
+ kind: "logList",
253
+ entries: grouped.slice(0, CAPS.listItems * 2),
254
+ total: num(r.totalCaptured) ?? entries.length,
255
+ source: "live"
256
+ };
257
+ }
258
+
259
+ // ---- state stores ---------------------------------------------------
260
+ if (toolId === "zustand" && action === "getStoreState" && r?.found !== false) {
261
+ const state = rec(r?.currentState);
262
+ if (!state) return undefined;
263
+ const items = looksLikeItems(state.lines ?? state.items);
264
+ if (items) {
265
+ return {
266
+ id: id(),
267
+ kind: "list",
268
+ title: str(params.storeName),
269
+ items: items.slice(0, CAPS.listItems).map(i => ({
270
+ id: str(i.lineId) ?? str(i.id),
271
+ title: str(i.name) ?? "",
272
+ subtitle: [str(i.sizeName), i.qty !== undefined ? `×${String(i.qty)}` : undefined].filter(Boolean).join(" · "),
273
+ trailing: money(i.unitPrice ?? i.price),
274
+ image: str(i.image)
275
+ })),
276
+ total: items.length,
277
+ source: "live"
278
+ };
279
+ }
280
+ const pairs = Object.entries(state).filter(([, v]) => typeof v !== "function").slice(0, CAPS.keyValuePairs).map(([key, value]) => ({
281
+ key,
282
+ value
283
+ }));
284
+ return {
285
+ id: id(),
286
+ kind: "keyValue",
287
+ title: str(params.storeName),
288
+ pairs,
289
+ source: "live"
290
+ };
291
+ }
292
+ if (toolId === "zustand" && action === "setState" && r?.ok !== false) {
293
+ const changes = shallowDiff(rec(input.before)?.currentState ?? input.before, params.state);
294
+ if (changes.length === 0) return undefined;
295
+ return {
296
+ id: id(),
297
+ kind: "diff",
298
+ title: `${str(params.storeName) ?? "store"} — what changed`,
299
+ changes,
300
+ source: "changed"
301
+ };
302
+ }
303
+ if (toolId === "zustand" && action === "listStores" && r) {
304
+ const stores = arr(r.stores).map(rec).filter(Boolean);
305
+ if (stores.length === 0) return undefined;
306
+ return {
307
+ id: id(),
308
+ kind: "list",
309
+ title: `${stores.length} store${stores.length === 1 ? "" : "s"}`,
310
+ items: stores.slice(0, CAPS.listItems * 2).map(s => ({
311
+ id: str(s.name),
312
+ title: str(s.name) ?? "",
313
+ subtitle: arr(s.keys).slice(0, 6).join(", "),
314
+ trailing: s.isPersisted ? "persisted" : undefined
315
+ })),
316
+ total: stores.length,
317
+ source: "live"
318
+ };
319
+ }
320
+ if (toolId === "redux" && action === "getState" && r?.available !== false) {
321
+ const state = rec(r?.state) ?? r;
322
+ if (!state) return undefined;
323
+ const pairs = Object.entries(state).slice(0, CAPS.keyValuePairs).map(([key, value]) => ({
324
+ key,
325
+ value
326
+ }));
327
+ return {
328
+ id: id(),
329
+ kind: "keyValue",
330
+ title: "Redux state",
331
+ pairs,
332
+ source: "live"
333
+ };
334
+ }
335
+
336
+ // ---- storage --------------------------------------------------------
337
+ if (toolId === "storage" && (action === "async.getItem" || action === "mmkv.get" || action === "secure.get")) {
338
+ const key = str(params.key) ?? "";
339
+ let value = r && "value" in r ? r.value : result;
340
+ if (r?.found === false || value === null || value === undefined) return undefined;
341
+ if (typeof value === "string") {
342
+ try {
343
+ value = JSON.parse(value);
344
+ } catch {
345
+ /* plain string */
346
+ }
347
+ }
348
+ const obj = rec(value);
349
+ const items = looksLikeItems(rec(obj?.state)?.lines ?? obj?.lines ?? obj?.items);
350
+ if (items) {
351
+ return {
352
+ id: id(),
353
+ kind: "list",
354
+ title: key,
355
+ items: items.slice(0, CAPS.listItems).map(i => ({
356
+ id: str(i.lineId) ?? str(i.id),
357
+ title: str(i.name) ?? "",
358
+ subtitle: [str(i.sizeName), i.qty !== undefined ? `×${String(i.qty)}` : undefined].filter(Boolean).join(" · "),
359
+ trailing: money(i.unitPrice ?? i.price),
360
+ image: str(i.image)
361
+ })),
362
+ total: items.length,
363
+ source: "live"
364
+ };
365
+ }
366
+ if (obj) {
367
+ const pairs = Object.entries(obj).slice(0, CAPS.keyValuePairs).map(([k, v]) => ({
368
+ key: k,
369
+ value: v
370
+ }));
371
+ return {
372
+ id: id(),
373
+ kind: "keyValue",
374
+ title: key,
375
+ pairs,
376
+ source: "live"
377
+ };
378
+ }
379
+ return {
380
+ id: id(),
381
+ kind: "keyValue",
382
+ title: key,
383
+ pairs: [{
384
+ key: "value",
385
+ value
386
+ }],
387
+ source: "live"
388
+ };
389
+ }
390
+ if (toolId === "storage" && action === "async.getAllKeys") {
391
+ const keys = arr(result).map(String);
392
+ if (keys.length === 0) return undefined;
393
+ return {
394
+ id: id(),
395
+ kind: "list",
396
+ title: `${keys.length} stored key${keys.length === 1 ? "" : "s"}`,
397
+ items: keys.slice(0, CAPS.listItems * 2).map(k => ({
398
+ id: k,
399
+ title: k,
400
+ action: {
401
+ label: "Read",
402
+ dispatch: {
403
+ toolId: "storage",
404
+ action: "async.getItem",
405
+ params: {
406
+ key: k
407
+ }
408
+ }
409
+ }
410
+ })),
411
+ total: keys.length,
412
+ source: "live"
413
+ };
414
+ }
415
+ if (toolId === "storage" && (action === "async.setItem" || action === "mmkv.set" || action === "secure.set")) {
416
+ const key = str(params.key) ?? "";
417
+ const b = rec(input.before);
418
+ const beforeVal = b?.had ? b.value : undefined;
419
+ return {
420
+ id: id(),
421
+ kind: "diff",
422
+ title: `${key} — saved`,
423
+ changes: [{
424
+ path: key,
425
+ before: beforeVal,
426
+ after: params.value
427
+ }],
428
+ source: "changed"
429
+ };
430
+ }
431
+
432
+ // ---- env ------------------------------------------------------------
433
+ if (toolId === "env" && action === "getSnapshot" && r) {
434
+ const env = rec(r.env);
435
+ if (!env || Object.keys(env).length === 0) return undefined;
436
+ const pairs = Object.entries(env).slice(0, CAPS.keyValuePairs).map(([key, value]) => ({
437
+ key,
438
+ value
439
+ }));
440
+ return {
441
+ id: id(),
442
+ kind: "keyValue",
443
+ title: `Environment — ${Object.keys(env).length} var${Object.keys(env).length === 1 ? "" : "s"}`,
444
+ pairs,
445
+ source: "live"
446
+ };
447
+ }
448
+
449
+ // ---- sentry ---------------------------------------------------------
450
+ if (toolId === "sentry" && action === "getSnapshot" && r) {
451
+ const envelopes = arr(r.envelopes).map(rec).filter(Boolean);
452
+ const status = str(r.status);
453
+ if (envelopes.length === 0) {
454
+ // The status IS the answer when nothing was captured: "no client" and
455
+ // "nothing errored" are different findings and must not look alike.
456
+ if (status === "sdk-not-found" || status === "no-client") {
457
+ return {
458
+ id: id(),
459
+ kind: "notice",
460
+ tone: "warning",
461
+ text: "This app has no live Sentry client, so nothing can ever be captured here — that's different from \"nothing was sent\"."
462
+ };
463
+ }
464
+ return undefined;
465
+ }
466
+ return {
467
+ id: id(),
468
+ kind: "list",
469
+ title: `${num(r.totalCaptured) ?? envelopes.length} envelope${(num(r.totalCaptured) ?? envelopes.length) === 1 ? "" : "s"} sent to Sentry`,
470
+ items: envelopes.slice(0, CAPS.listItems * 2).map(e => {
471
+ const items = arr(e.items).map(rec).filter(Boolean);
472
+ const first = items[0];
473
+ const isError = items.some(i => i.type === "event");
474
+ return {
475
+ id: e.id !== undefined ? String(e.id) : undefined,
476
+ title: str(first?.summary) ?? str(first?.type) ?? "envelope",
477
+ subtitle: items.map(i => str(i.type)).filter(Boolean).join(" · "),
478
+ trailing: str(e.origin),
479
+ tone: isError ? "danger" : undefined
480
+ };
481
+ }),
482
+ total: num(r.totalCaptured) ?? envelopes.length,
483
+ source: "live"
484
+ };
485
+ }
486
+
487
+ // ---- impersonate ----------------------------------------------------
488
+ if (toolId === "impersonate" && action === "searchUsers") {
489
+ const users = arr(result).map(rec).filter(Boolean);
490
+ if (users.length === 0) return undefined;
491
+ return {
492
+ id: id(),
493
+ kind: "list",
494
+ title: `${users.length} user${users.length === 1 ? "" : "s"} found`,
495
+ items: users.slice(0, CAPS.listItems).map(u => ({
496
+ id: str(u.id),
497
+ title: str(u.displayName) ?? str(u.email) ?? str(u.id) ?? "user",
498
+ subtitle: [str(u.email), str(rec(u.metadata)?.role)].filter(Boolean).join(" · "),
499
+ image: str(u.avatarUrl),
500
+ // The tap goes through runGatedAction, so it carries the same policy,
501
+ // approval and ledger path a model call would.
502
+ action: {
503
+ label: "Impersonate",
504
+ dispatch: {
505
+ toolId: "impersonate",
506
+ action: "startImpersonation",
507
+ params: {
508
+ user: u
509
+ }
510
+ }
511
+ }
512
+ })),
513
+ total: users.length,
514
+ source: "live"
515
+ };
516
+ }
517
+ if (toolId === "impersonate" && action === "startImpersonation" && r?.ok !== false) {
518
+ const u = rec(params.user);
519
+ const who = str(u?.displayName) ?? str(u?.email) ?? str(u?.id) ?? "that user";
520
+ return {
521
+ id: id(),
522
+ kind: "notice",
523
+ tone: "warning",
524
+ text: `Now viewing the app as ${who}. Cached data was cleared so everything refetches as them — stopping clears caches again.`
525
+ };
526
+ }
527
+ if (toolId === "impersonate" && action === "stopImpersonation") {
528
+ return {
529
+ id: id(),
530
+ kind: "notice",
531
+ tone: "info",
532
+ text: "Stopped impersonating — back to the real account. Cached app data was cleared again."
533
+ };
534
+ }
535
+
536
+ // ---- scenarios ------------------------------------------------------
537
+ if (toolId === "scenarios" && action === "listScenarios" && r) {
538
+ const all = [...arr(r.code), ...arr(r.device), ...arr(r.drafts)].map(rec).filter(Boolean);
539
+ if (all.length === 0) return undefined;
540
+ return {
541
+ id: id(),
542
+ kind: "list",
543
+ title: `${all.length} scenario${all.length === 1 ? "" : "s"}`,
544
+ items: all.slice(0, CAPS.listItems * 2).map(s => ({
545
+ id: str(s.id),
546
+ title: str(s.name) ?? str(s.id) ?? "scenario",
547
+ subtitle: str(s.description),
548
+ trailing: str(s.source) === "draft" ? "draft — needs accepting" : num(s.stepCount) !== undefined ? `${String(num(s.stepCount))} step${num(s.stepCount) === 1 ? "" : "s"}` : undefined,
549
+ tone: str(s.source) === "draft" ? "muted" : undefined
550
+ })),
551
+ total: all.length,
552
+ source: "live"
553
+ };
554
+ }
555
+ if (toolId === "scenarios" && action === "run" && r) {
556
+ if (r.ok === false) {
557
+ const errs = arr(r.preflightErrors).map(String).join("; ");
558
+ return {
559
+ id: id(),
560
+ kind: "notice",
561
+ tone: "warning",
562
+ text: `The scenario did not run — pre-flight failed${errs ? `: ${errs}` : ""}. Nothing was applied.`
563
+ };
564
+ }
565
+ const steps = arr(r.steps).map(rec).filter(Boolean);
566
+ if (steps.length === 0) return undefined;
567
+ return {
568
+ id: id(),
569
+ kind: "steps",
570
+ title: `Ran ${str(r.name) ?? "the scenario"}`,
571
+ steps: steps.slice(0, CAPS.listItems * 2).map(s => ({
572
+ text: str(s.label) ?? "step",
573
+ state: s.skipped === true ? "todo" : s.ok === false ? "failed" : "done"
574
+ }))
575
+ };
576
+ }
577
+
578
+ // ---- time machine ---------------------------------------------------
579
+ if (toolId === "time-machine" && action === "list" && r) {
580
+ const snaps = arr(r.snapshots).map(rec).filter(Boolean);
581
+ if (snaps.length === 0) return undefined;
582
+ return {
583
+ id: id(),
584
+ kind: "list",
585
+ title: `${snaps.length} restore point${snaps.length === 1 ? "" : "s"}`,
586
+ items: snaps.slice(0, CAPS.listItems * 2).map(s => ({
587
+ id: str(s.id),
588
+ title: str(s.name) ?? str(s.id) ?? "snapshot",
589
+ subtitle: str(rec(s.route)?.pathname),
590
+ trailing: s.baseline === true ? "baseline" : undefined
591
+ })),
592
+ total: snaps.length,
593
+ source: "live"
594
+ };
595
+ }
596
+ if (toolId === "time-machine" && action === "capture" && r) {
597
+ return {
598
+ id: id(),
599
+ kind: "notice",
600
+ tone: "info",
601
+ text: `Saved a restore point${typeof params.name === "string" ? ` — "${params.name}"` : ""}. The app can be brought back to this exact state any time.`
602
+ };
603
+ }
604
+ if (toolId === "time-machine" && action === "restore" && r) {
605
+ return {
606
+ id: id(),
607
+ kind: "notice",
608
+ tone: "warning",
609
+ text: "Restored the snapshot — the app's state just jumped back. What's on screen now reflects the restore point, not what happened since."
610
+ };
611
+ }
612
+
613
+ // ---- jotai ----------------------------------------------------------
614
+ if (toolId === "jotai" && action === "listAtoms" && r) {
615
+ const atoms = arr(r.atoms).map(rec).filter(Boolean);
616
+ if (atoms.length === 0) return undefined;
617
+ return {
618
+ id: id(),
619
+ kind: "list",
620
+ title: `${num(r.total) ?? atoms.length} atom${(num(r.total) ?? atoms.length) === 1 ? "" : "s"}`,
621
+ items: atoms.slice(0, CAPS.listItems * 2).map(a => ({
622
+ id: str(a.label),
623
+ title: str(a.label) ?? "atom",
624
+ subtitle: a.writable === true ? "writable" : "read-only (derived)",
625
+ trailing: num(a.changes) !== undefined ? `${String(num(a.changes))} change${num(a.changes) === 1 ? "" : "s"}` : undefined
626
+ })),
627
+ total: num(r.total) ?? atoms.length,
628
+ source: "live"
629
+ };
630
+ }
631
+
632
+ // ---- images ---------------------------------------------------------
633
+ if (toolId === "images" && action === "list" && r) {
634
+ const records = arr(r.records).map(rec).filter(Boolean);
635
+ const images = records.map(x => ({
636
+ url: str(x.uri) ?? "",
637
+ alt: str(x.uri)?.split("/").pop() ?? "image",
638
+ caption: x.ms !== undefined ? `${String(x.ms)} ms · ${str(x.cache) ?? ""}` : undefined
639
+ })).filter(i => i.url);
640
+ if (images.length === 0) return undefined;
641
+ return {
642
+ id: id(),
643
+ kind: "imageGrid",
644
+ title: "Images the app has loaded",
645
+ images: images.slice(0, CAPS.gridMax),
646
+ total: num(r.total) ?? images.length,
647
+ source: "live"
648
+ };
649
+ }
650
+
651
+ // ---- react-query ----------------------------------------------------
652
+ if (toolId === "query" && action === "listQueries" && r) {
653
+ const qs = arr(r.queries).map(rec).filter(Boolean);
654
+ if (qs.length === 0) return undefined;
655
+ return {
656
+ id: id(),
657
+ kind: "list",
658
+ title: `${qs.length} cached quer${qs.length === 1 ? "y" : "ies"}`,
659
+ items: qs.slice(0, CAPS.listItems * 2).map(q => {
660
+ const key = arr(q.queryKey).filter(k => typeof k === "string").join(" › ") || str(q.queryHash) || "query";
661
+ const stale = q.isStale === true;
662
+ return {
663
+ id: str(q.queryHash),
664
+ title: key,
665
+ subtitle: `${str(q.status) ?? ""}${q.fetchStatus && q.fetchStatus !== "idle" ? ` · ${String(q.fetchStatus)}` : ""}`,
666
+ trailing: stale ? "stale" : "fresh",
667
+ tone: stale ? "warning" : "accent"
668
+ };
669
+ }),
670
+ total: qs.length,
671
+ source: "live"
672
+ };
673
+ }
674
+
675
+ // ---- render measurement → chart --------------------------------------
676
+ if (toolId === "highlight-updates" && action === "endMeasurement" && r) {
677
+ const comps = arr(r.components ?? r.renders).map(rec).filter(Boolean);
678
+ const bars = comps.map(c => ({
679
+ label: str(c.name) ?? str(c.componentName) ?? "?",
680
+ value: num(c.renders) ?? num(c.count) ?? 0
681
+ })).filter(b => b.value > 0).sort((a, b) => b.value - a.value).slice(0, CAPS.chartBars);
682
+ if (bars.length === 0) return undefined;
683
+ return {
684
+ id: id(),
685
+ kind: "chart",
686
+ title: "Renders during the measurement",
687
+ bars: bars.map(b => ({
688
+ ...b,
689
+ unit: "renders"
690
+ })),
691
+ source: "live"
692
+ };
693
+ }
694
+ return undefined;
695
+ }
696
+ //# sourceMappingURL=receipts.js.map