@buoy-gg/agent-core 7.0.44 → 7.0.45

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 (40) hide show
  1. package/lib/commonjs/catalog/catalog.g.js +2 -2
  2. package/lib/commonjs/catalog/catalog.source.json +52 -9
  3. package/lib/commonjs/catalog/catalog.types.g.js +1 -1
  4. package/lib/commonjs/catalog/normalizeParams.js +2 -2
  5. package/lib/commonjs/catalog/snapshotReads.js +1 -1
  6. package/lib/commonjs/effects/ledger.js +1 -1
  7. package/lib/commonjs/effects/storeDiff.js +1 -0
  8. package/lib/commonjs/engine/compactScreen.js +1 -0
  9. package/lib/commonjs/engine/effectFor.js +1 -1
  10. package/lib/commonjs/engine/planOnlyStop.js +1 -0
  11. package/lib/commonjs/engine/runAgentTurn.js +7 -7
  12. package/lib/commonjs/engine/systemPrompt.js +12 -5
  13. package/lib/commonjs/engine/verify.js +1 -1
  14. package/lib/commonjs/providers/openai.js +3 -5
  15. package/lib/commonjs/providers/types.js +1 -1
  16. package/lib/module/catalog/catalog.g.js +2 -2
  17. package/lib/module/catalog/catalog.source.json +52 -9
  18. package/lib/module/catalog/catalog.types.g.js +1 -1
  19. package/lib/module/catalog/normalizeParams.js +1 -1
  20. package/lib/module/catalog/snapshotReads.js +1 -1
  21. package/lib/module/effects/ledger.js +1 -1
  22. package/lib/module/effects/storeDiff.js +1 -0
  23. package/lib/module/engine/compactScreen.js +1 -0
  24. package/lib/module/engine/effectFor.js +1 -1
  25. package/lib/module/engine/planOnlyStop.js +1 -0
  26. package/lib/module/engine/runAgentTurn.js +7 -7
  27. package/lib/module/engine/systemPrompt.js +12 -5
  28. package/lib/module/engine/verify.js +1 -1
  29. package/lib/module/providers/openai.js +3 -5
  30. package/lib/module/providers/types.js +1 -1
  31. package/lib/typescript/catalog/catalog.g.d.ts +2 -2
  32. package/lib/typescript/catalog/catalog.types.g.d.ts +3 -3
  33. package/lib/typescript/effects/ledger.d.ts +13 -0
  34. package/lib/typescript/effects/storeDiff.d.ts +23 -0
  35. package/lib/typescript/engine/compactScreen.d.ts +18 -0
  36. package/lib/typescript/engine/planOnlyStop.d.ts +22 -0
  37. package/lib/typescript/engine/runAgentTurn.d.ts +1 -1
  38. package/lib/typescript/providers/types.d.ts +8 -0
  39. package/lib/web/index.mjs +44 -39
  40. package/package.json +1 -1
@@ -967,7 +967,7 @@
967
967
  },
968
968
  "effect": "read",
969
969
  "release": "works",
970
- "description": "Projects each live query to light fields: queryHash, queryKey, status (one of fresh/stale/fetching/error/inactive/paused/disabled, from getQueryStatusLabel), fetchStatus, isStale, observers, updatedAt (state.dataUpdatedAt), and error.message when present. Returns {queries, total, returned, includedData}. Sorted most-recently-updated first. Safe to call repeatedly \u2014 unlike the full dehydrated snapshot, the heavy cached data stays on the device by default. TWO TRAPS: (1) the adapter has NO default limit \u2014 omit it and you get every query in the cache; (2) includeData:true returns q.state.data RAW and UNCAPPED (no 16KB wire marker, no 8MB cap like getQueryData), so it can blow the wire/token budget on a big cache \u2014 prefer getQueryData for one query's payload. The queryHash of each row is the handle every other action takes. What is NOT here has not been fetched yet \u2014 the cache fills as the user visits screens \u2014 so treat a missing key as a screen to go to (route-events.navigate, then highlight-updates.waitFor, then read again), not as an absence to report. Every read here also returns `shape`: a one-line sketch of the value's type plus, for each list in it, what its items look like. It is tiny and does not grow with the data, so it survives the 24,000-character cut when the data itself does not \u2014 read it before writing, and match it exactly. A list whose `item` is missing is EMPTY, which means nothing in the running app knows what belongs in it; anything you add there cannot be checked and will be accepted as-is, so say so rather than inventing fields. Send `staleOnly:true` to see only stale queries.",
970
+ "description": "Projects each live query to light fields: queryHash, queryKey, status (one of fresh/stale/fetching/error/inactive/paused/disabled, from getQueryStatusLabel), fetchStatus, isStale, observers, updatedAt (state.dataUpdatedAt), and error.message when present. Returns {queries, total, returned, includedData}. Sorted most-recently-updated first. Safe to call repeatedly \u2014 unlike the full dehydrated snapshot, the heavy cached data stays on the device by default. TWO TRAPS: (1) the adapter has NO default limit \u2014 omit it and you get every query in the cache; (2) includeData:true returns q.state.data RAW and UNCAPPED (no 16KB wire marker, no 8MB cap like getQueryData), so it can blow the wire/token budget on a big cache \u2014 prefer getQueryData for one query's payload. The queryHash of each row is the handle every other action takes. What is NOT here has not been fetched yet \u2014 the cache fills as the user visits screens \u2014 so treat a missing key as a screen to go to (route-events.navigate, then highlight-updates.waitFor, then read again), not as an absence to report. Every read here also returns `shape`: a one-line sketch of the value's type plus, for each list in it, what its items look like. It is tiny and does not grow with the data, so it survives the 24,000-character cut when the data itself does not \u2014 read it before writing, and match it exactly. A list whose `item` is missing is EMPTY, which means nothing in the running app knows what belongs in it; anything you add there cannot be checked and will be accepted as-is, so say so rather than inventing fields. Send `staleOnly:true` to see only stale queries. `refetchEveryMs` appears on a query a mounted screen refetches on a timer: any cache edit, error or loading pin on it is replaced within that many ms, so use a network override for anything that must last \u2014 and it answers \"is the app calling this API over and over?\".",
971
971
  "requires": [
972
972
  "QueryClientProvider above <FloatingDevTools/>",
973
973
  "@tanstack/react-query v5"
@@ -1441,7 +1441,7 @@
1441
1441
  },
1442
1442
  "effect": "read",
1443
1443
  "release": "works",
1444
- "description": "Runs the device's own \"Copy Settings\" formatter over the unified store and returns {output: string, returned: number, totalAvailable: number, includedData: boolean, format: string}. The store is a 200-event ring, newest-first. Defaults are compact: includeEventData=false, so heavy raw payloads (network bodies, redux state trees, query data) stay on the device \u2014 pass settings.includeEventData=true only when you actually need them.\n\nTHREE traps a caller must know:\n(1) COLD READS ARE EMPTY. Sources are only subscribed while a consumer holds a watch on toolId \"events\" (the adapter's subscribe() calls ensureRemoteSourcesDefault) or the on-device Events modal is open. A bare call_action does NOT arm capture \u2014 the MCP SyncClient only watches on getSnapshot, never on callAction (packages/mcp/src/tools/packs/events.ts:80). If totalAvailable is 0, say \"nothing was being recorded\", not \"nothing happened\": arm it first (read the events snapshot, or call setEnabledSources), reproduce, then export.\n(2) limit IS APPLIED BEFORE FILTERING. The adapter does all.slice(0, limit) and only then does generateExport filter by settings.filterSources / filterMode (packages/events/src/sync/eventsSyncAdapter.ts:378-381 + eventExportFormatter.ts:616). Filtering to one source with limit:25 can produce an EMPTY output while `returned` still reports 25 \u2014 `returned` counts events handed to the formatter, not events in the output. When filtering by source, pass a large limit (or omit it, which uses the whole buffer).\n(3) filterSources takes GRANULAR EventSource values, not friendly names. \"storage\" matches nothing \u2014 use [\"storage-async\",\"storage-mmkv\"]; react-query is three values ([\"react-query\",\"react-query-query\",\"react-query-mutation\"]). The MCP get_events wrapper expands those aliases for you; a direct call_action does not.\n\nAn unrecognized preset name silently falls back to DEFAULT_COPY_SETTINGS instead of erroring. settings is shallow-merged OVER the preset. Swift captures network, storage-async, storage-mmkv, and route. React state and render sources are unavailable. Native export accepts format, includeEventData, includeSource, includeStatus, includeTitle, includeSubtitle, includeSummaryHeader, filterMode, filterSources, dataSizeThreshold, and timestampFormat; other settings are rejected. Native detail inherits source snapshot payload limits.",
1444
+ "description": "Runs the device's own \"Copy Settings\" formatter over the unified store and returns {output: string, returned: number, totalAvailable: number, includedData: boolean, format: string}. The store is a 200-event ring, newest-first. Defaults are compact: includeEventData=false, so heavy raw payloads (network bodies, redux state trees, query data) stay on the device \u2014 pass settings.includeEventData=true only when you actually need them.\n\nTHREE traps a caller must know:\n(1) COLD READS ARE EMPTY. Sources are only subscribed while a consumer holds a watch on toolId \"events\" (the adapter's subscribe() calls ensureRemoteSourcesDefault) or the on-device Events modal is open. A bare call_action does NOT arm capture \u2014 the MCP SyncClient only watches on getSnapshot, never on callAction (packages/mcp/src/tools/packs/events.ts:80). If totalAvailable is 0, say \"nothing was being recorded\", not \"nothing happened\": arm it first (read the events snapshot, or call setEnabledSources), reproduce, then export.\n(2) limit IS APPLIED BEFORE FILTERING. The adapter does all.slice(0, limit) and only then does generateExport filter by settings.filterSources / filterMode (packages/events/src/sync/eventsSyncAdapter.ts:378-381 + eventExportFormatter.ts:616). Filtering to one source with limit:25 can produce an EMPTY output while `returned` still reports 25 \u2014 `returned` counts events handed to the formatter, not events in the output. When filtering by source, pass a large limit (or omit it, which uses the whole buffer).\n(3) filterSources takes GRANULAR EventSource values, not friendly names. \"storage\" matches nothing \u2014 use [\"storage-async\",\"storage-mmkv\"]; react-query is three values ([\"react-query\",\"react-query-query\",\"react-query-mutation\"]). The MCP get_events wrapper expands those aliases for you; a direct call_action does not.\n\nAn unrecognized preset name silently falls back to DEFAULT_COPY_SETTINGS instead of erroring. settings is shallow-merged OVER the preset. Swift captures network, storage-async, storage-mmkv, and route. React state and render sources are unavailable. Native export accepts format, includeEventData, includeSource, includeStatus, includeTitle, includeSubtitle, includeSummaryHeader, filterMode, filterSources, dataSizeThreshold, and timestampFormat; other settings are rejected. Native detail inherits source snapshot payload limits. A source records only while it is enabled (setEnabledSources), so enabling one now cannot show what happened before. For storage writes, read storage getSnapshot instead \u2014 the storage tool keeps its own timeline.",
1445
1445
  "requires": [
1446
1446
  "@buoy-gg/events installed and auto-discovered by FloatingDevTools",
1447
1447
  "a live watch on toolId \"events\" (an events snapshot read, a desktop Events panel, or a prior setEnabledSources call) before the window you want recorded \u2014 otherwise the buffer is empty",
@@ -1843,7 +1843,7 @@
1843
1843
  },
1844
1844
  "effect": "write",
1845
1845
  "release": "noop",
1846
- "description": "Create (or replace by id) a rule that forces matching requests to return a chosen status/body, fail outright, or arrive late. Pick this over query.setQueryData when the change must survive a refetch or reload, or when the ask is about what the API returns; follow it with query.invalidate so the mounted screen refetches through the rule. To change ONE value of the real response send fromRequestId plus `bodyPath` and `bodyValue` (bodyPath:\"sprites.front_default\", or \"stats[stat.name=hp].base_stat\" for one row of a list) \u2014 there is no nesting to get wrong, which matters most here because you read that body through a wire that truncates at 24,000 characters. For several fields use `bodyPatch` with ONLY those fields; never paste a whole captured body back. For \"just the NEXT call\" / \"once, then let it work again\", send rule.times:1 \u2014 the rule auto-disables after one match. Without times it overrides EVERY matching request until deleted. Never fake one-shot by triggering the request yourself and deleting the rule: that spends the failure the user wanted to see.",
1846
+ "description": "Create (or replace by id) a rule that forces matching requests to return a chosen status/body, fail outright, or arrive late. Pick this over query.setQueryData when the change must survive a refetch or reload, or when the ask is about what the API returns; follow it with query.invalidate so the mounted screen refetches through the rule. To change ONE value of the real response send fromRequestId plus `bodyPath` and `bodyValue` (bodyPath:\"sprites.front_default\", or \"stats[stat.name=hp].base_stat\" for one row of a list) \u2014 there is no nesting to get wrong, which matters most here because you read that body through a wire that truncates at 24,000 characters. For several fields use `bodyPatch` with ONLY those fields; never paste a whole captured body back. For \"just the NEXT call\" / \"once, then let it work again\", send rule.times:1 \u2014 the rule auto-disables after one match. Without times it overrides EVERY matching request until deleted. Never fake one-shot by triggering the request yourself and deleting the rule: that spends the failure the user wanted to see. No captured request is needed: urlPattern + methods + status/body (or kind/delayMs) works for a call the app has not made yet \u2014 use fromRequestId only when you need the real response to edit.",
1847
1847
  "releaseNote": "packages/network/src/network/overrides/engine.ts:74 \u2014 overrideForRequest returns null when __DEV__ === false. The rule is still created, persisted and listed, and this action still returns ok:true, but it is NEVER applied to real traffic in a release build. Confirm with debugOverrides \u2192 engine.devFlag before telling anyone the override took effect.",
1848
1848
  "requires": [
1849
1849
  "a __DEV__ build for the rule to actually apply",
@@ -3607,8 +3607,29 @@
3607
3607
  {
3608
3608
  "toolId": "storage",
3609
3609
  "title": "Storage",
3610
- "summary": "Read and write the app's persisted state across all three backends \u2014 AsyncStorage, every registered MMKV instance, and registered Expo SecureStore keys \u2014 plus a recorded timeline of storage writes with per-event undo/jump. Reach for this first when a value is wrong, stale, missing, or only broken after a restart/upgrade: the bad value is usually sitting in storage. Buoy's own devtool keys (@react_buoy*, @buoy*, buoy-*) are stripped from every read path, so results are app data only. Nothing here is __DEV__-gated \u2014 every action really runs in a release build.",
3610
+ "summary": "Read and write the app's persisted state across all three backends \u2014 AsyncStorage, every registered MMKV instance, and registered Expo SecureStore keys \u2014 plus a recorded timeline of storage writes with per-event undo/jump. Reach for this first when a value is wrong, stale, missing, or only broken after a restart/upgrade: the bad value is usually sitting in storage. Buoy's own devtool keys (@react_buoy*, @buoy*, buoy-*) are stripped from every read path, so results are app data only. Nothing here is __DEV__-gated \u2014 every action really runs in a release build. When a saved value looks wrong, compare it with getRequiredKeys: a key written by an older app version shows up there with the wrong type (a bare \"1856\" where the app expects an object). The storage write timeline is this tool's getSnapshot (newest events, each with its key, value and the value it replaced) \u2014 read it for \"what changed in storage?\" and to find the event to undo. Do not use the Events tool for that: it records a source only after setEnabledSources, so earlier writes are not there.",
3611
3611
  "actions": [
3612
+ {
3613
+ "action": "getSnapshot",
3614
+ "summary": "Read the storage write timeline: the app's recent AsyncStorage and MMKV writes, newest first, each with its key, the value written and the value it replaced. Answers \"what changed in storage?\" and finds the event to undo.",
3615
+ "description": "Returns `{ events: [{ id, at, action, storage, key, value, prevValue }], total, returned }` newest first. Values are cut to 300 characters (read the key for the full value). `id` is what timeTravel.undo and timeTravel.jump take. Recording starts when something first watches the storage tool, so writes before that are not listed. Pass `limit` for how many writes (default 20) and `key` to list only writes whose key contains that text.",
3616
+ "params": {
3617
+ "type": "object",
3618
+ "properties": {
3619
+ "limit": {
3620
+ "type": "number",
3621
+ "description": "Most-recent N writes. Default 20."
3622
+ },
3623
+ "key": {
3624
+ "type": "string",
3625
+ "description": "Only writes whose key contains this text."
3626
+ }
3627
+ },
3628
+ "additionalProperties": false
3629
+ },
3630
+ "effect": "read",
3631
+ "release": "works"
3632
+ },
3612
3633
  {
3613
3634
  "action": "getRequiredKeys",
3614
3635
  "summary": "List the storage keys the app declares as required, with their expected types and backends.",
@@ -4029,7 +4050,7 @@
4029
4050
  },
4030
4051
  "effect": "read",
4031
4052
  "release": "works",
4032
- "description": "Returns [{key, description, keychainService, requireAuthentication}]. SecureStore has no key-enumeration API, so only keys the app declared via registerSecureStoreKeys(...) are visible \u2014 an empty [] means nothing was registered, NOT that the keychain is empty. requireAuthentication:true marks a biometric-protected key whose value Buoy never reads.",
4053
+ "description": "Returns [{key, description, keychainService, requireAuthentication}]. SecureStore has no key-enumeration API, so only keys the app declared via registerSecureStoreKeys(...) are visible \u2014 an empty [] means nothing was registered, NOT that the keychain is empty. requireAuthentication:true marks a biometric-protected key whose value Buoy never reads. Each key also has `hasValue` (true / false; null for a biometric-protected key, which is never read) \u2014 so you can tell an empty key from a set one, or see that the login moved from one key to another, without the value ever leaving the device.",
4033
4054
  "requires": [
4034
4055
  "registerSecureStoreKeys(SecureStore, [...]) called by the app (expo-secure-store)"
4035
4056
  ]
@@ -6190,7 +6211,7 @@
6190
6211
  {
6191
6212
  "toolId": "ask-buoy",
6192
6213
  "title": "Ask Buoy",
6193
- "summary": "Ask Buoy's OWN session \u2014 the changes it has made in this conversation, and the undo for them. Call listChanges when the user asks \"what did you change?\"; call undoAll when they say \"undo that\" / \"put it back\" / \"clean up\". Undo restores exactly what the ledger captured at write time: override rules Ask Buoy created are deleted, impersonation is stopped, storage keys are restored to their pre-write values, and a query cache edit (setQueryData) is put back to the data read just before the write \u2014 unless the app has refetched it since, in which case the server's data is already back and undo says so. Changes listed with reversible:false (state writes, wipes, one-shot actions) cannot be automatically reversed \u2014 say so honestly. NEVER improvise a reverse-write with a guessed shape instead of calling undoAll; a hand-rolled \"undo\" that writes invented data is worse than telling the user a change is permanent. Its retrieve action re-reads any earlier tool result in FULL: every result over 24,000 characters is cut with a `[truncated \u2014 \u2026; ref ev_N]` marker and every result compressed out of memory leaves an `[earlier result \u2014 \u2026; ref ev_N]` marker \u2014 call retrieve with that ref (and a path or pattern) instead of asking the user for a narrower slice or calling the tool again for a value you already had.",
6214
+ "summary": "Ask Buoy's OWN session \u2014 the changes it has made in this conversation, and the undo for them. Call listChanges when the user asks \"what did you change?\"; call undoChange with the id of the change they mean when they say \"undo that\" about one change, and undoAll when they say \"undo everything\" / \"put it all back\" / \"clean up\". Undo restores exactly what the ledger captured at write time: override rules Ask Buoy created are deleted, impersonation is stopped, storage keys are restored to their pre-write values, a query cache edit (setQueryData) is put back to the data read just before the write \u2014 unless the app has refetched it since, in which case the server's data is already back and undo says so \u2014 and a store edit (zustand.setState) has exactly the values it changed put back, unless something changed those same values again since. To undo ONE change and keep the rest, call listChanges and pass that change's id to undoChange. Changes listed with reversible:false (state writes, wipes, one-shot actions) cannot be automatically reversed \u2014 say so honestly. NEVER improvise a reverse-write with a guessed shape instead of calling undoAll; a hand-rolled \"undo\" that writes invented data is worse than telling the user a change is permanent. Its retrieve action re-reads any earlier tool result in FULL: every result over 24,000 characters is cut with a `[truncated \u2014 \u2026; ref ev_N]` marker and every result compressed out of memory leaves an `[earlier result \u2014 \u2026; ref ev_N]` marker \u2014 call retrieve with that ref (and a path or pattern) instead of asking the user for a narrower slice or calling the tool again for a value you already had.",
6194
6215
  "actions": [
6195
6216
  {
6196
6217
  "action": "listChanges",
@@ -6203,11 +6224,33 @@
6203
6224
  },
6204
6225
  "effect": "read",
6205
6226
  "release": "works",
6206
- "description": "Returns `{changes:[{toolId, action, kind, label, reversible}], returned}` \u2014 outstanding (not yet undone) changes only, oldest first. `reversible:true` means undoAll can restore that change exactly. `kind` \"transient\" is a one-shot action (a navigation, a tap) that changed no persistent state."
6227
+ "description": "Returns `{changes:[{id, toolId, action, kind, label, reversible}], returned}` \u2014 outstanding (not yet undone) changes only, oldest first. `reversible:true` means undoChange (with that `id`) or undoAll can restore that change exactly. `kind` \"transient\" is a one-shot action (a navigation, a tap) that changed no persistent state."
6228
+ },
6229
+ {
6230
+ "action": "undoChange",
6231
+ "summary": "Undo ONE change Ask Buoy made this conversation, by the id listChanges gave it. Use it for \"undo that\" when the user means a single change (usually the latest one) and other changes should stay.",
6232
+ "params": {
6233
+ "type": "object",
6234
+ "properties": {
6235
+ "id": {
6236
+ "type": "string",
6237
+ "description": "The change's id from listChanges, exactly as listChanges returned it, e.g. \"fx-3-1759330000000\"."
6238
+ }
6239
+ },
6240
+ "required": [
6241
+ "id"
6242
+ ],
6243
+ "additionalProperties": false,
6244
+ "description": "`{id}` from listChanges."
6245
+ },
6246
+ "effect": "write",
6247
+ "release": "works",
6248
+ "description": "Returns `{ok:true, undone:{id, label}}`, or `{ok:false, error}` when the id is unknown, already undone, or the change can't be put back (it is permanent, or the same value was changed again since \u2014 the error says which). Report a refusal honestly; do not write the old value back by hand over a newer one.",
6249
+ "servedBy": "engine"
6207
6250
  },
6208
6251
  {
6209
6252
  "action": "undoAll",
6210
- "summary": "Undo every reversible change Ask Buoy made this conversation \u2014 deletes override rules it created, stops impersonation, restores storage keys to their prior values. The safe direction: use it whenever the user asks to undo or clean up.",
6253
+ "summary": "Undo every reversible change Ask Buoy made this conversation \u2014 deletes override rules it created, stops impersonation, restores storage keys, cache edits and store edits to their prior values. Use it when the user asks to undo everything or clean up; for one change use undoChange.",
6211
6254
  "params": {
6212
6255
  "type": "object",
6213
6256
  "properties": {},
@@ -6216,7 +6259,7 @@
6216
6259
  },
6217
6260
  "effect": "write",
6218
6261
  "release": "works",
6219
- "description": "Returns `{ok, reverted, failed:[{label, error}], skippedTransients, permanent}`. Report the numbers honestly: `failed` entries were attempted and could not be restored (tell the user which, using the labels); `permanent` is how many changes were never undoable (state writes, refetches) and are still applied \u2014 say so, they are not failures; `skippedTransients` are one-shot actions that never needed undoing. This undoes ALL reversible changes from this conversation, newest first \u2014 there is no per-change undo action, so if the user wants to keep one change, say so instead of calling this."
6262
+ "description": "Returns `{ok, reverted, failed:[{label, error}], skippedTransients, permanent}`. Report the numbers honestly: `failed` entries were attempted and could not be restored (tell the user which, using the labels); `permanent` is how many changes were never undoable (state writes, refetches) and are still applied \u2014 say so, they are not failures; `skippedTransients` are one-shot actions that never needed undoing. This undoes ALL reversible changes from this conversation, newest first. To undo one change and keep the others, use undoChange instead."
6220
6263
  },
6221
6264
  {
6222
6265
  "action": "retrieve",
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.CATALOG_ACTIONS=void 0;const CATALOG_ACTIONS=exports.CATALOG_ACTIONS={"env":["getSnapshot"],"console":["getSnapshot","clearEntries"],"sentry":["getSnapshot","clearEnvelopes"],"jotai":["listAtoms","getAtomValue","getChangeDetail","setAtom","clearEvents"],"route-events":["getSnapshot","getCurrentRoute","navigate","stackGoBack","stackNavigateToIndex","stackPopToIndex","stackPopToTop","clearEvents"],"debug-borders":["cycleMode","setMode"],"zustand":["listStores","getStoreState","getChangeDetail","setState","rehydrate","clearEvents"],"redux":["getState","getActionDetail","dispatch","clearEvents"],"impersonate":["searchUsers","startImpersonation","stopImpersonation","pauseImpersonation","resumeImpersonation","updateSettings","removeFromHistory","clearHistory"],"query":["listQueries","getQueryData","refetch","invalidate","reset","remove","setQueryData","triggerError","restoreError","triggerLoading","restoreLoading","clearQueryCache","clearMutationCache","setOnline"],"events":["exportEvents","setEnabledSources","clearEvents"],"network":["getSnapshot","getCaptureStatus","getEventBody","setPinned","setSaved","removeSavedRecord","clearSavedRequests","clearPinnedRequests","clearEvents","listOverrideRules","getOverrideRuleBody","debugOverrides","upsertOverrideRule","setOverrideRuleEnabled","setOverridesEnabled","deleteOverrideRule","clearOverrideRules","getNetworkConditions","setNetworkConditions"],"js-top":["sample","getOriginDetail","setEnabled","pause","resume","clear"],"app":["ping","reloadApp"],"time-machine":["list","capture","captureBaseline","wipeAll","restore","preview","inspect","delete","rename","duplicate","setExclusions","setScope","setRestoreRoute"],"clock":["getState","setTime","shift","freeze","resume","setRate","reset","updateSettings","jumpToTokenExpiry","failNextRequest","stopTokenCheck"],"lifecycle":["getState","background","interrupt","returnToApp","memoryWarning","openUrl","pressBack","setColorScheme","setPower","resetPower","relaunch","runRecipe","clearReport","reset"],"location":["getState","setLocation","startRoute","pauseRoute","resumeRoute","stopRoute","seekRoute","setSpeed","enterRegion","exitRegion","setConditions","setSignal","setServices","tune","useRealLocation","reset","updateSettings","clearLog"],"permissions":["getState","setOverride","clearOverride","resetAll","requestReal","updateSettings","simulateReturnFromSettings","clearLog"],"storage":["getRequiredKeys","async.getAllKeys","async.multiGet","async.getItem","async.setItem","async.removeItem","async.multiRemove","async.multiSet","async.clear","clearAppStorage","getEventDetail","clearEvents","timeTravel.undo","timeTravel.jump","mmkv.snapshot","mmkv.get","mmkv.set","mmkv.remove","secure.keys","secure.snapshot","secure.get","secure.set","secure.delete"],"highlight-updates":["describeScreen","tapElement","waitFor","beginMeasurement","endMeasurement","locateComponent","getRenderDetail","setEnabled","toggle","setSilentTracking","toggleFreeze","setSpotlight","clearRenderCounts","startTouchCapture","stopTouchCapture","clearTouchCapture","readTouchCapture"],"scenarios":["listScenarios","getScenario","save","acceptDraft","discardDraft","preview","run","deactivate","delete","setFolder","listFolders","getActive","export"],"perf-monitor":["setEnabled","startRecording","stopRecording","savePending","discardPending","mark","startAutomation","cancelAutomation","acknowledgeAutomation","refreshIndex","getAutomationConfig","setAutomationConfig","loadReport","deleteReport","deleteBatch","clearAll"],"assets":["list","getDetail","rescan","measureSizes","saveBaseline","clearBaseline","getDiff","clearRecords","getScanStatus"],"tv-remote":["arm","getEventsSince","setMenuCapture","disarm","clear"],"focus-inspector":["rescan","focusElement","setTracking","clearHistory"],"images":["list","getDetail","getCaptureStatus","retry","flash","proveSavings","setOverride","clearOverride","setNetworkMode","setBlankImages","hardReload","evictDisk","massAction","clearRecords","clearExpoCaches"],"ask-buoy":["listChanges","undoAll","retrieve","openProcedure"],"push-notifications":["getSnapshot","getCapabilities","getEvent","getPermissions","listPresented","refreshToken","setCaptureSession","clearCapturedEvents","scheduleLocal"],"image-overlay":["getSnapshot","listTargets","selectTarget","loadImage","setSettings","fitToScreen","resetSettings","remove"]};
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.CATALOG_ACTIONS=void 0;const CATALOG_ACTIONS=exports.CATALOG_ACTIONS={"env":["getSnapshot"],"console":["getSnapshot","clearEntries"],"sentry":["getSnapshot","clearEnvelopes"],"jotai":["listAtoms","getAtomValue","getChangeDetail","setAtom","clearEvents"],"route-events":["getSnapshot","getCurrentRoute","navigate","stackGoBack","stackNavigateToIndex","stackPopToIndex","stackPopToTop","clearEvents"],"debug-borders":["cycleMode","setMode"],"zustand":["listStores","getStoreState","getChangeDetail","setState","rehydrate","clearEvents"],"redux":["getState","getActionDetail","dispatch","clearEvents"],"impersonate":["searchUsers","startImpersonation","stopImpersonation","pauseImpersonation","resumeImpersonation","updateSettings","removeFromHistory","clearHistory"],"query":["listQueries","getQueryData","refetch","invalidate","reset","remove","setQueryData","triggerError","restoreError","triggerLoading","restoreLoading","clearQueryCache","clearMutationCache","setOnline"],"events":["exportEvents","setEnabledSources","clearEvents"],"network":["getSnapshot","getCaptureStatus","getEventBody","setPinned","setSaved","removeSavedRecord","clearSavedRequests","clearPinnedRequests","clearEvents","listOverrideRules","getOverrideRuleBody","debugOverrides","upsertOverrideRule","setOverrideRuleEnabled","setOverridesEnabled","deleteOverrideRule","clearOverrideRules","getNetworkConditions","setNetworkConditions"],"js-top":["sample","getOriginDetail","setEnabled","pause","resume","clear"],"app":["ping","reloadApp"],"time-machine":["list","capture","captureBaseline","wipeAll","restore","preview","inspect","delete","rename","duplicate","setExclusions","setScope","setRestoreRoute"],"clock":["getState","setTime","shift","freeze","resume","setRate","reset","updateSettings","jumpToTokenExpiry","failNextRequest","stopTokenCheck"],"lifecycle":["getState","background","interrupt","returnToApp","memoryWarning","openUrl","pressBack","setColorScheme","setPower","resetPower","relaunch","runRecipe","clearReport","reset"],"location":["getState","setLocation","startRoute","pauseRoute","resumeRoute","stopRoute","seekRoute","setSpeed","enterRegion","exitRegion","setConditions","setSignal","setServices","tune","useRealLocation","reset","updateSettings","clearLog"],"permissions":["getState","setOverride","clearOverride","resetAll","requestReal","updateSettings","simulateReturnFromSettings","clearLog"],"storage":["getSnapshot","getRequiredKeys","async.getAllKeys","async.multiGet","async.getItem","async.setItem","async.removeItem","async.multiRemove","async.multiSet","async.clear","clearAppStorage","getEventDetail","clearEvents","timeTravel.undo","timeTravel.jump","mmkv.snapshot","mmkv.get","mmkv.set","mmkv.remove","secure.keys","secure.snapshot","secure.get","secure.set","secure.delete"],"highlight-updates":["describeScreen","tapElement","waitFor","beginMeasurement","endMeasurement","locateComponent","getRenderDetail","setEnabled","toggle","setSilentTracking","toggleFreeze","setSpotlight","clearRenderCounts","startTouchCapture","stopTouchCapture","clearTouchCapture","readTouchCapture"],"scenarios":["listScenarios","getScenario","save","acceptDraft","discardDraft","preview","run","deactivate","delete","setFolder","listFolders","getActive","export"],"perf-monitor":["setEnabled","startRecording","stopRecording","savePending","discardPending","mark","startAutomation","cancelAutomation","acknowledgeAutomation","refreshIndex","getAutomationConfig","setAutomationConfig","loadReport","deleteReport","deleteBatch","clearAll"],"assets":["list","getDetail","rescan","measureSizes","saveBaseline","clearBaseline","getDiff","clearRecords","getScanStatus"],"tv-remote":["arm","getEventsSince","setMenuCapture","disarm","clear"],"focus-inspector":["rescan","focusElement","setTracking","clearHistory"],"images":["list","getDetail","getCaptureStatus","retry","flash","proveSavings","setOverride","clearOverride","setNetworkMode","setBlankImages","hardReload","evictDisk","massAction","clearRecords","clearExpoCaches"],"ask-buoy":["listChanges","undoChange","undoAll","retrieve","openProcedure"],"push-notifications":["getSnapshot","getCapabilities","getEvent","getPermissions","listPresented","refreshToken","setCaptureSession","clearCapturedEvents","scheduleLocal"],"image-overlay":["getSnapshot","listTargets","selectTarget","loadImage","setSettings","fitToScreen","resetSettings","remove"]};
@@ -1,3 +1,3 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.aliasNote=aliasNote;exports.hashQueryKey=hashQueryKey;exports.normalizeParams=normalizeParams;const isPlainObject=r=>!!r&&typeof r==="object"&&!Array.isArray(r);function hashQueryKey(r){return JSON.stringify(r,(e,t)=>isPlainObject(t)?Object.keys(t).sort().reduce((a,i)=>{a[i]=t[i];return a},{}):t)}function alias(r,e,t,a){if(r[t]!==void 0||r[e]===void 0)return false;r[t]=r[e];delete r[e];a.push(`Accepted \`${e}\` as \`${t}\` \u2014 the parameter is named \`${t}\`.`);return true}const QUERY_HASH_ACTIONS=new Set(["getQueryData","refetch","invalidate","reset","remove","triggerError","restoreError","triggerLoading","restoreLoading"]);const RULE_FIELDS=["id","urlPattern","enabled","name","methods","kind","status","statusText","headers","body","bodyPatch","bodyPath","bodyValue","failKind","delayMs","times","alternate","force","bodyOmitted"];function normalizeQuery(r,e,t){if(QUERY_HASH_ACTIONS.has(r)&&e.queryHash===void 0){const a=isPlainObject(e.filters)?e.filters:void 0;const i=e.queryKey??e.key??e.hash??a?.queryKey??a?.queryHash;if(Array.isArray(i)){e.queryHash=hashQueryKey(i);t.push(`Accepted the queryKey array as the query's hash (${e.queryHash}) \u2014 the parameter is \`queryHash\`, the string from listQueries.`)}else if(typeof i==="string"){e.queryHash=i;t.push(`Accepted it as \`queryHash\` \u2014 that is the parameter name.`)}delete e.queryKey;delete e.key;delete e.hash;delete e.filters}if(r==="setQueryData"){if(isPlainObject(e.data)){for(const a of["merge","force"]){if(typeof e.data[a]==="boolean"&&e[a]===void 0){e[a]=e.data[a];delete e.data[a];t.push(`Moved \`${a}\` out of \`data\` to the top level \u2014 it is a write flag, not part of the value.`)}}}alias(e,"newData","data",t)||e.path===void 0&&alias(e,"value","data",t)||alias(e,"patch","data",t);if(e.queryKey===void 0&&typeof e.queryHash==="string"){try{const a=JSON.parse(e.queryHash);if(Array.isArray(a)){e.queryKey=a;t.push("Accepted the queryHash as the key \u2014 setQueryData takes `queryKey`, the array.")}}catch{}}if(e.patch!==void 0||e.merge===void 0){if(t.some(a=>a.includes("`patch`")))e.merge=true}}}function normalizeNetwork(r,e,t){if(r==="getEventBody"||r==="setPinned"||r==="setSaved"){alias(e,"requestId","id",t)||alias(e,"eventId","id",t)}if(r==="deleteOverrideRule"||r==="setOverrideRuleEnabled"||r==="getOverrideRuleBody"){alias(e,"ruleId","id",t)}if(r==="upsertOverrideRule"){const a=isPlainObject(e.rule)?{...e.rule}:{};if(e.fromRequestId===void 0&&typeof a.fromRequestId==="string"){e.fromRequestId=a.fromRequestId;t.push("Accepted `rule.fromRequestId` \u2014 it is a top-level parameter, beside `rule`.")}delete a.fromRequestId;unwrapRuleSections(a,t);ruleIntentWords(e,a,t);const i=[];for(const d of RULE_FIELDS){if(e[d]!==void 0&&a[d]===void 0){a[d]=e[d];i.push(d)}delete e[d]}if(i.length)t.push(`Accepted ${i.map(d=>`\`${d}\``).join(", ")} at the top level \u2014 rule fields belong inside \`rule\`.`);if(Object.keys(a).length)e.rule=a}}function unwrapRuleSections(r,e){for(const[t,a]of[["match",["urlPattern","methods","method","url","pattern","urlContains","contains"]],["response",["status","statusText","body","headers","bodyPatch"]]]){const i=r[t];if(!isPlainObject(i))continue;const d=[];for(const s of a){if(i[s]===void 0)continue;const n=s==="method"?"methods":s==="url"||s==="pattern"||s==="urlContains"||s==="contains"?"urlPattern":s;if(r[n]!==void 0)continue;r[n]=n==="methods"&&!Array.isArray(i[s])?[i[s]]:i[s];delete i[s];d.push(s)}if(!d.length)continue;if(!Object.keys(i).length)delete r[t];e.push(`Read \`rule.${t}.{${d.join(", ")}}\` as top-level rule fields \u2014 this rule is flat, unlike msw/nock-style mocks.`)}}function ruleIntentWords(r,e,t){if(e.bodyPath===void 0&&typeof e.path==="string"){e.bodyPath=e.path;if(e.bodyValue===void 0&&e.value!==void 0)e.bodyValue=e.value;delete e.path;delete e.value;t.push("Read `path`/`value` as `bodyPath`/`bodyValue` \u2014 on a rule they address the response body.")}const a=s=>e[s]!==void 0?e[s]:r[s];const i=s=>{delete e[s];delete r[s]};const d=a("mode");if(typeof d==="string"&&["respond","fail","delay"].includes(d)&&e.kind===void 0){e.kind=d;t.push(`Read \`mode: "${d}"\` as \`kind: "${d}"\`.`)}i("mode");if(a("fail")===true&&e.kind===void 0){e.kind="fail";t.push('Read `fail: true` as `kind: "fail"` \u2014 a transport failure, as if offline.')}i("fail");if(a("failOnce")===true){if(e.kind===void 0)e.kind="fail";if(e.times===void 0)e.times=1;t.push('Read `failOnce: true` as `kind: "fail"` with `times: 1` \u2014 it fires once, then the rule disables itself.')}i("failOnce");if(a("once")===true&&e.times===void 0){e.times=1;t.push("Read `once: true` as `times: 1` \u2014 the rule auto-disables after one match.")}i("once");for(const s of["maxHits","hits","count","maxTimes","applyTimes"]){const n=a(s);if(typeof n==="number"&&Number.isFinite(n)&&n>0&&e.times===void 0){e.times=n;t.push(`Read \`${s}: ${n}\` as \`times: ${n}\` \u2014 the rule auto-disables after ${n} match(es).`)}i(s)}}function normalizeZustand(r,e,t){alias(e,"name","storeName",t)||alias(e,"store","storeName",t);if(r==="setState"){alias(e,"statePatch","state",t)||alias(e,"partial","state",t)||alias(e,"patch","state",t)||alias(e,"newState","state",t)||alias(e,"nextState","state",t)||e.path===void 0&&alias(e,"value","state",t);if(e.replace===void 0){e.replace=false;t.push("Applied replace:false (merge) \u2014 a store keeps action functions that can't be re-sent, and replacing would wipe them (the buttons that call them would then crash). Send replace:true only to deliberately reset the whole store.")}}}function normalizeRoutes(r,e,t){if(r==="navigate"){alias(e,"route","path",t)||alias(e,"to","path",t)||alias(e,"url","path",t)||alias(e,"pathname","path",t)}}function normalizeParams(r,e,t){const a={...t};const i=[];switch(r){case"query":normalizeQuery(e,a,i);break;case"network":normalizeNetwork(e,a,i);break;case"zustand":normalizeZustand(e,a,i);break;case"route-events":normalizeRoutes(e,a,i);break;default:break}return{params:a,notes:i}}function aliasNote(r){return r.length?`
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.aliasNote=aliasNote;exports.hashQueryKey=hashQueryKey;exports.normalizeParams=normalizeParams;const isPlainObject=i=>!!i&&typeof i==="object"&&!Array.isArray(i);function hashQueryKey(i){return JSON.stringify(i,(e,t)=>isPlainObject(t)?Object.keys(t).sort().reduce((a,r)=>{a[r]=t[r];return a},{}):t)}function alias(i,e,t,a){if(i[t]!==void 0||i[e]===void 0)return false;i[t]=i[e];delete i[e];a.push(`Accepted \`${e}\` as \`${t}\` \u2014 the parameter is named \`${t}\`.`);return true}const QUERY_HASH_ACTIONS=new Set(["getQueryData","refetch","invalidate","reset","remove","triggerError","restoreError","triggerLoading","restoreLoading"]);const RULE_FIELDS=["id","urlPattern","enabled","name","methods","kind","status","statusText","headers","body","bodyPatch","bodyPath","bodyValue","failKind","delayMs","times","alternate","force","bodyOmitted"];function normalizeQuery(i,e,t){if(QUERY_HASH_ACTIONS.has(i)&&e.queryHash===void 0){const a=isPlainObject(e.filters)?e.filters:void 0;const r=e.queryKey??e.key??e.hash??a?.queryKey??a?.queryHash;if(Array.isArray(r)){e.queryHash=hashQueryKey(r);t.push(`Accepted the queryKey array as the query's hash (${e.queryHash}) \u2014 the parameter is \`queryHash\`, the string from listQueries.`)}else if(typeof r==="string"){e.queryHash=r;t.push(`Accepted it as \`queryHash\` \u2014 that is the parameter name.`)}delete e.queryKey;delete e.key;delete e.hash;delete e.filters}if(i==="setQueryData"){if(isPlainObject(e.data)){for(const a of["merge","force"]){if(typeof e.data[a]==="boolean"&&e[a]===void 0){e[a]=e.data[a];delete e.data[a];t.push(`Moved \`${a}\` out of \`data\` to the top level \u2014 it is a write flag, not part of the value.`)}}}alias(e,"newData","data",t)||e.path===void 0&&alias(e,"value","data",t)||alias(e,"patch","data",t);if(e.queryKey===void 0&&typeof e.queryHash==="string"){try{const a=JSON.parse(e.queryHash);if(Array.isArray(a)){e.queryKey=a;t.push("Accepted the queryHash as the key \u2014 setQueryData takes `queryKey`, the array.")}}catch{}}if(e.patch!==void 0||e.merge===void 0){if(t.some(a=>a.includes("`patch`")))e.merge=true}}}function normalizeNetwork(i,e,t){if(i==="getEventBody"||i==="setPinned"||i==="setSaved"){alias(e,"requestId","id",t)||alias(e,"eventId","id",t)}if(i==="deleteOverrideRule"||i==="setOverrideRuleEnabled"||i==="getOverrideRuleBody"){alias(e,"ruleId","id",t)}if(i==="upsertOverrideRule"){const a=isPlainObject(e.rule)?{...e.rule}:{};if(e.fromRequestId===void 0&&typeof a.fromRequestId==="string"){e.fromRequestId=a.fromRequestId;t.push("Accepted `rule.fromRequestId` \u2014 it is a top-level parameter, beside `rule`.")}delete a.fromRequestId;unwrapRuleSections(a,t);ruleIntentWords(e,a,t);const r=[];for(const d of RULE_FIELDS){if(e[d]!==void 0&&a[d]===void 0){a[d]=e[d];r.push(d)}delete e[d]}if(r.length)t.push(`Accepted ${r.map(d=>`\`${d}\``).join(", ")} at the top level \u2014 rule fields belong inside \`rule\`.`);if(typeof a.times==="number"&&a.times<=0){delete a.times;t.push("Read `times: 0` as no limit \u2014 leave `times` out for a rule that keeps firing; `times: 1` fires once.")}if(Object.keys(a).length)e.rule=a}}function unwrapRuleSections(i,e){for(const[t,a]of[["match",["urlPattern","methods","method","url","pattern","urlContains","contains"]],["response",["status","statusText","body","headers","bodyPatch"]]]){const r=i[t];if(!isPlainObject(r))continue;const d=[];for(const s of a){if(r[s]===void 0)continue;const n=s==="method"?"methods":s==="url"||s==="pattern"||s==="urlContains"||s==="contains"?"urlPattern":s;if(i[n]!==void 0)continue;i[n]=n==="methods"&&!Array.isArray(r[s])?[r[s]]:r[s];delete r[s];d.push(s)}if(!d.length)continue;if(!Object.keys(r).length)delete i[t];e.push(`Read \`rule.${t}.{${d.join(", ")}}\` as top-level rule fields \u2014 this rule is flat, unlike msw/nock-style mocks.`)}}function ruleIntentWords(i,e,t){if(e.bodyPath===void 0&&typeof e.path==="string"){e.bodyPath=e.path;if(e.bodyValue===void 0&&e.value!==void 0)e.bodyValue=e.value;delete e.path;delete e.value;t.push("Read `path`/`value` as `bodyPath`/`bodyValue` \u2014 on a rule they address the response body.")}const a=s=>e[s]!==void 0?e[s]:i[s];const r=s=>{delete e[s];delete i[s]};const d=a("mode");if(typeof d==="string"&&["respond","fail","delay"].includes(d)&&e.kind===void 0){e.kind=d;t.push(`Read \`mode: "${d}"\` as \`kind: "${d}"\`.`)}r("mode");if(a("fail")===true&&e.kind===void 0){e.kind="fail";t.push('Read `fail: true` as `kind: "fail"` \u2014 a transport failure, as if offline.')}r("fail");if(a("failOnce")===true){if(e.kind===void 0)e.kind="fail";if(e.times===void 0)e.times=1;t.push('Read `failOnce: true` as `kind: "fail"` with `times: 1` \u2014 it fires once, then the rule disables itself.')}r("failOnce");if(a("once")===true&&e.times===void 0){e.times=1;t.push("Read `once: true` as `times: 1` \u2014 the rule auto-disables after one match.")}r("once");for(const s of["maxHits","hits","count","maxTimes","applyTimes"]){const n=a(s);if(typeof n==="number"&&Number.isFinite(n)&&n>0&&e.times===void 0){e.times=n;t.push(`Read \`${s}: ${n}\` as \`times: ${n}\` \u2014 the rule auto-disables after ${n} match(es).`)}r(s)}}function normalizeZustand(i,e,t){alias(e,"name","storeName",t)||alias(e,"store","storeName",t);if(i==="setState"){alias(e,"statePatch","state",t)||alias(e,"partial","state",t)||alias(e,"patch","state",t)||alias(e,"newState","state",t)||alias(e,"nextState","state",t)||e.path===void 0&&alias(e,"value","state",t);if(e.replace===void 0){e.replace=false;t.push("Applied replace:false (merge) \u2014 a store keeps action functions that can't be re-sent, and replacing would wipe them (the buttons that call them would then crash). Send replace:true only to deliberately reset the whole store.")}}}function normalizeRoutes(i,e,t){if(i==="navigate"){alias(e,"route","path",t)||alias(e,"to","path",t)||alias(e,"url","path",t)||alias(e,"pathname","path",t)}}function normalizeParams(i,e,t){const a={...t};const r=[];switch(i){case"query":normalizeQuery(e,a,r);break;case"network":normalizeNetwork(e,a,r);break;case"zustand":normalizeZustand(e,a,r);break;case"route-events":normalizeRoutes(e,a,r);break;default:break}return{params:a,notes:r}}function aliasNote(i){return i.length?`
2
2
 
3
- [Buoy] ${r.join(" ")}`:""}
3
+ [Buoy] ${i.join(" ")}`:""}
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.SNAPSHOT_ACTION=void 0;exports.projectSnapshot=projectSnapshot;var _redact=require("../policy/redact");const SNAPSHOT_ACTION=exports.SNAPSHOT_ACTION="getSnapshot";const SEVERITY={verbose:0,debug:0,trace:0,log:1,info:1,warn:2,warning:2,error:3};function asArray(t){return Array.isArray(t)?t:[]}function rec(t){return t&&typeof t==="object"?t:{}}function num(t,e){return typeof t==="number"&&t>0?t:e}function newest(t,e){return t.length<=e?t:t.slice(0,e)}function projectConsole(t,e){const o=asArray(rec(t).entries);const u=typeof e.level==="string"?SEVERITY[e.level]??0:0;const i=typeof e.pattern==="string"?e.pattern.toLowerCase():void 0;const c=o.filter(a=>{const n=rec(a);const r=String(n.level??"log");if((SEVERITY[r]??1)<u)return false;if(!i)return true;return String(n.message??"").toLowerCase().includes(i)});const p=newest(c,num(e.limit,40));return{totalCaptured:o.length,shown:p.length,entries:p.map(a=>{const n=rec(a);return{at:n.timestamp,level:n.level,message:n.message}})}}function projectNetwork(t,e){const o=rec(t);const u=asArray(o.events).filter(n=>!(0,_redact.isSelfTraffic)(rec(n).url));const i=e.failedOnly===true;const c=typeof e.pattern==="string"?e.pattern.toLowerCase():void 0;const p=u.filter(n=>{const r=rec(n);const s=typeof r.status==="number"?r.status:void 0;if(i&&!(r.error||s!==void 0&&s>=400))return false;if(!c)return true;return String(r.url??"").toLowerCase().includes(c)});const a=newest(p,num(e.limit,25));return{totalCaptured:u.length,shown:a.length,requests:a.map(n=>{const r=rec(n);return{id:r.id,method:r.method,url:r.url,status:r.status,durationMs:r.duration,error:r.error,overridden:r.override?true:void 0}}),hint:"Read one response with network.getEventBody({ id })."}}function flattenRoutes(t,e=[],o=80){for(const u of asArray(t)){if(e.length>=o)break;const i=rec(u);if(i.isInternal!==true&&typeof i.path==="string")e.push(i.path);flattenRoutes(i.children,e,o)}return e}function projectRoutes(t,e){const o=rec(t);const u=rec(o.sitemap);const i=asArray(o.stack).map(rec);const c=asArray(o.events).map(rec);const p=i.map(s=>({name:s.name,path:s.pathname,focused:s.isFocused===true?true:void 0}));const a=c.slice(0,num(e.limit,15)).map(s=>({at:s.timestamp,from:s.previousPathname,to:s.pathname}));const n=i.find(s=>s.isFocused===true)??i[i.length-1];const r=c.length?{path:c[0].pathname,source:"event"}:typeof n?.pathname==="string"?{path:n.pathname,source:"stack"}:void 0;return{currentRoute:r,stack:p,routes:flattenRoutes(u.routes),sitemapSource:u.source,recentNavigations:a}}function projectEnv(t){const e=rec(t);return{env:e.env??e.variables,requiredEnvVars:e.requiredEnvVars}}function projectSentry(t,e){const o=rec(t);const u=asArray(o.envelopes).map(rec);const i=typeof e.type==="string"?e.type:void 0;const c=typeof e.pattern==="string"?e.pattern.toLowerCase():void 0;const p=u.filter(n=>{const r=asArray(n.items).map(rec);if(i&&!r.some(s=>s.type===i))return false;if(!c)return true;return r.some(s=>String(s.summary??"").toLowerCase().includes(c))});const a=newest(p,num(e.limit,10));return{status:o.status,totalCaptured:u.length,shown:a.length,envelopes:a.map(n=>({id:n.id,at:n.timestamp,origin:n.origin,eventId:n.eventId,totalBytes:n.totalBytes,items:asArray(n.items).map(r=>{const s=rec(r);return{type:s.type,summary:s.summary,spanCount:s.spanCount,bytes:s.bytes}})})),hint:"For crash stack traces, console's [FATAL] entries carry the component stacks."}}function projectSnapshot(t,e,o={}){switch(t){case"console":return projectConsole(e,o);case"network":return projectNetwork(e,o);case"route-events":return projectRoutes(e,o);case"env":return projectEnv(e);case"sentry":return projectSentry(e,o);default:return e}}
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.SNAPSHOT_ACTION=void 0;exports.projectSnapshot=projectSnapshot;var _redact=require("../policy/redact");const SNAPSHOT_ACTION=exports.SNAPSHOT_ACTION="getSnapshot";const SEVERITY={verbose:0,debug:0,trace:0,log:1,info:1,warn:2,warning:2,error:3};function asArray(e){return Array.isArray(e)?e:[]}function rec(e){return e&&typeof e==="object"?e:{}}function num(e,t){return typeof e==="number"&&e>0?e:t}function newest(e,t){return e.length<=t?e:e.slice(0,t)}function projectConsole(e,t){const r=asArray(rec(e).entries);const a=typeof t.level==="string"?SEVERITY[t.level]??0:0;const o=typeof t.pattern==="string"?t.pattern.toLowerCase():void 0;const l=r.filter(u=>{const n=rec(u);const i=String(n.level??"log");if((SEVERITY[i]??1)<a)return false;if(!o)return true;return String(n.message??"").toLowerCase().includes(o)});const s=newest(l,num(t.limit,40));return{totalCaptured:r.length,shown:s.length,entries:s.map(u=>{const n=rec(u);return{at:n.timestamp,level:n.level,message:n.message}})}}function projectNetwork(e,t){const r=rec(e);const a=asArray(r.events).filter(n=>!(0,_redact.isSelfTraffic)(rec(n).url));const o=t.failedOnly===true;const l=typeof t.pattern==="string"?t.pattern.toLowerCase():void 0;const s=a.filter(n=>{const i=rec(n);const c=typeof i.status==="number"?i.status:void 0;if(o&&!(i.error||c!==void 0&&c>=400))return false;if(!l)return true;return String(i.url??"").toLowerCase().includes(l)});const u=newest(s,num(t.limit,25));return{totalCaptured:a.length,shown:u.length,requests:u.map(n=>{const i=rec(n);return{id:i.id,method:i.method,url:i.url,status:i.status,durationMs:i.duration,error:i.error,overridden:i.override?true:void 0}}),hint:"Read one response with network.getEventBody({ id })."}}function flattenRoutes(e,t=[],r=80){for(const a of asArray(e)){if(t.length>=r)break;const o=rec(a);if(o.isInternal!==true&&typeof o.path==="string")t.push(o.path);flattenRoutes(o.children,t,r)}return t}function projectStorage(e,t){const r=s=>typeof s==="string"&&s.length>300?`${s.slice(0,300)}\u2026`:s;const a=typeof t.key==="string"?t.key.toLowerCase():void 0;const o=asArray(e).map(rec).map(s=>{const u=rec(s.data);return{id:s.id,at:s.timestamp,action:s.action,storage:s.storageType,key:u.key??u.keys,value:r(u.value),prevValue:r(u.prevValue)}}).filter(s=>!a||JSON.stringify(s.key??"").toLowerCase().includes(a)).sort((s,u)=>String(u.at).localeCompare(String(s.at)));const l=num(t.limit,20);return{events:o.slice(0,l),total:o.length,returned:Math.min(l,o.length)}}function projectRoutes(e,t){const r=rec(e);const a=rec(r.sitemap);const o=asArray(r.stack).map(rec);const l=asArray(r.events).map(rec);const s=o.map(c=>({name:c.name,path:c.pathname,focused:c.isFocused===true?true:void 0}));const u=l.slice(0,num(t.limit,15)).map(c=>({at:c.timestamp,from:c.previousPathname,to:c.pathname}));const n=o.find(c=>c.isFocused===true)??o[o.length-1];const i=l.length?{path:l[0].pathname,source:"event"}:typeof n?.pathname==="string"?{path:n.pathname,source:"stack"}:void 0;return{currentRoute:i,stack:s,routes:flattenRoutes(a.routes),sitemapSource:a.source,recentNavigations:u}}function projectEnv(e){const t=rec(e);return{env:t.env??t.variables,requiredEnvVars:t.requiredEnvVars}}function projectSentry(e,t){const r=rec(e);const a=asArray(r.envelopes).map(rec);const o=typeof t.type==="string"?t.type:void 0;const l=typeof t.pattern==="string"?t.pattern.toLowerCase():void 0;const s=a.filter(n=>{const i=asArray(n.items).map(rec);if(o&&!i.some(c=>c.type===o))return false;if(!l)return true;return i.some(c=>String(c.summary??"").toLowerCase().includes(l))});const u=newest(s,num(t.limit,10));return{status:r.status,totalCaptured:a.length,shown:u.length,envelopes:u.map(n=>({id:n.id,at:n.timestamp,origin:n.origin,eventId:n.eventId,totalBytes:n.totalBytes,items:asArray(n.items).map(i=>{const c=rec(i);return{type:c.type,summary:c.summary,spanCount:c.spanCount,bytes:c.bytes}})})),hint:"For crash stack traces, console's [FATAL] entries carry the component stacks."}}function projectSnapshot(e,t,r={}){switch(e){case"console":return projectConsole(t,r);case"network":return projectNetwork(t,r);case"route-events":return projectRoutes(t,r);case"storage":return projectStorage(t,r);case"env":return projectEnv(t);case"sentry":return projectSentry(t,r);default:return t}}
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.EffectLedger=void 0;exports.isReversible=isReversible;var _digest=require("./digest");function isReversible(f){return f.kind!=="transient"&&f.kind!=="state-write"}let seq=0;class EffectLedger{entries=[];listeners=new Set;gen=0;get generation(){return this.gen}record(t,n,e,r){if(r!==void 0&&r!==this.gen)return void 0;const i={id:`fx-${++seq}-${Date.now()}`,at:Date.now(),toolId:t,action:n,effect:e};this.entries=[...this.entries,i];this.emit();return i}list(){return this.entries}liveCallIds(){const t=new Set;for(const n of this.entries)if(!n.undoneAt&&n.callId)t.add(n.callId);return t}restore(t){if(this.entries.length>0||t.length===0)return false;this.entries=[...t];this.emit();return true}touch(){this.entries=[...this.entries];this.emit()}reversible(){return this.entries.filter(t=>!t.undoneAt&&isReversible(t.effect))}transients(){return this.entries.filter(t=>!t.undoneAt&&t.effect.kind==="transient")}isEmpty(){return this.entries.every(t=>t.undoneAt!==void 0)}subscribe(t){this.listeners.add(t);return()=>this.listeners.delete(t)}clear(){this.entries=[];this.gen+=1;this.emit()}noteExternalRevert(t,n,e){let r=false;const i=s=>{for(const o of this.entries){if(!o.undoneAt&&s(o)){o.undoneAt=Date.now();o.undoError=void 0;r=true}}};if(t==="network"&&n==="deleteOverrideRule"){i(s=>s.effect.kind==="network-override"&&s.effect.ruleId===e.id)}else if(t==="network"&&n==="clearOverrideRules"){i(s=>s.effect.kind==="network-override")}else if(t==="impersonate"&&n==="stopImpersonation"){i(s=>s.effect.kind==="impersonation")}else{i(s=>{const o=s.effect;if(o.kind!=="inverse-call"||o.toolId!==t||o.action!==n)return false;return Object.entries(o.params).every(([a,u])=>JSON.stringify(e[a]??(a==="enabled"?false:void 0))===JSON.stringify(u))})}if(r)this.touch();return r}emit(){for(const t of this.listeners){try{t()}catch{}}}async revertOne(t,n){if(t.undoneAt)return void 0;const e=t.effect;try{switch(e.kind){case"network-override":await n("network","deleteOverrideRule",{id:e.ruleId});break;case"impersonation":await n("impersonate","stopImpersonation",{});break;case"storage-write":{const r=e.instanceId?{instanceId:e.instanceId}:{};if(e.wrote!==void 0){const i=e.action==="mmkv.set"?"mmkv.get":"async.getItem";try{const s=await n(e.toolId,i,{...r,key:e.key});const o=s&&typeof s==="object"?s:void 0;const a=o?o.value:s;if(o?.found!==false&&a!==void 0&&a!==e.wrote){return"This value was changed again after Ask Buoy wrote it \u2014 left the newer value alone"}}catch{}}if(e.before===void 0){if(!e.removeAction)return"No remove action known for this key";await n(e.toolId,e.removeAction,{...r,key:e.key})}else{await n(e.toolId,e.action,{...r,key:e.key,value:e.before})}break}case"query-write":{if(e.wroteDigest!==void 0){try{const i=await n("query","getQueryData",{queryHash:e.queryHash});if(i?.found===true){const s=i.hasData===false?void 0:(0,_digest.digestOf)(i.data);if(s!==e.wroteDigest){return"The app has refetched or changed this query since Ask Buoy wrote it \u2014 left the newer data alone"}}}catch{}}const r=await(e.before===void 0?n("query","invalidate",{queryHash:e.queryHash}):n("query","setQueryData",{queryHash:e.queryHash,data:e.before,force:true}));if(r&&typeof r==="object"&&r.ok===false){return typeof r.error==="string"?r.error:"The query could not be restored"}break}case"inverse-call":{const r=await n(e.toolId,e.action,e.params);if(r&&typeof r==="object"&&r.ok===false){return typeof r.error==="string"?r.error:"The change could not be undone"}break}case"state-write":return"This change can't be undone automatically";case"transient":return void 0}t.undoneAt=Date.now();t.undoError=void 0;this.touch();return void 0}catch(r){const i=r instanceof Error?r.message:String(r);t.undoError=i;this.touch();return i}}async revertAll(t){const n={reverted:0,failed:[],skippedTransients:0,permanent:0};const e=[...this.entries].filter(r=>!r.undoneAt).reverse();for(const r of e){if(r.effect.kind==="transient"){n.skippedTransients+=1;continue}if(!isReversible(r.effect)){n.permanent+=1;continue}const i=await this.revertOne(r,t);if(i)n.failed.push({label:r.effect.label,error:i});else n.reverted+=1}this.emit();return n}}exports.EffectLedger=EffectLedger;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.EffectLedger=void 0;exports.isReversible=isReversible;var _digest=require("./digest");var _storeDiff=require("./storeDiff");function isReversible(f){return f.kind!=="transient"&&f.kind!=="state-write"}let seq=0;class EffectLedger{entries=[];listeners=new Set;gen=0;get generation(){return this.gen}record(r,n,e,t){if(t!==void 0&&t!==this.gen)return void 0;const i={id:`fx-${++seq}-${Date.now()}`,at:Date.now(),toolId:r,action:n,effect:e};this.entries=[...this.entries,i];this.emit();return i}list(){return this.entries}liveCallIds(){const r=new Set;for(const n of this.entries)if(!n.undoneAt&&n.callId)r.add(n.callId);return r}restore(r){if(this.entries.length>0||r.length===0)return false;this.entries=[...r];this.emit();return true}touch(){this.entries=[...this.entries];this.emit()}reversible(){return this.entries.filter(r=>!r.undoneAt&&isReversible(r.effect))}transients(){return this.entries.filter(r=>!r.undoneAt&&r.effect.kind==="transient")}isEmpty(){return this.entries.every(r=>r.undoneAt!==void 0)}subscribe(r){this.listeners.add(r);return()=>this.listeners.delete(r)}clear(){this.entries=[];this.gen+=1;this.emit()}noteExternalRevert(r,n,e){let t=false;const i=o=>{for(const s of this.entries){if(!s.undoneAt&&o(s)){s.undoneAt=Date.now();s.undoError=void 0;t=true}}};if(r==="network"&&n==="deleteOverrideRule"){i(o=>o.effect.kind==="network-override"&&o.effect.ruleId===e.id)}else if(r==="network"&&n==="clearOverrideRules"){i(o=>o.effect.kind==="network-override")}else if(r==="impersonate"&&n==="stopImpersonation"){i(o=>o.effect.kind==="impersonation")}else{i(o=>{const s=o.effect;if(s.kind!=="inverse-call"||s.toolId!==r||s.action!==n)return false;return Object.entries(s.params).every(([a,u])=>JSON.stringify(e[a]??(a==="enabled"?false:void 0))===JSON.stringify(u))})}if(t)this.touch();return t}emit(){for(const r of this.listeners){try{r()}catch{}}}async revertOne(r,n){if(r.undoneAt)return void 0;const e=r.effect;try{switch(e.kind){case"network-override":await n("network","deleteOverrideRule",{id:e.ruleId});break;case"impersonation":await n("impersonate","stopImpersonation",{});break;case"storage-write":{const t=e.instanceId?{instanceId:e.instanceId}:{};if(e.wrote!==void 0){const i=e.action==="mmkv.set"?"mmkv.get":"async.getItem";try{const o=await n(e.toolId,i,{...t,key:e.key});const s=o&&typeof o==="object"?o:void 0;const a=s?s.value:o;if(s?.found!==false&&a!==void 0&&a!==e.wrote){return"This value was changed again after Ask Buoy wrote it \u2014 left the newer value alone"}}catch{}}if(e.before===void 0){if(!e.removeAction)return"No remove action known for this key";await n(e.toolId,e.removeAction,{...t,key:e.key})}else{await n(e.toolId,e.action,{...t,key:e.key,value:e.before})}break}case"query-write":{if(e.wroteDigest!==void 0){try{const i=await n("query","getQueryData",{queryHash:e.queryHash});if(i?.found===true){const o=i.hasData===false?void 0:(0,_digest.digestOf)(i.data);if(o!==e.wroteDigest){return"The app has refetched or changed this query since Ask Buoy wrote it \u2014 left the newer data alone"}}}catch{}}const t=await(e.before===void 0?n("query","invalidate",{queryHash:e.queryHash}):n("query","setQueryData",{queryHash:e.queryHash,data:e.before,force:true}));if(t&&typeof t==="object"&&t.ok===false){return typeof t.error==="string"?t.error:"The query could not be restored"}break}case"store-write":{let t;try{const o=await n(e.toolId,"getStoreState",{storeName:e.storeName});t=o?.found===true?o.currentState:void 0}catch{t=void 0}if(!t||typeof t!=="object"){return"Couldn't read the store to check for newer changes, so nothing was undone"}{const o=e.leaves.filter(s=>{const a=(0,_storeDiff.readLeaf)(t,s.path);return!a.found||(0,_digest.digestOf)(a.value)!==s.wroteDigest});if(o.length){return`${o.map(s=>s.path.join(".")).join(", ")} changed again after Ask Buoy wrote it \u2014 left the newer value alone`}}const i=await n(e.toolId,"setState",{storeName:e.storeName,state:(0,_storeDiff.restorePatch)(e.leaves),replace:false,force:true});if(i&&typeof i==="object"&&i.ok===false){return typeof i.error==="string"?i.error:"The store could not be restored"}break}case"inverse-call":{const t=await n(e.toolId,e.action,e.params);if(t&&typeof t==="object"&&t.ok===false){return typeof t.error==="string"?t.error:"The change could not be undone"}break}case"state-write":return"This change can't be undone automatically";case"transient":return void 0}r.undoneAt=Date.now();r.undoError=void 0;this.touch();return void 0}catch(t){const i=t instanceof Error?t.message:String(t);r.undoError=i;this.touch();return i}}async revertAll(r){const n={reverted:0,failed:[],skippedTransients:0,permanent:0};const e=[...this.entries].filter(t=>!t.undoneAt).reverse();for(const t of e){if(t.effect.kind==="transient"){n.skippedTransients+=1;continue}if(!isReversible(t.effect)){n.permanent+=1;continue}const i=await this.revertOne(t,r);if(i)n.failed.push({label:t.effect.label,error:i});else n.reverted+=1}this.emit();return n}}exports.EffectLedger=EffectLedger;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.diffLeaves=diffLeaves;exports.readLeaf=readLeaf;exports.restorePatch=restorePatch;var _digest=require("./digest");const isPlainRecord=o=>typeof o==="object"&&o!==null&&!Array.isArray(o);function diffLeaves(o,n){const e=[];const t=(r,s,u)=>{if(isPlainRecord(r)&&isPlainRecord(s)){const a=Object.keys(r);const i=Object.keys(s);if(a.length!==i.length||i.some(f=>!(f in r)))return false;return i.every(f=>t(r[f],s[f],[...u,f]))}const c=(0,_digest.digestOf)(s);if((0,_digest.digestOf)(r)!==c)e.push({path:u,before:r,wroteDigest:c});return true};return t(o,n,[])?e:void 0}function readLeaf(o,n){let e=o;for(const t of n){if(!isPlainRecord(e)||!(t in e))return{found:false};e=e[t]}return{found:true,value:e}}function restorePatch(o){const n={};for(const e of o){let t=n;e.path.forEach((r,s)=>{if(s===e.path.length-1)t[r]=e.before;else t=t[r]=isPlainRecord(t[r])?t[r]:{}})}return n}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.compactScreen=compactScreen;const ICON_ONLY=/^[\s-]*$/;const words=o=>o.label||o.text||"";function compactScreen(o){const s=o;if(!s||!Array.isArray(s.elements))return o;const a=new Map;for(const e of s.elements){const n=JSON.stringify([e.role,e.name.split("(")[0],words(e),e.testID,e.control,e.frame]);const t=a.get(n);if(!t||(e.nativeTag??0)>(t.nativeTag??0))a.set(n,e)}const l=[...a.values()].sort((e,n)=>(n.nativeTag??0)-(e.nativeTag??0));const i=[];let r;for(const e of l){const n=words(e);if(!e.testID&&!e.control&&ICON_ONLY.test(n))continue;if(e.name==="Text"&&!e.testID&&r&&r.nativeTag!==null&&e.nativeTag!==null&&r.nativeTag-e.nativeTag<=12&&words(r).includes(n)){continue}const t=[String(e.nativeTag),e.role??e.name.split("(")[0],JSON.stringify(e.label&&e.text&&e.text!==e.label?`${e.label} / ${e.text}`:n)];if(e.testID)t.push(`testID=${e.testID}`);if(e.control)t.push(`${e.control}=${JSON.stringify(e.value)}`);if(e.longPressable)t.push("longPress");if(!e.interactive)t.push("not tappable");if(e.frame&&(e.frame.y>=1||e.frame.y+e.frame.height<=0))t.push("off-screen");i.push(t.join(" \xB7 "));r=e}return{screen:s.screen,count:i.length,...s.hiddenBuoy?{hiddenBuoy:s.hiddenBuoy}:{},...s.lists?.length?{lists:s.lists.map(e=>`${e.owner??"A list"} holds ${e.items} items; only ${e.listed} are listed above (the rest are further down). Count items from the data behind the list (its query or store), not from these rows.`)}:{},format:"nativeTag \xB7 role or component \xB7 label/text \xB7 extras. Newest first: the screen opened last is at the top, screens still mounted beneath it follow. Tap with tapElement {nativeTag}; off-screen rows scroll into view on tap.",elements:i}}
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.effectFor=effectFor;var _policy=require("../policy/policy");var _digest=require("../effects/digest");const MAX_UNDO_JSON_CHARS=64e3;const cacheRead=n=>n&&typeof n==="object"?n:void 0;const QUERY_RESTORES={setQueryData:"query.invalidate",triggerError:"query.restoreError",triggerLoading:"query.restoreLoading",setOnline:"query.setOnline"};function effectFor(n,t,i,l,u,c){const r=(0,_policy.describeCall)(n,t,i);const d=l??{};if(n==="network"&&t.action==="upsertOverrideRule"){const e=d.rule;const a=e?.id??d.id;const s=i.id===void 0;if(a&&s)return{kind:"network-override",ruleId:a,label:r};return{kind:"state-write",toolId:n,action:t.action,label:r}}if(n==="impersonate"&&t.action==="startImpersonation"){return{kind:"impersonation",label:`${r} \u2014 stopping also clears cached app data`}}if(n==="storage"&&typeof i.key==="string"&&u){const e=t.action.startsWith("mmkv.");const a=e?"mmkv.set":"async.setItem";return{kind:"storage-write",toolId:n,action:a,key:i.key,instanceId:e&&typeof i.instanceId==="string"?i.instanceId:void 0,before:u.had?u.value:void 0,wrote:typeof i.value==="string"?i.value:void 0,removeAction:e?"mmkv.remove":"async.removeItem",label:r}}if(n==="query"&&t.action==="setQueryData"){const e=cacheRead(c?.before);const a=typeof i.queryHash==="string"?i.queryHash:e?.queryHash;if(e?.found===true&&typeof a==="string"){const s=e.hasData!==false&&e.data!==void 0;const f=s?e.data:void 0;if((0,_digest.jsonLength)(f)<=MAX_UNDO_JSON_CHARS){const o=cacheRead(c?.after);const y=o?.found===true&&o.hasData!==false?(0,_digest.digestOf)(o.data):void 0;return{kind:"query-write",queryHash:a,before:f,wroteDigest:y,label:r}}return{kind:"state-write",toolId:n,action:t.action,label:`${r} \u2014 prior value too large to hold for undo; undo with query.invalidate`}}}if(n==="query"&&typeof i.queryHash==="string"){const e=t.action==="triggerLoading"?"restoreLoading":t.action==="triggerError"?"restoreError":void 0;if(e){return{kind:"inverse-call",toolId:n,action:e,params:{queryHash:i.queryHash},label:r}}}if(n==="query"&&["refetch","invalidate","restoreLoading"].includes(t.action)){return{kind:"transient",label:r}}if(n==="network"&&(t.action==="setOverrideRuleEnabled"||t.action==="setOverridesEnabled")&&u?.had&&(u.value==="true"||u.value==="false")){const e=u.value==="true";const a=i.enabled===true;if(e===a)return void 0;const s=t.action==="setOverrideRuleEnabled"?{id:i.id,enabled:e}:{enabled:e};return{kind:"inverse-call",toolId:n,action:t.action,params:s,label:r}}if(n==="query"&&t.effect!=="read"){const e=QUERY_RESTORES[t.action];return{kind:"state-write",toolId:n,action:t.action,label:e?`${r} \u2014 undo with ${e}`:r}}if(t.effect==="destructive"){return{kind:"state-write",toolId:n,action:t.action,label:r}}return{kind:"transient",label:r}}
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.effectFor=effectFor;var _policy=require("../policy/policy");var _digest=require("../effects/digest");var _storeDiff=require("../effects/storeDiff");const MAX_UNDO_JSON_CHARS=64e3;const cacheRead=t=>t&&typeof t==="object"?t:void 0;const QUERY_RESTORES={setQueryData:"query.invalidate",triggerError:"query.restoreError",triggerLoading:"query.restoreLoading",setOnline:"query.setOnline"};function effectFor(t,n,r,g,f,d){const i=(0,_policy.describeCall)(t,n,r);const y=g??{};if(t==="network"&&n.action==="upsertOverrideRule"){const e=y.rule;const a=e?.id??y.id;const o=r.id===void 0;if(a&&o)return{kind:"network-override",ruleId:a,label:i};return{kind:"state-write",toolId:t,action:n.action,label:i}}if(t==="impersonate"&&n.action==="startImpersonation"){return{kind:"impersonation",label:`${i} \u2014 stopping also clears cached app data`}}if(t==="storage"&&typeof r.key==="string"&&f){const e=n.action.startsWith("mmkv.");const a=e?"mmkv.set":"async.setItem";return{kind:"storage-write",toolId:t,action:a,key:r.key,instanceId:e&&typeof r.instanceId==="string"?r.instanceId:void 0,before:f.had?f.value:void 0,wrote:typeof r.value==="string"?r.value:void 0,removeAction:e?"mmkv.remove":"async.removeItem",label:i}}if(t==="query"&&n.action==="setQueryData"){const e=cacheRead(d?.before);const a=typeof r.queryHash==="string"?r.queryHash:e?.queryHash;if(e?.found===true&&typeof a==="string"){const o=e.hasData!==false&&e.data!==void 0;const s=o?e.data:void 0;if((0,_digest.jsonLength)(s)<=MAX_UNDO_JSON_CHARS){const u=cacheRead(d?.after);const c=u?.found===true&&u.hasData!==false?(0,_digest.digestOf)(u.data):void 0;return{kind:"query-write",queryHash:a,before:s,wroteDigest:c,label:i}}return{kind:"state-write",toolId:t,action:n.action,label:`${i} \u2014 prior value too large to hold for undo; undo with query.invalidate`}}}if(t==="zustand"&&n.action==="setState"&&typeof r.storeName==="string"){const e=u=>{const c=u&&typeof u==="object"?u:void 0;const l=c?.found===true?c.currentState:void 0;return l&&typeof l==="object"&&!Array.isArray(l)?l:void 0};const a=e(d?.before);const o=e(d?.after);const s=a&&o?(0,_storeDiff.diffLeaves)(a,o):void 0;if(s&&s.length===0)return void 0;if(s&&s.every(u=>u.path.length>0)&&(0,_digest.jsonLength)(s)<=MAX_UNDO_JSON_CHARS){return{kind:"store-write",toolId:t,storeName:r.storeName,leaves:s,label:i}}return{kind:"state-write",toolId:t,action:n.action,label:s?`${i} \u2014 too large to hold for undo`:`${i} \u2014 added or removed fields, so it can't be undone exactly`}}if(t==="query"&&typeof r.queryHash==="string"){const e=n.action==="triggerLoading"?"restoreLoading":n.action==="triggerError"?"restoreError":void 0;if(e){return{kind:"inverse-call",toolId:t,action:e,params:{queryHash:r.queryHash},label:i}}}if(t==="query"&&["refetch","invalidate","restoreLoading"].includes(n.action)){return{kind:"transient",label:i}}if(t==="network"&&(n.action==="setOverrideRuleEnabled"||n.action==="setOverridesEnabled")&&f?.had&&(f.value==="true"||f.value==="false")){const e=f.value==="true";const a=r.enabled===true;if(e===a)return void 0;const o=n.action==="setOverrideRuleEnabled"?{id:r.id,enabled:e}:{enabled:e};return{kind:"inverse-call",toolId:t,action:n.action,params:o,label:i}}if(t==="query"&&n.effect!=="read"){const e=QUERY_RESTORES[n.action];return{kind:"state-write",toolId:t,action:n.action,label:e?`${i} \u2014 undo with ${e}`:i}}if(n.effect==="destructive"){return{kind:"state-write",toolId:t,action:n.action,label:i}}return{kind:"transient",label:i}}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.SILENT_NUDGE=exports.PLAN_ONLY_NUDGE=void 0;exports.announcesUnstartedWork=announcesUnstartedWork;const PLAN_ONLY_NUDGE=exports.PLAN_ONLY_NUDGE="[Buoy, not the user: your last message said what you would do next but called no tool, so nothing ran and the turn would end there. If the task is not finished, make the next call now. If you are done, say so. If the user has to pick or answer, send the buoy_ui block (choice, confirm or input) now \u2014 text alone gives them nothing to tap.]";const SILENT_NUDGE=exports.SILENT_NUDGE="[Buoy, not the user: you ended the turn without a word to the user. Tell them in a sentence or two what you found or did \u2014 a card alone does not say it.]";const WAITS_ON_USER=/\b(if you|once you|when you|after you|only if|let me know|would you like|do you want|shall i|should i|tap|choose|approve)\b/i;const POINTS_AT_MISSING_CONTROL=/\b(?:tap|press|click|choose|select)\s+\**["“]?(?:go|approve|yes|continue|start|confirm)\b|ready for your (?:approval|go-ahead|ok)|(?:plan|steps|card|options?) (?:above|below)\b|approve the plan|for your (?:approval|go-ahead)|here['’]s the (?:plan|flow|steps)\b|\b(?:choose|pick|select) (?:what|which|one|from)\b[^.?!]*:/i;const ASKS_TO_PICK=/\b(?:pick|choose|select|tell me) (?:what|which)\b/i;const ANNOUNCES=/\b(i['’]ll|i will|let me|i['’]m going to|i am going to|next,? i|now i['’]ll|i['’]m about to)\b/i;const IN_PROGRESS=/\bi['’]m (?:now )?(?!sorry\b)[a-z]+ing\b/i;function announcesUnstartedWork(s){const t=s.trim();if(POINTS_AT_MISSING_CONTROL.test(t))return true;if(ASKS_TO_PICK.test(t))return true;if(/:\s*$/.test(t))return true;if(!t||/\?\s*$/.test(t))return false;const o=t.split(/(?<=[.!*])\s*(?=[A-Z“"])/);const e=o.slice(-2).join(" ");if(IN_PROGRESS.test(o.at(-1)??"")&&!WAITS_ON_USER.test(e))return true;return ANNOUNCES.test(e)&&!WAITS_ON_USER.test(e)}
@@ -1,12 +1,12 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"digestOf",{enumerable:true,get:function(){return _digest.digestOf}});exports.readTargetDigest=readTargetDigest;exports.runAgentTurn=runAgentTurn;exports.runGatedAction=runGatedAction;var _catalog=require("../catalog/catalog.g");var _validateParams=require("../catalog/validateParams");var _normalizeParams=require("../catalog/normalizeParams");var _toProviderTools=require("../catalog/toProviderTools");var _snapshotReads=require("../catalog/snapshotReads");var _ledger=require("../effects/ledger");var _policy=require("../policy/policy");var _redact=require("../policy/redact");var _effectFor=require("./effectFor");var _digest=require("../effects/digest");var _uiTool=require("../blocks/uiTool");var _receipts=require("../blocks/receipts");var _types=require("../blocks/types");var _askGate=require("./askGate");var _textToolCalls=require("./textToolCalls");var _historyBudget=require("./historyBudget");var _tokenCalibration=require("./tokenCalibration");var _evidence=require("./evidence");var _retrieve=require("./retrieve");var _verify=require("./verify");var _realNow=require("../realNow");const MAX_RESULT_CHARS=24e3;const MAX_TRACE_RESULT_CHARS=2e3;const LOOP_REPEATS=3;function callSignature(t){const r=n=>{if(n===null||typeof n!=="object")return n;if(Array.isArray(n))return n.map(r);const l=n;return Object.keys(l).sort().reduce((c,b)=>(c[b]=r(l[b]),c),{})};return t.map(n=>`${n.name}:${JSON.stringify(r(n.input??{}))}`).join("|")}const LOOP_WARNING="[Buoy] You have now made this exact call three times in a row and gotten the same thing back. Repeating it again will not change the answer. Read the result you already have, then either try a different tool or different arguments, or tell the user plainly what you could not get and what you tried.";const DEFAULT_MAX_STEPS=12;const DEFAULT_TURN_MS=18e4;const THROTTLE_RETRIES=2;const OVERFLOW_RETRIES=1;const RETRY_BASE_MS=1e3;const RETRY_JITTER_MS=250;const RETRY_AFTER_CAP_MS=3e4;function sleep(t,r){return new Promise(n=>{if(r?.aborted)return n();const l=(0,_realNow.realSetTimeout)(c,t);function c(){(0,_realNow.realClearTimeout)(l);r?.removeEventListener("abort",c);n()}r?.addEventListener("abort",c,{once:true})})}function readAnswer(t){if(typeof t==="boolean")return{approved:t,trust:false};const r=t.reason?.trim();return{approved:t.approved,...r?{reason:r}:{},trust:t.trust===true&&t.approved}}function declinedResult(t){return t?`The user declined this change and said: "${t}". Do not retry it as proposed; take their note into account and, if a different change would fit, propose that instead.`:"The user declined this change. Do not retry it; ask what they would prefer."}function findDescriptor(t,r,n){return t.find(l=>l.toolId===r)?.actions.find(l=>l.action===n)}const URL_PROVENANCE_REJECTION="Rejected: every image URL must be one you read from a tool result in THIS conversation (images.list, a storage value, a response body). Never invent or remember URLs \u2014 read them first, then show them.";const droppedImageNote=t=>` NOTE: ${t} image ${t===1?"URL was":"URLs were"} dropped \u2014 they were not read from a tool result in this conversation, so the card the user is looking at has no picture there. Do not tell them it does; read the URLs and show it again, or say the artwork is missing.`;function fullText(t){try{return JSON.stringify(t??null)}catch{return String(t)}}function encodeResult(t,r){if(t.length<=MAX_RESULT_CHARS)return t;return`${t.slice(0,MAX_RESULT_CHARS)}${(0,_evidence.truncationMarker)(t.length,r)}`}function summarise(t){if(t===null||t===void 0)return"Done";if(Array.isArray(t))return countOf(t.length);if(typeof t==="boolean")return t?"Done":"Failed";if(typeof t==="object"){const r=t;if(r.ok===false)return typeof r.error==="string"?r.error:"Failed";if(r.found===false)return"Not found";if(r.tapped===false)return"Nothing to tap";if(r.active===false)return"Saved, but inactive in this build";for(const l of["returned","count","total"]){const c=r[l];if(typeof c==="number")return countOf(c)}const n=Object.values(r).filter(Array.isArray);if(n.length===1)return countOf(n[0].length);return"Done"}return String(t).slice(0,80)}function countOf(t){return t===1?"1 item":`${t} items`}function reportsFailure(t){return typeof t==="object"&&t!==null&&t.ok===false}async function*runAgentTurn(t){const{provider:r,catalog:n,system:l,systemVolatile:c,model:b,maxTokens:m,dispatch:u,readSnapshot:y,storeKeyOwners:H,ledger:v,policy:q,isRelease:P,requestApproval:L,signal:E,evidence:U,trusted:w,procedures:D}=t;const j=new Map;const $=t.calibration??new _tokenCalibration.TokenCalibration;const p=[...t.messages];const te=(0,_toProviderTools.toProviderTools)(n,{availableToolIds:t.availableToolIds,availableActions:t.availableActions,isRelease:P});te.push((0,_uiTool.uiProviderTool)());const ue=q.maxSteps??DEFAULT_MAX_STEPS;const Oe=v.generation;const fe=l.length+(c?.length??0)+te.reduce((Q,C)=>Q+C.description.length+JSON.stringify(C.inputSchema).length,0);const pe=()=>fe+$.chars(m);let K=(0,_realNow.realNow)()+DEFAULT_TURN_MS;const oe={throttled:0,overflow:0};let V=_historyBudget.MAX_HISTORY_TOKENS;const re=t.seenUrls??new Set;const qe=/https?:\/\/[^\s"'\\)\]}>]+/g;let he=false;const B=[];let ye=false;const me=[];let ge=false;let be=false;for(let Q=0;Q<ue;Q++){if(E?.aborted){yield{type:"done",stopReason:"stopped"};return p}if((0,_realNow.realNow)()>K){yield{type:"block",block:{id:`cap${(0,_realNow.realNow)()}`,kind:"notice",tone:"warning",text:`This is taking too long \u2014 stopped after ${Math.round(DEFAULT_TURN_MS/1e3)}s. What it found so far is above.`,actions:[{label:"Continue",primary:true,send:"Continue where you left off."}]}};yield{type:"done",stopReason:"time-cap"};return p}const C=(0,_historyBudget.budgetForRequest)(p,pe(),$.chars(V),$.charsPerToken,v.liveCallIds());if(C.droppedRounds>0){p.length=0;p.push(...C.messages);yield{type:"history-trimmed",droppedRounds:C.droppedRounds,droppedPinned:C.droppedPinned}}else if(C.messages!==p){p.length=0;p.push(...C.messages)}let _="";let ke=false;const z=e=>{const o=he&&!ke;ke=true;he=true;return o?{type:"text",delta:e,breakBefore:true}:{type:"text",delta:e}};let k=[];const ve=new Set;let N="";let ne=true;let G=[];let X=false;let Z;let F;for(;;){_="";k=[];N="";ne=true;G=[];X=false;Z=void 0;F=void 0;let e;const o=fe+p.reduce((s,I)=>s+(0,_historyBudget.messageSize)(I),0);for await(const s of r.send({messages:p,system:l,systemVolatile:c,tools:te,model:b,maxTokens:m,signal:E})){if(s.type==="text"){_+=s.delta;if(!ne){yield z(s.delta)}else if((0,_textToolCalls.couldBeToolCallEnvelope)(_)){N+=s.delta}else{ne=false;N="";yield z(_)}}else if(s.type==="tool-call"){k.push(s.call)}else if(s.type==="thinking"){G.push(s.block);if(s.block.type==="thinking"&&s.block.thinking.trim()){yield{type:"reasoning",text:s.block.thinking}}}else if(s.type==="error"){if(s.kind==="truncated"||s.kind==="timeout"){F={message:s.message}}else if(s.problem&&(s.problem.kind==="throttled"||s.problem.kind==="overflow")&&_===""&&k.length===0&&G.length===0){e={problem:s.problem,message:s.message}}else{yield{type:"error",message:s.message};X=true}}else if(s.type==="done"){Z=s.outcome;if(s.usage){$.observe(o,(0,_tokenCalibration.promptTokensOf)(s.usage,r.protocol));yield{type:"usage",...s.usage,model:s.model}}}}if(!e)break;const i=e.problem.kind;const f=1+(i==="throttled"?THROTTLE_RETRIES:OVERFLOW_RETRIES);const d=oe[i];let A=i==="throttled"?Math.min(e.problem.retryAfterMs??RETRY_BASE_MS*2**d,RETRY_AFTER_CAP_MS)+Math.floor(Math.random()*RETRY_JITTER_MS):0;let S=d>=f-1||E?.aborted===true||(0,_realNow.realNow)()+A>K;if(!S&&i==="overflow"){V=Math.floor(V/2);const s=p.reduce((M,O)=>M+(0,_historyBudget.messageSize)(O),0);const I=(0,_historyBudget.budgetForRequest)(p,pe(),$.chars(V),$.charsPerToken,v.liveCallIds());const W=I.messages.reduce((M,O)=>M+(0,_historyBudget.messageSize)(O),0);if(W>=s){S=true}else{p.length=0;p.push(...I.messages);if(I.droppedRounds>0)yield{type:"history-trimmed",droppedRounds:I.droppedRounds,droppedPinned:I.droppedPinned}}}if(S){const s=i==="throttled"?d>0?`The AI endpoint is rate-limiting requests \u2014 tried ${d+1} times.`:"The AI endpoint is rate-limiting requests.":"This conversation is too large for the AI endpoint and there was nothing older to trim. Start a new conversation, or ask a shorter question.";yield{type:"error",message:`${s} (${e.message})`};X=true;break}oe[i]+=1;yield{type:"retrying",reason:i,attempt:oe[i]+1,maxAttempts:f,inMs:A};if(A>0)await sleep(A,E);if(E?.aborted){yield{type:"done",stopReason:"stopped"};return p}}if(X){if(N)yield z(N);yield{type:"done",stopReason:"error"};return p}if(!F&&Z!=="completed"){F={message:Z==="output-limited"?k.length?"The model ran out of room mid-plan, so nothing was run. What it found so far is above.":"The model ran out of room before finishing this answer.":"The answer ended before the endpoint said it was finished, so nothing was run."}}if(F){if(N)yield z(N);if(_.trim())p.push({role:"assistant",text:_});yield{type:"block",block:{id:`cut${(0,_realNow.realNow)()}`,kind:"notice",tone:"warning",text:F.message,actions:[{label:"Continue",primary:true,send:"Continue where you left off."}]}};yield{type:"done",stopReason:"incomplete"};return p}if(N){const e=k.length===0?(0,_textToolCalls.parseTextToolCalls)(N,n):void 0;if(e){for(const o of e.calls){k.push(o);ve.add(o.id)}_=e.reply;if(e.reply)yield z(e.reply)}else{yield z(N)}N=""}p.push({role:"assistant",text:_,toolCalls:k.length?k:void 0,thinking:G.length?G:void 0});if(_.trim())me.push(_.trim());if(k.length===0){const e=me.join("\n\n");const o=(0,_askGate.synthesizeShortfallBlock)({text:e,attempted:be});if(o)yield{type:"block",block:o};const i=ge||o?null:(0,_askGate.synthesizeAskBlock)(e);if(i){yield{type:"block",block:i};yield{type:"awaiting-user",blockId:i.id}}yield{type:"done",stopReason:i?"awaiting-user":"answered"};return p}const g=[];let we=false;const Te=new Map;if(k.length>1){const e=k.every(o=>{if(o.name===_uiTool.UI_TOOL_NAME)return false;const i=(0,_toProviderTools.fromProviderToolName)(o.name,n);if(!i||i==="ask-buoy")return false;const f=o.input?.action;if(typeof f!=="string"||f===_snapshotReads.SNAPSHOT_ACTION)return false;const d=findDescriptor(n,i,f);if(!d||d.effect!=="read")return false;return(0,_policy.decide)({descriptor:d,toolId:i,policy:q,isRelease:P}).verdict==="allow"});if(e){for(const o of k){const i=(0,_toProviderTools.fromProviderToolName)(o.name,n);const f=String(o.input.action);const d={...o.input.params??{}};const A=findDescriptor(n,i,f);const S=(0,_normalizeParams.normalizeParams)(i,f,d);const s=(0,_validateParams.applyDefaults)(S.params,A.params);if(!(0,_validateParams.validateParams)(s,A.params).ok)continue;const I=Promise.resolve(u(i,f,s)).catch(W=>{throw W});I.catch(()=>{});Te.set(o.id,I)}}}let ie=null;const Pe=e=>{const o=(0,_toProviderTools.fromProviderToolName)(e.name,n)??e.name;const i=typeof e.input?.action==="string"?e.input.action:"";const f=i?findDescriptor(n,o,i):void 0;const d=e.input?.params??{};return[{type:"tool-start",id:e.id,toolId:o,action:i,label:f?(0,_policy.describeCall)(o,f,d):`${o}.${i}`,description:f?.summary??"",params:d,effect:f?.effect??"read"},{type:"tool-end",id:e.id,ok:false,summary:"stopped",durationMs:0}]};const Re=k.map(e=>{if(e.name===_uiTool.UI_TOOL_NAME)return void 0;const o=(0,_toProviderTools.fromProviderToolName)(e.name,n);const i=typeof e.input?.action==="string"?e.input.action:void 0;if(!o||!i)return void 0;const f=findDescriptor(n,o,i);if(!f)return void 0;const d=e.input.params??{};const A=(0,_validateParams.applyDefaults)((0,_normalizeParams.normalizeParams)(o,i,d).params,f.params);if(!(0,_validateParams.validateParams)(A,f.params).ok)return void 0;const S=(0,_policy.decide)({descriptor:f,toolId:o,policy:q,isRelease:P});return{call:e,toolId:o,action:i,descriptor:f,params:A,label:(0,_policy.describeCall)(o,f,A),mutates:f.effect!=="read",gated:S.verdict==="needs-approval",reason:S.verdict==="needs-approval"?S.reason:""}});const xe=Re.some(e=>e?.gated);const x=new Map;let Ae=false;let Ie=false;async function*Me(){Ae=true;for(const e of Re){if(!e?.gated||x.has(e.call.id))continue;if(w?.has(`${e.toolId}.${e.action}`)){x.set(e.call.id,true);continue}yield{type:"approval-required",id:e.call.id,toolId:e.toolId,action:e.action,label:e.label,description:e.descriptor.summary,reason:e.reason};const o=(0,_realNow.realNow)();const i=await readTargetDigest({toolId:e.toolId,action:e.action,params:e.params,dispatch:u,catalog:n});const f=readAnswer(w?.has(`${e.toolId}.${e.action}`)?true:L?await L({id:e.call.id,toolId:e.toolId,action:e.action,label:e.label,description:e.descriptor.summary,reason:e.reason,params:e.params,targetDigest:i}):false);K+=(0,_realNow.realNow)()-o;const d=f.approved;x.set(e.call.id,d);if(f.trust)w?.add(`${e.toolId}.${e.action}`);if(f.reason)j.set(e.call.id,f.reason);if(!d){Ie=true;return}}}for(const e of k){if(E?.aborted){if(e.name!==_uiTool.UI_TOOL_NAME){for(const a of Pe(e))yield a}g.push({toolCallId:e.id,content:"Not run \u2014 the user pressed Stop before this call started.",isError:true});continue}if(e.name===_uiTool.UI_TOOL_NAME){const a=(0,_uiTool.buildUiBlock)(e.input);if(!a.ok||!a.block){g.push({toolCallId:e.id,content:`Invalid ${_uiTool.UI_TOOL_NAME} block:
2
- ${a.errors.join("\n")}
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});Object.defineProperty(exports,"digestOf",{enumerable:true,get:function(){return _digest.digestOf}});exports.readTargetDigest=readTargetDigest;exports.runAgentTurn=runAgentTurn;exports.runGatedAction=runGatedAction;var _catalog=require("../catalog/catalog.g");var _validateParams=require("../catalog/validateParams");var _normalizeParams=require("../catalog/normalizeParams");var _toProviderTools=require("../catalog/toProviderTools");var _snapshotReads=require("../catalog/snapshotReads");var _types=require("../providers/types");var _ledger=require("../effects/ledger");var _policy=require("../policy/policy");var _redact=require("../policy/redact");var _effectFor=require("./effectFor");var _digest=require("../effects/digest");var _uiTool=require("../blocks/uiTool");var _receipts=require("../blocks/receipts");var _types2=require("../blocks/types");var _askGate=require("./askGate");var _textToolCalls=require("./textToolCalls");var _planOnlyStop=require("./planOnlyStop");var _compactScreen=require("./compactScreen");var _historyBudget=require("./historyBudget");var _tokenCalibration=require("./tokenCalibration");var _evidence=require("./evidence");var _retrieve=require("./retrieve");var _verify=require("./verify");var _realNow=require("../realNow");const MAX_RESULT_CHARS=24e3;const MAX_TRACE_RESULT_CHARS=2e3;const LOOP_REPEATS=3;function callSignature(t){const o=n=>{if(n===null||typeof n!=="object")return n;if(Array.isArray(n))return n.map(o);const c=n;return Object.keys(c).sort().reduce((u,T)=>(u[T]=o(c[T]),u),{})};return t.map(n=>`${n.name}:${JSON.stringify(o(n.input??{}))}`).join("|")}const LOOP_WARNING="[Buoy] You have now made this exact call three times in a row and gotten the same thing back. Repeating it again will not change the answer. Read the result you already have, then either try a different tool or different arguments, or tell the user plainly what you could not get and what you tried.";const DEFAULT_MAX_STEPS=24;const DEFAULT_TURN_MS=18e4;const THROTTLE_RETRIES=2;const OVERFLOW_RETRIES=1;const RETRY_BASE_MS=1e3;const RETRY_JITTER_MS=250;const RETRY_AFTER_CAP_MS=3e4;function sleep(t,o){return new Promise(n=>{if(o?.aborted)return n();const c=(0,_realNow.realSetTimeout)(u,t);function u(){(0,_realNow.realClearTimeout)(c);o?.removeEventListener("abort",u);n()}o?.addEventListener("abort",u,{once:true})})}function readAnswer(t){if(typeof t==="boolean")return{approved:t,trust:false};const o=t.reason?.trim();return{approved:t.approved,...o?{reason:o}:{},trust:t.trust===true&&t.approved}}function declinedResult(t){return t?`The user declined this change and said: "${t}". Do not retry it as proposed; take their note into account and, if a different change would fit, propose that instead.`:"The user declined this change. Do not retry it; ask what they would prefer."}function findDescriptor(t,o,n){return t.find(c=>c.toolId===o)?.actions.find(c=>c.action===n)}const URL_PROVENANCE_REJECTION="Rejected: every image URL must be one you read from a tool result in THIS conversation (images.list, a storage value, a response body). Never invent or remember URLs \u2014 read them first, then show them.";const droppedImageNote=t=>` NOTE: ${t} image ${t===1?"URL was":"URLs were"} dropped \u2014 they were not read from a tool result in this conversation, so the card the user is looking at has no picture there. Do not tell them it does; read the URLs and show it again, or say the artwork is missing.`;function fullText(t){try{return JSON.stringify(t??null)}catch{return String(t)}}function encodeResult(t,o){if(t.length<=MAX_RESULT_CHARS)return t;return`${t.slice(0,MAX_RESULT_CHARS)}${(0,_evidence.truncationMarker)(t.length,o)}`}function summarise(t){if(t===null||t===void 0)return"Done";if(Array.isArray(t))return countOf(t.length);if(typeof t==="boolean")return t?"Done":"Failed";if(typeof t==="object"){const o=t;if(o.ok===false)return typeof o.error==="string"?o.error:"Failed";if(o.found===false)return"Not found";if(o.tapped===false)return"Nothing to tap";if(o.active===false)return"Saved, but inactive in this build";for(const c of["returned","count","total"]){const u=o[c];if(typeof u==="number")return countOf(u)}const n=Object.values(o).filter(Array.isArray);if(n.length===1)return countOf(n[0].length);return"Done"}return String(t).slice(0,80)}function countOf(t){return t===1?"1 item":`${t} items`}function reportsFailure(t){return typeof t==="object"&&t!==null&&t.ok===false}async function*runAgentTurn(t){const{provider:o,catalog:n,system:c,systemVolatile:u,model:T,maxTokens:g,dispatch:a,readSnapshot:y,storeKeyOwners:E,ledger:v,policy:P,isRelease:q,requestApproval:U,signal:$,evidence:D,trusted:A,procedures:j}=t;const B=new Map;const C=t.calibration??new _tokenCalibration.TokenCalibration;const h=[...t.messages];const ne=(0,_toProviderTools.toProviderTools)(n,{availableToolIds:t.availableToolIds,availableActions:t.availableActions,isRelease:q});ne.push((0,_uiTool.uiProviderTool)());const V=P.maxSteps??DEFAULT_MAX_STEPS;const Le=v.generation;const me=c.length+(u?.length??0)+ne.reduce((x,O)=>x+O.description.length+JSON.stringify(O.inputSchema).length,0);const ge=()=>me+C.chars(g);let X=(0,_realNow.realNow)()+DEFAULT_TURN_MS;const ie={throttled:0,overflow:0};let Z=_historyBudget.MAX_HISTORY_TOKENS;const se=t.seenUrls??new Set;const Me=/https?:\/\/[^\s"'\\)\]}>]+/g;let be=false;const z=[];let ke=false;const ae=[];let de=false;let ve=false;let ee=false;let we=false;let Te=false;const Ue=[...h].reverse().find(x=>x.role==="user");const De=/\?\s*$/.test(Ue?.text?.trim()??"");for(let x=0;x<V;x++){if($?.aborted){yield{type:"done",stopReason:"stopped"};return h}if((0,_realNow.realNow)()>X){yield{type:"block",block:{id:`cap${(0,_realNow.realNow)()}`,kind:"notice",tone:"warning",text:`This is taking too long \u2014 stopped after ${Math.round(DEFAULT_TURN_MS/1e3)}s. What it found so far is above.`,actions:[{label:"Continue",primary:true,send:"Continue where you left off."}]}};yield{type:"done",stopReason:"time-cap"};return h}const O=(0,_historyBudget.budgetForRequest)(h,ge(),C.chars(Z),C.charsPerToken,v.liveCallIds());if(O.droppedRounds>0){h.length=0;h.push(...O.messages);yield{type:"history-trimmed",droppedRounds:O.droppedRounds,droppedPinned:O.droppedPinned}}else if(O.messages!==h){h.length=0;h.push(...O.messages)}let I="";let Se=false;const F=e=>{const d=be&&!Se;Se=true;be=true;return d?{type:"text",delta:e,breakBefore:true}:{type:"text",delta:e}};let w=[];const Ae=new Set;let N="";let le=true;let W=[];let te=false;let re;let G;for(;;){I="";w=[];N="";le=true;W=[];te=false;re=void 0;G=void 0;let e;const d=me+h.reduce((l,p)=>l+(0,_historyBudget.messageSize)(p),0);for await(const l of o.send({messages:h,system:c,systemVolatile:u,tools:ne,model:T,maxTokens:g,signal:$})){if(l.type==="text"){I+=l.delta;if(!le){yield F(l.delta)}else if((0,_textToolCalls.couldBeToolCallEnvelope)(I)){N+=l.delta}else{le=false;N="";yield F(I)}}else if(l.type==="tool-call"){w.push(l.call)}else if(l.type==="thinking"){W.push(l.block);if(l.block.type==="thinking"&&l.block.thinking.trim()){yield{type:"reasoning",text:l.block.thinking}}}else if(l.type==="error"){if(l.kind==="truncated"||l.kind==="timeout"){G={message:l.message}}else if(l.problem&&(l.problem.kind==="throttled"||l.problem.kind==="overflow")&&I===""&&w.length===0&&W.length===0){e={problem:l.problem,message:l.message}}else{yield{type:"error",message:l.message};te=true}}else if(l.type==="done"){re=l.outcome;if(l.usage){C.observe(d,(0,_tokenCalibration.promptTokensOf)(l.usage,o.protocol));yield{type:"usage",...l.usage,model:l.model}}}}if(!e)break;const r=e.problem.kind;const i=1+(r==="throttled"?THROTTLE_RETRIES:OVERFLOW_RETRIES);const b=ie[r];let f=r==="throttled"?Math.min(e.problem.retryAfterMs??RETRY_BASE_MS*2**b,RETRY_AFTER_CAP_MS)+Math.floor(Math.random()*RETRY_JITTER_MS):0;let _=b>=i-1||$?.aborted===true||(0,_realNow.realNow)()+f>X;if(!_&&r==="overflow"){Z=Math.floor(Z/2);const l=h.reduce((J,M)=>J+(0,_historyBudget.messageSize)(M),0);const p=(0,_historyBudget.budgetForRequest)(h,ge(),C.chars(Z),C.charsPerToken,v.liveCallIds());const H=p.messages.reduce((J,M)=>J+(0,_historyBudget.messageSize)(M),0);if(H>=l){_=true}else{h.length=0;h.push(...p.messages);if(p.droppedRounds>0)yield{type:"history-trimmed",droppedRounds:p.droppedRounds,droppedPinned:p.droppedPinned}}}if(_){const l=r==="throttled"?b>0?`The AI endpoint is rate-limiting requests \u2014 tried ${b+1} times.`:"The AI endpoint is rate-limiting requests.":"This conversation is too large for the AI endpoint and there was nothing older to trim. Start a new conversation, or ask a shorter question.";yield{type:"error",message:`${l} (${e.message})`};te=true;break}ie[r]+=1;yield{type:"retrying",reason:r,attempt:ie[r]+1,maxAttempts:i,inMs:f};if(f>0)await sleep(f,$);if($?.aborted){yield{type:"done",stopReason:"stopped"};return h}}if(te){if(N)yield F(N);yield{type:"done",stopReason:"error"};return h}if(!G&&re!=="completed"){G={message:re==="output-limited"?w.length?"The model ran out of room mid-plan, so nothing was run. What it found so far is above.":"The model ran out of room before finishing this answer.":"The answer ended before the endpoint said it was finished, so nothing was run."}}if(G){if(N)yield F(N);if(I.trim())h.push({role:"assistant",text:I});yield{type:"block",block:{id:`cut${(0,_realNow.realNow)()}`,kind:"notice",tone:"warning",text:G.message,actions:[{label:"Continue",primary:true,send:"Continue where you left off."}]}};yield{type:"done",stopReason:"incomplete"};return h}if(N){const e=w.length===0?(0,_textToolCalls.parseTextToolCalls)(N,n):void 0;if(e){for(const d of e.calls){w.push(d);Ae.add(d.id)}I=e.reply;if(e.reply)yield F(e.reply)}else{yield F(N)}N=""}h.push({role:"assistant",text:I,toolCalls:w.length?w:void 0,thinking:W.length?W:void 0});if(I.trim())ae.push(I.trim());if(w.length===0&&!ee&&!de&&x<V-1&&((0,_planOnlyStop.announcesUnstartedWork)(I)||we&&!Te&&!De)){ee=true;h.push({role:"user",text:_planOnlyStop.PLAN_ONLY_NUDGE});continue}if(w.length===0&&!ee&&ae.length===0&&x<V-1){ee=true;h.push({role:"user",text:_planOnlyStop.SILENT_NUDGE});continue}if(w.length===0){const e=ae.join("\n\n");const d=(0,_askGate.synthesizeShortfallBlock)({text:e,attempted:ve});if(d)yield{type:"block",block:d};const r=de||d?null:(0,_askGate.synthesizeAskBlock)(e);if(r){yield{type:"block",block:r};yield{type:"awaiting-user",blockId:r.id}}yield{type:"done",stopReason:r?"awaiting-user":"answered"};return h}const k=[];let Re=false;const Ie=new Map;if(w.length>1){const e=w.every(d=>{if(d.name===_uiTool.UI_TOOL_NAME)return false;const r=(0,_toProviderTools.fromProviderToolName)(d.name,n);if(!r||r==="ask-buoy")return false;const i=d.input?.action;if(typeof i!=="string"||i===_snapshotReads.SNAPSHOT_ACTION)return false;const b=findDescriptor(n,r,i);if(!b||b.effect!=="read")return false;return(0,_policy.decide)({descriptor:b,toolId:r,policy:P,isRelease:q}).verdict==="allow"});if(e){for(const d of w){const r=(0,_toProviderTools.fromProviderToolName)(d.name,n);const i=String(d.input.action);const b={...d.input.params??{}};const f=findDescriptor(n,r,i);const _=(0,_normalizeParams.normalizeParams)(r,i,b);const l=(0,_validateParams.applyDefaults)(_.params,f.params);if(!(0,_validateParams.validateParams)(l,f.params).ok)continue;const p=Promise.resolve(a(r,i,l)).catch(H=>{throw H});p.catch(()=>{});Ie.set(d.id,p)}}}let ce=null;const je=e=>{const d=(0,_toProviderTools.fromProviderToolName)(e.name,n)??e.name;const r=typeof e.input?.action==="string"?e.input.action:"";const i=r?findDescriptor(n,d,r):void 0;const b=e.input?.params??{};return[{type:"tool-start",id:e.id,toolId:d,action:r,label:i?(0,_policy.describeCall)(d,i,b):`${d}.${r}`,description:i?.summary??"",params:b,effect:i?.effect??"read"},{type:"tool-end",id:e.id,ok:false,summary:"stopped",durationMs:0}]};const Ne=w.map(e=>{if(e.name===_uiTool.UI_TOOL_NAME)return void 0;const d=(0,_toProviderTools.fromProviderToolName)(e.name,n);const r=typeof e.input?.action==="string"?e.input.action:void 0;if(!d||!r)return void 0;const i=findDescriptor(n,d,r);if(!i)return void 0;const b=e.input.params??{};const f=(0,_validateParams.applyDefaults)((0,_normalizeParams.normalizeParams)(d,r,b).params,i.params);if(!(0,_validateParams.validateParams)(f,i.params).ok)return void 0;const _=(0,_policy.decide)({descriptor:i,toolId:d,policy:P,isRelease:q});return{call:e,toolId:d,action:r,descriptor:i,params:f,label:(0,_policy.describeCall)(d,i,f),mutates:i.effect!=="read",gated:_.verdict==="needs-approval",reason:_.verdict==="needs-approval"?_.reason:""}});const Be=Ne.some(e=>e?.gated);const L=new Map;let _e=false;let Ee=false;async function*ze(){_e=true;for(const e of Ne){if(!e?.gated||L.has(e.call.id))continue;if(A?.has(`${e.toolId}.${e.action}`)){L.set(e.call.id,true);continue}yield{type:"approval-required",id:e.call.id,toolId:e.toolId,action:e.action,label:e.label,description:e.descriptor.summary,reason:e.reason};const d=(0,_realNow.realNow)();const r=await readTargetDigest({toolId:e.toolId,action:e.action,params:e.params,dispatch:a,catalog:n});const i=readAnswer(A?.has(`${e.toolId}.${e.action}`)?true:U?await U({id:e.call.id,toolId:e.toolId,action:e.action,label:e.label,description:e.descriptor.summary,reason:e.reason,params:e.params,targetDigest:r}):false);X+=(0,_realNow.realNow)()-d;const b=i.approved;L.set(e.call.id,b);if(i.trust)A?.add(`${e.toolId}.${e.action}`);if(i.reason)B.set(e.call.id,i.reason);if(!b){Ee=true;return}}}for(const e of w){if($?.aborted){if(e.name!==_uiTool.UI_TOOL_NAME){for(const s of je(e))yield s}k.push({toolCallId:e.id,content:"Not run \u2014 the user pressed Stop before this call started.",isError:true});continue}const d=e.input?.[_types.UNPARSEABLE_ARGS];if(typeof d==="string"){k.push({toolCallId:e.id,content:`Not run \u2014 this call's arguments were not valid JSON, so nothing changed (${d.slice(0,120)}). Send the call again with compact JSON: {"action": "...", "params": {...}}.`,isError:true});continue}if(e.name===_uiTool.UI_TOOL_NAME){const s=(0,_uiTool.buildUiBlock)(e.input);if(!s.ok||!s.block){k.push({toolCallId:e.id,content:`Invalid ${_uiTool.UI_TOOL_NAME} block:
2
+ ${s.errors.join("\n")}
3
3
 
4
- Resend ONLY the corrected block. Do not rewrite your answer \u2014 whatever you already said this turn has been shown to the user, and saying it again repeats it on their screen.`,isError:true});continue}let h=0;if(a.block.kind==="imageGrid"){const T=a.block.images.filter(R=>re.has(R.url));h=a.block.images.length-T.length;if(T.length===0){g.push({toolCallId:e.id,content:URL_PROVENANCE_REJECTION,isError:true});continue}a.block={...a.block,images:T}}if(a.block.kind==="list"){const T=a.block.items.map(R=>{if(!R.image||re.has(R.image))return R;h+=1;return{...R,image:void 0}});a.block={...a.block,items:T}}yield{type:"block",block:a.block,forToolCallId:e.id};if(a.block.kind==="actions"||a.block.kind==="suggestions")ge=true;if((0,_types.isAskBlock)(a.block)){ie=a.block.id;g.push({toolCallId:e.id,content:"Shown. Waiting for the user's answer \u2014 it will arrive as their next message. Do not guess it."})}else{g.push({toolCallId:e.id,content:"Shown to the user. Don't repeat its contents in text."+(h>0?droppedImageNote(h):"")})}continue}const o=(0,_toProviderTools.fromProviderToolName)(e.name,n);const i=e.input.action;const f=(a,h)=>[{type:"tool-start",id:e.id,toolId:o??e.name,action:i??"",label:a,description:"",params:e.input.params??{},effect:"read"},{type:"tool-end",id:e.id,ok:false,summary:h,durationMs:0}];if(!o){for(const a of f(e.name,"No such tool"))yield a;g.push({toolCallId:e.id,content:`There is no tool called "${e.name}". Use one of the tools you were given.`,isError:true});continue}if(!i){for(const a of f(e.name,"No action given"))yield a;g.push({toolCallId:e.id,content:`"${e.name}" needs an "action" \u2014 it is required, and it names which of this tool's actions to run. See the action list in the tool's description.`,isError:true});continue}const d=findDescriptor(n,o,i);if(!d){const a=n.find(h=>h.toolId===o)?.actions.map(h=>h.action).join(", ");for(const h of f(`${o}.${i}`,"No such action"))yield h;g.push({toolCallId:e.id,content:`"${i}" is not an action on ${o}.${a?` Valid actions: ${a}.`:""}`,isError:true});continue}const A=e.input.params??{};const S=(0,_normalizeParams.normalizeParams)(o,i,A);const s=(0,_validateParams.applyDefaults)(S.params,d.params);const I=(0,_validateParams.validateParams)(s,d.params);if(!I.ok){g.push({toolCallId:e.id,content:`Invalid parameters for ${o}.${i}:
5
- ${I.errors.join("\n")}
4
+ Resend ONLY the corrected block. Do not rewrite your answer \u2014 whatever you already said this turn has been shown to the user, and saying it again repeats it on their screen.`,isError:true});continue}let m=0;if(s.block.kind==="imageGrid"){const S=s.block.images.filter(R=>se.has(R.url));m=s.block.images.length-S.length;if(S.length===0){k.push({toolCallId:e.id,content:URL_PROVENANCE_REJECTION,isError:true});continue}s.block={...s.block,images:S}}if(s.block.kind==="list"){const S=s.block.items.map(R=>{if(!R.image||se.has(R.image))return R;m+=1;return{...R,image:void 0}});s.block={...s.block,items:S}}yield{type:"block",block:s.block,forToolCallId:e.id};if(s.block.kind==="actions"||s.block.kind==="suggestions"||s.block.kind==="plan"&&s.block.pending){de=true}if((0,_types2.isAskBlock)(s.block)){ce=s.block.id;k.push({toolCallId:e.id,content:"Shown. Waiting for the user's answer \u2014 it will arrive as their next message. Do not guess it."})}else{k.push({toolCallId:e.id,content:"Shown to the user. Don't repeat its contents in text."+(m>0?droppedImageNote(m):"")})}continue}const r=(0,_toProviderTools.fromProviderToolName)(e.name,n);const i=e.input.action;const b=(s,m)=>[{type:"tool-start",id:e.id,toolId:r??e.name,action:i??"",label:s,description:"",params:e.input.params??{},effect:"read"},{type:"tool-end",id:e.id,ok:false,summary:m,durationMs:0}];if(!r){for(const s of b(e.name,"No such tool"))yield s;k.push({toolCallId:e.id,content:`There is no tool called "${e.name}". Use one of the tools you were given.`,isError:true});continue}if(!i){for(const s of b(e.name,"No action given"))yield s;k.push({toolCallId:e.id,content:`"${e.name}" needs an "action" \u2014 it is required, and it names which of this tool's actions to run. See the action list in the tool's description.`,isError:true});continue}const f=findDescriptor(n,r,i);if(!f){const s=n.find(m=>m.toolId===r)?.actions.map(m=>m.action).join(", ");for(const m of b(`${r}.${i}`,"No such action"))yield m;k.push({toolCallId:e.id,content:`"${i}" is not an action on ${r}.${s?` Valid actions: ${s}.`:""}`,isError:true});continue}const _=e.input.params??{};const l=(0,_normalizeParams.normalizeParams)(r,i,_);const p=(0,_validateParams.applyDefaults)(l.params,f.params);const H=(0,_validateParams.validateParams)(p,f.params);if(!H.ok){k.push({toolCallId:e.id,content:`Invalid parameters for ${r}.${i}:
5
+ ${H.errors.join("\n")}
6
6
 
7
7
  Expected schema:
8
- ${JSON.stringify(d.params)}`,isError:true});continue}const W=(0,_normalizeParams.aliasNote)(S.notes);const M=(0,_policy.describeCall)(o,d,s);const O=(0,_policy.decide)({descriptor:d,toolId:o,policy:q,isRelease:P});const J=a=>[{type:"tool-start",id:e.id,toolId:o,action:i,label:M,description:d.summary,params:s,effect:d.effect},{type:"tool-end",id:e.id,ok:false,summary:a,durationMs:0}];if(O.verdict==="refuse"){for(const a of J("refused"))yield a;g.push({toolCallId:e.id,content:O.reason,isError:true});continue}const se=O.verdict==="needs-approval";if(xe&&!Ae&&(d.effect!=="read"||se)){yield*Me()}if(Ie&&(d.effect!=="read"||se)){for(const a of J("declined"))yield a;g.push({toolCallId:e.id,content:x.get(e.id)===false?declinedResult(j.get(e.id)):"Not run \u2014 the user declined another change in this same batch, so none of it was applied. Ask what they would prefer before proposing it again.",isError:true});continue}if(se){let a;if(x.has(e.id)){a=x.get(e.id)}else if(w?.has(`${o}.${i}`)){a=true;x.set(e.id,true)}else{yield{type:"approval-required",id:e.id,toolId:o,action:i,label:M,description:d.summary,reason:O.reason};const h=(0,_realNow.realNow)();const T=await readTargetDigest({toolId:o,action:i,params:s,dispatch:u,catalog:n});const R=readAnswer(L?await L({id:e.id,toolId:o,action:i,label:M,description:d.summary,reason:O.reason,params:s,targetDigest:T}):false);K+=(0,_realNow.realNow)()-h;a=R.approved;x.set(e.id,a);if(R.trust)w?.add(`${o}.${i}`);if(R.reason)j.set(e.id,R.reason)}if(!a){for(const h of J("declined"))yield h;g.push({toolCallId:e.id,content:declinedResult(j.get(e.id)),isError:true});continue}}if(_askGate.ACQUISITION_CALLS.has(`${o}.${i}`))be=true;yield{type:"tool-start",id:e.id,toolId:o,action:i,label:M,description:d.summary,params:s,effect:d.effect};const Se=(0,_realNow.realNow)();const _e=await captureBefore(d,o,s,u);const ae=await captureStoreState(d,o,s,u);if(E?.aborted){for(const a of J("stopped"))yield a;g.push({toolCallId:e.id,content:"Not run \u2014 the user pressed Stop before this call started.",isError:true});continue}const Ne=(0,_policy.decide)({descriptor:d,toolId:o,policy:q,isRelease:P});if(Ne.verdict==="refuse"){for(const a of J("refused"))yield a;g.push({toolCallId:e.id,content:Ne.reason,isError:true});continue}try{const a=i===_snapshotReads.SNAPSHOT_ACTION?(0,_snapshotReads.projectSnapshot)(o,await requireSnapshot(y,o),s):o==="ask-buoy"?await runAskBuoyAction(i,v,u,U,s,D):await(Te.get(e.id)??u(o,i,s));const h=(0,_redact.redact)((0,_redact.stripSelfTraffic)(a));const T=reportsFailure(a);const R=fullText(h);const de=U&&!T&&!(o==="ask-buoy"&&i==="retrieve")?U.stash({toolId:o,action:i,params:s,capturedAt:(0,_realNow.realNow)(),text:R}):void 0;const Y=encodeResult(R,de);const Le=Y.length>MAX_TRACE_RESULT_CHARS?`${Y.slice(0,MAX_TRACE_RESULT_CHARS)}\u2026`:Y;yield{type:"tool-end",id:e.id,ok:!T,summary:summarise(a),durationMs:(0,_realNow.realNow)()-Se,result:Le};const le=ae===void 0||T?void 0:await captureStoreState(d,o,s,u);if(d.effect!=="read"&&o!=="ask-buoy"&&!T){const ce=v.noteExternalRevert(o,i,s);const $e=(0,_effectFor.effectFor)(o,d,s,a,_e,{before:ae,after:le});if($e&&!ce){const Ce=v.record(o,i,$e,Oe);if(Ce)Ce.callId=e.id}}const Ee=T?void 0:(0,_receipts.projectReceipt)({toolId:o,action:i,params:s,result:h,before:ae??_e,after:le,effect:d.effect});if(Ee)yield{type:"block",block:Ee,forToolCallId:e.id};const ee=d.effect!=="read"&&!T?await(0,_verify.verifyOutcome)({toolId:o,action:i,params:s,result:a,dispatch:u,signal:E,after:le}):void 0;if(ee)yield{type:"tool-verified",id:e.id,verification:ee};for(const ce of Y.match(qe)??[])re.add(ce);g.push({toolCallId:e.id,content:Y+ownedKeyNote(o,d,s,H)+W+(ee?(0,_verify.verificationTrailer)(ee):"")+(ve.has(e.id)?_textToolCalls.SALVAGE_NOTE:""),...de?{ref:de}:{}});if(o==="app"&&(i==="reloadApp"||i==="reload")){we=true}}catch(a){const h=a instanceof Error?a.message:String(a);yield{type:"tool-end",id:e.id,ok:false,summary:h,durationMs:(0,_realNow.realNow)()-Se};g.push({toolCallId:e.id,content:`Failed: ${h}`,isError:true})}}if(k.length){B.push(callSignature(k));if(B.length>LOOP_REPEATS)B.shift();const e=!ye&&B.length===LOOP_REPEATS&&B.every(o=>o===B[0]);if(e){ye=true;const o=g[g.length-1];if(o)o.content=`${o.content}
8
+ ${JSON.stringify(f.params)}`,isError:true});continue}const J=(0,_normalizeParams.aliasNote)(l.notes);const M=(0,_policy.describeCall)(r,f,p);const Y=(0,_policy.decide)({descriptor:f,toolId:r,policy:P,isRelease:q});const K=s=>[{type:"tool-start",id:e.id,toolId:r,action:i,label:M,description:f.summary,params:p,effect:f.effect},{type:"tool-end",id:e.id,ok:false,summary:s,durationMs:0}];if(Y.verdict==="refuse"){for(const s of K("refused"))yield s;k.push({toolCallId:e.id,content:Y.reason,isError:true});continue}const ue=Y.verdict==="needs-approval";if(Be&&!_e&&(f.effect!=="read"||ue)){yield*ze()}if(Ee&&(f.effect!=="read"||ue)){for(const s of K("declined"))yield s;k.push({toolCallId:e.id,content:L.get(e.id)===false?declinedResult(B.get(e.id)):"Not run \u2014 the user declined another change in this same batch, so none of it was applied. Ask what they would prefer before proposing it again.",isError:true});continue}if(ue){let s;if(L.has(e.id)){s=L.get(e.id)}else if(A?.has(`${r}.${i}`)){s=true;L.set(e.id,true)}else{yield{type:"approval-required",id:e.id,toolId:r,action:i,label:M,description:f.summary,reason:Y.reason};const m=(0,_realNow.realNow)();const S=await readTargetDigest({toolId:r,action:i,params:p,dispatch:a,catalog:n});const R=readAnswer(U?await U({id:e.id,toolId:r,action:i,label:M,description:f.summary,reason:Y.reason,params:p,targetDigest:S}):false);X+=(0,_realNow.realNow)()-m;s=R.approved;L.set(e.id,s);if(R.trust)A?.add(`${r}.${i}`);if(R.reason)B.set(e.id,R.reason)}if(!s){for(const m of K("declined"))yield m;k.push({toolCallId:e.id,content:declinedResult(B.get(e.id)),isError:true});continue}}if(_askGate.ACQUISITION_CALLS.has(`${r}.${i}`))ve=true;if(r==="ask-buoy"&&i==="openProcedure")we=true;else if(f.effect!=="read")Te=true;yield{type:"tool-start",id:e.id,toolId:r,action:i,label:M,description:f.summary,params:p,effect:f.effect};const $e=(0,_realNow.realNow)();const Ce=await captureBefore(f,r,p,a);const fe=await captureStoreState(f,r,p,a);if($?.aborted){for(const s of K("stopped"))yield s;k.push({toolCallId:e.id,content:"Not run \u2014 the user pressed Stop before this call started.",isError:true});continue}const Oe=(0,_policy.decide)({descriptor:f,toolId:r,policy:P,isRelease:q});if(Oe.verdict==="refuse"){for(const s of K("refused"))yield s;k.push({toolCallId:e.id,content:Oe.reason,isError:true});continue}try{const s=i===_snapshotReads.SNAPSHOT_ACTION?(0,_snapshotReads.projectSnapshot)(r,await requireSnapshot(y,r),p):r==="ask-buoy"?await runAskBuoyAction(i,v,a,D,p,j):await(Ie.get(e.id)??a(r,i,p));const m=(0,_redact.redact)((0,_redact.stripSelfTraffic)(s));const S=reportsFailure(s);const R=fullText(r==="highlight-updates"&&i==="describeScreen"&&!S?(0,_compactScreen.compactScreen)(m):m);const pe=D&&!S&&!(r==="ask-buoy"&&i==="retrieve")?D.stash({toolId:r,action:i,params:p,capturedAt:(0,_realNow.realNow)(),text:R}):void 0;const Q=encodeResult(R,pe);const Fe=Q.length>MAX_TRACE_RESULT_CHARS?`${Q.slice(0,MAX_TRACE_RESULT_CHARS)}\u2026`:Q;yield{type:"tool-end",id:e.id,ok:!S,summary:summarise(s),durationMs:(0,_realNow.realNow)()-$e,result:Fe};const he=fe===void 0||S?void 0:await captureStoreState(f,r,p,a);if(f.effect!=="read"&&r!=="ask-buoy"&&!S){const ye=v.noteExternalRevert(r,i,p);const qe=(0,_effectFor.effectFor)(r,f,p,s,Ce,{before:fe,after:he});if(qe&&!ye){const xe=v.record(r,i,qe,Le);if(xe)xe.callId=e.id}}const Pe=S?void 0:(0,_receipts.projectReceipt)({toolId:r,action:i,params:p,result:m,before:fe??Ce,after:he,effect:f.effect});if(Pe)yield{type:"block",block:Pe,forToolCallId:e.id};const oe=f.effect!=="read"&&!S?await(0,_verify.verifyOutcome)({toolId:r,action:i,params:p,result:s,dispatch:a,signal:$,after:he}):void 0;if(oe)yield{type:"tool-verified",id:e.id,verification:oe};for(const ye of Q.match(Me)??[])se.add(ye);k.push({toolCallId:e.id,content:Q+ownedKeyNote(r,f,p,E)+J+(oe?(0,_verify.verificationTrailer)(oe):"")+(Ae.has(e.id)?_textToolCalls.SALVAGE_NOTE:""),...pe?{ref:pe}:{}});if(r==="app"&&(i==="reloadApp"||i==="reload")){Re=true}}catch(s){const m=s instanceof Error?s.message:String(s);yield{type:"tool-end",id:e.id,ok:false,summary:m,durationMs:(0,_realNow.realNow)()-$e};k.push({toolCallId:e.id,content:`Failed: ${m}`,isError:true})}}if(w.length){z.push(callSignature(w));if(z.length>LOOP_REPEATS)z.shift();const e=!ke&&z.length===LOOP_REPEATS&&z.every(d=>d===z[0]);if(e){ke=true;const d=k[k.length-1];if(d)d.content=`${d.content}
9
9
 
10
- ${LOOP_WARNING}`}}p.push({role:"tool-results",results:g});if(ie){yield{type:"awaiting-user",blockId:ie};yield{type:"done",stopReason:"awaiting-user"};return p}if(we){yield{type:"done",stopReason:"realm-died"};return p}}yield{type:"block",block:{id:`cap${(0,_realNow.realNow)()}`,kind:"notice",tone:"warning",text:`Stopped after ${ue} steps to avoid looping.`,actions:[{label:"Continue",primary:true,send:"Continue where you left off."}]}};yield{type:"done",stopReason:"step-cap"};return p}function ownedKeyNote(t,r,n,l){if(t!=="storage"||r.effect==="read")return"";if(!l||typeof n.key!=="string")return"";const c=l[n.key];if(!c)return"";return`
10
+ ${LOOP_WARNING}`}}h.push({role:"tool-results",results:k});if(ce){yield{type:"awaiting-user",blockId:ce};yield{type:"done",stopReason:"awaiting-user"};return h}if(Re){yield{type:"done",stopReason:"realm-died"};return h}}yield{type:"block",block:{id:`cap${(0,_realNow.realNow)()}`,kind:"notice",tone:"warning",text:`Stopped after ${V} steps to avoid looping.`,actions:[{label:"Continue",primary:true,send:"Continue where you left off."}]}};yield{type:"done",stopReason:"step-cap"};return h}function ownedKeyNote(t,o,n,c){if(t!=="storage"||o.effect==="read")return"";if(!c||typeof n.key!=="string")return"";const u=c[n.key];if(!u)return"";return`
11
11
 
12
- [Buoy] This wrote to disk, but "${n.key}" is the saved copy of the live "${c}" store, so nothing on screen has changed \u2014 the app read that key once at startup and has held the state in memory since. Call zustand.rehydrate({"storeName":"${c}"}) to make the app pick this up, or use zustand.setState next time to change the store directly. If you meant to set the value for the app's next launch, this is already done and no further action is needed.`}async function readTargetDigest(t){const r=findDescriptor(t.catalog??_catalog.CATALOG,t.toolId,t.action);if(!r||r.effect==="read")return void 0;const n=await captureStoreState(r,t.toolId,t.params,t.dispatch);if(n!==void 0)return(0,_digest.digestOf)(n);const l=await captureBefore(r,t.toolId,t.params,t.dispatch);if(l!==void 0)return(0,_digest.digestOf)(l);return void 0}async function runGatedAction(t){const{toolId:r,action:n,dispatch:l,ledger:c,policy:b,isRelease:m}=t;const u=t.catalog??_catalog.CATALOG;const y=findDescriptor(u,r,n);if(!y){return{ok:false,error:`${r}.${n} is not in Buoy's catalog.`}}const H=(0,_normalizeParams.normalizeParams)(r,n,t.params??{});const v=(0,_validateParams.applyDefaults)(H.params,y.params);const q=(0,_validateParams.validateParams)(v,y.params);if(!q.ok){return{ok:false,error:q.errors.join("\n")}}const P=(0,_policy.decide)({descriptor:y,toolId:r,policy:b,isRelease:m});if(P.verdict==="refuse"){return{ok:false,error:P.reason}}const L=await captureBefore(y,r,v,l);const E=await captureStoreState(y,r,v,l);const U=(0,_policy.decide)({descriptor:y,toolId:r,policy:b,isRelease:m});if(U.verdict==="refuse"){return{ok:false,error:U.reason}}try{const w=r==="ask-buoy"?await runAskBuoyAction(n,c,l,void 0,v):await l(r,n,v);if(reportsFailure(w)){const D=w.error;return{ok:false,error:typeof D==="string"?D:"The tool refused this."}}if(y.effect!=="read"&&r!=="ask-buoy"){const D=c.noteExternalRevert(r,n,v);const j=await captureStoreState(y,r,v,l);const $=(0,_effectFor.effectFor)(r,y,v,w,L,{before:E,after:j});if($&&!D)c.record(r,n,$)}return{ok:true,result:w}}catch(w){return{ok:false,error:w instanceof Error?w.message:String(w)}}}async function runAskBuoyAction(t,r,n,l,c,b){if(t==="retrieve"){return(0,_retrieve.retrieveEvidence)(l,c??{})}if(t==="openProcedure"){const m=typeof c?.id==="string"?c.id:"";const u=b?.find(y=>y.id===m);if(!u){const y=(b??[]).map(H=>H.id);return{ok:false,error:y.length?`No procedure "${m}". This app's procedures: ${y.join(", ")}.`:"This app has no procedures written for it."}}return{id:u.id,title:u.title,...u.version?{version:u.version}:{},...u.requires?.length?{requires:u.requires}:{},body:u.body,note:"Follow these steps with the ordinary tools. Every write still goes through the same policy, approval and undo as any other call; a step that is refused stays refused."}}if(t==="listChanges"){const m=r.list().filter(u=>!u.undoneAt).map(u=>({toolId:u.toolId,action:u.action,kind:u.effect.kind,label:u.effect.label,reversible:(0,_ledger.isReversible)(u.effect)}));return{changes:m,returned:m.length}}if(t==="undoAll"){const m=await r.revertAll(n);return{ok:m.failed.length===0,...m}}throw new Error(`Unknown ask-buoy action "${t}"`)}async function requireSnapshot(t,r){if(!t){throw new Error(`This app cannot read ${r}'s snapshot \u2014 Buoy's tool registry is not published here.`)}return await t(r)}async function captureStoreState(t,r,n,l){if(r==="zustand"&&t.action==="setState"){if(typeof n.storeName!=="string")return void 0;try{return await l(r,"getStoreState",{storeName:n.storeName})}catch{return void 0}}if(r==="query"&&t.action==="setQueryData"){const c=typeof n.queryHash==="string"?n.queryHash:Array.isArray(n.queryKey)?(0,_normalizeParams.hashQueryKey)(n.queryKey):void 0;if(!c)return void 0;try{return await l(r,"getQueryData",{queryHash:c})}catch{return void 0}}return void 0}async function captureBefore(t,r,n,l){if(t.effect==="read")return void 0;if(r==="network")return captureRuleSwitch(t,n,l);if(r!=="storage"||typeof n.key!=="string")return void 0;const c=t.action.startsWith("async.")?"async.getItem":t.action.startsWith("mmkv.")?"mmkv.get":void 0;if(!c)return void 0;const b={key:n.key};if(c==="mmkv.get"){if(typeof n.instanceId!=="string")return void 0;b.instanceId=n.instanceId}try{const m=await l(r,c,b);const u=m&&typeof m==="object"?m:void 0;if(u?.found===false)return void 0;const y=u?u.value:m;return{had:y!==void 0&&y!==null,value:y??void 0}}catch{return void 0}}async function captureRuleSwitch(t,r,n){if(t.action!=="setOverrideRuleEnabled"&&t.action!=="setOverridesEnabled"){return void 0}try{const l=await n("network","listOverrideRules",{});if(!l||typeof l!=="object")return void 0;if(t.action==="setOverridesEnabled"){return{had:true,value:String(l.enabled!==false)}}const c=(l.rules??[]).find(b=>b&&b.id===r.id);if(!c)return void 0;return{had:true,value:String(c.enabled!==false)}}catch{return void 0}}
12
+ [Buoy] This wrote to disk, but "${n.key}" is the saved copy of the live "${u}" store, so nothing on screen has changed \u2014 the app read that key once at startup and has held the state in memory since. Call zustand.rehydrate({"storeName":"${u}"}) to make the app pick this up, or use zustand.setState next time to change the store directly. If you meant to set the value for the app's next launch, this is already done and no further action is needed.`}async function readTargetDigest(t){const o=findDescriptor(t.catalog??_catalog.CATALOG,t.toolId,t.action);if(!o||o.effect==="read")return void 0;const n=await captureStoreState(o,t.toolId,t.params,t.dispatch);if(n!==void 0)return(0,_digest.digestOf)(n);const c=await captureBefore(o,t.toolId,t.params,t.dispatch);if(c!==void 0)return(0,_digest.digestOf)(c);return void 0}async function runGatedAction(t){const{toolId:o,action:n,dispatch:c,ledger:u,policy:T,isRelease:g}=t;const a=t.catalog??_catalog.CATALOG;const y=findDescriptor(a,o,n);if(!y){return{ok:false,error:`${o}.${n} is not in Buoy's catalog.`}}const E=(0,_normalizeParams.normalizeParams)(o,n,t.params??{});const v=(0,_validateParams.applyDefaults)(E.params,y.params);const P=(0,_validateParams.validateParams)(v,y.params);if(!P.ok){return{ok:false,error:P.errors.join("\n")}}const q=(0,_policy.decide)({descriptor:y,toolId:o,policy:T,isRelease:g});if(q.verdict==="refuse"){return{ok:false,error:q.reason}}const U=await captureBefore(y,o,v,c);const $=await captureStoreState(y,o,v,c);const D=(0,_policy.decide)({descriptor:y,toolId:o,policy:T,isRelease:g});if(D.verdict==="refuse"){return{ok:false,error:D.reason}}try{const A=o==="ask-buoy"?await runAskBuoyAction(n,u,c,void 0,v):await c(o,n,v);if(reportsFailure(A)){const j=A.error;return{ok:false,error:typeof j==="string"?j:"The tool refused this."}}if(y.effect!=="read"&&o!=="ask-buoy"){const j=u.noteExternalRevert(o,n,v);const B=await captureStoreState(y,o,v,c);const C=(0,_effectFor.effectFor)(o,y,v,A,U,{before:$,after:B});if(C&&!j)u.record(o,n,C)}return{ok:true,result:A}}catch(A){return{ok:false,error:A instanceof Error?A.message:String(A)}}}async function runAskBuoyAction(t,o,n,c,u,T){if(t==="retrieve"){return(0,_retrieve.retrieveEvidence)(c,u??{})}if(t==="openProcedure"){const g=typeof u?.id==="string"?u.id:"";const a=T?.find(y=>y.id===g);if(!a){const y=(T??[]).map(E=>E.id);return{ok:false,error:y.length?`No procedure "${g}". This app's procedures: ${y.join(", ")}.`:"This app has no procedures written for it."}}return{id:a.id,title:a.title,...a.version?{version:a.version}:{},...a.requires?.length?{requires:a.requires}:{},body:a.body,note:"Follow these steps with the ordinary tools. Every write still goes through the same policy, approval and undo as any other call; a step that is refused stays refused."}}if(t==="listChanges"){const g=o.list().filter(a=>!a.undoneAt).map(a=>({id:a.id,toolId:a.toolId,action:a.action,kind:a.effect.kind,label:a.effect.label,reversible:(0,_ledger.isReversible)(a.effect)}));return{changes:g,returned:g.length}}if(t==="undoChange"){const g=typeof u?.id==="string"?u.id:"";const a=o.list().find(E=>E.id===g);if(!a){const E=o.list().filter(v=>!v.undoneAt).map(v=>v.id);return{ok:false,error:`No change with id "${g}". Call listChanges for the current ids${E.length?` (${E.join(", ")})`:""}.`}}if(a.undoneAt)return{ok:false,error:`"${a.effect.label}" was already undone.`};if(!(0,_ledger.isReversible)(a.effect)){return{ok:false,error:`"${a.effect.label}" can't be undone automatically \u2014 it is permanent.`}}const y=await o.revertOne(a,n);return y?{ok:false,error:y}:{ok:true,undone:{id:a.id,label:a.effect.label}}}if(t==="undoAll"){const g=await o.revertAll(n);return{ok:g.failed.length===0,...g}}throw new Error(`Unknown ask-buoy action "${t}"`)}async function requireSnapshot(t,o){if(!t){throw new Error(`This app cannot read ${o}'s snapshot \u2014 Buoy's tool registry is not published here.`)}return await t(o)}async function captureStoreState(t,o,n,c){if(o==="zustand"&&t.action==="setState"){if(typeof n.storeName!=="string")return void 0;try{return await c(o,"getStoreState",{storeName:n.storeName})}catch{return void 0}}if(o==="query"&&t.action==="setQueryData"){const u=typeof n.queryHash==="string"?n.queryHash:Array.isArray(n.queryKey)?(0,_normalizeParams.hashQueryKey)(n.queryKey):void 0;if(!u)return void 0;try{return await c(o,"getQueryData",{queryHash:u})}catch{return void 0}}return void 0}async function captureBefore(t,o,n,c){if(t.effect==="read")return void 0;if(o==="network")return captureRuleSwitch(t,n,c);if(o!=="storage"||typeof n.key!=="string")return void 0;const u=t.action.startsWith("async.")?"async.getItem":t.action.startsWith("mmkv.")?"mmkv.get":void 0;if(!u)return void 0;const T={key:n.key};if(u==="mmkv.get"){if(typeof n.instanceId!=="string")return void 0;T.instanceId=n.instanceId}try{const g=await c(o,u,T);const a=g&&typeof g==="object"?g:void 0;if(a?.found===false)return void 0;const y=a?a.value:g;return{had:y!==void 0&&y!==null,value:y??void 0}}catch{return void 0}}async function captureRuleSwitch(t,o,n){if(t.action!=="setOverrideRuleEnabled"&&t.action!=="setOverridesEnabled"){return void 0}try{const c=await n("network","listOverrideRules",{});if(!c||typeof c!=="object")return void 0;if(t.action==="setOverridesEnabled"){return{had:true,value:String(c.enabled!==false)}}const u=(c.rules??[]).find(T=>T&&T.id===o.id);if(!u)return void 0;return{had:true,value:String(u.enabled!==false)}}catch{return void 0}}