@buoy-gg/agent-core 7.0.62 → 7.0.63

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 (46) hide show
  1. package/lib/commonjs/blocks/receipts.js +1 -1
  2. package/lib/commonjs/catalog/catalog.g.js +2 -2
  3. package/lib/commonjs/catalog/catalog.source.json +13 -1
  4. package/lib/commonjs/catalog/catalog.types.g.js +1 -1
  5. package/lib/commonjs/catalog/snapshotReads.js +1 -1
  6. package/lib/commonjs/context/buildContextPack.js +1 -1
  7. package/lib/commonjs/engine/emptyTap.test.js +1 -0
  8. package/lib/commonjs/engine/idGrounding.js +2 -0
  9. package/lib/commonjs/engine/planOnlyStop.js +1 -1
  10. package/lib/commonjs/engine/runAgentTurn.js +9 -7
  11. package/lib/commonjs/engine/systemPrompt.js +3 -1
  12. package/lib/commonjs/engine/verify.js +1 -1
  13. package/lib/commonjs/policy/policy.js +1 -1
  14. package/lib/commonjs/policy/redact.js +1 -1
  15. package/lib/commonjs/policy/turnScope.js +1 -0
  16. package/lib/commonjs/providers/anthropic.js +1 -1
  17. package/lib/commonjs/providers/openai.js +3 -3
  18. package/lib/module/blocks/receipts.js +1 -1
  19. package/lib/module/catalog/catalog.g.js +2 -2
  20. package/lib/module/catalog/catalog.source.json +13 -1
  21. package/lib/module/catalog/catalog.types.g.js +1 -1
  22. package/lib/module/catalog/snapshotReads.js +1 -1
  23. package/lib/module/context/buildContextPack.js +1 -1
  24. package/lib/module/engine/emptyTap.test.js +1 -0
  25. package/lib/module/engine/idGrounding.js +2 -0
  26. package/lib/module/engine/planOnlyStop.js +1 -1
  27. package/lib/module/engine/runAgentTurn.js +9 -7
  28. package/lib/module/engine/systemPrompt.js +3 -1
  29. package/lib/module/engine/verify.js +1 -1
  30. package/lib/module/policy/policy.js +1 -1
  31. package/lib/module/policy/redact.js +1 -1
  32. package/lib/module/policy/turnScope.js +1 -0
  33. package/lib/module/providers/anthropic.js +2 -2
  34. package/lib/module/providers/openai.js +3 -3
  35. package/lib/typescript/catalog/catalog.g.d.ts +2 -2
  36. package/lib/typescript/catalog/catalog.types.g.d.ts +2 -2
  37. package/lib/typescript/engine/emptyTap.test.d.ts +2 -0
  38. package/lib/typescript/engine/idGrounding.d.ts +7 -0
  39. package/lib/typescript/engine/planOnlyStop.d.ts +2 -2
  40. package/lib/typescript/engine/systemPrompt.d.ts +1 -1
  41. package/lib/typescript/policy/policy.d.ts +8 -3
  42. package/lib/typescript/policy/redact.d.ts +1 -0
  43. package/lib/typescript/policy/turnScope.d.ts +13 -0
  44. package/lib/typescript/providers/types.d.ts +1 -0
  45. package/lib/web/index.mjs +44 -36
  46. package/package.json +1 -1
@@ -831,8 +831,20 @@
831
831
  {
832
832
  "toolId": "impersonate",
833
833
  "title": "Impersonate",
834
- "summary": "Become another user inside the running app without logging out: it injects an impersonation header (default `x-impersonate-user-id: <user.id>`) into every outgoing globalThis.fetch and XMLHttpRequest, so the backend returns that user's data. Reach for it to reproduce a specific customer's bug (\"show me what account 8812 sees\"), then stop/pause to return to the real login. Actions only mutate state \u2014 they all resolve to void, so read the tool snapshot (isActive / isPaused / currentUser / history) to confirm anything took effect. Note that switching or stopping also CLEARS app caches per dataNukeSettings (react-query + redux on by default), so it is not a passive read-only view.",
834
+ "summary": "Become another user inside the running app without logging out: it injects an impersonation header (default `x-impersonate-user-id: <user.id>`) into every outgoing globalThis.fetch and XMLHttpRequest, so the backend returns that user's data. Reach for it to reproduce a specific customer's bug (\"show me what account 8812 sees\"), then stop/pause to return to the real login. Read impersonate.getSnapshot to check the switch. It shows isActive, isPaused, currentUser, and history. Note that switching or stopping also CLEARS app caches per dataNukeSettings (react-query + redux on by default), so it is not a passive read-only view.",
835
835
  "actions": [
836
+ {
837
+ "action": "getSnapshot",
838
+ "summary": "Read the user, pause state, and past users. Read settings too.",
839
+ "description": "Returns isActive, isPaused, currentUser, headerKey, ignorePatterns, dataNukeSettings, showBanner, and history. Users have id, displayName, and email. Each history row has user and lastUsedAt. Settings show what should clear, not proof it cleared.",
840
+ "params": {
841
+ "type": "object",
842
+ "properties": {},
843
+ "additionalProperties": false
844
+ },
845
+ "effect": "read",
846
+ "release": "works"
847
+ },
836
848
  {
837
849
  "action": "searchUsers",
838
850
  "summary": "Search the app's own user directory and get back User objects you can feed to startImpersonation.",
@@ -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":["getSnapshot","listAtoms","getAtomValue","getChangeDetail","setAtom","clearEvents"],"route-events":["getSnapshot","getCurrentRoute","navigate","stackGoBack","stackNavigateToIndex","stackPopToIndex","stackPopToTop","clearEvents"],"debug-borders":["cycleMode","setMode"],"zustand":["getSnapshot","listStores","getStoreState","getChangeDetail","setState","rehydrate","clearEvents"],"redux":["getSnapshot","getState","getActionDetail","dispatch","setState","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
+ "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":["getSnapshot","listAtoms","getAtomValue","getChangeDetail","setAtom","clearEvents"],"route-events":["getSnapshot","getCurrentRoute","navigate","stackGoBack","stackNavigateToIndex","stackPopToIndex","stackPopToTop","clearEvents"],"debug-borders":["cycleMode","setMode"],"zustand":["getSnapshot","listStores","getStoreState","getChangeDetail","setState","rehydrate","clearEvents"],"redux":["getSnapshot","getState","getActionDetail","dispatch","setState","clearEvents"],"impersonate":["getSnapshot","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 +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(r){return Array.isArray(r)?r:[]}function rec(r){return r&&typeof r==="object"?r:{}}function num(r,t){return typeof r==="number"&&r>0?r:t}function newest(r,t){return r.length<=t?r:r.slice(0,t)}function projectConsole(r,t){const s=asArray(rec(r).entries);const u=typeof t.level==="string"?SEVERITY[t.level]??0:0;const i=typeof t.pattern==="string"?t.pattern.toLowerCase():void 0;const d=s.filter(c=>{const n=rec(c);const o=String(n.level??"log");if((SEVERITY[o]??1)<u)return false;if(!i)return true;return String(n.message??"").toLowerCase().includes(i)});const e=newest(d,num(t.limit,40));return{totalCaptured:s.length,shown:e.length,entries:e.map(c=>{const n=rec(c);return{at:n.timestamp,level:n.level,message:n.message}})}}function projectNetwork(r,t){const s=rec(r);const u=asArray(s.events).filter(n=>!(0,_redact.isSelfTraffic)(rec(n).url));const i=t.failedOnly===true;const d=typeof t.pattern==="string"?t.pattern.toLowerCase():void 0;const e=u.filter(n=>{const o=rec(n);const a=typeof o.status==="number"?o.status:void 0;if(i&&!(o.error||a!==void 0&&a>=400))return false;if(!d)return true;return String(o.url??"").toLowerCase().includes(d)});const c=newest(e,num(t.limit,25));return{totalCaptured:u.length,shown:c.length,requests:c.map(n=>{const o=rec(n);return{id:o.id,method:o.method,url:o.url,status:o.status,durationMs:o.duration,error:o.error,overridden:o.override?true:void 0}}),hint:"Read one response with network.getEventBody({ id })."}}function flattenRoutes(r,t=[],s=80){for(const u of asArray(r)){if(t.length>=s)break;const i=rec(u);if(i.isInternal!==true&&typeof i.path==="string")t.push(i.path);flattenRoutes(i.children,t,s)}return t}function projectStorage(r,t){const s=e=>typeof e==="string"&&e.length>300?`${e.slice(0,300)}\u2026`:e;const u=typeof t.key==="string"?t.key.toLowerCase():void 0;const i=asArray(r).map(rec).map(e=>{const c=rec(e.data);return{id:e.id,at:e.timestamp,action:e.action,storage:e.storageType,key:c.key??c.keys,value:s(c.value),prevValue:s(c.prevValue)}}).filter(e=>!u||JSON.stringify(e.key??"").toLowerCase().includes(u)).sort((e,c)=>String(c.at).localeCompare(String(e.at)));const d=num(t.limit,20);return{events:i.slice(0,d),total:i.length,returned:Math.min(d,i.length)}}function projectRedux(r,t){const s=e=>{if(e===void 0)return void 0;const c=typeof e==="string"?e:JSON.stringify(e);return c&&c.length>400?`${c.slice(0,400)}\u2026`:e};const u=typeof t.type==="string"?t.type.toLowerCase():void 0;const i=asArray(r).map(rec).filter(e=>e.category!=="internal").map(e=>({id:e.id,at:e.timestamp,type:e.type,payload:s(e.payload),meta:s(e.meta),error:s(e.error),changed:e.diffSummary||void 0})).filter(e=>!u||String(e.type).toLowerCase().includes(u)).sort((e,c)=>Number(c.at)-Number(e.at));const d=num(t.limit,20);return{actions:i.slice(0,d),total:i.length,returned:Math.min(d,i.length)}}function projectStoreChanges(r,t,s,u){const i=n=>{if(n===void 0)return void 0;const o=typeof n==="string"?n:JSON.stringify(n);return o&&o.length>400?`${o.slice(0,400)}\u2026`:n};const d=typeof t[u]==="string"?String(t[u]).toLowerCase():void 0;const e=asArray(rec(r).changes).map(rec).map(n=>({id:n.id,at:n.timestamp,[u]:n[s],changed:n.changedKeys,summary:n.diffSummary||void 0,...s==="storeName"?{partial:i(n.partial)}:{value:i(n.valuePreview)}})).filter(n=>!d||String(n[u]??"").toLowerCase().includes(d)).sort((n,o)=>Number(o.at)-Number(n.at));const c=num(t.limit,20);return{changes:e.slice(0,c),total:e.length,returned:Math.min(c,e.length)}}function projectRoutes(r,t){const s=rec(r);const u=rec(s.sitemap);const i=asArray(s.stack).map(rec);const d=asArray(s.events).map(rec);const e=i.map(a=>({name:a.name,path:a.pathname,focused:a.isFocused===true?true:void 0}));const c=d.slice(0,num(t.limit,15)).map(a=>({at:a.timestamp,from:a.previousPathname,to:a.pathname}));const n=i.find(a=>a.isFocused===true)??i[i.length-1];const o=d.length?{path:d[0].pathname,source:"event"}:typeof n?.pathname==="string"?{path:n.pathname,source:"stack"}:void 0;return{currentRoute:o,stack:e,routes:flattenRoutes(u.routes),sitemapSource:u.source,recentNavigations:c}}function projectEnv(r){const t=rec(r);return{env:t.env??t.variables,requiredEnvVars:t.requiredEnvVars,...Array.isArray(t.checks)?{checks:t.checks}:{}}}function projectSentry(r,t){const s=rec(r);const u=asArray(s.envelopes).map(rec);const i=typeof t.type==="string"?t.type:void 0;const d=typeof t.pattern==="string"?t.pattern.toLowerCase():void 0;const e=u.filter(n=>{const o=asArray(n.items).map(rec);if(i&&!o.some(a=>a.type===i))return false;if(!d)return true;return o.some(a=>String(a.summary??"").toLowerCase().includes(d))});const c=newest(e,num(t.limit,10));return{status:s.status,totalCaptured:u.length,shown:c.length,envelopes:c.map(n=>({id:n.id,at:n.timestamp,origin:n.origin,eventId:n.eventId,totalBytes:n.totalBytes,items:asArray(n.items).map(o=>{const a=rec(o);return{type:a.type,summary:a.summary,spanCount:a.spanCount,bytes:a.bytes}})})),drops:(()=>{const n=rec(s.drops);const o=asArray(n.recent).map(rec);const a=l=>l.category==="span"||l.category==="transaction";const p=[...newest(o.filter(l=>!a(l)),8),...newest(o.filter(a),3)];const f=p.map(l=>({at:l.timestamp,reason:l.reason,category:l.category,quantity:l.quantity,detail:typeof l.detail==="string"?l.detail.slice(0,200):l.detail}));return f.length||n.summary?{summary:n.summary,recent:f}:void 0})(),hint:"For crash stack traces, console's [FATAL] entries carry the component stacks. `drops` lists events the SDK discarded before sending, with the reason."}}function projectSnapshot(r,t,s={}){switch(r){case"console":return projectConsole(t,s);case"network":return projectNetwork(t,s);case"route-events":return projectRoutes(t,s);case"storage":return projectStorage(t,s);case"redux":return projectRedux(t,s);case"zustand":return projectStoreChanges(t,s,"storeName","store");case"jotai":return projectStoreChanges(t,s,"atomLabel","atom");case"env":return projectEnv(t);case"sentry":return projectSentry(t,s);default:return t}}
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(a){return Array.isArray(a)?a:[]}function rec(a){return a&&typeof a==="object"?a:{}}function num(a,t){return typeof a==="number"&&a>0?a:t}function newest(a,t){const s=o=>{const r=rec(o).timestamp;const u=typeof r==="number"?r:typeof r==="string"?Date.parse(r):NaN;return Number.isFinite(u)?u:0};return[...a].sort((o,r)=>s(r)-s(o)).slice(0,t)}function withinBudget(a,t){const s=[];for(const o of a){const r=JSON.stringify(o).length+1;if(r>t)continue;s.push(o);t-=r}return s}function projectConsole(a,t){const s=asArray(rec(a).entries);const o=typeof t.level==="string"?SEVERITY[t.level]??0:0;const r=typeof t.pattern==="string"?t.pattern.toLowerCase():void 0;const u=s.filter(l=>{const e=rec(l);const i=String(e.level??"log");if((SEVERITY[i]??1)<o)return false;if(!r)return true;return String(e.message??"").toLowerCase().includes(r)});const n=newest(u,num(t.limit,40));return{totalCaptured:s.length,shown:n.length,entries:n.map(l=>{const e=rec(l);return{at:e.timestamp,level:e.level,message:e.message}})}}function projectNetwork(a,t){const s=rec(a);const o=asArray(s.events).filter(e=>!(0,_redact.isSelfTraffic)(rec(e).url));const r=t.failedOnly===true;const u=typeof t.pattern==="string"?t.pattern.toLowerCase():void 0;const n=o.filter(e=>{const i=rec(e);const c=typeof i.status==="number"?i.status:void 0;if(r&&!(i.error||c!==void 0&&c>=400))return false;if(!u)return true;return String(i.url??"").toLowerCase().includes(u)});const l=newest(n,num(t.limit,25));return{totalCaptured:o.length,shown:l.length,requests:l.map(e=>{const i=rec(e);return{id:i.id,at:i.timestamp,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 routeParams(a){const t=Object.entries(rec(a)).slice(0,6).map(([s,o])=>{const r=typeof o==="string"||typeof o==="number"||typeof o==="boolean";const u=r&&String(o).length<=48&&!(0,_redact.isSecretKey)(s,"route");return[s.slice(0,48),u?(0,_redact.redact)(o):"(value omitted)"]});return t.length?Object.fromEntries(t):void 0}function flattenRoutes(a,t,s=80){const o=new Map;for(const i of t.slice(0,160)){if(typeof i.pathname!=="string")continue;const c=routeParams(i.params);if(c)o.set(i.pathname,{...c,...o.get(i.pathname)})}const r=[];const u=new Set;let n=3e3;const l=(i,c=[])=>{if(r.length>=s||u.has(i))return;u.add(i);const f=o.get(i)??{};const p=[...new Set([...Object.keys(f),...asArray(c).filter(m=>typeof m==="string")])].slice(0,6).map(m=>{const g=f[m];return g===void 0||g==="(value omitted)"?m.slice(0,48):`${m}=${String(g)}`});const h=p.every(m=>Object.keys(f).some(g=>m===g||m.startsWith(`${g}=`)))?"params seen":"params";const d=p.length?` (${h}: ${p.join(", ")})`:"";const y=d.length<=n;if(y)n-=d.length;r.push(i+(y?d:""))};const e=(i,c=0)=>{if(c>12)return;for(const f of asArray(i)){if(r.length>=s)break;const p=rec(f);if(p.isInternal!==true&&typeof p.path==="string")l(p.path,p.params);e(p.children,c+1)}};e(a);for(const i of o.keys())l(i);return r}function projectStorage(a,t){const s=n=>typeof n==="string"&&n.length>300?`${n.slice(0,300)}\u2026`:n;const o=typeof t.key==="string"?t.key.toLowerCase():void 0;const r=asArray(a).map(rec).map(n=>{const l=rec(n.data);return{id:n.id,at:n.timestamp,action:n.action,storage:n.storageType,key:l.key??l.keys,value:s(l.value),prevValue:s(l.prevValue)}}).filter(n=>!o||JSON.stringify(n.key??"").toLowerCase().includes(o)).sort((n,l)=>String(l.at).localeCompare(String(n.at)));const u=num(t.limit,20);return{events:r.slice(0,u),total:r.length,returned:Math.min(u,r.length)}}function projectRedux(a,t){const s=n=>{if(n===void 0)return void 0;const l=typeof n==="string"?n:JSON.stringify(n);return l&&l.length>400?`${l.slice(0,400)}\u2026`:n};const o=typeof t.type==="string"?t.type.toLowerCase():void 0;const r=asArray(a).map(rec).filter(n=>n.category!=="internal").map(n=>({id:n.id,at:n.timestamp,type:n.type,payload:s(n.payload),meta:s(n.meta),error:s(n.error),changed:n.diffSummary||void 0})).filter(n=>!o||String(n.type).toLowerCase().includes(o)).sort((n,l)=>Number(l.at)-Number(n.at));const u=num(t.limit,20);return{actions:r.slice(0,u),total:r.length,returned:Math.min(u,r.length)}}function projectStoreChanges(a,t,s,o){const r=e=>{if(e===void 0)return void 0;const i=typeof e==="string"?e:JSON.stringify(e);return i&&i.length>400?`${i.slice(0,400)}\u2026`:e};const u=typeof t[o]==="string"?String(t[o]).toLowerCase():void 0;const n=asArray(rec(a).changes).map(rec).map(e=>({id:e.id,at:e.timestamp,[o]:e[s],changed:e.changedKeys,summary:e.diffSummary||void 0,...s==="storeName"?{partial:r(e.partial)}:{value:r(e.valuePreview)}})).filter(e=>!u||String(e[o]??"").toLowerCase().includes(u)).sort((e,i)=>Number(i.at)-Number(e.at));const l=num(t.limit,20);return{changes:n.slice(0,l),total:n.length,returned:Math.min(l,n.length)}}function projectRoutes(a,t){const s=rec(a);const o=rec(s.sitemap);const r=asArray(s.stack).map(rec);const u=asArray(s.events).map(rec);const n=r.map(c=>({name:c.name,path:c.pathname,...routeParams(c.params)?{params:routeParams(c.params)}:{},focused:c.isFocused===true?true:void 0}));const l=u.slice(0,num(t.limit,15)).map(c=>({at:c.timestamp,from:c.previousPathname,to:c.pathname,...routeParams(c.params)?{params:routeParams(c.params)}:{}}));const e=r.find(c=>c.isFocused===true)??r[r.length-1];const i=u.length?{path:u[0].pathname,source:"event",...routeParams(u[0].params)?{params:routeParams(u[0].params)}:{}}:typeof e?.pathname==="string"?{path:e.pathname,source:"stack",...routeParams(e.params)?{params:routeParams(e.params)}:{}}:void 0;return{currentRoute:i,stack:n,routes:flattenRoutes(o.routes,[...u.slice(0,80),...r.slice(0,80)]),sitemapSource:o.source,recentNavigations:l}}function projectEnv(a){const t=rec(a);return{env:t.env??t.variables,requiredEnvVars:t.requiredEnvVars,...Array.isArray(t.checks)?{checks:t.checks}:{}}}function projectSentry(a,t){const s=rec(a);const o=asArray(s.envelopes).map(rec);const r=typeof t.type==="string"?t.type:void 0;const u=typeof t.pattern==="string"?t.pattern.toLowerCase():void 0;const n=o.filter(e=>{const i=asArray(e.items).map(rec);if(r&&!i.some(c=>c.type===r))return false;if(!u)return true;return i.some(c=>String(c.summary??"").toLowerCase().includes(u))});const l=withinBudget(newest(n,num(t.limit,10)).map(e=>({id:e.id,at:e.timestamp,origin:e.origin,eventId:e.eventId,totalBytes:e.totalBytes,items:asArray(e.items).slice(0,8).map(i=>{const c=rec(i);return{type:c.type,summary:typeof c.summary==="string"?c.summary.slice(0,300):c.summary,spanCount:c.spanCount,bytes:c.bytes}})})),12e3);return{status:s.status,totalCaptured:o.length,shown:l.length,envelopes:l,drops:(()=>{const e=rec(s.drops);const i=asArray(e.recent).map(rec);const c=d=>d.category==="span"||d.category==="transaction";const f=[...newest(i.filter(d=>!c(d)),8),...newest(i.filter(c),3)];const p=withinBudget(f.map(d=>({at:d.timestamp,reason:d.reason,category:d.category,quantity:d.quantity,detail:typeof d.detail==="string"?d.detail.slice(0,200):d.detail})),4e3);const h=withinBudget(asArray(e.summary),2e3);return p.length||e.summary?{summary:h,recent:p}:void 0})(),hint:"For crash stack traces, console's [FATAL] entries carry the component stacks. `drops` lists events the SDK discarded before sending, with the reason."}}function projectImpersonate(a){const t=rec(a);const s=o=>{if(o===null||o===void 0)return null;const r=rec(o);return{id:r.id,displayName:r.displayName,email:r.email}};return{isActive:t.isActive,isPaused:t.isPaused,currentUser:s(t.currentUser),headerKey:t.headerKey,ignorePatterns:t.ignorePatterns,dataNukeSettings:t.dataNukeSettings,showBanner:t.showBanner,history:asArray(t.history).slice(0,10).map(o=>{const r=rec(o);return{user:s(r.user),lastUsedAt:r.lastUsedAt}})}}function projectSnapshot(a,t,s={}){switch(a){case"console":return projectConsole(t,s);case"network":return projectNetwork(t,s);case"route-events":return projectRoutes(t,s);case"storage":return projectStorage(t,s);case"redux":return projectRedux(t,s);case"zustand":return projectStoreChanges(t,s,"storeName","store");case"jotai":return projectStoreChanges(t,s,"atomLabel","atom");case"env":return projectEnv(t);case"impersonate":return projectImpersonate(t);case"sentry":return projectSentry(t,s);default:return t}}
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.buildContextDigest=buildContextDigest;exports.buildLiveContext=buildLiveContext;var _snapshotReads=require("../catalog/snapshotReads");var _realNow=require("../realNow");const PROBE_TIMEOUT_MS=2500;const list=t=>Array.isArray(t)?t:Array.isArray(t?.items)?t.items:[];const rec=t=>t&&typeof t==="object"?t:{};const names=(t,e,n=40)=>list(t).slice(0,n).map(s=>{if(!s||typeof s!=="object")return s;const i=s;for(const o of e){if(i[o]!==void 0)return i[o]}return i.id??i.name??i.key});const DIGEST_STORE_CAP=30;const DIGEST_STORE_KEYS=24;const DIGEST_LISTS_PER_STORE=8;const DIGEST_ITEM_CHARS=240;const DIGEST_SHAPES_TOTAL_CHARS=12e3;const clampText=(t,e)=>t.length<=e?t:`${t.slice(0,e-1)}\u2026`;function topLevelFields(t){const e=new Map;if(typeof t!=="string"||!t.startsWith("{"))return e;let n=0;let s="";const i=()=>{const o=s.indexOf(":");if(o>0)e.set(s.slice(0,o).trim(),s.slice(o+1).trim());s=""};for(const o of t.slice(1,-1)){if(o==="{"||o==="["||o==="(")n++;else if(o==="}"||o==="]"||o===")")n--;if(o===","&&n===0)i();else s+=o}i();return e}const STABLE_PROBES=[{key:"routes",toolId:"route-events",action:_snapshotReads.SNAPSHOT_ACTION,shape:t=>list(t?.routes).slice(0,60)},{key:"reduxSlices",toolId:"redux",action:"getState",params:{includeValues:false},shape:t=>{const e=rec(t);if(e.available===false)return void 0;const n=rec(e.sliceFields);if(Array.isArray(e.slices))return e.slices.slice(0,40).map(s=>Array.isArray(n[String(s)])&&n[String(s)].length?`${String(s)}: ${n[String(s)].join(", ")}`:s);return e.state&&typeof e.state==="object"?Object.keys(e.state).slice(0,40):void 0}},{key:"zustandStores",toolId:"zustand",action:"listStores",shape:t=>{let e=0;return list(t?.stores??t).slice(0,DIGEST_STORE_CAP).map(n=>{const s=n??{};const i=typeof s.name==="string"?s.name:s.id;const o=list(s.shape?.lists).map(rec);const u={};const l=[];for(const r of o.slice(0,DIGEST_LISTS_PER_STORE)){const a=typeof r.path==="string"?r.path:void 0;if(!a)continue;if(a.includes("[]."))continue;if(typeof r.item!=="string"){l.push(a);continue}const c=clampText(r.item,DIGEST_ITEM_CHARS);if(e+c.length>DIGEST_SHAPES_TOTAL_CHARS)continue;e+=c.length;u[a]=c}return{name:i,...typeof s.persistName==="string"?{persistsTo:s.persistName}:{},...(()=>{if(!Array.isArray(s.keys)||!s.keys.length)return{};const r=topLevelFields(s.shape?.shape);const a=s.keys.filter(c=>r.get(String(c))!=="function");return a.length?{keys:a.slice(0,DIGEST_STORE_KEYS)}:{}})(),...Object.keys(u).length?{listItemShapes:u}:{},...l.length?{unknownLists:l}:{}}})}},{key:"jotaiAtoms",toolId:"jotai",action:"listAtoms",shape:t=>names(rec(t).atoms??t,["debugLabel","label","id"],40)},{key:"storageKeys",toolId:"storage",action:"async.getAllKeys",shape:t=>list(t).slice(0,60)},{key:"env",toolId:"env",action:_snapshotReads.SNAPSHOT_ACTION,shape:t=>{const e=t?.env;return e&&typeof e==="object"?Object.keys(e).slice(0,40):void 0}}];const LIVE_QUERY_CAP=25;const LIVE_QUERY_SCAN=LIVE_QUERY_CAP*2;const LIVE_REQUEST_CAP=8;const LIVE_PROBES=[{key:"currentRoute",toolId:"route-events",action:"getCurrentRoute",shape:t=>{const e=rec(t);if(typeof e.path!=="string")return void 0;return e.params&&Object.keys(rec(e.params)).length?{path:e.path,params:e.params}:{path:e.path}}},{key:"queries",toolId:"query",action:"listQueries",params:{limit:LIVE_QUERY_SCAN},shape:t=>{const e=list(t?.queries??t).map(rec);const n=e.map(s=>{const i=typeof s.observers==="number"?s.observers:0;return{key:s.queryKey??s.queryHash,status:s.status,...i>0?{onScreen:true,observers:i}:{}}});n.sort((s,i)=>Number(Boolean(i.onScreen))-Number(Boolean(s.onScreen)));return n.slice(0,LIVE_QUERY_CAP)}},{key:"networkCapture",toolId:"network",action:"getCaptureStatus",shape:t=>{const e=rec(t);if(typeof e.capturing!=="boolean")return void 0;return{recording:e.capturing,...e.interceptorLive===false?{interceptorLive:false}:{},captured:typeof e.eventCount==="number"?e.eventCount:void 0}}},{key:"recentRequests",toolId:"network",action:_snapshotReads.SNAPSHOT_ACTION,params:{limit:LIVE_REQUEST_CAP},shape:t=>list(t?.requests).map(e=>{const n=rec(e);return{id:n.id,method:n.method,url:n.url,status:n.status,...n.overridden?{overridden:true}:{}}})},{key:"appClock",toolId:"clock",action:"getState",shape:t=>{const e=rec(t);if(e.active!==true||typeof e.virtualIso!=="string")return void 0;return{appTime:e.virtualIso,mode:e.mode,...typeof e.rate==="number"&&e.rate!==1?{rate:e.rate}:{},offsetMs:e.offsetMs}}},{key:"impersonating",toolId:"impersonate",action:_snapshotReads.SNAPSHOT_ACTION,shape:t=>{const e=rec(t);if(e.isActive!==true)return void 0;const n=rec(e.currentUser);return{user:typeof n.displayName==="string"?n.displayName:n.id,id:n.id,...e.isPaused===true?{paused:true}:{},header:e.headerKey}}},{key:"appLocation",toolId:"location",action:"getState",shape:t=>{const e=rec(t);if(e.active!==true)return void 0;const n=rec(e.override);const s=rec(e.current);const i=rec(s.fix);const o=rec(n.place);const u=rec(n.route);return{mode:n.mode,...typeof i.latitude==="number"?{latitude:i.latitude,longitude:i.longitude,accuracy:i.accuracy}:{noFix:s.reason},...n.mode==="fixed"&&typeof o.label==="string"?{place:o.label}:{},...n.mode==="route"?{route:u.name??u.id,playing:n.playing===true}:{},...n.signal===false?{signal:false}:{},...n.services===false?{services:false}:{}}}},{key:"appPermissions",toolId:"permissions",action:_snapshotReads.SNAPSHOT_ACTION,shape:t=>{const e=list(rec(t).permissions).map(rec).filter(n=>typeof n.override==="string");if(!e.length)return void 0;return Object.fromEntries(e.map(n=>[String(n.id),n.real&&n.real!==n.override?`${n.override} (system: ${n.real})`:n.override]))}},{key:"networkOverrides",toolId:"network",action:"listOverrideRules",shape:t=>{const e=rec(t);const n=list(e.rules).map(rec).filter(s=>s.enabled!==false).slice(0,10).map(s=>({id:s.id,urlPattern:s.urlPattern,kind:s.kind,status:s.status}));if(!n.length)return void 0;return{enabled:e.enabled===true,rules:n}}}];async function withTimeout(t,e){return await Promise.race([t.catch(()=>void 0),new Promise(n=>(0,_realNow.realSetTimeout)(()=>n(void 0),e))])}async function runProbes(t,{dispatch:e,readSnapshot:n,availableToolIds:s,availableActions:i}){const o=t.filter(r=>{if(!s.includes(r.toolId))return false;const a=i?.[r.toolId];return!a||a.includes(r.action)});const u=await Promise.all(o.map(async r=>{const a=r.action===_snapshotReads.SNAPSHOT_ACTION?n?Promise.resolve(n(r.toolId)).then(y=>(0,_snapshotReads.projectSnapshot)(r.toolId,y,r.params??{})):Promise.resolve(void 0):Promise.resolve(e(r.toolId,r.action,r.params));const c=await withTimeout(a,PROBE_TIMEOUT_MS);if(c===void 0)return[r.key,void 0];try{return[r.key,r.shape(c)]}catch{return[r.key,void 0]}}));const l={};for(const[r,a]of u){const c=a===void 0||a===null||Array.isArray(a)&&a.length===0;if(!c)l[r]=a}return l}function buildContextDigest(t){return runProbes(STABLE_PROBES,t)}function keyTokens(t){const e=Array.isArray(t)?t:typeof t==="string"?(()=>{try{const n=JSON.parse(t);return Array.isArray(n)?n:[t]}catch{return[t]}})():[t];return e.flatMap(n=>n===null||n===void 0?[]:splitTokens(String(typeof n==="object"?JSON.stringify(n):n)))}function splitTokens(t){return t.toLowerCase().split(/[^a-z0-9]+/i).filter(Boolean)}function annotateLikelyScreen(t){const e=t.currentRoute;const n=t.queries;if(!e?.path||!Array.isArray(n))return;const s=Object.values(e.params??{}).map(r=>splitTokens(String(r))).filter(r=>r.length>0);const i=splitTokens(e.path);let o=-1;let u=-1;let l=false;n.forEach((r,a)=>{if(!r.onScreen)return;const c=new Set(keyTokens(r.key));const y=s.filter(d=>d.every(p=>c.has(p))).length;const m=i.filter(d=>c.has(d)||[...c].some(p=>p.includes(d)||d.includes(p))).length;const f=y*3+m;if(f<=0)return;if(f>o){o=f;u=a;l=false}else if(f===o){l=true}});if(u>=0&&!l){n[u].likelyScreen=true;const[r]=n.splice(u,1);n.unshift(r)}}async function buildLiveContext(t){const e=await runProbes(LIVE_PROBES,t);annotateLikelyScreen(e);let n;try{n=Intl.DateTimeFormat().resolvedOptions().timeZone}catch{n=void 0}e.now={iso:new Date((0,_realNow.realNow)()).toISOString(),...n?{timeZone:n}:{}};return e}
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.buildContextDigest=buildContextDigest;exports.buildLiveContext=buildLiveContext;var _snapshotReads=require("../catalog/snapshotReads");var _realNow=require("../realNow");const PROBE_TIMEOUT_MS=2500;const list=t=>Array.isArray(t)?t:Array.isArray(t?.items)?t.items:[];const rec=t=>t&&typeof t==="object"?t:{};const names=(t,e,n=40)=>list(t).slice(0,n).map(s=>{if(!s||typeof s!=="object")return s;const i=s;for(const o of e){if(i[o]!==void 0)return i[o]}return i.id??i.name??i.key});const DIGEST_STORE_CAP=30;const DIGEST_STORE_KEYS=24;const DIGEST_LISTS_PER_STORE=8;const DIGEST_ITEM_CHARS=240;const DIGEST_SHAPES_TOTAL_CHARS=12e3;const clampText=(t,e)=>t.length<=e?t:`${t.slice(0,e-1)}\u2026`;function topLevelFields(t){const e=new Map;if(typeof t!=="string"||!t.startsWith("{"))return e;let n=0;let s="";const i=()=>{const o=s.indexOf(":");if(o>0)e.set(s.slice(0,o).trim(),s.slice(o+1).trim());s=""};for(const o of t.slice(1,-1)){if(o==="{"||o==="["||o==="(")n++;else if(o==="}"||o==="]"||o===")")n--;if(o===","&&n===0)i();else s+=o}i();return e}const STABLE_PROBES=[{key:"routes",toolId:"route-events",action:_snapshotReads.SNAPSHOT_ACTION,shape:t=>list(t?.routes).slice(0,60)},{key:"reduxSlices",toolId:"redux",action:"getState",params:{includeValues:false},shape:t=>{const e=rec(t);if(e.available===false)return void 0;const n=rec(e.sliceFields);if(Array.isArray(e.slices))return e.slices.slice(0,40).map(s=>Array.isArray(n[String(s)])&&n[String(s)].length?`${String(s)}: ${n[String(s)].join(", ")}`:s);return e.state&&typeof e.state==="object"?Object.keys(e.state).slice(0,40):void 0}},{key:"zustandStores",toolId:"zustand",action:"listStores",shape:t=>{let e=0;return list(t?.stores??t).slice(0,DIGEST_STORE_CAP).map(n=>{const s=n??{};const i=typeof s.name==="string"?s.name:s.id;const o=list(s.shape?.lists).map(rec);const u={};const l=[];for(const r of o.slice(0,DIGEST_LISTS_PER_STORE)){const a=typeof r.path==="string"?r.path:void 0;if(!a)continue;if(a.includes("[]."))continue;if(typeof r.item!=="string"){l.push(a);continue}const c=clampText(r.item,DIGEST_ITEM_CHARS);if(e+c.length>DIGEST_SHAPES_TOTAL_CHARS)continue;e+=c.length;u[a]=c}return{name:i,...typeof s.persistName==="string"?{persistsTo:s.persistName}:{},...(()=>{if(!Array.isArray(s.keys)||!s.keys.length)return{};const r=topLevelFields(s.shape?.shape);const a=s.keys.filter(c=>r.get(String(c))!=="function");return a.length?{keys:a.slice(0,DIGEST_STORE_KEYS)}:{}})(),...Object.keys(u).length?{listItemShapes:u}:{},...l.length?{unknownLists:l}:{}}})}},{key:"jotaiAtoms",toolId:"jotai",action:"listAtoms",shape:t=>names(rec(t).atoms??t,["debugLabel","label","id"],40)},{key:"storageKeys",toolId:"storage",action:"async.getAllKeys",shape:t=>list(t).slice(0,60)},{key:"env",toolId:"env",action:_snapshotReads.SNAPSHOT_ACTION,shape:t=>{const e=t?.env;return e&&typeof e==="object"?Object.keys(e).slice(0,40):void 0}}];const LIVE_QUERY_CAP=25;const LIVE_QUERY_SCAN=LIVE_QUERY_CAP*2;const LIVE_REQUEST_CAP=8;function requestAge(t){const e=typeof t==="number"?t:typeof t==="string"?Date.parse(t):NaN;return Number.isFinite(e)?{age:`${Math.max(0,Math.floor(((0,_realNow.realNow)()-e)/1e3))}s ago`}:{}}const LIVE_PROBES=[{key:"currentRoute",toolId:"route-events",action:"getCurrentRoute",shape:t=>{const e=rec(t);if(typeof e.path!=="string")return void 0;return e.params&&Object.keys(rec(e.params)).length?{path:e.path,params:e.params}:{path:e.path}}},{key:"queries",toolId:"query",action:"listQueries",params:{limit:LIVE_QUERY_SCAN},shape:t=>{const e=list(t?.queries??t).map(rec);const n=e.map(s=>{const i=typeof s.observers==="number"?s.observers:0;return{key:s.queryKey??s.queryHash,status:s.status,...i>0?{onScreen:true,observers:i}:{}}});n.sort((s,i)=>Number(Boolean(i.onScreen))-Number(Boolean(s.onScreen)));return n.slice(0,LIVE_QUERY_CAP)}},{key:"networkCapture",toolId:"network",action:"getCaptureStatus",shape:t=>{const e=rec(t);if(typeof e.capturing!=="boolean")return void 0;return{recording:e.capturing,...e.interceptorLive===false?{interceptorLive:false}:{},captured:typeof e.eventCount==="number"?e.eventCount:void 0}}},{key:"recentRequests",toolId:"network",action:_snapshotReads.SNAPSHOT_ACTION,params:{limit:LIVE_REQUEST_CAP},shape:t=>list(t?.requests).map(e=>{const n=rec(e);return{id:n.id,...n.at!==void 0?{at:n.at}:{},...requestAge(n.at),method:n.method,url:n.url,status:n.status,...n.overridden?{overridden:true}:{}}})},{key:"appClock",toolId:"clock",action:"getState",shape:t=>{const e=rec(t);if(e.active!==true||typeof e.virtualIso!=="string")return void 0;return{appTime:e.virtualIso,mode:e.mode,...typeof e.rate==="number"&&e.rate!==1?{rate:e.rate}:{},offsetMs:e.offsetMs}}},{key:"impersonating",toolId:"impersonate",action:_snapshotReads.SNAPSHOT_ACTION,shape:t=>{const e=rec(t);if(e.isActive!==true)return void 0;const n=rec(e.currentUser);return{user:typeof n.displayName==="string"?n.displayName:n.id,id:n.id,...e.isPaused===true?{paused:true}:{},header:e.headerKey}}},{key:"appLocation",toolId:"location",action:"getState",shape:t=>{const e=rec(t);if(e.active!==true)return void 0;const n=rec(e.override);const s=rec(e.current);const i=rec(s.fix);const o=rec(n.place);const u=rec(n.route);return{mode:n.mode,...typeof i.latitude==="number"?{latitude:i.latitude,longitude:i.longitude,accuracy:i.accuracy}:{noFix:s.reason},...n.mode==="fixed"&&typeof o.label==="string"?{place:o.label}:{},...n.mode==="route"?{route:u.name??u.id,playing:n.playing===true}:{},...n.signal===false?{signal:false}:{},...n.services===false?{services:false}:{}}}},{key:"appPermissions",toolId:"permissions",action:_snapshotReads.SNAPSHOT_ACTION,shape:t=>{const e=list(rec(t).permissions).map(rec).filter(n=>typeof n.override==="string");if(!e.length)return void 0;return Object.fromEntries(e.map(n=>[String(n.id),n.real&&n.real!==n.override?`${n.override} (system: ${n.real})`:n.override]))}},{key:"networkOverrides",toolId:"network",action:"listOverrideRules",shape:t=>{const e=rec(t);const n=list(e.rules).map(rec).filter(s=>s.enabled!==false).slice(0,10).map(s=>({id:s.id,urlPattern:s.urlPattern,kind:s.kind,status:s.status}));if(!n.length)return void 0;return{enabled:e.enabled===true,rules:n}}}];async function withTimeout(t,e){return await Promise.race([t.catch(()=>void 0),new Promise(n=>(0,_realNow.realSetTimeout)(()=>n(void 0),e))])}async function runProbes(t,{dispatch:e,readSnapshot:n,availableToolIds:s,availableActions:i}){const o=t.filter(r=>{if(!s.includes(r.toolId))return false;const a=i?.[r.toolId];return!a||a.includes(r.action)});const u=await Promise.all(o.map(async r=>{const a=r.action===_snapshotReads.SNAPSHOT_ACTION?n?Promise.resolve(n(r.toolId)).then(y=>(0,_snapshotReads.projectSnapshot)(r.toolId,y,r.params??{})):Promise.resolve(void 0):Promise.resolve(e(r.toolId,r.action,r.params));const c=await withTimeout(a,PROBE_TIMEOUT_MS);if(c===void 0)return[r.key,void 0];try{return[r.key,r.shape(c)]}catch{return[r.key,void 0]}}));const l={};for(const[r,a]of u){const c=a===void 0||a===null||Array.isArray(a)&&a.length===0;if(!c)l[r]=a}return l}function buildContextDigest(t){return runProbes(STABLE_PROBES,t)}function keyTokens(t){const e=Array.isArray(t)?t:typeof t==="string"?(()=>{try{const n=JSON.parse(t);return Array.isArray(n)?n:[t]}catch{return[t]}})():[t];return e.flatMap(n=>n===null||n===void 0?[]:splitTokens(String(typeof n==="object"?JSON.stringify(n):n)))}function splitTokens(t){return t.toLowerCase().split(/[^a-z0-9]+/i).filter(Boolean)}function annotateLikelyScreen(t){const e=t.currentRoute;const n=t.queries;if(!e?.path||!Array.isArray(n))return;const s=Object.values(e.params??{}).map(r=>splitTokens(String(r))).filter(r=>r.length>0);const i=splitTokens(e.path);let o=-1;let u=-1;let l=false;n.forEach((r,a)=>{if(!r.onScreen)return;const c=new Set(keyTokens(r.key));const y=s.filter(d=>d.every(p=>c.has(p))).length;const m=i.filter(d=>c.has(d)||[...c].some(p=>p.includes(d)||d.includes(p))).length;const f=y*3+m;if(f<=0)return;if(f>o){o=f;u=a;l=false}else if(f===o){l=true}});if(u>=0&&!l){n[u].likelyScreen=true;const[r]=n.splice(u,1);n.unshift(r)}}async function buildLiveContext(t){const e=await runProbes(LIVE_PROBES,t);annotateLikelyScreen(e);let n;try{n=Intl.DateTimeFormat().resolvedOptions().timeZone}catch{n=void 0}e.now={iso:new Date((0,_realNow.realNow)()).toISOString(),...n?{timeZone:n}:{}};return e}
@@ -0,0 +1 @@
1
+ "use strict";var _vitest=require("vitest");var _runAgentTurn=require("./runAgentTurn");var _catalog=require("../catalog/catalog.g");var _ledger=require("../effects/ledger");const catalog=[{toolId:"highlight-updates",title:"Screen",summary:"Screen tools",actions:[{action:"tapElement",summary:"Tap a control",effect:"write",release:"works",params:{type:"object",properties:{nativeTag:{type:"number"},testID:{type:"string"},query:{type:"string"}}}},{action:"describeScreen",summary:"Read the screen",effect:"read",release:"works",params:{type:"object",properties:{}}}]}];async function run(e,t,a=false,o=false){const n=new _ledger.EffectLedger;const s=[];let p=0;const d={protocol:"anthropic",async*send(l){s.push(l.messages);if(p++===0)yield{type:"tool-call",call:{id:"tap",name:"buoy_highlight-updates",input:{action:"tapElement",params:e}}};else yield{type:"text",delta:"The screen check is done."};yield{type:"done",outcome:"completed"}}};const r=_vitest.vi.fn(async(l,u)=>u==="describeScreen"?{elements:[{nativeTag:7,label:"Place test order",name:"Pressable"}]}:t);const i=_vitest.vi.fn(async()=>false);const c=[];for await(const l of(0,_runAgentTurn.runAgentTurn)({provider:d,catalog,system:"s",model:"m",maxTokens:100,messages:[{role:"user",text:a?"Why is checkout wrong?":"Tap the button."}],dispatch:r,ledger:n,policy:{requireApproval:o?["write"]:[]},isRelease:false,requestApproval:i}))c.push(l);return{ledger:n,requests:s,dispatch:r,requestApproval:i,events:c}}(0,_vitest.describe)("empty and missed taps",()=>{_vitest.it.each([{},{nativeTag:" ",testID:"",query:""},{testID:"",query:" \n "},{nativeTag:null,testID:" "}])("skips an empty tap: %j",async e=>{for(const t of[false,true]){const a=await run(e,{tapped:true},t,true);(0,_vitest.expect)(a.dispatch).not.toHaveBeenCalled();(0,_vitest.expect)(a.requestApproval).not.toHaveBeenCalled();(0,_vitest.expect)(a.events).toContainEqual(_vitest.expect.objectContaining({type:"tool-end",ok:false,summary:_vitest.expect.stringContaining("tapElement needs")}));(0,_vitest.expect)(JSON.stringify(a.requests)).toContain('\\"ok\\":false');(0,_vitest.expect)(a.ledger.list()).toEqual([]);(0,_vitest.expect)(a.events.some(o=>o.type==="approval-required"||o.type==="tool-verified"||o.type==="block")).toBe(false)}});_vitest.it.each([{query:"Add"},{testID:"add"},{nativeTag:7}])("runs a valid tap: %j",async e=>{const t=await run(e,{tapped:true});(0,_vitest.expect)(t.dispatch).toHaveBeenCalledWith("highlight-updates","tapElement",e);(0,_vitest.expect)(t.events).toContainEqual(_vitest.expect.objectContaining({type:"tool-end",ok:true}));(0,_vitest.expect)(t.ledger.list()).toHaveLength(1)});_vitest.it.each([{tapped:false,reason:"No match for Add."},{ok:true,tapped:false,reason:"No match for Add."}])("keeps a missed tap out of the ledger: %j",async e=>{const t=await run({query:"Add"},e);(0,_vitest.expect)(t.dispatch).toHaveBeenCalledOnce();(0,_vitest.expect)(t.events).toContainEqual(_vitest.expect.objectContaining({type:"tool-end",ok:false,summary:e.reason}));(0,_vitest.expect)(JSON.stringify(t.requests)).toContain('\\"ok\\":false');(0,_vitest.expect)(t.ledger.list()).toEqual([]);(0,_vitest.expect)(t.events.some(a=>a.type==="tool-verified"||a.type==="block")).toBe(false)});(0,_vitest.it)("keeps the question gate for valid taps",async()=>{const e=await run({query:"Place test order"},{tapped:true},true);(0,_vitest.expect)(e.requestApproval).toHaveBeenCalledOnce();(0,_vitest.expect)(e.dispatch).not.toHaveBeenCalledWith("highlight-updates","tapElement",_vitest.expect.anything());(0,_vitest.expect)(e.ledger.list()).toEqual([])});_vitest.it.each([{},{query:" "},{query:"Add"}])("guards a block button too: %j",async e=>{const t=new _ledger.EffectLedger;const a=_vitest.vi.fn(async()=>({tapped:false,reason:"No match for Add."}));const o=await(0,_runAgentTurn.runGatedAction)({toolId:"highlight-updates",action:"tapElement",params:e,catalog,dispatch:a,ledger:t,policy:{requireApproval:[]},isRelease:false});(0,_vitest.expect)(o).toEqual({ok:false,error:e.query==="Add"?"No match for Add.":"tapElement needs nativeTag, testID or query. Read describeScreen and pass one."});(0,_vitest.expect)(a).toHaveBeenCalledTimes(e.query==="Add"?1:0);(0,_vitest.expect)(t.list()).toEqual([])});_vitest.it.each([{},{query:"Add"}])("does not count a failed tap as plan work: %j",async e=>{let t=0;const a=new _ledger.EffectLedger;const o={protocol:"anthropic",async*send(){const n=t++;if(n===0)yield{type:"tool-call",call:{id:"plan",name:"buoy_ask-buoy",input:{action:"openProcedure",params:{id:"add"}}}};else if(n===1)yield{type:"tool-call",call:{id:"tap",name:"buoy_highlight-updates",input:{action:"tapElement",params:e}}};else yield{type:"text",delta:"The plan is set."};yield{type:"done",outcome:"completed"}}};for await(const n of(0,_runAgentTurn.runAgentTurn)({provider:o,catalog:[...catalog,_catalog.CATALOG.find(s=>s.toolId==="ask-buoy")],system:"s",model:"m",maxTokens:100,messages:[{role:"user",text:"Add the item."}],dispatch:async()=>({tapped:false,reason:"No match."}),ledger:a,policy:{requireApproval:[]},isRelease:false,procedures:[{id:"add",title:"Add an item",summary:"Add one item",body:"Tap Add."}]})){}(0,_vitest.expect)(t).toBe(4);(0,_vitest.expect)(a.list()).toEqual([])});_vitest.it.each([{},{query:"Add"}])("leaves failed taps out of the step-cap list: %j",async e=>{let t=0;const a={protocol:"anthropic",async*send(){if(t++===0)yield{type:"tool-call",call:{id:"tap",name:"buoy_highlight-updates",input:{action:"tapElement",params:e}}};yield{type:"done",outcome:"completed"}}};const o=[];for await(const s of(0,_runAgentTurn.runAgentTurn)({provider:a,catalog,system:"s",model:"m",maxTokens:100,messages:[{role:"user",text:"Tap Add."}],dispatch:async()=>({tapped:false,reason:"No match."}),ledger:new _ledger.EffectLedger,policy:{requireApproval:[],maxSteps:1},isRelease:false}))o.push(s);const n=o.filter(s=>s.type==="text").map(s=>s.delta).join("");(0,_vitest.expect)(n).toContain("The job is not done.");(0,_vitest.expect)(n).not.toContain("These steps ran:")})});
@@ -0,0 +1,2 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.idGrounding=idGrounding;const HISTORY_CHARS=64e3;const HOST_CHARS=32e3;const ID_VALUE=/\b(?:picker_\d+|[\da-f]{8}(?:-[\da-f]{4}){3}-[\da-f]{12})\b/gi;const idKey=r=>r==="id"||/Id$/.test(r);function idGrounding(r,a){let c="";const f=e=>{c=(c+"\n"+e.replace(/\[Buoy\] Unseen id:[^\n]*/g,"")).slice(-HISTORY_CHARS)};for(const e of r){if(e.role==="user")f(e.text);if(e.role==="tool-results")for(const o of e.results)f(o.content)}const g=a.slice(-HOST_CHARS);return{add:f,trailer(e){const o=new Set;let u=200;const d=(t,n="",l=0)=>{if(--u<0||l>6||o.size>=8)return;if(typeof t==="string"){if(idKey(n)&&t.length>0&&t.length<=160)o.add(t);for(const s of t.slice(0,2048).match(ID_VALUE)??[])o.add(s);for(const s of t.slice(0,2048).matchAll(/[?&]([^=&]+)=([^&#]*)/g)){try{if(idKey(decodeURIComponent(s[1])))d(decodeURIComponent(s[2]),"id",l+1)}catch{}}}else if(t&&typeof t==="object"){for(const[s,m]of Object.entries(t))d(m,s,l+1)}};d(e);const i=g+"\n"+c;const h=t=>{let n=i.indexOf(t);while(n>=0){if(!/[\w-]/.test(i[n-1]??"")&&!/[\w-]/.test(i[n+t.length]??""))return true;n=i.indexOf(t,n+1)}return false};return[...o].slice(0,8).filter(t=>!h(t)).map(t=>`
2
+ [Buoy] Unseen id: ${t.replace(/[\r\n]/g," ")} was not in any read. Use an id from a read, or search for it.`).join("")}}}
@@ -1 +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(n){const t=n.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 e=t.split(/(?<=[.!*])\s*(?=[A-Z“"])/).filter((i,a,s)=>!(a===s.length-1&&s.length>1&&/^[“"][^“”"]*[”"]\s*$/.test(i.trim())));const o=e.slice(-2).join(" ");const r=e.length>1&&!/\d/.test(e.at(-1)??"");if((IN_PROGRESS.test(e.at(-1)??"")||r&&IN_PROGRESS.test(e.at(-2)??""))&&!WAITS_ON_USER.test(o))return true;return ANNOUNCES.test(o)&&!WAITS_ON_USER.test(o)}
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 the user asked for this change, do it and check it. Do not ask them to grant that same step again. Still obey tool approval checks. 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 READY_TO_COMMIT=/\bi(?:['’]m| am) ready to (?:submit|place|refund|continue)\b|\bbefore i (?:submit|place|refund)\b[^.!?]*\bi need to confirm\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(n,r=false){const t=n.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 e=t.split(/(?<=[.!*])\s*(?=[A-Z“"])/).filter((i,u,s)=>!(u===s.length-1&&s.length>1&&/^[“"][^“”"]*[”"]\s*$/.test(i.trim())));const o=e.slice(-2).join(" ");if(r&&READY_TO_COMMIT.test(o))return true;const a=e.length>1&&!/\d/.test(e.at(-1)??"");if((IN_PROGRESS.test(e.at(-1)??"")||a&&IN_PROGRESS.test(e.at(-2)??""))&&!WAITS_ON_USER.test(o))return true;return ANNOUNCES.test(o)&&!WAITS_ON_USER.test(o)}
@@ -1,12 +1,14 @@
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:d,readSnapshot:y,storeKeyOwners:E,ledger:v,policy:P,isRelease:x,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:x});ne.push((0,_uiTool.uiProviderTool)());const V=P.maxSteps??DEFAULT_MAX_STEPS;const Le=newAskId();const Ue=v.generation;const ge=c.length+(u?.length??0)+ne.reduce((O,q)=>O+q.description.length+JSON.stringify(q.inputSchema).length,0);const be=()=>ge+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 De=/https?:\/\/[^\s"'\\)\]}>]+/g;let ke=false;const z=[];let ve=false;const ae=[];let de=false;let we=false;let ee=false;let Te=false;let Se=false;const je=[...h].reverse().find(O=>O.role==="user");const Be=/\?\s*$/.test(je?.text?.trim()??"");for(let O=0;O<V;O++){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 q=(0,_historyBudget.budgetForRequest)(h,be(),C.chars(Z),C.charsPerToken,v.liveCallIds());if(q.droppedRounds>0){h.length=0;h.push(...q.messages);yield{type:"history-trimmed",droppedRounds:q.droppedRounds,droppedPinned:q.droppedPinned}}else if(q.messages!==h){h.length=0;h.push(...q.messages)}let I="";let Ae=false;const F=e=>{const l=ke&&!Ae;Ae=true;ke=true;return l?{type:"text",delta:e,breakBefore:true}:{type:"text",delta:e}};let w=[];const Re=new Set;let N="";let le=true;let W=[];let te=false;let re;let G;let ce=0;for(;;){ce+=1;I="";w=[];N="";le=true;W=[];te=false;re=void 0;G=void 0;let e;const l=ge+h.reduce((a,p)=>a+(0,_historyBudget.messageSize)(p),0);for await(const a of o.send({messages:h,system:c,systemVolatile:u,tools:ne,model:T,maxTokens:g,signal:$,meta:{askId:Le,round:O+1,attempt:ce}})){if(a.type==="text"){I+=a.delta;if(!le){yield F(a.delta)}else if((0,_textToolCalls.couldBeToolCallEnvelope)(I)){N+=a.delta}else{le=false;N="";yield F(I)}}else if(a.type==="tool-call"){w.push(a.call)}else if(a.type==="meter"){yield{type:"meter",frame:a.frame}}else if(a.type==="thinking"){W.push(a.block);if(a.block.type==="thinking"&&a.block.thinking.trim()){yield{type:"reasoning",text:a.block.thinking}}}else if(a.type==="error"){if(a.kind==="truncated"||a.kind==="timeout"){G={message:a.message}}else if(a.problem&&(a.problem.kind==="throttled"||a.problem.kind==="overflow")&&I===""&&w.length===0&&W.length===0){e={problem:a.problem,message:a.message}}else{yield{type:"error",message:a.message,...a.problem?.hosted?{hosted:a.problem.hosted}:{}};te=true}}else if(a.type==="done"){re=a.outcome;if(a.usage){C.observe(l,(0,_tokenCalibration.promptTokensOf)(a.usage,o.protocol));yield{type:"usage",...a.usage,model:a.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(e.problem.hosted&&ce>=2)_=true;if(!_&&r==="overflow"){Z=Math.floor(Z/2);const a=h.reduce((J,L)=>J+(0,_historyBudget.messageSize)(L),0);const p=(0,_historyBudget.budgetForRequest)(h,be(),C.chars(Z),C.charsPerToken,v.liveCallIds());const H=p.messages.reduce((J,L)=>J+(0,_historyBudget.messageSize)(L),0);if(H>=a){_=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 a=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.";const p=e.problem.hosted;yield p?{type:"error",message:p.message,hosted:p}:{type:"error",message:`${a} (${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 l of e.calls){w.push(l);Re.add(l.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&&O<V-1&&((0,_planOnlyStop.announcesUnstartedWork)(I)||Te&&!Se&&!Be)){ee=true;h.push({role:"user",text:_planOnlyStop.PLAN_ONLY_NUDGE});continue}if(w.length===0&&!ee&&ae.length===0&&O<V-1){ee=true;h.push({role:"user",text:_planOnlyStop.SILENT_NUDGE});continue}if(w.length===0){const e=ae.join("\n\n");const l=(0,_askGate.synthesizeShortfallBlock)({text:e,attempted:we});if(l)yield{type:"block",block:l};const r=de||l?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 Ie=false;const _e=new Map;if(w.length>1){const e=w.every(l=>{if(l.name===_uiTool.UI_TOOL_NAME)return false;const r=(0,_toProviderTools.fromProviderToolName)(l.name,n);if(!r||r==="ask-buoy")return false;const i=l.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:x}).verdict==="allow"});if(e){for(const l of w){const r=(0,_toProviderTools.fromProviderToolName)(l.name,n);const i=String(l.input.action);const b={...l.input.params??{}};const f=findDescriptor(n,r,i);const _=(0,_normalizeParams.normalizeParams)(r,i,b);const a=(0,_validateParams.applyDefaults)(_.params,f.params);if(!(0,_validateParams.validateParams)(a,f.params).ok)continue;const p=Promise.resolve(d(r,i,a)).catch(H=>{throw H});p.catch(()=>{});_e.set(l.id,p)}}}let ue=null;const ze=e=>{const l=(0,_toProviderTools.fromProviderToolName)(e.name,n)??e.name;const r=typeof e.input?.action==="string"?e.input.action:"";const i=r?findDescriptor(n,l,r):void 0;const b=e.input?.params??{};return[{type:"tool-start",id:e.id,toolId:l,action:r,label:i?(0,_policy.describeCall)(l,i,b):`${l}.${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 l=(0,_toProviderTools.fromProviderToolName)(e.name,n);const r=typeof e.input?.action==="string"?e.input.action:void 0;if(!l||!r)return void 0;const i=findDescriptor(n,l,r);if(!i)return void 0;const b=e.input.params??{};const f=(0,_validateParams.applyDefaults)((0,_normalizeParams.normalizeParams)(l,r,b).params,i.params);if(!(0,_validateParams.validateParams)(f,i.params).ok)return void 0;const _=(0,_policy.decide)({descriptor:i,toolId:l,policy:P,isRelease:x});return{call:e,toolId:l,action:r,descriptor:i,params:f,label:(0,_policy.describeCall)(l,i,f),mutates:i.effect!=="read",gated:_.verdict==="needs-approval",reason:_.verdict==="needs-approval"?_.reason:""}});const Fe=Ne.some(e=>e?.gated);const M=new Map;let Ee=false;let $e=false;async function*Ge(){Ee=true;for(const e of Ne){if(!e?.gated||M.has(e.call.id))continue;if(A?.has(`${e.toolId}.${e.action}`)){M.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 l=(0,_realNow.realNow)();const r=await readTargetDigest({toolId:e.toolId,action:e.action,params:e.params,dispatch:d,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)()-l;const b=i.approved;M.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){$e=true;return}}}for(const e of w){if($?.aborted){if(e.name!==_uiTool.UI_TOOL_NAME){for(const s of ze(e))yield s}k.push({toolCallId:e.id,content:"Not run \u2014 the user pressed Stop before this call started.",isError:true});continue}const l=e.input?.[_types.UNPARSEABLE_ARGS];if(typeof l==="string"){k.push({toolCallId:e.id,content:`Not run \u2014 this call's arguments were not valid JSON, so nothing changed (${l.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")}
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 _turnScope=require("../policy/turnScope");var _policy=require("../policy/policy");var _redact=require("../policy/redact");var _idGrounding=require("./idGrounding");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 n=o=>{if(o===null||typeof o!=="object")return o;if(Array.isArray(o))return o.map(n);const a=o;return Object.keys(a).sort().reduce((d,R)=>(d[R]=n(a[R]),d),{})};return t.map(o=>`${o.name}:${JSON.stringify(n(o.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 CONTINUE_SEND="Resume where you left off. Don't redo past changes. Read the past tool results first.";const STEP_CAP_WRAP_UP="You are out of steps. No more tools may run. Reply in the user's language with plain words. Name every write that ran, what you found, and what is still not done. Use the tool results as proof. A tap alone does not prove a save or an order worked. Do not claim the job is done if you have not checked it. Keep the reply short.";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,n){return new Promise(o=>{if(n?.aborted)return o();const a=(0,_realNow.realSetTimeout)(d,t);function d(){(0,_realNow.realClearTimeout)(a);n?.removeEventListener("abort",d);o()}n?.addEventListener("abort",d,{once:true})})}function readAnswer(t){if(typeof t==="boolean")return{approved:t,trust:false};const n=t.reason?.trim();return{approved:t.approved,...n?{reason:n}:{},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 closeAction(t,n){const o=t.toLowerCase();const a=n.toLowerCase();if(Math.abs(o.length-a.length)>1)return false;let d=0;while(d<o.length&&o[d]===a[d])d++;return o===a||o.slice(d+1)===a.slice(d+1)||o.slice(d+1)===a.slice(d)||o.slice(d)===a.slice(d+1)}function findDescriptor(t,n,o){return t.find(a=>a.toolId===n)?.actions.find(a=>a.action===o)}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,n){if(t.length<=MAX_RESULT_CHARS)return t;return`${t.slice(0,MAX_RESULT_CHARS)}${(0,_evidence.truncationMarker)(t.length,n)}`}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 n=t;if(n.ok===false)return typeof n.error==="string"?n.error:"Failed";if(n.found===false)return"Not found";if(n.tapped===false)return"Nothing to tap";if(n.active===false)return"Saved, but inactive in this build";for(const a of["returned","count","total"]){const d=n[a];if(typeof d==="number")return countOf(d)}const o=Object.values(n).filter(Array.isArray);if(o.length===1)return countOf(o[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}const EMPTY_TAP_ERROR="tapElement needs nativeTag, testID or query. Read describeScreen and pass one.";function emptyTap(t,n,o){return t==="highlight-updates"&&n==="tapElement"&&![o.nativeTag,o.testID,o.query].some(a=>typeof a==="string"?a.trim().length>0:a!=null)}function tapResult(t,n,o){if(t!=="highlight-updates"||n!=="tapElement"||!o||typeof o!=="object")return o;const a=o;if(a.tapped!==false)return o;return{...a,ok:false,error:typeof a.reason==="string"?a.reason:typeof a.error==="string"?a.error:"Nothing was tapped. Read describeScreen and try again."}}async function*runAgentTurn(t){const{provider:n,catalog:o,system:a,systemVolatile:d,model:R,maxTokens:b,dispatch:c,readSnapshot:g,storeKeyOwners:q,ledger:k,policy:x,isRelease:L,requestApproval:j,signal:A,evidence:B,trusted:N,procedures:M}=t;const z=new Map;const O=t.calibration??new _tokenCalibration.TokenCalibration;const Ee=[...t.messages].reverse().find(y=>y.role==="user");const U=(0,_turnScope.classifyTurn)(Ee?.role==="user"?Ee.text:"");const h=[...t.messages];const Re=(0,_idGrounding.idGrounding)(h,`${t.system}
2
+ ${t.systemVolatile??""}
3
+ ${JSON.stringify(M??[])}`);const te=(0,_toProviderTools.toProviderTools)(o,{availableToolIds:t.availableToolIds,availableActions:t.availableActions,isRelease:L});te.push((0,_uiTool.uiProviderTool)());const oe=x.maxSteps??DEFAULT_MAX_STEPS;const Ne=newAskId();const Qe=k.generation;const Ke=new Set(k.list().map(y=>y.id));const Ae=a.length+(d?.length??0)+te.reduce((y,_)=>y+_.description.length+JSON.stringify(_.inputSchema).length,0);const ce=()=>Ae+O.chars(b);let re=(0,_realNow.realNow)()+DEFAULT_TURN_MS;const ue={throttled:0,overflow:0};let V=_historyBudget.MAX_HISTORY_TOKENS;const fe=t.seenUrls??new Set;const Ve=/https?:\/\/[^\s"'\\)\]}>]+/g;let pe=false;const F=[];let _e=false;const he=[];let ye=false;let $e=false;let ne=false;let Ce=false;let Oe=false;const Xe=[...h].reverse().find(y=>y.role==="user");const Ze=/\?\s*$/.test(Xe?.text?.trim()??"");for(let y=0;y<oe;y++){if(A?.aborted){yield{type:"done",stopReason:"stopped"};return h}if((0,_realNow.realNow)()>re){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_SEND}]}};yield{type:"done",stopReason:"time-cap"};return h}const _=(0,_historyBudget.budgetForRequest)(h,ce(),O.chars(V),O.charsPerToken,k.liveCallIds());if(_.droppedRounds>0){h.length=0;h.push(..._.messages);yield{type:"history-trimmed",droppedRounds:_.droppedRounds,droppedPinned:_.droppedPinned}}else if(_.messages!==h){h.length=0;h.push(..._.messages)}let C="";let Le=false;const H=e=>{const l=pe&&!Le;Le=true;pe=true;return l?{type:"text",delta:e,breakBefore:true}:{type:"text",delta:e}};let T=[];const Ue=new Set;let P="";let me=true;let X=[];let se=false;let ie;let W;let be=0;for(;;){be+=1;C="";T=[];P="";me=true;X=[];se=false;ie=void 0;W=void 0;let e;const l=Ae+h.reduce((u,p)=>u+(0,_historyBudget.messageSize)(p),0);for await(const u of n.send({messages:h,system:a,systemVolatile:d,tools:te,model:R,maxTokens:b,signal:A,meta:{askId:Ne,round:y+1,attempt:be}})){if(u.type==="text"){C+=u.delta;if(!me){yield H(u.delta)}else if((0,_textToolCalls.couldBeToolCallEnvelope)(C)){P+=u.delta}else{me=false;P="";yield H(C)}}else if(u.type==="tool-call"){T.push(u.call)}else if(u.type==="meter"){yield{type:"meter",frame:u.frame}}else if(u.type==="thinking"){X.push(u.block);if(u.block.type==="thinking"&&u.block.thinking.trim()){yield{type:"reasoning",text:u.block.thinking}}}else if(u.type==="error"){if(u.kind==="truncated"||u.kind==="timeout"){W={message:u.message}}else if(u.problem&&(u.problem.kind==="throttled"||u.problem.kind==="overflow")&&C===""&&T.length===0&&X.length===0){e={problem:u.problem,message:u.message}}else{yield{type:"error",message:u.message,...u.problem?.hosted?{hosted:u.problem.hosted}:{}};se=true}}else if(u.type==="done"){ie=u.outcome;if(u.usage){O.observe(l,(0,_tokenCalibration.promptTokensOf)(u.usage,n.protocol));yield{type:"usage",...u.usage,model:u.model}}}}if(!e)break;const r=e.problem.kind;const s=1+(r==="throttled"?THROTTLE_RETRIES:OVERFLOW_RETRIES);const m=ue[r];let f=r==="throttled"?Math.min(e.problem.retryAfterMs??RETRY_BASE_MS*2**m,RETRY_AFTER_CAP_MS)+Math.floor(Math.random()*RETRY_JITTER_MS):0;let $=m>=s-1||A?.aborted===true||(0,_realNow.realNow)()+f>re;if(e.problem.hosted&&be>=2)$=true;if(!$&&r==="overflow"){V=Math.floor(V/2);const u=h.reduce((Z,Y)=>Z+(0,_historyBudget.messageSize)(Y),0);const p=(0,_historyBudget.budgetForRequest)(h,ce(),O.chars(V),O.charsPerToken,k.liveCallIds());const J=p.messages.reduce((Z,Y)=>Z+(0,_historyBudget.messageSize)(Y),0);if(J>=u){$=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 u=r==="throttled"?m>0?`The AI endpoint is rate-limiting requests \u2014 tried ${m+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.";const p=e.problem.hosted;yield p?{type:"error",message:p.message,hosted:p}:{type:"error",message:`${u} (${e.message})`};se=true;break}ue[r]+=1;yield{type:"retrying",reason:r,attempt:ue[r]+1,maxAttempts:s,inMs:f};if(f>0)await sleep(f,A);if(A?.aborted){yield{type:"done",stopReason:"stopped"};return h}}if(se){if(P)yield H(P);yield{type:"done",stopReason:"error"};return h}if(!W&&ie!=="completed"){W={message:ie==="output-limited"?T.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(W){if(P)yield H(P);if(C.trim())h.push({role:"assistant",text:C});yield{type:"block",block:{id:`cut${(0,_realNow.realNow)()}`,kind:"notice",tone:"warning",text:W.message,actions:[{label:"Continue",primary:true,send:CONTINUE_SEND}]}};yield{type:"done",stopReason:"incomplete"};return h}if(P){const e=T.length===0?(0,_textToolCalls.parseTextToolCalls)(P,o):void 0;if(e){for(const l of e.calls){T.push(l);Ue.add(l.id)}C=e.reply;if(e.reply)yield H(e.reply)}else{yield H(P)}P=""}h.push({role:"assistant",text:C,toolCalls:T.length?T:void 0,thinking:X.length?X:void 0});if(C.trim())he.push(C.trim());if(T.length===0&&!ne&&!ye&&y<oe-1&&((0,_planOnlyStop.announcesUnstartedWork)(C,U==="task")||Ce&&!Oe&&!Ze)){ne=true;h.push({role:"user",text:_planOnlyStop.PLAN_ONLY_NUDGE});continue}if(T.length===0&&!ne&&he.length===0&&y<oe-1){ne=true;h.push({role:"user",text:_planOnlyStop.SILENT_NUDGE});continue}if(T.length===0){const e=he.join("\n\n");const l=(0,_askGate.synthesizeShortfallBlock)({text:e,attempted:$e});if(l)yield{type:"block",block:l};const r=ye||l?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 v=[];let De=false;const Me=new Map;if(T.length>1){const e=T.every(l=>{if(l.name===_uiTool.UI_TOOL_NAME)return false;const r=(0,_toProviderTools.fromProviderToolName)(l.name,o);if(!r||r==="ask-buoy")return false;const s=l.input?.action;if(typeof s!=="string"||s===_snapshotReads.SNAPSHOT_ACTION)return false;const m=findDescriptor(o,r,s);if(!m||m.effect!=="read")return false;return(0,_policy.decide)({descriptor:m,toolId:r,policy:x,isRelease:L}).verdict==="allow"});if(e){for(const l of T){const r=(0,_toProviderTools.fromProviderToolName)(l.name,o);const s=String(l.input.action);const m={...l.input.params??{}};const f=findDescriptor(o,r,s);const $=(0,_normalizeParams.normalizeParams)(r,s,m);const u=(0,_validateParams.applyDefaults)($.params,f.params);if(!(0,_validateParams.validateParams)(u,f.params).ok)continue;const p=Promise.resolve(c(r,s,u)).catch(J=>{throw J});p.catch(()=>{});Me.set(l.id,p)}}}let ke=null;const ot=e=>{const l=(0,_toProviderTools.fromProviderToolName)(e.name,o)??e.name;const r=typeof e.input?.action==="string"?e.input.action:"";const s=r?findDescriptor(o,l,r):void 0;const m=e.input?.params??{};return[{type:"tool-start",id:e.id,toolId:l,action:r,label:s?(0,_policy.describeCall)(l,s,m):`${l}.${r}`,description:s?.summary??"",params:m,effect:s?.effect??"read"},{type:"tool-end",id:e.id,ok:false,summary:"stopped",durationMs:0}]};const ae=new Map;if(U==="question"){for(const e of T){if((0,_toProviderTools.fromProviderToolName)(e.name,o)!=="highlight-updates"||e.input?.action!=="tapElement")continue;const l=(0,_normalizeParams.normalizeParams)("highlight-updates","tapElement",e.input.params??{}).params;if(emptyTap("highlight-updates","tapElement",l))continue;const r=findDescriptor(o,"highlight-updates","describeScreen");const s=findDescriptor(o,"highlight-updates","tapElement");if(A?.aborted||!r||!s||(0,_policy.decide)({descriptor:r,toolId:"highlight-updates",policy:x,isRelease:L}).verdict!=="allow"||(0,_policy.decide)({descriptor:s,toolId:"highlight-updates",policy:x,isRelease:L,turnScope:U,params:l}).verdict==="refuse")continue;const m=await c("highlight-updates","describeScreen",{}).catch(()=>void 0);ae.set(e.id,(0,_turnScope.tapLabels)(m,l))}}const je=T.map(e=>{if(e.name===_uiTool.UI_TOOL_NAME)return void 0;const l=(0,_toProviderTools.fromProviderToolName)(e.name,o);const r=typeof e.input?.action==="string"?e.input.action:void 0;if(!l||!r)return void 0;const s=findDescriptor(o,l,r);if(!s)return void 0;const m=e.input.params??{};const f=(0,_validateParams.applyDefaults)((0,_normalizeParams.normalizeParams)(l,r,m).params,s.params);if(emptyTap(l,r,f)||!(0,_validateParams.validateParams)(f,s.params).ok)return void 0;const $=(0,_policy.decide)({descriptor:s,toolId:l,policy:x,isRelease:L,turnScope:U,params:f,tapLabels:ae.get(e.id)});return{call:e,toolId:l,action:r,descriptor:s,params:f,label:(0,_policy.describeCall)(l,s,f),mutates:s.effect!=="read",gated:$.verdict==="needs-approval",turnScoped:$.verdict==="needs-approval"&&$.turnScoped,reason:$.verdict==="needs-approval"?$.reason:""}});const rt=je.some(e=>e?.gated);const D=new Map;let Be=false;let ze=false;async function*nt(){Be=true;for(const e of je){if(!e?.gated||D.has(e.call.id))continue;if(!e.turnScoped&&N?.has(`${e.toolId}.${e.action}`)){D.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 l=(0,_realNow.realNow)();const r=await readTargetDigest({toolId:e.toolId,action:e.action,params:e.params,dispatch:c,catalog:o});const s=readAnswer(!e.turnScoped&&N?.has(`${e.toolId}.${e.action}`)?true:j?await j({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);re+=(0,_realNow.realNow)()-l;const m=s.approved;D.set(e.call.id,m);if(s.trust)N?.add(`${e.toolId}.${e.action}`);if(s.reason)z.set(e.call.id,s.reason);if(!m){ze=true;return}}}for(const e of T){if(A?.aborted){if(e.name!==_uiTool.UI_TOOL_NAME){for(const i of ot(e))yield i}v.push({toolCallId:e.id,content:"Not run \u2014 the user pressed Stop before this call started.",isError:true});continue}const l=e.input?.[_types.UNPARSEABLE_ARGS];if(typeof l==="string"){v.push({toolCallId:e.id,content:`Not run \u2014 this call's arguments were not valid JSON, so nothing changed (${l.slice(0,120)}). Send the call again with compact JSON: {"action": "...", "params": {...}}.`,isError:true});continue}if(e.name===_uiTool.UI_TOOL_NAME){const i=(0,_uiTool.buildUiBlock)(e.input);if(!i.ok||!i.block){v.push({toolCallId:e.id,content:`Invalid ${_uiTool.UI_TOOL_NAME} block:
4
+ ${i.errors.join("\n")}
3
5
 
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)){ue=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 a=(0,_normalizeParams.normalizeParams)(r,i,_);const p=(0,_validateParams.applyDefaults)(a.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
+ 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 S=0;if(i.block.kind==="imageGrid"){const E=i.block.images.filter(I=>fe.has(I.url));S=i.block.images.length-E.length;if(E.length===0){v.push({toolCallId:e.id,content:URL_PROVENANCE_REJECTION,isError:true});continue}i.block={...i.block,images:E}}if(i.block.kind==="list"){const E=i.block.items.map(I=>{if(!I.image||fe.has(I.image))return I;S+=1;return{...I,image:void 0}});i.block={...i.block,items:E}}yield{type:"block",block:i.block,forToolCallId:e.id};if(i.block.kind==="actions"||i.block.kind==="suggestions"||i.block.kind==="plan"&&i.block.pending){ye=true}if((0,_types2.isAskBlock)(i.block)){ke=i.block.id;v.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{v.push({toolCallId:e.id,content:"Shown to the user. Don't repeat its contents in text."+(S>0?droppedImageNote(S):"")})}continue}const r=(0,_toProviderTools.fromProviderToolName)(e.name,o);const s=e.input.action;const m=(i,S)=>[{type:"tool-start",id:e.id,toolId:r??e.name,action:s??"",label:i,description:"",params:e.input.params??{},effect:"read"},{type:"tool-end",id:e.id,ok:false,summary:S,durationMs:0}];if(!r){for(const i of m(e.name,"No such tool"))yield i;v.push({toolCallId:e.id,content:`There is no tool called "${e.name}". Use one of the tools you were given.`,isError:true});continue}if(!s){for(const i of m(e.name,"No action given"))yield i;v.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(o,r,s);if(!f){const i=o.find(w=>w.toolId===r)?.actions.map(w=>w.action)??[];const S=o.filter(w=>w.toolId!==r&&w.actions.some(Te=>Te.action===s));const E=i.filter(w=>closeAction(w,s));const I=[...S.map(w=>`${w.toolId}.${s}`),...E.map(w=>`${r}.${w}`)].slice(0,3);const K=`${r} has no ${s}.${I.length?` Did you mean ${I.join(" or ")}?`:""}`;for(const w of m(`${r}.${s}`,I.length?K:"No such action"))yield w;v.push({toolCallId:e.id,content:`${K}${i.length?` Valid actions: ${i.join(", ")}.`:""}`,isError:true});continue}const $=e.input.params??{};const u=(0,_normalizeParams.normalizeParams)(r,s,$);const p=(0,_validateParams.applyDefaults)(u.params,f.params);if(emptyTap(r,s,p)){for(const i of m(`${r}.${s}`,EMPTY_TAP_ERROR))yield i;v.push({toolCallId:e.id,content:JSON.stringify({ok:false,error:EMPTY_TAP_ERROR}),isError:true});continue}const J=(0,_validateParams.validateParams)(p,f.params);if(!J.ok){v.push({toolCallId:e.id,content:`Invalid parameters for ${r}.${s}:
7
+ ${J.errors.join("\n")}
6
8
 
7
9
  Expected schema:
8
- ${JSON.stringify(f.params)}`,isError:true});continue}const J=(0,_normalizeParams.aliasNote)(a.notes);const L=(0,_policy.describeCall)(r,f,p);const Y=(0,_policy.decide)({descriptor:f,toolId:r,policy:P,isRelease:x});const K=s=>[{type:"tool-start",id:e.id,toolId:r,action:i,label:L,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 fe=Y.verdict==="needs-approval";if(Fe&&!Ee&&(f.effect!=="read"||fe)){yield*Ge()}if($e&&(f.effect!=="read"||fe)){for(const s of K("declined"))yield s;k.push({toolCallId:e.id,content:M.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(fe){let s;if(M.has(e.id)){s=M.get(e.id)}else if(A?.has(`${r}.${i}`)){s=true;M.set(e.id,true)}else{yield{type:"approval-required",id:e.id,toolId:r,action:i,label:L,description:f.summary,reason:Y.reason};const m=(0,_realNow.realNow)();const S=await readTargetDigest({toolId:r,action:i,params:p,dispatch:d,catalog:n});const R=readAnswer(U?await U({id:e.id,toolId:r,action:i,label:L,description:f.summary,reason:Y.reason,params:p,targetDigest:S}):false);X+=(0,_realNow.realNow)()-m;s=R.approved;M.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}`))we=true;if(r==="ask-buoy"&&i==="openProcedure")Te=true;else if(f.effect!=="read")Se=true;yield{type:"tool-start",id:e.id,toolId:r,action:i,label:L,description:f.summary,params:p,effect:f.effect};const Ce=(0,_realNow.realNow)();const Oe=await captureBefore(f,r,p,d);const pe=await captureStoreState(f,r,p,d);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 qe=(0,_policy.decide)({descriptor:f,toolId:r,policy:P,isRelease:x});if(qe.verdict==="refuse"){for(const s of K("refused"))yield s;k.push({toolCallId:e.id,content:qe.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,d,D,p,j):await(_e.get(e.id)??d(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 he=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,he);const He=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)()-Ce,result:He};const ye=pe===void 0||S?void 0:await captureStoreState(f,r,p,d);if(f.effect!=="read"&&r!=="ask-buoy"&&!S){const me=v.noteExternalRevert(r,i,p);const xe=(0,_effectFor.effectFor)(r,f,p,s,Oe,{before:pe,after:ye});if(xe&&!me){const Me=v.record(r,i,xe,Ue);if(Me)Me.callId=e.id}}const Pe=S?void 0:(0,_receipts.projectReceipt)({toolId:r,action:i,params:p,result:m,before:pe??Oe,after:ye,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:d,readSnapshot:y,signal:$,after:ye}):void 0;if(oe)yield{type:"tool-verified",id:e.id,verification:oe};for(const me of Q.match(De)??[])se.add(me);k.push({toolCallId:e.id,content:Q+ownedKeyNote(r,f,p,E)+J+(oe?(0,_verify.verificationTrailer)(oe):"")+(Re.has(e.id)?_textToolCalls.SALVAGE_NOTE:""),...he?{ref:he}:{}});if(r==="app"&&(i==="reloadApp"||i==="reload")){Ie=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)()-Ce};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=!ve&&z.length===LOOP_REPEATS&&z.every(l=>l===z[0]);if(e){ve=true;const l=k[k.length-1];if(l)l.content=`${l.content}
10
+ ${JSON.stringify(f.params)}`,isError:true});continue}const Z=(0,_normalizeParams.aliasNote)(u.notes);const Y=f.effect!=="read"?Re.trailer(p):"";const de=(0,_policy.describeCall)(r,f,p);const Q=(0,_policy.decide)({descriptor:f,toolId:r,policy:x,isRelease:L,turnScope:U,params:p,tapLabels:ae.get(e.id)});const ee=i=>[{type:"tool-start",id:e.id,toolId:r,action:s,label:de,description:f.summary,params:p,effect:f.effect},{type:"tool-end",id:e.id,ok:false,summary:i,durationMs:0}];if(Q.verdict==="refuse"){for(const i of ee("refused"))yield i;v.push({toolCallId:e.id,content:Q.reason,isError:true});continue}const ve=Q.verdict==="needs-approval";if(rt&&!Be&&(f.effect!=="read"||ve)){yield*nt()}if(ze&&(f.effect!=="read"||ve)){for(const i of ee("declined"))yield i;v.push({toolCallId:e.id,content:U==="question"?_turnScope.QUESTION_DECLINED:D.get(e.id)===false?declinedResult(z.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(ve){let i;if(D.has(e.id)){i=D.get(e.id)}else if(!Q.turnScoped&&N?.has(`${r}.${s}`)){i=true;D.set(e.id,true)}else{yield{type:"approval-required",id:e.id,toolId:r,action:s,label:de,description:f.summary,reason:Q.reason};const S=(0,_realNow.realNow)();const E=await readTargetDigest({toolId:r,action:s,params:p,dispatch:c,catalog:o});const I=readAnswer(j?await j({id:e.id,toolId:r,action:s,label:de,description:f.summary,reason:Q.reason,params:p,targetDigest:E}):false);re+=(0,_realNow.realNow)()-S;i=I.approved;D.set(e.id,i);if(I.trust)N?.add(`${r}.${s}`);if(I.reason)z.set(e.id,I.reason)}if(!i){for(const S of ee("declined"))yield S;v.push({toolCallId:e.id,content:U==="question"?_turnScope.QUESTION_DECLINED:declinedResult(z.get(e.id)),isError:true});continue}}if(_askGate.ACQUISITION_CALLS.has(`${r}.${s}`))$e=true;if(r==="ask-buoy"&&s==="openProcedure")Ce=true;yield{type:"tool-start",id:e.id,toolId:r,action:s,label:de,description:f.summary,params:p,effect:f.effect};const Fe=(0,_realNow.realNow)();const Ge=await captureBefore(f,r,p,c);const we=await captureStoreState(f,r,p,c);if(A?.aborted){for(const i of ee("stopped"))yield i;v.push({toolCallId:e.id,content:"Not run \u2014 the user pressed Stop before this call started.",isError:true});continue}const He=(0,_policy.decide)({descriptor:f,toolId:r,policy:x,isRelease:L,turnScope:U,params:p,tapLabels:ae.get(e.id)});if(He.verdict==="refuse"){for(const i of ee("refused"))yield i;v.push({toolCallId:e.id,content:He.reason,isError:true});continue}try{let i=s===_snapshotReads.SNAPSHOT_ACTION?(0,_snapshotReads.projectSnapshot)(r,await requireSnapshot(g,r),p):r==="ask-buoy"?await runAskBuoyAction(s,k,c,B,p,M):await(Me.get(e.id)??c(r,s,p));i=tapResult(r,s,i);const S=(0,_redact.redact)((0,_redact.stripSelfTraffic)(i));const E=reportsFailure(i);if(f.effect!=="read"&&!E)Oe=true;const I=fullText(r==="highlight-updates"&&s==="describeScreen"&&!E?(0,_compactScreen.compactScreen)(S):S);const K=B&&!E&&!(r==="ask-buoy"&&s==="retrieve")?B.stash({toolId:r,action:s,params:p,capturedAt:(0,_realNow.realNow)(),text:I}):void 0;const w=encodeResult(I,K);Re.add(I);const Te=w.length>MAX_TRACE_RESULT_CHARS?`${w.slice(0,MAX_TRACE_RESULT_CHARS)}\u2026`:w;yield{type:"tool-end",id:e.id,ok:!E,summary:summarise(i),durationMs:(0,_realNow.realNow)()-Fe,result:Te};const Se=we===void 0||E?void 0:await captureStoreState(f,r,p,c);if(f.effect!=="read"&&r!=="ask-buoy"&&!E){const Ie=k.noteExternalRevert(r,s,p);const Je=(0,_effectFor.effectFor)(r,f,p,i,Ge,{before:we,after:Se});if(Je&&!Ie){const Ye=k.record(r,s,Je,Qe);if(Ye)Ye.callId=e.id}}const We=E?void 0:(0,_receipts.projectReceipt)({toolId:r,action:s,params:p,result:S,before:we??Ge,after:Se,effect:f.effect});if(We)yield{type:"block",block:We,forToolCallId:e.id};const le=f.effect!=="read"&&!E?await(0,_verify.verifyOutcome)({toolId:r,action:s,params:p,result:i,dispatch:c,readSnapshot:g,signal:A,after:Se}):void 0;if(le)yield{type:"tool-verified",id:e.id,verification:le};for(const Ie of w.match(Ve)??[])fe.add(Ie);v.push({toolCallId:e.id,content:w+ownedKeyNote(r,f,p,q)+Z+Y+(le?(0,_verify.verificationTrailer)(le):"")+(Ue.has(e.id)?_textToolCalls.SALVAGE_NOTE:""),...K?{ref:K}:{}});if(r==="app"&&(s==="reloadApp"||s==="reload")){De=true}}catch(i){const S=i instanceof Error?i.message:String(i);yield{type:"tool-end",id:e.id,ok:false,summary:S,durationMs:(0,_realNow.realNow)()-Fe};v.push({toolCallId:e.id,content:`Failed: ${S}${Y}`,isError:true})}}if(T.length){F.push(callSignature(T));if(F.length>LOOP_REPEATS)F.shift();const e=!_e&&F.length===LOOP_REPEATS&&F.every(l=>l===F[0]);if(e){_e=true;const l=v[v.length-1];if(l)l.content=`${l.content}
9
11
 
10
- ${LOOP_WARNING}`}}h.push({role:"tool-results",results:k});if(ue){yield{type:"awaiting-user",blockId:ue};yield{type:"done",stopReason:"awaiting-user"};return h}if(Ie){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`
12
+ ${LOOP_WARNING}`}}h.push({role:"tool-results",results:v});if(ke){yield{type:"awaiting-user",blockId:ke};yield{type:"done",stopReason:"awaiting-user"};return h}if(De){yield{type:"done",stopReason:"realm-died"};return h}}if(A?.aborted){yield{type:"done",stopReason:"stopped"};return h}const G=[];let Pe=false;let ge=false;const et=[...h,{role:"user",text:STEP_CAP_WRAP_UP}];const tt=(0,_historyBudget.budgetForRequest)(et,ce(),O.chars(V),O.charsPerToken,k.liveCallIds());try{for await(const y of n.send({messages:tt.messages,system:a,systemVolatile:d,tools:te,toolChoice:"none",model:R,maxTokens:b,signal:A,meta:{askId:Ne,round:oe+1,attempt:1}})){if(y.type==="text")G.push(y.delta);else if(y.type==="tool-call"||y.type==="error")ge=true;else if(y.type==="meter")yield{type:"meter",frame:y.frame};else if(y.type==="done"){Pe=y.outcome==="completed";if(y.usage)yield{type:"usage",...y.usage,model:y.model}}}}catch{ge=true}if(A?.aborted){yield{type:"done",stopReason:"stopped"};return h}const qe=G.join("");if(ge||!Pe||!qe.trim()||(0,_textToolCalls.couldBeToolCallEnvelope)(qe)){const y=k.list().filter(_=>!Ke.has(_.id));G.length=0;G.push(["I ran out of steps. The job is not done.",...y.length?["These steps ran:",...y.map(_=>`- ${_.effect.label}${_.undoneAt?" (since undone)":""}`),"This list does not prove each change worked."]:[]].join("\n"))}let xe=true;for(const y of G){if(!y)continue;yield{type:"text",delta:y,...xe&&pe?{breakBefore:true}:{}};xe=false}h.push({role:"assistant",text:G.join("")});yield{type:"block",block:{id:`cap${(0,_realNow.realNow)()}`,kind:"notice",tone:"warning",text:"I ran out of steps. The job is not done. Tap Continue to go on.",actions:[{label:"Continue",primary:true,send:CONTINUE_SEND}]}};yield{type:"done",stopReason:"step-cap"};return h}function ownedKeyNote(t,n,o,a){if(t!=="storage"||n.effect==="read")return"";if(!a||typeof o.key!=="string")return"";const d=a[o.key];if(!d)return"";return`
11
13
 
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 d=t.catalog??_catalog.CATALOG;const y=findDescriptor(d,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 x=(0,_policy.decide)({descriptor:y,toolId:o,policy:T,isRelease:g});if(x.verdict==="refuse"){return{ok:false,error:x.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 d=T?.find(y=>y.id===g);if(!d){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:d.id,title:d.title,...d.version?{version:d.version}:{},...d.requires?.length?{requires:d.requires}:{},body:d.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(d=>!d.undoneAt).map(d=>({id:d.id,toolId:d.toolId,action:d.action,kind:d.effect.kind,label:d.effect.label,reversible:(0,_ledger.isReversible)(d.effect)}));return{changes:g,returned:g.length}}if(t==="undoChange"){const g=typeof u?.id==="string"?u.id:"";const d=o.list().find(E=>E.id===g);if(!d){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(d.undoneAt)return{ok:false,error:`"${d.effect.label}" was already undone.`};if(!(0,_ledger.isReversible)(d.effect)){return{ok:false,error:`"${d.effect.label}" can't be undone automatically \u2014 it is permanent.`}}const y=await o.revertOne(d,n);return y?{ok:false,error:y}:{ok:true,undone:{id:d.id,label:d.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 d=g&&typeof g==="object"?g:void 0;if(d?.found===false)return void 0;const y=d?d.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}}function newAskId(){const t="abcdefghijklmnopqrstuvwxyz0123456789";let o="ask_";for(let n=0;n<20;n++)o+=t[Math.floor(Math.random()*t.length)];return o}
14
+ [Buoy] This wrote to disk, but "${o.key}" is the saved copy of the live "${d}" 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":"${d}"}) 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 n=findDescriptor(t.catalog??_catalog.CATALOG,t.toolId,t.action);if(!n||n.effect==="read")return void 0;const o=await captureStoreState(n,t.toolId,t.params,t.dispatch);if(o!==void 0)return(0,_digest.digestOf)(o);const a=await captureBefore(n,t.toolId,t.params,t.dispatch);if(a!==void 0)return(0,_digest.digestOf)(a);return void 0}async function runGatedAction(t){const{toolId:n,action:o,dispatch:a,ledger:d,policy:R,isRelease:b}=t;const c=t.catalog??_catalog.CATALOG;const g=findDescriptor(c,n,o);if(!g){return{ok:false,error:`${n}.${o} is not in Buoy's catalog.`}}const q=(0,_normalizeParams.normalizeParams)(n,o,t.params??{});const k=(0,_validateParams.applyDefaults)(q.params,g.params);if(emptyTap(n,o,k))return{ok:false,error:EMPTY_TAP_ERROR};const x=(0,_validateParams.validateParams)(k,g.params);if(!x.ok){return{ok:false,error:x.errors.join("\n")}}const L=(0,_policy.decide)({descriptor:g,toolId:n,policy:R,isRelease:b});if(L.verdict==="refuse"){return{ok:false,error:L.reason}}const j=await captureBefore(g,n,k,a);const A=await captureStoreState(g,n,k,a);const B=(0,_policy.decide)({descriptor:g,toolId:n,policy:R,isRelease:b});if(B.verdict==="refuse"){return{ok:false,error:B.reason}}try{let N=n==="ask-buoy"?await runAskBuoyAction(o,d,a,void 0,k):await a(n,o,k);N=tapResult(n,o,N);if(reportsFailure(N)){const M=N.error;return{ok:false,error:typeof M==="string"?M:"The tool refused this."}}if(g.effect!=="read"&&n!=="ask-buoy"){const M=d.noteExternalRevert(n,o,k);const z=await captureStoreState(g,n,k,a);const O=(0,_effectFor.effectFor)(n,g,k,N,j,{before:A,after:z});if(O&&!M)d.record(n,o,O)}return{ok:true,result:N}}catch(N){return{ok:false,error:N instanceof Error?N.message:String(N)}}}async function runAskBuoyAction(t,n,o,a,d,R){if(t==="retrieve"){return(0,_retrieve.retrieveEvidence)(a,d??{})}if(t==="openProcedure"){const b=typeof d?.id==="string"?d.id:"";const c=R?.find(g=>g.id===b);if(!c){const g=(R??[]).map(q=>q.id);return{ok:false,error:g.length?`No procedure "${b}". This app's procedures: ${g.join(", ")}.`:"This app has no procedures written for it."}}return{id:c.id,title:c.title,...c.version?{version:c.version}:{},...c.requires?.length?{requires:c.requires}:{},body:c.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 b=n.list().filter(c=>!c.undoneAt).map(c=>({id:c.id,toolId:c.toolId,action:c.action,kind:c.effect.kind,label:c.effect.label,reversible:(0,_ledger.isReversible)(c.effect)}));return{changes:b,returned:b.length}}if(t==="undoChange"){const b=typeof d?.id==="string"?d.id:"";const c=n.list().find(q=>q.id===b);if(!c){const q=n.list().filter(k=>!k.undoneAt).map(k=>k.id);return{ok:false,error:`No change with id "${b}". Call listChanges for the current ids${q.length?` (${q.join(", ")})`:""}.`}}if(c.undoneAt)return{ok:false,error:`"${c.effect.label}" was already undone.`};if(!(0,_ledger.isReversible)(c.effect)){return{ok:false,error:`"${c.effect.label}" can't be undone automatically \u2014 it is permanent.`}}const g=await n.revertOne(c,o);return g?{ok:false,error:g}:{ok:true,undone:{id:c.id,label:c.effect.label}}}if(t==="undoAll"){const b=await n.revertAll(o);return{ok:b.failed.length===0,...b}}throw new Error(`Unknown ask-buoy action "${t}"`)}async function requireSnapshot(t,n){if(!t){throw new Error(`This app cannot read ${n}'s snapshot \u2014 Buoy's tool registry is not published here.`)}return await t(n)}async function captureStoreState(t,n,o,a){if(n==="zustand"&&t.action==="setState"){if(typeof o.storeName!=="string")return void 0;try{return await a(n,"getStoreState",{storeName:o.storeName})}catch{return void 0}}if(n==="query"&&t.action==="setQueryData"){const d=typeof o.queryHash==="string"?o.queryHash:Array.isArray(o.queryKey)?(0,_normalizeParams.hashQueryKey)(o.queryKey):void 0;if(!d)return void 0;try{return await a(n,"getQueryData",{queryHash:d})}catch{return void 0}}return void 0}async function captureBefore(t,n,o,a){if(t.effect==="read")return void 0;if(n==="network")return captureRuleSwitch(t,o,a);if(n!=="storage"||typeof o.key!=="string")return void 0;const d=t.action.startsWith("async.")?"async.getItem":t.action.startsWith("mmkv.")?"mmkv.get":void 0;if(!d)return void 0;const R={key:o.key};if(d==="mmkv.get"){if(typeof o.instanceId!=="string")return void 0;R.instanceId=o.instanceId}try{const b=await a(n,d,R);const c=b&&typeof b==="object"?b:void 0;if(c?.found===false)return void 0;const g=c?c.value:b;return{had:g!==void 0&&g!==null,value:g??void 0}}catch{return void 0}}async function captureRuleSwitch(t,n,o){if(t.action!=="setOverrideRuleEnabled"&&t.action!=="setOverridesEnabled"){return void 0}try{const a=await o("network","listOverrideRules",{});if(!a||typeof a!=="object")return void 0;if(t.action==="setOverridesEnabled"){return{had:true,value:String(a.enabled!==false)}}const d=(a.rules??[]).find(R=>R&&R.id===n.id);if(!d)return void 0;return{had:true,value:String(d.enabled!==false)}}catch{return void 0}}function newAskId(){const t="abcdefghijklmnopqrstuvwxyz0123456789";let n="ask_";for(let o=0;o<20;o++)n+=t[Math.floor(Math.random()*t.length)];return n}
@@ -14,6 +14,8 @@ You can read the app's live state and change it, through the Buoy tools availabl
14
14
  - When you change something, say plainly what you changed, in the user's language ("I put the double burger out of stock at store 100"), not in tool terms.
15
15
  - A WRITE'S RESULT IS CHECKED, AND THE CHECK TELLS YOU WHAT ACTUALLY HAPPENED. Some writes come back with a trailer: \`[Buoy] Verified: \u2026\` means the app was read back and shows the requested state \u2014 say it plainly. \`[Buoy] Unverified: \u2026\` means the write landed but the outcome is not visible yet (an override installed that no request has used) \u2014 do the thing the trailer names (a refetch, a visit to the screen) before saying the screen shows it, or say honestly that it will show on the next load. \`[Buoy] Check FAILED: \u2026\` means the app does NOT hold what you wrote: read the current state, work out why, and correct ONCE with a different, targeted change \u2014 never repeat the same write, never report the outcome as done.
16
16
  - If a tool returns an empty list, check whether the tool needed to be recording first. Several Buoy tools only capture while something is watching them, so an empty result often means "nothing was recording", not "nothing happened". Say which one it is.
17
+ - An old error can show where to look. It does not prove this try failed. For a vague "it is broken, fix it" with no steps, read first. Find the fault now, then ask which step fails. Do not place an order or change test settings for that vague ask. If the user asked you to place an order, refund it, or make a change, do it and check the result. Do not ask them to grant that same step again. Still obey tool approval checks.
18
+ - If you place an order or change state to test a fix, say so plainly in your reply.
17
19
  - A COUNT IS NOT A BUG UNTIL YOU HAVE CHECKED THE CLOCK. recentRequests and the events timeline accumulate for as long as the app has been open, so "23 calls to /orders/pending" is a polling interval times minutes, not one screen load, until the timestamps say otherwise. Before you call anything a bug, run the one check that would make it NOT one \u2014 the timestamps across the window, the query's refetchInterval, whether capture was on the whole time, whether the screen actually shows the wrong thing \u2014 and say in the finding what you checked. A finding you did not check is a guess wearing a card, and the person reading it cannot tell the two apart.
18
20
  - WHAT YOU READ IS A RECORD OF WHAT HAS ALREADY HAPPENED, never a list of what this app has. The query cache holds what the app has already fetched THIS SESSION; recentRequests what it has already called; console, events, images and assets only what was recording or has already rendered. So a SHORT list means "nobody has been there yet", not "that is all there is" \u2014 and you have to work out which, exactly as you would for an empty one. Four of something is not the answer to "show me all of them". It is four of them, and a gap.
19
21
  - GETTING DATA THAT ISN'T LOADED YET. You are inside the running app and you can drive it, so a gap is a job, not an answer. In order, cheapest first:
@@ -53,7 +55,7 @@ This app has configured you to look but not touch. Answer questions and diagnose
53
55
  Actions classed as ${i.join(" and ")} need the user to tap Approve before they run. Buoy shows that card itself the moment you call one \u2014 so CALL THE ACTION rather than asking for permission in words or with a confirm block first. If the result says the user declined, respect it and ask what they would prefer instead.
54
56
 
55
57
  Say what you are ABOUT to do, never what you have done, until the result is back. "Setting the Lugia line to 3" is right; "Bumped the Lugia line to 3" before the tap is a claim you cannot make \u2014 the user may decline, and then your own answer contradicts itself in the same breath.`)}else if(h){t.push(`APPROVALS ARE OFF
56
- The user has turned approval prompts off on this device. Every action you call runs the instant you call it \u2014 including destructive ones that wipe or reset data \u2014 and nothing will ask them first or give them a chance to stop it. So: do only what was actually asked, one change at a time; never run a destructive action speculatively, in bulk, or to find out what it does; and read the current state before you overwrite it. Say what you are about to change in the same message you change it, then report what happened. If a request is ambiguous and one reading is destructive, ask with buoy_ui choice instead of guessing \u2014 a broad "reset everything" always is.`)}t.push(`SHOWING THINGS
58
+ The user has turned approval prompts off on this device. Task writes can run at once, even wipes and resets. A question still needs a tap before a data change. So: do only what was actually asked, one change at a time; never run a destructive action speculatively, in bulk, or to find out what it does; and read the current state before you overwrite it. Say what you are about to change in the same message you change it, then report what happened. If a request is ambiguous and one reading is destructive, ask with buoy_ui choice instead of guessing \u2014 a broad "reset everything" always is.`)}t.push(`SHOWING THINGS
57
59
  You have a \`buoy_ui\` tool. Use it instead of prose whenever the answer is a set of things, a picture, a comparison, a number, or a decision the user has to make:
58
60
  - A request could mean two things \u2192 buoy_ui choice. Never guess between real alternatives.
59
61
  - You are about to write APP STATE in a shape you did not read (a store, a cache entry), or act as a user you invented \u2192 buoy_ui confirm first. A mock response the user described ("200 with an error in the body", "a 503 with a maintenance message") is not a guess: write it \u2014 for an endpoint you have not seen, a plain {"error": "...", "message": "..."} body \u2014 and say what you sent. Do NOT pre-confirm destructive tool actions: just call them. Buoy shows the user its own approval card and runs the action only if they tap Approve, so asking first makes them answer twice.
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.VERIFIERS=void 0;exports.atPath=atPath;exports.isSubset=isSubset;exports.verificationTrailer=verificationTrailer;exports.verifyOutcome=verifyOutcome;var _realNow=require("../realNow");const rec=e=>e&&typeof e==="object"&&!Array.isArray(e)?e:void 0;function sleep(e,n){return new Promise(r=>{if(n?.aborted)return r();const t=(0,_realNow.realSetTimeout)(i,e);function i(){(0,_realNow.realClearTimeout)(t);n?.removeEventListener("abort",i);r()}n?.addEventListener("abort",i,{once:true})})}async function poll(e,n,r,t,i){const a=(0,_realNow.realNow)()+r;for(;;){const o=await e();if(n(o))return{value:o,satisfied:true};if(i?.aborted||(0,_realNow.realNow)()+t>a)return{value:o,satisfied:false};await sleep(t,i)}}function isSubset(e,n){if(e===null||typeof e!=="object"||Array.isArray(e))return JSON.stringify(e)===JSON.stringify(n);const r=rec(n);if(!r)return false;for(const[t,i]of Object.entries(e)){if(!(t in r))return false;if(!isSubset(i,r[t]))return false}return true}function atPath(e,n){const r=n.replace(/^\/+/,"").match(/\[[^\]]*\]|[^.[\]/]+/g)??[];let t=e;for(const i of r){if(t===null||typeof t!=="object")return void 0;if(i.startsWith("[")){const a=i.slice(1,-1).trim();if(!Array.isArray(t))return void 0;const o=a.indexOf("=");if(o<0){t=t[Number(a)]}else{const s=a.slice(0,o).trim();const u=a.slice(o+1).trim().replace(/^["']|["']$/g,"");t=t.find(d=>d&&typeof d==="object"&&String(d[s])===u)}}else{t=t[i]}}return t}const brief=e=>{const n=JSON.stringify(e);return n===void 0?"undefined":n.length>80?`${n.slice(0,77)}\u2026`:n};const storageSetItem=async({params:e,dispatch:n})=>{if(typeof e.key!=="string")return void 0;const r=await n("storage","async.getItem",{key:e.key});const t=typeof r==="string"?r:rec(r)?.value;const i=typeof e.value==="string"?e.value:JSON.stringify(e.value);if(t===i)return{status:"verified",detail:`Read back: "${e.key}" now holds the written value.`};return{status:"failed",detail:`Read back after the write: "${e.key}" holds ${brief(t)}, not what was written. The write did not take.`}};function writtenLeaves(e){if(typeof e.path==="string"){const t=e.path.split(".").pop()?.replace(/\[.*\]$/,"")??"";return t?[[t,e.value]]:[]}const n=[];const r=(t,i,a)=>{const o=rec(t);if(o&&a<4)for(const[s,u]of Object.entries(o))r(u,s,a+1);else if(i&&(typeof t==="number"||typeof t==="string"||typeof t==="boolean"))n.push([i,t])};r(e.state,"",0);return n}function sameNamedDiffers(e,n,r,t=0){const i=rec(e);if(!i||t>4)return[];const a=[];for(const[o,s]of Object.entries(i)){if(o===n&&(typeof s==="number"||typeof s==="string"||typeof s==="boolean")&&JSON.stringify(s)!==JSON.stringify(r))a.push(s);else if(rec(s))a.push(...sameNamedDiffers(s,n,r,t+1))}return a}const GENERIC_FIELDS=new Set(["id","key","type","name","title","status","value","label"]);async function shadowingQuery(e,n){const r=writtenLeaves(e).filter(([a])=>a.length>2&&!GENERIC_FIELDS.has(a));if(!r.length)return void 0;const t=rec(await n("query","listQueries",{limit:25}).catch(()=>void 0));const i=(Array.isArray(t?.queries)?t.queries:[]).map(rec).filter(a=>!!a&&Number(a.observers)>0&&typeof a.queryHash==="string").slice(0,5);for(const a of i){const o=rec(await n("query","getQueryData",{queryHash:a.queryHash}).catch(()=>void 0));if(!o||o.found===false)continue;for(const[s,u]of r){const d=sameNamedDiffers(o.data,s,u);if(d.length)return`The query ${a.queryHash} on screen also has ${s} = ${brief(d[0])}. If the screen reads that query, it still shows ${brief(d[0])}: check the screen, and change the query (setQueryData) if so.`}}return void 0}const zustandSetState=async({params:e,dispatch:n,after:r})=>{const t=await zustandStoreReadBack({params:e,dispatch:n,after:r});if(t?.status!=="verified")return t;const i=await shadowingQuery(e,n).catch(()=>void 0);return i?{status:"unverified",detail:`${t.detail.replace(/\.$/,"")}, but that is the store, not the screen. ${i}`}:t};const zustandStoreReadBack=async({params:e,dispatch:n,after:r})=>{if(typeof e.storeName!=="string")return void 0;const t=rec(r!==void 0?r:await n("zustand","getStoreState",{storeName:e.storeName}));if(!t||t.found===false)return{status:"failed",detail:`Read back: store "${e.storeName}" could not be read after the write.`};const i=t.currentState;if(typeof e.path==="string"){const s=atPath(i,e.path);if(JSON.stringify(s)===JSON.stringify(e.value))return{status:"verified",detail:`Read back: ${e.storeName}.${e.path} is now ${brief(s)}.`};return{status:"failed",detail:`Read back: ${e.storeName}.${e.path} is ${brief(s)}, not ${brief(e.value)}. The store did not take the write.`}}const a=rec(e.state);if(!a)return void 0;if(isSubset(a,i))return{status:"verified",detail:`Read back: "${e.storeName}" now holds the written fields (${Object.keys(a).join(", ")}).`};const o=Object.keys(a).filter(s=>!isSubset(a[s],rec(i)?.[s]));return{status:"failed",detail:`Read back: "${e.storeName}" does not hold the written value for ${o.join(", ")}. The store did not take the write \u2014 read it before deciding what to do.`}};const querySetQueryData=async({params:e,dispatch:n,after:r,signal:t})=>{const i=e.queryHash!==void 0?{queryHash:e.queryHash}:e.queryKey!==void 0?{queryKey:e.queryKey}:void 0;if(!i)return void 0;const a=rec(r!==void 0?r:await n("query","getQueryData",i));if(!a||a.found===false)return{status:"failed",detail:"Read back: the query could not be read after the write."};const o=a.data;if(typeof e.path==="string"){const s=atPath(o,e.path);if(JSON.stringify(s)===JSON.stringify(e.value))return outlasts(e,n,`Read back: the cached ${e.path} is now ${brief(s)}.`,t);return{status:"failed",detail:`Read back: the cached ${e.path} is ${brief(s)}, not ${brief(e.value)}. The cache did not take the write \u2014 the app may have refetched over it.`}}if(e.data===void 0)return void 0;if(isSubset(e.data,o))return outlasts(e,n,"Read back: the cache now holds the written data.",t);return{status:"failed",detail:"Read back: the cache does not hold the written data \u2014 the app may have refetched over it, or the shape differed."}};async function outlasts(e,n,r,t){const i=typeof e.queryHash==="string"?e.queryHash:Array.isArray(e.queryKey)?JSON.stringify(e.queryKey):void 0;const a=i?await queryRow(n,i).catch(()=>void 0):void 0;const o=typeof a?.refetchEveryMs==="number"?a.refetchEveryMs:void 0;if(!i||!o||o>1e4)return{status:"verified",detail:r};await sleep(o+750,t);if(t?.aborted)return{status:"verified",detail:r};const s=rec(await n("query","getQueryData",{queryHash:i}).catch(()=>void 0));if(!s||s.found===false)return{status:"verified",detail:r};const u=typeof e.path==="string"?JSON.stringify(atPath(s.data,e.path))===JSON.stringify(e.value):e.data!==void 0&&isSubset(e.data,s.data);if(u)return{status:"verified",detail:`${r} It is still there after the screen's ${o} ms refetch.`};return{status:"failed",detail:`${r} But this screen refetches every ${o} ms, and it already has: the server's data replaced the edit. For a change that stays, override the API response \u2014 network.upsertOverrideRule (fromRequestId + bodyPatch, or a full body), then query.invalidate.`}}const navigate=async({params:e,dispatch:n,signal:r})=>{if(typeof e.path!=="string")return void 0;const t=e.path.split("?")[0];const{value:i,satisfied:a}=await poll(async()=>rec(await n("route-events","getCurrentRoute",{})),s=>typeof s?.path==="string"&&(s.path===t||s.path.split("?")[0]===t),2e3,250,r);if(a)return{status:"verified",detail:`The app is now on ${t}.`};const o=typeof i?.path==="string"?i.path:"an unknown route";return{status:"failed",detail:`Two seconds after navigating, the app is on ${o}, not ${t}. Do not navigate again blindly \u2014 check the route exists (the sitemap) and whether something redirected.`}};const upsertOverrideRule=async({params:e,result:n,dispatch:r,signal:t})=>{const i=rec(rec(n)?.rule)?.id??rec(e.rule)?.id;if(typeof i!=="string")return void 0;const a=async()=>{const u=rec(await r("network","listOverrideRules",{}));const d=Array.isArray(u?.rules)?u.rules:[];return d.map(rec).find(l=>l?.id===i)};const{value:o,satisfied:s}=await poll(a,u=>typeof u?.hits==="number"&&u.hits>0,3e3,500,t);if(!o)return{status:"failed",detail:"The rule is not in the override list after the write. It did not install."};if(o.enabled===false)return{status:"failed",detail:"The rule is installed but disabled, so it will not fire."};if(s)return{status:"verified",detail:`The rule has matched ${o.hits} request${o.hits===1?"":"s"} \u2014 the app has already received the overridden response.`};return{status:"unverified",detail:"The rule is installed, but no request has matched it yet \u2014 the app is still showing whatever it loaded before. A refetch or a visit to the screen that fetches this will exercise it; until then, do not say the screen shows the override."}};async function queryRow(e,n){const r=rec(await e("query","listQueries",{limit:100}));const t=Array.isArray(r?.queries)?r.queries:[];return t.map(rec).find(i=>i?.queryHash===n)}function pinWatcher(e,n,r){return async({params:t,dispatch:i,signal:a})=>{if(typeof t.queryHash!=="string")return void 0;const o=async()=>{const d=await queryRow(i,t.queryHash);return typeof d?.status==="string"?d.status:void 0};const{value:s,satisfied:u}=await poll(o,d=>d!==void 0&&!e(d),3e3,500,a);if(s===void 0)return void 0;if(!u)return{status:"verified",detail:`Still ${n} three seconds later.`};return{status:"failed",detail:`Within three seconds the app refetched and the query is "${s}" again: this screen polls, so a cache ${n} pin cannot last. ${r}`}}}const queryTriggerError=pinWatcher(e=>e==="error","error","For an error that stays, make the API fail instead \u2014 network.upsertOverrideRule with status 500 for this endpoint, then query.invalidate \u2014 and restoreError this query.");const queryTriggerLoading=pinWatcher(e=>e==="fetching"||e==="pending"||e==="loading","loading","To keep it loading, delay the API instead \u2014 network.upsertOverrideRule with a long delayMs (60000) for this endpoint, then query.invalidate \u2014 and restoreLoading this query.");const lifecycleRelaunch=async({dispatch:e,signal:n})=>{await sleep(2e3,n);const{satisfied:r}=await poll(async()=>{try{return rec(await e("app","ping",{}))?.ok===true}catch{return false}},t=>t,4e4,1500,n);return r?{status:"verified",detail:"The app reloaded and is answering again \u2014 read its screen or state now."}:{status:"failed",detail:"The app has not answered for 40 seconds after the relaunch. It may have crashed on start: check the console, then reload_app."}};const RECIPE_AWAY_MS={"phone-call":2e4,"quick-switch":5e3,"low-memory":1e4};const lifecycleAway=async({action:e,params:n,dispatch:r,signal:t})=>{let i;if(e==="runRecipe")i=RECIPE_AWAY_MS[String(n.id)];else if(e==="background"&&n.duration!==void 0&&n.skipWait!==true){const c=n.duration;const f=/^(\d+(?:\.\d+)?)\s*(ms|s|m)$/.exec(String(c));i=typeof c==="number"?c:f?Number(f[1])*(f[2]==="ms"?1:f[2]==="s"?1e3:6e4):void 0}if(!i||i>12e4)return void 0;const{satisfied:a}=await poll(async()=>rec(await r("lifecycle","getState",{}).catch(()=>void 0)),c=>!!c&&(c.away===null||c.away===void 0)&&c.realAppState!=="background",i+8e3,1e3,t);if(!a)return{status:"unverified",detail:`The app has not come back yet (expected about ${Math.round(i/1e3)} s). Do not report the result until it has.`};const o=await poll(async()=>rec(rec(await r("lifecycle","getState",{}).catch(()=>void 0))?.report),c=>!c||c.collectingUntil===null||c.collectingUntil===void 0,8e3,1e3,t);const s=o.value;const u=rec(s?.counts);const d=u&&Object.keys(u).length?` Seen during it: ${Object.entries(u).map(([c,f])=>`${c} ${f}`).join(", ")}.`:" Nothing was recorded during it.";const l=typeof s?.finding==="string"?` ${s.finding}`:"";return{status:"verified",detail:`The interruption is over and the app is active again.${s?`${l}${d}`:""}`}};const lifecycleSignal=async({readSnapshot:e,signal:n})=>{if(!e)return void 0;const r=(0,_realNow.realNow)();await sleep(1500,n);const t=rec(await Promise.resolve(e("console")).catch(()=>void 0));const i=(Array.isArray(t?.entries)?t.entries:Array.isArray(t)?t:[]).map(rec).filter(a=>!!a&&Number(a.timestamp??a.at??0)>=r-250).slice(-6).map(a=>`${String(a.level??a.type??"log")}: ${String(a.message??(Array.isArray(a.args)?a.args.join(" "):"")).slice(0,160)}`);if(!i.length)return{status:"unverified",detail:"Delivered. The app logged nothing in the next 1.5 s; check the screen or report for its reaction."};return{status:"verified",detail:`Delivered. The app logged right after: ${i.join(" | ")}`}};const SAVE_LABEL=/^\s*(save|submit|update|apply|done|confirm)\b/i;const tapControl=async({result:e,dispatch:n})=>{const r=rec(rec(e)?.matched);const t=typeof r?.via==="string"?r.via:"";if(t!=="onValueChange"&&t!=="onChangeText")return void 0;const i=rec(await n("highlight-updates","describeScreen",{}).catch(()=>void 0));const a=Array.isArray(i?.elements)?i.elements.map(rec):[];const o=a.find(u=>!!u&&u.interactive===true&&SAVE_LABEL.test(String(u.label??u.text??""))&&u.nativeTag!==r?.nativeTag);if(!o)return void 0;const s=String(o.label??o.text).trim();return{status:"unverified",detail:`Changed on screen only. This screen has a "${s}" button: the change isn't saved until it's pressed. Press it, then check the result.`}};const VERIFIERS=exports.VERIFIERS={"highlight-updates.tapElement":tapControl,"lifecycle.memoryWarning":lifecycleSignal,"lifecycle.relaunch":lifecycleRelaunch,"lifecycle.runRecipe":lifecycleAway,"lifecycle.background":lifecycleAway,"query.triggerError":queryTriggerError,"query.triggerLoading":queryTriggerLoading,"storage.async.setItem":storageSetItem,"zustand.setState":zustandSetState,"query.setQueryData":querySetQueryData,"route-events.navigate":navigate,"network.upsertOverrideRule":upsertOverrideRule};async function verifyOutcome(e){const n=VERIFIERS[`${e.toolId}.${e.action}`];if(!n)return void 0;try{return await n(e)}catch{return void 0}}function verificationTrailer(e){switch(e.status){case"verified":return`
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.VERIFIERS=void 0;exports.atPath=atPath;exports.isSubset=isSubset;exports.verificationTrailer=verificationTrailer;exports.verifyOutcome=verifyOutcome;var _realNow=require("../realNow");const rec=e=>e&&typeof e==="object"&&!Array.isArray(e)?e:void 0;function sleep(e,n){return new Promise(i=>{if(n?.aborted)return i();const t=(0,_realNow.realSetTimeout)(r,e);function r(){(0,_realNow.realClearTimeout)(t);n?.removeEventListener("abort",r);i()}n?.addEventListener("abort",r,{once:true})})}async function poll(e,n,i,t,r){const a=(0,_realNow.realNow)()+i;for(;;){const o=await e();if(n(o))return{value:o,satisfied:true};if(r?.aborted||(0,_realNow.realNow)()+t>a)return{value:o,satisfied:false};await sleep(t,r)}}function isSubset(e,n){if(e===null||typeof e!=="object"||Array.isArray(e))return JSON.stringify(e)===JSON.stringify(n);const i=rec(n);if(!i)return false;for(const[t,r]of Object.entries(e)){if(!(t in i))return false;if(!isSubset(r,i[t]))return false}return true}function atPath(e,n){const i=n.replace(/^\/+/,"").match(/\[[^\]]*\]|[^.[\]/]+/g)??[];let t=e;for(const r of i){if(t===null||typeof t!=="object")return void 0;if(r.startsWith("[")){const a=r.slice(1,-1).trim();if(!Array.isArray(t))return void 0;const o=a.indexOf("=");if(o<0){t=t[Number(a)]}else{const s=a.slice(0,o).trim();const u=a.slice(o+1).trim().replace(/^["']|["']$/g,"");t=t.find(d=>d&&typeof d==="object"&&String(d[s])===u)}}else{t=t[r]}}return t}const brief=e=>{const n=JSON.stringify(e);return n===void 0?"undefined":n.length>80?`${n.slice(0,77)}\u2026`:n};const storageSetItem=async({params:e,dispatch:n})=>{if(typeof e.key!=="string")return void 0;const i=await n("storage","async.getItem",{key:e.key});const t=typeof i==="string"?i:rec(i)?.value;const r=typeof e.value==="string"?e.value:JSON.stringify(e.value);if(t===r)return{status:"verified",detail:`Read back: "${e.key}" now holds the written value.`};return{status:"failed",detail:`Read back after the write: "${e.key}" holds ${brief(t)}, not what was written. The write did not take.`}};function writtenLeaves(e){if(typeof e.path==="string"){const t=e.path.split(".").pop()?.replace(/\[.*\]$/,"")??"";return t?[[t,e.value]]:[]}const n=[];const i=(t,r,a)=>{const o=rec(t);if(o&&a<4)for(const[s,u]of Object.entries(o))i(u,s,a+1);else if(r&&(typeof t==="number"||typeof t==="string"||typeof t==="boolean"))n.push([r,t])};i(e.state,"",0);return n}function sameNamedDiffers(e,n,i,t=0){const r=rec(e);if(!r||t>4)return[];const a=[];for(const[o,s]of Object.entries(r)){if(o===n&&(typeof s==="number"||typeof s==="string"||typeof s==="boolean")&&JSON.stringify(s)!==JSON.stringify(i))a.push(s);else if(rec(s))a.push(...sameNamedDiffers(s,n,i,t+1))}return a}const GENERIC_FIELDS=new Set(["id","key","type","name","title","status","value","label"]);async function shadowingQuery(e,n){const i=writtenLeaves(e).filter(([a])=>a.length>2&&!GENERIC_FIELDS.has(a));if(!i.length)return void 0;const t=rec(await n("query","listQueries",{limit:25}).catch(()=>void 0));const r=(Array.isArray(t?.queries)?t.queries:[]).map(rec).filter(a=>!!a&&Number(a.observers)>0&&typeof a.queryHash==="string").slice(0,5);for(const a of r){const o=rec(await n("query","getQueryData",{queryHash:a.queryHash}).catch(()=>void 0));if(!o||o.found===false)continue;for(const[s,u]of i){const d=sameNamedDiffers(o.data,s,u);if(d.length)return`The query ${a.queryHash} on screen also has ${s} = ${brief(d[0])}. If the screen reads that query, it still shows ${brief(d[0])}: check the screen, and change the query (setQueryData) if so.`}}return void 0}const zustandSetState=async({params:e,dispatch:n,after:i})=>{const t=await zustandStoreReadBack({params:e,dispatch:n,after:i});if(t?.status!=="verified")return t;const r=await shadowingQuery(e,n).catch(()=>void 0);return r?{status:"unverified",detail:`${t.detail.replace(/\.$/,"")}, but that is the store, not the screen. ${r}`}:t};const zustandStoreReadBack=async({params:e,dispatch:n,after:i})=>{if(typeof e.storeName!=="string")return void 0;const t=rec(i!==void 0?i:await n("zustand","getStoreState",{storeName:e.storeName}));if(!t||t.found===false)return{status:"failed",detail:`Read back: store "${e.storeName}" could not be read after the write.`};const r=t.currentState;if(typeof e.path==="string"){const s=atPath(r,e.path);if(JSON.stringify(s)===JSON.stringify(e.value))return{status:"verified",detail:`Read back: ${e.storeName}.${e.path} is now ${brief(s)}.`};return{status:"failed",detail:`Read back: ${e.storeName}.${e.path} is ${brief(s)}, not ${brief(e.value)}. The store did not take the write.`}}const a=rec(e.state);if(!a)return void 0;if(isSubset(a,r))return{status:"verified",detail:`Read back: "${e.storeName}" now holds the written fields (${Object.keys(a).join(", ")}).`};const o=Object.keys(a).filter(s=>!isSubset(a[s],rec(r)?.[s]));return{status:"failed",detail:`Read back: "${e.storeName}" does not hold the written value for ${o.join(", ")}. The store did not take the write \u2014 read it before deciding what to do.`}};const querySetQueryData=async({params:e,dispatch:n,after:i,signal:t})=>{const r=e.queryHash!==void 0?{queryHash:e.queryHash}:e.queryKey!==void 0?{queryKey:e.queryKey}:void 0;if(!r)return void 0;const a=rec(i!==void 0?i:await n("query","getQueryData",r));if(!a||a.found===false)return{status:"failed",detail:"Read back: the query could not be read after the write."};const o=a.data;if(typeof e.path==="string"){const s=atPath(o,e.path);if(JSON.stringify(s)===JSON.stringify(e.value))return outlasts(e,n,`Read back: the cached ${e.path} is now ${brief(s)}.`,t);return{status:"failed",detail:`Read back: the cached ${e.path} is ${brief(s)}, not ${brief(e.value)}. The cache did not take the write \u2014 the app may have refetched over it.`}}if(e.data===void 0)return void 0;if(isSubset(e.data,o))return outlasts(e,n,"Read back: the cache now holds the written data.",t);return{status:"failed",detail:"Read back: the cache does not hold the written data \u2014 the app may have refetched over it, or the shape differed."}};async function outlasts(e,n,i,t){const r=typeof e.queryHash==="string"?e.queryHash:Array.isArray(e.queryKey)?JSON.stringify(e.queryKey):void 0;const a=r?await queryRow(n,r).catch(()=>void 0):void 0;const o=typeof a?.refetchEveryMs==="number"?a.refetchEveryMs:void 0;if(!r||!o||o>1e4)return{status:"verified",detail:i};await sleep(o+750,t);if(t?.aborted)return{status:"verified",detail:i};const s=rec(await n("query","getQueryData",{queryHash:r}).catch(()=>void 0));if(!s||s.found===false)return{status:"verified",detail:i};const u=typeof e.path==="string"?JSON.stringify(atPath(s.data,e.path))===JSON.stringify(e.value):e.data!==void 0&&isSubset(e.data,s.data);if(u)return{status:"verified",detail:`${i} It is still there after the screen's ${o} ms refetch.`};return{status:"failed",detail:`${i} But this screen refetches every ${o} ms, and it already has: the server's data replaced the edit. For a change that stays, override the API response \u2014 network.upsertOverrideRule (fromRequestId + bodyPatch, or a full body), then query.invalidate.`}}const navigate=async({params:e,dispatch:n,signal:i})=>{if(typeof e.path!=="string")return void 0;const t=e.path.split("?")[0];const r=new URLSearchParams(e.path.split("?").slice(1).join("?").split("#")[0]);const a=f=>{const c=rec(f?.params);const l=Array.from(new Set(Array.from(r.keys())));return l.filter(y=>{const h=c?.[y];const g=Array.isArray(h)?h.map(String):h===void 0||h===null?[]:[String(h)];return JSON.stringify(r.getAll(y))!==JSON.stringify(g)})};const o=f=>typeof f?.path==="string"&&f.path.split("?")[0]===t;const{value:s,satisfied:u}=await poll(async()=>rec(await n("route-events","getCurrentRoute",{})),f=>o(f)&&a(f).length===0,2e3,250,i);if(u)return{status:"verified",detail:`The app is now on ${t}.`};if(o(s))return{status:"unverified",detail:`The app is on ${t}. These route params are missing or do not match: ${a(s).join(", ")}.`};const d=typeof s?.path==="string"?s.path:"an unknown route";return{status:"failed",detail:`Two seconds after navigating, the app is on ${d}, not ${t}. Do not navigate again blindly \u2014 check the route exists (the sitemap) and whether something redirected.`}};const upsertOverrideRule=async({params:e,result:n,dispatch:i,signal:t})=>{const r=rec(rec(n)?.rule)?.id??rec(e.rule)?.id;if(typeof r!=="string")return void 0;const a=async()=>{const u=rec(await i("network","listOverrideRules",{}));const d=Array.isArray(u?.rules)?u.rules:[];return d.map(rec).find(f=>f?.id===r)};const{value:o,satisfied:s}=await poll(a,u=>typeof u?.hits==="number"&&u.hits>0,3e3,500,t);if(!o)return{status:"failed",detail:"The rule is not in the override list after the write. It did not install."};if(o.enabled===false)return{status:"failed",detail:"The rule is installed but disabled, so it will not fire."};if(s)return{status:"verified",detail:`The rule has matched ${o.hits} request${o.hits===1?"":"s"} \u2014 the app has already received the overridden response.`};return{status:"unverified",detail:"The rule is installed, but no request has matched it yet \u2014 the app is still showing whatever it loaded before. A refetch or a visit to the screen that fetches this will exercise it; until then, do not say the screen shows the override."}};async function queryRow(e,n){const i=rec(await e("query","listQueries",{limit:100}));const t=Array.isArray(i?.queries)?i.queries:[];return t.map(rec).find(r=>r?.queryHash===n)}function pinWatcher(e,n,i){return async({params:t,dispatch:r,signal:a})=>{if(typeof t.queryHash!=="string")return void 0;const o=async()=>{const d=await queryRow(r,t.queryHash);return typeof d?.status==="string"?d.status:void 0};const{value:s,satisfied:u}=await poll(o,d=>d!==void 0&&!e(d),3e3,500,a);if(s===void 0)return void 0;if(!u)return{status:"verified",detail:`Still ${n} three seconds later.`};return{status:"failed",detail:`Within three seconds the app refetched and the query is "${s}" again: this screen polls, so a cache ${n} pin cannot last. ${i}`}}}const queryTriggerError=pinWatcher(e=>e==="error","error","For an error that stays, make the API fail instead \u2014 network.upsertOverrideRule with status 500 for this endpoint, then query.invalidate \u2014 and restoreError this query.");const queryTriggerLoading=pinWatcher(e=>e==="fetching"||e==="pending"||e==="loading","loading","To keep it loading, delay the API instead \u2014 network.upsertOverrideRule with a long delayMs (60000) for this endpoint, then query.invalidate \u2014 and restoreLoading this query.");const lifecycleRelaunch=async({dispatch:e,signal:n})=>{await sleep(2e3,n);const{satisfied:i}=await poll(async()=>{try{return rec(await e("app","ping",{}))?.ok===true}catch{return false}},t=>t,4e4,1500,n);return i?{status:"verified",detail:"The app reloaded and is answering again \u2014 read its screen or state now."}:{status:"failed",detail:"The app has not answered for 40 seconds after the relaunch. It may have crashed on start: check the console, then reload_app."}};const RECIPE_AWAY_MS={"phone-call":2e4,"quick-switch":5e3,"low-memory":1e4};const lifecycleAway=async({action:e,params:n,dispatch:i,signal:t})=>{let r;if(e==="runRecipe")r=RECIPE_AWAY_MS[String(n.id)];else if(e==="background"&&n.duration!==void 0&&n.skipWait!==true){const c=n.duration;const l=/^(\d+(?:\.\d+)?)\s*(ms|s|m)$/.exec(String(c));r=typeof c==="number"?c:l?Number(l[1])*(l[2]==="ms"?1:l[2]==="s"?1e3:6e4):void 0}if(!r||r>12e4)return void 0;const{satisfied:a}=await poll(async()=>rec(await i("lifecycle","getState",{}).catch(()=>void 0)),c=>!!c&&(c.away===null||c.away===void 0)&&c.realAppState!=="background",r+8e3,1e3,t);if(!a)return{status:"unverified",detail:`The app has not come back yet (expected about ${Math.round(r/1e3)} s). Do not report the result until it has.`};const o=await poll(async()=>rec(rec(await i("lifecycle","getState",{}).catch(()=>void 0))?.report),c=>!c||c.collectingUntil===null||c.collectingUntil===void 0,8e3,1e3,t);const s=o.value;const u=rec(s?.counts);const d=u&&Object.keys(u).length?` Seen during it: ${Object.entries(u).map(([c,l])=>`${c} ${l}`).join(", ")}.`:" Nothing was recorded during it.";const f=typeof s?.finding==="string"?` ${s.finding}`:"";return{status:"verified",detail:`The interruption is over and the app is active again.${s?`${f}${d}`:""}`}};const lifecycleSignal=async({readSnapshot:e,signal:n})=>{if(!e)return void 0;const i=(0,_realNow.realNow)();await sleep(1500,n);const t=rec(await Promise.resolve(e("console")).catch(()=>void 0));const r=(Array.isArray(t?.entries)?t.entries:Array.isArray(t)?t:[]).map(rec).filter(a=>!!a&&Number(a.timestamp??a.at??0)>=i-250).slice(-6).map(a=>`${String(a.level??a.type??"log")}: ${String(a.message??(Array.isArray(a.args)?a.args.join(" "):"")).slice(0,160)}`);if(!r.length)return{status:"unverified",detail:"Delivered. The app logged nothing in the next 1.5 s; check the screen or report for its reaction."};return{status:"verified",detail:`Delivered. The app logged right after: ${r.join(" | ")}`}};const SAVE_LABEL=/^\s*(save|submit|update|apply|done|confirm)\b/i;const tapControl=async({result:e,dispatch:n})=>{const i=rec(rec(e)?.matched);const t=typeof i?.via==="string"?i.via:"";if(t!=="onValueChange"&&t!=="onChangeText")return void 0;const r=rec(await n("highlight-updates","describeScreen",{}).catch(()=>void 0));const a=Array.isArray(r?.elements)?r.elements.map(rec):[];const o=a.find(u=>!!u&&u.interactive===true&&SAVE_LABEL.test(String(u.label??u.text??""))&&u.nativeTag!==i?.nativeTag);if(!o)return void 0;const s=String(o.label??o.text).trim();return{status:"unverified",detail:`Changed on screen only. This screen has a "${s}" button: the change isn't saved until it's pressed. Press it, then check the result.`}};const VERIFIERS=exports.VERIFIERS={"highlight-updates.tapElement":tapControl,"lifecycle.memoryWarning":lifecycleSignal,"lifecycle.relaunch":lifecycleRelaunch,"lifecycle.runRecipe":lifecycleAway,"lifecycle.background":lifecycleAway,"query.triggerError":queryTriggerError,"query.triggerLoading":queryTriggerLoading,"storage.async.setItem":storageSetItem,"zustand.setState":zustandSetState,"query.setQueryData":querySetQueryData,"route-events.navigate":navigate,"network.upsertOverrideRule":upsertOverrideRule};async function verifyOutcome(e){const n=VERIFIERS[`${e.toolId}.${e.action}`];if(!n)return void 0;try{return await n(e)}catch{return void 0}}function verificationTrailer(e){switch(e.status){case"verified":return`
2
2
 
3
3
  [Buoy] Verified: ${e.detail}`;case"unverified":return`
4
4
 
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.DEFAULT_REQUIRE_APPROVAL=void 0;exports.decide=decide;exports.describeCall=describeCall;exports.resolveRequireApproval=resolveRequireApproval;var _labels=require("./labels");const DEFAULT_REQUIRE_APPROVAL=exports.DEFAULT_REQUIRE_APPROVAL=["destructive"];function resolveRequireApproval(e){return e.requireApproval??DEFAULT_REQUIRE_APPROVAL}const SECURE_VALUE_READS=new Set(["secure.get","secure.snapshot"]);function decide({descriptor:e,toolId:t,policy:r,isRelease:a}){for(const n of r.deny??[]){if(n.toolId===t&&(!n.action||n.action===e.action)){return{verdict:"refuse",reason:"This action is disabled in this app."}}}if(a&&e.release!=="works"){return{verdict:"refuse",reason:releaseRefusal(e)}}if(r.allow){const n=r.allow.some(s=>(s.toolId===void 0||s.toolId===t)&&(s.action===void 0||s.action===e.action)&&(s.effect===void 0||s.effect===e.effect));if(!n){return{verdict:"refuse",reason:"This action isn't on Ask Buoy's allow list in this app."}}}if(t==="storage"&&SECURE_VALUE_READS.has(e.action)&&r.secureReads!==true){return{verdict:"refuse",reason:"Reading SecureStore values is off by default \u2014 they are credentials, and they would be sent to the configured model endpoint. Key names are still readable via secure.keys; the app can opt in with policy.secureReads: true."}}if(e.effect!=="read"&&r.readOnly){return{verdict:"refuse",reason:"Ask Buoy is in read-only mode in this app, so it can look at state but not change it."}}for(const n of r.requireApprovalFor??[]){if(n.toolId===t&&(!n.action||n.action===e.action)){return{verdict:"needs-approval",reason:"This app asks for a tap before this particular action runs."}}}if(e.effect==="read")return{verdict:"allow"};if(resolveRequireApproval(r).includes(e.effect)){return{verdict:"needs-approval",reason:e.effect==="destructive"?"Buoy can't undo this one \u2014 that's why it's asking.":"This will change the app's state."}}return{verdict:"allow"}}function releaseRefusal(e){const t=`\`${e.action}\` does not work in this build (a release build).`;switch(e.release){case"noop":return`${t} It would report success and change nothing, so Buoy refused to run it rather than let you be told it worked. ${e.releaseNote??""}`.trim();case"empty":return`${t} It relies on React's developer hook, which React Native only installs in development builds, so it can only ever return empty results here. ${e.releaseNote??""}`.trim();case"throws":return`${t} It is explicitly disabled outside development. ${e.releaseNote??""}`.trim();default:return`${t} Buoy could not confirm it behaves correctly outside development, so it refused rather than guess.`}}function describeCall(e,t,r){return(0,_labels.titleCall)(e,t.action,r)}
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.DEFAULT_REQUIRE_APPROVAL=void 0;exports.decide=decide;exports.describeCall=describeCall;exports.resolveRequireApproval=resolveRequireApproval;var _turnScope=require("./turnScope");var _labels=require("./labels");const DEFAULT_REQUIRE_APPROVAL=exports.DEFAULT_REQUIRE_APPROVAL=["destructive"];function resolveRequireApproval(e){return e.requireApproval??DEFAULT_REQUIRE_APPROVAL}const SECURE_VALUE_READS=new Set(["secure.get","secure.snapshot"]);function decide({descriptor:e,toolId:t,policy:r,isRelease:n,turnScope:i,tapLabels:o,params:u={}}){for(const a of r.deny??[]){if(a.toolId===t&&(!a.action||a.action===e.action)){return{verdict:"refuse",reason:"This action is disabled in this app."}}}if(n&&e.release!=="works"){return{verdict:"refuse",reason:releaseRefusal(e)}}if(r.allow){const a=r.allow.some(s=>(s.toolId===void 0||s.toolId===t)&&(s.action===void 0||s.action===e.action)&&(s.effect===void 0||s.effect===e.effect));if(!a){return{verdict:"refuse",reason:"This action isn't on Ask Buoy's allow list in this app."}}}if(t==="storage"&&SECURE_VALUE_READS.has(e.action)&&r.secureReads!==true){return{verdict:"refuse",reason:"Reading SecureStore values is off by default \u2014 they are credentials, and they would be sent to the configured model endpoint. Key names are still readable via secure.keys; the app can opt in with policy.secureReads: true."}}if(e.effect!=="read"&&r.readOnly){return{verdict:"refuse",reason:"Ask Buoy is in read-only mode in this app, so it can look at state but not change it."}}if(i==="question"&&e.effect!=="read"&&!(0,_turnScope.isQuestionLookup)(t,e.action,u,o)){const a=t==="impersonate"?"who the app acts as or their settings":t==="storage"?"saved app data":t==="highlight-updates"?"the app with this tap":"the app's state";return{verdict:"needs-approval",turnScoped:true,reason:`You asked a question. This would change ${a}. Go ahead?`}}for(const a of r.requireApprovalFor??[]){if(a.toolId===t&&(!a.action||a.action===e.action)){return{verdict:"needs-approval",reason:"This app asks for a tap before this particular action runs."}}}if(e.effect==="read")return{verdict:"allow"};if(resolveRequireApproval(r).includes(e.effect)){return{verdict:"needs-approval",reason:e.effect==="destructive"?"Buoy can't undo this one \u2014 that's why it's asking.":"This will change the app's state."}}return{verdict:"allow"}}function releaseRefusal(e){const t=`\`${e.action}\` does not work in this build (a release build).`;switch(e.release){case"noop":return`${t} It would report success and change nothing, so Buoy refused to run it rather than let you be told it worked. ${e.releaseNote??""}`.trim();case"empty":return`${t} It relies on React's developer hook, which React Native only installs in development builds, so it can only ever return empty results here. ${e.releaseNote??""}`.trim();case"throws":return`${t} It is explicitly disabled outside development. ${e.releaseNote??""}`.trim();default:return`${t} Buoy could not confirm it behaves correctly outside development, so it refused rather than guess.`}}function describeCall(e,t,r){return(0,_labels.titleCall)(e,t.action,r)}
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.isSelfTraffic=isSelfTraffic;exports.redact=redact;exports.registerSelfEndpoint=registerSelfEndpoint;exports.stripSelfTraffic=stripSelfTraffic;const SECRET_KEYS=["authorization","cookie","setcookie","apikey","proxyauthorization","authtoken","accesstoken","refreshtoken","idtoken","sessiontoken","bearertoken","licensekey","licencekey","buoykey","privatekey","secret","password","passwd","credential"];const REDACTED="[redacted by Buoy]";const SECRET_VALUE_PATTERNS=[{re:/\bBearer\s+[A-Za-z0-9._~+/-]{12,}=*/gi,label:"[bearer token removed]"},{re:/\beyJ[A-Za-z0-9._-]{20,}/g,label:"[jwt removed]"},{re:/\bsk-[A-Za-z0-9_-]{16,}/g,label:"[api key removed]"},{re:/\bBUOY-[A-Z0-9-]{8,}/g,label:"[license key removed]"},{re:/\b[A-F0-9]{6}(?:-[A-F0-9]{6}){4}-V\d\b/g,label:"[license key removed]"},{re:/("|')?[A-Za-z0-9_-]*(authorization|api[_-]?key|password|passwd|secret|access[_-]?token|refresh[_-]?token|auth[_-]?token|session[_-]?token|license[_-]?key|licence[_-]?key|buoy[_-]?key|private[_-]?key)\1?\s*[:=]\s*("|')?(?!(?:authorization|bearer|cookie|header)\b)[^\s"',}]{6,}/gi,label:"[credential removed]"}];function scrubString(e){let r=e;for(const{re:o,label:t}of SECRET_VALUE_PATTERNS){r=r.replace(o,t)}return r}function isSecretKey(e){const r=e.toLowerCase().replace(/[-_.\s]/g,"");return SECRET_KEYS.some(o=>r.includes(o))}function redact(e,r=0){if(typeof e==="string")return scrubString(e);if(r>12||e===null||typeof e!=="object")return e;if(Array.isArray(e)){return e.map(t=>redact(t,r+1))}const o={};for(const[t,s]of Object.entries(e)){o[t]=isSecretKey(t)?REDACTED:redact(s,r+1)}return o}const selfHosts=new Set;const INTERNAL_HOSTS_KEY="__BUOY_INTERNAL_HOSTS__";function registerSelfEndpoint(e){try{const r=/^[a-z]+:\/\/([^/?#]+)/i.exec(e);if(!r?.[1])return;const o=r[1].toLowerCase();selfHosts.add(o);const t=globalThis;const s=t[INTERNAL_HOSTS_KEY];const n=s instanceof Set?s:new Set;n.add(o);t[INTERNAL_HOSTS_KEY]=n}catch{}}function isSelfTraffic(e){if(typeof e!=="string")return false;const r=e.toLowerCase();for(const o of selfHosts){if(r.includes(o))return true}return false}function hasSelfUrl(e){if(!e||typeof e!=="object")return false;const r=e;return isSelfTraffic(r.url??r.uri??r.href)}function stripSelfTraffic(e,r=0){if(r>6||e===null||typeof e!=="object")return e;if(Array.isArray(e)){return e.filter(t=>!hasSelfUrl(t)).map(t=>stripSelfTraffic(t,r+1))}const o={};for(const[t,s]of Object.entries(e)){o[t]=stripSelfTraffic(s,r+1)}return o}
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.isSecretKey=isSecretKey;exports.isSelfTraffic=isSelfTraffic;exports.redact=redact;exports.registerSelfEndpoint=registerSelfEndpoint;exports.stripSelfTraffic=stripSelfTraffic;const SECRET_KEYS=["authorization","cookie","setcookie","apikey","proxyauthorization","authtoken","accesstoken","refreshtoken","idtoken","sessiontoken","bearertoken","licensekey","licencekey","buoykey","privatekey","secret","password","passwd","credential"];const REDACTED="[redacted by Buoy]";const SECRET_VALUE_PATTERNS=[{re:/\bBearer\s+[A-Za-z0-9._~+/-]{12,}=*/gi,label:"[bearer token removed]"},{re:/\beyJ[A-Za-z0-9._-]{20,}/g,label:"[jwt removed]"},{re:/\bsk-[A-Za-z0-9_-]{16,}/g,label:"[api key removed]"},{re:/\bBUOY-[A-Z0-9-]{8,}/g,label:"[license key removed]"},{re:/\b[A-F0-9]{6}(?:-[A-F0-9]{6}){4}-V\d\b/g,label:"[license key removed]"},{re:/("|')?[A-Za-z0-9_-]*(authorization|api[_-]?key|password|passwd|secret|access[_-]?token|refresh[_-]?token|auth[_-]?token|session[_-]?token|license[_-]?key|licence[_-]?key|buoy[_-]?key|private[_-]?key)\1?\s*[:=]\s*("|')?(?!(?:authorization|bearer|cookie|header)\b)[^\s"',}]{6,}/gi,label:"[credential removed]"}];function scrubString(e){let t=e;for(const{re:o,label:r}of SECRET_VALUE_PATTERNS){t=t.replace(o,r)}return t}function isSecretKey(e,t="tool"){const o=e.toLowerCase().replace(/[-_.\s]/g,"");return SECRET_KEYS.some(r=>o.includes(r))||t==="route"&&/token|code|key|auth|session|signature|otp|email|phone/.test(o)}function redact(e,t=0){if(typeof e==="string")return scrubString(e);if(t>12||e===null||typeof e!=="object")return e;if(Array.isArray(e)){return e.map(r=>redact(r,t+1))}const o={};for(const[r,s]of Object.entries(e)){o[r]=isSecretKey(r)?REDACTED:redact(s,t+1)}return o}const selfHosts=new Set;const INTERNAL_HOSTS_KEY="__BUOY_INTERNAL_HOSTS__";function registerSelfEndpoint(e){try{const t=/^[a-z]+:\/\/([^/?#]+)/i.exec(e);if(!t?.[1])return;const o=t[1].toLowerCase();selfHosts.add(o);const r=globalThis;const s=r[INTERNAL_HOSTS_KEY];const n=s instanceof Set?s:new Set;n.add(o);r[INTERNAL_HOSTS_KEY]=n}catch{}}function isSelfTraffic(e){if(typeof e!=="string")return false;const t=e.toLowerCase();for(const o of selfHosts){if(t.includes(o))return true}return false}function hasSelfUrl(e){if(!e||typeof e!=="object")return false;const t=e;return isSelfTraffic(t.url??t.uri??t.href)}function stripSelfTraffic(e,t=0){if(t>6||e===null||typeof e!=="object")return e;if(Array.isArray(e)){return e.filter(r=>!hasSelfUrl(r)).map(r=>stripSelfTraffic(r,t+1))}const o={};for(const[r,s]of Object.entries(e)){o[r]=stripSelfTraffic(s,t+1)}return o}
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.QUESTION_DECLINED=void 0;exports.classifyTurn=classifyTurn;exports.isQuestionLookup=isQuestionLookup;exports.tapLabels=tapLabels;const CHANGE=/(?:^|[.!?;,]\s*|\b(?:please|and|then|now|instead|actually)\s+|\b(?:can|could|would|will)\s+you\s+|\bi\s+(?:want|need)\s+you\s+to\s+)(?:please\s+)?(?:add|make|set|turn|fix|place|order|undo|clear|switch|save|delete|remove|reset|wipe|change|update|put|give|enable|disable|start|stop|pause|resume|refund|redeem|pay|submit|confirm|charge|run|try|test|act|pick|send|log\s+in|pretend|cut|sort|toggle|select|choose|replace|fill|type|enter|simulate|throttle|force|pin|hide|move|block|unblock|inject|reload|restart|restore|retry|apply|accept|create|edit)\b/i;const QUESTION=/(?:^|[.!?;,]\s*|\b(?:please|and|then|now|just)\s+)(?:why|what|can|could|is|are|does|do|did|how|which|where|when|check|find out|tell me|explain|read|show me|look|write test steps)\b/i;function classifyTurn(s){const e=s.replace(/\b(?:do not|don't|never)\b[^.!?;]*/gi,"");if(/\b(?:do the same|show (?:me )?what .+ looks like when|show .*(?:made-up|fake))\b/i.test(e)||CHANGE.test(e))return"task";return QUESTION.test(e)?"question":"task"}const QUESTION_DECLINED=exports.QUESTION_DECLINED="The user asked a question; this change was not made. Answer from what you can read.";const COMMIT_WORD=/\b(?:place|pay|submit|save|confirm|delete|redeem|refund|order)\b/i;function hasTapWrite(s,e){const o=[e.role,e.name].filter(Boolean).map(i=>i.toLowerCase());const n=e.via==="onValueChange"||o.some(i=>/^(switch|toggle|slider|adjustable|checkbox)$/.test(i));const t=e.via==="onChangeText"||o.some(i=>/^(textinput|textbox|input|textarea|searchbox)$/.test(i));const r=e.via==="onPress"||o.some(i=>/^(button|pressable|touchableopacity|touchablehighlight|touchablewithoutfeedback|text|statictext)$/.test(i));const a=!n&&!t&&!r;const u=typeof s.text==="string"?!!s.text.trim():s.text!==void 0;return u&&(t||a)||s.value!==void 0&&(n||t||a)}function isQuestionLookup(s,e,o,n){if(s==="route-events"){return["navigate","stackGoBack","stackNavigateToIndex","stackPopToIndex","stackPopToTop"].includes(e)}if(s==="query")return e==="refetch"||e==="invalidate";if(s!=="highlight-updates")return false;if(["describeScreen","waitFor","scroll"].includes(e))return true;if(e!=="tapElement"||o.longPress)return false;return!!n?.length&&n.every(t=>{const r=[t.label,t.text].filter(Boolean).join(" ");return r.trim()&&!COMMIT_WORD.test(r)&&!hasTapWrite(o,t)})}function tapLabels(s,e){const o=s?.elements;if(!Array.isArray(o))return[];const n=typeof e.query==="string"?e.query.toLowerCase():"";return o.filter(t=>{if(e.nativeTag!==void 0)return t.nativeTag===e.nativeTag;if(e.testID)return t.testID===e.testID;return n&&[t.label,t.text,t.testID,t.name].some(r=>r?.toLowerCase().includes(n))}).map(({label:t,text:r,name:a,role:u})=>({label:t,text:r,name:a,role:u}))}